Version in base suite: 3.7.1+dfsg-2 Base version: botan3_3.7.1+dfsg-2 Target version: botan3_3.12.0+dfsg-2~deb13u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/b/botan3/botan3_3.7.1+dfsg-2.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/b/botan3/botan3_3.12.0+dfsg-2~deb13u1.dsc /srv/release.debian.org/tmp/oKBJC3rrE3/botan3-3.12.0+dfsg/src/tests/data/x509/ocsp/byKey_responderID.der |binary botan3-3.12.0+dfsg/.clang-format | 59 botan3-3.12.0+dfsg/.devcontainer/Dockerfile | 70 botan3-3.12.0+dfsg/.devcontainer/devcontainer.json | 48 botan3-3.12.0+dfsg/.devcontainer/startup.sh | 28 botan3-3.12.0+dfsg/.github/actions/setup-build-agent/action.yml | 12 botan3-3.12.0+dfsg/.github/workflows/ci.yml | 149 botan3-3.12.0+dfsg/.github/workflows/codeql.yml | 19 botan3-3.12.0+dfsg/.github/workflows/nightly.yml | 223 botan3-3.12.0+dfsg/.gitignore | 9 botan3-3.12.0+dfsg/configure.py | 450 - botan3-3.12.0+dfsg/debian/changelog | 88 botan3-3.12.0+dfsg/debian/control | 15 botan3-3.12.0+dfsg/debian/copyright | 27 botan3-3.12.0+dfsg/debian/libbotan-3-12.install | 1 botan3-3.12.0+dfsg/debian/libbotan-3-7.install | 1 botan3-3.12.0+dfsg/debian/patches/readdir_hurd.patch | 19 botan3-3.12.0+dfsg/debian/patches/series | 2 botan3-3.12.0+dfsg/debian/patches/use_python3.patch | 185 botan3-3.12.0+dfsg/debian/rules | 1 botan3-3.12.0+dfsg/debian/watch | 18 botan3-3.12.0+dfsg/doc/api_ref/bigint.rst | 174 botan3-3.12.0+dfsg/doc/api_ref/cipher_modes.rst | 32 botan3-3.12.0+dfsg/doc/api_ref/ecc.rst | 18 botan3-3.12.0+dfsg/doc/api_ref/env_vars.rst | 3 botan3-3.12.0+dfsg/doc/api_ref/ffi.rst | 745 + botan3-3.12.0+dfsg/doc/api_ref/filters.rst | 2 botan3-3.12.0+dfsg/doc/api_ref/footguns.rst | 2 botan3-3.12.0+dfsg/doc/api_ref/fpe.rst | 4 botan3-3.12.0+dfsg/doc/api_ref/hash.rst | 38 botan3-3.12.0+dfsg/doc/api_ref/message_auth_codes.rst | 13 botan3-3.12.0+dfsg/doc/api_ref/otp.rst | 6 botan3-3.12.0+dfsg/doc/api_ref/pbkdf.rst | 22 botan3-3.12.0+dfsg/doc/api_ref/pkcs11.rst | 13 botan3-3.12.0+dfsg/doc/api_ref/pubkey.rst | 76 botan3-3.12.0+dfsg/doc/api_ref/python.rst | 125 botan3-3.12.0+dfsg/doc/api_ref/tls.rst | 59 botan3-3.12.0+dfsg/doc/api_ref/tpm.rst | 2 botan3-3.12.0+dfsg/doc/api_ref/versions.rst | 53 botan3-3.12.0+dfsg/doc/api_ref/x509.rst | 14 botan3-3.12.0+dfsg/doc/api_ref/zfec.rst | 2 botan3-3.12.0+dfsg/doc/authors.txt | 7 botan3-3.12.0+dfsg/doc/building.rst | 233 botan3-3.12.0+dfsg/doc/cli.rst | 29 botan3-3.12.0+dfsg/doc/contents.rst | 1 botan3-3.12.0+dfsg/doc/credits.rst | 9 botan3-3.12.0+dfsg/doc/deprecated.rst | 34 botan3-3.12.0+dfsg/doc/dev_ref/configure.rst | 4 botan3-3.12.0+dfsg/doc/dev_ref/contents.rst | 1 botan3-3.12.0+dfsg/doc/dev_ref/contributing.rst | 44 botan3-3.12.0+dfsg/doc/dev_ref/fuzzing.rst | 1 botan3-3.12.0+dfsg/doc/dev_ref/mistakes.rst | 2 botan3-3.12.0+dfsg/doc/dev_ref/next_major.rst | 8 botan3-3.12.0+dfsg/doc/dev_ref/os.rst | 17 botan3-3.12.0+dfsg/doc/dev_ref/pcurves.rst | 62 botan3-3.12.0+dfsg/doc/dev_ref/reading_list.rst | 4 botan3-3.12.0+dfsg/doc/dev_ref/release_process.rst | 21 botan3-3.12.0+dfsg/doc/dev_ref/test_framework.rst | 145 botan3-3.12.0+dfsg/doc/dev_ref/todo.rst | 42 botan3-3.12.0+dfsg/doc/goals.rst | 12 botan3-3.12.0+dfsg/doc/hardware_acceleration.rst | 337 botan3-3.12.0+dfsg/doc/migration_guide.rst | 6 botan3-3.12.0+dfsg/doc/news_2x.rst | 4 botan3-3.12.0+dfsg/doc/old_news.rst | 48 botan3-3.12.0+dfsg/doc/openssl_migration_guide.rst | 2 botan3-3.12.0+dfsg/doc/packaging.rst | 14 botan3-3.12.0+dfsg/doc/roadmap.rst | 35 botan3-3.12.0+dfsg/doc/security.rst | 73 botan3-3.12.0+dfsg/doc/sem_ver.rst | 2 botan3-3.12.0+dfsg/doc/side_channels.rst | 37 botan3-3.12.0+dfsg/doc/support.rst | 23 botan3-3.12.0+dfsg/doc/threat_model.rst | 66 botan3-3.12.0+dfsg/license.txt | 2 botan3-3.12.0+dfsg/news.rst | 487 + botan3-3.12.0+dfsg/readme.rst | 16 botan3-3.12.0+dfsg/src/.clang-tidy | 75 botan3-3.12.0+dfsg/src/bogo_shim/bogo_shim.cpp | 693 + botan3-3.12.0+dfsg/src/bogo_shim/config.json | 214 botan3-3.12.0+dfsg/src/bogo_shim/config_no_tls12.json | 334 botan3-3.12.0+dfsg/src/bogo_shim/config_no_tls13.json | 332 botan3-3.12.0+dfsg/src/build-data/arch/alpha.txt | 2 botan3-3.12.0+dfsg/src/build-data/arch/arm32.txt | 1 botan3-3.12.0+dfsg/src/build-data/arch/arm64.txt | 2 botan3-3.12.0+dfsg/src/build-data/arch/generic.txt | 2 botan3-3.12.0+dfsg/src/build-data/arch/ia64.txt | 1 botan3-3.12.0+dfsg/src/build-data/arch/llvm.txt | 1 botan3-3.12.0+dfsg/src/build-data/arch/loongarch64.txt | 7 botan3-3.12.0+dfsg/src/build-data/arch/m68k.txt | 1 botan3-3.12.0+dfsg/src/build-data/arch/mips64.txt | 1 botan3-3.12.0+dfsg/src/build-data/arch/powerpcspe.txt | 1 botan3-3.12.0+dfsg/src/build-data/arch/ppc32.txt | 5 botan3-3.12.0+dfsg/src/build-data/arch/ppc64.txt | 2 botan3-3.12.0+dfsg/src/build-data/arch/riscv32.txt | 1 botan3-3.12.0+dfsg/src/build-data/arch/riscv64.txt | 2 botan3-3.12.0+dfsg/src/build-data/arch/s390.txt | 1 botan3-3.12.0+dfsg/src/build-data/arch/s390x.txt | 2 botan3-3.12.0+dfsg/src/build-data/arch/sparc32.txt | 1 botan3-3.12.0+dfsg/src/build-data/arch/sparc64.txt | 2 botan3-3.12.0+dfsg/src/build-data/arch/wasm.txt | 5 botan3-3.12.0+dfsg/src/build-data/arch/x32.txt | 1 botan3-3.12.0+dfsg/src/build-data/arch/x86_32.txt | 1 botan3-3.12.0+dfsg/src/build-data/arch/x86_64.txt | 3 botan3-3.12.0+dfsg/src/build-data/botan-config-version.cmake.in | 23 botan3-3.12.0+dfsg/src/build-data/botan-config.cmake.in | 109 botan3-3.12.0+dfsg/src/build-data/botan.pc.in | 4 botan3-3.12.0+dfsg/src/build-data/buildh.in | 241 botan3-3.12.0+dfsg/src/build-data/cc/clang.txt | 7 botan3-3.12.0+dfsg/src/build-data/cc/clangcl.txt | 106 botan3-3.12.0+dfsg/src/build-data/cc/emcc.txt | 8 botan3-3.12.0+dfsg/src/build-data/cc/gcc.txt | 6 botan3-3.12.0+dfsg/src/build-data/cc/msvc.txt | 9 botan3-3.12.0+dfsg/src/build-data/cc/sunstudio.txt | 43 botan3-3.12.0+dfsg/src/build-data/cc/xcode.txt | 2 botan3-3.12.0+dfsg/src/build-data/compile_commands.json.in | 10 botan3-3.12.0+dfsg/src/build-data/detect_version.cpp | 2 botan3-3.12.0+dfsg/src/build-data/ec_groups.txt | 37 botan3-3.12.0+dfsg/src/build-data/ec_named.cpp.in | 39 botan3-3.12.0+dfsg/src/build-data/makefile.in | 10 botan3-3.12.0+dfsg/src/build-data/ninja.in | 19 botan3-3.12.0+dfsg/src/build-data/oids.txt | 119 botan3-3.12.0+dfsg/src/build-data/os/hurd.txt | 4 botan3-3.12.0+dfsg/src/build-data/os/netbsd.txt | 1 botan3-3.12.0+dfsg/src/build-data/os/openbsd.txt | 1 botan3-3.12.0+dfsg/src/build-data/policy/bsi.txt | 57 botan3-3.12.0+dfsg/src/build-data/policy/fips140.txt | 9 botan3-3.12.0+dfsg/src/build-data/policy/modern.txt | 21 botan3-3.12.0+dfsg/src/build-data/target_info.h.in | 110 botan3-3.12.0+dfsg/src/build-data/templates/ec_named.cpp.in | 56 botan3-3.12.0+dfsg/src/build-data/templates/pcurves.cpp.in | 44 botan3-3.12.0+dfsg/src/build-data/templates/pcurves_instance.h.in | 42 botan3-3.12.0+dfsg/src/build-data/templates/pcurves_stub.cpp.in | 80 botan3-3.12.0+dfsg/src/build-data/templates/static_oids.cpp.in | 91 botan3-3.12.0+dfsg/src/build-data/templates/tls_suite_info.cpp.in | 58 botan3-3.12.0+dfsg/src/build-data/version.txt | 12 botan3-3.12.0+dfsg/src/build-data/version_info.h.in | 20 botan3-3.12.0+dfsg/src/cli/argon2.cpp | 8 botan3-3.12.0+dfsg/src/cli/argparse.h | 42 botan3-3.12.0+dfsg/src/cli/asn1.cpp | 14 botan3-3.12.0+dfsg/src/cli/bcrypt.cpp | 8 botan3-3.12.0+dfsg/src/cli/cc_enc.cpp | 20 botan3-3.12.0+dfsg/src/cli/cipher.cpp | 6 botan3-3.12.0+dfsg/src/cli/cli.cpp | 8 botan3-3.12.0+dfsg/src/cli/cli.h | 12 botan3-3.12.0+dfsg/src/cli/cli_exceptions.h | 2 botan3-3.12.0+dfsg/src/cli/cli_rng.cpp | 21 botan3-3.12.0+dfsg/src/cli/codec.cpp | 13 botan3-3.12.0+dfsg/src/cli/compress.cpp | 7 botan3-3.12.0+dfsg/src/cli/entropy.cpp | 40 botan3-3.12.0+dfsg/src/cli/hash.cpp | 4 botan3-3.12.0+dfsg/src/cli/hmac.cpp | 6 botan3-3.12.0+dfsg/src/cli/main.cpp | 2 botan3-3.12.0+dfsg/src/cli/math.cpp | 40 botan3-3.12.0+dfsg/src/cli/pbkdf.cpp | 14 botan3-3.12.0+dfsg/src/cli/perf.cpp | 3 botan3-3.12.0+dfsg/src/cli/perf.h | 25 botan3-3.12.0+dfsg/src/cli/perf_ec.cpp | 244 botan3-3.12.0+dfsg/src/cli/perf_math.cpp | 73 botan3-3.12.0+dfsg/src/cli/perf_misc.cpp | 83 botan3-3.12.0+dfsg/src/cli/perf_pk_enc.cpp | 27 botan3-3.12.0+dfsg/src/cli/perf_pk_ka.cpp | 23 botan3-3.12.0+dfsg/src/cli/perf_pk_kem.cpp | 68 botan3-3.12.0+dfsg/src/cli/perf_pk_misc.cpp | 59 botan3-3.12.0+dfsg/src/cli/perf_pk_sig.cpp | 67 botan3-3.12.0+dfsg/src/cli/perf_pwdhash.cpp | 62 botan3-3.12.0+dfsg/src/cli/perf_rng.cpp | 7 botan3-3.12.0+dfsg/src/cli/perf_sym.cpp | 70 botan3-3.12.0+dfsg/src/cli/perf_x509.cpp | 157 botan3-3.12.0+dfsg/src/cli/pk_crypt.cpp | 11 botan3-3.12.0+dfsg/src/cli/psk.cpp | 7 botan3-3.12.0+dfsg/src/cli/pubkey.cpp | 33 botan3-3.12.0+dfsg/src/cli/roughtime.cpp | 15 botan3-3.12.0+dfsg/src/cli/sandbox.cpp | 32 botan3-3.12.0+dfsg/src/cli/sandbox.h | 5 botan3-3.12.0+dfsg/src/cli/socket_utils.h | 13 botan3-3.12.0+dfsg/src/cli/speed.cpp | 117 botan3-3.12.0+dfsg/src/cli/timer.cpp | 3 botan3-3.12.0+dfsg/src/cli/timer.h | 48 botan3-3.12.0+dfsg/src/cli/timing_tests.cpp | 254 botan3-3.12.0+dfsg/src/cli/tls_client.cpp | 50 botan3-3.12.0+dfsg/src/cli/tls_helpers.h | 30 botan3-3.12.0+dfsg/src/cli/tls_http_server.cpp | 17 botan3-3.12.0+dfsg/src/cli/tls_proxy.cpp | 86 botan3-3.12.0+dfsg/src/cli/tls_server.cpp | 29 botan3-3.12.0+dfsg/src/cli/tls_utils.cpp | 47 botan3-3.12.0+dfsg/src/cli/tss.cpp | 6 botan3-3.12.0+dfsg/src/cli/utils.cpp | 21 botan3-3.12.0+dfsg/src/cli/x509.cpp | 102 botan3-3.12.0+dfsg/src/cli/zfec.cpp | 22 botan3-3.12.0+dfsg/src/configs/ci_deps.conf | 28 botan3-3.12.0+dfsg/src/configs/clang-format | 60 botan3-3.12.0+dfsg/src/configs/pylint.rc | 5 botan3-3.12.0+dfsg/src/configs/repo_config.env | 23 botan3-3.12.0+dfsg/src/configs/sphinx/conf.py | 10 botan3-3.12.0+dfsg/src/configs/typos.toml | 61 botan3-3.12.0+dfsg/src/configs/zizmor.yml | 8 botan3-3.12.0+dfsg/src/ct_selftest/ct_selftest.cpp | 6 botan3-3.12.0+dfsg/src/editors/vscode/extensions.json | 11 botan3-3.12.0+dfsg/src/editors/vscode/launch.json | 55 botan3-3.12.0+dfsg/src/editors/vscode/scripts/bogo.py | 33 botan3-3.12.0+dfsg/src/editors/vscode/scripts/test.py | 50 botan3-3.12.0+dfsg/src/editors/vscode/settings.json | 9 botan3-3.12.0+dfsg/src/editors/vscode/tasks.json | 129 botan3-3.12.0+dfsg/src/examples/check_key.cpp | 2 botan3-3.12.0+dfsg/src/examples/custom_system_rng.cpp | 7 botan3-3.12.0+dfsg/src/examples/ecc_raw_private_key.cpp | 1 botan3-3.12.0+dfsg/src/examples/ecc_raw_public_key.cpp | 1 botan3-3.12.0+dfsg/src/examples/ecdh.cpp | 8 botan3-3.12.0+dfsg/src/examples/ecdsa.cpp | 2 botan3-3.12.0+dfsg/src/examples/entropy.cpp | 4 botan3-3.12.0+dfsg/src/examples/ffi.c | 53 botan3-3.12.0+dfsg/src/examples/fpe_alnum.cpp | 122 botan3-3.12.0+dfsg/src/examples/fpe_dictionary.cpp | 84 botan3-3.12.0+dfsg/src/examples/hash.cpp | 4 botan3-3.12.0+dfsg/src/examples/hmac.cpp | 6 botan3-3.12.0+dfsg/src/examples/hybrid_encryption.cpp | 4 botan3-3.12.0+dfsg/src/examples/hybrid_key_encapsulation.cpp | 49 botan3-3.12.0+dfsg/src/examples/ml_kem.cpp | 2 botan3-3.12.0+dfsg/src/examples/password_encryption.cpp | 20 botan3-3.12.0+dfsg/src/examples/pkcs10_csr_on_tpm2.cpp | 4 botan3-3.12.0+dfsg/src/examples/pkcs11_ecdh.cpp | 28 botan3-3.12.0+dfsg/src/examples/pkcs11_ecdsa.cpp | 18 botan3-3.12.0+dfsg/src/examples/pkcs11_low_level.cpp | 4 botan3-3.12.0+dfsg/src/examples/pkcs11_module.cpp | 2 botan3-3.12.0+dfsg/src/examples/pkcs11_objects.cpp | 15 botan3-3.12.0+dfsg/src/examples/pkcs11_rsa.cpp | 22 botan3-3.12.0+dfsg/src/examples/pkcs11_session.cpp | 20 botan3-3.12.0+dfsg/src/examples/pkcs11_slot.cpp | 14 botan3-3.12.0+dfsg/src/examples/pkcs11_token_management.cpp | 6 botan3-3.12.0+dfsg/src/examples/pkcs11_x509.cpp | 6 botan3-3.12.0+dfsg/src/examples/pwdhash.cpp | 10 botan3-3.12.0+dfsg/src/examples/rsa_encrypt.cpp | 6 botan3-3.12.0+dfsg/src/examples/tls_13_hybrid_key_exchange_client.cpp | 30 botan3-3.12.0+dfsg/src/examples/tls_client.cpp | 46 botan3-3.12.0+dfsg/src/examples/tls_custom_curves_client.cpp | 33 botan3-3.12.0+dfsg/src/examples/tls_proxy.cpp | 34 botan3-3.12.0+dfsg/src/examples/tls_ssl_key_log_file.cpp | 95 botan3-3.12.0+dfsg/src/examples/tls_stream_client.cpp | 37 botan3-3.12.0+dfsg/src/examples/tls_stream_coroutine_client.cpp | 4 botan3-3.12.0+dfsg/src/examples/x509_path.cpp | 10 botan3-3.12.0+dfsg/src/examples/xmss.cpp | 2 botan3-3.12.0+dfsg/src/fuzzer/asn1.cpp | 22 botan3-3.12.0+dfsg/src/fuzzer/barrett.cpp | 40 botan3-3.12.0+dfsg/src/fuzzer/bn_cmp.cpp | 20 botan3-3.12.0+dfsg/src/fuzzer/bn_sqr.cpp | 6 botan3-3.12.0+dfsg/src/fuzzer/cert.cpp | 4 botan3-3.12.0+dfsg/src/fuzzer/crl.cpp | 4 botan3-3.12.0+dfsg/src/fuzzer/divide.cpp | 10 botan3-3.12.0+dfsg/src/fuzzer/ec_scalar.cpp | 138 botan3-3.12.0+dfsg/src/fuzzer/ecc_bp256.cpp | 2 botan3-3.12.0+dfsg/src/fuzzer/ecc_bp384.cpp | 16 botan3-3.12.0+dfsg/src/fuzzer/ecc_bp512.cpp | 16 botan3-3.12.0+dfsg/src/fuzzer/ecc_frp256.cpp | 16 botan3-3.12.0+dfsg/src/fuzzer/ecc_helper.h | 8 botan3-3.12.0+dfsg/src/fuzzer/ecc_numsp512.cpp | 16 botan3-3.12.0+dfsg/src/fuzzer/ecc_p224.cpp | 16 botan3-3.12.0+dfsg/src/fuzzer/ecc_p256.cpp | 2 botan3-3.12.0+dfsg/src/fuzzer/ecc_p384.cpp | 2 botan3-3.12.0+dfsg/src/fuzzer/ecc_p521.cpp | 2 botan3-3.12.0+dfsg/src/fuzzer/ecc_secp256k1.cpp | 16 botan3-3.12.0+dfsg/src/fuzzer/ecc_sm2p256.cpp | 16 botan3-3.12.0+dfsg/src/fuzzer/fuzzers.h | 81 botan3-3.12.0+dfsg/src/fuzzer/gcd.cpp | 5 botan3-3.12.0+dfsg/src/fuzzer/invert.cpp | 14 botan3-3.12.0+dfsg/src/fuzzer/ipv4.cpp | 8 botan3-3.12.0+dfsg/src/fuzzer/mem_pool.cpp | 21 botan3-3.12.0+dfsg/src/fuzzer/mode_padding.cpp | 28 botan3-3.12.0+dfsg/src/fuzzer/mp_comba_mul.cpp | 2 botan3-3.12.0+dfsg/src/fuzzer/mp_fuzzers.h | 30 botan3-3.12.0+dfsg/src/fuzzer/mp_redc.cpp | 32 botan3-3.12.0+dfsg/src/fuzzer/mp_redc_crandall.cpp | 24 botan3-3.12.0+dfsg/src/fuzzer/ocsp.cpp | 4 botan3-3.12.0+dfsg/src/fuzzer/os2ecp.cpp | 16 botan3-3.12.0+dfsg/src/fuzzer/pkcs1.cpp | 9 botan3-3.12.0+dfsg/src/fuzzer/pkcs8.cpp | 7 botan3-3.12.0+dfsg/src/fuzzer/pow_mod.cpp | 6 botan3-3.12.0+dfsg/src/fuzzer/ressol.cpp | 11 botan3-3.12.0+dfsg/src/fuzzer/tls_13_handshake_layer.cpp | 9 botan3-3.12.0+dfsg/src/fuzzer/tls_client.cpp | 30 botan3-3.12.0+dfsg/src/fuzzer/tls_client_hello.cpp | 8 botan3-3.12.0+dfsg/src/fuzzer/tls_server.cpp | 32 botan3-3.12.0+dfsg/src/fuzzer/uri.cpp | 2 botan3-3.12.0+dfsg/src/fuzzer/x509_path.cpp | 10 botan3-3.12.0+dfsg/src/lib/asn1/alg_id.cpp | 9 botan3-3.12.0+dfsg/src/lib/asn1/asn1_obj.cpp | 22 botan3-3.12.0+dfsg/src/lib/asn1/asn1_obj.h | 146 botan3-3.12.0+dfsg/src/lib/asn1/asn1_oid.cpp | 36 botan3-3.12.0+dfsg/src/lib/asn1/asn1_print.cpp | 53 botan3-3.12.0+dfsg/src/lib/asn1/asn1_print.h | 26 botan3-3.12.0+dfsg/src/lib/asn1/asn1_str.cpp | 134 botan3-3.12.0+dfsg/src/lib/asn1/asn1_time.cpp | 35 botan3-3.12.0+dfsg/src/lib/asn1/asn1_time.h | 84 botan3-3.12.0+dfsg/src/lib/asn1/ber_dec.cpp | 474 - botan3-3.12.0+dfsg/src/lib/asn1/ber_dec.h | 131 botan3-3.12.0+dfsg/src/lib/asn1/der_enc.cpp | 50 botan3-3.12.0+dfsg/src/lib/asn1/der_enc.h | 50 botan3-3.12.0+dfsg/src/lib/asn1/info.txt | 1 botan3-3.12.0+dfsg/src/lib/asn1/oid_map.cpp | 34 botan3-3.12.0+dfsg/src/lib/asn1/oid_map.h | 6 botan3-3.12.0+dfsg/src/lib/asn1/oid_maps.cpp | 693 - botan3-3.12.0+dfsg/src/lib/asn1/pss_params.cpp | 24 botan3-3.12.0+dfsg/src/lib/asn1/pss_params.h | 18 botan3-3.12.0+dfsg/src/lib/asn1/static_oids.cpp | 1459 +++ botan3-3.12.0+dfsg/src/lib/base/buf_comp.cpp | 10 botan3-3.12.0+dfsg/src/lib/base/buf_comp.h | 10 botan3-3.12.0+dfsg/src/lib/base/secmem.h | 56 botan3-3.12.0+dfsg/src/lib/base/sym_algo.cpp | 5 botan3-3.12.0+dfsg/src/lib/base/sym_algo.h | 14 botan3-3.12.0+dfsg/src/lib/base/symkey.cpp | 6 botan3-3.12.0+dfsg/src/lib/block/aes/aes.cpp | 181 botan3-3.12.0+dfsg/src/lib/block/aes/aes.h | 1 botan3-3.12.0+dfsg/src/lib/block/aes/aes_armv8/aes_armv8.cpp | 51 botan3-3.12.0+dfsg/src/lib/block/aes/aes_armv8/info.txt | 8 botan3-3.12.0+dfsg/src/lib/block/aes/aes_ni/aes_ni.cpp | 67 botan3-3.12.0+dfsg/src/lib/block/aes/aes_ni/info.txt | 7 botan3-3.12.0+dfsg/src/lib/block/aes/aes_power8/aes_power8.cpp | 452 - botan3-3.12.0+dfsg/src/lib/block/aes/aes_power8/info.txt | 8 botan3-3.12.0+dfsg/src/lib/block/aes/aes_vaes/aes_vaes.cpp | 47 botan3-3.12.0+dfsg/src/lib/block/aes/aes_vaes/info.txt | 5 botan3-3.12.0+dfsg/src/lib/block/aes/aes_vperm/aes_vperm.cpp | 537 - botan3-3.12.0+dfsg/src/lib/block/aes/aes_vperm/info.txt | 19 botan3-3.12.0+dfsg/src/lib/block/aria/aria.cpp | 149 botan3-3.12.0+dfsg/src/lib/block/aria/aria.h | 37 botan3-3.12.0+dfsg/src/lib/block/aria/aria_avx512_gfni/aria_avx512_gfni.cpp | 390 + botan3-3.12.0+dfsg/src/lib/block/aria/aria_avx512_gfni/info.txt | 18 botan3-3.12.0+dfsg/src/lib/block/aria/aria_hwaes/aria_hwaes.cpp | 248 botan3-3.12.0+dfsg/src/lib/block/aria/aria_hwaes/info.txt | 12 botan3-3.12.0+dfsg/src/lib/block/block_cipher.cpp | 5 botan3-3.12.0+dfsg/src/lib/block/block_cipher.h | 41 botan3-3.12.0+dfsg/src/lib/block/blowfish/blowfish.cpp | 45 botan3-3.12.0+dfsg/src/lib/block/blowfish/blowfish.h | 1 botan3-3.12.0+dfsg/src/lib/block/camellia/camellia.cpp | 198 botan3-3.12.0+dfsg/src/lib/block/camellia/camellia.h | 55 botan3-3.12.0+dfsg/src/lib/block/camellia/camellia_avx2_gfni/camellia_avx2_gfni.cpp | 444 + botan3-3.12.0+dfsg/src/lib/block/camellia/camellia_avx2_gfni/info.txt | 18 botan3-3.12.0+dfsg/src/lib/block/camellia/camellia_avx512_gfni/camellia_avx512_gfni.cpp | 761 + botan3-3.12.0+dfsg/src/lib/block/camellia/camellia_avx512_gfni/info.txt | 18 botan3-3.12.0+dfsg/src/lib/block/camellia/camellia_hwaes/camellia_hwaes.cpp | 437 + botan3-3.12.0+dfsg/src/lib/block/camellia/camellia_hwaes/info.txt | 12 botan3-3.12.0+dfsg/src/lib/block/cascade/cascade.cpp | 10 botan3-3.12.0+dfsg/src/lib/block/cascade/cascade.h | 5 botan3-3.12.0+dfsg/src/lib/block/cast128/cast128.cpp | 24 botan3-3.12.0+dfsg/src/lib/block/cast128/cast128.h | 1 botan3-3.12.0+dfsg/src/lib/block/des/des.cpp | 1084 +- botan3-3.12.0+dfsg/src/lib/block/des/des.h | 9 botan3-3.12.0+dfsg/src/lib/block/gost_28147/gost_28147.cpp | 2 botan3-3.12.0+dfsg/src/lib/block/gost_28147/gost_28147.h | 1 botan3-3.12.0+dfsg/src/lib/block/idea/idea.cpp | 69 botan3-3.12.0+dfsg/src/lib/block/idea/idea.h | 5 botan3-3.12.0+dfsg/src/lib/block/idea/idea_avx2/idea_avx2.cpp | 219 botan3-3.12.0+dfsg/src/lib/block/idea/idea_avx2/info.txt | 16 botan3-3.12.0+dfsg/src/lib/block/idea/idea_sse2/idea_sse2.cpp | 98 botan3-3.12.0+dfsg/src/lib/block/idea/idea_sse2/info.txt | 8 botan3-3.12.0+dfsg/src/lib/block/kuznyechik/kuznyechik.cpp | 194 botan3-3.12.0+dfsg/src/lib/block/kuznyechik/kuznyechik.h | 10 botan3-3.12.0+dfsg/src/lib/block/lion/lion.cpp | 1 botan3-3.12.0+dfsg/src/lib/block/noekeon/noekeon.cpp | 17 botan3-3.12.0+dfsg/src/lib/block/noekeon/noekeon.h | 1 botan3-3.12.0+dfsg/src/lib/block/noekeon/noekeon_simd/info.txt | 20 botan3-3.12.0+dfsg/src/lib/block/noekeon/noekeon_simd/noekeon_simd.cpp | 26 botan3-3.12.0+dfsg/src/lib/block/seed/seed.cpp | 165 botan3-3.12.0+dfsg/src/lib/block/seed/seed.h | 13 botan3-3.12.0+dfsg/src/lib/block/seed/seed_avx512_gfni/info.txt | 17 botan3-3.12.0+dfsg/src/lib/block/seed/seed_avx512_gfni/seed_avx512_gfni.cpp | 331 botan3-3.12.0+dfsg/src/lib/block/seed/seed_hwaes/info.txt | 12 botan3-3.12.0+dfsg/src/lib/block/seed/seed_hwaes/seed_hwaes.cpp | 196 botan3-3.12.0+dfsg/src/lib/block/serpent/serpent.cpp | 38 botan3-3.12.0+dfsg/src/lib/block/serpent/serpent.h | 1 botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_avx2/info.txt | 10 botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_avx2/serpent_avx2.cpp | 10 botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_avx512/info.txt | 7 botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_avx512/serpent_avx512.cpp | 50 botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_fn.h | 20 botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_sbox.h | 32 botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_simd/info.txt | 19 botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_simd/serpent_simd.cpp | 6 botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2.cpp | 71 botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2.h | 6 botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_armv8/info.txt | 6 botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_armv8/shacal2_arvm8.cpp | 4 botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_avx2/info.txt | 5 botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_avx2/shacal2_avx2.cpp | 48 botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_avx512/info.txt | 18 botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_avx512/shacal2_avx512.cpp | 337 botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_simd/info.txt | 20 botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_simd/shacal2_simd.cpp | 42 botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_x86/info.txt | 6 botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_x86/shacal2_x86.cpp | 12 botan3-3.12.0+dfsg/src/lib/block/sm4/sm4.cpp | 85 botan3-3.12.0+dfsg/src/lib/block/sm4/sm4.h | 16 botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_armv8/info.txt | 8 botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_armv8/sm4_armv8.cpp | 29 botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_avx512/info.txt | 19 botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_avx512/sm4_avx512.cpp | 302 botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_gfni/info.txt | 5 botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_gfni/sm4_gfni.cpp | 22 botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_hwaes/info.txt | 12 botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_hwaes/sm4_hwaes.cpp | 274 botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_x86/info.txt | 23 botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_x86/sm4_x86.cpp | 142 botan3-3.12.0+dfsg/src/lib/block/threefish_512/threefish_512.cpp | 21 botan3-3.12.0+dfsg/src/lib/block/threefish_512/threefish_512.h | 1 botan3-3.12.0+dfsg/src/lib/block/twofish/twofish.cpp | 321 botan3-3.12.0+dfsg/src/lib/block/twofish/twofish.h | 22 botan3-3.12.0+dfsg/src/lib/block/twofish/twofish_avx512/info.txt | 22 botan3-3.12.0+dfsg/src/lib/block/twofish/twofish_avx512/twofish_avx512.cpp | 197 botan3-3.12.0+dfsg/src/lib/block/twofish/twofish_tab.cpp | 204 botan3-3.12.0+dfsg/src/lib/codec/base32/base32.cpp | 71 botan3-3.12.0+dfsg/src/lib/codec/base32/base32.h | 14 botan3-3.12.0+dfsg/src/lib/codec/base58/base58.cpp | 191 botan3-3.12.0+dfsg/src/lib/codec/base58/base58.h | 1 botan3-3.12.0+dfsg/src/lib/codec/base64/base64.cpp | 27 botan3-3.12.0+dfsg/src/lib/codec/hex/hex.cpp | 11 botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium.h | 12 botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium_25519.cpp | 8 botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium_aead.cpp | 17 botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium_auth.cpp | 9 botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium_chacha.cpp | 4 botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium_salsa.cpp | 4 botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium_secretbox.cpp | 1 botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium_utils.cpp | 40 botan3-3.12.0+dfsg/src/lib/compression/bzip2/bzip2.cpp | 10 botan3-3.12.0+dfsg/src/lib/compression/compress_utils.cpp | 28 botan3-3.12.0+dfsg/src/lib/compression/compression.h | 9 botan3-3.12.0+dfsg/src/lib/compression/lzma/lzma.cpp | 8 botan3-3.12.0+dfsg/src/lib/compression/zlib/zlib.cpp | 16 botan3-3.12.0+dfsg/src/lib/entropy/entropy_src.h | 19 botan3-3.12.0+dfsg/src/lib/entropy/entropy_srcs.cpp | 27 botan3-3.12.0+dfsg/src/lib/entropy/getentropy/getentropy.cpp | 2 botan3-3.12.0+dfsg/src/lib/entropy/getentropy/info.txt | 4 botan3-3.12.0+dfsg/src/lib/entropy/rdseed/info.txt | 8 botan3-3.12.0+dfsg/src/lib/entropy/rdseed/rdseed.cpp | 22 botan3-3.12.0+dfsg/src/lib/entropy/win32_stats/es_win32.cpp | 2 botan3-3.12.0+dfsg/src/lib/entropy/win32_stats/info.txt | 4 botan3-3.12.0+dfsg/src/lib/ffi/ffi.cpp | 145 botan3-3.12.0+dfsg/src/lib/ffi/ffi.h | 1038 ++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_block.cpp | 3 botan3-3.12.0+dfsg/src/lib/ffi/ffi_cert.cpp | 1104 ++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_cert.h | 31 botan3-3.12.0+dfsg/src/lib/ffi/ffi_cipher.cpp | 50 botan3-3.12.0+dfsg/src/lib/ffi/ffi_ec.cpp | 355 botan3-3.12.0+dfsg/src/lib/ffi/ffi_ec.h | 21 botan3-3.12.0+dfsg/src/lib/ffi/ffi_fpe.cpp | 12 botan3-3.12.0+dfsg/src/lib/ffi/ffi_hash.cpp | 8 botan3-3.12.0+dfsg/src/lib/ffi/ffi_hotp.cpp | 5 botan3-3.12.0+dfsg/src/lib/ffi/ffi_kdf.cpp | 34 botan3-3.12.0+dfsg/src/lib/ffi/ffi_keywrap.cpp | 11 botan3-3.12.0+dfsg/src/lib/ffi/ffi_mac.cpp | 28 botan3-3.12.0+dfsg/src/lib/ffi/ffi_mp.cpp | 83 botan3-3.12.0+dfsg/src/lib/ffi/ffi_oid.cpp | 80 botan3-3.12.0+dfsg/src/lib/ffi/ffi_oid.h | 19 botan3-3.12.0+dfsg/src/lib/ffi/ffi_pk_op.cpp | 31 botan3-3.12.0+dfsg/src/lib/ffi/ffi_pkey.cpp | 146 botan3-3.12.0+dfsg/src/lib/ffi/ffi_pkey_algs.cpp | 597 + botan3-3.12.0+dfsg/src/lib/ffi/ffi_rng.cpp | 86 botan3-3.12.0+dfsg/src/lib/ffi/ffi_srp6.cpp | 48 botan3-3.12.0+dfsg/src/lib/ffi/ffi_totp.cpp | 5 botan3-3.12.0+dfsg/src/lib/ffi/ffi_tpm2.cpp | 28 botan3-3.12.0+dfsg/src/lib/ffi/ffi_util.h | 129 botan3-3.12.0+dfsg/src/lib/ffi/ffi_xof.cpp | 96 botan3-3.12.0+dfsg/src/lib/ffi/ffi_zfec.cpp | 7 botan3-3.12.0+dfsg/src/lib/ffi/info.txt | 14 botan3-3.12.0+dfsg/src/lib/filters/algo_filt.cpp | 8 botan3-3.12.0+dfsg/src/lib/filters/b64_filt.cpp | 28 botan3-3.12.0+dfsg/src/lib/filters/basefilt.cpp | 10 botan3-3.12.0+dfsg/src/lib/filters/buf_filt.cpp | 16 botan3-3.12.0+dfsg/src/lib/filters/cipher_filter.cpp | 2 botan3-3.12.0+dfsg/src/lib/filters/comp_filter.cpp | 5 botan3-3.12.0+dfsg/src/lib/filters/data_snk.cpp | 4 botan3-3.12.0+dfsg/src/lib/filters/data_snk.h | 17 botan3-3.12.0+dfsg/src/lib/filters/fd_unix/fd_unix.cpp | 12 botan3-3.12.0+dfsg/src/lib/filters/filter.cpp | 25 botan3-3.12.0+dfsg/src/lib/filters/filter.h | 29 botan3-3.12.0+dfsg/src/lib/filters/filters.h | 82 botan3-3.12.0+dfsg/src/lib/filters/hex_filt.cpp | 21 botan3-3.12.0+dfsg/src/lib/filters/out_buf.cpp | 21 botan3-3.12.0+dfsg/src/lib/filters/out_buf.h | 16 botan3-3.12.0+dfsg/src/lib/filters/pipe.cpp | 61 botan3-3.12.0+dfsg/src/lib/filters/pipe.h | 34 botan3-3.12.0+dfsg/src/lib/filters/pipe_io.cpp | 10 botan3-3.12.0+dfsg/src/lib/filters/pipe_rw.cpp | 17 botan3-3.12.0+dfsg/src/lib/filters/secqueue.cpp | 59 botan3-3.12.0+dfsg/src/lib/filters/secqueue.h | 6 botan3-3.12.0+dfsg/src/lib/filters/threaded_fork.cpp | 7 botan3-3.12.0+dfsg/src/lib/hash/ascon_hash256/ascon_hash256.cpp | 56 botan3-3.12.0+dfsg/src/lib/hash/ascon_hash256/ascon_hash256.h | 45 botan3-3.12.0+dfsg/src/lib/hash/ascon_hash256/info.txt | 11 botan3-3.12.0+dfsg/src/lib/hash/blake2/blake2b.cpp | 6 botan3-3.12.0+dfsg/src/lib/hash/blake2/blake2b.h | 3 botan3-3.12.0+dfsg/src/lib/hash/blake2/info.txt | 2 botan3-3.12.0+dfsg/src/lib/hash/blake2s/blake2s.cpp | 150 botan3-3.12.0+dfsg/src/lib/hash/blake2s/blake2s.h | 36 botan3-3.12.0+dfsg/src/lib/hash/checksum/adler32/adler32.h | 11 botan3-3.12.0+dfsg/src/lib/hash/checksum/crc24/crc24.cpp | 2 botan3-3.12.0+dfsg/src/lib/hash/checksum/crc24/crc24.h | 12 botan3-3.12.0+dfsg/src/lib/hash/checksum/crc32/crc32.cpp | 40 botan3-3.12.0+dfsg/src/lib/hash/checksum/crc32/crc32.h | 10 botan3-3.12.0+dfsg/src/lib/hash/comb4p/comb4p.cpp | 3 botan3-3.12.0+dfsg/src/lib/hash/gost_3411/gost_3411.cpp | 17 botan3-3.12.0+dfsg/src/lib/hash/gost_3411/gost_3411.h | 4 botan3-3.12.0+dfsg/src/lib/hash/hash.cpp | 10 botan3-3.12.0+dfsg/src/lib/hash/hash.h | 2 botan3-3.12.0+dfsg/src/lib/hash/keccak/keccak.cpp | 5 botan3-3.12.0+dfsg/src/lib/hash/md4/md4.cpp | 8 botan3-3.12.0+dfsg/src/lib/hash/md5/md5.cpp | 9 botan3-3.12.0+dfsg/src/lib/hash/mdx_hash/info.txt | 4 botan3-3.12.0+dfsg/src/lib/hash/mdx_hash/mdx_hash.h | 6 botan3-3.12.0+dfsg/src/lib/hash/par_hash/par_hash.cpp | 2 botan3-3.12.0+dfsg/src/lib/hash/par_hash/par_hash.h | 7 botan3-3.12.0+dfsg/src/lib/hash/rmd160/rmd160.cpp | 28 botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1.cpp | 258 botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1.h | 8 botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_armv8/info.txt | 8 botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_armv8/sha1_armv8.cpp | 40 botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_avx2/info.txt | 24 botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_avx2/sha1_avx2.cpp | 554 + botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_f.h | 44 botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_simd/info.txt | 37 botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_simd/sha1_simd.cpp | 254 botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_sse2/info.txt | 16 botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_sse2/sha1_sse2.cpp | 286 botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_x86/info.txt | 9 botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_x86/sha1_x86.cpp | 300 botan3-3.12.0+dfsg/src/lib/hash/sha2_32/info.txt | 4 botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32.cpp | 68 botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32.h | 8 botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_armv8/info.txt | 9 botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_armv8/sha2_32_armv8.cpp | 200 botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_avx2/info.txt | 19 botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_avx2/sha2_32_avx2.cpp | 362 botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_bmi2/info.txt | 12 botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_bmi2/sha2_32_bmi2.cpp | 118 botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_f.h | 20 botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_simd/info.txt | 51 botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_simd/sha2_32_simd.cpp | 155 botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_x86/info.txt | 9 botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_x86/sha2_32_x86.cpp | 266 botan3-3.12.0+dfsg/src/lib/hash/sha2_64/info.txt | 4 botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64.cpp | 63 botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64.h | 12 botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_armv8/info.txt | 9 botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_armv8/sha2_64_armv8.cpp | 15 botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_avx2/info.txt | 25 botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_avx2/sha2_64_avx2.cpp | 346 botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_avx512/info.txt | 25 botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_avx512/sha2_64_avx512.cpp | 235 botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_bmi2/info.txt | 17 botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_bmi2/sha2_64_bmi2.cpp | 124 botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_f.h | 20 botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_x86/info.txt | 26 botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_x86/sha2_64_x86.cpp | 125 botan3-3.12.0+dfsg/src/lib/hash/sha3/sha3.cpp | 5 botan3-3.12.0+dfsg/src/lib/hash/shake/shake.cpp | 10 botan3-3.12.0+dfsg/src/lib/hash/skein/skein_512.cpp | 25 botan3-3.12.0+dfsg/src/lib/hash/skein/skein_512.h | 9 botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3.cpp | 249 botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3.h | 14 botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3_armv8/info.txt | 16 botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3_armv8/sm3_armv8.cpp | 170 botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3_avx2_bmi2/info.txt | 19 botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3_avx2_bmi2/sm3_avx2_bmi2.cpp | 422 + botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3_fn.h | 75 botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3_x86/info.txt | 25 botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3_x86/sm3_x86.cpp | 134 botan3-3.12.0+dfsg/src/lib/hash/streebog/streebog.cpp | 155 botan3-3.12.0+dfsg/src/lib/hash/streebog/streebog_precalc.cpp | 549 - botan3-3.12.0+dfsg/src/lib/hash/trunc_hash/trunc_hash.cpp | 10 botan3-3.12.0+dfsg/src/lib/hash/trunc_hash/trunc_hash.h | 4 botan3-3.12.0+dfsg/src/lib/hash/whirlpool/whirlpool.cpp | 180 botan3-3.12.0+dfsg/src/lib/hash/whirlpool/whirlpool.h | 10 botan3-3.12.0+dfsg/src/lib/hash/whirlpool/whirlpool_avx2/info.txt | 16 botan3-3.12.0+dfsg/src/lib/hash/whirlpool/whirlpool_avx2/whirlpool_avx2.cpp | 254 botan3-3.12.0+dfsg/src/lib/hash/whirlpool/whirlpool_avx512/info.txt | 20 botan3-3.12.0+dfsg/src/lib/hash/whirlpool/whirlpool_avx512/whirlpool_avx512.cpp | 239 botan3-3.12.0+dfsg/src/lib/kdf/hkdf/hkdf.cpp | 17 botan3-3.12.0+dfsg/src/lib/kdf/info.txt | 3 botan3-3.12.0+dfsg/src/lib/kdf/kdf.cpp | 7 botan3-3.12.0+dfsg/src/lib/kdf/kdf.h | 36 botan3-3.12.0+dfsg/src/lib/kdf/kdf1/kdf1.cpp | 4 botan3-3.12.0+dfsg/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.cpp | 5 botan3-3.12.0+dfsg/src/lib/kdf/kdf2/kdf2.cpp | 5 botan3-3.12.0+dfsg/src/lib/kdf/prf_tls/prf_tls.cpp | 4 botan3-3.12.0+dfsg/src/lib/kdf/prf_x942/prf_x942.cpp | 10 botan3-3.12.0+dfsg/src/lib/kdf/sp800_108/sp800_108.cpp | 31 botan3-3.12.0+dfsg/src/lib/kdf/sp800_56a/sp800_56c_one_step.cpp | 36 botan3-3.12.0+dfsg/src/lib/kdf/xmd/xmd.cpp | 3 botan3-3.12.0+dfsg/src/lib/kdf/xmd/xmd.h | 11 botan3-3.12.0+dfsg/src/lib/mac/blake2mac/blake2bmac.h | 3 botan3-3.12.0+dfsg/src/lib/mac/cmac/cmac.cpp | 5 botan3-3.12.0+dfsg/src/lib/mac/cmac/cmac.h | 9 botan3-3.12.0+dfsg/src/lib/mac/gmac/gmac.cpp | 16 botan3-3.12.0+dfsg/src/lib/mac/gmac/gmac.h | 11 botan3-3.12.0+dfsg/src/lib/mac/hmac/hmac.cpp | 3 botan3-3.12.0+dfsg/src/lib/mac/hmac/hmac.h | 9 botan3-3.12.0+dfsg/src/lib/mac/kmac/kmac.cpp | 5 botan3-3.12.0+dfsg/src/lib/mac/kmac/kmac.h | 14 botan3-3.12.0+dfsg/src/lib/mac/mac.cpp | 1 botan3-3.12.0+dfsg/src/lib/mac/mac.h | 4 botan3-3.12.0+dfsg/src/lib/mac/poly1305/poly1305.cpp | 346 botan3-3.12.0+dfsg/src/lib/mac/poly1305/poly1305.h | 17 botan3-3.12.0+dfsg/src/lib/mac/poly1305/poly1305_avx2/info.txt | 17 botan3-3.12.0+dfsg/src/lib/mac/poly1305/poly1305_avx2/poly1305_avx2.cpp | 255 botan3-3.12.0+dfsg/src/lib/mac/poly1305/poly1305_avx512/info.txt | 17 botan3-3.12.0+dfsg/src/lib/mac/poly1305/poly1305_avx512/poly1305_avx512.cpp | 222 botan3-3.12.0+dfsg/src/lib/mac/siphash/info.txt | 1 botan3-3.12.0+dfsg/src/lib/mac/siphash/siphash.cpp | 9 botan3-3.12.0+dfsg/src/lib/mac/siphash/siphash.h | 8 botan3-3.12.0+dfsg/src/lib/mac/x919_mac/x919_mac.cpp | 7 botan3-3.12.0+dfsg/src/lib/mac/x919_mac/x919_mac.h | 11 botan3-3.12.0+dfsg/src/lib/math/bigint/big_code.cpp | 148 botan3-3.12.0+dfsg/src/lib/math/bigint/big_io.cpp | 8 botan3-3.12.0+dfsg/src/lib/math/bigint/big_ops2.cpp | 67 botan3-3.12.0+dfsg/src/lib/math/bigint/big_ops3.cpp | 62 botan3-3.12.0+dfsg/src/lib/math/bigint/big_rand.cpp | 21 botan3-3.12.0+dfsg/src/lib/math/bigint/bigint.cpp | 199 botan3-3.12.0+dfsg/src/lib/math/bigint/bigint.h | 183 botan3-3.12.0+dfsg/src/lib/math/bigint/divide.cpp | 200 botan3-3.12.0+dfsg/src/lib/math/bigint/divide.h | 29 botan3-3.12.0+dfsg/src/lib/math/mp/info.txt | 4 botan3-3.12.0+dfsg/src/lib/math/mp/mp_asmi.h | 441 - botan3-3.12.0+dfsg/src/lib/math/mp/mp_comba.cpp | 3 botan3-3.12.0+dfsg/src/lib/math/mp/mp_core.h | 615 - botan3-3.12.0+dfsg/src/lib/math/mp/mp_karat.cpp | 59 botan3-3.12.0+dfsg/src/lib/math/mp/mp_monty.cpp | 87 botan3-3.12.0+dfsg/src/lib/math/mp/mp_monty_n.cpp | 232 botan3-3.12.0+dfsg/src/lib/math/numbertheory/barrett.cpp | 203 botan3-3.12.0+dfsg/src/lib/math/numbertheory/barrett.h | 84 botan3-3.12.0+dfsg/src/lib/math/numbertheory/dsa_gen.cpp | 13 botan3-3.12.0+dfsg/src/lib/math/numbertheory/info.txt | 1 botan3-3.12.0+dfsg/src/lib/math/numbertheory/make_prm.cpp | 53 botan3-3.12.0+dfsg/src/lib/math/numbertheory/mod_inv.cpp | 60 botan3-3.12.0+dfsg/src/lib/math/numbertheory/mod_inv.h | 2 botan3-3.12.0+dfsg/src/lib/math/numbertheory/monty.cpp | 467 - botan3-3.12.0+dfsg/src/lib/math/numbertheory/monty.h | 207 botan3-3.12.0+dfsg/src/lib/math/numbertheory/monty_exp.cpp | 87 botan3-3.12.0+dfsg/src/lib/math/numbertheory/monty_exp.h | 35 botan3-3.12.0+dfsg/src/lib/math/numbertheory/numthry.cpp | 115 botan3-3.12.0+dfsg/src/lib/math/numbertheory/numthry.h | 19 botan3-3.12.0+dfsg/src/lib/math/numbertheory/primality.cpp | 47 botan3-3.12.0+dfsg/src/lib/math/numbertheory/primality.h | 23 botan3-3.12.0+dfsg/src/lib/math/numbertheory/reducer.cpp | 128 botan3-3.12.0+dfsg/src/lib/math/numbertheory/reducer.h | 20 botan3-3.12.0+dfsg/src/lib/math/pcurves/info.txt | 7 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves.cpp | 224 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves.h | 232 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_algos.h | 503 + botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_brainpool256r1/info.txt | 4 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_brainpool256r1/pcurves_brainpool256r1.cpp | 9 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_brainpool384r1/info.txt | 4 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_brainpool384r1/pcurves_brainpool384r1.cpp | 10 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_brainpool512r1/info.txt | 4 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_brainpool512r1/pcurves_brainpool512r1.cpp | 9 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_frp256v1/info.txt | 4 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_frp256v1/pcurves_frp256v1.cpp | 9 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_generic/info.txt | 13 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_generic/pcurves_generic.cpp | 1751 ++++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_generic/pcurves_generic.h | 136 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_id.h | 76 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_impl/info.txt | 4 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_impl/pcurves_impl.h | 1330 +-- botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_impl/pcurves_solinas.h | 26 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_impl/pcurves_util.h | 81 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_impl/pcurves_wrap.h | 118 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_instance.h | 61 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_mul.h | 546 + botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_numsp512d1/info.txt | 4 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_numsp512d1/pcurves_numsp512d1.cpp | 46 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp192r1/info.txt | 4 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp192r1/pcurves_secp192r1.cpp | 70 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp224r1/info.txt | 4 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp224r1/pcurves_secp224r1.cpp | 3 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp256k1/info.txt | 4 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp256k1/pcurves_secp256k1.cpp | 48 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp256r1/info.txt | 4 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp256r1/pcurves_secp256r1.cpp | 28 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp384r1/info.txt | 4 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp384r1/pcurves_secp384r1.cpp | 45 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp521r1/info.txt | 4 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp521r1/pcurves_secp521r1.cpp | 46 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_sm2p256v1/info.txt | 4 botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_sm2p256v1/pcurves_sm2p256v1.cpp | 40 botan3-3.12.0+dfsg/src/lib/misc/cryptobox/cryptobox.cpp | 25 botan3-3.12.0+dfsg/src/lib/misc/cryptobox/info.txt | 3 botan3-3.12.0+dfsg/src/lib/misc/fpe_fe1/fpe_fe1.cpp | 33 botan3-3.12.0+dfsg/src/lib/misc/fpe_fe1/fpe_fe1.h | 23 botan3-3.12.0+dfsg/src/lib/misc/hotp/hotp.cpp | 31 botan3-3.12.0+dfsg/src/lib/misc/hotp/otp.h | 14 botan3-3.12.0+dfsg/src/lib/misc/hotp/totp.cpp | 3 botan3-3.12.0+dfsg/src/lib/misc/nist_keywrap/nist_keywrap.cpp | 30 botan3-3.12.0+dfsg/src/lib/misc/nist_keywrap/nist_keywrap.h | 41 botan3-3.12.0+dfsg/src/lib/misc/rfc3394/rfc3394.cpp | 1 botan3-3.12.0+dfsg/src/lib/misc/roughtime/roughtime.cpp | 48 botan3-3.12.0+dfsg/src/lib/misc/roughtime/roughtime.h | 8 botan3-3.12.0+dfsg/src/lib/misc/srp6/srp6.cpp | 8 botan3-3.12.0+dfsg/src/lib/misc/srp6/srp6.h | 2 botan3-3.12.0+dfsg/src/lib/misc/tss/tss.cpp | 135 botan3-3.12.0+dfsg/src/lib/misc/zfec/zfec.cpp | 52 botan3-3.12.0+dfsg/src/lib/misc/zfec/zfec.h | 4 botan3-3.12.0+dfsg/src/lib/misc/zfec/zfec_sse2/info.txt | 16 botan3-3.12.0+dfsg/src/lib/misc/zfec/zfec_sse2/zfec_sse2.cpp | 93 botan3-3.12.0+dfsg/src/lib/misc/zfec/zfec_vperm/info.txt | 13 botan3-3.12.0+dfsg/src/lib/misc/zfec/zfec_vperm/zfec_vperm.cpp | 33 botan3-3.12.0+dfsg/src/lib/modes/aead/aead.cpp | 30 botan3-3.12.0+dfsg/src/lib/modes/aead/aead.h | 2 botan3-3.12.0+dfsg/src/lib/modes/aead/ascon_aead128/ascon_aead128.cpp | 156 botan3-3.12.0+dfsg/src/lib/modes/aead/ascon_aead128/ascon_aead128.h | 99 botan3-3.12.0+dfsg/src/lib/modes/aead/ascon_aead128/info.txt | 11 botan3-3.12.0+dfsg/src/lib/modes/aead/ccm/ccm.cpp | 22 botan3-3.12.0+dfsg/src/lib/modes/aead/ccm/ccm.h | 8 botan3-3.12.0+dfsg/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp | 34 botan3-3.12.0+dfsg/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h | 14 botan3-3.12.0+dfsg/src/lib/modes/aead/eax/eax.cpp | 8 botan3-3.12.0+dfsg/src/lib/modes/aead/eax/eax.h | 20 botan3-3.12.0+dfsg/src/lib/modes/aead/gcm/gcm.cpp | 38 botan3-3.12.0+dfsg/src/lib/modes/aead/gcm/gcm.h | 42 botan3-3.12.0+dfsg/src/lib/modes/aead/ocb/ocb.cpp | 41 botan3-3.12.0+dfsg/src/lib/modes/aead/ocb/ocb.h | 44 botan3-3.12.0+dfsg/src/lib/modes/aead/siv/siv.cpp | 13 botan3-3.12.0+dfsg/src/lib/modes/aead/siv/siv.h | 36 botan3-3.12.0+dfsg/src/lib/modes/cbc/cbc.cpp | 22 botan3-3.12.0+dfsg/src/lib/modes/cbc/cbc.h | 1 botan3-3.12.0+dfsg/src/lib/modes/cfb/cfb.cpp | 9 botan3-3.12.0+dfsg/src/lib/modes/cfb/cfb.h | 6 botan3-3.12.0+dfsg/src/lib/modes/cipher_mode.cpp | 7 botan3-3.12.0+dfsg/src/lib/modes/cipher_mode.h | 11 botan3-3.12.0+dfsg/src/lib/modes/mode_pad/mode_pad.cpp | 229 botan3-3.12.0+dfsg/src/lib/modes/mode_pad/mode_pad.h | 84 botan3-3.12.0+dfsg/src/lib/modes/stream_mode.h | 4 botan3-3.12.0+dfsg/src/lib/modes/xts/xts.cpp | 69 botan3-3.12.0+dfsg/src/lib/modes/xts/xts.h | 18 botan3-3.12.0+dfsg/src/lib/modes/xts/xts_avx512_clmul/info.txt | 15 botan3-3.12.0+dfsg/src/lib/modes/xts/xts_avx512_clmul/xts_avx512_clmul.cpp | 65 botan3-3.12.0+dfsg/src/lib/passhash/argon2fmt/argon2fmt.cpp | 108 botan3-3.12.0+dfsg/src/lib/passhash/bcrypt/bcrypt.cpp | 51 botan3-3.12.0+dfsg/src/lib/passhash/passhash9/passhash9.cpp | 11 botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2.cpp | 53 botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2.h | 23 botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_avx2/argon2_avx2.cpp | 128 botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_avx2/info.txt | 9 botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_avx512/argon2_avx512.cpp | 88 botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_avx512/info.txt | 17 botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_simd64/argon2_simd64.cpp | 114 botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_simd64/info.txt | 18 botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_ssse3/argon2_ssse3.cpp | 234 botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_ssse3/info.txt | 17 botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2pwhash.cpp | 17 botan3-3.12.0+dfsg/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp | 23 botan3-3.12.0+dfsg/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h | 17 botan3-3.12.0+dfsg/src/lib/pbkdf/pbkdf.h | 7 botan3-3.12.0+dfsg/src/lib/pbkdf/pbkdf2/pbkdf2.cpp | 41 botan3-3.12.0+dfsg/src/lib/pbkdf/pbkdf2/pbkdf2.h | 14 botan3-3.12.0+dfsg/src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp | 38 botan3-3.12.0+dfsg/src/lib/pbkdf/pgp_s2k/pgp_s2k.h | 14 botan3-3.12.0+dfsg/src/lib/pbkdf/pwdhash.h | 68 botan3-3.12.0+dfsg/src/lib/pbkdf/scrypt/scrypt.cpp | 31 botan3-3.12.0+dfsg/src/lib/pbkdf/scrypt/scrypt.h | 11 botan3-3.12.0+dfsg/src/lib/permutations/ascon_perm/ascon_perm.cpp | 115 botan3-3.12.0+dfsg/src/lib/permutations/ascon_perm/ascon_perm.h | 74 botan3-3.12.0+dfsg/src/lib/permutations/ascon_perm/info.txt | 8 botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/info.txt | 8 botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_helpers.cpp | 9 botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_helpers.h | 4 botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_perm.cpp | 143 botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_perm.h | 61 botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_perm_avx512/info.txt | 15 botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_perm_avx512/keccak_perm_avx512.cpp | 153 botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_perm_bmi2/info.txt | 14 botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_perm_bmi2/keccak_perm_bmi2.cpp | 7 botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_perm_round.h | 2 botan3-3.12.0+dfsg/src/lib/permutations/sponge/info.txt | 4 botan3-3.12.0+dfsg/src/lib/permutations/sponge/sponge.h | 72 botan3-3.12.0+dfsg/src/lib/permutations/sponge/sponge_processing.h | 264 botan3-3.12.0+dfsg/src/lib/pk_pad/eme.cpp | 71 botan3-3.12.0+dfsg/src/lib/pk_pad/eme.h | 67 botan3-3.12.0+dfsg/src/lib/pk_pad/eme_oaep/info.txt | 12 botan3-3.12.0+dfsg/src/lib/pk_pad/eme_oaep/oaep.cpp | 159 botan3-3.12.0+dfsg/src/lib/pk_pad/eme_oaep/oaep.h | 55 botan3-3.12.0+dfsg/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp | 106 botan3-3.12.0+dfsg/src/lib/pk_pad/eme_pkcs1/eme_pkcs.h | 32 botan3-3.12.0+dfsg/src/lib/pk_pad/eme_pkcs1/info.txt | 9 botan3-3.12.0+dfsg/src/lib/pk_pad/eme_raw/eme_raw.cpp | 40 botan3-3.12.0+dfsg/src/lib/pk_pad/eme_raw/eme_raw.h | 31 botan3-3.12.0+dfsg/src/lib/pk_pad/eme_raw/info.txt | 7 botan3-3.12.0+dfsg/src/lib/pk_pad/emsa.cpp | 143 botan3-3.12.0+dfsg/src/lib/pk_pad/emsa.h | 88 botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp | 133 botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.h | 81 botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_pkcs1/info.txt | 12 botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_pssr/info.txt | 13 botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_pssr/pssr.cpp | 245 botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_pssr/pssr.h | 92 botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_raw/emsa_raw.cpp | 92 botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_raw/emsa_raw.h | 41 botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_raw/info.txt | 7 botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_x931/emsa_x931.cpp | 97 botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_x931/emsa_x931.h | 47 botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_x931/info.txt | 11 botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_oaep/info.txt | 12 botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_oaep/oaep.cpp | 159 botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_oaep/oaep.h | 55 botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_pkcs1/eme_pkcs.cpp | 105 botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_pkcs1/eme_pkcs.h | 32 botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_pkcs1/info.txt | 11 botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_raw/eme_raw.cpp | 40 botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_raw/eme_raw.h | 31 botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_raw/info.txt | 7 botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/enc_padding.cpp | 70 botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/enc_padding.h | 68 botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/info.txt | 13 botan3-3.12.0+dfsg/src/lib/pk_pad/hash_id/info.txt | 4 botan3-3.12.0+dfsg/src/lib/pk_pad/info.txt | 13 botan3-3.12.0+dfsg/src/lib/pk_pad/iso9796/info.txt | 13 botan3-3.12.0+dfsg/src/lib/pk_pad/iso9796/iso9796.cpp | 281 botan3-3.12.0+dfsg/src/lib/pk_pad/iso9796/iso9796.h | 91 botan3-3.12.0+dfsg/src/lib/pk_pad/mgf1/mgf1.cpp | 25 botan3-3.12.0+dfsg/src/lib/pk_pad/mgf1/mgf1.h | 12 botan3-3.12.0+dfsg/src/lib/pk_pad/raw_hash/info.txt | 4 botan3-3.12.0+dfsg/src/lib/pk_pad/raw_hash/raw_hash.cpp | 1 botan3-3.12.0+dfsg/src/lib/pk_pad/raw_hash/raw_hash.h | 3 botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_pkcs1/info.txt | 12 botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_pkcs1/pkcs1_sig_padding.cpp | 151 botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_pkcs1/pkcs1_sig_padding.h | 91 botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_pssr/info.txt | 13 botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_pssr/pssr.cpp | 256 botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_pssr/pssr.h | 97 botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_raw/info.txt | 8 botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_raw/raw_sig_padding.cpp | 75 botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_raw/raw_sig_padding.h | 47 botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_x931/info.txt | 12 botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_x931/x931_sig_padding.cpp | 103 botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_x931/x931_sig_padding.h | 52 botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/info.txt | 13 botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/iso9796/info.txt | 13 botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/iso9796/iso9796.cpp | 304 botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/iso9796/iso9796.h | 88 botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/sig_padding.cpp | 143 botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/sig_padding.h | 90 botan3-3.12.0+dfsg/src/lib/prov/commoncrypto/commoncrypto.h | 2 botan3-3.12.0+dfsg/src/lib/prov/commoncrypto/commoncrypto_hash.cpp | 3 botan3-3.12.0+dfsg/src/lib/prov/commoncrypto/commoncrypto_mode.cpp | 8 botan3-3.12.0+dfsg/src/lib/prov/tpm/info.txt | 4 botan3-3.12.0+dfsg/src/lib/prov/tpm/tpm.cpp | 1 botan3-3.12.0+dfsg/src/lib/prov/tpm2/info.txt | 2 botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_algo_mappings.h | 2 botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_context.cpp | 35 botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_context.h | 5 botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_crypto_backend/info.txt | 2 botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_crypto_backend/tpm2_crypto_backend.h | 2 botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_crypto_backend/tpm2_crypto_backend_impl.cpp | 96 botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.cpp | 28 botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.h | 6 botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_hash.cpp | 16 botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_key.cpp | 27 botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_key.h | 2 botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_object.cpp | 6 botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_pkops.cpp | 6 botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_rng.cpp | 7 botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_rng.h | 2 botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_rsa/info.txt | 2 botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.cpp | 38 botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.h | 9 botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_session.cpp | 27 botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_session.h | 16 botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_util.h | 44 botan3-3.12.0+dfsg/src/lib/psk_db/psk_db.cpp | 21 botan3-3.12.0+dfsg/src/lib/psk_db/psk_db.h | 11 botan3-3.12.0+dfsg/src/lib/pubkey/blinding/blinding.cpp | 8 botan3-3.12.0+dfsg/src/lib/pubkey/blinding/blinding.h | 37 botan3-3.12.0+dfsg/src/lib/pubkey/blinding/info.txt | 4 botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce.cpp | 8 botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce.h | 7 botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_decaps.cpp | 13 botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_decaps.h | 1 botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_encaps.cpp | 13 botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_field_ordering.cpp | 16 botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_field_ordering.h | 4 botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_gf.cpp | 4 botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_gf.h | 5 botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_keys_internal.cpp | 3 botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_keys_internal.h | 8 botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_matrix.cpp | 29 botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_matrix.h | 4 botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_parameter_set.cpp | 2 botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_parameter_set.h | 5 botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_parameters.cpp | 2 botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_parameters.h | 5 botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_poly.cpp | 13 botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_poly.h | 8 botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_types.h | 2 botan3-3.12.0+dfsg/src/lib/pubkey/curve448/curve448_gf.cpp | 367 botan3-3.12.0+dfsg/src/lib/pubkey/curve448/curve448_gf.h | 32 botan3-3.12.0+dfsg/src/lib/pubkey/curve448/curve448_scalar.cpp | 52 botan3-3.12.0+dfsg/src/lib/pubkey/curve448/curve448_scalar.h | 11 botan3-3.12.0+dfsg/src/lib/pubkey/curve448/ed448/ed448.cpp | 34 botan3-3.12.0+dfsg/src/lib/pubkey/curve448/ed448/ed448.h | 6 botan3-3.12.0+dfsg/src/lib/pubkey/curve448/ed448/ed448_internal.cpp | 239 botan3-3.12.0+dfsg/src/lib/pubkey/curve448/ed448/ed448_internal.h | 20 botan3-3.12.0+dfsg/src/lib/pubkey/curve448/info.txt | 8 botan3-3.12.0+dfsg/src/lib/pubkey/curve448/x448/x448.cpp | 20 botan3-3.12.0+dfsg/src/lib/pubkey/curve448/x448/x448.h | 8 botan3-3.12.0+dfsg/src/lib/pubkey/curve448/x448/x448_internal.cpp | 20 botan3-3.12.0+dfsg/src/lib/pubkey/dh/dh.cpp | 12 botan3-3.12.0+dfsg/src/lib/pubkey/dh/dh.h | 10 botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp | 27 botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium.h | 23 botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.cpp | 74 botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.h | 1 botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_constants.cpp | 6 botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_constants.h | 18 botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_keys.h | 6 botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_polynomial.h | 10 botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_shake/dilithium_shake_xof.cpp | 25 botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_shake/dilithium_shake_xof.h | 31 botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_symmetric_primitives.cpp | 18 botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_symmetric_primitives.h | 77 botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/info.txt | 1 botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_round3/dilithium/dilithium_round3.h | 2 botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_round3/dilithium_aes/dilithium_aes.cpp | 18 botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_round3/dilithium_aes/dilithium_aes.h | 2 botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_round3/dilithium_round3_symmetric_primitives.cpp | 1 botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/ml_dsa/ml_dsa.h | 2 botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.h | 2 botan3-3.12.0+dfsg/src/lib/pubkey/dl_algo/dl_scheme.cpp | 16 botan3-3.12.0+dfsg/src/lib/pubkey/dl_group/dl_group.cpp | 180 botan3-3.12.0+dfsg/src/lib/pubkey/dl_group/dl_group.h | 56 botan3-3.12.0+dfsg/src/lib/pubkey/dlies/dlies.cpp | 27 botan3-3.12.0+dfsg/src/lib/pubkey/dlies/dlies.h | 2 botan3-3.12.0+dfsg/src/lib/pubkey/dsa/dsa.cpp | 23 botan3-3.12.0+dfsg/src/lib/pubkey/dsa/dsa.h | 2 botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_apoint.cpp | 39 botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_apoint.h | 54 botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_group.cpp | 358 botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_group.h | 120 botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_inner_data.cpp | 214 botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_inner_data.h | 49 botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_inner_pc.cpp | 78 botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_inner_pc.h | 16 botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_named.cpp | 48 botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_point_format.h | 22 botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_scalar.cpp | 38 botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_scalar.h | 34 botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/curve_gfp.h | 13 botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/ec_inner_bn.cpp | 50 botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/ec_inner_bn.h | 14 botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/ec_point.cpp | 92 botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/ec_point.h | 39 botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/point_mul.cpp | 82 botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/point_mul.h | 14 botan3-3.12.0+dfsg/src/lib/pubkey/ecc_key/ec_key_data.cpp | 32 botan3-3.12.0+dfsg/src/lib/pubkey/ecc_key/ec_key_data.h | 17 botan3-3.12.0+dfsg/src/lib/pubkey/ecc_key/ecc_key.cpp | 53 botan3-3.12.0+dfsg/src/lib/pubkey/ecc_key/ecc_key.h | 30 botan3-3.12.0+dfsg/src/lib/pubkey/ecdh/ecdh.cpp | 34 botan3-3.12.0+dfsg/src/lib/pubkey/ecdh/ecdh.h | 10 botan3-3.12.0+dfsg/src/lib/pubkey/ecdsa/ecdsa.cpp | 52 botan3-3.12.0+dfsg/src/lib/pubkey/ecdsa/ecdsa.h | 14 botan3-3.12.0+dfsg/src/lib/pubkey/ecgdsa/ecgdsa.cpp | 12 botan3-3.12.0+dfsg/src/lib/pubkey/ecgdsa/ecgdsa.h | 10 botan3-3.12.0+dfsg/src/lib/pubkey/ecies/ecies.cpp | 144 botan3-3.12.0+dfsg/src/lib/pubkey/ecies/ecies.h | 131 botan3-3.12.0+dfsg/src/lib/pubkey/eckcdsa/eckcdsa.cpp | 13 botan3-3.12.0+dfsg/src/lib/pubkey/eckcdsa/eckcdsa.h | 10 botan3-3.12.0+dfsg/src/lib/pubkey/ed25519/ed25519.cpp | 68 botan3-3.12.0+dfsg/src/lib/pubkey/ed25519/ed25519.h | 43 botan3-3.12.0+dfsg/src/lib/pubkey/ed25519/ed25519_fe.cpp | 219 botan3-3.12.0+dfsg/src/lib/pubkey/ed25519/ed25519_fe.h | 196 botan3-3.12.0+dfsg/src/lib/pubkey/ed25519/ed25519_internal.h | 40 botan3-3.12.0+dfsg/src/lib/pubkey/ed25519/ed25519_key.cpp | 79 botan3-3.12.0+dfsg/src/lib/pubkey/ed25519/ge.cpp | 951 -- botan3-3.12.0+dfsg/src/lib/pubkey/elgamal/elgamal.cpp | 26 botan3-3.12.0+dfsg/src/lib/pubkey/elgamal/elgamal.h | 4 botan3-3.12.0+dfsg/src/lib/pubkey/elgamal/info.txt | 2 botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_aes/frodo_aes_generator.h | 4 botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_constants.cpp | 6 botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_constants.h | 8 botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_matrix.cpp | 33 botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_matrix.h | 4 botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_mode.cpp | 25 botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_mode.h | 19 botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_types.h | 4 botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodokem.cpp | 242 botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodokem.h | 9 botan3-3.12.0+dfsg/src/lib/pubkey/gost_3410/gost_3410.cpp | 20 botan3-3.12.0+dfsg/src/lib/pubkey/gost_3410/gost_3410.h | 8 botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/hss.cpp | 92 botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/hss.h | 12 botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/hss_lms.cpp | 20 botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/hss_lms.h | 18 botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/hss_lms_utils.cpp | 2 botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/hss_lms_utils.h | 2 botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/info.txt | 1 botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/lm_ots.cpp | 16 botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/lm_ots.h | 17 botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/lms.cpp | 27 botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/lms.h | 10 botan3-3.12.0+dfsg/src/lib/pubkey/hybrid_kem/hybrid_kem.cpp | 82 botan3-3.12.0+dfsg/src/lib/pubkey/hybrid_kem/hybrid_kem.h | 135 botan3-3.12.0+dfsg/src/lib/pubkey/hybrid_kem/hybrid_kem_ops.cpp | 110 botan3-3.12.0+dfsg/src/lib/pubkey/hybrid_kem/hybrid_kem_ops.h | 140 botan3-3.12.0+dfsg/src/lib/pubkey/hybrid_kem/info.txt | 17 botan3-3.12.0+dfsg/src/lib/pubkey/info.txt | 2 botan3-3.12.0+dfsg/src/lib/pubkey/kex_to_kem_adapter/info.txt | 18 botan3-3.12.0+dfsg/src/lib/pubkey/kex_to_kem_adapter/kex_to_kem_adapter.cpp | 270 botan3-3.12.0+dfsg/src/lib/pubkey/kex_to_kem_adapter/kex_to_kem_adapter.h | 92 botan3-3.12.0+dfsg/src/lib/pubkey/keypair/keypair.cpp | 6 botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber.cpp | 50 botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber.h | 92 botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_algos.cpp | 10 botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_algos.h | 11 botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_constants.cpp | 18 botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_constants.h | 24 botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_encaps_base.h | 14 botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_helpers.h | 2 botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_keys.cpp | 82 botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_keys.h | 28 botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_polynomial.h | 19 botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_symmetric_primitives.h | 78 botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_round3/kyber/kyber_modern.h | 68 botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_round3/kyber_90s/kyber_90s.h | 52 botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_round3/kyber_round3_impl.cpp | 67 botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_round3/kyber_round3_impl.h | 10 botan3-3.12.0+dfsg/src/lib/pubkey/kyber/ml_kem/ml_kem_impl.cpp | 30 botan3-3.12.0+dfsg/src/lib/pubkey/kyber/ml_kem/ml_kem_impl.h | 67 botan3-3.12.0+dfsg/src/lib/pubkey/mce/code_based_key_gen.cpp | 22 botan3-3.12.0+dfsg/src/lib/pubkey/mce/gf2m_rootfind_dcmp.cpp | 89 botan3-3.12.0+dfsg/src/lib/pubkey/mce/gf2m_small_m.cpp | 8 botan3-3.12.0+dfsg/src/lib/pubkey/mce/gf2m_small_m.h | 12 botan3-3.12.0+dfsg/src/lib/pubkey/mce/goppa_code.cpp | 36 botan3-3.12.0+dfsg/src/lib/pubkey/mce/mce_workfactor.cpp | 2 botan3-3.12.0+dfsg/src/lib/pubkey/mce/mceliece.cpp | 12 botan3-3.12.0+dfsg/src/lib/pubkey/mce/mceliece.h | 10 botan3-3.12.0+dfsg/src/lib/pubkey/mce/mceliece_key.cpp | 119 botan3-3.12.0+dfsg/src/lib/pubkey/mce/polyn_gf2m.cpp | 162 botan3-3.12.0+dfsg/src/lib/pubkey/mce/polyn_gf2m.h | 5 botan3-3.12.0+dfsg/src/lib/pubkey/pbes2/pbes2.cpp | 57 botan3-3.12.0+dfsg/src/lib/pubkey/pbes2/pbes2.h | 1 botan3-3.12.0+dfsg/src/lib/pubkey/pem/pem.cpp | 32 botan3-3.12.0+dfsg/src/lib/pubkey/pk_algs.cpp | 14 botan3-3.12.0+dfsg/src/lib/pubkey/pk_algs.h | 15 botan3-3.12.0+dfsg/src/lib/pubkey/pk_keys.cpp | 17 botan3-3.12.0+dfsg/src/lib/pubkey/pk_keys.h | 47 botan3-3.12.0+dfsg/src/lib/pubkey/pk_ops.cpp | 34 botan3-3.12.0+dfsg/src/lib/pubkey/pk_ops.h | 17 botan3-3.12.0+dfsg/src/lib/pubkey/pk_ops_impl.h | 22 botan3-3.12.0+dfsg/src/lib/pubkey/pkcs8.cpp | 25 botan3-3.12.0+dfsg/src/lib/pubkey/pkcs8.h | 2 botan3-3.12.0+dfsg/src/lib/pubkey/pqcrystals/info.txt | 4 botan3-3.12.0+dfsg/src/lib/pubkey/pqcrystals/pqcrystals.h | 15 botan3-3.12.0+dfsg/src/lib/pubkey/pqcrystals/pqcrystals_encoding.h | 7 botan3-3.12.0+dfsg/src/lib/pubkey/pqcrystals/pqcrystals_helpers.h | 17 botan3-3.12.0+dfsg/src/lib/pubkey/pubkey.cpp | 84 botan3-3.12.0+dfsg/src/lib/pubkey/pubkey.h | 32 botan3-3.12.0+dfsg/src/lib/pubkey/rfc6979/info.txt | 4 botan3-3.12.0+dfsg/src/lib/pubkey/rfc6979/rfc6979.cpp | 12 botan3-3.12.0+dfsg/src/lib/pubkey/rfc6979/rfc6979.h | 6 botan3-3.12.0+dfsg/src/lib/pubkey/rsa/info.txt | 3 botan3-3.12.0+dfsg/src/lib/pubkey/rsa/rsa.cpp | 225 botan3-3.12.0+dfsg/src/lib/pubkey/rsa/rsa.h | 10 botan3-3.12.0+dfsg/src/lib/pubkey/sm2/sm2.cpp | 42 botan3-3.12.0+dfsg/src/lib/pubkey/sm2/sm2.h | 20 botan3-3.12.0+dfsg/src/lib/pubkey/sm2/sm2_enc.cpp | 46 botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_address.h | 19 botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_fors.cpp | 13 botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hash.cpp | 6 botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hash.h | 16 botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hypertree.cpp | 3 botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hypertree.h | 1 botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_parameters.cpp | 4 botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_parameters.h | 4 botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_treehash.cpp | 2 botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_types.h | 2 botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_wots.cpp | 9 botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_xmss.cpp | 6 botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp | 20 botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.h | 12 botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus_sha2_base/sp_hash_sha2.h | 4 botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus_shake_base/sp_hash_shake.h | 6 botan3-3.12.0+dfsg/src/lib/pubkey/stateful_key_index/info.txt | 9 botan3-3.12.0+dfsg/src/lib/pubkey/stateful_key_index/stateful_key_index_registry.cpp | 82 botan3-3.12.0+dfsg/src/lib/pubkey/stateful_key_index/stateful_key_index_registry.h | 97 botan3-3.12.0+dfsg/src/lib/pubkey/workfactor.cpp | 70 botan3-3.12.0+dfsg/src/lib/pubkey/x25519/donna.cpp | 64 botan3-3.12.0+dfsg/src/lib/pubkey/x25519/x25519.cpp | 6 botan3-3.12.0+dfsg/src/lib/pubkey/x25519/x25519.h | 10 botan3-3.12.0+dfsg/src/lib/pubkey/x509_key.cpp | 14 botan3-3.12.0+dfsg/src/lib/pubkey/x509_key.h | 1 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/atomic.h | 56 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/info.txt | 8 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss.h | 64 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_address.h | 26 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_common_ops.cpp | 6 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_common_ops.h | 10 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_hash.cpp | 1 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_hash.h | 2 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_index_registry.cpp | 71 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_index_registry.h | 99 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_parameters.cpp | 384 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_parameters.h | 130 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_privatekey.cpp | 171 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_publickey.cpp | 125 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_signature.cpp | 21 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_signature.h | 1 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_signature_operation.cpp | 61 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_signature_operation.h | 31 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_tools.h | 71 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_verification_operation.cpp | 16 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_verification_operation.h | 5 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_wots.cpp | 76 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_wots.h | 31 botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_wots_parameters.cpp | 156 botan3-3.12.0+dfsg/src/lib/rng/auto_rng/auto_rng.cpp | 10 botan3-3.12.0+dfsg/src/lib/rng/auto_rng/auto_rng.h | 21 botan3-3.12.0+dfsg/src/lib/rng/auto_rng/info.txt | 4 botan3-3.12.0+dfsg/src/lib/rng/chacha_rng/chacha_rng.cpp | 6 botan3-3.12.0+dfsg/src/lib/rng/chacha_rng/chacha_rng.h | 10 botan3-3.12.0+dfsg/src/lib/rng/esdm_rng/esdm_rng.cpp | 3 botan3-3.12.0+dfsg/src/lib/rng/esdm_rng/esdm_rng.h | 4 botan3-3.12.0+dfsg/src/lib/rng/hmac_drbg/hmac_drbg.cpp | 38 botan3-3.12.0+dfsg/src/lib/rng/hmac_drbg/hmac_drbg.h | 17 botan3-3.12.0+dfsg/src/lib/rng/jitter_rng/jitter_rng.cpp | 23 botan3-3.12.0+dfsg/src/lib/rng/jitter_rng/jitter_rng.h | 3 botan3-3.12.0+dfsg/src/lib/rng/processor_rng/info.txt | 8 botan3-3.12.0+dfsg/src/lib/rng/processor_rng/processor_rng.cpp | 47 botan3-3.12.0+dfsg/src/lib/rng/processor_rng/processor_rng.h | 5 botan3-3.12.0+dfsg/src/lib/rng/rng.cpp | 7 botan3-3.12.0+dfsg/src/lib/rng/rng.h | 94 botan3-3.12.0+dfsg/src/lib/rng/stateful_rng/stateful_rng.cpp | 27 botan3-3.12.0+dfsg/src/lib/rng/stateful_rng/stateful_rng.h | 12 botan3-3.12.0+dfsg/src/lib/rng/system_rng/info.txt | 3 botan3-3.12.0+dfsg/src/lib/rng/system_rng/system_rng.cpp | 9 botan3-3.12.0+dfsg/src/lib/stream/chacha/chacha.cpp | 69 botan3-3.12.0+dfsg/src/lib/stream/chacha/chacha_avx2/chacha_avx2.cpp | 5 botan3-3.12.0+dfsg/src/lib/stream/chacha/chacha_avx2/info.txt | 5 botan3-3.12.0+dfsg/src/lib/stream/chacha/chacha_avx512/chacha_avx512.cpp | 9 botan3-3.12.0+dfsg/src/lib/stream/chacha/chacha_avx512/info.txt | 5 botan3-3.12.0+dfsg/src/lib/stream/chacha/chacha_simd32/chacha_simd32.cpp | 7 botan3-3.12.0+dfsg/src/lib/stream/chacha/chacha_simd32/info.txt | 19 botan3-3.12.0+dfsg/src/lib/stream/ctr/ctr.cpp | 57 botan3-3.12.0+dfsg/src/lib/stream/ctr/ctr.h | 8 botan3-3.12.0+dfsg/src/lib/stream/ctr/ctr_avx2/ctr_avx2.cpp | 83 botan3-3.12.0+dfsg/src/lib/stream/ctr/ctr_avx2/info.txt | 17 botan3-3.12.0+dfsg/src/lib/stream/ctr/ctr_simd32/ctr_simd32.cpp | 89 botan3-3.12.0+dfsg/src/lib/stream/ctr/ctr_simd32/info.txt | 26 botan3-3.12.0+dfsg/src/lib/stream/ofb/ofb.cpp | 3 botan3-3.12.0+dfsg/src/lib/stream/rc4/rc4.cpp | 5 botan3-3.12.0+dfsg/src/lib/stream/rc4/rc4.h | 2 botan3-3.12.0+dfsg/src/lib/stream/salsa20/salsa20.cpp | 50 botan3-3.12.0+dfsg/src/lib/stream/shake_cipher/shake_cipher.cpp | 2 botan3-3.12.0+dfsg/src/lib/stream/stream_cipher.cpp | 7 botan3-3.12.0+dfsg/src/lib/stream/stream_cipher.h | 11 botan3-3.12.0+dfsg/src/lib/tls/asio/asio_async_ops.h | 32 botan3-3.12.0+dfsg/src/lib/tls/asio/asio_compat.h | 5 botan3-3.12.0+dfsg/src/lib/tls/asio/asio_context.cpp | 8 botan3-3.12.0+dfsg/src/lib/tls/asio/asio_context.h | 23 botan3-3.12.0+dfsg/src/lib/tls/asio/asio_error.h | 42 botan3-3.12.0+dfsg/src/lib/tls/asio/asio_stream.h | 134 botan3-3.12.0+dfsg/src/lib/tls/credentials_manager.cpp | 2 botan3-3.12.0+dfsg/src/lib/tls/credentials_manager.h | 24 botan3-3.12.0+dfsg/src/lib/tls/info.txt | 10 botan3-3.12.0+dfsg/src/lib/tls/msg_cert_req.cpp | 155 botan3-3.12.0+dfsg/src/lib/tls/msg_cert_status.cpp | 54 botan3-3.12.0+dfsg/src/lib/tls/msg_cert_verify.cpp | 153 botan3-3.12.0+dfsg/src/lib/tls/msg_client_hello.cpp | 965 -- botan3-3.12.0+dfsg/src/lib/tls/msg_finished.cpp | 88 botan3-3.12.0+dfsg/src/lib/tls/msg_server_hello.cpp | 756 - botan3-3.12.0+dfsg/src/lib/tls/msg_session_ticket.cpp | 140 botan3-3.12.0+dfsg/src/lib/tls/sessions_sql/tls_session_manager_sql.cpp | 22 botan3-3.12.0+dfsg/src/lib/tls/sessions_sql/tls_session_manager_sql.h | 6 botan3-3.12.0+dfsg/src/lib/tls/tls12/info.txt | 11 botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_cert_status.cpp | 72 botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_cert_status_12.cpp | 22 botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_cert_verify_12.cpp | 61 botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_certificate_12.cpp | 20 botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_certificate_req_12.cpp | 164 botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_client_hello_12.cpp | 297 botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_client_kex.cpp | 170 botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_finished_12.cpp | 59 botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_hello_verify.cpp | 8 botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_server_hello_12.cpp | 229 botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_server_kex.cpp | 68 botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_session_ticket_12.cpp | 46 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_cbc/tls_cbc.cpp | 70 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_cbc/tls_cbc.h | 67 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_channel_impl_12.cpp | 277 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_channel_impl_12.h | 32 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_client_impl_12.cpp | 215 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_client_impl_12.h | 8 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_connection_state_12.cpp | 47 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_connection_state_12.h | 105 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_extensions_12.cpp | 118 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_extensions_12.h | 155 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_handshake_io.cpp | 166 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_handshake_io.h | 50 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_handshake_state.cpp | 198 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_handshake_state.h | 54 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_messages_12.h | 424 + botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_null/info.txt | 19 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_null/tls_null.cpp | 154 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_null/tls_null.h | 112 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_record.cpp | 84 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_record.h | 25 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_seq_numbers.h | 25 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_server_impl_12.cpp | 296 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_server_impl_12.h | 14 botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_session_key.cpp | 39 botan3-3.12.0+dfsg/src/lib/tls/tls13/info.txt | 5 botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_cert_verify_13.cpp | 123 botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_certificate_13.cpp | 91 botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_certificate_req_13.cpp | 26 botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_client_hello_13.cpp | 512 + botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_encrypted_extensions.cpp | 68 botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_finished_13.cpp | 24 botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_key_update.cpp | 2 botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_server_hello_13.cpp | 412 + botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_session_ticket_13.cpp | 104 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_channel_impl_13.cpp | 48 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_channel_impl_13.h | 26 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_cipher_state.cpp | 87 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_cipher_state.h | 16 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_client_impl_13.cpp | 334 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_client_impl_13.h | 19 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_connection_state_13.cpp | 46 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_connection_state_13.h | 82 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_extensions_13.cpp | 170 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_extensions_13.h | 334 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_extensions_key_share.cpp | 105 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_extensions_psk.cpp | 69 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_handshake_layer_13.cpp | 50 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_handshake_layer_13.h | 11 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_handshake_state_13.cpp | 18 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_handshake_state_13.h | 36 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_messages_13.h | 475 + botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_psk_13.h | 120 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_psk_identity_13.cpp | 13 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_psk_identity_13.h | 64 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_psk_importer_13.cpp | 121 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_record_layer_13.cpp | 74 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_record_layer_13.h | 22 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_server_impl_13.cpp | 308 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_server_impl_13.h | 15 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_transcript_hash_13.cpp | 24 botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_transcript_hash_13.h | 21 botan3-3.12.0+dfsg/src/lib/tls/tls13_pqc/hybrid_public_key.cpp | 360 botan3-3.12.0+dfsg/src/lib/tls/tls13_pqc/hybrid_public_key.h | 51 botan3-3.12.0+dfsg/src/lib/tls/tls13_pqc/info.txt | 3 botan3-3.12.0+dfsg/src/lib/tls/tls13_pqc/kex_to_kem_adapter.cpp | 251 botan3-3.12.0+dfsg/src/lib/tls/tls13_pqc/kex_to_kem_adapter.h | 89 botan3-3.12.0+dfsg/src/lib/tls/tls_alert.h | 6 botan3-3.12.0+dfsg/src/lib/tls/tls_algos.cpp | 150 botan3-3.12.0+dfsg/src/lib/tls/tls_algos.h | 62 botan3-3.12.0+dfsg/src/lib/tls/tls_callbacks.cpp | 104 botan3-3.12.0+dfsg/src/lib/tls/tls_callbacks.h | 109 botan3-3.12.0+dfsg/src/lib/tls/tls_channel.h | 24 botan3-3.12.0+dfsg/src/lib/tls/tls_channel_impl.h | 26 botan3-3.12.0+dfsg/src/lib/tls/tls_ciphersuite.cpp | 138 botan3-3.12.0+dfsg/src/lib/tls/tls_ciphersuite.h | 22 botan3-3.12.0+dfsg/src/lib/tls/tls_client.cpp | 46 botan3-3.12.0+dfsg/src/lib/tls/tls_client.h | 14 botan3-3.12.0+dfsg/src/lib/tls/tls_exceptn.h | 2 botan3-3.12.0+dfsg/src/lib/tls/tls_extensions.cpp | 598 - botan3-3.12.0+dfsg/src/lib/tls/tls_extensions.h | 577 - botan3-3.12.0+dfsg/src/lib/tls/tls_extensions_cert_status_req.cpp | 29 botan3-3.12.0+dfsg/src/lib/tls/tls_external_psk.cpp | 21 botan3-3.12.0+dfsg/src/lib/tls/tls_external_psk.h | 26 botan3-3.12.0+dfsg/src/lib/tls/tls_handshake_transitions.cpp | 2 botan3-3.12.0+dfsg/src/lib/tls/tls_handshake_transitions.h | 3 botan3-3.12.0+dfsg/src/lib/tls/tls_magic.cpp | 82 botan3-3.12.0+dfsg/src/lib/tls/tls_magic.h | 59 botan3-3.12.0+dfsg/src/lib/tls/tls_messages.h | 910 -- botan3-3.12.0+dfsg/src/lib/tls/tls_messages_internal.h | 161 botan3-3.12.0+dfsg/src/lib/tls/tls_policy.cpp | 52 botan3-3.12.0+dfsg/src/lib/tls/tls_policy.h | 56 botan3-3.12.0+dfsg/src/lib/tls/tls_reader.cpp | 25 botan3-3.12.0+dfsg/src/lib/tls/tls_reader.h | 68 botan3-3.12.0+dfsg/src/lib/tls/tls_server.cpp | 32 botan3-3.12.0+dfsg/src/lib/tls/tls_server.h | 11 botan3-3.12.0+dfsg/src/lib/tls/tls_server_info.h | 11 botan3-3.12.0+dfsg/src/lib/tls/tls_session.cpp | 114 botan3-3.12.0+dfsg/src/lib/tls/tls_session.h | 219 botan3-3.12.0+dfsg/src/lib/tls/tls_session_id.h | 121 botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager.cpp | 117 botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager.h | 29 botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_hybrid.cpp | 19 botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_hybrid.h | 14 botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_memory.cpp | 27 botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_memory.h | 6 botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_noop.cpp | 16 botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_noop.h | 16 botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_stateless.cpp | 15 botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_stateless.h | 9 botan3-3.12.0+dfsg/src/lib/tls/tls_signature_scheme.cpp | 33 botan3-3.12.0+dfsg/src/lib/tls/tls_signature_scheme.h | 27 botan3-3.12.0+dfsg/src/lib/tls/tls_suite_info.cpp | 341 botan3-3.12.0+dfsg/src/lib/tls/tls_text_policy.cpp | 9 botan3-3.12.0+dfsg/src/lib/tls/tls_version.cpp | 35 botan3-3.12.0+dfsg/src/lib/tls/tls_version.h | 19 botan3-3.12.0+dfsg/src/lib/utils/alignment_buffer.h | 23 botan3-3.12.0+dfsg/src/lib/utils/allocator.cpp | 9 botan3-3.12.0+dfsg/src/lib/utils/allocator.h | 1 botan3-3.12.0+dfsg/src/lib/utils/api.h | 16 botan3-3.12.0+dfsg/src/lib/utils/assert.cpp | 11 botan3-3.12.0+dfsg/src/lib/utils/assert.h | 58 botan3-3.12.0+dfsg/src/lib/utils/bit_ops.h | 167 botan3-3.12.0+dfsg/src/lib/utils/bitvector/bitvector.h | 46 botan3-3.12.0+dfsg/src/lib/utils/bitvector/info.txt | 4 botan3-3.12.0+dfsg/src/lib/utils/boost/info.txt | 4 botan3-3.12.0+dfsg/src/lib/utils/bswap.h | 1 botan3-3.12.0+dfsg/src/lib/utils/buffer_slicer.h | 76 botan3-3.12.0+dfsg/src/lib/utils/buffer_stuffer.h | 79 botan3-3.12.0+dfsg/src/lib/utils/calendar.cpp | 17 botan3-3.12.0+dfsg/src/lib/utils/calendar.h | 4 botan3-3.12.0+dfsg/src/lib/utils/charset.cpp | 101 botan3-3.12.0+dfsg/src/lib/utils/charset.h | 30 botan3-3.12.0+dfsg/src/lib/utils/codec_base.h | 35 botan3-3.12.0+dfsg/src/lib/utils/compiler.h | 20 botan3-3.12.0+dfsg/src/lib/utils/concat_util.h | 121 botan3-3.12.0+dfsg/src/lib/utils/concepts.h | 141 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid.cpp | 216 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid.h | 399 - botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_aarch64.cpp | 193 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_aarch64/cpuid_aarch64.cpp | 192 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_aarch64/cpuid_features.cpp | 67 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_aarch64/cpuid_features.h | 51 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_aarch64/info.txt | 11 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_arm32.cpp | 57 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_arm32/cpuid_arm32.cpp | 55 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_arm32/cpuid_features.cpp | 47 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_arm32/cpuid_features.h | 46 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_arm32/info.txt | 11 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_loongarch64/cpuid_features.cpp | 38 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_loongarch64/cpuid_features.h | 42 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_loongarch64/cpuid_loongarch64.cpp | 41 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_loongarch64/info.txt | 11 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_ppc.cpp | 87 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_ppc/cpuid_features.cpp | 39 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_ppc/cpuid_features.h | 44 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_ppc/cpuid_ppc.cpp | 90 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_ppc/info.txt | 12 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_riscv64/cpuid_features.cpp | 62 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_riscv64/cpuid_features.h | 47 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_riscv64/cpuid_riscv64.cpp | 83 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_riscv64/info.txt | 11 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_wasm/cpuid_features.cpp | 31 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_wasm/cpuid_features.h | 42 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_wasm/cpuid_wasm.cpp | 30 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_wasm/info.txt | 11 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_x86.cpp | 225 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_x86/cpuid_features.cpp | 108 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_x86/cpuid_features.h | 65 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_x86/cpuid_x86.cpp | 224 botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_x86/info.txt | 13 botan3-3.12.0+dfsg/src/lib/utils/cpuid/info.txt | 22 botan3-3.12.0+dfsg/src/lib/utils/ct_utils.cpp | 4 botan3-3.12.0+dfsg/src/lib/utils/ct_utils.h | 229 botan3-3.12.0+dfsg/src/lib/utils/data_src.cpp | 45 botan3-3.12.0+dfsg/src/lib/utils/data_src.h | 28 botan3-3.12.0+dfsg/src/lib/utils/database.h | 6 botan3-3.12.0+dfsg/src/lib/utils/donna128.h | 90 botan3-3.12.0+dfsg/src/lib/utils/dyn_load/dyn_load.cpp | 53 botan3-3.12.0+dfsg/src/lib/utils/dyn_load/dyn_load.h | 38 botan3-3.12.0+dfsg/src/lib/utils/dyn_load/info.txt | 4 botan3-3.12.0+dfsg/src/lib/utils/exceptn.h | 47 botan3-3.12.0+dfsg/src/lib/utils/filesystem.cpp | 9 botan3-3.12.0+dfsg/src/lib/utils/gfni_utils.h | 51 botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash.cpp | 101 botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash.h | 33 botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash_avx512_clmul/ghash_avx512_clmul.cpp | 207 botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash_avx512_clmul/info.txt | 16 botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash_cpu/ghash_cpu.cpp | 263 botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash_cpu/info.txt | 9 botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash_cpu/polyval_fn.h | 141 botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash_vperm/ghash_vperm.cpp | 60 botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash_vperm/info.txt | 12 botan3-3.12.0+dfsg/src/lib/utils/ghash/info.txt | 4 botan3-3.12.0+dfsg/src/lib/utils/http_util/http_util.cpp | 37 botan3-3.12.0+dfsg/src/lib/utils/http_util/info.txt | 4 botan3-3.12.0+dfsg/src/lib/utils/info.txt | 23 botan3-3.12.0+dfsg/src/lib/utils/int_utils.h | 2 botan3-3.12.0+dfsg/src/lib/utils/ip_address/info.txt | 8 botan3-3.12.0+dfsg/src/lib/utils/ip_address/ipv4_address.cpp | 131 botan3-3.12.0+dfsg/src/lib/utils/ip_address/ipv4_address.h | 133 botan3-3.12.0+dfsg/src/lib/utils/ip_address/ipv6_address.cpp | 164 botan3-3.12.0+dfsg/src/lib/utils/ip_address/ipv6_address.h | 134 botan3-3.12.0+dfsg/src/lib/utils/isa_extn.h | 91 botan3-3.12.0+dfsg/src/lib/utils/loadstor.h | 180 botan3-3.12.0+dfsg/src/lib/utils/locking_allocator/info.txt | 4 botan3-3.12.0+dfsg/src/lib/utils/locking_allocator/locking_allocator.cpp | 8 botan3-3.12.0+dfsg/src/lib/utils/locking_allocator/locking_allocator.h | 7 botan3-3.12.0+dfsg/src/lib/utils/mem_ops.h | 49 botan3-3.12.0+dfsg/src/lib/utils/mem_pool/info.txt | 4 botan3-3.12.0+dfsg/src/lib/utils/mem_pool/mem_pool.cpp | 71 botan3-3.12.0+dfsg/src/lib/utils/mem_pool/mem_pool.h | 8 botan3-3.12.0+dfsg/src/lib/utils/mem_utils.cpp | 43 botan3-3.12.0+dfsg/src/lib/utils/mem_utils.h | 82 botan3-3.12.0+dfsg/src/lib/utils/mul128.h | 3 botan3-3.12.0+dfsg/src/lib/utils/mutex.h | 2 botan3-3.12.0+dfsg/src/lib/utils/os_utils/info.txt | 4 botan3-3.12.0+dfsg/src/lib/utils/os_utils/os_utils.cpp | 152 botan3-3.12.0+dfsg/src/lib/utils/os_utils/os_utils.h | 7 botan3-3.12.0+dfsg/src/lib/utils/parsing.cpp | 295 botan3-3.12.0+dfsg/src/lib/utils/parsing.h | 29 botan3-3.12.0+dfsg/src/lib/utils/poly_dbl/info.txt | 4 botan3-3.12.0+dfsg/src/lib/utils/poly_dbl/poly_dbl.cpp | 6 botan3-3.12.0+dfsg/src/lib/utils/poly_dbl/poly_dbl.h | 7 botan3-3.12.0+dfsg/src/lib/utils/prefetch.h | 8 botan3-3.12.0+dfsg/src/lib/utils/range_concepts.h | 119 botan3-3.12.0+dfsg/src/lib/utils/read_cfg.cpp | 1 botan3-3.12.0+dfsg/src/lib/utils/read_kv.cpp | 10 botan3-3.12.0+dfsg/src/lib/utils/rotate.h | 42 botan3-3.12.0+dfsg/src/lib/utils/rounding.h | 1 botan3-3.12.0+dfsg/src/lib/utils/scan_name.cpp | 4 botan3-3.12.0+dfsg/src/lib/utils/scoped_cleanup.h | 58 botan3-3.12.0+dfsg/src/lib/utils/simd/info.txt | 31 botan3-3.12.0+dfsg/src/lib/utils/simd/simd_2x64/info.txt | 28 botan3-3.12.0+dfsg/src/lib/utils/simd/simd_2x64/simd_2x64.h | 326 botan3-3.12.0+dfsg/src/lib/utils/simd/simd_32.h | 640 - botan3-3.12.0+dfsg/src/lib/utils/simd/simd_4x32/info.txt | 34 botan3-3.12.0+dfsg/src/lib/utils/simd/simd_4x32/simd_4x32.h | 970 ++ botan3-3.12.0+dfsg/src/lib/utils/simd/simd_4x64/info.txt | 21 botan3-3.12.0+dfsg/src/lib/utils/simd/simd_4x64/simd_4x64.h | 206 botan3-3.12.0+dfsg/src/lib/utils/simd/simd_8x64/info.txt | 21 botan3-3.12.0+dfsg/src/lib/utils/simd/simd_8x64/simd_8x64.h | 193 botan3-3.12.0+dfsg/src/lib/utils/simd/simd_avx2/info.txt | 4 botan3-3.12.0+dfsg/src/lib/utils/simd/simd_avx2/simd_avx2.h | 198 botan3-3.12.0+dfsg/src/lib/utils/simd/simd_avx2/simd_avx2_gfni.h | 48 botan3-3.12.0+dfsg/src/lib/utils/simd/simd_avx512/info.txt | 5 botan3-3.12.0+dfsg/src/lib/utils/simd/simd_avx512/simd_avx512.h | 116 botan3-3.12.0+dfsg/src/lib/utils/simd/simd_avx512/simd_avx512_gfni.h | 29 botan3-3.12.0+dfsg/src/lib/utils/simd/simd_hwaes/info.txt | 29 botan3-3.12.0+dfsg/src/lib/utils/simd/simd_hwaes/simd_hwaes.h | 170 botan3-3.12.0+dfsg/src/lib/utils/socket/info.txt | 4 botan3-3.12.0+dfsg/src/lib/utils/socket/socket.cpp | 123 botan3-3.12.0+dfsg/src/lib/utils/socket/socket.h | 8 botan3-3.12.0+dfsg/src/lib/utils/socket/socket_udp.cpp | 121 botan3-3.12.0+dfsg/src/lib/utils/socket/socket_udp.h | 5 botan3-3.12.0+dfsg/src/lib/utils/socket/uri.cpp | 16 botan3-3.12.0+dfsg/src/lib/utils/socket/uri.h | 3 botan3-3.12.0+dfsg/src/lib/utils/sqlite3/info.txt | 4 botan3-3.12.0+dfsg/src/lib/utils/sqlite3/sqlite3.cpp | 25 botan3-3.12.0+dfsg/src/lib/utils/sqlite3/sqlite3.h | 14 botan3-3.12.0+dfsg/src/lib/utils/stack_scrubbing.h | 37 botan3-3.12.0+dfsg/src/lib/utils/stl_util.h | 361 botan3-3.12.0+dfsg/src/lib/utils/strong_type.h | 75 botan3-3.12.0+dfsg/src/lib/utils/thread_utils/barrier.cpp | 3 botan3-3.12.0+dfsg/src/lib/utils/thread_utils/barrier.h | 4 botan3-3.12.0+dfsg/src/lib/utils/thread_utils/info.txt | 4 botan3-3.12.0+dfsg/src/lib/utils/thread_utils/rwlock.cpp | 12 botan3-3.12.0+dfsg/src/lib/utils/thread_utils/rwlock.h | 4 botan3-3.12.0+dfsg/src/lib/utils/thread_utils/semaphore.cpp | 4 botan3-3.12.0+dfsg/src/lib/utils/thread_utils/semaphore.h | 4 botan3-3.12.0+dfsg/src/lib/utils/thread_utils/thread_pool.cpp | 28 botan3-3.12.0+dfsg/src/lib/utils/thread_utils/thread_pool.h | 12 botan3-3.12.0+dfsg/src/lib/utils/time_utils.h | 6 botan3-3.12.0+dfsg/src/lib/utils/tree_hash/info.txt | 4 botan3-3.12.0+dfsg/src/lib/utils/tree_hash/tree_hash.h | 12 botan3-3.12.0+dfsg/src/lib/utils/types.h | 31 botan3-3.12.0+dfsg/src/lib/utils/uuid/info.txt | 4 botan3-3.12.0+dfsg/src/lib/utils/uuid/uuid.cpp | 5 botan3-3.12.0+dfsg/src/lib/utils/uuid/uuid.h | 11 botan3-3.12.0+dfsg/src/lib/utils/value_barrier.h | 68 botan3-3.12.0+dfsg/src/lib/utils/version.cpp | 75 botan3-3.12.0+dfsg/src/lib/utils/version.h | 39 botan3-3.12.0+dfsg/src/lib/x509/alt_name.cpp | 47 botan3-3.12.0+dfsg/src/lib/x509/asn1_alt_name.cpp | 8 botan3-3.12.0+dfsg/src/lib/x509/cert_status.cpp | 12 botan3-3.12.0+dfsg/src/lib/x509/certstor.cpp | 183 botan3-3.12.0+dfsg/src/lib/x509/certstor.h | 59 botan3-3.12.0+dfsg/src/lib/x509/certstor_flatfile/certstor_flatfile.cpp | 62 botan3-3.12.0+dfsg/src/lib/x509/certstor_flatfile/certstor_flatfile.h | 13 botan3-3.12.0+dfsg/src/lib/x509/certstor_sql/certstor_sql.cpp | 55 botan3-3.12.0+dfsg/src/lib/x509/certstor_sql/certstor_sql.h | 16 botan3-3.12.0+dfsg/src/lib/x509/certstor_system/certstor_system.cpp | 10 botan3-3.12.0+dfsg/src/lib/x509/certstor_system/certstor_system.h | 5 botan3-3.12.0+dfsg/src/lib/x509/certstor_system_macos/certstor_macos.cpp | 56 botan3-3.12.0+dfsg/src/lib/x509/certstor_system_macos/certstor_macos.h | 12 botan3-3.12.0+dfsg/src/lib/x509/certstor_system_windows/certstor_windows.cpp | 494 - botan3-3.12.0+dfsg/src/lib/x509/certstor_system_windows/certstor_windows.h | 26 botan3-3.12.0+dfsg/src/lib/x509/crl_ent.cpp | 40 botan3-3.12.0+dfsg/src/lib/x509/info.txt | 6 botan3-3.12.0+dfsg/src/lib/x509/key_constraint.cpp | 20 botan3-3.12.0+dfsg/src/lib/x509/name_constraint.cpp | 295 botan3-3.12.0+dfsg/src/lib/x509/ocsp.cpp | 316 botan3-3.12.0+dfsg/src/lib/x509/ocsp.h | 38 botan3-3.12.0+dfsg/src/lib/x509/ocsp_types.cpp | 103 botan3-3.12.0+dfsg/src/lib/x509/pkcs10.cpp | 60 botan3-3.12.0+dfsg/src/lib/x509/pkcs10.h | 19 botan3-3.12.0+dfsg/src/lib/x509/pkix_enums.h | 44 botan3-3.12.0+dfsg/src/lib/x509/pkix_types.h | 143 botan3-3.12.0+dfsg/src/lib/x509/x509_ca.cpp | 17 botan3-3.12.0+dfsg/src/lib/x509/x509_ca.h | 4 botan3-3.12.0+dfsg/src/lib/x509/x509_cert_cache.cpp | 57 botan3-3.12.0+dfsg/src/lib/x509/x509_cert_cache.h | 87 botan3-3.12.0+dfsg/src/lib/x509/x509_crl.cpp | 133 botan3-3.12.0+dfsg/src/lib/x509/x509_crl.h | 53 botan3-3.12.0+dfsg/src/lib/x509/x509_dn.cpp | 172 botan3-3.12.0+dfsg/src/lib/x509/x509_dn_ub.cpp | 102 botan3-3.12.0+dfsg/src/lib/x509/x509_ext.cpp | 1332 +++ botan3-3.12.0+dfsg/src/lib/x509/x509_ext.h | 523 + botan3-3.12.0+dfsg/src/lib/x509/x509_obj.cpp | 47 botan3-3.12.0+dfsg/src/lib/x509/x509_obj.h | 30 botan3-3.12.0+dfsg/src/lib/x509/x509_utils.h | 37 botan3-3.12.0+dfsg/src/lib/x509/x509cert.cpp | 249 botan3-3.12.0+dfsg/src/lib/x509/x509cert.h | 113 botan3-3.12.0+dfsg/src/lib/x509/x509opt.cpp | 5 botan3-3.12.0+dfsg/src/lib/x509/x509path.cpp | 782 +- botan3-3.12.0+dfsg/src/lib/x509/x509path.h | 65 botan3-3.12.0+dfsg/src/lib/x509/x509self.cpp | 13 botan3-3.12.0+dfsg/src/lib/x509/x509self.h | 57 botan3-3.12.0+dfsg/src/lib/xof/aes_crystals_xof/aes_crystals_xof.h | 4 botan3-3.12.0+dfsg/src/lib/xof/aes_crystals_xof/info.txt | 4 botan3-3.12.0+dfsg/src/lib/xof/ascon_xof128/ascon_xof128.cpp | 64 botan3-3.12.0+dfsg/src/lib/xof/ascon_xof128/ascon_xof128.h | 48 botan3-3.12.0+dfsg/src/lib/xof/ascon_xof128/info.txt | 11 botan3-3.12.0+dfsg/src/lib/xof/cshake_xof/cshake_xof.cpp | 12 botan3-3.12.0+dfsg/src/lib/xof/cshake_xof/cshake_xof.h | 12 botan3-3.12.0+dfsg/src/lib/xof/shake_xof/shake_xof.cpp | 5 botan3-3.12.0+dfsg/src/lib/xof/shake_xof/shake_xof.h | 4 botan3-3.12.0+dfsg/src/lib/xof/xof.cpp | 12 botan3-3.12.0+dfsg/src/lib/xof/xof.h | 12 botan3-3.12.0+dfsg/src/python/botan3.py | 1914 +++- botan3-3.12.0+dfsg/src/scripts/Dockerfile.android | 17 botan3-3.12.0+dfsg/src/scripts/acvp_tests.py | 2954 +++++++ botan3-3.12.0+dfsg/src/scripts/bench.py | 38 botan3-3.12.0+dfsg/src/scripts/build_docs.py | 8 botan3-3.12.0+dfsg/src/scripts/ci/ci_tlsanvil_check.py | 199 botan3-3.12.0+dfsg/src/scripts/ci/ci_tlsanvil_test.py | 139 botan3-3.12.0+dfsg/src/scripts/ci/cmake_tests/CMakeLists.txt | 19 botan3-3.12.0+dfsg/src/scripts/ci/download_ci_dep.py | 115 botan3-3.12.0+dfsg/src/scripts/ci/gh_clang_tidy_fixes_in_pr.py | 12 botan3-3.12.0+dfsg/src/scripts/ci/gh_get_changes_in_pr.py | 80 botan3-3.12.0+dfsg/src/scripts/ci/gha_linux_packages.py | 162 botan3-3.12.0+dfsg/src/scripts/ci/setup_gh_actions.ps1 | 23 botan3-3.12.0+dfsg/src/scripts/ci/setup_gh_actions.sh | 163 botan3-3.12.0+dfsg/src/scripts/ci/setup_gh_actions_after_ccache.sh | 26 botan3-3.12.0+dfsg/src/scripts/ci/setup_gh_actions_after_vcvars.ps1 | 2 botan3-3.12.0+dfsg/src/scripts/ci/start_tpm2_simulator.sh | 77 botan3-3.12.0+dfsg/src/scripts/ci_build.py | 346 botan3-3.12.0+dfsg/src/scripts/ci_check_generated_files.py | 123 botan3-3.12.0+dfsg/src/scripts/ci_check_install.py | 2 botan3-3.12.0+dfsg/src/scripts/ci_report_sizes.py | 51 botan3-3.12.0+dfsg/src/scripts/compare_perf.py | 181 botan3-3.12.0+dfsg/src/scripts/dev_tools/addchain.py | 100 botan3-3.12.0+dfsg/src/scripts/dev_tools/analyze_timing_results.py | 2 botan3-3.12.0+dfsg/src/scripts/dev_tools/file_size_check.py | 62 botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_dilithium_kat.py | 4 botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_ec_groups.py | 210 botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_frodo_kat.py | 11 botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_kyber_kat.py | 7 botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_mlkem_acvp_kat.py | 2 botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_mp_comba.py | 1 botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_mp_monty.py | 54 botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_oids.py | 259 botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_os_features.py | 13 botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_pqc_dsa_kats.py | 2 botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_sphincsplus_kat.py | 8 botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_tls_suite_info.py | 164 botan3-3.12.0+dfsg/src/scripts/dev_tools/mychain_creater.sh | 224 botan3-3.12.0+dfsg/src/scripts/dev_tools/mychain_creator.sh | 222 botan3-3.12.0+dfsg/src/scripts/dev_tools/randombit_ocsp_forger.sh | 22 botan3-3.12.0+dfsg/src/scripts/dev_tools/run_clang_format.py | 47 botan3-3.12.0+dfsg/src/scripts/dev_tools/run_clang_tidy.py | 295 botan3-3.12.0+dfsg/src/scripts/dev_tools/show_dependencies.py | 4 botan3-3.12.0+dfsg/src/scripts/dist.py | 2 botan3-3.12.0+dfsg/src/scripts/docker-android.sh | 11 botan3-3.12.0+dfsg/src/scripts/gdb/strubtest.py | 179 botan3-3.12.0+dfsg/src/scripts/install.py | 6 botan3-3.12.0+dfsg/src/scripts/run_limbo_tests.py | 30 botan3-3.12.0+dfsg/src/scripts/run_tests_under_valgrind.py | 138 botan3-3.12.0+dfsg/src/scripts/run_tls_attacker.py | 6 botan3-3.12.0+dfsg/src/scripts/run_tls_fuzzer.py | 2 botan3-3.12.0+dfsg/src/scripts/test_cli.py | 161 botan3-3.12.0+dfsg/src/scripts/test_cli_crypt.py | 2 botan3-3.12.0+dfsg/src/scripts/test_fuzzers.py | 6 botan3-3.12.0+dfsg/src/scripts/test_python.py | 543 + botan3-3.12.0+dfsg/src/scripts/test_strubbed_symbols.py | 165 botan3-3.12.0+dfsg/src/scripts/tls_anvil/analyze_tls_anvil_report.py | 250 botan3-3.12.0+dfsg/src/scripts/tls_anvil/anvil_policy.txt | 13 botan3-3.12.0+dfsg/src/scripts/tls_anvil/run_tls_anvil_tests.py | 172 botan3-3.12.0+dfsg/src/scripts/tls_anvil/tls_anvil_trigger_server.py | 184 botan3-3.12.0+dfsg/src/scripts/tls_scanner/tls_scanner.py | 4 botan3-3.12.0+dfsg/src/scripts/wycheproof.py | 1831 ++++ botan3-3.12.0+dfsg/src/tests/data/aead/ascon_aead128.vec | 730 + botan3-3.12.0+dfsg/src/tests/data/aead/chacha20poly1305.vec | 948 ++ botan3-3.12.0+dfsg/src/tests/data/aead/gcm.vec | 71 botan3-3.12.0+dfsg/src/tests/data/argon2.vec | 2 botan3-3.12.0+dfsg/src/tests/data/asn1_decoding.vec | 252 botan3-3.12.0+dfsg/src/tests/data/asn1_oid_invalid.vec | 4 botan3-3.12.0+dfsg/src/tests/data/asn1_print/output7.txt | 14 botan3-3.12.0+dfsg/src/tests/data/asn1_string_validation.vec | 56 botan3-3.12.0+dfsg/src/tests/data/block/aes.vec | 32 botan3-3.12.0+dfsg/src/tests/data/block/aria.vec | 18 botan3-3.12.0+dfsg/src/tests/data/block/blowfish.vec | 5 botan3-3.12.0+dfsg/src/tests/data/block/camellia.vec | 17 botan3-3.12.0+dfsg/src/tests/data/block/des.vec | 269 botan3-3.12.0+dfsg/src/tests/data/block/noekeon.vec | 2 botan3-3.12.0+dfsg/src/tests/data/block/seed.vec | 7 botan3-3.12.0+dfsg/src/tests/data/block/serpent.vec | 2 botan3-3.12.0+dfsg/src/tests/data/block/shacal2.vec | 2 botan3-3.12.0+dfsg/src/tests/data/block/sm4.vec | 20 botan3-3.12.0+dfsg/src/tests/data/block/twofish.vec | 1547 --- botan3-3.12.0+dfsg/src/tests/data/bn/divide.vec | 12 botan3-3.12.0+dfsg/src/tests/data/bn/from_radix.vec | 233 botan3-3.12.0+dfsg/src/tests/data/bn/lshift.vec | 5 botan3-3.12.0+dfsg/src/tests/data/bn/mod.vec | 4 botan3-3.12.0+dfsg/src/tests/data/bn/rshift.vec | 5 botan3-3.12.0+dfsg/src/tests/data/charset.vec | 184 botan3-3.12.0+dfsg/src/tests/data/codec/base58.vec | 67 botan3-3.12.0+dfsg/src/tests/data/hash/ascon_hash256.vec | 325 botan3-3.12.0+dfsg/src/tests/data/hash/blake2b.vec | 3885 +++------- botan3-3.12.0+dfsg/src/tests/data/hash/sha1.vec | 8 botan3-3.12.0+dfsg/src/tests/data/hash/sha2_32.vec | 10 botan3-3.12.0+dfsg/src/tests/data/hash/sha2_64.vec | 2 botan3-3.12.0+dfsg/src/tests/data/hash/sha3.vec | 2 botan3-3.12.0+dfsg/src/tests/data/hash/shake.vec | 2 botan3-3.12.0+dfsg/src/tests/data/hash/sm3.vec | 2 botan3-3.12.0+dfsg/src/tests/data/hash/whirlpool.vec | 14 botan3-3.12.0+dfsg/src/tests/data/hostnames.vec | 18 botan3-3.12.0+dfsg/src/tests/data/kdf/hkdf.vec | 1 botan3-3.12.0+dfsg/src/tests/data/mac/cmac.vec | 6 botan3-3.12.0+dfsg/src/tests/data/mac/gmac.vec | 645 + botan3-3.12.0+dfsg/src/tests/data/mac/poly1305.vec | 36 botan3-3.12.0+dfsg/src/tests/data/modes/xts.vec | 7 botan3-3.12.0+dfsg/src/tests/data/pubkey/ec_h2s.vec | 98 botan3-3.12.0+dfsg/src/tests/data/pubkey/ecc_explicit_curve.vec | 63 botan3-3.12.0+dfsg/src/tests/data/pubkey/ecdsa_explicit.vec | 38 botan3-3.12.0+dfsg/src/tests/data/pubkey/ecdsa_keygen.vec | 6 botan3-3.12.0+dfsg/src/tests/data/pubkey/ecdsa_verify.vec | 117 botan3-3.12.0+dfsg/src/tests/data/pubkey/ecies.vec | 8 botan3-3.12.0+dfsg/src/tests/data/pubkey/eckcdsa.vec | 2 botan3-3.12.0+dfsg/src/tests/data/pubkey/frodokem_kat.vec | 2 botan3-3.12.0+dfsg/src/tests/data/pubkey/hss_lms_sig.vec | 18 botan3-3.12.0+dfsg/src/tests/data/pubkey/kyber_encodings.vec | 22 botan3-3.12.0+dfsg/src/tests/data/pubkey/kyber_kat.vec | 2 botan3-3.12.0+dfsg/src/tests/data/pubkey/rsa_verify.vec | 12 botan3-3.12.0+dfsg/src/tests/data/pubkey/sm2_invalid.vec | 60 botan3-3.12.0+dfsg/src/tests/data/pubkey/workfactor.vec | 21 botan3-3.12.0+dfsg/src/tests/data/roughtime/roughtime_response.vec | 4 botan3-3.12.0+dfsg/src/tests/data/stream/chacha.vec | 2 botan3-3.12.0+dfsg/src/tests/data/stream/ctr.vec | 7 botan3-3.12.0+dfsg/src/tests/data/tls-policy/compat.txt | 2 botan3-3.12.0+dfsg/src/tests/data/tls-policy/datagram.txt | 4 botan3-3.12.0+dfsg/src/tests/data/tls-policy/default.txt | 4 botan3-3.12.0+dfsg/src/tests/data/tls-policy/default_tls13.txt | 4 botan3-3.12.0+dfsg/src/tests/data/tls-policy/strict.txt | 2 botan3-3.12.0+dfsg/src/tests/data/tls-policy/strict_tls13.txt | 4 botan3-3.12.0+dfsg/src/tests/data/tls_13/client_hello.vec | 12 botan3-3.12.0+dfsg/src/tests/data/tls_13_psk_import.vec | 47 botan3-3.12.0+dfsg/src/tests/data/tls_13_rfc8448/transcripts.vec | 4 botan3-3.12.0+dfsg/src/tests/data/tls_cbc_kat.vec | 123 botan3-3.12.0+dfsg/src/tests/data/tls_extensions/generation/key_share_CH_offers.vec | 18 botan3-3.12.0+dfsg/src/tests/data/tls_extensions/parsing/alpn.vec | 32 botan3-3.12.0+dfsg/src/tests/data/tls_extensions/parsing/cookie.vec | 23 botan3-3.12.0+dfsg/src/tests/data/tls_extensions/parsing/key_share_CH.vec | 6 botan3-3.12.0+dfsg/src/tests/data/tls_extensions/parsing/key_share_SH.vec | 2 botan3-3.12.0+dfsg/src/tests/data/tls_extensions/parsing/signature_algorithms_cert.vec | 4 botan3-3.12.0+dfsg/src/tests/data/tls_extensions/parsing/supported_groups.vec | 4 botan3-3.12.0+dfsg/src/tests/data/tls_null.vec | 32 botan3-3.12.0+dfsg/src/tests/data/utils/dns.vec | 64 botan3-3.12.0+dfsg/src/tests/data/utils/ipv6.vec | 60 botan3-3.12.0+dfsg/src/tests/data/utils/ipv6_nc.vec | 46 botan3-3.12.0+dfsg/src/tests/data/x509/bsi/expected.txt | 6 botan3-3.12.0+dfsg/src/tests/data/x509/crl/ca.crt | 12 botan3-3.12.0+dfsg/src/tests/data/x509/crl/sub1.crt | 12 botan3-3.12.0+dfsg/src/tests/data/x509/crl/sub2.crt | 12 botan3-3.12.0+dfsg/src/tests/data/x509/cve_2026_35580/end_entity.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/cve_2026_35580/root.pem | 22 botan3-3.12.0+dfsg/src/tests/data/x509/ecc/nodompar_private.pkcs8.pem | 12 botan3-3.12.0+dfsg/src/tests/data/x509/ecc/withdompar_private.pkcs8.pem | 5 botan3-3.12.0+dfsg/src/tests/data/x509/general_name_ip.vec | 64 botan3-3.12.0+dfsg/src/tests/data/x509/misc/contains_any_extended_key_usage.pem | 16 botan3-3.12.0+dfsg/src/tests/data/x509/misc/contains_multiple_ocsp_responders.pem | 55 botan3-3.12.0+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/01.pem | 15 botan3-3.12.0+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/42.pem | 15 botan3-3.12.0+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/README.md | 21 botan3-3.12.0+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/ca.pem | 15 botan3-3.12.0+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/valid_forever.crl | 12 botan3-3.12.0+dfsg/src/tests/data/x509/misc/multiple_alternative_names.pem | 65 botan3-3.12.0+dfsg/src/tests/data/x509/misc/no_alternative_names.pem | 13 botan3-3.12.0+dfsg/src/tests/data/x509/misc/self-signed-end-entity.pem | 11 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint/Invalid_DNS_Excluded_Mixed_Case_CN.crt | 19 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint/Root_DNS_Excluded_Mixed_Case_CN.crt | 19 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_empty_subject/intermediate.pem | 21 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_empty_subject/leaf.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_empty_subject/root.pem | 19 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v4only/leaf_invalid.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v4only/leaf_valid.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v4only/root.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v6only/leaf_invalid.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v6only/leaf_valid.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v6only/root.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/excluded/leaf_invalid.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/excluded/leaf_valid.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/excluded/root.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/int.pem | 22 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_excl_v4.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_excl_v6.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_int_v4.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_int_v6.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_root_v4.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_root_v6.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_valid.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/root.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/permitted/leaf_invalid.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/permitted/leaf_valid.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/permitted/root.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_exclude_mapped/leaf_invalid.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_exclude_mapped/leaf_valid.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_exclude_mapped/root.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_permit_mapped/leaf_invalid.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_permit_mapped/leaf_valid.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_permit_mapped/root.pem | 20 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_ca_accepted.pem | 15 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_ca_not_accepted.pem | 15 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_im_accepted.pem | 15 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_im_not_accepted.pem | 15 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_subject_accepted.pem | 13 botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_subject_not_accepted.pem | 13 botan3-3.12.0+dfsg/src/tests/data/x509/nist/expected.txt | 6 botan3-3.12.0+dfsg/src/tests/data/x509/ocsp/byKey_responder.pem | 19 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/end01.pem | 12 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/end02.pem | 12 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/end03.pem | 12 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/end04.pem | 12 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/end05.pem | 12 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/end06.pem | 12 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/end07.pem | 12 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/expected.txt | 7 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level1_0.pem | 12 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level1_1.pem | 12 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level1_2.pem | 12 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level1_3.pem | 12 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level1_4.pem | 12 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level1_5.pem | 12 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level1_6.pem | 12 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level2_0.pem | 12 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level2_1.pem | 12 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level2_2.pem | 12 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level2_3.pem | 12 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level2_4.pem | 12 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level2_5.pem | 12 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level2_6.pem | 12 botan3-3.12.0+dfsg/src/tests/data/x509/path_building/root.pem | 11 botan3-3.12.0+dfsg/src/tests/data/x509/pss_certs/expected.txt | 42 botan3-3.12.0+dfsg/src/tests/data/x509/x509test/ASNumberCert.pem | 11 botan3-3.12.0+dfsg/src/tests/data/x509/x509test/ASNumberInherit.pem | 11 botan3-3.12.0+dfsg/src/tests/data/x509/x509test/ASNumberOnly.pem | 11 botan3-3.12.0+dfsg/src/tests/data/x509/x509test/ASRdiOnly.pem | 11 botan3-3.12.0+dfsg/src/tests/data/x509/x509test/IPAddrBlocksAll.pem | 23 botan3-3.12.0+dfsg/src/tests/data/x509/x509test/IPAddrBlocksUnsorted.pem | 17 botan3-3.12.0+dfsg/src/tests/data/x509/x509test/InvalidIPAddrBlocks.pem | 15 botan3-3.12.0+dfsg/src/tests/data/x509_dn.vec | 8 botan3-3.12.0+dfsg/src/tests/data/xof/ascon_xof128.vec | 326 botan3-3.12.0+dfsg/src/tests/data/zfec.vec | 2 botan3-3.12.0+dfsg/src/tests/main.cpp | 18 botan3-3.12.0+dfsg/src/tests/runner/test_reporter.cpp | 25 botan3-3.12.0+dfsg/src/tests/runner/test_reporter.h | 54 botan3-3.12.0+dfsg/src/tests/runner/test_runner.cpp | 62 botan3-3.12.0+dfsg/src/tests/runner/test_runner.h | 5 botan3-3.12.0+dfsg/src/tests/runner/test_stdout_reporter.cpp | 4 botan3-3.12.0+dfsg/src/tests/runner/test_stdout_reporter.h | 6 botan3-3.12.0+dfsg/src/tests/runner/test_xml_reporter.cpp | 53 botan3-3.12.0+dfsg/src/tests/test_aead.cpp | 191 botan3-3.12.0+dfsg/src/tests/test_alt_name.cpp | 38 botan3-3.12.0+dfsg/src/tests/test_arb_eq.h | 128 botan3-3.12.0+dfsg/src/tests/test_asn1.cpp | 385 botan3-3.12.0+dfsg/src/tests/test_bigint.cpp | 338 botan3-3.12.0+dfsg/src/tests/test_block.cpp | 153 botan3-3.12.0+dfsg/src/tests/test_blowfish.cpp | 6 botan3-3.12.0+dfsg/src/tests/test_bufcomp.cpp | 28 botan3-3.12.0+dfsg/src/tests/test_certstor.cpp | 114 botan3-3.12.0+dfsg/src/tests/test_certstor_flatfile.cpp | 93 botan3-3.12.0+dfsg/src/tests/test_certstor_system.cpp | 105 botan3-3.12.0+dfsg/src/tests/test_certstor_utils.cpp | 7 botan3-3.12.0+dfsg/src/tests/test_certstor_utils.h | 1 botan3-3.12.0+dfsg/src/tests/test_cmce.cpp | 66 botan3-3.12.0+dfsg/src/tests/test_codec.cpp | 30 botan3-3.12.0+dfsg/src/tests/test_compression.cpp | 111 botan3-3.12.0+dfsg/src/tests/test_concurrent_pk.cpp | 516 + botan3-3.12.0+dfsg/src/tests/test_cryptobox.cpp | 9 botan3-3.12.0+dfsg/src/tests/test_crystals.cpp | 229 botan3-3.12.0+dfsg/src/tests/test_ct_utils.cpp | 155 botan3-3.12.0+dfsg/src/tests/test_dh.cpp | 14 botan3-3.12.0+dfsg/src/tests/test_dilithium.cpp | 98 botan3-3.12.0+dfsg/src/tests/test_dl_group.cpp | 109 botan3-3.12.0+dfsg/src/tests/test_dlies.cpp | 29 botan3-3.12.0+dfsg/src/tests/test_ec_group.cpp | 632 - botan3-3.12.0+dfsg/src/tests/test_ecc_explicit_params.cpp | 80 botan3-3.12.0+dfsg/src/tests/test_ecc_h2c.cpp | 51 botan3-3.12.0+dfsg/src/tests/test_ecc_pointmul.cpp | 127 botan3-3.12.0+dfsg/src/tests/test_ecdh.cpp | 43 botan3-3.12.0+dfsg/src/tests/test_ecdsa.cpp | 87 botan3-3.12.0+dfsg/src/tests/test_ecgdsa.cpp | 3 botan3-3.12.0+dfsg/src/tests/test_ecies.cpp | 100 botan3-3.12.0+dfsg/src/tests/test_eckcdsa.cpp | 3 botan3-3.12.0+dfsg/src/tests/test_ed25519.cpp | 13 botan3-3.12.0+dfsg/src/tests/test_ed448.cpp | 23 botan3-3.12.0+dfsg/src/tests/test_entropy.cpp | 22 botan3-3.12.0+dfsg/src/tests/test_ffi.cpp | 2500 +++++- botan3-3.12.0+dfsg/src/tests/test_filters.cpp | 251 botan3-3.12.0+dfsg/src/tests/test_fpe.cpp | 8 botan3-3.12.0+dfsg/src/tests/test_frodokem.cpp | 61 botan3-3.12.0+dfsg/src/tests/test_gf2m.cpp | 12 botan3-3.12.0+dfsg/src/tests/test_gost_3410.cpp | 18 botan3-3.12.0+dfsg/src/tests/test_hash.cpp | 34 botan3-3.12.0+dfsg/src/tests/test_hash_id.cpp | 8 botan3-3.12.0+dfsg/src/tests/test_hss_lms.cpp | 115 botan3-3.12.0+dfsg/src/tests/test_jitter_rng.cpp | 6 botan3-3.12.0+dfsg/src/tests/test_kdf.cpp | 16 botan3-3.12.0+dfsg/src/tests/test_keccak_helpers.cpp | 139 botan3-3.12.0+dfsg/src/tests/test_keywrap.cpp | 9 botan3-3.12.0+dfsg/src/tests/test_kyber.cpp | 71 botan3-3.12.0+dfsg/src/tests/test_lmots.cpp | 15 botan3-3.12.0+dfsg/src/tests/test_lms.cpp | 15 botan3-3.12.0+dfsg/src/tests/test_mac.cpp | 30 botan3-3.12.0+dfsg/src/tests/test_mceliece.cpp | 34 botan3-3.12.0+dfsg/src/tests/test_ml_dsa.cpp | 2 botan3-3.12.0+dfsg/src/tests/test_modes.cpp | 153 botan3-3.12.0+dfsg/src/tests/test_monty.cpp | 64 botan3-3.12.0+dfsg/src/tests/test_mp.cpp | 45 botan3-3.12.0+dfsg/src/tests/test_name_constraint.cpp | 229 botan3-3.12.0+dfsg/src/tests/test_ocb.cpp | 26 botan3-3.12.0+dfsg/src/tests/test_ocsp.cpp | 295 botan3-3.12.0+dfsg/src/tests/test_octetstring.cpp | 59 botan3-3.12.0+dfsg/src/tests/test_oid.cpp | 40 botan3-3.12.0+dfsg/src/tests/test_os_utils.cpp | 74 botan3-3.12.0+dfsg/src/tests/test_otp.cpp | 44 botan3-3.12.0+dfsg/src/tests/test_pad.cpp | 32 botan3-3.12.0+dfsg/src/tests/test_passhash.cpp | 26 botan3-3.12.0+dfsg/src/tests/test_pbkdf.cpp | 85 botan3-3.12.0+dfsg/src/tests/test_pem.cpp | 10 botan3-3.12.0+dfsg/src/tests/test_pk_pad.cpp | 78 botan3-3.12.0+dfsg/src/tests/test_pkcs11.h | 11 botan3-3.12.0+dfsg/src/tests/test_pkcs11_high_level.cpp | 404 - botan3-3.12.0+dfsg/src/tests/test_pkcs11_low_level.cpp | 281 botan3-3.12.0+dfsg/src/tests/test_psk_db.cpp | 72 botan3-3.12.0+dfsg/src/tests/test_pubkey.cpp | 381 botan3-3.12.0+dfsg/src/tests/test_pubkey.h | 58 botan3-3.12.0+dfsg/src/tests/test_pubkey_pqc.h | 85 botan3-3.12.0+dfsg/src/tests/test_rfc6979.cpp | 10 botan3-3.12.0+dfsg/src/tests/test_rng.h | 65 botan3-3.12.0+dfsg/src/tests/test_rng_behavior.cpp | 228 botan3-3.12.0+dfsg/src/tests/test_rng_kat.cpp | 5 botan3-3.12.0+dfsg/src/tests/test_rngs.cpp | 41 botan3-3.12.0+dfsg/src/tests/test_roughtime.cpp | 109 botan3-3.12.0+dfsg/src/tests/test_rsa.cpp | 50 botan3-3.12.0+dfsg/src/tests/test_simd.cpp | 273 botan3-3.12.0+dfsg/src/tests/test_siv.cpp | 3 botan3-3.12.0+dfsg/src/tests/test_sm2.cpp | 44 botan3-3.12.0+dfsg/src/tests/test_sodium.cpp | 180 botan3-3.12.0+dfsg/src/tests/test_sphincsplus.cpp | 72 botan3-3.12.0+dfsg/src/tests/test_sphincsplus_fors.cpp | 22 botan3-3.12.0+dfsg/src/tests/test_sphincsplus_utils.cpp | 43 botan3-3.12.0+dfsg/src/tests/test_sphincsplus_wots.cpp | 26 botan3-3.12.0+dfsg/src/tests/test_srp6.cpp | 14 botan3-3.12.0+dfsg/src/tests/test_stream.cpp | 44 botan3-3.12.0+dfsg/src/tests/test_strong_type.cpp | 1246 +-- botan3-3.12.0+dfsg/src/tests/test_tests.cpp | 55 botan3-3.12.0+dfsg/src/tests/test_thread_utils.cpp | 4 botan3-3.12.0+dfsg/src/tests/test_tls.cpp | 502 + botan3-3.12.0+dfsg/src/tests/test_tls_cipher_state.cpp | 475 - botan3-3.12.0+dfsg/src/tests/test_tls_handshake_layer_13.cpp | 111 botan3-3.12.0+dfsg/src/tests/test_tls_handshake_state_13.cpp | 63 botan3-3.12.0+dfsg/src/tests/test_tls_handshake_transitions.cpp | 20 botan3-3.12.0+dfsg/src/tests/test_tls_hybrid_kem_key.cpp | 160 botan3-3.12.0+dfsg/src/tests/test_tls_messages.cpp | 306 botan3-3.12.0+dfsg/src/tests/test_tls_record_layer_13.cpp | 244 botan3-3.12.0+dfsg/src/tests/test_tls_rfc8448.cpp | 912 +- botan3-3.12.0+dfsg/src/tests/test_tls_session_manager.cpp | 556 - botan3-3.12.0+dfsg/src/tests/test_tls_signature_scheme.cpp | 44 botan3-3.12.0+dfsg/src/tests/test_tls_stream_integration.cpp | 74 botan3-3.12.0+dfsg/src/tests/test_tls_transcript_hash_13.cpp | 31 botan3-3.12.0+dfsg/src/tests/test_tpm.cpp | 18 botan3-3.12.0+dfsg/src/tests/test_tpm2.cpp | 453 - botan3-3.12.0+dfsg/src/tests/test_tss.cpp | 18 botan3-3.12.0+dfsg/src/tests/test_uri.cpp | 49 botan3-3.12.0+dfsg/src/tests/test_utils.cpp | 932 +- botan3-3.12.0+dfsg/src/tests/test_utils_bitvector.cpp | 529 - botan3-3.12.0+dfsg/src/tests/test_utils_buffer.cpp | 464 - botan3-3.12.0+dfsg/src/tests/test_workfactor.cpp | 6 botan3-3.12.0+dfsg/src/tests/test_x25519.cpp | 25 botan3-3.12.0+dfsg/src/tests/test_x509_dn.cpp | 14 botan3-3.12.0+dfsg/src/tests/test_x509_path.cpp | 898 +- botan3-3.12.0+dfsg/src/tests/test_x509_rpki.cpp | 2488 ++++++ botan3-3.12.0+dfsg/src/tests/test_xmss.cpp | 64 botan3-3.12.0+dfsg/src/tests/test_xof.cpp | 65 botan3-3.12.0+dfsg/src/tests/test_zfec.cpp | 27 botan3-3.12.0+dfsg/src/tests/tests.cpp | 919 +- botan3-3.12.0+dfsg/src/tests/tests.h | 628 - botan3-3.12.0+dfsg/src/tests/unit_asio_stream.cpp | 196 botan3-3.12.0+dfsg/src/tests/unit_ecdsa.cpp | 109 botan3-3.12.0+dfsg/src/tests/unit_tls.cpp | 523 + botan3-3.12.0+dfsg/src/tests/unit_tls_policy.cpp | 61 botan3-3.12.0+dfsg/src/tests/unit_x509.cpp | 858 +- 1892 files changed, 104386 insertions(+), 47530 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpc5m3mi89/botan3_3.7.1+dfsg-2.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpc5m3mi89/botan3_3.12.0+dfsg-2~deb13u1.dsc: no acceptable signature found diff -Nru botan3-3.7.1+dfsg/.clang-format botan3-3.12.0+dfsg/.clang-format --- botan3-3.7.1+dfsg/.clang-format 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/.clang-format 1970-01-01 00:00:00.000000000 +0000 @@ -1,59 +0,0 @@ -Language: Cpp -Standard: c++20 - -BasedOnStyle: Chromium - -ColumnLimit: 120 -AccessModifierOffset: -3 -IndentWidth: 3 -ContinuationIndentWidth: 3 -ConstructorInitializerIndentWidth: 6 - -PointerAlignment: Left -ReferenceAlignment: Left -QualifierAlignment: Left - -IncludeBlocks: Preserve -IncludeCategories: - - Regex: '^' - Priority: 3 - CaseSensitive: false - - Regex: '^' - Priority: 2 - CaseSensitive: false - - Regex: '^<.*' - Priority: 4 - CaseSensitive: false - - Regex: '^<.*\.h>' - Priority: 3 - CaseSensitive: false - - Regex: '.*' - Priority: 1 - CaseSensitive: false - -AttributeMacros: ['BOTAN_FUNC_ISA', - 'BOTAN_FUNC_ISA_INLINE', - 'BOTAN_FORCE_INLINE', - 'BOTAN_DEPRECATED', - 'BOTAN_DEPRECATED_API'] - -BinPackArguments: false -BreakStringLiterals: false -AllowAllArgumentsOnNextLine: true -AllowAllParametersOfDeclarationOnNextLine: true -ConstructorInitializerAllOnOneLineOrOnePerLine: true -EmptyLineBeforeAccessModifier: Always - -BreakConstructorInitializers: AfterColon -BreakInheritanceList: AfterComma -AllowShortBlocksOnASingleLine: Empty -AllowShortFunctionsOnASingleLine: Inline -SpaceBeforeParens: Never -IndentPPDirectives: BeforeHash -FixNamespaceComments: true -SeparateDefinitionBlocks: Always -KeepEmptyLinesAtTheStartOfBlocks: false -IndentAccessModifiers: true -ReflowComments: false -RequiresClausePosition: OwnLine -IndentRequiresClause: true diff -Nru botan3-3.7.1+dfsg/.devcontainer/Dockerfile botan3-3.12.0+dfsg/.devcontainer/Dockerfile --- botan3-3.7.1+dfsg/.devcontainer/Dockerfile 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/.devcontainer/Dockerfile 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,70 @@ +FROM ubuntu:24.04 + +ARG LANG=en_US.UTF-8 +ARG LANGUAGE=en_US.UTF-8 +ARG LC_ALL=en_US.UTF-8 + +RUN echo "unminimize the ubuntu base image" \ + && yes | unminimize + +RUN echo "updating packages of base image" \ + && apt-get update \ + && apt-get -y dist-upgrade \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y locales \ + && sed -i '/'${LANG}'/s/^# //g' /etc/locale.gen \ + && locale-gen + +ENV LANG=${LANG} +ENV LANGUAGE=${LANGUAGE} +ENV LC_ALL=${LC_ALL} + +RUN echo "installing essential devtools" \ + && DEBIAN_FRONTEND=noninteractive apt-get -y --no-install-recommends install \ + apt-transport-https \ + build-essential \ + ca-certificates \ + ccache \ + clang \ + clang-format-17 \ + clangd-20 \ + curl \ + fzf \ + gdb \ + git \ + jq \ + less \ + ninja-build \ + pipx \ + procps \ + pylint \ + python3-pip \ + python3.12 \ + shellcheck \ + software-properties-common \ + sudo \ + tig \ + unzip \ + valgrind \ + vim \ + wget \ + zsh + +RUN echo "installing Botan-specific tools and dependencies" \ + && DEBIAN_FRONTEND=noninteractive apt-get -y --no-install-recommends install \ + doxygen \ + golang \ + libboost-dev \ + libtss2-tcti-tabrmd0 \ + python3-docutils \ + python3-sphinx \ + softhsm2 \ + swtpm \ + swtpm-tools \ + tpm2-abrmd \ + tpm2-tools \ + && apt-get autoremove --purge \ + && rm -fR /var/cache/apt/archives \ + && pipx install \ + ruff + +ENTRYPOINT ["/usr/local/bin/docker-entrypoint"] diff -Nru botan3-3.7.1+dfsg/.devcontainer/devcontainer.json botan3-3.12.0+dfsg/.devcontainer/devcontainer.json --- botan3-3.7.1+dfsg/.devcontainer/devcontainer.json 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/.devcontainer/devcontainer.json 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,48 @@ +{ + "name": "Botan", + "build": { + "dockerfile": "Dockerfile" + }, + "features": { + "ghcr.io/devcontainers/features/common-utils": { + "installOhMyZsh": true, + "installOhMyZshConfig": true, + "configureZshAsDefaultShell": true + }, + "ghcr.io/devcontainers-extra/features/zsh-plugins:0": { + "plugins": "fzf" + } + }, + "postStartCommand": "cd ${containerWorkspaceFolder}; .devcontainer/startup.sh", + "remoteUser": "ubuntu", + "customizations": { + "vscode": { + "settings": { + "[cpp]": { + "editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd", + "editor.formatOnSave": true + }, + "clangd": { + "path": "clangd-20", + "arguments": [ + "--header-insertion=never" + ], + "checkUpdates": false + }, + "clang-format.executable": "clang-format-17", + "ruff.enable": true, + "pylint.args": [ + "--rcfile=src/configs/pylint.rc" + ] + }, + "extensions": [ + "llvm-vs-code-extensions.vscode-clangd", + "ms-vscode.cpptools", + "ms-vscode.cpptools-themes", + "ms-python.python", + "charliermarsh.ruff", + "editorconfig.editorconfig" + ] + } + } +} diff -Nru botan3-3.7.1+dfsg/.devcontainer/startup.sh botan3-3.12.0+dfsg/.devcontainer/startup.sh --- botan3-3.7.1+dfsg/.devcontainer/startup.sh 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/.devcontainer/startup.sh 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,28 @@ +#!/bin/bash + +# Development Container Startup Script +# +# This runs whenever the container starts. Use it to set up common things in the +# repository. The current working directory is always at the repository's root. +# +# (C) 2025 Jack Lloyd +# (C) René Meusel, Rohde & Schwarz Cybersecurity +# +# Botan is released under the Simplified BSD License (see license.txt) + +create_symlink() { + if [ ! -L "$1" ]; then + echo "Creating symlink from '$1' to '$2'" + ln -s "$2" "$1" + else + echo "Symlink '$1' already exists" + fi +} + +create_symlink .vscode src/editors/vscode +create_symlink .editorconfig src/editors/editorconfig +create_symlink .clang-format src/configs/clang-format + +echo "Setting up git blame to ignore certain commits" +git config --local blame.ignoreRevsFile src/configs/git-blame-ignore-revs +git config --local blame.markIgnoredLines true diff -Nru botan3-3.7.1+dfsg/.github/actions/setup-build-agent/action.yml botan3-3.12.0+dfsg/.github/actions/setup-build-agent/action.yml --- botan3-3.7.1+dfsg/.github/actions/setup-build-agent/action.yml 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/.github/actions/setup-build-agent/action.yml 2026-05-07 01:38:28.000000000 +0000 @@ -11,6 +11,10 @@ target: description: The ci_build.py target going to be built on this agent required: true + compiler: + description: The compiler used to build this target + required: false + default: unknown cache-key: description: The actions/cache key to be used for this runs, caching will be disabled when no key is provided required: false @@ -23,11 +27,11 @@ using: composite steps: - name: Setup Build Agent (Windows) - run: ${{ github.action_path }}/../../../src/scripts/ci/setup_gh_actions.ps1 "${{ inputs.target }}" "${{ inputs.arch }}" + run: ${{ github.action_path }}/../../../src/scripts/ci/setup_gh_actions.ps1 "${{ inputs.target }}" "${{ inputs.compiler }}" "${{ inputs.arch }}" shell: pwsh if: runner.os == 'Windows' - name: Setup Build Agent (Unix-like) - run: ${{ github.action_path }}/../../../src/scripts/ci/setup_gh_actions.sh "${{ inputs.target }}" "${{ inputs.arch }}" + run: ${{ github.action_path }}/../../../src/scripts/ci/setup_gh_actions.sh "${{ inputs.target }}" "${{ inputs.compiler }}" "${{ inputs.arch }}" shell: bash if: runner.os != 'Windows' @@ -38,7 +42,9 @@ - uses: actions/cache@v4 if: env.COMPILER_CACHE_LOCATION != '' && inputs.cache-key != '' with: - path: ${{ env.COMPILER_CACHE_LOCATION }} + path: | + ${{ env.COMPILER_CACHE_LOCATION }} + ${{ env.BOTAN_CLANG_TIDY_CACHE }} key: ${{ inputs.cache-key }}-${{ github.run_id }} restore-keys: ${{ inputs.cache-key }} save-always: true diff -Nru botan3-3.7.1+dfsg/.github/workflows/ci.yml botan3-3.12.0+dfsg/.github/workflows/ci.yml --- botan3-3.7.1+dfsg/.github/workflows/ci.yml 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/.github/workflows/ci.yml 2026-05-07 01:38:28.000000000 +0000 @@ -33,20 +33,38 @@ - target: shared arch: x86_64 host_os: windows-2022 + compiler: msvc - target: static arch: x86_64 host_os: windows-2022 + compiler: msvc - target: amalgamation arch: x86_64 host_os: windows-2022 + compiler: msvc - target: shared arch: x86 host_os: windows-2022 + compiler: msvc + - target: amalgamation + arch: x86 + host_os: windows-2022 + compiler: msvc + - target: shared + arch: x86_64 + host_os: windows-2022 + compiler: clangcl + - target: static + arch: x86 + host_os: windows-2022 + compiler: clangcl runs-on: ${{ matrix.host_os }} steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Read Repository Configuration uses: ./.github/actions/read-repo-config @@ -55,11 +73,12 @@ uses: ./.github/actions/setup-build-agent with: target: ${{ matrix.target }} - cache-key: ${{ matrix.host_os }}-msvc-${{ matrix.arch }}-${{ matrix.target }} + compiler: msvc + cache-key: ${{ matrix.host_os }}-${{ matrix.compiler }}-${{ matrix.arch }}-${{ matrix.target }} arch: ${{ matrix.arch }} - name: Build and Test Botan - run: python3 ./src/scripts/ci_build.py --cc='msvc' --make-tool='ninja' --cpu='${{ matrix.arch }}' --test-results-dir=junit_results ${{ matrix.target }} + run: python3 ./src/scripts/ci_build.py --cc='${{ matrix.compiler }}' --ci-image='${{ matrix.host_os }}' --make-tool='ninja' --cpu='${{ matrix.arch }}' --test-results-dir=junit_results ${{ matrix.target }} linux: name: "Linux" @@ -70,17 +89,23 @@ include: - compiler: gcc target: shared + host_os: ubuntu-22.04 - compiler: gcc target: amalgamation + host_os: ubuntu-24.04 - compiler: gcc target: static + host_os: ubuntu-22.04 - compiler: clang target: shared + host_os: ubuntu-22.04 - runs-on: ubuntu-22.04 + runs-on: ${{ matrix.host_os }} steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Read Repository Configuration uses: ./.github/actions/read-repo-config @@ -89,10 +114,11 @@ uses: ./.github/actions/setup-build-agent with: target: ${{ matrix.target }} - cache-key: linux-${{ matrix.compiler }}-x86_64-${{ matrix.target }} + compiler: ${{ matrix.compiler }} + cache-key: ${{ matrix.host_os }}-${{ matrix.compiler }}-x86_64-${{ matrix.target }} - name: Build and Test Botan - run: python3 ./src/scripts/ci_build.py --cc='${{ matrix.compiler }}' --test-results-dir=junit_results ${{ matrix.target }} + run: python3 ./src/scripts/ci_build.py --cc='${{ matrix.compiler }}' --ci-image='${{ matrix.host_os }}' --test-results-dir=junit_results ${{ matrix.target }} macos: name: "macOS" @@ -103,21 +129,25 @@ include: - target: shared compiler: xcode - os: macos-13 + host_os: macos-15-intel - target: amalgamation compiler: xcode - os: macos-13 + host_os: macos-15-intel + make_tool: ninja - target: shared compiler: xcode - os: macos-14 # uses Apple Silicon + host_os: macos-15 # uses Apple Silicon + make_tool: ninja - target: amalgamation compiler: xcode - os: macos-14 # uses Apple Silicon + host_os: macos-15 # uses Apple Silicon - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.host_os }} steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Read Repository Configuration uses: ./.github/actions/read-repo-config @@ -126,16 +156,37 @@ uses: ./.github/actions/setup-build-agent with: target: ${{ matrix.target }} - cache-key: macos-${{ matrix.compiler }}-${{ matrix.os }}-${{ matrix.target }} + compiler: ${{ matrix.compiler }} + cache-key: macos-${{ matrix.compiler }}-${{ matrix.host_os }}-${{ matrix.target }} - name: Build and Test Botan - run: python3 ./src/scripts/ci_build.py --cc='${{ matrix.compiler }}' --test-results-dir=junit_results ${{ matrix.target }} + run: python3 ./src/scripts/ci_build.py --cc='${{ matrix.compiler }}' --ci-image='${{ matrix.host_os }}' --make-tool='${{ matrix.make_tool }}' --test-results-dir=junit_results ${{ matrix.target }} + + acvp: + name: "ACVP" + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Read Repository Configuration + uses: ./.github/actions/read-repo-config + - name: Setup Build Agent + uses: ./.github/actions/setup-build-agent + with: + target: acvp + compiler: gcc + cache-key: linux-x86_64-acvp + - name: Run ACVP Tests + run: python3 ./src/scripts/ci_build.py --cc=gcc --make-tool=make acvp clang-tidy: name: "Clang Tidy" runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Read Repository Configuration uses: ./.github/actions/read-repo-config @@ -144,6 +195,7 @@ uses: ./.github/actions/setup-build-agent with: target: clang-tidy + compiler: clang cache-key: linux-x86_64-clang-tidy - name: Configure Build @@ -151,8 +203,7 @@ - name: Run Clang Tidy run: | - ./src/scripts/ci/gh_get_changes_in_pr.py $(git rev-parse HEAD) --api-token=${{ secrets.GITHUB_TOKEN }} | \ - python3 ./src/scripts/dev_tools/run_clang_tidy.py --verbose --take-file-list-from-stdin --export-fixes-dir=clang_tidy_diagnostics + python3 ./src/scripts/dev_tools/run_clang_tidy.py - name: Display Clang Tidy Results if: failure() @@ -165,9 +216,6 @@ matrix: include: - - target: coverage - compiler: gcc - host_os: ubuntu-24.04 - target: sanitizer compiler: clang host_os: ubuntu-24.04 @@ -186,16 +234,17 @@ - target: limbo compiler: gcc host_os: ubuntu-24.04 + - target: typos + compiler: gcc + host_os: ubuntu-24.04 runs-on: ${{ matrix.host_os }} - env: - COVERALLS_REPO_TOKEN: pbLoTMBxC1DFvbws9WfrzVOvfEdEZTcCS - steps: - uses: actions/checkout@v4 with: path: ./source + persist-credentials: false - name: Read Repository Configuration uses: ./source/.github/actions/read-repo-config @@ -203,19 +252,21 @@ - name: Fetch BoringSSL fork for BoGo tests uses: actions/checkout@v4 with: + persist-credentials: false repository: ${{ env.BORINGSSL_REPO }} ref: ${{ env.BORINGSSL_BRANCH }} path: ./boringssl - if: matrix.target == 'coverage' || matrix.target == 'sanitizer' + if: matrix.target == 'sanitizer' - name: Setup Build Agent uses: ./source/.github/actions/setup-build-agent with: target: ${{ matrix.target }} + compiler: ${{ matrix.compiler }} cache-key: ${{ matrix.host_os }}-${{ matrix.compiler }}-x86_64-${{ matrix.target }} - name: Build and Test Botan - run: python3 ./source/src/scripts/ci_build.py --root-dir=${{ github.workspace }}/source --build-dir=${{ github.workspace }}/build --boringssl-dir=${{ github.workspace }}/boringssl --cc='${{ matrix.compiler }}' --make-tool='${{ matrix.make_tool }}' --test-results-dir=junit_results ${{ matrix.target }} + run: python3 ./source/src/scripts/ci_build.py --root-dir=${{ github.workspace }}/source --build-dir=${{ github.workspace }}/build --boringssl-dir=${{ github.workspace }}/boringssl --cc='${{ matrix.compiler }}' --ci-image='${{ matrix.host_os }}' --make-tool='${{ matrix.make_tool }}' --test-results-dir=junit_results ${{ matrix.target }} specials: name: "Special" @@ -230,7 +281,19 @@ - target: minimized compiler: gcc host_os: ubuntu-24.04 - - target: bsi + - target: no_tls12 + compiler: gcc + host_os: ubuntu-24.04 + - target: no_tls13 + compiler: gcc + host_os: ubuntu-24.04 + - target: policy-bsi + compiler: gcc + host_os: ubuntu-24.04 + - target: policy-fips140 + compiler: gcc + host_os: ubuntu-24.04 + - target: policy-modern compiler: gcc host_os: ubuntu-24.04 - target: docs @@ -239,25 +302,42 @@ - target: no_pcurves compiler: gcc host_os: ubuntu-24.04 + - target: optional-rngs + compiler: gcc + host_os: ubuntu-24.04 + - target: pkcs11 + compiler: gcc + host_os: ubuntu-24.04 runs-on: ${{ matrix.host_os }} steps: - uses: actions/checkout@v4 with: + persist-credentials: false path: ./source - name: Read Repository Configuration uses: ./source/.github/actions/read-repo-config + - name: Fetch BoringSSL fork for BoGo tests + uses: actions/checkout@v4 + with: + persist-credentials: false + repository: ${{ env.BORINGSSL_REPO }} + ref: ${{ env.BORINGSSL_BRANCH }} + path: ./boringssl + if: matrix.target == 'no_tls12' || matrix.target == 'no_tls13' + - name: Setup Build Agent uses: ./source/.github/actions/setup-build-agent with: target: ${{ matrix.target }} + compiler: ${{ matrix.compiler }} cache-key: ${{ matrix.host_os }}-${{ matrix.compiler }}-x86_64-${{ matrix.target }} - name: Build and Test Botan - run: python3 ./source/src/scripts/ci_build.py --root-dir=${{ github.workspace }}/source --build-dir=${{ github.workspace }}/build --boringssl-dir=${{ github.workspace }}/boringssl --cc='${{ matrix.compiler }}' --test-results-dir=junit_results ${{ matrix.target }} + run: python3 ./source/src/scripts/ci_build.py --root-dir=${{ github.workspace }}/source --build-dir=${{ github.workspace }}/build --boringssl-dir=${{ github.workspace }}/boringssl --cc='${{ matrix.compiler }}' --ci-image='${{ matrix.host_os }}' --test-results-dir=junit_results ${{ matrix.target }} x-compile: name: "Cross" @@ -269,18 +349,24 @@ - target: cross-i386 compiler: gcc host_os: ubuntu-22.04 - - target: cross-arm32 + - target: shared compiler: gcc - host_os: ubuntu-24.04 - - target: cross-arm64 + host_os: ubuntu-24.04-arm + - target: amalgamation compiler: gcc - host_os: ubuntu-24.04 + host_os: ubuntu-24.04-arm + - target: shared + compiler: clang + host_os: ubuntu-24.04-arm - target: cross-ppc64 compiler: gcc host_os: ubuntu-24.04 - target: cross-mips64 compiler: gcc host_os: ubuntu-24.04 + - target: cross-loongarch64 + compiler: gcc + host_os: ubuntu-24.04 - target: cross-android-arm64 compiler: clang host_os: ubuntu-24.04 @@ -290,7 +376,7 @@ make_tool: make - target: cross-ios-arm64 compiler: xcode - host_os: macos-13 + host_os: macos-26 - target: cross-arm32-baremetal compiler: gcc host_os: ubuntu-24.04 @@ -299,6 +385,8 @@ steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Read Repository Configuration uses: ./.github/actions/read-repo-config @@ -307,7 +395,8 @@ uses: ./.github/actions/setup-build-agent with: target: ${{ matrix.target }} + compiler: ${{ matrix.compiler }} cache-key: ${{ matrix.host_os }}-${{ matrix.compiler }}-xcompile-${{ matrix.target }} - name: Build and Test Botan - run: python3 ./src/scripts/ci_build.py --cc='${{ matrix.compiler }}' --make-tool='${{ matrix.make_tool }}' --test-results-dir=junit_results ${{ matrix.target }} + run: python3 ./src/scripts/ci_build.py --cc='${{ matrix.compiler }}' --ci-image='${{ matrix.host_os }}' --make-tool='${{ matrix.make_tool }}' --test-results-dir=junit_results ${{ matrix.target }} diff -Nru botan3-3.7.1+dfsg/.github/workflows/codeql.yml botan3-3.12.0+dfsg/.github/workflows/codeql.yml --- botan3-3.7.1+dfsg/.github/workflows/codeql.yml 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/.github/workflows/codeql.yml 2026-05-07 01:38:28.000000000 +0000 @@ -3,9 +3,6 @@ on: push: branches: ["master"] - pull_request: - # The branches below must be a subset of the branches above - branches: ["master"] schedule: # runs every day at 4:23 AM UTC - cron: "23 4 * * *" @@ -16,7 +13,7 @@ jobs: codeql_cpp: name: C++ - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 permissions: actions: read contents: read @@ -25,6 +22,8 @@ steps: - name: Checkout repository uses: actions/checkout@v4 + with: + persist-credentials: false - name: Read Repository Configuration uses: ./.github/actions/read-repo-config @@ -36,7 +35,7 @@ cache-key: linux-gcc-x86_64-codeql - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: cpp config-file: ./src/configs/codeql.yml @@ -45,13 +44,13 @@ run: ./src/scripts/ci_build.py --compiler-cache=none codeql - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 with: category: cpp codeql_py: name: Python - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 permissions: actions: read contents: read @@ -60,14 +59,16 @@ steps: - name: Checkout repository uses: actions/checkout@v4 + with: + persist-credentials: false - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: python config-file: ./src/configs/codeql.yml - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 with: category: python diff -Nru botan3-3.7.1+dfsg/.github/workflows/nightly.yml botan3-3.12.0+dfsg/.github/workflows/nightly.yml --- botan3-3.7.1+dfsg/.github/workflows/nightly.yml 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/.github/workflows/nightly.yml 2026-05-07 01:38:28.000000000 +0000 @@ -12,7 +12,7 @@ on: workflow_dispatch: - push: + pull_request: paths: # Run if a pull request changes this workflow to # validate it works properly before merging. @@ -22,6 +22,75 @@ - cron: '14 3 * * *' jobs: + coverage: + name: "Coverage" + + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + with: + path: ./source + persist-credentials: false + + - name: Read Repository Configuration + uses: ./source/.github/actions/read-repo-config + + - name: Fetch BoringSSL fork for BoGo tests + uses: actions/checkout@v4 + with: + persist-credentials: false + repository: ${{ env.BORINGSSL_REPO }} + ref: ${{ env.BORINGSSL_BRANCH }} + path: ./boringssl + + - name: Setup Build Agent + uses: ./source/.github/actions/setup-build-agent + with: + target: coverage + compiler: gcc + cache-key: ubuntu-24.04-gcc-x86_64-coverage + + - name: Build and Test Botan + env: + COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} + run: python3 ./source/src/scripts/ci_build.py --root-dir=${{ github.workspace }}/source --build-dir=${{ github.workspace }}/build --boringssl-dir=${{ github.workspace }}/boringssl --cc='gcc' --ci-image='ubuntu-24.04' --test-results-dir=junit_results coverage + + strubbing: + name: "Strubbing" + strategy: + fail-fast: false + + matrix: + include: + - target: strubbing + compiler: gcc-14 + host_os: ubuntu-24.04 + - target: strubbing + compiler: gcc-14 + host_os: ubuntu-24.04-arm + + runs-on: ${{ matrix.host_os }} + + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + path: ./source + + - name: Read Repository Configuration + uses: ./source/.github/actions/read-repo-config + + - name: Setup Build Agent + uses: ./source/.github/actions/setup-build-agent + with: + target: ${{ matrix.target }} + compiler: ${{ matrix.compiler }} + cache-key: ${{ matrix.host_os }}-${{ matrix.compiler }}-x86_64-${{ matrix.target }} + + - name: Build and Test Botan + run: python3 ./source/src/scripts/ci_build.py --root-dir=${{ github.workspace }}/source --build-dir=${{ github.workspace }}/build --cc='${{ matrix.compiler }}' --ci-image='${{ matrix.host_os }}' --test-results-dir=junit_results ${{ matrix.target }} + sanitizer: name: "Sanitizers" strategy: @@ -34,7 +103,7 @@ host_os: windows-2022 make_tool: ninja - target: sanitizer - compiler: gcc + compiler: gcc-14 host_os: ubuntu-24.04 runs-on: ${{ matrix.host_os }} @@ -42,6 +111,7 @@ steps: - uses: actions/checkout@v4 with: + persist-credentials: false path: ./source - name: Read Repository Configuration @@ -50,6 +120,7 @@ - name: Fetch BoringSSL fork for BoGo tests uses: actions/checkout@v4 with: + persist-credentials: false repository: ${{ env.BORINGSSL_REPO }} ref: ${{ env.BORINGSSL_BRANCH }} path: ./boringssl @@ -58,10 +129,11 @@ uses: ./source/.github/actions/setup-build-agent with: target: ${{ matrix.target }} + compiler: ${{ matrix.compiler }} cache-key: ${{ matrix.host_os }}-${{ matrix.compiler }}-x86_64-${{ matrix.target }} - name: Build and Test Botan - run: python3 ./source/src/scripts/ci_build.py --root-dir=${{ github.workspace }}/source --build-dir=${{ github.workspace }}/build --boringssl-dir=${{ github.workspace }}/boringssl --cc='${{ matrix.compiler }}' --make-tool='${{ matrix.make_tool }}' --test-results-dir=junit_results ${{ matrix.target }} + run: python3 ./source/src/scripts/ci_build.py --root-dir=${{ github.workspace }}/source --build-dir=${{ github.workspace }}/build --boringssl-dir=${{ github.workspace }}/boringssl --cc='${{ matrix.compiler }}' --ci-image='${{ matrix.host_os }}' --make-tool='${{ matrix.make_tool }}' --test-results-dir=junit_results ${{ matrix.target }} x-compile: name: "Cross" @@ -70,13 +142,13 @@ matrix: include: - - target: cross-alpha + - target: cross-arm32 compiler: gcc host_os: ubuntu-24.04 - - target: cross-hppa64 + - target: cross-alpha compiler: gcc host_os: ubuntu-24.04 - - target: cross-m68k + - target: cross-hppa64 compiler: gcc host_os: ubuntu-24.04 - target: cross-mips @@ -100,12 +172,9 @@ - target: cross-android-arm64-amalgamation compiler: clang host_os: ubuntu-24.04 - - target: cross-arm64-amalgamation - compiler: gcc - host_os: ubuntu-24.04 - target: emscripten compiler: emcc - host_os: macos-14 + host_os: macos-26 - target: sde compiler: gcc host_os: ubuntu-24.04 @@ -114,6 +183,8 @@ steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Read Repository Configuration uses: ./.github/actions/read-repo-config @@ -122,36 +193,11 @@ uses: ./.github/actions/setup-build-agent with: target: ${{ matrix.target }} + compiler: ${{ matrix.compiler }} cache-key: ${{ matrix.host_os }}-${{ matrix.compiler }}-xcompile-${{ matrix.target }} - name: Build and Test Botan - run: python3 ./src/scripts/ci_build.py --cc='${{ matrix.compiler }}' --make-tool='${{ matrix.make_tool }}' --test-results-dir=junit_results ${{ matrix.target }} - - clang_tidy: - name: "clang-tidy" - - runs-on: ubuntu-24.04 - - steps: - - uses: actions/checkout@v4 - - - name: Read Repository Configuration - uses: ./.github/actions/read-repo-config - - - name: Setup Build Agent - uses: ./.github/actions/setup-build-agent - with: - target: clang-tidy - cache-key: linux-x86_64-clang-tidy - - - name: Install dependencies - run: sudo apt-get -qq install libboost-dev libbz2-dev liblzma-dev libsqlite3-dev - - - name: Configure Build - run: python3 ./configure.py --cc=clang --build-targets=shared,cli,tests,examples,bogo_shim --build-fuzzers=test --with-boost --with-sqlite --with-zlib --with-lzma --with-bzip2 - - - name: Run Clang Tidy - run: python3 ./src/scripts/dev_tools/run_clang_tidy.py --verbose + run: python3 ./src/scripts/ci_build.py --cc='${{ matrix.compiler }}' --ci-image='${{ matrix.host_os }}' --make-tool='${{ matrix.make_tool }}' --test-results-dir=junit_results ${{ matrix.target }} valgrind: name: "valgrind" @@ -167,28 +213,40 @@ matrix: # Run a matrix of compiler and optimization flag combinations to maximize # the signal of secret-dependent execution issues introduced by compilers. - compiler: ["clang", "gcc"] + compiler: ["clang", "gcc-14"] cxxflags: ["-O1", "-O2", "-O3"] target: ["valgrind-ct-full"] + host_os: ["ubuntu-24.04", "ubuntu-24.04-arm"] + + exclude: + - host_os: "ubuntu-24.04-arm" + compiler: clang + - host_os: "ubuntu-24.04-arm" + cxxflags: "-O1" include: - compiler: clang cxxflags: "" # default compilation flags target: "valgrind-full" # memory bug detection + host_os: "ubuntu-24.04" - compiler: clang cxxflags: "-Os" # Clang's -Os generated binary is fast enough to run the full test suite. target: "valgrind-ct-full" - - compiler: gcc + host_os: "ubuntu-24.04" + - compiler: gcc-14 cxxflags: "-Os" # GCC with -Os generates a much slower binary, that won't finish # before timing out on GH Actions, so we run a reduced set of tests. target: "valgrind-ct" + host_os: "ubuntu-24.04" - runs-on: ubuntu-24.04 + runs-on: ${{ matrix.host_os }} steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Read Repository Configuration uses: ./.github/actions/read-repo-config @@ -197,10 +255,29 @@ uses: ./.github/actions/setup-build-agent with: target: ${{ matrix.target }} - cache-key: linux-x86_64-${{ matrix.compiler }}-${{ matrix.target }}-${{ matrix.cxxflags }} + compiler: ${{ matrix.compiler }} + cache-key: ${{ matrix.host_os }}-${{ matrix.compiler }}-${{ matrix.target }}-${{ matrix.cxxflags }} - name: Valgrind Checks - run: python3 ./src/scripts/ci_build.py --make-tool=make --cc=${{ matrix.compiler }} --custom-optimization-flags="${{ matrix.cxxflags }}" ${{ matrix.target }} + run: python3 ./src/scripts/ci_build.py --make-tool=make --cc=${{ matrix.compiler }} --ci-image='${{ matrix.host_os }}' --custom-optimization-flags="${{ matrix.cxxflags }}" ${{ matrix.target }} + + wycheproof: + name: "Wycheproof" + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Read Repository Configuration + uses: ./.github/actions/read-repo-config + - name: Setup Build Agent + uses: ./.github/actions/setup-build-agent + with: + target: wycheproof + compiler: gcc + cache-key: linux-x86_64-wycheproof + - name: Run Wycheproof Tests + run: python3 ./src/scripts/ci_build.py --cc=gcc --make-tool=make wycheproof hybrid_tls_interop: name: "PQ/T TLS 1.3" @@ -209,6 +286,8 @@ steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Read Repository Configuration uses: ./.github/actions/read-repo-config @@ -217,6 +296,7 @@ uses: ./.github/actions/setup-build-agent with: target: hybrid-tls13-interop-test + compiler: gcc cache-key: linux-x86_64-hybrid_tls - name: Hybrid PQ/T TLS 1.3 Online Interop Checks @@ -230,6 +310,8 @@ steps: - name: Fetch Botan Repository uses: actions/checkout@v4 + with: + persist-credentials: false - name: Read Repository Configuration uses: ./.github/actions/read-repo-config @@ -238,14 +320,18 @@ uses: ./.github/actions/setup-build-agent with: target: tlsanvil - cache-key: linux-x86_64-tlsanvil + cache-key: linux-x86_64-tlsanvil-server + + - name: Build Botan + run: | + python3 ./configure.py --compiler-cache=ccache --build-targets=static,cli --without-documentation --with-boost + make -j$(nproc) - - name: Build and Test Botan Server with TLS-Anvil + - name: Test Botan Server with TLS-Anvil run: > - python3 ./src/scripts/ci/ci_tlsanvil_test.py - --botan-dir . + python3 ./src/scripts/tls_anvil/run_tls_anvil_tests.py + --botan-cli ./botan --test-target server - --parallel $(nproc) - uses: actions/upload-artifact@v4 with: @@ -255,4 +341,45 @@ ./logs/ - name: Check TLS-Anvil Test Results - run: python3 ./src/scripts/ci/ci_tlsanvil_check.py --verbose ./TestSuiteResults + run: python3 ./src/scripts/tls_anvil/analyze_tls_anvil_report.py --verbose server ./TestSuiteResults + + tls_anvil_client_test: + name: "TLS-Anvil (client)" + + runs-on: ubuntu-24.04 + + steps: + - name: Fetch Botan Repository + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Read Repository Configuration + uses: ./.github/actions/read-repo-config + + - name: Setup Build Agent + uses: ./.github/actions/setup-build-agent + with: + target: tlsanvil + cache-key: linux-x86_64-tlsanvil-client + + - name: Build Botan + run: | + python3 ./configure.py --compiler-cache=ccache --build-targets=static,cli --without-documentation --with-boost + make -j$(nproc) + + - name: Test Botan Client with TLS-Anvil + run: > + python3 ./src/scripts/tls_anvil/run_tls_anvil_tests.py + --botan-cli ./botan + --test-target client + + - uses: actions/upload-artifact@v4 + with: + name: tls-anvil-client-test-results + path: | + ./TestSuiteResults/ + ./logs/ + + - name: Check TLS-Anvil Test Results + run: python3 ./src/scripts/tls_anvil/analyze_tls_anvil_report.py --verbose client ./TestSuiteResults diff -Nru botan3-3.7.1+dfsg/.gitignore botan3-3.12.0+dfsg/.gitignore --- botan3-3.7.1+dfsg/.gitignore 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/.gitignore 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,7 @@ /Makefile build.ninja .ninja_log +.ninja_deps libbotan*.so.* *.a *.so @@ -27,10 +28,18 @@ \#*\# .\#* +# Benchmark output in top level +/*.json + +# Misc dev scripts in top level +/*.sh + # Editor configuration files (top level) /*.sublime-project /*.sublime-workspace /.editorconfig +/.vscode +/.clang-format # Archive files *.tgz diff -Nru botan3-3.7.1+dfsg/configure.py botan3-3.12.0+dfsg/configure.py --- botan3-3.7.1+dfsg/configure.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/configure.py 2026-05-07 01:38:28.000000000 +0000 @@ -3,16 +3,14 @@ """ Configuration program for botan +This script supports Python 3 syntax only. At least CPython 3.10 is recommended. +Latest PyPy3 should also work, but this is only occasionally tested. + (C) 2009-2020 Jack Lloyd (C) 2015,2016,2017 Simon Warta (Kullo GmbH) (C) 2019-2022 René Meusel (neXenio GmbH, Rohde & Schwarz Cybersecurity GmbH) Botan is released under the Simplified BSD License (see license.txt) - -This script is regularly tested with CPython 3.x, and -occasionally tested PyPy 4. - -On Jython target detection does not work (use --os and --cpu). """ import collections @@ -43,8 +41,8 @@ pass -def flatten(l): - return sum(l, []) +def flatten(lst): + return sum(lst, []) def normalize_source_path(source): """ @@ -55,6 +53,21 @@ def normalize_source_paths(sources): return [normalize_source_path(p) for p in sources] +def is_subpath(child_path, parent_path): + """ + Check if child_path is a subpath of parent_path + """ + + child_abs = os.path.abspath(child_path) + parent_abs = os.path.abspath(parent_path) + try: + rel = os.path.relpath(child_abs, parent_abs) + return (not rel.startswith(os.pardir + os.sep) + and rel != os.pardir + and rel != os.curdir) + except ValueError: # This can happen if on different drives under Windows + return False + def parse_version_file(version_path): version_file = open(version_path, encoding='utf8') key_and_val = re.compile(r"([a-z_]+) = ([a-zA-Z0-9:\-\']+)") @@ -132,6 +145,39 @@ return Version.get_data()["release_datestamp"] @staticmethod + def short_version_string(): + return "%d.%d.%d%s" % (Version.major(), Version.minor(), Version.patch(), Version.suffix()) + + @staticmethod + def full_version_string(options): + version = "Botan %s" % (Version.short_version_string()) + + if options.unsafe_fuzzer_mode or options.unsafe_terminate_on_asserts: + version += " UNSAFE " + if options.unsafe_fuzzer_mode: + version += "FUZZER MODE " + if options.unsafe_terminate_on_asserts: + version += "TERMINATE ON ASSERTS " + version += "BUILD" + + version += " (" + version += Version.release_type() + + if Version.datestamp() != 0: + version += ", dated %d" % (Version.datestamp()) + + if Version.vc_rev() != "unknown": + version += ", revision %s" % (Version.vc_rev()) + + if options.distribution_info is not None: + version += ", distribution '%s'" % (options.distribution_info) + + version += ")" + + return version + + + @staticmethod def as_string(): return '%d.%d.%d%s' % (Version.major(), Version.minor(), Version.patch(), Version.suffix()) @@ -400,9 +446,6 @@ target_group.add_option('--compiler-cache', help='specify a compiler cache to use') - target_group.add_option('--with-endian', metavar='ORDER', default=None, - help='override byte order guess') - target_group.add_option('--ct-value-barrier-type', metavar='TYPE', default=None, help=optparse.SUPPRESS_HELP) @@ -413,18 +456,17 @@ add_with_without_pair(target_group, 'compilation-database', True, 'disable compile_commands.json') - isa_extensions = [ - 'SSE2', 'SSSE3', 'SSE4.1', 'SSE4.2', 'AVX2', 'BMI2', 'RDRAND', 'RDSEED', - 'AES-NI', 'SHA-NI', - 'AltiVec', 'NEON', 'ARMv8 Crypto', 'POWER Crypto'] + isa_extensions_that_can_be_disabled = [('NEON', 'arm32')] - for isa_extn_name in isa_extensions: + for (isa_extn_name,arch) in isa_extensions_that_can_be_disabled: isa_extn = isa_extn_name.lower().replace(' ', '') + nm = isa_extn.replace('-', '').replace('.', '').replace(' ', '') + target_group.add_option('--disable-%s' % (isa_extn), help='disable %s intrinsics' % (isa_extn_name), action='append_const', - const=isa_extn.replace('-', '').replace('.', '').replace(' ', ''), + const=(nm,arch), dest='disable_intrinsics') build_group = optparse.OptionGroup(parser, 'Build options') @@ -438,10 +480,12 @@ add_enable_disable_pair(build_group, 'asm', True, 'disable inline assembly') + add_enable_disable_pair(build_group, 'stack-scrubbing', False, 'enable compiler-assisted stack scrubbing') + build_group.add_option('--enable-sanitizers', metavar='SAN', default='', help='enable specific sanitizers') - add_with_without_pair(build_group, 'stack-protector', True, 'disable stack smashing protections') + add_with_without_pair(build_group, 'stack-protector', None, 'disable stack smashing protections') add_with_without_pair(build_group, 'coverage-info', False, 'add coverage info') @@ -497,13 +541,8 @@ choices=link_methods, help='choose how links to include headers are created (%s)' % ', '.join(link_methods)) - build_group.add_option('--with-local-config', - dest='local_config', metavar='FILE', - help='include the contents of FILE into build.h') - build_group.add_option('--distribution-info', metavar='STRING', - help='distribution specific version', - default='unspecified') + help='distribution specific version', default=None) build_group.add_option('--maintainer-mode', dest='maintainer_mode', action='store_true', default=False, @@ -513,16 +552,13 @@ action='store_true', default=False, help="Prohibit compiler warnings") - build_group.add_option('--no-store-vc-rev', action='store_true', default=False, - help=optparse.SUPPRESS_HELP) - build_group.add_option('--no-install-python-module', action='store_true', default=False, help='skip installing Python module') build_group.add_option('--with-python-versions', dest='python_version', metavar='N.M', default='%d.%d' % (sys.version_info[0], sys.version_info[1]), - help='where to install botan2.py (def %default)') + help='where to install botan3.py (def %default)') build_group.add_option('--disable-cc-tests', dest='enable_cc_tests', default=True, action='store_false', @@ -562,6 +598,8 @@ add_with_without_pair(docs_group, 'pdf', False, 'run Sphinx to generate PDF doc') + add_with_without_pair(docs_group, 'texinfo', False, 'run Sphinx to generate texinfo doc') + add_with_without_pair(docs_group, 'rst2man', None, 'run rst2man to generate man page') add_with_without_pair(docs_group, 'doxygen', False, 'run Doxygen') @@ -624,6 +662,9 @@ help='set the install dir for man pages') install_group.add_option('--includedir', metavar='DIR', help='set the include file install dir') + install_group.add_option('--cmakeconfigdir', metavar='DIR', + help='set the CMake config (botan-config.cmake, botan-config-version.cmake) install dir') + add_with_without_pair(install_group, 'include-namespace', default=True, msg="don't add a 'botan-%d/' namespace to the include path" % (Version.major())) info_group = optparse.OptionGroup(parser, 'Informational') @@ -669,9 +710,6 @@ if args != []: raise UserError('Unhandled option(s): ' + ' '.join(args)) - if options.with_endian not in [None, 'little', 'big']: - raise UserError('Bad value to --with-endian "%s"' % (options.with_endian)) - if options.debug_mode: options.no_optimizations = True options.with_debug_info = True @@ -688,7 +726,7 @@ options.with_os_features = parse_multiple_enable(options.with_os_features) options.without_os_features = parse_multiple_enable(options.without_os_features) - options.disable_intrinsics = parse_multiple_enable(options.disable_intrinsics) + options.disable_intrinsics = [] if options.disable_intrinsics is None else options.disable_intrinsics return options @@ -838,10 +876,9 @@ infofile, ['header:internal', 'header:public', 'header:external', 'requires', 'os_features', 'arch', 'isa', 'cc', 'comment', 'warning'], - ['defines', 'libs', 'frameworks', 'module_info'], + ['defines', 'internal_defines', 'libs', 'frameworks', 'module_info'], { 'load_on': 'auto', - 'endian': 'any', }) def check_header_duplicates(header_list_public, header_list_internal): @@ -888,6 +925,8 @@ self.comment = combine_lines(lex.comment) self._defines = lex.defines self._validate_defines_content(self._defines) + self._internal_defines = lex.internal_defines + self._validate_defines_content(self._internal_defines) self.frameworks = convert_lib_list(lex.frameworks) self.libs = convert_lib_list(lex.libs) self.load_on = lex.load_on @@ -895,7 +934,6 @@ self.os_features = lex.os_features self.requires = lex.requires self.warning = combine_lines(lex.warning) - self.endian = lex.endian self._parse_module_info(lex) # Modify members @@ -1016,14 +1054,13 @@ def defines(self): return [(key + ' ' + value) for key, value in self._defines.items()] + def internal_defines(self): + return [(key + ' ' + value) for key, value in self._internal_defines.items()] + def compatible_cpu(self, archinfo, options): arch_name = archinfo.basename cpu_name = options.arch - if self.endian != 'any': - if self.endian != options.with_endian: - return False - for isa in self.isa: if isa.find(':') > 0: (arch, isa) = isa.split(':') @@ -1031,7 +1068,7 @@ if arch != arch_name: continue - if isa in options.disable_intrinsics: + if (isa, arch_name) in options.disable_intrinsics: return False # explicitly disabled if isa not in archinfo.isa_extensions: @@ -1104,7 +1141,23 @@ return supported_isa_flags(ccinfo, arch) and supported_compiler(ccinfo, cc_min_version) - def dependencies(self, osinfo): + def compatible_compiler_flags(self, ccinfo, arch, options): + if ccinfo.basename != 'emcc': + return True + + # Wasm SIMD optimizations are always opt-in. Binaries with unknown instructions cannot be instantiated. + compile_flags = " ".join(ccinfo.cc_compile_flags(options)) + for isa in self.isa: + isa_flags = ccinfo.isa_flags_for(isa, arch.basename) + if not isa_flags: + continue + + if isa_flags not in compile_flags: + return False + + return True + + def dependencies(self, osinfo, archinfo): # base is an implicit dep for all submodules deps = ['base'] if self.parent_module is not None: @@ -1113,8 +1166,11 @@ for req in self.requires: if req.find('?') != -1: (cond, dep) = req.split('?') - if osinfo is None or cond in osinfo.target_features: + if osinfo is None and archinfo is None: deps.append(dep) + else: + if cond == archinfo.basename or cond in osinfo.target_features: + deps.append(dep) else: deps.append(req) @@ -1135,7 +1191,7 @@ return True - missing = [s for s in self.dependencies(None) if s not in modules or is_dependency_on_virtual(self, modules[s])] + missing = [s for s in self.dependencies(None, None) if s not in modules or is_dependency_on_virtual(self, modules[s])] for modname in missing: if modname not in modules: @@ -1178,15 +1234,19 @@ self.prohibited = lex.prohibited def cross_check(self, modules): - def check(tp, lst): + def check(tp, lst, required): + msg = "Module policy %s includes non-existent module %s in <%s>" + for mod in lst: if mod not in modules: - logging.error("Module policy %s includes non-existent module %s in <%s>", - self.infofile, mod, tp) - - check('required', self.required) - check('if_available', self.if_available) - check('prohibited', self.prohibited) + if required: + logging.error(msg, self.infofile, mod, tp) + else: + logging.warning(msg, self.infofile, mod, tp) + + check('required', self.required, True) + check('if_available', self.if_available, False) + check('prohibited', self.prohibited, False) class ArchInfo(InfoObject): @@ -1197,21 +1257,14 @@ ['aliases', 'isa_extensions'], [], { - 'endian': None, 'family': None, - 'wordsize': 32 }) self.aliases = lex.aliases - self.endian = lex.endian self.family = lex.family self.isa_extensions = lex.isa_extensions - self.wordsize = int(lex.wordsize) - - if self.wordsize not in [32, 64]: - logging.error('Unexpected wordsize %d for arch %s', self.wordsize, infofile) - alphanumeric = re.compile('^[a-z0-9]+$') + alphanumeric = re.compile('^[a-z0-9_]+$') for isa in self.isa_extensions: if alphanumeric.match(isa) is None: logging.error('Invalid name for ISA extension "%s"', isa) @@ -1220,7 +1273,7 @@ isas = [] for isa in self.isa_extensions: - if isa not in options.disable_intrinsics: + if (isa, self.basename) not in options.disable_intrinsics: if cc.isa_flags_for(isa, self.basename) is not None: isas.append(isa) @@ -1359,32 +1412,6 @@ return None - def get_isa_specific_flags(self, isas, arch, options): - flags = set() - - def simd32_impl(): - for simd_isa in ['sse2', 'altivec', 'neon']: - if simd_isa in arch.isa_extensions and \ - simd_isa not in options.disable_intrinsics and \ - self.isa_flags_for(simd_isa, arch.basename): - return simd_isa - return None - - for isa in isas: - - if isa == 'simd': - isa = simd32_impl() - - if isa is None: - continue - - flagset = self.isa_flags_for(isa, arch.basename) - if flagset is None: - raise UserError('Compiler %s does not support %s' % (self.basename, isa)) - flags.add(flagset) - - return " ".join(sorted(flags)) - def gen_lib_flags(self, options, variables): """ Return any flags specific to building the library @@ -1392,8 +1419,11 @@ """ def flag_builder(): + # We always emit -fPIC or equivalent so that position independent executables + # can be created that link to the static library + yield self.shared_flags + if options.build_shared_lib: - yield self.shared_flags yield self.visibility_build_flags if 'debug' in self.lib_flags and options.with_debug_info: @@ -1557,8 +1587,7 @@ if not (options.debug_mode or sanitizers_enabled): yield self.cpu_flags_no_debug[options.arch] - for flag in options.extra_cxxflags: - yield flag + yield from options.extra_cxxflags for definition in options.define_build_macro: yield self.add_compile_definition_option + definition @@ -1567,7 +1596,7 @@ def _so_link_search(osname, debug_info): so_link_typ = [osname, 'default'] if debug_info: - so_link_typ = [l + '-debug' for l in so_link_typ] + so_link_typ + so_link_typ = [link + '-debug' for link in so_link_typ] + so_link_typ return so_link_typ def so_link_command_for(self, osname, options): @@ -1707,6 +1736,14 @@ return sorted(feats) + def enabled_features_public(self, options): + public_feat = set(['threads', 'filesystem']) + return sorted(list(set(self.enabled_features(options)) & public_feat)) + + def enabled_features_internal(self, options): + public_feat = set(['threads', 'filesystem']) + return sorted(list(set(self.enabled_features(options)) - public_feat)) + def macros(self, cc): value = [cc.add_compile_definition_option + define for define in self.feature_macros] @@ -1794,18 +1831,26 @@ k = match.group(1) if k.endswith('|upper'): k = k.replace('|upper', '') - v = get_replacement(k).upper() + return get_replacement(k).upper() elif k.endswith('|concat'): k = k.replace('|concat', '') if not match.group(2): raise InternalError("|concat must be of the form '%{val|concat:}'") v = get_replacement(k) if v: - v = f"{v}{match.group(2)}" - else: - v = get_replacement(k) + return f"{v}{match.group(2)}" + else: + return v + elif k.endswith('|as_bool'): + k = k.replace('|as_bool', '') + + if k not in self.vals: + raise KeyError(k) + v = self.vals.get(k) - return v + return str(bool(v)).lower() + else: + return get_replacement(k) def insert_join(match): var = match.group(1) @@ -1818,6 +1863,7 @@ output = "" idx = 0 + # pylint: disable=too-many-nested-blocks while idx < len(lines): cond_match = self.cond_pattern.match(lines[idx]) for_match = self.for_pattern.match(lines[idx]) @@ -1869,12 +1915,13 @@ else: output += for_body.replace('%{i}', v).replace('%{i|upper}', v.upper()) - omitlast_match = self.omitlast_pattern.match(output) - if omitlast_match: - output = omitlast_match.group(1) - if i + 1 < len(var): - output += omitlast_match.group(2) - output += omitlast_match.group(3) + if output.find('%{omitlast') >= 0: + omitlast_match = self.omitlast_pattern.match(output) + if omitlast_match: + output = omitlast_match.group(1) + if i + 1 < len(var): + output += omitlast_match.group(2) + output += omitlast_match.group(3) output += "\n" else: @@ -1936,7 +1983,7 @@ name = name.replace('.cpp', obj_suffix) yield normalize_source_path(os.path.join(obj_dir, name)) -def generate_build_info(build_paths, modules, cc, arch, osinfo, options): +def generate_build_info(build_paths, modules, osinfo, options): # first create a map of src_file->owning module module_that_owns = {} @@ -1945,27 +1992,12 @@ for src in mod.sources(): module_that_owns[src] = mod - def _isa_specific_flags(src): - if os.path.basename(src) == 'test_simd.cpp': - return cc.get_isa_specific_flags(['simd'], arch, options) - - if src in module_that_owns: - module = module_that_owns[src] - isas = module.isas_needed(arch.basename) - if 'simd' in module.dependencies(osinfo): - isas.append('simd') - - return cc.get_isa_specific_flags(isas, arch, options) - - return '' - def _build_info(sources, objects, target_type): output = [] for (obj_file, src) in zip(objects, sources): info = { 'src': src, 'obj': obj_file, - 'isa_flags': _isa_specific_flags(src) } if target_type in ['fuzzer', 'examples']: @@ -1985,8 +2017,6 @@ targets = ['lib', 'cli', 'test', 'fuzzer', 'examples'] - out['isa_build_info'] = [] - fuzzer_bin = [] example_bin = [] @@ -2005,10 +2035,6 @@ objects = list(yield_objectfile_list(src_list, src_dir, osinfo.obj_suffix, options)) build_info = _build_info(src_list, objects, t) - for b in build_info: - if b['isa_flags'] != '': - out['isa_build_info'].append(b) - if t == 'fuzzer': fuzzer_bin = [b['exe'] for b in build_info] elif t == 'examples': @@ -2053,11 +2079,6 @@ return sorted(libs) - def choose_mp_bits(): - mp_bits = arch.wordsize # allow command line override? - logging.debug('Using MP bits %d', mp_bits) - return mp_bits - def configure_command_line(): # Cut absolute path from main executable (e.g. configure.py or python interpreter) # to get the same result when configuring the same thing on different machines @@ -2147,6 +2168,11 @@ def test_exe_extra_ldflags(): if osinfo.matches_name("emscripten"): + # It doesn't make much sense (and it's not even possible) to preload files when FS is not virtualized. + virtualized_fs = '-sNODERAWFS=1' not in cc.ldflags(options) + if not virtualized_fs: + return '' + return '--preload-file=%s@src/tests/data' % source_paths.test_data_dir return '' @@ -2155,8 +2181,10 @@ 'version_major': Version.major(), 'version_minor': Version.minor(), 'version_patch': Version.patch(), - 'version_suffix': Version.suffix(), - 'version_vc_rev': 'unknown' if options.no_store_vc_rev else Version.vc_rev(), + 'version_vc_rev': None if Version.vc_rev() == 'unknown' else Version.vc_rev(), + + 'version_vc_rev_or_unknown': 'unknown' if Version.datestamp() == 0 else Version.vc_rev(), + 'abi_rev': Version.so_rev(), 'version': Version.as_string(), @@ -2164,6 +2192,10 @@ 'version_datestamp': Version.datestamp(), 'distribution_info': options.distribution_info, + 'distribution_info_or_unspecified': options.distribution_info or 'unspecified', + + 'full_version_string': Version.full_version_string(options), + 'short_version_string': Version.short_version_string(), 'macos_so_compat_ver': '%s.%s.0' % (Version.packed(), Version.so_rev()), 'macos_so_current_ver': '%s.%s.%s' % (Version.packed(), Version.so_rev(), Version.patch()), @@ -2197,7 +2229,6 @@ suffix=options.library_suffix), 'command_line': configure_command_line(), - 'local_config': read_textfile(options.local_config), 'program_suffix': program_suffix, @@ -2205,12 +2236,13 @@ 'bindir': absolute_install_dir(options.bindir or osinfo.bin_dir), 'libdir': absolute_install_dir(options.libdir or osinfo.lib_dir), 'mandir': options.mandir or osinfo.man_dir, - 'includedir': options.includedir or osinfo.header_dir, + 'includedir': absolute_install_dir(options.includedir or osinfo.header_dir), 'docdir': options.docdir or osinfo.doc_dir, 'with_documentation': options.with_documentation, 'with_sphinx': options.with_sphinx, 'with_pdf': options.with_pdf, + 'with_texinfo': options.with_texinfo, 'with_rst2man': options.with_rst2man, 'sphinx_config_dir': source_paths.sphinx_config_dir, 'with_doxygen': options.with_doxygen, @@ -2224,6 +2256,10 @@ 'makefile_path': os.path.join(build_paths.build_dir, '..', 'Makefile'), 'ninja_build_path': os.path.join(build_paths.build_dir, '..', 'build.ninja'), + # Use response files for the archive command on windows + # Note: macOS (and perhaps other OSes) do not support this + 'build_static_lib_using_cmdline_args': options.build_static_lib and osinfo.basename != 'windows', + 'build_static_lib_using_response_file': options.build_static_lib and osinfo.basename == 'windows', 'build_static_lib': options.build_static_lib, 'build_shared_lib': options.build_shared_lib, @@ -2249,10 +2285,6 @@ 'arch': options.arch, 'compiler': options.compiler, 'cpu_family': arch.family, - 'endian': options.with_endian, - 'cpu_is_64bit': arch.wordsize == 64, - - 'mp_bits': choose_mp_bits(), 'python_exe': choose_python_exe(), 'python_version': options.python_version, @@ -2264,6 +2296,7 @@ 'make_supports_phony': osinfo.basename != 'windows', 'cxx_supports_gcc_inline_asm': cc.supports_gcc_inline_asm and options.enable_asm, + 'compiler_assisted_stack_scrubbing': options.enable_stack_scrubbing, 'cxx_ct_value_barrier_type': cc.ct_value_barrier_type(options), @@ -2313,6 +2346,7 @@ 'internal_include_flags': build_paths.format_internal_include_flags(cc), 'external_include_flags': build_paths.format_external_include_flags(cc, options.with_external_includedir), 'module_defines': sorted(flatten([m.defines() for m in modules])), + 'module_internal_defines': sorted(flatten([m.internal_defines() for m in modules])), 'build_bogo_shim': bool('bogo_shim' in options.build_targets), 'bogo_shim_src': os.path.join(source_paths.src_dir, 'bogo_shim', 'bogo_shim.cpp'), @@ -2320,7 +2354,8 @@ 'build_ct_selftest': bool('ct_selftest' in options.build_targets), 'ct_selftest_src': os.path.join(source_paths.src_dir, 'ct_selftest', 'ct_selftest.cpp'), - 'os_features': osinfo.enabled_features(options), + 'os_features': osinfo.enabled_features_internal(options), + 'os_features_public': osinfo.enabled_features_public(options), 'os_name': osinfo.basename, 'cpu_features': arch.supported_isa_extensions(cc, options), 'system_cert_bundle': options.system_cert_bundle, @@ -2341,16 +2376,48 @@ 'disabled_mod_list': sorted([m.basename for m in disabled_modules]), } - variables['installed_include_dir'] = os.path.join( - variables['prefix'], + if not os.path.isabs(variables['prefix']): + raise UserError("The installation root must be an absolute path") + + if not is_subpath(variables['libdir'], variables['prefix']): + raise UserError("The libdir must be a subdirectory of the prefix") + + if not is_subpath(variables['includedir'], variables['prefix']): + raise UserError("The includedir must be a subdirectory of the prefix") + + variables['namespaced_includedir'] = os.path.join( variables['includedir'], - 'botan-%d' % (Version.major()), 'botan') + ('botan-%d' % Version.major()) if options.with_include_namespace else '') + variables['installed_include_dir'] = os.path.join( + variables['namespaced_includedir'], + 'botan') + + # A long time ago some packages required a bindir that was outside the installation + # prefix. In the CMake config we need the bindir to find DLLs on Windows. If the + # bindir is configured to be outside the prefix, CMake will fall back to a hard-coded + # path instead of a relative path for relocatability. + if is_subpath(variables['bindir'], variables['prefix']): + variables['bindir_rel'] = normalize_source_path(os.path.relpath(variables['bindir'], variables['prefix'])) + + variables['libdir_rel'] = normalize_source_path(os.path.relpath(variables['libdir'], variables['prefix'])) + variables['includedir_rel'] = normalize_source_path(os.path.relpath(variables['includedir'], variables['prefix'])) + variables['namespaced_includedir_rel'] = normalize_source_path(os.path.relpath(variables['namespaced_includedir'], variables['prefix'])) + + # On MSVC, the "ABI flags" should be passed to the compiler only, on other platforms, the + # ABI flags are passed to both the compiler and the linker and the compiler flags are also + # passed to the linker(?) + # + # TODO: Extend the build-data/cc/xxx.txt format to allow specifying different CFLAGS for + # different configurations, then /MD etc could be specified there rather than hijacking the ABI + # flags for it and having to special-case their exclusion from the linker command line. - if cc.basename == 'msvc' and variables['cxx_abi_flags'] != '': - # MSVC linker doesn't support/need the ABI options, - # just transfer them over to just the compiler invocations + if cc.basename in ('msvc', 'clangcl'): + # Move the "ABI flags" (/MD etc) into the compiler flags to exclude it from linker invocations variables['cc_compile_flags'] = '%s %s' % (variables['cxx_abi_flags'], variables['cc_compile_flags']) variables['cxx_abi_flags'] = '' + else: + # Append the compiler flags to the linker flags + variables['ldflags'] = '%s %s' % (variables['ldflags'], variables['cc_compile_flags']) variables['lib_flags'] = cc.gen_lib_flags(options, variables) @@ -2359,6 +2426,13 @@ if options.with_cmake_config: variables['botan_cmake_config'] = os.path.join(build_paths.build_dir, 'cmake', 'botan-config.cmake') variables['botan_cmake_version_config'] = os.path.join(build_paths.build_dir, 'cmake', 'botan-config-version.cmake') + cmake_install_dir = absolute_install_dir(options.cmakeconfigdir) if options.cmakeconfigdir else \ + os.path.join(variables['libdir'], 'cmake', 'Botan-%s' % variables['version']) + if not is_subpath(cmake_install_dir, variables['prefix']): + logging.error("The CMake module must be installed into a subdirectory of the install prefix.") + variables['cmake_install_dir'] = normalize_source_path(cmake_install_dir) + cmake_rel = os.path.relpath(cmake_install_dir, variables['prefix']) + variables['cmake_relpath_components'] = [p for p in cmake_rel.replace('\\', '/').split('/') if p and p != '.'] # The name is always set because Windows build needs it variables['static_lib_name'] = '%s%s.%s' % (variables['lib_prefix'], variables['libname'], @@ -2422,6 +2496,8 @@ self._not_using_because = collections.defaultdict(set) ModulesChooser._validate_dependencies_exist(self._modules) + self._options.enabled_modules = ModulesChooser._expand_wildcards_in_user_selection(self._modules, self._options.enabled_modules) + self._options.disabled_modules = ModulesChooser._expand_wildcards_in_user_selection(self._modules, self._options.disabled_modules) ModulesChooser._validate_user_selection( self._modules, self._options.enabled_modules, self._options.disabled_modules) @@ -2435,6 +2511,9 @@ elif not module.compatible_compiler(self._ccinfo, self._cc_min_version, self._archinfo.basename): self._not_using_because['incompatible compiler'].add(modname) return False + elif not module.compatible_compiler_flags(self._ccinfo, self._archinfo, self._options): + self._not_using_because['incompatible compiler flags'].add(modname) + return False elif module.is_deprecated() and not self._options.enable_deprecated_features and modname not in self._options.enabled_modules: self._not_using_because['deprecated'].add(modname) return False @@ -2500,6 +2579,21 @@ module.dependencies_exist(modules) @staticmethod + def _expand_wildcards_in_user_selection(modules, user_selected_modules): + valid_module_name_with_wildcard = re.compile(r'^[a-z0-9_*]+$') + public_modules = [modname for modname, modinfo in modules.items() if modinfo.is_public()] + def expand(user_selected_module): + if not valid_module_name_with_wildcard.match(user_selected_module): + logging.error("Invalid module name with wildcard: %s", user_selected_module) + return [] + regex_from_wildcards = re.compile("^%s$" % user_selected_module.replace('*', '[a-z0-9_]+')) + matching_modules = [mod for mod in public_modules if regex_from_wildcards.match(mod)] + if not matching_modules: + logging.warning("Wildcard '%s' did not match any modules", user_selected_module) + return matching_modules + return flatten([expand(mod) if '*' in mod else [mod] for mod in user_selected_modules]) + + @staticmethod def _validate_user_selection(modules, enabled_modules, disabled_modules): for modname in enabled_modules: if modname not in modules: @@ -2584,7 +2678,7 @@ def _modules_dependency_table(self): out = {} for modname in self._modules: - out[modname] = self._modules[modname].dependencies(self._osinfo) + out[modname] = self._modules[modname].dependencies(self._osinfo, self._archinfo) return out def _resolve_dependencies_for_all_modules(self): @@ -2819,8 +2913,7 @@ for line in self.file_contents[name]: header = AmalgamationHelper.is_botan_include(line) if header: - for c in self.header_contents(header): - yield c + yield from self.header_contents(header) else: std_header = AmalgamationHelper.is_unconditional_std_include(line) @@ -3081,15 +3174,23 @@ return os_name_variant # not found options.os = find_canonical_os_name(options.os) - def deduce_compiler_type_from_cc_bin(cc_bin): + def deduce_compiler_type_from_cc_bin(options): + cc_bin = options.compiler_binary if cc_bin.find('clang') != -1 or cc_bin in ['emcc', 'em++']: return 'clang' - if cc_bin.find('-g++') != -1 or cc_bin.find('g++') != -1: + if cc_bin.find('g++') != -1: return 'gcc' + + vers = run_compiler(options, None, '', ['--version']) + if vers.find('clang') != -1: + return 'clang' + if vers.find('Free Software Foundation') != -1: + return 'gcc' + return None if options.compiler is None and options.compiler_binary is not None: - options.compiler = deduce_compiler_type_from_cc_bin(options.compiler_binary) + options.compiler = deduce_compiler_type_from_cc_bin(options) if options.compiler is None: logging.error("Could not figure out what compiler type '%s' is, use --cc to set", @@ -3113,10 +3214,6 @@ options.cpu = cpu logging.info('Guessing target processor is a %s (use --cpu to set)', options.arch) - # OpenBSD uses an old binutils that does not support AVX2 - if options.os == 'openbsd': - del info_cc['gcc'].isa_flags['avx2'] - if options.with_documentation is True: if options.with_sphinx is None and have_program('sphinx-build'): logging.info('Found sphinx-build (use --without-sphinx to disable)') @@ -3132,6 +3229,7 @@ default_paths = [ '/etc/ssl/certs/ca-certificates.crt', # Ubuntu, Debian, Arch, Gentoo '/etc/pki/tls/certs/ca-bundle.crt', # RHEL + '/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem', # Fedora '/etc/ssl/ca-bundle.pem', # SuSE '/etc/ssl/cert.pem', # OpenBSD, FreeBSD, Alpine '/etc/certs/ca-certificates.crt', # Solaris @@ -3301,20 +3399,25 @@ raise UserError('Using --with-sphinx plus --without-documentation makes no sense') if options.with_pdf: raise UserError('Using --with-pdf plus --without-documentation makes no sense') + if options.with_texinfo: + raise UserError('Using --with-texinfo --without-documentation makes no sense') if options.with_pdf and not options.with_sphinx: raise UserError('Option --with-pdf requires --with-sphinx') + if options.with_texinfo and not options.with_sphinx: + raise UserError('Option --with-texinfo requires --with-sphinx') + if options.ct_value_barrier_type: if options.ct_value_barrier_type not in ['asm', 'volatile', 'none']: raise UserError('Unknown setting "%s" for --ct-value-barrier-type' % (options.ct_value_barrier_type)) # Warnings - if options.os == 'windows' and options.compiler != 'msvc': + if options.os == 'windows' and options.compiler not in ('msvc', 'clangcl'): logging.warning('The windows target is oriented towards MSVC; maybe you want --os=cygwin or --os=mingw') if options.msvc_runtime: - if options.compiler != 'msvc': + if options.compiler not in ('msvc', 'clangcl'): raise UserError("Makes no sense to specify MSVC runtime for %s" % (options.compiler)) if options.msvc_runtime not in ['MT', 'MD', 'MTd', 'MDd']: @@ -3330,7 +3433,7 @@ cc_output = run_compiler(options, ccinfo, default_return, ccinfo.preproc_flags.split(' ') + extra_flags + [source_file]) def cleanup_output(output): - return ('\n'.join([l for l in output.splitlines() if l.startswith('#') is False])).strip() + return ('\n'.join([line for line in output.splitlines() if not line.startswith('#')])).strip() return cleanup_output(cc_output) @@ -3339,6 +3442,7 @@ 'msvc': r'^ *MSVC ([0-9]{2})([0-9]{2})$', 'gcc': r'^ *GCC ([0-9]+) ([0-9]+)$', 'clang': r'^ *CLANG ([0-9]+) ([0-9]+)$', + 'clangcl': r'^ *CLANG ([0-9]+) ([0-9]+)$', 'xcode': r'^ *XCODE ([0-9]+) ([0-9]+)$', 'xlc': r'^ *XLC ([0-9]+) ([0-9]+)$', 'emcc': r'^ *EMCC ([0-9]+) ([0-9]+)$', @@ -3406,7 +3510,7 @@ logging.info('Auto-detected compiler arch %s', cc_output) return cc_output -def do_io_for_build(cc, arch, osinfo, using_mods, info_modules, build_paths, source_paths, template_vars, options): +def do_io_for_build(osinfo, using_mods, info_modules, build_paths, source_paths, template_vars, options): try: robust_rmtree(build_paths.build_dir) except OSError as ex: @@ -3439,6 +3543,8 @@ return os.path.join(build_paths.doc_module_info, p) write_template(in_build_dir('build.h'), in_build_data('buildh.in')) + write_template(in_build_dir('target_info.h'), in_build_data('target_info.h.in')) + write_template(in_build_dir('version_info.h'), in_build_data('version_info.h.in')) write_template(in_build_dir('botan.doxy'), in_build_data('botan.doxy.in')) if options.with_cmake_config: @@ -3484,7 +3590,7 @@ if options.build_shared_lib: logging.warning('Unless you are building a DLL or .so from the amalgamation, use --disable-shared as well') - template_vars.update(generate_build_info(build_paths, using_mods, cc, arch, osinfo, options)) + template_vars.update(generate_build_info(build_paths, using_mods, osinfo, options)) with open(os.path.join(build_paths.build_dir, 'build_config.json'), 'w', encoding='utf8') as f: json.dump(template_vars, f, sort_keys=True, indent=2) @@ -3640,18 +3746,16 @@ set_defaults_for_unset_options(options, info_arch, info_cc, info_os) canonicalize_options(options, info_os, info_arch) + validate_options(options, info_os, info_cc, info_module_policies) cc = info_cc[options.compiler] - arch = info_arch[options.arch] - osinfo = info_os[options.os] - module_policy = info_module_policies[options.module_policy] if options.module_policy else None if options.enable_cc_tests: cc_min_version = options.cc_min_version or calculate_cc_min_version(options, cc, source_paths) - cc_arch = check_compiler_arch(options, cc, info_arch, source_paths) - if options.arch != 'generic': + if options.arch not in ['generic', 'llvm']: + cc_arch = check_compiler_arch(options, cc, info_arch, source_paths) if cc_arch is not None and cc_arch != options.arch: logging.error("Configured target is %s but compiler probe indicates %s", options.arch, cc_arch) else: @@ -3660,20 +3764,12 @@ logging.info('Target is %s:%s-%s-%s', options.compiler, cc_min_version, options.os, options.arch) - def choose_endian(arch_info, options): - if options.with_endian is not None: - return options.with_endian - - if options.cpu.endswith('eb') or options.cpu.endswith('be'): - return 'big' - if options.cpu.endswith('el') or options.cpu.endswith('le'): - return 'little' - - if arch_info.endian: - logging.info('Assuming target %s is %s endian', arch_info.basename, arch_info.endian) - return arch_info.endian + if options.enable_stack_scrubbing and (options.compiler not in ['gcc'] or float(cc_min_version) < 14): + logging.warning('Your compiler does not support stack scrubbing. Only GCC 14 and newer support this at the moment.') - options.with_endian = choose_endian(arch, options) + arch = info_arch[options.arch] + osinfo = info_os[options.os] + module_policy = info_module_policies[options.module_policy] if options.module_policy else None chooser = ModulesChooser(info_modules, module_policy, arch, osinfo, cc, cc_min_version, options) loaded_module_names = chooser.choose() @@ -3682,11 +3778,13 @@ build_paths = BuildPaths(source_paths, options, using_mods) build_paths.public_headers.append(os.path.join(build_paths.build_dir, 'build.h')) + for internal_headers in ['target_info.h', 'version_info.h']: + build_paths.internal_headers.append(os.path.join(build_paths.build_dir, internal_headers)) template_vars = create_template_vars(source_paths, build_paths, options, using_mods, not_using_mods, cc, arch, osinfo) # Now we start writing to disk - do_io_for_build(cc, arch, osinfo, using_mods, info_modules, build_paths, source_paths, template_vars, options) + do_io_for_build(osinfo, using_mods, info_modules, build_paths, source_paths, template_vars, options) return 0 @@ -3696,7 +3794,7 @@ except UserError as e: logging.debug(traceback.format_exc()) logging.error(e) - except Exception as e: # pylint: disable=broad-except + except Exception: # pylint: disable=broad-except # error() will stop script, so wrap all information into one call logging.error("""%s An internal error occurred. diff -Nru botan3-3.7.1+dfsg/debian/changelog botan3-3.12.0+dfsg/debian/changelog --- botan3-3.7.1+dfsg/debian/changelog 2025-03-22 15:53:54.000000000 +0000 +++ botan3-3.12.0+dfsg/debian/changelog 2026-07-24 21:25:37.000000000 +0000 @@ -1,3 +1,91 @@ +botan3 (3.12.0+dfsg-2~deb13u1) trixie-security; urgency=high + + * Upload to trixie-security (CVE-2026-44378), there is no reverse + dependency of the library in trixie so moving with an ABI bump. + + -- Aron Xu Sat, 25 Jul 2026 05:25:37 +0800 + +botan3 (3.12.0+dfsg-2) unstable; urgency=medium + + * Upload to Sid. + + -- Laszlo Boszormenyi (GCS) Sat, 23 May 2026 07:58:39 +0200 + +botan3 (3.12.0+dfsg-1) experimental; urgency=medium + + * New upstream release: + - fixes CVE-2026-44378: CPU based denial of service when decoding BER + encoded data. + * Library transition from libbotan-3-11 to libbotan-3-12 . + + -- Laszlo Boszormenyi (GCS) Sat, 09 May 2026 08:45:18 +0200 + +botan3 (3.11.1+dfsg-2) unstable; urgency=medium + + * Remove obsolete readdir_hurd.patch (closes: #1132623). + * Upload to Sid. + + -- Laszlo Boszormenyi (GCS) Thu, 09 Apr 2026 07:04:12 +0200 + +botan3 (3.11.1+dfsg-1) experimental; urgency=medium + + * New upstream release: + - fixes CVE-2026-35580: resolve certificate verification bypass, + - fixes CVE-2026-35582: resolve TLS 1.3 client authentication bypass. + + -- Laszlo Boszormenyi (GCS) Fri, 03 Apr 2026 11:56:38 +0200 + +botan3 (3.11.0+dfsg-1) experimental; urgency=medium + + * New upstream release. + * Library transition from libbotan-3-10 to libbotan-3-11 . + + -- Laszlo Boszormenyi (GCS) Sun, 22 Mar 2026 08:51:26 +0100 + +botan3 (3.10.0+dfsg-2) unstable; urgency=medium + + * Upload to Sid (closes: #1114987). + + -- Laszlo Boszormenyi (GCS) Sun, 30 Nov 2025 22:35:28 +0100 + +botan3 (3.10.0+dfsg-1) experimental; urgency=medium + + * New upstream release. + * Library transition from libbotan-3-9 to libbotan-3-10 . + * Remove now redundant Rules-Requires-Root value. + * Update watch file. + + -- Laszlo Boszormenyi (GCS) Fri, 07 Nov 2025 17:22:38 +0100 + +botan3 (3.9.0+dfsg-2.1) experimental; urgency=medium + + * Non-maintainer upload. + * Let libbotan-3-dev depend on the dev packages that are in its pkgconf. + (closes: #1114813) + + -- Bastian Germann Thu, 11 Sep 2025 17:57:35 +0200 + +botan3 (3.9.0+dfsg-2) experimental; urgency=medium + + * Do not try to disable NEON on armel and armhf, it's not needed anymore. + + -- Laszlo Boszormenyi (GCS) Wed, 20 Aug 2025 16:57:51 +0200 + +botan3 (3.9.0+dfsg-1) experimental; urgency=medium + + * New upstream release. + * Library transition from libbotan-3-8 to libbotan-3-9 . + * Update Standards-Version to 4.7.2 . + + -- Laszlo Boszormenyi (GCS) Sat, 16 Aug 2025 15:01:29 +0200 + +botan3 (3.8.1+dfsg-1) experimental; urgency=medium + + * New upstream release. + * Library transition from libbotan-3-7 to libbotan-3-8 . + + -- Laszlo Boszormenyi (GCS) Wed, 14 May 2025 18:23:23 +0200 + botan3 (3.7.1+dfsg-2) unstable; urgency=medium * Build depend on ca-certificates. diff -Nru botan3-3.7.1+dfsg/debian/control botan3-3.12.0+dfsg/debian/control --- botan3-3.7.1+dfsg/debian/control 2025-03-22 15:53:54.000000000 +0000 +++ botan3-3.12.0+dfsg/debian/control 2026-05-09 06:45:18.000000000 +0000 @@ -13,11 +13,9 @@ zlib1g-dev, python3:any, python3-docutils, - ca-certificates, Build-Depends-Indep: python3-sphinx, -Standards-Version: 4.7.0 -Rules-Requires-Root: no +Standards-Version: 4.7.2 Homepage: https://botan.randombit.net/ Package: botan @@ -34,7 +32,7 @@ . This package contains the 3.x version of Botan. -Package: libbotan-3-7 +Package: libbotan-3-12 Section: libs Architecture: any Multi-Arch: same @@ -50,7 +48,12 @@ Package: libbotan-3-dev Section: libdevel Architecture: any -Depends: ${misc:Depends}, libbotan-3-7 (= ${binary:Version}) +Depends: ${misc:Depends}, libbotan-3-12 (= ${binary:Version}), + libbz2-dev, + liblzma-dev, + libsqlite3-dev, + libtspi-dev, + zlib1g-dev, Description: multiplatform crypto library (3.x version) Botan is a C++ library which provides support for many common cryptographic operations, including encryption, authentication, and X.509v3 certificates and @@ -75,7 +78,7 @@ Package: python3-botan Section: python Architecture: any -Depends: ${misc:Depends}, ${python3:Depends}, libbotan-3-7 (= ${binary:Version}) +Depends: ${misc:Depends}, ${python3:Depends}, libbotan-3-12 (= ${binary:Version}) Breaks: python3-botan (<< 3-1~) Replaces: python3-botan (<< 3-1~) Description: multiplatform crypto library (3.x version), Python3 module diff -Nru botan3-3.7.1+dfsg/debian/copyright botan3-3.12.0+dfsg/debian/copyright --- botan3-3.7.1+dfsg/debian/copyright 2024-07-14 09:24:20.000000000 +0000 +++ botan3-3.12.0+dfsg/debian/copyright 2026-03-22 07:51:26.000000000 +0000 @@ -5,8 +5,8 @@ Files-Excluded: src/lib/prov/pkcs11/* Files: * -Copyright: 1999-2023 The Botan Authors, - 1999-2023 Jack Lloyd +Copyright: 1999-2025 The Botan Authors, + 1999-2025 Jack Lloyd License: BSD-2-clause Files: configure.py @@ -14,6 +14,10 @@ 2015,2016,2017 Simon Warta (Kullo GmbH) License: BSD-2-clause +Files: src/build-data/botan-config.cmake.in src/build-data/botan-config-version.cmake.in +Copyright: 2023- The Botan Authors +License: MIT + Files: src/cli/* Copyright: 2015,2017 Simon Warta (Kullo GmbH), 2018 Ribose Inc, @@ -172,3 +176,22 @@ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + ( copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. diff -Nru botan3-3.7.1+dfsg/debian/libbotan-3-12.install botan3-3.12.0+dfsg/debian/libbotan-3-12.install --- botan3-3.7.1+dfsg/debian/libbotan-3-12.install 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/debian/libbotan-3-12.install 2024-02-23 21:36:42.000000000 +0000 @@ -0,0 +1 @@ +usr/lib/${DEB_HOST_MULTIARCH}/libbotan-3.so.* diff -Nru botan3-3.7.1+dfsg/debian/libbotan-3-7.install botan3-3.12.0+dfsg/debian/libbotan-3-7.install --- botan3-3.7.1+dfsg/debian/libbotan-3-7.install 2024-02-23 21:36:42.000000000 +0000 +++ botan3-3.12.0+dfsg/debian/libbotan-3-7.install 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -usr/lib/${DEB_HOST_MULTIARCH}/libbotan-3.so.* diff -Nru botan3-3.7.1+dfsg/debian/patches/readdir_hurd.patch botan3-3.12.0+dfsg/debian/patches/readdir_hurd.patch --- botan3-3.7.1+dfsg/debian/patches/readdir_hurd.patch 2019-10-07 15:13:12.000000000 +0000 +++ botan3-3.12.0+dfsg/debian/patches/readdir_hurd.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -Description: Hurd FTBFS fix - Add readdir possibility to Hurd architecture. -Origin: upstream -Author: Jack Lloyd -Forwarded: not-needed -Last-Update: 2019-10-07 - ---- - ---- botan-2.12.0.orig/src/build-data/os/hurd.txt -+++ botan-2.12.0/src/build-data/os/hurd.txt -@@ -11,6 +11,7 @@ sockets - threads - thread_local - filesystem -+readdir - - - diff -Nru botan3-3.7.1+dfsg/debian/patches/series botan3-3.12.0+dfsg/debian/patches/series --- botan3-3.7.1+dfsg/debian/patches/series 2023-07-08 18:09:33.000000000 +0000 +++ botan3-3.12.0+dfsg/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -readdir_hurd.patch -#use_python3.patch diff -Nru botan3-3.7.1+dfsg/debian/patches/use_python3.patch botan3-3.12.0+dfsg/debian/patches/use_python3.patch --- botan3-3.7.1+dfsg/debian/patches/use_python3.patch 2022-01-23 19:17:06.000000000 +0000 +++ botan3-3.12.0+dfsg/debian/patches/use_python3.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,185 +0,0 @@ -Description: use Python 3 everywhere - Execute python3 binary instead of simple python which is the 2.x version. -Author: Laszlo Boszormenyi (GCS) -Bug-Debian: https://bugs.debian.org/936230 -Forwarded: no -Last-Update: 2020-01-26 - ---- - ---- botan-2.13.0.orig/configure.py -+++ botan-2.13.0/configure.py -@@ -1,4 +1,4 @@ --#!/usr/bin/env python -+#!/usr/bin/env python3 - - """ - Configuration program for botan ---- botan-2.13.0.orig/src/python/botan2.py -+++ botan-2.13.0/src/python/botan2.py -@@ -1,4 +1,4 @@ --#!/usr/bin/python -+#!/usr/bin/python3 - - """ - Python wrapper of the botan crypto library ---- botan-2.13.0.orig/src/scripts/bench.py -+++ botan-2.13.0/src/scripts/bench.py -@@ -1,4 +1,4 @@ --#!/usr/bin/python -+#!/usr/bin/python3 - - """ - Compare Botan with OpenSSL using their respective benchmark utils ---- botan-2.13.0.orig/src/scripts/build_docs.py -+++ botan-2.13.0/src/scripts/build_docs.py -@@ -1,4 +1,4 @@ --#!/usr/bin/env python -+#!/usr/bin/env python3 - - """ - Botan doc generation script ---- botan-2.13.0.orig/src/scripts/ci_build.py -+++ botan-2.13.0/src/scripts/ci_build.py -@@ -1,4 +1,4 @@ --#!/usr/bin/env python -+#!/usr/bin/env python3 - - """ - CI build script ---- botan-2.13.0.orig/src/scripts/cleanup.py -+++ botan-2.13.0/src/scripts/cleanup.py -@@ -1,4 +1,4 @@ --#!/usr/bin/env python -+#!/usr/bin/env python3 - - """ - Implements the "make clean" target ---- botan-2.13.0.orig/src/scripts/create_corpus_zip.py -+++ botan-2.13.0/src/scripts/create_corpus_zip.py -@@ -1,4 +1,4 @@ --#!/usr/bin/python -+#!/usr/bin/python3 - - # These is used to create fuzzer corpus zip files - ---- botan-2.13.0.orig/src/scripts/dist.py -+++ botan-2.13.0/src/scripts/dist.py -@@ -1,4 +1,4 @@ --#!/usr/bin/env python -+#!/usr/bin/env python3 - - """ - Release script for botan (https://botan.randombit.net/) ---- botan-2.13.0.orig/src/scripts/ffi_decls.py -+++ botan-2.13.0/src/scripts/ffi_decls.py -@@ -1,4 +1,4 @@ --#!/usr/bin/python -+#!/usr/bin/python3 - - """ - Automatically generate declarations for the FFI layer ---- botan-2.13.0.orig/src/scripts/install.py -+++ botan-2.13.0/src/scripts/install.py -@@ -1,4 +1,4 @@ --#!/usr/bin/env python -+#!/usr/bin/env python3 - - """ - Botan install script ---- botan-2.13.0.orig/src/scripts/macro_checks.py -+++ botan-2.13.0/src/scripts/macro_checks.py -@@ -1,4 +1,4 @@ --#!/usr/bin/python -+#!/usr/bin/python3 - - # (C) 2018 Jack Lloyd - # Botan is released under the Simplified BSD License (see license.txt) ---- botan-2.13.0.orig/src/scripts/oids.py -+++ botan-2.13.0/src/scripts/oids.py -@@ -1,4 +1,4 @@ --#!/usr/bin/python -+#!/usr/bin/python3 - - """ - (C) 2016 Jack Lloyd ---- botan-2.13.0.orig/src/scripts/run_tls_attacker.py -+++ botan-2.13.0/src/scripts/run_tls_attacker.py -@@ -1,4 +1,4 @@ --#!/usr/bin/python -+#!/usr/bin/python3 - - import os - import sys ---- botan-2.13.0.orig/src/scripts/run_tls_fuzzer.py -+++ botan-2.13.0/src/scripts/run_tls_fuzzer.py -@@ -1,4 +1,4 @@ --#!/usr/bin/python -+#!/usr/bin/python3 - - import argparse - import subprocess ---- botan-2.13.0.orig/src/scripts/show_dependencies.py -+++ botan-2.13.0/src/scripts/show_dependencies.py -@@ -1,4 +1,4 @@ --#!/usr/bin/env python -+#!/usr/bin/env python3 - - """ - Show Botan module dependencies as a list or graph. ---- botan-2.13.0.orig/src/scripts/test_all_configs.py -+++ botan-2.13.0/src/scripts/test_all_configs.py -@@ -1,4 +1,4 @@ --#!/usr/bin/python -+#!/usr/bin/python3 - - """ - This configures and builds with many different sub-configurations ---- botan-2.13.0.orig/src/scripts/test_cli_crypt.py -+++ botan-2.13.0/src/scripts/test_cli_crypt.py -@@ -1,4 +1,4 @@ --#!/usr/bin/env python -+#!/usr/bin/env python3 - - import binascii - import argparse ---- botan-2.13.0.orig/src/scripts/test_fuzzers.py -+++ botan-2.13.0/src/scripts/test_fuzzers.py -@@ -1,4 +1,4 @@ --#!/usr/bin/python -+#!/usr/bin/python3 - - # (C) 2017,2018 Jack Lloyd - ---- botan-2.13.0.orig/src/scripts/test_python.py -+++ botan-2.13.0/src/scripts/test_python.py -@@ -1,4 +1,4 @@ --#!/usr/bin/env python -+#!/usr/bin/env python3 - - """ - (C) 2015,2017,2018,2019 Jack Lloyd ---- botan-2.13.0.orig/src/scripts/tls_scanner/tls_scanner.py -+++ botan-2.13.0/src/scripts/tls_scanner/tls_scanner.py -@@ -1,4 +1,4 @@ --#!/usr/bin/python2 -+#!/usr/bin/python3 - - import sys - import time ---- botan-2.13.0.orig/src/scripts/tls_suite_info.py -+++ botan-2.13.0/src/scripts/tls_suite_info.py -@@ -1,4 +1,4 @@ --#!/usr/bin/env python2 -+#!/usr/bin/env python3 - - """ - Used to generate lib/tls/tls_suite_info.cpp from IANA params ---- botan-2.13.0.orig/src/scripts/website.py -+++ botan-2.13.0/src/scripts/website.py -@@ -1,4 +1,4 @@ --#!/usr/bin/python -+#!/usr/bin/python3 - - """ - Generate the Botan website diff -Nru botan3-3.7.1+dfsg/debian/rules botan3-3.12.0+dfsg/debian/rules --- botan3-3.7.1+dfsg/debian/rules 2024-02-23 21:36:42.000000000 +0000 +++ botan3-3.12.0+dfsg/debian/rules 2025-08-20 14:57:48.000000000 +0000 @@ -39,7 +39,6 @@ $(CROSS_FLAGS) \ --prefix=/usr/ \ --libdir=/usr/lib/$(DEB_HOST_MULTIARCH) \ - $(if $(filter $(DEB_HOST_ARCH), armel armhf),--disable-neon) \ --with-rst2man \ --with-bzip2 \ --with-lzma \ diff -Nru botan3-3.7.1+dfsg/debian/watch botan3-3.12.0+dfsg/debian/watch --- botan3-3.7.1+dfsg/debian/watch 2023-11-10 19:04:33.000000000 +0000 +++ botan3-3.12.0+dfsg/debian/watch 2025-11-07 16:22:38.000000000 +0000 @@ -1,13 +1,7 @@ -version=4 -# GitHub -opts=uversionmangle=s/(\d)[_\.\-\+]?((rc|pre|dev|beta|alpha|b|a)[\-\.]?\d*)$/$1~$2/i,\ -dversionmangle=s/\+(debian|dfsg|ds|deb)(\.?\d+)?$//i,\ -pgpsigurlmangle=s/$/.asc/ \ -https://github.com/randombit/botan/tags \ -(?:|.*/)v?(3.\d\S*)@ARCHIVE_EXT@ +Version: 5 -# Upstream -opts=dversionmangle=s/\+(debian|dfsg|ds|deb)(\.?\d+)?$//,\ -uversionmangle=s/-?(beta)-?/~$1/;s/-?(alpha)-?/~$1/;s/-?(rc)-?/~rc/;s/\.?(RC)-?/~rc/,\ -pgpsigurlmangle=s/$/.asc/ \ -https://botan.randombit.net/releases/ Botan-(3.\d\S+)@ARCHIVE_EXT@ +Template: Github +Dversionmangle: s/\+(debian|dfsg|ds|deb)(\.?\d+)?$//i +Owner: randombit +Project: botan +Pgpsigurlmangle: s/$/.asc/ diff -Nru botan3-3.7.1+dfsg/doc/api_ref/bigint.rst botan3-3.12.0+dfsg/doc/api_ref/bigint.rst --- botan3-3.7.1+dfsg/doc/api_ref/bigint.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/api_ref/bigint.rst 2026-05-07 01:38:28.000000000 +0000 @@ -1,35 +1,74 @@ BigInt ======================================== -``BigInt`` is Botan's implementation of a multiple-precision integer. Thanks to -C++'s operator overloading features, using ``BigInt`` is often quite similar to -using a native integer type. The number of functions related to ``BigInt`` is -quite large, and not all of them are documented here. You can find the complete -declarations in ``botan/bigint.h`` and ``botan/numthry.h``. +``BigInt``, in ``bigint.h``, is an implementation of a signed magnitude +multiple-precision integer, which is used to implement certain older public key +algorithms such as RSA. It also appears in other contexts, for example X.509 +certificate serial numbers are technically integer values and can be quite +large, and so are represented using a ``BigInt``. + +A ``BigInt`` is a sequence of smaller integers of type ``word``; this type is +defined to be either ``uint32_t`` or ``uint64_t``, depending on the word size of +the processor. + +.. warning:: + + While it is possible to use the APIs provided by ``BigInt`` as a general + calculation facility, it is **extremely inadvisable** that you attempt to + implement a cryptographic scheme of any kind directly using ``BigInt``. + Botan internally has many facilities for fast and side channel safe + arithmetic which are not exposed to callers. + + In general, as a library user, avoid doing anything with ``BigInt`` besides + serializing or deserializing them as required to call other interfaces. + Some of the general calculation facilities of ``BigInt`` may be made internal + to the library in a future major release. .. cpp:class:: BigInt - .. cpp:function:: BigInt() + .. cpp:function:: static BigInt BigInt::from_string(std::string_view str) - Create a BigInt with value zero + Create a BigInt from a string. By default decimal is expected. With an 0x + prefix, instead it is treated as hexadecimal. A ``-`` prefix to indicate + negative numbers is also accepted. - .. cpp:function:: BigInt::from_u64(uint64_t n) + .. cpp:function:: static BigInt::from_bytes(std::span buf) - Create a BigInt with value *n* + Create a BigInt from a binary array (big-endian encoding). The result of + this function will always be positive; there is no support for a sign bit, + 2s complement encoding, or similar methods for indicating a negative value. - .. cpp:function:: BigInt(std::string_view str) + .. cpp:function:: void serialize_to(std::span buf) - Create a BigInt from a string. By default decimal is expected. With an 0x - prefix instead it is treated as hexadecimal. A ``-`` prefix to indicate - negative numbers is also accepted. + Encode this BigInt as a big-endian integer. The sign is ignored. - .. cpp:function:: BigInt(std::span buf) + There must be sufficient space to encode the entire integer in ``buf``. + If ``buf`` is larger than required, sufficient zero bytes will be + prefixed. + + .. cpp:function:: size_t bytes() const + + Return number of bytes needed to represent value of ``*this`` + + .. cpp:function:: size_t bits() const - Create a BigInt from a binary array (big-endian encoding). + Return number of bits needed to represent value of ``*this`` - .. cpp:function:: BigInt(RandomNumberGenerator& rng, size_t bits, bool set_high_bit = true) + .. cpp:function:: std::string to_dec_string() const - Create a random BigInt of the specified size. + Encode the integer as a decimal string. + + .. cpp:function:: std::string to_hex_string() const + + Encode the integer as a hexadecimal string, with "0x" prefix + + .. cpp:function:: BigInt::zero() + + Create a BigInt with value zero + + .. cpp:function:: BigInt::from_u64(uint64_t n) + + Create a BigInt with value *n* .. cpp:function:: BigInt operator+(const BigInt& x, const BigInt& y) @@ -147,13 +186,10 @@ Set ``*this`` to zero - .. cpp:function:: size_t bytes() const - - Return number of bytes need to represent value of ``*this`` - - .. cpp:function:: size_t bits() const + .. cpp:function:: uint32_t to_u32bit() const - Return number of bits need to represent value of ``*this`` + Return value of ``*this`` as a 32-bit integer, if possible. + If the integer is negative or not in range, an exception is thrown. .. cpp:function:: bool is_even() const @@ -171,103 +207,15 @@ Return true if ``*this`` is zero - .. cpp:function:: void set_bit(size_t n) - - Set bit *n* of ``*this`` - - .. cpp:function:: void clear_bit(size_t n) - - Clear bit *n* of ``*this`` - - .. cpp:function:: bool get_bit(size_t n) const - - Get bit *n* of ``*this`` - - .. cpp:function:: uint32_t to_u32bit() const - - Return value of ``*this`` as a 32-bit integer, if possible. - If the integer is negative or not in range, an exception is thrown. - .. cpp:function:: bool is_negative() const - Return true if ``*this`` is negative + Return true if ``*this`` is less than zero .. cpp:function:: bool is_positive() const - Return true if ``*this`` is negative + Return true if ``*this`` is greater than or equal to zero .. cpp:function:: BigInt abs() const Return absolute value of ``*this`` - .. cpp:function:: void serialize_to(std::span buf) - - Encode this BigInt as a big-endian integer. The sign is ignored. - - There must be sufficient space to encode the entire integer in ``buf``. - If ``buf`` is larger than required, sufficient zero bytes will be - prefixed. - - .. cpp:function:: std::string to_dec_string() const - - Encode the integer as a decimal string. - - .. cpp:function:: std::string to_hex_string() const - - Encode the integer as a hexadecimal string, with "0x" prefix - -Number Theory ----------------------------------------- - -Number theoretic functions available include: - -.. cpp:function:: BigInt gcd(BigInt x, BigInt y) - - Returns the greatest common divisor of x and y - -.. cpp:function:: BigInt lcm(BigInt x, BigInt y) - - Returns an integer z which is the smallest integer such that z % x - == 0 and z % y == 0 - -.. cpp:function:: BigInt jacobi(BigInt a, BigInt n) - - Return Jacobi symbol of (a|n). - -.. cpp:function:: BigInt inverse_mod(BigInt x, BigInt m) - - Returns the modular inverse of x modulo m, that is, an integer - y such that (x*y) % m == 1. If no such y exists, returns zero. - -.. cpp:function:: BigInt power_mod(BigInt b, BigInt x, BigInt m) - - Returns b to the xth power modulo m. If you are doing many - exponentiations with a single fixed modulus, it is faster to use a - ``Power_Mod`` implementation. - -.. cpp:function:: BigInt ressol(BigInt x, BigInt p) - - Returns the square root modulo a prime, that is, returns a number y - such that (y*y) % p == x. Returns -1 if no such integer exists. - -.. cpp:function:: bool is_prime(BigInt n, RandomNumberGenerator& rng, \ - size_t prob = 56, double is_random = false) - - Test *n* for primality using a probabilistic algorithm (Miller-Rabin). With - this algorithm, there is some non-zero probability that true will be returned - even if *n* is actually composite. Modifying *prob* allows you to decrease the - chance of such a false positive, at the cost of increased runtime. Sufficient - tests will be run such that the chance *n* is composite is no more than 1 in - 2\ :sup:`prob`. Set *is_random* to true if (and only if) *n* was randomly - chosen (ie, there is no danger it was chosen maliciously) as far fewer tests - are needed in that case. - -.. cpp:function:: BigInt random_prime(RandomNumberGenerator& rng, \ - size_t bits, \ - BigInt coprime = 1, \ - size_t equiv = 1, \ - size_t equiv_mod = 2) - - Return a random prime number of ``bits`` bits long that is - relatively prime to ``coprime``, and equivalent to ``equiv`` modulo - ``equiv_mod``. diff -Nru botan3-3.7.1+dfsg/doc/api_ref/cipher_modes.rst botan3-3.12.0+dfsg/doc/api_ref/cipher_modes.rst --- botan3-3.7.1+dfsg/doc/api_ref/cipher_modes.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/api_ref/cipher_modes.rst 2026-05-07 01:38:28.000000000 +0000 @@ -158,6 +158,11 @@ Available Unauthenticated Cipher Modes ----------------------------------------- +.. warning:: + + As noted above these modes are insecure if used without an authentication code. + Prefer using an AEAD. + .. note:: CTR and OFB modes are also implemented, but these are treated as :cpp:class:`Stream_Cipher`\s instead. @@ -167,6 +172,12 @@ Available if ``BOTAN_HAS_MODE_CBC`` is defined. +CBC mode has a significant drawback, namely that due to its structure, when +encrypting a message it is not possible to process multiple blocks simultaneously. +This effectively prevents any use of optimizations based on SIMD or interleaving, +resulting in relatively poor performance compared to the same cipher in another +mode. + CBC requires the plaintext be padded using a reversible rule. The following padding schemes are implemented @@ -350,6 +361,27 @@ more obscure (and is slower than either GCM or ChaCha20Poly1305), but has excellent security properties. +Ascon-AEAD128 +~~~~~~~~~~~~~ + +Available if ``BOTAN_HAS_ASCON_AEAD128`` is defined. + +An AEAD scheme based on the Ascon permutation, specifically designed to allow +small footprint implementations. Its main use case is in constrained +environments, such as IoT devices where traditional cryptographic functions +may be too resource intensive. + +Unless you are interoperating with an existing device which due to resource +constraints can only use Ascon, prefer more typical AEADs such as AES-256/GCM, +AES-256/SIV, or ChaCha20Poly1305. + +This AEAD scheme is standardized by NIST in SP.800-232. It is not compatible +with earlier versions of the Ascon specification. The current implementation +does not provide explicit support for the tag truncation and nonce masking +features specified in the standard. + +Algorithm specification name: ``Ascon-AEAD128`` + CCM ~~~~~ diff -Nru botan3-3.7.1+dfsg/doc/api_ref/ecc.rst botan3-3.12.0+dfsg/doc/api_ref/ecc.rst --- botan3-3.7.1+dfsg/doc/api_ref/ecc.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/api_ref/ecc.rst 2026-05-07 01:38:28.000000000 +0000 @@ -57,7 +57,7 @@ Return a random non-zero scalar value - .. cpp:function:: static EC_Scalar gk_x_mod_order(const EC_Scalar& k, RandomNumberGenerator& rng, std::vector& ws) + .. cpp:function:: static EC_Scalar gk_x_mod_order(const EC_Scalar& k, RandomNumberGenerator& rng) Compute the elliptic curve scalar multiplication (``g*k``) where ``g`` is the standard base point on the curve. Then extract the ``x`` coordinate @@ -66,6 +66,18 @@ If ``k`` is zero (resulting in the scalar multiplication producing the identity element) then this function returns zero. + .. cpp:function:: static EC_Scalar hash(const EC_Group& group, \ + std::string_view hash_fn, \ + std::span input, \ + std::span domain_sep) + + Hash to scalar following RFC 9380. + + This deterministically and portably hashes the provided input and domain + separator into an integer modulo the group order. + + This function is supported for all groups. + .. cpp:function:: size_t bytes() const Return the byte length of the scalar @@ -140,12 +152,12 @@ Return true if this point is the identity element. - .. cpp:function:: EC_AffinePoint mul(const EC_Scalar& scalar, RandomNumberGenerator& rng, std::vector& ws) const + .. cpp:function:: EC_AffinePoint mul(const EC_Scalar& scalar, RandomNumberGenerator& rng) const Variable base scalar multiplication. Constant time. If the rng object is seeded, also uses blinding and point rerandomization. - .. cpp:function:: static EC_AffinePoint g_mul(const EC_Scalar& scalar, RandomNumberGenerator& rng, std::vector& ws) + .. cpp:function:: static EC_AffinePoint g_mul(const EC_Scalar& scalar, RandomNumberGenerator& rng) Fixed base scalar multiplication. Constant time. If the rng object is seeded, also uses blinding and point rerandomization. diff -Nru botan3-3.7.1+dfsg/doc/api_ref/env_vars.rst botan3-3.12.0+dfsg/doc/api_ref/env_vars.rst --- botan3-3.7.1+dfsg/doc/api_ref/env_vars.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/api_ref/env_vars.rst 2026-05-07 01:38:28.000000000 +0000 @@ -6,6 +6,9 @@ Certain environment variables can affect or tune the behavior of the library. The variables and their behavior are described here. +These values can be set in the environment before the program starts, or using +``setenv`` somewhere at the start of ``main``, before Botan has been invoked. + * ``BOTAN_THREAD_POOL_SIZE`` controls the number of threads which will be created for a thread pool used for some purposes within the library. If not set, or set to 0, then it defaults to the number of CPUs available on the diff -Nru botan3-3.7.1+dfsg/doc/api_ref/ffi.rst botan3-3.12.0+dfsg/doc/api_ref/ffi.rst --- botan3-3.7.1+dfsg/doc/api_ref/ffi.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/api_ref/ffi.rst 2026-05-07 01:38:28.000000000 +0000 @@ -20,7 +20,7 @@ Writing language bindings for C or C++ libraries is typically a tedious and bug-prone experience. This FFI layer was designed to make the experience, if not -pleasant, at least straighforward. +pleasant, at least straightforward. * All objects manipulated by the API are opaque structs. Each struct is tagged with a 32-bit magic number which is unique to its type; accidentally passing @@ -103,6 +103,11 @@ While decrypting in an AEAD mode, the tag failed to verify. +.. cpp:enumerator:: BOTAN_FFI_ERROR_NO_VALUE = -3 + + Given the context of the invocation no semantically reasonable value could + be produced, any provided out-parameters must be ignored. + .. cpp:enumerator:: BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE = -10 Functions which write a variable amount of space return this if the indicated @@ -165,6 +170,11 @@ An operation was invoked that makes sense for the object, but it is in the wrong state to perform it. +.. cpp:enumerator:: BOTAN_FFI_ERROR_OUT_OF_RANGE = -36 + + Querying an enumerable value resulted in an "out of range" error. This error + code may be used as the marker for the end of a value enumeration. + .. cpp:enumerator:: BOTAN_FFI_ERROR_NOT_IMPLEMENTED = -40 This is returned if the functionality is not available for some reason. For @@ -179,7 +189,7 @@ .. cpp:enumerator:: BOTAN_FFI_TPM_ERROR = -78 - An error occured when performing TPM2 interactions. + An error occurred when performing TPM2 interactions. .. cpp:enumerator:: BOTAN_FFI_ERROR_UNKNOWN_ERROR = -100 @@ -188,7 +198,7 @@ Error values below -10000 are reserved for the application (these can be returned from view functions). -Further information about the error that occured is available via +Further information about the error that occurred is available via .. cpp:function:: const char* botan_error_last_exception_message() @@ -249,6 +259,13 @@ ============== =================== FFI Version Supported Starting ============== =================== +20260506 3.12.0 +20260303 3.11.0 +20250829 3.10.0 +20250506 3.8.0 +20240408 3.4.0 +20231009 3.2.0 +20230711 3.1.0 20230403 3.0.0 20210220 2.18.0 20191214 2.13.0 @@ -642,7 +659,7 @@ Multiple Precision Integers ---------------------------------------- -.. versionadded: 2.1.0 +.. versionadded:: 2.1.0 .. cpp:type:: opaque* botan_mp_t @@ -658,11 +675,34 @@ .. cpp:function:: int botan_mp_to_hex(botan_mp_t mp, char* out) - Writes exactly ``botan_mp_num_bytes(mp)*2 + 1`` bytes to out + Writes the hex encoding to the ``out`` parameter. This must point to a pre-allocated + buffer of at least ``botan_mp_num_bytes(mp)*2 + 5`` bytes. Some number of bytes will + be written, followed by a null terminator. + + .. warning:: + + This function is error-prone to use since the caller is not able to specify the + length of the buffer, so if insufficient space is allocated an overwrite will occur, + instead of the function returning ``BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE`` as + is typical for FFI. Prefer :cpp:func:`botan_mp_view_hex` which avoids this problem. + +.. cpp:function:: int botan_mp_view_hex(botan_mp_t mp, botan_view_ctx ctx, botan_view_str_fn view) -.. cpp:function:: int botan_mp_to_str(botan_mp_t mp, uint8_t base, char* out, size_t* out_len) + View the hex encoding of the integer. - Base can be either 10 or 16. +.. cpp:function:: int botan_mp_to_str(botan_mp_t mp, uint8_t radix, char* out, size_t* out_len) + + The ``radix`` can currently be either 10 or 16. If ``radix`` is 16 this behaves + identically to :cpp:func:`botan_mp_to_hex` with the addition that the output length is + checked rather than assumed. + + .. note:: + + Prefer using :cpp:func:`botan_mp_view_str` + +.. cpp:function:: int botan_mp_view_str(botan_mp_t mp, uint8_t radix, botan_view_ctx ctx, botan_view_str_fn view) + + View the string encoding of the integer. The radix can currently be either 10 or 16. .. cpp:function:: int botan_mp_set_from_int(botan_mp_t mp, int initial_value) @@ -688,6 +728,16 @@ Writes exactly ``botan_mp_num_bytes(mp)`` to ``vec``. + Note that the sign of ``mp`` is ignored. + + .. note:: + + Prefer :cpp:func:`botan_mp_view_bin`. + +.. cpp:function:: int botan_mp_view_bin(botan_mp_t mp, botan_view_ctx ctx, botan_view_bin_fn view) + + View the big-endian byte encoding of the integer. Note that the sign of ``mp`` is ignored. + .. cpp:function:: int botan_mp_from_bin(botan_mp_t mp, const uint8_t vec[], size_t vec_len) Loads ``botan_mp_t`` from a binary vector (as produced by ``botan_mp_to_bin``). @@ -830,6 +880,243 @@ if the combination is not valid (but otherwise well formed), negative on error. + +Object Identifiers +---------------------------------------- + +.. versionadded:: 3.8.0 + +.. cpp:type:: opaque* botan_asn1_oid_t + + An opaque data type for an object identifier. Don't mess with it. + +.. cpp:function:: int botan_oid_destroy(botan_asn1_oid_t oid) + + Destroy an object. + +.. cpp:function:: int botan_oid_from_string(botan_asn1_oid_t* oid, const char* oid_str) + + Create an OID from a string, either dot notation (e.g. '1.2.3.4') or a registered name (e.g. 'RSA') + +.. cpp:function:: int botan_oid_register(botan_asn1_oid_t oid, const char* name) + + Register an OID so that it may later be retrieved by name + +.. cpp:function:: int botan_oid_view_string(botan_asn1_oid_t oid, botan_view_ctx ctx, botan_view_str_fn view) + + View the OID in dot notation + +.. cpp:function:: int botan_oid_view_name(botan_asn1_oid_t oid, botan_view_ctx ctx, botan_view_str_fn view) + + View the OID as a name if it has one, otherwise as dot notation + +.. cpp:function:: int botan_oid_equal(botan_asn1_oid_t a, botan_asn1_oid_t b) + + Three way comparison: set result to -1 if ``a`` is less than ``b``, + 0 if ``a`` is equal to ``b``, and 1 if ``a`` is greater than ``b``. + +.. cpp:function:: int botan_oid_cmp(int* result, botan_asn1_oid_t a, botan_asn1_oid_t b) + + Return 1 if ``a`` is equal to ``b``, 0 if ``a`` is not equal to ``b`` + + +EC Groups +---------------------------------------- + +.. versionadded:: 3.8.0 + +.. cpp:type:: opaque* botan_ec_group_t + + An opaque data type for an EC Group. Don't mess with it. + +.. cpp:function:: int botan_ec_group_destroy(botan_ec_group_t oid) + + Destroy an object. + +.. cpp:function:: int botan_ec_group_supports_application_specific_group(int* out) + + Checks if in this build configuration it is possible to register an application specific elliptic curve, + and sets ``out`` to 1 if so, 0 otherwise. + +.. cpp:function:: int botan_ec_group_supports_named_group(const char* name, int* out) + + Checks if in this build configuration botan_ec_group_from_name(group_ptr, name) will succeed, + and sets ``out`` to 1 if so, 0 otherwise. + +.. cpp:function:: int botan_ec_group_from_params(botan_ec_group_t* ec_group, \ + botan_asn1_oid_t oid, \ + botan_mp_t p, \ + botan_mp_t a, \ + botan_mp_t b, \ + botan_mp_t base_x, \ + botan_mp_t base_y, \ + botan_mp_t order) + + Create a new EC Group from the given parameters. + + .. warning:: + Use only elliptic curve parameters you trust. + +.. cpp:function:: int botan_ec_group_from_ber(botan_ec_group_t* ec_group, const uint8_t* ber, size_t ber_len) + + Decode a BER encoded ECC domain parameter set + +.. cpp:function:: int botan_ec_group_from_pem(botan_ec_group_t* ec_group, const char* pem) + + Initialize an EC Group from the PEM/ASN.1 encoding + +.. cpp:function:: int botan_ec_group_from_oid(botan_ec_group_t* ec_group, botan_asn1_oid_t oid) + + Initialize an EC Group from a group named by an object identifier + +.. cpp:function:: int botan_ec_group_from_name(botan_ec_group_t* ec_group, const char* name) + + Initialize an EC Group from a common group name (eg "secp256r1") + +.. cpp:function:: int botan_ec_group_unregister(botan_asn1_oid_t oid) + + Unregister a previously registered group. Returns 1 if the group was found and unregistered, else 0. + + Using this is discouraged for normal use. This is only useful or necessary if + you are registering a very large number of distinct groups, and need to worry about memory constraints. + +.. cpp:function:: int botan_ec_group_view_der(botan_ec_group_t ec_group, botan_view_ctx ctx, botan_view_bin_fn view) + + View an EC Group in DER encoding + +.. cpp:function:: int botan_ec_group_view_pem(botan_ec_group_t ec_group, botan_view_ctx ctx, botan_view_str_fn view) + + View an EC Group in PEM encoding + +.. cpp:function:: int botan_ec_group_get_curve_oid(botan_asn1_oid_t* oid, botan_ec_group_t ec_group) + + Get the curve OID of an EC Group + +.. cpp:function:: int botan_ec_group_get_p(botan_mp_t* p, botan_ec_group_t ec_group) + + Get the prime modulus of the field + +.. cpp:function:: int botan_ec_group_get_a(botan_mp_t* a, botan_ec_group_t ec_group) + + Get the a parameter of the elliptic curve equation + +.. cpp:function:: int botan_ec_group_get_b(botan_mp_t* b, botan_ec_group_t ec_group) + + Get the b parameter of the elliptic curve equation + +.. cpp:function:: int botan_ec_group_get_g_x(botan_mp_t* g_x, botan_ec_group_t ec_group) + + Get the x coordinate of the base point + +.. cpp:function:: int botan_ec_group_get_g_y(botan_mp_t* g_y, botan_ec_group_t ec_group) + + Get the y coordinate of the base point + +.. cpp:function:: int botan_ec_group_get_order(botan_mp_t* order, botan_ec_group_t ec_group) + + Get the order of the base point + +.. cpp:function:: int botan_ec_group_equal(botan_ec_group_t curve1, botan_ec_group_t curve2) + + Return 1 if ``curve1`` is equal to ``curve2``, 0 if ``curve1`` is not equal to ``curve2`` + + +EC Points and Scalars +---------------------------------------- + +.. versionadded:: 3.12.0 + +.. cpp:type:: opaque* botan_ec_scalar_t + + An opaque data type for an EC Scalar. Don't mess with it. + +.. cpp:type:: opaque* botan_ec_point_t + + An opaque data type for an EC Point. Don't mess with it. + +.. cpp:function:: int botan_ec_scalar_destroy(botan_ec_scalar_t ec_scalar) + + Destroy an object. + +.. cpp:function:: int botan_ec_scalar_random(botan_ec_scalar_t* ec_scalar, botan_ec_group_t ec_group, botan_rng_t rng); + + Create a scalar with a random value. + +.. cpp:function:: int botan_ec_scalar_from_mp(botan_ec_scalar_t* ec_scalar, botan_ec_group_t ec_group, botan_mp_t mp); + + Convert from an MPI to a scalar. + +.. cpp:function:: int botan_ec_scalar_to_mp(botan_ec_scalar_t ec_scalar, botan_mp_t* mp) + + Convert from a scalar to an MPI. + +.. cpp:function:: int botan_ec_point_destroy(botan_ec_point_t ec_point) + + Destroy an object. + +.. cpp:function:: int botan_ec_point_identity(botan_ec_point_t* ec_point, botan_ec_group_t ec_group); + + Create a point set to the identity element of the group. + +.. cpp:function:: int botan_ec_point_generator(botan_ec_point_t* ec_point, botan_ec_group_t ec_group); + + Create a point set to the standard group generator. + +.. cpp:function:: int botan_ec_point_from_xy(botan_ec_point_t* ec_point, botan_ec_group_t ec_group, botan_mp_t x, botan_mp_t y); + + Create a point from a pair (x,y) of integers. + The integers must be within the field and must satisfy the curve equation. + +.. cpp:function:: int botan_ec_point_from_bytes(botan_ec_point_t* ec_point, \ + botan_ec_group_t ec_group, \ + const uint8_t* bytes, \ + size_t bytes_len); + + Create a point from a SEC1 compressed or uncompressed format. + +.. cpp:function:: int botan_ec_point_view_x_bytes(botan_ec_point_t ec_point, botan_view_ctx ctx, botan_view_bin_fn view); + + View the fixed length encoding of the affine x coordinate. + +.. cpp:function:: int botan_ec_point_view_y_bytes(botan_ec_point_t ec_point, botan_view_ctx ctx, botan_view_bin_fn view); + + View the fixed length encoding of the affine y coordinate. + +.. cpp:function:: int botan_ec_point_view_xy_bytes(botan_ec_point_t ec_point, botan_view_ctx ctx, botan_view_bin_fn view); + + View the fixed length encoding of the affine x and y coordinates. + +.. cpp:function:: int botan_ec_point_view_uncompressed(botan_ec_point_t ec_point, botan_view_ctx ctx, botan_view_bin_fn view); + + View the fixed length SEC1 uncompressed encoding. + +.. cpp:function:: int botan_ec_point_view_compressed(botan_ec_point_t ec_point, botan_view_ctx ctx, botan_view_bin_fn view); + + View the fixed length SEC1 compressed encoding. + +.. cpp:function:: int botan_ec_point_is_identity(botan_ec_point_t ec_point); + + Returns 1 if ``ec_point`` is equal to the group's identity element, otherwise 0. + +.. cpp:function:: int botan_ec_point_equal(botan_ec_point_t x, botan_ec_point_t y); + + Returns 1 if ``x`` == ``y``, otherwise 0. + +.. cpp:function:: int botan_ec_point_negate(botan_ec_point_t* result, botan_ec_point_t ec_point); + + Negates the provided point. + +.. cpp:function:: int botan_ec_point_add(botan_ec_point_t* result, botan_ec_point_t x, botan_ec_point_t y); + + Computes ``x`` + ``y``. + +.. cpp:function:: int botan_ec_point_mul(botan_ec_point_t* result, \ + botan_ec_point_t ec_point, \ + botan_ec_scalar_t ec_scalar, \ + botan_rng_t rng); + + Multiplies ``ec_point`` by the given ``ec_scalar``. + Public Key Creation, Import and Export ---------------------------------------- @@ -846,6 +1133,11 @@ const char* algo_params, \ botan_rng_t rng) +.. cpp:function:: int botan_ec_privkey_create(botan_privkey_t* key, \ + const char* algo_name, \ + botan_ec_group_t ec_group, \ + botan_rng_t rng) + .. cpp:function:: int botan_privkey_create_rsa(botan_privkey_t* key, botan_rng_t rng, size_t n_bits) Create an RSA key of the given size @@ -962,7 +1254,7 @@ Deprecated, use ``botan_privkey_export_encrypted_msec`` or ``botan_privkey_export_encrypted_iter`` -.. cpp::function:: int botan_privkey_export_encrypted_pbkdf_msec(botan_privkey_t key, +.. cpp:function:: int botan_privkey_export_encrypted_pbkdf_msec(botan_privkey_t key, \ uint8_t out[], size_t* out_len, \ botan_rng_t rng, \ const char* passphrase, \ @@ -978,7 +1270,7 @@ ``cipher_algo`` must specify a CBC mode cipher (such as "AES-128/CBC") or as a Botan-specific extension a GCM mode may be used. -.. cpp::function:: int botan_privkey_export_encrypted_pbkdf_iter(botan_privkey_t key, \ +.. cpp:function:: int botan_privkey_export_encrypted_pbkdf_iter(botan_privkey_t key, \ uint8_t out[], size_t* out_len, \ botan_rng_t rng, \ const char* passphrase, \ @@ -999,6 +1291,19 @@ Read an algorithm specific field from the private key object, placing it into output. For example "p" or "q" for RSA keys, or "x" for DSA keys or ECC keys. +.. cpp:function:: int botan_privkey_oid(botan_asn1_oid_t* oid, botan_privkey_t key) + + Get the key's associated OID. + +.. cpp:function:: int botan_privkey_stateful_operation(botan_privkey_t key, int* out) + + Checks whether a key is stateful and set ``out`` to 1 if it is, 0 otherwise. + +.. cpp:function:: int botan_privkey_remaining_operations(botan_privkey_t key, uint64_t* out) + + Set ``out`` to the number of remaining operations. + If the key is not stateful, an error will be returned. + .. cpp:type:: opaque* botan_pubkey_t An opaque data type for a public key. Don't mess with it. @@ -1039,6 +1344,10 @@ Read an algorithm specific field from the public key object, placing it into output. For example "n" or "e" for RSA keys or "p", "q", "g", and "y" for DSA keys. +.. cpp:function:: int botan_pubkey_oid(botan_asn1_oid_t* oid, botan_privkey_t key) + + Get the key's associated OID. + RSA specific functions ---------------------------------------- @@ -1084,6 +1393,21 @@ Initialize a public RSA key using parameters n and e. +EC specific functions +---------------------------------------- + +.. cpp:function:: int botan_ec_privkey_get_private_key(botan_privkey_t key, botan_ec_scalar_t* value) + + Get the private value of the EC key. + +.. cpp:function:: int botan_ec_privkey_get_group(botan_privkey_t key, botan_ec_group_t* ec_group) + + Get the group of this EC private key. + +.. cpp:function:: int botan_ec_pubkey_get_group(botan_pubkey_t key, botan_ec_group_t* ec_group) + + Get the group of this EC public key. + DSA specific functions ---------------------------------------- @@ -1449,6 +1773,11 @@ An opaque data type for an X.509 certificate. Don't mess with it. +.. cpp:type:: opaque* botan_x509_general_name_t + + An opaque data type for an X.509 GeneralName used to query subject/issuer + alternative names and name constraints. Don't mess with it. + .. cpp:function:: int botan_x509_cert_load(botan_x509_cert_t* cert_obj, \ const uint8_t cert[], size_t cert_len) @@ -1472,6 +1801,52 @@ const char* common_name, \ const char* org_name) +.. cpp:function:: int botan_x509_cert_view_binary_values(botan_x509_cert_t cert, \ + botan_x509_value_type value_type, \ + size_t index, \ + botan_view_ctx ctx, \ + botan_view_bin_fn view_fn) + + Access various binary fields of information contained in the certificate. + + Some of those may be multi-value fields, the `index` parameter may be used + to enumerate such values until :cpp:enumerator:`BOTAN_FFI_ERROR_OUT_OF_RANGE` + is returned. For singular values, an `index` of 0 must be used. + + See :ref:`x509_getter_function` for further information about the available + values. If a value does not exist :cpp:enumerator:`BOTAN_FFI_ERROR_NO_VALUE` + is returned. + +.. cpp:function:: int botan_x509_cert_view_binary_values_count(botan_x509_cert_t cert, \ + botan_x509_value_type value_type, \ + size_t* count) + + Get the number of entries for multi-value binary fields of information + contained in the certificate. + +.. cpp:function:: int botan_x509_cert_view_string_values(botan_x509_cert_t cert, \ + botan_x509_value_type value_type, \ + size_t index, \ + botan_view_ctx ctx, \ + botan_view_str_fn view_fn) + + Access various string fields of information contained in the certificate. + + Some of those may be multi-value fields, the `index` parameter may be used + to enumerate such values until :cpp:enumerator:`BOTAN_FFI_ERROR_OUT_OF_RANGE` + is returned. For singular values, an `index` of 0 must be used. + + See :ref:`x509_getter_function` for further information about the available + values. If a value does not exist :cpp:enumerator:`BOTAN_FFI_ERROR_NO_VALUE` + is returned. + +.. cpp:function:: int botan_x509_cert_view_string_values_count(botan_x509_cert_t cert, \ + botan_x509_value_type value_type, \ + size_t* count) + + Get the number of entries for multi-value string fields of information + contained in the certificate. + .. cpp:function:: int botan_x509_cert_get_time_starts(botan_x509_cert_t cert, char out[], size_t* out_len) Return the time the certificate becomes valid, as a string in form @@ -1496,7 +1871,19 @@ .. cpp:function:: int botan_x509_cert_get_serial_number(botan_x509_cert_t cert, uint8_t out[], size_t* out_len) - Return the serial number of the certificate. + Return the serial number of the certificate as big-endian encoded bytes. + +.. cpp:function:: int botan_x509_cert_serial_number(botan_x509_cert_t cert, botan_mp_t* serial_number) + + Return the serial number of the certificate as a multi-precision integer. + +.. cpp:function:: int botan_x509_cert_is_ca(botan_x509_cert_t cert) + + Check whether the certificate is marked as a CA certificate. + +.. cpp:function:: int botan_x509_cert_get_path_length_constraint(botan_x509_cert_t cert, size_t* path_len) + + Get the path length constraint for a CA certificate. .. cpp:function:: int botan_x509_cert_get_authority_key_id(botan_x509_cert_t cert, uint8_t out[], size_t* out_len) @@ -1524,13 +1911,27 @@ const char* key, size_t index, \ uint8_t out[], size_t* out_len) - Get a value from the issuer DN field. + Get a value from the issuer DN field. If the index is out of range, + :cpp:enumerator:`BOTAN_FFI_ERROR_BAD_PARAMETER` is returned for historical + reasons. + +.. cpp:function:: int botan_x509_cert_get_issuer_dn_count(botan_x509_cert_t cert, \ + const char* key, size_t* count) + + Get the number of values for a given key in the issuer DN field. .. cpp:function:: int botan_x509_cert_get_subject_dn(botan_x509_cert_t cert, \ const char* key, size_t index, \ uint8_t out[], size_t* out_len) - Get a value from the subject DN field. + Get a value from the subject DN field. If the index is out of range, + :cpp:enumerator:`BOTAN_FFI_ERROR_BAD_PARAMETER` is returned for historical + reasons. + +.. cpp:function:: int botan_x509_cert_get_subject_dn_count(botan_x509_cert_t cert, \ + const char* key, size_t* count) + + Get the number of values for a given key in the subject DN field. .. cpp:function:: int botan_x509_cert_to_string(botan_x509_cert_t cert, char out[], size_t* out_len) @@ -1550,6 +1951,112 @@ .. cpp:function:: int botan_x509_cert_allowed_usage(botan_x509_cert_t cert, unsigned int key_usage) +.. cpp:function:: int botan_x509_cert_allowed_extended_usage_str(botan_x509_cert_t cert, const char* oid) + + Check whether the certificate has the specified extended key usage OID from + `RFC 5280 - 4.2.1.12 `_. + If the certificate has no extended key usage extension, this will always + behave as if the requested OID is *not present*. + +.. cpp:function:: int botan_x509_cert_allowed_extended_usage_oid(botan_x509_cert_t cert, botan_asn1_oid_t oid) + + Check whether the certificate has the specified extended key usage OID from + `RFC 5280 - 4.2.1.12 `_. + If the certificate has no extended key usage extension, this will always + behave as if the requested OID is *not present*. + +.. cpp:enum:: botan_x509_general_name_types + + GeneralName data types. Allowed values: + `BOTAN_X509_OTHER_NAME`, `BOTAN_X509_EMAIL_ADDRESS`, `BOTAN_X509_DNS_NAME`, + `BOTAN_X509_DIRECTORY_NAME`, `BOTAN_X509_URI`, `BOTAN_X509_IP_ADDRESS`. + +.. cpp:function:: int botan_x509_general_name_get_type(botan_x509_general_name_t name, unsigned int* type) + + Get the data type of the GeneralName object as a member of + :cpp:enum:`botan_x509_general_name_types`. Depending on this type, one of the + view functions below can be used to extract the value. + + `BOTAN_X509_DIRECTORY_NAME` is a binary DER encoding of a distinguished name. + `BOTAN_X509_IP_ADDRESS` is a big endian binary encoding of the IP address + optionally concatenated with the subnet mask. + `BOTAN_X509_EMAIL_ADDRESS`, `BOTAN_X509_DNS_NAME`, and `BOTAN_X509_URI` are + characters arrays. + Support for `BOTAN_X509_OTHER_NAME` is deprecated and cannot be viewed using + these functions. + +.. cpp:function:: int botan_x509_general_name_view_string_value(botan_x509_general_name_t name, \ + botan_view_ctx ctx, \ + botan_view_str_fn view) + + Allows querying the value of GeneralName objects of type + `BOTAN_X509_EMAIL_ADDRESS`, `BOTAN_X509_DNS_NAME`, `BOTAN_X509_URI`, and + `BOTAN_X509_IP_ADDRESS`. + +.. cpp:function:: int botan_x509_general_name_view_binary_value(botan_x509_general_name_t name, \ + botan_view_ctx ctx, \ + botan_view_bin_fn view) + + Allows querying the value of GeneralName objects of type + `BOTAN_X509_DIRECTORY_NAME` (as DER encoded distinguished name) and + `BOTAN_X509_IP_ADDRESS` (as big-endian encoded IP address + subnet mask). + +.. cpp:function:: int botan_x509_general_name_destroy(botan_x509_general_name_t alt_names) + + Destroy the GeneralName object. + +.. cpp:function:: int botan_x509_cert_permitted_name_constraints(botan_x509_cert_t cert, \ + size_t index, \ + botan_x509_general_name_t* constraint) + + Enumerate the permitted name constraints in the certificate as GeneralName + objects. If the given index is not available, + :cpp:enumerator:`BOTAN_FFI_ERROR_OUT_OF_RANGE` is returned. + +.. cpp:function:: int botan_x509_cert_permitted_name_constraints_count(botan_x509_cert_t cert, \ + size_t* count) + + Get the number of permitted name constraints in the certificate. + +.. cpp:function:: int botan_x509_cert_excluded_name_constraints(botan_x509_cert_t cert, \ + size_t index, \ + botan_x509_general_name_t* constraint) + + Enumerate the excluded name constraints in the certificate as GeneralName + objects. If the given index is not available, + :cpp:enumerator:`BOTAN_FFI_ERROR_OUT_OF_RANGE` is returned. + +.. cpp:function:: int botan_x509_cert_excluded_name_constraints_count(botan_x509_cert_t cert, \ + size_t* count) + + Get the number of excluded name constraints in the certificate. + +.. cpp:function:: int botan_x509_cert_subject_alternative_names(botan_x509_cert_t cert, \ + size_t index, \ + botan_x509_general_name_t* alt_name) + + Enumerate the subject alternative names in the certificate as GeneralName + objects. If the given index is not available, + :cpp:enumerator:`BOTAN_FFI_ERROR_OUT_OF_RANGE` is returned. + +.. cpp:function:: int botan_x509_cert_subject_alternative_names_count(botan_x509_cert_t cert, \ + size_t* count) + + Get the number of subject alternative names in the certificate. + +.. cpp:function:: int botan_x509_cert_issuer_alternative_names(botan_x509_cert_t cert, \ + size_t index, \ + botan_x509_general_name_t* alt_name) + + Enumerate the issuer alternative names in the certificate as GeneralName + objects. If the given index is not available, + :cpp:enumerator:`BOTAN_FFI_ERROR_OUT_OF_RANGE` is returned. + +.. cpp:function:: int botan_x509_cert_issuer_alternative_names_count(botan_x509_cert_t cert, \ + size_t* count) + + Get the number of issuer alternative names in the certificate. + .. cpp:function:: int botan_x509_cert_verify(int* validation_result, \ botan_x509_cert_t cert, \ const botan_x509_cert_t* intermediates, \ @@ -1597,7 +2104,7 @@ Certificate path validation supporting Certificate Revocation Lists. - Works the same as ``botan_x509_cert_cerify``. + Works the same as ``botan_x509_cert_verify``. ``crls`` is an array of ``botan_x509_crl_t`` objects, ``crls_len`` is its length. @@ -1613,6 +2120,10 @@ An opaque data type for an X.509 CRL. +.. cpp:type:: opaque* botan_x509_crl_entry_t + + An opaque data type for an X.509 CRL entry. + .. cpp:function:: int botan_x509_crl_load(botan_x509_crl_t* crl_obj, \ const uint8_t crl[], size_t crl_len) @@ -1622,15 +2133,223 @@ Load a CRL from a file. +.. cpp:function:: int botan_x509_crl_create(botan_x509_crl_t* crl_obj, \ + botan_rng_t rng, \ + botan_x509_cert_t ca_cert, \ + botan_privkey_t ca_key, \ + uint64_t issue_time, \ + uint32_t next_update, \ + const char* hash_fn, \ + const char* padding) + + Create a new CRL. ``issue_time`` is expected to be a UNIX timestamp, in seconds. + ``next_update`` is the number of seconds after ``issue_time`` until the CRL expires. + ``hash_fn`` and ``padding`` may be NULL. + +.. cpp:enum:: botan_x509_crl_reason_code + + CRL revocation reason codes. Allowed values: `BOTAN_CRL_ENTRY_UNSPECIFIED`, + `BOTAN_CRL_ENTRY_KEY_COMPROMISE`, `BOTAN_CRL_ENTRY_CA_COMPROMISE`, `BOTAN_CRL_ENTRY_AFFILIATION_CHANGED`, + `BOTAN_CRL_ENTRY_SUPERSEDED`, `BOTAN_CRL_ENTRY_CESSATION_OF_OPERATION`, `BOTAN_CRL_ENTRY_CERTIFICATE_HOLD`, + `BOTAN_CRL_ENTRY_REMOVE_FROM_CRL`, `BOTAN_CRL_ENTRY_PRIVILEGE_WITHDRAWN`, `BOTAN_CRL_ENTRY_AA_COMPROMISE`. + +.. cpp:function:: int botan_x509_crl_entry_create(botan_x509_crl_entry_t* entry, botan_x509_cert_t cert, int reason_code) + + Create a new CRL entry to be added to a CRL later. + +.. cpp:function:: int botan_x509_crl_update(botan_x509_crl_t* crl_obj, \ + botan_x509_crl_t last_crl, \ + botan_rng_t rng, \ + botan_x509_cert_t ca_cert, \ + botan_privkey_t ca_key, \ + uint64_t issue_time, \ + uint32_t next_update, \ + const botan_x509_crl_entry_t* new_entries, \ + size_t new_entries_len, \ + const char* hash_fn, \ + const char* padding) + + Revoke some certificates. This does not update the given CRL in place. + ``issue_time`` is expected to be a UNIX timestamp, in seconds. + ``next_update`` is the number of seconds after ``issue_time`` until the CRL expires. + ``hash_fn`` and ``padding`` may be NULL. + ``new_entries`` is an array of ``botan_x509_crl_entry_t`` objects, ``new_entries_len`` is its length. + +.. cpp:function:: int botan_x509_crl_verify_signature(botan_x509_crl_t crl, botan_pubkey_t key) + + Verify the signature of a CRL. Returns 1 if the signature is valid, 0 otherwise. + .. cpp:function:: int botan_x509_crl_destroy(botan_x509_crl_t crl) Destroy the CRL object. +.. cpp:function:: int botan_x509_crl_this_update(botan_x509_crl_t crl, uint64_t* time_since_epoch) + + Return the time the CRL becomes valid, as seconds since epoch. + +.. cpp:function:: int botan_x509_crl_next_update(botan_x509_crl_t crl, uint64_t* time_since_epoch) + + Return the time the CRL expires, as seconds since epoch. Note that this field + is technically optional in CRLs, if the CRL does not specify a "next update" + timestamp, :cpp:enumerator:`BOTAN_FFI_ERROR_NO_VALUE` is returned. + +.. cpp:function:: int botan_x509_crl_view_binary_values(botan_x509_crl_t crl, \ + botan_x509_value_type value_type, \ + size_t index, \ + botan_view_ctx ctx, \ + botan_view_bin_fn view_fn) + + Access various binary fields of information contained in the CRL. + + Some of those may be multi-value fields, the `index` parameter may be used + to enumerate such values until :cpp:enumerator:`BOTAN_FFI_ERROR_OUT_OF_RANGE` + is returned. For singular values, an `index` of 0 must be used. + + See :ref:`x509_getter_function` for further information about the available + values. If a value does not exist :cpp:enumerator:`BOTAN_FFI_ERROR_NO_VALUE` + is returned. + +.. cpp:function:: int botan_x509_crl_view_binary_values_count(botan_x509_crl_t crl, \ + botan_x509_value_type value_type, \ + size_t* count) + + Get the number of entries for multi-value binary fields of information + contained in the CRL. + +.. cpp:function:: int botan_x509_crl_view_string_values(botan_x509_crl_t crl, \ + botan_x509_value_type value_type, \ + size_t index, \ + botan_view_ctx ctx, \ + botan_view_str_fn view_fn) + + Access various string fields of information contained in the CRL. + + Some of those may be multi-value fields, the `index` parameter may be used + to enumerate such values until :cpp:enumerator:`BOTAN_FFI_ERROR_OUT_OF_RANGE` + is returned. For singular values, an `index` of 0 must be used. + + See :ref:`x509_getter_function` for further information about the available + values. If a value does not exist :cpp:enumerator:`BOTAN_FFI_ERROR_NO_VALUE` + is returned. + +.. cpp:function:: int botan_x509_crl_view_string_values_count(botan_x509_crl_t crl, \ + botan_x509_value_type value_type, \ + size_t* count) + + Get the number of entries for multi-value string fields of information + contained in the CRL. + .. cpp:function:: int botan_x509_is_revoked(botan_x509_crl_t crl, botan_x509_cert_t cert) Check whether a given ``crl`` contains a given ``cert``. Return ``0`` when the certificate is revoked, ``-1`` otherwise. +.. cpp:function:: int botan_x509_crl_entries(botan_x509_crl_t crl, \ + size_t index, \ + botan_x509_crl_entry_t *entry) + + List the entries in the CRL. Using the `index` parameter applications can + enumerate all entries in the CRL. If the list of entries is exhausted, this + will return :cpp:enumerator:`BOTAN_FFI_ERROR_OUT_OF_RANGE`. + +.. cpp:function:: int botan_x509_crl_entries_count(botan_x509_crl_t crl, size_t* count) + + Get the number of entries in the CRL. + +.. cpp:function:: int botan_x509_crl_entry_reason(botan_x509_crl_entry_t entry, int* reason_code) + + Get the revocation reason code for the given CRL entry. The reason code is + according to `RFC 5280 - 5.3.1 `, see :cpp:enum:`botan_x509_crl_reason_code`. + +.. cpp:function:: int botan_x509_crl_entry_revocation_date(botan_x509_crl_entry_t entry, uint64_t* time_since_epoch) + + Get the revocation date for the given CRL entry, as seconds since epoch. + +.. cpp:function:: int botan_x509_crl_entry_serial_number(botan_x509_crl_entry_t entry, botan_mp_t* serial_number) + + Get the serial number for the given CRL entry as a multi-precision integer. + +.. cpp:function:: int botan_x509_crl_entry_view_serial_number(botan_x509_crl_entry_t entry, botan_view_ctx ctx, botan_view_bin_fn view) + + View the serial number for the given CRL entry, as big-endian encoded bytes. + +.. cpp:function:: int botan_x509_crl_entry_destroy(botan_x509_crl_entry_t entry) + + Destroy the CRL entry object. + +.. _x509_getter_function: + +X.509 Available Generic Getter Values +---------------------------------------- + +Most X.509 objects may contain various data fields that may be of interest for +using applications. Many of those values can be queried through a generic API +that is extensible without introducing ABI incompatibilities. + +All available value types of the generic X.509 object getters are: + +.. cpp:enumerator:: BOTAN_X509_SERIAL_NUMBER + + The binary big-endian encoded serial number of a certificate or CRL. + +.. cpp:enumerator:: BOTAN_X509_SUBJECT_DN_BITS + + The DER encoded subject distinguished name of the certificate. + +.. cpp:enumerator:: BOTAN_X509_ISSUER_DN_BITS + + The DER encoded issuer distinguished name of a certificate or CRL. + +.. cpp:enumerator:: BOTAN_X509_SUBJECT_KEY_IDENTIFIER + + The subject key identifier (usually a hash of the certificate's public key) + in binary format. + +.. cpp:enumerator:: BOTAN_X509_AUTHORITY_KEY_IDENTIFIER + + The issuer's key identifier (usually a hash of the issuer's public key) in + binary format. + +.. cpp:enumerator:: BOTAN_X509_PUBLIC_KEY_PKCS8_BITS + + The certificate's public key in PKCS#8 format (DER encoding). + +.. cpp:enumerator:: BOTAN_X509_TBS_DATA_BITS + + The "To-Be-Signed" data of a certificate or CRL (DER encoding). + +.. cpp:enumerator:: BOTAN_X509_SIGNATURE_SCHEME_BITS + + The signature scheme descriptor of a certificate or CRL (DER encoding). + +.. cpp:enumerator:: BOTAN_X509_SIGNATURE_BITS + + The raw signature data of a certificate or CRL. The encoding depends on the + signature algorithm but is always in binary format. + +.. cpp:enumerator:: BOTAN_X509_DER_ENCODING + + The binary DER encoding of the entire certificate or CRL object. + +.. cpp:enumerator:: BOTAN_X509_PEM_ENCODING + + The string-based PEM encoding of the entire certificate or CRL object. + +.. cpp:enumerator:: BOTAN_X509_CRL_DISTRIBUTION_URLS + + The CRL distribution points (URLs) noted in the certificate as a character + array. There might be more than one such URL defined in a certificate. + +.. cpp:enumerator:: BOTAN_X509_OCSP_RESPONDER_URLS + + The OCSP responders (URLs) noted in the certificate as a character array. + There might be more than one such URL defined in a certificate. + +.. cpp:enumerator:: BOTAN_X509_CA_ISSUERS_URLS + + The URLs of the issuing CA certificate of a certificate as a character array. + There might be more than one such URL defined in a certificate. + ZFEC (Forward Error Correction) ---------------------------------------- diff -Nru botan3-3.7.1+dfsg/doc/api_ref/filters.rst botan3-3.12.0+dfsg/doc/api_ref/filters.rst --- botan3-3.7.1+dfsg/doc/api_ref/filters.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/api_ref/filters.rst 2026-05-07 01:38:28.000000000 +0000 @@ -688,7 +688,7 @@ .. cpp:function:: std::string Filter::name() const - This should just return a useful decription of the filter object. + This should just return a useful description of the filter object. .. cpp:function:: void Filter::write(const uint8_t* input, size_t length) diff -Nru botan3-3.7.1+dfsg/doc/api_ref/footguns.rst botan3-3.12.0+dfsg/doc/api_ref/footguns.rst --- botan3-3.7.1+dfsg/doc/api_ref/footguns.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/api_ref/footguns.rst 2026-05-07 01:38:28.000000000 +0000 @@ -25,7 +25,7 @@ impossible or inconvenient, one option is to disable the pool, either at build time (disable the ``locking_allocator`` module) or at runtime. Unfortunately the runtime setting requires setting an environment variable (see :ref:`env_vars`), -and doing so consistently *prior to static intialization* is not trivial, due to +and doing so consistently *prior to static initialization* is not trivial, due to the previously mentioned fiasco. One option might be to use GCC's ``constructor`` function attribute. diff -Nru botan3-3.7.1+dfsg/doc/api_ref/fpe.rst botan3-3.12.0+dfsg/doc/api_ref/fpe.rst --- botan3-3.7.1+dfsg/doc/api_ref/fpe.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/api_ref/fpe.rst 2026-05-07 01:38:28.000000000 +0000 @@ -96,3 +96,7 @@ format, including a correct checksum. .. literalinclude:: ../../src/cli/cc_enc.cpp + +This example encrypts a string of dictionary words onto another string of dictionary words: + +.. literalinclude:: ../../src/examples/fpe_dictionary.cpp diff -Nru botan3-3.7.1+dfsg/doc/api_ref/hash.rst botan3-3.12.0+dfsg/doc/api_ref/hash.rst --- botan3-3.7.1+dfsg/doc/api_ref/hash.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/api_ref/hash.rst 2026-05-07 01:38:28.000000000 +0000 @@ -97,6 +97,44 @@ The following cryptographic hash functions are implemented. If in doubt, any of SHA-384, SHA-3, or BLAKE2b are fine choices. +Ascon-Hash256 +^^^^^^^^^^^^^ + +Available if ``BOTAN_HAS_ASCON_HASH256`` is defined. + +A hash function based on the Ascon permutation, specifically designed to allow +small footprint implementations. Its main use case is in constrained +environments, such as IoT devices where traditional cryptographic functions +may be too resource intensive. + +Unless you are interoperating with an existing device which due to resource +constraints can only use Ascon, prefer more typical hashes such as SHA-256, +SHA-512, or SHA-3. + +This hash function is standardized by NIST in SP.800-232. It is not compatible +with earlier versions of the Ascon specification. + +Algorithm specification name: ``Ascon-Hash256`` + +Ascon-XOF128 +^^^^^^^^^^^^ + +Available if ``BOTAN_HAS_ASCON_XOF128`` is defined. + +An eXtensible Output Functions (XOF) based on the Ascon permutation. Just like +the described Ascon-Hash above, its main use case is in constrained +environments, such as IoT devices where traditional cryptographic functions +may be too resource intensive. + +Unless you are interoperating with an existing device which due to resource +constraints can only use Ascon, prefer the more typical XOF SHAKE-128, or +SHAKE-512. + +This XOF is standardized by NIST in SP.800-232. It is not compatible +with earlier versions of the Ascon specification. + +Algorithm specification name: ``Ascon-XOF128`` + BLAKE2b ^^^^^^^^^ diff -Nru botan3-3.7.1+dfsg/doc/api_ref/message_auth_codes.rst botan3-3.12.0+dfsg/doc/api_ref/message_auth_codes.rst --- botan3-3.7.1+dfsg/doc/api_ref/message_auth_codes.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/api_ref/message_auth_codes.rst 2026-05-07 01:38:28.000000000 +0000 @@ -208,9 +208,14 @@ SipHash ~~~~~~~~~~~~ -A modern and very fast PRF. Produces only a 64-bit output. Defaults to -"SipHash(2,4)" which is the recommended configuration, using 2 rounds for each -input block and 4 rounds for finalization. +.. deprecated:: 3.8.0 + +SipHash is primarily designed for hash table randomization and, while not +known to be insecure for message authentication, is not advisable for this +use due to the small output size (just 64 bits). + +Defaults to "SipHash(2,4)" which is the recommended configuration, using 2 +rounds for each input block and 4 rounds for finalization. Available if ``BOTAN_HAS_SIPHASH`` is defined. @@ -224,6 +229,8 @@ X9.19-MAC ~~~~~~~~~~~~ +.. deprecated:: 3.7.0 + A CBC-MAC variant sometimes used in finance. Always uses DES. Sometimes called the "DES retail MAC", also standardized in ISO 9797-1. diff -Nru botan3-3.7.1+dfsg/doc/api_ref/otp.rst botan3-3.12.0+dfsg/doc/api_ref/otp.rst --- botan3-3.7.1+dfsg/doc/api_ref/otp.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/api_ref/otp.rst 2026-05-07 01:38:28.000000000 +0000 @@ -12,14 +12,14 @@ Botan implements the HOTP and TOTP schemes from RFC 4226 and 6238. -Since the range of possible OTPs is quite small, applications must rate limit +Since the range of possible OTP values is quite small, applications must rate limit OTP authentication attempts to some small number per second. Otherwise an attacker -could quickly try all 1000000 6-digit OTPs in a brief amount of time. +could quickly try all 1000000 6-digit values in a brief amount of time. HOTP ^^^^^^ -HOTP generates OTPs that are a short numeric sequence, between 6 and 8 digits +HOTP generates an OTP that is a short numeric sequence, between 6 and 8 digits (most applications use 6 digits), created using the HMAC of a 64-bit counter value. If the counter ever repeats the OTP will also repeat, thus both parties must assure the counter only increments and is never repeated or diff -Nru botan3-3.7.1+dfsg/doc/api_ref/pbkdf.rst botan3-3.12.0+dfsg/doc/api_ref/pbkdf.rst --- botan3-3.7.1+dfsg/doc/api_ref/pbkdf.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/api_ref/pbkdf.rst 2026-05-07 01:38:28.000000000 +0000 @@ -101,24 +101,32 @@ Create a default instance of the password hashing algorithm. Be warned the value returned here may change from release to release. - .. cpp:function:: std::unique_ptr tune( \ + .. cpp:function:: std::unique_ptr tune_params( \ size_t output_len, \ - std::chrono::milliseconds msec, \ - size_t max_memory_usage_mb = 0, \ - std::chrono::milliseconds tuning_msec = std::chrono::milliseconds(10)) const + uint64_t desired_msec, \ + std::optional max_memory_usage_mb = {}, \ + uint64_t tuning_msec = 10) const - Return a password hash instance tuned to run for approximately ``msec`` + Return a password hash instance tuned to run for approximately ``desired_msec`` milliseconds when producing an output of length ``output_len``. (Accuracy may vary, use the command line utility ``botan pbkdf_tune`` to check.) The parameters will be selected to use at most *max_memory_usage_mb* megabytes - of memory, or if left as zero any size is allowed. + of memory, or if left as nullopt any size is allowed. - This function works by runing a short tuning loop to estimate the + This function works by running a short tuning loop to estimate the performance of the algorithm, then scaling the parameters appropriately to hit the target size. The length of time the tuning loop runs can be controlled using the *tuning_msec* parameter. + .. cpp:function:: std::unique_ptr tune( \ + size_t output_len, \ + std::chrono::milliseconds msec, \ + size_t max_memory_usage_mb = 0, \ + std::chrono::milliseconds tuning_msec = std::chrono::milliseconds(10)) const + + A deprecated variant of tune_params. It will be removed in Botan4. + .. cpp:function:: std::unique_ptr from_params( \ size_t i1, size_t i2 = 0, size_t i3 = 0) const diff -Nru botan3-3.7.1+dfsg/doc/api_ref/pkcs11.rst botan3-3.12.0+dfsg/doc/api_ref/pkcs11.rst --- botan3-3.7.1+dfsg/doc/api_ref/pkcs11.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/api_ref/pkcs11.rst 2026-05-07 01:38:28.000000000 +0000 @@ -16,7 +16,8 @@ .. note:: - The Botan PKCS#11 interface is implemented against version v2.40 of the standard. + The Botan PKCS#11 interface is implemented against version v3.2 of the standard. + Versions 2.40 upto 3.2 are supported, but only the 3.2 headers are shipped with Botan. Botan wraps the C PKCS#11 API to provide a C++ PKCS#11 interface. This is done in two levels of abstraction: a low level API (see :ref:`pkcs11_low_level`) and @@ -39,7 +40,9 @@ The PKCS#11 standards committee provides header files (``pkcs11.h``, ``pkcs11f.h`` and ``pkcs11t.h``) which define the PKCS#11 API in the C programming language. These header files could be used directly to access PKCS#11 compatible smart cards or -HSMs. The external header files are shipped with Botan in version v2.4 of the standard. The PKCS#11 low +HSMs. A public domain variant of these header files is shipped with Botan in +version v3.2 (Draft wd13) of the standard. This variant is interchangeable with the original +v3.2 header files of OASIS. The PKCS#11 low level API wraps the original PKCS#11 API, but still allows to access all functions described in the standard and has the advantage that it is a C++ interface with features like RAII, exceptions and automatic memory management. @@ -777,7 +780,7 @@ Unlike the CardOS (4.4, 5.0, 5.3), the aforementioned SO-PIN/PUK is inappropriate for Gemalto (IDPrime MD 3840) cards, as it must be a byte array of length 24. For this reason some of the tests for Gemalto card involving - SO-PIN will fail. You run into a risk of exceding login attempts and as a + SO-PIN will fail. You run into a risk of exceeding login attempts and as a result locking your card! Currently, specifying pin via command-line option is not implemented, and therefore the desired PIN must be modified in the header src/tests/test_pkcs11.h: @@ -798,7 +801,7 @@ Test results +-------------------------------------+-------------------------------------------+---------------------------------------------------+---------------------------------------------------+---------------------------------------------------+---------------------------------------------------+ -| Smartcard | Status | OS | Midleware | Botan | Errors | +| Smartcard | Status | OS | Middleware | Botan | Errors | +=====================================+===========================================+===================================================+===================================================+===================================================+===================================================+ | CardOS 4.4 | mostly works | Windows 10, 64-bit, version 1709 | API Version 5.4.9.77 (Cryptoki v2.11) | 2.4.0, Cryptoki v2.40 | [50]_ | +-------------------------------------+-------------------------------------------+---------------------------------------------------+---------------------------------------------------+---------------------------------------------------+---------------------------------------------------+ @@ -887,7 +890,7 @@ - rng_add_entropy [5]_ -.. [53] Failing operations for CardOS 5.3 (middelware 5.5.1) +.. [53] Failing operations for CardOS 5.3 (middleware 5.5.1) - ecdh_privkey_export [2]_ - ecdh_generate_private_key [35]_ diff -Nru botan3-3.7.1+dfsg/doc/api_ref/pubkey.rst botan3-3.12.0+dfsg/doc/api_ref/pubkey.rst --- botan3-3.7.1+dfsg/doc/api_ref/pubkey.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/api_ref/pubkey.rst 2026-05-07 01:38:28.000000000 +0000 @@ -95,7 +95,7 @@ Return an object containing the public key corresponding to this private key. Prefer this over the (deprecated) implicit conversion of a private key to - a public key currently possible due to an inheritence relation. + a public key currently possible due to an inheritance relation. .. cpp:function:: secure_vector private_key_info() const @@ -107,7 +107,7 @@ .. cpp:function:: secure_vector private_key_bits() const - Return the serialization of the private key, cooresponding to the + Return the serialization of the private key, corresponding to the `PrivateKey` field of a PKCS #8 `PrivateKeyInfo` structure. See :rfc:`5208` for details. @@ -172,9 +172,12 @@ ~~~~~~~~~~~~~~~~~ Post-quantum key encapsulation scheme based on (structured) lattices. This -algorithm is standardized in FIPS 203. Decapsulation keys are always stored and -expanded from the 64-byte private random seeds (``d || z``), loading the -expanded key format specified in FIPS 203 is explicitly not supported. +algorithm is standardized in FIPS 203. New decapsulation keys are stored and +expanded from the 64-byte private random seeds (``d || z``). +Keys imported as seeds are always serialized as seeds, while keys imported in +expanded format (as specified in FIPS 203) are serialized in expanded format. +Exporting seeds as expanded keys is supported using ML-KEM private key-specific +methods. Support for ML-KEM is implemented in the module ``ml_kem``. @@ -206,6 +209,14 @@ signatures, then the whole scheme becomes insecure, and signatures can be forged. + .. warning:: + + Maintaining consistent state without replays is extremely difficult, + especially when multiple machines are involved. Even a single error will + compromise the entire signature scheme. XMSS should only be used in an + environment carefully designed to maintain consistent state. Prefer + the stateless SLH-DSA in new designs. + HSS-LMS ~~~~~~~ @@ -214,6 +225,14 @@ each signature. If the same state is ever used to generate two signatures, then the whole scheme becomes insecure, and signatures can be forged. + .. warning:: + + Maintaining consistent state without replays is extremely difficult, + especially when multiple machines are involved. Even a single error will + compromise the entire signature scheme. HSS-LMS should only be used in an + environment carefully designed to maintain consistent state. Prefer + the stateless SLH-DSA in new designs. + SLH-DSA (FIPS 205) ~~~~~~~~~~~~~~~~~~ @@ -349,7 +368,7 @@ Generate a new X448 private key -Others require additionally specfiying which curve to use. First create a +Others require additionally specifying which curve to use. First create a relevant :cpp:class:`EC_Group` using for example :cpp:func:`EC_Group::from_name` or :cpp:func:`EC_Group::from_OID`. Then pass it to the private key constructor. If the choice of group is not otherwise mandated by your @@ -890,7 +909,7 @@ .. cpp:function:: PK_Signer(const Private_Key& key, \ const std::string& padding, \ - Signature_Format format = Siganture_Format::Standard) + Signature_Format format = Signature_Format::Standard) Constructs a new signer object for the private key *key* using the hash/padding specified in *padding*. The key must support signature operations. In @@ -1003,7 +1022,7 @@ Botan implements the following signature algorithms: -1. RSA. Requires a :ref:`padding scheme ` as parameter. +1. RSA. Requires a :ref:`padding scheme ` as parameter. #. DSA. Requires a :ref:`hash function ` as parameter. #. ECDSA. Requires a :ref:`hash function ` as parameter. #. ECGDSA. Requires a :ref:`hash function ` as parameter. @@ -1038,7 +1057,7 @@ .. literalinclude:: /../src/examples/ecdsa.cpp :language: cpp -.. _emsa: +.. _rsa_padding: RSA signature padding schemes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1077,10 +1096,10 @@ ``PKCS1v15(Raw)``, ``PKCS1v15(Raw,MD5)``, -EMSA-PSS -"""""""" +Probabilistic signature scheme (PSS) +""""""""""""""""""""""""""""""""""""""" -Probabilistic signature scheme (PSS) (called EMSA4 in IEEE 1363). +Called EMSA4 in IEEE 1363. - Name: ``PSS`` - Deprecated aliases: ``EMSA-PSS``, ``PSSR``, ``PSS-MGF1``, ``EMSA4`` @@ -1140,28 +1159,28 @@ X9.31 """"" +Padding scheme from ANSI X9.31. Called EMSA2 in IEEE 1363. + .. deprecated:: 3.7.0 X9.31 signatures are obsolete, and support for it is deprecated -EMSA from X9.31 (EMSA2 in IEEE 1363). - - Name: ``X9.31`` - Deprecated aliases: ``EMSA2``, ``EMSA_X931`` - Parameters specification: ``()`` - Example: ``X9.31(SHA-256)`` -Raw EMSA +Raw """""""" Sign inputs directly with no hashing or padding .. warning:: - This exists as an escape hatch allowing an application to define - some protocol-specific padding scheme. Don't use this unless you - know what you are doing. + This exists as an escape hatch allowing an application to define some + protocol-specific padding scheme, and using it in a naive way is completely + insecure. Don't use this unless you know what you are doing. - Name: ``Raw`` - Parameters specification: @@ -1178,10 +1197,12 @@ For many signature schemes including ECDSA and DSA, simply naming a hash function like ``SHA-256`` is all that is required. -Previous versions of Botan required using a hash specifier -like ``EMSA1(SHA-256)`` when generating or verifying ECDSA/DSA signatures, -with the specified hash. -The ``EMSA1`` was a reference to a now obsolete IEEE standard. +.. note:: + + Previous versions of Botan required using a hash specifier like + ``EMSA1(SHA-256)`` when generating or verifying ECDSA/DSA signatures, with + the specified hash. The ``EMSA1`` was a reference to a now obsolete IEEE + standard. Parameters specification: @@ -1309,7 +1330,7 @@ The *peer_key* parameter must be the public key associated with the other party. - The shared key will be of length *key_len*. If the KDF cannot accomodate + The shared key will be of length *key_len*. If the KDF cannot accommodate outputs of this size (only likely for very large values, or if using KDF1), an exception will be thrown. If a KDF is not in use ("Raw" KDF), *key_len* is ignored and this function will always return directly what the agreement @@ -1566,6 +1587,15 @@ #. XMSS-SHAKE_10_512 #. XMSS-SHAKE_16_512 #. XMSS-SHAKE_20_512 +#. XMSS-SHA2_10_192 +#. XMSS-SHA2_16_192 +#. XMSS-SHA2_20_192 +#. XMSS-SHAKE256_10_256 +#. XMSS-SHAKE256_16_256 +#. XMSS-SHAKE256_20_256 +#. XMSS-SHAKE256_10_192 +#. XMSS-SHAKE256_16_192 +#. XMSS-SHAKE256_20_192 The algorithm name contains the hash function name, tree height and digest width defined by the corresponding parameter set. Choosing `XMSS-SHA2_10_256` diff -Nru botan3-3.7.1+dfsg/doc/api_ref/python.rst botan3-3.12.0+dfsg/doc/api_ref/python.rst --- botan3-3.7.1+dfsg/doc/api_ref/python.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/api_ref/python.rst 2026-05-07 01:38:28.000000000 +0000 @@ -46,6 +46,14 @@ no matter how many 'system' rng instances are created. Thus it is easy to use the RNG in a one-off way, with `botan.RandomNumberGenerator().get(32)`. + For some use cases it can be useful to provide a custom RNG implementation. + Use 'custom' as the rng_type and provide the ``get_callback=`` and + ``add_entropy_callback=`` arguments. The latter is optional. + ``get_callback`` takes an integer and is expected to return a bytes object + with the requested number of random bytes. + ``add_entropy_callback`` takes a bytes object containing entropy bytes and + is expected to add the given entropy to the RNG. + When Botan is configured with TPM 2.0 support, also 'tpm2' is allowed to instantiate a TPM-backed RNG. Note that this requires passing additional named arguments ``tpm2_context=`` with a ``TPM2Context`` and @@ -138,7 +146,7 @@ Previously ``cipher`` - The algorithm is spcified as a string (eg 'AES-128/GCM', + The algorithm is specified as a string (eg 'AES-128/GCM', 'Serpent/OCB(12)', 'Threefish-512/EAX'). Set the second param to False for decryption @@ -344,6 +352,10 @@ vary depending on the algorithm. For example RSA public modulus can be extracted with ``rsa_key.get_field("n")``. + .. py:method:: object_identifier() + + Returns the associated OID + .. py:method:: fingerprint(hash = 'SHA-256') Returns a hash of the public key @@ -373,6 +385,10 @@ "curve25519" and "x448" (which are actually completely distinct key types with a non-standard encoding). + .. py:classmethod:: create_ec(algo, ec_group, rng) + + Creates a new ec private key. + .. py:classmethod:: load(val, passphrase="") Return a private key (DER or PEM formats accepted) @@ -462,6 +478,17 @@ extracted with ``rsa_key.get_field("p")``. This function can also be used to extract the public parameters. + .. py:method:: object_identifier() + + Returns the associated OID + + .. py:method:: stateful_operation() + Return whether the key is stateful or not. + + .. py:method:: remaining_operations() + If the key is stateful, return the number of remaining operations. + Raises an exception if the key is not stateful. + Public Key Operations ---------------------------------------- @@ -563,6 +590,100 @@ Return the greatest common divisor of ``self`` and ``other`` +Object Identifiers (OID) +------------------------------------- +.. versionadded:: 3.8.0 + +.. py:class:: OID(object) + + .. py:classmethod:: from_string(value) + + Create a new OID from dot notation or from a known name + + .. py:method:: to_string() + + Export the OID in dot notation + + .. py:method:: to_name() + + Export the OID as a name if it has one, else in dot notation + + .. py:method:: register(name) + + Register the OID so that it may later be retrieved by the given name + + +EC Groups +------------------------------------- +.. versionadded:: 3.8.0 + +.. py:class:: ECGroup(object) + + .. py:classmethod:: supports_application_specific_group() + + Returns true if in this build configuration it is possible to register an application specific elliptic curve + + .. py:classmethod:: supports_named_group(name) + + Returns true if in this build configuration ECGroup.from_name(name) will succeed + + .. py:classmethod:: from_params(oid, p, a, b, base_x, base_y, order) + + Creates a new ECGroup from ec parameters + + .. py:classmethod:: from_ber(ber) + + Creates a new ECGroup from a BER blob + + .. py:classmethod:: from_pem(pem) + + Creates a new ECGroup from a pem encoding + + .. py:classmethod:: from_oid(oid) + + Creates a new ECGroup from a group named by an OID + + .. py:classmethod:: from_name(name) + + Creates a new ECGroup from a common group name + + .. py:method:: to_der() + + Export the group in DER encoding + + .. py:method:: to_pem() + + Export the group in PEM encoding + + .. py:method:: get_curve_oid() + + Get the curve OID + + .. py:method:: get_p() + + Get the prime modulus of the field + + .. py:method:: get_a() + + Get the a parameter of the elliptic curve equation + + .. py:method:: get_b() + + Get the b parameter of the elliptic curve equation + + .. py:method:: get_g_x() + + Get the x coordinate of the base point + + .. py:method:: get_g_y() + + Get the y coordinate of the base point + + .. py:method:: get_order() + + Get the order of the base point + + Format Preserving Encryption (FE1 scheme) ----------------------------------------- .. versionadded:: 2.8.0 @@ -688,7 +809,7 @@ crls=None) Verify a certificate. Returns 0 if validation was successful, returns a positive error code - if the validation was unsuccesful. + if the validation was unsuccessful. ``intermediates`` is a list of untrusted subauthorities. diff -Nru botan3-3.7.1+dfsg/doc/api_ref/tls.rst botan3-3.12.0+dfsg/doc/api_ref/tls.rst --- botan3-3.7.1+dfsg/doc/api_ref/tls.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/api_ref/tls.rst 2026-05-07 01:38:28.000000000 +0000 @@ -547,7 +547,7 @@ with full flexibility to handle session objects. More detail can be found in the API documentation inline. -.. cpp:class:: TLS::Session_Mananger +.. cpp:class:: TLS::Session_Manager .. cpp:function:: void store(const Session& session, const Session_Handle& handle) @@ -607,7 +607,7 @@ Limits the maximum number of saved sessions to *max_sessions*. -Noop Session Mananger +Noop Session Manager ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The ``TLS::Session_Manager_Noop`` implementation does not save @@ -695,19 +695,19 @@ No export key exchange mechanisms or ciphersuites are supported by botan. The null encryption ciphersuites (which provide only - authentication, sending data in cleartext) are also not supported - by the implementation and cannot be negotiated. + authentication, sending data in cleartext) are only supported if + they are explicitly enabled at build time by activating the + tls_null module. Cipher names without an explicit mode refers to CBC+HMAC ciphersuites. - Default value: "ChaCha20Poly1305", "AES-256/GCM", "AES-128/GCM" + Default value: "AES-256/GCM", "AES-128/GCM", "ChaCha20Poly1305" Also allowed: "AES-256", "AES-128", "AES-256/CCM", "AES-128/CCM", "AES-256/CCM(8)", "AES-128/CCM(8)", "Camellia-256/GCM", "Camellia-128/GCM", "ARIA-256/GCM", "ARIA-128/GCM" - Also allowed (though currently experimental): "AES-128/OCB(12)", - "AES-256/OCB(12)" + Also allowed (though currently experimental): "AES-256/OCB(12)" In versions up to 2.8.0, the CBC and CCM ciphersuites "AES-256", "AES-128", "AES-256/CCM" and "AES-128/CCM" were enabled by default. @@ -793,17 +793,22 @@ .. cpp:function:: std::vector key_exchange_groups() const Return a list of ECC curve and DH group TLS identifiers we are willing to use, in order of preference. - The default ordering puts the best performing ECC first. Default: - Group_Params::X25519, - Group_Params::SECP256R1, Group_Params::BRAINPOOL256R1, - Group_Params::SECP384R1, Group_Params::BRAINPOOL384R1, - Group_Params::SECP521R1, Group_Params::BRAINPOOL512R1, - Group_Params::FFDHE_2048, Group_Params::FFDHE_3072, Group_Params::FFDHE_4096, - Group_Params::FFDHE_6144, Group_Params::FFDHE_8192 - No other values are currently defined. + Group_Params::X25519, + Group_Params::SECP256R1, + Group_Params_Code::HYBRID_X25519_ML_KEM_768, + Group_Params_Code::HYBRID_SECP256R1_ML_KEM_768, + Group_Params_Code::HYBRID_SECP384R1_ML_KEM_1024, + Group_Params::X448, + Group_Params::SECP384R1, + Group_Params::SECP521R1, + Group_Params::BRAINPOOL256R1, + Group_Params::BRAINPOOL384R1, + Group_Params::BRAINPOOL512R1, + Group_Params::FFDHE_2048, + Group_Params::FFDHE_3072, .. cpp:function:: std::vector key_exchange_groups_to_offer() const @@ -1134,6 +1139,7 @@ to be in the future standardized by IETF * ``HYBRID_SECP256R1_ML_KEM_768`` ("secp256r1/ML-KEM-768") + * ``HYBRID_SECP384R1_ML_KEM_1024`` ("secp384r1/ML-KEM-1024") * ``HYBRID_X25519_ML_KEM_768`` ("x25519/ML-KEM-768") * Pure ML-KEM as documented in IETF draft ``draft-connolly-tls-mlkem-key-agreement`` @@ -1180,6 +1186,21 @@ .. literalinclude:: /../src/examples/tls_custom_curves_client.cpp :language: cpp +Special Case: Custom ECDH provider for TLS 1.2 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Users that wish to implement a custom ECDH provider for TLS 1.2 (e.g. to offload the implementation of +standard curves to some crypto hardware), must take the negotiated ECC point encoding (compressed vs. +uncompressed) into account. They should use the special callback ``tls12_generate_ephemeral_ecdh_key`` +which provides the desired ECC point encoding as an input parameter. + +This special callback is called *only for TLS 1.2* and *only for ECDH using standardized curves* that +Botan is aware of (for instance ``secp256r1``, ``brainpool256r1`` and such). Explicitly, that *does not +include X25519 and X448* as those algorithms have a well-defined point format. TLS 1.3 does not allow +negotiating the ECC point encoding (see `RFC 8446 Section 4.2.8.2 `_) +and thus does not call this callback either. Support for compressed points in TLS 1.2 is deprecated in +Botan and this callback will disappear when it is removed in a future release. + .. _tls_asio_stream: TLS Stream @@ -1335,9 +1356,9 @@ Aside of the modern coroutines-based approach, the ASIO stream may also be used in a more traditional way, using callback handler methods instead of coroutines. -Also, this example shows how to use a custom :cpp:class:`Credentials_Manager` -and pass it to the :cpp:class:`TLS::Stream` via a :cpp:class:`TLS::Context` -object. +Also, this example shows how to use custom :cpp:class:`Credentials_Manager` and +:cpp:class:`TLS::Policy` subclasses, passing them to the :cpp:class:`TLS::Stream` +via a :cpp:class:`TLS::Context` object. .. literalinclude:: /../src/examples/tls_stream_client.cpp :language: cpp @@ -1368,7 +1389,7 @@ random seed, and HMAC'ing it to produce a 256-bit value. This means for any one master key as many as 2\ :sup:`128` GCM keys can be created. This is done because NIST recommends that when using random nonces no one GCM key be used to -encrypt more than 2\ :sup:`32` messages (to avoid the possiblity of nonce +encrypt more than 2\ :sup:`32` messages (to avoid the possibility of nonce reuse). A random 96-bit nonce is created and included in the header. diff -Nru botan3-3.7.1+dfsg/doc/api_ref/tpm.rst botan3-3.12.0+dfsg/doc/api_ref/tpm.rst --- botan3-3.7.1+dfsg/doc/api_ref/tpm.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/api_ref/tpm.rst 2026-05-07 01:38:28.000000000 +0000 @@ -35,7 +35,7 @@ ~~~~~~~~~~~~~~~ The TPM context is the main entry point for all TPM operations. Also, it -provides authorative information about the TPM's capabilities and allows +provides authoritative information about the TPM's capabilities and allows persisting and evicting keys into the TPM's NVRAM. .. cpp:class:: Botan::TPM2::Context diff -Nru botan3-3.7.1+dfsg/doc/api_ref/versions.rst botan3-3.12.0+dfsg/doc/api_ref/versions.rst --- botan3-3.7.1+dfsg/doc/api_ref/versions.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/api_ref/versions.rst 2026-05-07 01:38:28.000000000 +0000 @@ -13,7 +13,7 @@ The library has functions for checking compile-time and runtime versions. -The build-time version information is defined in `botan/build.h` +The build-time version information is defined in ``botan/build.h`` .. c:macro:: BOTAN_VERSION_MAJOR @@ -30,8 +30,13 @@ .. c:macro:: BOTAN_VERSION_DATESTAMP Expands to an integer of the form YYYYMMDD if this is an official - release, or 0 otherwise. For instance, 1.10.1, which was released - on July 11, 2011, has a `BOTAN_VERSION_DATESTAMP` of 20110711. + release, or 0 otherwise. For instance, 3.6.1, which was released + on October 26, 2024, has a ``BOTAN_VERSION_DATESTAMP`` of 20241026. + + .. warning:: + + This macro is deprecated and will be removed in Botan4. Use + :cpp:func:`version_datestamp` .. c:macro:: BOTAN_DISTRIBUTION_INFO @@ -42,17 +47,26 @@ to specify any distribution-specific patches. If no value is given at build time, the value is the string "unspecified". + .. warning:: + + This macro is deprecated and will be removed in Botan4. Use + :cpp:func:`version_distribution_info` + .. c:macro:: BOTAN_VERSION_VC_REVISION .. versionadded:: 1.10.1 A macro expanding to a string that is set to a revision identifier corresponding to the source, or "unknown" if this could not be - determined. It is set for all official releases, and for builds that - originated from within a git checkout. + determined. It is set for all official releases. + + .. warning:: + + This macro is deprecated and will be removed in Botan4. Use + :cpp:func:`version_vc_revision` The runtime version information, and some helpers for compile time -version checks, are included in `botan/version.h` +version checks, are included in ``botan/version.h`` .. cpp:function:: std::string version_string() @@ -76,26 +90,31 @@ Return the datestamp of the release (or 0 if the current version is not an official release). -.. cpp:function:: std::string runtime_version_check(uint32_t major, uint32_t minor, uint32_t patch) +.. cpp:function:: std::optional version_vc_revision() + + .. versionadded:: 3.8 + + Returns a string that is set to a revision identifier corresponding to the + source, or ``nullopt`` if this could not be determined. It is set for all + official releases, and for builds that originated from within a git checkout. - Call this function with the compile-time version being built against, eg:: +.. cpp:function:: std::optional version_distribution_info() - Botan::runtime_version_check(BOTAN_VERSION_MAJOR, BOTAN_VERSION_MINOR, BOTAN_VERSION_PATCH) + .. versionadded:: 3.8 - It will return an empty string if the versions match, or otherwise - an error message indicating the discrepancy. This only is useful in - dynamic libraries, where it is possible to compile and run against - different versions. + Return any string that is set at build time using the ``--distribution-info`` + option. It allows a packager of the library to specify any distribution-specific + patches. If no value is given at build time, returns ``nullopt``. .. c:macro:: BOTAN_VERSION_CODE_FOR(maj,min,patch) Return a value that can be used to compare versions. The current (compile-time) version is available as the macro - `BOTAN_VERSION_CODE`. For instance, to choose one code path for - version 2.1.0 and later, and another code path for older releases:: + ``BOTAN_VERSION_CODE``. For instance, to choose one code path for + version 3.4.0 and later, and another code path for older releases:: - #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(2,1,0) - // 2.1+ code path + #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(3,4,0) + // 3.4+ code path #else // code path for older versions #endif diff -Nru botan3-3.7.1+dfsg/doc/api_ref/x509.rst botan3-3.12.0+dfsg/doc/api_ref/x509.rst --- botan3-3.7.1+dfsg/doc/api_ref/x509.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/api_ref/x509.rst 2026-05-07 01:38:28.000000000 +0000 @@ -166,6 +166,11 @@ Returns true if ``get_attribute`` or ``get_first_attribute`` will return a value. + .. cpp:function:: const std::vector>& dn_info() const + + Return the DN components as a vector of OID and ASN1_String pairs. Note that + the order of the components is preserved only when using the initializer list constructor. + .. cpp:function:: std::vector get_attribute(const std::string& attr) const Return all attributes associated with a certain attribute type. @@ -193,7 +198,10 @@ Add an attribute to a DN using an OID instead of string-valued attribute type. The ``X509_DN`` type also supports iostream extraction and insertion operators, -for formatted input and output. +for formatted input and output. Note that the class has deprecated constructors +taking a ``std::multimap``; use the initializer list constructor instead. When +using the deprecated constructors, the order of the DN components is not preserved, +which can violate RFC 5280 requirements. X.509v3 Extensions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -376,6 +384,10 @@ Adds given certificate to the store + .. cpp:function:: Certificate_Store_In_Memory(const X509_Certificate& cert, const X509_CRL& crl) + + Adds given certificate and CRL to the store + .. cpp:function:: Certificate_Store_In_Memory() Create an empty store diff -Nru botan3-3.7.1+dfsg/doc/api_ref/zfec.rst botan3-3.12.0+dfsg/doc/api_ref/zfec.rst --- botan3-3.7.1+dfsg/doc/api_ref/zfec.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/api_ref/zfec.rst 2026-05-07 01:38:28.000000000 +0000 @@ -28,7 +28,7 @@ size. An example application that adds padding and a hash checksum is available -in ``src/cli/zfec.cpp`` and invokable using ``botan fec_encode`` and +in ``src/cli/zfec.cpp`` and invocable using ``botan fec_encode`` and ``botan fec_decode``. .. cpp:class:: ZFEC diff -Nru botan3-3.7.1+dfsg/doc/authors.txt botan3-3.12.0+dfsg/doc/authors.txt --- botan3-3.7.1+dfsg/doc/authors.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/authors.txt 2026-05-07 01:38:28.000000000 +0000 @@ -5,6 +5,7 @@ Allan L. Bazinet Alon Bar-Lev Amos Treiber (Rohde & Schwarz Cybersecurity) +André Schomburg (Volkswagen AG) Andrew Moon Antonio Coratelli Atanas Filyanov @@ -22,6 +23,7 @@ Daniel Neus (Rohde & Schwarz Cybersecurity) Daniel Seither (Kullo GmbH) Daniel Wyatt +Dirk Dobkowitz (Volkswagen AG) Elektrobit Automotive GmbH Eric Cornelius Erwan Chaussy @@ -35,6 +37,7 @@ Florent Le Coz Francis Dupont Frank Schoenmann +Frederik Dornemann (CARIAD SE) Google Inc Gustavo Serra Scalet guywithcrookedface @@ -51,10 +54,12 @@ Jose Luis Pereira (Fyde Inc.) Juraj Somorovsky (Hackmanit GmbH) Justin Karneges +Kagan Can Sit Kai Michaelis (Rohde & Schwarz Cybersecurity) Kirill A. Korinsky Konstantinos Kolelis Krzysztof Kwiatkowski +Lars Dürkop (CARIAD SE) Lauri Nurmi Luca Piccarreta Manuel Glaser (Rohde & Schwarz Cybersecurity) @@ -86,6 +91,7 @@ Robert Dailey Ryuhei Mori schregger +Sebastian Ahrens (Volkswagen AG) Sergii Cherkavskyi seu Shlomi Fish @@ -106,3 +112,4 @@ Yves Jerschow Zoltan Gyarmati 0xdefaced +polarnis diff -Nru botan3-3.7.1+dfsg/doc/building.rst botan3-3.12.0+dfsg/doc/building.rst --- botan3-3.7.1+dfsg/doc/building.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/building.rst 2026-05-07 01:38:28.000000000 +0000 @@ -111,7 +111,7 @@ Common Build Targets -------------------- -Build everthing that is configured:: +Build everything that is configured:: $ make all @@ -197,19 +197,48 @@ On macOS -------------- -A build on macOS works much like that on any other Unix-like system. +A standard build on macOS works much like that on any other Unix-like system. -To build a universal binary for macOS, for older macOs releases, -you need to set some additional build flags. -Do this with the `configure.py` flag `--cc-abi-flags`:: +One notable difference with macOS is the common usage of "universal binaries", +which is effectively a multiarch binary. This was used first for the PowerPC to +x86 transition, and more recently for the x86 to Aarch64 transition. + +Building a universal binary is a bit trickier for Botan compared with a standard +application, as the library makes use of many architecture specific extensions, +for example AES-NI and AVX2 on x86, and NEON and the ARMv8 crypto extensions on +Aarch64. Botan's build system also assumes that it is knowable at setup time +which files are to be compiled. + +Typically (for software with no architecture dependent code) a universal binary +is built by adding additional compilation flags that look something like +``-force_cpusubtype_ALL -arch x86_64 -arch arm64``. This effectively causes XCode +to compile each file twice, once for x86_64 and again for Aarch64. For most source +files this works fine, but for architecture-specific files it will result in errors +when code specific to one architecture is encountered when compiling for a different +architecture, resulting in errors like:: - --cc-abi-flags="-force_cpusubtype_ALL -mmacosx-version-min=10.4 -arch i386 -arch ppc" - - -for mac M1 on arm64, you can build the x86_64 arch version via Rosetta separately. -Do this with with `arch -x86_64 configure.py --library-suffix=-x86_64` -Then using lipo to create a fat binary. -`lipo -create libbotan-arm64.dylib libbotan-x86_64.dylib -o libbotan.dylib` + $ make + ... + error: unknown target CPU 'armv8.2-a+sha3' + note: valid target CPU values are: ... + +There are currently two ways of proceeding. + +The first is to use ``--cpu=generic``. This disables all architecture specific +code, which has performance implications, especially for algorithms with +dedicated hardware support like AES. This can be alleviated somewhat by making +sure the CommonCrypto provider (module ``commoncrypto``) is built, since then +Botan offloads many of these specific operations to CommonCrypto, which will be +able to use the CPU instructions. + +The second, and recommended, approach is to build twice and use ``lipo`` to +combine the two binaries. This looks something like:: + +$ ./configure.py --with-build-dir=botan_x86_64 --disable-cc-tests --build-targets=shared --cpu=x86_64 --extra-cxxflags='-arch x86_64' --ldflags='-arch x86_64' --library-suffix=-x86_64 +$ make -j8 -f botan_x86_64/Makefile +$ ./configure.py --with-build-dir=botan_aarch64 --disable-cc-tests --build-targets=shared --cpu=aarch64 --extra-cxxflags='-arch arm64' --ldflags='-arch arm64' --library-suffix=-aarch64 +$ make -j8 -f botan_aarch64/Makefile +$ lipo -create botan_aarch64/libbotan-3-aarch64.dylib botan_x86_64/libbotan-3-x86_64.dylib -o libbotan-3.dylib On Windows -------------- @@ -227,7 +256,7 @@ $ nmake check $ nmake install -Micosoft's ``nmake`` does not support building multiple jobs in parallel, which +Microsoft's ``nmake`` does not support building multiple jobs in parallel, which is unfortunate when building on modern multicore machines. It is possible to use the (somewhat unmaintained) `Jom `_ build tool, which is a ``nmake`` compatible build system that supports parallel builds. Alternately, @@ -318,19 +347,8 @@ $ ./configure.py --os=android --cc=clang --cpu=arm64 $ make -If you are building for mobile development consider restricting the build -to only what you need (see :ref:`minimized_builds`) - -Docker -^^^^^^^^^^^ - -To build android version, there is the possibility to use -the docker way:: - - sudo ANDROID_SDK_VER=29 ANDROID_ARCH=aarch64 src/scripts/docker-android.sh - -This will produce the docker-builds/android folder containing -each architecture compiled. +If you are building for mobile development, consider restricting the build +to only what you need (see :ref:`minimized_builds`) to minimize code size. Emscripten (WebAssembly) --------------------------- @@ -344,6 +362,10 @@ along with a static archive ``libbotan-3.a`` which can be linked with other modules. +To use the Wasm SIMD128 extension for improved performance of certain +algorithms (see ``hardware_acceleration.rst``), ensure that you pass the +``-msimd128`` compilation flag. + Supporting Older Distros -------------------------- @@ -385,6 +407,13 @@ would ordinarily use, along with the option ``--amalgamation``. This will create two (rather large) files, ``botan_all.h`` and ``botan_all.cpp``. +.. warning:: + + Compiling a single 120K+ line C++ file containing a variety of + carefully optimized SIMD and inline asm has a way of triggering + compiler bugs. When using an amalgamation build, be sure to build + and run the test suite! + .. note:: The library will as usual be configured to target some specific operating @@ -469,6 +498,10 @@ inserted into ``build/build.h`` which is (indirectly) included into every Botan header and source file. +.. warning:: + + This option is deprecated and is planned to be removed in 3.9.0 + Enabling or Disabling Use of Certain OS Features ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -496,28 +529,19 @@ .. note:: Disabling ``dyn_load`` module will also disable the PKCS #11 wrapper, which relies on dynamic loading. -Configuration Parameters +Feature Check Macros ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -There are some configuration parameters which you may want to tweak -before building the library. These can be found in ``build.h``. This -file is overwritten every time the configure script is run (and does -not exist until after you run the script for the first time). - -Also included in ``build/build.h`` are macros which let applications -check which features are included in the current version of the -library. All of them begin with ``BOTAN_HAS_``. For example, if -``BOTAN_HAS_RSA`` is defined, then an application knows that this -version of the library has RSA available. - -``BOTAN_MP_WORD_BITS``: This macro controls the size of the words used for -calculations with the MPI implementation in Botan. It must be set to either 32 -or 64 bits. The default is chosen based on the target processor. There is -normally no reason to change this. - -``BOTAN_DEFAULT_BUFFER_SIZE``: This constant is used as the size of -buffers throughout Botan. The default should be fine for most -purposes, reduce if you are very concerned about runtime memory usage. +When ``build.h`` is created, a set of macros are defined which can be used for +compile-time feature checks. + +Each of these macros has the form ``BOTAN_HAS_FOO``, for example +``BOTAN_HAS_RSA`` or ``BOTAN_HAS_TLS_13``. Each of these macros also has a +value, which corresponds to a YYYYMMDD date code integer. If a user-visible +change is made to a module (for example adding a particular feature) the date +code is set to a new value. This can be useful for applications if they need to +check that both a feature is enabled in general and that it supports some +specific feature that was added in a particular change. Building Applications ---------------------------------------- @@ -593,10 +617,13 @@ -------------------- Many developers wish to configure a minimized build which contains only the -specific features their application will use. In general this is straighforward: +specific features their application will use. In general this is straightforward: use ``--minimized-build`` plus ``--enable-modules=`` to enable the specific modules -you wish to use. Any such configurations should build and pass the tests; if you -encounter a case where it doesn't please file an issue. +you wish to use. It is possible to use an asterisk (``*``) as a wildcard for +related modules. For instance to enable all available AES implementations, use +``--enable-modules='aes*'`` which will enable ``aes_ni``, ``aes_power8``, etc. +Any such configurations should build and pass the tests; if you encounter a case +where it doesn't please file an issue. The only trick is knowing which features you want to enable. The most common difficulty comes with entropy sources. By default, none are enabled, which means @@ -707,14 +734,6 @@ Specify a compiler cache (like ccache) to use for each compiler invocation. -``--with-endian=ORDER`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The parameter should be either "little" or "big". If not used then if -the target architecture has a default, that is used. Otherwise left -unspecified, which causes less optimal codepaths to be used but will -work on either little or big endian. - ``--with-os-features=FEAT`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -760,75 +779,16 @@ Disable all deprecated modules and features. Note that individual deprecated modules can be explicitly disabled using ``--disable-modules=MODS``. -``--disable-sse2`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Disable use of SSE2 intrinsics - -``--disable-ssse3`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Disable use of SSSE3 intrinsics - -``--disable-sse4.1`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Disable use of SSE4.1 intrinsics - -``--disable-sse4.2`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Disable use of SSE4.2 intrinsics - -``--disable-avx2`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Disable use of AVX2 intrinsics - -``--disable-bmi2`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Disable use of BMI2 intrinsics - -``--disable-rdrand`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Disable use of RDRAND intrinsics - -``--disable-rdseed`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Disable use of RDSEED intrinsics - -``--disable-aes-ni`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Disable use of AES-NI intrinsics - -``--disable-sha-ni`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Disable use of SHA-NI intrinsics - -``--disable-altivec`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Disable use of AltiVec intrinsics - ``--disable-neon`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Disable use of NEON intrinsics - -``--disable-armv8crypto`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Disable use of ARMv8 Crypto intrinsics - -``--disable-powercrypto`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Disable use of POWER Crypto intrinsics +Disable use of ARM NEON intrinsics at compile time. This is needed to support +certain distributions which still support obsolete ARMv7 cores that don't +support NEON and which, for whatever reason, completely disable support for NEON +in their toolchains. For ordinary usage this is not necessary; the NEON using +code will be compiled and simply not used if at runtime NEON support cannot be +detected. This option is supported only for 32-bit ARM processors; Aarch64 +requires NEON and for such targets this option is ignored. ``--system-cert-bundle=PATH`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -857,6 +817,13 @@ Disable stack smashing protections. **not recommended** +``--enable-stack-scrubbing`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Enable scrubbing of stack frames that were used for cryptographic calculations +on potentially sensitive data. At the moment, this is supported exclusively on +GCC 14 and newer. + ``--with-coverage-info`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1038,7 +1005,7 @@ Additional modules can be enabled if not prohibited by the policy. Currently available policies include ``bsi``, ``nist`` and ``modern``:: - $ ./configure.py --module-policy=bsi --enable-modules=tls,xts + $ ./configure.py --module-policy=bsi --enable-modules=tls13_pqc,xts ``--enable-modules=MODS`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1053,7 +1020,7 @@ ``--minimized-build`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Start with the bare minimum. This is mostly useful in conjuction with +Start with the bare minimum. This is mostly useful in conjunction with ``--enable-modules`` to get a build that has just the features a particular application requires. @@ -1138,6 +1105,22 @@ Set the include file installation dir. +``--without-include-namespace`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +By default, the header files (e.g. ``botan/hex.h``) are installed into an +additional subdirectory named ``botan-``. This option causes them to be +installed directly into ```` instead. + +This option is not needed for normal usage and is only required in order to work +around limitations in certain package managers. + +``--cmakeconfigdir=DIR`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Set the CMake config (botan-config.cmake, botan-config-version.cmake) installation dir. +Defaults to ``/cmake/Botan-``. + ``--list-modules`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff -Nru botan3-3.7.1+dfsg/doc/cli.rst botan3-3.12.0+dfsg/doc/cli.rst --- botan3-3.7.1+dfsg/doc/cli.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/cli.rst 2026-05-07 01:38:28.000000000 +0000 @@ -173,33 +173,34 @@ X.509 ---------------------------------------------- -``gen_pkcs10 key CN --country= --organization= --ca --path-limit=1 --email= --dns= --ext-ku= --key-pass= --hash=SHA-256 --emsa=`` +``gen_pkcs10 key CN --country= --organization= --ca --path-limit=1 --email= --dns= --ext-ku= --key-pass= --hash=SHA-256 --padding=`` Generate a PKCS #10 certificate signing request (CSR) using the passed PKCS #8 private key *key*. If the private key is encrypted, the decryption passphrase - *key-pass* has to be passed.*emsa* specifies the padding scheme to be used - when calculating the signature. + *key-pass* has to be passed. - - For RSA keys EMSA4 (RSA-PSS) is the default scheme. - - For ECDSA, DSA, ECGDSA, ECKCDSA and GOST-34.10 keys *emsa* defaults to EMSA1. + The *padding* option specifies the padding scheme to be used when calculating + the signature. This is only used for RSA; for such keys PSS is used by default. -``gen_self_signed key CN --country= --dns= --organization= --email= --path-limit=1 --days=365 --key-pass= --ca --hash=SHA-256 --emsa= --der`` +``gen_self_signed key CN --country= --dns= --organization= --email= --path-limit=1 --days=365 --key-pass= --ca --hash=SHA-256 --padding= --der`` Generate a self signed X.509 certificate using the PKCS #8 private key *key*. If the private key is encrypted, the decryption passphrase *key-pass* has to be passed. If *ca* is passed, the certificate is marked for certificate - authority (CA) usage. *emsa* specifies the padding scheme to be used when - calculating the signature. + authority (CA) usage. - - For RSA keys EMSA4 (RSA-PSS) is the default scheme. - - For ECDSA, DSA, ECGDSA, ECKCDSA and GOST-34.10 keys *emsa* defaults to EMSA1. + The *padding* option specifies the padding scheme to be used when calculating + the signature. This is only used for RSA; for such keys PSS is used by default. -``sign_cert --ca-key-pass= --hash=SHA-256 --duration=365 --emsa= ca_cert ca_key pkcs10_req`` +``sign_cert --ca-key-pass= --hash=SHA-256 --duration=365 --padding= ca_cert ca_key pkcs10_req`` Create a CA signed X.509 certificate from the information contained in the PKCS #10 CSR *pkcs10_req*. The CA certificate is passed as *ca_cert* and the respective PKCS #8 private key as *ca_key*. If the private key is encrypted, the decryption passphrase *ca-key-pass* has to be passed. The created - certificate has a validity period of *duration* days. *emsa* specifies the - padding scheme to be used when calculating the signature. *emsa* defaults to - the padding scheme used in the CA certificate. + certificate has a validity period of *duration* days. + + The *padding* argument specifies the padding scheme to be used when + calculating the signature; this is only used for RSA. If not set then it will + defaults to the padding scheme used in the CA certificate, or otherwise + some suitable default. ``ocsp_check --timeout=3000 subject issuer`` Verify an X.509 certificate against the issuers OCSP responder. Pass the diff -Nru botan3-3.7.1+dfsg/doc/contents.rst botan3-3.12.0+dfsg/doc/contents.rst --- botan3-3.7.1+dfsg/doc/contents.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/contents.rst 2026-05-07 01:38:28.000000000 +0000 @@ -20,5 +20,6 @@ abi packaging security + threat_model side_channels dev_ref/contents diff -Nru botan3-3.7.1+dfsg/doc/credits.rst botan3-3.12.0+dfsg/doc/credits.rst --- botan3-3.7.1+dfsg/doc/credits.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/credits.rst 2026-05-07 01:38:28.000000000 +0000 @@ -41,7 +41,6 @@ N: Simon Cogliani E: simon.cogliani@tanker.io - W: https://www.tanker.io/ P: EA73 D0AF 5A81 A61A 8931 C2CA C9AB F2E4 3820 4F25 D: Getting keystream of ChaCha S: Paris, France @@ -94,6 +93,12 @@ N: Justin Karneges D: Qt support modules (mutexes and types), X.509 API design + N: Kagan Can Sit + E: kagancansit@hotmail.com + W: https://kagancansit.github.io + D: C++20 modernization, performance optimizations, code quality improvements + S: Turkey + N: Rostyslav Khudolii E: rhudoliy@gmail.com D: SRP6 FFI @@ -124,7 +129,6 @@ E: jack@randombit.net W: https://www.randombit.net/ P: 3F69 2E64 6D92 3BBE E7AE 9258 5C0F 96E8 4EC1 6D6B - B: 1DwxWb2J4vuX4vjsbzaCXW696rZfeamahz D: Original designer/author, maintainer 2001-current S: Vermont, USA @@ -186,7 +190,6 @@ N: Simon Warta E: simon@kullo.net - W: https://www.kullo.net D: Build system S: Germany diff -Nru botan3-3.7.1+dfsg/doc/deprecated.rst botan3-3.12.0+dfsg/doc/deprecated.rst --- botan3-3.7.1+dfsg/doc/deprecated.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/deprecated.rst 2026-05-07 01:38:28.000000000 +0000 @@ -23,6 +23,14 @@ * Support for building for Windows systems prior to Windows 10 is deprecated. +X509/PKIX Deprecations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* Decoding or processing of certificates with negative serial numbers, or CRLs + containing any negative serial numbers in the revocation list, is + deprecated. In a future major release, any such certificate or CRL will be + rejected at parse time. + TLS Protocol Deprecations ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -103,6 +111,13 @@ elements are rarely if ever useful serialized into a protocol. Support for encoding or decoding EC identity elements is deprecated and will be removed. + +ASN.1 Deprecations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Support for encoding or decoding TeletexString types is deprecated and will +be removed in a future major release. + Deprecated Modules ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -117,6 +132,10 @@ - Dilithium mode ``dilithium_aes``: Similar situation to Kyber 90s mode. +- Kyber R3 support: prefer ML-KEM + +- Dilithium R3 support: prefer ML-DSA + - Block cipher ``gost_28147``: This cipher was obsolete 20 years ago. - Block cipher ``noekeon``: An interesting design but not widely implemented. @@ -139,6 +158,9 @@ permutation, but rather the Keccak hash originally proposed during the SHA-3 competition. +- MAC ``siphash``: Only supports a 64-bit output length, and not really intended + for cryptography per se. + - MAC ``x919_mac``: Quite obsolete at this point - Signature scheme ``dsa``: Finite field DSA is slow, very rarely used anymore, @@ -165,14 +187,12 @@ This section lists other functionality which will be removed in a future major release, or where a backwards incompatible change is expected. -- Support for OtherNames in X.509 certificates is deprecated - - The ``PBKDF`` class is deprecated in favor of ``PasswordHash`` and ``PasswordHashFamily``. - Implicit conversion of a private key into a public key. Currently ``Private_Key`` derives from ``Public_Key`` (and likewise for each of the - algorithm specfic classes, eg ``RSA_PrivateKey`` derives from + algorithm specific classes, eg ``RSA_PrivateKey`` derives from ``RSA_PublicKey``). In a future release these derivations will not exist. To correctly extract the public key from a private key, use the function ``Private_Key::public_key()`` @@ -217,15 +237,18 @@ Deprecated Headers ^^^^^^^^^^^^^^^^^^^^^^ -These headers are currently publically available, but will be made +These headers are currently publicly available, but will be made internal to the library in the future. + System-specific certificate store headers: ``certstor_macos.h``, ``certstor_windows.h`` -- + use via ``Certificate_Store_System`` in ``certstor_system.h`` + PBKDF headers: ``bcrypt_pbkdf.h``, ``pbkdf2.h``, ``pgp_s2k.h``, ``scrypt.h``, and ``argon2.h``: Use the ``PasswordHash`` interface instead. Internal implementation headers - seemingly no reason for applications to use: + ``assert.h``, ``curve_gfp.h``, - ``numthry.h``, ``reducer.h``, ``tls_algos.h``, ``tls_magic.h`` @@ -234,4 +257,5 @@ the library API and most are just sufficient for what the library needs to implement other functionality. ``compiler.h``, + ``mem_ops.h``, ``uuid.h``, diff -Nru botan3-3.7.1+dfsg/doc/dev_ref/configure.rst botan3-3.12.0+dfsg/doc/dev_ref/configure.rst --- botan3-3.7.1+dfsg/doc/dev_ref/configure.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/dev_ref/configure.rst 2026-05-07 01:38:28.000000000 +0000 @@ -227,7 +227,7 @@ ``--disable-modules`` or ``--disable-deprecated-features``. * ``libs`` specifies additional libraries which should be linked if this module is - included. It maps from the OS name to a list of libraries (comma seperated). + included. It maps from the OS name to a list of libraries (comma separated). * ``frameworks`` is a macOS/iOS specific feature which maps from an OS name to a framework. @@ -412,7 +412,7 @@ takes this from the OS specific information. * ``ar_output_to`` gives the flag to pass to ``ar_command`` to specify where to output the static library. - * ``werror_flags`` gives the complier flags to treat warnings as errors. + * ``werror_flags`` gives the compiler flags to treat warnings as errors. Supporting a new OS --------------------------- diff -Nru botan3-3.7.1+dfsg/doc/dev_ref/contents.rst botan3-3.12.0+dfsg/doc/dev_ref/contents.rst --- botan3-3.7.1+dfsg/doc/dev_ref/contents.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/dev_ref/contents.rst 2026-05-07 01:38:28.000000000 +0000 @@ -16,6 +16,7 @@ todo os oids + pcurves next_major reading_list mistakes diff -Nru botan3-3.7.1+dfsg/doc/dev_ref/contributing.rst botan3-3.12.0+dfsg/doc/dev_ref/contributing.rst --- botan3-3.7.1+dfsg/doc/dev_ref/contributing.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/dev_ref/contributing.rst 2026-05-07 01:38:28.000000000 +0000 @@ -12,7 +12,7 @@ * ``tests`` contain what you would expect. Input files go under ``tests/data``. * ``python/botan3.py`` is the Python ctypes wrapper * ``bogo_shim`` contains the shim binary and configuration for - `BoringSSL's TLS test suite `_ + `BoringSSL's TLS test suite `_ * ``fuzzer`` contains fuzz targets for various modules of the library * ``ct_selftest`` has some tests to validate constant time checker tools (e.g. valgrind) * ``build-data`` contains files read by the configure script. For @@ -165,24 +165,32 @@ of total coverage. This coverage build requires the development headers for zlib, bzip2, liblzma, TrouSerS (libtspi), and Sqlite3. +Development Container +---------------------------------------- + +The repository root contains a .devcontainer configuration based on Ubuntu which +conveniently sets up a fully-functional build and test environment. This is the +recommended way for new contributors to start developing. + +Currently, the .devcontainer integrates best with Visual Studio Code, but other +integrations would be welcome. The container should also work decently using the +bare-metal devcontainer CLI. + Editor Integrations ---------------------------------------- The folder ``src/editors`` contains configuration files for a few editors. To make use of them, create symlinks of those into the root of your local -Botan repository. For example, to enable integration with VSCode and configure -the editor using editorconfig, you can do the following: +Botan repository. For instance, to enable editorconfig for any editor that +supports it, you can do the following: .. code-block:: bash cd /home/you/projects/botan - ln -s src/editors/vscode .vscode ln -s src/editors/editorconfig .editorconfig - code . - -With the recommended extensions installed, you should now have a good starting -point for working with Botan in VSCode. +If you are using VSCode with the development container, the right symlinks are +created automatically and you should be good to go off the bat. Copyright Notice ---------------------------------------- @@ -241,12 +249,13 @@ Use ``m_`` prefix on all member variables. ``clang-format`` is used for all C++ formatting. The configuration is -in ``.clang-format`` in the root directory. You can rerun the -formatter using ``make fmt``, by invoking the script -``src/scripts/dev_tools/run_clang_format.py`` or using an appropriate editor -configuration from ``src/editors``. If the output would be truly horrible, it is -allowed to disable formatting for a specific area using ``// clang-format off`` -annotations. +in ``src/configs/clang-format``. You can rerun the formatter using ``make fmt``, +by invoking the script ``src/scripts/dev_tools/run_clang_format.py`` or symlink +the configuration into the repo root as ``.clang-format`` and using an appropriate +editor configuration from ``src/editors``. Note that the dev-container shipped with +this repository sets this up properly when used with VSCode. If the output would be +truly horrible, it is allowed to disable formatting for a specific area using +``// clang-format off`` annotations. .. note:: @@ -285,6 +294,13 @@ should in any case be annotated (using ``CT::poison``) so it can be checked at runtime with tools. +SIMD Intrinsics +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Using intrinsics is the preferred method of invoking hardware specific instructions. +In doing so, prefer using (and extending if required) the wrapper types included in +``utils/simd``. + Operating System Dependencies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff -Nru botan3-3.7.1+dfsg/doc/dev_ref/fuzzing.rst botan3-3.12.0+dfsg/doc/dev_ref/fuzzing.rst --- botan3-3.7.1+dfsg/doc/dev_ref/fuzzing.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/dev_ref/fuzzing.rst 2026-05-07 01:38:28.000000000 +0000 @@ -85,7 +85,6 @@ * https://github.com/randombit/crypto-corpus * https://github.com/mozilla/nss-fuzzing-corpus * https://github.com/google/boringssl/tree/master/fuzz -* https://github.com/openssl/openssl/tree/master/fuzz/corpora Adding new fuzzers --------------------- diff -Nru botan3-3.7.1+dfsg/doc/dev_ref/mistakes.rst botan3-3.12.0+dfsg/doc/dev_ref/mistakes.rst --- botan3-3.7.1+dfsg/doc/dev_ref/mistakes.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/dev_ref/mistakes.rst 2026-05-07 01:38:28.000000000 +0000 @@ -19,7 +19,7 @@ ------------------------------------ Classes like AES_128 and SHA_256 should never have been exposed to applications. -Intead such operations should have been accessible only via the higher level +Instead such operations should have been accessible only via the higher level interfaces (here BlockCipher and HashFunction). This would substantially reduce the overall API and ABI surface. diff -Nru botan3-3.7.1+dfsg/doc/dev_ref/next_major.rst botan3-3.12.0+dfsg/doc/dev_ref/next_major.rst --- botan3-3.7.1+dfsg/doc/dev_ref/next_major.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/dev_ref/next_major.rst 2026-05-07 01:38:28.000000000 +0000 @@ -16,21 +16,21 @@ into it... A number of operations currently defined on Public_Key can be -moved to Asymetric_Key, for example key_length and algorithm_identifier. +moved to Asymmetric_Key, for example key_length and algorithm_identifier. Due to Private_Key deriving from Public_Key, the fingerprint functions are oddly named. Otherwise we can't correctly disambiguate sk->fingerprint(); should this be the fingerprint of the public or private key. With the -split we can move this to Asymetric_Key::fingerprint and know that the +split we can move this to Asymmetric_Key::fingerprint and know that the correct thing happens. The public and private key encoding functions (pkcs8.h, x509_key.h) are also complicated by the combined keys. For example we have to use -PKCS8::PEM_encode(key) because key.PEM_encode() would be ambigious +PKCS8::PEM_encode(key) because key.PEM_encode() would be ambiguous (similar situation as with the fingerprint APIs currently). Once the key types are split, we can move all of this to the key types themselves, or again (for the shared cases, like unencrypted PEM) to -Asymetric_Key. +Asymmetric_Key. Decoding also can become simpler. We could consider moving to a model that doesn't use DataSource? Maybe just a span even? diff -Nru botan3-3.7.1+dfsg/doc/dev_ref/os.rst botan3-3.12.0+dfsg/doc/dev_ref/os.rst --- botan3-3.7.1+dfsg/doc/dev_ref/os.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/dev_ref/os.rst 2026-05-07 01:38:28.000000000 +0000 @@ -1,3 +1,6 @@ +.. This file was automatically generated by src/scripts/dev_tools/gen_os_features.py on 2026-04-24 +.. All manual changes will be lost. Edit the script instead. + OS Features ======================================== @@ -35,7 +38,6 @@ "apple_keychain", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", " ", " ", " " "arc4random", " ", "X", " ", "X", " ", "X", " ", " ", " ", " ", "X", " ", " ", "X", " ", "X", " ", "X", " ", " ", " ", " " "atomics", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", " ", "X", "X", "X", "X", "X" - "auxinfo", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", " " "cap_enter", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " " "ccrandom", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", " ", " ", " " "certificate_store", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", " ", "X" @@ -44,27 +46,24 @@ "crypto_ng", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", " " "dev_random", "X", "X", "X", "X", "X", "X", " ", "X", "X", "X", " ", "X", " ", "X", " ", "X", " ", "X", "X", "X", " ", " " "elf_aux_info", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", " ", " " - "explicit_bzero", " ", " ", " ", "X", " ", "X", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", "X", " ", " ", " ", " " + "explicit_bzero", " ", " ", " ", "X", " ", "X", " ", " ", " ", "X", " ", "X", " ", " ", " ", " ", " ", "X", " ", " ", " ", " " "explicit_memset", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", " " "filesystem", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", " ", "X", "X", "X", "X", "X" "getauxval", " ", "X", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", " ", " ", " ", " ", " " - "getentropy", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", "X", " ", "X", " ", " ", " ", "X", " ", "X", " ", " " - "getrandom", " ", " ", " ", "X", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", " ", " ", " ", " ", " " - "pledge", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", " ", " " + "getentropy", " ", " ", " ", " ", " ", "X", " ", " ", " ", "X", " ", "X", " ", "X", " ", " ", " ", "X", " ", "X", " ", " " + "getrandom", " ", " ", " ", "X", " ", "X", " ", " ", " ", "X", " ", "X", " ", " ", " ", " ", " ", " ", " ", " ", " ", " " "posix1", "X", "X", "X", "X", "X", "X", " ", "X", "X", "X", "X", "X", " ", "X", " ", "X", " ", "X", "X", "X", " ", " " "posix_mlock", "X", "X", " ", "X", " ", "X", " ", " ", "X", "X", "X", "X", " ", "X", " ", "X", " ", "X", "X", "X", " ", " " "prctl", " ", "X", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", " ", " ", " ", " ", " " - "proc_fs", "X", " ", " ", "X", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", " ", " ", "X", " ", " " "rtlgenrandom", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", " ", "X" "rtlsecurezeromemory", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", "X" "sandbox_proc", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", " ", " ", " " "setppriv", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", " ", " " "sockets", "X", "X", "X", "X", " ", "X", " ", "X", "X", "X", "X", "X", " ", "X", " ", "X", " ", "X", "X", "X", " ", " " + "sysctlbyname", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", "X", " ", " ", " ", " ", " ", " ", " ", " " + "system_clock", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X" "thread_local", "X", "X", "X", "X", " ", "X", "X", "X", "X", "X", "X", "X", " ", "X", "X", "X", " ", "X", "X", "X", "X", "X" "threads", "X", "X", "X", "X", " ", "X", "X", "X", "X", "X", "X", "X", " ", "X", "X", "X", " ", "X", "X", "X", "X", "X" "virtual_lock", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", " ", "X" "win32", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", "X", "X" "winsock2", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", "X" - -.. note:: - This file is auto generated by ``src/scripts/gen_os_features.py``. Dont modify it manually. diff -Nru botan3-3.7.1+dfsg/doc/dev_ref/pcurves.rst botan3-3.12.0+dfsg/doc/dev_ref/pcurves.rst --- botan3-3.7.1+dfsg/doc/dev_ref/pcurves.rst 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/dev_ref/pcurves.rst 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,62 @@ +Custom Elliptic Curve +=================================== + +Some products or protocols use custom designed (or even classified) elliptic +curve parameters. + +The default way of supporting curves like this is to use the constructor of +``EC_Group`` which accepts the various parameters as integers. This uses the +generic elliptic curve logic, which is already reasonably fast. + +However in certain cases the best possible performance is required, perhaps +because the hardware it is being deployed on is old/underpowered. The library +provides an escape hatch to support this, where a custom curve is supported +using the same curve-specific logic as used to implement common curves like +P-256. + +.. warning:: + + This process is documented for convenience but NOT OFFICIALLY SUPPORTED. + If you need to use this, please consider the life choices that brought you + to this point. + +The groups supported by the library are specified in a file +``src/build-data/ec_groups.txt``, which contains entries like + +.. code-block:: text + + Name = secp256r1 + OID = 1.2.840.10045.3.1.7 + Impl = pcurve generic legacy + P = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF + A = -3 + B = 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B + X = 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296 + Y = 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5 + N = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551 + +.. note:: + + Not all curve parameters can be supported by this process. In particular, + it is required that + + 1) The prime field is between 192 and 512 bits, and a multiple of 32 bits. + 2) The prime must be congruent to 3 modulo 4. + 3) The group order must have the same bit length as the prime. + 4) The group must be prime order; no cofactors are allowed. + +To add a new curve with curve specific optimizations, do the following: + +1) Add a new block to ``ec_groups.txt`` specifying the parameters. The + important value is that ``Impl`` contains ``pcurve``. If you only want to + support the group using the new dedicated implementation that will be + generated in a later step, you can skip ``generic`` and ``legacy`` here. + +2) Add the OID to ``src/build-data/oids.txt`` in the ``[ecc_param]`` block - the + OID name should match the value of ``Name`` in ``ec_groups.txt`` + +3) Run ``./src/scripts/dev_tools/gen_ec_groups.py``. This script requires the + Jinja2 template library, and the program ``addchain`` from + https://github.com/mmcloughlin/addchain + +4) Run ``./src/scripts/dev_tools/gen_oids.py`` to regenerate the OID lookup table diff -Nru botan3-3.7.1+dfsg/doc/dev_ref/reading_list.rst botan3-3.12.0+dfsg/doc/dev_ref/reading_list.rst --- botan3-3.7.1+dfsg/doc/dev_ref/reading_list.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/dev_ref/reading_list.rst 2026-05-07 01:38:28.000000000 +0000 @@ -12,12 +12,12 @@ * "Randomizing the Montgomery Powering Ladder" Le, Tan, Tunstall https://eprint.iacr.org/2015/657 - A variant of Algorithm 7 is used for GF(p) point multplications when + A variant of Algorithm 7 is used for GF(p) point multiplications when BOTAN_POINTGFP_BLINDED_MULTIPLY_USE_MONTGOMERY_LADDER is set * "Accelerating AES with vector permute instructions" Mike Hamburg https://shiftleft.org/papers/vector_aes/ - His public doman assembly code was rewritten into SSS3 intrinsics + His public domain assembly code was rewritten into SSS3 intrinsics for aes_ssse3. * "Elliptic curves and their implementation" Langley diff -Nru botan3-3.7.1+dfsg/doc/dev_ref/release_process.rst botan3-3.12.0+dfsg/doc/dev_ref/release_process.rst --- botan3-3.7.1+dfsg/doc/dev_ref/release_process.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/dev_ref/release_process.rst 2026-05-07 01:38:28.000000000 +0000 @@ -12,27 +12,24 @@ This information is only useful if you are a developer of botan who is creating a new release of the library. -Pre Release Testing +Pre Release Checks ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -In the week prior to a release: - -- [ ] Update relevant third party test suites (eg Limbo and BoGo) -- [ ] Do maintainer-mode builds with Clang and GCC to catch any warnings -- [ ] Test build configurations using `src/scripts/test_all_configs.py` -- [ ] Test a few builds on platforms not in CI (eg OpenBSD, FreeBSD, Solaris) - -Final Changes -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -When it is time to make the release: +In the week prior to a release, after feature freeze goes into effect - [ ] Check that the version number in ``src/build-data/version.txt`` is correct. - [ ] Confirm that the release notes in ``news.rst`` are accurate and complete. +- [ ] Diff ffi.h vs the previous release; is a new FFI version required? +- [ ] Perform a full clang-tidy run with latest available Clang +- [ ] Test build configurations using `src/scripts/test_all_configs.py` +- [ ] Test a few builds on platforms not in CI (eg OpenBSD, FreeBSD, Solaris) +- [ ] Update relevant third party test suites (eg Limbo, BoGo, TLS-Anvil, ...) Tag the Release ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +At the time the release is created + - [ ] Update the release date in ``news.rst`` - [ ] Update ``readme.rst`` with the new release URL/date - [ ] Check in those changes then backport to the release branch:: diff -Nru botan3-3.7.1+dfsg/doc/dev_ref/test_framework.rst botan3-3.12.0+dfsg/doc/dev_ref/test_framework.rst --- botan3-3.7.1+dfsg/doc/dev_ref/test_framework.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/dev_ref/test_framework.rst 2026-05-07 01:38:28.000000000 +0000 @@ -8,13 +8,13 @@ The intent is that the test framework and the test suite evolve symbiotically; as a general rule of thumb if a new function would make -the implementation of just two distinct tests simpler, it is worth +the implementation of just a few distinct tests simpler, it is worth adding to the framework on the assumption it will prove useful again. Feel free to propose changes to the test system. When writing a new test, there are three key classes that are used, namely ``Test``, ``Test::Result``, and ``Text_Based_Test``. A ``Test`` -(or ``Test_Based_Test``) runs and returns one or more ``Test::Result``. +(or ``Text_Based_Test``) runs and returns one or more ``Test::Result``. Namespaces in Test ------------------- @@ -30,12 +30,14 @@ ----------- The test framework is heavily data driven. As of this writing, there -is about 1 Mib of test code and 17 MiB of test data. For most (though +is about 2.5 Mib of test code and 28 MiB of test data. For most (though certainly not all) tests, it is better to add a data file representing the input and outputs, and run the tests over it. Data driven tests make adding or editing tests easier, for example by writing scripts which produce new test data and output it in the expected format. +Test data lives in ``src/tests/data``. + Test -------- @@ -70,7 +72,7 @@ .. cpp:function:: static Botan::RandomNumberGenerator& rng() Returns a reference to a fast, not cryptographically secure - random number generator. It is deterministicly seeded with the + random number generator. It is deterministically seeded with the seed logged by the test runner, so it is possible to reproduce results in "random" tests. @@ -93,7 +95,7 @@ return true or false if the test was successful or not; this allows performing conditional blocks as a result of earlier tests:: - if(result.test_eq("first value", produced, expected)) + if(result.test_str_eq("first value", produced, expected)) { // further tests that rely on the initial test being correct } @@ -110,91 +112,110 @@ Report a test that was successful. - .. cpp:function:: bool test_success(const std::string& note) + .. cpp:function:: bool test_success(std::string_view note) Report a test that was successful, including some comment. - .. cpp:function:: bool test_failure(const std::string& err) + .. cpp:function:: bool test_failure(std::string_view err) Report a test failure of some kind. The error string will be logged. - .. cpp:function:: bool test_failure(const std::string& what, const std::string& error) + .. cpp:function:: bool test_failure(std::string_view what, std::string_view error) Report a test failure of some kind, with a description of what failed and what the error was. - .. cpp:function:: void test_failure(const std::string& what, const uint8_t buf[], size_t buf_len) + .. cpp:function:: void test_failure(std::string_view what, std::span context) Report a test failure due to some particular input, which is provided as - arguments. Normally this is only used if the test was using some + ``context``. Normally this is only used if the test was using some randomized input which unexpectedly failed, since if the input is hardcoded or from a file it is easier to just reference the test number. - .. cpp:function:: bool test_eq(const std::string& what, const std::string& produced, const std::string& expected) + .. cpp:function:: bool test_str_eq(std::string_view what, std::string_view produced, std::string_view expected) - Compare to strings for equality. + Compare two strings for equality. - .. cpp:function:: bool test_ne(const std::string& what, const std::string& produced, const std::string& expected) + .. cpp:function:: bool test_str_ne(std::string_view what, std::string_view produced, std::string_view expected) - Compare to strings for non-equality. + Compare two strings for non-equality. - .. cpp:function:: bool test_eq(const char* producer, const std::string& what, \ - const uint8_t produced[], size_t produced_len, \ - const uint8_t expected[], size_t expected_len) + .. cpp:function:: bool test_bin_eq(std::string_view what, \ + std::span produced, \ + std::span expected); Compare two arrays for equality. - .. cpp:function:: bool test_ne(const char* producer, const std::string& what, \ - const uint8_t produced[], size_t produced_len, \ - const uint8_t expected[], size_t expected_len) + .. cpp:function:: bool test_bin_eq(std::string_view what, \ + std::span produced, \ + std::string_view expected_hex); + + Compare two arrays for equality, with the expected value provided as a hex string. - Compare two arrays for non-equality. + .. cpp:function:: template bool test_not_null(std::string_view what, T* ptr) - .. cpp:function:: bool test_eq(const std::string& producer, const std::string& what, \ - const std::vector& produced, \ - const std::vector& expected) + Verify that the pointer is not null. - Compare two vectors for equality. + .. cpp:function:: bool test_u8_eq(std::string_view what, uint8_t produced, uint8_t expected) - .. cpp:function:: bool test_ne(const std::string& producer, const std::string& what, \ - const std::vector& produced, \ - const std::vector& expected) + Test that ``produced`` == ``expected``. - Compare two vectors for non-equality. + .. cpp:function:: bool test_u16_eq(std::string_view what, uint16_t produced, uint16_t expected) - .. cpp:function:: bool confirm(const std::string& what, bool expr) + Test that ``produced`` == ``expected``. - Test that some expression evaluates to ``true``. + .. cpp:function:: bool test_u32_eq(std::string_view what, uint32_t produced, uint32_t expected) - .. cpp:function:: template bool test_not_null(const std::string& what, T* ptr) + Test that ``produced`` == ``expected``. - Verify that the pointer is not null. + .. cpp:function:: bool test_u64_eq(std::string_view what, uint64_t produced, uint64_t expected) - .. cpp:function:: bool test_lt(const std::string& what, size_t produced, size_t expected) + Test that ``produced`` == ``expected``. + + .. cpp:function:: bool test_sz_eq(std::string_view what, size_t produced, size_t expected) + + Test that ``produced`` == ``expected``. + + .. cpp:function:: bool test_sz_lt(std::string_view what, size_t produced, size_t expected) Test that ``produced`` < ``expected``. - .. cpp:function:: bool test_lte(const std::string& what, size_t produced, size_t expected) + .. cpp:function:: bool test_sz_lte(std::string_view what, size_t produced, size_t expected) Test that ``produced`` <= ``expected``. - .. cpp:function:: bool test_gt(const std::string& what, size_t produced, size_t expected) + .. cpp:function:: bool test_sz_gt(std::string_view what, size_t produced, size_t expected) Test that ``produced`` > ``expected``. - .. cpp:function:: bool test_gte(const std::string& what, size_t produced, size_t expected) + .. cpp:function:: bool test_sz_gte(std::string_view what, size_t produced, size_t expected) Test that ``produced`` >= ``expected``. - .. cpp:function:: bool test_throws(const std::string& what, std::function fn) + .. cpp:function:: bool test_throws(std::string_view what, std::function fn) Call a function and verify it throws an exception of some kind. - .. cpp:function:: bool test_throws(const std::string& what, const std::string& expected, std::function fn) + .. cpp:function:: bool test_throws(std::string_view what, std::string_view expected, std::function fn) Call a function and verify it throws an exception of some kind and that the exception message exactly equals ``expected``. +There is also a comparison function for arbitrary types, which is defined in the +separate header ``test_arb_eq.h`` because it drags in some additional headers. + +.. cpp:function:: template \ + bool test_arb_eq(Test::Result& result, std::string_view what, const T& produced, const T& expected) + + Compare some arbitrary Ts for equality. + + It is required that ``T`` not be something that is handled by one of the + existing comparison functions (eg not a string, integer, or bytestring + type) and also that ``test_arb_eq`` is able to deduce some way of + printing values of ``T``. Depending on your ``T`` you may need to extend + the implementation of ``detail::to_string`` in that header. + + Text_Based_Test ----------------- @@ -221,37 +242,36 @@ the test provides a default value which is returned if the key was not set for this particular instance of the test. - .. cpp:function:: std::vector get_req_bin(const std::string& key) const + .. cpp:function:: std::vector get_req_bin(std::string_view key) const Return a required binary string. The input is assumed to be hex encoded. - .. cpp:function:: std::vector get_opt_bin(const std::string& key) const + .. cpp:function:: std::vector get_opt_bin(std::string_view key) const Return an optional binary string. The input is assumed to be hex encoded. + Returns empty if the value was not provided. - .. cpp:function:: std::vector> get_req_bin_list(const std::string& key) const - - .. cpp:function:: Botan::BigInt get_req_bn(const std::string& key) const + .. cpp:function:: Botan::BigInt get_req_bn(std::string_view key) const Return a required BigInt. The input can be decimal or (with "0x" prefix) hex encoded. - .. cpp:function:: Botan::BigInt get_opt_bn(const std::string& key, const Botan::BigInt& def_value) const + .. cpp:function:: Botan::BigInt get_opt_bn(std::string_view key, const Botan::BigInt& def_value) const Return an optional BigInt. The input can be decimal or (with "0x" prefix) hex encoded. - .. cpp:function:: std::string get_req_str(const std::string& key) const + .. cpp:function:: std::string get_req_str(std::string_view key) const Return a required text string. - .. cpp:function:: std::string get_opt_str(const std::string& key, const std::string& def_value) const + .. cpp:function:: std::string get_opt_str(std::string_view key, std::string_view def_value) const - Return an optional text string. + Return an optional text string, or the specified default value if not set. - .. cpp:function:: size_t get_req_sz(const std::string& key) const + .. cpp:function:: size_t get_req_sz(std::string_view key) const Return a required integer. The input should be decimal. - .. cpp:function:: size_t get_opt_sz(const std::string& key, const size_t def_value) const + .. cpp:function:: size_t get_opt_sz(std::string_view key, const size_t def_value) const Return an optional integer. The input should be decimal. @@ -261,8 +281,6 @@ const std::string& required_keys, \ const std::string& optional_keys = "") - This constructor is - .. note:: The final element of required_keys is the "output key", that is the key which signifies the boundary between one test and the next. @@ -291,24 +309,21 @@ If you are simply writing a new test there should be no need to modify the runner, however it can be useful to be aware of its abilities. -The runner can run tests concurrently across many cores. By default single -threaded execution is used, but you can use ``--test-threads`` option to -specify the number of threads to use. If you use ``--test-threads=0`` then -the runner will probe the number of active CPUs and use that (but limited -to at most 16). If you want to run across many cores on a large machine, -explicitly specify a thread count. The speedup is close to linear. +The runner can run tests concurrently across many cores, and does so by default +on most systems. If you want single-threaded testing for some reason, use the +option ``--test-threads=1``. If not specified then (as of this writing) at most +16 threads will be used; you can use eg ``--test-threads=128`` if running on +a large system. The RNG used in the tests is deterministic, and the seed is logged for each execution. You can cause the random sequence to repeat using ``--drbg-seed`` -option. - -.. note:: - Currently the RNG is seeded just once at the start of execution. So you - must run the exact same sequence of tests as the original test run in - order to get reproducible results. +option. It's not necessary that the same sequence of tests be executed in order +to replay the state; if you see a test ``foo`` fail with a specific DRBG seed, +for example in a CI run, you should be able to replicate that with just +``botan-test --drbg-seed= foo``. If you are trying to track down a bug that happens only occasionally, two very useful options are ``--test-runs`` and ``--abort-on-first-fail``. The first takes an integer and runs the specified test cases that many times. The second -causes abort to be called on the very first failed test. This is sometimes +causes ``abort`` to be called on the very first failed test. This is sometimes useful when tracing a memory corruption bug. diff -Nru botan3-3.7.1+dfsg/doc/dev_ref/todo.rst botan3-3.12.0+dfsg/doc/dev_ref/todo.rst --- botan3-3.7.1+dfsg/doc/dev_ref/todo.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/dev_ref/todo.rst 2026-05-07 01:38:28.000000000 +0000 @@ -16,35 +16,26 @@ * Threefish-1024 * Skein-MAC * FFX format preserving encryption (NIST 800-38G) -* Adiantum (https://eprint.iacr.org/2018/720) * HPKE (RFC 9180) * Blake3 Hardware Specific Optimizations ---------------------------------------- - -* Stiched AES/GCM mode for CPUs supporting both AES and CLMUL -* Combine AES-NI, ARMv8 and POWER AES implementations (as already done for CLMUL) -* GFNI implementations for: Camellia, SEED, ARIA -* NEON/VMX support for the SIMD based GHASH -* Vector permute AES only supports little-endian systems; fix for big-endian -* Poly1305 using AVX2 -* SHA-512 using BMI2+AVX2 and/or new Intel instructions -* SM3 using x86 SM3-NI -* SM4 using x86 SM4-NI -* Constant time bitsliced DES +* AVX512 IFMA optimized field arithmetic for P-256 and/or P-384 +* Stitched AES/GCM implementation +* GFNI implementations of ZFEC, others? +* NEON/VMX/LSX support for the SIMD based GHASH * SIMD evaluation of SHA-2 and SHA-3 compression functions -* Improved Salsa implementations (SIMD_4x32 and/or AVX2) -* Add CLMUL/PMULL implementations for CRC24/CRC32 -* Add support for ARMv8.4-A SHA-3, SM3 and RNG instructions -* POWER8 SHA-2 extensions (GH #1486 + #1487) -* Add support for VPSUM on big-endian PPC64 (GH #2252) -* Add support for RISC-V crypto extensions +* Improved Salsa implementations (SIMD_4x32, AVX2, AVX512, ...) +* Add CLMUL/PMULL implementations for CRC24 +* Add support for ARMv8.4-A SHA-3 instructions +* Support POWER8 SHA-2 extensions (GH #1486 + #1487) +* Add support for RISC-V vector and crypto extensions +* Add support for using Loongarch64 LASX (256-bit SIMD) Public Key Crypto, Math ---------------------------------------- -* Short vector optimization for BigInt * BLS12-381 pairing, BLS signatures * Identity based encryption * Paillier homomorphic cryptosystem @@ -54,16 +45,12 @@ Utility Functions ------------------ -* Constant time base64 and hex is optimized using SWAR; apply this to base32 and base58 * Make Memory_Pool more concurrent (currently uses a global lock) * Guarded integer type to prevent overflow bugs External Providers ---------------------------------------- -* /dev/crypto provider (ciphers, hashes) -* Windows CryptoNG provider (ciphers, hashes) -* Extend Apple CommonCrypto provider (HMAC, CMAC, RSA, ECDSA, ECDH) * Add support for iOS keychain access * Extend support for TPM 2.0 (PCR, NVRAM, Policies, etc) @@ -71,7 +58,6 @@ ---------------------------------------- * Make DTLS support optional at build time -* Make TLS 1.2 support optional at build time * Improve/optimize DTLS defragmentation and retransmission * Make RSA optional at build time * Make finite field DH optional at build time @@ -83,7 +69,6 @@ ---------------------------------------- * Further tests of validation API (see GH #785) -* Test suite for validation of 'real world' cert chains (GH #611) * X.509 policy constraints * OCSP responder logic @@ -93,9 +78,6 @@ * Noise protocol * ACME protocol (needs a story for JSON) * Cryptographic Message Syntax (RFC 5652) -* Fernet symmetric encryption (https://cryptography.io/en/latest/fernet/) -* RNCryptor format (https://github.com/RNCryptor/RNCryptor) -* Age format (https://age-encryption.org/v1) * Useful OpenPGP subset 1: symmetrically encrypted files. Not aiming to process arbitrary OpenPGP, but rather produce something that happens to be readable by `gpg` and is relatively @@ -109,12 +91,11 @@ * Unicode path support on Windows (GH #1615) * The X.509 path validation tests have much duplicated logic -New C APIs +FFI APIs ---------------------------------------- * PKCS10 requests * Certificate signing -* CRLs * Expose TLS * Expose secret sharing * Expose deterministic PRNG @@ -130,7 +111,6 @@ so it can run as a standalone item (copied to a device, etc) * Run iOS binary under simulator in CI * Run Android binary under simulator in CI -* Add support for vxWorks CLI ---------------------------------------- diff -Nru botan3-3.7.1+dfsg/doc/goals.rst botan3-3.12.0+dfsg/doc/goals.rst --- botan3-3.7.1+dfsg/doc/goals.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/goals.rst 2026-05-07 01:38:28.000000000 +0000 @@ -14,6 +14,12 @@ accounted for where necessary. The library should never crash, or invoke undefined behavior, regardless of circumstances. +* Constant time programming. The table stakes for a modern cryptographic library + include being immune to basic timing/cache based side channels. Botan includes + utilities to assist in writing and testing constant time code. A test suite + run nightly in CI verifies Botan's constant time behavior across a range of + compilers, compiler options, and CPU architectures. + * Implement schemes important in practice. It should be practical to implement any real-world crypto protocol using just what the library provides. It is worth some (limited) additional complexity in the library, in order to expand @@ -48,9 +54,9 @@ least the option of using a post-quantum scheme. Botan provides a conservative selection of algorithms thought to be post-quantum secure. -* Performance. Botan does not in every case strive to be faster than every other - software implementation, but performance should be competitive and over time - new optimizations are identified and applied. +* Performance. Botan aims to have the fastest possible implementation of all + algorithms it supports, subject to the constraints implicit with the other + project goals. * Support whatever I/O mechanism the application wants. Allow the application to control all aspects of how the network is contacted, and ensure the API makes diff -Nru botan3-3.7.1+dfsg/doc/hardware_acceleration.rst botan3-3.12.0+dfsg/doc/hardware_acceleration.rst --- botan3-3.7.1+dfsg/doc/hardware_acceleration.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/hardware_acceleration.rst 2026-05-07 01:38:28.000000000 +0000 @@ -6,182 +6,293 @@ that are not available on all platforms and either speed up the algorithm or improve security in terms of side channel resistance. -A “base” software implementation is always provided. For example, for the AES-128 -block cipher three implementations are available. All of the AES-128 implementations -are immune to common cache/timing based side channels. - -* If AES hardware support is available (AES-NI, POWER8, Aarch64) use that -* If 128-bit SIMD with byte shuffles are available (SSSE3, NEON, or Altivec), - use the vperm technique published by Mike Hamburg at CHES 2009 -* If no hardware or SIMD support, fall back to a constant time bitsliced implementation - The following sections list the platforms and algorithms for which hardware acceleration is available. If the CPU specific optimizations are available at runtime, they are automatically used if enabled in the build. If not, the base implementation is used. +It is possible to disable CPU-specific optimizations at runtime by setting the +environment variable ``BOTAN_CLEAR_CPUID``. For example +``BOTAN_CLEAR_CPUID=avx2`` will disable use of any AVX2 instructions. + x86 -------------- -On x86-64 and x86-32 platforms, the following CPU specific optimizations are available: +On x86-64 and x86-32 platforms, the following CPU specific optimizations are available. + +.. note:: + + AVX-512 codepaths are only used on x86-64 processors that support AVX-512 + extensions similar to Intel Ice Lake or AMD Zen4 (requires AVX-512 F, VL, BW, + DQ, VBMI, VBMI2, BITALG, IFMA) + ++-----------+--------------------------------------------+-------------------------+------------+ +| Algorithm | Extension | Module | Added in | ++===========+============================================+=========================+============+ +| AES | VAES-AVX2 | ``aes_vaes`` | 3.6.0 | +| | | | | +| | AES-NI | ``aes_ni`` | 1.9.3 | +| | | | | +| | SSSE3 | ``aes_vperm`` | 1.9.10 | ++-----------+--------------------------------------------+-------------------------+------------+ +| AES-GCM | AVX-512 + CLMUL | ``ghash_avx512_clmul`` | 3.11.0 | +| | | | | +| | CLMUL | ``ghash_cpu`` | 1.11.6 | +| | | | | +| | SSSE3 | ``ghash_vperm`` | 1.9.10 | ++-----------+--------------------------------------------+-------------------------+------------+ +| Argon2 | AVX-512 | ``argon2_avx512`` | 3.11.1 | +| | | | | +| | AVX2 | ``argon2_avx2`` | 3.0.0 | +| | | | | +| | SSSE3 | ``argon2_simd64`` | 2.19.2 | ++-----------+--------------------------------------------+-------------------------+------------+ +| ARIA | AVX-512 + GFNI | ``aria_avx512_gfni`` | 3.11.0 | +| | | | | +| | AES-NI | ``aria_hwaes`` | 3.11.1 | ++-----------+--------------------------------------------+-------------------------+------------+ +| Camellia | AVX-512 + GFNI | ``camellia_avx512_gfni``| 3.11.0 | +| | | | | +| | AVX2 + GFNI | ``camellia_avx2_gfni`` | 3.9.0 | +| | | | | +| | AES-NI | ``camellia_hwaes`` | 3.11.1 | ++-----------+--------------------------------------------+-------------------------+------------+ +| ChaCha | AVX-512 | ``chacha_avx512`` | 3.1.0 | +| | | | | +| | AVX2 | ``chacha_avx2`` | 2.8.0 | +| | | | | +| | SSSE3 | ``chacha_simd32`` | 1.11.32 | ++-----------+--------------------------------------------+-------------------------+------------+ +| CTR | AVX2 | ``ctr_avx2`` | 3.11.1 | +| | | | | +| | SSSE3 | ``ctr_simd32`` | 3.11.1 | ++-----------+--------------------------------------------+-------------------------+------------+ +| IDEA | AVX2 | ``idea_avx2`` | 3.11.0 | +| | | | | +| | SSE2 | ``idea_sse2`` | 1.9.4 | ++-----------+--------------------------------------------+-------------------------+------------+ +| NOEKEON | SSSE3 | ``noekeon_simd`` | 1.9.4 | ++-----------+--------------------------------------------+-------------------------+------------+ +| Poly1305 | AVX-512 | ``poly1305_avx512`` | 3.11.0 | +| | | | | +| | AVX2 | ``poly1305_avx2`` | 3.11.0 | ++-----------+--------------------------------------------+-------------------------+------------+ +| RDRAND | RDRAND | ``processor_rng`` | 1.11.31 | ++-----------+--------------------------------------------+-------------------------+------------+ +| RDSEED | RDSEED | ``rdseed`` | 1.11.36 | ++-----------+--------------------------------------------+-------------------------+------------+ +| SEED | AVX-512 + GFNI | ``seed_avx512_gfni`` | 3.11.1 | +| | | | | +| | AES-NI | ``seed_hwaes`` | 3.11.1 | ++-----------+--------------------------------------------+-------------------------+------------+ +| Serpent | AVX-512 | ``serpent_avx512`` | 3.1.0 | +| | | | | +| | AVX2 | ``serpent_avx2`` | 2.8.0 | +| | | | | +| | SSSE3 | ``serpent_simd`` | 1.9.0 | ++-----------+--------------------------------------------+-------------------------+------------+ +| SHACAL2 | Intel SHA Extensions | ``shacal2_x86`` | 2.3.0 | +| | | | | +| | AVX-512 | ``shacal2_avx512`` | 3.9.0 | +| | | | | +| | AVX2 | ``shacal2_avx2`` | 2.13.0 | ++-----------+--------------------------------------------+-------------------------+------------+ +| SHA-1 | Intel SHA Extensions | ``sha1_x86`` | 2.2.0 | +| | | | | +| | AVX2 + BMI2 | ``sha1_avx2`` | 3.9.0 | +| | | | | +| | SSSE3 | ``sha1_simd`` | 1.7.12 | ++-----------+--------------------------------------------+-------------------------+------------+ +| SHA-256 | Intel SHA Extensions | ``sha2_32_x86`` | 2.2.0 | +| | | | | +| | AVX2 + BMI2 | ``sha2_32_avx2`` | 3.8.0 | +| | | | | +| | SSSE3 | ``sha2_32_simd`` | 3.8.0 | ++-----------+--------------------------------------------+-------------------------+------------+ +| SHA-512 | Intel SHA Extensions | ``sha2_64_x86`` | 3.8.0 | +| | | | | +| | AVX-512 + BMI2 | ``sha2_64_avx512`` | 3.8.0 | +| | | | | +| | AVX2 + BMI2 | ``sha2_64_avx2`` | 3.8.0 | ++-----------+--------------------------------------------+-------------------------+------------+ +| SHA-3 / | BMI2 | ``keccak_perm_bmi2`` | 2.10.0 | +| SHAKE / | | | | +| KMAC | AVX-512 | ``keccak_perm_avx512`` | 3.11.0 | ++-----------+--------------------------------------------+-------------------------+------------+ +| SM3 | AVX2 + BMI2 | ``sm3_avx2_bmi2`` | 3.11.0 | +| | | | | +| | SM3-NI | ``sm3_x86`` | 3.11.0 | ++-----------+--------------------------------------------+-------------------------+------------+ +| SM4 | AVX-512 + GFNI | ``sm4_avx512`` | 3.11.0 | +| | | | | +| | AVX2 + GFNI | ``sm4_gfni`` | 3.6.0 | +| | | | | +| | SM4-NI | ``sm4_x86`` | 3.8.0 | +| | | | | +| | AES-NI | ``sm4_hwaes`` | 3.11.1 | ++-----------+--------------------------------------------+-------------------------+------------+ +| Twofish | AVX-512 + GFNI | ``twofish_avx512`` | 3.11.1 | ++-----------+--------------------------------------------+-------------------------+------------+ +| Whirlpool | AVX-512 | ``whirlpool_avx512`` | 3.11.1 | +| | | | | +| | AVX2 | ``whirlpool_avx2`` | 3.11.1 | ++-----------+--------------------------------------------+-------------------------+------------+ +| XTS | AVX-512 + CLMUL | ``xts_avx512_clmul`` | 3.11.0 | ++-----------+--------------------------------------------+-------------------------+------------+ +| ZFEC | SSSE3 | ``zfec_vperm`` | 3.0.0 | ++-----------+--------------------------------------------+-------------------------+------------+ + +ARM +-------------- + +On ARM platforms, the following CPU specific optimizations are available. + +.. note:: + + The ARMv8 cryptography extensions are only used on 64-bit aarch64 systems +-----------+--------------------------------------------+--------------------+------------+ | Algorithm | Extension | Module | Added in | +===========+============================================+====================+============+ -| AES | VAES-AVX2 | `aes_vaes` | 3.6.0 | -| | | | | -| | AES-NI | `aes_ni` | 1.9.3 | +| AES | ARMv8 Cryptography Extensions | ``aes_armv8`` | 2.3.0 | | | | | | -| | SSSE3 | `aes_vperm` | 1.9.10 | +| | NEON | ``aes_vperm`` | 2.12.0 | +-----------+--------------------------------------------+--------------------+------------+ -| AES-GCM | CLMUL | `ghash_cpu` | 1.11.6 | -| | | | | -| | SSSE3 | `ghash_vperm` | 1.9.10 | -+-----------+--------------------------------------------+--------------------+------------+ -| Argon2 | AVX2 | `argon2_avx2` | 3.0.0 | -| | | | | -| | SSSE3 | `argon2_ssse3` | 2.19.2 | +| AES-GCM | ARMv8 Cryptography Extensions | ``ghash_cpu`` | 2.3.0 | +-----------+--------------------------------------------+--------------------+------------+ -| ChaCha | AVX512 (x86-64 only) | `chacha_avx512` | 3.1.0 | -| | | | | -| | AVX2 | `chacha_avx2` | 2.8.0 | -| | | | | -| | SSE2 | `chacha_simd32` | 1.11.32 | +| ARIA | ARMv8 Cryptography Extensions | ``aria_hwaes`` | 3.11.1 | +-----------+--------------------------------------------+--------------------+------------+ -| IDEA | SSE2 | `idea_sse2` | 1.9.4 | +| Camellia | ARMv8 Cryptography Extensions | ``camellia_hwaes`` | 3.11.1 | +-----------+--------------------------------------------+--------------------+------------+ -| KMAC | BMI2 | `keccak_perm_bmi2` | 3.2.0 | +| ChaCha | NEON | ``chacha_simd32`` | 2.8.0 | +-----------+--------------------------------------------+--------------------+------------+ -| NOEKEON | SSE2 | `noekeon_simd` | 1.9.4 | +| NOEKEON | NEON | ``noekeon_simd`` | 1.9.4 | +-----------+--------------------------------------------+--------------------+------------+ -| RDRAND | RDRAND | `processor_rng` | 1.11.31 | +| SEED | ARMv8 Cryptography Extensions | ``seed_hwaes`` | 3.11.1 | +-----------+--------------------------------------------+--------------------+------------+ -| RDSEED | RDSEED | `rdseed` | 1.11.36 | +| Serpent | NEON | ``serpent_simd`` | 1.9.2 | +-----------+--------------------------------------------+--------------------+------------+ -| Serpent | AVX512 (x86-64 only) | `serpent_avx512` | 3.1.0 | +| SHACAL2 | NEON | ``shacal2_simd`` | 2.3.0 | | | | | | -| | AVX2 | `serpent_avx2` | 2.8.0 | +| | ARMv8 Cryptography Extensions | ``shacal2_armv8`` | 2.13.0 | ++-----------+--------------------------------------------+--------------------+------------+ +| SHA-1 | ARMv8 Cryptography Extensions | ``sha1_armv8`` | 2.2.0 | | | | | | -| | SSE2 | `serpent_simd` | 1.9.0 | +| | NEON | ``sha1_simd`` | 3.8.0 | +-----------+--------------------------------------------+--------------------+------------+ -| SHACAL2 | Intel SHA Extensions | `shacal2_x86` | 2.3.0 | +| SHA-256 | ARMv8 Cryptography Extensions | ``sha2_32_armv8`` | 2.2.0 | | | | | | -| | AVX2 | `shacal2_avx2` | 2.13.0 | +| | NEON | ``sha2_32_simd`` | 3.8.0 | +-----------+--------------------------------------------+--------------------+------------+ -| SHAKE | BMI2 | `keccak_perm_bmi2` | 2.13.0 | +| SHA-384 | ARMv8 Cryptography Extensions | ``sha2_64_armv8`` | 3.3.0 | +-----------+--------------------------------------------+--------------------+------------+ -| SHA-1 | Intel SHA Extensions | `sha1_x86` | 2.2.0 | -| | | | | -| | SSE2 | `sha1_sse2` | 1.7.12 | +| SHA-512 | ARMv8 Cryptography Extensions | ``sha2_64_armv8`` | 3.3.0 | +-----------+--------------------------------------------+--------------------+------------+ -| SHA-256 | Intel SHA Extensions | `sha2_32_x86` | 2.2.0 | -| | | | | -| | BMI2 | `sha2_32_bmi2` | 2.7.0 | +| SM3 | ARMv8 Cryptography Extensions | ``sm3_armv8`` | 3.11.0 | +-----------+--------------------------------------------+--------------------+------------+ -| SHA-3 | BMI2 | `keccak_perm_bmi2` | 2.10.0 | +| SM4 | ARMv8 Cryptography Extensions | ``sm4_armv8`` | 2.8.0 | +| | | | | +| | ARMv8 Cryptography Extensions | ``sm4_hwaes`` | 3.11.1 | +-----------+--------------------------------------------+--------------------+------------+ -| SM4 | GFNI | `sm4_gfni` | 3.6.0 | +| ZFEC | NEON | ``zfec_vperm`` | 3.0.0 | +-----------+--------------------------------------------+--------------------+------------+ -ARM +POWER/PowerPC -------------- -On arm64 and arm32 platforms, the following CPU specific optimizations are available: +On 64-bit POWER/PowerPC platforms, the following CPU specific optimizations are available: +-----------+--------------------------------------------+--------------------+------------+ | Algorithm | Extension | Module | Added in | +===========+============================================+====================+============+ -| AES | NEON | `aes_armv8` | 1.9.3 | -+-----------+--------------------------------------------+--------------------+------------+ -| AES-GCM | PMULL (arm64 only) | `ghash_cpu` | 2.3.0 | +| AES | POWER8/POWER9 | ``aes_power8`` | 2.14.0 | | | | | | -| | NEON | `ghash_vperm` | 2.12.0 | +| | AltiVec | ``aes_vperm`` | 2.12.0 | +-----------+--------------------------------------------+--------------------+------------+ -| ChaCha | NEON | `chacha_simd32` | 2.8.0 | +| ChaCha | AltiVec | ``chacha_simd32`` | 2.8.0 | +-----------+--------------------------------------------+--------------------+------------+ -| NOEKEON | NEON | `noekeon_simd` | 1.9.4 | +| DARN | POWER9 | ``processor_rng`` | 2.15.0 | +-----------+--------------------------------------------+--------------------+------------+ -| Serpent | NEON | `serpent_simd` | 1.9.2 | +| Serpent | AltiVec | ``serpent_simd`` | 1.9.2 | +-----------+--------------------------------------------+--------------------+------------+ -| SHACAL2 | NEON | `shacal2_simd` | 2.3.0 | -| | | | | -| | ARMv8 Cryptography Extensions (arm64 only) | `shacal2_armv8` | 2.13.0 | +| SHACAL2 | AltiVec | ``shacal2_simd`` | 2.3.0 | ++-----------+--------------------------------------------+--------------------+------------+ +| NOEKEON | AltiVec | ``noekeon_simd`` | 1.9.4 | ++-----------+--------------------------------------------+--------------------+------------+ + +Loongarch64 +-------------- + +On loongarch64, the LSX extensions are used. + +.. note:: + + Loongarch64 apparently supports a "crypto" extension, for which hwcaps exist + for Linux, and there are shipping processors which do support these + extensions. However no documentation has been so far located. If you are + aware of any such documentation please do contact the maintainers. + +-----------+--------------------------------------------+--------------------+------------+ -| SHA-1 | ARMv8 Cryptography Extensions (arm64 only) | `sha1_armv8` | 2.2.0 | +| Algorithm | Extension | Module | Added in | ++===========+============================================+====================+============+ +| AES | LSX | ``aes_vperm`` | 3.8.0 | ++-----------+--------------------------------------------+--------------------+------------+ +| ChaCha | LSX | ``chacha_simd32`` | 3.8.0 | +-----------+--------------------------------------------+--------------------+------------+ -| SHA-256 | ARMv8 Cryptography Extensions (arm64 only) | `sha2_32_armv8` | 2.2.0 | +| Serpent | LSX | ``serpent_simd`` | 3.8.0 | +-----------+--------------------------------------------+--------------------+------------+ -| SHA-384 | ARMv8 Cryptography Extensions (arm64 only) | `sha2_64_armv8` | 3.3.0 | +| SHA-1 | LSX | ``sha1_simd`` | 3.8.0 | +-----------+--------------------------------------------+--------------------+------------+ -| SHA-512 | ARMv8 Cryptography Extensions (arm64 only) | `sha2_64_armv8` | 3.3.0 | +| SHACAL2 | LSX | ``shacal2_simd`` | 3.8.0 | +-----------+--------------------------------------------+--------------------+------------+ -| SM4 | ARMv8 Cryptography Extensions (arm64 only) | `sm4_armv8` | 2.8.0 | +| NOEKEON | LSX | ``noekeon_simd`` | 3.8.0 | ++-----------+--------------------------------------------+--------------------+------------+ +| ZFEC | LSX | ``zfec_vperm`` | 3.8.0 | +-----------+--------------------------------------------+--------------------+------------+ -PowerPC +Wasm -------------- -On ppc64 and ppc32 platforms, the following CPU specific optimizations are available: +On Wasm, the SIMD128 extension is used. + +.. note:: + + To make use of SIMD128, ````simd128`` compilation flag is required. +-----------+--------------------------------------------+--------------------+------------+ | Algorithm | Extension | Module | Added in | +===========+============================================+====================+============+ -| AES | POWER8/POWER9 | `aes_power8` | 2.14.0 | -| | | | | -| | AltiVec | `aes_vperm` | 2.12.0 | +| AES | SIMD128 | ``aes_vperm`` | 3.11.0 | ++-----------+--------------------------------------------+--------------------+------------+ +| AES-GCM | SIMD128 | ``ghash_vperm`` | 3.11.0 | ++-----------+--------------------------------------------+--------------------+------------+ +| Argon2 | SIMD128 | ``argon2_simd64`` | 3.11.0 | ++-----------+--------------------------------------------+--------------------+------------+ +| ChaCha | SIMD128 | ``chacha_simd32`` | 3.11.0 | +-----------+--------------------------------------------+--------------------+------------+ -| AES-GCM | AltiVec | `ghash_vperm` | 2.12.0 | +| Serpent | SIMD128 | ``serpent_simd`` | 3.11.0 | +-----------+--------------------------------------------+--------------------+------------+ -| ChaCha | AltiVec | `chacha_simd32` | 2.8.0 | +| SHA-1 | SIMD128 | ``sha1_simd`` | 3.11.0 | +-----------+--------------------------------------------+--------------------+------------+ -| DARN | POWER9 | `processor_rng` | 2.15.0 | +| SHA-256 | SIMD128 | ``sha2_32_simd`` | 3.11.0 | +-----------+--------------------------------------------+--------------------+------------+ -| Serpent | AltiVec | `serpent_simd` | 1.9.2 | +| SHACAL2 | SIMD128 | ``shacal2_simd`` | 3.11.0 | +-----------+--------------------------------------------+--------------------+------------+ -| SHACAL2 | AltiVec | `shacal2_simd` | 2.3.0 | +| NOEKEON | SIMD128 | ``noekeon_simd`` | 3.11.0 | +-----------+--------------------------------------------+--------------------+------------+ -| NOEKEON | AltiVec | `noekeon_simd` | 1.9.4 | +| ZFEC | SIMD128 | ``zfec_vperm`` | 3.11.0 | +-----------+--------------------------------------------+--------------------+------------+ Configuring Acceleration ------------------------------ -Hardware acceleration can be disabled at during configuring the build -by passing certain ``--disable-*`` options to ``configure.py``. -This will cause the base software implementation to be used instead -of the hardware accelerated one. The following options are currently supported: - -``--disable-sse2`` - disable SSE2 intrinsics -``--disable-ssse3`` - disable SSSE3 intrinsics -``--disable-sse4.1`` - disable SSE4.1 intrinsics -``--disable-sse4.2`` - disable SSE4.2 intrinsics -``--disable-avx2`` - disable AVX2 intrinsics -``--disable-bmi2`` - disable BMI2 intrinsics -``--disable-rdrand`` - disable RDRAND intrinsics -``--disable-rdseed`` - disable RDSEED intrinsics -``--disable-aes-ni`` - disable AES-NI intrinsics -``--disable-sha-ni`` - disable SHA-NI intrinsics -``--disable-altivec`` - disable AltiVec intrinsics -``--disable-neon`` - disable NEON intrinsics -``--disable-armv8crypto`` - disable ARMv8 Crypto intrinsics -``--disable-powercrypto`` - disable POWER Crypto intrinsics - -Additionally, ``--disable-modules=MODS`` can be used to remove a certain module, -if desirable. - -Last but not least, the ``BOTAN_CLEAR_CPUID`` :doc:`environment variable ` -can be set to a non-empty value *at runtime* to cause Botan to clear the CPUID bits for the CPU -extensions it uses. +If it is desirable to avoid using some form of acceleration, this can be accomplished +*at build time* by using ``--disable-modules=``. For instance, to remove support +of ARMv8 intrinsics for AES, use ``--disable-modules=aes_armv8``. Note that this is rarely +if ever required; if support for the CPU extension is not available at runtime then the +code using that extension will simply be skipped over. The only reason to do this is when +the code is being deployed to a fixed target (eg the specific board used in your product) +and you know that target does not support such an extension, and you wish to minimize code size. + +It is also possible to disable acceleration *at runtime* using +``BOTAN_CLEAR_CPUID`` :doc:`environment variable `. This is the preferred +mode of disabling acceleration. diff -Nru botan3-3.7.1+dfsg/doc/migration_guide.rst botan3-3.12.0+dfsg/doc/migration_guide.rst --- botan3-3.7.1+dfsg/doc/migration_guide.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/migration_guide.rst 2026-05-07 01:38:28.000000000 +0000 @@ -45,7 +45,7 @@ Starting with Botan 3.0 TLS 1.3 is supported. This development required a number of backward-incompatible changes to -accomodate the protocol differences to TLS 1.2, which is still supported. +accommodate the protocol differences to TLS 1.2, which is still supported. Build modules ^^^^^^^^^^^^^ @@ -201,7 +201,7 @@ self-contained encrypted and authenticated tickets) and stateful (identified with unique database handles). -To accomodates this flexibility the `Session_Manager` base class API has changed +To accommodate this flexibility the `Session_Manager` base class API has changed drastically and is now responsible for creation, storage and management of both stateful sessions and stateless session tickets. Sub-classes therefore gain full control over the session ticket's structure and @@ -453,5 +453,5 @@ Applications that rely on a static seed for deterministic RNG output might observe a different byte stream in such cases. As a workaround, users are -advised to "mimick" the legacy behaviour by manually pulling from the RNG in +advised to "mimic" the legacy behaviour by manually pulling from the RNG in "byte limit"-sized chunks and provide the "input" with each invocation. diff -Nru botan3-3.7.1+dfsg/doc/news_2x.rst botan3-3.12.0+dfsg/doc/news_2x.rst --- botan3-3.7.1+dfsg/doc/news_2x.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/news_2x.rst 2026-05-07 01:38:28.000000000 +0000 @@ -1817,8 +1817,8 @@ using ``--disable-modules=pkcs11`` (GH #837) * Add ``OS::run_cpu_instruction_probe`` for runtime probing of ISA extensions. - Supporting this requires system-specific techniques, currently Windows SEH and - Unix signal handling are supported. + Supporting this requires system-specific techniques, currently Windows Structured + Exception Handling and Unix signal handling are supported. * Add support for ARM NEON in the SIMD_4x32 type diff -Nru botan3-3.7.1+dfsg/doc/old_news.rst botan3-3.12.0+dfsg/doc/old_news.rst --- botan3-3.7.1+dfsg/doc/old_news.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/old_news.rst 2026-05-07 01:38:28.000000000 +0000 @@ -82,7 +82,7 @@ * Fix TLS session resumption bugs which caused resumption failures if an application used a single session cache for both TLS and DTLS. (GH #688) -* Add SHAKE-128 and SHAKE-256 XOFs as hash functions supporting paramaterized +* Add SHAKE-128 and SHAKE-256 XOFs as hash functions supporting parameterized output lengths. * Add MessageAuthenticationCode::start_msg interface, for MACs which require or @@ -621,7 +621,7 @@ * Add support for processing X.509 name constraint extension during path validation. GH #454 -* Add X509_Certificate::v3_extensions which allows retreiving the +* Add X509_Certificate::v3_extensions which allows retrieving the raw binary of all certificate extensions, including those which are not known to the library. This allows processing of custom extensions. GH #437 @@ -638,7 +638,7 @@ * SRP6 support is now optional in TLS * Support for negotiating MD5 and SHA-224 signatures in TLS v1.2 has - been removed. MD5 signatures are demonstratably insecure in TLS, + been removed. MD5 signatures are demonstrably insecure in TLS, SHA-224 is rarely used. * Support for negotiating ECC curves secp160r1, secp160r2, secp160k1, @@ -689,7 +689,7 @@ the library itself. GH #430 * Remove use of TickCount64 introduced in 1.11.27 which caused problem - with downstream distributors/users building XP compatiable binaries + with downstream distributors/users building XP compatible binaries which is still an option even in VS 2015 * MCEIES requires KDF1 at runtime but did not require it be enabled @@ -767,7 +767,7 @@ * Use TickCount64 and MemoryStatusEx in the Windows entropy source. Note these calls are only available in Vista/Server 2008. No - accomodations are made for XP or Server 2003, both of which are + accommodations are made for XP or Server 2003, both of which are no longer patched by the vendor. GH #365 Version 1.11.26, 2016-01-04 @@ -796,7 +796,7 @@ extended with new features and options. * Correct an error in PointGFp multiplication when multiplying a point - by the scalar value 3. PointGFp::operator* would instead erronously + by the scalar value 3. PointGFp::operator* would instead erroneously compute it as if the scalar was 1 instead. * Enable RdRand entropy source on Windows/MSVC. GH #364 @@ -855,7 +855,7 @@ * Work around a problem with some antivirus programs which causes the ``shutil.rmtree`` and ``os.makedirs`` Python calls to occasionally - fail. The could prevent ``configure.py`` from running sucessfully + fail. The could prevent ``configure.py`` from running successfully on such systems. GH #353 * Let ``configure.py`` run under CPython 2.6. GH #362 @@ -893,7 +893,7 @@ * Fixed the signature of the FFI function botan_pubkey_destroy, which took the wrong type and was not usable. -* The TLS client would erronously reject any server key exchange packet smaller +* The TLS client would erroneously reject any server key exchange packet smaller than 6 bytes. This prevented negotiating a plain PSK TLS ciphersuite with an empty identity hint. ECDHE_PSK and DHE_PSK suites were not affected. @@ -922,7 +922,7 @@ even when using a deterministic PRNG with the same seed. * In `configure,py`, the flags for controlling use of debug, sanitizer, and - converage information have been split out into individual options + coverage information have been split out into individual options `--with-debug-info`, `--with-sanitizers`, and `--with-coverage`. These allow enabling more than one in a build in a controlled way. The `--build-mode` flag added in 1.11.17 has been removed. @@ -1147,7 +1147,7 @@ create a pointer offset of a ``std::vector``. This failed when x was set equal to ``vec.size()`` to create the one-past-the-end address. The pointer in question was never dereferenced, but it triggered - the iterator debugging checks which prevented using these valuble + the iterator debugging checks which prevented using these valuable analysis tools. From Simon Warta and Daniel Seither. GH #125 * Several incorrect or missing module dependencies have been fixed. These @@ -1207,7 +1207,7 @@ * Added global timeout to HMAC_RNG entropy reseed. The defaults are the values set in the build.h macros ``BOTAN_RNG_AUTO_RESEED_TIMEOUT`` - and ``BOTAN_RNG_RESEED_DEFAULT_TIMEOUT``, but can be overriden + and ``BOTAN_RNG_RESEED_DEFAULT_TIMEOUT``, but can be overridden on a specific poll with the new API call reseed_with_timeout. * Fixed Python cipher update_granularity() and default_nonce_length() @@ -1343,7 +1343,7 @@ * Add SHA-512/256 -* The format of serialized TLS sessions has changed. Additiionally, PEM +* The format of serialized TLS sessions has changed. Additionally, PEM formatted sessions now use the label of "TLS SESSION" instead of "SSL SESSION" * Serialized TLS sessions are now encrypted using AES-256/GCM instead of a @@ -1469,7 +1469,7 @@ * Fixed a bug in CCM mode which caused it to produce incorrect tags when used with a value of L other than 2. This affected CCM TLS ciphersuites, which - use L=3. Thanks to Manuel Pégourié-Gonnard for the anaylsis and patch. + use L=3. Thanks to Manuel Pégourié-Gonnard for the analysis and patch. Bugzilla 270. * DTLS now supports timeouts and handshake retransmits. Timeout checking @@ -1648,7 +1648,7 @@ ``bcrypt``, ``keygen``, ``speed``, and various others. As part of this change many obsolete, duplicated, or one-off examples were removed, while others were extended with new functionality. Contributions of - new subcommands, new bling for exising ones, or documentation in any + new subcommands, new bling for existing ones, or documentation in any form is welcome. * Fix a bug in Lion, which was broken by a change in 1.11.0. The @@ -1722,7 +1722,7 @@ name for the EGD socket. Found by Coverity Scanner. * In PK_Encryptor_EME, PK_Decryptor_EME, PK_Verifier, and PK_Key_Agreement, - avoid dereferencing an unitialized pointer if no engine supported operations + avoid dereferencing an uninitialized pointer if no engine supported operations on the key object given. Found by Coverity scanner. * Avoid leaking a file descriptor in the /dev/random and EGD entropy sources if @@ -2317,7 +2317,7 @@ ``BOTAN_TARGET_ARCH_IS_X86_32``. The classes calling assembly have also been renamed. -* Similiarly to the above change, the AES implemenations using the +* Similarly to the above change, the AES implementations using the AES-NI instruction set have been renamed from AES_XXX_Intel to AES_XXX_NI. @@ -2423,7 +2423,7 @@ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GOST 34.10 signatures were being formatted in a way that was not -compatible with other implemenations, and specifically how GOST is +compatible with other implementations, and specifically how GOST is used in DNSSEC. The Keccak hash function was updated to the tweaked variant proposed @@ -2870,7 +2870,7 @@ Version 1.7.21, 2008-11-11 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -* Make algorithm lookup much more configuable +* Make algorithm lookup much more configurable * Add facilities for runtime performance testing of algorithms * Drop use of entropy estimation in the PRNGs * Increase intervals between HMAC_RNG automatic reseeding @@ -3137,7 +3137,7 @@ Version 1.6.2, 2007-03-24 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -* Fix autodection on Athlon64s running Linux +* Fix autodetection on Athlon64s running Linux * Fix builds on QNX and compilers using STLport * Remove a call to abort() that crept into production @@ -3192,7 +3192,7 @@ * Initialization failures are dealt with somewhat better * Add an example implementing Pollard's Rho algorithm * Better option handling in the test/benchmark tool -* Expand the xor_ciph example to support longer keys +* Expand the example of how to add a stream cipher to support longer keys * Some updates to the documentation Version 1.5.9, 2006-07-12 @@ -3626,7 +3626,7 @@ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Major improvements in ASN.1 string handling -* Added partial support for ASN.1 UTF8 STRINGs and BMP STRINGs +* Added partial support for ASN.1 UTF8 STRING and BMP STRING types * Added partial support for the X.509v3 certificate policies extension * Centralized the handling of character set information * Added FIPS 140-2 startup self tests @@ -3830,7 +3830,7 @@ * Renamed Rijndael to AES, created aes.h, deleted rijndael.h * Removed support for the 'no_timer' LibraryInitializer option * Removed 'es_pthr' module, pending further testing -* Cleaned up get_ciph.cpp +* Cleaned up cipher factory Version 1.1.12, 2003-04-15 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -3910,7 +3910,7 @@ * Initial support for X.509v3 certificates and CAs * Major redesign/rewrite of the ASN.1 encoding/decoding code -* Added handling for DSA/NR signatures encoded as DER SEQUENCEs +* Added handling for DSA/NR signatures encoded as a DER SEQUENCE * Documented the generic cipher lookup interface * Added an (untested) entropy source for BeOS * Various cleanups and bug fixes @@ -4215,7 +4215,7 @@ * Added ECB and CTS block cipher modes (ecb.h, cts.h) * Added a Mutex interface (mutex.h) * Added module pthr_mux, implementing the Mutex interface -* Added Threaded Filter interface (thr_filt.h) +* Added Threaded Filter interface * All algorithms can now by keyed with SymmetricKey objects * More testing occurs with --validate (expected failures) * Fixed two bugs reported by Hany Greiss, in Luby-Rackoff and RC6 diff -Nru botan3-3.7.1+dfsg/doc/openssl_migration_guide.rst botan3-3.12.0+dfsg/doc/openssl_migration_guide.rst --- botan3-3.7.1+dfsg/doc/openssl_migration_guide.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/openssl_migration_guide.rst 2026-05-07 01:38:28.000000000 +0000 @@ -457,7 +457,7 @@ :language: cpp This example uses the ``PK_Signer`` and ``PK_Verifier`` classes to sign and verify -a message using :ref:`api_ref/pubkey:ecdsa`. The private key is similary +a message using :ref:`api_ref/pubkey:ecdsa`. The private key is similarly :ref:`loaded from a file `. The :doc:`hash function ` is passed as a string parameter. ``PK_Verifier::check_signature()`` is used to diff -Nru botan3-3.7.1+dfsg/doc/packaging.rst botan3-3.12.0+dfsg/doc/packaging.rst --- botan3-3.7.1+dfsg/doc/packaging.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/packaging.rst 2026-05-07 01:38:28.000000000 +0000 @@ -38,11 +38,15 @@ ----------------- Starting in Botan 3.3.0, we ship ``botan-config.cmake`` files. While this config -file is somewhat relocatable, it assumes the default installation directory -structure as generated by ``make install``. If your distribution changes the -directory layout of the installed files you might want to either adapt the final -``botan-config.cmake`` file accordingly or leave it out entirely using -``--without-cmake-config``. +file is somewhat relocatable, it assumes that the installation directory layout +as generated by ``make install`` remains unchanged after installation. If your +distribution changes the directory layout of the installed files you might want +to either adapt the final ``botan-config.cmake`` file accordingly or leave it +out entirely using ``--without-cmake-config``. + +Note that you may change the installed location of these files using the +``--cmakeconfigdir`` option at configure time. However, the location must be a +subdirectory of the install prefix. Please don't hesitate to give your feedback on this new feature by opening a ticket on the upstream GitHub. diff -Nru botan3-3.7.1+dfsg/doc/roadmap.rst botan3-3.12.0+dfsg/doc/roadmap.rst --- botan3-3.7.1+dfsg/doc/roadmap.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/roadmap.rst 2026-05-07 01:38:28.000000000 +0000 @@ -5,37 +5,40 @@ Near Term Plans ---------------------------------------- -Here is an outline of the development plans over the next ~12 months, -as of December 2024. +Here is an outline of the development plans over the next ~12 months, as of +February 2026. Botan2 --------------- -Botan2 is still supported, but no further feature work is planned. -Only security issues and serious bugs will be addressed. - -Currently, Botan2 is scheduled to reach end of life at the end of 2024. +As of 2025-01-01, Botan2 has reached end of life. No further releases are planned. Botan3 --------------- -The following future work is currently planned for Botan3: - -* New ECC based password authenticated key exchanges, to replace SRP. - The most likely candidate algorithms are SPAKE2(+) and CPace. - -* Adding an implementation of BLS12-381 elliptic curve pairing. +The following major feature work is currently planned for Botan3: +* SPAKE2+ password authenticated key exchange +* BLS12-381 * HPKE (RFC 9180) +* XMSS^MT +* HQC, possibly implemented using Rust + +Along with the usual optimizations, bug fixes, and refinements. Botan4 --------------- -At this time there is no immediate plan for a new major version. When it occurs, -it will remove functionality currently marked as deprecated, and adopt a new C++ -version. This is unlikely to occur before 2027, at the earliest. +Botan4 is currently planned for release in 2027. + +See the current planning discussion in https://github.com/randombit/botan/issues/4666 + +Botan4 will continue using C++20 rather than adopting a more recent language version. + +Botan4 is expected to be largely a subtractive major release; +deprecated APIs and functionality will be removed, with few additions. -One major change already planned for Botan4 is that in that release, Public_Key +One notable change planned for Botan4 is that in that release, Public_Key will no longer derive from Private_Key. And similarly, specific private keys (for example RSA_PrivateKey) will no longer derive from their corresponding public key type. diff -Nru botan3-3.7.1+dfsg/doc/security.rst botan3-3.12.0+dfsg/doc/security.rst --- botan3-3.7.1+dfsg/doc/security.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/security.rst 2026-05-07 01:38:28.000000000 +0000 @@ -15,6 +15,79 @@ This key can be found in the file ``doc/pgpkey.txt`` or online at https://keybase.io/jacklloyd and on most PGP keyservers. +2026 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* 2026-05-06 (CVE-2026-44378): BER decoding denial of service + + Certain patterns of indefinite length encodings in BER data could cause + quadratic behavior in the parser, resulting in a denial of service. Up until + Botan 3.12.0, such BER encodings were accepted even in structures which are + required to be encoded as DER. Any party able to transmit any ASN.1 encoded + data, such as a certificate or OCSP response, can induce CPU based denial of + service. + + Fixed in 3.12.0, all prior versions affected + + Credit: yt3 + +* 2026-03-31 (CVE-2026-34582): TLS 1.3 client authentication bypass + + The TLS 1.3 implementation allowed ApplicationData records to be processed + prior to the Finished message being received. A server which is attempting to + enforce client authentication via certificates can by bypassed by a client + which entirely omits Certificate, CertificateVerify, and the Finished message + and instead sends application data records. + + Introduced in 3.0.0, fixed in 3.11.1 + + Credit: Ben Smyth + +* 2026-03-31 (CVE-2026-34580): Certificate verification bypass due to trust anchor confusion + + During path validation, an end-entity certificate whose DN collided with the + DN of a trust anchor would be accepted immediately without further validation. + This bug was introduced in 3.11.0; prior versions are not affected. + + Introduced in 3.11.0, fixed in 3.11.1 + + Credit: Nicholas Carlini with Claude, Anthropic + +* 2026-03-15 (CVE-2026-32883): OCSP Response Forgery + + During verification of X.509 paths involving OCSP responses, Botan omitted checking + that the response signature was itself valid. This would allow a MitM attacker to + insert forged responses. It would also allow a malicious TLS server to staple + forged OCSP responses. + + Introduced in 3.0.0, fixed in 3.11.0 + + Found by Haruto Kimura + +* 2026-03-15 (CVE-2026-32877): Heap Overread During SM2 Decryption + + Decryption of SM2 ciphertexts failed to account for the possibility that the enclosed + MAC was of an invalid length. An invalid ciphertext with a MAC of the wrong length would + cause a heap over-read when the computed MAC value was compared with the insufficiently + sized buffer. This could result in denial of service. + + Introduced in 2.3.0, fixed in 3.11.0 + + Found by Haruto Kimura + +* 2026-03-15 (CVE-2026-32884): Bypass of Name Constraint Exclusion in CN Fallback Case + + If DNS name constraints apply to a certificate, and the certificate does not + contain any Subject Alternative Name extension, Botan checks that the certificates + commonName field (CN) would not be prohibited by the name constraint. However it + failed to account for the possibility that the CN might be mixed case; a certificate + with a mixed case CN and omitted SAN would be accepted even if the DNS name in the + CN violated a name constraint imposed by the issuing chain. + + Introduced in 2.0.0, fixed in 3.11.0 + + Found by Haruto Kimura + 2024 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff -Nru botan3-3.7.1+dfsg/doc/sem_ver.rst botan3-3.12.0+dfsg/doc/sem_ver.rst --- botan3-3.7.1+dfsg/doc/sem_ver.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/sem_ver.rst 2026-05-07 01:38:28.000000000 +0000 @@ -1,7 +1,7 @@ Semantic Versioning ===================== -Starting with 2.0.0, Botan adopted semantic versioning. This means we endevour +Starting with 2.0.0, Botan adopted semantic versioning. This means we endeavour to make no change which will either break compilation of existing code, or cause different behavior in a way that will cause compatibility issues. Such changes are reserved for new major versions. diff -Nru botan3-3.7.1+dfsg/doc/side_channels.rst botan3-3.12.0+dfsg/doc/side_channels.rst --- botan3-3.7.1+dfsg/doc/side_channels.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/side_channels.rst 2026-05-07 01:38:28.000000000 +0000 @@ -1,3 +1,6 @@ + +.. _side_channels: + Side Channels ========================= @@ -78,7 +81,7 @@ For general ``e``, the inversion proceeds using a technique based on the CRT - ``phi(n)`` is factored to ``2**k * o`` for some ``k`` > 1 and some odd ``o``. Then ``e`` is inverted modulo ``2**k`` and also modulo ``o``. The -inversion modulo ``2**k`` is done via a specialized constant-time algoirthm +inversion modulo ``2**k`` is done via a specialized constant-time algorithm which only works for powers of 2. Then the two inversions are combined using the CRT. This process does leak the value of ``k``; when generating keys Botan chooses ``p`` and ``q`` so that ``k`` is always 1. @@ -169,7 +172,7 @@ -------------------------- Several elliptic curve scalar multiplication algorithms are implemented to -accomodate different use cases. The implementations can be found in +accommodate different use cases. The implementations can be found in pcurves_impl.h as PrecomputedBaseMulTable, WindowedMulTable, and WindowedMul2Table. @@ -271,7 +274,7 @@ On all other processors, a constant time bitsliced implementation is used. This is typically slower than the vector permute implementation, and additionally for -best performance multiple blocks must be processed in parellel. So modes such +best performance multiple blocks must be processed in parallel. So modes such as CTR, GCM or XTS are relatively fast, but others such as CBC encryption suffer. @@ -314,7 +317,8 @@ This algorithm uses table lookups with secret sboxes. No cache-based side channel attack on Twofish has ever been published, but it is possible nobody -sufficiently skilled has ever tried. +sufficiently skilled has ever tried. There is also an AVX-512 implementation +which avoids table lookups completely. ChaCha20, Serpent, Threefish, ... ----------------------------------- @@ -354,14 +358,22 @@ trick to zero out an array. If possible an OS provided routine (such as ``RtlSecureZeroMemory`` or ``explicit_bzero``) is used. -On other platforms, by default the trick of referencing memset through a +On other platforms, the trick of referencing memset through a volatile function pointer is used. This approach is not guaranteed to work on all platforms, and currently there is no systematic check of the resulting binary function that it is compiled as expected. But, it is the best approach currently known and has been verified to work as expected on common platforms. -If BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO is set to 0 in build.h (not the default) a -byte at a time loop through a volatile pointer is used to overwrite the array. +Stack Scrubbing +---------------------- + +GCC 14 and newer can emit code that scrubs the stack frames of functions that +handle sensitive information [GCCstrub] after they returned to the caller. This +can reduce the time window for sniffing sensitive information from a process. + +Botan can apply this to certain core routines of fundamental algorithms. For now +this feature is an opt-in. Configure with `--enable-stack-scrubbing` to benefit +from this feature if you are using a compatible version of GCC. Memory allocation ---------------------- @@ -435,7 +447,10 @@ [CoronDpa] Coron, "Resistance against Differential Power Analysis for Elliptic Curve Cryptosystems" -(https://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.1.5695) +(https://citeseerx.ist.psu.edu/document?doi=4d5d6dfdb582c0d695953e92c408f2377a6c9039) + +[GCCstrub] GCC Stack Scrubbing +(https://gcc.gnu.org/onlinedocs/gcc-14.2.0/gcc/Common-Type-Attributes.html#index-strub-type-attribute) [GcdFree] Joye, Paillier "GCD-Free Algorithms for Computing Modular Inverses" (https://marcjoye.github.io/papers/JP03gcdfree.pdf) @@ -452,11 +467,11 @@ elliptic-curve cryptography. (https://safecurves.cr.yp.to) [Lucky13] AlFardan, Paterson "Lucky Thirteen: Breaking the TLS and DTLS Record Protocols" -(http://www.isg.rhul.ac.uk/tls/TLStiming.pdf) +(https://www.isg.rhul.ac.uk/tls/Lucky13.html) [MillionMsg] Bleichenbacher "Chosen Ciphertext Attacks Against Protocols Based on the RSA Encryption Standard PKCS1" -(https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.19.8543) +(https://archiv.infsec.ethz.ch/education/fs08/secsem/bleichenbacher98.pdf) [MillionMsgTiming] Meyer, Somorovsky, Weiss, Schwenk, Schinzel, Tews: Revisiting SSL/TLS Implementations: New Bleichenbacher Side Channels and Attacks @@ -468,7 +483,7 @@ [RsaFault] Boneh, Demillo, Lipton "On the importance of checking cryptographic protocols for faults" -(https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.48.9764) +(https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&doi=7622200b9459a8c0e25e74ce7316c2402862e919) [RandomMonty] Le, Tan, Tunstall "Randomizing the Montgomery Powering Ladder" (https://eprint.iacr.org/2015/657) diff -Nru botan3-3.7.1+dfsg/doc/support.rst botan3-3.12.0+dfsg/doc/support.rst --- botan3-3.7.1+dfsg/doc/support.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/support.rst 2026-05-07 01:38:28.000000000 +0000 @@ -14,17 +14,13 @@ * Linux ppc64le, GCC 11.2 or later * Windows x86-64, Visual C++ 2022 or later -.. warning:: - - Starting in Botan 3.8, the minimum required version of Clang will change - to Clang 17. - These platforms are all tested by continuous integration, and the developers have access to hardware in order to test patches. Problems affecting these platforms are considered release blockers. For Botan 3, the tier-2 supported platforms are +* macOS aarch64, latest XCode Clang * macOS x86-64, latest XCode Clang * iOS aarch64, latest XCode Clang * Windows x86-64, latest MinGW GCC @@ -64,16 +60,16 @@ that platform. In theory any working C++20 compiler is fine but in practice, we only regularly -test with GCC, Clang, and Visual C++. Several other compilers (such as IBM XLC, -Intel C++, and Sun Studio) are supported by the build system but are not tested -by the developers and may have build or codegen problems. Patches to improve -support for these compilers is welcome. +test with GCC, Clang, and Visual C++. Several other compilers (such as IBM XLC +and Intel C++) are supported by the build system but are not tested by the +developers and may have build or codegen problems. Patches to improve support +for these compilers is welcome. Branch Support Status ------------------------- Following table provides the support status for Botan branches, as of -January 2025. +August 2025. "Active development" refers to adding new features and optimizations. At the conclusion of the active development phase, only bugfixes are applied. @@ -83,10 +79,9 @@ ============== ============== ========================== ============ Branch First Release End of Active Development End of Life ============== ============== ========================== ============ -Botan 1.8 2008-12-08 2010-08-31 2016-02-13 -Botan 1.10 2011-06-20 2012-07-10 2018-12-31 -Botan 2 2017-01-06 2020-11-05 2024-12-31 -Botan 3 2023-04-11 ? 2027-12-31 or later +Botan2 2017-01-06 2020-11-05 2024-12-31 +Botan3 2023-04-11 2027? 2028-12-31 or later +Botan4 2027? ? ? ============== ============== ========================== ============ Getting Help diff -Nru botan3-3.7.1+dfsg/doc/threat_model.rst botan3-3.12.0+dfsg/doc/threat_model.rst --- botan3-3.7.1+dfsg/doc/threat_model.rst 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/doc/threat_model.rst 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,66 @@ + +Threat Model +===================== + +It is somewhat difficult to fully articulate a threat model for any library since it may +be used in different contexts. However, this document attempts to clearly state which +attackers are considered in-scope (and thus which countermeasures are in place), and which +are not. + +The basic threat model Botan is written for is described well in "The Program Counter +Security Model" (Molnar, Piotrowski, Schultz, Wagner). + +We assume an attacker exists who is capable of colocating their attack code on the same +CPU (eg via SMT) and performing analysis based on side channels in cache, TLB or branch +predictor resources. A somewhat stronger model is in the context of SGX enclaves, where it +is practical for an attacker to cause code in an SGX enclave to single-step the execution +and precisely measure each conditional jump and memory access. + +This also covers the (weaker) threat model of an attacker on the same LAN who is +performing attacks based purely on timing of operations. + +Wherever possible, code that manipulates secret data (for example when generating an ECDSA +signature or decrypting an AES ciphertext) is written to be "constant time"; avoiding any +conditional jumps or memory accesses where the predicate is (derived from) secret +information. Botan uses extensive annotations (``CT::poison``) to indicate which values +are secret, and uses automated analysis (currently using ``valgrind`` similar to Adam +Langley's ``ctgrind`` idea, though support for other tools is welcome) to verify that the +assembly created by the compiler in fact avoids all conditional jumps or memory accesses +that might leak secrets. This testing step is essential as some compilers (notably Clang) +are excellent at performing range analysis of values and will sometimes generate +conditional jumps even when the code as written appears to avoid such operations. Botan's +CI runs these tests automatically against GCC and Clang on x86-64 and aarch64, with a +range of different optimization levels. + +Some algorithms have a structure which allows for very practical blinding/re-randomization +of the operations. This is used as an additional countermeasure in case some particular +combination of compiler, compiler options, and target architecture results in a +conditional jump being inserted in an unexpected place. For example during ECDSA signing, +the inversion of ``k``, the scalar multiplication of ``g*k`` and the recombination of +``x * r + m`` are all blinded, even though all of the relevant arithmetic operations are +written and tested to avoid side channels. + +For more about specific side channel countermeasures, see :ref:`side_channels`. + +Do keep in mind that side channels are intrinsically a property of the *hardware* computer +system which is executing the code. Thus while a variety of best-effort countermeasures +and analysis tools are in place, the absence of any kind of side channel cannot be +guaranteed by a software library on it's own. It can only be verified with a specific +compiled binary on a specific hardware platform. + +Out Of Scope +----------------- + +* Speculative execution attacks such as Spectre are out of scope since countermeasures are + incredibly costly, and there is currently no way to verify that any such countermeasures, + once applied, are effective. + +* Attacks based on ALU side channels (such as contention on the multiplication unit + leaking the Hamming weight of the multiplier) are currently out of scope, though + randomized blinding may be helpful in some circumstances. + +* Power analysis attacks and EM side channel attacks are considered out of scope. + Preventing these attacks requires hardware support and a system-wide view of how leakage + is handled. That said, blinding and rerandomization may provide some protection against + such attacks. Patches which make it easier to use Botan in a system which must address + these issues would be accepted. diff -Nru botan3-3.7.1+dfsg/license.txt botan3-3.12.0+dfsg/license.txt --- botan3-3.7.1+dfsg/license.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/license.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,4 @@ -Copyright (C) 1999-2025 The Botan Authors +Copyright (C) 1999-2026 The Botan Authors All rights reserved. Redistribution and use in source and binary forms, with or without diff -Nru botan3-3.7.1+dfsg/news.rst botan3-3.12.0+dfsg/news.rst --- botan3-3.7.1+dfsg/news.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/news.rst 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,485 @@ Release Notes ======================================== +Version 3.12.0, 2026-05-06 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* CVE-2026-44378: Resolve a CPU based denial of service when decoding + BER encoded data. + +* Optimize and improve certificate store search operations (GH #5510) + +* Require strict DER when decoding PKIX types such as certificates (#5521) + +* Discard TLS handshake state once the handshake has completed, retaining + only the data needed for the active connection (GH #5517) + +* Various TLS conformance, hardening, and performance fixes. (GH #5550 + #5551 #5568 #5555) + +* Various X509/PKIX/OCSP optimizations and bug fixes (GH #5535 #5536 #5546 #5554 + #5561 #5562 #5569) + +* Skip OCSP/CRL revocation checks on certificate chains which were already + going to be rejected due to path validation errors (GH #5512) + +* Add ``BER_Decoder::Limits`` which allows controlling what DER/BER syntax is + accepted while decoding. (GH #5507 #5514) + +* Add support for IPv6 name constraints in X.509 certificate path validation, + and add IPv6 address parsing and formatting utilities (GH #5534 #5537) + +* Refactor the Windows system certificate store and add a cache of materialized + certificates to avoid repeated parsing. (GH #5539) + +* Improve handling of unknown X.509 certificate extensions (GH #5518) + +* Skip checking the self-signature of self-signed certificates during parsing (GH #5515) + +* Add an index to ``X509_CRL`` for fast revocation checks (GH #5511) + +* Add ``X509_Certificate::Tag`` for fast searching/indexing of certificates (GH #5509) + +* Change ``X509_Object`` to share immutable state between copies (GH #5504) + +* Fix bugs in handling of indefinite length BER data, including missing + EOC markers being silently accepted (GH #5545) + +* Make certificate path building DFS incremental (GH #5513 #5520 #5521) + +* Avoid sending the TLS ``certificate_type`` extension unless TLS 1.2 is + disabled, since raw public keys are not currently supported in 1.2 (GH #5523) + +* Add support for RFC 9258 PSK import in TLS 1.3 (GH #5523) + +* Avoid truncation of large handshake messages in DTLS (GH #5522) + +* Add ALPN support to the Boost ASIO TLS stream (GH #5428) + +* Upgrade TLS-Anvil and add client-side TLS-Anvil testing (GH #5503) + +* Upgrade BoGo tests (GH #5523 #5556) + +* Add a script for running the NIST ACVP test vectors (GH #5527) + +* Add ``BigInt::signum`` to simplify sign comparisons (GH #5519) + +* Fixes for compiling with GCC 16 (GH #5564) + +* Add DRBG helpers to the C89/FFI interface and Python binding (GH #5527) + +* Add EC scalar and point operations to the C89/FFI interface (GH #5404 #5565) + +* Add NIST key wrap with padding to the Python binding (GH #5521) + +* Enforce maximum input length limits for ChaCha20Poly1305 and GHASH/GCM (GH #5521) + +* Add ``configure.py --without-include-namespace`` to allow installing + headers without the ``botan-3/`` subdirectory (GH #5528) + +Version 3.11.1, 2026-03-31 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* CVE-2026-34580: Resolve certificate verification bypass bug introduced in 3.11.0 (GH #5500) + +* CVE-2026-34582: Resolve TLS 1.3 client authentication bypass (GH #5599) + +* Add optimized Argon2 implementation using AVX512 (GH #5471) + +* Add optimized and constant-time Twofish implementation using AVX512/GFNI (GH #5465) + +* Add optimized and constant-time SEED implementation using AVX512/GFNI (GH #5472) + +* Add optimized and constant-time Whirlpool implementations using AVX2 and AVX512 (GH #5453 #5473) + +* Add SSSE3/NEON and AVX2 optimized codepaths for CTR (GH #5474 #5480) + +* Add constant time implementations of Camellia, ARIA, SEED and SM4 using AES-NI + or ARMv8 AES instructions to implement sbox lookups (GH #5476 #5477 #5479 #5481 #5485 #5492) + +* Improve performance of the AVX512 implementation of SHA-512 especially for Clang (GH #5490) + +* Optimizations for the IDEA modular multiplication (GH #5484) + +* Fix various minor TLS conformance issues flagged by TLS-Anvil (GH #5494 #5498) + +* Fix bug in Ed25519 where an invalid signature checked with PK_Verifier might + cause a later valid signature to be rejected. (GH #5454) + +* Fix a bug in handling of ECDSA DER-encode signatures where an invalid signature + checked with PK_Verifier might cause a later valid signature to be rejected. + (GH #5455) + +* Fix a problem introduced in 3.11.0 which could cause crashes on processors + without SSSE3 support, particularly when compiled by GCC. (GH #5460 #5463 #5469) + +* Fix various new warnings from ``clang-tidy`` 22 (GH #5456) + +* Fix a compilation error introduced in 3.11.0 which prevented using ``ffi`` unless + ``bcrypt`` was also enabled. (GH #5462) + +* Avoid a macro collision with Microsoft headers that could cause a compilation + problem in amalgamation mode. (GH #5486) + +* Enable explicit_bzero, getentropy, getrandom on Hurd (GH #5488) + +Version 3.11.0, 2026-03-15 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* CVE-2026-32877: Fix a heap over-read during SM2 decryption (GH #5450) + +* CVE-2026-32883: Fix an OCSP response forgery vulnerability (GH #5449) + +* CVE-2026-32884: Fix a name constraints bypass for DNS names (GH #5448) + +* Upgrade PKCS #11 wrapper to support v3.2 of the standard (GH #4540) + +* Add support for verifying X509 certificate chains when the trust anchor is + not self signed. (GH #5047) + +* Add support for multiple OCSP responders in an Authority Information Acesss + extension. (GH #5231) + +* Many additions to the C89/FFI interface, especially regarding X.509 + certificates and CRLs, also XOF support (GH #5148 #5166 #5188 #5217 #5220 + #5221 #5222 #5225 #5230 #5232 #5234 #5235 #5236 #5252) + +* Avoid using ISA enabling flags (like `-mavx2` or `/arch:AVX`) in the build + anymore, as this conflicts with precompiled headers and has been known to cause + miscompilations in certain circumstances. (GH #5297 #5260) + +* Add optimized Keccak permutation implementation using AVX-512 (GH #5191) + +* Add optimized SM3 implementations using AVX2/BMI2 (GH #5178), SM3-NI (GH #5183), + and ARMv8 instructions (GH #5444) + +* Add optimized SM4 implementation using AVX-512/GFNI (GH #5192 #5333) + +* Add optimized Camellia implementations using AVX2/GFNI and AVX-512/GFNI (GH #5442) + +* Add optimized ARIA implementation using AVX-512/GFNI (GH #5440) + +* Add AVX2 implementation of IDEA (GH #5447) + +* Ed448 and X448 optimizations (GH #5383) + +* Add AVX-512/CLMUL optimized XTS mode (GH #5251) + +* GCM and GMAC optimizations including new AVX-512 codepaths (GH #5273 #5278 #5379 #5418) + +* Poly1305 optimizations, including AVX2 and AVX-512 implementations (GH #5227) + +* Add new DES implementation using bitslicing (GH #5433) + +* Rewrite Twofish key schedule to avoid use of large tables (GH #5432) + +* Generate Streebog and Whirlpool tables at compile time (GH #5427 #5430 #5434) + +* Various elliptic curve arithmetic optimizations (GH #5186 #5194 #5195 #5196 + #5275 #5387 #5393 #5394 #5400 #5403) + +* Add some inline asm for aarch64 improving multiprecision integer performance (GH #5407) + +* Certain signature and KEM schemes, including XMSS, LMS, FrodoKEM, + ML-KEM/Kyber, and ML-DSA/Dilithium, would fail or produce incorrect results if + multiple threads attempted operations on the same key object concurrently. In + a strict sense uncoordinated multithreaded use of the same object was never + supported, but this usage did work for RSA, ECDSA, and other schemes, and the + previous behavior is potentially quite surprising. Tests have been added to + ensure this usage works for all schemes going forward. (GH #5359 #5361 #5366 + #5367 #5371 #5376 #5380 #5382) + +* Improve handling of constant time and variable time divisions (GH #5176 #5177 #5180) + +* In finite-field Diffie-Hellman use exponent lengths as prescribed in NIST SP 800-56A + and SP 800-56B. (GH #5384) + +* Optimize ECDSA signature setup phase (GH #5173) + +* The R3 versions of Kyber and Dilithium are official deprecated (GH #5368) + +* Check for already known/validated groups when decoding explicit EC parameters (GH #5268) + +* Add support for WebAssembly SIMD, optimizing various algorithms including AES, GCM, + ChaCha, SHA-1, SHA-256, Argon2 and others. (GH #5155 #5163 #5201) + +* Emscripten/WebAssembly improvements including using the new Wasm exception mechanism + (GH #5202) and re-enabling testing in CI with Emscripten (GH #5209) + +* Allow building TLS 1.3 without TLS 1.2 (GH #5292 #5293 #5303 #5309 #5318) + +* Add optional callback to provide a user-defined TLS 1.2 key derivation function (GH #5107) + +* Add scripts to run Wycheproof tests every night in CI (GH #5269) + +* Support for AltiVec on 32-bit PowerPC platforms has been dropped (GH #5266) + +* Many changes to improve library build times (GH #5279 #5280 #5284 #5285 #5286 #5287 #5288 + #5289 #5291 #5294 #5295 #5296 #5300 #5302 #5304 #5314 #5315 #5321 #5323 #5343 #5344 #5345 + #5346 #5347 #5354) + +* Test suite infrastructure cleanups (GH #5327 #5328 #5329 #5330 #5334 #5337 #5338 #5340 + #5341 #5342 #5348 #5351 #5352 #5357) + +* Unroll loops to improve Montgomery reduction performance. (GH #5150) + +* Increase maximum HMAC key length to 8192 bytes. (GH #5156) + +* Python binding additions including custom RNG (GH #5271) and checks for explicit + EC parameter encoding (GH #5282) + +* On MSVC default to using embedded debug info rather than the PDB (GH #5349) + +* Fix various clang-tidy and cppcheck warnings (GH #5172 #5207 #5204 #5205) + +Version 3.10.0, 2025-11-06 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* Add support for Ascon AEAD, hash and XOF from NIST SP 800-232 (GH #5061 #5076 #5097) + +* Add support for building with clang-cl (GH #4255) + +* Optimizations for base58 encoding and decoding (GH #5051) + +* Optimizations for SHA-3/SHAKE (GH #5133) + +* Optimizations for SEED (GH #5147) + +* Optimizations and cleanups for BLAKE2s (GH #5117) + +* Optimizations for Streebog (GH #5111) + +* Add new interface to ``Certificate_Store`` allowing search by issuer DN + plus serial. (GH #5072) + +* Fix a bug preventing botan_srp6_server_session_step1 from being reinvoked + (GH #5112 #5135) + +* Modify some bit operation functions to reduce risk of compilers introducing + non-constant time behavior (GH #5066) + +* Add new FFI functions for loading elliptic curve keys in SEC1 format (GH #5083) + +* Add new FFI functions for viewing the value of a ``botan_mp_t`` (GH #5131) + +* New faster implementation of Jacobi function (GH #5057) + +* Add optimized integer division logic for various special cases (GH #5068 #5077) + +* Correct documentation/comments relating to the maximum output length + that ``botan_mp_to_hex`` might write (GH #5131 #5129) + +* Fix an issue when trying to use CMake older than 3.18 (GH #5098 #5099) + +* Add typing hints to the Python binding (GH #5086 #5092) + +* Fix various issues flagged by the ``ruff`` Python linter (GH #5089) + +* Fix a bug in the Python binding which prevented signing raw bytes with ``PKSign`` + (GH #5082) + +* Update configure to check for Fedora's new location for trust roots (GH #5052) + +* Remove various internal references to "EME", an obsolete term used for RSA + encryption padding that originates from IEEE 1363. (GH #5055) + +* Fix various typos in the source and documentation (GH #5071 #5075 #5114) + +* Add a ``.devcontainer`` setup (GH #5094) + +Version 3.9.0, 2025-08-05 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* Add SHA-1 implementation using AVX2/BMI2 (GH #4852) + +* Add Camellia implementation using GFNI/AVX2 (GH #4848) + +* Add SHACAL2 implementation using AVX512 (GH #4878) + +* The eFrodoKEM TLS 1.3 ciphersuites have changed the suite code to match changes + in OQS. (GH #4900) + +* Add support for TLS 1.2 NULL cipher suites. These suites are disabled in the + build by default, enable ``tls_null`` module to use. (GH #4776) + +* Add support for X.509 extensions from RFC 3779 (GH #4699 #4883 #4884 #4886) + +* Elliptic curve improvements (GH #4841 #4934 #4935 #4937 #4949 $4953 #4991) + +* Add ``EC_Scalar::hash`` following RFC 9380's hash_to_field (GH #4950) + +* Modify the OID lookup system to use a static switch for builtin OIDs. (GH #4896 #4888) + +* Optimizations for X448 and Ed448 (GH #5037) + +* Modify ``BOTAN_CLEAR_CPUID`` so that clearing ``ssse3`` also disables AVX2/AVX512 + (GH #4853) + +* Remove various internal references to "EMSA", an obsolete term used for RSA + signature padding that originates from IEEE 1363. (GH #5008 #5024) + +* Enable support for GCC's "strub" stack clearing. This is disabled by default, use + the ``--enable-stack-scrubbing`` option to turn on. (GH #4882 #4925) + +* Use ``std::span`` in the internal block cipher padding mode interfaces (GH #4873) + +* Properly check DNS label length restrictions when checking wildcards. (GH #4876 #4881) + +* Work around a GCC 13/14 miscompilation when LTO is used (GH #4863 #4862) + +* Fix a bug preventing building ``System_RNG`` with only ``getrandom`` enabled. (GH #4932 #4930) + +* Document the specific threat model the library uses (GH #4955) + +* Remove ``configure.py`` options to disable specific CPU instructions. (GH #4927) + +* Remove ``configure.py`` option ``--with-local-config`` (GH #4905) + +* Add a better interface for encoding optional ASN.1 elements using ``std::optional`` (GH #5001) + +* Internal cleanups relating to multiprecision integers (GH #5009 #5010 #5012 #5014 #5017) + +* Resolve many warnings from ``clang-tidy`` (GH #4907 #4908 #4910 #4912 #4913 #4919 #4920 #4923 + #4924 #4931 #4956 #4957 #4958 #4959 #4960 #4961 #4962 #4963 #4964 #4968 #4969 #4971 #4972 #4973 + #4974 #4975 #4976 #4977 #4978 #4979 #4980 #4981 #4982 #4983 #4984 #4985 #4986 #4987 #4988 #4989 + #4990 #4992 #4993 #4998 #5004 #5005 #5031 #5032 #5034 #5035 #5036) + +* CMake improvements (GH #5022 #5027) + +* CI improvements (GH #4920 #4294 #4926 #4929) + +Version 3.8.1, 2025-05-07 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* Fix a bug that prevented building using the ``fips140`` or ``modern`` module + policies. (GH #4854 #4856) + +* Fix a missing include that caused compilation failures with libc++20 + (GH #4855 #4857) + +Version 3.8.0, 2025-05-06 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* Discussion has started regarding plans for Botan4, current ETA 2027. Check the + tracking ticket in https://github.com/randombit/botan/issues/4666 for the + current plans. + +* Ongoing elliptic curve optimizations and cleanups (GH #4554 #4620 #4623 #4625 + #4627 #4632 #4634 #4686 #4687 #4688 #4689 #4690 #4692 #4695 #4703 #4706 #4708 + #4710 #4711 #4746 #4794) + +* Add support for extended private keys in ML-KEM to handle certain implementations + which do not use the seed encoding. (GH #4817) + +* Add support for SHA-512 instructions added in upcoming Intel processors (GH #4766) + +* Add support for SM4 instructions added in upcoming Intel processors (GH #4768) + +* The SHA-1 implementation using SSE2 has been extended to support NEON and LoongArch LSX. + (GH #4809) + +* Add SHA-256 and SHA-512 implementations using AVX2/BMI2 (GH #4818 #4821) + +* Add SHA-512 implementation using AVX-512/BMI2 (GH #4842 #4849) + +* Add SHA-256 implementation using SSSE3 or NEON for message expansion (GH #4819) + +* The default TLS policy now prefers AES/GCM over ChaCha20Poly1305 (GH #4843) + +* Add support for TLS 1.3 post-quantum KEM secp384r1/ML-KEM-1024 (GH #4752) + +* Fix bugs in the server-side implementation of TLS 1.3 post-quantum hybrid + encryption which affected ciphersuites using NIST curves. (GH #4752) + +* Previously ``build.h`` included various parameters which could be modified by + end users prior to compilation. These have been removed. (GH #4639) + +* Previously ``build.h`` had macros reflecting various information about the + target system, such as ``BOTAN_TARGET_OS_IS_LINUX``. Now all such macros have + been moved to a new internal header. This allows sharing all installed + headers, including ``build.h``, across multiple different builds of the + library, as long as they all have the same version and module selection. This + simplifies vendoring the library. (GH #4642 #4747) + +* Various headers have been modified to minimize the number of inclusions they + make. You may need to modify your application to directly include any headers + which up until now had been implicitly pulled in. (GH #4650) + +* Add an FFI example which also works as a test in CI that prevents accidentally + making changes to ``ffi.h`` or ``build.h`` that make them incompatible with C. (GH #4640) + +* Add new FFI functions regarding stateful private keys (GH #4700), OIDS (GH #4816), + and EC_Group (GH #4834) + +* Add missing checks for null pointer arguments in FFI (#4704) + +* Faster base32 encoding using SWAR technique (GH #4765) + +* Add support for X.509 CRLs with the ``nextUpdate`` field unset. Such CRLs + are prohibited by RFC 5280, but do unfortunately exist within the ecosystem. (GH #4732) + +* When encoding a RSASSA-PSS-Params struct, skip encoding the trailer field + default value, as required by RFC 4055 (GH #4731) + +* Extend vector permute AES to support big-endian AltiVec/VMX systems. (GH #4738) + +* Extend use of POWER VMULL instruction to also support big-endian systems. (GH #4743) + +* Fix encoding extended key usage in PKCS10 requests (GH #4725) + +* Add internal API for hybrid PQ combiner keys (GH #4067) + +* Internal refactorings of CPU feature detection. (GH #4718) + +* Add support for CPU feature detection on RISCV64 (GH #4800 #4815) + +* Add support for the LoongArch LSX SIMD extension in AES, SHA-1, ZFEC, ChaCha (GH #4799) + +* Various SIMD-enabled implementations which previously only required SSE2 now additionally + require SSSE3. Such optimizations will no longer be used on (now quite rare) CPUs which + support SSE2 but not SSSE3. (GH #4803) + +* Optimize parsing of large CRLs (GH #4789 #4790 #4792) + +* Improve performance of RSA public and private key parsing (GH #4793) + +* Add a couple examples of using format preserving encryption (GH #4758) + +* CI cleanups and improvements (GH #4756 #4761 #4762 #4767 #4770 #4812 #4813) + +* The ``Ed25519_PrivateKey`` constructor had behavior that varied based on the + input length. Add explicit ``from_seed`` and ``from_bytes`` functions which + make the two options explicit. (GH #4701 #4702) + +* Add a new cleaner interface for handling ECIES flags (GH #4691) + +* Reduce use of heap in GCM/GMAC (GH #4826) and hex/base64 (GH #4832) + +* New faster Barrett reduction implementation (GH #4835) + +* Internal RSA signature padding cleanups (GH #4635) + +* Cleanups to the implementations of SHA-1 and SHA-256 using SHA-NI (GH #4773 #4774) + +* Cleanups to reduce code size where possible (GH #4775 #4777 #4781 #4825) + +* Fix a bug that caused the tests to skip testing AES-NI if AES-VAES was supported. + (GH #4649) + +* Fix issues with CMake integration when built in Debian-style multiarch setups. + (GH #4839) + +* Now even for purely static library builds, ``-fPIC`` is used to compile the + library objects. This allows linking position independent executables (PIE) + against the static library. (GH #4716) + +* Remove support for NetBSD ``_dlauxinfo`` which did not provide the information + that the library had expected it to. (GH #4736) + +* Add a script for comparing the performance between versions (GH #4693 #4754) + +* Update GHA CodeQL actions (GH #4644) + Version 3.7.1, 2025-02-05 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -34,7 +513,7 @@ all elliptic curves. This is no longer the case. You can re-enable support for specific named curves by adding a ``pcurves`` module, for example ``pcurves_secp256r1`` or ``pcurves_brainpool384r1``. Also in 3.7.0, the old - BigInt based EC arithemtic implementation was moved to ``legacy_ec_point``, + BigInt based EC arithmetic implementation was moved to ``legacy_ec_point``, which is marked as deprecated. Disabling this module will disable support for certain (also deprecated) elliptic curves such as "x962_p239v1" and "secp224k1". It will also disable support for application specific @@ -201,7 +680,7 @@ * Fix certificate validation when the trust root is a self-signed MD2 cert. (GH #4247 #4248) -* Internal "strong types" improvments (GH #4170) +* Internal "strong types" improvements (GH #4170) * Refactor the ``speed`` cli utility (GH #4364 #4367 #4369) @@ -607,8 +1086,8 @@ * Add checks for invalid length AD in Argon2 (GH #3626) -* CI now uses Android NDK 26, and earlier NDKs are not supported - due to limitations of the C++ library in earlier NDKs (GH #3718) +* CI now uses Android NDK 26. Earlier NDK versions are no longer supported + due to limitations in their C++ library implementations. (GH #3718) * Improve support for IBM's XLC compiler (GH #3730) diff -Nru botan3-3.7.1+dfsg/readme.rst botan3-3.12.0+dfsg/readme.rst --- botan3-3.7.1+dfsg/readme.rst 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/readme.rst 2026-05-07 01:38:28.000000000 +0000 @@ -1,8 +1,8 @@ Botan ======================================== -Botan (Japanese for peony flower) is a cryptography library released under the -permissive `Simplified BSD `_ license. +Botan is a C++ cryptography library released under the permissive +`Simplified BSD `_ license. Botan's `goal `_ is to be the best option for production cryptography by offering the tools @@ -73,14 +73,14 @@ February, May, August, and November. The latest release in the Botan3 series is -`3.7.1 `_ -`(sig) `__, -released on 2025-02-05. +`3.12.0 `_ +`(sig) `__, +released on 2026-05-06. Botan2 -------- -Botan2 has, as of 2025-1-1, reached end of life. No further releases are expected. +Botan2 has, as of 2025-01-01, reached end of life. No further releases are expected. The latest release in the Botan2 series is `2.19.5 `_ @@ -129,8 +129,7 @@ * Stream ciphers (X)ChaCha20, (X)Salsa20, RC4 * Hash functions SHA-1, SHA-2, SHA-3, RIPEMD-160, BLAKE2b/BLAKE2s, Skein-512, SM3, Whirlpool * Password hashing schemes Argon2, Scrypt, bcrypt, and PBKDF2 -* Authentication codes HMAC, CMAC, Poly1305, KMAC, SipHash, GMAC -* Non-cryptographic checksums Adler32, CRC24, CRC32 +* Authentication codes HMAC, CMAC, Poly1305, KMAC, GMAC Other Useful Things ---------------------------------------- @@ -150,3 +149,4 @@ * Encoding schemes including hex, base32, base64 and base58 * NIST key wrapping * Boost.Asio compatible TLS client stream +* 24-bit OpenPGP CRC diff -Nru botan3-3.7.1+dfsg/src/.clang-tidy botan3-3.12.0+dfsg/src/.clang-tidy --- botan3-3.7.1+dfsg/src/.clang-tidy 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/.clang-tidy 2026-05-07 01:38:28.000000000 +0000 @@ -1,8 +1,6 @@ --- -# This file was automatically generated by ./src/scripts/dev_tools/run_clang_tidy.py --regenerate-inline-config-file -# -# All manual edits to this file will be lost. Edit the script -# then regenerate this configuration file. +# This file was automatically generated by ./src/scripts/dev_tools/run_clang_tidy.py --regenerate-inline-config-file on 2026-04-24 +# All manual changes will be lost. Edit the script instead. Checks: > bugprone-*, @@ -15,35 +13,9 @@ performance-*, portability-*, readability-*, - -*-named-parameter, - -*-member-init, - -bugprone-lambda-function-name, - -bugprone-unchecked-optional-access, - -bugprone-empty-catch, - -cert-err58-cpp, - -cppcoreguidelines-avoid-const-or-ref-data-members, - -cppcoreguidelines-init-variables, - -cppcoreguidelines-owning-memory, - -cppcoreguidelines-prefer-member-initializer, - -cppcoreguidelines-slicing, - -hicpp-explicit-conversions, - -misc-const-correctness, - -misc-include-cleaner, - -misc-redundant-expression, - -misc-misplaced-const, - -misc-confusable-identifiers, - -modernize-avoid-bind, - -modernize-pass-by-value, - -modernize-use-ranges, - -performance-avoid-endl, - -readability-convert-member-functions-to-static, - -readability-implicit-bool-conversion, - -readability-inconsistent-declaration-parameter-name, - -readability-qualified-auto, - -readability-simplify-boolean-expr, - -readability-static-accessed-through-instance, -*-array-to-pointer-decay, -*-avoid-c-arrays, + -*-deprecated-headers, -*-else-after-return, -*-function-size, -*-magic-numbers, @@ -51,41 +23,46 @@ -*-no-array-decay, -*-use-auto, -*-use-emplace, - -*-deprecated-headers, - -bugprone-argument-comment, - -bugprone-branch-clone, -bugprone-easily-swappable-parameters, + -bugprone-empty-catch, -bugprone-implicit-widening-of-multiplication-result, - -bugprone-suspicious-stringview-data-usage, - -cppcoreguidelines-avoid-do-while, - -cppcoreguidelines-non-private-member-variables-in-classes, - -cppcoreguidelines-pro-bounds-pointer-arithmetic, + -bugprone-unchecked-optional-access, + -cert-dcl21-cpp, + -cppcoreguidelines-avoid-const-or-ref-data-members, + -cppcoreguidelines-pro-bounds-avoid-unchecked-container-access, -cppcoreguidelines-pro-bounds-constant-array-index, + -cppcoreguidelines-pro-bounds-pointer-arithmetic, -cppcoreguidelines-pro-type-const-cast, -cppcoreguidelines-pro-type-reinterpret-cast, - -cppcoreguidelines-pro-type-vararg, - -hicpp-no-assembler, - -hicpp-vararg, + -cppcoreguidelines-use-default-member-init, -hicpp-signed-bitwise, + -misc-include-cleaner, + -misc-multiple-inheritance, -misc-no-recursion, - -modernize-loop-convert, - -modernize-raw-string-literal, - -modernize-use-trailing-return-type, + -misc-override-with-different-visibility, + -modernize-avoid-c-style-cast, + -modernize-pass-by-value, -modernize-return-braced-init-list, -modernize-use-default-member-init, -modernize-use-designated-initializers, -modernize-use-nodiscard, + -modernize-use-ranges, + -modernize-use-trailing-return-type, -modernize-use-using, - -portability-simd-intrinsics, + -performance-avoid-endl, -readability-avoid-return-with-void-value, - -readability-container-data-pointer, + -readability-convert-member-functions-to-static, -readability-function-cognitive-complexity, -readability-identifier-length, - -readability-isolate-declaration, + -readability-inconsistent-declaration-parameter-name, -readability-math-missing-parentheses, -readability-non-const-parameter, -readability-redundant-access-specifiers, - -readability-suspicious-call-argument, - -readability-use-std-min-max, + -readability-redundant-inline-specifier, + -readability-redundant-parentheses, + -readability-redundant-typename, + -readability-simplify-boolean-expr, + -readability-static-accessed-through-instance, -readability-use-anyofallof, + -readability-use-concise-preprocessor-directives, --- diff -Nru botan3-3.7.1+dfsg/src/bogo_shim/bogo_shim.cpp botan3-3.12.0+dfsg/src/bogo_shim/bogo_shim.cpp --- botan3-3.7.1+dfsg/src/bogo_shim/bogo_shim.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/bogo_shim/bogo_shim.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,28 +11,46 @@ */ #include +#include #include +#include #include +#include #include #include #include #include #include +#include #include #include +#include +#include #include +#include #include #include #include +#include #include #include #include #include +#include +#if defined(BOTAN_HAS_TLS_13) + #include +#endif + +#include +#include #include +#include +#include #include #include #include +#include #include #include #include @@ -56,41 +74,46 @@ return rc; } -void shim_log(const std::string& s) { - if(::getenv("BOTAN_BOGO_SHIM_LOG")) { - /* - FIXMEs: - - Rewrite this to use a std::ostream instead - - Allow using the env variable to point to where the log is written - - Avoid rechecking the env variable with each call (!) - */ - - // NOLINTNEXTLINE(*-avoid-non-const-global-variables) - static FILE* g_log = std::fopen("/tmp/bogo_shim.log", "w"); - struct timeval tv; - ::gettimeofday(&tv, nullptr); - static_cast(std::fprintf(g_log, - "%lld.%lu: %s\n", - static_cast(tv.tv_sec), - static_cast(tv.tv_usec), - s.c_str())); - static_cast(std::fflush(g_log)); +void shim_log(std::string_view s) { + static const auto log_path = []() -> std::string { + const char* env = ::getenv("BOTAN_BOGO_SHIM_LOG"); + if(env == nullptr) { + return {}; + } + + auto log_file_path = std::string(env); + if(log_file_path.empty() || log_file_path == "1") { + return "/tmp/bogo_shim.log"; + } + return env; + }(); + + if(!log_path.empty()) { + static std::ofstream g_log(log_path, std::ios::out | std::ios::trunc); + if(g_log.is_open() && g_log.good()) { + const auto duration = std::chrono::system_clock::now().time_since_epoch(); + const auto seconds = std::chrono::duration_cast>(duration); + + g_log << std::fixed << std::setprecision(6) << seconds.count() << ": " << s << std::endl; + } } } -[[noreturn]] void shim_exit_with_error(const std::string& s, int rc = 1) { +[[noreturn]] void shim_exit_with_error(const std::string& s, int rc = 1) noexcept { shim_log("Exiting with " + s); std::cerr << s << "\n"; std::exit(rc); } -std::string map_to_bogo_error(const std::string& e) { +std::string map_to_bogo_error(const std::string& e) noexcept { shim_log("Original error " + e); static const std::unordered_map err_map{ {"Application data before handshake done", ":APPLICATION_DATA_INSTEAD_OF_HANDSHAKE:"}, {"Bad Hello_Request, has non-zero size", ":BAD_HELLO_REQUEST:"}, {"Bad code for TLS alert level", ":UNKNOWN_ALERT_TYPE:"}, + {"Bad encoding of SNI extension", ":DECODE_ERROR:"}, + {"Server sent non-empty SNI extension", ":DECODE_ERROR:"}, {"Bad encoding on signature algorithms extension", ":DECODE_ERROR:"}, {"Bad extension size", ":DECODE_ERROR:"}, {"Bad length in hello verify request", ":DECODE_ERROR:"}, @@ -99,6 +122,8 @@ {"Server certificate verification failed", ":BAD_SIGNATURE:"}, {"compression is not supported in TLS 1.3", ":DECODE_ERROR:"}, {"Cookie length must be at least 1 byte", ":DECODE_ERROR:"}, + {"Empty certificate_authorities list is illegal", ":DECODE_ERROR:"}, + {"Empty cookie extension is illegal", ":DECODE_ERROR:"}, {"Bad size (1) for TLS alert message", ":BAD_ALERT:"}, {"Bad size (4) for TLS alert message", ":BAD_ALERT:"}, {"CERTIFICATE decoding failed with PEM: No PEM header found", ":CANNOT_PARSE_LEAF_CERT:"}, @@ -120,6 +145,7 @@ {"Client did not comply with the requested key exchange group", ":WRONG_CURVE:"}, {"Client Hello must either contain both key_share and supported_groups extensions or neither", ":MISSING_KEY_SHARE:"}, + {"Server Hello did not contain a key share extension", ":MISSING_KEY_SHARE:"}, {"Client Hello offered a PSK without a psk_key_exchange_modes extension", ":MISSING_EXTENSION:"}, {"Client offered DTLS version with major version 0xFF", ":UNSUPPORTED_PROTOCOL:"}, {"Client offered SSLv3 which is not supported", ":UNSUPPORTED_PROTOCOL:"}, @@ -148,6 +174,7 @@ {"Empty PSK binders list", ":DECODE_ERROR: "}, {"Encoding error: Cannot encode PSS string, output length too small", ":NO_COMMON_SIGNATURE_ALGORITHMS:"}, {"Expected TLS but got a record with DTLS version", ":WRONG_VERSION_NUMBER:"}, + {"Expected ChangeCipherSpec but got a handshake message", ":UNEXPECTED_RECORD:"}, {"Extension removed in updated Client Hello", ":INCONSISTENT_CLIENT_HELLO:"}, {"Failed to agree on a signature algorithm", ":NO_COMMON_SIGNATURE_ALGORITHMS:"}, {"Failed to agree on any signature algorithm", ":NO_COMMON_SIGNATURE_ALGORITHMS:"}, @@ -156,17 +183,20 @@ ":NO_COMMON_SIGNATURE_ALGORITHMS:"}, {"PSK extension was not at the very end of the Client Hello", ":PRE_SHARED_KEY_MUST_BE_LAST:"}, {"Finished message didn't verify", ":DIGEST_CHECK_FAILED:"}, + {"Handshake message is 2293760 bytes, policy maximum is 65536", ":BAD_HANDSHAKE_RECORD:"}, {"Have data remaining in buffer after ClientHello", ":EXCESS_HANDSHAKE_DATA:"}, {"Have data remaining in buffer after Finished", ":EXCESS_HANDSHAKE_DATA:"}, {"Have data remaining in buffer after ServerHelloDone", ":EXCESS_HANDSHAKE_DATA:"}, {"Hello Retry Request does not request any changes to Client Hello", ":EMPTY_HELLO_RETRY_REQUEST:"}, {"Unexpected additional handshake message data found in record", ":EXCESS_HANDSHAKE_DATA:"}, {"Inconsistent length in certificate request", ":DECODE_ERROR:"}, + {"Inconsistent length in certificate_authorities extension", ":DECODE_ERROR:"}, {"unexpected key_update parameter", ":DECODE_ERROR:"}, {"Inconsistent values in fragmented DTLS handshake header", ":FRAGMENT_MISMATCH:"}, {"Invalid CertificateRequest: Length field outside parameters", ":DECODE_ERROR:"}, {"Invalid ServerHello: Length field outside parameters", ":DECODE_ERROR:"}, {"Invalid CertificateVerify: Extra bytes at end of message", ":DECODE_ERROR:"}, + {"Invalid Certificate_Status message: too small", ":DECODE_ERROR:"}, {"Invalid Certificate_Status: invalid length field", ":DECODE_ERROR:"}, {"Invalid ChangeCipherSpec", ":BAD_CHANGE_CIPHER_SPEC:"}, {"Invalid ClientHello: Length field outside parameters", ":DECODE_ERROR:"}, @@ -176,6 +206,7 @@ {"Invalid authentication tag: ChaCha20Poly1305 tag check failed", ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:"}, {"Invalid authentication tag: GCM tag check failed", ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:"}, {"Invalid encapsulated key length", ":BAD_ECPOINT:"}, + {"Invalid handshake message type", ":UNEXPECTED_RECORD:"}, {"Invalid hybrid KEM ciphertext", ":BAD_ECPOINT:"}, {"Invalid size 31 for X25519 public key", ":BAD_ECPOINT:"}, {"Invalid size 33 for X25519 public key", ":BAD_ECPOINT:"}, @@ -185,13 +216,17 @@ {"No shared TLS version", ":UNSUPPORTED_PROTOCOL:"}, {"OS2ECP: Unknown format type 251", ":BAD_ECPOINT:"}, {"Peer sent signature algorithm that is not suitable for TLS 1.3", ":WRONG_SIGNATURE_TYPE:"}, + {"Public key does not have the correct byte count", ":BAD_ECPOINT:"}, {"Policy forbids all available DTLS version", ":NO_SUPPORTED_VERSIONS_ENABLED:"}, {"Policy forbids all available TLS version", ":NO_SUPPORTED_VERSIONS_ENABLED:"}, {"Policy refuses to accept signing with any hash supported by peer", ":NO_COMMON_SIGNATURE_ALGORITHMS:"}, + {"Could not agree on a signature scheme with peer for RSA key", ":NO_COMMON_SIGNATURE_ALGORITHMS:"}, + {"Could not agree on a signature scheme with peer for ECDSA key", ":NO_COMMON_SIGNATURE_ALGORITHMS:"}, {"Policy requires client send a certificate, but it did not", ":PEER_DID_NOT_RETURN_A_CERTIFICATE:"}, {"PSK binder does not check out", ":DIGEST_CHECK_FAILED:"}, {"PSK identity selected by server is out of bounds", ":PSK_IDENTITY_NOT_FOUND:"}, {"PSK and ciphersuite selected by server are not compatible", ":OLD_SESSION_PRF_HASH_MISMATCH:"}, + {"Received an unexpectedly non-empty Certificate_Status_Request", ":DECODE_ERROR:"}, {"Received a record that exceeds maximum size", ":ENCRYPTED_LENGTH_TOO_LONG:"}, {"Received an encrypted record that exceeds maximum size", ":ENCRYPTED_LENGTH_TOO_LONG:"}, {"received an illegal handshake message", ":UNEXPECTED_MESSAGE:"}, @@ -224,6 +259,7 @@ {"Server replied with unsupported extensions: 0", ":UNEXPECTED_EXTENSION:"}, {"Server replied with unsupported extensions: 1234", ":UNEXPECTED_EXTENSION:"}, {"Server replied with unsupported extensions: 16", ":UNEXPECTED_EXTENSION:"}, + {"Server replied with unsupported extensions: 20", ":UNEXPECTED_EXTENSION:"}, {"Server replied with unsupported extensions: 43", ":UNEXPECTED_EXTENSION:"}, {"Server replied with unsupported extensions: 5", ":UNEXPECTED_EXTENSION:"}, {"Server resumed session and removed extended master secret", ":RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION:"}, @@ -231,10 +267,12 @@ {"Server resumed session but with wrong version", ":OLD_SESSION_VERSION_NOT_RETURNED:"}, {"Server selected a group that is not compatible with the negotiated ciphersuite", ":WRONG_CURVE:"}, {"Server sent ECC curve prohibited by policy", ":WRONG_CURVE:"}, + {"Server selected a curve we did not offer", ":WRONG_CURVE:"}, {"group was not advertised as supported", ":WRONG_CURVE:"}, {"group was already offered", ":WRONG_CURVE:"}, {"Server selected a key exchange group we didn't offer.", ":WRONG_CURVE:"}, {"TLS 1.3 Server Hello selected a different version", ":SECOND_SERVERHELLO_VERSION_MISMATCH:"}, + {"TLS signature extension did not allow for RSA_PSS_SHA256 signature", ":WRONG_SIGNATURE_TYPE:"}, {"Version downgrade received after Hello Retry", ":SECOND_SERVERHELLO_VERSION_MISMATCH:"}, {"protected change cipher spec received", ":UNEXPECTED_RECORD:"}, {"Server sent an unsupported extension", ":UNEXPECTED_EXTENSION:"}, @@ -257,7 +295,7 @@ {"TLS record version had unexpected value", ":WRONG_VERSION_NUMBER:"}, {"Test requires rejecting cert", ":CERTIFICATE_VERIFY_FAILED:"}, {"Too many PSK binders", ":PSK_IDENTITY_BINDER_COUNT_MISMATCH:"}, - {"Unexpected ALPN protocol", ":INVALID_ALPN_PROTOCOL:"}, + {"Server selected an ALPN protocol not offered by the client", ":INVALID_ALPN_PROTOCOL:"}, {"Unexpected record type 42 from counterparty", ":UNEXPECTED_RECORD:"}, {"Unexpected state transition in handshake got a certificate_request expected server_hello_done seen server_hello+server_key_exchange", ":UNEXPECTED_MESSAGE:"}, @@ -300,6 +338,10 @@ ":UNEXPECTED_MESSAGE:"}, {"Unexpected state transition in handshake got a server_key_exchange not expecting messages", ":BAD_HELLO_REQUEST:"}, + {"Unexpected state transition in handshake got a certificate_request expected finished seen server_hello+encrypted_extensions", + ":UNEXPECTED_MESSAGE:"}, + {"Unexpected state transition in handshake got a certificate expected finished seen server_hello+encrypted_extensions", + ":UNEXPECTED_MESSAGE:"}, {"Unexpected state transition in handshake got a finished expected certificate_verify seen server_hello+certificate+encrypted_extensions", ":BAD_HELLO_REQUEST:"}, {"Unknown TLS handshake message type 43", ":UNEXPECTED_MESSAGE:"}, @@ -331,6 +373,12 @@ {"Peer sent unknown signature scheme", ":WRONG_SIGNATURE_TYPE:"}, {"We did not offer the usage of RSA_PSS_SHA256 as a signature scheme", ":WRONG_SIGNATURE_TYPE:"}, {"X25519 public point appears to be of low order", ":BAD_ECPOINT:"}, + {"TLS signature extension did not allow for RSA/SHA-256 signature", ":WRONG_SIGNATURE_TYPE:"}, + {"No sufficient server certificate available", ":PSK_IDENTITY_NOT_FOUND:"}, + {"Failed to agree on certificate_type", ":UNSUPPORTED_CERTIFICATE:"}, + {"Selected certificate type was not offered: X509", ":UNSUPPORTED_CERTIFICATE:"}, + {"Certificate type extension contains no types", ":DECODE_ERROR:"}, + {"Application did not provide a means to validate the raw public key", ":CERTIFICATE_VERIFY_FAILED:"}, }; auto err_map_i = err_map.find(e); @@ -343,7 +391,7 @@ class Shim_Exception final : public std::exception { public: - Shim_Exception(std::string_view msg, int rc = 1) : m_msg(msg), m_rc(rc) {} + explicit Shim_Exception(std::string_view msg, int rc = 1) : m_msg(msg), m_rc(rc) {} const char* what() const noexcept override { return m_msg.c_str(); } @@ -365,24 +413,23 @@ static std::string get_last_socket_error() { return ::strerror(errno); } - using unique_addrinfo_t = std::unique_ptr; + using unique_addr_info_ptr = std::unique_ptr; public: Shim_Socket(const std::string& hostname, int port, const bool ipv6) : m_socket(-1) { - addrinfo hints; - std::memset(&hints, 0, sizeof(hints)); + addrinfo hints{}; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICSERV; const std::string service = std::to_string(port); - // TODO: C++23 will introduce std::out_ptr() that should replace the - // temporary variable for the call to ::getaddrinfo() and - // std::unique_ptr<>::reset(). - unique_addrinfo_t::pointer res_tmp; - int rc = ::getaddrinfo(hostname.c_str(), service.c_str(), &hints, &res_tmp); - unique_addrinfo_t res(res_tmp, &::freeaddrinfo); + unique_addr_info_ptr res = nullptr; + const int rc = ::getaddrinfo(hostname.c_str(), service.c_str(), &hints, Botan::out_ptr(res)); shim_log("Connecting " + hostname + ":" + service); @@ -390,7 +437,7 @@ throw Shim_Exception("Name resolution failed for " + hostname); } - for(addrinfo* rp = res.get(); (m_socket == -1) && (rp != nullptr); rp = rp->ai_next) { + for(const addrinfo* rp = res.get(); (m_socket == -1) && (rp != nullptr); rp = rp->ai_next) { if((!ipv6 && rp->ai_family != AF_INET) || (ipv6 && rp->ai_family != AF_INET6)) { continue; } @@ -402,7 +449,7 @@ continue; } - int err = ::connect(m_socket, rp->ai_addr, rp->ai_addrlen); + const int err = ::connect(m_socket, rp->ai_addr, rp->ai_addrlen); if(err != 0) { ::close(m_socket); @@ -422,8 +469,18 @@ Shim_Socket& operator=(Shim_Socket&&) = delete; ~Shim_Socket() { - ::close(m_socket); - m_socket = -1; + if(m_socket >= 0) { + // Signal that we are done writing so pending alert records + // are delivered with a FIN rather than lost to a RST. + ::shutdown(m_socket, SHUT_WR); + // Drain unread incoming data; if the receive buffer is + // non-empty when we close(), the kernel sends RST which + // discards our outgoing data (including any alert we sent). + char buf[256]; + while(::read(m_socket, buf, sizeof(buf)) > 0) {} + ::close(m_socket); + m_socket = -1; + } } void write(const uint8_t buf[], size_t len) const { @@ -433,7 +490,7 @@ size_t sent_so_far = 0; while(sent_so_far != len) { const size_t left = len - sent_so_far; - socket_op_ret_type sent = + const socket_op_ret_type sent = ::send(m_socket, Botan::cast_uint8_ptr_to_char(&buf[sent_so_far]), left, MSG_NOSIGNAL); if(sent < 0) { if(errno == EPIPE) { @@ -451,7 +508,7 @@ if(m_socket < 0) { throw Shim_Exception("Socket was bad on read"); } - socket_op_ret_type got = ::read(m_socket, Botan::cast_uint8_ptr_to_char(buf), len); + const socket_op_ret_type got = ::read(m_socket, Botan::cast_uint8_ptr_to_char(buf), len); if(got < 0) { if(errno == ECONNRESET) { @@ -469,7 +526,7 @@ } while(len > 0) { - socket_op_ret_type got = ::read(m_socket, Botan::cast_uint8_ptr_to_char(buf), len); + const socket_op_ret_type got = ::read(m_socket, Botan::cast_uint8_ptr_to_char(buf), len); if(got == 0) { throw Shim_Exception("Socket read EOF"); @@ -593,7 +650,7 @@ std::vector get_alpn_string_vec_opt(const std::string& option) const { // hack used for alpn list (relies on all ALPNs being 3 chars long...) - char delim = 0x03; + const char delim = 0x03; if(option_used(option)) { return Botan::split_on(get_string_opt(option), delim); @@ -606,15 +663,17 @@ if(!m_all_options.contains(key)) { throw Shim_Exception("Invalid option " + key); } - if(m_parsed_opts.find(key) != m_parsed_opts.end()) { + if(m_parsed_opts.contains(key)) { return true; } - if(m_parsed_int_vec_opts.find(key) != m_parsed_int_vec_opts.end()) { + if(m_parsed_int_vec_opts.contains(key)) { return true; } return false; } + const std::vector& raw_argv() const { return m_raw_argv; } + private: std::string get_opt(const std::string& key) const { auto i = m_parsed_opts.find(key); @@ -634,9 +693,113 @@ std::set m_parsed_flags; std::map m_parsed_opts; std::map> m_parsed_int_vec_opts; + std::vector m_raw_argv; }; +// A credential block parsed from a `-new-{x509,rpk,psk}-credential` argv segment. +// X509 and RPK blocks share cert/key file fields; the public key for an RPK +// block is derived from the loaded private key. +struct Shim_Credential { + enum class Kind : uint8_t { X509, RPK, PSK }; + + Kind kind = Kind::X509; + std::string cert_file; + std::string key_file; + Botan::secure_vector psk_key; + std::vector psk_identity; + std::vector psk_context; + std::string psk_hash; + + std::shared_ptr key; + std::shared_ptr raw_public_key; + std::vector cert_chain; +}; + +// Walk raw argv and extract `-new-{x509,rpk,psk}-credential` blocks. Each block +// captures the per-credential flags that follow until the next `-new-*-credential` +// or end of args. `-on-resume-*` blocks are ignored (we do not differentiate +// initial vs resume credentials). +std::vector parse_credential_blocks(const std::vector& argv) { + std::vector creds; + std::optional current; + + auto flush = [&]() { + if(current.has_value()) { + creds.push_back(std::move(*current)); + current.reset(); + } + }; + + for(size_t i = 1; i < argv.size(); ++i) { + const auto& arg = argv[i]; + + auto start_block = [&](Shim_Credential::Kind k) { + flush(); + Shim_Credential block; + block.kind = k; + current = std::move(block); + }; + + if(arg == "-new-x509-credential") { + start_block(Shim_Credential::Kind::X509); + } else if(arg == "-new-rpk-credential") { + start_block(Shim_Credential::Kind::RPK); + } else if(arg == "-new-psk-credential") { + start_block(Shim_Credential::Kind::PSK); + } else if(arg.starts_with("-on-resume-new-") || arg.starts_with("-new-")) { + // Unsupported credential block kind (resume, SPAKE2+, delegated, etc.). + flush(); + } else if(current.has_value()) { + auto take_arg = [&]() -> std::optional { + if(i + 1 < argv.size()) { + return argv[++i]; + } + return std::nullopt; + }; + + if(arg == "-cert-file") { + if(auto v = take_arg()) { + current->cert_file = *v; + } + } else if(arg == "-key-file") { + if(auto v = take_arg()) { + current->key_file = *v; + } + } else if(arg == "-psk-importer-key") { + if(auto v = take_arg()) { + current->psk_key = Botan::base64_decode(*v); + } + } else if(arg == "-psk-importer-identity") { + if(auto v = take_arg()) { + auto decoded = Botan::base64_decode(*v); + current->psk_identity.assign(decoded.begin(), decoded.end()); + } + } else if(arg == "-psk-importer-context") { + if(auto v = take_arg()) { + auto decoded = Botan::base64_decode(*v); + current->psk_context.assign(decoded.begin(), decoded.end()); + } + } else if(arg == "-psk-importer-sha256") { + current->psk_hash = "SHA-256"; + } else if(arg == "-psk-importer-sha384") { + current->psk_hash = "SHA-384"; + } + // Other per-credential fields (ocsp-response, signing-prefs, must-match-issuer, + // signed-cert-timestamps, trust-anchor-id, delegated-credential, pake-*) are + // accepted by the global parser but their semantics are not enforced here. + } + } + + flush(); + return creds; +} + void Shim_Arguments::parse_args(char* argv[]) { + // Store raw argv for later credential parsing + for(int j = 0; argv[j] != nullptr; ++j) { + m_raw_argv.emplace_back(argv[j]); + } + int i = 1; // skip argv[0] while(argv[i] != nullptr) { @@ -653,7 +816,7 @@ if(argv[i + 1] == nullptr) { throw Shim_Exception("Expected argument following " + param); } - std::string val(argv[i + 1]); + const std::string val(argv[i + 1]); shim_log(Botan::fmt("param {}={}", flag_name, val)); if(m_int_vec_opts.contains(flag_name)) { @@ -695,6 +858,7 @@ //"expect-accept-early-data", "expect-extended-master-secret", "expect-no-offer-early-data", + "expect-no-peer-cert", "expect-no-secure-renegotiation", "expect-no-session", "expect-no-session-id", @@ -729,6 +893,11 @@ "is-handshaker-supported", //"jdk11-workaround", "key-update", + "no-key-shares", + "new-psk-credential", + "new-rpk-credential", + "new-x509-credential", + "must-match-issuer", "no-check-client-certificate-type", "no-check-ecdsa-curve", "no-op-extra-handshake", @@ -738,10 +907,16 @@ "no-tls11", "no-tls12", "no-tls13", + "on-resume-expect-no-session", + //"on-resume-new-psk-credential", "on-resume-no-ticket", + //"on-resume-psk-importer-sha256", + //"on-resume-psk-importer-sha384", //"on-resume-verify-fail", //"partial-write", //"peek-then-read", + "psk-importer-sha256", + "psk-importer-sha384", //"read-with-unfinished-write", "reject-alpn", "renegotiate-freely", @@ -809,9 +984,19 @@ "expect-certificate-types", //"expect-channel-id", "expect-ocsp-response", + "expect-peer-rpk-sha256", + "delegated-credential", + "signed-cert-timestamps", + "trust-anchor-id", //"expect-quic-transport-params", //"expect-signed-cert-timestamps", "ocsp-response", + "on-resume-psk-importer-context", + "on-resume-psk-importer-identity", + "on-resume-psk-importer-key", + "psk-importer-context", + "psk-importer-identity", + "psk-importer-key", //"quic-transport-params", //"signed-cert-timestamps", //"ticket-key", /* we use a different ticket format from Boring */ @@ -821,7 +1006,12 @@ const std::set bogo_shim_int_opts{ "expect-cipher-aes", "expect-cipher-no-aes", + "expect-client-certificate-type", "expect-curve-id", + "expect-selected-credential", + "on-initial-expect-selected-credential", + "on-resume-expect-selected-credential", + "expect-peer-certificate-type", "expect-peer-signature-algorithm", "expect-ticket-age-skew", "expect-token-binding-param", @@ -845,8 +1035,10 @@ }; const std::set bogo_shim_int_vec_opts{ + "accepted-peer-cert-types", "curves", "expect-peer-verify-pref", + "key-shares", "signing-prefs", "verify-prefs", }; @@ -862,7 +1054,7 @@ class Shim_Policy final : public Botan::TLS::Policy { public: - Shim_Policy(const Shim_Arguments& args) : m_args(args), m_sessions(0) {} + explicit Shim_Policy(const Shim_Arguments& args) : m_args(args), m_sessions(0) {} void incr_session_established() { m_sessions += 1; } @@ -913,7 +1105,7 @@ std::vector allowed_signature_hashes() const override { if(m_args.option_used("signing-prefs")) { std::vector pref_hash; - for(size_t pref : m_args.get_int_vec_opt("signing-prefs")) { + for(const size_t pref : m_args.get_int_vec_opt("signing-prefs")) { const Botan::TLS::Signature_Scheme scheme(pref); if(!scheme.is_available()) { shim_log("skipping inavailable but preferred signature scheme: " + std::to_string(pref)); @@ -944,7 +1136,7 @@ std::vector acceptable_signature_schemes() const override { if(m_args.option_used("verify-prefs")) { std::vector schemes; - for(size_t pref : m_args.get_int_vec_opt("verify-prefs")) { + for(const size_t pref : m_args.get_int_vec_opt("verify-prefs")) { schemes.emplace_back(static_cast(pref)); } @@ -957,7 +1149,7 @@ std::vector allowed_signature_schemes() const override { if(m_args.option_used("signing-prefs")) { std::vector schemes; - for(size_t pref : m_args.get_int_vec_opt("signing-prefs")) { + for(const size_t pref : m_args.get_int_vec_opt("signing-prefs")) { schemes.emplace_back(static_cast(pref)); } @@ -988,13 +1180,9 @@ if(m_args.option_used("curves")) { std::vector groups; - // upcall to base class to find the groups actually supported by - // this Botan build - const auto supported_groups = Botan::TLS::Policy::key_exchange_groups(); - - for(size_t pref : m_args.get_int_vec_opt("curves")) { + for(const size_t pref : m_args.get_int_vec_opt("curves")) { const auto group = static_cast(pref); - if(std::find(supported_groups.cbegin(), supported_groups.cend(), group) != supported_groups.end()) { + if(group.to_string().has_value() && group.is_available()) { groups.push_back(group); } } @@ -1005,6 +1193,53 @@ return Botan::TLS::Policy::key_exchange_groups(); } + std::vector key_exchange_groups_to_offer() const override { + if(m_args.flag_set("no-key-shares")) { + return {}; + } + + const auto groups = key_exchange_groups(); + + if(m_args.option_used("key-shares")) { + // BoGo's -key-shares specifies an explicit subset of -curves to + // pre-emptively send key_share entries for. The list must be in + // the same relative order as key_exchange_groups(). + std::vector to_offer; + for(const size_t pref : m_args.get_int_vec_opt("key-shares")) { + const auto group = static_cast(pref); + if(group.to_string().has_value() && group.is_available()) { + to_offer.push_back(group); + } + } + return to_offer; + } + + // Default: offer key shares for the first classical group and the + // first post-quantum group, matching BoringSSL's default heuristic. + std::vector to_offer; + bool have_classical = false; + bool have_pq = false; + + for(auto g : groups) { + if(g.is_post_quantum()) { + if(!have_pq) { + to_offer.push_back(g); + have_pq = true; + } + } else { + if(!have_classical) { + to_offer.push_back(g); + have_classical = true; + } + } + if(have_classical && have_pq) { + break; + } + } + + return to_offer; + } + bool use_ecc_point_compression() const override { return false; } // BoGo expects this Botan::TLS::Group_Params choose_key_exchange_group( @@ -1033,16 +1268,12 @@ require_client_certificate_authentication(); } - bool allow_insecure_renegotiation() const override { - if(m_args.flag_set("expect-no-secure-renegotiation")) { - return true; - } else { - return false; - } - } + bool allow_insecure_renegotiation() const override { return m_args.flag_set("expect-no-secure-renegotiation"); } //bool include_time_in_hello_random() const override; + uint64_t minimum_key_update_interval_ms() const override { return 0; } + bool allow_client_initiated_renegotiation() const override { if(m_args.flag_set("renegotiate-freely")) { return true; @@ -1062,7 +1293,7 @@ bool allow_version(Botan::TLS::Protocol_Version version) const { if(m_args.option_used("min-version")) { const uint16_t min_version_16 = static_cast(m_args.get_int_opt("min-version")); - Botan::TLS::Protocol_Version min_version(min_version_16 >> 8, min_version_16 & 0xFF); + const Botan::TLS::Protocol_Version min_version(min_version_16 >> 8, min_version_16 & 0xFF); if(min_version > version) { return false; } @@ -1070,7 +1301,7 @@ if(m_args.option_used("max-version")) { const uint16_t max_version_16 = static_cast(m_args.get_int_opt("max-version")); - Botan::TLS::Protocol_Version max_version(max_version_16 >> 8, max_version_16 & 0xFF); + const Botan::TLS::Protocol_Version max_version(max_version_16 >> 8, max_version_16 & 0xFF); if(version > max_version) { return false; } @@ -1080,6 +1311,26 @@ } bool allow_tls12() const override { + // Botan implements RFC 7250 raw public keys only in its TLS 1.3 code + // path. When a test configures RawPublicKey as an accepted peer + // certificate type on the client side, disable TLS 1.2 so the TLS 1.3 + // ClientHello builder does not filter RawPublicKey out of the + // advertised certificate_type extension. + if(m_args.option_used("accepted-peer-cert-types") && !m_args.flag_set("server")) { + for(const size_t t : m_args.get_int_vec_opt("accepted-peer-cert-types")) { + if(static_cast(t) == Botan::TLS::Certificate_Type::RawPublicKey) { + return false; + } + } + } + // Likewise, when we have an RPK credential configured (server side or + // when the client uses -new-rpk-credential), disable TLS 1.2 so the + // ClientHello builder emits the certificate_type extensions. + for(const auto& a : m_args.raw_argv()) { + if(a == "-new-rpk-credential") { + return false; + } + } return !m_args.flag_set("dtls") && !m_args.flag_set("no-tls12") && allow_version(Botan::TLS::Protocol_Version::TLS_V12); } @@ -1118,7 +1369,7 @@ std::vector srtp_profiles() const override { if(m_args.option_used("srtp-profiles")) { - std::string srtp = m_args.get_string_opt("srtp-profiles"); + const std::string srtp = m_args.get_string_opt("srtp-profiles"); if(srtp == "SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32") { return {1, 2}; @@ -1138,6 +1389,8 @@ //bool negotiate_encrypt_then_mac() const override; + bool require_extended_master_secret() const override { return false; } + bool support_cert_status_message() const override { if(m_args.flag_set("server")) { if(!m_args.option_used("ocsp-response")) { @@ -1162,15 +1415,69 @@ //size_t dtls_maximum_timeout() const override; bool abort_connection_on_undesired_renegotiation() const override { - if(m_args.flag_set("renegotiate-ignore")) { - return false; - } else { - return true; - } + return !m_args.flag_set("renegotiate-ignore"); } size_t maximum_certificate_chain_size() const override { return m_args.get_int_opt_or_else("max-cert-list", 0); } + std::vector accepted_client_certificate_types() const override { + if(m_args.option_used("accepted-peer-cert-types") && m_args.flag_set("server")) { + std::vector types; + for(const size_t t : m_args.get_int_vec_opt("accepted-peer-cert-types")) { + types.push_back(static_cast(t)); + } + return types; + } + // As a client, advertise the cert types we have credentials for. + if(!m_args.flag_set("server")) { + if(auto types = configured_credential_types(); !types.empty()) { + return types; + } + } + return Botan::TLS::Policy::accepted_client_certificate_types(); + } + + std::vector accepted_server_certificate_types() const override { + if(m_args.option_used("accepted-peer-cert-types") && !m_args.flag_set("server")) { + std::vector types; + for(const size_t t : m_args.get_int_vec_opt("accepted-peer-cert-types")) { + types.push_back(static_cast(t)); + } + return types; + } + // As a server, advertise the cert types we have credentials for. + if(m_args.flag_set("server")) { + if(auto types = configured_credential_types(); !types.empty()) { + return types; + } + } + return Botan::TLS::Policy::accepted_server_certificate_types(); + } + + private: + // Scan raw argv for `-new-x509-credential` / `-new-rpk-credential` block markers + // and report which Certificate_Types are backed by available credentials, + // preserving the order in which credentials were configured (the first + // credential is the most-preferred one). + std::vector configured_credential_types() const { + std::vector types; + for(const auto& a : m_args.raw_argv()) { + Botan::TLS::Certificate_Type t = Botan::TLS::Certificate_Type::X509; + if(a == "-new-rpk-credential") { + t = Botan::TLS::Certificate_Type::RawPublicKey; + } else if(a == "-new-x509-credential") { + t = Botan::TLS::Certificate_Type::X509; + } else { + continue; + } + if(std::find(types.begin(), types.end(), t) == types.end()) { + types.push_back(t); + } + } + return types; + } + + public: bool tls_13_middlebox_compatibility_mode() const override { // These tests expect the client to send an alert in return of a malformed TLS 1.2 server hello. // However, our TLS 1.3 implementation produces an alert without downgrading to TLS 1.2 first. @@ -1185,11 +1492,7 @@ "MinimumVersion-Client-TLS13-TLS12-TLS", "MinimumVersion-Client2-TLS13-TLS12-TLS", }; - if(Botan::value_exists(alert_after_server_hello, m_args.test_name())) { - return false; - } - - return true; + return !Botan::value_exists(alert_after_server_hello, m_args.test_name()); } private: @@ -1203,7 +1506,7 @@ const std::string cipher_limit = m_args.get_string_opt_or_else("cipher", ""); if(cipher_limit == "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:[TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384|TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256|TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA]:TLS_RSA_WITH_AES_128_GCM_SHA256:TLS_RSA_WITH_AES_128_CBC_SHA:[TLS_RSA_WITH_AES_256_GCM_SHA384|TLS_RSA_WITH_AES_256_CBC_SHA]") { - std::vector suites = { + const std::vector suites = { "ECDHE_RSA_WITH_AES_128_GCM_SHA256", "ECDHE_RSA_WITH_AES_256_GCM_SHA384", "ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", @@ -1222,16 +1525,17 @@ } else { // Hack: go in reverse order to avoid preferring 3DES auto ciphersuites = Botan::TLS::Ciphersuite::all_known_ciphersuites(); + // TODO(Botan4) use std::ranges::reverse_view here once available (need newer Clang) + // NOLINTNEXTLINE(modernize-loop-convert) for(auto i = ciphersuites.rbegin(); i != ciphersuites.rend(); ++i) { const auto suite = *i; - // Can we use it? - if(suite.valid() == false || !suite.usable_in_version(version) || - !Botan::value_exists(allowed_ciphers(), suite.cipher_algo())) { - continue; - } + const bool usable = suite.valid() && suite.usable_in_version(version) && + Botan::value_exists(allowed_ciphers(), suite.cipher_algo()); - ciphersuite_codes.push_back(suite.ciphersuite_code()); + if(usable) { + ciphersuite_codes.push_back(suite.ciphersuite_code()); + } } } @@ -1240,7 +1544,7 @@ class Shim_Credentials final : public Botan::Credentials_Manager { public: - Shim_Credentials(const Shim_Arguments& args) : m_args(args) { + explicit Shim_Credentials(const Shim_Arguments& args) : m_args(args) { const auto psk_identity = m_args.get_string_opt_or_else("psk-identity", ""); const auto psk_str = m_args.get_string_opt_or_else("psk", ""); @@ -1254,17 +1558,23 @@ m_psk = Botan::SymmetricKey(reinterpret_cast(psk_str.data()), psk_str.size()); } - if(m_args.option_used("key-file") && m_args.option_used("cert-file")) { - Botan::DataSource_Stream key_stream(m_args.get_string_opt("key-file")); - m_key.reset(Botan::PKCS8::load_key(key_stream).release()); - - Botan::DataSource_Stream cert_stream(m_args.get_string_opt("cert-file")); - - while(!cert_stream.end_of_data()) { - try { - m_cert_chain.push_back(Botan::X509_Certificate(cert_stream)); - } catch(...) {} - } + m_credentials = parse_credential_blocks(m_args.raw_argv()); + for(auto& cred : m_credentials) { + load_credential(cred); + } + + // Legacy default cert/key (no `-new-*-credential` block) — only honored + // when no X509/RPK credential blocks were provided. + const bool has_block_cert = std::any_of(m_credentials.begin(), m_credentials.end(), [](const auto& c) { + return c.kind == Shim_Credential::Kind::X509 || c.kind == Shim_Credential::Kind::RPK; + }); + if(!has_block_cert && m_args.option_used("key-file") && m_args.option_used("cert-file")) { + Shim_Credential cred; + cred.kind = Shim_Credential::Kind::X509; + cred.key_file = m_args.get_string_opt("key-file"); + cred.cert_file = m_args.get_string_opt("cert-file"); + load_credential(cred); + m_credentials.push_back(std::move(cred)); } if(m_args.option_used("trust-cert") && !m_args.get_string_opt("trust-cert").empty()) { @@ -1277,6 +1587,31 @@ } } + private: + static void load_credential(Shim_Credential& cred) { + if(cred.kind == Shim_Credential::Kind::PSK) { + return; + } + if(cred.key_file.empty()) { + return; + } + Botan::DataSource_Stream key_stream(cred.key_file); + cred.key.reset(Botan::PKCS8::load_key(key_stream).release()); + + if(cred.kind == Shim_Credential::Kind::X509) { + Botan::DataSource_Stream cert_stream(cred.cert_file); + while(!cert_stream.end_of_data()) { + try { + cred.cert_chain.emplace_back(cert_stream); + } catch(...) {} + } + } else { + // RPK: derive the public key from the loaded private key. + cred.raw_public_key = cred.key->public_key(); + } + } + + public: std::vector trusted_certificate_authorities(const std::string& type, const std::string& context) override { if(m_args.flag_set("server") && type != "tls-server") { @@ -1317,6 +1652,41 @@ Botan::TLS::Connection_Side whoami, const std::vector& identities = {}, const std::optional& prf = std::nullopt) override { + std::vector psks; +#if defined(BOTAN_HAS_TLS_13) + // TLS 1.3 PSK credentials from -new-psk-credential blocks + const Botan::TLS::Protocol_Version target_version(Botan::TLS::Protocol_Version::TLS_V13); + bool any_psk_block = false; + + for(const auto& cred : m_credentials) { + if(cred.kind != Shim_Credential::Kind::PSK) { + continue; + } + any_psk_block = true; + const Botan::TLS::PSKImporter importer( + cred.psk_key, cred.psk_identity, cred.psk_context, cred.psk_hash.empty() ? "SHA-256" : cred.psk_hash); + + // Import each credential against both SHA-256 and SHA-384 cipher suites. + for(const auto& target_hash : {"SHA-256", "SHA-384"}) { + if(prf.has_value() && *prf != target_hash) { + continue; + } + + auto imported = importer.derive_imported_psk(target_version, target_hash); + + if(!identities.empty() && + std::find(identities.begin(), identities.end(), imported.identity()) == identities.end()) { + continue; + } + + psks.push_back(std::move(imported)); + } + } + if(any_psk_block) { + return psks; + } +#endif + // Legacy TLS 1.2 PSK from -psk / -psk-identity flags if(!m_psk_identity.has_value()) { return Botan::Credentials_Manager::find_preshared_keys(host, whoami, identities, prf); } @@ -1332,52 +1702,82 @@ throw Shim_Exception("PSK identified but not set"); } - std::vector psks; - - // Currently, BoGo tests PSK with TLS 1.2 only. In TLS 1.2 the PRF does not - // need to be specified for PSKs. - // - // TODO: Once BoGo has tests for TLS 1.3 with externally provided PSKs, this - // will need to be handled somehow. const std::string psk_prf = "SHA-256"; psks.emplace_back(m_psk_identity.value(), psk_prf, m_psk->bits_of()); return psks; } - std::vector cert_chain( + std::vector find_cert_chain( const std::vector& cert_key_types, const std::vector& /*cert_signature_schemes*/, + const std::vector& /*acceptable_CAs*/, const std::string& /*type*/, const std::string& /*context*/) override { if(m_args.flag_set("fail-cert-callback")) { throw std::runtime_error("Simulating cert verify callback failure"); } - if(m_key != nullptr && !m_cert_chain.empty()) { - for(const std::string& t : cert_key_types) { - if(t == m_key->algo_name()) { - return m_cert_chain; - } + for(const auto& cred : m_credentials) { + if(cred.kind != Shim_Credential::Kind::X509 || cred.key == nullptr || cred.cert_chain.empty()) { + continue; + } + if(cert_key_types.empty() || + std::find(cert_key_types.begin(), cert_key_types.end(), cred.key->algo_name()) != cert_key_types.end()) { + return cred.cert_chain; } } return {}; } - std::shared_ptr private_key_for(const Botan::X509_Certificate& /*cert*/, + std::shared_ptr find_raw_public_key(const std::vector& key_types, + const std::string& /*type*/, + const std::string& /*context*/) override { + for(const auto& cred : m_credentials) { + if(cred.kind != Shim_Credential::Kind::RPK || cred.raw_public_key == nullptr) { + continue; + } + if(key_types.empty() || + std::find(key_types.begin(), key_types.end(), cred.raw_public_key->algo_name()) != key_types.end()) { + return cred.raw_public_key; + } + } + return nullptr; + } + + std::shared_ptr private_key_for(const Botan::X509_Certificate& cert, const std::string& /*type*/, const std::string& /*context*/) override { - // assumes cert == m_cert - return m_key; + for(const auto& cred : m_credentials) { + if(cred.kind == Shim_Credential::Kind::X509 && !cred.cert_chain.empty() && + cred.cert_chain.front() == cert) { + return cred.key; + } + } + return nullptr; + } + + std::shared_ptr private_key_for(const Botan::Public_Key& raw_public_key, + const std::string& /*type*/, + const std::string& /*context*/) override { + const auto wanted = raw_public_key.public_key_bits(); + for(const auto& cred : m_credentials) { + if(cred.kind != Shim_Credential::Kind::RPK || cred.raw_public_key == nullptr) { + continue; + } + if(cred.raw_public_key->public_key_bits() == wanted) { + return cred.key; + } + } + return nullptr; } private: const Shim_Arguments& m_args; std::optional m_psk; std::optional m_psk_identity; - std::shared_ptr m_key; - std::vector m_cert_chain; Botan::Certificate_Store_In_Memory m_trust_roots; + std::vector m_credentials; }; class Shim_Callbacks final : public Botan::TLS::Callbacks { @@ -1426,8 +1826,8 @@ } } - std::vector tls_provide_cert_status(const std::vector&, - const Botan::TLS::Certificate_Status_Request&) override { + std::vector tls_provide_cert_status(const std::vector& /*certs*/, + const Botan::TLS::Certificate_Status_Request& /*status*/) override { if(m_args.flag_set("use-ocsp-callback") && m_args.flag_set("fail-ocsp-callback")) { throw std::runtime_error("Simulating failure from OCSP response callback"); } @@ -1508,8 +1908,8 @@ } if(!cert_chain.empty() && cert_chain.front().is_self_signed()) { - for(const auto roots : trusted_roots) { - if(roots->certificate_known(cert_chain.front())) { + for(auto* const roots : trusted_roots) { + if(roots->contains(cert_chain.front())) { shim_log("Trusting self-signed certificate"); return; } @@ -1522,6 +1922,31 @@ cert_chain, ocsp_responses, trusted_roots, usage, "" /* hostname */, policy); } + void tls_verify_raw_public_key(const Botan::Public_Key& raw_public_key, + Botan::Usage_Type /*usage*/, + std::string_view /*hostname*/, + const Botan::TLS::Policy& /*policy*/) override { + if(m_args.flag_set("verify-fail")) { + auto alert = Botan::TLS::Alert::HandshakeFailure; + if(m_args.flag_set("use-custom-verify-callback")) { + alert = Botan::TLS::Alert::CertificateUnknown; + } + throw Botan::TLS::TLS_Exception(alert, "Test requires rejecting cert"); + } + + if(m_args.option_used("expect-peer-rpk-sha256")) { + const auto expected = m_args.get_b64_opt("expect-peer-rpk-sha256"); + const auto spki = raw_public_key.subject_public_key(); + auto sha256 = Botan::HashFunction::create_or_throw("SHA-256"); + sha256->update(spki); + const auto digest = sha256->final_stdvec(); + if(digest != expected) { + throw Botan::TLS::TLS_Exception(Botan::TLS::Alert::CertificateUnknown, + "Raw public key SHA-256 did not match -expect-peer-rpk-sha256"); + } + } + } + std::optional tls_parse_ocsp_response(const std::vector& raw_response) override { if(m_args.option_used("expect-ocsp-response") && m_args.get_b64_opt("expect-ocsp-response") != raw_response) { shim_exit_with_error("unexpected OCSP response"); @@ -1607,7 +2032,7 @@ } if(alert.type() == Botan::TLS::Alert::CloseNotify) { - if(m_got_close == false && !m_args.flag_set("shim-shuts-down")) { + if(!m_got_close && !m_args.flag_set("shim-shuts-down")) { shim_log("Sending return close notify"); m_channel->send_alert(alert); } @@ -1619,8 +2044,8 @@ void tls_session_established(const Botan::TLS::Session_Summary& session) override { shim_log("Session established: " + Botan::hex_encode(session.session_id().get()) + " version " + - session.version().to_string() + " cipher " + session.ciphersuite().to_string() + " EMS " + - std::to_string(session.supports_extended_master_secret())); + session.version().to_string() + " cipher " + session.ciphersuite().to_string() + " " + + std::string((session.supports_extended_master_secret() ? "with EMS" : "without EMS"))); // probably need tests here? m_policy.incr_session_established(); @@ -1642,17 +2067,17 @@ } if(m_args.flag_set("expect-secure-renegotiation")) { - if(m_channel->secure_renegotiation_supported() == false) { + if(!m_channel->secure_renegotiation_supported()) { shim_exit_with_error("Expected secure renegotiation"); } } else if(m_args.flag_set("expect-no-secure-renegotiation")) { - if(m_channel->secure_renegotiation_supported() == true) { - shim_exit_with_error("Expected no secure renegotation"); + if(m_channel->secure_renegotiation_supported()) { + shim_exit_with_error("Expected no secure renegotiation"); } } if(m_args.flag_set("expect-extended-master-secret")) { - if(session.supports_extended_master_secret() == false) { + if(!session.supports_extended_master_secret()) { shim_exit_with_error("Expected extended maseter secret"); } } @@ -1664,7 +2089,7 @@ return; } - if(size_t length = m_args.get_int_opt_or_else("export-keying-material", 0)) { + if(const size_t length = m_args.get_int_opt_or_else("export-keying-material", 0)) { const std::string label = m_args.get_string_opt("export-label"); const std::string context = m_args.get_string_opt("export-context"); const auto exported = m_channel->key_material_export(label, context, length); @@ -1680,10 +2105,6 @@ } } - if(alpn == "baz" && !m_args.flag_set("allow-unknown-alpn-protos")) { - throw Botan::TLS::TLS_Exception(Botan::TLS::Alert::IllegalParameter, "Unexpected ALPN protocol"); - } - if(m_args.flag_set("shim-shuts-down")) { shim_log("Shim shutting down"); m_channel->close(); @@ -1694,7 +2115,7 @@ std::vector buf(32769, 0x42); - for(size_t sz : record_sizes) { + for(const size_t sz : record_sizes) { m_channel->send(buf.data(), sz); } @@ -1790,7 +2211,7 @@ // *before* any test data is transferred // See: https://github.com/google/boringssl/commit/50ee09552cde1c2019bef24520848d041920cfd4 shim_log("Sending ShimID: " + std::to_string(args->get_int_opt("shim-id"))); - std::array shim_id; + std::array shim_id{}; Botan::store_le(static_cast(args->get_int_opt("shim-id")), shim_id.data()); socket.write(shim_id.data(), shim_id.size()); @@ -1807,7 +2228,7 @@ if(is_server) { chan = std::make_unique(callbacks, session_manager, creds, policy, rng, is_datagram); } else { - Botan::TLS::Protocol_Version offer_version = policy->latest_supported_version(is_datagram); + const Botan::TLS::Protocol_Version offer_version = policy->latest_supported_version(is_datagram); shim_log("Offering " + offer_version.to_string()); std::string host_name = args->get_string_opt_or_else("host-name", hostname); @@ -1815,7 +2236,7 @@ host_name = ""; // avoid sending SNI for this test } - Botan::TLS::Server_Information server_info(host_name, port); + const Botan::TLS::Server_Information server_info(host_name, port); const std::vector next_protocols = args->get_alpn_string_vec_opt("advertise-alpn"); chan = std::make_unique( callbacks, session_manager, creds, policy, rng, server_info, offer_version, next_protocols); @@ -1827,8 +2248,8 @@ for(;;) { if(is_datagram) { - uint8_t opcode; - size_t got = socket.read(&opcode, 1); + uint8_t opcode = 0; + const size_t got = socket.read(&opcode, 1); if(got == 0) { shim_log("EOF on socket"); break; @@ -1838,7 +2259,7 @@ uint8_t len_bytes[4]; socket.read_exactly(len_bytes, sizeof(len_bytes)); - size_t packet_len = Botan::load_be(len_bytes, 0); + const size_t packet_len = Botan::load_be(len_bytes, 0); if(buf.size() < packet_len) { buf.resize(packet_len); @@ -1847,7 +2268,7 @@ chan->received_data(buf.data(), packet_len); } else if(opcode == 'T') { - uint8_t timeout_ack = 't'; + const uint8_t timeout_ack = 't'; uint8_t timeout_bytes[8]; socket.read_exactly(timeout_bytes, sizeof(timeout_bytes)); @@ -1863,7 +2284,7 @@ shim_exit_with_error("Unknown opcode " + std::to_string(opcode)); } } else { - size_t got = socket.read(buf.data(), buf.size()); + const size_t got = socket.read(buf.data(), buf.size()); if(got == 0) { shim_log("EOF on socket"); break; @@ -1883,7 +2304,7 @@ } const size_t needed = chan->received_data(buf.data(), got); - if(needed) { + if(needed > 0) { shim_log("Short read still need " + std::to_string(needed)); } } diff -Nru botan3-3.7.1+dfsg/src/bogo_shim/config.json botan3-3.12.0+dfsg/src/bogo_shim/config.json --- botan3-3.7.1+dfsg/src/bogo_shim/config.json 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/bogo_shim/config.json 2026-05-07 01:38:28.000000000 +0000 @@ -8,7 +8,6 @@ "InvalidECDHPoint-Server": "Unexpected error", "NoSharedCipher": "Unexpected error", "NoSharedCipher-TLS13": "Unexpected error", - "PartialFinishedWithServerHelloDone": "Unexpected record vs excess handshake data", "HelloRetryRequest-DuplicateCurve-TLS13": "expects 'illegal parameter' but we want to stick with 'decode error'", "HelloRetryRequest-DuplicateCookie-TLS13": "expects 'illegal parameter' but we want to stick with 'decode error'", @@ -16,13 +15,11 @@ "ClientSkipCertificateVerify-TLS13": "would require ambiguous error mapping", "Resume-Client-Mismatch-TLS13-TLS12-TLS": "server requests a downgrade to TLS 1.2, echoing the random session ID during a TLS 1.3 resumption. => error mapping conflict", "ServerAuth-NoFallback-TLS13": "would require ambiguous error mapping", - "TLS-TLS13-PSK_WITH_AES_128_CBC_SHA-server": "expects a different error for better coverage of Boring SSL's code base", "TLS-TLS13-PSK_WITH_AES_256_CBC_SHA-server": "expects a different error for better coverage of Boring SSL's code base", "TLS-TLS13-ECDHE_PSK_WITH_AES_128_CBC_SHA-server": "expects a different error for better coverage of Boring SSL's code base", "TLS-TLS13-ECDHE_PSK_WITH_AES_256_CBC_SHA-server": "expects a different error for better coverage of Boring SSL's code base", "TLS-TLS13-ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256-server": "expects a different error for better coverage of Boring SSL's code base", - "CertificateVerificationFail-Server-TLS12-TLS-Sync": "too picky TLS alert", "CertificateVerificationFail-Server-TLS12-CustomCallback-TLS-Sync": "too picky TLS alert", "CertificateVerificationFail-Server-TLS12-TLS-Sync-ImplicitHandshake": "too picky TLS alert", @@ -46,90 +43,104 @@ "CertificateVerificationFail-Server-TLS12-CustomCallback-DTLS-Sync-PackHandshake": "too picky TLS alert", "CertificateVerificationFail-Server-TLS12-DTLS-Sync-PackHandshake": "too picky TLS alert", "CertificateVerificationFail-Server-TLS13-DTLS-Sync-PackHandshake": "too picky TLS alert", - "CertificateVerificationFail-Server-TLS13-CustomCallback-DTLS-Sync-PackHandshake": "too picky TLS alert" + "CertificateVerificationFail-Server-TLS13-CustomCallback-DTLS-Sync-PackHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-TLS-Sync": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-CustomCallback-TLS-Sync": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-TLS-Sync-ImplicitHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-CustomCallback-TLS-Sync-ImplicitHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-TLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-CustomCallback-TLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Client-TLS12-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Client-TLS12-CustomCallback-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Client-TLS13-CustomCallback-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Client-TLS13-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "ClientOCSPCallback-FailNoStaple-TLS12-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "ClientOCSPCallback-FailNoStaple-TLS13-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "WrongMessageType-TLS13-ClientCertificate-TLS": "too picky TLS alert", + "TrailingMessageData-TLS13-ClientCertificate-TLS": "too picky TLS alert", + "GarbageCertificate-Server-TLS13": "too picky TLS alert", + "AppDataBeforeTLS13KeyChange": "too picky TLS alert", + "UnencryptedEncryptedExtensions": "Botan sends unexpected_message alert, BoGo expects bad_record_mac", + "TrustAnchors-Unsolicited-Certificate": "Botan sends illegal_parameter alert, BoGo expects unsupported_extension", + "Resume-Server-OmitAllPSKsOnSecondClientHello": "Botan reports inconsistent_client_hello, BoGo expects missing_extension", + "PSK-Server-OmitAllPSKsOnSecondClientHello-TLS": "Botan reports inconsistent_client_hello, BoGo expects missing_extension", + "PSK-Server-HRR-PSKMissing-TLS": "Botan sends handshake_failure alert, BoGo expects illegal_parameter", + "PSK-Server-MissingPSKMode-NoMatch-TLS": "Botan reports PSK_IDENTITY_NOT_FOUND, BoGo expects NO_SUPPORTED_PSK_MODE", + "ExtensionTrailingData-ServerName-Client-TLS-TLS12": "Botan sends illegal_parameter for malformed server SNI; BoGo expects decode_error", + "ExtensionTrailingData-ServerName-Client-TLS-TLS13": "Botan sends illegal_parameter for malformed server SNI; BoGo expects decode_error", + "ExtensionTrailingData-ServerName-Client-DTLS-TLS12": "Botan sends illegal_parameter for malformed server SNI; BoGo expects decode_error", + "ClientCertificateType-Server-RejectsInvalidEmptyExtension-VerifyPeer-TLS13": "Botan sends decode_error, BoGo expects illegal_parameter", + "ClientCertificateType-Server-RejectsInvalidEmptyExtension-FailIfNoClientCert-TLS13": "Botan sends decode_error, BoGo expects illegal_parameter", + "ClientCertificateType-Server-RejectsInvalidDefaultOnly-FailIfNoClientCert-TLS13": "Botan reports PEER_DID_NOT_RETURN_A_CERTIFICATE, BoGo expects DECODE_ERROR", + "ClientCertificateType-Server-NoClientHelloCertTypes-NoSharedType-FailIfNoClientCert-TLS13": "Botan reports PEER_DID_NOT_RETURN_A_CERTIFICATE, BoGo expects UNSUPPORTED_CERTIFICATE", + "ServerCertificateType-Client-RequestsRPKOnly-NegotiatedRPK-ServerIncorrectlySentX509-TLS13": "Botan parses RPK as X.509 and fails with BER decode error rather than DECODE_ERROR cleanly", + "ServerCertificateType-Client-RequestsRPKOnly-ServerSentEmptyRPK-TLS13": "Empty RPK rejected with generic ASN.1 error rather than INVALID_RAW_PUBLIC_KEY", + "ClientCertificateType-Server-RPKReceived-RPKAccepted-ClientSentX509InError-VerifyPeer-TLS13": "Botan parses RPK as X.509 and fails with BER decode error rather than DECODE_ERROR cleanly", + "ClientCertificateType-Server-RPKReceived-RPKAccepted-ClientSentX509InError-FailIfNoClientCert-TLS13": "Botan parses RPK as X.509 and fails with BER decode error rather than DECODE_ERROR cleanly", + "ClientCertificateType-Server-ClientSentEmptyRPK-TLS13": "Empty RPK rejected with generic ASN.1 error rather than INVALID_RAW_PUBLIC_KEY" }, - "DisabledTests": { + "*ML-DSA*": "Need support for the LAMPS tagged seed keys", "*TLS1": "No TLS 1.0", "*-TLS1-*": "No TLS 1.0", "*-TLS10-*": "No TLS 1.0", "TLS1-*": "No TLS 1.0", "VersionNegotiation*-TLS": "No TLS 1.0", "VersionNegotiation*-DTLS": "No DTLS 1.0", - "*TLS11": "No TLS 1.1", "*-TLS11-*": "No TLS 1.1", "TLS11-*": "No TLS 1.1", - "*DTLS13*": "No DTLS 1.3", "DTLS-TLS13*": "No DTLS 1.3", "*TLS13-DTLS": "No DTLS 1.3", "*DTLS-TLS13": "No DTLS 1.3", "TLS13*-DTLS-*": "No DTLS 1.3", "MinimumVersion-*-TLS13-*DTLS": "No DTLS 1.3", - "*RSA_PKCS1_MD5_SHA1": "We do not implement MD5/SHA1 concatenation anyway", "*RSA_PKCS1_SHA1*": "We do not implement PKCS1 SHA-1", "*-ECDSA_SHA1-*": "We do not implement ECDSA SHA-1", "*RSA_PKCS1_SHA256_LEGACY-TLS13": "We do allow for PKCS1 in TLS 1.3", - "Compliance-fips202205-*": "We do not have explicit support for a FIPS TLS policy", "Compliance-fips-202205-*": "We do not have explicit support for a FIPS TLS policy", "Compliance-wpa-202304-*": "We do not have explicit support for the WPA Enterprise mode", "Compliance-cnsa202407-*": "We do not have explicit support for CNSA", - "CBCRecordSplitting*": "No need to split CBC records in TLS 1.2", "DelegatedCredentials*": "No support of -delegated-cerdential", - "*SCSV*": "SCSV is meaningless without TLS 1.0/1.1 support", - "AllExtensions-*": "Not all extensions are implemented", - "VersionTolerance-TLS13": "We are not tolerating 0x0400 as Client Hello legacy_version", - "Server-JDK11-*": "We don't implement JDK-specific workarounds", "Client-RejectJDK11DowngradeRandom": "We don't implement this workaround", "ExportTrafficSecrets-*": "Exporting traffic secrets is not implemented", "TooManyChangeCipherSpec-Client-TLS13": "Limits on the number of CCS are not implemented", "TooManyChangeCipherSpec-Server-TLS13": "Limits on the number of CCS are not implemented", - "TooManyKeyUpdates": "Limits on the number of KeyUpdates are not implemented", - "PostQuantumNotEnabledByDefaultInClients": "Oh yes it is", - "TLS12SessionID-TLS13": "We don't offer TLS 1.3 when a TLS 1.2 session was found", "Ticket-Forbidden-TLS13": "We don't offer TLS 1.3 when a TLS 1.2 session was found", "Resume-Client-NoResume-TLS12-TLS13-TLS": "We don't offer TLS 1.3 when a TLS 1.2 session was found", "Resume-Client-Mismatch-TLS12-TLS13-TLS": "We don't offer TLS 1.3 when a TLS 1.2 session was found", "Resume-Server-UnofferedCipher-TLS13": "BoringSSL will not allow switching ciphers during TLS 1.3 resumption, we do, though.", - "HttpGET": "TLS 1.3 server does not detect HTTP", "HttpPOST": "TLS 1.3 server does not detect HTTP", "HttpPUT": "TLS 1.3 server does not detect HTTP", "HttpHEAD": "TLS 1.3 server does not detect HTTP", "HttpCONNECT": "TLS 1.3 server does not detect HTTP", - "*EarlyData*": "No TLS 1.3 Early Data, yet", "TLS13-TicketAgeSkew-*": "No TLS 1.3 Early Data, yet", "ExportKeyingMaterial-Server-HalfRTT-TLS13": "No TLS 1.3 Early Data, yet", "EarlyDataEnabled*": "No TLS 1.3 Early Data, yet", "EarlyData-Reject0RTT*": "No TLS 1.3 Early Data, yet", "PartialEndOfEarlyDataWithClientHello": "No TLS 1.3 Early Data, yet", - "SendNoClientCertificateExtensions-TLS13": "-signed-cert-timestamps currently not supported in the shim", "KeyUpdate-RequestACK-UnfinishedWrite": "-read-with-unfinished-write currently not supported in the shim", - "TLS-ECH*": "No ECH support", "ECH*": "No ECH support", - "DuplicateCertCompressionExt*": "No support for 1.3 cert compression extension", "CertCompression*-TLS13": "No support for 1.3 cert compression extension", - "SupportedVersionSelection-TLS12": "We just ignore the version extension in this case", "NoCommonSignatureAlgorithms-TLS12-Fallback": "Fallback behaviour not implemented by shim", "CheckClientCertificateTypes": "Client certificate type check is a library-user responsibility", - "Downgrade-*-Client-Ignore": "Not possible to ignore downgrade indicator", - "Agree-Digest-SHA1": "No SHA-1 in TLS 1.2", "ServerAuth-SHA1-Fallback-*": "No SHA-1 in TLS 1.2", "*-InvalidSignature-*_SHA1-TLS12": "No SHA-1 in TLS 1.2", @@ -137,24 +148,20 @@ "*-Sign-Negotiate-*_SHA1-TLS12": "No SHA-1 in TLS 1.2", "*-VerifyDefault-*_SHA1-TLS12": "No SHA-1 in TLS 1.2", "*-Verify-*_SHA1-TLS12": "No SHA-1 in TLS 1.2", - "*QUIC*": "No QUIC", "ALPS*": "No ALPS", "ExtraClientEncryptedExtension-*": "No ALPS", - "*NPN*": "No support for NPN", "ALPNServer-Preferred-*": "No support for NPN", "*-NextProtocol*": "No support for NPN", - + "TooManyKeyUpdates": "BoringSSL's approach to KeyUpdate throttling is ineffective and pointless", "*SignedCertificateTimestamp*": "No support for SCT", "*SCT*": "No support for SCT", "Renegotiation-ChangeAuthProperties": "No support for SCT", "UnsolicitedCertificateExtensions-*": "No support for SCT", "IgnoreExtensionsOnIntermediates-TLS13": "No support for SCT", "SendNoExtensionsOnIntermediate-TLS13": "No support for SCT", - - "CertificateVerificationSoftFail*": "Fail, but don't fail... wtf?", - + "CertificateVerificationSoftFail*": "Fail, but don't fail... wtf?", "*NULL-SHA*": "No support for NULL ciphers", "*WITH_NULL*": "No support for NULL ciphers", "*GREASE*": "No support for GREASE", @@ -170,107 +177,144 @@ "*FalseStart*": "Botan doesn't do false start", "MaxSendFragment*": "Maximum fragment extension not supported", "ExportKeyingMaterial-EmptyContext*": "No support for empty context", - "Peek-*": "No peek API", "*OldCallback*": "BoringSSL specific API test", - "*Renegotiate-Client-Explicit*": "BoringSSL specific API test", + "*Renegotiate-Client-Explicit*": "BoringSSL specific API test", "CBCRecordSplittingPartialWrite*": "BoringSSL specific API test", "TicketCallback*": "BoringSSL specific API test", "Server-DDoS*": "BoringSSL specific API test", "RetainOnlySHA256-*": "BoringSSL specific API test", "Renegotiate-Client-UnfinishedWrite": "BoringSSL specific API test", "FailEarlyCallback": "BoringSSL specific API test", - - "MLKEMKeyShareIncludedSecond": "BoringSSL specific policy test (we may offer solo PQ/T groups)", - "NotJustMLKEMKeyShare": "BoringSSL specific policy test (we may offer solo PQ/T groups)", - "MLKEMKeyShareIncludedThird": "BoringSSL specific policy test (we may offer solo PQ/T groups)", - "NotJustKyberKeyShare": "BoringSSL specific policy test (we may offer solo PQ/T groups)", - "KyberKeyShareIncludedSecond": "BoringSSL specific policy test (we may offer solo PQ/T groups)", - "KyberKeyShareIncludedThird": "BoringSSL specific policy test (we may offer solo PQ/T groups)", "CurveTest-*Kyber*": "We no longer support Kyber r3 key exchange", - "ShimTicketRewritable": "Botan has a different ticket format", "Resume-Server-DeclineCrossVersion*": "Botan has a different ticket format", "Resume-Server-DeclineBadCipher*": "Botan has a different ticket format", "Resume-Server-CipherNotPreferred*": "Botan has a different ticket format", - "TLS*-NoTicket-NoAccept": "BoGo expects that if ticket is issued stateful resumption is impossible", - "CheckLeafCurve": "Botan doesn't care what curve an ECDSA cert uses", "CheckECDSACurve-TLS12": "Botan doesn't care what curve an ECDSA cert uses", - "CertificateVerificationDoesNotFailOnResume*": "Botan doesn't support reverify on resume", "CertificateVerificationFailsOnResume*": "Botan doesn't support reverify on resume", "CertificateVerificationPassesOnResume*": "Botan doesn't support reverify on resume", - "CipherNegotiation-2": "No support for cipher equivalence classes", "CipherNegotiation-3": "No support for cipher equivalence classes", "CipherNegotiation-4": "No support for cipher equivalence classes", "CipherNegotiation-5": "No support for cipher equivalence classes", "CipherNegotiation-8": "No support for cipher equivalence classes", - "ALPNServer-SelectEmpty-*": "Botan treats empty ALPN from callback as a decline", - "AppDataAfterChangeCipherSpec-DTLS*": "BoringSSL DTLS drops out of order AppData, we reject", - "Resume-Client-NoResume-TLS1-TLS11-TLS": "BoGo expects resumption attempt sends latest version", "Resume-Client-NoResume-TLS1-TLS12-TLS": "BoGo expects resumption attempt sends latest version", "Resume-Client-NoResume-TLS11-TLS12-TLS": "BoGo expects resumption attempt sends latest version", "Resume-Client-NoResume-TLS1-TLS12-DTLS": "BoGo expects resumption attempt sends latest version", - "Resume-Client-Mismatch-TLS1-TLS11-TLS": "BoGo expects resumption attempt sends latest version", "Resume-Client-Mismatch-TLS1-TLS12-TLS": "BoGo expects resumption attempt sends latest version", "Resume-Client-Mismatch-TLS11-TLS12-TLS": "BoGo expects resumption attempt sends latest version", "Resume-Client-Mismatch-TLS1-TLS12-DTLS": "BoGo expects resumption attempt sends latest version", - "LooseInitialRecordVersion-TLS12": "Botan is somewhat strict about the record version number", - "CurveTest-*-Compressed*": "Point compression is supported, which BoGo doesn't expect", "PointFormat-*-MissingUncompressed": "Point compression is supported, which BoGo doesn't expect", - "RSAPSSSupport-ConfigPSS-NoCerts-TLS12-*": "Needs investigation", "RSAPSSSupport-Default-NoCerts-TLS12-*": "Needs investigation", - "DTLS-Retransmit*": "Shim needs timeout support", - "DTLS-StrayRetransmitFinished-ClientFull": "Needs investigation", "DTLS-StrayRetransmitFinished-ServerResume": "Needs investigation", - "DTLS-Replay-NonMonotonic": "Needs investigation, started failing after https://github.com/google/boringssl/commit/f94f3ed3965ea033001fb9ae006084eee408b861", - "SRTP-Server-IgnoreMKI-*": "Non-empty MKI is rejected (bug)", - "Renegotiate-Client-Packed": "Packing HelloRequest with Finished loses the HelloRequest (bug)", "SendHalfHelloRequest*PackHandshake": "Packing HelloRequest with Finished loses the HelloRequest (bug)", - "PartialClientFinishedWithClientHello": "Need to check for buffered messages when CCS (bug)", "SendUnencryptedFinished-DTLS": "Need to check for buffered messages when CCS (bug)", - "RSAKeyUsage-*-TLS12": "We always enforce key usage", "RSAKeyUsage-Client-WantSignature-GotEncipherment-AlwaysEnforced-TLS13": "We always enforce key usage", - - "AllExtensions-Client-Permute-TLS-TLS12" : "Requires new shim flags that are NYI (as of March 2022)", - "AllExtensions-Client-Permute-DTLS-TLS12" : "Requires new shim flags that are NYI (as of March 2022)", - "EarlyData-WriteAfterEncryptedExtensions" : "Requires new shim flags that are NYI (as of March 2022)", - "EarlyData-WriteAfterServerHello" : "Requires new shim flags that are NYI (as of March 2022)", - "TLS-HintMismatch-Certificate-*" : "Requires new shim flags that are NYI (as of May 2024)", - "TLS-HintMismatch-CipherMismatch1" : "Requires new shim flags that are NYI (as of March 2023)", - "TLS-HintMismatch-CipherMismatch2" : "Requires new shim flags that are NYI (as of March 2023)", - "TLS-HintMismatch-ECDHE-Group" : "Requires new shim flags that are NYI (as of March 2023)", - "TLS-HintMismatch-SignatureInput" : "Requires new shim flags that are NYI (as of March 2022)", - "TLS-HintMismatch-KeyShare" : "Requires new shim flags that are NYI (as of March 2022)", - "TLS-HintMismatch-HandshakerHelloRetryRequest" : "Requires new shim flags that are NYI (as of March 2022)", - "TLS-HintMismatch-ShimHelloRetryRequest" : "Requires new shim flags that are NYI (as of March 2022)", - "TLS-HintMismatch-SignatureAlgorithm-TLS*" : "Requires new shim flags that are NYI (as of March 2022)", - "TLS-HintMismatch-NoTickets1-TLS*" : "Requires new shim flags that are NYI (as of March 2022)", - "TLS-HintMismatch-NoTickets2-TLS*" : "Requires new shim flags that are NYI (as of March 2022)", - "TLS-HintMismatch-Version2" : "Requires new shim flags that are NYI (as of March 2022)", - "TLS-HintMismatch-CertificateRequest" : "Requires new shim flags that are NYI (as of March 2022)", - "TLS-HintMismatch-CertificateCompression-HandshakerOnly" : "Requires new shim flags that are NYI (as of March 2022)", - "TLS-HintMismatch-CertificateCompression-ShimOnly" : "Requires new shim flags that are NYI (as of March 2022)", - "TLS-HintMismatch-CertificateCompression-AlgorithmMismatch" : "Requires new shim flags that are NYI (as of March 2022)", - "TLS-HintMismatch-CertificateCompression-InputMismatch" : "Requires new shim flags that are NYI (as of March 2022)", - "TLS-HintMismatch-Version1" : "Requires new shim flags that are NYI (as of March 2022)", - - "CertificateSelection-*" : "Certificate selection is a library-user responsibility" - } + "AllExtensions-Client-Permute-TLS-TLS12": "Requires new shim flags that are NYI (as of March 2022)", + "AllExtensions-Client-Permute-DTLS-TLS12": "Requires new shim flags that are NYI (as of March 2022)", + "EarlyData-WriteAfterEncryptedExtensions": "Requires new shim flags that are NYI (as of March 2022)", + "EarlyData-WriteAfterServerHello": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-Certificate-*": "Requires new shim flags that are NYI (as of May 2024)", + "TLS-HintMismatch-CipherMismatch1": "Requires new shim flags that are NYI (as of March 2023)", + "TLS-HintMismatch-CipherMismatch2": "Requires new shim flags that are NYI (as of March 2023)", + "TLS-HintMismatch-ECDHE-Group": "Requires new shim flags that are NYI (as of March 2023)", + "TLS-HintMismatch-SignatureInput": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-KeyShare": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-HandshakerHelloRetryRequest": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-ShimHelloRetryRequest": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-SignatureAlgorithm-TLS*": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-NoTickets1-TLS*": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-NoTickets2-TLS*": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-Version2": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-CertificateRequest": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-CertificateCompression-HandshakerOnly": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-CertificateCompression-ShimOnly": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-CertificateCompression-AlgorithmMismatch": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-CertificateCompression-InputMismatch": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-Version1": "Requires new shim flags that are NYI (as of March 2022)", + "CertificateSelection-*": "Certificate selection is a library-user responsibility", + "ALPNClient-AllowUnknown-*": "Botan always validates server ALPN selection against offered list", + "SendEmptySessionTicket-TLS13": "Botan sends internal_error instead of decode_error - empty ticket is caught later than the spec expects", + "DTLS-ECH*": "No ECH support", + "KeyUpdate-*-DTLS": "No DTLS 1.3", + "AppDataBeforeTLS13KeyChange-DTLS*": "No DTLS 1.3", + "UnencryptedEncryptedExtensions-DTLS": "No DTLS 1.3", + "TLS13-OnlyPadding-DTLS": "No DTLS 1.3", + "Resume-*-TLS13-TLS12-DTLS": "No DTLS 1.3", + "Downgrade-TLS12-*-DTLS": "No DTLS 1.3", + "WrongMessageType-TLS13-*-DTLS": "No DTLS 1.3", + "TrailingMessageData-TLS13-EncryptedExtensions-DTLS": "No DTLS 1.3", + "TrailingMessageData-TLS13-ServerCertificateVerify-DTLS": "No DTLS 1.3", + "KeyChangeWithBufferedMessages-DTLS": "No DTLS 1.3", + "Renegotiate-DTLS-Server-Forbidden": "Botan tolerates DTLS renegotiation", + "Renegotiate-DTLS-Client-Forbidden": "Botan tolerates DTLS renegotiation", + "DTLS12-SendExtraFinished-*": "Botan tolerates extra Finished messages in DTLS", + "MixCompleteMessageWithFragments-DTLS-TLS12": "DTLS fragment handling difference", + "RejectPSSKeyType-*": "Botan does not reject RSA-PSS key type", + "CertificateCipherMismatch-PSS": "Botan does not reject PSS cipher mismatch", + "ServerNameExtensionServer-*-TLS12": "Botan does not echo server_name in TLS 1.2 ServerHello", + "IgnoreLegacyVersion-TLS13": "Botan strictly validates legacy_version in TLS 1.3 ClientHello", + "MTU-DTLS12-3DES-CBC": "No 3DES support", + "TLS13-Client-*TicketFlags": "Botan does not strictly validate ticket flags encoding", + "TLS12-NoTicket-NoOffer": "Different session ticket/ID handling", + "PAKE-*": "No PAKE support", + "TrustAnchors-EmptyID-*": "No TrustAnchors extension support", + "TrustAnchors-ServerSelect-*": "No TrustAnchors extension support", + "TrustAnchors-ServerReceiveEmptyRequest": "No TrustAnchors extension support", + "PSK-Server-CertOrPSK-Cert-*": "Botan shim does not honor credential ordering between PSK and X.509", + "ClientCertificateType-Client-OffersRPKOnly-ServerOmitsExtension-TLS13": "Botan does not reject when server omits cert_type extension and client only offers RPK", + "TLS13-EmptyRecords-DTLS": "No DTLS 1.3", + "TLS13-RecordPadding-DTLS": "No DTLS 1.3", + "ClientCertificateType-Server-*-TLS12": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ClientCertificateType-Client-*-TLS12*": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ServerCertificateType-Server-*-TLS12*": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ServerCertificateType-Client-RequestsRPKOnly-*-TLS12": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ServerCertificateType-Client-RequestsRPKX509-*-TLS12": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ServerCertificateType-Client-RequestsX509RPK-*-TLS12": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ServerCertificateType-Client-RPKVerifyFail-TLS12": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ServerCertificateType-Client-RequestsDefaultOnly-ServerPickedRPKInError-TLS12": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ClientCertificateType-Server-RejectsInvalidDefaultOnly-VerifyPeer-TLS13": "Botan does not reject X509-only cert type extension", + "ClientCertificateType-Server-NoClientHelloCertTypes-NoSharedType-VerifyPeer-TLS13": "Botan does not reject when no shared cert type exists", + "ServerCertificateType-Server-RejectsDefaultOnly-TLS12": "Botan does not reject X509-only cert type extension as server", + "ServerCertificateType-Server-RejectsDefaultOnly-TLS13": "Botan does not reject X509-only cert type extension as server", + "ServerCertificateType-Client-RequestsRPKOnly-ServerPickedX509ByDefaultInError-TLS13": "Botan does not enforce client-side cert type rejection of unsolicited X509", + "ExtensionTrailingData-TrustAnchors-ClientHello-Server-TLS-TLS13": "No TrustAnchors extension support", + "PSK-Client-PSKRequired-TLS": "Botan client does not enforce PSK-only mode", + "PSK-Client-PSKRequired-TLS12-TLS": "Botan client does not enforce PSK-only mode", + "PSK-*-DTLS": "No DTLS 1.3", + "NotJustKyberKeyShare*": "We no longer support Kyber r3 key exchange", + "KyberKeyShareIncludedSecond*": "We no longer support Kyber r3 key exchange", + "KyberKeyShareIncludedThird*": "We no longer support Kyber r3 key exchange", + "CustomKeyShares-All-TLS13": "We no longer support Kyber r3 key exchange", + "DTLS-Replay-NonMonotonic*": "Needs investigation, started failing after https://github.com/google/boringssl/commit/f94f3ed3965ea033001fb9ae006084eee408b861" + }, + "ErrorMap": { + ":CLIENTHELLO_PARSE_FAILED:": [ + ":DECODE_ERROR:" + ], + ":BAD_DECRYPT:": [ + ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:" + ], + ":ERROR_PARSING_EXTENSION:": [ + ":ERROR_PARSING_EXTENSION:", + ":DECODE_ERROR:" + ] + } } diff -Nru botan3-3.7.1+dfsg/src/bogo_shim/config_no_tls12.json botan3-3.12.0+dfsg/src/bogo_shim/config_no_tls12.json --- botan3-3.7.1+dfsg/src/bogo_shim/config_no_tls12.json 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/bogo_shim/config_no_tls12.json 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,334 @@ +{ + "LooseErrorTests": { + "AppDataBeforeHandshake": "BoGo expects different error before vs after CCS", + "AppDataBeforeHandshake-Empty": "Invalid record message", + "ServerHelloBogusCipher": "Unexpected error", + "Garbage": "Decoding error", + "Resume-Client-CipherMismatch": "Unexpected error", + "InvalidECDHPoint-Server": "Unexpected error", + "NoSharedCipher": "Unexpected error", + "NoSharedCipher-TLS13": "Unexpected error", + "PartialFinishedWithServerHelloDone": "Unexpected record vs excess handshake data", + "HelloRetryRequest-DuplicateCurve-TLS13": "expects 'illegal parameter' but we want to stick with 'decode error'", + "HelloRetryRequest-DuplicateCookie-TLS13": "expects 'illegal parameter' but we want to stick with 'decode error'", + "EncryptedExtensionsWithKeyShare-TLS13": "expects 'unsupported extension' but RFC requires 'illegal parameter'", + "ClientSkipCertificateVerify-TLS13": "would require ambiguous error mapping", + "Resume-Client-Mismatch-TLS13-TLS12-TLS": "server requests a downgrade to TLS 1.2, echoing the random session ID during a TLS 1.3 resumption. => error mapping conflict", + "ServerAuth-NoFallback-TLS13": "would require ambiguous error mapping", + "TLS-TLS13-PSK_WITH_AES_128_CBC_SHA-server": "expects a different error for better coverage of Boring SSL's code base", + "TLS-TLS13-PSK_WITH_AES_256_CBC_SHA-server": "expects a different error for better coverage of Boring SSL's code base", + "TLS-TLS13-ECDHE_PSK_WITH_AES_128_CBC_SHA-server": "expects a different error for better coverage of Boring SSL's code base", + "TLS-TLS13-ECDHE_PSK_WITH_AES_256_CBC_SHA-server": "expects a different error for better coverage of Boring SSL's code base", + "TLS-TLS13-ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256-server": "expects a different error for better coverage of Boring SSL's code base", + "CertificateVerificationFail-Server-TLS12-TLS-Sync": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-CustomCallback-TLS-Sync": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-TLS-Sync-ImplicitHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-CustomCallback-TLS-Sync-ImplicitHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-TLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-CustomCallback-TLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-TLS-Sync-PackHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-CustomCallback-TLS-Sync-PackHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-DTLS-Sync": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-DTLS-Sync": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-CustomCallback-DTLS-Sync": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-CustomCallback-DTLS-Sync": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-DTLS-Sync-ImplicitHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-CustomCallback-DTLS-Sync-ImplicitHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-CustomCallback-DTLS-Sync-ImplicitHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-DTLS-Sync-ImplicitHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-CustomCallback-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-CustomCallback-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-CustomCallback-DTLS-Sync-PackHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-DTLS-Sync-PackHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-DTLS-Sync-PackHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-CustomCallback-DTLS-Sync-PackHandshake": "too picky TLS alert", + "NoSSL3-Client-Unsolicited": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-TLS-Sync": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-CustomCallback-TLS-Sync": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-TLS-Sync-ImplicitHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-CustomCallback-TLS-Sync-ImplicitHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-TLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-CustomCallback-TLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Client-TLS12-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Client-TLS12-CustomCallback-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Client-TLS13-CustomCallback-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Client-TLS13-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "ClientOCSPCallback-FailNoStaple-TLS12-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "ClientOCSPCallback-FailNoStaple-TLS13-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "WrongMessageType-TLS13-ClientCertificate-TLS": "too picky TLS alert", + "TrailingMessageData-TLS13-ClientCertificate-TLS": "too picky TLS alert", + "GarbageCertificate-Server-TLS13": "too picky TLS alert", + "AppDataBeforeTLS13KeyChange": "too picky TLS alert", + "ExtensionTrailingData-ServerName-Client-TLS-TLS12": "Botan sends illegal_parameter for malformed server SNI; BoGo expects decode_error", + "ExtensionTrailingData-ServerName-Client-TLS-TLS13": "Botan sends illegal_parameter for malformed server SNI; BoGo expects decode_error", + "ExtensionTrailingData-ServerName-Client-DTLS-TLS12": "Botan sends illegal_parameter for malformed server SNI; BoGo expects decode_error", + "ClientCertificateType-Server-RejectsInvalidEmptyExtension-VerifyPeer-TLS13": "Botan sends decode_error, BoGo expects illegal_parameter", + "ClientCertificateType-Server-RejectsInvalidEmptyExtension-FailIfNoClientCert-TLS13": "Botan sends decode_error, BoGo expects illegal_parameter", + "ClientCertificateType-Server-RejectsInvalidDefaultOnly-FailIfNoClientCert-TLS13": "Botan reports PEER_DID_NOT_RETURN_A_CERTIFICATE, BoGo expects DECODE_ERROR", + "ClientCertificateType-Server-NoClientHelloCertTypes-NoSharedType-FailIfNoClientCert-TLS13": "Botan reports PEER_DID_NOT_RETURN_A_CERTIFICATE, BoGo expects UNSUPPORTED_CERTIFICATE", + "ServerCertificateType-Client-RequestsRPKOnly-NegotiatedRPK-ServerIncorrectlySentX509-TLS13": "Botan parses RPK as X.509 and fails with BER decode error rather than DECODE_ERROR cleanly", + "ServerCertificateType-Client-RequestsRPKOnly-ServerSentEmptyRPK-TLS13": "Empty RPK rejected with generic ASN.1 error rather than INVALID_RAW_PUBLIC_KEY", + "ClientCertificateType-Server-RPKReceived-RPKAccepted-ClientSentX509InError-VerifyPeer-TLS13": "Botan parses RPK as X.509 and fails with BER decode error rather than DECODE_ERROR cleanly", + "ClientCertificateType-Server-RPKReceived-RPKAccepted-ClientSentX509InError-FailIfNoClientCert-TLS13": "Botan parses RPK as X.509 and fails with BER decode error rather than DECODE_ERROR cleanly", + "ClientCertificateType-Server-ClientSentEmptyRPK-TLS13": "Empty RPK rejected with generic ASN.1 error rather than INVALID_RAW_PUBLIC_KEY" + }, + "DisabledTests": { + "*TLS1": "No TLS 1.0", + "*-TLS1-*": "No TLS 1.0", + "*-TLS10-*": "No TLS 1.0", + "TLS1-*": "No TLS 1.0", + "VersionNegotiation*-TLS": "No TLS 1.0", + "VersionNegotiation*-DTLS": "No DTLS 1.0", + "*TLS11": "No TLS 1.1", + "*-TLS11-*": "No TLS 1.1", + "TLS11-*": "No TLS 1.1", + "SendClientVersion-RSA": "No TLS 1.2", + "TLS12ClientShouldNotOffer-*": "No TLS 1.2", + "TLS12ServerShouldNotSelect-*": "No TLS 1.2", + "TLS12NoSessionID-TLS13": "No TLS 1.2", + "EMS-Forbidden-TLS13": "If we don't implement TLS 1.2, we won't offer Extended Master Secret", + "RenegotiationInfo-Forbidden-TLS13": "If we don't implement TLS 1.2, we won't offer Renegotiation Info", + "PointFormat-EncryptedExtensions-TLS13": "If we don't implement TLS 1.2, we won't offer Point Format in Encrypted Extensions", + "*DTLS13*": "No DTLS 1.3", + "DTLS-TLS13*": "No DTLS 1.3", + "*TLS13-DTLS": "No DTLS 1.3", + "*DTLS-TLS13": "No DTLS 1.3", + "TLS13*-DTLS-*": "No DTLS 1.3", + "MinimumVersion-*-TLS13-*DTLS": "No DTLS 1.3", + "*RSA_PKCS1_MD5_SHA1": "We do not implement MD5/SHA1 concatenation anyway", + "*RSA_PKCS1_SHA1*": "We do not implement PKCS1 SHA-1", + "*-ECDSA_SHA1-*": "We do not implement ECDSA SHA-1", + "*RSA_PKCS1_SHA256_LEGACY-TLS13": "We do allow for PKCS1 in TLS 1.3", + "Compliance-fips202205-*": "We do not have explicit support for a FIPS TLS policy", + "Compliance-fips-202205-*": "We do not have explicit support for a FIPS TLS policy", + "Compliance-wpa-202304-*": "We do not have explicit support for the WPA Enterprise mode", + "Compliance-cnsa202407-*": "We do not have explicit support for CNSA", + "CBCRecordSplitting*": "No need to split CBC records in TLS 1.2", + "DelegatedCredentials*": "No support of -delegated-cerdential", + "*SCSV*": "SCSV is meaningless without TLS 1.0/1.1 support", + "AllExtensions-*": "Not all extensions are implemented", + "VersionTolerance-TLS13": "We are not tolerating 0x0400 as Client Hello legacy_version", + "Server-JDK11-*": "We don't implement JDK-specific workarounds", + "Client-RejectJDK11DowngradeRandom": "We don't implement this workaround", + "ExportTrafficSecrets-*": "Exporting traffic secrets is not implemented", + "TooManyChangeCipherSpec-Client-TLS13": "Limits on the number of CCS are not implemented", + "TooManyChangeCipherSpec-Server-TLS13": "Limits on the number of CCS are not implemented", + "PostQuantumNotEnabledByDefaultInClients": "Oh yes it is", + "TLS12SessionID-TLS13": "We don't offer TLS 1.3 when a TLS 1.2 session was found", + "Ticket-Forbidden-TLS13": "We don't offer TLS 1.3 when a TLS 1.2 session was found", + "Resume-Client-NoResume-TLS12-TLS13-TLS": "We don't offer TLS 1.3 when a TLS 1.2 session was found", + "Resume-Client-Mismatch-TLS12-TLS13-TLS": "We don't offer TLS 1.3 when a TLS 1.2 session was found", + "Resume-Server-UnofferedCipher-TLS13": "BoringSSL will not allow switching ciphers during TLS 1.3 resumption, we do, though.", + "HttpGET": "TLS 1.3 server does not detect HTTP", + "HttpPOST": "TLS 1.3 server does not detect HTTP", + "HttpPUT": "TLS 1.3 server does not detect HTTP", + "HttpHEAD": "TLS 1.3 server does not detect HTTP", + "HttpCONNECT": "TLS 1.3 server does not detect HTTP", + "*EarlyData*": "No TLS 1.3 Early Data, yet", + "TLS13-TicketAgeSkew-*": "No TLS 1.3 Early Data, yet", + "ExportKeyingMaterial-Server-HalfRTT-TLS13": "No TLS 1.3 Early Data, yet", + "EarlyDataEnabled*": "No TLS 1.3 Early Data, yet", + "EarlyData-Reject0RTT*": "No TLS 1.3 Early Data, yet", + "PartialEndOfEarlyDataWithClientHello": "No TLS 1.3 Early Data, yet", + "SendNoClientCertificateExtensions-TLS13": "-signed-cert-timestamps currently not supported in the shim", + "KeyUpdate-RequestACK-UnfinishedWrite": "-read-with-unfinished-write currently not supported in the shim", + "TLS-ECH*": "No ECH support", + "ECH*": "No ECH support", + "DuplicateCertCompressionExt*": "No support for 1.3 cert compression extension", + "CertCompression*-TLS13": "No support for 1.3 cert compression extension", + "SupportedVersionSelection-TLS12": "We just ignore the version extension in this case", + "NoCommonSignatureAlgorithms-TLS12-Fallback": "Fallback behaviour not implemented by shim", + "CheckClientCertificateTypes": "Client certificate type check is a library-user responsibility", + "Downgrade-*-Client-Ignore": "Not possible to ignore downgrade indicator", + "Agree-Digest-SHA1": "No SHA-1 in TLS 1.2", + "ServerAuth-SHA1-Fallback-*": "No SHA-1 in TLS 1.2", + "*-InvalidSignature-*_SHA1-TLS12": "No SHA-1 in TLS 1.2", + "*-Sign-*_SHA1-TLS12": "No SHA-1 in TLS 1.2", + "*-Sign-Negotiate-*_SHA1-TLS12": "No SHA-1 in TLS 1.2", + "*-VerifyDefault-*_SHA1-TLS12": "No SHA-1 in TLS 1.2", + "*-Verify-*_SHA1-TLS12": "No SHA-1 in TLS 1.2", + "*QUIC*": "No QUIC", + "ALPS*": "No ALPS", + "ExtraClientEncryptedExtension-*": "No ALPS", + "*NPN*": "No support for NPN", + "ALPNServer-Preferred-*": "No support for NPN", + "*-NextProtocol*": "No support for NPN", + "TooManyKeyUpdates": "BoringSSL's approach to KeyUpdate throttling is ineffective and pointless", + "*SignedCertificateTimestamp*": "No support for SCT", + "*SCT*": "No support for SCT", + "Renegotiation-ChangeAuthProperties": "No support for SCT", + "UnsolicitedCertificateExtensions-*": "No support for SCT", + "IgnoreExtensionsOnIntermediates-TLS13": "No support for SCT", + "SendNoExtensionsOnIntermediate-TLS13": "No support for SCT", + "CertificateVerificationSoftFail*": "Fail, but don't fail... wtf?", + "*NULL-SHA*": "No support for NULL ciphers", + "*WITH_NULL*": "No support for NULL ciphers", + "*GREASE*": "No support for GREASE", + "*ChannelID*": "No support for ChannelID", + "*TokenBinding*": "No support for Token Binding", + "ClientHelloPadding": "No support for client hello padding extension", + "TLSUnique*": "Not supported", + "*CECPQ2*": "Not implemented", + "PQExperimentSignal*": "Not implemented", + "*P-224*": "P-224 not supported in TLS", + "*V2ClientHello*": "No support for SSLv2 client hellos", + "*Ed25519*": "Ed25519 not implemented in TLS", + "*FalseStart*": "Botan doesn't do false start", + "MaxSendFragment*": "Maximum fragment extension not supported", + "ExportKeyingMaterial-EmptyContext*": "No support for empty context", + "Peek-*": "No peek API", + "*OldCallback*": "BoringSSL specific API test", + "*Renegotiate-Client-Explicit*": "BoringSSL specific API test", + "CBCRecordSplittingPartialWrite*": "BoringSSL specific API test", + "TicketCallback*": "BoringSSL specific API test", + "Server-DDoS*": "BoringSSL specific API test", + "RetainOnlySHA256-*": "BoringSSL specific API test", + "Renegotiate-Client-UnfinishedWrite": "BoringSSL specific API test", + "FailEarlyCallback": "BoringSSL specific API test", + "CurveTest-*Kyber*": "We no longer support Kyber r3 key exchange", + "ShimTicketRewritable": "Botan has a different ticket format", + "Resume-Server-DeclineCrossVersion*": "Botan has a different ticket format", + "Resume-Server-DeclineBadCipher*": "Botan has a different ticket format", + "Resume-Server-CipherNotPreferred*": "Botan has a different ticket format", + "TLS*-NoTicket-NoAccept": "BoGo expects that if ticket is issued stateful resumption is impossible", + "CheckLeafCurve": "Botan doesn't care what curve an ECDSA cert uses", + "CheckECDSACurve-TLS12": "Botan doesn't care what curve an ECDSA cert uses", + "CertificateVerificationDoesNotFailOnResume*": "Botan doesn't support reverify on resume", + "CertificateVerificationFailsOnResume*": "Botan doesn't support reverify on resume", + "CertificateVerificationPassesOnResume*": "Botan doesn't support reverify on resume", + "CipherNegotiation-2": "No support for cipher equivalence classes", + "CipherNegotiation-3": "No support for cipher equivalence classes", + "CipherNegotiation-4": "No support for cipher equivalence classes", + "CipherNegotiation-5": "No support for cipher equivalence classes", + "CipherNegotiation-8": "No support for cipher equivalence classes", + "ALPNServer-SelectEmpty-*": "Botan treats empty ALPN from callback as a decline", + "AppDataAfterChangeCipherSpec-DTLS*": "BoringSSL DTLS drops out of order AppData, we reject", + "Resume-Client-NoResume-TLS1-TLS11-TLS": "BoGo expects resumption attempt sends latest version", + "Resume-Client-NoResume-TLS1-TLS12-TLS": "BoGo expects resumption attempt sends latest version", + "Resume-Client-NoResume-TLS11-TLS12-TLS": "BoGo expects resumption attempt sends latest version", + "Resume-Client-NoResume-TLS1-TLS12-DTLS": "BoGo expects resumption attempt sends latest version", + "Resume-Client-Mismatch-TLS1-TLS11-TLS": "BoGo expects resumption attempt sends latest version", + "Resume-Client-Mismatch-TLS1-TLS12-TLS": "BoGo expects resumption attempt sends latest version", + "Resume-Client-Mismatch-TLS11-TLS12-TLS": "BoGo expects resumption attempt sends latest version", + "Resume-Client-Mismatch-TLS1-TLS12-DTLS": "BoGo expects resumption attempt sends latest version", + "LooseInitialRecordVersion-TLS12": "Botan is somewhat strict about the record version number", + "CurveTest-*-Compressed*": "Point compression is supported, which BoGo doesn't expect", + "PointFormat-*-MissingUncompressed": "Point compression is supported, which BoGo doesn't expect", + "RSAPSSSupport-ConfigPSS-NoCerts-TLS12-*": "Needs investigation", + "RSAPSSSupport-Default-NoCerts-TLS12-*": "Needs investigation", + "DTLS-Retransmit*": "Shim needs timeout support", + "DTLS-StrayRetransmitFinished-ClientFull": "Needs investigation", + "DTLS-StrayRetransmitFinished-ServerResume": "Needs investigation", + "SRTP-Server-IgnoreMKI-*": "Non-empty MKI is rejected (bug)", + "Renegotiate-Client-Packed": "Packing HelloRequest with Finished loses the HelloRequest (bug)", + "SendHalfHelloRequest*PackHandshake": "Packing HelloRequest with Finished loses the HelloRequest (bug)", + "PartialClientFinishedWithClientHello": "Need to check for buffered messages when CCS (bug)", + "SendUnencryptedFinished-DTLS": "Need to check for buffered messages when CCS (bug)", + "RSAKeyUsage-*-TLS12": "We always enforce key usage", + "RSAKeyUsage-Client-WantSignature-GotEncipherment-AlwaysEnforced-TLS13": "We always enforce key usage", + "AllExtensions-Client-Permute-TLS-TLS12": "Requires new shim flags that are NYI (as of March 2022)", + "AllExtensions-Client-Permute-DTLS-TLS12": "Requires new shim flags that are NYI (as of March 2022)", + "EarlyData-WriteAfterEncryptedExtensions": "Requires new shim flags that are NYI (as of March 2022)", + "EarlyData-WriteAfterServerHello": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-Certificate-*": "Requires new shim flags that are NYI (as of May 2024)", + "TLS-HintMismatch-CipherMismatch1": "Requires new shim flags that are NYI (as of March 2023)", + "TLS-HintMismatch-CipherMismatch2": "Requires new shim flags that are NYI (as of March 2023)", + "TLS-HintMismatch-ECDHE-Group": "Requires new shim flags that are NYI (as of March 2023)", + "TLS-HintMismatch-SignatureInput": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-KeyShare": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-HandshakerHelloRetryRequest": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-ShimHelloRetryRequest": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-SignatureAlgorithm-TLS*": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-NoTickets1-TLS*": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-NoTickets2-TLS*": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-Version2": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-CertificateRequest": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-CertificateCompression-HandshakerOnly": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-CertificateCompression-ShimOnly": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-CertificateCompression-AlgorithmMismatch": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-CertificateCompression-InputMismatch": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-Version1": "Requires new shim flags that are NYI (as of March 2022)", + "CertificateSelection-*": "Certificate selection is a library-user responsibility", + "ALPNClient-AllowUnknown-*": "Botan always validates server ALPN selection against offered list", + "RejectEmptyOCSPResponse-TLS-TLS12": "Unmapped error string for empty OCSP response", + "RejectEmptyOCSPResponse-TLS-TLS13": "Unmapped error string for empty OCSP response", + "RejectEmptyOCSPResponse-DTLS-TLS12": "Unmapped error string for empty OCSP response", + "Resume-Server-OmitAllPSKsOnSecondClientHello": "Different error code for inconsistent ClientHello", + "SendEmptySessionTicket-TLS13": "Different error handling for empty session ticket", + "CertificateInResumption-TLS13": "Unmapped error string", + "CertificateRequestInResumption-TLS13": "Unmapped error string", + "AppDataBeforeTLS13KeyChange-Empty": "Different error code for bad decrypt", + "UnencryptedEncryptedExtensions": "Different error for unencrypted record", + "TrustAnchors-Unsolicited-Certificate": "Different alert code for unsolicited extension", + "PSK-Server-OmitAllPSKsOnSecondClientHello-TLS": "Botan reports INCONSISTENT_CLIENT_HELLO instead of MISSING_EXTENSION", + "PSK-Server-HRR-PSKMissing-TLS": "Botan sends handshake_failure alert instead of illegal_parameter", + "PSK-Server-MissingPSKMode-NoMatch-TLS": "Botan reports PSK_IDENTITY_NOT_FOUND instead of NO_SUPPORTED_PSK_MODE", + "DTLS-ECH*": "No ECH support", + "KeyUpdate-*-DTLS": "No DTLS 1.3", + "AppDataBeforeTLS13KeyChange-DTLS*": "No DTLS 1.3", + "UnencryptedEncryptedExtensions-DTLS": "No DTLS 1.3", + "TLS13-OnlyPadding-DTLS": "No DTLS 1.3", + "Resume-*-TLS13-TLS12-DTLS": "No DTLS 1.3", + "Downgrade-TLS12-*-DTLS": "No DTLS 1.3", + "WrongMessageType-TLS13-*-DTLS": "No DTLS 1.3", + "TrailingMessageData-TLS13-EncryptedExtensions-DTLS": "No DTLS 1.3", + "TrailingMessageData-TLS13-ServerCertificateVerify-DTLS": "No DTLS 1.3", + "KeyChangeWithBufferedMessages-DTLS": "No DTLS 1.3", + "Renegotiate-DTLS-Server-Forbidden": "Botan tolerates DTLS renegotiation", + "Renegotiate-DTLS-Client-Forbidden": "Botan tolerates DTLS renegotiation", + "DTLS12-SendExtraFinished-*": "Botan tolerates extra Finished messages in DTLS", + "MixCompleteMessageWithFragments-DTLS-TLS12": "DTLS fragment handling difference", + "RejectPSSKeyType-*": "Botan does not reject RSA-PSS key type", + "CertificateCipherMismatch-PSS": "Botan does not reject PSS cipher mismatch", + "ServerNameExtensionServer-*-TLS12": "Botan does not echo server_name in TLS 1.2 ServerHello", + "IgnoreLegacyVersion-TLS13": "Botan strictly validates legacy_version in TLS 1.3 ClientHello", + "MTU-DTLS12-3DES-CBC": "No 3DES support", + "TLS13-Client-*TicketFlags": "Botan does not strictly validate ticket flags encoding", + "TLS12-NoTicket-NoOffer": "Different session ticket/ID handling", + "PAKE-*": "No PAKE support", + "TrustAnchors-EmptyID-*": "No TrustAnchors extension support", + "TrustAnchors-ServerSelect-*": "No TrustAnchors extension support", + "TrustAnchors-ServerReceiveEmptyRequest": "No TrustAnchors extension support", + "ExtensionTrailingData-TrustAnchors-ClientHello-Server-TLS-TLS13": "No TrustAnchors extension support", + "PSK-Server-CertOrPSK-Cert-*": "Botan shim does not honor credential ordering between PSK and X.509", + "ClientCertificateType-Client-OffersRPKOnly-ServerOmitsExtension-TLS13": "Botan does not reject when server omits cert_type extension and client only offers RPK", + "*ML-DSA*": "Need support for the LAMPS tagged seed keys", + "TLS13-EmptyRecords-DTLS": "No DTLS 1.3", + "TLS13-RecordPadding-DTLS": "No DTLS 1.3", + "ClientCertificateType-Server-*-TLS12": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ClientCertificateType-Client-*-TLS12*": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ServerCertificateType-Server-*-TLS12*": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ServerCertificateType-Client-RequestsRPKOnly-*-TLS12": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ServerCertificateType-Client-RequestsRPKX509-*-TLS12": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ServerCertificateType-Client-RequestsX509RPK-*-TLS12": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ServerCertificateType-Client-RPKVerifyFail-TLS12": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ServerCertificateType-Client-RequestsDefaultOnly-ServerPickedRPKInError-TLS12": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ClientCertificateType-Server-RejectsInvalidDefaultOnly-VerifyPeer-TLS13": "Botan does not reject X509-only cert type extension", + "ClientCertificateType-Server-NoClientHelloCertTypes-NoSharedType-VerifyPeer-TLS13": "Botan does not reject when no shared cert type exists", + "ServerCertificateType-Server-RejectsDefaultOnly-TLS12": "Botan does not reject X509-only cert type extension as server", + "ServerCertificateType-Server-RejectsDefaultOnly-TLS13": "Botan does not reject X509-only cert type extension as server", + "ServerCertificateType-Client-RequestsRPKOnly-ServerPickedX509ByDefaultInError-TLS13": "Botan does not enforce client-side cert type rejection of unsolicited X509", + "PSK-Client-PSKRequired-TLS": "Botan client does not enforce PSK-only mode", + "PSK-Client-PSKRequired-TLS12-TLS": "Botan client does not enforce PSK-only mode", + "PSK-*-DTLS": "No DTLS 1.3", + "NotJustKyberKeyShare*": "We no longer support Kyber r3 key exchange", + "KyberKeyShareIncludedSecond*": "We no longer support Kyber r3 key exchange", + "KyberKeyShareIncludedThird*": "We no longer support Kyber r3 key exchange", + "CustomKeyShares-All-TLS13": "We no longer support Kyber r3 key exchange", + "DTLS-Replay-NonMonotonic*": "Needs investigation, started failing after https://github.com/google/boringssl/commit/f94f3ed3965ea033001fb9ae006084eee408b861" + }, + "ErrorMap": { + ":CLIENTHELLO_PARSE_FAILED:": [ + ":DECODE_ERROR:" + ], + ":BAD_DECRYPT:": [ + ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:" + ], + ":ERROR_PARSING_EXTENSION:": [ + ":ERROR_PARSING_EXTENSION:", + ":DECODE_ERROR:" + ] + } +} diff -Nru botan3-3.7.1+dfsg/src/bogo_shim/config_no_tls13.json botan3-3.12.0+dfsg/src/bogo_shim/config_no_tls13.json --- botan3-3.7.1+dfsg/src/bogo_shim/config_no_tls13.json 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/bogo_shim/config_no_tls13.json 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,332 @@ +{ + "LooseErrorTests": { + "AppDataBeforeHandshake": "BoGo expects different error before vs after CCS", + "AppDataBeforeHandshake-Empty": "Invalid record message", + "ServerHelloBogusCipher": "Unexpected error", + "Garbage": "Decoding error", + "Resume-Client-CipherMismatch": "Unexpected error", + "InvalidECDHPoint-Server": "Unexpected error", + "NoSharedCipher": "Unexpected error", + "NoSharedCipher-TLS13": "Unexpected error", + "PartialFinishedWithServerHelloDone": "Unexpected record vs excess handshake data", + "HelloRetryRequest-DuplicateCurve-TLS13": "expects 'illegal parameter' but we want to stick with 'decode error'", + "HelloRetryRequest-DuplicateCookie-TLS13": "expects 'illegal parameter' but we want to stick with 'decode error'", + "EncryptedExtensionsWithKeyShare-TLS13": "expects 'unsupported extension' but RFC requires 'illegal parameter'", + "ClientSkipCertificateVerify-TLS13": "would require ambiguous error mapping", + "Resume-Client-Mismatch-TLS13-TLS12-TLS": "server requests a downgrade to TLS 1.2, echoing the random session ID during a TLS 1.3 resumption. => error mapping conflict", + "ServerAuth-NoFallback-TLS13": "would require ambiguous error mapping", + "TLS-TLS13-PSK_WITH_AES_128_CBC_SHA-server": "expects a different error for better coverage of Boring SSL's code base", + "TLS-TLS13-PSK_WITH_AES_256_CBC_SHA-server": "expects a different error for better coverage of Boring SSL's code base", + "TLS-TLS13-ECDHE_PSK_WITH_AES_128_CBC_SHA-server": "expects a different error for better coverage of Boring SSL's code base", + "TLS-TLS13-ECDHE_PSK_WITH_AES_256_CBC_SHA-server": "expects a different error for better coverage of Boring SSL's code base", + "TLS-TLS13-ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256-server": "expects a different error for better coverage of Boring SSL's code base", + "CertificateVerificationFail-Server-TLS12-TLS-Sync": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-CustomCallback-TLS-Sync": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-TLS-Sync-ImplicitHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-CustomCallback-TLS-Sync-ImplicitHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-TLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-CustomCallback-TLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-TLS-Sync-PackHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-CustomCallback-TLS-Sync-PackHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-DTLS-Sync": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-DTLS-Sync": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-CustomCallback-DTLS-Sync": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-CustomCallback-DTLS-Sync": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-DTLS-Sync-ImplicitHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-CustomCallback-DTLS-Sync-ImplicitHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-CustomCallback-DTLS-Sync-ImplicitHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-DTLS-Sync-ImplicitHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-CustomCallback-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-CustomCallback-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-CustomCallback-DTLS-Sync-PackHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS12-DTLS-Sync-PackHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-DTLS-Sync-PackHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-CustomCallback-DTLS-Sync-PackHandshake": "too picky TLS alert", + "NoSSL3-Client-Unsolicited": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-TLS-Sync": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-CustomCallback-TLS-Sync": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-TLS-Sync-ImplicitHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-CustomCallback-TLS-Sync-ImplicitHandshake": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-TLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Server-TLS13-CustomCallback-TLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Client-TLS12-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Client-TLS12-CustomCallback-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Client-TLS13-CustomCallback-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "CertificateVerificationFail-Client-TLS13-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "ClientOCSPCallback-FailNoStaple-TLS12-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "ClientOCSPCallback-FailNoStaple-TLS13-DTLS-Sync-SplitHandshakeRecords": "too picky TLS alert", + "WrongMessageType-TLS13-ClientCertificate-TLS": "too picky TLS alert", + "TrailingMessageData-TLS13-ClientCertificate-TLS": "too picky TLS alert", + "GarbageCertificate-Server-TLS13": "too picky TLS alert", + "AppDataBeforeTLS13KeyChange": "too picky TLS alert", + "ExtensionTrailingData-ServerName-Client-TLS-TLS12": "Botan sends illegal_parameter for malformed server SNI; BoGo expects decode_error", + "ExtensionTrailingData-ServerName-Client-TLS-TLS13": "Botan sends illegal_parameter for malformed server SNI; BoGo expects decode_error", + "ExtensionTrailingData-ServerName-Client-DTLS-TLS12": "Botan sends illegal_parameter for malformed server SNI; BoGo expects decode_error", + "ClientCertificateType-Server-RejectsInvalidEmptyExtension-VerifyPeer-TLS13": "Botan sends decode_error, BoGo expects illegal_parameter", + "ClientCertificateType-Server-RejectsInvalidEmptyExtension-FailIfNoClientCert-TLS13": "Botan sends decode_error, BoGo expects illegal_parameter", + "ClientCertificateType-Server-RejectsInvalidDefaultOnly-FailIfNoClientCert-TLS13": "Botan reports PEER_DID_NOT_RETURN_A_CERTIFICATE, BoGo expects DECODE_ERROR", + "ClientCertificateType-Server-NoClientHelloCertTypes-NoSharedType-FailIfNoClientCert-TLS13": "Botan reports PEER_DID_NOT_RETURN_A_CERTIFICATE, BoGo expects UNSUPPORTED_CERTIFICATE", + "ServerCertificateType-Client-RequestsRPKOnly-NegotiatedRPK-ServerIncorrectlySentX509-TLS13": "Botan parses RPK as X.509 and fails with BER decode error rather than DECODE_ERROR cleanly", + "ServerCertificateType-Client-RequestsRPKOnly-ServerSentEmptyRPK-TLS13": "Empty RPK rejected with generic ASN.1 error rather than INVALID_RAW_PUBLIC_KEY", + "ClientCertificateType-Server-RPKReceived-RPKAccepted-ClientSentX509InError-VerifyPeer-TLS13": "Botan parses RPK as X.509 and fails with BER decode error rather than DECODE_ERROR cleanly", + "ClientCertificateType-Server-RPKReceived-RPKAccepted-ClientSentX509InError-FailIfNoClientCert-TLS13": "Botan parses RPK as X.509 and fails with BER decode error rather than DECODE_ERROR cleanly", + "ClientCertificateType-Server-ClientSentEmptyRPK-TLS13": "Empty RPK rejected with generic ASN.1 error rather than INVALID_RAW_PUBLIC_KEY" + }, + "DisabledTests": { + "*TLS1": "No TLS 1.0", + "*-TLS1-*": "No TLS 1.0", + "*-TLS10-*": "No TLS 1.0", + "TLS1-*": "No TLS 1.0", + "VersionNegotiation*-TLS": "No TLS 1.0", + "VersionNegotiation*-DTLS": "No DTLS 1.0", + "*TLS11": "No TLS 1.1", + "*-TLS11-*": "No TLS 1.1", + "TLS11-*": "No TLS 1.1", + "EchoTLS13CompatibilitySessionID": "If we don't implement TLS 1.3, we won't set session ID accordingly", + "Downgrade-TLS12-Client-*": "If we don't implement TLS 1.3, we won't enforce the downgrade sentinel value", + "Downgrade-TLS12-Server-*": "If we don't implement TLS 1.3, we won't emit the downgrade sentinel value", + "MinimumVersion-Client2-TLS13-*": "We don't implement TLS 1.3, so we cannot offer TLS 1.3", + "MinimumVersion-Client-TLS13-*": "We don't implement TLS 1.3, so we cannot offer TLS 1.3", + "MinimumVersion-Server-TLS13-*": "We don't implement TLS 1.3, so we cannot expect TLS 1.3", + "MinimumVersion-Server2-TLS13-*": "We don't implement TLS 1.3, so we cannot expect TLS 1.3", + "*DTLS13*": "No DTLS 1.3", + "DTLS-TLS13*": "No DTLS 1.3", + "*TLS13-DTLS": "No DTLS 1.3", + "*DTLS-TLS13": "No DTLS 1.3", + "TLS13*-DTLS-*": "No DTLS 1.3", + "MinimumVersion-*-TLS13-*DTLS": "No DTLS 1.3", + "*RSA_PKCS1_MD5_SHA1": "We do not implement MD5/SHA1 concatenation anyway", + "*RSA_PKCS1_SHA1*": "We do not implement PKCS1 SHA-1", + "*-ECDSA_SHA1-*": "We do not implement ECDSA SHA-1", + "*RSA_PKCS1_SHA256_LEGACY-TLS13": "We do allow for PKCS1 in TLS 1.3", + "Compliance-fips202205-*": "We do not have explicit support for a FIPS TLS policy", + "Compliance-fips-202205-*": "We do not have explicit support for a FIPS TLS policy", + "Compliance-wpa-202304-*": "We do not have explicit support for the WPA Enterprise mode", + "Compliance-cnsa202407-*": "We do not have explicit support for CNSA", + "CBCRecordSplitting*": "No need to split CBC records in TLS 1.2", + "DelegatedCredentials*": "No support of -delegated-cerdential", + "*SCSV*": "SCSV is meaningless without TLS 1.0/1.1 support", + "AllExtensions-*": "Not all extensions are implemented", + "VersionTolerance-TLS13": "We are not tolerating 0x0400 as Client Hello legacy_version", + "Server-JDK11-*": "We don't implement JDK-specific workarounds", + "Client-RejectJDK11DowngradeRandom": "We don't implement this workaround", + "ExportTrafficSecrets-*": "Exporting traffic secrets is not implemented", + "TooManyChangeCipherSpec-Client-TLS13": "Limits on the number of CCS are not implemented", + "TooManyChangeCipherSpec-Server-TLS13": "Limits on the number of CCS are not implemented", + "PostQuantumNotEnabledByDefaultInClients": "Oh yes it is", + "TLS12SessionID-TLS13": "We don't offer TLS 1.3 when a TLS 1.2 session was found", + "Ticket-Forbidden-TLS13": "We don't offer TLS 1.3 when a TLS 1.2 session was found", + "Resume-Client-NoResume-TLS12-TLS13-TLS": "We don't offer TLS 1.3 when a TLS 1.2 session was found", + "Resume-Client-Mismatch-TLS12-TLS13-TLS": "We don't offer TLS 1.3 when a TLS 1.2 session was found", + "Resume-Server-UnofferedCipher-TLS13": "BoringSSL will not allow switching ciphers during TLS 1.3 resumption, we do, though.", + "HttpGET": "TLS 1.3 server does not detect HTTP", + "HttpPOST": "TLS 1.3 server does not detect HTTP", + "HttpPUT": "TLS 1.3 server does not detect HTTP", + "HttpHEAD": "TLS 1.3 server does not detect HTTP", + "HttpCONNECT": "TLS 1.3 server does not detect HTTP", + "*EarlyData*": "No TLS 1.3 Early Data, yet", + "TLS13-TicketAgeSkew-*": "No TLS 1.3 Early Data, yet", + "ExportKeyingMaterial-Server-HalfRTT-TLS13": "No TLS 1.3 Early Data, yet", + "EarlyDataEnabled*": "No TLS 1.3 Early Data, yet", + "EarlyData-Reject0RTT*": "No TLS 1.3 Early Data, yet", + "PartialEndOfEarlyDataWithClientHello": "No TLS 1.3 Early Data, yet", + "SendNoClientCertificateExtensions-TLS13": "-signed-cert-timestamps currently not supported in the shim", + "KeyUpdate-RequestACK-UnfinishedWrite": "-read-with-unfinished-write currently not supported in the shim", + "TLS-ECH*": "No ECH support", + "ECH*": "No ECH support", + "DuplicateCertCompressionExt*": "No support for 1.3 cert compression extension", + "CertCompression*-TLS13": "No support for 1.3 cert compression extension", + "SupportedVersionSelection-TLS12": "We just ignore the version extension in this case", + "NoCommonSignatureAlgorithms-TLS12-Fallback": "Fallback behaviour not implemented by shim", + "CheckClientCertificateTypes": "Client certificate type check is a library-user responsibility", + "Downgrade-*-Client-Ignore": "Not possible to ignore downgrade indicator", + "Agree-Digest-SHA1": "No SHA-1 in TLS 1.2", + "ServerAuth-SHA1-Fallback-*": "No SHA-1 in TLS 1.2", + "*-InvalidSignature-*_SHA1-TLS12": "No SHA-1 in TLS 1.2", + "*-Sign-*_SHA1-TLS12": "No SHA-1 in TLS 1.2", + "*-Sign-Negotiate-*_SHA1-TLS12": "No SHA-1 in TLS 1.2", + "*-VerifyDefault-*_SHA1-TLS12": "No SHA-1 in TLS 1.2", + "*-Verify-*_SHA1-TLS12": "No SHA-1 in TLS 1.2", + "*QUIC*": "No QUIC", + "ALPS*": "No ALPS", + "ExtraClientEncryptedExtension-*": "No ALPS", + "*NPN*": "No support for NPN", + "ALPNServer-Preferred-*": "No support for NPN", + "*-NextProtocol*": "No support for NPN", + "*SignedCertificateTimestamp*": "No support for SCT", + "*SCT*": "No support for SCT", + "Renegotiation-ChangeAuthProperties": "No support for SCT", + "UnsolicitedCertificateExtensions-*": "No support for SCT", + "IgnoreExtensionsOnIntermediates-TLS13": "No support for SCT", + "SendNoExtensionsOnIntermediate-TLS13": "No support for SCT", + "CertificateVerificationSoftFail*": "Fail, but don't fail... wtf?", + "*NULL-SHA*": "No support for NULL ciphers", + "*WITH_NULL*": "No support for NULL ciphers", + "*GREASE*": "No support for GREASE", + "*ChannelID*": "No support for ChannelID", + "*TokenBinding*": "No support for Token Binding", + "ClientHelloPadding": "No support for client hello padding extension", + "TLSUnique*": "Not supported", + "*CECPQ2*": "Not implemented", + "PQExperimentSignal*": "Not implemented", + "*P-224*": "P-224 not supported in TLS", + "*V2ClientHello*": "No support for SSLv2 client hellos", + "*Ed25519*": "Ed25519 not implemented in TLS", + "*FalseStart*": "Botan doesn't do false start", + "MaxSendFragment*": "Maximum fragment extension not supported", + "ExportKeyingMaterial-EmptyContext*": "No support for empty context", + "Peek-*": "No peek API", + "*OldCallback*": "BoringSSL specific API test", + "*Renegotiate-Client-Explicit*": "BoringSSL specific API test", + "CBCRecordSplittingPartialWrite*": "BoringSSL specific API test", + "TicketCallback*": "BoringSSL specific API test", + "Server-DDoS*": "BoringSSL specific API test", + "RetainOnlySHA256-*": "BoringSSL specific API test", + "Renegotiate-Client-UnfinishedWrite": "BoringSSL specific API test", + "FailEarlyCallback": "BoringSSL specific API test", + "CurveTest-*Kyber*": "We no longer support Kyber r3 key exchange", + "ShimTicketRewritable": "Botan has a different ticket format", + "Resume-Server-DeclineCrossVersion*": "Botan has a different ticket format", + "Resume-Server-DeclineBadCipher*": "Botan has a different ticket format", + "Resume-Server-CipherNotPreferred*": "Botan has a different ticket format", + "TLS*-NoTicket-NoAccept": "BoGo expects that if ticket is issued stateful resumption is impossible", + "CheckLeafCurve": "Botan doesn't care what curve an ECDSA cert uses", + "CheckECDSACurve-TLS12": "Botan doesn't care what curve an ECDSA cert uses", + "CertificateVerificationDoesNotFailOnResume*": "Botan doesn't support reverify on resume", + "CertificateVerificationFailsOnResume*": "Botan doesn't support reverify on resume", + "CertificateVerificationPassesOnResume*": "Botan doesn't support reverify on resume", + "CipherNegotiation-2": "No support for cipher equivalence classes", + "CipherNegotiation-3": "No support for cipher equivalence classes", + "CipherNegotiation-4": "No support for cipher equivalence classes", + "CipherNegotiation-5": "No support for cipher equivalence classes", + "CipherNegotiation-8": "No support for cipher equivalence classes", + "ALPNServer-SelectEmpty-*": "Botan treats empty ALPN from callback as a decline", + "AppDataAfterChangeCipherSpec-DTLS*": "BoringSSL DTLS drops out of order AppData, we reject", + "Resume-Client-NoResume-TLS1-TLS11-TLS": "BoGo expects resumption attempt sends latest version", + "Resume-Client-NoResume-TLS1-TLS12-TLS": "BoGo expects resumption attempt sends latest version", + "Resume-Client-NoResume-TLS11-TLS12-TLS": "BoGo expects resumption attempt sends latest version", + "Resume-Client-NoResume-TLS1-TLS12-DTLS": "BoGo expects resumption attempt sends latest version", + "Resume-Client-Mismatch-TLS1-TLS11-TLS": "BoGo expects resumption attempt sends latest version", + "Resume-Client-Mismatch-TLS1-TLS12-TLS": "BoGo expects resumption attempt sends latest version", + "Resume-Client-Mismatch-TLS11-TLS12-TLS": "BoGo expects resumption attempt sends latest version", + "Resume-Client-Mismatch-TLS1-TLS12-DTLS": "BoGo expects resumption attempt sends latest version", + "LooseInitialRecordVersion-TLS12": "Botan is somewhat strict about the record version number", + "CurveTest-*-Compressed*": "Point compression is supported, which BoGo doesn't expect", + "PointFormat-*-MissingUncompressed": "Point compression is supported, which BoGo doesn't expect", + "RSAPSSSupport-ConfigPSS-NoCerts-TLS12-*": "Needs investigation", + "RSAPSSSupport-Default-NoCerts-TLS12-*": "Needs investigation", + "DTLS-Retransmit*": "Shim needs timeout support", + "DTLS-StrayRetransmitFinished-ClientFull": "Needs investigation", + "DTLS-StrayRetransmitFinished-ServerResume": "Needs investigation", + "SRTP-Server-IgnoreMKI-*": "Non-empty MKI is rejected (bug)", + "Renegotiate-Client-Packed": "Packing HelloRequest with Finished loses the HelloRequest (bug)", + "SendHalfHelloRequest*PackHandshake": "Packing HelloRequest with Finished loses the HelloRequest (bug)", + "PartialClientFinishedWithClientHello": "Need to check for buffered messages when CCS (bug)", + "SendUnencryptedFinished-DTLS": "Need to check for buffered messages when CCS (bug)", + "RSAKeyUsage-*-TLS12": "We always enforce key usage", + "RSAKeyUsage-Client-WantSignature-GotEncipherment-AlwaysEnforced-TLS13": "We always enforce key usage", + "AllExtensions-Client-Permute-TLS-TLS12": "Requires new shim flags that are NYI (as of March 2022)", + "AllExtensions-Client-Permute-DTLS-TLS12": "Requires new shim flags that are NYI (as of March 2022)", + "EarlyData-WriteAfterEncryptedExtensions": "Requires new shim flags that are NYI (as of March 2022)", + "EarlyData-WriteAfterServerHello": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-Certificate-*": "Requires new shim flags that are NYI (as of May 2024)", + "TLS-HintMismatch-CipherMismatch1": "Requires new shim flags that are NYI (as of March 2023)", + "TLS-HintMismatch-CipherMismatch2": "Requires new shim flags that are NYI (as of March 2023)", + "TLS-HintMismatch-ECDHE-Group": "Requires new shim flags that are NYI (as of March 2023)", + "TLS-HintMismatch-SignatureInput": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-KeyShare": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-HandshakerHelloRetryRequest": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-ShimHelloRetryRequest": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-SignatureAlgorithm-TLS*": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-NoTickets1-TLS*": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-NoTickets2-TLS*": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-Version2": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-CertificateRequest": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-CertificateCompression-HandshakerOnly": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-CertificateCompression-ShimOnly": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-CertificateCompression-AlgorithmMismatch": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-CertificateCompression-InputMismatch": "Requires new shim flags that are NYI (as of March 2022)", + "TLS-HintMismatch-Version1": "Requires new shim flags that are NYI (as of March 2022)", + "CertificateSelection-*": "Certificate selection is a library-user responsibility", + "ALPNClient-AllowUnknown-*": "Botan always validates server ALPN selection against offered list", + "Resume-Server-OmitAllPSKsOnSecondClientHello": "Different error code for inconsistent ClientHello", + "SendEmptySessionTicket-TLS13": "Different error handling for empty session ticket", + "CertificateInResumption-TLS13": "Unmapped error string", + "CertificateRequestInResumption-TLS13": "Unmapped error string", + "AppDataBeforeTLS13KeyChange-Empty": "Different error code for bad decrypt", + "UnencryptedEncryptedExtensions": "Different error for unencrypted record", + "TrustAnchors-Unsolicited-Certificate": "Different alert code for unsolicited extension", + "PSK-Server-OmitAllPSKsOnSecondClientHello-TLS": "Botan reports INCONSISTENT_CLIENT_HELLO instead of MISSING_EXTENSION", + "PSK-Server-HRR-PSKMissing-TLS": "Botan sends handshake_failure alert instead of illegal_parameter", + "PSK-Server-MissingPSKMode-NoMatch-TLS": "Botan reports PSK_IDENTITY_NOT_FOUND instead of NO_SUPPORTED_PSK_MODE", + "DTLS-ECH*": "No ECH support", + "KeyUpdate-*-DTLS": "No DTLS 1.3", + "AppDataBeforeTLS13KeyChange-DTLS*": "No DTLS 1.3", + "UnencryptedEncryptedExtensions-DTLS": "No DTLS 1.3", + "TLS13-OnlyPadding-DTLS": "No DTLS 1.3", + "Resume-*-TLS13-TLS12-DTLS": "No DTLS 1.3", + "Downgrade-TLS12-*-DTLS": "No DTLS 1.3", + "WrongMessageType-TLS13-*-DTLS": "No DTLS 1.3", + "TrailingMessageData-TLS13-EncryptedExtensions-DTLS": "No DTLS 1.3", + "TrailingMessageData-TLS13-ServerCertificateVerify-DTLS": "No DTLS 1.3", + "KeyChangeWithBufferedMessages-DTLS": "No DTLS 1.3", + "Renegotiate-DTLS-Server-Forbidden": "Botan tolerates DTLS renegotiation", + "Renegotiate-DTLS-Client-Forbidden": "Botan tolerates DTLS renegotiation", + "DTLS12-SendExtraFinished-*": "Botan tolerates extra Finished messages in DTLS", + "MixCompleteMessageWithFragments-DTLS-TLS12": "DTLS fragment handling difference", + "RejectPSSKeyType-*": "Botan does not reject RSA-PSS key type", + "CertificateCipherMismatch-PSS": "Botan does not reject PSS cipher mismatch", + "ServerNameExtensionServer-*-TLS12": "Botan does not echo server_name in TLS 1.2 ServerHello", + "IgnoreLegacyVersion-TLS13": "Botan strictly validates legacy_version in TLS 1.3 ClientHello", + "MTU-DTLS12-3DES-CBC": "No 3DES support", + "TLS13-Client-*TicketFlags": "Botan does not strictly validate ticket flags encoding", + "TLS12-NoTicket-NoOffer": "Different session ticket/ID handling", + "PAKE-*": "No PAKE support", + "TrustAnchors-EmptyID-*": "No TrustAnchors extension support", + "TrustAnchors-ServerSelect-*": "No TrustAnchors extension support", + "TrustAnchors-ServerReceiveEmptyRequest": "No TrustAnchors extension support", + "ExtensionTrailingData-TrustAnchors-ClientHello-Server-TLS-TLS13": "No TrustAnchors extension support", + "PSK-Server-CertOrPSK-Cert-*": "Botan shim does not honor credential ordering between PSK and X.509", + "ClientCertificateType-Client-OffersRPKOnly-ServerOmitsExtension-TLS13": "Botan does not reject when server omits cert_type extension and client only offers RPK", + "TooManyKeyUpdates": "BoringSSL's approach to KeyUpdate throttling is ineffective and pointless", + "*ML-DSA*": "Need support for the LAMPS tagged seed keys", + "TLS13-EmptyRecords-DTLS": "No DTLS 1.3", + "TLS13-RecordPadding-DTLS": "No DTLS 1.3", + "ClientCertificateType-Server-*-TLS12": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ClientCertificateType-Client-*-TLS12*": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ServerCertificateType-Server-*-TLS12*": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ServerCertificateType-Client-*-TLS12": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ServerCertificateType-Client-RequestsRPKOnly-*-TLS12": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ServerCertificateType-Client-RequestsRPKX509-*-TLS12": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ServerCertificateType-Client-RequestsX509RPK-*-TLS12": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ServerCertificateType-Client-RPKVerifyFail-TLS12": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ServerCertificateType-Client-RequestsDefaultOnly-ServerPickedRPKInError-TLS12": "Botan does not support RPK certificate type negotiation in TLS 1.2", + "ClientCertificateType-Server-RejectsInvalidDefaultOnly-VerifyPeer-TLS13": "Botan does not reject X509-only cert type extension", + "ClientCertificateType-Server-NoClientHelloCertTypes-NoSharedType-VerifyPeer-TLS13": "Botan does not reject when no shared cert type exists", + "ServerCertificateType-Server-RejectsDefaultOnly-TLS12": "Botan does not reject X509-only cert type extension as server", + "ServerCertificateType-Server-RejectsDefaultOnly-TLS13": "Botan does not reject X509-only cert type extension as server", + "ServerCertificateType-Client-RequestsRPKOnly-ServerPickedX509ByDefaultInError-TLS13": "Botan does not enforce client-side cert type rejection of unsolicited X509", + "PSK-Client-PSKRequired-TLS": "Botan client does not enforce PSK-only mode", + "PSK-Client-PSKRequired-TLS12-TLS": "Botan client does not enforce PSK-only mode", + "PSK-*-DTLS": "No DTLS 1.3", + "NotJustKyberKeyShare*": "We no longer support Kyber r3 key exchange", + "KyberKeyShareIncludedSecond*": "We no longer support Kyber r3 key exchange", + "KyberKeyShareIncludedThird*": "We no longer support Kyber r3 key exchange", + "CustomKeyShares-All-TLS13": "We no longer support Kyber r3 key exchange", + "DTLS-Replay-NonMonotonic*": "Needs investigation, started failing after https://github.com/google/boringssl/commit/f94f3ed3965ea033001fb9ae006084eee408b861" + }, + "ErrorMap": { + ":CLIENTHELLO_PARSE_FAILED:": [ + ":DECODE_ERROR:" + ], + ":BAD_DECRYPT:": [ + ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:" + ], + ":ERROR_PARSING_EXTENSION:": [ + ":ERROR_PARSING_EXTENSION:", + ":DECODE_ERROR:" + ] + } +} diff -Nru botan3-3.7.1+dfsg/src/build-data/arch/alpha.txt botan3-3.12.0+dfsg/src/build-data/arch/alpha.txt --- botan3-3.7.1+dfsg/src/build-data/arch/alpha.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/arch/alpha.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,3 @@ -endian little -wordsize 64 axp diff -Nru botan3-3.7.1+dfsg/src/build-data/arch/arm32.txt botan3-3.12.0+dfsg/src/build-data/arch/arm32.txt --- botan3-3.7.1+dfsg/src/build-data/arch/arm32.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/arch/arm32.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,3 @@ -endian little family arm diff -Nru botan3-3.7.1+dfsg/src/build-data/arch/arm64.txt botan3-3.12.0+dfsg/src/build-data/arch/arm64.txt --- botan3-3.7.1+dfsg/src/build-data/arch/arm64.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/arch/arm64.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,3 @@ -endian little -wordsize 64 family arm diff -Nru botan3-3.7.1+dfsg/src/build-data/arch/generic.txt botan3-3.12.0+dfsg/src/build-data/arch/generic.txt --- botan3-3.7.1+dfsg/src/build-data/arch/generic.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/arch/generic.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,4 @@ # This target can be used when building an amalgamation which must -# be built on multiple architectures, or when targetting a CPU +# be built on multiple architectures, or when targeting a CPU # which the build system doesn't know about. diff -Nru botan3-3.7.1+dfsg/src/build-data/arch/ia64.txt botan3-3.12.0+dfsg/src/build-data/arch/ia64.txt --- botan3-3.7.1+dfsg/src/build-data/arch/ia64.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/arch/ia64.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,3 @@ -wordsize 64 itanium diff -Nru botan3-3.7.1+dfsg/src/build-data/arch/llvm.txt botan3-3.12.0+dfsg/src/build-data/arch/llvm.txt --- botan3-3.7.1+dfsg/src/build-data/arch/llvm.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/arch/llvm.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1 +0,0 @@ -wordsize 64 diff -Nru botan3-3.7.1+dfsg/src/build-data/arch/loongarch64.txt botan3-3.12.0+dfsg/src/build-data/arch/loongarch64.txt --- botan3-3.7.1+dfsg/src/build-data/arch/loongarch64.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/arch/loongarch64.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,3 +1,6 @@ family loongarch -endian little -wordsize 64 + + +lsx +lasx + diff -Nru botan3-3.7.1+dfsg/src/build-data/arch/m68k.txt botan3-3.12.0+dfsg/src/build-data/arch/m68k.txt --- botan3-3.7.1+dfsg/src/build-data/arch/m68k.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/arch/m68k.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,3 @@ -endian big 680x0 diff -Nru botan3-3.7.1+dfsg/src/build-data/arch/mips64.txt botan3-3.12.0+dfsg/src/build-data/arch/mips64.txt --- botan3-3.7.1+dfsg/src/build-data/arch/mips64.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/arch/mips64.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,3 @@ -wordsize 64 mips64el diff -Nru botan3-3.7.1+dfsg/src/build-data/arch/powerpcspe.txt botan3-3.12.0+dfsg/src/build-data/arch/powerpcspe.txt --- botan3-3.7.1+dfsg/src/build-data/arch/powerpcspe.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/arch/powerpcspe.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,3 +1,2 @@ -endian big family ppc diff -Nru botan3-3.7.1+dfsg/src/build-data/arch/ppc32.txt botan3-3.12.0+dfsg/src/build-data/arch/ppc32.txt --- botan3-3.7.1+dfsg/src/build-data/arch/ppc32.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/arch/ppc32.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,3 @@ -endian big family ppc @@ -6,7 +5,3 @@ powerpc ppc - - -altivec - diff -Nru botan3-3.7.1+dfsg/src/build-data/arch/ppc64.txt botan3-3.12.0+dfsg/src/build-data/arch/ppc64.txt --- botan3-3.7.1+dfsg/src/build-data/arch/ppc64.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/arch/ppc64.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,7 +1,5 @@ -endian big family ppc -wordsize 64 powerpc64 diff -Nru botan3-3.7.1+dfsg/src/build-data/arch/riscv32.txt botan3-3.12.0+dfsg/src/build-data/arch/riscv32.txt --- botan3-3.7.1+dfsg/src/build-data/arch/riscv32.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/arch/riscv32.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,2 +1 @@ family riscv -endian little diff -Nru botan3-3.7.1+dfsg/src/build-data/arch/riscv64.txt botan3-3.12.0+dfsg/src/build-data/arch/riscv64.txt --- botan3-3.7.1+dfsg/src/build-data/arch/riscv64.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/arch/riscv64.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,3 +1 @@ family riscv -endian little -wordsize 64 diff -Nru botan3-3.7.1+dfsg/src/build-data/arch/s390.txt botan3-3.12.0+dfsg/src/build-data/arch/s390.txt --- botan3-3.7.1+dfsg/src/build-data/arch/s390.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/arch/s390.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1 +0,0 @@ -endian big diff -Nru botan3-3.7.1+dfsg/src/build-data/arch/s390x.txt botan3-3.12.0+dfsg/src/build-data/arch/s390x.txt --- botan3-3.7.1+dfsg/src/build-data/arch/s390x.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/arch/s390x.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,2 +0,0 @@ -endian big -wordsize 64 diff -Nru botan3-3.7.1+dfsg/src/build-data/arch/sparc32.txt botan3-3.12.0+dfsg/src/build-data/arch/sparc32.txt --- botan3-3.7.1+dfsg/src/build-data/arch/sparc32.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/arch/sparc32.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,3 @@ -endian big family sparc diff -Nru botan3-3.7.1+dfsg/src/build-data/arch/sparc64.txt botan3-3.12.0+dfsg/src/build-data/arch/sparc64.txt --- botan3-3.7.1+dfsg/src/build-data/arch/sparc64.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/arch/sparc64.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,3 +1 @@ family sparc -wordsize 64 -endian big diff -Nru botan3-3.7.1+dfsg/src/build-data/arch/wasm.txt botan3-3.12.0+dfsg/src/build-data/arch/wasm.txt --- botan3-3.7.1+dfsg/src/build-data/arch/wasm.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/arch/wasm.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,2 +1,3 @@ -endian little -wordsize 32 + +simd128 + diff -Nru botan3-3.7.1+dfsg/src/build-data/arch/x32.txt botan3-3.12.0+dfsg/src/build-data/arch/x32.txt --- botan3-3.7.1+dfsg/src/build-data/arch/x32.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/arch/x32.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,3 @@ -endian little family x86 diff -Nru botan3-3.7.1+dfsg/src/build-data/arch/x86_32.txt botan3-3.12.0+dfsg/src/build-data/arch/x86_32.txt --- botan3-3.7.1+dfsg/src/build-data/arch/x86_32.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/arch/x86_32.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,3 @@ -endian little family x86 diff -Nru botan3-3.7.1+dfsg/src/build-data/arch/x86_64.txt botan3-3.12.0+dfsg/src/build-data/arch/x86_64.txt --- botan3-3.7.1+dfsg/src/build-data/arch/x86_64.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/arch/x86_64.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,3 @@ -endian little -wordsize 64 family x86 @@ -23,6 +21,7 @@ sse41 ssse3 avx512 +avx512_clmul vaes sha512 sm3 diff -Nru botan3-3.7.1+dfsg/src/build-data/botan-config-version.cmake.in botan3-3.12.0+dfsg/src/build-data/botan-config-version.cmake.in --- botan3-3.7.1+dfsg/src/build-data/botan-config-version.cmake.in 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/botan-config-version.cmake.in 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,25 @@ -set(PACKAGE_VERSION %{version}) +# Copyright (c) 2023-present The Botan Authors (see doc/authors.txt) +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +set(PACKAGE_VERSION %{version_major}.%{version_minor}.%{version_patch}) # Botan follows semver: # * the requested version should be less or equal to the installed version, however diff -Nru botan3-3.7.1+dfsg/src/build-data/botan-config.cmake.in botan3-3.12.0+dfsg/src/build-data/botan-config.cmake.in --- botan3-3.7.1+dfsg/src/build-data/botan-config.cmake.in 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/botan-config.cmake.in 2026-05-07 01:38:28.000000000 +0000 @@ -1,3 +1,24 @@ +# Copyright (c) 2023-present The Botan Authors (see doc/authors.txt) +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + #.rst: # botan-config.cmake # ----------- @@ -19,11 +40,18 @@ # # This module defines :prop_tgt:`IMPORTED` targets: # -# ``Botan::Botan`` +# ``botan::botan`` # The botan shared library, if found. -# ``Botan::Botan-static`` +# ``botan::botan-static`` # The botan static library, if found. # +# Previous versions of this CMake module defined the targets in uppercase, +# such as ``Botan::Botan``, for backward-compatibility we define those as +# aliases (if CMake is 3.18 or newer, see GH #5098): +# +# ``Botan::Botan`` as an alias for ``botan::botan`` +# ``Botan::Botan-static`` as an alias for ``botan::botan-static`` +# # Result variables # ^^^^^^^^^^^^^^^^ # @@ -65,51 +93,88 @@ return() endif() -# botan-config.cmake lives in "${_Botan_PREFIX}/lib/cmake/Botan-X": traverse up to $_Botan_PREFIX -set(_Botan_PREFIX "${CMAKE_CURRENT_LIST_DIR}") -get_filename_component(_Botan_PREFIX "${_Botan_PREFIX}" DIRECTORY) -get_filename_component(_Botan_PREFIX "${_Botan_PREFIX}" DIRECTORY) -get_filename_component(_Botan_PREFIX "${_Botan_PREFIX}" DIRECTORY) +# botan-config.cmake lives N levels below the install prefix in the file system. +# Traverse up to the prefix, but first snap to the known original install path +# if loaded from there -- this handles cross-prefix symbolic links such as +# /lib -> /usr/lib that would otherwise make the traversal land at the wrong root. +get_filename_component(_realCurr "${CMAKE_CURRENT_LIST_DIR}" REALPATH) +get_filename_component(_realOrig "%{cmake_install_dir}" REALPATH) +if(_realCurr STREQUAL _realOrig) + set(_Botan_PREFIX "%{cmake_install_dir}") +else() + set(_Botan_PREFIX "${CMAKE_CURRENT_LIST_DIR}") +endif() +unset(_realCurr) +unset(_realOrig) + +# Traverse from the cmake config directory up to the install prefix. +# The exact number of steps is fixed at configure time from the known layout. +%{for cmake_relpath_components} +get_filename_component(_Botan_PREFIX "${_Botan_PREFIX}" PATH) +%{endfor} +if(_Botan_PREFIX STREQUAL "/") + set(_Botan_PREFIX "") +endif() + +set(_Botan_INCLUDE_DIR "${_Botan_PREFIX}/%{namespaced_includedir_rel}") +set(_Botan_LIB_PREFIX "${_Botan_PREFIX}/%{libdir_rel}") + +%{if bindir_rel} +set(_Botan_BIN_DIR "${_Botan_PREFIX}/%{bindir_rel}") +%{endif} +%{unless bindir_rel} +set(_Botan_BIN_DIR "%{bindir}") +%{endif} %{if build_static_lib} -if(NOT TARGET Botan::Botan-static) - add_library(Botan::Botan-static STATIC IMPORTED) - set_target_properties(Botan::Botan-static +if(NOT TARGET botan::botan-static) + add_library(botan::botan-static STATIC IMPORTED) + set_target_properties(botan::botan-static PROPERTIES - IMPORTED_LOCATION "${_Botan_PREFIX}/lib/%{static_lib_name}" - INTERFACE_INCLUDE_DIRECTORIES "${_Botan_PREFIX}/include/botan-%{version_major}" + IMPORTED_LOCATION "${_Botan_LIB_PREFIX}/%{static_lib_name}" + INTERFACE_INCLUDE_DIRECTORIES "${_Botan_INCLUDE_DIR}" IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" INTERFACE_LINK_OPTIONS "SHELL:%{cxx_abi_flags}") + + # TODO(Botan4): Remove this alias + if(NOT ${CMAKE_VERSION} VERSION_LESS "3.18.0") # 3.18 allows creating ALIAS targets to non-GLOBAL targets + add_library(Botan::Botan-static ALIAS botan::botan-static) + endif() endif() %{endif} %{if implib_name} -set(_Botan_implib "${_Botan_PREFIX}/lib/%{implib_name}") -set(_Botan_shared_lib "${_Botan_PREFIX}/bin/%{shared_lib_name}") +set(_Botan_implib "${_Botan_LIB_PREFIX}/%{implib_name}") +set(_Botan_shared_lib "${_Botan_BIN_DIR}/%{shared_lib_name}") %{endif} %{unless implib_name} set(_Botan_implib "") %{endif} %{if build_shared_lib} -if(NOT TARGET Botan::Botan) +if(NOT TARGET botan::botan) if(NOT DEFINED _Botan_shared_lib) - set(_Botan_shared_lib "${_Botan_PREFIX}/lib/%{shared_lib_name}") + set(_Botan_shared_lib "${_Botan_LIB_PREFIX}/%{shared_lib_name}") endif() - add_library(Botan::Botan SHARED IMPORTED) - set_target_properties(Botan::Botan + add_library(botan::botan SHARED IMPORTED) + set_target_properties(botan::botan PROPERTIES IMPORTED_LOCATION "${_Botan_shared_lib}" IMPORTED_IMPLIB "${_Botan_implib}" - INTERFACE_INCLUDE_DIRECTORIES "${_Botan_PREFIX}/include/botan-%{version_major}" + INTERFACE_INCLUDE_DIRECTORIES "${_Botan_INCLUDE_DIR}" INTERFACE_LINK_OPTIONS "SHELL:%{cxx_abi_flags}") - set_property(TARGET Botan::Botan APPEND PROPERTY IMPORTED_CONFIGURATIONS NOCONFIG) - set_target_properties(Botan::Botan + set_property(TARGET botan::botan APPEND PROPERTY IMPORTED_CONFIGURATIONS NOCONFIG) + set_target_properties(botan::botan PROPERTIES - IMPORTED_LOCATION_NOCONFIG "${_Botan_PREFIX}/lib/%{shared_lib_name}" + IMPORTED_LOCATION_NOCONFIG "${_Botan_LIB_PREFIX}/%{shared_lib_name}" IMPORTED_SONAME_NOCONFIG "%{shared_lib_name}" IMPORTED_IMPLIB_NOCONFIG "${_Botan_implib}") + + # TODO(Botan4): Remove this alias + if(NOT ${CMAKE_VERSION} VERSION_LESS "3.18.0") # 3.18 allows creating ALIAS targets to non-GLOBAL targets + add_library(Botan::Botan ALIAS botan::botan) + endif() endif() %{endif} diff -Nru botan3-3.7.1+dfsg/src/build-data/botan.pc.in botan3-3.12.0+dfsg/src/build-data/botan.pc.in --- botan3-3.7.1+dfsg/src/build-data/botan.pc.in 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/botan.pc.in 2026-05-07 01:38:28.000000000 +0000 @@ -1,7 +1,7 @@ prefix=%{prefix} exec_prefix=${prefix} -libdir=%{libdir} -includedir=${prefix}/include/botan-%{version_major} +libdir=${prefix}/%{libdir_rel} +includedir=${prefix}/%{namespaced_includedir_rel} Name: Botan Description: Crypto and TLS for Modern C++ diff -Nru botan3-3.7.1+dfsg/src/build-data/buildh.in botan3-3.12.0+dfsg/src/build-data/buildh.in --- botan3-3.7.1+dfsg/src/build-data/buildh.in 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/buildh.in 2026-05-07 01:38:28.000000000 +0000 @@ -1,19 +1,13 @@ -#ifndef BOTAN_BUILD_CONFIG_H_ -#define BOTAN_BUILD_CONFIG_H_ +#ifndef BOTAN_BUILD_INFO_H_ +#define BOTAN_BUILD_INFO_H_ /** * @file build.h * @brief Build configuration for Botan %{version} -* -* Automatically generated from -* '%{command_line}' -* -* Target -* - Compiler: %{cxx} %{cxx_abi_flags} %{cc_lang_flags} %{cc_compile_flags} -* - Arch: %{arch} -* - OS: %{os} */ +/* NOLINTBEGIN(*-macro-usage,*-macro-to-enum) */ + /** * @defgroup buildinfo Build Information */ @@ -24,42 +18,63 @@ * @{ */ -/// The major version of the release +/** +* The major version of the release +*/ #define BOTAN_VERSION_MAJOR %{version_major} -/// The minor version of the release + +/** +* The minor version of the release +*/ #define BOTAN_VERSION_MINOR %{version_minor} -/// The patch version of the release + +/** +* The patch version of the release +*/ #define BOTAN_VERSION_PATCH %{version_patch} /** * Expands to an integer of the form YYYYMMDD if this is an official * release, or 0 otherwise. For instance, 2.19.0, which was released * on January 19, 2022, has a `BOTAN_VERSION_DATESTAMP` of 20220119. + * + * This macro is deprecated; use version_datestamp from version.h + * + * TODO(Botan4) remove this */ #define BOTAN_VERSION_DATESTAMP %{version_datestamp} -%{if version_suffix} -#define BOTAN_VERSION_SUFFIX %{version_suffix} -#define BOTAN_VERSION_SUFFIX_STR "%{version_suffix}" -%{endif} - +/** + * A string set to the release type + * + * This macro is deprecated + * + * TODO(Botan4) remove this + */ #define BOTAN_VERSION_RELEASE_TYPE "%{release_type}" /** * A macro expanding to a string that is set to a revision identifier * corresponding to the source, or "unknown" if this could not be - * determined. It is set for all official releases, and for builds that - * originated from within a git checkout. + * determined. It is set for all official releases. + * + * This macro is deprecated; use version_vc_revision from version.h + * + * TODO(Botan4) remove this */ -#define BOTAN_VERSION_VC_REVISION "%{version_vc_rev}" +#define BOTAN_VERSION_VC_REVISION "%{version_vc_rev_or_unknown}" /** * A macro expanding to a string that is set at build time using the * `--distribution-info` option. It allows a packager of the library * to specify any distribution-specific patches. If no value is given * at build time, the value is the string "unspecified". + * + * This macro is deprecated; use version_distribution_info from version.h + * + * TODO(Botan4) remove this */ -#define BOTAN_DISTRIBUTION_INFO "%{distribution_info}" +#define BOTAN_DISTRIBUTION_INFO "%{distribution_info_or_unspecified}" /** * @} @@ -71,97 +86,42 @@ * @{ */ -/** How many bits per limb in a BigInt */ -#define BOTAN_MP_WORD_BITS %{mp_bits} - -%{if fuzzer_mode} -/** Disables certain validation checks to ease fuzzability of the library - * @warning This causes the library build to be insecure, hence, it must not be - * used in a production environment! - */ -#define BOTAN_UNSAFE_FUZZER_MODE -%{endif} %{if fuzzer_type} #define BOTAN_FUZZERS_ARE_BEING_BUILT -#define BOTAN_FUZZER_IS_%{fuzzer_type} %{endif} %{if disable_deprecated_features} +/** + * Indicates that deprecated features have been disabled + */ #define BOTAN_DISABLE_DEPRECATED_FEATURES %{endif} %{if enable_experimental_features} +/** + * Indicates that experimental features have been enabled + */ #define BOTAN_ENABLE_EXPERIMENTAL_FEATURES %{endif} -#define BOTAN_INSTALL_PREFIX R"(%{prefix})" -#define BOTAN_INSTALL_HEADER_DIR R"(%{includedir}/botan-%{version_major})" -#define BOTAN_INSTALL_LIB_DIR R"(%{libdir})" -#define BOTAN_LIB_LINK "%{link_to}" -#define BOTAN_LINK_FLAGS "%{cxx_abi_flags}" - -%{if system_cert_bundle} -#define BOTAN_SYSTEM_CERT_BUNDLE "%{system_cert_bundle}" -%{endif} - #ifndef BOTAN_DLL #define BOTAN_DLL %{visibility_attribute} #endif /* Target identification and feature test macros */ -#define BOTAN_TARGET_OS_IS_%{os_name|upper} - -%{for os_features} +%{for os_features_public} #define BOTAN_TARGET_OS_HAS_%{i|upper} %{endfor} -#define BOTAN_BUILD_COMPILER_IS_%{cc_macro} - -%{if cxx_supports_gcc_inline_asm} -#define BOTAN_USE_GCC_INLINE_ASM -%{endif} - -%{if cxx_ct_value_barrier_type} -#define BOTAN_CT_VALUE_BARRIER_USE_%{cxx_ct_value_barrier_type|upper} -%{endif} - -%{for sanitizer_types} -#define BOTAN_HAS_SANITIZER_%{i|upper} -%{endfor} - -#define BOTAN_TARGET_ARCH "%{arch}" -#define BOTAN_TARGET_ARCH_IS_%{arch|upper} -%{if endian} -#define BOTAN_TARGET_CPU_IS_%{endian|upper}_ENDIAN -%{endif} -%{if cpu_family} -#define BOTAN_TARGET_CPU_IS_%{cpu_family|upper}_FAMILY -%{endif} -%{if cpu_is_64bit} -#define BOTAN_TARGET_CPU_HAS_NATIVE_64BIT -%{endif} - -%{for cpu_features} -#define BOTAN_TARGET_SUPPORTS_%{i|upper} -%{endfor} - -%{if with_valgrind} -#define BOTAN_HAS_VALGRIND -%{endif} - %{if with_debug_asserts} +/** + * Has to be public due to use in assert.h + * TODO(Botan4) move this to target_info.h once assert.h is internal + */ #define BOTAN_ENABLE_DEBUG_ASSERTS %{endif} -%{if terminate_on_asserts} -#define BOTAN_TERMINATE_ON_ASSERTS -%{endif} - -%{if optimize_for_size} -#define BOTAN_OPTIMIZE_FOR_SIZE -%{endif} - /** * @} */ @@ -179,110 +139,21 @@ #define BOTAN_HAS_%{i} %{endfor} -/** - * @} - */ - -/** - * @addtogroup buildinfo_configuration - * @{ - */ - -/** Local/misc configuration options (if any) follow */ -%{local_config} - /* -* Things you can edit (but probably shouldn't) -*/ - -/** How much to allocate for a buffer of no particular size */ -#define BOTAN_DEFAULT_BUFFER_SIZE 4096 - -#if defined(BOTAN_HAS_VALGRIND) || defined(BOTAN_ENABLE_DEBUG_ASSERTS) - /** - * @brief Prohibits access to unused memory pages in Botan's memory pool - * - * If BOTAN_MEM_POOL_USE_MMU_PROTECTIONS is defined, the Memory_Pool - * class used for mlock'ed memory will use OS calls to set page - * permissions so as to prohibit access to pages on the free list, then - * enable read/write access when the page is set to be used. This will - * turn (some) use after free bugs into a crash. - * - * The additional syscalls have a substantial performance impact, which - * is why this option is not enabled by default. It is used when built for - * running in valgrind or debug assertions are enabled. - */ - #define BOTAN_MEM_POOL_USE_MMU_PROTECTIONS -#endif - -#if defined(BOTAN_HAS_VALGRIND) - /** - * If `BOTAN_CT_POISON_ENABLED` is defined, then the `CT::poison` and - * `CT::unpoison` functions have an effect and do not just compile to no-ops. - * - * At the moment that is only the case when building with valgrind support. We - * could potentially add support for other tools in the future. - */ - #define BOTAN_CT_POISON_ENABLED -#endif - -/** -* If enabled uses memset via volatile function pointer to zero memory, -* otherwise does a byte at a time write via a volatile pointer. -*/ -#define BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO 1 - -/** -* Normally blinding is performed by choosing a random starting point (plus -* its inverse, of a form appropriate to the algorithm being blinded), and -* then choosing new blinding operands by successive squaring of both -* values. This is much faster than computing a new starting point but -* introduces some possible corelation +* Internal module feature definitions * -* To avoid possible leakage problems in long-running processes, the blinder -* periodically reinitializes the sequence. This value specifies how often -* a new sequence should be started. -*/ -#define BOTAN_BLINDING_REINIT_INTERVAL 64 - -/** -* Userspace RNGs like HMAC_DRBG will reseed after a specified number -* of outputs are generated. Set to zero to disable automatic reseeding. -*/ -#define BOTAN_RNG_DEFAULT_RESEED_INTERVAL 1024 - -/** Number of entropy bits polled for reseeding userspace RNGs like HMAC_DRBG */ -#define BOTAN_RNG_RESEED_POLL_BITS 256 - -#define BOTAN_RNG_RESEED_DEFAULT_TIMEOUT std::chrono::milliseconds(50) - -/** -* Specifies (in order) the list of entropy sources that will be used -* to seed an in-memory RNG. +* These macros have been in the past visible in build.h as feature macros +* but in the future these will be only visible in an internal header. +* Applications should not rely on or check for these macros. */ -#define BOTAN_ENTROPY_DEFAULT_SOURCES \ - { "rdseed", "hwrng", "getentropy", "system_rng", "system_stats" } - -/** Multiplier on a block cipher's native parallelism */ -#define BOTAN_BLOCK_CIPHER_PAR_MULT 4 +%{for module_internal_defines} +#define BOTAN_HAS_%{i} +%{endfor} /** * @} */ -/* Check for a common build problem */ - -#if defined(BOTAN_TARGET_ARCH_IS_X86_64) && ((defined(_MSC_VER) && !defined(_WIN64)) || \ - (defined(__clang__) && !defined(__x86_64__)) || \ - (defined(__GNUG__) && !defined(__x86_64__))) - #error "Trying to compile Botan configured as x86_64 with non-x86_64 compiler." -#endif - -#if defined(BOTAN_TARGET_ARCH_IS_X86_32) && ((defined(_MSC_VER) && defined(_WIN64)) || \ - (defined(__clang__) && !defined(__i386__)) || \ - (defined(__GNUG__) && !defined(__i386__))) - - #error "Trying to compile Botan configured as x86_32 with non-x86_32 compiler." -#endif +/* NOLINTEND(*-macro-usage,*-macro-to-enum) */ #endif diff -Nru botan3-3.7.1+dfsg/src/build-data/cc/clang.txt botan3-3.12.0+dfsg/src/build-data/cc/clang.txt --- botan3-3.7.1+dfsg/src/build-data/cc/clang.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/cc/clang.txt 2026-05-07 01:38:28.000000000 +0000 @@ -69,6 +69,8 @@ avx2 -> "-mavx2" avx512 -> "-mavx512f -mavx512bw -mavx512dq -mavx512vbmi -mavx512vbmi2 -mavx512bitalg -mavx512vl -mavx512ifma" +avx512_clmul -> "-mavx512f -mavx512bw -mavx512dq -mavx512vbmi -mavx512vbmi2 -mavx512bitalg -mavx512vl -mavx512ifma -mvpclmulqdq" + vaes -> "-mvaes -mavx2" sha512 -> "-msha512 -mavx2" sm3 -> "-msm3" @@ -88,6 +90,11 @@ arm64:armv8crypto -> "-march=armv8+crypto" arm64:armv8sha512 -> "-march=armv8.2-a+sha3" +arm64:armv8sm3 -> "-march=armv8.2-a+sm4" +arm64:armv8sm4 -> "-march=armv8.2-a+sm4" + +loongarch64:lsx -> "-mlsx" +loongarch64:lasx -> "-mlasx" arm32:neon -> "-mfpu=neon" arm64:neon -> "" diff -Nru botan3-3.7.1+dfsg/src/build-data/cc/clangcl.txt botan3-3.12.0+dfsg/src/build-data/cc/clangcl.txt --- botan3-3.7.1+dfsg/src/build-data/cc/clangcl.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/cc/clangcl.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,106 @@ +macro_name CLANGCL + +minimum_supported_version 14.0 + +binary_name clang-cl +linker_name lld-link + +output_to_object "/Fo" +output_to_exe "/OUT:" + +add_include_dir_option "/I" +add_system_include_dir_option "/external:W0 /external:I" +add_lib_dir_option "/LIBPATH:" +add_compile_definition_option "/D" +add_lib_option "%s.lib" + +compile_flags "/nologo /c" + +supports_gcc_inline_asm yes + +optimization_flags "/O2 /Oi" +size_optimization_flags "/O1 /Os" + +# for debug info in the object file (required if using sccache): +#debug_info_flags "/Z7" + +# for using a PDB file: +debug_info_flags "/Zi /FS" + +preproc_flags "/nologo /EP" + +# clang-cl has /Zc:preprocessor behavior by default, and does not accept the flag +lang_flags "/Zc:inline /std:c++20 /EHs /GR" + +# 4251: STL types used in DLL interface +# 4275: ??? +# 5072: ASan without debug info +warning_flags "/W4 /wd4251 /wd4275 /wd5072" + +werror_flags "/WX" + +visibility_build_flags "/DBOTAN_DLL=__declspec(dllexport)" +visibility_attribute "__declspec(dllimport)" + +# Include dependency tracking for Ninja +# See: https://ninja-build.org/manual.html#ref_headers +ninja_header_deps_style 'msvc' +header_deps_flag '/showIncludes' + +ar_command lib +ar_options "/nologo" +ar_output_to "/OUT:" + + +default -> address + +iterator -> "/D_ITERATOR_DEBUG_LEVEL=1" +address -> "/fsanitize=address" + + + +sse2 -> "-msse2" +ssse3 -> "-mssse3" +sse41 -> "-msse4.1" +avx2 -> "-mavx2" +avx512 -> "-mavx512f -mavx512bw -mavx512dq -mavx512vbmi -mavx512vbmi2 -mavx512bitalg -mavx512vl -mavx512ifma" + +bmi2 -> "-mbmi -mbmi2" +aesni -> "-maes -mpclmul" +rdrand -> "-mrdrnd" +rdseed -> "-mrdseed" +sha -> "-msha" +altivec -> "-maltivec" + +arm64:armv8crypto -> "-march=armv8+crypto" +arm64:armv8sha512 -> "-march=armv8.2-a+sha3" + +arm32:neon -> "-mfpu=neon" +arm64:neon -> "" + + + +debug -> "/Fd%{build_dir}/%{libname}.pdb" + + + +default -> "{linker} /DLL" +default-debug -> "{linker} /DLL /DEBUG" + + + +default -> "{linker}" +default-debug -> "{linker} /DEBUG" + + + +all -> "/bigobj" + +# These can be overridden with --msvc-runtime option +rt -> "/MD" +rt-debug -> "/MDd" + + + +default -> asm + diff -Nru botan3-3.7.1+dfsg/src/build-data/cc/emcc.txt botan3-3.12.0+dfsg/src/build-data/cc/emcc.txt --- botan3-3.7.1+dfsg/src/build-data/cc/emcc.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/cc/emcc.txt 2026-05-07 01:38:28.000000000 +0000 @@ -2,8 +2,8 @@ binary_name em++ -lang_flags "-s DISABLE_EXCEPTION_CATCHING=0 -std=c++20 -D_REENTRANT" -lang_binary_linker_flags "-s ALLOW_MEMORY_GROWTH=1 -s WASM=1 -s NO_DISABLE_EXCEPTION_CATCHING" +lang_flags "-fwasm-exceptions -std=c++20 -D_REENTRANT" +lang_binary_linker_flags "-s ALLOW_MEMORY_GROWTH=1 -s WASM=1 -fwasm-exceptions" warning_flags "-Wall -Wextra -Wpedantic -Wshadow -Wstrict-aliasing -Wstrict-overflow=5 -Wcast-align -Wmissing-declarations -Wpointer-arith -Wcast-qual -Wshorten-64-to-32" @@ -22,6 +22,10 @@ default -> "false" + +simd128 -> "-msimd128" + + default -> "{cxx}" diff -Nru botan3-3.7.1+dfsg/src/build-data/cc/gcc.txt botan3-3.12.0+dfsg/src/build-data/cc/gcc.txt --- botan3-3.7.1+dfsg/src/build-data/cc/gcc.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/cc/gcc.txt 2026-05-07 01:38:28.000000000 +0000 @@ -36,6 +36,7 @@ iterator -> "-D_GLIBCXX_DEBUG" address -> "-fsanitize=address" undefined -> "-fsanitize=undefined -fno-sanitize-recover=undefined" +thread -> "-fsanitize=thread" visibility_build_flags "-fvisibility=hidden" @@ -70,6 +71,8 @@ avx2 -> "-mavx2" avx512 -> "-mavx512f -mavx512bw -mavx512dq -mavx512vbmi -mavx512vbmi2 -mavx512bitalg -mavx512vl -mavx512ifma" +avx512_clmul -> "-mavx512f -mavx512bw -mavx512dq -mavx512vbmi -mavx512vbmi2 -mavx512bitalg -mavx512vl -mavx512ifma -mvpclmulqdq" + vaes -> "-mvaes -mavx2" sha512 -> "-msha512 -mavx2" sm3 -> "-msm3" @@ -93,6 +96,9 @@ arm64:armv8sha512 -> "-march=armv8.2-a+sha3" arm64:armv8sha3 -> "-march=armv8.2-a+sha3" +loongarch64:lsx -> "-mlsx" +loongarch64:lasx -> "-mlasx" + # For Aarch32 -mfpu=neon is required # For Aarch64 NEON is enabled by default arm32:neon -> "-mfpu=neon" diff -Nru botan3-3.7.1+dfsg/src/build-data/cc/msvc.txt botan3-3.12.0+dfsg/src/build-data/cc/msvc.txt --- botan3-3.7.1+dfsg/src/build-data/cc/msvc.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/cc/msvc.txt 2026-05-07 01:38:28.000000000 +0000 @@ -20,14 +20,14 @@ size_optimization_flags "/O1 /Os" # for debug info in the object file (required if using sccache): -#debug_info_flags "/Z7" +debug_info_flags "/Z7" # for using a PDB file: -debug_info_flags "/Zi /FS" +#debug_info_flags "/Zi /FS" preproc_flags "/nologo /EP /Zc:preprocessor" -lang_flags "/Zc:preprocessor /std:c++20 /EHs /GR" +lang_flags "/Zc:preprocessor /Zc:inline /std:c++20 /EHs /GR" # 4251: STL types used in DLL interface # 4275: ??? @@ -61,11 +61,14 @@ sse41 -> "" x86_64:avx2 -> "/arch:AVX" x86_64:avx512 -> "/arch:AVX512" +x86_64:avx512_clmul -> "/arch:AVX512" aesni -> "" clmul -> "" rdrand -> "" rdseed -> "" sha -> "" +bmi2 -> "" +gfni -> "" diff -Nru botan3-3.7.1+dfsg/src/build-data/cc/sunstudio.txt botan3-3.12.0+dfsg/src/build-data/cc/sunstudio.txt --- botan3-3.7.1+dfsg/src/build-data/cc/sunstudio.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/cc/sunstudio.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -macro_name SUN_STUDIO - -binary_name CC - -optimization_flags "-xO2" - -shared_flags "-KPIC" -warning_flags "+w -erroff=truncwarn,wnoretvalue,wlessrestrictedthrow" -lang_flags "-std=c++20 +p -features=extensions" - -ar_command CC -ar_options "-xar -o" - -supports_gcc_inline_asm yes - - -default -> "{cxx} -G -h{soname_abi}" - - - -# Needed on some Linux distros -linux -> "-library=stlport4" - -sparc64 -> "-m64 -xarch=sparc" -x86_64 -> "-m64" - - - -# Botan needs C++11, and that requires Sun Studio 12.4 or above. -# Sun Studio 12.4 supports upto -xarch=avx2, but the processor must support it -# AESNI requires -xarch=aes, and RDRAND requires -xarch=avx_i. -# https://docs.oracle.com/cd/E37069_01/html/E37074/bjapp.html#OSSCGbkazd -sse2 -> "-xarch=sse2" -ssse3 -> "-xarch=ssse3" -sse41 -> "-xarch=sse4.1" -aesni -> "-xarch=aes" -rdrand -> "-xarch=avx_i" -avx2 -> "-xarch=avx2" - - - -default -> none - diff -Nru botan3-3.7.1+dfsg/src/build-data/cc/xcode.txt botan3-3.12.0+dfsg/src/build-data/cc/xcode.txt --- botan3-3.7.1+dfsg/src/build-data/cc/xcode.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/cc/xcode.txt 2026-05-07 01:38:28.000000000 +0000 @@ -60,6 +60,8 @@ avx2 -> "-mavx2" avx512 -> "-mavx512f -mavx512bw -mavx512dq -mavx512vbmi -mavx512vbmi2 -mavx512bitalg -mavx512vl -mavx512ifma" +avx512_clmul -> "-mavx512f -mavx512bw -mavx512dq -mavx512vbmi -mavx512vbmi2 -mavx512bitalg -mavx512vl -mavx512ifma -mvpclmulqdq" + bmi2 -> "-mbmi -mbmi2" aesni -> "-maes -mpclmul" rdrand -> "-mrdrnd" diff -Nru botan3-3.7.1+dfsg/src/build-data/compile_commands.json.in botan3-3.12.0+dfsg/src/build-data/compile_commands.json.in --- botan3-3.7.1+dfsg/src/build-data/compile_commands.json.in 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/compile_commands.json.in 2026-05-07 01:38:28.000000000 +0000 @@ -1,28 +1,28 @@ [ %{for lib_build_info} { "directory": "%{abs_root_dir}", - "command": "%{cxx} %{lib_flags} %{cc_sysroot} %{cxx_abi_flags} %{cc_lang_flags} %{os_feature_macros} %{cc_compile_flags} -DBOTAN_IS_BEING_BUILT %{cc_warning_flags} %{isa_flags} %{public_include_flags} %{internal_include_flags} %{external_include_flags} %{dash_c} %{src} %{dash_o}%{obj}", + "command": "%{cxx} %{lib_flags} %{cc_sysroot} %{cxx_abi_flags} %{cc_lang_flags} %{os_feature_macros} %{cc_compile_flags} -DBOTAN_IS_BEING_BUILT %{cc_warning_flags} %{public_include_flags} %{internal_include_flags} %{external_include_flags} %{dash_c} %{src} %{dash_o}%{obj}", "file": "%{src}" }, %{endfor} %{for test_build_info} { "directory": "%{abs_root_dir}", - "command": "%{cxx} %{cc_sysroot} %{cxx_abi_flags} %{cc_lang_flags} %{os_feature_macros} %{cc_compile_flags} -DBOTAN_IS_BEING_BUILT %{cc_warning_flags} %{isa_flags} %{public_include_flags} %{internal_include_flags} %{external_include_flags} %{dash_c} %{src} %{dash_o}%{obj}", + "command": "%{cxx} %{cc_sysroot} %{cxx_abi_flags} %{cc_lang_flags} %{os_feature_macros} %{cc_compile_flags} -DBOTAN_IS_BEING_BUILT %{cc_warning_flags} %{public_include_flags} %{internal_include_flags} %{external_include_flags} %{dash_c} %{src} %{dash_o}%{obj}", "file": "%{src}" }, %{endfor} %{for examples_build_info} { "directory": "%{abs_root_dir}", - "command": "%{cxx} %{cc_sysroot} %{cxx_abi_flags} %{cc_lang_flags} %{os_feature_macros} %{cc_compile_flags} %{cc_warning_flags} %{isa_flags} %{public_include_flags} %{external_include_flags} %{dash_c} %{src} %{dash_o}%{obj}", + "command": "%{cxx} %{cc_sysroot} %{cxx_abi_flags} %{cc_lang_flags} %{os_feature_macros} %{cc_compile_flags} %{cc_warning_flags} %{public_include_flags} %{external_include_flags} %{dash_c} %{src} %{dash_o}%{obj}", "file": "%{src}" }, %{endfor} %{for fuzzer_build_info} { "directory": "%{abs_root_dir}", - "command": "%{cxx} %{cc_sysroot} %{cxx_abi_flags} %{cc_lang_flags} %{os_feature_macros} %{cc_compile_flags} -DBOTAN_IS_BEING_BUILT %{cc_warning_flags} %{isa_flags} %{public_include_flags} %{internal_include_flags} %{external_include_flags} %{dash_c} %{src} %{dash_o}%{obj}", + "command": "%{cxx} %{cc_sysroot} %{cxx_abi_flags} %{cc_lang_flags} %{os_feature_macros} %{cc_compile_flags} -DBOTAN_IS_BEING_BUILT %{cc_warning_flags} %{public_include_flags} %{internal_include_flags} %{external_include_flags} %{dash_c} %{src} %{dash_o}%{obj}", "file": "%{src}" }, %{endfor} @@ -37,7 +37,7 @@ %{for cli_build_info} { "directory": "%{abs_root_dir}", - "command": "%{cxx} %{cc_sysroot} %{cxx_abi_flags} %{cc_lang_flags} %{os_feature_macros} %{cc_compile_flags} -DBOTAN_IS_BEING_BUILT %{cc_warning_flags} %{isa_flags} %{public_include_flags} %{internal_include_flags} %{external_include_flags} %{dash_c} %{src} %{dash_o}%{obj}", + "command": "%{cxx} %{cc_sysroot} %{cxx_abi_flags} %{cc_lang_flags} %{os_feature_macros} %{cc_compile_flags} -DBOTAN_IS_BEING_BUILT %{cc_warning_flags} %{public_include_flags} %{internal_include_flags} %{external_include_flags} %{dash_c} %{src} %{dash_o}%{obj}", "file": "%{src}" }%{omitlast ,} %{endfor} diff -Nru botan3-3.7.1+dfsg/src/build-data/detect_version.cpp botan3-3.12.0+dfsg/src/build-data/detect_version.cpp --- botan3-3.7.1+dfsg/src/build-data/detect_version.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/detect_version.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -3,7 +3,7 @@ * configure.py to determine the compilers version number. */ -#if defined(_MSC_VER) +#if defined(_MSC_VER) and !defined(__clang__) /* _MSC_VER Defined as an integer literal that encodes the major and diff -Nru botan3-3.7.1+dfsg/src/build-data/ec_groups.txt botan3-3.12.0+dfsg/src/build-data/ec_groups.txt --- botan3-3.7.1+dfsg/src/build-data/ec_groups.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/ec_groups.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,6 @@ Name = secp256r1 OID = 1.2.840.10045.3.1.7 +Impl = pcurve generic legacy P = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF A = -3 B = 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B @@ -9,6 +10,7 @@ Name = secp384r1 OID = 1.3.132.0.34 +Impl = pcurve generic legacy P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF A = -3 B = 0xB3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF @@ -18,6 +20,7 @@ Name = secp521r1 OID = 1.3.132.0.35 +Impl = pcurve generic legacy P = 0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF A = -3 B = 0x51953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00 @@ -27,6 +30,7 @@ Name = brainpool160r1 OID = 1.3.36.3.3.2.8.1.1.1 +Impl = legacy P = 0xE95E4A5F737059DC60DFC7AD95B3D8139515620F A = 0x340E7BE2A280EB74E2BE61BADA745D97E8F7C300 B = 0x1E589A8595423412134FAA2DBDEC95C8D8675E58 @@ -36,6 +40,7 @@ Name = brainpool192r1 OID = 1.3.36.3.3.2.8.1.1.3 +Impl = generic legacy P = 0xC302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297 A = 0x6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF B = 0x469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9 @@ -45,6 +50,7 @@ Name = brainpool224r1 OID = 1.3.36.3.3.2.8.1.1.5 +Impl = generic legacy P = 0xD7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF A = 0x68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43 B = 0x2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B @@ -54,6 +60,7 @@ Name = brainpool256r1 OID = 1.3.36.3.3.2.8.1.1.7 +Impl = pcurve generic legacy P = 0xA9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377 A = 0x7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9 B = 0x26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6 @@ -63,6 +70,7 @@ Name = brainpool320r1 OID = 1.3.36.3.3.2.8.1.1.9 +Impl = generic legacy P = 0xD35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E27 A = 0x3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9F492F375A97D860EB4 B = 0x520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD884539816F5EB4AC8FB1F1A6 @@ -72,6 +80,7 @@ Name = brainpool384r1 OID = 1.3.36.3.3.2.8.1.1.11 +Impl = pcurve generic legacy P = 0x8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53 A = 0x7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503AD4EB04A8C7DD22CE2826 B = 0x4A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DBC9943AB78696FA504C11 @@ -81,6 +90,7 @@ Name = brainpool512r1 OID = 1.3.36.3.3.2.8.1.1.13 +Impl = pcurve generic legacy P = 0xAADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3 A = 0x7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA B = 0x3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723 @@ -90,6 +100,7 @@ Name = frp256v1 OID = 1.2.250.1.223.101.256.1 +Impl = pcurve generic legacy P = 0xF1FD178C0B3AD58F10126DE8CE42435B3961ADBCABC8CA6DE8FCF353D86E9C03 A = -3 B = 0xEE353FCA5428A9300D4ABA754A44C00FDFEC0C9AE4B1A1803075ED967B7BB73F @@ -98,6 +109,7 @@ N = 0xF1FD178C0B3AD58F10126DE8CE42435B53DC67E140D2BF941FFDD459C6D655E1 Name = gost_256A +Impl = generic legacy OID = 1.2.643.7.1.2.1.1.1 1.2.643.2.2.35.1 1.2.643.2.2.36.0 P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97 A = -3 @@ -107,6 +119,7 @@ N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C611070995AD10045841B09B761B893 Name = gost_512A +Impl = generic legacy OID = 1.2.643.7.1.2.1.2.1 P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7 A = -3 @@ -116,6 +129,7 @@ N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27E69532F48D89116FF22B8D4E0560609B4B38ABFAD2B85DCACDB1411F10B275 Name = secp160k1 +Impl = legacy OID = 1.3.132.0.9 P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73 A = 0x0 @@ -125,6 +139,7 @@ N = 0x100000000000000000001B8FA16DFAB9ACA16B6B3 Name = secp160r1 +Impl = legacy OID = 1.3.132.0.8 P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF A = -3 @@ -134,6 +149,7 @@ N = 0x100000000000000000001F4C8F927AED3CA752257 Name = secp160r2 +Impl = legacy OID = 1.3.132.0.30 P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73 A = -3 @@ -143,6 +159,7 @@ N = 0x100000000000000000000351EE786A818F3A1A16B Name = secp192k1 +Impl = generic legacy OID = 1.3.132.0.31 P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37 A = 0x0 @@ -152,6 +169,7 @@ N = 0xFFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D Name = secp192r1 +Impl = pcurve generic legacy OID = 1.2.840.10045.3.1.1 P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF A = -3 @@ -161,6 +179,7 @@ N = 0xFFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831 Name = secp224k1 +Impl = legacy OID = 1.3.132.0.32 P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D A = 0x0 @@ -170,6 +189,7 @@ N = 0x10000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7 Name = secp224r1 +Impl = pcurve legacy OID = 1.3.132.0.33 P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001 A = -3 @@ -179,6 +199,7 @@ N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D Name = secp256k1 +Impl = pcurve generic legacy OID = 1.3.132.0.10 P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F A = 0x0 @@ -188,6 +209,7 @@ N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 Name = sm2p256v1 +Impl = pcurve generic legacy OID = 1.2.156.10197.1.301 P = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF A = -3 @@ -197,6 +219,7 @@ N = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123 Name = x962_p192v2 +Impl = generic legacy OID = 1.2.840.10045.3.1.2 P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF A = -3 @@ -206,6 +229,7 @@ N = 0xFFFFFFFFFFFFFFFFFFFFFFFE5FB1A724DC80418648D8DD31 Name = x962_p192v3 +Impl = generic legacy OID = 1.2.840.10045.3.1.3 P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF A = -3 @@ -215,6 +239,7 @@ N = 0xFFFFFFFFFFFFFFFFFFFFFFFF7A62D031C83F4294F640EC13 Name = x962_p239v1 +Impl = generic legacy OID = 1.2.840.10045.3.1.4 P = 0x7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF A = -3 @@ -224,6 +249,7 @@ N = 0x7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF9E5E9A9F5D9071FBD1522688909D0B Name = x962_p239v2 +Impl = generic legacy OID = 1.2.840.10045.3.1.5 P = 0x7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF A = -3 @@ -233,6 +259,7 @@ N = 0x7FFFFFFFFFFFFFFFFFFFFFFF800000CFA7E8594377D414C03821BC582063 Name = x962_p239v3 +Impl = generic legacy OID = 1.2.840.10045.3.1.6 P = 0x7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF A = -3 @@ -240,3 +267,13 @@ X = 0x6768AE8E18BB92CFCF005C949AA2C6D94853D0E660BBF854B1C9505FE95A Y = 0x1607E6898F390C06BC1D552BAD226F3B6FCFE48B6E818499AF18E3ED6CF3 N = 0x7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF975DEB41B3A6057C3C432146526551 + +Name = numsp512d1 +Impl = pcurve generic legacy +OID = 1.3.6.1.4.1.25258.4.3 +P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7 +A = -3 +B = 0x01D99B +X = 0x02 +Y = 0x1C282EB23327F9711952C250EA61AD53FCC13031CF6DD336E0B9328433AFBDD8CC5A1C1F0C716FDC724DDE537C2B0ADB00BB3D08DC83755B205CC30D7F83CF28 +N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F390433555D diff -Nru botan3-3.7.1+dfsg/src/build-data/ec_named.cpp.in botan3-3.12.0+dfsg/src/build-data/ec_named.cpp.in --- botan3-3.7.1+dfsg/src/build-data/ec_named.cpp.in 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/ec_named.cpp.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -/* -* ECC Group Info -* This file was automatically generated by %s on %s -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -namespace Botan { - -// clang-format off - -//static -std::shared_ptr EC_Group::EC_group_info(const OID& oid) { -%s - return std::shared_ptr(); -} - -//static -OID EC_Group::EC_group_identity_from_order(const BigInt& order) - { - const uint32_t low_bits = static_cast(order.word_at(0)); - -%s - return OID(); -} - -//static -const std::set& EC_Group::known_named_groups() { - static const std::set named_groups = { -%s - }; - return named_groups; -} - -} // namespace Botan - -// clang-format on diff -Nru botan3-3.7.1+dfsg/src/build-data/makefile.in botan3-3.12.0+dfsg/src/build-data/makefile.in --- botan3-3.7.1+dfsg/src/build-data/makefile.in 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/makefile.in 2026-05-07 01:38:28.000000000 +0000 @@ -151,22 +151,22 @@ %{for lib_build_info} %{obj}: %{src} - $(CXX) $(LIB_FLAGS) $(BUILD_FLAGS) -DBOTAN_IS_BEING_BUILT %{isa_flags} %{public_include_flags} %{internal_include_flags} %{external_include_flags} %{dash_c} %{src} %{dash_o}$@ + $(CXX) $(LIB_FLAGS) $(BUILD_FLAGS) -DBOTAN_IS_BEING_BUILT %{public_include_flags} %{internal_include_flags} %{external_include_flags} %{dash_c} %{src} %{dash_o}$@ %{endfor} %{for cli_build_info} %{obj}: %{src} - $(CXX) $(BUILD_FLAGS) -DBOTAN_IS_BEING_BUILT %{isa_flags} %{public_include_flags} %{internal_include_flags} %{external_include_flags} %{dash_c} %{src} %{dash_o}$@ + $(CXX) $(BUILD_FLAGS) -DBOTAN_IS_BEING_BUILT %{public_include_flags} %{internal_include_flags} %{external_include_flags} %{dash_c} %{src} %{dash_o}$@ %{endfor} %{for test_build_info} %{obj}: %{src} - $(CXX) $(BUILD_FLAGS) -DBOTAN_IS_BEING_BUILT %{isa_flags} %{public_include_flags} %{internal_include_flags} %{external_include_flags} %{dash_c} %{src} %{dash_o}$@ + $(CXX) $(BUILD_FLAGS) -DBOTAN_IS_BEING_BUILT %{public_include_flags} %{internal_include_flags} %{external_include_flags} %{dash_c} %{src} %{dash_o}$@ %{endfor} %{for fuzzer_build_info} %{obj}: %{src} - $(CXX) $(BUILD_FLAGS) -DBOTAN_IS_BEING_BUILT %{isa_flags} %{public_include_flags} %{internal_include_flags} %{external_include_flags} %{dash_c} %{src} %{dash_o}$@ + $(CXX) $(BUILD_FLAGS) -DBOTAN_IS_BEING_BUILT %{public_include_flags} %{internal_include_flags} %{external_include_flags} %{dash_c} %{src} %{dash_o}$@ %{exe}: %{obj} $(LIBRARIES) $(EXE_LINK_CMD) $(ABI_FLAGS) %{obj} $(BUILD_DIR_LINK_PATH) $(LANG_EXE_FLAGS) $(LDFLAGS) $(EXE_LINKS_TO) %{fuzzer_lib} %{output_to_exe}$@ @@ -174,7 +174,7 @@ %{for examples_build_info} %{obj}: %{src} - $(CXX) $(BUILD_FLAGS) %{isa_flags} %{public_include_flags} %{external_include_flags} %{dash_c} %{src} %{dash_o}$@ + $(CXX) $(BUILD_FLAGS) %{public_include_flags} %{external_include_flags} %{dash_c} %{src} %{dash_o}$@ %{exe}: %{obj} $(LIBRARIES) $(EXE_LINK_CMD) $(ABI_FLAGS) %{obj} $(BUILD_DIR_LINK_PATH) $(LANG_EXE_FLAGS) $(LDFLAGS) $(EXE_LINKS_TO) %{fuzzer_lib} %{output_to_exe}$@ diff -Nru botan3-3.7.1+dfsg/src/build-data/ninja.in botan3-3.12.0+dfsg/src/build-data/ninja.in --- botan3-3.7.1+dfsg/src/build-data/ninja.in 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/ninja.in 2026-05-07 01:38:28.000000000 +0000 @@ -28,7 +28,7 @@ %{if ninja_header_deps_style} deps = %{ninja_header_deps_style} %{endif} - command = %{cxx} %{lib_flags} ${ABI_FLAGS} ${LANG_FLAGS} ${CXXFLAGS} -DBOTAN_IS_BEING_BUILT ${WARN_FLAGS} ${isa_flags} %{public_include_flags} %{internal_include_flags} %{external_include_flags} %{header_deps_flag} %{header_deps_out|concat: $out.d} %{dash_c} $in %{dash_o}$out + command = %{cxx} %{lib_flags} ${ABI_FLAGS} ${LANG_FLAGS} ${CXXFLAGS} -DBOTAN_IS_BEING_BUILT ${WARN_FLAGS} %{public_include_flags} %{internal_include_flags} %{external_include_flags} %{header_deps_flag} %{header_deps_out|concat: $out.d} %{dash_c} $in %{dash_o}$out rule compile_exe %{if header_deps_out} @@ -37,7 +37,7 @@ %{if ninja_header_deps_style} deps = %{ninja_header_deps_style} %{endif} - command = %{cxx} ${ABI_FLAGS} ${LANG_FLAGS} ${CXXFLAGS} -DBOTAN_IS_BEING_BUILT ${WARN_FLAGS} ${isa_flags} %{public_include_flags} %{internal_include_flags} %{external_include_flags} %{header_deps_flag} %{header_deps_out|concat: $out.d} %{dash_c} $in %{dash_o}$out + command = %{cxx} ${ABI_FLAGS} ${LANG_FLAGS} ${CXXFLAGS} -DBOTAN_IS_BEING_BUILT ${WARN_FLAGS} %{public_include_flags} %{internal_include_flags} %{external_include_flags} %{header_deps_flag} %{header_deps_out|concat: $out.d} %{dash_c} $in %{dash_o}$out rule compile_example_exe %{if header_deps_out} @@ -46,7 +46,7 @@ %{if ninja_header_deps_style} deps = %{ninja_header_deps_style} %{endif} - command = %{cxx} ${ABI_FLAGS} ${LANG_FLAGS} ${CXXFLAGS} ${WARN_FLAGS} ${isa_flags} %{public_include_flags} %{external_include_flags} %{header_deps_flag} %{header_deps_out|concat: $out.d} %{dash_c} $in %{dash_o}$out + command = %{cxx} ${ABI_FLAGS} ${LANG_FLAGS} ${CXXFLAGS} ${WARN_FLAGS} %{public_include_flags} %{external_include_flags} %{header_deps_flag} %{header_deps_out|concat: $out.d} %{dash_c} $in %{dash_o}$out # The primary target build all: phony %{all_targets} @@ -54,16 +54,24 @@ # Library targets -%{if build_static_lib} +%{if build_static_lib_using_response_file} rule link_static rspfile = %{response_file_dir}/static.txt rspfile_content = $in command = %{ar_command} %{ar_options} %{ar_output_to}$out @%{response_file_dir}/static.txt -build %{out_dir}/%{static_lib_name}: link_static %{join lib_objs} +%{endif} +%{if build_static_lib_using_cmdline_args} + +rule link_static + command = %{ar_command} %{ar_options} %{ar_output_to}$out $in %{endif} +%{if build_static_lib} +build %{out_dir}/%{static_lib_name}: link_static %{join lib_objs} +%{endif} + %{if build_shared_lib} rule link_shared @@ -204,7 +212,6 @@ %{for lib_build_info} build %{obj}: compile_lib %{src} - isa_flags = %{isa_flags} %{endfor} %{for cli_build_info} diff -Nru botan3-3.7.1+dfsg/src/build-data/oids.txt botan3-3.12.0+dfsg/src/build-data/oids.txt --- botan3-3.7.1+dfsg/src/build-data/oids.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/oids.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,22 +1,26 @@ -# Regenerate with ./src/scripts/dev_tools/gen_oids.py oids > src/lib/asn1/oid_maps.cpp -# AND ./src/scripts/dev_tools/gen_oids.py dn_ub > src/lib/x509/x509_dn_ub.cpp -# (if you modified something under [dn]) +# Static OID data +# +# If you modify this data you must run ./src/scripts/dev_tools/gen_oids.py +# to regenerate the relevant source files # Public key types [pubkey] 1.2.840.113549.1.1.1 = RSA -2.5.8.1.1 = RSA 1.2.840.10040.4.1 = DSA 1.2.840.10046.2.1 = DH 1.3.6.1.4.1.3029.1.2.1 = ElGamal 1.3.6.1.4.1.25258.1.3 = McEliece -# Deprecated alias for X25519 -1.3.101.110 = Curve25519 1.3.101.110 = X25519 1.3.101.111 = X448 1.3.101.112 = Ed25519 1.3.101.113 = Ed448 +# Deprecated alternate RSA OID +2.5.8.1.1 = RSA + +# Deprecated alias for X25519 TODO(Botan4) remove this +1.3.101.110 = Curve25519 + # From NIST: 2.16.840.1.101.3.4.4.1 = ML-KEM-512 2.16.840.1.101.3.4.4.2 = ML-KEM-768 @@ -128,8 +132,8 @@ 1.2.840.10045.2.1 = ECDSA 1.3.132.1.12 = ECDH -1.2.156.10197.1.301.1 = SM2_Sig 1.2.156.10197.1.301.1 = SM2 +1.2.156.10197.1.301.1 = SM2_Sig 1.2.156.10197.1.301.2 = SM2_Kex 1.2.156.10197.1.301.3 = SM2_Enc @@ -260,6 +264,22 @@ 2.16.840.1.101.3.4.3.7 = DSA/SHA-3(384) 2.16.840.1.101.3.4.3.8 = DSA/SHA-3(512) +1.2.840.113549.1.1.2 = RSA/PKCS1v15(MD2) +1.2.840.113549.1.1.4 = RSA/PKCS1v15(MD5) +1.2.840.113549.1.1.5 = RSA/PKCS1v15(SHA-1) +1.2.840.113549.1.1.11 = RSA/PKCS1v15(SHA-256) +1.2.840.113549.1.1.12 = RSA/PKCS1v15(SHA-384) +1.2.840.113549.1.1.13 = RSA/PKCS1v15(SHA-512) +1.2.840.113549.1.1.14 = RSA/PKCS1v15(SHA-224) +1.2.840.113549.1.1.16 = RSA/PKCS1v15(SHA-512-256) +2.16.840.1.101.3.4.3.13 = RSA/PKCS1v15(SHA-3(224)) +2.16.840.1.101.3.4.3.14 = RSA/PKCS1v15(SHA-3(256)) +2.16.840.1.101.3.4.3.15 = RSA/PKCS1v15(SHA-3(384)) +2.16.840.1.101.3.4.3.16 = RSA/PKCS1v15(SHA-3(512)) +1.2.156.10197.1.504 = RSA/PKCS1v15(SM3) +1.3.36.3.3.1.2 = RSA/PKCS1v15(RIPEMD-160) +1.2.840.113549.1.1.10 = RSA/PSS + # TODO(Botan4) remove these EMSA3/EMSA4 aliases 1.2.840.113549.1.1.2 = RSA/EMSA3(MD2) 1.2.840.113549.1.1.4 = RSA/EMSA3(MD5) @@ -277,22 +297,6 @@ 1.3.36.3.3.1.2 = RSA/EMSA3(RIPEMD-160) 1.2.840.113549.1.1.10 = RSA/EMSA4 -1.2.840.113549.1.1.2 = RSA/PKCS1v15(MD2) -1.2.840.113549.1.1.4 = RSA/PKCS1v15(MD5) -1.2.840.113549.1.1.5 = RSA/PKCS1v15(SHA-1) -1.2.840.113549.1.1.11 = RSA/PKCS1v15(SHA-256) -1.2.840.113549.1.1.12 = RSA/PKCS1v15(SHA-384) -1.2.840.113549.1.1.13 = RSA/PKCS1v15(SHA-512) -1.2.840.113549.1.1.14 = RSA/PKCS1v15(SHA-224) -1.2.840.113549.1.1.16 = RSA/PKCS1v15(SHA-512-256) -2.16.840.1.101.3.4.3.13 = RSA/PKCS1v15(SHA-3(224)) -2.16.840.1.101.3.4.3.14 = RSA/PKCS1v15(SHA-3(256)) -2.16.840.1.101.3.4.3.15 = RSA/PKCS1v15(SHA-3(384)) -2.16.840.1.101.3.4.3.16 = RSA/PKCS1v15(SHA-3(512)) -1.2.156.10197.1.504 = RSA/PKCS1v15(SM3) -1.3.36.3.3.1.2 = RSA/PKCS1v15(RIPEMD-160) -1.2.840.113549.1.1.10 = RSA/PSS - 1.2.840.10045.4.1 = ECDSA/SHA-1 1.2.840.10045.4.3.1 = ECDSA/SHA-224 1.2.840.10045.4.3.2 = ECDSA/SHA-256 @@ -326,32 +330,47 @@ 1.2.840.113549.1.1.7 = RSA/OAEP 1.2.840.113549.1.1.8 = MGF1 -# DN with upper bounds from RFC 5280, Appendix A [dn] -2.5.4.3 = X520.CommonName = 64 -2.5.4.4 = X520.Surname = 40 -2.5.4.5 = X520.SerialNumber = 64 -2.5.4.6 = X520.Country = 3 -2.5.4.7 = X520.Locality = 128 -2.5.4.8 = X520.State = 128 -2.5.4.9 = X520.StreetAddress = 128 -2.5.4.10 = X520.Organization = 64 -2.5.4.11 = X520.OrganizationalUnit = 64 -2.5.4.12 = X520.Title = 64 -# the following three types are naming attributes of type "X520name" and inherit its bound -2.5.4.42 = X520.GivenName = 32768 -2.5.4.43 = X520.Initials = 32768 -2.5.4.44 = X520.GenerationalQualifier = 32768 -2.5.4.46 = X520.DNQualifier = 64 -2.5.4.65 = X520.Pseudonym = 128 +2.5.4.3 = X520.CommonName +2.5.4.4 = X520.Surname +2.5.4.5 = X520.SerialNumber +2.5.4.6 = X520.Country +2.5.4.7 = X520.Locality +2.5.4.8 = X520.State +2.5.4.9 = X520.StreetAddress +2.5.4.10 = X520.Organization +2.5.4.11 = X520.OrganizationalUnit +2.5.4.12 = X520.Title +2.5.4.42 = X520.GivenName +2.5.4.43 = X520.Initials +2.5.4.44 = X520.GenerationalQualifier +2.5.4.46 = X520.DNQualifier +2.5.4.65 = X520.Pseudonym [pbe] 1.2.840.113549.1.5.12 = PKCS5.PBKDF2 -1.2.840.113549.1.5.13 = PBES2 1.2.840.113549.1.5.13 = PBE-PKCS5v20 +1.2.840.113549.1.5.13 = PBES2 1.3.6.1.4.1.11591.4.11 = Scrypt +# PKCS#12 PBE algorithms (password-based encryption) +1.2.840.113549.1.12.1.3 = PBE-SHA1-3DES +1.2.840.113549.1.12.1.4 = PBE-SHA1-2DES + +[pkcs12] +# PKCS#12 bag types +1.2.840.113549.1.12.10.1.1 = PKCS12.KeyBag +1.2.840.113549.1.12.10.1.2 = PKCS12.PKCS8ShroudedKeyBag +1.2.840.113549.1.12.10.1.3 = PKCS12.CertBag +1.2.840.113549.1.12.10.1.4 = PKCS12.CRLBag +1.2.840.113549.1.12.10.1.5 = PKCS12.SecretBag +1.2.840.113549.1.12.10.1.6 = PKCS12.SafeContentsBag + +# PKCS#7 content types (used by PKCS#12) +1.2.840.113549.1.7.1 = PKCS7.Data +1.2.840.113549.1.7.6 = PKCS7.EncryptedData + [pkcs9] 1.2.840.113549.1.9.1 = PKCS9.EmailAddress 1.2.840.113549.1.9.2 = PKCS9.UnstructuredName @@ -360,6 +379,15 @@ 1.2.840.113549.1.9.7 = PKCS9.ChallengePassword 1.2.840.113549.1.9.14 = PKCS9.ExtensionRequest +# PKCS#12 cert/CRL bag types +1.2.840.113549.1.9.22.1 = PKCS9.X509Certificate +1.2.840.113549.1.9.22.2 = PKCS9.SDSICertificate +1.2.840.113549.1.9.23.1 = PKCS9.X509CRL + +# PKCS#12 attributes +1.2.840.113549.1.9.20 = PKCS9.FriendlyName +1.2.840.113549.1.9.21 = PKCS9.LocalKeyId + [pkix] 2.5.29.14 = X509v3.SubjectKeyIdentifier 2.5.29.15 = X509v3.KeyUsage @@ -378,7 +406,11 @@ 2.5.29.35 = X509v3.AuthorityKeyIdentifier 2.5.29.36 = X509v3.PolicyConstraints 2.5.29.37 = X509v3.ExtendedKeyUsage +2.5.29.37.0 = X509v3.AnyExtendedKeyUsage 1.3.6.1.5.5.7.1.1 = PKIX.AuthorityInformationAccess +# the following are taken from RFC 3779 https://www.rfc-editor.org/rfc/rfc3779.html +1.3.6.1.5.5.7.1.7 = PKIX.IpAddrBlocks +1.3.6.1.5.5.7.1.8 = PKIX.AutonomousSysIds 1.3.6.1.5.5.7.1.26 = PKIX.TNAuthList 2.5.29.32.0 = X509v3.AnyPolicy @@ -422,6 +454,11 @@ 1.3.132.0.33 = secp224r1 1.3.132.0.34 = secp384r1 1.3.132.0.35 = secp521r1 + +# TODO(Botan4) remove this OID assignment +# +# This was assigned by TU-Darmstadt to "primeCurve 38" which may or may not be +# secp521r1. In any case this OID is not used by any other implementation. 1.3.6.1.4.1.8301.3.1.2.9.0.38 = secp521r1 1.2.840.10045.3.1.1 = secp192r1 diff -Nru botan3-3.7.1+dfsg/src/build-data/os/hurd.txt botan3-3.12.0+dfsg/src/build-data/os/hurd.txt --- botan3-3.7.1+dfsg/src/build-data/os/hurd.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/os/hurd.txt 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,10 @@ dev_random clock_gettime +explicit_bzero +getrandom +getentropy + atomics sockets system_clock diff -Nru botan3-3.7.1+dfsg/src/build-data/os/netbsd.txt botan3-3.12.0+dfsg/src/build-data/os/netbsd.txt --- botan3-3.7.1+dfsg/src/build-data/os/netbsd.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/os/netbsd.txt 2026-05-07 01:38:28.000000000 +0000 @@ -15,7 +15,6 @@ threads thread_local filesystem -auxinfo diff -Nru botan3-3.7.1+dfsg/src/build-data/os/openbsd.txt botan3-3.12.0+dfsg/src/build-data/os/openbsd.txt --- botan3-3.7.1+dfsg/src/build-data/os/openbsd.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/os/openbsd.txt 2026-05-07 01:38:28.000000000 +0000 @@ -16,7 +16,6 @@ elf_aux_info getentropy explicit_bzero -pledge alloc_conceal atomics diff -Nru botan3-3.7.1+dfsg/src/build-data/policy/bsi.txt botan3-3.12.0+dfsg/src/build-data/policy/bsi.txt --- botan3-3.7.1+dfsg/src/build-data/policy/bsi.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/policy/bsi.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,9 +1,9 @@ -# For reference see BSI TR-02102-1 (2024-01): -# https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/TechGuidelines/TG02102/BSI-TR-02102-1.pdf?__blob=publicationFile&v=7 +# For reference see BSI TR-02102-1 (2025-01): +# https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/TechGuidelines/TG02102/BSI-TR-02102-1.pdf?__blob=publicationFile&v=9 # === 2. Asymmetric Encryption Schemes and Key Agreement === -# Table 2.1: Recommended classical asymmetric encryption and key derivation schemes +# Table 2.2: Recommended classical asymmetric encryption and key derivation schemes rsa # dlies (deprecated) ecies @@ -13,15 +13,18 @@ # Allowed KDF for ECIES (see 2.3.4) kdf1_iso18033 -# Table 2.3: Recommended formatting method for the RSA encryption algorithm +# Table 2.4: Recommended formatting method for the RSA encryption algorithm eme_oaep -# Table 2.4: Recommended parameters for FrodoKEM +# Table 2.5: Recommended parameters for FrodoKEM frodokem -# Table 2.5: Recommended parameters for ClassicMcEliece-KEM. +# Table 2.6: Recommended parameters for ClassicMcEliece-KEM. classic_mceliece +# Table 2.7: Recommended parameters for ML-KEM +ml_kem + # === 3. Symmetric Encryption Schemes === # Table 3.1: Recommended block ciphers aes @@ -35,6 +38,9 @@ # Table 3.3: Recommended padding schemes for block ciphers mode_pad # contains various paddings +# Section 3.3: Protection of Key Material +nist_keywrap + # === 4. Hash Functions === # Table 4.1: Recommended hash functions sha2_32 @@ -54,6 +60,9 @@ ecdsa ecgdsa eckcdsa +ml_dsa +slh_dsa_shake +slh_dsa_sha2 xmss hss_lms @@ -70,26 +79,13 @@ sp800_56c # (Two-Step KDF) hkdf -# B.1.3. Password-Based Key Derivation +# B.1.2. Password-Based Key Derivation argon2 argon2fmt + -# Addition: ML-KEM, ML-DSA, and SLH-DSA -# We expect the BSI to approve the new FIPS (203,204,205) PQC algorithms -# in the upcoming TR (see Section 2.4.3 and Remark 5.5). We believe it is in -# the BSI's interest to allow them here so applications can migrate to -# post-quantum security as soon as possible. -ml_kem -ml_dsa -slh_dsa_shake -slh_dsa_sha2 - - -# Optimization: PCurves -# To benefit from the speedup of pcurves, we activate all pcurve modules. We -# activate all curves regardless of the recommendation in the TR since they -# would be accessible anyway in a worse generic implementation. - + +# pcurves pcurves_brainpool256r1 pcurves_brainpool384r1 pcurves_brainpool512r1 @@ -105,14 +101,14 @@ pcurves_numsp512d1 pcurves_sm2p256v1 - +pcurves_generic - # block aes_ni aes_vperm aes_armv8 aes_power8 +aes_vaes # modes ghash_cpu @@ -121,8 +117,12 @@ # hash sha2_32_x86 sha2_32_armv8 -sha2_32_bmi2 -sha2_64_bmi2 +sha2_32_simd +sha2_32_avx2 +sha2_64_x86 +sha2_64_armv8 +sha2_64_avx2 +sha2_64_avx512 keccak_perm_bmi2 # entropy sources @@ -132,7 +132,7 @@ # pbkdf argon2_avx2 -argon2_ssse3 +argon2_simd64 # rng processor_rng @@ -141,7 +141,6 @@ # utils http_util # needed by x509 for OCSP online checks locking_allocator -simd diff -Nru botan3-3.7.1+dfsg/src/build-data/policy/fips140.txt botan3-3.12.0+dfsg/src/build-data/policy/fips140.txt --- botan3-3.7.1+dfsg/src/build-data/policy/fips140.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/policy/fips140.txt 2026-05-07 01:38:28.000000000 +0000 @@ -60,8 +60,9 @@ # hash sha2_32_x86 sha2_32_armv8 -sha2_32_bmi2 -sha2_64_bmi2 +sha2_32_simd +sha2_32_avx2 +sha2_64_avx2 keccak_perm_bmi2 # modes @@ -82,7 +83,6 @@ # utils http_util # needed by x509 for OCSP online checks locking_allocator -simd @@ -115,6 +115,7 @@ # mac blake2mac +poly1305 # modes chacha20poly1305 @@ -133,7 +134,6 @@ salsa20 # kdf -hkdf kdf1 kdf2 prf_x942 @@ -194,7 +194,6 @@ # misc bcrypt srp6 -sodium # tls tls_cbc diff -Nru botan3-3.7.1+dfsg/src/build-data/policy/modern.txt botan3-3.12.0+dfsg/src/build-data/policy/modern.txt --- botan3-3.7.1+dfsg/src/build-data/policy/modern.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/policy/modern.txt 2026-05-07 01:38:28.000000000 +0000 @@ -8,9 +8,11 @@ sha2_64 blake2 skein -keccak +shake sha3 +shake_xof + gcm ocb chacha20poly1305 @@ -20,7 +22,6 @@ cmac hmac poly1305 -siphash pbkdf2 bcrypt @@ -50,6 +51,13 @@ tls prf_tls +# pcurves +pcurves_secp256r1 +pcurves_secp384r1 +pcurves_secp521r1 + +pcurves_generic + ghash_cpu ghash_vperm @@ -65,17 +73,16 @@ chacha_simd32 chacha_avx2 -sha1_sse2 +sha1_simd sha1_x86 sha1_armv8 sha2_32_x86 +sha2_32_simd sha2_32_armv8 -sha2_32_bmi2 -sha2_64_bmi2 +sha2_32_avx2 +sha2_64_avx2 keccak_perm_bmi2 -simd - sessions_sql certstor_sql diff -Nru botan3-3.7.1+dfsg/src/build-data/target_info.h.in botan3-3.12.0+dfsg/src/build-data/target_info.h.in --- botan3-3.7.1+dfsg/src/build-data/target_info.h.in 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/target_info.h.in 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,110 @@ +#ifndef BOTAN_TARGET_INFO_H_ +#define BOTAN_TARGET_INFO_H_ + +#include + +/** +* @file target_info.h +* +* Automatically generated from +* '%{command_line}' +* +* Target +* - Compiler: %{cxx} %{cxx_abi_flags} %{cc_lang_flags} %{cc_compile_flags} +* - Arch: %{arch} +* - OS: %{os} +*/ + +/* NOLINTBEGIN(*-macro-usage,*-macro-to-enum) */ + +/* +* Configuration +*/ +%{if cxx_ct_value_barrier_type} +#define BOTAN_CT_VALUE_BARRIER_USE_%{cxx_ct_value_barrier_type|upper} +%{endif} + +[[maybe_unused]] static constexpr bool OptimizeForSize = %{optimize_for_size|as_bool}; + +%{if terminate_on_asserts} +#define BOTAN_TERMINATE_ON_ASSERTS +%{endif} + +%{if fuzzer_mode} +/** Disables certain validation checks to ease fuzzability of the library + * @warning This causes the library build to be insecure, hence, it must not be + * used in a production environment! + */ +#define BOTAN_UNSAFE_FUZZER_MODE +%{endif} + +/* +* Compiler Information +*/ +#define BOTAN_BUILD_COMPILER_IS_%{cc_macro} + +#define BOTAN_COMPILER_INVOCATION_STRING "%{cxx} %{cxx_abi_flags} %{cc_compile_flags}" + +%{if cxx_supports_gcc_inline_asm} +#define BOTAN_USE_GCC_INLINE_ASM +%{endif} + +%{if compiler_assisted_stack_scrubbing} +#define BOTAN_USE_COMPILER_ASSISTED_STACK_SCRUBBING +%{endif} + +/* +* External tool settings +*/ +%{if with_valgrind} +#define BOTAN_HAS_VALGRIND +%{endif} + +%{if fuzzer_type} +#define BOTAN_FUZZER_IS_%{fuzzer_type} +%{endif} + +%{for sanitizer_types} +#define BOTAN_HAS_SANITIZER_%{i|upper} +%{endfor} + +/* +* CPU feature information +*/ +#define BOTAN_TARGET_ARCH "%{arch}" + +#define BOTAN_TARGET_ARCH_IS_%{arch|upper} + +%{if cpu_family} +#define BOTAN_TARGET_ARCH_IS_%{cpu_family|upper}_FAMILY +%{endif} + +%{for cpu_features} +#define BOTAN_TARGET_ARCH_SUPPORTS_%{i|upper} +%{endfor} + +/* +* Operating system information +*/ +#define BOTAN_TARGET_OS_IS_%{os_name|upper} + +%{for os_features} +#define BOTAN_TARGET_OS_HAS_%{i|upper} +%{endfor} + +/* +* System paths +*/ +#define BOTAN_INSTALL_PREFIX R"(%{prefix})" +#define BOTAN_INSTALL_HEADER_DIR R"(%{namespaced_includedir_rel})" +#define BOTAN_INSTALL_LIB_DIR R"(%{libdir})" +#define BOTAN_LIB_LINK "%{link_to}" +#define BOTAN_LINK_FLAGS "%{cxx_abi_flags}" + +%{if system_cert_bundle} +#define BOTAN_SYSTEM_CERT_BUNDLE "%{system_cert_bundle}" +%{endif} + +/* NOLINTEND(*-macro-usage,*-macro-to-enum) */ + +#endif diff -Nru botan3-3.7.1+dfsg/src/build-data/templates/ec_named.cpp.in botan3-3.12.0+dfsg/src/build-data/templates/ec_named.cpp.in --- botan3-3.7.1+dfsg/src/build-data/templates/ec_named.cpp.in 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/templates/ec_named.cpp.in 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,56 @@ +/* +* ECC Group Info +* This file was automatically generated by {{ script }} on {{ date }} +* All manual changes will be lost. Edit the script instead. +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +namespace Botan { + +// clang-format off + +//static +std::shared_ptr EC_Group::EC_group_info(const OID& oid) { +{%- for curve in curves %} + // {{ curve["Name"] }} + if({% for oid in curve["OIDExpr"] %}oid == {{ oid }}{% if not loop.last %} || {% endif %}{% endfor %}) { + return load_EC_group_info( + "0x{{ "%X" % curve["P"] }}", + "0x{{ "%X" % curve["A"] }}", + "0x{{ "%X" % curve["B"] }}", + "0x{{ "%X" % curve["X"] }}", + "0x{{ "%X" % curve["Y"] }}", + "0x{{ "%X" % curve["N"] }}", + {% if curve["OIDExpr"] | length == 1 %}oid{% else %}{{ curve["OIDExpr"][0] }}{% endif %}); + } +{% endfor %} + return std::shared_ptr(); +} + +//static +OID EC_Group::EC_group_identity_from_order(const BigInt& order) + { + const uint32_t low_bits = static_cast(order.word_at(0)); +{% for curve in curves %} + if(low_bits == 0x{{ "%08X" % curve["N32"]}} && order == BigInt("0x{{ "%X" % curve["N"] }}")) { + return {{ curve["OIDExpr"][0] }}; + } +{% endfor %} + return OID(); +} + +//static +const std::set& EC_Group::known_named_groups() { + static const std::set named_groups = { +{{named_groups}} + }; + + return named_groups; +} + +} // namespace Botan + +// clang-format on diff -Nru botan3-3.7.1+dfsg/src/build-data/templates/pcurves.cpp.in botan3-3.12.0+dfsg/src/build-data/templates/pcurves.cpp.in --- botan3-3.7.1+dfsg/src/build-data/templates/pcurves.cpp.in 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/templates/pcurves.cpp.in 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,44 @@ +/* +* This file was automatically generated by {{ script }} on {{ date }} +* All manual changes will be lost. Edit the script instead. +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include + +namespace Botan::PCurve { + +void PrimeOrderCurve::Scalar::_zeroize() { + secure_zeroize_buffer(m_value.data(), m_value.size() * sizeof(word)); +} + +//static +std::shared_ptr PrimeOrderCurve::from_params( + const BigInt& p, const BigInt& a, const BigInt& b, const BigInt& base_x, const BigInt& base_y, const BigInt& order) { +#if defined(BOTAN_HAS_PCURVES_GENERIC) + return PCurveInstance::from_params(p, a, b, base_x, base_y, order); +#endif + + BOTAN_UNUSED(p, a, b, base_x, base_y, order); + return {}; +} + +//static +std::shared_ptr PrimeOrderCurve::for_named_curve(std::string_view name) { +{%- for curve in pcurves %} +#if defined(BOTAN_HAS_PCURVES_{{curve["Name"] | upper}}) + if(name == "{{curve["Name"]}}") { + return PCurveInstance::{{curve["Name"]}}(); + } +#endif +{% endfor %} + BOTAN_UNUSED(name); + return {}; +} + +} // namespace Botan::PCurve diff -Nru botan3-3.7.1+dfsg/src/build-data/templates/pcurves_instance.h.in botan3-3.12.0+dfsg/src/build-data/templates/pcurves_instance.h.in --- botan3-3.7.1+dfsg/src/build-data/templates/pcurves_instance.h.in 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/templates/pcurves_instance.h.in 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,42 @@ +/* +* This file was automatically generated by {{ script }} on {{ date }} +* All manual changes will be lost. Edit the script instead. +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_PCURVES_INSTANCE_H_ +#define BOTAN_PCURVES_INSTANCE_H_ + +#include +#include + +namespace Botan { + +class BigInt; + +} + +namespace Botan::PCurve { + +class PrimeOrderCurve; + +class PCurveInstance final { + public:{% for curve in pcurves %} +#if defined(BOTAN_HAS_PCURVES_{{ curve["Name"] | upper }}) + static std::shared_ptr {{ curve["Name"] }}(); +#endif +{% endfor %} +#if defined(BOTAN_HAS_PCURVES_GENERIC) + static std::shared_ptr from_params(const BigInt& p, + const BigInt& a, + const BigInt& b, + const BigInt& base_x, + const BigInt& base_y, + const BigInt& order); +#endif +}; + +} // namespace Botan::PCurve + +#endif diff -Nru botan3-3.7.1+dfsg/src/build-data/templates/pcurves_stub.cpp.in botan3-3.12.0+dfsg/src/build-data/templates/pcurves_stub.cpp.in --- botan3-3.7.1+dfsg/src/build-data/templates/pcurves_stub.cpp.in 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/templates/pcurves_stub.cpp.in 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,80 @@ +/* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include + +namespace Botan::PCurve { + +namespace { + +namespace {{ curve["Name"] }} { + +{% if crandall > 0 -%} +template +class {{ curve["Name"] | capitalize }}Rep final { + public: + static constexpr auto P = Params::P; + static constexpr size_t N = Params::N; + typedef typename Params::W W; + + static constexpr W C = {{ crandall }}; + + constexpr static std::array one() { return std::array{1}; } + + constexpr static std::array redc(const std::array& z) { + return redc_crandall(std::span{z}); + } + + constexpr static std::array to_rep(const std::array& x) { return x; } + + constexpr static std::array wide_to_rep(const std::array& x) { return redc(x); } + + constexpr static std::array from_rep(const std::array& z) { return z; } +}; +{% endif %} +// clang-format off +class Params final : public EllipticCurveParameters< + "{{ "%X" % curve['P'] }}", + "{{ "%X" % curve['A'] }}", + "{{ "%X" % curve['B'] }}", + "{{ "%X" % curve['N'] }}", + "{{ "%X" % curve['X'] }}", + "{{ "%X" % curve['Y'] }}"> { +}; +// clang-format on + +class Curve final : public EllipticCurve 0 %}, {{ curve["Name"] | capitalize}}Rep{% endif %}> { + public: + // Return the square of the inverse of x + static constexpr FieldElement fe_invert2(const FieldElement& x) { + // Generated using https://github.com/mmcloughlin/addchain + {{ addchain_fe2 }} + } + + {% if addchain_fe_sqrt != None -%} + // Return the square root of this field element (if it is a quadratic residue) + static constexpr FieldElement fe_sqrt(const FieldElement& x) { + // Generated using https://github.com/mmcloughlin/addchain + {{ addchain_fe_sqrt }} + } + {%- endif %} + + // Return the inverse of an integer modulo the order + static constexpr Scalar scalar_invert(const Scalar& x) { + // Generated using https://github.com/mmcloughlin/addchain + {{ addchain_scalar }} + } +}; + +} // namespace {{ curve["Name"] }} + +} // namespace + +std::shared_ptr PCurveInstance::{{ curve["Name"] }}() { + return PrimeOrderCurveImpl<{{ curve["Name"] }}::Curve>::instance(); +} + +} // namespace Botan::PCurve diff -Nru botan3-3.7.1+dfsg/src/build-data/templates/static_oids.cpp.in botan3-3.12.0+dfsg/src/build-data/templates/static_oids.cpp.in --- botan3-3.7.1+dfsg/src/build-data/templates/static_oids.cpp.in 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/templates/static_oids.cpp.in 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,91 @@ +/* +* This file was automatically generated by {{ script }} on {{ date }} +* All manual changes will be lost. Edit the script instead. +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan { + +namespace { + +// The hash can collide so we must verify the actual value matches before returning +std::optional if_match(const OID& oid, std::initializer_list val, std::string_view name) { + if(oid.matches(val)) { + return name; + } else { + return {}; + } +} + +std::optional if_match(std::string_view req, std::string_view actual, std::initializer_list oid) { + if(req == actual) { + return OID(oid); + } else { + return {}; + } +} + +uint32_t hash_oid_name(std::string_view s) { + uint64_t hash = 0x8188B31879A4879A; + + for(const char c : s) { + hash *= 251; + hash += c; + } + + return static_cast(hash % 805289); +} + +} // namespace + +//static +std::optional OID_Map::lookup_static_oid(const OID& oid) { + const uint32_t hc = static_cast(oid.hash_code() % 858701); + + switch(hc) { +{%- for match in static_oid_data|sort(attribute="oid_hash") %} + case 0x{{ "%05X" % (match.oid_hash) }}: + return if_match(oid, {{ match.oid }}, "{{match.name}}"); +{%- endfor %} + default: + return {}; + } +} + +//static +std::optional OID_Map::lookup_static_oid_name(std::string_view req) { + const uint32_t hc = hash_oid_name(req); + + switch(hc) { +{%- for match in static_oid_data|sort(attribute="name_hash") %} + case 0x{{ "%05X" % (match.name_hash) }}: + return if_match(req, "{{match.name}}", {{ match.oid }}); +{%- endfor %} + default: + return {}; + } +} + +std::unordered_map OID_Map::load_oid2str_map() { + return { +{%- for oid in dup_oids %} + {OID{{ oid.oid }}, "{{ oid.name }}"}, +{%- endfor %} + }; +} + +std::unordered_map OID_Map::load_str2oid_map() { + return { +{%- for oid in aliases %} + {"{{ oid.name }}", OID{{ oid.oid }}}, +{%- endfor %} + }; +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/build-data/templates/tls_suite_info.cpp.in botan3-3.12.0+dfsg/src/build-data/templates/tls_suite_info.cpp.in --- botan3-3.7.1+dfsg/src/build-data/templates/tls_suite_info.cpp.in 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/templates/tls_suite_info.cpp.in 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,58 @@ +/* +* TLS cipher suite information +* +* This file was automatically generated by {{ script }} on {{ date }} +* using the IANA assignments (tls-parameters.txt sha256 {{ contents_hash }}) +* All manual changes will be lost. Edit the script instead. +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan::TLS { + +namespace { + +consteval auto available_ciphersuites() { + // clang-format off + auto codes = std::array { +{%- for suite in suites %} +#if {{ suite.gates_expr }} + uint16_t{0x{{ suite.code }}}, // {{ suite.name }} +#endif +{%- endfor %} + }; + // clang-format on + + return codes; +} + +} // namespace + +//static +bool Ciphersuite::is_known_usable(uint16_t code) { + static constexpr auto codes = available_ciphersuites(); + return std::binary_search(codes.begin(), codes.end(), code); +} + +//static +const std::vector& Ciphersuite::all_known_ciphersuites() { + // clang-format off + + // Note that this list of ciphersuites is ordered by id! + static const std::vector g_ciphersuite_list = { +{%- for suite in suites %} + Ciphersuite(0x{{ suite.code }}, "{{ suite.name }}", Auth_Method::{{ suite.sig_algo }}, Kex_Algo::{{ suite.kex_algo }}, "{{ suite.cipher_algo }}", {{ suite.cipher_keylen }}, "{{ suite.mac_algo }}", {{ suite.mac_keylen }}, KDF_Algo::{{ suite.kdf_algo }}, Nonce_Format::{{ suite.nonce_format }}), +{%- endfor %} + }; + + // clang-format on + + return g_ciphersuite_list; +} + +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/build-data/version.txt botan3-3.12.0+dfsg/src/build-data/version.txt --- botan3-3.7.1+dfsg/src/build-data/version.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/version.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,13 +1,13 @@ release_major = 3 -release_minor = 7 -release_patch = 1 +release_minor = 12 +release_patch = 0 -release_so_abi_rev = 7 +release_so_abi_rev = 12 release_suffix = '' # These are set by the distribution script -release_vc_rev = 'git:09cc7f97ceb828c19461b2a63f820d3226bb921b' -release_datestamp = 20250205 -release_type = 'release' +release_vc_rev = None +release_datestamp = 0 +release_type = 'unreleased' diff -Nru botan3-3.7.1+dfsg/src/build-data/version_info.h.in botan3-3.12.0+dfsg/src/build-data/version_info.h.in --- botan3-3.7.1+dfsg/src/build-data/version_info.h.in 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/build-data/version_info.h.in 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +#ifndef BOTAN_VERSION_INFO_H_ +#define BOTAN_VERSION_INFO_H_ + +/* NOLINTBEGIN(*-macro-usage) */ + +#define BOTAN_FULL_VERSION_STRING "%{full_version_string}" + +#define BOTAN_SHORT_VERSION_STRING "%{short_version_string}" + +%{if version_vc_rev} +#define BOTAN_VC_REVISION "%{version_vc_rev}" +%{endif} + +%{if distribution_info} +#define BOTAN_DISTRIBUTION_INFO_STRING "%{distribution_info}" +%{endif} + +/* NOLINTEND(*-macro-usage) */ + +#endif diff -Nru botan3-3.7.1+dfsg/src/cli/argon2.cpp botan3-3.12.0+dfsg/src/cli/argon2.cpp --- botan3-3.7.1+dfsg/src/cli/argon2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/argon2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,6 +12,8 @@ namespace Botan_CLI { +namespace { + #if defined(BOTAN_HAS_ARGON2_FMT) class Generate_Argon2 final : public Command { @@ -48,9 +50,9 @@ const bool ok = Botan::argon2_check_pwhash(password.data(), password.size(), hash); - output() << "Password is " << (ok ? "valid" : "NOT valid") << std::endl; + output() << "Password is " << (ok ? "valid" : "NOT valid") << "\n"; - if(ok == false) { + if(!ok) { set_return_code(1); } } @@ -60,4 +62,6 @@ #endif // argon2 +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/argparse.h botan3-3.12.0+dfsg/src/cli/argparse.h --- botan3-3.7.1+dfsg/src/cli/argparse.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/argparse.h 2026-05-07 01:38:28.000000000 +0000 @@ -17,9 +17,9 @@ class Argument_Parser final { public: - Argument_Parser(const std::string& spec, - const std::vector& extra_flags = {}, - const std::vector& extra_opts = {}); + explicit Argument_Parser(const std::string& spec, + const std::vector& extra_flags = {}, + const std::vector& extra_opts = {}); void parse_args(const std::vector& params); @@ -51,21 +51,21 @@ std::vector m_user_rest; }; -std::vector Argument_Parser::split_on(const std::string& str, char delim) { +inline std::vector Argument_Parser::split_on(const std::string& str, char delim) { std::vector elems; if(str.empty()) { return elems; } std::string substr; - for(auto i = str.begin(); i != str.end(); ++i) { - if(*i == delim) { + for(const char c : str) { + if(c == delim) { if(!substr.empty()) { elems.push_back(substr); } substr.clear(); } else { - substr += *i; + substr += c; } } @@ -77,15 +77,15 @@ return elems; } -bool Argument_Parser::flag_set(const std::string& flag_name) const { +inline bool Argument_Parser::flag_set(const std::string& flag_name) const { return m_user_flags.contains(flag_name); } -bool Argument_Parser::has_arg(const std::string& opt_name) const { +inline bool Argument_Parser::has_arg(const std::string& opt_name) const { return m_user_args.contains(opt_name); } -std::string Argument_Parser::get_arg(const std::string& opt_name) const { +inline std::string Argument_Parser::get_arg(const std::string& opt_name) const { auto i = m_user_args.find(opt_name); if(i == m_user_args.end()) { // this shouldn't occur unless you passed the wrong thing to get_arg @@ -94,7 +94,7 @@ return i->second; } -std::string Argument_Parser::get_arg_or(const std::string& opt_name, const std::string& otherwise) const { +inline std::string Argument_Parser::get_arg_or(const std::string& opt_name, const std::string& otherwise) const { auto i = m_user_args.find(opt_name); if(i == m_user_args.end() || i->second.empty()) { return otherwise; @@ -102,7 +102,7 @@ return i->second; } -size_t Argument_Parser::get_arg_sz(const std::string& opt_name) const { +inline size_t Argument_Parser::get_arg_sz(const std::string& opt_name) const { const std::string s = get_arg(opt_name); try { @@ -112,7 +112,7 @@ } } -size_t Argument_Parser::get_arg_hex_sz_or(const std::string& opt_name, const std::string& otherwise) const { +inline size_t Argument_Parser::get_arg_hex_sz_or(const std::string& opt_name, const std::string& otherwise) const { const std::string s = get_arg_or(opt_name, otherwise); try { @@ -122,7 +122,7 @@ } } -std::vector Argument_Parser::get_arg_list(const std::string& what) const { +inline std::vector Argument_Parser::get_arg_list(const std::string& what) const { if(what == m_spec_rest) { return m_user_rest; } @@ -130,10 +130,10 @@ return split_on(get_arg(what), ','); } -void Argument_Parser::parse_args(const std::vector& params) { +inline void Argument_Parser::parse_args(const std::vector& params) { std::vector args; for(const auto& param : params) { - if(param.find("--") == 0) { + if(param.starts_with("--")) { // option const auto eq = param.find('='); @@ -209,9 +209,9 @@ } } -Argument_Parser::Argument_Parser(const std::string& spec, - const std::vector& extra_flags, - const std::vector& extra_opts) { +inline Argument_Parser::Argument_Parser(const std::string& spec, + const std::vector& extra_flags, + const std::vector& extra_opts) { class CLI_Error_Invalid_Spec final : public CLI_Error { public: explicit CLI_Error_Invalid_Spec(const std::string& bad_spec) : @@ -251,8 +251,8 @@ } } else { // named argument - if(!m_spec_rest.empty()) // rest arg wasn't last - { + if(!m_spec_rest.empty()) { + // rest arg wasn't last throw CLI_Error_Invalid_Spec(spec); } diff -Nru botan3-3.7.1+dfsg/src/cli/asn1.cpp botan3-3.12.0+dfsg/src/cli/asn1.cpp --- botan3-3.7.1+dfsg/src/cli/asn1.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/asn1.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -17,6 +17,8 @@ namespace Botan_CLI { +namespace { + class ASN1_Printer final : public Command { public: ASN1_Printer() : @@ -45,7 +47,7 @@ const std::string input = get_arg("file"); const size_t print_limit = get_arg_sz("print-limit"); const size_t bin_limit = get_arg_sz("bin-limit"); - const bool print_context_specific = flag_set("skip-context-specific") == false; + const bool print_context_specific = !flag_set("skip-context-specific"); const size_t max_depth = get_arg_sz("max-depth"); const size_t value_column = 60; @@ -67,7 +69,7 @@ data.swap(file_contents); } - Botan::ASN1_Pretty_Printer printer( + const Botan::ASN1_Pretty_Printer printer( print_limit, bin_limit, print_context_specific, initial_level, value_column, max_depth); printer.print_to_stream(output(), data.data(), data.size()); @@ -92,9 +94,9 @@ } try { - Botan::OID oid(oid_str); + const Botan::OID oid(oid_str); - std::string name = oid.human_name_or_empty(); + const std::string name = oid.human_name_or_empty(); if(name.empty()) { output() << "OID " << oid_str << " is not recognized\n"; } else { @@ -105,13 +107,15 @@ } catch(Botan::Exception&) {} // This throws if the string is not known - Botan::OID oid = Botan::OID::from_string(oid_str); + const Botan::OID oid = Botan::OID::from_string(oid_str); output() << "The string '" << oid_str << "' is associated with OID " << oid.to_string() << "\n"; } }; BOTAN_REGISTER_COMMAND("oid_info", OID_Info); +} // namespace + } // namespace Botan_CLI #endif // BOTAN_HAS_ASN1 diff -Nru botan3-3.7.1+dfsg/src/cli/bcrypt.cpp botan3-3.12.0+dfsg/src/cli/bcrypt.cpp --- botan3-3.7.1+dfsg/src/cli/bcrypt.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/bcrypt.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,6 +12,8 @@ namespace Botan_CLI { +namespace { + #if defined(BOTAN_HAS_BCRYPT) class Generate_Bcrypt final : public Command { @@ -55,9 +57,9 @@ const bool ok = Botan::check_bcrypt(password, hash); - output() << "Password is " << (ok ? "valid" : "NOT valid") << std::endl; + output() << "Password is " << (ok ? "valid" : "NOT valid") << "\n"; - if(ok == false) { + if(!ok) { set_return_code(1); } } @@ -67,4 +69,6 @@ #endif // bcrypt +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/cc_enc.cpp botan3-3.12.0+dfsg/src/cli/cc_enc.cpp --- botan3-3.7.1+dfsg/src/cli/cc_enc.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/cc_enc.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,8 +9,10 @@ #if defined(BOTAN_HAS_FPE_FE1) && defined(BOTAN_HAS_PBKDF) + #include #include #include + #include namespace Botan_CLI { @@ -20,7 +22,7 @@ uint8_t sum = 0; bool alt = false; - while(cc_number) { + while(cc_number > 0) { uint8_t digit = cc_number % 10; if(alt) { digit *= 2; @@ -58,11 +60,9 @@ } uint64_t encrypt_cc_number(uint64_t cc_number, const Botan::SymmetricKey& key, const std::vector& tweak) { - const Botan::BigInt n = 1000000000000000; + const Botan::BigInt n(1000000000000000); - const uint64_t cc_ranked = cc_rank(cc_number); - - const Botan::BigInt c = Botan::FPE::fe1_encrypt(n, cc_ranked, key, tweak); + const Botan::BigInt c = Botan::FPE::fe1_encrypt(n, Botan::BigInt::from_u64(cc_rank(cc_number)), key, tweak); if(c.bits() > 50) { throw Botan::Internal_Error("FPE produced a number too large"); @@ -76,11 +76,9 @@ } uint64_t decrypt_cc_number(uint64_t enc_cc, const Botan::SymmetricKey& key, const std::vector& tweak) { - const Botan::BigInt n = 1000000000000000; - - const uint64_t cc_ranked = cc_rank(enc_cc); + const Botan::BigInt n(1000000000000000); - const Botan::BigInt c = Botan::FPE::fe1_decrypt(n, cc_ranked, key, tweak); + const Botan::BigInt c = Botan::FPE::fe1_decrypt(n, Botan::BigInt::from_u64(cc_rank(enc_cc)), key, tweak); if(c.bits() > 50) { throw CLI_Error("FPE produced a number too large"); @@ -93,8 +91,6 @@ return cc_derank(dec_cc); } -} // namespace - class CC_Encrypt final : public Command { public: CC_Encrypt() : Command("cc_encrypt CC passphrase --tweak=") {} @@ -151,6 +147,8 @@ BOTAN_REGISTER_COMMAND("cc_decrypt", CC_Decrypt); +} // namespace + } // namespace Botan_CLI #endif // FPE && PBKDF diff -Nru botan3-3.7.1+dfsg/src/cli/cipher.cpp botan3-3.12.0+dfsg/src/cli/cipher.cpp --- botan3-3.7.1+dfsg/src/cli/cipher.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/cipher.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,7 +11,7 @@ #include #include - #include + #include #if defined(BOTAN_HAS_AEAD_MODES) #include @@ -19,6 +19,8 @@ namespace Botan_CLI { +namespace { + class Cipher final : public Command { public: Cipher() : Command("cipher --cipher=AES-256/GCM --decrypt --key= --nonce= --ad= --buf-size=4096 input-file") {} @@ -75,6 +77,8 @@ BOTAN_REGISTER_COMMAND("cipher", Cipher); +} // namespace + } // namespace Botan_CLI #endif diff -Nru botan3-3.7.1+dfsg/src/cli/cli.cpp botan3-3.12.0+dfsg/src/cli/cli.cpp --- botan3-3.7.1+dfsg/src/cli/cli.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/cli.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -176,14 +176,16 @@ return std::cerr; } -std::vector Command::slurp_file(const std::string& input_file, size_t buf_size) const { +//static +std::vector Command::slurp_file(const std::string& input_file, size_t buf_size) { std::vector buf; auto insert_fn = [&](const uint8_t b[], size_t l) { buf.insert(buf.end(), b, b + l); }; Command::read_file(input_file, insert_fn, buf_size); return buf; } -std::string Command::slurp_file_as_str(const std::string& input_file, size_t buf_size) const { +//static +std::string Command::slurp_file_as_str(const std::string& input_file, size_t buf_size) { std::string str; auto insert_fn = [&](const uint8_t b[], size_t l) { str.append(reinterpret_cast(b), l); }; Command::read_file(input_file, insert_fn, buf_size); @@ -255,7 +257,7 @@ } // namespace std::string Command::get_passphrase(const std::string& prompt) { - if(echo_suppression_supported() == false) { + if(!echo_suppression_supported()) { error_output() << "Warning: terminal echo suppression not enabled for this platform\n"; } diff -Nru botan3-3.7.1+dfsg/src/cli/cli.h botan3-3.12.0+dfsg/src/cli/cli.h --- botan3-3.7.1+dfsg/src/cli/cli.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/cli.h 2026-05-07 01:38:28.000000000 +0000 @@ -31,7 +31,7 @@ std::shared_ptr cli_make_rng(const std::string& type = "", const std::string& hex_drbg_seed = ""); -class Command { +class Command /* NOLINT(*special-member-functions) */ { public: /** * Get a registered command @@ -160,9 +160,9 @@ /* * Read an entire file into memory and return the contents */ - std::vector slurp_file(const std::string& input_file, size_t buf_size = 0) const; + static std::vector slurp_file(const std::string& input_file, size_t buf_size = 0); - std::string slurp_file_as_str(const std::string& input_file, size_t buf_size = 0) const; + static std::string slurp_file_as_str(const std::string& input_file, size_t buf_size = 0); /* * Read a file calling consumer_fn() with the inputs @@ -218,8 +218,10 @@ }; }; -#define BOTAN_REGISTER_COMMAND(name, CLI_Class) \ - const Botan_CLI::Command::Registration reg_cmd_##CLI_Class( \ +// NOLINTNEXTLINE(*-macro-usage) +#define BOTAN_REGISTER_COMMAND(name, CLI_Class) \ + /* NOLINTNEXTLINE(cert-err58-cpp,*-throwing-static-initialization) */ \ + const Botan_CLI::Command::Registration reg_cmd_##CLI_Class( \ name, []() -> std::unique_ptr { return std::make_unique(); }) } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/cli_exceptions.h botan3-3.12.0+dfsg/src/cli/cli_exceptions.h --- botan3-3.7.1+dfsg/src/cli/cli_exceptions.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/cli_exceptions.h 2026-05-07 01:38:28.000000000 +0000 @@ -32,7 +32,7 @@ */ class CLI_Error_Unsupported final : public CLI_Error { public: - CLI_Error_Unsupported(const std::string& msg) : CLI_Error(msg) {} + explicit CLI_Error_Unsupported(const std::string& msg) : CLI_Error(msg) {} CLI_Error_Unsupported(const std::string& what, const std::string& who) : CLI_Error(what + " with '" + who + "' unsupported or not available") {} diff -Nru botan3-3.7.1+dfsg/src/cli/cli_rng.cpp botan3-3.12.0+dfsg/src/cli/cli_rng.cpp --- botan3-3.7.1+dfsg/src/cli/cli_rng.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/cli_rng.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -18,6 +18,10 @@ #include #endif +#if defined(BOTAN_HAS_JITTER_RNG) + #include +#endif + #if defined(BOTAN_HAS_ESDM_RNG) #include #endif @@ -32,6 +36,7 @@ #if defined(BOTAN_HAS_HMAC_DRBG) #include + #include #endif namespace Botan_CLI { @@ -53,6 +58,12 @@ } #endif +#if defined(BOTAN_HAS_JITTER_RNG) + if(rng_type == "jitter") { + return std::make_shared(); + } +#endif + const std::vector drbg_seed = Botan::hex_decode(hex_drbg_seed); #if defined(BOTAN_HAS_AUTO_SEEDING_RNG) @@ -108,11 +119,13 @@ } } +namespace { + class RNG final : public Command { public: RNG() : Command( - "rng --format=hex --system --esdm-full --esdm-pr --rdrand --auto --entropy --drbg --drbg-seed= *bytes") { + "rng --format=hex --system --esdm-full --esdm-pr --jitter --rdrand --auto --entropy --drbg --drbg-seed= *bytes") { } std::string group() const override { return "misc"; } @@ -124,7 +137,9 @@ std::string type = get_arg("rng-type"); if(type.empty()) { - for(std::string flag : {"system", "rdrand", "auto", "entropy", "drbg", "esdm-full", "esdm-pr"}) { + const std::vector known_rng_types = { + "system", "rdrand", "auto", "entropy", "drbg", "esdm-full", "esdm-pr", "jitter"}; + for(const auto& flag : known_rng_types) { if(flag_set(flag)) { type = flag; break; @@ -150,4 +165,6 @@ BOTAN_REGISTER_COMMAND("rng", RNG); +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/codec.cpp botan3-3.12.0+dfsg/src/cli/codec.cpp --- botan3-3.7.1+dfsg/src/cli/codec.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/codec.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -24,6 +24,8 @@ namespace Botan_CLI { +namespace { + #if defined(BOTAN_HAS_HEX_CODEC) class Hex_Encode final : public Command { @@ -52,8 +54,7 @@ void go() override { auto hex_dec_f = [&](const uint8_t b[], size_t l) { - std::vector bin = Botan::hex_decode(reinterpret_cast(b), l); - write_output(bin); + write_output(Botan::hex_decode(reinterpret_cast(b), l)); }; Command::read_file(get_arg("file"), hex_dec_f, 2); @@ -142,8 +143,7 @@ void go() override { auto write_bin = [&](const uint8_t b[], size_t l) { - Botan::secure_vector bin = Botan::base32_decode(reinterpret_cast(b), l); - write_output(bin); + write_output(Botan::base32_decode(reinterpret_cast(b), l)); }; Command::read_file(get_arg("file"), write_bin, 1024); @@ -182,8 +182,7 @@ void go() override { auto write_bin = [&](const uint8_t b[], size_t l) { - Botan::secure_vector bin = Botan::base64_decode(reinterpret_cast(b), l); - write_output(bin); + write_output(Botan::base64_decode(reinterpret_cast(b), l)); }; Command::read_file(get_arg("file"), write_bin, 1024); @@ -194,4 +193,6 @@ #endif // base64 +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/compress.cpp botan3-3.12.0+dfsg/src/cli/compress.cpp --- botan3-3.7.1+dfsg/src/cli/compress.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/compress.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -19,7 +19,7 @@ public: Compress() : Command("compress --type=gzip --level=6 --buf-size=8192 file") {} - std::string output_filename(const std::string& input_fsname, const std::string& comp_type) { + static std::string output_filename(const std::string& input_fsname, const std::string& comp_type) { const std::map suffixes = { {"zlib", "zlib"}, {"gzip", "gz"}, @@ -89,7 +89,7 @@ public: Decompress() : Command("decompress --buf-size=8192 file") {} - void parse_extension(const std::string& in_file, std::string& out_file, std::string& suffix) { + static void parse_extension(const std::string& in_file, std::string& out_file, std::string& suffix) { auto last_dot = in_file.find_last_of('.'); if(last_dot == std::string::npos || last_dot == 0) { throw CLI_Error("No extension detected in filename '" + in_file + "'"); @@ -106,7 +106,8 @@ void go() override { const size_t buf_size = get_arg_sz("buf-size"); const std::string in_file = get_arg("file"); - std::string out_file, suffix; + std::string out_file; + std::string suffix; parse_extension(in_file, out_file, suffix); std::ifstream in(in_file, std::ios::binary); diff -Nru botan3-3.7.1+dfsg/src/cli/entropy.cpp botan3-3.12.0+dfsg/src/cli/entropy.cpp --- botan3-3.7.1+dfsg/src/cli/entropy.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/entropy.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -4,11 +4,12 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include "../tests/test_rng.h" // FIXME #include "cli.h" #if defined(BOTAN_HAS_ENTROPY_SOURCE) #include + #include + #include #endif #if defined(BOTAN_HAS_COMPRESSION) @@ -17,8 +18,39 @@ namespace Botan_CLI { +namespace { + #if defined(BOTAN_HAS_ENTROPY_SOURCE) +class SeedCapturing_RNG final : public Botan::RandomNumberGenerator { + public: + bool accepts_input() const override { return true; } + + void clear() override {} + + bool is_seeded() const override { return false; } + + std::string name() const override { return "SeedCapturing"; } + + size_t samples() const { return m_samples; } + + const std::vector& seed_material() const { return m_seed; } + + private: + void fill_bytes_with_input(std::span output, std::span input) override { + if(!output.empty()) { + throw CLI_Error("SeedCapturing_RNG has no output"); + } + + m_samples++; + m_seed.insert(m_seed.end(), input.begin(), input.end()); + } + + private: + std::vector m_seed; + size_t m_samples = 0; +}; + class Entropy final : public Command { public: Entropy() : Command("entropy --truncate-at=128 source") {} @@ -41,7 +73,7 @@ } for(const std::string& source : sources) { - Botan_Tests::SeedCapturing_RNG rng; + SeedCapturing_RNG rng; const size_t entropy_estimate = entropy_sources.poll_just(rng, source); if(rng.samples() == 0) { @@ -78,7 +110,7 @@ if(sample.size() <= truncate_sample) { output() << Botan::hex_encode(sample) << "\n"; } else if(truncate_sample > 0) { - output() << Botan::hex_encode(&sample[0], truncate_sample) << "...\n"; + output() << Botan::hex_encode(sample.data(), truncate_sample) << "...\n"; } } } @@ -88,4 +120,6 @@ #endif +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/hash.cpp botan3-3.12.0+dfsg/src/cli/hash.cpp --- botan3-3.7.1+dfsg/src/cli/hash.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/hash.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,6 +12,8 @@ namespace Botan_CLI { +namespace { + #if defined(BOTAN_HAS_HASH) class Hash final : public Command { @@ -62,4 +64,6 @@ #endif +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/hmac.cpp botan3-3.12.0+dfsg/src/cli/hmac.cpp --- botan3-3.7.1+dfsg/src/cli/hmac.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/hmac.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -15,6 +15,8 @@ namespace Botan_CLI { +namespace { + #if defined(BOTAN_HAS_HMAC) class HMAC final : public Command { @@ -50,7 +52,7 @@ read_file(fsname, update_hmac, buf_size); output() << Botan::hex_encode(hmac->final()); - if(no_fsname == false) { + if(!no_fsname) { output() << " " << fsname; } @@ -66,4 +68,6 @@ #endif // hmac +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/main.cpp botan3-3.12.0+dfsg/src/cli/main.cpp --- botan3-3.7.1+dfsg/src/cli/main.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/main.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -31,6 +31,6 @@ return 1; } - std::vector args(argv + std::min(argc, 2), argv + argc); + const std::vector args(argv + std::min(argc, 2), argv + argc); return cmd->run(args); } diff -Nru botan3-3.7.1+dfsg/src/cli/math.cpp botan3-3.12.0+dfsg/src/cli/math.cpp --- botan3-3.7.1+dfsg/src/cli/math.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/math.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,10 +11,12 @@ #include #include #include - #include + #include namespace Botan_CLI { +namespace { + class Modular_Inverse final : public Command { public: Modular_Inverse() : Command("mod_inverse n mod") {} @@ -54,7 +56,7 @@ const Botan::BigInt p = Botan::random_prime(rng(), bits); if(hex) { - output() << "0x" << std::hex << p << "\n"; + output() << std::hex << p << "\n"; } else { output() << p << "\n"; } @@ -73,7 +75,7 @@ std::string description() const override { return "Test if the integer n is composite or prime"; } void go() override { - Botan::BigInt n(get_arg("n")); + const Botan::BigInt n(get_arg("n")); const size_t prob = get_arg_sz("prob"); const bool prime = Botan::is_prime(n, rng(), prob); @@ -96,7 +98,7 @@ std::string description() const override { return "Factor a given integer"; } void go() override { - Botan::BigInt n(get_arg("n")); + const Botan::BigInt n(get_arg("n")); std::vector factors = factorize(n, rng()); std::sort(factors.begin(), factors.end()); @@ -119,7 +121,7 @@ break; } - Botan::BigInt a_factor = 0; + Botan::BigInt a_factor; while(a_factor == 0) { a_factor = rho(n, rng); } @@ -140,11 +142,13 @@ * Uses Brent's cycle finding */ static Botan::BigInt rho(const Botan::BigInt& n, Botan::RandomNumberGenerator& rng) { - auto monty_n = std::make_shared(n); + const Botan::Montgomery_Params monty_n(n); - const Botan::Montgomery_Int one(monty_n, monty_n->R1(), false); + const auto one = Botan::Montgomery_Int::one(monty_n); - Botan::Montgomery_Int x(monty_n, Botan::BigInt::random_integer(rng, 2, n - 3), false); + const auto two = Botan::BigInt::from_s32(2); + const auto three = Botan::BigInt::from_s32(3); + Botan::Montgomery_Int x(monty_n, Botan::BigInt::random_integer(rng, two, n - three), false); Botan::Montgomery_Int y = x; Botan::Montgomery_Int z = one; Botan::Montgomery_Int t(monty_n); @@ -152,7 +156,8 @@ Botan::secure_vector ws; - size_t i = 1, k = 2; + size_t i = 1; + size_t k = 2; while(true) { i++; @@ -162,11 +167,10 @@ break; } - x.square_this(ws); // x = x^2 - x.add(one, ws); + x.square_this_n_times(ws, 1); // x = x^2 + x = x + one; - t = y; - t.sub(x, ws); + t = y - x; z.mul_by(t, ws); @@ -191,7 +195,7 @@ } // failed - return 0; + return Botan::BigInt::zero(); } // Remove (and return) any small (< 2^16) factors @@ -199,12 +203,12 @@ std::vector factors; while(n.is_even()) { - factors.push_back(2); - n /= 2; + factors.push_back(Botan::BigInt::from_s32(2)); + n >>= 1; } for(size_t j = 0; j != Botan::PRIME_TABLE_SIZE; j++) { - uint16_t prime = Botan::PRIMES[j]; + auto prime = Botan::BigInt::from_s32(Botan::PRIMES[j]); if(n < prime) { break; } @@ -227,6 +231,8 @@ BOTAN_REGISTER_COMMAND("factor", Factor); +} // namespace + } // namespace Botan_CLI #endif diff -Nru botan3-3.7.1+dfsg/src/cli/pbkdf.cpp botan3-3.12.0+dfsg/src/cli/pbkdf.cpp --- botan3-3.7.1+dfsg/src/cli/pbkdf.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/pbkdf.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -16,6 +16,8 @@ namespace Botan_CLI { +namespace { + #if defined(BOTAN_HAS_PASSWORD_HASHING) class PBKDF_Tune final : public Command { @@ -29,7 +31,7 @@ void go() override { const size_t output_len = get_arg_sz("output-len"); const size_t max_mem = get_arg_sz("max-mem"); - const auto tune_msec = std::chrono::milliseconds(get_arg_sz("tune-msec")); + const size_t tune_msec = get_arg_sz("tune-msec"); const std::string algo = get_arg("algo"); const bool check_time = flag_set("check"); @@ -45,14 +47,14 @@ if(time == "default") { pwhash = pwdhash_fam->default_params(); } else { - size_t msec = 0; + size_t desired_runtime_msec = 0; try { - msec = std::stoul(time); + desired_runtime_msec = std::stoul(time); } catch(std::exception&) { throw CLI_Usage_Error("Unknown time value '" + time + "' for pbkdf_tune"); } - pwhash = pwdhash_fam->tune(output_len, std::chrono::milliseconds(msec), max_mem, tune_msec); + pwhash = pwdhash_fam->tune_params(output_len, desired_runtime_msec, max_mem, tune_msec); } output() << "For " << time << " ms selected " << pwhash->to_string(); @@ -71,7 +73,7 @@ const uint64_t end_ns = Botan::OS::get_system_timestamp_ns(); const uint64_t dur_ns = end_ns - start_ns; - output() << " took " << (dur_ns / 1000000.0) << " msec to compute"; + output() << " took " << (static_cast(dur_ns) / 1000000.0) << " msec to compute"; #else output() << "No system clock"; #endif @@ -86,4 +88,6 @@ #endif +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/perf.cpp botan3-3.12.0+dfsg/src/cli/perf.cpp --- botan3-3.7.1+dfsg/src/cli/perf.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/perf.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -5,6 +5,7 @@ */ #include "perf.h" + #include "cli_exceptions.h" #include @@ -45,7 +46,7 @@ if(param.starts_with(alg)) { return param; } - return Botan::fmt("{}-{}", alg, param); + return alg + "-" + param; } } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/perf.h botan3-3.12.0+dfsg/src/cli/perf.h --- botan3-3.7.1+dfsg/src/cli/perf.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/perf.h 2026-05-07 01:38:28.000000000 +0000 @@ -7,16 +7,21 @@ #ifndef BOTAN_CLI_PERF_H_ #define BOTAN_CLI_PERF_H_ -#include -#include -#include #include #include #include +#include #include +#include #include "timer.h" +namespace Botan { + +class RandomNumberGenerator; + +} + namespace Botan_CLI { class PerfConfig final { @@ -24,7 +29,7 @@ PerfConfig(std::function record_result, size_t clock_speed, double clock_cycle_ratio, - std::chrono::milliseconds runtime, + uint64_t runtime, const std::vector& ecc_groups, const std::vector& buffer_sizes, std::ostream& error_output, @@ -42,7 +47,7 @@ const std::vector& ecc_groups() const { return m_ecc_groups; } - std::chrono::milliseconds runtime() const { return m_runtime; } + uint64_t runtime() const { return m_runtime; } std::ostream& error_output() const { return m_error_output; } @@ -62,14 +67,14 @@ std::function m_record_result; size_t m_clock_speed = 0; double m_clock_cycle_ratio = 0.0; - std::chrono::milliseconds m_runtime; + uint64_t m_runtime; std::vector m_ecc_groups; std::vector m_buffer_sizes; std::ostream& m_error_output; Botan::RandomNumberGenerator& m_rng; }; -class PerfTest { +class PerfTest /* NOLINT(*-special-member-functions) */ { public: virtual ~PerfTest() = default; @@ -93,8 +98,10 @@ static std::map& global_registry(); }; -#define BOTAN_REGISTER_PERF_TEST(name, Perf_Class) \ - const Botan_CLI::PerfTest::Registration reg_perf_##Perf_Class( \ +// NOLINTNEXTLINE(*-macro-usage) +#define BOTAN_REGISTER_PERF_TEST(name, Perf_Class) \ + /* NOLINTNEXTLINE(cert-err58-cpp,bugprone-throwing-static-initialization) */ \ + const Botan_CLI::PerfTest::Registration reg_perf_##Perf_Class( \ name, []() -> std::unique_ptr { return std::make_unique(); }) } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/perf_ec.cpp botan3-3.12.0+dfsg/src/cli/perf_ec.cpp --- botan3-3.7.1+dfsg/src/cli/perf_ec.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/perf_ec.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,102 +7,258 @@ #include "perf.h" #if defined(BOTAN_HAS_ECC_GROUP) + #include #include + #include #endif namespace Botan_CLI { +namespace { + #if defined(BOTAN_HAS_ECC_GROUP) -class PerfTest_EllipticCurve final : public PerfTest { +class PerfTest_EllipticCurve_Mul final : public PerfTest { public: void go(const PerfConfig& config) override { const auto run = config.runtime(); auto& rng = config.rng(); for(const auto& group_name : config.ecc_groups()) { - auto init_timer = config.make_timer(group_name + " initialization"); + const auto group = Botan::EC_Group::from_name(group_name); - while(init_timer->under(run)) { - Botan::EC_Group::clear_registered_curve_data(); - init_timer->run([&]() { Botan::EC_Group::from_name(group_name); }); + auto bp_timer = config.make_timer(group_name + " blinded base point mul"); + auto bp_nb_timer = config.make_timer(group_name + " unblinded base point mul"); + + auto vp_timer = config.make_timer(group_name + " blinded variable point mul"); + auto vp_nb_timer = config.make_timer(group_name + " unblinded variable point mul"); + + auto g = Botan::EC_AffinePoint::generator(group); + + Botan::Null_RNG null_rng; + + while(bp_timer->under(run) && vp_timer->under(run)) { + const auto k = Botan::EC_Scalar::random(group, rng); + + const auto r1 = bp_timer->run([&]() { return Botan::EC_AffinePoint::g_mul(k, rng); }); + const auto r2 = vp_timer->run([&]() { return g.mul(k, rng); }); + const auto r3 = bp_nb_timer->run([&]() { return Botan::EC_AffinePoint::g_mul(k, null_rng); }); + const auto r4 = vp_nb_timer->run([&]() { return g.mul(k, null_rng); }); + + BOTAN_ASSERT_NOMSG(r1 == r2); + BOTAN_ASSERT_NOMSG(r1 == r3); + BOTAN_ASSERT_NOMSG(r1 == r4); } - config.record_result(*init_timer); + config.record_result(*bp_timer); + config.record_result(*bp_nb_timer); + config.record_result(*vp_timer); + config.record_result(*vp_nb_timer); + } + } +}; + +BOTAN_REGISTER_PERF_TEST("ecc_mul", PerfTest_EllipticCurve_Mul); + +class PerfTest_EllipticCurve_Mul2 final : public PerfTest { + public: + void go(const PerfConfig& config) override { + const auto run = config.runtime(); + auto& rng = config.rng(); + for(const auto& group_name : config.ecc_groups()) { const auto group = Botan::EC_Group::from_name(group_name); - auto bp_timer = config.make_timer(group_name + " base point mul"); - auto vp_timer = config.make_timer(group_name + " variable point mul"); - auto add_timer = config.make_timer(group_name + " point addition"); - auto der_uc_timer = config.make_timer(group_name + " point deserialize (uncompressed)"); - auto der_c_timer = config.make_timer(group_name + " point deserialize (compressed)"); - auto mul2_setup_timer = config.make_timer(group_name + " mul2 setup"); - auto mul2_timer = config.make_timer(group_name + " mul2"); - auto scalar_inv_timer = config.make_timer(group_name + " scalar inversion"); - auto h2c_nu_timer = config.make_timer(group_name + " hash to curve (NU)"); - auto h2c_ro_timer = config.make_timer(group_name + " hash to curve (RO)"); + auto mul2_setup_timer = config.make_timer(group_name + " mul2_vartime setup"); + auto mul2_vt_timer = config.make_timer(group_name + " mul2_vartime"); + auto mul2_ct_timer = config.make_timer(group_name + " blinded mul2"); + auto mul2_ct_nb_timer = config.make_timer(group_name + " unblinded mul2"); - std::vector ws; + Botan::Null_RNG null_rng; auto g = Botan::EC_AffinePoint::generator(group); + while(mul2_setup_timer->under(run) && mul2_ct_timer->under(run)) { + const auto k = Botan::EC_Scalar::random(group, rng); + const auto k2 = Botan::EC_Scalar::random(group, rng); + + const auto y = Botan::EC_AffinePoint::g_mul(Botan::EC_Scalar::random(group, rng), rng); + + auto mul2 = mul2_setup_timer->run([&]() { return Botan::EC_Group::Mul2Table(y); }); + + auto pt = mul2_vt_timer->run([&]() { return mul2.mul2_vartime(k, k2); }); + + auto pt2 = mul2_ct_timer->run([&]() { return Botan::EC_AffinePoint::mul_px_qy(g, k, y, k2, rng); }); + + auto pt3 = + mul2_ct_nb_timer->run([&]() { return Botan::EC_AffinePoint::mul_px_qy(g, k, y, k2, null_rng); }); + + BOTAN_ASSERT_NOMSG(pt == pt2); + BOTAN_ASSERT_NOMSG(pt == pt3); + } + + config.record_result(*mul2_setup_timer); + config.record_result(*mul2_vt_timer); + config.record_result(*mul2_ct_timer); + config.record_result(*mul2_ct_nb_timer); + } + } +}; + +BOTAN_REGISTER_PERF_TEST("ecc_mul2", PerfTest_EllipticCurve_Mul2); + +class PerfTest_EllipticCurve_H2C final : public PerfTest { + public: + void go(const PerfConfig& config) override { + const auto run = config.runtime(); + auto& rng = config.rng(); + + for(const auto& group_name : config.ecc_groups()) { + const auto group = Botan::EC_Group::from_name(group_name); + const bool h2c_supported = [&]() { try { - Botan::EC_AffinePoint::hash_to_curve_nu(group, "SHA-256", {}, {}); + Botan::EC_AffinePoint::hash_to_curve_nu(group, "SHA-256", {}, ""); } catch(Botan::Not_Implemented&) { return false; } return true; }(); - while(bp_timer->under(run) && vp_timer->under(run)) { - const auto k = Botan::EC_Scalar::random(group, rng); - const auto r1 = bp_timer->run([&]() { return Botan::EC_AffinePoint::g_mul(k, rng, ws); }); - const auto r2 = vp_timer->run([&]() { return g.mul(k, rng, ws); }); + if(!h2c_supported) { + continue; + } + + auto h2c_nu_timer = config.make_timer(group_name + " hash to curve (NU)"); + auto h2c_ro_timer = config.make_timer(group_name + " hash to curve (RO)"); + + std::vector input(32); + + while(h2c_ro_timer->under(run)) { + rng.randomize(input); + h2c_nu_timer->run([&]() { Botan::EC_AffinePoint::hash_to_curve_nu(group, "SHA-256", input, "domain"); }); + h2c_ro_timer->run([&]() { Botan::EC_AffinePoint::hash_to_curve_ro(group, "SHA-256", input, "domain"); }); + } + + config.record_result(*h2c_nu_timer); + config.record_result(*h2c_ro_timer); + } + } +}; + +BOTAN_REGISTER_PERF_TEST("ecc_h2c", PerfTest_EllipticCurve_H2C); + +class PerfTest_EllipticCurve_Misc final : public PerfTest { + public: + void go(const PerfConfig& config) override { + const auto run = config.runtime(); + auto& rng = config.rng(); + + for(const auto& group_name : config.ecc_groups()) { + auto init_timer = config.make_timer(group_name + " initialization"); + + while(init_timer->under(run)) { + Botan::EC_Group::clear_registered_curve_data(); + init_timer->run([&]() { Botan::EC_Group::from_name(group_name); }); + } + + config.record_result(*init_timer); + + const auto group = Botan::EC_Group::from_name(group_name); + + auto pt_add_timer = config.make_timer(group_name + " point addition"); + auto pt_neg_timer = config.make_timer(group_name + " point negation"); + auto der_uc_timer = config.make_timer(group_name + " point deserialize (uncompressed)"); + auto der_c_timer = config.make_timer(group_name + " point deserialize (compressed)"); + + while(pt_add_timer->under(run) && der_c_timer->under(run)) { + const auto r1 = Botan::EC_AffinePoint::g_mul(Botan::EC_Scalar::random(group, rng), rng); + const auto r2 = Botan::EC_AffinePoint::g_mul(Botan::EC_Scalar::random(group, rng), rng); const auto r1_bytes = r1.serialize_uncompressed(); const auto r2_bytes = r2.serialize_uncompressed(); - BOTAN_ASSERT_EQUAL(r1_bytes, r2_bytes, "Same result for multiplication"); - add_timer->run([&]() { r1.add(r2); }); + pt_add_timer->run([&]() { r1.add(r2); }); + pt_neg_timer->run([&]() { return r1.negate(); }); der_uc_timer->run([&]() { Botan::EC_AffinePoint::deserialize(group, r1_bytes); }); + der_uc_timer->run([&]() { Botan::EC_AffinePoint::deserialize(group, r2_bytes); }); const auto r1_cbytes = r1.serialize_compressed(); + const auto r2_cbytes = r2.serialize_compressed(); der_c_timer->run([&]() { Botan::EC_AffinePoint::deserialize(group, r1_cbytes); }); + der_c_timer->run([&]() { Botan::EC_AffinePoint::deserialize(group, r2_cbytes); }); + } + + config.record_result(*pt_add_timer); + config.record_result(*pt_neg_timer); + config.record_result(*der_uc_timer); + config.record_result(*der_c_timer); + } + } +}; - auto mul2 = mul2_setup_timer->run([&]() { return Botan::EC_Group::Mul2Table(r1); }); +BOTAN_REGISTER_PERF_TEST("ecc_misc", PerfTest_EllipticCurve_Misc); + +class PerfTest_EllipticCurve_Scalar final : public PerfTest { + public: + void go(const PerfConfig& config) override { + const auto run = config.runtime(); + auto& rng = config.rng(); - auto k_inv = scalar_inv_timer->run([&]() { return k.invert(); }); + for(const auto& group_name : config.ecc_groups()) { + const auto group = Botan::EC_Group::from_name(group_name); - auto pt = mul2_timer->run([&]() { return mul2.mul2_vartime(k, k_inv); }); + auto scalar_add_timer = config.make_timer(group_name + " scalar add"); + auto scalar_mul_timer = config.make_timer(group_name + " scalar mul"); + auto scalar_redc_timer = config.make_timer(group_name + " scalar redc"); + auto scalar_inv_timer = config.make_timer(group_name + " scalar inversion"); + auto scalar_inv_vt_timer = config.make_timer(group_name + " scalar inversion vartime"); - if(h2c_supported) { - h2c_nu_timer->run([&]() { Botan::EC_AffinePoint::hash_to_curve_nu(group, "SHA-256", r1_bytes, {}); }); - h2c_ro_timer->run([&]() { Botan::EC_AffinePoint::hash_to_curve_ro(group, "SHA-256", r1_bytes, {}); }); - } + while(scalar_inv_timer->under(run)) { + const auto rnd1 = rng.random_vec(group.get_order_bytes() * 2); + const auto rnd2 = rng.random_vec(group.get_order_bytes() * 2); + + const auto s1 = + scalar_redc_timer->run([&]() { return Botan::EC_Scalar::from_bytes_mod_order(group, rnd1); }); + const auto s2 = + scalar_redc_timer->run([&]() { return Botan::EC_Scalar::from_bytes_mod_order(group, rnd2); }); + + const auto sum1 = scalar_add_timer->run([&]() { return s1 + s2; }); + const auto sum2 = scalar_add_timer->run([&]() { return s2 + s1; }); + BOTAN_ASSERT_NOMSG(sum1 == sum2); + + const auto s1_inv = scalar_inv_timer->run([&]() { return s1.invert(); }); + const auto s1_inv_vt = scalar_inv_vt_timer->run([&]() { return s1.invert_vartime(); }); + BOTAN_ASSERT_NOMSG(s1_inv == s1_inv_vt); + + const auto s2_inv = scalar_inv_timer->run([&]() { return s2.invert(); }); + const auto s2_inv_vt = scalar_inv_vt_timer->run([&]() { return s2.invert_vartime(); }); + BOTAN_ASSERT_NOMSG(s2_inv == s2_inv_vt); + + const auto p1 = scalar_mul_timer->run([&]() { return s1 * s2; }); + const auto p2 = scalar_mul_timer->run([&]() { return s2 * s1; }); + BOTAN_ASSERT_NOMSG(p1 == p2); + + const auto c1 = scalar_mul_timer->run([&]() { return p1 * s1_inv; }); + BOTAN_ASSERT_NOMSG(c1 == s2); + const auto c2 = scalar_mul_timer->run([&]() { return p2 * s1_inv_vt; }); + BOTAN_ASSERT_NOMSG(c2 == s2); } - config.record_result(*add_timer); - config.record_result(*bp_timer); - config.record_result(*vp_timer); - config.record_result(*mul2_setup_timer); - config.record_result(*mul2_timer); + config.record_result(*scalar_add_timer); + config.record_result(*scalar_mul_timer); + config.record_result(*scalar_redc_timer); config.record_result(*scalar_inv_timer); - config.record_result(*der_uc_timer); - config.record_result(*der_c_timer); - - if(h2c_supported) { - config.record_result(*h2c_nu_timer); - config.record_result(*h2c_ro_timer); - } + config.record_result(*scalar_inv_vt_timer); } } }; -BOTAN_REGISTER_PERF_TEST("ecc", PerfTest_EllipticCurve); +BOTAN_REGISTER_PERF_TEST("ecc_scalar", PerfTest_EllipticCurve_Scalar); #endif +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/perf_math.cpp botan3-3.12.0+dfsg/src/cli/perf_math.cpp --- botan3-3.7.1+dfsg/src/cli/perf_math.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/perf_math.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,14 +6,18 @@ #include "perf.h" +#include + #if defined(BOTAN_HAS_BIGINT) + #include #include #include #endif #if defined(BOTAN_HAS_NUMBERTHEORY) #include - #include + #include + #include #include #endif @@ -23,14 +27,16 @@ namespace Botan_CLI { +namespace { + #if defined(BOTAN_HAS_BIGINT) class PerfTest_MpMul final : public PerfTest { public: void go(const PerfConfig& config) override { - std::chrono::milliseconds runtime_per_size = config.runtime(); + const auto runtime_per_size = config.runtime(); - for(size_t bits : {256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096}) { + for(const size_t bits : {256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096}) { auto mul_timer = config.make_timer("BigInt mul " + std::to_string(bits)); auto sqr_timer = config.make_timer("BigInt sqr " + std::to_string(bits)); @@ -62,9 +68,9 @@ class PerfTest_MpDiv final : public PerfTest { public: void go(const PerfConfig& config) override { - std::chrono::milliseconds runtime_per_size = config.runtime(); + const auto runtime_per_size = config.runtime(); - for(size_t n_bits : {256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096}) { + for(const size_t n_bits : {256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096}) { const size_t q_bits = n_bits / 2; const std::string bit_descr = std::to_string(n_bits) + "/" + std::to_string(q_bits); @@ -73,9 +79,12 @@ Botan::BigInt y; Botan::BigInt x; - Botan::secure_vector ws; + const Botan::secure_vector ws; - Botan::BigInt q1, r1, q2, r2; + Botan::BigInt q1; + Botan::BigInt r1; + Botan::BigInt q2; + Botan::BigInt r2; while(ct_div_timer->under(runtime_per_size)) { x.randomize(config.rng(), n_bits); @@ -104,20 +113,22 @@ class PerfTest_MpDiv10 final : public PerfTest { public: void go(const PerfConfig& config) override { - std::chrono::milliseconds runtime_per_size = config.runtime(); + const auto runtime_per_size = config.runtime(); - for(size_t n_bits : {256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096}) { + for(const size_t n_bits : {256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096}) { const std::string bit_descr = std::to_string(n_bits) + "/10"; auto div_timer = config.make_timer("BigInt div " + bit_descr); auto ct_div_timer = config.make_timer("BigInt ct_div " + bit_descr); Botan::BigInt x; - Botan::secure_vector ws; + const Botan::secure_vector ws; const auto ten = Botan::BigInt::from_word(10); - Botan::BigInt q1, r1, q2; - Botan::word r2; + Botan::BigInt q1; + Botan::BigInt r1; + Botan::BigInt q2; + Botan::word r2 = 0; while(ct_div_timer->under(runtime_per_size)) { x.randomize(config.rng(), n_bits); @@ -151,22 +162,22 @@ void go(const PerfConfig& config) override { const auto runtime = config.runtime(); - for(size_t bitsize : {512, 1024, 2048, 4096}) { + for(const size_t bitsize : {256, 512, 1024, 2048, 4096}) { Botan::BigInt p(config.rng(), bitsize); - std::string bit_str = std::to_string(bitsize) + " bit "; + const std::string bit_str = std::to_string(bitsize) + " bit "; auto barrett_setup_pub_timer = config.make_timer(bit_str + "Barrett setup public"); auto barrett_setup_sec_timer = config.make_timer(bit_str + "Barrett setup secret"); while(barrett_setup_sec_timer->under(runtime)) { - barrett_setup_sec_timer->run([&]() { Botan::Modular_Reducer::for_secret_modulus(p); }); - barrett_setup_pub_timer->run([&]() { Botan::Modular_Reducer::for_public_modulus(p); }); + barrett_setup_sec_timer->run([&]() { Botan::Barrett_Reduction::for_secret_modulus(p); }); + barrett_setup_pub_timer->run([&]() { Botan::Barrett_Reduction::for_public_modulus(p); }); } config.record_result(*barrett_setup_pub_timer); config.record_result(*barrett_setup_sec_timer); - auto mod_p = Botan::Modular_Reducer::for_public_modulus(p); + auto mod_p = Botan::Barrett_Reduction::for_public_modulus(p); auto barrett_timer = config.make_timer(bit_str + "Barrett redc"); auto knuth_timer = config.make_timer(bit_str + "Knuth redc"); @@ -197,7 +208,7 @@ void go(const PerfConfig& config) override { const auto runtime = config.runtime(); - for(size_t bits : {256, 384, 512, 1024, 2048}) { + for(const size_t bits : {256, 384, 512, 1024, 2048}) { const std::string bit_str = std::to_string(bits); auto timer = config.make_timer("inverse_mod-" + bit_str); @@ -233,7 +244,7 @@ void go(const PerfConfig& config) override { const auto runtime = config.runtime(); - for(size_t bits : {256, 512, 1024}) { + for(const size_t bits : {256, 512, 1024}) { auto mr_timer = config.make_timer("Miller-Rabin-" + std::to_string(bits)); auto bpsw_timer = config.make_timer("Bailie-PSW-" + std::to_string(bits)); auto lucas_timer = config.make_timer("Lucas-" + std::to_string(bits)); @@ -241,9 +252,11 @@ Botan::BigInt n = Botan::random_prime(config.rng(), bits); while(lucas_timer->under(runtime)) { - auto mod_n = Botan::Modular_Reducer::for_public_modulus(n); + auto mod_n = Botan::Barrett_Reduction::for_public_modulus(n); + const Botan::Montgomery_Params monty_n(n, mod_n); - mr_timer->run([&]() { return Botan::is_miller_rabin_probable_prime(n, mod_n, config.rng(), 2); }); + mr_timer->run( + [&]() { return Botan::is_miller_rabin_probable_prime(n, mod_n, monty_n, config.rng(), 2); }); bpsw_timer->run([&]() { return Botan::is_bailie_psw_probable_prime(n, mod_n); }); @@ -271,26 +284,15 @@ for(size_t bits : {256, 384, 512, 768, 1024, 1536}) { auto genprime_timer = config.make_timer("random_prime " + std::to_string(bits)); - auto gensafe_timer = config.make_timer("random_safe_prime " + std::to_string(bits)); auto is_prime_timer = config.make_timer("is_prime " + std::to_string(bits)); - while(gensafe_timer->under(runtime)) { + while(genprime_timer->under(runtime) && is_prime_timer->under(runtime)) { const Botan::BigInt p = genprime_timer->run([&] { return Botan::random_prime(rng, bits, coprime); }); if(!is_prime_timer->run([&] { return Botan::is_prime(p, rng, 64, true); })) { config.error_output() << "Generated prime " << p << " which failed a primality test"; } - const Botan::BigInt sg = gensafe_timer->run([&] { return Botan::random_safe_prime(rng, bits); }); - - if(!is_prime_timer->run([&] { return Botan::is_prime(sg, rng, 64, true); })) { - config.error_output() << "Generated safe prime " << sg << " which failed a primality test"; - } - - if(!is_prime_timer->run([&] { return Botan::is_prime(sg / 2, rng, 64, true); })) { - config.error_output() << "Generated prime " << sg / 2 << " which failed a primality test"; - } - // Now test p+2, p+4, ... which may or may not be prime for(size_t i = 2; i <= 64; i += 2) { is_prime_timer->run([&]() { Botan::is_prime(p + i, rng, 64, true); }); @@ -298,7 +300,6 @@ } config.record_result(*genprime_timer); - config.record_result(*gensafe_timer); config.record_result(*is_prime_timer); } } @@ -313,7 +314,7 @@ class PerfTest_ModExp final : public PerfTest { public: void go(const PerfConfig& config) override { - for(size_t group_bits : {1024, 1536, 2048, 3072, 4096, 6144, 8192}) { + for(const size_t group_bits : {1024, 1536, 2048, 3072, 4096, 6144, 8192}) { const std::string group_name = "modp/ietf/" + std::to_string(group_bits); auto group = Botan::DL_Group::from_name(group_name); @@ -341,4 +342,6 @@ #endif +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/perf_misc.cpp botan3-3.12.0+dfsg/src/cli/perf_misc.cpp --- botan3-3.7.1+dfsg/src/cli/perf_misc.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/perf_misc.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,7 +8,18 @@ #include // Always available: +#include #include +#include +#include + +#if defined(BOTAN_HAS_BASE32_CODEC) + #include +#endif + +#if defined(BOTAN_HAS_BASE58_CODEC) + #include +#endif #if defined(BOTAN_HAS_BASE64_CODEC) #include @@ -28,10 +39,12 @@ namespace Botan_CLI { +namespace { + class PerfTest_Hex final : public PerfTest { public: void go(const PerfConfig& config) override { - for(size_t buf_size : config.buffer_sizes()) { + for(const size_t buf_size : config.buffer_sizes()) { std::vector ibuf(buf_size); std::vector rbuf(buf_size); const size_t olen = 2 * buf_size; @@ -59,11 +72,45 @@ BOTAN_REGISTER_PERF_TEST("hex", PerfTest_Hex); +#if defined(BOTAN_HAS_BASE32_CODEC) +class PerfTest_Base32 final : public PerfTest { + public: + void go(const PerfConfig& config) override { + for(const size_t buf_size : config.buffer_sizes()) { + std::vector ibuf(buf_size); + std::vector rbuf(buf_size); + const size_t olen = Botan::base32_encode_max_output(ibuf.size()); + + auto enc_timer = config.make_timer("base32", ibuf.size(), "encode", "", ibuf.size()); + + auto dec_timer = config.make_timer("base32", olen, "decode", "", olen); + + const auto msec = config.runtime(); + + while(enc_timer->under(msec) && dec_timer->under(msec)) { + config.rng().randomize(ibuf); + + std::string b32 = enc_timer->run([&]() { return Botan::base32_encode(ibuf); }); + + dec_timer->run([&]() { Botan::base32_decode(rbuf.data(), b32); }); + BOTAN_ASSERT(rbuf == ibuf, "Encode/decode round trip ok"); + } + + config.record_result(*enc_timer); + config.record_result(*dec_timer); + } + } +}; + +BOTAN_REGISTER_PERF_TEST("base32", PerfTest_Base32); + +#endif + #if defined(BOTAN_HAS_BASE64_CODEC) class PerfTest_Base64 final : public PerfTest { public: void go(const PerfConfig& config) override { - for(size_t buf_size : config.buffer_sizes()) { + for(const size_t buf_size : config.buffer_sizes()) { std::vector ibuf(buf_size); std::vector rbuf(buf_size); const size_t olen = Botan::base64_encode_max_output(ibuf.size()); @@ -93,6 +140,36 @@ #endif +#if defined(BOTAN_HAS_BASE58_CODEC) +class PerfTest_Base58 final : public PerfTest { + public: + void go(const PerfConfig& config) override { + for(const size_t buf_size : config.buffer_sizes()) { + std::vector ibuf(buf_size); + + auto enc_timer = config.make_timer("base58", ibuf.size(), "encode", "", ibuf.size()); + auto dec_timer = config.make_timer("base58", ibuf.size(), "decode", "", ibuf.size()); + + const auto msec = config.runtime(); + + while(enc_timer->under(msec) && dec_timer->under(msec)) { + config.rng().randomize(ibuf); + + const std::string b58 = enc_timer->run([&]() { return Botan::base58_encode(ibuf); }); + const auto rbuf = dec_timer->run([&] { return Botan::base58_decode(b58); }); + BOTAN_ASSERT(rbuf == ibuf, "Encode/decode round trip ok"); + } + + config.record_result(*enc_timer); + config.record_result(*dec_timer); + } + } +}; + +BOTAN_REGISTER_PERF_TEST("base58", PerfTest_Base58); + +#endif + #if defined(BOTAN_HAS_FPE_FE1) class PerfTest_FpeFe1 final : public PerfTest { @@ -230,4 +307,6 @@ #endif +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/perf_pk_enc.cpp botan3-3.12.0+dfsg/src/cli/perf_pk_enc.cpp --- botan3-3.7.1+dfsg/src/cli/perf_pk_enc.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/perf_pk_enc.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,20 +9,21 @@ #if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) #include #include + #include + #include #endif namespace Botan_CLI { +namespace { + #if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) class PerfTest_PKEnc : public PerfTest { public: virtual std::string algo() const = 0; - virtual std::vector keygen_params(const PerfConfig& config) const { - BOTAN_UNUSED(config); - return {""}; - } + virtual std::vector keygen_params(const PerfConfig& /*config*/) const { return {""}; } void go(const PerfConfig& config) override { const std::string alg = this->algo(); @@ -35,15 +36,16 @@ } } - void bench_pk_ka(const PerfConfig& config, - const std::string& nm, - const std::string& algo, - const std::string& params, - const std::string& provider = "") { + static void bench_pk_ka(const PerfConfig& config, + const std::string& nm, + const std::string& algo, + const std::string& params, + const std::string& provider = "") { auto& rng = config.rng(); const auto msec = config.runtime(); - std::vector plaintext, ciphertext; + std::vector plaintext; + std::vector ciphertext; auto keygen_timer = config.make_timer(nm, 1, "keygen"); @@ -97,8 +99,7 @@ public: std::string algo() const override { return "ElGamal"; } - std::vector keygen_params(const PerfConfig& config) const override { - BOTAN_UNUSED(config); + std::vector keygen_params(const PerfConfig& /*config*/) const override { return { "modp/ietf/1024", "modp/ietf/2048", @@ -116,4 +117,6 @@ #endif +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/perf_pk_ka.cpp botan3-3.12.0+dfsg/src/cli/perf_pk_ka.cpp --- botan3-3.7.1+dfsg/src/cli/perf_pk_ka.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/perf_pk_ka.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,20 +9,20 @@ #if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) #include #include + #include #endif namespace Botan_CLI { +namespace { + #if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) class PerfTest_PKKa : public PerfTest { public: virtual std::string algo() const = 0; - virtual std::vector keygen_params(const PerfConfig& config) const { - BOTAN_UNUSED(config); - return {""}; - } + virtual std::vector keygen_params(const PerfConfig& /*config*/) const { return {""}; } void go(const PerfConfig& config) override { const std::string alg = this->algo(); @@ -35,11 +35,11 @@ } } - void bench_pk_ka(const PerfConfig& config, - const std::string& nm, - const std::string& algo, - const std::string& params, - const std::string& provider = "") { + static void bench_pk_ka(const PerfConfig& config, + const std::string& nm, + const std::string& algo, + const std::string& params, + const std::string& provider = "") { const auto msec = config.runtime(); const std::string kdf = "KDF2(SHA-256)"; // arbitrary choice @@ -91,8 +91,7 @@ public: std::string algo() const override { return "DH"; } - std::vector keygen_params(const PerfConfig& config) const override { - BOTAN_UNUSED(config); + std::vector keygen_params(const PerfConfig& /*config*/) const override { return { "ffdhe/ietf/2048", "ffdhe/ietf/3072", @@ -146,4 +145,6 @@ #endif +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/perf_pk_kem.cpp botan3-3.12.0+dfsg/src/cli/perf_pk_kem.cpp --- botan3-3.7.1+dfsg/src/cli/perf_pk_kem.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/perf_pk_kem.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,23 +6,25 @@ #include "perf.h" +#include + #if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) #include #include + #include #endif namespace Botan_CLI { +namespace { + #if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) class PerfTest_PK_KEM : public PerfTest { public: virtual std::string algo() const = 0; - virtual std::vector keygen_params(const PerfConfig& config) const { - BOTAN_UNUSED(config); - return {""}; - } + virtual std::vector keygen_params(const PerfConfig& /*config*/) const { return {""}; } void go(const PerfConfig& config) override { const std::string alg = this->algo(); @@ -35,11 +37,11 @@ } } - void bench_pk_kem(const PerfConfig& config, - const std::string& nm, - const std::string& algo, - const std::string& params, - const std::string& provider = "") { + static void bench_pk_kem(const PerfConfig& config, + const std::string& nm, + const std::string& algo, + const std::string& params, + const std::string& provider = "") { const auto msec = config.runtime(); auto& rng = config.rng(); @@ -70,7 +72,7 @@ kem_enc_timer->stop(); kem_dec_timer->start(); - Botan::secure_vector dec_shared_key = + const Botan::secure_vector dec_shared_key = dec.decrypt(kem_result.encapsulated_shared_key(), 64, salt); kem_dec_timer->stop(); @@ -93,8 +95,7 @@ public: std::string algo() const override { return "Kyber"; } - std::vector keygen_params(const PerfConfig& config) const override { - BOTAN_UNUSED(config); + std::vector keygen_params(const PerfConfig& /*config*/) const override { return { "Kyber-512-r3", "Kyber-512-90s-r3", @@ -116,8 +117,7 @@ public: std::string algo() const override { return "ML-KEM"; } - std::vector keygen_params(const PerfConfig& config) const override { - BOTAN_UNUSED(config); + std::vector keygen_params(const PerfConfig& /*config*/) const override { return { "ML-KEM-512", "ML-KEM-768", @@ -136,8 +136,7 @@ public: std::string algo() const override { return "FrodoKEM"; } - std::vector keygen_params(const PerfConfig& config) const override { - BOTAN_UNUSED(config); + std::vector keygen_params(const PerfConfig& /*config*/) const override { return { "FrodoKEM-640-SHAKE", "FrodoKEM-640-AES", @@ -165,25 +164,24 @@ public: std::string algo() const override { return "ClassicMcEliece"; } - std::vector keygen_params(const PerfConfig& config) const override { - BOTAN_UNUSED(config); + std::vector keygen_params(const PerfConfig& /*config*/) const override { return { - "mceliece348864", - "mceliece348864f", - "mceliece460896", - "mceliece460896f", - "mceliece6688128", - "mceliece6688128f", - "mceliece6688128pc", - "mceliece6688128pcf", - "mceliece6960119", - "mceliece6960119f", - "mceliece6960119pc", - "mceliece6960119pcf", - "mceliece8192128", - "mceliece8192128f", - "mceliece8192128pc", - "mceliece8192128pcf", + "348864", + "348864f", + "460896", + "460896f", + "6688128", + "6688128f", + "6688128pc", + "6688128pcf", + "6960119", + "6960119f", + "6960119pc", + "6960119pcf", + "8192128", + "8192128f", + "8192128pc", + "8192128pcf", }; } }; @@ -192,4 +190,6 @@ #endif +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/perf_pk_misc.cpp botan3-3.12.0+dfsg/src/cli/perf_pk_misc.cpp --- botan3-3.7.1+dfsg/src/cli/perf_pk_misc.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/perf_pk_misc.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,16 +7,71 @@ #include "perf.h" #if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) + #include #include + #include + #include + #include + #include #endif #if defined(BOTAN_HAS_ECDSA) + #include #include #include #endif namespace Botan_CLI { +namespace { + +#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) + +class PerfTest_PKKeyParsing final : public PerfTest { + public: + void go(const PerfConfig& config) override { + const auto runtime = config.runtime(); + auto& rng = config.rng(); + + const std::pair keygen_algos[] = { + {"RSA", "2048"}, + {"ECDSA", "secp256r1"}, + {"ECDSA", "brainpool512r1"}, + {"DH", "modp/ietf/2048"}, + {"X25519", ""}, + {"Ed25519", ""}, + {"ML-DSA", "ML-DSA-6x5"}, + {"ML-KEM", "ML-KEM-768"}, + }; + + for(const auto& [algo, params] : keygen_algos) { + auto sk = Botan::create_private_key(algo, rng, params); + + if(!sk) { + continue; + } + + const auto pk = sk->public_key(); + + const std::string nm = params.empty() ? algo : Botan::fmt("{} {}", algo, params); + + auto pk_parse = config.make_timer(nm, 1, "public key parse"); + const auto pk_bytes = pk->subject_public_key(); + pk_parse->run_until_elapsed(runtime, [&]() { Botan::X509::load_key(pk_bytes); }); + config.record_result(*pk_parse); + + auto sk_parse = config.make_timer(nm, 1, "private key parse"); + const auto sk_bytes = sk->private_key_info(); + sk_parse->run_until_elapsed(runtime, [&]() { Botan::PKCS8::load_key(sk_bytes); }); + config.record_result(*sk_parse); + } + } +}; + +BOTAN_REGISTER_PERF_TEST("key_parsing", PerfTest_PKKeyParsing); + +#endif + #if defined(BOTAN_HAS_RSA) class PerfTest_RSAKeyGen final : public PerfTest { @@ -77,7 +132,7 @@ const uint8_t v = key.recovery_param(message, r, s); recovery_timer->run([&]() { - Botan::ECDSA_PublicKey recovered_key(group, message, r, s, v); + const Botan::ECDSA_PublicKey recovered_key(group, message, r, s, v); BOTAN_ASSERT(recovered_key.public_key_bits() == key.public_key_bits(), "Recovered public key correctly"); }); @@ -92,4 +147,6 @@ #endif +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/perf_pk_sig.cpp botan3-3.12.0+dfsg/src/cli/perf_pk_sig.cpp --- botan3-3.7.1+dfsg/src/cli/perf_pk_sig.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/perf_pk_sig.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,10 +9,14 @@ #if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) #include #include + #include + #include #endif namespace Botan_CLI { +namespace { + #if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) class PerfTest_PKSig : public PerfTest { @@ -21,10 +25,7 @@ virtual std::string hash() const { return "SHA-256"; } - virtual std::vector keygen_params(const PerfConfig& config) const { - BOTAN_UNUSED(config); - return {""}; - } + virtual std::vector keygen_params(const PerfConfig& /*config*/) const { return {""}; } void go(const PerfConfig& config) override { const std::string alg = this->algo(); @@ -38,12 +39,12 @@ } } - void bench_pk_sig(const PerfConfig& config, - const std::string& nm, - const std::string& alg, - const std::string& param, - const std::string& padding, - const std::string& provider = "") { + static void bench_pk_sig(const PerfConfig& config, + const std::string& nm, + const std::string& alg, + const std::string& param, + const std::string& padding, + const std::string& provider = "") { auto& rng = config.rng(); const auto msec = config.runtime(); @@ -60,7 +61,9 @@ auto pk = sk->public_key(); - std::vector message, signature, bad_signature; + std::vector message; + std::vector signature; + std::vector bad_signature; Botan::PK_Signer sig(*sk, rng, padding, Botan::Signature_Format::Standard, provider); Botan::PK_Verifier ver(*pk, padding, Botan::Signature_Format::Standard, provider); @@ -117,8 +120,7 @@ public: std::string algo() const override { return "DSA"; } - std::vector keygen_params(const PerfConfig& config) const override { - BOTAN_UNUSED(config); + std::vector keygen_params(const PerfConfig& /*config*/) const override { return {"dsa/jce/1024", "dsa/botan/2048", "dsa/botan/3072"}; } @@ -139,8 +141,7 @@ std::string hash() const override { return "PKCS1v15(SHA-256)"; } - std::vector keygen_params(const PerfConfig& config) const override { - BOTAN_UNUSED(config); + std::vector keygen_params(const PerfConfig& /*config*/) const override { return {"1024", "2048", "3072", "4096"}; } }; @@ -196,10 +197,7 @@ std::string hash() const override { return "GOST-34.11"; } - std::vector keygen_params(const PerfConfig& config) const override { - BOTAN_UNUSED(config); - return {"gost_256A"}; - } + std::vector keygen_params(const PerfConfig& /*config*/) const override { return {"gost_256A"}; } }; BOTAN_REGISTER_PERF_TEST("GOST-34.10", PerfTest_Gost3410); @@ -214,10 +212,7 @@ std::string hash() const override { return "SM3"; } - std::vector keygen_params(const PerfConfig& config) const override { - BOTAN_UNUSED(config); - return {"sm2p256v1"}; - } + std::vector keygen_params(const PerfConfig& /*config*/) const override { return {"sm2p256v1"}; } }; BOTAN_REGISTER_PERF_TEST("SM2", PerfTest_SM2); @@ -258,9 +253,7 @@ std::string hash() const override { return ""; } - std::vector keygen_params(const PerfConfig& config) const override { - BOTAN_UNUSED(config); - + std::vector keygen_params(const PerfConfig& /*config*/) const override { /* We only test H10 signatures here since already they are quite slow (a few seconds per signature). On a fast machine, H16 signatures take 1-2 @@ -287,9 +280,7 @@ std::string hash() const override { return ""; } - std::vector keygen_params(const PerfConfig& config) const override { - BOTAN_UNUSED(config); - + std::vector keygen_params(const PerfConfig& /*config*/) const override { // At first we compare instances with multiple hash functions. LMS trees with // height 10 are suitable, since they can be used for enough signatures and are // fast enough for speed testing. @@ -319,9 +310,7 @@ return alg + param.substr(11); } - std::vector keygen_params(const PerfConfig& config) const override { - BOTAN_UNUSED(config); - + std::vector keygen_params(const PerfConfig& /*config*/) const override { return {"SphincsPlus-sha2-128s-r3.1", "SphincsPlus-sha2-128f-r3.1", "SphincsPlus-sha2-192s-r3.1", @@ -349,9 +338,7 @@ std::string hash() const override { return ""; } - std::vector keygen_params(const PerfConfig& config) const override { - BOTAN_UNUSED(config); - + std::vector keygen_params(const PerfConfig& /*config*/) const override { return {"SLH-DSA-SHA2-128s", "SLH-DSA-SHA2-128f", "SLH-DSA-SHA2-192s", @@ -379,9 +366,7 @@ std::string hash() const override { return ""; } - std::vector keygen_params(const PerfConfig& config) const override { - BOTAN_UNUSED(config); - + std::vector keygen_params(const PerfConfig& /*config*/) const override { return { "Dilithium-4x4-r3", "Dilithium-4x4-AES-r3", @@ -405,9 +390,7 @@ std::string hash() const override { return ""; } - std::vector keygen_params(const PerfConfig& config) const override { - BOTAN_UNUSED(config); - + std::vector keygen_params(const PerfConfig& /*config*/) const override { return { "ML-DSA-4x4", "ML-DSA-6x5", @@ -420,4 +403,6 @@ #endif +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/perf_pwdhash.cpp botan3-3.12.0+dfsg/src/cli/perf_pwdhash.cpp --- botan3-3.7.1+dfsg/src/cli/perf_pwdhash.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/perf_pwdhash.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,8 +6,13 @@ #include "perf.h" +#include +#include + #if defined(BOTAN_HAS_PASSWORD_HASHING) #include + #include + #include #endif #if defined(BOTAN_HAS_BCRYPT) @@ -20,6 +25,8 @@ namespace Botan_CLI { +namespace { + #if defined(BOTAN_HAS_BCRYPT) class PerfTest_Bcrypt final : public PerfTest { @@ -49,7 +56,7 @@ const std::string password = "not a very good password"; for(uint8_t alg = 0; alg <= 4; ++alg) { - if(Botan::is_passhash9_alg_supported(alg) == false) { + if(!Botan::is_passhash9_alg_supported(alg)) { continue; } @@ -76,9 +83,9 @@ void go(const PerfConfig& config) override { auto pwdhash_fam = Botan::PasswordHashFamily::create_or_throw("Scrypt"); - for(size_t N : {8192, 16384, 32768, 65536}) { - for(size_t r : {1, 8, 16}) { - for(size_t p : {1}) { + for(const size_t N : {8192, 16384, 32768, 65536}) { + for(const size_t r : {1, 8, 16}) { + for(const size_t p : {1}) { auto pwdhash = pwdhash_fam->from_params(N, r, p); const size_t mem_usage = pwdhash->total_memory_usage() / (1024 * 1024); @@ -112,6 +119,45 @@ #endif +#if defined(BOTAN_HAS_PBKDF2) && defined(BOTAN_HAS_PASSWORD_HASHING) + +class PerfTest_PBKDF2 final : public PerfTest { + public: + void go(const PerfConfig& config) override { + const std::string hash = "SHA-256"; + auto pwdhash_fam = Botan::PasswordHashFamily::create(Botan::fmt("PBKDF2({})", hash)); + + if(pwdhash_fam != nullptr) { + for(const size_t iter : {10000, 100000}) { + auto pwdhash = pwdhash_fam->from_params(iter); + + auto pbkdf2_timer = config.make_timer(Botan::fmt("PBKDF2({},{})", hash, iter)); + + std::array salt{}; + config.rng().randomize(salt); + + const std::string password = "password"; + auto runtime = config.runtime(); + + std::array out{}; + + while(pbkdf2_timer->under(runtime)) { + pbkdf2_timer->run([&] { + pwdhash->hash(out, password, salt); + std::memcpy(salt.data(), out.data(), 8); + }); + } + + config.record_result(*pbkdf2_timer); + } + } + } +}; + +BOTAN_REGISTER_PERF_TEST("pbkdf2", PerfTest_PBKDF2); + +#endif + #if defined(BOTAN_HAS_ARGON2) class PerfTest_Argon2 final : public PerfTest { @@ -121,9 +167,9 @@ const auto msec = config.runtime(); - for(size_t M : {8 * 1024, 64 * 1024, 256 * 1024}) { - for(size_t t : {1, 4}) { - for(size_t p : {1, 4}) { + for(const size_t M : {8 * 1024, 64 * 1024, 256 * 1024}) { + for(const size_t t : {1, 4}) { + for(const size_t p : {1, 4}) { auto pwhash = pwhash_fam->from_params(M, t, p); auto timer = config.make_timer(pwhash->to_string()); @@ -146,4 +192,6 @@ #endif +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/perf_rng.cpp botan3-3.12.0+dfsg/src/cli/perf_rng.cpp --- botan3-3.7.1+dfsg/src/cli/perf_rng.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/perf_rng.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,6 +6,7 @@ #include "perf.h" +#include #include #if defined(BOTAN_HAS_COMPRESSION) @@ -34,11 +35,13 @@ namespace Botan_CLI { +namespace { + class PerfTest_Rng final : public PerfTest { public: void go(const PerfConfig& config) override { #if defined(BOTAN_HAS_HMAC_DRBG) - for(std::string hash : {"SHA-256", "SHA-384", "SHA-512"}) { + for(const std::string hash : {"SHA-256", "SHA-384", "SHA-512"}) { Botan::HMAC_DRBG hmac_drbg(hash); bench_rng(config, hmac_drbg, hmac_drbg.name()); } @@ -95,4 +98,6 @@ BOTAN_REGISTER_PERF_TEST("RNG", PerfTest_Rng); +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/perf_sym.cpp botan3-3.12.0+dfsg/src/cli/perf_sym.cpp --- botan3-3.7.1+dfsg/src/cli/perf_sym.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/perf_sym.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -5,8 +5,13 @@ */ #include "perf.h" + +#include #include +#include +#include + #if defined(BOTAN_HAS_BLOCK_CIPHER) #include #endif @@ -33,15 +38,17 @@ namespace Botan_CLI { +namespace { + #if defined(BOTAN_HAS_BLOCK_CIPHER) class PerfTest_BlockCipher final : public PerfTest { public: - PerfTest_BlockCipher(std::string_view alg) : m_alg(alg) {} + explicit PerfTest_BlockCipher(std::string_view alg) : m_alg(alg) {} void go(const PerfConfig& config) override { for(const auto& provider : Botan::BlockCipher::providers(m_alg)) { if(auto cipher = Botan::BlockCipher::create(m_alg, provider)) { - bench_stream_cipher(config, *cipher); + bench_block_cipher(config, *cipher); } } } @@ -49,7 +56,7 @@ static bool has_impl_for(std::string_view alg) { return !Botan::BlockCipher::providers(alg).empty(); } private: - void bench_stream_cipher(const PerfConfig& config, Botan::BlockCipher& cipher) { + static void bench_block_cipher(const PerfConfig& config, Botan::BlockCipher& cipher) { auto& rng = config.rng(); const auto runtime = config.runtime(); const auto provider = cipher.provider(); @@ -61,7 +68,7 @@ const size_t bs = cipher.block_size(); std::set buf_sizes_in_blocks; - for(size_t buf_size : config.buffer_sizes()) { + for(const size_t buf_size : config.buffer_sizes()) { if(buf_size % bs == 0) { buf_sizes_in_blocks.insert(buf_size); } else { @@ -69,7 +76,7 @@ } } - for(size_t buf_size : buf_sizes_in_blocks) { + for(const size_t buf_size : buf_sizes_in_blocks) { std::vector buffer(buf_size); const size_t mult = std::max(1, 65536 / buf_size); const size_t blocks = buf_size / bs; @@ -79,14 +86,14 @@ encrypt_timer->run_until_elapsed(runtime, [&]() { for(size_t i = 0; i != mult; ++i) { - cipher.encrypt_n(&buffer[0], &buffer[0], blocks); + cipher.encrypt_n(buffer.data(), buffer.data(), blocks); } }); config.record_result(*encrypt_timer); decrypt_timer->run_until_elapsed(runtime, [&]() { for(size_t i = 0; i != mult; ++i) { - cipher.decrypt_n(&buffer[0], &buffer[0], blocks); + cipher.decrypt_n(buffer.data(), buffer.data(), blocks); } }); config.record_result(*decrypt_timer); @@ -100,7 +107,7 @@ #if defined(BOTAN_HAS_CIPHER_MODES) class PerfTest_CipherMode final : public PerfTest { public: - PerfTest_CipherMode(std::string_view alg) : m_alg(alg) {} + explicit PerfTest_CipherMode(std::string_view alg) : m_alg(alg) {} void go(const PerfConfig& config) override { for(const auto& provider : Botan::Cipher_Mode::providers(m_alg)) { @@ -114,7 +121,7 @@ static bool has_impl_for(std::string_view alg) { return !Botan::Cipher_Mode::providers(alg).empty(); } private: - void bench_cipher_mode(const PerfConfig& config, Botan::Cipher_Mode& enc, Botan::Cipher_Mode& dec) { + static void bench_cipher_mode(const PerfConfig& config, Botan::Cipher_Mode& enc, Botan::Cipher_Mode& dec) { auto& rng = config.rng(); const auto runtime = config.runtime(); const auto provider = enc.provider(); @@ -146,17 +153,23 @@ } }); + Botan::secure_vector dbuffer; + + size_t iter = 0; + while(decrypt_timer->under(runtime)) { - if(!iv.empty()) { - iv[iv.size() - 1] += 1; - } + if(iter == 0 || iter % 128 == 0) { + if(!iv.empty()) { + iv[iv.size() - 1] += 1; + } - // Create a valid ciphertext/tag for decryption to run on - buffer.resize(buf_size); - enc.start(iv); - enc.finish(buffer); + // Create a valid ciphertext/tag for decryption to run on + buffer.resize(buf_size); + enc.start(iv); + enc.finish(buffer); + } - Botan::secure_vector dbuffer; + ++iter; decrypt_timer->run([&]() { for(size_t i = 0; i != mult; ++i) { @@ -180,7 +193,7 @@ #if defined(BOTAN_HAS_STREAM_CIPHER) class PerfTest_StreamCipher final : public PerfTest { public: - PerfTest_StreamCipher(std::string_view alg) : m_alg(alg) {} + explicit PerfTest_StreamCipher(std::string_view alg) : m_alg(alg) {} void go(const PerfConfig& config) override { for(const auto& provider : Botan::StreamCipher::providers(m_alg)) { @@ -193,7 +206,7 @@ static bool has_impl_for(std::string_view alg) { return !Botan::StreamCipher::providers(alg).empty(); } private: - void bench_stream_cipher(const PerfConfig& config, Botan::StreamCipher& cipher) { + static void bench_stream_cipher(const PerfConfig& config, Botan::StreamCipher& cipher) { auto& rng = config.rng(); const auto runtime = config.runtime(); const auto provider = cipher.provider(); @@ -243,7 +256,7 @@ #if defined(BOTAN_HAS_HASH) class PerfTest_HashFunction final : public PerfTest { public: - PerfTest_HashFunction(std::string_view alg) : m_alg(alg) {} + explicit PerfTest_HashFunction(std::string_view alg) : m_alg(alg) {} void go(const PerfConfig& config) override { for(const auto& provider : Botan::HashFunction::providers(m_alg)) { @@ -256,7 +269,7 @@ static bool has_impl_for(std::string_view alg) { return !Botan::HashFunction::providers(alg).empty(); } private: - void bench_hash_fn(const PerfConfig& config, Botan::HashFunction& hash) { + static void bench_hash_fn(const PerfConfig& config, Botan::HashFunction& hash) { std::vector output(hash.output_length()); const auto provider = hash.provider(); const auto runtime = config.runtime(); @@ -284,7 +297,7 @@ #if defined(BOTAN_HAS_MAC) class PerfTest_MessageAuthenticationCode final : public PerfTest { public: - PerfTest_MessageAuthenticationCode(std::string_view alg) : m_alg(alg) {} + explicit PerfTest_MessageAuthenticationCode(std::string_view alg) : m_alg(alg) {} void go(const PerfConfig& config) override { for(const auto& provider : Botan::MessageAuthenticationCode::providers(m_alg)) { @@ -299,7 +312,7 @@ } private: - void bench_mac_fn(const PerfConfig& config, Botan::MessageAuthenticationCode& mac) { + static void bench_mac_fn(const PerfConfig& config, Botan::MessageAuthenticationCode& mac) { std::vector output(mac.output_length()); const auto provider = mac.provider(); const auto runtime = config.runtime(); @@ -335,7 +348,7 @@ #if defined(BOTAN_HAS_XOF) class PerfTest_XOF final : public PerfTest { public: - PerfTest_XOF(std::string_view alg) : m_alg(alg) {} + explicit PerfTest_XOF(std::string_view alg) : m_alg(alg) {} void go(const PerfConfig& config) override { for(const auto& provider : Botan::XOF::providers(m_alg)) { @@ -348,11 +361,11 @@ static bool has_impl_for(std::string_view alg) { return !Botan::XOF::providers(alg).empty(); } private: - void bench_xof_fn(const PerfConfig& config, Botan::XOF& xof) { + static void bench_xof_fn(const PerfConfig& config, Botan::XOF& xof) { const auto runtime = config.runtime(); const auto provider = xof.provider(); - for(size_t buf_size : config.buffer_sizes()) { + for(const size_t buf_size : config.buffer_sizes()) { auto in = config.rng().random_vec(buf_size); Botan::secure_vector out(buf_size); @@ -364,6 +377,9 @@ config.record_result(*in_timer); config.record_result(*out_timer); + + // Our XOFs don't want to consume inputs after producing output, so reset the state + xof.clear(); } } @@ -371,6 +387,8 @@ }; #endif +} // namespace + //static std::unique_ptr PerfTest::get_sym(const std::string& alg) { #if defined(BOTAN_HAS_XOF) diff -Nru botan3-3.7.1+dfsg/src/cli/perf_x509.cpp botan3-3.12.0+dfsg/src/cli/perf_x509.cpp --- botan3-3.7.1+dfsg/src/cli/perf_x509.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/perf_x509.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,157 @@ +/* +* (C) 2025 Jack Lloyd +* 2025 René Meusel - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "perf.h" +#include + +// Always available: +#include + +#if defined(BOTAN_HAS_X509) + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif + +namespace Botan_CLI { + +namespace { + +#if defined(BOTAN_HAS_X509) && defined(BOTAN_HAS_ML_DSA) + +class PerfTest_ASN1_Parsing final : public PerfTest { + private: + struct CA { + std::unique_ptr root_key; + Botan::X509_CA ca; + }; + + private: + static std::string_view get_hash_function() { return "SHAKE-256(512)"; } + + static std::unique_ptr create_private_key(Botan::RandomNumberGenerator& rng) { + return Botan::create_private_key("ML-DSA", rng, "ML-DSA-6x5"); + } + + static CA create_ca(Botan::RandomNumberGenerator& rng) { + auto root_cert_options = Botan::X509_Cert_Options("Benchmark Root/DE/RS/CS"); + root_cert_options.dns = "unobtainium.example.com"; + root_cert_options.email = "idont@exist.com"; + root_cert_options.is_CA = true; + + auto root_key = create_private_key(rng); + BOTAN_ASSERT_NONNULL(root_key); + auto root_cert = Botan::X509::create_self_signed_cert(root_cert_options, *root_key, get_hash_function(), rng); + auto ca = Botan::X509_CA(root_cert, *root_key, get_hash_function(), rng); + + return CA{ + std::move(root_key), + std::move(ca), + }; + } + + static Botan::X509_Certificate make_certificate(std::string_view common_name, + CA& ca, + Botan::RandomNumberGenerator& rng) { + Botan::X509_DN subject; + subject.add_attribute("X520.CommonName", common_name); + subject.add_attribute("X520.Country", "DE"); + subject.add_attribute("X520.State", "Berlin"); + subject.add_attribute("X520.Organization", "RS"); + subject.add_attribute("X520.OrganizationalUnit", "CS"); + + Botan::AlternativeName an; + an.add_dns("gibtsnicht.example.com"); + an.add_email("not.available@anywhere.com"); + + Botan::Extensions exts; + exts.add(std::make_unique(an)); + + const auto cert_key = create_private_key(rng); + BOTAN_ASSERT_NONNULL(cert_key); + const auto cert_req = Botan::PKCS10_Request::create(*cert_key, subject, exts, get_hash_function(), rng); + + const auto now = std::chrono::system_clock::now(); + using namespace std::chrono_literals; + return ca.ca.sign_request(cert_req, rng, Botan::X509_Time(now), Botan::X509_Time(now + 24h * 365)); + } + + static Botan::X509_CRL make_revocation_list(size_t entries, CA& ca, Botan::RandomNumberGenerator& rng) { + const auto empty_crl = ca.ca.new_crl(rng); + + std::vector crl_entries(entries); + std::generate(crl_entries.begin(), crl_entries.end(), [&] { + std::vector crl_entry_buffer; + + // Generating the CRL entries through their ASN.1 structure because + // our public API does not allow creating them without the actual + // certificate that is supposed to be revoked. + Botan::Extensions exts; + exts.add(std::make_unique(Botan::CRL_Code::KeyCompromise)); + Botan::DER_Encoder(crl_entry_buffer) + .start_sequence() + .encode(Botan::BigInt::from_bytes(rng.random_array<16>())) + .encode(Botan::X509_Time(std::chrono::system_clock::now())) + .start_sequence() + .encode(exts) + .end_cons() + .end_cons(); + + Botan::BER_Decoder ber(crl_entry_buffer, Botan::BER_Decoder::Limits::DER()); + + Botan::CRL_Entry entry; + entry.decode_from(ber); + return entry; + }); + + return ca.ca.update_crl(empty_crl, crl_entries, rng); + } + + public: + void go(const PerfConfig& config) override { + auto ca = create_ca(config.rng()); + auto cert = make_certificate("Test Certificate", ca, config.rng()); + auto crl = make_revocation_list(500, ca, config.rng()); + + const auto cert_encoded = cert.BER_encode(); + const auto crl_encoded = crl.BER_encode(); + + auto cert_timer = config.make_timer("X509 Certificate Parsing"); + auto crl_timer = config.make_timer("X509 CRL Parsing"); + + const auto runtime = config.runtime(); + + while(cert_timer->under(runtime)) { + cert_timer->start(); + std::ignore = Botan::X509_Certificate(cert_encoded); + cert_timer->stop(); + } + + while(crl_timer->under(runtime)) { + crl_timer->start(); + std::ignore = Botan::X509_CRL(crl_encoded); + crl_timer->stop(); + } + + config.record_result(*cert_timer); + config.record_result(*crl_timer); + } +}; + +BOTAN_REGISTER_PERF_TEST("asn1_parsing", PerfTest_ASN1_Parsing); + +#endif + +} // namespace + +} // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/pk_crypt.cpp botan3-3.12.0+dfsg/src/cli/pk_crypt.cpp --- botan3-3.7.1+dfsg/src/cli/pk_crypt.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/pk_crypt.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -61,7 +61,7 @@ const Botan::AlgorithmIdentifier hash_id(OAEP_HASH, Botan::AlgorithmIdentifier::USE_EMPTY_PARAM); const Botan::AlgorithmIdentifier pk_alg_id("RSA/OAEP", hash_id.BER_encode()); - Botan::PK_Encryptor_EME enc(*key, rng(), "OAEP(" + OAEP_HASH + ")"); + const Botan::PK_Encryptor_EME enc(*key, rng(), "OAEP(" + OAEP_HASH + ")"); const Botan::secure_vector file_key = rng().random_vec(aead->key_spec().maximum_keylength()); @@ -121,7 +121,8 @@ try { Botan::DataSource_Stream input(get_arg("datafile")); - Botan::BER_Decoder(Botan::PEM_Code::decode_check_label(input, "PUBKEY ENCRYPTED MESSAGE")) + Botan::BER_Decoder(Botan::PEM_Code::decode_check_label(input, "PUBKEY ENCRYPTED MESSAGE"), + Botan::BER_Decoder::Limits::DER()) .start_sequence() .decode(pk_alg_id) .decode(encrypted_key, Botan::ASN1_Type::OctetString) @@ -146,7 +147,7 @@ } Botan::AlgorithmIdentifier oaep_hash_id; - Botan::BER_Decoder(pk_alg_id.parameters()).decode(oaep_hash_id); + Botan::BER_Decoder(pk_alg_id.parameters(), Botan::BER_Decoder::Limits::DER()).decode(oaep_hash_id); const std::string oaep_hash = oaep_hash_id.oid().human_name_or_empty(); @@ -155,7 +156,7 @@ return set_return_code(1); } - if(oaep_hash_id.parameters().empty() == false) { + if(!oaep_hash_id.parameters().empty()) { error_output() << "Unknown OAEP parameters used\n"; return set_return_code(1); } @@ -165,7 +166,7 @@ const size_t expected_keylen = aead->key_spec().maximum_keylength(); - Botan::PK_Decryptor_EME dec(*key, rng(), "OAEP(" + oaep_hash + ")"); + const Botan::PK_Decryptor_EME dec(*key, rng(), "OAEP(" + oaep_hash + ")"); const Botan::secure_vector file_key = dec.decrypt_or_random(encrypted_key.data(), encrypted_key.size(), expected_keylen, rng()); diff -Nru botan3-3.7.1+dfsg/src/cli/psk.cpp botan3-3.12.0+dfsg/src/cli/psk.cpp --- botan3-3.7.1+dfsg/src/cli/psk.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/psk.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -16,8 +16,6 @@ class PSK_Tool_Base : public Command { public: - PSK_Tool_Base(const std::string& spec) : Command(spec) {} - std::string group() const override { return "psk"; } void go() override { @@ -25,12 +23,15 @@ const Botan::secure_vector db_key = Botan::hex_decode_locked(get_passphrase_arg("Database key", "db_key")); - std::shared_ptr db = std::make_shared(db_filename); + const std::shared_ptr db = std::make_shared(db_filename); Botan::Encrypted_PSK_Database_SQL psk(db_key, db, "psk"); psk_operation(psk); } + protected: + explicit PSK_Tool_Base(const std::string& spec) : Command(spec) {} + private: virtual void psk_operation(Botan::PSK_Database& db) = 0; }; diff -Nru botan3-3.7.1+dfsg/src/cli/pubkey.cpp botan3-3.12.0+dfsg/src/cli/pubkey.cpp --- botan3-3.7.1+dfsg/src/cli/pubkey.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/pubkey.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,18 +11,15 @@ #if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) #include - #include - #include - #include #include + #include #include #include #include #include #include #include - #include #include @@ -36,11 +33,13 @@ namespace Botan_CLI { +namespace { + class PK_Keygen final : public Command { public: PK_Keygen() : Command( - "keygen --algo=RSA --params= --passphrase= --cipher= --pbkdf= --pbkdf-ms=300 --pbkdf-iter= --provider= --der-out") { + "keygen --algo=RSA --params= --passphrase= --cipher= --pbkdf= --pbkdf-ms=300 --pbkdf-iter= --provider= --rng-type= --drbg-seed= --der-out") { } std::string group() const override { return "pubkey"; } @@ -52,7 +51,7 @@ const std::string params = get_arg("params"); const std::string provider = get_arg("provider"); - std::unique_ptr key = Botan::create_private_key(algo, rng(), params, provider); + const std::unique_ptr key = Botan::create_private_key(algo, rng(), params, provider); if(!key) { throw CLI_Error_Unsupported("keygen", algo); @@ -178,7 +177,10 @@ class PK_Sign final : public Command { public: - PK_Sign() : Command("sign --der-format --passphrase= --hash=SHA-256 --padding= --provider= key file") {} + PK_Sign() : + Command( + "sign --der-format --passphrase= --hash=SHA-256 --padding= --provider= --rng-type= --drbg-seed= key file") { + } std::string group() const override { return "pubkey"; } @@ -284,7 +286,7 @@ public: PKCS8_Tool() : Command( - "pkcs8 --pass-in= --pub-out --der-out --pass-out= --cipher= --pbkdf= --pbkdf-ms=300 --pbkdf-iter= key") { + "pkcs8 --pass-in= --pub-out --der-out --pass-out= --cipher= --pbkdf= --pbkdf-ms=300 --pbkdf-iter= --rng-type= --drbg-seed= key") { } std::string group() const override { return "pubkey"; } @@ -366,7 +368,7 @@ const auto ec_group = Botan::EC_Group::from_name(get_arg("name")); if(flag_set("pem")) { - output() << ec_group.PEM_encode(); + output() << ec_group.PEM_encode(Botan::EC_Group_Encoding::NamedCurve); } else { output() << "P = " << std::hex << ec_group.get_p() << "\n" << "A = " << std::hex << ec_group.get_a() << "\n" @@ -435,7 +437,8 @@ class Gen_DL_Group final : public Command { public: - Gen_DL_Group() : Command("gen_dl_group --pbits=2048 --qbits=0 --seed= --type=subgroup") {} + Gen_DL_Group() : + Command("gen_dl_group --pbits=2048 --qbits=0 --seed= --type=subgroup --rng-type= --drbg-seed=") {} std::string group() const override { return "pubkey"; } @@ -452,13 +455,13 @@ if(!seed_str.empty()) { throw CLI_Usage_Error("Seed only supported for DSA param gen"); } - Botan::DL_Group grp(rng(), Botan::DL_Group::Strong, pbits); + const Botan::DL_Group grp(rng(), Botan::DL_Group::Strong, pbits); output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_42); } else if(type == "subgroup") { if(!seed_str.empty()) { throw CLI_Usage_Error("Seed only supported for DSA param gen"); } - Botan::DL_Group grp(rng(), Botan::DL_Group::Prime_Subgroup, pbits, qbits); + const Botan::DL_Group grp(rng(), Botan::DL_Group::Prime_Subgroup, pbits, qbits); output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_42); } else if(type == "dsa") { size_t dsa_qbits = qbits; @@ -473,11 +476,11 @@ } if(seed_str.empty()) { - Botan::DL_Group grp(rng(), Botan::DL_Group::DSA_Kosherizer, pbits, dsa_qbits); + const Botan::DL_Group grp(rng(), Botan::DL_Group::DSA_Kosherizer, pbits, dsa_qbits); output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_57); } else { const std::vector seed = Botan::hex_decode(seed_str); - Botan::DL_Group grp(rng(), seed, pbits, dsa_qbits); + const Botan::DL_Group grp(rng(), seed, pbits, dsa_qbits); output() << grp.PEM_encode(Botan::DL_Group_Format::ANSI_X9_57); } @@ -491,6 +494,8 @@ #endif +} // namespace + } // namespace Botan_CLI #endif diff -Nru botan3-3.7.1+dfsg/src/cli/roughtime.cpp botan3-3.12.0+dfsg/src/cli/roughtime.cpp --- botan3-3.7.1+dfsg/src/cli/roughtime.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/roughtime.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,9 +11,6 @@ #include #include - #include - #include - #include #include #include @@ -22,6 +19,8 @@ namespace Botan_CLI { +namespace { + class RoughtimeCheck final : public Command { public: RoughtimeCheck() : Command("roughtime_check --raw-time chain-file") {} @@ -69,7 +68,7 @@ Google-Sandbox-Roughtime ed25519 etPaaIxcBMY1oUeGpwvPMCJMwlRVNxv51KK/tktoJTQ= udp roughtime.sandbox.google.com:2002 --chain-file= - Succesfull queries are appended to this file. + Successful queries are appended to this file. If limit of --max-chain-size records is reached, the oldest records are truncated. This queries records can be replayed using command roughtime_check . @@ -86,8 +85,8 @@ const size_t max_chain_size, const std::string& address, const Botan::Ed25519_PublicKey& public_key) { - Botan::Roughtime::Nonce nonce; - Botan::Roughtime::Nonce blind; + Botan::Roughtime::Nonce nonce{}; + Botan::Roughtime::Nonce blind{}; if(chain) { blind = Botan::Roughtime::Nonce(rng()); nonce = chain->next_nonce(blind); @@ -110,7 +109,7 @@ return; } const auto tolerance = get_arg_sz("check-local-clock"); - if(tolerance) { + if(tolerance > 0) { const auto now = std::chrono::system_clock::now(); const auto diff_abs = now >= response.utc_midpoint() ? now - response.utc_midpoint() : response.utc_midpoint() - now; @@ -181,6 +180,8 @@ BOTAN_REGISTER_COMMAND("roughtime", Roughtime); +} // namespace + } // namespace Botan_CLI #endif diff -Nru botan3-3.7.1+dfsg/src/cli/sandbox.cpp botan3-3.12.0+dfsg/src/cli/sandbox.cpp --- botan3-3.7.1+dfsg/src/cli/sandbox.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/sandbox.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -5,14 +5,15 @@ */ #include "sandbox.h" + #include +#include -#if defined(BOTAN_TARGET_OS_HAS_PLEDGE) - #include -#elif defined(BOTAN_TARGET_OS_HAS_CAP_ENTER) +#if defined(BOTAN_TARGET_OS_HAS_CAP_ENTER) #include #include #elif defined(BOTAN_TARGET_OS_HAS_SETPPRIV) + #include #include #elif defined(BOTAN_TARGET_OS_HAS_SANDBOX_PROC) #include @@ -29,27 +30,28 @@ }; #endif -Sandbox::Sandbox() { -#if defined(BOTAN_TARGET_OS_HAS_PLEDGE) - m_name = "pledge"; -#elif defined(BOTAN_TARGET_OS_HAS_CAP_ENTER) - m_name = "capsicum"; +namespace { + +std::string sandbox_impl_name() { +#if defined(BOTAN_TARGET_OS_HAS_CAP_ENTER) + return "capsicum"; #elif defined(BOTAN_TARGET_OS_HAS_SETPPRIV) - m_name = "privilege"; + return "privilege"; #elif defined(BOTAN_TARGET_OS_HAS_SANDBOX_PROC) - m_name = "sandbox"; + return "sandbox"; #else - m_name = ""; + return ""; #endif } +} // namespace + +Sandbox::Sandbox() : m_name(sandbox_impl_name()) {} + bool Sandbox::init() { Botan::initialize_allocator(); -#if defined(BOTAN_TARGET_OS_HAS_PLEDGE) - const static char* opts = "stdio rpath inet error"; - return (::pledge(opts, nullptr) == 0); -#elif defined(BOTAN_TARGET_OS_HAS_CAP_ENTER) +#if defined(BOTAN_TARGET_OS_HAS_CAP_ENTER) cap_rights_t wt, rd; if(::cap_rights_init(&wt, CAP_READ, CAP_WRITE) == nullptr) { diff -Nru botan3-3.7.1+dfsg/src/cli/sandbox.h botan3-3.12.0+dfsg/src/cli/sandbox.h --- botan3-3.7.1+dfsg/src/cli/sandbox.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/sandbox.h 2026-05-07 01:38:28.000000000 +0000 @@ -16,6 +16,11 @@ explicit Sandbox(); virtual ~Sandbox(); + Sandbox(const Sandbox& other) = delete; + Sandbox(Sandbox&& other) = delete; + Sandbox& operator=(const Sandbox& other) = delete; + Sandbox& operator=(Sandbox&& other) = delete; + static bool init(); const std::string& name() const { return m_name; } diff -Nru botan3-3.7.1+dfsg/src/cli/socket_utils.h botan3-3.12.0+dfsg/src/cli/socket_utils.h --- botan3-3.7.1+dfsg/src/cli/socket_utils.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/socket_utils.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,7 @@ #include "cli_exceptions.h" #include +#include #include #if defined(BOTAN_TARGET_OS_HAS_WINSOCK2) @@ -51,8 +52,16 @@ } inline std::string err_to_string(int e) { - // TODO use strerror_s here - return "Error code " + std::to_string(e); + /* + * MS documentation specifies 94 character max for user messages. + * strerror_s truncates to buffer size - 1 and guarantees null termination. + * Using 100 bytes yo ensure sufficient space with safety margin. + * https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/strerror-s-strerror-s-wcserror-s-wcserror-s + */ + std::array buf{}; + const auto res = strerror_s(buf.data(), buf.size() - 1, e); + const std::string_view msg = (res == 0) ? buf.data() : "failed to map error with strerror_s()"; + return Botan::fmt("Error: {} - {}", e, msg); } inline int close(int fd) { diff -Nru botan3-3.7.1+dfsg/src/cli/speed.cpp botan3-3.12.0+dfsg/src/cli/speed.cpp --- botan3-3.7.1+dfsg/src/cli/speed.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/speed.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,7 +9,6 @@ #include "perf.h" #include -#include #include #include #include @@ -17,9 +16,11 @@ // Always available: #include -#include -#include -#include +#include + +#if defined(BOTAN_HAS_CPUID) + #include +#endif #if defined(BOTAN_HAS_OS_UTILS) #include @@ -42,12 +43,23 @@ out << "[\n"; + out << "{" + << R"("arch": ")" << BOTAN_TARGET_ARCH << "\", " + << R"("version": ")" << Botan::short_version_cstr() << "\", "; + + if(auto vc_revision = Botan::version_vc_revision()) { + out << R"("git": ")" << *vc_revision << "\", "; + } + + out << R"("compiler": ")" << BOTAN_COMPILER_INVOCATION_STRING << "\"" + << "},\n"; + for(size_t i = 0; i != m_results.size(); ++i) { const Timer& t = m_results[i]; out << "{" - << "\"algo\": \"" << t.get_name() << "\", " - << "\"op\": \"" << t.doing() << "\", " + << R"("algo": ")" << t.get_name() << "\", " + << R"("op": ")" << t.doing() << "\", " << "\"events\": " << t.events() << ", "; if(t.cycles_consumed() > 0) { @@ -55,7 +67,7 @@ } if(t.buf_size() > 0) { - out << "\"bps\": " << static_cast(t.events() / (t.value() / 1000000000.0)) << ", "; + out << "\"bps\": " << static_cast(t.events() / (t.nanoseconds() / 1000000000.0)) << ", "; out << "\"buf_size\": " << t.buf_size() << ", "; } @@ -256,7 +268,45 @@ return oss.str(); } -} // namespace +std::vector interpret_ecc_groups(const std::string& arg) { + if(arg.empty()) { + return {"secp256r1", "secp384r1", "secp521r1", "brainpool256r1", "brainpool384r1", "brainpool512r1"}; + } + if(arg == "nist") { + return {"secp224r1", "secp256r1", "secp384r1", "secp521r1"}; + } + +#if defined(BOTAN_HAS_ECC_GROUP) + if(arg == "all") { + const auto& all = Botan::EC_Group::known_named_groups(); + return std::vector(all.begin(), all.end()); + } + + if(arg == "generic") { + std::vector groups; + for(const auto& group_name : Botan::EC_Group::known_named_groups()) { + const Botan::EC_Group group(group_name); + if(group.engine() == Botan::EC_Group_Engine::Generic) { + groups.push_back(group_name); + } + } + return groups; + } + + if(arg == "pcurves") { + std::vector groups; + for(const auto& group_name : Botan::EC_Group::known_named_groups()) { + const Botan::EC_Group group(group_name); + if(group.engine() == Botan::EC_Group_Engine::Optimized) { + groups.push_back(group_name); + } + } + return groups; + } +#endif + + return Command::split_on(arg, ','); +} class Speed final : public Command { public: @@ -303,6 +353,7 @@ "AES-128/GCM", "AES-128/XTS", "AES-128/SIV", + "Ascon-AEAD128", "Serpent/CBC", "Serpent/CTR-BE", @@ -325,6 +376,7 @@ "SHA-512", "SHA-3(256)", "SHA-3(512)", + "Ascon-Hash256", "RIPEMD-160", "Skein-512", "Blake2b", @@ -333,6 +385,7 @@ /* XOFs */ "SHAKE-128", "SHAKE-256", + "Ascon-XOF128", /* MACs */ "CMAC(AES-128)", @@ -361,8 +414,8 @@ std::string description() const override { return "Measures the speed of algorithms"; } void go() override { - std::chrono::milliseconds msec(get_arg_sz("msec")); - std::vector ecc_groups = Command::split_on(get_arg("ecc-groups"), ','); + const uint64_t milliseconds = get_arg_sz("msec"); + const std::string ecc_groups_arg = get_arg("ecc-groups"); const std::string format = get_arg("format"); const std::string clock_ratio = get_arg("cpu-clock-ratio"); @@ -410,33 +463,29 @@ throw CLI_Usage_Error("Unknown --format type '" + format + "'"); } -#if defined(BOTAN_HAS_ECC_GROUP) - if(ecc_groups.empty()) { - ecc_groups = {"secp256r1", "secp384r1", "secp521r1", "brainpool256r1", "brainpool384r1", "brainpool512r1"}; - } else if(ecc_groups.size() == 1 && ecc_groups[0] == "all") { - auto all = Botan::EC_Group::known_named_groups(); - ecc_groups.assign(all.begin(), all.end()); - } -#endif + const auto ecc_groups = interpret_ecc_groups(ecc_groups_arg); std::vector algos = get_arg_list("algos"); const std::vector buf_sizes = unique_buffer_sizes(get_arg("buf-size")); +#if defined(BOTAN_HAS_CPUID) for(const std::string& cpuid_to_clear : Command::split_on(get_arg("clear-cpuid"), ',')) { - auto bits = Botan::CPUID::bit_from_string(cpuid_to_clear); - if(bits.empty()) { + if(auto bit = Botan::CPUID::bit_from_string(cpuid_to_clear)) { + Botan::CPUID::clear_cpuid_bit(*bit); + } else { error_output() << "Warning don't know CPUID flag '" << cpuid_to_clear << "'\n"; } - - for(auto bit : bits) { - Botan::CPUID::clear_cpuid_bit(bit); - } } +#endif if(verbose() || m_summary) { +#if defined(BOTAN_HAS_CPUID) output() << Botan::version_string() << "\n" << "CPUID: " << Botan::CPUID::to_string() << "\n\n"; +#else + output() << Botan::version_string() << "\n\n"; +#endif } const bool using_defaults = (algos.empty()); @@ -444,14 +493,14 @@ algos = default_benchmark_list(); } - PerfConfig perf_config([&](const Timer& t) { this->record_result(t); }, - clock_speed, - clock_cycle_ratio, - msec, - ecc_groups, - buf_sizes, - this->error_output(), - this->rng()); + const PerfConfig perf_config([&](const Timer& t) { this->record_result(t); }, + clock_speed, + clock_cycle_ratio, + milliseconds, + ecc_groups, + buf_sizes, + this->error_output(), + this->rng()); for(const auto& algo : algos) { if(auto perf = PerfTest::get(algo)) { @@ -489,7 +538,7 @@ if(m_json) { m_json->add(t); } else { - output() << format_timer(t, m_time_unit) << std::endl; + output() << format_timer(t, m_time_unit) << "\n" << std::flush; if(m_summary) { m_summary->add(t); @@ -500,4 +549,6 @@ BOTAN_REGISTER_COMMAND("speed", Speed); +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/timer.cpp botan3-3.12.0+dfsg/src/cli/timer.cpp --- botan3-3.7.1+dfsg/src/cli/timer.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/timer.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,7 +7,6 @@ #include "timer.h" #include -#include #include #if defined(BOTAN_HAS_OS_UTILS) @@ -64,7 +63,7 @@ } void Timer::stop() { - if(m_timer_start) { + if(m_timer_start != 0) { const uint64_t now = timestamp_ns(); if(now > m_timer_start) { diff -Nru botan3-3.7.1+dfsg/src/cli/timer.h botan3-3.12.0+dfsg/src/cli/timer.h --- botan3-3.7.1+dfsg/src/cli/timer.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/timer.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,7 +8,6 @@ #define BOTAN_CLI_TIMER_H_ #include -#include #include namespace Botan_CLI { @@ -23,18 +22,18 @@ double clock_cycle_ratio, uint64_t clock_speed); - Timer(std::string_view name) : Timer(name, "", "", 1, 0, 0.0, 0) {} + explicit Timer(std::string_view name) : Timer(name, "", "", 1, 0, 0.0, 0) {} Timer(std::string_view name, size_t buf_size) : Timer(name, "", "", buf_size, buf_size, 0.0, 0) {} - Timer(const Timer& other) = default; - Timer& operator=(const Timer& other) = default; - void start(); void stop(); - bool under(std::chrono::milliseconds msec) const { return (milliseconds() < msec.count()); } + bool under(uint64_t msec) const { + const uint64_t nano = msec * 1000000; + return value() < nano; + } class Timer_Scope final { public: @@ -46,18 +45,23 @@ } catch(...) {} } + Timer_Scope(const Timer_Scope& other) = delete; + Timer_Scope(Timer_Scope&& other) = delete; + Timer_Scope& operator=(const Timer_Scope& other) = delete; + Timer_Scope& operator=(Timer_Scope&& other) = delete; + private: Timer& m_timer; }; template auto run(F f) -> decltype(f()) { - Timer_Scope timer(*this); + const Timer_Scope timer(*this); return f(); } template - void run_until_elapsed(std::chrono::milliseconds msec, F f) { + void run_until_elapsed(uint64_t msec, F f) { while(this->under(msec)) { run(f); } @@ -65,19 +69,17 @@ uint64_t value() const { return m_time_used; } - double seconds() const { return value() / 1000000000.0; } + double seconds() const { return nanoseconds() / 1000000000.0; } - double milliseconds() const { return value() / 1000000.0; } + double milliseconds() const { return nanoseconds() / 1000000.0; } - double microseconds() const { return value() / 1000.0; } + double microseconds() const { return nanoseconds() / 1000.0; } double nanoseconds() const { return static_cast(value()); } - double ms_per_event() const { return milliseconds() / events(); } - uint64_t cycles_consumed() const { if(m_clock_speed != 0) { - return static_cast((m_clock_speed * value()) / 1000.0); + return (m_clock_speed * value()) / 1000; } return m_cpu_cycles_used; } @@ -90,11 +92,23 @@ size_t buf_size() const { return m_buf_size; } - double bytes_per_second() const { return seconds() > 0.0 ? events() / seconds() : 0.0; } + double bytes_per_second() const { return events_per_second(); } - double events_per_second() const { return seconds() > 0.0 ? events() / seconds() : 0.0; } + double events_per_second() const { + if(seconds() > 0.0 && events() > 0) { + return static_cast(events()) / seconds(); + } else { + return 0.0; + } + } - double seconds_per_event() const { return events() > 0 ? seconds() / events() : 0.0; } + double seconds_per_event() const { + if(seconds() > 0.0 && events() > 0) { + return seconds() / static_cast(events()); + } else { + return 0.0; + } + } bool operator<(const Timer& other) const; diff -Nru botan3-3.7.1+dfsg/src/cli/timing_tests.cpp botan3-3.12.0+dfsg/src/cli/timing_tests.cpp --- botan3-3.7.1+dfsg/src/cli/timing_tests.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/timing_tests.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -21,12 +21,16 @@ #include #include +#include #include #include #include #include +#include #include #include +#include +#include #include #if defined(BOTAN_HAS_BIGINT) @@ -56,21 +60,44 @@ #endif #if defined(BOTAN_HAS_TLS_CBC) + #include + #include #include + #include #include #endif -#if defined(BOTAN_HAS_ECDSA) - #include +#if defined(BOTAN_HAS_SYSTEM_RNG) + #include +#endif + +#if defined(BOTAN_HAS_CHACHA_RNG) + #include +#endif + +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) + #include + #include #endif namespace Botan_CLI { namespace { -class TimingTestTimer { +void shuffle_idx(std::vector& vec, Botan::RandomNumberGenerator& rng) { + const size_t n = vec.size(); + for(size_t i = 0; i != n; ++i) { + uint8_t jb[sizeof(uint64_t)]; + rng.randomize(jb, sizeof(jb)); + const uint64_t j8 = Botan::load_le(jb, 0); + const size_t j = i + static_cast(j8) % (n - i); + std::swap(vec[i], vec[j]); + } +} + +class TimingTestTimer final { public: - TimingTestTimer() { m_start = get_high_resolution_clock(); } + TimingTestTimer() : m_start(get_high_resolution_clock()) {} uint64_t complete() const { return get_high_resolution_clock() - m_start; } @@ -84,8 +111,6 @@ uint64_t m_start; }; -} // namespace - class Timing_Test { public: Timing_Test() { @@ -93,8 +118,14 @@ A constant seed is ok here since the timing test rng just needs to be "random" but not cryptographically secure - even std::rand() would be ok. */ - const std::string drbg_seed(64, 'A'); - m_rng = cli_make_rng("", drbg_seed); // throws if it can't find anything to use + +#if defined(BOTAN_HAS_CHACHA_RNG) + m_rng = std::make_unique(std::vector(64, 0)); +#elif defined(BOTAN_HAS_SYSTEM_RNG) + m_rng = std::make_unique(); +#else + throw Botan::Not_Implemented("Missing RNG for timing_test"); +#endif } virtual ~Timing_Test() = default; @@ -116,7 +147,7 @@ Botan::RandomNumberGenerator& timing_test_rng() { return (*m_rng); } private: - std::shared_ptr m_rng; + std::unique_ptr m_rng; }; #if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EME_PKCS1) && defined(BOTAN_HAS_EME_RAW) @@ -125,8 +156,8 @@ public: explicit Bleichenbacker_Timing_Test(size_t keysize) : m_privkey(timing_test_rng(), keysize), - m_pubkey(m_privkey), - m_enc(m_pubkey, timing_test_rng(), "Raw"), + m_pubkey(m_privkey.public_key()), + m_enc(*m_pubkey, timing_test_rng(), "Raw"), m_dec(m_privkey, timing_test_rng(), "PKCS1v15") {} std::vector prepare_input(const std::string& input) override { @@ -135,7 +166,7 @@ } uint64_t measure_critical_function(const std::vector& input) override { - TimingTestTimer timer; + const TimingTestTimer timer; m_dec.decrypt_or_random(input.data(), m_ctext_length, m_expected_content_size, timing_test_rng()); return timer.complete(); } @@ -144,7 +175,7 @@ const size_t m_expected_content_size = 48; const size_t m_ctext_length = 256; Botan::RSA_PrivateKey m_privkey; - Botan::RSA_PublicKey m_pubkey; + std::unique_ptr m_pubkey; Botan::PK_Encryptor_EME m_enc; Botan::PK_Decryptor_EME m_dec; }; @@ -164,8 +195,8 @@ public: explicit Manger_Timing_Test(size_t keysize) : m_privkey(timing_test_rng(), keysize), - m_pubkey(m_privkey), - m_enc(m_pubkey, timing_test_rng(), m_encrypt_padding), + m_pubkey(m_privkey.public_key()), + m_enc(*m_pubkey, timing_test_rng(), m_encrypt_padding), m_dec(m_privkey, timing_test_rng(), m_decrypt_padding) {} std::vector prepare_input(const std::string& input) override { @@ -174,7 +205,7 @@ } uint64_t measure_critical_function(const std::vector& input) override { - TimingTestTimer timer; + const TimingTestTimer timer; try { m_dec.decrypt(input.data(), m_ctext_length); } catch(Botan::Decoding_Error&) {} @@ -186,7 +217,7 @@ const std::string m_decrypt_padding = "EME1(SHA-256)"; const size_t m_ctext_length = 256; Botan::RSA_PrivateKey m_privkey; - Botan::RSA_PublicKey m_pubkey; + std::unique_ptr m_pubkey; Botan::PK_Encryptor_EME m_enc; Botan::PK_Decryptor_EME m_dec; }; @@ -237,13 +268,13 @@ Botan::secure_vector data(input.begin(), input.end()); Botan::secure_vector aad(13); const Botan::secure_vector iv(16); - Botan::secure_vector key(16 + m_mac_keylen); + const Botan::secure_vector key(16 + m_mac_keylen); m_dec.set_key(unlock(key)); m_dec.set_associated_data(aad); m_dec.start(unlock(iv)); - TimingTestTimer timer; + const TimingTestTimer timer; try { m_dec.finish(data); } catch(Botan::TLS::TLS_Exception&) {} @@ -262,34 +293,35 @@ private: const Botan::EC_Group m_group; - const Botan::ECDSA_PrivateKey m_privkey; const Botan::EC_Scalar m_x; Botan::EC_Scalar m_b; - Botan::EC_Scalar m_b_inv; - std::vector m_ws; }; ECDSA_Timing_Test::ECDSA_Timing_Test(const std::string& ecgroup) : m_group(Botan::EC_Group::from_name(ecgroup)), - m_privkey(timing_test_rng(), m_group), - m_x(m_privkey._private_key()), - m_b(Botan::EC_Scalar::random(m_group, timing_test_rng())), - m_b_inv(m_b.invert()) {} + m_x(Botan::EC_Scalar::random(m_group, timing_test_rng())), + m_b(Botan::EC_Scalar::random(m_group, timing_test_rng())) {} uint64_t ECDSA_Timing_Test::measure_critical_function(const std::vector& input) { const auto k = Botan::EC_Scalar::from_bytes_with_trunc(m_group, input); // fixed message to minimize noise const auto m = Botan::EC_Scalar::from_bytes_with_trunc(m_group, std::vector{5}); - TimingTestTimer timer; + const TimingTestTimer timer; // the following ECDSA operations involve and should not leak any information about k - const auto r = Botan::EC_Scalar::gk_x_mod_order(k, timing_test_rng(), m_ws); - const auto k_inv = k.invert(); - m_b.square_self(); - m_b_inv.square_self(); + + const auto r = Botan::EC_Scalar::gk_x_mod_order(k, timing_test_rng()); + + const auto k_inv = (m_b * k).invert(); + const auto xr_m = ((m_x * m_b) * r) + (m * m_b); - const auto s = (k_inv * xr_m) * m_b_inv; + + const auto s = (k_inv * xr_m); + + // Generate the next blinding value via modular squaring + m_b.square_self(); + BOTAN_UNUSED(r, s); return timer.complete(); @@ -307,14 +339,13 @@ private: const Botan::EC_Group m_group; - std::vector m_ws; }; uint64_t ECC_Mul_Timing_Test::measure_critical_function(const std::vector& input) { const auto k = Botan::EC_Scalar::from_bytes_with_trunc(m_group, input); - TimingTestTimer timer; - const auto kG = Botan::EC_AffinePoint::g_mul(k, timing_test_rng(), m_ws); + const TimingTestTimer timer; + const auto kG = Botan::EC_AffinePoint::g_mul(k, timing_test_rng()); return timer.complete(); } @@ -336,7 +367,7 @@ const Botan::BigInt x(input.data(), input.size()); const size_t max_x_bits = m_group.p_bits(); - TimingTestTimer timer; + const TimingTestTimer timer; const Botan::BigInt g_x_p = m_group.power_g_p(x, max_x_bits); @@ -360,7 +391,7 @@ uint64_t Invmod_Timing_Test::measure_critical_function(const std::vector& input) { const Botan::BigInt k(input.data(), input.size()); - TimingTestTimer timer; + const TimingTestTimer timer; const Botan::BigInt inv = Botan::inverse_mod_secret_prime(k, m_p); return timer.complete(); } @@ -389,9 +420,14 @@ size_t total_runs = 0; std::vector results(inputs.size()); + std::vector indexes(inputs.size()); + std::iota(indexes.begin(), indexes.end(), size_t{0}); + while(total_runs < (warmup_runs + measurement_runs)) { - for(size_t i = 0; i != inputs.size(); ++i) { - results[i] = measure_critical_function(inputs[i]); + shuffle_idx(indexes, *m_rng); + + for(const size_t testcase : indexes) { + results[testcase] = measure_critical_function(inputs[testcase]); } total_runs++; @@ -435,7 +471,7 @@ filename = test_data_dir + "/" + test_type + ".vec"; } - std::vector lines = read_testdata(filename); + const std::vector lines = read_testdata(filename); std::vector> results = test->execute_evaluation(lines, warmup_runs, measurement_runs); @@ -454,7 +490,7 @@ static std::vector read_testdata(const std::string& filename) { std::vector lines; std::ifstream infile(filename); - if(infile.good() == false) { + if(!infile.good()) { throw CLI_Error("Error reading test data from '" + filename + "'"); } std::string line; @@ -541,23 +577,47 @@ BOTAN_REGISTER_COMMAND("timing_test", Timing_Test_Command); -#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EME_PKCS1) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) +#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EME_PKCS1) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) && \ + defined(BOTAN_HAS_SYSTEM_RNG) class MARVIN_Test_Command final : public Command { public: - MARVIN_Test_Command() : Command("marvin_test key_file ctext_dir --runs=10 --output-nsec --expect-pt-len=0") {} + MARVIN_Test_Command() : + Command("marvin_test key_file ctext_dir --runs=1K --report-every=0 --output-nsec --expect-pt-len=0") {} std::string group() const override { return "testing"; } std::string description() const override { return "Run a test for MARVIN attack"; } + #if defined(BOTAN_TARGET_OS_HAS_POSIX1) + static inline volatile sig_atomic_t g_sigint_recv = 0; + + static void marvin_sigint_handler(int /*signal*/) { g_sigint_recv = 1; } + #endif + void go() override { const std::string key_file = get_arg("key_file"); const std::string ctext_dir = get_arg("ctext_dir"); - const size_t measurement_runs = get_arg_sz("runs"); + const size_t measurement_runs = parse_runs_arg(get_arg("runs")); const size_t expect_pt_len = get_arg_sz("expect-pt-len"); + const size_t report_every = get_arg_sz("report-every"); const bool output_nsec = flag_set("output-nsec"); + #if defined(BOTAN_TARGET_OS_HAS_POSIX1) + ::setenv("BOTAN_THREAD_POOL_SIZE", "none", /*overwrite?*/ 1); + + struct sigaction sigaction {}; + + sigaction.sa_handler = marvin_sigint_handler; + sigemptyset(&sigaction.sa_mask); + sigaction.sa_flags = 0; + + const int rc = ::sigaction(SIGINT, &sigaction, nullptr); + if(rc != 0) { + throw CLI_Error("Failed to set SIGINT handler"); + } + #endif + Botan::DataSource_Stream key_src(key_file); const auto key = Botan::PKCS8::load_key(key_src); @@ -591,71 +651,113 @@ throw CLI_Usage_Error("Empty ciphertext directory for MARVIN test"); } - Botan::PK_Decryptor_EME op(*key, rng(), "PKCS1v15"); + auto& test_results_file = output(); - std::vector indexes; - for(size_t i = 0; i != names.size(); ++i) { - indexes.push_back(i); - } + const size_t testcases = names.size(); - std::vector> measurements(names.size()); + #if defined(BOTAN_HAS_CHACHA_RNG) + auto rng = Botan::ChaCha_RNG(Botan::system_rng()); + #else + auto& rng = Botan::system_rng(); + #endif + + const Botan::PK_Decryptor_EME op(*key, rng, "PKCS1v15"); + + std::vector indexes(testcases); + std::iota(indexes.begin(), indexes.end(), size_t{0}); + + std::vector> measurements(testcases); for(auto& m : measurements) { m.reserve(measurement_runs); } + // This is only set differently if we exit early from the loop + size_t runs_completed = measurement_runs; + + std::vector ciphertext(modulus_bytes); + for(size_t r = 0; r != measurement_runs; ++r) { - shuffle(indexes, rng()); + if(r > 0 && report_every > 0 && (r % report_every) == 0) { + std::cerr << "Gathering sample # " << r << "\n"; + } - std::vector ciphertext(modulus_bytes); - for(size_t i = 0; i != indexes.size(); ++i) { - const size_t testcase = indexes[i]; + shuffle_idx(indexes, rng); - // FIXME should this load be constant time? - Botan::copy_mem(&ciphertext[0], &ciphertext_data[testcase * modulus_bytes], modulus_bytes); + for(const size_t testcase : indexes) { + // Load the test ciphertext in constant time to avoid cache pollution + for(size_t j = 0; j != testcases; ++j) { + const auto j_eq_testcase = Botan::CT::Mask::is_equal(j, testcase).as_choice(); + const auto* testcase_j = &ciphertext_data[j * modulus_bytes]; + Botan::CT::conditional_assign_mem(j_eq_testcase, ciphertext.data(), testcase_j, modulus_bytes); + } - TimingTestTimer timer; - op.decrypt_or_random(ciphertext.data(), modulus_bytes, expect_pt_len, rng()); + const TimingTestTimer timer; + op.decrypt_or_random(ciphertext.data(), modulus_bytes, expect_pt_len, rng); const uint64_t duration = timer.complete(); BOTAN_ASSERT_NOMSG(measurements[testcase].size() == r); measurements[testcase].push_back(duration); } + + #if defined(BOTAN_TARGET_OS_HAS_POSIX1) + // Early exit check + if(g_sigint_recv != 0) { + std::cerr << "Exiting early after " << r << " measurements\n"; + runs_completed = r; + break; + } + #endif } + report_results(test_results_file, names, measurements, runs_completed, output_nsec); + } + + private: + static void report_results(std::ostream& output, + std::span names, + std::span> measurements, + size_t runs_completed, + bool output_nsec) { for(size_t t = 0; t != names.size(); ++t) { if(t > 0) { - output() << ","; + output << ","; } - output() << names[t]; + output << names[t]; } - output() << "\n"; + output << "\n"; - for(size_t r = 0; r != measurement_runs; ++r) { + for(size_t r = 0; r != runs_completed; ++r) { for(size_t t = 0; t != names.size(); ++t) { if(t > 0) { - output() << ","; + output << ","; } const uint64_t dur_nsec = measurements[t][r]; if(output_nsec) { - output() << dur_nsec; + output << dur_nsec; } else { const double dur_s = static_cast(dur_nsec) / 1000000000.0; - output() << dur_s; + output << dur_s; } } - output() << "\n"; + output << "\n"; } } - template - void shuffle(std::vector& vec, Botan::RandomNumberGenerator& rng) { - const size_t n = vec.size(); - for(size_t i = 0; i != n; ++i) { - uint8_t jb[sizeof(uint64_t)]; - rng.randomize(jb, sizeof(jb)); - uint64_t j8 = Botan::load_le(jb, 0); - size_t j = i + static_cast(j8) % (n - i); - std::swap(vec[i], vec[j]); + static size_t parse_runs_arg(const std::string& param) { + if(param.starts_with("-")) { + throw CLI_Usage_Error("Cannot have a negative run count"); + } + + if(param.ends_with("m") || param.ends_with("M")) { + return parse_runs_arg(param.substr(0, param.size() - 1)) * 1'000'000; + } else if(param.ends_with("k") || param.ends_with("K")) { + return parse_runs_arg(param.substr(0, param.size() - 1)) * 1'000; + } else { + try { + return static_cast(std::stoul(param)); + } catch(std::exception&) { + throw CLI_Usage_Error("Unexpected syntax for --runs option (try 1000, 1K, or 2M)"); + } } } }; @@ -664,4 +766,6 @@ #endif +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/tls_client.cpp botan3-3.12.0+dfsg/src/cli/tls_client.cpp --- botan3-3.7.1+dfsg/src/cli/tls_client.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/tls_client.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,9 @@ #include "cli.h" +#include +#include + #if defined(BOTAN_HAS_TLS) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) && defined(BOTAN_TARGET_OS_HAS_SOCKETS) #include @@ -20,7 +23,6 @@ #include #include #include - #include #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER) #include @@ -40,14 +42,14 @@ class Callbacks : public Botan::TLS::Callbacks { public: - Callbacks(TLS_Client& client_command) : m_client_command(client_command), m_peer_closed(false) {} + explicit Callbacks(TLS_Client& client_command) : m_client_command(client_command), m_peer_closed(false) {} std::ostream& output(); bool flag_set(const std::string& flag_name) const; std::string get_arg(const std::string& arg_name) const; void send(std::span buffer); - int peer_closed() const { return m_peer_closed; } + bool peer_closed() const { return m_peer_closed; } void tls_verify_cert_chain(const std::vector& cert_chain, const std::vector>& ocsp, @@ -59,14 +61,14 @@ throw Botan::Invalid_Argument("Certificate chain was empty"); } - Botan::Path_Validation_Restrictions restrictions(policy.require_cert_revocation_info(), - policy.minimum_signature_strength()); + const Botan::Path_Validation_Restrictions restrictions(policy.require_cert_revocation_info(), + policy.minimum_signature_strength()); auto ocsp_timeout = std::chrono::milliseconds(1000); const std::string checked_name = flag_set("skip-hostname-check") ? "" : std::string(hostname); - Botan::Path_Validation_Result result = Botan::x509_path_validate( + const Botan::Path_Validation_Result result = Botan::x509_path_validate( cert_chain, restrictions, trusted_roots, checked_name, usage, tls_current_timestamp(), ocsp_timeout, ocsp); if(result.successful_validation()) { @@ -203,7 +205,7 @@ const uint16_t port = get_arg_u16("port"); const std::string transport = get_arg("type"); const std::string next_protos = get_arg("next-protocols"); - const bool use_system_cert_store = flag_set("skip-system-cert-store") == false; + const bool use_system_cert_store = !flag_set("skip-system-cert-store"); const std::string trusted_CAs = get_arg("trusted-cas"); const auto tls_version = get_arg("tls-version"); @@ -290,7 +292,7 @@ if(client.is_active()) { FD_SET(STDIN_FILENO, &readfds); if(first_active && !protocols_to_offer.empty()) { - std::string app = client.application_protocol(); + const std::string app = client.application_protocol(); if(!app.empty()) { output() << "Server choose protocol: " << client.application_protocol() << "\n"; } @@ -305,7 +307,7 @@ if(FD_ISSET(m_sockfd, &readfds)) { uint8_t buf[4 * 1024] = {0}; - ssize_t got = ::read(m_sockfd, buf, sizeof(buf)); + const ssize_t got = ::read(m_sockfd, buf, sizeof(buf)); if(got == 0) { output() << "EOF on socket\n"; @@ -324,7 +326,7 @@ if(FD_ISSET(STDIN_FILENO, &readfds)) { uint8_t buf[1024] = {0}; - ssize_t got = read(STDIN_FILENO, buf, sizeof(buf)); + const ssize_t got = read(STDIN_FILENO, buf, sizeof(buf)); if(got == 0) { output() << "EOF on stdin\n"; @@ -337,7 +339,7 @@ } if(got == 2 && buf[1] == '\n') { - char cmd = buf[0]; + const char cmd = buf[0]; if(cmd == 'R' || cmd == 'r') { output() << "Client initiated renegotiation\n"; @@ -385,19 +387,20 @@ private: static socket_type connect_to_host(const std::string& host, uint16_t port, bool tcp) { - addrinfo hints; - Botan::clear_mem(&hints, 1); + addrinfo hints{}; hints.ai_family = AF_UNSPEC; hints.ai_socktype = tcp ? SOCK_STREAM : SOCK_DGRAM; - addrinfo *res, *rp = nullptr; - if(::getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &res) != 0) { + unique_addr_info_ptr res = nullptr; + + if(::getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, Botan::out_ptr(res)) != 0) { throw CLI_Error("getaddrinfo failed for " + host); } socket_type fd = 0; + bool success = false; - for(rp = res; rp != nullptr; rp = rp->ai_next) { + for(const addrinfo* rp = res.get(); rp != nullptr; rp = rp->ai_next) { fd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if(fd == invalid_socket()) { @@ -409,14 +412,13 @@ continue; } + success = true; break; } - ::freeaddrinfo(res); - - if(rp == nullptr) // no address succeeded - { - throw CLI_Error("connect failed"); + if(!success) { + // no address succeeded + throw CLI_Error("Connecting to host failed"); } return fd; @@ -431,6 +433,12 @@ } socket_type m_sockfd = invalid_socket(); + + using unique_addr_info_ptr = std::unique_ptr; }; namespace { diff -Nru botan3-3.7.1+dfsg/src/cli/tls_helpers.h botan3-3.12.0+dfsg/src/cli/tls_helpers.h --- botan3-3.7.1+dfsg/src/cli/tls_helpers.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/tls_helpers.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,15 +8,19 @@ #ifndef BOTAN_CLI_TLS_HELPERS_H_ #define BOTAN_CLI_TLS_HELPERS_H_ +#include +#include #include #include #include #include +#include #include #include #include #include #include +#include #include "cli_exceptions.h" @@ -25,8 +29,8 @@ #endif inline bool value_exists(const std::vector& vec, const std::string& val) { - for(size_t i = 0; i != vec.size(); ++i) { - if(vec[i] == val) { + for(const auto& v : vec) { + if(v == val) { return true; } } @@ -35,9 +39,13 @@ inline std::string maybe_hex_encode(std::string_view v) { auto is_printable_char = [](uint8_t c) { return c >= 32 && c < 127; }; - if(!std::all_of(v.begin(), v.end(), is_printable_char)) { - return Botan::hex_encode(std::span(reinterpret_cast(v.data()), v.size())); + + for(const char c : v) { + if(!is_printable_char(c)) { + return Botan::hex_encode(std::span(reinterpret_cast(v.data()), v.size())); + } } + return std::string(v); } @@ -88,7 +96,7 @@ // the Hash algorithm MUST be set when the PSK is established or // default to SHA-256 if no such algorithm is defined. m_psk_prf(psk_prf.value_or("SHA-256")) { - if(ca_path.empty() == false) { + if(!ca_path.empty()) { m_certstores.push_back(std::make_shared(ca_path)); } @@ -252,11 +260,10 @@ class TLS_All_Policy final : public Botan::TLS::Policy { public: std::vector allowed_ciphers() const override { - return std::vector{"ChaCha20Poly1305", - "AES-256/OCB(12)", - "AES-128/OCB(12)", - "AES-256/GCM", + return std::vector{"AES-256/GCM", "AES-128/GCM", + "ChaCha20Poly1305", + "AES-256/OCB(12)", "AES-256/CCM", "AES-128/CCM", "AES-256/CCM(8)", @@ -270,7 +277,8 @@ "Camellia-256", "Camellia-128", "SEED", - "3DES"}; + "3DES", + "NULL"}; } std::vector allowed_key_exchange_methods() const override { @@ -296,7 +304,7 @@ } else if(policy_type == "bsi") { return std::make_shared(); } else if(policy_type == "datagram") { - return std::make_shared(); + return std::make_shared(); } else if(policy_type == "all" || policy_type == "everything") { return std::make_shared(); } diff -Nru botan3-3.7.1+dfsg/src/cli/tls_http_server.cpp botan3-3.12.0+dfsg/src/cli/tls_http_server.cpp --- botan3-3.7.1+dfsg/src/cli/tls_http_server.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/tls_http_server.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -37,6 +37,7 @@ #include #include + #include #include #include #include @@ -78,17 +79,17 @@ Logger(std::ostream& out, std::ostream& err) : m_out(out), m_err(err) {} void log(std::string_view out) { - std::scoped_lock lk(m_mutex); + const std::scoped_lock lk(m_mutex); m_out << Botan::fmt("[{}] {}", timestamp(), out) << "\n"; } void error(std::string_view err) { - std::scoped_lock lk(m_mutex); + const std::scoped_lock lk(m_mutex); m_err << Botan::fmt("[{}] {}", timestamp(), err) << "\n"; } void flush() { - std::scoped_lock lk(m_mutex); + const std::scoped_lock lk(m_mutex); m_out.flush(); m_err.flush(); } @@ -133,7 +134,7 @@ strm << "Client random: " << Botan::hex_encode(client_hello.random()) << "\n"; strm << "Client offered following ciphersuites:\n"; - for(uint16_t suite_id : client_hello.ciphersuites()) { + for(const uint16_t suite_id : client_hello.ciphersuites()) { const auto ciphersuite = Botan::TLS::Ciphersuite::by_id(suite_id); strm << " - 0x" << std::hex << std::setfill('0') << std::setw(4) << suite_id << std::dec @@ -337,11 +338,11 @@ std::string description() const override { return "Provides a simple HTTP server"; } size_t thread_count() const { - if(size_t t = get_arg_sz("threads")) { + if(const size_t t = get_arg_sz("threads")) { return t; } #if defined(BOTAN_HAS_OS_UTILS) - if(size_t t = Botan::OS::get_cpu_available()) { + if(const size_t t = Botan::OS::get_cpu_available()) { return t; } #endif @@ -400,8 +401,8 @@ io.run(); - for(size_t i = 0; i < threads.size(); ++i) { - threads[i]->join(); + for(auto& thread : threads) { + thread->join(); } } }; diff -Nru botan3-3.7.1+dfsg/src/cli/tls_proxy.cpp botan3-3.12.0+dfsg/src/cli/tls_proxy.cpp --- botan3-3.7.1+dfsg/src/cli/tls_proxy.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/tls_proxy.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -19,11 +19,12 @@ #include #include - #include + #include #include #include #include + #include #include #include #include @@ -42,6 +43,8 @@ namespace { +// NOLINTBEGIN(*-avoid-endl,*-avoid-bind) + using boost::asio::ip::tcp; template @@ -83,7 +86,7 @@ class ServerStatus { public: - ServerStatus(size_t max_clients) : m_max_clients(max_clients), m_clients_serviced(0) {} + explicit ServerStatus(size_t max_clients) : m_max_clients(max_clients), m_clients_serviced(0) {} bool should_exit() const { if(m_max_clients == 0) { @@ -131,7 +134,7 @@ } void stop() { - if(m_is_closed == false) { + if(!m_is_closed) { /* Don't need to talk to the server anymore Client socket is closed during write callback @@ -175,20 +178,21 @@ try { if(!m_tls->is_active()) { - log_binary_message("From client", &m_c2p[0], bytes_transferred); + log_binary_message("From client", m_c2p.data(), bytes_transferred); } - m_tls->received_data(&m_c2p[0], bytes_transferred); + m_tls->received_data(m_c2p.data(), bytes_transferred); } catch(Botan::Exception& e) { log_exception("TLS connection failed", e); stop(); return; } - m_client_socket.async_read_some(boost::asio::buffer(&m_c2p[0], m_c2p.size()), - m_strand.wrap(boost::bind(&tls_proxy_session::client_read, - shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred))); + m_client_socket.async_read_some( + boost::asio::buffer(m_c2p), + boost::asio::bind_executor( + m_strand, [self = shared_from_this()](const boost::system::error_code& ec, std::size_t bytes) { + self->client_read(ec, bytes); + })); } void handle_client_write_completion(const boost::system::error_code& error) { @@ -231,13 +235,15 @@ if(m_p2c.empty() && !m_p2c_pending.empty()) { std::swap(m_p2c_pending, m_p2c); - log_binary_message("To Client", &m_p2c[0], m_p2c.size()); + log_binary_message("To Client", m_p2c.data(), m_p2c.size()); - boost::asio::async_write(m_client_socket, - boost::asio::buffer(&m_p2c[0], m_p2c.size()), - m_strand.wrap(boost::bind(&tls_proxy_session::handle_client_write_completion, - shared_from_this(), - boost::asio::placeholders::error))); + boost::asio::async_write( + m_client_socket, + boost::asio::buffer(m_p2c), + boost::asio::bind_executor( + m_strand, [self = shared_from_this()](const boost::system::error_code& ec, std::size_t /*bytes*/) { + self->handle_client_write_completion(ec); + })); } } @@ -250,13 +256,15 @@ if(m_p2s.empty() && !m_p2s_pending.empty()) { std::swap(m_p2s_pending, m_p2s); - log_text_message("To Server", &m_p2s[0], m_p2s.size()); + log_text_message("To Server", m_p2s.data(), m_p2s.size()); - boost::asio::async_write(m_server_socket, - boost::asio::buffer(&m_p2s[0], m_p2s.size()), - m_strand.wrap(boost::bind(&tls_proxy_session::handle_server_write_completion, - shared_from_this(), - boost::asio::placeholders::error))); + boost::asio::async_write( + m_server_socket, + boost::asio::buffer(m_p2s), + boost::asio::bind_executor( + m_strand, [self = shared_from_this()](const boost::system::error_code& ec, std::size_t /*bytes*/) { + self->handle_server_write_completion(ec); + })); } } @@ -268,10 +276,10 @@ } try { - if(bytes_transferred) { - log_text_message("Server to client", &m_s2p[0], m_s2p.size()); - log_binary_message("Server to client", &m_s2p[0], m_s2p.size()); - m_tls->send(&m_s2p[0], bytes_transferred); + if(bytes_transferred > 0) { + log_text_message("Server to client", m_s2p.data(), m_s2p.size()); + log_binary_message("Server to client", m_s2p.data(), m_s2p.size()); + m_tls->send(m_s2p.data(), bytes_transferred); } } catch(Botan::Exception& e) { log_exception("TLS connection failed", e); @@ -281,11 +289,12 @@ m_s2p.resize(readbuf_size); - m_server_socket.async_read_some(boost::asio::buffer(&m_s2p[0], m_s2p.size()), - m_strand.wrap(boost::bind(&tls_proxy_session::server_read, - shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred))); + m_server_socket.async_read_some( + boost::asio::buffer(m_s2p), + boost::asio::bind_executor( + m_strand, [self = shared_from_this()](const boost::system::error_code& ec, std::size_t bytes) { + self->server_read(ec, bytes); + })); } void tls_session_activated() override { @@ -367,7 +376,7 @@ } void serve_one_session() { - session::pointer new_session = make_session(); + const session::pointer new_session = make_session(); m_acceptor.async_accept( new_session->client_socket(), @@ -408,11 +417,11 @@ std::string description() const override { return "Proxies requests between a TLS client and a TLS server"; } size_t thread_count() const { - if(size_t t = get_arg_sz("threads")) { + if(const size_t t = get_arg_sz("threads")) { return t; } #if defined(BOTAN_HAS_OS_UTILS) - if(size_t t = Botan::OS::get_cpu_available()) { + if(const size_t t = Botan::OS::get_cpu_available()) { return t; } #endif @@ -454,7 +463,8 @@ session_mgr = std::make_shared(rng_as_shared()); } - tls_proxy_server server(io, listen_port, server_endpoint_iterator, creds, policy, session_mgr, max_clients); + const tls_proxy_server server( + io, listen_port, server_endpoint_iterator, creds, policy, session_mgr, max_clients); std::vector> threads; @@ -465,12 +475,14 @@ io.run(); - for(size_t i = 0; i < threads.size(); ++i) { - threads[i]->join(); + for(auto& thread : threads) { + thread->join(); } } }; +// NOLINTEND(*-avoid-endl,*-avoid-bind) + BOTAN_REGISTER_COMMAND("tls_proxy", TLS_Proxy); } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/tls_server.cpp botan3-3.12.0+dfsg/src/cli/tls_server.cpp --- botan3-3.7.1+dfsg/src/cli/tls_server.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/tls_server.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,8 @@ #include "cli.h" #include "sandbox.h" +#include + #if defined(BOTAN_TARGET_OS_HAS_SOCKETS) #include #endif @@ -49,7 +51,7 @@ class Callbacks : public Botan::TLS::Callbacks { public: - Callbacks(TLS_Server& server_command) : m_server_command(server_command) {} + explicit Callbacks(TLS_Server& server_command) : m_server_command(server_command) {} std::ostream& output(); void send(std::span buffer); @@ -75,8 +77,8 @@ } void tls_record_received(uint64_t /*seq_no*/, std::span input) override { - for(size_t i = 0; i != input.size(); ++i) { - const char c = static_cast(input[i]); + for(auto uc : input) { + const char c = static_cast(uc); m_line_buf += c; if(c == '\n') { push_pending_output(std::exchange(m_line_buf, {})); @@ -174,7 +176,7 @@ return; } - socket_type server_fd = make_server_socket(port); + const socket_type server_fd = make_server_socket(port); size_t clients_served = 0; output() << "Listening for new connections on " << transport << " port " << port << std::endl; @@ -187,11 +189,12 @@ if(m_is_tcp) { m_socket = ::accept(server_fd, nullptr, nullptr); } else { - struct sockaddr_in from; + struct sockaddr_in from {}; + socklen_t from_len = sizeof(sockaddr_in); void* peek_buf = nullptr; - size_t peek_len = 0; + size_t peek_len = 0; // NOLINT(*-const-correctness) #if defined(BOTAN_TARGET_OS_IS_MACOS) // macOS handles zero size buffers differently - it will return 0 even if there's no incoming data, @@ -224,7 +227,7 @@ if(!dump_traces_to.empty()) { auto now = std::chrono::system_clock::now().time_since_epoch(); - uint64_t timestamp = std::chrono::duration_cast(now).count(); + const uint64_t timestamp = std::chrono::duration_cast(now).count(); const std::string dump_file = dump_traces_to + "/tls_" + std::to_string(timestamp) + ".bin"; dump_stream = std::make_unique(dump_file.c_str()); } @@ -233,7 +236,7 @@ while(!server.is_closed()) { try { uint8_t buf[4 * 1024] = {0}; - ssize_t got = ::recv(m_socket, Botan::cast_uint8_ptr_to_char(buf), sizeof(buf), 0); + const ssize_t got = ::recv(m_socket, Botan::cast_uint8_ptr_to_char(buf), sizeof(buf), 0); if(got == -1) { error_output() << "Error in socket read - " << err_to_string(errno) << std::endl; @@ -252,7 +255,7 @@ server.received_data(buf, got); while(server.is_active() && !m_pending_output.empty()) { - std::string output = m_pending_output.front(); + const std::string output = m_pending_output.front(); m_pending_output.pop_front(); server.send(output); @@ -287,11 +290,11 @@ void send(std::span buf) { if(m_is_tcp) { - ssize_t sent = ::send(m_socket, buf.data(), static_cast(buf.size()), MSG_NOSIGNAL); + const ssize_t sent = ::send(m_socket, buf.data(), static_cast(buf.size()), MSG_NOSIGNAL); if(sent == -1) { error_output() << "Error writing to socket - " << err_to_string(errno) << std::endl; - } else if(sent != static_cast(buf.size())) { + } else if(sent >= 0 && static_cast(sent) != buf.size()) { error_output() << "Packet of length " << buf.size() << " truncated to " << sent << std::endl; } } else { @@ -317,12 +320,12 @@ socket_type make_server_socket(uint16_t port) { const int type = m_is_tcp ? SOCK_STREAM : SOCK_DGRAM; - socket_type fd = ::socket(PF_INET, type, 0); + const socket_type fd = ::socket(PF_INET, type, 0); if(fd == invalid_socket()) { throw CLI_Error("Unable to acquire socket"); } - sockaddr_in socket_info; + sockaddr_in socket_info{}; Botan::clear_mem(&socket_info, 1); socket_info.sin_family = AF_INET; socket_info.sin_port = htons(port); diff -Nru botan3-3.7.1+dfsg/src/cli/tls_utils.cpp botan3-3.12.0+dfsg/src/cli/tls_utils.cpp --- botan3-3.7.1+dfsg/src/cli/tls_utils.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/tls_utils.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,18 +9,26 @@ #if defined(BOTAN_HAS_TLS) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) #include + #include #include - #include #include #include #include #include #include + #if defined(BOTAN_HAS_TLS_12) && defined(BOTAN_HAS_TLS_13) + #include + #include + #include + #endif + #include "tls_helpers.h" namespace Botan_CLI { +namespace { + class TLS_Ciphersuites final : public Command { public: TLS_Ciphersuites() : Command("tls_ciphers --policy=default --version=tls1.2") {} @@ -46,12 +54,12 @@ auto policy = load_tls_policy(policy_type); - if(policy->acceptable_protocol_version(version) == false) { + if(!policy->acceptable_protocol_version(version)) { error_output() << "Error: the policy specified does not allow the given TLS version\n"; return; } - for(uint16_t suite_id : policy->ciphersuite_list(version)) { + for(const uint16_t suite_id : policy->ciphersuite_list(version)) { const auto s = Botan::TLS::Ciphersuite::by_id(suite_id); output() << ((s) ? s->to_string() : "unknown cipher suite") << "\n"; } @@ -60,7 +68,7 @@ BOTAN_REGISTER_COMMAND("tls_ciphers", TLS_Ciphersuites); - #if defined(BOTAN_HAS_TLS_13) + #if defined(BOTAN_HAS_TLS_12) && defined(BOTAN_HAS_TLS_13) class TLS_Client_Hello_Reader final : public Command { public: @@ -110,9 +118,14 @@ } try { - auto hello = Botan::TLS::Client_Hello_13::parse(input); - - output() << format_hello(hello); + output() << format_hello([&]() -> std::variant { + auto data = Botan::TLS::Client_Hello_13::parse(input); + if(std::holds_alternative(data)) { + return std::get(std::move(data)); + } else { + return Botan::TLS::Client_Hello_12(input); + } + }()); } catch(std::exception& e) { error_output() << "Parsing client hello failed: " << e.what() << "\n"; } @@ -126,11 +139,11 @@ const auto* hello_base = std::visit([](const auto& ch) -> const Botan::TLS::Client_Hello* { return &ch; }, hello); - const auto version = std::visit(Botan::overloaded{ - [](const Botan::TLS::Client_Hello_12&) { return "1.2"; }, - [](const Botan::TLS::Client_Hello_13&) { return "1.3"; }, - }, - hello); + const std::string version = std::visit(Botan::overloaded{ + [](const Botan::TLS::Client_Hello_12&) { return "1.2"; }, + [](const Botan::TLS::Client_Hello_13&) { return "1.3"; }, + }, + hello); oss << "Version: " << version << "\n" << "Random: " << Botan::hex_encode(hello_base->random()) << "\n"; @@ -138,7 +151,7 @@ if(!hello_base->session_id().empty()) { oss << "SessionID: " << Botan::hex_encode(hello_base->session_id().get()) << "\n"; } - for(uint16_t csuite_id : hello_base->ciphersuites()) { + for(const uint16_t csuite_id : hello_base->ciphersuites()) { const auto csuite = Botan::TLS::Ciphersuite::by_id(csuite_id); if(csuite && csuite->valid()) { oss << "Cipher: " << csuite->to_string() << "\n"; @@ -154,7 +167,7 @@ if(hello_base->signature_schemes().empty()) { oss << "Did not send signature_algorithms extension\n"; } else { - for(Botan::TLS::Signature_Scheme scheme : hello_base->signature_schemes()) { + for(const Botan::TLS::Signature_Scheme scheme : hello_base->signature_schemes()) { try { auto s = scheme.to_string(); oss << s << " "; @@ -165,7 +178,7 @@ oss << "\n"; } - if(auto sg = hello_base->extensions().get()) { + if(auto* sg = hello_base->extensions().get()) { oss << "Supported Groups: "; for(const auto group : sg->groups()) { oss << group.to_string().value_or(Botan::fmt("Unknown group: {}", group.wire_code())) << " "; @@ -183,7 +196,7 @@ hello_flags["Session Ticket"] = ch12.supports_session_ticket(); }, [&](const Botan::TLS::Client_Hello_13& ch13) { - if(auto ks = ch13.extensions().get()) { + if(auto* ks = ch13.extensions().get()) { oss << "Key Shares: "; for(const auto group : ks->offered_groups()) { oss << group.to_string().value_or(Botan::fmt("Unknown group: {}", group.wire_code())) @@ -207,6 +220,8 @@ #endif +} // namespace + } // namespace Botan_CLI #endif diff -Nru botan3-3.7.1+dfsg/src/cli/tss.cpp botan3-3.12.0+dfsg/src/cli/tss.cpp --- botan3-3.7.1+dfsg/src/cli/tss.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/tss.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -17,6 +17,8 @@ namespace Botan_CLI { +namespace { + #if defined(BOTAN_HAS_THRESHOLD_SECRET_SHARING) class TSS_Split final : public Command { @@ -73,7 +75,7 @@ } private: - Botan::secure_vector slurp_file_lvec(const std::string& input_file) { + static Botan::secure_vector slurp_file_lvec(const std::string& input_file) { Botan::secure_vector buf; auto insert_fn = [&](const uint8_t b[], size_t l) { buf.insert(buf.end(), b, b + l); }; Command::read_file(input_file, insert_fn, 4096); @@ -117,4 +119,6 @@ #endif +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/utils.cpp botan3-3.12.0+dfsg/src/cli/utils.cpp --- botan3-3.7.1+dfsg/src/cli/utils.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/utils.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,11 +8,14 @@ #include "cli.h" #include -#include -#include +#include #include #include +#if defined(BOTAN_HAS_CPUID) + #include +#endif + #if defined(BOTAN_HAS_HTTP_UTIL) #include #endif @@ -27,6 +30,8 @@ namespace Botan_CLI { +namespace { + class Print_Help final : public Command { public: Print_Help() : Command("help") {} @@ -126,7 +131,7 @@ output() << "Command '" << cmd << "' is " << (exists ? "" : "not ") << "available\n"; } - if(exists == false) { + if(!exists) { this->set_return_code(1); } } @@ -158,7 +163,7 @@ } else if(arg == "cflags") { output() << "-I" << BOTAN_INSTALL_PREFIX << "/" << BOTAN_INSTALL_HEADER_DIR << "\n"; } else if(arg == "ldflags") { - if(*BOTAN_LINK_FLAGS) { + if(*BOTAN_LINK_FLAGS != 0) { output() << BOTAN_LINK_FLAGS << ' '; } output() << "-L" << BOTAN_INSTALL_LIB_DIR << "\n"; @@ -191,6 +196,8 @@ BOTAN_REGISTER_COMMAND("version", Version_Info); +#if defined(BOTAN_HAS_CPUID) + class Print_Cpuid final : public Command { public: Print_Cpuid() : Command("cpuid") {} @@ -206,6 +213,8 @@ BOTAN_REGISTER_COMMAND("cpuid", Print_Cpuid); +#endif + #if defined(BOTAN_HAS_OS_UTILS) class Cycle_Counter final : public Command { @@ -280,7 +289,7 @@ std::string description() const override { return "Print a random UUID"; } void go() override { - Botan::UUID uuid(rng()); + const Botan::UUID uuid(rng()); output() << uuid.to_string() << "\n"; } }; @@ -312,4 +321,6 @@ #endif // http_util +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/cli/x509.cpp botan3-3.12.0+dfsg/src/cli/x509.cpp --- botan3-3.7.1+dfsg/src/cli/x509.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/x509.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -59,8 +59,6 @@ } } -} // namespace - #if defined(BOTAN_HAS_CERTSTOR_SYSTEM) class Trust_Root_Info final : public Command { @@ -72,7 +70,7 @@ std::string description() const override { return "List certs in the system trust store"; } void go() override { - Botan::System_Certificate_Store trust_roots; + const Botan::System_Certificate_Store trust_roots; const auto dn_list = trust_roots.all_subjects(); @@ -106,37 +104,48 @@ class Sign_Cert final : public Command { public: Sign_Cert() : - Command( - "sign_cert --ca-key-pass= --hash= " - "--duration=365 --emsa= ca_cert ca_key pkcs10_req") {} + Command("sign_cert --ca-key-pass= --hash= --padding= --emsa= --duration=365 ca_cert ca_key pkcs10_req") {} std::string group() const override { return "x509"; } std::string description() const override { return "Create a CA-signed X.509 certificate from a PKCS #10 CSR"; } void go() override { - Botan::X509_Certificate ca_cert(get_arg("ca_cert")); + const Botan::X509_Certificate ca_cert(get_arg("ca_cert")); const std::string key_file = get_arg("ca_key"); const std::string pass = get_passphrase_arg("Password for " + key_file, "ca-key-pass"); - const std::string emsa = get_arg("emsa"); + + // TODO(Botan4) remove --emsa option and this logic + const std::string padding = [&]() { + auto p = get_arg("padding"); + auto e = get_arg("emsa"); + if(e.empty() || p == e) { + return p; + } else if(p.empty()) { + return e; + } else { + throw CLI_Usage_Error("Use either --padding or --emsa not both"); + } + }(); + const std::string hash = get_arg("hash"); auto key = load_private_key(key_file, pass); - Botan::X509_CA ca(ca_cert, *key, hash, emsa, rng()); + const Botan::X509_CA ca(ca_cert, *key, hash, padding, rng()); - Botan::PKCS10_Request req(get_arg("pkcs10_req")); + const Botan::PKCS10_Request req(get_arg("pkcs10_req")); auto now = std::chrono::system_clock::now(); - Botan::X509_Time start_time(now); + const Botan::X509_Time start_time(now); typedef std::chrono::duration> days; - Botan::X509_Time end_time(now + days(get_arg_sz("duration"))); + const Botan::X509_Time end_time(now + days(get_arg_sz("duration"))); - Botan::X509_Certificate new_cert = ca.sign_request(req, rng(), start_time, end_time); + const Botan::X509_Certificate new_cert = ca.sign_request(req, rng(), start_time, end_time); update_stateful_private_key(*key, rng(), key_file, pass); output() << new_cert.PEM_encode(); @@ -154,23 +163,23 @@ std::string description() const override { return "Parse X.509 certificate and display data fields"; } void go() override { - std::vector data = slurp_file(get_arg("file")); + const std::vector data = slurp_file(get_arg("file")); Botan::DataSource_Memory in(data); while(!in.end_of_data()) { try { - Botan::X509_Certificate cert(in); + const Botan::X509_Certificate cert(in); try { - output() << cert.to_string() << std::endl; + output() << cert.to_string() << "\n"; } catch(Botan::Exception& e) { // to_string failed - report the exception and continue output() << "X509_Certificate::to_string failed: " << e.what() << "\n"; } if(flag_set("fingerprint")) { - output() << "Fingerprint: " << cert.fingerprint("SHA-256") << std::endl; + output() << "Fingerprint: " << cert.fingerprint("SHA-256") << "\n"; } } catch(Botan::Exception& e) { if(!in.end_of_data()) { @@ -196,13 +205,13 @@ } void go() override { - Botan::X509_Certificate subject(get_arg("subject")); - Botan::X509_Certificate issuer(get_arg("issuer")); - std::chrono::milliseconds timeout(get_arg_sz("timeout")); + const Botan::X509_Certificate subject(get_arg("subject")); + const Botan::X509_Certificate issuer(get_arg("issuer")); + const std::chrono::milliseconds timeout(get_arg_sz("timeout")); Botan::Certificate_Store_In_Memory cas; cas.add_certificate(issuer); - Botan::OCSP::Response resp = Botan::OCSP::online_check(issuer, subject, timeout); + const Botan::OCSP::Response resp = Botan::OCSP::online_check(issuer, subject, timeout); auto status = resp.status_for(issuer, subject, std::chrono::system_clock::now()); @@ -229,16 +238,16 @@ } void go() override { - Botan::X509_Certificate subject_cert(get_arg("subject")); + const Botan::X509_Certificate subject_cert(get_arg("subject")); Botan::Certificate_Store_In_Memory trusted; for(const auto& certfile : get_arg_list("ca_certs")) { trusted.add_certificate(Botan::X509_Certificate(certfile)); } - Botan::Path_Validation_Restrictions restrictions; + const Botan::Path_Validation_Restrictions restrictions; - Botan::Path_Validation_Result result = Botan::x509_path_validate(subject_cert, restrictions, trusted); + const Botan::Path_Validation_Result result = Botan::x509_path_validate(subject_cert, restrictions, trusted); if(result.successful_validation()) { output() << "Certificate passes validation checks\n"; @@ -255,7 +264,8 @@ Gen_Self_Signed() : Command( "gen_self_signed key CN --country= --dns= " - "--organization= --email= --path-limit=1 --days=365 --key-pass= --ca --hash= --emsa= --der") {} + "--organization= --email= --path-limit=1 --days=365 --key-pass= --ca --hash= --padding= --emsa= --der") { + } std::string group() const override { return "x509"; } @@ -277,17 +287,28 @@ opts.more_dns = Command::split_on(get_arg("dns"), ','); const bool der_format = flag_set("der"); - std::string emsa = get_arg("emsa"); + // TODO(Botan4) remove --emsa option and this logic + const std::string padding = [&]() { + auto p = get_arg("padding"); + auto e = get_arg("emsa"); + if(e.empty() || p == e) { + return p; + } else if(p.empty()) { + return e; + } else { + throw CLI_Usage_Error("Use either --padding or --emsa not both"); + } + }(); - if(emsa.empty() == false) { - opts.set_padding_scheme(emsa); + if(padding.empty() == false) { + opts.set_padding_scheme(padding); } if(flag_set("ca")) { opts.CA_key(get_arg_sz("path-limit")); } - Botan::X509_Certificate cert = Botan::X509::create_self_signed_cert(opts, *key, get_arg("hash"), rng()); + const Botan::X509_Certificate cert = Botan::X509::create_self_signed_cert(opts, *key, get_arg("hash"), rng()); update_stateful_private_key(*key, rng(), key_file, passphrase); if(der_format) { @@ -306,7 +327,7 @@ Generate_PKCS10() : Command( "gen_pkcs10 key CN --country= --organization= " - "--ca --path-limit=1 --email= --dns= --ext-ku= --key-pass= --hash= --emsa=") {} + "--ca --path-limit=1 --email= --dns= --ext-ku= --key-pass= --hash= --padding= --emsa=") {} std::string group() const override { return "x509"; } @@ -334,13 +355,24 @@ opts.add_ex_constraint(ext_ku); } - std::string emsa = get_arg("emsa"); + // TODO(Botan4) remove --emsa option and this logic + const std::string padding = [&]() { + auto p = get_arg("padding"); + auto e = get_arg("emsa"); + if(e.empty() || p == e) { + return p; + } else if(p.empty()) { + return e; + } else { + throw CLI_Usage_Error("Use either --padding or --emsa not both"); + } + }(); - if(emsa.empty() == false) { - opts.set_padding_scheme(emsa); + if(padding.empty() == false) { + opts.set_padding_scheme(padding); } - Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *key, get_arg("hash"), rng()); + const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *key, get_arg("hash"), rng()); update_stateful_private_key(*key, rng(), key_file, passphrase); output() << req.PEM_encode(); @@ -349,6 +381,8 @@ BOTAN_REGISTER_COMMAND("gen_pkcs10", Generate_PKCS10); +} // namespace + } // namespace Botan_CLI #endif diff -Nru botan3-3.7.1+dfsg/src/cli/zfec.cpp botan3-3.12.0+dfsg/src/cli/zfec.cpp --- botan3-3.7.1+dfsg/src/cli/zfec.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/cli/zfec.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -17,14 +17,16 @@ namespace Botan_CLI { +namespace { + #if defined(BOTAN_HAS_ZFEC) && defined(BOTAN_HAS_SHA2_64) -static const uint32_t FEC_MAGIC = 0xFECC0DEC; +constexpr uint32_t FEC_MAGIC = 0xFECC0DEC; const char* const FEC_SHARE_HASH = "SHA-512-256"; class FEC_Share final { public: - FEC_Share() : m_share(0), m_k(0), m_n(0), m_padding(0), m_bits() {} + FEC_Share() : m_share(0), m_k(0), m_n(0), m_padding(0) {} FEC_Share(size_t share, size_t k, size_t n, size_t padding, const uint8_t bits[], size_t len) : m_share(share), m_k(k), m_n(n), m_padding(padding), m_bits(bits, bits + len) {} @@ -59,10 +61,10 @@ } } - size_t share_id = bits[4]; - size_t k = bits[5]; - size_t n = bits[6]; - size_t padding = bits[7]; + const size_t share_id = bits[4]; + const size_t k = bits[5]; + const size_t n = bits[6]; + const size_t padding = bits[7]; if(share_id >= n || k >= n || padding >= k) { throw CLI_Error("FEC share has invalid k/n/padding fields"); @@ -128,7 +130,7 @@ const std::string input = get_arg("input"); const std::string output_dir = get_arg("output-dir"); - Botan::ZFEC fec(k, n); // checks k/n for validity + const Botan::ZFEC fec(k, n); // checks k/n for validity auto hash = Botan::HashFunction::create_or_throw(FEC_SHARE_HASH); @@ -167,7 +169,7 @@ std::ofstream output(output_fsname.str(), std::ios::binary); - FEC_Share fec_share(share, k, n, padding, bits, len); + const FEC_Share fec_share(share, k, n, padding, bits, len); fec_share.serialize_to(*hash, output); }; @@ -234,7 +236,7 @@ return; } - Botan::ZFEC fec(k, n); + const Botan::ZFEC fec(k, n); std::vector decoded(share_size * k); @@ -297,4 +299,6 @@ #endif +} // namespace + } // namespace Botan_CLI diff -Nru botan3-3.7.1+dfsg/src/configs/ci_deps.conf botan3-3.12.0+dfsg/src/configs/ci_deps.conf --- botan3-3.7.1+dfsg/src/configs/ci_deps.conf 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/configs/ci_deps.conf 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,28 @@ + +[android_ndk] +url=https://dl.google.com/android/repository/android-ndk-r29-linux.zip +sha256=4abbbcdc842f3d4879206e9695d52709603e52dd68d3c1fff04b3b5e7a308ecf + +[intel_sde] +url=https://downloadmirror.intel.com/915934/sde-external-10.8.0-2026-03-15-lin.tar.xz +sha256=50b320cd226acef7a491f5b321fc1be3c3c7984f9e27a456e64894b5b0979dd3 + +[limbo] +url=https://raw.githubusercontent.com/C2SP/x509-limbo/f47fd1ae26eebaee24116039a4e28d85840b79a5/limbo.json +sha256=12bde89c688edd921ba8e892b314317aa04f98bbcd3d62e985bde2bebe099357 + +[coveralls] +url=https://github.com/coverallsapp/coverage-reporter/releases/download/v0.6.17/coveralls-linux.tar.gz +sha256=f3c837413f66a6402953eee9ba3486366bd3de536100adb4a670bbd83c0382e0 + +[esdm] +url=https://github.com/smuellerDD/esdm/archive/refs/tags/v1.2.0.tar.gz +sha256=83e5f0539ab8688661f099f8fa380289cb1d24a942ce93306766bb2edb1bb19d + +[jitterentropy] +url=https://github.com/smuellerDD/jitterentropy-library/archive/refs/tags/v3.6.2.tar.gz +sha256=33825562f62e599d402e9106a5626090572fcb2cb41d157736f236455d1c3fdb + +[sccache_windows] +url=https://github.com/mozilla/sccache/releases/download/v0.15.0/sccache-v0.15.0-x86_64-pc-windows-msvc.tar.gz +sha256=b0b257a164bf438b2dea134ca7ded41c100f59a64b3bf275a202f1e8102ab217 diff -Nru botan3-3.7.1+dfsg/src/configs/clang-format botan3-3.12.0+dfsg/src/configs/clang-format --- botan3-3.7.1+dfsg/src/configs/clang-format 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/configs/clang-format 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,60 @@ +Language: Cpp +Standard: c++20 + +BasedOnStyle: Chromium + +ColumnLimit: 120 +AccessModifierOffset: -3 +IndentWidth: 3 +ContinuationIndentWidth: 3 +ConstructorInitializerIndentWidth: 6 + +PointerAlignment: Left +ReferenceAlignment: Left +QualifierAlignment: Left + +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^' + Priority: 3 + CaseSensitive: false + - Regex: '^' + Priority: 2 + CaseSensitive: false + - Regex: '^<.*' + Priority: 4 + CaseSensitive: false + - Regex: '^<.*\.h>' + Priority: 3 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + CaseSensitive: false + +AttributeMacros: ['BOTAN_FUNC_ISA', + 'BOTAN_FUNC_ISA_INLINE', + 'BOTAN_FORCE_INLINE', + 'BOTAN_DEPRECATED', + 'BOTAN_DEPRECATED_API'] + +BinPackArguments: false +BreakStringLiterals: false +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +ConstructorInitializerAllOnOneLineOrOnePerLine: true +EmptyLineBeforeAccessModifier: Always + +BreakConstructorInitializers: AfterColon +BreakInheritanceList: AfterComma +AllowShortBlocksOnASingleLine: Empty +AllowShortFunctionsOnASingleLine: Inline +SpaceBeforeParens: Never +IndentPPDirectives: BeforeHash +FixNamespaceComments: true +SeparateDefinitionBlocks: Always +KeepEmptyLinesAtTheStartOfBlocks: false +IndentAccessModifiers: true +ReflowComments: false +RequiresClausePosition: OwnLine +IndentRequiresClause: true +InsertNewlineAtEOF: True diff -Nru botan3-3.7.1+dfsg/src/configs/pylint.rc botan3-3.12.0+dfsg/src/configs/pylint.rc --- botan3-3.7.1+dfsg/src/configs/pylint.rc 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/configs/pylint.rc 2026-05-07 01:38:28.000000000 +0000 @@ -41,6 +41,7 @@ too-many-instance-attributes, too-many-public-methods, too-many-return-statements, +# too-many-positional-arguments, missing-docstring, # nice to have, but not necessary in every script fixme, # is it better to omit the note that something is worth improving??? @@ -283,7 +284,9 @@ [DESIGN] # Maximum number of arguments for function / method -max-args=8 +max-args = 10 + +#max-positional-arguments = 10 # Argument names that match this expression will be ignored. Default to name # with leading underscore diff -Nru botan3-3.7.1+dfsg/src/configs/repo_config.env botan3-3.12.0+dfsg/src/configs/repo_config.env --- botan3-3.7.1+dfsg/src/configs/repo_config.env 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/configs/repo_config.env 2026-05-07 01:38:28.000000000 +0000 @@ -16,24 +16,21 @@ BORINGSSL_REPO="randombit/boringssl" # The branch in our fork of boringssl that should be used for BoGo tests -BORINGSSL_BRANCH="rene/runner-20241016" +BORINGSSL_BRANCH="botan-bogo-20260429" # The Android NDK to for the cross platform builds to Android -ANDROID_NDK="android-ndk-r26" - -# Jitterentropy library version to be used for testing the 'jitter_rng' module -JITTERENTROPY_VERSION="3.6.0" - -# Entropy Source and DRNG Manager (ESDM) bundle version used to test the ESDM adapter -ESDM_VERSION="1.2.0" +ANDROID_NDK="android-ndk-r29" # The version of the Intel SDE tool to use for running the Intel SDE tests -INTEL_SDE_VERSION="sde-external-9.38.0-2024-04-18-lin" +INTEL_SDE_VERSION="sde-external-10.8.0-2026-03-15-lin" + +# Git repository URL for Wycheproof test vectors +WYCHEPROOF_GIT_URL=https://github.com/C2SP/wycheproof.git -# Limbo test suite revision to be used in run_limbo_tests.py -LIMBO_TEST_SUITE_REVISION="ec604cf2b1eebe22c6ffc40e380517c6d49c78cc" +# Git repository URL for NIST ACVP test vectors +ACVP_SERVER_GIT_URL=https://github.com/usnistgov/ACVP-Server.git # The maximum size of the compiler cache in CI # Those variables are directly consumed by ccache and sccache respectively -CCACHE_MAXSIZE="200M" -SCCACHE_CACHE_SIZE="200M" +CCACHE_MAXSIZE="300M" +SCCACHE_CACHE_SIZE="300M" diff -Nru botan3-3.7.1+dfsg/src/configs/sphinx/conf.py botan3-3.12.0+dfsg/src/configs/sphinx/conf.py --- botan3-3.7.1+dfsg/src/configs/sphinx/conf.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/configs/sphinx/conf.py 2026-05-07 01:38:28.000000000 +0000 @@ -101,7 +101,7 @@ try: # On Arch this is python-sphinx-furo - import furo + import furo # noqa: F401 html_theme = "furo" # Add a small edit button to each document to allow visitors to easily @@ -111,7 +111,7 @@ 'source_branch': 'master', 'source_directory': 'doc/', } -except ImportError as e: +except ImportError: print("Could not import furo theme; falling back to agago") html_theme = 'agogo' html_theme_path = [] @@ -234,3 +234,9 @@ # Make sure the target is unique autosectionlabel_prefix_document = True + +# -- Options for texinfo output -------------------------------------------------- +authors = 'The Botan Authors' + +# Show URL addresses after external links, options are 'inline', 'footnote' and 'no' +texinfo_show_urls = 'inline' diff -Nru botan3-3.7.1+dfsg/src/configs/typos.toml botan3-3.12.0+dfsg/src/configs/typos.toml --- botan3-3.7.1+dfsg/src/configs/typos.toml 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/configs/typos.toml 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,61 @@ +[files] +extend-exclude = [ + "src/lib/prov/pkcs11/pkcs11.h", + "src/build-data/cc/msvc.txt", + "src/build-data/cc/clangcl.txt", + "doc/authors.txt", +] + +[default] +extend-ignore-re = [ + "\\b[0-9A-Za-z+/]{20,80}(=|==)?\\b", + "\\b[0-9A-Fa-f]{9,80}\\b", +] + +[default.extend-words] +# This is a hack for dealing with hex +BA = "BA" +ba = "ba" + +# These should be removed if possible +Probablistic = "Probablistic" # pkcs11.h typo +divisable = "divisable" # Typo in Limbo test data + +# Weird names only used in a few modules +Lik = "Lik" # TODO(Botan4) remove when mce is removed +Projet = "Projet" # TODO(Botan4) remove when mce is removed + +# Names that typos doesn't know about +EMAC = "EMAC" +GOST = "GOST" +EDE = "EDE" +vor = "vor" + +# clang-analyzer +optin = "optin" + +# Abbreviations used locally, some not ideal +chello = "chello" +issu = "issu" +parm = "parm" +ser = "ser" +stuf = "stuf" +typ = "typ" +indx = "indx" +ACI = "ACI" + +[default.extend-identifiers] +countr_zero = "countr_zero" +Tru64 = "Tru64" + +# ARIA "FO" rounds makes typos upset +ARIA_FO = "ARIA_FO" +FO = "FO" +apply_fo_sbox = "apply_fo_sbox" +aria_fo_sbox = "aria_fo_sbox" +aria_fo = "aria_fo" +aria_fo_m = "aria_fo_m" +fo_pre_const = "fo_pre_const" +fo_post_const = "fo_post_const" +fo_pre_mat = "fo_pre_mat" +fo_post_mat = "fo_post_mat" diff -Nru botan3-3.7.1+dfsg/src/configs/zizmor.yml botan3-3.12.0+dfsg/src/configs/zizmor.yml --- botan3-3.7.1+dfsg/src/configs/zizmor.yml 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/configs/zizmor.yml 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,8 @@ +rules: + unpinned-uses: + config: + policies: + actions/*: ref-pin + google/oss-fuzz/*: ref-pin + github/codeql-action/*: ref-pin + "*": hash-pin diff -Nru botan3-3.7.1+dfsg/src/ct_selftest/ct_selftest.cpp botan3-3.12.0+dfsg/src/ct_selftest/ct_selftest.cpp --- botan3-3.7.1+dfsg/src/ct_selftest/ct_selftest.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/ct_selftest/ct_selftest.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -16,7 +16,9 @@ #include #include +#include +#include #include #include @@ -135,7 +137,7 @@ std::array output_bytes; std::memset(output_bytes.data(), 0x42, sizeof(output_bytes)); - // This mimicks what went wrong in Kyber's secret message expansion + // This mimics what went wrong in Kyber's secret message expansion // that was found by PQShield in Kyber's reference implementation and // was fixed in https://github.com/randombit/botan/pull/4107. // @@ -332,7 +334,7 @@ return 1; } -#if !defined(BOTAN_CT_POISON_ENABLED) +#if !defined(BOTAN_HAS_VALGRIND) std::cout << "The CT::poison API is disabled in this build, this test won't do anything useful\n" << "Configure with a compatible checker (e.g. --with-valgrind) to make the magic happen." << std::endl; return 1; diff -Nru botan3-3.7.1+dfsg/src/editors/vscode/extensions.json botan3-3.12.0+dfsg/src/editors/vscode/extensions.json --- botan3-3.7.1+dfsg/src/editors/vscode/extensions.json 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/editors/vscode/extensions.json 2026-05-07 01:38:28.000000000 +0000 @@ -1,14 +1,9 @@ { - // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. - // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp - // List of extensions which should be recommended for users of this workspace. "recommendations": [ + "llvm-vs-code-extensions.vscode-clangd", "ms-vscode.cpptools", + "ms-vscode.cpptools-themes", "ms-python.python", - "ms-python.pylint", - "xaver.clang-format", "EditorConfig.EditorConfig" - ], - // List of extensions recommended by VS Code that should not be recommended for users of this workspace. - "unwantedRecommendations": [] + ] } diff -Nru botan3-3.7.1+dfsg/src/editors/vscode/launch.json botan3-3.12.0+dfsg/src/editors/vscode/launch.json --- botan3-3.7.1+dfsg/src/editors/vscode/launch.json 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/editors/vscode/launch.json 2026-05-07 01:38:28.000000000 +0000 @@ -1,19 +1,30 @@ { + "inputs": [ + { + "id": "testSelection", + "type": "promptString", + "description": "Which test would you like to run? (see './botan-test --list-tests')", + "args": { + "prompt": "Test Selection" + } + } + ], "configurations": [ { - "name": "Debug Unittests", "name": "Debug Unittests (gdb)", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/botan-test", "args": [ - "--test-threads=1" + "--test-threads=1", + "${input:testSelection}" ], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, "MIMode": "gdb", + "preLaunchTask": "Build Unittests", "setupCommands": [ { "description": "Enable pretty-printing for gdb", @@ -33,12 +44,48 @@ "request": "launch", "program": "${workspaceFolder}/botan-test.exe", "args": [ - "--test-threads=1" + "--test-threads=1", + "${input:testSelection}" ], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], - "console": "externalTerminal" + "console": "externalTerminal", + "preLaunchTask": "Build Unittests" + }, + { + // Debugging of BoGo tests: + // + // 1. Run the BoGo tests with the --wait-for-debugger option + // $> src/editors/vscode/scripts/bogo.py --wait-for-debugger '' + // 2. Start this debugger configuration from VS Code and + // select the 'botan_bogo_shim' process in the process picker + // 3. Wait for the BoGo test to start the test + // + // Note that attaching might fail due to missing privileges. + // In that case, you can try to first run the following command: + // $> sudo sysctl kernel.yama.ptrace_scope=0 + "name": "Debug BoGo (gdb)", + "type": "cppdbg", + "request": "attach", + "processId":"${command:pickProcess}", + "program": "${workspaceFolder}/botan_bogo_shim", + "stopAtEntry": true, + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ] } ] } diff -Nru botan3-3.7.1+dfsg/src/editors/vscode/scripts/bogo.py botan3-3.12.0+dfsg/src/editors/vscode/scripts/bogo.py --- botan3-3.7.1+dfsg/src/editors/vscode/scripts/bogo.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/editors/vscode/scripts/bogo.py 2026-05-07 01:38:28.000000000 +0000 @@ -1,7 +1,7 @@ #!/usr/bin/env python3 +import argparse import os -import sys from common import run_cmd, get_concurrency @@ -12,10 +12,30 @@ BOGO_PATH = os.path.join(BORING_PATH, "ssl", "test", "runner") SHIM_PATH = "./botan_bogo_shim" +SHIM_CONFIG_NO_TLS13 = "src/bogo_shim/config_no_tls13.json" +SHIM_CONFIG_NO_TLS12 = "src/bogo_shim/config_no_tls12.json" SHIM_CONFIG = "src/bogo_shim/config.json" def main(): + parser = argparse.ArgumentParser(description='Run BoringSSL Bogo tests with Botan shim') + parser.add_argument('--without-tls-12', action='store_true', + help='Use shim config that disables TLS 1.2') + parser.add_argument('--without-tls-13', action='store_true', + help='Use shim config that disables TLS 1.3') + parser.add_argument('--wait-for-debugger', action='store_true', + help='BoGo waits for some seconds so that we can attach a debugger to the shim') + parser.add_argument('bogo_args', nargs=argparse.REMAINDER, help='Extra args for the bogo runner') + args = parser.parse_args() + + # Select config depending on the option + if args.without_tls_13: + config_path = SHIM_CONFIG_NO_TLS13 + elif args.without_tls_12: + config_path = SHIM_CONFIG_NO_TLS12 + else: + config_path = SHIM_CONFIG + if not os.path.isdir(BORING_PATH): # check out our fork of boring ssl run_cmd("git clone --depth 1 --branch %s %s %s" % @@ -24,12 +44,15 @@ # make doubly sure we're on the correct branch run_cmd("git -C %s checkout %s" % (BORING_PATH, BORING_BRANCH)) - extra_args = "-debug -test '%s'" % ';'.join( - sys.argv[1:]) if len(sys.argv) > 1 else '' + bogo_args = ';'.join(args.bogo_args) if args.bogo_args else '' + extra_args = "-wait-for-debugger " if args.wait_for_debugger else "" + extra_args += "-skip-tls12 -skip-dtls " if args.without_tls_12 else "" + extra_args += "-skip-tls13 " if args.without_tls_13 else "" + extra_args += "-debug -test '%s'" % bogo_args if bogo_args else '' run_cmd("go test -pipe -num-workers %d -shim-path %s -shim-config %s %s" % - (get_concurrency(), os.path.abspath(SHIM_PATH), os.path.abspath(SHIM_CONFIG), extra_args), BOGO_PATH) - + (get_concurrency(), os.path.abspath(SHIM_PATH), os.path.abspath(config_path), extra_args), + BOGO_PATH) if __name__ == '__main__': main() diff -Nru botan3-3.7.1+dfsg/src/editors/vscode/scripts/test.py botan3-3.12.0+dfsg/src/editors/vscode/scripts/test.py --- botan3-3.7.1+dfsg/src/editors/vscode/scripts/test.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/editors/vscode/scripts/test.py 2026-05-07 01:38:28.000000000 +0000 @@ -2,43 +2,57 @@ import os import re -import sys +import argparse +import glob import common - TESTS_DIR = "src/tests" -def get_test_names_from(test_file): +def discover_tests_in_file(test_file): if not os.path.dirname(test_file) == TESTS_DIR: - raise common.BuildError( - 'Given file path is not a Botan unit test: ' + test_file) + return [] with open(test_file, 'r', encoding='utf-8') as f: find_test_registration = \ re.compile( - r'BOTAN_REGISTER_TEST(_FN)?\s*\(\s*\"(.+)\",\s*\"(.+)\",[^)]+\)') + r'BOTAN_REGISTER_[A-Z_]*TEST(_FN)?\s*\(\s*\"(.+)\",\s*\"(.+)\",[^)]+\)') matches = find_test_registration.findall(f.read()) - tests = [match[-1] for match in matches] - - if not tests: - raise common.BuildError( - 'Failed to find a BOTAN_REGISTER_TEST in the given test file: ' + test_file) + return [match[-1] for match in matches] - return tests +def discover_tests(args): + tests = [] + if args.test_src_file: + tests = discover_tests_in_file(args.test_src_file) + + if args.list and not tests: + # Apparently 'test_src_file' didn't contain any tests, lets + # go ahead and discover all unit tests in the src/tests dir. + test_files = glob.glob(os.path.join(TESTS_DIR, '*.cpp'), recursive=False) + for test_file in test_files: + tests += discover_tests_in_file(test_file) + return sorted(set(tests)) def main(): test_binary = os.path.join('.', common.get_test_binary_name()) + args = argparse.ArgumentParser(description='Run Botan tests') + args.add_argument('--list', action='store_true', default=False, help='List all available tests') + args.add_argument('test_src_file', nargs='?', help='Path to the test source file') + parsed_args = args.parse_args() + + discovered_tests = discover_tests(parsed_args) + + if parsed_args.list: + print("\n".join(discovered_tests)) + return + + if not parsed_args.test_src_file: + discovered_tests.clear() - if len(sys.argv) == 2: - test_src_file = sys.argv[1] - test_names = get_test_names_from(test_src_file) - common.run_cmd("%s %s" % (test_binary, ' '.join(test_names))) - else: - common.run_cmd(test_binary) + common.run_cmd(" ".join([test_binary, *discovered_tests])) if __name__ == '__main__': diff -Nru botan3-3.7.1+dfsg/src/editors/vscode/settings.json botan3-3.12.0+dfsg/src/editors/vscode/settings.json --- botan3-3.7.1+dfsg/src/editors/vscode/settings.json 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/editors/vscode/settings.json 2026-05-07 01:38:28.000000000 +0000 @@ -1,16 +1,11 @@ { "[cpp]": { "editor.formatOnSave": true, - "editor.defaultFormatter": "xaver.clang-format" + "editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd" }, - "C_Cpp.codeAnalysis.clangTidy.enabled": true, - "C_Cpp.default.cppStandard": "c++20", - "C_Cpp.default.cStandard": "c17", - "C_Cpp.formatting": "disabled", "clangd.arguments": [ "--header-insertion=never" ], "clang-format.executable": "clang-format-17", - "pylint.args": [ - "--rcfile=src/configs/pylint.rc"], + "pylint.args": ["--rcfile=src/configs/pylint.rc"] } diff -Nru botan3-3.7.1+dfsg/src/editors/vscode/tasks.json botan3-3.12.0+dfsg/src/editors/vscode/tasks.json --- botan3-3.7.1+dfsg/src/editors/vscode/tasks.json 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/editors/vscode/tasks.json 2026-05-07 01:38:28.000000000 +0000 @@ -2,27 +2,19 @@ "version": "2.0.0", "tasks": [ { - "label": "Configure (gcc)", - "detail": "Default ./configure.py invocation for gcc. Run your own if you want.", + "label": "Configure", + "detail": "Default ./configure.py invocation. Run your own if you want.", "group": "build", "type": "shell", - "command": "python3 ./configure.py --cc gcc --compiler-cache=ccache --build-tool=ninja --without-documentation --debug-mode --build-targets=\"static,tests,bogo_shim\"", - "presentation": { - "reveal": "always", - "panel": "shared", - "close": false - } - }, - { - "label": "Configure (msvc)", - "detail": "Default ./configure.py invocation for msvc. Run your own if you want.", - "group": "build", - "type": "shell", - "shell": { - "args": [ - ] + "linux": { + "command": "./configure.py --compiler-cache=ccache --build-tool=ninja --without-documentation --debug-mode --build-targets=\"static,tests,cli,bogo_shim\"" + }, + "osx": { + "command": "./configure.py --compiler-cache=ccache --build-tool=ninja --without-documentation --debug-mode --build-targets=\"static,tests,cli\"" + }, + "windows": { + "command": "./configure.py --compiler-cache=sccache.exe --build-tool=ninja --link-method=hardlink --without-documentation --debug-mode --build-targets=\"static,tests,cli\"", }, - "command": "python ./configure.py --cc msvc --compiler-cache=sccache.exe --build-tool=ninja --link-method=hardlink --without-documentation --debug-mode --build-targets=\"static,tests\"", "presentation": { "reveal": "always", "panel": "shared", @@ -31,14 +23,18 @@ }, { "label": "Build All", - "group": "build", + "group": { + "kind": "build", + "isDefault": true + }, "type": "shell", + "dependsOn": "Configure", "linux": { - "command": "make -j $(nproc)", + "command": "ninja", "problemMatcher": "$gcc" }, "osx": { - "command": "make -j $(sysctl -n hw.logicalcpu)", + "command": "ninja", "problemMatcher": "$gcc" }, "windows": { @@ -55,90 +51,53 @@ "label": "Build Unittests", "group": "build", "type": "shell", + "dependsOn": "Configure", "linux": { - "command": "make -j $(nproc) tests" + "command": "ninja tests", + "problemMatcher": "$gcc" }, "osx": { - "command": "make -j $(sysctl -n hw.logicalcpu) tests" + "command": "ninja tests", + "problemMatcher": "$gcc" + }, + "windows": { + "command": "ninja tests", + "problemMatcher": "$msCompile" }, "presentation": { "reveal": "always", "panel": "shared", "close": false }, - "problemMatcher": "$gcc" }, { "label": "Build BoGo Shim", "group": "build", "type": "shell", + "dependsOn": "Configure", "linux": { - "command": "make -j $(nproc) bogo_shim" + "command": "ninja bogo_shim", + "problemMatcher": "$gcc" }, "osx": { - "command": "make -j $(sysctl -n hw.logicalcpu) bogo_shim" + "command": "ninja bogo_shim", + "problemMatcher": "$gcc" }, "presentation": { "reveal": "always", "panel": "shared", "close": true }, - "problemMatcher": "$gcc" }, { "label": "Run Unittests", - "detail": "run all unittests", - "group": "test", - "type": "shell", - "command": "python3 ${workspaceFolder}/src/editors/vscode/scripts/test.py", - "dependsOn": "Build Unittests", - "presentation": { - "reveal": "always", - "panel": "shared", - "close": false + "detail": "opportunistically runs the currently open unit test file or all unit tests", + "group": { + "kind": "test", + "isDefault": true }, - "problemMatcher": [ - { - "owner": "cpp", - "pattern": { - "regexp": "^Failure \\d+: (.+Internal error: False assertion .*) @(.*):(.*)$", - "message": 1, - "file": 2, - "line": 3, - "column": 0, - "endColumn": 0 - } - }, - { - "owner": "cpp", - "pattern": { - "regexp": "Failure \\d+: (.*) \\(at ([^:]+):(\\d+)\\)", - "message": 1, - "file": 2, - "line": 3, - "column": 0, - "endColumn": 0 - } - }, - { - "owner": "cpp", - "pattern": { - "regexp": "Failure \\d+: (.*)", - "message": 1, - "file": 0, - "line": 0, - "column": 0, - "endColumn": 0 - } - } - ] - }, - { - "label": "Run Current Unittest File", - "detail": "run the unittest file that is currently in focus", - "group": "test", "type": "shell", - "command": "python3 ${workspaceFolder}/src/editors/vscode/scripts/test.py ${relativeFile}", + "command": "${workspaceFolder}/src/editors/vscode/scripts/test.py ${relativeFile}", "dependsOn": "Build Unittests", "presentation": { "reveal": "always", @@ -185,7 +144,7 @@ "label": "Run BoGo Tests", "group": "test", "type": "shell", - "command": "python3 ${workspaceFolder}/src/editors/vscode/scripts/bogo.py", + "command": "${workspaceFolder}/src/editors/vscode/scripts/bogo.py", "dependsOn": "Build BoGo Shim", "presentation": { "reveal": "always", @@ -206,6 +165,18 @@ } ] } - } + }, + { + "label": "List Unittests", + "detail": "Lists all available unit tests", + "group": "test", + "type": "shell", + "command": "${workspaceFolder}/src/editors/vscode/scripts/test.py --list ${relativeFile}", + "presentation": { + "reveal": "always", + "panel": "shared", + "close": false + } + }, ] } diff -Nru botan3-3.7.1+dfsg/src/examples/check_key.cpp botan3-3.12.0+dfsg/src/examples/check_key.cpp --- botan3-3.7.1+dfsg/src/examples/check_key.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/check_key.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -5,7 +5,7 @@ #include int main() { - Botan::X509_Certificate cert("cert.pem"); + const Botan::X509_Certificate cert("cert.pem"); Botan::AutoSeeded_RNG rng; auto key = cert.subject_public_key(); if(!key->check_key(rng, false)) { diff -Nru botan3-3.7.1+dfsg/src/examples/custom_system_rng.cpp botan3-3.12.0+dfsg/src/examples/custom_system_rng.cpp --- botan3-3.7.1+dfsg/src/examples/custom_system_rng.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/custom_system_rng.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -13,6 +13,7 @@ */ #include +#include class MySoC_RandomNumberGenerator final : public Botan::Hardware_RNG { public: @@ -80,12 +81,8 @@ */ }; -#include - int main() { MySoC_RandomNumberGenerator my_rng; - printf("%d\n", my_rng.next_byte()); - - return 0; + return my_rng.next_byte(); } diff -Nru botan3-3.7.1+dfsg/src/examples/ecc_raw_private_key.cpp botan3-3.12.0+dfsg/src/examples/ecc_raw_private_key.cpp --- botan3-3.7.1+dfsg/src/examples/ecc_raw_private_key.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/ecc_raw_private_key.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,5 @@ #include +#include #include #include #include diff -Nru botan3-3.7.1+dfsg/src/examples/ecc_raw_public_key.cpp botan3-3.12.0+dfsg/src/examples/ecc_raw_public_key.cpp --- botan3-3.7.1+dfsg/src/examples/ecc_raw_public_key.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/ecc_raw_public_key.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,3 +1,4 @@ +#include #include #include #include diff -Nru botan3-3.7.1+dfsg/src/examples/ecdh.cpp botan3-3.12.0+dfsg/src/examples/ecdh.cpp --- botan3-3.7.1+dfsg/src/examples/ecdh.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/ecdh.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -14,18 +14,18 @@ const std::string kdf = "KDF2(SHA-256)"; // the two parties generate ECDH keys - Botan::ECDH_PrivateKey key_a(rng, domain); - Botan::ECDH_PrivateKey key_b(rng, domain); + const Botan::ECDH_PrivateKey key_a(rng, domain); + const Botan::ECDH_PrivateKey key_b(rng, domain); // now they exchange their public values const auto key_apub = key_a.public_value(); const auto key_bpub = key_b.public_value(); // Construct key agreements and agree on a shared secret - Botan::PK_Key_Agreement ka_a(key_a, rng, kdf); + const Botan::PK_Key_Agreement ka_a(key_a, rng, kdf); const auto sA = ka_a.derive_key(32, key_bpub).bits_of(); - Botan::PK_Key_Agreement ka_b(key_b, rng, kdf); + const Botan::PK_Key_Agreement ka_b(key_b, rng, kdf); const auto sB = ka_b.derive_key(32, key_apub).bits_of(); if(sA != sB) { diff -Nru botan3-3.7.1+dfsg/src/examples/ecdsa.cpp botan3-3.12.0+dfsg/src/examples/ecdsa.cpp --- botan3-3.7.1+dfsg/src/examples/ecdsa.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/ecdsa.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,7 +10,7 @@ Botan::AutoSeeded_RNG rng; // Generate ECDSA keypair const auto group = Botan::EC_Group::from_name("secp521r1"); - Botan::ECDSA_PrivateKey key(rng, group); + const Botan::ECDSA_PrivateKey key(rng, group); const std::string message("This is a tasty burger!"); diff -Nru botan3-3.7.1+dfsg/src/examples/entropy.cpp botan3-3.12.0+dfsg/src/examples/entropy.cpp --- botan3-3.7.1+dfsg/src/examples/entropy.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/entropy.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -14,8 +14,8 @@ // includes needed for example Entropy_Source implementations #include #include -#include #include +#include // includes needed for main #include @@ -103,7 +103,7 @@ */ for(size_t i = 0; i != poll_goal; ++i) { - uint64_t timer = high_resolution_timer(); + const uint64_t timer = high_resolution_timer(); // If the timer is fast, this loop will almost always exit immediately and // the counter will just be zero. diff -Nru botan3-3.7.1+dfsg/src/examples/ffi.c botan3-3.12.0+dfsg/src/examples/ffi.c --- botan3-3.7.1+dfsg/src/examples/ffi.c 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/ffi.c 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,53 @@ +/* The two headers we guarantee to be parseable as C are ffi.h and build.h */ +#include + +#if defined(BOTAN_HAS_FFI) + #include +#else + #error "The C89 interface is not available in this build" +#endif + +#include +#include + +#define CHECK_RC(rc) \ + do { \ + if(rc != BOTAN_FFI_SUCCESS) { \ + printf("Call failed rc=%d (%s)\n", rc, botan_error_description(rc)); \ + return 1; \ + } \ + } while(0) + +int main() { + uint8_t digest[32]; + char hex[64 + 1] = {0}; + const char* str_to_hash = "Hello world"; + int rc = 0; + + printf("This is %s\n", botan_version_string()); + +#if defined(BOTAN_HAS_SHA_256) + botan_hash_t hash; + rc = botan_hash_init(&hash, "SHA-256", 0); + CHECK_RC(rc); + + rc = botan_hash_update(hash, (const uint8_t*)str_to_hash, strlen(str_to_hash)); + CHECK_RC(rc); + + rc = botan_hash_final(hash, digest); + CHECK_RC(rc); + + rc = botan_hash_destroy(hash); + CHECK_RC(rc); + + rc = botan_hex_encode(digest, sizeof(digest), hex, sizeof(hex)); + CHECK_RC(rc); + + printf("SHA-256(\"%s\") = %s\n", str_to_hash, hex); + +#else + printf("SHA-256 not included in the build\n"); +#endif + + return 0; +} diff -Nru botan3-3.7.1+dfsg/src/examples/fpe_alnum.cpp botan3-3.12.0+dfsg/src/examples/fpe_alnum.cpp --- botan3-3.7.1+dfsg/src/examples/fpe_alnum.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/fpe_alnum.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include + +namespace { + +constexpr size_t power(size_t b, size_t e) { + size_t p = 1; + + for(size_t i = 0; i != e; ++i) { + p *= b; + } + + return p; +} + +/* +* This example FPE encrypts strings of length 10 which +* are in [A-Z0-9], ie radix 36. +*/ +constexpr size_t LEN = 10; +constexpr size_t RADIX = 26 + 10; +constexpr size_t POWER = power(RADIX, LEN); + +size_t to_radix(char c) { + if(c >= '0' && c <= '9') { + return c - '0'; + } else if(c >= 'A' && c <= 'Z') { + return c - 'A' + 10; + } else { + throw std::invalid_argument("String contains unexpected character"); + } +} + +// Map from the string to an integer in [0,RADIX**LEN) +Botan::BigInt rank(std::string_view s) { + if(s.size() != LEN) { + throw std::invalid_argument("Cannot FPE encrypt string of incorrect length"); + } + + Botan::BigInt z = 0; + + for(size_t i = 0; i != LEN; ++i) { + z = z * RADIX + to_radix(s[i]); + } + + return z; +} + +char from_radix(size_t c) { + if(c <= 9) { + return static_cast(c + '0'); + } else if(c <= 35) { + return static_cast(c + 'A' - 10); + } else { + throw std::invalid_argument("Output contains unexpected character"); + } +} + +// Map from an integer in [0,RADIX**LEN) to the string +std::string derank(Botan::BigInt z) { + std::string s; + + for(size_t i = 0; i != LEN; ++i) { + const auto zi = z % RADIX; + s.push_back(from_radix(zi)); + z /= RADIX; + } + + std::reverse(s.begin(), s.end()); + + return s; +} + +} // namespace + +int main(int argc, char* argv[]) { + if(argc <= 3) { + std::cerr << "Usage: " << argv[0] << " \n"; + return 1; + } + + try { + const bool encrypt = [=]() { + const std::string arg1(argv[1]); + if(arg1 == "encrypt") { + return true; + } else if(arg1 == "decrypt") { + return false; + } else { + throw std::invalid_argument("Expected 'encrypt' or 'decrypt' not " + arg1); + } + }(); + + const auto key = Botan::hex_decode(argv[2]); + + Botan::FPE_FE1 fpe(Botan::BigInt::from_u64(POWER)); + fpe.set_key(key); + + for(size_t i = 3; argv[i] != nullptr; ++i) { + /* + * The tweak ensures that even if the same input is encrypted more than + * once it produces a different output. The same tweak must be used for + * decryption. Commonly this is available, eg a database row id. If not + * available then the tweak can be set to a constant. + */ + const uint64_t tweak = static_cast(i - 3); + + auto z = rank(std::string(argv[i])); + auto enc_z = encrypt ? fpe.encrypt(z, tweak) : fpe.decrypt(z, tweak); + auto enc_word = derank(enc_z); + std::cout << enc_word << " "; + } + std::cout << "\n"; + return 0; + } catch(std::exception& e) { + std::cout << e.what() << "\n"; + return 2; + } +} diff -Nru botan3-3.7.1+dfsg/src/examples/fpe_dictionary.cpp botan3-3.12.0+dfsg/src/examples/fpe_dictionary.cpp --- botan3-3.7.1+dfsg/src/examples/fpe_dictionary.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/fpe_dictionary.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include + +class Dictionary { + public: + explicit Dictionary(const std::string& filename) { + std::ifstream in(filename); + + while(in.good()) { + std::string word; + std::getline(in, word); + m_dict.push_back(word); + } + + std::sort(m_dict.begin(), m_dict.end()); + } + + size_t rank(const std::string& word) const { + auto i = std::lower_bound(m_dict.begin(), m_dict.end(), word); + + const size_t r = i - m_dict.begin(); + + if(m_dict[r] != word) { + throw std::runtime_error("The word " + word + " does not appear in the dictionary"); + } + + return r; + } + + std::string derank(size_t rank) const { return m_dict.at(rank); } + + size_t size() const { return m_dict.size(); } + + private: + std::vector m_dict; +}; + +int main(int argc, char* argv[]) { + if(argc <= 4) { + std::cerr << "Usage: " << argv[0] << " words...\n"; + return 1; + } + + try { + const bool encrypt = [=]() { + const std::string arg1(argv[1]); + if(arg1 == "encrypt") { + return true; + } else if(arg1 == "decrypt") { + return false; + } else { + throw std::invalid_argument("Expected 'encrypt' or 'decrypt' not " + arg1); + } + }(); + const Dictionary dict(argv[2]); + const auto key = Botan::hex_decode(argv[3]); + + Botan::FPE_FE1 fpe(Botan::BigInt::from_u64(dict.size())); + fpe.set_key(key); + + for(size_t i = 4; argv[i] != nullptr; ++i) { + /* + * The tweak ensures that even if the same input is encrypted more than + * once it produces a different output. The same tweak must be used for + * decryption. Commonly this is available, eg a database row id. If not + * available then the tweak can be set to a constant. + */ + const uint64_t tweak = static_cast(i - 4); + + auto z = Botan::BigInt(dict.rank(std::string(argv[i]))); + auto enc_z = encrypt ? fpe.encrypt(z, tweak) : fpe.decrypt(z, tweak); + auto enc_word = dict.derank(enc_z.word_at(0)); + std::cout << enc_word << " "; + } + std::cout << "\n"; + return 0; + } catch(std::exception& e) { + std::cout << e.what() << "\n"; + return 2; + } +} diff -Nru botan3-3.7.1+dfsg/src/examples/hash.cpp botan3-3.12.0+dfsg/src/examples/hash.cpp --- botan3-3.7.1+dfsg/src/examples/hash.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/hash.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,8 +11,8 @@ while(std::cin.good()) { // read STDIN to buffer - std::cin.read(reinterpret_cast(buf.data()), buf.size()); - size_t readcount = std::cin.gcount(); + std::cin.read(reinterpret_cast(buf.data()), static_cast(buf.size())); + const auto readcount = static_cast(std::cin.gcount()); // update hash computations with read data hash1->update(std::span{buf}.first(readcount)); hash2->update(std::span{buf}.first(readcount)); diff -Nru botan3-3.7.1+dfsg/src/examples/hmac.cpp botan3-3.12.0+dfsg/src/examples/hmac.cpp --- botan3-3.7.1+dfsg/src/examples/hmac.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/hmac.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -23,12 +23,12 @@ const auto key = rng.random_vec(32); // 256 bit random key // "Message" != "Mussage" so tags will also not match - std::string tag1 = compute_mac("Message", key); - std::string tag2 = compute_mac("Mussage", key); + const std::string tag1 = compute_mac("Message", key); + const std::string tag2 = compute_mac("Mussage", key); assert(tag1 != tag2); // Recomputing with original input message results in identical tag - std::string tag3 = compute_mac("Message", key); + const std::string tag3 = compute_mac("Message", key); assert(tag1 == tag3); return 0; diff -Nru botan3-3.7.1+dfsg/src/examples/hybrid_encryption.cpp botan3-3.12.0+dfsg/src/examples/hybrid_encryption.cpp --- botan3-3.7.1+dfsg/src/examples/hybrid_encryption.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/hybrid_encryption.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -36,7 +36,7 @@ sym_cipher->finish(d.ciphertext); // encrypt the symmetric key using RSA with a secure padding scheme - Botan::PK_Encryptor_EME asym_cipher(*pubkey, rng, "EME-OAEP(SHA-256,MGF1)"); + const Botan::PK_Encryptor_EME asym_cipher(*pubkey, rng, "EME-OAEP(SHA-256,MGF1)"); d.encryptedKey = asym_cipher.encrypt(key, rng); return d; @@ -48,7 +48,7 @@ Botan::secure_vector plaintext = encdata.ciphertext; // decrypt the symmetric key - Botan::PK_Decryptor_EME asym_cipher(privkey, rng, "EME-OAEP(SHA-256,MGF1)"); + const Botan::PK_Decryptor_EME asym_cipher(privkey, rng, "EME-OAEP(SHA-256,MGF1)"); const auto key = asym_cipher.decrypt(encdata.encryptedKey); // decrypt the data symmetrically diff -Nru botan3-3.7.1+dfsg/src/examples/hybrid_key_encapsulation.cpp botan3-3.12.0+dfsg/src/examples/hybrid_key_encapsulation.cpp --- botan3-3.7.1+dfsg/src/examples/hybrid_key_encapsulation.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/hybrid_key_encapsulation.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -27,10 +27,16 @@ 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)); + if(m_kem_pk == nullptr || m_kex_pk == nullptr) { + throw std::runtime_error("Null arguments not allowed"); + } + + if(m_kex_pk->supports_operation(Botan::PublicKeyOperation::KeyAgreement)) { + throw std::runtime_error("The kex key must support key agreement"); + } + if(m_kex_pk->supports_operation(Botan::PublicKeyOperation::KeyEncapsulation)) { + throw std::runtime_error("The kem key must support key encapsulation"); + } } std::string algo_name() const override { @@ -165,9 +171,7 @@ 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); - } + m_kdf(Botan::KDF::create_or_throw(kdf)) {} /** * This returns the length of the encapsulated key in bytes. For such a @@ -214,7 +218,7 @@ // // 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"); + const 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 @@ -236,7 +240,11 @@ // 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()); + + if(out_encapsed_key.size() != kex_encapsed_key.size() + kem_encapsed_key.size()) { + throw std::runtime_error("The output span is not the expected 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()); @@ -252,7 +260,10 @@ concat_shared_key.insert(concat_shared_key.end(), kem_encapsed_key.begin(), kem_encapsed_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); + if(out_shared_key.size() < desired_shared_key_length) { + throw std::runtime_error("The output span is smaller than the requested length"); + } + m_kdf->derive_key(out_shared_key.first(desired_shared_key_length), concat_shared_key, salt, {}); } @@ -276,9 +287,7 @@ 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); - } + m_kdf(Botan::KDF::create_or_throw(kdf)) {} /** * This returns the length of the encapsulated key in bytes. For such a @@ -303,7 +312,9 @@ std::span encapsulated_key, size_t desired_shared_key_length, std::span salt) override { - BOTAN_ASSERT_NOMSG(encapsulated_key.size() == encapsulated_key_length()); + if(encapsulated_key.size() != encapsulated_key_length()) { + throw std::runtime_error("The provided encapsulated key is not of the expected length"); + } // The basic idea of the hybrid operation: // 1. Extract the ephemeral public key and the KEM's encapsulation @@ -340,7 +351,9 @@ concat_shared_key.insert(concat_shared_key.end(), kem_encapsed_key.begin(), kem_encapsed_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); + if(out_shared_key.size() < desired_shared_key_length) { + throw std::runtime_error("The output buffer is smaller than the requested key length"); + } m_kdf->derive_key(out_shared_key.first(desired_shared_key_length), concat_shared_key, salt, {}); } @@ -353,13 +366,13 @@ } // namespace -std::unique_ptr Hybrid_PublicKey::create_kem_encryption_op(std::string_view params, - std::string_view) const { +std::unique_ptr Hybrid_PublicKey::create_kem_encryption_op( + std::string_view params, std::string_view /*provider*/) 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 { + Botan::RandomNumberGenerator& rng, std::string_view params, std::string_view /*provider*/) const { return std::make_unique(*this, rng, params); } diff -Nru botan3-3.7.1+dfsg/src/examples/ml_kem.cpp botan3-3.12.0+dfsg/src/examples/ml_kem.cpp --- botan3-3.7.1+dfsg/src/examples/ml_kem.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/ml_kem.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,7 +12,7 @@ const auto salt = rng.random_array<16>(); - Botan::ML_KEM_PrivateKey priv_key(rng, Botan::ML_KEM_Mode::ML_KEM_768); + const Botan::ML_KEM_PrivateKey priv_key(rng, Botan::ML_KEM_Mode::ML_KEM_768); auto pub_key = priv_key.public_key(); Botan::PK_KEM_Encryptor enc(*pub_key, kdf); diff -Nru botan3-3.7.1+dfsg/src/examples/password_encryption.cpp botan3-3.12.0+dfsg/src/examples/password_encryption.cpp --- botan3-3.7.1+dfsg/src/examples/password_encryption.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/password_encryption.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ #include -#include #include +#include #include #include @@ -27,7 +27,7 @@ std::span salt, size_t output_length) { // Here, we use statically defined password hash parameters. Alternatively - // you could use Botan::PasswordHashFamily::tune() to automatically select + // you could use Botan::PasswordHashFamily::tune_params() to automatically select // parameters based on your desired runtime and memory usage. // // Defining those parameters highly depends on your use case and the @@ -38,7 +38,7 @@ constexpr size_t p = 2; // parallelism auto pbkdf = Botan::PasswordHashFamily::create_or_throw(pbkdf_algo)->from_params(M, t, p); - BOTAN_ASSERT_NONNULL(pbkdf); + // create_or_throw always either throws or returns a non-null pointer Botan::secure_vector key(output_length); pbkdf->hash(key, password, salt); @@ -57,7 +57,12 @@ // Stretch the password into enough cryptographically strong key material // to initialize the AEAD with a key and nonce (aka. initialization vector). const auto keydata = derive_key_material(password, salt, key_length + nonce_length); - BOTAN_ASSERT_NOMSG(keydata.size() == key_length + nonce_length); + + // The function always returns the requested length but lets check to make sure + if(keydata.size() != key_length + nonce_length) { + throw std::runtime_error("Unexpected output from derive_key_material"); + } + const auto key = std::span{keydata}.first(key_length); const auto nonce = std::span{keydata}.last(nonce_length); @@ -122,17 +127,14 @@ // Note: For simplicity we omit the authentication of any associated data. // If your use case would benefit from it, you should add it. Perhaps // to both the password hashing and the AEAD. - std::string_view password = "geheimnis"; - std::string_view message = "Attack at dawn!"; + const std::string_view password = "geheimnis"; + const std::string_view message = "Attack at dawn!"; try { const auto ciphertext = encrypt_by_password(password, rng, as>(message)); std::cout << "Ciphertext: " << Botan::hex_encode(ciphertext) << "\n"; const auto decrypted_message = decrypt_by_password(password, ciphertext); - BOTAN_ASSERT_NOMSG(message.size() == decrypted_message.size() && - std::equal(message.begin(), message.end(), decrypted_message.begin())); - std::cout << "Decrypted message: " << as(decrypted_message) << "\n"; } catch(const std::exception& ex) { std::cerr << "Something went wrong: " << ex.what() << "\n"; diff -Nru botan3-3.7.1+dfsg/src/examples/pkcs10_csr_on_tpm2.cpp botan3-3.12.0+dfsg/src/examples/pkcs10_csr_on_tpm2.cpp --- botan3-3.7.1+dfsg/src/examples/pkcs10_csr_on_tpm2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/pkcs10_csr_on_tpm2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -17,10 +17,14 @@ #include #include +namespace { + std::span as_byteview(std::string_view str) { return {reinterpret_cast(str.data()), str.size()}; } +} // namespace + int main() { // This TCTI configuration is just an example, adjust as needed! constexpr auto tcti_nameconf = "tabrmd:bus_name=net.randombit.botan.tabrmd,bus_type=session"; diff -Nru botan3-3.7.1+dfsg/src/examples/pkcs11_ecdh.cpp botan3-3.12.0+dfsg/src/examples/pkcs11_ecdh.cpp --- botan3-3.7.1+dfsg/src/examples/pkcs11_ecdh.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/pkcs11_ecdh.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -20,7 +20,7 @@ Botan::PKCS11::Slot slot(module, slots.at(0)); Botan::PKCS11::Session session(slot, false); - Botan::PKCS11::secure_string pin = {'1', '2', '3', '4', '5', '6'}; + const Botan::PKCS11::secure_string pin = {'1', '2', '3', '4', '5', '6'}; session.login(Botan::PKCS11::UserType::User, pin); /************ import ECDH private key *************/ @@ -28,7 +28,7 @@ Botan::AutoSeeded_RNG rng; // create private key in software - Botan::ECDH_PrivateKey priv_key_sw(rng, Botan::EC_Group::from_name("secp256r1")); + const Botan::ECDH_PrivateKey priv_key_sw(rng, Botan::EC_Group::from_name("secp256r1")); // set import properties Botan::PKCS11::EC_PrivateKeyImportProperties priv_import_props(priv_key_sw.DER_domain(), @@ -44,10 +44,10 @@ priv_import_props.set_label(label); // import to card - Botan::PKCS11::PKCS11_ECDH_PrivateKey priv_key(session, priv_import_props); + const Botan::PKCS11::PKCS11_ECDH_PrivateKey priv_key(session, priv_import_props); /************ export ECDH private key *************/ - Botan::ECDH_PrivateKey exported = priv_key.export_key(); + const Botan::ECDH_PrivateKey exported = priv_key.export_key(); /************ import ECDH public key *************/ @@ -65,10 +65,10 @@ pub_import_props.set_label(label); // import - Botan::PKCS11::PKCS11_ECDH_PublicKey pub_key(session, pub_import_props); + const Botan::PKCS11::PKCS11_ECDH_PublicKey pub_key(session, pub_import_props); /************ export ECDH private key *************/ - Botan::ECDH_PublicKey exported_pub = pub_key.export_key(); + const Botan::ECDH_PublicKey exported_pub = pub_key.export_key(); /************ generate ECDH private key *************/ @@ -77,7 +77,7 @@ priv_generate_props.set_private(true); priv_generate_props.set_derive(true); - Botan::PKCS11::PKCS11_ECDH_PrivateKey priv_key2( + const Botan::PKCS11::PKCS11_ECDH_PrivateKey priv_key2( session, Botan::EC_Group::from_name("secp256r1").DER_encode(), priv_generate_props); /************ generate ECDH key pair *************/ @@ -91,22 +91,22 @@ pub_generate_props.set_private(false); pub_generate_props.set_modifiable(true); - Botan::PKCS11::PKCS11_ECDH_KeyPair key_pair = + const Botan::PKCS11::PKCS11_ECDH_KeyPair key_pair = Botan::PKCS11::generate_ecdh_keypair(session, pub_generate_props, priv_generate_props); /************ ECDH derive *************/ - Botan::PKCS11::PKCS11_ECDH_KeyPair key_pair_other = + const Botan::PKCS11::PKCS11_ECDH_KeyPair key_pair_other = Botan::PKCS11::generate_ecdh_keypair(session, pub_generate_props, priv_generate_props); - Botan::PK_Key_Agreement ka(key_pair.second, rng, "Raw", "pkcs11"); - Botan::PK_Key_Agreement kb(key_pair_other.second, rng, "Raw", "pkcs11"); + const Botan::PK_Key_Agreement ka(key_pair.second, rng, "Raw", "pkcs11"); + const Botan::PK_Key_Agreement kb(key_pair_other.second, rng, "Raw", "pkcs11"); - Botan::SymmetricKey alice_key = ka.derive_key(32, key_pair_other.first.raw_public_key_bits()); + const Botan::SymmetricKey alice_key = ka.derive_key(32, key_pair_other.first.raw_public_key_bits()); - Botan::SymmetricKey bob_key = kb.derive_key(32, key_pair.first.raw_public_key_bits()); + const Botan::SymmetricKey bob_key = kb.derive_key(32, key_pair.first.raw_public_key_bits()); - bool eq = alice_key == bob_key; + const bool eq = alice_key == bob_key; return eq ? 0 : 1; } diff -Nru botan3-3.7.1+dfsg/src/examples/pkcs11_ecdsa.cpp botan3-3.12.0+dfsg/src/examples/pkcs11_ecdsa.cpp --- botan3-3.7.1+dfsg/src/examples/pkcs11_ecdsa.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/pkcs11_ecdsa.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -19,7 +19,7 @@ Botan::PKCS11::Slot slot(module, slots.at(0)); Botan::PKCS11::Session session(slot, false); - Botan::PKCS11::secure_string pin = {'1', '2', '3', '4', '5', '6'}; + const Botan::PKCS11::secure_string pin = {'1', '2', '3', '4', '5', '6'}; session.login(Botan::PKCS11::UserType::User, pin); /************ import ECDSA private key *************/ @@ -27,7 +27,7 @@ // create private key in software Botan::AutoSeeded_RNG rng; - Botan::ECDSA_PrivateKey priv_key_sw(rng, Botan::EC_Group::from_name("secp256r1")); + const Botan::ECDSA_PrivateKey priv_key_sw(rng, Botan::EC_Group::from_name("secp256r1")); // set the private key import properties Botan::PKCS11::EC_PrivateKeyImportProperties priv_import_props(priv_key_sw.DER_domain(), @@ -43,10 +43,10 @@ priv_import_props.set_label(label); // import to card - Botan::PKCS11::PKCS11_ECDSA_PrivateKey priv_key(session, priv_import_props); + const Botan::PKCS11::PKCS11_ECDSA_PrivateKey priv_key(session, priv_import_props); /************ export PKCS#11 ECDSA private key *************/ - Botan::ECDSA_PrivateKey priv_exported = priv_key.export_key(); + const Botan::ECDSA_PrivateKey priv_exported = priv_key.export_key(); /************ import ECDSA public key *************/ @@ -63,10 +63,10 @@ label = "test ECDSA pub key"; pub_import_props.set_label(label); - Botan::PKCS11::PKCS11_ECDSA_PublicKey public_key(session, pub_import_props); + const Botan::PKCS11::PKCS11_ECDSA_PublicKey public_key(session, pub_import_props); /************ export PKCS#11 ECDSA public key *************/ - Botan::ECDSA_PublicKey pub_exported = public_key.export_key(); + const Botan::ECDSA_PublicKey pub_exported = public_key.export_key(); /************ generate PKCS#11 ECDSA private key *************/ Botan::PKCS11::EC_PrivateKeyGenerationProperties priv_generate_props; @@ -74,7 +74,7 @@ priv_generate_props.set_private(true); priv_generate_props.set_sign(true); - Botan::PKCS11::PKCS11_ECDSA_PrivateKey pk( + const Botan::PKCS11::PKCS11_ECDSA_PrivateKey pk( session, Botan::EC_Group::from_name("secp256r1").DER_encode(), priv_generate_props); /************ generate PKCS#11 ECDSA key pair *************/ @@ -88,7 +88,7 @@ pub_generate_props.set_private(false); pub_generate_props.set_modifiable(true); - Botan::PKCS11::PKCS11_ECDSA_KeyPair key_pair = + const Botan::PKCS11::PKCS11_ECDSA_KeyPair key_pair = Botan::PKCS11::generate_ecdsa_keypair(session, pub_generate_props, priv_generate_props); /************ PKCS#11 ECDSA sign and verify *************/ @@ -99,7 +99,7 @@ auto signature = signer.sign_message(plaintext, rng); Botan::PK_Verifier token_verifier(key_pair.first, "Raw", Botan::Signature_Format::Standard, "pkcs11"); - bool ecdsa_ok = token_verifier.verify_message(plaintext, signature); + const bool ecdsa_ok = token_verifier.verify_message(plaintext, signature); return ecdsa_ok ? 0 : 1; } diff -Nru botan3-3.7.1+dfsg/src/examples/pkcs11_low_level.cpp botan3-3.12.0+dfsg/src/examples/pkcs11_low_level.cpp --- botan3-3.7.1+dfsg/src/examples/pkcs11_low_level.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/pkcs11_low_level.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -4,14 +4,14 @@ #include int main() { - Botan::PKCS11::Module module("C:\\pkcs11-middleware\\library.dll"); + const Botan::PKCS11::Module module("C:\\pkcs11-middleware\\library.dll"); // C_Initialize is automatically called by the constructor of the Module // work with the token std::vector slot_ids; - [[maybe_unused]] bool success = module->C_GetSlotList(true, slot_ids); + [[maybe_unused]] const bool success = module->C_GetSlotList(true, slot_ids); // C_Finalize is automatically called by the destructor of the Module diff -Nru botan3-3.7.1+dfsg/src/examples/pkcs11_module.cpp botan3-3.12.0+dfsg/src/examples/pkcs11_module.cpp --- botan3-3.7.1+dfsg/src/examples/pkcs11_module.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/pkcs11_module.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,7 +10,7 @@ // Sometimes useful if a newly connected token is not detected by the PKCS#11 module module.reload(); - Botan::PKCS11::Info info = module.get_info(); + const Botan::PKCS11::Info info = module.get_info(); // print library version std::cout << std::to_string(info.libraryVersion.major) << "." << std::to_string(info.libraryVersion.minor) << '\n'; diff -Nru botan3-3.7.1+dfsg/src/examples/pkcs11_objects.cpp botan3-3.12.0+dfsg/src/examples/pkcs11_objects.cpp --- botan3-3.7.1+dfsg/src/examples/pkcs11_objects.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/pkcs11_objects.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -16,9 +16,9 @@ Botan::PKCS11::Session session(slot, false); // create an simple data object - Botan::secure_vector value = {0x00, 0x01, 0x02, 0x03}; - std::size_t id = 1337; - std::string label = "test data object"; + const Botan::secure_vector value = {0x00, 0x01, 0x02, 0x03}; + const std::size_t id = 1337; + const std::string label = "test data object"; // set properties of the new object Botan::PKCS11::DataObjectProperties data_obj_props; @@ -31,19 +31,20 @@ data_obj_props.set_object_id(encoded_id); // create the object - Botan::PKCS11::Object data_obj(session, data_obj_props); + const Botan::PKCS11::Object data_obj(session, data_obj_props); // get label of this object - Botan::PKCS11::secure_string retrieved_label = data_obj.get_attribute_value(Botan::PKCS11::AttributeType::Label); + const Botan::PKCS11::secure_string retrieved_label = + data_obj.get_attribute_value(Botan::PKCS11::AttributeType::Label); // set a new label - Botan::PKCS11::secure_string new_label = {'B', 'o', 't', 'a', 'n'}; + const Botan::PKCS11::secure_string new_label = {'B', 'o', 't', 'a', 'n'}; data_obj.set_attribute_value(Botan::PKCS11::AttributeType::Label, new_label); // copy the object Botan::PKCS11::AttributeContainer copy_attributes; copy_attributes.add_string(Botan::PKCS11::AttributeType::Label, "copied object"); - [[maybe_unused]] Botan::PKCS11::ObjectHandle copied_obj_handle = data_obj.copy(copy_attributes); + [[maybe_unused]] const Botan::PKCS11::ObjectHandle copied_obj_handle = data_obj.copy(copy_attributes); // search for an object Botan::PKCS11::AttributeContainer search_template; diff -Nru botan3-3.7.1+dfsg/src/examples/pkcs11_rsa.cpp botan3-3.12.0+dfsg/src/examples/pkcs11_rsa.cpp --- botan3-3.7.1+dfsg/src/examples/pkcs11_rsa.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/pkcs11_rsa.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -14,14 +14,14 @@ Botan::PKCS11::Slot slot(module, slots.at(0)); Botan::PKCS11::Session session(slot, false); - Botan::PKCS11::secure_string pin = {'1', '2', '3', '4', '5', '6'}; + const Botan::PKCS11::secure_string pin = {'1', '2', '3', '4', '5', '6'}; session.login(Botan::PKCS11::UserType::User, pin); /************ import RSA private key *************/ // create private key in software Botan::AutoSeeded_RNG rng; - Botan::RSA_PrivateKey priv_key_sw(rng, 2048); + const Botan::RSA_PrivateKey priv_key_sw(rng, 2048); // set the private key import properties Botan::PKCS11::RSA_PrivateKeyImportProperties priv_import_props(priv_key_sw.get_n(), priv_key_sw.get_d()); @@ -39,10 +39,10 @@ priv_import_props.set_sign(true); // import - Botan::PKCS11::PKCS11_RSA_PrivateKey priv_key(session, priv_import_props); + const Botan::PKCS11::PKCS11_RSA_PrivateKey priv_key(session, priv_import_props); /************ export PKCS#11 RSA private key *************/ - Botan::RSA_PrivateKey exported = priv_key.export_key(); + const Botan::RSA_PrivateKey exported = priv_key.export_key(); /************ import RSA public key *************/ @@ -53,7 +53,7 @@ pub_import_props.set_private(false); // import - Botan::PKCS11::PKCS11_RSA_PublicKey public_key(session, pub_import_props); + const Botan::PKCS11::PKCS11_RSA_PublicKey public_key(session, pub_import_props); /************ generate RSA private key *************/ @@ -64,7 +64,7 @@ priv_generate_props.set_decrypt(true); priv_generate_props.set_label("BOTAN_TEST_RSA_PRIV_KEY"); - Botan::PKCS11::PKCS11_RSA_PrivateKey private_key2(session, 2048, priv_generate_props); + const Botan::PKCS11::PKCS11_RSA_PrivateKey private_key2(session, 2048, priv_generate_props); /************ generate RSA key pair *************/ @@ -76,28 +76,28 @@ pub_generate_props.set_verify(true); pub_generate_props.set_private(false); - Botan::PKCS11::PKCS11_RSA_KeyPair rsa_keypair = + const Botan::PKCS11::PKCS11_RSA_KeyPair rsa_keypair = Botan::PKCS11::generate_rsa_keypair(session, pub_generate_props, priv_generate_props); /************ RSA encrypt *************/ Botan::secure_vector plaintext = {0x00, 0x01, 0x02, 0x03}; - Botan::PK_Encryptor_EME encryptor(rsa_keypair.first, rng, "Raw"); + const Botan::PK_Encryptor_EME encryptor(rsa_keypair.first, rng, "Raw"); auto ciphertext = encryptor.encrypt(plaintext, rng); /************ RSA decrypt *************/ - Botan::PK_Decryptor_EME decryptor(rsa_keypair.second, rng, "Raw"); + const Botan::PK_Decryptor_EME decryptor(rsa_keypair.second, rng, "Raw"); plaintext = decryptor.decrypt(ciphertext); /************ RSA sign *************/ - Botan::PK_Signer signer(rsa_keypair.second, rng, "EMSA4(SHA-256)", Botan::Signature_Format::Standard); + Botan::PK_Signer signer(rsa_keypair.second, rng, "PSS(SHA-256)", Botan::Signature_Format::Standard); auto signature = signer.sign_message(plaintext, rng); /************ RSA verify *************/ - Botan::PK_Verifier verifier(rsa_keypair.first, "EMSA4(SHA-256)", Botan::Signature_Format::Standard); + Botan::PK_Verifier verifier(rsa_keypair.first, "PSS(SHA-256)", Botan::Signature_Format::Standard); auto ok = verifier.verify_message(plaintext, signature); return ok ? 0 : 1; diff -Nru botan3-3.7.1+dfsg/src/examples/pkcs11_session.cpp botan3-3.12.0+dfsg/src/examples/pkcs11_session.cpp --- botan3-3.7.1+dfsg/src/examples/pkcs11_session.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/pkcs11_session.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,46 +11,46 @@ Botan::PKCS11::Slot slot(module, slots.at(0)); // open read only session - { Botan::PKCS11::Session read_only_session(slot, true); } + { const Botan::PKCS11::Session read_only_session(slot, true); } // open read write session - { Botan::PKCS11::Session read_write_session(slot, false); } + { const Botan::PKCS11::Session read_write_session(slot, false); } // open read write session by passing flags { - Botan::PKCS11::Flags flags = + const Botan::PKCS11::Flags flags = Botan::PKCS11::flags(Botan::PKCS11::Flag::SerialSession | Botan::PKCS11::Flag::RwSession); - Botan::PKCS11::Session read_write_session(slot, flags, nullptr, nullptr); + const Botan::PKCS11::Session read_write_session(slot, flags, nullptr, nullptr); } // move ownership of a session { Botan::PKCS11::Session session(slot, false); - Botan::PKCS11::SessionHandle handle = session.release(); + const Botan::PKCS11::SessionHandle handle = session.release(); - Botan::PKCS11::Session session2(slot, handle); + const Botan::PKCS11::Session session2(slot, handle); } Botan::PKCS11::Session session(slot, false); // get session info - Botan::PKCS11::SessionInfo info = session.get_info(); + const Botan::PKCS11::SessionInfo info = session.get_info(); std::cout << info.slotID << '\n'; // login - Botan::PKCS11::secure_string pin = {'1', '2', '3', '4', '5', '6'}; + const Botan::PKCS11::secure_string pin = {'1', '2', '3', '4', '5', '6'}; session.login(Botan::PKCS11::UserType::User, pin); // set pin - Botan::PKCS11::secure_string new_pin = {'6', '5', '4', '3', '2', '1'}; + const Botan::PKCS11::secure_string new_pin = {'6', '5', '4', '3', '2', '1'}; session.set_pin(pin, new_pin); // logoff session.logoff(); // log in as security officer - Botan::PKCS11::secure_string so_pin = {'0', '0', '0', '0', '0', '0', '0', '0'}; + const Botan::PKCS11::secure_string so_pin = {'0', '0', '0', '0', '0', '0', '0', '0'}; session.login(Botan::PKCS11::UserType::SO, so_pin); // change pin to old pin diff -Nru botan3-3.7.1+dfsg/src/examples/pkcs11_slot.cpp botan3-3.12.0+dfsg/src/examples/pkcs11_slot.cpp --- botan3-3.7.1+dfsg/src/examples/pkcs11_slot.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/pkcs11_slot.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,32 +9,32 @@ Botan::PKCS11::Module module("C:\\pkcs11-middleware\\library.dll"); // only slots with connected token - std::vector slots = Botan::PKCS11::Slot::get_available_slots(module, true); + const std::vector slots = Botan::PKCS11::Slot::get_available_slots(module, true); // use first slot - Botan::PKCS11::Slot slot(module, slots.at(0)); + const Botan::PKCS11::Slot slot(module, slots.at(0)); // print firmware version of the slot - Botan::PKCS11::SlotInfo slot_info = slot.get_slot_info(); + const Botan::PKCS11::SlotInfo slot_info = slot.get_slot_info(); std::cout << std::to_string(slot_info.firmwareVersion.major) << "." << std::to_string(slot_info.firmwareVersion.minor) << '\n'; // print firmware version of the token - Botan::PKCS11::TokenInfo token_info = slot.get_token_info(); + const Botan::PKCS11::TokenInfo token_info = slot.get_token_info(); std::cout << std::to_string(token_info.firmwareVersion.major) << "." << std::to_string(token_info.firmwareVersion.minor) << '\n'; // retrieve all mechanisms supported by the token - std::vector mechanisms = slot.get_mechanism_list(); + const std::vector mechanisms = slot.get_mechanism_list(); // retrieve information about a particular mechanism - Botan::PKCS11::MechanismInfo mech_info = slot.get_mechanism_info(Botan::PKCS11::MechanismType::RsaPkcsOaep); + const Botan::PKCS11::MechanismInfo mech_info = slot.get_mechanism_info(Botan::PKCS11::MechanismType::RsaPkcsOaep); // maximum RSA key length supported: std::cout << mech_info.ulMaxKeySize << '\n'; // initialize the token - Botan::PKCS11::secure_string so_pin(8, '0'); + const Botan::PKCS11::secure_string so_pin(8, '0'); slot.initialize("Botan PKCS11 documentation test label", so_pin); return 0; diff -Nru botan3-3.7.1+dfsg/src/examples/pkcs11_token_management.cpp botan3-3.12.0+dfsg/src/examples/pkcs11_token_management.cpp --- botan3-3.7.1+dfsg/src/examples/pkcs11_token_management.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/pkcs11_token_management.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -14,9 +14,9 @@ // use first slot Botan::PKCS11::Slot slot(module, slots.at(0)); - Botan::PKCS11::secure_string so_pin = {'1', '2', '3', '4', '5', '6', '7', '8'}; - Botan::PKCS11::secure_string pin = {'1', '2', '3', '4', '5', '6'}; - Botan::PKCS11::secure_string test_pin = {'6', '5', '4', '3', '2', '1'}; + const Botan::PKCS11::secure_string so_pin = {'1', '2', '3', '4', '5', '6', '7', '8'}; + const Botan::PKCS11::secure_string pin = {'1', '2', '3', '4', '5', '6'}; + const Botan::PKCS11::secure_string test_pin = {'6', '5', '4', '3', '2', '1'}; // set pin Botan::PKCS11::set_pin(slot, so_pin, test_pin); diff -Nru botan3-3.7.1+dfsg/src/examples/pkcs11_x509.cpp botan3-3.12.0+dfsg/src/examples/pkcs11_x509.cpp --- botan3-3.7.1+dfsg/src/examples/pkcs11_x509.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/pkcs11_x509.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -14,7 +14,7 @@ Botan::PKCS11::Session session(slot, false); // load existing certificate - Botan::X509_Certificate root("test.crt"); + const Botan::X509_Certificate root("test.crt"); // set props Botan::PKCS11::X509_CertificateProperties props(root.subject_dn().DER_encode(), root.BER_encode()); @@ -24,10 +24,10 @@ props.set_token(true); // import - Botan::PKCS11::PKCS11_X509_Certificate pkcs11_cert(session, props); + const Botan::PKCS11::PKCS11_X509_Certificate pkcs11_cert(session, props); // load by handle - Botan::PKCS11::PKCS11_X509_Certificate pkcs11_cert2(session, pkcs11_cert.handle()); + const Botan::PKCS11::PKCS11_X509_Certificate pkcs11_cert2(session, pkcs11_cert.handle()); return 0; } diff -Nru botan3-3.7.1+dfsg/src/examples/pwdhash.cpp botan3-3.12.0+dfsg/src/examples/pwdhash.cpp --- botan3-3.7.1+dfsg/src/examples/pwdhash.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/pwdhash.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,23 +6,23 @@ int main() { // You can change this to "PBKDF2(SHA-512)" or "Scrypt" or "Argon2id" or ... - std::string_view pbkdf_algo = "Argon2i"; - auto pbkdf_runtime = std::chrono::milliseconds(300); + const std::string_view pbkdf_algo = "Argon2i"; + constexpr uint64_t pbkdf_runtime = 300; // milliseconds constexpr size_t output_hash = 32; constexpr size_t salt_len = 32; constexpr size_t max_pbkdf_mb = 128; auto pwd_fam = Botan::PasswordHashFamily::create_or_throw(pbkdf_algo); - auto pwdhash = pwd_fam->tune(output_hash, pbkdf_runtime, max_pbkdf_mb); + auto pwdhash = pwd_fam->tune_params(output_hash, pbkdf_runtime, max_pbkdf_mb); std::cout << "Using params " << pwdhash->to_string() << '\n'; const auto salt = Botan::system_rng().random_array(); - std::string_view password = "tell no one"; + const std::string_view password = "tell no one"; - std::array key; + std::array key{}; pwdhash->hash(key, password, salt); std::cout << Botan::hex_encode(key) << '\n'; diff -Nru botan3-3.7.1+dfsg/src/examples/rsa_encrypt.cpp botan3-3.12.0+dfsg/src/examples/rsa_encrypt.cpp --- botan3-3.7.1+dfsg/src/examples/rsa_encrypt.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/rsa_encrypt.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,7 +11,7 @@ if(argc != 2) { return 1; } - std::string_view plaintext( + const std::string_view plaintext( "Your great-grandfather gave this watch to your granddad for good luck. " "Unfortunately, Dane's luck wasn't as good as his old man's."); const Botan::secure_vector pt(plaintext.data(), plaintext.data() + plaintext.length()); @@ -22,11 +22,11 @@ auto kp = Botan::PKCS8::load_key(in); // encrypt with pk - Botan::PK_Encryptor_EME enc(*kp, rng, "OAEP(SHA-256)"); + const Botan::PK_Encryptor_EME enc(*kp, rng, "OAEP(SHA-256)"); const auto ct = enc.encrypt(pt, rng); // decrypt with sk - Botan::PK_Decryptor_EME dec(*kp, rng, "OAEP(SHA-256)"); + const Botan::PK_Decryptor_EME dec(*kp, rng, "OAEP(SHA-256)"); const auto pt2 = dec.decrypt(ct); std::cout << "\nenc: " << Botan::hex_encode(ct) << "\ndec: " << Botan::hex_encode(pt2); diff -Nru botan3-3.7.1+dfsg/src/examples/tls_13_hybrid_key_exchange_client.cpp botan3-3.12.0+dfsg/src/examples/tls_13_hybrid_key_exchange_client.cpp --- botan3-3.7.1+dfsg/src/examples/tls_13_hybrid_key_exchange_client.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/tls_13_hybrid_key_exchange_client.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,19 +11,17 @@ */ class Callbacks : public Botan::TLS::Callbacks { public: - void tls_emit_data(std::span data) override { - BOTAN_UNUSED(data); + void tls_emit_data([[maybe_unused]] std::span data) override { // send data to tls server, e.g., using BSD sockets or boost asio } - void tls_record_received(uint64_t seq_no, std::span data) override { - BOTAN_UNUSED(seq_no, data); + void tls_record_received([[maybe_unused]] uint64_t seq_no, + [[maybe_unused]] std::span data) override { // process full TLS record received by tls server, e.g., // by passing it to the application } - void tls_alert(Botan::TLS::Alert alert) override { - BOTAN_UNUSED(alert); + void tls_alert([[maybe_unused]] Botan::TLS::Alert alert) override { // handle a tls alert received from the tls server } }; @@ -36,9 +34,8 @@ */ class Client_Credentials : public Botan::Credentials_Manager { public: - std::vector trusted_certificate_authorities(const std::string& type, - const std::string& context) override { - BOTAN_UNUSED(type, context); + std::vector trusted_certificate_authorities( + [[maybe_unused]] const std::string& type, [[maybe_unused]] const std::string& context) override { // return a list of certificates of CAs we trust for tls server certificates, // e.g., all the certificates in the local directory "cas" return {&m_cert_store}; @@ -56,6 +53,7 @@ auto groups = Botan::TLS::Default_Policy::key_exchange_groups(); groups.push_back(Botan::TLS::Group_Params::HYBRID_X25519_ML_KEM_768); groups.push_back(Botan::TLS::Group_Params::HYBRID_SECP256R1_ML_KEM_768); + groups.push_back(Botan::TLS::Group_Params::HYBRID_SECP384R1_ML_KEM_1024); return groups; } @@ -75,13 +73,13 @@ auto policy = std::make_shared(); // open the tls connection - Botan::TLS::Client client(callbacks, - session_mgr, - creds, - policy, - rng, - Botan::TLS::Server_Information("botan.randombit.net", 443), - Botan::TLS::Protocol_Version::TLS_V12); + const Botan::TLS::Client client(callbacks, + session_mgr, + creds, + policy, + rng, + Botan::TLS::Server_Information("botan.randombit.net", 443), + Botan::TLS::Protocol_Version::TLS_V12); while(!client.is_closed()) { // read data received from the tls server, e.g., using BSD sockets or boost asio diff -Nru botan3-3.7.1+dfsg/src/examples/tls_client.cpp botan3-3.12.0+dfsg/src/examples/tls_client.cpp --- botan3-3.7.1+dfsg/src/examples/tls_client.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/tls_client.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,20 +12,18 @@ */ class Callbacks : public Botan::TLS::Callbacks { public: - void tls_emit_data(std::span data) override { + void tls_emit_data([[maybe_unused]] std::span data) override { // send data to tls server, e.g., using BSD sockets or boost asio - BOTAN_UNUSED(data); } - void tls_record_received(uint64_t seq_no, std::span data) override { + void tls_record_received([[maybe_unused]] uint64_t seq_no, + [[maybe_unused]] std::span data) override { // process full TLS record received by tls server, e.g., // by passing it to the application - BOTAN_UNUSED(seq_no, data); } - void tls_alert(Botan::TLS::Alert alert) override { + void tls_alert([[maybe_unused]] Botan::TLS::Alert alert) override { // handle a tls alert received from the tls server - BOTAN_UNUSED(alert); } }; @@ -38,31 +36,27 @@ */ class Client_Credentials : public Botan::Credentials_Manager { public: - std::vector trusted_certificate_authorities(const std::string& type, - const std::string& context) override { - BOTAN_UNUSED(type, context); + std::vector trusted_certificate_authorities( + [[maybe_unused]] const std::string& type, [[maybe_unused]] const std::string& context) override { // return a list of certificates of CAs we trust for tls server certificates // ownership of the pointers remains with Credentials_Manager return {&m_cert_store}; } std::vector cert_chain( - const std::vector& cert_key_types, - const std::vector& cert_signature_schemes, - const std::string& type, - const std::string& context) override { - BOTAN_UNUSED(cert_key_types, cert_signature_schemes, type, context); - + [[maybe_unused]] const std::vector& cert_key_types, + [[maybe_unused]] const std::vector& cert_signature_schemes, + [[maybe_unused]] const std::string& type, + [[maybe_unused]] const std::string& context) override { // when using tls client authentication (optional), return // a certificate chain being sent to the tls server, // else an empty list return {}; } - std::shared_ptr private_key_for(const Botan::X509_Certificate& cert, - const std::string& type, - const std::string& context) override { - BOTAN_UNUSED(cert, type, context); + std::shared_ptr private_key_for([[maybe_unused]] const Botan::X509_Certificate& cert, + [[maybe_unused]] const std::string& type, + [[maybe_unused]] const std::string& context) override { // when returning a chain in cert_chain(), return the private key // associated with the leaf certificate here return nullptr; @@ -81,13 +75,13 @@ auto policy = std::make_shared(); // open the tls connection - Botan::TLS::Client client(callbacks, - session_mgr, - creds, - policy, - rng, - Botan::TLS::Server_Information("botan.randombit.net", 443), - Botan::TLS::Protocol_Version::TLS_V12); + const Botan::TLS::Client client(callbacks, + session_mgr, + creds, + policy, + rng, + Botan::TLS::Server_Information("botan.randombit.net", 443), + Botan::TLS::Protocol_Version::TLS_V12); while(!client.is_closed()) { // read data received from the tls server, e.g., using BSD sockets or boost asio diff -Nru botan3-3.7.1+dfsg/src/examples/tls_custom_curves_client.cpp botan3-3.12.0+dfsg/src/examples/tls_custom_curves_client.cpp --- botan3-3.7.1+dfsg/src/examples/tls_custom_curves_client.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/tls_custom_curves_client.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include @@ -12,19 +14,17 @@ */ class Callbacks : public Botan::TLS::Callbacks { public: - void tls_emit_data(std::span data) override { - BOTAN_UNUSED(data); + void tls_emit_data([[maybe_unused]] std::span data) override { // send data to tls server, e.g., using BSD sockets or boost asio } - void tls_record_received(uint64_t seq_no, std::span data) override { - BOTAN_UNUSED(seq_no, data); + void tls_record_received([[maybe_unused]] uint64_t seq_no, + [[maybe_unused]] std::span data) override { // process full TLS record received by tls server, e.g., // by passing it to the application } - void tls_alert(Botan::TLS::Alert alert) override { - BOTAN_UNUSED(alert); + void tls_alert([[maybe_unused]] Botan::TLS::Alert alert) override { // handle a tls alert received from the tls server } @@ -65,9 +65,8 @@ */ class Client_Credentials : public Botan::Credentials_Manager { public: - std::vector trusted_certificate_authorities(const std::string& type, - const std::string& context) override { - BOTAN_UNUSED(type, context); + std::vector trusted_certificate_authorities( + [[maybe_unused]] const std::string& type, [[maybe_unused]] const std::string& context) override { // return a list of certificates of CAs we trust for tls server certificates, // e.g., all the certificates in the local directory "cas" return {&m_cert_store}; @@ -111,7 +110,7 @@ const Botan::OID oid("1.3.6.1.4.1.25258.4.1"); // create EC_Group object to register the curve - Botan::EC_Group numsp256d1(oid, p, a, b, g_x, g_y, n); + const Botan::EC_Group numsp256d1(oid, p, a, b, g_x, g_y, n); if(!numsp256d1.verify_group(*rng)) { return 1; @@ -128,13 +127,13 @@ auto policy = std::make_shared(); // open the tls connection - Botan::TLS::Client client(callbacks, - session_mgr, - creds, - policy, - rng, - Botan::TLS::Server_Information("botan.randombit.net", 443), - Botan::TLS::Protocol_Version::TLS_V12); + const Botan::TLS::Client client(callbacks, + session_mgr, + creds, + policy, + rng, + Botan::TLS::Server_Information("botan.randombit.net", 443), + Botan::TLS::Protocol_Version::TLS_V12); while(!client.is_closed()) { // read data received from the tls server, e.g., using BSD sockets or boost asio diff -Nru botan3-3.7.1+dfsg/src/examples/tls_proxy.cpp botan3-3.12.0+dfsg/src/examples/tls_proxy.cpp --- botan3-3.7.1+dfsg/src/examples/tls_proxy.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/tls_proxy.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -15,20 +15,18 @@ */ class Callbacks : public Botan::TLS::Callbacks { public: - void tls_emit_data(std::span data) override { + void tls_emit_data([[maybe_unused]] std::span data) override { // send data to tls client, e.g., using BSD sockets or boost asio - BOTAN_UNUSED(data); } - void tls_record_received(uint64_t seq_no, std::span data) override { + void tls_record_received([[maybe_unused]] uint64_t seq_no, + [[maybe_unused]] std::span data) override { // process full TLS record received by tls client, e.g., // by passing it to the application - BOTAN_UNUSED(seq_no, data); } - void tls_alert(Botan::TLS::Alert alert) override { + void tls_alert([[maybe_unused]] Botan::TLS::Alert alert) override { // handle a tls alert received from the tls server - BOTAN_UNUSED(alert); } }; @@ -47,9 +45,8 @@ m_key.reset(Botan::PKCS8::load_key(in).release()); } - std::vector trusted_certificate_authorities(const std::string& type, - const std::string& context) override { - BOTAN_UNUSED(type, context); + std::vector trusted_certificate_authorities( + [[maybe_unused]] const std::string& type, [[maybe_unused]] const std::string& context) override { // if client authentication is required, this function // shall return a list of certificates of CAs we trust // for tls client certificates, otherwise return an empty list @@ -57,21 +54,18 @@ } std::vector cert_chain( - const std::vector& cert_key_types, - const std::vector& cert_signature_schemes, - const std::string& type, - const std::string& context) override { - BOTAN_UNUSED(cert_key_types, cert_signature_schemes, type, context); - + [[maybe_unused]] const std::vector& cert_key_types, + [[maybe_unused]] const std::vector& cert_signature_schemes, + [[maybe_unused]] const std::string& type, + [[maybe_unused]] const std::string& context) override { // return the certificate chain being sent to the tls client // e.g., the certificate file "botan.randombit.net.crt" return {Botan::X509_Certificate("botan.randombit.net.crt")}; } - std::shared_ptr private_key_for(const Botan::X509_Certificate& cert, - const std::string& type, - const std::string& context) override { - BOTAN_UNUSED(cert, type, context); + std::shared_ptr private_key_for([[maybe_unused]] const Botan::X509_Certificate& cert, + [[maybe_unused]] const std::string& type, + [[maybe_unused]] const std::string& context) override { // return the private key associated with the leaf certificate, // in this case the one associated with "botan.randombit.net.crt" return m_key; @@ -90,7 +84,7 @@ auto policy = std::make_shared(); // accept tls connection from client - Botan::TLS::Server server(callbacks, session_mgr, creds, policy, rng); + const Botan::TLS::Server server(callbacks, session_mgr, creds, policy, rng); // read data received from the tls client, e.g., using BSD sockets or boost asio // and pass it to server.received_data(). diff -Nru botan3-3.7.1+dfsg/src/examples/tls_ssl_key_log_file.cpp botan3-3.12.0+dfsg/src/examples/tls_ssl_key_log_file.cpp --- botan3-3.7.1+dfsg/src/examples/tls_ssl_key_log_file.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/tls_ssl_key_log_file.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -17,14 +17,14 @@ #include #include -#if defined(BOTAN_TARGET_OS_HAS_SOCKETS) +#if __has_include() #include #include #include #include -#endif -#if defined(BOTAN_TARGET_OS_HAS_POSIX1) #include + + #define HAS_BSD_SOCKETS #endif namespace { @@ -36,8 +36,8 @@ public: Client_Credential() = default; - std::vector trusted_certificate_authorities(const std::string&, - const std::string&) override { + std::vector trusted_certificate_authorities(const std::string& /*type*/, + const std::string& /*context*/) override { return {&m_cert_store}; } @@ -64,24 +64,22 @@ } } - std::vector trusted_certificate_authorities(const std::string&, - const std::string&) override { + std::vector trusted_certificate_authorities(const std::string& /*type*/, + const std::string& /*context*/) override { return {&m_cert_store}; } std::vector cert_chain( - const std::vector& cert_key_types, - const std::vector& cert_signature_schemes, - const std::string& type, - const std::string& context) override { - BOTAN_UNUSED(cert_signature_schemes, type, context); - + [[maybe_unused]] const std::vector& cert_key_types, + [[maybe_unused]] const std::vector& cert_signature_schemes, + [[maybe_unused]] const std::string& type, + [[maybe_unused]] const std::string& context) override { // return the certificate chain being sent to the tls client // e.g., the certificate file "botan.randombit.net.crt" std::vector certs; for(auto& cert : certificates) { - std::string algorithm = cert.subject_public_key()->algo_name(); - for(auto& key : cert_key_types) { + const std::string algorithm = cert.subject_public_key()->algo_name(); + for(const auto& key : cert_key_types) { if(algorithm == key) { certs.push_back(cert); } @@ -90,10 +88,9 @@ return certs; } - std::shared_ptr private_key_for(const Botan::X509_Certificate& cert, - const std::string& type, - const std::string& context) override { - BOTAN_UNUSED(cert, type, context); + std::shared_ptr private_key_for([[maybe_unused]] const Botan::X509_Certificate& cert, + [[maybe_unused]] const std::string& type, + [[maybe_unused]] const std::string& context) override { // return the private key associated with the leaf certificate, // in this case the one associated with "botan.randombit.net.crt" return m_key; @@ -114,13 +111,14 @@ Botan::TLS::Callbacks& parent; public: - BotanTLSCallbacksProxy(Botan::TLS::Callbacks& callbacks) : parent(callbacks) {} + explicit BotanTLSCallbacksProxy(Botan::TLS::Callbacks& callbacks) : parent(callbacks) {} void tls_emit_data(std::span data) override { parent.tls_emit_data(data); } - void tls_record_received(uint64_t seq_no, std::span data) override { BOTAN_UNUSED(seq_no, data); } + void tls_record_received([[maybe_unused]] uint64_t seq_no, + [[maybe_unused]] std::span data) override {} - void tls_alert(Botan::TLS::Alert alert) override { BOTAN_UNUSED(alert); } + void tls_alert([[maybe_unused]] Botan::TLS::Alert alert) override {} void tls_ssl_key_log_data(std::string_view label, std::span client_random, @@ -133,20 +131,22 @@ class DtlsConnection : public Botan::TLS::Callbacks { int fd; -#if defined(BOTAN_TARGET_OS_HAS_SOCKETS) - sockaddr_in remote_addr; +#if defined(HAS_BSD_SOCKETS) + sockaddr_in remote_addr{}; #endif std::unique_ptr dtls_channel; std::function activated_callback; public: - DtlsConnection(const std::string& r_addr, int r_port, int socket, bool is_server) : fd(socket) { -#if defined(BOTAN_TARGET_OS_HAS_SOCKETS) + DtlsConnection([[maybe_unused]] const std::string& r_addr, + [[maybe_unused]] int r_port, + int socket, + bool is_server) : + fd(socket) { +#if defined(HAS_BSD_SOCKETS) remote_addr.sin_family = AF_INET; inet_aton(r_addr.c_str(), &remote_addr.sin_addr); remote_addr.sin_port = htons(r_port); -#else - BOTAN_UNUSED(r_addr, r_port); #endif auto tls_callbacks_proxy = std::make_shared(*this); auto rng = std::make_shared(); @@ -170,19 +170,18 @@ } } - void tls_emit_data(std::span data) override { -#if defined(BOTAN_TARGET_OS_HAS_SOCKETS) - sendto(fd, data.data(), data.size(), 0, reinterpret_cast(&remote_addr), sizeof(sockaddr_in)); -#else - BOTAN_UNUSED(data); + void tls_emit_data([[maybe_unused]] std::span data) override { +#if defined(HAS_BSD_SOCKETS) // send data to the other side // ... + sendto(fd, data.data(), data.size(), 0, reinterpret_cast(&remote_addr), sizeof(sockaddr_in)); #endif } - void tls_record_received(uint64_t seq_no, std::span data) override { BOTAN_UNUSED(seq_no, data); } + void tls_record_received([[maybe_unused]] uint64_t seq_no, + [[maybe_unused]] std::span data) override {} - void tls_alert(Botan::TLS::Alert alert) override { BOTAN_UNUSED(alert); } + void tls_alert([[maybe_unused]] Botan::TLS::Alert alert) override {} void tls_session_activated() override { std::cout << "************ on_dtls_connect() ***********" << std::endl; @@ -204,12 +203,10 @@ void set_activated_callback(std::function callback) { activated_callback = std::move(callback); } void close() const { - if(fd) { -#if defined(BOTAN_TARGET_OS_HAS_SOCKETS) + if(fd >= 0) { +#if defined(HAS_BSD_SOCKETS) shutdown(fd, SHUT_RDWR); - #if defined(BOTAN_TARGET_OS_HAS_POSIX1) ::close(fd); - #endif #endif } } @@ -219,7 +216,7 @@ std::cout << "Start Server" << std::endl; int fd = 0; -#if defined(BOTAN_TARGET_OS_HAS_SOCKETS) +#if defined(HAS_BSD_SOCKETS) fd = socket(AF_INET, SOCK_DGRAM, 0); if(fd == -1) { return; @@ -228,14 +225,14 @@ if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, static_cast(&true_opt), sizeof(true_opt)) == -1) { return; } - sockaddr_in addr; + sockaddr_in addr{}; addr.sin_family = AF_INET; addr.sin_port = htons(SERVER_PORT); inet_aton("127.0.0.1", &addr.sin_addr); if(bind(fd, reinterpret_cast(&addr), sizeof(sockaddr_in)) == -1) { return; } - sockaddr_in fromaddr; + sockaddr_in fromaddr{}; fromaddr.sin_family = AF_INET; socklen_t len = sizeof(sockaddr_in); #else @@ -246,7 +243,7 @@ auto connection = std::make_shared("127.0.0.1", CLIENT_PORT, fd, true); conn_callback(connection); -#if defined(BOTAN_TARGET_OS_HAS_SOCKETS) +#if defined(HAS_BSD_SOCKETS) static uint8_t data[8192]; ssize_t recvlen = 0; while((recvlen = recvfrom(fd, data, sizeof(data), 0, reinterpret_cast(&fromaddr), &len)) > 0) { @@ -265,7 +262,7 @@ std::cout << "Start Client" << std::endl; int fd = 0; -#if defined(BOTAN_TARGET_OS_HAS_SOCKETS) +#if defined(HAS_BSD_SOCKETS) fd = socket(AF_INET, SOCK_DGRAM, 0); if(fd == -1) { return; @@ -274,14 +271,14 @@ if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, static_cast(&true_opt), sizeof(true_opt)) == -1) { return; } - sockaddr_in addr; + sockaddr_in addr{}; addr.sin_family = AF_INET; addr.sin_port = htons(CLIENT_PORT); inet_aton("127.0.0.1", &addr.sin_addr); if(bind(fd, reinterpret_cast(&addr), sizeof(sockaddr_in)) == -1) { return; } - sockaddr_in fromaddr; + sockaddr_in fromaddr{}; fromaddr.sin_family = AF_INET; socklen_t len = sizeof(sockaddr_in); #else @@ -291,7 +288,7 @@ auto connection = std::make_shared("127.0.0.1", SERVER_PORT, fd, false); conn_callback(connection); -#if defined(BOTAN_TARGET_OS_HAS_SOCKETS) +#if defined(HAS_BSD_SOCKETS) static uint8_t data[8192]; ssize_t recvlen = 0; while((recvlen = recvfrom(fd, data, sizeof(data), 0, reinterpret_cast(&fromaddr), &len)) > 0) { @@ -313,14 +310,14 @@ std::condition_variable conn_cond; std::vector> connections; std::thread server(server_proc, [&](std::shared_ptr conn) { - std::lock_guard lk(m); + const std::scoped_lock lk(m); connections.push_back(std::move(conn)); if(connections.size() == 2) { conn_cond.notify_one(); } }); std::thread client(client_proc, [&](std::shared_ptr conn) { - std::lock_guard lk(m); + const std::scoped_lock lk(m); connections.push_back(std::move(conn)); if(connections.size() == 2) { conn_cond.notify_one(); diff -Nru botan3-3.7.1+dfsg/src/examples/tls_stream_client.cpp botan3-3.12.0+dfsg/src/examples/tls_stream_client.cpp --- botan3-3.7.1+dfsg/src/examples/tls_stream_client.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/tls_stream_client.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,8 +11,7 @@ #include #include - #include - #include + #include namespace http = boost::beast::http; namespace ap = boost::asio::placeholders; @@ -22,8 +21,8 @@ public: Credentials_Manager() = default; - std::vector trusted_certificate_authorities(const std::string&, - const std::string&) override { + std::vector trusted_certificate_authorities(const std::string& /*type*/, + const std::string& /*context*/) override { return {&m_cert_store}; } @@ -31,6 +30,16 @@ Botan::System_Certificate_Store m_cert_store; }; +// Custom TLS policy that relaxes the certificate revocation info requirement. +// Often this setting causes frustration for new users. However, applications +// should carefully consider whether or not to enable revocation checks. +class Example_Policy : public Botan::TLS::Policy { + public: + bool require_cert_revocation_info() const override { return false; } +}; + +// NOLINTBEGIN(*-avoid-bind) + // a simple https client based on TLS::Stream class client { public: @@ -42,8 +51,8 @@ m_ctx(std::make_shared(std::make_shared(), std::make_shared(), std::make_shared(), - std::make_shared(), - host)), + std::make_shared(), + Botan::TLS::Server_Information(host))), m_stream(io_context, m_ctx) { boost::asio::async_connect(m_stream.lowest_layer(), endpoints.begin(), @@ -69,7 +78,7 @@ m_stream, m_request, boost::bind(&client::handle_write, this, ap::error, ap::bytes_transferred)); } - void handle_write(const boost::system::error_code& error, size_t) { + void handle_write(const boost::system::error_code& error, size_t /*unused*/) { if(error) { std::cout << "Write failed: " << error.message() << '\n'; return; @@ -78,7 +87,7 @@ m_stream, m_reply, m_response, boost::bind(&client::handle_read, this, ap::error, ap::bytes_transferred)); } - void handle_read(const boost::system::error_code& error, size_t) { + void handle_read(const boost::system::error_code& error, size_t /*unused*/) { if(!error) { std::cout << "Reply: "; std::cout << m_response.body() << '\n'; @@ -96,6 +105,8 @@ Botan::TLS::Stream m_stream; }; +// NOLINTEND(*-avoid-bind) + int main(int argc, char* argv[]) { if(argc != 4) { std::cerr << "Usage: tls_stream_client \n" @@ -104,15 +115,15 @@ return 1; } - const auto host = argv[1]; - const auto port = argv[2]; - const auto target = argv[3]; + auto* const host = argv[1]; + auto* const port = argv[2]; + auto* const target = argv[3]; try { boost::asio::io_context io_context; boost::asio::ip::tcp::resolver resolver(io_context); - boost::asio::ip::tcp::resolver::results_type endpoints = resolver.resolve(host, port); + const boost::asio::ip::tcp::resolver::results_type endpoints = resolver.resolve(host, port); http::request req; req.version(11); @@ -121,7 +132,7 @@ req.set(http::field::host, host); req.set(http::field::user_agent, Botan::version_string()); - client c(io_context, endpoints, host, req); + const client c(io_context, endpoints, host, req); io_context.run(); } catch(std::exception& e) { diff -Nru botan3-3.7.1+dfsg/src/examples/tls_stream_coroutine_client.cpp botan3-3.12.0+dfsg/src/examples/tls_stream_coroutine_client.cpp --- botan3-3.7.1+dfsg/src/examples/tls_stream_coroutine_client.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/tls_stream_coroutine_client.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,7 +10,7 @@ #define BOOST_VERSION_IS_COMPATIBLE #endif -#if defined(BOOST_VERSION_IS_COMPATIBLE) && defined(BOTAN_HAS_HAS_DEFAULT_TLS_CONTEXT) +#if defined(BOOST_VERSION_IS_COMPATIBLE) && defined(BOTAN_HAS_DEFAULT_TLS_CONTEXT) #include #include @@ -114,7 +114,7 @@ std::cout << "Your boost version is too old, sorry.\n" << "Or did you compile Botan without --with-boost?\n"; #endif - #if !defined(BOTAN_HAS_HAS_DEFAULT_TLS_CONTEXT) + #if !defined(BOTAN_HAS_DEFAULT_TLS_CONTEXT) std::cout << "Your system needs an auto seeded RNG and a certificate store.\n"; #endif return 1; diff -Nru botan3-3.7.1+dfsg/src/examples/x509_path.cpp botan3-3.12.0+dfsg/src/examples/x509_path.cpp --- botan3-3.7.1+dfsg/src/examples/x509_path.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/x509_path.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,7 +9,7 @@ // Additionally trust all system-specific CA certificates Botan::System_Certificate_Store systemStore; - std::vector trusted_roots{&customStore, &systemStore}; + const std::vector trusted_roots{&customStore, &systemStore}; // Load the end entity certificate and two untrusted intermediate CAs from file std::vector end_certs; @@ -18,15 +18,15 @@ end_certs.emplace_back(Botan::X509_Certificate("int1.crt")); // intermediate 1 // Optional: Set up restrictions, e.g. min. key strength, maximum age of OCSP responses - Botan::Path_Validation_Restrictions restrictions; + const Botan::Path_Validation_Restrictions restrictions; // Optional: Specify usage type, compared against the key usage in end_certs[0] - Botan::Usage_Type usage = Botan::Usage_Type::UNSPECIFIED; + const Botan::Usage_Type usage = Botan::Usage_Type::UNSPECIFIED; // Optional: Specify hostname, if not empty, compared against the DNS name in end_certs[0] - std::string hostname; + const std::string hostname; - Botan::Path_Validation_Result validationResult = + const Botan::Path_Validation_Result validationResult = Botan::x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage); if(!validationResult.successful_validation()) { diff -Nru botan3-3.7.1+dfsg/src/examples/xmss.cpp botan3-3.12.0+dfsg/src/examples/xmss.cpp --- botan3-3.7.1+dfsg/src/examples/xmss.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/examples/xmss.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,7 +12,7 @@ // create a new public/private key pair using SHA2 256 as hash // function and a tree height of 10. - Botan::XMSS_PrivateKey private_key(Botan::XMSS_Parameters::xmss_algorithm_t::XMSS_SHA2_10_256, rng); + const Botan::XMSS_PrivateKey private_key(Botan::XMSS_Parameters::xmss_algorithm_t::XMSS_SHA2_10_256, rng); const Botan::XMSS_PublicKey& public_key(private_key); // create Public Key Signer using the private key. diff -Nru botan3-3.7.1+dfsg/src/fuzzer/asn1.cpp botan3-3.12.0+dfsg/src/fuzzer/asn1.cpp --- botan3-3.7.1+dfsg/src/fuzzer/asn1.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/asn1.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,7 @@ #include "fuzzers.h" #include +#include #include class ASN1_Parser final : public Botan::ASN1_Formatter { @@ -14,15 +15,26 @@ ASN1_Parser() : Botan::ASN1_Formatter(true, 64) {} protected: - std::string format(Botan::ASN1_Type, Botan::ASN1_Class, size_t, size_t, std::string_view) const override { + std::string format(Botan::ASN1_Type type, + Botan::ASN1_Class klass, + size_t level, + size_t length, + std::string_view value) const override { + BOTAN_UNUSED(type, klass, level, length, value); return ""; } - std::string format_bin(Botan::ASN1_Type, Botan::ASN1_Class, const std::vector&) const override { + std::string format_bin(Botan::ASN1_Type type, + Botan::ASN1_Class klass, + const std::vector& value) const override { + BOTAN_UNUSED(type, klass, value); return ""; } - std::string format_bn(const Botan::BigInt&) const override { return ""; } + std::string format_bn(const Botan::BigInt& bn) const override { + BOTAN_UNUSED(bn); + return ""; + } }; void fuzz(std::span in) { @@ -32,7 +44,7 @@ * on actual output formatting, no memory is allocated, etc. */ std::ofstream out; - ASN1_Parser printer; + const ASN1_Parser printer; printer.print_to_stream(out, in.data(), in.size()); - } catch(Botan::Exception& e) {} + } catch(const Botan::Exception& e) {} } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/barrett.cpp botan3-3.12.0+dfsg/src/fuzzer/barrett.cpp --- botan3-3.7.1+dfsg/src/fuzzer/barrett.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/barrett.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,7 +7,7 @@ #include "fuzzers.h" #include -#include +#include #include void fuzz(std::span in) { @@ -21,32 +21,28 @@ return; } - const size_t x_len = 2 * ((in.size() + 2) / 3); + const size_t x_len = 2 * in.size() / 3; - Botan::BigInt x = Botan::BigInt::from_bytes(in.subspan(0, x_len)); + const Botan::BigInt x = Botan::BigInt::from_bytes(in.subspan(0, x_len)); const Botan::BigInt p = Botan::BigInt::from_bytes(in.subspan(x_len, in.size() - x_len)); if(p.is_zero()) { return; } - const size_t x_bits = x.bits(); - if(x_bits % 8 == 0 && x_bits / 8 == x_len) { - x.flip_sign(); - } - - const Botan::BigInt ref = x % p; - - const Botan::Modular_Reducer mod_p(p); - const Botan::BigInt z = mod_p.reduce(x); - - const Botan::BigInt ct = ct_modulo(x, p); - - if(ref != z || ref != ct) { - FUZZER_WRITE_AND_CRASH("X = " << x.to_hex_string() << "\n" - << "P = " << p.to_hex_string() << "\n" - << "Barrett = " << z.to_hex_string() << "\n" - << "Ct = " << ct.to_hex_string() << "\n" - << "Ref = " << ref.to_hex_string() << "\n"); - } + try { + const auto mod_p = Botan::Barrett_Reduction::for_public_modulus(p); + const Botan::BigInt z = mod_p.reduce(x); + + const Botan::BigInt ref = x % p; + const Botan::BigInt ct = ct_modulo(x, p); + + if(ref != z || ref != ct) { + FUZZER_WRITE_AND_CRASH("X = " << x.to_hex_string() << "\n" + << "P = " << p.to_hex_string() << "\n" + << "Barrett = " << z.to_hex_string() << "\n" + << "Ct = " << ct.to_hex_string() << "\n" + << "Ref = " << ref.to_hex_string() << "\n"); + } + } catch(const Botan::Invalid_Argument&) {} } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/bn_cmp.cpp botan3-3.12.0+dfsg/src/fuzzer/bn_cmp.cpp --- botan3-3.7.1+dfsg/src/fuzzer/bn_cmp.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/bn_cmp.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -21,10 +21,10 @@ Botan::BigInt x = Botan::BigInt::from_bytes(in.subspan(1, x_len)); Botan::BigInt y = Botan::BigInt::from_bytes(in.subspan(1 + x_len, in.size() - x_len - 1)); - if(signs & 1) { + if((signs & 1) != 0) { x.flip_sign(); } - if(signs & 2) { + if((signs & 2) != 0) { y.flip_sign(); } @@ -54,17 +54,17 @@ if(is_lt) { FUZZER_ASSERT_TRUE(!is_gt); - FUZZER_ASSERT_TRUE(d1.is_nonzero()); - FUZZER_ASSERT_TRUE(d2.is_nonzero()); - FUZZER_ASSERT_TRUE(d1.is_negative()); - FUZZER_ASSERT_TRUE(d2.is_positive()); + FUZZER_ASSERT_TRUE(d1.signum() != 0); + FUZZER_ASSERT_TRUE(d2.signum() != 0); + FUZZER_ASSERT_TRUE(d1.signum() < 0); + FUZZER_ASSERT_TRUE(d2.signum() > 0); } if(is_gt) { FUZZER_ASSERT_TRUE(!is_lt); - FUZZER_ASSERT_TRUE(d1.is_nonzero()); - FUZZER_ASSERT_TRUE(d2.is_nonzero()); - FUZZER_ASSERT_TRUE(d1.is_positive()); - FUZZER_ASSERT_TRUE(d2.is_negative()); + FUZZER_ASSERT_TRUE(d1.signum() != 0); + FUZZER_ASSERT_TRUE(d2.signum() != 0); + FUZZER_ASSERT_TRUE(d1.signum() > 0); + FUZZER_ASSERT_TRUE(d2.signum() < 0); } } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/bn_sqr.cpp botan3-3.12.0+dfsg/src/fuzzer/bn_sqr.cpp --- botan3-3.7.1+dfsg/src/fuzzer/bn_sqr.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/bn_sqr.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -14,10 +14,10 @@ return; } - Botan::BigInt x = Botan::BigInt::from_bytes(in); + const Botan::BigInt x = Botan::BigInt::from_bytes(in); - Botan::BigInt x_sqr = square(x); - Botan::BigInt x_mul = x * x; + const Botan::BigInt x_sqr = square(x); + const Botan::BigInt x_mul = x * x; FUZZER_ASSERT_EQUAL(x_sqr, x_mul); } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/cert.cpp botan3-3.12.0+dfsg/src/fuzzer/cert.cpp --- botan3-3.7.1+dfsg/src/fuzzer/cert.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/cert.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -16,6 +16,6 @@ try { Botan::DataSource_Memory input(in); - Botan::X509_Certificate cert(input); - } catch(Botan::Exception& e) {} + const Botan::X509_Certificate cert(input); + } catch(const Botan::Exception& e) {} } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/crl.cpp botan3-3.12.0+dfsg/src/fuzzer/crl.cpp --- botan3-3.7.1+dfsg/src/fuzzer/crl.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/crl.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,6 +12,6 @@ void fuzz(std::span in) { try { Botan::DataSource_Memory input(in); - Botan::X509_CRL crl(input); - } catch(Botan::Exception& e) {} + const Botan::X509_CRL crl(input); + } catch(const Botan::Exception& e) {} } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/divide.cpp botan3-3.12.0+dfsg/src/fuzzer/divide.cpp --- botan3-3.7.1+dfsg/src/fuzzer/divide.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/divide.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -13,7 +13,13 @@ } // Save on allocations by making these static - static Botan::BigInt x, y, q, r, ct_q, ct_r, z; + static Botan::BigInt x; + static Botan::BigInt y; + static Botan::BigInt q; + static Botan::BigInt r; + static Botan::BigInt ct_q; + static Botan::BigInt ct_r; + static Botan::BigInt z; x = Botan::BigInt::from_bytes(in.subspan(0, in.size() / 2)); y = Botan::BigInt::from_bytes(in.subspan(in.size() / 2, in.size() - in.size() / 2)); @@ -48,7 +54,7 @@ z = q * y + r; FUZZER_ASSERT_EQUAL(z, x); - Botan::word rw; + Botan::word rw = 0; Botan::ct_divide_word(x, y.word_at(0), ct_q, rw); FUZZER_ASSERT_EQUAL(ct_q, q); FUZZER_ASSERT_EQUAL(rw, r.word_at(0)); diff -Nru botan3-3.7.1+dfsg/src/fuzzer/ec_scalar.cpp botan3-3.12.0+dfsg/src/fuzzer/ec_scalar.cpp --- botan3-3.7.1+dfsg/src/fuzzer/ec_scalar.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/ec_scalar.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,138 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include "fuzzers.h" + +#include +#include +#include + +namespace { + +void check_scalar_arith(const Botan::EC_Group& group, std::span in) { + // Need at least 2 scalars worth of input + const size_t scalar_bytes = group.get_order_bytes(); + + if(in.size() < 2 * scalar_bytes || in.size() > 2 * 2 * scalar_bytes) { + return; + } + + const auto a = Botan::EC_Scalar::from_bytes_mod_order(group, in.first(in.size() / 2)); + const auto b = Botan::EC_Scalar::from_bytes_mod_order(group, in.last(in.size() / 2)); + + const auto one = Botan::EC_Scalar::one(group); + + // a - a == 0 + FUZZER_ASSERT_TRUE((a - a).is_zero()); + + // a + (-a) == 0 + FUZZER_ASSERT_TRUE((a + a.negate()).is_zero()); + + // a * 1 == a + FUZZER_ASSERT_TRUE((a * one) == a); + + // a + b == b + a (commutativity) + FUZZER_ASSERT_TRUE((a + b) == (b + a)); + + // a * b == b * a (commutativity) + FUZZER_ASSERT_TRUE((a * b) == (b * a)); + + if(!a.is_zero()) { + const auto a_inv = a.invert(); + const auto a_inv_vt = a.invert_vartime(); + + // invert and invert_vartime agree + FUZZER_ASSERT_TRUE(a_inv == a_inv_vt); + + // a * a^-1 == 1 + FUZZER_ASSERT_TRUE((a * a_inv) == one); + + // (a^-1)^-1 == a + FUZZER_ASSERT_TRUE(a_inv.invert() == a); + } + + if(!b.is_zero()) { + const auto b_inv = b.invert(); + const auto b_inv_vt = b.invert_vartime(); + + FUZZER_ASSERT_TRUE(b_inv == b_inv_vt); + FUZZER_ASSERT_TRUE((b * b_inv) == one); + } + + // (a + b) * c == a*c + b*c for c = a (distributivity, reusing a as c) + FUZZER_ASSERT_TRUE((a + b) * a == (a * a + b * a)); + + // square_self: a^2 == a * a + auto a_sq = Botan::EC_Scalar(a); + a_sq.square_self(); + FUZZER_ASSERT_TRUE(a_sq == (a * a)); + + /* + Serialization round-trip tests + + The value of zero can be serialized but *not* deserialized + */ + if(!a.is_zero()) { + std::vector a_bytes(scalar_bytes); + a.serialize_to(a_bytes); + const auto a_rt = Botan::EC_Scalar::deserialize(group, a_bytes); + FUZZER_ASSERT_TRUE(a_rt.has_value()); + FUZZER_ASSERT_TRUE(a_rt.value() == a); + } + + if(!b.is_zero()) { + std::vector b_bytes(scalar_bytes); + b.serialize_to(b_bytes); + const auto b_rt = Botan::EC_Scalar::deserialize(group, b_bytes); + FUZZER_ASSERT_TRUE(b_rt.has_value()); + FUZZER_ASSERT_TRUE(b_rt.value() == b); + } +} + +} // namespace + +void fuzz(std::span in) { + // First byte selects the curve + if(in.empty()) { + return; + } + + const uint8_t curve_id = in[0]; + const auto data = in.subspan(1); + + static const Botan::EC_Group p192 = Botan::EC_Group::from_name("secp192r1"); + static const Botan::EC_Group p224 = Botan::EC_Group::from_name("secp224r1"); + static const Botan::EC_Group p256 = Botan::EC_Group::from_name("secp256r1"); + static const Botan::EC_Group p384 = Botan::EC_Group::from_name("secp384r1"); + static const Botan::EC_Group p521 = Botan::EC_Group::from_name("secp521r1"); + static const Botan::EC_Group bp256 = Botan::EC_Group::from_name("brainpool256r1"); + static const Botan::EC_Group bp384 = Botan::EC_Group::from_name("brainpool384r1"); + static const Botan::EC_Group bp512 = Botan::EC_Group::from_name("brainpool512r1"); + static const Botan::EC_Group k256 = Botan::EC_Group::from_name("secp256k1"); + static const Botan::EC_Group frp256 = Botan::EC_Group::from_name("frp256v1"); + static const Botan::EC_Group sm2 = Botan::EC_Group::from_name("sm2p256v1"); + static const Botan::EC_Group numsp512 = Botan::EC_Group::from_name("numsp512d1"); + + constexpr size_t total_curves = 12; + + // NOLINTNEXTLINE(*-avoid-c-arrays) + std::array curves{ + &p192, + &p224, + &p256, + &p384, + &p521, + &bp256, + &bp384, + &bp512, + &k256, + &frp256, + &sm2, + &numsp512, + }; + + const auto& group = *curves[curve_id % total_curves]; + check_scalar_arith(group, data); +} diff -Nru botan3-3.7.1+dfsg/src/fuzzer/ecc_bp256.cpp botan3-3.12.0+dfsg/src/fuzzer/ecc_bp256.cpp --- botan3-3.7.1+dfsg/src/fuzzer/ecc_bp256.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/ecc_bp256.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,6 +12,6 @@ return; } - static Botan::EC_Group bp256("brainpool256r1"); + static const Botan::EC_Group bp256("brainpool256r1"); return check_ecc_math(bp256, in); } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/ecc_bp384.cpp botan3-3.12.0+dfsg/src/fuzzer/ecc_bp384.cpp --- botan3-3.7.1+dfsg/src/fuzzer/ecc_bp384.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/ecc_bp384.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,16 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include "fuzzers.h" + +#include "ecc_helper.h" + +void fuzz(std::span in) { + if(in.size() > 2 * 384 / 8) { + return; + } + static const Botan::EC_Group bp384("brainpool384r1"); + return check_ecc_math(bp384, in); +} diff -Nru botan3-3.7.1+dfsg/src/fuzzer/ecc_bp512.cpp botan3-3.12.0+dfsg/src/fuzzer/ecc_bp512.cpp --- botan3-3.7.1+dfsg/src/fuzzer/ecc_bp512.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/ecc_bp512.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,16 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include "fuzzers.h" + +#include "ecc_helper.h" + +void fuzz(std::span in) { + if(in.size() > 2 * 512 / 8) { + return; + } + static const Botan::EC_Group bp512("brainpool512r1"); + return check_ecc_math(bp512, in); +} diff -Nru botan3-3.7.1+dfsg/src/fuzzer/ecc_frp256.cpp botan3-3.12.0+dfsg/src/fuzzer/ecc_frp256.cpp --- botan3-3.7.1+dfsg/src/fuzzer/ecc_frp256.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/ecc_frp256.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,16 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include "fuzzers.h" + +#include "ecc_helper.h" + +void fuzz(std::span in) { + if(in.size() > 2 * 256 / 8) { + return; + } + static const Botan::EC_Group frp256("frp256v1"); + return check_ecc_math(frp256, in); +} diff -Nru botan3-3.7.1+dfsg/src/fuzzer/ecc_helper.h botan3-3.12.0+dfsg/src/fuzzer/ecc_helper.h --- botan3-3.7.1+dfsg/src/fuzzer/ecc_helper.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/ecc_helper.h 2026-05-07 01:38:28.000000000 +0000 @@ -12,9 +12,7 @@ #include #include #include -#include - -namespace { +#include inline std::ostream& operator<<(std::ostream& o, const Botan::EC_AffinePoint& point) { o << Botan::hex_encode(point.serialize_uncompressed()) << "\n"; @@ -26,7 +24,7 @@ const Botan::BigInt& curve_p, const Botan::BigInt& curve_a, const Botan::BigInt& curve_b) { - Botan::BigInt xpow3 = x * x * x; + const Botan::BigInt xpow3 = x * x * x; Botan::BigInt g = curve_a * x; g += xpow3; @@ -83,6 +81,4 @@ FUZZER_ASSERT_EQUAL(T2, R2); } -} // namespace - #endif diff -Nru botan3-3.7.1+dfsg/src/fuzzer/ecc_numsp512.cpp botan3-3.12.0+dfsg/src/fuzzer/ecc_numsp512.cpp --- botan3-3.7.1+dfsg/src/fuzzer/ecc_numsp512.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/ecc_numsp512.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,16 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include "fuzzers.h" + +#include "ecc_helper.h" + +void fuzz(std::span in) { + if(in.size() > 2 * 512 / 8) { + return; + } + static const Botan::EC_Group numsp512("numsp512d1"); + return check_ecc_math(numsp512, in); +} diff -Nru botan3-3.7.1+dfsg/src/fuzzer/ecc_p224.cpp botan3-3.12.0+dfsg/src/fuzzer/ecc_p224.cpp --- botan3-3.7.1+dfsg/src/fuzzer/ecc_p224.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/ecc_p224.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,16 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include "fuzzers.h" + +#include "ecc_helper.h" + +void fuzz(std::span in) { + if(in.size() > 2 * 224 / 8) { + return; + } + static const Botan::EC_Group p224("secp224r1"); + return check_ecc_math(p224, in); +} diff -Nru botan3-3.7.1+dfsg/src/fuzzer/ecc_p256.cpp botan3-3.12.0+dfsg/src/fuzzer/ecc_p256.cpp --- botan3-3.7.1+dfsg/src/fuzzer/ecc_p256.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/ecc_p256.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,6 +11,6 @@ if(in.size() > 2 * 256 / 8) { return; } - static Botan::EC_Group p256("secp256r1"); + static const Botan::EC_Group p256("secp256r1"); return check_ecc_math(p256, in); } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/ecc_p384.cpp botan3-3.12.0+dfsg/src/fuzzer/ecc_p384.cpp --- botan3-3.7.1+dfsg/src/fuzzer/ecc_p384.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/ecc_p384.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,6 +11,6 @@ if(in.size() > 2 * 384 / 8) { return; } - static Botan::EC_Group p384("secp384r1"); + static const Botan::EC_Group p384("secp384r1"); return check_ecc_math(p384, in); } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/ecc_p521.cpp botan3-3.12.0+dfsg/src/fuzzer/ecc_p521.cpp --- botan3-3.7.1+dfsg/src/fuzzer/ecc_p521.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/ecc_p521.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,6 +11,6 @@ if(in.size() > 2 * (521 + 7) / 8) { return; } - static Botan::EC_Group p521("secp521r1"); + static const Botan::EC_Group p521("secp521r1"); return check_ecc_math(p521, in); } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/ecc_secp256k1.cpp botan3-3.12.0+dfsg/src/fuzzer/ecc_secp256k1.cpp --- botan3-3.7.1+dfsg/src/fuzzer/ecc_secp256k1.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/ecc_secp256k1.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,16 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include "fuzzers.h" + +#include "ecc_helper.h" + +void fuzz(std::span in) { + if(in.size() > 2 * 256 / 8) { + return; + } + static const Botan::EC_Group secp256k1("secp256k1"); + return check_ecc_math(secp256k1, in); +} diff -Nru botan3-3.7.1+dfsg/src/fuzzer/ecc_sm2p256.cpp botan3-3.12.0+dfsg/src/fuzzer/ecc_sm2p256.cpp --- botan3-3.7.1+dfsg/src/fuzzer/ecc_sm2p256.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/ecc_sm2p256.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,16 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include "fuzzers.h" + +#include "ecc_helper.h" + +void fuzz(std::span in) { + if(in.size() > 2 * 256 / 8) { + return; + } + static const Botan::EC_Group sm2("sm2p256v1"); + return check_ecc_math(sm2, in); +} diff -Nru botan3-3.7.1+dfsg/src/fuzzer/fuzzers.h botan3-3.12.0+dfsg/src/fuzzer/fuzzers.h --- botan3-3.7.1+dfsg/src/fuzzer/fuzzers.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/fuzzers.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,19 +9,23 @@ #include #include +#include +#include #include #include #include // for setenv #include -static const size_t max_fuzzer_input_size = 8192; +static constexpr size_t max_fuzzer_input_size = 8192; extern void fuzz(std::span in); +// Need to declare these before defining them; extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv); extern "C" int LLVMFuzzerTestOneInput(const uint8_t in[], size_t len); -extern "C" int LLVMFuzzerInitialize(int*, char***) { +// NOLINTNEXTLINE(*-definitions-in-headers) +extern "C" int LLVMFuzzerInitialize(int* /*argc*/, char*** /*argv*/) { /* * This disables the mlock pool, as overwrites within the pool are * opaque to ASan or other instrumentation. @@ -31,9 +35,18 @@ } // Called by main() in libFuzzer or in main for AFL below +// NOLINTNEXTLINE(*-definitions-in-headers) extern "C" int LLVMFuzzerTestOneInput(const uint8_t in[], size_t len) { if(len <= max_fuzzer_input_size) { - fuzz(std::span(in, len)); + try { + fuzz(std::span(in, len)); + } catch(const std::exception& e) { + std::cerr << "Uncaught exception from fuzzer driver " << e.what() << "\n"; + abort(); + } catch(...) { + std::cerr << "Uncaught exception from fuzzer driver (unknown type)\n"; + abort(); + } } return 0; } @@ -41,7 +54,7 @@ // Some helpers for the fuzzer jigs inline std::shared_ptr fuzzer_rng_as_shared() { - static std::shared_ptr rng = + static const std::shared_ptr rng = std::make_shared(Botan::secure_vector(32)); return rng; } @@ -50,47 +63,45 @@ return *fuzzer_rng_as_shared(); } -#define FUZZER_WRITE_AND_CRASH(expr) \ - do { \ - std::cerr << expr << " @ Line " << __LINE__ << " in " << __FILE__ << "\n"; \ - abort(); \ +// TODO use a constexpr function with std::source_location +// NOLINTNEXTLINE(*-macro-usage) +#define FUZZER_WRITE_AND_CRASH(expr) \ + /* NOLINTNEXTLINE(*-avoid-do-while) */ \ + do { \ + std::cerr << expr << " @ Line " << __LINE__ << " in " << __FILE__ << "\n"; /* NOLINT(*-macro-paren*) */ \ + abort(); \ } while(0) -#define FUZZER_ASSERT_EQUAL(x, y) \ - do { \ - if(x != y) { \ - FUZZER_WRITE_AND_CRASH(#x << " = " << x << " != " << #y << " = " << y << "\n"); \ - } \ +// TODO use a constexpr function with std::source_location +// NOLINTNEXTLINE(*-macro-usage) +#define FUZZER_ASSERT_EQUAL(x, y) \ + /* NOLINTNEXTLINE(*-avoid-do-while) */ \ + do { \ + if((x) != (y)) { \ + FUZZER_WRITE_AND_CRASH(#x << " = " << (x) << " != " << #y << " = " << (y) << "\n"); \ + } \ } while(0) +// TODO use a constexpr function with std::source_location +// NOLINTNEXTLINE(*-macro-usage) #define FUZZER_ASSERT_TRUE(e) \ + /* NOLINTNEXTLINE(*-avoid-do-while) */ \ do { \ + /* NOLINTNEXTLINE(*-simplify-boolean-expr) */ \ if(!(e)) { \ FUZZER_WRITE_AND_CRASH("Expression " << #e << " was false"); \ } \ } while(0) -#if defined(BOTAN_FUZZER_IS_AFL) || defined(BOTAN_FUZZER_IS_TEST) - - /* Stub for AFL */ - - #if defined(BOTAN_FUZZER_IS_AFL) && !defined(__AFL_COMPILER) - #error "Build configured for AFL but not being compiled by AFL compiler" - #endif - - #if defined(BOTAN_FUZZER_IS_TEST) - - #include - -namespace { +#if defined(BOTAN_FUZZER_IS_TEST) -int fuzz_files(char* files[]) { - for(size_t i = 0; files[i]; ++i) { +inline int fuzz_files(char* files[]) { + for(size_t i = 0; files[i] != nullptr; ++i) { std::ifstream in(files[i]); if(in.good()) { std::vector buf(max_fuzzer_input_size); - in.read(reinterpret_cast(buf.data()), buf.size()); + in.read(reinterpret_cast(buf.data()), static_cast(buf.size())); const size_t got = in.gcount(); buf.resize(got); buf.shrink_to_fit(); @@ -102,10 +113,17 @@ return 0; } -} // namespace +#endif + +#if defined(BOTAN_FUZZER_IS_AFL) || defined(BOTAN_FUZZER_IS_TEST) + /* Stub for AFL */ + + #if defined(BOTAN_FUZZER_IS_AFL) && !defined(__AFL_COMPILER) + #error "Build configured for AFL but not being compiled by AFL compiler" #endif +// NOLINTNEXTLINE(*-definitions-in-headers) int main(int argc, char* argv[]) { LLVMFuzzerInitialize(&argc, &argv); @@ -120,7 +138,7 @@ #endif { std::vector buf(max_fuzzer_input_size); - std::cin.read(reinterpret_cast(buf.data()), buf.size()); + std::cin.read(reinterpret_cast(buf.data()), static_cast(buf.size())); const size_t got = std::cin.gcount(); buf.resize(got); @@ -134,6 +152,7 @@ #include +// NOLINTNEXTLINE(*-definitions-in-headers) int main(int argc, char* argv[]) { LLVMFuzzerInitialize(&argc, &argv); diff -Nru botan3-3.7.1+dfsg/src/fuzzer/gcd.cpp botan3-3.12.0+dfsg/src/fuzzer/gcd.cpp --- botan3-3.7.1+dfsg/src/fuzzer/gcd.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/gcd.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -29,8 +29,9 @@ return; } - const Botan::BigInt x = Botan::BigInt::from_bytes(in.subspan(in.size() / 2)); - const Botan::BigInt y = Botan::BigInt::from_bytes(in.subspan(in.size() / 2, in.size() - (in.size() / 2))); + const size_t half = in.size() / 2; + const Botan::BigInt x = Botan::BigInt::from_bytes(in.subspan(0, half)); + const Botan::BigInt y = Botan::BigInt::from_bytes(in.subspan(half, in.size() - half)); const Botan::BigInt ref = ref_gcd(x, y); const Botan::BigInt lib = Botan::gcd(x, y); diff -Nru botan3-3.7.1+dfsg/src/fuzzer/invert.cpp botan3-3.12.0+dfsg/src/fuzzer/invert.cpp --- botan3-3.7.1+dfsg/src/fuzzer/invert.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/invert.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -16,10 +16,14 @@ if(n.is_even() && mod.is_even()) { return 0; } - Botan::BigInt u = mod, v = n; - Botan::BigInt A = 1, B = 0, C = 0, D = 1; + Botan::BigInt u = mod; + Botan::BigInt v = n; + Botan::BigInt A = 1; + Botan::BigInt B = 0; + Botan::BigInt C = 0; + Botan::BigInt D = 1; - while(u.is_nonzero()) { + while(!u.is_zero()) { const size_t u_zero_bits = Botan::low_zero_bits(u); u >>= u_zero_bits; for(size_t i = 0; i != u_zero_bits; ++i) { @@ -57,7 +61,7 @@ return 0; // no modular inverse } - while(D.is_negative()) { + while(D.signum() < 0) { D += mod; } while(D >= mod) { @@ -77,7 +81,7 @@ } const Botan::BigInt x = Botan::BigInt::from_bytes(in.subspan(0, in.size() / 2)); - Botan::BigInt mod = Botan::BigInt::from_bytes(in.subspan(in.size() / 2, in.size() - in.size() / 2)); + const Botan::BigInt mod = Botan::BigInt::from_bytes(in.subspan(in.size() / 2, in.size() - in.size() / 2)); if(mod < 2) { return; diff -Nru botan3-3.7.1+dfsg/src/fuzzer/ipv4.cpp botan3-3.12.0+dfsg/src/fuzzer/ipv4.cpp --- botan3-3.7.1+dfsg/src/fuzzer/ipv4.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/ipv4.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,16 +10,16 @@ #include void fuzz(std::span in) { - std::string_view str(reinterpret_cast(in.data()), in.size()); + const std::string_view str(reinterpret_cast(in.data()), in.size()); if(auto ipv4 = Botan::string_to_ipv4(str)) { const auto rt = Botan::ipv4_to_string(*ipv4); FUZZER_ASSERT_EQUAL(str, rt); } if(in.size() == 4) { - uint32_t ip = Botan::load_be(in.data(), 0); - auto s = Botan::ipv4_to_string(ip); - auto rt = Botan::string_to_ipv4(s); + const uint32_t ip = Botan::load_be(in.data(), 0); + const auto s = Botan::ipv4_to_string(ip); + const auto rt = Botan::string_to_ipv4(s); FUZZER_ASSERT_TRUE(rt.has_value()); FUZZER_ASSERT_EQUAL(rt.value(), ip); } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/mem_pool.cpp botan3-3.12.0+dfsg/src/fuzzer/mem_pool.cpp --- botan3-3.7.1+dfsg/src/fuzzer/mem_pool.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/mem_pool.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -26,10 +27,10 @@ struct RawPage { public: - RawPage(void* p) : m_p(p) {} + explicit RawPage(void* p) : m_p(p) {} ~RawPage() { - // NOLINTNEXTLINE(*-no-malloc) + // NOLINTNEXTLINE(*-no-malloc,*-owning-memory) std::free(m_p); } @@ -58,10 +59,10 @@ for(size_t i = 0; i != count; ++i) { void* ptr = nullptr; - int rc = ::posix_memalign(&ptr, page_size, page_size); + const int rc = ::posix_memalign(&ptr, page_size, page_size); FUZZER_ASSERT_EQUAL(rc, 0); - if(ptr) { + if(ptr != nullptr) { pages.push_back(RawPage(ptr)); } } @@ -76,19 +77,19 @@ const size_t page_size = 4096; // static to avoid repeated allocations - static std::vector raw_mem = allocate_raw_pages(page_count, page_size); + static const std::vector raw_mem = allocate_raw_pages(page_count, page_size); std::vector mem_pages; mem_pages.reserve(raw_mem.size()); - for(size_t i = 0; i != raw_mem.size(); ++i) { - mem_pages.push_back(raw_mem[i].ptr()); + for(const auto& rm : raw_mem) { + mem_pages.push_back(rm.ptr()); } Botan::Memory_Pool pool(mem_pages, page_size); std::map ptrs; size_t in_len = in.size(); - auto x = in.data(); + const auto* x = in.data(); while(in_len > 0) { const uint8_t op = in[0] % 2; size_t idx = (in[0] >> 1); @@ -105,7 +106,7 @@ const size_t plen = idx + 1; // ensure non-zero uint8_t* p = static_cast(pool.allocate(plen)); - if(p) { + if(p != nullptr) { const size_t expected_alignment = compute_expected_alignment(plen); const size_t alignment = reinterpret_cast(p) % expected_alignment; if(alignment != 0) { @@ -126,7 +127,7 @@ std::memset(p, static_cast(idx), plen); auto insert = ptrs.insert(std::make_pair(p, plen)); - if(insert.second == false) { + if(!insert.second) { FUZZER_WRITE_AND_CRASH("Pointer " << static_cast(p) << " already existed\n"); } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/mode_padding.cpp botan3-3.12.0+dfsg/src/fuzzer/mode_padding.cpp --- botan3-3.7.1+dfsg/src/fuzzer/mode_padding.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/mode_padding.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -15,7 +15,7 @@ if(in.size() <= 2) { return in.size(); } - size_t len = in.size(); + const size_t len = in.size(); const size_t padding_length = in[len - 1]; @@ -35,7 +35,7 @@ } size_t ref_x923_unpad(std::span in) { - size_t len = in.size(); + const size_t len = in.size(); if(len <= 2) { return len; } @@ -57,7 +57,7 @@ } size_t ref_oneandzero_unpad(std::span in) { - size_t len = in.size(); + const size_t len = in.size(); if(len <= 2) { return len; } @@ -82,7 +82,7 @@ } size_t ref_esp_unpad(std::span in) { - size_t len = in.size(); + const size_t len = in.size(); if(len <= 2) { return len; } @@ -104,7 +104,7 @@ } uint16_t ref_tls_cbc_unpad(std::span in) { - size_t len = in.size(); + const size_t len = in.size(); if(len == 0) { return 0; } @@ -130,33 +130,33 @@ } // namespace void fuzz(std::span in) { - static Botan::PKCS7_Padding pkcs7; - static Botan::ANSI_X923_Padding x923; - static Botan::OneAndZeros_Padding oneandzero; - static Botan::ESP_Padding esp; + static const Botan::PKCS7_Padding pkcs7; + static const Botan::ANSI_X923_Padding x923; + static const Botan::OneAndZeros_Padding oneandzero; + static const Botan::ESP_Padding esp; - size_t len = in.size(); + const size_t len = in.size(); if(pkcs7.valid_blocksize(len)) { - const size_t ct_pkcs7 = pkcs7.unpad(in.data(), len); + const size_t ct_pkcs7 = pkcs7.unpad(in); const size_t ref_pkcs7 = ref_pkcs7_unpad(in); FUZZER_ASSERT_EQUAL(ct_pkcs7, ref_pkcs7); } if(x923.valid_blocksize(len)) { - const size_t ct_x923 = x923.unpad(in.data(), len); + const size_t ct_x923 = x923.unpad(in); const size_t ref_x923 = ref_x923_unpad(in); FUZZER_ASSERT_EQUAL(ct_x923, ref_x923); } if(oneandzero.valid_blocksize(len)) { - const size_t ct_oneandzero = oneandzero.unpad(in.data(), len); + const size_t ct_oneandzero = oneandzero.unpad(in); const size_t ref_oneandzero = ref_oneandzero_unpad(in); FUZZER_ASSERT_EQUAL(ct_oneandzero, ref_oneandzero); } if(esp.valid_blocksize(len)) { - const size_t ct_esp = esp.unpad(in.data(), len); + const size_t ct_esp = esp.unpad(in); const size_t ref_esp = ref_esp_unpad(in); FUZZER_ASSERT_EQUAL(ct_esp, ref_esp); } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/mp_comba_mul.cpp botan3-3.12.0+dfsg/src/fuzzer/mp_comba_mul.cpp --- botan3-3.7.1+dfsg/src/fuzzer/mp_comba_mul.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/mp_comba_mul.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,7 +12,7 @@ if(in.empty() || words > 2 * 16) { return; } - size_t in_len = in.size(); + const size_t in_len = in.size(); word x[24] = {0}; word y[24] = {0}; diff -Nru botan3-3.7.1+dfsg/src/fuzzer/mp_fuzzers.h botan3-3.12.0+dfsg/src/fuzzer/mp_fuzzers.h --- botan3-3.7.1+dfsg/src/fuzzer/mp_fuzzers.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/mp_fuzzers.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,24 +10,28 @@ #include "fuzzers.h" #include - -#if BOTAN_MP_WORD_BITS == 64 - #define WORD_FORMAT_STRING "%016lX" -#else - #define WORD_FORMAT_STRING "%08X" -#endif +#include +#include +#include using Botan::word; -namespace { +inline std::string format_word_vec(std::string_view name, const word x[], size_t x_len) { + std::ostringstream oss; + oss << name << " = "; + + constexpr size_t width = 2 * sizeof(word); -inline void dump_word_vec(const char* name, const word x[], size_t x_len) { - fprintf(stderr, "%s = ", name); for(size_t i = 0; i != x_len; ++i) { - fprintf(stderr, WORD_FORMAT_STRING, x[i]); - fprintf(stderr, " "); + oss << std::uppercase << std::setw(width) << std::setfill('0') << std::hex << x[i] << " "; } - fprintf(stderr, "\n"); + + oss << "\n"; + return oss.str(); +} + +inline void dump_word_vec(std::string_view name, const word x[], size_t x_len) { + std::cerr << format_word_vec(name, x, x_len); } inline void compare_word_vec(const word x[], size_t x_len, const word y[], size_t y_len, const char* comparing) { @@ -58,6 +62,4 @@ } } -} // namespace - #endif diff -Nru botan3-3.7.1+dfsg/src/fuzzer/mp_redc.cpp botan3-3.12.0+dfsg/src/fuzzer/mp_redc.cpp --- botan3-3.7.1+dfsg/src/fuzzer/mp_redc.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/mp_redc.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -14,8 +14,8 @@ word z[2 * N] = {0}; - word z_script[2 * N] = {0}; - word z_ref[2 * N] = {0}; + word r_script[N] = {0}; + word r_ref[N] = {0}; word p[N] = {0}; word p_dash = 0; @@ -25,39 +25,35 @@ std::memcpy(p, in.data() + sizeof(z), sizeof(p)); std::memcpy(&p_dash, in.data() + sizeof(z) + sizeof(p), sizeof(p_dash)); - for(size_t i = 0; i != 2 * N; ++i) { - z_script[i] = z_ref[i] = z[i]; - } - if(N == 4) { - Botan::bigint_monty_redc_4(z_script, p, p_dash, ws); + Botan::bigint_monty_redc_4(r_script, z, p, p_dash, ws); } else if(N == 6) { - Botan::bigint_monty_redc_6(z_script, p, p_dash, ws); + Botan::bigint_monty_redc_6(r_script, z, p, p_dash, ws); } else if(N == 8) { - Botan::bigint_monty_redc_8(z_script, p, p_dash, ws); + Botan::bigint_monty_redc_8(r_script, z, p, p_dash, ws); } else if(N == 16) { - Botan::bigint_monty_redc_16(z_script, p, p_dash, ws); + Botan::bigint_monty_redc_16(r_script, z, p, p_dash, ws); } else if(N == 24) { - Botan::bigint_monty_redc_24(z_script, p, p_dash, ws); + Botan::bigint_monty_redc_24(r_script, z, p, p_dash, ws); } else if(N == 32) { - Botan::bigint_monty_redc_32(z_script, p, p_dash, ws); + Botan::bigint_monty_redc_32(r_script, z, p, p_dash, ws); } else { std::abort(); } - Botan::bigint_monty_redc_generic(z_ref, 2 * N, p, N, p_dash, ws); + Botan::bigint_monty_redc_generic(r_ref, z, 2 * N, p, N, p_dash, ws); - for(size_t i = 0; i != 2 * N; ++i) { - if(z_script[i] != z_ref[i]) { + for(size_t i = 0; i != N; ++i) { + if(r_script[i] != r_ref[i]) { dump_word_vec("input", z, 2 * N); - dump_word_vec("z_script", z_script, 2 * N); - dump_word_vec("z_ref", z_ref, 2 * N); + dump_word_vec("r_script", r_script, 2 * N); + dump_word_vec("r_ref", r_ref, 2 * N); dump_word_vec("p", p, N); dump_word_vec("p_dash", &p_dash, 1); std::abort(); } } - compare_word_vec(z_script, 2 * N, z_ref, 2 * N, "redc generic vs specialized"); + compare_word_vec(r_script, N, r_ref, N, "redc generic vs specialized"); } } // namespace diff -Nru botan3-3.7.1+dfsg/src/fuzzer/mp_redc_crandall.cpp botan3-3.12.0+dfsg/src/fuzzer/mp_redc_crandall.cpp --- botan3-3.7.1+dfsg/src/fuzzer/mp_redc_crandall.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/mp_redc_crandall.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,20 +9,28 @@ #include #include +namespace { + +consteval word crandall_C() { + if(sizeof(word) == 8) { + // secp256k1 modulus + return static_cast(0x1000003d1); + } else { + // 128 bit prime with largest possible C + return 0xffffffe1; + } +} + +} // namespace + void fuzz(std::span in) { if(in.size() != 8 * sizeof(word)) { return; } -#if BOTAN_MP_WORD_BITS == 64 - // secp256k1 modulus - const word C = 0x1000003d1; -#else - // 128 bit prime with largest possible C - const word C = 0xffffffe1; -#endif + constexpr word C = crandall_C(); - static const Botan::BigInt refp = Botan::BigInt::power_of_2(4 * BOTAN_MP_WORD_BITS) - C; + static const Botan::BigInt refp = Botan::BigInt::power_of_2(4 * 8 * sizeof(C)) - C; static const Botan::BigInt refp2 = refp * refp; const auto refz = Botan::BigInt::from_bytes(in); diff -Nru botan3-3.7.1+dfsg/src/fuzzer/ocsp.cpp botan3-3.12.0+dfsg/src/fuzzer/ocsp.cpp --- botan3-3.7.1+dfsg/src/fuzzer/ocsp.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/ocsp.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,6 @@ void fuzz(std::span in) { try { - Botan::OCSP::Response response(in.data(), in.size()); - } catch(Botan::Exception& e) {} + const Botan::OCSP::Response response(in.data(), in.size()); + } catch(const Botan::Exception& e) {} } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/os2ecp.cpp botan3-3.12.0+dfsg/src/fuzzer/os2ecp.cpp --- botan3-3.7.1+dfsg/src/fuzzer/os2ecp.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/os2ecp.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -13,7 +13,7 @@ void check_os2ecp(const Botan::EC_Group& group, std::span in) { try { Botan::EC_AffinePoint(group, in); - } catch(Botan::Exception& e) {} + } catch(const Botan::Exception& e) {} } } // namespace @@ -23,13 +23,13 @@ return; } - static Botan::EC_Group p192 = Botan::EC_Group::from_name("secp192r1"); - static Botan::EC_Group p224 = Botan::EC_Group::from_name("secp224r1"); - static Botan::EC_Group p256 = Botan::EC_Group::from_name("secp256r1"); - static Botan::EC_Group p384 = Botan::EC_Group::from_name("secp384r1"); - static Botan::EC_Group p521 = Botan::EC_Group::from_name("secp521r1"); - static Botan::EC_Group bp256 = Botan::EC_Group::from_name("brainpool256r1"); - static Botan::EC_Group bp512 = Botan::EC_Group::from_name("brainpool512r1"); + static const Botan::EC_Group p192 = Botan::EC_Group::from_name("secp192r1"); + static const Botan::EC_Group p224 = Botan::EC_Group::from_name("secp224r1"); + static const Botan::EC_Group p256 = Botan::EC_Group::from_name("secp256r1"); + static const Botan::EC_Group p384 = Botan::EC_Group::from_name("secp384r1"); + static const Botan::EC_Group p521 = Botan::EC_Group::from_name("secp521r1"); + static const Botan::EC_Group bp256 = Botan::EC_Group::from_name("brainpool256r1"); + static const Botan::EC_Group bp512 = Botan::EC_Group::from_name("brainpool512r1"); check_os2ecp(p192, in); check_os2ecp(p224, in); diff -Nru botan3-3.7.1+dfsg/src/fuzzer/pkcs1.cpp botan3-3.12.0+dfsg/src/fuzzer/pkcs1.cpp --- botan3-3.7.1+dfsg/src/fuzzer/pkcs1.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/pkcs1.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -39,21 +39,22 @@ std::vector lib_result; std::vector ref_result; - bool lib_rejected = false, ref_rejected = false; + bool lib_rejected = false; + bool ref_rejected = false; try { lib_result.resize(in.size()); - auto written = (static_cast(&pkcs1))->unpad(lib_result, in); + auto written = (static_cast(&pkcs1))->unpad(lib_result, in); lib_rejected = !written.has_value().as_bool(); lib_result.resize(written.value_or(0)); - } catch(Botan::Decoding_Error&) { + } catch(const Botan::Decoding_Error&) { lib_rejected = true; } try { ref_result = simple_pkcs1_unpad(in.data(), in.size()); - } catch(Botan::Decoding_Error& e) { + } catch(const Botan::Decoding_Error& e) { ref_rejected = true; } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/pkcs8.cpp botan3-3.12.0+dfsg/src/fuzzer/pkcs8.cpp --- botan3-3.7.1+dfsg/src/fuzzer/pkcs8.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/pkcs8.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -14,12 +14,15 @@ void fuzz(std::span in) { try { Botan::DataSource_Memory input(in); - std::unique_ptr key = Botan::PKCS8::load_key(input); - } catch(Botan::Exception& e) {} + Botan::PKCS8::load_key(input); + } catch(const Botan::Exception& e) {} /* * This avoids OOMs in OSS-Fuzz caused by storing precomputations * for thousands of curves randomly generated by the fuzzer. + * + * TODO(Botan4) we can remove this call once support for explicit curves + * is removed */ Botan::EC_Group::clear_registered_curve_data(); } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/pow_mod.cpp botan3-3.12.0+dfsg/src/fuzzer/pow_mod.cpp --- botan3-3.7.1+dfsg/src/fuzzer/pow_mod.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/pow_mod.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,7 +7,7 @@ #include "fuzzers.h" #include -#include +#include namespace { @@ -19,7 +19,7 @@ return 1; } - Botan::Modular_Reducer mod_p(p); + auto mod_p = Botan::Barrett_Reduction::for_public_modulus(p); Botan::BigInt y = 1; while(n > 1) { @@ -62,5 +62,5 @@ << "Z = " << z.to_hex_string() << "\n" << "R = " << ref.to_hex_string() << "\n"); } - } catch(Botan::Exception& e) {} + } catch(const Botan::Exception& e) {} } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/ressol.cpp botan3-3.12.0+dfsg/src/fuzzer/ressol.cpp --- botan3-3.7.1+dfsg/src/fuzzer/ressol.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/ressol.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,13 +7,14 @@ #include "fuzzers.h" #include -#include +#include void fuzz(std::span in) { // Ressol is mostly used for ECC point decompression so best to test smaller sizes static const size_t p_bits = 256; - static const Botan::BigInt p = random_prime(fuzzer_rng(), p_bits); - static const Botan::Modular_Reducer mod_p(p); + // Use p == 1 mod 4 since sqrt modulo p == 3 mod 4 is a fast case + static const Botan::BigInt p = random_prime(fuzzer_rng(), p_bits, 0, 1, 4); + static auto mod_p = Botan::Barrett_Reduction::for_public_modulus(p); if(in.size() > p_bits / 8) { return; @@ -21,7 +22,7 @@ try { const Botan::BigInt a = Botan::BigInt::from_bytes(in); - Botan::BigInt a_sqrt = Botan::sqrt_modulo_prime(a, p); + const Botan::BigInt a_sqrt = Botan::sqrt_modulo_prime(a, p); if(a_sqrt > 0) { const Botan::BigInt a_redc = mod_p.reduce(a); @@ -34,5 +35,5 @@ << "Z = " << z.to_hex_string() << "\n"); } } - } catch(Botan::Exception& e) {} + } catch(const Botan::Exception& e) {} } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/tls_13_handshake_layer.cpp botan3-3.12.0+dfsg/src/fuzzer/tls_13_handshake_layer.cpp --- botan3-3.7.1+dfsg/src/fuzzer/tls_13_handshake_layer.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/tls_13_handshake_layer.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include "fuzzers.h" +#include #include #include @@ -22,14 +23,14 @@ } // namespace void fuzz(std::span in) { - static Botan::TLS::Default_Policy policy; + static const Botan::TLS::Default_Policy policy; try { auto hl1 = prepare(in); - Botan::TLS::Transcript_Hash_State ths("SHA-256"); - while(hl1.next_message(policy, ths).has_value()) {}; + Botan::TLS::Transcript_Hash_State transcript_hash("SHA-256"); + while(hl1.next_message(policy, transcript_hash).has_value()) {}; auto hl2 = prepare(in); while(hl2.next_post_handshake_message(policy).has_value()) {}; - } catch(Botan::Exception& e) {} + } catch(const Botan::Exception& e) {} } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/tls_client.cpp botan3-3.12.0+dfsg/src/fuzzer/tls_client.cpp --- botan3-3.7.1+dfsg/src/fuzzer/tls_client.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/tls_client.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,14 +7,24 @@ #include "fuzzers.h" #include +#include +#include #include +#include +#include #include class Fuzzer_TLS_Client_Creds : public Botan::Credentials_Manager { public: - std::string psk_identity_hint(const std::string&, const std::string&) override { return "psk_hint"; } + std::string psk_identity_hint(const std::string& /*type*/, const std::string& /*context*/) override { + return "psk_hint"; + } - std::string psk_identity(const std::string&, const std::string&, const std::string&) override { return "psk_id"; } + std::string psk_identity(const std::string& /*type*/, + const std::string& /*context*/, + const std::string& /*hint*/) override { + return "psk_id"; + } Botan::secure_vector session_ticket_key() override { return Botan::hex_decode_locked("AABBCCDDEEFF00112233445566778899"); @@ -41,11 +51,11 @@ class Fuzzer_TLS_Policy : public Botan::TLS::Policy { public: - std::vector ciphersuite_list(Botan::TLS::Protocol_Version) const override { + std::vector ciphersuite_list(Botan::TLS::Protocol_Version version) const override { std::vector ciphersuites; for(auto&& suite : Botan::TLS::Ciphersuite::all_known_ciphersuites()) { - if(suite.valid() == false) { + if(suite.valid() && suite.usable_in_version(version)) { ciphersuites.push_back(suite.ciphersuite_code()); } } @@ -56,15 +66,15 @@ class Fuzzer_TLS_Client_Callbacks : public Botan::TLS::Callbacks { public: - void tls_emit_data(std::span) override { + void tls_emit_data(std::span /*data*/) override { // discard } - void tls_record_received(uint64_t, std::span) override { + void tls_record_received(uint64_t /*rec*/, std::span /*data*/) override { // ignore peer data } - void tls_alert(Botan::TLS::Alert) override { + void tls_alert(Botan::TLS::Alert /*alert*/) override { // ignore alert } @@ -91,8 +101,8 @@ auto session_manager = std::make_shared(); auto policy = std::make_shared(); - Botan::TLS::Protocol_Version client_offer = Botan::TLS::Protocol_Version::TLS_V12; - Botan::TLS::Server_Information info("server.name", 443); + const Botan::TLS::Protocol_Version client_offer = Botan::TLS::Protocol_Version::TLS_V12; + const Botan::TLS::Server_Information info("server.name", 443); auto callbacks = std::make_shared(); auto creds = std::make_shared(); @@ -100,5 +110,5 @@ try { client.received_data(in); - } catch(std::exception& e) {} + } catch(const std::exception& e) {} } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/tls_client_hello.cpp botan3-3.12.0+dfsg/src/fuzzer/tls_client_hello.cpp --- botan3-3.7.1+dfsg/src/fuzzer/tls_client_hello.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/tls_client_hello.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,11 +6,11 @@ #include "fuzzers.h" -#include +#include void fuzz(std::span in) { try { - std::vector v(in.begin(), in.end()); - Botan::TLS::Client_Hello_12 ch(v); // TODO: We might want to do that for TLS 1.3 as well - } catch(Botan::Exception& e) {} + const std::vector v(in.begin(), in.end()); + const Botan::TLS::Client_Hello_12 ch(v); // TODO: We might want to do that for TLS 1.3 as well + } catch(const Botan::Exception& e) {} } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/tls_server.cpp botan3-3.12.0+dfsg/src/fuzzer/tls_server.cpp --- botan3-3.7.1+dfsg/src/fuzzer/tls_server.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/tls_server.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,8 +9,14 @@ #include #include #include +#include +#include +#include +#include #include +#include #include +#include #include @@ -82,9 +88,15 @@ return Botan::hex_decode_locked("AABBCCDDEEFF00112233445566778899"); } - std::string psk_identity_hint(const std::string&, const std::string&) override { return "psk_hint"; } + std::string psk_identity_hint(const std::string& /*type*/, const std::string& /*context*/) override { + return "psk_hint"; + } - std::string psk_identity(const std::string&, const std::string&, const std::string&) override { return "psk_id"; } + std::string psk_identity(const std::string& /*type*/, + const std::string& /*context*/, + const std::string& /*hint*/) override { + return "psk_id"; + } std::vector find_preshared_keys( std::string_view host, @@ -107,11 +119,11 @@ class Fuzzer_TLS_Policy : public Botan::TLS::Policy { public: - std::vector ciphersuite_list(Botan::TLS::Protocol_Version) const override { + std::vector ciphersuite_list(Botan::TLS::Protocol_Version version) const override { std::vector ciphersuites; for(auto&& suite : Botan::TLS::Ciphersuite::all_known_ciphersuites()) { - if(suite.valid()) { + if(suite.valid() and suite.usable_in_version(version)) { ciphersuites.push_back(suite.ciphersuite_code()); } } @@ -122,15 +134,15 @@ class Fuzzer_TLS_Server_Callbacks : public Botan::TLS::Callbacks { public: - void tls_emit_data(std::span) override { + void tls_emit_data(std::span /*data*/) override { // discard } - void tls_record_received(uint64_t, std::span) override { + void tls_record_received(uint64_t /*rec*/, std::span /*data*/) override { // ignore peer data } - void tls_alert(Botan::TLS::Alert) override { + void tls_alert(Botan::TLS::Alert /*alert*/) override { // ignore alert } @@ -167,15 +179,15 @@ auto session_manager = std::make_shared(); auto policy = std::make_shared(); - Botan::TLS::Server_Information info("server.name", 443); + const Botan::TLS::Server_Information info("server.name", 443); auto creds = std::make_shared(); auto callbacks = std::make_shared(); - const bool is_datagram = in[0] & 1; + const bool is_datagram = (in[0] & 1) == 1; Botan::TLS::Server server(callbacks, session_manager, creds, policy, fuzzer_rng_as_shared(), is_datagram); try { server.received_data(in.subspan(1, in.size() - 1)); - } catch(std::exception& e) {} + } catch(const std::exception& e) {} } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/uri.cpp botan3-3.12.0+dfsg/src/fuzzer/uri.cpp --- botan3-3.7.1+dfsg/src/fuzzer/uri.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/uri.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -15,5 +15,5 @@ try { Botan::URI::from_any(std::string(reinterpret_cast(input.data()), input.size())); - } catch(Botan::Exception& e) {} + } catch(const Botan::Exception& e) {} } diff -Nru botan3-3.7.1+dfsg/src/fuzzer/x509_path.cpp botan3-3.12.0+dfsg/src/fuzzer/x509_path.cpp --- botan3-3.7.1+dfsg/src/fuzzer/x509_path.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/fuzzer/x509_path.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -14,15 +14,15 @@ Botan::DataSource_Memory input(in); try { - Botan::X509_Certificate subject(input); - Botan::X509_Certificate issuer(input); + const Botan::X509_Certificate subject(input); + const Botan::X509_Certificate issuer(input); std::vector roots; - std::unique_ptr root_store(new Botan::Certificate_Store_In_Memory(issuer)); + const std::unique_ptr root_store(new Botan::Certificate_Store_In_Memory(issuer)); roots.push_back(root_store.get()); - Botan::Path_Validation_Restrictions restrictions; + const Botan::Path_Validation_Restrictions restrictions; x509_path_validate({subject}, restrictions, roots); - } catch(Botan::Exception& e) {} + } catch(const Botan::Exception& e) {} } diff -Nru botan3-3.7.1+dfsg/src/lib/asn1/alg_id.cpp botan3-3.12.0+dfsg/src/lib/asn1/alg_id.cpp --- botan3-3.7.1+dfsg/src/lib/asn1/alg_id.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/asn1/alg_id.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -27,8 +27,8 @@ /* * Create an AlgorithmIdentifier */ -AlgorithmIdentifier::AlgorithmIdentifier(const OID& oid, Encoding_Option option) : m_oid(oid), m_parameters() { - const uint8_t DER_NULL[] = {0x05, 0x00}; +AlgorithmIdentifier::AlgorithmIdentifier(const OID& oid, Encoding_Option option) : m_oid(oid) { + constexpr uint8_t DER_NULL[] = {0x05, 0x00}; if(option == USE_NULL_PARAM) { m_parameters.assign(DER_NULL, DER_NULL + 2); @@ -38,9 +38,8 @@ /* * Create an AlgorithmIdentifier */ -AlgorithmIdentifier::AlgorithmIdentifier(std::string_view oid, Encoding_Option option) : - m_oid(OID::from_string(oid)), m_parameters() { - const uint8_t DER_NULL[] = {0x05, 0x00}; +AlgorithmIdentifier::AlgorithmIdentifier(std::string_view oid, Encoding_Option option) : m_oid(OID::from_string(oid)) { + constexpr uint8_t DER_NULL[2] = {0x05, 0x00}; if(option == USE_NULL_PARAM) { m_parameters.assign(DER_NULL, DER_NULL + 2); diff -Nru botan3-3.7.1+dfsg/src/lib/asn1/asn1_obj.cpp botan3-3.12.0+dfsg/src/lib/asn1/asn1_obj.cpp --- botan3-3.7.1+dfsg/src/lib/asn1/asn1_obj.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/asn1/asn1_obj.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,11 +7,12 @@ #include +#include #include #include #include #include -#include +#include #include namespace Botan { @@ -23,11 +24,15 @@ return output; } +BER_Object::~BER_Object() { + secure_scrub_memory(m_value); +} + /* * Check a type invariant on BER data */ void BER_Object::assert_is_a(ASN1_Type expected_type_tag, ASN1_Class expected_class_tag, std::string_view descr) const { - if(this->is_a(expected_type_tag, expected_class_tag) == false) { + if(!this->is_a(expected_type_tag, expected_class_tag)) { std::stringstream msg; msg << "Tag mismatch when decoding " << descr << " got "; @@ -160,7 +165,7 @@ /* * BER Decoding Exceptions */ -BER_Decoding_Error::BER_Decoding_Error(std::string_view str) : Decoding_Error(fmt("BER: {}", str)) {} +BER_Decoding_Error::BER_Decoding_Error(std::string_view err) : Decoding_Error(fmt("BER: {}", err)) {} BER_Bad_Tag::BER_Bad_Tag(std::string_view str, uint32_t tagging) : BER_Decoding_Error(fmt("{}: {}", str, tagging)) {} @@ -183,24 +188,21 @@ * Convert a BER object into a string object */ std::string to_string(const BER_Object& obj) { - return std::string(cast_uint8_ptr_to_char(obj.bits()), obj.length()); + return bytes_to_string(obj.data()); } /* * Do heuristic tests for BER data */ bool maybe_BER(DataSource& source) { - uint8_t first_u8; - if(!source.peek_byte(first_u8)) { + uint8_t first_u8 = 0; + if(source.peek_byte(first_u8) == 0) { BOTAN_ASSERT_EQUAL(source.read_byte(first_u8), 0, "Expected EOF"); throw Stream_IO_Error("ASN1::maybe_BER: Source was empty"); } const auto cons_seq = static_cast(ASN1_Class::Constructed) | static_cast(ASN1_Type::Sequence); - if(first_u8 == cons_seq) { - return true; - } - return false; + return first_u8 == cons_seq; } } // namespace ASN1 diff -Nru botan3-3.7.1+dfsg/src/lib/asn1/asn1_obj.h botan3-3.12.0+dfsg/src/lib/asn1/asn1_obj.h --- botan3-3.7.1+dfsg/src/lib/asn1/asn1_obj.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/asn1/asn1_obj.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,25 +8,24 @@ #define BOTAN_ASN1_OBJECT_TYPES_H_ #include -#include -#include #include #include #include #include #include -#include #include namespace Botan { class BER_Decoder; class DER_Encoder; +class ASN1_Time; // in asn1_time.h +typedef ASN1_Time X509_Time; /** * ASN.1 Class Tags */ -enum class ASN1_Class : uint32_t { +enum class ASN1_Class : uint32_t /* NOLINT(performance-enum-size) */ { Universal = 0b0000'0000, Application = 0b0100'0000, ContextSpecific = 0b1000'0000, @@ -41,7 +40,7 @@ /** * ASN.1 Type Tags */ -enum class ASN1_Type : uint32_t { +enum class ASN1_Type : uint32_t /* NOLINT(performance-enum-size) */ { Eoc = 0x00, Boolean = 0x01, Integer = 0x02, @@ -69,7 +68,7 @@ }; inline bool intersects(ASN1_Class x, ASN1_Class y) { - return static_cast(x) & static_cast(y); + return (static_cast(x) & static_cast(y)) != 0; } inline ASN1_Type operator|(ASN1_Type x, ASN1_Type y) { @@ -118,6 +117,8 @@ ASN1_Object() = default; ASN1_Object(const ASN1_Object&) = default; ASN1_Object& operator=(const ASN1_Object&) = default; + ASN1_Object(ASN1_Object&&) = default; + ASN1_Object& operator=(ASN1_Object&&) = default; virtual ~ASN1_Object() = default; }; @@ -126,15 +127,13 @@ */ class BOTAN_PUBLIC_API(2, 0) BER_Object final { public: - BER_Object() : m_type_tag(ASN1_Type::NoObject), m_class_tag(ASN1_Class::Universal) {} + BER_Object() = default; BER_Object(const BER_Object& other) = default; - - BER_Object& operator=(const BER_Object& other) = default; - BER_Object(BER_Object&& other) = default; - + BER_Object& operator=(const BER_Object& other) = default; BER_Object& operator=(BER_Object&& other) = default; + ~BER_Object(); bool is_set() const { return m_type_tag != ASN1_Type::NoObject; } @@ -161,9 +160,9 @@ bool is_a(int type_tag, ASN1_Class class_tag) const; private: - ASN1_Type m_type_tag; - ASN1_Class m_class_tag; - secure_vector m_value; + ASN1_Type m_type_tag = ASN1_Type::NoObject; + ASN1_Class m_class_tag = ASN1_Class::Universal; + std::vector m_value; friend class BER_Decoder; @@ -199,7 +198,7 @@ */ class BOTAN_PUBLIC_API(2, 0) BER_Decoding_Error : public Decoding_Error { public: - explicit BER_Decoding_Error(std::string_view); + explicit BER_Decoding_Error(std::string_view err); }; /** @@ -216,7 +215,7 @@ class BOTAN_PUBLIC_API(2, 0) OID final : public ASN1_Object { public: /** - * Create an uninitialied OID object + * Create an uninitialised OID object */ explicit OID() = default; @@ -231,12 +230,12 @@ /** * Initialize an OID from a sequence of integer values */ - explicit OID(std::initializer_list init); + OID(std::initializer_list init); /** * Initialize an OID from a vector of integer values */ - BOTAN_DEPRECATED("Use another contructor") explicit OID(std::vector&& init); + explicit OID(std::vector&& init); /** * Construct an OID from a string. @@ -256,8 +255,8 @@ */ static void register_oid(const OID& oid, std::string_view name); - void encode_into(DER_Encoder&) const override; - void decode_from(BER_Decoder&) override; + void encode_into(DER_Encoder& to) const override; + void decode_from(BER_Decoder& from) override; /** * Find out whether this OID is empty @@ -304,10 +303,15 @@ /** * Return a hash code for this OID * - * This value is only meant as a std::unsorted_map hash and + * This value is only meant as a std::unordered_map hash and * can change value from release to release. */ - size_t hash_code() const; + uint64_t hash_code() const; + + /** + * Check if this OID matches the provided value + */ + bool matches(std::initializer_list other) const; /** * Get this OID as list (vector) of its components. @@ -327,10 +331,7 @@ std::vector m_id; }; -inline std::ostream& operator<<(std::ostream& out, const OID& oid) { - out << oid.to_string(); - return out; -} +BOTAN_PUBLIC_API(3, 0) std::ostream& operator<<(std::ostream& out, const OID& oid); /** * Compare two OIDs. @@ -351,79 +352,13 @@ BOTAN_PUBLIC_API(2, 0) bool operator<(const OID& a, const OID& b); /** -* Time (GeneralizedTime/UniversalTime) -*/ -class BOTAN_PUBLIC_API(2, 0) ASN1_Time final : public ASN1_Object { - public: - /// DER encode a ASN1_Time - void encode_into(DER_Encoder&) const override; - - // Decode a BER encoded ASN1_Time - void decode_from(BER_Decoder&) override; - - /// Return an internal string representation of the time - std::string to_string() const; - - /// Returns a human friendly string replesentation of no particular formatting - std::string readable_string() const; - - /// Return if the time has been set somehow - bool time_is_set() const; - - /// Compare this time against another - int32_t cmp(const ASN1_Time& other) const; - - /// Create an invalid ASN1_Time - ASN1_Time() = default; - - /// Create a ASN1_Time from a time point - explicit ASN1_Time(const std::chrono::system_clock::time_point& time); - - /// Create an ASN1_Time from string - ASN1_Time(std::string_view t_spec); - - /// Create an ASN1_Time from string and a specified tagging (Utc or Generalized) - ASN1_Time(std::string_view t_spec, ASN1_Type tag); - - /// Returns a STL timepoint object - std::chrono::system_clock::time_point to_std_timepoint() const; - - /// Return time since epoch - uint64_t time_since_epoch() const; - - private: - void set_to(std::string_view t_spec, ASN1_Type type); - bool passes_sanity_check() const; - - uint32_t m_year = 0; - uint32_t m_month = 0; - uint32_t m_day = 0; - uint32_t m_hour = 0; - uint32_t m_minute = 0; - uint32_t m_second = 0; - ASN1_Type m_tag = ASN1_Type::NoObject; -}; - -/* -* Comparison Operations -*/ -BOTAN_PUBLIC_API(2, 0) bool operator==(const ASN1_Time&, const ASN1_Time&); -BOTAN_PUBLIC_API(2, 0) bool operator!=(const ASN1_Time&, const ASN1_Time&); -BOTAN_PUBLIC_API(2, 0) bool operator<=(const ASN1_Time&, const ASN1_Time&); -BOTAN_PUBLIC_API(2, 0) bool operator>=(const ASN1_Time&, const ASN1_Time&); -BOTAN_PUBLIC_API(2, 0) bool operator<(const ASN1_Time&, const ASN1_Time&); -BOTAN_PUBLIC_API(2, 0) bool operator>(const ASN1_Time&, const ASN1_Time&); - -typedef ASN1_Time X509_Time; - -/** * ASN.1 string type * This class normalizes all inputs to a UTF-8 std::string */ class BOTAN_PUBLIC_API(2, 0) ASN1_String final : public ASN1_Object { public: - void encode_into(DER_Encoder&) const override; - void decode_from(BER_Decoder&) override; + void encode_into(DER_Encoder& to) const override; + void decode_from(BER_Decoder& from) override; ASN1_Type tagging() const { return m_tag; } @@ -456,10 +391,10 @@ */ class BOTAN_PUBLIC_API(2, 0) AlgorithmIdentifier final : public ASN1_Object { public: - enum Encoding_Option { USE_NULL_PARAM, USE_EMPTY_PARAM }; + enum Encoding_Option : uint8_t { USE_NULL_PARAM, USE_EMPTY_PARAM }; /* NOLINT(*-use-enum-class) */ - void encode_into(DER_Encoder&) const override; - void decode_from(BER_Decoder&) override; + void encode_into(DER_Encoder& to) const override; + void decode_from(BER_Decoder& from) override; AlgorithmIdentifier() = default; @@ -495,15 +430,26 @@ /* * Comparison Operations */ -BOTAN_PUBLIC_API(2, 0) bool operator==(const AlgorithmIdentifier&, const AlgorithmIdentifier&); -BOTAN_PUBLIC_API(2, 0) bool operator!=(const AlgorithmIdentifier&, const AlgorithmIdentifier&); +BOTAN_PUBLIC_API(2, 0) bool operator==(const AlgorithmIdentifier& x, const AlgorithmIdentifier& y); +BOTAN_PUBLIC_API(2, 0) bool operator!=(const AlgorithmIdentifier& x, const AlgorithmIdentifier& y); } // namespace Botan template <> class std::hash { public: - size_t operator()(const Botan::OID& oid) const noexcept { return oid.hash_code(); } + size_t operator()(const Botan::OID& oid) const noexcept { return static_cast(oid.hash_code()); } }; +/* +In 3.11 ASN1_Time was split out to its own header as is huge in C++20 +However we continue to include this header (when not building the library), +to avoid breaking applications which would expect it to still be available. + +TODO(Botan4) remove this +*/ +#if defined(BOTAN_AMALGAMATION_H_) || (!defined(BOTAN_IS_BEING_BUILT) && !defined(__clang_analyzer__)) + #include +#endif + #endif diff -Nru botan3-3.7.1+dfsg/src/lib/asn1/asn1_oid.cpp botan3-3.12.0+dfsg/src/lib/asn1/asn1_oid.cpp --- botan3-3.7.1+dfsg/src/lib/asn1/asn1_oid.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/asn1/asn1_oid.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,11 +10,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include @@ -38,7 +38,7 @@ std::string elem; std::vector oid_elems; - for(char c : oid) { + for(const char c : oid) { if(c == '.') { if(elem.empty()) { return std::vector(); @@ -152,13 +152,19 @@ return !human_name_or_empty().empty(); } -size_t OID::hash_code() const { - constexpr uint64_t mod = 0xffffffffffffffc5; - uint64_t hash = 0; +bool OID::matches(std::initializer_list other) const { + // TODO: once all target compilers support it, use std::ranges::equal + return std::equal(m_id.begin(), m_id.end(), other.begin(), other.end()); +} + +uint64_t OID::hash_code() const { + // If this is changed also update gen_oids.py to match + uint64_t hash = 0x621F302327D9A49A; for(auto id : m_id) { - hash = (hash * 257 + id) % mod; + hash *= 193; + hash += id; } - return static_cast(hash); + return hash; } /* @@ -183,7 +189,7 @@ if(z <= 0x7F) { encoding.push_back(static_cast(z)); } else { - size_t z7 = (high_bit(z) + 7 - 1) / 7; + const size_t z7 = (high_bit(z) + 7 - 1) / 7; for(size_t j = 0; j != z7; ++j) { uint8_t zp = static_cast(z >> (7 * (z7 - j - 1)) & 0x7F); @@ -200,9 +206,10 @@ std::vector encoding; // We know 40 * root can't overflow because root is between 0 and 2 - auto first = BOTAN_ASSERT_IS_SOME(checked_add(40 * m_id[0], m_id[1])); + auto first = checked_add(40 * m_id[0], m_id[1]); + BOTAN_ASSERT_NOMSG(first.has_value()); - append(encoding, first); + append(encoding, *first); for(size_t i = 2; i != m_id.size(); ++i) { append(encoding, m_id[i]); @@ -214,7 +221,7 @@ * Decode a BER encoded OBJECT IDENTIFIER */ void OID::decode_from(BER_Decoder& decoder) { - BER_Object obj = decoder.get_next_object(); + const BER_Object obj = decoder.get_next_object(); if(obj.tagging() != (ASN1_Class::Universal | ASN1_Type::ObjectId)) { throw BER_Bad_Tag("Error decoding OID, unknown tag", obj.tagging()); } @@ -243,7 +250,7 @@ } const uint8_t next = data.take_byte(); - const bool more = (next & 0x80); + const bool more = (next & 0x80) == 0x80; const uint8_t value = next & 0x7F; if((b >> (32 - 7)) != 0) { @@ -290,4 +297,9 @@ m_id = parts; } +std::ostream& operator<<(std::ostream& out, const OID& oid) { + out << oid.to_string(); + return out; +} + } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/asn1/asn1_print.cpp botan3-3.12.0+dfsg/src/lib/asn1/asn1_print.cpp --- botan3-3.7.1+dfsg/src/lib/asn1/asn1_print.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/asn1/asn1_print.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,12 +6,13 @@ #include +#include #include #include #include #include #include -#include +#include #include #include @@ -19,14 +20,30 @@ namespace { +// Printable here means fits into an ASN.1 "PRINTABLE STRING" type +bool is_printable_char(char c) { + if(c >= 'a' && c <= 'z') { + return true; + } + + if(c >= 'A' && c <= 'Z') { + return true; + } + + if(c >= '0' && c <= '9') { + return true; + } + + if(c == '.' || c == ':' || c == '/' || c == '-') { + return true; + } + + return false; +} + bool all_printable_chars(const uint8_t bits[], size_t bits_len) { for(size_t i = 0; i != bits_len; ++i) { - int c = bits[i]; - if(c > 127) { - return false; - } - - if((std::isalnum(c) || c == '.' || c == ':' || c == '/' || c == '-') == false) { + if(!is_printable_char(bits[i])) { return false; } } @@ -49,7 +66,7 @@ return false; } - if(all_printable_chars(bits + 2, bits_len - 2) == false) { + if(!all_printable_chars(bits + 2, bits_len - 2)) { return false; } @@ -65,7 +82,8 @@ } void ASN1_Formatter::print_to_stream(std::ostream& output, const uint8_t in[], size_t len) const { - BER_Decoder dec(in, len); + const auto decoder_limits = m_require_der ? BER_Decoder::Limits::DER() : BER_Decoder::Limits::BER(); + BER_Decoder dec(std::span{in, len}, decoder_limits); decode(output, dec, 0); } @@ -84,10 +102,10 @@ std::vector bits; DER_Encoder(bits).add_object(type_tag, class_tag, obj.bits(), obj.length()); - BER_Decoder data(bits); + BER_Decoder data(bits, decoder.limits()); if(intersects(class_tag, ASN1_Class::Constructed)) { - BER_Decoder cons_info(obj.bits(), obj.length()); + BER_Decoder cons_info(obj, decoder.limits()); if(recurse_deeper) { output << format(type_tag, class_tag, level, length, ""); @@ -101,14 +119,13 @@ if(m_print_context_specific) { try { if(possibly_a_general_name(bits.data(), bits.size())) { - output << format( - type_tag, class_tag, level, level, std::string(cast_uint8_ptr_to_char(&bits[2]), bits.size() - 2)); + output << format(type_tag, class_tag, level, level, bytes_to_string(std::span{bits}.subspan(2))); success_parsing_cs = true; } else if(recurse_deeper) { std::vector inner_bits; data.decode(inner_bits, type_tag); - BER_Decoder inner(inner_bits); + BER_Decoder inner(inner_bits, decoder.limits()); std::ostringstream inner_data; decode(inner_data, inner, level + 1); // recurse output << inner_data.str(); @@ -117,7 +134,7 @@ } catch(...) {} } - if(success_parsing_cs == false) { + if(!success_parsing_cs) { output << format(type_tag, class_tag, level, length, format_bin(type_tag, class_tag, bits)); } } else if(type_tag == ASN1_Type::ObjectId) { @@ -143,7 +160,7 @@ output << format(type_tag, class_tag, level, length, format_bn(number)); } else if(type_tag == ASN1_Type::Boolean) { - bool boolean; + bool boolean = false; data.decode(boolean); output << format(type_tag, class_tag, level, length, (boolean ? "true" : "false")); } else if(type_tag == ASN1_Type::Null) { @@ -155,7 +172,7 @@ if(recurse_deeper) { try { - BER_Decoder inner(decoded_bits); + BER_Decoder inner(decoded_bits, decoder.limits()); std::ostringstream inner_data; decode(inner_data, inner, level + 1); // recurse @@ -253,7 +270,7 @@ ASN1_Class /*class_tag*/, const std::vector& vec) const { if(all_printable_chars(vec.data(), vec.size())) { - return std::string(cast_uint8_ptr_to_char(vec.data()), vec.size()); + return bytes_to_string(vec); } else { return hex_encode(vec); } diff -Nru botan3-3.7.1+dfsg/src/lib/asn1/asn1_print.h botan3-3.12.0+dfsg/src/lib/asn1/asn1_print.h --- botan3-3.7.1+dfsg/src/lib/asn1/asn1_print.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/asn1/asn1_print.h 2026-05-07 01:38:28.000000000 +0000 @@ -20,7 +20,7 @@ /** * Format ASN.1 data and call a virtual to format */ -class BOTAN_PUBLIC_API(2, 4) ASN1_Formatter { +class BOTAN_PUBLIC_API(2, 4) ASN1_Formatter /* NOLINT(*-special-member-functions) */ { public: virtual ~ASN1_Formatter() = default; @@ -28,9 +28,10 @@ * @param print_context_specific if true, try to parse nested context specific data. * @param max_depth do not recurse more than this many times. If zero, recursion * is unbounded. + * @param require_der if true then non-canonical BER data is rejected */ - ASN1_Formatter(bool print_context_specific, size_t max_depth) : - m_print_context_specific(print_context_specific), m_max_depth(max_depth) {} + ASN1_Formatter(bool print_context_specific, size_t max_depth, bool require_der = false) : + m_print_context_specific(print_context_specific), m_max_depth(max_depth), m_require_der(require_der) {} void print_to_stream(std::ostream& out, const uint8_t in[], size_t len) const; @@ -52,6 +53,8 @@ * This is called to format binary elements that we don't know how to * convert to a string. The result will be passed as value to format; the * tags are included as a hint to aid decoding. + * + * TODO(Botan4) change the vector to a span */ virtual std::string format_bin(ASN1_Type type_tag, ASN1_Class class_tag, @@ -67,6 +70,7 @@ const bool m_print_context_specific; const size_t m_max_depth; + const bool m_require_der; }; /** @@ -83,14 +87,16 @@ * @param value_column ASN.1 values are lined up at this column in output * @param max_depth do not recurse more than this many times. If zero, recursion * is unbounded. + * @param require_der if true then non-canonical BER data is rejected */ - ASN1_Pretty_Printer(size_t print_limit = 4096, - size_t print_binary_limit = 2048, - bool print_context_specific = true, - size_t initial_level = 0, - size_t value_column = 60, - size_t max_depth = 64) : - ASN1_Formatter(print_context_specific, max_depth), + explicit ASN1_Pretty_Printer(size_t print_limit = 4096, + size_t print_binary_limit = 2048, + bool print_context_specific = true, + size_t initial_level = 0, + size_t value_column = 60, + size_t max_depth = 64, + bool require_der = false) : + ASN1_Formatter(print_context_specific, max_depth, require_der), m_print_limit(print_limit), m_print_binary_limit(print_binary_limit), m_initial_level(initial_level), diff -Nru botan3-3.7.1+dfsg/src/lib/asn1/asn1_str.cpp botan3-3.12.0+dfsg/src/lib/asn1/asn1_str.cpp --- botan3-3.7.1+dfsg/src/lib/asn1/asn1_str.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/asn1/asn1_str.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,42 +7,89 @@ #include +#include #include #include #include -#include #include +#include namespace Botan { namespace { -/* -* Choose an encoding for the string -*/ -ASN1_Type choose_encoding(std::string_view str) { - auto all_printable = CT::Mask::set(); - - for(size_t i = 0; i != str.size(); ++i) { - const uint8_t c = static_cast(str[i]); +class ASN1_String_Codepoint_Validator final { + public: + constexpr ASN1_String_Codepoint_Validator() : m_table(make_table()) {} + + constexpr bool valid_encoding(std::string_view str, ASN1_Type tag) const { + const uint8_t mask = mask_for(tag); + for(const char c : str) { + const uint8_t codepoint = static_cast(c); + const bool is_valid = (m_table[codepoint] & mask) != 0; + + if(!is_valid) { + return false; + } + } + + return true; + } + + private: + static constexpr uint8_t Numeric_String = 0x01; + static constexpr uint8_t Printable_String = 0x02; + static constexpr uint8_t IA5_String = 0x04; + static constexpr uint8_t Visible_String = 0x08; + + static constexpr uint8_t mask_for(ASN1_Type tag) { + switch(tag) { + case ASN1_Type::NumericString: + return Numeric_String; + case ASN1_Type::PrintableString: + return Printable_String; + case ASN1_Type::Ia5String: + return IA5_String; + case ASN1_Type::VisibleString: + return Visible_String; + default: + return 0; + } + } + + static constexpr std::array make_table() { + std::array table = {}; + + for(size_t i = 0; i != table.size(); ++i) { + const auto c = static_cast(i); + + // Don't allow embedded null in IA5 even if technically valid + if(c >= 1 && c <= 0x7F) { + table[i] |= IA5_String; + } + + if(c >= 0x20 && c <= 0x7E) { + table[i] |= Visible_String; + } + + if(c == ' ' || (c >= '0' && c <= '9')) { + table[i] |= Numeric_String; + } + + if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == ' ' || c == '\'' || + c == '(' || c == ')' || c == '+' || c == ',' || c == '-' || c == '.' || c == '/' || c == ':' || + c == '=' || c == '?') { + table[i] |= Printable_String; + } + } - auto is_alpha_lower = CT::Mask::is_within_range(c, 'a', 'z'); - auto is_alpha_upper = CT::Mask::is_within_range(c, 'A', 'Z'); - auto is_decimal = CT::Mask::is_within_range(c, '0', '9'); + return table; + } - auto is_print_punc = CT::Mask::is_any_of(c, {' ', '(', ')', '+', ',', '-', '.', '/', ':', '=', '?'}); + std::array m_table; +}; - auto is_printable = is_alpha_lower | is_alpha_upper | is_decimal | is_print_punc; - - all_printable &= is_printable; - } - - if(all_printable.as_bool()) { - return ASN1_Type::PrintableString; - } else { - return ASN1_Type::Utf8String; - } -} +constexpr ASN1_String_Codepoint_Validator g_char_validator; bool is_utf8_subset_string_type(ASN1_Type tag) { return (tag == ASN1_Type::NumericString || tag == ASN1_Type::PrintableString || tag == ASN1_Type::VisibleString || @@ -54,6 +101,30 @@ tag == ASN1_Type::UniversalString); } +bool is_valid_asn1_string_content(const std::string& str, ASN1_Type tag) { + BOTAN_ASSERT_NOMSG(is_utf8_subset_string_type(tag)); + + switch(tag) { + case ASN1_Type::Utf8String: + return is_valid_utf8(str); + case ASN1_Type::NumericString: + case ASN1_Type::PrintableString: + case ASN1_Type::Ia5String: + case ASN1_Type::VisibleString: + return g_char_validator.valid_encoding(str, tag); + default: + return false; + } +} + +ASN1_Type choose_encoding(std::string_view str) { + if(g_char_validator.valid_encoding(str, ASN1_Type::PrintableString)) { + return ASN1_Type::PrintableString; + } else { + return ASN1_Type::Utf8String; + } +} + } // namespace //static @@ -65,6 +136,10 @@ if(!is_utf8_subset_string_type(m_tag)) { throw Invalid_Argument("ASN1_String only supports encoding to UTF-8 or a UTF-8 subset"); } + + if(!is_valid_asn1_string_content(m_utf8_str, m_tag)) { + throw Invalid_Argument(fmt("ASN1_String: Invalid {} encoding", asn1_tag_to_string(m_tag))); + } } ASN1_String::ASN1_String(std::string_view str) : ASN1_String(str, choose_encoding(str)) {} @@ -86,11 +161,12 @@ * Decode a BER encoded ASN1_String */ void ASN1_String::decode_from(BER_Decoder& source) { - BER_Object obj = source.get_next_object(); + const BER_Object obj = source.get_next_object(); - if(!is_asn1_string_type(obj.type())) { + if(obj.get_class() != ASN1_Class::Universal || !is_asn1_string_type(obj.type())) { auto typ = static_cast(obj.type()); - throw Decoding_Error(fmt("ASN1_String: Unknown string type {}", typ)); + auto cls = static_cast(obj.get_class()); + throw Decoding_Error(fmt("ASN1_String: Unknown string type {}/{}", typ, cls)); } m_tag = obj.type(); @@ -109,6 +185,10 @@ } else { // All other supported string types are UTF-8 or some subset thereof m_utf8_str = ASN1::to_string(obj); + + if(!is_valid_asn1_string_content(m_utf8_str, m_tag)) { + throw Decoding_Error(fmt("ASN1_String: Invalid {} encoding", asn1_tag_to_string(m_tag))); + } } } diff -Nru botan3-3.7.1+dfsg/src/lib/asn1/asn1_time.cpp botan3-3.12.0+dfsg/src/lib/asn1/asn1_time.cpp --- botan3-3.7.1+dfsg/src/lib/asn1/asn1_time.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/asn1/asn1_time.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -5,8 +5,9 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include +#include +#include #include #include #include @@ -17,8 +18,12 @@ namespace Botan { +ASN1_Time ASN1_Time::from_seconds_since_epoch(uint64_t time_since_epoch) { + return ASN1_Time(std::chrono::system_clock::time_point(std::chrono::seconds(time_since_epoch))); +} + ASN1_Time::ASN1_Time(const std::chrono::system_clock::time_point& time) { - calendar_point cal(time); + const calendar_point cal(time); m_year = cal.year(); m_month = cal.month(); @@ -27,6 +32,7 @@ m_minute = cal.minutes(); m_second = cal.seconds(); + // NOLINTNEXTLINE(*-prefer-member-initializer) m_tag = (m_year >= 2050) ? ASN1_Type::GeneralizedTime : ASN1_Type::UtcTime; } @@ -51,13 +57,24 @@ } void ASN1_Time::decode_from(BER_Decoder& source) { - BER_Object ber_time = source.get_next_object(); + const BER_Object ber_time = source.get_next_object(); - set_to(ASN1::to_string(ber_time), ber_time.type()); + if(ber_time.get_class() != ASN1_Class::Universal || + (ber_time.type() != ASN1_Type::UtcTime && ber_time.type() != ASN1_Type::GeneralizedTime)) { + throw Decoding_Error(fmt("ASN1_Time: Unexpected tag {}/{}", + static_cast(ber_time.type()), + static_cast(ber_time.get_class()))); + } + + try { + set_to(ASN1::to_string(ber_time), ber_time.type()); + } catch(Invalid_Argument& e) { + throw Decoding_Error(fmt("Invalid ASN1_Time encoding: {}", e.what())); + } } std::string ASN1_Time::to_string() const { - if(time_is_set() == false) { + if(!time_is_set()) { throw Invalid_State("ASN1_Time::to_string: No time set"); } @@ -80,7 +97,7 @@ const uint64_t int_repr = year_factor * full_year + mon_factor * m_month + day_factor * m_day + hour_factor * m_hour + min_factor * m_minute + m_second; - std::string repr = std::to_string(int_repr) + "Z"; + const std::string repr = std::to_string(int_repr) + "Z"; const size_t desired_size = (m_tag == ASN1_Type::UtcTime) ? 13 : 15; @@ -90,7 +107,7 @@ } std::string ASN1_Time::readable_string() const { - if(time_is_set() == false) { + if(!time_is_set()) { throw Invalid_State("ASN1_Time::readable_string: No time set"); } @@ -112,7 +129,9 @@ throw Invalid_State("ASN1_Time::cmp: Cannot compare empty times"); } - const int32_t EARLIER = -1, LATER = 1, SAME_TIME = 0; + constexpr int32_t EARLIER = -1; + constexpr int32_t LATER = 1; + constexpr int32_t SAME_TIME = 0; if(m_year < other.m_year) { return EARLIER; diff -Nru botan3-3.7.1+dfsg/src/lib/asn1/asn1_time.h botan3-3.12.0+dfsg/src/lib/asn1/asn1_time.h --- botan3-3.7.1+dfsg/src/lib/asn1/asn1_time.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/asn1/asn1_time.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,84 @@ +/* +* (C) 1999-2007,2018,2020,2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_ASN1_TIME_TYPE_H_ +#define BOTAN_ASN1_TIME_TYPE_H_ + +#include +#include + +namespace Botan { + +/** +* Time (GeneralizedTime/UniversalTime) +*/ +class BOTAN_PUBLIC_API(2, 0) ASN1_Time final : public ASN1_Object { + public: + /// DER encode a ASN1_Time + void encode_into(DER_Encoder& to) const override; + + // Decode a BER encoded ASN1_Time + void decode_from(BER_Decoder& from) override; + + /// Return an internal string representation of the time + std::string to_string() const; + + /// Returns a human friendly string representation of no particular formatting + std::string readable_string() const; + + /// Return if the time has been set somehow + bool time_is_set() const; + + /// Compare this time against another + int32_t cmp(const ASN1_Time& other) const; + + /// Create an invalid ASN1_Time + ASN1_Time() = default; + + /// Create a ASN1_Time from a time point + explicit ASN1_Time(const std::chrono::system_clock::time_point& time); + + /// Create an ASN1_Time from seconds since epoch + static ASN1_Time from_seconds_since_epoch(uint64_t seconds); + + /// Create an ASN1_Time from string + BOTAN_FUTURE_EXPLICIT ASN1_Time(std::string_view t_spec); + + /// Create an ASN1_Time from string and a specified tagging (Utc or Generalized) + ASN1_Time(std::string_view t_spec, ASN1_Type tag); + + /// Returns a STL timepoint object + std::chrono::system_clock::time_point to_std_timepoint() const; + + /// Return time since epoch + uint64_t time_since_epoch() const; + + private: + void set_to(std::string_view t_spec, ASN1_Type type); + bool passes_sanity_check() const; + + uint32_t m_year = 0; + uint32_t m_month = 0; + uint32_t m_day = 0; + uint32_t m_hour = 0; + uint32_t m_minute = 0; + uint32_t m_second = 0; + ASN1_Type m_tag = ASN1_Type::NoObject; +}; + +/* +* Comparison Operations +*/ +BOTAN_PUBLIC_API(2, 0) bool operator==(const ASN1_Time& x, const ASN1_Time& y); +BOTAN_PUBLIC_API(2, 0) bool operator!=(const ASN1_Time& x, const ASN1_Time& y); +BOTAN_PUBLIC_API(2, 0) bool operator<=(const ASN1_Time& x, const ASN1_Time& y); +BOTAN_PUBLIC_API(2, 0) bool operator>=(const ASN1_Time& x, const ASN1_Time& y); +BOTAN_PUBLIC_API(2, 0) bool operator<(const ASN1_Time& x, const ASN1_Time& y); +BOTAN_PUBLIC_API(2, 0) bool operator>(const ASN1_Time& x, const ASN1_Time& y); + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/asn1/ber_dec.cpp botan3-3.12.0+dfsg/src/lib/asn1/ber_dec.cpp --- botan3-3.7.1+dfsg/src/lib/asn1/ber_dec.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/asn1/ber_dec.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ /* * BER Decoder -* (C) 1999-2008,2015,2017,2018 Jack Lloyd +* (C) 1999-2008,2015,2017,2018,2026 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -16,142 +17,309 @@ namespace { -/* -* This value is somewhat arbitrary. OpenSSL allows up to 128 nested -* indefinite length sequences. If you increase this, also increase the -* limit in the test in test_asn1.cpp -*/ -const size_t ALLOWED_EOC_NESTINGS = 16; +bool is_constructed(ASN1_Class class_tag) { + return (static_cast(class_tag) & static_cast(ASN1_Class::Constructed)) != 0; +} /* * BER decode an ASN.1 type tag */ size_t decode_tag(DataSource* ber, ASN1_Type& type_tag, ASN1_Class& class_tag) { - uint8_t b; - if(!ber->read_byte(b)) { + auto b = ber->read_byte(); + + if(!b) { type_tag = ASN1_Type::NoObject; class_tag = ASN1_Class::NoObject; return 0; } - if((b & 0x1F) != 0x1F) { - type_tag = ASN1_Type(b & 0x1F); - class_tag = ASN1_Class(b & 0xE0); + if((*b & 0x1F) != 0x1F) { + type_tag = ASN1_Type(*b & 0x1F); + class_tag = ASN1_Class(*b & 0xE0); return 1; } size_t tag_bytes = 1; - class_tag = ASN1_Class(b & 0xE0); + class_tag = ASN1_Class(*b & 0xE0); - size_t tag_buf = 0; + uint32_t tag_buf = 0; while(true) { - if(!ber->read_byte(b)) { + b = ber->read_byte(); + if(!b) { throw BER_Decoding_Error("Long-form tag truncated"); } - if(tag_buf & 0xFF000000) { + if((tag_buf >> 24) != 0) { throw BER_Decoding_Error("Long-form tag overflowed 32 bits"); } - // This is required even by BER (see X.690 section 8.1.2.4.2 sentence c) - if(tag_bytes == 0 && b == 0x80) { + // This is required even by BER (see X.690 section 8.1.2.4.2 sentence c). + // Bits 7-1 of the first subsequent octet must not be all zero; this rules + // out both 0x80 (continuation with no data) and 0x00 (a long-form encoding + // of tag value 0, which collides with the EOC marker). + if(tag_bytes == 1 && (*b & 0x7F) == 0) { throw BER_Decoding_Error("Long form tag with leading zero"); } ++tag_bytes; - tag_buf = (tag_buf << 7) | (b & 0x7F); - if((b & 0x80) == 0) { + tag_buf = (tag_buf << 7) | (*b & 0x7F); + if((*b & 0x80) == 0) { break; } } + // Per X.690 8.1.2.2, tag values 0-30 shall be encoded in the short form. + // Long-form encoding is reserved for tag values >= 31 (X.690 8.1.2.3). + // This is unconditional and applies to BER as well as DER. + if(tag_buf <= 30) { + throw BER_Decoding_Error("Long-form tag encoding used for small tag value"); + } + + if(tag_buf == static_cast(ASN1_Type::NoObject)) { + throw BER_Decoding_Error("Tag value collides with internal sentinel"); + } + + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) type_tag = ASN1_Type(tag_buf); return tag_bytes; } /* -* Find the EOC marker +* Find the EOC marker by scanning TLVs via peek, without buffering. +* Returns the number of bytes before and including the EOC marker. */ -size_t find_eoc(DataSource* src, size_t allow_indef); +size_t find_eoc(DataSource* src, size_t base_offset, size_t allow_indef); + +/* +* Result of decoding a BER length field. +* +* If indefinite is true, indefinite-length encoding was used: content_length +* is the number of content bytes (excluding the 2-byte EOC marker) and the +* caller must consume the EOC bytes after reading the content. +*/ +class BerDecodedLength final { + public: + BerDecodedLength(size_t content_length, size_t field_length) : + BerDecodedLength(content_length, field_length, false) {} + + static BerDecodedLength indefinite(size_t content_length, size_t field_length) { + return BerDecodedLength(content_length, field_length, true); + } + + size_t content_length() const { return m_content_length; } + + // Length plus the EOC bytes if an indefinite length field + size_t total_length() const { return m_indefinite ? m_content_length + 2 : m_content_length; } + + size_t field_length() const { return m_field_length; } + + bool indefinite_length() const { return m_indefinite; } + + private: + BerDecodedLength(size_t content_length, size_t field_length, bool indefinite) : + m_content_length(content_length), m_field_length(field_length), m_indefinite(indefinite) {} + + size_t m_content_length; + size_t m_field_length; + bool m_indefinite; +}; /* * BER decode an ASN.1 length field */ -size_t decode_length(DataSource* ber, size_t& field_size, size_t allow_indef) { - uint8_t b; - if(!ber->read_byte(b)) { +BerDecodedLength decode_length(DataSource* ber, size_t allow_indef, bool der_mode, bool constructed) { + uint8_t b = 0; + if(ber->read_byte(b) == 0) { throw BER_Decoding_Error("Length field not found"); } - field_size = 1; if((b & 0x80) == 0) { - return b; + return BerDecodedLength(b, 1); } - field_size += (b & 0x7F); - if(field_size > 5) { + const size_t num_length_bytes = (b & 0x7F); + if(num_length_bytes > 4) { throw BER_Decoding_Error("Length field is too large"); } - if(field_size == 1) { - if(allow_indef == 0) { + const size_t field_size = 1 + num_length_bytes; + + if(num_length_bytes == 0) { + if(der_mode) { + throw BER_Decoding_Error("Detected indefinite-length encoding in DER structure"); + } else if(!constructed) { + // Indefinite length is only valid for constructed types (X.690 8.1.3.2) + throw BER_Decoding_Error("Indefinite-length encoding used with non-constructed type"); + } else if(allow_indef == 0) { throw BER_Decoding_Error("Nested EOC markers too deep, rejecting to avoid stack exhaustion"); } else { - return find_eoc(ber, allow_indef - 1); + // find_eoc returns bytes up to and including the EOC marker. + // Return the content length; the caller consumes the EOC separately. + const size_t eoc_len = find_eoc(ber, /*base_offset=*/0, allow_indef - 1); + if(eoc_len < 2) { + throw BER_Decoding_Error("Invalid EOC encoding"); + } + return BerDecodedLength::indefinite(eoc_len - 2, field_size); } } size_t length = 0; - for(size_t i = 0; i != field_size - 1; ++i) { - if(get_byte<0>(length) != 0) { - throw BER_Decoding_Error("Field length overflow"); - } - if(!ber->read_byte(b)) { + for(size_t i = 0; i != num_length_bytes; ++i) { + if(ber->read_byte(b) == 0) { throw BER_Decoding_Error("Corrupted length field"); } + // Can't overflow since we already checked that num_length_bytes <= 4 length = (length << 8) | b; } - return length; + + // DER requires shortest possible length encoding + if(der_mode) { + if(length < 128) { + throw BER_Decoding_Error("Detected non-canonical length encoding in DER structure"); + } + if(num_length_bytes > 1 && length < (size_t(1) << ((num_length_bytes - 1) * 8))) { + throw BER_Decoding_Error("Detected non-canonical length encoding in DER structure"); + } + } + + return BerDecodedLength(length, field_size); } /* -* Find the EOC marker +* Peek a tag from the source at the given offset without consuming any data. +* Returns the number of bytes consumed by the tag, or 0 on EOF. */ -size_t find_eoc(DataSource* ber, size_t allow_indef) { - secure_vector buffer(BOTAN_DEFAULT_BUFFER_SIZE), data; +size_t peek_tag(DataSource* src, size_t offset, ASN1_Type& type_tag, ASN1_Class& class_tag) { + uint8_t b = 0; + if(src->peek(&b, 1, offset) == 0) { + type_tag = ASN1_Type::NoObject; + class_tag = ASN1_Class::NoObject; + return 0; + } + + if((b & 0x1F) != 0x1F) { + type_tag = ASN1_Type(b & 0x1F); + class_tag = ASN1_Class(b & 0xE0); + return 1; + } + + class_tag = ASN1_Class(b & 0xE0); + size_t tag_bytes = 1; + uint32_t tag_buf = 0; while(true) { - const size_t got = ber->peek(buffer.data(), buffer.size(), data.size()); - if(got == 0) { + if(src->peek(&b, 1, offset + tag_bytes) == 0) { + throw BER_Decoding_Error("Long-form tag truncated"); + } + if((tag_buf >> 24) != 0) { + throw BER_Decoding_Error("Long-form tag overflowed 32 bits"); + } + // Required even by BER (X.690 section 8.1.2.4.2 sentence c). + // Bits 7-1 of the first subsequent octet must not be all zero; this rules + // out both 0x80 (continuation with no data) and 0x00 (a long-form encoding + // of tag value 0, which collides with the EOC marker). + if(tag_bytes == 1 && (b & 0x7F) == 0) { + throw BER_Decoding_Error("Long form tag with leading zero"); + } + ++tag_bytes; + tag_buf = (tag_buf << 7) | (b & 0x7F); + if((b & 0x80) == 0) { break; } + } + + // Per X.690 8.1.2.2, tag values 0-30 shall be encoded in the short form. + // Long-form encoding is reserved for tag values >= 31 (X.690 8.1.2.3). + // This is unconditional and applies to BER as well as DER. + if(tag_buf <= 30) { + throw BER_Decoding_Error("Long-form tag encoding used for small tag value"); + } + + if(tag_buf == static_cast(ASN1_Type::NoObject)) { + throw BER_Decoding_Error("Tag value collides with internal sentinel"); + } + + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) + type_tag = ASN1_Type(tag_buf); + return tag_bytes; +} + +/* +* Peek a length from the source at the given offset without consuming any data. +* Returns the decoded length and sets field_size to the number of bytes consumed. +* For indefinite-length encoding, recursively scans ahead to find the EOC marker. +*/ +size_t peek_length(DataSource* src, size_t offset, size_t& field_size, size_t allow_indef, bool constructed) { + uint8_t b = 0; + if(src->peek(&b, 1, offset) == 0) { + throw BER_Decoding_Error("Length field not found"); + } - data += std::make_pair(buffer.data(), got); + field_size = 1; + if((b & 0x80) == 0) { + return b; } - DataSource_Memory source(data); - data.clear(); + const size_t num_length_bytes = (b & 0x7F); + field_size += num_length_bytes; + if(field_size > 5) { + throw BER_Decoding_Error("Length field is too large"); + } + + if(num_length_bytes == 0) { + // Indefinite length is only valid for constructed types (X.690 8.1.3.2) + if(!constructed) { + throw BER_Decoding_Error("Indefinite-length encoding used with non-constructed type"); + } + if(allow_indef == 0) { + throw BER_Decoding_Error("Nested EOC markers too deep, rejecting to avoid stack exhaustion"); + } + return find_eoc(src, offset + 1, allow_indef - 1); + } size_t length = 0; + for(size_t i = 0; i < num_length_bytes; ++i) { + if(src->peek(&b, 1, offset + 1 + i) == 0) { + throw BER_Decoding_Error("Corrupted length field"); + } + if(get_byte<0>(length) != 0) { + throw BER_Decoding_Error("Field length overflow"); + } + length = (length << 8) | b; + } + return length; +} + +/* +* Find the EOC marker by scanning TLVs via peek, without buffering. +* Returns the number of bytes before and including the EOC marker. +*/ +size_t find_eoc(DataSource* src, size_t base_offset, size_t allow_indef) { + size_t offset = base_offset; + while(true) { - ASN1_Type type_tag; - ASN1_Class class_tag; - const size_t tag_size = decode_tag(&source, type_tag, class_tag); + ASN1_Type type_tag = ASN1_Type::NoObject; + ASN1_Class class_tag = ASN1_Class::NoObject; + const size_t tag_size = peek_tag(src, offset, type_tag, class_tag); if(type_tag == ASN1_Type::NoObject) { - break; + throw BER_Decoding_Error("Missing EOC marker in indefinite-length encoding"); } size_t length_size = 0; - const size_t item_size = decode_length(&source, length_size, allow_indef); - source.discard_next(item_size); + const size_t item_size = peek_length(src, offset + tag_size, length_size, allow_indef, is_constructed(class_tag)); - if(auto new_len = checked_add(length, item_size, tag_size, length_size)) { - length = new_len.value(); + if(auto new_offset = checked_add(offset, tag_size, length_size, item_size)) { + offset = new_offset.value(); } else { - throw Decoding_Error("Integer overflow while decoding DER"); + throw Decoding_Error("Integer overflow while scanning for EOC"); } if(type_tag == ASN1_Type::Eoc && class_tag == ASN1_Class::Universal) { + // Per X.690 8.1.5 the EOC marker is exactly two zero octets + if(length_size != 1 || item_size != 0) { + throw BER_Decoding_Error("EOC marker with non-zero length"); + } break; } } - return length; + + return offset - base_offset; } class DataSource_BERObject final : public DataSource { @@ -186,15 +354,17 @@ size_t get_bytes_read() const override { return m_offset; } - explicit DataSource_BERObject(BER_Object&& obj) : m_obj(std::move(obj)), m_offset(0) {} + explicit DataSource_BERObject(BER_Object&& obj) : m_obj(std::move(obj)) {} private: BER_Object m_obj; - size_t m_offset; + size_t m_offset = 0; }; } // namespace +BER_Decoder::~BER_Decoder() = default; + /* * Check if more objects are there */ @@ -226,11 +396,22 @@ * Discard all the bytes remaining in the source */ BER_Decoder& BER_Decoder::discard_remaining() { - uint8_t buf; - while(m_source->read_byte(buf)) {} + m_pushed = BER_Object(); + uint8_t buf = 0; + while(m_source->read_byte(buf) != 0) {} return (*this); } +std::optional BER_Decoder::read_next_byte() { + BOTAN_ASSERT_NOMSG(m_source != nullptr); + uint8_t b = 0; + if(m_source->read_byte(b) != 0) { + return b; + } else { + return {}; + } +} + const BER_Object& BER_Decoder::peek_next_object() { if(!m_pushed.is_set()) { m_pushed = get_next_object(); @@ -251,26 +432,47 @@ } for(;;) { - ASN1_Type type_tag; - ASN1_Class class_tag; + ASN1_Type type_tag = ASN1_Type::NoObject; + ASN1_Class class_tag = ASN1_Class::NoObject; decode_tag(m_source, type_tag, class_tag); next.set_tagging(type_tag, class_tag); if(next.is_set() == false) { // no more objects return next; } - size_t field_size; - const size_t length = decode_length(m_source, field_size, ALLOWED_EOC_NESTINGS); - if(!m_source->check_available(length)) { + const size_t allow_indef = m_limits.allow_ber_encoding() ? m_limits.max_nested_indefinite_length() : 0; + const bool der_mode = m_limits.require_der_encoding(); + const auto dl = decode_length(m_source, allow_indef, der_mode, is_constructed(class_tag)); + + // Per X.690 8.1.5 the only valid EOC encoding is the two-octet + // sequence 0x00 0x00. Reject any other length encoding on a tag of + // (Eoc, Universal) before we consume the "content" bytes. + if(type_tag == ASN1_Type::Eoc && class_tag == ASN1_Class::Universal && + (dl.content_length() != 0 || dl.indefinite_length())) { + throw BER_Decoding_Error("EOC marker with non-zero length"); + } + + if(!m_source->check_available(dl.total_length())) { throw BER_Decoding_Error("Value truncated"); } - uint8_t* out = next.mutable_bits(length); - if(m_source->read(out, length) != length) { + uint8_t* out = next.mutable_bits(dl.content_length()); + if(m_source->read(out, dl.content_length()) != dl.content_length()) { throw BER_Decoding_Error("Value truncated"); } + if(dl.indefinite_length()) { + // After reading the data consume the 2-byte EOC + uint8_t eoc[2] = {0xFF, 0xFF}; + if(m_source->read(eoc, 2) != 2 || eoc[0] != 0x00 || eoc[1] != 0x00) { + throw BER_Decoding_Error("Missing or malformed EOC marker"); + } + } + if(next.tagging() == static_cast(ASN1_Type::Eoc)) { + if(m_limits.require_der_encoding()) { + throw BER_Decoding_Error("Detected EOC marker in DER structure"); + } continue; } else { break; @@ -280,6 +482,18 @@ return next; } +BER_Object BER_Decoder::get_next_value(size_t sizeofT, ASN1_Type type_tag, ASN1_Class class_tag) { + const BER_Object obj = get_next_object(); + obj.assert_is_a(type_tag, class_tag); + + if(obj.length() != sizeofT) { + throw BER_Decoding_Error("Size mismatch. Object value size is " + std::to_string(obj.length()) + + "; Output type size is " + std::to_string(sizeofT)); + } + + return obj; +} + /* * Push a object back into the stream */ @@ -300,69 +514,45 @@ BER_Decoder BER_Decoder::start_cons(ASN1_Type type_tag, ASN1_Class class_tag) { BER_Object obj = get_next_object(); obj.assert_is_a(type_tag, class_tag | ASN1_Class::Constructed); - return BER_Decoder(std::move(obj), this); + BER_Decoder child(std::move(obj), this); + return child; } /* * Finish decoding a CONSTRUCTED type */ BER_Decoder& BER_Decoder::end_cons() { - if(!m_parent) { + if(m_parent == nullptr) { throw Invalid_State("BER_Decoder::end_cons called with null parent"); } - if(!m_source->end_of_data()) { + if(!m_source->end_of_data() || m_pushed.is_set()) { throw Decoding_Error("BER_Decoder::end_cons called with data left"); } return (*m_parent); } -BER_Decoder::BER_Decoder(BER_Object&& obj, BER_Decoder* parent) { +BER_Decoder::BER_Decoder(BER_Object&& obj, BER_Decoder* parent) : + m_limits(parent != nullptr ? parent->limits() : BER_Decoder::Limits::BER()), m_parent(parent) { m_data_src = std::make_unique(std::move(obj)); m_source = m_data_src.get(); - m_parent = parent; } /* * BER_Decoder Constructor */ -BER_Decoder::BER_Decoder(DataSource& src) { - m_source = &src; -} +BER_Decoder::BER_Decoder(DataSource& src, Limits limits) : m_limits(limits), m_source(&src) {} /* * BER_Decoder Constructor */ -BER_Decoder::BER_Decoder(const uint8_t data[], size_t length) { - m_data_src = std::make_unique(data, length); +BER_Decoder::BER_Decoder(std::span buf, Limits limits) : m_limits(limits) { + m_data_src = std::make_unique(buf); m_source = m_data_src.get(); } -/* -* BER_Decoder Constructor -*/ -BER_Decoder::BER_Decoder(const secure_vector& data) { - m_data_src = std::make_unique(data); - m_source = m_data_src.get(); -} +BER_Decoder::BER_Decoder(BER_Decoder&& other) noexcept = default; -/* -* BER_Decoder Constructor -*/ -BER_Decoder::BER_Decoder(const std::vector& data) { - m_data_src = std::make_unique(data.data(), data.size()); - m_source = m_data_src.get(); -} - -/* -* BER_Decoder Copy Constructor -*/ -BER_Decoder::BER_Decoder(const BER_Decoder& other) { - m_source = other.m_source; - - // take ownership - std::swap(m_data_src, other.m_data_src); - m_parent = other.m_parent; -} +BER_Decoder& BER_Decoder::operator=(BER_Decoder&&) noexcept = default; /* * Request for an object to decode itself @@ -376,7 +566,7 @@ * Decode a BER encoded NULL */ BER_Decoder& BER_Decoder::decode_null() { - BER_Object obj = get_next_object(); + const BER_Object obj = get_next_object(); obj.assert_is_a(ASN1_Type::Null, ASN1_Class::Universal); if(obj.length() > 0) { throw BER_Decoding_Error("NULL object had nonzero size"); @@ -395,14 +585,22 @@ * Decode a BER encoded BOOLEAN */ BER_Decoder& BER_Decoder::decode(bool& out, ASN1_Type type_tag, ASN1_Class class_tag) { - BER_Object obj = get_next_object(); + const BER_Object obj = get_next_object(); obj.assert_is_a(type_tag, class_tag); if(obj.length() != 1) { throw BER_Decoding_Error("BER boolean value had invalid size"); } - out = (obj.bits()[0]) ? true : false; + const uint8_t val = obj.bits()[0]; + + // DER requires boolean values to be exactly 0x00 or 0xFF + if(m_limits.require_der_encoding() && val != 0x00 && val != 0xFF) { + throw BER_Decoding_Error("Detected non-canonical boolean encoding in DER structure"); + } + + out = (val != 0) ? true : false; + return (*this); } @@ -413,7 +611,7 @@ BigInt integer; decode(integer, type_tag, class_tag); - if(integer.is_negative()) { + if(integer.signum() < 0) { throw BER_Decoding_Error("Decoded small integer value was negative"); } @@ -440,6 +638,10 @@ BigInt integer; decode(integer, type_tag, class_tag); + if(integer.is_negative()) { + throw BER_Decoding_Error("Decoded small integer value was negative"); + } + if(integer.bits() > 8 * T_bytes) { throw BER_Decoding_Error("Decoded integer value larger than expected"); } @@ -456,18 +658,36 @@ * Decode a BER encoded INTEGER */ BER_Decoder& BER_Decoder::decode(BigInt& out, ASN1_Type type_tag, ASN1_Class class_tag) { - BER_Object obj = get_next_object(); + const BER_Object obj = get_next_object(); obj.assert_is_a(type_tag, class_tag); + // DER requires minimal INTEGER encoding (X.690 section 8.3.2) + if(m_limits.require_der_encoding()) { + if(obj.length() == 0) { + throw BER_Decoding_Error("Detected empty INTEGER encoding in DER structure"); + } + if(obj.length() > 1) { + if(obj.bits()[0] == 0x00 && (obj.bits()[1] & 0x80) == 0) { + throw BER_Decoding_Error("Detected non-minimal INTEGER encoding in DER structure"); + } + if(obj.bits()[0] == 0xFF && (obj.bits()[1] & 0x80) != 0) { + throw BER_Decoding_Error("Detected non-minimal INTEGER encoding in DER structure"); + } + } + } + if(obj.length() == 0) { out.clear(); } else { - const bool negative = (obj.bits()[0] & 0x80) ? true : false; + const uint8_t first = obj.bits()[0]; + const bool negative = (first & 0x80) == 0x80; if(negative) { secure_vector vec(obj.bits(), obj.bits() + obj.length()); for(size_t i = obj.length(); i > 0; --i) { - if(vec[i - 1]--) { + const bool gt0 = (vec[i - 1] > 0); + vec[i - 1] -= 1; + if(gt0) { break; } } @@ -486,24 +706,50 @@ namespace { +bool is_constructed(const BER_Object& obj) { + return is_constructed(obj.class_tag()); +} + template void asn1_decode_binary_string(std::vector& buffer, const BER_Object& obj, ASN1_Type real_type, ASN1_Type type_tag, - ASN1_Class class_tag) { + ASN1_Class class_tag, + bool require_der) { obj.assert_is_a(type_tag, class_tag); + // DER requires BIT STRING and OCTET STRING to use primitive encoding + if(require_der && is_constructed(obj)) { + throw BER_Decoding_Error("Detected constructed string encoding in DER structure"); + } + if(real_type == ASN1_Type::OctetString) { buffer.assign(obj.bits(), obj.bits() + obj.length()); } else { if(obj.length() == 0) { throw BER_Decoding_Error("Invalid BIT STRING"); } - if(obj.bits()[0] >= 8) { + + const uint8_t unused_bits = obj.bits()[0]; + + if(unused_bits >= 8) { throw BER_Decoding_Error("Bad number of unused bits in BIT STRING"); } + // Empty BIT STRING with unused bits > 0 ... + if(unused_bits > 0 && obj.length() < 2) { + throw BER_Decoding_Error("Invalid BIT STRING"); + } + + // DER requires unused bits in BIT STRING to be zero (X.690 section 11.2.2) + if(require_der && unused_bits > 0) { + const uint8_t last_byte = obj.bits()[obj.length() - 1]; + if((last_byte & ((1 << unused_bits) - 1)) != 0) { + throw BER_Decoding_Error("Detected non-zero padding bits in BIT STRING in DER structure"); + } + } + buffer.resize(obj.length() - 1); if(obj.length() > 1) { @@ -525,7 +771,8 @@ throw BER_Bad_Tag("Bad tag for {BIT,OCTET} STRING", static_cast(real_type)); } - asn1_decode_binary_string(buffer, get_next_object(), real_type, type_tag, class_tag); + asn1_decode_binary_string( + buffer, get_next_object(), real_type, type_tag, class_tag, m_limits.require_der_encoding()); return (*this); } @@ -537,7 +784,8 @@ throw BER_Bad_Tag("Bad tag for {BIT,OCTET} STRING", static_cast(real_type)); } - asn1_decode_binary_string(buffer, get_next_object(), real_type, type_tag, class_tag); + asn1_decode_binary_string( + buffer, get_next_object(), real_type, type_tag, class_tag, m_limits.require_der_encoding()); return (*this); } diff -Nru botan3-3.7.1+dfsg/src/lib/asn1/ber_dec.h botan3-3.12.0+dfsg/src/lib/asn1/ber_dec.h --- botan3-3.7.1+dfsg/src/lib/asn1/ber_dec.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/asn1/ber_dec.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,12 +9,15 @@ #define BOTAN_BER_DECODER_H_ #include -#include -#include +#include +#include +#include +#include namespace Botan { class BigInt; +class DataSource; /** * BER Decoding Object @@ -22,43 +25,77 @@ class BOTAN_PUBLIC_API(2, 0) BER_Decoder final { public: /** - * Set up to BER decode the data in buf of length len + * Controls what encoding rules the decoder accepts. */ - BER_Decoder(const uint8_t buf[], size_t len); + class BOTAN_PUBLIC_API(3, 12) Limits final { + public: + /** + * Accept only DER encodings + */ + static Limits DER() { return Limits(false, 0); } - /** - * Set up to BER decode the data in buf of length len - */ - BER_Decoder(std::span buf) : BER_Decoder(buf.data(), buf.size()) {} + /** + * Accept non-canonical BER encodings. + * + * @param max_nested_indef maximum number of nested indefinite-length encodings accepted + */ + static Limits BER(size_t max_nested_indef = 16) { return Limits(true, max_nested_indef); } + + bool allow_ber_encoding() const { return m_allow_ber; } + + bool require_der_encoding() const { return !allow_ber_encoding(); } + + size_t max_nested_indefinite_length() const { return m_max_nested_indef; } + + bool operator==(const Limits&) const = default; + + private: + Limits(bool allow_ber, size_t max_nested_indef) : + m_allow_ber(allow_ber), m_max_nested_indef(max_nested_indef) {} + + bool m_allow_ber; + size_t m_max_nested_indef; + }; /** - * Set up to BER decode the data in vec + * Set up to BER decode the data in buf of length len */ - explicit BER_Decoder(const secure_vector& vec); + BOTAN_DEPRECATED("Use BER_Decoder(span) constructor") + BER_Decoder(const uint8_t buf[], size_t len, Limits limits = Limits::BER()) : + BER_Decoder(std::span{buf, len}, limits) {} /** - * Set up to BER decode the data in vec + * Set up to BER decode the data in buf */ - explicit BER_Decoder(const std::vector& vec); + explicit BER_Decoder(std::span buf, Limits limits = Limits::BER()); /** * Set up to BER decode the data in src */ - explicit BER_Decoder(DataSource& src); + explicit BER_Decoder(DataSource& src, Limits limits = Limits::BER()); /** * Set up to BER decode the data in obj */ - BER_Decoder(const BER_Object& obj) : BER_Decoder(obj.bits(), obj.length()) {} + BOTAN_FUTURE_EXPLICIT BER_Decoder(const BER_Object& obj, Limits limits = Limits::BER()) : + BER_Decoder(obj.data(), limits) {} /** * Set up to BER decode the data in obj + * TODO(Botan4) remove this? */ - BER_Decoder(BER_Object&& obj) : BER_Decoder(std::move(obj), nullptr) {} + BOTAN_FUTURE_EXPLICIT BER_Decoder(BER_Object&& obj) : BER_Decoder(std::move(obj), nullptr) {} - BER_Decoder(const BER_Decoder& other); + BER_Decoder(const BER_Decoder& other) = delete; + BER_Decoder(BER_Decoder&& other) noexcept; BER_Decoder& operator=(const BER_Decoder&) = delete; + BER_Decoder& operator=(BER_Decoder&&) noexcept; + + /** + * Returns the limits currently applied to this decoder + */ + Limits limits() const { return m_limits; } /** * Get the next object in the data stream. @@ -150,17 +187,11 @@ */ template BER_Decoder& get_next_value(T& out, ASN1_Type type_tag, ASN1_Class class_tag = ASN1_Class::ContextSpecific) - requires std::is_standard_layout::value && std::is_trivial::value + requires std::is_standard_layout_v && std::is_trivial_v { - BER_Object obj = get_next_object(); - obj.assert_is_a(type_tag, class_tag); - - if(obj.length() != sizeof(T)) { - throw BER_Decoding_Error("Size mismatch. Object value size is " + std::to_string(obj.length()) + - "; Output type size is " + std::to_string(sizeof(T))); - } + const BER_Object obj = get_next_value(sizeof(T), type_tag, class_tag); - copy_mem(reinterpret_cast(&out), obj.bits(), obj.length()); + std::memcpy(reinterpret_cast(&out), obj.bits(), obj.length()); return (*this); } @@ -171,9 +202,12 @@ template BER_Decoder& raw_bytes(std::vector& out) { out.clear(); - uint8_t buf; - while(m_source->read_byte(buf)) { - out.push_back(buf); + for(;;) { + if(auto next = this->read_next_byte()) { + out.push_back(*next); + } else { + break; + } } return (*this); } @@ -248,7 +282,15 @@ } template - BER_Decoder& decode_optional(T& out, ASN1_Type type_tag, ASN1_Class class_tag, const T& default_value = T()); + BER_Decoder& decode_optional(T& out, ASN1_Type type_tag, ASN1_Class class_tag, const T& default_value = T()) { + std::optional optval; + this->decode_optional(optval, type_tag, class_tag); + out = optval ? *optval : default_value; + return (*this); + } + + template + BER_Decoder& decode_optional(std::optional& out, ASN1_Type type_tag, ASN1_Class class_tag); template BER_Decoder& decode_optional_implicit(T& out, @@ -290,11 +332,11 @@ ASN1_Class class_tag = ASN1_Class::ContextSpecific) { BER_Object obj = get_next_object(); - ASN1_Type type_tag = static_cast(expected_tag); + const ASN1_Type type_tag = static_cast(expected_tag); if(obj.is_a(type_tag, class_tag)) { if(class_tag == ASN1_Class::ExplicitContextSpecific) { - BER_Decoder(std::move(obj)).decode(out, real_type).verify_end(); + BER_Decoder(obj, m_limits).decode(out, real_type).verify_end(); } else { push_back(std::move(obj)); decode(out, real_type, type_tag, class_tag); @@ -315,33 +357,42 @@ return decode_optional_string(out, real_type, static_cast(expected_tag), class_tag); } + ~BER_Decoder(); + private: BER_Decoder(BER_Object&& obj, BER_Decoder* parent); + std::optional read_next_byte(); + + BER_Object get_next_value(size_t sizeofT, ASN1_Type type_tag, ASN1_Class class_tag); + + Limits m_limits; BER_Decoder* m_parent = nullptr; BER_Object m_pushed; // either m_data_src.get() or an unowned pointer DataSource* m_source; - mutable std::unique_ptr m_data_src; + std::unique_ptr m_data_src; }; /* * Decode an OPTIONAL or DEFAULT element */ template -BER_Decoder& BER_Decoder::decode_optional(T& out, ASN1_Type type_tag, ASN1_Class class_tag, const T& default_value) { +BER_Decoder& BER_Decoder::decode_optional(std::optional& optval, ASN1_Type type_tag, ASN1_Class class_tag) { BER_Object obj = get_next_object(); if(obj.is_a(type_tag, class_tag)) { + T out{}; if(class_tag == ASN1_Class::ExplicitContextSpecific) { - BER_Decoder(std::move(obj)).decode(out).verify_end(); + BER_Decoder(obj, m_limits).decode(out).verify_end(); } else { - push_back(std::move(obj)); - decode(out, type_tag, class_tag); + this->push_back(std::move(obj)); + this->decode(out, type_tag, class_tag); } + optval = std::move(out); } else { - out = default_value; - push_back(std::move(obj)); + this->push_back(std::move(obj)); + optval = std::nullopt; } return (*this); @@ -373,7 +424,7 @@ } /* -* Decode a list of homogenously typed values +* Decode a list of homogeneously typed values */ template BER_Decoder& BER_Decoder::decode_list(std::vector& vec, ASN1_Type type_tag, ASN1_Class class_tag) { @@ -391,7 +442,7 @@ } /* -* Decode an optional list of homogenously typed values +* Decode an optional list of homogeneously typed values */ template bool BER_Decoder::decode_optional_list(std::vector& vec, ASN1_Type type_tag, ASN1_Class class_tag) { diff -Nru botan3-3.7.1+dfsg/src/lib/asn1/der_enc.cpp botan3-3.12.0+dfsg/src/lib/asn1/der_enc.cpp --- botan3-3.7.1+dfsg/src/lib/asn1/der_enc.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/asn1/der_enc.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace Botan { @@ -78,7 +79,7 @@ void DER_Encoder::DER_Sequence::push_contents(DER_Encoder& der) { const auto real_class_tag = m_class_tag | ASN1_Class::Constructed; - if(m_type_tag == ASN1_Type::Set) { + if(m_type_tag == ASN1_Type::Set && m_class_tag == ASN1_Class::Universal) { std::sort(m_set_contents.begin(), m_set_contents.end()); for(const auto& set_elem : m_set_contents) { m_contents += set_elem; @@ -94,7 +95,7 @@ * Add an encoded value to the SEQUENCE/SET */ void DER_Encoder::DER_Sequence::add_bytes(const uint8_t data[], size_t length) { - if(m_type_tag == ASN1_Type::Set) { + if(m_type_tag == ASN1_Type::Set && m_class_tag == ASN1_Class::Universal) { m_set_contents.push_back(secure_vector(data, data + length)); } else { m_contents += std::make_pair(data, length); @@ -102,7 +103,7 @@ } void DER_Encoder::DER_Sequence::add_bytes(const uint8_t hdr[], size_t hdr_len, const uint8_t val[], size_t val_len) { - if(m_type_tag == ASN1_Type::Set) { + if(m_type_tag == ASN1_Type::Set && m_class_tag == ASN1_Class::Universal) { secure_vector m; m.reserve(hdr_len + val_len); m += std::make_pair(hdr, hdr_len); @@ -124,7 +125,8 @@ /* * DER_Sequence Constructor */ -DER_Encoder::DER_Sequence::DER_Sequence(ASN1_Type t1, ASN1_Class t2) : m_type_tag(t1), m_class_tag(t2) {} +DER_Encoder::DER_Sequence::DER_Sequence(ASN1_Type type_tag, ASN1_Class class_tag) : + m_type_tag(type_tag), m_class_tag(class_tag) {} /* * Return the encoded contents @@ -184,14 +186,7 @@ * Start a new ASN.1 EXPLICIT encoding */ DER_Encoder& DER_Encoder::start_explicit(uint16_t type_no) { - ASN1_Type type_tag = static_cast(type_no); - - // This would confuse DER_Sequence - if(type_tag == ASN1_Type::Set) { - throw Internal_Error("DER_Encoder.start_explicit(SET) not supported"); - } - - return start_cons(type_tag, ASN1_Class::ContextSpecific); + return start_cons(static_cast(type_no), ASN1_Class::ContextSpecific); } /* @@ -276,7 +271,7 @@ * DER encode a BOOLEAN */ DER_Encoder& DER_Encoder::encode(bool is_true, ASN1_Type type_tag, ASN1_Class class_tag) { - uint8_t val = is_true ? 0xFF : 0x00; + const uint8_t val = is_true ? 0xFF : 0x00; return add_object(type_tag, class_tag, &val, 1); } @@ -295,21 +290,30 @@ return add_object(type_tag, class_tag, 0); } - const size_t extra_zero = (n.bits() % 8 == 0) ? 1 : 0; + // Serialize magnitude with one extra leading byte + auto contents = n.serialize(n.bytes() + 1); - auto contents = n.serialize(n.bytes() + extra_zero); - if(n < 0) { - for(unsigned char& content : contents) { - content = ~content; + if(n.signum() < 0) { + // Two's complement: bitwise NOT then increment + for(auto& byte : contents) { + byte = ~byte; } for(size_t i = contents.size(); i > 0; --i) { - if(++contents[i - 1]) { + if(++contents[i - 1] != 0) { break; } } } - return add_object(type_tag, class_tag, contents); + /* + * DER requires the leading byte be emitted only if it required + */ + BOTAN_ASSERT_NOMSG(contents.size() >= 2); + const bool leading_byte_redundant = + (contents[0] == 0x00 && (contents[1] & 0x80) == 0) || (contents[0] == 0xFF && (contents[1] & 0x80) != 0); + auto encoding = std::span{contents}.subspan(leading_byte_redundant ? 1 : 0); + + return add_object(type_tag, class_tag, encoding); } /* @@ -340,16 +344,14 @@ * Write the encoding of the byte(s) */ DER_Encoder& DER_Encoder::add_object(ASN1_Type type_tag, ASN1_Class class_tag, std::string_view rep_str) { - const uint8_t* rep = cast_char_ptr_to_uint8(rep_str.data()); - const size_t rep_len = rep_str.size(); - return add_object(type_tag, class_tag, rep, rep_len); + return add_object(type_tag, class_tag, as_span_of_bytes(rep_str)); } /* * Write the encoding of the byte */ DER_Encoder& DER_Encoder::add_object(ASN1_Type type_tag, ASN1_Class class_tag, uint8_t rep) { - return add_object(type_tag, class_tag, &rep, 1); + return add_object(type_tag, class_tag, std::span{&rep, 1}); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/asn1/der_enc.h botan3-3.12.0+dfsg/src/lib/asn1/der_enc.h --- botan3-3.7.1+dfsg/src/lib/asn1/der_enc.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/asn1/der_enc.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,7 +9,10 @@ #define BOTAN_DER_ENCODER_H_ #include +#include #include +#include +#include #include namespace Botan { @@ -34,19 +37,19 @@ * DER encode, writing to @param vec * If this constructor is used, get_contents* may not be called. */ - DER_Encoder(secure_vector& vec); + BOTAN_FUTURE_EXPLICIT DER_Encoder(secure_vector& vec); /** * DER encode, writing to @param vec * If this constructor is used, get_contents* may not be called. */ - DER_Encoder(std::vector& vec); + BOTAN_FUTURE_EXPLICIT DER_Encoder(std::vector& vec); /** * DER encode, calling append to write output * If this constructor is used, get_contents* may not be called. */ - DER_Encoder(append_fn append) : m_append_output(std::move(append)) {} + BOTAN_FUTURE_EXPLICIT DER_Encoder(append_fn append) : m_append_output(std::move(append)) {} secure_vector get_contents(); @@ -54,8 +57,8 @@ * Return the encoded contents as a std::vector * * If using this function, instead pass a std::vector to the - * contructor of DER_Encoder where the output will be placed. This - * avoids several unecessary copies. + * constructor of DER_Encoder where the output will be placed. This + * avoids several unnecessary copies. */ BOTAN_DEPRECATED("Use DER_Encoder(vector) instead") std::vector get_contents_unlocked(); @@ -83,10 +86,7 @@ */ DER_Encoder& raw_bytes(const uint8_t val[], size_t len); - template - DER_Encoder& raw_bytes(const std::vector& val) { - return raw_bytes(val.data(), val.size()); - } + DER_Encoder& raw_bytes(std::span val) { return raw_bytes(val.data(), val.size()); } DER_Encoder& encode_null(); DER_Encoder& encode(bool b); @@ -120,6 +120,7 @@ } template + BOTAN_DEPRECATED("Use the version that takes a std::optional") DER_Encoder& encode_optional(const T& value, const T& default_value) { if(value != default_value) { encode(value); @@ -128,6 +129,14 @@ } template + DER_Encoder& encode_optional(const std::optional& value) { + if(value) { + encode(*value); + } + return (*this); + } + + template DER_Encoder& encode_list(const std::vector& values) { for(size_t i = 0; i != values.size(); ++i) { encode(values[i]); @@ -164,14 +173,25 @@ return (*this); } + DER_Encoder& encode_if(bool pred, bool num) { + if(pred) { + encode(num); + } + return (*this); + } + DER_Encoder& add_object(ASN1_Type type_tag, ASN1_Class class_tag, const uint8_t rep[], size_t length); - DER_Encoder& add_object(ASN1_Type type_tag, ASN1_Class class_tag, const std::vector& rep) { + DER_Encoder& add_object(ASN1_Type type_tag, ASN1_Class class_tag, std::span rep) { return add_object(type_tag, class_tag, rep.data(), rep.size()); } + DER_Encoder& add_object(ASN1_Type type_tag, ASN1_Class class_tag, const std::vector& rep) { + return add_object(type_tag, class_tag, std::span{rep}); + } + DER_Encoder& add_object(ASN1_Type type_tag, ASN1_Class class_tag, const secure_vector& rep) { - return add_object(type_tag, class_tag, rep.data(), rep.size()); + return add_object(type_tag, class_tag, std::span{rep}); } DER_Encoder& add_object(ASN1_Type type_tag, ASN1_Class class_tag, std::string_view str); @@ -189,11 +209,11 @@ void add_bytes(const uint8_t hdr[], size_t hdr_len, const uint8_t val[], size_t val_len); - DER_Sequence(ASN1_Type, ASN1_Class); + DER_Sequence(ASN1_Type type_tag, ASN1_Class class_tag); DER_Sequence(DER_Sequence&& seq) noexcept : - m_type_tag(std::move(seq.m_type_tag)), - m_class_tag(std::move(seq.m_class_tag)), + m_type_tag(seq.m_type_tag), + m_class_tag(seq.m_class_tag), m_contents(std::move(seq.m_contents)), m_set_contents(std::move(seq.m_set_contents)) {} @@ -206,8 +226,8 @@ } DER_Sequence(const DER_Sequence& seq) = default; - DER_Sequence& operator=(const DER_Sequence& seq) = default; + ~DER_Sequence() = default; private: ASN1_Type m_type_tag; diff -Nru botan3-3.7.1+dfsg/src/lib/asn1/info.txt botan3-3.12.0+dfsg/src/lib/asn1/info.txt --- botan3-3.7.1+dfsg/src/lib/asn1/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/asn1/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -12,6 +12,7 @@ +asn1_time.h asn1_print.h asn1_obj.h der_enc.h diff -Nru botan3-3.7.1+dfsg/src/lib/asn1/oid_map.cpp botan3-3.12.0+dfsg/src/lib/asn1/oid_map.cpp --- botan3-3.7.1+dfsg/src/lib/asn1/oid_map.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/asn1/oid_map.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -19,7 +19,15 @@ } void OID_Map::add_oid(const OID& oid, std::string_view str) { - lock_guard_type lock(m_mutex); + if(auto name = lookup_static_oid(oid)) { + if(*name != str) { + throw Invalid_State("Cannot register two different names to a single OID"); + } else { + return; + } + } + + const lock_guard_type lock(m_mutex); auto o2s = m_oid2str.find(oid); @@ -37,21 +45,33 @@ } void OID_Map::add_str2oid(const OID& oid, std::string_view str) { - lock_guard_type lock(m_mutex); + if(lookup_static_oid_name(str).has_value()) { + return; + } + + const lock_guard_type lock(m_mutex); if(!m_str2oid.contains(std::string(str))) { m_str2oid.insert(std::make_pair(str, oid)); } } void OID_Map::add_oid2str(const OID& oid, std::string_view str) { - lock_guard_type lock(m_mutex); + if(lookup_static_oid(oid).has_value()) { + return; + } + + const lock_guard_type lock(m_mutex); if(!m_oid2str.contains(oid)) { m_oid2str.insert(std::make_pair(oid, str)); } } std::string OID_Map::oid2str(const OID& oid) { - lock_guard_type lock(m_mutex); + if(auto name = lookup_static_oid(oid)) { + return std::string(*name); + } + + const lock_guard_type lock(m_mutex); auto i = m_oid2str.find(oid); if(i != m_oid2str.end()) { @@ -62,7 +82,11 @@ } OID OID_Map::str2oid(std::string_view str) { - lock_guard_type lock(m_mutex); + if(auto oid = lookup_static_oid_name(str)) { + return std::move(*oid); + } + + const lock_guard_type lock(m_mutex); auto i = m_str2oid.find(std::string(str)); if(i != m_str2oid.end()) { return i->second; diff -Nru botan3-3.7.1+dfsg/src/lib/asn1/oid_map.h botan3-3.12.0+dfsg/src/lib/asn1/oid_map.h --- botan3-3.7.1+dfsg/src/lib/asn1/oid_map.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/asn1/oid_map.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -19,8 +20,10 @@ public: void add_oid(const OID& oid, std::string_view str); + // TODO(Botan4) remove this function when oids.h is removed void add_str2oid(const OID& oid, std::string_view str); + // TODO(Botan4) remove this function when oids.h is removed void add_oid2str(const OID& oid, std::string_view str); std::string oid2str(const OID& oid); @@ -30,6 +33,9 @@ static OID_Map& global_registry(); private: + static std::optional lookup_static_oid(const OID& oid); + static std::optional lookup_static_oid_name(std::string_view name); + static std::unordered_map load_oid2str_map(); static std::unordered_map load_str2oid_map(); diff -Nru botan3-3.7.1+dfsg/src/lib/asn1/oid_maps.cpp botan3-3.12.0+dfsg/src/lib/asn1/oid_maps.cpp --- botan3-3.7.1+dfsg/src/lib/asn1/oid_maps.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/asn1/oid_maps.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,693 +0,0 @@ -/* -* OID maps -* -* This file was automatically generated by ./src/scripts/dev_tools/gen_oids.py on 2025-01-26 -* -* All manual edits to this file will be lost. Edit the script -* then regenerate this source file. -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include - -namespace Botan { - -std::unordered_map OID_Map::load_oid2str_map() { - return std::unordered_map{ - - {OID({0, 3, 4401, 5, 3, 1, 9, 26}), "Camellia-192/GCM"}, - {OID({0, 3, 4401, 5, 3, 1, 9, 46}), "Camellia-256/GCM"}, - {OID({0, 3, 4401, 5, 3, 1, 9, 6}), "Camellia-128/GCM"}, - {OID({0, 4, 0, 127, 0, 15, 1, 1, 13, 0}), "XMSS"}, - {OID({1, 0, 14888, 3, 0, 5}), "ECKCDSA"}, - {OID({1, 2, 156, 10197, 1, 104, 100}), "SM4/OCB"}, - {OID({1, 2, 156, 10197, 1, 104, 2}), "SM4/CBC"}, - {OID({1, 2, 156, 10197, 1, 104, 8}), "SM4/GCM"}, - {OID({1, 2, 156, 10197, 1, 301}), "sm2p256v1"}, - {OID({1, 2, 156, 10197, 1, 301, 1}), "SM2"}, - {OID({1, 2, 156, 10197, 1, 301, 2}), "SM2_Kex"}, - {OID({1, 2, 156, 10197, 1, 301, 3}), "SM2_Enc"}, - {OID({1, 2, 156, 10197, 1, 401}), "SM3"}, - {OID({1, 2, 156, 10197, 1, 501}), "SM2_Sig/SM3"}, - {OID({1, 2, 156, 10197, 1, 504}), "RSA/PKCS1v15(SM3)"}, - {OID({1, 2, 250, 1, 223, 101, 256, 1}), "frp256v1"}, - {OID({1, 2, 392, 200011, 61, 1, 1, 1, 2}), "Camellia-128/CBC"}, - {OID({1, 2, 392, 200011, 61, 1, 1, 1, 3}), "Camellia-192/CBC"}, - {OID({1, 2, 392, 200011, 61, 1, 1, 1, 4}), "Camellia-256/CBC"}, - {OID({1, 2, 410, 200004, 1, 100, 4, 3}), "ECKCDSA/SHA-1"}, - {OID({1, 2, 410, 200004, 1, 100, 4, 4}), "ECKCDSA/SHA-224"}, - {OID({1, 2, 410, 200004, 1, 100, 4, 5}), "ECKCDSA/SHA-256"}, - {OID({1, 2, 410, 200004, 1, 4}), "SEED/CBC"}, - {OID({1, 2, 643, 100, 1}), "GOST.OGRN"}, - {OID({1, 2, 643, 100, 111}), "GOST.SubjectSigningTool"}, - {OID({1, 2, 643, 100, 112}), "GOST.IssuerSigningTool"}, - {OID({1, 2, 643, 2, 2, 19}), "GOST-34.10"}, - {OID({1, 2, 643, 2, 2, 3}), "GOST-34.10/GOST-R-34.11-94"}, - {OID({1, 2, 643, 2, 2, 35, 1}), "gost_256A"}, - {OID({1, 2, 643, 2, 2, 36, 0}), "gost_256A"}, - {OID({1, 2, 643, 3, 131, 1, 1}), "GOST.INN"}, - {OID({1, 2, 643, 7, 1, 1, 1, 1}), "GOST-34.10-2012-256"}, - {OID({1, 2, 643, 7, 1, 1, 1, 2}), "GOST-34.10-2012-512"}, - {OID({1, 2, 643, 7, 1, 1, 2, 2}), "Streebog-256"}, - {OID({1, 2, 643, 7, 1, 1, 2, 3}), "Streebog-512"}, - {OID({1, 2, 643, 7, 1, 1, 3, 2}), "GOST-34.10-2012-256/Streebog-256"}, - {OID({1, 2, 643, 7, 1, 1, 3, 3}), "GOST-34.10-2012-512/Streebog-512"}, - {OID({1, 2, 643, 7, 1, 2, 1, 1, 1}), "gost_256A"}, - {OID({1, 2, 643, 7, 1, 2, 1, 1, 2}), "gost_256B"}, - {OID({1, 2, 643, 7, 1, 2, 1, 2, 1}), "gost_512A"}, - {OID({1, 2, 643, 7, 1, 2, 1, 2, 2}), "gost_512B"}, - {OID({1, 2, 840, 10040, 4, 1}), "DSA"}, - {OID({1, 2, 840, 10040, 4, 3}), "DSA/SHA-1"}, - {OID({1, 2, 840, 10045, 2, 1}), "ECDSA"}, - {OID({1, 2, 840, 10045, 3, 1, 1}), "secp192r1"}, - {OID({1, 2, 840, 10045, 3, 1, 2}), "x962_p192v2"}, - {OID({1, 2, 840, 10045, 3, 1, 3}), "x962_p192v3"}, - {OID({1, 2, 840, 10045, 3, 1, 4}), "x962_p239v1"}, - {OID({1, 2, 840, 10045, 3, 1, 5}), "x962_p239v2"}, - {OID({1, 2, 840, 10045, 3, 1, 6}), "x962_p239v3"}, - {OID({1, 2, 840, 10045, 3, 1, 7}), "secp256r1"}, - {OID({1, 2, 840, 10045, 4, 1}), "ECDSA/SHA-1"}, - {OID({1, 2, 840, 10045, 4, 3, 1}), "ECDSA/SHA-224"}, - {OID({1, 2, 840, 10045, 4, 3, 2}), "ECDSA/SHA-256"}, - {OID({1, 2, 840, 10045, 4, 3, 3}), "ECDSA/SHA-384"}, - {OID({1, 2, 840, 10045, 4, 3, 4}), "ECDSA/SHA-512"}, - {OID({1, 2, 840, 10046, 2, 1}), "DH"}, - {OID({1, 2, 840, 113533, 7, 66, 10}), "CAST-128/CBC"}, - {OID({1, 2, 840, 113533, 7, 66, 15}), "KeyWrap.CAST-128"}, - {OID({1, 2, 840, 113549, 1, 1, 1}), "RSA"}, - {OID({1, 2, 840, 113549, 1, 1, 10}), "RSA/PSS"}, - {OID({1, 2, 840, 113549, 1, 1, 11}), "RSA/PKCS1v15(SHA-256)"}, - {OID({1, 2, 840, 113549, 1, 1, 12}), "RSA/PKCS1v15(SHA-384)"}, - {OID({1, 2, 840, 113549, 1, 1, 13}), "RSA/PKCS1v15(SHA-512)"}, - {OID({1, 2, 840, 113549, 1, 1, 14}), "RSA/PKCS1v15(SHA-224)"}, - {OID({1, 2, 840, 113549, 1, 1, 16}), "RSA/PKCS1v15(SHA-512-256)"}, - {OID({1, 2, 840, 113549, 1, 1, 2}), "RSA/PKCS1v15(MD2)"}, - {OID({1, 2, 840, 113549, 1, 1, 4}), "RSA/PKCS1v15(MD5)"}, - {OID({1, 2, 840, 113549, 1, 1, 5}), "RSA/PKCS1v15(SHA-1)"}, - {OID({1, 2, 840, 113549, 1, 1, 7}), "RSA/OAEP"}, - {OID({1, 2, 840, 113549, 1, 1, 8}), "MGF1"}, - {OID({1, 2, 840, 113549, 1, 5, 12}), "PKCS5.PBKDF2"}, - {OID({1, 2, 840, 113549, 1, 5, 13}), "PBE-PKCS5v20"}, - {OID({1, 2, 840, 113549, 1, 9, 1}), "PKCS9.EmailAddress"}, - {OID({1, 2, 840, 113549, 1, 9, 14}), "PKCS9.ExtensionRequest"}, - {OID({1, 2, 840, 113549, 1, 9, 16, 3, 17}), "HSS-LMS"}, - {OID({1, 2, 840, 113549, 1, 9, 16, 3, 18}), "ChaCha20Poly1305"}, - {OID({1, 2, 840, 113549, 1, 9, 16, 3, 6}), "KeyWrap.TripleDES"}, - {OID({1, 2, 840, 113549, 1, 9, 16, 3, 8}), "Compression.Zlib"}, - {OID({1, 2, 840, 113549, 1, 9, 2}), "PKCS9.UnstructuredName"}, - {OID({1, 2, 840, 113549, 1, 9, 3}), "PKCS9.ContentType"}, - {OID({1, 2, 840, 113549, 1, 9, 4}), "PKCS9.MessageDigest"}, - {OID({1, 2, 840, 113549, 1, 9, 7}), "PKCS9.ChallengePassword"}, - {OID({1, 2, 840, 113549, 2, 10}), "HMAC(SHA-384)"}, - {OID({1, 2, 840, 113549, 2, 11}), "HMAC(SHA-512)"}, - {OID({1, 2, 840, 113549, 2, 13}), "HMAC(SHA-512-256)"}, - {OID({1, 2, 840, 113549, 2, 5}), "MD5"}, - {OID({1, 2, 840, 113549, 2, 7}), "HMAC(SHA-1)"}, - {OID({1, 2, 840, 113549, 2, 8}), "HMAC(SHA-224)"}, - {OID({1, 2, 840, 113549, 2, 9}), "HMAC(SHA-256)"}, - {OID({1, 2, 840, 113549, 3, 7}), "TripleDES/CBC"}, - {OID({1, 3, 101, 110}), "X25519"}, - {OID({1, 3, 101, 111}), "X448"}, - {OID({1, 3, 101, 112}), "Ed25519"}, - {OID({1, 3, 101, 113}), "Ed448"}, - {OID({1, 3, 132, 0, 10}), "secp256k1"}, - {OID({1, 3, 132, 0, 30}), "secp160r2"}, - {OID({1, 3, 132, 0, 31}), "secp192k1"}, - {OID({1, 3, 132, 0, 32}), "secp224k1"}, - {OID({1, 3, 132, 0, 33}), "secp224r1"}, - {OID({1, 3, 132, 0, 34}), "secp384r1"}, - {OID({1, 3, 132, 0, 35}), "secp521r1"}, - {OID({1, 3, 132, 0, 8}), "secp160r1"}, - {OID({1, 3, 132, 0, 9}), "secp160k1"}, - {OID({1, 3, 132, 1, 12}), "ECDH"}, - {OID({1, 3, 14, 3, 2, 26}), "SHA-1"}, - {OID({1, 3, 14, 3, 2, 7}), "DES/CBC"}, - {OID({1, 3, 36, 3, 2, 1}), "RIPEMD-160"}, - {OID({1, 3, 36, 3, 3, 1, 2}), "RSA/PKCS1v15(RIPEMD-160)"}, - {OID({1, 3, 36, 3, 3, 2, 5, 2, 1}), "ECGDSA"}, - {OID({1, 3, 36, 3, 3, 2, 5, 4, 1}), "ECGDSA/RIPEMD-160"}, - {OID({1, 3, 36, 3, 3, 2, 5, 4, 2}), "ECGDSA/SHA-1"}, - {OID({1, 3, 36, 3, 3, 2, 5, 4, 3}), "ECGDSA/SHA-224"}, - {OID({1, 3, 36, 3, 3, 2, 5, 4, 4}), "ECGDSA/SHA-256"}, - {OID({1, 3, 36, 3, 3, 2, 5, 4, 5}), "ECGDSA/SHA-384"}, - {OID({1, 3, 36, 3, 3, 2, 5, 4, 6}), "ECGDSA/SHA-512"}, - {OID({1, 3, 36, 3, 3, 2, 8, 1, 1, 1}), "brainpool160r1"}, - {OID({1, 3, 36, 3, 3, 2, 8, 1, 1, 11}), "brainpool384r1"}, - {OID({1, 3, 36, 3, 3, 2, 8, 1, 1, 13}), "brainpool512r1"}, - {OID({1, 3, 36, 3, 3, 2, 8, 1, 1, 3}), "brainpool192r1"}, - {OID({1, 3, 36, 3, 3, 2, 8, 1, 1, 5}), "brainpool224r1"}, - {OID({1, 3, 36, 3, 3, 2, 8, 1, 1, 7}), "brainpool256r1"}, - {OID({1, 3, 36, 3, 3, 2, 8, 1, 1, 9}), "brainpool320r1"}, - {OID({1, 3, 6, 1, 4, 1, 11591, 15, 1}), "OpenPGP.Ed25519"}, - {OID({1, 3, 6, 1, 4, 1, 11591, 4, 11}), "Scrypt"}, - {OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 1}), "ClassicMcEliece_348864"}, - {OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 10}), "ClassicMcEliece_8192128f"}, - {OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 2}), "ClassicMcEliece_348864f"}, - {OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 3}), "ClassicMcEliece_460896"}, - {OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 4}), "ClassicMcEliece_460896f"}, - {OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 5}), "ClassicMcEliece_6688128"}, - {OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 6}), "ClassicMcEliece_6688128f"}, - {OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 7}), "ClassicMcEliece_6960119"}, - {OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 8}), "ClassicMcEliece_6960119f"}, - {OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 9}), "ClassicMcEliece_8192128"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 10, 1}), "Dilithium-4x4-AES-r3"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 10, 2}), "Dilithium-6x5-AES-r3"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 10, 3}), "Dilithium-8x7-AES-r3"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 11, 1}), "Kyber-512-90s-r3"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 11, 2}), "Kyber-768-90s-r3"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 11, 3}), "Kyber-1024-90s-r3"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 1}), "SphincsPlus-shake-128s-r3.1"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 2}), "SphincsPlus-shake-128f-r3.1"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 3}), "SphincsPlus-shake-192s-r3.1"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 4}), "SphincsPlus-shake-192f-r3.1"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 5}), "SphincsPlus-shake-256s-r3.1"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 6}), "SphincsPlus-shake-256f-r3.1"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 1}), "SphincsPlus-sha2-128s-r3.1"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 2}), "SphincsPlus-sha2-128f-r3.1"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 3}), "SphincsPlus-sha2-192s-r3.1"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 4}), "SphincsPlus-sha2-192f-r3.1"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 5}), "SphincsPlus-sha2-256s-r3.1"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 6}), "SphincsPlus-sha2-256f-r3.1"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 1}), "SphincsPlus-haraka-128s-r3.1"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 2}), "SphincsPlus-haraka-128f-r3.1"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 3}), "SphincsPlus-haraka-192s-r3.1"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 4}), "SphincsPlus-haraka-192f-r3.1"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 5}), "SphincsPlus-haraka-256s-r3.1"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 6}), "SphincsPlus-haraka-256f-r3.1"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 13}), "HSS-LMS-Private-Key"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 14, 1}), "FrodoKEM-640-SHAKE"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 14, 2}), "FrodoKEM-976-SHAKE"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 14, 3}), "FrodoKEM-1344-SHAKE"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 15, 1}), "FrodoKEM-640-AES"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 15, 2}), "FrodoKEM-976-AES"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 15, 3}), "FrodoKEM-1344-AES"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 16, 1}), "eFrodoKEM-640-SHAKE"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 16, 2}), "eFrodoKEM-976-SHAKE"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 16, 3}), "eFrodoKEM-1344-SHAKE"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 17, 1}), "eFrodoKEM-640-AES"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 17, 2}), "eFrodoKEM-976-AES"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 17, 3}), "eFrodoKEM-1344-AES"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 18, 1}), "ClassicMcEliece_6688128pc"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 18, 2}), "ClassicMcEliece_6688128pcf"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 18, 3}), "ClassicMcEliece_6960119pc"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 18, 4}), "ClassicMcEliece_6960119pcf"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 18, 5}), "ClassicMcEliece_8192128pc"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 18, 6}), "ClassicMcEliece_8192128pcf"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 3}), "McEliece"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 5}), "XMSS-draft6"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 6, 1}), "GOST-34.10-2012-256/SHA-256"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 7, 1}), "Kyber-512-r3"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 7, 2}), "Kyber-768-r3"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 7, 3}), "Kyber-1024-r3"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 8}), "XMSS-draft12"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 9, 1}), "Dilithium-4x4-r3"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 9, 2}), "Dilithium-6x5-r3"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 1, 9, 3}), "Dilithium-8x7-r3"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 3, 1}), "Serpent/CBC"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 3, 101}), "Serpent/GCM"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 3, 102}), "Twofish/GCM"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 3, 2}), "Threefish-512/CBC"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 3, 2, 1}), "AES-128/OCB"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 3, 2, 2}), "AES-192/OCB"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 3, 2, 3}), "AES-256/OCB"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 3, 2, 4}), "Serpent/OCB"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 3, 2, 5}), "Twofish/OCB"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 3, 2, 6}), "Camellia-128/OCB"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 3, 2, 7}), "Camellia-192/OCB"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 3, 2, 8}), "Camellia-256/OCB"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 3, 3}), "Twofish/CBC"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 3, 4, 1}), "AES-128/SIV"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 3, 4, 2}), "AES-192/SIV"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 3, 4, 3}), "AES-256/SIV"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 3, 4, 4}), "Serpent/SIV"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 3, 4, 5}), "Twofish/SIV"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 3, 4, 6}), "Camellia-128/SIV"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 3, 4, 7}), "Camellia-192/SIV"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 3, 4, 8}), "Camellia-256/SIV"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 3, 4, 9}), "SM4/SIV"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 4, 1}), "numsp256d1"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 4, 2}), "numsp384d1"}, - {OID({1, 3, 6, 1, 4, 1, 25258, 4, 3}), "numsp512d1"}, - {OID({1, 3, 6, 1, 4, 1, 3029, 1, 2, 1}), "ElGamal"}, - {OID({1, 3, 6, 1, 4, 1, 3029, 1, 5, 1}), "OpenPGP.Curve25519"}, - {OID({1, 3, 6, 1, 4, 1, 311, 20, 2, 2}), "Microsoft SmartcardLogon"}, - {OID({1, 3, 6, 1, 4, 1, 311, 20, 2, 3}), "Microsoft UPN"}, - {OID({1, 3, 6, 1, 4, 1, 8301, 3, 1, 2, 9, 0, 38}), "secp521r1"}, - {OID({1, 3, 6, 1, 5, 5, 7, 1, 1}), "PKIX.AuthorityInformationAccess"}, - {OID({1, 3, 6, 1, 5, 5, 7, 1, 26}), "PKIX.TNAuthList"}, - {OID({1, 3, 6, 1, 5, 5, 7, 3, 1}), "PKIX.ServerAuth"}, - {OID({1, 3, 6, 1, 5, 5, 7, 3, 2}), "PKIX.ClientAuth"}, - {OID({1, 3, 6, 1, 5, 5, 7, 3, 3}), "PKIX.CodeSigning"}, - {OID({1, 3, 6, 1, 5, 5, 7, 3, 4}), "PKIX.EmailProtection"}, - {OID({1, 3, 6, 1, 5, 5, 7, 3, 5}), "PKIX.IPsecEndSystem"}, - {OID({1, 3, 6, 1, 5, 5, 7, 3, 6}), "PKIX.IPsecTunnel"}, - {OID({1, 3, 6, 1, 5, 5, 7, 3, 7}), "PKIX.IPsecUser"}, - {OID({1, 3, 6, 1, 5, 5, 7, 3, 8}), "PKIX.TimeStamping"}, - {OID({1, 3, 6, 1, 5, 5, 7, 3, 9}), "PKIX.OCSPSigning"}, - {OID({1, 3, 6, 1, 5, 5, 7, 48, 1}), "PKIX.OCSP"}, - {OID({1, 3, 6, 1, 5, 5, 7, 48, 1, 1}), "PKIX.OCSP.BasicResponse"}, - {OID({1, 3, 6, 1, 5, 5, 7, 48, 1, 5}), "PKIX.OCSP.NoCheck"}, - {OID({1, 3, 6, 1, 5, 5, 7, 48, 2}), "PKIX.CertificateAuthorityIssuers"}, - {OID({1, 3, 6, 1, 5, 5, 7, 8, 5}), "PKIX.XMPPAddr"}, - {OID({2, 16, 840, 1, 101, 3, 4, 1, 2}), "AES-128/CBC"}, - {OID({2, 16, 840, 1, 101, 3, 4, 1, 22}), "AES-192/CBC"}, - {OID({2, 16, 840, 1, 101, 3, 4, 1, 25}), "KeyWrap.AES-192"}, - {OID({2, 16, 840, 1, 101, 3, 4, 1, 26}), "AES-192/GCM"}, - {OID({2, 16, 840, 1, 101, 3, 4, 1, 27}), "AES-192/CCM"}, - {OID({2, 16, 840, 1, 101, 3, 4, 1, 42}), "AES-256/CBC"}, - {OID({2, 16, 840, 1, 101, 3, 4, 1, 45}), "KeyWrap.AES-256"}, - {OID({2, 16, 840, 1, 101, 3, 4, 1, 46}), "AES-256/GCM"}, - {OID({2, 16, 840, 1, 101, 3, 4, 1, 47}), "AES-256/CCM"}, - {OID({2, 16, 840, 1, 101, 3, 4, 1, 5}), "KeyWrap.AES-128"}, - {OID({2, 16, 840, 1, 101, 3, 4, 1, 6}), "AES-128/GCM"}, - {OID({2, 16, 840, 1, 101, 3, 4, 1, 7}), "AES-128/CCM"}, - {OID({2, 16, 840, 1, 101, 3, 4, 2, 1}), "SHA-256"}, - {OID({2, 16, 840, 1, 101, 3, 4, 2, 10}), "SHA-3(512)"}, - {OID({2, 16, 840, 1, 101, 3, 4, 2, 11}), "SHAKE-128"}, - {OID({2, 16, 840, 1, 101, 3, 4, 2, 12}), "SHAKE-256"}, - {OID({2, 16, 840, 1, 101, 3, 4, 2, 2}), "SHA-384"}, - {OID({2, 16, 840, 1, 101, 3, 4, 2, 3}), "SHA-512"}, - {OID({2, 16, 840, 1, 101, 3, 4, 2, 4}), "SHA-224"}, - {OID({2, 16, 840, 1, 101, 3, 4, 2, 6}), "SHA-512-256"}, - {OID({2, 16, 840, 1, 101, 3, 4, 2, 7}), "SHA-3(224)"}, - {OID({2, 16, 840, 1, 101, 3, 4, 2, 8}), "SHA-3(256)"}, - {OID({2, 16, 840, 1, 101, 3, 4, 2, 9}), "SHA-3(384)"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 1}), "DSA/SHA-224"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 10}), "ECDSA/SHA-3(256)"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 11}), "ECDSA/SHA-3(384)"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 12}), "ECDSA/SHA-3(512)"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 13}), "RSA/PKCS1v15(SHA-3(224))"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 14}), "RSA/PKCS1v15(SHA-3(256))"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 15}), "RSA/PKCS1v15(SHA-3(384))"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 16}), "RSA/PKCS1v15(SHA-3(512))"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 17}), "ML-DSA-4x4"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 18}), "ML-DSA-6x5"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 19}), "ML-DSA-8x7"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 2}), "DSA/SHA-256"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 20}), "SLH-DSA-SHA2-128s"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 21}), "SLH-DSA-SHA2-128f"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 22}), "SLH-DSA-SHA2-192s"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 23}), "SLH-DSA-SHA2-192f"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 24}), "SLH-DSA-SHA2-256s"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 25}), "SLH-DSA-SHA2-256f"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 26}), "SLH-DSA-SHAKE-128s"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 27}), "SLH-DSA-SHAKE-128f"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 28}), "SLH-DSA-SHAKE-192s"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 29}), "SLH-DSA-SHAKE-192f"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 3}), "DSA/SHA-384"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 30}), "SLH-DSA-SHAKE-256s"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 31}), "SLH-DSA-SHAKE-256f"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 4}), "DSA/SHA-512"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 5}), "DSA/SHA-3(224)"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 6}), "DSA/SHA-3(256)"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 7}), "DSA/SHA-3(384)"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 8}), "DSA/SHA-3(512)"}, - {OID({2, 16, 840, 1, 101, 3, 4, 3, 9}), "ECDSA/SHA-3(224)"}, - {OID({2, 16, 840, 1, 101, 3, 4, 4, 1}), "ML-KEM-512"}, - {OID({2, 16, 840, 1, 101, 3, 4, 4, 2}), "ML-KEM-768"}, - {OID({2, 16, 840, 1, 101, 3, 4, 4, 3}), "ML-KEM-1024"}, - {OID({2, 16, 840, 1, 113730, 1, 13}), "Certificate Comment"}, - {OID({2, 5, 29, 14}), "X509v3.SubjectKeyIdentifier"}, - {OID({2, 5, 29, 15}), "X509v3.KeyUsage"}, - {OID({2, 5, 29, 16}), "X509v3.PrivateKeyUsagePeriod"}, - {OID({2, 5, 29, 17}), "X509v3.SubjectAlternativeName"}, - {OID({2, 5, 29, 18}), "X509v3.IssuerAlternativeName"}, - {OID({2, 5, 29, 19}), "X509v3.BasicConstraints"}, - {OID({2, 5, 29, 20}), "X509v3.CRLNumber"}, - {OID({2, 5, 29, 21}), "X509v3.ReasonCode"}, - {OID({2, 5, 29, 23}), "X509v3.HoldInstructionCode"}, - {OID({2, 5, 29, 24}), "X509v3.InvalidityDate"}, - {OID({2, 5, 29, 28}), "X509v3.CRLIssuingDistributionPoint"}, - {OID({2, 5, 29, 30}), "X509v3.NameConstraints"}, - {OID({2, 5, 29, 31}), "X509v3.CRLDistributionPoints"}, - {OID({2, 5, 29, 32}), "X509v3.CertificatePolicies"}, - {OID({2, 5, 29, 32, 0}), "X509v3.AnyPolicy"}, - {OID({2, 5, 29, 35}), "X509v3.AuthorityKeyIdentifier"}, - {OID({2, 5, 29, 36}), "X509v3.PolicyConstraints"}, - {OID({2, 5, 29, 37}), "X509v3.ExtendedKeyUsage"}, - {OID({2, 5, 4, 10}), "X520.Organization"}, - {OID({2, 5, 4, 11}), "X520.OrganizationalUnit"}, - {OID({2, 5, 4, 12}), "X520.Title"}, - {OID({2, 5, 4, 3}), "X520.CommonName"}, - {OID({2, 5, 4, 4}), "X520.Surname"}, - {OID({2, 5, 4, 42}), "X520.GivenName"}, - {OID({2, 5, 4, 43}), "X520.Initials"}, - {OID({2, 5, 4, 44}), "X520.GenerationalQualifier"}, - {OID({2, 5, 4, 46}), "X520.DNQualifier"}, - {OID({2, 5, 4, 5}), "X520.SerialNumber"}, - {OID({2, 5, 4, 6}), "X520.Country"}, - {OID({2, 5, 4, 65}), "X520.Pseudonym"}, - {OID({2, 5, 4, 7}), "X520.Locality"}, - {OID({2, 5, 4, 8}), "X520.State"}, - {OID({2, 5, 4, 9}), "X520.StreetAddress"}, - {OID({2, 5, 8, 1, 1}), "RSA"}}; -} - -std::unordered_map OID_Map::load_str2oid_map() { - return std::unordered_map{ - - {"AES-128/CBC", OID({2, 16, 840, 1, 101, 3, 4, 1, 2})}, - {"AES-128/CCM", OID({2, 16, 840, 1, 101, 3, 4, 1, 7})}, - {"AES-128/GCM", OID({2, 16, 840, 1, 101, 3, 4, 1, 6})}, - {"AES-128/OCB", OID({1, 3, 6, 1, 4, 1, 25258, 3, 2, 1})}, - {"AES-128/SIV", OID({1, 3, 6, 1, 4, 1, 25258, 3, 4, 1})}, - {"AES-192/CBC", OID({2, 16, 840, 1, 101, 3, 4, 1, 22})}, - {"AES-192/CCM", OID({2, 16, 840, 1, 101, 3, 4, 1, 27})}, - {"AES-192/GCM", OID({2, 16, 840, 1, 101, 3, 4, 1, 26})}, - {"AES-192/OCB", OID({1, 3, 6, 1, 4, 1, 25258, 3, 2, 2})}, - {"AES-192/SIV", OID({1, 3, 6, 1, 4, 1, 25258, 3, 4, 2})}, - {"AES-256/CBC", OID({2, 16, 840, 1, 101, 3, 4, 1, 42})}, - {"AES-256/CCM", OID({2, 16, 840, 1, 101, 3, 4, 1, 47})}, - {"AES-256/GCM", OID({2, 16, 840, 1, 101, 3, 4, 1, 46})}, - {"AES-256/OCB", OID({1, 3, 6, 1, 4, 1, 25258, 3, 2, 3})}, - {"AES-256/SIV", OID({1, 3, 6, 1, 4, 1, 25258, 3, 4, 3})}, - {"CAST-128/CBC", OID({1, 2, 840, 113533, 7, 66, 10})}, - {"Camellia-128/CBC", OID({1, 2, 392, 200011, 61, 1, 1, 1, 2})}, - {"Camellia-128/GCM", OID({0, 3, 4401, 5, 3, 1, 9, 6})}, - {"Camellia-128/OCB", OID({1, 3, 6, 1, 4, 1, 25258, 3, 2, 6})}, - {"Camellia-128/SIV", OID({1, 3, 6, 1, 4, 1, 25258, 3, 4, 6})}, - {"Camellia-192/CBC", OID({1, 2, 392, 200011, 61, 1, 1, 1, 3})}, - {"Camellia-192/GCM", OID({0, 3, 4401, 5, 3, 1, 9, 26})}, - {"Camellia-192/OCB", OID({1, 3, 6, 1, 4, 1, 25258, 3, 2, 7})}, - {"Camellia-192/SIV", OID({1, 3, 6, 1, 4, 1, 25258, 3, 4, 7})}, - {"Camellia-256/CBC", OID({1, 2, 392, 200011, 61, 1, 1, 1, 4})}, - {"Camellia-256/GCM", OID({0, 3, 4401, 5, 3, 1, 9, 46})}, - {"Camellia-256/OCB", OID({1, 3, 6, 1, 4, 1, 25258, 3, 2, 8})}, - {"Camellia-256/SIV", OID({1, 3, 6, 1, 4, 1, 25258, 3, 4, 8})}, - {"Certificate Comment", OID({2, 16, 840, 1, 113730, 1, 13})}, - {"ChaCha20Poly1305", OID({1, 2, 840, 113549, 1, 9, 16, 3, 18})}, - {"ClassicMcEliece_348864", OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 1})}, - {"ClassicMcEliece_348864f", OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 2})}, - {"ClassicMcEliece_460896", OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 3})}, - {"ClassicMcEliece_460896f", OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 4})}, - {"ClassicMcEliece_6688128", OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 5})}, - {"ClassicMcEliece_6688128f", OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 6})}, - {"ClassicMcEliece_6688128pc", OID({1, 3, 6, 1, 4, 1, 25258, 1, 18, 1})}, - {"ClassicMcEliece_6688128pcf", OID({1, 3, 6, 1, 4, 1, 25258, 1, 18, 2})}, - {"ClassicMcEliece_6960119", OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 7})}, - {"ClassicMcEliece_6960119f", OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 8})}, - {"ClassicMcEliece_6960119pc", OID({1, 3, 6, 1, 4, 1, 25258, 1, 18, 3})}, - {"ClassicMcEliece_6960119pcf", OID({1, 3, 6, 1, 4, 1, 25258, 1, 18, 4})}, - {"ClassicMcEliece_8192128", OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 9})}, - {"ClassicMcEliece_8192128f", OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 10})}, - {"ClassicMcEliece_8192128pc", OID({1, 3, 6, 1, 4, 1, 25258, 1, 18, 5})}, - {"ClassicMcEliece_8192128pcf", OID({1, 3, 6, 1, 4, 1, 25258, 1, 18, 6})}, - {"Compression.Zlib", OID({1, 2, 840, 113549, 1, 9, 16, 3, 8})}, - {"Curve25519", OID({1, 3, 101, 110})}, - {"DES/CBC", OID({1, 3, 14, 3, 2, 7})}, - {"DH", OID({1, 2, 840, 10046, 2, 1})}, - {"DSA", OID({1, 2, 840, 10040, 4, 1})}, - {"DSA/SHA-1", OID({1, 2, 840, 10040, 4, 3})}, - {"DSA/SHA-224", OID({2, 16, 840, 1, 101, 3, 4, 3, 1})}, - {"DSA/SHA-256", OID({2, 16, 840, 1, 101, 3, 4, 3, 2})}, - {"DSA/SHA-3(224)", OID({2, 16, 840, 1, 101, 3, 4, 3, 5})}, - {"DSA/SHA-3(256)", OID({2, 16, 840, 1, 101, 3, 4, 3, 6})}, - {"DSA/SHA-3(384)", OID({2, 16, 840, 1, 101, 3, 4, 3, 7})}, - {"DSA/SHA-3(512)", OID({2, 16, 840, 1, 101, 3, 4, 3, 8})}, - {"DSA/SHA-384", OID({2, 16, 840, 1, 101, 3, 4, 3, 3})}, - {"DSA/SHA-512", OID({2, 16, 840, 1, 101, 3, 4, 3, 4})}, - {"Dilithium-4x4-AES-r3", OID({1, 3, 6, 1, 4, 1, 25258, 1, 10, 1})}, - {"Dilithium-4x4-r3", OID({1, 3, 6, 1, 4, 1, 25258, 1, 9, 1})}, - {"Dilithium-6x5-AES-r3", OID({1, 3, 6, 1, 4, 1, 25258, 1, 10, 2})}, - {"Dilithium-6x5-r3", OID({1, 3, 6, 1, 4, 1, 25258, 1, 9, 2})}, - {"Dilithium-8x7-AES-r3", OID({1, 3, 6, 1, 4, 1, 25258, 1, 10, 3})}, - {"Dilithium-8x7-r3", OID({1, 3, 6, 1, 4, 1, 25258, 1, 9, 3})}, - {"ECDH", OID({1, 3, 132, 1, 12})}, - {"ECDSA", OID({1, 2, 840, 10045, 2, 1})}, - {"ECDSA/SHA-1", OID({1, 2, 840, 10045, 4, 1})}, - {"ECDSA/SHA-224", OID({1, 2, 840, 10045, 4, 3, 1})}, - {"ECDSA/SHA-256", OID({1, 2, 840, 10045, 4, 3, 2})}, - {"ECDSA/SHA-3(224)", OID({2, 16, 840, 1, 101, 3, 4, 3, 9})}, - {"ECDSA/SHA-3(256)", OID({2, 16, 840, 1, 101, 3, 4, 3, 10})}, - {"ECDSA/SHA-3(384)", OID({2, 16, 840, 1, 101, 3, 4, 3, 11})}, - {"ECDSA/SHA-3(512)", OID({2, 16, 840, 1, 101, 3, 4, 3, 12})}, - {"ECDSA/SHA-384", OID({1, 2, 840, 10045, 4, 3, 3})}, - {"ECDSA/SHA-512", OID({1, 2, 840, 10045, 4, 3, 4})}, - {"ECGDSA", OID({1, 3, 36, 3, 3, 2, 5, 2, 1})}, - {"ECGDSA/RIPEMD-160", OID({1, 3, 36, 3, 3, 2, 5, 4, 1})}, - {"ECGDSA/SHA-1", OID({1, 3, 36, 3, 3, 2, 5, 4, 2})}, - {"ECGDSA/SHA-224", OID({1, 3, 36, 3, 3, 2, 5, 4, 3})}, - {"ECGDSA/SHA-256", OID({1, 3, 36, 3, 3, 2, 5, 4, 4})}, - {"ECGDSA/SHA-384", OID({1, 3, 36, 3, 3, 2, 5, 4, 5})}, - {"ECGDSA/SHA-512", OID({1, 3, 36, 3, 3, 2, 5, 4, 6})}, - {"ECKCDSA", OID({1, 0, 14888, 3, 0, 5})}, - {"ECKCDSA/SHA-1", OID({1, 2, 410, 200004, 1, 100, 4, 3})}, - {"ECKCDSA/SHA-224", OID({1, 2, 410, 200004, 1, 100, 4, 4})}, - {"ECKCDSA/SHA-256", OID({1, 2, 410, 200004, 1, 100, 4, 5})}, - {"Ed25519", OID({1, 3, 101, 112})}, - {"Ed448", OID({1, 3, 101, 113})}, - {"ElGamal", OID({1, 3, 6, 1, 4, 1, 3029, 1, 2, 1})}, - {"FrodoKEM-1344-AES", OID({1, 3, 6, 1, 4, 1, 25258, 1, 15, 3})}, - {"FrodoKEM-1344-SHAKE", OID({1, 3, 6, 1, 4, 1, 25258, 1, 14, 3})}, - {"FrodoKEM-640-AES", OID({1, 3, 6, 1, 4, 1, 25258, 1, 15, 1})}, - {"FrodoKEM-640-SHAKE", OID({1, 3, 6, 1, 4, 1, 25258, 1, 14, 1})}, - {"FrodoKEM-976-AES", OID({1, 3, 6, 1, 4, 1, 25258, 1, 15, 2})}, - {"FrodoKEM-976-SHAKE", OID({1, 3, 6, 1, 4, 1, 25258, 1, 14, 2})}, - {"GOST-34.10", OID({1, 2, 643, 2, 2, 19})}, - {"GOST-34.10-2012-256", OID({1, 2, 643, 7, 1, 1, 1, 1})}, - {"GOST-34.10-2012-256/SHA-256", OID({1, 3, 6, 1, 4, 1, 25258, 1, 6, 1})}, - {"GOST-34.10-2012-256/Streebog-256", OID({1, 2, 643, 7, 1, 1, 3, 2})}, - {"GOST-34.10-2012-512", OID({1, 2, 643, 7, 1, 1, 1, 2})}, - {"GOST-34.10-2012-512/Streebog-512", OID({1, 2, 643, 7, 1, 1, 3, 3})}, - {"GOST-34.10/GOST-R-34.11-94", OID({1, 2, 643, 2, 2, 3})}, - {"GOST.INN", OID({1, 2, 643, 3, 131, 1, 1})}, - {"GOST.IssuerSigningTool", OID({1, 2, 643, 100, 112})}, - {"GOST.OGRN", OID({1, 2, 643, 100, 1})}, - {"GOST.SubjectSigningTool", OID({1, 2, 643, 100, 111})}, - {"HMAC(SHA-1)", OID({1, 2, 840, 113549, 2, 7})}, - {"HMAC(SHA-224)", OID({1, 2, 840, 113549, 2, 8})}, - {"HMAC(SHA-256)", OID({1, 2, 840, 113549, 2, 9})}, - {"HMAC(SHA-384)", OID({1, 2, 840, 113549, 2, 10})}, - {"HMAC(SHA-512)", OID({1, 2, 840, 113549, 2, 11})}, - {"HMAC(SHA-512-256)", OID({1, 2, 840, 113549, 2, 13})}, - {"HSS-LMS", OID({1, 2, 840, 113549, 1, 9, 16, 3, 17})}, - {"HSS-LMS-Private-Key", OID({1, 3, 6, 1, 4, 1, 25258, 1, 13})}, - {"KeyWrap.AES-128", OID({2, 16, 840, 1, 101, 3, 4, 1, 5})}, - {"KeyWrap.AES-192", OID({2, 16, 840, 1, 101, 3, 4, 1, 25})}, - {"KeyWrap.AES-256", OID({2, 16, 840, 1, 101, 3, 4, 1, 45})}, - {"KeyWrap.CAST-128", OID({1, 2, 840, 113533, 7, 66, 15})}, - {"KeyWrap.TripleDES", OID({1, 2, 840, 113549, 1, 9, 16, 3, 6})}, - {"Kyber-1024-90s-r3", OID({1, 3, 6, 1, 4, 1, 25258, 1, 11, 3})}, - {"Kyber-1024-r3", OID({1, 3, 6, 1, 4, 1, 25258, 1, 7, 3})}, - {"Kyber-512-90s-r3", OID({1, 3, 6, 1, 4, 1, 25258, 1, 11, 1})}, - {"Kyber-512-r3", OID({1, 3, 6, 1, 4, 1, 25258, 1, 7, 1})}, - {"Kyber-768-90s-r3", OID({1, 3, 6, 1, 4, 1, 25258, 1, 11, 2})}, - {"Kyber-768-r3", OID({1, 3, 6, 1, 4, 1, 25258, 1, 7, 2})}, - {"MD5", OID({1, 2, 840, 113549, 2, 5})}, - {"MGF1", OID({1, 2, 840, 113549, 1, 1, 8})}, - {"ML-DSA-4x4", OID({2, 16, 840, 1, 101, 3, 4, 3, 17})}, - {"ML-DSA-6x5", OID({2, 16, 840, 1, 101, 3, 4, 3, 18})}, - {"ML-DSA-8x7", OID({2, 16, 840, 1, 101, 3, 4, 3, 19})}, - {"ML-KEM-1024", OID({2, 16, 840, 1, 101, 3, 4, 4, 3})}, - {"ML-KEM-512", OID({2, 16, 840, 1, 101, 3, 4, 4, 1})}, - {"ML-KEM-768", OID({2, 16, 840, 1, 101, 3, 4, 4, 2})}, - {"McEliece", OID({1, 3, 6, 1, 4, 1, 25258, 1, 3})}, - {"Microsoft SmartcardLogon", OID({1, 3, 6, 1, 4, 1, 311, 20, 2, 2})}, - {"Microsoft UPN", OID({1, 3, 6, 1, 4, 1, 311, 20, 2, 3})}, - {"OpenPGP.Curve25519", OID({1, 3, 6, 1, 4, 1, 3029, 1, 5, 1})}, - {"OpenPGP.Ed25519", OID({1, 3, 6, 1, 4, 1, 11591, 15, 1})}, - {"PBE-PKCS5v20", OID({1, 2, 840, 113549, 1, 5, 13})}, - {"PBES2", OID({1, 2, 840, 113549, 1, 5, 13})}, - {"PKCS5.PBKDF2", OID({1, 2, 840, 113549, 1, 5, 12})}, - {"PKCS9.ChallengePassword", OID({1, 2, 840, 113549, 1, 9, 7})}, - {"PKCS9.ContentType", OID({1, 2, 840, 113549, 1, 9, 3})}, - {"PKCS9.EmailAddress", OID({1, 2, 840, 113549, 1, 9, 1})}, - {"PKCS9.ExtensionRequest", OID({1, 2, 840, 113549, 1, 9, 14})}, - {"PKCS9.MessageDigest", OID({1, 2, 840, 113549, 1, 9, 4})}, - {"PKCS9.UnstructuredName", OID({1, 2, 840, 113549, 1, 9, 2})}, - {"PKIX.AuthorityInformationAccess", OID({1, 3, 6, 1, 5, 5, 7, 1, 1})}, - {"PKIX.CertificateAuthorityIssuers", OID({1, 3, 6, 1, 5, 5, 7, 48, 2})}, - {"PKIX.ClientAuth", OID({1, 3, 6, 1, 5, 5, 7, 3, 2})}, - {"PKIX.CodeSigning", OID({1, 3, 6, 1, 5, 5, 7, 3, 3})}, - {"PKIX.EmailProtection", OID({1, 3, 6, 1, 5, 5, 7, 3, 4})}, - {"PKIX.IPsecEndSystem", OID({1, 3, 6, 1, 5, 5, 7, 3, 5})}, - {"PKIX.IPsecTunnel", OID({1, 3, 6, 1, 5, 5, 7, 3, 6})}, - {"PKIX.IPsecUser", OID({1, 3, 6, 1, 5, 5, 7, 3, 7})}, - {"PKIX.OCSP", OID({1, 3, 6, 1, 5, 5, 7, 48, 1})}, - {"PKIX.OCSP.BasicResponse", OID({1, 3, 6, 1, 5, 5, 7, 48, 1, 1})}, - {"PKIX.OCSP.NoCheck", OID({1, 3, 6, 1, 5, 5, 7, 48, 1, 5})}, - {"PKIX.OCSPSigning", OID({1, 3, 6, 1, 5, 5, 7, 3, 9})}, - {"PKIX.ServerAuth", OID({1, 3, 6, 1, 5, 5, 7, 3, 1})}, - {"PKIX.TNAuthList", OID({1, 3, 6, 1, 5, 5, 7, 1, 26})}, - {"PKIX.TimeStamping", OID({1, 3, 6, 1, 5, 5, 7, 3, 8})}, - {"PKIX.XMPPAddr", OID({1, 3, 6, 1, 5, 5, 7, 8, 5})}, - {"RIPEMD-160", OID({1, 3, 36, 3, 2, 1})}, - {"RSA", OID({1, 2, 840, 113549, 1, 1, 1})}, - {"RSA/EMSA3(MD2)", OID({1, 2, 840, 113549, 1, 1, 2})}, - {"RSA/EMSA3(MD5)", OID({1, 2, 840, 113549, 1, 1, 4})}, - {"RSA/EMSA3(RIPEMD-160)", OID({1, 3, 36, 3, 3, 1, 2})}, - {"RSA/EMSA3(SHA-1)", OID({1, 2, 840, 113549, 1, 1, 5})}, - {"RSA/EMSA3(SHA-224)", OID({1, 2, 840, 113549, 1, 1, 14})}, - {"RSA/EMSA3(SHA-256)", OID({1, 2, 840, 113549, 1, 1, 11})}, - {"RSA/EMSA3(SHA-3(224))", OID({2, 16, 840, 1, 101, 3, 4, 3, 13})}, - {"RSA/EMSA3(SHA-3(256))", OID({2, 16, 840, 1, 101, 3, 4, 3, 14})}, - {"RSA/EMSA3(SHA-3(384))", OID({2, 16, 840, 1, 101, 3, 4, 3, 15})}, - {"RSA/EMSA3(SHA-3(512))", OID({2, 16, 840, 1, 101, 3, 4, 3, 16})}, - {"RSA/EMSA3(SHA-384)", OID({1, 2, 840, 113549, 1, 1, 12})}, - {"RSA/EMSA3(SHA-512)", OID({1, 2, 840, 113549, 1, 1, 13})}, - {"RSA/EMSA3(SHA-512-256)", OID({1, 2, 840, 113549, 1, 1, 16})}, - {"RSA/EMSA3(SM3)", OID({1, 2, 156, 10197, 1, 504})}, - {"RSA/EMSA4", OID({1, 2, 840, 113549, 1, 1, 10})}, - {"RSA/OAEP", OID({1, 2, 840, 113549, 1, 1, 7})}, - {"RSA/PKCS1v15(MD2)", OID({1, 2, 840, 113549, 1, 1, 2})}, - {"RSA/PKCS1v15(MD5)", OID({1, 2, 840, 113549, 1, 1, 4})}, - {"RSA/PKCS1v15(RIPEMD-160)", OID({1, 3, 36, 3, 3, 1, 2})}, - {"RSA/PKCS1v15(SHA-1)", OID({1, 2, 840, 113549, 1, 1, 5})}, - {"RSA/PKCS1v15(SHA-224)", OID({1, 2, 840, 113549, 1, 1, 14})}, - {"RSA/PKCS1v15(SHA-256)", OID({1, 2, 840, 113549, 1, 1, 11})}, - {"RSA/PKCS1v15(SHA-3(224))", OID({2, 16, 840, 1, 101, 3, 4, 3, 13})}, - {"RSA/PKCS1v15(SHA-3(256))", OID({2, 16, 840, 1, 101, 3, 4, 3, 14})}, - {"RSA/PKCS1v15(SHA-3(384))", OID({2, 16, 840, 1, 101, 3, 4, 3, 15})}, - {"RSA/PKCS1v15(SHA-3(512))", OID({2, 16, 840, 1, 101, 3, 4, 3, 16})}, - {"RSA/PKCS1v15(SHA-384)", OID({1, 2, 840, 113549, 1, 1, 12})}, - {"RSA/PKCS1v15(SHA-512)", OID({1, 2, 840, 113549, 1, 1, 13})}, - {"RSA/PKCS1v15(SHA-512-256)", OID({1, 2, 840, 113549, 1, 1, 16})}, - {"RSA/PKCS1v15(SM3)", OID({1, 2, 156, 10197, 1, 504})}, - {"RSA/PSS", OID({1, 2, 840, 113549, 1, 1, 10})}, - {"SEED/CBC", OID({1, 2, 410, 200004, 1, 4})}, - {"SHA-1", OID({1, 3, 14, 3, 2, 26})}, - {"SHA-224", OID({2, 16, 840, 1, 101, 3, 4, 2, 4})}, - {"SHA-256", OID({2, 16, 840, 1, 101, 3, 4, 2, 1})}, - {"SHA-3(224)", OID({2, 16, 840, 1, 101, 3, 4, 2, 7})}, - {"SHA-3(256)", OID({2, 16, 840, 1, 101, 3, 4, 2, 8})}, - {"SHA-3(384)", OID({2, 16, 840, 1, 101, 3, 4, 2, 9})}, - {"SHA-3(512)", OID({2, 16, 840, 1, 101, 3, 4, 2, 10})}, - {"SHA-384", OID({2, 16, 840, 1, 101, 3, 4, 2, 2})}, - {"SHA-512", OID({2, 16, 840, 1, 101, 3, 4, 2, 3})}, - {"SHA-512-256", OID({2, 16, 840, 1, 101, 3, 4, 2, 6})}, - {"SHAKE-128", OID({2, 16, 840, 1, 101, 3, 4, 2, 11})}, - {"SHAKE-256", OID({2, 16, 840, 1, 101, 3, 4, 2, 12})}, - {"SLH-DSA-SHA2-128f", OID({2, 16, 840, 1, 101, 3, 4, 3, 21})}, - {"SLH-DSA-SHA2-128s", OID({2, 16, 840, 1, 101, 3, 4, 3, 20})}, - {"SLH-DSA-SHA2-192f", OID({2, 16, 840, 1, 101, 3, 4, 3, 23})}, - {"SLH-DSA-SHA2-192s", OID({2, 16, 840, 1, 101, 3, 4, 3, 22})}, - {"SLH-DSA-SHA2-256f", OID({2, 16, 840, 1, 101, 3, 4, 3, 25})}, - {"SLH-DSA-SHA2-256s", OID({2, 16, 840, 1, 101, 3, 4, 3, 24})}, - {"SLH-DSA-SHAKE-128f", OID({2, 16, 840, 1, 101, 3, 4, 3, 27})}, - {"SLH-DSA-SHAKE-128s", OID({2, 16, 840, 1, 101, 3, 4, 3, 26})}, - {"SLH-DSA-SHAKE-192f", OID({2, 16, 840, 1, 101, 3, 4, 3, 29})}, - {"SLH-DSA-SHAKE-192s", OID({2, 16, 840, 1, 101, 3, 4, 3, 28})}, - {"SLH-DSA-SHAKE-256f", OID({2, 16, 840, 1, 101, 3, 4, 3, 31})}, - {"SLH-DSA-SHAKE-256s", OID({2, 16, 840, 1, 101, 3, 4, 3, 30})}, - {"SM2", OID({1, 2, 156, 10197, 1, 301, 1})}, - {"SM2_Enc", OID({1, 2, 156, 10197, 1, 301, 3})}, - {"SM2_Kex", OID({1, 2, 156, 10197, 1, 301, 2})}, - {"SM2_Sig", OID({1, 2, 156, 10197, 1, 301, 1})}, - {"SM2_Sig/SM3", OID({1, 2, 156, 10197, 1, 501})}, - {"SM3", OID({1, 2, 156, 10197, 1, 401})}, - {"SM4/CBC", OID({1, 2, 156, 10197, 1, 104, 2})}, - {"SM4/GCM", OID({1, 2, 156, 10197, 1, 104, 8})}, - {"SM4/OCB", OID({1, 2, 156, 10197, 1, 104, 100})}, - {"SM4/SIV", OID({1, 3, 6, 1, 4, 1, 25258, 3, 4, 9})}, - {"Scrypt", OID({1, 3, 6, 1, 4, 1, 11591, 4, 11})}, - {"Serpent/CBC", OID({1, 3, 6, 1, 4, 1, 25258, 3, 1})}, - {"Serpent/GCM", OID({1, 3, 6, 1, 4, 1, 25258, 3, 101})}, - {"Serpent/OCB", OID({1, 3, 6, 1, 4, 1, 25258, 3, 2, 4})}, - {"Serpent/SIV", OID({1, 3, 6, 1, 4, 1, 25258, 3, 4, 4})}, - {"SphincsPlus-haraka-128f-r3.1", OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 2})}, - {"SphincsPlus-haraka-128s-r3.1", OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 1})}, - {"SphincsPlus-haraka-192f-r3.1", OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 4})}, - {"SphincsPlus-haraka-192s-r3.1", OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 3})}, - {"SphincsPlus-haraka-256f-r3.1", OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 6})}, - {"SphincsPlus-haraka-256s-r3.1", OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 5})}, - {"SphincsPlus-sha2-128f-r3.1", OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 2})}, - {"SphincsPlus-sha2-128s-r3.1", OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 1})}, - {"SphincsPlus-sha2-192f-r3.1", OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 4})}, - {"SphincsPlus-sha2-192s-r3.1", OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 3})}, - {"SphincsPlus-sha2-256f-r3.1", OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 6})}, - {"SphincsPlus-sha2-256s-r3.1", OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 5})}, - {"SphincsPlus-shake-128f-r3.1", OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 2})}, - {"SphincsPlus-shake-128s-r3.1", OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 1})}, - {"SphincsPlus-shake-192f-r3.1", OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 4})}, - {"SphincsPlus-shake-192s-r3.1", OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 3})}, - {"SphincsPlus-shake-256f-r3.1", OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 6})}, - {"SphincsPlus-shake-256s-r3.1", OID({1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 5})}, - {"Streebog-256", OID({1, 2, 643, 7, 1, 1, 2, 2})}, - {"Streebog-512", OID({1, 2, 643, 7, 1, 1, 2, 3})}, - {"Threefish-512/CBC", OID({1, 3, 6, 1, 4, 1, 25258, 3, 2})}, - {"TripleDES/CBC", OID({1, 2, 840, 113549, 3, 7})}, - {"Twofish/CBC", OID({1, 3, 6, 1, 4, 1, 25258, 3, 3})}, - {"Twofish/GCM", OID({1, 3, 6, 1, 4, 1, 25258, 3, 102})}, - {"Twofish/OCB", OID({1, 3, 6, 1, 4, 1, 25258, 3, 2, 5})}, - {"Twofish/SIV", OID({1, 3, 6, 1, 4, 1, 25258, 3, 4, 5})}, - {"X25519", OID({1, 3, 101, 110})}, - {"X448", OID({1, 3, 101, 111})}, - {"X509v3.AnyPolicy", OID({2, 5, 29, 32, 0})}, - {"X509v3.AuthorityKeyIdentifier", OID({2, 5, 29, 35})}, - {"X509v3.BasicConstraints", OID({2, 5, 29, 19})}, - {"X509v3.CRLDistributionPoints", OID({2, 5, 29, 31})}, - {"X509v3.CRLIssuingDistributionPoint", OID({2, 5, 29, 28})}, - {"X509v3.CRLNumber", OID({2, 5, 29, 20})}, - {"X509v3.CertificatePolicies", OID({2, 5, 29, 32})}, - {"X509v3.ExtendedKeyUsage", OID({2, 5, 29, 37})}, - {"X509v3.HoldInstructionCode", OID({2, 5, 29, 23})}, - {"X509v3.InvalidityDate", OID({2, 5, 29, 24})}, - {"X509v3.IssuerAlternativeName", OID({2, 5, 29, 18})}, - {"X509v3.KeyUsage", OID({2, 5, 29, 15})}, - {"X509v3.NameConstraints", OID({2, 5, 29, 30})}, - {"X509v3.PolicyConstraints", OID({2, 5, 29, 36})}, - {"X509v3.PrivateKeyUsagePeriod", OID({2, 5, 29, 16})}, - {"X509v3.ReasonCode", OID({2, 5, 29, 21})}, - {"X509v3.SubjectAlternativeName", OID({2, 5, 29, 17})}, - {"X509v3.SubjectKeyIdentifier", OID({2, 5, 29, 14})}, - {"X520.CommonName", OID({2, 5, 4, 3})}, - {"X520.Country", OID({2, 5, 4, 6})}, - {"X520.DNQualifier", OID({2, 5, 4, 46})}, - {"X520.GenerationalQualifier", OID({2, 5, 4, 44})}, - {"X520.GivenName", OID({2, 5, 4, 42})}, - {"X520.Initials", OID({2, 5, 4, 43})}, - {"X520.Locality", OID({2, 5, 4, 7})}, - {"X520.Organization", OID({2, 5, 4, 10})}, - {"X520.OrganizationalUnit", OID({2, 5, 4, 11})}, - {"X520.Pseudonym", OID({2, 5, 4, 65})}, - {"X520.SerialNumber", OID({2, 5, 4, 5})}, - {"X520.State", OID({2, 5, 4, 8})}, - {"X520.StreetAddress", OID({2, 5, 4, 9})}, - {"X520.Surname", OID({2, 5, 4, 4})}, - {"X520.Title", OID({2, 5, 4, 12})}, - {"XMSS", OID({0, 4, 0, 127, 0, 15, 1, 1, 13, 0})}, - {"XMSS-draft12", OID({1, 3, 6, 1, 4, 1, 25258, 1, 8})}, - {"XMSS-draft6", OID({1, 3, 6, 1, 4, 1, 25258, 1, 5})}, - {"brainpool160r1", OID({1, 3, 36, 3, 3, 2, 8, 1, 1, 1})}, - {"brainpool192r1", OID({1, 3, 36, 3, 3, 2, 8, 1, 1, 3})}, - {"brainpool224r1", OID({1, 3, 36, 3, 3, 2, 8, 1, 1, 5})}, - {"brainpool256r1", OID({1, 3, 36, 3, 3, 2, 8, 1, 1, 7})}, - {"brainpool320r1", OID({1, 3, 36, 3, 3, 2, 8, 1, 1, 9})}, - {"brainpool384r1", OID({1, 3, 36, 3, 3, 2, 8, 1, 1, 11})}, - {"brainpool512r1", OID({1, 3, 36, 3, 3, 2, 8, 1, 1, 13})}, - {"eFrodoKEM-1344-AES", OID({1, 3, 6, 1, 4, 1, 25258, 1, 17, 3})}, - {"eFrodoKEM-1344-SHAKE", OID({1, 3, 6, 1, 4, 1, 25258, 1, 16, 3})}, - {"eFrodoKEM-640-AES", OID({1, 3, 6, 1, 4, 1, 25258, 1, 17, 1})}, - {"eFrodoKEM-640-SHAKE", OID({1, 3, 6, 1, 4, 1, 25258, 1, 16, 1})}, - {"eFrodoKEM-976-AES", OID({1, 3, 6, 1, 4, 1, 25258, 1, 17, 2})}, - {"eFrodoKEM-976-SHAKE", OID({1, 3, 6, 1, 4, 1, 25258, 1, 16, 2})}, - {"frp256v1", OID({1, 2, 250, 1, 223, 101, 256, 1})}, - {"gost_256A", OID({1, 2, 643, 7, 1, 2, 1, 1, 1})}, - {"gost_256B", OID({1, 2, 643, 7, 1, 2, 1, 1, 2})}, - {"gost_512A", OID({1, 2, 643, 7, 1, 2, 1, 2, 1})}, - {"gost_512B", OID({1, 2, 643, 7, 1, 2, 1, 2, 2})}, - {"numsp256d1", OID({1, 3, 6, 1, 4, 1, 25258, 4, 1})}, - {"numsp384d1", OID({1, 3, 6, 1, 4, 1, 25258, 4, 2})}, - {"numsp512d1", OID({1, 3, 6, 1, 4, 1, 25258, 4, 3})}, - {"secp160k1", OID({1, 3, 132, 0, 9})}, - {"secp160r1", OID({1, 3, 132, 0, 8})}, - {"secp160r2", OID({1, 3, 132, 0, 30})}, - {"secp192k1", OID({1, 3, 132, 0, 31})}, - {"secp192r1", OID({1, 2, 840, 10045, 3, 1, 1})}, - {"secp224k1", OID({1, 3, 132, 0, 32})}, - {"secp224r1", OID({1, 3, 132, 0, 33})}, - {"secp256k1", OID({1, 3, 132, 0, 10})}, - {"secp256r1", OID({1, 2, 840, 10045, 3, 1, 7})}, - {"secp384r1", OID({1, 3, 132, 0, 34})}, - {"secp521r1", OID({1, 3, 132, 0, 35})}, - {"sm2p256v1", OID({1, 2, 156, 10197, 1, 301})}, - {"x962_p192v2", OID({1, 2, 840, 10045, 3, 1, 2})}, - {"x962_p192v3", OID({1, 2, 840, 10045, 3, 1, 3})}, - {"x962_p239v1", OID({1, 2, 840, 10045, 3, 1, 4})}, - {"x962_p239v2", OID({1, 2, 840, 10045, 3, 1, 5})}, - {"x962_p239v3", OID({1, 2, 840, 10045, 3, 1, 6})}}; -} - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/asn1/pss_params.cpp botan3-3.12.0+dfsg/src/lib/asn1/pss_params.cpp --- botan3-3.7.1+dfsg/src/lib/asn1/pss_params.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/asn1/pss_params.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,7 @@ #include +#include #include #include #include @@ -15,15 +16,15 @@ namespace Botan { //static -PSS_Params PSS_Params::from_emsa_name(std::string_view emsa_name) { - SCAN_Name scanner(emsa_name); +PSS_Params PSS_Params::from_padding_name(std::string_view padding_name) { + const SCAN_Name scanner(padding_name); if((scanner.algo_name() != "PSS" && scanner.algo_name() != "PSS_Raw") || scanner.arg_count() != 3) { - throw Invalid_Argument(fmt("PSS_Params::from_emsa_name unexpected param '{}'", emsa_name)); + throw Invalid_Argument(fmt("PSS_Params::from_padding_name unexpected param '{}'", padding_name)); } const std::string hash_fn = scanner.arg(0); - BOTAN_ASSERT_NOMSG(scanner.arg(1) == "MGF1"); + BOTAN_ARG_CHECK(scanner.arg(1) == "MGF1", "PSS requires MGF1"); const size_t salt_len = scanner.arg_as_integer(2); return PSS_Params(hash_fn, salt_len); } @@ -32,11 +33,13 @@ m_hash(hash_fn, AlgorithmIdentifier::USE_NULL_PARAM), m_mgf("MGF1", m_hash.BER_encode()), m_mgf_hash(m_hash), - m_salt_len(salt_len) {} + m_salt_len(salt_len), + m_trailer_field(1) {} -PSS_Params::PSS_Params(std::span der) { - BER_Decoder decoder(der); +PSS_Params::PSS_Params(std::span der) : m_salt_len(0), m_trailer_field(1) { + BER_Decoder decoder(der, BER_Decoder::Limits::DER()); this->decode_from(decoder); + decoder.verify_end(); } std::vector PSS_Params::serialize() const { @@ -46,8 +49,6 @@ } void PSS_Params::encode_into(DER_Encoder& to) const { - const size_t trailer_field = 1; - to.start_sequence() .start_context_specific(0) .encode(m_hash) @@ -58,9 +59,6 @@ .start_context_specific(2) .encode(m_salt_len) .end_cons() - .start_context_specific(3) - .encode(trailer_field) - .end_cons() .end_cons(); } @@ -77,7 +75,7 @@ .decode_optional(m_trailer_field, ASN1_Type(3), ASN1_Class::ExplicitContextSpecific, default_trailer) .end_cons(); - BER_Decoder(m_mgf.parameters()).decode(m_mgf_hash); + BER_Decoder(m_mgf.parameters(), from.limits()).decode(m_mgf_hash); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/asn1/pss_params.h botan3-3.12.0+dfsg/src/lib/asn1/pss_params.h --- botan3-3.7.1+dfsg/src/lib/asn1/pss_params.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/asn1/pss_params.h 2026-05-07 01:38:28.000000000 +0000 @@ -25,17 +25,27 @@ public: /** * Note that the only valid strings you can pass to this function - * are values returned by EMSA::name() and these may change in a - * minor release. + * are values returned by SignaturePaddingScheme::name() and + * these may change in a minor release. */ - static PSS_Params from_emsa_name(std::string_view emsa_name); + static PSS_Params from_padding_name(std::string_view padding_name); + + /** + * Note that the only valid strings you can pass to this function + * are values returned by SignaturePaddingScheme::name() and + * these may change in a minor release. + */ + BOTAN_DEPRECATED("Use PSS_Params::from_padding_name") + static PSS_Params from_emsa_name(std::string_view padding_name) { + return PSS_Params::from_padding_name(padding_name); + } PSS_Params(std::string_view hash_fn, size_t salt_len); /** * Decode an encoded RSASSA-PSS-params */ - PSS_Params(std::span der); + BOTAN_FUTURE_EXPLICIT PSS_Params(std::span der); const AlgorithmIdentifier& hash_algid() const { return m_hash; } diff -Nru botan3-3.7.1+dfsg/src/lib/asn1/static_oids.cpp botan3-3.12.0+dfsg/src/lib/asn1/static_oids.cpp --- botan3-3.7.1+dfsg/src/lib/asn1/static_oids.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/asn1/static_oids.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,1459 @@ +/* +* This file was automatically generated by ./src/scripts/dev_tools/gen_oids.py on 2026-04-24 +* All manual changes will be lost. Edit the script instead. +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan { + +namespace { + +// The hash can collide so we must verify the actual value matches before returning +std::optional if_match(const OID& oid, std::initializer_list val, std::string_view name) { + if(oid.matches(val)) { + return name; + } else { + return {}; + } +} + +std::optional if_match(std::string_view req, std::string_view actual, std::initializer_list oid) { + if(req == actual) { + return OID(oid); + } else { + return {}; + } +} + +uint32_t hash_oid_name(std::string_view s) { + uint64_t hash = 0x8188B31879A4879A; + + for(const char c : s) { + hash *= 251; + hash += c; + } + + return static_cast(hash % 805289); +} + +} // namespace + +//static +std::optional OID_Map::lookup_static_oid(const OID& oid) { + const uint32_t hc = static_cast(oid.hash_code() % 858701); + + switch(hc) { + case 0x01506: + return if_match(oid, {1, 2, 840, 10045, 4, 3, 1}, "ECDSA/SHA-224"); + case 0x01507: + return if_match(oid, {1, 2, 840, 10045, 4, 3, 2}, "ECDSA/SHA-256"); + case 0x01508: + return if_match(oid, {1, 2, 840, 10045, 4, 3, 3}, "ECDSA/SHA-384"); + case 0x01509: + return if_match(oid, {1, 2, 840, 10045, 4, 3, 4}, "ECDSA/SHA-512"); + case 0x04C1E: + return if_match(oid, {1, 3, 6, 1, 4, 1, 3029, 1, 2, 1}, "ElGamal"); + case 0x04E61: + return if_match(oid, {1, 3, 6, 1, 4, 1, 3029, 1, 5, 1}, "OpenPGP.Curve25519"); + case 0x0779B: + return if_match(oid, {1, 2, 840, 113549, 2, 5}, "MD5"); + case 0x0779D: + return if_match(oid, {1, 2, 840, 113549, 2, 7}, "HMAC(SHA-1)"); + case 0x0779E: + return if_match(oid, {1, 2, 840, 113549, 2, 8}, "HMAC(SHA-224)"); + case 0x0779F: + return if_match(oid, {1, 2, 840, 113549, 2, 9}, "HMAC(SHA-256)"); + case 0x077A0: + return if_match(oid, {1, 2, 840, 113549, 2, 10}, "HMAC(SHA-384)"); + case 0x077A1: + return if_match(oid, {1, 2, 840, 113549, 2, 11}, "HMAC(SHA-512)"); + case 0x077A3: + return if_match(oid, {1, 2, 840, 113549, 2, 13}, "HMAC(SHA-512-256)"); + case 0x0785E: + return if_match(oid, {1, 2, 840, 113549, 3, 7}, "TripleDES/CBC"); + case 0x0C904: + return if_match(oid, {1, 0, 14888, 3, 0, 5}, "ECKCDSA"); + case 0x11547: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 1}, "SphincsPlus-shake-128s-r3.1"); + case 0x11548: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 2}, "SphincsPlus-shake-128f-r3.1"); + case 0x11549: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 3}, "SphincsPlus-shake-192s-r3.1"); + case 0x1154A: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 4}, "SphincsPlus-shake-192f-r3.1"); + case 0x1154B: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 5}, "SphincsPlus-shake-256s-r3.1"); + case 0x1154C: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 6}, "SphincsPlus-shake-256f-r3.1"); + case 0x11608: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 1}, "SphincsPlus-sha2-128s-r3.1"); + case 0x11609: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 2}, "SphincsPlus-sha2-128f-r3.1"); + case 0x1160A: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 3}, "SphincsPlus-sha2-192s-r3.1"); + case 0x1160B: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 4}, "SphincsPlus-sha2-192f-r3.1"); + case 0x1160C: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 5}, "SphincsPlus-sha2-256s-r3.1"); + case 0x1160D: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 6}, "SphincsPlus-sha2-256f-r3.1"); + case 0x116C9: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 1}, "SphincsPlus-haraka-128s-r3.1"); + case 0x116CA: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 2}, "SphincsPlus-haraka-128f-r3.1"); + case 0x116CB: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 3}, "SphincsPlus-haraka-192s-r3.1"); + case 0x116CC: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 4}, "SphincsPlus-haraka-192f-r3.1"); + case 0x116CD: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 5}, "SphincsPlus-haraka-256s-r3.1"); + case 0x116CE: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 6}, "SphincsPlus-haraka-256f-r3.1"); + case 0x1533B: + return if_match(oid, {1, 2, 156, 10197, 1, 104, 2}, "SM4/CBC"); + case 0x15341: + return if_match(oid, {1, 2, 156, 10197, 1, 104, 8}, "SM4/GCM"); + case 0x1539D: + return if_match(oid, {1, 2, 156, 10197, 1, 104, 100}, "SM4/OCB"); + case 0x187D7: + return if_match(oid, {1, 3, 14, 3, 2, 7}, "DES/CBC"); + case 0x187EA: + return if_match(oid, {1, 3, 14, 3, 2, 26}, "SHA-1"); + case 0x19933: + return if_match(oid, {1, 3, 132, 0, 8}, "secp160r1"); + case 0x19934: + return if_match(oid, {1, 3, 132, 0, 9}, "secp160k1"); + case 0x19935: + return if_match(oid, {1, 3, 132, 0, 10}, "secp256k1"); + case 0x19949: + return if_match(oid, {1, 3, 132, 0, 30}, "secp160r2"); + case 0x1994A: + return if_match(oid, {1, 3, 132, 0, 31}, "secp192k1"); + case 0x1994B: + return if_match(oid, {1, 3, 132, 0, 32}, "secp224k1"); + case 0x1994C: + return if_match(oid, {1, 3, 132, 0, 33}, "secp224r1"); + case 0x1994D: + return if_match(oid, {1, 3, 132, 0, 34}, "secp384r1"); + case 0x1994E: + return if_match(oid, {1, 3, 132, 0, 35}, "secp521r1"); + case 0x199F8: + return if_match(oid, {1, 3, 132, 1, 12}, "ECDH"); + case 0x1E7BF: + return if_match(oid, {1, 2, 156, 10197, 1, 301, 1}, "SM2"); + case 0x1E7C0: + return if_match(oid, {1, 2, 156, 10197, 1, 301, 2}, "SM2_Kex"); + case 0x1E7C1: + return if_match(oid, {1, 2, 156, 10197, 1, 301, 3}, "SM2_Enc"); + case 0x21960: + return if_match(oid, {1, 3, 36, 3, 3, 1, 2}, "RSA/PKCS1v15(RIPEMD-160)"); + case 0x2198A: + return if_match(oid, {1, 2, 840, 113533, 7, 66, 10}, "CAST-128/CBC"); + case 0x2198F: + return if_match(oid, {1, 2, 840, 113533, 7, 66, 15}, "KeyWrap.CAST-128"); + case 0x227C0: + return if_match(oid, {1, 3, 101, 110}, "X25519"); + case 0x227C1: + return if_match(oid, {1, 3, 101, 111}, "X448"); + case 0x227C2: + return if_match(oid, {1, 3, 101, 112}, "Ed25519"); + case 0x227C3: + return if_match(oid, {1, 3, 101, 113}, "Ed448"); + case 0x27565: + return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 48, 1, 1}, "PKIX.OCSP.BasicResponse"); + case 0x27569: + return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 48, 1, 5}, "PKIX.OCSP.NoCheck"); + case 0x29F7C: + return if_match(oid, {1, 2, 410, 200004, 1, 100, 4, 3}, "ECKCDSA/SHA-1"); + case 0x29F7D: + return if_match(oid, {1, 2, 410, 200004, 1, 100, 4, 4}, "ECKCDSA/SHA-224"); + case 0x29F7E: + return if_match(oid, {1, 2, 410, 200004, 1, 100, 4, 5}, "ECKCDSA/SHA-256"); + case 0x2AC3B: + return if_match(oid, {2, 5, 29, 32, 0}, "X509v3.AnyPolicy"); + case 0x2B000: + return if_match(oid, {2, 5, 29, 37, 0}, "X509v3.AnyExtendedKeyUsage"); + case 0x2B5C9: + return if_match(oid, {1, 2, 840, 10045, 2, 1}, "ECDSA"); + case 0x2B74B: + return if_match(oid, {1, 2, 840, 10045, 4, 1}, "ECDSA/SHA-1"); + case 0x3474A: + return if_match(oid, {1, 2, 840, 10046, 2, 1}, "DH"); + case 0x38D6D: + return if_match(oid, {1, 2, 643, 7, 1, 2, 1, 1, 1}, "gost_256A"); + case 0x38D6E: + return if_match(oid, {1, 2, 643, 7, 1, 2, 1, 1, 2}, "gost_256B"); + case 0x38E2E: + return if_match(oid, {1, 2, 643, 7, 1, 2, 1, 2, 1}, "gost_512A"); + case 0x38E2F: + return if_match(oid, {1, 2, 643, 7, 1, 2, 1, 2, 2}, "gost_512B"); + case 0x38F2C: + return if_match(oid, {1, 2, 643, 2, 2, 3}, "GOST-34.10/GOST-R-34.11-94"); + case 0x38F3C: + return if_match(oid, {1, 2, 643, 2, 2, 19}, "GOST-34.10"); + case 0x3D7B8: + return if_match(oid, {0, 3, 4401, 5, 3, 1, 9, 6}, "Camellia-128/GCM"); + case 0x3D7CC: + return if_match(oid, {0, 3, 4401, 5, 3, 1, 9, 26}, "Camellia-192/GCM"); + case 0x3D7E0: + return if_match(oid, {0, 3, 4401, 5, 3, 1, 9, 46}, "Camellia-256/GCM"); + case 0x3F20F: + return if_match(oid, {1, 3, 36, 3, 2, 1}, "RIPEMD-160"); + case 0x4266E: + return if_match(oid, {0, 4, 0, 127, 0, 15, 1, 1, 13, 0}, "XMSS"); + case 0x478C4: + return if_match(oid, {1, 2, 410, 200004, 1, 4}, "SEED/CBC"); + case 0x47D98: + return if_match(oid, {1, 2, 156, 10197, 1, 301}, "sm2p256v1"); + case 0x47DFC: + return if_match(oid, {1, 2, 156, 10197, 1, 401}, "SM3"); + case 0x47E60: + return if_match(oid, {1, 2, 156, 10197, 1, 501}, "SM2_Sig/SM3"); + case 0x47E63: + return if_match(oid, {1, 2, 156, 10197, 1, 504}, "RSA/PKCS1v15(SM3)"); + case 0x52B13: + return if_match(oid, {1, 2, 643, 3, 131, 1, 1}, "GOST.INN"); + case 0x635AE: + return if_match(oid, {1, 2, 250, 1, 223, 101, 256, 1}, "frp256v1"); + case 0x6A784: + return if_match(oid, {1, 2, 840, 113549, 1, 12, 10, 1, 1}, "PKCS12.KeyBag"); + case 0x6A785: + return if_match(oid, {1, 2, 840, 113549, 1, 12, 10, 1, 2}, "PKCS12.PKCS8ShroudedKeyBag"); + case 0x6A786: + return if_match(oid, {1, 2, 840, 113549, 1, 12, 10, 1, 3}, "PKCS12.CertBag"); + case 0x6A787: + return if_match(oid, {1, 2, 840, 113549, 1, 12, 10, 1, 4}, "PKCS12.CRLBag"); + case 0x6A788: + return if_match(oid, {1, 2, 840, 113549, 1, 12, 10, 1, 5}, "PKCS12.SecretBag"); + case 0x6A789: + return if_match(oid, {1, 2, 840, 113549, 1, 12, 10, 1, 6}, "PKCS12.SafeContentsBag"); + case 0x6EB86: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 6, 1}, "GOST-34.10-2012-256/SHA-256"); + case 0x6EC47: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 7, 1}, "Kyber-512-r3"); + case 0x6EC48: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 7, 2}, "Kyber-768-r3"); + case 0x6EC49: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 7, 3}, "Kyber-1024-r3"); + case 0x6EDC9: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 9, 1}, "Dilithium-4x4-r3"); + case 0x6EDCA: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 9, 2}, "Dilithium-6x5-r3"); + case 0x6EDCB: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 9, 3}, "Dilithium-8x7-r3"); + case 0x6EE8A: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 10, 1}, "Dilithium-4x4-AES-r3"); + case 0x6EE8B: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 10, 2}, "Dilithium-6x5-AES-r3"); + case 0x6EE8C: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 10, 3}, "Dilithium-8x7-AES-r3"); + case 0x6EF4B: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 11, 1}, "Kyber-512-90s-r3"); + case 0x6EF4C: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 11, 2}, "Kyber-768-90s-r3"); + case 0x6EF4D: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 11, 3}, "Kyber-1024-90s-r3"); + case 0x6F18E: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 14, 1}, "FrodoKEM-640-SHAKE"); + case 0x6F18F: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 14, 2}, "FrodoKEM-976-SHAKE"); + case 0x6F190: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 14, 3}, "FrodoKEM-1344-SHAKE"); + case 0x6F24F: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 15, 1}, "FrodoKEM-640-AES"); + case 0x6F250: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 15, 2}, "FrodoKEM-976-AES"); + case 0x6F251: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 15, 3}, "FrodoKEM-1344-AES"); + case 0x6F310: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 16, 1}, "eFrodoKEM-640-SHAKE"); + case 0x6F311: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 16, 2}, "eFrodoKEM-976-SHAKE"); + case 0x6F312: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 16, 3}, "eFrodoKEM-1344-SHAKE"); + case 0x6F3D1: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 17, 1}, "eFrodoKEM-640-AES"); + case 0x6F3D2: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 17, 2}, "eFrodoKEM-976-AES"); + case 0x6F3D3: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 17, 3}, "eFrodoKEM-1344-AES"); + case 0x6F492: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 18, 1}, "ClassicMcEliece_6688128pc"); + case 0x6F493: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 18, 2}, "ClassicMcEliece_6688128pcf"); + case 0x6F494: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 18, 3}, "ClassicMcEliece_6960119pc"); + case 0x6F495: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 18, 4}, "ClassicMcEliece_6960119pcf"); + case 0x6F496: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 18, 5}, "ClassicMcEliece_8192128pc"); + case 0x6F497: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 18, 6}, "ClassicMcEliece_8192128pcf"); + case 0x6F79D: + return if_match(oid, {2, 16, 840, 1, 113730, 1, 13}, "Certificate Comment"); + case 0x701A0: + return if_match(oid, {1, 3, 36, 3, 3, 2, 5, 2, 1}, "ECGDSA"); + case 0x70322: + return if_match(oid, {1, 3, 36, 3, 3, 2, 5, 4, 1}, "ECGDSA/RIPEMD-160"); + case 0x70323: + return if_match(oid, {1, 3, 36, 3, 3, 2, 5, 4, 2}, "ECGDSA/SHA-1"); + case 0x70324: + return if_match(oid, {1, 3, 36, 3, 3, 2, 5, 4, 3}, "ECGDSA/SHA-224"); + case 0x70325: + return if_match(oid, {1, 3, 36, 3, 3, 2, 5, 4, 4}, "ECGDSA/SHA-256"); + case 0x70326: + return if_match(oid, {1, 3, 36, 3, 3, 2, 5, 4, 5}, "ECGDSA/SHA-384"); + case 0x70327: + return if_match(oid, {1, 3, 36, 3, 3, 2, 5, 4, 6}, "ECGDSA/SHA-512"); + case 0x72B21: + return if_match(oid, {1, 2, 643, 7, 1, 1, 1, 1}, "GOST-34.10-2012-256"); + case 0x72B22: + return if_match(oid, {1, 2, 643, 7, 1, 1, 1, 2}, "GOST-34.10-2012-512"); + case 0x72BE3: + return if_match(oid, {1, 2, 643, 7, 1, 1, 2, 2}, "Streebog-256"); + case 0x72BE4: + return if_match(oid, {1, 2, 643, 7, 1, 1, 2, 3}, "Streebog-512"); + case 0x72CA4: + return if_match(oid, {1, 2, 643, 7, 1, 1, 3, 2}, "GOST-34.10-2012-256/Streebog-256"); + case 0x72CA5: + return if_match(oid, {1, 2, 643, 7, 1, 1, 3, 3}, "GOST-34.10-2012-512/Streebog-512"); + case 0x7C7C7: + return if_match(oid, {1, 2, 840, 113549, 1, 9, 22, 1}, "PKCS9.X509Certificate"); + case 0x7C7C8: + return if_match(oid, {1, 2, 840, 113549, 1, 9, 22, 2}, "PKCS9.SDSICertificate"); + case 0x7C888: + return if_match(oid, {1, 2, 840, 113549, 1, 9, 23, 1}, "PKCS9.X509CRL"); + case 0x7E10F: + return if_match(oid, {2, 5, 4, 3}, "X520.CommonName"); + case 0x7E110: + return if_match(oid, {2, 5, 4, 4}, "X520.Surname"); + case 0x7E111: + return if_match(oid, {2, 5, 4, 5}, "X520.SerialNumber"); + case 0x7E112: + return if_match(oid, {2, 5, 4, 6}, "X520.Country"); + case 0x7E113: + return if_match(oid, {2, 5, 4, 7}, "X520.Locality"); + case 0x7E114: + return if_match(oid, {2, 5, 4, 8}, "X520.State"); + case 0x7E115: + return if_match(oid, {2, 5, 4, 9}, "X520.StreetAddress"); + case 0x7E116: + return if_match(oid, {2, 5, 4, 10}, "X520.Organization"); + case 0x7E117: + return if_match(oid, {2, 5, 4, 11}, "X520.OrganizationalUnit"); + case 0x7E118: + return if_match(oid, {2, 5, 4, 12}, "X520.Title"); + case 0x7E136: + return if_match(oid, {2, 5, 4, 42}, "X520.GivenName"); + case 0x7E137: + return if_match(oid, {2, 5, 4, 43}, "X520.Initials"); + case 0x7E138: + return if_match(oid, {2, 5, 4, 44}, "X520.GenerationalQualifier"); + case 0x7E13A: + return if_match(oid, {2, 5, 4, 46}, "X520.DNQualifier"); + case 0x7E14D: + return if_match(oid, {2, 5, 4, 65}, "X520.Pseudonym"); + case 0x7F3F3: + return if_match(oid, {2, 5, 29, 14}, "X509v3.SubjectKeyIdentifier"); + case 0x7F3F4: + return if_match(oid, {2, 5, 29, 15}, "X509v3.KeyUsage"); + case 0x7F3F5: + return if_match(oid, {2, 5, 29, 16}, "X509v3.PrivateKeyUsagePeriod"); + case 0x7F3F6: + return if_match(oid, {2, 5, 29, 17}, "X509v3.SubjectAlternativeName"); + case 0x7F3F7: + return if_match(oid, {2, 5, 29, 18}, "X509v3.IssuerAlternativeName"); + case 0x7F3F8: + return if_match(oid, {2, 5, 29, 19}, "X509v3.BasicConstraints"); + case 0x7F3F9: + return if_match(oid, {2, 5, 29, 20}, "X509v3.CRLNumber"); + case 0x7F3FA: + return if_match(oid, {2, 5, 29, 21}, "X509v3.ReasonCode"); + case 0x7F3FC: + return if_match(oid, {2, 5, 29, 23}, "X509v3.HoldInstructionCode"); + case 0x7F3FD: + return if_match(oid, {2, 5, 29, 24}, "X509v3.InvalidityDate"); + case 0x7F401: + return if_match(oid, {2, 5, 29, 28}, "X509v3.CRLIssuingDistributionPoint"); + case 0x7F403: + return if_match(oid, {2, 5, 29, 30}, "X509v3.NameConstraints"); + case 0x7F404: + return if_match(oid, {2, 5, 29, 31}, "X509v3.CRLDistributionPoints"); + case 0x7F405: + return if_match(oid, {2, 5, 29, 32}, "X509v3.CertificatePolicies"); + case 0x7F408: + return if_match(oid, {2, 5, 29, 35}, "X509v3.AuthorityKeyIdentifier"); + case 0x7F409: + return if_match(oid, {2, 5, 29, 36}, "X509v3.PolicyConstraints"); + case 0x7F40A: + return if_match(oid, {2, 5, 29, 37}, "X509v3.ExtendedKeyUsage"); + case 0x80B84: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 2, 1}, "AES-128/OCB"); + case 0x80B85: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 2, 2}, "AES-192/OCB"); + case 0x80B86: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 2, 3}, "AES-256/OCB"); + case 0x80B87: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 2, 4}, "Serpent/OCB"); + case 0x80B88: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 2, 5}, "Twofish/OCB"); + case 0x80B89: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 2, 6}, "Camellia-128/OCB"); + case 0x80B8A: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 2, 7}, "Camellia-192/OCB"); + case 0x80B8B: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 2, 8}, "Camellia-256/OCB"); + case 0x80D06: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 4, 1}, "AES-128/SIV"); + case 0x80D07: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 4, 2}, "AES-192/SIV"); + case 0x80D08: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 4, 3}, "AES-256/SIV"); + case 0x80D09: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 4, 4}, "Serpent/SIV"); + case 0x80D0A: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 4, 5}, "Twofish/SIV"); + case 0x80D0B: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 4, 6}, "Camellia-128/SIV"); + case 0x80D0C: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 4, 7}, "Camellia-192/SIV"); + case 0x80D0D: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 4, 8}, "Camellia-256/SIV"); + case 0x80D0E: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 4, 9}, "SM4/SIV"); + case 0x84C6A: + return if_match(oid, {1, 2, 392, 200011, 61, 1, 1, 1, 2}, "Camellia-128/CBC"); + case 0x84C6B: + return if_match(oid, {1, 2, 392, 200011, 61, 1, 1, 1, 3}, "Camellia-192/CBC"); + case 0x84C6C: + return if_match(oid, {1, 2, 392, 200011, 61, 1, 1, 1, 4}, "Camellia-256/CBC"); + case 0x88CD3: + return if_match(oid, {1, 2, 840, 113549, 1, 9, 16, 3, 6}, "KeyWrap.TripleDES"); + case 0x88CD5: + return if_match(oid, {1, 2, 840, 113549, 1, 9, 16, 3, 8}, "Compression.Zlib"); + case 0x88CDE: + return if_match(oid, {1, 2, 840, 113549, 1, 9, 16, 3, 17}, "HSS-LMS"); + case 0x88CDF: + return if_match(oid, {1, 2, 840, 113549, 1, 9, 16, 3, 18}, "ChaCha20Poly1305"); + case 0x92296: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 2}, "AES-128/CBC"); + case 0x92299: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 5}, "KeyWrap.AES-128"); + case 0x9229A: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 6}, "AES-128/GCM"); + case 0x9229B: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 7}, "AES-128/CCM"); + case 0x922AA: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 22}, "AES-192/CBC"); + case 0x922AD: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 25}, "KeyWrap.AES-192"); + case 0x922AE: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 26}, "AES-192/GCM"); + case 0x922AF: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 27}, "AES-192/CCM"); + case 0x922BE: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 42}, "AES-256/CBC"); + case 0x922C1: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 45}, "KeyWrap.AES-256"); + case 0x922C2: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 46}, "AES-256/GCM"); + case 0x922C3: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 47}, "AES-256/CCM"); + case 0x92356: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 2, 1}, "SHA-256"); + case 0x92357: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 2, 2}, "SHA-384"); + case 0x92358: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 2, 3}, "SHA-512"); + case 0x92359: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 2, 4}, "SHA-224"); + case 0x9235B: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 2, 6}, "SHA-512-256"); + case 0x9235C: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 2, 7}, "SHA-3(224)"); + case 0x9235D: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 2, 8}, "SHA-3(256)"); + case 0x9235E: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 2, 9}, "SHA-3(384)"); + case 0x9235F: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 2, 10}, "SHA-3(512)"); + case 0x92360: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 2, 11}, "SHAKE-128"); + case 0x92361: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 2, 12}, "SHAKE-256"); + case 0x92417: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 1}, "DSA/SHA-224"); + case 0x92418: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 2}, "DSA/SHA-256"); + case 0x92419: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 3}, "DSA/SHA-384"); + case 0x9241A: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 4}, "DSA/SHA-512"); + case 0x9241B: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 5}, "DSA/SHA-3(224)"); + case 0x9241C: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 6}, "DSA/SHA-3(256)"); + case 0x9241D: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 7}, "DSA/SHA-3(384)"); + case 0x9241E: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 8}, "DSA/SHA-3(512)"); + case 0x9241F: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 9}, "ECDSA/SHA-3(224)"); + case 0x92420: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 10}, "ECDSA/SHA-3(256)"); + case 0x92421: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 11}, "ECDSA/SHA-3(384)"); + case 0x92422: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 12}, "ECDSA/SHA-3(512)"); + case 0x92423: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 13}, "RSA/PKCS1v15(SHA-3(224))"); + case 0x92424: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 14}, "RSA/PKCS1v15(SHA-3(256))"); + case 0x92425: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 15}, "RSA/PKCS1v15(SHA-3(384))"); + case 0x92426: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 16}, "RSA/PKCS1v15(SHA-3(512))"); + case 0x92427: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 17}, "ML-DSA-4x4"); + case 0x92428: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 18}, "ML-DSA-6x5"); + case 0x92429: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 19}, "ML-DSA-8x7"); + case 0x9242A: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 20}, "SLH-DSA-SHA2-128s"); + case 0x9242B: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 21}, "SLH-DSA-SHA2-128f"); + case 0x9242C: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 22}, "SLH-DSA-SHA2-192s"); + case 0x9242D: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 23}, "SLH-DSA-SHA2-192f"); + case 0x9242E: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 24}, "SLH-DSA-SHA2-256s"); + case 0x9242F: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 25}, "SLH-DSA-SHA2-256f"); + case 0x92430: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 26}, "SLH-DSA-SHAKE-128s"); + case 0x92431: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 27}, "SLH-DSA-SHAKE-128f"); + case 0x92432: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 28}, "SLH-DSA-SHAKE-192s"); + case 0x92433: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 29}, "SLH-DSA-SHAKE-192f"); + case 0x92434: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 30}, "SLH-DSA-SHAKE-256s"); + case 0x92435: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 31}, "SLH-DSA-SHAKE-256f"); + case 0x924D8: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 4, 1}, "ML-KEM-512"); + case 0x924D9: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 4, 2}, "ML-KEM-768"); + case 0x924DA: + return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 4, 3}, "ML-KEM-1024"); + case 0x9479F: + return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 1, 1}, "PKIX.AuthorityInformationAccess"); + case 0x947A5: + return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 1, 7}, "PKIX.IpAddrBlocks"); + case 0x947A6: + return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 1, 8}, "PKIX.AutonomousSysIds"); + case 0x947B8: + return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 1, 26}, "PKIX.TNAuthList"); + case 0x94921: + return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 3, 1}, "PKIX.ServerAuth"); + case 0x94922: + return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 3, 2}, "PKIX.ClientAuth"); + case 0x94923: + return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 3, 3}, "PKIX.CodeSigning"); + case 0x94924: + return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 3, 4}, "PKIX.EmailProtection"); + case 0x94925: + return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 3, 5}, "PKIX.IPsecEndSystem"); + case 0x94926: + return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 3, 6}, "PKIX.IPsecTunnel"); + case 0x94927: + return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 3, 7}, "PKIX.IPsecUser"); + case 0x94928: + return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 3, 8}, "PKIX.TimeStamping"); + case 0x94929: + return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 3, 9}, "PKIX.OCSPSigning"); + case 0x94CEA: + return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 8, 5}, "PKIX.XMPPAddr"); + case 0x954DB: + return if_match(oid, {1, 3, 6, 1, 4, 1, 311, 20, 2, 2}, "Microsoft SmartcardLogon"); + case 0x954DC: + return if_match(oid, {1, 3, 6, 1, 4, 1, 311, 20, 2, 3}, "Microsoft UPN"); + case 0x96B0E: + return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 48, 1}, "PKIX.OCSP"); + case 0x96B0F: + return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 48, 2}, "PKIX.CertificateAuthorityIssuers"); + case 0x96C77: + return if_match(oid, {1, 2, 840, 113549, 1, 12, 1, 3}, "PBE-SHA1-3DES"); + case 0x96C78: + return if_match(oid, {1, 2, 840, 113549, 1, 12, 1, 4}, "PBE-SHA1-2DES"); + case 0x9A008: + return if_match(oid, {1, 3, 36, 3, 3, 2, 8, 1, 1, 1}, "brainpool160r1"); + case 0x9A00A: + return if_match(oid, {1, 3, 36, 3, 3, 2, 8, 1, 1, 3}, "brainpool192r1"); + case 0x9A00C: + return if_match(oid, {1, 3, 36, 3, 3, 2, 8, 1, 1, 5}, "brainpool224r1"); + case 0x9A00E: + return if_match(oid, {1, 3, 36, 3, 3, 2, 8, 1, 1, 7}, "brainpool256r1"); + case 0x9A010: + return if_match(oid, {1, 3, 36, 3, 3, 2, 8, 1, 1, 9}, "brainpool320r1"); + case 0x9A012: + return if_match(oid, {1, 3, 36, 3, 3, 2, 8, 1, 1, 11}, "brainpool384r1"); + case 0x9A014: + return if_match(oid, {1, 3, 36, 3, 3, 2, 8, 1, 1, 13}, "brainpool512r1"); + case 0xA0D61: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 3}, "McEliece"); + case 0xA0D63: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 5}, "XMSS-draft6"); + case 0xA0D66: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 8}, "XMSS-draft12"); + case 0xA0D6B: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 13}, "HSS-LMS-Private-Key"); + case 0xA0EE1: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 1}, "Serpent/CBC"); + case 0xA0EE2: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 2}, "Threefish-512/CBC"); + case 0xA0EE3: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 3}, "Twofish/CBC"); + case 0xA0F45: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 101}, "Serpent/GCM"); + case 0xA0F46: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 102}, "Twofish/GCM"); + case 0xA0FA2: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 4, 1}, "numsp256d1"); + case 0xA0FA3: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 4, 2}, "numsp384d1"); + case 0xA0FA4: + return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 4, 3}, "numsp512d1"); + case 0xA244B: + return if_match(oid, {1, 3, 6, 1, 4, 1, 22554, 5, 1, 1}, "ClassicMcEliece_348864"); + case 0xA244C: + return if_match(oid, {1, 3, 6, 1, 4, 1, 22554, 5, 1, 2}, "ClassicMcEliece_348864f"); + case 0xA244D: + return if_match(oid, {1, 3, 6, 1, 4, 1, 22554, 5, 1, 3}, "ClassicMcEliece_460896"); + case 0xA244E: + return if_match(oid, {1, 3, 6, 1, 4, 1, 22554, 5, 1, 4}, "ClassicMcEliece_460896f"); + case 0xA244F: + return if_match(oid, {1, 3, 6, 1, 4, 1, 22554, 5, 1, 5}, "ClassicMcEliece_6688128"); + case 0xA2450: + return if_match(oid, {1, 3, 6, 1, 4, 1, 22554, 5, 1, 6}, "ClassicMcEliece_6688128f"); + case 0xA2451: + return if_match(oid, {1, 3, 6, 1, 4, 1, 22554, 5, 1, 7}, "ClassicMcEliece_6960119"); + case 0xA2452: + return if_match(oid, {1, 3, 6, 1, 4, 1, 22554, 5, 1, 8}, "ClassicMcEliece_6960119f"); + case 0xA2453: + return if_match(oid, {1, 3, 6, 1, 4, 1, 22554, 5, 1, 9}, "ClassicMcEliece_8192128"); + case 0xA2454: + return if_match(oid, {1, 3, 6, 1, 4, 1, 22554, 5, 1, 10}, "ClassicMcEliece_8192128f"); + case 0xAF989: + return if_match(oid, {1, 2, 840, 113549, 1, 1, 1}, "RSA"); + case 0xAF98A: + return if_match(oid, {1, 2, 840, 113549, 1, 1, 2}, "RSA/PKCS1v15(MD2)"); + case 0xAF98C: + return if_match(oid, {1, 2, 840, 113549, 1, 1, 4}, "RSA/PKCS1v15(MD5)"); + case 0xAF98D: + return if_match(oid, {1, 2, 840, 113549, 1, 1, 5}, "RSA/PKCS1v15(SHA-1)"); + case 0xAF98F: + return if_match(oid, {1, 2, 840, 113549, 1, 1, 7}, "RSA/OAEP"); + case 0xAF990: + return if_match(oid, {1, 2, 840, 113549, 1, 1, 8}, "MGF1"); + case 0xAF992: + return if_match(oid, {1, 2, 840, 113549, 1, 1, 10}, "RSA/PSS"); + case 0xAF993: + return if_match(oid, {1, 2, 840, 113549, 1, 1, 11}, "RSA/PKCS1v15(SHA-256)"); + case 0xAF994: + return if_match(oid, {1, 2, 840, 113549, 1, 1, 12}, "RSA/PKCS1v15(SHA-384)"); + case 0xAF995: + return if_match(oid, {1, 2, 840, 113549, 1, 1, 13}, "RSA/PKCS1v15(SHA-512)"); + case 0xAF996: + return if_match(oid, {1, 2, 840, 113549, 1, 1, 14}, "RSA/PKCS1v15(SHA-224)"); + case 0xAF998: + return if_match(oid, {1, 2, 840, 113549, 1, 1, 16}, "RSA/PKCS1v15(SHA-512-256)"); + case 0xAFC98: + return if_match(oid, {1, 2, 840, 113549, 1, 5, 12}, "PKCS5.PBKDF2"); + case 0xAFC99: + return if_match(oid, {1, 2, 840, 113549, 1, 5, 13}, "PBE-PKCS5v20"); + case 0xAFE0F: + return if_match(oid, {1, 2, 840, 113549, 1, 7, 1}, "PKCS7.Data"); + case 0xAFE14: + return if_match(oid, {1, 2, 840, 113549, 1, 7, 6}, "PKCS7.EncryptedData"); + case 0xAFF91: + return if_match(oid, {1, 2, 840, 113549, 1, 9, 1}, "PKCS9.EmailAddress"); + case 0xAFF92: + return if_match(oid, {1, 2, 840, 113549, 1, 9, 2}, "PKCS9.UnstructuredName"); + case 0xAFF93: + return if_match(oid, {1, 2, 840, 113549, 1, 9, 3}, "PKCS9.ContentType"); + case 0xAFF94: + return if_match(oid, {1, 2, 840, 113549, 1, 9, 4}, "PKCS9.MessageDigest"); + case 0xAFF97: + return if_match(oid, {1, 2, 840, 113549, 1, 9, 7}, "PKCS9.ChallengePassword"); + case 0xAFF9E: + return if_match(oid, {1, 2, 840, 113549, 1, 9, 14}, "PKCS9.ExtensionRequest"); + case 0xAFFA4: + return if_match(oid, {1, 2, 840, 113549, 1, 9, 20}, "PKCS9.FriendlyName"); + case 0xAFFA5: + return if_match(oid, {1, 2, 840, 113549, 1, 9, 21}, "PKCS9.LocalKeyId"); + case 0xC0226: + return if_match(oid, {1, 3, 6, 1, 4, 1, 11591, 4, 11}, "Scrypt"); + case 0xC0A67: + return if_match(oid, {1, 3, 6, 1, 4, 1, 11591, 15, 1}, "OpenPGP.Ed25519"); + case 0xC4CE5: + return if_match(oid, {1, 2, 643, 100, 1}, "GOST.OGRN"); + case 0xC4D53: + return if_match(oid, {1, 2, 643, 100, 111}, "GOST.SubjectSigningTool"); + case 0xC4D54: + return if_match(oid, {1, 2, 643, 100, 112}, "GOST.IssuerSigningTool"); + case 0xC9C50: + return if_match(oid, {1, 2, 840, 10045, 3, 1, 1}, "secp192r1"); + case 0xC9C51: + return if_match(oid, {1, 2, 840, 10045, 3, 1, 2}, "x962_p192v2"); + case 0xC9C52: + return if_match(oid, {1, 2, 840, 10045, 3, 1, 3}, "x962_p192v3"); + case 0xC9C53: + return if_match(oid, {1, 2, 840, 10045, 3, 1, 4}, "x962_p239v1"); + case 0xC9C54: + return if_match(oid, {1, 2, 840, 10045, 3, 1, 5}, "x962_p239v2"); + case 0xC9C55: + return if_match(oid, {1, 2, 840, 10045, 3, 1, 6}, "x962_p239v3"); + case 0xC9C56: + return if_match(oid, {1, 2, 840, 10045, 3, 1, 7}, "secp256r1"); + case 0xCFA13: + return if_match(oid, {1, 2, 840, 10040, 4, 1}, "DSA"); + case 0xCFA15: + return if_match(oid, {1, 2, 840, 10040, 4, 3}, "DSA/SHA-1"); + default: + return {}; + } +} + +//static +std::optional OID_Map::lookup_static_oid_name(std::string_view req) { + const uint32_t hc = hash_oid_name(req); + + switch(hc) { + case 0x00545: + return if_match(req, "Twofish/GCM", {1, 3, 6, 1, 4, 1, 25258, 3, 102}); + case 0x00CF3: + return if_match(req, "SphincsPlus-sha2-192f-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 4}); + case 0x015FE: + return if_match(req, "FrodoKEM-640-SHAKE", {1, 3, 6, 1, 4, 1, 25258, 1, 14, 1}); + case 0x01F9E: + return if_match(req, "MD5", {1, 2, 840, 113549, 2, 5}); + case 0x02293: + return if_match(req, "SphincsPlus-shake-192f-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 4}); + case 0x02B93: + return if_match(req, "Microsoft SmartcardLogon", {1, 3, 6, 1, 4, 1, 311, 20, 2, 2}); + case 0x041D5: + return if_match(req, "secp160k1", {1, 3, 132, 0, 9}); + case 0x044B3: + return if_match(req, "Camellia-256/SIV", {1, 3, 6, 1, 4, 1, 25258, 3, 4, 8}); + case 0x048B2: + return if_match(req, "secp160r1", {1, 3, 132, 0, 8}); + case 0x048B3: + return if_match(req, "secp160r2", {1, 3, 132, 0, 30}); + case 0x05CDA: + return if_match(req, "X520.Country", {2, 5, 4, 6}); + case 0x07783: + return if_match(req, "PKIX.ServerAuth", {1, 3, 6, 1, 5, 5, 7, 3, 1}); + case 0x086C7: + return if_match(req, "numsp384d1", {1, 3, 6, 1, 4, 1, 25258, 4, 2}); + case 0x08A92: + return if_match(req, "RSA/PKCS1v15(SHA-1)", {1, 2, 840, 113549, 1, 1, 5}); + case 0x09EA0: + return if_match(req, "DES/CBC", {1, 3, 14, 3, 2, 7}); + case 0x0B2D6: + return if_match(req, "ECDSA/SHA-3(512)", {2, 16, 840, 1, 101, 3, 4, 3, 12}); + case 0x0BA72: + return if_match(req, "SphincsPlus-sha2-128s-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 1}); + case 0x0BE23: + return if_match(req, "ECGDSA", {1, 3, 36, 3, 3, 2, 5, 2, 1}); + case 0x0C109: + return if_match(req, "PKCS9.FriendlyName", {1, 2, 840, 113549, 1, 9, 20}); + case 0x0D012: + return if_match(req, "SphincsPlus-shake-128s-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 1}); + case 0x0DCE9: + return if_match(req, "ClassicMcEliece_8192128f", {1, 3, 6, 1, 4, 1, 22554, 5, 1, 10}); + case 0x0E52A: + return if_match(req, "numsp512d1", {1, 3, 6, 1, 4, 1, 25258, 4, 3}); + case 0x0F9CC: + return if_match(req, "PKCS9.UnstructuredName", {1, 2, 840, 113549, 1, 9, 2}); + case 0x0FF45: + return if_match(req, "Camellia-256/GCM", {0, 3, 4401, 5, 3, 1, 9, 46}); + case 0x1033D: + return if_match(req, "DSA/SHA-3(384)", {2, 16, 840, 1, 101, 3, 4, 3, 7}); + case 0x1139D: + return if_match(req, "secp192k1", {1, 3, 132, 0, 31}); + case 0x113D6: + return if_match(req, "X520.DNQualifier", {2, 5, 4, 46}); + case 0x11A7A: + return if_match(req, "secp192r1", {1, 2, 840, 10045, 3, 1, 1}); + case 0x12096: + return if_match(req, "SM2_Kex", {1, 2, 156, 10197, 1, 301, 2}); + case 0x13FC1: + return if_match(req, "X520.GenerationalQualifier", {2, 5, 4, 44}); + case 0x1445B: + return if_match(req, "PKCS5.PBKDF2", {1, 2, 840, 113549, 1, 5, 12}); + case 0x1495D: + return if_match(req, "eFrodoKEM-1344-AES", {1, 3, 6, 1, 4, 1, 25258, 1, 17, 3}); + case 0x14E30: + return if_match(req, "ClassicMcEliece_460896", {1, 3, 6, 1, 4, 1, 22554, 5, 1, 3}); + case 0x14FB1: + return if_match(req, "XMSS-draft12", {1, 3, 6, 1, 4, 1, 25258, 1, 8}); + case 0x156E3: + return if_match(req, "Compression.Zlib", {1, 2, 840, 113549, 1, 9, 16, 3, 8}); + case 0x1579E: + return if_match(req, "Streebog-512", {1, 2, 643, 7, 1, 1, 2, 3}); + case 0x1701A: + return if_match(req, "X509v3.AnyExtendedKeyUsage", {2, 5, 29, 37, 0}); + case 0x175EF: + return if_match(req, "Kyber-1024-90s-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 11, 3}); + case 0x17709: + return if_match(req, "X520.GivenName", {2, 5, 4, 42}); + case 0x17AD9: + return if_match(req, "RSA/PKCS1v15(SM3)", {1, 2, 156, 10197, 1, 504}); + case 0x17CE2: + return if_match(req, "SLH-DSA-SHA2-256f", {2, 16, 840, 1, 101, 3, 4, 3, 25}); + case 0x17CEF: + return if_match(req, "SLH-DSA-SHA2-256s", {2, 16, 840, 1, 101, 3, 4, 3, 24}); + case 0x18618: + return if_match(req, "FrodoKEM-976-AES", {1, 3, 6, 1, 4, 1, 25258, 1, 15, 2}); + case 0x19480: + return if_match(req, "eFrodoKEM-1344-SHAKE", {1, 3, 6, 1, 4, 1, 25258, 1, 16, 3}); + case 0x1958A: + return if_match(req, "X509v3.InvalidityDate", {2, 5, 29, 24}); + case 0x19851: + return if_match(req, "DSA/SHA-1", {1, 2, 840, 10040, 4, 3}); + case 0x1B2E7: + return if_match(req, "KeyWrap.AES-128", {2, 16, 840, 1, 101, 3, 4, 1, 5}); + case 0x1B9BE: + return if_match(req, "KeyWrap.AES-192", {2, 16, 840, 1, 101, 3, 4, 1, 25}); + case 0x1D439: + return if_match(req, "SphincsPlus-haraka-192f-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 4}); + case 0x2065B: + return if_match(req, "KeyWrap.CAST-128", {1, 2, 840, 113533, 7, 66, 15}); + case 0x216A0: + return if_match(req, "ML-KEM-512", {2, 16, 840, 1, 101, 3, 4, 4, 1}); + case 0x2216B: + return if_match(req, "GOST-34.10-2012-512", {1, 2, 643, 7, 1, 1, 1, 2}); + case 0x22C2C: + return if_match(req, "ElGamal", {1, 3, 6, 1, 4, 1, 3029, 1, 2, 1}); + case 0x2559A: + return if_match(req, "X520.Initials", {2, 5, 4, 43}); + case 0x271AC: + return if_match(req, "PKIX.AutonomousSysIds", {1, 3, 6, 1, 5, 5, 7, 1, 8}); + case 0x2808B: + return if_match(req, "PKCS7.Data", {1, 2, 840, 113549, 1, 7, 1}); + case 0x281B8: + return if_match(req, "SphincsPlus-haraka-128s-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 1}); + case 0x29999: + return if_match(req, "DSA/SHA-3(256)", {2, 16, 840, 1, 101, 3, 4, 3, 6}); + case 0x2A83D: + return if_match(req, "SHA-224", {2, 16, 840, 1, 101, 3, 4, 2, 4}); + case 0x2AB30: + return if_match(req, "SHA-256", {2, 16, 840, 1, 101, 3, 4, 2, 1}); + case 0x2ABEF: + return if_match(req, "KeyWrap.AES-256", {2, 16, 840, 1, 101, 3, 4, 1, 45}); + case 0x2BAEF: + return if_match(req, "SM2_Sig/SM3", {1, 2, 156, 10197, 1, 501}); + case 0x2C39A: + return if_match(req, "ECGDSA/RIPEMD-160", {1, 3, 36, 3, 3, 2, 5, 4, 1}); + case 0x2C54F: + return if_match(req, "ECDSA/SHA-3(224)", {2, 16, 840, 1, 101, 3, 4, 3, 9}); + case 0x2EEA6: + return if_match(req, "RSA/PKCS1v15(RIPEMD-160)", {1, 3, 36, 3, 3, 1, 2}); + case 0x2EFBA: + return if_match(req, "Kyber-512-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 7, 1}); + case 0x2F0AD: + return if_match(req, "PKCS7.EncryptedData", {1, 2, 840, 113549, 1, 7, 6}); + case 0x2F219: + return if_match(req, "PBE-SHA1-2DES", {1, 2, 840, 113549, 1, 12, 1, 4}); + case 0x3133E: + return if_match(req, "SLH-DSA-SHA2-128f", {2, 16, 840, 1, 101, 3, 4, 3, 21}); + case 0x3134B: + return if_match(req, "SLH-DSA-SHA2-128s", {2, 16, 840, 1, 101, 3, 4, 3, 20}); + case 0x3160D: + return if_match(req, "RSA/PKCS1v15(SHA-3(224))", {2, 16, 840, 1, 101, 3, 4, 3, 13}); + case 0x319E0: + return if_match(req, "GOST-34.10-2012-256/Streebog-256", {1, 2, 643, 7, 1, 1, 3, 2}); + case 0x31B3D: + return if_match(req, "HMAC(SHA-512)", {1, 2, 840, 113549, 2, 11}); + case 0x31C6D: + return if_match(req, "secp384r1", {1, 3, 132, 0, 34}); + case 0x32899: + return if_match(req, "TripleDES/CBC", {1, 2, 840, 113549, 3, 7}); + case 0x33D04: + return if_match(req, "PKCS12.SecretBag", {1, 2, 840, 113549, 1, 12, 10, 1, 5}); + case 0x3615D: + return if_match(req, "FrodoKEM-976-SHAKE", {1, 3, 6, 1, 4, 1, 25258, 1, 14, 2}); + case 0x361B8: + return if_match(req, "Ed25519", {1, 3, 101, 112}); + case 0x3649D: + return if_match(req, "SHAKE-128", {2, 16, 840, 1, 101, 3, 4, 2, 11}); + case 0x36693: + return if_match(req, "ClassicMcEliece_348864", {1, 3, 6, 1, 4, 1, 22554, 5, 1, 1}); + case 0x373C7: + return if_match(req, "ML-DSA-4x4", {2, 16, 840, 1, 101, 3, 4, 3, 17}); + case 0x3750B: + return if_match(req, "ClassicMcEliece_8192128", {1, 3, 6, 1, 4, 1, 22554, 5, 1, 9}); + case 0x39890: + return if_match(req, "Ed448", {1, 3, 101, 113}); + case 0x3A438: + return if_match(req, "SHA-384", {2, 16, 840, 1, 101, 3, 4, 2, 2}); + case 0x3A963: + return if_match(req, "DH", {1, 2, 840, 10046, 2, 1}); + case 0x3AC83: + return if_match(req, "MGF1", {1, 2, 840, 113549, 1, 1, 8}); + case 0x3ACBA: + return if_match(req, "X509v3.IssuerAlternativeName", {2, 5, 29, 18}); + case 0x3B273: + return if_match(req, "KeyWrap.TripleDES", {1, 2, 840, 113549, 1, 9, 16, 3, 6}); + case 0x3B91E: + return if_match(req, "X509v3.PrivateKeyUsagePeriod", {2, 5, 29, 16}); + case 0x3BC8A: + return if_match(req, "SLH-DSA-SHAKE-192f", {2, 16, 840, 1, 101, 3, 4, 3, 29}); + case 0x3BC97: + return if_match(req, "SLH-DSA-SHAKE-192s", {2, 16, 840, 1, 101, 3, 4, 3, 28}); + case 0x3D127: + return if_match(req, "DSA", {1, 2, 840, 10040, 4, 1}); + case 0x3E249: + return if_match(req, "HSS-LMS", {1, 2, 840, 113549, 1, 9, 16, 3, 17}); + case 0x3E7D5: + return if_match(req, "RSA/PKCS1v15(SHA-3(256))", {2, 16, 840, 1, 101, 3, 4, 3, 14}); + case 0x3F748: + return if_match(req, "GOST.OGRN", {1, 2, 643, 100, 1}); + case 0x3F99F: + return if_match(req, "X509v3.BasicConstraints", {2, 5, 29, 19}); + case 0x40726: + return if_match(req, "SHA-3(512)", {2, 16, 840, 1, 101, 3, 4, 2, 10}); + case 0x407BF: + return if_match(req, "ML-KEM-768", {2, 16, 840, 1, 101, 3, 4, 4, 2}); + case 0x41334: + return if_match(req, "ECDSA/SHA-3(384)", {2, 16, 840, 1, 101, 3, 4, 3, 11}); + case 0x42DF3: + return if_match(req, "X509v3.CRLDistributionPoints", {2, 5, 29, 31}); + case 0x437FB: + return if_match(req, "brainpool160r1", {1, 3, 36, 3, 3, 2, 8, 1, 1, 1}); + case 0x441F5: + return if_match(req, "gost_256A", {1, 2, 643, 7, 1, 2, 1, 1, 1}); + case 0x441F6: + return if_match(req, "gost_256B", {1, 2, 643, 7, 1, 2, 1, 1, 2}); + case 0x44221: + return if_match(req, "GOST-34.10-2012-512/Streebog-512", {1, 2, 643, 7, 1, 1, 3, 3}); + case 0x44322: + return if_match(req, "ClassicMcEliece_6960119pc", {1, 3, 6, 1, 4, 1, 25258, 1, 18, 3}); + case 0x44973: + return if_match(req, "Kyber-512-90s-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 11, 1}); + case 0x45C27: + return if_match(req, "RSA/PKCS1v15(SHA-512-256)", {1, 2, 840, 113549, 1, 1, 16}); + case 0x45C85: + return if_match(req, "X509v3.ReasonCode", {2, 5, 29, 21}); + case 0x45DA5: + return if_match(req, "SHAKE-256", {2, 16, 840, 1, 101, 3, 4, 2, 12}); + case 0x4663C: + return if_match(req, "X509v3.PolicyConstraints", {2, 5, 29, 36}); + case 0x480F7: + return if_match(req, "Serpent/OCB", {1, 3, 6, 1, 4, 1, 25258, 3, 2, 4}); + case 0x48627: + return if_match(req, "Dilithium-4x4-AES-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 10, 1}); + case 0x48861: + return if_match(req, "ChaCha20Poly1305", {1, 2, 840, 113549, 1, 9, 16, 3, 18}); + case 0x4A292: + return if_match(req, "frp256v1", {1, 2, 250, 1, 223, 101, 256, 1}); + case 0x4A9EE: + return if_match(req, "ClassicMcEliece_6960119f", {1, 3, 6, 1, 4, 1, 22554, 5, 1, 8}); + case 0x4BF87: + return if_match(req, "PKIX.TNAuthList", {1, 3, 6, 1, 5, 5, 7, 1, 26}); + case 0x4C088: + return if_match(req, "eFrodoKEM-976-AES", {1, 3, 6, 1, 4, 1, 25258, 1, 17, 2}); + case 0x4C513: + return if_match(req, "DSA/SHA-224", {2, 16, 840, 1, 101, 3, 4, 3, 1}); + case 0x4C806: + return if_match(req, "DSA/SHA-256", {2, 16, 840, 1, 101, 3, 4, 3, 2}); + case 0x4D740: + return if_match(req, "X509v3.AnyPolicy", {2, 5, 29, 32, 0}); + case 0x4DE49: + return if_match(req, "RSA/PKCS1v15(SHA-512)", {1, 2, 840, 113549, 1, 1, 13}); + case 0x4ED5D: + return if_match(req, "CAST-128/CBC", {1, 2, 840, 113533, 7, 66, 10}); + case 0x4FCDC: + return if_match(req, "RSA", {1, 2, 840, 113549, 1, 1, 1}); + case 0x501CB: + return if_match(req, "ECDSA/SHA-224", {1, 2, 840, 10045, 4, 3, 1}); + case 0x50395: + return if_match(req, "GOST-34.10/GOST-R-34.11-94", {1, 2, 643, 2, 2, 3}); + case 0x504BE: + return if_match(req, "ECDSA/SHA-256", {1, 2, 840, 10045, 4, 3, 2}); + case 0x509C3: + return if_match(req, "brainpool192r1", {1, 3, 36, 3, 3, 2, 8, 1, 1, 3}); + case 0x509F9: + return if_match(req, "PKCS9.ContentType", {1, 2, 840, 113549, 1, 9, 3}); + case 0x50B26: + return if_match(req, "FrodoKEM-640-AES", {1, 3, 6, 1, 4, 1, 25258, 1, 15, 1}); + case 0x50D78: + return if_match(req, "x962_p192v2", {1, 2, 840, 10045, 3, 1, 2}); + case 0x50D79: + return if_match(req, "x962_p192v3", {1, 2, 840, 10045, 3, 1, 3}); + case 0x51DC6: + return if_match(req, "AES-128/OCB", {1, 3, 6, 1, 4, 1, 25258, 3, 2, 1}); + case 0x52DB6: + return if_match(req, "HMAC(SHA-224)", {1, 2, 840, 113549, 2, 8}); + case 0x53E11: + return if_match(req, "FrodoKEM-1344-SHAKE", {1, 3, 6, 1, 4, 1, 25258, 1, 14, 3}); + case 0x54012: + return if_match(req, "PKIX.TimeStamping", {1, 3, 6, 1, 5, 5, 7, 3, 8}); + case 0x5407A: + return if_match(req, "Serpent/CBC", {1, 3, 6, 1, 4, 1, 25258, 3, 1}); + case 0x5576D: + return if_match(req, "SphincsPlus-sha2-128f-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 2}); + case 0x55EF6: + return if_match(req, "AES-192/OCB", {1, 3, 6, 1, 4, 1, 25258, 3, 2, 2}); + case 0x55FFA: + return if_match(req, "ML-DSA-6x5", {2, 16, 840, 1, 101, 3, 4, 3, 18}); + case 0x56826: + return if_match(req, "brainpool320r1", {1, 3, 36, 3, 3, 2, 8, 1, 1, 9}); + case 0x56D0D: + return if_match(req, "SphincsPlus-shake-128f-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 2}); + case 0x57077: + return if_match(req, "XMSS-draft6", {1, 3, 6, 1, 4, 1, 25258, 1, 5}); + case 0x5818B: + return if_match(req, "ECGDSA/SHA-224", {1, 3, 36, 3, 3, 2, 5, 4, 3}); + case 0x5847E: + return if_match(req, "ECGDSA/SHA-256", {1, 3, 36, 3, 3, 2, 5, 4, 4}); + case 0x5898B: + return if_match(req, "SHA-512", {2, 16, 840, 1, 101, 3, 4, 2, 3}); + case 0x58991: + return if_match(req, "PKIX.OCSP.NoCheck", {1, 3, 6, 1, 5, 5, 7, 48, 1, 5}); + case 0x59717: + return if_match(req, "X509v3.SubjectKeyIdentifier", {2, 5, 29, 14}); + case 0x5A1E1: + return if_match(req, "PKCS12.KeyBag", {1, 2, 840, 113549, 1, 12, 10, 1, 1}); + case 0x5A570: + return if_match(req, "X520.CommonName", {2, 5, 4, 3}); + case 0x5A990: + return if_match(req, "ECDSA/SHA-3(256)", {2, 16, 840, 1, 101, 3, 4, 3, 10}); + case 0x5AB0E: + return if_match(req, "SphincsPlus-sha2-256s-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 5}); + case 0x5AC4A: + return if_match(req, "X520.Surname", {2, 5, 4, 4}); + case 0x5AF2C: + return if_match(req, "ClassicMcEliece_8192128pc", {1, 3, 6, 1, 4, 1, 25258, 1, 18, 5}); + case 0x5BC39: + return if_match(req, "X509v3.KeyUsage", {2, 5, 29, 15}); + case 0x5BDDB: + return if_match(req, "numsp256d1", {1, 3, 6, 1, 4, 1, 25258, 4, 1}); + case 0x5C0AE: + return if_match(req, "SphincsPlus-shake-256s-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 5}); + case 0x5C10E: + return if_match(req, "DSA/SHA-384", {2, 16, 840, 1, 101, 3, 4, 3, 3}); + case 0x5CFE5: + return if_match(req, "PKCS9.X509Certificate", {1, 2, 840, 113549, 1, 9, 22, 1}); + case 0x5D1CF: + return if_match(req, "X520.SerialNumber", {2, 5, 4, 5}); + case 0x5D375: + return if_match(req, "SM4/OCB", {1, 2, 156, 10197, 1, 104, 100}); + case 0x5DD49: + return if_match(req, "AES-128/CBC", {2, 16, 840, 1, 101, 3, 4, 1, 2}); + case 0x5DE4E: + return if_match(req, "AES-128/CCM", {2, 16, 840, 1, 101, 3, 4, 1, 7}); + case 0x5DF23: + return if_match(req, "HMAC(SHA-512-256)", {1, 2, 840, 113549, 2, 13}); + case 0x5ED04: + return if_match(req, "SM2", {1, 2, 156, 10197, 1, 301, 1}); + case 0x5ED05: + return if_match(req, "SM3", {1, 2, 156, 10197, 1, 401}); + case 0x5FDC6: + return if_match(req, "ECDSA/SHA-384", {1, 2, 840, 10045, 4, 3, 3}); + case 0x6199F: + return if_match(req, "SHA-3(224)", {2, 16, 840, 1, 101, 3, 4, 2, 7}); + case 0x61E79: + return if_match(req, "AES-192/CBC", {2, 16, 840, 1, 101, 3, 4, 1, 22}); + case 0x61F7E: + return if_match(req, "AES-192/CCM", {2, 16, 840, 1, 101, 3, 4, 1, 27}); + case 0x64947: + return if_match(req, "OpenPGP.Ed25519", {1, 3, 6, 1, 4, 1, 11591, 15, 1}); + case 0x652E7: + return if_match(req, "sm2p256v1", {1, 2, 156, 10197, 1, 301}); + case 0x6697B: + return if_match(req, "FrodoKEM-1344-AES", {1, 3, 6, 1, 4, 1, 25258, 1, 15, 3}); + case 0x67B2C: + return if_match(req, "X520.State", {2, 5, 4, 8}); + case 0x67B9B: + return if_match(req, "HMAC(SHA-384)", {1, 2, 840, 113549, 2, 10}); + case 0x67D86: + return if_match(req, "ECGDSA/SHA-384", {1, 3, 36, 3, 3, 2, 5, 4, 5}); + case 0x68A0B: + return if_match(req, "Camellia-128/OCB", {1, 3, 6, 1, 4, 1, 25258, 3, 2, 6}); + case 0x68E33: + return if_match(req, "PKCS9.ExtensionRequest", {1, 2, 840, 113549, 1, 9, 14}); + case 0x69126: + return if_match(req, "X509v3.SubjectAlternativeName", {2, 5, 29, 17}); + case 0x692F8: + return if_match(req, "SM4/CBC", {1, 2, 156, 10197, 1, 104, 2}); + case 0x695E1: + return if_match(req, "Dilithium-4x4-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 9, 1}); + case 0x696DC: + return if_match(req, "PKIX.IpAddrBlocks", {1, 3, 6, 1, 5, 5, 7, 1, 7}); + case 0x6A7CA: + return if_match(req, "ECDSA", {1, 2, 840, 10045, 2, 1}); + case 0x6BD26: + return if_match(req, "GOST.INN", {1, 2, 643, 3, 131, 1, 1}); + case 0x6CB3B: + return if_match(req, "Camellia-192/OCB", {1, 3, 6, 1, 4, 1, 25258, 3, 2, 7}); + case 0x6E602: + return if_match(req, "Dilithium-8x7-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 9, 3}); + case 0x6F0C2: + return if_match(req, "RSA/PKCS1v15(SHA-224)", {1, 2, 840, 113549, 1, 1, 14}); + case 0x6F9F8: + return if_match(req, "PKCS12.SafeContentsBag", {1, 2, 840, 113549, 1, 12, 10, 1, 6}); + case 0x6FB26: + return if_match(req, "PKIX.AuthorityInformationAccess", {1, 3, 6, 1, 5, 5, 7, 1, 1}); + case 0x70BB6: + return if_match(req, "brainpool384r1", {1, 3, 36, 3, 3, 2, 8, 1, 1, 11}); + case 0x70EA6: + return if_match(req, "PKCS12.PKCS8ShroudedKeyBag", {1, 2, 840, 113549, 1, 12, 10, 1, 2}); + case 0x71EB3: + return if_match(req, "SphincsPlus-haraka-128f-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 2}); + case 0x7382C: + return if_match(req, "ML-KEM-1024", {2, 16, 840, 1, 101, 3, 4, 4, 3}); + case 0x743BD: + return if_match(req, "AES-256/OCB", {1, 3, 6, 1, 4, 1, 25258, 3, 2, 3}); + case 0x7498E: + return if_match(req, "Camellia-128/CBC", {1, 2, 392, 200011, 61, 1, 1, 1, 2}); + case 0x74C2E: + return if_match(req, "ML-DSA-8x7", {2, 16, 840, 1, 101, 3, 4, 3, 19}); + case 0x7505F: + return if_match(req, "PKIX.XMPPAddr", {1, 3, 6, 1, 5, 5, 7, 8, 5}); + case 0x7517A: + return if_match(req, "RSA/PKCS1v15(MD2)", {1, 2, 840, 113549, 1, 1, 2}); + case 0x7546B: + return if_match(req, "RSA/PKCS1v15(MD5)", {1, 2, 840, 113549, 1, 1, 4}); + case 0x75921: + return if_match(req, "ClassicMcEliece_348864f", {1, 3, 6, 1, 4, 1, 22554, 5, 1, 2}); + case 0x76784: + return if_match(req, "SHA-3(384)", {2, 16, 840, 1, 101, 3, 4, 2, 9}); + case 0x768FD: + return if_match(req, "PKCS9.LocalKeyId", {1, 2, 840, 113549, 1, 9, 21}); + case 0x76A19: + return if_match(req, "brainpool512r1", {1, 3, 36, 3, 3, 2, 8, 1, 1, 13}); + case 0x77254: + return if_match(req, "SphincsPlus-haraka-256s-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 5}); + case 0x77ADC: + return if_match(req, "secp224k1", {1, 3, 132, 0, 32}); + case 0x781B9: + return if_match(req, "secp224r1", {1, 3, 132, 0, 33}); + case 0x78ABE: + return if_match(req, "Camellia-192/CBC", {1, 2, 392, 200011, 61, 1, 1, 1, 3}); + case 0x792F2: + return if_match(req, "ClassicMcEliece_6688128pc", {1, 3, 6, 1, 4, 1, 25258, 1, 18, 1}); + case 0x7A661: + return if_match(req, "DSA/SHA-512", {2, 16, 840, 1, 101, 3, 4, 3, 4}); + case 0x7A977: + return if_match(req, "X509v3.ExtendedKeyUsage", {2, 5, 29, 37}); + case 0x7AE67: + return if_match(req, "SM2_Enc", {1, 2, 156, 10197, 1, 301, 3}); + case 0x7B602: + return if_match(req, "Twofish/OCB", {1, 3, 6, 1, 4, 1, 25258, 3, 2, 5}); + case 0x7B9A1: + return if_match(req, "SphincsPlus-sha2-192s-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 3}); + case 0x7BB0A: + return if_match(req, "SLH-DSA-SHAKE-256f", {2, 16, 840, 1, 101, 3, 4, 3, 31}); + case 0x7BB17: + return if_match(req, "SLH-DSA-SHAKE-256s", {2, 16, 840, 1, 101, 3, 4, 3, 30}); + case 0x7BCF3: + return if_match(req, "PKIX.EmailProtection", {1, 3, 6, 1, 5, 5, 7, 3, 4}); + case 0x7CC2C: + return if_match(req, "SHA-512-256", {2, 16, 840, 1, 101, 3, 4, 2, 6}); + case 0x7CF41: + return if_match(req, "SphincsPlus-shake-192s-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 3}); + case 0x7DB91: + return if_match(req, "GOST-34.10", {1, 2, 643, 2, 2, 19}); + case 0x7E319: + return if_match(req, "ECDSA/SHA-512", {1, 2, 840, 10045, 4, 3, 4}); + case 0x7E874: + return if_match(req, "ClassicMcEliece_6688128f", {1, 3, 6, 1, 4, 1, 22554, 5, 1, 6}); + case 0x7EAAF: + return if_match(req, "eFrodoKEM-640-SHAKE", {1, 3, 6, 1, 4, 1, 25258, 1, 16, 1}); + case 0x7F51F: + return if_match(req, "PKIX.IPsecTunnel", {1, 3, 6, 1, 5, 5, 7, 3, 6}); + case 0x80272: + return if_match(req, "X520.Organization", {2, 5, 4, 10}); + case 0x80340: + return if_match(req, "AES-256/CBC", {2, 16, 840, 1, 101, 3, 4, 1, 42}); + case 0x80445: + return if_match(req, "AES-256/CCM", {2, 16, 840, 1, 101, 3, 4, 1, 47}); + case 0x811F7: + return if_match(req, "HMAC(SHA-256)", {1, 2, 840, 113549, 2, 9}); + case 0x82434: + return if_match(req, "PKCS9.X509CRL", {1, 2, 840, 113549, 1, 9, 23, 1}); + case 0x82B47: + return if_match(req, "Threefish-512/CBC", {1, 3, 6, 1, 4, 1, 25258, 3, 2}); + case 0x83EA7: + return if_match(req, "RSA/PKCS1v15(SHA-384)", {1, 2, 840, 113549, 1, 1, 12}); + case 0x84596: + return if_match(req, "eFrodoKEM-640-AES", {1, 3, 6, 1, 4, 1, 25258, 1, 17, 1}); + case 0x8469F: + return if_match(req, "ClassicMcEliece_6960119pcf", {1, 3, 6, 1, 4, 1, 25258, 1, 18, 4}); + case 0x84CA4: + return if_match(req, "secp256k1", {1, 3, 132, 0, 10}); + case 0x85381: + return if_match(req, "secp256r1", {1, 2, 840, 10045, 3, 1, 7}); + case 0x854FC: + return if_match(req, "PKIX.IPsecUser", {1, 3, 6, 1, 5, 5, 7, 3, 7}); + case 0x85F51: + return if_match(req, "Serpent/SIV", {1, 3, 6, 1, 4, 1, 25258, 3, 4, 4}); + case 0x862D9: + return if_match(req, "ECGDSA/SHA-512", {1, 3, 36, 3, 3, 2, 5, 4, 6}); + case 0x87585: + return if_match(req, "Twofish/CBC", {1, 3, 6, 1, 4, 1, 25258, 3, 3}); + case 0x877D1: + return if_match(req, "PKCS9.EmailAddress", {1, 2, 840, 113549, 1, 9, 1}); + case 0x87D27: + return if_match(req, "PKIX.CertificateAuthorityIssuers", {1, 3, 6, 1, 5, 5, 7, 48, 2}); + case 0x87E42: + return if_match(req, "X509v3.AuthorityKeyIdentifier", {2, 5, 29, 35}); + case 0x889B1: + return if_match(req, "ECDSA/SHA-1", {1, 2, 840, 10045, 4, 1}); + case 0x89658: + return if_match(req, "PBE-PKCS5v20", {1, 2, 840, 113549, 1, 5, 13}); + case 0x8976D: + return if_match(req, "PKCS9.MessageDigest", {1, 2, 840, 113549, 1, 9, 4}); + case 0x8B002: + return if_match(req, "Camellia-256/OCB", {1, 3, 6, 1, 4, 1, 25258, 3, 2, 8}); + case 0x8B935: + return if_match(req, "ClassicMcEliece_6688128", {1, 3, 6, 1, 4, 1, 22554, 5, 1, 5}); + case 0x8CE3D: + return if_match(req, "PKCS9.ChallengePassword", {1, 2, 840, 113549, 1, 9, 7}); + case 0x8D45C: + return if_match(req, "ECKCDSA", {1, 0, 14888, 3, 0, 5}); + case 0x8E0C1: + return if_match(req, "X509v3.CertificatePolicies", {2, 5, 29, 32}); + case 0x8E39A: + return if_match(req, "HSS-LMS-Private-Key", {1, 3, 6, 1, 4, 1, 25258, 1, 13}); + case 0x8EC51: + return if_match(req, "Kyber-768-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 7, 2}); + case 0x8F94A: + return if_match(req, "Dilithium-6x5-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 9, 2}); + case 0x8FC20: + return if_match(req, "AES-128/SIV", {1, 3, 6, 1, 4, 1, 25258, 3, 4, 1}); + case 0x8FDE0: + return if_match(req, "SHA-3(256)", {2, 16, 840, 1, 101, 3, 4, 2, 8}); + case 0x919E3: + return if_match(req, "Serpent/GCM", {1, 3, 6, 1, 4, 1, 25258, 3, 101}); + case 0x91C1A: + return if_match(req, "X25519", {1, 3, 101, 110}); + case 0x91DC4: + return if_match(req, "McEliece", {1, 3, 6, 1, 4, 1, 25258, 1, 3}); + case 0x93467: + return if_match(req, "Dilithium-6x5-AES-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 10, 2}); + case 0x93D50: + return if_match(req, "AES-192/SIV", {1, 3, 6, 1, 4, 1, 25258, 3, 4, 2}); + case 0x95166: + return if_match(req, "SLH-DSA-SHAKE-128f", {2, 16, 840, 1, 101, 3, 4, 3, 27}); + case 0x95173: + return if_match(req, "SLH-DSA-SHAKE-128s", {2, 16, 840, 1, 101, 3, 4, 3, 26}); + case 0x952D6: + return if_match(req, "PKIX.OCSP", {1, 3, 6, 1, 5, 5, 7, 48, 1}); + case 0x959B9: + return if_match(req, "PKIX.IPsecEndSystem", {1, 3, 6, 1, 5, 5, 7, 3, 5}); + case 0x96F85: + return if_match(req, "Camellia-256/CBC", {1, 2, 392, 200011, 61, 1, 1, 1, 4}); + case 0x97D5E: + return if_match(req, "HMAC(SHA-1)", {1, 2, 840, 113549, 2, 7}); + case 0x9805C: + return if_match(req, "SEED/CBC", {1, 2, 410, 200004, 1, 4}); + case 0x980E7: + return if_match(req, "SphincsPlus-haraka-192s-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 3}); + case 0x980F5: + return if_match(req, "GOST.SubjectSigningTool", {1, 2, 643, 100, 111}); + case 0x98B03: + return if_match(req, "XMSS", {0, 4, 0, 127, 0, 15, 1, 1, 13, 0}); + case 0x9A6B2: + return if_match(req, "ECKCDSA/SHA-1", {1, 2, 410, 200004, 1, 100, 4, 3}); + case 0x9B1CF: + return if_match(req, "SM4/SIV", {1, 3, 6, 1, 4, 1, 25258, 3, 4, 9}); + case 0x9B6B2: + return if_match(req, "AES-128/GCM", {2, 16, 840, 1, 101, 3, 4, 1, 6}); + case 0x9B6BB: + return if_match(req, "X520.OrganizationalUnit", {2, 5, 4, 11}); + case 0x9B851: + return if_match(req, "OpenPGP.Curve25519", {1, 3, 6, 1, 4, 1, 3029, 1, 5, 1}); + case 0x9C80B: + return if_match(req, "SLH-DSA-SHA2-192f", {2, 16, 840, 1, 101, 3, 4, 3, 23}); + case 0x9C818: + return if_match(req, "SLH-DSA-SHA2-192s", {2, 16, 840, 1, 101, 3, 4, 3, 22}); + case 0x9CD2B: + return if_match(req, "Scrypt", {1, 3, 6, 1, 4, 1, 11591, 4, 11}); + case 0x9CDE1: + return if_match(req, "GOST-34.10-2012-256/SHA-256", {1, 3, 6, 1, 4, 1, 25258, 1, 6, 1}); + case 0x9CF73: + return if_match(req, "ClassicMcEliece_460896f", {1, 3, 6, 1, 4, 1, 22554, 5, 1, 4}); + case 0x9D354: + return if_match(req, "RIPEMD-160", {1, 3, 36, 3, 2, 1}); + case 0x9D503: + return if_match(req, "RSA/PKCS1v15(SHA-256)", {1, 2, 840, 113549, 1, 1, 11}); + case 0x9EC88: + return if_match(req, "DSA/SHA-3(512)", {2, 16, 840, 1, 101, 3, 4, 3, 8}); + case 0x9EF36: + return if_match(req, "ClassicMcEliece_6960119", {1, 3, 6, 1, 4, 1, 22554, 5, 1, 7}); + case 0x9F764: + return if_match(req, "X448", {1, 3, 101, 111}); + case 0x9F7E2: + return if_match(req, "AES-192/GCM", {2, 16, 840, 1, 101, 3, 4, 1, 26}); + case 0x9F9C5: + return if_match(req, "ClassicMcEliece_6688128pcf", {1, 3, 6, 1, 4, 1, 25258, 1, 18, 2}); + case 0xA0805: + return if_match(req, "PKCS9.SDSICertificate", {1, 2, 840, 113549, 1, 9, 22, 2}); + case 0xA2B5B: + return if_match(req, "X509v3.CRLNumber", {2, 5, 29, 20}); + case 0xA3005: + return if_match(req, "X520.Title", {2, 5, 4, 12}); + case 0xA323F: + return if_match(req, "X509v3.NameConstraints", {2, 5, 29, 30}); + case 0xA3C55: + return if_match(req, "X520.Pseudonym", {2, 5, 4, 65}); + case 0xA4809: + return if_match(req, "SphincsPlus-sha2-256f-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 6}); + case 0xA57AF: + return if_match(req, "secp521r1", {1, 3, 132, 0, 35}); + case 0xA5DA9: + return if_match(req, "SphincsPlus-shake-256f-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 6}); + case 0xA6865: + return if_match(req, "Camellia-128/SIV", {1, 3, 6, 1, 4, 1, 25258, 3, 4, 6}); + case 0xA6C61: + return if_match(req, "SM4/GCM", {1, 2, 156, 10197, 1, 104, 8}); + case 0xA8439: + return if_match(req, "PKCS12.CertBag", {1, 2, 840, 113549, 1, 12, 10, 1, 3}); + case 0xA9061: + return if_match(req, "Kyber-768-90s-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 11, 2}); + case 0xAA995: + return if_match(req, "Camellia-192/SIV", {1, 3, 6, 1, 4, 1, 25258, 3, 4, 7}); + case 0xAAE2B: + return if_match(req, "Dilithium-8x7-AES-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 10, 3}); + case 0xABCED: + return if_match(req, "GOST.IssuerSigningTool", {1, 2, 643, 100, 112}); + case 0xABD24: + return if_match(req, "RSA/OAEP", {1, 2, 840, 113549, 1, 1, 7}); + case 0xAC2EC: + return if_match(req, "Streebog-256", {1, 2, 643, 7, 1, 1, 2, 2}); + case 0xAC3DD: + return if_match(req, "Certificate Comment", {2, 16, 840, 1, 113730, 1, 13}); + case 0xAC511: + return if_match(req, "PBE-SHA1-3DES", {1, 2, 840, 113549, 1, 12, 1, 3}); + case 0xAE6FE: + return if_match(req, "PKIX.ClientAuth", {1, 3, 6, 1, 5, 5, 7, 3, 2}); + case 0xAE8D3: + return if_match(req, "ClassicMcEliece_8192128pcf", {1, 3, 6, 1, 4, 1, 25258, 1, 18, 6}); + case 0xAF476: + return if_match(req, "ECDH", {1, 3, 132, 1, 12}); + case 0xAFA6A: + return if_match(req, "RSA/PKCS1v15(SHA-3(384))", {2, 16, 840, 1, 101, 3, 4, 3, 15}); + case 0xB2217: + return if_match(req, "AES-256/SIV", {1, 3, 6, 1, 4, 1, 25258, 3, 4, 3}); + case 0xB22F7: + return if_match(req, "Camellia-128/GCM", {0, 3, 4401, 5, 3, 1, 9, 6}); + case 0xB23DE: + return if_match(req, "X520.Locality", {2, 5, 4, 7}); + case 0xB2FBD: + return if_match(req, "ECKCDSA/SHA-224", {1, 2, 410, 200004, 1, 100, 4, 4}); + case 0xB32B0: + return if_match(req, "ECKCDSA/SHA-256", {1, 2, 410, 200004, 1, 100, 4, 5}); + case 0xB360E: + return if_match(req, "eFrodoKEM-976-SHAKE", {1, 3, 6, 1, 4, 1, 25258, 1, 16, 2}); + case 0xB4368: + return if_match(req, "ECGDSA/SHA-1", {1, 3, 36, 3, 3, 2, 5, 4, 2}); + case 0xB58CD: + return if_match(req, "RSA/PKCS1v15(SHA-3(512))", {2, 16, 840, 1, 101, 3, 4, 3, 16}); + case 0xB6427: + return if_match(req, "Camellia-192/GCM", {0, 3, 4401, 5, 3, 1, 9, 26}); + case 0xB7102: + return if_match(req, "brainpool224r1", {1, 3, 36, 3, 3, 2, 8, 1, 1, 5}); + case 0xB710D: + return if_match(req, "X509v3.CRLIssuingDistributionPoint", {2, 5, 29, 28}); + case 0xB72D4: + return if_match(req, "Microsoft UPN", {1, 3, 6, 1, 4, 1, 311, 20, 2, 3}); + case 0xB73A5: + return if_match(req, "RSA/PSS", {1, 2, 840, 113549, 1, 1, 10}); + case 0xB84B3: + return if_match(req, "PKIX.CodeSigning", {1, 3, 6, 1, 5, 5, 7, 3, 3}); + case 0xB8CB9: + return if_match(req, "GOST-34.10-2012-256", {1, 2, 643, 7, 1, 1, 1, 1}); + case 0xB945C: + return if_match(req, "Twofish/SIV", {1, 3, 6, 1, 4, 1, 25258, 3, 4, 5}); + case 0xB94E4: + return if_match(req, "gost_512A", {1, 2, 643, 7, 1, 2, 1, 2, 1}); + case 0xB94E5: + return if_match(req, "gost_512B", {1, 2, 643, 7, 1, 2, 1, 2, 2}); + case 0xBA1D8: + return if_match(req, "X520.StreetAddress", {2, 5, 4, 9}); + case 0xBCB45: + return if_match(req, "PKCS12.CRLBag", {1, 2, 840, 113549, 1, 12, 10, 1, 4}); + case 0xBCC82: + return if_match(req, "x962_p239v1", {1, 2, 840, 10045, 3, 1, 4}); + case 0xBCC83: + return if_match(req, "x962_p239v2", {1, 2, 840, 10045, 3, 1, 5}); + case 0xBCC84: + return if_match(req, "x962_p239v3", {1, 2, 840, 10045, 3, 1, 6}); + case 0xBD92B: + return if_match(req, "X509v3.HoldInstructionCode", {2, 5, 29, 23}); + case 0xBDCA9: + return if_match(req, "AES-256/GCM", {2, 16, 840, 1, 101, 3, 4, 1, 46}); + case 0xBE48D: + return if_match(req, "PKIX.OCSP.BasicResponse", {1, 3, 6, 1, 5, 5, 7, 48, 1, 1}); + case 0xBF71E: + return if_match(req, "Kyber-1024-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 7, 3}); + case 0xBFF01: + return if_match(req, "DSA/SHA-3(224)", {2, 16, 840, 1, 101, 3, 4, 3, 5}); + case 0xC0F4F: + return if_match(req, "SphincsPlus-haraka-256f-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 6}); + case 0xC1875: + return if_match(req, "SHA-1", {1, 3, 14, 3, 2, 26}); + case 0xC28D1: + return if_match(req, "PKIX.OCSPSigning", {1, 3, 6, 1, 5, 5, 7, 3, 9}); + case 0xC42CA: + return if_match(req, "brainpool256r1", {1, 3, 36, 3, 3, 2, 8, 1, 1, 7}); + default: + return {}; + } +} + +std::unordered_map OID_Map::load_oid2str_map() { + return { + {OID{2, 5, 8, 1, 1}, "RSA"}, + {OID{1, 3, 6, 1, 4, 1, 8301, 3, 1, 2, 9, 0, 38}, "secp521r1"}, + {OID{1, 2, 643, 2, 2, 35, 1}, "gost_256A"}, + {OID{1, 2, 643, 2, 2, 36, 0}, "gost_256A"}, + }; +} + +std::unordered_map OID_Map::load_str2oid_map() { + return { + {"Curve25519", OID{1, 3, 101, 110}}, + {"SM2_Sig", OID{1, 2, 156, 10197, 1, 301, 1}}, + {"RSA/EMSA3(MD2)", OID{1, 2, 840, 113549, 1, 1, 2}}, + {"RSA/EMSA3(MD5)", OID{1, 2, 840, 113549, 1, 1, 4}}, + {"RSA/EMSA3(SHA-1)", OID{1, 2, 840, 113549, 1, 1, 5}}, + {"RSA/EMSA3(SHA-256)", OID{1, 2, 840, 113549, 1, 1, 11}}, + {"RSA/EMSA3(SHA-384)", OID{1, 2, 840, 113549, 1, 1, 12}}, + {"RSA/EMSA3(SHA-512)", OID{1, 2, 840, 113549, 1, 1, 13}}, + {"RSA/EMSA3(SHA-224)", OID{1, 2, 840, 113549, 1, 1, 14}}, + {"RSA/EMSA3(SHA-512-256)", OID{1, 2, 840, 113549, 1, 1, 16}}, + {"RSA/EMSA3(SHA-3(224))", OID{2, 16, 840, 1, 101, 3, 4, 3, 13}}, + {"RSA/EMSA3(SHA-3(256))", OID{2, 16, 840, 1, 101, 3, 4, 3, 14}}, + {"RSA/EMSA3(SHA-3(384))", OID{2, 16, 840, 1, 101, 3, 4, 3, 15}}, + {"RSA/EMSA3(SHA-3(512))", OID{2, 16, 840, 1, 101, 3, 4, 3, 16}}, + {"RSA/EMSA3(SM3)", OID{1, 2, 156, 10197, 1, 504}}, + {"RSA/EMSA3(RIPEMD-160)", OID{1, 3, 36, 3, 3, 1, 2}}, + {"RSA/EMSA4", OID{1, 2, 840, 113549, 1, 1, 10}}, + {"PBES2", OID{1, 2, 840, 113549, 1, 5, 13}}, + }; +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/base/buf_comp.cpp botan3-3.12.0+dfsg/src/lib/base/buf_comp.cpp --- botan3-3.7.1+dfsg/src/lib/base/buf_comp.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/base/buf_comp.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,9 +7,14 @@ #include #include +#include namespace Botan { +void Buffered_Computation::update(std::string_view str) { + add_data(as_span_of_bytes(str)); +} + void Buffered_Computation::update_be(uint16_t val) { uint8_t inb[sizeof(val)]; store_be(val, inb); @@ -46,4 +51,9 @@ add_data({inb, sizeof(inb)}); } +void Buffered_Computation::final(std::span out) { + BOTAN_ARG_CHECK(out.size() >= output_length(), "provided output buffer has insufficient capacity"); + final_result(out); +} + } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/base/buf_comp.h botan3-3.12.0+dfsg/src/lib/base/buf_comp.h --- botan3-3.7.1+dfsg/src/lib/base/buf_comp.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/base/buf_comp.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,7 +9,6 @@ #define BOTAN_BUFFERED_COMPUTATION_H_ #include -#include #include #include #include @@ -20,7 +19,7 @@ * This class represents any kind of computation which uses an internal * state, such as hash functions or MACs */ -class BOTAN_PUBLIC_API(2, 0) Buffered_Computation { +class BOTAN_PUBLIC_API(2, 0) Buffered_Computation /* NOLINT(*special-member-functions) */ { public: /** * @return length of the output of this function in bytes @@ -53,7 +52,7 @@ * @param str the input to process as a std::string_view. Will be interpreted * as a byte array based on the strings encoding. */ - void update(std::string_view str) { add_data({cast_char_ptr_to_uint8(str.data()), str.size()}); } + void update(std::string_view str); /** * Process a single byte. @@ -83,10 +82,7 @@ std::vector final_stdvec() { return final>(); } - void final(std::span out) { - BOTAN_ARG_CHECK(out.size() >= output_length(), "provided output buffer has insufficient capacity"); - final_result(out); - } + void final(std::span out); template void final(T& out) { diff -Nru botan3-3.7.1+dfsg/src/lib/base/secmem.h botan3-3.12.0+dfsg/src/lib/base/secmem.h --- botan3-3.7.1+dfsg/src/lib/base/secmem.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/base/secmem.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,23 +10,27 @@ #include #include // IWYU pragma: export -#include -#include +#include #include #include // IWYU pragma: export +#if !defined(BOTAN_IS_BEING_BUILT) && !defined(BOTAN_DISABLE_DEPRECATED_FEATURES) + // TODO(Botan4) remove this + #include +#endif + namespace Botan { template #if !defined(_ITERATOR_DEBUG_LEVEL) || _ITERATOR_DEBUG_LEVEL == 0 /* - * Assert exists to prevent someone from doing something that will + * Check exists to prevent someone from doing something that will * probably crash anyway (like secure_vector where ~non_POD_t * deletes a member pointer which was zeroed before it ran). * MSVC in debug mode uses non-integral proxy types in container types * like std::vector, thus we disable the check there. */ - requires std::is_integral::value || std::is_enum::value + requires std::is_integral_v || std::is_enum_v #endif class secure_allocator { @@ -37,10 +41,13 @@ secure_allocator() noexcept = default; secure_allocator(const secure_allocator&) noexcept = default; secure_allocator& operator=(const secure_allocator&) noexcept = default; + secure_allocator(secure_allocator&&) noexcept = default; + secure_allocator& operator=(secure_allocator&&) noexcept = default; + ~secure_allocator() noexcept = default; template - secure_allocator(const secure_allocator&) noexcept {} + explicit secure_allocator(const secure_allocator& /*other*/) noexcept {} T* allocate(std::size_t n) { return static_cast(allocate_memory(n, sizeof(T))); } @@ -48,19 +55,22 @@ }; template -inline bool operator==(const secure_allocator&, const secure_allocator&) { +inline bool operator==(const secure_allocator& /*a*/, const secure_allocator& /*b*/) { return true; } template -inline bool operator!=(const secure_allocator&, const secure_allocator&) { +inline bool operator!=(const secure_allocator& /*a*/, const secure_allocator& /*b*/) { return false; } template using secure_vector = std::vector>; + +#if !defined(BOTAN_IS_BEING_BUILT) && !defined(BOTAN_DISABLE_DEPRECATED_FEATURES) template using secure_deque = std::deque>; +#endif // For better compatibility with 1.10 API template @@ -76,6 +86,8 @@ return std::vector(in.begin(), in.end()); } +// TODO(Botan4) remove these += operators entirely + template std::vector& operator+=(std::vector& out, const std::vector& in) { out.insert(out.end(), in.begin(), in.end()); @@ -83,6 +95,12 @@ } template +std::vector& operator+=(std::vector& out, std::span in) { + out.insert(out.end(), in.begin(), in.end()); + return out; +} + +template std::vector& operator+=(std::vector& out, T in) { out.push_back(in); return out; @@ -102,15 +120,37 @@ /** * Zeroise the values; length remains unchanged +* +* Note this is not intended for cases where the compiler might elide +* the writes as being without side-effects; use secure_scrub_memory +* for that. +* +* TODO(Botan4): make these not-inlined and only for secure_vector, eg declare +* void zeroize(secure_vector& v); +* void zeroize(secure_vector& v); +* void zeroize(secure_vector& v); +* void zeroize(secure_vector& v); +* * @param vec the vector to zeroise */ template void zeroise(std::vector& vec) { - std::fill(vec.begin(), vec.end(), static_cast(0)); + for(size_t i = 0; i != vec.size(); ++i) { + vec[i] = static_cast(0); + } } /** * Zeroise the values then free the memory +* +* TODO(Botan4): make these not-inlined and only for secure_vector, eg declare +* void zap(secure_vector& v); +* void zap(secure_vector& v); +* void zap(secure_vector& v); +* void zap(secure_vector& v); +* +* [And maybe rename as well] +* * @param vec the vector to zeroise and free */ template diff -Nru botan3-3.7.1+dfsg/src/lib/base/sym_algo.cpp botan3-3.12.0+dfsg/src/lib/base/sym_algo.cpp --- botan3-3.7.1+dfsg/src/lib/base/sym_algo.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/base/sym_algo.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,9 +7,14 @@ #include #include +#include namespace Botan { +void SymmetricAlgorithm::set_key(const OctetString& key) { + set_key(std::span{key.begin(), key.length()}); +} + void SymmetricAlgorithm::throw_key_not_set_error() const { throw Key_Not_Set(name()); } diff -Nru botan3-3.7.1+dfsg/src/lib/base/sym_algo.h botan3-3.12.0+dfsg/src/lib/base/sym_algo.h --- botan3-3.7.1+dfsg/src/lib/base/sym_algo.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/base/sym_algo.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,13 +8,14 @@ #ifndef BOTAN_SYMMETRIC_ALGORITHM_H_ #define BOTAN_SYMMETRIC_ALGORITHM_H_ -#include #include - #include +#include namespace Botan { +class OctetString; + /** * Represents the length requirements on an algorithm key */ @@ -33,7 +34,7 @@ * @param k_mod the number of bytes the key must be a multiple of */ Key_Length_Specification(size_t min_k, size_t max_k, size_t k_mod = 1) : - m_min_keylen(min_k), m_max_keylen(max_k ? max_k : min_k), m_keylen_mod(k_mod) {} + m_min_keylen(min_k), m_max_keylen(max_k > 0 ? max_k : min_k), m_keylen_mod(k_mod) {} /** * @param length is a key length in bytes @@ -76,7 +77,12 @@ */ class BOTAN_PUBLIC_API(2, 0) SymmetricAlgorithm { public: + SymmetricAlgorithm() = default; virtual ~SymmetricAlgorithm() = default; + SymmetricAlgorithm(const SymmetricAlgorithm& other) = default; + SymmetricAlgorithm(SymmetricAlgorithm&& other) = default; + SymmetricAlgorithm& operator=(const SymmetricAlgorithm& other) = default; + SymmetricAlgorithm& operator=(SymmetricAlgorithm&& other) = default; /** * Reset the internal state. This includes not just the key, but @@ -110,7 +116,7 @@ * Set the symmetric key of this object. * @param key the SymmetricKey to be set. */ - void set_key(const SymmetricKey& key) { set_key(std::span{key.begin(), key.length()}); } + void set_key(const OctetString& key); /** * Set the symmetric key of this object. diff -Nru botan3-3.7.1+dfsg/src/lib/base/symkey.cpp botan3-3.12.0+dfsg/src/lib/base/symkey.cpp --- botan3-3.7.1+dfsg/src/lib/base/symkey.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/base/symkey.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -55,8 +55,8 @@ * Set the parity of each key byte to odd */ void OctetString::set_odd_parity() { - for(size_t j = 0; j != m_data.size(); ++j) { - m_data[j] = odd_parity_of(m_data[j]); + for(auto& b : m_data) { + b = odd_parity_of(b); } } @@ -87,7 +87,7 @@ } /* -* Unequality Operation for OctetStrings +* Inequality Operation for OctetStrings */ bool operator!=(const OctetString& s1, const OctetString& s2) { return !(s1 == s2); diff -Nru botan3-3.7.1+dfsg/src/lib/block/aes/aes.cpp botan3-3.12.0+dfsg/src/lib/block/aes/aes.cpp --- botan3-3.7.1+dfsg/src/lib/block/aes/aes.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/aes/aes.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,17 +8,24 @@ #include #include -#include #include #include #include -namespace Botan { +#if defined(BOTAN_HAS_CPUID) + #include +#endif #if defined(BOTAN_HAS_AES_POWER8) || defined(BOTAN_HAS_AES_ARMV8) || defined(BOTAN_HAS_AES_NI) #define BOTAN_HAS_HW_AES_SUPPORT #endif +#if defined(BOTAN_HAS_HW_AES_SUPPORT) + #include +#endif + +namespace Botan { + /* * One of three AES implementation strategies are used to get a constant time * implementation which is immune to common cache/timing based side channels: @@ -298,7 +305,7 @@ const uint32_t d3 = tinv12 ^ tinv13; const uint32_t sd1 = d1 ^ d3; const uint32_t sd0 = d0 ^ d2; - const uint32_t dl = d0 ^ d1; + const uint32_t dl = d0 ^ d1; // NOLINT(misc-confusable-identifiers) const uint32_t dh = d2 ^ d3; const uint32_t dd = sd0 ^ sd1; const uint32_t abcd3 = dh & bh; @@ -408,43 +415,43 @@ inline void shift_rows(uint32_t B[8]) { // 3 0 1 2 7 4 5 6 10 11 8 9 14 15 12 13 17 18 19 16 21 22 23 20 24 25 26 27 28 29 30 31 -#if defined(BOTAN_TARGET_CPU_HAS_NATIVE_64BIT) - for(size_t i = 0; i != 8; i += 2) { - uint64_t x = (static_cast(B[i]) << 32) | B[i + 1]; - x = bit_permute_step(x, 0x0022331100223311, 2); - x = bit_permute_step(x, 0x0055005500550055, 1); - B[i] = static_cast(x >> 32); - B[i + 1] = static_cast(x); - } -#else - for(size_t i = 0; i != 8; ++i) { - uint32_t x = B[i]; - x = bit_permute_step(x, 0x00223311, 2); - x = bit_permute_step(x, 0x00550055, 1); - B[i] = x; + if constexpr(HasNative64BitRegisters) { + for(size_t i = 0; i != 8; i += 2) { + uint64_t x = (static_cast(B[i]) << 32) | B[i + 1]; + x = bit_permute_step(x, 0x0022331100223311, 2); + x = bit_permute_step(x, 0x0055005500550055, 1); + B[i] = static_cast(x >> 32); + B[i + 1] = static_cast(x); + } + } else { + for(size_t i = 0; i != 8; ++i) { + uint32_t x = B[i]; + x = bit_permute_step(x, 0x00223311, 2); + x = bit_permute_step(x, 0x00550055, 1); + B[i] = x; + } } -#endif } inline void inv_shift_rows(uint32_t B[8]) { // Inverse of shift_rows, just inverting the steps -#if defined(BOTAN_TARGET_CPU_HAS_NATIVE_64BIT) - for(size_t i = 0; i != 8; i += 2) { - uint64_t x = (static_cast(B[i]) << 32) | B[i + 1]; - x = bit_permute_step(x, 0x0055005500550055, 1); - x = bit_permute_step(x, 0x0022331100223311, 2); - B[i] = static_cast(x >> 32); - B[i + 1] = static_cast(x); - } -#else - for(size_t i = 0; i != 8; ++i) { - uint32_t x = B[i]; - x = bit_permute_step(x, 0x00550055, 1); - x = bit_permute_step(x, 0x00223311, 2); - B[i] = x; + if constexpr(HasNative64BitRegisters) { + for(size_t i = 0; i != 8; i += 2) { + uint64_t x = (static_cast(B[i]) << 32) | B[i + 1]; + x = bit_permute_step(x, 0x0055005500550055, 1); + x = bit_permute_step(x, 0x0022331100223311, 2); + B[i] = static_cast(x >> 32); + B[i + 1] = static_cast(x); + } + } else { + for(size_t i = 0; i != 8; ++i) { + uint32_t x = B[i]; + x = bit_permute_step(x, 0x00550055, 1); + x = bit_permute_step(x, 0x00223311, 2); + B[i] = x; + } } -#endif } inline void mix_columns(uint32_t B[8]) { @@ -580,10 +587,10 @@ uint32_t B[8] = {0}; - CT::poison(B, 8); - load_be(B, in, this_loop * 4); + CT::poison(B, 8); + for(size_t i = 0; i != 8; ++i) { B[i] ^= DK[i % 4]; } @@ -692,8 +699,9 @@ CT::poison(key, length); - EK.resize(length + 28); - DK.resize(length + 28); + const size_t KS_len = length + 28; + EK.resize(KS_len); + DK.resize(KS_len); for(size_t i = 0; i != X; ++i) { EK[i] = load_be(key, i); @@ -726,10 +734,8 @@ if(bswap_keys) { // HW AES on little endian needs the subkeys to be byte reversed - for(size_t i = 0; i != EK.size(); ++i) { + for(size_t i = 0; i != KS_len; ++i) { EK[i] = reverse_bytes(EK[i]); - } - for(size_t i = 0; i != DK.size(); ++i) { DK[i] = reverse_bytes(DK[i]); } } @@ -741,19 +747,19 @@ size_t aes_parallelism() { #if defined(BOTAN_HAS_AES_VAES) - if(CPUID::has_avx2_vaes()) { + if(CPUID::has(CPUID::Feature::AVX2_AES)) { return 8; // pipelined } #endif #if defined(BOTAN_HAS_HW_AES_SUPPORT) - if(CPUID::has_hw_aes()) { + if(CPUID::has(CPUID::Feature::HW_AES)) { return 4; // pipelined } #endif #if defined(BOTAN_HAS_AES_VPERM) - if(CPUID::has_vperm()) { + if(CPUID::has(CPUID::Feature::SIMD_4X32)) { return 2; // pipelined } #endif @@ -762,22 +768,22 @@ return 2; } -const char* aes_provider() { +std::string aes_provider() { #if defined(BOTAN_HAS_AES_VAES) - if(CPUID::has_avx2_vaes()) { - return "vaes"; + if(auto feat = CPUID::check(CPUID::Feature::AVX2_AES)) { + return *feat; } #endif #if defined(BOTAN_HAS_HW_AES_SUPPORT) - if(CPUID::has_hw_aes()) { - return "cpu"; + if(auto feat = CPUID::check(CPUID::Feature::HW_AES)) { + return *feat; } #endif #if defined(BOTAN_HAS_AES_VPERM) - if(CPUID::has_vperm()) { - return "vperm"; + if(auto feat = CPUID::check(CPUID::Feature::SIMD_4X32)) { + return *feat; } #endif @@ -826,19 +832,19 @@ assert_key_material_set(); #if defined(BOTAN_HAS_AES_VAES) - if(CPUID::has_avx2_vaes()) { + if(CPUID::has(CPUID::Feature::AVX2_AES)) { return x86_vaes_encrypt_n(in, out, blocks); } #endif #if defined(BOTAN_HAS_HW_AES_SUPPORT) - if(CPUID::has_hw_aes()) { + if(CPUID::has(CPUID::Feature::HW_AES)) { return hw_aes_encrypt_n(in, out, blocks); } #endif #if defined(BOTAN_HAS_AES_VPERM) - if(CPUID::has_vperm()) { + if(CPUID::has(CPUID::Feature::SIMD_4X32)) { return vperm_encrypt_n(in, out, blocks); } #endif @@ -850,19 +856,19 @@ assert_key_material_set(); #if defined(BOTAN_HAS_AES_VAES) - if(CPUID::has_avx2_vaes()) { + if(CPUID::has(CPUID::Feature::AVX2_AES)) { return x86_vaes_decrypt_n(in, out, blocks); } #endif #if defined(BOTAN_HAS_HW_AES_SUPPORT) - if(CPUID::has_hw_aes()) { + if(CPUID::has(CPUID::Feature::HW_AES)) { return hw_aes_decrypt_n(in, out, blocks); } #endif #if defined(BOTAN_HAS_AES_VPERM) - if(CPUID::has_vperm()) { + if(CPUID::has(CPUID::Feature::SIMD_4X32)) { return vperm_decrypt_n(in, out, blocks); } #endif @@ -872,25 +878,26 @@ void AES_128::key_schedule(std::span key) { #if defined(BOTAN_HAS_AES_NI) - if(CPUID::has_aes_ni()) { + if(CPUID::has(CPUID::Feature::AESNI)) { return aesni_key_schedule(key.data(), key.size()); } #endif #if defined(BOTAN_HAS_AES_VAES) - if(CPUID::has_avx2_vaes()) { - return aes_key_schedule(key.data(), key.size(), m_EK, m_DK, CPUID::is_little_endian()); + if(CPUID::has(CPUID::Feature::AVX2_AES)) { + return aes_key_schedule(key.data(), key.size(), m_EK, m_DK, true); } #endif #if defined(BOTAN_HAS_HW_AES_SUPPORT) - if(CPUID::has_hw_aes()) { - return aes_key_schedule(key.data(), key.size(), m_EK, m_DK, CPUID::is_little_endian()); + if(CPUID::has(CPUID::Feature::HW_AES)) { + constexpr bool is_little_endian = std::endian::native == std::endian::little; + return aes_key_schedule(key.data(), key.size(), m_EK, m_DK, is_little_endian); } #endif #if defined(BOTAN_HAS_AES_VPERM) - if(CPUID::has_vperm()) { + if(CPUID::has(CPUID::Feature::SIMD_4X32)) { return vperm_key_schedule(key.data(), key.size()); } #endif @@ -907,19 +914,19 @@ assert_key_material_set(); #if defined(BOTAN_HAS_AES_VAES) - if(CPUID::has_avx2_vaes()) { + if(CPUID::has(CPUID::Feature::AVX2_AES)) { return x86_vaes_encrypt_n(in, out, blocks); } #endif #if defined(BOTAN_HAS_HW_AES_SUPPORT) - if(CPUID::has_hw_aes()) { + if(CPUID::has(CPUID::Feature::HW_AES)) { return hw_aes_encrypt_n(in, out, blocks); } #endif #if defined(BOTAN_HAS_AES_VPERM) - if(CPUID::has_vperm()) { + if(CPUID::has(CPUID::Feature::SIMD_4X32)) { return vperm_encrypt_n(in, out, blocks); } #endif @@ -931,19 +938,19 @@ assert_key_material_set(); #if defined(BOTAN_HAS_AES_VAES) - if(CPUID::has_avx2_vaes()) { + if(CPUID::has(CPUID::Feature::AVX2_AES)) { return x86_vaes_decrypt_n(in, out, blocks); } #endif #if defined(BOTAN_HAS_HW_AES_SUPPORT) - if(CPUID::has_hw_aes()) { + if(CPUID::has(CPUID::Feature::HW_AES)) { return hw_aes_decrypt_n(in, out, blocks); } #endif #if defined(BOTAN_HAS_AES_VPERM) - if(CPUID::has_vperm()) { + if(CPUID::has(CPUID::Feature::SIMD_4X32)) { return vperm_decrypt_n(in, out, blocks); } #endif @@ -953,25 +960,26 @@ void AES_192::key_schedule(std::span key) { #if defined(BOTAN_HAS_AES_NI) - if(CPUID::has_aes_ni()) { + if(CPUID::has(CPUID::Feature::AESNI)) { return aesni_key_schedule(key.data(), key.size()); } #endif #if defined(BOTAN_HAS_AES_VAES) - if(CPUID::has_avx2_vaes()) { - return aes_key_schedule(key.data(), key.size(), m_EK, m_DK, CPUID::is_little_endian()); + if(CPUID::has(CPUID::Feature::AVX2_AES)) { + return aes_key_schedule(key.data(), key.size(), m_EK, m_DK, true); } #endif #if defined(BOTAN_HAS_HW_AES_SUPPORT) - if(CPUID::has_hw_aes()) { - return aes_key_schedule(key.data(), key.size(), m_EK, m_DK, CPUID::is_little_endian()); + if(CPUID::has(CPUID::Feature::HW_AES)) { + constexpr bool is_little_endian = std::endian::native == std::endian::little; + return aes_key_schedule(key.data(), key.size(), m_EK, m_DK, is_little_endian); } #endif #if defined(BOTAN_HAS_AES_VPERM) - if(CPUID::has_vperm()) { + if(CPUID::has(CPUID::Feature::SIMD_4X32)) { return vperm_key_schedule(key.data(), key.size()); } #endif @@ -988,19 +996,19 @@ assert_key_material_set(); #if defined(BOTAN_HAS_AES_VAES) - if(CPUID::has_avx2_vaes()) { + if(CPUID::has(CPUID::Feature::AVX2_AES)) { return x86_vaes_encrypt_n(in, out, blocks); } #endif #if defined(BOTAN_HAS_HW_AES_SUPPORT) - if(CPUID::has_hw_aes()) { + if(CPUID::has(CPUID::Feature::HW_AES)) { return hw_aes_encrypt_n(in, out, blocks); } #endif #if defined(BOTAN_HAS_AES_VPERM) - if(CPUID::has_vperm()) { + if(CPUID::has(CPUID::Feature::SIMD_4X32)) { return vperm_encrypt_n(in, out, blocks); } #endif @@ -1012,19 +1020,19 @@ assert_key_material_set(); #if defined(BOTAN_HAS_AES_VAES) - if(CPUID::has_avx2_vaes()) { + if(CPUID::has(CPUID::Feature::AVX2_AES)) { return x86_vaes_decrypt_n(in, out, blocks); } #endif #if defined(BOTAN_HAS_HW_AES_SUPPORT) - if(CPUID::has_hw_aes()) { + if(CPUID::has(CPUID::Feature::HW_AES)) { return hw_aes_decrypt_n(in, out, blocks); } #endif #if defined(BOTAN_HAS_AES_VPERM) - if(CPUID::has_vperm()) { + if(CPUID::has(CPUID::Feature::SIMD_4X32)) { return vperm_decrypt_n(in, out, blocks); } #endif @@ -1034,25 +1042,26 @@ void AES_256::key_schedule(std::span key) { #if defined(BOTAN_HAS_AES_NI) - if(CPUID::has_aes_ni()) { + if(CPUID::has(CPUID::Feature::AESNI)) { return aesni_key_schedule(key.data(), key.size()); } #endif #if defined(BOTAN_HAS_AES_VAES) - if(CPUID::has_avx2_vaes()) { - return aes_key_schedule(key.data(), key.size(), m_EK, m_DK, CPUID::is_little_endian()); + if(CPUID::has(CPUID::Feature::AVX2_AES)) { + return aes_key_schedule(key.data(), key.size(), m_EK, m_DK, true); } #endif #if defined(BOTAN_HAS_HW_AES_SUPPORT) - if(CPUID::has_hw_aes()) { - return aes_key_schedule(key.data(), key.size(), m_EK, m_DK, CPUID::is_little_endian()); + if(CPUID::has(CPUID::Feature::HW_AES)) { + constexpr bool is_little_endian = std::endian::native == std::endian::little; + return aes_key_schedule(key.data(), key.size(), m_EK, m_DK, is_little_endian); } #endif #if defined(BOTAN_HAS_AES_VPERM) - if(CPUID::has_vperm()) { + if(CPUID::has(CPUID::Feature::SIMD_4X32)) { return vperm_key_schedule(key.data(), key.size()); } #endif diff -Nru botan3-3.7.1+dfsg/src/lib/block/aes/aes.h botan3-3.12.0+dfsg/src/lib/block/aes/aes.h --- botan3-3.7.1+dfsg/src/lib/block/aes/aes.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/aes/aes.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #define BOTAN_AES_H_ #include +#include namespace Botan { diff -Nru botan3-3.7.1+dfsg/src/lib/block/aes/aes_armv8/aes_armv8.cpp botan3-3.12.0+dfsg/src/lib/block/aes/aes_armv8/aes_armv8.cpp --- botan3-3.7.1+dfsg/src/lib/block/aes/aes_armv8/aes_armv8.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/aes/aes_armv8/aes_armv8.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,7 @@ #include +#include #include #include @@ -17,60 +18,64 @@ namespace AES_AARCH64 { -BOTAN_FUNC_ISA_INLINE("+crypto+aes") void enc(uint8x16_t& B, uint8x16_t K) { +namespace { + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AES void enc(uint8x16_t& B, uint8x16_t K) { B = vaesmcq_u8(vaeseq_u8(B, K)); } -BOTAN_FUNC_ISA_INLINE("+crypto+aes") -void enc4(uint8x16_t& B0, uint8x16_t& B1, uint8x16_t& B2, uint8x16_t& B3, uint8x16_t K) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AES void enc4( + uint8x16_t& B0, uint8x16_t& B1, uint8x16_t& B2, uint8x16_t& B3, uint8x16_t K) { B0 = vaesmcq_u8(vaeseq_u8(B0, K)); B1 = vaesmcq_u8(vaeseq_u8(B1, K)); B2 = vaesmcq_u8(vaeseq_u8(B2, K)); B3 = vaesmcq_u8(vaeseq_u8(B3, K)); } -BOTAN_FUNC_ISA_INLINE("+crypto+aes") void enc_last(uint8x16_t& B, uint8x16_t K, uint8x16_t K2) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AES void enc_last(uint8x16_t& B, uint8x16_t K, uint8x16_t K2) { B = veorq_u8(vaeseq_u8(B, K), K2); } -BOTAN_FUNC_ISA_INLINE("+crypto+aes") -void enc4_last(uint8x16_t& B0, uint8x16_t& B1, uint8x16_t& B2, uint8x16_t& B3, uint8x16_t K, uint8x16_t K2) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AES void enc4_last( + uint8x16_t& B0, uint8x16_t& B1, uint8x16_t& B2, uint8x16_t& B3, uint8x16_t K, uint8x16_t K2) { B0 = veorq_u8(vaeseq_u8(B0, K), K2); B1 = veorq_u8(vaeseq_u8(B1, K), K2); B2 = veorq_u8(vaeseq_u8(B2, K), K2); B3 = veorq_u8(vaeseq_u8(B3, K), K2); } -BOTAN_FUNC_ISA_INLINE("+crypto+aes") void dec(uint8x16_t& B, uint8x16_t K) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AES void dec(uint8x16_t& B, uint8x16_t K) { B = vaesimcq_u8(vaesdq_u8(B, K)); } -BOTAN_FUNC_ISA_INLINE("+crypto+aes") -void dec4(uint8x16_t& B0, uint8x16_t& B1, uint8x16_t& B2, uint8x16_t& B3, uint8x16_t K) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AES void dec4( + uint8x16_t& B0, uint8x16_t& B1, uint8x16_t& B2, uint8x16_t& B3, uint8x16_t K) { B0 = vaesimcq_u8(vaesdq_u8(B0, K)); B1 = vaesimcq_u8(vaesdq_u8(B1, K)); B2 = vaesimcq_u8(vaesdq_u8(B2, K)); B3 = vaesimcq_u8(vaesdq_u8(B3, K)); } -BOTAN_FUNC_ISA_INLINE("+crypto+aes") void dec_last(uint8x16_t& B, uint8x16_t K, uint8x16_t K2) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AES void dec_last(uint8x16_t& B, uint8x16_t K, uint8x16_t K2) { B = veorq_u8(vaesdq_u8(B, K), K2); } -BOTAN_FUNC_ISA_INLINE("+crypto+aes") -void dec4_last(uint8x16_t& B0, uint8x16_t& B1, uint8x16_t& B2, uint8x16_t& B3, uint8x16_t K, uint8x16_t K2) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AES void dec4_last( + uint8x16_t& B0, uint8x16_t& B1, uint8x16_t& B2, uint8x16_t& B3, uint8x16_t K, uint8x16_t K2) { B0 = veorq_u8(vaesdq_u8(B0, K), K2); B1 = veorq_u8(vaesdq_u8(B1, K), K2); B2 = veorq_u8(vaesdq_u8(B2, K), K2); B3 = veorq_u8(vaesdq_u8(B3, K), K2); } +} // namespace + } // namespace AES_AARCH64 /* * AES-128 Encryption */ -BOTAN_FUNC_ISA("+crypto+aes") void AES_128::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AES void AES_128::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const uint8_t* skey = reinterpret_cast(m_EK.data()); const uint8x16_t K0 = vld1q_u8(skey + 0 * 16); @@ -133,7 +138,7 @@ /* * AES-128 Decryption */ -BOTAN_FUNC_ISA("+crypto+aes") void AES_128::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AES void AES_128::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const uint8_t* skey = reinterpret_cast(m_DK.data()); const uint8x16_t K0 = vld1q_u8(skey + 0 * 16); @@ -188,7 +193,7 @@ dec(B, K6); dec(B, K7); dec(B, K8); - B = veorq_u8(vaesdq_u8(B, K9), K10); + dec_last(B, K9, K10); vst1q_u8(out + 16 * i, B); } } @@ -196,7 +201,7 @@ /* * AES-192 Encryption */ -BOTAN_FUNC_ISA("+crypto+aes") void AES_192::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AES void AES_192::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const uint8_t* skey = reinterpret_cast(m_EK.data()); const uint8x16_t K0 = vld1q_u8(skey + 0 * 16); @@ -257,7 +262,7 @@ enc(B, K8); enc(B, K9); enc(B, K10); - B = veorq_u8(vaeseq_u8(B, K11), K12); + enc_last(B, K11, K12); vst1q_u8(out + 16 * i, B); } } @@ -265,7 +270,7 @@ /* * AES-192 Decryption */ -BOTAN_FUNC_ISA("+crypto+aes") void AES_192::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AES void AES_192::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const uint8_t* skey = reinterpret_cast(m_DK.data()); const uint8x16_t K0 = vld1q_u8(skey + 0 * 16); @@ -326,7 +331,7 @@ dec(B, K8); dec(B, K9); dec(B, K10); - B = veorq_u8(vaesdq_u8(B, K11), K12); + dec_last(B, K11, K12); vst1q_u8(out + 16 * i, B); } } @@ -334,7 +339,7 @@ /* * AES-256 Encryption */ -BOTAN_FUNC_ISA("+crypto+aes") void AES_256::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AES void AES_256::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const uint8_t* skey = reinterpret_cast(m_EK.data()); const uint8x16_t K0 = vld1q_u8(skey + 0 * 16); @@ -403,7 +408,7 @@ enc(B, K10); enc(B, K11); enc(B, K12); - B = veorq_u8(vaeseq_u8(B, K13), K14); + enc_last(B, K13, K14); vst1q_u8(out + 16 * i, B); } } @@ -411,7 +416,7 @@ /* * AES-256 Decryption */ -BOTAN_FUNC_ISA("+crypto+aes") void AES_256::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AES void AES_256::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const uint8_t* skey = reinterpret_cast(m_DK.data()); const uint8x16_t K0 = vld1q_u8(skey + 0 * 16); @@ -478,7 +483,7 @@ dec(B, K10); dec(B, K11); dec(B, K12); - B = veorq_u8(vaesdq_u8(B, K13), K14); + dec_last(B, K13, K14); vst1q_u8(out + 16 * i, B); } } diff -Nru botan3-3.7.1+dfsg/src/lib/block/aes/aes_armv8/info.txt botan3-3.12.0+dfsg/src/lib/block/aes/aes_armv8/info.txt --- botan3-3.7.1+dfsg/src/lib/block/aes/aes_armv8/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/aes/aes_armv8/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + AES_ARMV8 -> 20170903 - + name -> "AES ARMv8" @@ -10,3 +10,7 @@ armv8crypto + + +cpuid + diff -Nru botan3-3.7.1+dfsg/src/lib/block/aes/aes_ni/aes_ni.cpp botan3-3.12.0+dfsg/src/lib/block/aes/aes_ni/aes_ni.cpp --- botan3-3.7.1+dfsg/src/lib/block/aes/aes_ni/aes_ni.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/aes/aes_ni/aes_ni.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,17 +7,19 @@ #include +#include #include -#include +#include #include namespace Botan { namespace { +// NOLINTBEGIN(portability-simd-intrinsics) + template -BOTAN_FUNC_ISA("ssse3,aes") -inline __m128i aes_128_key_expansion(__m128i key, __m128i key_getting_rcon) { +BOTAN_FN_ISA_AESNI inline __m128i aes_128_key_expansion(__m128i key, __m128i key_getting_rcon) { __m128i key_with_rcon = _mm_aeskeygenassist_si128(key_getting_rcon, RC); key_with_rcon = _mm_shuffle_epi32(key_with_rcon, _MM_SHUFFLE(3, 3, 3, 3)); key = _mm_xor_si128(key, _mm_slli_si128(key, 4)); @@ -26,7 +28,7 @@ return _mm_xor_si128(key, key_with_rcon); } -BOTAN_FUNC_ISA("ssse3") +BOTAN_FN_ISA_AESNI void aes_192_key_expansion( __m128i* K1, __m128i* K2, __m128i key2_with_rcon, secure_vector& out, size_t offset) { __m128i key1 = *K1; @@ -56,7 +58,7 @@ /* * The second half of the AES-256 key expansion (other half same as AES-128) */ -BOTAN_FUNC_ISA("ssse3,aes") __m128i aes_256_key_expansion(__m128i key, __m128i key2) { +BOTAN_FN_ISA_AESNI __m128i aes_256_key_expansion(__m128i key, __m128i key2) { __m128i key_with_rcon = _mm_aeskeygenassist_si128(key2, 0x00); key_with_rcon = _mm_shuffle_epi32(key_with_rcon, _MM_SHUFFLE(2, 2, 2, 2)); @@ -66,63 +68,70 @@ return _mm_xor_si128(key, key_with_rcon); } -BOTAN_FORCE_INLINE void keyxor(SIMD_4x32 K, SIMD_4x32& B0, SIMD_4x32& B1, SIMD_4x32& B2, SIMD_4x32& B3) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AESNI void keyxor( + SIMD_4x32 K, SIMD_4x32& B0, SIMD_4x32& B1, SIMD_4x32& B2, SIMD_4x32& B3) { B0 ^= K; B1 ^= K; B2 ^= K; B3 ^= K; } -BOTAN_FUNC_ISA_INLINE("aes") void aesenc(SIMD_4x32 K, SIMD_4x32& B) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AESNI void aesenc(SIMD_4x32 K, SIMD_4x32& B) { B = SIMD_4x32(_mm_aesenc_si128(B.raw(), K.raw())); } -BOTAN_FUNC_ISA_INLINE("aes") void aesenc(SIMD_4x32 K, SIMD_4x32& B0, SIMD_4x32& B1, SIMD_4x32& B2, SIMD_4x32& B3) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AESNI void aesenc( + SIMD_4x32 K, SIMD_4x32& B0, SIMD_4x32& B1, SIMD_4x32& B2, SIMD_4x32& B3) { B0 = SIMD_4x32(_mm_aesenc_si128(B0.raw(), K.raw())); B1 = SIMD_4x32(_mm_aesenc_si128(B1.raw(), K.raw())); B2 = SIMD_4x32(_mm_aesenc_si128(B2.raw(), K.raw())); B3 = SIMD_4x32(_mm_aesenc_si128(B3.raw(), K.raw())); } -BOTAN_FUNC_ISA_INLINE("aes") void aesenclast(SIMD_4x32 K, SIMD_4x32& B) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AESNI void aesenclast(SIMD_4x32 K, SIMD_4x32& B) { B = SIMD_4x32(_mm_aesenclast_si128(B.raw(), K.raw())); } -BOTAN_FUNC_ISA_INLINE("aes") void aesenclast(SIMD_4x32 K, SIMD_4x32& B0, SIMD_4x32& B1, SIMD_4x32& B2, SIMD_4x32& B3) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AESNI void aesenclast( + SIMD_4x32 K, SIMD_4x32& B0, SIMD_4x32& B1, SIMD_4x32& B2, SIMD_4x32& B3) { B0 = SIMD_4x32(_mm_aesenclast_si128(B0.raw(), K.raw())); B1 = SIMD_4x32(_mm_aesenclast_si128(B1.raw(), K.raw())); B2 = SIMD_4x32(_mm_aesenclast_si128(B2.raw(), K.raw())); B3 = SIMD_4x32(_mm_aesenclast_si128(B3.raw(), K.raw())); } -BOTAN_FUNC_ISA_INLINE("aes") void aesdec(SIMD_4x32 K, SIMD_4x32& B) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AESNI void aesdec(SIMD_4x32 K, SIMD_4x32& B) { B = SIMD_4x32(_mm_aesdec_si128(B.raw(), K.raw())); } -BOTAN_FUNC_ISA_INLINE("aes") void aesdec(SIMD_4x32 K, SIMD_4x32& B0, SIMD_4x32& B1, SIMD_4x32& B2, SIMD_4x32& B3) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AESNI void aesdec( + SIMD_4x32 K, SIMD_4x32& B0, SIMD_4x32& B1, SIMD_4x32& B2, SIMD_4x32& B3) { B0 = SIMD_4x32(_mm_aesdec_si128(B0.raw(), K.raw())); B1 = SIMD_4x32(_mm_aesdec_si128(B1.raw(), K.raw())); B2 = SIMD_4x32(_mm_aesdec_si128(B2.raw(), K.raw())); B3 = SIMD_4x32(_mm_aesdec_si128(B3.raw(), K.raw())); } -BOTAN_FUNC_ISA_INLINE("aes") void aesdeclast(SIMD_4x32 K, SIMD_4x32& B) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AESNI void aesdeclast(SIMD_4x32 K, SIMD_4x32& B) { B = SIMD_4x32(_mm_aesdeclast_si128(B.raw(), K.raw())); } -BOTAN_FUNC_ISA_INLINE("aes") void aesdeclast(SIMD_4x32 K, SIMD_4x32& B0, SIMD_4x32& B1, SIMD_4x32& B2, SIMD_4x32& B3) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AESNI void aesdeclast( + SIMD_4x32 K, SIMD_4x32& B0, SIMD_4x32& B1, SIMD_4x32& B2, SIMD_4x32& B3) { B0 = SIMD_4x32(_mm_aesdeclast_si128(B0.raw(), K.raw())); B1 = SIMD_4x32(_mm_aesdeclast_si128(B1.raw(), K.raw())); B2 = SIMD_4x32(_mm_aesdeclast_si128(B2.raw(), K.raw())); B3 = SIMD_4x32(_mm_aesdeclast_si128(B3.raw(), K.raw())); } +// NOLINTEND(portability-simd-intrinsics) + } // namespace /* * AES-128 Encryption */ -BOTAN_FUNC_ISA("ssse3,aes") void AES_128::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AESNI void AES_128::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const SIMD_4x32 K0 = SIMD_4x32::load_le(&m_EK[4 * 0]); const SIMD_4x32 K1 = SIMD_4x32::load_le(&m_EK[4 * 1]); const SIMD_4x32 K2 = SIMD_4x32::load_le(&m_EK[4 * 2]); @@ -185,7 +194,7 @@ /* * AES-128 Decryption */ -BOTAN_FUNC_ISA("ssse3,aes") void AES_128::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AESNI void AES_128::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const SIMD_4x32 K0 = SIMD_4x32::load_le(&m_DK[4 * 0]); const SIMD_4x32 K1 = SIMD_4x32::load_le(&m_DK[4 * 1]); const SIMD_4x32 K2 = SIMD_4x32::load_le(&m_DK[4 * 2]); @@ -248,10 +257,12 @@ /* * AES-128 Key Schedule */ -BOTAN_FUNC_ISA("ssse3,aes") void AES_128::aesni_key_schedule(const uint8_t key[], size_t /*length*/) { +BOTAN_FN_ISA_AESNI void AES_128::aesni_key_schedule(const uint8_t key[], size_t /*length*/) { m_EK.resize(44); m_DK.resize(44); + // NOLINTBEGIN(portability-simd-intrinsics) TODO convert to using SIMD_4x32 + const __m128i K0 = _mm_loadu_si128(reinterpret_cast(key)); const __m128i K1 = aes_128_key_expansion<0x01>(K0, K0); const __m128i K2 = aes_128_key_expansion<0x02>(K1, K1); @@ -291,12 +302,14 @@ _mm_storeu_si128(DK_mm + 8, _mm_aesimc_si128(K2)); _mm_storeu_si128(DK_mm + 9, _mm_aesimc_si128(K1)); _mm_storeu_si128(DK_mm + 10, K0); + + // NOLINTEND(portability-simd-intrinsics) } /* * AES-192 Encryption */ -BOTAN_FUNC_ISA("ssse3,aes") void AES_192::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AESNI void AES_192::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const SIMD_4x32 K0 = SIMD_4x32::load_le(&m_EK[4 * 0]); const SIMD_4x32 K1 = SIMD_4x32::load_le(&m_EK[4 * 1]); const SIMD_4x32 K2 = SIMD_4x32::load_le(&m_EK[4 * 2]); @@ -366,7 +379,7 @@ /* * AES-192 Decryption */ -BOTAN_FUNC_ISA("ssse3,aes") void AES_192::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AESNI void AES_192::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const SIMD_4x32 K0 = SIMD_4x32::load_le(&m_DK[4 * 0]); const SIMD_4x32 K1 = SIMD_4x32::load_le(&m_DK[4 * 1]); const SIMD_4x32 K2 = SIMD_4x32::load_le(&m_DK[4 * 2]); @@ -436,10 +449,12 @@ /* * AES-192 Key Schedule */ -BOTAN_FUNC_ISA("ssse3,aes") void AES_192::aesni_key_schedule(const uint8_t key[], size_t /*length*/) { +BOTAN_FN_ISA_AESNI void AES_192::aesni_key_schedule(const uint8_t key[], size_t /*length*/) { m_EK.resize(52); m_DK.resize(52); + // NOLINTBEGIN(portability-simd-intrinsics) TODO convert to using SIMD_4x32 + __m128i K0 = _mm_loadu_si128(reinterpret_cast(key)); __m128i K1 = _mm_loadu_si128(reinterpret_cast(key + 8)); K1 = _mm_srli_si128(K1, 8); @@ -472,12 +487,14 @@ _mm_storeu_si128(DK_mm + 10, _mm_aesimc_si128(_mm_loadu_si128(EK_mm + 2))); _mm_storeu_si128(DK_mm + 11, _mm_aesimc_si128(_mm_loadu_si128(EK_mm + 1))); _mm_storeu_si128(DK_mm + 12, _mm_loadu_si128(EK_mm + 0)); + + // NOLINTEND(portability-simd-intrinsics) } /* * AES-256 Encryption */ -BOTAN_FUNC_ISA("ssse3,aes") void AES_256::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AESNI void AES_256::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const SIMD_4x32 K0 = SIMD_4x32::load_le(&m_EK[4 * 0]); const SIMD_4x32 K1 = SIMD_4x32::load_le(&m_EK[4 * 1]); const SIMD_4x32 K2 = SIMD_4x32::load_le(&m_EK[4 * 2]); @@ -553,7 +570,7 @@ /* * AES-256 Decryption */ -BOTAN_FUNC_ISA("ssse3,aes") void AES_256::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AESNI void AES_256::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const SIMD_4x32 K0 = SIMD_4x32::load_le(&m_DK[4 * 0]); const SIMD_4x32 K1 = SIMD_4x32::load_le(&m_DK[4 * 1]); const SIMD_4x32 K2 = SIMD_4x32::load_le(&m_DK[4 * 2]); @@ -629,10 +646,12 @@ /* * AES-256 Key Schedule */ -BOTAN_FUNC_ISA("ssse3,aes") void AES_256::aesni_key_schedule(const uint8_t key[], size_t /*length*/) { +BOTAN_FN_ISA_AESNI void AES_256::aesni_key_schedule(const uint8_t key[], size_t /*length*/) { m_EK.resize(60); m_DK.resize(60); + // NOLINTBEGIN(portability-simd-intrinsics) TODO convert to using SIMD_4x32 + const __m128i K0 = _mm_loadu_si128(reinterpret_cast(key)); const __m128i K1 = _mm_loadu_si128(reinterpret_cast(key + 16)); @@ -690,6 +709,8 @@ _mm_storeu_si128(DK_mm + 12, _mm_aesimc_si128(K2)); _mm_storeu_si128(DK_mm + 13, _mm_aesimc_si128(K1)); _mm_storeu_si128(DK_mm + 14, K0); + + // NOLINTEND(portability-simd-intrinsics) } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/aes/aes_ni/info.txt botan3-3.12.0+dfsg/src/lib/block/aes/aes_ni/info.txt --- botan3-3.7.1+dfsg/src/lib/block/aes/aes_ni/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/aes/aes_ni/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + AES_NI -> 20131128 - + name -> "AES-NI" @@ -8,7 +8,8 @@ -simd +cpuid +simd_4x32 diff -Nru botan3-3.7.1+dfsg/src/lib/block/aes/aes_power8/aes_power8.cpp botan3-3.12.0+dfsg/src/lib/block/aes/aes_power8/aes_power8.cpp --- botan3-3.7.1+dfsg/src/lib/block/aes/aes_power8/aes_power8.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/aes/aes_power8/aes_power8.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,8 +11,8 @@ #include -#include -#include +#include +#include #include #undef vector @@ -20,14 +20,18 @@ namespace Botan { +// NOLINTBEGIN(readability-container-data-pointer) + typedef __vector unsigned long long Altivec64x2; typedef __vector unsigned int Altivec32x4; typedef __vector unsigned char Altivec8x16; namespace { -inline Altivec8x16 reverse_vec(Altivec8x16 src) { - if(CPUID::is_little_endian()) { +static_assert(std::endian::native == std::endian::big || std::endian::native == std::endian::little); + +BOTAN_FORCE_INLINE Altivec8x16 reverse_vec(Altivec8x16 src) { + if constexpr(std::endian::native == std::endian::little) { const Altivec8x16 mask = {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; const Altivec8x16 zero = {0}; return vec_perm(src, zero, mask); @@ -36,68 +40,104 @@ } } -BOTAN_FUNC_ISA("vsx") inline Altivec64x2 load_key(const uint32_t key[]) { +BOTAN_FN_ISA_AES BOTAN_FORCE_INLINE Altivec64x2 load_key(const uint32_t key[]) { return reinterpret_cast(reverse_vec(reinterpret_cast(vec_vsx_ld(0, key)))); } -BOTAN_FUNC_ISA("vsx") inline Altivec64x2 load_block(const uint8_t src[]) { +BOTAN_FN_ISA_AES BOTAN_FORCE_INLINE Altivec64x2 load_block(const uint8_t src[]) { return reinterpret_cast(reverse_vec(vec_vsx_ld(0, src))); } -BOTAN_FUNC_ISA("vsx") inline void store_block(Altivec64x2 src, uint8_t dest[]) { +BOTAN_FN_ISA_AES BOTAN_FORCE_INLINE void store_block(Altivec64x2 src, uint8_t dest[]) { vec_vsx_st(reverse_vec(reinterpret_cast(src)), 0, dest); } -inline void store_blocks(Altivec64x2 B0, Altivec64x2 B1, Altivec64x2 B2, Altivec64x2 B3, uint8_t out[]) { +BOTAN_FORCE_INLINE void store_blocks(Altivec64x2 B0, Altivec64x2 B1, Altivec64x2 B2, Altivec64x2 B3, uint8_t out[]) { store_block(B0, out); store_block(B1, out + 16); store_block(B2, out + 16 * 2); store_block(B3, out + 16 * 3); } -#define AES_XOR_4(B0, B1, B2, B3, K) \ - do { \ - B0 = vec_xor(B0, K); \ - B1 = vec_xor(B1, K); \ - B2 = vec_xor(B2, K); \ - B3 = vec_xor(B3, K); \ - } while(0) - -#define AES_ENCRYPT_4(B0, B1, B2, B3, K) \ - do { \ - B0 = __builtin_crypto_vcipher(B0, K); \ - B1 = __builtin_crypto_vcipher(B1, K); \ - B2 = __builtin_crypto_vcipher(B2, K); \ - B3 = __builtin_crypto_vcipher(B3, K); \ - } while(0) - -#define AES_ENCRYPT_4_LAST(B0, B1, B2, B3, K) \ - do { \ - B0 = __builtin_crypto_vcipherlast(B0, K); \ - B1 = __builtin_crypto_vcipherlast(B1, K); \ - B2 = __builtin_crypto_vcipherlast(B2, K); \ - B3 = __builtin_crypto_vcipherlast(B3, K); \ - } while(0) - -#define AES_DECRYPT_4(B0, B1, B2, B3, K) \ - do { \ - B0 = __builtin_crypto_vncipher(B0, K); \ - B1 = __builtin_crypto_vncipher(B1, K); \ - B2 = __builtin_crypto_vncipher(B2, K); \ - B3 = __builtin_crypto_vncipher(B3, K); \ - } while(0) - -#define AES_DECRYPT_4_LAST(B0, B1, B2, B3, K) \ - do { \ - B0 = __builtin_crypto_vncipherlast(B0, K); \ - B1 = __builtin_crypto_vncipherlast(B1, K); \ - B2 = __builtin_crypto_vncipherlast(B2, K); \ - B3 = __builtin_crypto_vncipherlast(B3, K); \ - } while(0) +BOTAN_FN_ISA_AES BOTAN_FORCE_INLINE void xor_blocks( + Altivec64x2& B0, Altivec64x2& B1, Altivec64x2& B2, Altivec64x2& B3, Altivec64x2 K) { + B0 = vec_xor(B0, K); + B1 = vec_xor(B1, K); + B2 = vec_xor(B2, K); + B3 = vec_xor(B3, K); +} + +BOTAN_FN_ISA_AES BOTAN_FORCE_INLINE void aes_vcipher(Altivec64x2& B, Altivec64x2 K) { +#if defined(__clang__) + B = reinterpret_cast( + __builtin_crypto_vcipher(reinterpret_cast(B), reinterpret_cast(K))); +#else + B = __builtin_crypto_vcipher(B, K); +#endif +} + +BOTAN_FN_ISA_AES BOTAN_FORCE_INLINE void aes_vcipherlast(Altivec64x2& B, Altivec64x2 K) { +#if defined(__clang__) + B = reinterpret_cast( + __builtin_crypto_vcipherlast(reinterpret_cast(B), reinterpret_cast(K))); +#else + B = __builtin_crypto_vcipherlast(B, K); +#endif +} + +BOTAN_FN_ISA_AES BOTAN_FORCE_INLINE void aes_vncipher(Altivec64x2& B, Altivec64x2 K) { +#if defined(__clang__) + B = reinterpret_cast( + __builtin_crypto_vncipher(reinterpret_cast(B), reinterpret_cast(K))); +#else + B = __builtin_crypto_vncipher(B, K); +#endif +} + +BOTAN_FN_ISA_AES BOTAN_FORCE_INLINE void aes_vncipherlast(Altivec64x2& B, Altivec64x2 K) { +#if defined(__clang__) + B = reinterpret_cast( + __builtin_crypto_vncipherlast(reinterpret_cast(B), reinterpret_cast(K))); +#else + B = __builtin_crypto_vncipherlast(B, K); +#endif +} + +BOTAN_FN_ISA_AES BOTAN_FORCE_INLINE void aes_vcipher( + Altivec64x2& B0, Altivec64x2& B1, Altivec64x2& B2, Altivec64x2& B3, Altivec64x2 K) { + aes_vcipher(B0, K); + aes_vcipher(B1, K); + aes_vcipher(B2, K); + aes_vcipher(B3, K); +} + +BOTAN_FN_ISA_AES BOTAN_FORCE_INLINE void aes_vcipherlast( + Altivec64x2& B0, Altivec64x2& B1, Altivec64x2& B2, Altivec64x2& B3, Altivec64x2 K) { + aes_vcipherlast(B0, K); + aes_vcipherlast(B1, K); + aes_vcipherlast(B2, K); + aes_vcipherlast(B3, K); +} + +BOTAN_FN_ISA_AES BOTAN_FORCE_INLINE void aes_vncipher( + Altivec64x2& B0, Altivec64x2& B1, Altivec64x2& B2, Altivec64x2& B3, Altivec64x2 K) { + aes_vncipher(B0, K); + aes_vncipher(B1, K); + aes_vncipher(B2, K); + aes_vncipher(B3, K); +} + +BOTAN_FN_ISA_AES BOTAN_FORCE_INLINE void aes_vncipherlast( + Altivec64x2& B0, Altivec64x2& B1, Altivec64x2& B2, Altivec64x2& B3, Altivec64x2 K) { + aes_vncipherlast(B0, K); + aes_vncipherlast(B1, K); + aes_vncipherlast(B2, K); + aes_vncipherlast(B3, K); +} } // namespace -BOTAN_FUNC_ISA("crypto,vsx") void AES_128::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AES void AES_128::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const Altivec64x2 K0 = load_key(&m_EK[0]); const Altivec64x2 K1 = load_key(&m_EK[4]); const Altivec64x2 K2 = load_key(&m_EK[8]); @@ -116,17 +156,17 @@ Altivec64x2 B2 = load_block(in + 16 * 2); Altivec64x2 B3 = load_block(in + 16 * 3); - AES_XOR_4(B0, B1, B2, B3, K0); - AES_ENCRYPT_4(B0, B1, B2, B3, K1); - AES_ENCRYPT_4(B0, B1, B2, B3, K2); - AES_ENCRYPT_4(B0, B1, B2, B3, K3); - AES_ENCRYPT_4(B0, B1, B2, B3, K4); - AES_ENCRYPT_4(B0, B1, B2, B3, K5); - AES_ENCRYPT_4(B0, B1, B2, B3, K6); - AES_ENCRYPT_4(B0, B1, B2, B3, K7); - AES_ENCRYPT_4(B0, B1, B2, B3, K8); - AES_ENCRYPT_4(B0, B1, B2, B3, K9); - AES_ENCRYPT_4_LAST(B0, B1, B2, B3, K10); + xor_blocks(B0, B1, B2, B3, K0); + aes_vcipher(B0, B1, B2, B3, K1); + aes_vcipher(B0, B1, B2, B3, K2); + aes_vcipher(B0, B1, B2, B3, K3); + aes_vcipher(B0, B1, B2, B3, K4); + aes_vcipher(B0, B1, B2, B3, K5); + aes_vcipher(B0, B1, B2, B3, K6); + aes_vcipher(B0, B1, B2, B3, K7); + aes_vcipher(B0, B1, B2, B3, K8); + aes_vcipher(B0, B1, B2, B3, K9); + aes_vcipherlast(B0, B1, B2, B3, K10); store_blocks(B0, B1, B2, B3, out); @@ -139,16 +179,16 @@ Altivec64x2 B = load_block(in); B = vec_xor(B, K0); - B = __builtin_crypto_vcipher(B, K1); - B = __builtin_crypto_vcipher(B, K2); - B = __builtin_crypto_vcipher(B, K3); - B = __builtin_crypto_vcipher(B, K4); - B = __builtin_crypto_vcipher(B, K5); - B = __builtin_crypto_vcipher(B, K6); - B = __builtin_crypto_vcipher(B, K7); - B = __builtin_crypto_vcipher(B, K8); - B = __builtin_crypto_vcipher(B, K9); - B = __builtin_crypto_vcipherlast(B, K10); + aes_vcipher(B, K1); + aes_vcipher(B, K2); + aes_vcipher(B, K3); + aes_vcipher(B, K4); + aes_vcipher(B, K5); + aes_vcipher(B, K6); + aes_vcipher(B, K7); + aes_vcipher(B, K8); + aes_vcipher(B, K9); + aes_vcipherlast(B, K10); store_block(B, out); @@ -157,7 +197,7 @@ } } -BOTAN_FUNC_ISA("crypto,vsx") void AES_128::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AES void AES_128::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const Altivec64x2 K0 = load_key(&m_EK[40]); const Altivec64x2 K1 = load_key(&m_EK[36]); const Altivec64x2 K2 = load_key(&m_EK[32]); @@ -176,17 +216,17 @@ Altivec64x2 B2 = load_block(in + 16 * 2); Altivec64x2 B3 = load_block(in + 16 * 3); - AES_XOR_4(B0, B1, B2, B3, K0); - AES_DECRYPT_4(B0, B1, B2, B3, K1); - AES_DECRYPT_4(B0, B1, B2, B3, K2); - AES_DECRYPT_4(B0, B1, B2, B3, K3); - AES_DECRYPT_4(B0, B1, B2, B3, K4); - AES_DECRYPT_4(B0, B1, B2, B3, K5); - AES_DECRYPT_4(B0, B1, B2, B3, K6); - AES_DECRYPT_4(B0, B1, B2, B3, K7); - AES_DECRYPT_4(B0, B1, B2, B3, K8); - AES_DECRYPT_4(B0, B1, B2, B3, K9); - AES_DECRYPT_4_LAST(B0, B1, B2, B3, K10); + xor_blocks(B0, B1, B2, B3, K0); + aes_vncipher(B0, B1, B2, B3, K1); + aes_vncipher(B0, B1, B2, B3, K2); + aes_vncipher(B0, B1, B2, B3, K3); + aes_vncipher(B0, B1, B2, B3, K4); + aes_vncipher(B0, B1, B2, B3, K5); + aes_vncipher(B0, B1, B2, B3, K6); + aes_vncipher(B0, B1, B2, B3, K7); + aes_vncipher(B0, B1, B2, B3, K8); + aes_vncipher(B0, B1, B2, B3, K9); + aes_vncipherlast(B0, B1, B2, B3, K10); store_blocks(B0, B1, B2, B3, out); @@ -199,16 +239,16 @@ Altivec64x2 B = load_block(in); B = vec_xor(B, K0); - B = __builtin_crypto_vncipher(B, K1); - B = __builtin_crypto_vncipher(B, K2); - B = __builtin_crypto_vncipher(B, K3); - B = __builtin_crypto_vncipher(B, K4); - B = __builtin_crypto_vncipher(B, K5); - B = __builtin_crypto_vncipher(B, K6); - B = __builtin_crypto_vncipher(B, K7); - B = __builtin_crypto_vncipher(B, K8); - B = __builtin_crypto_vncipher(B, K9); - B = __builtin_crypto_vncipherlast(B, K10); + aes_vncipher(B, K1); + aes_vncipher(B, K2); + aes_vncipher(B, K3); + aes_vncipher(B, K4); + aes_vncipher(B, K5); + aes_vncipher(B, K6); + aes_vncipher(B, K7); + aes_vncipher(B, K8); + aes_vncipher(B, K9); + aes_vncipherlast(B, K10); store_block(B, out); @@ -217,7 +257,7 @@ } } -BOTAN_FUNC_ISA("crypto,vsx") void AES_192::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AES void AES_192::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const Altivec64x2 K0 = load_key(&m_EK[0]); const Altivec64x2 K1 = load_key(&m_EK[4]); const Altivec64x2 K2 = load_key(&m_EK[8]); @@ -238,19 +278,19 @@ Altivec64x2 B2 = load_block(in + 16 * 2); Altivec64x2 B3 = load_block(in + 16 * 3); - AES_XOR_4(B0, B1, B2, B3, K0); - AES_ENCRYPT_4(B0, B1, B2, B3, K1); - AES_ENCRYPT_4(B0, B1, B2, B3, K2); - AES_ENCRYPT_4(B0, B1, B2, B3, K3); - AES_ENCRYPT_4(B0, B1, B2, B3, K4); - AES_ENCRYPT_4(B0, B1, B2, B3, K5); - AES_ENCRYPT_4(B0, B1, B2, B3, K6); - AES_ENCRYPT_4(B0, B1, B2, B3, K7); - AES_ENCRYPT_4(B0, B1, B2, B3, K8); - AES_ENCRYPT_4(B0, B1, B2, B3, K9); - AES_ENCRYPT_4(B0, B1, B2, B3, K10); - AES_ENCRYPT_4(B0, B1, B2, B3, K11); - AES_ENCRYPT_4_LAST(B0, B1, B2, B3, K12); + xor_blocks(B0, B1, B2, B3, K0); + aes_vcipher(B0, B1, B2, B3, K1); + aes_vcipher(B0, B1, B2, B3, K2); + aes_vcipher(B0, B1, B2, B3, K3); + aes_vcipher(B0, B1, B2, B3, K4); + aes_vcipher(B0, B1, B2, B3, K5); + aes_vcipher(B0, B1, B2, B3, K6); + aes_vcipher(B0, B1, B2, B3, K7); + aes_vcipher(B0, B1, B2, B3, K8); + aes_vcipher(B0, B1, B2, B3, K9); + aes_vcipher(B0, B1, B2, B3, K10); + aes_vcipher(B0, B1, B2, B3, K11); + aes_vcipherlast(B0, B1, B2, B3, K12); store_blocks(B0, B1, B2, B3, out); @@ -263,18 +303,18 @@ Altivec64x2 B = load_block(in); B = vec_xor(B, K0); - B = __builtin_crypto_vcipher(B, K1); - B = __builtin_crypto_vcipher(B, K2); - B = __builtin_crypto_vcipher(B, K3); - B = __builtin_crypto_vcipher(B, K4); - B = __builtin_crypto_vcipher(B, K5); - B = __builtin_crypto_vcipher(B, K6); - B = __builtin_crypto_vcipher(B, K7); - B = __builtin_crypto_vcipher(B, K8); - B = __builtin_crypto_vcipher(B, K9); - B = __builtin_crypto_vcipher(B, K10); - B = __builtin_crypto_vcipher(B, K11); - B = __builtin_crypto_vcipherlast(B, K12); + aes_vcipher(B, K1); + aes_vcipher(B, K2); + aes_vcipher(B, K3); + aes_vcipher(B, K4); + aes_vcipher(B, K5); + aes_vcipher(B, K6); + aes_vcipher(B, K7); + aes_vcipher(B, K8); + aes_vcipher(B, K9); + aes_vcipher(B, K10); + aes_vcipher(B, K11); + aes_vcipherlast(B, K12); store_block(B, out); @@ -283,7 +323,7 @@ } } -BOTAN_FUNC_ISA("crypto,vsx") void AES_192::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AES void AES_192::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const Altivec64x2 K0 = load_key(&m_EK[48]); const Altivec64x2 K1 = load_key(&m_EK[44]); const Altivec64x2 K2 = load_key(&m_EK[40]); @@ -304,19 +344,19 @@ Altivec64x2 B2 = load_block(in + 16 * 2); Altivec64x2 B3 = load_block(in + 16 * 3); - AES_XOR_4(B0, B1, B2, B3, K0); - AES_DECRYPT_4(B0, B1, B2, B3, K1); - AES_DECRYPT_4(B0, B1, B2, B3, K2); - AES_DECRYPT_4(B0, B1, B2, B3, K3); - AES_DECRYPT_4(B0, B1, B2, B3, K4); - AES_DECRYPT_4(B0, B1, B2, B3, K5); - AES_DECRYPT_4(B0, B1, B2, B3, K6); - AES_DECRYPT_4(B0, B1, B2, B3, K7); - AES_DECRYPT_4(B0, B1, B2, B3, K8); - AES_DECRYPT_4(B0, B1, B2, B3, K9); - AES_DECRYPT_4(B0, B1, B2, B3, K10); - AES_DECRYPT_4(B0, B1, B2, B3, K11); - AES_DECRYPT_4_LAST(B0, B1, B2, B3, K12); + xor_blocks(B0, B1, B2, B3, K0); + aes_vncipher(B0, B1, B2, B3, K1); + aes_vncipher(B0, B1, B2, B3, K2); + aes_vncipher(B0, B1, B2, B3, K3); + aes_vncipher(B0, B1, B2, B3, K4); + aes_vncipher(B0, B1, B2, B3, K5); + aes_vncipher(B0, B1, B2, B3, K6); + aes_vncipher(B0, B1, B2, B3, K7); + aes_vncipher(B0, B1, B2, B3, K8); + aes_vncipher(B0, B1, B2, B3, K9); + aes_vncipher(B0, B1, B2, B3, K10); + aes_vncipher(B0, B1, B2, B3, K11); + aes_vncipherlast(B0, B1, B2, B3, K12); store_blocks(B0, B1, B2, B3, out); @@ -329,18 +369,18 @@ Altivec64x2 B = load_block(in); B = vec_xor(B, K0); - B = __builtin_crypto_vncipher(B, K1); - B = __builtin_crypto_vncipher(B, K2); - B = __builtin_crypto_vncipher(B, K3); - B = __builtin_crypto_vncipher(B, K4); - B = __builtin_crypto_vncipher(B, K5); - B = __builtin_crypto_vncipher(B, K6); - B = __builtin_crypto_vncipher(B, K7); - B = __builtin_crypto_vncipher(B, K8); - B = __builtin_crypto_vncipher(B, K9); - B = __builtin_crypto_vncipher(B, K10); - B = __builtin_crypto_vncipher(B, K11); - B = __builtin_crypto_vncipherlast(B, K12); + aes_vncipher(B, K1); + aes_vncipher(B, K2); + aes_vncipher(B, K3); + aes_vncipher(B, K4); + aes_vncipher(B, K5); + aes_vncipher(B, K6); + aes_vncipher(B, K7); + aes_vncipher(B, K8); + aes_vncipher(B, K9); + aes_vncipher(B, K10); + aes_vncipher(B, K11); + aes_vncipherlast(B, K12); store_block(B, out); @@ -349,7 +389,7 @@ } } -BOTAN_FUNC_ISA("crypto,vsx") void AES_256::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AES void AES_256::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const Altivec64x2 K0 = load_key(&m_EK[0]); const Altivec64x2 K1 = load_key(&m_EK[4]); const Altivec64x2 K2 = load_key(&m_EK[8]); @@ -372,21 +412,21 @@ Altivec64x2 B2 = load_block(in + 16 * 2); Altivec64x2 B3 = load_block(in + 16 * 3); - AES_XOR_4(B0, B1, B2, B3, K0); - AES_ENCRYPT_4(B0, B1, B2, B3, K1); - AES_ENCRYPT_4(B0, B1, B2, B3, K2); - AES_ENCRYPT_4(B0, B1, B2, B3, K3); - AES_ENCRYPT_4(B0, B1, B2, B3, K4); - AES_ENCRYPT_4(B0, B1, B2, B3, K5); - AES_ENCRYPT_4(B0, B1, B2, B3, K6); - AES_ENCRYPT_4(B0, B1, B2, B3, K7); - AES_ENCRYPT_4(B0, B1, B2, B3, K8); - AES_ENCRYPT_4(B0, B1, B2, B3, K9); - AES_ENCRYPT_4(B0, B1, B2, B3, K10); - AES_ENCRYPT_4(B0, B1, B2, B3, K11); - AES_ENCRYPT_4(B0, B1, B2, B3, K12); - AES_ENCRYPT_4(B0, B1, B2, B3, K13); - AES_ENCRYPT_4_LAST(B0, B1, B2, B3, K14); + xor_blocks(B0, B1, B2, B3, K0); + aes_vcipher(B0, B1, B2, B3, K1); + aes_vcipher(B0, B1, B2, B3, K2); + aes_vcipher(B0, B1, B2, B3, K3); + aes_vcipher(B0, B1, B2, B3, K4); + aes_vcipher(B0, B1, B2, B3, K5); + aes_vcipher(B0, B1, B2, B3, K6); + aes_vcipher(B0, B1, B2, B3, K7); + aes_vcipher(B0, B1, B2, B3, K8); + aes_vcipher(B0, B1, B2, B3, K9); + aes_vcipher(B0, B1, B2, B3, K10); + aes_vcipher(B0, B1, B2, B3, K11); + aes_vcipher(B0, B1, B2, B3, K12); + aes_vcipher(B0, B1, B2, B3, K13); + aes_vcipherlast(B0, B1, B2, B3, K14); store_blocks(B0, B1, B2, B3, out); @@ -399,20 +439,20 @@ Altivec64x2 B = load_block(in); B = vec_xor(B, K0); - B = __builtin_crypto_vcipher(B, K1); - B = __builtin_crypto_vcipher(B, K2); - B = __builtin_crypto_vcipher(B, K3); - B = __builtin_crypto_vcipher(B, K4); - B = __builtin_crypto_vcipher(B, K5); - B = __builtin_crypto_vcipher(B, K6); - B = __builtin_crypto_vcipher(B, K7); - B = __builtin_crypto_vcipher(B, K8); - B = __builtin_crypto_vcipher(B, K9); - B = __builtin_crypto_vcipher(B, K10); - B = __builtin_crypto_vcipher(B, K11); - B = __builtin_crypto_vcipher(B, K12); - B = __builtin_crypto_vcipher(B, K13); - B = __builtin_crypto_vcipherlast(B, K14); + aes_vcipher(B, K1); + aes_vcipher(B, K2); + aes_vcipher(B, K3); + aes_vcipher(B, K4); + aes_vcipher(B, K5); + aes_vcipher(B, K6); + aes_vcipher(B, K7); + aes_vcipher(B, K8); + aes_vcipher(B, K9); + aes_vcipher(B, K10); + aes_vcipher(B, K11); + aes_vcipher(B, K12); + aes_vcipher(B, K13); + aes_vcipherlast(B, K14); store_block(B, out); @@ -421,7 +461,7 @@ } } -BOTAN_FUNC_ISA("crypto,vsx") void AES_256::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AES void AES_256::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const Altivec64x2 K0 = load_key(&m_EK[56]); const Altivec64x2 K1 = load_key(&m_EK[52]); const Altivec64x2 K2 = load_key(&m_EK[48]); @@ -444,21 +484,21 @@ Altivec64x2 B2 = load_block(in + 16 * 2); Altivec64x2 B3 = load_block(in + 16 * 3); - AES_XOR_4(B0, B1, B2, B3, K0); - AES_DECRYPT_4(B0, B1, B2, B3, K1); - AES_DECRYPT_4(B0, B1, B2, B3, K2); - AES_DECRYPT_4(B0, B1, B2, B3, K3); - AES_DECRYPT_4(B0, B1, B2, B3, K4); - AES_DECRYPT_4(B0, B1, B2, B3, K5); - AES_DECRYPT_4(B0, B1, B2, B3, K6); - AES_DECRYPT_4(B0, B1, B2, B3, K7); - AES_DECRYPT_4(B0, B1, B2, B3, K8); - AES_DECRYPT_4(B0, B1, B2, B3, K9); - AES_DECRYPT_4(B0, B1, B2, B3, K10); - AES_DECRYPT_4(B0, B1, B2, B3, K11); - AES_DECRYPT_4(B0, B1, B2, B3, K12); - AES_DECRYPT_4(B0, B1, B2, B3, K13); - AES_DECRYPT_4_LAST(B0, B1, B2, B3, K14); + xor_blocks(B0, B1, B2, B3, K0); + aes_vncipher(B0, B1, B2, B3, K1); + aes_vncipher(B0, B1, B2, B3, K2); + aes_vncipher(B0, B1, B2, B3, K3); + aes_vncipher(B0, B1, B2, B3, K4); + aes_vncipher(B0, B1, B2, B3, K5); + aes_vncipher(B0, B1, B2, B3, K6); + aes_vncipher(B0, B1, B2, B3, K7); + aes_vncipher(B0, B1, B2, B3, K8); + aes_vncipher(B0, B1, B2, B3, K9); + aes_vncipher(B0, B1, B2, B3, K10); + aes_vncipher(B0, B1, B2, B3, K11); + aes_vncipher(B0, B1, B2, B3, K12); + aes_vncipher(B0, B1, B2, B3, K13); + aes_vncipherlast(B0, B1, B2, B3, K14); store_blocks(B0, B1, B2, B3, out); @@ -471,20 +511,20 @@ Altivec64x2 B = load_block(in); B = vec_xor(B, K0); - B = __builtin_crypto_vncipher(B, K1); - B = __builtin_crypto_vncipher(B, K2); - B = __builtin_crypto_vncipher(B, K3); - B = __builtin_crypto_vncipher(B, K4); - B = __builtin_crypto_vncipher(B, K5); - B = __builtin_crypto_vncipher(B, K6); - B = __builtin_crypto_vncipher(B, K7); - B = __builtin_crypto_vncipher(B, K8); - B = __builtin_crypto_vncipher(B, K9); - B = __builtin_crypto_vncipher(B, K10); - B = __builtin_crypto_vncipher(B, K11); - B = __builtin_crypto_vncipher(B, K12); - B = __builtin_crypto_vncipher(B, K13); - B = __builtin_crypto_vncipherlast(B, K14); + aes_vncipher(B, K1); + aes_vncipher(B, K2); + aes_vncipher(B, K3); + aes_vncipher(B, K4); + aes_vncipher(B, K5); + aes_vncipher(B, K6); + aes_vncipher(B, K7); + aes_vncipher(B, K8); + aes_vncipher(B, K9); + aes_vncipher(B, K10); + aes_vncipher(B, K11); + aes_vncipher(B, K12); + aes_vncipher(B, K13); + aes_vncipherlast(B, K14); store_block(B, out); @@ -493,10 +533,6 @@ } } -#undef AES_XOR_4 -#undef AES_ENCRYPT_4 -#undef AES_ENCRYPT_4_LAST -#undef AES_DECRYPT_4 -#undef AES_DECRYPT_4_LAST +// NOLINTEND(readability-container-data-pointer) } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/aes/aes_power8/info.txt botan3-3.12.0+dfsg/src/lib/block/aes/aes_power8/info.txt --- botan3-3.7.1+dfsg/src/lib/block/aes/aes_power8/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/aes/aes_power8/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,12 +1,16 @@ - + AES_POWER8 -> 20180223 - + name -> "AES Power8" brief -> "AES using Power8 instructions" + +cpuid + + ppc64 diff -Nru botan3-3.7.1+dfsg/src/lib/block/aes/aes_vaes/aes_vaes.cpp botan3-3.12.0+dfsg/src/lib/block/aes/aes_vaes/aes_vaes.cpp --- botan3-3.7.1+dfsg/src/lib/block/aes/aes_vaes/aes_vaes.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/aes/aes_vaes/aes_vaes.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,75 +6,80 @@ #include -#include +#include #include -#include +#include namespace Botan { namespace { -BOTAN_FORCE_INLINE void keyxor(SIMD_8x32 K, SIMD_8x32& B0, SIMD_8x32& B1, SIMD_8x32& B2, SIMD_8x32& B3) { +BOTAN_FORCE_INLINE void BOTAN_FN_ISA_AVX2_VAES +keyxor(SIMD_8x32 K, SIMD_8x32& B0, SIMD_8x32& B1, SIMD_8x32& B2, SIMD_8x32& B3) { B0 ^= K; B1 ^= K; B2 ^= K; B3 ^= K; } -BOTAN_FUNC_ISA_INLINE("vaes,avx2") void aesenc(SIMD_8x32 K, SIMD_8x32& B) { +// NOLINTBEGIN(portability-simd-intrinsics) + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_VAES void aesenc(SIMD_8x32 K, SIMD_8x32& B) { B = SIMD_8x32(_mm256_aesenc_epi128(B.raw(), K.raw())); } -BOTAN_FUNC_ISA_INLINE("vaes,avx2") -void aesenc(SIMD_8x32 K, SIMD_8x32& B0, SIMD_8x32& B1, SIMD_8x32& B2, SIMD_8x32& B3) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_VAES void aesenc( + SIMD_8x32 K, SIMD_8x32& B0, SIMD_8x32& B1, SIMD_8x32& B2, SIMD_8x32& B3) { B0 = SIMD_8x32(_mm256_aesenc_epi128(B0.raw(), K.raw())); B1 = SIMD_8x32(_mm256_aesenc_epi128(B1.raw(), K.raw())); B2 = SIMD_8x32(_mm256_aesenc_epi128(B2.raw(), K.raw())); B3 = SIMD_8x32(_mm256_aesenc_epi128(B3.raw(), K.raw())); } -BOTAN_FUNC_ISA_INLINE("vaes,avx2") void aesenclast(SIMD_8x32 K, SIMD_8x32& B) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_VAES void aesenclast(SIMD_8x32 K, SIMD_8x32& B) { B = SIMD_8x32(_mm256_aesenclast_epi128(B.raw(), K.raw())); } -BOTAN_FUNC_ISA_INLINE("vaes,avx2") -void aesenclast(SIMD_8x32 K, SIMD_8x32& B0, SIMD_8x32& B1, SIMD_8x32& B2, SIMD_8x32& B3) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_VAES void aesenclast( + SIMD_8x32 K, SIMD_8x32& B0, SIMD_8x32& B1, SIMD_8x32& B2, SIMD_8x32& B3) { B0 = SIMD_8x32(_mm256_aesenclast_epi128(B0.raw(), K.raw())); B1 = SIMD_8x32(_mm256_aesenclast_epi128(B1.raw(), K.raw())); B2 = SIMD_8x32(_mm256_aesenclast_epi128(B2.raw(), K.raw())); B3 = SIMD_8x32(_mm256_aesenclast_epi128(B3.raw(), K.raw())); } -BOTAN_FUNC_ISA_INLINE("vaes,avx2") void aesdec(SIMD_8x32 K, SIMD_8x32& B) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_VAES void aesdec(SIMD_8x32 K, SIMD_8x32& B) { B = SIMD_8x32(_mm256_aesdec_epi128(B.raw(), K.raw())); } -BOTAN_FUNC_ISA_INLINE("vaes,avx2") -void aesdec(SIMD_8x32 K, SIMD_8x32& B0, SIMD_8x32& B1, SIMD_8x32& B2, SIMD_8x32& B3) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_VAES void aesdec( + SIMD_8x32 K, SIMD_8x32& B0, SIMD_8x32& B1, SIMD_8x32& B2, SIMD_8x32& B3) { B0 = SIMD_8x32(_mm256_aesdec_epi128(B0.raw(), K.raw())); B1 = SIMD_8x32(_mm256_aesdec_epi128(B1.raw(), K.raw())); B2 = SIMD_8x32(_mm256_aesdec_epi128(B2.raw(), K.raw())); B3 = SIMD_8x32(_mm256_aesdec_epi128(B3.raw(), K.raw())); } -BOTAN_FUNC_ISA_INLINE("vaes,avx2") void aesdeclast(SIMD_8x32 K, SIMD_8x32& B) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_VAES void aesdeclast(SIMD_8x32 K, SIMD_8x32& B) { B = SIMD_8x32(_mm256_aesdeclast_epi128(B.raw(), K.raw())); } -BOTAN_FUNC_ISA_INLINE("vaes,avx2") -void aesdeclast(SIMD_8x32 K, SIMD_8x32& B0, SIMD_8x32& B1, SIMD_8x32& B2, SIMD_8x32& B3) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_VAES void aesdeclast( + SIMD_8x32 K, SIMD_8x32& B0, SIMD_8x32& B1, SIMD_8x32& B2, SIMD_8x32& B3) { B0 = SIMD_8x32(_mm256_aesdeclast_epi128(B0.raw(), K.raw())); B1 = SIMD_8x32(_mm256_aesdeclast_epi128(B1.raw(), K.raw())); B2 = SIMD_8x32(_mm256_aesdeclast_epi128(B2.raw(), K.raw())); B3 = SIMD_8x32(_mm256_aesdeclast_epi128(B3.raw(), K.raw())); } +// NOLINTEND(portability-simd-intrinsics) + } // namespace /* * AES-128 Encryption */ -BOTAN_FUNC_ISA("vaes,avx2") void AES_128::x86_vaes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AVX2_VAES void AES_128::x86_vaes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const SIMD_8x32 K0 = SIMD_8x32::load_le128(&m_EK[4 * 0]); const SIMD_8x32 K1 = SIMD_8x32::load_le128(&m_EK[4 * 1]); const SIMD_8x32 K2 = SIMD_8x32::load_le128(&m_EK[4 * 2]); @@ -159,7 +164,7 @@ /* * AES-128 Decryption */ -BOTAN_FUNC_ISA("vaes,avx2") void AES_128::x86_vaes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AVX2_VAES void AES_128::x86_vaes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const SIMD_8x32 K0 = SIMD_8x32::load_le128(&m_DK[4 * 0]); const SIMD_8x32 K1 = SIMD_8x32::load_le128(&m_DK[4 * 1]); const SIMD_8x32 K2 = SIMD_8x32::load_le128(&m_DK[4 * 2]); @@ -244,7 +249,7 @@ /* * AES-192 Encryption */ -BOTAN_FUNC_ISA("vaes,avx2") void AES_192::x86_vaes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AVX2_VAES void AES_192::x86_vaes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const SIMD_8x32 K0 = SIMD_8x32::load_le128(&m_EK[4 * 0]); const SIMD_8x32 K1 = SIMD_8x32::load_le128(&m_EK[4 * 1]); const SIMD_8x32 K2 = SIMD_8x32::load_le128(&m_EK[4 * 2]); @@ -337,7 +342,7 @@ /* * AES-192 Decryption */ -BOTAN_FUNC_ISA("vaes,avx2") void AES_192::x86_vaes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AVX2_VAES void AES_192::x86_vaes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const SIMD_8x32 K0 = SIMD_8x32::load_le128(&m_DK[4 * 0]); const SIMD_8x32 K1 = SIMD_8x32::load_le128(&m_DK[4 * 1]); const SIMD_8x32 K2 = SIMD_8x32::load_le128(&m_DK[4 * 2]); @@ -427,7 +432,7 @@ } } -BOTAN_FUNC_ISA("vaes,avx2") void AES_256::x86_vaes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AVX2_VAES void AES_256::x86_vaes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const SIMD_8x32 K0 = SIMD_8x32::load_le128(&m_EK[4 * 0]); const SIMD_8x32 K1 = SIMD_8x32::load_le128(&m_EK[4 * 1]); const SIMD_8x32 K2 = SIMD_8x32::load_le128(&m_EK[4 * 2]); @@ -528,7 +533,7 @@ /* * AES-256 Decryption */ -BOTAN_FUNC_ISA("vaes,avx2") void AES_256::x86_vaes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +BOTAN_FN_ISA_AVX2_VAES void AES_256::x86_vaes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const SIMD_8x32 K0 = SIMD_8x32::load_le128(&m_DK[4 * 0]); const SIMD_8x32 K1 = SIMD_8x32::load_le128(&m_DK[4 * 1]); const SIMD_8x32 K2 = SIMD_8x32::load_le128(&m_DK[4 * 2]); diff -Nru botan3-3.7.1+dfsg/src/lib/block/aes/aes_vaes/info.txt botan3-3.12.0+dfsg/src/lib/block/aes/aes_vaes/info.txt --- botan3-3.7.1+dfsg/src/lib/block/aes/aes_vaes/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/aes/aes_vaes/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + AES_VAES -> 20240803 - + name -> "AES-VAES" @@ -8,6 +8,7 @@ +cpuid simd_avx2 diff -Nru botan3-3.7.1+dfsg/src/lib/block/aes/aes_vperm/aes_vperm.cpp botan3-3.12.0+dfsg/src/lib/block/aes/aes_vperm/aes_vperm.cpp --- botan3-3.7.1+dfsg/src/lib/block/aes/aes_vperm/aes_vperm.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/aes/aes_vperm/aes_vperm.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -13,212 +13,156 @@ #include #include -#include - -#if defined(BOTAN_SIMD_USE_SSE2) - #include -#endif +#include +#include +#include +#include namespace Botan { namespace { -inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) shuffle(SIMD_4x32 a, SIMD_4x32 b) { -#if defined(BOTAN_SIMD_USE_SSE2) - return SIMD_4x32(_mm_shuffle_epi8(a.raw(), b.raw())); -#elif defined(BOTAN_SIMD_USE_NEON) - const uint8x16_t tbl = vreinterpretq_u8_u32(a.raw()); - const uint8x16_t idx = vreinterpretq_u8_u32(b.raw()); - - #if defined(BOTAN_TARGET_ARCH_IS_ARM32) - const uint8x8x2_t tbl2 = {vget_low_u8(tbl), vget_high_u8(tbl)}; - - return SIMD_4x32( - vreinterpretq_u32_u8(vcombine_u8(vtbl2_u8(tbl2, vget_low_u8(idx)), vtbl2_u8(tbl2, vget_high_u8(idx))))); - - #else - return SIMD_4x32(vreinterpretq_u32_u8(vqtbl1q_u8(tbl, idx))); - #endif - -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - - const auto zero = vec_splat_s8(0x00); - const auto mask = vec_cmplt(reinterpret_cast<__vector signed char>(b.raw()), zero); - const auto r = vec_perm(reinterpret_cast<__vector signed char>(a.raw()), - reinterpret_cast<__vector signed char>(a.raw()), - reinterpret_cast<__vector unsigned char>(b.raw())); - return SIMD_4x32(reinterpret_cast<__vector unsigned int>(vec_sel(r, zero, mask))); - -#else - #error "No shuffle implementation available" -#endif -} - -inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) alignr8(SIMD_4x32 a, SIMD_4x32 b) { -#if defined(BOTAN_SIMD_USE_SSE2) - return SIMD_4x32(_mm_alignr_epi8(a.raw(), b.raw(), 8)); -#elif defined(BOTAN_SIMD_USE_NEON) - return SIMD_4x32(vextq_u32(b.raw(), a.raw(), 2)); -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - const __vector unsigned char mask = {8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; - return SIMD_4x32(vec_perm(b.raw(), a.raw(), mask)); -#else - #error "No alignr8 implementation available" -#endif -} - -const SIMD_4x32 k_ipt1 = SIMD_4x32(0x5A2A7000, 0xC2B2E898, 0x52227808, 0xCABAE090); -const SIMD_4x32 k_ipt2 = SIMD_4x32(0x317C4D00, 0x4C01307D, 0xB0FDCC81, 0xCD80B1FC); - -const SIMD_4x32 k_inv1 = SIMD_4x32(0x0D080180, 0x0E05060F, 0x0A0B0C02, 0x04070309); -const SIMD_4x32 k_inv2 = SIMD_4x32(0x0F0B0780, 0x01040A06, 0x02050809, 0x030D0E0C); - -const SIMD_4x32 sb1u = SIMD_4x32(0xCB503E00, 0xB19BE18F, 0x142AF544, 0xA5DF7A6E); -const SIMD_4x32 sb1t = SIMD_4x32(0xFAE22300, 0x3618D415, 0x0D2ED9EF, 0x3BF7CCC1); -const SIMD_4x32 sbou = SIMD_4x32(0x6FBDC700, 0xD0D26D17, 0xC502A878, 0x15AABF7A); -const SIMD_4x32 sbot = SIMD_4x32(0x5FBB6A00, 0xCFE474A5, 0x412B35FA, 0x8E1E90D1); - -const SIMD_4x32 sboud = SIMD_4x32(0x7EF94000, 0x1387EA53, 0xD4943E2D, 0xC7AA6DB9); -const SIMD_4x32 sbotd = SIMD_4x32(0x93441D00, 0x12D7560F, 0xD8C58E9C, 0xCA4B8159); - -const SIMD_4x32 mc_forward[4] = {SIMD_4x32(0x00030201, 0x04070605, 0x080B0A09, 0x0C0F0E0D), - SIMD_4x32(0x04070605, 0x080B0A09, 0x0C0F0E0D, 0x00030201), - SIMD_4x32(0x080B0A09, 0x0C0F0E0D, 0x00030201, 0x04070605), - SIMD_4x32(0x0C0F0E0D, 0x00030201, 0x04070605, 0x080B0A09)}; - -const SIMD_4x32 vperm_sr[4] = { - SIMD_4x32(0x03020100, 0x07060504, 0x0B0A0908, 0x0F0E0D0C), - SIMD_4x32(0x0F0A0500, 0x030E0904, 0x07020D08, 0x0B06010C), - SIMD_4x32(0x0B020900, 0x0F060D04, 0x030A0108, 0x070E050C), - SIMD_4x32(0x070A0D00, 0x0B0E0104, 0x0F020508, 0x0306090C), -}; - -const SIMD_4x32 rcon[10] = { - SIMD_4x32(0x00000070, 0x00000000, 0x00000000, 0x00000000), - SIMD_4x32(0x0000002A, 0x00000000, 0x00000000, 0x00000000), - SIMD_4x32(0x00000098, 0x00000000, 0x00000000, 0x00000000), - SIMD_4x32(0x00000008, 0x00000000, 0x00000000, 0x00000000), - SIMD_4x32(0x0000004D, 0x00000000, 0x00000000, 0x00000000), - SIMD_4x32(0x0000007C, 0x00000000, 0x00000000, 0x00000000), - SIMD_4x32(0x0000007D, 0x00000000, 0x00000000, 0x00000000), - SIMD_4x32(0x00000081, 0x00000000, 0x00000000, 0x00000000), - SIMD_4x32(0x0000001F, 0x00000000, 0x00000000, 0x00000000), - SIMD_4x32(0x00000083, 0x00000000, 0x00000000, 0x00000000), -}; - -const SIMD_4x32 sb2u = SIMD_4x32(0x0B712400, 0xE27A93C6, 0xBC982FCD, 0x5EB7E955); -const SIMD_4x32 sb2t = SIMD_4x32(0x0AE12900, 0x69EB8840, 0xAB82234A, 0xC2A163C8); - -const SIMD_4x32 k_dipt1 = SIMD_4x32(0x0B545F00, 0x0F505B04, 0x114E451A, 0x154A411E); -const SIMD_4x32 k_dipt2 = SIMD_4x32(0x60056500, 0x86E383E6, 0xF491F194, 0x12771772); - -const SIMD_4x32 sb9u = SIMD_4x32(0x9A86D600, 0x851C0353, 0x4F994CC9, 0xCAD51F50); -const SIMD_4x32 sb9t = SIMD_4x32(0xECD74900, 0xC03B1789, 0xB2FBA565, 0x725E2C9E); - -const SIMD_4x32 sbeu = SIMD_4x32(0x26D4D000, 0x46F29296, 0x64B4F6B0, 0x22426004); -const SIMD_4x32 sbet = SIMD_4x32(0xFFAAC100, 0x0C55A6CD, 0x98593E32, 0x9467F36B); - -const SIMD_4x32 sbdu = SIMD_4x32(0xE6B1A200, 0x7D57CCDF, 0x882A4439, 0xF56E9B13); -const SIMD_4x32 sbdt = SIMD_4x32(0x24C6CB00, 0x3CE2FAF7, 0x15DEEFD3, 0x2931180D); - -const SIMD_4x32 sbbu = SIMD_4x32(0x96B44200, 0xD0226492, 0xB0F2D404, 0x602646F6); -const SIMD_4x32 sbbt = SIMD_4x32(0xCD596700, 0xC19498A6, 0x3255AA6B, 0xF3FF0C3E); - -const SIMD_4x32 mcx[4] = { - SIMD_4x32(0x0C0F0E0D, 0x00030201, 0x04070605, 0x080B0A09), - SIMD_4x32(0x080B0A09, 0x0C0F0E0D, 0x00030201, 0x04070605), - SIMD_4x32(0x04070605, 0x080B0A09, 0x0C0F0E0D, 0x00030201), - SIMD_4x32(0x00030201, 0x04070605, 0x080B0A09, 0x0C0F0E0D), -}; - -const SIMD_4x32 mc_backward[4] = { - SIMD_4x32(0x02010003, 0x06050407, 0x0A09080B, 0x0E0D0C0F), - SIMD_4x32(0x0E0D0C0F, 0x02010003, 0x06050407, 0x0A09080B), - SIMD_4x32(0x0A09080B, 0x0E0D0C0F, 0x02010003, 0x06050407), - SIMD_4x32(0x06050407, 0x0A09080B, 0x0E0D0C0F, 0x02010003), -}; +inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 shuffle(SIMD_4x32 tbl, SIMD_4x32 idx) { + if constexpr(std::endian::native == std::endian::little) { + return SIMD_4x32::byte_shuffle(tbl, idx); + } else { + return SIMD_4x32::byte_shuffle(tbl.bswap(), idx.bswap()).bswap(); + } +} -const SIMD_4x32 lo_nibs_mask = SIMD_4x32::splat_u8(0x0F); +inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 masked_shuffle(SIMD_4x32 tbl, SIMD_4x32 idx) { + if constexpr(std::endian::native == std::endian::little) { + return SIMD_4x32::masked_byte_shuffle(tbl, idx); + } else { + return SIMD_4x32::masked_byte_shuffle(tbl.bswap(), idx.bswap()).bswap(); + } +} -inline SIMD_4x32 low_nibs(SIMD_4x32 x) { +inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 shiftrows(SIMD_4x32 x, size_t r) { + const SIMD_4x32 vperm_sr[4] = { + SIMD_4x32(0x03020100, 0x07060504, 0x0B0A0908, 0x0F0E0D0C), + SIMD_4x32(0x0F0A0500, 0x030E0904, 0x07020D08, 0x0B06010C), + SIMD_4x32(0x0B020900, 0x0F060D04, 0x030A0108, 0x070E050C), + SIMD_4x32(0x070A0D00, 0x0B0E0104, 0x0F020508, 0x0306090C), + }; + + return shuffle(x, vperm_sr[r]); +} + +inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 low_nibs(SIMD_4x32 x) { + const SIMD_4x32 lo_nibs_mask = SIMD_4x32::splat_u8(0x0F); return lo_nibs_mask & x; } -inline SIMD_4x32 high_nibs(SIMD_4x32 x) { +inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 high_nibs(SIMD_4x32 x) { + const SIMD_4x32 lo_nibs_mask = SIMD_4x32::splat_u8(0x0F); return (x.shr<4>() & lo_nibs_mask); } -inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_enc_first_round(SIMD_4x32 B, SIMD_4x32 K) { +inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 aes_enc_first_round(SIMD_4x32 B, SIMD_4x32 K) { + const SIMD_4x32 k_ipt1 = SIMD_4x32(0x5A2A7000, 0xC2B2E898, 0x52227808, 0xCABAE090); + const SIMD_4x32 k_ipt2 = SIMD_4x32(0x317C4D00, 0x4C01307D, 0xB0FDCC81, 0xCD80B1FC); + return shuffle(k_ipt1, low_nibs(B)) ^ shuffle(k_ipt2, high_nibs(B)) ^ K; } -inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_enc_round(SIMD_4x32 B, SIMD_4x32 K, size_t r) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_SIMD_4X32 std::pair aes_decompose_kinv(const SIMD_4x32 B) { + const SIMD_4x32 k_inv1 = SIMD_4x32(0x0D080180, 0x0E05060F, 0x0A0B0C02, 0x04070309); + const SIMD_4x32 k_inv2 = SIMD_4x32(0x0F0B0780, 0x01040A06, 0x02050809, 0x030D0E0C); + const SIMD_4x32 Bh = high_nibs(B); SIMD_4x32 Bl = low_nibs(B); const SIMD_4x32 t2 = shuffle(k_inv2, Bl); Bl ^= Bh; - const SIMD_4x32 t5 = Bl ^ shuffle(k_inv1, t2 ^ shuffle(k_inv1, Bh)); - const SIMD_4x32 t6 = Bh ^ shuffle(k_inv1, t2 ^ shuffle(k_inv1, Bl)); + const SIMD_4x32 t5 = Bl ^ masked_shuffle(k_inv1, t2 ^ shuffle(k_inv1, Bh)); + const SIMD_4x32 t6 = Bh ^ masked_shuffle(k_inv1, t2 ^ shuffle(k_inv1, Bl)); - const SIMD_4x32 t7 = shuffle(sb1t, t6) ^ shuffle(sb1u, t5) ^ K; - const SIMD_4x32 t8 = shuffle(sb2t, t6) ^ shuffle(sb2u, t5) ^ shuffle(t7, mc_forward[r % 4]); + return std::make_pair(t5, t6); +} + +inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 aes_enc_round(SIMD_4x32 B, SIMD_4x32 K, size_t r) { + const SIMD_4x32 sb2u = SIMD_4x32(0x0B712400, 0xE27A93C6, 0xBC982FCD, 0x5EB7E955); + const SIMD_4x32 sb2t = SIMD_4x32(0x0AE12900, 0x69EB8840, 0xAB82234A, 0xC2A163C8); + + const SIMD_4x32 mc_forward[4] = {SIMD_4x32(0x00030201, 0x04070605, 0x080B0A09, 0x0C0F0E0D), + SIMD_4x32(0x04070605, 0x080B0A09, 0x0C0F0E0D, 0x00030201), + SIMD_4x32(0x080B0A09, 0x0C0F0E0D, 0x00030201, 0x04070605), + SIMD_4x32(0x0C0F0E0D, 0x00030201, 0x04070605, 0x080B0A09)}; + const SIMD_4x32 mc_backward[4] = { + SIMD_4x32(0x02010003, 0x06050407, 0x0A09080B, 0x0E0D0C0F), + SIMD_4x32(0x0E0D0C0F, 0x02010003, 0x06050407, 0x0A09080B), + SIMD_4x32(0x0A09080B, 0x0E0D0C0F, 0x02010003, 0x06050407), + SIMD_4x32(0x06050407, 0x0A09080B, 0x0E0D0C0F, 0x02010003), + }; + const SIMD_4x32 sb1u = SIMD_4x32(0xCB503E00, 0xB19BE18F, 0x142AF544, 0xA5DF7A6E); + const SIMD_4x32 sb1t = SIMD_4x32(0xFAE22300, 0x3618D415, 0x0D2ED9EF, 0x3BF7CCC1); + + const auto [t5, t6] = aes_decompose_kinv(B); + + const SIMD_4x32 t7 = masked_shuffle(sb1t, t6) ^ masked_shuffle(sb1u, t5) ^ K; + const SIMD_4x32 t8 = masked_shuffle(sb2t, t6) ^ masked_shuffle(sb2u, t5) ^ shuffle(t7, mc_forward[r % 4]); return shuffle(t8, mc_forward[r % 4]) ^ shuffle(t7, mc_backward[r % 4]) ^ t8; } -inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_enc_last_round(SIMD_4x32 B, SIMD_4x32 K, size_t r) { - const SIMD_4x32 Bh = high_nibs(B); - SIMD_4x32 Bl = low_nibs(B); - const SIMD_4x32 t2 = shuffle(k_inv2, Bl); - Bl ^= Bh; +inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 aes_enc_last_round(SIMD_4x32 B, SIMD_4x32 K, size_t r) { + const SIMD_4x32 sbou = SIMD_4x32(0x6FBDC700, 0xD0D26D17, 0xC502A878, 0x15AABF7A); + const SIMD_4x32 sbot = SIMD_4x32(0x5FBB6A00, 0xCFE474A5, 0x412B35FA, 0x8E1E90D1); - const SIMD_4x32 t5 = Bl ^ shuffle(k_inv1, t2 ^ shuffle(k_inv1, Bh)); - const SIMD_4x32 t6 = Bh ^ shuffle(k_inv1, t2 ^ shuffle(k_inv1, Bl)); + const auto [t5, t6] = aes_decompose_kinv(B); - return shuffle(shuffle(sbou, t5) ^ shuffle(sbot, t6) ^ K, vperm_sr[r % 4]); + return shiftrows(masked_shuffle(sbou, t5) ^ masked_shuffle(sbot, t6) ^ K, r % 4); } -inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_dec_first_round(SIMD_4x32 B, SIMD_4x32 K) { +inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 aes_dec_first_round(SIMD_4x32 B, SIMD_4x32 K) { + const SIMD_4x32 k_dipt1 = SIMD_4x32(0x0B545F00, 0x0F505B04, 0x114E451A, 0x154A411E); + const SIMD_4x32 k_dipt2 = SIMD_4x32(0x60056500, 0x86E383E6, 0xF491F194, 0x12771772); + return shuffle(k_dipt1, low_nibs(B)) ^ shuffle(k_dipt2, high_nibs(B)) ^ K; } -inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_dec_round(SIMD_4x32 B, SIMD_4x32 K, size_t r) { - const SIMD_4x32 Bh = high_nibs(B); - B = low_nibs(B); - const SIMD_4x32 t2 = shuffle(k_inv2, B); +inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 aes_dec_round(SIMD_4x32 B, SIMD_4x32 K, size_t r) { + const SIMD_4x32 mcx[4] = { + SIMD_4x32(0x0C0F0E0D, 0x00030201, 0x04070605, 0x080B0A09), + SIMD_4x32(0x080B0A09, 0x0C0F0E0D, 0x00030201, 0x04070605), + SIMD_4x32(0x04070605, 0x080B0A09, 0x0C0F0E0D, 0x00030201), + SIMD_4x32(0x00030201, 0x04070605, 0x080B0A09, 0x0C0F0E0D), + }; - B ^= Bh; + const SIMD_4x32 sbbu = SIMD_4x32(0x96B44200, 0xD0226492, 0xB0F2D404, 0x602646F6); + const SIMD_4x32 sbbt = SIMD_4x32(0xCD596700, 0xC19498A6, 0x3255AA6B, 0xF3FF0C3E); + const SIMD_4x32 sbdu = SIMD_4x32(0xE6B1A200, 0x7D57CCDF, 0x882A4439, 0xF56E9B13); + const SIMD_4x32 sbdt = SIMD_4x32(0x24C6CB00, 0x3CE2FAF7, 0x15DEEFD3, 0x2931180D); + const SIMD_4x32 sbeu = SIMD_4x32(0x26D4D000, 0x46F29296, 0x64B4F6B0, 0x22426004); + const SIMD_4x32 sbet = SIMD_4x32(0xFFAAC100, 0x0C55A6CD, 0x98593E32, 0x9467F36B); + const SIMD_4x32 sb9u = SIMD_4x32(0x9A86D600, 0x851C0353, 0x4F994CC9, 0xCAD51F50); + const SIMD_4x32 sb9t = SIMD_4x32(0xECD74900, 0xC03B1789, 0xB2FBA565, 0x725E2C9E); - const SIMD_4x32 t5 = B ^ shuffle(k_inv1, t2 ^ shuffle(k_inv1, Bh)); - const SIMD_4x32 t6 = Bh ^ shuffle(k_inv1, t2 ^ shuffle(k_inv1, B)); + const auto [t5, t6] = aes_decompose_kinv(B); const SIMD_4x32 mc = mcx[(r - 1) % 4]; - const SIMD_4x32 t8 = shuffle(sb9t, t6) ^ shuffle(sb9u, t5) ^ K; - const SIMD_4x32 t9 = shuffle(t8, mc) ^ shuffle(sbdu, t5) ^ shuffle(sbdt, t6); - const SIMD_4x32 t12 = shuffle(t9, mc) ^ shuffle(sbbu, t5) ^ shuffle(sbbt, t6); - return shuffle(t12, mc) ^ shuffle(sbeu, t5) ^ shuffle(sbet, t6); + const SIMD_4x32 t8 = masked_shuffle(sb9t, t6) ^ masked_shuffle(sb9u, t5) ^ K; + const SIMD_4x32 t9 = shuffle(t8, mc) ^ masked_shuffle(sbdu, t5) ^ masked_shuffle(sbdt, t6); + const SIMD_4x32 t12 = shuffle(t9, mc) ^ masked_shuffle(sbbu, t5) ^ masked_shuffle(sbbt, t6); + return shuffle(t12, mc) ^ masked_shuffle(sbeu, t5) ^ masked_shuffle(sbet, t6); } -inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_dec_last_round(SIMD_4x32 B, SIMD_4x32 K, size_t r) { - const uint32_t which_sr = ((((r - 1) << 4) ^ 48) & 48) / 16; - - const SIMD_4x32 Bh = high_nibs(B); - B = low_nibs(B); - const SIMD_4x32 t2 = shuffle(k_inv2, B); +inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 aes_dec_last_round(SIMD_4x32 B, SIMD_4x32 K, size_t r) { + const SIMD_4x32 sboud = SIMD_4x32(0x7EF94000, 0x1387EA53, 0xD4943E2D, 0xC7AA6DB9); + const SIMD_4x32 sbotd = SIMD_4x32(0x93441D00, 0x12D7560F, 0xD8C58E9C, 0xCA4B8159); - B ^= Bh; + const uint32_t which_sr = ((((r - 1) << 4) ^ 48) & 48) / 16; - const SIMD_4x32 t5 = B ^ shuffle(k_inv1, t2 ^ shuffle(k_inv1, Bh)); - const SIMD_4x32 t6 = Bh ^ shuffle(k_inv1, t2 ^ shuffle(k_inv1, B)); + const auto [t5, t6] = aes_decompose_kinv(B); - const SIMD_4x32 x = shuffle(sboud, t5) ^ shuffle(sbotd, t6) ^ K; - return shuffle(x, vperm_sr[which_sr]); + const SIMD_4x32 x = masked_shuffle(sboud, t5) ^ masked_shuffle(sbotd, t6) ^ K; + return shiftrows(x, which_sr); } -void BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) - vperm_encrypt_blocks(const uint8_t in[], uint8_t out[], size_t blocks, const SIMD_4x32 K[], size_t rounds) { +void BOTAN_FN_ISA_SIMD_4X32 +vperm_encrypt_blocks(const uint8_t in[], uint8_t out[], size_t blocks, const SIMD_4x32 K[], size_t rounds) { CT::poison(in, blocks * 16); const size_t blocks2 = blocks - (blocks % 2); @@ -259,8 +203,8 @@ CT::unpoison(out, blocks * 16); } -void BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) - vperm_decrypt_blocks(const uint8_t in[], uint8_t out[], size_t blocks, const SIMD_4x32 K[], size_t rounds) { +void BOTAN_FN_ISA_SIMD_4X32 +vperm_decrypt_blocks(const uint8_t in[], uint8_t out[], size_t blocks, const SIMD_4x32 K[], size_t rounds) { CT::poison(in, blocks * 16); const size_t blocks2 = blocks - (blocks % 2); @@ -303,121 +247,121 @@ } // namespace -void AES_128::vperm_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +void BOTAN_FN_ISA_SIMD_4X32 AES_128::vperm_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const SIMD_4x32 K[11] = { - SIMD_4x32(&m_EK[4 * 0]), - SIMD_4x32(&m_EK[4 * 1]), - SIMD_4x32(&m_EK[4 * 2]), - SIMD_4x32(&m_EK[4 * 3]), - SIMD_4x32(&m_EK[4 * 4]), - SIMD_4x32(&m_EK[4 * 5]), - SIMD_4x32(&m_EK[4 * 6]), - SIMD_4x32(&m_EK[4 * 7]), - SIMD_4x32(&m_EK[4 * 8]), - SIMD_4x32(&m_EK[4 * 9]), - SIMD_4x32(&m_EK[4 * 10]), + SIMD_4x32::load_le(&m_EK[4 * 0]), + SIMD_4x32::load_le(&m_EK[4 * 1]), + SIMD_4x32::load_le(&m_EK[4 * 2]), + SIMD_4x32::load_le(&m_EK[4 * 3]), + SIMD_4x32::load_le(&m_EK[4 * 4]), + SIMD_4x32::load_le(&m_EK[4 * 5]), + SIMD_4x32::load_le(&m_EK[4 * 6]), + SIMD_4x32::load_le(&m_EK[4 * 7]), + SIMD_4x32::load_le(&m_EK[4 * 8]), + SIMD_4x32::load_le(&m_EK[4 * 9]), + SIMD_4x32::load_le(&m_EK[4 * 10]), }; return vperm_encrypt_blocks(in, out, blocks, K, 10); } -void AES_128::vperm_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +void BOTAN_FN_ISA_SIMD_4X32 AES_128::vperm_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const SIMD_4x32 K[11] = { - SIMD_4x32(&m_DK[4 * 0]), - SIMD_4x32(&m_DK[4 * 1]), - SIMD_4x32(&m_DK[4 * 2]), - SIMD_4x32(&m_DK[4 * 3]), - SIMD_4x32(&m_DK[4 * 4]), - SIMD_4x32(&m_DK[4 * 5]), - SIMD_4x32(&m_DK[4 * 6]), - SIMD_4x32(&m_DK[4 * 7]), - SIMD_4x32(&m_DK[4 * 8]), - SIMD_4x32(&m_DK[4 * 9]), - SIMD_4x32(&m_DK[4 * 10]), + SIMD_4x32::load_le(&m_DK[4 * 0]), + SIMD_4x32::load_le(&m_DK[4 * 1]), + SIMD_4x32::load_le(&m_DK[4 * 2]), + SIMD_4x32::load_le(&m_DK[4 * 3]), + SIMD_4x32::load_le(&m_DK[4 * 4]), + SIMD_4x32::load_le(&m_DK[4 * 5]), + SIMD_4x32::load_le(&m_DK[4 * 6]), + SIMD_4x32::load_le(&m_DK[4 * 7]), + SIMD_4x32::load_le(&m_DK[4 * 8]), + SIMD_4x32::load_le(&m_DK[4 * 9]), + SIMD_4x32::load_le(&m_DK[4 * 10]), }; return vperm_decrypt_blocks(in, out, blocks, K, 10); } -void AES_192::vperm_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +void BOTAN_FN_ISA_SIMD_4X32 AES_192::vperm_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const SIMD_4x32 K[13] = { - SIMD_4x32(&m_EK[4 * 0]), - SIMD_4x32(&m_EK[4 * 1]), - SIMD_4x32(&m_EK[4 * 2]), - SIMD_4x32(&m_EK[4 * 3]), - SIMD_4x32(&m_EK[4 * 4]), - SIMD_4x32(&m_EK[4 * 5]), - SIMD_4x32(&m_EK[4 * 6]), - SIMD_4x32(&m_EK[4 * 7]), - SIMD_4x32(&m_EK[4 * 8]), - SIMD_4x32(&m_EK[4 * 9]), - SIMD_4x32(&m_EK[4 * 10]), - SIMD_4x32(&m_EK[4 * 11]), - SIMD_4x32(&m_EK[4 * 12]), + SIMD_4x32::load_le(&m_EK[4 * 0]), + SIMD_4x32::load_le(&m_EK[4 * 1]), + SIMD_4x32::load_le(&m_EK[4 * 2]), + SIMD_4x32::load_le(&m_EK[4 * 3]), + SIMD_4x32::load_le(&m_EK[4 * 4]), + SIMD_4x32::load_le(&m_EK[4 * 5]), + SIMD_4x32::load_le(&m_EK[4 * 6]), + SIMD_4x32::load_le(&m_EK[4 * 7]), + SIMD_4x32::load_le(&m_EK[4 * 8]), + SIMD_4x32::load_le(&m_EK[4 * 9]), + SIMD_4x32::load_le(&m_EK[4 * 10]), + SIMD_4x32::load_le(&m_EK[4 * 11]), + SIMD_4x32::load_le(&m_EK[4 * 12]), }; return vperm_encrypt_blocks(in, out, blocks, K, 12); } -void AES_192::vperm_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +void BOTAN_FN_ISA_SIMD_4X32 AES_192::vperm_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const SIMD_4x32 K[13] = { - SIMD_4x32(&m_DK[4 * 0]), - SIMD_4x32(&m_DK[4 * 1]), - SIMD_4x32(&m_DK[4 * 2]), - SIMD_4x32(&m_DK[4 * 3]), - SIMD_4x32(&m_DK[4 * 4]), - SIMD_4x32(&m_DK[4 * 5]), - SIMD_4x32(&m_DK[4 * 6]), - SIMD_4x32(&m_DK[4 * 7]), - SIMD_4x32(&m_DK[4 * 8]), - SIMD_4x32(&m_DK[4 * 9]), - SIMD_4x32(&m_DK[4 * 10]), - SIMD_4x32(&m_DK[4 * 11]), - SIMD_4x32(&m_DK[4 * 12]), + SIMD_4x32::load_le(&m_DK[4 * 0]), + SIMD_4x32::load_le(&m_DK[4 * 1]), + SIMD_4x32::load_le(&m_DK[4 * 2]), + SIMD_4x32::load_le(&m_DK[4 * 3]), + SIMD_4x32::load_le(&m_DK[4 * 4]), + SIMD_4x32::load_le(&m_DK[4 * 5]), + SIMD_4x32::load_le(&m_DK[4 * 6]), + SIMD_4x32::load_le(&m_DK[4 * 7]), + SIMD_4x32::load_le(&m_DK[4 * 8]), + SIMD_4x32::load_le(&m_DK[4 * 9]), + SIMD_4x32::load_le(&m_DK[4 * 10]), + SIMD_4x32::load_le(&m_DK[4 * 11]), + SIMD_4x32::load_le(&m_DK[4 * 12]), }; return vperm_decrypt_blocks(in, out, blocks, K, 12); } -void AES_256::vperm_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +void BOTAN_FN_ISA_SIMD_4X32 AES_256::vperm_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const SIMD_4x32 K[15] = { - SIMD_4x32(&m_EK[4 * 0]), - SIMD_4x32(&m_EK[4 * 1]), - SIMD_4x32(&m_EK[4 * 2]), - SIMD_4x32(&m_EK[4 * 3]), - SIMD_4x32(&m_EK[4 * 4]), - SIMD_4x32(&m_EK[4 * 5]), - SIMD_4x32(&m_EK[4 * 6]), - SIMD_4x32(&m_EK[4 * 7]), - SIMD_4x32(&m_EK[4 * 8]), - SIMD_4x32(&m_EK[4 * 9]), - SIMD_4x32(&m_EK[4 * 10]), - SIMD_4x32(&m_EK[4 * 11]), - SIMD_4x32(&m_EK[4 * 12]), - SIMD_4x32(&m_EK[4 * 13]), - SIMD_4x32(&m_EK[4 * 14]), + SIMD_4x32::load_le(&m_EK[4 * 0]), + SIMD_4x32::load_le(&m_EK[4 * 1]), + SIMD_4x32::load_le(&m_EK[4 * 2]), + SIMD_4x32::load_le(&m_EK[4 * 3]), + SIMD_4x32::load_le(&m_EK[4 * 4]), + SIMD_4x32::load_le(&m_EK[4 * 5]), + SIMD_4x32::load_le(&m_EK[4 * 6]), + SIMD_4x32::load_le(&m_EK[4 * 7]), + SIMD_4x32::load_le(&m_EK[4 * 8]), + SIMD_4x32::load_le(&m_EK[4 * 9]), + SIMD_4x32::load_le(&m_EK[4 * 10]), + SIMD_4x32::load_le(&m_EK[4 * 11]), + SIMD_4x32::load_le(&m_EK[4 * 12]), + SIMD_4x32::load_le(&m_EK[4 * 13]), + SIMD_4x32::load_le(&m_EK[4 * 14]), }; return vperm_encrypt_blocks(in, out, blocks, K, 14); } -void AES_256::vperm_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { +void BOTAN_FN_ISA_SIMD_4X32 AES_256::vperm_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { const SIMD_4x32 K[15] = { - SIMD_4x32(&m_DK[4 * 0]), - SIMD_4x32(&m_DK[4 * 1]), - SIMD_4x32(&m_DK[4 * 2]), - SIMD_4x32(&m_DK[4 * 3]), - SIMD_4x32(&m_DK[4 * 4]), - SIMD_4x32(&m_DK[4 * 5]), - SIMD_4x32(&m_DK[4 * 6]), - SIMD_4x32(&m_DK[4 * 7]), - SIMD_4x32(&m_DK[4 * 8]), - SIMD_4x32(&m_DK[4 * 9]), - SIMD_4x32(&m_DK[4 * 10]), - SIMD_4x32(&m_DK[4 * 11]), - SIMD_4x32(&m_DK[4 * 12]), - SIMD_4x32(&m_DK[4 * 13]), - SIMD_4x32(&m_DK[4 * 14]), + SIMD_4x32::load_le(&m_DK[4 * 0]), + SIMD_4x32::load_le(&m_DK[4 * 1]), + SIMD_4x32::load_le(&m_DK[4 * 2]), + SIMD_4x32::load_le(&m_DK[4 * 3]), + SIMD_4x32::load_le(&m_DK[4 * 4]), + SIMD_4x32::load_le(&m_DK[4 * 5]), + SIMD_4x32::load_le(&m_DK[4 * 6]), + SIMD_4x32::load_le(&m_DK[4 * 7]), + SIMD_4x32::load_le(&m_DK[4 * 8]), + SIMD_4x32::load_le(&m_DK[4 * 9]), + SIMD_4x32::load_le(&m_DK[4 * 10]), + SIMD_4x32::load_le(&m_DK[4 * 11]), + SIMD_4x32::load_le(&m_DK[4 * 12]), + SIMD_4x32::load_le(&m_DK[4 * 13]), + SIMD_4x32::load_le(&m_DK[4 * 14]), }; return vperm_decrypt_blocks(in, out, blocks, K, 14); @@ -425,22 +369,25 @@ namespace { -inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) - aes_schedule_transform(SIMD_4x32 input, SIMD_4x32 table_1, SIMD_4x32 table_2) { +inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 aes_schedule_transform(SIMD_4x32 input, SIMD_4x32 table_1, SIMD_4x32 table_2) { return shuffle(table_1, low_nibs(input)) ^ shuffle(table_2, high_nibs(input)); } -SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_schedule_mangle(SIMD_4x32 k, uint8_t round_no) { +inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 aes_schedule_transform_init(SIMD_4x32 input) { + return aes_enc_first_round(input, SIMD_4x32()); +} + +SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 aes_schedule_mangle(SIMD_4x32 k, uint8_t round_no) { const SIMD_4x32 mc_forward0(0x00030201, 0x04070605, 0x080B0A09, 0x0C0F0E0D); SIMD_4x32 t = shuffle(k ^ SIMD_4x32::splat_u8(0x5B), mc_forward0); SIMD_4x32 t2 = t; t = shuffle(t, mc_forward0); t2 = t ^ t2 ^ shuffle(t, mc_forward0); - return shuffle(t2, vperm_sr[round_no % 4]); + return shiftrows(t2, round_no % 4); } -SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_schedule_mangle_dec(SIMD_4x32 k, uint8_t round_no) { +SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 aes_schedule_mangle_dec(SIMD_4x32 k, uint8_t round_no) { const SIMD_4x32 mc_forward0(0x00030201, 0x04070605, 0x080B0A09, 0x0C0F0E0D); const SIMD_4x32 dsk[8] = { @@ -466,19 +413,18 @@ t = aes_schedule_transform(t, dsk[6], dsk[7]); output = shuffle(t ^ output, mc_forward0); - return shuffle(output, vperm_sr[round_no % 4]); + return shiftrows(output, round_no % 4); } -SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_schedule_mangle_last(SIMD_4x32 k, uint8_t round_no) { +SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 aes_schedule_mangle_last(SIMD_4x32 k, uint8_t round_no) { const SIMD_4x32 out_tr1(0xD6B66000, 0xFF9F4929, 0xDEBE6808, 0xF7974121); const SIMD_4x32 out_tr2(0x50BCEC00, 0x01EDBD51, 0xB05C0CE0, 0xE10D5DB1); - k = shuffle(k, vperm_sr[round_no % 4]); - k ^= SIMD_4x32::splat_u8(0x5B); + k = shiftrows(k, round_no % 4) ^ SIMD_4x32::splat_u8(0x5B); return aes_schedule_transform(k, out_tr1, out_tr2); } -SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_schedule_mangle_last_dec(SIMD_4x32 k) { +SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 aes_schedule_mangle_last_dec(SIMD_4x32 k) { const SIMD_4x32 deskew1(0x47A4E300, 0x07E4A340, 0x5DBEF91A, 0x1DFEB95A); const SIMD_4x32 deskew2(0x83EA6900, 0x5F36B5DC, 0xF49D1E77, 0x2841C2AB); @@ -486,31 +432,39 @@ return aes_schedule_transform(k, deskew1, deskew2); } -SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_schedule_round(SIMD_4x32 input1, SIMD_4x32 input2) { +SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 aes_schedule_round(SIMD_4x32 input1, SIMD_4x32 input2) { + const SIMD_4x32 sb1u = SIMD_4x32(0xCB503E00, 0xB19BE18F, 0x142AF544, 0xA5DF7A6E); + const SIMD_4x32 sb1t = SIMD_4x32(0xFAE22300, 0x3618D415, 0x0D2ED9EF, 0x3BF7CCC1); + SIMD_4x32 smeared = input2 ^ input2.shift_elems_left<1>(); smeared ^= smeared.shift_elems_left<2>(); smeared ^= SIMD_4x32::splat_u8(0x5B); - const SIMD_4x32 Bh = high_nibs(input1); - SIMD_4x32 Bl = low_nibs(input1); + const auto [t5, t6] = aes_decompose_kinv(input1); - const SIMD_4x32 t2 = shuffle(k_inv2, Bl); - - Bl ^= Bh; - - SIMD_4x32 t5 = Bl ^ shuffle(k_inv1, t2 ^ shuffle(k_inv1, Bh)); - SIMD_4x32 t6 = Bh ^ shuffle(k_inv1, t2 ^ shuffle(k_inv1, Bl)); - - return smeared ^ shuffle(sb1u, t5) ^ shuffle(sb1t, t6); + return smeared ^ masked_shuffle(sb1u, t5) ^ masked_shuffle(sb1t, t6); } -SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_schedule_round(SIMD_4x32 rc, SIMD_4x32 input1, SIMD_4x32 input2) { +SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 aes_schedule_round_rcon(size_t rc, SIMD_4x32 input1, SIMD_4x32 input2) { + const SIMD_4x32 rcon[10] = { + SIMD_4x32(0x00000070, 0x00000000, 0x00000000, 0x00000000), + SIMD_4x32(0x0000002A, 0x00000000, 0x00000000, 0x00000000), + SIMD_4x32(0x00000098, 0x00000000, 0x00000000, 0x00000000), + SIMD_4x32(0x00000008, 0x00000000, 0x00000000, 0x00000000), + SIMD_4x32(0x0000004D, 0x00000000, 0x00000000, 0x00000000), + SIMD_4x32(0x0000007C, 0x00000000, 0x00000000, 0x00000000), + SIMD_4x32(0x0000007D, 0x00000000, 0x00000000, 0x00000000), + SIMD_4x32(0x00000081, 0x00000000, 0x00000000, 0x00000000), + SIMD_4x32(0x0000001F, 0x00000000, 0x00000000, 0x00000000), + SIMD_4x32(0x00000083, 0x00000000, 0x00000000, 0x00000000), + }; + // This byte shuffle is equivalent to alignr<1>(shuffle32(input1, (3,3,3,3))); const SIMD_4x32 shuffle3333_15 = SIMD_4x32::splat(0x0C0F0E0D); - return aes_schedule_round(shuffle(input1, shuffle3333_15), input2 ^ rc); + return aes_schedule_round(shuffle(input1, shuffle3333_15), input2 ^ rcon[rc]); } -SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_schedule_192_smear(SIMD_4x32 x, SIMD_4x32 y) { +SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 aes_schedule_192_smear(SIMD_4x32 x, SIMD_4x32 y) { const SIMD_4x32 shuffle3332 = SIMD_4x32(0x0B0A0908, 0x0F0E0D0C, 0x0F0E0D0C, 0x0F0E0D0C); const SIMD_4x32 shuffle2000 = SIMD_4x32(0x03020100, 0x03020100, 0x03020100, 0x0B0A0908); @@ -521,49 +475,52 @@ } // namespace -void AES_128::vperm_key_schedule(const uint8_t keyb[], size_t /*unused*/) { +// NOLINTBEGIN(readability-container-data-pointer) + +void BOTAN_FN_ISA_SIMD_4X32 AES_128::vperm_key_schedule(const uint8_t keyb[], size_t /*unused*/) { m_EK.resize(11 * 4); m_DK.resize(11 * 4); SIMD_4x32 key = SIMD_4x32::load_le(keyb); - shuffle(key, vperm_sr[2]).store_le(&m_DK[4 * 10]); + shiftrows(key, 2).store_le(&m_DK[4 * 10]); - key = aes_schedule_transform(key, k_ipt1, k_ipt2); + key = aes_schedule_transform_init(key); key.store_le(&m_EK[0]); for(size_t i = 1; i != 10; ++i) { - key = aes_schedule_round(rcon[i - 1], key, key); + key = aes_schedule_round_rcon(i - 1, key, key); aes_schedule_mangle(key, (12 - i) % 4).store_le(&m_EK[4 * i]); aes_schedule_mangle_dec(key, (10 - i) % 4).store_le(&m_DK[4 * (10 - i)]); } - key = aes_schedule_round(rcon[9], key, key); + key = aes_schedule_round_rcon(9, key, key); aes_schedule_mangle_last(key, 2).store_le(&m_EK[4 * 10]); aes_schedule_mangle_last_dec(key).store_le(&m_DK[0]); } -void AES_192::vperm_key_schedule(const uint8_t keyb[], size_t /*unused*/) { +void BOTAN_FN_ISA_SIMD_4X32 AES_192::vperm_key_schedule(const uint8_t keyb[], size_t /*unused*/) { m_EK.resize(13 * 4); m_DK.resize(13 * 4); SIMD_4x32 key1 = SIMD_4x32::load_le(keyb); SIMD_4x32 key2 = SIMD_4x32::load_le(keyb + 8); - shuffle(key1, vperm_sr[0]).store_le(&m_DK[12 * 4]); + shiftrows(key1, 0).store_le(&m_DK[12 * 4]); - key1 = aes_schedule_transform(key1, k_ipt1, k_ipt2); - key2 = aes_schedule_transform(key2, k_ipt1, k_ipt2); + key1 = aes_schedule_transform_init(key1); + key2 = aes_schedule_transform_init(key2); key1.store_le(&m_EK[0]); for(size_t i = 0; i != 4; ++i) { // key2 with 8 high bytes masked off SIMD_4x32 t = key2; - key2 = aes_schedule_round(rcon[2 * i], key2, key1); - const SIMD_4x32 key2t = alignr8(key2, t); + key2 = aes_schedule_round_rcon(2 * i, key2, key1); + const auto key2t = SIMD_4x32::alignr8(key2, t); + aes_schedule_mangle(key2t, (i + 3) % 4).store_le(&m_EK[4 * (3 * i + 1)]); aes_schedule_mangle_dec(key2t, (i + 3) % 4).store_le(&m_DK[4 * (11 - 3 * i)]); @@ -572,7 +529,7 @@ aes_schedule_mangle(t, (i + 2) % 4).store_le(&m_EK[4 * (3 * i + 2)]); aes_schedule_mangle_dec(t, (i + 2) % 4).store_le(&m_DK[4 * (10 - 3 * i)]); - key2 = aes_schedule_round(rcon[2 * i + 1], t, key2); + key2 = aes_schedule_round_rcon(2 * i + 1, t, key2); if(i == 3) { aes_schedule_mangle_last(key2, (i + 1) % 4).store_le(&m_EK[4 * (3 * i + 3)]); @@ -587,17 +544,17 @@ } } -void AES_256::vperm_key_schedule(const uint8_t keyb[], size_t /*unused*/) { +void BOTAN_FN_ISA_SIMD_4X32 AES_256::vperm_key_schedule(const uint8_t keyb[], size_t /*unused*/) { m_EK.resize(15 * 4); m_DK.resize(15 * 4); SIMD_4x32 key1 = SIMD_4x32::load_le(keyb); SIMD_4x32 key2 = SIMD_4x32::load_le(keyb + 16); - shuffle(key1, vperm_sr[2]).store_le(&m_DK[4 * 14]); + shiftrows(key1, 2).store_le(&m_DK[4 * 14]); - key1 = aes_schedule_transform(key1, k_ipt1, k_ipt2); - key2 = aes_schedule_transform(key2, k_ipt1, k_ipt2); + key1 = aes_schedule_transform_init(key1); + key2 = aes_schedule_transform_init(key2); key1.store_le(&m_EK[0]); aes_schedule_mangle(key2, 3).store_le(&m_EK[4]); @@ -608,7 +565,7 @@ for(size_t i = 2; i != 14; i += 2) { const SIMD_4x32 k_t = key2; - key1 = key2 = aes_schedule_round(rcon[(i / 2) - 1], key2, key1); + key1 = key2 = aes_schedule_round_rcon((i / 2) - 1, key2, key1); aes_schedule_mangle(key2, i % 4).store_le(&m_EK[4 * i]); aes_schedule_mangle_dec(key2, (i + 2) % 4).store_le(&m_DK[4 * (14 - i)]); @@ -619,10 +576,12 @@ aes_schedule_mangle_dec(key2, (i + 1) % 4).store_le(&m_DK[4 * (13 - i)]); } - key2 = aes_schedule_round(rcon[6], key2, key1); + key2 = aes_schedule_round_rcon(6, key2, key1); aes_schedule_mangle_last(key2, 2).store_le(&m_EK[4 * 14]); aes_schedule_mangle_last_dec(key2).store_le(&m_DK[0]); } +// NOLINTEND(readability-container-data-pointer) + } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/aes/aes_vperm/info.txt botan3-3.12.0+dfsg/src/lib/block/aes/aes_vperm/info.txt --- botan3-3.7.1+dfsg/src/lib/block/aes/aes_vperm/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/aes/aes_vperm/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,34 +1,35 @@ - + AES_VPERM -> 20190901 - + name -> "AES Vector Permutation" brief -> "AES using Vector Permutation Instructions" -endian little - -x86_32:sse2 -x86_64:sse2 x86_32:ssse3 x86_64:ssse3 +x32:ssse3 arm32:neon arm64:neon -ppc32:altivec ppc64:altivec +loongarch64:lsx +wasm:simd128 x86_32 x86_64 +x32 arm32 arm64 -ppc32 ppc64 +loongarch64 +wasm -simd +cpuid +simd_4x32 diff -Nru botan3-3.7.1+dfsg/src/lib/block/aria/aria.cpp botan3-3.12.0+dfsg/src/lib/block/aria/aria.cpp --- botan3-3.7.1+dfsg/src/lib/block/aria/aria.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/aria/aria.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -23,6 +23,10 @@ #include #include +#if defined(BOTAN_HAS_CPUID) + #include +#endif + namespace Botan { namespace { @@ -170,7 +174,10 @@ const size_t ROUNDS = (KS.size() / 4) - 1; for(size_t i = 0; i != blocks; ++i) { - uint32_t t0, t1, t2, t3; + uint32_t t0 = 0; + uint32_t t1 = 0; + uint32_t t2 = 0; + uint32_t t3 = 0; load_be(in + 16 * i, t0, t1, t2, t3); for(size_t r = 0; r < ROUNDS; r += 2) { @@ -317,7 +324,7 @@ ERK.resize(4 * 17); } - ARIA_ROL128<19>(w0, w1, &ERK[0]); + ARIA_ROL128<19>(w0, w1, &ERK[0]); // NOLINT(*-container-data-pointer) ARIA_ROL128<19>(w1, w2, &ERK[4]); ARIA_ROL128<19>(w2, w3, &ERK[8]); ARIA_ROL128<19>(w3, w0, &ERK[12]); @@ -362,31 +369,109 @@ void ARIA_128::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); + +#if defined(BOTAN_HAS_ARIA_AVX512_GFNI) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return aria_avx512_gfni_encrypt(in, out, blocks); + } +#endif + +#if defined(BOTAN_HAS_ARIA_HWAES) + if(CPUID::has(CPUID::Feature::HW_AES)) { + return aria_hwaes_encrypt(in, out, blocks); + } +#endif + ARIA_F::transform(in, out, blocks, m_ERK); } void ARIA_192::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); + +#if defined(BOTAN_HAS_ARIA_AVX512_GFNI) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return aria_avx512_gfni_encrypt(in, out, blocks); + } +#endif + +#if defined(BOTAN_HAS_ARIA_HWAES) + if(CPUID::has(CPUID::Feature::HW_AES)) { + return aria_hwaes_encrypt(in, out, blocks); + } +#endif + ARIA_F::transform(in, out, blocks, m_ERK); } void ARIA_256::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); + +#if defined(BOTAN_HAS_ARIA_AVX512_GFNI) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return aria_avx512_gfni_encrypt(in, out, blocks); + } +#endif + +#if defined(BOTAN_HAS_ARIA_HWAES) + if(CPUID::has(CPUID::Feature::HW_AES)) { + return aria_hwaes_encrypt(in, out, blocks); + } +#endif + ARIA_F::transform(in, out, blocks, m_ERK); } void ARIA_128::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); + +#if defined(BOTAN_HAS_ARIA_AVX512_GFNI) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return aria_avx512_gfni_decrypt(in, out, blocks); + } +#endif + +#if defined(BOTAN_HAS_ARIA_HWAES) + if(CPUID::has(CPUID::Feature::HW_AES)) { + return aria_hwaes_decrypt(in, out, blocks); + } +#endif + ARIA_F::transform(in, out, blocks, m_DRK); } void ARIA_192::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); + +#if defined(BOTAN_HAS_ARIA_AVX512_GFNI) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return aria_avx512_gfni_decrypt(in, out, blocks); + } +#endif + +#if defined(BOTAN_HAS_ARIA_HWAES) + if(CPUID::has(CPUID::Feature::HW_AES)) { + return aria_hwaes_decrypt(in, out, blocks); + } +#endif + ARIA_F::transform(in, out, blocks, m_DRK); } void ARIA_256::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); + +#if defined(BOTAN_HAS_ARIA_AVX512_GFNI) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return aria_avx512_gfni_decrypt(in, out, blocks); + } +#endif + +#if defined(BOTAN_HAS_ARIA_HWAES) + if(CPUID::has(CPUID::Feature::HW_AES)) { + return aria_hwaes_decrypt(in, out, blocks); + } +#endif + ARIA_F::transform(in, out, blocks, m_DRK); } @@ -402,6 +487,66 @@ return !m_ERK.empty(); } +namespace { + +size_t aria_parallelism() { +#if defined(BOTAN_HAS_ARIA_AVX512_GFNI) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return 16; + } +#endif + +#if defined(BOTAN_HAS_ARIA_HWAES) + if(CPUID::has(CPUID::Feature::HW_AES)) { + return 4; + } +#endif + + return 1; +} + +std::string aria_provider() { +#if defined(BOTAN_HAS_ARIA_AVX512_GFNI) + if(auto feat = CPUID::check(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return *feat; + } +#endif + +#if defined(BOTAN_HAS_ARIA_HWAES) + if(auto feat = CPUID::check(CPUID::Feature::HW_AES)) { + return *feat; + } +#endif + + return "base"; +} + +} // namespace + +size_t ARIA_128::parallelism() const { + return aria_parallelism(); +} + +std::string ARIA_128::provider() const { + return aria_provider(); +} + +size_t ARIA_192::parallelism() const { + return aria_parallelism(); +} + +std::string ARIA_192::provider() const { + return aria_provider(); +} + +size_t ARIA_256::parallelism() const { + return aria_parallelism(); +} + +std::string ARIA_256::provider() const { + return aria_provider(); +} + void ARIA_128::key_schedule(std::span key) { ARIA_F::key_schedule(m_ERK, m_DRK, key); } diff -Nru botan3-3.7.1+dfsg/src/lib/block/aria/aria.h botan3-3.12.0+dfsg/src/lib/block/aria/aria.h --- botan3-3.7.1+dfsg/src/lib/block/aria/aria.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/aria/aria.h 2026-05-07 01:38:28.000000000 +0000 @@ -17,6 +17,7 @@ #define BOTAN_ARIA_H_ #include +#include namespace Botan { @@ -34,11 +35,23 @@ std::unique_ptr new_object() const override { return std::make_unique(); } + std::string provider() const override; + size_t parallelism() const override; bool has_keying_material() const override; private: void key_schedule(std::span key) override; +#if defined(BOTAN_HAS_ARIA_AVX512_GFNI) + void aria_avx512_gfni_encrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; + void aria_avx512_gfni_decrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; +#endif + +#if defined(BOTAN_HAS_ARIA_HWAES) + void aria_hwaes_encrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; + void aria_hwaes_decrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; +#endif + // Encryption and Decryption round keys. secure_vector m_ERK, m_DRK; }; @@ -57,11 +70,23 @@ std::unique_ptr new_object() const override { return std::make_unique(); } + std::string provider() const override; + size_t parallelism() const override; bool has_keying_material() const override; private: void key_schedule(std::span key) override; +#if defined(BOTAN_HAS_ARIA_AVX512_GFNI) + void aria_avx512_gfni_encrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; + void aria_avx512_gfni_decrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; +#endif + +#if defined(BOTAN_HAS_ARIA_HWAES) + void aria_hwaes_encrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; + void aria_hwaes_decrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; +#endif + // Encryption and Decryption round keys. secure_vector m_ERK, m_DRK; }; @@ -80,11 +105,23 @@ std::unique_ptr new_object() const override { return std::make_unique(); } + std::string provider() const override; + size_t parallelism() const override; bool has_keying_material() const override; private: void key_schedule(std::span key) override; +#if defined(BOTAN_HAS_ARIA_AVX512_GFNI) + void aria_avx512_gfni_encrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; + void aria_avx512_gfni_decrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; +#endif + +#if defined(BOTAN_HAS_ARIA_HWAES) + void aria_hwaes_encrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; + void aria_hwaes_decrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; +#endif + // Encryption and Decryption round keys. secure_vector m_ERK, m_DRK; }; diff -Nru botan3-3.7.1+dfsg/src/lib/block/aria/aria_avx512_gfni/aria_avx512_gfni.cpp botan3-3.12.0+dfsg/src/lib/block/aria/aria_avx512_gfni/aria_avx512_gfni.cpp --- botan3-3.7.1+dfsg/src/lib/block/aria/aria_avx512_gfni/aria_avx512_gfni.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/aria/aria_avx512_gfni/aria_avx512_gfni.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,390 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include + +namespace Botan { + +namespace ARIA_AVX512 { + +namespace { + +/* +* ARIA has two S-boxes pairs S1/X1 (the Rijndael sbox and its inverse) +* and S2/X2 (another sbox and its inverse), all of which can be described +* as an affine transformation applied to an inversion in GF(2^8) +* +* A very helpful reference for this implementation was +* +* "AVX-Based Acceleration of ARIA Block Cipher Algorithm" +* by Yoo, Kivilinna, Cho. +* IEEE Access, Vol. 11, 2023 (DOI: 10.1109/ACCESS.2023.3298026) +* +* +* The paper describes the sbox decompositions (Section IV. A. 1.) +* +* S1(x) = A_S1(inv(x)) -> affineinv(AFF_S1, x, 0x63) +* S2(x) = A_S2(inv(x)) -> affineinv(AFF_S2, x, 0xE2) +* X1(x) = inv(A_{S1^-1}(x)) -> affine(AFF_X1, x, 0x05) then affineinv(I, y, 0) +* X2(x) = inv(A_{S2^-1}(x)) -> affine(AFF_X2, x, 0x2C) then affineinv(I, y, 0) +* +* where inv(x) = x^-1 in GF(2^8), implemented by the GFNI affineinv instruction +* and the AFF_* matrixes are the constants following. +* +* The approach used here diverges from the implementation described in the +* paper; they used AVX-512 to compute 64 blocks in parallel. This implementation +* instead takes advantage of the fact that AVX-512/GFNI can use 4 different GFNI +* affine constants in a single call, and so needs only 16 block chunks. This +* leads to less register pressure and (imo) a simpler implementation, albeit likely +* giving up some performance with larger input sizes. +*/ + +constexpr uint64_t AFF_S1 = gfni_matrix(R"( + 1 0 0 0 1 1 1 1 + 1 1 0 0 0 1 1 1 + 1 1 1 0 0 0 1 1 + 1 1 1 1 0 0 0 1 + 1 1 1 1 1 0 0 0 + 0 1 1 1 1 1 0 0 + 0 0 1 1 1 1 1 0 + 0 0 0 1 1 1 1 1)"); + +constexpr uint64_t AFF_S2 = gfni_matrix(R"( + 0 1 0 1 0 1 1 1 + 0 0 1 1 1 1 1 1 + 1 1 1 0 1 1 0 1 + 1 1 0 0 0 0 1 1 + 0 1 0 0 0 0 1 1 + 1 1 0 0 1 1 1 0 + 0 1 1 0 0 0 1 1 + 1 1 1 1 0 1 1 0)"); + +constexpr uint64_t AFF_X1 = gfni_matrix(R"( + 0 0 1 0 0 1 0 1 + 1 0 0 1 0 0 1 0 + 0 1 0 0 1 0 0 1 + 1 0 1 0 0 1 0 0 + 0 1 0 1 0 0 1 0 + 0 0 1 0 1 0 0 1 + 1 0 0 1 0 1 0 0 + 0 1 0 0 1 0 1 0)"); + +constexpr uint64_t AFF_X2 = gfni_matrix(R"( + 0 0 0 1 1 0 0 0 + 0 0 1 0 0 1 1 0 + 0 0 0 0 1 0 1 0 + 1 1 1 0 0 0 1 1 + 1 1 1 0 1 1 0 0 + 0 1 1 0 1 0 1 1 + 1 0 1 1 1 1 0 1 + 1 0 0 1 0 0 1 1)"); + +// GFNI identity matrix +constexpr uint64_t IDENTITY = gfni_matrix(R"( + 1 0 0 0 0 0 0 0 + 0 1 0 0 0 0 0 0 + 0 0 1 0 0 0 0 0 + 0 0 0 1 0 0 0 0 + 0 0 0 0 1 0 0 0 + 0 0 0 0 0 1 0 0 + 0 0 0 0 0 0 1 0 + 0 0 0 0 0 0 0 1)"); + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI SIMD_16x32 +apply_aria_sbox(SIMD_16x32 x, __m512i pre_mat, __m512i pre_const, __m512i post_mat, __m512i post_const) { + /* + * After transposing the blocks, we have 4 16-word registers where register 0 contains + * all of the first words of the block, etc. + * + * However ARIA wants to send adjacent bytes of each word through the 4 different + * sboxes (either S1||S2||X1||X2 for "FE rounds" or X1||X2||S1||S2 for "FO rounds"). + * This is handled here by using a permutation to send the 16 first bytes into the + * first zmm lane, the 16 second bytes in the second zmm lane, etc. GFNI lets you + * specify different affine matrices for each lane so we can then compute all 4 sboxes + * with a single sequence. We cannot make use of GFNI's builtin XOR/add instruction, + * since we need to use different constants for each lane, but this just requires an + * extra XOR instruction after the GFNI instructions. + */ + + const __m512i fwd_perm = _mm512_set_epi64(0x3F3B37332F2B2723, + 0x1F1B17130F0B0703, + 0x3E3A36322E2A2622, + 0x1E1A16120E0A0602, + 0x3D3935312D292521, + 0x1D1915110D090501, + 0x3C3834302C282420, + 0x1C1814100C080400); + + const __m512i inv_perm = _mm512_set_epi64(0x3F2F1F0F3E2E1E0E, + 0x3D2D1D0D3C2C1C0C, + 0x3B2B1B0B3A2A1A0A, + 0x3929190938281808, + 0x3727170736261606, + 0x3525150534241404, + 0x3323130332221202, + 0x3121110130201000); + + // Permute to align bytes into the 128-bit sbox lanes + __m512i v = _mm512_permutexvar_epi8(fwd_perm, x.raw()); + + // The sbox magic + v = _mm512_xor_si512(_mm512_gf2p8affine_epi64_epi8(v, pre_mat, 0), pre_const); + v = _mm512_xor_si512(_mm512_gf2p8affineinv_epi64_epi8(v, post_mat, 0), post_const); + + // Permute back to standard ordering + v = _mm512_permutexvar_epi8(inv_perm, v); + return SIMD_16x32(v); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI SIMD_16x32 apply_fo_sbox(SIMD_16x32 x) { + /* + * FO is S1 || S2 || X1 || X2 + * + * S1/S2 requires the affine transformation after the inversion, likewise X1/X2 requires + * the affine transformation before the inversion. So half of the matrices in use for + * each instruction are the identity. + */ + const __m512i fo_pre_mat = _mm512_set_epi64(IDENTITY, IDENTITY, IDENTITY, IDENTITY, AFF_X1, AFF_X1, AFF_X2, AFF_X2); + + const __m512i fo_post_mat = _mm512_set_epi64(AFF_S1, AFF_S1, AFF_S2, AFF_S2, IDENTITY, IDENTITY, IDENTITY, IDENTITY); + + const __m512i fo_pre_const = _mm512_set_epi64(0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0505050505050505, + 0x0505050505050505, + 0x2C2C2C2C2C2C2C2C, + 0x2C2C2C2C2C2C2C2C); + + const __m512i fo_post_const = _mm512_set_epi64(0x6363636363636363, + 0x6363636363636363, + 0xE2E2E2E2E2E2E2E2, + 0xE2E2E2E2E2E2E2E2, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000); + + return apply_aria_sbox(x, fo_pre_mat, fo_pre_const, fo_post_mat, fo_post_const); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI SIMD_16x32 apply_fe_sbox(SIMD_16x32 x) { + const __m512i fe_pre_mat = _mm512_set_epi64(AFF_X1, AFF_X1, AFF_X2, AFF_X2, IDENTITY, IDENTITY, IDENTITY, IDENTITY); + + const __m512i fe_post_mat = _mm512_set_epi64(IDENTITY, IDENTITY, IDENTITY, IDENTITY, AFF_S1, AFF_S1, AFF_S2, AFF_S2); + + const __m512i fe_pre_const = _mm512_set_epi64(0x0505050505050505, + 0x0505050505050505, + 0x2C2C2C2C2C2C2C2C, + 0x2C2C2C2C2C2C2C2C, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000); + + const __m512i fe_post_const = _mm512_set_epi64(0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x6363636363636363, + 0x6363636363636363, + 0xE2E2E2E2E2E2E2E2, + 0xE2E2E2E2E2E2E2E2); + + return apply_aria_sbox(x, fe_pre_mat, fe_pre_const, fe_post_mat, fe_post_const); +} + +BOTAN_FN_ISA_AVX512 BOTAN_FORCE_INLINE SIMD_16x32 swap_abcd_badc(SIMD_16x32 x) { + // Why you no 16-bit rotate Intel? + + const __m512i rol16 = _mm512_set_epi64(0x0E0F0C0D0A0B0809, + 0x0607040502030001, + 0x0E0F0C0D0A0B0809, + 0x0607040502030001, + 0x0E0F0C0D0A0B0809, + 0x0607040502030001, + 0x0E0F0C0D0A0B0809, + 0x0607040502030001); + + return SIMD_16x32(_mm512_shuffle_epi8(x.raw(), rol16)); +} + +/* +* This applies mixing in much the same way as the M1/M2/M3/M4 constants in the +* scalar/table version in aria.cpp (ARIA_F1/ARIA_F2) +* +* Notice that the constants are rotational and each has the property that it +* maps the byte into all 3 of the other bytes, ie byte 0 goes into bytes 1,2,3, +* then byte 1 goes into bytes 0,2,3, .... +* +* This is neatly handled by XORing together rotations of the words +*/ +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512 SIMD_16x32 aria_fo_m(SIMD_16x32 x) { + return x.rotl<8>() ^ x.rotl<16>() ^ x.rotl<24>(); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512 SIMD_16x32 aria_fe_m(SIMD_16x32 x) { + return x ^ x.rotl<8>() ^ x.rotl<24>(); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512 void aria_mix(SIMD_16x32& B0, SIMD_16x32& B1, SIMD_16x32& B2, SIMD_16x32& B3) { + B1 ^= B2; + B2 ^= B3; + B0 ^= B1; + B3 ^= B1; + B2 ^= B0; + B1 ^= B2; +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI void aria_fo(SIMD_16x32& B0, + SIMD_16x32& B1, + SIMD_16x32& B2, + SIMD_16x32& B3) { + B0 = aria_fo_m(apply_fo_sbox(B0)); + B1 = aria_fo_m(apply_fo_sbox(B1)); + B2 = aria_fo_m(apply_fo_sbox(B2)); + B3 = aria_fo_m(apply_fo_sbox(B3)); + + aria_mix(B0, B1, B2, B3); + + B1 = swap_abcd_badc(B1); + B2 = B2.rotl<16>(); + B3 = B3.bswap(); + + aria_mix(B0, B1, B2, B3); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI void aria_fe(SIMD_16x32& B0, + SIMD_16x32& B1, + SIMD_16x32& B2, + SIMD_16x32& B3) { + B0 = aria_fe_m(apply_fe_sbox(B0)); + B1 = aria_fe_m(apply_fe_sbox(B1)); + B2 = aria_fe_m(apply_fe_sbox(B2)); + B3 = aria_fe_m(apply_fe_sbox(B3)); + + aria_mix(B0, B1, B2, B3); + + B3 = swap_abcd_badc(B3); + B0 = B0.rotl<16>(); + B1 = B1.bswap(); + + aria_mix(B0, B1, B2, B3); +} + +/* +* 16-wide ARIA block processing +*/ +BOTAN_FN_ISA_AVX512_GFNI +void transform_16(const uint8_t in[], uint8_t out[], std::span KS) { + const size_t ROUNDS = (KS.size() / 4) - 1; + + BOTAN_ASSERT_NOMSG(ROUNDS == 12 || ROUNDS == 14 || ROUNDS == 16); + + SIMD_16x32 B0 = SIMD_16x32::load_be(in); + SIMD_16x32 B1 = SIMD_16x32::load_be(in + 64); + SIMD_16x32 B2 = SIMD_16x32::load_be(in + 128); + SIMD_16x32 B3 = SIMD_16x32::load_be(in + 192); + + SIMD_16x32::transpose(B0, B1, B2, B3); + + for(size_t r = 0; r != ROUNDS; r += 2) { + B0 ^= SIMD_16x32::splat(KS[4 * r]); + B1 ^= SIMD_16x32::splat(KS[4 * r + 1]); + B2 ^= SIMD_16x32::splat(KS[4 * r + 2]); + B3 ^= SIMD_16x32::splat(KS[4 * r + 3]); + aria_fo(B0, B1, B2, B3); + + B0 ^= SIMD_16x32::splat(KS[4 * r + 4]); + B1 ^= SIMD_16x32::splat(KS[4 * r + 5]); + B2 ^= SIMD_16x32::splat(KS[4 * r + 6]); + B3 ^= SIMD_16x32::splat(KS[4 * r + 7]); + + if(r != ROUNDS - 2) { + aria_fe(B0, B1, B2, B3); + } + } + + B0 = apply_fe_sbox(B0) ^ SIMD_16x32::splat(KS[4 * ROUNDS]); + B1 = apply_fe_sbox(B1) ^ SIMD_16x32::splat(KS[4 * ROUNDS + 1]); + B2 = apply_fe_sbox(B2) ^ SIMD_16x32::splat(KS[4 * ROUNDS + 2]); + B3 = apply_fe_sbox(B3) ^ SIMD_16x32::splat(KS[4 * ROUNDS + 3]); + + SIMD_16x32::transpose(B0, B1, B2, B3); + + B0.store_be(out); + B1.store_be(out + 64); + B2.store_be(out + 128); + B3.store_be(out + 192); +} + +void BOTAN_FN_ISA_AVX512_GFNI aria_transform(const uint8_t in[], + uint8_t out[], + size_t blocks, + std::span KS) { + while(blocks >= 16) { + ARIA_AVX512::transform_16(in, out, KS); + in += 16 * 16; + out += 16 * 16; + blocks -= 16; + } + + if(blocks > 0) { + uint8_t ibuf[16 * 16] = {0}; + uint8_t obuf[16 * 16] = {0}; + copy_mem(ibuf, in, blocks * 16); + ARIA_AVX512::transform_16(ibuf, obuf, KS); + copy_mem(out, obuf, blocks * 16); + } +} + +} // namespace + +} // namespace ARIA_AVX512 + +void BOTAN_FN_ISA_AVX512_GFNI ARIA_128::aria_avx512_gfni_encrypt(const uint8_t in[], + uint8_t out[], + size_t blocks) const { + ARIA_AVX512::aria_transform(in, out, blocks, m_ERK); +} + +void BOTAN_FN_ISA_AVX512_GFNI ARIA_128::aria_avx512_gfni_decrypt(const uint8_t in[], + uint8_t out[], + size_t blocks) const { + ARIA_AVX512::aria_transform(in, out, blocks, m_DRK); +} + +void BOTAN_FN_ISA_AVX512_GFNI ARIA_192::aria_avx512_gfni_encrypt(const uint8_t in[], + uint8_t out[], + size_t blocks) const { + ARIA_AVX512::aria_transform(in, out, blocks, m_ERK); +} + +void BOTAN_FN_ISA_AVX512_GFNI ARIA_192::aria_avx512_gfni_decrypt(const uint8_t in[], + uint8_t out[], + size_t blocks) const { + ARIA_AVX512::aria_transform(in, out, blocks, m_DRK); +} + +void BOTAN_FN_ISA_AVX512_GFNI ARIA_256::aria_avx512_gfni_encrypt(const uint8_t in[], + uint8_t out[], + size_t blocks) const { + ARIA_AVX512::aria_transform(in, out, blocks, m_ERK); +} + +void BOTAN_FN_ISA_AVX512_GFNI ARIA_256::aria_avx512_gfni_decrypt(const uint8_t in[], + uint8_t out[], + size_t blocks) const { + ARIA_AVX512::aria_transform(in, out, blocks, m_DRK); +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/aria/aria_avx512_gfni/info.txt botan3-3.12.0+dfsg/src/lib/block/aria/aria_avx512_gfni/info.txt --- botan3-3.7.1+dfsg/src/lib/block/aria/aria_avx512_gfni/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/aria/aria_avx512_gfni/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,18 @@ + +ARIA_AVX512_GFNI -> 20260303 + + + +name -> "ARIA AVX-512/GFNI" + + + +cpuid +simd_avx2 +simd_avx512 + + + +gfni +avx512 + diff -Nru botan3-3.7.1+dfsg/src/lib/block/aria/aria_hwaes/aria_hwaes.cpp botan3-3.12.0+dfsg/src/lib/block/aria/aria_hwaes/aria_hwaes.cpp --- botan3-3.7.1+dfsg/src/lib/block/aria/aria_hwaes/aria_hwaes.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/aria/aria_hwaes/aria_hwaes.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,248 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include + +namespace Botan { + +namespace ARIA_HWAES { + +namespace { + +// ARIA S1 is just the AES sbox +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES SIMD_4x32 aria_s1(SIMD_4x32 v) { + return hw_aes_sbox(v); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES SIMD_4x32 aria_s2(SIMD_4x32 v) { + constexpr uint64_t AFF_S2 = gfni_matrix(R"( + 0 1 0 1 0 1 1 1 + 0 0 1 1 1 1 1 1 + 1 1 1 0 1 1 0 1 + 1 1 0 0 0 0 1 1 + 0 1 0 0 0 0 1 1 + 1 1 0 0 1 1 1 0 + 0 1 1 0 0 0 1 1 + 1 1 1 1 0 1 1 0)"); + + constexpr auto POST_S2 = Gf2AffineTransformation::post_sbox(AFF_S2, 0xE2); + return POST_S2.affine_transform(hw_aes_sbox(v)); +} + +// ARIA X1 is just the AES inverse sbox +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES SIMD_4x32 aria_x1(SIMD_4x32 v) { + return hw_aes_inv_sbox(v); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES SIMD_4x32 aria_x2(SIMD_4x32 v) { + constexpr uint64_t AFF_X2 = gfni_matrix(R"( + 0 0 0 1 1 0 0 0 + 0 0 1 0 0 1 1 0 + 0 0 0 0 1 0 1 0 + 1 1 1 0 0 0 1 1 + 1 1 1 0 1 1 0 0 + 0 1 1 0 1 0 1 1 + 1 0 1 1 1 1 0 1 + 1 0 0 1 0 0 1 1)"); + constexpr auto PRE_X2D = Gf2AffineTransformation::post_inv_sbox(AFF_X2, 0x2C); + + return hw_aes_inv_sbox(PRE_X2D.affine_transform(v)); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES SIMD_4x32 aria_fo_m(SIMD_4x32 x) { + return x.rotl<8>() ^ x.rotl<16>() ^ x.rotl<24>(); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES SIMD_4x32 aria_fe_m(SIMD_4x32 x) { + return x ^ x.rotl<8>() ^ x.rotl<24>(); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES void aria_mix(SIMD_4x32& B0, SIMD_4x32& B1, SIMD_4x32& B2, SIMD_4x32& B3) { + B1 ^= B2; + B2 ^= B3; + B0 ^= B1; + B3 ^= B1; + B2 ^= B0; + B1 ^= B2; +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES SIMD_4x32 swap_abcd_badc(SIMD_4x32 x) { + const auto shuf = SIMD_4x32(0x02030001, 0x06070405, 0x0A0B0809, 0x0E0F0C0D); + return SIMD_4x32::byte_shuffle(x, shuf); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES SIMD_4x32 byte_transpose(SIMD_4x32 v) { + const SIMD_4x32 tbl(0x0C080400, 0x0D090501, 0x0E0A0602, 0x0F0B0703); + return SIMD_4x32::byte_shuffle(v, tbl); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES void aria_fo_sbox(SIMD_4x32& B0, SIMD_4x32& B1, SIMD_4x32& B2, SIMD_4x32& B3) { + B0 = byte_transpose(B0); + B1 = byte_transpose(B1); + B2 = byte_transpose(B2); + B3 = byte_transpose(B3); + SIMD_4x32::transpose(B0, B1, B2, B3); + + B3 = aria_s1(B3); + B2 = aria_s2(B2); + B1 = aria_x1(B1); + B0 = aria_x2(B0); + + SIMD_4x32::transpose(B0, B1, B2, B3); + B0 = byte_transpose(B0); + B1 = byte_transpose(B1); + B2 = byte_transpose(B2); + B3 = byte_transpose(B3); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES void aria_fe_sbox(SIMD_4x32& B0, SIMD_4x32& B1, SIMD_4x32& B2, SIMD_4x32& B3) { + B0 = byte_transpose(B0); + B1 = byte_transpose(B1); + B2 = byte_transpose(B2); + B3 = byte_transpose(B3); + SIMD_4x32::transpose(B0, B1, B2, B3); + + B3 = aria_x1(B3); + B2 = aria_x2(B2); + B1 = aria_s1(B1); + B0 = aria_s2(B0); + + SIMD_4x32::transpose(B0, B1, B2, B3); + B0 = byte_transpose(B0); + B1 = byte_transpose(B1); + B2 = byte_transpose(B2); + B3 = byte_transpose(B3); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES void aria_fo(SIMD_4x32& B0, SIMD_4x32& B1, SIMD_4x32& B2, SIMD_4x32& B3) { + aria_fo_sbox(B0, B1, B2, B3); + + B0 = aria_fo_m(B0); + B1 = aria_fo_m(B1); + B2 = aria_fo_m(B2); + B3 = aria_fo_m(B3); + + aria_mix(B0, B1, B2, B3); + + B1 = swap_abcd_badc(B1); + B2 = B2.rotl<16>(); + B3 = B3.bswap(); + + aria_mix(B0, B1, B2, B3); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES void aria_fe(SIMD_4x32& B0, SIMD_4x32& B1, SIMD_4x32& B2, SIMD_4x32& B3) { + aria_fe_sbox(B0, B1, B2, B3); + + B0 = aria_fe_m(B0); + B1 = aria_fe_m(B1); + B2 = aria_fe_m(B2); + B3 = aria_fe_m(B3); + + aria_mix(B0, B1, B2, B3); + + B3 = swap_abcd_badc(B3); + B0 = B0.rotl<16>(); + B1 = B1.bswap(); + + aria_mix(B0, B1, B2, B3); +} + +BOTAN_FN_ISA_HWAES void transform_4(const uint8_t in[], uint8_t out[], std::span KS) { + const size_t ROUNDS = (KS.size() / 4) - 1; + + auto B0 = SIMD_4x32::load_be(in); + auto B1 = SIMD_4x32::load_be(in + 16); + auto B2 = SIMD_4x32::load_be(in + 32); + auto B3 = SIMD_4x32::load_be(in + 48); + + SIMD_4x32::transpose(B0, B1, B2, B3); + + for(size_t r = 0; r != ROUNDS; r += 2) { + B0 ^= SIMD_4x32::splat(KS[4 * r]); + B1 ^= SIMD_4x32::splat(KS[4 * r + 1]); + B2 ^= SIMD_4x32::splat(KS[4 * r + 2]); + B3 ^= SIMD_4x32::splat(KS[4 * r + 3]); + + aria_fo(B0, B1, B2, B3); + + B0 ^= SIMD_4x32::splat(KS[4 * r + 4]); + B1 ^= SIMD_4x32::splat(KS[4 * r + 5]); + B2 ^= SIMD_4x32::splat(KS[4 * r + 6]); + B3 ^= SIMD_4x32::splat(KS[4 * r + 7]); + + if(r != ROUNDS - 2) { + aria_fe(B0, B1, B2, B3); + } + } + + // Last half-round: FE sbox only + aria_fe_sbox(B0, B1, B2, B3); + + B0 ^= SIMD_4x32::splat(KS[4 * ROUNDS]); + B1 ^= SIMD_4x32::splat(KS[4 * ROUNDS + 1]); + B2 ^= SIMD_4x32::splat(KS[4 * ROUNDS + 2]); + B3 ^= SIMD_4x32::splat(KS[4 * ROUNDS + 3]); + + SIMD_4x32::transpose(B0, B1, B2, B3); + + B0.store_be(out); + B1.store_be(out + 16); + B2.store_be(out + 32); + B3.store_be(out + 48); +} + +void BOTAN_FN_ISA_HWAES aria_transform(const uint8_t in[], uint8_t out[], size_t blocks, std::span KS) { + while(blocks >= 4) { + transform_4(in, out, KS); + in += 4 * 16; + out += 4 * 16; + blocks -= 4; + } + + if(blocks > 0) { + uint8_t ibuf[4 * 16] = {0}; + uint8_t obuf[4 * 16] = {0}; + copy_mem(ibuf, in, blocks * 16); + transform_4(ibuf, obuf, KS); + copy_mem(out, obuf, blocks * 16); + } +} + +} // namespace + +} // namespace ARIA_HWAES + +void BOTAN_FN_ISA_HWAES ARIA_128::aria_hwaes_encrypt(const uint8_t in[], uint8_t out[], size_t blocks) const { + ARIA_HWAES::aria_transform(in, out, blocks, m_ERK); +} + +void BOTAN_FN_ISA_HWAES ARIA_128::aria_hwaes_decrypt(const uint8_t in[], uint8_t out[], size_t blocks) const { + ARIA_HWAES::aria_transform(in, out, blocks, m_DRK); +} + +void BOTAN_FN_ISA_HWAES ARIA_192::aria_hwaes_encrypt(const uint8_t in[], uint8_t out[], size_t blocks) const { + ARIA_HWAES::aria_transform(in, out, blocks, m_ERK); +} + +void BOTAN_FN_ISA_HWAES ARIA_192::aria_hwaes_decrypt(const uint8_t in[], uint8_t out[], size_t blocks) const { + ARIA_HWAES::aria_transform(in, out, blocks, m_DRK); +} + +void BOTAN_FN_ISA_HWAES ARIA_256::aria_hwaes_encrypt(const uint8_t in[], uint8_t out[], size_t blocks) const { + ARIA_HWAES::aria_transform(in, out, blocks, m_ERK); +} + +void BOTAN_FN_ISA_HWAES ARIA_256::aria_hwaes_decrypt(const uint8_t in[], uint8_t out[], size_t blocks) const { + ARIA_HWAES::aria_transform(in, out, blocks, m_DRK); +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/aria/aria_hwaes/info.txt botan3-3.12.0+dfsg/src/lib/block/aria/aria_hwaes/info.txt --- botan3-3.7.1+dfsg/src/lib/block/aria/aria_hwaes/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/aria/aria_hwaes/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ + +ARIA_HWAES -> 20260323 + + + +name -> "ARIA using hardware AES instructions" + + + +cpuid +simd_hwaes + diff -Nru botan3-3.7.1+dfsg/src/lib/block/block_cipher.cpp botan3-3.12.0+dfsg/src/lib/block/block_cipher.cpp --- botan3-3.7.1+dfsg/src/lib/block/block_cipher.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/block_cipher.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,8 +7,11 @@ #include +#include #include #include +#include +#include #if defined(BOTAN_HAS_AES) #include @@ -51,6 +54,8 @@ #endif #if defined(BOTAN_HAS_LION) + #include + #include #include #endif diff -Nru botan3-3.7.1+dfsg/src/lib/block/block_cipher.h botan3-3.12.0+dfsg/src/lib/block/block_cipher.h --- botan3-3.7.1+dfsg/src/lib/block/block_cipher.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/block_cipher.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,7 +8,6 @@ #ifndef BOTAN_BLOCK_CIPHER_H_ #define BOTAN_BLOCK_CIPHER_H_ -#include #include #include #include @@ -45,6 +44,14 @@ static std::vector providers(std::string_view algo_spec); /** + * Multiplier on a block cipher's native parallelism + * + * Usually notable performance gains come from further loop blocking, + * at least for 2 or 4x + */ + static constexpr size_t ParallelismMult = 4; + + /** * @return block size of this algorithm */ virtual size_t block_size() const = 0; @@ -55,9 +62,9 @@ virtual size_t parallelism() const { return 1; } /** - * @return prefererred parallelism of this cipher in bytes + * @return preferred parallelism of this cipher in bytes */ - size_t parallel_bytes() const { return parallelism() * block_size() * BOTAN_BLOCK_CIPHER_PAR_MULT; } + size_t parallel_bytes() const { return parallelism() * block_size() * BlockCipher::ParallelismMult; } /** * @return provider information about this implementation. Default is "base", @@ -76,7 +83,7 @@ /** * Decrypt a block. - * @param in The ciphertext block to be decypted as a byte array. + * @param in The ciphertext block to be decrypted as a byte array. * Must be of length block_size(). * @param out The byte array designated to hold the decrypted block. * Must be of length block_size(). @@ -149,18 +156,28 @@ */ virtual void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const = 0; - virtual void encrypt_n_xex(uint8_t data[], const uint8_t mask[], size_t blocks) const { + BOTAN_DEPRECATED("Deprecated no replacement") + void encrypt_n_xex(uint8_t data[], const uint8_t mask[], size_t blocks) const { const size_t BS = block_size(); - xor_buf(data, mask, blocks * BS); + for(size_t i = 0; i != blocks * BS; ++i) { + data[i] ^= mask[i]; + } encrypt_n(data, data, blocks); - xor_buf(data, mask, blocks * BS); + for(size_t i = 0; i != blocks * BS; ++i) { + data[i] ^= mask[i]; + } } - virtual void decrypt_n_xex(uint8_t data[], const uint8_t mask[], size_t blocks) const { + BOTAN_DEPRECATED("Deprecated no replacement") + void decrypt_n_xex(uint8_t data[], const uint8_t mask[], size_t blocks) const { const size_t BS = block_size(); - xor_buf(data, mask, blocks * BS); + for(size_t i = 0; i != blocks * BS; ++i) { + data[i] ^= mask[i]; + } decrypt_n(data, data, blocks); - xor_buf(data, mask, blocks * BS); + for(size_t i = 0; i != blocks * BS; ++i) { + data[i] ^= mask[i]; + } } /** @@ -169,8 +186,6 @@ virtual std::unique_ptr new_object() const = 0; BlockCipher* clone() const { return this->new_object().release(); } - - ~BlockCipher() override = default; }; /** @@ -194,7 +209,7 @@ template class Block_Cipher_Fixed_Params : public BaseClass { public: - enum { BLOCK_SIZE = BS }; + enum { BLOCK_SIZE = BS }; /* NOLINT(*-enum-size,*-use-enum-class) */ size_t block_size() const final { return BS; } diff -Nru botan3-3.7.1+dfsg/src/lib/block/blowfish/blowfish.cpp botan3-3.12.0+dfsg/src/lib/block/blowfish/blowfish.cpp --- botan3-3.7.1+dfsg/src/lib/block/blowfish/blowfish.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/blowfish/blowfish.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -157,8 +157,14 @@ assert_key_material_set(); while(blocks >= 4) { - uint32_t L0, R0, L1, R1, L2, R2, L3, R3; - load_be(in, L0, R0, L1, R1, L2, R2, L3, R3); + uint32_t L0 = load_be(in, 0); + uint32_t R0 = load_be(in, 1); + uint32_t L1 = load_be(in, 2); + uint32_t R1 = load_be(in, 3); + uint32_t L2 = load_be(in, 4); + uint32_t R2 = load_be(in, 5); + uint32_t L3 = load_be(in, 6); + uint32_t R3 = load_be(in, 7); for(size_t r = 0; r != 16; r += 2) { L0 ^= m_P[r]; @@ -196,9 +202,9 @@ blocks -= 4; } - while(blocks) { - uint32_t L, R; - load_be(in, L, R); + while(blocks > 0) { + uint32_t L = load_be(in, 0); + uint32_t R = load_be(in, 1); for(size_t r = 0; r != 16; r += 2) { L ^= m_P[r]; @@ -226,8 +232,14 @@ assert_key_material_set(); while(blocks >= 4) { - uint32_t L0, R0, L1, R1, L2, R2, L3, R3; - load_be(in, L0, R0, L1, R1, L2, R2, L3, R3); + uint32_t L0 = load_be(in, 0); + uint32_t R0 = load_be(in, 1); + uint32_t L1 = load_be(in, 2); + uint32_t R1 = load_be(in, 3); + uint32_t L2 = load_be(in, 4); + uint32_t R2 = load_be(in, 5); + uint32_t L3 = load_be(in, 6); + uint32_t R3 = load_be(in, 7); for(size_t r = 17; r != 1; r -= 2) { L0 ^= m_P[r]; @@ -266,9 +278,9 @@ blocks -= 4; } - while(blocks) { - uint32_t L, R; - load_be(in, L, R); + while(blocks > 0) { + uint32_t L = load_be(in, 0); + uint32_t R = load_be(in, 1); for(size_t r = 17; r != 1; r -= 2) { L ^= m_P[r]; @@ -307,6 +319,7 @@ } void Blowfish::key_expansion(const uint8_t key[], size_t length, const uint8_t salt[], size_t salt_length) { + BOTAN_ASSERT_NOMSG(length > 0); BOTAN_ASSERT_NOMSG(salt_length % 4 == 0); for(size_t i = 0, j = 0; i != 18; ++i, j += 4) { @@ -315,7 +328,8 @@ const size_t P_salt_offset = (salt_length > 0) ? 18 % (salt_length / 4) : 0; - uint32_t L = 0, R = 0; + uint32_t L = 0; + uint32_t R = 0; generate_sbox(m_P, L, R, salt, salt_length, 0); generate_sbox(m_S, L, R, salt, salt_length, P_salt_offset); } @@ -327,10 +341,8 @@ const uint8_t key[], size_t length, const uint8_t salt[], size_t salt_length, size_t workfactor, bool salt_first) { BOTAN_ARG_CHECK(salt_length > 0 && salt_length % 4 == 0, "Invalid salt length for Blowfish salted key schedule"); - if(length > 72) { - // Truncate longer passwords to the 72 char bcrypt limit - length = 72; - } + // Truncate longer passwords to the 72 char bcrypt limit + length = std::min(length, 72); m_P.resize(18); copy_mem(m_P.data(), P_INIT, 18); @@ -377,7 +389,8 @@ L ^= BFF(R, m_S); } - uint32_t T = R; + // Must read-then-write since sometimes sbox parameter is m_P + const uint32_t T = R; R = L ^ m_P[16]; L = T ^ m_P[17]; box[i] = L; diff -Nru botan3-3.7.1+dfsg/src/lib/block/blowfish/blowfish.h botan3-3.12.0+dfsg/src/lib/block/blowfish/blowfish.h --- botan3-3.7.1+dfsg/src/lib/block/blowfish/blowfish.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/blowfish/blowfish.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #define BOTAN_BLOWFISH_H_ #include +#include namespace Botan { diff -Nru botan3-3.7.1+dfsg/src/lib/block/camellia/camellia.cpp botan3-3.12.0+dfsg/src/lib/block/camellia/camellia.cpp --- botan3-3.7.1+dfsg/src/lib/block/camellia/camellia.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/camellia/camellia.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,6 +11,10 @@ #include #include +#if defined(BOTAN_HAS_CPUID) + #include +#endif + namespace Botan { namespace { @@ -137,12 +141,12 @@ /* * Camellia Encryption */ -void encrypt(const uint8_t in[], uint8_t out[], size_t blocks, const secure_vector& SK, const size_t rounds) { +void encrypt(const uint8_t in[], uint8_t out[], size_t blocks, const secure_vector& SK, size_t rounds) { prefetch_arrays(SBOX1, SBOX2, SBOX3, SBOX4); for(size_t i = 0; i < blocks; ++i) { - uint64_t D1, D2; - load_be(in + 16 * i, D1, D2); + uint64_t D1 = load_be(in, 2 * i + 0); + uint64_t D2 = load_be(in, 2 * i + 1); const uint64_t* K = SK.data(); @@ -175,12 +179,12 @@ /* * Camellia Decryption */ -void decrypt(const uint8_t in[], uint8_t out[], size_t blocks, const secure_vector& SK, const size_t rounds) { +void decrypt(const uint8_t in[], uint8_t out[], size_t blocks, const secure_vector& SK, size_t rounds) { prefetch_arrays(SBOX1, SBOX2, SBOX3, SBOX4); for(size_t i = 0; i < blocks; ++i) { - uint64_t D1, D2; - load_be(in + 16 * i, D1, D2); + uint64_t D1 = load_be(in, 2 * i + 0); + uint64_t D2 = load_be(in, 2 * i + 1); const uint64_t* K = &SK[SK.size() - 1]; @@ -345,37 +349,195 @@ } } +std::string provider() { +#if defined(BOTAN_HAS_CAMELLIA_AVX512_GFNI) + if(auto feat = CPUID::check(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return *feat; + } +#endif + +#if defined(BOTAN_HAS_CAMELLIA_AVX2_GFNI) + if(auto feat = CPUID::check(CPUID::Feature::GFNI)) { + return *feat; + } +#endif + +#if defined(BOTAN_HAS_CAMELLIA_HWAES) + if(auto feat = CPUID::check(CPUID::Feature::HW_AES)) { + return *feat; + } +#endif + + return "base"; +} + +size_t parallelism() { +#if defined(BOTAN_HAS_CAMELLIA_AVX512_GFNI) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return 16; + } +#endif + +#if defined(BOTAN_HAS_CAMELLIA_AVX2_GFNI) + if(CPUID::has(CPUID::Feature::GFNI)) { + return 4; + } +#endif + +#if defined(BOTAN_HAS_CAMELLIA_HWAES) + if(CPUID::has(CPUID::Feature::HW_AES)) { + return 2; + } +#endif + + return 1; +} + } // namespace Camellia_F } // namespace void Camellia_128::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); + +#if defined(BOTAN_HAS_CAMELLIA_AVX512_GFNI) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return avx512_gfni_encrypt(in, out, blocks, m_SK); + } +#endif + +#if defined(BOTAN_HAS_CAMELLIA_AVX2_GFNI) + if(CPUID::has(CPUID::Feature::GFNI)) { + return avx2_gfni_encrypt(in, out, blocks, m_SK); + } +#endif + +#if defined(BOTAN_HAS_CAMELLIA_HWAES) + if(CPUID::has(CPUID::Feature::HW_AES)) { + return hwaes_encrypt(in, out, blocks, m_SK); + } +#endif + Camellia_F::encrypt(in, out, blocks, m_SK, 9); } void Camellia_192::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); + +#if defined(BOTAN_HAS_CAMELLIA_AVX512_GFNI) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return avx512_gfni_encrypt(in, out, blocks, m_SK); + } +#endif + +#if defined(BOTAN_HAS_CAMELLIA_AVX2_GFNI) + if(CPUID::has(CPUID::Feature::GFNI)) { + return avx2_gfni_encrypt(in, out, blocks, m_SK); + } +#endif + +#if defined(BOTAN_HAS_CAMELLIA_HWAES) + if(CPUID::has(CPUID::Feature::HW_AES)) { + return hwaes_encrypt(in, out, blocks, m_SK); + } +#endif + Camellia_F::encrypt(in, out, blocks, m_SK, 12); } void Camellia_256::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); + +#if defined(BOTAN_HAS_CAMELLIA_AVX512_GFNI) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return avx512_gfni_encrypt(in, out, blocks, m_SK); + } +#endif + +#if defined(BOTAN_HAS_CAMELLIA_AVX2_GFNI) + if(CPUID::has(CPUID::Feature::GFNI)) { + return avx2_gfni_encrypt(in, out, blocks, m_SK); + } +#endif + +#if defined(BOTAN_HAS_CAMELLIA_HWAES) + if(CPUID::has(CPUID::Feature::HW_AES)) { + return hwaes_encrypt(in, out, blocks, m_SK); + } +#endif + Camellia_F::encrypt(in, out, blocks, m_SK, 12); } void Camellia_128::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); + +#if defined(BOTAN_HAS_CAMELLIA_AVX512_GFNI) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return avx512_gfni_decrypt(in, out, blocks, m_SK); + } +#endif + +#if defined(BOTAN_HAS_CAMELLIA_AVX2_GFNI) + if(CPUID::has(CPUID::Feature::GFNI)) { + return avx2_gfni_decrypt(in, out, blocks, m_SK); + } +#endif + +#if defined(BOTAN_HAS_CAMELLIA_HWAES) + if(CPUID::has(CPUID::Feature::HW_AES)) { + return hwaes_decrypt(in, out, blocks, m_SK); + } +#endif + Camellia_F::decrypt(in, out, blocks, m_SK, 9); } void Camellia_192::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); + +#if defined(BOTAN_HAS_CAMELLIA_AVX512_GFNI) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return avx512_gfni_decrypt(in, out, blocks, m_SK); + } +#endif + +#if defined(BOTAN_HAS_CAMELLIA_AVX2_GFNI) + if(CPUID::has(CPUID::Feature::GFNI)) { + return avx2_gfni_decrypt(in, out, blocks, m_SK); + } +#endif + +#if defined(BOTAN_HAS_CAMELLIA_HWAES) + if(CPUID::has(CPUID::Feature::HW_AES)) { + return hwaes_decrypt(in, out, blocks, m_SK); + } +#endif + Camellia_F::decrypt(in, out, blocks, m_SK, 12); } void Camellia_256::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); + +#if defined(BOTAN_HAS_CAMELLIA_AVX512_GFNI) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return avx512_gfni_decrypt(in, out, blocks, m_SK); + } +#endif + +#if defined(BOTAN_HAS_CAMELLIA_AVX2_GFNI) + if(CPUID::has(CPUID::Feature::GFNI)) { + return avx2_gfni_decrypt(in, out, blocks, m_SK); + } +#endif + +#if defined(BOTAN_HAS_CAMELLIA_HWAES) + if(CPUID::has(CPUID::Feature::HW_AES)) { + return hwaes_decrypt(in, out, blocks, m_SK); + } +#endif + Camellia_F::decrypt(in, out, blocks, m_SK, 12); } @@ -415,4 +577,28 @@ zap(m_SK); } +std::string Camellia_128::provider() const { + return Camellia_F::provider(); +} + +std::string Camellia_192::provider() const { + return Camellia_F::provider(); +} + +std::string Camellia_256::provider() const { + return Camellia_F::provider(); +} + +size_t Camellia_128::parallelism() const { + return Camellia_F::parallelism(); +} + +size_t Camellia_192::parallelism() const { + return Camellia_F::parallelism(); +} + +size_t Camellia_256::parallelism() const { + return Camellia_F::parallelism(); +} + } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/camellia/camellia.h botan3-3.12.0+dfsg/src/lib/block/camellia/camellia.h --- botan3-3.7.1+dfsg/src/lib/block/camellia/camellia.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/camellia/camellia.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #define BOTAN_CAMELLIA_H_ #include +#include namespace Botan { @@ -24,6 +25,9 @@ std::string name() const override { return "Camellia-128"; } + std::string provider() const override; + size_t parallelism() const override; + std::unique_ptr new_object() const override { return std::make_unique(); } bool has_keying_material() const override; @@ -31,6 +35,21 @@ private: void key_schedule(std::span key) override; +#if defined(BOTAN_HAS_CAMELLIA_AVX2_GFNI) + static void avx2_gfni_encrypt(const uint8_t in[], uint8_t out[], size_t blocks, std::span SK); + static void avx2_gfni_decrypt(const uint8_t in[], uint8_t out[], size_t blocks, std::span SK); +#endif + +#if defined(BOTAN_HAS_CAMELLIA_AVX512_GFNI) + static void avx512_gfni_encrypt(const uint8_t in[], uint8_t out[], size_t blocks, std::span SK); + static void avx512_gfni_decrypt(const uint8_t in[], uint8_t out[], size_t blocks, std::span SK); +#endif + +#if defined(BOTAN_HAS_CAMELLIA_HWAES) + static void hwaes_encrypt(const uint8_t in[], uint8_t out[], size_t blocks, std::span SK); + static void hwaes_decrypt(const uint8_t in[], uint8_t out[], size_t blocks, std::span SK); +#endif + secure_vector m_SK; }; @@ -46,6 +65,9 @@ std::string name() const override { return "Camellia-192"; } + std::string provider() const override; + size_t parallelism() const override; + std::unique_ptr new_object() const override { return std::make_unique(); } bool has_keying_material() const override; @@ -53,6 +75,21 @@ private: void key_schedule(std::span key) override; +#if defined(BOTAN_HAS_CAMELLIA_AVX2_GFNI) + static void avx2_gfni_encrypt(const uint8_t in[], uint8_t out[], size_t blocks, std::span SK); + static void avx2_gfni_decrypt(const uint8_t in[], uint8_t out[], size_t blocks, std::span SK); +#endif + +#if defined(BOTAN_HAS_CAMELLIA_AVX512_GFNI) + static void avx512_gfni_encrypt(const uint8_t in[], uint8_t out[], size_t blocks, std::span SK); + static void avx512_gfni_decrypt(const uint8_t in[], uint8_t out[], size_t blocks, std::span SK); +#endif + +#if defined(BOTAN_HAS_CAMELLIA_HWAES) + static void hwaes_encrypt(const uint8_t in[], uint8_t out[], size_t blocks, std::span SK); + static void hwaes_decrypt(const uint8_t in[], uint8_t out[], size_t blocks, std::span SK); +#endif + secure_vector m_SK; }; @@ -68,6 +105,9 @@ std::string name() const override { return "Camellia-256"; } + std::string provider() const override; + size_t parallelism() const override; + std::unique_ptr new_object() const override { return std::make_unique(); } bool has_keying_material() const override; @@ -75,6 +115,21 @@ private: void key_schedule(std::span key) override; +#if defined(BOTAN_HAS_CAMELLIA_AVX2_GFNI) + static void avx2_gfni_encrypt(const uint8_t in[], uint8_t out[], size_t blocks, std::span SK); + static void avx2_gfni_decrypt(const uint8_t in[], uint8_t out[], size_t blocks, std::span SK); +#endif + +#if defined(BOTAN_HAS_CAMELLIA_AVX512_GFNI) + static void avx512_gfni_encrypt(const uint8_t in[], uint8_t out[], size_t blocks, std::span SK); + static void avx512_gfni_decrypt(const uint8_t in[], uint8_t out[], size_t blocks, std::span SK); +#endif + +#if defined(BOTAN_HAS_CAMELLIA_HWAES) + static void hwaes_encrypt(const uint8_t in[], uint8_t out[], size_t blocks, std::span SK); + static void hwaes_decrypt(const uint8_t in[], uint8_t out[], size_t blocks, std::span SK); +#endif + secure_vector m_SK; }; diff -Nru botan3-3.7.1+dfsg/src/lib/block/camellia/camellia_avx2_gfni/camellia_avx2_gfni.cpp botan3-3.12.0+dfsg/src/lib/block/camellia/camellia_avx2_gfni/camellia_avx2_gfni.cpp --- botan3-3.7.1+dfsg/src/lib/block/camellia/camellia_avx2_gfni/camellia_avx2_gfni.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/camellia/camellia_avx2_gfni/camellia_avx2_gfni.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,444 @@ +/* +* (C) 2025,2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include + +namespace Botan { + +namespace Camellia_AVX2_GFNI { + +/* +* This follows exactly the approach used in the AVX-512+GFNI implementation +* with only minor complications due to missing rotate and masked operations. +*/ + +namespace { + +constexpr uint64_t pre123_a = gfni_matrix(R"( + 1 1 1 0 1 1 0 1 + 0 0 1 1 0 0 1 0 + 1 1 0 1 0 0 0 0 + 1 0 1 1 0 0 1 1 + 0 0 0 0 1 1 0 0 + 1 0 1 0 0 1 0 0 + 0 0 1 0 1 1 0 0 + 1 0 0 0 0 1 1 0)"); + +constexpr uint64_t pre4_a = gfni_matrix(R"( + 1 1 0 1 1 0 1 1 + 0 1 1 0 0 1 0 0 + 1 0 1 0 0 0 0 1 + 0 1 1 0 0 1 1 1 + 0 0 0 1 1 0 0 0 + 0 1 0 0 1 0 0 1 + 0 1 0 1 1 0 0 0 + 0 0 0 0 1 1 0 1)"); + +constexpr uint8_t pre_c = 0b01000101; + +constexpr uint64_t post2_a = gfni_matrix(R"( + 0 0 0 1 1 1 0 0 + 0 0 0 0 0 0 0 1 + 0 1 1 0 0 1 1 0 + 1 0 1 1 1 1 1 0 + 0 0 0 1 1 0 1 1 + 1 0 0 0 1 1 1 0 + 0 1 0 1 1 1 1 0 + 0 1 1 1 1 1 1 1)"); + +constexpr uint64_t post3_a = gfni_matrix(R"( + 0 1 1 0 0 1 1 0 + 1 0 1 1 1 1 1 0 + 0 0 0 1 1 0 1 1 + 1 0 0 0 1 1 1 0 + 0 1 0 1 1 1 1 0 + 0 1 1 1 1 1 1 1 + 0 0 0 1 1 1 0 0 + 0 0 0 0 0 0 0 1)"); + +constexpr uint64_t post14_a = gfni_matrix(R"( + 0 0 0 0 0 0 0 1 + 0 1 1 0 0 1 1 0 + 1 0 1 1 1 1 1 0 + 0 0 0 1 1 0 1 1 + 1 0 0 0 1 1 1 0 + 0 1 0 1 1 1 1 0 + 0 1 1 1 1 1 1 1 + 0 0 0 1 1 1 0 0)"); + +// NOLINTBEGIN(portability-simd-intrinsics) + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_GFNI SIMD_4x64 camellia_f(SIMD_4x64 x) { + const __m256i xr = x.raw(); + + // Compute S1(x), S2(x), S3(x), S4(x) for all bytes + const auto y123 = _mm256_gf2p8affine_epi64_epi8(xr, _mm256_set1_epi64x(pre123_a), pre_c); + const auto y4 = _mm256_gf2p8affine_epi64_epi8(xr, _mm256_set1_epi64x(pre4_a), pre_c); + + const auto s1 = _mm256_gf2p8affineinv_epi64_epi8(y123, _mm256_set1_epi64x(post14_a), 0x6E); + const auto s2 = _mm256_gf2p8affineinv_epi64_epi8(y123, _mm256_set1_epi64x(post2_a), 0xDC); + const auto s3 = _mm256_gf2p8affineinv_epi64_epi8(y123, _mm256_set1_epi64x(post3_a), 0x37); + const auto s4 = _mm256_gf2p8affineinv_epi64_epi8(y4, _mm256_set1_epi64x(post14_a), 0x6E); + + // Blend to find correct S(x) for each byte position + + const auto mask_s2 = _mm256_set1_epi64x(0x00FF0000FF000000); + const auto mask_s3 = _mm256_set1_epi64x(0x0000FF0000FF0000); + const auto mask_s4 = _mm256_set1_epi64x(0x000000FF0000FF00); + + auto sx = s1; + sx = _mm256_blendv_epi8(sx, s2, mask_s2); + sx = _mm256_blendv_epi8(sx, s3, mask_s3); + sx = _mm256_blendv_epi8(sx, s4, mask_s4); + + // Linear mixing layer + const auto P1 = _mm256_set_epi64x(0x0808080908080809, 0x0000000100000001, 0x0808080908080809, 0x0000000100000001); + const auto P2 = _mm256_set_epi64x(0x09090A0A09090A0A, 0x0101020201010202, 0x09090A0A09090A0A, 0x0101020201010202); + const auto P3 = _mm256_set_epi64x(0x0A0B0B0B0A0B0B0B, 0x0203030302030303, 0x0A0B0B0B0A0B0B0B, 0x0203030302030303); + const auto P4 = _mm256_set_epi64x(0x0C0C0D0C0E0D0C0C, 0x0404050406050404, 0x0C0C0D0C0E0D0C0C, 0x0404050406050404); + const auto P5 = _mm256_set_epi64x(0x0D0E0E0D0F0E0D0F, 0x0506060507060507, 0x0D0E0E0D0F0E0D0F, 0x0506060507060507); + const auto P6 = _mm256_set_epi64x(0x0F0F0F0EFFFFFFFF, 0x07070706FFFFFFFF, 0x0F0F0F0EFFFFFFFF, 0x07070706FFFFFFFF); + + const auto t1 = SIMD_4x64(_mm256_shuffle_epi8(sx, P1)); + const auto t2 = SIMD_4x64(_mm256_shuffle_epi8(sx, P2)); + const auto t3 = SIMD_4x64(_mm256_shuffle_epi8(sx, P3)); + const auto t4 = SIMD_4x64(_mm256_shuffle_epi8(sx, P4)); + const auto t5 = SIMD_4x64(_mm256_shuffle_epi8(sx, P5)); + const auto t6 = SIMD_4x64(_mm256_shuffle_epi8(sx, P6)); + + return (t1 ^ t2 ^ t3 ^ t4 ^ t5 ^ t6); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2 void load_and_deinterleave(const uint8_t in[], SIMD_4x64& L, SIMD_4x64& R) { + auto A = SIMD_4x64::load_be(in); + auto B = SIMD_4x64::load_be(in + 32); + + auto Ap = _mm256_permute4x64_epi64(A.raw(), 0b11'01'10'00); // [L[0], L[1], R[0], R[1]] + auto Bp = _mm256_permute4x64_epi64(B.raw(), 0b11'01'10'00); // [L[2], L[3], R[2], R[3]] + + L = SIMD_4x64(_mm256_permute2x128_si256(Ap, Bp, 0x20)); // [L[0], L[1], L[2], L[3]] + R = SIMD_4x64(_mm256_permute2x128_si256(Ap, Bp, 0x31)); // [R[0], R[1], R[2], R[3]] +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2 void interleave_and_store(uint8_t out[], SIMD_4x64 L, SIMD_4x64 R) { + auto T1 = _mm256_permute2x128_si256(R.raw(), L.raw(), 0x20); // [R[0], R[1], L[0], L[1]] + auto T2 = _mm256_permute2x128_si256(R.raw(), L.raw(), 0x31); // [R[2], R[3], L[2], L[3]] + + auto A = SIMD_4x64(_mm256_permute4x64_epi64(T1, 0b11'01'10'00)); // [R[0], L[0], R[1], L[1]] + auto B = SIMD_4x64(_mm256_permute4x64_epi64(T2, 0b11'01'10'00)); // [R[2], L[2], R[3], L[3]] + + A.store_be(out); + B.store_be(out + 32); +} + +/* +* 32-bit rotate on SIMD_4x64 helper for FL/FLINV +*/ +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2 SIMD_4x64 rotl32_1(SIMD_4x64 t) { + return SIMD_4x64(_mm256_or_si256(_mm256_slli_epi32(t.raw(), 1), _mm256_srli_epi32(t.raw(), 31))); +} + +// NOLINTEND(portability-simd-intrinsics) + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2 SIMD_4x64 FL_4(SIMD_4x64 v, uint64_t K) { + const uint32_t k1 = static_cast(K >> 32); + const uint32_t k2 = static_cast(K & 0xFFFFFFFF); + + auto x1 = v.shr<32>(); + auto x2 = v & SIMD_4x64::splat(0xFFFFFFFF); + + x2 ^= rotl32_1(x1 & SIMD_4x64::splat(k1)); + x1 ^= (x2 | SIMD_4x64::splat(k2)); + + return x1.shl<32>() | x2; +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2 SIMD_4x64 FLINV_4(SIMD_4x64 v, uint64_t K) { + const uint32_t k1 = static_cast(K >> 32); + const uint32_t k2 = static_cast(K & 0xFFFFFFFF); + + auto x1 = v.shr<32>(); + auto x2 = v & SIMD_4x64::splat(0xFFFFFFFF); + + x1 ^= (x2 | SIMD_4x64::splat(k2)); + x2 ^= rotl32_1(x1 & SIMD_4x64::splat(k1)); + + return x1.shl<32>() | x2; +} + +// Helpers for 6 round iterations + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_GFNI void six_e_rounds(SIMD_4x64& L, SIMD_4x64& R, std::span SK) { + R ^= camellia_f(L ^ SIMD_4x64::splat(SK[0])); + L ^= camellia_f(R ^ SIMD_4x64::splat(SK[1])); + R ^= camellia_f(L ^ SIMD_4x64::splat(SK[2])); + L ^= camellia_f(R ^ SIMD_4x64::splat(SK[3])); + R ^= camellia_f(L ^ SIMD_4x64::splat(SK[4])); + L ^= camellia_f(R ^ SIMD_4x64::splat(SK[5])); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_GFNI void six_d_rounds(SIMD_4x64& L, SIMD_4x64& R, std::span SK) { + R ^= camellia_f(L ^ SIMD_4x64::splat(SK[5])); + L ^= camellia_f(R ^ SIMD_4x64::splat(SK[4])); + R ^= camellia_f(L ^ SIMD_4x64::splat(SK[3])); + L ^= camellia_f(R ^ SIMD_4x64::splat(SK[2])); + R ^= camellia_f(L ^ SIMD_4x64::splat(SK[1])); + L ^= camellia_f(R ^ SIMD_4x64::splat(SK[0])); +} + +BOTAN_FN_ISA_AVX2_GFNI +void camellia_encrypt_x4_18r(const uint8_t in[], uint8_t out[], std::span SK) { + SIMD_4x64 L; + SIMD_4x64 R; + load_and_deinterleave(in, L, R); + + L ^= SIMD_4x64::splat(SK[0]); + R ^= SIMD_4x64::splat(SK[1]); + + six_e_rounds(L, R, SK.subspan(2)); + + L = FL_4(L, SK[8]); + R = FLINV_4(R, SK[9]); + + six_e_rounds(L, R, SK.subspan(10)); + + L = FL_4(L, SK[16]); + R = FLINV_4(R, SK[17]); + + six_e_rounds(L, R, SK.subspan(18)); + + R ^= SIMD_4x64::splat(SK[24]); + L ^= SIMD_4x64::splat(SK[25]); + + interleave_and_store(out, L, R); +} + +BOTAN_FN_ISA_AVX2_GFNI +void camellia_decrypt_x4_18r(const uint8_t in[], uint8_t out[], std::span SK) { + SIMD_4x64 L; + SIMD_4x64 R; + load_and_deinterleave(in, L, R); + + R ^= SIMD_4x64::splat(SK[25]); + L ^= SIMD_4x64::splat(SK[24]); + + six_d_rounds(L, R, SK.subspan(18)); + + L = FL_4(L, SK[17]); + R = FLINV_4(R, SK[16]); + + six_d_rounds(L, R, SK.subspan(10)); + + L = FL_4(L, SK[9]); + R = FLINV_4(R, SK[8]); + + six_d_rounds(L, R, SK.subspan(2)); + + L ^= SIMD_4x64::splat(SK[1]); + R ^= SIMD_4x64::splat(SK[0]); + + interleave_and_store(out, L, R); +} + +BOTAN_FN_ISA_AVX2_GFNI +void camellia_encrypt_x4_24r(const uint8_t in[], uint8_t out[], std::span SK) { + SIMD_4x64 L; + SIMD_4x64 R; + load_and_deinterleave(in, L, R); + + L ^= SIMD_4x64::splat(SK[0]); + R ^= SIMD_4x64::splat(SK[1]); + + six_e_rounds(L, R, SK.subspan(2)); + + L = FL_4(L, SK[8]); + R = FLINV_4(R, SK[9]); + + six_e_rounds(L, R, SK.subspan(10)); + + L = FL_4(L, SK[16]); + R = FLINV_4(R, SK[17]); + + six_e_rounds(L, R, SK.subspan(18)); + + L = FL_4(L, SK[24]); + R = FLINV_4(R, SK[25]); + + six_e_rounds(L, R, SK.subspan(26)); + + R ^= SIMD_4x64::splat(SK[32]); + L ^= SIMD_4x64::splat(SK[33]); + + interleave_and_store(out, L, R); +} + +BOTAN_FN_ISA_AVX2_GFNI +void camellia_decrypt_x4_24r(const uint8_t in[], uint8_t out[], std::span SK) { + SIMD_4x64 L; + SIMD_4x64 R; + load_and_deinterleave(in, L, R); + + R ^= SIMD_4x64::splat(SK[33]); + L ^= SIMD_4x64::splat(SK[32]); + + six_d_rounds(L, R, SK.subspan(26)); + + L = FL_4(L, SK[25]); + R = FLINV_4(R, SK[24]); + + six_d_rounds(L, R, SK.subspan(18)); + + L = FL_4(L, SK[17]); + R = FLINV_4(R, SK[16]); + + six_d_rounds(L, R, SK.subspan(10)); + + L = FL_4(L, SK[9]); + R = FLINV_4(R, SK[8]); + + six_d_rounds(L, R, SK.subspan(2)); + + L ^= SIMD_4x64::splat(SK[1]); + R ^= SIMD_4x64::splat(SK[0]); + + interleave_and_store(out, L, R); +} + +} // namespace + +} // namespace Camellia_AVX2_GFNI + +// static +void BOTAN_FN_ISA_AVX2_GFNI Camellia_128::avx2_gfni_encrypt(const uint8_t in[], + uint8_t out[], + size_t blocks, + std::span SK) { + while(blocks >= 4) { + Camellia_AVX2_GFNI::camellia_encrypt_x4_18r(in, out, SK); + in += 4 * 16; + out += 4 * 16; + blocks -= 4; + } + + if(blocks > 0) { + uint8_t ibuf[4 * 16] = {0}; + uint8_t obuf[4 * 16] = {0}; + copy_mem(ibuf, in, blocks * 16); + Camellia_AVX2_GFNI::camellia_encrypt_x4_18r(ibuf, obuf, SK); + copy_mem(out, obuf, blocks * 16); + } +} + +// static +void BOTAN_FN_ISA_AVX2_GFNI Camellia_128::avx2_gfni_decrypt(const uint8_t in[], + uint8_t out[], + size_t blocks, + std::span SK) { + while(blocks >= 4) { + Camellia_AVX2_GFNI::camellia_decrypt_x4_18r(in, out, SK); + in += 4 * 16; + out += 4 * 16; + blocks -= 4; + } + + if(blocks > 0) { + uint8_t ibuf[4 * 16] = {0}; + uint8_t obuf[4 * 16] = {0}; + copy_mem(ibuf, in, blocks * 16); + Camellia_AVX2_GFNI::camellia_decrypt_x4_18r(ibuf, obuf, SK); + copy_mem(out, obuf, blocks * 16); + } +} + +// static +void BOTAN_FN_ISA_AVX2_GFNI Camellia_192::avx2_gfni_encrypt(const uint8_t in[], + uint8_t out[], + size_t blocks, + std::span SK) { + while(blocks >= 4) { + Camellia_AVX2_GFNI::camellia_encrypt_x4_24r(in, out, SK); + in += 4 * 16; + out += 4 * 16; + blocks -= 4; + } + + if(blocks > 0) { + uint8_t ibuf[4 * 16] = {0}; + uint8_t obuf[4 * 16] = {0}; + copy_mem(ibuf, in, blocks * 16); + Camellia_AVX2_GFNI::camellia_encrypt_x4_24r(ibuf, obuf, SK); + copy_mem(out, obuf, blocks * 16); + } +} + +// static +void BOTAN_FN_ISA_AVX2_GFNI Camellia_192::avx2_gfni_decrypt(const uint8_t in[], + uint8_t out[], + size_t blocks, + std::span SK) { + while(blocks >= 4) { + Camellia_AVX2_GFNI::camellia_decrypt_x4_24r(in, out, SK); + in += 4 * 16; + out += 4 * 16; + blocks -= 4; + } + + if(blocks > 0) { + uint8_t ibuf[4 * 16] = {0}; + uint8_t obuf[4 * 16] = {0}; + copy_mem(ibuf, in, blocks * 16); + Camellia_AVX2_GFNI::camellia_decrypt_x4_24r(ibuf, obuf, SK); + copy_mem(out, obuf, blocks * 16); + } +} + +// static +void BOTAN_FN_ISA_AVX2_GFNI Camellia_256::avx2_gfni_encrypt(const uint8_t in[], + uint8_t out[], + size_t blocks, + std::span SK) { + while(blocks >= 4) { + Camellia_AVX2_GFNI::camellia_encrypt_x4_24r(in, out, SK); + in += 4 * 16; + out += 4 * 16; + blocks -= 4; + } + + if(blocks > 0) { + uint8_t ibuf[4 * 16] = {0}; + uint8_t obuf[4 * 16] = {0}; + copy_mem(ibuf, in, blocks * 16); + Camellia_AVX2_GFNI::camellia_encrypt_x4_24r(ibuf, obuf, SK); + copy_mem(out, obuf, blocks * 16); + } +} + +// static +void BOTAN_FN_ISA_AVX2_GFNI Camellia_256::avx2_gfni_decrypt(const uint8_t in[], + uint8_t out[], + size_t blocks, + std::span SK) { + while(blocks >= 4) { + Camellia_AVX2_GFNI::camellia_decrypt_x4_24r(in, out, SK); + in += 4 * 16; + out += 4 * 16; + blocks -= 4; + } + + if(blocks > 0) { + uint8_t ibuf[4 * 16] = {0}; + uint8_t obuf[4 * 16] = {0}; + copy_mem(ibuf, in, blocks * 16); + Camellia_AVX2_GFNI::camellia_decrypt_x4_24r(ibuf, obuf, SK); + copy_mem(out, obuf, blocks * 16); + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/camellia/camellia_avx2_gfni/info.txt botan3-3.12.0+dfsg/src/lib/block/camellia/camellia_avx2_gfni/info.txt --- botan3-3.7.1+dfsg/src/lib/block/camellia/camellia_avx2_gfni/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/camellia/camellia_avx2_gfni/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,18 @@ + +CAMELLIA_AVX2_GFNI -> 20250502 + + + +name -> "Camellia using GFNI/AVX2" + + + +avx2 +gfni + + + +simd_avx2 +simd_4x64 +cpuid + diff -Nru botan3-3.7.1+dfsg/src/lib/block/camellia/camellia_avx512_gfni/camellia_avx512_gfni.cpp botan3-3.12.0+dfsg/src/lib/block/camellia/camellia_avx512_gfni/camellia_avx512_gfni.cpp --- botan3-3.7.1+dfsg/src/lib/block/camellia/camellia_avx512_gfni/camellia_avx512_gfni.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/camellia/camellia_avx512_gfni/camellia_avx512_gfni.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,761 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include + +namespace Botan { + +namespace Camellia_AVX512 { + +namespace { + +constexpr uint64_t pre123_a = gfni_matrix(R"( + 1 1 1 0 1 1 0 1 + 0 0 1 1 0 0 1 0 + 1 1 0 1 0 0 0 0 + 1 0 1 1 0 0 1 1 + 0 0 0 0 1 1 0 0 + 1 0 1 0 0 1 0 0 + 0 0 1 0 1 1 0 0 + 1 0 0 0 0 1 1 0)"); + +constexpr uint64_t pre4_a = gfni_matrix(R"( + 1 1 0 1 1 0 1 1 + 0 1 1 0 0 1 0 0 + 1 0 1 0 0 0 0 1 + 0 1 1 0 0 1 1 1 + 0 0 0 1 1 0 0 0 + 0 1 0 0 1 0 0 1 + 0 1 0 1 1 0 0 0 + 0 0 0 0 1 1 0 1)"); + +constexpr uint8_t pre_c = 0b01000101; + +constexpr uint64_t post2_a = gfni_matrix(R"( + 0 0 0 1 1 1 0 0 + 0 0 0 0 0 0 0 1 + 0 1 1 0 0 1 1 0 + 1 0 1 1 1 1 1 0 + 0 0 0 1 1 0 1 1 + 1 0 0 0 1 1 1 0 + 0 1 0 1 1 1 1 0 + 0 1 1 1 1 1 1 1)"); + +constexpr uint64_t post3_a = gfni_matrix(R"( + 0 1 1 0 0 1 1 0 + 1 0 1 1 1 1 1 0 + 0 0 0 1 1 0 1 1 + 1 0 0 0 1 1 1 0 + 0 1 0 1 1 1 1 0 + 0 1 1 1 1 1 1 1 + 0 0 0 1 1 1 0 0 + 0 0 0 0 0 0 0 1)"); + +constexpr uint64_t post14_a = gfni_matrix(R"( + 0 0 0 0 0 0 0 1 + 0 1 1 0 0 1 1 0 + 1 0 1 1 1 1 1 0 + 0 0 0 1 1 0 1 1 + 1 0 0 0 1 1 1 0 + 0 1 0 1 1 1 1 0 + 0 1 1 1 1 1 1 1 + 0 0 0 1 1 1 0 0)"); + +// NOLINTBEGIN(portability-simd-intrinsics) + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI SIMD_8x64 camellia_f(SIMD_8x64 x) { + const __m512i xr = x.raw(); + + /* + * Camellia sends different bytes of each word through different sboxes; we + * resolve this like cavemen by computing all 4 S-box variants over the full + * vector in parallel, then blending the results. + */ + + // Compute S1(x), S2(x), S3(x), S4(x) for all bytes + const __m512i y123 = _mm512_gf2p8affine_epi64_epi8(xr, _mm512_set1_epi64(pre123_a), pre_c); + const __m512i y4 = _mm512_gf2p8affine_epi64_epi8(xr, _mm512_set1_epi64(pre4_a), pre_c); + + const __m512i s1 = _mm512_gf2p8affineinv_epi64_epi8(y123, _mm512_set1_epi64(post14_a), 0x6E); + const __m512i s2 = _mm512_gf2p8affineinv_epi64_epi8(y123, _mm512_set1_epi64(post2_a), 0xDC); + const __m512i s3 = _mm512_gf2p8affineinv_epi64_epi8(y123, _mm512_set1_epi64(post3_a), 0x37); + const __m512i s4 = _mm512_gf2p8affineinv_epi64_epi8(y4, _mm512_set1_epi64(post14_a), 0x6E); + + // Blend to find correct S(x) for each byte position + + auto sx = s1; + sx = _mm512_mask_blend_epi8(__mmask64(0x4848484848484848), sx, s2); // s2 at bytes {3,6} + sx = _mm512_mask_blend_epi8(__mmask64(0x2424242424242424), sx, s3); // s3 at bytes {2,5} + sx = _mm512_mask_blend_epi8(__mmask64(0x1212121212121212), sx, s4); // s4 at bytes {1,4} + + // Linear mixing layer + const auto P1 = _mm512_set_epi64(0x0808080908080809, + 0x0000000100000001, + 0x0808080908080809, + 0x0000000100000001, + 0x0808080908080809, + 0x0000000100000001, + 0x0808080908080809, + 0x0000000100000001); + const auto P2 = _mm512_set_epi64(0x09090A0A09090A0A, + 0x0101020201010202, + 0x09090A0A09090A0A, + 0x0101020201010202, + 0x09090A0A09090A0A, + 0x0101020201010202, + 0x09090A0A09090A0A, + 0x0101020201010202); + const auto P3 = _mm512_set_epi64(0x0A0B0B0B0A0B0B0B, + 0x0203030302030303, + 0x0A0B0B0B0A0B0B0B, + 0x0203030302030303, + 0x0A0B0B0B0A0B0B0B, + 0x0203030302030303, + 0x0A0B0B0B0A0B0B0B, + 0x0203030302030303); + const auto P4 = _mm512_set_epi64(0x0C0C0D0C0E0D0C0C, + 0x0404050406050404, + 0x0C0C0D0C0E0D0C0C, + 0x0404050406050404, + 0x0C0C0D0C0E0D0C0C, + 0x0404050406050404, + 0x0C0C0D0C0E0D0C0C, + 0x0404050406050404); + const auto P5 = _mm512_set_epi64(0x0D0E0E0D0F0E0D0F, + 0x0506060507060507, + 0x0D0E0E0D0F0E0D0F, + 0x0506060507060507, + 0x0D0E0E0D0F0E0D0F, + 0x0506060507060507, + 0x0D0E0E0D0F0E0D0F, + 0x0506060507060507); + const auto P6 = _mm512_set_epi64(0x0F0F0F0EFFFFFFFF, + 0x07070706FFFFFFFF, + 0x0F0F0F0EFFFFFFFF, + 0x07070706FFFFFFFF, + 0x0F0F0F0EFFFFFFFF, + 0x07070706FFFFFFFF, + 0x0F0F0F0EFFFFFFFF, + 0x07070706FFFFFFFF); + + const auto t1 = SIMD_8x64(_mm512_shuffle_epi8(sx, P1)); + const auto t2 = SIMD_8x64(_mm512_shuffle_epi8(sx, P2)); + const auto t3 = SIMD_8x64(_mm512_shuffle_epi8(sx, P3)); + const auto t4 = SIMD_8x64(_mm512_shuffle_epi8(sx, P4)); + const auto t5 = SIMD_8x64(_mm512_shuffle_epi8(sx, P5)); + const auto t6 = SIMD_8x64(_mm512_shuffle_epi8(sx, P6)); + + return (t1 ^ t2 ^ t3 ^ t4 ^ t5 ^ t6); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512 SIMD_8x64 FL_8(SIMD_8x64 v, uint64_t K) { + const auto Kv = _mm512_set1_epi64(K); + auto vr = v.raw(); + + // x2 ^= rotl<1>(x1 & k1): AND, rotate 32-bit elements, shift high->low, XOR + vr = _mm512_xor_si512(vr, _mm512_srli_epi64(_mm512_rol_epi32(_mm512_and_si512(vr, Kv), 1), 32)); + + // x1 ^= (x2 | k2): OR, shift low->high, XOR + vr = _mm512_xor_si512(vr, _mm512_slli_epi64(_mm512_or_si512(vr, Kv), 32)); + + return SIMD_8x64(vr); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512 SIMD_8x64 FLINV_8(SIMD_8x64 v, uint64_t K) { + const auto Kv = _mm512_set1_epi64(K); + auto vr = v.raw(); + + // x1 ^= (x2 | k2): OR, shift low->high, XOR + vr = _mm512_xor_si512(vr, _mm512_slli_epi64(_mm512_or_si512(vr, Kv), 32)); + + // x2 ^= rotl<1>(x1 & k1): AND, rotate 32-bit elements, shift high->low, XOR + vr = _mm512_xor_si512(vr, _mm512_srli_epi64(_mm512_rol_epi32(_mm512_and_si512(vr, Kv), 1), 32)); + + return SIMD_8x64(vr); +} + +/* +* Load 8 blocks, byte-swap, and deinterleave into L (even) and R (odd) halves +*/ +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512 void load_and_deinterleave(const uint8_t in[], SIMD_8x64& L, SIMD_8x64& R) { + const auto idx_l = _mm512_set_epi64(0x0E, 0x0C, 0x0A, 0x08, 0x06, 0x04, 0x02, 0x00); + const auto idx_r = _mm512_set_epi64(0x0F, 0x0D, 0x0B, 0x09, 0x07, 0x05, 0x03, 0x01); + + auto A = SIMD_8x64::load_be(in); + auto B = SIMD_8x64::load_be(in + 64); + + L = SIMD_8x64(_mm512_permutex2var_epi64(A.raw(), idx_l, B.raw())); + R = SIMD_8x64(_mm512_permutex2var_epi64(A.raw(), idx_r, B.raw())); +} + +/* +* Interleave R/L halves (note swap), byte-swap, and store 8 blocks +*/ +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512 void interleave_and_store(uint8_t out[], SIMD_8x64 L, SIMD_8x64 R) { + const auto idx_lo = _mm512_set_epi64(0x0B, 0x03, 0x0A, 0x02, 0x09, 0x01, 0x08, 0x00); + const auto idx_hi = _mm512_set_epi64(0x0F, 0x07, 0x0E, 0x06, 0x0D, 0x05, 0x0C, 0x04); + + auto A = SIMD_8x64(_mm512_permutex2var_epi64(R.raw(), idx_lo, L.raw())); + auto B = SIMD_8x64(_mm512_permutex2var_epi64(R.raw(), idx_hi, L.raw())); + + A.store_be(out); + B.store_be(out + 64); +} + +// NOLINTEND(portability-simd-intrinsics) + +// Helpers for 6 round iterations + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI void six_e_rounds(SIMD_8x64& L, + SIMD_8x64& R, + std::span SK) { + R ^= camellia_f(L ^ SIMD_8x64::splat(SK[0])); + L ^= camellia_f(R ^ SIMD_8x64::splat(SK[1])); + R ^= camellia_f(L ^ SIMD_8x64::splat(SK[2])); + L ^= camellia_f(R ^ SIMD_8x64::splat(SK[3])); + R ^= camellia_f(L ^ SIMD_8x64::splat(SK[4])); + L ^= camellia_f(R ^ SIMD_8x64::splat(SK[5])); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI void six_d_rounds(SIMD_8x64& L, + SIMD_8x64& R, + std::span SK) { + R ^= camellia_f(L ^ SIMD_8x64::splat(SK[5])); + L ^= camellia_f(R ^ SIMD_8x64::splat(SK[4])); + R ^= camellia_f(L ^ SIMD_8x64::splat(SK[3])); + L ^= camellia_f(R ^ SIMD_8x64::splat(SK[2])); + R ^= camellia_f(L ^ SIMD_8x64::splat(SK[1])); + L ^= camellia_f(R ^ SIMD_8x64::splat(SK[0])); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI void six_e_rounds_x2( + SIMD_8x64& L1, SIMD_8x64& R1, SIMD_8x64& L2, SIMD_8x64& R2, std::span SK) { + const auto K0 = SIMD_8x64::splat(SK[0]); + const auto K1 = SIMD_8x64::splat(SK[1]); + const auto K2 = SIMD_8x64::splat(SK[2]); + const auto K3 = SIMD_8x64::splat(SK[3]); + const auto K4 = SIMD_8x64::splat(SK[4]); + const auto K5 = SIMD_8x64::splat(SK[5]); + + R1 ^= camellia_f(L1 ^ K0); + R2 ^= camellia_f(L2 ^ K0); + L1 ^= camellia_f(R1 ^ K1); + L2 ^= camellia_f(R2 ^ K1); + R1 ^= camellia_f(L1 ^ K2); + R2 ^= camellia_f(L2 ^ K2); + L1 ^= camellia_f(R1 ^ K3); + L2 ^= camellia_f(R2 ^ K3); + R1 ^= camellia_f(L1 ^ K4); + R2 ^= camellia_f(L2 ^ K4); + L1 ^= camellia_f(R1 ^ K5); + L2 ^= camellia_f(R2 ^ K5); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI void six_d_rounds_x2( + SIMD_8x64& L1, SIMD_8x64& R1, SIMD_8x64& L2, SIMD_8x64& R2, std::span SK) { + const auto K0 = SIMD_8x64::splat(SK[0]); + const auto K1 = SIMD_8x64::splat(SK[1]); + const auto K2 = SIMD_8x64::splat(SK[2]); + const auto K3 = SIMD_8x64::splat(SK[3]); + const auto K4 = SIMD_8x64::splat(SK[4]); + const auto K5 = SIMD_8x64::splat(SK[5]); + + R1 ^= camellia_f(L1 ^ K5); + R2 ^= camellia_f(L2 ^ K5); + L1 ^= camellia_f(R1 ^ K4); + L2 ^= camellia_f(R2 ^ K4); + R1 ^= camellia_f(L1 ^ K3); + R2 ^= camellia_f(L2 ^ K3); + L1 ^= camellia_f(R1 ^ K2); + L2 ^= camellia_f(R2 ^ K2); + R1 ^= camellia_f(L1 ^ K1); + R2 ^= camellia_f(L2 ^ K1); + L1 ^= camellia_f(R1 ^ K0); + L2 ^= camellia_f(R2 ^ K0); +} + +BOTAN_FN_ISA_AVX512_GFNI +void camellia_encrypt_x16_18r(const uint8_t in[], uint8_t out[], std::span SK) { + SIMD_8x64 L1; + SIMD_8x64 R1; + SIMD_8x64 L2; + SIMD_8x64 R2; + load_and_deinterleave(in, L1, R1); + load_and_deinterleave(in + 128, L2, R2); + + const auto K0 = SIMD_8x64::splat(SK[0]); + const auto K1 = SIMD_8x64::splat(SK[1]); + L1 ^= K0; + L2 ^= K0; + R1 ^= K1; + R2 ^= K1; + + six_e_rounds_x2(L1, R1, L2, R2, SK.subspan(2)); + + L1 = FL_8(L1, SK[8]); + L2 = FL_8(L2, SK[8]); + R1 = FLINV_8(R1, SK[9]); + R2 = FLINV_8(R2, SK[9]); + + six_e_rounds_x2(L1, R1, L2, R2, SK.subspan(10)); + + L1 = FL_8(L1, SK[16]); + L2 = FL_8(L2, SK[16]); + R1 = FLINV_8(R1, SK[17]); + R2 = FLINV_8(R2, SK[17]); + + six_e_rounds_x2(L1, R1, L2, R2, SK.subspan(18)); + + const auto K24 = SIMD_8x64::splat(SK[24]); + const auto K25 = SIMD_8x64::splat(SK[25]); + R1 ^= K24; + R2 ^= K24; + L1 ^= K25; + L2 ^= K25; + + interleave_and_store(out, L1, R1); + interleave_and_store(out + 128, L2, R2); +} + +BOTAN_FN_ISA_AVX512_GFNI +void camellia_decrypt_x16_18r(const uint8_t in[], uint8_t out[], std::span SK) { + SIMD_8x64 L1; + SIMD_8x64 R1; + SIMD_8x64 L2; + SIMD_8x64 R2; + load_and_deinterleave(in, L1, R1); + load_and_deinterleave(in + 128, L2, R2); + + const auto K25 = SIMD_8x64::splat(SK[25]); + const auto K24 = SIMD_8x64::splat(SK[24]); + R1 ^= K25; + R2 ^= K25; + L1 ^= K24; + L2 ^= K24; + + six_d_rounds_x2(L1, R1, L2, R2, SK.subspan(18)); + + L1 = FL_8(L1, SK[17]); + L2 = FL_8(L2, SK[17]); + R1 = FLINV_8(R1, SK[16]); + R2 = FLINV_8(R2, SK[16]); + + six_d_rounds_x2(L1, R1, L2, R2, SK.subspan(10)); + + L1 = FL_8(L1, SK[9]); + L2 = FL_8(L2, SK[9]); + R1 = FLINV_8(R1, SK[8]); + R2 = FLINV_8(R2, SK[8]); + + six_d_rounds_x2(L1, R1, L2, R2, SK.subspan(2)); + + const auto K1 = SIMD_8x64::splat(SK[1]); + const auto K0 = SIMD_8x64::splat(SK[0]); + L1 ^= K1; + L2 ^= K1; + R1 ^= K0; + R2 ^= K0; + + interleave_and_store(out, L1, R1); + interleave_and_store(out + 128, L2, R2); +} + +BOTAN_FN_ISA_AVX512_GFNI +void camellia_encrypt_x16_24r(const uint8_t in[], uint8_t out[], std::span SK) { + SIMD_8x64 L1; + SIMD_8x64 R1; + SIMD_8x64 L2; + SIMD_8x64 R2; + load_and_deinterleave(in, L1, R1); + load_and_deinterleave(in + 128, L2, R2); + + const auto K0 = SIMD_8x64::splat(SK[0]); + const auto K1 = SIMD_8x64::splat(SK[1]); + L1 ^= K0; + L2 ^= K0; + R1 ^= K1; + R2 ^= K1; + + six_e_rounds_x2(L1, R1, L2, R2, SK.subspan(2)); + + L1 = FL_8(L1, SK[8]); + L2 = FL_8(L2, SK[8]); + R1 = FLINV_8(R1, SK[9]); + R2 = FLINV_8(R2, SK[9]); + + six_e_rounds_x2(L1, R1, L2, R2, SK.subspan(10)); + + L1 = FL_8(L1, SK[16]); + L2 = FL_8(L2, SK[16]); + R1 = FLINV_8(R1, SK[17]); + R2 = FLINV_8(R2, SK[17]); + + six_e_rounds_x2(L1, R1, L2, R2, SK.subspan(18)); + + L1 = FL_8(L1, SK[24]); + L2 = FL_8(L2, SK[24]); + R1 = FLINV_8(R1, SK[25]); + R2 = FLINV_8(R2, SK[25]); + + six_e_rounds_x2(L1, R1, L2, R2, SK.subspan(26)); + + const auto K32 = SIMD_8x64::splat(SK[32]); + const auto K33 = SIMD_8x64::splat(SK[33]); + R1 ^= K32; + R2 ^= K32; + L1 ^= K33; + L2 ^= K33; + + interleave_and_store(out, L1, R1); + interleave_and_store(out + 128, L2, R2); +} + +BOTAN_FN_ISA_AVX512_GFNI +void camellia_decrypt_x16_24r(const uint8_t in[], uint8_t out[], std::span SK) { + SIMD_8x64 L1; + SIMD_8x64 R1; + SIMD_8x64 L2; + SIMD_8x64 R2; + load_and_deinterleave(in, L1, R1); + load_and_deinterleave(in + 128, L2, R2); + + const auto K33 = SIMD_8x64::splat(SK[33]); + const auto K32 = SIMD_8x64::splat(SK[32]); + R1 ^= K33; + R2 ^= K33; + L1 ^= K32; + L2 ^= K32; + + six_d_rounds_x2(L1, R1, L2, R2, SK.subspan(26)); + + L1 = FL_8(L1, SK[25]); + L2 = FL_8(L2, SK[25]); + R1 = FLINV_8(R1, SK[24]); + R2 = FLINV_8(R2, SK[24]); + + six_d_rounds_x2(L1, R1, L2, R2, SK.subspan(18)); + + L1 = FL_8(L1, SK[17]); + L2 = FL_8(L2, SK[17]); + R1 = FLINV_8(R1, SK[16]); + R2 = FLINV_8(R2, SK[16]); + + six_d_rounds_x2(L1, R1, L2, R2, SK.subspan(10)); + + L1 = FL_8(L1, SK[9]); + L2 = FL_8(L2, SK[9]); + R1 = FLINV_8(R1, SK[8]); + R2 = FLINV_8(R2, SK[8]); + + six_d_rounds_x2(L1, R1, L2, R2, SK.subspan(2)); + + const auto K1 = SIMD_8x64::splat(SK[1]); + const auto K0 = SIMD_8x64::splat(SK[0]); + L1 ^= K1; + L2 ^= K1; + R1 ^= K0; + R2 ^= K0; + + interleave_and_store(out, L1, R1); + interleave_and_store(out + 128, L2, R2); +} + +BOTAN_FN_ISA_AVX512_GFNI +void camellia_encrypt_x8_18r(const uint8_t in[], uint8_t out[], std::span SK) { + SIMD_8x64 L; + SIMD_8x64 R; + load_and_deinterleave(in, L, R); + + L ^= SIMD_8x64::splat(SK[0]); + R ^= SIMD_8x64::splat(SK[1]); + + six_e_rounds(L, R, SK.subspan(2)); + + L = FL_8(L, SK[8]); + R = FLINV_8(R, SK[9]); + + six_e_rounds(L, R, SK.subspan(10)); + + L = FL_8(L, SK[16]); + R = FLINV_8(R, SK[17]); + + six_e_rounds(L, R, SK.subspan(18)); + + R ^= SIMD_8x64::splat(SK[24]); + L ^= SIMD_8x64::splat(SK[25]); + + interleave_and_store(out, L, R); +} + +BOTAN_FN_ISA_AVX512_GFNI +void camellia_decrypt_x8_18r(const uint8_t in[], uint8_t out[], std::span SK) { + SIMD_8x64 L; + SIMD_8x64 R; + load_and_deinterleave(in, L, R); + + R ^= SIMD_8x64::splat(SK[25]); + L ^= SIMD_8x64::splat(SK[24]); + + six_d_rounds(L, R, SK.subspan(18)); + + L = FL_8(L, SK[17]); + R = FLINV_8(R, SK[16]); + + six_d_rounds(L, R, SK.subspan(10)); + + L = FL_8(L, SK[9]); + R = FLINV_8(R, SK[8]); + + six_d_rounds(L, R, SK.subspan(2)); + + L ^= SIMD_8x64::splat(SK[1]); + R ^= SIMD_8x64::splat(SK[0]); + + interleave_and_store(out, L, R); +} + +BOTAN_FN_ISA_AVX512_GFNI +void camellia_encrypt_x8_24r(const uint8_t in[], uint8_t out[], std::span SK) { + SIMD_8x64 L; + SIMD_8x64 R; + load_and_deinterleave(in, L, R); + + L ^= SIMD_8x64::splat(SK[0]); + R ^= SIMD_8x64::splat(SK[1]); + + six_e_rounds(L, R, SK.subspan(2)); + + L = FL_8(L, SK[8]); + R = FLINV_8(R, SK[9]); + + six_e_rounds(L, R, SK.subspan(10)); + + L = FL_8(L, SK[16]); + R = FLINV_8(R, SK[17]); + + six_e_rounds(L, R, SK.subspan(18)); + + L = FL_8(L, SK[24]); + R = FLINV_8(R, SK[25]); + + six_e_rounds(L, R, SK.subspan(26)); + + R ^= SIMD_8x64::splat(SK[32]); + L ^= SIMD_8x64::splat(SK[33]); + + interleave_and_store(out, L, R); +} + +BOTAN_FN_ISA_AVX512_GFNI +void camellia_decrypt_x8_24r(const uint8_t in[], uint8_t out[], std::span SK) { + SIMD_8x64 L; + SIMD_8x64 R; + load_and_deinterleave(in, L, R); + + R ^= SIMD_8x64::splat(SK[33]); + L ^= SIMD_8x64::splat(SK[32]); + + six_d_rounds(L, R, SK.subspan(26)); + + L = FL_8(L, SK[25]); + R = FLINV_8(R, SK[24]); + + six_d_rounds(L, R, SK.subspan(18)); + + L = FL_8(L, SK[17]); + R = FLINV_8(R, SK[16]); + + six_d_rounds(L, R, SK.subspan(10)); + + L = FL_8(L, SK[9]); + R = FLINV_8(R, SK[8]); + + six_d_rounds(L, R, SK.subspan(2)); + + L ^= SIMD_8x64::splat(SK[1]); + R ^= SIMD_8x64::splat(SK[0]); + + interleave_and_store(out, L, R); +} + +} // namespace + +} // namespace Camellia_AVX512 + +// static +void BOTAN_FN_ISA_AVX512_GFNI Camellia_128::avx512_gfni_encrypt(const uint8_t in[], + uint8_t out[], + size_t blocks, + std::span SK) { + while(blocks >= 16) { + Camellia_AVX512::camellia_encrypt_x16_18r(in, out, SK); + in += 16 * 16; + out += 16 * 16; + blocks -= 16; + } + + while(blocks >= 8) { + Camellia_AVX512::camellia_encrypt_x8_18r(in, out, SK); + in += 8 * 16; + out += 8 * 16; + blocks -= 8; + } + + if(blocks > 0) { + uint8_t ibuf[8 * 16] = {0}; + uint8_t obuf[8 * 16] = {0}; + copy_mem(ibuf, in, blocks * 16); + Camellia_AVX512::camellia_encrypt_x8_18r(ibuf, obuf, SK); + copy_mem(out, obuf, blocks * 16); + } +} + +// static +void BOTAN_FN_ISA_AVX512_GFNI Camellia_128::avx512_gfni_decrypt(const uint8_t in[], + uint8_t out[], + size_t blocks, + std::span SK) { + while(blocks >= 16) { + Camellia_AVX512::camellia_decrypt_x16_18r(in, out, SK); + in += 16 * 16; + out += 16 * 16; + blocks -= 16; + } + + while(blocks >= 8) { + Camellia_AVX512::camellia_decrypt_x8_18r(in, out, SK); + in += 8 * 16; + out += 8 * 16; + blocks -= 8; + } + + if(blocks > 0) { + uint8_t ibuf[8 * 16] = {0}; + uint8_t obuf[8 * 16] = {0}; + copy_mem(ibuf, in, blocks * 16); + Camellia_AVX512::camellia_decrypt_x8_18r(ibuf, obuf, SK); + copy_mem(out, obuf, blocks * 16); + } +} + +// static +void BOTAN_FN_ISA_AVX512_GFNI Camellia_192::avx512_gfni_encrypt(const uint8_t in[], + uint8_t out[], + size_t blocks, + std::span SK) { + while(blocks >= 16) { + Camellia_AVX512::camellia_encrypt_x16_24r(in, out, SK); + in += 16 * 16; + out += 16 * 16; + blocks -= 16; + } + + while(blocks >= 8) { + Camellia_AVX512::camellia_encrypt_x8_24r(in, out, SK); + in += 8 * 16; + out += 8 * 16; + blocks -= 8; + } + + if(blocks > 0) { + uint8_t ibuf[8 * 16] = {0}; + uint8_t obuf[8 * 16] = {0}; + copy_mem(ibuf, in, blocks * 16); + Camellia_AVX512::camellia_encrypt_x8_24r(ibuf, obuf, SK); + copy_mem(out, obuf, blocks * 16); + } +} + +// static +void BOTAN_FN_ISA_AVX512_GFNI Camellia_192::avx512_gfni_decrypt(const uint8_t in[], + uint8_t out[], + size_t blocks, + std::span SK) { + while(blocks >= 16) { + Camellia_AVX512::camellia_decrypt_x16_24r(in, out, SK); + in += 16 * 16; + out += 16 * 16; + blocks -= 16; + } + + while(blocks >= 8) { + Camellia_AVX512::camellia_decrypt_x8_24r(in, out, SK); + in += 8 * 16; + out += 8 * 16; + blocks -= 8; + } + + if(blocks > 0) { + uint8_t ibuf[8 * 16] = {0}; + uint8_t obuf[8 * 16] = {0}; + copy_mem(ibuf, in, blocks * 16); + Camellia_AVX512::camellia_decrypt_x8_24r(ibuf, obuf, SK); + copy_mem(out, obuf, blocks * 16); + } +} + +// static +void BOTAN_FN_ISA_AVX512_GFNI Camellia_256::avx512_gfni_encrypt(const uint8_t in[], + uint8_t out[], + size_t blocks, + std::span SK) { + while(blocks >= 16) { + Camellia_AVX512::camellia_encrypt_x16_24r(in, out, SK); + in += 16 * 16; + out += 16 * 16; + blocks -= 16; + } + + while(blocks >= 8) { + Camellia_AVX512::camellia_encrypt_x8_24r(in, out, SK); + in += 8 * 16; + out += 8 * 16; + blocks -= 8; + } + + if(blocks > 0) { + uint8_t ibuf[8 * 16] = {0}; + uint8_t obuf[8 * 16] = {0}; + copy_mem(ibuf, in, blocks * 16); + Camellia_AVX512::camellia_encrypt_x8_24r(ibuf, obuf, SK); + copy_mem(out, obuf, blocks * 16); + } +} + +// static +void BOTAN_FN_ISA_AVX512_GFNI Camellia_256::avx512_gfni_decrypt(const uint8_t in[], + uint8_t out[], + size_t blocks, + std::span SK) { + while(blocks >= 16) { + Camellia_AVX512::camellia_decrypt_x16_24r(in, out, SK); + in += 16 * 16; + out += 16 * 16; + blocks -= 16; + } + + while(blocks >= 8) { + Camellia_AVX512::camellia_decrypt_x8_24r(in, out, SK); + in += 8 * 16; + out += 8 * 16; + blocks -= 8; + } + + if(blocks > 0) { + uint8_t ibuf[8 * 16] = {0}; + uint8_t obuf[8 * 16] = {0}; + copy_mem(ibuf, in, blocks * 16); + Camellia_AVX512::camellia_decrypt_x8_24r(ibuf, obuf, SK); + copy_mem(out, obuf, blocks * 16); + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/camellia/camellia_avx512_gfni/info.txt botan3-3.12.0+dfsg/src/lib/block/camellia/camellia_avx512_gfni/info.txt --- botan3-3.7.1+dfsg/src/lib/block/camellia/camellia_avx512_gfni/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/camellia/camellia_avx512_gfni/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,18 @@ + +CAMELLIA_AVX512_GFNI -> 20260313 + + + +name -> "Camellia AVX-512/GFNI" + + + +cpuid +simd_avx2 +simd_8x64 + + + +gfni +avx512 + diff -Nru botan3-3.7.1+dfsg/src/lib/block/camellia/camellia_hwaes/camellia_hwaes.cpp botan3-3.12.0+dfsg/src/lib/block/camellia/camellia_hwaes/camellia_hwaes.cpp --- botan3-3.7.1+dfsg/src/lib/block/camellia/camellia_hwaes/camellia_hwaes.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/camellia/camellia_hwaes/camellia_hwaes.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,437 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan { + +namespace Camellia_HWAES { + +namespace { + +/* Helpers for 64-bit operations on SIMD_4x32 */ + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES SIMD_4x32 load_be64(const uint8_t* in) { + const auto bswap64 = SIMD_4x32(0x04050607, 0x00010203, 0x0C0D0E0F, 0x08090A0B); + return SIMD_4x32::byte_shuffle(SIMD_4x32::load_le(in), bswap64); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES void store_be64(uint8_t* out, SIMD_4x32 v) { + const auto bswap64 = SIMD_4x32(0x04050607, 0x00010203, 0x0C0D0E0F, 0x08090A0B); + SIMD_4x32::byte_shuffle(v, bswap64).store_le(out); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES SIMD_4x32 splat64(uint64_t v) { + const uint32_t lo = static_cast(v); + const uint32_t hi = static_cast(v >> 32); + return SIMD_4x32(lo, hi, lo, hi); +} + +/* The Camellia round function */ +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES SIMD_4x32 camellia_f(SIMD_4x32 x) { + // Pre-affine shared by S1/S2/S3 + constexpr uint64_t pre123_a = gfni_matrix(R"( + 1 1 1 0 1 1 0 1 + 0 0 1 1 0 0 1 0 + 1 1 0 1 0 0 0 0 + 1 0 1 1 0 0 1 1 + 0 0 0 0 1 1 0 0 + 1 0 1 0 0 1 0 0 + 0 0 1 0 1 1 0 0 + 1 0 0 0 0 1 1 0)"); + + // Pre-affine for S4 + constexpr uint64_t pre4_a = gfni_matrix(R"( + 1 1 0 1 1 0 1 1 + 0 1 1 0 0 1 0 0 + 1 0 1 0 0 0 0 1 + 0 1 1 0 0 1 1 1 + 0 0 0 1 1 0 0 0 + 0 1 0 0 1 0 0 1 + 0 1 0 1 1 0 0 0 + 0 0 0 0 1 1 0 1)"); + + constexpr uint8_t pre_c = 0x45; + + // Post-affine for S1 and S4 + constexpr uint64_t post14_a = gfni_matrix(R"( + 0 0 0 0 0 0 0 1 + 0 1 1 0 0 1 1 0 + 1 0 1 1 1 1 1 0 + 0 0 0 1 1 0 1 1 + 1 0 0 0 1 1 1 0 + 0 1 0 1 1 1 1 0 + 0 1 1 1 1 1 1 1 + 0 0 0 1 1 1 0 0)"); + constexpr uint8_t post14_c = 0x6E; + + // Post-affine for S2 + constexpr uint64_t post2_a = gfni_matrix(R"( + 0 0 0 1 1 1 0 0 + 0 0 0 0 0 0 0 1 + 0 1 1 0 0 1 1 0 + 1 0 1 1 1 1 1 0 + 0 0 0 1 1 0 1 1 + 1 0 0 0 1 1 1 0 + 0 1 0 1 1 1 1 0 + 0 1 1 1 1 1 1 1)"); + constexpr uint8_t post2_c = 0xDC; + + // Post-affine for S3 + constexpr uint64_t post3_a = gfni_matrix(R"( + 0 1 1 0 0 1 1 0 + 1 0 1 1 1 1 1 0 + 0 0 0 1 1 0 1 1 + 1 0 0 0 1 1 1 0 + 0 1 0 1 1 1 1 0 + 0 1 1 1 1 1 1 1 + 0 0 0 1 1 1 0 0 + 0 0 0 0 0 0 0 1)"); + constexpr uint8_t post3_c = 0x37; + + constexpr auto PRE123 = Gf2AffineTransformation(pre123_a, pre_c); + constexpr auto PRE4 = Gf2AffineTransformation(pre4_a, pre_c); + constexpr auto POST14 = Gf2AffineTransformation::post_sbox(post14_a, post14_c); + constexpr auto POST2 = Gf2AffineTransformation::post_sbox(post2_a, post2_c); + constexpr auto POST3 = Gf2AffineTransformation::post_sbox(post3_a, post3_c); + + const auto mask_s2 = SIMD_4x32(0xFF000000, 0x00FF0000, 0xFF000000, 0x00FF0000); + const auto mask_s3 = SIMD_4x32(0x00FF0000, 0x0000FF00, 0x00FF0000, 0x0000FF00); + const auto mask_s4 = SIMD_4x32(0x0000FF00, 0x000000FF, 0x0000FF00, 0x000000FF); + + const auto pre123 = PRE123.affine_transform(x); + const auto pre4 = PRE4.affine_transform(x); + + const auto sub = hw_aes_sbox(SIMD_4x32::byte_blend(mask_s4, pre4, pre123)); + + const auto s14 = POST14.affine_transform(sub); + const auto s2 = POST2.affine_transform(sub); + const auto s3 = POST3.affine_transform(sub); + + // Final merged Sbox output for all bytes + const auto sbox = SIMD_4x32::byte_blend(mask_s3, s3, SIMD_4x32::byte_blend(mask_s2, s2, s14)); + + // The linear mixing step + const auto P1 = SIMD_4x32(0x00000001, 0x00000001, 0x08080809, 0x08080809); + const auto P2 = SIMD_4x32(0x01010202, 0x01010202, 0x09090A0A, 0x09090A0A); + const auto P3 = SIMD_4x32(0x02030303, 0x02030303, 0x0A0B0B0B, 0x0A0B0B0B); + const auto P4 = SIMD_4x32(0x06050404, 0x04040504, 0x0E0D0C0C, 0x0C0C0D0C); + const auto P5 = SIMD_4x32(0x07060507, 0x05060605, 0x0F0E0D0F, 0x0D0E0E0D); + const auto P6 = SIMD_4x32(0xFFFFFFFF, 0x07070706, 0xFFFFFFFF, 0x0F0F0F0E); + + const auto sxp1 = SIMD_4x32::byte_shuffle(sbox, P1); + const auto sxp2 = SIMD_4x32::byte_shuffle(sbox, P2); + const auto sxp3 = SIMD_4x32::byte_shuffle(sbox, P3); + const auto sxp4 = SIMD_4x32::byte_shuffle(sbox, P4); + const auto sxp5 = SIMD_4x32::byte_shuffle(sbox, P5); + const auto sxp6 = SIMD_4x32::byte_shuffle(sbox, P6); + + return (sxp1 ^ sxp2 ^ sxp3 ^ sxp4 ^ sxp5 ^ sxp6); +} + +/* +* FL and FL-inverse operate on 32-bit sub-halves within each 64-bit element. +* We use byte_shuffle to broadcast each 32-bit half, then recombine with byte_blend. +*/ +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES SIMD_4x32 FL_2(SIMD_4x32 v, uint64_t K) { + const uint32_t k1 = static_cast(K >> 32); + const uint32_t k2 = static_cast(K); + + // Broadcast upper/lower 32-bit halves of each 64-bit element + const auto shuf_hi = SIMD_4x32(0x07060504, 0x07060504, 0x0F0E0D0C, 0x0F0E0D0C); + const auto shuf_lo = SIMD_4x32(0x03020100, 0x03020100, 0x0B0A0908, 0x0B0A0908); + + auto x1 = SIMD_4x32::byte_shuffle(v, shuf_hi); + auto x2 = SIMD_4x32::byte_shuffle(v, shuf_lo); + + x2 ^= (x1 & SIMD_4x32::splat(k1)).rotl<1>(); + x1 ^= x2 | SIMD_4x32::splat(k2); + + // Recombine: lo from x2, hi from x1 + const auto mask_hi = SIMD_4x32(0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF); + return SIMD_4x32::byte_blend(mask_hi, x1, x2); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES SIMD_4x32 FLINV_2(SIMD_4x32 v, uint64_t K) { + const uint32_t k1 = static_cast(K >> 32); + const uint32_t k2 = static_cast(K); + + const auto shuf_hi = SIMD_4x32(0x07060504, 0x07060504, 0x0F0E0D0C, 0x0F0E0D0C); + const auto shuf_lo = SIMD_4x32(0x03020100, 0x03020100, 0x0B0A0908, 0x0B0A0908); + + auto x1 = SIMD_4x32::byte_shuffle(v, shuf_hi); + auto x2 = SIMD_4x32::byte_shuffle(v, shuf_lo); + + x1 ^= x2 | SIMD_4x32::splat(k2); + x2 ^= (x1 & SIMD_4x32::splat(k1)).rotl<1>(); + + const auto mask_hi = SIMD_4x32(0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF); + return SIMD_4x32::byte_blend(mask_hi, x1, x2); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES void load_and_deinterleave(const uint8_t in[], SIMD_4x32& L, SIMD_4x32& R) { + auto A = load_be64(in); // block 0: [L0, R0] + auto B = load_be64(in + 16); // block 1: [L1, R1] + const auto mask_upper = SIMD_4x32(0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF); + L = SIMD_4x32::byte_blend(mask_upper, B.swap_halves(), A); // [L0, L1] + R = SIMD_4x32::byte_blend(mask_upper, B, A.swap_halves()); // [R0, R1] +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES void interleave_and_store(uint8_t out[], SIMD_4x32 L, SIMD_4x32 R) { + // Camellia output swaps L and R + const auto mask_upper = SIMD_4x32(0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF); + auto A = SIMD_4x32::byte_blend(mask_upper, L.swap_halves(), R); // [R0, L0] + auto B = SIMD_4x32::byte_blend(mask_upper, L, R.swap_halves()); // [R1, L1] + store_be64(out, A); + store_be64(out + 16, B); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES void six_e_rounds(SIMD_4x32& L, SIMD_4x32& R, const uint64_t SK[]) { + R ^= camellia_f(L ^ splat64(SK[0])); + L ^= camellia_f(R ^ splat64(SK[1])); + R ^= camellia_f(L ^ splat64(SK[2])); + L ^= camellia_f(R ^ splat64(SK[3])); + R ^= camellia_f(L ^ splat64(SK[4])); + L ^= camellia_f(R ^ splat64(SK[5])); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES void six_d_rounds(SIMD_4x32& L, SIMD_4x32& R, const uint64_t SK[]) { + R ^= camellia_f(L ^ splat64(SK[5])); + L ^= camellia_f(R ^ splat64(SK[4])); + R ^= camellia_f(L ^ splat64(SK[3])); + L ^= camellia_f(R ^ splat64(SK[2])); + R ^= camellia_f(L ^ splat64(SK[1])); + L ^= camellia_f(R ^ splat64(SK[0])); +} + +BOTAN_FN_ISA_HWAES void camellia_encrypt_x2_18r(const uint8_t in[], uint8_t out[], std::span SK) { + SIMD_4x32 L; + SIMD_4x32 R; + load_and_deinterleave(in, L, R); + + L ^= splat64(SK[0]); + R ^= splat64(SK[1]); + + six_e_rounds(L, R, &SK[2]); + L = FL_2(L, SK[8]); + R = FLINV_2(R, SK[9]); + six_e_rounds(L, R, &SK[10]); + L = FL_2(L, SK[16]); + R = FLINV_2(R, SK[17]); + six_e_rounds(L, R, &SK[18]); + + R ^= splat64(SK[24]); + L ^= splat64(SK[25]); + + interleave_and_store(out, L, R); +} + +BOTAN_FN_ISA_HWAES void camellia_decrypt_x2_18r(const uint8_t in[], uint8_t out[], std::span SK) { + SIMD_4x32 L; + SIMD_4x32 R; + load_and_deinterleave(in, L, R); + + R ^= splat64(SK[25]); + L ^= splat64(SK[24]); + + six_d_rounds(L, R, &SK[18]); + L = FL_2(L, SK[17]); + R = FLINV_2(R, SK[16]); + six_d_rounds(L, R, &SK[10]); + L = FL_2(L, SK[9]); + R = FLINV_2(R, SK[8]); + six_d_rounds(L, R, &SK[2]); + + L ^= splat64(SK[1]); + R ^= splat64(SK[0]); + + interleave_and_store(out, L, R); +} + +BOTAN_FN_ISA_HWAES void camellia_encrypt_x2_24r(const uint8_t in[], uint8_t out[], std::span SK) { + SIMD_4x32 L; + SIMD_4x32 R; + load_and_deinterleave(in, L, R); + + L ^= splat64(SK[0]); + R ^= splat64(SK[1]); + + six_e_rounds(L, R, &SK[2]); + L = FL_2(L, SK[8]); + R = FLINV_2(R, SK[9]); + six_e_rounds(L, R, &SK[10]); + L = FL_2(L, SK[16]); + R = FLINV_2(R, SK[17]); + six_e_rounds(L, R, &SK[18]); + L = FL_2(L, SK[24]); + R = FLINV_2(R, SK[25]); + six_e_rounds(L, R, &SK[26]); + + R ^= splat64(SK[32]); + L ^= splat64(SK[33]); + + interleave_and_store(out, L, R); +} + +BOTAN_FN_ISA_HWAES void camellia_decrypt_x2_24r(const uint8_t in[], uint8_t out[], std::span SK) { + SIMD_4x32 L; + SIMD_4x32 R; + load_and_deinterleave(in, L, R); + + R ^= splat64(SK[33]); + L ^= splat64(SK[32]); + + six_d_rounds(L, R, &SK[26]); + L = FL_2(L, SK[25]); + R = FLINV_2(R, SK[24]); + six_d_rounds(L, R, &SK[18]); + L = FL_2(L, SK[17]); + R = FLINV_2(R, SK[16]); + six_d_rounds(L, R, &SK[10]); + L = FL_2(L, SK[9]); + R = FLINV_2(R, SK[8]); + six_d_rounds(L, R, &SK[2]); + + L ^= splat64(SK[1]); + R ^= splat64(SK[0]); + + interleave_and_store(out, L, R); +} + +} // namespace + +} // namespace Camellia_HWAES + +// static +void BOTAN_FN_ISA_HWAES Camellia_128::hwaes_encrypt(const uint8_t in[], + uint8_t out[], + size_t blocks, + std::span SK) { + while(blocks >= 2) { + Camellia_HWAES::camellia_encrypt_x2_18r(in, out, SK); + in += 2 * 16; + out += 2 * 16; + blocks -= 2; + } + + if(blocks > 0) { + uint8_t ibuf[2 * 16] = {0}; + uint8_t obuf[2 * 16] = {0}; + copy_mem(ibuf, in, 16); + Camellia_HWAES::camellia_encrypt_x2_18r(ibuf, obuf, SK); + copy_mem(out, obuf, 16); + } +} + +// static +void BOTAN_FN_ISA_HWAES Camellia_128::hwaes_decrypt(const uint8_t in[], + uint8_t out[], + size_t blocks, + std::span SK) { + while(blocks >= 2) { + Camellia_HWAES::camellia_decrypt_x2_18r(in, out, SK); + in += 2 * 16; + out += 2 * 16; + blocks -= 2; + } + + if(blocks > 0) { + uint8_t ibuf[2 * 16] = {0}; + uint8_t obuf[2 * 16] = {0}; + copy_mem(ibuf, in, 16); + Camellia_HWAES::camellia_decrypt_x2_18r(ibuf, obuf, SK); + copy_mem(out, obuf, 16); + } +} + +// static +void BOTAN_FN_ISA_HWAES Camellia_192::hwaes_encrypt(const uint8_t in[], + uint8_t out[], + size_t blocks, + std::span SK) { + while(blocks >= 2) { + Camellia_HWAES::camellia_encrypt_x2_24r(in, out, SK); + in += 2 * 16; + out += 2 * 16; + blocks -= 2; + } + + if(blocks > 0) { + uint8_t ibuf[2 * 16] = {0}; + uint8_t obuf[2 * 16] = {0}; + copy_mem(ibuf, in, 16); + Camellia_HWAES::camellia_encrypt_x2_24r(ibuf, obuf, SK); + copy_mem(out, obuf, 16); + } +} + +// static +void BOTAN_FN_ISA_HWAES Camellia_192::hwaes_decrypt(const uint8_t in[], + uint8_t out[], + size_t blocks, + std::span SK) { + while(blocks >= 2) { + Camellia_HWAES::camellia_decrypt_x2_24r(in, out, SK); + in += 2 * 16; + out += 2 * 16; + blocks -= 2; + } + + if(blocks > 0) { + uint8_t ibuf[2 * 16] = {0}; + uint8_t obuf[2 * 16] = {0}; + copy_mem(ibuf, in, 16); + Camellia_HWAES::camellia_decrypt_x2_24r(ibuf, obuf, SK); + copy_mem(out, obuf, 16); + } +} + +// static +void BOTAN_FN_ISA_HWAES Camellia_256::hwaes_encrypt(const uint8_t in[], + uint8_t out[], + size_t blocks, + std::span SK) { + while(blocks >= 2) { + Camellia_HWAES::camellia_encrypt_x2_24r(in, out, SK); + in += 2 * 16; + out += 2 * 16; + blocks -= 2; + } + + if(blocks > 0) { + uint8_t ibuf[2 * 16] = {0}; + uint8_t obuf[2 * 16] = {0}; + copy_mem(ibuf, in, 16); + Camellia_HWAES::camellia_encrypt_x2_24r(ibuf, obuf, SK); + copy_mem(out, obuf, 16); + } +} + +// static +void BOTAN_FN_ISA_HWAES Camellia_256::hwaes_decrypt(const uint8_t in[], + uint8_t out[], + size_t blocks, + std::span SK) { + while(blocks >= 2) { + Camellia_HWAES::camellia_decrypt_x2_24r(in, out, SK); + in += 2 * 16; + out += 2 * 16; + blocks -= 2; + } + + if(blocks > 0) { + uint8_t ibuf[2 * 16] = {0}; + uint8_t obuf[2 * 16] = {0}; + copy_mem(ibuf, in, 16); + Camellia_HWAES::camellia_decrypt_x2_24r(ibuf, obuf, SK); + copy_mem(out, obuf, 16); + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/camellia/camellia_hwaes/info.txt botan3-3.12.0+dfsg/src/lib/block/camellia/camellia_hwaes/info.txt --- botan3-3.7.1+dfsg/src/lib/block/camellia/camellia_hwaes/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/camellia/camellia_hwaes/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ + +CAMELLIA_HWAES -> 20260321 + + + +name -> "Camellia using hardware AES instructions" + + + +cpuid +simd_hwaes + diff -Nru botan3-3.7.1+dfsg/src/lib/block/cascade/cascade.cpp botan3-3.12.0+dfsg/src/lib/block/cascade/cascade.cpp --- botan3-3.7.1+dfsg/src/lib/block/cascade/cascade.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/cascade/cascade.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,23 +7,23 @@ #include +#include #include -#include #include namespace Botan { void Cascade_Cipher::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { - size_t c1_blocks = blocks * (block_size() / m_cipher1->block_size()); - size_t c2_blocks = blocks * (block_size() / m_cipher2->block_size()); + const size_t c1_blocks = blocks * (block_size() / m_cipher1->block_size()); + const size_t c2_blocks = blocks * (block_size() / m_cipher2->block_size()); m_cipher1->encrypt_n(in, out, c1_blocks); m_cipher2->encrypt_n(out, out, c2_blocks); } void Cascade_Cipher::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { - size_t c1_blocks = blocks * (block_size() / m_cipher1->block_size()); - size_t c2_blocks = blocks * (block_size() / m_cipher2->block_size()); + const size_t c1_blocks = blocks * (block_size() / m_cipher1->block_size()); + const size_t c2_blocks = blocks * (block_size() / m_cipher2->block_size()); m_cipher2->decrypt_n(in, out, c2_blocks); m_cipher1->decrypt_n(out, out, c1_blocks); diff -Nru botan3-3.7.1+dfsg/src/lib/block/cascade/cascade.h botan3-3.12.0+dfsg/src/lib/block/cascade/cascade.h --- botan3-3.7.1+dfsg/src/lib/block/cascade/cascade.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/cascade/cascade.h 2026-05-07 01:38:28.000000000 +0000 @@ -39,11 +39,8 @@ */ Cascade_Cipher(std::unique_ptr cipher1, std::unique_ptr cipher2); - Cascade_Cipher(const Cascade_Cipher&) = delete; - Cascade_Cipher& operator=(const Cascade_Cipher&) = delete; - private: - void key_schedule(std::span) override; + void key_schedule(std::span key) override; std::unique_ptr m_cipher1, m_cipher2; size_t m_block_size; diff -Nru botan3-3.7.1+dfsg/src/lib/block/cast128/cast128.cpp botan3-3.12.0+dfsg/src/lib/block/cast128/cast128.cpp --- botan3-3.7.1+dfsg/src/lib/block/cast128/cast128.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/cast128/cast128.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,7 @@ #include +#include #include #include @@ -187,7 +188,10 @@ assert_key_material_set(); while(blocks >= 2) { - uint32_t L0, R0, L1, R1; + uint32_t L0 = 0; + uint32_t R0 = 0; + uint32_t L1 = 0; + uint32_t R1 = 0; load_be(in, L0, R0, L1, R1); L0 ^= F1(R0, m_MK[0], m_RK[0]); @@ -230,8 +234,9 @@ in += 2 * BLOCK_SIZE; } - if(blocks) { - uint32_t L, R; + if(blocks > 0) { + uint32_t L = 0; + uint32_t R = 0; load_be(in, L, R); L ^= F1(R, m_MK[0], m_RK[0]); @@ -262,7 +267,10 @@ assert_key_material_set(); while(blocks >= 2) { - uint32_t L0, R0, L1, R1; + uint32_t L0 = 0; + uint32_t R0 = 0; + uint32_t L1 = 0; + uint32_t R1 = 0; load_be(in, L0, R0, L1, R1); L0 ^= F1(R0, m_MK[15], m_RK[15]); @@ -305,8 +313,9 @@ in += 2 * BLOCK_SIZE; } - if(blocks) { - uint32_t L, R; + if(blocks > 0) { + uint32_t L = 0; + uint32_t R = 0; load_be(in, L, R); L ^= F1(R, m_MK[15], m_RK[15]); @@ -503,7 +512,8 @@ }; secure_vector Z(4); - ByteReader x(X.data()), z(Z.data()); + const ByteReader x(X.data()); + const ByteReader z(Z.data()); Z[0] = X[0] ^ S5[x(13)] ^ S6[x(15)] ^ S7[x(12)] ^ S8[x(14)] ^ S7[x(8)]; Z[1] = X[2] ^ S5[z(0)] ^ S6[z(2)] ^ S7[z(1)] ^ S8[z(3)] ^ S8[x(10)]; diff -Nru botan3-3.7.1+dfsg/src/lib/block/cast128/cast128.h botan3-3.12.0+dfsg/src/lib/block/cast128/cast128.h --- botan3-3.7.1+dfsg/src/lib/block/cast128/cast128.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/cast128/cast128.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #define BOTAN_CAST128_H_ #include +#include namespace Botan { diff -Nru botan3-3.7.1+dfsg/src/lib/block/des/des.cpp botan3-3.12.0+dfsg/src/lib/block/des/des.cpp --- botan3-3.7.1+dfsg/src/lib/block/des/des.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/des/des.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,86 +1,659 @@ /* * DES -* (C) 1999-2008,2018,2020 Jack Lloyd -* -* Based on a public domain implemenation by Phil Karn (who in turn -* credited Richard Outerbridge and Jim Gillogly) +* (C) 1999-2008,2018,2020,2026 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include +#include +#include #include -#include namespace Botan { namespace { -alignas(256) const uint8_t SPBOX_CATS[64 * 8] = { - 0x54, 0x00, 0x10, 0x55, 0x51, 0x15, 0x01, 0x10, 0x04, 0x54, 0x55, 0x04, 0x45, 0x51, 0x40, 0x01, - 0x05, 0x44, 0x44, 0x14, 0x14, 0x50, 0x50, 0x45, 0x11, 0x41, 0x41, 0x11, 0x00, 0x05, 0x15, 0x40, - 0x10, 0x55, 0x01, 0x50, 0x54, 0x40, 0x40, 0x04, 0x51, 0x10, 0x14, 0x41, 0x04, 0x01, 0x45, 0x15, - 0x55, 0x11, 0x50, 0x45, 0x41, 0x05, 0x15, 0x54, 0x05, 0x44, 0x44, 0x00, 0x11, 0x14, 0x00, 0x51, - - 0x55, 0x44, 0x04, 0x15, 0x10, 0x01, 0x51, 0x45, 0x41, 0x55, 0x54, 0x40, 0x44, 0x10, 0x01, 0x51, - 0x14, 0x11, 0x45, 0x00, 0x40, 0x04, 0x15, 0x50, 0x11, 0x41, 0x00, 0x14, 0x05, 0x54, 0x50, 0x05, - 0x00, 0x15, 0x51, 0x10, 0x45, 0x50, 0x54, 0x04, 0x50, 0x44, 0x01, 0x55, 0x15, 0x01, 0x04, 0x40, - 0x05, 0x54, 0x10, 0x41, 0x11, 0x45, 0x41, 0x11, 0x14, 0x00, 0x44, 0x05, 0x40, 0x51, 0x55, 0x14, - - 0x09, 0xA8, 0x00, 0xA1, 0x88, 0x00, 0x29, 0x88, 0x21, 0x81, 0x81, 0x20, 0xA9, 0x21, 0xA0, 0x09, - 0x80, 0x01, 0xA8, 0x08, 0x28, 0xA0, 0xA1, 0x29, 0x89, 0x28, 0x20, 0x89, 0x01, 0xA9, 0x08, 0x80, - 0xA8, 0x80, 0x21, 0x09, 0x20, 0xA8, 0x88, 0x00, 0x08, 0x21, 0xA9, 0x88, 0x81, 0x08, 0x00, 0xA1, - 0x89, 0x20, 0x80, 0xA9, 0x01, 0x29, 0x28, 0x81, 0xA0, 0x89, 0x09, 0xA0, 0x29, 0x01, 0xA1, 0x28, - - 0x51, 0x15, 0x15, 0x04, 0x54, 0x45, 0x41, 0x11, 0x00, 0x50, 0x50, 0x55, 0x05, 0x00, 0x44, 0x41, - 0x01, 0x10, 0x40, 0x51, 0x04, 0x40, 0x11, 0x14, 0x45, 0x01, 0x14, 0x44, 0x10, 0x54, 0x55, 0x05, - 0x44, 0x41, 0x50, 0x55, 0x05, 0x00, 0x00, 0x50, 0x14, 0x44, 0x45, 0x01, 0x51, 0x15, 0x15, 0x04, - 0x55, 0x05, 0x01, 0x10, 0x41, 0x11, 0x54, 0x45, 0x11, 0x14, 0x40, 0x51, 0x04, 0x40, 0x10, 0x54, - - 0x01, 0x29, 0x28, 0xA1, 0x08, 0x01, 0x80, 0x28, 0x89, 0x08, 0x21, 0x89, 0xA1, 0xA8, 0x09, 0x80, - 0x20, 0x88, 0x88, 0x00, 0x81, 0xA9, 0xA9, 0x21, 0xA8, 0x81, 0x00, 0xA0, 0x29, 0x20, 0xA0, 0x09, - 0x08, 0xA1, 0x01, 0x20, 0x80, 0x28, 0xA1, 0x89, 0x21, 0x80, 0xA8, 0x29, 0x89, 0x01, 0x20, 0xA8, - 0xA9, 0x09, 0xA0, 0xA9, 0x28, 0x00, 0x88, 0xA0, 0x09, 0x21, 0x81, 0x08, 0x00, 0x88, 0x29, 0x81, - - 0x41, 0x50, 0x04, 0x55, 0x50, 0x01, 0x55, 0x10, 0x44, 0x15, 0x10, 0x41, 0x11, 0x44, 0x40, 0x05, - 0x00, 0x11, 0x45, 0x04, 0x14, 0x45, 0x01, 0x51, 0x51, 0x00, 0x15, 0x54, 0x05, 0x14, 0x54, 0x40, - 0x44, 0x01, 0x51, 0x14, 0x55, 0x10, 0x05, 0x41, 0x10, 0x44, 0x40, 0x05, 0x41, 0x55, 0x14, 0x50, - 0x15, 0x54, 0x00, 0x51, 0x01, 0x04, 0x50, 0x15, 0x04, 0x11, 0x45, 0x00, 0x54, 0x40, 0x11, 0x45, - - 0x10, 0x51, 0x45, 0x00, 0x04, 0x45, 0x15, 0x54, 0x55, 0x10, 0x00, 0x41, 0x01, 0x40, 0x51, 0x05, - 0x44, 0x15, 0x11, 0x44, 0x41, 0x50, 0x54, 0x11, 0x50, 0x04, 0x05, 0x55, 0x14, 0x01, 0x40, 0x14, - 0x40, 0x14, 0x10, 0x45, 0x45, 0x51, 0x51, 0x01, 0x11, 0x40, 0x44, 0x10, 0x54, 0x05, 0x15, 0x54, - 0x05, 0x41, 0x55, 0x50, 0x14, 0x00, 0x01, 0x55, 0x00, 0x15, 0x50, 0x04, 0x41, 0x44, 0x04, 0x11, - - 0x89, 0x08, 0x20, 0xA9, 0x80, 0x89, 0x01, 0x80, 0x21, 0xA0, 0xA9, 0x28, 0xA8, 0x29, 0x08, 0x01, - 0xA0, 0x81, 0x88, 0x09, 0x28, 0x21, 0xA1, 0xA8, 0x09, 0x00, 0x00, 0xA1, 0x81, 0x88, 0x29, 0x20, - 0x29, 0x20, 0xa8, 0x08, 0x01, 0xA1, 0x08, 0x29, 0x88, 0x01, 0x81, 0xA0, 0xA1, 0x80, 0x20, 0x89, - 0x00, 0xA9, 0x21, 0x81, 0xA0, 0x88, 0x89, 0x00, 0xA9, 0x28, 0x28, 0x09, 0x09, 0x21, 0x80, 0xA8, +template +concept BitsliceT = requires(T& a, const T& b) { + a ^= b; + a &= b; + a |= b; + ~a; }; -const uint32_t SPBOX_CAT_0_MUL = 0x70041106; -const uint32_t SPBOX_CAT_1_MUL = 0x02012020; -const uint32_t SPBOX_CAT_2_MUL = 0x00901048; -const uint32_t SPBOX_CAT_3_MUL = 0x8e060221; -const uint32_t SPBOX_CAT_4_MUL = 0x00912140; -const uint32_t SPBOX_CAT_5_MUL = 0x80841018; -const uint32_t SPBOX_CAT_6_MUL = 0xe0120202; -const uint32_t SPBOX_CAT_7_MUL = 0x00212240; - -const uint32_t SPBOX_CAT_0_MASK = 0x01010404; -const uint32_t SPBOX_CAT_1_MASK = 0x80108020; -const uint32_t SPBOX_CAT_2_MASK = 0x08020208; -const uint32_t SPBOX_CAT_3_MASK = 0x00802081; -const uint32_t SPBOX_CAT_4_MASK = 0x42080100; -const uint32_t SPBOX_CAT_5_MASK = 0x20404010; -const uint32_t SPBOX_CAT_6_MASK = 0x04200802; -const uint32_t SPBOX_CAT_7_MASK = 0x10041040; +/* +* The circuits for the DES sboxes used here were found by Roman Rusakov and +* Solar Designer for use in JtR. The designers explicitly disclaimed all +* copyright with regards to the circuits themselves ("Being mathematical +* formulas, they are not copyrighted and are free for reuse by anyone.") +* +* John The Ripper also contains Sbox circuit descriptions making use of select +* and ternlogd-style instruction sets which are significantly more compact than +* these circuits. Sadly, very few CPUs support such instructions on GPRs. +*/ + +template +BOTAN_FORCE_INLINE void SBox1(T a1, T a2, T a3, T a4, T a5, T a6, T& out1, T& out2, T& out3, T& out4) { + const T x1 = a1 & ~a5; + const T x2 = a4 ^ x1; + const T x3 = a3 | a6; + const T x4 = a1 ^ a3; + const T x5 = x3 & x4; + const T x6 = a4 ^ x5; + const T x7 = x6 & ~x2; + + const T x8 = a5 ^ a6; + const T x9 = a3 ^ x8; + const T x10 = x2 & ~x9; + const T x11 = a6 | x5; + const T x12 = x10 ^ x11; + const T x13 = x12 & ~x7; + + const T x14 = a1 | a6; + const T x15 = x12 | x14; + const T x16 = a5 & ~x6; + const T x17 = x15 ^ x16; + + const T x18 = a4 & ~x14; + const T x19 = x16 ^ x18; + const T x20 = x8 & ~x4; + const T x21 = x19 | x20; + + const T x22 = a3 & ~x1; + const T x23 = x2 ^ x15; + const T x24 = x23 & ~x22; + const T x25 = ~x24; + const T x26 = x3 & x12; + const T x27 = x25 ^ x26; + const T x28 = x17 & ~a2; + const T x29 = x28 ^ x27; + out3 ^= x29; + + const T x30 = x8 ^ x24; + const T x31 = x16 | x30; + const T x32 = x3 ^ x31; + const T x33 = a1 ^ x32; + const T x34 = x27 ^ x33; + const T x35 = x7 | a2; + const T x36 = x35 ^ x34; + out1 ^= x36; + + const T x37 = x2 & ~x21; + const T x38 = x30 ^ x37; + const T x39 = x16 ^ x32; + const T x40 = x34 & ~x39; + const T x41 = x38 ^ x40; + const T x42 = a2 & ~x13; + const T x43 = x42 ^ x41; + out2 ^= x43; + + const T x44 = x9 ^ x20; + const T x45 = x14 ^ x40; + const T x46 = x45 & ~x44; + const T x47 = x41 ^ x46; + const T x48 = x47 | a2; + const T x49 = x48 ^ x21; + out4 ^= x49; +} + +template +BOTAN_FORCE_INLINE void SBox2(T a1, T a2, T a3, T a4, T a5, T a6, T& out1, T& out2, T& out3, T& out4) { + const T x1 = a2 ^ a5; + + const T x2 = a1 & ~a6; + const T x3 = a5 & ~x2; + const T x4 = a2 | x3; + + const T x5 = x1 & ~a6; + const T x6 = a1 & x1; + const T x7 = a5 ^ x6; + const T x8 = x7 & ~x5; + + const T x9 = a3 & a6; + const T x10 = x3 ^ x5; + const T x11 = x4 & x10; + const T x12 = x11 & ~x9; + + const T x13 = a3 & x11; + const T x14 = ~a1; + const T x15 = x13 ^ x14; + const T x16 = a6 ^ x1; + const T x17 = x16 & ~x9; + const T x18 = x15 ^ x17; + const T x19 = a4 & ~x12; + const T x20 = x19 ^ x18; + out2 ^= x20; + + const T x21 = a2 & ~x17; + const T x22 = x7 ^ x21; + const T x23 = x15 & ~x22; + const T x24 = a3 ^ x16; + const T x25 = x23 ^ x24; + const T x26 = x4 & ~a4; + const T x27 = x26 ^ x25; + out1 ^= x27; + + const T x28 = a2 & ~x9; + const T x29 = x24 | x28; + const T x30 = x4 ^ x18; + const T x31 = x9 | x30; + const T x32 = x29 ^ x31; + + const T x33 = x11 ^ x18; + const T x34 = x25 ^ x33; + const T x35 = x31 & x34; + const T x36 = x1 & x29; + const T x37 = x35 ^ x36; + const T x38 = x37 | a4; + const T x39 = x38 ^ x32; + out3 ^= x39; + + const T x40 = x37 & ~x22; + const T x41 = x16 | x30; + const T x42 = x40 ^ x41; + const T x43 = x8 | a4; + const T x44 = x43 ^ x42; + out4 ^= x44; +} + +template +BOTAN_FORCE_INLINE void SBox3(T a1, T a2, T a3, T a4, T a5, T a6, T& out1, T& out2, T& out3, T& out4) { + const T x1 = a1 & ~a2; + const T x2 = a3 ^ a6; + const T x3 = x1 | x2; + const T x4 = a4 ^ a6; + const T x5 = x4 & ~a1; + const T x6 = x3 ^ x5; + + const T x7 = a2 ^ x2; + const T x8 = x7 & ~a6; + const T x9 = x3 ^ x8; + const T x10 = x6 & ~x9; + + const T x11 = a6 & x6; + const T x12 = a4 | x11; + const T x13 = a1 & x12; + const T x14 = x7 ^ x13; + const T x15 = x6 & ~a5; + const T x16 = x15 ^ x14; + out4 ^= x16; + + const T x17 = x2 & x4; + const T x18 = a1 ^ a4; + const T x19 = x9 ^ x18; + const T x20 = a3 | x19; + const T x21 = x20 & ~x17; + + const T x22 = x5 | x18; + const T x23 = x14 & ~x22; + const T x24 = a4 & a6; + const T x25 = x24 & ~a2; + const T x26 = x23 ^ x25; + + const T x27 = x9 & x26; + const T x28 = x7 | x24; + const T x29 = x28 & ~x27; + const T x30 = a1 ^ x29; + const T x31 = x21 & a5; + const T x32 = x31 ^ x30; + out2 ^= x32; + + const T x33 = x6 & ~a2; + const T x34 = x33 & ~a3; + const T x35 = ~x7; + const T x36 = x22 ^ x35; + const T x37 = x34 ^ x36; + const T x38 = a5 & ~x10; + const T x39 = x38 ^ x37; + out1 ^= x39; + + const T x40 = x34 | x36; + const T x41 = x5 | x33; + const T x42 = x40 ^ x41; + const T x43 = a4 & ~x6; + const T x44 = x42 | x43; + const T x45 = a5 & ~x26; + const T x46 = x45 ^ x44; + out3 ^= x46; +} + +template +BOTAN_FORCE_INLINE void SBox4(T a1, T a2, T a3, T a4, T a5, T a6, T& out1, T& out2, T& out3, T& out4) { + const T x1 = a1 ^ a3; + const T x2 = a3 ^ a5; + const T x3 = a2 | a4; + const T x4 = a5 ^ x3; + const T x5 = x2 & ~x4; + const T x6 = x2 & ~a2; + const T x7 = a4 ^ x6; + const T x8 = x1 | x7; + const T x9 = x8 & ~x5; + const T x10 = a2 ^ x9; + + const T x11 = x7 & x10; + const T x12 = x2 & ~x11; + const T x13 = x1 ^ x10; + const T x14 = x13 & ~x12; + const T x15 = x5 ^ x14; + + const T x16 = a2 ^ a4; + const T x17 = a5 | x6; + const T x18 = x13 ^ x17; + const T x19 = x18 & ~x16; + const T x20 = x9 ^ x19; + const T x21 = a6 & ~x15; + const T x22 = x21 ^ x20; + out1 ^= x22; + + const T x23 = ~x20; + const T x24 = x15 & ~a6; + const T x25 = x24 ^ x23; + out2 ^= x25; + + const T x26 = x15 ^ x23; + const T x27 = x26 & ~x16; + const T x28 = x11 | x27; + const T x29 = x18 ^ x28; + const T x30 = x10 | a6; + const T x31 = x30 ^ x29; + out3 ^= x31; + + const T x32 = a6 & x10; + const T x33 = x32 ^ x29; + out4 ^= x33; +} + +template +BOTAN_FORCE_INLINE void SBox5(T a1, T a2, T a3, T a4, T a5, T a6, T& out1, T& out2, T& out3, T& out4) { + const T x1 = a1 | a3; + const T x2 = x1 & ~a6; + const T x3 = a1 ^ x2; + const T x4 = a3 ^ x3; + const T x5 = a4 | x4; + + const T x6 = x2 & ~a4; + const T x7 = a3 ^ x6; + const T x8 = a5 & x7; + const T x9 = a1 | x4; + const T x10 = x8 ^ x9; + const T x11 = a4 ^ x10; + + const T x12 = a6 ^ x11; + const T x13 = x3 | x12; + const T x14 = a5 & x13; + const T x15 = x3 ^ x14; + const T x16 = a4 & x9; + const T x17 = x15 ^ x16; + + const T x18 = x13 & ~a1; + const T x19 = x7 ^ x18; + const T x20 = a5 ^ x5; + const T x21 = x20 & ~x19; + const T x22 = ~x21; + const T x23 = x22 & ~a2; + const T x24 = x23 ^ x11; + out3 ^= x24; + + const T x25 = x7 & ~x14; + const T x26 = x18 ^ x20; + const T x27 = x17 | x26; + const T x28 = x27 & ~x25; + const T x29 = x5 & ~x28; + + const T x30 = x12 & x28; + const T x31 = x20 ^ x30; + const T x32 = x7 & x9; + const T x33 = x31 | x32; + const T x34 = x14 ^ x33; + const T x35 = x34 & a2; + const T x36 = x35 ^ x17; + out4 ^= x36; + + const T x37 = x1 ^ x28; + const T x38 = a1 ^ x37; + const T x39 = a4 & x31; + const T x40 = x38 ^ x39; + const T x41 = x29 | a2; + const T x42 = x41 ^ x40; + out1 ^= x42; + + const T x43 = x5 ^ x7; + const T x44 = x43 & ~x40; + const T x45 = x3 ^ x31; + const T x46 = x44 ^ x45; + const T x47 = x5 & a2; + const T x48 = x47 ^ x46; + out2 ^= x48; +} + +template +BOTAN_FORCE_INLINE void SBox6(T a1, T a2, T a3, T a4, T a5, T a6, T& out1, T& out2, T& out3, T& out4) { + const T x1 = a2 ^ a5; + + const T x2 = a2 | a6; + const T x3 = a1 & x2; + const T x4 = x1 ^ x3; + const T x5 = a6 ^ x4; + const T x6 = a5 & ~x5; + + const T x7 = a1 & x5; + const T x8 = a2 ^ x7; + const T x9 = a1 ^ a3; + const T x10 = x8 | x9; + const T x11 = x4 ^ x10; + + const T x12 = a3 & x11; + const T x13 = x12 & ~a6; + const T x14 = x6 | x8; + const T x15 = x13 ^ x14; + const T x16 = x15 & a4; + const T x17 = x16 ^ x11; + out4 ^= x17; + + const T x18 = a2 ^ x10; + const T x19 = a6 & ~x18; + const T x20 = a3 ^ x19; + const T x21 = a5 & ~x12; + const T x22 = x20 | x21; + + const T x23 = a2 | x9; + const T x24 = x15 ^ x23; + const T x25 = x3 | x22; + const T x26 = x24 ^ x25; + + const T x27 = a1 | x11; + const T x28 = x14 & x27; + const T x29 = x20 ^ x28; + const T x30 = x29 & ~x13; + const T x31 = x6 | a4; + const T x32 = x31 ^ x30; + out3 ^= x32; + + const T x33 = x4 ^ x29; + const T x34 = a5 & ~x33; + const T x35 = ~x23; + const T x36 = x18 ^ x35; + const T x37 = x34 ^ x36; + const T x38 = x37 & ~a4; + const T x39 = x38 ^ x26; + out2 ^= x39; + + const T x40 = a6 ^ x7; + const T x41 = a1 ^ x20; + const T x42 = x40 & x41; + const T x43 = x12 ^ x36; + const T x44 = x42 ^ x43; + const T x45 = x22 & ~a4; + const T x46 = x45 ^ x44; + out1 ^= x46; +} + +template +BOTAN_FORCE_INLINE void SBox7(T a1, T a2, T a3, T a4, T a5, T a6, T& out1, T& out2, T& out3, T& out4) { + const T x1 = a4 ^ a5; + const T x2 = a3 ^ x1; + const T x3 = a6 & x2; + const T x4 = a4 & x1; + const T x5 = a2 ^ x4; + const T x6 = x3 & x5; + + const T x7 = a6 & x4; + const T x8 = a3 ^ x7; + const T x9 = x5 | x8; + const T x10 = a6 ^ x1; + const T x11 = x9 ^ x10; + const T x12 = a1 & ~x6; + const T x13 = x12 ^ x11; + out4 ^= x13; + + const T x14 = a5 & ~x2; + const T x15 = x5 | x14; + const T x16 = x3 ^ x8; + const T x17 = x15 ^ x16; + + const T x18 = x3 ^ x10; + const T x19 = a4 & ~x18; + const T x20 = x5 & ~x19; + const T x21 = a5 ^ x16; + const T x22 = x20 ^ x21; + + const T x23 = x18 & ~x7; + const T x24 = x19 | x23; + const T x25 = a2 ^ x9; + const T x26 = x22 & x25; + const T x27 = x24 ^ x26; + const T x28 = x27 & a1; + const T x29 = x28 ^ x22; + out3 ^= x29; + + const T x30 = x5 & ~a3; + const T x31 = x23 | x30; + const T x32 = x4 | x22; + const T x33 = x31 & x32; + const T x34 = x27 ^ x33; + + const T x35 = x17 | x24; + const T x36 = x14 ^ x35; + const T x37 = a6 & x36; + const T x38 = x33 ^ x37; + const T x39 = x38 & ~a1; + const T x40 = x39 ^ x17; + out1 ^= x40; + + const T x41 = ~x37; + const T x42 = a2 | x41; + const T x43 = x17 ^ x33; + const T x44 = x42 ^ x43; + const T x45 = x34 | a1; + const T x46 = x45 ^ x44; + out2 ^= x46; +} + +template +BOTAN_FORCE_INLINE void SBox8(T a1, T a2, T a3, T a4, T a5, T a6, T& out1, T& out2, T& out3, T& out4) { + const T x1 = a3 & ~a2; + const T x2 = a5 & ~a3; + const T x3 = a4 ^ x2; + const T x4 = a1 & x3; + const T x5 = x4 & ~x1; + + const T x6 = a2 & ~x3; + const T x7 = a1 | x6; + const T x8 = a2 & ~a3; + const T x9 = a5 ^ x8; + const T x10 = x7 & x9; + const T x11 = x4 | x10; + + const T x12 = ~x3; + const T x13 = x10 ^ x12; + const T x14 = a3 & ~x7; + const T x15 = x13 ^ x14; + const T x16 = x1 ^ x15; + const T x17 = x5 | a6; + const T x18 = x17 ^ x16; + out2 ^= x18; + + const T x19 = a1 ^ x16; + const T x20 = a5 & x19; + const T x21 = a2 ^ x15; + const T x22 = x20 ^ x21; + const T x23 = x6 ^ x22; + + const T x24 = x11 ^ x22; + const T x25 = a2 | x24; + const T x26 = a5 ^ x19; + const T x27 = x25 ^ x26; + const T x28 = x11 & a6; + const T x29 = x28 ^ x27; + out3 ^= x29; + + const T x30 = x9 ^ x23; + const T x31 = a4 | x21; + const T x32 = x30 ^ x31; + const T x33 = a1 ^ x32; + const T x34 = x33 & a6; + const T x35 = x34 ^ x23; + out4 ^= x35; + + const T x36 = x30 & ~a4; + const T x37 = x27 & x36; + const T x38 = x5 ^ x32; + const T x39 = x37 ^ x38; + const T x40 = x39 | a6; + const T x41 = x40 ^ x23; + out1 ^= x41; +} + +void des_transpose(uint64_t M[32]) { + for(size_t i = 0; i != 16; ++i) { + swap_bits(M[i], M[i + 16], 0x0000FFFF0000FFFF, 16); + } + + for(size_t i = 0; i != 32; i += 16) { + for(size_t j = 0; j != 8; ++j) { + swap_bits(M[i + j], M[i + j + 8], 0x00FF00FF00FF00FF, 8); + } + } + + for(size_t i = 0; i != 32; i += 8) { + for(size_t j = 0; j != 4; ++j) { + swap_bits(M[i + j + 0], M[i + j + 4], 0x0F0F0F0F0F0F0F0F, 4); + } + } + + for(size_t i = 0; i != 32; i += 4) { + for(size_t j = 0; j != 2; ++j) { + swap_bits(M[i + j + 0], M[i + j + 2], 0x3333333333333333, 2); + } + } + + for(size_t i = 0; i != 32; i += 2) { + swap_bits(M[i], M[i + 1], 0x5555555555555555, 1); + } +} + +void transpose_in(uint32_t B[64], const uint8_t in[], size_t n_blocks) { + uint64_t M[32] = {}; + + load_be(M, in, n_blocks); + + des_transpose(M); + + // clang-format off + static constexpr uint8_t IP[64] = { + 57, 49, 41, 33, 25, 17, 9, 1, + 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, + 63, 55, 47, 39, 31, 23, 15, 7, + 56, 48, 40, 32, 24, 16, 8, 0, + 58, 50, 42, 34, 26, 18, 10, 2, + 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6 + }; + // clang-format on + + for(size_t i = 0; i < 64; ++i) { + const uint8_t src = IP[i]; + if(src < 32) { + B[i] = static_cast(M[31 - src] >> 32); + } else { + B[i] = static_cast(M[63 - src]); + } + } +} + +void transpose_out(uint8_t out[], const uint32_t B[64], size_t n_blocks) { + // clang-format off + static constexpr uint8_t FP[64] = { + 39, 7, 47, 15, 55, 23, 63, 31, + 38, 6, 46, 14, 54, 22, 62, 30, + 37, 5, 45, 13, 53, 21, 61, 29, + 36, 4, 44, 12, 52, 20, 60, 28, + 35, 3, 43, 11, 51, 19, 59, 27, + 34, 2, 42, 10, 50, 18, 58, 26, + 33, 1, 41, 9, 49, 17, 57, 25, + 32, 0, 40, 8, 48, 16, 56, 24 + }; + // clang-format on + + uint64_t M[32]; + for(size_t i = 0; i != 32; ++i) { + // XOR with 32 here absorbs the DES output swap into the FP + M[i] = (static_cast(B[FP[31 - i] ^ 32]) << 32) | B[FP[63 - i] ^ 32]; + } + + des_transpose(M); + + for(size_t i = 0; i != n_blocks; ++i) { + store_be(out + i * 8, M[i]); + } +} /* -* DES Key Schedule +* DES round - L ^= P(S(E(R) ^ K)) +* +* Each S-box takes 6 bits from E(R) XORed with 6 round key bits, +* and XORs 4 output bits into L at positions given by the P permutation. +* The E expansion, key XOR, S-box evaluation, and P permutation are +* all fused into the calls below. */ -void des_key_schedule(uint32_t round_key[32], const uint8_t key[8]) { +void des_round(uint32_t L[32], const uint32_t R[32], const uint32_t RK[48]) { + // clang-format off + SBox1(R[31] ^ RK[ 0], R[ 0] ^ RK[ 1], R[ 1] ^ RK[ 2], + R[ 2] ^ RK[ 3], R[ 3] ^ RK[ 4], R[ 4] ^ RK[ 5], + L[ 8], L[16], L[22], L[30]); + + SBox2(R[ 3] ^ RK[ 6], R[ 4] ^ RK[ 7], R[ 5] ^ RK[ 8], + R[ 6] ^ RK[ 9], R[ 7] ^ RK[10], R[ 8] ^ RK[11], + L[12], L[27], L[ 1], L[17]); + + SBox3(R[ 7] ^ RK[12], R[ 8] ^ RK[13], R[ 9] ^ RK[14], + R[10] ^ RK[15], R[11] ^ RK[16], R[12] ^ RK[17], + L[23], L[15], L[29], L[ 5]); + + SBox4(R[11] ^ RK[18], R[12] ^ RK[19], R[13] ^ RK[20], + R[14] ^ RK[21], R[15] ^ RK[22], R[16] ^ RK[23], + L[25], L[19], L[ 9], L[ 0]); + + SBox5(R[15] ^ RK[24], R[16] ^ RK[25], R[17] ^ RK[26], + R[18] ^ RK[27], R[19] ^ RK[28], R[20] ^ RK[29], + L[ 7], L[13], L[24], L[ 2]); + + SBox6(R[19] ^ RK[30], R[20] ^ RK[31], R[21] ^ RK[32], + R[22] ^ RK[33], R[23] ^ RK[34], R[24] ^ RK[35], + L[ 3], L[28], L[10], L[18]); + + SBox7(R[23] ^ RK[36], R[24] ^ RK[37], R[25] ^ RK[38], + R[26] ^ RK[39], R[27] ^ RK[40], R[28] ^ RK[41], + L[31], L[11], L[21], L[ 6]); + + SBox8(R[27] ^ RK[42], R[28] ^ RK[43], R[29] ^ RK[44], + R[30] ^ RK[45], R[31] ^ RK[46], R[ 0] ^ RK[47], + L[ 4], L[26], L[14], L[20]); + // clang-format on +} + +void des_encrypt(uint32_t L[32], uint32_t R[32], const uint32_t round_key[]) { + for(size_t round = 0; round < 16; round += 2) { + des_round(L, R, &round_key[round * 48]); + des_round(R, L, &round_key[(round + 1) * 48]); + } +} + +void des_decrypt(uint32_t L[32], uint32_t R[32], const uint32_t round_key[]) { + for(size_t round = 16; round > 0; round -= 2) { + des_round(L, R, &round_key[(round - 1) * 48]); + des_round(R, L, &round_key[(round - 2) * 48]); + } +} + +/* +* The usual DES key schedule except that each round key is instead of 48 bits, +* is 48 32-bit values which are either all-1 or all-0 +*/ +void des_key_schedule(uint32_t round_key[], const uint8_t key[8]) { static const uint8_t ROT[16] = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1}; uint32_t C = ((key[7] & 0x80) << 20) | ((key[6] & 0x80) << 19) | ((key[5] & 0x80) << 18) | ((key[4] & 0x80) << 17) | @@ -98,147 +671,28 @@ ((key[3] & 0x08) << 4) | ((key[2] & 0x08) << 3) | ((key[1] & 0x08) << 2) | ((key[0] & 0x08) << 1) | ((key[3] & 0x10) >> 1) | ((key[2] & 0x10) >> 2) | ((key[1] & 0x10) >> 3) | ((key[0] & 0x10) >> 4); + static const uint8_t PC2_C[24] = {13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, + 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1}; + + static const uint8_t PC2_D[24] = {12, 23, 2, 8, 18, 26, 1, 11, 22, 16, 4, 19, + 15, 20, 10, 27, 5, 24, 17, 13, 21, 7, 0, 3}; + for(size_t i = 0; i != 16; ++i) { C = ((C << ROT[i]) | (C >> (28 - ROT[i]))) & 0x0FFFFFFF; D = ((D << ROT[i]) | (D >> (28 - ROT[i]))) & 0x0FFFFFFF; - round_key[2 * i] = ((C & 0x00000010) << 22) | ((C & 0x00000800) << 17) | ((C & 0x00000020) << 16) | - ((C & 0x00004004) << 15) | ((C & 0x00000200) << 11) | ((C & 0x00020000) << 10) | - ((C & 0x01000000) >> 6) | ((C & 0x00100000) >> 4) | ((C & 0x00010000) << 3) | - ((C & 0x08000000) >> 2) | ((C & 0x00800000) << 1) | ((D & 0x00000010) << 8) | - ((D & 0x00000002) << 7) | ((D & 0x00000001) << 2) | ((D & 0x00000200)) | - ((D & 0x00008000) >> 2) | ((D & 0x00000088) >> 3) | ((D & 0x00001000) >> 7) | - ((D & 0x00080000) >> 9) | ((D & 0x02020000) >> 14) | ((D & 0x00400000) >> 21); - round_key[2 * i + 1] = - ((C & 0x00000001) << 28) | ((C & 0x00000082) << 18) | ((C & 0x00002000) << 14) | ((C & 0x00000100) << 10) | - ((C & 0x00001000) << 9) | ((C & 0x00040000) << 6) | ((C & 0x02400000) << 4) | ((C & 0x00008000) << 2) | - ((C & 0x00200000) >> 1) | ((C & 0x04000000) >> 10) | ((D & 0x00000020) << 6) | ((D & 0x00000100)) | - ((D & 0x00000800) >> 1) | ((D & 0x00000040) >> 3) | ((D & 0x00010000) >> 4) | ((D & 0x00000400) >> 5) | - ((D & 0x00004000) >> 10) | ((D & 0x04000000) >> 13) | ((D & 0x00800000) >> 14) | ((D & 0x00100000) >> 18) | - ((D & 0x01000000) >> 24) | ((D & 0x08000000) >> 26); - } -} -inline uint32_t spbox(uint32_t T0, uint32_t T1) { - return ((SPBOX_CATS[0 * 64 + ((T0 >> 24) & 0x3F)] * SPBOX_CAT_0_MUL) & SPBOX_CAT_0_MASK) ^ - ((SPBOX_CATS[1 * 64 + ((T1 >> 24) & 0x3F)] * SPBOX_CAT_1_MUL) & SPBOX_CAT_1_MASK) ^ - ((SPBOX_CATS[2 * 64 + ((T0 >> 16) & 0x3F)] * SPBOX_CAT_2_MUL) & SPBOX_CAT_2_MASK) ^ - ((SPBOX_CATS[3 * 64 + ((T1 >> 16) & 0x3F)] * SPBOX_CAT_3_MUL) & SPBOX_CAT_3_MASK) ^ - ((SPBOX_CATS[4 * 64 + ((T0 >> 8) & 0x3F)] * SPBOX_CAT_4_MUL) & SPBOX_CAT_4_MASK) ^ - ((SPBOX_CATS[5 * 64 + ((T1 >> 8) & 0x3F)] * SPBOX_CAT_5_MUL) & SPBOX_CAT_5_MASK) ^ - ((SPBOX_CATS[6 * 64 + ((T0 >> 0) & 0x3F)] * SPBOX_CAT_6_MUL) & SPBOX_CAT_6_MASK) ^ - ((SPBOX_CATS[7 * 64 + ((T1 >> 0) & 0x3F)] * SPBOX_CAT_7_MUL) & SPBOX_CAT_7_MASK); -} + uint32_t* rk = &round_key[i * 48]; -/* -* DES Encryption -*/ -inline void des_encrypt(uint32_t& Lr, uint32_t& Rr, const uint32_t round_key[32]) { - uint32_t L = Lr; - uint32_t R = Rr; - for(size_t i = 0; i != 16; i += 2) { - L ^= spbox(rotr<4>(R) ^ round_key[2 * i], R ^ round_key[2 * i + 1]); - R ^= spbox(rotr<4>(L) ^ round_key[2 * i + 2], L ^ round_key[2 * i + 3]); + for(size_t j = 0; j < 24; ++j) { + const uint32_t bit = (C >> (27 - PC2_C[j])) & 1; + rk[j] = static_cast(0) - bit; + } + + for(size_t j = 0; j < 24; ++j) { + const uint32_t bit = (D >> (27 - PC2_D[j])) & 1; + rk[24 + j] = static_cast(0) - bit; + } } - - Lr = L; - Rr = R; -} - -inline void des_encrypt_x2(uint32_t& L0r, uint32_t& R0r, uint32_t& L1r, uint32_t& R1r, const uint32_t round_key[32]) { - uint32_t L0 = L0r; - uint32_t R0 = R0r; - uint32_t L1 = L1r; - uint32_t R1 = R1r; - - for(size_t i = 0; i != 16; i += 2) { - L0 ^= spbox(rotr<4>(R0) ^ round_key[2 * i], R0 ^ round_key[2 * i + 1]); - L1 ^= spbox(rotr<4>(R1) ^ round_key[2 * i], R1 ^ round_key[2 * i + 1]); - - R0 ^= spbox(rotr<4>(L0) ^ round_key[2 * i + 2], L0 ^ round_key[2 * i + 3]); - R1 ^= spbox(rotr<4>(L1) ^ round_key[2 * i + 2], L1 ^ round_key[2 * i + 3]); - } - - L0r = L0; - R0r = R0; - L1r = L1; - R1r = R1; -} - -/* -* DES Decryption -*/ -inline void des_decrypt(uint32_t& Lr, uint32_t& Rr, const uint32_t round_key[32]) { - uint32_t L = Lr; - uint32_t R = Rr; - for(size_t i = 16; i != 0; i -= 2) { - L ^= spbox(rotr<4>(R) ^ round_key[2 * i - 2], R ^ round_key[2 * i - 1]); - R ^= spbox(rotr<4>(L) ^ round_key[2 * i - 4], L ^ round_key[2 * i - 3]); - } - Lr = L; - Rr = R; -} - -inline void des_decrypt_x2(uint32_t& L0r, uint32_t& R0r, uint32_t& L1r, uint32_t& R1r, const uint32_t round_key[32]) { - uint32_t L0 = L0r; - uint32_t R0 = R0r; - uint32_t L1 = L1r; - uint32_t R1 = R1r; - - for(size_t i = 16; i != 0; i -= 2) { - L0 ^= spbox(rotr<4>(R0) ^ round_key[2 * i - 2], R0 ^ round_key[2 * i - 1]); - L1 ^= spbox(rotr<4>(R1) ^ round_key[2 * i - 2], R1 ^ round_key[2 * i - 1]); - - R0 ^= spbox(rotr<4>(L0) ^ round_key[2 * i - 4], L0 ^ round_key[2 * i - 3]); - R1 ^= spbox(rotr<4>(L1) ^ round_key[2 * i - 4], L1 ^ round_key[2 * i - 3]); - } - - L0r = L0; - R0r = R0; - L1r = L1; - R1r = R1; -} - -inline void des_IP(uint32_t& L, uint32_t& R) { - // IP sequence by Wei Dai, taken from public domain Crypto++ - uint32_t T; - R = rotl<4>(R); - T = (L ^ R) & 0xF0F0F0F0; - L ^= T; - R = rotr<20>(R ^ T); - T = (L ^ R) & 0xFFFF0000; - L ^= T; - R = rotr<18>(R ^ T); - T = (L ^ R) & 0x33333333; - L ^= T; - R = rotr<6>(R ^ T); - T = (L ^ R) & 0x00FF00FF; - L ^= T; - R = rotl<9>(R ^ T); - T = (L ^ R) & 0xAAAAAAAA; - L = rotl<1>(L ^ T); - R ^= T; -} - -inline void des_FP(uint32_t& L, uint32_t& R) { - // FP sequence by Wei Dai, taken from public domain Crypto++ - uint32_t T; - - R = rotr<1>(R); - T = (L ^ R) & 0xAAAAAAAA; - R ^= T; - L = rotr<9>(L ^ T); - T = (L ^ R) & 0x00FF00FF; - R ^= T; - L = rotl<6>(L ^ T); - T = (L ^ R) & 0x33333333; - R ^= T; - L = rotl<18>(L ^ T); - T = (L ^ R) & 0xFFFF0000; - R ^= T; - L = rotl<20>(L ^ T); - T = (L ^ R) & 0xF0F0F0F0; - R ^= T; - L = rotr<4>(L ^ T); } } // namespace @@ -249,38 +703,22 @@ void DES::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); - while(blocks >= 2) { - uint32_t L0 = load_be(in, 0); - uint32_t R0 = load_be(in, 1); - uint32_t L1 = load_be(in, 2); - uint32_t R1 = load_be(in, 3); - - des_IP(L0, R0); - des_IP(L1, R1); - - des_encrypt_x2(L0, R0, L1, R1, m_round_key.data()); - - des_FP(L0, R0); - des_FP(L1, R1); - - store_be(out, R0, L0, R1, L1); - - in += 2 * BLOCK_SIZE; - out += 2 * BLOCK_SIZE; - blocks -= 2; - } - - while(blocks > 0) { - uint32_t L0 = load_be(in, 0); - uint32_t R0 = load_be(in, 1); - des_IP(L0, R0); - des_encrypt(L0, R0, m_round_key.data()); - des_FP(L0, R0); - store_be(out, R0, L0); - - in += BLOCK_SIZE; - out += BLOCK_SIZE; - blocks -= 1; + uint32_t B[64]; + + while(blocks >= 32) { + transpose_in(B, in, 32); + des_encrypt(&B[0], &B[32], m_round_key.data()); + transpose_out(out, B, 32); + + in += 32 * BLOCK_SIZE; + out += 32 * BLOCK_SIZE; + blocks -= 32; + } + + if(blocks > 0) { + transpose_in(B, in, blocks); + des_encrypt(&B[0], &B[32], m_round_key.data()); + transpose_out(out, B, blocks); } } @@ -290,38 +728,22 @@ void DES::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); - while(blocks >= 2) { - uint32_t L0 = load_be(in, 0); - uint32_t R0 = load_be(in, 1); - uint32_t L1 = load_be(in, 2); - uint32_t R1 = load_be(in, 3); - - des_IP(L0, R0); - des_IP(L1, R1); - - des_decrypt_x2(L0, R0, L1, R1, m_round_key.data()); - - des_FP(L0, R0); - des_FP(L1, R1); - - store_be(out, R0, L0, R1, L1); - - in += 2 * BLOCK_SIZE; - out += 2 * BLOCK_SIZE; - blocks -= 2; - } - - while(blocks > 0) { - uint32_t L0 = load_be(in, 0); - uint32_t R0 = load_be(in, 1); - des_IP(L0, R0); - des_decrypt(L0, R0, m_round_key.data()); - des_FP(L0, R0); - store_be(out, R0, L0); - - in += BLOCK_SIZE; - out += BLOCK_SIZE; - blocks -= 1; + uint32_t B[64]; + + while(blocks >= 32) { + transpose_in(B, in, 32); + des_decrypt(&B[0], &B[32], m_round_key.data()); + transpose_out(out, B, 32); + + in += 32 * BLOCK_SIZE; + out += 32 * BLOCK_SIZE; + blocks -= 32; + } + + if(blocks > 0) { + transpose_in(B, in, blocks); + des_decrypt(&B[0], &B[32], m_round_key.data()); + transpose_out(out, B, blocks); } } @@ -333,7 +755,7 @@ * DES Key Schedule */ void DES::key_schedule(std::span key) { - m_round_key.resize(32); + m_round_key.resize(16 * 48); des_key_schedule(m_round_key.data(), key.data()); } @@ -347,44 +769,30 @@ void TripleDES::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); - while(blocks >= 2) { - uint32_t L0 = load_be(in, 0); - uint32_t R0 = load_be(in, 1); - uint32_t L1 = load_be(in, 2); - uint32_t R1 = load_be(in, 3); - - des_IP(L0, R0); - des_IP(L1, R1); - - des_encrypt_x2(L0, R0, L1, R1, &m_round_key[0]); - des_decrypt_x2(R0, L0, R1, L1, &m_round_key[32]); - des_encrypt_x2(L0, R0, L1, R1, &m_round_key[64]); - - des_FP(L0, R0); - des_FP(L1, R1); - - store_be(out, R0, L0, R1, L1); - - in += 2 * BLOCK_SIZE; - out += 2 * BLOCK_SIZE; - blocks -= 2; - } - - while(blocks > 0) { - uint32_t L0 = load_be(in, 0); - uint32_t R0 = load_be(in, 1); - - des_IP(L0, R0); - des_encrypt(L0, R0, &m_round_key[0]); - des_decrypt(R0, L0, &m_round_key[32]); - des_encrypt(L0, R0, &m_round_key[64]); - des_FP(L0, R0); - - store_be(out, R0, L0); - - in += BLOCK_SIZE; - out += BLOCK_SIZE; - blocks -= 1; + const uint32_t* k1 = m_round_key.data(); + const uint32_t* k2 = k1 + 16 * 48; + const uint32_t* k3 = k2 + 16 * 48; + + uint32_t B[64]; + + while(blocks >= 32) { + transpose_in(B, in, 32); + des_encrypt(&B[0], &B[32], k1); + des_decrypt(&B[32], &B[0], k2); + des_encrypt(&B[0], &B[32], k3); + transpose_out(out, B, 32); + + in += 32 * BLOCK_SIZE; + out += 32 * BLOCK_SIZE; + blocks -= 32; + } + + if(blocks > 0) { + transpose_in(B, in, blocks); + des_encrypt(&B[0], &B[32], k1); + des_decrypt(&B[32], &B[0], k2); + des_encrypt(&B[0], &B[32], k3); + transpose_out(out, B, blocks); } } @@ -394,44 +802,30 @@ void TripleDES::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); - while(blocks >= 2) { - uint32_t L0 = load_be(in, 0); - uint32_t R0 = load_be(in, 1); - uint32_t L1 = load_be(in, 2); - uint32_t R1 = load_be(in, 3); - - des_IP(L0, R0); - des_IP(L1, R1); - - des_decrypt_x2(L0, R0, L1, R1, &m_round_key[64]); - des_encrypt_x2(R0, L0, R1, L1, &m_round_key[32]); - des_decrypt_x2(L0, R0, L1, R1, &m_round_key[0]); - - des_FP(L0, R0); - des_FP(L1, R1); - - store_be(out, R0, L0, R1, L1); - - in += 2 * BLOCK_SIZE; - out += 2 * BLOCK_SIZE; - blocks -= 2; - } - - while(blocks > 0) { - uint32_t L0 = load_be(in, 0); - uint32_t R0 = load_be(in, 1); - - des_IP(L0, R0); - des_decrypt(L0, R0, &m_round_key[64]); - des_encrypt(R0, L0, &m_round_key[32]); - des_decrypt(L0, R0, &m_round_key[0]); - des_FP(L0, R0); - - store_be(out, R0, L0); - - in += BLOCK_SIZE; - out += BLOCK_SIZE; - blocks -= 1; + const uint32_t* k1 = m_round_key.data(); + const uint32_t* k2 = k1 + 16 * 48; + const uint32_t* k3 = k2 + 16 * 48; + + uint32_t B[64]; + + while(blocks >= 32) { + transpose_in(B, in, 32); + des_decrypt(&B[0], &B[32], k3); + des_encrypt(&B[32], &B[0], k2); + des_decrypt(&B[0], &B[32], k1); + transpose_out(out, B, 32); + + in += 32 * BLOCK_SIZE; + out += 32 * BLOCK_SIZE; + blocks -= 32; + } + + if(blocks > 0) { + transpose_in(B, in, blocks); + des_decrypt(&B[0], &B[32], k3); + des_encrypt(&B[32], &B[0], k2); + des_decrypt(&B[0], &B[32], k1); + transpose_out(out, B, blocks); } } @@ -443,14 +837,14 @@ * TripleDES Key Schedule */ void TripleDES::key_schedule(std::span key) { - m_round_key.resize(3 * 32); - des_key_schedule(&m_round_key[0], key.first(8).data()); - des_key_schedule(&m_round_key[32], key.subspan(8, 8).data()); + m_round_key.resize(3 * 16 * 48); + des_key_schedule(m_round_key.data(), key.first(8).data()); + des_key_schedule(m_round_key.data() + 16 * 48, key.subspan(8, 8).data()); if(key.size() == 24) { - des_key_schedule(&m_round_key[64], key.last(8).data()); + des_key_schedule(m_round_key.data() + 2 * 16 * 48, key.last(8).data()); } else { - copy_mem(&m_round_key[64], &m_round_key[0], 32); + copy_mem(m_round_key.data() + 2 * 16 * 48, m_round_key.data(), 16 * 48); } } diff -Nru botan3-3.7.1+dfsg/src/lib/block/des/des.h botan3-3.12.0+dfsg/src/lib/block/des/des.h --- botan3-3.7.1+dfsg/src/lib/block/des/des.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/des/des.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #define BOTAN_DES_H_ #include +#include namespace Botan { @@ -26,10 +27,12 @@ std::unique_ptr new_object() const override { return std::make_unique(); } + size_t parallelism() const override { return 32; } + bool has_keying_material() const override; private: - void key_schedule(std::span) override; + void key_schedule(std::span key) override; secure_vector m_round_key; }; @@ -48,10 +51,12 @@ std::unique_ptr new_object() const override { return std::make_unique(); } + size_t parallelism() const override { return 32; } + bool has_keying_material() const override; private: - void key_schedule(std::span) override; + void key_schedule(std::span key) override; secure_vector m_round_key; }; diff -Nru botan3-3.7.1+dfsg/src/lib/block/gost_28147/gost_28147.cpp botan3-3.12.0+dfsg/src/lib/block/gost_28147/gost_28147.cpp --- botan3-3.7.1+dfsg/src/lib/block/gost_28147/gost_28147.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/gost_28147/gost_28147.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -26,7 +26,7 @@ } GOST_28147_89_Params::GOST_28147_89_Params(std::string_view n) : m_name(n) { - // Encoded in the packed fromat from RFC 4357 + // Encoded in the packed format from RFC 4357 // GostR3411_94_TestParamSet (OID 1.2.643.2.2.31.0) static const uint8_t GOST_R_3411_TEST_PARAMS[64] = { diff -Nru botan3-3.7.1+dfsg/src/lib/block/gost_28147/gost_28147.h botan3-3.12.0+dfsg/src/lib/block/gost_28147/gost_28147.h --- botan3-3.7.1+dfsg/src/lib/block/gost_28147/gost_28147.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/gost_28147/gost_28147.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #define BOTAN_GOST_28147_89_H_ #include +#include namespace Botan { diff -Nru botan3-3.7.1+dfsg/src/lib/block/idea/idea.cpp botan3-3.12.0+dfsg/src/lib/block/idea/idea.cpp --- botan3-3.7.1+dfsg/src/lib/block/idea/idea.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/idea/idea.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,10 +7,13 @@ #include -#include #include #include +#if defined(BOTAN_HAS_CPUID) + #include +#endif + namespace Botan { namespace { @@ -19,17 +22,14 @@ * Multiplication modulo 65537 */ inline uint16_t mul(uint16_t x, uint16_t y) { - const uint32_t P = static_cast(x) * y; - const auto P_mask = CT::Mask(CT::Mask::is_zero(P)); - - const uint32_t P_hi = P >> 16; - const uint32_t P_lo = P & 0xFFFF; + uint32_t P = static_cast(x) * y; + const uint16_t P_is_zero = static_cast(ct_is_zero(P)); - const uint16_t carry = (P_lo < P_hi); - const uint16_t r_1 = static_cast((P_lo - P_hi) + carry); - const uint16_t r_2 = 1 - x - y; + P = (P & 0xFFFF) - (P >> 16); + const uint16_t R1 = static_cast(P - (P >> 16)); + const uint16_t R0 = 1 - x - y; - return P_mask.select(r_2, r_1); + return choose(P_is_zero, R0, R1); } /* @@ -65,7 +65,10 @@ CT::poison(K, 52); for(size_t i = 0; i < blocks; ++i) { - uint16_t X1, X2, X3, X4; + uint16_t X1 = 0; + uint16_t X2 = 0; + uint16_t X3 = 0; + uint16_t X4 = 0; load_be(in + BLOCK_SIZE * i, X1, X2, X3, X4); for(size_t j = 0; j != 8; ++j) { @@ -103,8 +106,14 @@ } // namespace size_t IDEA::parallelism() const { +#if defined(BOTAN_HAS_IDEA_AVX2) + if(CPUID::has(CPUID::Feature::AVX2)) { + return 16; + } +#endif + #if defined(BOTAN_HAS_IDEA_SSE2) - if(CPUID::has_sse2()) { + if(CPUID::has(CPUID::Feature::SSE2)) { return 8; } #endif @@ -113,9 +122,15 @@ } std::string IDEA::provider() const { +#if defined(BOTAN_HAS_IDEA_AVX2) + if(auto feat = CPUID::check(CPUID::Feature::AVX2)) { + return *feat; + } +#endif + #if defined(BOTAN_HAS_IDEA_SSE2) - if(CPUID::has_sse2()) { - return "sse2"; + if(auto feat = CPUID::check(CPUID::Feature::SSE2)) { + return *feat; } #endif @@ -128,8 +143,19 @@ void IDEA::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); +#if defined(BOTAN_HAS_IDEA_AVX2) + if(CPUID::has(CPUID::Feature::AVX2)) { + while(blocks >= 16) { + avx2_idea_op_16(in, out, m_EK.data()); + in += 16 * BLOCK_SIZE; + out += 16 * BLOCK_SIZE; + blocks -= 16; + } + } +#endif + #if defined(BOTAN_HAS_IDEA_SSE2) - if(CPUID::has_sse2()) { + if(CPUID::has(CPUID::Feature::SSE2)) { while(blocks >= 8) { sse2_idea_op_8(in, out, m_EK.data()); in += 8 * BLOCK_SIZE; @@ -148,8 +174,19 @@ void IDEA::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); +#if defined(BOTAN_HAS_IDEA_AVX2) + if(CPUID::has(CPUID::Feature::AVX2)) { + while(blocks >= 16) { + avx2_idea_op_16(in, out, m_DK.data()); + in += 16 * BLOCK_SIZE; + out += 16 * BLOCK_SIZE; + blocks -= 16; + } + } +#endif + #if defined(BOTAN_HAS_IDEA_SSE2) - if(CPUID::has_sse2()) { + if(CPUID::has(CPUID::Feature::SSE2)) { while(blocks >= 8) { sse2_idea_op_8(in, out, m_DK.data()); in += 8 * BLOCK_SIZE; diff -Nru botan3-3.7.1+dfsg/src/lib/block/idea/idea.h botan3-3.12.0+dfsg/src/lib/block/idea/idea.h --- botan3-3.7.1+dfsg/src/lib/block/idea/idea.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/idea/idea.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #define BOTAN_IDEA_H_ #include +#include namespace Botan { @@ -32,6 +33,10 @@ bool has_keying_material() const override; private: +#if defined(BOTAN_HAS_IDEA_AVX2) + static void avx2_idea_op_16(const uint8_t in[128], uint8_t out[128], const uint16_t EK[52]); +#endif + #if defined(BOTAN_HAS_IDEA_SSE2) static void sse2_idea_op_8(const uint8_t in[64], uint8_t out[64], const uint16_t EK[52]); #endif diff -Nru botan3-3.7.1+dfsg/src/lib/block/idea/idea_avx2/idea_avx2.cpp botan3-3.12.0+dfsg/src/lib/block/idea/idea_avx2/idea_avx2.cpp --- botan3-3.7.1+dfsg/src/lib/block/idea/idea_avx2/idea_avx2.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/idea/idea_avx2/idea_avx2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,219 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include + +namespace Botan { + +namespace { + +// NOLINTBEGIN(portability-simd-intrinsics) + +/* +* SIMD type of 16 16-bit elements +*/ +class SIMD_16x16 final { + public: + using native_type = __m256i; + + SIMD_16x16(const SIMD_16x16&) = default; + SIMD_16x16& operator=(const SIMD_16x16&) = default; + SIMD_16x16(SIMD_16x16&&) = default; + SIMD_16x16& operator=(SIMD_16x16&&) = default; + ~SIMD_16x16() = default; + + BOTAN_FN_ISA_AVX2 explicit SIMD_16x16(native_type x) : m_simd(x) {} + + static SIMD_16x16 BOTAN_FN_ISA_AVX2 load_le(const uint8_t in[]) { + return SIMD_16x16(_mm256_loadu_si256(reinterpret_cast(in))); + } + + void BOTAN_FN_ISA_AVX2 store_le(uint8_t out[]) const { + _mm256_storeu_si256(reinterpret_cast<__m256i*>(out), m_simd); + } + + static SIMD_16x16 BOTAN_FN_ISA_AVX2 load_be(const uint8_t in[]) { return load_le(in).bswap(); } + + void BOTAN_FN_ISA_AVX2 store_be(uint8_t out[]) const { bswap().store_le(out); } + + SIMD_16x16 BOTAN_FN_ISA_AVX2 bswap() const { + // clang-format off + const auto bswap_tbl = _mm256_set_epi8( + 14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1, + 14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1); + // clang-format on + return SIMD_16x16(_mm256_shuffle_epi8(m_simd, bswap_tbl)); + } + + SIMD_16x16 BOTAN_FN_ISA_AVX2 operator-(const SIMD_16x16& o) const { + return SIMD_16x16(_mm256_sub_epi16(m_simd, o.m_simd)); + } + + SIMD_16x16 BOTAN_FN_ISA_AVX2 operator^(const SIMD_16x16& o) const { + return SIMD_16x16(_mm256_xor_si256(m_simd, o.m_simd)); + } + + void BOTAN_FN_ISA_AVX2 operator+=(const SIMD_16x16& o) { m_simd = _mm256_add_epi16(m_simd, o.m_simd); } + + void BOTAN_FN_ISA_AVX2 operator+=(uint16_t v) { m_simd = _mm256_add_epi16(m_simd, _mm256_set1_epi16(v)); } + + void BOTAN_FN_ISA_AVX2 operator^=(const SIMD_16x16& o) { m_simd = _mm256_xor_si256(m_simd, o.m_simd); } + + static inline BOTAN_FN_ISA_AVX2 SIMD_16x16 mul_mod_65537(SIMD_16x16 X, uint16_t K_16) { + const auto zeros = SIMD_16x16::splat(0); + const auto ones = SIMD_16x16::splat(1); + const auto K = SIMD_16x16::splat(K_16); + + // If X == 0 or K == 0 then P == X * K == 0 + const auto P_is_zero = SIMD_16x16( + _mm256_or_si256(_mm256_cmpeq_epi16(X.raw(), zeros.raw()), _mm256_cmpeq_epi16(K.raw(), zeros.raw()))); + + // Return value if P == 0: 1 - X - K + const auto R0 = ones - X - K; + + const auto mul_lo = SIMD_16x16(_mm256_mullo_epi16(X.raw(), K.raw())); + const auto mul_hi = SIMD_16x16(_mm256_mulhi_epu16(X.raw(), K.raw())); + + // AVX2 doesn't have unsigned comparisons so emulate with a signed compare by flipping the sign bit + const auto sign_bit = SIMD_16x16::splat(0x8000); + const auto borrow = SIMD_16x16(_mm256_cmpgt_epi16((mul_hi ^ sign_bit).raw(), (mul_lo ^ sign_bit).raw())); + + // R1 = mul_lo - mul_hi + (mul_hi > mul_lo ? 1 : 0) + const auto R1 = mul_lo - mul_hi - borrow; + + return SIMD_16x16(_mm256_blendv_epi8(R1.raw(), R0.raw(), P_is_zero.raw())); + } + + /* + * 4x16 matrix transpose + */ + static void BOTAN_FN_ISA_AVX2 transpose_in(SIMD_16x16& B0, SIMD_16x16& B1, SIMD_16x16& B2, SIMD_16x16& B3) { + auto B0r = _mm256_shuffle_epi32(B0.raw(), _MM_SHUFFLE(3, 1, 2, 0)); + auto B1r = _mm256_shuffle_epi32(B1.raw(), _MM_SHUFFLE(3, 1, 2, 0)); + auto B2r = _mm256_shuffle_epi32(B2.raw(), _MM_SHUFFLE(3, 1, 2, 0)); + auto B3r = _mm256_shuffle_epi32(B3.raw(), _MM_SHUFFLE(3, 1, 2, 0)); + + B0r = _mm256_shufflelo_epi16(B0r, _MM_SHUFFLE(3, 1, 2, 0)); + B1r = _mm256_shufflelo_epi16(B1r, _MM_SHUFFLE(3, 1, 2, 0)); + B2r = _mm256_shufflelo_epi16(B2r, _MM_SHUFFLE(3, 1, 2, 0)); + B3r = _mm256_shufflelo_epi16(B3r, _MM_SHUFFLE(3, 1, 2, 0)); + + B0r = _mm256_shufflehi_epi16(B0r, _MM_SHUFFLE(3, 1, 2, 0)); + B1r = _mm256_shufflehi_epi16(B1r, _MM_SHUFFLE(3, 1, 2, 0)); + B2r = _mm256_shufflehi_epi16(B2r, _MM_SHUFFLE(3, 1, 2, 0)); + B3r = _mm256_shufflehi_epi16(B3r, _MM_SHUFFLE(3, 1, 2, 0)); + + const auto T0 = _mm256_unpacklo_epi32(B0r, B1r); + const auto T1 = _mm256_unpackhi_epi32(B0r, B1r); + const auto T2 = _mm256_unpacklo_epi32(B2r, B3r); + const auto T3 = _mm256_unpackhi_epi32(B2r, B3r); + + B0 = SIMD_16x16(_mm256_unpacklo_epi64(T0, T2)); + B1 = SIMD_16x16(_mm256_unpackhi_epi64(T0, T2)); + B2 = SIMD_16x16(_mm256_unpacklo_epi64(T1, T3)); + B3 = SIMD_16x16(_mm256_unpackhi_epi64(T1, T3)); + } + + /* + * 4x16 matrix transpose (inverse) + */ + static void BOTAN_FN_ISA_AVX2 transpose_out(SIMD_16x16& B0, SIMD_16x16& B1, SIMD_16x16& B2, SIMD_16x16& B3) { + auto T0 = _mm256_unpacklo_epi64(B0.raw(), B1.raw()); + auto T1 = _mm256_unpacklo_epi64(B2.raw(), B3.raw()); + auto T2 = _mm256_unpackhi_epi64(B0.raw(), B1.raw()); + auto T3 = _mm256_unpackhi_epi64(B2.raw(), B3.raw()); + + T0 = _mm256_shuffle_epi32(T0, _MM_SHUFFLE(3, 1, 2, 0)); + T1 = _mm256_shuffle_epi32(T1, _MM_SHUFFLE(3, 1, 2, 0)); + T2 = _mm256_shuffle_epi32(T2, _MM_SHUFFLE(3, 1, 2, 0)); + T3 = _mm256_shuffle_epi32(T3, _MM_SHUFFLE(3, 1, 2, 0)); + + T0 = _mm256_shufflehi_epi16(T0, _MM_SHUFFLE(3, 1, 2, 0)); + T1 = _mm256_shufflehi_epi16(T1, _MM_SHUFFLE(3, 1, 2, 0)); + T2 = _mm256_shufflehi_epi16(T2, _MM_SHUFFLE(3, 1, 2, 0)); + T3 = _mm256_shufflehi_epi16(T3, _MM_SHUFFLE(3, 1, 2, 0)); + + T0 = _mm256_shufflelo_epi16(T0, _MM_SHUFFLE(3, 1, 2, 0)); + T1 = _mm256_shufflelo_epi16(T1, _MM_SHUFFLE(3, 1, 2, 0)); + T2 = _mm256_shufflelo_epi16(T2, _MM_SHUFFLE(3, 1, 2, 0)); + T3 = _mm256_shufflelo_epi16(T3, _MM_SHUFFLE(3, 1, 2, 0)); + + B0 = SIMD_16x16(_mm256_unpacklo_epi32(T0, T1)); + B1 = SIMD_16x16(_mm256_unpackhi_epi32(T0, T1)); + B2 = SIMD_16x16(_mm256_unpacklo_epi32(T2, T3)); + B3 = SIMD_16x16(_mm256_unpackhi_epi32(T2, T3)); + } + + native_type BOTAN_FN_ISA_AVX2 raw() const { return m_simd; } + + private: + static SIMD_16x16 BOTAN_FN_ISA_AVX2 splat(uint16_t v) { return SIMD_16x16(_mm256_set1_epi16(v)); } + + native_type m_simd; +}; + +// NOLINTEND(portability-simd-intrinsics) + +} // namespace + +BOTAN_FN_ISA_AVX2 void IDEA::avx2_idea_op_16(const uint8_t in[128], uint8_t out[128], const uint16_t EK[52]) { + CT::poison(in, 128); + CT::poison(out, 128); + CT::poison(EK, 52); + + auto B0 = SIMD_16x16::load_be(in + 0); + auto B1 = SIMD_16x16::load_be(in + 32); + auto B2 = SIMD_16x16::load_be(in + 64); + auto B3 = SIMD_16x16::load_be(in + 96); + + SIMD_16x16::transpose_in(B0, B1, B2, B3); + + for(size_t i = 0; i != 8; ++i) { + B0 = SIMD_16x16::mul_mod_65537(B0, EK[6 * i + 0]); + B1 += EK[6 * i + 1]; + B2 += EK[6 * i + 2]; + B3 = SIMD_16x16::mul_mod_65537(B3, EK[6 * i + 3]); + + const auto T0 = B2; + B2 ^= B0; + B2 = SIMD_16x16::mul_mod_65537(B2, EK[6 * i + 4]); + + const auto T1 = B1; + + B1 ^= B3; + B1 += B2; + B1 = SIMD_16x16::mul_mod_65537(B1, EK[6 * i + 5]); + + B2 += B1; + + B0 ^= B1; + B1 ^= T0; + B3 ^= B2; + B2 ^= T1; + } + + B0 = SIMD_16x16::mul_mod_65537(B0, EK[48]); + B1 += EK[50]; + B2 += EK[49]; + B3 = SIMD_16x16::mul_mod_65537(B3, EK[51]); + + SIMD_16x16::transpose_out(B0, B2, B1, B3); + + B0.store_be(out + 0); + B2.store_be(out + 32); + B1.store_be(out + 64); + B3.store_be(out + 96); + + CT::unpoison(in, 128); + CT::unpoison(out, 128); + CT::unpoison(EK, 52); +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/idea/idea_avx2/info.txt botan3-3.12.0+dfsg/src/lib/block/idea/idea_avx2/info.txt --- botan3-3.7.1+dfsg/src/lib/block/idea/idea_avx2/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/idea/idea_avx2/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,16 @@ + +IDEA_AVX2 -> 20260314 + + + +name -> "IDEA AVX2" +brief -> "IDEA using AVX2 SIMD instructions" + + + +avx2 + + + +cpuid + diff -Nru botan3-3.7.1+dfsg/src/lib/block/idea/idea_sse2/idea_sse2.cpp botan3-3.12.0+dfsg/src/lib/block/idea/idea_sse2/idea_sse2.cpp --- botan3-3.7.1+dfsg/src/lib/block/idea/idea_sse2/idea_sse2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/idea/idea_sse2/idea_sse2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,79 +8,65 @@ #include #include +#include #include namespace Botan { +// NOLINTBEGIN(portability-simd-intrinsics) TODO add various helper fns + namespace { -BOTAN_FUNC_ISA("sse2") inline __m128i mul(__m128i X, uint16_t K_16) { +BOTAN_FN_ISA_SSE2 inline __m128i mul(__m128i X, uint16_t K_16) { const __m128i zeros = _mm_set1_epi16(0); const __m128i ones = _mm_set1_epi16(1); - const __m128i K = _mm_set1_epi16(K_16); - const __m128i X_is_zero = _mm_cmpeq_epi16(X, zeros); - const __m128i K_is_zero = _mm_cmpeq_epi16(K, zeros); + // If X == 0 or K == 0 then P == X * K == 0 + const __m128i P_is_zero = _mm_or_si128(_mm_cmpeq_epi16(X, zeros), _mm_cmpeq_epi16(K, zeros)); + + // Return value if P == 0: 1 - X - K + const __m128i R0 = _mm_sub_epi16(_mm_sub_epi16(ones, X), K); const __m128i mul_lo = _mm_mullo_epi16(X, K); const __m128i mul_hi = _mm_mulhi_epu16(X, K); - __m128i T = _mm_sub_epi16(mul_lo, mul_hi); - - // Unsigned compare; cmp = 1 if mul_lo < mul_hi else 0 - const __m128i subs = _mm_subs_epu16(mul_hi, mul_lo); - const __m128i cmp = _mm_min_epu8(_mm_or_si128(subs, _mm_srli_epi16(subs, 8)), ones); - - T = _mm_add_epi16(T, cmp); - - /* Selection: if X[i] is zero then assign 1-K - if K is zero then assign 1-X[i] - - Could if() off value of K_16 for the second, but this gives a - constant time implementation which is a nice bonus. - */ + __m128i R1 = _mm_sub_epi16(mul_lo, mul_hi); - T = _mm_or_si128(_mm_andnot_si128(X_is_zero, T), _mm_and_si128(_mm_sub_epi16(ones, K), X_is_zero)); + // SSE doesn't have unsigned comparisons so emulate with a signed compare by flipping the sign bit + const __m128i sign_bit = _mm_set1_epi16(static_cast(0x8000)); + const __m128i borrow = _mm_cmpgt_epi16(_mm_xor_si128(mul_hi, sign_bit), _mm_xor_si128(mul_lo, sign_bit)); - T = _mm_or_si128(_mm_andnot_si128(K_is_zero, T), _mm_and_si128(_mm_sub_epi16(ones, X), K_is_zero)); + // R1 = mul_lo - mul_hi + (mul_hi > mul_lo ? 1 : 0) + R1 = _mm_sub_epi16(R1, borrow); - return T; + // Return either R1 or R0 (1-X-K) depending on if P == 0 or not + return _mm_or_si128(_mm_andnot_si128(P_is_zero, R1), _mm_and_si128(P_is_zero, R0)); } /* * 4x8 matrix transpose -* -* FIXME: why do I need the extra set of unpack_epi32 here? Inverse in -* transpose_out doesn't need it. Something with the shuffle? Removing -* that extra unpack could easily save 3-4 cycles per block, and would -* also help a lot with register pressure on 32-bit x86 */ -BOTAN_FUNC_ISA("sse2") void transpose_in(__m128i& B0, __m128i& B1, __m128i& B2, __m128i& B3) { - __m128i T0 = _mm_unpackhi_epi32(B0, B1); - __m128i T1 = _mm_unpacklo_epi32(B0, B1); - __m128i T2 = _mm_unpackhi_epi32(B2, B3); - __m128i T3 = _mm_unpacklo_epi32(B2, B3); - - __m128i T4 = _mm_unpacklo_epi32(T0, T1); - __m128i T5 = _mm_unpackhi_epi32(T0, T1); - __m128i T6 = _mm_unpacklo_epi32(T2, T3); - __m128i T7 = _mm_unpackhi_epi32(T2, T3); - - T0 = _mm_shufflehi_epi16(T4, _MM_SHUFFLE(1, 3, 0, 2)); - T1 = _mm_shufflehi_epi16(T5, _MM_SHUFFLE(1, 3, 0, 2)); - T2 = _mm_shufflehi_epi16(T6, _MM_SHUFFLE(1, 3, 0, 2)); - T3 = _mm_shufflehi_epi16(T7, _MM_SHUFFLE(1, 3, 0, 2)); - - T0 = _mm_shufflelo_epi16(T0, _MM_SHUFFLE(1, 3, 0, 2)); - T1 = _mm_shufflelo_epi16(T1, _MM_SHUFFLE(1, 3, 0, 2)); - T2 = _mm_shufflelo_epi16(T2, _MM_SHUFFLE(1, 3, 0, 2)); - T3 = _mm_shufflelo_epi16(T3, _MM_SHUFFLE(1, 3, 0, 2)); - - T0 = _mm_shuffle_epi32(T0, _MM_SHUFFLE(3, 1, 2, 0)); - T1 = _mm_shuffle_epi32(T1, _MM_SHUFFLE(3, 1, 2, 0)); - T2 = _mm_shuffle_epi32(T2, _MM_SHUFFLE(3, 1, 2, 0)); - T3 = _mm_shuffle_epi32(T3, _MM_SHUFFLE(3, 1, 2, 0)); +BOTAN_FN_ISA_SSE2 void transpose_in(__m128i& B0, __m128i& B1, __m128i& B2, __m128i& B3) { + B0 = _mm_shuffle_epi32(B0, _MM_SHUFFLE(3, 1, 2, 0)); + B1 = _mm_shuffle_epi32(B1, _MM_SHUFFLE(3, 1, 2, 0)); + B2 = _mm_shuffle_epi32(B2, _MM_SHUFFLE(3, 1, 2, 0)); + B3 = _mm_shuffle_epi32(B3, _MM_SHUFFLE(3, 1, 2, 0)); + + B0 = _mm_shufflelo_epi16(B0, _MM_SHUFFLE(3, 1, 2, 0)); + B1 = _mm_shufflelo_epi16(B1, _MM_SHUFFLE(3, 1, 2, 0)); + B2 = _mm_shufflelo_epi16(B2, _MM_SHUFFLE(3, 1, 2, 0)); + B3 = _mm_shufflelo_epi16(B3, _MM_SHUFFLE(3, 1, 2, 0)); + + B0 = _mm_shufflehi_epi16(B0, _MM_SHUFFLE(3, 1, 2, 0)); + B1 = _mm_shufflehi_epi16(B1, _MM_SHUFFLE(3, 1, 2, 0)); + B2 = _mm_shufflehi_epi16(B2, _MM_SHUFFLE(3, 1, 2, 0)); + B3 = _mm_shufflehi_epi16(B3, _MM_SHUFFLE(3, 1, 2, 0)); + + const __m128i T0 = _mm_unpacklo_epi32(B0, B1); + const __m128i T1 = _mm_unpackhi_epi32(B0, B1); + const __m128i T2 = _mm_unpacklo_epi32(B2, B3); + const __m128i T3 = _mm_unpackhi_epi32(B2, B3); B0 = _mm_unpacklo_epi64(T0, T2); B1 = _mm_unpackhi_epi64(T0, T2); @@ -91,7 +77,7 @@ /* * 4x8 matrix transpose (reverse) */ -BOTAN_FUNC_ISA("sse2") void transpose_out(__m128i& B0, __m128i& B1, __m128i& B2, __m128i& B3) { +BOTAN_FN_ISA_SSE2 void transpose_out(__m128i& B0, __m128i& B1, __m128i& B2, __m128i& B3) { __m128i T0 = _mm_unpacklo_epi64(B0, B1); __m128i T1 = _mm_unpacklo_epi64(B2, B3); __m128i T2 = _mm_unpackhi_epi64(B0, B1); @@ -123,7 +109,7 @@ /* * 8 wide IDEA encryption/decryption in SSE2 */ -BOTAN_FUNC_ISA("sse2") void IDEA::sse2_idea_op_8(const uint8_t in[64], uint8_t out[64], const uint16_t EK[52]) { +BOTAN_FN_ISA_SSE2 void IDEA::sse2_idea_op_8(const uint8_t in[64], uint8_t out[64], const uint16_t EK[52]) { CT::poison(in, 64); CT::poison(out, 64); CT::poison(EK, 52); @@ -149,11 +135,11 @@ B2 = _mm_add_epi16(B2, _mm_set1_epi16(EK[6 * i + 2])); B3 = mul(B3, EK[6 * i + 3]); - __m128i T0 = B2; + const __m128i T0 = B2; B2 = _mm_xor_si128(B2, B0); B2 = mul(B2, EK[6 * i + 4]); - __m128i T1 = B1; + const __m128i T1 = B1; B1 = _mm_xor_si128(B1, B3); B1 = _mm_add_epi16(B1, B2); @@ -192,4 +178,6 @@ CT::unpoison(EK, 52); } +// NOLINTEND(portability-simd-intrinsics) + } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/idea/idea_sse2/info.txt botan3-3.12.0+dfsg/src/lib/block/idea/idea_sse2/info.txt --- botan3-3.7.1+dfsg/src/lib/block/idea/idea_sse2/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/idea/idea_sse2/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + IDEA_SSE2 -> 20131128 - + name -> "IDEA SSE2" @@ -10,3 +10,7 @@ sse2 + + +cpuid + diff -Nru botan3-3.7.1+dfsg/src/lib/block/kuznyechik/kuznyechik.cpp botan3-3.12.0+dfsg/src/lib/block/kuznyechik/kuznyechik.cpp --- botan3-3.7.1+dfsg/src/lib/block/kuznyechik/kuznyechik.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/kuznyechik/kuznyechik.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,7 +11,7 @@ #include -#include +#include #include namespace Botan { @@ -53,37 +53,7 @@ const constexpr uint8_t LINEAR[16] = { 0x94, 0x20, 0x85, 0x10, 0xC2, 0xC0, 0x01, 0xFB, 0x01, 0xC0, 0xC2, 0x10, 0x85, 0x20, 0x94, 0x01}; -constexpr uint8_t poly_mul(uint8_t x, uint8_t y) { - const uint8_t poly = 0xC3; - - uint8_t r = 0; - while(x > 0 && y > 0) { - if(y & 1) { - r ^= x; - } - x = (x << 1) ^ ((x >> 7) * poly); - y >>= 1; - } - return r; -} - -constexpr uint64_t poly_mul(uint64_t x, uint8_t y) { - const uint64_t lo_bit = 0x0101010101010101; - const uint64_t mask = 0x7F7F7F7F7F7F7F7F; - const uint64_t poly = 0xC3; - - uint64_t r = 0; - while(x > 0 && y > 0) { - if(y & 1) { - r ^= x; - } - x = ((x & mask) << 1) ^ (((x >> 7) & lo_bit) * poly); - y >>= 1; - } - return r; -} - -consteval std::array T_table(bool forward) { +consteval std::array L_table(bool forward) noexcept { std::array L = {}; for(size_t i = 0; i != 16; ++i) { @@ -94,7 +64,10 @@ } if(!forward) { - std::reverse(L.begin(), L.end()); + // Reverse L + for(size_t i = 0; i != 128; ++i) { + std::swap(L[i], L[255 - i]); + } } auto sqr_matrix = [](std::span mat) { @@ -102,7 +75,7 @@ for(size_t i = 0; i != 16; ++i) { for(size_t j = 0; j != 16; ++j) { for(size_t k = 0; k != 16; ++k) { - res[16 * i + j] ^= poly_mul(mat[16 * i + k], mat[16 * k + j]); + res[16 * i + j] ^= poly_mul<0xC3>(mat[16 * i + k], mat[16 * k + j]); } } } @@ -113,8 +86,11 @@ L = sqr_matrix(L); } - const auto SB = forward ? S : IS; + return L; +} +consteval std::array T_table(std::span L, + std::span SB) noexcept { std::array T = {}; for(size_t i = 0; i != 16; ++i) { @@ -127,8 +103,8 @@ for(size_t j = 0; j != 256; ++j) { const uint8_t Sj = SB[j]; - T[512 * i + 2 * j] = poly_mul(L_stride_0, Sj); - T[512 * i + 2 * j + 1] = poly_mul(L_stride_1, Sj); + T[512 * i + 2 * j] = poly_mul<0xC3>(L_stride_0, Sj); + T[512 * i + 2 * j + 1] = poly_mul<0xC3>(L_stride_1, Sj); } } @@ -137,8 +113,13 @@ } // namespace Kuznyechik_T -const constinit auto T = Kuznyechik_T::T_table(true); -const constinit auto IT = Kuznyechik_T::T_table(false); +// TODO(Botan4) this indirection with L/IL is required to work around a problem +// with Clang 19, where suddenly T_table became too much for it to handle as constexpr. +// Check if it's possible to remove this. +constexpr auto L = Kuznyechik_T::L_table(true); +constexpr auto IL = Kuznyechik_T::L_table(false); +const constinit auto T = Kuznyechik_T::T_table(L, S); +const constinit auto IT = Kuznyechik_T::T_table(IL, IS); const uint64_t C[32][2] = {{0xb87a486c7276a26e, 0x019484dd10bd275d}, {0xb3f490d8e4ec87dc, 0x02ebcb7920b94eba}, {0x0b8ed8b4969a25b2, 0x037f4fa4300469e7}, {0xa52be3730b1bcd7b, 0x041555f240b19cb7}, @@ -207,18 +188,13 @@ } // namespace -Kuznyechik::~Kuznyechik() { - clear(); -} - void Kuznyechik::clear() { - secure_scrub_memory(m_rke, sizeof(m_rke)); - secure_scrub_memory(m_rkd, sizeof(m_rkd)); - m_has_keying_material = false; + zap(m_rke); + zap(m_rkd); } bool Kuznyechik::has_keying_material() const { - return m_has_keying_material; + return !m_rke.empty(); } void Kuznyechik::key_schedule(std::span key) { @@ -231,19 +207,19 @@ uint64_t k2 = load_le(key.data(), 2); uint64_t k3 = load_le(key.data(), 3); - m_rke[0][0] = k0; - m_rke[0][1] = k1; - m_rke[1][0] = k2; - m_rke[1][1] = k3; + m_rke.resize(20); + + m_rke[0] = k0; + m_rke[1] = k1; + m_rke[2] = k2; + m_rke[3] = k3; for(size_t i = 0; i != 4; ++i) { for(size_t r = 0; r != 8; r += 2) { - uint64_t t0, t1, t2, t3; - - t0 = k0 ^ C[8 * i + r][0]; - t1 = k1 ^ C[8 * i + r][1]; - t2 = k0; - t3 = k1; + uint64_t t0 = k0 ^ C[8 * i + r][0]; + uint64_t t1 = k1 ^ C[8 * i + r][1]; + const uint64_t t2 = k0; + const uint64_t t3 = k1; LS(t0, t1); t0 ^= k2; t1 ^= k3; @@ -257,15 +233,17 @@ k1 ^= t3; } - m_rke[2 * i + 2][0] = k0; - m_rke[2 * i + 2][1] = k1; - m_rke[2 * i + 3][0] = k2; - m_rke[2 * i + 3][1] = k3; + m_rke[4 * (i + 1) + 0] = k0; + m_rke[4 * (i + 1) + 1] = k1; + m_rke[4 * (i + 1) + 2] = k2; + m_rke[4 * (i + 1) + 3] = k3; } + m_rkd.resize(20); + for(size_t i = 0; i != 10; i++) { - uint64_t t0 = m_rke[i][0]; - uint64_t t1 = m_rke[i][1]; + uint64_t t0 = m_rke[2 * i + 0]; + uint64_t t1 = m_rke[2 * i + 1]; if(i > 0) { Kuznyechik_F::ILSS(t0, t1); @@ -273,57 +251,55 @@ const size_t dest = 9 - i; - m_rkd[dest][0] = t0; - m_rkd[dest][1] = t1; + m_rkd[2 * dest + 0] = t0; + m_rkd[2 * dest + 1] = t1; } - - m_has_keying_material = true; } void Kuznyechik::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); - while(blocks) { + while(blocks > 0) { uint64_t x1 = load_le(in, 0); uint64_t x2 = load_le(in, 1); - x1 ^= m_rke[0][0]; - x2 ^= m_rke[0][1]; + x1 ^= m_rke[0]; + x2 ^= m_rke[1]; Kuznyechik_F::LS(x1, x2); - x1 ^= m_rke[1][0]; - x2 ^= m_rke[1][1]; + x1 ^= m_rke[2]; + x2 ^= m_rke[3]; Kuznyechik_F::LS(x1, x2); - x1 ^= m_rke[2][0]; - x2 ^= m_rke[2][1]; + x1 ^= m_rke[4]; + x2 ^= m_rke[5]; Kuznyechik_F::LS(x1, x2); - x1 ^= m_rke[3][0]; - x2 ^= m_rke[3][1]; + x1 ^= m_rke[6]; + x2 ^= m_rke[7]; Kuznyechik_F::LS(x1, x2); - x1 ^= m_rke[4][0]; - x2 ^= m_rke[4][1]; + x1 ^= m_rke[8]; + x2 ^= m_rke[9]; Kuznyechik_F::LS(x1, x2); - x1 ^= m_rke[5][0]; - x2 ^= m_rke[5][1]; + x1 ^= m_rke[10]; + x2 ^= m_rke[11]; Kuznyechik_F::LS(x1, x2); - x1 ^= m_rke[6][0]; - x2 ^= m_rke[6][1]; + x1 ^= m_rke[12]; + x2 ^= m_rke[13]; Kuznyechik_F::LS(x1, x2); - x1 ^= m_rke[7][0]; - x2 ^= m_rke[7][1]; + x1 ^= m_rke[14]; + x2 ^= m_rke[15]; Kuznyechik_F::LS(x1, x2); - x1 ^= m_rke[8][0]; - x2 ^= m_rke[8][1]; + x1 ^= m_rke[16]; + x2 ^= m_rke[17]; Kuznyechik_F::LS(x1, x2); - x1 ^= m_rke[9][0]; - x2 ^= m_rke[9][1]; + x1 ^= m_rke[18]; + x2 ^= m_rke[19]; store_le(out, x1, x2); @@ -335,51 +311,51 @@ void Kuznyechik::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); - while(blocks) { + while(blocks > 0) { uint64_t x1 = load_le(in, 0); uint64_t x2 = load_le(in, 1); Kuznyechik_F::ILSS(x1, x2); - x1 ^= m_rkd[0][0]; - x2 ^= m_rkd[0][1]; + x1 ^= m_rkd[0]; + x2 ^= m_rkd[1]; Kuznyechik_F::ILS(x1, x2); - x1 ^= m_rkd[1][0]; - x2 ^= m_rkd[1][1]; + x1 ^= m_rkd[2]; + x2 ^= m_rkd[3]; Kuznyechik_F::ILS(x1, x2); - x1 ^= m_rkd[2][0]; - x2 ^= m_rkd[2][1]; + x1 ^= m_rkd[4]; + x2 ^= m_rkd[5]; Kuznyechik_F::ILS(x1, x2); - x1 ^= m_rkd[3][0]; - x2 ^= m_rkd[3][1]; + x1 ^= m_rkd[6]; + x2 ^= m_rkd[7]; Kuznyechik_F::ILS(x1, x2); - x1 ^= m_rkd[4][0]; - x2 ^= m_rkd[4][1]; + x1 ^= m_rkd[8]; + x2 ^= m_rkd[9]; Kuznyechik_F::ILS(x1, x2); - x1 ^= m_rkd[5][0]; - x2 ^= m_rkd[5][1]; + x1 ^= m_rkd[10]; + x2 ^= m_rkd[11]; Kuznyechik_F::ILS(x1, x2); - x1 ^= m_rkd[6][0]; - x2 ^= m_rkd[6][1]; + x1 ^= m_rkd[12]; + x2 ^= m_rkd[13]; Kuznyechik_F::ILS(x1, x2); - x1 ^= m_rkd[7][0]; - x2 ^= m_rkd[7][1]; + x1 ^= m_rkd[14]; + x2 ^= m_rkd[15]; Kuznyechik_F::ILS(x1, x2); - x1 ^= m_rkd[8][0]; - x2 ^= m_rkd[8][1]; + x1 ^= m_rkd[16]; + x2 ^= m_rkd[17]; x1 = Kuznyechik_F::ISI(x1); x2 = Kuznyechik_F::ISI(x2); - x1 ^= m_rkd[9][0]; - x2 ^= m_rkd[9][1]; + x1 ^= m_rkd[18]; + x2 ^= m_rkd[19]; store_le(out, x1, x2); diff -Nru botan3-3.7.1+dfsg/src/lib/block/kuznyechik/kuznyechik.h botan3-3.12.0+dfsg/src/lib/block/kuznyechik/kuznyechik.h --- botan3-3.7.1+dfsg/src/lib/block/kuznyechik/kuznyechik.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/kuznyechik/kuznyechik.h 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,7 @@ /* * Kuznyechik -* (C) 2012 Jack Lloyd +* (C) 2023 Richard Huveneers +* 2025 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -9,6 +10,7 @@ #define BOTAN_KUZNYECHIK_H_ #include +#include namespace Botan { @@ -27,13 +29,11 @@ std::unique_ptr new_object() const override { return std::make_unique(); } bool has_keying_material() const override; - ~Kuznyechik() override; private: void key_schedule(std::span key) override; - uint64_t m_rke[10][2]; - uint64_t m_rkd[10][2]; - bool m_has_keying_material; + secure_vector m_rke; + secure_vector m_rkd; }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/lion/lion.cpp botan3-3.12.0+dfsg/src/lib/block/lion/lion.cpp --- botan3-3.7.1+dfsg/src/lib/block/lion/lion.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/lion/lion.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include #include +#include #include namespace Botan { diff -Nru botan3-3.7.1+dfsg/src/lib/block/noekeon/noekeon.cpp botan3-3.12.0+dfsg/src/lib/block/noekeon/noekeon.cpp --- botan3-3.7.1+dfsg/src/lib/block/noekeon/noekeon.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/noekeon/noekeon.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,10 +7,13 @@ #include -#include #include #include +#if defined(BOTAN_HAS_CPUID) + #include +#endif + namespace Botan { namespace { @@ -57,7 +60,7 @@ A1 ^= ~(A2 | A3); A0 ^= A2 & A1; - uint32_t T = A3; + const uint32_t T = A3; A3 = A0; A0 = T; @@ -71,7 +74,7 @@ size_t Noekeon::parallelism() const { #if defined(BOTAN_HAS_NOEKEON_SIMD) - if(CPUID::has_simd_32()) { + if(CPUID::has(CPUID::Feature::SIMD_4X32)) { return 4; } #endif @@ -81,8 +84,8 @@ std::string Noekeon::provider() const { #if defined(BOTAN_HAS_NOEKEON_SIMD) - if(CPUID::has_simd_32()) { - return "simd"; + if(auto feat = CPUID::check(CPUID::Feature::SIMD_4X32)) { + return *feat; } #endif @@ -102,7 +105,7 @@ assert_key_material_set(); #if defined(BOTAN_HAS_NOEKEON_SIMD) - if(CPUID::has_simd_32()) { + if(CPUID::has(CPUID::Feature::SIMD_4X32)) { while(blocks >= 4) { simd_encrypt_4(in, out); in += 4 * BLOCK_SIZE; @@ -150,7 +153,7 @@ assert_key_material_set(); #if defined(BOTAN_HAS_NOEKEON_SIMD) - if(CPUID::has_simd_32()) { + if(CPUID::has(CPUID::Feature::SIMD_4X32)) { while(blocks >= 4) { simd_decrypt_4(in, out); in += 4 * BLOCK_SIZE; diff -Nru botan3-3.7.1+dfsg/src/lib/block/noekeon/noekeon.h botan3-3.12.0+dfsg/src/lib/block/noekeon/noekeon.h --- botan3-3.7.1+dfsg/src/lib/block/noekeon/noekeon.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/noekeon/noekeon.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #define BOTAN_NOEKEON_H_ #include +#include namespace Botan { diff -Nru botan3-3.7.1+dfsg/src/lib/block/noekeon/noekeon_simd/info.txt botan3-3.12.0+dfsg/src/lib/block/noekeon/noekeon_simd/info.txt --- botan3-3.7.1+dfsg/src/lib/block/noekeon/noekeon_simd/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/noekeon/noekeon_simd/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + NOEKEON_SIMD -> 20160903 - + name -> "Noekeon SIMD" @@ -8,6 +8,18 @@ -noekeon -simd +cpuid +simd_4x32 + + +x86_32:ssse3 +x86_64:ssse3 +x32:ssse3 +arm32:neon +arm64:neon +ppc32:altivec +ppc64:altivec +loongarch64:lsx +wasm:simd128 + diff -Nru botan3-3.7.1+dfsg/src/lib/block/noekeon/noekeon_simd/noekeon_simd.cpp botan3-3.12.0+dfsg/src/lib/block/noekeon/noekeon_simd/noekeon_simd.cpp --- botan3-3.7.1+dfsg/src/lib/block/noekeon/noekeon_simd/noekeon_simd.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/noekeon/noekeon_simd/noekeon_simd.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,7 +7,7 @@ #include -#include +#include namespace Botan { @@ -16,14 +16,14 @@ /* * Noekeon's Theta Operation */ -inline void theta(SIMD_4x32& A0, - SIMD_4x32& A1, - SIMD_4x32& A2, - SIMD_4x32& A3, - const SIMD_4x32& K0, - const SIMD_4x32& K1, - const SIMD_4x32& K2, - const SIMD_4x32& K3) { +inline void BOTAN_FN_ISA_SIMD_4X32 theta(SIMD_4x32& A0, + SIMD_4x32& A1, + SIMD_4x32& A2, + SIMD_4x32& A3, + const SIMD_4x32& K0, + const SIMD_4x32& K1, + const SIMD_4x32& K2, + const SIMD_4x32& K3) { SIMD_4x32 T = A0 ^ A2; T ^= T.rotl<8>() ^ T.rotr<8>(); A1 ^= T; @@ -43,11 +43,11 @@ /* * Noekeon's Gamma S-Box Layer */ -inline void gamma(SIMD_4x32& A0, SIMD_4x32& A1, SIMD_4x32& A2, SIMD_4x32& A3) { +inline void BOTAN_FN_ISA_SIMD_4X32 gamma(SIMD_4x32& A0, SIMD_4x32& A1, SIMD_4x32& A2, SIMD_4x32& A3) { A1 ^= ~(A2 | A3); A0 ^= A2 & A1; - SIMD_4x32 T = A3; + const SIMD_4x32 T = A3; A3 = A0; A0 = T; @@ -62,7 +62,7 @@ /* * Noekeon Encryption */ -void Noekeon::simd_encrypt_4(const uint8_t in[], uint8_t out[]) const { +void BOTAN_FN_ISA_SIMD_4X32 Noekeon::simd_encrypt_4(const uint8_t in[], uint8_t out[]) const { const SIMD_4x32 K0 = SIMD_4x32::splat(m_EK[0]); const SIMD_4x32 K1 = SIMD_4x32::splat(m_EK[1]); const SIMD_4x32 K2 = SIMD_4x32::splat(m_EK[2]); @@ -105,7 +105,7 @@ /* * Noekeon Encryption */ -void Noekeon::simd_decrypt_4(const uint8_t in[], uint8_t out[]) const { +void BOTAN_FN_ISA_SIMD_4X32 Noekeon::simd_decrypt_4(const uint8_t in[], uint8_t out[]) const { const SIMD_4x32 K0 = SIMD_4x32::splat(m_DK[0]); const SIMD_4x32 K1 = SIMD_4x32::splat(m_DK[1]); const SIMD_4x32 K2 = SIMD_4x32::splat(m_DK[2]); diff -Nru botan3-3.7.1+dfsg/src/lib/block/seed/seed.cpp botan3-3.12.0+dfsg/src/lib/block/seed/seed.cpp --- botan3-3.7.1+dfsg/src/lib/block/seed/seed.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/seed/seed.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,10 @@ #include #include +#if defined(BOTAN_HAS_CPUID) + #include +#endif + namespace Botan { namespace { @@ -51,7 +55,7 @@ /* * SEED G Function */ -inline uint32_t SEED_G(uint32_t X) { +BOTAN_FORCE_INLINE uint32_t SEED_G(uint32_t X) { const uint32_t M = 0x01010101; const uint32_t s0 = M * SEED_S0[get_byte<3>(X)]; const uint32_t s1 = M * SEED_S1[get_byte<2>(X)]; @@ -74,8 +78,66 @@ void SEED::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); +#if defined(BOTAN_HAS_SEED_AVX512_GFNI) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return avx512_gfni_encrypt(in, out, blocks); + } +#endif + +#if defined(BOTAN_HAS_SEED_HWAES) + if(CPUID::has(CPUID::Feature::HW_AES)) { + return hwaes_encrypt(in, out, blocks); + } +#endif + prefetch_arrays(SEED_S0, SEED_S1); + while(blocks >= 2) { + uint32_t B00 = load_be(in, 0); + uint32_t B01 = load_be(in, 1); + uint32_t B02 = load_be(in, 2); + uint32_t B03 = load_be(in, 3); + uint32_t B10 = load_be(in, 4); + uint32_t B11 = load_be(in, 5); + uint32_t B12 = load_be(in, 6); + uint32_t B13 = load_be(in, 7); + + for(size_t j = 0; j != 16; j += 2) { + uint32_t T00 = B02 ^ m_K[2 * j]; + uint32_t T10 = B12 ^ m_K[2 * j]; + uint32_t T01 = SEED_G(B02 ^ B03 ^ m_K[2 * j + 1]); + uint32_t T11 = SEED_G(B12 ^ B13 ^ m_K[2 * j + 1]); + T00 = SEED_G(T01 + T00); + T10 = SEED_G(T11 + T10); + T01 = SEED_G(T01 + T00); + T11 = SEED_G(T11 + T10); + B01 ^= T01; + B11 ^= T11; + B00 ^= T00 + T01; + B10 ^= T10 + T11; + + T00 = B00 ^ m_K[2 * j + 2]; + T10 = B10 ^ m_K[2 * j + 2]; + T01 = SEED_G(B00 ^ B01 ^ m_K[2 * j + 3]); + T11 = SEED_G(B10 ^ B11 ^ m_K[2 * j + 3]); + T10 = SEED_G(T11 + T10); + T00 = SEED_G(T01 + T00); + T01 = SEED_G(T01 + T00); + T11 = SEED_G(T11 + T10); + B03 ^= T01; + B13 ^= T11; + B02 ^= T00 + T01; + B12 ^= T10 + T11; + } + + store_be(out, B02, B03, B00, B01, B12, B13, B10, B11); + + in += 2 * BLOCK_SIZE; + out += 2 * BLOCK_SIZE; + + blocks -= 2; + } + for(size_t i = 0; i != blocks; ++i) { uint32_t B0 = load_be(in, 0); uint32_t B1 = load_be(in, 1); @@ -83,10 +145,8 @@ uint32_t B3 = load_be(in, 3); for(size_t j = 0; j != 16; j += 2) { - uint32_t T0, T1; - - T0 = B2 ^ m_K[2 * j]; - T1 = SEED_G(B2 ^ B3 ^ m_K[2 * j + 1]); + uint32_t T0 = B2 ^ m_K[2 * j]; + uint32_t T1 = SEED_G(B2 ^ B3 ^ m_K[2 * j + 1]); T0 = SEED_G(T1 + T0); T1 = SEED_G(T1 + T0); B1 ^= T1; @@ -113,8 +173,65 @@ void SEED::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); +#if defined(BOTAN_HAS_SEED_AVX512_GFNI) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return avx512_gfni_decrypt(in, out, blocks); + } +#endif + +#if defined(BOTAN_HAS_SEED_HWAES) + if(CPUID::has(CPUID::Feature::HW_AES)) { + return hwaes_decrypt(in, out, blocks); + } +#endif + prefetch_arrays(SEED_S0, SEED_S1); + while(blocks >= 2) { + uint32_t B00 = load_be(in, 0); + uint32_t B01 = load_be(in, 1); + uint32_t B02 = load_be(in, 2); + uint32_t B03 = load_be(in, 3); + uint32_t B10 = load_be(in, 4); + uint32_t B11 = load_be(in, 5); + uint32_t B12 = load_be(in, 6); + uint32_t B13 = load_be(in, 7); + + for(size_t j = 0; j != 16; j += 2) { + uint32_t T00 = B02 ^ m_K[30 - 2 * j]; + uint32_t T10 = B12 ^ m_K[30 - 2 * j]; + uint32_t T01 = SEED_G(B02 ^ B03 ^ m_K[31 - 2 * j]); + uint32_t T11 = SEED_G(B12 ^ B13 ^ m_K[31 - 2 * j]); + T00 = SEED_G(T01 + T00); + T10 = SEED_G(T11 + T10); + T01 = SEED_G(T01 + T00); + T11 = SEED_G(T11 + T10); + B01 ^= T01; + B11 ^= T11; + B00 ^= T00 + T01; + B10 ^= T10 + T11; + + T00 = B00 ^ m_K[28 - 2 * j]; + T10 = B10 ^ m_K[28 - 2 * j]; + T01 = SEED_G(B00 ^ B01 ^ m_K[29 - 2 * j]); + T11 = SEED_G(B10 ^ B11 ^ m_K[29 - 2 * j]); + T00 = SEED_G(T01 + T00); + T10 = SEED_G(T11 + T10); + T01 = SEED_G(T01 + T00); + T11 = SEED_G(T11 + T10); + B03 ^= T01; + B13 ^= T11; + B02 ^= T00 + T01; + B12 ^= T10 + T11; + } + + store_be(out, B02, B03, B00, B01, B12, B13, B10, B11); + + in += 2 * BLOCK_SIZE; + out += 2 * BLOCK_SIZE; + blocks -= 2; + } + for(size_t i = 0; i != blocks; ++i) { uint32_t B0 = load_be(in, 0); uint32_t B1 = load_be(in, 1); @@ -122,10 +239,8 @@ uint32_t B3 = load_be(in, 3); for(size_t j = 0; j != 16; j += 2) { - uint32_t T0, T1; - - T0 = B2 ^ m_K[30 - 2 * j]; - T1 = SEED_G(B2 ^ B3 ^ m_K[31 - 2 * j]); + uint32_t T0 = B2 ^ m_K[30 - 2 * j]; + uint32_t T1 = SEED_G(B2 ^ B3 ^ m_K[31 - 2 * j]); T0 = SEED_G(T1 + T0); T1 = SEED_G(T1 + T0); B1 ^= T1; @@ -200,4 +315,36 @@ zap(m_K); } +size_t SEED::parallelism() const { +#if defined(BOTAN_HAS_SEED_AVX512_GFNI) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return 16; + } +#endif + +#if defined(BOTAN_HAS_SEED_HWAES) + if(CPUID::has(CPUID::Feature::HW_AES)) { + return 4; + } +#endif + + return 1; +} + +std::string SEED::provider() const { +#if defined(BOTAN_HAS_SEED_AVX512_GFNI) + if(auto feat = CPUID::check(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return *feat; + } +#endif + +#if defined(BOTAN_HAS_SEED_HWAES) + if(auto feat = CPUID::check(CPUID::Feature::HW_AES)) { + return *feat; + } +#endif + + return "base"; +} + } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/seed/seed.h botan3-3.12.0+dfsg/src/lib/block/seed/seed.h --- botan3-3.7.1+dfsg/src/lib/block/seed/seed.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/seed/seed.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #define BOTAN_SEED_H_ #include +#include namespace Botan { @@ -26,11 +27,23 @@ std::unique_ptr new_object() const override { return std::make_unique(); } + std::string provider() const override; + size_t parallelism() const override; bool has_keying_material() const override; private: void key_schedule(std::span key) override; +#if defined(BOTAN_HAS_SEED_AVX512_GFNI) + void avx512_gfni_encrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; + void avx512_gfni_decrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; +#endif + +#if defined(BOTAN_HAS_SEED_HWAES) + void hwaes_encrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; + void hwaes_decrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; +#endif + secure_vector m_K; }; diff -Nru botan3-3.7.1+dfsg/src/lib/block/seed/seed_avx512_gfni/info.txt botan3-3.12.0+dfsg/src/lib/block/seed/seed_avx512_gfni/info.txt --- botan3-3.7.1+dfsg/src/lib/block/seed/seed_avx512_gfni/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/seed/seed_avx512_gfni/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,17 @@ + +SEED_AVX512_GFNI -> 20260319 + + + +name -> "SEED AVX-512/GFNI" + + + +cpuid +simd_avx512 + + + +gfni +avx512 + diff -Nru botan3-3.7.1+dfsg/src/lib/block/seed/seed_avx512_gfni/seed_avx512_gfni.cpp botan3-3.12.0+dfsg/src/lib/block/seed/seed_avx512_gfni/seed_avx512_gfni.cpp --- botan3-3.7.1+dfsg/src/lib/block/seed/seed_avx512_gfni/seed_avx512_gfni.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/seed/seed_avx512_gfni/seed_avx512_gfni.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,331 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include + +namespace Botan { + +namespace SEED_AVX512_GFNI { + +namespace { + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI SIMD_16x32 seed_g(const SIMD_16x32& X) { + /* + * SEED's two sboxes are both based on inversions in GF(2^8) modulo the polynomial + * x^8+x^6+x^5+x+1 (0x163), followed by different affine transforms. + * + * GFNI uses AES's field (modulo 0x11B) so the pre-inversion matrix is a field isomorphism + * that maps the inputs into the AES field. The post-inversion matrices then apply map + * back to SEED's field and apply the appropriate linear transform. + */ + + // Field isomorphism from SEED's field to AES field + constexpr uint64_t seed_pre_a = gfni_matrix(R"( + 1 1 0 1 0 0 0 0 + 0 0 1 1 0 0 1 1 + 0 0 0 0 1 1 0 1 + 0 1 1 1 0 1 0 0 + 0 1 1 0 1 0 0 0 + 0 0 0 1 1 0 0 0 + 0 0 1 1 1 1 0 0 + 0 0 0 0 1 1 1 0 + )"); + + // Field isomorphism from AES->SEED multiplied by S0's affine matrix + constexpr uint64_t seed_s0_post_a = gfni_matrix(R"( + 0 1 0 1 1 0 0 1 + 0 0 1 1 1 0 1 0 + 1 0 0 0 1 1 1 0 + 1 1 0 0 1 0 0 1 + 0 1 0 1 1 0 1 1 + 1 1 1 1 1 0 1 1 + 0 0 1 1 0 1 0 1 + 0 0 0 1 0 1 1 1 + )"); + + // Field isomorphism from AES->SEED multiplied by S1's affine matrix + constexpr uint64_t seed_s1_post_a = gfni_matrix(R"( + 0 0 1 1 0 1 1 0 + 0 1 1 0 0 0 1 0 + 0 1 0 1 1 0 1 1 + 0 0 0 0 0 0 1 1 + 1 1 0 1 0 0 0 0 + 0 1 0 0 1 0 1 1 + 1 1 1 0 1 0 1 1 + 1 1 1 1 0 0 0 1 + )"); + + constexpr uint8_t seed_s0_post_c = 0xA9; + constexpr uint8_t seed_s1_post_c = 0x38; + + // Compute S0(x) and S1(x) for all bytes + const auto pre = gf2p8affine(X); + const auto s0 = gf2p8affineinv(pre); + const auto s1 = gf2p8affineinv(pre); + + // Blend S0/S1 outputs by alternating bytes + constexpr uint64_t blend_mask = 0xAAAAAAAAAAAAAAAA; // 0b1010.... + const auto sbox = SIMD_16x32(_mm512_mask_blend_epi8(blend_mask, s0.raw(), s1.raw())); + + // Linear mixing layer + const auto M0 = SIMD_16x32::splat(0x3FCFF3FC); + const auto M1 = SIMD_16x32::splat(0xFC3FCFF3); + const auto M2 = SIMD_16x32::splat(0xF3FC3FCF); + const auto M3 = SIMD_16x32::splat(0xCFF3FC3F); + + // Masks for broadcasting each byte across the 32 bit word that contains it + + // clang-format off + alignas(64) constexpr uint8_t SHUF_BYTE0[64] = { + 0, 0, 0, 0, 4, 4, 4, 4, 8, 8, 8, 8, 12, 12, 12, 12, + 0, 0, 0, 0, 4, 4, 4, 4, 8, 8, 8, 8, 12, 12, 12, 12, + 0, 0, 0, 0, 4, 4, 4, 4, 8, 8, 8, 8, 12, 12, 12, 12, + 0, 0, 0, 0, 4, 4, 4, 4, 8, 8, 8, 8, 12, 12, 12, 12, + }; + alignas(64) constexpr uint8_t SHUF_BYTE1[64] = { + 1, 1, 1, 1, 5, 5, 5, 5, 9, 9, 9, 9, 13, 13, 13, 13, + 1, 1, 1, 1, 5, 5, 5, 5, 9, 9, 9, 9, 13, 13, 13, 13, + 1, 1, 1, 1, 5, 5, 5, 5, 9, 9, 9, 9, 13, 13, 13, 13, + 1, 1, 1, 1, 5, 5, 5, 5, 9, 9, 9, 9, 13, 13, 13, 13, + }; + alignas(64) constexpr uint8_t SHUF_BYTE2[64] = { + 2, 2, 2, 2, 6, 6, 6, 6, 10, 10, 10, 10, 14, 14, 14, 14, + 2, 2, 2, 2, 6, 6, 6, 6, 10, 10, 10, 10, 14, 14, 14, 14, + 2, 2, 2, 2, 6, 6, 6, 6, 10, 10, 10, 10, 14, 14, 14, 14, + 2, 2, 2, 2, 6, 6, 6, 6, 10, 10, 10, 10, 14, 14, 14, 14, + }; + alignas(64) constexpr uint8_t SHUF_BYTE3[64] = { + 3, 3, 3, 3, 7, 7, 7, 7, 11, 11, 11, 11, 15, 15, 15, 15, + 3, 3, 3, 3, 7, 7, 7, 7, 11, 11, 11, 11, 15, 15, 15, 15, + 3, 3, 3, 3, 7, 7, 7, 7, 11, 11, 11, 11, 15, 15, 15, 15, + 3, 3, 3, 3, 7, 7, 7, 7, 11, 11, 11, 11, 15, 15, 15, 15, + }; + // clang-format on + + const auto b0 = SIMD_16x32(_mm512_shuffle_epi8(sbox.raw(), _mm512_load_si512(SHUF_BYTE0))); + const auto b1 = SIMD_16x32(_mm512_shuffle_epi8(sbox.raw(), _mm512_load_si512(SHUF_BYTE1))); + const auto b2 = SIMD_16x32(_mm512_shuffle_epi8(sbox.raw(), _mm512_load_si512(SHUF_BYTE2))); + const auto b3 = SIMD_16x32(_mm512_shuffle_epi8(sbox.raw(), _mm512_load_si512(SHUF_BYTE3))); + + // Return (b0 & M0) ^ (b1 & M1) ^ (b2 & M2) ^ (b3 & M3) + // ternlogd 0x78 is a ^ (b & c) + auto result = SIMD_16x32(b0) & M0; + result = SIMD_16x32::ternary_fn<0x78>(result, b1, M1); + result = SIMD_16x32::ternary_fn<0x78>(result, b2, M2); + result = SIMD_16x32::ternary_fn<0x78>(result, b3, M3); + + return SIMD_16x32(result); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI void seed_round( + SIMD_16x32& B0, SIMD_16x32& B1, SIMD_16x32& B2, SIMD_16x32& B3, uint32_t K0, uint32_t K1, uint32_t K2, uint32_t K3) { + auto T0 = B2 ^ SIMD_16x32::splat(K0); + auto T1 = seed_g(B2 ^ B3 ^ SIMD_16x32::splat(K1)); + T0 = seed_g(T1 + T0); + T1 = seed_g(T1 + T0); + B1 ^= T1; + B0 ^= T0 + T1; + + T0 = B0 ^ SIMD_16x32::splat(K2); + T1 = seed_g(B0 ^ B1 ^ SIMD_16x32::splat(K3)); + T0 = seed_g(T1 + T0); + T1 = seed_g(T1 + T0); + B3 ^= T1; + B2 ^= T0 + T1; +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI void encrypt(const uint8_t ptext[16 * 4 * 4], + uint8_t ctext[16 * 4 * 4], + std::span RK) { + SIMD_16x32 B0 = SIMD_16x32::load_be(ptext + 16 * 4 * 0); + SIMD_16x32 B1 = SIMD_16x32::load_be(ptext + 16 * 4 * 1); + SIMD_16x32 B2 = SIMD_16x32::load_be(ptext + 16 * 4 * 2); + SIMD_16x32 B3 = SIMD_16x32::load_be(ptext + 16 * 4 * 3); + + SIMD_16x32::transpose(B0, B1, B2, B3); + + for(size_t j = 0; j != 8; ++j) { + const uint32_t K0 = RK[4 * j]; + const uint32_t K1 = RK[4 * j + 1]; + const uint32_t K2 = RK[4 * j + 2]; + const uint32_t K3 = RK[4 * j + 3]; + + seed_round(B0, B1, B2, B3, K0, K1, K2, K3); + } + + // Output order is B2, B3, B0, B1 + SIMD_16x32::transpose(B2, B3, B0, B1); + B2.store_be(ctext + 16 * 4 * 0); + B3.store_be(ctext + 16 * 4 * 1); + B0.store_be(ctext + 16 * 4 * 2); + B1.store_be(ctext + 16 * 4 * 3); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI void decrypt(const uint8_t ctext[16 * 4 * 4], + uint8_t ptext[16 * 4 * 4], + std::span RK) { + SIMD_16x32 B0 = SIMD_16x32::load_be(ctext + 16 * 4 * 0); + SIMD_16x32 B1 = SIMD_16x32::load_be(ctext + 16 * 4 * 1); + SIMD_16x32 B2 = SIMD_16x32::load_be(ctext + 16 * 4 * 2); + SIMD_16x32 B3 = SIMD_16x32::load_be(ctext + 16 * 4 * 3); + + SIMD_16x32::transpose(B0, B1, B2, B3); + + for(size_t j = 0; j != 8; ++j) { + const uint32_t K0 = RK[30 - 4 * j]; + const uint32_t K1 = RK[31 - 4 * j]; + const uint32_t K2 = RK[28 - 4 * j]; + const uint32_t K3 = RK[29 - 4 * j]; + + seed_round(B0, B1, B2, B3, K0, K1, K2, K3); + } + + SIMD_16x32::transpose(B2, B3, B0, B1); + B2.store_be(ptext + 16 * 4 * 0); + B3.store_be(ptext + 16 * 4 * 1); + B0.store_be(ptext + 16 * 4 * 2); + B1.store_be(ptext + 16 * 4 * 3); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI void encrypt_x2(const uint8_t ptext[32 * 4 * 4], + uint8_t ctext[32 * 4 * 4], + std::span RK) { + SIMD_16x32 B0 = SIMD_16x32::load_be(ptext + 16 * 4 * 0); + SIMD_16x32 B1 = SIMD_16x32::load_be(ptext + 16 * 4 * 1); + SIMD_16x32 B2 = SIMD_16x32::load_be(ptext + 16 * 4 * 2); + SIMD_16x32 B3 = SIMD_16x32::load_be(ptext + 16 * 4 * 3); + + SIMD_16x32 B4 = SIMD_16x32::load_be(ptext + 16 * 4 * 4); + SIMD_16x32 B5 = SIMD_16x32::load_be(ptext + 16 * 4 * 5); + SIMD_16x32 B6 = SIMD_16x32::load_be(ptext + 16 * 4 * 6); + SIMD_16x32 B7 = SIMD_16x32::load_be(ptext + 16 * 4 * 7); + + SIMD_16x32::transpose(B0, B1, B2, B3); + SIMD_16x32::transpose(B4, B5, B6, B7); + + for(size_t j = 0; j != 8; ++j) { + const uint32_t K0 = RK[4 * j]; + const uint32_t K1 = RK[4 * j + 1]; + const uint32_t K2 = RK[4 * j + 2]; + const uint32_t K3 = RK[4 * j + 3]; + + seed_round(B0, B1, B2, B3, K0, K1, K2, K3); + seed_round(B4, B5, B6, B7, K0, K1, K2, K3); + } + + SIMD_16x32::transpose(B2, B3, B0, B1); + SIMD_16x32::transpose(B6, B7, B4, B5); + + B2.store_be(ctext + 16 * 4 * 0); + B3.store_be(ctext + 16 * 4 * 1); + B0.store_be(ctext + 16 * 4 * 2); + B1.store_be(ctext + 16 * 4 * 3); + + B6.store_be(ctext + 16 * 4 * 4); + B7.store_be(ctext + 16 * 4 * 5); + B4.store_be(ctext + 16 * 4 * 6); + B5.store_be(ctext + 16 * 4 * 7); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI void decrypt_x2(const uint8_t ctext[32 * 4 * 4], + uint8_t ptext[32 * 4 * 4], + std::span RK) { + SIMD_16x32 B0 = SIMD_16x32::load_be(ctext + 16 * 4 * 0); + SIMD_16x32 B1 = SIMD_16x32::load_be(ctext + 16 * 4 * 1); + SIMD_16x32 B2 = SIMD_16x32::load_be(ctext + 16 * 4 * 2); + SIMD_16x32 B3 = SIMD_16x32::load_be(ctext + 16 * 4 * 3); + + SIMD_16x32 B4 = SIMD_16x32::load_be(ctext + 16 * 4 * 4); + SIMD_16x32 B5 = SIMD_16x32::load_be(ctext + 16 * 4 * 5); + SIMD_16x32 B6 = SIMD_16x32::load_be(ctext + 16 * 4 * 6); + SIMD_16x32 B7 = SIMD_16x32::load_be(ctext + 16 * 4 * 7); + + SIMD_16x32::transpose(B0, B1, B2, B3); + SIMD_16x32::transpose(B4, B5, B6, B7); + + for(size_t j = 0; j != 8; ++j) { + const uint32_t K0 = RK[30 - 4 * j]; + const uint32_t K1 = RK[31 - 4 * j]; + const uint32_t K2 = RK[28 - 4 * j]; + const uint32_t K3 = RK[29 - 4 * j]; + + seed_round(B0, B1, B2, B3, K0, K1, K2, K3); + seed_round(B4, B5, B6, B7, K0, K1, K2, K3); + } + + SIMD_16x32::transpose(B2, B3, B0, B1); + SIMD_16x32::transpose(B6, B7, B4, B5); + + B2.store_be(ptext + 16 * 4 * 0); + B3.store_be(ptext + 16 * 4 * 1); + B0.store_be(ptext + 16 * 4 * 2); + B1.store_be(ptext + 16 * 4 * 3); + + B6.store_be(ptext + 16 * 4 * 4); + B7.store_be(ptext + 16 * 4 * 5); + B4.store_be(ptext + 16 * 4 * 6); + B5.store_be(ptext + 16 * 4 * 7); +} + +} // namespace + +} // namespace SEED_AVX512_GFNI + +void BOTAN_FN_ISA_AVX512_GFNI SEED::avx512_gfni_encrypt(const uint8_t ptext[], uint8_t ctext[], size_t blocks) const { + while(blocks >= 32) { + SEED_AVX512_GFNI::encrypt_x2(ptext, ctext, m_K); + ptext += 16 * 32; + ctext += 16 * 32; + blocks -= 32; + } + + while(blocks >= 16) { + SEED_AVX512_GFNI::encrypt(ptext, ctext, m_K); + ptext += 16 * 16; + ctext += 16 * 16; + blocks -= 16; + } + + if(blocks > 0) { + BOTAN_ASSERT_NOMSG(blocks < 16); + uint8_t pbuf[16 * 16] = {0}; + uint8_t cbuf[16 * 16] = {0}; + copy_mem(pbuf, ptext, blocks * 16); + SEED_AVX512_GFNI::encrypt(pbuf, cbuf, m_K); + copy_mem(ctext, cbuf, blocks * 16); + } +} + +void BOTAN_FN_ISA_AVX512_GFNI SEED::avx512_gfni_decrypt(const uint8_t ctext[], uint8_t ptext[], size_t blocks) const { + while(blocks >= 32) { + SEED_AVX512_GFNI::decrypt_x2(ctext, ptext, m_K); + ptext += 16 * 32; + ctext += 16 * 32; + blocks -= 32; + } + + while(blocks >= 16) { + SEED_AVX512_GFNI::decrypt(ctext, ptext, m_K); + ptext += 16 * 16; + ctext += 16 * 16; + blocks -= 16; + } + + if(blocks > 0) { + BOTAN_ASSERT_NOMSG(blocks < 16); + uint8_t pbuf[16 * 16] = {0}; + uint8_t cbuf[16 * 16] = {0}; + copy_mem(cbuf, ctext, blocks * 16); + SEED_AVX512_GFNI::decrypt(cbuf, pbuf, m_K); + copy_mem(ptext, pbuf, blocks * 16); + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/seed/seed_hwaes/info.txt botan3-3.12.0+dfsg/src/lib/block/seed/seed_hwaes/info.txt --- botan3-3.7.1+dfsg/src/lib/block/seed/seed_hwaes/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/seed/seed_hwaes/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ + +SEED_HWAES -> 20260322 + + + +name -> "SEED using hardware AES instructions" + + + +cpuid +simd_hwaes + diff -Nru botan3-3.7.1+dfsg/src/lib/block/seed/seed_hwaes/seed_hwaes.cpp botan3-3.12.0+dfsg/src/lib/block/seed/seed_hwaes/seed_hwaes.cpp --- botan3-3.7.1+dfsg/src/lib/block/seed/seed_hwaes/seed_hwaes.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/seed/seed_hwaes/seed_hwaes.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,196 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include + +namespace Botan { + +namespace SEED_HWAES { + +namespace { + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES SIMD_4x32 seed_g(SIMD_4x32 X) { + // Field isomorphism from SEED's field (0x163) to AES field (0x11B) + constexpr uint64_t pre_a = gfni_matrix(R"( + 1 1 0 1 0 0 0 0 + 0 0 1 1 0 0 1 1 + 0 0 0 0 1 1 0 1 + 0 1 1 1 0 1 0 0 + 0 1 1 0 1 0 0 0 + 0 0 0 1 1 0 0 0 + 0 0 1 1 1 1 0 0 + 0 0 0 0 1 1 1 0)"); + + // AES->SEED field isomorphism composed with S0's affine + constexpr uint64_t s0_post_a = gfni_matrix(R"( + 0 1 0 1 1 0 0 1 + 0 0 1 1 1 0 1 0 + 1 0 0 0 1 1 1 0 + 1 1 0 0 1 0 0 1 + 0 1 0 1 1 0 1 1 + 1 1 1 1 1 0 1 1 + 0 0 1 1 0 1 0 1 + 0 0 0 1 0 1 1 1)"); + constexpr uint8_t s0_post_c = 0xA9; + + // AES->SEED field isomorphism composed with S1's affine + constexpr uint64_t s1_post_a = gfni_matrix(R"( + 0 0 1 1 0 1 1 0 + 0 1 1 0 0 0 1 0 + 0 1 0 1 1 0 1 1 + 0 0 0 0 0 0 1 1 + 1 1 0 1 0 0 0 0 + 0 1 0 0 1 0 1 1 + 1 1 1 0 1 0 1 1 + 1 1 1 1 0 0 0 1)"); + constexpr uint8_t s1_post_c = 0x38; + + constexpr auto pre = Gf2AffineTransformation(pre_a, 0x00); + constexpr auto post_s0 = Gf2AffineTransformation::post_sbox(s0_post_a, s0_post_c); + constexpr auto post_s1 = Gf2AffineTransformation::post_sbox(s1_post_a, s1_post_c); + + // Shared computation for S0(x) and S1(x) + const auto sub = hw_aes_sbox(pre.affine_transform(X)); + + // Compute S0(x) and S1(x) + const auto s0 = post_s0.affine_transform(sub); + const auto s1 = post_s1.affine_transform(sub); + + // Blend S0(x) and S1(x) outputs in alternating bytes + const auto sbox = SIMD_4x32::byte_blend(0x00FF00FF, s0, s1); + + // Linear mixing step + const auto M0 = SIMD_4x32::splat(0x3FCFF3FC); + const auto M1 = SIMD_4x32::splat(0xFC3FCFF3); + const auto M2 = SIMD_4x32::splat(0xF3FC3FCF); + const auto M3 = SIMD_4x32::splat(0xCFF3FC3F); + + // Broadcast each byte of a 32-bit word to all 4 positions + const auto SHUF0 = SIMD_4x32(0x00000000, 0x04040404, 0x08080808, 0x0C0C0C0C); + const auto SHUF1 = SIMD_4x32(0x01010101, 0x05050505, 0x09090909, 0x0D0D0D0D); + const auto SHUF2 = SIMD_4x32(0x02020202, 0x06060606, 0x0A0A0A0A, 0x0E0E0E0E); + const auto SHUF3 = SIMD_4x32(0x03030303, 0x07070707, 0x0B0B0B0B, 0x0F0F0F0F); + + auto b0 = SIMD_4x32::byte_shuffle(sbox, SHUF0); + auto b1 = SIMD_4x32::byte_shuffle(sbox, SHUF1); + auto b2 = SIMD_4x32::byte_shuffle(sbox, SHUF2); + auto b3 = SIMD_4x32::byte_shuffle(sbox, SHUF3); + + return (b0 & M0) ^ (b1 & M1) ^ (b2 & M2) ^ (b3 & M3); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES void seed_round( + SIMD_4x32& B0, SIMD_4x32& B1, SIMD_4x32& B2, SIMD_4x32& B3, uint32_t K0, uint32_t K1, uint32_t K2, uint32_t K3) { + auto T0 = B2 ^ SIMD_4x32::splat(K0); + auto T1 = seed_g(B2 ^ B3 ^ SIMD_4x32::splat(K1)); + T0 = seed_g(T1 + T0); + T1 = seed_g(T1 + T0); + B1 ^= T1; + B0 ^= T0 + T1; + + T0 = B0 ^ SIMD_4x32::splat(K2); + T1 = seed_g(B0 ^ B1 ^ SIMD_4x32::splat(K3)); + T0 = seed_g(T1 + T0); + T1 = seed_g(T1 + T0); + B3 ^= T1; + B2 ^= T0 + T1; +} + +BOTAN_FN_ISA_HWAES void encrypt_4(const uint8_t ptext[4 * 16], uint8_t ctext[4 * 16], std::span RK) { + auto B0 = SIMD_4x32::load_be(ptext); + auto B1 = SIMD_4x32::load_be(ptext + 16); + auto B2 = SIMD_4x32::load_be(ptext + 32); + auto B3 = SIMD_4x32::load_be(ptext + 48); + + SIMD_4x32::transpose(B0, B1, B2, B3); + + for(size_t j = 0; j != 8; ++j) { + const uint32_t K0 = RK[4 * j]; + const uint32_t K1 = RK[4 * j + 1]; + const uint32_t K2 = RK[4 * j + 2]; + const uint32_t K3 = RK[4 * j + 3]; + + seed_round(B0, B1, B2, B3, K0, K1, K2, K3); + } + + // Output order: B2, B3, B0, B1 + SIMD_4x32::transpose(B2, B3, B0, B1); + + B2.store_be(ctext); + B3.store_be(ctext + 16); + B0.store_be(ctext + 32); + B1.store_be(ctext + 48); +} + +BOTAN_FN_ISA_HWAES void decrypt_4(const uint8_t ctext[4 * 16], uint8_t ptext[4 * 16], std::span RK) { + auto B0 = SIMD_4x32::load_be(ctext); + auto B1 = SIMD_4x32::load_be(ctext + 16); + auto B2 = SIMD_4x32::load_be(ctext + 32); + auto B3 = SIMD_4x32::load_be(ctext + 48); + + SIMD_4x32::transpose(B0, B1, B2, B3); + + for(size_t j = 0; j != 8; ++j) { + const uint32_t K0 = RK[30 - 4 * j]; + const uint32_t K1 = RK[31 - 4 * j]; + const uint32_t K2 = RK[28 - 4 * j]; + const uint32_t K3 = RK[29 - 4 * j]; + + seed_round(B0, B1, B2, B3, K0, K1, K2, K3); + } + + SIMD_4x32::transpose(B2, B3, B0, B1); + + B2.store_be(ptext); + B3.store_be(ptext + 16); + B0.store_be(ptext + 32); + B1.store_be(ptext + 48); +} + +} // namespace + +} // namespace SEED_HWAES + +void BOTAN_FN_ISA_HWAES SEED::hwaes_encrypt(const uint8_t ptext[], uint8_t ctext[], size_t blocks) const { + while(blocks >= 4) { + SEED_HWAES::encrypt_4(ptext, ctext, m_K); + ptext += 4 * 16; + ctext += 4 * 16; + blocks -= 4; + } + + if(blocks > 0) { + uint8_t pbuf[4 * 16] = {0}; + uint8_t cbuf[4 * 16] = {0}; + copy_mem(pbuf, ptext, blocks * 16); + SEED_HWAES::encrypt_4(pbuf, cbuf, m_K); + copy_mem(ctext, cbuf, blocks * 16); + } +} + +void BOTAN_FN_ISA_HWAES SEED::hwaes_decrypt(const uint8_t ctext[], uint8_t ptext[], size_t blocks) const { + while(blocks >= 4) { + SEED_HWAES::decrypt_4(ctext, ptext, m_K); + ptext += 4 * 16; + ctext += 4 * 16; + blocks -= 4; + } + + if(blocks > 0) { + uint8_t cbuf[4 * 16] = {0}; + uint8_t pbuf[4 * 16] = {0}; + copy_mem(cbuf, ctext, blocks * 16); + SEED_HWAES::decrypt_4(cbuf, pbuf, m_K); + copy_mem(ptext, pbuf, blocks * 16); + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/serpent/serpent.cpp botan3-3.12.0+dfsg/src/lib/block/serpent/serpent.cpp --- botan3-3.7.1+dfsg/src/lib/block/serpent/serpent.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/serpent/serpent.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,7 +11,7 @@ #include #include -#if defined(BOTAN_HAS_SERPENT_SIMD) || defined(BOTAN_HAS_SERPENT_AVX2) || defined(BOTAN_HAS_SERPENT_AVX512) +#if defined(BOTAN_HAS_CPUID) #include #endif @@ -26,7 +26,7 @@ assert_key_material_set(); #if defined(BOTAN_HAS_SERPENT_AVX512) - if(CPUID::has_avx512()) { + if(CPUID::has(CPUID::Feature::AVX512)) { while(blocks >= 16) { avx512_encrypt_16(in, out); in += 16 * BLOCK_SIZE; @@ -37,7 +37,7 @@ #endif #if defined(BOTAN_HAS_SERPENT_AVX2) - if(CPUID::has_avx2()) { + if(CPUID::has(CPUID::Feature::AVX2)) { while(blocks >= 8) { avx2_encrypt_8(in, out); in += 8 * BLOCK_SIZE; @@ -48,7 +48,7 @@ #endif #if defined(BOTAN_HAS_SERPENT_SIMD) - if(CPUID::has_simd_32()) { + if(CPUID::has(CPUID::Feature::SIMD_4X32)) { while(blocks >= 4) { simd_encrypt_4(in, out); in += 4 * BLOCK_SIZE; @@ -61,7 +61,10 @@ const Key_Inserter key_xor(m_round_key.data()); for(size_t i = 0; i < blocks; ++i) { - uint32_t B0, B1, B2, B3; + uint32_t B0 = 0; + uint32_t B1 = 0; + uint32_t B2 = 0; + uint32_t B3 = 0; load_le(in + 16 * i, B0, B1, B2, B3); key_xor(0, B0, B1, B2, B3); @@ -174,7 +177,7 @@ assert_key_material_set(); #if defined(BOTAN_HAS_SERPENT_AVX512) - if(CPUID::has_avx512()) { + if(CPUID::has(CPUID::Feature::AVX512)) { while(blocks >= 16) { avx512_decrypt_16(in, out); in += 16 * BLOCK_SIZE; @@ -185,7 +188,7 @@ #endif #if defined(BOTAN_HAS_SERPENT_AVX2) - if(CPUID::has_avx2()) { + if(CPUID::has(CPUID::Feature::AVX2)) { while(blocks >= 8) { avx2_decrypt_8(in, out); in += 8 * BLOCK_SIZE; @@ -196,7 +199,7 @@ #endif #if defined(BOTAN_HAS_SERPENT_SIMD) - if(CPUID::has_simd_32()) { + if(CPUID::has(CPUID::Feature::SIMD_4X32)) { while(blocks >= 4) { simd_decrypt_4(in, out); in += 4 * BLOCK_SIZE; @@ -209,7 +212,10 @@ const Key_Inserter key_xor(m_round_key.data()); for(size_t i = 0; i < blocks; ++i) { - uint32_t B0, B1, B2, B3; + uint32_t B0 = 0; + uint32_t B1 = 0; + uint32_t B2 = 0; + uint32_t B3 = 0; load_le(in + 16 * i, B0, B1, B2, B3); key_xor(32, B0, B1, B2, B3); @@ -333,7 +339,7 @@ W[key.size() / 4] |= uint32_t(1) << ((key.size() % 4) * 8); for(size_t i = 8; i != 140; ++i) { - uint32_t wi = W[i - 8] ^ W[i - 5] ^ W[i - 3] ^ W[i - 1] ^ PHI ^ uint32_t(i - 8); + const uint32_t wi = W[i - 8] ^ W[i - 5] ^ W[i - 3] ^ W[i - 1] ^ PHI ^ uint32_t(i - 8); W[i] = rotl<11>(wi); } @@ -387,20 +393,20 @@ std::string Serpent::provider() const { #if defined(BOTAN_HAS_SERPENT_AVX512) - if(CPUID::has_avx512()) { - return "avx512"; + if(auto feat = CPUID::check(CPUID::Feature::AVX512)) { + return *feat; } #endif #if defined(BOTAN_HAS_SERPENT_AVX2) - if(CPUID::has_avx2()) { - return "avx2"; + if(auto feat = CPUID::check(CPUID::Feature::AVX2)) { + return *feat; } #endif #if defined(BOTAN_HAS_SERPENT_SIMD) - if(CPUID::has_simd_32()) { - return "simd"; + if(auto feat = CPUID::check(CPUID::Feature::SIMD_4X32)) { + return *feat; } #endif diff -Nru botan3-3.7.1+dfsg/src/lib/block/serpent/serpent.h botan3-3.12.0+dfsg/src/lib/block/serpent/serpent.h --- botan3-3.7.1+dfsg/src/lib/block/serpent/serpent.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/serpent/serpent.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #define BOTAN_SERPENT_H_ #include +#include namespace Botan { diff -Nru botan3-3.7.1+dfsg/src/lib/block/serpent/serpent_avx2/info.txt botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_avx2/info.txt --- botan3-3.7.1+dfsg/src/lib/block/serpent/serpent_avx2/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_avx2/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + SERPENT_AVX2 -> 20180824 - + name -> "Serpent AVX2" @@ -12,10 +12,6 @@ +cpuid simd_avx2 - -# MSVC 2019 miscompiles this code (see #2120) - -!msvc - diff -Nru botan3-3.7.1+dfsg/src/lib/block/serpent/serpent_avx2/serpent_avx2.cpp botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_avx2/serpent_avx2.cpp --- botan3-3.7.1+dfsg/src/lib/block/serpent/serpent_avx2/serpent_avx2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_avx2/serpent_avx2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,7 +11,8 @@ namespace Botan { -#if defined(__GNUG__) && !defined(__clang__) +// TODO(Botan4) if minimum GCC is increased we can remove this +#if defined(__GNUG__) && !defined(__clang__) && (__GNUG__ < 13) // These macros are redundant with the versions in serpent_sbox.h // but unfortunately removing them seems to trigger a bug in GCC @@ -47,8 +48,7 @@ #endif -BOTAN_AVX2_FN -void Serpent::avx2_encrypt_8(const uint8_t in[128], uint8_t out[128]) const { +void BOTAN_FN_ISA_AVX2 Serpent::avx2_encrypt_8(const uint8_t in[128], uint8_t out[128]) const { using namespace Botan::Serpent_F; SIMD_8x32::reset_registers(); @@ -171,8 +171,7 @@ SIMD_8x32::zero_registers(); } -BOTAN_AVX2_FN -void Serpent::avx2_decrypt_8(const uint8_t in[128], uint8_t out[128]) const { +void BOTAN_FN_ISA_AVX2 Serpent::avx2_decrypt_8(const uint8_t in[128], uint8_t out[128]) const { using namespace Botan::Serpent_F; SIMD_8x32::reset_registers(); @@ -296,6 +295,7 @@ SIMD_8x32::zero_registers(); } +// TODO(Botan4) remove when compiler hack above is removed #undef transform #undef i_transform diff -Nru botan3-3.7.1+dfsg/src/lib/block/serpent/serpent_avx512/info.txt botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_avx512/info.txt --- botan3-3.7.1+dfsg/src/lib/block/serpent/serpent_avx512/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_avx512/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + SERPENT_AVX512 -> 20230101 - + name -> "Serpent AVX512" @@ -12,10 +12,11 @@ +cpuid simd_avx512 -# MSVC miscompiles this code +# MSVC miscompiles this code !msvc diff -Nru botan3-3.7.1+dfsg/src/lib/block/serpent/serpent_avx512/serpent_avx512.cpp botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_avx512/serpent_avx512.cpp --- botan3-3.7.1+dfsg/src/lib/block/serpent/serpent_avx512/serpent_avx512.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_avx512/serpent_avx512.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -5,11 +5,49 @@ */ #include -#include + +#include #include namespace Botan { +// TODO(Botan4) if minimum GCC is increased we can remove this +#if defined(__GNUG__) && !defined(__clang__) && (__GNUG__ < 14) + +// These macros are redundant with the versions in serpent_sbox.h +// but unfortunately removing them seems to trigger a bug in GCC +// when building in amalgamation mode + + #define transform(B0, B1, B2, B3) \ + do { \ + B0 = B0.rotl<13>(); \ + B2 = B2.rotl<3>(); \ + B1 ^= B0 ^ B2; \ + B3 ^= B2 ^ B0.shl<3>(); \ + B1 = B1.rotl<1>(); \ + B3 = B3.rotl<7>(); \ + B0 ^= B1 ^ B3; \ + B2 ^= B3 ^ B1.shl<7>(); \ + B0 = B0.rotl<5>(); \ + B2 = B2.rotl<22>(); \ + } while(0) + + #define i_transform(B0, B1, B2, B3) \ + do { \ + B2 = B2.rotr<22>(); \ + B0 = B0.rotr<5>(); \ + B2 ^= B3 ^ B1.shl<7>(); \ + B0 ^= B1 ^ B3; \ + B3 = B3.rotr<7>(); \ + B1 = B1.rotr<1>(); \ + B3 ^= B2 ^ B0.shl<3>(); \ + B1 ^= B0 ^ B2; \ + B2 = B2.rotr<3>(); \ + B0 = B0.rotr<13>(); \ + } while(0) + +#endif + namespace { BOTAN_FORCE_INLINE void SBoxE0(SIMD_16x32& a, SIMD_16x32& b, SIMD_16x32& c, SIMD_16x32& d) { @@ -265,8 +303,7 @@ } // namespace -BOTAN_AVX512_FN -void Serpent::avx512_encrypt_16(const uint8_t in[16 * 16], uint8_t out[16 * 16]) const { +void BOTAN_FN_ISA_AVX512 Serpent::avx512_encrypt_16(const uint8_t in[16 * 16], uint8_t out[16 * 16]) const { using namespace Botan::Serpent_F; SIMD_16x32 B0 = SIMD_16x32::load_le(in); @@ -387,8 +424,7 @@ SIMD_16x32::zero_registers(); } -BOTAN_AVX512_FN -void Serpent::avx512_decrypt_16(const uint8_t in[16 * 16], uint8_t out[16 * 16]) const { +void BOTAN_FN_ISA_AVX512 Serpent::avx512_decrypt_16(const uint8_t in[16 * 16], uint8_t out[16 * 16]) const { using namespace Botan::Serpent_F; SIMD_16x32 B0 = SIMD_16x32::load_le(in); @@ -510,4 +546,8 @@ SIMD_16x32::zero_registers(); } +// TODO(Botan4) remove when compiler hack above is removed +#undef transform +#undef i_transform + } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/serpent/serpent_fn.h botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_fn.h --- botan3-3.7.1+dfsg/src/lib/block/serpent/serpent_fn.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_fn.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,9 +10,19 @@ #include #include #include +#include namespace Botan::Serpent_F { +// Concept for types that support bitwise operations (unsigned integers or SIMD types) +template +concept BitsliceT = requires(T& a, const T& b) { + a ^= b; + a &= b; + a |= b; + ~a; +}; + template BOTAN_FORCE_INLINE uint32_t shl(uint32_t v) { return v << S; @@ -21,7 +31,7 @@ /* * Serpent's Linear Transform */ -template +template BOTAN_FORCE_INLINE void transform(T& B0, T& B1, T& B2, T& B3) { B0 = rotl<13>(B0); B2 = rotl<3>(B2); @@ -38,7 +48,7 @@ /* * Serpent's Inverse Linear Transform */ -template +template BOTAN_FORCE_INLINE void i_transform(T& B0, T& B1, T& B2, T& B3) { B2 = rotr<22>(B2); B0 = rotr<5>(B0); @@ -54,10 +64,10 @@ class Key_Inserter final { public: - Key_Inserter(const uint32_t* RK) : m_RK(RK) {} + explicit Key_Inserter(const uint32_t* RK) : m_RK(RK) {} - template - inline void operator()(size_t R, T& B0, T& B1, T& B2, T& B3) const { + template + BOTAN_FORCE_INLINE void operator()(size_t R, T& B0, T& B1, T& B2, T& B3) const { B0 ^= m_RK[4 * R]; B1 ^= m_RK[4 * R + 1]; B2 ^= m_RK[4 * R + 2]; diff -Nru botan3-3.7.1+dfsg/src/lib/block/serpent/serpent_sbox.h botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_sbox.h --- botan3-3.7.1+dfsg/src/lib/block/serpent/serpent_sbox.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_sbox.h 2026-05-07 01:38:28.000000000 +0000 @@ -15,7 +15,7 @@ namespace Botan::Serpent_F { -template +template BOTAN_FORCE_INLINE void SBoxE0(T& a, T& b, T& c, T& d) { d ^= a; T t0 = b; @@ -40,7 +40,7 @@ b = t0; } -template +template BOTAN_FORCE_INLINE void SBoxE1(T& a, T& b, T& c, T& d) { a = ~a; c = ~c; @@ -66,7 +66,7 @@ b = t0; } -template +template BOTAN_FORCE_INLINE void SBoxE2(T& a, T& b, T& c, T& d) { T t0 = a; a &= c; @@ -89,7 +89,7 @@ d = ~t0; } -template +template BOTAN_FORCE_INLINE void SBoxE3(T& a, T& b, T& c, T& d) { T t0 = a; a |= d; @@ -115,7 +115,7 @@ d = t0; } -template +template BOTAN_FORCE_INLINE void SBoxE4(T& a, T& b, T& c, T& d) { b ^= d; d = ~d; @@ -142,7 +142,7 @@ b = t0; } -template +template BOTAN_FORCE_INLINE void SBoxE5(T& a, T& b, T& c, T& d) { a ^= b; b ^= d; @@ -169,7 +169,7 @@ d = t0; } -template +template BOTAN_FORCE_INLINE void SBoxE6(T& a, T& b, T& c, T& d) { c = ~c; T t0 = d; @@ -192,7 +192,7 @@ c = t0; } -template +template BOTAN_FORCE_INLINE void SBoxE7(T& a, T& b, T& c, T& d) { T t0 = b; b |= c; @@ -220,7 +220,7 @@ a = t0; } -template +template BOTAN_FORCE_INLINE void SBoxD0(T& a, T& b, T& c, T& d) { c = ~c; T t0 = b; @@ -245,7 +245,7 @@ b = t0; } -template +template BOTAN_FORCE_INLINE void SBoxD1(T& a, T& b, T& c, T& d) { T t0 = b; b ^= d; @@ -273,7 +273,7 @@ d = t0; } -template +template BOTAN_FORCE_INLINE void SBoxD2(T& a, T& b, T& c, T& d) { c ^= d; d ^= a; @@ -298,7 +298,7 @@ b = t0; } -template +template BOTAN_FORCE_INLINE void SBoxD3(T& a, T& b, T& c, T& d) { T t0 = c; c ^= b; @@ -324,7 +324,7 @@ d = t0; } -template +template BOTAN_FORCE_INLINE void SBoxD4(T& a, T& b, T& c, T& d) { T t0 = c; c &= d; @@ -350,7 +350,7 @@ d = t0; } -template +template BOTAN_FORCE_INLINE void SBoxD5(T& a, T& b, T& c, T& d) { b = ~b; T t0 = d; @@ -378,7 +378,7 @@ c = t0; } -template +template BOTAN_FORCE_INLINE void SBoxD6(T& a, T& b, T& c, T& d) { a ^= c; T t0 = c; @@ -402,7 +402,7 @@ c = t0; } -template +template BOTAN_FORCE_INLINE void SBoxD7(T& a, T& b, T& c, T& d) { T t0 = c; c ^= a; diff -Nru botan3-3.7.1+dfsg/src/lib/block/serpent/serpent_simd/info.txt botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_simd/info.txt --- botan3-3.7.1+dfsg/src/lib/block/serpent/serpent_simd/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_simd/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + SERPENT_SIMD -> 20160903 - + name -> "Serpent SIMD" @@ -8,5 +8,18 @@ -simd +cpuid +simd_4x32 + + +x86_32:ssse3 +x86_64:ssse3 +x32:ssse3 +arm32:neon +arm64:neon +ppc32:altivec +ppc64:altivec +loongarch64:lsx +wasm:simd128 + diff -Nru botan3-3.7.1+dfsg/src/lib/block/serpent/serpent_simd/serpent_simd.cpp botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_simd/serpent_simd.cpp --- botan3-3.7.1+dfsg/src/lib/block/serpent/serpent_simd/serpent_simd.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/serpent/serpent_simd/serpent_simd.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,14 +8,14 @@ #include #include -#include +#include namespace Botan { /* * SIMD Serpent Encryption of 4 blocks in parallel */ -void Serpent::simd_encrypt_4(const uint8_t in[64], uint8_t out[64]) const { +void BOTAN_FN_ISA_SIMD_4X32 Serpent::simd_encrypt_4(const uint8_t in[64], uint8_t out[64]) const { using namespace Botan::Serpent_F; SIMD_4x32 B0 = SIMD_4x32::load_le(in); @@ -138,7 +138,7 @@ /* * SIMD Serpent Decryption of 4 blocks in parallel */ -void Serpent::simd_decrypt_4(const uint8_t in[64], uint8_t out[64]) const { +void BOTAN_FN_ISA_SIMD_4X32 Serpent::simd_decrypt_4(const uint8_t in[64], uint8_t out[64]) const { using namespace Botan::Serpent_F; SIMD_4x32 B0 = SIMD_4x32::load_le(in); diff -Nru botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2.cpp botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2.cpp --- botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,10 +8,13 @@ #include #include -#include #include #include +#if defined(BOTAN_HAS_CPUID) + #include +#endif + namespace Botan { namespace { @@ -44,20 +47,29 @@ void SHACAL2::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); +#if defined(BOTAN_HAS_SHACAL2_AVX512) + if(CPUID::has(CPUID::Feature::AVX512)) { + const size_t consumed = avx512_encrypt_blocks(in, out, blocks); + in += consumed * BLOCK_SIZE; + out += consumed * BLOCK_SIZE; + blocks -= consumed; + } +#endif + #if defined(BOTAN_HAS_SHACAL2_X86) - if(CPUID::has_intel_sha()) { + if(CPUID::has(CPUID::Feature::SHA)) { return x86_encrypt_blocks(in, out, blocks); } #endif #if defined(BOTAN_HAS_SHACAL2_ARMV8) - if(CPUID::has_arm_sha2()) { + if(CPUID::has(CPUID::Feature::SHA2)) { return armv8_encrypt_blocks(in, out, blocks); } #endif #if defined(BOTAN_HAS_SHACAL2_AVX2) - if(CPUID::has_avx2()) { + if(CPUID::has(CPUID::Feature::AVX2)) { while(blocks >= 8) { avx2_encrypt_8(in, out); in += 8 * BLOCK_SIZE; @@ -68,7 +80,7 @@ #endif #if defined(BOTAN_HAS_SHACAL2_SIMD) - if(CPUID::has_simd_32()) { + if(CPUID::has(CPUID::Feature::SIMD_4X32)) { while(blocks >= 4) { simd_encrypt_4(in, out); in += 4 * BLOCK_SIZE; @@ -112,8 +124,17 @@ void SHACAL2::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); +#if defined(BOTAN_HAS_SHACAL2_AVX512) + if(CPUID::has(CPUID::Feature::AVX512)) { + const size_t consumed = avx512_decrypt_blocks(in, out, blocks); + in += consumed * BLOCK_SIZE; + out += consumed * BLOCK_SIZE; + blocks -= consumed; + } +#endif + #if defined(BOTAN_HAS_SHACAL2_AVX2) - if(CPUID::has_avx2()) { + if(CPUID::has(CPUID::Feature::AVX2)) { while(blocks >= 8) { avx2_decrypt_8(in, out); in += 8 * BLOCK_SIZE; @@ -124,7 +145,7 @@ #endif #if defined(BOTAN_HAS_SHACAL2_SIMD) - if(CPUID::has_simd_32()) { + if(CPUID::has(CPUID::Feature::SIMD_4X32)) { while(blocks >= 4) { simd_decrypt_4(in, out); in += 4 * BLOCK_SIZE; @@ -200,26 +221,32 @@ } size_t SHACAL2::parallelism() const { +#if defined(BOTAN_HAS_SHACAL2_AVX512) + if(CPUID::has(CPUID::Feature::AVX512)) { + return 16; + } +#endif + #if defined(BOTAN_HAS_SHACAL2_X86) - if(CPUID::has_intel_sha()) { + if(CPUID::has(CPUID::Feature::SHA)) { return 2; } #endif #if defined(BOTAN_HAS_SHACAL2_ARMV8) - if(CPUID::has_arm_sha2()) { + if(CPUID::has(CPUID::Feature::SHA2)) { return 2; } #endif #if defined(BOTAN_HAS_SHACAL2_AVX2) - if(CPUID::has_avx2()) { + if(CPUID::has(CPUID::Feature::AVX2)) { return 8; } #endif #if defined(BOTAN_HAS_SHACAL2_SIMD) - if(CPUID::has_simd_32()) { + if(CPUID::has(CPUID::Feature::SIMD_4X32)) { return 4; } #endif @@ -228,27 +255,33 @@ } std::string SHACAL2::provider() const { +#if defined(BOTAN_HAS_SHACAL2_AVX512) + if(auto feat = CPUID::check(CPUID::Feature::AVX512)) { + return *feat; + } +#endif + #if defined(BOTAN_HAS_SHACAL2_X86) - if(CPUID::has_intel_sha()) { - return "intel_sha"; + if(auto feat = CPUID::check(CPUID::Feature::SHA)) { + return *feat; } #endif #if defined(BOTAN_HAS_SHACAL2_ARMV8) - if(CPUID::has_arm_sha2()) { - return "armv8_sha2"; + if(auto feat = CPUID::check(CPUID::Feature::SHA2)) { + return *feat; } #endif #if defined(BOTAN_HAS_SHACAL2_AVX2) - if(CPUID::has_avx2()) { - return "avx2"; + if(auto feat = CPUID::check(CPUID::Feature::AVX2)) { + return *feat; } #endif #if defined(BOTAN_HAS_SHACAL2_SIMD) - if(CPUID::has_simd_32()) { - return "simd"; + if(auto feat = CPUID::check(CPUID::Feature::SIMD_4X32)) { + return *feat; } #endif diff -Nru botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2.h botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2.h --- botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #define BOTAN_SHACAL2_H_ #include +#include namespace Botan { @@ -43,6 +44,11 @@ void avx2_decrypt_8(const uint8_t in[], uint8_t out[]) const; #endif +#if defined(BOTAN_HAS_SHACAL2_AVX512) + size_t avx512_encrypt_blocks(const uint8_t in[], uint8_t out[], size_t blocks) const; + size_t avx512_decrypt_blocks(const uint8_t in[], uint8_t out[], size_t blocks) const; +#endif + #if defined(BOTAN_HAS_SHACAL2_X86) void x86_encrypt_blocks(const uint8_t in[], uint8_t out[], size_t blocks) const; #endif diff -Nru botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2_armv8/info.txt botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_armv8/info.txt --- botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2_armv8/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_armv8/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + SHACAL2_ARMV8 -> 20201221 - + name -> "SHACAL-2 ARMv8" @@ -8,7 +8,7 @@ -shacal2 +cpuid diff -Nru botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2_armv8/shacal2_arvm8.cpp botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_armv8/shacal2_arvm8.cpp --- botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2_armv8/shacal2_arvm8.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_armv8/shacal2_arvm8.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,7 +6,7 @@ #include -#include +#include #include namespace Botan { @@ -15,7 +15,7 @@ Only encryption is supported since the inverse round function would require a different instruction */ -BOTAN_FUNC_ISA("+crypto+sha2") +BOTAN_FN_ISA_SHA2 void SHACAL2::armv8_encrypt_blocks(const uint8_t in[], uint8_t out[], size_t blocks) const { const uint32_t* input32 = reinterpret_cast(in); uint32_t* output32 = reinterpret_cast(out); diff -Nru botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2_avx2/info.txt botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_avx2/info.txt --- botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2_avx2/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_avx2/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + SHACAL2_AVX2 -> 20180826 - + name -> "SHACAL-2 AVX2" @@ -12,5 +12,6 @@ +cpuid simd_avx2 diff -Nru botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2_avx2/shacal2_avx2.cpp botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_avx2/shacal2_avx2.cpp --- botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2_avx2/shacal2_avx2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_avx2/shacal2_avx2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,31 +10,33 @@ namespace Botan { +namespace SHACAL2_AVX2_F { + namespace { -void BOTAN_FORCE_INLINE BOTAN_AVX2_FN SHACAL2_Fwd(const SIMD_8x32& A, - const SIMD_8x32& B, - const SIMD_8x32& C, - SIMD_8x32& D, - const SIMD_8x32& E, - const SIMD_8x32& F, - const SIMD_8x32& G, - SIMD_8x32& H, - uint32_t RK) { +void BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2 SHACAL2_Fwd(const SIMD_8x32& A, + const SIMD_8x32& B, + const SIMD_8x32& C, + SIMD_8x32& D, + const SIMD_8x32& E, + const SIMD_8x32& F, + const SIMD_8x32& G, + SIMD_8x32& H, + uint32_t RK) { H += E.sigma1() + SIMD_8x32::choose(E, F, G) + SIMD_8x32::splat(RK); D += H; H += A.sigma0() + SIMD_8x32::majority(A, B, C); } -void BOTAN_FORCE_INLINE BOTAN_AVX2_FN SHACAL2_Rev(const SIMD_8x32& A, - const SIMD_8x32& B, - const SIMD_8x32& C, - SIMD_8x32& D, - const SIMD_8x32& E, - const SIMD_8x32& F, - const SIMD_8x32& G, - SIMD_8x32& H, - uint32_t RK) { +void BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2 SHACAL2_Rev(const SIMD_8x32& A, + const SIMD_8x32& B, + const SIMD_8x32& C, + SIMD_8x32& D, + const SIMD_8x32& E, + const SIMD_8x32& F, + const SIMD_8x32& G, + SIMD_8x32& H, + uint32_t RK) { H -= A.sigma0() + SIMD_8x32::majority(A, B, C); D -= H; H -= E.sigma1() + SIMD_8x32::choose(E, F, G) + SIMD_8x32::splat(RK); @@ -42,7 +44,11 @@ } // namespace -void BOTAN_AVX2_FN SHACAL2::avx2_encrypt_8(const uint8_t in[], uint8_t out[]) const { +} // namespace SHACAL2_AVX2_F + +void BOTAN_FN_ISA_AVX2 SHACAL2::avx2_encrypt_8(const uint8_t in[], uint8_t out[]) const { + using namespace SHACAL2_AVX2_F; + SIMD_8x32::reset_registers(); SIMD_8x32 A = SIMD_8x32::load_be(in); @@ -83,7 +89,9 @@ SIMD_8x32::zero_registers(); } -BOTAN_AVX2_FN void SHACAL2::avx2_decrypt_8(const uint8_t in[], uint8_t out[]) const { +void BOTAN_FN_ISA_AVX2 SHACAL2::avx2_decrypt_8(const uint8_t in[], uint8_t out[]) const { + using namespace SHACAL2_AVX2_F; + SIMD_8x32::reset_registers(); SIMD_8x32 A = SIMD_8x32::load_be(in); diff -Nru botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2_avx512/info.txt botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_avx512/info.txt --- botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2_avx512/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_avx512/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,18 @@ + +SHACAL2_AVX512 -> 20250516 + + + +name -> "SHACAL-2 AVX512" +brief -> "SHACAL-2 using AVX512 instructions" + + + +avx512 + + + +cpuid +simd_avx512 +simd_avx2 + diff -Nru botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2_avx512/shacal2_avx512.cpp botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_avx512/shacal2_avx512.cpp --- botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2_avx512/shacal2_avx512.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_avx512/shacal2_avx512.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,337 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan { + +namespace SHACAL2_AVX512_F { + +namespace { + +// NOLINTBEGIN(portability-simd-intrinsics) + +/* +* 8x16 Transpose +* +* Convert from +* +* A00 B00 C00 ... H00 +* A01 B01 C01 ... H01 +* .. +* A15 B15 C15 ... H15 +* +* with two blocks stored in each register, into +* +* A00 A01 ... A15 +* B00 B01 ... B15 +* ... +* H00 H01 ... H15 +*/ +BOTAN_FN_ISA_AVX512 +void transpose_in(SIMD_16x32& B0, + SIMD_16x32& B1, + SIMD_16x32& B2, + SIMD_16x32& B3, + SIMD_16x32& B4, + SIMD_16x32& B5, + SIMD_16x32& B6, + SIMD_16x32& B7) { + auto t0 = _mm512_unpacklo_epi32(B0.raw(), B1.raw()); + auto t1 = _mm512_unpackhi_epi32(B0.raw(), B1.raw()); + auto t2 = _mm512_unpacklo_epi32(B2.raw(), B3.raw()); + auto t3 = _mm512_unpackhi_epi32(B2.raw(), B3.raw()); + auto t4 = _mm512_unpacklo_epi32(B4.raw(), B5.raw()); + auto t5 = _mm512_unpackhi_epi32(B4.raw(), B5.raw()); + auto t6 = _mm512_unpacklo_epi32(B6.raw(), B7.raw()); + auto t7 = _mm512_unpackhi_epi32(B6.raw(), B7.raw()); + + auto r0 = _mm512_unpacklo_epi64(t0, t2); + auto r1 = _mm512_unpackhi_epi64(t0, t2); + auto r2 = _mm512_unpacklo_epi64(t1, t3); + auto r3 = _mm512_unpackhi_epi64(t1, t3); + auto r4 = _mm512_unpacklo_epi64(t4, t6); + auto r5 = _mm512_unpackhi_epi64(t4, t6); + auto r6 = _mm512_unpacklo_epi64(t5, t7); + auto r7 = _mm512_unpackhi_epi64(t5, t7); + + const __m512i tbl0 = _mm512_set_epi32(27, 19, 26, 18, 25, 17, 24, 16, 11, 3, 10, 2, 9, 1, 8, 0); + const __m512i tbl1 = _mm512_add_epi32(tbl0, _mm512_set1_epi32(4)); + B0 = SIMD_16x32(_mm512_permutex2var_epi32(r0, tbl0, r4)); + B1 = SIMD_16x32(_mm512_permutex2var_epi32(r1, tbl0, r5)); + B2 = SIMD_16x32(_mm512_permutex2var_epi32(r2, tbl0, r6)); + B3 = SIMD_16x32(_mm512_permutex2var_epi32(r3, tbl0, r7)); + B4 = SIMD_16x32(_mm512_permutex2var_epi32(r0, tbl1, r4)); + B5 = SIMD_16x32(_mm512_permutex2var_epi32(r1, tbl1, r5)); + B6 = SIMD_16x32(_mm512_permutex2var_epi32(r2, tbl1, r6)); + B7 = SIMD_16x32(_mm512_permutex2var_epi32(r3, tbl1, r7)); +} + +BOTAN_FN_ISA_AVX512 +void transpose_out(SIMD_16x32& B0, + SIMD_16x32& B1, + SIMD_16x32& B2, + SIMD_16x32& B3, + SIMD_16x32& B4, + SIMD_16x32& B5, + SIMD_16x32& B6, + SIMD_16x32& B7) { + auto t0 = _mm512_unpacklo_epi32(B0.raw(), B1.raw()); + auto t1 = _mm512_unpackhi_epi32(B0.raw(), B1.raw()); + auto t2 = _mm512_unpacklo_epi32(B2.raw(), B3.raw()); + auto t3 = _mm512_unpackhi_epi32(B2.raw(), B3.raw()); + auto t4 = _mm512_unpacklo_epi32(B4.raw(), B5.raw()); + auto t5 = _mm512_unpackhi_epi32(B4.raw(), B5.raw()); + auto t6 = _mm512_unpacklo_epi32(B6.raw(), B7.raw()); + auto t7 = _mm512_unpackhi_epi32(B6.raw(), B7.raw()); + + auto r0 = _mm512_unpacklo_epi64(t0, t2); + auto r1 = _mm512_unpackhi_epi64(t0, t2); + auto r2 = _mm512_unpacklo_epi64(t1, t3); + auto r3 = _mm512_unpackhi_epi64(t1, t3); + auto r4 = _mm512_unpacklo_epi64(t4, t6); + auto r5 = _mm512_unpackhi_epi64(t4, t6); + auto r6 = _mm512_unpacklo_epi64(t5, t7); + auto r7 = _mm512_unpackhi_epi64(t5, t7); + + const __m512i tbl0 = _mm512_set_epi32(23, 22, 21, 20, 7, 6, 5, 4, 19, 18, 17, 16, 3, 2, 1, 0); + const __m512i tbl1 = _mm512_add_epi32(tbl0, _mm512_set1_epi32(8)); + + auto s0 = _mm512_permutex2var_epi32(r0, tbl0, r4); + auto s1 = _mm512_permutex2var_epi32(r1, tbl0, r5); + auto s2 = _mm512_permutex2var_epi32(r2, tbl0, r6); + auto s3 = _mm512_permutex2var_epi32(r3, tbl0, r7); + auto s4 = _mm512_permutex2var_epi32(r0, tbl1, r4); + auto s5 = _mm512_permutex2var_epi32(r1, tbl1, r5); + auto s6 = _mm512_permutex2var_epi32(r2, tbl1, r6); + auto s7 = _mm512_permutex2var_epi32(r3, tbl1, r7); + + B0 = SIMD_16x32(_mm512_shuffle_i32x4(s0, s1, 0b01000100)); + B1 = SIMD_16x32(_mm512_shuffle_i32x4(s2, s3, 0b01000100)); + B2 = SIMD_16x32(_mm512_shuffle_i32x4(s0, s1, 0b11101110)); + B3 = SIMD_16x32(_mm512_shuffle_i32x4(s2, s3, 0b11101110)); + B4 = SIMD_16x32(_mm512_shuffle_i32x4(s4, s5, 0b01000100)); + B5 = SIMD_16x32(_mm512_shuffle_i32x4(s6, s7, 0b01000100)); + B6 = SIMD_16x32(_mm512_shuffle_i32x4(s4, s5, 0b11101110)); + B7 = SIMD_16x32(_mm512_shuffle_i32x4(s6, s7, 0b11101110)); +} + +// NOLINTEND(portability-simd-intrinsics) + +template +void BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512 SHACAL2_Fwd(const SimdT& A, + const SimdT& B, + const SimdT& C, + SimdT& D, + const SimdT& E, + const SimdT& F, + const SimdT& G, + SimdT& H, + uint32_t RK) { + H += E.sigma1() + SimdT::choose(E, F, G) + SimdT::splat(RK); + D += H; + H += A.sigma0() + SimdT::majority(A, B, C); +} + +template +void BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512 SHACAL2_Rev(const SimdT& A, + const SimdT& B, + const SimdT& C, + SimdT& D, + const SimdT& E, + const SimdT& F, + const SimdT& G, + SimdT& H, + uint32_t RK) { + H -= A.sigma0() + SimdT::majority(A, B, C); + D -= H; + H -= E.sigma1() + SimdT::choose(E, F, G) + SimdT::splat(RK); +} + +} // namespace + +} // namespace SHACAL2_AVX512_F + +size_t BOTAN_FN_ISA_AVX512 SHACAL2::avx512_encrypt_blocks(const uint8_t in[], uint8_t out[], size_t blocks) const { + using namespace SHACAL2_AVX512_F; + + size_t consumed = 0; + + while(blocks >= 16) { + SIMD_16x32 A = SIMD_16x32::load_be(in + 64 * 0); + SIMD_16x32 B = SIMD_16x32::load_be(in + 64 * 1); + SIMD_16x32 C = SIMD_16x32::load_be(in + 64 * 2); + SIMD_16x32 D = SIMD_16x32::load_be(in + 64 * 3); + SIMD_16x32 E = SIMD_16x32::load_be(in + 64 * 4); + SIMD_16x32 F = SIMD_16x32::load_be(in + 64 * 5); + SIMD_16x32 G = SIMD_16x32::load_be(in + 64 * 6); + SIMD_16x32 H = SIMD_16x32::load_be(in + 64 * 7); + + transpose_in(A, B, C, D, E, F, G, H); + + for(size_t r = 0; r != 64; r += 8) { + SHACAL2_Fwd(A, B, C, D, E, F, G, H, m_RK[r + 0]); + SHACAL2_Fwd(H, A, B, C, D, E, F, G, m_RK[r + 1]); + SHACAL2_Fwd(G, H, A, B, C, D, E, F, m_RK[r + 2]); + SHACAL2_Fwd(F, G, H, A, B, C, D, E, m_RK[r + 3]); + SHACAL2_Fwd(E, F, G, H, A, B, C, D, m_RK[r + 4]); + SHACAL2_Fwd(D, E, F, G, H, A, B, C, m_RK[r + 5]); + SHACAL2_Fwd(C, D, E, F, G, H, A, B, m_RK[r + 6]); + SHACAL2_Fwd(B, C, D, E, F, G, H, A, m_RK[r + 7]); + } + + transpose_out(A, B, C, D, E, F, G, H); + + A.store_be(out + 64 * 0); + B.store_be(out + 64 * 1); + C.store_be(out + 64 * 2); + D.store_be(out + 64 * 3); + E.store_be(out + 64 * 4); + F.store_be(out + 64 * 5); + G.store_be(out + 64 * 6); + H.store_be(out + 64 * 7); + + in += 16 * BLOCK_SIZE; + out += 16 * BLOCK_SIZE; + blocks -= 16; + consumed += 16; + } + + while(blocks >= 8) { + SIMD_8x32 A = SIMD_8x32::load_be(in + 32 * 0); + SIMD_8x32 B = SIMD_8x32::load_be(in + 32 * 1); + SIMD_8x32 C = SIMD_8x32::load_be(in + 32 * 2); + SIMD_8x32 D = SIMD_8x32::load_be(in + 32 * 3); + SIMD_8x32 E = SIMD_8x32::load_be(in + 32 * 4); + SIMD_8x32 F = SIMD_8x32::load_be(in + 32 * 5); + SIMD_8x32 G = SIMD_8x32::load_be(in + 32 * 6); + SIMD_8x32 H = SIMD_8x32::load_be(in + 32 * 7); + + SIMD_8x32::transpose(A, B, C, D, E, F, G, H); + + for(size_t r = 0; r != 64; r += 8) { + SHACAL2_Fwd(A, B, C, D, E, F, G, H, m_RK[r + 0]); + SHACAL2_Fwd(H, A, B, C, D, E, F, G, m_RK[r + 1]); + SHACAL2_Fwd(G, H, A, B, C, D, E, F, m_RK[r + 2]); + SHACAL2_Fwd(F, G, H, A, B, C, D, E, m_RK[r + 3]); + SHACAL2_Fwd(E, F, G, H, A, B, C, D, m_RK[r + 4]); + SHACAL2_Fwd(D, E, F, G, H, A, B, C, m_RK[r + 5]); + SHACAL2_Fwd(C, D, E, F, G, H, A, B, m_RK[r + 6]); + SHACAL2_Fwd(B, C, D, E, F, G, H, A, m_RK[r + 7]); + } + + SIMD_8x32::transpose(A, B, C, D, E, F, G, H); + + A.store_be(out + 32 * 0); + B.store_be(out + 32 * 1); + C.store_be(out + 32 * 2); + D.store_be(out + 32 * 3); + E.store_be(out + 32 * 4); + F.store_be(out + 32 * 5); + G.store_be(out + 32 * 6); + H.store_be(out + 32 * 7); + + in += 8 * BLOCK_SIZE; + out += 8 * BLOCK_SIZE; + blocks -= 8; + consumed += 8; + } + + return consumed; +} + +size_t BOTAN_FN_ISA_AVX512 SHACAL2::avx512_decrypt_blocks(const uint8_t in[], uint8_t out[], size_t blocks) const { + using namespace SHACAL2_AVX512_F; + + size_t consumed = 0; + + while(blocks >= 16) { + SIMD_16x32 A = SIMD_16x32::load_be(in + 64 * 0); + SIMD_16x32 B = SIMD_16x32::load_be(in + 64 * 1); + SIMD_16x32 C = SIMD_16x32::load_be(in + 64 * 2); + SIMD_16x32 D = SIMD_16x32::load_be(in + 64 * 3); + SIMD_16x32 E = SIMD_16x32::load_be(in + 64 * 4); + SIMD_16x32 F = SIMD_16x32::load_be(in + 64 * 5); + SIMD_16x32 G = SIMD_16x32::load_be(in + 64 * 6); + SIMD_16x32 H = SIMD_16x32::load_be(in + 64 * 7); + + transpose_in(A, B, C, D, E, F, G, H); + + for(size_t r = 0; r != 64; r += 8) { + SHACAL2_Rev(B, C, D, E, F, G, H, A, m_RK[63 - r]); + SHACAL2_Rev(C, D, E, F, G, H, A, B, m_RK[62 - r]); + SHACAL2_Rev(D, E, F, G, H, A, B, C, m_RK[61 - r]); + SHACAL2_Rev(E, F, G, H, A, B, C, D, m_RK[60 - r]); + SHACAL2_Rev(F, G, H, A, B, C, D, E, m_RK[59 - r]); + SHACAL2_Rev(G, H, A, B, C, D, E, F, m_RK[58 - r]); + SHACAL2_Rev(H, A, B, C, D, E, F, G, m_RK[57 - r]); + SHACAL2_Rev(A, B, C, D, E, F, G, H, m_RK[56 - r]); + } + + transpose_out(A, B, C, D, E, F, G, H); + + A.store_be(out + 64 * 0); + B.store_be(out + 64 * 1); + C.store_be(out + 64 * 2); + D.store_be(out + 64 * 3); + E.store_be(out + 64 * 4); + F.store_be(out + 64 * 5); + G.store_be(out + 64 * 6); + H.store_be(out + 64 * 7); + + in += 16 * BLOCK_SIZE; + out += 16 * BLOCK_SIZE; + blocks -= 16; + consumed += 16; + } + + while(blocks >= 8) { + SIMD_8x32 A = SIMD_8x32::load_be(in + 32 * 0); + SIMD_8x32 B = SIMD_8x32::load_be(in + 32 * 1); + SIMD_8x32 C = SIMD_8x32::load_be(in + 32 * 2); + SIMD_8x32 D = SIMD_8x32::load_be(in + 32 * 3); + SIMD_8x32 E = SIMD_8x32::load_be(in + 32 * 4); + SIMD_8x32 F = SIMD_8x32::load_be(in + 32 * 5); + SIMD_8x32 G = SIMD_8x32::load_be(in + 32 * 6); + SIMD_8x32 H = SIMD_8x32::load_be(in + 32 * 7); + + SIMD_8x32::transpose(A, B, C, D, E, F, G, H); + + for(size_t r = 0; r != 64; r += 8) { + SHACAL2_Rev(B, C, D, E, F, G, H, A, m_RK[63 - r]); + SHACAL2_Rev(C, D, E, F, G, H, A, B, m_RK[62 - r]); + SHACAL2_Rev(D, E, F, G, H, A, B, C, m_RK[61 - r]); + SHACAL2_Rev(E, F, G, H, A, B, C, D, m_RK[60 - r]); + SHACAL2_Rev(F, G, H, A, B, C, D, E, m_RK[59 - r]); + SHACAL2_Rev(G, H, A, B, C, D, E, F, m_RK[58 - r]); + SHACAL2_Rev(H, A, B, C, D, E, F, G, m_RK[57 - r]); + SHACAL2_Rev(A, B, C, D, E, F, G, H, m_RK[56 - r]); + } + + SIMD_8x32::transpose(A, B, C, D, E, F, G, H); + + A.store_be(out + 32 * 0); + B.store_be(out + 32 * 1); + C.store_be(out + 32 * 2); + D.store_be(out + 32 * 3); + E.store_be(out + 32 * 4); + F.store_be(out + 32 * 5); + G.store_be(out + 32 * 6); + H.store_be(out + 32 * 7); + + in += 8 * BLOCK_SIZE; + out += 8 * BLOCK_SIZE; + blocks -= 8; + consumed += 8; + } + + return consumed; +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2_simd/info.txt botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_simd/info.txt --- botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2_simd/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_simd/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + SHACAL2_SIMD -> 20170813 - + name -> "SHACAL-2 SIMD" @@ -8,6 +8,18 @@ -shacal2 -simd +cpuid +simd_4x32 + + +x86_32:ssse3 +x86_64:ssse3 +x32:ssse3 +arm32:neon +arm64:neon +ppc32:altivec +ppc64:altivec +loongarch64:lsx +wasm:simd128 + diff -Nru botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2_simd/shacal2_simd.cpp botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_simd/shacal2_simd.cpp --- botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2_simd/shacal2_simd.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_simd/shacal2_simd.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,35 +7,35 @@ #include -#include +#include namespace Botan { namespace { -inline void SHACAL2_Fwd(const SIMD_4x32& A, - const SIMD_4x32& B, - const SIMD_4x32& C, - SIMD_4x32& D, - const SIMD_4x32& E, - const SIMD_4x32& F, - const SIMD_4x32& G, - SIMD_4x32& H, - uint32_t RK) { +inline void BOTAN_FN_ISA_SIMD_4X32 SHACAL2_Fwd(const SIMD_4x32& A, + const SIMD_4x32& B, + const SIMD_4x32& C, + SIMD_4x32& D, + const SIMD_4x32& E, + const SIMD_4x32& F, + const SIMD_4x32& G, + SIMD_4x32& H, + uint32_t RK) { H += E.sigma1() + SIMD_4x32::choose(E, F, G) + SIMD_4x32::splat(RK); D += H; H += A.sigma0() + SIMD_4x32::majority(A, B, C); } -inline void SHACAL2_Rev(const SIMD_4x32& A, - const SIMD_4x32& B, - const SIMD_4x32& C, - SIMD_4x32& D, - const SIMD_4x32& E, - const SIMD_4x32& F, - const SIMD_4x32& G, - SIMD_4x32& H, - uint32_t RK) { +inline void BOTAN_FN_ISA_SIMD_4X32 SHACAL2_Rev(const SIMD_4x32& A, + const SIMD_4x32& B, + const SIMD_4x32& C, + SIMD_4x32& D, + const SIMD_4x32& E, + const SIMD_4x32& F, + const SIMD_4x32& G, + SIMD_4x32& H, + uint32_t RK) { H -= A.sigma0() + SIMD_4x32::majority(A, B, C); D -= H; H -= E.sigma1() + SIMD_4x32::choose(E, F, G) + SIMD_4x32::splat(RK); @@ -43,7 +43,7 @@ } // namespace -void SHACAL2::simd_encrypt_4(const uint8_t in[], uint8_t out[]) const { +void BOTAN_FN_ISA_SIMD_4X32 SHACAL2::simd_encrypt_4(const uint8_t in[], uint8_t out[]) const { SIMD_4x32 A = SIMD_4x32::load_be(in); SIMD_4x32 E = SIMD_4x32::load_be(in + 16); SIMD_4x32 B = SIMD_4x32::load_be(in + 32); @@ -82,7 +82,7 @@ H.store_be(out + 112); } -void SHACAL2::simd_decrypt_4(const uint8_t in[], uint8_t out[]) const { +void BOTAN_FN_ISA_SIMD_4X32 SHACAL2::simd_decrypt_4(const uint8_t in[], uint8_t out[]) const { SIMD_4x32 A = SIMD_4x32::load_be(in); SIMD_4x32 E = SIMD_4x32::load_be(in + 16); SIMD_4x32 B = SIMD_4x32::load_be(in + 32); diff -Nru botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2_x86/info.txt botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_x86/info.txt --- botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2_x86/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_x86/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + SHACAL2_X86 -> 20170814 - + name -> "SHACAL-2 X86" @@ -8,7 +8,7 @@ -shacal2 +cpuid diff -Nru botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2_x86/shacal2_x86.cpp botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_x86/shacal2_x86.cpp --- botan3-3.7.1+dfsg/src/lib/block/shacal2/shacal2_x86/shacal2_x86.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/shacal2/shacal2_x86/shacal2_x86.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,7 +7,7 @@ #include -#include +#include #include namespace Botan { @@ -17,7 +17,9 @@ require a different instruction */ -BOTAN_FUNC_ISA("sha,ssse3") void SHACAL2::x86_encrypt_blocks(const uint8_t in[], uint8_t out[], size_t blocks) const { +void BOTAN_FN_ISA_SHANI SHACAL2::x86_encrypt_blocks(const uint8_t in[], uint8_t out[], size_t blocks) const { + // NOLINTBEGIN(portability-simd-intrinsics) TODO convert to SIMD_4x32 plus SHA-NI helpers + const __m128i MASK1 = _mm_set_epi8(8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7); const __m128i MASK2 = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); @@ -68,11 +70,11 @@ out_mm += 4; } - while(blocks) { + while(blocks > 0) { __m128i B0 = _mm_loadu_si128(in_mm); __m128i B1 = _mm_loadu_si128(in_mm + 1); - __m128i TMP = _mm_shuffle_epi8(_mm_unpacklo_epi64(B0, B1), MASK2); + const __m128i TMP = _mm_shuffle_epi8(_mm_unpacklo_epi64(B0, B1), MASK2); B1 = _mm_shuffle_epi8(_mm_unpackhi_epi64(B0, B1), MASK2); B0 = TMP; @@ -95,6 +97,8 @@ in_mm += 2; out_mm += 2; } + + // NOLINTEND(portability-simd-intrinsics) } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/sm4/sm4.cpp botan3-3.12.0+dfsg/src/lib/block/sm4/sm4.cpp --- botan3-3.7.1+dfsg/src/lib/block/sm4/sm4.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/sm4/sm4.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,10 +8,13 @@ #include -#include #include #include +#if defined(BOTAN_HAS_CPUID) + #include +#endif + namespace Botan { namespace { @@ -162,17 +165,35 @@ assert_key_material_set(); #if defined(BOTAN_HAS_SM4_ARMV8) - if(CPUID::has_arm_sm4()) { + if(CPUID::has(CPUID::Feature::SM4)) { return sm4_armv8_encrypt(in, out, blocks); } #endif +#if defined(BOTAN_HAS_SM4_X86) + if(CPUID::has(CPUID::Feature::SM4)) { + return sm4_x86_encrypt(in, out, blocks); + } +#endif + +#if defined(BOTAN_HAS_SM4_AVX512_GFNI) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return sm4_avx512_gfni_encrypt(in, out, blocks); + } +#endif + #if defined(BOTAN_HAS_SM4_GFNI) - if(CPUID::has_gfni()) { + if(CPUID::has(CPUID::Feature::GFNI)) { return sm4_gfni_encrypt(in, out, blocks); } #endif +#if defined(BOTAN_HAS_SM4_HWAES) + if(CPUID::has(CPUID::Feature::HW_AES)) { + return sm4_hwaes_encrypt(in, out, blocks); + } +#endif + while(blocks >= 2) { uint32_t B0 = load_be(in, 0); uint32_t B1 = load_be(in, 1); @@ -229,17 +250,35 @@ assert_key_material_set(); #if defined(BOTAN_HAS_SM4_ARMV8) - if(CPUID::has_arm_sm4()) { + if(CPUID::has(CPUID::Feature::SM4)) { return sm4_armv8_decrypt(in, out, blocks); } #endif +#if defined(BOTAN_HAS_SM4_X86) + if(CPUID::has(CPUID::Feature::SM4)) { + return sm4_x86_decrypt(in, out, blocks); + } +#endif + +#if defined(BOTAN_HAS_SM4_AVX512_GFNI) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return sm4_avx512_gfni_decrypt(in, out, blocks); + } +#endif + #if defined(BOTAN_HAS_SM4_GFNI) - if(CPUID::has_gfni()) { + if(CPUID::has(CPUID::Feature::GFNI)) { return sm4_gfni_decrypt(in, out, blocks); } #endif +#if defined(BOTAN_HAS_SM4_HWAES) + if(CPUID::has(CPUID::Feature::HW_AES)) { + return sm4_hwaes_decrypt(in, out, blocks); + } +#endif + while(blocks >= 2) { uint32_t B0 = load_be(in, 0); uint32_t B1 = load_be(in, 1); @@ -325,30 +364,54 @@ size_t SM4::parallelism() const { #if defined(BOTAN_HAS_SM4_ARMV8) - if(CPUID::has_arm_sm4()) { + if(CPUID::has(CPUID::Feature::SM4)) { return 4; } #endif +#if defined(BOTAN_HAS_SM4_AVX512_GFNI) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return 16; + } +#endif + #if defined(BOTAN_HAS_SM4_GFNI) - if(CPUID::has_gfni()) { + if(CPUID::has(CPUID::Feature::GFNI)) { return 8; } #endif +#if defined(BOTAN_HAS_SM4_HWAES) + if(CPUID::has(CPUID::Feature::HW_AES)) { + return 4; + } +#endif + return 1; } std::string SM4::provider() const { #if defined(BOTAN_HAS_SM4_ARMV8) - if(CPUID::has_arm_sm4()) { - return "armv8"; + if(auto feat = CPUID::check(CPUID::Feature::SM4)) { + return *feat; + } +#endif + +#if defined(BOTAN_HAS_SM4_AVX512_GFNI) + if(auto feat = CPUID::check(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return *feat; } #endif #if defined(BOTAN_HAS_SM4_GFNI) - if(CPUID::has_gfni()) { - return "gfni"; + if(auto feat = CPUID::check(CPUID::Feature::GFNI)) { + return *feat; + } +#endif + +#if defined(BOTAN_HAS_SM4_HWAES) + if(auto feat = CPUID::check(CPUID::Feature::HW_AES)) { + return *feat; } #endif diff -Nru botan3-3.7.1+dfsg/src/lib/block/sm4/sm4.h botan3-3.12.0+dfsg/src/lib/block/sm4/sm4.h --- botan3-3.7.1+dfsg/src/lib/block/sm4/sm4.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/sm4/sm4.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #define BOTAN_SM4_H_ #include +#include namespace Botan { @@ -38,11 +39,26 @@ void sm4_armv8_decrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; #endif +#if defined(BOTAN_HAS_SM4_X86) + void sm4_x86_encrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; + void sm4_x86_decrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; +#endif + +#if defined(BOTAN_HAS_SM4_AVX512_GFNI) + void sm4_avx512_gfni_encrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; + void sm4_avx512_gfni_decrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; +#endif + #if defined(BOTAN_HAS_SM4_GFNI) void sm4_gfni_encrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; void sm4_gfni_decrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; #endif +#if defined(BOTAN_HAS_SM4_HWAES) + void sm4_hwaes_encrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; + void sm4_hwaes_decrypt(const uint8_t in[], uint8_t out[], size_t blocks) const; +#endif + secure_vector m_RK; }; diff -Nru botan3-3.7.1+dfsg/src/lib/block/sm4/sm4_armv8/info.txt botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_armv8/info.txt --- botan3-3.7.1+dfsg/src/lib/block/sm4/sm4_armv8/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_armv8/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + SM4_ARMV8 -> 20180709 - + name -> "SM4 ARMv8" @@ -10,3 +10,7 @@ armv8sm4 + + +cpuid + diff -Nru botan3-3.7.1+dfsg/src/lib/block/sm4/sm4_armv8/sm4_armv8.cpp botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_armv8/sm4_armv8.cpp --- botan3-3.7.1+dfsg/src/lib/block/sm4/sm4_armv8/sm4_armv8.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_armv8/sm4_armv8.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,16 +6,16 @@ #include -#include +#include #include namespace Botan { namespace { -alignas(16) static const uint8_t qswap_tbl[16] = {12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3}; +alignas(16) const uint8_t qswap_tbl[16] = {12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3}; -alignas(16) static const uint8_t bswap_tbl[16] = {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; +alignas(16) const uint8_t bswap_tbl[16] = {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; inline uint32x4_t qswap_32(uint32x4_t B) { return vreinterpretq_u32_u8(vqtbl1q_u8(vreinterpretq_u8_u32(B), vld1q_u8(qswap_tbl))); @@ -33,8 +33,7 @@ return vreinterpretq_u32_u8(vqtbl1q_u8(vreinterpretq_u8_u32(B), vld1q_u8(bswap_tbl))); } -inline void BOTAN_FUNC_ISA("arch=armv8.2-a+sm4") - SM4_E(uint32x4_t& B0, uint32x4_t& B1, uint32x4_t& B2, uint32x4_t& B3, uint32x4_t K) { +inline void BOTAN_FN_ISA_SM4 SM4_E(uint32x4_t& B0, uint32x4_t& B1, uint32x4_t& B2, uint32x4_t& B3, uint32x4_t K) { B0 = vsm4eq_u32(B0, K); B1 = vsm4eq_u32(B1, K); B2 = vsm4eq_u32(B2, K); @@ -43,10 +42,8 @@ } // namespace -void BOTAN_FUNC_ISA("arch=armv8.2-a+sm4") SM4::sm4_armv8_encrypt(const uint8_t input8[], - uint8_t output8[], - size_t blocks) const { - const uint32x4_t K0 = vld1q_u32(&m_RK[0]); +void BOTAN_FN_ISA_SM4 SM4::sm4_armv8_encrypt(const uint8_t input8[], uint8_t output8[], size_t blocks) const { + const uint32x4_t K0 = vld1q_u32(&m_RK[0]); // NOLINT(*-container-data-pointer) const uint32x4_t K1 = vld1q_u32(&m_RK[4]); const uint32x4_t K2 = vld1q_u32(&m_RK[8]); const uint32x4_t K3 = vld1q_u32(&m_RK[12]); @@ -55,8 +52,8 @@ const uint32x4_t K6 = vld1q_u32(&m_RK[24]); const uint32x4_t K7 = vld1q_u32(&m_RK[28]); - const uint32_t* input32 = reinterpret_cast(reinterpret_cast(input8)); - uint32_t* output32 = reinterpret_cast(reinterpret_cast(output8)); + const uint32_t* input32 = reinterpret_cast(input8); + uint32_t* output32 = reinterpret_cast(output8); while(blocks >= 4) { uint32x4_t B0 = bswap_32(vld1q_u32(input32)); @@ -102,10 +99,8 @@ } } -void BOTAN_FUNC_ISA("arch=armv8.2-a+sm4") SM4::sm4_armv8_decrypt(const uint8_t input8[], - uint8_t output8[], - size_t blocks) const { - const uint32x4_t K0 = qswap_32(vld1q_u32(&m_RK[0])); +void BOTAN_FN_ISA_SM4 SM4::sm4_armv8_decrypt(const uint8_t input8[], uint8_t output8[], size_t blocks) const { + const uint32x4_t K0 = qswap_32(vld1q_u32(&m_RK[0])); // NOLINT(*-container-data-pointer) const uint32x4_t K1 = qswap_32(vld1q_u32(&m_RK[4])); const uint32x4_t K2 = qswap_32(vld1q_u32(&m_RK[8])); const uint32x4_t K3 = qswap_32(vld1q_u32(&m_RK[12])); @@ -114,8 +109,8 @@ const uint32x4_t K6 = qswap_32(vld1q_u32(&m_RK[24])); const uint32x4_t K7 = qswap_32(vld1q_u32(&m_RK[28])); - const uint32_t* input32 = reinterpret_cast(reinterpret_cast(input8)); - uint32_t* output32 = reinterpret_cast(reinterpret_cast(output8)); + const uint32_t* input32 = reinterpret_cast(input8); + uint32_t* output32 = reinterpret_cast(output8); while(blocks >= 4) { uint32x4_t B0 = bswap_32(vld1q_u32(input32)); diff -Nru botan3-3.7.1+dfsg/src/lib/block/sm4/sm4_avx512/info.txt botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_avx512/info.txt --- botan3-3.7.1+dfsg/src/lib/block/sm4/sm4_avx512/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_avx512/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,19 @@ + +SM4_AVX512_GFNI -> 20251227 + + + +name -> "SM4 AVX-512/GFNI" + + + +cpuid +simd_4x32 +simd_avx2 +simd_avx512 + + + +gfni +avx512 + diff -Nru botan3-3.7.1+dfsg/src/lib/block/sm4/sm4_avx512/sm4_avx512.cpp botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_avx512/sm4_avx512.cpp --- botan3-3.7.1+dfsg/src/lib/block/sm4/sm4_avx512/sm4_avx512.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_avx512/sm4_avx512.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,302 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include + +namespace Botan { + +namespace SM4_AVX512_GFNI { + +namespace { + +template +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI SIMD_T sm4_sbox(const SIMD_T& x) { + /* + * See https://eprint.iacr.org/2022/1154 section 3.3 for details on + * how this works + */ + constexpr uint64_t pre_a = gfni_matrix(R"( + 0 0 1 1 0 0 1 0 + 0 0 0 1 0 1 0 0 + 1 0 1 1 1 1 1 0 + 1 0 0 1 1 1 0 1 + 0 1 0 1 1 0 0 0 + 0 1 0 0 0 1 0 0 + 0 0 0 0 1 0 1 0 + 1 0 1 1 1 0 1 0)"); + + constexpr uint8_t pre_c = 0b00111110; + + constexpr uint64_t post_a = gfni_matrix(R"( + 1 1 0 0 1 1 1 1 + 1 1 0 1 0 1 0 1 + 0 0 1 0 1 1 0 0 + 1 0 0 1 0 1 0 1 + 0 0 1 0 1 1 1 0 + 0 1 1 0 0 1 0 1 + 1 0 1 0 1 1 0 1 + 1 0 0 1 0 0 0 1)"); + + constexpr uint8_t post_c = 0b11010011; + + auto y = gf2p8affine(x); + return gf2p8affineinv(y); +} + +template +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI SIMD_T sm4_f(const SIMD_T& x) { + const auto sx = sm4_sbox(x); + return sx ^ sx.template rotl<2>() ^ sx.template rotl<10>() ^ sx.template rotl<18>() ^ sx.template rotl<24>(); +} + +template +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI void encrypt(const uint8_t ptext[16 * 4 * M], + uint8_t ctext[16 * 4 * M], + std::span RK) { + SIMD_T B0 = SIMD_T::load_be(ptext); + SIMD_T B1 = SIMD_T::load_be(ptext + 16 * M); + SIMD_T B2 = SIMD_T::load_be(ptext + 16 * 2 * M); + SIMD_T B3 = SIMD_T::load_be(ptext + 16 * 3 * M); + + SIMD_T::transpose(B0, B1, B2, B3); + + B0 = B0.rev_words(); + B1 = B1.rev_words(); + B2 = B2.rev_words(); + B3 = B3.rev_words(); + + for(size_t j = 0; j != 8; ++j) { + B0 ^= sm4_f(B1 ^ B2 ^ B3 ^ SIMD_T::splat(RK[4 * j])); + B1 ^= sm4_f(B2 ^ B3 ^ B0 ^ SIMD_T::splat(RK[4 * j + 1])); + B2 ^= sm4_f(B3 ^ B0 ^ B1 ^ SIMD_T::splat(RK[4 * j + 2])); + B3 ^= sm4_f(B0 ^ B1 ^ B2 ^ SIMD_T::splat(RK[4 * j + 3])); + } + + SIMD_T::transpose(B0, B1, B2, B3); + + B3.rev_words().store_be(ctext); + B2.rev_words().store_be(ctext + 16 * M); + B1.rev_words().store_be(ctext + 16 * 2 * M); + B0.rev_words().store_be(ctext + 16 * 3 * M); +} + +template +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI void encrypt_x2(const uint8_t ptext[32 * 4 * M], + uint8_t ctext[32 * 4 * M], + std::span RK) { + SIMD_T B0 = SIMD_T::load_be(ptext); + SIMD_T B1 = SIMD_T::load_be(ptext + 16 * M); + SIMD_T B2 = SIMD_T::load_be(ptext + 16 * 2 * M); + SIMD_T B3 = SIMD_T::load_be(ptext + 16 * 3 * M); + + SIMD_T B4 = SIMD_T::load_be(ptext + 16 * 4 * M); + SIMD_T B5 = SIMD_T::load_be(ptext + 16 * 5 * M); + SIMD_T B6 = SIMD_T::load_be(ptext + 16 * 6 * M); + SIMD_T B7 = SIMD_T::load_be(ptext + 16 * 7 * M); + + SIMD_T::transpose(B0, B1, B2, B3); + SIMD_T::transpose(B4, B5, B6, B7); + + B0 = B0.rev_words(); + B1 = B1.rev_words(); + B2 = B2.rev_words(); + B3 = B3.rev_words(); + + B4 = B4.rev_words(); + B5 = B5.rev_words(); + B6 = B6.rev_words(); + B7 = B7.rev_words(); + + for(size_t j = 0; j != 8; ++j) { + B0 ^= sm4_f(B1 ^ B2 ^ B3 ^ SIMD_T::splat(RK[4 * j])); + B4 ^= sm4_f(B5 ^ B6 ^ B7 ^ SIMD_T::splat(RK[4 * j])); + + B1 ^= sm4_f(B2 ^ B3 ^ B0 ^ SIMD_T::splat(RK[4 * j + 1])); + B5 ^= sm4_f(B6 ^ B7 ^ B4 ^ SIMD_T::splat(RK[4 * j + 1])); + + B2 ^= sm4_f(B3 ^ B0 ^ B1 ^ SIMD_T::splat(RK[4 * j + 2])); + B6 ^= sm4_f(B7 ^ B4 ^ B5 ^ SIMD_T::splat(RK[4 * j + 2])); + + B3 ^= sm4_f(B0 ^ B1 ^ B2 ^ SIMD_T::splat(RK[4 * j + 3])); + B7 ^= sm4_f(B4 ^ B5 ^ B6 ^ SIMD_T::splat(RK[4 * j + 3])); + } + + SIMD_T::transpose(B0, B1, B2, B3); + SIMD_T::transpose(B4, B5, B6, B7); + + B3.rev_words().store_be(ctext); + B2.rev_words().store_be(ctext + 16 * M); + B1.rev_words().store_be(ctext + 16 * 2 * M); + B0.rev_words().store_be(ctext + 16 * 3 * M); + + B7.rev_words().store_be(ctext + 16 * 4 * M); + B6.rev_words().store_be(ctext + 16 * 5 * M); + B5.rev_words().store_be(ctext + 16 * 6 * M); + B4.rev_words().store_be(ctext + 16 * 7 * M); +} + +template +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI void decrypt(const uint8_t ctext[16 * 4 * M], + uint8_t ptext[16 * 4 * M], + std::span RK) { + SIMD_T B0 = SIMD_T::load_be(ctext); + SIMD_T B1 = SIMD_T::load_be(ctext + 16 * M); + SIMD_T B2 = SIMD_T::load_be(ctext + 16 * 2 * M); + SIMD_T B3 = SIMD_T::load_be(ctext + 16 * 3 * M); + + SIMD_T::transpose(B0, B1, B2, B3); + + B0 = B0.rev_words(); + B1 = B1.rev_words(); + B2 = B2.rev_words(); + B3 = B3.rev_words(); + + for(size_t j = 0; j != 8; ++j) { + B0 ^= sm4_f(B1 ^ B2 ^ B3 ^ SIMD_T::splat(RK[32 - (4 * j + 1)])); + B1 ^= sm4_f(B2 ^ B3 ^ B0 ^ SIMD_T::splat(RK[32 - (4 * j + 2)])); + B2 ^= sm4_f(B3 ^ B0 ^ B1 ^ SIMD_T::splat(RK[32 - (4 * j + 3)])); + B3 ^= sm4_f(B0 ^ B1 ^ B2 ^ SIMD_T::splat(RK[32 - (4 * j + 4)])); + } + + SIMD_T::transpose(B0, B1, B2, B3); + + B3.rev_words().store_be(ptext); + B2.rev_words().store_be(ptext + 16 * M); + B1.rev_words().store_be(ptext + 16 * 2 * M); + B0.rev_words().store_be(ptext + 16 * 3 * M); +} + +template +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI void decrypt_x2(const uint8_t ctext[32 * 4 * M], + uint8_t ptext[32 * 4 * M], + std::span RK) { + SIMD_T B0 = SIMD_T::load_be(ctext); + SIMD_T B1 = SIMD_T::load_be(ctext + 16 * M); + SIMD_T B2 = SIMD_T::load_be(ctext + 16 * 2 * M); + SIMD_T B3 = SIMD_T::load_be(ctext + 16 * 3 * M); + + SIMD_T B4 = SIMD_T::load_be(ctext + 16 * 4 * M); + SIMD_T B5 = SIMD_T::load_be(ctext + 16 * 5 * M); + SIMD_T B6 = SIMD_T::load_be(ctext + 16 * 6 * M); + SIMD_T B7 = SIMD_T::load_be(ctext + 16 * 7 * M); + + SIMD_T::transpose(B0, B1, B2, B3); + SIMD_T::transpose(B4, B5, B6, B7); + + B0 = B0.rev_words(); + B1 = B1.rev_words(); + B2 = B2.rev_words(); + B3 = B3.rev_words(); + + B4 = B4.rev_words(); + B5 = B5.rev_words(); + B6 = B6.rev_words(); + B7 = B7.rev_words(); + + for(size_t j = 0; j != 8; ++j) { + B0 ^= sm4_f(B1 ^ B2 ^ B3 ^ SIMD_T::splat(RK[32 - (4 * j + 1)])); + B4 ^= sm4_f(B5 ^ B6 ^ B7 ^ SIMD_T::splat(RK[32 - (4 * j + 1)])); + + B1 ^= sm4_f(B2 ^ B3 ^ B0 ^ SIMD_T::splat(RK[32 - (4 * j + 2)])); + B5 ^= sm4_f(B6 ^ B7 ^ B4 ^ SIMD_T::splat(RK[32 - (4 * j + 2)])); + + B2 ^= sm4_f(B3 ^ B0 ^ B1 ^ SIMD_T::splat(RK[32 - (4 * j + 3)])); + B6 ^= sm4_f(B7 ^ B4 ^ B5 ^ SIMD_T::splat(RK[32 - (4 * j + 3)])); + + B3 ^= sm4_f(B0 ^ B1 ^ B2 ^ SIMD_T::splat(RK[32 - (4 * j + 4)])); + B7 ^= sm4_f(B4 ^ B5 ^ B6 ^ SIMD_T::splat(RK[32 - (4 * j + 4)])); + } + + SIMD_T::transpose(B0, B1, B2, B3); + SIMD_T::transpose(B4, B5, B6, B7); + + B3.rev_words().store_be(ptext); + B2.rev_words().store_be(ptext + 16 * M); + B1.rev_words().store_be(ptext + 16 * 2 * M); + B0.rev_words().store_be(ptext + 16 * 3 * M); + + B7.rev_words().store_be(ptext + 16 * 4 * M); + B6.rev_words().store_be(ptext + 16 * 5 * M); + B5.rev_words().store_be(ptext + 16 * 6 * M); + B4.rev_words().store_be(ptext + 16 * 7 * M); +} + +} // namespace + +} // namespace SM4_AVX512_GFNI + +void BOTAN_FN_ISA_AVX512_GFNI SM4::sm4_avx512_gfni_encrypt(const uint8_t ptext[], + uint8_t ctext[], + size_t blocks) const { + while(blocks >= 32) { + SM4_AVX512_GFNI::encrypt_x2(ptext, ctext, m_RK); + ptext += 16 * 32; + ctext += 16 * 32; + blocks -= 32; + } + + while(blocks >= 16) { + SM4_AVX512_GFNI::encrypt(ptext, ctext, m_RK); + ptext += 16 * 16; + ctext += 16 * 16; + blocks -= 16; + } + + while(blocks >= 8) { + SM4_AVX512_GFNI::encrypt(ptext, ctext, m_RK); + ptext += 16 * 8; + ctext += 16 * 8; + blocks -= 8; + } + + if(blocks > 0) { + uint8_t pbuf[16 * 8] = {0}; + uint8_t cbuf[16 * 8] = {0}; + copy_mem(pbuf, ptext, blocks * 16); + SM4_AVX512_GFNI::encrypt(pbuf, cbuf, m_RK); + copy_mem(ctext, cbuf, blocks * 16); + } +} + +void BOTAN_FN_ISA_AVX512_GFNI SM4::sm4_avx512_gfni_decrypt(const uint8_t ctext[], + uint8_t ptext[], + size_t blocks) const { + while(blocks >= 32) { + SM4_AVX512_GFNI::decrypt_x2(ctext, ptext, m_RK); + ptext += 16 * 32; + ctext += 16 * 32; + blocks -= 32; + } + + while(blocks >= 16) { + SM4_AVX512_GFNI::decrypt(ctext, ptext, m_RK); + ptext += 16 * 16; + ctext += 16 * 16; + blocks -= 16; + } + + while(blocks >= 8) { + SM4_AVX512_GFNI::decrypt(ctext, ptext, m_RK); + ptext += 16 * 8; + ctext += 16 * 8; + blocks -= 8; + } + + if(blocks > 0) { + uint8_t cbuf[16 * 8] = {0}; + uint8_t pbuf[16 * 8] = {0}; + copy_mem(cbuf, ctext, blocks * 16); + SM4_AVX512_GFNI::decrypt(cbuf, pbuf, m_RK); + copy_mem(ptext, pbuf, blocks * 16); + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/sm4/sm4_gfni/info.txt botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_gfni/info.txt --- botan3-3.7.1+dfsg/src/lib/block/sm4/sm4_gfni/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_gfni/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + SM4_GFNI -> 20240803 - + name -> "SM4 GFNI" @@ -8,6 +8,7 @@ +cpuid simd_avx2 diff -Nru botan3-3.7.1+dfsg/src/lib/block/sm4/sm4_gfni/sm4_gfni.cpp botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_gfni/sm4_gfni.cpp --- botan3-3.7.1+dfsg/src/lib/block/sm4/sm4_gfni/sm4_gfni.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_gfni/sm4_gfni.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,6 +6,8 @@ #include +#include +#include #include #include @@ -13,7 +15,7 @@ namespace { -BOTAN_FUNC_ISA_INLINE(BOTAN_GFNI_ISA) SIMD_8x32 sm4_sbox(const SIMD_8x32& x) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_GFNI SIMD_8x32 sm4_sbox(const SIMD_8x32& x) { /* * See https://eprint.iacr.org/2022/1154 section 3.3 for details on * how this works @@ -46,13 +48,14 @@ return gf2p8affineinv(y); } -BOTAN_FUNC_ISA_INLINE(BOTAN_GFNI_ISA) SIMD_8x32 sm4_f(const SIMD_8x32& x) { - SIMD_8x32 sx = sm4_sbox(x); +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_GFNI SIMD_8x32 sm4_f(const SIMD_8x32& x) { + const SIMD_8x32 sx = sm4_sbox(x); return sx ^ sx.rotl<2>() ^ sx.rotl<10>() ^ sx.rotl<18>() ^ sx.rotl<24>(); } -BOTAN_FUNC_ISA_INLINE(BOTAN_GFNI_ISA) -void sm4_gfni_encrypt_8(const uint8_t ptext[8 * 16], uint8_t ctext[8 * 16], std::span RK) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_GFNI void sm4_gfni_encrypt_8(const uint8_t ptext[8 * 16], + uint8_t ctext[8 * 16], + std::span RK) { SIMD_8x32 B0 = SIMD_8x32::load_be(ptext); SIMD_8x32 B1 = SIMD_8x32::load_be(ptext + 16 * 2); SIMD_8x32 B2 = SIMD_8x32::load_be(ptext + 16 * 4); @@ -80,8 +83,9 @@ B0.rev_words().store_be(ctext + 16 * 6); } -BOTAN_FUNC_ISA_INLINE(BOTAN_GFNI_ISA) -void sm4_gfni_decrypt_8(const uint8_t ctext[8 * 16], uint8_t ptext[8 * 16], std::span RK) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_GFNI void sm4_gfni_decrypt_8(const uint8_t ctext[8 * 16], + uint8_t ptext[8 * 16], + std::span RK) { SIMD_8x32 B0 = SIMD_8x32::load_be(ctext); SIMD_8x32 B1 = SIMD_8x32::load_be(ctext + 16 * 2); SIMD_8x32 B2 = SIMD_8x32::load_be(ctext + 16 * 4); @@ -111,7 +115,7 @@ } // namespace -void BOTAN_FUNC_ISA("gfni,avx2") SM4::sm4_gfni_encrypt(const uint8_t ptext[], uint8_t ctext[], size_t blocks) const { +void BOTAN_FN_ISA_AVX2_GFNI SM4::sm4_gfni_encrypt(const uint8_t ptext[], uint8_t ctext[], size_t blocks) const { while(blocks >= 8) { sm4_gfni_encrypt_8(ptext, ctext, m_RK); ptext += 16 * 8; @@ -128,7 +132,7 @@ } } -void BOTAN_FUNC_ISA("gfni,avx2") SM4::sm4_gfni_decrypt(const uint8_t ctext[], uint8_t ptext[], size_t blocks) const { +void BOTAN_FN_ISA_AVX2_GFNI SM4::sm4_gfni_decrypt(const uint8_t ctext[], uint8_t ptext[], size_t blocks) const { while(blocks >= 8) { sm4_gfni_decrypt_8(ctext, ptext, m_RK); ptext += 16 * 8; diff -Nru botan3-3.7.1+dfsg/src/lib/block/sm4/sm4_hwaes/info.txt botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_hwaes/info.txt --- botan3-3.7.1+dfsg/src/lib/block/sm4/sm4_hwaes/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_hwaes/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ + +SM4_HWAES -> 20260322 + + + +name -> "SM4 using hardware AES instructions" + + + +cpuid +simd_hwaes + diff -Nru botan3-3.7.1+dfsg/src/lib/block/sm4/sm4_hwaes/sm4_hwaes.cpp botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_hwaes/sm4_hwaes.cpp --- botan3-3.7.1+dfsg/src/lib/block/sm4/sm4_hwaes/sm4_hwaes.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_hwaes/sm4_hwaes.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,274 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include + +namespace Botan { + +namespace { + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES SIMD_4x32 sm4_sbox(const SIMD_4x32& x) { + /* + * The SM4 sbox is, like the AES sbox, based on inversion in GF(2^8) plus an + * affine transformation. + * + * See + * - sections 3.1 and 3.3 + * - + * - + * describes a similar approach for implementing Camellia in section 4.4 + */ + + constexpr uint64_t pre_a = gfni_matrix(R"( + 0 0 1 1 0 0 1 0 + 0 0 0 1 0 1 0 0 + 1 0 1 1 1 1 1 0 + 1 0 0 1 1 1 0 1 + 0 1 0 1 1 0 0 0 + 0 1 0 0 0 1 0 0 + 0 0 0 0 1 0 1 0 + 1 0 1 1 1 0 1 0)"); + constexpr uint8_t pre_c = 0b00111110; + + constexpr uint64_t post_a = gfni_matrix(R"( + 1 1 0 0 1 1 1 1 + 1 1 0 1 0 1 0 1 + 0 0 1 0 1 1 0 0 + 1 0 0 1 0 1 0 1 + 0 0 1 0 1 1 1 0 + 0 1 1 0 0 1 0 1 + 1 0 1 0 1 1 0 1 + 1 0 0 1 0 0 0 1)"); + constexpr uint8_t post_c = 0b11010011; + + constexpr auto pre = Gf2AffineTransformation(pre_a, pre_c); + constexpr auto post = Gf2AffineTransformation::post_sbox(post_a, post_c); + + return post.affine_transform(hw_aes_sbox(pre.affine_transform(x))); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES SIMD_4x32 sm4_f(const SIMD_4x32& x) { + const auto sx = sm4_sbox(x); + // L linear transform + return sx ^ sx.rotl<2>() ^ sx.rotl<10>() ^ sx.rotl<18>() ^ sx.rotl<24>(); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES void sm4_hwaes_encrypt_4(const uint8_t ptext[4 * 16], + uint8_t ctext[4 * 16], + std::span RK) { + auto B0 = SIMD_4x32::load_be(ptext + 16 * 0); + auto B1 = SIMD_4x32::load_be(ptext + 16 * 1); + auto B2 = SIMD_4x32::load_be(ptext + 16 * 2); + auto B3 = SIMD_4x32::load_be(ptext + 16 * 3); + + SIMD_4x32::transpose(B0, B1, B2, B3); + + for(size_t j = 0; j != 8; ++j) { + const auto K0 = SIMD_4x32::splat(RK[4 * j]); + const auto K1 = SIMD_4x32::splat(RK[4 * j + 1]); + const auto K2 = SIMD_4x32::splat(RK[4 * j + 2]); + const auto K3 = SIMD_4x32::splat(RK[4 * j + 3]); + B0 ^= sm4_f(B1 ^ B2 ^ B3 ^ K0); + B1 ^= sm4_f(B2 ^ B3 ^ B0 ^ K1); + B2 ^= sm4_f(B3 ^ B0 ^ B1 ^ K2); + B3 ^= sm4_f(B0 ^ B1 ^ B2 ^ K3); + } + + // SM4 reverses word order + SIMD_4x32::transpose(B3, B2, B1, B0); + + B3.store_be(ctext + 16 * 0); + B2.store_be(ctext + 16 * 1); + B1.store_be(ctext + 16 * 2); + B0.store_be(ctext + 16 * 3); +} + +// Same as sm4_hwaes_encrypt_4 except interleaved 2x +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES void sm4_hwaes_encrypt_8(const uint8_t ptext[8 * 16], + uint8_t ctext[8 * 16], + std::span RK) { + auto B0 = SIMD_4x32::load_be(ptext + 16 * 0); + auto B1 = SIMD_4x32::load_be(ptext + 16 * 1); + auto B2 = SIMD_4x32::load_be(ptext + 16 * 2); + auto B3 = SIMD_4x32::load_be(ptext + 16 * 3); + auto B4 = SIMD_4x32::load_be(ptext + 16 * 4); + auto B5 = SIMD_4x32::load_be(ptext + 16 * 5); + auto B6 = SIMD_4x32::load_be(ptext + 16 * 6); + auto B7 = SIMD_4x32::load_be(ptext + 16 * 7); + + SIMD_4x32::transpose(B0, B1, B2, B3); + SIMD_4x32::transpose(B4, B5, B6, B7); + + for(size_t j = 0; j != 8; ++j) { + const auto K0 = SIMD_4x32::splat(RK[4 * j]); + const auto K1 = SIMD_4x32::splat(RK[4 * j + 1]); + const auto K2 = SIMD_4x32::splat(RK[4 * j + 2]); + const auto K3 = SIMD_4x32::splat(RK[4 * j + 3]); + + B0 ^= sm4_f(B1 ^ B2 ^ B3 ^ K0); + B4 ^= sm4_f(B5 ^ B6 ^ B7 ^ K0); + + B1 ^= sm4_f(B2 ^ B3 ^ B0 ^ K1); + B5 ^= sm4_f(B6 ^ B7 ^ B4 ^ K1); + + B2 ^= sm4_f(B3 ^ B0 ^ B1 ^ K2); + B6 ^= sm4_f(B7 ^ B4 ^ B5 ^ K2); + + B3 ^= sm4_f(B0 ^ B1 ^ B2 ^ K3); + B7 ^= sm4_f(B4 ^ B5 ^ B6 ^ K3); + } + + // SM4 reverses word order + SIMD_4x32::transpose(B3, B2, B1, B0); + SIMD_4x32::transpose(B7, B6, B5, B4); + + B3.store_be(ctext + 16 * 0); + B2.store_be(ctext + 16 * 1); + B1.store_be(ctext + 16 * 2); + B0.store_be(ctext + 16 * 3); + + B7.store_be(ctext + 16 * 4); + B6.store_be(ctext + 16 * 5); + B5.store_be(ctext + 16 * 6); + B4.store_be(ctext + 16 * 7); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES void sm4_hwaes_decrypt_4(const uint8_t ctext[4 * 16], + uint8_t ptext[4 * 16], + std::span RK) { + auto B0 = SIMD_4x32::load_be(ctext + 16 * 0); + auto B1 = SIMD_4x32::load_be(ctext + 16 * 1); + auto B2 = SIMD_4x32::load_be(ctext + 16 * 2); + auto B3 = SIMD_4x32::load_be(ctext + 16 * 3); + + SIMD_4x32::transpose(B0, B1, B2, B3); + + for(size_t j = 0; j != 8; ++j) { + const auto K0 = SIMD_4x32::splat(RK[32 - (4 * j + 1)]); + const auto K1 = SIMD_4x32::splat(RK[32 - (4 * j + 2)]); + const auto K2 = SIMD_4x32::splat(RK[32 - (4 * j + 3)]); + const auto K3 = SIMD_4x32::splat(RK[32 - (4 * j + 4)]); + B0 ^= sm4_f(B1 ^ B2 ^ B3 ^ K0); + B1 ^= sm4_f(B2 ^ B3 ^ B0 ^ K1); + B2 ^= sm4_f(B3 ^ B0 ^ B1 ^ K2); + B3 ^= sm4_f(B0 ^ B1 ^ B2 ^ K3); + } + + // SM4 reverses word order + SIMD_4x32::transpose(B3, B2, B1, B0); + + B3.store_be(ptext + 16 * 0); + B2.store_be(ptext + 16 * 1); + B1.store_be(ptext + 16 * 2); + B0.store_be(ptext + 16 * 3); +} + +// Same as sm4_hwaes_decrypt_4 except interleaved 2x +BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES void sm4_hwaes_decrypt_8(const uint8_t ctext[8 * 16], + uint8_t ptext[8 * 16], + std::span RK) { + auto B0 = SIMD_4x32::load_be(ctext + 16 * 0); + auto B1 = SIMD_4x32::load_be(ctext + 16 * 1); + auto B2 = SIMD_4x32::load_be(ctext + 16 * 2); + auto B3 = SIMD_4x32::load_be(ctext + 16 * 3); + auto B4 = SIMD_4x32::load_be(ctext + 16 * 4); + auto B5 = SIMD_4x32::load_be(ctext + 16 * 5); + auto B6 = SIMD_4x32::load_be(ctext + 16 * 6); + auto B7 = SIMD_4x32::load_be(ctext + 16 * 7); + + SIMD_4x32::transpose(B0, B1, B2, B3); + SIMD_4x32::transpose(B4, B5, B6, B7); + + for(size_t j = 0; j != 8; ++j) { + const auto K0 = SIMD_4x32::splat(RK[32 - (4 * j + 1)]); + const auto K1 = SIMD_4x32::splat(RK[32 - (4 * j + 2)]); + const auto K2 = SIMD_4x32::splat(RK[32 - (4 * j + 3)]); + const auto K3 = SIMD_4x32::splat(RK[32 - (4 * j + 4)]); + + B0 ^= sm4_f(B1 ^ B2 ^ B3 ^ K0); + B4 ^= sm4_f(B5 ^ B6 ^ B7 ^ K0); + + B1 ^= sm4_f(B2 ^ B3 ^ B0 ^ K1); + B5 ^= sm4_f(B6 ^ B7 ^ B4 ^ K1); + + B2 ^= sm4_f(B3 ^ B0 ^ B1 ^ K2); + B6 ^= sm4_f(B7 ^ B4 ^ B5 ^ K2); + + B3 ^= sm4_f(B0 ^ B1 ^ B2 ^ K3); + B7 ^= sm4_f(B4 ^ B5 ^ B6 ^ K3); + } + + // SM4 reverses word order + SIMD_4x32::transpose(B3, B2, B1, B0); + SIMD_4x32::transpose(B7, B6, B5, B4); + + B3.store_be(ptext + 16 * 0); + B2.store_be(ptext + 16 * 1); + B1.store_be(ptext + 16 * 2); + B0.store_be(ptext + 16 * 3); + + B7.store_be(ptext + 16 * 4); + B6.store_be(ptext + 16 * 5); + B5.store_be(ptext + 16 * 6); + B4.store_be(ptext + 16 * 7); +} + +} // namespace + +void BOTAN_FN_ISA_HWAES SM4::sm4_hwaes_encrypt(const uint8_t ptext[], uint8_t ctext[], size_t blocks) const { + while(blocks >= 8) { + sm4_hwaes_encrypt_8(ptext, ctext, m_RK); + ptext += 16 * 8; + ctext += 16 * 8; + blocks -= 8; + } + + while(blocks >= 4) { + sm4_hwaes_encrypt_4(ptext, ctext, m_RK); + ptext += 16 * 4; + ctext += 16 * 4; + blocks -= 4; + } + + if(blocks > 0) { + uint8_t pbuf[4 * 16] = {0}; + uint8_t cbuf[4 * 16] = {0}; + copy_mem(pbuf, ptext, blocks * 16); + sm4_hwaes_encrypt_4(pbuf, cbuf, m_RK); + copy_mem(ctext, cbuf, blocks * 16); + } +} + +void BOTAN_FN_ISA_HWAES SM4::sm4_hwaes_decrypt(const uint8_t ctext[], uint8_t ptext[], size_t blocks) const { + while(blocks >= 8) { + sm4_hwaes_decrypt_8(ctext, ptext, m_RK); + ptext += 16 * 8; + ctext += 16 * 8; + blocks -= 8; + } + + while(blocks >= 4) { + sm4_hwaes_decrypt_4(ctext, ptext, m_RK); + ptext += 16 * 4; + ctext += 16 * 4; + blocks -= 4; + } + + if(blocks > 0) { + uint8_t cbuf[4 * 16] = {0}; + uint8_t pbuf[4 * 16] = {0}; + copy_mem(cbuf, ctext, blocks * 16); + sm4_hwaes_decrypt_4(cbuf, pbuf, m_RK); + copy_mem(ptext, pbuf, blocks * 16); + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/sm4/sm4_x86/info.txt botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_x86/info.txt --- botan3-3.7.1+dfsg/src/lib/block/sm4/sm4_x86/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_x86/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,23 @@ + +SM4_X86 -> 20250311 + + + +name -> "SM4 x86" +brief -> "SM4 using Intel SM4 extension" + + + +cpuid +simd_avx2 + + + +sm4 + + + +gcc:14 +clang:17 +msvc + diff -Nru botan3-3.7.1+dfsg/src/lib/block/sm4/sm4_x86/sm4_x86.cpp botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_x86/sm4_x86.cpp --- botan3-3.7.1+dfsg/src/lib/block/sm4/sm4_x86/sm4_x86.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/sm4/sm4_x86/sm4_x86.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,142 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include + +namespace Botan { + +namespace { + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_SM4 SIMD_8x32 sm4_x86_rnds4(const SIMD_8x32& b, const SIMD_8x32& k) { + // NOLINTNEXTLINE(portability-simd-intrinsics) + return SIMD_8x32(_mm256_sm4rnds4_epi32(b.raw(), k.raw())); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_SM4 void sm4_x86_encrypt_x2(uint8_t out[2 * 16], + const uint8_t inp[2 * 16], + std::span RK) { + auto B0 = SIMD_8x32::load_be(inp); + + for(size_t i = 0; i != 8; ++i) { + const auto RK_i = SIMD_8x32::load_le128(&RK[4 * i]); + B0 = sm4_x86_rnds4(B0, RK_i); + } + + B0.reverse().store_le(out); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_SM4 void sm4_x86_encrypt_x8(uint8_t out[8 * 16], + const uint8_t inp[8 * 16], + std::span RK) { + auto B0 = SIMD_8x32::load_be(inp); + auto B1 = SIMD_8x32::load_be(inp + 32); + auto B2 = SIMD_8x32::load_be(inp + 64); + auto B3 = SIMD_8x32::load_be(inp + 96); + + for(size_t i = 0; i != 8; ++i) { + auto RK_i = SIMD_8x32::load_le128(&RK[4 * i]); + B0 = sm4_x86_rnds4(B0, RK_i); + B1 = sm4_x86_rnds4(B1, RK_i); + B2 = sm4_x86_rnds4(B2, RK_i); + B3 = sm4_x86_rnds4(B3, RK_i); + } + + B0.reverse().store_le(out); + B1.reverse().store_le(out + 32); + B2.reverse().store_le(out + 64); + B3.reverse().store_le(out + 96); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_SM4 void sm4_x86_decrypt_x2(uint8_t out[2 * 16], + const uint8_t inp[2 * 16], + std::span RK) { + auto B0 = SIMD_8x32::load_be(inp); + + for(size_t i = 0; i != 8; ++i) { + auto RK_i = SIMD_8x32::load_le128(&RK[28 - 4 * i]).rev_words(); + B0 = sm4_x86_rnds4(B0, RK_i); + } + + B0.reverse().store_le(out); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_SM4 void sm4_x86_decrypt_x8(uint8_t out[8 * 16], + const uint8_t inp[8 * 16], + std::span RK) { + auto B0 = SIMD_8x32::load_be(inp); + auto B1 = SIMD_8x32::load_be(inp + 32); + auto B2 = SIMD_8x32::load_be(inp + 64); + auto B3 = SIMD_8x32::load_be(inp + 96); + + for(size_t i = 0; i != 8; ++i) { + auto RK_i = SIMD_8x32::load_le128(&RK[28 - 4 * i]).rev_words(); + B0 = sm4_x86_rnds4(B0, RK_i); + B1 = sm4_x86_rnds4(B1, RK_i); + B2 = sm4_x86_rnds4(B2, RK_i); + B3 = sm4_x86_rnds4(B3, RK_i); + } + + B0.reverse().store_le(out); + B1.reverse().store_le(out + 32); + B2.reverse().store_le(out + 64); + B3.reverse().store_le(out + 96); +} + +} // namespace + +void BOTAN_FN_ISA_AVX2_SM4 SM4::sm4_x86_encrypt(const uint8_t inp[], uint8_t out[], size_t blocks) const { + while(blocks >= 8) { + sm4_x86_encrypt_x8(out, inp, m_RK); + inp += 8 * 16; + out += 8 * 16; + blocks -= 8; + } + + while(blocks >= 2) { + sm4_x86_encrypt_x2(out, inp, m_RK); + inp += 2 * 16; + out += 2 * 16; + blocks -= 2; + } + + if(blocks > 0) { + uint8_t ibuf[2 * 16] = {0}; + uint8_t obuf[2 * 16] = {0}; + copy_mem(ibuf, inp, blocks * 16); + sm4_x86_encrypt_x2(obuf, ibuf, m_RK); + copy_mem(out, obuf, blocks * 16); + } +} + +void BOTAN_FN_ISA_AVX2_SM4 SM4::sm4_x86_decrypt(const uint8_t inp[], uint8_t out[], size_t blocks) const { + while(blocks >= 8) { + sm4_x86_decrypt_x8(out, inp, m_RK); + inp += 8 * 16; + out += 8 * 16; + blocks -= 8; + } + + while(blocks >= 2) { + sm4_x86_decrypt_x2(out, inp, m_RK); + inp += 2 * 16; + out += 2 * 16; + blocks -= 2; + } + + if(blocks > 0) { + uint8_t ibuf[2 * 16] = {0}; + uint8_t obuf[2 * 16] = {0}; + copy_mem(ibuf, inp, blocks * 16); + sm4_x86_decrypt_x2(obuf, ibuf, m_RK); + copy_mem(out, obuf, blocks * 16); + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/threefish_512/threefish_512.cpp botan3-3.12.0+dfsg/src/lib/block/threefish_512/threefish_512.cpp --- botan3-3.7.1+dfsg/src/lib/block/threefish_512/threefish_512.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/threefish_512/threefish_512.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,7 +7,6 @@ #include -#include #include #include @@ -51,7 +50,7 @@ X3 -= X7; } -class Key_Inserter { +class Key_Inserter final { public: Key_Inserter(const uint64_t* K, const uint64_t* T) : m_K(K), m_T(T) {} @@ -201,7 +200,14 @@ const Key_Inserter key(m_K.data(), m_T.data()); for(size_t i = 0; i < blocks; ++i) { - uint64_t X0, X1, X2, X3, X4, X5, X6, X7; + uint64_t X0 = 0; + uint64_t X1 = 0; + uint64_t X2 = 0; + uint64_t X3 = 0; + uint64_t X4 = 0; + uint64_t X5 = 0; + uint64_t X6 = 0; + uint64_t X7 = 0; load_le(in + BLOCK_SIZE * i, X0, X1, X2, X3, X4, X5, X6, X7); key.e_add(0, X0, X1, X2, X3, X4, X5, X6, X7); @@ -228,7 +234,14 @@ const Key_Inserter key(m_K.data(), m_T.data()); for(size_t i = 0; i < blocks; ++i) { - uint64_t X0, X1, X2, X3, X4, X5, X6, X7; + uint64_t X0 = 0; + uint64_t X1 = 0; + uint64_t X2 = 0; + uint64_t X3 = 0; + uint64_t X4 = 0; + uint64_t X5 = 0; + uint64_t X6 = 0; + uint64_t X7 = 0; load_le(in + BLOCK_SIZE * i, X0, X1, X2, X3, X4, X5, X6, X7); key.d_add(18, X0, X1, X2, X3, X4, X5, X6, X7); diff -Nru botan3-3.7.1+dfsg/src/lib/block/threefish_512/threefish_512.h botan3-3.12.0+dfsg/src/lib/block/threefish_512/threefish_512.h --- botan3-3.7.1+dfsg/src/lib/block/threefish_512/threefish_512.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/threefish_512/threefish_512.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #define BOTAN_THREEFISH_512_H_ #include +#include namespace Botan { diff -Nru botan3-3.7.1+dfsg/src/lib/block/twofish/twofish.cpp botan3-3.12.0+dfsg/src/lib/block/twofish/twofish.cpp --- botan3-3.7.1+dfsg/src/lib/block/twofish/twofish.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/twofish/twofish.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,22 +1,140 @@ /* * Twofish -* (C) 1999-2007,2017 Jack Lloyd -* -* The key schedule implemenation is based on a public domain -* implementation by Matthew Skala +* (C) 1999-2007,2017,2026 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include +#include #include #include +#if defined(BOTAN_HAS_CPUID) + #include +#endif + namespace Botan { namespace { +namespace Twofish_KS { + +// Twofish q-permutation derived from four 4-bit sboxes +// ("Twofish: A 128-Bit Block Cipher", section 4.3.5) +consteval std::array twofish_q_perm(std::array t0, + std::array t1, + std::array t2, + std::array t3) noexcept { + std::array Q = {}; + for(size_t x = 0; x != 256; ++x) { + const uint8_t a0 = static_cast((x >> 4) & 0x0F); + const uint8_t b0 = static_cast(x & 0x0F); + + const uint8_t a1 = a0 ^ b0; + const uint8_t b1 = a0 ^ ((b0 >> 1) | ((b0 & 1) << 3)) ^ ((8 * a0) & 0x0F); + + const uint8_t a2 = t0[a1]; + const uint8_t b2 = t1[b1]; + + const uint8_t a3 = a2 ^ b2; + const uint8_t b3 = a2 ^ ((b2 >> 1) | ((b2 & 1) << 3)) ^ ((8 * a2) & 0x0F); + + const uint8_t a4 = t2[a3]; + const uint8_t b4 = t3[b3]; + + Q[x] = static_cast((b4 << 4) | a4); + } + return Q; +} + +// clang-format off +alignas(256) constexpr auto Q0 = twofish_q_perm( + {8, 1, 7, 13, 6, 15, 3, 2, 0, 11, 5, 9, 14, 12, 10, 4}, + {14, 12, 11, 8, 1, 2, 3, 5, 15, 4, 10, 6, 7, 0, 9, 13}, + {11, 10, 5, 14, 6, 13, 9, 0, 12, 8, 15, 3, 2, 4, 7, 1}, + {13, 7, 15, 4, 1, 2, 6, 14, 9, 11, 3, 0, 8, 5, 12, 10}); + +alignas(256) constexpr auto Q1 = twofish_q_perm( + {2, 8, 11, 13, 15, 7, 6, 14, 3, 1, 9, 4, 0, 10, 12, 5}, + {1, 14, 2, 11, 4, 12, 3, 7, 6, 13, 10, 5, 15, 9, 0, 8}, + {4, 12, 7, 5, 1, 6, 9, 10, 0, 14, 13, 8, 2, 11, 3, 15}, + {11, 9, 5, 1, 12, 3, 13, 14, 6, 4, 7, 15, 2, 0, 8, 10}); + +// clang-format on + +/* +* MDS matrix multiplication (Twofish paper Section 4.2) +* +* MDS = [01, EF, 5B, 5B] +* [5B, EF, EF, 01] +* [EF, 5B, 01, EF] +* [EF, 01, EF, 5B] +* +* The MDS coefficients are 01, 5B, and EF. These were chosen so that +* +* 5B = 1 + 1/x^2 +* EF = 1 + 1/x + 1/x^2 +* +* in GF(2^8) mod x^8+x^6+x^5+x^3+1, where 1/x is computed by shifting +* right and conditionally XORing with 0xB4 (which is itself just the +* irreducible polynomial 0x169 shifted right by 1). +* +* This property of the MDS constants is described (briefly) in Section 7.3 +* of the Twofish paper. +*/ + +inline uint8_t mds_div_x(uint8_t q) { + return (q >> 1) ^ (CT::value_barrier(q & 1) * 0xB4); +} + +inline uint32_t mds0(uint8_t q) { + const uint8_t q_div_x = mds_div_x(q); + const uint8_t q5b = q ^ mds_div_x(q_div_x); + const uint8_t qef = q5b ^ q_div_x; + return make_uint32(qef, qef, q5b, q); +} + +inline uint32_t mds1(uint8_t q) { + const uint8_t q_div_x = mds_div_x(q); + const uint8_t q5b = q ^ mds_div_x(q_div_x); + const uint8_t qef = q5b ^ q_div_x; + return make_uint32(q, q5b, qef, qef); +} + +inline uint32_t mds2(uint8_t q) { + const uint8_t q_div_x = mds_div_x(q); + const uint8_t q5b = q ^ mds_div_x(q_div_x); + const uint8_t qef = q5b ^ q_div_x; + return make_uint32(qef, q, qef, q5b); +} + +inline uint32_t mds3(uint8_t q) { + const uint8_t q_div_x = mds_div_x(q); + const uint8_t q5b = q ^ mds_div_x(q_div_x); + const uint8_t qef = q5b ^ q_div_x; + return make_uint32(q5b, qef, q, q5b); +} + +// Constant-time GF(2^8) multiply in the RS field (irreducible polynomial 0x14D) +inline uint32_t gf_mul_rs32(uint32_t rs, uint8_t k) { + constexpr uint32_t lo_bit = 0x01010101; + constexpr uint32_t mask = 0x7F7F7F7F; + constexpr uint32_t poly = 0x4D; + + uint32_t r = 0; + for(size_t i = 0; i != 8; ++i) { + const auto k_lo = CT::Mask::expand(k & 1); + r ^= k_lo.if_set_return(rs); + rs = ((rs & mask) << 1) ^ (((rs >> 7) & lo_bit) * poly); + k >>= 1; + } + return r; +} + +} // namespace Twofish_KS + inline void TF_E( uint32_t A, uint32_t B, uint32_t& C, uint32_t& D, uint32_t RK1, uint32_t RK2, const secure_vector& SB) { uint32_t X = SB[get_byte<3>(A)] ^ SB[256 + get_byte<2>(A)] ^ SB[512 + get_byte<1>(A)] ^ SB[768 + get_byte<0>(A)]; @@ -55,9 +173,26 @@ void Twofish::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); +#if defined(BOTAN_HAS_TWOFISH_AVX512) + if(!m_QS.empty()) { + while(blocks >= 16) { + avx512_encrypt_16(in, out); + in += 16 * BLOCK_SIZE; + out += 16 * BLOCK_SIZE; + blocks -= 16; + } + } +#endif + while(blocks >= 2) { - uint32_t A0, B0, C0, D0; - uint32_t A1, B1, C1, D1; + uint32_t A0 = 0; + uint32_t B0 = 0; + uint32_t C0 = 0; + uint32_t D0 = 0; + uint32_t A1 = 0; + uint32_t B1 = 0; + uint32_t C1 = 0; + uint32_t D1 = 0; load_le(in, A0, B0, C0, D0, A1, B1, C1, D1); A0 ^= m_RK[0]; @@ -93,8 +228,11 @@ in += 2 * BLOCK_SIZE; } - if(blocks) { - uint32_t A, B, C, D; + if(blocks > 0) { + uint32_t A = 0; + uint32_t B = 0; + uint32_t C = 0; + uint32_t D = 0; load_le(in, A, B, C, D); A ^= m_RK[0]; @@ -122,9 +260,26 @@ void Twofish::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const { assert_key_material_set(); +#if defined(BOTAN_HAS_TWOFISH_AVX512) + if(!m_QS.empty()) { + while(blocks >= 16) { + avx512_decrypt_16(in, out); + in += 16 * BLOCK_SIZE; + out += 16 * BLOCK_SIZE; + blocks -= 16; + } + } +#endif + while(blocks >= 2) { - uint32_t A0, B0, C0, D0; - uint32_t A1, B1, C1, D1; + uint32_t A0 = 0; + uint32_t B0 = 0; + uint32_t C0 = 0; + uint32_t D0 = 0; + uint32_t A1 = 0; + uint32_t B1 = 0; + uint32_t C1 = 0; + uint32_t D1 = 0; load_le(in, A0, B0, C0, D0, A1, B1, C1, D1); A0 ^= m_RK[4]; @@ -160,8 +315,11 @@ in += 2 * BLOCK_SIZE; } - if(blocks) { - uint32_t A, B, C, D; + if(blocks > 0) { + uint32_t A = 0; + uint32_t B = 0; + uint32_t C = 0; + uint32_t D = 0; load_le(in, A, B, C, D); A ^= m_RK[4]; @@ -187,47 +345,77 @@ return !m_SB.empty(); } +std::string Twofish::provider() const { +#if defined(BOTAN_HAS_TWOFISH_AVX512) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return "avx512"; + } +#endif + return "base"; +} + +size_t Twofish::parallelism() const { +#if defined(BOTAN_HAS_TWOFISH_AVX512) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + return 16; + } +#endif + return 1; +} + /* * Twofish Key Schedule */ void Twofish::key_schedule(std::span key) { - m_SB.resize(1024); + using namespace Twofish_KS; + + // Reed-Solomon matrix for key schedule (Twofish paper Section 4.3) + // in column-major form + + // clang-format off + constexpr uint32_t RS32[8] = { + 0x01A402A4, + 0xA456A155, + 0x5582FC87, + 0x87F3C15A, + 0x5A1E4758, + 0x58C6AEDB, + 0xDB683D9E, + 0x9EE51903 + }; + // clang-format on + m_RK.resize(40); secure_vector S(16); for(size_t i = 0; i != key.size(); ++i) { - /* - * Do one column of the RS matrix multiplcation - */ - if(key[i]) { - uint8_t X = POLY_TO_EXP[key[i] - 1]; - - uint8_t RS1 = RS[(4 * i) % 32]; - uint8_t RS2 = RS[(4 * i + 1) % 32]; - uint8_t RS3 = RS[(4 * i + 2) % 32]; - uint8_t RS4 = RS[(4 * i + 3) % 32]; - - S[4 * (i / 8)] ^= EXP_TO_POLY[(X + POLY_TO_EXP[RS1 - 1]) % 255]; - S[4 * (i / 8) + 1] ^= EXP_TO_POLY[(X + POLY_TO_EXP[RS2 - 1]) % 255]; - S[4 * (i / 8) + 2] ^= EXP_TO_POLY[(X + POLY_TO_EXP[RS3 - 1]) % 255]; - S[4 * (i / 8) + 3] ^= EXP_TO_POLY[(X + POLY_TO_EXP[RS4 - 1]) % 255]; - } + const uint8_t ki = key[i]; + const size_t s_off = 4 * (i / 8); + + const uint32_t p = gf_mul_rs32(RS32[i % 8], ki); + + S[s_off + 0] ^= get_byte<0>(p); + S[s_off + 1] ^= get_byte<1>(p); + S[s_off + 2] ^= get_byte<2>(p); + S[s_off + 3] ^= get_byte<3>(p); } + secure_vector QS(1024); + if(key.size() == 16) { for(size_t i = 0; i != 256; ++i) { - m_SB[i] = MDS0[Q0[Q0[i] ^ S[0]] ^ S[4]]; - m_SB[256 + i] = MDS1[Q0[Q1[i] ^ S[1]] ^ S[5]]; - m_SB[512 + i] = MDS2[Q1[Q0[i] ^ S[2]] ^ S[6]]; - m_SB[768 + i] = MDS3[Q1[Q1[i] ^ S[3]] ^ S[7]]; + QS[i] = Q1[Q0[Q0[i] ^ S[0]] ^ S[4]]; + QS[256 + i] = Q0[Q0[Q1[i] ^ S[1]] ^ S[5]]; + QS[512 + i] = Q1[Q1[Q0[i] ^ S[2]] ^ S[6]]; + QS[768 + i] = Q0[Q1[Q1[i] ^ S[3]] ^ S[7]]; } for(size_t i = 0; i < 40; i += 2) { - uint32_t X = MDS0[Q0[Q0[i] ^ key[8]] ^ key[0]] ^ MDS1[Q0[Q1[i] ^ key[9]] ^ key[1]] ^ - MDS2[Q1[Q0[i] ^ key[10]] ^ key[2]] ^ MDS3[Q1[Q1[i] ^ key[11]] ^ key[3]]; - uint32_t Y = MDS0[Q0[Q0[i + 1] ^ key[12]] ^ key[4]] ^ MDS1[Q0[Q1[i + 1] ^ key[13]] ^ key[5]] ^ - MDS2[Q1[Q0[i + 1] ^ key[14]] ^ key[6]] ^ MDS3[Q1[Q1[i + 1] ^ key[15]] ^ key[7]]; + uint32_t X = mds0(Q1[Q0[Q0[i] ^ key[8]] ^ key[0]]) ^ mds1(Q0[Q0[Q1[i] ^ key[9]] ^ key[1]]) ^ + mds2(Q1[Q1[Q0[i] ^ key[10]] ^ key[2]]) ^ mds3(Q0[Q1[Q1[i] ^ key[11]] ^ key[3]]); + uint32_t Y = mds0(Q1[Q0[Q0[i + 1] ^ key[12]] ^ key[4]]) ^ mds1(Q0[Q0[Q1[i + 1] ^ key[13]] ^ key[5]]) ^ + mds2(Q1[Q1[Q0[i + 1] ^ key[14]] ^ key[6]]) ^ mds3(Q0[Q1[Q1[i + 1] ^ key[15]] ^ key[7]]); Y = rotl<8>(Y); X += Y; Y += X; @@ -237,20 +425,20 @@ } } else if(key.size() == 24) { for(size_t i = 0; i != 256; ++i) { - m_SB[i] = MDS0[Q0[Q0[Q1[i] ^ S[0]] ^ S[4]] ^ S[8]]; - m_SB[256 + i] = MDS1[Q0[Q1[Q1[i] ^ S[1]] ^ S[5]] ^ S[9]]; - m_SB[512 + i] = MDS2[Q1[Q0[Q0[i] ^ S[2]] ^ S[6]] ^ S[10]]; - m_SB[768 + i] = MDS3[Q1[Q1[Q0[i] ^ S[3]] ^ S[7]] ^ S[11]]; + QS[i] = Q1[Q0[Q0[Q1[i] ^ S[0]] ^ S[4]] ^ S[8]]; + QS[256 + i] = Q0[Q0[Q1[Q1[i] ^ S[1]] ^ S[5]] ^ S[9]]; + QS[512 + i] = Q1[Q1[Q0[Q0[i] ^ S[2]] ^ S[6]] ^ S[10]]; + QS[768 + i] = Q0[Q1[Q1[Q0[i] ^ S[3]] ^ S[7]] ^ S[11]]; } for(size_t i = 0; i < 40; i += 2) { uint32_t X = - MDS0[Q0[Q0[Q1[i] ^ key[16]] ^ key[8]] ^ key[0]] ^ MDS1[Q0[Q1[Q1[i] ^ key[17]] ^ key[9]] ^ key[1]] ^ - MDS2[Q1[Q0[Q0[i] ^ key[18]] ^ key[10]] ^ key[2]] ^ MDS3[Q1[Q1[Q0[i] ^ key[19]] ^ key[11]] ^ key[3]]; - uint32_t Y = MDS0[Q0[Q0[Q1[i + 1] ^ key[20]] ^ key[12]] ^ key[4]] ^ - MDS1[Q0[Q1[Q1[i + 1] ^ key[21]] ^ key[13]] ^ key[5]] ^ - MDS2[Q1[Q0[Q0[i + 1] ^ key[22]] ^ key[14]] ^ key[6]] ^ - MDS3[Q1[Q1[Q0[i + 1] ^ key[23]] ^ key[15]] ^ key[7]]; + mds0(Q1[Q0[Q0[Q1[i] ^ key[16]] ^ key[8]] ^ key[0]]) ^ mds1(Q0[Q0[Q1[Q1[i] ^ key[17]] ^ key[9]] ^ key[1]]) ^ + mds2(Q1[Q1[Q0[Q0[i] ^ key[18]] ^ key[10]] ^ key[2]]) ^ mds3(Q0[Q1[Q1[Q0[i] ^ key[19]] ^ key[11]] ^ key[3]]); + uint32_t Y = mds0(Q1[Q0[Q0[Q1[i + 1] ^ key[20]] ^ key[12]] ^ key[4]]) ^ + mds1(Q0[Q0[Q1[Q1[i + 1] ^ key[21]] ^ key[13]] ^ key[5]]) ^ + mds2(Q1[Q1[Q0[Q0[i + 1] ^ key[22]] ^ key[14]] ^ key[6]]) ^ + mds3(Q0[Q1[Q1[Q0[i + 1] ^ key[23]] ^ key[15]] ^ key[7]]); Y = rotl<8>(Y); X += Y; Y += X; @@ -260,21 +448,21 @@ } } else if(key.size() == 32) { for(size_t i = 0; i != 256; ++i) { - m_SB[i] = MDS0[Q0[Q0[Q1[Q1[i] ^ S[0]] ^ S[4]] ^ S[8]] ^ S[12]]; - m_SB[256 + i] = MDS1[Q0[Q1[Q1[Q0[i] ^ S[1]] ^ S[5]] ^ S[9]] ^ S[13]]; - m_SB[512 + i] = MDS2[Q1[Q0[Q0[Q0[i] ^ S[2]] ^ S[6]] ^ S[10]] ^ S[14]]; - m_SB[768 + i] = MDS3[Q1[Q1[Q0[Q1[i] ^ S[3]] ^ S[7]] ^ S[11]] ^ S[15]]; + QS[i] = Q1[Q0[Q0[Q1[Q1[i] ^ S[0]] ^ S[4]] ^ S[8]] ^ S[12]]; + QS[256 + i] = Q0[Q0[Q1[Q1[Q0[i] ^ S[1]] ^ S[5]] ^ S[9]] ^ S[13]]; + QS[512 + i] = Q1[Q1[Q0[Q0[Q0[i] ^ S[2]] ^ S[6]] ^ S[10]] ^ S[14]]; + QS[768 + i] = Q0[Q1[Q1[Q0[Q1[i] ^ S[3]] ^ S[7]] ^ S[11]] ^ S[15]]; } for(size_t i = 0; i < 40; i += 2) { - uint32_t X = MDS0[Q0[Q0[Q1[Q1[i] ^ key[24]] ^ key[16]] ^ key[8]] ^ key[0]] ^ - MDS1[Q0[Q1[Q1[Q0[i] ^ key[25]] ^ key[17]] ^ key[9]] ^ key[1]] ^ - MDS2[Q1[Q0[Q0[Q0[i] ^ key[26]] ^ key[18]] ^ key[10]] ^ key[2]] ^ - MDS3[Q1[Q1[Q0[Q1[i] ^ key[27]] ^ key[19]] ^ key[11]] ^ key[3]]; - uint32_t Y = MDS0[Q0[Q0[Q1[Q1[i + 1] ^ key[28]] ^ key[20]] ^ key[12]] ^ key[4]] ^ - MDS1[Q0[Q1[Q1[Q0[i + 1] ^ key[29]] ^ key[21]] ^ key[13]] ^ key[5]] ^ - MDS2[Q1[Q0[Q0[Q0[i + 1] ^ key[30]] ^ key[22]] ^ key[14]] ^ key[6]] ^ - MDS3[Q1[Q1[Q0[Q1[i + 1] ^ key[31]] ^ key[23]] ^ key[15]] ^ key[7]]; + uint32_t X = mds0(Q1[Q0[Q0[Q1[Q1[i] ^ key[24]] ^ key[16]] ^ key[8]] ^ key[0]]) ^ + mds1(Q0[Q0[Q1[Q1[Q0[i] ^ key[25]] ^ key[17]] ^ key[9]] ^ key[1]]) ^ + mds2(Q1[Q1[Q0[Q0[Q0[i] ^ key[26]] ^ key[18]] ^ key[10]] ^ key[2]]) ^ + mds3(Q0[Q1[Q1[Q0[Q1[i] ^ key[27]] ^ key[19]] ^ key[11]] ^ key[3]]); + uint32_t Y = mds0(Q1[Q0[Q0[Q1[Q1[i + 1] ^ key[28]] ^ key[20]] ^ key[12]] ^ key[4]]) ^ + mds1(Q0[Q0[Q1[Q1[Q0[i + 1] ^ key[29]] ^ key[21]] ^ key[13]] ^ key[5]]) ^ + mds2(Q1[Q1[Q0[Q0[Q0[i + 1] ^ key[30]] ^ key[22]] ^ key[14]] ^ key[6]]) ^ + mds3(Q0[Q1[Q1[Q0[Q1[i + 1] ^ key[31]] ^ key[23]] ^ key[15]] ^ key[7]]); Y = rotl<8>(Y); X += Y; Y += X; @@ -283,6 +471,20 @@ m_RK[i + 1] = rotl<9>(Y); } } + + m_SB.resize(1024); + for(size_t i = 0; i != 256; ++i) { + m_SB[i] = mds0(QS[i]); + m_SB[256 + i] = mds1(QS[256 + i]); + m_SB[512 + i] = mds2(QS[512 + i]); + m_SB[768 + i] = mds3(QS[768 + i]); + } + +#if defined(BOTAN_HAS_TWOFISH_AVX512) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::GFNI)) { + m_QS = std::move(QS); + } +#endif } /* @@ -291,6 +493,7 @@ void Twofish::clear() { zap(m_SB); zap(m_RK); + zap(m_QS); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/twofish/twofish.h botan3-3.12.0+dfsg/src/lib/block/twofish/twofish.h --- botan3-3.7.1+dfsg/src/lib/block/twofish/twofish.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/twofish/twofish.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #define BOTAN_TWOFISH_H_ #include +#include namespace Botan { @@ -21,27 +22,28 @@ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override; void clear() override; + std::string provider() const override; std::string name() const override { return "Twofish"; } std::unique_ptr new_object() const override { return std::make_unique(); } + size_t parallelism() const override; + bool has_keying_material() const override; private: void key_schedule(std::span key) override; - static const uint32_t MDS0[256]; - static const uint32_t MDS1[256]; - static const uint32_t MDS2[256]; - static const uint32_t MDS3[256]; - static const uint8_t Q0[256]; - static const uint8_t Q1[256]; - static const uint8_t RS[32]; - static const uint8_t EXP_TO_POLY[255]; - static const uint8_t POLY_TO_EXP[255]; +#if defined(BOTAN_HAS_TWOFISH_AVX512) + void avx512_encrypt_16(const uint8_t in[16 * 16], uint8_t out[16 * 16]) const; + void avx512_decrypt_16(const uint8_t in[16 * 16], uint8_t out[16 * 16]) const; +#endif + + secure_vector m_SB; + secure_vector m_RK; - secure_vector m_SB, m_RK; + secure_vector m_QS; // Sboxes without MDS applied, only used for AVX-512 }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/twofish/twofish_avx512/info.txt botan3-3.12.0+dfsg/src/lib/block/twofish/twofish_avx512/info.txt --- botan3-3.7.1+dfsg/src/lib/block/twofish/twofish_avx512/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/twofish/twofish_avx512/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,22 @@ + +TWOFISH_AVX512 -> 20260318 + + + +name -> "Twofish AVX-512" +brief -> "Twofish using AVX-512 and GFNI instructions" + + + +avx512 +gfni + + + +cpuid +simd_avx512 + + + +!msvc + diff -Nru botan3-3.7.1+dfsg/src/lib/block/twofish/twofish_avx512/twofish_avx512.cpp botan3-3.12.0+dfsg/src/lib/block/twofish/twofish_avx512/twofish_avx512.cpp --- botan3-3.7.1+dfsg/src/lib/block/twofish/twofish_avx512/twofish_avx512.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/twofish/twofish_avx512/twofish_avx512.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,197 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include + +namespace Botan { + +namespace { + +namespace Twofish_AVX512 { + +// NOLINTBEGIN(portability-simd-intrinsics) + +template +BOTAN_FN_ISA_AVX512_GFNI BOTAN_FORCE_INLINE __m512i lookup_sbox(const SIMD_16x32 W, const uint8_t* QS) { + static_assert(N < 4); + + // Parallel sbox lookup using permutations + blend + + const auto q0 = _mm512_loadu_si512(QS); + const auto q1 = _mm512_loadu_si512(QS + 64); + const auto q2 = _mm512_loadu_si512(QS + 128); + const auto q3 = _mm512_loadu_si512(QS + 192); + + const auto bytemask = _mm512_set1_epi32(0xFF); + const auto idx = _mm512_and_si512(_mm512_srli_epi32(W.raw(), N * 8), bytemask); + + // Select on both Q[0-128] and Q[128-256] using the low 7 bits + const __m512i lo = _mm512_permutex2var_epi8(q0, idx, q1); + const __m512i hi = _mm512_permutex2var_epi8(q2, idx, q3); + + // Then select between those results using the top bit + return _mm512_mask_blend_epi8(_mm512_movepi8_mask(idx), lo, hi); +} + +BOTAN_FN_ISA_AVX512_GFNI +BOTAN_FORCE_INLINE SIMD_16x32 apply_mds(__m512i q, __m512i mds_gfni) { + // clang-format off + alignas(64) constexpr uint8_t MDS_PRE_SHUFFLE[64] = { + 0, 4, 8, 12, 16, 20, 24, 28, 0, 4, 8, 12, 16, 20, 24, 28, + 0, 4, 8, 12, 16, 20, 24, 28, 0, 4, 8, 12, 16, 20, 24, 28, + 32, 36, 40, 44, 48, 52, 56, 60, 32, 36, 40, 44, 48, 52, 56, 60, + 32, 36, 40, 44, 48, 52, 56, 60, 32, 36, 40, 44, 48, 52, 56, 60, + }; + + alignas(64) constexpr uint8_t MDS_POST_SHUFFLE[64] = { + 0, 8, 16, 24, 1, 9, 17, 25, 2, 10, 18, 26, 3, 11, 19, 27, + 4, 12, 20, 28, 5, 13, 21, 29, 6, 14, 22, 30, 7, 15, 23, 31, + 32, 40, 48, 56, 33, 41, 49, 57, 34, 42, 50, 58, 35, 43, 51, 59, + 36, 44, 52, 60, 37, 45, 53, 61, 38, 46, 54, 62, 39, 47, 55, 63, + }; + // clang-format on + + const __m512i pre = _mm512_permutexvar_epi8(_mm512_load_si512(MDS_PRE_SHUFFLE), q); + const __m512i transformed = _mm512_gf2p8affine_epi64_epi8(pre, mds_gfni, 0); + return SIMD_16x32(_mm512_permutexvar_epi8(_mm512_load_si512(MDS_POST_SHUFFLE), transformed)); +} + +BOTAN_FN_ISA_AVX512_GFNI +BOTAN_FORCE_INLINE SIMD_16x32 g_func(SIMD_16x32 W, const uint8_t* QS) { + constexpr uint64_t GFNI_ID = 0x0102040810204080; + constexpr uint64_t GFNI_5B = 0x050B162953A24182; + constexpr uint64_t GFNI_EF = 0x070F1F3972E3C183; + + const __m512i MDS0 = _mm512_set_epi64(GFNI_EF, GFNI_EF, GFNI_5B, GFNI_ID, GFNI_EF, GFNI_EF, GFNI_5B, GFNI_ID); + const __m512i MDS1 = _mm512_set_epi64(GFNI_ID, GFNI_5B, GFNI_EF, GFNI_EF, GFNI_ID, GFNI_5B, GFNI_EF, GFNI_EF); + const __m512i MDS2 = _mm512_set_epi64(GFNI_EF, GFNI_ID, GFNI_EF, GFNI_5B, GFNI_EF, GFNI_ID, GFNI_EF, GFNI_5B); + const __m512i MDS3 = _mm512_set_epi64(GFNI_5B, GFNI_EF, GFNI_ID, GFNI_5B, GFNI_5B, GFNI_EF, GFNI_ID, GFNI_5B); + + const auto r0 = apply_mds(lookup_sbox<0>(W, QS), MDS0); + const auto r1 = apply_mds(lookup_sbox<1>(W, QS + 256), MDS1); + const auto r2 = apply_mds(lookup_sbox<2>(W, QS + 512), MDS2); + const auto r3 = apply_mds(lookup_sbox<3>(W, QS + 768), MDS3); + + return (r0 ^ r1 ^ r2 ^ r3); +} + +// NOLINTEND(portability-simd-intrinsics) + +BOTAN_FN_ISA_AVX512_GFNI +BOTAN_FORCE_INLINE void twofish_encrypt_round( + SIMD_16x32 A, SIMD_16x32 B, SIMD_16x32& C, SIMD_16x32& D, uint32_t rk1, uint32_t rk2, const uint8_t* QS) { + SIMD_16x32 X = g_func(A, QS); + SIMD_16x32 Y = g_func(B.rotl<8>(), QS); + + X += Y; + Y += X; + + X += SIMD_16x32::splat(rk1); + Y += SIMD_16x32::splat(rk2); + + C = (C ^ X).rotr<1>(); + D = D.rotl<1>() ^ Y; +} + +BOTAN_FN_ISA_AVX512_GFNI +BOTAN_FORCE_INLINE void twofish_decrypt_round( + SIMD_16x32 A, SIMD_16x32 B, SIMD_16x32& C, SIMD_16x32& D, uint32_t rk1, uint32_t rk2, const uint8_t* QS) { + SIMD_16x32 X = g_func(A, QS); + SIMD_16x32 Y = g_func(B.rotl<8>(), QS); + + X += Y; + Y += X; + + X += SIMD_16x32::splat(rk1); + Y += SIMD_16x32::splat(rk2); + + C = C.rotl<1>() ^ X; + D = (D ^ Y).rotr<1>(); +} + +} // namespace Twofish_AVX512 + +} // namespace + +void BOTAN_FN_ISA_AVX512_GFNI Twofish::avx512_encrypt_16(const uint8_t in[16 * 16], uint8_t out[16 * 16]) const { + using namespace Twofish_AVX512; + + SIMD_16x32 B0 = SIMD_16x32::load_le(in); + SIMD_16x32 B1 = SIMD_16x32::load_le(in + 64); + SIMD_16x32 B2 = SIMD_16x32::load_le(in + 128); + SIMD_16x32 B3 = SIMD_16x32::load_le(in + 192); + + SIMD_16x32::transpose(B0, B1, B2, B3); + + B0 ^= SIMD_16x32::splat(m_RK[0]); + B1 ^= SIMD_16x32::splat(m_RK[1]); + B2 ^= SIMD_16x32::splat(m_RK[2]); + B3 ^= SIMD_16x32::splat(m_RK[3]); + + const uint8_t* QS = m_QS.data(); + + for(size_t k = 8; k != 40; k += 4) { + twofish_encrypt_round(B0, B1, B2, B3, m_RK[k], m_RK[k + 1], QS); + twofish_encrypt_round(B2, B3, B0, B1, m_RK[k + 2], m_RK[k + 3], QS); + } + + B2 ^= SIMD_16x32::splat(m_RK[4]); + B3 ^= SIMD_16x32::splat(m_RK[5]); + B0 ^= SIMD_16x32::splat(m_RK[6]); + B1 ^= SIMD_16x32::splat(m_RK[7]); + + SIMD_16x32::transpose(B2, B3, B0, B1); + + B2.store_le(out); + B3.store_le(out + 64); + B0.store_le(out + 128); + B1.store_le(out + 192); + + SIMD_16x32::zero_registers(); +} + +void BOTAN_FN_ISA_AVX512_GFNI Twofish::avx512_decrypt_16(const uint8_t in[16 * 16], uint8_t out[16 * 16]) const { + using namespace Twofish_AVX512; + + SIMD_16x32 B0 = SIMD_16x32::load_le(in); + SIMD_16x32 B1 = SIMD_16x32::load_le(in + 64); + SIMD_16x32 B2 = SIMD_16x32::load_le(in + 128); + SIMD_16x32 B3 = SIMD_16x32::load_le(in + 192); + + SIMD_16x32::transpose(B0, B1, B2, B3); + + B0 ^= SIMD_16x32::splat(m_RK[4]); + B1 ^= SIMD_16x32::splat(m_RK[5]); + B2 ^= SIMD_16x32::splat(m_RK[6]); + B3 ^= SIMD_16x32::splat(m_RK[7]); + + const uint8_t* QS = m_QS.data(); + + for(size_t k = 40; k != 8; k -= 4) { + twofish_decrypt_round(B0, B1, B2, B3, m_RK[k - 2], m_RK[k - 1], QS); + twofish_decrypt_round(B2, B3, B0, B1, m_RK[k - 4], m_RK[k - 3], QS); + } + + B2 ^= SIMD_16x32::splat(m_RK[0]); + B3 ^= SIMD_16x32::splat(m_RK[1]); + B0 ^= SIMD_16x32::splat(m_RK[2]); + B1 ^= SIMD_16x32::splat(m_RK[3]); + + SIMD_16x32::transpose(B2, B3, B0, B1); + + B2.store_le(out); + B3.store_le(out + 64); + B0.store_le(out + 128); + B1.store_le(out + 192); + + SIMD_16x32::zero_registers(); +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/block/twofish/twofish_tab.cpp botan3-3.12.0+dfsg/src/lib/block/twofish/twofish_tab.cpp --- botan3-3.7.1+dfsg/src/lib/block/twofish/twofish_tab.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/block/twofish/twofish_tab.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,204 +0,0 @@ -/* -* S-Box and MDS Tables for Twofish -* (C) 1999-2007 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -namespace Botan { - -alignas(256) const uint8_t Twofish::Q0[256] = { - 0xA9, 0x67, 0xB3, 0xE8, 0x04, 0xFD, 0xA3, 0x76, 0x9A, 0x92, 0x80, 0x78, 0xE4, 0xDD, 0xD1, 0x38, 0x0D, 0xC6, 0x35, - 0x98, 0x18, 0xF7, 0xEC, 0x6C, 0x43, 0x75, 0x37, 0x26, 0xFA, 0x13, 0x94, 0x48, 0xF2, 0xD0, 0x8B, 0x30, 0x84, 0x54, - 0xDF, 0x23, 0x19, 0x5B, 0x3D, 0x59, 0xF3, 0xAE, 0xA2, 0x82, 0x63, 0x01, 0x83, 0x2E, 0xD9, 0x51, 0x9B, 0x7C, 0xA6, - 0xEB, 0xA5, 0xBE, 0x16, 0x0C, 0xE3, 0x61, 0xC0, 0x8C, 0x3A, 0xF5, 0x73, 0x2C, 0x25, 0x0B, 0xBB, 0x4E, 0x89, 0x6B, - 0x53, 0x6A, 0xB4, 0xF1, 0xE1, 0xE6, 0xBD, 0x45, 0xE2, 0xF4, 0xB6, 0x66, 0xCC, 0x95, 0x03, 0x56, 0xD4, 0x1C, 0x1E, - 0xD7, 0xFB, 0xC3, 0x8E, 0xB5, 0xE9, 0xCF, 0xBF, 0xBA, 0xEA, 0x77, 0x39, 0xAF, 0x33, 0xC9, 0x62, 0x71, 0x81, 0x79, - 0x09, 0xAD, 0x24, 0xCD, 0xF9, 0xD8, 0xE5, 0xC5, 0xB9, 0x4D, 0x44, 0x08, 0x86, 0xE7, 0xA1, 0x1D, 0xAA, 0xED, 0x06, - 0x70, 0xB2, 0xD2, 0x41, 0x7B, 0xA0, 0x11, 0x31, 0xC2, 0x27, 0x90, 0x20, 0xF6, 0x60, 0xFF, 0x96, 0x5C, 0xB1, 0xAB, - 0x9E, 0x9C, 0x52, 0x1B, 0x5F, 0x93, 0x0A, 0xEF, 0x91, 0x85, 0x49, 0xEE, 0x2D, 0x4F, 0x8F, 0x3B, 0x47, 0x87, 0x6D, - 0x46, 0xD6, 0x3E, 0x69, 0x64, 0x2A, 0xCE, 0xCB, 0x2F, 0xFC, 0x97, 0x05, 0x7A, 0xAC, 0x7F, 0xD5, 0x1A, 0x4B, 0x0E, - 0xA7, 0x5A, 0x28, 0x14, 0x3F, 0x29, 0x88, 0x3C, 0x4C, 0x02, 0xB8, 0xDA, 0xB0, 0x17, 0x55, 0x1F, 0x8A, 0x7D, 0x57, - 0xC7, 0x8D, 0x74, 0xB7, 0xC4, 0x9F, 0x72, 0x7E, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34, 0x6E, 0x50, 0xDE, 0x68, - 0x65, 0xBC, 0xDB, 0xF8, 0xC8, 0xA8, 0x2B, 0x40, 0xDC, 0xFE, 0x32, 0xA4, 0xCA, 0x10, 0x21, 0xF0, 0xD3, 0x5D, 0x0F, - 0x00, 0x6F, 0x9D, 0x36, 0x42, 0x4A, 0x5E, 0xC1, 0xE0}; - -alignas(256) const uint8_t Twofish::Q1[256] = { - 0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8, 0x4A, 0xD3, 0xE6, 0x6B, 0x45, 0x7D, 0xE8, 0x4B, 0xD6, 0x32, 0xD8, - 0xFD, 0x37, 0x71, 0xF1, 0xE1, 0x30, 0x0F, 0xF8, 0x1B, 0x87, 0xFA, 0x06, 0x3F, 0x5E, 0xBA, 0xAE, 0x5B, 0x8A, 0x00, - 0xBC, 0x9D, 0x6D, 0xC1, 0xB1, 0x0E, 0x80, 0x5D, 0xD2, 0xD5, 0xA0, 0x84, 0x07, 0x14, 0xB5, 0x90, 0x2C, 0xA3, 0xB2, - 0x73, 0x4C, 0x54, 0x92, 0x74, 0x36, 0x51, 0x38, 0xB0, 0xBD, 0x5A, 0xFC, 0x60, 0x62, 0x96, 0x6C, 0x42, 0xF7, 0x10, - 0x7C, 0x28, 0x27, 0x8C, 0x13, 0x95, 0x9C, 0xC7, 0x24, 0x46, 0x3B, 0x70, 0xCA, 0xE3, 0x85, 0xCB, 0x11, 0xD0, 0x93, - 0xB8, 0xA6, 0x83, 0x20, 0xFF, 0x9F, 0x77, 0xC3, 0xCC, 0x03, 0x6F, 0x08, 0xBF, 0x40, 0xE7, 0x2B, 0xE2, 0x79, 0x0C, - 0xAA, 0x82, 0x41, 0x3A, 0xEA, 0xB9, 0xE4, 0x9A, 0xA4, 0x97, 0x7E, 0xDA, 0x7A, 0x17, 0x66, 0x94, 0xA1, 0x1D, 0x3D, - 0xF0, 0xDE, 0xB3, 0x0B, 0x72, 0xA7, 0x1C, 0xEF, 0xD1, 0x53, 0x3E, 0x8F, 0x33, 0x26, 0x5F, 0xEC, 0x76, 0x2A, 0x49, - 0x81, 0x88, 0xEE, 0x21, 0xC4, 0x1A, 0xEB, 0xD9, 0xC5, 0x39, 0x99, 0xCD, 0xAD, 0x31, 0x8B, 0x01, 0x18, 0x23, 0xDD, - 0x1F, 0x4E, 0x2D, 0xF9, 0x48, 0x4F, 0xF2, 0x65, 0x8E, 0x78, 0x5C, 0x58, 0x19, 0x8D, 0xE5, 0x98, 0x57, 0x67, 0x7F, - 0x05, 0x64, 0xAF, 0x63, 0xB6, 0xFE, 0xF5, 0xB7, 0x3C, 0xA5, 0xCE, 0xE9, 0x68, 0x44, 0xE0, 0x4D, 0x43, 0x69, 0x29, - 0x2E, 0xAC, 0x15, 0x59, 0xA8, 0x0A, 0x9E, 0x6E, 0x47, 0xDF, 0x34, 0x35, 0x6A, 0xCF, 0xDC, 0x22, 0xC9, 0xC0, 0x9B, - 0x89, 0xD4, 0xED, 0xAB, 0x12, 0xA2, 0x0D, 0x52, 0xBB, 0x02, 0x2F, 0xA9, 0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, - 0xC2, 0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xBE, 0x91}; - -alignas(64) const uint8_t Twofish::RS[32] = {0x01, 0xA4, 0x02, 0xA4, 0xA4, 0x56, 0xA1, 0x55, 0x55, 0x82, 0xFC, - 0x87, 0x87, 0xF3, 0xC1, 0x5A, 0x5A, 0x1E, 0x47, 0x58, 0x58, 0xC6, - 0xAE, 0xDB, 0xDB, 0x68, 0x3D, 0x9E, 0x9E, 0xE5, 0x19, 0x03}; - -alignas(256) const uint8_t Twofish::EXP_TO_POLY[255] = { - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x4D, 0x9A, 0x79, 0xF2, 0xA9, 0x1F, 0x3E, 0x7C, 0xF8, 0xBD, 0x37, - 0x6E, 0xDC, 0xF5, 0xA7, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0xCD, 0xD7, 0xE3, 0x8B, 0x5B, 0xB6, 0x21, 0x42, - 0x84, 0x45, 0x8A, 0x59, 0xB2, 0x29, 0x52, 0xA4, 0x05, 0x0A, 0x14, 0x28, 0x50, 0xA0, 0x0D, 0x1A, 0x34, 0x68, 0xD0, - 0xED, 0x97, 0x63, 0xC6, 0xC1, 0xCF, 0xD3, 0xEB, 0x9B, 0x7B, 0xF6, 0xA1, 0x0F, 0x1E, 0x3C, 0x78, 0xF0, 0xAD, 0x17, - 0x2E, 0x5C, 0xB8, 0x3D, 0x7A, 0xF4, 0xA5, 0x07, 0x0E, 0x1C, 0x38, 0x70, 0xE0, 0x8D, 0x57, 0xAE, 0x11, 0x22, 0x44, - 0x88, 0x5D, 0xBA, 0x39, 0x72, 0xE4, 0x85, 0x47, 0x8E, 0x51, 0xA2, 0x09, 0x12, 0x24, 0x48, 0x90, 0x6D, 0xDA, 0xF9, - 0xBF, 0x33, 0x66, 0xCC, 0xD5, 0xE7, 0x83, 0x4B, 0x96, 0x61, 0xC2, 0xC9, 0xDF, 0xF3, 0xAB, 0x1B, 0x36, 0x6C, 0xD8, - 0xFD, 0xB7, 0x23, 0x46, 0x8C, 0x55, 0xAA, 0x19, 0x32, 0x64, 0xC8, 0xDD, 0xF7, 0xA3, 0x0B, 0x16, 0x2C, 0x58, 0xB0, - 0x2D, 0x5A, 0xB4, 0x25, 0x4A, 0x94, 0x65, 0xCA, 0xD9, 0xFF, 0xB3, 0x2B, 0x56, 0xAC, 0x15, 0x2A, 0x54, 0xA8, 0x1D, - 0x3A, 0x74, 0xE8, 0x9D, 0x77, 0xEE, 0x91, 0x6F, 0xDE, 0xF1, 0xAF, 0x13, 0x26, 0x4C, 0x98, 0x7D, 0xFA, 0xB9, 0x3F, - 0x7E, 0xFC, 0xB5, 0x27, 0x4E, 0x9C, 0x75, 0xEA, 0x99, 0x7F, 0xFE, 0xB1, 0x2F, 0x5E, 0xBC, 0x35, 0x6A, 0xD4, 0xE5, - 0x87, 0x43, 0x86, 0x41, 0x82, 0x49, 0x92, 0x69, 0xD2, 0xE9, 0x9F, 0x73, 0xE6, 0x81, 0x4F, 0x9E, 0x71, 0xE2, 0x89, - 0x5F, 0xBE, 0x31, 0x62, 0xC4, 0xC5, 0xC7, 0xC3, 0xCB, 0xDB, 0xFB, 0xBB, 0x3B, 0x76, 0xEC, 0x95, 0x67, 0xCE, 0xD1, - 0xEF, 0x93, 0x6B, 0xD6, 0xE1, 0x8F, 0x53, 0xA6}; - -alignas(256) const uint8_t Twofish::POLY_TO_EXP[255] = { - 0x00, 0x01, 0x17, 0x02, 0x2E, 0x18, 0x53, 0x03, 0x6A, 0x2F, 0x93, 0x19, 0x34, 0x54, 0x45, 0x04, 0x5C, 0x6B, 0xB6, - 0x30, 0xA6, 0x94, 0x4B, 0x1A, 0x8C, 0x35, 0x81, 0x55, 0xAA, 0x46, 0x0D, 0x05, 0x24, 0x5D, 0x87, 0x6C, 0x9B, 0xB7, - 0xC1, 0x31, 0x2B, 0xA7, 0xA3, 0x95, 0x98, 0x4C, 0xCA, 0x1B, 0xE6, 0x8D, 0x73, 0x36, 0xCD, 0x82, 0x12, 0x56, 0x62, - 0xAB, 0xF0, 0x47, 0x4F, 0x0E, 0xBD, 0x06, 0xD4, 0x25, 0xD2, 0x5E, 0x27, 0x88, 0x66, 0x6D, 0xD6, 0x9C, 0x79, 0xB8, - 0x08, 0xC2, 0xDF, 0x32, 0x68, 0x2C, 0xFD, 0xA8, 0x8A, 0xA4, 0x5A, 0x96, 0x29, 0x99, 0x22, 0x4D, 0x60, 0xCB, 0xE4, - 0x1C, 0x7B, 0xE7, 0x3B, 0x8E, 0x9E, 0x74, 0xF4, 0x37, 0xD8, 0xCE, 0xF9, 0x83, 0x6F, 0x13, 0xB2, 0x57, 0xE1, 0x63, - 0xDC, 0xAC, 0xC4, 0xF1, 0xAF, 0x48, 0x0A, 0x50, 0x42, 0x0F, 0xBA, 0xBE, 0xC7, 0x07, 0xDE, 0xD5, 0x78, 0x26, 0x65, - 0xD3, 0xD1, 0x5F, 0xE3, 0x28, 0x21, 0x89, 0x59, 0x67, 0xFC, 0x6E, 0xB1, 0xD7, 0xF8, 0x9D, 0xF3, 0x7A, 0x3A, 0xB9, - 0xC6, 0x09, 0x41, 0xC3, 0xAE, 0xE0, 0xDB, 0x33, 0x44, 0x69, 0x92, 0x2D, 0x52, 0xFE, 0x16, 0xA9, 0x0C, 0x8B, 0x80, - 0xA5, 0x4A, 0x5B, 0xB5, 0x97, 0xC9, 0x2A, 0xA2, 0x9A, 0xC0, 0x23, 0x86, 0x4E, 0xBC, 0x61, 0xEF, 0xCC, 0x11, 0xE5, - 0x72, 0x1D, 0x3D, 0x7C, 0xEB, 0xE8, 0xE9, 0x3C, 0xEA, 0x8F, 0x7D, 0x9F, 0xEC, 0x75, 0x1E, 0xF5, 0x3E, 0x38, 0xF6, - 0xD9, 0x3F, 0xCF, 0x76, 0xFA, 0x1F, 0x84, 0xA0, 0x70, 0xED, 0x14, 0x90, 0xB3, 0x7E, 0x58, 0xFB, 0xE2, 0x20, 0x64, - 0xD0, 0xDD, 0x77, 0xAD, 0xDA, 0xC5, 0x40, 0xF2, 0x39, 0xB0, 0xF7, 0x49, 0xB4, 0x0B, 0x7F, 0x51, 0x15, 0x43, 0x91, - 0x10, 0x71, 0xBB, 0xEE, 0xBF, 0x85, 0xC8, 0xA1}; - -alignas(256) const uint32_t Twofish::MDS0[256] = { - 0xBCBC3275, 0xECEC21F3, 0x202043C6, 0xB3B3C9F4, 0xDADA03DB, 0x02028B7B, 0xE2E22BFB, 0x9E9EFAC8, 0xC9C9EC4A, - 0xD4D409D3, 0x18186BE6, 0x1E1E9F6B, 0x98980E45, 0xB2B2387D, 0xA6A6D2E8, 0x2626B74B, 0x3C3C57D6, 0x93938A32, - 0x8282EED8, 0x525298FD, 0x7B7BD437, 0xBBBB3771, 0x5B5B97F1, 0x474783E1, 0x24243C30, 0x5151E20F, 0xBABAC6F8, - 0x4A4AF31B, 0xBFBF4887, 0x0D0D70FA, 0xB0B0B306, 0x7575DE3F, 0xD2D2FD5E, 0x7D7D20BA, 0x666631AE, 0x3A3AA35B, - 0x59591C8A, 0x00000000, 0xCDCD93BC, 0x1A1AE09D, 0xAEAE2C6D, 0x7F7FABC1, 0x2B2BC7B1, 0xBEBEB90E, 0xE0E0A080, - 0x8A8A105D, 0x3B3B52D2, 0x6464BAD5, 0xD8D888A0, 0xE7E7A584, 0x5F5FE807, 0x1B1B1114, 0x2C2CC2B5, 0xFCFCB490, - 0x3131272C, 0x808065A3, 0x73732AB2, 0x0C0C8173, 0x79795F4C, 0x6B6B4154, 0x4B4B0292, 0x53536974, 0x94948F36, - 0x83831F51, 0x2A2A3638, 0xC4C49CB0, 0x2222C8BD, 0xD5D5F85A, 0xBDBDC3FC, 0x48487860, 0xFFFFCE62, 0x4C4C0796, - 0x4141776C, 0xC7C7E642, 0xEBEB24F7, 0x1C1C1410, 0x5D5D637C, 0x36362228, 0x6767C027, 0xE9E9AF8C, 0x4444F913, - 0x1414EA95, 0xF5F5BB9C, 0xCFCF18C7, 0x3F3F2D24, 0xC0C0E346, 0x7272DB3B, 0x54546C70, 0x29294CCA, 0xF0F035E3, - 0x0808FE85, 0xC6C617CB, 0xF3F34F11, 0x8C8CE4D0, 0xA4A45993, 0xCACA96B8, 0x68683BA6, 0xB8B84D83, 0x38382820, - 0xE5E52EFF, 0xADAD569F, 0x0B0B8477, 0xC8C81DC3, 0x9999FFCC, 0x5858ED03, 0x19199A6F, 0x0E0E0A08, 0x95957EBF, - 0x70705040, 0xF7F730E7, 0x6E6ECF2B, 0x1F1F6EE2, 0xB5B53D79, 0x09090F0C, 0x616134AA, 0x57571682, 0x9F9F0B41, - 0x9D9D803A, 0x111164EA, 0x2525CDB9, 0xAFAFDDE4, 0x4545089A, 0xDFDF8DA4, 0xA3A35C97, 0xEAEAD57E, 0x353558DA, - 0xEDEDD07A, 0x4343FC17, 0xF8F8CB66, 0xFBFBB194, 0x3737D3A1, 0xFAFA401D, 0xC2C2683D, 0xB4B4CCF0, 0x32325DDE, - 0x9C9C71B3, 0x5656E70B, 0xE3E3DA72, 0x878760A7, 0x15151B1C, 0xF9F93AEF, 0x6363BFD1, 0x3434A953, 0x9A9A853E, - 0xB1B1428F, 0x7C7CD133, 0x88889B26, 0x3D3DA65F, 0xA1A1D7EC, 0xE4E4DF76, 0x8181942A, 0x91910149, 0x0F0FFB81, - 0xEEEEAA88, 0x161661EE, 0xD7D77321, 0x9797F5C4, 0xA5A5A81A, 0xFEFE3FEB, 0x6D6DB5D9, 0x7878AEC5, 0xC5C56D39, - 0x1D1DE599, 0x7676A4CD, 0x3E3EDCAD, 0xCBCB6731, 0xB6B6478B, 0xEFEF5B01, 0x12121E18, 0x6060C523, 0x6A6AB0DD, - 0x4D4DF61F, 0xCECEE94E, 0xDEDE7C2D, 0x55559DF9, 0x7E7E5A48, 0x2121B24F, 0x03037AF2, 0xA0A02665, 0x5E5E198E, - 0x5A5A6678, 0x65654B5C, 0x62624E58, 0xFDFD4519, 0x0606F48D, 0x404086E5, 0xF2F2BE98, 0x3333AC57, 0x17179067, - 0x05058E7F, 0xE8E85E05, 0x4F4F7D64, 0x89896AAF, 0x10109563, 0x74742FB6, 0x0A0A75FE, 0x5C5C92F5, 0x9B9B74B7, - 0x2D2D333C, 0x3030D6A5, 0x2E2E49CE, 0x494989E9, 0x46467268, 0x77775544, 0xA8A8D8E0, 0x9696044D, 0x2828BD43, - 0xA9A92969, 0xD9D97929, 0x8686912E, 0xD1D187AC, 0xF4F44A15, 0x8D8D1559, 0xD6D682A8, 0xB9B9BC0A, 0x42420D9E, - 0xF6F6C16E, 0x2F2FB847, 0xDDDD06DF, 0x23233934, 0xCCCC6235, 0xF1F1C46A, 0xC1C112CF, 0x8585EBDC, 0x8F8F9E22, - 0x7171A1C9, 0x9090F0C0, 0xAAAA539B, 0x0101F189, 0x8B8BE1D4, 0x4E4E8CED, 0x8E8E6FAB, 0xABABA212, 0x6F6F3EA2, - 0xE6E6540D, 0xDBDBF252, 0x92927BBB, 0xB7B7B602, 0x6969CA2F, 0x3939D9A9, 0xD3D30CD7, 0xA7A72361, 0xA2A2AD1E, - 0xC3C399B4, 0x6C6C4450, 0x07070504, 0x04047FF6, 0x272746C2, 0xACACA716, 0xD0D07625, 0x50501386, 0xDCDCF756, - 0x84841A55, 0xE1E15109, 0x7A7A25BE, 0x1313EF91}; - -alignas(256) const uint32_t Twofish::MDS1[256] = { - 0xA9D93939, 0x67901717, 0xB3719C9C, 0xE8D2A6A6, 0x04050707, 0xFD985252, 0xA3658080, 0x76DFE4E4, 0x9A084545, - 0x92024B4B, 0x80A0E0E0, 0x78665A5A, 0xE4DDAFAF, 0xDDB06A6A, 0xD1BF6363, 0x38362A2A, 0x0D54E6E6, 0xC6432020, - 0x3562CCCC, 0x98BEF2F2, 0x181E1212, 0xF724EBEB, 0xECD7A1A1, 0x6C774141, 0x43BD2828, 0x7532BCBC, 0x37D47B7B, - 0x269B8888, 0xFA700D0D, 0x13F94444, 0x94B1FBFB, 0x485A7E7E, 0xF27A0303, 0xD0E48C8C, 0x8B47B6B6, 0x303C2424, - 0x84A5E7E7, 0x54416B6B, 0xDF06DDDD, 0x23C56060, 0x1945FDFD, 0x5BA33A3A, 0x3D68C2C2, 0x59158D8D, 0xF321ECEC, - 0xAE316666, 0xA23E6F6F, 0x82165757, 0x63951010, 0x015BEFEF, 0x834DB8B8, 0x2E918686, 0xD9B56D6D, 0x511F8383, - 0x9B53AAAA, 0x7C635D5D, 0xA63B6868, 0xEB3FFEFE, 0xA5D63030, 0xBE257A7A, 0x16A7ACAC, 0x0C0F0909, 0xE335F0F0, - 0x6123A7A7, 0xC0F09090, 0x8CAFE9E9, 0x3A809D9D, 0xF5925C5C, 0x73810C0C, 0x2C273131, 0x2576D0D0, 0x0BE75656, - 0xBB7B9292, 0x4EE9CECE, 0x89F10101, 0x6B9F1E1E, 0x53A93434, 0x6AC4F1F1, 0xB499C3C3, 0xF1975B5B, 0xE1834747, - 0xE66B1818, 0xBDC82222, 0x450E9898, 0xE26E1F1F, 0xF4C9B3B3, 0xB62F7474, 0x66CBF8F8, 0xCCFF9999, 0x95EA1414, - 0x03ED5858, 0x56F7DCDC, 0xD4E18B8B, 0x1C1B1515, 0x1EADA2A2, 0xD70CD3D3, 0xFB2BE2E2, 0xC31DC8C8, 0x8E195E5E, - 0xB5C22C2C, 0xE9894949, 0xCF12C1C1, 0xBF7E9595, 0xBA207D7D, 0xEA641111, 0x77840B0B, 0x396DC5C5, 0xAF6A8989, - 0x33D17C7C, 0xC9A17171, 0x62CEFFFF, 0x7137BBBB, 0x81FB0F0F, 0x793DB5B5, 0x0951E1E1, 0xADDC3E3E, 0x242D3F3F, - 0xCDA47676, 0xF99D5555, 0xD8EE8282, 0xE5864040, 0xC5AE7878, 0xB9CD2525, 0x4D049696, 0x44557777, 0x080A0E0E, - 0x86135050, 0xE730F7F7, 0xA1D33737, 0x1D40FAFA, 0xAA346161, 0xED8C4E4E, 0x06B3B0B0, 0x706C5454, 0xB22A7373, - 0xD2523B3B, 0x410B9F9F, 0x7B8B0202, 0xA088D8D8, 0x114FF3F3, 0x3167CBCB, 0xC2462727, 0x27C06767, 0x90B4FCFC, - 0x20283838, 0xF67F0404, 0x60784848, 0xFF2EE5E5, 0x96074C4C, 0x5C4B6565, 0xB1C72B2B, 0xAB6F8E8E, 0x9E0D4242, - 0x9CBBF5F5, 0x52F2DBDB, 0x1BF34A4A, 0x5FA63D3D, 0x9359A4A4, 0x0ABCB9B9, 0xEF3AF9F9, 0x91EF1313, 0x85FE0808, - 0x49019191, 0xEE611616, 0x2D7CDEDE, 0x4FB22121, 0x8F42B1B1, 0x3BDB7272, 0x47B82F2F, 0x8748BFBF, 0x6D2CAEAE, - 0x46E3C0C0, 0xD6573C3C, 0x3E859A9A, 0x6929A9A9, 0x647D4F4F, 0x2A948181, 0xCE492E2E, 0xCB17C6C6, 0x2FCA6969, - 0xFCC3BDBD, 0x975CA3A3, 0x055EE8E8, 0x7AD0EDED, 0xAC87D1D1, 0x7F8E0505, 0xD5BA6464, 0x1AA8A5A5, 0x4BB72626, - 0x0EB9BEBE, 0xA7608787, 0x5AF8D5D5, 0x28223636, 0x14111B1B, 0x3FDE7575, 0x2979D9D9, 0x88AAEEEE, 0x3C332D2D, - 0x4C5F7979, 0x02B6B7B7, 0xB896CACA, 0xDA583535, 0xB09CC4C4, 0x17FC4343, 0x551A8484, 0x1FF64D4D, 0x8A1C5959, - 0x7D38B2B2, 0x57AC3333, 0xC718CFCF, 0x8DF40606, 0x74695353, 0xB7749B9B, 0xC4F59797, 0x9F56ADAD, 0x72DAE3E3, - 0x7ED5EAEA, 0x154AF4F4, 0x229E8F8F, 0x12A2ABAB, 0x584E6262, 0x07E85F5F, 0x99E51D1D, 0x34392323, 0x6EC1F6F6, - 0x50446C6C, 0xDE5D3232, 0x68724646, 0x6526A0A0, 0xBC93CDCD, 0xDB03DADA, 0xF8C6BABA, 0xC8FA9E9E, 0xA882D6D6, - 0x2BCF6E6E, 0x40507070, 0xDCEB8585, 0xFE750A0A, 0x328A9393, 0xA48DDFDF, 0xCA4C2929, 0x10141C1C, 0x2173D7D7, - 0xF0CCB4B4, 0xD309D4D4, 0x5D108A8A, 0x0FE25151, 0x00000000, 0x6F9A1919, 0x9DE01A1A, 0x368F9494, 0x42E6C7C7, - 0x4AECC9C9, 0x5EFDD2D2, 0xC1AB7F7F, 0xE0D8A8A8}; - -alignas(256) const uint32_t Twofish::MDS2[256] = { - 0xBC75BC32, 0xECF3EC21, 0x20C62043, 0xB3F4B3C9, 0xDADBDA03, 0x027B028B, 0xE2FBE22B, 0x9EC89EFA, 0xC94AC9EC, - 0xD4D3D409, 0x18E6186B, 0x1E6B1E9F, 0x9845980E, 0xB27DB238, 0xA6E8A6D2, 0x264B26B7, 0x3CD63C57, 0x9332938A, - 0x82D882EE, 0x52FD5298, 0x7B377BD4, 0xBB71BB37, 0x5BF15B97, 0x47E14783, 0x2430243C, 0x510F51E2, 0xBAF8BAC6, - 0x4A1B4AF3, 0xBF87BF48, 0x0DFA0D70, 0xB006B0B3, 0x753F75DE, 0xD25ED2FD, 0x7DBA7D20, 0x66AE6631, 0x3A5B3AA3, - 0x598A591C, 0x00000000, 0xCDBCCD93, 0x1A9D1AE0, 0xAE6DAE2C, 0x7FC17FAB, 0x2BB12BC7, 0xBE0EBEB9, 0xE080E0A0, - 0x8A5D8A10, 0x3BD23B52, 0x64D564BA, 0xD8A0D888, 0xE784E7A5, 0x5F075FE8, 0x1B141B11, 0x2CB52CC2, 0xFC90FCB4, - 0x312C3127, 0x80A38065, 0x73B2732A, 0x0C730C81, 0x794C795F, 0x6B546B41, 0x4B924B02, 0x53745369, 0x9436948F, - 0x8351831F, 0x2A382A36, 0xC4B0C49C, 0x22BD22C8, 0xD55AD5F8, 0xBDFCBDC3, 0x48604878, 0xFF62FFCE, 0x4C964C07, - 0x416C4177, 0xC742C7E6, 0xEBF7EB24, 0x1C101C14, 0x5D7C5D63, 0x36283622, 0x672767C0, 0xE98CE9AF, 0x441344F9, - 0x149514EA, 0xF59CF5BB, 0xCFC7CF18, 0x3F243F2D, 0xC046C0E3, 0x723B72DB, 0x5470546C, 0x29CA294C, 0xF0E3F035, - 0x088508FE, 0xC6CBC617, 0xF311F34F, 0x8CD08CE4, 0xA493A459, 0xCAB8CA96, 0x68A6683B, 0xB883B84D, 0x38203828, - 0xE5FFE52E, 0xAD9FAD56, 0x0B770B84, 0xC8C3C81D, 0x99CC99FF, 0x580358ED, 0x196F199A, 0x0E080E0A, 0x95BF957E, - 0x70407050, 0xF7E7F730, 0x6E2B6ECF, 0x1FE21F6E, 0xB579B53D, 0x090C090F, 0x61AA6134, 0x57825716, 0x9F419F0B, - 0x9D3A9D80, 0x11EA1164, 0x25B925CD, 0xAFE4AFDD, 0x459A4508, 0xDFA4DF8D, 0xA397A35C, 0xEA7EEAD5, 0x35DA3558, - 0xED7AEDD0, 0x431743FC, 0xF866F8CB, 0xFB94FBB1, 0x37A137D3, 0xFA1DFA40, 0xC23DC268, 0xB4F0B4CC, 0x32DE325D, - 0x9CB39C71, 0x560B56E7, 0xE372E3DA, 0x87A78760, 0x151C151B, 0xF9EFF93A, 0x63D163BF, 0x345334A9, 0x9A3E9A85, - 0xB18FB142, 0x7C337CD1, 0x8826889B, 0x3D5F3DA6, 0xA1ECA1D7, 0xE476E4DF, 0x812A8194, 0x91499101, 0x0F810FFB, - 0xEE88EEAA, 0x16EE1661, 0xD721D773, 0x97C497F5, 0xA51AA5A8, 0xFEEBFE3F, 0x6DD96DB5, 0x78C578AE, 0xC539C56D, - 0x1D991DE5, 0x76CD76A4, 0x3EAD3EDC, 0xCB31CB67, 0xB68BB647, 0xEF01EF5B, 0x1218121E, 0x602360C5, 0x6ADD6AB0, - 0x4D1F4DF6, 0xCE4ECEE9, 0xDE2DDE7C, 0x55F9559D, 0x7E487E5A, 0x214F21B2, 0x03F2037A, 0xA065A026, 0x5E8E5E19, - 0x5A785A66, 0x655C654B, 0x6258624E, 0xFD19FD45, 0x068D06F4, 0x40E54086, 0xF298F2BE, 0x335733AC, 0x17671790, - 0x057F058E, 0xE805E85E, 0x4F644F7D, 0x89AF896A, 0x10631095, 0x74B6742F, 0x0AFE0A75, 0x5CF55C92, 0x9BB79B74, - 0x2D3C2D33, 0x30A530D6, 0x2ECE2E49, 0x49E94989, 0x46684672, 0x77447755, 0xA8E0A8D8, 0x964D9604, 0x284328BD, - 0xA969A929, 0xD929D979, 0x862E8691, 0xD1ACD187, 0xF415F44A, 0x8D598D15, 0xD6A8D682, 0xB90AB9BC, 0x429E420D, - 0xF66EF6C1, 0x2F472FB8, 0xDDDFDD06, 0x23342339, 0xCC35CC62, 0xF16AF1C4, 0xC1CFC112, 0x85DC85EB, 0x8F228F9E, - 0x71C971A1, 0x90C090F0, 0xAA9BAA53, 0x018901F1, 0x8BD48BE1, 0x4EED4E8C, 0x8EAB8E6F, 0xAB12ABA2, 0x6FA26F3E, - 0xE60DE654, 0xDB52DBF2, 0x92BB927B, 0xB702B7B6, 0x692F69CA, 0x39A939D9, 0xD3D7D30C, 0xA761A723, 0xA21EA2AD, - 0xC3B4C399, 0x6C506C44, 0x07040705, 0x04F6047F, 0x27C22746, 0xAC16ACA7, 0xD025D076, 0x50865013, 0xDC56DCF7, - 0x8455841A, 0xE109E151, 0x7ABE7A25, 0x139113EF}; - -alignas(256) const uint32_t Twofish::MDS3[256] = { - 0xD939A9D9, 0x90176790, 0x719CB371, 0xD2A6E8D2, 0x05070405, 0x9852FD98, 0x6580A365, 0xDFE476DF, 0x08459A08, - 0x024B9202, 0xA0E080A0, 0x665A7866, 0xDDAFE4DD, 0xB06ADDB0, 0xBF63D1BF, 0x362A3836, 0x54E60D54, 0x4320C643, - 0x62CC3562, 0xBEF298BE, 0x1E12181E, 0x24EBF724, 0xD7A1ECD7, 0x77416C77, 0xBD2843BD, 0x32BC7532, 0xD47B37D4, - 0x9B88269B, 0x700DFA70, 0xF94413F9, 0xB1FB94B1, 0x5A7E485A, 0x7A03F27A, 0xE48CD0E4, 0x47B68B47, 0x3C24303C, - 0xA5E784A5, 0x416B5441, 0x06DDDF06, 0xC56023C5, 0x45FD1945, 0xA33A5BA3, 0x68C23D68, 0x158D5915, 0x21ECF321, - 0x3166AE31, 0x3E6FA23E, 0x16578216, 0x95106395, 0x5BEF015B, 0x4DB8834D, 0x91862E91, 0xB56DD9B5, 0x1F83511F, - 0x53AA9B53, 0x635D7C63, 0x3B68A63B, 0x3FFEEB3F, 0xD630A5D6, 0x257ABE25, 0xA7AC16A7, 0x0F090C0F, 0x35F0E335, - 0x23A76123, 0xF090C0F0, 0xAFE98CAF, 0x809D3A80, 0x925CF592, 0x810C7381, 0x27312C27, 0x76D02576, 0xE7560BE7, - 0x7B92BB7B, 0xE9CE4EE9, 0xF10189F1, 0x9F1E6B9F, 0xA93453A9, 0xC4F16AC4, 0x99C3B499, 0x975BF197, 0x8347E183, - 0x6B18E66B, 0xC822BDC8, 0x0E98450E, 0x6E1FE26E, 0xC9B3F4C9, 0x2F74B62F, 0xCBF866CB, 0xFF99CCFF, 0xEA1495EA, - 0xED5803ED, 0xF7DC56F7, 0xE18BD4E1, 0x1B151C1B, 0xADA21EAD, 0x0CD3D70C, 0x2BE2FB2B, 0x1DC8C31D, 0x195E8E19, - 0xC22CB5C2, 0x8949E989, 0x12C1CF12, 0x7E95BF7E, 0x207DBA20, 0x6411EA64, 0x840B7784, 0x6DC5396D, 0x6A89AF6A, - 0xD17C33D1, 0xA171C9A1, 0xCEFF62CE, 0x37BB7137, 0xFB0F81FB, 0x3DB5793D, 0x51E10951, 0xDC3EADDC, 0x2D3F242D, - 0xA476CDA4, 0x9D55F99D, 0xEE82D8EE, 0x8640E586, 0xAE78C5AE, 0xCD25B9CD, 0x04964D04, 0x55774455, 0x0A0E080A, - 0x13508613, 0x30F7E730, 0xD337A1D3, 0x40FA1D40, 0x3461AA34, 0x8C4EED8C, 0xB3B006B3, 0x6C54706C, 0x2A73B22A, - 0x523BD252, 0x0B9F410B, 0x8B027B8B, 0x88D8A088, 0x4FF3114F, 0x67CB3167, 0x4627C246, 0xC06727C0, 0xB4FC90B4, - 0x28382028, 0x7F04F67F, 0x78486078, 0x2EE5FF2E, 0x074C9607, 0x4B655C4B, 0xC72BB1C7, 0x6F8EAB6F, 0x0D429E0D, - 0xBBF59CBB, 0xF2DB52F2, 0xF34A1BF3, 0xA63D5FA6, 0x59A49359, 0xBCB90ABC, 0x3AF9EF3A, 0xEF1391EF, 0xFE0885FE, - 0x01914901, 0x6116EE61, 0x7CDE2D7C, 0xB2214FB2, 0x42B18F42, 0xDB723BDB, 0xB82F47B8, 0x48BF8748, 0x2CAE6D2C, - 0xE3C046E3, 0x573CD657, 0x859A3E85, 0x29A96929, 0x7D4F647D, 0x94812A94, 0x492ECE49, 0x17C6CB17, 0xCA692FCA, - 0xC3BDFCC3, 0x5CA3975C, 0x5EE8055E, 0xD0ED7AD0, 0x87D1AC87, 0x8E057F8E, 0xBA64D5BA, 0xA8A51AA8, 0xB7264BB7, - 0xB9BE0EB9, 0x6087A760, 0xF8D55AF8, 0x22362822, 0x111B1411, 0xDE753FDE, 0x79D92979, 0xAAEE88AA, 0x332D3C33, - 0x5F794C5F, 0xB6B702B6, 0x96CAB896, 0x5835DA58, 0x9CC4B09C, 0xFC4317FC, 0x1A84551A, 0xF64D1FF6, 0x1C598A1C, - 0x38B27D38, 0xAC3357AC, 0x18CFC718, 0xF4068DF4, 0x69537469, 0x749BB774, 0xF597C4F5, 0x56AD9F56, 0xDAE372DA, - 0xD5EA7ED5, 0x4AF4154A, 0x9E8F229E, 0xA2AB12A2, 0x4E62584E, 0xE85F07E8, 0xE51D99E5, 0x39233439, 0xC1F66EC1, - 0x446C5044, 0x5D32DE5D, 0x72466872, 0x26A06526, 0x93CDBC93, 0x03DADB03, 0xC6BAF8C6, 0xFA9EC8FA, 0x82D6A882, - 0xCF6E2BCF, 0x50704050, 0xEB85DCEB, 0x750AFE75, 0x8A93328A, 0x8DDFA48D, 0x4C29CA4C, 0x141C1014, 0x73D72173, - 0xCCB4F0CC, 0x09D4D309, 0x108A5D10, 0xE2510FE2, 0x00000000, 0x9A196F9A, 0xE01A9DE0, 0x8F94368F, 0xE6C742E6, - 0xECC94AEC, 0xFDD25EFD, 0xAB7FC1AB, 0xD8A8E0D8}; - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/codec/base32/base32.cpp botan3-3.12.0+dfsg/src/lib/codec/base32/base32.cpp --- botan3-3.7.1+dfsg/src/lib/codec/base32/base32.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/codec/base32/base32.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,7 +1,7 @@ /* * Base32 Encoding and Decoding * (C) 2018 Erwan Chaussy -* (C) 2018,2020 Jack Lloyd +* (C) 2018,2020,2025 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include namespace Botan { @@ -22,23 +24,23 @@ public: static std::string name() noexcept { return "base32"; } - static size_t encoding_bytes_in() noexcept { return m_encoding_bytes_in; } + static constexpr size_t encoding_bytes_in() noexcept { return m_encoding_bytes_in; } - static size_t encoding_bytes_out() noexcept { return m_encoding_bytes_out; } + static constexpr size_t encoding_bytes_out() noexcept { return m_encoding_bytes_out; } - static size_t decoding_bytes_in() noexcept { return m_encoding_bytes_out; } + static constexpr size_t decoding_bytes_in() noexcept { return m_encoding_bytes_out; } - static size_t decoding_bytes_out() noexcept { return m_encoding_bytes_in; } + static constexpr size_t decoding_bytes_out() noexcept { return m_encoding_bytes_in; } - static size_t bits_consumed() noexcept { return m_encoding_bits; } + static constexpr size_t bits_consumed() noexcept { return m_encoding_bits; } - static size_t remaining_bits_before_padding() noexcept { return m_remaining_bits_before_padding; } + static constexpr size_t remaining_bits_before_padding() noexcept { return m_remaining_bits_before_padding; } - static size_t encode_max_output(size_t input_length) { + static constexpr size_t encode_max_output(size_t input_length) { return (round_up(input_length, m_encoding_bytes_in) / m_encoding_bytes_in) * m_encoding_bytes_out; } - static size_t decode_max_output(size_t input_length) { + static constexpr size_t decode_max_output(size_t input_length) { return (round_up(input_length, m_encoding_bytes_out) * m_encoding_bytes_in) / m_encoding_bytes_out; } @@ -56,27 +58,26 @@ out_ptr[4] = (decode_buf[6] << 5) | decode_buf[7]; } - static size_t bytes_to_remove(size_t final_truncate) { return final_truncate ? (final_truncate / 2) + 1 : 0; } + static size_t bytes_to_remove(size_t final_truncate) { + return (final_truncate > 0) ? (final_truncate / 2) + 1 : 0; + } private: - static const size_t m_encoding_bits = 5; - static const size_t m_remaining_bits_before_padding = 6; + static constexpr size_t m_encoding_bits = 5; + static constexpr size_t m_remaining_bits_before_padding = 6; - static const size_t m_encoding_bytes_in = 5; - static const size_t m_encoding_bytes_out = 8; + static constexpr size_t m_encoding_bytes_in = 5; + static constexpr size_t m_encoding_bytes_out = 8; }; namespace { -char lookup_base32_char(uint8_t x) { - BOTAN_DEBUG_ASSERT(x < 32); - - const auto in_AZ = CT::Mask::is_lt(x, 26); +uint64_t lookup_base32_char(uint64_t x) { + uint64_t r = x; + r += swar_lt(x, 0x1a1a1a1a1a1a1a1a) & 0x2929292929292929; + r += 0x1818181818181818; - const char c_AZ = 'A' + x; - const char c_27 = '2' + (x - 26); - - return in_AZ.select(c_AZ, c_27); + return r; } } // namespace @@ -92,14 +93,16 @@ const uint8_t b6 = ((in[3] & 0x03) << 3) | (in[4] >> 5); const uint8_t b7 = in[4] & 0x1F; - out[0] = lookup_base32_char(b0); - out[1] = lookup_base32_char(b1); - out[2] = lookup_base32_char(b2); - out[3] = lookup_base32_char(b3); - out[4] = lookup_base32_char(b4); - out[5] = lookup_base32_char(b5); - out[6] = lookup_base32_char(b6); - out[7] = lookup_base32_char(b7); + auto b = lookup_base32_char(make_uint64(b0, b1, b2, b3, b4, b5, b6, b7)); + + out[0] = static_cast(get_byte<0>(b)); + out[1] = static_cast(get_byte<1>(b)); + out[2] = static_cast(get_byte<2>(b)); + out[3] = static_cast(get_byte<3>(b)); + out[4] = static_cast(get_byte<4>(b)); + out[5] = static_cast(get_byte<5>(b)); + out[6] = static_cast(get_byte<6>(b)); + out[7] = static_cast(get_byte<7>(b)); } //static @@ -167,4 +170,12 @@ return base32_decode(input.data(), input.size(), ignore_ws); } +size_t base32_encode_max_output(size_t input_length) { + return Base32::encode_max_output(input_length); +} + +size_t base32_decode_max_output(size_t input_length) { + return Base32::decode_max_output(input_length); +} + } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/codec/base32/base32.h botan3-3.12.0+dfsg/src/lib/codec/base32/base32.h --- botan3-3.7.1+dfsg/src/lib/codec/base32/base32.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/codec/base32/base32.h 2026-05-07 01:38:28.000000000 +0000 @@ -112,6 +112,20 @@ */ secure_vector BOTAN_PUBLIC_API(2, 7) base32_decode(std::string_view input, bool ignore_ws = true); +/** +* Calculate the size of output buffer for base32_encode +* @param input_length the length of input in bytes +* @return the size of output buffer in bytes +*/ +size_t BOTAN_PUBLIC_API(3, 8) base32_encode_max_output(size_t input_length); + +/** +* Calculate the size of output buffer for base32_decode +* @param input_length the length of input in bytes +* @return the size of output buffer in bytes +*/ +size_t BOTAN_PUBLIC_API(3, 8) base32_decode_max_output(size_t input_length); + } // namespace Botan #endif diff -Nru botan3-3.7.1+dfsg/src/lib/codec/base58/base58.cpp botan3-3.12.0+dfsg/src/lib/codec/base58/base58.cpp --- botan3-3.7.1+dfsg/src/lib/codec/base58/base58.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/codec/base58/base58.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,5 @@ /* -* (C) 2018,2020 Jack Lloyd +* (C) 2018,2020,2026 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include namespace Botan { @@ -35,43 +37,92 @@ // "123456789 ABCDEFGH JKLMN PQRSTUVWXYZ abcdefghijk mnopqrstuvwxyz" BOTAN_DEBUG_ASSERT(x < 58); - const auto is_dec_19 = CT::Mask::is_lte(x, 8); - const auto is_alpha_AH = CT::Mask::is_within_range(x, 9, 16); - const auto is_alpha_JN = CT::Mask::is_within_range(x, 17, 21); - const auto is_alpha_PZ = CT::Mask::is_within_range(x, 22, 32); - const auto is_alpha_ak = CT::Mask::is_within_range(x, 33, 43); - // otherwise in 'm'-'z' - - const char c_19 = '1' + x; - const char c_AH = 'A' + (x - 9); - const char c_JN = 'J' + (x - 17); - const char c_PZ = 'P' + (x - 22); - const char c_ak = 'a' + (x - 33); - const char c_mz = 'm' + (x - 44); - - char ret = c_mz; - ret = is_dec_19.select(c_19, ret); - ret = is_alpha_AH.select(c_AH, ret); - ret = is_alpha_JN.select(c_JN, ret); - ret = is_alpha_PZ.select(c_PZ, ret); - ret = is_alpha_ak.select(c_ak, ret); + // This works by computing offset(x) such that x + offset(x) is equal to the + // desired character - return ret; + size_t offset = 49; + + offset += CT::Mask::is_gt(x, 8).if_set_return(7); + offset += CT::Mask::is_gt(x, 16).if_set_return(1); + offset += CT::Mask::is_gt(x, 21).if_set_return(1); + offset += CT::Mask::is_gt(x, 32).if_set_return(6); + offset += CT::Mask::is_gt(x, 43).if_set_return(1); + return static_cast(x + offset); +} + +consteval word base58_conversion_radix() { + if constexpr(sizeof(word) == 8) { + // 58^10 largest that fits into a 64 bit word + return 430804206899405824U; + } else { + // 58^5 largest that fits into a 32 bit word + return 656356768U; + } +} + +consteval size_t base58_conversion_radix_digits() { + if constexpr(sizeof(word) == 8) { + return 10; + } else { + return 5; + } +} + +constexpr std::pair divmod_58(word x) { + BOTAN_DEBUG_ASSERT(x < base58_conversion_radix()); + + word q = 0; + + // Division by constant 58 + // + // Compilers will *usually* convert an expression like `x / 58` into + // exactly this kind of operation, but not necessarily always... + if constexpr(sizeof(word) == 4) { + const uint64_t magic = 2369637129; // ceil(2**36 / 29) + const uint64_t z = magic * x; + q = z >> 37; + } else { + const uint64_t magic = 5088756985850910791; // ceil(2**67 / 29) + uint64_t lo = 0; // unused + uint64_t hi = 0; + mul64x64_128(magic, x >> 1, &lo, &hi); + q = static_cast(hi >> 3); + } + + const uint8_t r = static_cast(x - q * 58); + return std::make_pair(r, q); } std::string base58_encode(BigInt v, size_t leading_zeros) { - const word radix = 58; + constexpr word radix = base58_conversion_radix(); + constexpr size_t radix_digits = base58_conversion_radix_digits(); - std::string result; BigInt q; + std::vector digits; - while(v.is_nonzero()) { - word r; + while(!v.is_zero()) { + word r = 0; ct_divide_word(v, radix, q, r); - result.push_back(lookup_base58_char(static_cast(r))); + + for(size_t i = 0; i != radix_digits; ++i) { + const auto [r58, q58] = divmod_58(r); + digits.push_back(r58); + r = q58; + } v.swap(q); } + // remove leading zeros + while(!digits.empty() && digits.back() == 0) { + digits.pop_back(); + } + + std::string result; + + for(const uint8_t d : digits) { + result.push_back(lookup_base58_char(d)); + } + for(size_t i = 0; i != leading_zeros; ++i) { result.push_back('1'); // 'zero' byte } @@ -91,41 +142,44 @@ } uint8_t base58_value_of(char input) { - // "123456789 ABCDEFGH JKLMN PQRSTUVWXYZ abcdefghijk mnopqrstuvwxyz" + /* + * Alphabet: "123456789 ABCDEFGH JKLMN PQRSTUVWXYZ abcdefghijk mnopqrstuvwxyz" + * + * Valid input ranges are: + * + * '1'-'9' (length 9) + * 'A'-'H' (length 8) + * 'J'-'N' (length 5) + * 'P'-'Z' (length 11) + * 'a'-'k' (length 11) + * 'm'-'z' (length 14) + */ + constexpr uint64_t v_lo = make_uint64(0, '1', 'A', 'J', 'P', 'a', 'm', 0); + constexpr uint64_t v_range = make_uint64(0, 9, 8, 5, 11, 11, 14, 0); + + const uint8_t x = static_cast(input); + const uint64_t x8 = x * 0x0101010101010101; // replicate x to each byte + + // is x8 in any of the ranges? + const uint64_t v_mask = swar_in_range(x8, v_lo, v_range) ^ 0x8000000000000000; + + /* + * Offsets mapping from the character code x to the base58 value of x in each range + * + * For example '2' (50) + 0xCF == 1 + * + * Fallback byte 7 is set to 0xFF - x so that if used it results in 0xFF to indicate invalid. + */ + constexpr uint64_t val_v_const = make_uint64(0, 0xCF, 0xC8, 0xC7, 0xC6, 0xC0, 0xBF, 0); + const uint64_t val_v = val_v_const ^ (static_cast(0xFF - x) << 56); - const uint8_t c = static_cast(input); - - const auto is_dec_19 = CT::Mask::is_within_range(c, uint8_t('1'), uint8_t('9')); - const auto is_alpha_AH = CT::Mask::is_within_range(c, uint8_t('A'), uint8_t('H')); - const auto is_alpha_JN = CT::Mask::is_within_range(c, uint8_t('J'), uint8_t('N')); - const auto is_alpha_PZ = CT::Mask::is_within_range(c, uint8_t('P'), uint8_t('Z')); - - const auto is_alpha_ak = CT::Mask::is_within_range(c, uint8_t('a'), uint8_t('k')); - const auto is_alpha_mz = CT::Mask::is_within_range(c, uint8_t('m'), uint8_t('z')); - - const uint8_t c_dec_19 = c - uint8_t('1'); - const uint8_t c_AH = c - uint8_t('A') + 9; - const uint8_t c_JN = c - uint8_t('J') + 17; - const uint8_t c_PZ = c - uint8_t('P') + 22; - - const uint8_t c_ak = c - uint8_t('a') + 33; - const uint8_t c_mz = c - uint8_t('m') + 44; - - uint8_t ret = 0xFF; // default value - - ret = is_dec_19.select(c_dec_19, ret); - ret = is_alpha_AH.select(c_AH, ret); - ret = is_alpha_JN.select(c_JN, ret); - ret = is_alpha_PZ.select(c_PZ, ret); - ret = is_alpha_ak.select(c_ak, ret); - ret = is_alpha_mz.select(c_mz, ret); - return ret; + return x + static_cast(val_v >> (8 * index_of_first_set_byte(v_mask))); } } // namespace std::string base58_encode(const uint8_t input[], size_t input_length) { - BigInt v(input, input_length); + const BigInt v(input, input_length); return base58_encode(v, count_leading_zeros(input, input_length, 0)); } @@ -139,7 +193,7 @@ std::vector base58_decode(const char input[], size_t input_length) { const size_t leading_zeros = count_leading_zeros(input, input_length, '1'); - BigInt v; + std::vector digits; for(size_t i = leading_zeros; i != input_length; ++i) { const char c = input[i]; @@ -154,8 +208,29 @@ throw Decoding_Error("Invalid base58"); } + digits.push_back(idx); + } + + BigInt v; + + constexpr word radix1 = 58; + constexpr word radix2 = 58 * 58; + constexpr word radix3 = 58 * 58 * 58; + constexpr word radix4 = 58 * 58 * 58 * 58; + + std::span remaining{digits}; + + while(remaining.size() >= 4) { + const word accum = radix3 * remaining[0] + radix2 * remaining[1] + radix1 * remaining[2] + remaining[3]; + v *= radix4; + v += accum; + remaining = remaining.subspan(4); + } + + while(!remaining.empty()) { v *= 58; - v += idx; + v += remaining[0]; + remaining = remaining.subspan(1); } return v.serialize(v.bytes() + leading_zeros); diff -Nru botan3-3.7.1+dfsg/src/lib/codec/base58/base58.h botan3-3.12.0+dfsg/src/lib/codec/base58/base58.h --- botan3-3.7.1+dfsg/src/lib/codec/base58/base58.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/codec/base58/base58.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,7 +9,6 @@ #include -#include #include #include #include diff -Nru botan3-3.7.1+dfsg/src/lib/codec/base64/base64.cpp botan3-3.12.0+dfsg/src/lib/codec/base64/base64.cpp --- botan3-3.7.1+dfsg/src/lib/codec/base64/base64.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/codec/base64/base64.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -24,23 +23,23 @@ public: static std::string name() noexcept { return "base64"; } - static size_t encoding_bytes_in() noexcept { return m_encoding_bytes_in; } + static constexpr size_t encoding_bytes_in() noexcept { return m_encoding_bytes_in; } - static size_t encoding_bytes_out() noexcept { return m_encoding_bytes_out; } + static constexpr size_t encoding_bytes_out() noexcept { return m_encoding_bytes_out; } - static size_t decoding_bytes_in() noexcept { return m_encoding_bytes_out; } + static constexpr size_t decoding_bytes_in() noexcept { return m_encoding_bytes_out; } - static size_t decoding_bytes_out() noexcept { return m_encoding_bytes_in; } + static constexpr size_t decoding_bytes_out() noexcept { return m_encoding_bytes_in; } - static size_t bits_consumed() noexcept { return m_encoding_bits; } + static constexpr size_t bits_consumed() noexcept { return m_encoding_bits; } - static size_t remaining_bits_before_padding() noexcept { return m_remaining_bits_before_padding; } + static constexpr size_t remaining_bits_before_padding() noexcept { return m_remaining_bits_before_padding; } - static size_t encode_max_output(size_t input_length) { + static constexpr size_t encode_max_output(size_t input_length) { return (round_up(input_length, m_encoding_bytes_in) / m_encoding_bytes_in) * m_encoding_bytes_out; } - static size_t decode_max_output(size_t input_length) { + static constexpr size_t decode_max_output(size_t input_length) { return (round_up(input_length, m_encoding_bytes_out) * m_encoding_bytes_in) / m_encoding_bytes_out; } @@ -59,11 +58,11 @@ static size_t bytes_to_remove(size_t final_truncate) { return final_truncate; } private: - static const size_t m_encoding_bits = 6; - static const size_t m_remaining_bits_before_padding = 8; + static constexpr size_t m_encoding_bits = 6; + static constexpr size_t m_remaining_bits_before_padding = 8; - static const size_t m_encoding_bytes_in = 3; - static const size_t m_encoding_bytes_out = 4; + static constexpr size_t m_encoding_bytes_in = 3; + static constexpr size_t m_encoding_bytes_out = 4; }; uint32_t lookup_base64_chars(uint32_t x32) { @@ -133,7 +132,7 @@ // This is the offset added to x to get the value const uint64_t val_v = 0xbfb904 ^ (0xFF000000 - (x << 24)); - uint8_t z = x + static_cast(val_v >> (8 * index_of_first_set_byte(v_mask))); + const uint8_t z = x + static_cast(val_v >> (8 * index_of_first_set_byte(v_mask))); // Valid base64 special characters, and some whitespace chars constexpr uint64_t specials_i = make_uint64(0, '+', '/', '=', ' ', '\n', '\t', '\r'); diff -Nru botan3-3.7.1+dfsg/src/lib/codec/hex/hex.cpp botan3-3.12.0+dfsg/src/lib/codec/hex/hex.cpp --- botan3-3.7.1+dfsg/src/lib/codec/hex/hex.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/codec/hex/hex.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -43,7 +42,7 @@ std::string hex_encode(const uint8_t input[], size_t input_length, bool uppercase) { std::string output(2 * input_length, 0); - if(input_length) { + if(input_length > 0) { hex_encode(&output.front(), input, input_length, uppercase); } @@ -100,7 +99,7 @@ } input_consumed = input_length; - size_t written = (out_ptr - output); + const size_t written = (out_ptr - output); /* * We only got half of a uint8_t at the end; zap the half-written @@ -116,7 +115,7 @@ size_t hex_decode(uint8_t output[], const char input[], size_t input_length, bool ignore_ws) { size_t consumed = 0; - size_t written = hex_decode(output, input, input_length, consumed, ignore_ws); + const size_t written = hex_decode(output, input, input_length, consumed, ignore_ws); if(consumed != input_length) { throw Invalid_Argument("hex_decode: input did not have full bytes"); @@ -136,7 +135,7 @@ secure_vector hex_decode_locked(const char input[], size_t input_length, bool ignore_ws) { secure_vector bin(1 + input_length / 2); - size_t written = hex_decode(bin.data(), input, input_length, ignore_ws); + const size_t written = hex_decode(bin.data(), input, input_length, ignore_ws); bin.resize(written); return bin; @@ -149,7 +148,7 @@ std::vector hex_decode(const char input[], size_t input_length, bool ignore_ws) { std::vector bin(1 + input_length / 2); - size_t written = hex_decode(bin.data(), input, input_length, ignore_ws); + const size_t written = hex_decode(bin.data(), input, input_length, ignore_ws); bin.resize(written); return bin; diff -Nru botan3-3.7.1+dfsg/src/lib/compat/sodium/sodium.h botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium.h --- botan3-3.7.1+dfsg/src/lib/compat/sodium/sodium.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium.h 2026-05-07 01:38:28.000000000 +0000 @@ -16,7 +16,7 @@ namespace Botan::Sodium { // sodium/randombytes.h -enum Sodium_Constants : size_t { +enum Sodium_Constants : uint32_t /* NOLINT(*-use-enum-class) */ { SODIUM_SIZE_MAX = 0xFFFFFFFF, crypto_aead_chacha20poly1305_ABYTES = 16, @@ -1138,7 +1138,7 @@ // sodium/crypto_stream_salsa20.h inline size_t crypto_stream_salsa20_keybytes() { - return crypto_stream_xsalsa20_KEYBYTES; + return crypto_stream_salsa20_KEYBYTES; } inline size_t crypto_stream_salsa20_noncebytes() { @@ -1254,7 +1254,7 @@ BOTAN_PUBLIC_API(2, 11) int crypto_sign_ed25519_detached( - uint8_t sig[], unsigned long long* sig_len, const uint8_t msg[], size_t msg_len, const uint8_t sk[32]); + uint8_t sig[], unsigned long long* sig_len, const uint8_t msg[], size_t msg_len, const uint8_t sk[64]); BOTAN_PUBLIC_API(2, 11) int crypto_sign_ed25519_verify_detached(const uint8_t sig[], const uint8_t msg[], size_t msg_len, const uint8_t pk[32]); @@ -1291,16 +1291,16 @@ return "ed25519"; } -inline int crypto_sign_seed_keypair(uint8_t pk[32], uint8_t sk[32], const uint8_t seed[]) { +inline int crypto_sign_seed_keypair(uint8_t pk[32], uint8_t sk[64], const uint8_t seed[]) { return crypto_sign_ed25519_seed_keypair(pk, sk, seed); } -inline int crypto_sign_keypair(uint8_t pk[32], uint8_t sk[32]) { +inline int crypto_sign_keypair(uint8_t pk[32], uint8_t sk[64]) { return crypto_sign_ed25519_keypair(pk, sk); } inline int crypto_sign_detached( - uint8_t sig[], unsigned long long* sig_len, const uint8_t msg[], size_t msg_len, const uint8_t sk[32]) { + uint8_t sig[], unsigned long long* sig_len, const uint8_t msg[], size_t msg_len, const uint8_t sk[64]) { return crypto_sign_ed25519_detached(sig, sig_len, msg, msg_len, sk); } diff -Nru botan3-3.7.1+dfsg/src/lib/compat/sodium/sodium_25519.cpp botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium_25519.cpp --- botan3-3.7.1+dfsg/src/lib/compat/sodium/sodium_25519.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium_25519.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,12 +8,14 @@ #include #include +#include namespace Botan { int Sodium::crypto_scalarmult_curve25519(uint8_t out[32], const uint8_t scalar[32], const uint8_t point[32]) { curve25519_donna(out, scalar, point); - return 0; + // Return -1 if the result is the identity + return -static_cast(CT::all_zeros(out, 32).if_set_return(1)); } int Sodium::crypto_scalarmult_curve25519_base(uint8_t out[32], const uint8_t scalar[32]) { @@ -22,10 +24,10 @@ } int Sodium::crypto_sign_ed25519_detached( - uint8_t sig[], unsigned long long* sig_len, const uint8_t msg[], size_t msg_len, const uint8_t sk[32]) { + uint8_t sig[], unsigned long long* sig_len, const uint8_t msg[], size_t msg_len, const uint8_t sk[64]) { ed25519_sign(sig, msg, msg_len, sk, nullptr, 0); - if(sig_len) { + if(sig_len != nullptr) { *sig_len = 64; } return 0; diff -Nru botan3-3.7.1+dfsg/src/lib/compat/sodium/sodium_aead.cpp botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium_aead.cpp --- botan3-3.7.1+dfsg/src/lib/compat/sodium/sodium_aead.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium_aead.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,7 @@ #include #include +#include #include namespace Botan { @@ -36,7 +37,7 @@ chacha20poly1305->finish(buf); copy_mem(ctext, buf.data(), buf.size()); - if(ctext_len) { + if(ctext_len != nullptr) { *ctext_len = buf.size(); } return 0; @@ -55,7 +56,9 @@ return -1; } - *ptext_len = 0; + if(ptext_len != nullptr) { + *ptext_len = 0; + } auto chacha20poly1305 = AEAD_Mode::create_or_throw("ChaCha20Poly1305", Cipher_Dir::Decryption); @@ -73,7 +76,9 @@ return -1; } - *ptext_len = ctext_len - 16; + if(ptext_len != nullptr) { + *ptext_len = ctext_len - 16; + } copy_mem(ptext, buf.data(), buf.size()); return 0; @@ -181,7 +186,7 @@ const uint8_t key[]) { BOTAN_UNUSED(unused_secret_nonce); - if(mac_len) { + if(mac_len != nullptr) { *mac_len = 16; } @@ -243,7 +248,7 @@ const uint8_t nonce[], const uint8_t key[]) { BOTAN_UNUSED(unused_secret_nonce); - if(mac_len) { + if(mac_len != nullptr) { *mac_len = 16; } @@ -307,7 +312,7 @@ const uint8_t nonce[], const uint8_t key[]) { BOTAN_UNUSED(unused_secret_nonce); - if(mac_len) { + if(mac_len != nullptr) { *mac_len = 16; } diff -Nru botan3-3.7.1+dfsg/src/lib/compat/sodium/sodium_auth.cpp botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium_auth.cpp --- botan3-3.7.1+dfsg/src/lib/compat/sodium/sodium_auth.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium_auth.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include #include +#include namespace Botan { @@ -47,7 +48,7 @@ const uint8_t key[]) { secure_vector computed(crypto_onetimeauth_poly1305_BYTES); crypto_onetimeauth_poly1305(computed.data(), in, in_len, key); - return crypto_verify_16(computed.data(), mac) ? 0 : -1; + return sodium_memcmp(computed.data(), mac, computed.size()); } int Sodium::crypto_auth_hmacsha512(uint8_t out[], const uint8_t in[], size_t in_len, const uint8_t key[]) { @@ -61,7 +62,7 @@ int Sodium::crypto_auth_hmacsha512_verify(const uint8_t mac[], const uint8_t in[], size_t in_len, const uint8_t key[]) { secure_vector computed(crypto_auth_hmacsha512_BYTES); crypto_auth_hmacsha512(computed.data(), in, in_len, key); - return crypto_verify_64(computed.data(), mac) ? 0 : -1; + return sodium_memcmp(computed.data(), mac, computed.size()); } int Sodium::crypto_auth_hmacsha512256(uint8_t out[], const uint8_t in[], size_t in_len, const uint8_t key[]) { @@ -82,7 +83,7 @@ const uint8_t key[]) { secure_vector computed(crypto_auth_hmacsha512256_BYTES); crypto_auth_hmacsha512256(computed.data(), in, in_len, key); - return crypto_verify_32(computed.data(), mac) ? 0 : -1; + return sodium_memcmp(computed.data(), mac, computed.size()); } int Sodium::crypto_auth_hmacsha256(uint8_t out[], const uint8_t in[], size_t in_len, const uint8_t key[]) { @@ -96,7 +97,7 @@ int Sodium::crypto_auth_hmacsha256_verify(const uint8_t mac[], const uint8_t in[], size_t in_len, const uint8_t key[]) { secure_vector computed(crypto_auth_hmacsha256_BYTES); crypto_auth_hmacsha256(computed.data(), in, in_len, key); - return crypto_verify_32(computed.data(), mac) ? 0 : -1; + return sodium_memcmp(computed.data(), mac, computed.size()); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/compat/sodium/sodium_chacha.cpp botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium_chacha.cpp --- botan3-3.7.1+dfsg/src/lib/compat/sodium/sodium_chacha.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium_chacha.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -25,7 +25,7 @@ int Sodium::crypto_stream_chacha20_xor_ic( uint8_t out[], const uint8_t in[], size_t in_len, const uint8_t nonce[], uint64_t ic, const uint8_t key[]) { - if((ic >> 6) != 0) { // otherwise multiply overflows + if((ic >> 58) != 0) { // otherwise multiply overflows return -1; } @@ -75,7 +75,7 @@ int Sodium::crypto_stream_xchacha20_xor_ic( uint8_t out[], const uint8_t in[], size_t in_len, const uint8_t nonce[], uint64_t ic, const uint8_t key[]) { - if((ic >> 6) != 0) { // otherwise multiply overflows + if((ic >> 58) != 0) { // otherwise multiply overflows return -1; } diff -Nru botan3-3.7.1+dfsg/src/lib/compat/sodium/sodium_salsa.cpp botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium_salsa.cpp --- botan3-3.7.1+dfsg/src/lib/compat/sodium/sodium_salsa.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium_salsa.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -64,7 +64,7 @@ int Sodium::crypto_stream_salsa20_xor_ic( uint8_t out[], const uint8_t in[], size_t in_len, const uint8_t nonce[], uint64_t ic, const uint8_t key[]) { - if((ic >> 6) != 0) { // otherwise multiply overflows + if((ic >> 58) != 0) { // otherwise multiply overflows return -1; } @@ -91,7 +91,7 @@ int Sodium::crypto_stream_xsalsa20_xor_ic( uint8_t out[], const uint8_t in[], size_t in_len, const uint8_t nonce[], uint64_t ic, const uint8_t key[]) { - if((ic >> 6) != 0) { // otherwise multiply overflows + if((ic >> 58) != 0) { // otherwise multiply overflows return -1; } diff -Nru botan3-3.7.1+dfsg/src/lib/compat/sodium/sodium_secretbox.cpp botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium_secretbox.cpp --- botan3-3.7.1+dfsg/src/lib/compat/sodium/sodium_secretbox.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium_secretbox.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,7 @@ #include #include +#include #include #include #include diff -Nru botan3-3.7.1+dfsg/src/lib/compat/sodium/sodium_utils.cpp botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium_utils.cpp --- botan3-3.7.1+dfsg/src/lib/compat/sodium/sodium_utils.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/compat/sodium/sodium_utils.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -13,10 +13,6 @@ #include #include -#if defined(BOTAN_HAS_OS_UTILS) - #include -#endif - namespace Botan { void Sodium::randombytes_buf(void* buf, size_t len) { @@ -29,7 +25,7 @@ } // Not completely uniform - uint64_t x; + uint64_t x = 0; randombytes_buf(&x, sizeof(x)); return x % upper_bound; } @@ -44,15 +40,15 @@ } int Sodium::crypto_verify_16(const uint8_t x[16], const uint8_t y[16]) { - return static_cast(CT::is_equal(x, y, 16).select(1, 0)); + return static_cast(CT::is_equal(x, y, 16).select(1, 0)) - 1; } int Sodium::crypto_verify_32(const uint8_t x[32], const uint8_t y[32]) { - return static_cast(CT::is_equal(x, y, 32).select(1, 0)); + return static_cast(CT::is_equal(x, y, 32).select(1, 0)) - 1; } int Sodium::crypto_verify_64(const uint8_t x[64], const uint8_t y[64]) { - return static_cast(CT::is_equal(x, y, 64).select(1, 0)); + return static_cast(CT::is_equal(x, y, 64).select(1, 0)) - 1; } void Sodium::sodium_memzero(void* ptr, size_t len) { @@ -93,15 +89,16 @@ uint8_t carry = 1; for(size_t i = 0; i != len; ++i) { b[i] += carry; - carry &= (b[i] == 0); + carry &= CT::Mask::is_zero(b[i]).if_set_return(1); } } void Sodium::sodium_add(uint8_t a[], const uint8_t b[], size_t len) { - uint8_t carry = 0; + uint16_t carry = 0; for(size_t i = 0; i != len; ++i) { - a[i] += b[i] + carry; - carry = (a[i] < b[i]); + carry += static_cast(a[i]) + b[i]; + a[i] = static_cast(carry); + carry >>= 8; } } @@ -112,7 +109,7 @@ return nullptr; } - // NOLINTNEXTLINE(*-no-malloc) + // NOLINTNEXTLINE(*-no-malloc,*-owning-memory,*-const-correctness) uint8_t* p = static_cast(std::calloc(size + sizeof(len), 1)); store_le(len, p); return p + 8; @@ -126,36 +123,25 @@ uint8_t* p = static_cast(ptr) - 8; const uint64_t len = load_le(p, 0); secure_scrub_memory(ptr, static_cast(len)); - // NOLINTNEXTLINE(*-no-malloc) + // NOLINTNEXTLINE(*-no-malloc,*-owning-memory) std::free(p); } void* Sodium::sodium_allocarray(size_t count, size_t size) { - const size_t bytes = count * size; - if(bytes < count || bytes < size) { + if(count > 0 && size > SIZE_MAX / count) { return nullptr; } - return sodium_malloc(bytes); + return sodium_malloc(count * size); } int Sodium::sodium_mprotect_noaccess(void* ptr) { -#if defined(BOTAN_HAS_OS_UTILS) - OS::page_prohibit_access(ptr); - return 0; -#else BOTAN_UNUSED(ptr); return -1; -#endif } int Sodium::sodium_mprotect_readwrite(void* ptr) { -#if defined(BOTAN_HAS_OS_UTILS) - OS::page_allow_access(ptr); - return 0; -#else BOTAN_UNUSED(ptr); return -1; -#endif } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/compression/bzip2/bzip2.cpp botan3-3.12.0+dfsg/src/lib/compression/bzip2/bzip2.cpp --- botan3-3.7.1+dfsg/src/lib/compression/bzip2/bzip2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/compression/bzip2/bzip2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -46,7 +46,7 @@ block_size = 9; } - int rc = BZ2_bzCompressInit(streamp(), static_cast(block_size), 0, 0); + const int rc = BZ2_bzCompressInit(streamp(), static_cast(block_size), 0, 0); if(rc != BZ_OK) { throw Compression_Error("BZ2_bzCompressInit", ErrorType::Bzip2Error, rc); @@ -61,7 +61,7 @@ ~Bzip2_Compression_Stream() override { BZ2_bzCompressEnd(streamp()); } bool run(uint32_t flags) override { - int rc = BZ2_bzCompress(streamp(), flags); + const int rc = BZ2_bzCompress(streamp(), flags); if(rc < 0) { throw Compression_Error("BZ2_bzCompress", ErrorType::Bzip2Error, rc); @@ -74,7 +74,7 @@ class Bzip2_Decompression_Stream final : public Bzip2_Stream { public: Bzip2_Decompression_Stream() { - int rc = BZ2_bzDecompressInit(streamp(), 0, 0); + const int rc = BZ2_bzDecompressInit(streamp(), 0, 0); if(rc != BZ_OK) { throw Compression_Error("BZ2_bzDecompressInit", ErrorType::Bzip2Error, rc); @@ -88,8 +88,8 @@ ~Bzip2_Decompression_Stream() override { BZ2_bzDecompressEnd(streamp()); } - bool run(uint32_t) override { - int rc = BZ2_bzDecompress(streamp()); + bool run(uint32_t /*flags*/) override { + const int rc = BZ2_bzDecompress(streamp()); if(rc != BZ_OK && rc != BZ_STREAM_END) { throw Compression_Error("BZ2_bzDecompress", ErrorType::Bzip2Error, rc); diff -Nru botan3-3.7.1+dfsg/src/lib/compression/compress_utils.cpp botan3-3.12.0+dfsg/src/lib/compression/compress_utils.cpp --- botan3-3.7.1+dfsg/src/lib/compression/compress_utils.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/compression/compress_utils.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -25,7 +25,7 @@ return nullptr; } - void* ptr = std::calloc(n, size); // NOLINT(*-no-malloc) + void* ptr = std::calloc(n, size); // NOLINT(*-no-malloc,*-owning-memory,*-const-correctness) /* * Return null rather than throwing here as we are being called by a @@ -35,7 +35,7 @@ * send upwards to the compression wrappers. */ - if(ptr) { + if(ptr != nullptr) { m_current_allocs[ptr] = n * size; } @@ -43,7 +43,7 @@ } void Compression_Alloc_Info::do_free(void* ptr) { - if(ptr) { + if(ptr != nullptr) { auto i = m_current_allocs.find(ptr); if(i == m_current_allocs.end()) { @@ -51,7 +51,7 @@ } secure_scrub_memory(ptr, i->second); - std::free(ptr); // NOLINT(*-no-malloc) + std::free(ptr); // NOLINT(*-no-malloc,*-owning-memory) m_current_allocs.erase(i); } } @@ -152,6 +152,9 @@ } // More data follows: try to process as a following stream + // Remove stream1's unused output space so stream2's output + // is placed immediately after stream1's data with no gap. + m_buffer.resize(m_buffer.size() - m_stream->avail_out()); const size_t read = (buf.size() - offset) - m_stream->avail_in(); start(); m_stream->next_in(buf.data() + offset + read, buf.size() - offset - read); @@ -172,14 +175,27 @@ } void Stream_Decompression::update(secure_vector& buf, size_t offset) { + if(!m_stream) { + if(buf.size() == offset) { + return; + } + // Previous stream ended cleanly; re-initialize for a concatenated stream + start(); + } process(buf, offset, m_stream->run_flag()); } void Stream_Decompression::finish(secure_vector& buf, size_t offset) { - if(buf.size() != offset || m_stream.get()) { - process(buf, offset, m_stream->finish_flag()); + if(!m_stream) { + if(buf.size() == offset) { + return; + } + // Previous stream ended cleanly; re-initialize for a concatenated stream + start(); } + process(buf, offset, m_stream->finish_flag()); + if(m_stream) { throw Invalid_State(fmt("{} finished but not at stream end", name())); } diff -Nru botan3-3.7.1+dfsg/src/lib/compression/compression.h botan3-3.12.0+dfsg/src/lib/compression/compression.h --- botan3-3.7.1+dfsg/src/lib/compression/compression.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/compression/compression.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,7 @@ #include #include +#include #include namespace Botan { @@ -17,7 +18,7 @@ /** * Interface for a compression algorithm. */ -class BOTAN_PUBLIC_API(2, 0) Compression_Algorithm { +class BOTAN_PUBLIC_API(2, 0) Compression_Algorithm /* NOLINT(*-special-member-functions) */ { public: /** * Create an instance based on a name, or return null if the @@ -89,7 +90,7 @@ /* * Interface for a decompression algorithm. */ -class BOTAN_PUBLIC_API(2, 0) Decompression_Algorithm { +class BOTAN_PUBLIC_API(2, 0) Decompression_Algorithm /* NOLINT(*-special-member-functions) */ { public: /** * Create an instance based on a name, or return null if the @@ -186,7 +187,7 @@ /** * Adapts a zlib style API */ -class Compression_Stream { +class Compression_Stream /* NOLINT(*-special-member-functions) */ { public: virtual ~Compression_Stream() = default; @@ -228,7 +229,7 @@ }; /** -* FIXME add doc +* Used to implement decompression using Compression_Stream */ class Stream_Decompression : public Decompression_Algorithm { public: diff -Nru botan3-3.7.1+dfsg/src/lib/compression/lzma/lzma.cpp botan3-3.12.0+dfsg/src/lib/compression/lzma/lzma.cpp --- botan3-3.7.1+dfsg/src/lib/compression/lzma/lzma.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/compression/lzma/lzma.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -35,7 +35,7 @@ LZMA_Stream& operator=(LZMA_Stream&& other) = delete; bool run(uint32_t flags) override { - lzma_ret rc = ::lzma_code(streamp(), static_cast(flags)); + const lzma_ret rc = ::lzma_code(streamp(), static_cast(flags)); if(rc != LZMA_OK && rc != LZMA_STREAM_END) { throw Compression_Error("lzma_code", ErrorType::LzmaError, rc); @@ -51,7 +51,7 @@ uint32_t finish_flag() const override { return LZMA_FINISH; } private: - ::lzma_allocator m_allocator; + ::lzma_allocator m_allocator{}; }; class LZMA_Compression_Stream final : public LZMA_Stream { @@ -63,7 +63,7 @@ level = 9; // clamp to maximum allowed value } - lzma_ret rc = ::lzma_easy_encoder(streamp(), static_cast(level), LZMA_CHECK_CRC64); + const lzma_ret rc = ::lzma_easy_encoder(streamp(), static_cast(level), LZMA_CHECK_CRC64); if(rc != LZMA_OK) { throw Compression_Error("lzam_easy_encoder", ErrorType::LzmaError, rc); @@ -74,7 +74,7 @@ class LZMA_Decompression_Stream final : public LZMA_Stream { public: LZMA_Decompression_Stream() { - lzma_ret rc = ::lzma_stream_decoder(streamp(), UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK); + const lzma_ret rc = ::lzma_stream_decoder(streamp(), UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK); if(rc != LZMA_OK) { throw Compression_Error("lzma_stream_decoder", ErrorType::LzmaError, rc); diff -Nru botan3-3.7.1+dfsg/src/lib/compression/zlib/zlib.cpp botan3-3.12.0+dfsg/src/lib/compression/zlib/zlib.cpp --- botan3-3.7.1+dfsg/src/lib/compression/zlib/zlib.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/compression/zlib/zlib.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -31,7 +31,7 @@ uint32_t finish_flag() const override { return Z_FINISH; } - int compute_window_bits(int wbits, int wbits_offset) const { + static int compute_window_bits(int wbits, int wbits_offset) { if(wbits_offset == -1) { return -wbits; } else { @@ -51,7 +51,7 @@ level = 6; } - int rc = ::deflateInit2(streamp(), static_cast(level), Z_DEFLATED, wbits, 8, Z_DEFAULT_STRATEGY); + const int rc = ::deflateInit2(streamp(), static_cast(level), Z_DEFLATED, wbits, 8, Z_DEFAULT_STRATEGY); if(rc != Z_OK) { throw Compression_Error("deflateInit2", ErrorType::ZlibError, rc); @@ -66,7 +66,7 @@ Zlib_Compression_Stream& operator=(Zlib_Compression_Stream&& other) = delete; bool run(uint32_t flags) override { - int rc = ::deflate(streamp(), flags); + const int rc = ::deflate(streamp(), flags); if(rc != Z_OK && rc != Z_STREAM_END && rc != Z_BUF_ERROR) { throw Compression_Error("zlib deflate", ErrorType::ZlibError, rc); @@ -78,8 +78,8 @@ class Zlib_Decompression_Stream : public Zlib_Stream { public: - Zlib_Decompression_Stream(int wbits, int wbits_offset = 0) { - int rc = ::inflateInit2(streamp(), compute_window_bits(wbits, wbits_offset)); + explicit Zlib_Decompression_Stream(int wbits, int wbits_offset = 0) { + const int rc = ::inflateInit2(streamp(), compute_window_bits(wbits, wbits_offset)); if(rc != Z_OK) { throw Compression_Error("inflateInit2", ErrorType::ZlibError, rc); @@ -94,7 +94,7 @@ Zlib_Decompression_Stream& operator=(Zlib_Decompression_Stream&& other) = delete; bool run(uint32_t flags) override { - int rc = ::inflate(streamp(), flags); + const int rc = ::inflate(streamp(), flags); if(rc != Z_OK && rc != Z_STREAM_END && rc != Z_BUF_ERROR) { throw Compression_Error("zlib inflate", ErrorType::ZlibError, rc); @@ -122,14 +122,14 @@ m_header.os = os_code; m_header.time = static_cast(hdr_time); - int rc = deflateSetHeader(streamp(), &m_header); + const int rc = deflateSetHeader(streamp(), &m_header); if(rc != Z_OK) { throw Compression_Error("deflateSetHeader", ErrorType::ZlibError, rc); } } private: - ::gz_header m_header; + ::gz_header m_header{}; }; class Gzip_Decompression_Stream final : public Zlib_Decompression_Stream { diff -Nru botan3-3.7.1+dfsg/src/lib/entropy/entropy_src.h botan3-3.12.0+dfsg/src/lib/entropy/entropy_src.h --- botan3-3.7.1+dfsg/src/lib/entropy/entropy_src.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/entropy/entropy_src.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,11 +8,11 @@ #ifndef BOTAN_ENTROPY_H_ #define BOTAN_ENTROPY_H_ -#include -#include +#include #include #include #include +#include #include namespace Botan { @@ -48,6 +48,7 @@ Entropy_Source(const Entropy_Source& other) = delete; Entropy_Source(Entropy_Source&& other) = delete; Entropy_Source& operator=(const Entropy_Source& other) = delete; + Entropy_Source& operator=(Entropy_Source&& other) = delete; virtual ~Entropy_Source() = default; }; @@ -70,10 +71,22 @@ * source blocks forever, this invocation will potentially also block. * * @returns the number of bits collected from the entropy sources + * + * TODO(Botan4) remove this variant, and the include above */ + BOTAN_DEPRECATED("Use version without a timeout argument") size_t poll(RandomNumberGenerator& rng, size_t bits, std::chrono::milliseconds timeout); /** + * Poll all sources to collect @p bits of entropy. Entropy collection is + * aborted as soon as the requested number of bits are obtained or the + * timeout runs out. + * + * @returns the number of bits collected from the entropy sources + */ + size_t poll(RandomNumberGenerator& rng, size_t bits); + + /** * Poll just a single named source. Ordinally only used for testing */ size_t poll_just(RandomNumberGenerator& rng, std::string_view src); @@ -84,6 +97,8 @@ Entropy_Sources(const Entropy_Sources& other) = delete; Entropy_Sources(Entropy_Sources&& other) = delete; Entropy_Sources& operator=(const Entropy_Sources& other) = delete; + Entropy_Sources& operator=(Entropy_Sources&& other) = delete; + ~Entropy_Sources() = default; private: std::vector> m_srcs; diff -Nru botan3-3.7.1+dfsg/src/lib/entropy/entropy_srcs.cpp botan3-3.12.0+dfsg/src/lib/entropy/entropy_srcs.cpp --- botan3-3.7.1+dfsg/src/lib/entropy/entropy_srcs.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/entropy/entropy_srcs.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,7 +7,9 @@ #include +#include #include +#include #if defined(BOTAN_HAS_SYSTEM_RNG) #include @@ -42,7 +44,7 @@ class System_RNG_EntropySource final : public Entropy_Source { public: size_t poll(RandomNumberGenerator& rng) override { - const size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS; + const size_t poll_bits = RandomNumberGenerator::DefaultPollBits; rng.reseed_from_rng(system_rng(), poll_bits); return poll_bits; } @@ -71,7 +73,7 @@ * * The reseeding conditions of the POWER and ARM processor RNGs are not known * but probably work in a somewhat similar manner. The exact amount requested - * may be tweaked if and when such conditions become publically known. + * may be tweaked if and when such conditions become publicly known. */ const size_t poll_bits = 65536; rng.reseed_from_rng(m_hwrng, poll_bits); @@ -92,9 +94,8 @@ class Jitter_RNG_EntropySource final : public Entropy_Source { public: size_t poll(RandomNumberGenerator& rng) override { - const size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS; - rng.reseed_from_rng(m_rng, poll_bits); - return poll_bits; + rng.reseed_from_rng(m_rng); + return RandomNumberGenerator::DefaultPollBits; } std::string name() const override { return m_rng.name(); } @@ -186,6 +187,20 @@ return bits_collected; } +size_t Entropy_Sources::poll(RandomNumberGenerator& rng, size_t poll_bits) { + size_t bits_collected = 0; + + for(auto& src : m_srcs) { + bits_collected += src->poll(rng); + + if(bits_collected >= poll_bits) { + break; + } + } + + return bits_collected; +} + size_t Entropy_Sources::poll_just(RandomNumberGenerator& rng, std::string_view the_src) { for(auto& src : m_srcs) { if(src->name() == the_src) { @@ -203,7 +218,7 @@ } Entropy_Sources& Entropy_Sources::global_sources() { - static Entropy_Sources global_entropy_sources(BOTAN_ENTROPY_DEFAULT_SOURCES); + static Entropy_Sources global_entropy_sources({"rdseed", "hwrng", "getentropy", "system_rng", "system_stats"}); return global_entropy_sources; } diff -Nru botan3-3.7.1+dfsg/src/lib/entropy/getentropy/getentropy.cpp botan3-3.12.0+dfsg/src/lib/entropy/getentropy/getentropy.cpp --- botan3-3.7.1+dfsg/src/lib/entropy/getentropy/getentropy.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/entropy/getentropy/getentropy.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,6 +6,8 @@ */ #include + +#include #include // macOS and Android include it in sys/random.h instead diff -Nru botan3-3.7.1+dfsg/src/lib/entropy/getentropy/info.txt botan3-3.12.0+dfsg/src/lib/entropy/getentropy/info.txt --- botan3-3.7.1+dfsg/src/lib/entropy/getentropy/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/entropy/getentropy/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + ENTROPY_SRC_GETENTROPY -> 20170327 - + name -> "getentropy" diff -Nru botan3-3.7.1+dfsg/src/lib/entropy/rdseed/info.txt botan3-3.12.0+dfsg/src/lib/entropy/rdseed/info.txt --- botan3-3.7.1+dfsg/src/lib/entropy/rdseed/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/entropy/rdseed/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + ENTROPY_SRC_RDSEED -> 20151218 - + name -> "RDSEED" @@ -15,3 +15,7 @@ rdseed.h + + +cpuid + diff -Nru botan3-3.7.1+dfsg/src/lib/entropy/rdseed/rdseed.cpp botan3-3.12.0+dfsg/src/lib/entropy/rdseed/rdseed.cpp --- botan3-3.7.1+dfsg/src/lib/entropy/rdseed/rdseed.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/entropy/rdseed/rdseed.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,15 +9,19 @@ #include #include +#include #include +#include -#include +#if !defined(BOTAN_USE_GCC_INLINE_ASM) + #include +#endif namespace Botan { namespace { -BOTAN_FUNC_ISA("rdseed") bool read_rdseed(secure_vector& seed) { +BOTAN_FUNC_ISA("rdseed,sse2") bool read_rdseed(secure_vector& seed) { /* * RDSEED is not guaranteed to generate an output within any specific number * of attempts. However in testing on a Skylake system, with all hyperthreads @@ -33,11 +37,11 @@ const size_t RDSEED_RETRIES = 1024; for(size_t i = 0; i != RDSEED_RETRIES; ++i) { - uint32_t r = 0; - int cf = 0; + uint32_t r = 0; // NOLINT(*-const-correctness) clang-tidy doesn't understand inline asm + int cf = 0; // NOLINT(*-const-correctness) clang-tidy doesn't understand inline asm #if defined(BOTAN_USE_GCC_INLINE_ASM) - asm("rdseed %0; adcl $0,%1" : "=r"(r), "=r"(cf) : "0"(r), "1"(cf) : "cc"); + asm("rdseed %0; adcl $0,%1" : "=r"(r), "=r"(cf) : "0"(r), "1"(cf) : "cc"); // NOLINT(*-no-assembler) #else cf = _rdseed32_step(&r); #endif @@ -48,7 +52,11 @@ } // Intel suggests pausing if RDSEED fails. - _mm_pause(); +#if defined(BOTAN_USE_GCC_INLINE_ASM) + asm volatile("pause"); // NOLINT(*-no-assembler) +#else + _mm_pause(); // NOLINT(portability-simd-intrinsics) +#endif } return false; // failed to produce an output after many attempts @@ -60,7 +68,7 @@ const size_t RDSEED_BYTES = 1024; static_assert(RDSEED_BYTES % 4 == 0, "Bad RDSEED configuration"); - if(CPUID::has_rdseed()) { + if(CPUID::has(CPUID::Feature::RDSEED)) { secure_vector seed; seed.reserve(RDSEED_BYTES / 4); diff -Nru botan3-3.7.1+dfsg/src/lib/entropy/win32_stats/es_win32.cpp botan3-3.12.0+dfsg/src/lib/entropy/win32_stats/es_win32.cpp --- botan3-3.7.1+dfsg/src/lib/entropy/win32_stats/es_win32.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/entropy/win32_stats/es_win32.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,6 +6,8 @@ #include +#include + #define NOMINMAX 1 #define _WINSOCKAPI_ // stop windows.h including winsock.h #include diff -Nru botan3-3.7.1+dfsg/src/lib/entropy/win32_stats/info.txt botan3-3.12.0+dfsg/src/lib/entropy/win32_stats/info.txt --- botan3-3.7.1+dfsg/src/lib/entropy/win32_stats/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/entropy/win32_stats/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + ENTROPY_SRC_WIN32 -> 20200209 - + name -> "Win32 Statistics" diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi.cpp botan3-3.12.0+dfsg/src/lib/ffi/ffi.cpp --- botan3-3.7.1+dfsg/src/lib/ffi/ffi.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -13,7 +13,6 @@ #include #include #include -#include #if defined(BOTAN_HAS_OS_UTILS) #include @@ -26,48 +25,6 @@ // NOLINTNEXTLINE(*-avoid-non-const-global-variables) thread_local std::string g_last_exception_what; -} // namespace - -int ffi_error_exception_thrown(const char* func_name, const char* exn, int rc) { - g_last_exception_what.assign(exn); - -#if defined(BOTAN_HAS_OS_UTILS) - std::string val; - if(Botan::OS::read_env_variable(val, "BOTAN_FFI_PRINT_EXCEPTIONS") == true && !val.empty()) { - static_cast(std::fprintf(stderr, "in %s exception '%s' returning %d\n", func_name, exn, rc)); - } -#endif - - return rc; -} - -int botan_view_str_bounce_fn(botan_view_ctx vctx, const char* str, size_t len) { - return botan_view_bin_bounce_fn(vctx, reinterpret_cast(str), len); -} - -int botan_view_bin_bounce_fn(botan_view_ctx vctx, const uint8_t* buf, size_t len) { - if(vctx == nullptr || buf == nullptr) { - return BOTAN_FFI_ERROR_NULL_POINTER; - } - - botan_view_bounce_struct* ctx = static_cast(vctx); - - const size_t avail = *ctx->out_len; - *ctx->out_len = len; - - if(avail < len || ctx->out_ptr == nullptr) { - if(ctx->out_ptr) { - Botan::clear_mem(ctx->out_ptr, avail); - } - return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE; - } else { - Botan::copy_mem(ctx->out_ptr, buf, len); - return BOTAN_FFI_SUCCESS; - } -} - -namespace { - int ffi_map_error_type(Botan::ErrorType err) { switch(err) { case Botan::ErrorType::Unknown: @@ -125,21 +82,50 @@ } // namespace -int ffi_guard_thunk(const char* func_name, const std::function& thunk) { +void ffi_clear_last_exception() { g_last_exception_what.clear(); +} + +int ffi_error_exception_thrown(const char* func_name, const char* exn, int rc) { + g_last_exception_what.assign(exn); - try { - return thunk(); - } catch(std::bad_alloc&) { - return ffi_error_exception_thrown(func_name, "bad_alloc", BOTAN_FFI_ERROR_OUT_OF_MEMORY); - } catch(Botan_FFI::FFI_Error& e) { - return ffi_error_exception_thrown(func_name, e.what(), e.error_code()); - } catch(Botan::Exception& e) { - return ffi_error_exception_thrown(func_name, e.what(), ffi_map_error_type(e.error_type())); - } catch(std::exception& e) { - return ffi_error_exception_thrown(func_name, e.what()); - } catch(...) { - return ffi_error_exception_thrown(func_name, "unknown exception"); +#if defined(BOTAN_HAS_OS_UTILS) + std::string val; + if(Botan::OS::read_env_variable(val, "BOTAN_FFI_PRINT_EXCEPTIONS") && !val.empty()) { + // NOLINTNEXTLINE(*-vararg) + static_cast(std::fprintf(stderr, "in %s exception '%s' returning %d\n", func_name, exn, rc)); + } +#endif + + return rc; +} + +int ffi_error_exception_thrown(const char* func_name, const char* exn, Botan::ErrorType err) { + return ffi_error_exception_thrown(func_name, exn, ffi_map_error_type(err)); +} + +int botan_view_str_bounce_fn(botan_view_ctx vctx, const char* str, size_t len) { + return botan_view_bin_bounce_fn(vctx, reinterpret_cast(str), len); +} + +int botan_view_bin_bounce_fn(botan_view_ctx vctx, const uint8_t* buf, size_t len) { + if(vctx == nullptr || buf == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + const botan_view_bounce_struct* ctx = static_cast(vctx); + + const size_t avail = *ctx->out_len; + *ctx->out_len = len; + + if(avail < len || ctx->out_ptr == nullptr) { + if(ctx->out_ptr != nullptr) { + Botan::clear_mem(ctx->out_ptr, avail); + } + return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE; + } else { + Botan::copy_mem(ctx->out_ptr, buf, len); + return BOTAN_FFI_SUCCESS; } } @@ -167,6 +153,9 @@ case BOTAN_FFI_ERROR_BAD_MAC: return "Invalid authentication code"; + case BOTAN_FFI_ERROR_NO_VALUE: + return "No value available"; + case BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE: return "Insufficient buffer space"; @@ -203,6 +192,9 @@ case BOTAN_FFI_ERROR_INVALID_OBJECT_STATE: return "Invalid object state"; + case BOTAN_FFI_ERROR_OUT_OF_RANGE: + return "Index out of range"; + case BOTAN_FFI_ERROR_NOT_IMPLEMENTED: return "Not implemented"; @@ -216,8 +208,6 @@ return "HTTP error"; case BOTAN_FFI_ERROR_UNKNOWN_ERROR: - return "Unknown error"; - default: return "Unknown error"; } @@ -231,6 +221,26 @@ } int botan_ffi_supports_api(uint32_t api_version) { + // This is the API introduced in 3.12 + if(api_version == 20260506) { + return BOTAN_FFI_SUCCESS; + } + + // This is the API introduced in 3.11 + if(api_version == 20260303) { + return BOTAN_FFI_SUCCESS; + } + + // This is the API introduced in 3.10 + if(api_version == 20250829) { + return BOTAN_FFI_SUCCESS; + } + + // This is the API introduced in 3.8 + if(api_version == 20250506) { + return BOTAN_FFI_SUCCESS; + } + // This is the API introduced in 3.4 if(api_version == 20240408) { return BOTAN_FFI_SUCCESS; @@ -306,6 +316,9 @@ } int botan_constant_time_compare(const uint8_t* x, const uint8_t* y, size_t len) { + if(len > 0 && any_null_pointers(x, y)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } auto same = Botan::CT::is_equal(x, y, len); // Return 0 if same or -1 otherwise return static_cast(same.select(1, 0)) - 1; @@ -316,11 +329,17 @@ } int botan_scrub_mem(void* mem, size_t bytes) { + if(bytes > 0 && mem == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } Botan::secure_scrub_memory(mem, bytes); return BOTAN_FFI_SUCCESS; } int botan_hex_encode(const uint8_t* in, size_t len, char* out, uint32_t flags) { + if(len > 0 && (in == nullptr || out == nullptr)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return ffi_guard_thunk(__func__, [=]() -> int { const bool uppercase = (flags & BOTAN_FFI_HEX_LOWER_CASE) == 0; Botan::hex_encode(out, in, len, uppercase); @@ -329,6 +348,9 @@ } int botan_hex_decode(const char* hex_str, size_t in_len, uint8_t* out, size_t* out_len) { + if(any_null_pointers(hex_str, out_len)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return ffi_guard_thunk(__func__, [=]() -> int { const std::vector bin = Botan::hex_decode(hex_str, in_len); return Botan_FFI::write_vec_output(out, out_len, bin); @@ -336,6 +358,9 @@ } int botan_base64_encode(const uint8_t* in, size_t len, char* out, size_t* out_len) { + if(len > 0 && in == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return ffi_guard_thunk(__func__, [=]() -> int { const std::string base64 = Botan::base64_encode(in, len); return Botan_FFI::write_str_output(out, out_len, base64); @@ -343,6 +368,10 @@ } int botan_base64_decode(const char* base64_str, size_t in_len, uint8_t* out, size_t* out_len) { + if(any_null_pointers(out, out_len, base64_str)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + return ffi_guard_thunk(__func__, [=]() -> int { if(*out_len < Botan::base64_decode_max_output(in_len)) { *out_len = Botan::base64_decode_max_output(in_len); diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi.h botan3-3.12.0+dfsg/src/lib/ffi/ffi.h --- botan3-3.7.1+dfsg/src/lib/ffi/ffi.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi.h 2026-05-07 01:38:28.000000000 +0000 @@ -2,6 +2,7 @@ * FFI (C89 API) * (C) 2015,2017 Jack Lloyd * (C) 2021 René Fischer +* (C) 2024,2025,2026 Amos Treiber, René Meusel, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -15,7 +16,7 @@ /* This header exports some of botan's functionality via a C89 interface. This API -is uesd by the Python, OCaml, Rust, Ruby, and Haskell bindings via those languages +is used by the Python, OCaml, Rust, Ruby, and Haskell bindings via those languages respective ctypes/FFI libraries. The API is intended to be as easy as possible to call from other @@ -32,33 +33,34 @@ - Use simple types: size_t for lengths, const char* NULL terminated strings, uint8_t for binary. -- No ownership of memory transfers across the API boundary. The API will - consume data from const pointers, and will produce output by writing to - buffers provided by (and allocated by) the caller. - -- If exporting a value (a string or a blob) the function takes a pointer to the - output array and a read/write pointer to the length. If the length is insufficient, an - error is returned. So passing nullptr/0 allows querying the final value. - - Typically there is also a function which allows querying the expected output - length of a function, for example `botan_hash_output_length` allows knowing in - advance the expected size for `botan_hash_final`. Some of these are exact, - while others such as `botan_pk_op_decrypt_output_length` only provide an upper - bound. - - The big exception to this currently is the various functions which serialize - public and private keys, where there are currently no function that can - estimate the serialized size. Here view functions are used; see the handbook - for further details. - - TODO: - - Doxygen comments for all functions/params - - TLS +- No ownership of memory transfers across the API boundary. The API will consume + data from const pointers with specified lengths. Outputs are either placed into + buffers provided by (and allocated by) the caller, or are returned via a + callback (what the FFI layer calls "view" functions). + + When writing to an application-provided buffer, the function takes a pointer + to the output array and a read/write pointer to the length. The length field + is always set to the actual amount of data that would have been written. If + the input buffer's size was insufficient an error is returned. + + In many situations the length of the output can be known in advance without + difficulty, in which case there will be a function which allows querying the + expected output length. For example `botan_hash_output_length` allows knowing + in advance the expected size for `botan_hash_final`. Some of these are exact, + while others such as `botan_pk_op_decrypt_output_length` can only provide an + upper bound for various technical reasons. + + In some cases knowing the exact size is difficult or impossible. In these + situations view functions are used; see the handbook for further details. + + TODO: Doxygen comments for all parameters */ #include #include +/* NOLINTBEGIN(*-macro-usage,*-misplaced-const) */ + /** * The compile time API version. This matches the value of * botan_ffi_api_version. This can be used for compile-time checking if a @@ -68,7 +70,7 @@ * that declaration is not visible here since this header is intentionally * free-standing, depending only on a few C standard library headers. */ -#define BOTAN_FFI_API_VERSION 20240408 +#define BOTAN_FFI_API_VERSION 20260506 /** * BOTAN_FFI_EXPORT indicates public FFI functions. @@ -90,7 +92,7 @@ #endif #endif -#if !defined(BOTAN_NO_DEPRECATED_WARNINGS) +#if !defined(BOTAN_NO_DEPRECATED_WARNINGS) && !defined(BOTAN_AMALGAMATION_H_) && !defined(BOTAN_IS_BEING_BUILT) #if defined(__has_attribute) #if __has_attribute(deprecated) #define BOTAN_FFI_DEPRECATED(msg) __attribute__((deprecated(msg))) @@ -110,12 +112,14 @@ * If you add a new value here be sure to also add it in * botan_error_description */ -enum BOTAN_FFI_ERROR { +enum BOTAN_FFI_ERROR /* NOLINT(*-enum-size,*-use-enum-class) */ { BOTAN_FFI_SUCCESS = 0, + BOTAN_FFI_INVALID_VERIFIER = 1, BOTAN_FFI_ERROR_INVALID_INPUT = -1, BOTAN_FFI_ERROR_BAD_MAC = -2, + BOTAN_FFI_ERROR_NO_VALUE = -3, BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE = -10, BOTAN_FFI_ERROR_STRING_CONVERSION_ERROR = -11, @@ -131,6 +135,7 @@ BOTAN_FFI_ERROR_KEY_NOT_SET = -33, BOTAN_FFI_ERROR_INVALID_KEY_LENGTH = -34, BOTAN_FFI_ERROR_INVALID_OBJECT_STATE = -35, + BOTAN_FFI_ERROR_OUT_OF_RANGE = -36, BOTAN_FFI_ERROR_NOT_IMPLEMENTED = -40, BOTAN_FFI_ERROR_INVALID_OBJECT = -50, @@ -143,6 +148,9 @@ BOTAN_FFI_ERROR_UNKNOWN_ERROR = -100, }; +/** +* The application provided context for a view function +*/ typedef void* botan_view_ctx; /** @@ -212,8 +220,9 @@ BOTAN_FFI_EXPORT(2, 0) uint32_t botan_version_patch(void); /** -* Return the date this version was released as -* an integer, or 0 if an unreleased version +* Return the date this version was released as an integer. +* +* Returns 0 if the library was not built from an official release */ BOTAN_FFI_EXPORT(2, 0) uint32_t botan_version_datestamp(void); @@ -234,6 +243,9 @@ */ BOTAN_FFI_EXPORT(2, 2) int botan_scrub_mem(void* mem, size_t bytes); +/** +* Flag that can be provided to botan_hex_encode to request lower case hex +*/ #define BOTAN_FFI_HEX_LOWER_CASE 1 /** @@ -258,6 +270,13 @@ /** * Perform base64 encoding +* +* @param x the input data +* @param len the length of x +* @param out the output buffer +* @param out_len the size of the output buffer on input, set to the number of bytes written +* @return 0 on success, a negative value on failure + */ BOTAN_FFI_EXPORT(2, 3) int botan_base64_encode(const uint8_t* x, size_t len, char* out, size_t* out_len); @@ -304,6 +323,7 @@ /** * Get random bytes from a random number generator +* * @param rng rng object * @param out output buffer of size out_len * @param out_len number of requested bytes @@ -313,6 +333,7 @@ /** * Get random bytes from system random number generator +* * @param out output buffer of size out_len * @param out_len number of requested bytes * @return 0 on success, negative on failure @@ -350,6 +371,35 @@ BOTAN_FFI_EXPORT(2, 8) int botan_rng_add_entropy(botan_rng_t rng, const uint8_t* entropy, size_t entropy_len); /** +* Create and seed a DRBG +* +* @param rng_out the new DRBG object +* @param drbg_name the name of the DRBG (e.g. "HMAC_DRBG(SHA-256)") +* @param seed the seed material (entropy || nonce || personalization_string) +* @param seed_len length of seed in bytes +* @return 0 on success, negative on failure +*/ +BOTAN_FFI_EXPORT(3, 12) +int botan_rng_init_drbg(botan_rng_t* rng_out, const char* drbg_name, const uint8_t* seed, size_t seed_len); + +/** +* Generate random bytes from an RNG with additional input. +* +* For a DRBG, the additional input is mixed in before generating. +* Many other RNG types (eg RDRAND or system RNG) will ignore the input. +* +* @param rng the RNG object +* @param out output buffer +* @param out_len number of bytes to generate +* @param addl_input additional input to mix in (may be NULL if addl_len is 0) +* @param addl_len length of additional input +* @return 0 on success, negative on failure +*/ +BOTAN_FFI_EXPORT(3, 12) +int botan_rng_generate_with_input( + botan_rng_t rng, uint8_t* out, size_t out_len, const uint8_t* addl_input, size_t addl_len); + +/** * Frees all resources of the random number generator object * @param rng rng object * @return 0 if success, error if invalid object handle @@ -357,7 +407,85 @@ BOTAN_FFI_EXPORT(2, 0) int botan_rng_destroy(botan_rng_t rng); /* -* Hash type +* Opaque type of an eXtendable Output Function (XOF) +*/ +typedef struct botan_xof_struct* botan_xof_t; + +/** +* Initialize an eXtendable Output Function +* @param xof XOF object +* @param xof_name name of the XOF, e.g., "SHAKE-128" +* @param flags should be 0 in current API revision, all other uses are reserved +* and return BOTAN_FFI_ERROR_BAD_FLAG +*/ +BOTAN_FFI_EXPORT(3, 11) int botan_xof_init(botan_xof_t* xof, const char* xof_name, uint32_t flags); + +/** +* Copy the state of an eXtendable Output Function +* @param dest destination XOF object +* @param source source XOF object +* @return 0 on success, a negative value on failure +*/ +BOTAN_FFI_EXPORT(3, 11) int botan_xof_copy_state(botan_xof_t* dest, botan_xof_t source); + +/** +* Writes the block size of the eXtendable Output Function to *block_size +* @param xof XOF object +* @param block_size variable to hold the XOF's block size +* @return 0 on success, a negative value on failure +*/ +BOTAN_FFI_EXPORT(3, 11) int botan_xof_block_size(botan_xof_t xof, size_t* block_size); + +/** +* Get the name of this eXtendable Output Function +* @param xof the object to read +* @param name output buffer +* @param name_len on input, the length of buffer, on success the number of bytes written +*/ +BOTAN_FFI_EXPORT(3, 11) int botan_xof_name(botan_xof_t xof, char* name, size_t* name_len); + +/** +* Get the input/output state of this eXtendable Output Function +* Typically, XOFs don't accept input as soon as the first output bytes were requested. +* @param xof the object to read +* @returns 1 iff the XOF is still accepting input bytes +*/ +BOTAN_FFI_EXPORT(3, 11) int botan_xof_accepts_input(botan_xof_t xof); + +/** +* Reinitializes the state of the eXtendable Output Function. +* @param xof XOF object +* @return 0 on success, a negative value on failure +*/ +BOTAN_FFI_EXPORT(3, 11) int botan_xof_clear(botan_xof_t xof); + +/** +* Send more input to the eXtendable Output Function +* @param xof XOF object +* @param in input buffer +* @param in_len number of bytes to read from the input buffer +* @return 0 on success, a negative value on failure +*/ +BOTAN_FFI_EXPORT(3, 11) int botan_xof_update(botan_xof_t xof, const uint8_t* in, size_t in_len); + +/** +* Generate output bytes from the eXtendable Output Function +* @param xof XOF object +* @param out output buffer +* @param out_len number of bytes to write into the output buffer +* @return 0 on success, a negative value on failure +*/ +BOTAN_FFI_EXPORT(3, 11) int botan_xof_output(botan_xof_t xof, uint8_t* out, size_t out_len); + +/** +* Frees all resources of the eXtendable Output Function object +* @param xof xof object +* @return 0 if success, error if invalid object handle +*/ +BOTAN_FFI_EXPORT(3, 11) int botan_xof_destroy(botan_xof_t xof); + +/* +* Opaque type of a hash function */ typedef struct botan_hash_struct* botan_hash_t; @@ -437,7 +565,7 @@ BOTAN_FFI_EXPORT(2, 8) int botan_hash_name(botan_hash_t hash, char* name, size_t* name_len); /* -* Message Authentication type +* Opaque type of a message authentication code */ typedef struct botan_mac_struct* botan_mac_t; @@ -533,7 +661,7 @@ BOTAN_FFI_EXPORT(2, 0) int botan_mac_destroy(botan_mac_t mac); /* -* Cipher modes +* Opaque type of a cipher mode */ typedef struct botan_cipher_struct* botan_cipher_t; @@ -599,13 +727,13 @@ * Get information about the key lengths. Prefer botan_cipher_get_keyspec */ BOTAN_FFI_EXPORT(2, 0) -int botan_cipher_query_keylen(botan_cipher_t, size_t* out_minimum_keylength, size_t* out_maximum_keylength); +int botan_cipher_query_keylen(botan_cipher_t cipher, size_t* out_minimum_keylength, size_t* out_maximum_keylength); /** * Get information about the supported key lengths. */ BOTAN_FFI_EXPORT(2, 8) -int botan_cipher_get_keyspec(botan_cipher_t, size_t* min_keylen, size_t* max_keylen, size_t* mod_keylen); +int botan_cipher_get_keyspec(botan_cipher_t cipher, size_t* min_keylen, size_t* max_keylen, size_t* mod_keylen); /** * Set the key for this cipher object @@ -751,16 +879,17 @@ * @param salt_len length of salt in bytes * @return 0 on success, a negative value on failure */ -int BOTAN_FFI_EXPORT(2, 8) botan_pwdhash(const char* algo, - size_t param1, - size_t param2, - size_t param3, - uint8_t out[], - size_t out_len, - const char* passphrase, - size_t passphrase_len, - const uint8_t salt[], - size_t salt_len); +BOTAN_FFI_EXPORT(2, 8) +int botan_pwdhash(const char* algo, + size_t param1, + size_t param2, + size_t param3, + uint8_t out[], + size_t out_len, + const char* passphrase, + size_t passphrase_len, + const uint8_t salt[], + size_t salt_len); /* * Derive a key from a passphrase @@ -778,17 +907,18 @@ * @param salt_len length of salt in bytes * @return 0 on success, a negative value on failure */ -int BOTAN_FFI_EXPORT(2, 8) botan_pwdhash_timed(const char* algo, - uint32_t msec, - size_t* param1, - size_t* param2, - size_t* param3, - uint8_t out[], - size_t out_len, - const char* passphrase, - size_t passphrase_len, - const uint8_t salt[], - size_t salt_len); +BOTAN_FFI_EXPORT(2, 8) +int botan_pwdhash_timed(const char* algo, + uint32_t msec, + size_t* param1, + size_t* param2, + size_t* param3, + uint8_t out[], + size_t out_len, + const char* passphrase, + size_t passphrase_len, + const uint8_t salt[], + size_t salt_len); /** * Derive a key using scrypt @@ -913,14 +1043,26 @@ BOTAN_FFI_EXPORT(2, 1) int botan_mp_destroy(botan_mp_t mp); /** -* Convert the MPI to a hex string. Writes botan_mp_num_bytes(mp)*2 + 1 bytes +* Convert the MPI to a hex string. Writes up to botan_mp_num_bytes(mp)*2 + 5 bytes +* +* Prefer botan_mp_view_hex */ BOTAN_FFI_EXPORT(2, 1) int botan_mp_to_hex(botan_mp_t mp, char* out); /** -* Convert the MPI to a string. Currently base == 10 and base == 16 are supported. +* View the hex string encoding of the MPI. +*/ +BOTAN_FFI_EXPORT(3, 10) int botan_mp_view_hex(botan_mp_t mp, botan_view_ctx ctx, botan_view_str_fn view); + +/** +* Convert the MPI to a string. Currently radix == 10 and radix == 16 are supported. +*/ +BOTAN_FFI_EXPORT(2, 1) int botan_mp_to_str(botan_mp_t mp, uint8_t radix, char* out, size_t* out_len); + +/** +* View the MPI as a radix-N integer. Currently only radix 10 and radix 16 are supported */ -BOTAN_FFI_EXPORT(2, 1) int botan_mp_to_str(botan_mp_t mp, uint8_t base, char* out, size_t* out_len); +BOTAN_FFI_EXPORT(3, 10) int botan_mp_view_str(botan_mp_t mp, uint8_t radix, botan_view_ctx ctx, botan_view_str_fn view); /** * Set the MPI to zero @@ -960,10 +1102,19 @@ /* * Convert the MPI to a big-endian binary string. Writes botan_mp_num_bytes to vec +* +* Note that the sign of the integer is ignored here; only the absolute value is copied */ BOTAN_FFI_EXPORT(2, 1) int botan_mp_to_bin(botan_mp_t mp, uint8_t vec[]); /* +* View the big-endian binary string encoding of this integer +* +* Note that the sign of the integer is ignored here; only the absolute value is viewed +*/ +BOTAN_FFI_EXPORT(3, 10) int botan_mp_view_bin(botan_mp_t mp, botan_view_ctx ctx, botan_view_bin_fn view); + +/* * Set an MP to the big-endian binary value */ BOTAN_FFI_EXPORT(2, 1) int botan_mp_from_bin(botan_mp_t mp, const uint8_t vec[], size_t vec_len); @@ -1076,8 +1227,10 @@ * @param flags should be 0 in current API revision, all other uses are reserved * and return BOTAN_FFI_ERROR_BAD_FLAG * @return 0 on success, a negative value on failure - +* * Output is formatted bcrypt $2a$... +* +* TOD(Botan4) this should use char for the type of `out` */ BOTAN_FFI_EXPORT(2, 0) int botan_bcrypt_generate( @@ -1094,6 +1247,318 @@ BOTAN_FFI_EXPORT(2, 0) int botan_bcrypt_is_valid(const char* pass, const char* hash); /* +* OIDs +*/ + +typedef struct botan_asn1_oid_struct* botan_asn1_oid_t; + +/** +* @returns negative number on error, or zero on success +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_oid_destroy(botan_asn1_oid_t oid); + +/** +* Create an OID from a string, either dot notation (e.g. '1.2.3.4') or a registered name (e.g. 'RSA') +* @param oid handle to the resulting OID +* @param oid_str the name of the OID to create +* @returns negative number on error, or zero on success +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_oid_from_string(botan_asn1_oid_t* oid, const char* oid_str); + +/** +* Registers an OID so that it may later be retrieved by name +* @returns negative number on error, or zero on success +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_oid_register(botan_asn1_oid_t oid, const char* name); + +/** +* View an OID in dot notation +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_oid_view_string(botan_asn1_oid_t oid, botan_view_ctx ctx, botan_view_str_fn view); + +/** +* View an OIDs registered name if it exists, else its dot notation +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_oid_view_name(botan_asn1_oid_t oid, botan_view_ctx ctx, botan_view_str_fn view); + +/** +* @returns 0 if a != b +* @returns 1 if a == b +* @returns negative number on error +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_oid_equal(botan_asn1_oid_t a, botan_asn1_oid_t b); + +/** +* Sets @param result to comparison result: +* -1 if a < b, 0 if a == b, 1 if a > b +* @returns negative number on error or zero on success +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_oid_cmp(int* result, botan_asn1_oid_t a, botan_asn1_oid_t b); + +/* +* EC Groups +*/ + +typedef struct botan_ec_group_struct* botan_ec_group_t; + +/** +* @returns negative number on error, or zero on success +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_ec_group_destroy(botan_ec_group_t ec_group); + +/** +* Checks if in this build configuration it is possible to register an application specific elliptic curve and sets +* @param out to 1 if so, 0 otherwise +* @returns 0 on success, a negative value on failure +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_ec_group_supports_application_specific_group(int* out); + +/** +* Checks if in this build configuration botan_ec_group_from_name(group_ptr, name) will succeed and sets +* @param out to 1 if so, 0 otherwise. +* @returns negative number on error, or zero on success +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_ec_group_supports_named_group(const char* name, int* out); + +/** +* Create a new EC Group from parameters +* @warning use only elliptic curve parameters that you trust +* +* @param ec_group the new object will be placed here +* @param p the elliptic curve prime (at most 521 bits) +* @param a the elliptic curve a param +* @param b the elliptic curve b param +* @param base_x the x coordinate of the group generator +* @param base_y the y coordinate of the group generator +* @param order the order of the group +* @returns negative number on error, or zero on success +*/ +BOTAN_FFI_EXPORT(3, 8) +int botan_ec_group_from_params(botan_ec_group_t* ec_group, + botan_asn1_oid_t oid, + botan_mp_t p, + botan_mp_t a, + botan_mp_t b, + botan_mp_t base_x, + botan_mp_t base_y, + botan_mp_t order); + +/** +* Decode a BER encoded ECC domain parameter set +* @param ec_group the new object will be placed here +* @param ber encoding +* @param ber_len size of the encoding in bytes +* @returns negative number on error, or zero on success +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_ec_group_from_ber(botan_ec_group_t* ec_group, const uint8_t* ber, size_t ber_len); + +/** +* Initialize an EC Group from the PEM/ASN.1 encoding +* @param ec_group the new object will be placed here +* @param pem encoding +* @returns negative number on error, or zero on success +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_ec_group_from_pem(botan_ec_group_t* ec_group, const char* pem); + +/** +* Initialize an EC Group from a group named by an object identifier +* @param ec_group the new object will be placed here +* @param oid a known OID +* @returns negative number on error, or zero on success +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_ec_group_from_oid(botan_ec_group_t* ec_group, botan_asn1_oid_t oid); + +/** +* Initialize an EC Group from a common group name (eg "secp256r1") +* @param ec_group the new object will be placed here +* @param name a known group name +* @returns negative number on error, or zero on success +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_ec_group_from_name(botan_ec_group_t* ec_group, const char* name); + +/** +* Unregister a previously registered group. +* @param oid the oid associated with the group to unregister +* @returns 1 if the group was found and unregistered, else 0 +* +* Using this is discouraged for normal use. This is only useful or necessary if +* you are registering a very large number of distinct groups, and need to worry about memory constraints. +*/ +BOTAN_FFI_EXPORT(3, 11) int botan_ec_group_unregister(botan_asn1_oid_t oid); + +/** +* View an EC Group in DER encoding +*/ +BOTAN_FFI_EXPORT(3, 8) +int botan_ec_group_view_der(botan_ec_group_t ec_group, botan_view_ctx ctx, botan_view_bin_fn view); + +/** +* View an EC Group in PEM encoding +*/ +BOTAN_FFI_EXPORT(3, 8) +int botan_ec_group_view_pem(botan_ec_group_t ec_group, botan_view_ctx ctx, botan_view_str_fn view); + +/** +* Get the curve OID of an EC Group +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_ec_group_get_curve_oid(botan_asn1_oid_t* oid, botan_ec_group_t ec_group); + +/** +* Get the prime modulus of the field +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_ec_group_get_p(botan_mp_t* p, botan_ec_group_t ec_group); + +/** +* Get the a parameter of the elliptic curve equation +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_ec_group_get_a(botan_mp_t* a, botan_ec_group_t ec_group); + +/** +* Get the b parameter of the elliptic curve equation +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_ec_group_get_b(botan_mp_t* b, botan_ec_group_t ec_group); + +/** +* Get the x coordinate of the base point +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_ec_group_get_g_x(botan_mp_t* g_x, botan_ec_group_t ec_group); + +/** +* Get the y coordinate of the base point +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_ec_group_get_g_y(botan_mp_t* g_y, botan_ec_group_t ec_group); + +/** +* Get the order of the base point +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_ec_group_get_order(botan_mp_t* order, botan_ec_group_t ec_group); + +/** +* @returns 0 if curve1 != curve2 +* @returns 1 if curve1 == curve2 +* @returns negative number on error +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_ec_group_equal(botan_ec_group_t curve1, botan_ec_group_t curve2); + +/* +* EC Points and Scalars +*/ +typedef struct botan_ec_scalar_struct* botan_ec_scalar_t; +typedef struct botan_ec_point_struct* botan_ec_point_t; + +BOTAN_FFI_EXPORT(3, 12) int botan_ec_scalar_destroy(botan_ec_scalar_t ec_scalar); + +/** +* Create a new random scalar value +*/ +BOTAN_FFI_EXPORT(3, 12) +int botan_ec_scalar_random(botan_ec_scalar_t* ec_scalar, botan_ec_group_t ec_group, botan_rng_t rng); + +/** +* Convert from an MPI to a scalar +* @returns a negative number if the provided MPI is negative or too large, 0 on success +*/ +BOTAN_FFI_EXPORT(3, 12) +int botan_ec_scalar_from_mp(botan_ec_scalar_t* ec_scalar, botan_ec_group_t ec_group, botan_mp_t mp); + +/** +* Convert from a scalar to an MPI +* @returns a negative number on failure, 0 on success +*/ +BOTAN_FFI_EXPORT(3, 12) +int botan_ec_scalar_to_mp(botan_ec_scalar_t ec_scalar, botan_mp_t* mp); + +BOTAN_FFI_EXPORT(3, 12) int botan_ec_point_destroy(botan_ec_point_t ec_point); + +/** +* Create a point set to the identity element of the group +*/ +BOTAN_FFI_EXPORT(3, 12) int botan_ec_point_identity(botan_ec_point_t* ec_point, botan_ec_group_t ec_group); + +/** +* Create a point set to the standard group generator +*/ +BOTAN_FFI_EXPORT(3, 12) int botan_ec_point_generator(botan_ec_point_t* ec_point, botan_ec_group_t ec_group); + +/** +* Create a point from a pair (x,y) of integers +* The integers must be within the field and must satisfy the curve equation +*/ +BOTAN_FFI_EXPORT(3, 12) +int botan_ec_point_from_xy(botan_ec_point_t* ec_point, botan_ec_group_t ec_group, botan_mp_t x, botan_mp_t y); + +/** +* Create a point from a SEC1 compressed or uncompressed format. +* @returns negative number on error +*/ +BOTAN_FFI_EXPORT(3, 12) +int botan_ec_point_from_bytes(botan_ec_point_t* ec_point, + botan_ec_group_t ec_group, + const uint8_t* bytes, + size_t bytes_len); + +/** +* View the fixed length encoding of the affine x coordinate +* @returns negative number on error +*/ +BOTAN_FFI_EXPORT(3, 12) +int botan_ec_point_view_x_bytes(botan_ec_point_t ec_point, botan_view_ctx ctx, botan_view_bin_fn view); + +/** +* View the fixed length encoding of the affine y coordinate +* @returns negative number on error +*/ +BOTAN_FFI_EXPORT(3, 12) +int botan_ec_point_view_y_bytes(botan_ec_point_t ec_point, botan_view_ctx ctx, botan_view_bin_fn view); + +/** +* View the fixed length encoding of the affine x and y coordinates +* @returns negative number on error +*/ +BOTAN_FFI_EXPORT(3, 12) +int botan_ec_point_view_xy_bytes(botan_ec_point_t ec_point, botan_view_ctx ctx, botan_view_bin_fn view); + +/** +* View the fixed length SEC1 uncompressed encoding +* @returns negative number on error +*/ +BOTAN_FFI_EXPORT(3, 12) +int botan_ec_point_view_uncompressed(botan_ec_point_t ec_point, botan_view_ctx ctx, botan_view_bin_fn view); + +/** +* View the fixed length SEC1 compressed encoding +* @returns negative number on error +*/ +BOTAN_FFI_EXPORT(3, 12) +int botan_ec_point_view_compressed(botan_ec_point_t ec_point, botan_view_ctx ctx, botan_view_bin_fn view); + +/** +* @returns 1 if @param ec_point is the identity element, else 0 +* @returns negative number on error +*/ +BOTAN_FFI_EXPORT(3, 12) int botan_ec_point_is_identity(botan_ec_point_t ec_point); + +/** +* @returns 1 if @param x == @param y else 0 otherwise +* @returns negative number on error +*/ +BOTAN_FFI_EXPORT(3, 12) int botan_ec_point_equal(botan_ec_point_t x, botan_ec_point_t y); + +/** +* @param ec_point point to negate +* @param result contains the result +*/ +BOTAN_FFI_EXPORT(3, 12) int botan_ec_point_negate(botan_ec_point_t* result, botan_ec_point_t ec_point); + +BOTAN_FFI_EXPORT(3, 12) int botan_ec_point_add(botan_ec_point_t* result, botan_ec_point_t x, botan_ec_point_t y); + +BOTAN_FFI_EXPORT(3, 12) +int botan_ec_point_mul(botan_ec_point_t* result, + botan_ec_point_t ec_point, + botan_ec_scalar_t ec_scalar, + botan_rng_t rng); + +/* * Public/private key creation, import, ... */ typedef struct botan_privkey_struct* botan_privkey_t; @@ -1109,6 +1574,16 @@ BOTAN_FFI_EXPORT(2, 0) int botan_privkey_create(botan_privkey_t* key, const char* algo_name, const char* algo_params, botan_rng_t rng); +/** +* Create a new ec private key +* @param key the new object will be placed here +* @param algo_name something like "ECDSA" or "ECDH" +* @param ec_group a (possibly application specific) elliptic curve +* @param rng a random number generator +*/ +BOTAN_FFI_EXPORT(3, 8) +int botan_ec_privkey_create(botan_privkey_t* key, const char* algo_name, botan_ec_group_t ec_group, botan_rng_t rng); + #define BOTAN_CHECK_KEY_EXPENSIVE_TESTS 1 BOTAN_FFI_EXPORT(2, 0) int botan_privkey_check_key(botan_privkey_t key, botan_rng_t rng, uint32_t flags); @@ -1190,6 +1665,7 @@ * Returns 0 on success and sets * If some other error occurs a negative integer is returned. */ +BOTAN_FFI_DEPRECATED("Use botan_privkey_view_{der,pem,raw}") BOTAN_FFI_EXPORT(2, 0) int botan_privkey_export(botan_privkey_t key, uint8_t out[], size_t* out_len, uint32_t flags); /** @@ -1228,6 +1704,7 @@ * * Note: starting in 3.0, the output iterations count is not provided */ +BOTAN_FFI_DEPRECATED("Use botan_privkey_view_encrypted_{der,pem}_timed") BOTAN_FFI_EXPORT(2, 0) int botan_privkey_export_encrypted_pbkdf_msec(botan_privkey_t key, uint8_t out[], @@ -1243,6 +1720,7 @@ /** * Export a private key using the specified number of iterations. */ +BOTAN_FFI_DEPRECATED("Use botan_privkey_view_encrypted_{der,pem}") BOTAN_FFI_EXPORT(2, 0) int botan_privkey_export_encrypted_pbkdf_iter(botan_privkey_t key, uint8_t out[], @@ -1322,6 +1800,7 @@ BOTAN_FFI_EXPORT(2, 0) int botan_privkey_export_pubkey(botan_pubkey_t* out, botan_privkey_t in); +BOTAN_FFI_DEPRECATED("Use botan_pubkey_view_{der,pem,raw}") BOTAN_FFI_EXPORT(2, 0) int botan_pubkey_export(botan_pubkey_t key, uint8_t out[], size_t* out_len, uint32_t flags); /** @@ -1364,6 +1843,29 @@ BOTAN_FFI_EXPORT(2, 0) int botan_privkey_get_field(botan_mp_t output, botan_privkey_t key, const char* field_name); /* +* Get the OID from public or private keys +*/ +BOTAN_FFI_EXPORT(3, 8) +int botan_pubkey_oid(botan_asn1_oid_t* oid, botan_pubkey_t key); + +BOTAN_FFI_EXPORT(3, 8) +int botan_privkey_oid(botan_asn1_oid_t* oid, botan_privkey_t key); + +/** +* Checks whether a key is stateful and sets +* @param out to 1 if it is, or 0 if the key is not stateful +* @return 0 on success, a negative value on failure +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_privkey_stateful_operation(botan_privkey_t key, int* out); + +/** +* Gets information on many operations a (stateful) key has remaining and sets +* @param out to that value +* @return 0 on success, a negative value on failure or if the key is not stateful +*/ +BOTAN_FFI_EXPORT(3, 8) int botan_privkey_remaining_operations(botan_privkey_t key, uint64_t* out); + +/* * Algorithm specific key operations: RSA */ BOTAN_FFI_EXPORT(2, 0) int botan_privkey_load_rsa(botan_privkey_t* key, botan_mp_t p, botan_mp_t q, botan_mp_t e); @@ -1386,6 +1888,8 @@ BOTAN_FFI_EXPORT(2, 0) int botan_pubkey_load_rsa(botan_pubkey_t* key, botan_mp_t n, botan_mp_t e); +BOTAN_FFI_EXPORT(3, 11) int botan_pubkey_load_rsa_pkcs1(botan_pubkey_t* key, const uint8_t bits[], size_t len); + BOTAN_FFI_DEPRECATED("Use botan_pubkey_get_field") BOTAN_FFI_EXPORT(2, 0) int botan_pubkey_rsa_get_e(botan_mp_t e, botan_pubkey_t rsa_key); BOTAN_FFI_DEPRECATED("Use botan_pubkey_get_field") @@ -1475,6 +1979,15 @@ BOTAN_FFI_EXPORT(2, 0) int botan_privkey_load_elgamal(botan_privkey_t* key, botan_mp_t p, botan_mp_t g, botan_mp_t x); /* +* Algorithm specific key operations: EC keys +*/ + +BOTAN_FFI_EXPORT(3, 12) int botan_ec_privkey_get_private_key(botan_privkey_t key, botan_ec_scalar_t* value); + +BOTAN_FFI_EXPORT(3, 12) int botan_ec_privkey_get_group(botan_privkey_t key, botan_ec_group_t* ec_group); + +BOTAN_FFI_EXPORT(3, 12) int botan_ec_pubkey_get_group(botan_pubkey_t key, botan_ec_group_t* ec_group); +/* * Algorithm specific key operations: Ed25519 */ @@ -1482,8 +1995,10 @@ BOTAN_FFI_EXPORT(2, 2) int botan_pubkey_load_ed25519(botan_pubkey_t* key, const uint8_t pubkey[32]); +BOTAN_FFI_DEPRECATED("Use botan_privkey_view_raw") BOTAN_FFI_EXPORT(2, 2) int botan_privkey_ed25519_get_privkey(botan_privkey_t key, uint8_t output[64]); +BOTAN_FFI_DEPRECATED("Use botan_pubkey_view_raw") BOTAN_FFI_EXPORT(2, 2) int botan_pubkey_ed25519_get_pubkey(botan_pubkey_t key, uint8_t pubkey[32]); /* @@ -1494,8 +2009,10 @@ BOTAN_FFI_EXPORT(3, 4) int botan_pubkey_load_ed448(botan_pubkey_t* key, const uint8_t pubkey[57]); +BOTAN_FFI_DEPRECATED("Use botan_privkey_view_raw") BOTAN_FFI_EXPORT(3, 4) int botan_privkey_ed448_get_privkey(botan_privkey_t key, uint8_t output[57]); +BOTAN_FFI_DEPRECATED("Use botan_pubkey_view_raw") BOTAN_FFI_EXPORT(3, 4) int botan_pubkey_ed448_get_pubkey(botan_pubkey_t key, uint8_t pubkey[57]); /* @@ -1506,8 +2023,10 @@ BOTAN_FFI_EXPORT(2, 8) int botan_pubkey_load_x25519(botan_pubkey_t* key, const uint8_t pubkey[32]); +BOTAN_FFI_DEPRECATED("Use botan_privkey_view_raw") BOTAN_FFI_EXPORT(2, 8) int botan_privkey_x25519_get_privkey(botan_privkey_t key, uint8_t output[32]); +BOTAN_FFI_DEPRECATED("Use botan_pubkey_view_raw") BOTAN_FFI_EXPORT(2, 8) int botan_pubkey_x25519_get_pubkey(botan_pubkey_t key, uint8_t pubkey[32]); /* @@ -1518,8 +2037,10 @@ BOTAN_FFI_EXPORT(3, 4) int botan_pubkey_load_x448(botan_pubkey_t* key, const uint8_t pubkey[56]); +BOTAN_FFI_DEPRECATED("Use botan_privkey_view_raw") BOTAN_FFI_EXPORT(3, 4) int botan_privkey_x448_get_privkey(botan_privkey_t key, uint8_t output[56]); +BOTAN_FFI_DEPRECATED("Use botan_pubkey_view_raw") BOTAN_FFI_EXPORT(3, 4) int botan_pubkey_x448_get_pubkey(botan_pubkey_t key, uint8_t pubkey[56]); /* @@ -1533,11 +2054,17 @@ int botan_pubkey_load_ml_dsa(botan_pubkey_t* key, const uint8_t pubkey[], size_t key_len, const char* mldsa_mode); /* -* Algorithm specific key operations: Kyber +* Algorithm specific key operations: Kyber R3 +* +* Note that Kyber R3 support is somewhat deprecated and may be removed in a +* future major release. Using the final ML-KEM is highly recommended in any new +* system. */ +BOTAN_FFI_DEPRECATED("Kyber R3 support is deprecated") BOTAN_FFI_EXPORT(3, 1) int botan_privkey_load_kyber(botan_privkey_t* key, const uint8_t privkey[], size_t key_len); +BOTAN_FFI_DEPRECATED("Kyber R3 support is deprecated") BOTAN_FFI_EXPORT(3, 1) int botan_pubkey_load_kyber(botan_pubkey_t* key, const uint8_t pubkey[], size_t key_len); BOTAN_FFI_DEPRECATED("Use generic botan_privkey_view_raw") @@ -1606,15 +2133,24 @@ BOTAN_FFI_EXPORT(2, 2) int botan_pubkey_load_ecdsa(botan_pubkey_t* key, botan_mp_t public_x, botan_mp_t public_y, const char* curve_name); +BOTAN_FFI_EXPORT(3, 10) +int botan_pubkey_load_ecdsa_sec1(botan_pubkey_t* key, const uint8_t sec1[], size_t sec1_len, const char* curve_name); + BOTAN_FFI_EXPORT(2, 2) int botan_pubkey_load_ecdh(botan_pubkey_t* key, botan_mp_t public_x, botan_mp_t public_y, const char* curve_name); +BOTAN_FFI_EXPORT(3, 10) +int botan_pubkey_load_ecdh_sec1(botan_pubkey_t* key, const uint8_t sec1[], size_t sec1_len, const char* curve_name); + BOTAN_FFI_EXPORT(2, 2) int botan_privkey_load_ecdh(botan_privkey_t* key, botan_mp_t scalar, const char* curve_name); BOTAN_FFI_EXPORT(2, 2) int botan_pubkey_load_sm2(botan_pubkey_t* key, botan_mp_t public_x, botan_mp_t public_y, const char* curve_name); +BOTAN_FFI_EXPORT(3, 10) +int botan_pubkey_load_sm2_sec1(botan_pubkey_t* key, const uint8_t sec1[], size_t sec1_len, const char* curve_name); + BOTAN_FFI_EXPORT(2, 2) int botan_privkey_load_sm2(botan_privkey_t* key, botan_mp_t scalar, const char* curve_name); @@ -1850,6 +2386,39 @@ typedef struct botan_x509_cert_struct* botan_x509_cert_t; +/** + * Generic values that may be retrieved from X.509 certificates or CRLs via + * the generic getter functions. + * + * When extending this list the existing entries must stay backward-compatible + * to remain ABI compatible across versions. Therefore, new values must be added + * to the end of this list. + * + * See: + * * botan_x509_cert_view_binary_values() + * * botan_x509_crl_view_binary_values() + * * botan_x509_cert_view_string_values() + */ +typedef enum /* NOLINT(*-enum-size,*-use-enum-class) */ { + BOTAN_X509_SERIAL_NUMBER = 0, /** singleton binary big-endian encoding */ + BOTAN_X509_SUBJECT_DN_BITS = 1, /** singleton binary DER encoding of the subject distinguished name */ + BOTAN_X509_ISSUER_DN_BITS = 2, /** singleton binary DER encoding of the issuer distinguished name */ + BOTAN_X509_SUBJECT_KEY_IDENTIFIER = 3, /** singleton binary encoding */ + BOTAN_X509_AUTHORITY_KEY_IDENTIFIER = 4, /** singleton binary encoding */ + + BOTAN_X509_PUBLIC_KEY_PKCS8_BITS = 200, /** singleton binary DER encoding of the PKCS#8 public key */ + BOTAN_X509_TBS_DATA_BITS = 201, /** singleton binary DER encoding */ + BOTAN_X509_SIGNATURE_SCHEME_BITS = 202, /** singleton binary DER encoding of the algorithm identifier */ + BOTAN_X509_SIGNATURE_BITS = 203, /** singleton binary signature bits */ + + BOTAN_X509_DER_ENCODING = 300, /** singleton binary DER encoding of the whole object */ + BOTAN_X509_PEM_ENCODING = 301, /** singleton string value PEM encoding of the whole object */ + + BOTAN_X509_CRL_DISTRIBUTION_URLS = 400, /** multi-value string of the CRL distribution points */ + BOTAN_X509_OCSP_RESPONDER_URLS = 401, /** multi-value string of the OCSP responder URLs */ + BOTAN_X509_CA_ISSUERS_URLS = 402, /** multi-value string of the CA issuer URLs */ +} botan_x509_value_type; + BOTAN_FFI_EXPORT(2, 0) int botan_x509_cert_load(botan_x509_cert_t* cert_obj, const uint8_t cert[], size_t cert_len); BOTAN_FFI_EXPORT(2, 0) int botan_x509_cert_load_file(botan_x509_cert_t* cert_obj, const char* filename); @@ -1860,6 +2429,38 @@ BOTAN_FFI_EXPORT(2, 8) int botan_x509_cert_dup(botan_x509_cert_t* new_cert, botan_x509_cert_t cert); +/** + * Retrieve a specific binary value from an X.509 certificate. + * + * For multi-values @p index allows enumerating the available entries, until + * BOTAN_FFI_ERROR_OUT_OF_RANGE is returned. For singleton values, an @p index + * of value "0" is expected. + * + * @returns BOTAN_FFI_ERROR_NO_VALUE if the provided @p cert does not provide + * the requested @p value_type at all or not in binary format. + */ +BOTAN_FFI_EXPORT(3, 11) +int botan_x509_cert_view_binary_values( + botan_x509_cert_t cert, botan_x509_value_type value_type, size_t index, botan_view_ctx ctx, botan_view_bin_fn view); +BOTAN_FFI_EXPORT(3, 11) +int botan_x509_cert_view_binary_values_count(botan_x509_cert_t cert, botan_x509_value_type value_type, size_t* count); + +/** + * Retrieve a specific string value from an X.509 certificate. + * + * For multi-values @p index allows enumerating the available entries, until + * BOTAN_FFI_ERROR_OUT_OF_RANGE is returned. For singleton values, an @p index + * of value "0" is expected. + * + * @returns BOTAN_FFI_ERROR_NO_VALUE if the provided @p cert does not provide + * the requested @p value_type at all or not in string format. + */ +BOTAN_FFI_EXPORT(3, 11) +int botan_x509_cert_view_string_values( + botan_x509_cert_t cert, botan_x509_value_type value_type, size_t index, botan_view_ctx ctx, botan_view_str_fn view); +BOTAN_FFI_EXPORT(3, 11) +int botan_x509_cert_view_string_values_count(botan_x509_cert_t cert, botan_x509_value_type value_type, size_t* count); + /* Prefer botan_x509_cert_not_before and botan_x509_cert_not_after */ BOTAN_FFI_EXPORT(2, 0) int botan_x509_cert_get_time_starts(botan_x509_cert_t cert, char out[], size_t* out_len); BOTAN_FFI_EXPORT(2, 0) int botan_x509_cert_get_time_expires(botan_x509_cert_t cert, char out[], size_t* out_len); @@ -1867,10 +2468,12 @@ BOTAN_FFI_EXPORT(2, 8) int botan_x509_cert_not_before(botan_x509_cert_t cert, uint64_t* time_since_epoch); BOTAN_FFI_EXPORT(2, 8) int botan_x509_cert_not_after(botan_x509_cert_t cert, uint64_t* time_since_epoch); +/* TODO(Botan4) this should use char for the out param */ BOTAN_FFI_EXPORT(2, 0) int botan_x509_cert_get_fingerprint(botan_x509_cert_t cert, const char* hash, uint8_t out[], size_t* out_len); BOTAN_FFI_EXPORT(2, 0) int botan_x509_cert_get_serial_number(botan_x509_cert_t cert, uint8_t out[], size_t* out_len); +BOTAN_FFI_EXPORT(3, 11) int botan_x509_cert_serial_number(botan_x509_cert_t cert, botan_mp_t* serial_number); BOTAN_FFI_EXPORT(2, 0) int botan_x509_cert_get_authority_key_id(botan_x509_cert_t cert, uint8_t out[], size_t* out_len); BOTAN_FFI_EXPORT(2, 0) int botan_x509_cert_get_subject_key_id(botan_x509_cert_t cert, uint8_t out[], size_t* out_len); @@ -1881,21 +2484,49 @@ BOTAN_FFI_EXPORT(2, 0) int botan_x509_cert_get_public_key(botan_x509_cert_t cert, botan_pubkey_t* key); +/** + * Returns 1 iff the cert is a CA certificate + */ +BOTAN_FFI_EXPORT(3, 11) int botan_x509_cert_is_ca(botan_x509_cert_t cert); + +/** + * Retrieves the path length constraint from the certificate. + * If no such constraint is present, BOTAN_FFI_ERROR_NO_VALUE is returned. + */ +BOTAN_FFI_EXPORT(3, 11) int botan_x509_cert_get_path_length_constraint(botan_x509_cert_t cert, size_t* path_limit); + +/** + * Enumerates the names of the given @p key in the issuer DN. If @p index is + * out of bounds, BOTAN_FFI_ERROR_BAD_PARAMETER is returned. + * + * TODO(Botan4) use BOTAN_FFI_ERROR_OUT_OF_RANGE instead of BAD_PARAMETER + * TODO(Botan4) this should use char for the out param + */ BOTAN_FFI_EXPORT(2, 0) int botan_x509_cert_get_issuer_dn( botan_x509_cert_t cert, const char* key, size_t index, uint8_t out[], size_t* out_len); +BOTAN_FFI_EXPORT(3, 11) int botan_x509_cert_get_issuer_dn_count(botan_x509_cert_t cert, const char* key, size_t* count); +/** + * Enumerates the names of the given @p key in the subject DN. If @p index is + * out of bounds, BOTAN_FFI_ERROR_BAD_PARAMETER is returned. + * + * TODO(Botan4) use BOTAN_FFI_ERROR_OUT_OF_RANGE instead of BAD_PARAMETER + * TODO(Botan4) this should use char for the out param + */ BOTAN_FFI_EXPORT(2, 0) int botan_x509_cert_get_subject_dn( botan_x509_cert_t cert, const char* key, size_t index, uint8_t out[], size_t* out_len); +BOTAN_FFI_EXPORT(3, 11) +int botan_x509_cert_get_subject_dn_count(botan_x509_cert_t cert, const char* key, size_t* count); BOTAN_FFI_EXPORT(2, 0) int botan_x509_cert_to_string(botan_x509_cert_t cert, char out[], size_t* out_len); BOTAN_FFI_EXPORT(3, 0) int botan_x509_cert_view_as_string(botan_x509_cert_t cert, botan_view_ctx ctx, botan_view_str_fn view); -/* Must match values of Key_Constraints in key_constraints.h */ -enum botan_x509_cert_key_constraints { +/* Must match values of Key_Constraints in pkix_enums.h */ +enum botan_x509_cert_key_constraints /* NOLINT(*-enum-size,*-use-enum-class) */ { NO_CONSTRAINTS = 0, DIGITAL_SIGNATURE = 32768, NON_REPUDIATION = 16384, @@ -1911,6 +2542,127 @@ BOTAN_FFI_EXPORT(2, 0) int botan_x509_cert_allowed_usage(botan_x509_cert_t cert, unsigned int key_usage); /** +* Check if the certificate allows the specified extended usage OID. See RFC 5280 +* Section 4.2.1.12 for OIDs to query for this. If no extended key usage +* extension is found in the certificate, this always returns "not success". +* +* Typical OIDs to check for: +* * "PKIX.ServerAuth" +* * "PKIX.ClientAuth" +* * "PKIX.CodeSigning" +* * "PKIX.OCSPSigning" +* +* The @p oid parameter can be either a canonical OID string or identifiers as +* indicated in the examples above. +*/ +BOTAN_FFI_EXPORT(3, 11) int botan_x509_cert_allowed_extended_usage_str(botan_x509_cert_t cert, const char* oid); + +/** +* Check if the certificate allows the specified extended usage OID. See RFC 5280 +* Section 4.2.1.12 for OIDs to query for this. If no extended key usage +* extension is found in the certificate, this always returns "not success". +* +* This is similar to botan_x509_cert_allowed_extended_usage_str but takes an OID +* object instead of a string describing the OID. +*/ +BOTAN_FFI_EXPORT(3, 11) int botan_x509_cert_allowed_extended_usage_oid(botan_x509_cert_t cert, botan_asn1_oid_t oid); + +typedef struct botan_x509_general_name_struct* botan_x509_general_name_t; + +/** +* GeneralName type identifiers as defined in RFC 5280 A.2 (GeneralName ::= CHOICE) +* Type identifiers that are omitted here are (currently) not supported. Also, +* there is currently no way to access OTHER_NAME values via the FFI. +*/ +enum botan_x509_general_name_types /* NOLINT(*-enum-size,*-use-enum-class) */ { + BOTAN_X509_OTHER_NAME = 0, + BOTAN_X509_EMAIL_ADDRESS = 1, + BOTAN_X509_DNS_NAME = 2, + BOTAN_X509_DIRECTORY_NAME = 4, + BOTAN_X509_URI = 6, + BOTAN_X509_IP_ADDRESS = 7, +}; + +/** +* Provides the contained type of the @p name and returns BOTAN_FFI_SUCCESS if +* that type is supported and may be retrieved via the view functions below. +* Otherwise BOTAN_FFI_ERROR_INVALID_OBJECT_STATE is returned. +*/ +BOTAN_FFI_EXPORT(3, 11) int botan_x509_general_name_get_type(botan_x509_general_name_t name, unsigned int* type); + +/** +* Views the name as a string or returns BOTAN_FFI_ERROR_INVALID_OBJECT_STATE +* if the contained GeneralName value cannot be represented as a string. +* +* The types BOTAN_X509_EMAIL_ADDRESS, BOTAN_X509_DNS_NAME, BOTAN_X509_URI, +* BOTAN_X509_IP_ADDRESS may be viewed as "string". +*/ +BOTAN_FFI_EXPORT(3, 11) +int botan_x509_general_name_view_string_value(botan_x509_general_name_t name, + botan_view_ctx ctx, + botan_view_str_fn view); + +/** +* Views the name as a bit string or returns BOTAN_FFI_ERROR_INVALID_OBJECT_STATE +* if the contained GeneralName value cannot be represented as a binary string. +* +* The types BOTAN_X509_DIRECTORY_NAME, BOTAN_X509_IP_ADDRESS may be viewed as +* "binary". +*/ +BOTAN_FFI_EXPORT(3, 11) +int botan_x509_general_name_view_binary_value(botan_x509_general_name_t name, + botan_view_ctx ctx, + botan_view_bin_fn view); + +BOTAN_FFI_EXPORT(3, 11) int botan_x509_general_name_destroy(botan_x509_general_name_t alt_names); + +/** +* Extracts "permitted" name constraints from a given @p cert one-by-one. +* Returns BOTAN_FFI_ERROR_OUT_OF_RANGE if the given @p index is larger than the +* available number of "permitted" name constraints. +*/ +BOTAN_FFI_EXPORT(3, 11) +int botan_x509_cert_permitted_name_constraints(botan_x509_cert_t cert, + size_t index, + botan_x509_general_name_t* constraint); +BOTAN_FFI_EXPORT(3, 11) int botan_x509_cert_permitted_name_constraints_count(botan_x509_cert_t cert, size_t* count); + +/** +* Extracts "excluded" name constraints from a given @p cert one-by-one. +* Returns BOTAN_FFI_ERROR_OUT_OF_RANGE if the given @p index is larger than the +* available number of "excluded" name constraints. +*/ +BOTAN_FFI_EXPORT(3, 11) +int botan_x509_cert_excluded_name_constraints(botan_x509_cert_t cert, + size_t index, + botan_x509_general_name_t* constraint); +BOTAN_FFI_EXPORT(3, 11) int botan_x509_cert_excluded_name_constraints_count(botan_x509_cert_t cert, size_t* count); + +/** +* Provides access to all "subject alternative names", where each entry is +* returned as a botan_x509_general_name_t. If the given @p index is not +* within range of the available entries, BOTAN_FFI_ERROR_OUT_OF_RANGE is +* returned. If @p cert does not contain a SubjectAlternativeNames extension, +* BOTAN_FFI_ERROR_NO_VALUE is returned. +*/ +BOTAN_FFI_EXPORT(3, 11) +int botan_x509_cert_subject_alternative_names(botan_x509_cert_t cert, + size_t index, + botan_x509_general_name_t* alt_name); +BOTAN_FFI_EXPORT(3, 11) int botan_x509_cert_subject_alternative_names_count(botan_x509_cert_t cert, size_t* count); + +/** +* Provides access to all "issuer alternative names", where each entry is +* returned as a botan_x509_general_name_t. If the given @p index is not +* within range of the available entries, BOTAN_FFI_ERROR_OUT_OF_RANGE is +* returned. If @p cert does not contain an IssuerAlternativeNames extension, +* BOTAN_FFI_ERROR_NO_VALUE is returned. +*/ +BOTAN_FFI_EXPORT(3, 11) +int botan_x509_cert_issuer_alternative_names(botan_x509_cert_t cert, size_t index, botan_x509_general_name_t* alt_name); +BOTAN_FFI_EXPORT(3, 11) int botan_x509_cert_issuer_alternative_names_count(botan_x509_cert_t cert, size_t* count); + +/** * Check if the certificate matches the specified hostname via alternative name or CN match. * RFC 5280 wildcards also supported. */ @@ -1947,20 +2699,176 @@ **************************/ typedef struct botan_x509_crl_struct* botan_x509_crl_t; +typedef struct botan_x509_crl_entry_struct* botan_x509_crl_entry_t; BOTAN_FFI_EXPORT(2, 13) int botan_x509_crl_load_file(botan_x509_crl_t* crl_obj, const char* crl_path); BOTAN_FFI_EXPORT(2, 13) int botan_x509_crl_load(botan_x509_crl_t* crl_obj, const uint8_t crl_bits[], size_t crl_bits_len); +BOTAN_FFI_EXPORT(3, 11) int botan_x509_crl_this_update(botan_x509_crl_t crl, uint64_t* time_since_epoch); +BOTAN_FFI_EXPORT(3, 11) int botan_x509_crl_next_update(botan_x509_crl_t crl, uint64_t* time_since_epoch); + +/** +* Create a new CRL +* @param crl_obj The newly created CRL +* @param rng a random number generator object +* @param ca_cert The CA Certificate the CRL belongs to +* @param ca_key The private key of that CA +* @param issue_time The time when the CRL becomes valid +* @param next_update The number of seconds after issue_time until the CRL expires +* @param hash_fn The hash function to use, may be null +* @param padding The padding to use, may be null +*/ +BOTAN_FFI_EXPORT(3, 11) +int botan_x509_crl_create(botan_x509_crl_t* crl_obj, + botan_rng_t rng, + botan_x509_cert_t ca_cert, + botan_privkey_t ca_key, + uint64_t issue_time, + uint32_t next_update, + const char* hash_fn, + const char* padding); + +/* Must match values of CRL_Code in pkix_enums.h */ +enum botan_x509_crl_reason_code /* NOLINT(*-enum-size,*-use-enum-class) */ { + BOTAN_CRL_ENTRY_UNSPECIFIED = 0, + BOTAN_CRL_ENTRY_KEY_COMPROMISE = 1, + BOTAN_CRL_ENTRY_CA_COMPROMISE = 2, + BOTAN_CRL_ENTRY_AFFILIATION_CHANGED = 3, + BOTAN_CRL_ENTRY_SUPERSEDED = 4, + BOTAN_CRL_ENTRY_CESSATION_OF_OPERATION = 5, + BOTAN_CRL_ENTRY_CERTIFICATE_HOLD = 6, + BOTAN_CRL_ENTRY_REMOVE_FROM_CRL = 8, + BOTAN_CRL_ENTRY_PRIVILEGE_WITHDRAWN = 9, + BOTAN_CRL_ENTRY_AA_COMPROMISE = 10 +}; + +/** +* Create a new CRL entry that marks @p cert as revoked +* @param entry The newly created CRL entry +* @param cert The certificate to mark as revoked +* @param reason_code The reason code for revocation +*/ +BOTAN_FFI_EXPORT(3, 11) +int botan_x509_crl_entry_create(botan_x509_crl_entry_t* entry, botan_x509_cert_t cert, int reason_code); + +/** +* Update a CRL with new revoked entries. This does not modify the old crl, and instead creates a new one. +* @param crl_obj The newly created CRL +* @param last_crl The CRL to update +* @param rng a random number generator object +* @param ca_cert The CA Certificate the CRL belongs to +* @param ca_key The private key of that CA +* @param issue_time The time when the CRL becomes valid +* @param next_update The number of seconds after issue_time until the CRL expires +* @param new_entries The entries to add to the CRL +* @param new_entries_len The number of entries +* @param hash_fn The hash function to use, may be null +* @param padding The padding to use, may be null +*/ +BOTAN_FFI_EXPORT(3, 11) +int botan_x509_crl_update(botan_x509_crl_t* crl_obj, + botan_x509_crl_t last_crl, + botan_rng_t rng, + botan_x509_cert_t ca_cert, + botan_privkey_t ca_key, + uint64_t issue_time, + uint32_t next_update, + const botan_x509_crl_entry_t* new_entries, + size_t new_entries_len, + const char* hash_fn, + const char* padding); + +BOTAN_FFI_EXPORT(3, 11) int botan_x509_crl_verify_signature(botan_x509_crl_t crl, botan_pubkey_t key); + BOTAN_FFI_EXPORT(2, 13) int botan_x509_crl_destroy(botan_x509_crl_t crl); /** + * Retrieve a specific binary value from an X.509 certificate revocation list. + * + * For multi-values @p index allows enumerating the available entries, until + * BOTAN_FFI_ERROR_OUT_OF_RANGE is returned. For singleton values, an @p index + * of value "0" is expected. + * + * @returns BOTAN_FFI_ERROR_NO_VALUE if the provided @p crl_obj does not provide + * the requested @p value_type at all or not in binary format. + */ +BOTAN_FFI_EXPORT(3, 11) +int botan_x509_crl_view_binary_values(botan_x509_crl_t crl_obj, + botan_x509_value_type value_type, + size_t index, + botan_view_ctx ctx, + botan_view_bin_fn view); +BOTAN_FFI_EXPORT(3, 11) +int botan_x509_crl_view_binary_values_count(botan_x509_crl_t crl_obj, botan_x509_value_type value_type, size_t* count); + +/** + * Retrieve a specific string value from an X.509 certificate revocation list. + * + * For multi-values @p index allows enumerating the available entries, until + * BOTAN_FFI_ERROR_OUT_OF_RANGE is returned. For singleton values, an @p index + * of value "0" is expected. + * + * @returns BOTAN_FFI_ERROR_NO_VALUE if the provided @p crl_obj does not provide + * the requested @p value_type at all or not in string format. + */ +BOTAN_FFI_EXPORT(3, 11) +int botan_x509_crl_view_string_values(botan_x509_crl_t crl_obj, + botan_x509_value_type value_type, + size_t index, + botan_view_ctx ctx, + botan_view_str_fn view); +BOTAN_FFI_EXPORT(3, 11) +int botan_x509_crl_view_string_values_count(botan_x509_crl_t crl_obj, botan_x509_value_type value_type, size_t* count); + +/** * Given a CRL and a certificate, * check if the certificate is revoked on that particular CRL */ BOTAN_FFI_EXPORT(2, 13) int botan_x509_is_revoked(botan_x509_crl_t crl, botan_x509_cert_t cert); /** +* Allows iterating all entries of the CRL. +* +* @param crl the CRL whose entries should be listed +* @param index the index of the CRL entry to return +* @param entry an object handle containing the CRL entry data +* +* @returns BOTAN_FFI_ERROR_OUT_OF_RANGE if the given @p index is out of range of +* the CRL entry list. +*/ +BOTAN_FFI_EXPORT(3, 11) +int botan_x509_crl_entries(botan_x509_crl_t crl, size_t index, botan_x509_crl_entry_t* entry); +BOTAN_FFI_EXPORT(3, 11) int botan_x509_crl_entries_count(botan_x509_crl_t crl, size_t* count); + +/** +* Return the revocation reason code for the given CRL @p entry. +* See `botan_x509_crl_reason_code` and RFC 5280 - 5.3.1 for possible reason codes. +*/ +BOTAN_FFI_EXPORT(3, 11) int botan_x509_crl_entry_reason(botan_x509_crl_entry_t entry, int* reason_code); + +/** +* Return the revocation date for the given CRL @p entry as time since epoch +* in seconds. +*/ +BOTAN_FFI_EXPORT(3, 11) +int botan_x509_crl_entry_revocation_date(botan_x509_crl_entry_t entry, uint64_t* time_since_epoch); + +/** +* Return the serial number associated with the given CRL @p entry. +*/ +BOTAN_FFI_EXPORT(3, 11) +int botan_x509_crl_entry_serial_number(botan_x509_crl_entry_t entry, botan_mp_t* serial_number); + +/** +* View the serial number associated with the given CRL @p entry. +*/ +BOTAN_FFI_EXPORT(3, 11) +int botan_x509_crl_entry_view_serial_number(botan_x509_crl_entry_t entry, botan_view_ctx ctx, botan_view_bin_fn view); + +BOTAN_FFI_EXPORT(3, 11) int botan_x509_crl_entry_destroy(botan_x509_crl_entry_t entry); + +/** * Different flavor of `botan_x509_cert_verify`, supports revocation lists. * CRLs are passed as an array, same as intermediates and trusted CAs */ @@ -2295,7 +3203,7 @@ * @returns 1 if the crypto backend can be enabled */ BOTAN_FFI_EXPORT(3, 6) -int botan_tpm2_supports_crypto_backend(); +int botan_tpm2_supports_crypto_backend(void); /** * Initialize a TPM2 context @@ -2339,7 +3247,7 @@ int botan_tpm2_ctx_enable_crypto_backend(botan_tpm2_ctx_t ctx, botan_rng_t rng); /** -* Frees all resouces of a TPM2 context +* Frees all resources of a TPM2 context * @param ctx TPM2 context * @return 0 on success */ @@ -2362,7 +3270,7 @@ botan_rng_t rng); /** -* Frees all resouces of a TPM2 Crypto Callback State +* Frees all resources of a TPM2 Crypto Callback State * Note that this does not attempt to de-register the crypto backend, * it just frees the resource pointed to by @p cbs. Use the ESAPI function * ``Esys_SetCryptoCallbacks(ctx, nullptr)`` to deregister manually. @@ -2401,6 +3309,8 @@ BOTAN_FFI_EXPORT(3, 6) int botan_tpm2_session_destroy(botan_tpm2_session_t session); +/* NOLINTEND(*-macro-usage,*-misplaced-const) */ + #ifdef __cplusplus } #endif diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_block.cpp botan3-3.12.0+dfsg/src/lib/ffi/ffi_block.cpp --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_block.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_block.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -28,8 +28,7 @@ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; } - *bc = new botan_block_cipher_struct(std::move(cipher)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(bc, std::move(cipher)); }); } diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_cert.cpp botan3-3.12.0+dfsg/src/lib/ffi/ffi_cert.cpp --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_cert.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_cert.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,29 +6,176 @@ #include +#include +#include #include +#include #include #include #if defined(BOTAN_HAS_X509_CERTIFICATES) #include #include + #include #include #include + #include + #include + #include + #include #endif -extern "C" { +#if defined(BOTAN_HAS_X509_CERTIFICATES) -using namespace Botan_FFI; +namespace Botan_FFI { + +namespace { + +/** + * As specified in RFC 5280 Section 4.2.1.6. alternative names essentially are a + * collection of GeneralNames. This allows mapping a single entry of @p altnames + * to a GeneralName by its @p index. If the index is out of range, std::nullopt + * is returned. + * + * NOTE: if the set of alternative name types handled here is extended, + * count_general_names_in() must be updated accordingly! + */ +std::optional extract_general_name_at(const Botan::AlternativeName& altnames, size_t index) { + if(index < altnames.email().size()) { + auto itr = altnames.email().begin(); + std::advance(itr, index); + return Botan::GeneralName::email(*itr); + } + index -= altnames.email().size(); -#if defined(BOTAN_HAS_X509_CERTIFICATES) + if(index < altnames.dns().size()) { + auto itr = altnames.dns().begin(); + std::advance(itr, index); + return Botan::GeneralName::dns(*itr); + } + index -= altnames.dns().size(); + + if(index < altnames.directory_names().size()) { + auto itr = altnames.directory_names().begin(); + std::advance(itr, index); + return Botan::GeneralName::directory_name(*itr); + } + index -= altnames.directory_names().size(); + + if(index < altnames.uris().size()) { + auto itr = altnames.uris().begin(); + std::advance(itr, index); + return Botan::GeneralName::uri(*itr); + } + index -= altnames.uris().size(); + + if(index < altnames.ipv4_address().size()) { + auto itr = altnames.ipv4_address().begin(); + std::advance(itr, index); + return Botan::GeneralName::ipv4_address(*itr); + } + index -= altnames.ipv4_address().size(); + + if(index < altnames.ipv6_address().size()) { + auto itr = altnames.ipv6_address().begin(); + std::advance(itr, index); + return Botan::GeneralName::ipv6_address(*itr); + } + + return std::nullopt; +} + +/** + * Counts the total number of GeneralNames contained in the given + * AlternativeName @p alt_names. + * + * NOTE: if the set of alternative name types handled here is extended, + * extract_general_name_at() must be updated accordingly! + */ +size_t count_general_names_in(const Botan::AlternativeName& alt_names) { + return alt_names.email().size() + alt_names.dns().size() + alt_names.directory_names().size() + + alt_names.uris().size() + alt_names.ipv4_address().size() + alt_names.ipv6_address().size(); +} + +std::optional to_botan_x509_general_name_types(Botan::GeneralName::NameType gn_type) { + using Type = Botan::GeneralName::NameType; + switch(gn_type) { + case Type::Unknown: + return std::nullopt; + case Type::RFC822: + return BOTAN_X509_EMAIL_ADDRESS; + case Type::DNS: + return BOTAN_X509_DNS_NAME; + case Type::URI: + return BOTAN_X509_URI; + case Type::DN: + return BOTAN_X509_DIRECTORY_NAME; + case Type::IPv4: + case Type::IPv6: + return BOTAN_X509_IP_ADDRESS; + case Type::Other: + return BOTAN_X509_OTHER_NAME; + } + + BOTAN_ASSERT_UNREACHABLE(); +} + +/** + * Given some enumerator-style function @p fn, count how many values it can + * produce before returning BOTAN_FFI_ERROR_OUT_OF_RANGE. If the first call to + * @p fn returns BOTAN_FFI_ERROR_NO_VALUE, zero is written to @p count. + * + * If this function returns BOTAN_FFI_SUCCESS, @p count contains the number of + * values that can be enumerated. Otherwise, the value of @p count is undefined. + */ +template EnumeratorT> +int enumerator_count_values(size_t* count, EnumeratorT fn) { + if(Botan::any_null_pointers(count)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + *count = 0; + for(;; ++(*count)) { + const auto rc = fn(*count); + switch(rc) { + case BOTAN_FFI_ERROR_NO_VALUE: + case BOTAN_FFI_ERROR_OUT_OF_RANGE: + // hit the end of the enumeration + return BOTAN_FFI_SUCCESS; + case BOTAN_FFI_SUCCESS: + // got a value, continue counting + break; + default: + // unexpected error from enumerator function + return rc; + } + } +} + +std::chrono::system_clock::time_point timepoint_from_timestamp(uint64_t time_since_epoch) { + return std::chrono::system_clock::time_point(std::chrono::seconds(time_since_epoch)); +} + +std::string default_from_ptr(const char* value) { + std::string ret; + if(value != nullptr) { + ret = value; + } + return ret; +} -BOTAN_FFI_DECLARE_STRUCT(botan_x509_cert_struct, Botan::X509_Certificate, 0x8F628937); +} // namespace + +} // namespace Botan_FFI #endif +extern "C" { + +using namespace Botan_FFI; + int botan_x509_cert_load_file(botan_x509_cert_t* cert_obj, const char* cert_path) { - if(!cert_obj || !cert_path) { + if(cert_obj == nullptr || cert_path == nullptr) { return BOTAN_FFI_ERROR_NULL_POINTER; } @@ -36,8 +183,7 @@ return ffi_guard_thunk(__func__, [=]() -> int { auto c = std::make_unique(cert_path); - *cert_obj = new botan_x509_cert_struct(std::move(c)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(cert_obj, std::move(c)); }); #else @@ -46,7 +192,7 @@ } int botan_x509_cert_dup(botan_x509_cert_t* cert_obj, botan_x509_cert_t cert) { - if(!cert_obj) { + if(cert_obj == nullptr) { return BOTAN_FFI_ERROR_NULL_POINTER; } @@ -54,8 +200,7 @@ return ffi_guard_thunk(__func__, [=]() -> int { auto c = std::make_unique(safe_get(cert)); - *cert_obj = new botan_x509_cert_struct(std::move(c)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(cert_obj, std::move(c)); }); #else @@ -65,7 +210,7 @@ } int botan_x509_cert_load(botan_x509_cert_t* cert_obj, const uint8_t cert_bits[], size_t cert_bits_len) { - if(!cert_obj || !cert_bits) { + if(cert_obj == nullptr || cert_bits == nullptr) { return BOTAN_FFI_ERROR_NULL_POINTER; } @@ -73,14 +218,247 @@ return ffi_guard_thunk(__func__, [=]() -> int { Botan::DataSource_Memory bits(cert_bits, cert_bits_len); auto c = std::make_unique(bits); - *cert_obj = new botan_x509_cert_struct(std::move(c)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(cert_obj, std::move(c)); }); #else BOTAN_UNUSED(cert_bits_len); return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; #endif } +} + +namespace { + +#if defined(BOTAN_HAS_X509_CERTIFICATES) + +int botan_x509_object_view_value(const Botan::X509_Object& object, + botan_x509_value_type value_type, + size_t index, + botan_view_ctx ctx, + botan_view_str_fn view_fn) { + if(index != 0) { + // As of now there are no multi-value generic string entries. + return BOTAN_FFI_ERROR_OUT_OF_RANGE; + } + + auto view = [=](const std::string& value) { return invoke_view_callback(view_fn, ctx, value); }; + + switch(value_type) { + case BOTAN_X509_PEM_ENCODING: + return view(object.PEM_encode()); + default: + BOTAN_ASSERT_UNREACHABLE(); /* called with unexpected (non-generic) value_type */ + } +} + +int botan_x509_object_view_value(const Botan::X509_Object& object, + botan_x509_value_type value_type, + size_t index, + botan_view_ctx ctx, + botan_view_bin_fn view_fn) { + if(index != 0) { + // As of now there are no multi-value generic binary entries. + return BOTAN_FFI_ERROR_OUT_OF_RANGE; + } + + auto view = [=](std::span value) { return invoke_view_callback(view_fn, ctx, value); }; + + switch(value_type) { + case BOTAN_X509_TBS_DATA_BITS: + return view(object.tbs_data()); + case BOTAN_X509_SIGNATURE_SCHEME_BITS: + return view(object.signature_algorithm().BER_encode()); + case BOTAN_X509_SIGNATURE_BITS: + return view(object.signature()); + case BOTAN_X509_DER_ENCODING: + return view(object.BER_encode()); + default: + BOTAN_ASSERT_UNREACHABLE(); /* called with unexpected (non-generic) value_type */ + } +} + +#endif + +} // namespace + +extern "C" { + +int botan_x509_cert_view_binary_values(botan_x509_cert_t cert, + botan_x509_value_type value_type, + size_t index, + botan_view_ctx ctx, + botan_view_bin_fn view_fn) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + if(index != 0) { + // As of now there are no multi-value binary entries. + return BOTAN_FFI_ERROR_OUT_OF_RANGE; + } + + auto view = [=](std::span value) -> int { + if(value.empty()) { + return BOTAN_FFI_ERROR_NO_VALUE; + } else { + return invoke_view_callback(view_fn, ctx, value); + } + }; + + return BOTAN_FFI_VISIT(cert, [=](const Botan::X509_Certificate& c) -> int { + switch(value_type) { + case BOTAN_X509_SERIAL_NUMBER: + return view(c.serial_number()); + case BOTAN_X509_SUBJECT_DN_BITS: + return view(c.raw_subject_dn()); + case BOTAN_X509_ISSUER_DN_BITS: + return view(c.raw_issuer_dn()); + case BOTAN_X509_SUBJECT_KEY_IDENTIFIER: + return view(c.subject_key_id()); + case BOTAN_X509_AUTHORITY_KEY_IDENTIFIER: + return view(c.authority_key_id()); + case BOTAN_X509_PUBLIC_KEY_PKCS8_BITS: + return view(c.subject_public_key_info()); + + case BOTAN_X509_TBS_DATA_BITS: + case BOTAN_X509_SIGNATURE_SCHEME_BITS: + case BOTAN_X509_SIGNATURE_BITS: + case BOTAN_X509_DER_ENCODING: + return botan_x509_object_view_value(c, value_type, index, ctx, view_fn); + + case BOTAN_X509_PEM_ENCODING: + case BOTAN_X509_CRL_DISTRIBUTION_URLS: + case BOTAN_X509_OCSP_RESPONDER_URLS: + case BOTAN_X509_CA_ISSUERS_URLS: + return BOTAN_FFI_ERROR_NO_VALUE; + } + + return BOTAN_FFI_ERROR_BAD_PARAMETER; + }); +#else + BOTAN_UNUSED(cert, value_type, index, ctx, view_fn); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_cert_view_binary_values_count(botan_x509_cert_t cert, botan_x509_value_type value_type, size_t* count) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return enumerator_count_values(count, [=](size_t index) { + return botan_x509_cert_view_binary_values( + cert, value_type, index, nullptr, [](auto, auto, auto) -> int { return BOTAN_FFI_SUCCESS; }); + }); +#else + BOTAN_UNUSED(cert, value_type, count); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_cert_view_string_values(botan_x509_cert_t cert, + botan_x509_value_type value_type, + size_t index, + botan_view_ctx ctx, + botan_view_str_fn view_fn) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + auto enumerate = [view_fn, ctx](auto values, size_t idx) -> int { + if(idx >= values.size()) { + return BOTAN_FFI_ERROR_OUT_OF_RANGE; + } else { + return invoke_view_callback(view_fn, ctx, values[idx]); + } + }; + + auto enumerate_crl_distribution_points = [view_fn, ctx](const Botan::X509_Certificate& c, size_t idx) -> int { + const auto* crl_dp_ext = + c.v3_extensions().get_extension_object_as(); + if(crl_dp_ext == nullptr) { + return BOTAN_FFI_ERROR_OUT_OF_RANGE; // essentially an empty list + } + + const auto& dps = crl_dp_ext->distribution_points(); + for(size_t i = idx; const auto& dp : dps) { + const auto& uris = dp.point().uris(); + if(i >= uris.size()) { + i -= uris.size(); + continue; + } + + auto itr = uris.begin(); + std::advance(itr, i); + return invoke_view_callback(view_fn, ctx, *itr); + } + + return BOTAN_FFI_ERROR_OUT_OF_RANGE; + }; + + return BOTAN_FFI_VISIT(cert, [=](const Botan::X509_Certificate& c) -> int { + switch(value_type) { + case BOTAN_X509_CRL_DISTRIBUTION_URLS: + return enumerate_crl_distribution_points(c, index); + case BOTAN_X509_OCSP_RESPONDER_URLS: + return enumerate(c.ocsp_responders(), index); + case BOTAN_X509_CA_ISSUERS_URLS: + return enumerate(c.ca_issuers(), index); + case BOTAN_X509_PEM_ENCODING: + return botan_x509_object_view_value(c, value_type, index, ctx, view_fn); + + case BOTAN_X509_SERIAL_NUMBER: + case BOTAN_X509_SUBJECT_DN_BITS: + case BOTAN_X509_ISSUER_DN_BITS: + case BOTAN_X509_SUBJECT_KEY_IDENTIFIER: + case BOTAN_X509_AUTHORITY_KEY_IDENTIFIER: + case BOTAN_X509_PUBLIC_KEY_PKCS8_BITS: + case BOTAN_X509_TBS_DATA_BITS: + case BOTAN_X509_SIGNATURE_SCHEME_BITS: + case BOTAN_X509_SIGNATURE_BITS: + case BOTAN_X509_DER_ENCODING: + return BOTAN_FFI_ERROR_NO_VALUE; + } + + return BOTAN_FFI_ERROR_BAD_PARAMETER; + }); +#else + BOTAN_UNUSED(cert, value_type, index, ctx, view_fn); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_cert_view_string_values_count(botan_x509_cert_t cert, botan_x509_value_type value_type, size_t* count) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return enumerator_count_values(count, [=](size_t index) { + return botan_x509_cert_view_string_values( + cert, value_type, index, nullptr, [](auto, auto, auto) -> int { return BOTAN_FFI_SUCCESS; }); + }); +#else + BOTAN_UNUSED(cert, value_type, count); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_cert_is_ca(botan_x509_cert_t cert) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT(cert, [=](const auto& c) { return c.is_CA_cert() ? 1 : 0; }); +#else + BOTAN_UNUSED(cert); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_cert_get_path_length_constraint(botan_x509_cert_t cert, size_t* path_limit) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT(cert, [=](const auto& c) -> int { + if(Botan::any_null_pointers(path_limit)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + if(const auto path_len = c.path_length_constraint()) { + *path_limit = path_len.value(); + return BOTAN_FFI_SUCCESS; + } else { + return BOTAN_FFI_ERROR_NO_VALUE; + } + }); +#else + BOTAN_UNUSED(cert, path_limit); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} int botan_x509_cert_get_public_key(botan_x509_cert_t cert, botan_pubkey_t* key) { if(key == nullptr) { @@ -92,8 +470,7 @@ #if defined(BOTAN_HAS_X509_CERTIFICATES) return ffi_guard_thunk(__func__, [=]() -> int { auto public_key = safe_get(cert).subject_public_key(); - *key = new botan_pubkey_struct(std::move(public_key)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(public_key)); }); #else BOTAN_UNUSED(cert); @@ -103,13 +480,17 @@ int botan_x509_cert_get_issuer_dn( botan_x509_cert_t cert, const char* key, size_t index, uint8_t out[], size_t* out_len) { + if(key == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } #if defined(BOTAN_HAS_X509_CERTIFICATES) return BOTAN_FFI_VISIT(cert, [=](const auto& c) -> int { auto issuer_info = c.issuer_info(key); if(index < issuer_info.size()) { - return write_str_output(out, out_len, c.issuer_info(key).at(index)); + // TODO(Botan4) change the type of out and remove this cast + return write_str_output(reinterpret_cast(out), out_len, c.issuer_info(key).at(index)); } else { - return BOTAN_FFI_ERROR_BAD_PARAMETER; + return BOTAN_FFI_ERROR_BAD_PARAMETER; // TODO(Botan4): use BOTAN_FFI_ERROR_OUT_OF_RANGE } }); #else @@ -118,15 +499,35 @@ #endif } +int botan_x509_cert_get_issuer_dn_count(botan_x509_cert_t cert, const char* key, size_t* count) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT(cert, [=](const auto& c) -> int { + if(Botan::any_null_pointers(count)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + *count = c.issuer_info(key).size(); + return BOTAN_FFI_SUCCESS; + }); +#else + BOTAN_UNUSED(cert, key, count); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + int botan_x509_cert_get_subject_dn( botan_x509_cert_t cert, const char* key, size_t index, uint8_t out[], size_t* out_len) { + if(key == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } #if defined(BOTAN_HAS_X509_CERTIFICATES) return BOTAN_FFI_VISIT(cert, [=](const auto& c) -> int { auto subject_info = c.subject_info(key); if(index < subject_info.size()) { - return write_str_output(out, out_len, c.subject_info(key).at(index)); + // TODO(Botan4) change the type of out and remove this cast + return write_str_output(reinterpret_cast(out), out_len, c.subject_info(key).at(index)); } else { - return BOTAN_FFI_ERROR_BAD_PARAMETER; + return BOTAN_FFI_ERROR_BAD_PARAMETER; // TODO(Botan4): use BOTAN_FFI_ERROR_OUT_OF_RANGE } }); #else @@ -135,6 +536,22 @@ #endif } +int botan_x509_cert_get_subject_dn_count(botan_x509_cert_t cert, const char* key, size_t* count) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT(cert, [=](const auto& c) -> int { + if(Botan::any_null_pointers(count)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + *count = c.subject_info(key).size(); + return BOTAN_FFI_SUCCESS; + }); +#else + BOTAN_UNUSED(cert, key, count); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + int botan_x509_cert_to_string(botan_x509_cert_t cert, char out[], size_t* out_len) { return copy_view_str(reinterpret_cast(out), out_len, botan_x509_cert_view_as_string, cert); } @@ -163,6 +580,30 @@ #endif } +int botan_x509_cert_allowed_extended_usage_str(botan_x509_cert_t cert, const char* oid) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT(cert, [=](const auto& c) -> int { + if(Botan::any_null_pointers(oid)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + return c.has_ex_constraint(oid) ? 1 : 0; + }); +#else + BOTAN_UNUSED(cert, oid); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_cert_allowed_extended_usage_oid(botan_x509_cert_t cert, botan_asn1_oid_t oid) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT(cert, [=](const auto& c) -> int { return c.has_ex_constraint(safe_get(oid)) ? 1 : 0; }); +#else + BOTAN_UNUSED(cert, oid); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + int botan_x509_cert_destroy(botan_x509_cert_t cert) { #if defined(BOTAN_HAS_X509_CERTIFICATES) return BOTAN_FFI_CHECKED_DELETE(cert); @@ -193,6 +634,9 @@ } int botan_x509_cert_not_before(botan_x509_cert_t cert, uint64_t* time_since_epoch) { + if(time_since_epoch == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } #if defined(BOTAN_HAS_X509_CERTIFICATES) return BOTAN_FFI_VISIT(cert, [=](const auto& c) { *time_since_epoch = c.not_before().time_since_epoch(); }); #else @@ -202,6 +646,9 @@ } int botan_x509_cert_not_after(botan_x509_cert_t cert, uint64_t* time_since_epoch) { + if(time_since_epoch == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } #if defined(BOTAN_HAS_X509_CERTIFICATES) return BOTAN_FFI_VISIT(cert, [=](const auto& c) { *time_since_epoch = c.not_after().time_since_epoch(); }); #else @@ -219,9 +666,32 @@ #endif } +int botan_x509_cert_serial_number(botan_x509_cert_t cert, botan_mp_t* serial_number) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT(cert, [=](const Botan::X509_Certificate& c) { + if(Botan::any_null_pointers(serial_number)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + auto serial_bn = Botan::BigInt::from_bytes(c.serial_number()); + return ffi_new_object(serial_number, std::make_unique(std::move(serial_bn))); + }); +#else + BOTAN_UNUSED(cert, serial_number); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + int botan_x509_cert_get_fingerprint(botan_x509_cert_t cert, const char* hash, uint8_t out[], size_t* out_len) { + if(hash == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } #if defined(BOTAN_HAS_X509_CERTIFICATES) - return BOTAN_FFI_VISIT(cert, [=](const auto& c) { return write_str_output(out, out_len, c.fingerprint(hash)); }); + // TODO(Botan4) change the type of out and remove this cast + + return BOTAN_FFI_VISIT(cert, [=](const auto& c) { + return write_str_output(reinterpret_cast(out), out_len, c.fingerprint(hash)); + }); #else BOTAN_UNUSED(cert, hash, out, out_len); return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; @@ -260,6 +730,233 @@ #endif } +int botan_x509_general_name_get_type(botan_x509_general_name_t name, unsigned int* type) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT(name, [=](const Botan::GeneralName& n) { + if(Botan::any_null_pointers(type)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + const auto mapped_type = to_botan_x509_general_name_types(n.type_code()); + if(!mapped_type.has_value()) { + return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE; + } + + *type = mapped_type.value(); + if(*type == BOTAN_X509_OTHER_NAME /* ... viewing of other-names not supported */) { + return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE; + } + + return BOTAN_FFI_SUCCESS; + }); +#else + BOTAN_UNUSED(name, type); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_general_name_view_string_value(botan_x509_general_name_t name, + botan_view_ctx ctx, + botan_view_str_fn view) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT(name, [=](const Botan::GeneralName& n) -> int { + const auto type = to_botan_x509_general_name_types(n.type_code()); + if(!type) { + return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE; + } + + if(type != BOTAN_X509_EMAIL_ADDRESS && type != BOTAN_X509_DNS_NAME && type != BOTAN_X509_URI && + type != BOTAN_X509_IP_ADDRESS) { + return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE; + } + + return invoke_view_callback(view, ctx, n.name()); + }); +#else + BOTAN_UNUSED(name, ctx, view); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_general_name_view_binary_value(botan_x509_general_name_t name, + botan_view_ctx ctx, + botan_view_bin_fn view) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT(name, [=](const Botan::GeneralName& n) -> int { + const auto type = to_botan_x509_general_name_types(n.type_code()); + if(!type) { + return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE; + } + + if(type != BOTAN_X509_DIRECTORY_NAME && type != BOTAN_X509_IP_ADDRESS) { + return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE; + } + + return invoke_view_callback(view, ctx, n.binary_name()); + }); +#else + BOTAN_UNUSED(name, ctx, view); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_general_name_destroy(botan_x509_general_name_t name) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_CHECKED_DELETE(name); +#else + BOTAN_UNUSED(name); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_cert_permitted_name_constraints(botan_x509_cert_t cert, + size_t index, + botan_x509_general_name_t* constraint) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT(cert, [=](const Botan::X509_Certificate& c) { + if(Botan::any_null_pointers(constraint)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + const auto& constraints = c.name_constraints().permitted(); + if(index >= constraints.size()) { + return BOTAN_FFI_ERROR_OUT_OF_RANGE; + } + + return ffi_new_object(constraint, std::make_unique(constraints[index].base())); + }); +#else + BOTAN_UNUSED(cert, index, constraint); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_cert_permitted_name_constraints_count(botan_x509_cert_t cert, size_t* count) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + if(Botan::any_null_pointers(count)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + return BOTAN_FFI_VISIT(cert, [=](const auto& c) { *count = c.name_constraints().permitted().size(); }); +#else + BOTAN_UNUSED(cert, count); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_cert_excluded_name_constraints(botan_x509_cert_t cert, + size_t index, + botan_x509_general_name_t* constraint) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT(cert, [=](const Botan::X509_Certificate& c) { + if(Botan::any_null_pointers(constraint)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + const auto& constraints = c.name_constraints().excluded(); + if(index >= constraints.size()) { + return BOTAN_FFI_ERROR_OUT_OF_RANGE; + } + + return ffi_new_object(constraint, std::make_unique(constraints[index].base())); + }); +#else + BOTAN_UNUSED(cert, index, constraint); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_cert_excluded_name_constraints_count(botan_x509_cert_t cert, size_t* count) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + if(Botan::any_null_pointers(count)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + return BOTAN_FFI_VISIT(cert, [=](const auto& c) { *count = c.name_constraints().excluded().size(); }); +#else + BOTAN_UNUSED(cert, count); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_cert_subject_alternative_names(botan_x509_cert_t cert, + size_t index, + botan_x509_general_name_t* alt_name) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT(cert, [=](const Botan::X509_Certificate& c) { + if(Botan::any_null_pointers(alt_name)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + if(!c.v3_extensions().extension_set(Botan::OID::from_string("X509v3.SubjectAlternativeName"))) { + return BOTAN_FFI_ERROR_NO_VALUE; + } + + if(auto name = extract_general_name_at(c.subject_alt_name(), index)) { + return ffi_new_object(alt_name, std::make_unique(std::move(name).value())); + } + + return BOTAN_FFI_ERROR_OUT_OF_RANGE; + }); +#else + BOTAN_UNUSED(cert, index, alt_name); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_cert_subject_alternative_names_count(botan_x509_cert_t cert, size_t* count) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + if(Botan::any_null_pointers(count)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + return BOTAN_FFI_VISIT( + cert, [=](const Botan::X509_Certificate& c) { *count = count_general_names_in(c.subject_alt_name()); }); +#else + BOTAN_UNUSED(cert, count); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_cert_issuer_alternative_names(botan_x509_cert_t cert, + size_t index, + botan_x509_general_name_t* alt_name) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT(cert, [=](const Botan::X509_Certificate& c) { + if(Botan::any_null_pointers(alt_name)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + if(!c.v3_extensions().extension_set(Botan::OID::from_string("X509v3.IssuerAlternativeName"))) { + return BOTAN_FFI_ERROR_NO_VALUE; + } + + if(auto name = extract_general_name_at(c.issuer_alt_name(), index)) { + return ffi_new_object(alt_name, std::make_unique(std::move(name).value())); + } + + return BOTAN_FFI_ERROR_OUT_OF_RANGE; + }); +#else + BOTAN_UNUSED(cert, index, alt_name); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_cert_issuer_alternative_names_count(botan_x509_cert_t cert, size_t* count) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + if(Botan::any_null_pointers(count)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + return BOTAN_FFI_VISIT( + cert, [=](const Botan::X509_Certificate& c) { *count = count_general_names_in(c.issuer_alt_name()); }); +#else + BOTAN_UNUSED(cert, count); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + int botan_x509_cert_hostname_match(botan_x509_cert_t cert, const char* hostname) { if(hostname == nullptr) { return BOTAN_FFI_ERROR_NULL_POINTER; @@ -305,7 +1002,7 @@ std::unique_ptr trusted_extra; std::vector trusted_roots; - if(trusted_path && *trusted_path) { + if(trusted_path != nullptr && *trusted_path != 0) { trusted_from_path = std::make_unique(trusted_path); trusted_roots.push_back(trusted_from_path.get()); } @@ -318,12 +1015,12 @@ trusted_roots.push_back(trusted_extra.get()); } - Botan::Path_Validation_Restrictions restrictions(false, required_strength); + const Botan::Path_Validation_Restrictions restrictions(false, required_strength); auto validation_result = Botan::x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, validation_time); - if(result_code) { + if(result_code != nullptr) { *result_code = static_cast(validation_result.result()); } @@ -346,21 +1043,15 @@ } #if defined(BOTAN_HAS_X509_CERTIFICATES) - Botan::Certificate_Status_Code sc = static_cast(code); + const Botan::Certificate_Status_Code sc = static_cast(code); return Botan::to_string(sc); #else return nullptr; #endif } -#if defined(BOTAN_HAS_X509_CERTIFICATES) - -BOTAN_FFI_DECLARE_STRUCT(botan_x509_crl_struct, Botan::X509_CRL, 0x2C628910); - -#endif - int botan_x509_crl_load_file(botan_x509_crl_t* crl_obj, const char* crl_path) { - if(!crl_obj || !crl_path) { + if(crl_obj == nullptr || crl_path == nullptr) { return BOTAN_FFI_ERROR_NULL_POINTER; } @@ -368,8 +1059,7 @@ return ffi_guard_thunk(__func__, [=]() -> int { auto c = std::make_unique(crl_path); - *crl_obj = new botan_x509_crl_struct(std::move(c)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(crl_obj, std::move(c)); }); #else @@ -378,7 +1068,7 @@ } int botan_x509_crl_load(botan_x509_crl_t* crl_obj, const uint8_t crl_bits[], size_t crl_bits_len) { - if(!crl_obj || !crl_bits) { + if(crl_obj == nullptr || crl_bits == nullptr) { return BOTAN_FFI_ERROR_NULL_POINTER; } @@ -386,8 +1076,7 @@ return ffi_guard_thunk(__func__, [=]() -> int { Botan::DataSource_Memory bits(crl_bits, crl_bits_len); auto c = std::make_unique(bits); - *crl_obj = new botan_x509_crl_struct(std::move(c)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(crl_obj, std::move(c)); }); #else BOTAN_UNUSED(crl_bits_len); @@ -395,6 +1084,132 @@ #endif } +int botan_x509_crl_this_update(botan_x509_crl_t crl, uint64_t* time_since_epoch) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT(crl, [=](const auto& c) { + if(Botan::any_null_pointers(time_since_epoch)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + *time_since_epoch = c.this_update().time_since_epoch(); + return BOTAN_FFI_SUCCESS; + }); +#else + BOTAN_UNUSED(crl, time_since_epoch); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_crl_next_update(botan_x509_crl_t crl, uint64_t* time_since_epoch) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT(crl, [=](const auto& c) { + const auto& time = c.next_update(); + if(!time.time_is_set()) { + return BOTAN_FFI_ERROR_NO_VALUE; + } + + if(Botan::any_null_pointers(time_since_epoch)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + *time_since_epoch = c.next_update().time_since_epoch(); + return BOTAN_FFI_SUCCESS; + }); +#else + BOTAN_UNUSED(crl, time_since_epoch); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_crl_create(botan_x509_crl_t* crl_obj, + botan_rng_t rng, + botan_x509_cert_t ca_cert, + botan_privkey_t ca_key, + uint64_t issue_time, + uint32_t next_update, + const char* hash_fn, + const char* padding) { + if(Botan::any_null_pointers(crl_obj)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return ffi_guard_thunk(__func__, [=]() -> int { + auto& rng_ = safe_get(rng); + auto ca = Botan::X509_CA( + safe_get(ca_cert), safe_get(ca_key), default_from_ptr(hash_fn), default_from_ptr(padding), rng_); + auto crl = std::make_unique( + ca.new_crl(rng_, timepoint_from_timestamp(issue_time), std::chrono::seconds(next_update))); + return ffi_new_object(crl_obj, std::move(crl)); + }); +#else + BOTAN_UNUSED(rng, ca_cert, ca_key, hash_fn, padding, issue_time, next_update); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_crl_entry_create(botan_x509_crl_entry_t* entry, botan_x509_cert_t cert, int reason_code) { + if(Botan::any_null_pointers(entry)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return ffi_guard_thunk(__func__, [=]() -> int { + return ffi_new_object( + entry, std::make_unique(safe_get(cert), static_cast(reason_code))); + }); +#else + BOTAN_UNUSED(cert, reason_code); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_crl_update(botan_x509_crl_t* crl_obj, + botan_x509_crl_t last_crl, + botan_rng_t rng, + botan_x509_cert_t ca_cert, + botan_privkey_t ca_key, + uint64_t issue_time, + uint32_t next_update, + const botan_x509_crl_entry_t* new_entries, + size_t new_entries_len, + const char* hash_fn, + const char* padding) { + if(Botan::any_null_pointers(crl_obj)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + if(new_entries_len > 0 && Botan::any_null_pointers(new_entries)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return ffi_guard_thunk(__func__, [=]() -> int { + auto& rng_ = safe_get(rng); + auto ca = Botan::X509_CA( + safe_get(ca_cert), safe_get(ca_key), default_from_ptr(hash_fn), default_from_ptr(padding), rng_); + + std::vector entries; + entries.reserve(new_entries_len); + for(size_t i = 0; i < new_entries_len; i++) { + entries.push_back(safe_get(new_entries[i])); + } + + auto crl = std::make_unique(ca.update_crl( + safe_get(last_crl), entries, rng_, timepoint_from_timestamp(issue_time), std::chrono::seconds(next_update))); + return ffi_new_object(crl_obj, std::move(crl)); + }); +#else + BOTAN_UNUSED( + last_crl, rng, ca_cert, ca_key, hash_fn, padding, issue_time, next_update, new_entries, new_entries_len); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_crl_verify_signature(botan_x509_crl_t crl, botan_pubkey_t key) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT(crl, [=](const auto& c) -> int { return c.check_signature(safe_get(key)) ? 1 : 0; }); +#else + BOTAN_UNUSED(crl, key); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + int botan_x509_crl_destroy(botan_x509_crl_t crl) { #if defined(BOTAN_HAS_X509_CERTIFICATES) return BOTAN_FFI_CHECKED_DELETE(crl); @@ -404,6 +1219,117 @@ #endif } +int botan_x509_crl_view_binary_values(botan_x509_crl_t crl_obj, + botan_x509_value_type value_type, + size_t index, + botan_view_ctx ctx, + botan_view_bin_fn view_fn) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + if(index != 0) { + // As of now there are no multi-value binary entries. + return BOTAN_FFI_ERROR_OUT_OF_RANGE; + } + + auto view = [=](std::span value) -> int { + if(value.empty()) { + return BOTAN_FFI_ERROR_NO_VALUE; + } else { + return invoke_view_callback(view_fn, ctx, value); + } + }; + + return BOTAN_FFI_VISIT(crl_obj, [=](const Botan::X509_CRL& crl) -> int { + switch(value_type) { + case BOTAN_X509_SERIAL_NUMBER: + return view(Botan::store_be(crl.crl_number())); + case BOTAN_X509_ISSUER_DN_BITS: + return view(Botan::ASN1::put_in_sequence(crl.issuer_dn().get_bits())); + case BOTAN_X509_AUTHORITY_KEY_IDENTIFIER: + return view(crl.authority_key_id()); + + case BOTAN_X509_TBS_DATA_BITS: + case BOTAN_X509_SIGNATURE_SCHEME_BITS: + case BOTAN_X509_SIGNATURE_BITS: + case BOTAN_X509_DER_ENCODING: + return botan_x509_object_view_value(crl, value_type, index, ctx, view_fn); + + case BOTAN_X509_SUBJECT_DN_BITS: + case BOTAN_X509_SUBJECT_KEY_IDENTIFIER: + case BOTAN_X509_PUBLIC_KEY_PKCS8_BITS: + case BOTAN_X509_PEM_ENCODING: + case BOTAN_X509_CRL_DISTRIBUTION_URLS: + case BOTAN_X509_OCSP_RESPONDER_URLS: + case BOTAN_X509_CA_ISSUERS_URLS: + return BOTAN_FFI_ERROR_NO_VALUE; + } + + return BOTAN_FFI_ERROR_BAD_PARAMETER; + }); +#else + BOTAN_UNUSED(crl_obj, value_type, index, ctx, view_fn); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_crl_view_binary_values_count(botan_x509_crl_t crl_obj, botan_x509_value_type value_type, size_t* count) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return enumerator_count_values(count, [=](size_t index) { + return botan_x509_crl_view_binary_values( + crl_obj, value_type, index, nullptr, [](auto, auto, auto) -> int { return BOTAN_FFI_SUCCESS; }); + }); +#else + BOTAN_UNUSED(crl_obj, value_type, count); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_crl_view_string_values(botan_x509_crl_t crl_obj, + botan_x509_value_type value_type, + size_t index, + botan_view_ctx ctx, + botan_view_str_fn view) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT(crl_obj, [=](const Botan::X509_CRL& crl) -> int { + switch(value_type) { + case BOTAN_X509_PEM_ENCODING: + return botan_x509_object_view_value(crl, value_type, index, ctx, view); + + case BOTAN_X509_SERIAL_NUMBER: + case BOTAN_X509_SUBJECT_DN_BITS: + case BOTAN_X509_ISSUER_DN_BITS: + case BOTAN_X509_SUBJECT_KEY_IDENTIFIER: + case BOTAN_X509_AUTHORITY_KEY_IDENTIFIER: + case BOTAN_X509_PUBLIC_KEY_PKCS8_BITS: + case BOTAN_X509_TBS_DATA_BITS: + case BOTAN_X509_SIGNATURE_SCHEME_BITS: + case BOTAN_X509_SIGNATURE_BITS: + case BOTAN_X509_DER_ENCODING: + case BOTAN_X509_CRL_DISTRIBUTION_URLS: + case BOTAN_X509_OCSP_RESPONDER_URLS: + case BOTAN_X509_CA_ISSUERS_URLS: + return BOTAN_FFI_ERROR_NO_VALUE; + } + + return BOTAN_FFI_ERROR_BAD_PARAMETER; + }); +#else + BOTAN_UNUSED(crl_obj, value_type, index, ctx, view); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_crl_view_string_values_count(botan_x509_crl_t crl_obj, botan_x509_value_type value_type, size_t* count) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return enumerator_count_values(count, [=](size_t index) { + return botan_x509_crl_view_string_values( + crl_obj, value_type, index, nullptr, [](auto, auto, auto) -> int { return BOTAN_FFI_SUCCESS; }); + }); +#else + BOTAN_UNUSED(crl_obj, value_type, count); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + int botan_x509_is_revoked(botan_x509_crl_t crl, botan_x509_cert_t cert) { #if defined(BOTAN_HAS_X509_CERTIFICATES) return BOTAN_FFI_VISIT(crl, [=](const auto& c) { return c.is_revoked(safe_get(cert)) ? 0 : -1; }); @@ -414,6 +1340,106 @@ #endif } +int botan_x509_crl_entries(botan_x509_crl_t crl, size_t index, botan_x509_crl_entry_t* entry) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT(crl, [=](const Botan::X509_CRL& c) -> int { + const auto& entries = c.get_revoked(); + if(index >= entries.size()) { + return BOTAN_FFI_ERROR_OUT_OF_RANGE; + } + + if(Botan::any_null_pointers(entry)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + return ffi_new_object(entry, std::make_unique(entries[index])); + }); +#else + BOTAN_UNUSED(crl, index, entry); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_crl_entries_count(botan_x509_crl_t crl, size_t* count) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + if(Botan::any_null_pointers(count)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + return BOTAN_FFI_VISIT(crl, [=](const Botan::X509_CRL& c) { *count = c.get_revoked().size(); }); +#else + BOTAN_UNUSED(crl, count); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_crl_entry_destroy(botan_x509_crl_entry_t entry) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_CHECKED_DELETE(entry); +#else + BOTAN_UNUSED(entry); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_crl_entry_reason(botan_x509_crl_entry_t entry, int* reason_code) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT(entry, [=](const Botan::CRL_Entry& e) { + if(Botan::any_null_pointers(reason_code)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + *reason_code = static_cast(e.reason_code()); + return BOTAN_FFI_SUCCESS; + }); +#else + BOTAN_UNUSED(entry, reason_code); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_crl_entry_serial_number(botan_x509_crl_entry_t entry, botan_mp_t* serial_number) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT(entry, [=](const Botan::CRL_Entry& e) { + if(Botan::any_null_pointers(serial_number)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + auto serial_bn = Botan::BigInt::from_bytes(e.serial_number()); + return ffi_new_object(serial_number, std::make_unique(std::move(serial_bn))); + }); +#else + BOTAN_UNUSED(entry, serial_number); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_crl_entry_view_serial_number(botan_x509_crl_entry_t entry, botan_view_ctx ctx, botan_view_bin_fn view) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT( + entry, [=](const Botan::CRL_Entry& e) { return invoke_view_callback(view, ctx, e.serial_number()); }); +#else + BOTAN_UNUSED(entry, ctx, view); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_x509_crl_entry_revocation_date(botan_x509_crl_entry_t entry, uint64_t* time_since_epoch) { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + return BOTAN_FFI_VISIT(entry, [=](const Botan::CRL_Entry& e) { + if(Botan::any_null_pointers(time_since_epoch)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + *time_since_epoch = e.expire_time().time_since_epoch(); + return BOTAN_FFI_SUCCESS; + }); +#else + BOTAN_UNUSED(entry, time_since_epoch); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + int botan_x509_cert_verify_with_crl(int* result_code, botan_x509_cert_t cert, const botan_x509_cert_t* intermediates, @@ -449,7 +1475,7 @@ std::unique_ptr trusted_crls; std::vector trusted_roots; - if(trusted_path && *trusted_path) { + if(trusted_path != nullptr && *trusted_path != 0) { trusted_from_path = std::make_unique(trusted_path); trusted_roots.push_back(trusted_from_path.get()); } @@ -470,12 +1496,12 @@ trusted_roots.push_back(trusted_crls.get()); } - Botan::Path_Validation_Restrictions restrictions(false, required_strength); + const Botan::Path_Validation_Restrictions restrictions(false, required_strength); auto validation_result = Botan::x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, validation_time); - if(result_code) { + if(result_code != nullptr) { *result_code = static_cast(validation_result.result()); } diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_cert.h botan3-3.12.0+dfsg/src/lib/ffi/ffi_cert.h --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_cert.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_cert.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,31 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_FFI_CERT_H_ +#define BOTAN_FFI_CERT_H_ + +#include + +#if defined(BOTAN_HAS_X509_CERTIFICATES) + #include + #include + #include + #include + #include +#endif + +extern "C" { +#if defined(BOTAN_HAS_X509_CERTIFICATES) + +BOTAN_FFI_DECLARE_STRUCT(botan_x509_cert_struct, Botan::X509_Certificate, 0x8F628937); +BOTAN_FFI_DECLARE_STRUCT(botan_x509_crl_struct, Botan::X509_CRL, 0x2C628910); +BOTAN_FFI_DECLARE_STRUCT(botan_x509_crl_entry_struct, Botan::CRL_Entry, 0x4EAA5346); +BOTAN_FFI_DECLARE_STRUCT(botan_x509_general_name_struct, Botan::GeneralName, 0x563654FD); + +#endif +} + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_cipher.cpp botan3-3.12.0+dfsg/src/lib/ffi/ffi_cipher.cpp --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_cipher.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_cipher.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,9 +7,12 @@ #include #include +#include #include +#include +#include #include -#include +#include #include @@ -95,6 +98,9 @@ int botan_cipher_init(botan_cipher_t* cipher, const char* cipher_name, uint32_t flags) { return ffi_guard_thunk(__func__, [=]() -> int { + if(any_null_pointers(cipher, cipher_name)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } const bool encrypt_p = ((flags & BOTAN_CIPHER_INIT_FLAG_MASK_DIRECTION) == BOTAN_CIPHER_INIT_FLAG_ENCRYPT); const Botan::Cipher_Dir dir = encrypt_p ? Botan::Cipher_Dir::Encryption : Botan::Cipher_Dir::Decryption; @@ -106,8 +112,7 @@ const size_t update_size = ffi_choose_update_size(*mode); const size_t ideal_update_size = std::max(mode->ideal_granularity(), update_size); - *cipher = new botan_cipher_struct(std::move(mode), update_size, ideal_update_size); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(cipher, std::move(mode), update_size, ideal_update_size); }); } @@ -116,11 +121,17 @@ } int botan_cipher_clear(botan_cipher_t cipher) { - return BOTAN_FFI_VISIT(cipher, [](auto& c) { c.clear(); }); + return BOTAN_FFI_VISIT(cipher, [=](auto& c) { + cipher->buf().clear(); + c.clear(); + }); } int botan_cipher_reset(botan_cipher_t cipher) { - return BOTAN_FFI_VISIT(cipher, [](auto& c) { c.reset(); }); + return BOTAN_FFI_VISIT(cipher, [=](auto& c) { + cipher->buf().clear(); + c.reset(); + }); } int botan_cipher_output_length(botan_cipher_t cipher, size_t in_len, size_t* out_len) { @@ -133,8 +144,12 @@ int botan_cipher_query_keylen(botan_cipher_t cipher, size_t* out_minimum_keylength, size_t* out_maximum_keylength) { return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { - *out_minimum_keylength = c.key_spec().minimum_keylength(); - *out_maximum_keylength = c.key_spec().maximum_keylength(); + if(out_minimum_keylength) { + *out_minimum_keylength = c.key_spec().minimum_keylength(); + } + if(out_maximum_keylength) { + *out_maximum_keylength = c.key_spec().maximum_keylength(); + } }); } @@ -156,6 +171,9 @@ } int botan_cipher_set_key(botan_cipher_t cipher, const uint8_t* key, size_t key_len) { + if(key_len > 0 && key == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return BOTAN_FFI_VISIT(cipher, [=](auto& c) { c.set_key(key, key_len); }); } @@ -175,6 +193,10 @@ const uint8_t input[], size_t input_size, size_t* input_consumed) { + if(any_null_pointers(output_written, input_consumed)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + return ffi_guard_thunk(__func__, [=]() -> int { using namespace Botan; Cipher_Mode& cipher = safe_get(cipher_obj); @@ -185,7 +207,7 @@ // called with the final flag set but not enough buffer space was provided // to accommodate the final output. const bool was_finished_before = !mbuf.empty(); - const bool final_input = (flags & BOTAN_CIPHER_UPDATE_FLAG_FINAL); + const bool final_input = (flags & BOTAN_CIPHER_UPDATE_FLAG_FINAL) != 0; // Bring the output variables into a defined state. *output_written = 0; @@ -300,18 +322,30 @@ } int botan_cipher_get_default_nonce_length(botan_cipher_t cipher, size_t* nl) { + if(nl == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { *nl = c.default_nonce_length(); }); } int botan_cipher_get_update_granularity(botan_cipher_t cipher, size_t* ug) { + if(ug == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return BOTAN_FFI_VISIT(cipher, [=](const auto& /*c*/) { *ug = cipher->update_size(); }); } int botan_cipher_get_ideal_update_granularity(botan_cipher_t cipher, size_t* ug) { + if(ug == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { *ug = c.ideal_granularity(); }); } int botan_cipher_get_tag_length(botan_cipher_t cipher, size_t* tl) { + if(tl == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { *tl = c.tag_size(); }); } diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_ec.cpp botan3-3.12.0+dfsg/src/lib/ffi/ffi_ec.cpp --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_ec.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_ec.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,355 @@ +/* +* (C) 2025 Jack Lloyd +* (C) 2025,2026 Dominik Schricker +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include +#include + +extern "C" { + +using namespace Botan_FFI; + +int botan_ec_group_destroy(botan_ec_group_t ec_group) { + return BOTAN_FFI_CHECKED_DELETE(ec_group); +} + +int botan_ec_group_supports_application_specific_group(int* out) { + if(out == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + if(Botan::EC_Group::supports_application_specific_group()) { + *out = 1; + } else { + *out = 0; + } + return BOTAN_FFI_SUCCESS; +} + +int botan_ec_group_supports_named_group(const char* name, int* out) { + return ffi_guard_thunk(__func__, [=]() -> int { + if(name == nullptr || out == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + if(Botan::EC_Group::supports_named_group(name)) { + *out = 1; + } else { + *out = 0; + } + return BOTAN_FFI_SUCCESS; + }); +} + +int botan_ec_group_from_params(botan_ec_group_t* ec_group, + botan_asn1_oid_t oid, + botan_mp_t p, + botan_mp_t a, + botan_mp_t b, + botan_mp_t base_x, + botan_mp_t base_y, + botan_mp_t order) { + return ffi_guard_thunk(__func__, [=]() -> int { + if(ec_group == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + Botan::EC_Group group( + safe_get(oid), safe_get(p), safe_get(a), safe_get(b), safe_get(base_x), safe_get(base_y), safe_get(order)); + + auto group_ptr = std::make_unique(std::move(group)); + return ffi_new_object(ec_group, std::move(group_ptr)); + }); +} + +int botan_ec_group_from_ber(botan_ec_group_t* ec_group, const uint8_t* ber, size_t ber_len) { + return ffi_guard_thunk(__func__, [=]() -> int { + if(ec_group == nullptr || ber == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + Botan::EC_Group group(ber, ber_len); + + auto group_ptr = std::make_unique(std::move(group)); + return ffi_new_object(ec_group, std::move(group_ptr)); + }); +} + +int botan_ec_group_from_pem(botan_ec_group_t* ec_group, const char* pem) { + return ffi_guard_thunk(__func__, [=]() -> int { + if(ec_group == nullptr || pem == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + Botan::EC_Group group = Botan::EC_Group::from_PEM(pem); + + auto group_ptr = std::make_unique(std::move(group)); + return ffi_new_object(ec_group, std::move(group_ptr)); + }); +} + +int botan_ec_group_from_oid(botan_ec_group_t* ec_group, botan_asn1_oid_t oid) { + return ffi_guard_thunk(__func__, [=]() -> int { + if(ec_group == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + Botan::EC_Group group = Botan::EC_Group::from_OID(safe_get(oid)); + + auto group_ptr = std::make_unique(std::move(group)); + return ffi_new_object(ec_group, std::move(group_ptr)); + }); +} + +int botan_ec_group_from_name(botan_ec_group_t* ec_group, const char* name) { + return ffi_guard_thunk(__func__, [=]() -> int { + if(ec_group == nullptr || name == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + Botan::EC_Group group = Botan::EC_Group::from_name(name); + + auto group_ptr = std::make_unique(std::move(group)); + return ffi_new_object(ec_group, std::move(group_ptr)); + }); +} + +int botan_ec_group_unregister(botan_asn1_oid_t oid) { + return BOTAN_FFI_VISIT(oid, [=](const auto& o) -> int { return Botan::EC_Group::unregister(o) ? 1 : 0; }); +} + +int botan_ec_group_view_der(botan_ec_group_t ec_group, botan_view_ctx ctx, botan_view_bin_fn view) { + return BOTAN_FFI_VISIT(ec_group, + [=](const auto& g) -> int { return invoke_view_callback(view, ctx, g.DER_encode()); }); +} + +int botan_ec_group_view_pem(botan_ec_group_t ec_group, botan_view_ctx ctx, botan_view_str_fn view) { + return BOTAN_FFI_VISIT(ec_group, [=](const auto& g) -> int { + return invoke_view_callback(view, ctx, g.PEM_encode(Botan::EC_Group_Encoding::NamedCurve)); + }); +} + +int botan_ec_group_get_curve_oid(botan_asn1_oid_t* oid, botan_ec_group_t ec_group) { + return BOTAN_FFI_VISIT(ec_group, [=](const auto& g) -> int { + if(oid == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + auto oid_ptr = std::make_unique(g.get_curve_oid()); + return ffi_new_object(oid, std::move(oid_ptr)); + }); +} + +namespace { +int botan_ec_group_get_component(botan_mp_t* out, + botan_ec_group_t ec_group, + const std::function& getter) { + return BOTAN_FFI_VISIT(ec_group, [=](const auto& g) -> int { + if(out == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + auto val = std::make_unique(getter(g)); + return ffi_new_object(out, std::move(val)); + }); +} +} // namespace + +int botan_ec_group_get_p(botan_mp_t* p, botan_ec_group_t ec_group) { + return botan_ec_group_get_component(p, ec_group, [](const auto& g) -> const Botan::BigInt& { return g.get_p(); }); +} + +int botan_ec_group_get_a(botan_mp_t* a, botan_ec_group_t ec_group) { + return botan_ec_group_get_component(a, ec_group, [](const auto& g) -> const Botan::BigInt& { return g.get_a(); }); +} + +int botan_ec_group_get_b(botan_mp_t* b, botan_ec_group_t ec_group) { + return botan_ec_group_get_component(b, ec_group, [](const auto& g) -> const Botan::BigInt& { return g.get_b(); }); +} + +int botan_ec_group_get_g_x(botan_mp_t* g_x, botan_ec_group_t ec_group) { + return botan_ec_group_get_component( + g_x, ec_group, [](const auto& g) -> const Botan::BigInt& { return g.get_g_x(); }); +} + +int botan_ec_group_get_g_y(botan_mp_t* g_y, botan_ec_group_t ec_group) { + return botan_ec_group_get_component( + g_y, ec_group, [](const auto& g) -> const Botan::BigInt& { return g.get_g_y(); }); +} + +int botan_ec_group_get_order(botan_mp_t* order, botan_ec_group_t ec_group) { + return botan_ec_group_get_component( + order, ec_group, [](const auto& g) -> const Botan::BigInt& { return g.get_order(); }); +} + +int botan_ec_group_equal(botan_ec_group_t curve1_w, botan_ec_group_t curve2_w) { + return BOTAN_FFI_VISIT(curve1_w, [=](const auto& curve1) -> int { return curve1 == safe_get(curve2_w); }); +} + +// ec scalars + +int botan_ec_scalar_destroy(botan_ec_scalar_t ec_scalar) { + return BOTAN_FFI_CHECKED_DELETE(ec_scalar); +} + +int botan_ec_scalar_random(botan_ec_scalar_t* ec_scalar, botan_ec_group_t ec_group, botan_rng_t rng) { + if(Botan::any_null_pointers(ec_scalar)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + return BOTAN_FFI_VISIT(ec_group, [=](const auto& g) -> int { + return ffi_new_object(ec_scalar, std::make_unique(Botan::EC_Scalar::random(g, safe_get(rng)))); + }); +} + +int botan_ec_scalar_from_mp(botan_ec_scalar_t* ec_scalar, botan_ec_group_t ec_group, botan_mp_t mp) { + if(Botan::any_null_pointers(ec_scalar)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + return BOTAN_FFI_VISIT(ec_group, [=](const auto& g) -> int { + return ffi_new_object(ec_scalar, + std::make_unique(Botan::EC_Scalar::from_bigint(g, safe_get(mp)))); + }); +} + +int botan_ec_scalar_to_mp(botan_ec_scalar_t ec_scalar, botan_mp_t* mp) { + if(Botan::any_null_pointers(mp)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + return BOTAN_FFI_VISIT(ec_scalar, [=](const auto& sc) -> int { + return ffi_new_object(mp, std::make_unique(sc.to_bigint())); + }); +} + +// ec points + +int botan_ec_point_destroy(botan_ec_point_t ec_point) { + return BOTAN_FFI_CHECKED_DELETE(ec_point); +} + +int botan_ec_point_identity(botan_ec_point_t* ec_point, botan_ec_group_t ec_group) { + if(Botan::any_null_pointers(ec_point)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + return BOTAN_FFI_VISIT(ec_group, [=](const auto& g) -> int { + return ffi_new_object(ec_point, std::make_unique(Botan::EC_AffinePoint::identity(g))); + }); +} + +int botan_ec_point_generator(botan_ec_point_t* ec_point, botan_ec_group_t ec_group) { + if(Botan::any_null_pointers(ec_point)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + return BOTAN_FFI_VISIT(ec_group, [=](const auto& g) -> int { + return ffi_new_object(ec_point, std::make_unique(Botan::EC_AffinePoint::generator(g))); + }); +} + +int botan_ec_point_from_xy(botan_ec_point_t* ec_point, botan_ec_group_t ec_group, botan_mp_t x, botan_mp_t y) { + if(Botan::any_null_pointers(ec_point)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + return ffi_guard_thunk(__func__, [=]() -> int { + std::optional pt = + Botan::EC_AffinePoint::from_bigint_xy(safe_get(ec_group), safe_get(x), safe_get(y)); + if(!pt.has_value()) { + return BOTAN_FFI_ERROR_BAD_PARAMETER; + } + + return ffi_new_object(ec_point, std::make_unique(pt.value())); + }); +} + +int botan_ec_point_from_bytes(botan_ec_point_t* ec_point, + botan_ec_group_t ec_group, + const uint8_t* bytes, + size_t bytes_len) { + if(Botan::any_null_pointers(ec_point, bytes)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + return BOTAN_FFI_VISIT(ec_group, [=](const auto& g) -> int { + Botan::EC_AffinePoint pt(g, std::span{bytes, bytes_len}); + return ffi_new_object(ec_point, std::make_unique(std::move(pt))); + }); +} + +int botan_ec_point_view_x_bytes(botan_ec_point_t ec_point, botan_view_ctx ctx, botan_view_bin_fn view) { + return BOTAN_FFI_VISIT(ec_point, [=](const auto& p) -> int { + auto bytes = p.x_bytes(); + return invoke_view_callback(view, ctx, bytes); + }); +} + +int botan_ec_point_view_y_bytes(botan_ec_point_t ec_point, botan_view_ctx ctx, botan_view_bin_fn view) { + return BOTAN_FFI_VISIT(ec_point, [=](const auto& p) -> int { + auto bytes = p.y_bytes(); + return invoke_view_callback(view, ctx, bytes); + }); +} + +int botan_ec_point_view_xy_bytes(botan_ec_point_t ec_point, botan_view_ctx ctx, botan_view_bin_fn view) { + return BOTAN_FFI_VISIT(ec_point, [=](const auto& p) -> int { + auto bytes = p.xy_bytes(); + return invoke_view_callback(view, ctx, bytes); + }); +} + +int botan_ec_point_view_uncompressed(botan_ec_point_t ec_point, botan_view_ctx ctx, botan_view_bin_fn view) { + return BOTAN_FFI_VISIT(ec_point, [=](const auto& p) -> int { + auto bytes = p.serialize_uncompressed(); + return invoke_view_callback(view, ctx, bytes); + }); +} + +int botan_ec_point_view_compressed(botan_ec_point_t ec_point, botan_view_ctx ctx, botan_view_bin_fn view) { + return BOTAN_FFI_VISIT(ec_point, [=](const auto& p) -> int { + auto bytes = p.serialize_compressed(); + return invoke_view_callback(view, ctx, bytes); + }); +} + +int botan_ec_point_is_identity(botan_ec_point_t ec_point) { + return BOTAN_FFI_VISIT(ec_point, [=](const auto& p) -> int { return p.is_identity() ? 1 : 0; }); +} + +int botan_ec_point_equal(botan_ec_point_t x_w, botan_ec_point_t y_w) { + return BOTAN_FFI_VISIT(x_w, [=](const auto& x) -> int { return x == safe_get(y_w) ? 1 : 0; }); +} + +int botan_ec_point_mul(botan_ec_point_t* result, + botan_ec_point_t ec_point, + botan_ec_scalar_t ec_scalar, + botan_rng_t rng) { + if(Botan::any_null_pointers(result)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + return BOTAN_FFI_VISIT(ec_point, [=](auto& pt) -> int { + Botan::EC_AffinePoint res = pt.mul(safe_get(ec_scalar), safe_get(rng)); + return ffi_new_object(result, std::make_unique(std::move(res))); + }); +} + +int botan_ec_point_negate(botan_ec_point_t* result, botan_ec_point_t ec_point) { + if(Botan::any_null_pointers(result)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + return BOTAN_FFI_VISIT(ec_point, [=](auto& pt) -> int { + Botan::EC_AffinePoint res = pt.negate(); + return ffi_new_object(result, std::make_unique(std::move(res))); + }); +} + +int botan_ec_point_add(botan_ec_point_t* result, botan_ec_point_t x_w, botan_ec_point_t y_w) { + if(Botan::any_null_pointers(result)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + return BOTAN_FFI_VISIT(x_w, [=](auto& x) -> int { + Botan::EC_AffinePoint res = x.add(safe_get(y_w)); + return ffi_new_object(result, std::make_unique(std::move(res))); + }); +} +} diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_ec.h botan3-3.12.0+dfsg/src/lib/ffi/ffi_ec.h --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_ec.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_ec.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,21 @@ +/* +* (C) 2025 Jack Lloyd +* (C) 2025,2026 Dominik Schricker +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_FFI_EC_H_ +#define BOTAN_FFI_EC_H_ + +#include +#include + +extern "C" { + +BOTAN_FFI_DECLARE_STRUCT(botan_ec_group_struct, Botan::EC_Group, 0xC5A5DB46); +BOTAN_FFI_DECLARE_STRUCT(botan_ec_scalar_struct, Botan::EC_Scalar, 0x504CC641); +BOTAN_FFI_DECLARE_STRUCT(botan_ec_point_struct, Botan::EC_AffinePoint, 0xE3DAD046); +} + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_fpe.cpp botan3-3.12.0+dfsg/src/lib/ffi/ffi_fpe.cpp --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_fpe.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_fpe.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,6 +6,7 @@ #include +#include #include #include #include @@ -38,14 +39,13 @@ return BOTAN_FFI_ERROR_BAD_FLAG; } - const bool compat_mode = (flags & BOTAN_FPE_FLAG_FE1_COMPAT_MODE); + const bool compat_mode = (flags & BOTAN_FPE_FLAG_FE1_COMPAT_MODE) != 0; - std::unique_ptr fpe_obj(new Botan::FPE_FE1(safe_get(n), rounds, compat_mode)); + auto fpe_obj = std::make_unique(safe_get(n), rounds, compat_mode); fpe_obj->set_key(key, key_len); - *fpe = new botan_fpe_struct(std::move(fpe_obj)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(fpe, std::move(fpe_obj)); }); #else BOTAN_UNUSED(fpe, n, key, key_len, rounds, flags); @@ -65,7 +65,7 @@ int botan_fpe_encrypt(botan_fpe_t fpe, botan_mp_t x, const uint8_t tweak[], size_t tweak_len) { #if defined(BOTAN_HAS_FPE_FE1) return ffi_guard_thunk(__func__, [=]() { - Botan::BigInt r = safe_get(fpe).encrypt(safe_get(x), tweak, tweak_len); + const Botan::BigInt r = safe_get(fpe).encrypt(safe_get(x), tweak, tweak_len); safe_get(x) = r; return BOTAN_FFI_SUCCESS; }); @@ -78,7 +78,7 @@ int botan_fpe_decrypt(botan_fpe_t fpe, botan_mp_t x, const uint8_t tweak[], size_t tweak_len) { #if defined(BOTAN_HAS_FPE_FE1) return ffi_guard_thunk(__func__, [=]() { - Botan::BigInt r = safe_get(fpe).decrypt(safe_get(x), tweak, tweak_len); + const Botan::BigInt r = safe_get(fpe).decrypt(safe_get(x), tweak, tweak_len); safe_get(x) = r; return BOTAN_FFI_SUCCESS; }); diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_hash.cpp botan3-3.12.0+dfsg/src/lib/ffi/ffi_hash.cpp --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_hash.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_hash.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -29,7 +29,7 @@ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; } - *hash = new botan_hash_struct(std::move(h)); + ffi_new_object(hash, std::move(h)); return BOTAN_FFI_SUCCESS; }); } @@ -75,8 +75,12 @@ return BOTAN_FFI_VISIT(hash, [=](auto& h) { h.final(out); }); } +// NOLINTNEXTLINE(misc-misplaced-const) int botan_hash_copy_state(botan_hash_t* dest, const botan_hash_t source) { - return BOTAN_FFI_VISIT(source, [=](const auto& src) { *dest = new botan_hash_struct(src.copy_state()); }); + if(dest == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + return BOTAN_FFI_VISIT(source, [=](const auto& src) { return ffi_new_object(dest, src.copy_state()); }); } int botan_hash_name(botan_hash_t hash, char* name, size_t* name_len) { diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_hotp.cpp botan3-3.12.0+dfsg/src/lib/ffi/ffi_hotp.cpp --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_hotp.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_hotp.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,6 +6,7 @@ #include +#include #include #if defined(BOTAN_HAS_HOTP) @@ -32,9 +33,7 @@ #if defined(BOTAN_HAS_HOTP) return ffi_guard_thunk(__func__, [=]() -> int { auto otp = std::make_unique(key, key_len, hash_algo, digits); - *hotp = new botan_hotp_struct(std::move(otp)); - - return BOTAN_FFI_SUCCESS; + return ffi_new_object(hotp, std::move(otp)); }); #else BOTAN_UNUSED(hotp, key, key_len, hash_algo, digits); diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_kdf.cpp botan3-3.12.0+dfsg/src/lib/ffi/ffi_kdf.cpp --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_kdf.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_kdf.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,6 +6,7 @@ #include +#include #include #include #include @@ -63,6 +64,12 @@ if(algo == nullptr || password == nullptr) { return BOTAN_FFI_ERROR_NULL_POINTER; } + if(out_len > 0 && out == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + if(salt_len > 0 && salt == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } if(password_len == 0) { password_len = std::strlen(password); @@ -97,6 +104,12 @@ if(algo == nullptr || password == nullptr) { return BOTAN_FFI_ERROR_NULL_POINTER; } + if(out_len > 0 && out == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + if(salt_len > 0 && salt == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } if(password_len == 0) { password_len = std::strlen(password); @@ -109,15 +122,15 @@ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; } - auto pwdhash = pwdhash_fam->tune(out_len, std::chrono::milliseconds(msec)); + auto pwdhash = pwdhash_fam->tune_params(out_len, msec); - if(param1) { + if(param1 != nullptr) { *param1 = pwdhash->iterations(); } - if(param2) { + if(param2 != nullptr) { *param2 = pwdhash->parallelism(); } - if(param3) { + if(param3 != nullptr) { *param3 = pwdhash->memory_param(); } @@ -136,6 +149,13 @@ size_t salt_len, const uint8_t label[], size_t label_len) { + if(kdf_algo == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + if((out_len > 0 && out == nullptr) || (secret_len > 0 && secret == nullptr) || (salt_len > 0 && salt == nullptr) || + (label_len > 0 && label == nullptr)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return ffi_guard_thunk(__func__, [=]() -> int { auto kdf = Botan::KDF::create_or_throw(kdf_algo); kdf->kdf(out, out_len, secret, secret_len, salt, salt_len, label, label_len); @@ -177,7 +197,8 @@ Botan::RandomNumberGenerator& rng = safe_get(rng_obj); const std::string bcrypt = Botan::generate_bcrypt(pass, rng, static_cast(wf)); - return write_str_output(out, out_len, bcrypt); + // TODO(Botan4) change the type of out and remove this cast + return write_str_output(reinterpret_cast(out), out_len, bcrypt); }); #else BOTAN_UNUSED(out, out_len, pass, rng_obj, wf, flags); @@ -186,6 +207,9 @@ } int botan_bcrypt_is_valid(const char* pass, const char* hash) { + if(any_null_pointers(pass, hash)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } #if defined(BOTAN_HAS_BCRYPT) return ffi_guard_thunk(__func__, [=]() -> int { return Botan::check_bcrypt(pass, hash) ? BOTAN_FFI_SUCCESS : BOTAN_FFI_INVALID_VERIFIER; diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_keywrap.cpp botan3-3.12.0+dfsg/src/lib/ffi/ffi_keywrap.cpp --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_keywrap.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_keywrap.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,7 @@ #include +#include #include #if defined(BOTAN_HAS_NIST_KEYWRAP) @@ -26,6 +27,9 @@ size_t kek_len, uint8_t wrapped_key[], size_t* wrapped_key_len) { + if(any_null_pointers(cipher_algo, key, kek)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } #if defined(BOTAN_HAS_NIST_KEYWRAP) return ffi_guard_thunk(__func__, [=]() -> int { if(padded != 0 && padded != 1) { @@ -58,6 +62,9 @@ size_t kek_len, uint8_t key[], size_t* key_len) { + if(any_null_pointers(cipher_algo, wrapped_key, kek)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } #if defined(BOTAN_HAS_NIST_KEYWRAP) return ffi_guard_thunk(__func__, [=]() -> int { if(padded != 0 && padded != 1) { @@ -89,7 +96,7 @@ size_t kek_len, uint8_t wrapped_key[], size_t* wrapped_key_len) { - std::string cipher_name = "AES-" + std::to_string(8 * kek_len); + const std::string cipher_name = "AES-" + std::to_string(8 * kek_len); return botan_nist_kw_enc(cipher_name.c_str(), 0, key, key_len, kek, kek_len, wrapped_key, wrapped_key_len); } @@ -100,7 +107,7 @@ size_t kek_len, uint8_t key[], size_t* key_len) { - std::string cipher_name = "AES-" + std::to_string(8 * kek_len); + const std::string cipher_name = "AES-" + std::to_string(8 * kek_len); return botan_nist_kw_dec(cipher_name.c_str(), 0, wrapped_key, wrapped_key_len, kek, kek_len, key, key_len); } diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_mac.cpp botan3-3.12.0+dfsg/src/lib/ffi/ffi_mac.cpp --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_mac.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_mac.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -17,18 +17,19 @@ int botan_mac_init(botan_mac_t* mac, const char* mac_name, uint32_t flags) { return ffi_guard_thunk(__func__, [=]() -> int { - if(!mac || !mac_name || flags != 0) { + if(any_null_pointers(mac, mac_name)) { return BOTAN_FFI_ERROR_NULL_POINTER; } - std::unique_ptr m = Botan::MessageAuthenticationCode::create(mac_name); + if(flags != 0) { + return BOTAN_FFI_ERROR_BAD_FLAG; + } - if(m == nullptr) { + if(auto m = Botan::MessageAuthenticationCode::create(mac_name)) { + return ffi_new_object(mac, std::move(m)); + } else { return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; } - - *mac = new botan_mac_struct(std::move(m)); - return BOTAN_FFI_SUCCESS; }); } @@ -37,6 +38,9 @@ } int botan_mac_set_key(botan_mac_t mac, const uint8_t* key, size_t key_len) { + if(key_len > 0 && key == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return BOTAN_FFI_VISIT(mac, [=](auto& m) { m.set_key(key, key_len); }); } @@ -45,6 +49,9 @@ } int botan_mac_output_length(botan_mac_t mac, size_t* out) { + if(out == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return BOTAN_FFI_VISIT(mac, [=](const auto& m) { *out = m.output_length(); }); } @@ -53,10 +60,19 @@ } int botan_mac_update(botan_mac_t mac, const uint8_t* buf, size_t len) { + if(len == 0) { + return BOTAN_FFI_SUCCESS; + } + if(buf == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return BOTAN_FFI_VISIT(mac, [=](auto& m) { m.update(buf, len); }); } int botan_mac_final(botan_mac_t mac, uint8_t out[]) { + if(out == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return BOTAN_FFI_VISIT(mac, [=](auto& m) { m.final(out); }); } diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_mp.cpp botan3-3.12.0+dfsg/src/lib/ffi/ffi_mp.cpp --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_mp.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_mp.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,12 +7,14 @@ #include +#include #include -#include +#include #include #include #include #include +#include #include extern "C" { @@ -26,8 +28,7 @@ } auto mp = std::make_unique(); - *mp_out = new botan_mp_struct(std::move(mp)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(mp_out, std::move(mp)); }); } @@ -45,33 +46,27 @@ int botan_mp_set_from_radix_str(botan_mp_t mp, const char* str, size_t radix) { return BOTAN_FFI_VISIT(mp, [=](auto& bn) { - Botan::BigInt::Base base; - if(radix == 10) { - base = Botan::BigInt::Decimal; - } else if(radix == 16) { - base = Botan::BigInt::Hexadecimal; - } else { + if(radix != 10 && radix != 16) { return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; } - const uint8_t* bytes = Botan::cast_char_ptr_to_uint8(str); - const size_t len = strlen(str); - - bn = Botan::BigInt(bytes, len, base); + bn = Botan::BigInt::from_radix_digits(std::string_view(str), radix); return BOTAN_FFI_SUCCESS; }); } +// NOLINTBEGIN(misc-misplaced-const) + int botan_mp_set_from_mp(botan_mp_t dest, const botan_mp_t source) { return BOTAN_FFI_VISIT(dest, [=](auto& bn) { bn = safe_get(source); }); } int botan_mp_is_negative(const botan_mp_t mp) { - return BOTAN_FFI_VISIT(mp, [](const auto& bn) { return bn.is_negative() ? 1 : 0; }); + return BOTAN_FFI_VISIT(mp, [](const auto& bn) { return bn.signum() < 0 ? 1 : 0; }); } int botan_mp_is_positive(const botan_mp_t mp) { - return BOTAN_FFI_VISIT(mp, [](const auto& bn) { return bn.is_positive() ? 1 : 0; }); + return BOTAN_FFI_VISIT(mp, [](const auto& bn) { return bn.signum() >= 0 ? 1 : 0; }); } int botan_mp_flip_sign(botan_mp_t mp) { @@ -79,21 +74,38 @@ } int botan_mp_from_bin(botan_mp_t mp, const uint8_t bin[], size_t bin_len) { + if(bin_len > 0 && bin == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return BOTAN_FFI_VISIT(mp, [=](auto& bn) { bn._assign_from_bytes({bin, bin_len}); }); } int botan_mp_to_hex(const botan_mp_t mp, char* out) { + if(out == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return BOTAN_FFI_VISIT(mp, [=](const auto& bn) { const std::string hex = bn.to_hex_string(); + + // Check that we are about to write no more than the documented upper bound + const size_t upper_bound = 2 * bn.bytes() + 5; + BOTAN_ASSERT_NOMSG(hex.size() + 1 <= upper_bound); std::memcpy(out, hex.c_str(), 1 + hex.size()); }); } -int botan_mp_to_str(const botan_mp_t mp, uint8_t digit_base, char* out, size_t* out_len) { +int botan_mp_view_hex(const botan_mp_t mp, botan_view_ctx ctx, botan_view_str_fn view) { + return BOTAN_FFI_VISIT(mp, [=](const auto& bn) -> int { + const std::string hex = bn.to_hex_string(); + return invoke_view_callback(view, ctx, hex); + }); +} + +int botan_mp_to_str(const botan_mp_t mp, uint8_t radix, char* out, size_t* out_len) { return BOTAN_FFI_VISIT(mp, [=](const auto& bn) -> int { - if(digit_base == 0 || digit_base == 10) { + if(radix == 0 || radix == 10) { return write_str_output(out, out_len, bn.to_dec_string()); - } else if(digit_base == 16) { + } else if(radix == 16) { return write_str_output(out, out_len, bn.to_hex_string()); } else { return BOTAN_FFI_ERROR_BAD_PARAMETER; @@ -101,10 +113,32 @@ }); } +int botan_mp_view_str(const botan_mp_t mp, uint8_t radix, botan_view_ctx ctx, botan_view_str_fn view) { + return BOTAN_FFI_VISIT(mp, [=](const auto& bn) -> int { + if(radix == 10) { + return invoke_view_callback(view, ctx, bn.to_dec_string()); + } else if(radix == 16) { + return invoke_view_callback(view, ctx, bn.to_hex_string()); + } else { + return BOTAN_FFI_ERROR_BAD_PARAMETER; + } + }); +} + int botan_mp_to_bin(const botan_mp_t mp, uint8_t vec[]) { + if(vec == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return BOTAN_FFI_VISIT(mp, [=](const auto& bn) { bn.serialize_to(std::span{vec, bn.bytes()}); }); } +int botan_mp_view_bin(const botan_mp_t mp, botan_view_ctx ctx, botan_view_bin_fn view) { + return BOTAN_FFI_VISIT(mp, [=](const auto& bn) { + const auto bytes = bn.serialize(); + return invoke_view_callback(view, ctx, bytes); + }); +} + int botan_mp_to_uint32(const botan_mp_t mp, uint32_t* val) { if(val == nullptr) { return BOTAN_FFI_ERROR_NULL_POINTER; @@ -191,6 +225,9 @@ } int botan_mp_cmp(int* result, const botan_mp_t x_w, const botan_mp_t y_w) { + if(result == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return BOTAN_FFI_VISIT(x_w, [=](auto& x) { *result = x.cmp(safe_get(y_w)); }); } @@ -220,7 +257,7 @@ int botan_mp_mod_mul(botan_mp_t out, const botan_mp_t x, const botan_mp_t y, const botan_mp_t modulus) { return BOTAN_FFI_VISIT(out, [=](auto& o) { - auto reducer = Botan::Modular_Reducer::for_secret_modulus(safe_get(modulus)); + auto reducer = Botan::Barrett_Reduction::for_secret_modulus(safe_get(modulus)); o = reducer.multiply(safe_get(x), safe_get(y)); }); } @@ -255,10 +292,18 @@ } int botan_mp_num_bits(const botan_mp_t mp, size_t* bits) { + if(bits == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return BOTAN_FFI_VISIT(mp, [=](const auto& n) { *bits = n.bits(); }); } int botan_mp_num_bytes(const botan_mp_t mp, size_t* bytes) { + if(bytes == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return BOTAN_FFI_VISIT(mp, [=](const auto& n) { *bytes = n.bytes(); }); } + +// NOLINTEND(misc-misplaced-const) } diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_oid.cpp botan3-3.12.0+dfsg/src/lib/ffi/ffi_oid.cpp --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_oid.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_oid.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,80 @@ +/* +* (C) 2025 Jack Lloyd +* (C) 2025 Dominik Schricker +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include + +extern "C" { + +using namespace Botan_FFI; + +int botan_oid_destroy(botan_asn1_oid_t oid) { + return BOTAN_FFI_CHECKED_DELETE(oid); +} + +int botan_oid_from_string(botan_asn1_oid_t* oid_obj, const char* oid_str) { + return ffi_guard_thunk(__func__, [=]() -> int { + if(oid_obj == nullptr || oid_str == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + Botan::OID oid; + // This returns a Lookup_Error if an unknown name is passed, + // which would get turned into NOT_IMPLEMENTED + try { + oid = Botan::OID::from_string(oid_str); + } catch(Botan::Lookup_Error&) { + return BOTAN_FFI_ERROR_BAD_PARAMETER; + } + auto oid_ptr = std::make_unique(std::move(oid)); + return ffi_new_object(oid_obj, std::move(oid_ptr)); + }); +} + +int botan_oid_register(botan_asn1_oid_t oid, const char* name) { + return BOTAN_FFI_VISIT(oid, [=](const auto& o) -> int { + if(name == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + Botan::OID::register_oid(o, name); + return BOTAN_FFI_SUCCESS; + }); +} + +int botan_oid_view_string(botan_asn1_oid_t oid, botan_view_ctx ctx, botan_view_str_fn view) { + return BOTAN_FFI_VISIT(oid, [=](const auto& o) -> int { return invoke_view_callback(view, ctx, o.to_string()); }); +} + +int botan_oid_view_name(botan_asn1_oid_t oid, botan_view_ctx ctx, botan_view_str_fn view) { + return BOTAN_FFI_VISIT( + oid, [=](const auto& o) -> int { return invoke_view_callback(view, ctx, o.to_formatted_string()); }); +} + +int botan_oid_equal(botan_asn1_oid_t a_w, botan_asn1_oid_t b_w) { + return BOTAN_FFI_VISIT(a_w, [=](const auto& a) -> int { return a == safe_get(b_w); }); +} + +int botan_oid_cmp(int* result, botan_asn1_oid_t a_w, botan_asn1_oid_t b_w) { + return BOTAN_FFI_VISIT(a_w, [=](auto& a) { + if(result == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + const Botan::OID b = safe_get(b_w); + // we don't have .cmp for OID + if(a == b) { + *result = 0; + } else if(a < b) { + *result = -1; + } else { + *result = 1; + } + return BOTAN_FFI_SUCCESS; + }); +} +} diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_oid.h botan3-3.12.0+dfsg/src/lib/ffi/ffi_oid.h --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_oid.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_oid.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,19 @@ +/* +* (C) 2025 Jack Lloyd +* (C) 2025 Dominik Schricker +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_FFI_OID_H_ +#define BOTAN_FFI_OID_H_ + +#include +#include + +extern "C" { + +BOTAN_FFI_DECLARE_STRUCT(botan_asn1_oid_struct, Botan::OID, 0x9217DA20); +} + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_pk_op.cpp botan3-3.12.0+dfsg/src/lib/ffi/ffi_pk_op.cpp --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_pk_op.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_pk_op.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -38,8 +38,7 @@ *op = nullptr; auto pk = std::make_unique(safe_get(key_obj), Botan::system_rng(), padding); - *op = new botan_pk_op_encrypt_struct(std::move(pk)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(op, std::move(pk)); }); } @@ -84,8 +83,7 @@ *op = nullptr; auto pk = std::make_unique(safe_get(key_obj), Botan::system_rng(), padding); - *op = new botan_pk_op_decrypt_struct(std::move(pk)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(op, std::move(pk)); }); } @@ -121,12 +119,11 @@ return ffi_guard_thunk(__func__, [=]() -> int { *op = nullptr; - auto format = (flags & BOTAN_PUBKEY_DER_FORMAT_SIGNATURE) ? Botan::Signature_Format::DerSequence - : Botan::Signature_Format::Standard; + const bool use_der = (flags & BOTAN_PUBKEY_DER_FORMAT_SIGNATURE) != 0; + auto format = use_der ? Botan::Signature_Format::DerSequence : Botan::Signature_Format::Standard; auto pk = std::make_unique(safe_get(key_obj), Botan::system_rng(), hash, format); - *op = new botan_pk_op_sign_struct(std::move(pk)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(op, std::move(pk)); }); } @@ -161,11 +158,10 @@ return ffi_guard_thunk(__func__, [=]() -> int { *op = nullptr; - auto format = (flags & BOTAN_PUBKEY_DER_FORMAT_SIGNATURE) ? Botan::Signature_Format::DerSequence - : Botan::Signature_Format::Standard; + const bool use_der = (flags & BOTAN_PUBKEY_DER_FORMAT_SIGNATURE) != 0; + auto format = use_der ? Botan::Signature_Format::DerSequence : Botan::Signature_Format::Standard; auto pk = std::make_unique(safe_get(key_obj), hash, format); - *op = new botan_pk_op_verify_struct(std::move(pk)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(op, std::move(pk)); }); } @@ -201,8 +197,7 @@ return ffi_guard_thunk(__func__, [=]() -> int { *op = nullptr; auto pk = std::make_unique(safe_get(key_obj), Botan::system_rng(), kdf); - *op = new botan_pk_op_ka_struct(std::move(pk)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(op, std::move(pk)); }); } @@ -254,8 +249,7 @@ return ffi_guard_thunk(__func__, [=]() -> int { auto pk = std::make_unique(safe_get(key_obj), padding); - *op = new botan_pk_op_kem_encrypt_struct(std::move(pk)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(op, std::move(pk)); }); } @@ -300,7 +294,7 @@ return BOTAN_FFI_VISIT(op, [=](auto& kem) { const auto result = kem.encrypt(safe_get(rng), desired_shared_key_len, {salt, salt_len}); - int rc = write_vec_output(encapsulated_key_out, encapsulated_key_len, result.encapsulated_shared_key()); + const int rc = write_vec_output(encapsulated_key_out, encapsulated_key_len, result.encapsulated_shared_key()); if(rc != 0) { return rc; @@ -317,8 +311,7 @@ return ffi_guard_thunk(__func__, [=]() -> int { auto pk = std::make_unique(safe_get(key_obj), Botan::system_rng(), padding); - *op = new botan_pk_op_kem_decrypt_struct(std::move(pk)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(op, std::move(pk)); }); } diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_pkey.cpp botan3-3.12.0+dfsg/src/lib/ffi/ffi_pkey.cpp --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_pkey.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_pkey.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,12 +6,15 @@ #include +#include #include #include #include #include #include #include +#include +#include #include #include #include @@ -28,6 +31,11 @@ const char* algo_name, const char* algo_params, botan_rng_t rng_obj) { + // TODO(Botan4) remove this implicit algorithm choice and reject nullptr algo_name + if(algo_name == nullptr) { + return botan_privkey_create(key_obj, "RSA", algo_params, rng_obj); + } + return ffi_guard_thunk(__func__, [=]() -> int { if(key_obj == nullptr) { return BOTAN_FFI_ERROR_NULL_POINTER; @@ -38,13 +46,38 @@ return BOTAN_FFI_ERROR_NULL_POINTER; } + const std::string params(algo_params != nullptr ? algo_params : ""); + Botan::RandomNumberGenerator& rng = safe_get(rng_obj); - std::unique_ptr key( - Botan::create_private_key(algo_name ? algo_name : "RSA", rng, algo_params ? algo_params : "")); - if(key) { - *key_obj = new botan_privkey_struct(std::move(key)); - return BOTAN_FFI_SUCCESS; + if(auto key = Botan::create_private_key(algo_name, rng, params)) { + return ffi_new_object(key_obj, std::move(key)); + } else { + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; + } + }); +} + +int botan_ec_privkey_create(botan_privkey_t* key_obj, + const char* algo_name, + botan_ec_group_t ec_group_obj, + botan_rng_t rng_obj) { + // TODO(Botan4) remove this implicit algorithm choice and reject nullptr algo_name + if(algo_name == nullptr) { + return botan_ec_privkey_create(key_obj, "ECDSA", ec_group_obj, rng_obj); + } + + return ffi_guard_thunk(__func__, [=]() -> int { + if(key_obj == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + *key_obj = nullptr; + + const Botan::EC_Group ec_group = safe_get(ec_group_obj); + Botan::RandomNumberGenerator& rng = safe_get(rng_obj); + + if(auto key = Botan::create_ec_private_key(algo_name, ec_group, rng)) { + return ffi_new_object(key_obj, std::move(key)); } else { return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; } @@ -55,8 +88,16 @@ botan_privkey_t* key, botan_rng_t rng_obj, const uint8_t bits[], size_t len, const char* password) { BOTAN_UNUSED(rng_obj); + if(key == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + *key = nullptr; + if(bits == nullptr && len > 0) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + return ffi_guard_thunk(__func__, [=]() -> int { Botan::DataSource_Memory src(bits, len); @@ -69,7 +110,7 @@ } if(pkcs8) { - *key = new botan_privkey_struct(std::move(pkcs8)); + ffi_new_object(key, std::move(pkcs8)); return BOTAN_FFI_SUCCESS; } return BOTAN_FFI_ERROR_UNKNOWN_ERROR; @@ -81,8 +122,16 @@ } int botan_pubkey_load(botan_pubkey_t* key, const uint8_t bits[], size_t bits_len) { + if(key == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + *key = nullptr; + if(bits == nullptr && bits_len > 0) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + return ffi_guard_thunk(__func__, [=]() -> int { Botan::DataSource_Memory src(bits, bits_len); std::unique_ptr pubkey(Botan::X509::load_key(src)); @@ -91,7 +140,7 @@ return BOTAN_FFI_ERROR_UNKNOWN_ERROR; } - *key = new botan_pubkey_struct(std::move(pubkey)); + ffi_new_object(key, std::move(pubkey)); return BOTAN_FFI_SUCCESS; }); } @@ -101,10 +150,12 @@ } int botan_privkey_export_pubkey(botan_pubkey_t* pubout, botan_privkey_t key_obj) { + if(pubout == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return ffi_guard_thunk(__func__, [=]() -> int { auto public_key = safe_get(key_obj).public_key(); - *pubout = new botan_pubkey_struct(std::move(public_key)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(pubout, std::move(public_key)); }); } @@ -117,7 +168,7 @@ } int botan_pubkey_check_key(botan_pubkey_t key, botan_rng_t rng, uint32_t flags) { - const bool strong = (flags & BOTAN_CHECK_KEY_EXPENSIVE_TESTS); + const bool strong = (flags & BOTAN_CHECK_KEY_EXPENSIVE_TESTS) != 0; return BOTAN_FFI_VISIT(key, [=](const auto& k) { return (k.check_key(safe_get(rng), strong) == true) ? 0 : BOTAN_FFI_ERROR_INVALID_INPUT; @@ -125,7 +176,7 @@ } int botan_privkey_check_key(botan_privkey_t key, botan_rng_t rng, uint32_t flags) { - const bool strong = (flags & BOTAN_CHECK_KEY_EXPENSIVE_TESTS); + const bool strong = (flags & BOTAN_CHECK_KEY_EXPENSIVE_TESTS) != 0; return BOTAN_FFI_VISIT(key, [=](const auto& k) { return (k.check_key(safe_get(rng), strong) == true) ? 0 : BOTAN_FFI_ERROR_INVALID_INPUT; }); @@ -145,7 +196,7 @@ int botan_pubkey_view_der(botan_pubkey_t key, botan_view_ctx ctx, botan_view_bin_fn view) { return BOTAN_FFI_VISIT( - key, [=](const auto& k) -> int { return invoke_view_callback(view, ctx, Botan::X509::BER_encode(k)); }); + key, [=](const auto& k) -> int { return invoke_view_callback(view, ctx, k.subject_public_key()); }); } int botan_pubkey_view_pem(botan_pubkey_t key, botan_view_ctx ctx, botan_view_str_fn view) { @@ -171,8 +222,8 @@ } int botan_privkey_view_der(botan_privkey_t key, botan_view_ctx ctx, botan_view_bin_fn view) { - return BOTAN_FFI_VISIT( - key, [=](const auto& k) -> int { return invoke_view_callback(view, ctx, Botan::PKCS8::BER_encode(k)); }); + return BOTAN_FFI_VISIT(key, + [=](const auto& k) -> int { return invoke_view_callback(view, ctx, k.private_key_info()); }); } int botan_privkey_view_pem(botan_privkey_t key, botan_view_ctx ctx, botan_view_str_fn view) { @@ -205,7 +256,7 @@ const char* cipher, const char* pbkdf_hash, uint32_t flags) { - if(pbkdf_iters_out) { + if(pbkdf_iters_out != nullptr) { *pbkdf_iters_out = 0; } @@ -342,11 +393,73 @@ }); } +int botan_pubkey_oid(botan_asn1_oid_t* oid, botan_pubkey_t key) { + return BOTAN_FFI_VISIT(key, [=](const auto& k) { + if(oid == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + auto oid_ptr = std::make_unique(k.object_identifier()); + ffi_new_object(oid, std::move(oid_ptr)); + + return BOTAN_FFI_SUCCESS; + }); +} + +int botan_privkey_oid(botan_asn1_oid_t* oid, botan_privkey_t key) { + return BOTAN_FFI_VISIT(key, [=](const auto& k) { + if(oid == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + auto oid_ptr = std::make_unique(k.object_identifier()); + ffi_new_object(oid, std::move(oid_ptr)); + + return BOTAN_FFI_SUCCESS; + }); +} + +int botan_privkey_stateful_operation(botan_privkey_t key, int* out) { + return BOTAN_FFI_VISIT(key, [=](const auto& k) { + if(out == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + if(k.stateful_operation()) { + *out = 1; + } else { + *out = 0; + } + return BOTAN_FFI_SUCCESS; + }); +} + +int botan_privkey_remaining_operations(botan_privkey_t key, uint64_t* out) { + return BOTAN_FFI_VISIT(key, [=](const auto& k) { + if(out == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + if(auto remaining = k.remaining_operations()) { + *out = remaining.value(); + return BOTAN_FFI_SUCCESS; + } else { + return BOTAN_FFI_ERROR_NO_VALUE; + } + }); +} + int botan_pubkey_estimated_strength(botan_pubkey_t key, size_t* estimate) { + if(estimate == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return BOTAN_FFI_VISIT(key, [=](const auto& k) { *estimate = k.estimated_strength(); }); } int botan_pubkey_fingerprint(botan_pubkey_t key, const char* hash_fn, uint8_t out[], size_t* out_len) { + if(hash_fn == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return BOTAN_FFI_VISIT(key, [=](const auto& k) { auto h = Botan::HashFunction::create_or_throw(hash_fn); return write_vec_output(out, out_len, h->process(k.public_key_bits())); @@ -354,6 +467,9 @@ } int botan_pkcs_hash_id(const char* hash_name, uint8_t pkcs_id[], size_t* pkcs_id_len) { + if(hash_name == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } #if defined(BOTAN_HAS_HASH_ID) return ffi_guard_thunk(__func__, [=]() -> int { const std::vector hash_id = Botan::pkcs_hash_id(hash_name); diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_pkey_algs.cpp botan3-3.12.0+dfsg/src/lib/ffi/ffi_pkey_algs.cpp --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_pkey_algs.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_pkey_algs.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,8 +8,12 @@ #include +#include +#include #include +#include #include +#include #include #include #include @@ -63,10 +67,6 @@ #include #endif -#if defined(BOTAN_HAS_MCELIECE) - #include -#endif - #if defined(BOTAN_HAS_DIFFIE_HELLMAN) #include #endif @@ -106,6 +106,9 @@ if(curve_name == nullptr) { return BOTAN_FFI_ERROR_NULL_POINTER; } + if(!Botan::EC_Group::supports_named_group(curve_name)) { + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; + } Botan::Null_RNG null_rng; const auto grp = Botan::EC_Group::from_name(curve_name); @@ -122,6 +125,10 @@ return BOTAN_FFI_ERROR_NULL_POINTER; } + if(!Botan::EC_Group::supports_named_group(curve_name)) { + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; + } + const auto group = Botan::EC_Group::from_name(curve_name); if(auto pt = Botan::EC_AffinePoint::from_bigint_xy(group, public_x, public_y)) { @@ -132,6 +139,24 @@ } } +template +int pubkey_load_ec_sec1(std::unique_ptr& key, + std::span sec1, + std::string_view curve_name) { + if(!Botan::EC_Group::supports_named_group(curve_name)) { + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; + } + + const auto group = Botan::EC_Group::from_name(curve_name); + + if(auto pt = Botan::EC_AffinePoint::deserialize(group, sec1)) { + key.reset(new ECPublicKey_t(group, pt.value())); + return BOTAN_FFI_SUCCESS; + } else { + return BOTAN_FFI_ERROR_BAD_PARAMETER; + } +} + #endif Botan::BigInt pubkey_get_field(const Botan::Public_Key& key, std::string_view field) { @@ -205,19 +230,21 @@ return BOTAN_FFI_ERROR_BAD_PARAMETER; } - std::string n_str = std::to_string(n_bits); + const std::string n_str = std::to_string(n_bits); return botan_privkey_create(key_obj, "RSA", n_str.c_str(), rng_obj); } int botan_privkey_load_rsa(botan_privkey_t* key, botan_mp_t rsa_p, botan_mp_t rsa_q, botan_mp_t rsa_e) { #if defined(BOTAN_HAS_RSA) + if(key == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } *key = nullptr; return ffi_guard_thunk(__func__, [=]() -> int { auto rsa = std::make_unique(safe_get(rsa_p), safe_get(rsa_q), safe_get(rsa_e)); - *key = new botan_privkey_struct(std::move(rsa)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(rsa)); }); #else BOTAN_UNUSED(key, rsa_p, rsa_q, rsa_e); @@ -227,14 +254,15 @@ int botan_privkey_load_rsa_pkcs1(botan_privkey_t* key, const uint8_t bits[], size_t len) { #if defined(BOTAN_HAS_RSA) + if(Botan::any_null_pointers(key, bits)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } *key = nullptr; - Botan::secure_vector src(bits, bits + len); return ffi_guard_thunk(__func__, [=]() -> int { - Botan::AlgorithmIdentifier alg_id("RSA", Botan::AlgorithmIdentifier::USE_NULL_PARAM); - auto rsa = std::make_unique(alg_id, src); - *key = new botan_privkey_struct(std::move(rsa)); - return BOTAN_FFI_SUCCESS; + const Botan::AlgorithmIdentifier alg_id("RSA", Botan::AlgorithmIdentifier::USE_NULL_PARAM); + auto rsa = std::make_unique(alg_id, std::span{bits, len}); + return ffi_new_object(key, std::move(rsa)); }); #else BOTAN_UNUSED(key, bits, len); @@ -244,11 +272,13 @@ int botan_pubkey_load_rsa(botan_pubkey_t* key, botan_mp_t n, botan_mp_t e) { #if defined(BOTAN_HAS_RSA) + if(key == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } *key = nullptr; return ffi_guard_thunk(__func__, [=]() -> int { auto rsa = std::make_unique(safe_get(n), safe_get(e)); - *key = new botan_pubkey_struct(std::move(rsa)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(rsa)); }); #else BOTAN_UNUSED(key, n, e); @@ -256,6 +286,24 @@ #endif } +int botan_pubkey_load_rsa_pkcs1(botan_pubkey_t* key, const uint8_t bits[], size_t len) { +#if defined(BOTAN_HAS_RSA) + if(Botan::any_null_pointers(key, bits)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + *key = nullptr; + + return ffi_guard_thunk(__func__, [=]() -> int { + const Botan::AlgorithmIdentifier alg_id("RSA", Botan::AlgorithmIdentifier::USE_NULL_PARAM); + auto rsa = std::make_unique(alg_id, std::span{bits, len}); + return ffi_new_object(key, std::move(rsa)); + }); +#else + BOTAN_UNUSED(key, bits, len); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + int botan_privkey_rsa_get_p(botan_mp_t p, botan_privkey_t key) { return botan_privkey_get_field(p, key, "p"); } @@ -291,7 +339,10 @@ if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_DER) { return write_vec_output(out, out_len, rsa->private_key_bits()); } else if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_PEM) { - return write_str_output(out, out_len, Botan::PEM_Code::encode(rsa->private_key_bits(), "RSA PRIVATE KEY")); + // TODO define new generic functions for this + return write_str_output(reinterpret_cast(out), + out_len, + Botan::PEM_Code::encode(rsa->private_key_bits(), "RSA PRIVATE KEY")); } else { return BOTAN_FFI_ERROR_BAD_FLAG; } @@ -309,20 +360,19 @@ int botan_privkey_create_dsa(botan_privkey_t* key, botan_rng_t rng_obj, size_t pbits, size_t qbits) { #if defined(BOTAN_HAS_DSA) - if((rng_obj == nullptr) || (key == nullptr)) { + if(Botan::any_null_pointers(rng_obj, key)) { return BOTAN_FFI_ERROR_NULL_POINTER; } - if((pbits % 64) || (qbits % 8) || (pbits < 1024) || (pbits > 3072) || (qbits < 160) || (qbits > 256)) { + if((pbits % 64 != 0) || (qbits % 8 != 0) || (pbits < 1024) || (pbits > 3072) || (qbits < 160) || (qbits > 256)) { return BOTAN_FFI_ERROR_BAD_PARAMETER; } return ffi_guard_thunk(__func__, [=]() -> int { Botan::RandomNumberGenerator& rng = safe_get(rng_obj); - Botan::DL_Group group(rng, Botan::DL_Group::Prime_Subgroup, pbits, qbits); + const Botan::DL_Group group(rng, Botan::DL_Group::Prime_Subgroup, pbits, qbits); auto dsa = std::make_unique(rng, group); - *key = new botan_privkey_struct(std::move(dsa)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(dsa)); }); #else BOTAN_UNUSED(key, rng_obj, pbits, qbits); @@ -332,13 +382,15 @@ int botan_privkey_load_dsa(botan_privkey_t* key, botan_mp_t p, botan_mp_t q, botan_mp_t g, botan_mp_t x) { #if defined(BOTAN_HAS_DSA) + if(key == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } *key = nullptr; return ffi_guard_thunk(__func__, [=]() -> int { - Botan::DL_Group group(safe_get(p), safe_get(q), safe_get(g)); + const Botan::DL_Group group(safe_get(p), safe_get(q), safe_get(g)); auto dsa = std::make_unique(group, safe_get(x)); - *key = new botan_privkey_struct(std::move(dsa)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(dsa)); }); #else BOTAN_UNUSED(key, p, q, g, x); @@ -348,13 +400,15 @@ int botan_pubkey_load_dsa(botan_pubkey_t* key, botan_mp_t p, botan_mp_t q, botan_mp_t g, botan_mp_t y) { #if defined(BOTAN_HAS_DSA) + if(key == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } *key = nullptr; return ffi_guard_thunk(__func__, [=]() -> int { - Botan::DL_Group group(safe_get(p), safe_get(q), safe_get(g)); + const Botan::DL_Group group(safe_get(p), safe_get(q), safe_get(g)); auto dsa = std::make_unique(group, safe_get(y)); - *key = new botan_pubkey_struct(std::move(dsa)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(dsa)); }); #else BOTAN_UNUSED(key, p, q, g, y); @@ -406,17 +460,24 @@ #endif } +// NOLINTBEGIN(misc-misplaced-const) + int botan_pubkey_load_ecdsa(botan_pubkey_t* key, const botan_mp_t public_x, const botan_mp_t public_y, const char* curve_name) { #if defined(BOTAN_HAS_ECDSA) + if(Botan::any_null_pointers(key, curve_name)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + *key = nullptr; + return ffi_guard_thunk(__func__, [=]() -> int { std::unique_ptr p_key; - int rc = pubkey_load_ec(p_key, safe_get(public_x), safe_get(public_y), curve_name); + const int rc = pubkey_load_ec(p_key, safe_get(public_x), safe_get(public_y), curve_name); if(rc == BOTAN_FFI_SUCCESS) { - *key = new botan_pubkey_struct(std::move(p_key)); + ffi_new_object(key, std::move(p_key)); } return rc; @@ -427,13 +488,41 @@ #endif } +int botan_pubkey_load_ecdsa_sec1(botan_pubkey_t* key, const uint8_t sec1[], size_t sec1_len, const char* curve_name) { +#if defined(BOTAN_HAS_ECDSA) + if(Botan::any_null_pointers(key, sec1, curve_name)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + *key = nullptr; + + return ffi_guard_thunk(__func__, [=]() -> int { + std::unique_ptr p_key; + + const int rc = pubkey_load_ec_sec1(p_key, {sec1, sec1_len}, curve_name); + if(rc == BOTAN_FFI_SUCCESS) { + ffi_new_object(key, std::move(p_key)); + } + + return rc; + }); +#else + BOTAN_UNUSED(key, sec1, sec1_len, curve_name); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + int botan_privkey_load_ecdsa(botan_privkey_t* key, const botan_mp_t scalar, const char* curve_name) { #if defined(BOTAN_HAS_ECDSA) + if(Botan::any_null_pointers(key, curve_name)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + *key = nullptr; + return ffi_guard_thunk(__func__, [=]() -> int { std::unique_ptr p_key; - int rc = privkey_load_ec(p_key, safe_get(scalar), curve_name); + const int rc = privkey_load_ec(p_key, safe_get(scalar), curve_name); if(rc == BOTAN_FFI_SUCCESS) { - *key = new botan_privkey_struct(std::move(p_key)); + ffi_new_object(key, std::move(p_key)); } return rc; }); @@ -446,24 +535,23 @@ /* ElGamal specific operations */ int botan_privkey_create_elgamal(botan_privkey_t* key, botan_rng_t rng_obj, size_t pbits, size_t qbits) { #if defined(BOTAN_HAS_ELGAMAL) - - if((rng_obj == nullptr) || (key == nullptr)) { + if(Botan::any_null_pointers(key, rng_obj)) { return BOTAN_FFI_ERROR_NULL_POINTER; } + *key = nullptr; - if((pbits < 1024) || (qbits < 160)) { + if(pbits < 1024 || qbits < 160) { return BOTAN_FFI_ERROR_BAD_PARAMETER; } - Botan::DL_Group::PrimeType prime_type = + const Botan::DL_Group::PrimeType prime_type = ((pbits - 1) == qbits) ? Botan::DL_Group::Strong : Botan::DL_Group::Prime_Subgroup; return ffi_guard_thunk(__func__, [=]() -> int { Botan::RandomNumberGenerator& rng = safe_get(rng_obj); - Botan::DL_Group group(rng, prime_type, pbits, qbits); + const Botan::DL_Group group(rng, prime_type, pbits, qbits); auto elg = std::make_unique(rng, group); - *key = new botan_privkey_struct(std::move(elg)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(elg)); }); #else BOTAN_UNUSED(key, rng_obj, pbits, qbits); @@ -473,12 +561,14 @@ int botan_pubkey_load_elgamal(botan_pubkey_t* key, botan_mp_t p, botan_mp_t g, botan_mp_t y) { #if defined(BOTAN_HAS_ELGAMAL) + if(key == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } *key = nullptr; return ffi_guard_thunk(__func__, [=]() -> int { - Botan::DL_Group group(safe_get(p), safe_get(g)); + const Botan::DL_Group group(safe_get(p), safe_get(g)); auto elg = std::make_unique(group, safe_get(y)); - *key = new botan_pubkey_struct(std::move(elg)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(elg)); }); #else BOTAN_UNUSED(key, p, g, y); @@ -488,12 +578,14 @@ int botan_privkey_load_elgamal(botan_privkey_t* key, botan_mp_t p, botan_mp_t g, botan_mp_t x) { #if defined(BOTAN_HAS_ELGAMAL) + if(key == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } *key = nullptr; return ffi_guard_thunk(__func__, [=]() -> int { - Botan::DL_Group group(safe_get(p), safe_get(g)); + const Botan::DL_Group group(safe_get(p), safe_get(g)); auto elg = std::make_unique(group, safe_get(x)); - *key = new botan_privkey_struct(std::move(elg)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(elg)); }); #else BOTAN_UNUSED(key, p, g, x); @@ -509,12 +601,14 @@ int botan_privkey_load_dh(botan_privkey_t* key, botan_mp_t p, botan_mp_t g, botan_mp_t x) { #if defined(BOTAN_HAS_DIFFIE_HELLMAN) + if(key == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } *key = nullptr; return ffi_guard_thunk(__func__, [=]() -> int { - Botan::DL_Group group(safe_get(p), safe_get(g)); + const Botan::DL_Group group(safe_get(p), safe_get(g)); auto dh = std::make_unique(group, safe_get(x)); - *key = new botan_privkey_struct(std::move(dh)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(dh)); }); #else BOTAN_UNUSED(key, p, g, x); @@ -524,12 +618,14 @@ int botan_pubkey_load_dh(botan_pubkey_t* key, botan_mp_t p, botan_mp_t g, botan_mp_t y) { #if defined(BOTAN_HAS_DIFFIE_HELLMAN) + if(key == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } *key = nullptr; return ffi_guard_thunk(__func__, [=]() -> int { - Botan::DL_Group group(safe_get(p), safe_get(g)); + const Botan::DL_Group group(safe_get(p), safe_get(g)); auto dh = std::make_unique(group, safe_get(y)); - *key = new botan_pubkey_struct(std::move(dh)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(dh)); }); #else BOTAN_UNUSED(key, p, g, y); @@ -540,17 +636,18 @@ /* ECDH + x25519/x448 specific operations */ int botan_privkey_create_ecdh(botan_privkey_t* key_obj, botan_rng_t rng_obj, const char* param_str) { - if(param_str == nullptr) { + if(Botan::any_null_pointers(key_obj, param_str)) { return BOTAN_FFI_ERROR_NULL_POINTER; } + *key_obj = nullptr; const std::string params(param_str); - if(params == "x25519" || params == "curve25519") { + if(params == "X25519" || params == "x25519" || params == "curve25519") { return botan_privkey_create(key_obj, "X25519", "", rng_obj); } - if(params == "x448") { + if(params == "X448" || params == "x448") { return botan_privkey_create(key_obj, "X448", "", rng_obj); } @@ -562,12 +659,16 @@ const botan_mp_t public_y, const char* curve_name) { #if defined(BOTAN_HAS_ECDH) + if(Botan::any_null_pointers(key, curve_name)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + *key = nullptr; return ffi_guard_thunk(__func__, [=]() -> int { std::unique_ptr p_key; - int rc = pubkey_load_ec(p_key, safe_get(public_x), safe_get(public_y), curve_name); + const int rc = pubkey_load_ec(p_key, safe_get(public_x), safe_get(public_y), curve_name); if(rc == BOTAN_FFI_SUCCESS) { - *key = new botan_pubkey_struct(std::move(p_key)); + ffi_new_object(key, std::move(p_key)); } return rc; }); @@ -577,13 +678,40 @@ #endif } +int botan_pubkey_load_ecdh_sec1(botan_pubkey_t* key, const uint8_t sec1[], size_t sec1_len, const char* curve_name) { +#if defined(BOTAN_HAS_ECDH) + if(Botan::any_null_pointers(key, sec1, curve_name)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + *key = nullptr; + + return ffi_guard_thunk(__func__, [=]() -> int { + std::unique_ptr p_key; + + const int rc = pubkey_load_ec_sec1(p_key, {sec1, sec1_len}, curve_name); + if(rc == BOTAN_FFI_SUCCESS) { + ffi_new_object(key, std::move(p_key)); + } + + return rc; + }); +#else + BOTAN_UNUSED(key, sec1, sec1_len, curve_name); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + int botan_privkey_load_ecdh(botan_privkey_t* key, const botan_mp_t scalar, const char* curve_name) { #if defined(BOTAN_HAS_ECDH) + if(Botan::any_null_pointers(key, curve_name)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + *key = nullptr; return ffi_guard_thunk(__func__, [=]() -> int { std::unique_ptr p_key; - int rc = privkey_load_ec(p_key, safe_get(scalar), curve_name); + const int rc = privkey_load_ec(p_key, safe_get(scalar), curve_name); if(rc == BOTAN_FFI_SUCCESS) { - *key = new botan_privkey_struct(std::move(p_key)); + ffi_new_object(key, std::move(p_key)); } return rc; }); @@ -597,10 +725,7 @@ int botan_pubkey_sm2_compute_za( uint8_t out[], size_t* out_len, const char* ident, const char* hash_algo, const botan_pubkey_t key) { - if(out == nullptr || out_len == nullptr) { - return BOTAN_FFI_ERROR_NULL_POINTER; - } - if(ident == nullptr || hash_algo == nullptr || key == nullptr) { + if(Botan::any_null_pointers(out, out_len, ident, hash_algo, key)) { return BOTAN_FFI_ERROR_NULL_POINTER; } @@ -620,8 +745,9 @@ const std::string ident_str(ident); std::unique_ptr hash = Botan::HashFunction::create_or_throw(hash_algo); - const std::vector za = - Botan::sm2_compute_za(*hash, ident_str, ec_key->domain(), ec_key->_public_ec_point()); + const auto& pt = ec_key->_public_ec_point(); + + const auto za = Botan::sm2_compute_za(*hash, ident_str, ec_key->domain(), pt); return write_vec_output(out, out_len, za); }); @@ -635,13 +761,18 @@ const botan_mp_t public_y, const char* curve_name) { #if defined(BOTAN_HAS_SM2) + if(Botan::any_null_pointers(key, curve_name)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + *key = nullptr; + return ffi_guard_thunk(__func__, [=]() -> int { std::unique_ptr p_key; - if(!pubkey_load_ec(p_key, safe_get(public_x), safe_get(public_y), curve_name)) { - *key = new botan_pubkey_struct(std::move(p_key)); - return BOTAN_FFI_SUCCESS; + if(pubkey_load_ec(p_key, safe_get(public_x), safe_get(public_y), curve_name) == 0) { + return ffi_new_object(key, std::move(p_key)); + } else { + return BOTAN_FFI_ERROR_UNKNOWN_ERROR; } - return BOTAN_FFI_ERROR_UNKNOWN_ERROR; }); #else BOTAN_UNUSED(key, public_x, public_y, curve_name); @@ -649,14 +780,42 @@ #endif } +int botan_pubkey_load_sm2_sec1(botan_pubkey_t* key, const uint8_t sec1[], size_t sec1_len, const char* curve_name) { +#if defined(BOTAN_HAS_SM2) + if(Botan::any_null_pointers(key, sec1, curve_name)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + *key = nullptr; + + return ffi_guard_thunk(__func__, [=]() -> int { + std::unique_ptr p_key; + + const int rc = pubkey_load_ec_sec1(p_key, {sec1, sec1_len}, curve_name); + if(rc == BOTAN_FFI_SUCCESS) { + ffi_new_object(key, std::move(p_key)); + } + + return rc; + }); +#else + BOTAN_UNUSED(key, sec1, sec1_len, curve_name); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + int botan_privkey_load_sm2(botan_privkey_t* key, const botan_mp_t scalar, const char* curve_name) { #if defined(BOTAN_HAS_SM2) + if(Botan::any_null_pointers(key, curve_name)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + *key = nullptr; + return ffi_guard_thunk(__func__, [=]() -> int { std::unique_ptr p_key; - int rc = privkey_load_ec(p_key, safe_get(scalar), curve_name); + const int rc = privkey_load_ec(p_key, safe_get(scalar), curve_name); if(rc == BOTAN_FFI_SUCCESS) { - *key = new botan_privkey_struct(std::move(p_key)); + ffi_new_object(key, std::move(p_key)); } return rc; }); @@ -677,16 +836,75 @@ return botan_privkey_load_sm2(key, scalar, curve_name); } +/* EC key specific operations */ + +int botan_ec_privkey_get_private_key(botan_privkey_t key, botan_ec_scalar_t* value) { + if(Botan::any_null_pointers(value)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } +#if defined(BOTAN_HAS_ECC_KEY) + return ffi_guard_thunk(__func__, [=]() -> int { + const Botan::EC_PrivateKey* ec_key = dynamic_cast(&safe_get(key)); + if(ec_key == nullptr) { + return BOTAN_FFI_ERROR_BAD_PARAMETER; + } + return ffi_new_object(value, std::make_unique(ec_key->_private_key())); + }); +#else + BOTAN_UNUSED(key, value); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_ec_privkey_get_group(botan_privkey_t key, botan_ec_group_t* ec_group) { + if(Botan::any_null_pointers(ec_group)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + +#if defined(BOTAN_HAS_ECC_KEY) + return ffi_guard_thunk(__func__, [=]() -> int { + const Botan::EC_PrivateKey* ec_key = dynamic_cast(&safe_get(key)); + if(ec_key == nullptr) { + return BOTAN_FFI_ERROR_BAD_PARAMETER; + } + return ffi_new_object(ec_group, std::make_unique(ec_key->domain())); + }); +#else + BOTAN_UNUSED(key, ec_group); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_ec_pubkey_get_group(botan_pubkey_t key, botan_ec_group_t* ec_group) { + if(Botan::any_null_pointers(ec_group)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } +#if defined(BOTAN_HAS_ECC_KEY) + return ffi_guard_thunk(__func__, [=]() -> int { + const Botan::EC_PublicKey* ec_key = dynamic_cast(&safe_get(key)); + if(ec_key == nullptr) { + return BOTAN_FFI_ERROR_BAD_PARAMETER; + } + return ffi_new_object(ec_group, std::make_unique(ec_key->domain())); + }); +#else + BOTAN_UNUSED(key, ec_group); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + /* Ed25519 specific operations */ int botan_privkey_load_ed25519(botan_privkey_t* key, const uint8_t privkey[32]) { #if defined(BOTAN_HAS_ED25519) + if(key == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } *key = nullptr; return ffi_guard_thunk(__func__, [=]() -> int { - const Botan::secure_vector privkey_vec(privkey, privkey + 32); - auto ed25519 = std::make_unique(privkey_vec); - *key = new botan_privkey_struct(std::move(ed25519)); - return BOTAN_FFI_SUCCESS; + auto ed25519 = + std::make_unique(Botan::Ed25519_PrivateKey::from_seed(std::span{privkey, 32})); + return ffi_new_object(key, std::move(ed25519)); }); #else BOTAN_UNUSED(key, privkey); @@ -696,12 +914,14 @@ int botan_pubkey_load_ed25519(botan_pubkey_t* key, const uint8_t pubkey[32]) { #if defined(BOTAN_HAS_ED25519) + if(key == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } *key = nullptr; return ffi_guard_thunk(__func__, [=]() -> int { const std::vector pubkey_vec(pubkey, pubkey + 32); auto ed25519 = std::make_unique(pubkey_vec); - *key = new botan_pubkey_struct(std::move(ed25519)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(ed25519)); }); #else BOTAN_UNUSED(key, pubkey); @@ -710,6 +930,9 @@ } int botan_privkey_ed25519_get_privkey(botan_privkey_t key, uint8_t output[64]) { + if(output == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } #if defined(BOTAN_HAS_ED25519) return BOTAN_FFI_VISIT(key, [=](const auto& k) { if(auto ed = dynamic_cast(&k)) { @@ -730,6 +953,9 @@ } int botan_pubkey_ed25519_get_pubkey(botan_pubkey_t key, uint8_t output[32]) { + if(output == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } #if defined(BOTAN_HAS_ED25519) return BOTAN_FFI_VISIT(key, [=](const auto& k) { if(auto ed = dynamic_cast(&k)) { @@ -753,11 +979,13 @@ int botan_privkey_load_ed448(botan_privkey_t* key, const uint8_t privkey[57]) { #if defined(BOTAN_HAS_ED448) + if(key == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } *key = nullptr; return ffi_guard_thunk(__func__, [=]() -> int { auto ed448 = std::make_unique(std::span(privkey, 57)); - *key = new botan_privkey_struct(std::move(ed448)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(ed448)); }); #else BOTAN_UNUSED(key, privkey); @@ -767,11 +995,13 @@ int botan_pubkey_load_ed448(botan_pubkey_t* key, const uint8_t pubkey[57]) { #if defined(BOTAN_HAS_ED448) + if(key == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } *key = nullptr; return ffi_guard_thunk(__func__, [=]() -> int { auto ed448 = std::make_unique(std::span(pubkey, 57)); - *key = new botan_pubkey_struct(std::move(ed448)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(ed448)); }); #else BOTAN_UNUSED(key, pubkey); @@ -780,6 +1010,9 @@ } int botan_privkey_ed448_get_privkey(botan_privkey_t key, uint8_t output[57]) { + if(output == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } #if defined(BOTAN_HAS_ED448) return BOTAN_FFI_VISIT(key, [=](const auto& k) { if(auto ed = dynamic_cast(&k)) { @@ -797,6 +1030,9 @@ } int botan_pubkey_ed448_get_pubkey(botan_pubkey_t key, uint8_t output[57]) { + if(output == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } #if defined(BOTAN_HAS_ED448) return BOTAN_FFI_VISIT(key, [=](const auto& k) { if(auto ed = dynamic_cast(&k)) { @@ -817,12 +1053,13 @@ int botan_privkey_load_x25519(botan_privkey_t* key, const uint8_t privkey[32]) { #if defined(BOTAN_HAS_X25519) + if(key == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } *key = nullptr; return ffi_guard_thunk(__func__, [=]() -> int { - const Botan::secure_vector privkey_vec(privkey, privkey + 32); - auto x25519 = std::make_unique(privkey_vec); - *key = new botan_privkey_struct(std::move(x25519)); - return BOTAN_FFI_SUCCESS; + auto x25519 = std::make_unique(std::span{privkey, 32}); + return ffi_new_object(key, std::move(x25519)); }); #else BOTAN_UNUSED(key, privkey); @@ -832,12 +1069,13 @@ int botan_pubkey_load_x25519(botan_pubkey_t* key, const uint8_t pubkey[32]) { #if defined(BOTAN_HAS_X25519) + if(key == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } *key = nullptr; return ffi_guard_thunk(__func__, [=]() -> int { - const std::vector pubkey_vec(pubkey, pubkey + 32); - auto x25519 = std::make_unique(pubkey_vec); - *key = new botan_pubkey_struct(std::move(x25519)); - return BOTAN_FFI_SUCCESS; + auto x25519 = std::make_unique(std::span{pubkey, 32}); + return ffi_new_object(key, std::move(x25519)); }); #else BOTAN_UNUSED(key, pubkey); @@ -846,6 +1084,9 @@ } int botan_privkey_x25519_get_privkey(botan_privkey_t key, uint8_t output[32]) { + if(output == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } #if defined(BOTAN_HAS_X25519) return BOTAN_FFI_VISIT(key, [=](const auto& k) { if(auto x25519 = dynamic_cast(&k)) { @@ -866,14 +1107,13 @@ } int botan_pubkey_x25519_get_pubkey(botan_pubkey_t key, uint8_t output[32]) { + if(output == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } #if defined(BOTAN_HAS_X25519) return BOTAN_FFI_VISIT(key, [=](const auto& k) { if(auto x25519 = dynamic_cast(&k)) { - const std::vector& x25519_key = x25519->public_value(); - if(x25519_key.size() != 32) { - return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE; - } - Botan::copy_mem(output, x25519_key.data(), x25519_key.size()); + Botan::copy_mem(std::span{output, 32}, x25519->raw_public_key_bits()); return BOTAN_FFI_SUCCESS; } else { return BOTAN_FFI_ERROR_BAD_PARAMETER; @@ -889,11 +1129,13 @@ int botan_privkey_load_x448(botan_privkey_t* key, const uint8_t privkey[56]) { #if defined(BOTAN_HAS_X448) + if(key == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } *key = nullptr; return ffi_guard_thunk(__func__, [=]() -> int { - auto x448 = std::make_unique(std::span(privkey, 56)); - *key = new botan_privkey_struct(std::move(x448)); - return BOTAN_FFI_SUCCESS; + auto x448 = std::make_unique(std::span{privkey, 56}); + return ffi_new_object(key, std::move(x448)); }); #else BOTAN_UNUSED(key, privkey); @@ -903,11 +1145,13 @@ int botan_pubkey_load_x448(botan_pubkey_t* key, const uint8_t pubkey[56]) { #if defined(BOTAN_HAS_X448) + if(key == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } *key = nullptr; return ffi_guard_thunk(__func__, [=]() -> int { - auto x448 = std::make_unique(std::span(pubkey, 56)); - *key = new botan_pubkey_struct(std::move(x448)); - return BOTAN_FFI_SUCCESS; + auto x448 = std::make_unique(std::span{pubkey, 56}); + return ffi_new_object(key, std::move(x448)); }); #else BOTAN_UNUSED(key, pubkey); @@ -916,11 +1160,14 @@ } int botan_privkey_x448_get_privkey(botan_privkey_t key, uint8_t output[56]) { + if(output == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } #if defined(BOTAN_HAS_X448) return BOTAN_FFI_VISIT(key, [=](const auto& k) { if(auto x448 = dynamic_cast(&k)) { const auto x448_key = x448->raw_private_key_bits(); - Botan::copy_mem(std::span(output, 56), x448_key); + Botan::copy_mem(std::span{output, 56}, x448_key); return BOTAN_FFI_SUCCESS; } else { return BOTAN_FFI_ERROR_BAD_PARAMETER; @@ -933,11 +1180,13 @@ } int botan_pubkey_x448_get_pubkey(botan_pubkey_t key, uint8_t output[56]) { + if(output == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } #if defined(BOTAN_HAS_X448) return BOTAN_FFI_VISIT(key, [=](const auto& k) { if(auto x448 = dynamic_cast(&k)) { - const std::vector& x448_key = x448->public_value(); - Botan::copy_mem(std::span(output, 56), x448_key); + Botan::copy_mem(std::span{output, 56}, x448->raw_public_key_bits()); return BOTAN_FFI_SUCCESS; } else { return BOTAN_FFI_ERROR_BAD_PARAMETER; @@ -955,32 +1204,30 @@ int botan_privkey_load_kyber(botan_privkey_t* key, const uint8_t privkey[], size_t key_len) { #if defined(BOTAN_HAS_KYBER) + if(Botan::any_null_pointers(key, privkey)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } *key = nullptr; - switch(key_len) { - case 1632: - return ffi_guard_thunk(__func__, [=]() -> int { - const Botan::secure_vector privkey_vec(privkey, privkey + 1632); - auto kyber512 = std::make_unique(privkey_vec, Botan::KyberMode::Kyber512_R3); - *key = new botan_privkey_struct(std::move(kyber512)); - return BOTAN_FFI_SUCCESS; - }); - case 2400: - return ffi_guard_thunk(__func__, [=]() -> int { - const Botan::secure_vector privkey_vec(privkey, privkey + 2400); - auto kyber768 = std::make_unique(privkey_vec, Botan::KyberMode::Kyber768_R3); - *key = new botan_privkey_struct(std::move(kyber768)); - return BOTAN_FFI_SUCCESS; - }); - case 3168: - return ffi_guard_thunk(__func__, [=]() -> int { - const Botan::secure_vector privkey_vec(privkey, privkey + 3168); - auto kyber1024 = std::make_unique(privkey_vec, Botan::KyberMode::Kyber1024_R3); - *key = new botan_privkey_struct(std::move(kyber1024)); - return BOTAN_FFI_SUCCESS; - }); - default: - BOTAN_UNUSED(key, privkey, key_len); - return BOTAN_FFI_ERROR_BAD_PARAMETER; + + const auto mode = [](size_t len) -> std::optional { + if(len == 1632) { + return Botan::KyberMode::Kyber512_R3; + } else if(len == 2400) { + return Botan::KyberMode::Kyber768_R3; + } else if(len == 3168) { + return Botan::KyberMode::Kyber1024_R3; + } else { + return {}; + } + }(key_len); + + if(mode.has_value()) { + return ffi_guard_thunk(__func__, [=]() -> int { + auto kyber = std::make_unique(std::span{privkey, key_len}, *mode); + return ffi_new_object(key, std::move(kyber)); + }); + } else { + return BOTAN_FFI_ERROR_BAD_PARAMETER; } #else BOTAN_UNUSED(key, key_len, privkey); @@ -990,32 +1237,30 @@ int botan_pubkey_load_kyber(botan_pubkey_t* key, const uint8_t pubkey[], size_t key_len) { #if defined(BOTAN_HAS_KYBER) + if(Botan::any_null_pointers(key, pubkey)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } *key = nullptr; - switch(key_len) { - case 800: - return ffi_guard_thunk(__func__, [=]() -> int { - const std::vector pubkey_vec(pubkey, pubkey + 800); - auto kyber512 = std::make_unique(pubkey_vec, Botan::KyberMode::Kyber512_R3); - *key = new botan_pubkey_struct(std::move(kyber512)); - return BOTAN_FFI_SUCCESS; - }); - case 1184: - return ffi_guard_thunk(__func__, [=]() -> int { - const std::vector pubkey_vec(pubkey, pubkey + 1184); - auto kyber768 = std::make_unique(pubkey_vec, Botan::KyberMode::Kyber768_R3); - *key = new botan_pubkey_struct(std::move(kyber768)); - return BOTAN_FFI_SUCCESS; - }); - case 1568: - return ffi_guard_thunk(__func__, [=]() -> int { - const std::vector pubkey_vec(pubkey, pubkey + 1568); - auto kyber1024 = std::make_unique(pubkey_vec, Botan::KyberMode::Kyber1024_R3); - *key = new botan_pubkey_struct(std::move(kyber1024)); - return BOTAN_FFI_SUCCESS; - }); - default: - BOTAN_UNUSED(key, pubkey, key_len); - return BOTAN_FFI_ERROR_BAD_PARAMETER; + + const auto mode = [](size_t len) -> std::optional { + if(len == 800) { + return Botan::KyberMode::Kyber512_R3; + } else if(len == 1184) { + return Botan::KyberMode::Kyber768_R3; + } else if(len == 1568) { + return Botan::KyberMode::Kyber1024_R3; + } else { + return {}; + } + }(key_len); + + if(mode.has_value()) { + return ffi_guard_thunk(__func__, [=]() -> int { + auto kyber = std::make_unique(std::span{pubkey, key_len}, *mode); + return ffi_new_object(key, std::move(kyber)); + }); + } else { + return BOTAN_FFI_ERROR_BAD_PARAMETER; } #else BOTAN_UNUSED(key, pubkey, key_len); @@ -1059,7 +1304,7 @@ int botan_privkey_load_ml_kem(botan_privkey_t* key, const uint8_t privkey[], size_t key_len, const char* mlkem_mode) { #if defined(BOTAN_HAS_ML_KEM) - if(key == nullptr || privkey == nullptr || mlkem_mode == nullptr) { + if(Botan::any_null_pointers(key, privkey, mlkem_mode)) { return BOTAN_FFI_ERROR_NULL_POINTER; } @@ -1072,8 +1317,7 @@ } auto mlkem_key = std::make_unique(std::span{privkey, key_len}, mode); - *key = new botan_privkey_struct(std::move(mlkem_key)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(mlkem_key)); }); #else BOTAN_UNUSED(key, key_len, privkey, mlkem_mode); @@ -1083,7 +1327,7 @@ int botan_pubkey_load_ml_kem(botan_pubkey_t* key, const uint8_t pubkey[], size_t key_len, const char* mlkem_mode) { #if defined(BOTAN_HAS_ML_KEM) - if(key == nullptr || pubkey == nullptr || mlkem_mode == nullptr) { + if(Botan::any_null_pointers(key, pubkey, mlkem_mode)) { return BOTAN_FFI_ERROR_NULL_POINTER; } @@ -1096,8 +1340,7 @@ } auto mlkem_key = std::make_unique(std::span{pubkey, key_len}, mode.mode()); - *key = new botan_pubkey_struct(std::move(mlkem_key)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(mlkem_key)); }); #else BOTAN_UNUSED(key, key_len, pubkey, mlkem_mode); @@ -1111,7 +1354,7 @@ int botan_privkey_load_ml_dsa(botan_privkey_t* key, const uint8_t privkey[], size_t key_len, const char* mldsa_mode) { #if defined(BOTAN_HAS_ML_DSA) - if(key == nullptr || privkey == nullptr || mldsa_mode == nullptr) { + if(Botan::any_null_pointers(key, privkey, mldsa_mode)) { return BOTAN_FFI_ERROR_NULL_POINTER; } @@ -1124,8 +1367,7 @@ } auto mldsa_key = std::make_unique(std::span{privkey, key_len}, mode); - *key = new botan_privkey_struct(std::move(mldsa_key)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(mldsa_key)); }); #else BOTAN_UNUSED(key, key_len, privkey, mldsa_mode); @@ -1135,7 +1377,7 @@ int botan_pubkey_load_ml_dsa(botan_pubkey_t* key, const uint8_t pubkey[], size_t key_len, const char* mldsa_mode) { #if defined(BOTAN_HAS_ML_DSA) - if(key == nullptr || pubkey == nullptr || mldsa_mode == nullptr) { + if(Botan::any_null_pointers(key, pubkey, mldsa_mode)) { return BOTAN_FFI_ERROR_NULL_POINTER; } @@ -1148,8 +1390,7 @@ } auto mldsa_key = std::make_unique(std::span{pubkey, key_len}, mode); - *key = new botan_pubkey_struct(std::move(mldsa_key)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(mldsa_key)); }); #else BOTAN_UNUSED(key, key_len, pubkey, mldsa_mode); @@ -1163,7 +1404,7 @@ int botan_privkey_load_slh_dsa(botan_privkey_t* key, const uint8_t privkey[], size_t key_len, const char* slhdsa_mode) { #if defined(BOTAN_HAS_SLH_DSA_WITH_SHA2) || defined(BOTAN_HAS_SLH_DSA_WITH_SHAKE) - if(key == nullptr || privkey == nullptr || slhdsa_mode == nullptr) { + if(Botan::any_null_pointers(key, privkey, slhdsa_mode)) { return BOTAN_FFI_ERROR_NULL_POINTER; } @@ -1176,8 +1417,7 @@ } auto slhdsa_key = std::make_unique(std::span{privkey, key_len}, mode); - *key = new botan_privkey_struct(std::move(slhdsa_key)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(slhdsa_key)); }); #else BOTAN_UNUSED(key, key_len, privkey, slhdsa_mode); @@ -1187,7 +1427,7 @@ int botan_pubkey_load_slh_dsa(botan_pubkey_t* key, const uint8_t pubkey[], size_t key_len, const char* slhdsa_mode) { #if defined(BOTAN_HAS_SLH_DSA_WITH_SHA2) || defined(BOTAN_HAS_SLH_DSA_WITH_SHAKE) - if(key == nullptr || pubkey == nullptr || slhdsa_mode == nullptr) { + if(Botan::any_null_pointers(key, pubkey, slhdsa_mode)) { return BOTAN_FFI_ERROR_NULL_POINTER; } @@ -1200,8 +1440,7 @@ } auto mldsa_key = std::make_unique(std::span{pubkey, key_len}, mode); - *key = new botan_pubkey_struct(std::move(mldsa_key)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(mldsa_key)); }); #else BOTAN_UNUSED(key, key_len, pubkey, slhdsa_mode); @@ -1215,7 +1454,7 @@ int botan_privkey_load_frodokem(botan_privkey_t* key, const uint8_t privkey[], size_t key_len, const char* frodo_mode) { #if defined(BOTAN_HAS_FRODOKEM) - if(key == nullptr || privkey == nullptr || frodo_mode == nullptr) { + if(Botan::any_null_pointers(key, privkey, frodo_mode)) { return BOTAN_FFI_ERROR_NULL_POINTER; } @@ -1224,8 +1463,7 @@ return ffi_guard_thunk(__func__, [=]() -> int { const auto mode = Botan::FrodoKEMMode(frodo_mode); auto frodo_key = std::make_unique(std::span{privkey, key_len}, mode); - *key = new botan_privkey_struct(std::move(frodo_key)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(frodo_key)); }); #else BOTAN_UNUSED(key, privkey, key_len, frodo_mode); @@ -1235,7 +1473,7 @@ int botan_pubkey_load_frodokem(botan_pubkey_t* key, const uint8_t pubkey[], size_t key_len, const char* frodo_mode) { #if defined(BOTAN_HAS_FRODOKEM) - if(key == nullptr || pubkey == nullptr || frodo_mode == nullptr) { + if(Botan::any_null_pointers(key, pubkey, frodo_mode)) { return BOTAN_FFI_ERROR_NULL_POINTER; } @@ -1244,8 +1482,7 @@ return ffi_guard_thunk(__func__, [=]() -> int { const auto mode = Botan::FrodoKEMMode(frodo_mode); auto frodo_key = std::make_unique(std::span{pubkey, key_len}, mode); - *key = new botan_pubkey_struct(std::move(frodo_key)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(frodo_key)); }); #else BOTAN_UNUSED(key, pubkey, key_len, frodo_mode); @@ -1262,7 +1499,7 @@ size_t key_len, const char* cmce_mode) { #if defined(BOTAN_HAS_CLASSICMCELIECE) - if(key == nullptr || privkey == nullptr || cmce_mode == nullptr) { + if(Botan::any_null_pointers(key, privkey, cmce_mode)) { return BOTAN_FFI_ERROR_NULL_POINTER; } @@ -1271,8 +1508,7 @@ return ffi_guard_thunk(__func__, [=]() -> int { const auto mode = Botan::Classic_McEliece_Parameter_Set::from_string(cmce_mode); auto cmce_key = std::make_unique(std::span{privkey, key_len}, mode); - *key = new botan_privkey_struct(std::move(cmce_key)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(cmce_key)); }); #else BOTAN_UNUSED(key, privkey, key_len, cmce_mode); @@ -1285,7 +1521,7 @@ size_t key_len, const char* cmce_mode) { #if defined(BOTAN_HAS_CLASSICMCELIECE) - if(key == nullptr || pubkey == nullptr || cmce_mode == nullptr) { + if(Botan::any_null_pointers(key, pubkey, cmce_mode)) { return BOTAN_FFI_ERROR_NULL_POINTER; } @@ -1294,8 +1530,7 @@ return ffi_guard_thunk(__func__, [=]() -> int { const auto mode = Botan::Classic_McEliece_Parameter_Set::from_string(cmce_mode); auto cmce_key = std::make_unique(std::span{pubkey, key_len}, mode); - *key = new botan_pubkey_struct(std::move(cmce_key)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(key, std::move(cmce_key)); }); #else BOTAN_UNUSED(key, pubkey, key_len, cmce_mode); @@ -1319,6 +1554,8 @@ #endif } +// NOLINTEND(misc-misplaced-const) + int botan_privkey_create_mceliece(botan_privkey_t* key_obj, botan_rng_t rng_obj, size_t n, size_t t) { const std::string mce_params = std::to_string(n) + "," + std::to_string(t); return botan_privkey_create(key_obj, "McEliece", mce_params.c_str(), rng_obj); diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_rng.cpp botan3-3.12.0+dfsg/src/lib/ffi/ffi_rng.cpp --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_rng.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_rng.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,5 @@ /* -* (C) 2015,2017 Jack Lloyd +* (C) 2015,2017,2026 Jack Lloyd * (C) 2021 René Fischer * * Botan is released under the Simplified BSD License (see license.txt) @@ -15,6 +15,10 @@ #include #include +#if defined(BOTAN_HAS_HMAC_DRBG) + #include +#endif + #if defined(BOTAN_HAS_PROCESSOR_RNG) #include #endif @@ -22,6 +26,7 @@ #if defined(BOTAN_HAS_JITTER_RNG) #include #endif + #if defined(BOTAN_HAS_ESDM_RNG) #include #endif @@ -36,7 +41,7 @@ return BOTAN_FFI_ERROR_NULL_POINTER; } - const std::string rng_type_s(rng_type ? rng_type : "system"); + const std::string rng_type_s(rng_type != nullptr ? rng_type : "system"); std::unique_ptr rng; @@ -69,8 +74,7 @@ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; } - *rng_out = new botan_rng_struct(std::move(rng)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(rng_out, std::move(rng)); }); } @@ -93,19 +97,18 @@ return BOTAN_FFI_ERROR_NULL_POINTER; } - class Custom_RNG : public Botan::RandomNumberGenerator { + class Custom_RNG final : public Botan::RandomNumberGenerator { public: Custom_RNG(std::string_view name, void* context, int (*get_cb)(void* context, uint8_t* out, size_t out_len), int (*add_entropy_cb)(void* context, const uint8_t input[], size_t length), void (*destroy_cb)(void* context)) : - m_name(name) { - m_context = context; - m_get_cb = get_cb; - m_add_entropy_cb = add_entropy_cb; - m_destroy_cb = destroy_cb; - } + m_name(name), + m_context(context), + m_get_cb(get_cb), + m_add_entropy_cb(add_entropy_cb), + m_destroy_cb(destroy_cb) {} ~Custom_RNG() override { if(m_destroy_cb) { @@ -121,15 +124,15 @@ protected: void fill_bytes_with_input(std::span output, std::span input) override { if(accepts_input() && !input.empty()) { - int rc = m_add_entropy_cb(m_context, input.data(), input.size()); - if(rc) { + const int rc = m_add_entropy_cb(m_context, input.data(), input.size()); + if(rc != 0) { throw Botan::Invalid_State("Failed to add entropy via C callback, rc=" + std::to_string(rc)); } } if(!output.empty()) { - int rc = m_get_cb(m_context, output.data(), output.size()); - if(rc) { + const int rc = m_get_cb(m_context, output.data(), output.size()); + if(rc != 0) { throw Botan::Invalid_State("Failed to get random from C callback, rc=" + std::to_string(rc)); } } @@ -154,8 +157,7 @@ auto rng = std::make_unique(rng_name, context, get_cb, add_entropy_cb, destroy_cb); - *rng_out = new botan_rng_struct(std::move(rng)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(rng_out, std::move(rng)); }); } @@ -164,10 +166,16 @@ } int botan_rng_get(botan_rng_t rng, uint8_t* out, size_t out_len) { + if(out_len > 0 && out == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return BOTAN_FFI_VISIT(rng, [=](auto& r) { r.randomize(out, out_len); }); } int botan_system_rng_get(uint8_t* out, size_t out_len) { + if(out_len > 0 && out == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return ffi_guard_thunk(__func__, [=]() -> int { Botan::system_rng().randomize(out, out_len); return BOTAN_FFI_SUCCESS; @@ -179,10 +187,54 @@ } int botan_rng_add_entropy(botan_rng_t rng, const uint8_t* input, size_t len) { + if(len > 0 && input == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return BOTAN_FFI_VISIT(rng, [=](auto& r) { r.add_entropy(input, len); }); } int botan_rng_reseed_from_rng(botan_rng_t rng, botan_rng_t source_rng, size_t bits) { return BOTAN_FFI_VISIT(rng, [=](auto& r) { r.reseed_from_rng(safe_get(source_rng), bits); }); } + +int botan_rng_init_drbg(botan_rng_t* rng_out, const char* drbg_name, const uint8_t* seed, size_t seed_len) { + return ffi_guard_thunk(__func__, [=]() -> int { + if(rng_out == nullptr || drbg_name == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + if(seed_len > 0 && seed == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + std::unique_ptr drbg; + const std::string name(drbg_name); + +#if defined(BOTAN_HAS_HMAC_DRBG) + if(name.starts_with("HMAC_DRBG(") && name.ends_with(")") && name.size() > 12) { + const std::string hash = name.substr(10, name.size() - 11); + drbg = std::make_unique(hash); + } +#endif + + if(!drbg) { + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; + } + + drbg->initialize_with(std::span(seed, seed_len)); + // Upcast to RandomNumberGenerator for the FFI object + std::unique_ptr rng(std::move(drbg)); + return ffi_new_object(rng_out, std::move(rng)); + }); +} + +int botan_rng_generate_with_input( + botan_rng_t rng, uint8_t* out, size_t out_len, const uint8_t* addl_input, size_t addl_len) { + if(out_len > 0 && out == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + if(addl_len > 0 && addl_input == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + return BOTAN_FFI_VISIT(rng, [=](auto& r) { r.randomize_with_input({out, out_len}, {addl_input, addl_len}); }); +} } diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_srp6.cpp botan3-3.12.0+dfsg/src/lib/ffi/ffi_srp6.cpp --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_srp6.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_srp6.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,7 @@ #include +#include #include #include @@ -30,10 +31,11 @@ int botan_srp6_server_session_init(botan_srp6_server_session_t* srp6) { #if defined(BOTAN_HAS_SRP6) - return ffi_guard_thunk(__func__, [=]() -> int { - *srp6 = new botan_srp6_server_session_struct(std::make_unique()); - return BOTAN_FFI_SUCCESS; - }); + if(srp6 == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + return ffi_guard_thunk( + __func__, [=]() -> int { return ffi_new_object(srp6, std::make_unique()); }); #else BOTAN_UNUSED(srp6); return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; @@ -46,12 +48,12 @@ int botan_srp6_group_size(const char* group_id, size_t* group_p_bytes) { #if defined(BOTAN_HAS_SRP6) - if(group_id == nullptr || group_p_bytes == nullptr) { + if(any_null_pointers(group_id, group_p_bytes)) { return BOTAN_FFI_ERROR_NULL_POINTER; } return ffi_guard_thunk(__func__, [=]() -> int { - auto group = Botan::DL_Group::from_name(group_id); + const auto group = Botan::DL_Group::from_name(group_id); *group_p_bytes = group.p_bytes(); return BOTAN_FFI_SUCCESS; }); @@ -71,14 +73,20 @@ size_t* b_pub_len) { #if defined(BOTAN_HAS_SRP6) return BOTAN_FFI_VISIT(srp6, [=](auto& s) -> int { - if(!verifier || !group_id || !hash_id || !rng_obj) { + if(any_null_pointers(verifier, group_id, hash_id, rng_obj)) { return BOTAN_FFI_ERROR_NULL_POINTER; } try { + const auto group = Botan::DL_Group::from_name(group_id); + const auto rc = check_and_prepare_output_space(b_pub, b_pub_len, group.p_bytes()); + if(rc != BOTAN_FFI_SUCCESS) { + return rc; + } + Botan::RandomNumberGenerator& rng = safe_get(rng_obj); auto v_bn = Botan::BigInt::from_bytes(std::span{verifier, verifier_len}); - auto b_pub_bn = s.step1(v_bn, group_id, hash_id, rng); - return write_vec_output(b_pub, b_pub_len, b_pub_bn.serialize()); + auto b_pub_bn = s.step1(v_bn, group, hash_id, group.exponent_bits(), rng); + return write_vec_output(b_pub, b_pub_len, b_pub_bn.serialize(group.p_bytes())); } catch(Botan::Decoding_Error&) { return BOTAN_FFI_ERROR_BAD_PARAMETER; } catch(Botan::Lookup_Error&) { @@ -99,7 +107,7 @@ return BOTAN_FFI_ERROR_NULL_POINTER; } try { - Botan::BigInt a_bn = Botan::BigInt::from_bytes({a, a_len}); + const Botan::BigInt a_bn = Botan::BigInt::from_bytes({a, a_len}); auto key_sk = s.step2(a_bn); return write_vec_output(key, key_len, key_sk.bits_of()); } catch(Botan::Decoding_Error&) { @@ -122,13 +130,15 @@ size_t* verifier_len) { #if defined(BOTAN_HAS_SRP6) return ffi_guard_thunk(__func__, [=]() -> int { - if(!username || !password || !salt || !group_id || !hash_id) { + if(any_null_pointers(username, password, salt, group_id, hash_id)) { return BOTAN_FFI_ERROR_NULL_POINTER; } try { - std::vector salt_vec(salt, salt + salt_len); - auto verifier_bn = Botan::srp6_generate_verifier(username, password, salt_vec, group_id, hash_id); - return write_vec_output(verifier, verifier_len, verifier_bn.serialize()); + const std::vector salt_vec(salt, salt + salt_len); + const auto group = Botan::DL_Group::from_name(group_id); + const size_t p_bytes = group.p_bytes(); + auto verifier_bn = Botan::srp6_generate_verifier(username, password, salt_vec, group, hash_id); + return write_vec_output(verifier, verifier_len, verifier_bn.serialize(p_bytes)); } catch(Botan::Lookup_Error&) { return BOTAN_FFI_ERROR_BAD_PARAMETER; } @@ -155,15 +165,17 @@ size_t* K_len) { #if defined(BOTAN_HAS_SRP6) return ffi_guard_thunk(__func__, [=]() -> int { - if(!identity || !password || !salt || !group_id || !hash_id || !b || !rng_obj) { + if(any_null_pointers(identity, password, salt, group_id, hash_id, b, rng_obj)) { return BOTAN_FFI_ERROR_NULL_POINTER; } try { - std::vector saltv(salt, salt + salt_len); + const std::vector saltv(salt, salt + salt_len); Botan::RandomNumberGenerator& rng = safe_get(rng_obj); auto b_bn = Botan::BigInt::from_bytes({b, b_len}); - auto [A_bn, K_sk] = Botan::srp6_client_agree(identity, password, group_id, hash_id, saltv, b_bn, rng); - auto ret_a = write_vec_output(A, A_len, A_bn.serialize()); + const auto group = Botan::DL_Group::from_name(group_id); + const size_t a_bits = group.exponent_bits(); + auto [A_bn, K_sk] = Botan::srp6_client_agree(identity, password, group, hash_id, saltv, b_bn, a_bits, rng); + auto ret_a = write_vec_output(A, A_len, A_bn.serialize(group.p_bytes())); auto ret_k = write_vec_output(K, K_len, K_sk.bits_of()); if(ret_a != BOTAN_FFI_SUCCESS) { return ret_a; diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_totp.cpp botan3-3.12.0+dfsg/src/lib/ffi/ffi_totp.cpp --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_totp.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_totp.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,6 +6,7 @@ #include +#include #include #if defined(BOTAN_HAS_TOTP) @@ -33,9 +34,7 @@ #if defined(BOTAN_HAS_TOTP) return ffi_guard_thunk(__func__, [=]() -> int { auto otp = std::make_unique(key, key_len, hash_algo, digits, time_step); - *totp = new botan_totp_struct(std::move(otp)); - - return BOTAN_FFI_SUCCESS; + return ffi_new_object(totp, std::move(otp)); }); #else BOTAN_UNUSED(totp, key, key_len, hash_algo, digits, time_step); diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_tpm2.cpp botan3-3.12.0+dfsg/src/lib/ffi/ffi_tpm2.cpp --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_tpm2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_tpm2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,7 @@ #include +#include #include #include #include @@ -88,8 +89,7 @@ }(); ctx->ctx = Botan::TPM2::Context::create(std::move(tcti)); - *ctx_out = new botan_tpm2_ctx_struct(std::move(ctx)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(ctx_out, std::move(ctx)); }); #else BOTAN_UNUSED(ctx_out, tcti_nameconf); @@ -122,8 +122,7 @@ }(); ctx->ctx = Botan::TPM2::Context::create(std::move(tcti_name_str), std::move(tcti_conf_str)); - *ctx_out = new botan_tpm2_ctx_struct(std::move(ctx)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(ctx_out, std::move(ctx)); }); #else BOTAN_UNUSED(ctx_out, tcti_name, tcti_conf); @@ -140,8 +139,7 @@ auto ctx = std::make_unique(); ctx->ctx = Botan::TPM2::Context::create(esys_ctx); - *ctx_out = new botan_tpm2_ctx_struct(std::move(ctx)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(ctx_out, std::move(ctx)); }); #else BOTAN_UNUSED(ctx_out, esys_ctx); @@ -157,8 +155,7 @@ // The lifetime of the RNG used for the crypto backend should be managed // by the TPM2::Context. Here, we just need to trust the user that they // keep the passed-in RNG instance intact for the lifetime of the context. - std::shared_ptr rng_ptr(&rng_ref, [](auto*) {}); - ctx_wrapper.ctx->use_botan_crypto_backend(rng_ptr); + ctx_wrapper.ctx->use_botan_crypto_backend(std::shared_ptr(&rng_ref, [](auto*) {})); return BOTAN_FFI_SUCCESS; }); #else @@ -168,7 +165,7 @@ } /** - * Frees all resouces of a TPM2 context + * Frees all resources of a TPM2 context * @param ctx TPM2 context * @return 0 on success */ @@ -194,9 +191,8 @@ // Here, we just need to trust the user that they keep the passed-in RNG // instance intact for the lifetime of the context. - std::shared_ptr rng_ptr(&rng_ref, [](auto*) {}); - *cbs_out = new botan_tpm2_crypto_backend_state_struct(Botan::TPM2::use_botan_crypto_backend(esys_ctx, rng_ptr)); - return BOTAN_FFI_SUCCESS; + const std::shared_ptr rng_ptr(&rng_ref, [](auto*) {}); + return ffi_new_object(cbs_out, Botan::TPM2::use_botan_crypto_backend(esys_ctx, rng_ptr)); }); #else BOTAN_UNUSED(cbs_out, esys_ctx, rng); @@ -224,9 +220,8 @@ return BOTAN_FFI_ERROR_NULL_POINTER; } - *rng_out = new botan_rng_struct( - std::make_unique(ctx_wrapper.ctx, sessions(s1, s2, s3))); - return BOTAN_FFI_SUCCESS; + return ffi_new_object( + rng_out, std::make_unique(ctx_wrapper.ctx, sessions(s1, s2, s3))); }); #else BOTAN_UNUSED(rng_out, ctx, s1, s2, s3); @@ -243,8 +238,7 @@ auto session = std::make_unique(); session->session = Botan::TPM2::Session::unauthenticated_session(ctx_wrapper.ctx); - *session_out = new botan_tpm2_session_struct(std::move(session)); - return BOTAN_FFI_SUCCESS; + return ffi_new_object(session_out, std::move(session)); }); #else BOTAN_UNUSED(session_out, ctx); diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_util.h botan3-3.12.0+dfsg/src/lib/ffi/ffi_util.h --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_util.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_util.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,14 +9,17 @@ #include #include -#include +#include +#include #include -#include +#include +#include #include -#include namespace Botan_FFI { +using Botan::any_null_pointers; + class BOTAN_UNSTABLE_API FFI_Error final : public Botan::Exception { public: FFI_Error(std::string_view what, int err_code) : Exception("FFI error", what), m_err_code(err_code) {} @@ -32,13 +35,18 @@ template struct botan_struct { public: - botan_struct(std::unique_ptr obj) : m_magic(MAGIC), m_obj(std::move(obj)) {} + explicit botan_struct(std::unique_ptr obj) : m_magic(MAGIC), m_obj(std::move(obj)) {} virtual ~botan_struct() { m_magic = 0; - m_obj.reset(); + m_obj.reset(); // NOLINT(*-ambiguous-smartptr-reset-call) } + botan_struct(const botan_struct& other) = delete; + botan_struct(botan_struct&& other) = delete; + botan_struct& operator=(const botan_struct& other) = delete; + botan_struct& operator=(botan_struct&& other) = delete; + bool magic_ok() const { return (m_magic == MAGIC); } T* unsafe_get() const { return m_obj.get(); } @@ -48,6 +56,8 @@ std::unique_ptr m_obj; }; +// NOLINTBEGIN(*-macro-usage) + #define BOTAN_FFI_DECLARE_STRUCT(NAME, TYPE, MAGIC) \ struct NAME final : public Botan_FFI::botan_struct { \ explicit NAME(std::unique_ptr x) : botan_struct(std::move(x)) {} \ @@ -56,15 +66,21 @@ #define BOTAN_FFI_DECLARE_DUMMY_STRUCT(NAME, MAGIC) \ struct NAME final : public Botan_FFI::botan_struct {} +// NOLINTEND(*-macro-usage) + // Declared in ffi.cpp -int ffi_error_exception_thrown(const char* func_name, const char* exn, int rc = BOTAN_FFI_ERROR_EXCEPTION_THROWN); +void ffi_clear_last_exception(); + +int ffi_error_exception_thrown(const char* func_name, const char* exn, int rc); + +int ffi_error_exception_thrown(const char* func_name, const char* exn, Botan::ErrorType err); template T& safe_get(botan_struct* p) { if(!p) { throw FFI_Error("Null pointer argument", BOTAN_FFI_ERROR_NULL_POINTER); } - if(p->magic_ok() == false) { + if(!p->magic_ok()) { throw FFI_Error("Bad magic in ffi object", BOTAN_FFI_ERROR_INVALID_OBJECT); } @@ -75,7 +91,24 @@ throw FFI_Error("Invalid object pointer", BOTAN_FFI_ERROR_INVALID_OBJECT); } -int ffi_guard_thunk(const char* func_name, const std::function& thunk); +template +int ffi_guard_thunk(const char* func_name, T thunk) { + ffi_clear_last_exception(); + + try { + return thunk(); + } catch(std::bad_alloc&) { + return ffi_error_exception_thrown(func_name, "bad_alloc", BOTAN_FFI_ERROR_OUT_OF_MEMORY); + } catch(Botan_FFI::FFI_Error& e) { + return ffi_error_exception_thrown(func_name, e.what(), e.error_code()); + } catch(Botan::Exception& e) { + return ffi_error_exception_thrown(func_name, e.what(), e.error_type()); + } catch(std::exception& e) { + return ffi_error_exception_thrown(func_name, e.what(), BOTAN_FFI_ERROR_EXCEPTION_THROWN); + } catch(...) { + return ffi_error_exception_thrown(func_name, "unknown exception", BOTAN_FFI_ERROR_EXCEPTION_THROWN); + } +} template int botan_ffi_visit(botan_struct* o, F func, const char* func_name) { @@ -87,7 +120,7 @@ return BOTAN_FFI_ERROR_NULL_POINTER; } - if(o->magic_ok() == false) { + if(!o->magic_ok()) { return BOTAN_FFI_ERROR_INVALID_OBJECT; } @@ -121,6 +154,7 @@ // } // // [...] // } +// NOLINTNEXTLINE(*-macro-usage) #define BOTAN_FFI_VISIT(obj, lambda) botan_ffi_visit(obj, lambda, __func__) template @@ -131,23 +165,40 @@ return BOTAN_FFI_SUCCESS; } - if(obj->magic_ok() == false) { + if(!obj->magic_ok()) { return BOTAN_FFI_ERROR_INVALID_OBJECT; } - delete obj; + delete obj; // NOLINT(*-owning-memory) return BOTAN_FFI_SUCCESS; }); } +template +BOTAN_FFI_ERROR ffi_new_object(T* obj, Args&&... args) { + if(obj == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + // NOLINTNEXTLINE(*-owning-memory) + *obj = new std::remove_pointer_t(std::forward(args)...); + return BOTAN_FFI_SUCCESS; +} + +// NOLINTNEXTLINE(*-macro-usage) #define BOTAN_FFI_CHECKED_DELETE(o) ffi_delete_object(o, __func__) -template -inline int invoke_view_callback(botan_view_bin_fn view, botan_view_ctx ctx, const std::vector& buf) { +inline int invoke_view_callback(botan_view_bin_fn view, botan_view_ctx ctx, std::span buf) { + if(view == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return view(ctx, buf.data(), buf.size()); } -inline int invoke_view_callback(botan_view_str_fn view, botan_view_ctx ctx, std::string_view str) { +// Should not be std::string_view as we rely on being able to NULL terminate +inline int invoke_view_callback(botan_view_str_fn view, botan_view_ctx ctx, const std::string& str) { + if(view == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } return view(ctx, str.data(), str.size() + 1); } @@ -161,9 +212,7 @@ template int copy_view_bin(uint8_t out[], size_t* out_len, Fn fn, Args... args) { - botan_view_bounce_struct ctx; - ctx.out_ptr = out; - ctx.out_len = out_len; + botan_view_bounce_struct ctx{out, out_len}; return fn(args..., &ctx, botan_view_bin_bounce_fn); } @@ -172,46 +221,52 @@ if(fn == nullptr) { return BOTAN_FFI_ERROR_NULL_POINTER; } - botan_view_bounce_struct ctx; - ctx.out_ptr = out; - ctx.out_len = out_len; + botan_view_bounce_struct ctx{out, out_len}; return fn(args..., &ctx, botan_view_str_bounce_fn); } -inline int write_output(uint8_t out[], size_t* out_len, const uint8_t buf[], size_t buf_len) { +template + requires(sizeof(T) == 1) +inline int check_and_prepare_output_space(T out[], size_t* out_len, size_t required_len) { if(out_len == nullptr) { return BOTAN_FFI_ERROR_NULL_POINTER; } const size_t avail = *out_len; - *out_len = buf_len; + *out_len = required_len; - if((avail >= buf_len) && (out != nullptr)) { - Botan::copy_mem(out, buf, buf_len); - return BOTAN_FFI_SUCCESS; - } else { + if(avail < required_len || out == nullptr) { if(out != nullptr) { - Botan::clear_mem(out, avail); + std::memset(out, 0, sizeof(T) * avail); } return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE; + } else { + return BOTAN_FFI_SUCCESS; } } -template -int write_vec_output(uint8_t out[], size_t* out_len, const std::vector& buf) { - return write_output(out, out_len, buf.data(), buf.size()); -} +template +inline int write_output(T out[], size_t* out_len, const T buf[], size_t buf_len) { + static_assert(sizeof(T) == 1, "T should be either uint8_t or char"); + + const auto rc = check_and_prepare_output_space(out, out_len, buf_len); + if(rc != BOTAN_FFI_SUCCESS) { + return rc; + } + + if(out != nullptr) { + std::memcpy(out, buf, sizeof(T) * buf_len); + } -inline int write_str_output(uint8_t out[], size_t* out_len, std::string_view str) { - return write_output(out, out_len, Botan::cast_char_ptr_to_uint8(str.data()), str.size() + 1); + return BOTAN_FFI_SUCCESS; } -inline int write_str_output(char out[], size_t* out_len, std::string_view str) { - return write_str_output(Botan::cast_char_ptr_to_uint8(out), out_len, str); +inline int write_vec_output(uint8_t out[], size_t* out_len, std::span buf) { + return write_output(out, out_len, buf.data(), buf.size()); } -inline int write_str_output(char out[], size_t* out_len, const std::vector& str_vec) { - return write_output(Botan::cast_char_ptr_to_uint8(out), out_len, str_vec.data(), str_vec.size()); +inline int write_str_output(char out[], size_t* out_len, const std::string& str) { + return write_output(out, out_len, str.data(), str.size() + 1); } } // namespace Botan_FFI diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_xof.cpp botan3-3.12.0+dfsg/src/lib/ffi/ffi_xof.cpp --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_xof.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_xof.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,96 @@ +/* +* (C) 2025 Jack Lloyd +* 2025 René Meusel, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +extern "C" { + +using namespace Botan_FFI; + +BOTAN_FFI_DECLARE_STRUCT(botan_xof_struct, Botan::XOF, 0x0f1303a0); + +int botan_xof_init(botan_xof_t* this_xof, const char* xof_name, uint32_t flags) { + return ffi_guard_thunk(__func__, [=]() -> int { + if(Botan::any_null_pointers(this_xof, xof_name) || *xof_name == 0) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + if(flags != 0) { + return BOTAN_FFI_ERROR_BAD_FLAG; + } + + auto xof = Botan::XOF::create(xof_name); + if(xof == nullptr) { + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; + } + + ffi_new_object(this_xof, std::move(xof)); + return BOTAN_FFI_SUCCESS; + }); +} + +// NOLINTNEXTLINE(misc-misplaced-const) +int botan_xof_copy_state(botan_xof_t* dest, const botan_xof_t this_xof) { + if(dest == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + return BOTAN_FFI_VISIT(this_xof, [=](const auto& src) { return ffi_new_object(dest, src.copy_state()); }); +} + +int botan_xof_block_size(botan_xof_t this_xof, size_t* out) { + if(Botan::any_null_pointers(out)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + return BOTAN_FFI_VISIT(this_xof, [=](const auto& xof) { *out = xof.block_size(); }); +} + +int botan_xof_name(botan_xof_t this_xof, char* name, size_t* name_len) { + if(Botan::any_null_pointers(name_len)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + return BOTAN_FFI_VISIT(this_xof, [=](const auto& xof) { return write_str_output(name, name_len, xof.name()); }); +} + +int botan_xof_accepts_input(botan_xof_t this_xof) { + return BOTAN_FFI_VISIT(this_xof, [=](const auto& xof) { return xof.accepts_input() ? 1 : 0; }); +} + +int botan_xof_clear(botan_xof_t this_xof) { + return BOTAN_FFI_VISIT(this_xof, [](auto& xof) { xof.clear(); }); +} + +int botan_xof_update(botan_xof_t this_xof, const uint8_t* in, size_t in_len) { + if(in_len == 0) { + return 0; + } + + if(Botan::any_null_pointers(in)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + return BOTAN_FFI_VISIT(this_xof, [=](auto& xof) { xof.update({in, in_len}); }); +} + +int botan_xof_output(botan_xof_t this_xof, uint8_t* out, size_t out_len) { + if(out_len == 0) { + return 0; + } + + if(Botan::any_null_pointers(out)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + return BOTAN_FFI_VISIT(this_xof, [=](auto& xof) { xof.output({out, out_len}); }); +} + +int botan_xof_destroy(botan_xof_t xof) { + return BOTAN_FFI_CHECKED_DELETE(xof); +} +} diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/ffi_zfec.cpp botan3-3.12.0+dfsg/src/lib/ffi/ffi_zfec.cpp --- botan3-3.7.1+dfsg/src/lib/ffi/ffi_zfec.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/ffi_zfec.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,6 +6,7 @@ #include +#include #include #if defined(BOTAN_HAS_ZFEC) @@ -15,6 +16,9 @@ extern "C" { int botan_zfec_encode(size_t K, size_t N, const uint8_t* input, size_t size, uint8_t** outputs) { + if(Botan::any_null_pointers(input, outputs)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } #if defined(BOTAN_HAS_ZFEC) return Botan_FFI::ffi_guard_thunk(__func__, [=]() -> int { Botan::ZFEC(K, N).encode(input, size, [=](size_t index, const uint8_t block[], size_t blockSize) -> void { @@ -30,6 +34,9 @@ int botan_zfec_decode( size_t K, size_t N, const size_t* indexes, uint8_t* const* const inputs, size_t shareSize, uint8_t** outputs) { + if(Botan::any_null_pointers(indexes, inputs, outputs)) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } #if defined(BOTAN_HAS_ZFEC) return Botan_FFI::ffi_guard_thunk(__func__, [=]() -> int { std::map shares; diff -Nru botan3-3.7.1+dfsg/src/lib/ffi/info.txt botan3-3.12.0+dfsg/src/lib/ffi/info.txt --- botan3-3.7.1+dfsg/src/lib/ffi/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/ffi/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,10 @@ -FFI -> 20240408 +# When bumping remember to also update +# - BOTAN_FFI_API_VERSION in ffi.h +# - botan_ffi_supports_api in ffi.cpp +# - The table of versions in ffi.rst +# - BOTAN_FFI_VERSION in botan3.py +FFI -> 20260506 @@ -8,7 +13,10 @@ +ffi_cert.h +ffi_ec.h ffi_mp.h +ffi_oid.h ffi_pkey.h ffi_rng.h ffi_util.h @@ -26,6 +34,7 @@ kdf pbkdf pubkey +xof pem bigint sha2_32 @@ -33,4 +42,7 @@ #tls system_rng auto_rng + +# TODO this should be made optional +ec_group diff -Nru botan3-3.7.1+dfsg/src/lib/filters/algo_filt.cpp botan3-3.12.0+dfsg/src/lib/filters/algo_filt.cpp --- botan3-3.7.1+dfsg/src/lib/filters/algo_filt.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/filters/algo_filt.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -27,8 +27,8 @@ } void StreamCipher_Filter::write(const uint8_t input[], size_t length) { - while(length) { - size_t copied = std::min(length, m_buffer.size()); + while(length > 0) { + const size_t copied = std::min(length, m_buffer.size()); m_cipher->cipher(input, m_buffer.data(), copied); send(m_buffer, copied); input += copied; @@ -45,7 +45,7 @@ void Hash_Filter::end_msg() { secure_vector output = m_hash->final(); - if(m_out_len) { + if(m_out_len != 0) { send(output, std::min(m_out_len, output.size())); } else { send(output); @@ -64,7 +64,7 @@ void MAC_Filter::end_msg() { secure_vector output = m_mac->final(); - if(m_out_len) { + if(m_out_len != 0) { send(output, std::min(m_out_len, output.size())); } else { send(output); diff -Nru botan3-3.7.1+dfsg/src/lib/filters/b64_filt.cpp botan3-3.12.0+dfsg/src/lib/filters/b64_filt.cpp --- botan3-3.7.1+dfsg/src/lib/filters/b64_filt.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/filters/b64_filt.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #include #include +#include #include namespace Botan { @@ -20,19 +21,17 @@ m_line_length(line_breaks ? line_length : 0), m_trailing_newline(trailing_newline && line_breaks), m_in(48), - m_out(64), - m_position(0), - m_out_position(0) {} + m_out(64) {} /* * Encode and send a block */ void Base64_Encoder::encode_and_send(const uint8_t input[], size_t length, bool final_inputs) { - while(length) { + while(length > 0) { const size_t proc = std::min(length, m_in.size()); size_t consumed = 0; - size_t produced = base64_encode(cast_uint8_ptr_to_char(m_out.data()), input, proc, consumed, final_inputs); + const size_t produced = base64_encode(cast_uint8_ptr_to_char(m_out.data()), input, proc, consumed, final_inputs); do_output(m_out.data(), produced); @@ -49,9 +48,10 @@ if(m_line_length == 0) { send(input, length); } else { - size_t remaining = length, offset = 0; - while(remaining) { - size_t sent = std::min(m_line_length - m_out_position, remaining); + size_t remaining = length; + size_t offset = 0; + while(remaining > 0) { + const size_t sent = std::min(m_line_length - m_out_position, remaining); send(input + offset, sent); m_out_position += sent; remaining -= sent; @@ -92,7 +92,7 @@ void Base64_Encoder::end_msg() { encode_and_send(m_in.data(), m_position, true); - if(m_trailing_newline || (m_out_position && m_line_length)) { + if(m_trailing_newline || (m_out_position > 0 && m_line_length > 0)) { send('\n'); } @@ -102,14 +102,14 @@ /* * Base64_Decoder Constructor */ -Base64_Decoder::Base64_Decoder(Decoder_Checking c) : m_checking(c), m_in(64), m_out(48), m_position(0) {} +Base64_Decoder::Base64_Decoder(Decoder_Checking c) : m_checking(c), m_in(64), m_out(48) {} /* * Convert some data from Base64 */ void Base64_Decoder::write(const uint8_t input[], size_t length) { - while(length) { - size_t to_copy = std::min(length, m_in.size() - m_position); + while(length > 0) { + const size_t to_copy = std::min(length, m_in.size() - m_position); if(to_copy == 0) { m_in.resize(m_in.size() * 2); m_out.resize(m_out.size() * 2); @@ -118,7 +118,7 @@ m_position += to_copy; size_t consumed = 0; - size_t written = base64_decode( + const size_t written = base64_decode( m_out.data(), cast_uint8_ptr_to_char(m_in.data()), m_position, consumed, false, m_checking != FULL_CHECK); send(m_out, written); @@ -140,7 +140,7 @@ */ void Base64_Decoder::end_msg() { size_t consumed = 0; - size_t written = base64_decode( + const size_t written = base64_decode( m_out.data(), cast_uint8_ptr_to_char(m_in.data()), m_position, consumed, true, m_checking != FULL_CHECK); send(m_out, written); diff -Nru botan3-3.7.1+dfsg/src/lib/filters/basefilt.cpp botan3-3.12.0+dfsg/src/lib/filters/basefilt.cpp --- botan3-3.7.1+dfsg/src/lib/filters/basefilt.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/filters/basefilt.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,19 +12,19 @@ * Chain Constructor */ Chain::Chain(Filter* f1, Filter* f2, Filter* f3, Filter* f4) { - if(f1) { + if(f1 != nullptr) { attach(f1); incr_owns(); } - if(f2) { + if(f2 != nullptr) { attach(f2); incr_owns(); } - if(f3) { + if(f3 != nullptr) { attach(f3); incr_owns(); } - if(f4) { + if(f4 != nullptr) { attach(f4); incr_owns(); } @@ -35,7 +35,7 @@ */ Chain::Chain(Filter* filters[], size_t count) { for(size_t j = 0; j != count; ++j) { - if(filters[j]) { + if(filters[j] != nullptr) { attach(filters[j]); incr_owns(); } diff -Nru botan3-3.7.1+dfsg/src/lib/filters/buf_filt.cpp botan3-3.12.0+dfsg/src/lib/filters/buf_filt.cpp --- botan3-3.7.1+dfsg/src/lib/filters/buf_filt.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/filters/buf_filt.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -32,12 +32,12 @@ * Buffer input into blocks, trying to minimize copying */ void Buffered_Filter::write(const uint8_t input[], size_t input_size) { - if(!input_size) { + if(input_size == 0) { return; } if(m_buffer_pos + input_size >= m_main_block_mod + m_final_minimum) { - size_t to_copy = std::min(m_buffer.size() - m_buffer_pos, input_size); + const size_t to_copy = std::min(m_buffer.size() - m_buffer_pos, input_size); copy_mem(&m_buffer[m_buffer_pos], input, to_copy); m_buffer_pos += to_copy; @@ -58,10 +58,10 @@ } if(input_size >= m_final_minimum) { - size_t full_blocks = (input_size - m_final_minimum) / m_main_block_mod; - size_t to_copy = full_blocks * m_main_block_mod; + const size_t full_blocks = (input_size - m_final_minimum) / m_main_block_mod; + const size_t to_copy = full_blocks * m_main_block_mod; - if(to_copy) { + if(to_copy > 0) { buffered_block(input, to_copy); input += to_copy; @@ -81,10 +81,10 @@ throw Invalid_State("Buffered filter end_msg without enough input"); } - size_t spare_blocks = (m_buffer_pos - m_final_minimum) / m_main_block_mod; + const size_t spare_blocks = (m_buffer_pos - m_final_minimum) / m_main_block_mod; - if(spare_blocks) { - size_t spare_bytes = m_main_block_mod * spare_blocks; + if(spare_blocks > 0) { + const size_t spare_bytes = m_main_block_mod * spare_blocks; buffered_block(m_buffer.data(), spare_bytes); buffered_final(&m_buffer[spare_bytes], m_buffer_pos - spare_bytes); } else { diff -Nru botan3-3.7.1+dfsg/src/lib/filters/cipher_filter.cpp botan3-3.12.0+dfsg/src/lib/filters/cipher_filter.cpp --- botan3-3.7.1+dfsg/src/lib/filters/cipher_filter.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/filters/cipher_filter.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -69,7 +69,7 @@ } void Cipher_Mode_Filter::buffered_block(const uint8_t input[], size_t input_length) { - while(input_length) { + while(input_length > 0) { const size_t take = std::min(m_mode->ideal_granularity(), input_length); m_buffer.assign(input, input + take); diff -Nru botan3-3.7.1+dfsg/src/lib/filters/comp_filter.cpp botan3-3.12.0+dfsg/src/lib/filters/comp_filter.cpp --- botan3-3.7.1+dfsg/src/lib/filters/comp_filter.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/filters/comp_filter.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include +#include #include #include @@ -37,7 +38,7 @@ } void Compression_Filter::write(const uint8_t input[], size_t input_length) { - while(input_length) { + while(input_length > 0) { const size_t take = std::min(m_buffersize, input_length); BOTAN_ASSERT(take > 0, "Consumed something"); @@ -81,7 +82,7 @@ } void Decompression_Filter::write(const uint8_t input[], size_t input_length) { - while(input_length) { + while(input_length > 0) { const size_t take = std::min(m_buffersize, input_length); BOTAN_ASSERT(take > 0, "Consumed something"); diff -Nru botan3-3.7.1+dfsg/src/lib/filters/data_snk.cpp botan3-3.12.0+dfsg/src/lib/filters/data_snk.cpp --- botan3-3.7.1+dfsg/src/lib/filters/data_snk.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/filters/data_snk.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -23,8 +23,8 @@ /* * Write to a stream */ -void DataSink_Stream::write(const uint8_t out[], size_t length) { - m_sink.write(cast_uint8_ptr_to_char(out), length); +void DataSink_Stream::write(const uint8_t buf[], size_t length) { + m_sink.write(cast_uint8_ptr_to_char(buf), length); if(!m_sink.good()) { throw Stream_IO_Error("DataSink_Stream: Failure writing to " + m_identifier); } diff -Nru botan3-3.7.1+dfsg/src/lib/filters/data_snk.h botan3-3.12.0+dfsg/src/lib/filters/data_snk.h --- botan3-3.7.1+dfsg/src/lib/filters/data_snk.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/filters/data_snk.h 2026-05-07 01:38:28.000000000 +0000 @@ -21,12 +21,6 @@ class BOTAN_PUBLIC_API(2, 0) DataSink : public Filter { public: bool attachable() override { return false; } - - DataSink() = default; - ~DataSink() override = default; - - DataSink& operator=(const DataSink&) = delete; - DataSink(const DataSink&) = delete; }; /** @@ -39,7 +33,7 @@ * @param stream the stream to write to * @param name identifier */ - DataSink_Stream(std::ostream& stream, std::string_view name = ""); + BOTAN_FUTURE_EXPLICIT DataSink_Stream(std::ostream& stream, std::string_view name = ""); #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) @@ -49,12 +43,17 @@ * @param use_binary indicates whether to treat the file * as a binary file or not */ - DataSink_Stream(std::string_view pathname, bool use_binary = false); + BOTAN_FUTURE_EXPLICIT DataSink_Stream(std::string_view pathname, bool use_binary = false); #endif + DataSink_Stream(const DataSink_Stream& other) = delete; + DataSink_Stream(DataSink_Stream&& other) = delete; + DataSink_Stream& operator=(const DataSink_Stream& other) = delete; + DataSink_Stream& operator=(DataSink_Stream&& other) = delete; + std::string name() const override { return m_identifier; } - void write(const uint8_t[], size_t) override; + void write(const uint8_t buf[], size_t len) override; void end_msg() override; diff -Nru botan3-3.7.1+dfsg/src/lib/filters/fd_unix/fd_unix.cpp botan3-3.12.0+dfsg/src/lib/filters/fd_unix/fd_unix.cpp --- botan3-3.7.1+dfsg/src/lib/filters/fd_unix/fd_unix.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/filters/fd_unix/fd_unix.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -16,12 +16,12 @@ * Write data from a pipe into a Unix fd */ int operator<<(int fd, Pipe& pipe) { - secure_vector buffer(BOTAN_DEFAULT_BUFFER_SIZE); - while(pipe.remaining()) { + secure_vector buffer(DefaultBufferSize); + while(pipe.remaining() > 0) { size_t got = pipe.read(buffer.data(), buffer.size()); size_t position = 0; - while(got) { - ssize_t ret = ::write(fd, &buffer[position], got); + while(got > 0) { + const ssize_t ret = ::write(fd, &buffer[position], got); if(ret < 0) { throw Stream_IO_Error("Pipe output operator (unixfd) has failed"); } @@ -37,9 +37,9 @@ * Read data from a Unix fd into a pipe */ int operator>>(int fd, Pipe& pipe) { - secure_vector buffer(BOTAN_DEFAULT_BUFFER_SIZE); + secure_vector buffer(DefaultBufferSize); while(true) { - ssize_t ret = ::read(fd, buffer.data(), buffer.size()); + const ssize_t ret = ::read(fd, buffer.data(), buffer.size()); if(ret < 0) { throw Stream_IO_Error("Pipe input operator (unixfd) has failed"); } else if(ret == 0) { diff -Nru botan3-3.7.1+dfsg/src/lib/filters/filter.cpp botan3-3.12.0+dfsg/src/lib/filters/filter.cpp --- botan3-3.7.1+dfsg/src/lib/filters/filter.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/filters/filter.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,7 @@ #include +#include #include namespace Botan { @@ -16,22 +17,24 @@ */ Filter::Filter() { m_next.resize(1); - m_port_num = 0; - m_filter_owns = 0; - m_owned = false; +} + +void Filter::send(std::span in, size_t length) { + BOTAN_ASSERT_NOMSG(length <= in.size()); + send(in.data(), length); } /* * Send data to all ports */ void Filter::send(const uint8_t input[], size_t length) { - if(!length) { + if(length == 0) { return; } bool nothing_attached = true; for(size_t j = 0; j != total_ports(); ++j) { - if(m_next[j]) { + if(m_next[j] != nullptr) { if(!m_write_queue.empty()) { m_next[j]->write(m_write_queue.data(), m_write_queue.size()); } @@ -53,7 +56,7 @@ void Filter::new_msg() { start_msg(); for(size_t j = 0; j != total_ports(); ++j) { - if(m_next[j]) { + if(m_next[j] != nullptr) { m_next[j]->new_msg(); } } @@ -65,7 +68,7 @@ void Filter::finish_msg() { end_msg(); for(size_t j = 0; j != total_ports(); ++j) { - if(m_next[j]) { + if(m_next[j] != nullptr) { m_next[j]->finish_msg(); } } @@ -75,9 +78,9 @@ * Attach a filter to the current port */ void Filter::attach(Filter* new_filter) { - if(new_filter) { + if(new_filter != nullptr) { Filter* last = this; - while(last->get_next()) { + while(last->get_next() != nullptr) { last = last->get_next(); } last->m_next[last->current_port()] = new_filter; @@ -113,11 +116,11 @@ m_port_num = 0; m_filter_owns = 0; - while(size && filters && (filters[size - 1] == nullptr)) { + while(size > 0 && filters != nullptr && (filters[size - 1] == nullptr)) { --size; } - if(filters && size) { + if(filters != nullptr && size > 0) { m_next.assign(filters, filters + size); } } diff -Nru botan3-3.7.1+dfsg/src/lib/filters/filter.h botan3-3.12.0+dfsg/src/lib/filters/filter.h --- botan3-3.7.1+dfsg/src/lib/filters/filter.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/filters/filter.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,7 @@ #define BOTAN_FILTER_H_ #include +#include #include #include @@ -55,7 +56,9 @@ virtual ~Filter() = default; Filter(const Filter&) = delete; + Filter(Filter&&) = delete; Filter& operator=(const Filter&) = delete; + Filter& operator=(Filter&&) = delete; protected: /** @@ -72,20 +75,18 @@ /** * @param in some input for the filter */ - template - void send(const std::vector& in) { - send(in.data(), in.size()); - } + void send(std::span in) { send(in.data(), in.size()); } /** * @param in some input for the filter * @param length the number of bytes of in to send + * + * This previously took a std::vector, for which the length field (allowing + * using just a prefix of the vector) somewhat made sense. It makes less + * sense now that we are using a span here; you can just use `first` to get + * a prefix. */ - template - void send(const std::vector& in, size_t length) { - BOTAN_ASSERT_NOMSG(length <= in.size()); - send(in.data(), length); - } + void send(std::span in, size_t length); Filter(); @@ -133,10 +134,11 @@ secure_vector m_write_queue; std::vector m_next; // not owned - size_t m_port_num, m_filter_owns; + size_t m_port_num = 0; + size_t m_filter_owns = 0; // true if filter belongs to a pipe --> prohibit filter sharing! - bool m_owned; + bool m_owned = false; }; /** @@ -149,10 +151,13 @@ */ void incr_owns() { ++m_filter_owns; } + // NOLINTNEXTLINE(bugprone-derived-method-shadowing-base-method) void set_port(size_t n) { Filter::set_port(n); } + // NOLINTNEXTLINE(bugprone-derived-method-shadowing-base-method) void set_next(Filter* f[], size_t n) { Filter::set_next(f, n); } + // NOLINTNEXTLINE(bugprone-derived-method-shadowing-base-method) void attach(Filter* f) { Filter::attach(f); } }; @@ -162,7 +167,7 @@ * whitespaces, FULL_CHECK - perform checks, also complain * about white spaces. */ -enum Decoder_Checking { NONE, IGNORE_WS, FULL_CHECK }; +enum Decoder_Checking : uint8_t /* NOLINT(*-use-enum-class) */ { NONE, IGNORE_WS, FULL_CHECK }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/filters/filters.h botan3-3.12.0+dfsg/src/lib/filters/filters.h --- botan3-3.7.1+dfsg/src/lib/filters/filters.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/filters/filters.h 2026-05-07 01:38:28.000000000 +0000 @@ -37,7 +37,7 @@ * Filter mixin that breaks input into blocks, useful for * cipher modes */ -class BOTAN_PUBLIC_API(2, 0) Buffered_Filter { +class BOTAN_PUBLIC_API(2, 0) Buffered_Filter /* NOLINT(*-special-member-functions) */ { public: /** * Write bytes into the buffered filter, which will them emit them @@ -127,7 +127,7 @@ * @param iv the initialization vector to use */ virtual void set_iv(const InitializationVector& iv) { - if(iv.length() != 0) { + if(!iv.empty()) { throw Invalid_IV_Length(name(), iv.length()); } } @@ -200,6 +200,7 @@ */ inline Keyed_Filter* get_cipher(std::string_view algo_spec, Cipher_Dir direction) { auto c = Cipher_Mode::create_or_throw(algo_spec, direction); + // NOLINTNEXTLINE(*-owning-memory) return new Cipher_Mode_Filter(c.release()); } @@ -233,7 +234,7 @@ const InitializationVector& iv, Cipher_Dir direction) { Keyed_Filter* cipher = get_cipher(algo_spec, key, direction); - if(iv.length()) { + if(!iv.empty()) { cipher->set_iv(iv); } return cipher; @@ -324,7 +325,7 @@ * hash. Otherwise, specify a smaller value here so that the * output of the hash algorithm will be cut off. */ - Hash_Filter(HashFunction* hash, size_t len = 0) : m_hash(hash), m_out_len(len) {} + BOTAN_FUTURE_EXPLICIT Hash_Filter(HashFunction* hash, size_t len = 0) : m_hash(hash), m_out_len(len) {} /** * Construct a hash filter. @@ -334,7 +335,7 @@ * hash. Otherwise, specify a smaller value here so that the * output of the hash algorithm will be cut off. */ - Hash_Filter(std::string_view request, size_t len = 0); + BOTAN_FUTURE_EXPLICIT Hash_Filter(std::string_view request, size_t len = 0); private: std::unique_ptr m_hash; @@ -371,7 +372,8 @@ * MAC. Otherwise, specify a smaller value here so that the * output of the MAC will be cut off. */ - MAC_Filter(MessageAuthenticationCode* mac, size_t out_len = 0) : m_mac(mac), m_out_len(out_len) {} + BOTAN_FUTURE_EXPLICIT MAC_Filter(MessageAuthenticationCode* mac, size_t out_len = 0) : + m_mac(mac), m_out_len(out_len) {} /** * Construct a MAC filter. @@ -395,7 +397,7 @@ * MAC. Otherwise, specify a smaller value here so that the * output of the MAC will be cut off. */ - MAC_Filter(std::string_view mac, size_t len = 0); + BOTAN_FUTURE_EXPLICIT MAC_Filter(std::string_view mac, size_t len = 0); /** * Construct a MAC filter. @@ -436,6 +438,11 @@ ~Compression_Filter() override; + Compression_Filter(const Compression_Filter& other) = delete; + Compression_Filter(Compression_Filter&& other) = delete; + Compression_Filter& operator=(const Compression_Filter& other) = delete; + Compression_Filter& operator=(Compression_Filter&& other) = delete; + private: std::unique_ptr m_comp; size_t m_buffersize, m_level; @@ -453,10 +460,15 @@ std::string name() const override; - Decompression_Filter(std::string_view type, size_t buffer_size = 4096); + BOTAN_FUTURE_EXPLICIT Decompression_Filter(std::string_view type, size_t buffer_size = 4096); ~Decompression_Filter() override; + Decompression_Filter(const Decompression_Filter& other) = delete; + Decompression_Filter(Decompression_Filter&& other) = delete; + Decompression_Filter& operator=(const Decompression_Filter& other) = delete; + Decompression_Filter& operator=(Decompression_Filter&& other) = delete; + private: std::unique_ptr m_comp; std::size_t m_buffersize; @@ -490,7 +502,9 @@ * @param line_length the length of the lines of the output * @param trailing_newline whether to use a trailing newline */ - Base64_Encoder(bool line_breaks = false, size_t line_length = 72, bool trailing_newline = false); + BOTAN_FUTURE_EXPLICIT Base64_Encoder(bool line_breaks = false, + size_t line_length = 72, + bool trailing_newline = false); private: void encode_and_send(const uint8_t input[], size_t length, bool final_inputs = false); @@ -499,7 +513,8 @@ const size_t m_line_length; const bool m_trailing_newline; std::vector m_in, m_out; - size_t m_position, m_out_position; + size_t m_position = 0; + size_t m_out_position = 0; }; /** @@ -530,8 +545,9 @@ private: const Decoder_Checking m_checking; - std::vector m_in, m_out; - size_t m_position; + std::vector m_in; + std::vector m_out; + size_t m_position = 0; }; /** @@ -543,7 +559,7 @@ /** * Whether to use uppercase or lowercase letters for the encoded string. */ - enum Case { Uppercase, Lowercase }; + enum Case : uint8_t /* NOLINT(*-use-enum-class) */ { Uppercase, Lowercase }; std::string name() const override { return "Hex_Encoder"; } @@ -562,15 +578,17 @@ * @param line_length if newlines are used, how long are lines * @param the_case the case to use in the encoded strings */ - Hex_Encoder(bool newlines = false, size_t line_length = 72, Case the_case = Uppercase); + BOTAN_FUTURE_EXPLICIT Hex_Encoder(bool newlines = false, size_t line_length = 72, Case the_case = Uppercase); private: - void encode_and_send(const uint8_t[], size_t); + void encode_and_send(const uint8_t input[], size_t length); const Case m_casing; const size_t m_line_length; - std::vector m_in, m_out; - size_t m_position, m_counter; + std::vector m_in; + std::vector m_out; + size_t m_position = 0; + size_t m_counter = 0; }; /** @@ -580,7 +598,7 @@ public: std::string name() const override { return "Hex_Decoder"; } - void write(const uint8_t[], size_t) override; + void write(const uint8_t input[], size_t length) override; void end_msg() override; /** @@ -593,7 +611,7 @@ private: const Decoder_Checking m_checking; std::vector m_in, m_out; - size_t m_position; + size_t m_position = 0; }; /** @@ -601,7 +619,7 @@ */ class BOTAN_PUBLIC_API(2, 0) BitBucket final : public Filter { public: - void write(const uint8_t[], size_t) override { /* discard */ + void write(const uint8_t /*input*/[], size_t /*length*/) override { /* discard */ } std::string name() const override { return "BitBucket"; } @@ -623,7 +641,10 @@ * Construct a chain of up to four filters. The filters are set * up in the same order as the arguments. */ - Chain(Filter* = nullptr, Filter* = nullptr, Filter* = nullptr, Filter* = nullptr); + BOTAN_FUTURE_EXPLICIT Chain(Filter* f1 = nullptr, + Filter* f2 = nullptr, + Filter* f3 = nullptr, + Filter* f4 = nullptr); /** * Construct a chain from range of filters @@ -642,6 +663,7 @@ public: void write(const uint8_t input[], size_t length) override { send(input, length); } + // NOLINTNEXTLINE(bugprone-derived-method-shadowing-base-method) void set_port(size_t n) { Fanout_Filter::set_port(n); } std::string name() const override { return "Fork"; } @@ -649,7 +671,7 @@ /** * Construct a Fork filter with up to four forks. */ - Fork(Filter*, Filter*, Filter* = nullptr, Filter* = nullptr); + Fork(Filter* f1, Filter* f2, Filter* f3 = nullptr, Filter* f4 = nullptr); /** * Construct a Fork from range of filters @@ -665,6 +687,12 @@ * This class is a threaded version of the Fork filter. While this uses * threads, the class itself is NOT thread-safe. This is meant as a drop- * in replacement for Fork where performance gains are possible. +* +* This is deprecated as supporting it requires quite a bit of extra complexity +* and realistically if performance is a concern, avoiding this Pipe/Filters +* interface entirely is highly recommended. +* +* TODO(Botan4) remove this and all associated helpers (like Barrier and Semaphore) */ class BOTAN_PUBLIC_API(2, 0) Threaded_Fork final : public Fork { public: @@ -673,17 +701,23 @@ /** * Construct a Threaded_Fork filter with up to four forks. */ - Threaded_Fork(Filter*, Filter*, Filter* = nullptr, Filter* = nullptr); + BOTAN_DEPRECATED("Deprecated, use plain Fork") + Threaded_Fork(Filter* f1, Filter* f2, Filter* f3 = nullptr, Filter* f4 = nullptr); /** * Construct a Threaded_Fork from range of filters * @param filter_arr the list of filters * @param length how many filters */ - Threaded_Fork(Filter* filter_arr[], size_t length); + BOTAN_DEPRECATED("Deprecated, use plain Fork") Threaded_Fork(Filter* filter_arr[], size_t length); ~Threaded_Fork() override; + Threaded_Fork(const Threaded_Fork& other) = delete; + Threaded_Fork(Threaded_Fork&& other) = delete; + Threaded_Fork& operator=(const Threaded_Fork& other) = delete; + Threaded_Fork& operator=(Threaded_Fork&& other) = delete; + private: void set_next(Filter* f[], size_t n); void send(const uint8_t in[], size_t length) override; diff -Nru botan3-3.7.1+dfsg/src/lib/filters/hex_filt.cpp botan3-3.12.0+dfsg/src/lib/filters/hex_filt.cpp --- botan3-3.7.1+dfsg/src/lib/filters/hex_filt.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/filters/hex_filt.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #include #include +#include #include namespace Botan { @@ -24,7 +25,6 @@ Hex_Encoder::Hex_Encoder(bool breaks, size_t length, Case c) : m_casing(c), m_line_length(breaks ? length : 0) { m_in.resize(HEX_CODEC_BUFFER_SIZE); m_out.resize(2 * m_in.size()); - m_counter = m_position = 0; } /* @@ -33,7 +33,6 @@ Hex_Encoder::Hex_Encoder(Case c) : m_casing(c), m_line_length(0) { m_in.resize(HEX_CODEC_BUFFER_SIZE); m_out.resize(2 * m_in.size()); - m_counter = m_position = 0; } /* @@ -45,9 +44,10 @@ if(m_line_length == 0) { send(m_out, 2 * length); } else { - size_t remaining = 2 * length, offset = 0; - while(remaining) { - size_t sent = std::min(m_line_length - m_counter, remaining); + size_t remaining = 2 * length; + size_t offset = 0; + while(remaining > 0) { + const size_t sent = std::min(m_line_length - m_counter, remaining); send(&m_out[offset], sent); m_counter += sent; remaining -= sent; @@ -87,7 +87,7 @@ */ void Hex_Encoder::end_msg() { encode_and_send(m_in.data(), m_position); - if(m_counter && m_line_length) { + if(m_counter > 0 && m_line_length > 0) { send('\n'); } m_counter = m_position = 0; @@ -99,20 +99,19 @@ Hex_Decoder::Hex_Decoder(Decoder_Checking c) : m_checking(c) { m_in.resize(HEX_CODEC_BUFFER_SIZE); m_out.resize(m_in.size() / 2); - m_position = 0; } /* * Convert some data from hex format */ void Hex_Decoder::write(const uint8_t input[], size_t length) { - while(length) { - size_t to_copy = std::min(length, m_in.size() - m_position); + while(length > 0) { + const size_t to_copy = std::min(length, m_in.size() - m_position); copy_mem(&m_in[m_position], input, to_copy); m_position += to_copy; size_t consumed = 0; - size_t written = + const size_t written = hex_decode(m_out.data(), cast_uint8_ptr_to_char(m_in.data()), m_position, consumed, m_checking != FULL_CHECK); send(m_out, written); @@ -134,7 +133,7 @@ */ void Hex_Decoder::end_msg() { size_t consumed = 0; - size_t written = + const size_t written = hex_decode(m_out.data(), cast_uint8_ptr_to_char(m_in.data()), m_position, consumed, m_checking != FULL_CHECK); send(m_out, written); diff -Nru botan3-3.7.1+dfsg/src/lib/filters/out_buf.cpp botan3-3.12.0+dfsg/src/lib/filters/out_buf.cpp --- botan3-3.7.1+dfsg/src/lib/filters/out_buf.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/filters/out_buf.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -18,7 +18,7 @@ */ size_t Output_Buffers::read(uint8_t output[], size_t length, Pipe::message_id msg) { SecureQueue* q = get(msg); - if(q) { + if(q != nullptr) { return q->read(output, length); } return 0; @@ -28,8 +28,8 @@ * Peek at data in a message */ size_t Output_Buffers::peek(uint8_t output[], size_t length, size_t stream_offset, Pipe::message_id msg) const { - SecureQueue* q = get(msg); - if(q) { + const SecureQueue* q = get(msg); + if(q != nullptr) { return q->peek(output, length, stream_offset); } return 0; @@ -39,8 +39,8 @@ * Check available bytes in a message */ size_t Output_Buffers::remaining(Pipe::message_id msg) const { - SecureQueue* q = get(msg); - if(q) { + const SecureQueue* q = get(msg); + if(q != nullptr) { return q->size(); } return 0; @@ -50,8 +50,8 @@ * Return the total bytes of a message that have already been read. */ size_t Output_Buffers::get_bytes_read(Pipe::message_id msg) const { - SecureQueue* q = get(msg); - if(q) { + const SecureQueue* q = get(msg); + if(q != nullptr) { return q->get_bytes_read(); } return 0; @@ -104,11 +104,4 @@ return (m_offset + m_buffers.size()); } -/* -* Output_Buffers Constructor -*/ -Output_Buffers::Output_Buffers() { - m_offset = 0; -} - } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/filters/out_buf.h botan3-3.12.0+dfsg/src/lib/filters/out_buf.h --- botan3-3.7.1+dfsg/src/lib/filters/out_buf.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/filters/out_buf.h 2026-05-07 01:38:28.000000000 +0000 @@ -22,23 +22,23 @@ */ class Output_Buffers final { public: - size_t read(uint8_t[], size_t, Pipe::message_id); - size_t peek(uint8_t[], size_t, size_t, Pipe::message_id) const; - size_t get_bytes_read(Pipe::message_id) const; - size_t remaining(Pipe::message_id) const; + size_t read(uint8_t output[], size_t length, Pipe::message_id msg); + size_t peek(uint8_t output[], size_t length, size_t stream_offset, Pipe::message_id msg) const; + size_t get_bytes_read(Pipe::message_id msg) const; + size_t remaining(Pipe::message_id msg) const; - void add(SecureQueue*); + void add(SecureQueue* queue); void retire(); Pipe::message_id message_count() const; - Output_Buffers(); + Output_Buffers() = default; private: - class SecureQueue* get(Pipe::message_id) const; + SecureQueue* get(Pipe::message_id msg) const; std::deque> m_buffers; - Pipe::message_id m_offset; + Pipe::message_id m_offset = 0; }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/filters/pipe.cpp botan3-3.12.0+dfsg/src/lib/filters/pipe.cpp --- botan3-3.7.1+dfsg/src/lib/filters/pipe.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/filters/pipe.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,8 +7,8 @@ #include -#include #include +#include #include #include #include @@ -29,6 +29,8 @@ } // namespace +Pipe::Pipe(Pipe&&) noexcept = default; + Pipe::Invalid_Message_Number::Invalid_Message_Number(std::string_view where, message_id msg) : Invalid_Argument(fmt("Pipe::{}: Invalid message number {}", where, msg)) {} @@ -40,13 +42,10 @@ /* * Pipe Constructor */ -Pipe::Pipe(std::initializer_list args) { +Pipe::Pipe(std::initializer_list args) : m_pipe(nullptr), m_default_read(0), m_inside_msg(false) { m_outputs = std::make_unique(); - m_pipe = nullptr; - m_default_read = 0; - m_inside_msg = false; - for(auto arg : args) { + for(auto* arg : args) { do_append(arg); } } @@ -71,13 +70,18 @@ * Destroy the Pipe */ void Pipe::destruct(Filter* to_kill) { - if(!to_kill || dynamic_cast(to_kill)) { + if(to_kill == nullptr) { + return; + } + + if(dynamic_cast(to_kill) != nullptr) { return; } + for(size_t j = 0; j != to_kill->total_ports(); ++j) { destruct(to_kill->m_next[j]); } - delete to_kill; + delete to_kill; // NOLINT(*owning-memory) } /* @@ -106,22 +110,26 @@ end_msg(); } +void Pipe::process_msg(std::span input) { + this->process_msg(input.data(), input.size()); +} + /* * Process a full message at once */ void Pipe::process_msg(const secure_vector& input) { - process_msg(input.data(), input.size()); + this->process_msg(std::span{input}); } void Pipe::process_msg(const std::vector& input) { - process_msg(input.data(), input.size()); + this->process_msg(std::span{input}); } /* * Process a full message at once */ void Pipe::process_msg(std::string_view input) { - process_msg(cast_char_ptr_to_uint8(input.data()), input.length()); + process_msg(as_span_of_bytes(input)); } /* @@ -141,7 +149,7 @@ throw Invalid_State("Pipe::start_msg: Message was already started"); } if(m_pipe == nullptr) { - m_pipe = new Null_Filter; + m_pipe = new Null_Filter; // NOLINT(*-owning-memory) } find_endpoints(m_pipe); m_pipe->new_msg(); @@ -157,7 +165,7 @@ } m_pipe->finish_msg(); clear_endpoints(m_pipe); - if(dynamic_cast(m_pipe)) { + if(dynamic_cast(m_pipe) != nullptr) { delete m_pipe; m_pipe = nullptr; } @@ -171,10 +179,10 @@ */ void Pipe::find_endpoints(Filter* f) { for(size_t j = 0; j != f->total_ports(); ++j) { - if(f->m_next[j] && !dynamic_cast(f->m_next[j])) { + if(f->m_next[j] != nullptr && dynamic_cast(f->m_next[j]) == nullptr) { find_endpoints(f->m_next[j]); } else { - SecureQueue* q = new SecureQueue; + SecureQueue* q = new SecureQueue; // NOLINT(*-owning-memory) f->m_next[j] = q; m_outputs->add(q); } @@ -185,11 +193,11 @@ * Remove the SecureQueues attached to the Filter */ void Pipe::clear_endpoints(Filter* f) { - if(!f) { + if(f == nullptr) { return; } for(size_t j = 0; j != f->total_ports(); ++j) { - if(f->m_next[j] && dynamic_cast(f->m_next[j])) { + if(f->m_next[j] != nullptr && dynamic_cast(f->m_next[j]) != nullptr) { f->m_next[j] = nullptr; } clear_endpoints(f->m_next[j]); @@ -224,10 +232,10 @@ * Append a Filter to the Pipe */ void Pipe::do_append(Filter* filter) { - if(!filter) { + if(filter == nullptr) { return; } - if(dynamic_cast(filter)) { + if(dynamic_cast(filter) != nullptr) { throw Invalid_Argument("Pipe::append: SecureQueue cannot be used"); } if(filter->m_owned) { @@ -240,7 +248,7 @@ filter->m_owned = true; - if(!m_pipe) { + if(m_pipe == nullptr) { m_pipe = filter; } else { m_pipe->attach(filter); @@ -254,10 +262,10 @@ if(m_inside_msg) { throw Invalid_State("Cannot prepend to a Pipe while it is processing"); } - if(!filter) { + if(filter == nullptr) { return; } - if(dynamic_cast(filter)) { + if(dynamic_cast(filter) != nullptr) { throw Invalid_Argument("Pipe::prepend: SecureQueue cannot be used"); } if(filter->m_owned) { @@ -266,7 +274,7 @@ filter->m_owned = true; - if(m_pipe) { + if(m_pipe != nullptr) { filter->attach(m_pipe); } m_pipe = filter; @@ -280,7 +288,7 @@ throw Invalid_State("Cannot pop off a Pipe while it is processing"); } - if(!m_pipe) { + if(m_pipe == nullptr) { return; } @@ -290,9 +298,10 @@ size_t to_remove = m_pipe->owns() + 1; - while(to_remove--) { - std::unique_ptr to_destroy(m_pipe); + while(to_remove > 0) { + const std::unique_ptr to_destroy(m_pipe); m_pipe = m_pipe->m_next[0]; + to_remove -= 1; } } diff -Nru botan3-3.7.1+dfsg/src/lib/filters/pipe.h botan3-3.12.0+dfsg/src/lib/filters/pipe.h --- botan3-3.7.1+dfsg/src/lib/filters/pipe.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/filters/pipe.h 2026-05-07 01:38:28.000000000 +0000 @@ -13,6 +13,7 @@ #include #include #include +#include namespace Botan { @@ -25,6 +26,10 @@ * through the pipe until it reaches the end, where the output is * collected for retrieval. If you're familiar with the Unix shell * environment, this design will sound quite familiar. +* +* @warning This Pipe interface, and all associated types (Filter, etc) +* are considered decrepit, no longer used within the library itself, +* and likely will see no future development. Avoid in new code. */ class BOTAN_PUBLIC_API(2, 0) Pipe final : public DataSource { public: @@ -65,6 +70,12 @@ /** * Write input to the pipe, i.e. to its first filter. + * @param in the byte array to write + */ + void write(std::span in); + + /** + * Write input to the pipe, i.e. to its first filter. * @param in the secure_vector containing the data to write */ void write(const secure_vector& in) { write(in.data(), in.size()); } @@ -102,6 +113,12 @@ /** * Perform start_msg(), write() and end_msg() sequentially. + * @param input the byte array containing the data to write + */ + void process_msg(std::span input); + + /** + * Perform start_msg(), write() and end_msg() sequentially. * @param in the secure_vector containing the data to write */ void process_msg(const secure_vector& in); @@ -313,27 +330,32 @@ * Construct a Pipe of up to four filters. The filters are set up * in the same order as the arguments. */ - Pipe(Filter* = nullptr, Filter* = nullptr, Filter* = nullptr, Filter* = nullptr); + BOTAN_FUTURE_EXPLICIT Pipe(Filter* f1 = nullptr, + Filter* f2 = nullptr, + Filter* f3 = nullptr, + Filter* f4 = nullptr); /** * Construct a Pipe from a list of filters * @param filters the set of filters to use */ - explicit Pipe(std::initializer_list filters); + Pipe(std::initializer_list filters); Pipe(const Pipe&) = delete; + Pipe(Pipe&&) noexcept; Pipe& operator=(const Pipe&) = delete; + Pipe& operator=(Pipe&&) = delete; ~Pipe() override; private: - void destruct(Filter*); + void destruct(Filter* filt); void do_append(Filter* filt); void do_prepend(Filter* filt); - void find_endpoints(Filter*); - void clear_endpoints(Filter*); + void find_endpoints(Filter* filt); + void clear_endpoints(Filter* filt); - message_id get_message_no(std::string_view, message_id) const; + message_id get_message_no(std::string_view func_name, message_id msg) const; Filter* m_pipe; std::unique_ptr m_outputs; diff -Nru botan3-3.7.1+dfsg/src/lib/filters/pipe_io.cpp botan3-3.12.0+dfsg/src/lib/filters/pipe_io.cpp --- botan3-3.7.1+dfsg/src/lib/filters/pipe_io.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/filters/pipe_io.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -17,10 +17,10 @@ * Write data from a pipe into an ostream */ std::ostream& operator<<(std::ostream& stream, Pipe& pipe) { - secure_vector buffer(BOTAN_DEFAULT_BUFFER_SIZE); - while(stream.good() && pipe.remaining()) { + secure_vector buffer(DefaultBufferSize); + while(stream.good() && pipe.remaining() > 0) { const size_t got = pipe.read(buffer.data(), buffer.size()); - stream.write(cast_uint8_ptr_to_char(buffer.data()), got); + stream.write(cast_uint8_ptr_to_char(buffer.data()), static_cast(got)); } if(!stream.good()) { throw Stream_IO_Error("Pipe output operator (iostream) has failed"); @@ -32,9 +32,9 @@ * Read data from an istream into a pipe */ std::istream& operator>>(std::istream& stream, Pipe& pipe) { - secure_vector buffer(BOTAN_DEFAULT_BUFFER_SIZE); + secure_vector buffer(DefaultBufferSize); while(stream.good()) { - stream.read(cast_uint8_ptr_to_char(buffer.data()), buffer.size()); + stream.read(cast_uint8_ptr_to_char(buffer.data()), static_cast(buffer.size())); const size_t got = static_cast(stream.gcount()); pipe.write(buffer.data(), got); } diff -Nru botan3-3.7.1+dfsg/src/lib/filters/pipe_rw.cpp botan3-3.12.0+dfsg/src/lib/filters/pipe_rw.cpp --- botan3-3.7.1+dfsg/src/lib/filters/pipe_rw.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/filters/pipe_rw.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,7 @@ #include #include +#include #include namespace Botan { @@ -31,6 +32,10 @@ return msg; } +void Pipe::write(std::span input) { + this->write(input.data(), input.size()); +} + /* * Write into a Pipe */ @@ -45,7 +50,7 @@ * Write a string into a Pipe */ void Pipe::write(std::string_view str) { - write(cast_char_ptr_to_uint8(str.data()), str.size()); + write(as_span_of_bytes(str)); } /* @@ -59,9 +64,9 @@ * Write the contents of a DataSource into a Pipe */ void Pipe::write(DataSource& source) { - secure_vector buffer(BOTAN_DEFAULT_BUFFER_SIZE); + secure_vector buffer(DefaultBufferSize); while(!source.end_of_data()) { - size_t got = source.read(buffer.data(), buffer.size()); + const size_t got = source.read(buffer.data(), buffer.size()); write(buffer.data(), got); } } @@ -93,7 +98,7 @@ secure_vector Pipe::read_all(message_id msg) { msg = ((msg != DEFAULT_MESSAGE) ? msg : default_msg()); secure_vector buffer(remaining(msg)); - size_t got = read(buffer.data(), buffer.size(), msg); + const size_t got = read(buffer.data(), buffer.size(), msg); buffer.resize(got); return buffer; } @@ -103,12 +108,12 @@ */ std::string Pipe::read_all_as_string(message_id msg) { msg = ((msg != DEFAULT_MESSAGE) ? msg : default_msg()); - secure_vector buffer(BOTAN_DEFAULT_BUFFER_SIZE); + secure_vector buffer(DefaultBufferSize); std::string str; str.reserve(remaining(msg)); while(true) { - size_t got = read(buffer.data(), buffer.size(), msg); + const size_t got = read(buffer.data(), buffer.size(), msg); if(got == 0) { break; } diff -Nru botan3-3.7.1+dfsg/src/lib/filters/secqueue.cpp botan3-3.12.0+dfsg/src/lib/filters/secqueue.cpp --- botan3-3.7.1+dfsg/src/lib/filters/secqueue.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/filters/secqueue.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -18,10 +18,7 @@ */ class SecureQueueNode final { public: - SecureQueueNode() : m_buffer(BOTAN_DEFAULT_BUFFER_SIZE) { - m_next = nullptr; - m_start = m_end = 0; - } + SecureQueueNode() : m_next(nullptr), m_buffer(DefaultBufferSize), m_start(0), m_end(0) {} ~SecureQueueNode() { m_next = nullptr; @@ -34,14 +31,14 @@ SecureQueueNode& operator=(SecureQueueNode&& other) = delete; size_t write(const uint8_t input[], size_t length) { - size_t copied = std::min(length, m_buffer.size() - m_end); + const size_t copied = std::min(length, m_buffer.size() - m_end); copy_mem(m_buffer.data() + m_end, input, copied); m_end += copied; return copied; } size_t read(uint8_t output[], size_t length) { - size_t copied = std::min(length, m_end - m_start); + const size_t copied = std::min(length, m_end - m_start); copy_mem(output, m_buffer.data() + m_start, copied); m_start += copied; return copied; @@ -52,7 +49,7 @@ if(offset >= left) { return 0; } - size_t copied = std::min(length, left - offset); + const size_t copied = std::min(length, left - offset); copy_mem(output, m_buffer.data() + m_start + offset, copied); return copied; } @@ -69,22 +66,20 @@ /* * Create a SecureQueue */ -SecureQueue::SecureQueue() { - m_bytes_read = 0; +SecureQueue::SecureQueue() : m_bytes_read(0) { set_next(nullptr, 0); - m_head = m_tail = new SecureQueueNode; + m_head = m_tail = new SecureQueueNode; // NOLINT(*-owning-memory) } /* * Copy a SecureQueue */ -SecureQueue::SecureQueue(const SecureQueue& input) : Fanout_Filter(), DataSource() { - m_bytes_read = 0; +SecureQueue::SecureQueue(const SecureQueue& input) : Fanout_Filter(), m_bytes_read(0) { set_next(nullptr, 0); - m_head = m_tail = new SecureQueueNode; + m_head = m_tail = new SecureQueueNode; // NOLINT(*-owning-memory) SecureQueueNode* temp = input.m_head; - while(temp) { + while(temp != nullptr) { write(&temp->m_buffer[temp->m_start], temp->m_end - temp->m_start); temp = temp->m_next; } @@ -94,10 +89,10 @@ * Destroy this SecureQueue */ void SecureQueue::destroy() { - SecureQueueNode* temp = m_head; - while(temp) { - SecureQueueNode* holder = temp->m_next; - delete temp; + const SecureQueueNode* temp = m_head; + while(temp != nullptr) { + const SecureQueueNode* holder = temp->m_next; + delete temp; // NOLINT(*-owning-memory) temp = holder; } m_head = m_tail = nullptr; @@ -113,9 +108,9 @@ destroy(); m_bytes_read = input.get_bytes_read(); - m_head = m_tail = new SecureQueueNode; + m_head = m_tail = new SecureQueueNode; // NOLINT(*-owning-memory) SecureQueueNode* temp = input.m_head; - while(temp) { + while(temp != nullptr) { write(&temp->m_buffer[temp->m_start], temp->m_end - temp->m_start); temp = temp->m_next; } @@ -126,15 +121,15 @@ * Add some bytes to the queue */ void SecureQueue::write(const uint8_t input[], size_t length) { - if(!m_head) { - m_head = m_tail = new SecureQueueNode; + if(m_head == nullptr) { + m_head = m_tail = new SecureQueueNode; // NOLINT(*-owning-memory) } - while(length) { + while(length > 0) { const size_t n = m_tail->write(input, length); input += n; length -= n; - if(length) { - m_tail->m_next = new SecureQueueNode; + if(length > 0) { + m_tail->m_next = new SecureQueueNode; // NOLINT(*-owning-memory) m_tail = m_tail->m_next; } } @@ -145,14 +140,14 @@ */ size_t SecureQueue::read(uint8_t output[], size_t length) { size_t got = 0; - while(length && m_head) { + while(length > 0 && m_head != nullptr) { const size_t n = m_head->read(output, length); output += n; got += n; length -= n; if(m_head->size() == 0) { SecureQueueNode* holder = m_head->m_next; - delete m_head; + delete m_head; // NOLINT(*-owning-memory) m_head = holder; } } @@ -166,7 +161,7 @@ size_t SecureQueue::peek(uint8_t output[], size_t length, size_t offset) const { SecureQueueNode* current = m_head; - while(offset && current) { + while(offset > 0 && current != nullptr) { if(offset >= current->size()) { offset -= current->size(); current = current->m_next; @@ -176,7 +171,7 @@ } size_t got = 0; - while(length && current) { + while(length > 0 && current != nullptr) { const size_t n = current->peek(output, length, offset); offset = 0; output += n; @@ -198,10 +193,10 @@ * Return how many bytes the queue holds */ size_t SecureQueue::size() const { - SecureQueueNode* current = m_head; + const SecureQueueNode* current = m_head; size_t count = 0; - while(current) { + while(current != nullptr) { count += current->size(); current = current->m_next; } @@ -212,7 +207,7 @@ * Test if the queue has any data in it */ bool SecureQueue::end_of_data() const { - return (size() == 0); + return empty(); } bool SecureQueue::empty() const { diff -Nru botan3-3.7.1+dfsg/src/lib/filters/secqueue.h botan3-3.12.0+dfsg/src/lib/filters/secqueue.h --- botan3-3.7.1+dfsg/src/lib/filters/secqueue.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/filters/secqueue.h 2026-05-07 01:38:28.000000000 +0000 @@ -22,10 +22,10 @@ public: std::string name() const override { return "Queue"; } - void write(const uint8_t[], size_t) override; + void write(const uint8_t input[], size_t length) override; - size_t read(uint8_t[], size_t) override; - size_t peek(uint8_t[], size_t, size_t = 0) const override; + size_t read(uint8_t output[], size_t length) override; + size_t peek(uint8_t output[], size_t length, size_t offset = 0) const override; size_t get_bytes_read() const override; bool end_of_data() const override; diff -Nru botan3-3.7.1+dfsg/src/lib/filters/threaded_fork.cpp botan3-3.12.0+dfsg/src/lib/filters/threaded_fork.cpp --- botan3-3.7.1+dfsg/src/lib/filters/threaded_fork.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/filters/threaded_fork.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,7 +12,6 @@ #include #include - #include namespace Botan { @@ -82,7 +81,7 @@ } else { m_threads.reserve(n); for(size_t i = m_threads.size(); i != n; ++i) { - m_threads.push_back(std::make_shared(std::bind(&Threaded_Fork::thread_entry, this, m_next[i]))); + m_threads.push_back(std::make_shared([this, next = m_next[i]] { thread_entry(next); })); } } } @@ -95,7 +94,7 @@ bool nothing_attached = true; for(size_t j = 0; j != total_ports(); ++j) { - if(m_next[j]) { + if(m_next[j] != nullptr) { nothing_attached = false; } } @@ -128,7 +127,7 @@ while(true) { m_thread_data->m_input_ready_semaphore.acquire(); - if(!m_thread_data->m_input) { + if(m_thread_data->m_input == nullptr) { break; } diff -Nru botan3-3.7.1+dfsg/src/lib/hash/ascon_hash256/ascon_hash256.cpp botan3-3.12.0+dfsg/src/lib/hash/ascon_hash256/ascon_hash256.cpp --- botan3-3.7.1+dfsg/src/lib/hash/ascon_hash256/ascon_hash256.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/ascon_hash256/ascon_hash256.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,56 @@ +/* +* Ascon-Hash256 (NIST SP.800-232) +* (C) 2025 Jack Lloyd +* 2025 René Meusel +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +namespace Botan { + +namespace { + +// NIST SP.800-232 Appendix A (Table 12) +constexpr Ascon_p initial_state_of_ascon_hash_permutation({ + .init_and_final_rounds = 12, + .processing_rounds = 12, + .bit_rate = 64, + .initial_state = + { + 0x9b1e5494e934d681, + 0x4bc3a01e333751d2, + 0xae65396c6b34b81a, + 0x3c7fd4a4d56a4db3, + 0x1a5c464906c5976d, + }, +}); + +} // namespace + +Ascon_Hash256::Ascon_Hash256() : m_ascon_p(initial_state_of_ascon_hash_permutation) {} + +void Ascon_Hash256::clear() { + m_ascon_p = initial_state_of_ascon_hash_permutation; +} + +std::unique_ptr Ascon_Hash256::new_object() const { + return std::make_unique(); +} + +std::unique_ptr Ascon_Hash256::copy_state() const { + return std::make_unique(*this); +} + +void Ascon_Hash256::add_data(std::span input) { + m_ascon_p.absorb(input); +} + +void Ascon_Hash256::final_result(std::span out) { + m_ascon_p.finish(); + m_ascon_p.squeeze(out); + clear(); +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/ascon_hash256/ascon_hash256.h botan3-3.12.0+dfsg/src/lib/hash/ascon_hash256/ascon_hash256.h --- botan3-3.7.1+dfsg/src/lib/hash/ascon_hash256/ascon_hash256.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/ascon_hash256/ascon_hash256.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,45 @@ +/* +* Ascon-Hash256 (NIST SP.800-232) +* (C) 2025 Jack Lloyd +* 2025 René Meusel +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_ASCON_HASH256_H_ +#define BOTAN_ASCON_HASH256_H_ + +#include +#include + +namespace Botan { + +/** +* Ascon-Hash256 (NIST SP.800-232 Section 5.1) +*/ +class Ascon_Hash256 final : public HashFunction { + public: + Ascon_Hash256(); + + size_t output_length() const override { return 32; } + + std::string name() const override { return "Ascon-Hash256"; } + + std::string provider() const override { return m_ascon_p.provider(); } + + void clear() override; + + std::unique_ptr new_object() const override; + std::unique_ptr copy_state() const override; + + private: + void add_data(std::span input) override; + void final_result(std::span out) override; + + private: + Ascon_p m_ascon_p; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/hash/ascon_hash256/info.txt botan3-3.12.0+dfsg/src/lib/hash/ascon_hash256/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/ascon_hash256/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/ascon_hash256/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,11 @@ + +ASCON_HASH256 -> 20250816 + + + +name -> "Ascon-Hash256" + + + +ascon_perm + diff -Nru botan3-3.7.1+dfsg/src/lib/hash/blake2/blake2b.cpp botan3-3.12.0+dfsg/src/lib/hash/blake2/blake2b.cpp --- botan3-3.7.1+dfsg/src/lib/hash/blake2/blake2b.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/blake2/blake2b.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,12 +10,10 @@ #include #include +#include #include #include #include -#include - -#include #include namespace Botan { @@ -192,7 +190,7 @@ m_padded_key_buffer.resize(m_buffer.size()); if(m_padded_key_buffer.size() > m_key_size) { - size_t padding = m_padded_key_buffer.size() - m_key_size; + const size_t padding = m_padded_key_buffer.size() - m_key_size; clear_mem(m_padded_key_buffer.data() + m_key_size, padding); } diff -Nru botan3-3.7.1+dfsg/src/lib/hash/blake2/blake2b.h botan3-3.12.0+dfsg/src/lib/hash/blake2/blake2b.h --- botan3-3.7.1+dfsg/src/lib/hash/blake2/blake2b.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/blake2/blake2b.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,13 +11,10 @@ #include #include #include -#include #include namespace Botan { -class BLAKE2bMAC; - constexpr size_t BLAKE2B_BLOCKBYTES = 128; /** diff -Nru botan3-3.7.1+dfsg/src/lib/hash/blake2/info.txt botan3-3.12.0+dfsg/src/lib/hash/blake2/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/blake2/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/blake2/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -2,6 +2,8 @@ BLAKE2B -> 20130131 +# TODO(Botan4) rename this module blake2b + name -> "BLAKE2b" diff -Nru botan3-3.7.1+dfsg/src/lib/hash/blake2s/blake2s.cpp botan3-3.12.0+dfsg/src/lib/hash/blake2s/blake2s.cpp --- botan3-3.7.1+dfsg/src/lib/hash/blake2s/blake2s.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/blake2s/blake2s.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,8 @@ /* * BLAKE2s - * (C) 2023 Richard Huveneers + * (C) 2023, 2025 Richard Huveneers + * (C) 2025 Kagan Can Sit + * (C) 2025 René Meusel, Rohde & Schwarz Cybersecurity * * Based on the RFC7693 reference implementation * @@ -10,6 +12,7 @@ #include #include +#include #include #include #include @@ -20,12 +23,14 @@ // Initialization Vector. -const uint32_t blake2s_iv[8] = { +constexpr std::array blake2s_iv{ 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19}; // Mixing function G. -inline void B2S_G(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint32_t x, uint32_t y, uint32_t* v) { +template + requires(a < 16 && b < 16 && c < 16 && d < 16) +constexpr void B2S_G(uint32_t x, uint32_t y, std::span v) { v[a] = v[a] + v[b] + x; v[d] = rotr<16>(v[d] ^ v[a]); v[c] = v[c] + v[d]; @@ -42,65 +47,52 @@ return fmt("BLAKE2s({})", m_outlen << 3); } -// Secret key (also <= 32 bytes) is optional (keylen = 0). -// (keylen=0: no key) +// BLAKE2s is specified as a message authentication code. For that, the +// key would need to be zero-padded and incorporated into the initial hash +// state. See RFC 7693 Section 3.3 and Appendix D.2 `blake2s_init()`. +void BLAKE2s::state_init(size_t outlen) { + m_h = blake2s_iv; // state, "param block" + m_h[0] ^= 0x01010000 ^ outlen; -void BLAKE2s::state_init(size_t outlen, const uint8_t* key, size_t keylen) { - for(size_t i = 0; i < 8; i++) { // state, "param block" - m_h[i] = blake2s_iv[i]; - } - m_h[0] ^= 0x01010000 ^ (keylen << 8) ^ outlen; - - m_t[0] = 0; // input count low word - m_t[1] = 0; // input count high word - m_c = 0; // pointer within buffer + m_bytes_processed = 0; m_outlen = outlen; - - for(size_t i = keylen; i < 64; i++) { // zero input block - m_b[i] = 0; - } - if(keylen > 0) { - add_data(std::span(key, keylen)); - m_c = 64; // at the end - } + m_buffer.clear(); } // Compression function. "last" flag indicates last block. - -void BLAKE2s::compress(bool last) { - const uint8_t sigma[10][16] = {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, - {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, - {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, - {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, - {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, - {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, - {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, - {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, - {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, - {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}}; - uint32_t v[16], m[16]; - - for(size_t i = 0; i < 8; i++) { // init work variables - v[i] = m_h[i]; - v[i + 8] = blake2s_iv[i]; - } - - v[12] ^= m_t[0]; // low 32 bits of offset - v[13] ^= m_t[1]; // high 32 bits - if(last) { // last block flag set ? +void BLAKE2s::compress(bool last, std::span buf) { + BOTAN_ASSERT_NOMSG(buf.size() == block_size); + constexpr std::array, 10> sigma{{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, + {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, + {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, + {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, + {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, + {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, + {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, + {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, + {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}}}; + + // init work variables + std::array v = concat(m_h, blake2s_iv); + + v[12] ^= static_cast(m_bytes_processed); + v[13] ^= static_cast(m_bytes_processed >> 32); + if(last) { // last block flag set ? v[14] = ~v[14]; } - load_le(m, m_b, 16); // get little-endian words - for(size_t i = 0; i < 10; i++) { // ten rounds - B2S_G(0, 4, 8, 12, m[sigma[i][0]], m[sigma[i][1]], v); - B2S_G(1, 5, 9, 13, m[sigma[i][2]], m[sigma[i][3]], v); - B2S_G(2, 6, 10, 14, m[sigma[i][4]], m[sigma[i][5]], v); - B2S_G(3, 7, 11, 15, m[sigma[i][6]], m[sigma[i][7]], v); - B2S_G(0, 5, 10, 15, m[sigma[i][8]], m[sigma[i][9]], v); - B2S_G(1, 6, 11, 12, m[sigma[i][10]], m[sigma[i][11]], v); - B2S_G(2, 7, 8, 13, m[sigma[i][12]], m[sigma[i][13]], v); - B2S_G(3, 4, 9, 14, m[sigma[i][14]], m[sigma[i][15]], v); + const auto m = load_le>(buf); // get little-endian words + + for(const auto& perm : sigma) { // ten rounds + B2S_G<0, 4, 8, 12>(m[perm[0]], m[perm[1]], v); + B2S_G<1, 5, 9, 13>(m[perm[2]], m[perm[3]], v); + B2S_G<2, 6, 10, 14>(m[perm[4]], m[perm[5]], v); + B2S_G<3, 7, 11, 15>(m[perm[6]], m[perm[7]], v); + B2S_G<0, 5, 10, 15>(m[perm[8]], m[perm[9]], v); + B2S_G<1, 6, 11, 12>(m[perm[10]], m[perm[11]], v); + B2S_G<2, 7, 8, 13>(m[perm[12]], m[perm[13]], v); + B2S_G<3, 4, 9, 14>(m[perm[14]], m[perm[15]], v); } for(size_t i = 0; i < 8; ++i) { @@ -112,33 +104,32 @@ * Clear memory of sensitive data */ void BLAKE2s::clear() { - state_init(m_outlen, nullptr, 0); + state_init(m_outlen); } -void BLAKE2s::add_data(std::span in) { - for(size_t i = 0; i < in.size(); i++) { - if(m_c == 64) { // buffer full ? - m_t[0] += m_c; // add counters - if(m_t[0] < m_c) { // carry overflow ? - m_t[1]++; // high word +void BLAKE2s::add_data(std::span input) { + BufferSlicer in(input); + + while(!in.empty()) { + if(const auto one_block = m_buffer.handle_unaligned_data(in)) { + m_bytes_processed += block_size; + compress(false, *one_block); + } + + if(m_buffer.in_alignment()) { + while(const auto aligned_block = m_buffer.next_aligned_block_to_process(in)) { + m_bytes_processed += block_size; + compress(false, *aligned_block); } - compress(false); // compress (not last) - m_c = 0; // counter to zero } - m_b[m_c++] = in[i]; } } void BLAKE2s::final_result(std::span out) { - m_t[0] += m_c; // mark last block offset - if(m_t[0] < m_c) { // carry overflow - m_t[1]++; // high word - } + m_bytes_processed += m_buffer.elements_in_buffer(); - while(m_c < 64) { // fill up with zeros - m_b[m_c++] = 0; - } - compress(true); // final block flag = 1 + m_buffer.fill_up_with_zeros(); + compress(true, m_buffer.consume()); // little endian convert and store copy_out_le(out.first(output_length()), m_h); @@ -147,12 +138,7 @@ } std::unique_ptr BLAKE2s::copy_state() const { - std::unique_ptr h = std::make_unique(m_outlen << 3); - memcpy(h->m_b, m_b, sizeof(m_b)); - memcpy(h->m_h, m_h, sizeof(m_h)); - memcpy(h->m_t, m_t, sizeof(m_t)); - h->m_c = m_c; - return h; + return std::make_unique(*this); } /* @@ -161,14 +147,12 @@ BLAKE2s::BLAKE2s(size_t output_bits) { if(output_bits == 0 || output_bits > 256 || output_bits % 8 != 0) { throw Invalid_Argument("Bad output bits size for BLAKE2s"); - }; - state_init(output_bits >> 3, nullptr, 0); + } + state_init(output_bits >> 3); } BLAKE2s::~BLAKE2s() { - secure_scrub_memory(m_b, sizeof(m_b)); - secure_scrub_memory(m_h, sizeof(m_h)); - secure_scrub_memory(m_t, sizeof(m_t)); + secure_scrub_memory(m_h); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/blake2s/blake2s.h botan3-3.12.0+dfsg/src/lib/hash/blake2s/blake2s.h --- botan3-3.7.1+dfsg/src/lib/hash/blake2s/blake2s.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/blake2s/blake2s.h 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,8 @@ /* * BLAKE2s - * (C) 2023 Richard Huveneers + * (C) 2023, 2025 Richard Huveneers + * (C) 2025 Kagan Can Sit + * (C) 2025 René Meusel, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -9,6 +11,7 @@ #define BOTAN_BLAKE2S_H_ #include +#include namespace Botan { @@ -16,15 +19,23 @@ * BLAKE2s */ class BLAKE2s final : public HashFunction { + private: + static constexpr size_t block_size = 64; + public: explicit BLAKE2s(size_t output_bits = 256); ~BLAKE2s() override; + BLAKE2s(const BLAKE2s&) = default; + BLAKE2s& operator=(const BLAKE2s&) = delete; + BLAKE2s(BLAKE2s&&) = delete; + BLAKE2s& operator=(BLAKE2s&&) = delete; + std::string name() const override; size_t output_length() const override { return m_outlen; } - size_t hash_block_size() const override { return 64; } + size_t hash_block_size() const override { return block_size; } std::unique_ptr copy_state() const override; @@ -33,16 +44,17 @@ void clear() override; private: - void add_data(std::span) override; - void final_result(std::span) override; - void state_init(size_t outlen, const uint8_t* key, size_t keylen); - void compress(bool last); - - uint8_t m_b[64]; // input buffer - uint32_t m_h[8]; // chained state - uint32_t m_t[2]; // total number of bytes - uint8_t m_c; // pointer for b[] - size_t m_outlen; // digest size + void add_data(std::span input) override; + void final_result(std::span output) override; + void state_init(size_t outlen); + void compress(bool last, std::span buf); + + private: + uint64_t m_bytes_processed = 0; + AlignmentBuffer m_buffer; + + std::array m_h{}; // chained state + size_t m_outlen = 0; // digest size }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/checksum/adler32/adler32.h botan3-3.12.0+dfsg/src/lib/hash/checksum/adler32/adler32.h --- botan3-3.7.1+dfsg/src/lib/hash/checksum/adler32/adler32.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/checksum/adler32/adler32.h 2026-05-07 01:38:28.000000000 +0000 @@ -30,14 +30,11 @@ m_S2 = 0; } - Adler32() { clear(); } - - ~Adler32() override { clear(); } - private: - void add_data(std::span) override; - void final_result(std::span) override; - uint16_t m_S1, m_S2; + void add_data(std::span input) override; + void final_result(std::span output) override; + uint16_t m_S1 = 1; + uint16_t m_S2 = 0; }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/checksum/crc24/crc24.cpp botan3-3.12.0+dfsg/src/lib/hash/checksum/crc24/crc24.cpp --- botan3-3.7.1+dfsg/src/lib/hash/checksum/crc24/crc24.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/checksum/crc24/crc24.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -178,7 +178,7 @@ static const uint8_t WA = sizeof(size_t) - 1; // Ensure input is word aligned before processing in parallel - for(; !input.empty() && (reinterpret_cast(input.data()) & WA); input = input.last(input.size() - 1)) { + for(; !input.empty() && (reinterpret_cast(input.data()) & WA) > 0; input = input.last(input.size() - 1)) { tmp = process8(tmp, input.front()); } diff -Nru botan3-3.7.1+dfsg/src/lib/hash/checksum/crc24/crc24.h botan3-3.12.0+dfsg/src/lib/hash/checksum/crc24/crc24.h --- botan3-3.7.1+dfsg/src/lib/hash/checksum/crc24/crc24.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/checksum/crc24/crc24.h 2026-05-07 01:38:28.000000000 +0000 @@ -28,16 +28,12 @@ std::unique_ptr copy_state() const override; - void clear() override { m_crc = 0XCE04B7L; } - - CRC24() { clear(); } - - ~CRC24() override { clear(); } + void clear() override { m_crc = 0xCE04B7; } private: - void add_data(std::span) override; - void final_result(std::span) override; - uint32_t m_crc; + void add_data(std::span input) override; + void final_result(std::span output) override; + uint32_t m_crc = 0xCE04B7; }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/checksum/crc32/crc32.cpp botan3-3.12.0+dfsg/src/lib/hash/checksum/crc32/crc32.cpp --- botan3-3.7.1+dfsg/src/lib/hash/checksum/crc32/crc32.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/checksum/crc32/crc32.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -50,31 +50,31 @@ * Update a CRC32 Checksum */ void CRC32::add_data(std::span input) { - uint32_t tmp = m_crc; + uint32_t crc = m_crc; for(; input.size() >= 16; input = input.last(input.size() - 16)) { - tmp = CRC32_T0[(tmp ^ input[0]) & 0xFF] ^ (tmp >> 8); - tmp = CRC32_T0[(tmp ^ input[1]) & 0xFF] ^ (tmp >> 8); - tmp = CRC32_T0[(tmp ^ input[2]) & 0xFF] ^ (tmp >> 8); - tmp = CRC32_T0[(tmp ^ input[3]) & 0xFF] ^ (tmp >> 8); - tmp = CRC32_T0[(tmp ^ input[4]) & 0xFF] ^ (tmp >> 8); - tmp = CRC32_T0[(tmp ^ input[5]) & 0xFF] ^ (tmp >> 8); - tmp = CRC32_T0[(tmp ^ input[6]) & 0xFF] ^ (tmp >> 8); - tmp = CRC32_T0[(tmp ^ input[7]) & 0xFF] ^ (tmp >> 8); - tmp = CRC32_T0[(tmp ^ input[8]) & 0xFF] ^ (tmp >> 8); - tmp = CRC32_T0[(tmp ^ input[9]) & 0xFF] ^ (tmp >> 8); - tmp = CRC32_T0[(tmp ^ input[10]) & 0xFF] ^ (tmp >> 8); - tmp = CRC32_T0[(tmp ^ input[11]) & 0xFF] ^ (tmp >> 8); - tmp = CRC32_T0[(tmp ^ input[12]) & 0xFF] ^ (tmp >> 8); - tmp = CRC32_T0[(tmp ^ input[13]) & 0xFF] ^ (tmp >> 8); - tmp = CRC32_T0[(tmp ^ input[14]) & 0xFF] ^ (tmp >> 8); - tmp = CRC32_T0[(tmp ^ input[15]) & 0xFF] ^ (tmp >> 8); + crc = CRC32_T0[(crc ^ input[0]) & 0xFF] ^ (crc >> 8); + crc = CRC32_T0[(crc ^ input[1]) & 0xFF] ^ (crc >> 8); + crc = CRC32_T0[(crc ^ input[2]) & 0xFF] ^ (crc >> 8); + crc = CRC32_T0[(crc ^ input[3]) & 0xFF] ^ (crc >> 8); + crc = CRC32_T0[(crc ^ input[4]) & 0xFF] ^ (crc >> 8); + crc = CRC32_T0[(crc ^ input[5]) & 0xFF] ^ (crc >> 8); + crc = CRC32_T0[(crc ^ input[6]) & 0xFF] ^ (crc >> 8); + crc = CRC32_T0[(crc ^ input[7]) & 0xFF] ^ (crc >> 8); + crc = CRC32_T0[(crc ^ input[8]) & 0xFF] ^ (crc >> 8); + crc = CRC32_T0[(crc ^ input[9]) & 0xFF] ^ (crc >> 8); + crc = CRC32_T0[(crc ^ input[10]) & 0xFF] ^ (crc >> 8); + crc = CRC32_T0[(crc ^ input[11]) & 0xFF] ^ (crc >> 8); + crc = CRC32_T0[(crc ^ input[12]) & 0xFF] ^ (crc >> 8); + crc = CRC32_T0[(crc ^ input[13]) & 0xFF] ^ (crc >> 8); + crc = CRC32_T0[(crc ^ input[14]) & 0xFF] ^ (crc >> 8); + crc = CRC32_T0[(crc ^ input[15]) & 0xFF] ^ (crc >> 8); } - for(size_t i = 0; i != input.size(); ++i) { - tmp = CRC32_T0[(tmp ^ input[i]) & 0xFF] ^ (tmp >> 8); + for(const uint8_t b : input) { + crc = CRC32_T0[(crc ^ b) & 0xFF] ^ (crc >> 8); } - m_crc = tmp; + m_crc = crc; } /* diff -Nru botan3-3.7.1+dfsg/src/lib/hash/checksum/crc32/crc32.h botan3-3.12.0+dfsg/src/lib/hash/checksum/crc32/crc32.h --- botan3-3.7.1+dfsg/src/lib/hash/checksum/crc32/crc32.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/checksum/crc32/crc32.h 2026-05-07 01:38:28.000000000 +0000 @@ -27,14 +27,10 @@ void clear() override { m_crc = 0xFFFFFFFF; } - CRC32() { clear(); } - - ~CRC32() override { clear(); } - private: - void add_data(std::span) override; - void final_result(std::span) override; - uint32_t m_crc; + void add_data(std::span input) override; + void final_result(std::span output) override; + uint32_t m_crc = 0xFFFFFFFF; }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/comb4p/comb4p.cpp botan3-3.12.0+dfsg/src/lib/hash/comb4p/comb4p.cpp --- botan3-3.7.1+dfsg/src/lib/hash/comb4p/comb4p.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/comb4p/comb4p.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,8 +8,9 @@ #include #include +#include +#include #include -#include namespace Botan { diff -Nru botan3-3.7.1+dfsg/src/lib/hash/gost_3411/gost_3411.cpp botan3-3.12.0+dfsg/src/lib/hash/gost_3411/gost_3411.cpp --- botan3-3.7.1+dfsg/src/lib/hash/gost_3411/gost_3411.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/gost_3411/gost_3411.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,17 +7,15 @@ #include +#include #include -#include namespace Botan { /** * GOST 34.11 Constructor */ -GOST_34_11::GOST_34_11() : m_cipher(GOST_28147_89_Params("R3411_CryptoPro")), m_sum(32), m_hash(32) { - m_count = 0; -} +GOST_34_11::GOST_34_11() : m_cipher(GOST_28147_89_Params("R3411_CryptoPro")), m_sum(32), m_hash(32), m_count(0) {} void GOST_34_11::clear() { m_cipher.clear(); @@ -59,14 +57,15 @@ void GOST_34_11::compress_n(const uint8_t input[], size_t blocks) { for(size_t i = 0; i != blocks; ++i) { for(uint16_t j = 0, carry = 0; j != 32; ++j) { - uint16_t s = m_sum[j] + input[32 * i + j] + carry; + const uint16_t s = m_sum[j] + input[32 * i + j] + carry; carry = get_byte<0>(s); m_sum[j] = get_byte<1>(s); } uint8_t S[32] = {0}; - uint64_t U[4], V[4]; + uint64_t U[4]; + uint64_t V[4]; load_be(U, m_hash.data(), 4); load_be(V, input + 32 * i, 4); @@ -89,7 +88,7 @@ } // A(x) - uint64_t A_U = U[0]; + const uint64_t A_U = U[0]; U[0] = U[1]; U[1] = U[2]; U[2] = U[3]; @@ -104,8 +103,8 @@ } // A(A(x)) - uint64_t AA_V_1 = V[0] ^ V[1]; - uint64_t AA_V_2 = V[1] ^ V[2]; + const uint64_t AA_V_1 = V[0] ^ V[1]; + const uint64_t AA_V_2 = V[1] ^ V[2]; V[0] = V[2]; V[1] = V[3]; V[2] = AA_V_1; diff -Nru botan3-3.7.1+dfsg/src/lib/hash/gost_3411/gost_3411.h botan3-3.12.0+dfsg/src/lib/hash/gost_3411/gost_3411.h --- botan3-3.7.1+dfsg/src/lib/hash/gost_3411/gost_3411.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/gost_3411/gost_3411.h 2026-05-07 01:38:28.000000000 +0000 @@ -36,8 +36,8 @@ private: void compress_n(const uint8_t input[], size_t blocks); - void add_data(std::span) override; - void final_result(std::span) override; + void add_data(std::span input) override; + void final_result(std::span output) override; GOST_28147_89 m_cipher; AlignmentBuffer m_buffer; diff -Nru botan3-3.7.1+dfsg/src/lib/hash/hash.cpp botan3-3.12.0+dfsg/src/lib/hash/hash.cpp --- botan3-3.7.1+dfsg/src/lib/hash/hash.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/hash.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -14,6 +14,10 @@ #include #endif +#if defined(BOTAN_HAS_ASCON_HASH256) + #include +#endif + #if defined(BOTAN_HAS_CRC24) #include #endif @@ -185,6 +189,12 @@ } #endif +#if defined(BOTAN_HAS_ASCON_HASH256) + if(algo_spec == "Ascon-Hash256") { + return std::make_unique(); + } +#endif + #if defined(BOTAN_HAS_CRC24) if(algo_spec == "CRC24") { return std::make_unique(); diff -Nru botan3-3.7.1+dfsg/src/lib/hash/hash.h botan3-3.12.0+dfsg/src/lib/hash/hash.h --- botan3-3.7.1+dfsg/src/lib/hash/hash.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/hash.h 2026-05-07 01:38:28.000000000 +0000 @@ -48,8 +48,6 @@ */ virtual std::string provider() const { return "base"; } - ~HashFunction() override = default; - /** * Reset the state. */ diff -Nru botan3-3.7.1+dfsg/src/lib/hash/keccak/keccak.cpp botan3-3.12.0+dfsg/src/lib/hash/keccak/keccak.cpp --- botan3-3.7.1+dfsg/src/lib/hash/keccak/keccak.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/keccak/keccak.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,7 +9,6 @@ #include #include -#include namespace Botan { @@ -17,7 +16,9 @@ return std::make_unique(*this); } -Keccak_1600::Keccak_1600(size_t output_bits) : m_keccak(2 * output_bits, 0, 0), m_output_length(output_bits / 8) { +Keccak_1600::Keccak_1600(size_t output_bits) : + m_keccak({.capacity_bits = 2 * output_bits, .padding = KeccakPadding::keccak1600()}), + m_output_length(output_bits / 8) { // We only support the parameters for the SHA-3 proposal if(output_bits != 224 && output_bits != 256 && output_bits != 384 && output_bits != 512) { diff -Nru botan3-3.7.1+dfsg/src/lib/hash/md4/md4.cpp botan3-3.12.0+dfsg/src/lib/hash/md4/md4.cpp --- botan3-3.7.1+dfsg/src/lib/hash/md4/md4.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/md4/md4.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -75,11 +76,14 @@ * MD4 Compression Function */ void MD4::compress_n(digest_type& digest, std::span input, size_t blocks) { - uint32_t A = digest[0], B = digest[1], C = digest[2], D = digest[3]; + uint32_t A = digest[0]; + uint32_t B = digest[1]; + uint32_t C = digest[2]; + uint32_t D = digest[3]; BufferSlicer in(input); - std::array M; + std::array M{}; for(size_t i = 0; i != blocks; ++i) { load_le(M, in.take()); diff -Nru botan3-3.7.1+dfsg/src/lib/hash/md5/md5.cpp botan3-3.12.0+dfsg/src/lib/hash/md5/md5.cpp --- botan3-3.7.1+dfsg/src/lib/hash/md5/md5.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/md5/md5.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,9 +8,9 @@ #include #include +#include #include #include -#include #include @@ -60,8 +60,11 @@ * MD5 Compression Function */ void MD5::compress_n(MD5::digest_type& digest, std::span input, size_t blocks) { - uint32_t A = digest[0], B = digest[1], C = digest[2], D = digest[3]; - std::array M; + uint32_t A = digest[0]; + uint32_t B = digest[1]; + uint32_t C = digest[2]; + uint32_t D = digest[3]; + std::array M{}; BufferSlicer in(input); diff -Nru botan3-3.7.1+dfsg/src/lib/hash/mdx_hash/info.txt botan3-3.12.0+dfsg/src/lib/hash/mdx_hash/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/mdx_hash/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/mdx_hash/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + MDX_HASH_FUNCTION -> 20131128 - + name -> "Merkle-Damgård Helper" diff -Nru botan3-3.7.1+dfsg/src/lib/hash/mdx_hash/mdx_hash.h botan3-3.12.0+dfsg/src/lib/hash/mdx_hash/mdx_hash.h --- botan3-3.7.1+dfsg/src/lib/hash/mdx_hash/mdx_hash.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/mdx_hash/mdx_hash.h 2026-05-07 01:38:28.000000000 +0000 @@ -12,12 +12,12 @@ #include #include +#include #include -#include namespace Botan { -enum class MD_Endian { +enum class MD_Endian : uint8_t { Little, Big, }; @@ -122,7 +122,7 @@ private: typename MD::digest_type m_digest; - uint64_t m_count; + uint64_t m_count = 0; AlignmentBuffer m_buffer; }; diff -Nru botan3-3.7.1+dfsg/src/lib/hash/par_hash/par_hash.cpp botan3-3.12.0+dfsg/src/lib/hash/par_hash/par_hash.cpp --- botan3-3.7.1+dfsg/src/lib/hash/par_hash/par_hash.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/par_hash/par_hash.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,7 +7,7 @@ #include -#include +#include #include diff -Nru botan3-3.7.1+dfsg/src/lib/hash/par_hash/par_hash.h botan3-3.12.0+dfsg/src/lib/hash/par_hash/par_hash.h --- botan3-3.7.1+dfsg/src/lib/hash/par_hash/par_hash.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/par_hash/par_hash.h 2026-05-07 01:38:28.000000000 +0000 @@ -32,11 +32,14 @@ explicit Parallel(std::vector>& hashes); Parallel(const Parallel&) = delete; + Parallel(Parallel&&) = default; Parallel& operator=(const Parallel&) = delete; + Parallel& operator=(Parallel&&) = default; + ~Parallel() override = default; private: - void add_data(std::span) override; - void final_result(std::span) override; + void add_data(std::span input) override; + void final_result(std::span output) override; std::vector> m_hashes; }; diff -Nru botan3-3.7.1+dfsg/src/lib/hash/rmd160/rmd160.cpp botan3-3.12.0+dfsg/src/lib/hash/rmd160/rmd160.cpp --- botan3-3.7.1+dfsg/src/lib/hash/rmd160/rmd160.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/rmd160/rmd160.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,9 +8,9 @@ #include #include +#include #include #include -#include #include @@ -74,17 +74,33 @@ * RIPEMD-160 Compression Function */ void RIPEMD_160::compress_n(digest_type& digest, std::span input, size_t blocks) { - const uint32_t MAGIC2 = 0x5A827999, MAGIC3 = 0x6ED9EBA1, MAGIC4 = 0x8F1BBCDC, MAGIC5 = 0xA953FD4E, - MAGIC6 = 0x50A28BE6, MAGIC7 = 0x5C4DD124, MAGIC8 = 0x6D703EF3, MAGIC9 = 0x7A6D76E9; - std::array M; + constexpr uint32_t MAGIC2 = 0x5A827999; + constexpr uint32_t MAGIC3 = 0x6ED9EBA1; + constexpr uint32_t MAGIC4 = 0x8F1BBCDC; + constexpr uint32_t MAGIC5 = 0xA953FD4E; + constexpr uint32_t MAGIC6 = 0x50A28BE6; + constexpr uint32_t MAGIC7 = 0x5C4DD124; + constexpr uint32_t MAGIC8 = 0x6D703EF3; + constexpr uint32_t MAGIC9 = 0x7A6D76E9; + + std::array M{}; BufferSlicer in(input); for(size_t i = 0; i != blocks; ++i) { load_le(M, in.take()); - uint32_t A1 = digest[0], A2 = A1, B1 = digest[1], B2 = B1, C1 = digest[2], C2 = C1, D1 = digest[3], D2 = D1, - E1 = digest[4], E2 = E1; + uint32_t A1 = digest[0]; + uint32_t B1 = digest[1]; + uint32_t C1 = digest[2]; + uint32_t D1 = digest[3]; + uint32_t E1 = digest[4]; + + uint32_t A2 = A1; + uint32_t B2 = B1; + uint32_t C2 = C1; + uint32_t D2 = D1; + uint32_t E2 = E1; // clang-format off diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1.cpp botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,55 +7,16 @@ #include -#include -#include #include #include -#include - +#include #include -namespace Botan { - -namespace SHA1_F { - -namespace { - -/* -* SHA-1 F1 Function -*/ -inline void F1(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t msg) { - E += choose(B, C, D) + msg + 0x5A827999 + rotl<5>(A); - B = rotl<30>(B); -} - -/* -* SHA-1 F2 Function -*/ -inline void F2(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t msg) { - E += (B ^ C ^ D) + msg + 0x6ED9EBA1 + rotl<5>(A); - B = rotl<30>(B); -} - -/* -* SHA-1 F3 Function -*/ -inline void F3(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t msg) { - E += majority(B, C, D) + msg + 0x8F1BBCDC + rotl<5>(A); - B = rotl<30>(B); -} - -/* -* SHA-1 F4 Function -*/ -inline void F4(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t msg) { - E += (B ^ C ^ D) + msg + 0xCA62C1D6 + rotl<5>(A); - B = rotl<30>(B); -} - -} // namespace +#if defined(BOTAN_HAS_CPUID) + #include +#endif -} // namespace SHA1_F +namespace Botan { /* * SHA-1 Compression Function @@ -64,26 +25,35 @@ using namespace SHA1_F; #if defined(BOTAN_HAS_SHA1_X86_SHA_NI) - if(CPUID::has_intel_sha()) { + if(CPUID::has(CPUID::Feature::SHA)) { return sha1_compress_x86(digest, input, blocks); } #endif #if defined(BOTAN_HAS_SHA1_ARMV8) - if(CPUID::has_arm_sha1()) { + if(CPUID::has(CPUID::Feature::SHA1)) { return sha1_armv8_compress_n(digest, input, blocks); } #endif -#if defined(BOTAN_HAS_SHA1_SSE2) - if(CPUID::has_sse2()) { - return sse2_compress_n(digest, input, blocks); +#if defined(BOTAN_HAS_SHA1_AVX2) + if(CPUID::has(CPUID::Feature::AVX2, CPUID::Feature::BMI)) { + return avx2_compress_n(digest, input, blocks); } +#endif +#if defined(BOTAN_HAS_SHA1_SIMD_4X32) + if(CPUID::has(CPUID::Feature::SIMD_4X32)) { + return simd_compress_n(digest, input, blocks); + } #endif - uint32_t A = digest[0], B = digest[1], C = digest[2], D = digest[3], E = digest[4]; - std::array W; + uint32_t A = digest[0]; + uint32_t B = digest[1]; + uint32_t C = digest[2]; + uint32_t D = digest[3]; + uint32_t E = digest[4]; + std::array W{}; auto W_in = std::span{W}.first(); BufferSlicer in(input); @@ -106,89 +76,89 @@ // clang-format on - F1(A, B, C, D, E, W[0]); - F1(E, A, B, C, D, W[1]); - F1(D, E, A, B, C, W[2]); - F1(C, D, E, A, B, W[3]); - F1(B, C, D, E, A, W[4]); - F1(A, B, C, D, E, W[5]); - F1(E, A, B, C, D, W[6]); - F1(D, E, A, B, C, W[7]); - F1(C, D, E, A, B, W[8]); - F1(B, C, D, E, A, W[9]); - F1(A, B, C, D, E, W[10]); - F1(E, A, B, C, D, W[11]); - F1(D, E, A, B, C, W[12]); - F1(C, D, E, A, B, W[13]); - F1(B, C, D, E, A, W[14]); - F1(A, B, C, D, E, W[15]); - F1(E, A, B, C, D, W[16]); - F1(D, E, A, B, C, W[17]); - F1(C, D, E, A, B, W[18]); - F1(B, C, D, E, A, W[19]); - - F2(A, B, C, D, E, W[20]); - F2(E, A, B, C, D, W[21]); - F2(D, E, A, B, C, W[22]); - F2(C, D, E, A, B, W[23]); - F2(B, C, D, E, A, W[24]); - F2(A, B, C, D, E, W[25]); - F2(E, A, B, C, D, W[26]); - F2(D, E, A, B, C, W[27]); - F2(C, D, E, A, B, W[28]); - F2(B, C, D, E, A, W[29]); - F2(A, B, C, D, E, W[30]); - F2(E, A, B, C, D, W[31]); - F2(D, E, A, B, C, W[32]); - F2(C, D, E, A, B, W[33]); - F2(B, C, D, E, A, W[34]); - F2(A, B, C, D, E, W[35]); - F2(E, A, B, C, D, W[36]); - F2(D, E, A, B, C, W[37]); - F2(C, D, E, A, B, W[38]); - F2(B, C, D, E, A, W[39]); - - F3(A, B, C, D, E, W[40]); - F3(E, A, B, C, D, W[41]); - F3(D, E, A, B, C, W[42]); - F3(C, D, E, A, B, W[43]); - F3(B, C, D, E, A, W[44]); - F3(A, B, C, D, E, W[45]); - F3(E, A, B, C, D, W[46]); - F3(D, E, A, B, C, W[47]); - F3(C, D, E, A, B, W[48]); - F3(B, C, D, E, A, W[49]); - F3(A, B, C, D, E, W[50]); - F3(E, A, B, C, D, W[51]); - F3(D, E, A, B, C, W[52]); - F3(C, D, E, A, B, W[53]); - F3(B, C, D, E, A, W[54]); - F3(A, B, C, D, E, W[55]); - F3(E, A, B, C, D, W[56]); - F3(D, E, A, B, C, W[57]); - F3(C, D, E, A, B, W[58]); - F3(B, C, D, E, A, W[59]); - - F4(A, B, C, D, E, W[60]); - F4(E, A, B, C, D, W[61]); - F4(D, E, A, B, C, W[62]); - F4(C, D, E, A, B, W[63]); - F4(B, C, D, E, A, W[64]); - F4(A, B, C, D, E, W[65]); - F4(E, A, B, C, D, W[66]); - F4(D, E, A, B, C, W[67]); - F4(C, D, E, A, B, W[68]); - F4(B, C, D, E, A, W[69]); - F4(A, B, C, D, E, W[70]); - F4(E, A, B, C, D, W[71]); - F4(D, E, A, B, C, W[72]); - F4(C, D, E, A, B, W[73]); - F4(B, C, D, E, A, W[74]); - F4(A, B, C, D, E, W[75]); - F4(E, A, B, C, D, W[76]); - F4(D, E, A, B, C, W[77]); - F4(C, D, E, A, B, W[78]); - F4(B, C, D, E, A, W[79]); + F1(A, B, C, D, E, W[0] + K1); + F1(E, A, B, C, D, W[1] + K1); + F1(D, E, A, B, C, W[2] + K1); + F1(C, D, E, A, B, W[3] + K1); + F1(B, C, D, E, A, W[4] + K1); + F1(A, B, C, D, E, W[5] + K1); + F1(E, A, B, C, D, W[6] + K1); + F1(D, E, A, B, C, W[7] + K1); + F1(C, D, E, A, B, W[8] + K1); + F1(B, C, D, E, A, W[9] + K1); + F1(A, B, C, D, E, W[10] + K1); + F1(E, A, B, C, D, W[11] + K1); + F1(D, E, A, B, C, W[12] + K1); + F1(C, D, E, A, B, W[13] + K1); + F1(B, C, D, E, A, W[14] + K1); + F1(A, B, C, D, E, W[15] + K1); + F1(E, A, B, C, D, W[16] + K1); + F1(D, E, A, B, C, W[17] + K1); + F1(C, D, E, A, B, W[18] + K1); + F1(B, C, D, E, A, W[19] + K1); + + F2(A, B, C, D, E, W[20] + K2); + F2(E, A, B, C, D, W[21] + K2); + F2(D, E, A, B, C, W[22] + K2); + F2(C, D, E, A, B, W[23] + K2); + F2(B, C, D, E, A, W[24] + K2); + F2(A, B, C, D, E, W[25] + K2); + F2(E, A, B, C, D, W[26] + K2); + F2(D, E, A, B, C, W[27] + K2); + F2(C, D, E, A, B, W[28] + K2); + F2(B, C, D, E, A, W[29] + K2); + F2(A, B, C, D, E, W[30] + K2); + F2(E, A, B, C, D, W[31] + K2); + F2(D, E, A, B, C, W[32] + K2); + F2(C, D, E, A, B, W[33] + K2); + F2(B, C, D, E, A, W[34] + K2); + F2(A, B, C, D, E, W[35] + K2); + F2(E, A, B, C, D, W[36] + K2); + F2(D, E, A, B, C, W[37] + K2); + F2(C, D, E, A, B, W[38] + K2); + F2(B, C, D, E, A, W[39] + K2); + + F3(A, B, C, D, E, W[40] + K3); + F3(E, A, B, C, D, W[41] + K3); + F3(D, E, A, B, C, W[42] + K3); + F3(C, D, E, A, B, W[43] + K3); + F3(B, C, D, E, A, W[44] + K3); + F3(A, B, C, D, E, W[45] + K3); + F3(E, A, B, C, D, W[46] + K3); + F3(D, E, A, B, C, W[47] + K3); + F3(C, D, E, A, B, W[48] + K3); + F3(B, C, D, E, A, W[49] + K3); + F3(A, B, C, D, E, W[50] + K3); + F3(E, A, B, C, D, W[51] + K3); + F3(D, E, A, B, C, W[52] + K3); + F3(C, D, E, A, B, W[53] + K3); + F3(B, C, D, E, A, W[54] + K3); + F3(A, B, C, D, E, W[55] + K3); + F3(E, A, B, C, D, W[56] + K3); + F3(D, E, A, B, C, W[57] + K3); + F3(C, D, E, A, B, W[58] + K3); + F3(B, C, D, E, A, W[59] + K3); + + F4(A, B, C, D, E, W[60] + K4); + F4(E, A, B, C, D, W[61] + K4); + F4(D, E, A, B, C, W[62] + K4); + F4(C, D, E, A, B, W[63] + K4); + F4(B, C, D, E, A, W[64] + K4); + F4(A, B, C, D, E, W[65] + K4); + F4(E, A, B, C, D, W[66] + K4); + F4(D, E, A, B, C, W[67] + K4); + F4(C, D, E, A, B, W[68] + K4); + F4(B, C, D, E, A, W[69] + K4); + F4(A, B, C, D, E, W[70] + K4); + F4(E, A, B, C, D, W[71] + K4); + F4(D, E, A, B, C, W[72] + K4); + F4(C, D, E, A, B, W[73] + K4); + F4(B, C, D, E, A, W[74] + K4); + F4(A, B, C, D, E, W[75] + K4); + F4(E, A, B, C, D, W[76] + K4); + F4(D, E, A, B, C, W[77] + K4); + F4(C, D, E, A, B, W[78] + K4); + F4(B, C, D, E, A, W[79] + K4); A = (digest[0] += A); B = (digest[1] += B); @@ -207,20 +177,26 @@ std::string SHA_1::provider() const { #if defined(BOTAN_HAS_SHA1_X86_SHA_NI) - if(CPUID::has_intel_sha()) { - return "intel_sha"; + if(auto feat = CPUID::check(CPUID::Feature::SHA)) { + return *feat; } #endif #if defined(BOTAN_HAS_SHA1_ARMV8) - if(CPUID::has_arm_sha1()) { - return "armv8_sha"; + if(auto feat = CPUID::check(CPUID::Feature::SHA1)) { + return *feat; + } +#endif + +#if defined(BOTAN_HAS_SHA1_AVX2) + if(auto feat = CPUID::check(CPUID::Feature::AVX2, CPUID::Feature::BMI)) { + return *feat; } #endif -#if defined(BOTAN_HAS_SHA1_SSE2) - if(CPUID::has_sse2()) { - return "sse2"; +#if defined(BOTAN_HAS_SHA1_SIMD_4X32) + if(auto feat = CPUID::check(CPUID::Feature::SIMD_4X32)) { + return *feat; } #endif diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1.h botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1.h --- botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1.h 2026-05-07 01:38:28.000000000 +0000 @@ -47,8 +47,12 @@ static void sha1_armv8_compress_n(digest_type& digest, std::span blocks, size_t block_count); #endif -#if defined(BOTAN_HAS_SHA1_SSE2) - static void sse2_compress_n(digest_type& digest, std::span blocks, size_t block_count); +#if defined(BOTAN_HAS_SHA1_SIMD_4X32) + static void simd_compress_n(digest_type& digest, std::span blocks, size_t block_count); +#endif + +#if defined(BOTAN_HAS_SHA1_AVX2) + static void avx2_compress_n(digest_type& digest, std::span blocks, size_t block_count); #endif #if defined(BOTAN_HAS_SHA1_X86_SHA_NI) diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1_armv8/info.txt botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_armv8/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1_armv8/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_armv8/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + SHA1_ARMV8 -> 20170117 - + name -> "SHA-1 ARMv8" @@ -10,3 +10,7 @@ armv8crypto + + +cpuid + diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1_armv8/sha1_armv8.cpp botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_armv8/sha1_armv8.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1_armv8/sha1_armv8.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_armv8/sha1_armv8.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,8 @@ */ #include + +#include #include namespace Botan { @@ -16,47 +18,40 @@ * SHA-1 using CPU instructions in ARMv8 */ //static -BOTAN_FUNC_ISA("+crypto+sha2") -void SHA_1::sha1_armv8_compress_n(digest_type& digest, std::span input8, size_t blocks) { - uint32x4_t ABCD; - uint32_t E0; - +void BOTAN_FN_ISA_SHA2 SHA_1::sha1_armv8_compress_n(digest_type& digest, + std::span input8, + size_t blocks) { // Load magic constants const uint32x4_t C0 = vdupq_n_u32(0x5A827999); const uint32x4_t C1 = vdupq_n_u32(0x6ED9EBA1); const uint32x4_t C2 = vdupq_n_u32(0x8F1BBCDC); const uint32x4_t C3 = vdupq_n_u32(0xCA62C1D6); - ABCD = vld1q_u32(&digest[0]); - E0 = digest[4]; + uint32x4_t ABCD = vld1q_u32(&digest[0]); // NOLINT(*-container-data-pointer) + uint32_t E0 = digest[4]; - // Intermediate void* cast due to https://llvm.org/bugs/show_bug.cgi?id=20670 - const uint32_t* input32 = reinterpret_cast(reinterpret_cast(input8.data())); + const uint32_t* input32 = reinterpret_cast(input8.data()); - while(blocks) { + while(blocks > 0) { // Save current hash const uint32x4_t ABCD_SAVED = ABCD; const uint32_t E0_SAVED = E0; - uint32x4_t MSG0, MSG1, MSG2, MSG3; - uint32x4_t TMP0, TMP1; - uint32_t E1; - - MSG0 = vld1q_u32(input32 + 0); - MSG1 = vld1q_u32(input32 + 4); - MSG2 = vld1q_u32(input32 + 8); - MSG3 = vld1q_u32(input32 + 12); + uint32x4_t MSG0 = vld1q_u32(input32 + 0); + uint32x4_t MSG1 = vld1q_u32(input32 + 4); + uint32x4_t MSG2 = vld1q_u32(input32 + 8); + uint32x4_t MSG3 = vld1q_u32(input32 + 12); MSG0 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG0))); MSG1 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG1))); MSG2 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG2))); MSG3 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG3))); - TMP0 = vaddq_u32(MSG0, C0); - TMP1 = vaddq_u32(MSG1, C0); + uint32x4_t TMP0 = vaddq_u32(MSG0, C0); + uint32x4_t TMP1 = vaddq_u32(MSG1, C0); // Rounds 0-3 - E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0)); + uint32_t E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0)); ABCD = vsha1cq_u32(ABCD, E0, TMP0); TMP0 = vaddq_u32(MSG2, C0); MSG0 = vsha1su0q_u32(MSG0, MSG1, MSG2); @@ -177,7 +172,6 @@ E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0)); ABCD = vsha1pq_u32(ABCD, E1, TMP1); TMP1 = vaddq_u32(MSG3, C3); - MSG0 = vsha1su1q_u32(MSG0, MSG3); // Rounds 72-75 E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0)); @@ -196,7 +190,7 @@ } // Save digest - vst1q_u32(&digest[0], ABCD); + vst1q_u32(&digest[0], ABCD); // NOLINT(*-container-data-pointer) digest[4] = E0; } diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1_avx2/info.txt botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_avx2/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1_avx2/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_avx2/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,24 @@ + +SHA1_AVX2 -> 20250505 + + + +name -> "SHA-1 AVX2/BMI2" +brief -> "SHA-1 using AVX2/BMI2 instructions" + + + +avx2 +bmi2 + + + +x86_32 +x86_64 +x32 + + + +cpuid +simd_avx2 + diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1_avx2/sha1_avx2.cpp botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_avx2/sha1_avx2.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1_avx2/sha1_avx2.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_avx2/sha1_avx2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,554 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include + +namespace Botan { + +namespace { + +/* +* This is exactly the same approach as used in sha1_simd.cpp, just done +* twice in the two AVX2 "lanes" - remember that alignr and slli/srli +* here are working not across the entire register but instead as if +* there were two smaller vectors. +*/ +BOTAN_FN_ISA_AVX2_BMI2 BOTAN_FORCE_INLINE SIMD_8x32 sha1_avx2_next_w(SIMD_8x32& XW0, + SIMD_8x32 XW1, + SIMD_8x32 XW2, + SIMD_8x32 XW3) { + SIMD_8x32 T0 = XW0; // W[t-16..t-13] + T0 ^= SIMD_8x32(_mm256_alignr_epi8(XW1.raw(), XW0.raw(), 8)); + T0 ^= XW2; // W[t-8..t-5] + T0 ^= SIMD_8x32(_mm256_srli_si256(XW3.raw(), 4)); // W[t-3..t-1] || 0 + + /* unrotated W[t]..W[t+2] in T0 ... still need W[t+3] */ + + // Extract w[t+0] into T2 + auto T2 = SIMD_8x32(_mm256_slli_si256(T0.raw(), 3 * 4)); + + // Main rotation + T0 = T0.rotl<1>(); + + // Rotation of W[t+3] has rot by 2 to account for us working on non-rotated words + T2 = T2.rotl<2>(); + + // Merge rol(W[t+0], 1) into W[t+3] + T0 ^= T2; + + XW0 = T0; + return T0; +} + +/* +* Helper for word permutation with zeroing because AVX2 is awful +* +* Clang and GCC both compile this to a couple of stored constants plus +* a vpermd/vpand pair. +*/ +template +BOTAN_FN_ISA_AVX2_BMI2 BOTAN_FORCE_INLINE SIMD_8x32 permute_words(SIMD_8x32 v) { + const __m256i tbl = _mm256_setr_epi32(I0, I1, I2, I3, I4, I5, I6, I7); + const __m256i mask = _mm256_setr_epi32(I0 >= 0 ? 0xFFFFFFFF : 0, + I1 >= 0 ? 0xFFFFFFFF : 0, + I2 >= 0 ? 0xFFFFFFFF : 0, + I3 >= 0 ? 0xFFFFFFFF : 0, + I4 >= 0 ? 0xFFFFFFFF : 0, + I5 >= 0 ? 0xFFFFFFFF : 0, + I6 >= 0 ? 0xFFFFFFFF : 0, + I7 >= 0 ? 0xFFFFFFFF : 0); + + return SIMD_8x32(_mm256_and_si256(mask, _mm256_permutevar8x32_epi32(v.raw(), tbl))); +} + +/* +This is the same approach as the (single buffer) SHA-1 expansion in sha1_simd.cpp +except unrolled further; instead of computing 4 words of W at once, we compute 8. + +However this is complicated both by the SHA-1 recurrence and AVX2 +limitations; it is faster than what's done in sha1_simd.cpp but only just barely. + +The basic idea here is that when computing this (8x per message block): + +W[j + 0] = rotl<1>(W[j - 3] ^ W[j - 8] ^ W[j - 14] ^ W[j - 16]); +W[j + 1] = rotl<1>(W[j - 2] ^ W[j - 7] ^ W[j - 13] ^ W[j - 15]); +W[j + 2] = rotl<1>(W[j - 1] ^ W[j - 6] ^ W[j - 12] ^ W[j - 14]); +W[j + 3] = rotl<1>(W[j ] ^ W[j - 5] ^ W[j - 11] ^ W[j - 13]); +W[j + 4] = rotl<1>(W[j + 1] ^ W[j - 4] ^ W[j - 10] ^ W[j - 12]); +W[j + 5] = rotl<1>(W[j + 2] ^ W[j - 3] ^ W[j - 9] ^ W[j - 11]); +W[j + 6] = rotl<1>(W[j + 3] ^ W[j - 2] ^ W[j - 8] ^ W[j - 10]); +W[j + 7] = rotl<1>(W[j + 4] ^ W[j - 1] ^ W[j - 7] ^ W[j - 9]); + +We instead compute a partial expansion: + +W[j + 0] = rotl<1>(W[j - 3] ^ W[j - 8] ^ W[j - 14] ^ W[j - 16]); +W[j + 1] = rotl<1>(W[j - 2] ^ W[j - 7] ^ W[j - 13] ^ W[j - 15]); +W[j + 2] = rotl<1>(W[j - 1] ^ W[j - 6] ^ W[j - 12] ^ W[j - 14]); +W[j + 3] = rotl<1>( W[j - 5] ^ W[j - 11] ^ W[j - 13]); +W[j + 4] = rotl<1>( W[j - 4] ^ W[j - 10] ^ W[j - 12]); +W[j + 5] = rotl<1>( W[j - 3] ^ W[j - 9] ^ W[j - 11]); +W[j + 6] = rotl<1>( W[j - 2] ^ W[j - 8] ^ W[j - 10]); +W[j + 7] = rotl<1>( W[j - 1] ^ W[j - 7] ^ W[j - 9]); + +Then update it with values that were not available until the first expansion is +completed: + +W[j + 3] ^= rotl<1>(W[j ]); +W[j + 4] ^= rotl<1>(W[j + 1]); +W[j + 5] ^= rotl<1>(W[j + 2]); + +And then update again with values not available until the second expansion step +is completed: + +W[j + 6] ^= rotl<1>(W[j + 3]); +W[j + 7] ^= rotl<1>(W[j + 4]); +*/ + +BOTAN_FN_ISA_AVX2_BMI2 BOTAN_FORCE_INLINE SIMD_8x32 sha1_avx2_next_w2(SIMD_8x32& W0, SIMD_8x32 W2) { + // W[j-16..j-9] ^ W[j-8...j-1] + auto WN = W0 ^ W2; + + // XOR in W[j-3..j-1] || 0 || 0 || 0 || W[j-8...j-7] + WN ^= permute_words<5, 6, 7, -1, -1, -1, 0, 1>(W2); + + // XOR in W[j-14...j-9] || 0 || 0 + WN ^= permute_words<2, 3, 4, 5, 6, 7, -1, -1>(W0); + + // Extract W[j...j+2], rotate, and XOR into W[j+3...j+5] + auto T0 = permute_words<-1, -1, -1, 0, 1, 2, -1, -1>(WN).rotl<2>(); + WN = WN.rotl<1>(); // main block rotation + + WN ^= T0; + + // Extract W[j+3...j+4], rotate, and XOR into W[j+6...j+7] + WN ^= permute_words<-1, -1, -1, -1, -1, -1, 3, 4>(WN).rotl<1>(); + + W0 = WN; + return WN; +} + +} // namespace + +/* +* SHA-1 Compression Function using SIMD for message expansion +*/ +//static +void BOTAN_FN_ISA_AVX2_BMI2 SHA_1::avx2_compress_n(digest_type& digest, std::span input, size_t blocks) { + using namespace SHA1_F; + + const SIMD_8x32 K11 = SIMD_8x32::splat(K1); + const SIMD_8x32 K22 = SIMD_8x32::splat(K2); + const SIMD_8x32 K33 = SIMD_8x32::splat(K3); + const SIMD_8x32 K44 = SIMD_8x32::splat(K4); + + const SIMD_8x32 K12(K1, K1, K1, K1, K2, K2, K2, K2); + const SIMD_8x32 K34(K3, K3, K3, K3, K4, K4, K4, K4); + + uint32_t A = digest[0]; + uint32_t B = digest[1]; + uint32_t C = digest[2]; + uint32_t D = digest[3]; + uint32_t E = digest[4]; + + BufferSlicer in(input); + + while(blocks >= 2) { + const auto block = in.take(2 * block_bytes); + blocks -= 2; + + uint32_t W2[80] = {0}; + + uint32_t PT[4]; + + // NOLINTNEXTLINE(*-container-data-pointer) + SIMD_8x32 XW0 = SIMD_8x32::load_be128(&block[0], &block[64]); + SIMD_8x32 XW1 = SIMD_8x32::load_be128(&block[16], &block[80]); + SIMD_8x32 XW2 = SIMD_8x32::load_be128(&block[32], &block[96]); + SIMD_8x32 XW3 = SIMD_8x32::load_be128(&block[48], &block[112]); + + SIMD_8x32 P0 = XW0 + SIMD_8x32::splat(K1); + SIMD_8x32 P1 = XW1 + SIMD_8x32::splat(K1); + SIMD_8x32 P2 = XW2 + SIMD_8x32::splat(K1); + SIMD_8x32 P3 = XW3 + SIMD_8x32::splat(K1); + + // NOLINTBEGIN(readability-suspicious-call-argument) XW rotation + + P0.store_le128(PT, &W2[0]); + P0 = sha1_avx2_next_w(XW0, XW1, XW2, XW3) + SIMD_8x32::splat(K1); + F1(A, B, C, D, E, PT[0]); + F1(E, A, B, C, D, PT[1]); + F1(D, E, A, B, C, PT[2]); + F1(C, D, E, A, B, PT[3]); + + P1.store_le128(PT, &W2[4]); + P1 = sha1_avx2_next_w(XW1, XW2, XW3, XW0) + SIMD_8x32::splat(K2); + F1(B, C, D, E, A, PT[0]); + F1(A, B, C, D, E, PT[1]); + F1(E, A, B, C, D, PT[2]); + F1(D, E, A, B, C, PT[3]); + + P2.store_le128(PT, &W2[8]); + P2 = sha1_avx2_next_w(XW2, XW3, XW0, XW1) + SIMD_8x32::splat(K2); + F1(C, D, E, A, B, PT[0]); + F1(B, C, D, E, A, PT[1]); + F1(A, B, C, D, E, PT[2]); + F1(E, A, B, C, D, PT[3]); + + P3.store_le128(PT, &W2[12]); + P3 = sha1_avx2_next_w(XW3, XW0, XW1, XW2) + SIMD_8x32::splat(K2); + F1(D, E, A, B, C, PT[0]); + F1(C, D, E, A, B, PT[1]); + F1(B, C, D, E, A, PT[2]); + F1(A, B, C, D, E, PT[3]); + + P0.store_le128(PT, &W2[16]); + P0 = sha1_avx2_next_w(XW0, XW1, XW2, XW3) + SIMD_8x32::splat(K2); + F1(E, A, B, C, D, PT[0]); + F1(D, E, A, B, C, PT[1]); + F1(C, D, E, A, B, PT[2]); + F1(B, C, D, E, A, PT[3]); + + P1.store_le128(PT, &W2[20]); + P1 = sha1_avx2_next_w(XW1, XW2, XW3, XW0) + SIMD_8x32::splat(K2); + F2(A, B, C, D, E, PT[0]); + F2(E, A, B, C, D, PT[1]); + F2(D, E, A, B, C, PT[2]); + F2(C, D, E, A, B, PT[3]); + + P2.store_le128(PT, &W2[24]); + P2 = sha1_avx2_next_w(XW2, XW3, XW0, XW1) + SIMD_8x32::splat(K3); + F2(B, C, D, E, A, PT[0]); + F2(A, B, C, D, E, PT[1]); + F2(E, A, B, C, D, PT[2]); + F2(D, E, A, B, C, PT[3]); + + P3.store_le128(PT, &W2[28]); + P3 = sha1_avx2_next_w(XW3, XW0, XW1, XW2) + SIMD_8x32::splat(K3); + F2(C, D, E, A, B, PT[0]); + F2(B, C, D, E, A, PT[1]); + F2(A, B, C, D, E, PT[2]); + F2(E, A, B, C, D, PT[3]); + + P0.store_le128(PT, &W2[32]); + P0 = sha1_avx2_next_w(XW0, XW1, XW2, XW3) + SIMD_8x32::splat(K3); + F2(D, E, A, B, C, PT[0]); + F2(C, D, E, A, B, PT[1]); + F2(B, C, D, E, A, PT[2]); + F2(A, B, C, D, E, PT[3]); + + P1.store_le128(PT, &W2[36]); + P1 = sha1_avx2_next_w(XW1, XW2, XW3, XW0) + SIMD_8x32::splat(K3); + F2(E, A, B, C, D, PT[0]); + F2(D, E, A, B, C, PT[1]); + F2(C, D, E, A, B, PT[2]); + F2(B, C, D, E, A, PT[3]); + + P2.store_le128(PT, &W2[40]); + P2 = sha1_avx2_next_w(XW2, XW3, XW0, XW1) + SIMD_8x32::splat(K3); + F3(A, B, C, D, E, PT[0]); + F3(E, A, B, C, D, PT[1]); + F3(D, E, A, B, C, PT[2]); + F3(C, D, E, A, B, PT[3]); + + P3.store_le128(PT, &W2[44]); + P3 = sha1_avx2_next_w(XW3, XW0, XW1, XW2) + SIMD_8x32::splat(K4); + F3(B, C, D, E, A, PT[0]); + F3(A, B, C, D, E, PT[1]); + F3(E, A, B, C, D, PT[2]); + F3(D, E, A, B, C, PT[3]); + + P0.store_le128(PT, &W2[48]); + P0 = sha1_avx2_next_w(XW0, XW1, XW2, XW3) + SIMD_8x32::splat(K4); + F3(C, D, E, A, B, PT[0]); + F3(B, C, D, E, A, PT[1]); + F3(A, B, C, D, E, PT[2]); + F3(E, A, B, C, D, PT[3]); + + P1.store_le128(PT, &W2[52]); + P1 = sha1_avx2_next_w(XW1, XW2, XW3, XW0) + SIMD_8x32::splat(K4); + F3(D, E, A, B, C, PT[0]); + F3(C, D, E, A, B, PT[1]); + F3(B, C, D, E, A, PT[2]); + F3(A, B, C, D, E, PT[3]); + + P2.store_le128(PT, &W2[56]); + P2 = sha1_avx2_next_w(XW2, XW3, XW0, XW1) + SIMD_8x32::splat(K4); + F3(E, A, B, C, D, PT[0]); + F3(D, E, A, B, C, PT[1]); + F3(C, D, E, A, B, PT[2]); + F3(B, C, D, E, A, PT[3]); + + P3.store_le128(PT, &W2[60]); + P3 = sha1_avx2_next_w(XW3, XW0, XW1, XW2) + SIMD_8x32::splat(K4); + F4(A, B, C, D, E, PT[0]); + F4(E, A, B, C, D, PT[1]); + F4(D, E, A, B, C, PT[2]); + F4(C, D, E, A, B, PT[3]); + + P0.store_le128(PT, &W2[64]); + F4(B, C, D, E, A, PT[0]); + F4(A, B, C, D, E, PT[1]); + F4(E, A, B, C, D, PT[2]); + F4(D, E, A, B, C, PT[3]); + + P1.store_le128(PT, &W2[68]); + F4(C, D, E, A, B, PT[0]); + F4(B, C, D, E, A, PT[1]); + F4(A, B, C, D, E, PT[2]); + F4(E, A, B, C, D, PT[3]); + + P2.store_le128(PT, &W2[72]); + F4(D, E, A, B, C, PT[0]); + F4(C, D, E, A, B, PT[1]); + F4(B, C, D, E, A, PT[2]); + F4(A, B, C, D, E, PT[3]); + + P3.store_le128(PT, &W2[76]); + F4(E, A, B, C, D, PT[0]); + F4(D, E, A, B, C, PT[1]); + F4(C, D, E, A, B, PT[2]); + F4(B, C, D, E, A, PT[3]); + + // NOLINTEND(readability-suspicious-call-argument) + + A = (digest[0] += A); + B = (digest[1] += B); + C = (digest[2] += C); + D = (digest[3] += D); + E = (digest[4] += E); + + // Second block with pre-expanded message + F1(A, B, C, D, E, W2[0]); + F1(E, A, B, C, D, W2[1]); + F1(D, E, A, B, C, W2[2]); + F1(C, D, E, A, B, W2[3]); + F1(B, C, D, E, A, W2[4]); + F1(A, B, C, D, E, W2[5]); + F1(E, A, B, C, D, W2[6]); + F1(D, E, A, B, C, W2[7]); + F1(C, D, E, A, B, W2[8]); + F1(B, C, D, E, A, W2[9]); + F1(A, B, C, D, E, W2[10]); + F1(E, A, B, C, D, W2[11]); + F1(D, E, A, B, C, W2[12]); + F1(C, D, E, A, B, W2[13]); + F1(B, C, D, E, A, W2[14]); + F1(A, B, C, D, E, W2[15]); + F1(E, A, B, C, D, W2[16]); + F1(D, E, A, B, C, W2[17]); + F1(C, D, E, A, B, W2[18]); + F1(B, C, D, E, A, W2[19]); + F2(A, B, C, D, E, W2[20]); + F2(E, A, B, C, D, W2[21]); + F2(D, E, A, B, C, W2[22]); + F2(C, D, E, A, B, W2[23]); + F2(B, C, D, E, A, W2[24]); + F2(A, B, C, D, E, W2[25]); + F2(E, A, B, C, D, W2[26]); + F2(D, E, A, B, C, W2[27]); + F2(C, D, E, A, B, W2[28]); + F2(B, C, D, E, A, W2[29]); + F2(A, B, C, D, E, W2[30]); + F2(E, A, B, C, D, W2[31]); + F2(D, E, A, B, C, W2[32]); + F2(C, D, E, A, B, W2[33]); + F2(B, C, D, E, A, W2[34]); + F2(A, B, C, D, E, W2[35]); + F2(E, A, B, C, D, W2[36]); + F2(D, E, A, B, C, W2[37]); + F2(C, D, E, A, B, W2[38]); + F2(B, C, D, E, A, W2[39]); + F3(A, B, C, D, E, W2[40]); + F3(E, A, B, C, D, W2[41]); + F3(D, E, A, B, C, W2[42]); + F3(C, D, E, A, B, W2[43]); + F3(B, C, D, E, A, W2[44]); + F3(A, B, C, D, E, W2[45]); + F3(E, A, B, C, D, W2[46]); + F3(D, E, A, B, C, W2[47]); + F3(C, D, E, A, B, W2[48]); + F3(B, C, D, E, A, W2[49]); + F3(A, B, C, D, E, W2[50]); + F3(E, A, B, C, D, W2[51]); + F3(D, E, A, B, C, W2[52]); + F3(C, D, E, A, B, W2[53]); + F3(B, C, D, E, A, W2[54]); + F3(A, B, C, D, E, W2[55]); + F3(E, A, B, C, D, W2[56]); + F3(D, E, A, B, C, W2[57]); + F3(C, D, E, A, B, W2[58]); + F3(B, C, D, E, A, W2[59]); + F4(A, B, C, D, E, W2[60]); + F4(E, A, B, C, D, W2[61]); + F4(D, E, A, B, C, W2[62]); + F4(C, D, E, A, B, W2[63]); + F4(B, C, D, E, A, W2[64]); + F4(A, B, C, D, E, W2[65]); + F4(E, A, B, C, D, W2[66]); + F4(D, E, A, B, C, W2[67]); + F4(C, D, E, A, B, W2[68]); + F4(B, C, D, E, A, W2[69]); + F4(A, B, C, D, E, W2[70]); + F4(E, A, B, C, D, W2[71]); + F4(D, E, A, B, C, W2[72]); + F4(C, D, E, A, B, W2[73]); + F4(B, C, D, E, A, W2[74]); + F4(A, B, C, D, E, W2[75]); + F4(E, A, B, C, D, W2[76]); + F4(D, E, A, B, C, W2[77]); + F4(C, D, E, A, B, W2[78]); + F4(B, C, D, E, A, W2[79]); + + A = (digest[0] += A); + B = (digest[1] += B); + C = (digest[2] += C); + D = (digest[3] += D); + E = (digest[4] += E); + } + + for(size_t i = 0; i != blocks; ++i) { + uint32_t PT[8]; + + const auto block = in.take(block_bytes); + + SIMD_8x32 W0 = SIMD_8x32::load_be(&block[0]); // NOLINT(*-container-data-pointer) + SIMD_8x32 W2 = SIMD_8x32::load_be(&block[32]); + + SIMD_8x32 P0 = W0 + K11; + SIMD_8x32 P2 = W2 + K11; + + P0.store_le(PT); + P0 = sha1_avx2_next_w2(W0, W2) + K12; + + F1(A, B, C, D, E, PT[0]); + F1(E, A, B, C, D, PT[1]); + F1(D, E, A, B, C, PT[2]); + F1(C, D, E, A, B, PT[3]); + F1(B, C, D, E, A, PT[4]); + F1(A, B, C, D, E, PT[5]); + F1(E, A, B, C, D, PT[6]); + F1(D, E, A, B, C, PT[7]); + + P2.store_le(PT); + P2 = sha1_avx2_next_w2(W2, W0) + K22; + + F1(C, D, E, A, B, PT[0]); + F1(B, C, D, E, A, PT[1]); + F1(A, B, C, D, E, PT[2]); + F1(E, A, B, C, D, PT[3]); + F1(D, E, A, B, C, PT[4]); + F1(C, D, E, A, B, PT[5]); + F1(B, C, D, E, A, PT[6]); + F1(A, B, C, D, E, PT[7]); + + P0.store_le(PT); + P0 = sha1_avx2_next_w2(W0, W2) + K22; + + F1(E, A, B, C, D, PT[0]); + F1(D, E, A, B, C, PT[1]); + F1(C, D, E, A, B, PT[2]); + F1(B, C, D, E, A, PT[3]); + F2(A, B, C, D, E, PT[4]); + F2(E, A, B, C, D, PT[5]); + F2(D, E, A, B, C, PT[6]); + F2(C, D, E, A, B, PT[7]); + + P2.store_le(PT); + P2 = sha1_avx2_next_w2(W2, W0) + K33; + + F2(B, C, D, E, A, PT[0]); + F2(A, B, C, D, E, PT[1]); + F2(E, A, B, C, D, PT[2]); + F2(D, E, A, B, C, PT[3]); + F2(C, D, E, A, B, PT[4]); + F2(B, C, D, E, A, PT[5]); + F2(A, B, C, D, E, PT[6]); + F2(E, A, B, C, D, PT[7]); + + P0.store_le(PT); + P0 = sha1_avx2_next_w2(W0, W2) + K33; + + F2(D, E, A, B, C, PT[0]); + F2(C, D, E, A, B, PT[1]); + F2(B, C, D, E, A, PT[2]); + F2(A, B, C, D, E, PT[3]); + F2(E, A, B, C, D, PT[4]); + F2(D, E, A, B, C, PT[5]); + F2(C, D, E, A, B, PT[6]); + F2(B, C, D, E, A, PT[7]); + + P2.store_le(PT); + P2 = sha1_avx2_next_w2(W2, W0) + K34; + + F3(A, B, C, D, E, PT[0]); + F3(E, A, B, C, D, PT[1]); + F3(D, E, A, B, C, PT[2]); + F3(C, D, E, A, B, PT[3]); + F3(B, C, D, E, A, PT[4]); + F3(A, B, C, D, E, PT[5]); + F3(E, A, B, C, D, PT[6]); + F3(D, E, A, B, C, PT[7]); + + P0.store_le(PT); + P0 = sha1_avx2_next_w2(W0, W2) + K44; + + F3(C, D, E, A, B, PT[0]); + F3(B, C, D, E, A, PT[1]); + F3(A, B, C, D, E, PT[2]); + F3(E, A, B, C, D, PT[3]); + F3(D, E, A, B, C, PT[4]); + F3(C, D, E, A, B, PT[5]); + F3(B, C, D, E, A, PT[6]); + F3(A, B, C, D, E, PT[7]); + + P2.store_le(PT); + P2 = sha1_avx2_next_w2(W2, W0) + K44; + + F3(E, A, B, C, D, PT[0]); + F3(D, E, A, B, C, PT[1]); + F3(C, D, E, A, B, PT[2]); + F3(B, C, D, E, A, PT[3]); + F4(A, B, C, D, E, PT[4]); + F4(E, A, B, C, D, PT[5]); + F4(D, E, A, B, C, PT[6]); + F4(C, D, E, A, B, PT[7]); + + P0.store_le(PT); + + F4(B, C, D, E, A, PT[0]); + F4(A, B, C, D, E, PT[1]); + F4(E, A, B, C, D, PT[2]); + F4(D, E, A, B, C, PT[3]); + F4(C, D, E, A, B, PT[4]); + F4(B, C, D, E, A, PT[5]); + F4(A, B, C, D, E, PT[6]); + F4(E, A, B, C, D, PT[7]); + + P2.store_le(PT); + + F4(D, E, A, B, C, PT[0]); + F4(C, D, E, A, B, PT[1]); + F4(B, C, D, E, A, PT[2]); + F4(A, B, C, D, E, PT[3]); + F4(E, A, B, C, D, PT[4]); + F4(D, E, A, B, C, PT[5]); + F4(C, D, E, A, B, PT[6]); + F4(B, C, D, E, A, PT[7]); + + A = (digest[0] += A); + B = (digest[1] += B); + C = (digest[2] += C); + D = (digest[3] += D); + E = (digest[4] += E); + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1_f.h botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_f.h --- botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1_f.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_f.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,44 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SHA1_FN_H_ +#define BOTAN_SHA1_FN_H_ + +#include +#include +#include + +namespace Botan::SHA1_F { + +constexpr uint32_t K1 = 0x5A827999; +constexpr uint32_t K2 = 0x6ED9EBA1; +constexpr uint32_t K3 = 0x8F1BBCDC; +constexpr uint32_t K4 = 0xCA62C1D6; + +inline void F1(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t M) { + E += choose(B, C, D) + M + rotl<5>(A); + B = rotl<30>(B); +} + +inline void F2(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t M) { + E += (B ^ C ^ D) + M + rotl<5>(A); + B = rotl<30>(B); +} + +inline void F3(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t M) { + E += majority(B, C, D) + M + rotl<5>(A); + B = rotl<30>(B); +} + +// NOTE: identical to F4 besides the constant addition +inline void F4(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t M) { + E += (B ^ C ^ D) + M + rotl<5>(A); + B = rotl<30>(B); +} + +} // namespace Botan::SHA1_F + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1_simd/info.txt botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_simd/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1_simd/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_simd/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,37 @@ + +SHA1_SIMD_4X32 -> 20250331 + + + +name -> "SHA-1 SIMD" +brief -> "SHA-1 using SIMD instructions" + + + +x86_32:ssse3 +x86_64:ssse3 +x32:ssse3 +arm32:neon +arm64:neon +loongarch64:lsx +wasm:simd128 + +# AltiVec/VMX also does work, but at least on the machines tested (POWER8 and +# POWER10) this was slower than scalar, while for ARM and x86 speedups of +# 25-30% are typical. + + + +x86_32 +x86_64 +x32 +arm32 +arm64 +loongarch64 +wasm + + + +cpuid +simd_4x32 + diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1_simd/sha1_simd.cpp botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_simd/sha1_simd.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1_simd/sha1_simd.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_simd/sha1_simd.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,254 @@ +/* +* SHA-1 using SIMD instructions +* Based on public domain code by Dean Gaudet +* (http://arctic.org/~dean/crypto/sha1.html) +* (C) 2009-2011,2023,2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include + +namespace Botan { + +namespace { + +/* +For each multiple of 4, t, we want to calculate this: + +W[t+0] = rol(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1); +W[t+1] = rol(W[t-2] ^ W[t-7] ^ W[t-13] ^ W[t-15], 1); +W[t+2] = rol(W[t-1] ^ W[t-6] ^ W[t-12] ^ W[t-14], 1); +W[t+3] = rol(W[t] ^ W[t-5] ^ W[t-11] ^ W[t-13], 1); + +we'll actually calculate this: + +W[t+0] = rol(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1); +W[t+1] = rol(W[t-2] ^ W[t-7] ^ W[t-13] ^ W[t-15], 1); +W[t+2] = rol(W[t-1] ^ W[t-6] ^ W[t-12] ^ W[t-14], 1); +W[t+3] = rol( 0 ^ W[t-5] ^ W[t-11] ^ W[t-13], 1); +W[t+3] ^= rol(W[t+0], 1); + +the parameters are: + +W0 = &W[t-16]; +W1 = &W[t-12]; +W2 = &W[t- 8]; +W3 = &W[t- 4]; + +and on output: +W0 = W[t]..W[t+3] +*/ +BOTAN_FORCE_INLINE SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 sha1_simd_next_w(SIMD_4x32& XW0, + SIMD_4x32 XW1, + SIMD_4x32 XW2, + SIMD_4x32 XW3) { + SIMD_4x32 T0 = XW0; // W[t-16..t-13] + T0 ^= SIMD_4x32::alignr8(XW1, XW0); // W[t-14..t-11] + T0 ^= XW2; // W[t-8..t-5] + T0 ^= XW3.shift_elems_right<1>(); // W[t-3..t-1] || 0 + + /* unrotated W[t]..W[t+2] in T0 ... still need W[t+3] */ + + // Extract w[t+0] into T2 + auto T2 = T0.shift_elems_left<3>(); + + // Main rotation + T0 = T0.rotl<1>(); + + // Rotation of W[t+3] has rot by 2 to account for us working on non-rotated words + T2 = T2.rotl<2>(); + + // Merge rol(W[t+0], 1) into W[t+3] + T0 ^= T2; + + XW0 = T0; + return T0; +} + +} // namespace + +/* +* SHA-1 Compression Function using SIMD for message expansion +*/ +//static +void BOTAN_FN_ISA_SIMD_4X32 SHA_1::simd_compress_n(digest_type& digest, std::span input, size_t blocks) { + using namespace SHA1_F; + + const SIMD_4x32 K00_19 = SIMD_4x32::splat(K1); + const SIMD_4x32 K20_39 = SIMD_4x32::splat(K2); + const SIMD_4x32 K40_59 = SIMD_4x32::splat(K3); + const SIMD_4x32 K60_79 = SIMD_4x32::splat(K4); + + uint32_t A = digest[0]; + uint32_t B = digest[1]; + uint32_t C = digest[2]; + uint32_t D = digest[3]; + uint32_t E = digest[4]; + + BufferSlicer in(input); + + for(size_t i = 0; i != blocks; ++i) { + uint32_t PT[4]; + + const auto block = in.take(block_bytes); + + SIMD_4x32 W0 = SIMD_4x32::load_be(&block[0]); // NOLINT(*-container-data-pointer) + SIMD_4x32 W1 = SIMD_4x32::load_be(&block[16]); + SIMD_4x32 W2 = SIMD_4x32::load_be(&block[32]); + SIMD_4x32 W3 = SIMD_4x32::load_be(&block[48]); + + SIMD_4x32 P0 = W0 + K00_19; + SIMD_4x32 P1 = W1 + K00_19; + SIMD_4x32 P2 = W2 + K00_19; + SIMD_4x32 P3 = W3 + K00_19; + + P0.store_le(PT); + F1(A, B, C, D, E, PT[0]); + F1(E, A, B, C, D, PT[1]); + F1(D, E, A, B, C, PT[2]); + F1(C, D, E, A, B, PT[3]); + P0 = sha1_simd_next_w(W0, W1, W2, W3) + K00_19; + + P1.store_le(PT); + F1(B, C, D, E, A, PT[0]); + F1(A, B, C, D, E, PT[1]); + F1(E, A, B, C, D, PT[2]); + F1(D, E, A, B, C, PT[3]); + P1 = sha1_simd_next_w(W1, W2, W3, W0) + K20_39; + + P2.store_le(PT); + F1(C, D, E, A, B, PT[0]); + F1(B, C, D, E, A, PT[1]); + F1(A, B, C, D, E, PT[2]); + F1(E, A, B, C, D, PT[3]); + P2 = sha1_simd_next_w(W2, W3, W0, W1) + K20_39; + + P3.store_le(PT); + F1(D, E, A, B, C, PT[0]); + F1(C, D, E, A, B, PT[1]); + F1(B, C, D, E, A, PT[2]); + F1(A, B, C, D, E, PT[3]); + P3 = sha1_simd_next_w(W3, W0, W1, W2) + K20_39; + + P0.store_le(PT); + F1(E, A, B, C, D, PT[0]); + F1(D, E, A, B, C, PT[1]); + F1(C, D, E, A, B, PT[2]); + F1(B, C, D, E, A, PT[3]); + P0 = sha1_simd_next_w(W0, W1, W2, W3) + K20_39; + + P1.store_le(PT); + F2(A, B, C, D, E, PT[0]); + F2(E, A, B, C, D, PT[1]); + F2(D, E, A, B, C, PT[2]); + F2(C, D, E, A, B, PT[3]); + P1 = sha1_simd_next_w(W1, W2, W3, W0) + K20_39; + + P2.store_le(PT); + F2(B, C, D, E, A, PT[0]); + F2(A, B, C, D, E, PT[1]); + F2(E, A, B, C, D, PT[2]); + F2(D, E, A, B, C, PT[3]); + P2 = sha1_simd_next_w(W2, W3, W0, W1) + K40_59; + + P3.store_le(PT); + F2(C, D, E, A, B, PT[0]); + F2(B, C, D, E, A, PT[1]); + F2(A, B, C, D, E, PT[2]); + F2(E, A, B, C, D, PT[3]); + P3 = sha1_simd_next_w(W3, W0, W1, W2) + K40_59; + + P0.store_le(PT); + F2(D, E, A, B, C, PT[0]); + F2(C, D, E, A, B, PT[1]); + F2(B, C, D, E, A, PT[2]); + F2(A, B, C, D, E, PT[3]); + P0 = sha1_simd_next_w(W0, W1, W2, W3) + K40_59; + + P1.store_le(PT); + F2(E, A, B, C, D, PT[0]); + F2(D, E, A, B, C, PT[1]); + F2(C, D, E, A, B, PT[2]); + F2(B, C, D, E, A, PT[3]); + P1 = sha1_simd_next_w(W1, W2, W3, W0) + K40_59; + + P2.store_le(PT); + F3(A, B, C, D, E, PT[0]); + F3(E, A, B, C, D, PT[1]); + F3(D, E, A, B, C, PT[2]); + F3(C, D, E, A, B, PT[3]); + P2 = sha1_simd_next_w(W2, W3, W0, W1) + K40_59; + + P3.store_le(PT); + F3(B, C, D, E, A, PT[0]); + F3(A, B, C, D, E, PT[1]); + F3(E, A, B, C, D, PT[2]); + F3(D, E, A, B, C, PT[3]); + P3 = sha1_simd_next_w(W3, W0, W1, W2) + K60_79; + + P0.store_le(PT); + F3(C, D, E, A, B, PT[0]); + F3(B, C, D, E, A, PT[1]); + F3(A, B, C, D, E, PT[2]); + F3(E, A, B, C, D, PT[3]); + P0 = sha1_simd_next_w(W0, W1, W2, W3) + K60_79; + + P1.store_le(PT); + F3(D, E, A, B, C, PT[0]); + F3(C, D, E, A, B, PT[1]); + F3(B, C, D, E, A, PT[2]); + F3(A, B, C, D, E, PT[3]); + P1 = sha1_simd_next_w(W1, W2, W3, W0) + K60_79; + + P2.store_le(PT); + F3(E, A, B, C, D, PT[0]); + F3(D, E, A, B, C, PT[1]); + F3(C, D, E, A, B, PT[2]); + F3(B, C, D, E, A, PT[3]); + P2 = sha1_simd_next_w(W2, W3, W0, W1) + K60_79; + + P3.store_le(PT); + F4(A, B, C, D, E, PT[0]); + F4(E, A, B, C, D, PT[1]); + F4(D, E, A, B, C, PT[2]); + F4(C, D, E, A, B, PT[3]); + P3 = sha1_simd_next_w(W3, W0, W1, W2) + K60_79; + + P0.store_le(PT); + F4(B, C, D, E, A, PT[0]); + F4(A, B, C, D, E, PT[1]); + F4(E, A, B, C, D, PT[2]); + F4(D, E, A, B, C, PT[3]); + + P1.store_le(PT); + F4(C, D, E, A, B, PT[0]); + F4(B, C, D, E, A, PT[1]); + F4(A, B, C, D, E, PT[2]); + F4(E, A, B, C, D, PT[3]); + + P2.store_le(PT); + F4(D, E, A, B, C, PT[0]); + F4(C, D, E, A, B, PT[1]); + F4(B, C, D, E, A, PT[2]); + F4(A, B, C, D, E, PT[3]); + + P3.store_le(PT); + F4(E, A, B, C, D, PT[0]); + F4(D, E, A, B, C, PT[1]); + F4(C, D, E, A, B, PT[2]); + F4(B, C, D, E, A, PT[3]); + + A = (digest[0] += A); + B = (digest[1] += B); + C = (digest[2] += C); + D = (digest[3] += D); + E = (digest[4] += E); + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1_sse2/info.txt botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_sse2/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1_sse2/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_sse2/info.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ - -SHA1_SSE2 -> 20160803 - - - -name -> "SHA-1 SSE2" -brief -> "SHA-1 using SSE2 instructions" - - - -sse2 - - - -simd - diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1_sse2/sha1_sse2.cpp botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_sse2/sha1_sse2.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1_sse2/sha1_sse2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_sse2/sha1_sse2.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,286 +0,0 @@ -/* -* SHA-1 using SSE2 -* Based on public domain code by Dean Gaudet -* (http://arctic.org/~dean/crypto/sha1.html) -* (C) 2009-2011,2023 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include -#include -#include -#include - -namespace Botan { - -namespace SHA1_SSE2_F { - -namespace { - -/* -For each multiple of 4, t, we want to calculate this: - -W[t+0] = rol(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1); -W[t+1] = rol(W[t-2] ^ W[t-7] ^ W[t-13] ^ W[t-15], 1); -W[t+2] = rol(W[t-1] ^ W[t-6] ^ W[t-12] ^ W[t-14], 1); -W[t+3] = rol(W[t] ^ W[t-5] ^ W[t-11] ^ W[t-13], 1); - -we'll actually calculate this: - -W[t+0] = rol(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1); -W[t+1] = rol(W[t-2] ^ W[t-7] ^ W[t-13] ^ W[t-15], 1); -W[t+2] = rol(W[t-1] ^ W[t-6] ^ W[t-12] ^ W[t-14], 1); -W[t+3] = rol( 0 ^ W[t-5] ^ W[t-11] ^ W[t-13], 1); -W[t+3] ^= rol(W[t+0], 1); - -the parameters are: - -W0 = &W[t-16]; -W1 = &W[t-12]; -W2 = &W[t- 8]; -W3 = &W[t- 4]; - -and on output: -prepared = W0 + K -W0 = W[t]..W[t+3] -*/ -BOTAN_FORCE_INLINE SIMD_4x32 prep(SIMD_4x32& XW0, SIMD_4x32 XW1, SIMD_4x32 XW2, SIMD_4x32 XW3, SIMD_4x32 K) { - SIMD_4x32 T0 = XW0; - /* load W[t-4] 16-byte aligned, and shift */ - SIMD_4x32 T2 = XW3.shift_elems_right<1>(); - /* get high 64-bits of XW0 into low 64-bits */ - SIMD_4x32 T1 = SIMD_4x32(_mm_shuffle_epi32(XW0.raw(), _MM_SHUFFLE(1, 0, 3, 2))); - /* load high 64-bits of T1 */ - T1 = SIMD_4x32(_mm_unpacklo_epi64(T1.raw(), XW1.raw())); - - T0 ^= T1; - T2 ^= XW2; - T0 ^= T2; - /* unrotated W[t]..W[t+2] in T0 ... still need W[t+3] */ - - T2 = T0.shift_elems_left<3>(); - T0 = T0.rotl<1>(); - T2 = T2.rotl<2>(); - - T0 ^= T2; /* T0 now has W[t+3] */ - - XW0 = T0; - return T0 + K; -} - -/* -* SHA-1 F1 Function -*/ -inline void F1(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t msg) { - E += choose(B, C, D) + msg + rotl<5>(A); - B = rotl<30>(B); -} - -/* -* SHA-1 F2 Function -*/ -inline void F2(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t msg) { - E += (B ^ C ^ D) + msg + rotl<5>(A); - B = rotl<30>(B); -} - -/* -* SHA-1 F3 Function -*/ -inline void F3(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t msg) { - E += majority(B, C, D) + msg + rotl<5>(A); - B = rotl<30>(B); -} - -/* -* SHA-1 F4 Function -*/ -inline void F4(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t msg) { - E += (B ^ C ^ D) + msg + rotl<5>(A); - B = rotl<30>(B); -} - -} // namespace - -} // namespace SHA1_SSE2_F - -/* -* SHA-1 Compression Function using SSE for message expansion -*/ -//static -BOTAN_FUNC_ISA("sse2") void SHA_1::sse2_compress_n(digest_type& digest, std::span input, size_t blocks) { - using namespace SHA1_SSE2_F; - - const SIMD_4x32 K00_19 = SIMD_4x32::splat(0x5A827999); - const SIMD_4x32 K20_39 = SIMD_4x32::splat(0x6ED9EBA1); - const SIMD_4x32 K40_59 = SIMD_4x32::splat(0x8F1BBCDC); - const SIMD_4x32 K60_79 = SIMD_4x32::splat(0xCA62C1D6); - - uint32_t A = digest[0], B = digest[1], C = digest[2], D = digest[3], E = digest[4]; - - BufferSlicer in(input); - - for(size_t i = 0; i != blocks; ++i) { - uint32_t PT[4]; - - const auto block = in.take(block_bytes); - - SIMD_4x32 W0 = SIMD_4x32::load_be(&block[0]); - SIMD_4x32 W1 = SIMD_4x32::load_be(&block[16]); - SIMD_4x32 W2 = SIMD_4x32::load_be(&block[32]); - SIMD_4x32 W3 = SIMD_4x32::load_be(&block[48]); - - SIMD_4x32 P0 = W0 + K00_19; - SIMD_4x32 P1 = W1 + K00_19; - SIMD_4x32 P2 = W2 + K00_19; - SIMD_4x32 P3 = W3 + K00_19; - - SIMD_4x32(P0).store_le(PT); - F1(A, B, C, D, E, PT[0]); - F1(E, A, B, C, D, PT[1]); - F1(D, E, A, B, C, PT[2]); - F1(C, D, E, A, B, PT[3]); - P0 = prep(W0, W1, W2, W3, K00_19); - - SIMD_4x32(P1).store_le(PT); - F1(B, C, D, E, A, PT[0]); - F1(A, B, C, D, E, PT[1]); - F1(E, A, B, C, D, PT[2]); - F1(D, E, A, B, C, PT[3]); - P1 = prep(W1, W2, W3, W0, K20_39); - - SIMD_4x32(P2).store_le(PT); - F1(C, D, E, A, B, PT[0]); - F1(B, C, D, E, A, PT[1]); - F1(A, B, C, D, E, PT[2]); - F1(E, A, B, C, D, PT[3]); - P2 = prep(W2, W3, W0, W1, K20_39); - - SIMD_4x32(P3).store_le(PT); - F1(D, E, A, B, C, PT[0]); - F1(C, D, E, A, B, PT[1]); - F1(B, C, D, E, A, PT[2]); - F1(A, B, C, D, E, PT[3]); - P3 = prep(W3, W0, W1, W2, K20_39); - - SIMD_4x32(P0).store_le(PT); - F1(E, A, B, C, D, PT[0]); - F1(D, E, A, B, C, PT[1]); - F1(C, D, E, A, B, PT[2]); - F1(B, C, D, E, A, PT[3]); - P0 = prep(W0, W1, W2, W3, K20_39); - - SIMD_4x32(P1).store_le(PT); - F2(A, B, C, D, E, PT[0]); - F2(E, A, B, C, D, PT[1]); - F2(D, E, A, B, C, PT[2]); - F2(C, D, E, A, B, PT[3]); - P1 = prep(W1, W2, W3, W0, K20_39); - - SIMD_4x32(P2).store_le(PT); - F2(B, C, D, E, A, PT[0]); - F2(A, B, C, D, E, PT[1]); - F2(E, A, B, C, D, PT[2]); - F2(D, E, A, B, C, PT[3]); - P2 = prep(W2, W3, W0, W1, K40_59); - - SIMD_4x32(P3).store_le(PT); - F2(C, D, E, A, B, PT[0]); - F2(B, C, D, E, A, PT[1]); - F2(A, B, C, D, E, PT[2]); - F2(E, A, B, C, D, PT[3]); - P3 = prep(W3, W0, W1, W2, K40_59); - - SIMD_4x32(P0).store_le(PT); - F2(D, E, A, B, C, PT[0]); - F2(C, D, E, A, B, PT[1]); - F2(B, C, D, E, A, PT[2]); - F2(A, B, C, D, E, PT[3]); - P0 = prep(W0, W1, W2, W3, K40_59); - - SIMD_4x32(P1).store_le(PT); - F2(E, A, B, C, D, PT[0]); - F2(D, E, A, B, C, PT[1]); - F2(C, D, E, A, B, PT[2]); - F2(B, C, D, E, A, PT[3]); - P1 = prep(W1, W2, W3, W0, K40_59); - - SIMD_4x32(P2).store_le(PT); - F3(A, B, C, D, E, PT[0]); - F3(E, A, B, C, D, PT[1]); - F3(D, E, A, B, C, PT[2]); - F3(C, D, E, A, B, PT[3]); - P2 = prep(W2, W3, W0, W1, K40_59); - - SIMD_4x32(P3).store_le(PT); - F3(B, C, D, E, A, PT[0]); - F3(A, B, C, D, E, PT[1]); - F3(E, A, B, C, D, PT[2]); - F3(D, E, A, B, C, PT[3]); - P3 = prep(W3, W0, W1, W2, K60_79); - - SIMD_4x32(P0).store_le(PT); - F3(C, D, E, A, B, PT[0]); - F3(B, C, D, E, A, PT[1]); - F3(A, B, C, D, E, PT[2]); - F3(E, A, B, C, D, PT[3]); - P0 = prep(W0, W1, W2, W3, K60_79); - - SIMD_4x32(P1).store_le(PT); - F3(D, E, A, B, C, PT[0]); - F3(C, D, E, A, B, PT[1]); - F3(B, C, D, E, A, PT[2]); - F3(A, B, C, D, E, PT[3]); - P1 = prep(W1, W2, W3, W0, K60_79); - - SIMD_4x32(P2).store_le(PT); - F3(E, A, B, C, D, PT[0]); - F3(D, E, A, B, C, PT[1]); - F3(C, D, E, A, B, PT[2]); - F3(B, C, D, E, A, PT[3]); - P2 = prep(W2, W3, W0, W1, K60_79); - - SIMD_4x32(P3).store_le(PT); - F4(A, B, C, D, E, PT[0]); - F4(E, A, B, C, D, PT[1]); - F4(D, E, A, B, C, PT[2]); - F4(C, D, E, A, B, PT[3]); - P3 = prep(W3, W0, W1, W2, K60_79); - - SIMD_4x32(P0).store_le(PT); - F4(B, C, D, E, A, PT[0]); - F4(A, B, C, D, E, PT[1]); - F4(E, A, B, C, D, PT[2]); - F4(D, E, A, B, C, PT[3]); - - SIMD_4x32(P1).store_le(PT); - F4(C, D, E, A, B, PT[0]); - F4(B, C, D, E, A, PT[1]); - F4(A, B, C, D, E, PT[2]); - F4(E, A, B, C, D, PT[3]); - - SIMD_4x32(P2).store_le(PT); - F4(D, E, A, B, C, PT[0]); - F4(C, D, E, A, B, PT[1]); - F4(B, C, D, E, A, PT[2]); - F4(A, B, C, D, E, PT[3]); - - SIMD_4x32(P3).store_le(PT); - F4(E, A, B, C, D, PT[0]); - F4(D, E, A, B, C, PT[1]); - F4(C, D, E, A, B, PT[2]); - F4(B, C, D, E, A, PT[3]); - - A = (digest[0] += A); - B = (digest[1] += B); - C = (digest[2] += C); - D = (digest[3] += D); - E = (digest[4] += E); - } -} - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1_x86/info.txt botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_x86/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1_x86/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_x86/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + SHA1_X86_SHA_NI -> 20170518 - + name -> "SHA-1 SIMD" @@ -13,3 +13,8 @@ ssse3 sse41 + + +cpuid +simd_4x32 + diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1_x86/sha1_x86.cpp botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_x86/sha1_x86.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sha1/sha1_x86/sha1_x86.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha1/sha1_x86/sha1_x86.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,210 +1,142 @@ /* -* SHA-1 using Intel SHA intrinsic -* * Based on public domain code by Sean Gulley -* (https://github.com/mitls/hacl-star/tree/master/experimental/hash) +* * Adapted to Botan by Jeffrey Walton. * * Further changes * -* (C) 2017 Jack Lloyd +* (C) 2017,2025 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include + +#include +#include #include namespace Botan { -BOTAN_FUNC_ISA("sha,ssse3,sse4.1") -void SHA_1::sha1_compress_x86(digest_type& digest, std::span input, size_t blocks) { - const __m128i MASK = _mm_set_epi64x(0x0001020304050607, 0x08090a0b0c0d0e0f); - const __m128i* input_mm = reinterpret_cast(input.data()); - - uint32_t* state = digest.data(); - - // Load initial values - __m128i ABCD = _mm_loadu_si128(reinterpret_cast<__m128i*>(state)); - __m128i E0 = _mm_set_epi32(state[4], 0, 0, 0); - ABCD = _mm_shuffle_epi32(ABCD, 0x1B); +namespace { + +// NOLINTBEGIN(portability-simd-intrinsics) + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_SHANI SIMD_4x32 sha1_x86_nexte(const SIMD_4x32& x, const SIMD_4x32& y) { + return SIMD_4x32(_mm_sha1nexte_epu32(x.raw(), y.raw())); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_SHANI SIMD_4x32 sha1_x86_msg1(const SIMD_4x32& W0, const SIMD_4x32& W1) { + return SIMD_4x32(_mm_sha1msg1_epu32(W0.raw(), W1.raw())); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_SHANI void sha1_x86_next_msg(const SIMD_4x32& W0, + SIMD_4x32& W1, + SIMD_4x32& W2, + SIMD_4x32& W3) { + W3 = SIMD_4x32(_mm_sha1msg1_epu32(W3.raw(), W0.raw())); + W1 = SIMD_4x32(_mm_sha1msg2_epu32(W1.raw(), W0.raw())); + W2 ^= W0; +} + +template +BOTAN_FORCE_INLINE BOTAN_FN_ISA_SHANI void sha1_x86_first8(SIMD_4x32& ABCD, + SIMD_4x32& E, + const SIMD_4x32& W0, + const SIMD_4x32& W1) { + auto TE = ABCD; + ABCD = SIMD_4x32(_mm_sha1rnds4_epu32(ABCD.raw(), (E + W0).raw(), R1)); + + E = ABCD; + ABCD = SIMD_4x32(_mm_sha1rnds4_epu32(ABCD.raw(), sha1_x86_nexte(TE, W1).raw(), R2)); +} + +template +BOTAN_FORCE_INLINE BOTAN_FN_ISA_SHANI void sha1_x86_rnds8(SIMD_4x32& ABCD, + SIMD_4x32& E, + const SIMD_4x32& W0, + const SIMD_4x32& W1) { + auto TE = ABCD; + ABCD = SIMD_4x32(_mm_sha1rnds4_epu32(ABCD.raw(), sha1_x86_nexte(E, W0).raw(), R1)); + + E = ABCD; + ABCD = SIMD_4x32(_mm_sha1rnds4_epu32(ABCD.raw(), sha1_x86_nexte(TE, W1).raw(), R2)); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_SHANI SIMD_4x32 rev_words(const SIMD_4x32& v) { + return SIMD_4x32(_mm_shuffle_epi32(v.raw(), 0b00011011)); +} + +// NOLINTEND(portability-simd-intrinsics) + +} // namespace - while(blocks) { +void BOTAN_FN_ISA_SHANI SHA_1::sha1_compress_x86(digest_type& digest, + std::span input_span, + size_t blocks) { + const uint8_t* input = input_span.data(); + + SIMD_4x32 ABCD = rev_words(SIMD_4x32::load_le(&digest[0])); // NOLINT(*-container-data-pointer) + SIMD_4x32 E0 = SIMD_4x32(0, 0, 0, digest[4]); + + while(blocks > 0) { // Save current hash - const __m128i ABCD_SAVE = ABCD; - const __m128i E0_SAVE = E0; + const auto ABCD_SAVE = ABCD; + const auto E0_SAVE = E0; + + auto W0 = rev_words(SIMD_4x32::load_be(input)); + auto W1 = rev_words(SIMD_4x32::load_be(input + 16)); + auto W2 = rev_words(SIMD_4x32::load_be(input + 32)); + auto W3 = rev_words(SIMD_4x32::load_be(input + 48)); + + sha1_x86_first8<0>(ABCD, E0, W0, W1); + sha1_x86_rnds8<0>(ABCD, E0, W2, W3); + + W0 = sha1_x86_msg1(W0, W1); + W1 = sha1_x86_msg1(W1, W2); + W0 ^= W2; + + sha1_x86_next_msg(W3, W0, W1, W2); + sha1_x86_next_msg(W0, W1, W2, W3); + sha1_x86_rnds8<0, 1>(ABCD, E0, W0, W1); + + sha1_x86_next_msg(W1, W2, W3, W0); + sha1_x86_next_msg(W2, W3, W0, W1); + sha1_x86_rnds8<1>(ABCD, E0, W2, W3); + + sha1_x86_next_msg(W3, W0, W1, W2); + sha1_x86_next_msg(W0, W1, W2, W3); + sha1_x86_rnds8<1>(ABCD, E0, W0, W1); + + sha1_x86_next_msg(W1, W2, W3, W0); + sha1_x86_next_msg(W2, W3, W0, W1); + sha1_x86_rnds8<2>(ABCD, E0, W2, W3); + + sha1_x86_next_msg(W3, W0, W1, W2); + sha1_x86_next_msg(W0, W1, W2, W3); + sha1_x86_rnds8<2>(ABCD, E0, W0, W1); + + sha1_x86_next_msg(W1, W2, W3, W0); + sha1_x86_next_msg(W2, W3, W0, W1); + sha1_x86_rnds8<2, 3>(ABCD, E0, W2, W3); + + sha1_x86_next_msg(W3, W0, W1, W2); + sha1_x86_next_msg(W0, W1, W2, W3); + sha1_x86_rnds8<3>(ABCD, E0, W0, W1); - __m128i MSG0, MSG1, MSG2, MSG3; - __m128i E1; + sha1_x86_next_msg(W1, W2, W3, W0); + sha1_x86_next_msg(W2, W3, W0, W1); + sha1_x86_rnds8<3>(ABCD, E0, W2, W3); - // Rounds 0-3 - MSG0 = _mm_loadu_si128(input_mm + 0); - MSG0 = _mm_shuffle_epi8(MSG0, MASK); - E0 = _mm_add_epi32(E0, MSG0); - E1 = ABCD; - ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 0); - - // Rounds 4-7 - MSG1 = _mm_loadu_si128(input_mm + 1); - MSG1 = _mm_shuffle_epi8(MSG1, MASK); - E1 = _mm_sha1nexte_epu32(E1, MSG1); - E0 = ABCD; - ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 0); - MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1); - - // Rounds 8-11 - MSG2 = _mm_loadu_si128(input_mm + 2); - MSG2 = _mm_shuffle_epi8(MSG2, MASK); - E0 = _mm_sha1nexte_epu32(E0, MSG2); - E1 = ABCD; - ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 0); - MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2); - MSG0 = _mm_xor_si128(MSG0, MSG2); - - // Rounds 12-15 - MSG3 = _mm_loadu_si128(input_mm + 3); - MSG3 = _mm_shuffle_epi8(MSG3, MASK); - E1 = _mm_sha1nexte_epu32(E1, MSG3); - E0 = ABCD; - MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3); - ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 0); - MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3); - MSG1 = _mm_xor_si128(MSG1, MSG3); - - // Rounds 16-19 - E0 = _mm_sha1nexte_epu32(E0, MSG0); - E1 = ABCD; - MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0); - ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 0); - MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0); - MSG2 = _mm_xor_si128(MSG2, MSG0); - - // Rounds 20-23 - E1 = _mm_sha1nexte_epu32(E1, MSG1); - E0 = ABCD; - MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1); - ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 1); - MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1); - MSG3 = _mm_xor_si128(MSG3, MSG1); - - // Rounds 24-27 - E0 = _mm_sha1nexte_epu32(E0, MSG2); - E1 = ABCD; - MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2); - ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 1); - MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2); - MSG0 = _mm_xor_si128(MSG0, MSG2); - - // Rounds 28-31 - E1 = _mm_sha1nexte_epu32(E1, MSG3); - E0 = ABCD; - MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3); - ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 1); - MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3); - MSG1 = _mm_xor_si128(MSG1, MSG3); - - // Rounds 32-35 - E0 = _mm_sha1nexte_epu32(E0, MSG0); - E1 = ABCD; - MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0); - ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 1); - MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0); - MSG2 = _mm_xor_si128(MSG2, MSG0); - - // Rounds 36-39 - E1 = _mm_sha1nexte_epu32(E1, MSG1); - E0 = ABCD; - MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1); - ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 1); - MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1); - MSG3 = _mm_xor_si128(MSG3, MSG1); - - // Rounds 40-43 - E0 = _mm_sha1nexte_epu32(E0, MSG2); - E1 = ABCD; - MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2); - ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 2); - MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2); - MSG0 = _mm_xor_si128(MSG0, MSG2); - - // Rounds 44-47 - E1 = _mm_sha1nexte_epu32(E1, MSG3); - E0 = ABCD; - MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3); - ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 2); - MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3); - MSG1 = _mm_xor_si128(MSG1, MSG3); - - // Rounds 48-51 - E0 = _mm_sha1nexte_epu32(E0, MSG0); - E1 = ABCD; - MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0); - ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 2); - MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0); - MSG2 = _mm_xor_si128(MSG2, MSG0); - - // Rounds 52-55 - E1 = _mm_sha1nexte_epu32(E1, MSG1); - E0 = ABCD; - MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1); - ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 2); - MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1); - MSG3 = _mm_xor_si128(MSG3, MSG1); - - // Rounds 56-59 - E0 = _mm_sha1nexte_epu32(E0, MSG2); - E1 = ABCD; - MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2); - ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 2); - MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2); - MSG0 = _mm_xor_si128(MSG0, MSG2); - - // Rounds 60-63 - E1 = _mm_sha1nexte_epu32(E1, MSG3); - E0 = ABCD; - MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3); - ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 3); - MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3); - MSG1 = _mm_xor_si128(MSG1, MSG3); - - // Rounds 64-67 - E0 = _mm_sha1nexte_epu32(E0, MSG0); - E1 = ABCD; - MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0); - ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 3); - MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0); - MSG2 = _mm_xor_si128(MSG2, MSG0); - - // Rounds 68-71 - E1 = _mm_sha1nexte_epu32(E1, MSG1); - E0 = ABCD; - MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1); - ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 3); - MSG3 = _mm_xor_si128(MSG3, MSG1); - - // Rounds 72-75 - E0 = _mm_sha1nexte_epu32(E0, MSG2); - E1 = ABCD; - MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2); - ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 3); - - // Rounds 76-79 - E1 = _mm_sha1nexte_epu32(E1, MSG3); - E0 = ABCD; - ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 3); - - // Add values back to state - E0 = _mm_sha1nexte_epu32(E0, E0_SAVE); - ABCD = _mm_add_epi32(ABCD, ABCD_SAVE); + ABCD += ABCD_SAVE; + E0 = sha1_x86_nexte(E0, E0_SAVE); - input_mm += 4; + input += 64; blocks--; } - // Save state - ABCD = _mm_shuffle_epi32(ABCD, 0x1B); - _mm_storeu_si128(reinterpret_cast<__m128i*>(state), ABCD); - state[4] = _mm_extract_epi32(E0, 3); + rev_words(ABCD).store_le(&digest[0]); // NOLINT(*-container-data-pointer) + digest[4] = _mm_extract_epi32(E0.raw(), 3); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_32/info.txt botan3-3.12.0+dfsg/src/lib/hash/sha2_32/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/sha2_32/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_32/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,7 +1,9 @@ -SHA2_32 -> 20131128 SHA_224 -> 20250130 SHA_256 -> 20250130 + +# TODO(Botan4) remove this macro +SHA2_32 -> 20131128 diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32.cpp botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,33 +8,41 @@ #include -#include -#include +#include #include -#include #include -#include +#include + +#if defined(BOTAN_HAS_CPUID) + #include +#endif namespace Botan { namespace { std::string sha256_provider() { +#if defined(BOTAN_HAS_SHA2_32_ARMV8) + if(auto feat = CPUID::check(CPUID::Feature::SHA2)) { + return *feat; + } +#endif + #if defined(BOTAN_HAS_SHA2_32_X86) - if(CPUID::has_intel_sha()) { - return "shani"; + if(auto feat = CPUID::check(CPUID::Feature::SHA)) { + return *feat; } #endif -#if defined(BOTAN_HAS_SHA2_32_X86_BMI2) - if(CPUID::has_bmi2()) { - return "bmi2"; +#if defined(BOTAN_HAS_SHA2_32_X86_AVX2) + if(auto feat = CPUID::check(CPUID::Feature::AVX2, CPUID::Feature::BMI)) { + return *feat; } #endif -#if defined(BOTAN_HAS_SHA2_32_ARMV8) - if(CPUID::has_arm_sha2()) { - return "armv8"; +#if defined(BOTAN_HAS_SHA2_32_SIMD) + if(auto feat = CPUID::check(CPUID::Feature::SIMD_4X32)) { + return *feat; } #endif @@ -46,29 +54,43 @@ /* * SHA-224 / SHA-256 compression function */ -void SHA_256::compress_digest(digest_type& digest, std::span input, size_t blocks) { +void BOTAN_SCRUB_STACK_AFTER_RETURN SHA_256::compress_digest(digest_type& digest, + std::span input, + size_t blocks) { #if defined(BOTAN_HAS_SHA2_32_X86) - if(CPUID::has_intel_sha()) { + if(CPUID::has(CPUID::Feature::SHA)) { return SHA_256::compress_digest_x86(digest, input, blocks); } #endif -#if defined(BOTAN_HAS_SHA2_32_X86_BMI2) - if(CPUID::has_bmi2()) { - return SHA_256::compress_digest_x86_bmi2(digest, input, blocks); +#if defined(BOTAN_HAS_SHA2_32_ARMV8) + if(CPUID::has(CPUID::Feature::SHA2)) { + return SHA_256::compress_digest_armv8(digest, input, blocks); } #endif -#if defined(BOTAN_HAS_SHA2_32_ARMV8) - if(CPUID::has_arm_sha2()) { - return SHA_256::compress_digest_armv8(digest, input, blocks); +#if defined(BOTAN_HAS_SHA2_32_X86_AVX2) + if(CPUID::has(CPUID::Feature::AVX2, CPUID::Feature::BMI)) { + return SHA_256::compress_digest_x86_avx2(digest, input, blocks); + } +#endif + +#if defined(BOTAN_HAS_SHA2_32_SIMD) + if(CPUID::has(CPUID::Feature::SIMD_4X32)) { + return SHA_256::compress_digest_x86_simd(digest, input, blocks); } #endif - uint32_t A = digest[0], B = digest[1], C = digest[2], D = digest[3], E = digest[4], F = digest[5], G = digest[6], - H = digest[7]; + uint32_t A = digest[0]; + uint32_t B = digest[1]; + uint32_t C = digest[2]; + uint32_t D = digest[3]; + uint32_t E = digest[4]; + uint32_t F = digest[5]; + uint32_t G = digest[6]; + uint32_t H = digest[7]; - std::array W; + std::array W{}; BufferSlicer in(input); diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32.h botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32.h --- botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32.h 2026-05-07 01:38:28.000000000 +0000 @@ -91,8 +91,12 @@ static void compress_digest_armv8(digest_type& digest, std::span input, size_t blocks); #endif -#if defined(BOTAN_HAS_SHA2_32_X86_BMI2) - static void compress_digest_x86_bmi2(digest_type& digest, std::span input, size_t blocks); +#if defined(BOTAN_HAS_SHA2_32_SIMD) + static void compress_digest_x86_simd(digest_type& digest, std::span input, size_t blocks); +#endif + +#if defined(BOTAN_HAS_SHA2_32_X86_AVX2) + static void compress_digest_x86_avx2(digest_type& digest, std::span input, size_t blocks); #endif #if defined(BOTAN_HAS_SHA2_32_X86) diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32_armv8/info.txt botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_armv8/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32_armv8/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_armv8/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + SHA2_32_ARMV8 -> 20170117 - + name -> "SHA-256 ARMv8" @@ -10,3 +10,8 @@ armv8crypto + + +cpuid +simd_4x32 + diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32_armv8/sha2_32_armv8.cpp botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_armv8/sha2_32_armv8.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32_armv8/sha2_32_armv8.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_armv8/sha2_32_armv8.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -4,22 +4,48 @@ * Contributed by Jeffrey Walton. Based on public domain code by * Johannes Schneiders, Skip Hovsmith and Barry O'Rourke. * -* Further changes (C) 2020 Jack Lloyd +* Further changes (C) 2020,2025 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include + +#include +#include +#include #include namespace Botan { +namespace { + +inline BOTAN_FN_ISA_SHA2 SIMD_4x32 aarch64_sha256_expand_w(const SIMD_4x32 w0, + const SIMD_4x32 w1, + const SIMD_4x32 w2, + const SIMD_4x32 w3) { + return SIMD_4x32(vsha256su1q_u32(vsha256su0q_u32(w0.raw(), w1.raw()), w2.raw(), w3.raw())); +} + +inline BOTAN_FN_ISA_SHA2 void aarch64_sha256_update(SIMD_4x32& s0, + SIMD_4x32& s1, + const SIMD_4x32 w, + const uint32_t K[4]) { + auto w_k = w + SIMD_4x32::load_le(K); + auto t = vsha256hq_u32(s0.raw(), s1.raw(), w_k.raw()); + s1 = SIMD_4x32(vsha256h2q_u32(s1.raw(), s0.raw(), w_k.raw())); + s0 = SIMD_4x32(t); +} + +} // namespace + /* * SHA-256 using CPU instructions in ARMv8 */ //static -BOTAN_FUNC_ISA("+crypto+sha2") -void SHA_256::compress_digest_armv8(digest_type& digest, std::span input8, size_t blocks) { +void BOTAN_FN_ISA_SHA2 BOTAN_SCRUB_STACK_AFTER_RETURN SHA_256::compress_digest_armv8(digest_type& digest, + std::span input8, + size_t blocks) { alignas(64) static const uint32_t K[] = { 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, @@ -32,148 +58,48 @@ }; // Load initial values - uint32x4_t STATE0 = vld1q_u32(&digest[0]); - uint32x4_t STATE1 = vld1q_u32(&digest[4]); + SIMD_4x32 s0 = SIMD_4x32::load_le(&digest[0]); // NOLINT(*-container-data-pointer) + SIMD_4x32 s1 = SIMD_4x32::load_le(&digest[4]); - // Intermediate void* cast due to https://llvm.org/bugs/show_bug.cgi?id=20670 - const uint32_t* input32 = reinterpret_cast(reinterpret_cast(input8.data())); + const uint32_t* input32 = reinterpret_cast(input8.data()); while(blocks > 0) { - // Save current state - const uint32x4_t ABCD_SAVE = STATE0; - const uint32x4_t EFGH_SAVE = STATE1; - - uint32x4_t MSG0 = vld1q_u32(input32 + 0); - uint32x4_t MSG1 = vld1q_u32(input32 + 4); - uint32x4_t MSG2 = vld1q_u32(input32 + 8); - uint32x4_t MSG3 = vld1q_u32(input32 + 12); - - MSG0 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG0))); - MSG1 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG1))); - MSG2 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG2))); - MSG3 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG3))); - - uint32x4_t MSG_K, TSTATE; - - // Rounds 0-3 - MSG_K = vaddq_u32(MSG0, vld1q_u32(&K[4 * 0])); - TSTATE = vsha256hq_u32(STATE0, STATE1, MSG_K); - STATE1 = vsha256h2q_u32(STATE1, STATE0, MSG_K); - STATE0 = TSTATE; - MSG0 = vsha256su1q_u32(vsha256su0q_u32(MSG0, MSG1), MSG2, MSG3); - - // Rounds 4-7 - MSG_K = vaddq_u32(MSG1, vld1q_u32(&K[4 * 1])); - TSTATE = vsha256hq_u32(STATE0, STATE1, MSG_K); - STATE1 = vsha256h2q_u32(STATE1, STATE0, MSG_K); - STATE0 = TSTATE; - MSG1 = vsha256su1q_u32(vsha256su0q_u32(MSG1, MSG2), MSG3, MSG0); - - // Rounds 8-11 - MSG_K = vaddq_u32(MSG2, vld1q_u32(&K[4 * 2])); - TSTATE = vsha256hq_u32(STATE0, STATE1, MSG_K); - STATE1 = vsha256h2q_u32(STATE1, STATE0, MSG_K); - STATE0 = TSTATE; - MSG2 = vsha256su1q_u32(vsha256su0q_u32(MSG2, MSG3), MSG0, MSG1); - - // Rounds 12-15 - MSG_K = vaddq_u32(MSG3, vld1q_u32(&K[4 * 3])); - TSTATE = vsha256hq_u32(STATE0, STATE1, MSG_K); - STATE1 = vsha256h2q_u32(STATE1, STATE0, MSG_K); - STATE0 = TSTATE; - MSG3 = vsha256su1q_u32(vsha256su0q_u32(MSG3, MSG0), MSG1, MSG2); - - // Rounds 16-19 - MSG_K = vaddq_u32(MSG0, vld1q_u32(&K[4 * 4])); - TSTATE = vsha256hq_u32(STATE0, STATE1, MSG_K); - STATE1 = vsha256h2q_u32(STATE1, STATE0, MSG_K); - STATE0 = TSTATE; - MSG0 = vsha256su1q_u32(vsha256su0q_u32(MSG0, MSG1), MSG2, MSG3); - - // Rounds 20-23 - MSG_K = vaddq_u32(MSG1, vld1q_u32(&K[4 * 5])); - TSTATE = vsha256hq_u32(STATE0, STATE1, MSG_K); - STATE1 = vsha256h2q_u32(STATE1, STATE0, MSG_K); - STATE0 = TSTATE; - MSG1 = vsha256su1q_u32(vsha256su0q_u32(MSG1, MSG2), MSG3, MSG0); - - // Rounds 24-27 - MSG_K = vaddq_u32(MSG2, vld1q_u32(&K[4 * 6])); - TSTATE = vsha256hq_u32(STATE0, STATE1, MSG_K); - STATE1 = vsha256h2q_u32(STATE1, STATE0, MSG_K); - STATE0 = TSTATE; - MSG2 = vsha256su1q_u32(vsha256su0q_u32(MSG2, MSG3), MSG0, MSG1); - - // Rounds 28-31 - MSG_K = vaddq_u32(MSG3, vld1q_u32(&K[4 * 7])); - TSTATE = vsha256hq_u32(STATE0, STATE1, MSG_K); - STATE1 = vsha256h2q_u32(STATE1, STATE0, MSG_K); - STATE0 = TSTATE; - MSG3 = vsha256su1q_u32(vsha256su0q_u32(MSG3, MSG0), MSG1, MSG2); - - // Rounds 32-35 - MSG_K = vaddq_u32(MSG0, vld1q_u32(&K[4 * 8])); - TSTATE = vsha256hq_u32(STATE0, STATE1, MSG_K); - STATE1 = vsha256h2q_u32(STATE1, STATE0, MSG_K); - STATE0 = TSTATE; - MSG0 = vsha256su1q_u32(vsha256su0q_u32(MSG0, MSG1), MSG2, MSG3); - - // Rounds 36-39 - MSG_K = vaddq_u32(MSG1, vld1q_u32(&K[4 * 9])); - TSTATE = vsha256hq_u32(STATE0, STATE1, MSG_K); - STATE1 = vsha256h2q_u32(STATE1, STATE0, MSG_K); - STATE0 = TSTATE; - MSG1 = vsha256su1q_u32(vsha256su0q_u32(MSG1, MSG2), MSG3, MSG0); - - // Rounds 40-43 - MSG_K = vaddq_u32(MSG2, vld1q_u32(&K[4 * 10])); - TSTATE = vsha256hq_u32(STATE0, STATE1, MSG_K); - STATE1 = vsha256h2q_u32(STATE1, STATE0, MSG_K); - STATE0 = TSTATE; - MSG2 = vsha256su1q_u32(vsha256su0q_u32(MSG2, MSG3), MSG0, MSG1); - - // Rounds 44-47 - MSG_K = vaddq_u32(MSG3, vld1q_u32(&K[4 * 11])); - TSTATE = vsha256hq_u32(STATE0, STATE1, MSG_K); - STATE1 = vsha256h2q_u32(STATE1, STATE0, MSG_K); - STATE0 = TSTATE; - MSG3 = vsha256su1q_u32(vsha256su0q_u32(MSG3, MSG0), MSG1, MSG2); - - // Rounds 48-51 - MSG_K = vaddq_u32(MSG0, vld1q_u32(&K[4 * 12])); - TSTATE = vsha256hq_u32(STATE0, STATE1, MSG_K); - STATE1 = vsha256h2q_u32(STATE1, STATE0, MSG_K); - STATE0 = TSTATE; - - // Rounds 52-55 - MSG_K = vaddq_u32(MSG1, vld1q_u32(&K[4 * 13])); - TSTATE = vsha256hq_u32(STATE0, STATE1, MSG_K); - STATE1 = vsha256h2q_u32(STATE1, STATE0, MSG_K); - STATE0 = TSTATE; - - // Rounds 56-59 - MSG_K = vaddq_u32(MSG2, vld1q_u32(&K[4 * 14])); - TSTATE = vsha256hq_u32(STATE0, STATE1, MSG_K); - STATE1 = vsha256h2q_u32(STATE1, STATE0, MSG_K); - STATE0 = TSTATE; - - // Rounds 60-63 - MSG_K = vaddq_u32(MSG3, vld1q_u32(&K[4 * 15])); - TSTATE = vsha256hq_u32(STATE0, STATE1, MSG_K); - STATE1 = vsha256h2q_u32(STATE1, STATE0, MSG_K); - STATE0 = TSTATE; - - // Add back to state - STATE0 = vaddq_u32(STATE0, ABCD_SAVE); - STATE1 = vaddq_u32(STATE1, EFGH_SAVE); + const auto s0_save = s0; + const auto s1_save = s1; + + auto w0 = SIMD_4x32::load_be(input32); + auto w1 = SIMD_4x32::load_be(input32 + 4); + auto w2 = SIMD_4x32::load_be(input32 + 8); + auto w3 = SIMD_4x32::load_be(input32 + 12); + + for(size_t r = 0; r != 48; r += 16) { + aarch64_sha256_update(s0, s1, w0, &K[r]); + w0 = aarch64_sha256_expand_w(w0, w1, w2, w3); + + aarch64_sha256_update(s0, s1, w1, &K[r + 4 * 1]); + w1 = aarch64_sha256_expand_w(w1, w2, w3, w0); + + aarch64_sha256_update(s0, s1, w2, &K[r + 4 * 2]); + w2 = aarch64_sha256_expand_w(w2, w3, w0, w1); + + aarch64_sha256_update(s0, s1, w3, &K[r + 4 * 3]); + w3 = aarch64_sha256_expand_w(w3, w0, w1, w2); + } + + aarch64_sha256_update(s0, s1, w0, &K[4 * 12]); + aarch64_sha256_update(s0, s1, w1, &K[4 * 13]); + aarch64_sha256_update(s0, s1, w2, &K[4 * 14]); + aarch64_sha256_update(s0, s1, w3, &K[4 * 15]); + + s0 += s0_save; + s1 += s1_save; input32 += 64 / 4; blocks--; } - // Save state - vst1q_u32(&digest[0], STATE0); - vst1q_u32(&digest[4], STATE1); + s0.store_le(&digest[0]); // NOLINT(*-container-data-pointer) + s1.store_le(&digest[4]); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32_avx2/info.txt botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_avx2/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32_avx2/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_avx2/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,19 @@ + +SHA2_32_X86_AVX2 -> 20250402 + + + +name -> "SHA-256 using AVX2/BMI2" +brief -> "SHA-256 using AVX2/BMI2 instructions" + + + +avx2 +bmi2 + + + +cpuid +simd_4x32 +simd_avx2 + diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32_avx2/sha2_32_avx2.cpp botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_avx2/sha2_32_avx2.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32_avx2/sha2_32_avx2.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_avx2/sha2_32_avx2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,362 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include + +#include + +namespace Botan { + +namespace { + +BOTAN_FN_ISA_AVX2_BMI2 inline SIMD_4x32 alignr4(const SIMD_4x32& a, const SIMD_4x32& b) { + return SIMD_4x32(_mm_alignr_epi8(a.raw(), b.raw(), 4)); +} + +template +BOTAN_FN_ISA_AVX2_BMI2 inline SIMD_4x32 shr64(const SIMD_4x32& a) { + return SIMD_4x32(_mm_srli_epi64(a.raw(), S)); +} + +template +BOTAN_FN_ISA_AVX2_BMI2 inline SIMD_4x32 shuffle_32(const SIMD_4x32& a) { + return SIMD_4x32(_mm_shuffle_epi32(a.raw(), S)); +} + +BOTAN_FN_ISA_AVX2_BMI2 inline SIMD_8x32 alignr4(const SIMD_8x32& a, const SIMD_8x32& b) { + return SIMD_8x32(_mm256_alignr_epi8(a.raw(), b.raw(), 4)); +} + +template +BOTAN_FN_ISA_AVX2_BMI2 inline SIMD_8x32 shr64(const SIMD_8x32& a) { + return SIMD_8x32(_mm256_srli_epi64(a.raw(), S)); +} + +template +BOTAN_FN_ISA_AVX2_BMI2 inline SIMD_8x32 shuffle_32(const SIMD_8x32& a) { + return SIMD_8x32(_mm256_shuffle_epi32(a.raw(), S)); +} + +template +BOTAN_FN_ISA_AVX2_BMI2 BOTAN_FORCE_INLINE SIMD_T next_w(SIMD_T x[4]) { + constexpr size_t sigma0_0 = 7; + constexpr size_t sigma0_1 = 18; + constexpr size_t sigma0_2 = 3; + constexpr size_t sigma1_0 = 17; + constexpr size_t sigma1_1 = 19; + constexpr size_t sigma1_2 = 10; + + const SIMD_T lo_mask = SIMD_T(0x03020100, 0x0b0a0908, 0x80808080, 0x80808080); + const SIMD_T hi_mask = SIMD_T(0x80808080, 0x80808080, 0x03020100, 0x0b0a0908); + + auto t0 = alignr4(x[1], x[0]); + x[0] += alignr4(x[3], x[2]); + + auto t1 = t0.template shl<32 - sigma0_1>(); + auto t2 = t0.template shr(); + auto t3 = t0.template shr(); + t0 = t3 ^ t2; + + t3 = shuffle_32<0b11111010>(x[3]); + t2 = t2.template shr(); + t0 ^= t1 ^ t2; + t1 = t1.template shl(); + t2 = t3.template shr(); + t3 = shr64(t3); + x[0] += t0 ^ t1; + + t2 ^= t3; + t3 = shr64(t3); + x[0] += SIMD_T::byte_shuffle(t2 ^ t3, lo_mask); + + t3 = shuffle_32<0b01010000>(x[0]); + t2 = t3.template shr(); + t3 = shr64(t3); + t2 ^= t3; + t3 = shr64(t3); + x[0] += SIMD_T::byte_shuffle(t2 ^ t3, hi_mask); + + const auto tmp = x[0]; + x[0] = x[1]; + x[1] = x[2]; + x[2] = x[3]; + x[3] = tmp; + + return x[3]; +} + +} // namespace + +BOTAN_FN_ISA_AVX2_BMI2 BOTAN_SCRUB_STACK_AFTER_RETURN void SHA_256::compress_digest_x86_avx2( + digest_type& digest, std::span input, size_t blocks) { + // clang-format off + + alignas(64) const uint32_t K[64] = { + 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, + 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, + 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, + 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, + 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, + 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, + 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, + 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2}; + + // clang-format on + + alignas(64) uint32_t W[16]; + alignas(64) uint32_t W2[64]; + + uint32_t A = digest[0]; + uint32_t B = digest[1]; + uint32_t C = digest[2]; + uint32_t D = digest[3]; + uint32_t E = digest[4]; + uint32_t F = digest[5]; + uint32_t G = digest[6]; + uint32_t H = digest[7]; + + const uint8_t* data = input.data(); + + while(blocks >= 2) { + SIMD_8x32 WS[4]; + + for(size_t i = 0; i < 4; i++) { + WS[i] = SIMD_8x32::load_be128(&data[16 * i], &data[64 + 16 * i]); + auto WK = WS[i] + SIMD_8x32::load_le128(&K[4 * i]); + WK.store_le128(&W[4 * i], &W2[4 * i]); + } + + data += 2 * 64; + blocks -= 2; + + for(size_t r = 0; r != 48; r += 16) { + auto w = next_w(WS) + SIMD_8x32::load_le128(&K[r + 16]); + + SHA2_32_F(A, B, C, D, E, F, G, H, W[0]); + SHA2_32_F(H, A, B, C, D, E, F, G, W[1]); + SHA2_32_F(G, H, A, B, C, D, E, F, W[2]); + SHA2_32_F(F, G, H, A, B, C, D, E, W[3]); + + w.store_le128(&W[0], &W2[r + 16]); + + w = next_w(WS) + SIMD_8x32::load_le128(&K[r + 20]); + + SHA2_32_F(E, F, G, H, A, B, C, D, W[4]); + SHA2_32_F(D, E, F, G, H, A, B, C, W[5]); + SHA2_32_F(C, D, E, F, G, H, A, B, W[6]); + SHA2_32_F(B, C, D, E, F, G, H, A, W[7]); + + w.store_le128(&W[4], &W2[r + 20]); + + w = next_w(WS) + SIMD_8x32::load_le128(&K[r + 24]); + + SHA2_32_F(A, B, C, D, E, F, G, H, W[8]); + SHA2_32_F(H, A, B, C, D, E, F, G, W[9]); + SHA2_32_F(G, H, A, B, C, D, E, F, W[10]); + SHA2_32_F(F, G, H, A, B, C, D, E, W[11]); + + w.store_le128(&W[8], &W2[r + 24]); + + w = next_w(WS) + SIMD_8x32::load_le128(&K[r + 28]); + + SHA2_32_F(E, F, G, H, A, B, C, D, W[12]); + SHA2_32_F(D, E, F, G, H, A, B, C, W[13]); + SHA2_32_F(C, D, E, F, G, H, A, B, W[14]); + SHA2_32_F(B, C, D, E, F, G, H, A, W[15]); + + w.store_le128(&W[12], &W2[r + 28]); + } + + SHA2_32_F(A, B, C, D, E, F, G, H, W[0]); + SHA2_32_F(H, A, B, C, D, E, F, G, W[1]); + SHA2_32_F(G, H, A, B, C, D, E, F, W[2]); + SHA2_32_F(F, G, H, A, B, C, D, E, W[3]); + SHA2_32_F(E, F, G, H, A, B, C, D, W[4]); + SHA2_32_F(D, E, F, G, H, A, B, C, W[5]); + SHA2_32_F(C, D, E, F, G, H, A, B, W[6]); + SHA2_32_F(B, C, D, E, F, G, H, A, W[7]); + SHA2_32_F(A, B, C, D, E, F, G, H, W[8]); + SHA2_32_F(H, A, B, C, D, E, F, G, W[9]); + SHA2_32_F(G, H, A, B, C, D, E, F, W[10]); + SHA2_32_F(F, G, H, A, B, C, D, E, W[11]); + SHA2_32_F(E, F, G, H, A, B, C, D, W[12]); + SHA2_32_F(D, E, F, G, H, A, B, C, W[13]); + SHA2_32_F(C, D, E, F, G, H, A, B, W[14]); + SHA2_32_F(B, C, D, E, F, G, H, A, W[15]); + + A = (digest[0] += A); + B = (digest[1] += B); + C = (digest[2] += C); + D = (digest[3] += D); + E = (digest[4] += E); + F = (digest[5] += F); + G = (digest[6] += G); + H = (digest[7] += H); + + // Now the second block, with already expanded message + SHA2_32_F(A, B, C, D, E, F, G, H, W2[0]); + SHA2_32_F(H, A, B, C, D, E, F, G, W2[1]); + SHA2_32_F(G, H, A, B, C, D, E, F, W2[2]); + SHA2_32_F(F, G, H, A, B, C, D, E, W2[3]); + SHA2_32_F(E, F, G, H, A, B, C, D, W2[4]); + SHA2_32_F(D, E, F, G, H, A, B, C, W2[5]); + SHA2_32_F(C, D, E, F, G, H, A, B, W2[6]); + SHA2_32_F(B, C, D, E, F, G, H, A, W2[7]); + SHA2_32_F(A, B, C, D, E, F, G, H, W2[8]); + SHA2_32_F(H, A, B, C, D, E, F, G, W2[9]); + SHA2_32_F(G, H, A, B, C, D, E, F, W2[10]); + SHA2_32_F(F, G, H, A, B, C, D, E, W2[11]); + SHA2_32_F(E, F, G, H, A, B, C, D, W2[12]); + SHA2_32_F(D, E, F, G, H, A, B, C, W2[13]); + SHA2_32_F(C, D, E, F, G, H, A, B, W2[14]); + SHA2_32_F(B, C, D, E, F, G, H, A, W2[15]); + + SHA2_32_F(A, B, C, D, E, F, G, H, W2[16]); + SHA2_32_F(H, A, B, C, D, E, F, G, W2[17]); + SHA2_32_F(G, H, A, B, C, D, E, F, W2[18]); + SHA2_32_F(F, G, H, A, B, C, D, E, W2[19]); + SHA2_32_F(E, F, G, H, A, B, C, D, W2[20]); + SHA2_32_F(D, E, F, G, H, A, B, C, W2[21]); + SHA2_32_F(C, D, E, F, G, H, A, B, W2[22]); + SHA2_32_F(B, C, D, E, F, G, H, A, W2[23]); + SHA2_32_F(A, B, C, D, E, F, G, H, W2[24]); + SHA2_32_F(H, A, B, C, D, E, F, G, W2[25]); + SHA2_32_F(G, H, A, B, C, D, E, F, W2[26]); + SHA2_32_F(F, G, H, A, B, C, D, E, W2[27]); + SHA2_32_F(E, F, G, H, A, B, C, D, W2[28]); + SHA2_32_F(D, E, F, G, H, A, B, C, W2[29]); + SHA2_32_F(C, D, E, F, G, H, A, B, W2[30]); + SHA2_32_F(B, C, D, E, F, G, H, A, W2[31]); + + SHA2_32_F(A, B, C, D, E, F, G, H, W2[32]); + SHA2_32_F(H, A, B, C, D, E, F, G, W2[33]); + SHA2_32_F(G, H, A, B, C, D, E, F, W2[34]); + SHA2_32_F(F, G, H, A, B, C, D, E, W2[35]); + SHA2_32_F(E, F, G, H, A, B, C, D, W2[36]); + SHA2_32_F(D, E, F, G, H, A, B, C, W2[37]); + SHA2_32_F(C, D, E, F, G, H, A, B, W2[38]); + SHA2_32_F(B, C, D, E, F, G, H, A, W2[39]); + SHA2_32_F(A, B, C, D, E, F, G, H, W2[40]); + SHA2_32_F(H, A, B, C, D, E, F, G, W2[41]); + SHA2_32_F(G, H, A, B, C, D, E, F, W2[42]); + SHA2_32_F(F, G, H, A, B, C, D, E, W2[43]); + SHA2_32_F(E, F, G, H, A, B, C, D, W2[44]); + SHA2_32_F(D, E, F, G, H, A, B, C, W2[45]); + SHA2_32_F(C, D, E, F, G, H, A, B, W2[46]); + SHA2_32_F(B, C, D, E, F, G, H, A, W2[47]); + + SHA2_32_F(A, B, C, D, E, F, G, H, W2[48]); + SHA2_32_F(H, A, B, C, D, E, F, G, W2[49]); + SHA2_32_F(G, H, A, B, C, D, E, F, W2[50]); + SHA2_32_F(F, G, H, A, B, C, D, E, W2[51]); + SHA2_32_F(E, F, G, H, A, B, C, D, W2[52]); + SHA2_32_F(D, E, F, G, H, A, B, C, W2[53]); + SHA2_32_F(C, D, E, F, G, H, A, B, W2[54]); + SHA2_32_F(B, C, D, E, F, G, H, A, W2[55]); + SHA2_32_F(A, B, C, D, E, F, G, H, W2[56]); + SHA2_32_F(H, A, B, C, D, E, F, G, W2[57]); + SHA2_32_F(G, H, A, B, C, D, E, F, W2[58]); + SHA2_32_F(F, G, H, A, B, C, D, E, W2[59]); + SHA2_32_F(E, F, G, H, A, B, C, D, W2[60]); + SHA2_32_F(D, E, F, G, H, A, B, C, W2[61]); + SHA2_32_F(C, D, E, F, G, H, A, B, W2[62]); + SHA2_32_F(B, C, D, E, F, G, H, A, W2[63]); + + A = (digest[0] += A); + B = (digest[1] += B); + C = (digest[2] += C); + D = (digest[3] += D); + E = (digest[4] += E); + F = (digest[5] += F); + G = (digest[6] += G); + H = (digest[7] += H); + } + + while(blocks > 0) { + SIMD_4x32 WS[4]; + + for(size_t i = 0; i < 4; i++) { + WS[i] = SIMD_4x32::load_be(&data[16 * i]); + auto WK = WS[i] + SIMD_4x32::load_le(&K[4 * i]); + WK.store_le(&W[4 * i]); + } + + data += 64; + blocks -= 1; + + for(size_t r = 0; r != 48; r += 16) { + auto w = next_w(WS) + SIMD_4x32::load_le(&K[r + 16]); + + SHA2_32_F(A, B, C, D, E, F, G, H, W[0]); + SHA2_32_F(H, A, B, C, D, E, F, G, W[1]); + SHA2_32_F(G, H, A, B, C, D, E, F, W[2]); + SHA2_32_F(F, G, H, A, B, C, D, E, W[3]); + + w.store_le(&W[0]); + + w = next_w(WS) + SIMD_4x32::load_le(&K[r + 20]); + + SHA2_32_F(E, F, G, H, A, B, C, D, W[4]); + SHA2_32_F(D, E, F, G, H, A, B, C, W[5]); + SHA2_32_F(C, D, E, F, G, H, A, B, W[6]); + SHA2_32_F(B, C, D, E, F, G, H, A, W[7]); + + w.store_le(&W[4]); + + w = next_w(WS) + SIMD_4x32::load_le(&K[r + 24]); + + SHA2_32_F(A, B, C, D, E, F, G, H, W[8]); + SHA2_32_F(H, A, B, C, D, E, F, G, W[9]); + SHA2_32_F(G, H, A, B, C, D, E, F, W[10]); + SHA2_32_F(F, G, H, A, B, C, D, E, W[11]); + + w.store_le(&W[8]); + + w = next_w(WS) + SIMD_4x32::load_le(&K[r + 28]); + + SHA2_32_F(E, F, G, H, A, B, C, D, W[12]); + SHA2_32_F(D, E, F, G, H, A, B, C, W[13]); + SHA2_32_F(C, D, E, F, G, H, A, B, W[14]); + SHA2_32_F(B, C, D, E, F, G, H, A, W[15]); + + w.store_le(&W[12]); + } + + SHA2_32_F(A, B, C, D, E, F, G, H, W[0]); + SHA2_32_F(H, A, B, C, D, E, F, G, W[1]); + SHA2_32_F(G, H, A, B, C, D, E, F, W[2]); + SHA2_32_F(F, G, H, A, B, C, D, E, W[3]); + SHA2_32_F(E, F, G, H, A, B, C, D, W[4]); + SHA2_32_F(D, E, F, G, H, A, B, C, W[5]); + SHA2_32_F(C, D, E, F, G, H, A, B, W[6]); + SHA2_32_F(B, C, D, E, F, G, H, A, W[7]); + SHA2_32_F(A, B, C, D, E, F, G, H, W[8]); + SHA2_32_F(H, A, B, C, D, E, F, G, W[9]); + SHA2_32_F(G, H, A, B, C, D, E, F, W[10]); + SHA2_32_F(F, G, H, A, B, C, D, E, W[11]); + SHA2_32_F(E, F, G, H, A, B, C, D, W[12]); + SHA2_32_F(D, E, F, G, H, A, B, C, W[13]); + SHA2_32_F(C, D, E, F, G, H, A, B, W[14]); + SHA2_32_F(B, C, D, E, F, G, H, A, W[15]); + + A = (digest[0] += A); + B = (digest[1] += B); + C = (digest[2] += C); + D = (digest[3] += D); + E = (digest[4] += E); + F = (digest[5] += F); + G = (digest[6] += G); + H = (digest[7] += H); + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32_bmi2/info.txt botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_bmi2/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32_bmi2/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_bmi2/info.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ - -SHA2_32_X86_BMI2 -> 20180526 - - - -name -> "SHA-256 BMI2" -brief -> "SHA-256 using BMI2 instructions" - - - -bmi2 - diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32_bmi2/sha2_32_bmi2.cpp botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_bmi2/sha2_32_bmi2.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32_bmi2/sha2_32_bmi2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_bmi2/sha2_32_bmi2.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,118 +0,0 @@ -/* -* (C) 2018 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include -#include -#include -#include - -namespace Botan { - -/* -Your eyes do not decieve you; this is currently just a copy of the -baseline SHA-256 implementation. Because we compile it with BMI2 -flags, GCC and Clang use the BMI2 instructions without further help. - -Likely instruction scheduling could be improved by using inline asm. -*/ -void SHA_256::compress_digest_x86_bmi2(digest_type& digest, std::span input, size_t blocks) { - uint32_t A = digest[0], B = digest[1], C = digest[2], D = digest[3], E = digest[4], F = digest[5], G = digest[6], - H = digest[7]; - - std::array W; - - BufferSlicer in(input); - - for(size_t i = 0; i != blocks; ++i) { - load_be(W, in.take()); - - // clang-format off - - SHA2_32_F(A, B, C, D, E, F, G, H, W[ 0], W[14], W[ 9], W[ 1], 0x428A2F98); - SHA2_32_F(H, A, B, C, D, E, F, G, W[ 1], W[15], W[10], W[ 2], 0x71374491); - SHA2_32_F(G, H, A, B, C, D, E, F, W[ 2], W[ 0], W[11], W[ 3], 0xB5C0FBCF); - SHA2_32_F(F, G, H, A, B, C, D, E, W[ 3], W[ 1], W[12], W[ 4], 0xE9B5DBA5); - SHA2_32_F(E, F, G, H, A, B, C, D, W[ 4], W[ 2], W[13], W[ 5], 0x3956C25B); - SHA2_32_F(D, E, F, G, H, A, B, C, W[ 5], W[ 3], W[14], W[ 6], 0x59F111F1); - SHA2_32_F(C, D, E, F, G, H, A, B, W[ 6], W[ 4], W[15], W[ 7], 0x923F82A4); - SHA2_32_F(B, C, D, E, F, G, H, A, W[ 7], W[ 5], W[ 0], W[ 8], 0xAB1C5ED5); - SHA2_32_F(A, B, C, D, E, F, G, H, W[ 8], W[ 6], W[ 1], W[ 9], 0xD807AA98); - SHA2_32_F(H, A, B, C, D, E, F, G, W[ 9], W[ 7], W[ 2], W[10], 0x12835B01); - SHA2_32_F(G, H, A, B, C, D, E, F, W[10], W[ 8], W[ 3], W[11], 0x243185BE); - SHA2_32_F(F, G, H, A, B, C, D, E, W[11], W[ 9], W[ 4], W[12], 0x550C7DC3); - SHA2_32_F(E, F, G, H, A, B, C, D, W[12], W[10], W[ 5], W[13], 0x72BE5D74); - SHA2_32_F(D, E, F, G, H, A, B, C, W[13], W[11], W[ 6], W[14], 0x80DEB1FE); - SHA2_32_F(C, D, E, F, G, H, A, B, W[14], W[12], W[ 7], W[15], 0x9BDC06A7); - SHA2_32_F(B, C, D, E, F, G, H, A, W[15], W[13], W[ 8], W[ 0], 0xC19BF174); - - SHA2_32_F(A, B, C, D, E, F, G, H, W[ 0], W[14], W[ 9], W[ 1], 0xE49B69C1); - SHA2_32_F(H, A, B, C, D, E, F, G, W[ 1], W[15], W[10], W[ 2], 0xEFBE4786); - SHA2_32_F(G, H, A, B, C, D, E, F, W[ 2], W[ 0], W[11], W[ 3], 0x0FC19DC6); - SHA2_32_F(F, G, H, A, B, C, D, E, W[ 3], W[ 1], W[12], W[ 4], 0x240CA1CC); - SHA2_32_F(E, F, G, H, A, B, C, D, W[ 4], W[ 2], W[13], W[ 5], 0x2DE92C6F); - SHA2_32_F(D, E, F, G, H, A, B, C, W[ 5], W[ 3], W[14], W[ 6], 0x4A7484AA); - SHA2_32_F(C, D, E, F, G, H, A, B, W[ 6], W[ 4], W[15], W[ 7], 0x5CB0A9DC); - SHA2_32_F(B, C, D, E, F, G, H, A, W[ 7], W[ 5], W[ 0], W[ 8], 0x76F988DA); - SHA2_32_F(A, B, C, D, E, F, G, H, W[ 8], W[ 6], W[ 1], W[ 9], 0x983E5152); - SHA2_32_F(H, A, B, C, D, E, F, G, W[ 9], W[ 7], W[ 2], W[10], 0xA831C66D); - SHA2_32_F(G, H, A, B, C, D, E, F, W[10], W[ 8], W[ 3], W[11], 0xB00327C8); - SHA2_32_F(F, G, H, A, B, C, D, E, W[11], W[ 9], W[ 4], W[12], 0xBF597FC7); - SHA2_32_F(E, F, G, H, A, B, C, D, W[12], W[10], W[ 5], W[13], 0xC6E00BF3); - SHA2_32_F(D, E, F, G, H, A, B, C, W[13], W[11], W[ 6], W[14], 0xD5A79147); - SHA2_32_F(C, D, E, F, G, H, A, B, W[14], W[12], W[ 7], W[15], 0x06CA6351); - SHA2_32_F(B, C, D, E, F, G, H, A, W[15], W[13], W[ 8], W[ 0], 0x14292967); - - SHA2_32_F(A, B, C, D, E, F, G, H, W[ 0], W[14], W[ 9], W[ 1], 0x27B70A85); - SHA2_32_F(H, A, B, C, D, E, F, G, W[ 1], W[15], W[10], W[ 2], 0x2E1B2138); - SHA2_32_F(G, H, A, B, C, D, E, F, W[ 2], W[ 0], W[11], W[ 3], 0x4D2C6DFC); - SHA2_32_F(F, G, H, A, B, C, D, E, W[ 3], W[ 1], W[12], W[ 4], 0x53380D13); - SHA2_32_F(E, F, G, H, A, B, C, D, W[ 4], W[ 2], W[13], W[ 5], 0x650A7354); - SHA2_32_F(D, E, F, G, H, A, B, C, W[ 5], W[ 3], W[14], W[ 6], 0x766A0ABB); - SHA2_32_F(C, D, E, F, G, H, A, B, W[ 6], W[ 4], W[15], W[ 7], 0x81C2C92E); - SHA2_32_F(B, C, D, E, F, G, H, A, W[ 7], W[ 5], W[ 0], W[ 8], 0x92722C85); - SHA2_32_F(A, B, C, D, E, F, G, H, W[ 8], W[ 6], W[ 1], W[ 9], 0xA2BFE8A1); - SHA2_32_F(H, A, B, C, D, E, F, G, W[ 9], W[ 7], W[ 2], W[10], 0xA81A664B); - SHA2_32_F(G, H, A, B, C, D, E, F, W[10], W[ 8], W[ 3], W[11], 0xC24B8B70); - SHA2_32_F(F, G, H, A, B, C, D, E, W[11], W[ 9], W[ 4], W[12], 0xC76C51A3); - SHA2_32_F(E, F, G, H, A, B, C, D, W[12], W[10], W[ 5], W[13], 0xD192E819); - SHA2_32_F(D, E, F, G, H, A, B, C, W[13], W[11], W[ 6], W[14], 0xD6990624); - SHA2_32_F(C, D, E, F, G, H, A, B, W[14], W[12], W[ 7], W[15], 0xF40E3585); - SHA2_32_F(B, C, D, E, F, G, H, A, W[15], W[13], W[ 8], W[ 0], 0x106AA070); - - SHA2_32_F(A, B, C, D, E, F, G, H, W[ 0], W[14], W[ 9], W[ 1], 0x19A4C116); - SHA2_32_F(H, A, B, C, D, E, F, G, W[ 1], W[15], W[10], W[ 2], 0x1E376C08); - SHA2_32_F(G, H, A, B, C, D, E, F, W[ 2], W[ 0], W[11], W[ 3], 0x2748774C); - SHA2_32_F(F, G, H, A, B, C, D, E, W[ 3], W[ 1], W[12], W[ 4], 0x34B0BCB5); - SHA2_32_F(E, F, G, H, A, B, C, D, W[ 4], W[ 2], W[13], W[ 5], 0x391C0CB3); - SHA2_32_F(D, E, F, G, H, A, B, C, W[ 5], W[ 3], W[14], W[ 6], 0x4ED8AA4A); - SHA2_32_F(C, D, E, F, G, H, A, B, W[ 6], W[ 4], W[15], W[ 7], 0x5B9CCA4F); - SHA2_32_F(B, C, D, E, F, G, H, A, W[ 7], W[ 5], W[ 0], W[ 8], 0x682E6FF3); - SHA2_32_F(A, B, C, D, E, F, G, H, W[ 8], W[ 6], W[ 1], W[ 9], 0x748F82EE); - SHA2_32_F(H, A, B, C, D, E, F, G, W[ 9], W[ 7], W[ 2], W[10], 0x78A5636F); - SHA2_32_F(G, H, A, B, C, D, E, F, W[10], W[ 8], W[ 3], W[11], 0x84C87814); - SHA2_32_F(F, G, H, A, B, C, D, E, W[11], W[ 9], W[ 4], W[12], 0x8CC70208); - SHA2_32_F(E, F, G, H, A, B, C, D, W[12], W[10], W[ 5], W[13], 0x90BEFFFA); - SHA2_32_F(D, E, F, G, H, A, B, C, W[13], W[11], W[ 6], W[14], 0xA4506CEB); - SHA2_32_F(C, D, E, F, G, H, A, B, W[14], W[12], W[ 7], W[15], 0xBEF9A3F7); - SHA2_32_F(B, C, D, E, F, G, H, A, W[15], W[13], W[ 8], W[ 0], 0xC67178F2); - - // clang-format on - - A = (digest[0] += A); - B = (digest[1] += B); - C = (digest[2] += C); - D = (digest[3] += D); - E = (digest[4] += E); - F = (digest[5] += F); - G = (digest[6] += G); - H = (digest[7] += H); - } -} - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32_f.h botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_f.h --- botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32_f.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_f.h 2026-05-07 01:38:28.000000000 +0000 @@ -29,14 +29,20 @@ uint32_t M3, uint32_t M4, uint32_t magic) { - uint32_t A_rho = rho<2, 13, 22>(A); - uint32_t E_rho = rho<6, 11, 25>(E); - uint32_t M2_sigma = sigma<17, 19, 10>(M2); - uint32_t M4_sigma = sigma<7, 18, 3>(M4); - H += magic + E_rho + choose(E, F, G) + M1; + H += magic + rho<6, 11, 25>(E) + choose(E, F, G) + M1; D += H; - H += A_rho + majority(A, B, C); - M1 += M2_sigma + M3 + M4_sigma; + H += rho<2, 13, 22>(A) + majority(A, B, C); + M1 += sigma<17, 19, 10>(M2) + M3 + sigma<7, 18, 3>(M4); +} + +/* +* SHA-256 F1 Function (No Message Expansion) +*/ +BOTAN_FORCE_INLINE void SHA2_32_F( + uint32_t A, uint32_t B, uint32_t C, uint32_t& D, uint32_t E, uint32_t F, uint32_t G, uint32_t& H, uint32_t M) { + H += rho<6, 11, 25>(E) + choose(E, F, G) + M; + D += H; + H += rho<2, 13, 22>(A) + majority(A, B, C); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32_simd/info.txt botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_simd/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32_simd/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_simd/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,51 @@ + +SHA2_32_SIMD -> 20250402 + + + +name -> "SHA-256 using SIMD" +brief -> "SHA-256 using SIMD instructions" + + + +x86_64:ssse3 +x86_32:ssse3 +x32:ssse3 + +arm32:neon +arm64:neon + +wasm:simd128 + + + +x86_64 +x86_32 +x32 + +arm32 +arm64 + +# Works for VMX and LSX but not faster on systems tested so far +#ppc64 +#loongson64 + +wasm + + + +x86_32:ssse3 +x86_64:ssse3 +x32:ssse3 +arm32:neon +arm64:neon +ppc32:altivec +ppc64:altivec +loongarch64:lsx +wasm:simd128 + + + +cpuid +simd_4x32 + diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32_simd/sha2_32_simd.cpp botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_simd/sha2_32_simd.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32_simd/sha2_32_simd.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_simd/sha2_32_simd.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,155 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include + +namespace Botan { + +namespace { + +BOTAN_FN_ISA_SIMD_4X32 BOTAN_FORCE_INLINE SIMD_4x32 sha256_simd_next_w(SIMD_4x32 x[4]) { + const SIMD_4x32 lo_mask = SIMD_4x32(0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000); + const SIMD_4x32 hi_mask = SIMD_4x32(0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF); + + const SIMD_4x32 lo_word_shuf = SIMD_4x32(0x03020100, 0x07060504, 0x03020100, 0x07060504); + const SIMD_4x32 hi_word_shuf = SIMD_4x32(0x0B0A0908, 0x0F0E0D0C, 0x0B0A0908, 0x0F0E0D0C); + + auto t0 = SIMD_4x32::alignr4(x[1], x[0]); + x[0] += SIMD_4x32::alignr4(x[3], x[2]); + + x[0] += t0.rotr<7>() ^ t0.rotr<18>() ^ t0.shr<3>(); + + t0 = SIMD_4x32::byte_shuffle(x[3], hi_word_shuf); + auto s1 = t0.rotr<17>() ^ t0.rotr<19>() ^ t0.shr<10>(); + x[0] += s1 & lo_mask; + + t0 = SIMD_4x32::byte_shuffle(x[0], lo_word_shuf); + s1 = t0.rotr<17>() ^ t0.rotr<19>() ^ t0.shr<10>(); + x[0] += s1 & hi_mask; + + const auto tmp = x[0]; + x[0] = x[1]; + x[1] = x[2]; + x[2] = x[3]; + x[3] = tmp; + + return x[3]; +} + +} // namespace + +void BOTAN_FN_ISA_SIMD_4X32 BOTAN_SCRUB_STACK_AFTER_RETURN +SHA_256::compress_digest_x86_simd(digest_type& digest, std::span input, size_t blocks) { + // clang-format off + + alignas(64) const uint32_t K[64] = { + 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, + 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, + 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, + 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, + 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, + 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, + 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, + 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2}; + + // clang-format on + + alignas(64) uint32_t W[16]; + + uint32_t A = digest[0]; + uint32_t B = digest[1]; + uint32_t C = digest[2]; + uint32_t D = digest[3]; + uint32_t E = digest[4]; + uint32_t F = digest[5]; + uint32_t G = digest[6]; + uint32_t H = digest[7]; + + const uint8_t* data = input.data(); + + while(blocks > 0) { + SIMD_4x32 WS[4]; + + for(size_t i = 0; i < 4; i++) { + WS[i] = SIMD_4x32::load_be(&data[16 * i]); + auto WK = WS[i] + SIMD_4x32::load_le(&K[4 * i]); + WK.store_le(&W[4 * i]); + } + + data += 64; + blocks -= 1; + + for(size_t r = 0; r != 48; r += 16) { + auto w = sha256_simd_next_w(WS) + SIMD_4x32::load_le(&K[r + 16]); + + SHA2_32_F(A, B, C, D, E, F, G, H, W[0]); + SHA2_32_F(H, A, B, C, D, E, F, G, W[1]); + SHA2_32_F(G, H, A, B, C, D, E, F, W[2]); + SHA2_32_F(F, G, H, A, B, C, D, E, W[3]); + + w.store_le(&W[0]); + + w = sha256_simd_next_w(WS) + SIMD_4x32::load_le(&K[r + 20]); + + SHA2_32_F(E, F, G, H, A, B, C, D, W[4]); + SHA2_32_F(D, E, F, G, H, A, B, C, W[5]); + SHA2_32_F(C, D, E, F, G, H, A, B, W[6]); + SHA2_32_F(B, C, D, E, F, G, H, A, W[7]); + + w.store_le(&W[4]); + + w = sha256_simd_next_w(WS) + SIMD_4x32::load_le(&K[r + 24]); + + SHA2_32_F(A, B, C, D, E, F, G, H, W[8]); + SHA2_32_F(H, A, B, C, D, E, F, G, W[9]); + SHA2_32_F(G, H, A, B, C, D, E, F, W[10]); + SHA2_32_F(F, G, H, A, B, C, D, E, W[11]); + + w.store_le(&W[8]); + + w = sha256_simd_next_w(WS) + SIMD_4x32::load_le(&K[r + 28]); + + SHA2_32_F(E, F, G, H, A, B, C, D, W[12]); + SHA2_32_F(D, E, F, G, H, A, B, C, W[13]); + SHA2_32_F(C, D, E, F, G, H, A, B, W[14]); + SHA2_32_F(B, C, D, E, F, G, H, A, W[15]); + + w.store_le(&W[12]); + } + + SHA2_32_F(A, B, C, D, E, F, G, H, W[0]); + SHA2_32_F(H, A, B, C, D, E, F, G, W[1]); + SHA2_32_F(G, H, A, B, C, D, E, F, W[2]); + SHA2_32_F(F, G, H, A, B, C, D, E, W[3]); + SHA2_32_F(E, F, G, H, A, B, C, D, W[4]); + SHA2_32_F(D, E, F, G, H, A, B, C, W[5]); + SHA2_32_F(C, D, E, F, G, H, A, B, W[6]); + SHA2_32_F(B, C, D, E, F, G, H, A, W[7]); + SHA2_32_F(A, B, C, D, E, F, G, H, W[8]); + SHA2_32_F(H, A, B, C, D, E, F, G, W[9]); + SHA2_32_F(G, H, A, B, C, D, E, F, W[10]); + SHA2_32_F(F, G, H, A, B, C, D, E, W[11]); + SHA2_32_F(E, F, G, H, A, B, C, D, W[12]); + SHA2_32_F(D, E, F, G, H, A, B, C, W[13]); + SHA2_32_F(C, D, E, F, G, H, A, B, W[14]); + SHA2_32_F(B, C, D, E, F, G, H, A, W[15]); + + A = (digest[0] += A); + B = (digest[1] += B); + C = (digest[2] += C); + D = (digest[3] += D); + E = (digest[4] += E); + F = (digest[5] += F); + G = (digest[6] += G); + H = (digest[7] += H); + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32_x86/info.txt botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_x86/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32_x86/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_x86/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + SHA2_32_X86 -> 20170518 - + name -> "SHA-256 SIMD" @@ -13,3 +13,8 @@ ssse3 sse41 + + +cpuid +simd_4x32 + diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32_x86/sha2_32_x86.cpp botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_x86/sha2_32_x86.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sha2_32/sha2_32_x86/sha2_32_x86.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_32/sha2_32_x86/sha2_32_x86.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,19 +1,61 @@ /* -* Support for SHA-256 x86 instrinsic * Based on public domain code by Sean Gulley -* (https://github.com/mitls/hacl-star/tree/master/experimental/hash) +* +* Further changes +* +* (C) 2017,2020,2025,2026 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include + +#include +#include +#include #include namespace Botan { -// called from sha2_32.cpp -BOTAN_FUNC_ISA("sha,sse4.1,ssse3") -void SHA_256::compress_digest_x86(digest_type& digest, std::span input, size_t blocks) { +namespace { + +// NOLINTBEGIN(portability-simd-intrinsics) + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_SHANI void sha256_rnds4(SIMD_4x32& S0, + SIMD_4x32& S1, + const SIMD_4x32& msg, + const SIMD_4x32& k) { + const auto mk = msg + k; + S1 = SIMD_4x32(_mm_sha256rnds2_epu32(S1.raw(), S0.raw(), mk.raw())); + S0 = SIMD_4x32(_mm_sha256rnds2_epu32(S0.raw(), S1.raw(), mk.shift_elems_right<2>().raw())); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_SHANI void sha256_msg_exp(SIMD_4x32& W0, SIMD_4x32& W1, SIMD_4x32& W2, SIMD_4x32& W3) { + W2 += SIMD_4x32::alignr4(W1, W0); + W0 = SIMD_4x32(_mm_sha256msg1_epu32(W0.raw(), W1.raw())); + W2 = SIMD_4x32(_mm_sha256msg2_epu32(W2.raw(), W1.raw())); + + W3 += SIMD_4x32::alignr4(W2, W1); + W1 = SIMD_4x32(_mm_sha256msg1_epu32(W1.raw(), W2.raw())); + W3 = SIMD_4x32(_mm_sha256msg2_epu32(W3.raw(), W2.raw())); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_SHANI void sha256_permute_state(SIMD_4x32& S0, SIMD_4x32& S1) { + S0 = SIMD_4x32(_mm_shuffle_epi32(S0.raw(), 0b10110001)); // CDAB + S1 = SIMD_4x32(_mm_shuffle_epi32(S1.raw(), 0b00011011)); // EFGH + + const auto T = SIMD_4x32::alignr8(S0, S1); // ABEF + S1 = SIMD_4x32(_mm_blend_epi16(S1.raw(), S0.raw(), 0xF0)); // CDGH + S0 = T; +} + +// NOLINTEND(portability-simd-intrinsics) + +} // namespace + +void BOTAN_FN_ISA_SHANI BOTAN_SCRUB_STACK_AFTER_RETURN SHA_256::compress_digest_x86(digest_type& digest, + std::span input_span, + size_t blocks) { alignas(64) static const uint32_t K[] = { 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, @@ -25,180 +67,72 @@ 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2, }; - const __m128i* K_mm = reinterpret_cast(K); + const uint8_t* input = input_span.data(); - uint32_t* state = &digest[0]; + SIMD_4x32 S0 = SIMD_4x32::load_le(&digest[0]); // NOLINT(*container-data-pointer) + SIMD_4x32 S1 = SIMD_4x32::load_le(&digest[4]); - const __m128i* input_mm = reinterpret_cast(input.data()); - const __m128i MASK = _mm_set_epi64x(0x0c0d0e0f08090a0b, 0x0405060700010203); + sha256_permute_state(S0, S1); - // Load initial values - __m128i STATE0 = _mm_loadu_si128(reinterpret_cast<__m128i*>(&state[0])); - __m128i STATE1 = _mm_loadu_si128(reinterpret_cast<__m128i*>(&state[4])); + while(blocks > 0) { + const auto S0_SAVE = S0; + const auto S1_SAVE = S1; - STATE0 = _mm_shuffle_epi32(STATE0, 0xB1); // CDAB - STATE1 = _mm_shuffle_epi32(STATE1, 0x1B); // EFGH + auto W0 = SIMD_4x32::load_be(input); + auto W1 = SIMD_4x32::load_be(input + 16); + auto W2 = SIMD_4x32::load_be(input + 32); + auto W3 = SIMD_4x32::load_be(input + 48); - __m128i TMP = _mm_alignr_epi8(STATE0, STATE1, 8); // ABEF - STATE1 = _mm_blend_epi16(STATE1, STATE0, 0xF0); // CDGH - STATE0 = TMP; + sha256_rnds4(S0, S1, W0, SIMD_4x32::load_le(&K[0])); + sha256_rnds4(S0, S1, W1, SIMD_4x32::load_le(&K[4])); + sha256_rnds4(S0, S1, W2, SIMD_4x32::load_le(&K[8])); + sha256_rnds4(S0, S1, W3, SIMD_4x32::load_le(&K[12])); - while(blocks > 0) { - // Save current state - const __m128i ABEF_SAVE = STATE0; - const __m128i CDGH_SAVE = STATE1; - - __m128i MSG; - - __m128i TMSG0 = _mm_shuffle_epi8(_mm_loadu_si128(input_mm), MASK); - __m128i TMSG1 = _mm_shuffle_epi8(_mm_loadu_si128(input_mm + 1), MASK); - __m128i TMSG2 = _mm_shuffle_epi8(_mm_loadu_si128(input_mm + 2), MASK); - __m128i TMSG3 = _mm_shuffle_epi8(_mm_loadu_si128(input_mm + 3), MASK); - - // Rounds 0-3 - MSG = _mm_add_epi32(TMSG0, _mm_load_si128(K_mm)); - STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); - STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, _mm_shuffle_epi32(MSG, 0x0E)); - - // Rounds 4-7 - MSG = _mm_add_epi32(TMSG1, _mm_load_si128(K_mm + 1)); - STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); - STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, _mm_shuffle_epi32(MSG, 0x0E)); - - TMSG0 = _mm_sha256msg1_epu32(TMSG0, TMSG1); - - // Rounds 8-11 - MSG = _mm_add_epi32(TMSG2, _mm_load_si128(K_mm + 2)); - STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); - STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, _mm_shuffle_epi32(MSG, 0x0E)); - - TMSG1 = _mm_sha256msg1_epu32(TMSG1, TMSG2); - - // Rounds 12-15 - MSG = _mm_add_epi32(TMSG3, _mm_load_si128(K_mm + 3)); - STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); - STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, _mm_shuffle_epi32(MSG, 0x0E)); - - TMSG0 = _mm_add_epi32(TMSG0, _mm_alignr_epi8(TMSG3, TMSG2, 4)); - TMSG0 = _mm_sha256msg2_epu32(TMSG0, TMSG3); - TMSG2 = _mm_sha256msg1_epu32(TMSG2, TMSG3); - - // Rounds 16-19 - MSG = _mm_add_epi32(TMSG0, _mm_load_si128(K_mm + 4)); - STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); - STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, _mm_shuffle_epi32(MSG, 0x0E)); - - TMSG1 = _mm_add_epi32(TMSG1, _mm_alignr_epi8(TMSG0, TMSG3, 4)); - TMSG1 = _mm_sha256msg2_epu32(TMSG1, TMSG0); - TMSG3 = _mm_sha256msg1_epu32(TMSG3, TMSG0); - - // Rounds 20-23 - MSG = _mm_add_epi32(TMSG1, _mm_load_si128(K_mm + 5)); - STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); - STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, _mm_shuffle_epi32(MSG, 0x0E)); - - TMSG2 = _mm_add_epi32(TMSG2, _mm_alignr_epi8(TMSG1, TMSG0, 4)); - TMSG2 = _mm_sha256msg2_epu32(TMSG2, TMSG1); - TMSG0 = _mm_sha256msg1_epu32(TMSG0, TMSG1); - - // Rounds 24-27 - MSG = _mm_add_epi32(TMSG2, _mm_load_si128(K_mm + 6)); - STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); - STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, _mm_shuffle_epi32(MSG, 0x0E)); - - TMSG3 = _mm_add_epi32(TMSG3, _mm_alignr_epi8(TMSG2, TMSG1, 4)); - TMSG3 = _mm_sha256msg2_epu32(TMSG3, TMSG2); - TMSG1 = _mm_sha256msg1_epu32(TMSG1, TMSG2); - - // Rounds 28-31 - MSG = _mm_add_epi32(TMSG3, _mm_load_si128(K_mm + 7)); - STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); - STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, _mm_shuffle_epi32(MSG, 0x0E)); - - TMSG0 = _mm_add_epi32(TMSG0, _mm_alignr_epi8(TMSG3, TMSG2, 4)); - TMSG0 = _mm_sha256msg2_epu32(TMSG0, TMSG3); - TMSG2 = _mm_sha256msg1_epu32(TMSG2, TMSG3); - - // Rounds 32-35 - MSG = _mm_add_epi32(TMSG0, _mm_load_si128(K_mm + 8)); - STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); - STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, _mm_shuffle_epi32(MSG, 0x0E)); - - TMSG1 = _mm_add_epi32(TMSG1, _mm_alignr_epi8(TMSG0, TMSG3, 4)); - TMSG1 = _mm_sha256msg2_epu32(TMSG1, TMSG0); - TMSG3 = _mm_sha256msg1_epu32(TMSG3, TMSG0); - - // Rounds 36-39 - MSG = _mm_add_epi32(TMSG1, _mm_load_si128(K_mm + 9)); - STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); - STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, _mm_shuffle_epi32(MSG, 0x0E)); - - TMSG2 = _mm_add_epi32(TMSG2, _mm_alignr_epi8(TMSG1, TMSG0, 4)); - TMSG2 = _mm_sha256msg2_epu32(TMSG2, TMSG1); - TMSG0 = _mm_sha256msg1_epu32(TMSG0, TMSG1); - - // Rounds 40-43 - MSG = _mm_add_epi32(TMSG2, _mm_load_si128(K_mm + 10)); - STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); - STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, _mm_shuffle_epi32(MSG, 0x0E)); - - TMSG3 = _mm_add_epi32(TMSG3, _mm_alignr_epi8(TMSG2, TMSG1, 4)); - TMSG3 = _mm_sha256msg2_epu32(TMSG3, TMSG2); - TMSG1 = _mm_sha256msg1_epu32(TMSG1, TMSG2); - - // Rounds 44-47 - MSG = _mm_add_epi32(TMSG3, _mm_load_si128(K_mm + 11)); - STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); - STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, _mm_shuffle_epi32(MSG, 0x0E)); - - TMSG0 = _mm_add_epi32(TMSG0, _mm_alignr_epi8(TMSG3, TMSG2, 4)); - TMSG0 = _mm_sha256msg2_epu32(TMSG0, TMSG3); - TMSG2 = _mm_sha256msg1_epu32(TMSG2, TMSG3); - - // Rounds 48-51 - MSG = _mm_add_epi32(TMSG0, _mm_load_si128(K_mm + 12)); - STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); - STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, _mm_shuffle_epi32(MSG, 0x0E)); - - TMSG1 = _mm_add_epi32(TMSG1, _mm_alignr_epi8(TMSG0, TMSG3, 4)); - TMSG1 = _mm_sha256msg2_epu32(TMSG1, TMSG0); - TMSG3 = _mm_sha256msg1_epu32(TMSG3, TMSG0); - - // Rounds 52-55 - MSG = _mm_add_epi32(TMSG1, _mm_load_si128(K_mm + 13)); - STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); - STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, _mm_shuffle_epi32(MSG, 0x0E)); - - TMSG2 = _mm_add_epi32(TMSG2, _mm_alignr_epi8(TMSG1, TMSG0, 4)); - TMSG2 = _mm_sha256msg2_epu32(TMSG2, TMSG1); - - // Rounds 56-59 - MSG = _mm_add_epi32(TMSG2, _mm_load_si128(K_mm + 14)); - STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); - STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, _mm_shuffle_epi32(MSG, 0x0E)); - - TMSG3 = _mm_add_epi32(TMSG3, _mm_alignr_epi8(TMSG2, TMSG1, 4)); - TMSG3 = _mm_sha256msg2_epu32(TMSG3, TMSG2); - - // Rounds 60-63 - MSG = _mm_add_epi32(TMSG3, _mm_load_si128(K_mm + 15)); - STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); - STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, _mm_shuffle_epi32(MSG, 0x0E)); + W0 = SIMD_4x32(_mm_sha256msg1_epu32(W0.raw(), W1.raw())); + W1 = SIMD_4x32(_mm_sha256msg1_epu32(W1.raw(), W2.raw())); + + sha256_msg_exp(W2, W3, W0, W1); + + sha256_rnds4(S0, S1, W0, SIMD_4x32::load_le(&K[4 * 4])); + sha256_rnds4(S0, S1, W1, SIMD_4x32::load_le(&K[4 * 5])); + + sha256_msg_exp(W0, W1, W2, W3); + + sha256_rnds4(S0, S1, W2, SIMD_4x32::load_le(&K[4 * 6])); + sha256_rnds4(S0, S1, W3, SIMD_4x32::load_le(&K[4 * 7])); + + sha256_msg_exp(W2, W3, W0, W1); + + sha256_rnds4(S0, S1, W0, SIMD_4x32::load_le(&K[4 * 8])); + sha256_rnds4(S0, S1, W1, SIMD_4x32::load_le(&K[4 * 9])); + + sha256_msg_exp(W0, W1, W2, W3); + + sha256_rnds4(S0, S1, W2, SIMD_4x32::load_le(&K[4 * 10])); + sha256_rnds4(S0, S1, W3, SIMD_4x32::load_le(&K[4 * 11])); + + sha256_msg_exp(W2, W3, W0, W1); + + sha256_rnds4(S0, S1, W0, SIMD_4x32::load_le(&K[4 * 12])); + sha256_rnds4(S0, S1, W1, SIMD_4x32::load_le(&K[4 * 13])); + + sha256_msg_exp(W0, W1, W2, W3); + + sha256_rnds4(S0, S1, W2, SIMD_4x32::load_le(&K[4 * 14])); + sha256_rnds4(S0, S1, W3, SIMD_4x32::load_le(&K[4 * 15])); // Add values back to state - STATE0 = _mm_add_epi32(STATE0, ABEF_SAVE); - STATE1 = _mm_add_epi32(STATE1, CDGH_SAVE); + S0 += S0_SAVE; + S1 += S1_SAVE; - input_mm += 4; + input += 64; blocks--; } - STATE0 = _mm_shuffle_epi32(STATE0, 0x1B); // FEBA - STATE1 = _mm_shuffle_epi32(STATE1, 0xB1); // DCHG + sha256_permute_state(S1, S0); - // Save state - _mm_storeu_si128(reinterpret_cast<__m128i*>(&state[0]), _mm_blend_epi16(STATE0, STATE1, 0xF0)); // DCBA - _mm_storeu_si128(reinterpret_cast<__m128i*>(&state[4]), _mm_alignr_epi8(STATE1, STATE0, 8)); // ABEF + S0.store_le(&digest[0]); // NOLINT(*container-data-pointer) + S1.store_le(&digest[4]); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_64/info.txt botan3-3.12.0+dfsg/src/lib/hash/sha2_64/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/sha2_64/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_64/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,8 +1,10 @@ -SHA2_64 -> 20131128 SHA_384 -> 20250130 SHA_512 -> 20250130 SHA_512_256 -> 20250130 + +# TODO(Botan4) remove this macro +SHA2_64 -> 20131128 diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64.cpp botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,27 +7,40 @@ #include -#include -#include +#include #include -#include #include -#include + +#if defined(BOTAN_HAS_CPUID) + #include +#endif namespace Botan { namespace { std::string sha512_provider() { -#if defined(BOTAN_HAS_SHA2_64_BMI2) - if(CPUID::has_bmi2()) { - return "bmi2"; +#if defined(BOTAN_HAS_SHA2_64_X86) + if(auto feat = CPUID::check(CPUID::Feature::SHA512)) { + return *feat; } #endif #if defined(BOTAN_HAS_SHA2_64_ARMV8) - if(CPUID::has_arm_sha2_512()) { - return "armv8"; + if(auto feat = CPUID::check(CPUID::Feature::SHA2_512)) { + return *feat; + } +#endif + +#if defined(BOTAN_HAS_SHA2_64_X86_AVX512) + if(auto feat = CPUID::check(CPUID::Feature::AVX512, CPUID::Feature::BMI)) { + return *feat; + } +#endif + +#if defined(BOTAN_HAS_SHA2_64_X86_AVX2) + if(auto feat = CPUID::check(CPUID::Feature::AVX2, CPUID::Feature::BMI)) { + return *feat; } #endif @@ -41,22 +54,40 @@ */ //static void SHA_512::compress_digest(digest_type& digest, std::span input, size_t blocks) { -#if defined(BOTAN_HAS_SHA2_64_BMI2) - if(CPUID::has_bmi2()) { - return compress_digest_bmi2(digest, input, blocks); +#if defined(BOTAN_HAS_SHA2_64_X86) + if(CPUID::has(CPUID::Feature::SHA512)) { + return compress_digest_x86(digest, input, blocks); } #endif #if defined(BOTAN_HAS_SHA2_64_ARMV8) - if(CPUID::has_arm_sha2_512()) { + if(CPUID::has(CPUID::Feature::SHA2_512)) { return compress_digest_armv8(digest, input, blocks); } #endif - uint64_t A = digest[0], B = digest[1], C = digest[2], D = digest[3], E = digest[4], F = digest[5], G = digest[6], - H = digest[7]; +#if defined(BOTAN_HAS_SHA2_64_X86_AVX512) + if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::BMI)) { + return compress_digest_x86_avx512(digest, input, blocks); + } +#endif + +#if defined(BOTAN_HAS_SHA2_64_X86_AVX2) + if(CPUID::has(CPUID::Feature::AVX2, CPUID::Feature::BMI)) { + return compress_digest_x86_avx2(digest, input, blocks); + } +#endif + + uint64_t A = digest[0]; + uint64_t B = digest[1]; + uint64_t C = digest[2]; + uint64_t D = digest[3]; + uint64_t E = digest[4]; + uint64_t F = digest[5]; + uint64_t G = digest[6]; + uint64_t H = digest[7]; - std::array W; + std::array W{}; BufferSlicer in(input); diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64.h botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64.h --- botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64.h 2026-05-07 01:38:28.000000000 +0000 @@ -86,8 +86,16 @@ public: static void compress_digest(digest_type& digest, std::span input, size_t blocks); -#if defined(BOTAN_HAS_SHA2_64_BMI2) - static void compress_digest_bmi2(digest_type& digest, std::span input, size_t blocks); +#if defined(BOTAN_HAS_SHA2_64_X86_AVX2) + static void compress_digest_x86_avx2(digest_type& digest, std::span input, size_t blocks); +#endif + +#if defined(BOTAN_HAS_SHA2_64_X86_AVX512) + static void compress_digest_x86_avx512(digest_type& digest, std::span input, size_t blocks); +#endif + +#if defined(BOTAN_HAS_SHA2_64_X86) + static void compress_digest_x86(digest_type& digest, std::span input, size_t blocks); #endif #if defined(BOTAN_HAS_SHA2_64_ARMV8) diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64_armv8/info.txt botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_armv8/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64_armv8/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_armv8/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + SHA2_64_ARMV8 -> 20231220 - + name -> "SHA-512 ARMv8" @@ -8,5 +8,10 @@ +armv8crypto armv8sha512 + + +cpuid + diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64_armv8/sha2_64_armv8.cpp botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_armv8/sha2_64_armv8.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64_armv8/sha2_64_armv8.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_armv8/sha2_64_armv8.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,8 @@ */ #include + +#include #include namespace Botan { @@ -14,8 +16,9 @@ /* * SHA-512 using CPU instructions in ARMv8 */ -BOTAN_FUNC_ISA("arch=armv8.2-a+sha3") -void SHA_512::compress_digest_armv8(digest_type& digest, std::span input8, size_t blocks) { +void BOTAN_FN_ISA_SHA512 SHA_512::compress_digest_armv8(digest_type& digest, + std::span input8, + size_t blocks) { alignas(128) static const uint64_t K[] = { 0x428A2F98D728AE22, 0x7137449123EF65CD, 0xB5C0FBCFEC4D3B2F, 0xE9B5DBA58189DBBC, 0x3956C25BF348B538, 0x59F111F1B605D019, 0x923F82A4AF194F9B, 0xAB1C5ED5DA6D8118, 0xD807AA98A3030242, 0x12835B0145706FBE, @@ -35,7 +38,7 @@ 0x431D67C49C100D4C, 0x4CC5D4BECB3E42B6, 0x597F299CFC657E2A, 0x5FCB6FAB3AD6FAEC, 0x6C44198C4A475817}; // Load initial values - uint64x2_t STATE0 = vld1q_u64(&digest[0]); // ab + uint64x2_t STATE0 = vld1q_u64(&digest[0]); // ab NOLINT(*-container-data-pointer) uint64x2_t STATE1 = vld1q_u64(&digest[2]); // cd uint64x2_t STATE2 = vld1q_u64(&digest[4]); // ef uint64x2_t STATE3 = vld1q_u64(&digest[6]); // gh @@ -67,7 +70,9 @@ MSG6 = vreinterpretq_u64_u8(vrev64q_u8(vreinterpretq_u8_u64(MSG6))); MSG7 = vreinterpretq_u64_u8(vrev64q_u8(vreinterpretq_u8_u64(MSG7))); - uint64x2_t MSG_K, TSTATE0, TSTATE1; + uint64x2_t MSG_K; + uint64x2_t TSTATE0; + uint64x2_t TSTATE1; // Rounds 0-1 MSG_K = vaddq_u64(MSG0, vld1q_u64(&K[2 * 0])); @@ -392,7 +397,7 @@ } // Save state - vst1q_u64(&digest[0], STATE0); + vst1q_u64(&digest[0], STATE0); // NOLINT(*-container-data-pointer) vst1q_u64(&digest[2], STATE1); vst1q_u64(&digest[4], STATE2); vst1q_u64(&digest[6], STATE3); diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64_avx2/info.txt botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_avx2/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64_avx2/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_avx2/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,25 @@ + +SHA2_64_X86_AVX2 -> 20190117 + + + +name -> "SHA-512 AVX2/BMI2" +brief -> "SHA-512 using AVX2/BMI2 instructions" + + + +bmi2 +avx2 + + +# Needs 64-bit registers to be useful + +x86_64 +x32 + + + +cpuid +simd_4x64 +simd_2x64 + diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64_avx2/sha2_64_avx2.cpp botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_avx2/sha2_64_avx2.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64_avx2/sha2_64_avx2.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_avx2/sha2_64_avx2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,346 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include + +namespace Botan { + +namespace { + +template +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_BMI2 SIMD_T sha512_next_w(SIMD_T x[8]) { + auto t0 = SIMD_T::alignr8(x[1], x[0]); + auto t1 = SIMD_T::alignr8(x[5], x[4]); + + auto s0 = t0.template rotr<1>() ^ t0.template rotr<8>() ^ t0.template shr<7>(); + auto s1 = x[7].template rotr<19>() ^ x[7].template rotr<61>() ^ x[7].template shr<6>(); + + auto nx = x[0] + s0 + s1 + t1; + + x[0] = x[1]; + x[1] = x[2]; + x[2] = x[3]; + x[3] = x[4]; + x[4] = x[5]; + x[5] = x[6]; + x[6] = x[7]; + x[7] = nx; + + return x[7]; +} + +} // namespace + +BOTAN_FN_ISA_AVX2_BMI2 void SHA_512::compress_digest_x86_avx2(digest_type& digest, + std::span input, + size_t blocks) { + // clang-format off + alignas(64) const uint64_t K[80] = { + 0x428A2F98D728AE22, 0x7137449123EF65CD, 0xB5C0FBCFEC4D3B2F, 0xE9B5DBA58189DBBC, + 0x3956C25BF348B538, 0x59F111F1B605D019, 0x923F82A4AF194F9B, 0xAB1C5ED5DA6D8118, + 0xD807AA98A3030242, 0x12835B0145706FBE, 0x243185BE4EE4B28C, 0x550C7DC3D5FFB4E2, + 0x72BE5D74F27B896F, 0x80DEB1FE3B1696B1, 0x9BDC06A725C71235, 0xC19BF174CF692694, + 0xE49B69C19EF14AD2, 0xEFBE4786384F25E3, 0x0FC19DC68B8CD5B5, 0x240CA1CC77AC9C65, + 0x2DE92C6F592B0275, 0x4A7484AA6EA6E483, 0x5CB0A9DCBD41FBD4, 0x76F988DA831153B5, + 0x983E5152EE66DFAB, 0xA831C66D2DB43210, 0xB00327C898FB213F, 0xBF597FC7BEEF0EE4, + 0xC6E00BF33DA88FC2, 0xD5A79147930AA725, 0x06CA6351E003826F, 0x142929670A0E6E70, + 0x27B70A8546D22FFC, 0x2E1B21385C26C926, 0x4D2C6DFC5AC42AED, 0x53380D139D95B3DF, + 0x650A73548BAF63DE, 0x766A0ABB3C77B2A8, 0x81C2C92E47EDAEE6, 0x92722C851482353B, + 0xA2BFE8A14CF10364, 0xA81A664BBC423001, 0xC24B8B70D0F89791, 0xC76C51A30654BE30, + 0xD192E819D6EF5218, 0xD69906245565A910, 0xF40E35855771202A, 0x106AA07032BBD1B8, + 0x19A4C116B8D2D0C8, 0x1E376C085141AB53, 0x2748774CDF8EEB99, 0x34B0BCB5E19B48A8, + 0x391C0CB3C5C95A63, 0x4ED8AA4AE3418ACB, 0x5B9CCA4F7763E373, 0x682E6FF3D6B2B8A3, + 0x748F82EE5DEFB2FC, 0x78A5636F43172F60, 0x84C87814A1F0AB72, 0x8CC702081A6439EC, + 0x90BEFFFA23631E28, 0xA4506CEBDE82BDE9, 0xBEF9A3F7B2C67915, 0xC67178F2E372532B, + 0xCA273ECEEA26619C, 0xD186B8C721C0C207, 0xEADA7DD6CDE0EB1E, 0xF57D4F7FEE6ED178, + 0x06F067AA72176FBA, 0x0A637DC5A2C898A6, 0x113F9804BEF90DAE, 0x1B710B35131C471B, + 0x28DB77F523047D84, 0x32CAAB7B40C72493, 0x3C9EBE0A15C9BEBC, 0x431D67C49C100D4C, + 0x4CC5D4BECB3E42B6, 0x597F299CFC657E2A, 0x5FCB6FAB3AD6FAEC, 0x6C44198C4A475817, + }; + // clang-format on + + alignas(64) uint64_t W[16] = {0}; + alignas(64) uint64_t W2[80]; + + uint64_t A = digest[0]; + uint64_t B = digest[1]; + uint64_t C = digest[2]; + uint64_t D = digest[3]; + uint64_t E = digest[4]; + uint64_t F = digest[5]; + uint64_t G = digest[6]; + uint64_t H = digest[7]; + + const uint8_t* data = input.data(); + + while(blocks >= 2) { + SIMD_4x64 WS[8]; + + for(size_t i = 0; i < 8; i++) { + WS[i] = SIMD_4x64::load_be2(&data[16 * i], &data[128 + 16 * i]); + auto WK = WS[i] + SIMD_4x64::broadcast_2x64(&K[2 * i]); + WK.store_le2(&W[2 * i], &W2[2 * i]); + } + + data += 2 * 128; + blocks -= 2; + + // First 64 rounds of SHA-512 + for(size_t r = 0; r != 64; r += 16) { + auto w = sha512_next_w(WS) + SIMD_4x64::broadcast_2x64(&K[r + 16]); + SHA2_64_F(A, B, C, D, E, F, G, H, W[0]); + SHA2_64_F(H, A, B, C, D, E, F, G, W[1]); + w.store_le2(&W[0], &W2[r + 16]); + + w = sha512_next_w(WS) + SIMD_4x64::broadcast_2x64(&K[r + 18]); + SHA2_64_F(G, H, A, B, C, D, E, F, W[2]); + SHA2_64_F(F, G, H, A, B, C, D, E, W[3]); + w.store_le2(&W[2], &W2[r + 18]); + + w = sha512_next_w(WS) + SIMD_4x64::broadcast_2x64(&K[r + 20]); + SHA2_64_F(E, F, G, H, A, B, C, D, W[4]); + SHA2_64_F(D, E, F, G, H, A, B, C, W[5]); + w.store_le2(&W[4], &W2[r + 20]); + + w = sha512_next_w(WS) + SIMD_4x64::broadcast_2x64(&K[r + 22]); + SHA2_64_F(C, D, E, F, G, H, A, B, W[6]); + SHA2_64_F(B, C, D, E, F, G, H, A, W[7]); + w.store_le2(&W[6], &W2[r + 22]); + + w = sha512_next_w(WS) + SIMD_4x64::broadcast_2x64(&K[r + 24]); + SHA2_64_F(A, B, C, D, E, F, G, H, W[8]); + SHA2_64_F(H, A, B, C, D, E, F, G, W[9]); + w.store_le2(&W[8], &W2[r + 24]); + + w = sha512_next_w(WS) + SIMD_4x64::broadcast_2x64(&K[r + 26]); + SHA2_64_F(G, H, A, B, C, D, E, F, W[10]); + SHA2_64_F(F, G, H, A, B, C, D, E, W[11]); + w.store_le2(&W[10], &W2[r + 26]); + + w = sha512_next_w(WS) + SIMD_4x64::broadcast_2x64(&K[r + 28]); + SHA2_64_F(E, F, G, H, A, B, C, D, W[12]); + SHA2_64_F(D, E, F, G, H, A, B, C, W[13]); + w.store_le2(&W[12], &W2[r + 28]); + + w = sha512_next_w(WS) + SIMD_4x64::broadcast_2x64(&K[r + 30]); + SHA2_64_F(C, D, E, F, G, H, A, B, W[14]); + SHA2_64_F(B, C, D, E, F, G, H, A, W[15]); + w.store_le2(&W[14], &W2[r + 30]); + } + + // Final 16 rounds of SHA-512 + SHA2_64_F(A, B, C, D, E, F, G, H, W[0]); + SHA2_64_F(H, A, B, C, D, E, F, G, W[1]); + SHA2_64_F(G, H, A, B, C, D, E, F, W[2]); + SHA2_64_F(F, G, H, A, B, C, D, E, W[3]); + SHA2_64_F(E, F, G, H, A, B, C, D, W[4]); + SHA2_64_F(D, E, F, G, H, A, B, C, W[5]); + SHA2_64_F(C, D, E, F, G, H, A, B, W[6]); + SHA2_64_F(B, C, D, E, F, G, H, A, W[7]); + SHA2_64_F(A, B, C, D, E, F, G, H, W[8]); + SHA2_64_F(H, A, B, C, D, E, F, G, W[9]); + SHA2_64_F(G, H, A, B, C, D, E, F, W[10]); + SHA2_64_F(F, G, H, A, B, C, D, E, W[11]); + SHA2_64_F(E, F, G, H, A, B, C, D, W[12]); + SHA2_64_F(D, E, F, G, H, A, B, C, W[13]); + SHA2_64_F(C, D, E, F, G, H, A, B, W[14]); + SHA2_64_F(B, C, D, E, F, G, H, A, W[15]); + + A = (digest[0] += A); + B = (digest[1] += B); + C = (digest[2] += C); + D = (digest[3] += D); + E = (digest[4] += E); + F = (digest[5] += F); + G = (digest[6] += G); + H = (digest[7] += H); + + // Second block of SHA-512 compression, with pre-expanded message + SHA2_64_F(A, B, C, D, E, F, G, H, W2[0]); + SHA2_64_F(H, A, B, C, D, E, F, G, W2[1]); + SHA2_64_F(G, H, A, B, C, D, E, F, W2[2]); + SHA2_64_F(F, G, H, A, B, C, D, E, W2[3]); + SHA2_64_F(E, F, G, H, A, B, C, D, W2[4]); + SHA2_64_F(D, E, F, G, H, A, B, C, W2[5]); + SHA2_64_F(C, D, E, F, G, H, A, B, W2[6]); + SHA2_64_F(B, C, D, E, F, G, H, A, W2[7]); + SHA2_64_F(A, B, C, D, E, F, G, H, W2[8]); + SHA2_64_F(H, A, B, C, D, E, F, G, W2[9]); + SHA2_64_F(G, H, A, B, C, D, E, F, W2[10]); + SHA2_64_F(F, G, H, A, B, C, D, E, W2[11]); + SHA2_64_F(E, F, G, H, A, B, C, D, W2[12]); + SHA2_64_F(D, E, F, G, H, A, B, C, W2[13]); + SHA2_64_F(C, D, E, F, G, H, A, B, W2[14]); + SHA2_64_F(B, C, D, E, F, G, H, A, W2[15]); + + SHA2_64_F(A, B, C, D, E, F, G, H, W2[16]); + SHA2_64_F(H, A, B, C, D, E, F, G, W2[17]); + SHA2_64_F(G, H, A, B, C, D, E, F, W2[18]); + SHA2_64_F(F, G, H, A, B, C, D, E, W2[19]); + SHA2_64_F(E, F, G, H, A, B, C, D, W2[20]); + SHA2_64_F(D, E, F, G, H, A, B, C, W2[21]); + SHA2_64_F(C, D, E, F, G, H, A, B, W2[22]); + SHA2_64_F(B, C, D, E, F, G, H, A, W2[23]); + SHA2_64_F(A, B, C, D, E, F, G, H, W2[24]); + SHA2_64_F(H, A, B, C, D, E, F, G, W2[25]); + SHA2_64_F(G, H, A, B, C, D, E, F, W2[26]); + SHA2_64_F(F, G, H, A, B, C, D, E, W2[27]); + SHA2_64_F(E, F, G, H, A, B, C, D, W2[28]); + SHA2_64_F(D, E, F, G, H, A, B, C, W2[29]); + SHA2_64_F(C, D, E, F, G, H, A, B, W2[30]); + SHA2_64_F(B, C, D, E, F, G, H, A, W2[31]); + + SHA2_64_F(A, B, C, D, E, F, G, H, W2[32]); + SHA2_64_F(H, A, B, C, D, E, F, G, W2[33]); + SHA2_64_F(G, H, A, B, C, D, E, F, W2[34]); + SHA2_64_F(F, G, H, A, B, C, D, E, W2[35]); + SHA2_64_F(E, F, G, H, A, B, C, D, W2[36]); + SHA2_64_F(D, E, F, G, H, A, B, C, W2[37]); + SHA2_64_F(C, D, E, F, G, H, A, B, W2[38]); + SHA2_64_F(B, C, D, E, F, G, H, A, W2[39]); + SHA2_64_F(A, B, C, D, E, F, G, H, W2[40]); + SHA2_64_F(H, A, B, C, D, E, F, G, W2[41]); + SHA2_64_F(G, H, A, B, C, D, E, F, W2[42]); + SHA2_64_F(F, G, H, A, B, C, D, E, W2[43]); + SHA2_64_F(E, F, G, H, A, B, C, D, W2[44]); + SHA2_64_F(D, E, F, G, H, A, B, C, W2[45]); + SHA2_64_F(C, D, E, F, G, H, A, B, W2[46]); + SHA2_64_F(B, C, D, E, F, G, H, A, W2[47]); + + SHA2_64_F(A, B, C, D, E, F, G, H, W2[48]); + SHA2_64_F(H, A, B, C, D, E, F, G, W2[49]); + SHA2_64_F(G, H, A, B, C, D, E, F, W2[50]); + SHA2_64_F(F, G, H, A, B, C, D, E, W2[51]); + SHA2_64_F(E, F, G, H, A, B, C, D, W2[52]); + SHA2_64_F(D, E, F, G, H, A, B, C, W2[53]); + SHA2_64_F(C, D, E, F, G, H, A, B, W2[54]); + SHA2_64_F(B, C, D, E, F, G, H, A, W2[55]); + SHA2_64_F(A, B, C, D, E, F, G, H, W2[56]); + SHA2_64_F(H, A, B, C, D, E, F, G, W2[57]); + SHA2_64_F(G, H, A, B, C, D, E, F, W2[58]); + SHA2_64_F(F, G, H, A, B, C, D, E, W2[59]); + SHA2_64_F(E, F, G, H, A, B, C, D, W2[60]); + SHA2_64_F(D, E, F, G, H, A, B, C, W2[61]); + SHA2_64_F(C, D, E, F, G, H, A, B, W2[62]); + SHA2_64_F(B, C, D, E, F, G, H, A, W2[63]); + + SHA2_64_F(A, B, C, D, E, F, G, H, W2[64]); + SHA2_64_F(H, A, B, C, D, E, F, G, W2[65]); + SHA2_64_F(G, H, A, B, C, D, E, F, W2[66]); + SHA2_64_F(F, G, H, A, B, C, D, E, W2[67]); + SHA2_64_F(E, F, G, H, A, B, C, D, W2[68]); + SHA2_64_F(D, E, F, G, H, A, B, C, W2[69]); + SHA2_64_F(C, D, E, F, G, H, A, B, W2[70]); + SHA2_64_F(B, C, D, E, F, G, H, A, W2[71]); + SHA2_64_F(A, B, C, D, E, F, G, H, W2[72]); + SHA2_64_F(H, A, B, C, D, E, F, G, W2[73]); + SHA2_64_F(G, H, A, B, C, D, E, F, W2[74]); + SHA2_64_F(F, G, H, A, B, C, D, E, W2[75]); + SHA2_64_F(E, F, G, H, A, B, C, D, W2[76]); + SHA2_64_F(D, E, F, G, H, A, B, C, W2[77]); + SHA2_64_F(C, D, E, F, G, H, A, B, W2[78]); + SHA2_64_F(B, C, D, E, F, G, H, A, W2[79]); + + A = (digest[0] += A); + B = (digest[1] += B); + C = (digest[2] += C); + D = (digest[3] += D); + E = (digest[4] += E); + F = (digest[5] += F); + G = (digest[6] += G); + H = (digest[7] += H); + } + + while(blocks > 0) { + SIMD_2x64 WS[8]; + + for(size_t i = 0; i < 8; i++) { + WS[i] = SIMD_2x64::load_be(&data[16 * i]); + auto WK = WS[i] + SIMD_2x64::load_le(&K[2 * i]); + WK.store_le(&W[2 * i]); + } + + data += 128; + blocks -= 1; + + // First 64 rounds of SHA-512 + for(size_t r = 0; r != 64; r += 16) { + auto w = sha512_next_w(WS) + SIMD_2x64::load_le(&K[r + 16]); + SHA2_64_F(A, B, C, D, E, F, G, H, W[0]); + SHA2_64_F(H, A, B, C, D, E, F, G, W[1]); + w.store_le(&W[0]); + + w = sha512_next_w(WS) + SIMD_2x64::load_le(&K[r + 18]); + SHA2_64_F(G, H, A, B, C, D, E, F, W[2]); + SHA2_64_F(F, G, H, A, B, C, D, E, W[3]); + w.store_le(&W[2]); + + w = sha512_next_w(WS) + SIMD_2x64::load_le(&K[r + 20]); + SHA2_64_F(E, F, G, H, A, B, C, D, W[4]); + SHA2_64_F(D, E, F, G, H, A, B, C, W[5]); + w.store_le(&W[4]); + + w = sha512_next_w(WS) + SIMD_2x64::load_le(&K[r + 22]); + SHA2_64_F(C, D, E, F, G, H, A, B, W[6]); + SHA2_64_F(B, C, D, E, F, G, H, A, W[7]); + w.store_le(&W[6]); + + w = sha512_next_w(WS) + SIMD_2x64::load_le(&K[r + 24]); + SHA2_64_F(A, B, C, D, E, F, G, H, W[8]); + SHA2_64_F(H, A, B, C, D, E, F, G, W[9]); + w.store_le(&W[8]); + + w = sha512_next_w(WS) + SIMD_2x64::load_le(&K[r + 26]); + SHA2_64_F(G, H, A, B, C, D, E, F, W[10]); + SHA2_64_F(F, G, H, A, B, C, D, E, W[11]); + w.store_le(&W[10]); + + w = sha512_next_w(WS) + SIMD_2x64::load_le(&K[r + 28]); + SHA2_64_F(E, F, G, H, A, B, C, D, W[12]); + SHA2_64_F(D, E, F, G, H, A, B, C, W[13]); + w.store_le(&W[12]); + + w = sha512_next_w(WS) + SIMD_2x64::load_le(&K[r + 30]); + SHA2_64_F(C, D, E, F, G, H, A, B, W[14]); + SHA2_64_F(B, C, D, E, F, G, H, A, W[15]); + w.store_le(&W[14]); + } + + // Final 16 rounds of SHA-512 + SHA2_64_F(A, B, C, D, E, F, G, H, W[0]); + SHA2_64_F(H, A, B, C, D, E, F, G, W[1]); + SHA2_64_F(G, H, A, B, C, D, E, F, W[2]); + SHA2_64_F(F, G, H, A, B, C, D, E, W[3]); + SHA2_64_F(E, F, G, H, A, B, C, D, W[4]); + SHA2_64_F(D, E, F, G, H, A, B, C, W[5]); + SHA2_64_F(C, D, E, F, G, H, A, B, W[6]); + SHA2_64_F(B, C, D, E, F, G, H, A, W[7]); + SHA2_64_F(A, B, C, D, E, F, G, H, W[8]); + SHA2_64_F(H, A, B, C, D, E, F, G, W[9]); + SHA2_64_F(G, H, A, B, C, D, E, F, W[10]); + SHA2_64_F(F, G, H, A, B, C, D, E, W[11]); + SHA2_64_F(E, F, G, H, A, B, C, D, W[12]); + SHA2_64_F(D, E, F, G, H, A, B, C, W[13]); + SHA2_64_F(C, D, E, F, G, H, A, B, W[14]); + SHA2_64_F(B, C, D, E, F, G, H, A, W[15]); + + A = (digest[0] += A); + B = (digest[1] += B); + C = (digest[2] += C); + D = (digest[3] += D); + E = (digest[4] += E); + F = (digest[5] += F); + G = (digest[6] += G); + H = (digest[7] += H); + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64_avx512/info.txt botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_avx512/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64_avx512/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_avx512/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,25 @@ + +SHA2_64_X86_AVX512 -> 20250427 + + + +name -> "SHA-512 AVX512/BMI2" +brief -> "SHA-512 using AVX512/BMI2 instructions" + + + +bmi2 +avx512 + + +# Needs 64-bit registers to be useful + +x86_64 +x32 + + + +cpuid +simd_8x64 +simd_2x64 + diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64_avx512/sha2_64_avx512.cpp botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_avx512/sha2_64_avx512.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64_avx512/sha2_64_avx512.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_avx512/sha2_64_avx512.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,235 @@ +/* +* (C) 2025,2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include + +namespace Botan { + +namespace SHA512_AVX512 { + +namespace { + +template +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_BMI2 SIMD_2x64 avx512_sigma(SIMD_2x64 v) { + const auto vr1 = _mm_ror_epi64(v.raw(), R1); + const auto vr2 = _mm_ror_epi64(v.raw(), R2); + const auto vs1 = _mm_srli_epi64(v.raw(), S1); + return SIMD_2x64(_mm_ternarylogic_epi64(vr1, vr2, vs1, 0x96)); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_BMI2 SIMD_2x64 sha512_next_w_avx512(SIMD_2x64 x[8]) { + auto t0 = SIMD_2x64::alignr8(x[1], x[0]); + auto t1 = SIMD_2x64::alignr8(x[5], x[4]); + + auto s0 = avx512_sigma<1, 8, 7>(t0); + auto s1 = avx512_sigma<19, 61, 6>(x[7]); + + auto nx = x[0] + s0 + s1 + t1; + + x[0] = x[1]; + x[1] = x[2]; + x[2] = x[3]; + x[3] = x[4]; + x[4] = x[5]; + x[5] = x[6]; + x[6] = x[7]; + x[7] = nx; + + return nx; +} + +template +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_BMI2 SIMD_2x64 rho(SIMD_2x64 v) { + const auto vr1 = _mm_ror_epi64(v.raw(), R1); + const auto vr2 = _mm_ror_epi64(v.raw(), R2); + const auto vr3 = _mm_ror_epi64(v.raw(), R3); + return SIMD_2x64(_mm_ternarylogic_epi64(vr1, vr2, vr3, 0x96)); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_BMI2 void SHA2_64_F(SIMD_2x64 A, + SIMD_2x64 B, + SIMD_2x64 C, + SIMD_2x64& D, + SIMD_2x64 E, + SIMD_2x64 F, + SIMD_2x64 G, + SIMD_2x64& H, + uint64_t M) { + constexpr uint8_t ch = 0xca; + constexpr uint8_t maj = 0xe8; + + H += rho<14, 18, 41>(E) + SIMD_2x64(_mm_ternarylogic_epi64(E.raw(), F.raw(), G.raw(), ch)) + SIMD_2x64::splat(M); + D += H; + H += rho<28, 34, 39>(A) + SIMD_2x64(_mm_ternarylogic_epi64(A.raw(), B.raw(), C.raw(), maj)); +} + +} // namespace + +} // namespace SHA512_AVX512 + +BOTAN_FN_ISA_AVX512_BMI2 void SHA_512::compress_digest_x86_avx512(digest_type& digest, + std::span input, + size_t blocks) { + using namespace SHA512_AVX512; + + // clang-format off + alignas(64) const uint64_t K[80] = { + 0x428A2F98D728AE22, 0x7137449123EF65CD, 0xB5C0FBCFEC4D3B2F, 0xE9B5DBA58189DBBC, + 0x3956C25BF348B538, 0x59F111F1B605D019, 0x923F82A4AF194F9B, 0xAB1C5ED5DA6D8118, + 0xD807AA98A3030242, 0x12835B0145706FBE, 0x243185BE4EE4B28C, 0x550C7DC3D5FFB4E2, + 0x72BE5D74F27B896F, 0x80DEB1FE3B1696B1, 0x9BDC06A725C71235, 0xC19BF174CF692694, + 0xE49B69C19EF14AD2, 0xEFBE4786384F25E3, 0x0FC19DC68B8CD5B5, 0x240CA1CC77AC9C65, + 0x2DE92C6F592B0275, 0x4A7484AA6EA6E483, 0x5CB0A9DCBD41FBD4, 0x76F988DA831153B5, + 0x983E5152EE66DFAB, 0xA831C66D2DB43210, 0xB00327C898FB213F, 0xBF597FC7BEEF0EE4, + 0xC6E00BF33DA88FC2, 0xD5A79147930AA725, 0x06CA6351E003826F, 0x142929670A0E6E70, + 0x27B70A8546D22FFC, 0x2E1B21385C26C926, 0x4D2C6DFC5AC42AED, 0x53380D139D95B3DF, + 0x650A73548BAF63DE, 0x766A0ABB3C77B2A8, 0x81C2C92E47EDAEE6, 0x92722C851482353B, + 0xA2BFE8A14CF10364, 0xA81A664BBC423001, 0xC24B8B70D0F89791, 0xC76C51A30654BE30, + 0xD192E819D6EF5218, 0xD69906245565A910, 0xF40E35855771202A, 0x106AA07032BBD1B8, + 0x19A4C116B8D2D0C8, 0x1E376C085141AB53, 0x2748774CDF8EEB99, 0x34B0BCB5E19B48A8, + 0x391C0CB3C5C95A63, 0x4ED8AA4AE3418ACB, 0x5B9CCA4F7763E373, 0x682E6FF3D6B2B8A3, + 0x748F82EE5DEFB2FC, 0x78A5636F43172F60, 0x84C87814A1F0AB72, 0x8CC702081A6439EC, + 0x90BEFFFA23631E28, 0xA4506CEBDE82BDE9, 0xBEF9A3F7B2C67915, 0xC67178F2E372532B, + 0xCA273ECEEA26619C, 0xD186B8C721C0C207, 0xEADA7DD6CDE0EB1E, 0xF57D4F7FEE6ED178, + 0x06F067AA72176FBA, 0x0A637DC5A2C898A6, 0x113F9804BEF90DAE, 0x1B710B35131C471B, + 0x28DB77F523047D84, 0x32CAAB7B40C72493, 0x3C9EBE0A15C9BEBC, 0x431D67C49C100D4C, + 0x4CC5D4BECB3E42B6, 0x597F299CFC657E2A, 0x5FCB6FAB3AD6FAEC, 0x6C44198C4A475817, + }; + + // clang-format on + + alignas(64) uint64_t W[16] = {0}; + + auto digest0 = SIMD_2x64::splat(digest[0]); + auto digest1 = SIMD_2x64::splat(digest[1]); + auto digest2 = SIMD_2x64::splat(digest[2]); + auto digest3 = SIMD_2x64::splat(digest[3]); + auto digest4 = SIMD_2x64::splat(digest[4]); + auto digest5 = SIMD_2x64::splat(digest[5]); + auto digest6 = SIMD_2x64::splat(digest[6]); + auto digest7 = SIMD_2x64::splat(digest[7]); + + auto A = digest0; + auto B = digest1; + auto C = digest2; + auto D = digest3; + auto E = digest4; + auto F = digest5; + auto G = digest6; + auto H = digest7; + + const uint8_t* data = input.data(); + + while(blocks > 0) { + SIMD_2x64 WS[8]; + + for(size_t i = 0; i < 8; i++) { + WS[i] = SIMD_2x64::load_be(&data[16 * i]); + auto WK = WS[i] + SIMD_2x64::load_le(&K[2 * i]); + WK.store_le(&W[2 * i]); + } + + data += 128; + blocks -= 1; + + // First 64 rounds of SHA-512 + for(size_t r = 0; r != 64; r += 16) { + auto w = sha512_next_w_avx512(WS) + SIMD_2x64::load_le(&K[r + 16]); + SHA2_64_F(A, B, C, D, E, F, G, H, W[0]); + SHA2_64_F(H, A, B, C, D, E, F, G, W[1]); + w.store_le(&W[0]); + + w = sha512_next_w_avx512(WS) + SIMD_2x64::load_le(&K[r + 18]); + SHA2_64_F(G, H, A, B, C, D, E, F, W[2]); + SHA2_64_F(F, G, H, A, B, C, D, E, W[3]); + w.store_le(&W[2]); + + w = sha512_next_w_avx512(WS) + SIMD_2x64::load_le(&K[r + 20]); + SHA2_64_F(E, F, G, H, A, B, C, D, W[4]); + SHA2_64_F(D, E, F, G, H, A, B, C, W[5]); + w.store_le(&W[4]); + + w = sha512_next_w_avx512(WS) + SIMD_2x64::load_le(&K[r + 22]); + SHA2_64_F(C, D, E, F, G, H, A, B, W[6]); + SHA2_64_F(B, C, D, E, F, G, H, A, W[7]); + w.store_le(&W[6]); + + w = sha512_next_w_avx512(WS) + SIMD_2x64::load_le(&K[r + 24]); + SHA2_64_F(A, B, C, D, E, F, G, H, W[8]); + SHA2_64_F(H, A, B, C, D, E, F, G, W[9]); + w.store_le(&W[8]); + + w = sha512_next_w_avx512(WS) + SIMD_2x64::load_le(&K[r + 26]); + SHA2_64_F(G, H, A, B, C, D, E, F, W[10]); + SHA2_64_F(F, G, H, A, B, C, D, E, W[11]); + w.store_le(&W[10]); + + w = sha512_next_w_avx512(WS) + SIMD_2x64::load_le(&K[r + 28]); + SHA2_64_F(E, F, G, H, A, B, C, D, W[12]); + SHA2_64_F(D, E, F, G, H, A, B, C, W[13]); + w.store_le(&W[12]); + + w = sha512_next_w_avx512(WS) + SIMD_2x64::load_le(&K[r + 30]); + SHA2_64_F(C, D, E, F, G, H, A, B, W[14]); + SHA2_64_F(B, C, D, E, F, G, H, A, W[15]); + w.store_le(&W[14]); + } + + // Final 16 rounds of SHA-512 + SHA2_64_F(A, B, C, D, E, F, G, H, W[0]); + SHA2_64_F(H, A, B, C, D, E, F, G, W[1]); + SHA2_64_F(G, H, A, B, C, D, E, F, W[2]); + SHA2_64_F(F, G, H, A, B, C, D, E, W[3]); + SHA2_64_F(E, F, G, H, A, B, C, D, W[4]); + SHA2_64_F(D, E, F, G, H, A, B, C, W[5]); + SHA2_64_F(C, D, E, F, G, H, A, B, W[6]); + SHA2_64_F(B, C, D, E, F, G, H, A, W[7]); + SHA2_64_F(A, B, C, D, E, F, G, H, W[8]); + SHA2_64_F(H, A, B, C, D, E, F, G, W[9]); + SHA2_64_F(G, H, A, B, C, D, E, F, W[10]); + SHA2_64_F(F, G, H, A, B, C, D, E, W[11]); + SHA2_64_F(E, F, G, H, A, B, C, D, W[12]); + SHA2_64_F(D, E, F, G, H, A, B, C, W[13]); + SHA2_64_F(C, D, E, F, G, H, A, B, W[14]); + SHA2_64_F(B, C, D, E, F, G, H, A, W[15]); + + digest0 += A; + digest1 += B; + digest2 += C; + digest3 += D; + digest4 += E; + digest5 += F; + digest6 += G; + digest7 += H; + + A = digest0; + B = digest1; + C = digest2; + D = digest3; + E = digest4; + F = digest5; + G = digest6; + H = digest7; + } + + // Could be optimized a bit by interleaving the registers, reducing store pressure + // but probably not worth bothering with + _mm_mask_storeu_epi64(&digest[0], 0b01, digest0.raw()); // NOLINT(*-container-data-pointer) + _mm_mask_storeu_epi64(&digest[1], 0b01, digest1.raw()); + _mm_mask_storeu_epi64(&digest[2], 0b01, digest2.raw()); + _mm_mask_storeu_epi64(&digest[3], 0b01, digest3.raw()); + _mm_mask_storeu_epi64(&digest[4], 0b01, digest4.raw()); + _mm_mask_storeu_epi64(&digest[5], 0b01, digest5.raw()); + _mm_mask_storeu_epi64(&digest[6], 0b01, digest6.raw()); + _mm_mask_storeu_epi64(&digest[7], 0b01, digest7.raw()); +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64_bmi2/info.txt botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_bmi2/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64_bmi2/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_bmi2/info.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ - -SHA2_64_BMI2 -> 20190117 - - - -name -> "SHA-512 BMI2" -brief -> "SHA-512 using BMI2 instructions" - - - -bmi2 - - -# Needs 64-bit registers to be useful - -x86_64 - diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64_bmi2/sha2_64_bmi2.cpp botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_bmi2/sha2_64_bmi2.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64_bmi2/sha2_64_bmi2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_bmi2/sha2_64_bmi2.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,124 +0,0 @@ -/* -* (C) 2019 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include -#include -#include -#include - -namespace Botan { - -void SHA_512::compress_digest_bmi2(digest_type& digest, std::span input, size_t blocks) { - uint64_t A = digest[0], B = digest[1], C = digest[2], D = digest[3], E = digest[4], F = digest[5], G = digest[6], - H = digest[7]; - - std::array W; - - BufferSlicer in(input); - - for(size_t i = 0; i != blocks; ++i) { - load_be(W, in.take()); - - // clang-format off - - SHA2_64_F(A, B, C, D, E, F, G, H, W[ 0], W[14], W[ 9], W[ 1], 0x428A2F98D728AE22); - SHA2_64_F(H, A, B, C, D, E, F, G, W[ 1], W[15], W[10], W[ 2], 0x7137449123EF65CD); - SHA2_64_F(G, H, A, B, C, D, E, F, W[ 2], W[ 0], W[11], W[ 3], 0xB5C0FBCFEC4D3B2F); - SHA2_64_F(F, G, H, A, B, C, D, E, W[ 3], W[ 1], W[12], W[ 4], 0xE9B5DBA58189DBBC); - SHA2_64_F(E, F, G, H, A, B, C, D, W[ 4], W[ 2], W[13], W[ 5], 0x3956C25BF348B538); - SHA2_64_F(D, E, F, G, H, A, B, C, W[ 5], W[ 3], W[14], W[ 6], 0x59F111F1B605D019); - SHA2_64_F(C, D, E, F, G, H, A, B, W[ 6], W[ 4], W[15], W[ 7], 0x923F82A4AF194F9B); - SHA2_64_F(B, C, D, E, F, G, H, A, W[ 7], W[ 5], W[ 0], W[ 8], 0xAB1C5ED5DA6D8118); - SHA2_64_F(A, B, C, D, E, F, G, H, W[ 8], W[ 6], W[ 1], W[ 9], 0xD807AA98A3030242); - SHA2_64_F(H, A, B, C, D, E, F, G, W[ 9], W[ 7], W[ 2], W[10], 0x12835B0145706FBE); - SHA2_64_F(G, H, A, B, C, D, E, F, W[10], W[ 8], W[ 3], W[11], 0x243185BE4EE4B28C); - SHA2_64_F(F, G, H, A, B, C, D, E, W[11], W[ 9], W[ 4], W[12], 0x550C7DC3D5FFB4E2); - SHA2_64_F(E, F, G, H, A, B, C, D, W[12], W[10], W[ 5], W[13], 0x72BE5D74F27B896F); - SHA2_64_F(D, E, F, G, H, A, B, C, W[13], W[11], W[ 6], W[14], 0x80DEB1FE3B1696B1); - SHA2_64_F(C, D, E, F, G, H, A, B, W[14], W[12], W[ 7], W[15], 0x9BDC06A725C71235); - SHA2_64_F(B, C, D, E, F, G, H, A, W[15], W[13], W[ 8], W[ 0], 0xC19BF174CF692694); - SHA2_64_F(A, B, C, D, E, F, G, H, W[ 0], W[14], W[ 9], W[ 1], 0xE49B69C19EF14AD2); - SHA2_64_F(H, A, B, C, D, E, F, G, W[ 1], W[15], W[10], W[ 2], 0xEFBE4786384F25E3); - SHA2_64_F(G, H, A, B, C, D, E, F, W[ 2], W[ 0], W[11], W[ 3], 0x0FC19DC68B8CD5B5); - SHA2_64_F(F, G, H, A, B, C, D, E, W[ 3], W[ 1], W[12], W[ 4], 0x240CA1CC77AC9C65); - SHA2_64_F(E, F, G, H, A, B, C, D, W[ 4], W[ 2], W[13], W[ 5], 0x2DE92C6F592B0275); - SHA2_64_F(D, E, F, G, H, A, B, C, W[ 5], W[ 3], W[14], W[ 6], 0x4A7484AA6EA6E483); - SHA2_64_F(C, D, E, F, G, H, A, B, W[ 6], W[ 4], W[15], W[ 7], 0x5CB0A9DCBD41FBD4); - SHA2_64_F(B, C, D, E, F, G, H, A, W[ 7], W[ 5], W[ 0], W[ 8], 0x76F988DA831153B5); - SHA2_64_F(A, B, C, D, E, F, G, H, W[ 8], W[ 6], W[ 1], W[ 9], 0x983E5152EE66DFAB); - SHA2_64_F(H, A, B, C, D, E, F, G, W[ 9], W[ 7], W[ 2], W[10], 0xA831C66D2DB43210); - SHA2_64_F(G, H, A, B, C, D, E, F, W[10], W[ 8], W[ 3], W[11], 0xB00327C898FB213F); - SHA2_64_F(F, G, H, A, B, C, D, E, W[11], W[ 9], W[ 4], W[12], 0xBF597FC7BEEF0EE4); - SHA2_64_F(E, F, G, H, A, B, C, D, W[12], W[10], W[ 5], W[13], 0xC6E00BF33DA88FC2); - SHA2_64_F(D, E, F, G, H, A, B, C, W[13], W[11], W[ 6], W[14], 0xD5A79147930AA725); - SHA2_64_F(C, D, E, F, G, H, A, B, W[14], W[12], W[ 7], W[15], 0x06CA6351E003826F); - SHA2_64_F(B, C, D, E, F, G, H, A, W[15], W[13], W[ 8], W[ 0], 0x142929670A0E6E70); - SHA2_64_F(A, B, C, D, E, F, G, H, W[ 0], W[14], W[ 9], W[ 1], 0x27B70A8546D22FFC); - SHA2_64_F(H, A, B, C, D, E, F, G, W[ 1], W[15], W[10], W[ 2], 0x2E1B21385C26C926); - SHA2_64_F(G, H, A, B, C, D, E, F, W[ 2], W[ 0], W[11], W[ 3], 0x4D2C6DFC5AC42AED); - SHA2_64_F(F, G, H, A, B, C, D, E, W[ 3], W[ 1], W[12], W[ 4], 0x53380D139D95B3DF); - SHA2_64_F(E, F, G, H, A, B, C, D, W[ 4], W[ 2], W[13], W[ 5], 0x650A73548BAF63DE); - SHA2_64_F(D, E, F, G, H, A, B, C, W[ 5], W[ 3], W[14], W[ 6], 0x766A0ABB3C77B2A8); - SHA2_64_F(C, D, E, F, G, H, A, B, W[ 6], W[ 4], W[15], W[ 7], 0x81C2C92E47EDAEE6); - SHA2_64_F(B, C, D, E, F, G, H, A, W[ 7], W[ 5], W[ 0], W[ 8], 0x92722C851482353B); - SHA2_64_F(A, B, C, D, E, F, G, H, W[ 8], W[ 6], W[ 1], W[ 9], 0xA2BFE8A14CF10364); - SHA2_64_F(H, A, B, C, D, E, F, G, W[ 9], W[ 7], W[ 2], W[10], 0xA81A664BBC423001); - SHA2_64_F(G, H, A, B, C, D, E, F, W[10], W[ 8], W[ 3], W[11], 0xC24B8B70D0F89791); - SHA2_64_F(F, G, H, A, B, C, D, E, W[11], W[ 9], W[ 4], W[12], 0xC76C51A30654BE30); - SHA2_64_F(E, F, G, H, A, B, C, D, W[12], W[10], W[ 5], W[13], 0xD192E819D6EF5218); - SHA2_64_F(D, E, F, G, H, A, B, C, W[13], W[11], W[ 6], W[14], 0xD69906245565A910); - SHA2_64_F(C, D, E, F, G, H, A, B, W[14], W[12], W[ 7], W[15], 0xF40E35855771202A); - SHA2_64_F(B, C, D, E, F, G, H, A, W[15], W[13], W[ 8], W[ 0], 0x106AA07032BBD1B8); - SHA2_64_F(A, B, C, D, E, F, G, H, W[ 0], W[14], W[ 9], W[ 1], 0x19A4C116B8D2D0C8); - SHA2_64_F(H, A, B, C, D, E, F, G, W[ 1], W[15], W[10], W[ 2], 0x1E376C085141AB53); - SHA2_64_F(G, H, A, B, C, D, E, F, W[ 2], W[ 0], W[11], W[ 3], 0x2748774CDF8EEB99); - SHA2_64_F(F, G, H, A, B, C, D, E, W[ 3], W[ 1], W[12], W[ 4], 0x34B0BCB5E19B48A8); - SHA2_64_F(E, F, G, H, A, B, C, D, W[ 4], W[ 2], W[13], W[ 5], 0x391C0CB3C5C95A63); - SHA2_64_F(D, E, F, G, H, A, B, C, W[ 5], W[ 3], W[14], W[ 6], 0x4ED8AA4AE3418ACB); - SHA2_64_F(C, D, E, F, G, H, A, B, W[ 6], W[ 4], W[15], W[ 7], 0x5B9CCA4F7763E373); - SHA2_64_F(B, C, D, E, F, G, H, A, W[ 7], W[ 5], W[ 0], W[ 8], 0x682E6FF3D6B2B8A3); - SHA2_64_F(A, B, C, D, E, F, G, H, W[ 8], W[ 6], W[ 1], W[ 9], 0x748F82EE5DEFB2FC); - SHA2_64_F(H, A, B, C, D, E, F, G, W[ 9], W[ 7], W[ 2], W[10], 0x78A5636F43172F60); - SHA2_64_F(G, H, A, B, C, D, E, F, W[10], W[ 8], W[ 3], W[11], 0x84C87814A1F0AB72); - SHA2_64_F(F, G, H, A, B, C, D, E, W[11], W[ 9], W[ 4], W[12], 0x8CC702081A6439EC); - SHA2_64_F(E, F, G, H, A, B, C, D, W[12], W[10], W[ 5], W[13], 0x90BEFFFA23631E28); - SHA2_64_F(D, E, F, G, H, A, B, C, W[13], W[11], W[ 6], W[14], 0xA4506CEBDE82BDE9); - SHA2_64_F(C, D, E, F, G, H, A, B, W[14], W[12], W[ 7], W[15], 0xBEF9A3F7B2C67915); - SHA2_64_F(B, C, D, E, F, G, H, A, W[15], W[13], W[ 8], W[ 0], 0xC67178F2E372532B); - SHA2_64_F(A, B, C, D, E, F, G, H, W[ 0], W[14], W[ 9], W[ 1], 0xCA273ECEEA26619C); - SHA2_64_F(H, A, B, C, D, E, F, G, W[ 1], W[15], W[10], W[ 2], 0xD186B8C721C0C207); - SHA2_64_F(G, H, A, B, C, D, E, F, W[ 2], W[ 0], W[11], W[ 3], 0xEADA7DD6CDE0EB1E); - SHA2_64_F(F, G, H, A, B, C, D, E, W[ 3], W[ 1], W[12], W[ 4], 0xF57D4F7FEE6ED178); - SHA2_64_F(E, F, G, H, A, B, C, D, W[ 4], W[ 2], W[13], W[ 5], 0x06F067AA72176FBA); - SHA2_64_F(D, E, F, G, H, A, B, C, W[ 5], W[ 3], W[14], W[ 6], 0x0A637DC5A2C898A6); - SHA2_64_F(C, D, E, F, G, H, A, B, W[ 6], W[ 4], W[15], W[ 7], 0x113F9804BEF90DAE); - SHA2_64_F(B, C, D, E, F, G, H, A, W[ 7], W[ 5], W[ 0], W[ 8], 0x1B710B35131C471B); - SHA2_64_F(A, B, C, D, E, F, G, H, W[ 8], W[ 6], W[ 1], W[ 9], 0x28DB77F523047D84); - SHA2_64_F(H, A, B, C, D, E, F, G, W[ 9], W[ 7], W[ 2], W[10], 0x32CAAB7B40C72493); - SHA2_64_F(G, H, A, B, C, D, E, F, W[10], W[ 8], W[ 3], W[11], 0x3C9EBE0A15C9BEBC); - SHA2_64_F(F, G, H, A, B, C, D, E, W[11], W[ 9], W[ 4], W[12], 0x431D67C49C100D4C); - SHA2_64_F(E, F, G, H, A, B, C, D, W[12], W[10], W[ 5], W[13], 0x4CC5D4BECB3E42B6); - SHA2_64_F(D, E, F, G, H, A, B, C, W[13], W[11], W[ 6], W[14], 0x597F299CFC657E2A); - SHA2_64_F(C, D, E, F, G, H, A, B, W[14], W[12], W[ 7], W[15], 0x5FCB6FAB3AD6FAEC); - SHA2_64_F(B, C, D, E, F, G, H, A, W[15], W[13], W[ 8], W[ 0], 0x6C44198C4A475817); - - // clang-format on - - A = (digest[0] += A); - B = (digest[1] += B); - C = (digest[2] += C); - D = (digest[3] += D); - E = (digest[4] += E); - F = (digest[5] += F); - G = (digest[6] += G); - H = (digest[7] += H); - } -} - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64_f.h botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_f.h --- botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64_f.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_f.h 2026-05-07 01:38:28.000000000 +0000 @@ -29,14 +29,20 @@ uint64_t M3, uint64_t M4, uint64_t magic) { - const uint64_t E_rho = rho<14, 18, 41>(E); - const uint64_t A_rho = rho<28, 34, 39>(A); - const uint64_t M2_sigma = sigma<19, 61, 6>(M2); - const uint64_t M4_sigma = sigma<1, 8, 7>(M4); - H += magic + E_rho + choose(E, F, G) + M1; + H += magic + rho<14, 18, 41>(E) + choose(E, F, G) + M1; D += H; - H += A_rho + majority(A, B, C); - M1 += M2_sigma + M3 + M4_sigma; + H += rho<28, 34, 39>(A) + majority(A, B, C); + M1 += sigma<19, 61, 6>(M2) + M3 + sigma<1, 8, 7>(M4); +} + +/* +* SHA-512 F1 Function (No Message Expansion) +*/ +BOTAN_FORCE_INLINE void SHA2_64_F( + uint64_t A, uint64_t B, uint64_t C, uint64_t& D, uint64_t E, uint64_t F, uint64_t G, uint64_t& H, uint64_t M) { + H += rho<14, 18, 41>(E) + choose(E, F, G) + M; + D += H; + H += rho<28, 34, 39>(A) + majority(A, B, C); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64_x86/info.txt botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_x86/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64_x86/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_x86/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,26 @@ + +SHA2_64_X86 -> 20250310 + + + +name -> "SHA-512 SHA-NI" +brief -> "SHA-512 using x86 instructions" + + + +sha512 + + + +x86_64 + + + +cpuid + + + +gcc:14 +clang:17 +msvc + diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64_x86/sha2_64_x86.cpp botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_x86/sha2_64_x86.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sha2_64/sha2_64_x86/sha2_64_x86.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha2_64/sha2_64_x86/sha2_64_x86.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,125 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan { + +namespace { + +// NOLINTBEGIN(portability-simd-intrinsics) + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_SHA512 void sha512_msg_expand(__m256i& m0, __m256i& m1, __m256i& m2, __m256i& m3) { + m3 = _mm256_sha512msg1_epi64(m3, _mm256_extracti128_si256(m0, 0)); + m2 = _mm256_add_epi64(m2, _mm256_permute4x64_epi64(_mm256_blend_epi32(m0, m1, 3), 0b00111001)); + m2 = _mm256_sha512msg2_epi64(m2, m1); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_SHA512 void sha512_4rounds(__m256i& state0, + __m256i& state1, + const __m256i msg, + const __m256i K) { + const auto tmp = _mm256_add_epi64(msg, K); + state0 = _mm256_sha512rnds2_epi64(state0, state1, _mm256_extracti128_si256(tmp, 0)); + state1 = _mm256_sha512rnds2_epi64(state1, state0, _mm256_extracti128_si256(tmp, 1)); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2 void permute_state(__m256i& state0, __m256i& state1) { + state0 = _mm256_shuffle_epi32(state0, 0b01001110); + state1 = _mm256_shuffle_epi32(state1, 0b01001110); + auto statet = state0; + state0 = _mm256_permute2x128_si256(state0, state1, 0x13); + state1 = _mm256_permute2x128_si256(statet, state1, 0x02); +} + +// NOLINTEND(portability-simd-intrinsics) + +} // namespace + +BOTAN_FN_ISA_SHA512 +void SHA_512::compress_digest_x86(digest_type& digest, std::span input, size_t blocks) { + alignas(128) static const uint64_t K[] = { + 0x428A2F98D728AE22, 0x7137449123EF65CD, 0xB5C0FBCFEC4D3B2F, 0xE9B5DBA58189DBBC, 0x3956C25BF348B538, + 0x59F111F1B605D019, 0x923F82A4AF194F9B, 0xAB1C5ED5DA6D8118, 0xD807AA98A3030242, 0x12835B0145706FBE, + 0x243185BE4EE4B28C, 0x550C7DC3D5FFB4E2, 0x72BE5D74F27B896F, 0x80DEB1FE3B1696B1, 0x9BDC06A725C71235, + 0xC19BF174CF692694, 0xE49B69C19EF14AD2, 0xEFBE4786384F25E3, 0x0FC19DC68B8CD5B5, 0x240CA1CC77AC9C65, + 0x2DE92C6F592B0275, 0x4A7484AA6EA6E483, 0x5CB0A9DCBD41FBD4, 0x76F988DA831153B5, 0x983E5152EE66DFAB, + 0xA831C66D2DB43210, 0xB00327C898FB213F, 0xBF597FC7BEEF0EE4, 0xC6E00BF33DA88FC2, 0xD5A79147930AA725, + 0x06CA6351E003826F, 0x142929670A0E6E70, 0x27B70A8546D22FFC, 0x2E1B21385C26C926, 0x4D2C6DFC5AC42AED, + 0x53380D139D95B3DF, 0x650A73548BAF63DE, 0x766A0ABB3C77B2A8, 0x81C2C92E47EDAEE6, 0x92722C851482353B, + 0xA2BFE8A14CF10364, 0xA81A664BBC423001, 0xC24B8B70D0F89791, 0xC76C51A30654BE30, 0xD192E819D6EF5218, + 0xD69906245565A910, 0xF40E35855771202A, 0x106AA07032BBD1B8, 0x19A4C116B8D2D0C8, 0x1E376C085141AB53, + 0x2748774CDF8EEB99, 0x34B0BCB5E19B48A8, 0x391C0CB3C5C95A63, 0x4ED8AA4AE3418ACB, 0x5B9CCA4F7763E373, + 0x682E6FF3D6B2B8A3, 0x748F82EE5DEFB2FC, 0x78A5636F43172F60, 0x84C87814A1F0AB72, 0x8CC702081A6439EC, + 0x90BEFFFA23631E28, 0xA4506CEBDE82BDE9, 0xBEF9A3F7B2C67915, 0xC67178F2E372532B, 0xCA273ECEEA26619C, + 0xD186B8C721C0C207, 0xEADA7DD6CDE0EB1E, 0xF57D4F7FEE6ED178, 0x06F067AA72176FBA, 0x0A637DC5A2C898A6, + 0x113F9804BEF90DAE, 0x1B710B35131C471B, 0x28DB77F523047D84, 0x32CAAB7B40C72493, 0x3C9EBE0A15C9BEBC, + 0x431D67C49C100D4C, 0x4CC5D4BECB3E42B6, 0x597F299CFC657E2A, 0x5FCB6FAB3AD6FAEC, 0x6C44198C4A475817, + }; + + // NOLINTBEGIN(portability-simd-intrinsics) TODO Use SIMD_4x64 here + + const __m256i* K_mm = reinterpret_cast(K); + + const __m256i bswap_mask = + _mm256_set_epi64x(0x08090a0b0c0d0e0f, 0x0001020304050607, 0x08090a0b0c0d0e0f, 0x0001020304050607); + + __m256i* digest_mm = reinterpret_cast<__m256i*>(digest.data()); + const __m256i* input_mm = reinterpret_cast(input.data()); + + auto state0 = _mm256_loadu_si256(digest_mm); + auto state1 = _mm256_loadu_si256(digest_mm + 1); + + permute_state(state0, state1); + + for(size_t i = 0; i != blocks; ++i) { + const auto state0_save = state0; + const auto state1_save = state1; + + auto m0 = _mm256_shuffle_epi8(_mm256_loadu_si256(input_mm + 0), bswap_mask); + auto m1 = _mm256_shuffle_epi8(_mm256_loadu_si256(input_mm + 1), bswap_mask); + auto m2 = _mm256_shuffle_epi8(_mm256_loadu_si256(input_mm + 2), bswap_mask); + auto m3 = _mm256_shuffle_epi8(_mm256_loadu_si256(input_mm + 3), bswap_mask); + + sha512_4rounds(state0, state1, m0, _mm256_load_si256(&K_mm[0])); + sha512_4rounds(state0, state1, m1, _mm256_load_si256(&K_mm[1])); + m0 = _mm256_sha512msg1_epi64(m0, _mm256_extracti128_si256(m1, 0)); + + for(size_t r = 2; r != 18; r += 4) { + sha512_4rounds(state0, state1, m2, _mm256_load_si256(&K_mm[r + 0])); + sha512_msg_expand(m2, m3, m0, m1); + + sha512_4rounds(state0, state1, m3, _mm256_load_si256(&K_mm[r + 1])); + sha512_msg_expand(m3, m0, m1, m2); + + sha512_4rounds(state0, state1, m0, _mm256_load_si256(&K_mm[r + 2])); + sha512_msg_expand(m0, m1, m2, m3); + + sha512_4rounds(state0, state1, m1, _mm256_load_si256(&K_mm[r + 3])); + sha512_msg_expand(m1, m2, m3, m0); + } + + sha512_4rounds(state0, state1, m2, _mm256_load_si256(&K_mm[18])); + sha512_4rounds(state0, state1, m3, _mm256_load_si256(&K_mm[19])); + + state0 = _mm256_add_epi64(state0, state0_save); + state1 = _mm256_add_epi64(state1, state1_save); + + input_mm += 4; + } + + permute_state(state0, state1); + + _mm256_storeu_si256(digest_mm, state0); + _mm256_storeu_si256(digest_mm + 1, state1); + + // NOLINTEND(portability-simd-intrinsics) +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sha3/sha3.cpp botan3-3.12.0+dfsg/src/lib/hash/sha3/sha3.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sha3/sha3.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sha3/sha3.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,14 +8,13 @@ #include #include -#include #include #include -#include namespace Botan { -SHA_3::SHA_3(size_t output_bits) : m_keccak(2 * output_bits, 2, 2), m_output_length(output_bits / 8) { +SHA_3::SHA_3(size_t output_bits) : + m_keccak({.capacity_bits = output_bits * 2, .padding = KeccakPadding::sha3()}), m_output_length(output_bits / 8) { // We only support the parameters for SHA-3 in this constructor if(output_bits != 224 && output_bits != 256 && output_bits != 384 && output_bits != 512) { diff -Nru botan3-3.7.1+dfsg/src/lib/hash/shake/shake.cpp botan3-3.12.0+dfsg/src/lib/hash/shake/shake.cpp --- botan3-3.7.1+dfsg/src/lib/hash/shake/shake.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/shake/shake.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,8 +12,9 @@ namespace Botan { -SHAKE_128::SHAKE_128(size_t output_bits) : m_keccak(256, 0xF, 4), m_output_bits(output_bits) { - if(output_bits % 8 != 0) { +SHAKE_128::SHAKE_128(size_t output_bits) : + m_keccak({.capacity_bits = 256, .padding = KeccakPadding::shake()}), m_output_bits(output_bits) { + if(output_bits == 0 || output_bits % 8 != 0) { throw Invalid_Argument(fmt("SHAKE_128: Invalid output length {}", output_bits)); } } @@ -40,8 +41,9 @@ clear(); } -SHAKE_256::SHAKE_256(size_t output_bits) : m_keccak(512, 0xF, 4), m_output_bits(output_bits) { - if(output_bits % 8 != 0) { +SHAKE_256::SHAKE_256(size_t output_bits) : + m_keccak({.capacity_bits = 512, .padding = KeccakPadding::shake()}), m_output_bits(output_bits) { + if(output_bits == 0 || output_bits % 8 != 0) { throw Invalid_Argument(fmt("SHAKE_256: Invalid output length {}", output_bits)); } } diff -Nru botan3-3.7.1+dfsg/src/lib/hash/skein/skein_512.cpp botan3-3.12.0+dfsg/src/lib/hash/skein/skein_512.cpp --- botan3-3.7.1+dfsg/src/lib/hash/skein/skein_512.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/skein/skein_512.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include namespace Botan { @@ -70,7 +70,7 @@ store_le(uint32_t(m_output_bits), config_str + 8); reset_tweak(SKEIN_CONFIG, true); - ubi_512(config_str, sizeof(config_str)); + ubi_512(std::span{config_str}); if(!m_personalization.empty()) { /* @@ -82,24 +82,24 @@ throw Invalid_Argument("Skein personalization must be less than 64 bytes"); } - const uint8_t* bits = cast_char_ptr_to_uint8(m_personalization.data()); reset_tweak(SKEIN_PERSONALIZATION, true); - ubi_512(bits, m_personalization.length()); + ubi_512(as_span_of_bytes(m_personalization)); } reset_tweak(SKEIN_MSG, false); } -void Skein_512::ubi_512(const uint8_t msg[], size_t msg_len) { +void Skein_512::ubi_512(std::span msg) { secure_vector M(8); + /* NOLINTNEXTLINE(*-avoid-do-while) */ do { - const size_t to_proc = std::min(msg_len, 64); + const size_t to_proc = std::min(msg.size(), 64); m_T[0] += to_proc; - load_le(M.data(), msg, to_proc / 8); + load_le(M.data(), msg.data(), to_proc / 8); - if(to_proc % 8) { + if(to_proc % 8 > 0) { for(size_t j = 0; j != to_proc % 8; ++j) { M[to_proc / 8] |= static_cast(msg[8 * (to_proc / 8) + j]) << (8 * j); } @@ -110,9 +110,8 @@ // clear first flag if set m_T[1] &= ~(static_cast(1) << 62); - msg_len -= to_proc; - msg += to_proc; - } while(msg_len); + msg = msg.subspan(to_proc); + } while(!msg.empty()); } void Skein_512::add_data(std::span input) { @@ -139,10 +138,10 @@ m_buffer.fill_up_with_zeros(); ubi_512(m_buffer.consume().data(), pos); - const uint8_t counter[8] = {0}; + std::array counter{}; // all zero reset_tweak(SKEIN_OUTPUT, true); - ubi_512(counter, sizeof(counter)); + ubi_512(counter); copy_out_le(out.first(m_output_bits / 8), m_threefish->m_K); diff -Nru botan3-3.7.1+dfsg/src/lib/hash/skein/skein_512.h botan3-3.12.0+dfsg/src/lib/hash/skein/skein_512.h --- botan3-3.7.1+dfsg/src/lib/hash/skein/skein_512.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/skein/skein_512.h 2026-05-07 01:38:28.000000000 +0000 @@ -13,6 +13,7 @@ #include #include +#include #include namespace Botan { @@ -27,7 +28,7 @@ * @param personalization is a string that will parameterize the * hash output */ - Skein_512(size_t output_bits = 512, std::string_view personalization = ""); + explicit Skein_512(size_t output_bits = 512, std::string_view personalization = ""); size_t hash_block_size() const override { return 64; } @@ -39,7 +40,7 @@ void clear() override; private: - enum type_code { + enum type_code : uint8_t /* NOLINT(*-use-enum-class) */ { SKEIN_KEY = 0, SKEIN_CONFIG = 4, SKEIN_PERSONALIZATION = 8, @@ -53,7 +54,9 @@ void add_data(std::span input) override; void final_result(std::span out) override; - void ubi_512(const uint8_t msg[], size_t msg_len); + void ubi_512(std::span msg); + + void ubi_512(const uint8_t msg[], size_t length) { ubi_512({msg, length}); } void initial_block(); void reset_tweak(type_code type, bool is_final); diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sm3/sm3.cpp botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sm3/sm3.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,80 +8,47 @@ #include -#include +#include #include -#include -#include +#include -namespace Botan { - -namespace { - -inline uint32_t P0(uint32_t X) { - return X ^ rotl<9>(X) ^ rotl<17>(X); -} - -inline void R1(uint32_t A, - uint32_t& B, - uint32_t C, - uint32_t& D, - uint32_t E, - uint32_t& F, - uint32_t G, - uint32_t& H, - uint32_t TJ, - uint32_t Wi, - uint32_t Wj) { - const uint32_t A12 = rotl<12>(A); - const uint32_t SS1 = rotl<7>(A12 + E + TJ); - const uint32_t TT1 = (A ^ B ^ C) + D + (SS1 ^ A12) + Wj; - const uint32_t TT2 = (E ^ F ^ G) + H + SS1 + Wi; - - B = rotl<9>(B); - D = TT1; - F = rotl<19>(F); - H = P0(TT2); -} - -inline void R2(uint32_t A, - uint32_t& B, - uint32_t C, - uint32_t& D, - uint32_t E, - uint32_t& F, - uint32_t G, - uint32_t& H, - uint32_t TJ, - uint32_t Wi, - uint32_t Wj) { - const uint32_t A12 = rotl<12>(A); - const uint32_t SS1 = rotl<7>(A12 + E + TJ); - const uint32_t TT1 = majority(A, B, C) + D + (SS1 ^ A12) + Wj; - const uint32_t TT2 = choose(E, F, G) + H + SS1 + Wi; - - B = rotl<9>(B); - D = TT1; - F = rotl<19>(F); - H = P0(TT2); -} - -inline uint32_t P1(uint32_t X) { - return X ^ rotl<15>(X) ^ rotl<23>(X); -} +#if defined(BOTAN_HAS_CPUID) + #include +#endif -inline uint32_t SM3_E(uint32_t W0, uint32_t W7, uint32_t W13, uint32_t W3, uint32_t W10) { - return P1(W0 ^ W7 ^ rotl<15>(W13)) ^ rotl<7>(W3) ^ W10; -} - -} // namespace +namespace Botan { /* * SM3 Compression Function */ void SM3::compress_n(digest_type& digest, std::span input, size_t blocks) { - uint32_t A = digest[0], B = digest[1], C = digest[2], D = digest[3], E = digest[4], F = digest[5], G = digest[6], - H = digest[7]; - std::array W; +#if defined(BOTAN_HAS_SM3_ARMV8) + if(CPUID::has(CPUID::Feature::SM3)) { + return compress_digest_armv8(digest, input, blocks); + } +#endif + +#if defined(BOTAN_HAS_SM3_X86) + if(CPUID::has(CPUID::Feature::SM3, CPUID::Feature::AVX2)) { + return compress_digest_x86(digest, input, blocks); + } +#endif + +#if defined(BOTAN_HAS_SM3_X86_AVX2_BMI2) + if(CPUID::has(CPUID::Feature::AVX2, CPUID::Feature::BMI)) { + return compress_digest_x86_avx2(digest, input, blocks); + } +#endif + + uint32_t A = digest[0]; + uint32_t B = digest[1]; + uint32_t C = digest[2]; + uint32_t D = digest[3]; + uint32_t E = digest[4]; + uint32_t F = digest[5]; + uint32_t G = digest[6]; + uint32_t H = digest[7]; + std::array W{}; BufferSlicer in(input); @@ -90,122 +57,122 @@ // clang-format off - R1(A, B, C, D, E, F, G, H, 0x79CC4519, W[ 0], W[ 0] ^ W[ 4]); + R1(A, B, C, D, E, F, G, H, 0x79CC4519, W[ 0], W[ 4]); W[ 0] = SM3_E(W[ 0], W[ 7], W[13], W[ 3], W[10]); - R1(D, A, B, C, H, E, F, G, 0xF3988A32, W[ 1], W[ 1] ^ W[ 5]); + R1(D, A, B, C, H, E, F, G, 0xF3988A32, W[ 1], W[ 5]); W[ 1] = SM3_E(W[ 1], W[ 8], W[14], W[ 4], W[11]); - R1(C, D, A, B, G, H, E, F, 0xE7311465, W[ 2], W[ 2] ^ W[ 6]); + R1(C, D, A, B, G, H, E, F, 0xE7311465, W[ 2], W[ 6]); W[ 2] = SM3_E(W[ 2], W[ 9], W[15], W[ 5], W[12]); - R1(B, C, D, A, F, G, H, E, 0xCE6228CB, W[ 3], W[ 3] ^ W[ 7]); + R1(B, C, D, A, F, G, H, E, 0xCE6228CB, W[ 3], W[ 7]); W[ 3] = SM3_E(W[ 3], W[10], W[ 0], W[ 6], W[13]); - R1(A, B, C, D, E, F, G, H, 0x9CC45197, W[ 4], W[ 4] ^ W[ 8]); + R1(A, B, C, D, E, F, G, H, 0x9CC45197, W[ 4], W[ 8]); W[ 4] = SM3_E(W[ 4], W[11], W[ 1], W[ 7], W[14]); - R1(D, A, B, C, H, E, F, G, 0x3988A32F, W[ 5], W[ 5] ^ W[ 9]); + R1(D, A, B, C, H, E, F, G, 0x3988A32F, W[ 5], W[ 9]); W[ 5] = SM3_E(W[ 5], W[12], W[ 2], W[ 8], W[15]); - R1(C, D, A, B, G, H, E, F, 0x7311465E, W[ 6], W[ 6] ^ W[10]); + R1(C, D, A, B, G, H, E, F, 0x7311465E, W[ 6], W[10]); W[ 6] = SM3_E(W[ 6], W[13], W[ 3], W[ 9], W[ 0]); - R1(B, C, D, A, F, G, H, E, 0xE6228CBC, W[ 7], W[ 7] ^ W[11]); + R1(B, C, D, A, F, G, H, E, 0xE6228CBC, W[ 7], W[11]); W[ 7] = SM3_E(W[ 7], W[14], W[ 4], W[10], W[ 1]); - R1(A, B, C, D, E, F, G, H, 0xCC451979, W[ 8], W[ 8] ^ W[12]); + R1(A, B, C, D, E, F, G, H, 0xCC451979, W[ 8], W[12]); W[ 8] = SM3_E(W[ 8], W[15], W[ 5], W[11], W[ 2]); - R1(D, A, B, C, H, E, F, G, 0x988A32F3, W[ 9], W[ 9] ^ W[13]); + R1(D, A, B, C, H, E, F, G, 0x988A32F3, W[ 9], W[13]); W[ 9] = SM3_E(W[ 9], W[ 0], W[ 6], W[12], W[ 3]); - R1(C, D, A, B, G, H, E, F, 0x311465E7, W[10], W[10] ^ W[14]); + R1(C, D, A, B, G, H, E, F, 0x311465E7, W[10], W[14]); W[10] = SM3_E(W[10], W[ 1], W[ 7], W[13], W[ 4]); - R1(B, C, D, A, F, G, H, E, 0x6228CBCE, W[11], W[11] ^ W[15]); + R1(B, C, D, A, F, G, H, E, 0x6228CBCE, W[11], W[15]); W[11] = SM3_E(W[11], W[ 2], W[ 8], W[14], W[ 5]); - R1(A, B, C, D, E, F, G, H, 0xC451979C, W[12], W[12] ^ W[ 0]); + R1(A, B, C, D, E, F, G, H, 0xC451979C, W[12], W[ 0]); W[12] = SM3_E(W[12], W[ 3], W[ 9], W[15], W[ 6]); - R1(D, A, B, C, H, E, F, G, 0x88A32F39, W[13], W[13] ^ W[ 1]); + R1(D, A, B, C, H, E, F, G, 0x88A32F39, W[13], W[ 1]); W[13] = SM3_E(W[13], W[ 4], W[10], W[ 0], W[ 7]); - R1(C, D, A, B, G, H, E, F, 0x11465E73, W[14], W[14] ^ W[ 2]); + R1(C, D, A, B, G, H, E, F, 0x11465E73, W[14], W[ 2]); W[14] = SM3_E(W[14], W[ 5], W[11], W[ 1], W[ 8]); - R1(B, C, D, A, F, G, H, E, 0x228CBCE6, W[15], W[15] ^ W[ 3]); + R1(B, C, D, A, F, G, H, E, 0x228CBCE6, W[15], W[ 3]); W[15] = SM3_E(W[15], W[ 6], W[12], W[ 2], W[ 9]); - R2(A, B, C, D, E, F, G, H, 0x9D8A7A87, W[ 0], W[ 0] ^ W[ 4]); + R2(A, B, C, D, E, F, G, H, 0x9D8A7A87, W[ 0], W[ 4]); W[ 0] = SM3_E(W[ 0], W[ 7], W[13], W[ 3], W[10]); - R2(D, A, B, C, H, E, F, G, 0x3B14F50F, W[ 1], W[ 1] ^ W[ 5]); + R2(D, A, B, C, H, E, F, G, 0x3B14F50F, W[ 1], W[ 5]); W[ 1] = SM3_E(W[ 1], W[ 8], W[14], W[ 4], W[11]); - R2(C, D, A, B, G, H, E, F, 0x7629EA1E, W[ 2], W[ 2] ^ W[ 6]); + R2(C, D, A, B, G, H, E, F, 0x7629EA1E, W[ 2], W[ 6]); W[ 2] = SM3_E(W[ 2], W[ 9], W[15], W[ 5], W[12]); - R2(B, C, D, A, F, G, H, E, 0xEC53D43C, W[ 3], W[ 3] ^ W[ 7]); + R2(B, C, D, A, F, G, H, E, 0xEC53D43C, W[ 3], W[ 7]); W[ 3] = SM3_E(W[ 3], W[10], W[ 0], W[ 6], W[13]); - R2(A, B, C, D, E, F, G, H, 0xD8A7A879, W[ 4], W[ 4] ^ W[ 8]); + R2(A, B, C, D, E, F, G, H, 0xD8A7A879, W[ 4], W[ 8]); W[ 4] = SM3_E(W[ 4], W[11], W[ 1], W[ 7], W[14]); - R2(D, A, B, C, H, E, F, G, 0xB14F50F3, W[ 5], W[ 5] ^ W[ 9]); + R2(D, A, B, C, H, E, F, G, 0xB14F50F3, W[ 5], W[ 9]); W[ 5] = SM3_E(W[ 5], W[12], W[ 2], W[ 8], W[15]); - R2(C, D, A, B, G, H, E, F, 0x629EA1E7, W[ 6], W[ 6] ^ W[10]); + R2(C, D, A, B, G, H, E, F, 0x629EA1E7, W[ 6], W[10]); W[ 6] = SM3_E(W[ 6], W[13], W[ 3], W[ 9], W[ 0]); - R2(B, C, D, A, F, G, H, E, 0xC53D43CE, W[ 7], W[ 7] ^ W[11]); + R2(B, C, D, A, F, G, H, E, 0xC53D43CE, W[ 7], W[11]); W[ 7] = SM3_E(W[ 7], W[14], W[ 4], W[10], W[ 1]); - R2(A, B, C, D, E, F, G, H, 0x8A7A879D, W[ 8], W[ 8] ^ W[12]); + R2(A, B, C, D, E, F, G, H, 0x8A7A879D, W[ 8], W[12]); W[ 8] = SM3_E(W[ 8], W[15], W[ 5], W[11], W[ 2]); - R2(D, A, B, C, H, E, F, G, 0x14F50F3B, W[ 9], W[ 9] ^ W[13]); + R2(D, A, B, C, H, E, F, G, 0x14F50F3B, W[ 9], W[13]); W[ 9] = SM3_E(W[ 9], W[ 0], W[ 6], W[12], W[ 3]); - R2(C, D, A, B, G, H, E, F, 0x29EA1E76, W[10], W[10] ^ W[14]); + R2(C, D, A, B, G, H, E, F, 0x29EA1E76, W[10], W[14]); W[10] = SM3_E(W[10], W[ 1], W[ 7], W[13], W[ 4]); - R2(B, C, D, A, F, G, H, E, 0x53D43CEC, W[11], W[11] ^ W[15]); + R2(B, C, D, A, F, G, H, E, 0x53D43CEC, W[11], W[15]); W[11] = SM3_E(W[11], W[ 2], W[ 8], W[14], W[ 5]); - R2(A, B, C, D, E, F, G, H, 0xA7A879D8, W[12], W[12] ^ W[ 0]); + R2(A, B, C, D, E, F, G, H, 0xA7A879D8, W[12], W[ 0]); W[12] = SM3_E(W[12], W[ 3], W[ 9], W[15], W[ 6]); - R2(D, A, B, C, H, E, F, G, 0x4F50F3B1, W[13], W[13] ^ W[ 1]); + R2(D, A, B, C, H, E, F, G, 0x4F50F3B1, W[13], W[ 1]); W[13] = SM3_E(W[13], W[ 4], W[10], W[ 0], W[ 7]); - R2(C, D, A, B, G, H, E, F, 0x9EA1E762, W[14], W[14] ^ W[ 2]); + R2(C, D, A, B, G, H, E, F, 0x9EA1E762, W[14], W[ 2]); W[14] = SM3_E(W[14], W[ 5], W[11], W[ 1], W[ 8]); - R2(B, C, D, A, F, G, H, E, 0x3D43CEC5, W[15], W[15] ^ W[ 3]); + R2(B, C, D, A, F, G, H, E, 0x3D43CEC5, W[15], W[ 3]); W[15] = SM3_E(W[15], W[ 6], W[12], W[ 2], W[ 9]); - R2(A, B, C, D, E, F, G, H, 0x7A879D8A, W[ 0], W[ 0] ^ W[ 4]); + R2(A, B, C, D, E, F, G, H, 0x7A879D8A, W[ 0], W[ 4]); W[ 0] = SM3_E(W[ 0], W[ 7], W[13], W[ 3], W[10]); - R2(D, A, B, C, H, E, F, G, 0xF50F3B14, W[ 1], W[ 1] ^ W[ 5]); + R2(D, A, B, C, H, E, F, G, 0xF50F3B14, W[ 1], W[ 5]); W[ 1] = SM3_E(W[ 1], W[ 8], W[14], W[ 4], W[11]); - R2(C, D, A, B, G, H, E, F, 0xEA1E7629, W[ 2], W[ 2] ^ W[ 6]); + R2(C, D, A, B, G, H, E, F, 0xEA1E7629, W[ 2], W[ 6]); W[ 2] = SM3_E(W[ 2], W[ 9], W[15], W[ 5], W[12]); - R2(B, C, D, A, F, G, H, E, 0xD43CEC53, W[ 3], W[ 3] ^ W[ 7]); + R2(B, C, D, A, F, G, H, E, 0xD43CEC53, W[ 3], W[ 7]); W[ 3] = SM3_E(W[ 3], W[10], W[ 0], W[ 6], W[13]); - R2(A, B, C, D, E, F, G, H, 0xA879D8A7, W[ 4], W[ 4] ^ W[ 8]); + R2(A, B, C, D, E, F, G, H, 0xA879D8A7, W[ 4], W[ 8]); W[ 4] = SM3_E(W[ 4], W[11], W[ 1], W[ 7], W[14]); - R2(D, A, B, C, H, E, F, G, 0x50F3B14F, W[ 5], W[ 5] ^ W[ 9]); + R2(D, A, B, C, H, E, F, G, 0x50F3B14F, W[ 5], W[ 9]); W[ 5] = SM3_E(W[ 5], W[12], W[ 2], W[ 8], W[15]); - R2(C, D, A, B, G, H, E, F, 0xA1E7629E, W[ 6], W[ 6] ^ W[10]); + R2(C, D, A, B, G, H, E, F, 0xA1E7629E, W[ 6], W[10]); W[ 6] = SM3_E(W[ 6], W[13], W[ 3], W[ 9], W[ 0]); - R2(B, C, D, A, F, G, H, E, 0x43CEC53D, W[ 7], W[ 7] ^ W[11]); + R2(B, C, D, A, F, G, H, E, 0x43CEC53D, W[ 7], W[11]); W[ 7] = SM3_E(W[ 7], W[14], W[ 4], W[10], W[ 1]); - R2(A, B, C, D, E, F, G, H, 0x879D8A7A, W[ 8], W[ 8] ^ W[12]); + R2(A, B, C, D, E, F, G, H, 0x879D8A7A, W[ 8], W[12]); W[ 8] = SM3_E(W[ 8], W[15], W[ 5], W[11], W[ 2]); - R2(D, A, B, C, H, E, F, G, 0x0F3B14F5, W[ 9], W[ 9] ^ W[13]); + R2(D, A, B, C, H, E, F, G, 0x0F3B14F5, W[ 9], W[13]); W[ 9] = SM3_E(W[ 9], W[ 0], W[ 6], W[12], W[ 3]); - R2(C, D, A, B, G, H, E, F, 0x1E7629EA, W[10], W[10] ^ W[14]); + R2(C, D, A, B, G, H, E, F, 0x1E7629EA, W[10], W[14]); W[10] = SM3_E(W[10], W[ 1], W[ 7], W[13], W[ 4]); - R2(B, C, D, A, F, G, H, E, 0x3CEC53D4, W[11], W[11] ^ W[15]); + R2(B, C, D, A, F, G, H, E, 0x3CEC53D4, W[11], W[15]); W[11] = SM3_E(W[11], W[ 2], W[ 8], W[14], W[ 5]); - R2(A, B, C, D, E, F, G, H, 0x79D8A7A8, W[12], W[12] ^ W[ 0]); + R2(A, B, C, D, E, F, G, H, 0x79D8A7A8, W[12], W[ 0]); W[12] = SM3_E(W[12], W[ 3], W[ 9], W[15], W[ 6]); - R2(D, A, B, C, H, E, F, G, 0xF3B14F50, W[13], W[13] ^ W[ 1]); + R2(D, A, B, C, H, E, F, G, 0xF3B14F50, W[13], W[ 1]); W[13] = SM3_E(W[13], W[ 4], W[10], W[ 0], W[ 7]); - R2(C, D, A, B, G, H, E, F, 0xE7629EA1, W[14], W[14] ^ W[ 2]); + R2(C, D, A, B, G, H, E, F, 0xE7629EA1, W[14], W[ 2]); W[14] = SM3_E(W[14], W[ 5], W[11], W[ 1], W[ 8]); - R2(B, C, D, A, F, G, H, E, 0xCEC53D43, W[15], W[15] ^ W[ 3]); + R2(B, C, D, A, F, G, H, E, 0xCEC53D43, W[15], W[ 3]); W[15] = SM3_E(W[15], W[ 6], W[12], W[ 2], W[ 9]); - R2(A, B, C, D, E, F, G, H, 0x9D8A7A87, W[ 0], W[ 0] ^ W[ 4]); + R2(A, B, C, D, E, F, G, H, 0x9D8A7A87, W[ 0], W[ 4]); W[ 0] = SM3_E(W[ 0], W[ 7], W[13], W[ 3], W[10]); - R2(D, A, B, C, H, E, F, G, 0x3B14F50F, W[ 1], W[ 1] ^ W[ 5]); + R2(D, A, B, C, H, E, F, G, 0x3B14F50F, W[ 1], W[ 5]); W[ 1] = SM3_E(W[ 1], W[ 8], W[14], W[ 4], W[11]); - R2(C, D, A, B, G, H, E, F, 0x7629EA1E, W[ 2], W[ 2] ^ W[ 6]); + R2(C, D, A, B, G, H, E, F, 0x7629EA1E, W[ 2], W[ 6]); W[ 2] = SM3_E(W[ 2], W[ 9], W[15], W[ 5], W[12]); - R2(B, C, D, A, F, G, H, E, 0xEC53D43C, W[ 3], W[ 3] ^ W[ 7]); + R2(B, C, D, A, F, G, H, E, 0xEC53D43C, W[ 3], W[ 7]); W[ 3] = SM3_E(W[ 3], W[10], W[ 0], W[ 6], W[13]); - R2(A, B, C, D, E, F, G, H, 0xD8A7A879, W[ 4], W[ 4] ^ W[ 8]); - R2(D, A, B, C, H, E, F, G, 0xB14F50F3, W[ 5], W[ 5] ^ W[ 9]); - R2(C, D, A, B, G, H, E, F, 0x629EA1E7, W[ 6], W[ 6] ^ W[10]); - R2(B, C, D, A, F, G, H, E, 0xC53D43CE, W[ 7], W[ 7] ^ W[11]); - R2(A, B, C, D, E, F, G, H, 0x8A7A879D, W[ 8], W[ 8] ^ W[12]); - R2(D, A, B, C, H, E, F, G, 0x14F50F3B, W[ 9], W[ 9] ^ W[13]); - R2(C, D, A, B, G, H, E, F, 0x29EA1E76, W[10], W[10] ^ W[14]); - R2(B, C, D, A, F, G, H, E, 0x53D43CEC, W[11], W[11] ^ W[15]); - R2(A, B, C, D, E, F, G, H, 0xA7A879D8, W[12], W[12] ^ W[ 0]); - R2(D, A, B, C, H, E, F, G, 0x4F50F3B1, W[13], W[13] ^ W[ 1]); - R2(C, D, A, B, G, H, E, F, 0x9EA1E762, W[14], W[14] ^ W[ 2]); - R2(B, C, D, A, F, G, H, E, 0x3D43CEC5, W[15], W[15] ^ W[ 3]); + R2(A, B, C, D, E, F, G, H, 0xD8A7A879, W[ 4], W[ 8]); + R2(D, A, B, C, H, E, F, G, 0xB14F50F3, W[ 5], W[ 9]); + R2(C, D, A, B, G, H, E, F, 0x629EA1E7, W[ 6], W[10]); + R2(B, C, D, A, F, G, H, E, 0xC53D43CE, W[ 7], W[11]); + R2(A, B, C, D, E, F, G, H, 0x8A7A879D, W[ 8], W[12]); + R2(D, A, B, C, H, E, F, G, 0x14F50F3B, W[ 9], W[13]); + R2(C, D, A, B, G, H, E, F, 0x29EA1E76, W[10], W[14]); + R2(B, C, D, A, F, G, H, E, 0x53D43CEC, W[11], W[15]); + R2(A, B, C, D, E, F, G, H, 0xA7A879D8, W[12], W[ 0]); + R2(D, A, B, C, H, E, F, G, 0x4F50F3B1, W[13], W[ 1]); + R2(C, D, A, B, G, H, E, F, 0x9EA1E762, W[14], W[ 2]); + R2(B, C, D, A, F, G, H, E, 0x3D43CEC5, W[15], W[ 3]); // clang-format on @@ -241,4 +208,26 @@ m_md.final(output); } +std::string SM3::provider() const { +#if defined(BOTAN_HAS_SM3_ARMV8) + if(auto feat = CPUID::check(CPUID::Feature::SM3)) { + return *feat; + } +#endif + +#if defined(BOTAN_HAS_SM3_X86) + if(auto feat = CPUID::check(CPUID::Feature::SM3, CPUID::Feature::AVX2)) { + return *feat; + } +#endif + +#if defined(BOTAN_HAS_SM3_X86_AVX2_BMI2) + if(auto feat = CPUID::check(CPUID::Feature::AVX2, CPUID::Feature::BMI)) { + return *feat; + } +#endif + + return "base"; +} + } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sm3/sm3.h botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3.h --- botan3-3.7.1+dfsg/src/lib/hash/sm3/sm3.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3.h 2026-05-07 01:38:28.000000000 +0000 @@ -31,6 +31,8 @@ public: std::string name() const override { return "SM3"; } + std::string provider() const override; + size_t output_length() const override { return output_bytes; } size_t hash_block_size() const override { return block_bytes; } @@ -46,6 +48,18 @@ void final_result(std::span output) override; +#if defined(BOTAN_HAS_SM3_X86_AVX2_BMI2) + static void compress_digest_x86_avx2(digest_type& digest, std::span input, size_t blocks); +#endif + +#if defined(BOTAN_HAS_SM3_ARMV8) + static void compress_digest_armv8(digest_type& digest, std::span input, size_t blocks); +#endif + +#if defined(BOTAN_HAS_SM3_X86) + static void compress_digest_x86(digest_type& digest, std::span input, size_t blocks); +#endif + private: MerkleDamgard_Hash m_md; }; diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sm3/sm3_armv8/info.txt botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3_armv8/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/sm3/sm3_armv8/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3_armv8/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,16 @@ + +SM3_ARMV8 -> 20260314 + + + +name -> "SM3 ARMv8" +brief -> "SM3 using ARMv8 crypto instructions" + + + +armv8sm3 + + + +cpuid + diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sm3/sm3_armv8/sm3_armv8.cpp botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3_armv8/sm3_armv8.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sm3/sm3_armv8/sm3_armv8.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3_armv8/sm3_armv8.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,170 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan { + +namespace { + +// clang-format off +alignas(64) const uint32_t SM3_TJ[64] = { + 0x79CC4519, 0xF3988A32, 0xE7311465, 0xCE6228CB, + 0x9CC45197, 0x3988A32F, 0x7311465E, 0xE6228CBC, + 0xCC451979, 0x988A32F3, 0x311465E7, 0x6228CBCE, + 0xC451979C, 0x88A32F39, 0x11465E73, 0x228CBCE6, + 0x9D8A7A87, 0x3B14F50F, 0x7629EA1E, 0xEC53D43C, + 0xD8A7A879, 0xB14F50F3, 0x629EA1E7, 0xC53D43CE, + 0x8A7A879D, 0x14F50F3B, 0x29EA1E76, 0x53D43CEC, + 0xA7A879D8, 0x4F50F3B1, 0x9EA1E762, 0x3D43CEC5, + 0x7A879D8A, 0xF50F3B14, 0xEA1E7629, 0xD43CEC53, + 0xA879D8A7, 0x50F3B14F, 0xA1E7629E, 0x43CEC53D, + 0x879D8A7A, 0x0F3B14F5, 0x1E7629EA, 0x3CEC53D4, + 0x79D8A7A8, 0xF3B14F50, 0xE7629EA1, 0xCEC53D43, + 0x9D8A7A87, 0x3B14F50F, 0x7629EA1E, 0xEC53D43C, + 0xD8A7A879, 0xB14F50F3, 0x629EA1E7, 0xC53D43CE, + 0x8A7A879D, 0x14F50F3B, 0x29EA1E76, 0x53D43CEC, + 0xA7A879D8, 0x4F50F3B1, 0x9EA1E762, 0x3D43CEC5, +}; + +// clang-format on + +// The SM3 instructions expect the state words in reverse order (why??) +BOTAN_FORCE_INLINE BOTAN_FN_ISA_SM3 uint32x4_t sm3_reverse_words(uint32x4_t v) { + v = vrev64q_u32(v); + return vextq_u32(v, v, 2); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_SM3 uint32x4_t sm3_tj(size_t round) { + // vsm3ss1q expects the constant to be in the top word + return vsetq_lane_u32(SM3_TJ[round], vdupq_n_u32(0), 3); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_SM3 void sm3_x4_r1( + uint32x4_t& S0, uint32x4_t& S1, uint32x4_t w, uint32x4_t w_prime, size_t round) { + auto t = vsm3ss1q_u32(S0, S1, sm3_tj(round)); + S0 = vsm3tt1aq_u32(S0, t, w_prime, 0); + S1 = vsm3tt2aq_u32(S1, t, w, 0); + + t = vsm3ss1q_u32(S0, S1, sm3_tj(round + 1)); + S0 = vsm3tt1aq_u32(S0, t, w_prime, 1); + S1 = vsm3tt2aq_u32(S1, t, w, 1); + + t = vsm3ss1q_u32(S0, S1, sm3_tj(round + 2)); + S0 = vsm3tt1aq_u32(S0, t, w_prime, 2); + S1 = vsm3tt2aq_u32(S1, t, w, 2); + + t = vsm3ss1q_u32(S0, S1, sm3_tj(round + 3)); + S0 = vsm3tt1aq_u32(S0, t, w_prime, 3); + S1 = vsm3tt2aq_u32(S1, t, w, 3); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_SM3 void sm3_x4_r2( + uint32x4_t& S0, uint32x4_t& S1, uint32x4_t w, uint32x4_t w_prime, size_t round) { + auto t = vsm3ss1q_u32(S0, S1, sm3_tj(round)); + S0 = vsm3tt1bq_u32(S0, t, w_prime, 0); + S1 = vsm3tt2bq_u32(S1, t, w, 0); + + t = vsm3ss1q_u32(S0, S1, sm3_tj(round + 1)); + S0 = vsm3tt1bq_u32(S0, t, w_prime, 1); + S1 = vsm3tt2bq_u32(S1, t, w, 1); + + t = vsm3ss1q_u32(S0, S1, sm3_tj(round + 2)); + S0 = vsm3tt1bq_u32(S0, t, w_prime, 2); + S1 = vsm3tt2bq_u32(S1, t, w, 2); + + t = vsm3ss1q_u32(S0, S1, sm3_tj(round + 3)); + S0 = vsm3tt1bq_u32(S0, t, w_prime, 3); + S1 = vsm3tt2bq_u32(S1, t, w, 3); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_SM3 void sm3_msg_expand(uint32x4_t& w0, + const uint32x4_t& w1, + const uint32x4_t& w2, + const uint32x4_t& w3) { + const uint32x4_t w7_10 = vextq_u32(w1, w2, 3); + const uint32x4_t w3_6 = vextq_u32(w0, w1, 3); + const uint32x4_t w10_13 = vextq_u32(w2, w3, 2); + + uint32x4_t t = vsm3partw1q_u32(w0, w7_10, w3); + w0 = vsm3partw2q_u32(t, w10_13, w3_6); +} + +} // namespace + +void BOTAN_FN_ISA_SM3 SM3::compress_digest_armv8(digest_type& digest, std::span input, size_t blocks) { + uint32x4_t S0 = sm3_reverse_words(vld1q_u32(&digest[0])); // NOLINT(*-container-data-pointer) + uint32x4_t S1 = sm3_reverse_words(vld1q_u32(&digest[4])); + + const uint8_t* data = input.data(); + + while(blocks > 0) { + const uint32x4_t S0_save = S0; + const uint32x4_t S1_save = S1; + + uint32x4_t W0 = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(data))); + uint32x4_t W1 = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(data + 16))); + uint32x4_t W2 = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(data + 32))); + uint32x4_t W3 = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(data + 48))); + + data += block_bytes; + blocks -= 1; + + sm3_x4_r1(S0, S1, W0, veorq_u32(W0, W1), 0); + sm3_msg_expand(W0, W1, W2, W3); + + sm3_x4_r1(S0, S1, W1, veorq_u32(W1, W2), 4); + sm3_msg_expand(W1, W2, W3, W0); + + sm3_x4_r1(S0, S1, W2, veorq_u32(W2, W3), 8); + sm3_msg_expand(W2, W3, W0, W1); + + sm3_x4_r1(S0, S1, W3, veorq_u32(W3, W0), 12); + sm3_msg_expand(W3, W0, W1, W2); + + sm3_x4_r2(S0, S1, W0, veorq_u32(W0, W1), 16); + sm3_msg_expand(W0, W1, W2, W3); + + sm3_x4_r2(S0, S1, W1, veorq_u32(W1, W2), 20); + sm3_msg_expand(W1, W2, W3, W0); + + sm3_x4_r2(S0, S1, W2, veorq_u32(W2, W3), 24); + sm3_msg_expand(W2, W3, W0, W1); + + sm3_x4_r2(S0, S1, W3, veorq_u32(W3, W0), 28); + sm3_msg_expand(W3, W0, W1, W2); + + sm3_x4_r2(S0, S1, W0, veorq_u32(W0, W1), 32); + sm3_msg_expand(W0, W1, W2, W3); + + sm3_x4_r2(S0, S1, W1, veorq_u32(W1, W2), 36); + sm3_msg_expand(W1, W2, W3, W0); + + sm3_x4_r2(S0, S1, W2, veorq_u32(W2, W3), 40); + sm3_msg_expand(W2, W3, W0, W1); + + sm3_x4_r2(S0, S1, W3, veorq_u32(W3, W0), 44); + sm3_msg_expand(W3, W0, W1, W2); + + sm3_x4_r2(S0, S1, W0, veorq_u32(W0, W1), 48); + sm3_msg_expand(W0, W1, W2, W3); + + sm3_x4_r2(S0, S1, W1, veorq_u32(W1, W2), 52); + sm3_x4_r2(S0, S1, W2, veorq_u32(W2, W3), 56); + sm3_x4_r2(S0, S1, W3, veorq_u32(W3, W0), 60); + + S0 = veorq_u32(S0, S0_save); + S1 = veorq_u32(S1, S1_save); + } + + vst1q_u32(&digest[0], sm3_reverse_words(S0)); // NOLINT(*-container-data-pointer) + vst1q_u32(&digest[4], sm3_reverse_words(S1)); +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sm3/sm3_avx2_bmi2/info.txt botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3_avx2_bmi2/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/sm3/sm3_avx2_bmi2/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3_avx2_bmi2/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,19 @@ + +SM3_X86_AVX2_BMI2 -> 20251212 + + + +name -> "SM3 using AVX2/BMI2" +brief -> "SM3 using AVX2/BMI2 instructions" + + + +avx2 +bmi2 + + + +cpuid +simd_4x32 +simd_avx2 + diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sm3/sm3_avx2_bmi2/sm3_avx2_bmi2.cpp botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3_avx2_bmi2/sm3_avx2_bmi2.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sm3/sm3_avx2_bmi2/sm3_avx2_bmi2.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3_avx2_bmi2/sm3_avx2_bmi2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,422 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include + +namespace Botan { + +namespace { + +BOTAN_FN_ISA_AVX2_BMI2 inline SIMD_8x32 alignr12(const SIMD_8x32& a, const SIMD_8x32& b) { + return SIMD_8x32(_mm256_alignr_epi8(a.raw(), b.raw(), 12)); +} + +BOTAN_FN_ISA_AVX2_BMI2 inline SIMD_4x32 alignr12(const SIMD_4x32& a, const SIMD_4x32& b) { + return SIMD_4x32(_mm_alignr_epi8(a.raw(), b.raw(), 12)); +} + +template +BOTAN_FN_ISA_AVX2_BMI2 inline void next_SM3_W(SIMD_T& W0, const SIMD_T& W1, const SIMD_T& W2, const SIMD_T& W3) { + auto X3 = alignr12(W1, W0); // W[3..6] + auto X7 = alignr12(W2, W1); // W[7..10] + auto X10 = SIMD_T::alignr8(W3, W2); // W[10..13] + auto X13 = W3.template shift_elems_right<1>(); // W[13..15] || 0 + + auto P1_I = W0 ^ X7 ^ X13.template rotl<15>(); + auto P1_O = P1_I ^ P1_I.template rotl<15>() ^ P1_I.template rotl<23>(); + auto T = P1_O ^ X3.template rotl<7>() ^ X10; + + /* + * There is one hole in the recurrence, we now must compute P1(rotl<15>(W[0])) + * and xor it into W[3] + */ + + // Extract W[0] into T2 in position 3 + auto T2 = T.template shift_elems_left<3>(); + + // Compute P1(rotl<15>(W[0])) [combining the rotation values] + auto P1_T2 = T2.template rotl<15>() ^ T2.template rotl<30>() ^ T2.template rotl<6>(); + + // XOR in + T ^= P1_T2; + + W0 = T; +} + +} // namespace + +BOTAN_FN_ISA_AVX2_BMI2 void SM3::compress_digest_x86_avx2(digest_type& digest, + std::span input, + size_t blocks) { + uint32_t A = digest[0]; + uint32_t B = digest[1]; + uint32_t C = digest[2]; + uint32_t D = digest[3]; + uint32_t E = digest[4]; + uint32_t F = digest[5]; + uint32_t G = digest[6]; + uint32_t H = digest[7]; + std::array W{}; + std::array E2{}; + + const uint8_t* data = input.data(); + + // NOLINTBEGIN(*-container-data-pointer) + + while(blocks >= 2) { + auto W0 = SIMD_8x32::load_be128(&data[0], &data[64]); + auto W1 = SIMD_8x32::load_be128(&data[16], &data[80]); + auto W2 = SIMD_8x32::load_be128(&data[32], &data[96]); + auto W3 = SIMD_8x32::load_be128(&data[48], &data[112]); + + W0.store_le128(&W[0], &E2[0]); + W1.store_le128(&W[4], &E2[4]); + W2.store_le128(&W[8], &E2[8]); + W3.store_le128(&W[12], &E2[12]); + + data += 2 * block_bytes; + blocks -= 2; + + // clang-format off + + R1(A, B, C, D, E, F, G, H, 0x79CC4519, W[ 0], W[ 4]); + R1(D, A, B, C, H, E, F, G, 0xF3988A32, W[ 1], W[ 5]); + R1(C, D, A, B, G, H, E, F, 0xE7311465, W[ 2], W[ 6]); + R1(B, C, D, A, F, G, H, E, 0xCE6228CB, W[ 3], W[ 7]); + next_SM3_W(W0, W1, W2, W3); + W0.store_le128(&W[0], &E2[16]); + + R1(A, B, C, D, E, F, G, H, 0x9CC45197, W[ 4], W[ 8]); + R1(D, A, B, C, H, E, F, G, 0x3988A32F, W[ 5], W[ 9]); + R1(C, D, A, B, G, H, E, F, 0x7311465E, W[ 6], W[10]); + R1(B, C, D, A, F, G, H, E, 0xE6228CBC, W[ 7], W[11]); + next_SM3_W(W1, W2, W3, W0); + W1.store_le128(&W[4], &E2[20]); + + R1(A, B, C, D, E, F, G, H, 0xCC451979, W[ 8], W[12]); + R1(D, A, B, C, H, E, F, G, 0x988A32F3, W[ 9], W[13]); + R1(C, D, A, B, G, H, E, F, 0x311465E7, W[10], W[14]); + R1(B, C, D, A, F, G, H, E, 0x6228CBCE, W[11], W[15]); + next_SM3_W(W2, W3, W0, W1); + W2.store_le128(&W[8], &E2[24]); + + R1(A, B, C, D, E, F, G, H, 0xC451979C, W[12], W[ 0]); + R1(D, A, B, C, H, E, F, G, 0x88A32F39, W[13], W[ 1]); + R1(C, D, A, B, G, H, E, F, 0x11465E73, W[14], W[ 2]); + R1(B, C, D, A, F, G, H, E, 0x228CBCE6, W[15], W[ 3]); + next_SM3_W(W3, W0, W1, W2); + W3.store_le128(&W[12], &E2[28]); + + R2(A, B, C, D, E, F, G, H, 0x9D8A7A87, W[ 0], W[ 4]); + R2(D, A, B, C, H, E, F, G, 0x3B14F50F, W[ 1], W[ 5]); + R2(C, D, A, B, G, H, E, F, 0x7629EA1E, W[ 2], W[ 6]); + R2(B, C, D, A, F, G, H, E, 0xEC53D43C, W[ 3], W[ 7]); + next_SM3_W(W0, W1, W2, W3); + W0.store_le128(&W[0], &E2[32]); + + R2(A, B, C, D, E, F, G, H, 0xD8A7A879, W[ 4], W[ 8]); + R2(D, A, B, C, H, E, F, G, 0xB14F50F3, W[ 5], W[ 9]); + R2(C, D, A, B, G, H, E, F, 0x629EA1E7, W[ 6], W[10]); + R2(B, C, D, A, F, G, H, E, 0xC53D43CE, W[ 7], W[11]); + next_SM3_W(W1, W2, W3, W0); + W1.store_le128(&W[4], &E2[36]); + + R2(A, B, C, D, E, F, G, H, 0x8A7A879D, W[ 8], W[12]); + R2(D, A, B, C, H, E, F, G, 0x14F50F3B, W[ 9], W[13]); + R2(C, D, A, B, G, H, E, F, 0x29EA1E76, W[10], W[14]); + R2(B, C, D, A, F, G, H, E, 0x53D43CEC, W[11], W[15]); + next_SM3_W(W2, W3, W0, W1); + W2.store_le128(&W[8], &E2[40]); + + R2(A, B, C, D, E, F, G, H, 0xA7A879D8, W[12], W[ 0]); + R2(D, A, B, C, H, E, F, G, 0x4F50F3B1, W[13], W[ 1]); + R2(C, D, A, B, G, H, E, F, 0x9EA1E762, W[14], W[ 2]); + R2(B, C, D, A, F, G, H, E, 0x3D43CEC5, W[15], W[ 3]); + next_SM3_W(W3, W0, W1, W2); + W3.store_le128(&W[12], &E2[44]); + + R2(A, B, C, D, E, F, G, H, 0x7A879D8A, W[ 0], W[ 4]); + R2(D, A, B, C, H, E, F, G, 0xF50F3B14, W[ 1], W[ 5]); + R2(C, D, A, B, G, H, E, F, 0xEA1E7629, W[ 2], W[ 6]); + R2(B, C, D, A, F, G, H, E, 0xD43CEC53, W[ 3], W[ 7]); + next_SM3_W(W0, W1, W2, W3); + W0.store_le128(&W[0], &E2[48]); + + R2(A, B, C, D, E, F, G, H, 0xA879D8A7, W[ 4], W[ 8]); + R2(D, A, B, C, H, E, F, G, 0x50F3B14F, W[ 5], W[ 9]); + R2(C, D, A, B, G, H, E, F, 0xA1E7629E, W[ 6], W[10]); + R2(B, C, D, A, F, G, H, E, 0x43CEC53D, W[ 7], W[11]); + next_SM3_W(W1, W2, W3, W0); + W1.store_le128(&W[4], &E2[52]); + + R2(A, B, C, D, E, F, G, H, 0x879D8A7A, W[ 8], W[12]); + R2(D, A, B, C, H, E, F, G, 0x0F3B14F5, W[ 9], W[13]); + R2(C, D, A, B, G, H, E, F, 0x1E7629EA, W[10], W[14]); + R2(B, C, D, A, F, G, H, E, 0x3CEC53D4, W[11], W[15]); + next_SM3_W(W2, W3, W0, W1); + W2.store_le128(&W[8], &E2[56]); + + R2(A, B, C, D, E, F, G, H, 0x79D8A7A8, W[12], W[ 0]); + R2(D, A, B, C, H, E, F, G, 0xF3B14F50, W[13], W[ 1]); + R2(C, D, A, B, G, H, E, F, 0xE7629EA1, W[14], W[ 2]); + R2(B, C, D, A, F, G, H, E, 0xCEC53D43, W[15], W[ 3]); + next_SM3_W(W3, W0, W1, W2); + W3.store_le128(&W[12], &E2[60]); + + R2(A, B, C, D, E, F, G, H, 0x9D8A7A87, W[ 0], W[ 4]); + R2(D, A, B, C, H, E, F, G, 0x3B14F50F, W[ 1], W[ 5]); + R2(C, D, A, B, G, H, E, F, 0x7629EA1E, W[ 2], W[ 6]); + R2(B, C, D, A, F, G, H, E, 0xEC53D43C, W[ 3], W[ 7]); + next_SM3_W(W0, W1, W2, W3); + W0.store_le128(&W[0], &E2[64]); + + R2(A, B, C, D, E, F, G, H, 0xD8A7A879, W[ 4], W[ 8]); + R2(D, A, B, C, H, E, F, G, 0xB14F50F3, W[ 5], W[ 9]); + R2(C, D, A, B, G, H, E, F, 0x629EA1E7, W[ 6], W[10]); + R2(B, C, D, A, F, G, H, E, 0xC53D43CE, W[ 7], W[11]); + + R2(A, B, C, D, E, F, G, H, 0x8A7A879D, W[ 8], W[12]); + R2(D, A, B, C, H, E, F, G, 0x14F50F3B, W[ 9], W[13]); + R2(C, D, A, B, G, H, E, F, 0x29EA1E76, W[10], W[14]); + R2(B, C, D, A, F, G, H, E, 0x53D43CEC, W[11], W[15]); + + R2(A, B, C, D, E, F, G, H, 0xA7A879D8, W[12], W[ 0]); + R2(D, A, B, C, H, E, F, G, 0x4F50F3B1, W[13], W[ 1]); + R2(C, D, A, B, G, H, E, F, 0x9EA1E762, W[14], W[ 2]); + R2(B, C, D, A, F, G, H, E, 0x3D43CEC5, W[15], W[ 3]); + + // clang-format on + + A = (digest[0] ^= A); + B = (digest[1] ^= B); + C = (digest[2] ^= C); + D = (digest[3] ^= D); + E = (digest[4] ^= E); + F = (digest[5] ^= F); + G = (digest[6] ^= G); + H = (digest[7] ^= H); + + // clang-format off + R1(A, B, C, D, E, F, G, H, 0x79CC4519, E2[0], E2[4]); + R1(D, A, B, C, H, E, F, G, 0xF3988A32, E2[1], E2[5]); + R1(C, D, A, B, G, H, E, F, 0xE7311465, E2[2], E2[6]); + R1(B, C, D, A, F, G, H, E, 0xCE6228CB, E2[3], E2[7]); + R1(A, B, C, D, E, F, G, H, 0x9CC45197, E2[4], E2[8]); + R1(D, A, B, C, H, E, F, G, 0x3988A32F, E2[5], E2[9]); + R1(C, D, A, B, G, H, E, F, 0x7311465E, E2[6], E2[10]); + R1(B, C, D, A, F, G, H, E, 0xE6228CBC, E2[7], E2[11]); + R1(A, B, C, D, E, F, G, H, 0xCC451979, E2[8], E2[12]); + R1(D, A, B, C, H, E, F, G, 0x988A32F3, E2[9], E2[13]); + R1(C, D, A, B, G, H, E, F, 0x311465E7, E2[10], E2[14]); + R1(B, C, D, A, F, G, H, E, 0x6228CBCE, E2[11], E2[15]); + R1(A, B, C, D, E, F, G, H, 0xC451979C, E2[12], E2[16]); + R1(D, A, B, C, H, E, F, G, 0x88A32F39, E2[13], E2[17]); + R1(C, D, A, B, G, H, E, F, 0x11465E73, E2[14], E2[18]); + R1(B, C, D, A, F, G, H, E, 0x228CBCE6, E2[15], E2[19]); + R2(A, B, C, D, E, F, G, H, 0x9D8A7A87, E2[16], E2[20]); + R2(D, A, B, C, H, E, F, G, 0x3B14F50F, E2[17], E2[21]); + R2(C, D, A, B, G, H, E, F, 0x7629EA1E, E2[18], E2[22]); + R2(B, C, D, A, F, G, H, E, 0xEC53D43C, E2[19], E2[23]); + R2(A, B, C, D, E, F, G, H, 0xD8A7A879, E2[20], E2[24]); + R2(D, A, B, C, H, E, F, G, 0xB14F50F3, E2[21], E2[25]); + R2(C, D, A, B, G, H, E, F, 0x629EA1E7, E2[22], E2[26]); + R2(B, C, D, A, F, G, H, E, 0xC53D43CE, E2[23], E2[27]); + R2(A, B, C, D, E, F, G, H, 0x8A7A879D, E2[24], E2[28]); + R2(D, A, B, C, H, E, F, G, 0x14F50F3B, E2[25], E2[29]); + R2(C, D, A, B, G, H, E, F, 0x29EA1E76, E2[26], E2[30]); + R2(B, C, D, A, F, G, H, E, 0x53D43CEC, E2[27], E2[31]); + R2(A, B, C, D, E, F, G, H, 0xA7A879D8, E2[28], E2[32]); + R2(D, A, B, C, H, E, F, G, 0x4F50F3B1, E2[29], E2[33]); + R2(C, D, A, B, G, H, E, F, 0x9EA1E762, E2[30], E2[34]); + R2(B, C, D, A, F, G, H, E, 0x3D43CEC5, E2[31], E2[35]); + R2(A, B, C, D, E, F, G, H, 0x7A879D8A, E2[32], E2[36]); + R2(D, A, B, C, H, E, F, G, 0xF50F3B14, E2[33], E2[37]); + R2(C, D, A, B, G, H, E, F, 0xEA1E7629, E2[34], E2[38]); + R2(B, C, D, A, F, G, H, E, 0xD43CEC53, E2[35], E2[39]); + R2(A, B, C, D, E, F, G, H, 0xA879D8A7, E2[36], E2[40]); + R2(D, A, B, C, H, E, F, G, 0x50F3B14F, E2[37], E2[41]); + R2(C, D, A, B, G, H, E, F, 0xA1E7629E, E2[38], E2[42]); + R2(B, C, D, A, F, G, H, E, 0x43CEC53D, E2[39], E2[43]); + R2(A, B, C, D, E, F, G, H, 0x879D8A7A, E2[40], E2[44]); + R2(D, A, B, C, H, E, F, G, 0x0F3B14F5, E2[41], E2[45]); + R2(C, D, A, B, G, H, E, F, 0x1E7629EA, E2[42], E2[46]); + R2(B, C, D, A, F, G, H, E, 0x3CEC53D4, E2[43], E2[47]); + R2(A, B, C, D, E, F, G, H, 0x79D8A7A8, E2[44], E2[48]); + R2(D, A, B, C, H, E, F, G, 0xF3B14F50, E2[45], E2[49]); + R2(C, D, A, B, G, H, E, F, 0xE7629EA1, E2[46], E2[50]); + R2(B, C, D, A, F, G, H, E, 0xCEC53D43, E2[47], E2[51]); + R2(A, B, C, D, E, F, G, H, 0x9D8A7A87, E2[48], E2[52]); + R2(D, A, B, C, H, E, F, G, 0x3B14F50F, E2[49], E2[53]); + R2(C, D, A, B, G, H, E, F, 0x7629EA1E, E2[50], E2[54]); + R2(B, C, D, A, F, G, H, E, 0xEC53D43C, E2[51], E2[55]); + R2(A, B, C, D, E, F, G, H, 0xD8A7A879, E2[52], E2[56]); + R2(D, A, B, C, H, E, F, G, 0xB14F50F3, E2[53], E2[57]); + R2(C, D, A, B, G, H, E, F, 0x629EA1E7, E2[54], E2[58]); + R2(B, C, D, A, F, G, H, E, 0xC53D43CE, E2[55], E2[59]); + R2(A, B, C, D, E, F, G, H, 0x8A7A879D, E2[56], E2[60]); + R2(D, A, B, C, H, E, F, G, 0x14F50F3B, E2[57], E2[61]); + R2(C, D, A, B, G, H, E, F, 0x29EA1E76, E2[58], E2[62]); + R2(B, C, D, A, F, G, H, E, 0x53D43CEC, E2[59], E2[63]); + R2(A, B, C, D, E, F, G, H, 0xA7A879D8, E2[60], E2[64]); + R2(D, A, B, C, H, E, F, G, 0x4F50F3B1, E2[61], E2[65]); + R2(C, D, A, B, G, H, E, F, 0x9EA1E762, E2[62], E2[66]); + R2(B, C, D, A, F, G, H, E, 0x3D43CEC5, E2[63], E2[67]); + + // clang-format on + + A = (digest[0] ^= A); + B = (digest[1] ^= B); + C = (digest[2] ^= C); + D = (digest[3] ^= D); + E = (digest[4] ^= E); + F = (digest[5] ^= F); + G = (digest[6] ^= G); + H = (digest[7] ^= H); + } + + while(blocks > 0) { + SIMD_4x32 W0 = SIMD_4x32::load_be(&data[0]); + SIMD_4x32 W1 = SIMD_4x32::load_be(&data[16]); + SIMD_4x32 W2 = SIMD_4x32::load_be(&data[32]); + SIMD_4x32 W3 = SIMD_4x32::load_be(&data[48]); + + W0.store_le(&W[0]); + W1.store_le(&W[4]); + W2.store_le(&W[8]); + W3.store_le(&W[12]); + + data += block_bytes; + blocks -= 1; + + // clang-format off + + R1(A, B, C, D, E, F, G, H, 0x79CC4519, W[ 0], W[ 4]); + R1(D, A, B, C, H, E, F, G, 0xF3988A32, W[ 1], W[ 5]); + R1(C, D, A, B, G, H, E, F, 0xE7311465, W[ 2], W[ 6]); + R1(B, C, D, A, F, G, H, E, 0xCE6228CB, W[ 3], W[ 7]); + next_SM3_W(W0, W1, W2, W3); + W0.store_le(&W[0]); + + R1(A, B, C, D, E, F, G, H, 0x9CC45197, W[ 4], W[ 8]); + R1(D, A, B, C, H, E, F, G, 0x3988A32F, W[ 5], W[ 9]); + R1(C, D, A, B, G, H, E, F, 0x7311465E, W[ 6], W[10]); + R1(B, C, D, A, F, G, H, E, 0xE6228CBC, W[ 7], W[11]); + next_SM3_W(W1, W2, W3, W0); + W1.store_le(&W[4]); + + R1(A, B, C, D, E, F, G, H, 0xCC451979, W[ 8], W[12]); + R1(D, A, B, C, H, E, F, G, 0x988A32F3, W[ 9], W[13]); + R1(C, D, A, B, G, H, E, F, 0x311465E7, W[10], W[14]); + R1(B, C, D, A, F, G, H, E, 0x6228CBCE, W[11], W[15]); + next_SM3_W(W2, W3, W0, W1); + W2.store_le(&W[8]); + + R1(A, B, C, D, E, F, G, H, 0xC451979C, W[12], W[ 0]); + R1(D, A, B, C, H, E, F, G, 0x88A32F39, W[13], W[ 1]); + R1(C, D, A, B, G, H, E, F, 0x11465E73, W[14], W[ 2]); + R1(B, C, D, A, F, G, H, E, 0x228CBCE6, W[15], W[ 3]); + next_SM3_W(W3, W0, W1, W2); + W3.store_le(&W[12]); + + R2(A, B, C, D, E, F, G, H, 0x9D8A7A87, W[ 0], W[ 4]); + R2(D, A, B, C, H, E, F, G, 0x3B14F50F, W[ 1], W[ 5]); + R2(C, D, A, B, G, H, E, F, 0x7629EA1E, W[ 2], W[ 6]); + R2(B, C, D, A, F, G, H, E, 0xEC53D43C, W[ 3], W[ 7]); + next_SM3_W(W0, W1, W2, W3); + W0.store_le(&W[0]); + + R2(A, B, C, D, E, F, G, H, 0xD8A7A879, W[ 4], W[ 8]); + R2(D, A, B, C, H, E, F, G, 0xB14F50F3, W[ 5], W[ 9]); + R2(C, D, A, B, G, H, E, F, 0x629EA1E7, W[ 6], W[10]); + R2(B, C, D, A, F, G, H, E, 0xC53D43CE, W[ 7], W[11]); + next_SM3_W(W1, W2, W3, W0); + W1.store_le(&W[4]); + + R2(A, B, C, D, E, F, G, H, 0x8A7A879D, W[ 8], W[12]); + R2(D, A, B, C, H, E, F, G, 0x14F50F3B, W[ 9], W[13]); + R2(C, D, A, B, G, H, E, F, 0x29EA1E76, W[10], W[14]); + R2(B, C, D, A, F, G, H, E, 0x53D43CEC, W[11], W[15]); + next_SM3_W(W2, W3, W0, W1); + W2.store_le(&W[8]); + + R2(A, B, C, D, E, F, G, H, 0xA7A879D8, W[12], W[ 0]); + R2(D, A, B, C, H, E, F, G, 0x4F50F3B1, W[13], W[ 1]); + R2(C, D, A, B, G, H, E, F, 0x9EA1E762, W[14], W[ 2]); + R2(B, C, D, A, F, G, H, E, 0x3D43CEC5, W[15], W[ 3]); + next_SM3_W(W3, W0, W1, W2); + W3.store_le(&W[12]); + + R2(A, B, C, D, E, F, G, H, 0x7A879D8A, W[ 0], W[ 4]); + R2(D, A, B, C, H, E, F, G, 0xF50F3B14, W[ 1], W[ 5]); + R2(C, D, A, B, G, H, E, F, 0xEA1E7629, W[ 2], W[ 6]); + R2(B, C, D, A, F, G, H, E, 0xD43CEC53, W[ 3], W[ 7]); + next_SM3_W(W0, W1, W2, W3); + W0.store_le(&W[0]); + + R2(A, B, C, D, E, F, G, H, 0xA879D8A7, W[ 4], W[ 8]); + R2(D, A, B, C, H, E, F, G, 0x50F3B14F, W[ 5], W[ 9]); + R2(C, D, A, B, G, H, E, F, 0xA1E7629E, W[ 6], W[10]); + R2(B, C, D, A, F, G, H, E, 0x43CEC53D, W[ 7], W[11]); + next_SM3_W(W1, W2, W3, W0); + W1.store_le(&W[4]); + + R2(A, B, C, D, E, F, G, H, 0x879D8A7A, W[ 8], W[12]); + R2(D, A, B, C, H, E, F, G, 0x0F3B14F5, W[ 9], W[13]); + R2(C, D, A, B, G, H, E, F, 0x1E7629EA, W[10], W[14]); + R2(B, C, D, A, F, G, H, E, 0x3CEC53D4, W[11], W[15]); + next_SM3_W(W2, W3, W0, W1); + W2.store_le(&W[8]); + + R2(A, B, C, D, E, F, G, H, 0x79D8A7A8, W[12], W[ 0]); + R2(D, A, B, C, H, E, F, G, 0xF3B14F50, W[13], W[ 1]); + R2(C, D, A, B, G, H, E, F, 0xE7629EA1, W[14], W[ 2]); + R2(B, C, D, A, F, G, H, E, 0xCEC53D43, W[15], W[ 3]); + next_SM3_W(W3, W0, W1, W2); + W3.store_le(&W[12]); + + R2(A, B, C, D, E, F, G, H, 0x9D8A7A87, W[ 0], W[ 4]); + R2(D, A, B, C, H, E, F, G, 0x3B14F50F, W[ 1], W[ 5]); + R2(C, D, A, B, G, H, E, F, 0x7629EA1E, W[ 2], W[ 6]); + R2(B, C, D, A, F, G, H, E, 0xEC53D43C, W[ 3], W[ 7]); + next_SM3_W(W0, W1, W2, W3); + W0.store_le(&W[0]); + + R2(A, B, C, D, E, F, G, H, 0xD8A7A879, W[ 4], W[ 8]); + R2(D, A, B, C, H, E, F, G, 0xB14F50F3, W[ 5], W[ 9]); + R2(C, D, A, B, G, H, E, F, 0x629EA1E7, W[ 6], W[10]); + R2(B, C, D, A, F, G, H, E, 0xC53D43CE, W[ 7], W[11]); + + R2(A, B, C, D, E, F, G, H, 0x8A7A879D, W[ 8], W[12]); + R2(D, A, B, C, H, E, F, G, 0x14F50F3B, W[ 9], W[13]); + R2(C, D, A, B, G, H, E, F, 0x29EA1E76, W[10], W[14]); + R2(B, C, D, A, F, G, H, E, 0x53D43CEC, W[11], W[15]); + + R2(A, B, C, D, E, F, G, H, 0xA7A879D8, W[12], W[ 0]); + R2(D, A, B, C, H, E, F, G, 0x4F50F3B1, W[13], W[ 1]); + R2(C, D, A, B, G, H, E, F, 0x9EA1E762, W[14], W[ 2]); + R2(B, C, D, A, F, G, H, E, 0x3D43CEC5, W[15], W[ 3]); + + // clang-format on + + A = (digest[0] ^= A); + B = (digest[1] ^= B); + C = (digest[2] ^= C); + D = (digest[3] ^= D); + E = (digest[4] ^= E); + F = (digest[5] ^= F); + G = (digest[6] ^= G); + H = (digest[7] ^= H); + } + + // NOLINTEND(*-container-data-pointer) +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sm3/sm3_fn.h botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3_fn.h --- botan3-3.7.1+dfsg/src/lib/hash/sm3/sm3_fn.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3_fn.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,75 @@ +/* +* (C) 2017 Ribose Inc. +* (C) 2021 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SM3_FN_H_ +#define BOTAN_SM3_FN_H_ + +#include +#include +#include + +namespace Botan { + +inline uint32_t P0(uint32_t X) { + return X ^ rotl<9>(X) ^ rotl<17>(X); +} + +inline void R1(uint32_t A, + uint32_t& B, + uint32_t C, + uint32_t& D, + uint32_t E, + uint32_t& F, + uint32_t G, + uint32_t& H, + uint32_t TJ, + uint32_t Wi, + uint32_t Wj) { + const uint32_t A12 = rotl<12>(A); + const uint32_t SS1 = rotl<7>(A12 + E + TJ); + const uint32_t TT1 = (A ^ B ^ C) + D + (SS1 ^ A12) + (Wi ^ Wj); + const uint32_t TT2 = (E ^ F ^ G) + H + SS1 + Wi; + + B = rotl<9>(B); + D = TT1; + F = rotl<19>(F); + H = P0(TT2); +} + +inline void R2(uint32_t A, + uint32_t& B, + uint32_t C, + uint32_t& D, + uint32_t E, + uint32_t& F, + uint32_t G, + uint32_t& H, + uint32_t TJ, + uint32_t Wi, + uint32_t Wj) { + const uint32_t A12 = rotl<12>(A); + const uint32_t SS1 = rotl<7>(A12 + E + TJ); + const uint32_t TT1 = majority(A, B, C) + D + (SS1 ^ A12) + (Wi ^ Wj); + const uint32_t TT2 = choose(E, F, G) + H + SS1 + Wi; + + B = rotl<9>(B); + D = TT1; + F = rotl<19>(F); + H = P0(TT2); +} + +inline uint32_t P1(uint32_t X) { + return X ^ rotl<15>(X) ^ rotl<23>(X); +} + +inline uint32_t SM3_E(uint32_t W0, uint32_t W7, uint32_t W13, uint32_t W3, uint32_t W10) { + return P1(W0 ^ W7 ^ rotl<15>(W13)) ^ rotl<7>(W3) ^ W10; +} + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sm3/sm3_x86/info.txt botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3_x86/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/sm3/sm3_x86/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3_x86/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,25 @@ + +SM3_X86 -> 20251225 + + + +name -> "SM3 x86" +brief -> "SM3 using Intel SM3 extension" + + + +cpuid +simd_4x32 + + + +sm3 +ssse3 +avx2 + + + +gcc:14 +clang:17 +msvc + diff -Nru botan3-3.7.1+dfsg/src/lib/hash/sm3/sm3_x86/sm3_x86.cpp botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3_x86/sm3_x86.cpp --- botan3-3.7.1+dfsg/src/lib/hash/sm3/sm3_x86/sm3_x86.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/sm3/sm3_x86/sm3_x86.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,134 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include + +namespace Botan { + +namespace { + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_SM3 void sm3_permute_state_in(SIMD_4x32& S0, SIMD_4x32& S1) { + S0 = SIMD_4x32(_mm_shuffle_epi32(S0.raw(), 0b10110001)); // CDAB + S1 = SIMD_4x32(_mm_shuffle_epi32(S1.raw(), 0b00011011)); // EFGH + + const auto T = SIMD_4x32::alignr8(S0, S1); // ABEF + S1 = SIMD_4x32(_mm_blend_epi16(S1.rotr<19>().raw(), S0.rotr<9>().raw(), 0xF0)); // CDGH + S0 = T; +} + +BOTAN_FN_ISA_AVX2_SM3 inline void SM3_NI_next(SIMD_4x32& W0, + const SIMD_4x32& W1, + const SIMD_4x32& W2, + const SIMD_4x32& W3) { + auto X3 = SIMD_4x32(_mm_alignr_epi8(W1.raw(), W0.raw(), 12)); // W[3..6] + auto X7 = SIMD_4x32(_mm_alignr_epi8(W2.raw(), W1.raw(), 12)); // W[7..10] + auto X10 = SIMD_4x32::alignr8(W3, W2); // W[10..13] + auto X13 = W3.template shift_elems_right<1>(); // W[13..15] || 0 + + auto P1_O = SIMD_4x32(_mm_sm3msg1_epi32(X7.raw(), X13.raw(), W0.raw())); + W0 = SIMD_4x32(_mm_sm3msg2_epi32(P1_O.raw(), X3.raw(), X10.raw())); +} + +template +BOTAN_FN_ISA_AVX2_SM3 inline void SM3_NI_Rx4(SIMD_4x32& S0, SIMD_4x32& S1, SIMD_4x32 W0, SIMD_4x32 W1) { + const auto W0145 = SIMD_4x32(_mm_unpacklo_epi64(W0.raw(), W1.raw())); + const auto W2367 = SIMD_4x32(_mm_unpackhi_epi64(W0.raw(), W1.raw())); + + S0 = SIMD_4x32(_mm_sm3rnds2_epi32(S0.raw(), S1.raw(), W0145.raw(), R)); + S1 = SIMD_4x32(_mm_sm3rnds2_epi32(S1.raw(), S0.raw(), W2367.raw(), R + 2)); +} + +} // namespace + +BOTAN_FN_ISA_AVX2_SM3 void SM3::compress_digest_x86(digest_type& digest, + std::span input, + size_t blocks) { + auto S0 = SIMD_4x32::load_le(&digest[0]); // NOLINT(*-container-data-pointer) + auto S1 = SIMD_4x32::load_le(&digest[4]); + sm3_permute_state_in(S0, S1); + + const uint8_t* data = input.data(); + + while(blocks > 0) { + SIMD_4x32 W0 = SIMD_4x32::load_be(&data[0]); // NOLINT(*-container-data-pointer) + SIMD_4x32 W1 = SIMD_4x32::load_be(&data[16]); + SIMD_4x32 W2 = SIMD_4x32::load_be(&data[32]); + SIMD_4x32 W3 = SIMD_4x32::load_be(&data[48]); + + const auto S0_save = S0; + const auto S1_save = S1; + + data += block_bytes; + blocks -= 1; + + SM3_NI_Rx4<0>(S1, S0, W0, W1); + SM3_NI_next(W0, W1, W2, W3); + + SM3_NI_Rx4<4>(S1, S0, W1, W2); + SM3_NI_next(W1, W2, W3, W0); + + SM3_NI_Rx4<8>(S1, S0, W2, W3); + SM3_NI_next(W2, W3, W0, W1); + + SM3_NI_Rx4<12>(S1, S0, W3, W0); + SM3_NI_next(W3, W0, W1, W2); + + SM3_NI_Rx4<16>(S1, S0, W0, W1); + SM3_NI_next(W0, W1, W2, W3); + + SM3_NI_Rx4<20>(S1, S0, W1, W2); + SM3_NI_next(W1, W2, W3, W0); + + SM3_NI_Rx4<24>(S1, S0, W2, W3); + SM3_NI_next(W2, W3, W0, W1); + + SM3_NI_Rx4<28>(S1, S0, W3, W0); + SM3_NI_next(W3, W0, W1, W2); + + SM3_NI_Rx4<32>(S1, S0, W0, W1); + SM3_NI_next(W0, W1, W2, W3); + + SM3_NI_Rx4<36>(S1, S0, W1, W2); + SM3_NI_next(W1, W2, W3, W0); + + SM3_NI_Rx4<40>(S1, S0, W2, W3); + SM3_NI_next(W2, W3, W0, W1); + + SM3_NI_Rx4<44>(S1, S0, W3, W0); + SM3_NI_next(W3, W0, W1, W2); + + SM3_NI_Rx4<48>(S1, S0, W0, W1); + SM3_NI_next(W0, W1, W2, W3); + + SM3_NI_Rx4<52>(S1, S0, W1, W2); + SM3_NI_Rx4<56>(S1, S0, W2, W3); + SM3_NI_Rx4<60>(S1, S0, W3, W0); + + S0 ^= S0_save; + S1 ^= S1_save; + } + + // TODO do this with SIMD instead + uint32_t T[8] = {0}; + S0.store_le(&T[0]); + S1.store_le(&T[4]); + + digest[0] = T[3]; + digest[1] = T[2]; + digest[2] = rotr<23>(T[7]); + digest[3] = rotr<23>(T[6]); + digest[4] = T[1]; + digest[5] = T[0]; + digest[6] = rotr<13>(T[5]); + digest[7] = rotr<13>(T[4]); +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/streebog/streebog.cpp botan3-3.12.0+dfsg/src/lib/hash/streebog/streebog.cpp --- botan3-3.7.1+dfsg/src/lib/hash/streebog/streebog.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/streebog/streebog.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,7 +1,7 @@ /* -* Streebog +* Streebog (GOST R 34.11-2012) * (C) 2017 Ribose Inc. -* (C) 2018 Jack Lloyd +* (C) 2018,2026 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -9,15 +9,117 @@ #include #include +#include #include +#include #include #include -#include +#include +#include namespace Botan { -extern const uint64_t STREEBOG_Ax[8][256]; -extern const uint64_t STREEBOG_C[12][8]; +namespace { + +// Build the combined T-tables at compile time +consteval std::array, 8> streebog_Ax_table() noexcept { + // Streebog sbox (same as Kuznyechik's), RFC 6986 Section 6.2 + alignas(256) const constexpr uint8_t S[256] = { + 252, 238, 221, 17, 207, 110, 49, 22, 251, 196, 250, 218, 35, 197, 4, 77, 233, 119, 240, 219, 147, 46, + 153, 186, 23, 54, 241, 187, 20, 205, 95, 193, 249, 24, 101, 90, 226, 92, 239, 33, 129, 28, 60, 66, + 139, 1, 142, 79, 5, 132, 2, 174, 227, 106, 143, 160, 6, 11, 237, 152, 127, 212, 211, 31, 235, 52, + 44, 81, 234, 200, 72, 171, 242, 42, 104, 162, 253, 58, 206, 204, 181, 112, 14, 86, 8, 12, 118, 18, + 191, 114, 19, 71, 156, 183, 93, 135, 21, 161, 150, 41, 16, 123, 154, 199, 243, 145, 120, 111, 157, 158, + 178, 177, 50, 117, 25, 61, 255, 53, 138, 126, 109, 84, 198, 128, 195, 189, 13, 87, 223, 245, 36, 169, + 62, 168, 67, 201, 215, 121, 214, 246, 124, 34, 185, 3, 224, 15, 236, 222, 122, 148, 176, 188, 220, 232, + 40, 80, 78, 51, 10, 74, 167, 151, 96, 115, 30, 0, 98, 68, 26, 184, 56, 130, 100, 159, 38, 65, + 173, 69, 70, 146, 39, 94, 85, 47, 140, 163, 165, 125, 105, 213, 149, 59, 7, 88, 179, 64, 134, 172, + 29, 247, 48, 55, 107, 228, 136, 217, 231, 137, 225, 27, 131, 73, 76, 63, 248, 254, 141, 83, 170, 144, + 202, 216, 133, 97, 32, 113, 103, 164, 45, 43, 9, 91, 203, 155, 37, 208, 190, 229, 108, 82, 89, 166, + 116, 210, 230, 244, 180, 192, 209, 102, 175, 194, 57, 75, 99, 182, + }; + + // Columns of the 8x8 linear transformation matrix over GF(2^8) + const constexpr uint64_t L[8] = { + 0x641c314b2b8ee083, + 0xa48b474f9ef5dc18, + 0xf97d86d98a327728, + 0x5b068c651810a89e, + 0x0321658cba93c138, + 0xaccc9ca9328a8950, + 0x46b60f011a83988e, + 0x83478b07b2468764, + }; + + std::array, 8> Ax = {}; + + for(size_t j = 0; j != 8; ++j) { + for(size_t x = 0; x != 256; ++x) { + Ax[j][x] = poly_mul<0x1D>(L[j], S[x]); + } + } + + return Ax; +} + +const constinit auto STREEBOG_Ax = streebog_Ax_table(); + +// Iteration constants C[1]..C[12] from GOST R 34.11-2012 (RFC 6986 Section 6.5) +// Word order matches the RFC (big-endian presentation); indexed with 7-j below +// clang-format off +const constexpr uint64_t STREEBOG_C[12][8] = { + {0xb1085bda1ecadae9, 0xebcb2f81c0657c1f, 0x2f6a76432e45d016, 0x714eb88d7585c4fc, + 0x4b7ce09192676901, 0xa2422a08a460d315, 0x05767436cc744d23, 0xdd806559f2a64507}, + {0x6fa3b58aa99d2f1a, 0x4fe39d460f70b5d7, 0xf3feea720a232b98, 0x61d55e0f16b50131, + 0x9ab5176b12d69958, 0x5cb561c2db0aa7ca, 0x55dda21bd7cbcd56, 0xe679047021b19bb7}, + {0xf574dcac2bce2fc7, 0x0a39fc286a3d8435, 0x06f15e5f529c1f8b, 0xf2ea7514b1297b7b, + 0xd3e20fe490359eb1, 0xc1c93a376062db09, 0xc2b6f443867adb31, 0x991e96f50aba0ab2}, + {0xef1fdfb3e81566d2, 0xf948e1a05d71e4dd, 0x488e857e335c3c7d, 0x9d721cad685e353f, + 0xa9d72c82ed03d675, 0xd8b71333935203be, 0x3453eaa193e837f1, 0x220cbebc84e3d12e}, + {0x4bea6bacad474799, 0x9a3f410c6ca92363, 0x7f151c1f1686104a, 0x359e35d7800fffbd, + 0xbfcd1747253af5a3, 0xdfff00b723271a16, 0x7a56a27ea9ea63f5, 0x601758fd7c6cfe57}, + {0xae4faeae1d3ad3d9, 0x6fa4c33b7a3039c0, 0x2d66c4f95142a46c, 0x187f9ab49af08ec6, + 0xcffaa6b71c9ab7b4, 0x0af21f66c2bec6b6, 0xbf71c57236904f35, 0xfa68407a46647d6e}, + {0xf4c70e16eeaac5ec, 0x51ac86febf240954, 0x399ec6c7e6bf87c9, 0xd3473e33197a93c9, + 0x0992abc52d822c37, 0x06476983284a0504, 0x3517454ca23c4af3, 0x8886564d3a14d493}, + {0x9b1f5b424d93c9a7, 0x03e7aa020c6e4141, 0x4eb7f8719c36de1e, 0x89b4443b4ddbc49a, + 0xf4892bcb929b0690, 0x69d18d2bd1a5c42f, 0x36acc2355951a8d9, 0xa47f0dd4bf02e71e}, + {0x378f5a541631229b, 0x944c9ad8ec165fde, 0x3a7d3a1b25894224, 0x3cd955b7e00d0984, + 0x800a440bdbb2ceb1, 0x7b2b8a9aa6079c54, 0x0e38dc92cb1f2a60, 0x7261445183235adb}, + {0xabbedea680056f52, 0x382ae548b2e4f3f3, 0x8941e71cff8a78db, 0x1fffe18a1b336103, + 0x9fe76702af69334b, 0x7a1e6c303b7652f4, 0x3698fad1153bb6c3, 0x74b4c7fb98459ced}, + {0x7bcd9ed0efc889fb, 0x3002c6cd635afe94, 0xd8fa6bbbebab0761, 0x2001802114846679, + 0x8a1d71efea48b9ca, 0xefbacd1d7d476e98, 0xdea2594ac06fd85d, 0x6bcaa4cd81f32d1b}, + {0x378ee767f11631ba, 0xd21380b00449b17a, 0xcda43c32bcdf1d77, 0xf82012d430219f9b, + 0x5d80ef9d1891cc86, 0xe71da4aa88e12852, 0xfaf417d5d9b21b99, 0x48bc924af11bd720}, +}; + +// clang-format on + +inline uint64_t force_le(uint64_t x) { + if constexpr(std::endian::native == std::endian::little) { + return x; + } else if constexpr(std::endian::native == std::endian::big) { + return reverse_bytes(x); + } else { + store_le(x, reinterpret_cast(&x)); + return x; + } +} + +inline void lps(uint64_t block[8]) { + const uint64_t block2[8] = {block[0], block[1], block[2], block[3], block[4], block[5], block[6], block[7]}; + const std::span r{reinterpret_cast(block2), 64}; + + for(int i = 0; i < 8; ++i) { + block[i] = force_le(STREEBOG_Ax[0][r[i + 0 * 8]]) ^ force_le(STREEBOG_Ax[1][r[i + 1 * 8]]) ^ + force_le(STREEBOG_Ax[2][r[i + 2 * 8]]) ^ force_le(STREEBOG_Ax[3][r[i + 3 * 8]]) ^ + force_le(STREEBOG_Ax[4][r[i + 4 * 8]]) ^ force_le(STREEBOG_Ax[5][r[i + 5 * 8]]) ^ + force_le(STREEBOG_Ax[6][r[i + 6 * 8]]) ^ force_le(STREEBOG_Ax[7][r[i + 7 * 8]]); + } +} + +} //namespace std::unique_ptr Streebog::copy_state() const { return std::make_unique(*this); @@ -86,43 +188,16 @@ compress(m_buffer.consume().data(), true); compress_64(m_S.data(), true); - // FIXME - std::memcpy(output.data(), &m_h[8 - output_length() / 8], output_length()); - clear(); -} - -namespace { - -inline uint64_t force_le(uint64_t x) { -#if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) - return x; -#elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) - return reverse_bytes(x); -#else - store_le(x, reinterpret_cast(&x)); - return x; -#endif -} - -inline void lps(uint64_t block[8]) { - uint8_t r[64]; - // FIXME - std::memcpy(r, block, 64); - for(int i = 0; i < 8; ++i) { - block[i] = force_le(STREEBOG_Ax[0][r[i + 0 * 8]]) ^ force_le(STREEBOG_Ax[1][r[i + 1 * 8]]) ^ - force_le(STREEBOG_Ax[2][r[i + 2 * 8]]) ^ force_le(STREEBOG_Ax[3][r[i + 3 * 8]]) ^ - force_le(STREEBOG_Ax[4][r[i + 4 * 8]]) ^ force_le(STREEBOG_Ax[5][r[i + 5 * 8]]) ^ - force_le(STREEBOG_Ax[6][r[i + 6 * 8]]) ^ force_le(STREEBOG_Ax[7][r[i + 7 * 8]]); - } + const size_t offset = 8 - output_length() / 8; + const size_t count = output_length() / sizeof(uint64_t); + typecast_copy(output, std::span(&m_h[offset], count)); + clear(); } -} //namespace - void Streebog::compress(const uint8_t input[], bool last_block) { uint64_t M[8]; - std::memcpy(M, input, 64); - + typecast_copy(M, std::span(input, 64)); compress_64(M, last_block); } @@ -142,9 +217,9 @@ hN[i] ^= M[i]; } - for(size_t i = 0; i < 12; ++i) { + for(size_t i = 0; i < 12; ++i) { // NOLINT(modernize-loop-convert) for(size_t j = 0; j != 8; ++j) { - A[j] ^= force_le(STREEBOG_C[i][j]); + A[j] ^= force_le(STREEBOG_C[i][7 - j]); } lps(A); @@ -167,7 +242,7 @@ m_S[i] = force_le(t); if(t != m) { - carry = (t < m); + carry = (t < m) ? 1 : 0; } } } diff -Nru botan3-3.7.1+dfsg/src/lib/hash/streebog/streebog_precalc.cpp botan3-3.12.0+dfsg/src/lib/hash/streebog/streebog_precalc.cpp --- botan3-3.7.1+dfsg/src/lib/hash/streebog/streebog_precalc.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/streebog/streebog_precalc.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,549 +0,0 @@ -/* - * Derived from: - * https://github.com/degtyarevalexey/streebog - * - * Copyright (c) 2013, Alexey Degtyarev . - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include - -namespace Botan { - -extern const uint64_t STREEBOG_Ax[8][256] = { - {0xd01f715b5c7ef8e6, 0x16fa240980778325, 0xa8a42e857ee049c8, 0x6ac1068fa186465b, 0x6e417bd7a2e9320b, - 0x665c8167a437daab, 0x7666681aa89617f6, 0x4b959163700bdcf5, 0xf14be6b78df36248, 0xc585bd689a625cff, - 0x9557d7fca67d82cb, 0x89f0b969af6dd366, 0xb0833d48749f6c35, 0xa1998c23b1ecbc7c, 0x8d70c431ac02a736, - 0xd6dfbc2fd0a8b69e, 0x37aeb3e551fa198b, 0x0b7d128a40b5cf9c, 0x5a8f2008b5780cbc, 0xedec882284e333e5, - 0xd25fc177d3c7c2ce, 0x5e0f5d50b61778ec, 0x1d873683c0c24cb9, 0xad040bcbb45d208c, 0x2f89a0285b853c76, - 0x5732fff6791b8d58, 0x3e9311439ef6ec3f, 0xc9183a809fd3c00f, 0x83adf3f5260a01ee, 0xa6791941f4e8ef10, - 0x103ae97d0ca1cd5d, 0x2ce948121dee1b4a, 0x39738421dbf2bf53, 0x093da2a6cf0cf5b4, 0xcd9847d89cbcb45f, - 0xf9561c078b2d8ae8, 0x9c6a755a6971777f, 0xbc1ebaa0712ef0c5, 0x72e61542abf963a6, 0x78bb5fde229eb12e, - 0x14ba94250fceb90d, 0x844d6697630e5282, 0x98ea08026a1e032f, 0xf06bbea144217f5c, 0xdb6263d11ccb377a, - 0x641c314b2b8ee083, 0x320e96ab9b4770cf, 0x1ee7deb986a96b85, 0xe96cf57a878c47b5, 0xfdd6615f8842feb8, - 0xc83862965601dd1b, 0x2ea9f83e92572162, 0xf876441142ff97fc, 0xeb2c455608357d9d, 0x5612a7e0b0c9904c, - 0x6c01cbfb2d500823, 0x4548a6a7fa037a2d, 0xabc4c6bf388b6ef4, 0xbade77d4fdf8bebd, 0x799b07c8eb4cac3a, - 0x0c9d87e805b19cf0, 0xcb588aac106afa27, 0xea0c1d40c1e76089, 0x2869354a1e816f1a, 0xff96d17307fbc490, - 0x9f0a9d602f1a5043, 0x96373fc6e016a5f7, 0x5292dab8b3a6e41c, 0x9b8ae0382c752413, 0x4f15ec3b7364a8a5, - 0x3fb349555724f12b, 0xc7c50d4415db66d7, 0x92b7429ee379d1a7, 0xd37f99611a15dfda, 0x231427c05e34a086, - 0xa439a96d7b51d538, 0xb403401077f01865, 0xdda2aea5901d7902, 0x0a5d4a9c8967d288, 0xc265280adf660f93, - 0x8bb0094520d4e94e, 0x2a29856691385532, 0x42a833c5bf072941, 0x73c64d54622b7eb2, 0x07e095624504536c, - 0x8a905153e906f45a, 0x6f6123c16b3b2f1f, 0xc6e55552dc097bc3, 0x4468feb133d16739, 0xe211e7f0c7398829, - 0xa2f96419f7879b40, 0x19074bdbc3ad38e9, 0xf4ebc3f9474e0b0c, 0x43886bd376d53455, 0xd8028beb5aa01046, - 0x51f23282f5cdc320, 0xe7b1c2be0d84e16d, 0x081dfab006dee8a0, 0x3b33340d544b857b, 0x7f5bcabc679ae242, - 0x0edd37c48a08a6d8, 0x81ed43d9a9b33bc6, 0xb1a3655ebd4d7121, 0x69a1eeb5e7ed6167, 0xf6ab73d5c8f73124, - 0x1a67a3e185c61fd5, 0x2dc91004d43c065e, 0x0240b02c8fb93a28, 0x90f7f2b26cc0eb8f, 0x3cd3a16f114fd617, - 0xaae49ea9f15973e0, 0x06c0cd748cd64e78, 0xda423bc7d5192a6e, 0xc345701c16b41287, 0x6d2193ede4821537, - 0xfcf639494190e3ac, 0x7c3b228621f1c57e, 0xfb16ac2b0494b0c0, 0xbf7e529a3745d7f9, 0x6881b6a32e3f7c73, - 0xca78d2bad9b8e733, 0xbbfe2fc2342aa3a9, 0x0dbddffecc6381e4, 0x70a6a56e2440598e, 0xe4d12a844befc651, - 0x8c509c2765d0ba22, 0xee8c6018c28814d9, 0x17da7c1f49a59e31, 0x609c4c1328e194d3, 0xb3e3d57232f44b09, - 0x91d7aaa4a512f69b, 0x0ffd6fd243dabbcc, 0x50d26a943c1fde34, 0x6be15e9968545b4f, 0x94778fea6faf9fdf, - 0x2b09dd7058ea4826, 0x677cd9716de5c7bf, 0x49d5214fffb2e6dd, 0x0360e83a466b273c, 0x1fc786af4f7b7691, - 0xa0b9d435783ea168, 0xd49f0c035f118cb6, 0x01205816c9d21d14, 0xac2453dd7d8f3d98, 0x545217cc3f70aa64, - 0x26b4028e9489c9c2, 0xdec2469fd6765e3e, 0x04807d58036f7450, 0xe5f17292823ddb45, 0xf30b569b024a5860, - 0x62dcfc3fa758aefb, 0xe84cad6c4e5e5aa1, 0xccb81fce556ea94b, 0x53b282ae7a74f908, 0x1b47fbf74c1402c1, - 0x368eebf39828049f, 0x7afbeff2ad278b06, 0xbe5e0a8cfe97caed, 0xcfd8f7f413058e77, 0xf78b2bc301252c30, - 0x4d555c17fcdd928d, 0x5f2f05467fc565f8, 0x24f4b2a21b30f3ea, 0x860dd6bbecb768aa, 0x4c750401350f8f99, - 0x0000000000000000, 0xecccd0344d312ef1, 0xb5231806be220571, 0xc105c030990d28af, 0x653c695de25cfd97, - 0x159acc33c61ca419, 0xb89ec7f872418495, 0xa9847693b73254dc, 0x58cf90243ac13694, 0x59efc832f3132b80, - 0x5c4fed7c39ae42c4, 0x828dabe3efd81cfa, 0xd13f294d95ace5f2, 0x7d1b7a90e823d86a, 0xb643f03cf849224d, - 0x3df3f979d89dcb03, 0x7426d836272f2dde, 0xdfe21e891fa4432a, 0x3a136c1b9d99986f, 0xfa36f43dcd46add4, - 0xc025982650df35bb, 0x856d3e81aadc4f96, 0xc4a5e57e53b041eb, 0x4708168b75ba4005, 0xaf44bbe73be41aa4, - 0x971767d029c4b8e3, 0xb9be9feebb939981, 0x215497ecd18d9aae, 0x316e7e91dd2c57f3, 0xcef8afe2dad79363, - 0x3853dc371220a247, 0x35ee03c9de4323a3, 0xe6919aa8c456fc79, 0xe05157dc4880b201, 0x7bdbb7e464f59612, - 0x127a59518318f775, 0x332ecebd52956ddb, 0x8f30741d23bb9d1e, 0xd922d3fd93720d52, 0x7746300c61440ae2, - 0x25d4eab4d2e2eefe, 0x75068020eefd30ca, 0x135a01474acaea61, 0x304e268714fe4ae7, 0xa519f17bb283c82c, - 0xdc82f6b359cf6416, 0x5baf781e7caa11a8, 0xb2c38d64fb26561d, 0x34ce5bdf17913eb7, 0x5d6fb56af07c5fd0, - 0x182713cd0a7f25fd, 0x9e2ac576e6c84d57, 0x9aaab82ee5a73907, 0xa3d93c0f3e558654, 0x7e7b92aaae48ff56, - 0x872d8ead256575be, 0x41c8dbfff96c0e7d, 0x99ca5014a3cc1e3b, 0x40e883e930be1369, 0x1ca76e95091051ad, - 0x4e35b42dbab6b5b1, 0x05a0254ecabd6944, 0xe1710fca8152af15, 0xf22b0e8dcb984574, 0xb763a82a319b3f59, - 0x63fca4296e8ab3ef, 0x9d4a2d4ca0a36a6b, 0xe331bfe60eeb953d, 0xd5bf541596c391a2, 0xf5cb9bef8e9c1618, - 0x46284e9dbc685d11, 0x2074cffa185f87ba, 0xbd3ee2b6b8fcedd1, 0xae64e3f1f23607b0, 0xfeb68965ce29d984, - 0x55724fdaf6a2b770, 0x29496d5cd753720e, 0xa75941573d3af204, 0x8e102c0bea69800a, 0x111ab16bc573d049, - 0xd7ffe439197aab8a, 0xefac380e0b5a09cd, 0x48f579593660fbc9, 0x22347fd697e6bd92, 0x61bc1405e13389c7, - 0x4ab5c975b9d9c1e1, 0x80cd1bcf606126d2, 0x7186fd78ed92449a, 0x93971a882aabccb3, 0x88d0e17f66bfce72, - 0x27945a985d5bd4d6}, - {0xde553f8c05a811c8, 0x1906b59631b4f565, 0x436e70d6b1964ff7, 0x36d343cb8b1e9d85, 0x843dfacc858aab5a, - 0xfdfc95c299bfc7f9, 0x0f634bdea1d51fa2, 0x6d458b3b76efb3cd, 0x85c3f77cf8593f80, 0x3c91315fbe737cb2, - 0x2148b03366ace398, 0x18f8b8264c6761bf, 0xc830c1c495c9fb0f, 0x981a76102086a0aa, 0xaa16012142f35760, - 0x35cc54060c763cf6, 0x42907d66cc45db2d, 0x8203d44b965af4bc, 0x3d6f3cefc3a0e868, 0xbc73ff69d292bda7, - 0x8722ed0102e20a29, 0x8f8185e8cd34deb7, 0x9b0561dda7ee01d9, 0x5335a0193227fad6, 0xc9cecc74e81a6fd5, - 0x54f5832e5c2431ea, 0x99e47ba05d553470, 0xf7bee756acd226ce, 0x384e05a5571816fd, 0xd1367452a47d0e6a, - 0xf29fde1c386ad85b, 0x320c77316275f7ca, 0xd0c879e2d9ae9ab0, 0xdb7406c69110ef5d, 0x45505e51a2461011, - 0xfc029872e46c5323, 0xfa3cb6f5f7bc0cc5, 0x031f17cd8768a173, 0xbd8df2d9af41297d, 0x9d3b4f5ab43e5e3f, - 0x4071671b36feee84, 0x716207e7d3e3b83d, 0x48d20ff2f9283a1a, 0x27769eb4757cbc7e, 0x5c56ebc793f2e574, - 0xa48b474f9ef5dc18, 0x52cbada94ff46e0c, 0x60c7da982d8199c6, 0x0e9d466edc068b78, 0x4eec2175eaf865fc, - 0x550b8e9e21f7a530, 0x6b7ba5bc653fec2b, 0x5eb7f1ba6949d0dd, 0x57ea94e3db4c9099, 0xf640eae6d101b214, - 0xdd4a284182c0b0bb, 0xff1d8fbf6304f250, 0xb8accb933bf9d7e8, 0xe8867c478eb68c4d, 0x3f8e2692391bddc1, - 0xcb2fd60912a15a7c, 0xaec935dbab983d2f, 0xf55ffd2b56691367, 0x80e2ce366ce1c115, 0x179bf3f8edb27e1d, - 0x01fe0db07dd394da, 0xda8a0b76ecc37b87, 0x44ae53e1df9584cb, 0xb310b4b77347a205, 0xdfab323c787b8512, - 0x3b511268d070b78e, 0x65e6e3d2b9396753, 0x6864b271e2574d58, 0x259784c98fc789d7, 0x02e11a7dfabb35a9, - 0x8841a6dfa337158b, 0x7ade78c39b5dcdd0, 0xb7cf804d9a2cc84a, 0x20b6bd831b7f7742, 0x75bd331d3a88d272, - 0x418f6aab4b2d7a5e, 0xd9951cbb6babdaf4, 0xb6318dfde7ff5c90, 0x1f389b112264aa83, 0x492c024284fbaec0, - 0xe33a0363c608f9a0, 0x2688930408af28a4, 0xc7538a1a341ce4ad, 0x5da8e677ee2171ae, 0x8c9e92254a5c7fc4, - 0x63d8cd55aae938b5, 0x29ebd8daa97a3706, 0x959827b37be88aa1, 0x1484e4356adadf6e, 0xa7945082199d7d6b, - 0xbf6ce8a455fa1cd4, 0x9cc542eac9edcae5, 0x79c16f0e1c356ca3, 0x89bfab6fdee48151, 0xd4174d1830c5f0ff, - 0x9258048415eb419d, 0x6139d72850520d1c, 0x6a85a80c18ec78f1, 0xcd11f88e0171059a, 0xcceff53e7ca29140, - 0xd229639f2315af19, 0x90b91ef9ef507434, 0x5977d28d074a1be1, 0x311360fce51d56b9, 0xc093a92d5a1f2f91, - 0x1a19a25bb6dc5416, 0xeb996b8a09de2d3e, 0xfee3820f1ed7668a, 0xd7085ad5b7ad518c, 0x7fff41890fe53345, - 0xec5948bd67dde602, 0x2fd5f65dbaaa68e0, 0xa5754affe32648c2, 0xf8ddac880d07396c, 0x6fa491468c548664, - 0x0c7c5c1326bdbed1, 0x4a33158f03930fb3, 0x699abfc19f84d982, 0xe4fa2054a80b329c, 0x6707f9af438252fa, - 0x08a368e9cfd6d49e, 0x47b1442c58fd25b8, 0xbbb3dc5ebc91769b, 0x1665fe489061eac7, 0x33f27a811fa66310, - 0x93a609346838d547, 0x30ed6d4c98cec263, 0x1dd9816cd8df9f2a, 0x94662a03063b1e7b, 0x83fdd9fbeb896066, - 0x7b207573e68e590a, 0x5f49fc0a149a4407, 0x343259b671a5a82c, 0xfbc2bb458a6f981f, 0xc272b350a0a41a38, - 0x3aaf1fd8ada32354, 0x6cbb868b0b3c2717, 0xa2b569c88d2583fe, 0xf180c9d1bf027928, 0xaf37386bd64ba9f5, - 0x12bacab2790a8088, 0x4c0d3b0810435055, 0xb2eeb9070e9436df, 0xc5b29067cea7d104, 0xdcb425f1ff132461, - 0x4f122cc5972bf126, 0xac282fa651230886, 0xe7e537992f6393ef, 0xe61b3a2952b00735, 0x709c0a57ae302ce7, - 0xe02514ae416058d3, 0xc44c9dd7b37445de, 0x5a68c5408022ba92, 0x1c278cdca50c0bf0, 0x6e5a9cf6f18712be, - 0x86dce0b17f319ef3, 0x2d34ec2040115d49, 0x4bcd183f7e409b69, 0x2815d56ad4a9a3dc, 0x24698979f2141d0d, - 0x0000000000000000, 0x1ec696a15fb73e59, 0xd86b110b16784e2e, 0x8e7f8858b0e74a6d, 0x063e2e8713d05fe6, - 0xe2c40ed3bbdb6d7a, 0xb1f1aeca89fc97ac, 0xe1db191e3cb3cc09, 0x6418ee62c4eaf389, 0xc6ad87aa49cf7077, - 0xd6f65765ca7ec556, 0x9afb6c6dda3d9503, 0x7ce05644888d9236, 0x8d609f95378feb1e, 0x23a9aa4e9c17d631, - 0x6226c0e5d73aac6f, 0x56149953a69f0443, 0xeeb852c09d66d3ab, 0x2b0ac2a753c102af, 0x07c023376e03cb3c, - 0x2ccae1903dc2c993, 0xd3d76e2f5ec63bc3, 0x9e2458973356ff4c, 0xa66a5d32644ee9b1, 0x0a427294356de137, - 0x783f62be61e6f879, 0x1344c70204d91452, 0x5b96c8f0fdf12e48, 0xa90916ecc59bf613, 0xbe92e5142829880e, - 0x727d102a548b194e, 0x1be7afebcb0fc0cc, 0x3e702b2244c8491b, 0xd5e940a84d166425, 0x66f9f41f3e51c620, - 0xabe80c913f20c3ba, 0xf07ec461c2d1edf2, 0xf361d3ac45b94c81, 0x0521394a94b8fe95, 0xadd622162cf09c5c, - 0xe97871f7f3651897, 0xf4a1f09b2bba87bd, 0x095d6559b2054044, 0x0bbc7f2448be75ed, 0x2af4cf172e129675, - 0x157ae98517094bb4, 0x9fda55274e856b96, 0x914713499283e0ee, 0xb952c623462a4332, 0x74433ead475b46a8, - 0x8b5eb112245fb4f8, 0xa34b6478f0f61724, 0x11a5dd7ffe6221fb, 0xc16da49d27ccbb4b, 0x76a224d0bde07301, - 0x8aa0bca2598c2022, 0x4df336b86d90c48f, 0xea67663a740db9e4, 0xef465f70e0b54771, 0x39b008152acb8227, - 0x7d1e5bf4f55e06ec, 0x105bd0cf83b1b521, 0x775c2960c033e7db, 0x7e014c397236a79f, 0x811cc386113255cf, - 0xeda7450d1a0e72d8, 0x5889df3d7a998f3b, 0x2e2bfbedc779fc3a, 0xce0eef438619a4e9, 0x372d4e7bf6cd095f, - 0x04df34fae96b6a4f, 0xf923a13870d4adb6, 0xa1aa7e050a4d228d, 0xa8f71b5cb84862c9, 0xb52e9a306097fde3, - 0x0d8251a35b6e2a0b, 0x2257a7fee1c442eb, 0x73831d9a29588d94, 0x51d4ba64c89ccf7f, 0x502ab7d4b54f5ba5, - 0x97793dce8153bf08, 0xe5042de4d5d8a646, 0x9687307efc802bd2, 0xa05473b5779eb657, 0xb4d097801d446939, - 0xcff0e2f3fbca3033, 0xc38cbee0dd778ee2, 0x464f499c252eb162, 0xcad1dbb96f72cea6, 0xba4dd1eec142e241, - 0xb00fa37af42f0376}, - {0xcce4cd3aa968b245, 0x089d5484e80b7faf, 0x638246c1b3548304, 0xd2fe0ec8c2355492, 0xa7fbdf7ff2374eee, - 0x4df1600c92337a16, 0x84e503ea523b12fb, 0x0790bbfd53ab0c4a, 0x198a780f38f6ea9d, 0x2ab30c8f55ec48cb, - 0xe0f7fed6b2c49db5, 0xb6ecf3f422cadbdc, 0x409c9a541358df11, 0xd3ce8a56dfde3fe3, 0xc3e9224312c8c1a0, - 0x0d6dfa58816ba507, 0xddf3e1b179952777, 0x04c02a42748bb1d9, 0x94c2abff9f2decb8, 0x4f91752da8f8acf4, - 0x78682befb169bf7b, 0xe1c77a48af2ff6c4, 0x0c5d7ec69c80ce76, 0x4cc1e4928fd81167, 0xfeed3d24d9997b62, - 0x518bb6dfc3a54a23, 0x6dbf2d26151f9b90, 0xb5bc624b05ea664f, 0xe86aaa525acfe21a, 0x4801ced0fb53a0be, - 0xc91463e6c00868ed, 0x1027a815cd16fe43, 0xf67069a0319204cd, 0xb04ccc976c8abce7, 0xc0b9b3fc35e87c33, - 0xf380c77c58f2de65, 0x50bb3241de4e2152, 0xdf93f490435ef195, 0xf1e0d25d62390887, 0xaf668bfb1a3c3141, - 0xbc11b251f00a7291, 0x73a5eed47e427d47, 0x25bee3f6ee4c3b2e, 0x43cc0beb34786282, 0xc824e778dde3039c, - 0xf97d86d98a327728, 0xf2b043e24519b514, 0xe297ebf7880f4b57, 0x3a94a49a98fab688, 0x868516cb68f0c419, - 0xeffa11af0964ee50, 0xa4ab4ec0d517f37d, 0xa9c6b498547c567a, 0x8e18424f80fbbbb6, 0x0bcdc53bcf2bc23c, - 0x137739aaea3643d0, 0x2c1333ec1bac2ff0, 0x8d48d3f0a7db0625, 0x1e1ac3f26b5de6d7, 0xf520f81f16b2b95e, - 0x9f0f6ec450062e84, 0x0130849e1deb6b71, 0xd45e31ab8c7533a9, 0x652279a2fd14e43f, 0x3209f01e70f1c927, - 0xbe71a770cac1a473, 0x0e3d6be7a64b1894, 0x7ec8148cff29d840, 0xcb7476c7fac3be0f, 0x72956a4a63a91636, - 0x37f95ec21991138f, 0x9e3fea5a4ded45f5, 0x7b38ba50964902e8, 0x222e580bbde73764, 0x61e253e0899f55e6, - 0xfc8d2805e352ad80, 0x35994be3235ac56d, 0x09add01af5e014de, 0x5e8659a6780539c6, 0xb17c48097161d796, - 0x026015213acbd6e2, 0xd1ae9f77e515e901, 0xb7dc776a3f21b0ad, 0xaba6a1b96eb78098, 0x9bcf4486248d9f5d, - 0x582666c536455efd, 0xfdbdac9bfeb9c6f1, 0xc47999be4163cdea, 0x765540081722a7ef, 0x3e548ed8ec710751, - 0x3d041f67cb51bac2, 0x7958af71ac82d40a, 0x36c9da5c047a78fe, 0xed9a048e33af38b2, 0x26ee7249c96c86bd, - 0x900281bdeba65d61, 0x11172c8bd0fd9532, 0xea0abf73600434f8, 0x42fc8f75299309f3, 0x34a9cf7d3eb1ae1c, - 0x2b838811480723ba, 0x5ce64c8742ceef24, 0x1adae9b01fd6570e, 0x3c349bf9d6bad1b3, 0x82453c891c7b75c0, - 0x97923a40b80d512b, 0x4a61dbf1c198765c, 0xb48ce6d518010d3e, 0xcfb45c858e480fd6, 0xd933cbf30d1e96ae, - 0xd70ea014ab558e3a, 0xc189376228031742, 0x9262949cd16d8b83, 0xeb3a3bed7def5f89, 0x49314a4ee6b8cbcf, - 0xdcc3652f647e4c06, 0xda635a4c2a3e2b3d, 0x470c21a940f3d35b, 0x315961a157d174b4, 0x6672e81dda3459ac, - 0x5b76f77a1165e36e, 0x445cb01667d36ec8, 0xc5491d205c88a69b, 0x456c34887a3805b9, 0xffddb9bac4721013, - 0x99af51a71e4649bf, 0xa15be01cbc7729d5, 0x52db2760e485f7b0, 0x8c78576eba306d54, 0xae560f6507d75a30, - 0x95f22f6182c687c9, 0x71c5fbf54489aba5, 0xca44f259e728d57e, 0x88b87d2ccebbdc8d, 0xbab18d32be4a15aa, - 0x8be8ec93e99b611e, 0x17b713e89ebdf209, 0xb31c5d284baa0174, 0xeeca9531148f8521, 0xb8d198138481c348, - 0x8988f9b2d350b7fc, 0xb9e11c8d996aa839, 0x5a4673e40c8e881f, 0x1687977683569978, 0xbf4123eed72acf02, - 0x4ea1f1b3b513c785, 0xe767452be16f91ff, 0x7505d1b730021a7c, 0xa59bca5ec8fc980c, 0xad069eda20f7e7a3, - 0x38f4b1bba231606a, 0x60d2d77e94743e97, 0x9affc0183966f42c, 0x248e6768f3a7505f, 0xcdd449a4b483d934, - 0x87b59255751baf68, 0x1bea6d2e023d3c7f, 0x6b1f12455b5ffcab, 0x743555292de9710d, 0xd8034f6d10f5fddf, - 0xc6198c9f7ba81b08, 0xbb8109aca3a17edb, 0xfa2d1766ad12cabb, 0xc729080166437079, 0x9c5fff7b77269317, - 0x0000000000000000, 0x15d706c9a47624eb, 0x6fdf38072fd44d72, 0x5fb6dd3865ee52b7, 0xa33bf53d86bcff37, - 0xe657c1b5fc84fa8e, 0xaa962527735cebe9, 0x39c43525bfda0b1b, 0x204e4d2a872ce186, 0x7a083ece8ba26999, - 0x554b9c9db72efbfa, 0xb22cd9b656416a05, 0x96a2bedea5e63a5a, 0x802529a826b0a322, 0x8115ad363b5bc853, - 0x8375b81701901eb1, 0x3069e53f4a3a1fc5, 0xbd2136cfede119e0, 0x18bafc91251d81ec, 0x1d4a524d4c7d5b44, - 0x05f0aedc6960daa8, 0x29e39d3072ccf558, 0x70f57f6b5962c0d4, 0x989fd53903ad22ce, 0xf84d024797d91c59, - 0x547b1803aac5908b, 0xf0d056c37fd263f6, 0xd56eb535919e58d8, 0x1c7ad6d351963035, 0x2e7326cd2167f912, - 0xac361a443d1c8cd2, 0x697f076461942a49, 0x4b515f6fdc731d2d, 0x8ad8680df4700a6f, 0x41ac1eca0eb3b460, - 0x7d988533d80965d3, 0xa8f6300649973d0b, 0x7765c4960ac9cc9e, 0x7ca801adc5e20ea2, 0xdea3700e5eb59ae4, - 0xa06b6482a19c42a4, 0x6a2f96db46b497da, 0x27def6d7d487edcc, 0x463ca5375d18b82a, 0xa6cb5be1efdc259f, - 0x53eba3fef96e9cc1, 0xce84d81b93a364a7, 0xf4107c810b59d22f, 0x333974806d1aa256, 0x0f0def79bba073e5, - 0x231edc95a00c5c15, 0xe437d494c64f2c6c, 0x91320523f64d3610, 0x67426c83c7df32dd, 0x6eefbc99323f2603, - 0x9d6f7be56acdf866, 0x5916e25b2bae358c, 0x7ff89012e2c2b331, 0x035091bf2720bd93, 0x561b0d22900e4669, - 0x28d319ae6f279e29, 0x2f43a2533c8c9263, 0xd09e1be9f8fe8270, 0xf740ed3e2c796fbc, 0xdb53ded237d5404c, - 0x62b2c25faebfe875, 0x0afd41a5d2c0a94d, 0x6412fd3ce0ff8f4e, 0xe3a76f6995e42026, 0x6c8fa9b808f4f0e1, - 0xc2d9a6dd0f23aad1, 0x8f28c6d19d10d0c7, 0x85d587744fd0798a, 0xa20b71a39b579446, 0x684f83fa7c7f4138, - 0xe507500adba4471d, 0x3f640a46f19a6c20, 0x1247bd34f7dd28a1, 0x2d23b77206474481, 0x93521002cc86e0f2, - 0x572b89bc8de52d18, 0xfb1d93f8b0f9a1ca, 0xe95a2ecc4724896b, 0x3ba420048511ddf9, 0xd63e248ab6bee54b, - 0x5dd6c8195f258455, 0x06a03f634e40673b, 0x1f2a476c76b68da6, 0x217ec9b49ac78af7, 0xecaa80102e4453c3, - 0x14e78257b99d4f9a}, - {0x20329b2cc87bba05, 0x4f5eb6f86546a531, 0xd4f44775f751b6b1, 0x8266a47b850dfa8b, 0xbb986aa15a6ca985, - 0xc979eb08f9ae0f99, 0x2da6f447a2375ea1, 0x1e74275dcd7d8576, 0xbc20180a800bc5f8, 0xb4a2f701b2dc65be, - 0xe726946f981b6d66, 0x48e6c453bf21c94c, 0x42cad9930f0a4195, 0xefa47b64aacccd20, 0x71180a8960409a42, - 0x8bb3329bf6a44e0c, 0xd34c35de2d36dacc, 0xa92f5b7cbc23dc96, 0xb31a85aa68bb09c3, 0x13e04836a73161d2, - 0xb24dfc4129c51d02, 0x8ae44b70b7da5acd, 0xe671ed84d96579a7, 0xa4bb3417d66f3832, 0x4572ab38d56d2de8, - 0xb1b47761ea47215c, 0xe81c09cf70aba15d, 0xffbdb872ce7f90ac, 0xa8782297fd5dc857, 0x0d946f6b6a4ce4a4, - 0xe4df1f4f5b995138, 0x9ebc71edca8c5762, 0x0a2c1dc0b02b88d9, 0x3b503c115d9d7b91, 0xc64376a8111ec3a2, - 0xcec199a323c963e4, 0xdc76a87ec58616f7, 0x09d596e073a9b487, 0x14583a9d7d560daf, 0xf4c6dc593f2a0cb4, - 0xdd21d19584f80236, 0x4a4836983ddde1d3, 0xe58866a41ae745f9, 0xf591a5b27e541875, 0x891dc05074586693, - 0x5b068c651810a89e, 0xa30346bc0c08544f, 0x3dbf3751c684032d, 0x2a1e86ec785032dc, 0xf73f5779fca830ea, - 0xb60c05ca30204d21, 0x0cc316802b32f065, 0x8770241bdd96be69, 0xb861e18199ee95db, 0xf805cad91418fcd1, - 0x29e70dccbbd20e82, 0xc7140f435060d763, 0x0f3a9da0e8b0cc3b, 0xa2543f574d76408e, 0xbd7761e1c175d139, - 0x4b1f4f737ca3f512, 0x6dc2df1f2fc137ab, 0xf1d05c3967b14856, 0xa742bf3715ed046c, 0x654030141d1697ed, - 0x07b872abda676c7d, 0x3ce84eba87fa17ec, 0xc1fb0403cb79afdf, 0x3e46bc7105063f73, 0x278ae987121cd678, - 0xa1adb4778ef47cd0, 0x26dd906c5362c2b9, 0x05168060589b44e2, 0xfbfc41f9d79ac08f, 0x0e6de44ba9ced8fa, - 0x9feb08068bf243a3, 0x7b341749d06b129b, 0x229c69e74a87929a, 0xe09ee6c4427c011b, 0x5692e30e725c4c3a, - 0xda99a33e5e9f6e4b, 0x353dd85af453a36b, 0x25241b4c90e0fee7, 0x5de987258309d022, 0xe230140fc0802984, - 0x93281e86a0c0b3c6, 0xf229d719a4337408, 0x6f6c2dd4ad3d1f34, 0x8ea5b2fbae3f0aee, 0x8331dd90c473ee4a, - 0x346aa1b1b52db7aa, 0xdf8f235e06042aa9, 0xcc6f6b68a1354b7b, 0x6c95a6f46ebf236a, 0x52d31a856bb91c19, - 0x1a35ded6d498d555, 0xf37eaef2e54d60c9, 0x72e181a9a3c2a61c, 0x98537aad51952fde, 0x16f6c856ffaa2530, - 0xd960281e9d1d5215, 0x3a0745fa1ce36f50, 0x0b7b642bf1559c18, 0x59a87eae9aec8001, 0x5e100c05408bec7c, - 0x0441f98b19e55023, 0xd70dcc5534d38aef, 0x927f676de1bea707, 0x9769e70db925e3e5, 0x7a636ea29115065a, - 0x468b201816ef11b6, 0xab81a9b73edff409, 0xc0ac7de88a07bb1e, 0x1f235eb68c0391b7, 0x6056b074458dd30f, - 0xbe8eeac102f7ed67, 0xcd381283e04b5fba, 0x5cbefecec277c4e3, 0xd21b4c356c48ce0d, 0x1019c31664b35d8c, - 0x247362a7d19eea26, 0xebe582efb3299d03, 0x02aef2cb82fc289f, 0x86275df09ce8aaa8, 0x28b07427faac1a43, - 0x38a9b7319e1f47cf, 0xc82e92e3b8d01b58, 0x06ef0b409b1978bc, 0x62f842bfc771fb90, 0x9904034610eb3b1f, - 0xded85ab5477a3e68, 0x90d195a663428f98, 0x5384636e2ac708d8, 0xcbd719c37b522706, 0xae9729d76644b0eb, - 0x7c8c65e20a0c7ee6, 0x80c856b007f1d214, 0x8c0b40302cc32271, 0xdbcedad51fe17a8a, 0x740e8ae938dbdea0, - 0xa615c6dc549310ad, 0x19cc55f6171ae90b, 0x49b1bdb8fe5fdd8d, 0xed0a89af2830e5bf, 0x6a7aadb4f5a65bd6, - 0x7e22972988f05679, 0xf952b3325566e810, 0x39fecedadf61530e, 0x6101c99f04f3c7ce, 0x2e5f7f6761b562ff, - 0xf08725d226cf5c97, 0x63af3b54860fef51, 0x8ff2cb10ef411e2f, 0x884ab9bb35267252, 0x4df04433e7ba8dae, - 0x9afd8866d3690741, 0x66b9bb34de94abb3, 0x9baaf18d92171380, 0x543c11c5f0a064a5, 0x17a1b1bdbed431f1, - 0xb5f58eeaf3a2717f, 0xc355f6c849858740, 0xec5df044694ef17e, 0xd83751f5dc6346d4, 0xfc4433520dfdacf2, - 0x0000000000000000, 0x5a51f58e596ebc5f, 0x3285aaf12e34cf16, 0x8d5c39db6dbd36b0, 0x12b731dde64f7513, - 0x94906c2d7aa7dfbb, 0x302b583aacc8e789, 0x9d45facd090e6b3c, 0x2165e2c78905aec4, 0x68d45f7f775a7349, - 0x189b2c1d5664fdca, 0xe1c99f2f030215da, 0x6983269436246788, 0x8489af3b1e148237, 0xe94b702431d5b59c, - 0x33d2d31a6f4adbd7, 0xbfd9932a4389f9a6, 0xb0e30e8aab39359d, 0xd1e2c715afcaf253, 0x150f43763c28196e, - 0xc4ed846393e2eb3d, 0x03f98b20c3823c5e, 0xfd134ab94c83b833, 0x556b682eb1de7064, 0x36c4537a37d19f35, - 0x7559f30279a5ca61, 0x799ae58252973a04, 0x9c12832648707ffd, 0x78cd9c6913e92ec5, 0x1d8dac7d0effb928, - 0x439da0784e745554, 0x413352b3cc887dcb, 0xbacf134a1b12bd44, 0x114ebafd25cd494d, 0x2f08068c20cb763e, - 0x76a07822ba27f63f, 0xeab2fb04f25789c2, 0xe3676de481fe3d45, 0x1b62a73d95e6c194, 0x641749ff5c68832c, - 0xa5ec4dfc97112cf3, 0xf6682e92bdd6242b, 0x3f11c59a44782bb2, 0x317c21d1edb6f348, 0xd65ab5be75ad9e2e, - 0x6b2dd45fb4d84f17, 0xfaab381296e4d44e, 0xd0b5befeeeb4e692, 0x0882ef0b32d7a046, 0x512a91a5a83b2047, - 0x963e9ee6f85bf724, 0x4e09cf132438b1f0, 0x77f701c9fb59e2fe, 0x7ddb1c094b726a27, 0x5f4775ee01f5f8bd, - 0x9186ec4d223c9b59, 0xfeeac1998f01846d, 0xac39db1ce4b89874, 0xb75b7c21715e59e0, 0xafc0503c273aa42a, - 0x6e3b543fec430bf5, 0x704f7362213e8e83, 0x58ff0745db9294c0, 0x67eec2df9feabf72, 0xa0facd9ccf8a6811, - 0xb936986ad890811a, 0x95c715c63bd9cb7a, 0xca8060283a2c33c7, 0x507de84ee9453486, 0x85ded6d05f6a96f6, - 0x1cdad5964f81ade9, 0xd5a33e9eb62fa270, 0x40642b588df6690a, 0x7f75eec2c98e42b8, 0x2cf18dace3494a60, - 0x23cb100c0bf9865b, 0xeef3028febb2d9e1, 0x4425d2d394133929, 0xaad6d05c7fa1e0c8, 0xad6ea2f7a5c68cb5, - 0xc2028f2308fb9381, 0x819f2f5b468fc6d5, 0xc5bafd88d29cfffc, 0x47dc59f357910577, 0x2b49ff07392e261d, - 0x57c59ae5332258fb, 0x73b6f842e2bcb2dd, 0xcf96e04862b77725, 0x4ca73dd8a6c4996f, 0x015779eb417e14c1, - 0x37932a9176af8bf4}, - {0x190a2c9b249df23e, 0x2f62f8b62263e1e9, 0x7a7f754740993655, 0x330b7ba4d5564d9f, 0x4c17a16a46672582, - 0xb22f08eb7d05f5b8, 0x535f47f40bc148cc, 0x3aec5d27d4883037, 0x10ed0a1825438f96, 0x516101f72c233d17, - 0x13cc6f949fd04eae, 0x739853c441474bfd, 0x653793d90d3f5b1b, 0x5240647b96b0fc2f, 0x0c84890ad27623e0, - 0xd7189b32703aaea3, 0x2685de3523bd9c41, 0x99317c5b11bffefa, 0x0d9baa854f079703, 0x70b93648fbd48ac5, - 0xa80441fce30bc6be, 0x7287704bdc36ff1e, 0xb65384ed33dc1f13, 0xd36417343ee34408, 0x39cd38ab6e1bf10f, - 0x5ab861770a1f3564, 0x0ebacf09f594563b, 0xd04572b884708530, 0x3cae9722bdb3af47, 0x4a556b6f2f5cbaf2, - 0xe1704f1f76c4bd74, 0x5ec4ed7144c6dfcf, 0x16afc01d4c7810e6, 0x283f113cd629ca7a, 0xaf59a8761741ed2d, - 0xeed5a3991e215fac, 0x3bf37ea849f984d4, 0xe413e096a56ce33c, 0x2c439d3a98f020d1, 0x637559dc6404c46b, - 0x9e6c95d1e5f5d569, 0x24bb9836045fe99a, 0x44efa466dac8ecc9, 0xc6eab2a5c80895d6, 0x803b50c035220cc4, - 0x0321658cba93c138, 0x8f9ebc465dc7ee1c, 0xd15a5137190131d3, 0x0fa5ec8668e5e2d8, 0x91c979578d1037b1, - 0x0642ca05693b9f70, 0xefca80168350eb4f, 0x38d21b24f36a45ec, 0xbeab81e1af73d658, 0x8cbfd9cae7542f24, - 0xfd19cc0d81f11102, 0x0ac6430fbb4dbc90, 0x1d76a09d6a441895, 0x2a01573ff1cbbfa1, 0xb572e161894fde2b, - 0x8124734fa853b827, 0x614b1fdf43e6b1b0, 0x68ac395c4238cc18, 0x21d837bfd7f7b7d2, 0x20c714304a860331, - 0x5cfaab726324aa14, 0x74c5ba4eb50d606e, 0xf3a3030474654739, 0x23e671bcf015c209, 0x45f087e947b9582a, - 0xd8bd77b418df4c7b, 0xe06f6c90ebb50997, 0x0bd96080263c0873, 0x7e03f9410e40dcfe, 0xb8e94be4c6484928, - 0xfb5b0608e8ca8e72, 0x1a2b49179e0e3306, 0x4e29e76961855059, 0x4f36c4e6fcf4e4ba, 0x49740ee395cf7bca, - 0xc2963ea386d17f7d, 0x90d65ad810618352, 0x12d34c1b02a1fa4d, 0xfa44258775bb3a91, 0x18150f14b9ec46dd, - 0x1491861e6b9a653d, 0x9a1019d7ab2c3fc2, 0x3668d42d06fe13d7, 0xdcc1fbb25606a6d0, 0x969490dd795a1c22, - 0x3549b1a1bc6dd2ef, 0xc94f5e23a0ed770e, 0xb9f6686b5b39fdcb, 0xc4d4f4a6efeae00d, 0xe732851a1fff2204, - 0x94aad6de5eb869f9, 0x3f8ff2ae07206e7f, 0xfe38a9813b62d03a, 0xa7a1ad7a8bee2466, 0x7b6056c8dde882b6, - 0x302a1e286fc58ca7, 0x8da0fa457a259bc7, 0xb3302b64e074415b, 0x5402ae7eff8b635f, 0x08f8050c9cafc94b, - 0xae468bf98a3059ce, 0x88c355cca98dc58f, 0xb10e6d67c7963480, 0xbad70de7e1aa3cf3, 0xbfb4a26e320262bb, - 0xcb711820870f02d5, 0xce12b7a954a75c9d, 0x563ce87dd8691684, 0x9f73b65e7884618a, 0x2b1e74b06cba0b42, - 0x47cec1ea605b2df1, 0x1c698312f735ac76, 0x5fdbcefed9b76b2c, 0x831a354c8fb1cdfc, 0x820516c312c0791f, - 0xb74ca762aeadabf0, 0xfc06ef821c80a5e1, 0x5723cbf24518a267, 0x9d4df05d5f661451, 0x588627742dfd40bf, - 0xda8331b73f3d39a0, 0x17b0e392d109a405, 0xf965400bcf28fba9, 0x7c3dbf4229a2a925, 0x023e460327e275db, - 0x6cd0b55a0ce126b3, 0xe62da695828e96e7, 0x42ad6e63b3f373b9, 0xe50cc319381d57df, 0xc5cbd729729b54ee, - 0x46d1e265fd2a9912, 0x6428b056904eeff8, 0x8be23040131e04b7, 0x6709d5da2add2ec0, 0x075de98af44a2b93, - 0x8447dcc67bfbe66f, 0x6616f655b7ac9a23, 0xd607b8bded4b1a40, 0x0563af89d3a85e48, 0x3db1b4ad20c21ba4, - 0x11f22997b8323b75, 0x292032b34b587e99, 0x7f1cdace9331681d, 0x8e819fc9c0b65aff, 0xa1e3677fe2d5bb16, - 0xcd33d225ee349da5, 0xd9a2543b85aef898, 0x795e10cbfa0af76d, 0x25a4bbb9992e5d79, 0x78413344677b438e, - 0xf0826688cef68601, 0xd27b34bba392f0eb, 0x551d8df162fad7bc, 0x1e57c511d0d7d9ad, 0xdeffbdb171e4d30b, - 0xf4feea8e802f6caa, 0xa480c8f6317de55e, 0xa0fc44f07fa40ff5, 0x95b5f551c3c9dd1a, 0x22f952336d6476ea, - 0x0000000000000000, 0xa6be8ef5169f9085, 0xcc2cf1aa73452946, 0x2e7ddb39bf12550a, 0xd526dd3157d8db78, - 0x486b2d6c08becf29, 0x9b0f3a58365d8b21, 0xac78cdfaadd22c15, 0xbc95c7e28891a383, 0x6a927f5f65dab9c3, - 0xc3891d2c1ba0cb9e, 0xeaa92f9f50f8b507, 0xcf0d9426c9d6e87e, 0xca6e3baf1a7eb636, 0xab25247059980786, - 0x69b31ad3df4978fb, 0xe2512a93cc577c4c, 0xff278a0ea61364d9, 0x71a615c766a53e26, 0x89dc764334fc716c, - 0xf87a638452594f4a, 0xf2bc208be914f3da, 0x8766b94ac1682757, 0xbbc82e687cdb8810, 0x626a7a53f9757088, - 0xa2c202f358467a2e, 0x4d0882e5db169161, 0x09e7268301de7da8, 0xe897699c771ac0dc, 0xc8507dac3d9cc3ed, - 0xc0a878a0a1330aa6, 0x978bb352e42ba8c1, 0xe9884a13ea6b743f, 0x279afdbabecc28a2, 0x047c8c064ed9eaab, - 0x507e2278b15289f4, 0x599904fbb08cf45c, 0xbd8ae46d15e01760, 0x31353da7f2b43844, 0x8558ff49e68a528c, - 0x76fbfc4d92ef15b5, 0x3456922e211c660c, 0x86799ac55c1993b4, 0x3e90d1219a51da9c, 0x2d5cbeb505819432, - 0x982e5fd48cce4a19, 0xdb9c1238a24c8d43, 0xd439febecaa96f9b, 0x418c0bef0960b281, 0x158ea591f6ebd1de, - 0x1f48e69e4da66d4e, 0x8afd13cf8e6fb054, 0xf5e1c9011d5ed849, 0xe34e091c5126c8af, 0xad67ee7530a398f6, - 0x43b24dec2e82c75a, 0x75da99c1287cd48d, 0x92e81cdb3783f689, 0xa3dd217cc537cecd, 0x60543c50de970553, - 0x93f73f54aaf2426a, 0xa91b62737e7a725d, 0xf19d4507538732e2, 0x77e4dfc20f9ea156, 0x7d229ccdb4d31dc6, - 0x1b346a98037f87e5, 0xedf4c615a4b29e94, 0x4093286094110662, 0xb0114ee85ae78063, 0x6ff1d0d6b672e78b, - 0x6dcf96d591909250, 0xdfe09e3eec9567e8, 0x3214582b4827f97c, 0xb46dc2ee143e6ac8, 0xf6c0ac8da7cd1971, - 0xebb60c10cd8901e4, 0xf7df8f023abcad92, 0x9c52d3d2c217a0b2, 0x6b8d5cd0f8ab0d20, 0x3777f7a29b8fa734, - 0x011f238f9d71b4e3, 0xc1b75b2f3c42be45, 0x5de588fdfe551ef7, 0x6eeef3592b035368, 0xaa3a07ffc4e9b365, - 0xecebe59a39c32a77, 0x5ba742f8976e8187, 0x4b4a48e0b22d0e11, 0xddded83dcb771233, 0xa59feb79ac0c51bd, - 0xc7f5912a55792135}, - {0x6d6ae04668a9b08a, 0x3ab3f04b0be8c743, 0xe51e166b54b3c908, 0xbe90a9eb35c2f139, 0xb2c7066637f2bec1, - 0xaa6945613392202c, 0x9a28c36f3b5201eb, 0xddce5a93ab536994, 0x0e34133ef6382827, 0x52a02ba1ec55048b, - 0xa2f88f97c4b2a177, 0x8640e513ca2251a5, 0xcdf1d36258137622, 0xfe6cb708dedf8ddb, 0x8a174a9ec8121e5d, - 0x679896036b81560e, 0x59ed033395795fee, 0x1dd778ab8b74edaf, 0xee533ef92d9f926d, 0x2a8c79baf8a8d8f5, - 0x6bcf398e69b119f6, 0xe20491742fafdd95, 0x276488e0809c2aec, 0xea955b82d88f5cce, 0x7102c63a99d9e0c4, - 0xf9763017a5c39946, 0x429fa2501f151b3d, 0x4659c72bea05d59e, 0x984b7fdccf5a6634, 0xf742232953fbb161, - 0x3041860e08c021c7, 0x747bfd9616cd9386, 0x4bb1367192312787, 0x1b72a1638a6c44d3, 0x4a0e68a6e8359a66, - 0x169a5039f258b6ca, 0xb98a2ef44edee5a4, 0xd9083fe85e43a737, 0x967f6ce239624e13, 0x8874f62d3c1a7982, - 0x3c1629830af06e3f, 0x9165ebfd427e5a8e, 0xb5dd81794ceeaa5c, 0x0de8f15a7834f219, 0x70bd98ede3dd5d25, - 0xaccc9ca9328a8950, 0x56664eda1945ca28, 0x221db34c0f8859ae, 0x26dbd637fa98970d, 0x1acdffb4f068f932, - 0x4585254f64090fa0, 0x72de245e17d53afa, 0x1546b25d7c546cf4, 0x207e0ffffb803e71, 0xfaaad2732bcf4378, - 0xb462dfae36ea17bd, 0xcf926fd1ac1b11fd, 0xe0672dc7dba7ba4a, 0xd3fa49ad5d6b41b3, 0x8ba81449b216a3bc, - 0x14f9ec8a0650d115, 0x40fc1ee3eb1d7ce2, 0x23a2ed9b758ce44f, 0x782c521b14fddc7e, 0x1c68267cf170504e, - 0xbcf31558c1ca96e6, 0xa781b43b4ba6d235, 0xf6fd7dfe29ff0c80, 0xb0a4bad5c3fad91e, 0xd199f51ea963266c, - 0x414340349119c103, 0x5405f269ed4dadf7, 0xabd61bb649969dcd, 0x6813dbeae7bdc3c8, 0x65fb2ab09f8931d1, - 0xf1e7fae152e3181d, 0xc1a67cef5a2339da, 0x7a4feea8e0f5bba1, 0x1e0b9acf05783791, 0x5b8ebf8061713831, - 0x80e53cdbcb3af8d9, 0x7e898bd315e57502, 0xc6bcfbf0213f2d47, 0x95a38e86b76e942d, 0x092e94218d243cba, - 0x8339debf453622e7, 0xb11be402b9fe64ff, 0x57d9100d634177c9, 0xcc4e8db52217cbc3, 0x3b0cae9c71ec7aa2, - 0xfb158ca451cbfe99, 0x2b33276d82ac6514, 0x01bf5ed77a04bde1, 0xc5601994af33f779, 0x75c4a3416cc92e67, - 0xf3844652a6eb7fc2, 0x3487e375fdd0ef64, 0x18ae430704609eed, 0x4d14efb993298efb, 0x815a620cb13e4538, - 0x125c354207487869, 0x9eeea614ce42cf48, 0xce2d3106d61fac1c, 0xbbe99247bad6827b, 0x071a871f7b1c149d, - 0x2e4a1cc10db81656, 0x77a71ff298c149b8, 0x06a5d9c80118a97c, 0xad73c27e488e34b1, 0x443a7b981e0db241, - 0xe3bbcfa355ab6074, 0x0af276450328e684, 0x73617a896dd1871b, 0x58525de4ef7de20f, 0xb7be3dcab8e6cd83, - 0x19111dd07e64230c, 0x842359a03e2a367a, 0x103f89f1f3401fb6, 0xdc710444d157d475, 0xb835702334da5845, - 0x4320fc876511a6dc, 0xd026abc9d3679b8d, 0x17250eee885c0b2b, 0x90dab52a387ae76f, 0x31fed8d972c49c26, - 0x89cba8fa461ec463, 0x2ff5421677bcabb7, 0x396f122f85e41d7d, 0xa09b332430bac6a8, 0xc888e8ced7070560, - 0xaeaf201ac682ee8f, 0x1180d7268944a257, 0xf058a43628e7a5fc, 0xbd4c4b8fbbce2b07, 0xa1246df34abe7b49, - 0x7d5569b79be9af3c, 0xa9b5a705bd9efa12, 0xdb6b835baa4bc0e8, 0x05793bac8f147342, 0x21c1512881848390, - 0xfdb0556c50d357e5, 0x613d4fcb6a99ff72, 0x03dce2648e0cda3e, 0xe949b9e6568386f0, 0xfc0f0bbb2ad7ea04, - 0x6a70675913b5a417, 0x7f36d5046fe1c8e3, 0x0c57af8d02304ff8, 0x32223abdfcc84618, 0x0891caf6f720815b, - 0xa63eeaec31a26fd4, 0x2507345374944d33, 0x49d28ac266394058, 0xf5219f9aa7f3d6be, 0x2d96fea583b4cc68, - 0x5a31e1571b7585d0, 0x8ed12fe53d02d0fe, 0xdfade6205f5b0e4b, 0x4cabb16ee92d331a, 0x04c6657bf510cea3, - 0xd73c2cd6a87b8f10, 0xe1d87310a1a307ab, 0x6cd5be9112ad0d6b, 0x97c032354366f3f2, 0xd4e0ceb22677552e, - 0x0000000000000000, 0x29509bde76a402cb, 0xc27a9e8bd42fe3e4, 0x5ef7842cee654b73, 0xaf107ecdbc86536e, - 0x3fcacbe784fcb401, 0xd55f90655c73e8cf, 0xe6c2f40fdabf1336, 0xe8f6e7312c873b11, 0xeb2a0555a28be12f, - 0xe4a148bc2eb774e9, 0x9b979db84156bc0a, 0x6eb60222e6a56ab4, 0x87ffbbc4b026ec44, 0xc703a5275b3b90a6, - 0x47e699fc9001687f, 0x9c8d1aa73a4aa897, 0x7cea3760e1ed12dd, 0x4ec80ddd1d2554c5, 0x13e36b957d4cc588, - 0x5d2b66486069914d, 0x92b90999cc7280b0, 0x517cc9c56259deb5, 0xc937b619ad03b881, 0xec30824ad997f5b2, - 0xa45d565fc5aa080b, 0xd6837201d27f32f1, 0x635ef3789e9198ad, 0x531f75769651b96a, 0x4f77530a6721e924, - 0x486dd4151c3dfdb9, 0x5f48dafb9461f692, 0x375b011173dc355a, 0x3da9775470f4d3de, 0x8d0dcd81b30e0ac0, - 0x36e45fc609d888bb, 0x55baacbe97491016, 0x8cb29356c90ab721, 0x76184125e2c5f459, 0x99f4210bb55edbd5, - 0x6f095cf59ca1d755, 0x9f51f8c3b44672a9, 0x3538bda287d45285, 0x50c39712185d6354, 0xf23b1885dcefc223, - 0x79930ccc6ef9619f, 0xed8fdc9da3934853, 0xcb540aaa590bdf5e, 0x5c94389f1a6d2cac, 0xe77daad8a0bbaed7, - 0x28efc5090ca0bf2a, 0xbf2ff73c4fc64cd8, 0xb37858b14df60320, 0xf8c96ec0dfc724a7, 0x828680683f329f06, - 0x941cd051cd6a29cc, 0xc3c5c05cae2b5e05, 0xb601631dc2e27062, 0xc01922382027843b, 0x24b86a840e90f0d2, - 0xd245177a276ffc52, 0x0f8b4de98c3c95c6, 0x3e759530fef809e0, 0x0b4d2892792c5b65, 0xc4df4743d5374a98, - 0xa5e20888bfaeb5ea, 0xba56cc90c0d23f9a, 0x38d04cf8ffe0a09c, 0x62e1adafe495254c, 0x0263bcb3f40867df, - 0xcaeb547d230f62bf, 0x6082111c109d4293, 0xdad4dd8cd04f7d09, 0xefec602e579b2f8c, 0x1fb4c4187f7c8a70, - 0xffd3e9dfa4db303a, 0x7bf0b07f9af10640, 0xf49ec14dddf76b5f, 0x8f6e713247066d1f, 0x339d646a86ccfbf9, - 0x64447467e58d8c30, 0x2c29a072f9b07189, 0xd8b7613f24471ad6, 0x6627c8d41185ebef, 0xa347d140beb61c96, - 0xde12b8f7255fb3aa, 0x9d324470404e1576, 0x9306574eb6763d51, 0xa80af9d2c79a47f3, 0x859c0777442e8b9b, - 0x69ac853d9db97e29}, - {0xc3407dfc2de6377e, 0x5b9e93eea4256f77, 0xadb58fdd50c845e0, 0x5219ff11a75bed86, 0x356b61cfd90b1de9, - 0xfb8f406e25abe037, 0x7a5a0231c0f60796, 0x9d3cd216e1f5020b, 0x0c6550fb6b48d8f3, 0xf57508c427ff1c62, - 0x4ad35ffa71cb407d, 0x6290a2da1666aa6d, 0xe284ec2349355f9f, 0xb3c307c53d7c84ec, 0x05e23c0468365a02, - 0x190bac4d6c9ebfa8, 0x94bbbee9e28b80fa, 0xa34fc777529cb9b5, 0xcc7b39f095bcd978, 0x2426addb0ce532e3, - 0x7e79329312ce4fc7, 0xab09a72eebec2917, 0xf8d15499f6b9d6c2, 0x1a55b8babf8c895d, 0xdb8add17fb769a85, - 0xb57f2f368658e81b, 0x8acd36f18f3f41f6, 0x5ce3b7bba50f11d3, 0x114dcc14d5ee2f0a, 0xb91a7fcded1030e8, - 0x81d5425fe55de7a1, 0xb6213bc1554adeee, 0x80144ef95f53f5f2, 0x1e7688186db4c10c, 0x3b912965db5fe1bc, - 0xc281715a97e8252d, 0x54a5d7e21c7f8171, 0x4b12535ccbc5522e, 0x1d289cefbea6f7f9, 0x6ef5f2217d2e729e, - 0xe6a7dc819b0d17ce, 0x1b94b41c05829b0e, 0x33d7493c622f711e, 0xdcf7f942fa5ce421, 0x600fba8b7f7a8ecb, - 0x46b60f011a83988e, 0x235b898e0dcf4c47, 0x957ab24f588592a9, 0x4354330572b5c28c, 0xa5f3ef84e9b8d542, - 0x8c711e02341b2d01, 0x0b1874ae6a62a657, 0x1213d8e306fc19ff, 0xfe6d7c6a4d9dba35, 0x65ed868f174cd4c9, - 0x88522ea0e6236550, 0x899322065c2d7703, 0xc01e690bfef4018b, 0x915982ed8abddaf8, 0xbe675b98ec3a4e4c, - 0xa996bf7f82f00db1, 0xe1daf8d49a27696a, 0x2effd5d3dc8986e7, 0xd153a51f2b1a2e81, 0x18caa0ebd690adfb, - 0x390e3134b243c51a, 0x2778b92cdff70416, 0x029f1851691c24a6, 0x5e7cafeacc133575, 0xfa4e4cc89fa5f264, - 0x5a5f9f481e2b7d24, 0x484c47ab18d764db, 0x400a27f2a1a7f479, 0xaeeb9b2a83da7315, 0x721c626879869734, - 0x042330a2d2384851, 0x85f672fd3765aff0, 0xba446b3a3e02061d, 0x73dd6ecec3888567, 0xffac70ccf793a866, - 0xdfa9edb5294ed2d4, 0x6c6aea7014325638, 0x834a5a0e8c41c307, 0xcdba35562fb2cb2b, 0x0ad97808d06cb404, - 0x0f3b440cb85aee06, 0xe5f9c876481f213b, 0x98deee1289c35809, 0x59018bbfcd394bd1, 0xe01bf47220297b39, - 0xde68e1139340c087, 0x9fa3ca4788e926ad, 0xbb85679c840c144e, 0x53d8f3b71d55ffd5, 0x0da45c5dd146caa0, - 0x6f34fe87c72060cd, 0x57fbc315cf6db784, 0xcee421a1fca0fdde, 0x3d2d0196607b8d4b, 0x642c8a29ad42c69a, - 0x14aff010bdd87508, 0xac74837beac657b3, 0x3216459ad821634d, 0x3fb219c70967a9ed, 0x06bc28f3bb246cf7, - 0xf2082c9126d562c6, 0x66b39278c45ee23c, 0xbd394f6f3f2878b9, 0xfd33689d9e8f8cc0, 0x37f4799eb017394f, - 0x108cc0b26fe03d59, 0xda4bd1b1417888d6, 0xb09d1332ee6eb219, 0x2f3ed975668794b4, 0x58c0871977375982, - 0x7561463d78ace990, 0x09876cff037e82f1, 0x7fb83e35a8c05d94, 0x26b9b58a65f91645, 0xef20b07e9873953f, - 0x3148516d0b3355b8, 0x41cb2b541ba9e62a, 0x790416c613e43163, 0xa011d380818e8f40, 0x3a5025c36151f3ef, - 0xd57095bdf92266d0, 0x498d4b0da2d97688, 0x8b0c3a57353153a5, 0x21c491df64d368e1, 0x8f2f0af5e7091bf4, - 0x2da1c1240f9bb012, 0xc43d59a92ccc49da, 0xbfa6573e56345c1f, 0x828b56a8364fd154, 0x9a41f643e0df7caf, - 0xbcf843c985266aea, 0x2b1de9d7b4bfdce5, 0x20059d79dedd7ab2, 0x6dabe6d6ae3c446b, 0x45e81bf6c991ae7b, - 0x6351ae7cac68b83e, 0xa432e32253b6c711, 0xd092a9b991143cd2, 0xcac711032e98b58f, 0xd8d4c9e02864ac70, - 0xc5fc550f96c25b89, 0xd7ef8dec903e4276, 0x67729ede7e50f06f, 0xeac28c7af045cf3d, 0xb15c1f945460a04a, - 0x9cfddeb05bfb1058, 0x93c69abce3a1fe5e, 0xeb0380dc4a4bdd6e, 0xd20db1e8f8081874, 0x229a8528b7c15e14, - 0x44291750739fbc28, 0xd3ccbd4e42060a27, 0xf62b1c33f4ed2a97, 0x86a8660ae4779905, 0xd62e814a2a305025, - 0x477703a7a08d8add, 0x7b9b0e977af815c5, 0x78c51a60a9ea2330, 0xa6adfb733aaae3b7, 0x97e5aa1e3199b60f, - 0x0000000000000000, 0xf4b404629df10e31, 0x5564db44a6719322, 0x9207961a59afec0d, 0x9624a6b88b97a45c, - 0x363575380a192b1c, 0x2c60cd82b595a241, 0x7d272664c1dc7932, 0x7142769faa94a1c1, 0xa1d0df263b809d13, - 0x1630e841d4c451ae, 0xc1df65ad44fa13d8, 0x13d2d445bcf20bac, 0xd915c546926abe23, 0x38cf3d92084dd749, - 0xe766d0272103059d, 0xc7634d5effde7f2f, 0x077d2455012a7ea4, 0xedbfa82ff16fb199, 0xaf2a978c39d46146, - 0x42953fa3c8bbd0df, 0xcb061da59496a7dc, 0x25e7a17db6eb20b0, 0x34aa6d6963050fba, 0xa76cf7d580a4f1e4, - 0xf7ea10954ee338c4, 0xfcf2643b24819e93, 0xcf252d0746aeef8d, 0x4ef06f58a3f3082c, 0x563acfb37563a5d7, - 0x5086e740ce47c920, 0x2982f186dda3f843, 0x87696aac5e798b56, 0x5d22bb1d1f010380, 0x035e14f7d31236f5, - 0x3cec0d30da759f18, 0xf3c920379cdb7095, 0xb8db736b571e22bb, 0xdd36f5e44052f672, 0xaac8ab8851e23b44, - 0xa857b3d938fe1fe2, 0x17f1e4e76eca43fd, 0xec7ea4894b61a3ca, 0x9e62c6e132e734fe, 0xd4b1991b432c7483, - 0x6ad6c283af163acf, 0x1ce9904904a8e5aa, 0x5fbda34c761d2726, 0xf910583f4cb7c491, 0xc6a241f845d06d7c, - 0x4f3163fe19fd1a7f, 0xe99c988d2357f9c8, 0x8eee06535d0709a7, 0x0efa48aa0254fc55, 0xb4be23903c56fa48, - 0x763f52caabbedf65, 0xeee1bcd8227d876c, 0xe345e085f33b4dcc, 0x3e731561b369bbbe, 0x2843fd2067adea10, - 0x2adce5710eb1ceb6, 0xb7e03767ef44ccbd, 0x8db012a48e153f52, 0x61ceb62dc5749c98, 0xe85d942b9959eb9b, - 0x4c6f7709caef2c8a, 0x84377e5b8d6bbda3, 0x30895dcbb13d47eb, 0x74a04a9bc2a2fbc3, 0x6b17ce251518289c, - 0xe438c4d0f2113368, 0x1fb784bed7bad35f, 0x9b80fae55ad16efc, 0x77fe5e6c11b0cd36, 0xc858095247849129, - 0x08466059b97090a2, 0x01c10ca6ba0e1253, 0x6988d6747c040c3a, 0x6849dad2c60a1e69, 0x5147ebe67449db73, - 0xc99905f4fd8a837a, 0x991fe2b433cd4a5a, 0xf09734c04fc94660, 0xa28ecbd1e892abe6, 0xf1563866f5c75433, - 0x4dae7baf70e13ed9, 0x7ce62ac27bd26b61, 0x70837a39109ab392, 0x90988e4b30b3c8ab, 0xb2020b63877296bf, - 0x156efcb607d6675b}, - {0xe63f55ce97c331d0, 0x25b506b0015bba16, 0xc8706e29e6ad9ba8, 0x5b43d3775d521f6a, 0x0bfa3d577035106e, - 0xab95fc172afb0e66, 0xf64b63979e7a3276, 0xf58b4562649dad4b, 0x48f7c3dbae0c83f1, 0xff31916642f5c8c5, - 0xcbb048dc1c4a0495, 0x66b8f83cdf622989, 0x35c130e908e2b9b0, 0x7c761a61f0b34fa1, 0x3601161cf205268d, - 0x9e54ccfe2219b7d6, 0x8b7d90a538940837, 0x9cd403588ea35d0b, 0xbc3c6fea9ccc5b5a, 0xe5ff733b6d24aeed, - 0xceed22de0f7eb8d2, 0xec8581cab1ab545e, 0xb96105e88ff8e71d, 0x8ca03501871a5ead, 0x76ccce65d6db2a2f, - 0x5883f582a7b58057, 0x3f7be4ed2e8adc3e, 0x0fe7be06355cd9c9, 0xee054e6c1d11be83, 0x1074365909b903a6, - 0x5dde9f80b4813c10, 0x4a770c7d02b6692c, 0x5379c8d5d7809039, 0xb4067448161ed409, 0x5f5e5026183bd6cd, - 0xe898029bf4c29df9, 0x7fb63c940a54d09c, 0xc5171f897f4ba8bc, 0xa6f28db7b31d3d72, 0x2e4f3be7716eaa78, - 0x0d6771a099e63314, 0x82076254e41bf284, 0x2f0fd2b42733df98, 0x5c9e76d3e2dc49f0, 0x7aeb569619606cdb, - 0x83478b07b2468764, 0xcfadcb8d5923cd32, 0x85dac7f05b95a41e, 0xb5469d1b4043a1e9, 0xb821ecbbd9a592fd, - 0x1b8e0b0e798c13c8, 0x62a57b6d9a0be02e, 0xfcf1b793b81257f8, 0x9d94ea0bd8fe28eb, 0x4cea408aeb654a56, - 0x23284a47e888996c, 0x2d8f1d128b893545, 0xf4cbac3132c0d8ab, 0xbd7c86b9ca912eba, 0x3a268eef3dbe6079, - 0xf0d62f6077a9110c, 0x2735c916ade150cb, 0x89fd5f03942ee2ea, 0x1acee25d2fd16628, 0x90f39bab41181bff, - 0x430dfe8cde39939f, 0xf70b8ac4c8274796, 0x1c53aeaac6024552, 0x13b410acf35e9c9b, 0xa532ab4249faa24f, - 0x2b1251e5625a163f, 0xd7e3e676da4841c7, 0xa7b264e4e5404892, 0xda8497d643ae72d3, 0x861ae105a1723b23, - 0x38a6414991048aa4, 0x6578dec92585b6b4, 0x0280cfa6acbaeadd, 0x88bdb650c273970a, 0x9333bd5ebbff84c2, - 0x4e6a8f2c47dfa08b, 0x321c954db76cef2a, 0x418d312a72837942, 0xb29b38bfffcdf773, 0x6c022c38f90a4c07, - 0x5a033a240b0f6a8a, 0x1f93885f3ce5da6f, 0xc38a537e96988bc6, 0x39e6a81ac759ff44, 0x29929e43cee0fce2, - 0x40cdd87924de0ca2, 0xe9d8ebc8a29fe819, 0x0c2798f3cfbb46f4, 0x55e484223e53b343, 0x4650948ecd0d2fd8, - 0x20e86cb2126f0651, 0x6d42c56baf5739e7, 0xa06fc1405ace1e08, 0x7babbfc54f3d193b, 0x424d17df8864e67f, - 0xd8045870ef14980e, 0xc6d7397c85ac3781, 0x21a885e1443273b1, 0x67f8116f893f5c69, 0x24f5efe35706cff6, - 0xd56329d076f2ab1a, 0x5e1eb9754e66a32d, 0x28d2771098bd8902, 0x8f6013f47dfdc190, 0x17a993fdb637553c, - 0xe0a219397e1012aa, 0x786b9930b5da8606, 0x6e82e39e55b0a6da, 0x875a0856f72f4ec3, 0x3741ff4fa458536d, - 0xac4859b3957558fc, 0x7ef6d5c75c09a57c, 0xc04a758b6c7f14fb, 0xf9acdd91ab26ebbf, 0x7391a467c5ef9668, - 0x335c7c1ee1319aca, 0xa91533b18641e4bb, 0xe4bf9a683b79db0d, 0x8e20faa72ba0b470, 0x51f907737b3a7ae4, - 0x2268a314bed5ec8c, 0xd944b123b949edee, 0x31dcb3b84d8b7017, 0xd3fe65279f218860, 0x097af2f1dc8ffab3, - 0x9b09a6fc312d0b91, 0xcc6ded78a3c4520f, 0x3481d9ba5ebfcc50, 0x4f2a667f1182d56b, 0xdfd9fdd4509ace94, - 0x26752045fbbc252b, 0xbffc491f662bc467, 0xdd593272fc202449, 0x3cbbc218d46d4303, 0x91b372f817456e1f, - 0x681faf69bc6385a0, 0xb686bbeebaa43ed4, 0x1469b5084cd0ca01, 0x98c98009cbca94ac, 0x6438379a73d8c354, - 0xc2caba2dc0c5fe26, 0x3e3b0dbe78d7a9de, 0x50b9ee202d670f04, 0x4590b27b37eab0e5, 0x6025b4cb36b10af3, - 0xfb2c1237079c0162, 0xa12f28130c936be8, 0x4b37e52e54eb1ccc, 0x083a1ba28ad28f53, 0xc10a9cd83a22611b, - 0x9f1425ad7444c236, 0x069d4cf7e9d3237a, 0xedc56899e7f621be, 0x778c273680865fcf, 0x309c5aeb1bd605f7, - 0x8de0dc52d1472b4d, 0xf8ec34c2fd7b9e5f, 0xea18cd3d58787724, 0xaad515447ca67b86, 0x9989695a9d97e14c, - 0x0000000000000000, 0xf196c63321f464ec, 0x71116bc169557cb5, 0xaf887f466f92c7c1, 0x972e3e0ffe964d65, - 0x190ec4a8d536f915, 0x95aef1a9522ca7b8, 0xdc19db21aa7d51a9, 0x94ee18fa0471d258, 0x8087adf248a11859, - 0xc457f6da2916dd5c, 0xfa6cfb6451c17482, 0xf256e0c6db13fbd1, 0x6a9f60cf10d96f7d, 0x4daaa9d9bd383fb6, - 0x03c026f5fae79f3d, 0xde99148706c7bb74, 0x2a52b8b6340763df, 0x6fc20acd03edd33a, 0xd423c08320afdefa, - 0xbbe1ca4e23420dc0, 0x966ed75ca8cb3885, 0xeb58246e0e2502c4, 0x055d6a021334bc47, 0xa47242111fa7d7af, - 0xe3623fcc84f78d97, 0x81c744a11efc6db9, 0xaec8961539cfb221, 0xf31609958d4e8e31, 0x63e5923ecc5695ce, - 0x47107ddd9b505a38, 0xa3afe7b5a0298135, 0x792b7063e387f3e6, 0x0140e953565d75e0, 0x12f4f9ffa503e97b, - 0x750ce8902c3cb512, 0xdbc47e8515f30733, 0x1ed3610c6ab8af8f, 0x5239218681dde5d9, 0xe222d69fd2aaf877, - 0xfe71783514a8bd25, 0xcaf0a18f4a177175, 0x61655d9860ec7f13, 0xe77fbc9dc19e4430, 0x2ccff441ddd440a5, - 0x16e97aaee06a20dc, 0xa855dae2d01c915b, 0x1d1347f9905f30b2, 0xb7c652bdecf94b34, 0xd03e43d265c6175d, - 0xfdb15ec0ee4f2218, 0x57644b8492e9599e, 0x07dda5a4bf8e569a, 0x54a46d71680ec6a3, 0x5624a2d7c4b42c7e, - 0xbebca04c3076b187, 0x7d36f332a6ee3a41, 0x3b6667bc6be31599, 0x695f463aea3ef040, 0xad08b0e0c3282d1c, - 0xb15b1e4a052a684e, 0x44d05b2861b7c505, 0x15295c5b1a8dbfe1, 0x744c01c37a61c0f2, 0x59c31cd1f1e8f5b7, - 0xef45a73f4b4ccb63, 0x6bdf899c46841a9d, 0x3dfb2b4b823036e3, 0xa2ef0ee6f674f4d5, 0x184e2dfb836b8cf5, - 0x1134df0a5fe47646, 0xbaa1231d751f7820, 0xd17eaa81339b62bd, 0xb01bf71953771dae, 0x849a2ea30dc8d1fe, - 0x705182923f080955, 0x0ea757556301ac29, 0x041d83514569c9a7, 0x0abad4042668658e, 0x49b72a88f851f611, - 0x8a3d79f66ec97dd7, 0xcd2d042bf59927ef, 0xc930877ab0f0ee48, 0x9273540deda2f122, 0xc797d02fd3f14261, - 0xe1e2f06a284d674a, 0xd2be8c74c97cfd80, 0x9a494faf67707e71, 0xb3dbd1eca9908293, 0x72d14d3493b2e388, - 0xd6a30f258c153427}}; - -extern const uint64_t STREEBOG_C[12][8] = {{0xdd806559f2a64507, - 0x05767436cc744d23, - 0xa2422a08a460d315, - 0x4b7ce09192676901, - 0x714eb88d7585c4fc, - 0x2f6a76432e45d016, - 0xebcb2f81c0657c1f, - 0xb1085bda1ecadae9}, - {0xe679047021b19bb7, - 0x55dda21bd7cbcd56, - 0x5cb561c2db0aa7ca, - 0x9ab5176b12d69958, - 0x61d55e0f16b50131, - 0xf3feea720a232b98, - 0x4fe39d460f70b5d7, - 0x6fa3b58aa99d2f1a}, - {0x991e96f50aba0ab2, - 0xc2b6f443867adb31, - 0xc1c93a376062db09, - 0xd3e20fe490359eb1, - 0xf2ea7514b1297b7b, - 0x06f15e5f529c1f8b, - 0x0a39fc286a3d8435, - 0xf574dcac2bce2fc7}, - {0x220cbebc84e3d12e, - 0x3453eaa193e837f1, - 0xd8b71333935203be, - 0xa9d72c82ed03d675, - 0x9d721cad685e353f, - 0x488e857e335c3c7d, - 0xf948e1a05d71e4dd, - 0xef1fdfb3e81566d2}, - {0x601758fd7c6cfe57, - 0x7a56a27ea9ea63f5, - 0xdfff00b723271a16, - 0xbfcd1747253af5a3, - 0x359e35d7800fffbd, - 0x7f151c1f1686104a, - 0x9a3f410c6ca92363, - 0x4bea6bacad474799}, - {0xfa68407a46647d6e, - 0xbf71c57236904f35, - 0x0af21f66c2bec6b6, - 0xcffaa6b71c9ab7b4, - 0x187f9ab49af08ec6, - 0x2d66c4f95142a46c, - 0x6fa4c33b7a3039c0, - 0xae4faeae1d3ad3d9}, - {0x8886564d3a14d493, - 0x3517454ca23c4af3, - 0x06476983284a0504, - 0x0992abc52d822c37, - 0xd3473e33197a93c9, - 0x399ec6c7e6bf87c9, - 0x51ac86febf240954, - 0xf4c70e16eeaac5ec}, - {0xa47f0dd4bf02e71e, - 0x36acc2355951a8d9, - 0x69d18d2bd1a5c42f, - 0xf4892bcb929b0690, - 0x89b4443b4ddbc49a, - 0x4eb7f8719c36de1e, - 0x03e7aa020c6e4141, - 0x9b1f5b424d93c9a7}, - {0x7261445183235adb, - 0x0e38dc92cb1f2a60, - 0x7b2b8a9aa6079c54, - 0x800a440bdbb2ceb1, - 0x3cd955b7e00d0984, - 0x3a7d3a1b25894224, - 0x944c9ad8ec165fde, - 0x378f5a541631229b}, - {0x74b4c7fb98459ced, - 0x3698fad1153bb6c3, - 0x7a1e6c303b7652f4, - 0x9fe76702af69334b, - 0x1fffe18a1b336103, - 0x8941e71cff8a78db, - 0x382ae548b2e4f3f3, - 0xabbedea680056f52}, - {0x6bcaa4cd81f32d1b, - 0xdea2594ac06fd85d, - 0xefbacd1d7d476e98, - 0x8a1d71efea48b9ca, - 0x2001802114846679, - 0xd8fa6bbbebab0761, - 0x3002c6cd635afe94, - 0x7bcd9ed0efc889fb}, - {0x48bc924af11bd720, - 0xfaf417d5d9b21b99, - 0xe71da4aa88e12852, - 0x5d80ef9d1891cc86, - 0xf82012d430219f9b, - 0xcda43c32bcdf1d77, - 0xd21380b00449b17a, - 0x378ee767f11631ba}}; - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/trunc_hash/trunc_hash.cpp botan3-3.12.0+dfsg/src/lib/hash/trunc_hash/trunc_hash.cpp --- botan3-3.7.1+dfsg/src/lib/hash/trunc_hash/trunc_hash.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/trunc_hash/trunc_hash.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,7 +8,9 @@ #include +#include #include +#include #include namespace Botan { @@ -24,7 +26,7 @@ // truncate output to a full number of bytes const auto bytes = output_length(); - std::copy_n(m_buffer.begin(), bytes, out.data()); + copy_mem(out.data(), m_buffer.data(), bytes); zeroise(m_buffer); // mask the unwanted bits in the final byte @@ -55,16 +57,18 @@ } Truncated_Hash::Truncated_Hash(std::unique_ptr hash, size_t bits) : - m_hash(std::move(hash)), m_output_bits(bits), m_buffer(m_hash->output_length()) { + m_hash(std::move(hash)), m_output_bits(bits) { BOTAN_ASSERT_NONNULL(m_hash); if(m_output_bits == 0) { throw Invalid_Argument("Truncating a hash to 0 does not make sense"); } - if(m_hash->output_length() * 8 < m_output_bits) { + const size_t hash_output_length = m_hash->output_length(); + if(hash_output_length * 8 < m_output_bits) { throw Invalid_Argument("Underlying hash function does not produce enough bytes for truncation"); } + m_buffer.resize(hash_output_length); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/trunc_hash/trunc_hash.h botan3-3.12.0+dfsg/src/lib/hash/trunc_hash/trunc_hash.h --- botan3-3.7.1+dfsg/src/lib/hash/trunc_hash/trunc_hash.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/trunc_hash/trunc_hash.h 2026-05-07 01:38:28.000000000 +0000 @@ -36,8 +36,8 @@ Truncated_Hash(std::unique_ptr hash, size_t length); private: - void add_data(std::span) override; - void final_result(std::span) override; + void add_data(std::span input) override; + void final_result(std::span output) override; std::unique_ptr m_hash; size_t m_output_bits; diff -Nru botan3-3.7.1+dfsg/src/lib/hash/whirlpool/whirlpool.cpp botan3-3.12.0+dfsg/src/lib/hash/whirlpool/whirlpool.cpp --- botan3-3.7.1+dfsg/src/lib/hash/whirlpool/whirlpool.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/whirlpool/whirlpool.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,103 +1,119 @@ /* * Whirlpool -* (C) 1999-2007,2020 Jack Lloyd +* (C) 1999-2007,2020,2026 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include +#include +#include #include #include -#include +#include + +#if defined(BOTAN_HAS_CPUID) + #include +#endif namespace Botan { namespace { -alignas(256) const uint64_t WHIRL_S[256] = { - 0x18186018C07830D8, 0x23238C2305AF4626, 0xC6C63FC67EF991B8, 0xE8E887E8136FCDFB, 0x878726874CA113CB, - 0xB8B8DAB8A9626D11, 0x0101040108050209, 0x4F4F214F426E9E0D, 0x3636D836ADEE6C9B, 0xA6A6A2A6590451FF, - 0xD2D26FD2DEBDB90C, 0xF5F5F3F5FB06F70E, 0x7979F979EF80F296, 0x6F6FA16F5FCEDE30, 0x91917E91FCEF3F6D, - 0x52525552AA07A4F8, 0x60609D6027FDC047, 0xBCBCCABC89766535, 0x9B9B569BACCD2B37, 0x8E8E028E048C018A, - 0xA3A3B6A371155BD2, 0x0C0C300C603C186C, 0x7B7BF17BFF8AF684, 0x3535D435B5E16A80, 0x1D1D741DE8693AF5, - 0xE0E0A7E05347DDB3, 0xD7D77BD7F6ACB321, 0xC2C22FC25EED999C, 0x2E2EB82E6D965C43, 0x4B4B314B627A9629, - 0xFEFEDFFEA321E15D, 0x575741578216AED5, 0x15155415A8412ABD, 0x7777C1779FB6EEE8, 0x3737DC37A5EB6E92, - 0xE5E5B3E57B56D79E, 0x9F9F469F8CD92313, 0xF0F0E7F0D317FD23, 0x4A4A354A6A7F9420, 0xDADA4FDA9E95A944, - 0x58587D58FA25B0A2, 0xC9C903C906CA8FCF, 0x2929A429558D527C, 0x0A0A280A5022145A, 0xB1B1FEB1E14F7F50, - 0xA0A0BAA0691A5DC9, 0x6B6BB16B7FDAD614, 0x85852E855CAB17D9, 0xBDBDCEBD8173673C, 0x5D5D695DD234BA8F, - 0x1010401080502090, 0xF4F4F7F4F303F507, 0xCBCB0BCB16C08BDD, 0x3E3EF83EEDC67CD3, 0x0505140528110A2D, - 0x676781671FE6CE78, 0xE4E4B7E47353D597, 0x27279C2725BB4E02, 0x4141194132588273, 0x8B8B168B2C9D0BA7, - 0xA7A7A6A7510153F6, 0x7D7DE97DCF94FAB2, 0x95956E95DCFB3749, 0xD8D847D88E9FAD56, 0xFBFBCBFB8B30EB70, - 0xEEEE9FEE2371C1CD, 0x7C7CED7CC791F8BB, 0x6666856617E3CC71, 0xDDDD53DDA68EA77B, 0x17175C17B84B2EAF, - 0x4747014702468E45, 0x9E9E429E84DC211A, 0xCACA0FCA1EC589D4, 0x2D2DB42D75995A58, 0xBFBFC6BF9179632E, - 0x07071C07381B0E3F, 0xADAD8EAD012347AC, 0x5A5A755AEA2FB4B0, 0x838336836CB51BEF, 0x3333CC3385FF66B6, - 0x636391633FF2C65C, 0x02020802100A0412, 0xAAAA92AA39384993, 0x7171D971AFA8E2DE, 0xC8C807C80ECF8DC6, - 0x19196419C87D32D1, 0x494939497270923B, 0xD9D943D9869AAF5F, 0xF2F2EFF2C31DF931, 0xE3E3ABE34B48DBA8, - 0x5B5B715BE22AB6B9, 0x88881A8834920DBC, 0x9A9A529AA4C8293E, 0x262698262DBE4C0B, 0x3232C8328DFA64BF, - 0xB0B0FAB0E94A7D59, 0xE9E983E91B6ACFF2, 0x0F0F3C0F78331E77, 0xD5D573D5E6A6B733, 0x80803A8074BA1DF4, - 0xBEBEC2BE997C6127, 0xCDCD13CD26DE87EB, 0x3434D034BDE46889, 0x48483D487A759032, 0xFFFFDBFFAB24E354, - 0x7A7AF57AF78FF48D, 0x90907A90F4EA3D64, 0x5F5F615FC23EBE9D, 0x202080201DA0403D, 0x6868BD6867D5D00F, - 0x1A1A681AD07234CA, 0xAEAE82AE192C41B7, 0xB4B4EAB4C95E757D, 0x54544D549A19A8CE, 0x93937693ECE53B7F, - 0x222288220DAA442F, 0x64648D6407E9C863, 0xF1F1E3F1DB12FF2A, 0x7373D173BFA2E6CC, 0x12124812905A2482, - 0x40401D403A5D807A, 0x0808200840281048, 0xC3C32BC356E89B95, 0xECEC97EC337BC5DF, 0xDBDB4BDB9690AB4D, - 0xA1A1BEA1611F5FC0, 0x8D8D0E8D1C830791, 0x3D3DF43DF5C97AC8, 0x97976697CCF1335B, 0x0000000000000000, - 0xCFCF1BCF36D483F9, 0x2B2BAC2B4587566E, 0x7676C57697B3ECE1, 0x8282328264B019E6, 0xD6D67FD6FEA9B128, - 0x1B1B6C1BD87736C3, 0xB5B5EEB5C15B7774, 0xAFAF86AF112943BE, 0x6A6AB56A77DFD41D, 0x50505D50BA0DA0EA, - 0x45450945124C8A57, 0xF3F3EBF3CB18FB38, 0x3030C0309DF060AD, 0xEFEF9BEF2B74C3C4, 0x3F3FFC3FE5C37EDA, - 0x55554955921CAAC7, 0xA2A2B2A2791059DB, 0xEAEA8FEA0365C9E9, 0x656589650FECCA6A, 0xBABAD2BAB9686903, - 0x2F2FBC2F65935E4A, 0xC0C027C04EE79D8E, 0xDEDE5FDEBE81A160, 0x1C1C701CE06C38FC, 0xFDFDD3FDBB2EE746, - 0x4D4D294D52649A1F, 0x92927292E4E03976, 0x7575C9758FBCEAFA, 0x06061806301E0C36, 0x8A8A128A249809AE, - 0xB2B2F2B2F940794B, 0xE6E6BFE66359D185, 0x0E0E380E70361C7E, 0x1F1F7C1FF8633EE7, 0x6262956237F7C455, - 0xD4D477D4EEA3B53A, 0xA8A89AA829324D81, 0x96966296C4F43152, 0xF9F9C3F99B3AEF62, 0xC5C533C566F697A3, - 0x2525942535B14A10, 0x59597959F220B2AB, 0x84842A8454AE15D0, 0x7272D572B7A7E4C5, 0x3939E439D5DD72EC, - 0x4C4C2D4C5A619816, 0x5E5E655ECA3BBC94, 0x7878FD78E785F09F, 0x3838E038DDD870E5, 0x8C8C0A8C14860598, - 0xD1D163D1C6B2BF17, 0xA5A5AEA5410B57E4, 0xE2E2AFE2434DD9A1, 0x616199612FF8C24E, 0xB3B3F6B3F1457B42, - 0x2121842115A54234, 0x9C9C4A9C94D62508, 0x1E1E781EF0663CEE, 0x4343114322528661, 0xC7C73BC776FC93B1, - 0xFCFCD7FCB32BE54F, 0x0404100420140824, 0x51515951B208A2E3, 0x99995E99BCC72F25, 0x6D6DA96D4FC4DA22, - 0x0D0D340D68391A65, 0xFAFACFFA8335E979, 0xDFDF5BDFB684A369, 0x7E7EE57ED79BFCA9, 0x242490243DB44819, - 0x3B3BEC3BC5D776FE, 0xABAB96AB313D4B9A, 0xCECE1FCE3ED181F0, 0x1111441188552299, 0x8F8F068F0C890383, - 0x4E4E254E4A6B9C04, 0xB7B7E6B7D1517366, 0xEBEB8BEB0B60CBE0, 0x3C3CF03CFDCC78C1, 0x81813E817CBF1FFD, - 0x94946A94D4FE3540, 0xF7F7FBF7EB0CF31C, 0xB9B9DEB9A1676F18, 0x13134C13985F268B, 0x2C2CB02C7D9C5851, - 0xD3D36BD3D6B8BB05, 0xE7E7BBE76B5CD38C, 0x6E6EA56E57CBDC39, 0xC4C437C46EF395AA, 0x03030C03180F061B, - 0x565645568A13ACDC, 0x44440D441A49885E, 0x7F7FE17FDF9EFEA0, 0xA9A99EA921374F88, 0x2A2AA82A4D825467, - 0xBBBBD6BBB16D6B0A, 0xC1C123C146E29F87, 0x53535153A202A6F1, 0xDCDC57DCAE8BA572, 0x0B0B2C0B58271653, - 0x9D9D4E9D9CD32701, 0x6C6CAD6C47C1D82B, 0x3131C43195F562A4, 0x7474CD7487B9E8F3, 0xF6F6FFF6E309F115, - 0x464605460A438C4C, 0xACAC8AAC092645A5, 0x89891E893C970FB5, 0x14145014A04428B4, 0xE1E1A3E15B42DFBA, - 0x16165816B04E2CA6, 0x3A3AE83ACDD274F7, 0x6969B9696FD0D206, 0x09092409482D1241, 0x7070DD70A7ADE0D7, - 0xB6B6E2B6D954716F, 0xD0D067D0CEB7BD1E, 0xEDED93ED3B7EC7D6, 0xCCCC17CC2EDB85E2, 0x424215422A578468, - 0x98985A98B4C22D2C, 0xA4A4AAA4490E55ED, 0x2828A0285D885075, 0x5C5C6D5CDA31B886, 0xF8F8C7F8933FED6B, - 0x8686228644A411C2}; +// Derive the 256-byte S-box from the Whirlpool E and R mini-boxes +consteval std::array whirlpool_sbox() noexcept { + constexpr uint8_t Ebox[16] = {1, 11, 9, 12, 13, 6, 15, 3, 14, 8, 7, 4, 10, 2, 5, 0}; + constexpr uint8_t Rbox[16] = {7, 12, 11, 13, 14, 4, 9, 15, 6, 3, 8, 10, 2, 5, 1, 0}; + + // Derive the inverse of the E table + uint8_t Eibox[16] = {}; + for(size_t i = 0; i != 16; ++i) { + Eibox[Ebox[i]] = static_cast(i); + } + + std::array S = {}; + for(size_t i = 0; i != 256; ++i) { + const uint8_t L = Ebox[i >> 4]; + const uint8_t R = Eibox[i & 0x0F]; + const uint8_t T = Rbox[L ^ R]; + S[i] = static_cast((Ebox[L ^ T] << 4) | Eibox[R ^ T]); + } + return S; +} + +// Combined S-box + MDS diffusion table +consteval std::array whirlpool_T_table(const std::array& S) noexcept { + // MDS circulant matrix first row: [1, 1, 4, 1, 8, 5, 2, 9] over GF(2^8) + constexpr uint64_t MDS = 0x0101040108050209; + + std::array T = {}; + for(size_t i = 0; i != 256; ++i) { + T[i] = poly_mul<0x1D>(MDS, S[i]); + } + return T; +} + +// Round constants are from the first 64 elements of the sbox +consteval std::array whirlpool_rc(const std::array& S) noexcept { + std::array RC = {}; + for(size_t r = 0; r != 10; ++r) { + RC[r] = load_be(S.data(), r); + } + return RC; +} + +constexpr auto WHIRL_S = whirlpool_sbox(); +alignas(256) constexpr auto WHIRL_T = whirlpool_T_table(WHIRL_S); +constexpr auto WHIRL_RC = whirlpool_rc(WHIRL_S); uint64_t whirl(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6, uint64_t x7) { - const uint64_t s0 = WHIRL_S[get_byte<0>(x0)]; - const uint64_t s1 = WHIRL_S[get_byte<1>(x1)]; - const uint64_t s2 = WHIRL_S[get_byte<2>(x2)]; - const uint64_t s3 = WHIRL_S[get_byte<3>(x3)]; - const uint64_t s4 = WHIRL_S[get_byte<4>(x4)]; - const uint64_t s5 = WHIRL_S[get_byte<5>(x5)]; - const uint64_t s6 = WHIRL_S[get_byte<6>(x6)]; - const uint64_t s7 = WHIRL_S[get_byte<7>(x7)]; + const uint64_t s0 = WHIRL_T[get_byte<0>(x0)]; + const uint64_t s1 = WHIRL_T[get_byte<1>(x1)]; + const uint64_t s2 = WHIRL_T[get_byte<2>(x2)]; + const uint64_t s3 = WHIRL_T[get_byte<3>(x3)]; + const uint64_t s4 = WHIRL_T[get_byte<4>(x4)]; + const uint64_t s5 = WHIRL_T[get_byte<5>(x5)]; + const uint64_t s6 = WHIRL_T[get_byte<6>(x6)]; + const uint64_t s7 = WHIRL_T[get_byte<7>(x7)]; return s0 ^ rotr<8>(s1) ^ rotr<16>(s2) ^ rotr<24>(s3) ^ rotr<32>(s4) ^ rotr<40>(s5) ^ rotr<48>(s6) ^ rotr<56>(s7); } } // namespace +std::string Whirlpool::provider() const { +#if defined(BOTAN_HAS_WHIRLPOOL_AVX512) + if(auto feat = CPUID::check(CPUID::Feature::AVX512)) { + return *feat; + } +#endif + +#if defined(BOTAN_HAS_WHIRLPOOL_AVX2) + if(auto feat = CPUID::check(CPUID::Feature::AVX2)) { + return *feat; + } +#endif + + return "base"; +} + /* * Whirlpool Compression Function */ void Whirlpool::compress_n(digest_type& digest, std::span input, size_t blocks) { - static const uint64_t RC[10] = {0x1823C6E887B8014F, - 0x36A6D2F5796F9152, - 0x60BC9B8EA30C7B35, - 0x1DE0D7C22E4BFE57, - 0x157737E59FF04ADA, - 0x58C9290AB1A06B85, - 0xBD5D10F4CB3E0567, - 0xE427418BA77D95D8, - 0xFBEE7C66DD17479E, - 0xCA2DBF07AD5A8333}; +#if defined(BOTAN_HAS_WHIRLPOOL_AVX512) + if(CPUID::has(CPUID::Feature::AVX512)) { + return compress_n_avx512(digest, input, blocks); + } +#endif + +#if defined(BOTAN_HAS_WHIRLPOOL_AVX2) + if(CPUID::has(CPUID::Feature::AVX2)) { + return compress_n_avx2(digest, input, blocks); + } +#endif + BufferSlicer in(input); for(size_t i = 0; i != blocks; ++i) { @@ -125,7 +141,7 @@ const uint64_t PK6 = K[8 * (r - 1) + 6]; const uint64_t PK7 = K[8 * (r - 1) + 7]; - K[8 * r + 0] = whirl(PK0, PK7, PK6, PK5, PK4, PK3, PK2, PK1) ^ RC[r - 1]; + K[8 * r + 0] = whirl(PK0, PK7, PK6, PK5, PK4, PK3, PK2, PK1) ^ WHIRL_RC[r - 1]; K[8 * r + 1] = whirl(PK1, PK0, PK7, PK6, PK5, PK4, PK3, PK2); K[8 * r + 2] = whirl(PK2, PK1, PK0, PK7, PK6, PK5, PK4, PK3); K[8 * r + 3] = whirl(PK3, PK2, PK1, PK0, PK7, PK6, PK5, PK4); @@ -149,14 +165,14 @@ uint64_t B7 = M[7] ^ K[7]; for(size_t r = 1; r != 11; ++r) { - uint64_t T0 = whirl(B0, B7, B6, B5, B4, B3, B2, B1) ^ K[8 * r + 0]; - uint64_t T1 = whirl(B1, B0, B7, B6, B5, B4, B3, B2) ^ K[8 * r + 1]; - uint64_t T2 = whirl(B2, B1, B0, B7, B6, B5, B4, B3) ^ K[8 * r + 2]; - uint64_t T3 = whirl(B3, B2, B1, B0, B7, B6, B5, B4) ^ K[8 * r + 3]; - uint64_t T4 = whirl(B4, B3, B2, B1, B0, B7, B6, B5) ^ K[8 * r + 4]; - uint64_t T5 = whirl(B5, B4, B3, B2, B1, B0, B7, B6) ^ K[8 * r + 5]; - uint64_t T6 = whirl(B6, B5, B4, B3, B2, B1, B0, B7) ^ K[8 * r + 6]; - uint64_t T7 = whirl(B7, B6, B5, B4, B3, B2, B1, B0) ^ K[8 * r + 7]; + const uint64_t T0 = whirl(B0, B7, B6, B5, B4, B3, B2, B1) ^ K[8 * r + 0]; + const uint64_t T1 = whirl(B1, B0, B7, B6, B5, B4, B3, B2) ^ K[8 * r + 1]; + const uint64_t T2 = whirl(B2, B1, B0, B7, B6, B5, B4, B3) ^ K[8 * r + 2]; + const uint64_t T3 = whirl(B3, B2, B1, B0, B7, B6, B5, B4) ^ K[8 * r + 3]; + const uint64_t T4 = whirl(B4, B3, B2, B1, B0, B7, B6, B5) ^ K[8 * r + 4]; + const uint64_t T5 = whirl(B5, B4, B3, B2, B1, B0, B7, B6) ^ K[8 * r + 5]; + const uint64_t T6 = whirl(B6, B5, B4, B3, B2, B1, B0, B7) ^ K[8 * r + 6]; + const uint64_t T7 = whirl(B7, B6, B5, B4, B3, B2, B1, B0) ^ K[8 * r + 7]; B0 = T0; B1 = T1; diff -Nru botan3-3.7.1+dfsg/src/lib/hash/whirlpool/whirlpool.h botan3-3.12.0+dfsg/src/lib/hash/whirlpool/whirlpool.h --- botan3-3.7.1+dfsg/src/lib/hash/whirlpool/whirlpool.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/whirlpool/whirlpool.h 2026-05-07 01:38:28.000000000 +0000 @@ -28,6 +28,14 @@ static void compress_n(digest_type& digest, std::span input, size_t blocks); static void init(digest_type& digest); +#if defined(BOTAN_HAS_WHIRLPOOL_AVX512) + static void compress_n_avx512(digest_type& digest, std::span input, size_t blocks); +#endif + +#if defined(BOTAN_HAS_WHIRLPOOL_AVX2) + static void compress_n_avx2(digest_type& digest, std::span input, size_t blocks); +#endif + public: std::string name() const override { return "Whirlpool"; } @@ -39,6 +47,8 @@ std::unique_ptr copy_state() const override; + std::string provider() const override; + void clear() override { m_md.clear(); } private: diff -Nru botan3-3.7.1+dfsg/src/lib/hash/whirlpool/whirlpool_avx2/info.txt botan3-3.12.0+dfsg/src/lib/hash/whirlpool/whirlpool_avx2/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/whirlpool/whirlpool_avx2/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/whirlpool/whirlpool_avx2/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,16 @@ + +WHIRLPOOL_AVX2 -> 20260321 + + + +name -> "Whirlpool AVX2" + + + +avx2 + + + +cpuid +simd_avx2 + diff -Nru botan3-3.7.1+dfsg/src/lib/hash/whirlpool/whirlpool_avx2/whirlpool_avx2.cpp botan3-3.12.0+dfsg/src/lib/hash/whirlpool/whirlpool_avx2/whirlpool_avx2.cpp --- botan3-3.7.1+dfsg/src/lib/hash/whirlpool/whirlpool_avx2/whirlpool_avx2.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/whirlpool/whirlpool_avx2/whirlpool_avx2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,254 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan { + +namespace WhirlpoolAVX2 { + +namespace { + +// NOLINTBEGIN(portability-simd-intrinsics) + +class WhirlpoolState final { + public: + BOTAN_FN_ISA_AVX2 + WhirlpoolState() : m_lo(_mm256_setzero_si256()), m_hi(_mm256_setzero_si256()) {} + + BOTAN_FN_ISA_AVX2 + WhirlpoolState(__m256i lo, __m256i hi) : m_lo(lo), m_hi(hi) {} + + WhirlpoolState(const WhirlpoolState& other) = default; + WhirlpoolState(WhirlpoolState&& other) = default; + WhirlpoolState& operator=(const WhirlpoolState& other) = default; + WhirlpoolState& operator=(WhirlpoolState&& other) = default; + ~WhirlpoolState() = default; + + BOTAN_FN_ISA_AVX2 + static WhirlpoolState load_bytes(const uint8_t src[64]) { + return WhirlpoolState(_mm256_loadu_si256(reinterpret_cast(src)), + _mm256_loadu_si256(reinterpret_cast(src + 32))); + } + + BOTAN_FN_ISA_AVX2 + static WhirlpoolState load_be(const uint64_t src[8]) { + return WhirlpoolState(_mm256_loadu_si256(reinterpret_cast(src)), + _mm256_loadu_si256(reinterpret_cast(src + 4))) + .bswap(); + } + + BOTAN_FN_ISA_AVX2 + void store_be(uint64_t dst[8]) const { + auto s = bswap(); + _mm256_storeu_si256(reinterpret_cast<__m256i*>(dst), s.m_lo); + _mm256_storeu_si256(reinterpret_cast<__m256i*>(dst + 4), s.m_hi); + } + + BOTAN_FN_ISA_AVX2 + inline friend WhirlpoolState operator^(WhirlpoolState a, WhirlpoolState b) { + return WhirlpoolState(_mm256_xor_si256(a.m_lo, b.m_lo), _mm256_xor_si256(a.m_hi, b.m_hi)); + } + + BOTAN_FN_ISA_AVX2 + inline friend WhirlpoolState operator^(WhirlpoolState a, uint64_t rc) { + return WhirlpoolState(_mm256_xor_si256(a.m_lo, _mm256_set_epi64x(0, 0, 0, rc)), a.m_hi); + } + + BOTAN_FN_ISA_AVX2 + inline WhirlpoolState& operator^=(WhirlpoolState other) { + m_lo = _mm256_xor_si256(m_lo, other.m_lo); + m_hi = _mm256_xor_si256(m_hi, other.m_hi); + return *this; + } + + BOTAN_FN_ISA_AVX2 + inline WhirlpoolState sub_bytes() const { return WhirlpoolState(sub_bytes(m_lo), sub_bytes(m_hi)); } + + BOTAN_FN_ISA_AVX2 + inline WhirlpoolState shift_columns() const { + /* + * This is a lot more complicated than the AVX-512 version since first we have + * the state split between two registers and also AVX2 permutes are much weaker + * than AVX512's due to mostly only working on 128 bit lanes + */ + + constexpr char non = -1; + + const auto sc0 = _mm_setr_epi8(0x0, non, non, non, non, non, non, 0xF, 0x8, 0x1, non, non, non, non, non, non); + const auto sc1 = _mm_setr_epi8(non, 0x9, 0x2, non, non, non, non, non, non, non, 0xA, 0x3, non, non, non, non); + const auto sc2 = _mm_setr_epi8(non, non, non, 0xB, 0x4, non, non, non, non, non, non, non, 0xC, 0x5, non, non); + const auto sc3 = _mm_setr_epi8(non, non, non, non, non, 0xD, 0x6, non, non, non, non, non, non, non, 0xE, 0x7); + + const auto idx_same_lane = _mm256_broadcastsi128_si256(sc0); + const auto idx_other_half = _mm256_broadcastsi128_si256(sc2); + const auto idx_other_lane = _mm256_set_m128i(sc1, sc3); + const auto idx_other_both = _mm256_set_m128i(sc3, sc1); + + // Swap the two lanes within the registers so we can get at the values we need via in-lane shuffles + const auto r_lo = _mm256_permute2x128_si256(m_lo, m_lo, 0x01); + const auto r_hi = _mm256_permute2x128_si256(m_hi, m_hi, 0x01); + + /* + * Compute the shift column output by shuffling all 4 input lanes (lo[0], lo[1], hi[0], hi[1]) + * to select out the values we want from each source lane, placing them in the + * index we want, and OR each into the result. + */ + __m256i new_lo = _mm256_shuffle_epi8(m_lo, idx_same_lane); + new_lo = _mm256_or_si256(new_lo, _mm256_shuffle_epi8(r_lo, idx_other_lane)); + new_lo = _mm256_or_si256(new_lo, _mm256_shuffle_epi8(m_hi, idx_other_half)); + new_lo = _mm256_or_si256(new_lo, _mm256_shuffle_epi8(r_hi, idx_other_both)); + + // Same as above just with hi/lo swapped + __m256i new_hi = _mm256_shuffle_epi8(m_hi, idx_same_lane); + new_hi = _mm256_or_si256(new_hi, _mm256_shuffle_epi8(r_hi, idx_other_lane)); + new_hi = _mm256_or_si256(new_hi, _mm256_shuffle_epi8(m_lo, idx_other_half)); + new_hi = _mm256_or_si256(new_hi, _mm256_shuffle_epi8(r_lo, idx_other_both)); + + return WhirlpoolState(new_lo, new_hi); + } + + BOTAN_FN_ISA_AVX2 + BOTAN_FORCE_INLINE WhirlpoolState mix_rows() const { return WhirlpoolState(mix_rows(m_lo), mix_rows(m_hi)); } + + BOTAN_FN_ISA_AVX2 + BOTAN_FORCE_INLINE WhirlpoolState round() const { return sub_bytes().shift_columns().mix_rows(); } + + private: + BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2 static __m256i sub_bytes(__m256i v) { + const auto Ebox = + _mm256_broadcastsi128_si256(_mm_setr_epi8(1, 11, 9, 12, 13, 6, 15, 3, 14, 8, 7, 4, 10, 2, 5, 0)); + const auto Eibox = + _mm256_broadcastsi128_si256(_mm_setr_epi8(15, 0, 13, 7, 11, 14, 5, 10, 9, 2, 12, 1, 3, 4, 8, 6)); + const auto Rbox = + _mm256_broadcastsi128_si256(_mm_setr_epi8(7, 12, 11, 13, 14, 4, 9, 15, 6, 3, 8, 10, 2, 5, 1, 0)); + + const auto lo_mask = _mm256_set1_epi8(0x0F); + + const auto lo_nib = _mm256_and_si256(v, lo_mask); + const auto hi_nib = _mm256_and_si256(_mm256_srli_epi16(v, 4), lo_mask); + + const auto L = _mm256_shuffle_epi8(Ebox, hi_nib); + const auto R = _mm256_shuffle_epi8(Eibox, lo_nib); + const auto T = _mm256_shuffle_epi8(Rbox, _mm256_xor_si256(L, R)); + + const auto out_hi = _mm256_shuffle_epi8(Ebox, _mm256_xor_si256(L, T)); + const auto out_lo = _mm256_shuffle_epi8(Eibox, _mm256_xor_si256(R, T)); + + return _mm256_or_si256(_mm256_slli_epi16(out_hi, 4), out_lo); + } + + BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2 static __m256i mix_rows(__m256i v) { + // Shuffles for 64-bit rotations + const auto rot1 = + _mm256_broadcastsi128_si256(_mm_setr_epi8(7, 0, 1, 2, 3, 4, 5, 6, 15, 8, 9, 10, 11, 12, 13, 14)); + const auto rot2 = + _mm256_broadcastsi128_si256(_mm_setr_epi8(6, 7, 0, 1, 2, 3, 4, 5, 14, 15, 8, 9, 10, 11, 12, 13)); + const auto rot3 = + _mm256_broadcastsi128_si256(_mm_setr_epi8(5, 6, 7, 0, 1, 2, 3, 4, 13, 14, 15, 8, 9, 10, 11, 12)); + const auto rot4 = + _mm256_broadcastsi128_si256(_mm_setr_epi8(4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11)); + const auto rot5 = + _mm256_broadcastsi128_si256(_mm_setr_epi8(3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10)); + const auto rot6 = + _mm256_broadcastsi128_si256(_mm_setr_epi8(2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9)); + const auto rot7 = + _mm256_broadcastsi128_si256(_mm_setr_epi8(1, 2, 3, 4, 5, 6, 7, 0, 9, 10, 11, 12, 13, 14, 15, 8)); + + const auto x2 = xtime(v); + const auto x4 = xtime(x2); + const auto x8 = xtime(x4); + const auto x5 = _mm256_xor_si256(x4, v); + const auto x9 = _mm256_xor_si256(x8, v); + + const auto t01 = _mm256_xor_si256(v, _mm256_shuffle_epi8(v, rot1)); + const auto t23 = _mm256_xor_si256(_mm256_shuffle_epi8(x4, rot2), _mm256_shuffle_epi8(v, rot3)); + const auto t45 = _mm256_xor_si256(_mm256_shuffle_epi8(x8, rot4), _mm256_shuffle_epi8(x5, rot5)); + const auto t67 = _mm256_xor_si256(_mm256_shuffle_epi8(x2, rot6), _mm256_shuffle_epi8(x9, rot7)); + + return _mm256_xor_si256(_mm256_xor_si256(t01, t23), _mm256_xor_si256(t45, t67)); + } + + BOTAN_FN_ISA_AVX2 + WhirlpoolState bswap() const { + // 64-bit byteswap + const auto tbl = + _mm256_broadcastsi128_si256(_mm_setr_epi8(7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8)); + + return WhirlpoolState(_mm256_shuffle_epi8(m_lo, tbl), _mm256_shuffle_epi8(m_hi, tbl)); + } + + BOTAN_FN_ISA_AVX2 + static __m256i xtime(__m256i a) { + const auto poly = _mm256_set1_epi8(0x1D); + const auto shifted = _mm256_add_epi8(a, a); // shifted = a << 1 + // blendv uses the top bit of the mask argument (a) to select between the inputs + return _mm256_blendv_epi8(shifted, _mm256_xor_si256(shifted, poly), a); + } + + __m256i m_lo; + __m256i m_hi; +}; + +// NOLINTEND(portability-simd-intrinsics) + +} // namespace + +} // namespace WhirlpoolAVX2 + +BOTAN_FN_ISA_AVX2 +void Whirlpool::compress_n_avx2(digest_type& digest, std::span input, size_t blocks) { + using WhirlpoolAVX2::WhirlpoolState; + + auto H = WhirlpoolState::load_be(digest.data()); + + for(size_t i = 0; i != blocks; ++i) { + const auto M = WhirlpoolState::load_bytes(input.data() + i * 64); + + auto K = H; + H ^= M; + auto B = H; // B = M ^ K + + K = K.round() ^ 0x4F01B887E8C62318; + B = B.round() ^ K; + + K = K.round() ^ 0x52916F79F5D2A636; + B = B.round() ^ K; + + K = K.round() ^ 0x357B0CA38E9BBC60; + B = B.round() ^ K; + + K = K.round() ^ 0x57FE4B2EC2D7E01D; + B = B.round() ^ K; + + K = K.round() ^ 0xDA4AF09FE5377715; + B = B.round() ^ K; + + K = K.round() ^ 0x856BA0B10A29C958; + B = B.round() ^ K; + + K = K.round() ^ 0x67053ECBF4105DBD; + B = B.round() ^ K; + + K = K.round() ^ 0xD8957DA78B4127E4; + B = B.round() ^ K; + + K = K.round() ^ 0x9E4717DD667CEEFB; + B = B.round() ^ K; + + K = K.round() ^ 0x33835AAD07BF2DCA; + B = B.round() ^ K; + + H ^= B; + } + + H.store_be(digest.data()); +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/hash/whirlpool/whirlpool_avx512/info.txt botan3-3.12.0+dfsg/src/lib/hash/whirlpool/whirlpool_avx512/info.txt --- botan3-3.7.1+dfsg/src/lib/hash/whirlpool/whirlpool_avx512/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/whirlpool/whirlpool_avx512/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ + +WHIRLPOOL_AVX512 -> 20260316 + + + +name -> "Whirlpool using AVX512" +brief -> "Whirlpool using AVX512 instructions" + + + +avx512 + + + +x86_64 + + + +cpuid + diff -Nru botan3-3.7.1+dfsg/src/lib/hash/whirlpool/whirlpool_avx512/whirlpool_avx512.cpp botan3-3.12.0+dfsg/src/lib/hash/whirlpool/whirlpool_avx512/whirlpool_avx512.cpp --- botan3-3.7.1+dfsg/src/lib/hash/whirlpool/whirlpool_avx512/whirlpool_avx512.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/hash/whirlpool/whirlpool_avx512/whirlpool_avx512.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,239 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan { + +namespace WhirlpoolAVX512 { + +namespace { + +// NOLINTBEGIN(portability-simd-intrinsics) + +class WhirlpoolState final { + public: + BOTAN_FN_ISA_AVX512 + WhirlpoolState() : m_v(_mm512_setzero_si512()) {} + + BOTAN_FN_ISA_AVX512 + explicit WhirlpoolState(__m512i v) : m_v(v) {} + + WhirlpoolState(const WhirlpoolState& other) = default; + WhirlpoolState(WhirlpoolState&& other) = default; + WhirlpoolState& operator=(const WhirlpoolState& other) = default; + WhirlpoolState& operator=(WhirlpoolState&& other) = default; + ~WhirlpoolState() = default; + + // Load 64 bytes of message data + BOTAN_FN_ISA_AVX512 + static WhirlpoolState load_bytes(const uint8_t src[64]) { return WhirlpoolState(_mm512_loadu_si512(src)); } + + BOTAN_FN_ISA_AVX512 + static WhirlpoolState load_be(const uint64_t src[8]) { return WhirlpoolState(_mm512_loadu_si512(src)).bswap(); } + + BOTAN_FN_ISA_AVX512 + void store_be(uint64_t dst[8]) const { _mm512_storeu_si512(dst, bswap().m_v); } + + BOTAN_FN_ISA_AVX512 + inline friend WhirlpoolState operator^(WhirlpoolState a, WhirlpoolState b) { + return WhirlpoolState(_mm512_xor_si512(a.m_v, b.m_v)); + } + + BOTAN_FN_ISA_AVX512 + inline WhirlpoolState& operator^=(WhirlpoolState other) { + m_v = _mm512_xor_si512(m_v, other.m_v); + return *this; + } + + /* + * The Whirlpool 8-bit Sbox is built out of 4-bit sboxes, which can be + * individually computed using pshufb-style shuffles. + */ + BOTAN_FN_ISA_AVX512 + inline WhirlpoolState sub_bytes() const { + const __m512i Ebox = + _mm512_broadcast_i32x4(_mm_setr_epi8(1, 11, 9, 12, 13, 6, 15, 3, 14, 8, 7, 4, 10, 2, 5, 0)); + const __m512i Eibox = + _mm512_broadcast_i32x4(_mm_setr_epi8(15, 0, 13, 7, 11, 14, 5, 10, 9, 2, 12, 1, 3, 4, 8, 6)); + const __m512i Rbox = + _mm512_broadcast_i32x4(_mm_setr_epi8(7, 12, 11, 13, 14, 4, 9, 15, 6, 3, 8, 10, 2, 5, 1, 0)); + + const __m512i lo_mask = _mm512_set1_epi8(0x0F); + + const __m512i lo_nib = _mm512_and_si512(m_v, lo_mask); + const __m512i hi_nib = _mm512_and_si512(_mm512_srli_epi16(m_v, 4), lo_mask); + + // L = Ebox[hi], R = Eibox[lo], T = Rbox[L ^ R] + const __m512i L = _mm512_shuffle_epi8(Ebox, hi_nib); + const __m512i R = _mm512_shuffle_epi8(Eibox, lo_nib); + const __m512i T = _mm512_shuffle_epi8(Rbox, _mm512_xor_si512(L, R)); + + // result = (Ebox[L ^ T] << 4) | Eibox[R ^ T] + const __m512i out_hi = _mm512_shuffle_epi8(Ebox, _mm512_xor_si512(L, T)); + const __m512i out_lo = _mm512_shuffle_epi8(Eibox, _mm512_xor_si512(R, T)); + + return WhirlpoolState(_mm512_or_si512(_mm512_slli_epi16(out_hi, 4), _mm512_and_si512(out_lo, lo_mask))); + } + + /* + * ShiftColumns: column j is cyclically shifted down by j positions. + * + * For output row r, column c: source = row (r - c + 8) % 8, column c. + * Implemented as a single vpermb with a fixed 64-byte permutation. + */ + BOTAN_FN_ISA_AVX512 + inline WhirlpoolState shift_columns() const { + // Register byte for (row r, col c) = r*8 + c + // Source byte = ((r - c + 8) % 8) * 8 + c + alignas(64) static constexpr uint8_t perm[64] = { + // clang-format off + 0*8+0, 7*8+1, 6*8+2, 5*8+3, 4*8+4, 3*8+5, 2*8+6, 1*8+7, + 1*8+0, 0*8+1, 7*8+2, 6*8+3, 5*8+4, 4*8+5, 3*8+6, 2*8+7, + 2*8+0, 1*8+1, 0*8+2, 7*8+3, 6*8+4, 5*8+5, 4*8+6, 3*8+7, + 3*8+0, 2*8+1, 1*8+2, 0*8+3, 7*8+4, 6*8+5, 5*8+6, 4*8+7, + 4*8+0, 3*8+1, 2*8+2, 1*8+3, 0*8+4, 7*8+5, 6*8+6, 5*8+7, + 5*8+0, 4*8+1, 3*8+2, 2*8+3, 1*8+4, 0*8+5, 7*8+6, 6*8+7, + 6*8+0, 5*8+1, 4*8+2, 3*8+3, 2*8+4, 1*8+5, 0*8+6, 7*8+7, + 7*8+0, 6*8+1, 5*8+2, 4*8+3, 3*8+4, 2*8+5, 1*8+6, 0*8+7, + // clang-format on + }; + return WhirlpoolState(_mm512_permutexvar_epi8(_mm512_load_si512(perm), m_v)); + } + + /* + * MixRows: MDS circulant [1, 1, 4, 1, 8, 5, 2, 9] over GF(2^8) mod 0x11D + * + * Since the MDS coefficients are so small we can easily compute them using + * a few xtimes plus additions (aka XOR) + */ + BOTAN_FN_ISA_AVX512 + inline WhirlpoolState mix_rows() const { + /* + Constants for quadword rotations by X bytes. + + Could use _mm512_rol_epi64 for this, but it's oddly slower even though + all documentation suggests that both instructions have the same latency + and throughput. + */ + const __m512i rot1 = + _mm512_broadcast_i32x4(_mm_setr_epi8(7, 0, 1, 2, 3, 4, 5, 6, 15, 8, 9, 10, 11, 12, 13, 14)); + const __m512i rot2 = + _mm512_broadcast_i32x4(_mm_setr_epi8(6, 7, 0, 1, 2, 3, 4, 5, 14, 15, 8, 9, 10, 11, 12, 13)); + const __m512i rot3 = + _mm512_broadcast_i32x4(_mm_setr_epi8(5, 6, 7, 0, 1, 2, 3, 4, 13, 14, 15, 8, 9, 10, 11, 12)); + const __m512i rot4 = + _mm512_broadcast_i32x4(_mm_setr_epi8(4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11)); + const __m512i rot5 = + _mm512_broadcast_i32x4(_mm_setr_epi8(3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10)); + const __m512i rot6 = + _mm512_broadcast_i32x4(_mm_setr_epi8(2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9)); + const __m512i rot7 = + _mm512_broadcast_i32x4(_mm_setr_epi8(1, 2, 3, 4, 5, 6, 7, 0, 9, 10, 11, 12, 13, 14, 15, 8)); + + const __m512i x2 = xtime(m_v); + const __m512i x4 = xtime(x2); + const __m512i x8 = xtime(x4); + const __m512i x5 = _mm512_xor_si512(x4, m_v); + const __m512i x9 = _mm512_xor_si512(x8, m_v); + + const __m512i t01 = _mm512_xor_si512(m_v, _mm512_shuffle_epi8(m_v, rot1)); + const __m512i t23 = _mm512_xor_si512(_mm512_shuffle_epi8(x4, rot2), _mm512_shuffle_epi8(m_v, rot3)); + const __m512i t45 = _mm512_xor_si512(_mm512_shuffle_epi8(x8, rot4), _mm512_shuffle_epi8(x5, rot5)); + const __m512i t67 = _mm512_xor_si512(_mm512_shuffle_epi8(x2, rot6), _mm512_shuffle_epi8(x9, rot7)); + + return WhirlpoolState(_mm512_xor_si512(_mm512_xor_si512(t01, t23), _mm512_xor_si512(t45, t67))); + } + + /* + * Whirlpool round: SubBytes -> ShiftColumns -> MixRows + */ + BOTAN_FN_ISA_AVX512 + inline WhirlpoolState round() const { return sub_bytes().shift_columns().mix_rows(); } + + // Round constant + BOTAN_FN_ISA_AVX512 + static inline WhirlpoolState rc(uint64_t v) { return WhirlpoolState(_mm512_set_epi64(0, 0, 0, 0, 0, 0, 0, v)); } + + private: + BOTAN_FN_ISA_AVX512 + WhirlpoolState bswap() const { + const __m512i tbl = _mm512_broadcast_i32x4(_mm_set_epi8(8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7)); + + return WhirlpoolState(_mm512_shuffle_epi8(m_v, tbl)); + } + + // Packed 16-wide doubling in GF(2^8) mod 0x11D + BOTAN_FN_ISA_AVX512 + static __m512i xtime(__m512i a) { + const __m512i poly = _mm512_set1_epi8(0x1D); + const __mmask64 top_bits = _mm512_movepi8_mask(a); + const __m512i shifted = _mm512_add_epi8(a, a); // no 8-bit shift in AVX512 + return _mm512_mask_blend_epi8(top_bits, shifted, _mm512_xor_si512(shifted, poly)); + } + + __m512i m_v; +}; + +// NOLINTEND(portability-simd-intrinsics) + +} // namespace + +} // namespace WhirlpoolAVX512 + +BOTAN_FN_ISA_AVX512 +void Whirlpool::compress_n_avx512(digest_type& digest, std::span input, size_t blocks) { + using WhirlpoolAVX512::WhirlpoolState; + + auto H = WhirlpoolState::load_be(digest.data()); + + for(size_t i = 0; i != blocks; ++i) { + const auto M = WhirlpoolState::load_bytes(input.data() + i * 64); + + auto K = H; + H ^= M; + auto B = H; // B = M ^ K + + K = K.round() ^ WhirlpoolState::rc(0x4F01B887E8C62318); + B = B.round() ^ K; + + K = K.round() ^ WhirlpoolState::rc(0x52916F79F5D2A636); + B = B.round() ^ K; + + K = K.round() ^ WhirlpoolState::rc(0x357B0CA38E9BBC60); + B = B.round() ^ K; + + K = K.round() ^ WhirlpoolState::rc(0x57FE4B2EC2D7E01D); + B = B.round() ^ K; + + K = K.round() ^ WhirlpoolState::rc(0xDA4AF09FE5377715); + B = B.round() ^ K; + + K = K.round() ^ WhirlpoolState::rc(0x856BA0B10A29C958); + B = B.round() ^ K; + + K = K.round() ^ WhirlpoolState::rc(0x67053ECBF4105DBD); + B = B.round() ^ K; + + K = K.round() ^ WhirlpoolState::rc(0xD8957DA78B4127E4); + B = B.round() ^ K; + + K = K.round() ^ WhirlpoolState::rc(0x9E4717DD667CEEFB); + B = B.round() ^ K; + + K = K.round() ^ WhirlpoolState::rc(0x33835AAD07BF2DCA); + B = B.round() ^ K; + + H ^= B; + } + + H.store_be(digest.data()); +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/kdf/hkdf/hkdf.cpp botan3-3.12.0+dfsg/src/lib/kdf/hkdf/hkdf.cpp --- botan3-3.7.1+dfsg/src/lib/kdf/hkdf/hkdf.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/kdf/hkdf/hkdf.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,10 +9,11 @@ #include -#include +#include +#include #include #include -#include +#include namespace Botan { @@ -28,8 +29,8 @@ std::span secret, std::span salt, std::span label) const { - HKDF_Extract extract(m_prf->new_object()); - HKDF_Expand expand(m_prf->new_object()); + const HKDF_Extract extract(m_prf->new_object()); + const HKDF_Expand expand(m_prf->new_object()); secure_vector prk(m_prf->output_length()); extract.derive_key(prk, secret, salt, {}); @@ -49,7 +50,7 @@ std::span salt, std::span label) const { const size_t prf_output_len = m_prf->output_length(); - BOTAN_ARG_CHECK(key.size() <= prf_output_len, "HKDF-Extract maximum output length exceeeded"); + BOTAN_ARG_CHECK(key.size() <= prf_output_len, "HKDF-Extract maximum output length exceeded"); BOTAN_ARG_CHECK(label.empty(), "HKDF-Extract does not support a label input"); if(key.empty()) { @@ -85,7 +86,7 @@ std::span salt, std::span label) const { const auto prf_output_length = m_prf->output_length(); - BOTAN_ARG_CHECK(key.size() <= prf_output_length * 255, "HKDF-Expand maximum output length exceeeded"); + BOTAN_ARG_CHECK(key.size() <= prf_output_length * 255, "HKDF-Expand maximum output length exceeded"); if(key.empty()) { return; @@ -124,11 +125,11 @@ BOTAN_ARG_CHECK(label.size() <= 0xFF, "HKDF-Expand-Label label too long"); BOTAN_ARG_CHECK(hash_val.size() <= 0xFF, "HKDF-Expand-Label hash too long"); - HKDF_Expand hkdf(MessageAuthenticationCode::create_or_throw(fmt("HMAC({})", hash_fn))); + const HKDF_Expand hkdf(MessageAuthenticationCode::create_or_throw(fmt("HMAC({})", hash_fn))); const auto prefix = concat>(store_be(static_cast(length)), store_be(static_cast(label.size())), - std::span{cast_char_ptr_to_uint8(label.data()), label.size()}, + as_span_of_bytes(label), store_be(static_cast(hash_val.size()))); /* diff -Nru botan3-3.7.1+dfsg/src/lib/kdf/info.txt botan3-3.12.0+dfsg/src/lib/kdf/info.txt --- botan3-3.7.1+dfsg/src/lib/kdf/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/kdf/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,7 @@ +KDF -> 20250528 + +# TODO(Botan4) remove this macro KDF_BASE -> 20131128 diff -Nru botan3-3.7.1+dfsg/src/lib/kdf/kdf.cpp botan3-3.12.0+dfsg/src/lib/kdf/kdf.cpp --- botan3-3.7.1+dfsg/src/lib/kdf/kdf.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/kdf/kdf.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,10 +7,12 @@ #include +#include #include #include #include #include +#include #include #if defined(BOTAN_HAS_HKDF) @@ -210,4 +212,9 @@ return probe_providers_of(algo_spec); } +//static +std::span KDF::_as_span(std::string_view s) { + return as_span_of_bytes(s); +} + } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/kdf/kdf.h botan3-3.12.0+dfsg/src/lib/kdf/kdf.h --- botan3-3.7.1+dfsg/src/lib/kdf/kdf.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/kdf/kdf.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,9 +10,9 @@ #define BOTAN_KDF_BASE_H_ #include -#include -#include #include +#include +#include #include #include #include @@ -22,7 +22,7 @@ /** * Key Derivation Function */ -class BOTAN_PUBLIC_API(2, 0) KDF { +class BOTAN_PUBLIC_API(2, 0) KDF /* NOLINT(*-special-member-functions*) */ { public: virtual ~KDF() = default; @@ -111,10 +111,7 @@ std::span secret, std::string_view salt = "", std::string_view label = "") const { - return derive_key(key_len, - secret, - {cast_char_ptr_to_uint8(salt.data()), salt.length()}, - {cast_char_ptr_to_uint8(label.data()), label.length()}); + return derive_key(key_len, secret, _as_span(salt), _as_span(label)); } /** @@ -165,7 +162,7 @@ const uint8_t salt[], size_t salt_len, std::string_view label = "") const { - return derive_key(key_len, secret, {salt, salt_len}, {cast_char_ptr_to_uint8(label.data()), label.size()}); + return derive_key(key_len, secret, {salt, salt_len}, _as_span(label)); } /** @@ -184,10 +181,7 @@ size_t secret_len, std::string_view salt = "", std::string_view label = "") const { - return derive_key(key_len, - {secret, secret_len}, - {cast_char_ptr_to_uint8(salt.data()), salt.length()}, - {cast_char_ptr_to_uint8(label.data()), label.length()}); + return derive_key(key_len, {secret, secret_len}, _as_span(salt), _as_span(label)); } /** @@ -202,7 +196,7 @@ std::array derive_key(std::span secret, std::span salt = {}, std::span label = {}) { - std::array key; + std::array key{}; perform_kdf(key, secret, salt, label); return key; } @@ -219,7 +213,7 @@ std::array derive_key(std::span secret, std::span salt = {}, std::string_view label = "") { - return derive_key(secret, salt, {cast_char_ptr_to_uint8(label.data()), label.size()}); + return derive_key(secret, salt, _as_span(label)); } /** @@ -234,9 +228,7 @@ std::array derive_key(std::span secret, std::string_view salt = "", std::string_view label = "") { - return derive_key(secret, - {cast_char_ptr_to_uint8(salt.data()), salt.size()}, - {cast_char_ptr_to_uint8(label.data()), label.size()}); + return derive_key(secret, _as_span(salt), _as_span(label)); } /** @@ -265,6 +257,9 @@ std::span secret, std::span salt, std::span label) const = 0; + + private: + static std::span _as_span(std::string_view s); }; /** @@ -277,16 +272,11 @@ BOTAN_DEPRECATED("Use KDF::create") inline KDF* get_kdf(std::string_view algo_spec) { - auto kdf = KDF::create(algo_spec); - if(kdf) { - return kdf.release(); - } - if(algo_spec == "Raw") { return nullptr; } - throw Algorithm_Not_Found(algo_spec); + return KDF::create_or_throw(algo_spec).release(); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/kdf/kdf1/kdf1.cpp botan3-3.12.0+dfsg/src/lib/kdf/kdf1/kdf1.cpp --- botan3-3.7.1+dfsg/src/lib/kdf/kdf1/kdf1.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/kdf/kdf1/kdf1.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,7 +8,7 @@ #include -#include +#include #include namespace Botan { @@ -30,7 +30,7 @@ } const size_t hash_output_len = m_hash->output_length(); - BOTAN_ARG_CHECK(key.size() <= hash_output_len, "KDF1 maximum output length exceeeded"); + BOTAN_ARG_CHECK(key.size() <= hash_output_len, "KDF1 maximum output length exceeded"); m_hash->update(secret); m_hash->update(label); diff -Nru botan3-3.7.1+dfsg/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.cpp botan3-3.12.0+dfsg/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.cpp --- botan3-3.7.1+dfsg/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,10 +8,9 @@ #include -#include #include +#include #include -#include namespace Botan { @@ -29,7 +28,7 @@ // This KDF uses a 32-bit counter for the hash blocks, initialized at 0. // It will wrap around after 2^32 iterations which limits the theoretically // possible output to 2^32 blocks. - BOTAN_ARG_CHECK(blocks_required <= 0xFFFFFFFF, "KDF1-18033 maximum output length exceeeded"); + BOTAN_ARG_CHECK(blocks_required <= 0xFFFFFFFF, "KDF1-18033 maximum output length exceeded"); BufferStuffer k(key); for(uint32_t counter = 0; !k.full(); ++counter) { diff -Nru botan3-3.7.1+dfsg/src/lib/kdf/kdf2/kdf2.cpp botan3-3.12.0+dfsg/src/lib/kdf/kdf2/kdf2.cpp --- botan3-3.7.1+dfsg/src/lib/kdf/kdf2/kdf2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/kdf/kdf2/kdf2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,10 +8,9 @@ #include -#include #include +#include #include -#include namespace Botan { @@ -37,7 +36,7 @@ // This KDF uses a 32-bit counter for the hash blocks, initialized at 1. // It will wrap around after 2^32 - 1 iterations limiting the theoretically // possible output to 2^32 - 1 blocks. - BOTAN_ARG_CHECK(blocks_required <= 0xFFFFFFFE, "KDF2 maximum output length exceeeded"); + BOTAN_ARG_CHECK(blocks_required <= 0xFFFFFFFE, "KDF2 maximum output length exceeded"); BufferStuffer k(key); for(uint32_t counter = 1; !k.full(); ++counter) { diff -Nru botan3-3.7.1+dfsg/src/lib/kdf/prf_tls/prf_tls.cpp botan3-3.12.0+dfsg/src/lib/kdf/prf_tls/prf_tls.cpp --- botan3-3.7.1+dfsg/src/lib/kdf/prf_tls/prf_tls.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/kdf/prf_tls/prf_tls.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,8 +9,10 @@ #include #include +#include +#include +#include #include -#include namespace Botan { diff -Nru botan3-3.7.1+dfsg/src/lib/kdf/prf_x942/prf_x942.cpp botan3-3.12.0+dfsg/src/lib/kdf/prf_x942/prf_x942.cpp --- botan3-3.7.1+dfsg/src/lib/kdf/prf_x942/prf_x942.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/kdf/prf_x942/prf_x942.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,8 +11,9 @@ #include #include #include +#include +#include #include -#include namespace Botan { @@ -45,7 +46,10 @@ // This KDF uses a 32-bit counter for the hash blocks, initialized at 1. // It will wrap around after 2^32 - 1 iterations limiting the theoretically // possible output to 2^32 - 1 blocks. - BOTAN_ARG_CHECK(blocks_required <= 0xFFFFFFFE, "X942_PRF maximum output length exceeeded"); + BOTAN_ARG_CHECK(blocks_required <= 0xFFFFFFFE, "X942_PRF maximum output length exceeded"); + + // The key length in bits is encoded as a uint32_t in the DER output + BOTAN_ARG_CHECK(key.size() <= 0x1FFFFFFF, "X942_PRF output length too large for DER encoding"); auto hash = HashFunction::create("SHA-1"); const auto in = concat>(label, salt); @@ -78,7 +82,7 @@ if(k.remaining_capacity() >= sha1_output_bytes) { hash->final(k.next(sha1_output_bytes)); } else { - std::array h; + std::array h{}; hash->final(h); k.append(std::span{h}.first(k.remaining_capacity())); } diff -Nru botan3-3.7.1+dfsg/src/lib/kdf/sp800_108/sp800_108.cpp botan3-3.12.0+dfsg/src/lib/kdf/sp800_108/sp800_108.cpp --- botan3-3.7.1+dfsg/src/lib/kdf/sp800_108/sp800_108.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/kdf/sp800_108/sp800_108.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,11 +9,11 @@ #include -#include #include +#include +#include #include #include -#include #include @@ -21,7 +21,7 @@ namespace { -class CounterParams { +class CounterParams final { public: constexpr static void validate_bit_lengths(size_t counter_bits, size_t output_length_bits) { BOTAN_ARG_CHECK(counter_bits % 8 == 0 && counter_bits <= 32, @@ -30,10 +30,10 @@ "SP.800-108 output length encoding may be one of {8, 16, 24, 32} only"); } - constexpr static CounterParams create_or_throw(size_t output_bytes, - size_t output_length_bits, - size_t counter_bits, - size_t prf_output_bytes) { + static CounterParams create_or_throw(size_t output_bytes, + size_t output_length_bits, + size_t counter_bits, + size_t prf_output_bytes) { // The maximum legal output bit length is limited by the requested encoding // bit length of the "L" field. BOTAN_ARG_CHECK(static_cast(output_bytes) * 8 <= std::numeric_limits::max(), @@ -52,12 +52,8 @@ const auto max_blocks = (uint64_t(1) << counter_bits) - 1; BOTAN_ARG_CHECK(blocks_required < max_blocks, "SP.800-108 output size too large"); - CounterParams out; - out.m_output_length_bits = output_bits; - out.m_output_length_encoding_bytes = output_length_bits / 8; - out.m_counter_bytes = counter_bits / 8; - out.m_blocks_required = static_cast(blocks_required); - return out; + return CounterParams( + output_bits, output_length_bits / 8, counter_bits / 8, static_cast(blocks_required)); } template , std::span> Fn> @@ -71,7 +67,14 @@ } private: - constexpr CounterParams() = default; + CounterParams(uint32_t output_length_bits, + size_t output_length_encoding_bytes, + size_t counter_bytes, + uint32_t blocks_required) : + m_output_length_bits(output_length_bits), + m_output_length_encoding_bytes(output_length_encoding_bytes), + m_counter_bytes(counter_bytes), + m_blocks_required(blocks_required) {} private: uint32_t m_output_length_bits; diff -Nru botan3-3.7.1+dfsg/src/lib/kdf/sp800_56a/sp800_56c_one_step.cpp botan3-3.12.0+dfsg/src/lib/kdf/sp800_56a/sp800_56c_one_step.cpp --- botan3-3.7.1+dfsg/src/lib/kdf/sp800_56a/sp800_56c_one_step.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/kdf/sp800_56a/sp800_56c_one_step.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,7 +12,9 @@ #include #include +#include #include +#include #include #include @@ -33,12 +35,19 @@ std::span fixed_info, HashOrMacType& hash_or_mac, const std::function& init_h_callback) { - size_t l = output_buffer.size() * 8; // 1. If L > 0, then set reps = ceil(L / H_outputBits); otherwise, // output an error indicator and exit this process without // performing the remaining actions (i.e., omit steps 2 through 8). - BOTAN_ARG_CHECK(l > 0, "Zero KDM output length"); - size_t reps = ceil_division(l, hash_or_mac.output_length() * 8); + // + // We follow the usual convention within the library that a KDF request for + // zero bytes is valid and, exactly as requested, outputs nothing. + if(output_buffer.empty()) { + return; + } + + const size_t output_len = output_buffer.size(); + const size_t h_output_len = hash_or_mac.output_length(); + const size_t reps = ceil_division(output_len, h_output_len); // 2. If reps > (2^32 − 1), then output an error indicator and exit this // process without performing the remaining actions @@ -55,11 +64,8 @@ // without performing any of the remaining actions (i.e., omit // steps 5 through 8). => SHA3 and KMAC are unlimited - // 5. Initialize Result(0) as an empty bit string - // (i.e., the null string). - secure_vector result; - - // 6. For i = 1 to reps, do the following: + // 5-7. Derive keying material directly into the output buffer. + BufferStuffer k(output_buffer); for(size_t i = 1; i <= reps; i++) { // 6.1. Increment counter by 1. counter++; @@ -71,14 +77,18 @@ hash_or_mac.update_be(counter); hash_or_mac.update(z); hash_or_mac.update(fixed_info); - auto k_i = hash_or_mac.final(); // 6.3. Set Result(i) = Result(i−1) || K(i). - result.insert(result.end(), k_i.begin(), k_i.end()); + if(k.remaining_capacity() >= h_output_len) { + hash_or_mac.final(k.next(h_output_len)); + } else { + // Needs truncation so can't write directly to the output buffer + const auto k_i = hash_or_mac.final(); + k.append(std::span{k_i}.first(k.remaining_capacity())); + } } - // 7. Set DerivedKeyingMaterial equal to the leftmost L bits of Result(reps). - copy_mem(output_buffer, std::span(result).subspan(0, output_buffer.size())); + BOTAN_ASSERT_NOMSG(k.full()); } } // namespace @@ -87,7 +97,7 @@ std::span secret, std::span salt, std::span label) const { - BOTAN_ARG_CHECK(salt.empty(), "SP800_56A_Hash does not support a non-empty salt"); + BOTAN_ARG_CHECK(salt.empty(), "SP800-56C KDF with hash does not support using a salt parameter"); kdm_internal(key, secret, label, *m_hash, [](HashFunction&) { /* NOP */ }); } diff -Nru botan3-3.7.1+dfsg/src/lib/kdf/xmd/xmd.cpp botan3-3.12.0+dfsg/src/lib/kdf/xmd/xmd.cpp --- botan3-3.7.1+dfsg/src/lib/kdf/xmd/xmd.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/kdf/xmd/xmd.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -59,7 +60,7 @@ for(;;) { const size_t produced = std::min(output.size(), hash_output_size); - copy_mem(&output[0], b_i.data(), produced); + copy_mem(output.data(), b_i.data(), produced); output = output.subspan(produced); if(output.empty()) { diff -Nru botan3-3.7.1+dfsg/src/lib/kdf/xmd/xmd.h botan3-3.12.0+dfsg/src/lib/kdf/xmd/xmd.h --- botan3-3.7.1+dfsg/src/lib/kdf/xmd/xmd.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/kdf/xmd/xmd.h 2026-05-07 01:38:28.000000000 +0000 @@ -24,17 +24,6 @@ std::span input, std::span domain_sep); -inline void expand_message_xmd(std::string_view hash_fn, - std::span output, - std::string_view input_str, - std::string_view domain_sep_str) { - std::span input(reinterpret_cast(input_str.data()), input_str.size()); - - std::span domain_sep(reinterpret_cast(domain_sep_str.data()), domain_sep_str.size()); - - expand_message_xmd(hash_fn, output, input, domain_sep); -} - } // namespace Botan #endif diff -Nru botan3-3.7.1+dfsg/src/lib/mac/blake2mac/blake2bmac.h botan3-3.12.0+dfsg/src/lib/mac/blake2mac/blake2bmac.h --- botan3-3.7.1+dfsg/src/lib/mac/blake2mac/blake2bmac.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/mac/blake2mac/blake2bmac.h 2026-05-07 01:38:28.000000000 +0000 @@ -21,9 +21,6 @@ public: explicit BLAKE2bMAC(size_t output_bits = 512); - BLAKE2bMAC(const BLAKE2bMAC&) = delete; - BLAKE2bMAC& operator=(const BLAKE2bMAC&) = delete; - std::string name() const override { return m_blake.name(); } size_t output_length() const override { return m_blake.output_length(); } diff -Nru botan3-3.7.1+dfsg/src/lib/mac/cmac/cmac.cpp botan3-3.12.0+dfsg/src/lib/mac/cmac/cmac.cpp --- botan3-3.7.1+dfsg/src/lib/mac/cmac/cmac.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/mac/cmac/cmac.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,9 +8,10 @@ #include #include +#include +#include #include #include -#include namespace Botan { @@ -109,7 +110,7 @@ * CMAC Constructor */ CMAC::CMAC(std::unique_ptr cipher) : m_cipher(std::move(cipher)), m_block_size(m_cipher->block_size()) { - if(poly_double_supported_size(m_block_size) == false) { + if(!poly_double_supported_size(m_block_size)) { throw Invalid_Argument(fmt("CMAC cannot use the {} bit cipher {}", m_block_size * 8, m_cipher->name())); } diff -Nru botan3-3.7.1+dfsg/src/lib/mac/cmac/cmac.h botan3-3.12.0+dfsg/src/lib/mac/cmac/cmac.h --- botan3-3.7.1+dfsg/src/lib/mac/cmac/cmac.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/mac/cmac/cmac.h 2026-05-07 01:38:28.000000000 +0000 @@ -34,13 +34,10 @@ */ explicit CMAC(std::unique_ptr cipher); - CMAC(const CMAC&) = delete; - CMAC& operator=(const CMAC&) = delete; - private: - void add_data(std::span) override; - void final_result(std::span) override; - void key_schedule(std::span) override; + void add_data(std::span input) override; + void final_result(std::span output) override; + void key_schedule(std::span key) override; std::unique_ptr m_cipher; secure_vector m_buffer, m_state, m_B, m_P; diff -Nru botan3-3.7.1+dfsg/src/lib/mac/gmac/gmac.cpp botan3-3.12.0+dfsg/src/lib/mac/gmac/gmac.cpp --- botan3-3.7.1+dfsg/src/lib/mac/gmac/gmac.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/mac/gmac/gmac.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -35,6 +36,10 @@ return fmt("GMAC({})", m_cipher->name()); } +std::string GMAC::provider() const { + return m_ghash->provider(); +} + size_t GMAC::output_length() const { return GCM_BS; } @@ -56,7 +61,7 @@ } void GMAC::start_msg(std::span nonce) { - secure_vector y0(GCM_BS); + std::array y0 = {0}; if(nonce.size() == 12) { copy_mem(y0.data(), nonce.data(), nonce.size()); @@ -65,9 +70,8 @@ m_ghash->nonce_hash(y0, nonce); } - secure_vector m_enc_y0(GCM_BS); - m_cipher->encrypt(y0.data(), m_enc_y0.data()); - m_ghash->start(m_enc_y0); + m_cipher->encrypt(y0.data()); + m_ghash->start(y0); m_initialized = true; } @@ -75,12 +79,12 @@ // This ensures the GMAC computation has been initialized with a fresh // nonce. The aim of this check is to prevent developers from re-using // nonces (and potential nonce-reuse attacks). - if(m_initialized == false) { + if(!m_initialized) { throw Invalid_State("GMAC was not used with a fresh nonce"); } m_ghash->final(mac.first(output_length())); - m_ghash->set_key(m_H); + m_ghash->reset_associated_data(); } std::unique_ptr GMAC::new_object() const { diff -Nru botan3-3.7.1+dfsg/src/lib/mac/gmac/gmac.h botan3-3.12.0+dfsg/src/lib/mac/gmac/gmac.h --- botan3-3.7.1+dfsg/src/lib/mac/gmac/gmac.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/mac/gmac/gmac.h 2026-05-07 01:38:28.000000000 +0000 @@ -26,6 +26,7 @@ public: void clear() override; std::string name() const override; + std::string provider() const override; size_t output_length() const override; std::unique_ptr new_object() const override; @@ -40,14 +41,16 @@ */ explicit GMAC(std::unique_ptr cipher); - GMAC(const GMAC&) = delete; - GMAC& operator=(const GMAC&) = delete; + GMAC(const GMAC& other) = delete; + GMAC(GMAC&& other) = default; + GMAC& operator=(const GMAC& other) = delete; + GMAC& operator=(GMAC&& other) = default; ~GMAC() override; private: - void add_data(std::span) override; - void final_result(std::span) override; + void add_data(std::span input) override; + void final_result(std::span output) override; void start_msg(std::span nonce) override; void key_schedule(std::span key) override; diff -Nru botan3-3.7.1+dfsg/src/lib/mac/hmac/hmac.cpp botan3-3.12.0+dfsg/src/lib/mac/hmac/hmac.cpp --- botan3-3.7.1+dfsg/src/lib/mac/hmac/hmac.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/mac/hmac/hmac.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include +#include #include #include @@ -35,7 +36,7 @@ Key_Length_Specification HMAC::key_spec() const { // Support very long lengths for things like PBKDF2 and the TLS PRF - return Key_Length_Specification(0, 4096); + return Key_Length_Specification(0, 8192); } size_t HMAC::output_length() const { diff -Nru botan3-3.7.1+dfsg/src/lib/mac/hmac/hmac.h botan3-3.12.0+dfsg/src/lib/mac/hmac/hmac.h --- botan3-3.7.1+dfsg/src/lib/mac/hmac/hmac.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/mac/hmac/hmac.h 2026-05-07 01:38:28.000000000 +0000 @@ -33,13 +33,10 @@ */ explicit HMAC(std::unique_ptr hash); - HMAC(const HMAC&) = delete; - HMAC& operator=(const HMAC&) = delete; - private: - void add_data(std::span) override; - void final_result(std::span) override; - void key_schedule(std::span) override; + void add_data(std::span input) override; + void final_result(std::span output) override; + void key_schedule(std::span key) override; std::unique_ptr m_hash; secure_vector m_ikey, m_okey; diff -Nru botan3-3.7.1+dfsg/src/lib/mac/kmac/kmac.cpp botan3-3.12.0+dfsg/src/lib/mac/kmac/kmac.cpp --- botan3-3.7.1+dfsg/src/lib/mac/kmac/kmac.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/mac/kmac/kmac.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -70,7 +70,10 @@ void KMAC::final_result(std::span output) { assert_key_material_set(); - std::array encoded_output_length_buffer; + if(!m_message_started) { + start(); + } + std::array encoded_output_length_buffer{}; m_cshake->update(keccak_int_right_encode(encoded_output_length_buffer, m_output_bit_length)); m_cshake->output(output.first(output_length())); m_cshake->clear(); diff -Nru botan3-3.7.1+dfsg/src/lib/mac/kmac/kmac.h botan3-3.12.0+dfsg/src/lib/mac/kmac/kmac.h --- botan3-3.7.1+dfsg/src/lib/mac/kmac/kmac.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/mac/kmac/kmac.h 2026-05-07 01:38:28.000000000 +0000 @@ -16,12 +16,12 @@ class cSHAKE_XOF; -class KMAC : public MessageAuthenticationCode { +class KMAC /* NOLINT(*-special-member-functions*) */ : public MessageAuthenticationCode { protected: KMAC(std::unique_ptr cshake, size_t output_bit_length); public: - virtual ~KMAC(); + ~KMAC() override; KMAC(const KMAC&) = delete; KMAC& operator=(const KMAC&) = delete; @@ -35,9 +35,9 @@ private: void start_msg(std::span nonce) final; - void add_data(std::span) final; - void final_result(std::span) final; - void key_schedule(std::span) final; + void add_data(std::span input) final; + void final_result(std::span output) final; + void key_schedule(std::span key) final; private: size_t m_output_bit_length; @@ -52,7 +52,7 @@ */ class KMAC128 final : public KMAC { public: - KMAC128(size_t output_bit_length); + explicit KMAC128(size_t output_bit_length); std::string name() const override; std::unique_ptr new_object() const override; }; @@ -62,7 +62,7 @@ */ class KMAC256 final : public KMAC { public: - KMAC256(size_t output_bit_length); + explicit KMAC256(size_t output_bit_length); std::string name() const override; std::unique_ptr new_object() const override; }; diff -Nru botan3-3.7.1+dfsg/src/lib/mac/mac.cpp botan3-3.12.0+dfsg/src/lib/mac/mac.cpp --- botan3-3.7.1+dfsg/src/lib/mac/mac.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/mac/mac.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,7 +8,6 @@ #include #include -#include #include #include diff -Nru botan3-3.7.1+dfsg/src/lib/mac/mac.h botan3-3.12.0+dfsg/src/lib/mac/mac.h --- botan3-3.7.1+dfsg/src/lib/mac/mac.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/mac/mac.h 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Base class for message authentiction codes +* Base class for message authentication codes * (C) 1999-2007 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) @@ -47,8 +47,6 @@ */ static std::vector providers(std::string_view algo_spec); - ~MessageAuthenticationCode() override = default; - /** * Prepare for processing a message under the specified nonce * diff -Nru botan3-3.7.1+dfsg/src/lib/mac/poly1305/poly1305.cpp botan3-3.12.0+dfsg/src/lib/mac/poly1305/poly1305.cpp --- botan3-3.7.1+dfsg/src/lib/mac/poly1305/poly1305.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/mac/poly1305/poly1305.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -3,107 +3,274 @@ * in https://github.com/floodyberry/poly1305-donna * * (C) 2014 Andrew Moon -* (C) 2014 Jack Lloyd +* (C) 2014,2025,2026 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include +#include #include #include #include -#include -#include + +#if defined(BOTAN_HAS_POLY1305_AVX2) || defined(BOTAN_HAS_POLY1305_AVX512) + #include +#endif namespace Botan { namespace { +// State layout: pad || accum || r || r^2 || r^3 || ... || r^n +// This ordering allows extending with more powers of r at the end +constexpr size_t PAD_BASE = 0; // pad[0..1] +constexpr size_t H_BASE = 2; // h[0..2] (accumulator) +constexpr size_t R_BASE = 5; // r^1[0..2], r^2[3..5], r^3[6..8], etc. + +// Multiply two values in radix 2^44 representation mod (2^130 - 5) +// h = a * b mod p +BOTAN_FORCE_INLINE void poly1305_mul_44(uint64_t& h0, + uint64_t& h1, + uint64_t& h2, + uint64_t a0, + uint64_t a1, + uint64_t a2, + uint64_t b0, + uint64_t b1, + uint64_t b2) { + constexpr uint64_t M44 = 0xFFFFFFFFFFF; + constexpr uint64_t M42 = 0x3FFFFFFFFFF; + +#if !defined(BOTAN_TARGET_HAS_NATIVE_UINT128) + typedef donna128 uint128_t; +#endif + + const uint64_t s1 = b1 * 20; + const uint64_t s2 = b2 * 20; + + const uint128_t d0 = uint128_t(a0) * b0 + uint128_t(a1) * s2 + uint128_t(a2) * s1; + const uint64_t c0 = carry_shift(d0, 44); + + const uint128_t d1 = uint128_t(a0) * b1 + uint128_t(a1) * b0 + uint128_t(a2) * s2 + c0; + const uint64_t c1 = carry_shift(d1, 44); + + const uint128_t d2 = uint128_t(a0) * b2 + uint128_t(a1) * b1 + uint128_t(a2) * b0 + c1; + const uint64_t c2 = carry_shift(d2, 42); + + h0 = (d0 & M44) + c2 * 5; + h1 = (d1 & M44) + (h0 >> 44); + h0 &= M44; + h2 = d2 & M42; +} + +// Extend powers of r from current max to target +void poly1305_extend_powers(secure_vector& X, size_t target_powers) { + const size_t current_powers = (X.size() - 5) / 3; + + if(current_powers >= target_powers) { + return; + } + + // Load r^1 for multiplication + const uint64_t r0 = X[R_BASE + 0]; + const uint64_t r1 = X[R_BASE + 1]; + const uint64_t r2 = X[R_BASE + 2]; + + X.resize(5 + target_powers * 3); + + // Compute r^(current+1) through r^target + for(size_t i = current_powers + 1; i <= target_powers; ++i) { + const size_t offset = R_BASE + (i - 1) * 3; + poly1305_mul_44( + X[offset + 0], X[offset + 1], X[offset + 2], X[offset - 3], X[offset - 2], X[offset - 1], r0, r1, r2); + } +} + +// Initialize Poly1305 state and precompute powers of r void poly1305_init(secure_vector& X, const uint8_t key[32]) { - /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */ + X.clear(); + X.reserve(2 + 3 + 2 * 3); + X.resize(2 + 3 + 3); + + /* Save pad for later (first 2 slots) */ + X[PAD_BASE + 0] = load_le(key, 2); + X[PAD_BASE + 1] = load_le(key, 3); + + /* h = 0 (accumulator, next 3 slots) */ + X[H_BASE + 0] = 0; + X[H_BASE + 1] = 0; + X[H_BASE + 2] = 0; + + /* r &= 0xffffffc0ffffffc0ffffffc0fffffff (clamping) */ const uint64_t t0 = load_le(key, 0); const uint64_t t1 = load_le(key, 1); - X[0] = (t0) & 0xffc0fffffff; - X[1] = ((t0 >> 44) | (t1 << 20)) & 0xfffffc0ffff; - X[2] = ((t1 >> 24)) & 0x00ffffffc0f; - - /* h = 0 */ - X[3] = 0; - X[4] = 0; - X[5] = 0; - - /* save pad for later */ - X[6] = load_le(key, 2); - X[7] = load_le(key, 3); + const uint64_t r0 = (t0) & 0xffc0fffffff; + const uint64_t r1 = ((t0 >> 44) | (t1 << 20)) & 0xfffffc0ffff; + const uint64_t r2 = ((t1 >> 24)) & 0x00ffffffc0f; + + // Store r^1 + X[R_BASE + 0] = r0; + X[R_BASE + 1] = r1; + X[R_BASE + 2] = r2; + + poly1305_extend_powers(X, 2); } -void poly1305_blocks(secure_vector& X, const uint8_t* m, size_t blocks, bool is_final = false) { +// Process a single block: h = (h + m) * r mod p +BOTAN_FORCE_INLINE void poly1305_block_single(uint64_t& h0, + uint64_t& h1, + uint64_t& h2, + uint64_t r0, + uint64_t r1, + uint64_t r2, + uint64_t s1, + uint64_t s2, + const uint8_t* m, + uint64_t hibit) { + constexpr uint64_t M44 = 0xFFFFFFFFFFF; + constexpr uint64_t M42 = 0x3FFFFFFFFFF; + #if !defined(BOTAN_TARGET_HAS_NATIVE_UINT128) typedef donna128 uint128_t; #endif - const uint64_t hibit = is_final ? 0 : (static_cast(1) << 40); /* 1 << 128 */ + const uint64_t t0 = load_le(m, 0); + const uint64_t t1 = load_le(m, 1); - const uint64_t r0 = X[0]; - const uint64_t r1 = X[1]; - const uint64_t r2 = X[2]; - - const uint64_t M44 = 0xFFFFFFFFFFF; - const uint64_t M42 = 0x3FFFFFFFFFF; - - uint64_t h0 = X[3 + 0]; - uint64_t h1 = X[3 + 1]; - uint64_t h2 = X[3 + 2]; + h0 += (t0 & M44); + h1 += ((t0 >> 44) | (t1 << 20)) & M44; + h2 += ((t1 >> 24) & M42) | hibit; - const uint64_t s1 = r1 * 20; - const uint64_t s2 = r2 * 20; + const uint128_t d0 = uint128_t(h0) * r0 + uint128_t(h1) * s2 + uint128_t(h2) * s1; + const uint64_t c0 = carry_shift(d0, 44); + + const uint128_t d1 = uint128_t(h0) * r1 + uint128_t(h1) * r0 + uint128_t(h2) * s2 + c0; + const uint64_t c1 = carry_shift(d1, 44); - for(size_t i = 0; i != blocks; ++i) { - const uint64_t t0 = load_le(m, 0); - const uint64_t t1 = load_le(m, 1); + const uint128_t d2 = uint128_t(h0) * r2 + uint128_t(h1) * r1 + uint128_t(h2) * r0 + c1; + const uint64_t c2 = carry_shift(d2, 42); - h0 += ((t0)&M44); - h1 += (((t0 >> 44) | (t1 << 20)) & M44); - h2 += (((t1 >> 24)) & M42) | hibit; + h0 = (d0 & M44) + c2 * 5; + h1 = (d1 & M44) + (h0 >> 44); + h0 &= M44; + h2 = d2 & M42; +} + +// Process two blocks in parallel: h = ((h + m0) * r + m1) * r = (h + m0) * r^2 + m1 * r +// The multiplications by r^2 and r are independent, enabling ILP +BOTAN_FORCE_INLINE void poly1305_block_pair(uint64_t& h0, + uint64_t& h1, + uint64_t& h2, + uint64_t r0, + uint64_t r1, + uint64_t r2, + uint64_t s1, + uint64_t s2, + uint64_t rr0, + uint64_t rr1, + uint64_t rr2, + uint64_t ss1, + uint64_t ss2, + const uint8_t* m, + uint64_t hibit) { + constexpr uint64_t M44 = 0xFFFFFFFFFFF; + constexpr uint64_t M42 = 0x3FFFFFFFFFF; + +#if !defined(BOTAN_TARGET_HAS_NATIVE_UINT128) + typedef donna128 uint128_t; +#endif - const uint128_t d0 = uint128_t(h0) * r0 + uint128_t(h1) * s2 + uint128_t(h2) * s1; - const uint64_t c0 = carry_shift(d0, 44); + // Load first block (will be multiplied by r^2) + const uint64_t m0_t0 = load_le(m, 0); + const uint64_t m0_t1 = load_le(m, 1); + + // Load second block (will be multiplied by r) + const uint64_t m1_t0 = load_le(m + 16, 0); + const uint64_t m1_t1 = load_le(m + 16, 1); + + // Add first block to h + h0 += (m0_t0 & M44); + h1 += ((m0_t0 >> 44) | (m0_t1 << 20)) & M44; + h2 += ((m0_t1 >> 24) & M42) | hibit; + + // Convert second block to limbs + const uint64_t b0 = (m1_t0 & M44); + const uint64_t b1 = ((m1_t0 >> 44) | (m1_t1 << 20)) & M44; + const uint64_t b2 = ((m1_t1 >> 24) & M42) | hibit; + + // Compute (h + m0) * r^2 + m1 * r + const uint128_t d0 = uint128_t(h0) * rr0 + uint128_t(h1) * ss2 + uint128_t(h2) * ss1 + uint128_t(b0) * r0 + + uint128_t(b1) * s2 + uint128_t(b2) * s1; + const uint64_t c0 = carry_shift(d0, 44); + + const uint128_t d1 = uint128_t(h0) * rr1 + uint128_t(h1) * rr0 + uint128_t(h2) * ss2 + uint128_t(b0) * r1 + + uint128_t(b1) * r0 + uint128_t(b2) * s2 + c0; + const uint64_t c1 = carry_shift(d1, 44); + + const uint128_t d2 = uint128_t(h0) * rr2 + uint128_t(h1) * rr1 + uint128_t(h2) * rr0 + uint128_t(b0) * r2 + + uint128_t(b1) * r1 + uint128_t(b2) * r0 + c1; + const uint64_t c2 = carry_shift(d2, 42); - const uint128_t d1 = uint128_t(h0) * r1 + uint128_t(h1) * r0 + uint128_t(h2) * s2 + c0; - const uint64_t c1 = carry_shift(d1, 44); + h0 = (d0 & M44) + c2 * 5; + h1 = (d1 & M44) + (h0 >> 44); + h0 &= M44; + h2 = d2 & M42; +} - const uint128_t d2 = uint128_t(h0) * r2 + uint128_t(h1) * r1 + uint128_t(h2) * r0 + c1; - const uint64_t c2 = carry_shift(d2, 42); +void poly1305_blocks(secure_vector& X, const uint8_t* m, size_t blocks, bool is_final = false) { + const uint64_t hibit = is_final ? 0 : (static_cast(1) << 40); - h0 = d0 & M44; - h1 = d1 & M44; - h2 = d2 & M42; + // Load r (at R_BASE + 0) + const uint64_t r0 = X[R_BASE + 0]; + const uint64_t r1 = X[R_BASE + 1]; + const uint64_t r2 = X[R_BASE + 2]; + const uint64_t s1 = r1 * 20; + const uint64_t s2 = r2 * 20; - h0 += c2 * 5; - h1 += carry_shift(h0, 44); - h0 = h0 & M44; + // Load r^2 (at R_BASE + 3) + const uint64_t rr0 = X[R_BASE + 3]; + const uint64_t rr1 = X[R_BASE + 4]; + const uint64_t rr2 = X[R_BASE + 5]; + + // Precompute + const uint64_t ss1 = rr1 * 20; + const uint64_t ss2 = rr2 * 20; + + // Load accumulator + uint64_t h0 = X[H_BASE + 0]; + uint64_t h1 = X[H_BASE + 1]; + uint64_t h2 = X[H_BASE + 2]; + + while(blocks >= 2) { + poly1305_block_pair(h0, h1, h2, r0, r1, r2, s1, s2, rr0, rr1, rr2, ss1, ss2, m, hibit); + m += 32; + blocks -= 2; + } - m += 16; + // Final block? + if(blocks > 0) { + poly1305_block_single(h0, h1, h2, r0, r1, r2, s1, s2, m, hibit); } - X[3 + 0] = h0; - X[3 + 1] = h1; - X[3 + 2] = h2; + // Store accumulator + X[H_BASE + 0] = h0; + X[H_BASE + 1] = h1; + X[H_BASE + 2] = h2; } void poly1305_finish(secure_vector& X, uint8_t mac[16]) { - const uint64_t M44 = 0xFFFFFFFFFFF; - const uint64_t M42 = 0x3FFFFFFFFFF; + constexpr uint64_t M44 = 0xFFFFFFFFFFF; + constexpr uint64_t M42 = 0x3FFFFFFFFFF; /* fully carry h */ - uint64_t h0 = X[3 + 0]; - uint64_t h1 = X[3 + 1]; - uint64_t h2 = X[3 + 2]; + uint64_t h0 = X[H_BASE + 0]; + uint64_t h1 = X[H_BASE + 1]; + uint64_t h2 = X[H_BASE + 2]; - uint64_t c; - c = (h1 >> 44); + uint64_t c = (h1 >> 44); h1 &= M44; h2 += c; c = (h2 >> 42); @@ -129,17 +296,17 @@ uint64_t g1 = h1 + c; c = (g1 >> 44); g1 &= M44; - uint64_t g2 = h2 + c - (static_cast(1) << 42); + const uint64_t g2 = h2 + c - (static_cast(1) << 42); /* select h if h < p, or h + -p if h >= p */ - const auto c_mask = CT::Mask::expand(c); - h0 = c_mask.select(g0, h0); - h1 = c_mask.select(g1, h1); - h2 = c_mask.select(g2, h2); + const auto h_mask = CT::Mask::expand_top_bit(g2); + h0 = h_mask.select(h0, g0); + h1 = h_mask.select(h1, g1); + h2 = h_mask.select(h2, g2); /* h = (h + pad) */ - const uint64_t t0 = X[6]; - const uint64_t t1 = X[7]; + const uint64_t t0 = X[PAD_BASE + 0]; + const uint64_t t1 = X[PAD_BASE + 1]; h0 += ((t0)&M44); c = (h0 >> 44); @@ -168,16 +335,32 @@ } bool Poly1305::has_keying_material() const { - return m_poly.size() == 8; + // Minimum size: pad(2) + accum(3) + r(3) + r^2(3) = 11 + return m_poly.size() >= 11; } void Poly1305::key_schedule(std::span key) { m_buffer.clear(); - m_poly.resize(8); poly1305_init(m_poly, key.data()); } +std::string Poly1305::provider() const { +#if defined(BOTAN_HAS_POLY1305_AVX512) + if(auto feat = CPUID::check(CPUID::Feature::AVX512)) { + return *feat; + } +#endif + +#if defined(BOTAN_HAS_POLY1305_AVX2) + if(auto feat = CPUID::check(CPUID::Feature::AVX2)) { + return *feat; + } +#endif + + return "base"; +} + void Poly1305::add_data(std::span input) { assert_key_material_set(); @@ -191,7 +374,32 @@ if(m_buffer.in_alignment()) { const auto [aligned_data, full_blocks] = m_buffer.aligned_data_to_process(in); if(full_blocks > 0) { - poly1305_blocks(m_poly, aligned_data.data(), full_blocks); + const uint8_t* data_ptr = aligned_data.data(); + size_t blocks_remaining = full_blocks; + +#if defined(BOTAN_HAS_POLY1305_AVX512) + if(blocks_remaining >= 8 * 3 && CPUID::has(CPUID::Feature::AVX512)) { + // Lazily compute r^3 through r^8 on first AVX512 use + poly1305_extend_powers(m_poly, 8); + const size_t processed = poly1305_avx512_blocks(m_poly, data_ptr, blocks_remaining); + data_ptr += processed * 16; + blocks_remaining -= processed; + } +#endif + +#if defined(BOTAN_HAS_POLY1305_AVX2) + if(blocks_remaining >= 4 * 6 && CPUID::has(CPUID::Feature::AVX2)) { + // Lazily compute r^3 and r^4 on first AVX2 use + poly1305_extend_powers(m_poly, 4); + const size_t processed = poly1305_avx2_blocks(m_poly, data_ptr, blocks_remaining); + data_ptr += processed * 16; + blocks_remaining -= processed; + } +#endif + + if(blocks_remaining > 0) { + poly1305_blocks(m_poly, data_ptr, blocks_remaining); + } } } } diff -Nru botan3-3.7.1+dfsg/src/lib/mac/poly1305/poly1305.h botan3-3.12.0+dfsg/src/lib/mac/poly1305/poly1305.h --- botan3-3.7.1+dfsg/src/lib/mac/poly1305/poly1305.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/mac/poly1305/poly1305.h 2026-05-07 01:38:28.000000000 +0000 @@ -22,6 +22,8 @@ public: std::string name() const override { return "Poly1305"; } + std::string provider() const override; + std::unique_ptr new_object() const override { return std::make_unique(); } void clear() override; @@ -35,10 +37,19 @@ bool has_keying_material() const override; private: - void add_data(std::span) override; - void final_result(std::span) override; - void key_schedule(std::span) override; + void add_data(std::span input) override; + void final_result(std::span output) override; + void key_schedule(std::span key) override; + +#if defined(BOTAN_HAS_POLY1305_AVX2) + static size_t poly1305_avx2_blocks(secure_vector& X, const uint8_t m[], size_t blocks); +#endif + +#if defined(BOTAN_HAS_POLY1305_AVX512) + static size_t poly1305_avx512_blocks(secure_vector& X, const uint8_t m[], size_t blocks); +#endif + // State layout: pad [2] || accum [3] || r [3] || r^2 [3] || ... || r^n [3] secure_vector m_poly; AlignmentBuffer m_buffer; }; diff -Nru botan3-3.7.1+dfsg/src/lib/mac/poly1305/poly1305_avx2/info.txt botan3-3.12.0+dfsg/src/lib/mac/poly1305/poly1305_avx2/info.txt --- botan3-3.7.1+dfsg/src/lib/mac/poly1305/poly1305_avx2/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/mac/poly1305/poly1305_avx2/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,17 @@ + +POLY1305_AVX2 -> 20260108 + + + +name -> "Poly1305 AVX2" +brief -> "Poly1305 using AVX2 instructions" + + + +avx2 + + + +simd_avx2 +cpuid + diff -Nru botan3-3.7.1+dfsg/src/lib/mac/poly1305/poly1305_avx2/poly1305_avx2.cpp botan3-3.12.0+dfsg/src/lib/mac/poly1305/poly1305_avx2/poly1305_avx2.cpp --- botan3-3.7.1+dfsg/src/lib/mac/poly1305/poly1305_avx2/poly1305_avx2.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/mac/poly1305/poly1305_avx2/poly1305_avx2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,255 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan { + +// NOLINTBEGIN(portability-simd-intrinsics) + +namespace { + +constexpr uint32_t MASK26 = 0x3FFFFFF; + +/* +* 4x26 values packed in a 256-bit register +* +* The 26 bit is somewhat a lie; we actually use the full 64 bit width +* but assume that after a 32x32->64 multiply there is still enough +* space to store sums into 64 bits. We could pack slightly more bits, +* but 26x5 = 130 is enough. +*/ +class SIMD_4x26 final { + public: + BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(_mm256_setzero_si256()) {} + + // Construct from raw __m256i (for vectorized loading) + static BOTAN_FN_ISA_AVX2 SIMD_4x26 from_raw(__m256i v) { return SIMD_4x26(v); } + + // Pack 4 values into lanes (high to low: v3, v2, v1, v0) + static BOTAN_FN_ISA_AVX2 SIMD_4x26 set(uint32_t v3, uint32_t v2, uint32_t v1, uint32_t v0) { + return SIMD_4x26(_mm256_set_epi32(0, v3, 0, v2, 0, v1, 0, v0)); + } + + // Multiply by 5: 5*x = (x << 2) + x + BOTAN_FN_ISA_AVX2 SIMD_4x26 mul_5() const { return SIMD_4x26(_mm256_add_epi32(_mm256_slli_epi32(m_v, 2), m_v)); } + + friend SIMD_4x26 BOTAN_FN_ISA_AVX2 operator+(const SIMD_4x26& x, const SIMD_4x26& y) { + return SIMD_4x26(_mm256_add_epi64(x.raw(), y.raw())); + } + + friend SIMD_4x26 BOTAN_FN_ISA_AVX2 operator*(const SIMD_4x26& x, const SIMD_4x26& y) { + return SIMD_4x26(_mm256_mul_epi32(x.raw(), y.raw())); + } + + // Horizontal sum of 4x64-bit values + BOTAN_FN_ISA_AVX2 uint64_t horizontal_add64() const { + uint64_t tmp[4]; + _mm256_storeu_si256(reinterpret_cast<__m256i*>(tmp), m_v); + return tmp[0] + tmp[1] + tmp[2] + tmp[3]; + } + + __m256i BOTAN_FN_ISA_AVX2 raw() const { return m_v; } + + private: + explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(__m256i v) : m_v(v) {} + + __m256i m_v; +}; + +/* +* Vectorized load of 4 message blocks into radix 2^26 representation +* +* Loads 64 bytes (4 blocks), deinterleaves t0/t1 halves, and converts +* to radix 2^26 using vector shift/mask operations. +* +* Lane ordering: block 0 in lane 3, block 3 in lane 0 (reversed for multiply) +*/ +BOTAN_FN_ISA_AVX2 void load_4_blocks_26(SIMD_4x26& msg_0, + SIMD_4x26& msg_1, + SIMD_4x26& msg_2, + SIMD_4x26& msg_3, + SIMD_4x26& msg_4, + const uint8_t* m, + std::array h) { + // Load 64 bytes (4 blocks of 16 bytes each) + const __m256i d0 = _mm256_loadu_si256(reinterpret_cast(m)); + const __m256i d1 = _mm256_loadu_si256(reinterpret_cast(m + 32)); + + // Deinterleave: extract low 64-bit (t0) and high 64-bit (t1) from each block + // unpacklo/hi work within 128-bit lanes: pairs adjacent blocks + const __m256i t0_mixed = _mm256_unpacklo_epi64(d0, d1); // [blk3_lo, blk1_lo, blk2_lo, blk0_lo] + const __m256i t1_mixed = _mm256_unpackhi_epi64(d0, d1); // [blk3_hi, blk1_hi, blk2_hi, blk0_hi] + + const __m256i t0 = _mm256_permute4x64_epi64(t0_mixed, 0b00100111); + const __m256i t1 = _mm256_permute4x64_epi64(t1_mixed, 0b00100111); + + // Constants for radix conversion + const __m256i mask26 = _mm256_set1_epi64x(MASK26); + const __m256i hibit_vec = _mm256_set1_epi64x(1 << 24); + + // Convert to radix 2^26: + // limb0 = t0[25:0] + // limb1 = t0[51:26] + // limb2 = t0[63:52] | t1[13:0] << 12 (bits 52-77) + // limb3 = t1[39:14] (bits 78-103) + // limb4 = t1[63:40] | hibit (bits 104-127 + 2^128 marker) + __m256i limb0 = _mm256_and_si256(t0, mask26); + __m256i limb1 = _mm256_and_si256(_mm256_srli_epi64(t0, 26), mask26); + __m256i limb2 = _mm256_and_si256(_mm256_or_si256(_mm256_srli_epi64(t0, 52), _mm256_slli_epi64(t1, 12)), mask26); + __m256i limb3 = _mm256_and_si256(_mm256_srli_epi64(t1, 14), mask26); + __m256i limb4 = _mm256_or_si256(_mm256_srli_epi64(t1, 40), hibit_vec); + + // Add h to lane 3 (block 0): h + m[0] before multiply by r^4 + limb0 = _mm256_add_epi64(limb0, _mm256_set_epi64x(h[0], 0, 0, 0)); + limb1 = _mm256_add_epi64(limb1, _mm256_set_epi64x(h[1], 0, 0, 0)); + limb2 = _mm256_add_epi64(limb2, _mm256_set_epi64x(h[2], 0, 0, 0)); + limb3 = _mm256_add_epi64(limb3, _mm256_set_epi64x(h[3], 0, 0, 0)); + limb4 = _mm256_add_epi64(limb4, _mm256_set_epi64x(h[4], 0, 0, 0)); + + msg_0 = SIMD_4x26::from_raw(limb0); + msg_1 = SIMD_4x26::from_raw(limb1); + msg_2 = SIMD_4x26::from_raw(limb2); + msg_3 = SIMD_4x26::from_raw(limb3); + msg_4 = SIMD_4x26::from_raw(limb4); +} + +// NOLINTEND(portability-simd-intrinsics) + +// Convert radix-2^26 limbs back to radix-2^44 +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2 void convert_26_to_44(uint64_t& r0, + uint64_t& r1, + uint64_t& r2, + const std::array in) { + constexpr uint64_t M44 = 0xFFFFFFFFFFF; + constexpr uint64_t M42 = 0x3FFFFFFFFFF; + + // Expand to 64 bits + const uint64_t i0 = in[0]; + const uint64_t i1 = in[1]; + const uint64_t i2 = in[2]; + const uint64_t i3 = in[3]; + const uint64_t i4 = in[4]; + + r0 = (i0 | (i1 << 26)) & M44; + r1 = ((i1 >> 18) | (i2 << 8) | (i3 << 34)) & M44; + r2 = ((i3 >> 10) | (i4 << 16)) & M42; +} + +// Convert radix-2^44 limbs to radix-2^26 +BOTAN_FORCE_INLINE std::array convert_44_to_26(uint64_t r0, uint64_t r1, uint64_t r2) { + std::array out{}; + out[0] = static_cast(r0) & MASK26; // bits 0-25 + out[1] = static_cast((r0 >> 26) | (r1 << 18)) & MASK26; // bits 26-51 + out[2] = static_cast(r1 >> 8) & MASK26; // bits 52-77 + out[3] = static_cast((r1 >> 34) | (r2 << 10)) & MASK26; // bits 78-103 + out[4] = static_cast(r2 >> 16) & MASK26; // bits 104-129 + return out; +} + +inline void BOTAN_FN_ISA_AVX2 +load_r(SIMD_4x26& r0, SIMD_4x26& r1, SIMD_4x26& r2, SIMD_4x26& r3, SIMD_4x26& r4, const secure_vector& X) { + // TODO do this in vector registers instead + const auto t = convert_44_to_26(X[5], X[6], X[7]); + const auto t2 = convert_44_to_26(X[8], X[9], X[10]); + const auto t3 = convert_44_to_26(X[11], X[12], X[13]); + const auto t4 = convert_44_to_26(X[14], X[15], X[16]); + + r0 = SIMD_4x26::set(t4[0], t3[0], t2[0], t[0]); + r1 = SIMD_4x26::set(t4[1], t3[1], t2[1], t[1]); + r2 = SIMD_4x26::set(t4[2], t3[2], t2[2], t[2]); + r3 = SIMD_4x26::set(t4[3], t3[3], t2[3], t[3]); + r4 = SIMD_4x26::set(t4[4], t3[4], t2[4], t[4]); +} + +} // namespace + +/* +* Process 4 blocks at a time using AVX2 +* h = (h + m[0]) * r^4 + m[1] * r^3 + m[2] * r^2 + m[3] * r +*/ +size_t BOTAN_FN_ISA_AVX2 Poly1305::poly1305_avx2_blocks(secure_vector& X, const uint8_t m[], size_t blocks) { + if(blocks < 4) { + return 0; + } + + const size_t incoming_blocks = blocks; + + auto h = convert_44_to_26(X[2], X[3], X[4]); + + SIMD_4x26 r0; + SIMD_4x26 r1; + SIMD_4x26 r2; + SIMD_4x26 r3; + SIMD_4x26 r4; + load_r(r0, r1, r2, r3, r4, X); + + const auto r1_5 = r1.mul_5(); + const auto r2_5 = r2.mul_5(); + const auto r3_5 = r3.mul_5(); + const auto r4_5 = r4.mul_5(); + + while(blocks >= 4) { + // Load 4 message blocks, convert to radix 2^26, and add h to block 0 + SIMD_4x26 m0; + SIMD_4x26 m1; + SIMD_4x26 m2; + SIMD_4x26 m3; + SIMD_4x26 m4; + load_4_blocks_26(m0, m1, m2, m3, m4, m, h); + + const auto d0 = m0 * r0 + m1 * r4_5 + m2 * r3_5 + m3 * r2_5 + m4 * r1_5; + const auto d1 = m0 * r1 + m1 * r0 + m2 * r4_5 + m3 * r3_5 + m4 * r2_5; + const auto d2 = m0 * r2 + m1 * r1 + m2 * r0 + m3 * r4_5 + m4 * r3_5; + const auto d3 = m0 * r3 + m1 * r2 + m2 * r1 + m3 * r0 + m4 * r4_5; + const auto d4 = m0 * r4 + m1 * r3 + m2 * r2 + m3 * r1 + m4 * r0; + + const uint64_t h0_64 = d0.horizontal_add64(); + uint64_t h1_64 = d1.horizontal_add64(); + uint64_t h2_64 = d2.horizontal_add64(); + uint64_t h3_64 = d3.horizontal_add64(); + uint64_t h4_64 = d4.horizontal_add64(); + + h1_64 += h0_64 >> 26; + h[0] = static_cast(h0_64) & MASK26; + h2_64 += h1_64 >> 26; + h[1] = static_cast(h1_64) & MASK26; + h3_64 += h2_64 >> 26; + h[2] = static_cast(h2_64) & MASK26; + h4_64 += h3_64 >> 26; + h[3] = static_cast(h3_64) & MASK26; + + const uint64_t c = h4_64 >> 26; + h[4] = static_cast(h4_64) & MASK26; + + uint64_t carry = c * 5; + carry += h[0]; + h[0] = static_cast(carry) & MASK26; + carry >>= 26; + carry += h[1]; + h[1] = static_cast(carry) & MASK26; + carry >>= 26; + carry += h[2]; + h[2] = static_cast(carry) & MASK26; + carry >>= 26; + carry += h[3]; + h[3] = static_cast(carry) & MASK26; + carry >>= 26; + h[4] += static_cast(carry); + + m += 64; + blocks -= 4; + } + + convert_26_to_44(X[2], X[3], X[4], h); + + return (incoming_blocks - blocks); +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/mac/poly1305/poly1305_avx512/info.txt botan3-3.12.0+dfsg/src/lib/mac/poly1305/poly1305_avx512/info.txt --- botan3-3.7.1+dfsg/src/lib/mac/poly1305/poly1305_avx512/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/mac/poly1305/poly1305_avx512/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,17 @@ + +POLY1305_AVX512 -> 20260108 + + + +name -> "Poly1305 AVX512" +brief -> "Poly1305 using AVX-512 IFMA instructions" + + + +avx512 + + + +simd_avx512 +cpuid + diff -Nru botan3-3.7.1+dfsg/src/lib/mac/poly1305/poly1305_avx512/poly1305_avx512.cpp botan3-3.12.0+dfsg/src/lib/mac/poly1305/poly1305_avx512/poly1305_avx512.cpp --- botan3-3.7.1+dfsg/src/lib/mac/poly1305/poly1305_avx512/poly1305_avx512.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/mac/poly1305/poly1305_avx512/poly1305_avx512.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,222 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan { + +namespace { + +// NOLINTBEGIN(portability-simd-intrinsics) + +class SIMD_8x44 final { + public: + BOTAN_FN_ISA_AVX512 SIMD_8x44() : m_v(_mm512_setzero_si512()) {} + + static BOTAN_FN_ISA_AVX512 SIMD_8x44 splat(uint64_t x) { return SIMD_8x44(_mm512_set1_epi64(x)); } + + static BOTAN_FN_ISA_AVX512 SIMD_8x44 load(const void* p) { + return SIMD_8x44(_mm512_loadu_si512(reinterpret_cast(p))); + } + + BOTAN_FN_ISA_AVX512 SIMD_8x44(int e7, int e6, int e5, int e4, int e3, int e2, int e1, int e0) : + SIMD_8x44(_mm512_set_epi64(e7, e6, e5, e4, e3, e2, e1, e0)) {} + + // Permute across two vectors using index vector + static BOTAN_FN_ISA_AVX512 SIMD_8x44 permute2(const SIMD_8x44& idx, const SIMD_8x44& a, const SIMD_8x44& b) { + return SIMD_8x44(_mm512_permutex2var_epi64(a.m_v, idx.m_v, b.m_v)); + } + + static BOTAN_FN_ISA_AVX512 SIMD_8x44 permute3( + const SIMD_8x44& idx0, const SIMD_8x44& idx1, const SIMD_8x44& a, const SIMD_8x44& b, const SIMD_8x44& c) { + return SIMD_8x44::permute2(idx1, SIMD_8x44::permute2(idx0, a, b), c); + } + + // VBMI2 double shift right: concatenate (b:a) and shift right by count + template + static BOTAN_FN_ISA_AVX512 SIMD_8x44 shrdi(const SIMD_8x44& a, const SIMD_8x44& b) { + return SIMD_8x44(_mm512_shrdi_epi64(a.m_v, b.m_v, COUNT)); + } + + BOTAN_FN_ISA_AVX512 SIMD_8x44 add_lane_zero(uint64_t b) { + return SIMD_8x44(_mm512_mask_add_epi64(m_v, 0x01, m_v, _mm512_set1_epi64(b))); + } + + // IFMA: accumulator += (a * b) low 52 bits + BOTAN_FN_ISA_AVX512 SIMD_8x44& ifma_lo(const SIMD_8x44& a, const SIMD_8x44& b) { + m_v = _mm512_madd52lo_epu64(m_v, a.m_v, b.m_v); + return *this; + } + + // IFMA: accumulator += (a * b) high 52 bits + BOTAN_FN_ISA_AVX512 SIMD_8x44& ifma_hi(const SIMD_8x44& a, const SIMD_8x44& b) { + m_v = _mm512_madd52hi_epu64(m_v, a.m_v, b.m_v); + return *this; + } + + // Multiply by 20: 20*x = (x << 4) + (x << 2) + BOTAN_FN_ISA_AVX512 SIMD_8x44 mul_20() const { + return SIMD_8x44(_mm512_add_epi64(_mm512_slli_epi64(m_v, 4), _mm512_slli_epi64(m_v, 2))); + } + + template + BOTAN_FN_ISA_AVX512 SIMD_8x44 shr() const { + return SIMD_8x44(_mm512_srli_epi64(m_v, S)); + } + + BOTAN_FN_ISA_AVX512 uint64_t horizontal_add() const { return _mm512_reduce_add_epi64(m_v); } + + BOTAN_FN_ISA_AVX512 SIMD_8x44 operator&(const SIMD_8x44& other) const { + return SIMD_8x44(_mm512_and_si512(m_v, other.m_v)); + } + + BOTAN_FN_ISA_AVX512 SIMD_8x44 operator|(const SIMD_8x44& other) const { + return SIMD_8x44(_mm512_or_si512(m_v, other.m_v)); + } + + static BOTAN_FN_ISA_AVX512 void interleave_3x8(SIMD_8x44& r0, SIMD_8x44& r1, SIMD_8x44& r2) { + const auto idx1_z0 = SIMD_8x44(0, 3, 6, 9, 12, 15, -1, -1); + const auto idx2_z0 = SIMD_8x44(7, 6, 5, 4, 3, 2, 10, 13); + const auto idx1_z1 = SIMD_8x44(1, 4, 7, 10, 13, -1, -1, -1); + const auto idx2_z1 = SIMD_8x44(7, 6, 5, 4, 3, 8, 11, 14); + const auto idx1_z2 = SIMD_8x44(2, 5, 8, 11, 14, -1, -1, -1); + const auto idx2_z2 = SIMD_8x44(7, 6, 5, 4, 3, 9, 12, 15); + + // NOLINTBEGIN(*-suspicious-call-argument) + auto z0 = SIMD_8x44::permute3(idx1_z0, idx2_z0, r0, r1, r2); + auto z1 = SIMD_8x44::permute3(idx1_z1, idx2_z1, r0, r1, r2); + auto z2 = SIMD_8x44::permute3(idx1_z2, idx2_z2, r0, r1, r2); + // NOLINTEND(*-suspicious-call-argument) + + r0 = z0; + r1 = z1; + r2 = z2; + } + + private: + __m512i BOTAN_FN_ISA_AVX512 raw() const { return m_v; } + + explicit BOTAN_FN_ISA_AVX512 SIMD_8x44(__m512i v) : m_v(v) {} + + __m512i m_v; +}; + +// NOLINTEND(portability-simd-intrinsics) + +} // namespace + +/* +* Process 8 blocks at a time using AVX-512 IFMA +* h = (h + m[0]) * r^8 + m[1] * r^7 + ... + m[7] * r +*/ +size_t BOTAN_FN_ISA_AVX512 Poly1305::poly1305_avx512_blocks(secure_vector& X, + const uint8_t* m, + size_t blocks) { + constexpr uint64_t M44 = 0xFFFFFFFFFFF; + constexpr uint64_t M42 = 0x3FFFFFFFFFF; + constexpr uint64_t hibit64 = static_cast(1) << 40; + + if(blocks < 8) { + return 0; + } + + const size_t original_blocks = blocks; + + // Load h from state + uint64_t h0 = X[2]; + uint64_t h1 = X[3]; + uint64_t h2 = X[4]; + + SIMD_8x44 r0 = SIMD_8x44::load(&X[5]); + SIMD_8x44 r1 = SIMD_8x44::load(&X[5 + 8]); + SIMD_8x44 r2 = SIMD_8x44::load(&X[5 + 2 * 8]); + SIMD_8x44::interleave_3x8(r0, r1, r2); + + const auto s1 = r1.mul_20(); + const auto s2 = r2.mul_20(); + + // Constants for vectorized message loading + // Deinterleave indices: separate low (t0) and high (t1) 64-bit halves of each 128-bit block + // Memory layout: [t0_0, t1_0, t0_1, t1_1, ...] -> want [t0_0..t0_7] and [t1_0..t1_7] + const auto idx_lo = SIMD_8x44(14, 12, 10, 8, 6, 4, 2, 0); + const auto idx_hi = SIMD_8x44(15, 13, 11, 9, 7, 5, 3, 1); + const auto mask44 = SIMD_8x44::splat(M44); + const auto mask42 = SIMD_8x44::splat(M42); + const auto hibit = SIMD_8x44::splat(hibit64); + + while(blocks >= 8) { + // Load 8 message blocks (128 bytes) with two 512-bit loads + const auto data0 = SIMD_8x44::load(m); + const auto data1 = SIMD_8x44::load(m + 64); + + // Deinterleave: separate low and high 64-bit halves of each 128-bit block + const auto t0 = SIMD_8x44::permute2(idx_lo, data0, data1); + const auto t1 = SIMD_8x44::permute2(idx_hi, data0, data1); + + // Convert to radix 2^44 representation using VBMI2 + // limb0 = t0[43:0] + // limb1 = t1[23:0]:t0[63:44] (bits 44-87 of block) + // limb2 = t1[63:24] | hibit (bits 88-129 of block + high bit) + auto m0 = t0 & mask44; + auto m1 = SIMD_8x44::shrdi<44>(t0, t1) & mask44; + auto m2 = (t1.shr<24>() & mask42) | hibit; + + // Add h to first block + m0 = m0.add_lane_zero(h0); + m1 = m1.add_lane_zero(h1); + m2 = m2.add_lane_zero(h2); + + // d0 = m0*r0 + m1*s2 + m2*s1 + const SIMD_8x44 d0_lo = SIMD_8x44().ifma_lo(m0, r0).ifma_lo(m1, s2).ifma_lo(m2, s1); + const SIMD_8x44 d0_hi = SIMD_8x44().ifma_hi(m0, r0).ifma_hi(m1, s2).ifma_hi(m2, s1); + + // d1 = m0*r1 + m1*r0 + m2*s2 + const SIMD_8x44 d1_lo = SIMD_8x44().ifma_lo(m0, r1).ifma_lo(m1, r0).ifma_lo(m2, s2); + const SIMD_8x44 d1_hi = SIMD_8x44().ifma_hi(m0, r1).ifma_hi(m1, r0).ifma_hi(m2, s2); + + // d2 = m0*r2 + m1*r1 + m2*r0 + const SIMD_8x44 d2_lo = SIMD_8x44().ifma_lo(m0, r2).ifma_lo(m1, r1).ifma_lo(m2, r0); + const SIMD_8x44 d2_hi = SIMD_8x44().ifma_hi(m0, r2).ifma_hi(m1, r1).ifma_hi(m2, r0); + + // Horizontal adds can't overflow - at most 8*3*(2**52-1) ~= 2**57 + const uint64_t sum0_lo = d0_lo.horizontal_add(); + const uint64_t sum0_hi = d0_hi.horizontal_add(); + uint64_t sum1_lo = d1_lo.horizontal_add(); + const uint64_t sum1_hi = d1_hi.horizontal_add(); + uint64_t sum2_lo = d2_lo.horizontal_add(); + const uint64_t sum2_hi = d2_hi.horizontal_add(); + + h0 = sum0_lo & M44; + sum1_lo += (sum0_lo >> 44) + (sum0_hi << 8); + h1 = sum1_lo & M44; + sum2_lo += (sum1_lo >> 44) + (sum1_hi << 8); + h2 = sum2_lo & M42; + + // Wrap-around reduction: carry * 5 goes back to h0 + uint64_t carry = ((sum2_lo >> 42) + (sum2_hi << 10)) * 5; + carry += h0; + h0 = carry & M44; + carry >>= 44; + carry += h1; + h1 = carry & M44; + carry >>= 44; + h2 += carry; + + m += 8 * 16; + blocks -= 8; + } + + X[2] = h0; + X[3] = h1; + X[4] = h2; + + return (original_blocks - blocks); +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/mac/siphash/info.txt botan3-3.12.0+dfsg/src/lib/mac/siphash/info.txt --- botan3-3.7.1+dfsg/src/lib/mac/siphash/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/mac/siphash/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -4,4 +4,5 @@ name -> "SipHash" +lifecycle -> "Deprecated" diff -Nru botan3-3.7.1+dfsg/src/lib/mac/siphash/siphash.cpp botan3-3.12.0+dfsg/src/lib/mac/siphash/siphash.cpp --- botan3-3.7.1+dfsg/src/lib/mac/siphash/siphash.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/mac/siphash/siphash.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,17 +7,20 @@ #include +#include #include #include #include -#include namespace Botan { namespace { void SipRounds(uint64_t M, secure_vector& V, size_t r) { - uint64_t V0 = V[0], V1 = V[1], V2 = V[2], V3 = V[3]; + uint64_t V0 = V[0]; + uint64_t V1 = V[1]; + uint64_t V2 = V[2]; + uint64_t V3 = V[3]; V3 ^= M; for(size_t i = 0; i != r; ++i) { @@ -55,7 +58,7 @@ BufferSlicer in(input); - if(m_mbuf_pos) { + if(m_mbuf_pos > 0) { while(!in.empty() && m_mbuf_pos != 8) { m_mbuf = (m_mbuf >> 8) | (static_cast(in.take_byte()) << 56); ++m_mbuf_pos; diff -Nru botan3-3.7.1+dfsg/src/lib/mac/siphash/siphash.h botan3-3.12.0+dfsg/src/lib/mac/siphash/siphash.h --- botan3-3.7.1+dfsg/src/lib/mac/siphash/siphash.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/mac/siphash/siphash.h 2026-05-07 01:38:28.000000000 +0000 @@ -14,7 +14,7 @@ class SipHash final : public MessageAuthenticationCode { public: - SipHash(size_t c = 2, size_t d = 4) : m_C(c), m_D(d) {} + explicit SipHash(size_t c = 2, size_t d = 4) : m_C(c), m_D(d) {} void clear() override; std::string name() const override; @@ -28,9 +28,9 @@ Key_Length_Specification key_spec() const override { return Key_Length_Specification(16); } private: - void add_data(std::span) override; - void final_result(std::span) override; - void key_schedule(std::span) override; + void add_data(std::span input) override; + void final_result(std::span output) override; + void key_schedule(std::span key) override; const size_t m_C, m_D; secure_vector m_K; diff -Nru botan3-3.7.1+dfsg/src/lib/mac/x919_mac/x919_mac.cpp botan3-3.12.0+dfsg/src/lib/mac/x919_mac/x919_mac.cpp --- botan3-3.7.1+dfsg/src/lib/mac/x919_mac/x919_mac.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/mac/x919_mac/x919_mac.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,7 +7,8 @@ #include -#include +#include +#include namespace Botan { @@ -42,7 +43,7 @@ * Finalize an ANSI X9.19 MAC Calculation */ void ANSI_X919_MAC::final_result(std::span mac) { - if(m_position) { + if(m_position > 0) { m_des1->encrypt(m_state); } m_des2->decrypt(m_state.data(), mac.data()); @@ -91,6 +92,6 @@ /* * ANSI X9.19 MAC Constructor */ -ANSI_X919_MAC::ANSI_X919_MAC() : m_des1(BlockCipher::create("DES")), m_des2(m_des1->new_object()), m_position(0) {} +ANSI_X919_MAC::ANSI_X919_MAC() : m_des1(BlockCipher::create_or_throw("DES")), m_des2(m_des1->new_object()) {} } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/mac/x919_mac/x919_mac.h botan3-3.12.0+dfsg/src/lib/mac/x919_mac/x919_mac.h --- botan3-3.7.1+dfsg/src/lib/mac/x919_mac/x919_mac.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/mac/x919_mac/x919_mac.h 2026-05-07 01:38:28.000000000 +0000 @@ -31,17 +31,14 @@ ANSI_X919_MAC(); - ANSI_X919_MAC(const ANSI_X919_MAC&) = delete; - ANSI_X919_MAC& operator=(const ANSI_X919_MAC&) = delete; - private: - void add_data(std::span) override; - void final_result(std::span) override; - void key_schedule(std::span) override; + void add_data(std::span input) override; + void final_result(std::span output) override; + void key_schedule(std::span key) override; std::unique_ptr m_des1, m_des2; secure_vector m_state; - size_t m_position; + size_t m_position = 0; }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/math/bigint/big_code.cpp botan3-3.12.0+dfsg/src/lib/math/bigint/big_code.cpp --- botan3-3.7.1+dfsg/src/lib/math/bigint/big_code.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/bigint/big_code.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,24 +7,43 @@ #include +#include +#include #include +#include +#include #include -#include +#include namespace Botan { +namespace { + +consteval word decimal_conversion_radix() { + if constexpr(sizeof(word) == 8) { + return 10000000000000000000U; + } else { + return 1000000000U; + } +} + +consteval size_t decimal_conversion_radix_digits() { + if constexpr(sizeof(word) == 8) { + return 19; + } else { + return 9; + } +} + +} // namespace + std::string BigInt::to_dec_string() const { // Use the largest power of 10 that fits in a word -#if(BOTAN_MP_WORD_BITS == 64) - const word conversion_radix = 10000000000000000000U; - const word radix_digits = 19; -#else - const word conversion_radix = 1000000000U; - const word radix_digits = 9; -#endif + constexpr word conversion_radix = decimal_conversion_radix(); + constexpr size_t radix_digits = decimal_conversion_radix_digits(); // (over-)estimate of the number of digits needed; log2(10) ~ 3.3219 - const size_t digit_estimate = static_cast(1 + (this->bits() / 3.32)); + const size_t digit_estimate = static_cast(1 + (static_cast(this->bits()) / 3.32)); // (over-)estimate of db such that conversion_radix^db > *this const size_t digit_blocks = (digit_estimate + radix_digits - 1) / radix_digits; @@ -49,10 +68,10 @@ for(size_t i = 0; i != digit_blocks; ++i) { word remainder = digit_groups[i]; for(size_t j = 0; j != radix_digits; ++j) { - // Compiler should convert div/mod by 10 into mul by magic constant - const word digit = remainder % 10; - remainder /= 10; + const word new_remainder = divide_10(remainder); + const word digit = remainder - new_remainder * 10; digits[radix_digits * i + j] = static_cast(digit); + remainder = new_remainder; } } @@ -67,11 +86,13 @@ std::string s; s.reserve(1 + digits.size()); - if(is_negative()) { + if(signum() < 0) { s += "-"; } // Reverse and convert to textual digits + // TODO(Botan4) use std::ranges::reverse_view here once available (need newer Clang) + // NOLINTNEXTLINE(modernize-loop-convert) for(auto i = digits.rbegin(); i != digits.rend(); ++i) { s.push_back(*i + '0'); // assumes ASCII } @@ -92,7 +113,7 @@ } std::string hrep; - if(is_negative()) { + if(signum() < 0) { hrep += "-"; } hrep += "0x"; @@ -100,11 +121,70 @@ return hrep; } +//static +BigInt BigInt::from_radix_digits(std::string_view digits, size_t radix) { + if(radix == 16) { + secure_vector binary; + + if(digits.size() % 2 == 1) { + // Handle lack of leading 0 + const char buf0_with_leading_0[2] = {'0', digits[0]}; + + binary = hex_decode_locked(buf0_with_leading_0, 2); + + if(digits.size() > 1) { + binary += hex_decode_locked(&digits[1], digits.size() - 1, false); + } + } else { + binary = hex_decode_locked(digits, false); + } + + return BigInt::from_bytes(binary); + } else if(radix == 10) { + // Use the largest power of 10 that fits in a word, accumulating + // groups of digits into word-sized chunks to minimize the number + // of multiprecision multiplications. + constexpr word conversion_radix = decimal_conversion_radix(); + constexpr size_t radix_digits = decimal_conversion_radix_digits(); + + BigInt r; + + // Handle the initial partial block (if digit count is not a multiple of radix_digits) + const size_t partial_block = digits.size() % radix_digits; + + if(partial_block > 0) { + word acc = 0; + for(size_t i = 0; i < partial_block; ++i) { + const char c = digits[i]; + BOTAN_ARG_CHECK(c >= '0' && c <= '9', "Invalid decimal character"); + acc = acc * 10 + static_cast(c - '0'); + } + r += acc; + } + + // Process full blocks of radix_digits + for(size_t i = partial_block; i != digits.size(); i += radix_digits) { + word acc = 0; + for(size_t j = 0; j < radix_digits; ++j) { + const char c = digits[i + j]; + BOTAN_ARG_CHECK(c >= '0' && c <= '9', "Invalid decimal character"); + acc = acc * 10 + static_cast(c - '0'); + } + r *= conversion_radix; + r += acc; + } + + return r; + } else { + throw Invalid_Argument("BigInt::from_radix_digits unknown radix"); + } +} + /* * Encode two BigInt, with leading 0s if needed, and concatenate */ secure_vector BigInt::encode_fixed_length_int_pair(const BigInt& n1, const BigInt& n2, size_t bytes) { - if(n1.is_negative() || n2.is_negative()) { + if(n1.signum() < 0 || n2.signum() < 0) { throw Encoding_Error("encode_fixed_length_int_pair: values must be positive"); } if(n1.bytes() > bytes || n2.bytes() > bytes) { @@ -131,41 +211,11 @@ if(base == Binary) { return BigInt::from_bytes(std::span{buf, length}); } else if(base == Hexadecimal) { - BigInt r; - secure_vector binary; - - if(length % 2) { - // Handle lack of leading 0 - const char buf0_with_leading_0[2] = {'0', static_cast(buf[0])}; - - binary = hex_decode_locked(buf0_with_leading_0, 2); - - if(length > 1) { - binary += hex_decode_locked(cast_uint8_ptr_to_char(&buf[1]), length - 1, false); - } - } else { - binary = hex_decode_locked(cast_uint8_ptr_to_char(buf), length, false); - } - - r.assign_from_bytes(binary); - return r; + const std::string_view sv{cast_uint8_ptr_to_char(buf), length}; + return BigInt::from_radix_digits(sv, 16); } else if(base == Decimal) { - BigInt r; - // This could be made faster using the same trick as to_dec_string - for(size_t i = 0; i != length; ++i) { - const char c = buf[i]; - - if(c < '0' || c > '9') { - throw Invalid_Argument("BigInt::decode: invalid decimal char"); - } - - const uint8_t x = c - '0'; - BOTAN_ASSERT_NOMSG(x < 10); - - r *= 10; - r += x; - } - return r; + const std::string_view sv{cast_uint8_ptr_to_char(buf), length}; + return BigInt::from_radix_digits(sv, 10); } else { throw Invalid_Argument("Unknown BigInt decoding method"); } diff -Nru botan3-3.7.1+dfsg/src/lib/math/bigint/big_io.cpp botan3-3.12.0+dfsg/src/lib/math/bigint/big_io.cpp --- botan3-3.7.1+dfsg/src/lib/math/bigint/big_io.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/bigint/big_io.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,6 +6,8 @@ */ #include + +#include #include #include @@ -16,13 +18,11 @@ */ std::ostream& operator<<(std::ostream& stream, const BigInt& n) { const auto stream_flags = stream.flags(); - // NOLINTNEXTLINE(*-non-zero-enum-to-bool-conversion) - if(stream_flags & std::ios::oct) { + if((stream_flags & std::ios::oct) != 0) { throw Invalid_Argument("Octal output of BigInt not supported"); } - // NOLINTNEXTLINE(*-non-zero-enum-to-bool-conversion) - const size_t base = (stream_flags & std::ios::hex) ? 16 : 10; + const size_t base = (stream_flags & std::ios::hex) != 0 ? 16 : 10; if(base == 10) { stream << n.to_dec_string(); diff -Nru botan3-3.7.1+dfsg/src/lib/math/bigint/big_ops2.cpp botan3-3.12.0+dfsg/src/lib/math/bigint/big_ops2.cpp --- botan3-3.7.1+dfsg/src/lib/math/bigint/big_ops2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/bigint/big_ops2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,9 +7,9 @@ #include +#include #include #include -#include namespace Botan { @@ -19,7 +19,8 @@ grow_to(std::max(x_sw, y_words) + 1); if(sign() == y_sign) { - bigint_add2(mutable_data(), size() - 1, y, y_words); + const word carry = bigint_add2(mutable_data(), size() - 1, y, y_words); + mutable_data()[size() - 1] += carry; } else { const int32_t relative_size = bigint_cmp(_data(), x_sw, y, y_words); @@ -27,11 +28,10 @@ // *this >= y bigint_sub2(mutable_data(), x_sw, y, y_words); } else { - // *this < y + // *this < y: compute *this = y - *this bigint_sub2_rev(mutable_data(), y, y_words); } - //this->sign_fixup(relative_size, y_sign); if(relative_size < 0) { set_sign(y_sign); } else if(relative_size == 0) { @@ -43,7 +43,7 @@ } BigInt& BigInt::mod_add(const BigInt& s, const BigInt& mod, secure_vector& ws) { - if(this->is_negative() || s.is_negative() || mod.is_negative()) { + if(this->signum() < 0 || s.signum() < 0 || mod.signum() < 0) { throw Invalid_Argument("BigInt::mod_add expects all arguments are positive"); } @@ -71,6 +71,8 @@ ws.resize(3 * mod_sw); } + // NOLINTBEGIN(readability-container-data-pointer) + word borrow = bigint_sub3(&ws[0], mod._data(), mod_sw, s._data(), mod_sw); BOTAN_DEBUG_ASSERT(borrow == 0); BOTAN_UNUSED(borrow); @@ -79,16 +81,18 @@ borrow = bigint_sub3(&ws[mod_sw], this->_data(), mod_sw, &ws[0], mod_sw); // Compute t + s - bigint_add3_nc(&ws[mod_sw * 2], this->_data(), mod_sw, s._data(), mod_sw); + bigint_add3(&ws[mod_sw * 2], this->_data(), mod_sw, s._data(), mod_sw); CT::conditional_copy_mem(borrow, &ws[0], &ws[mod_sw * 2], &ws[mod_sw], mod_sw); set_words(&ws[0], mod_sw); + // NOLINTEND(readability-container-data-pointer) + return (*this); } BigInt& BigInt::mod_sub(const BigInt& s, const BigInt& mod, secure_vector& ws) { - if(this->is_negative() || s.is_negative() || mod.is_negative()) { + if(this->signum() < 0 || s.signum() < 0 || mod.signum() < 0) { throw Invalid_Argument("BigInt::mod_sub expects all arguments are positive"); } @@ -105,13 +109,18 @@ ws.resize(mod_sw); } - bigint_mod_sub(mutable_data(), s._data(), mod._data(), mod_sw, ws.data()); + const word borrow = bigint_sub3(ws.data(), mutable_data(), mod_sw, s._data(), mod_sw); + + // Conditionally add back the modulus + bigint_cnd_add(borrow, ws.data(), mod._data(), mod_sw); + + unchecked_copy_memory(mutable_data(), ws.data(), mod_sw); return (*this); } BigInt& BigInt::mod_mul(uint8_t y, const BigInt& mod, secure_vector& ws) { - BOTAN_ARG_CHECK(this->is_negative() == false, "*this must be positive"); + BOTAN_ARG_CHECK(this->signum() >= 0, "*this must be positive"); BOTAN_ARG_CHECK(y < 16, "y too large"); BOTAN_DEBUG_ASSERT(*this < mod); @@ -122,20 +131,10 @@ } BigInt& BigInt::rev_sub(const word y[], size_t y_sw, secure_vector& ws) { - if(this->sign() != BigInt::Positive) { - throw Invalid_State("BigInt::sub_rev requires this is positive"); - } - - const size_t x_sw = this->sig_words(); - - ws.resize(std::max(x_sw, y_sw)); - clear_mem(ws.data(), ws.size()); - - const int32_t relative_size = bigint_sub_abs(ws.data(), _data(), x_sw, y, y_sw); - - this->cond_flip_sign(relative_size > 0); - this->swap_reg(ws); - + BOTAN_UNUSED(ws); + BigInt y_bn; + y_bn.m_data.set_words(y, y_sw); + *this = y_bn - *this; return (*this); } @@ -155,15 +154,14 @@ if(x_sw == 0 || y_sw == 0) { clear(); set_sign(Positive); - } else if(x_sw == 1 && y_sw) { + } else if(x_sw == 1 && y_sw > 0) { grow_to(y_sw + 1); bigint_linmul3(mutable_data(), y._data(), y_sw, word_at(0)); - } else if(y_sw == 1 && x_sw) { - word carry = bigint_linmul2(mutable_data(), x_sw, y.word_at(0)); - set_word_at(x_sw, carry); } else { const size_t new_size = x_sw + y_sw + 1; - ws.resize(new_size); + if(ws.size() < new_size) { + ws.resize(new_size); + } secure_vector z_reg(new_size); bigint_mul(z_reg.data(), z_reg.size(), _data(), size(), x_sw, y._data(), y.size(), y_sw, ws.data(), ws.size()); @@ -204,7 +202,7 @@ * Division Operator */ BigInt& BigInt::operator/=(const BigInt& y) { - if(y.sig_words() == 1 && is_power_of_2(y.word_at(0))) { + if(y.sig_words() == 1 && signum() >= 0 && y.signum() >= 0 && is_power_of_2(y.word_at(0))) { (*this) >>= (y.bits() - 1); } else { (*this) = (*this) / y; @@ -232,13 +230,14 @@ if(is_power_of_2(mod)) { remainder = (word_at(0) & (mod - 1)); } else { + const divide_precomp redc_mod(mod); const size_t sw = sig_words(); for(size_t i = sw; i > 0; --i) { - remainder = bigint_modop_vartime(remainder, word_at(i - 1), mod); + remainder = redc_mod.vartime_mod_2to1(remainder, word_at(i - 1)); } } - if(remainder && sign() == BigInt::Negative) { + if(remainder != 0 && sign() == BigInt::Negative) { remainder = mod - remainder; } @@ -253,7 +252,7 @@ */ BigInt& BigInt::operator<<=(size_t shift) { const size_t sw = sig_words(); - const size_t new_size = sw + (shift + BOTAN_MP_WORD_BITS - 1) / BOTAN_MP_WORD_BITS; + const size_t new_size = sw + (shift + WordInfo::bits - 1) / WordInfo::bits; m_data.grow_to(new_size); @@ -268,8 +267,8 @@ BigInt& BigInt::operator>>=(size_t shift) { bigint_shr1(m_data.mutable_data(), m_data.size(), shift); - if(is_negative() && is_zero()) { - set_sign(Positive); + if(sig_words() == 0 && m_signedness == Negative) { + m_signedness = Positive; } return (*this); diff -Nru botan3-3.7.1+dfsg/src/lib/math/bigint/big_ops3.cpp botan3-3.12.0+dfsg/src/lib/math/bigint/big_ops3.cpp --- botan3-3.7.1+dfsg/src/lib/math/bigint/big_ops3.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/bigint/big_ops3.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include +#include #include #include #include @@ -16,23 +17,33 @@ namespace Botan { //static -BigInt BigInt::add2(const BigInt& x, const word y[], size_t y_words, BigInt::Sign y_sign) { +BigInt BigInt::add2(const BigInt& x, const word y[], size_t y_size, BigInt::Sign y_sign) { const size_t x_sw = x.sig_words(); - BigInt z = BigInt::with_capacity(std::max(x_sw, y_words) + 1); + BigInt z = BigInt::with_capacity(std::max(x_sw, y_size) + 1); if(x.sign() == y_sign) { - bigint_add3(z.mutable_data(), x._data(), x_sw, y, y_words); + const word carry = bigint_add3(z.mutable_data(), x._data(), x_sw, y, y_size); + z.mutable_data()[std::max(x_sw, y_size)] += carry; z.set_sign(x.sign()); } else { - const int32_t relative_size = bigint_sub_abs(z.mutable_data(), x._data(), x_sw, y, y_words); + const int32_t relative_size = bigint_cmp(x.data(), x_sw, y, y_size); - //z.sign_fixup(relative_size, y_sign); if(relative_size < 0) { + // x < y so z = abs(y - x) + // NOLINTNEXTLINE(*-suspicious-call-argument) intentionally swapping x and y here + bigint_sub3(z.mutable_data(), y, y_size, x.data(), x_sw); z.set_sign(y_sign); } else if(relative_size == 0) { - z.set_sign(BigInt::Positive); + // Positive zero (nothing to do in this case) } else { + /* + * We know at this point that x >= y so if y_size is larger than + * x_sw, we are guaranteed they are just leading zeros which can + * be ignored + */ + y_size = std::min(x_sw, y_size); + bigint_sub3(z.mutable_data(), x.data(), x_sw, y, y_size); z.set_sign(x.sign()); } } @@ -49,11 +60,11 @@ BigInt z = BigInt::with_capacity(x.size() + y.size()); - if(x_sw == 1 && y_sw) { + if(x_sw == 1 && y_sw > 0) { bigint_linmul3(z.mutable_data(), y._data(), y_sw, x.word_at(0)); - } else if(y_sw == 1 && x_sw) { + } else if(y_sw == 1 && x_sw > 0) { bigint_linmul3(z.mutable_data(), x._data(), x_sw, y.word_at(0)); - } else if(x_sw && y_sw) { + } else if(x_sw > 0 && y_sw > 0) { secure_vector workspace(z.size()); bigint_mul(z.mutable_data(), @@ -81,7 +92,7 @@ BigInt z = BigInt::with_capacity(x_sw + 1); - if(x_sw && y) { + if(x_sw > 0 && y > 0) { bigint_linmul3(z.mutable_data(), x._data(), x_sw, y); z.set_sign(x.sign()); } @@ -93,11 +104,12 @@ * Division Operator */ BigInt operator/(const BigInt& x, const BigInt& y) { - if(y.sig_words() == 1) { + if(y.sig_words() == 1 && y.signum() >= 0) { return x / y.word_at(0); } - BigInt q, r; + BigInt q; + BigInt r; vartime_divide(x, y, q, r); return q; } @@ -111,7 +123,7 @@ } BigInt q; - word r; + word r = 0; ct_divide_word(x, y, q, r); return q; } @@ -123,10 +135,10 @@ if(mod.is_zero()) { throw Invalid_Argument("BigInt::operator% divide by zero"); } - if(mod.is_negative()) { + if(mod.signum() < 0) { throw Invalid_Argument("BigInt::operator% modulus must be > 0"); } - if(n.is_positive() && mod.is_positive() && n < mod) { + if(n.signum() >= 0 && mod.signum() >= 0 && n < mod) { return n; } @@ -134,7 +146,8 @@ return BigInt::from_word(n % mod.word_at(0)); } - BigInt q, r; + BigInt q; + BigInt r; vartime_divide(n, mod, q, r); return r; } @@ -153,16 +166,17 @@ word remainder = 0; - if(is_power_of_2(mod)) { + if(n.signum() >= 0 && is_power_of_2(mod)) { remainder = (n.word_at(0) & (mod - 1)); } else { + const divide_precomp redc_mod(mod); const size_t sw = n.sig_words(); for(size_t i = sw; i > 0; --i) { - remainder = bigint_modop_vartime(remainder, n.word_at(i - 1), mod); + remainder = redc_mod.vartime_mod_2to1(remainder, n.word_at(i - 1)); } } - if(remainder && n.sign() == BigInt::Negative) { + if(remainder != 0 && n.sign() == BigInt::Negative) { return mod - remainder; } return remainder; @@ -172,9 +186,13 @@ * Left Shift Operator */ BigInt operator<<(const BigInt& x, size_t shift) { + if(x.is_zero()) { + return BigInt::zero(); + } + const size_t x_sw = x.sig_words(); - const size_t new_size = x_sw + (shift + BOTAN_MP_WORD_BITS - 1) / BOTAN_MP_WORD_BITS; + const size_t new_size = x_sw + (shift + WordInfo::bits - 1) / WordInfo::bits; BigInt y = BigInt::with_capacity(new_size); bigint_shl2(y.mutable_data(), x._data(), x_sw, shift); y.set_sign(x.sign()); @@ -185,7 +203,7 @@ * Right Shift Operator */ BigInt operator>>(const BigInt& x, size_t shift) { - const size_t shift_words = shift / BOTAN_MP_WORD_BITS; + const size_t shift_words = shift / WordInfo::bits; const size_t x_sw = x.sig_words(); if(shift_words >= x_sw) { @@ -195,7 +213,7 @@ BigInt y = BigInt::with_capacity(x_sw - shift_words); bigint_shr2(y.mutable_data(), x._data(), x_sw, shift); - if(x.is_negative() && y.is_zero()) { + if(x.signum() < 0 && y.is_zero()) { y.set_sign(BigInt::Positive); } else { y.set_sign(x.sign()); diff -Nru botan3-3.7.1+dfsg/src/lib/math/bigint/big_rand.cpp botan3-3.12.0+dfsg/src/lib/math/bigint/big_rand.cpp --- botan3-3.7.1+dfsg/src/lib/math/bigint/big_rand.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/bigint/big_rand.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,7 @@ #include +#include #include #include @@ -24,13 +25,13 @@ secure_vector array = rng.random_vec(round_up(bitsize, 8) / 8); // Always cut unwanted bits - if(bitsize % 8) { + if(bitsize % 8 > 0) { array[0] &= 0xFF >> (8 - (bitsize % 8)); } // Set the highest bit if wanted if(set_high_bit) { - array[0] |= 0x80 >> ((bitsize % 8) ? (8 - bitsize % 8) : 0); + array[0] |= 0x80 >> ((bitsize % 8) > 0 ? (8 - bitsize % 8) : 0); } assign_from_bytes(array); @@ -41,7 +42,7 @@ * Generate a random integer within given range */ BigInt BigInt::random_integer(RandomNumberGenerator& rng, const BigInt& min, const BigInt& max) { - if(min.is_negative() || max.is_negative() || max <= min) { + if(min.signum() < 0 || max.signum() < 0 || max <= min) { throw Invalid_Argument("BigInt::random_integer invalid range"); } @@ -49,7 +50,7 @@ If min is > 1 then we generate a random number `r` in [0,max-min) and return min + r. - This same logic could also be reasonbly chosen for min == 1, but + This same logic could also be reasonably chosen for min == 1, but that breaks certain tests which expect stability of this function when generating within [1,n) */ @@ -63,13 +64,13 @@ const size_t bits = max.bits(); - BigInt r; - - do { + for(;;) { + BigInt r; r.randomize(rng, bits, false); - } while(r < min || r >= max); - - return r; + if(r >= min && r < max) { + return r; + } + } } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/math/bigint/bigint.cpp botan3-3.12.0+dfsg/src/lib/math/bigint/bigint.cpp --- botan3-3.7.1+dfsg/src/lib/math/bigint/bigint.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/bigint/bigint.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,35 +7,28 @@ #include +#include #include #include #include +#include #include #include namespace Botan { BigInt::BigInt(uint64_t n) { -#if BOTAN_MP_WORD_BITS == 64 - m_data.set_word_at(0, n); -#else - m_data.set_word_at(1, static_cast(n >> 32)); - m_data.set_word_at(0, static_cast(n)); -#endif + if constexpr(sizeof(word) == 8) { + m_data.set_word_at(0, static_cast(n)); + } else { + m_data.set_word_at(1, static_cast(n >> 32)); + m_data.set_word_at(0, static_cast(n)); + } } //static BigInt BigInt::from_u64(uint64_t n) { - BigInt bn; - -#if BOTAN_MP_WORD_BITS == 64 - bn.set_word_at(0, n); -#else - bn.set_word_at(1, static_cast(n >> 32)); - bn.set_word_at(0, static_cast(n)); -#endif - - return bn; + return BigInt(n); } //static @@ -61,35 +54,30 @@ return bn; } -/* -* Construct a BigInt from a string -*/ -BigInt::BigInt(std::string_view str) { - Base base = Decimal; - size_t markers = 0; +BigInt BigInt::from_string(std::string_view str) { + size_t prefix_bytes = 0; bool negative = false; + size_t radix = 10; if(!str.empty() && str[0] == '-') { - markers += 1; + prefix_bytes += 1; negative = true; } - if(str.length() > markers + 2 && str[markers] == '0' && str[markers + 1] == 'x') { - markers += 2; - base = Hexadecimal; + if(str.length() > prefix_bytes + 2 && str[prefix_bytes] == '0' && str[prefix_bytes + 1] == 'x') { + prefix_bytes += 2; + radix = 16; } - *this = decode(cast_char_ptr_to_uint8(str.data()) + markers, str.length() - markers, base); + BigInt r = BigInt::from_radix_digits(str.substr(prefix_bytes), radix); if(negative) { - set_sign(Negative); + r.set_sign(Negative); } else { - set_sign(Positive); + r.set_sign(Positive); } -} -BigInt BigInt::from_string(std::string_view str) { - return BigInt(str); + return r; } BigInt BigInt::from_bytes(std::span input) { @@ -132,7 +120,7 @@ } int32_t BigInt::cmp_word(word other) const { - if(is_negative()) { + if(signum() < 0) { return -1; // other is positive ... } @@ -149,15 +137,15 @@ */ int32_t BigInt::cmp(const BigInt& other, bool check_signs) const { if(check_signs) { - if(other.is_positive() && this->is_negative()) { + if(other.signum() >= 0 && this->signum() < 0) { return -1; } - if(other.is_negative() && this->is_positive()) { + if(other.signum() < 0 && this->signum() >= 0) { return 1; } - if(other.is_negative() && this->is_negative()) { + if(other.signum() < 0 && this->signum() < 0) { return (-bigint_cmp(this->_data(), this->size(), other._data(), other.size())); } } @@ -170,23 +158,23 @@ return false; } - return bigint_ct_is_eq(this->_data(), this->sig_words(), other._data(), other.sig_words()).as_bool(); + return bigint_ct_is_eq(this->_data(), this->size(), other._data(), other.size()).as_bool(); } bool BigInt::is_less_than(const BigInt& other) const { - if(this->is_negative() && other.is_positive()) { + if(this->signum() < 0 && other.signum() >= 0) { return true; } - if(this->is_positive() && other.is_negative()) { + if(this->signum() >= 0 && other.signum() < 0) { return false; } - if(other.is_negative() && this->is_negative()) { - return bigint_ct_is_lt(other._data(), other.sig_words(), this->_data(), this->sig_words()).as_bool(); + if(other.signum() < 0 && this->signum() < 0) { + return bigint_ct_is_lt(other._data(), other.size(), this->_data(), this->size()).as_bool(); } - return bigint_ct_is_lt(this->_data(), this->sig_words(), other._data(), other.sig_words()).as_bool(); + return bigint_ct_is_lt(this->_data(), this->size(), other._data(), other.size()).as_bool(); } void BigInt::encode_words(word out[], size_t size) const { @@ -200,6 +188,30 @@ copy_mem(out, _data(), words); } +void BigInt::Data::set_to_zero() { + m_reg.resize(m_reg.capacity()); + clear_mem(m_reg.data(), m_reg.size()); + m_sig_words = 0; +} + +void BigInt::Data::mask_bits(size_t n) { + if(n == 0) { + return set_to_zero(); + } + + const size_t top_word = n / WordInfo::bits; + + if(top_word < size()) { + const word mask = (static_cast(1) << (n % WordInfo::bits)) - 1; + const size_t len = size() - (top_word + 1); + if(len > 0) { + clear_mem(&m_reg[top_word + 1], len); + } + m_reg[top_word] &= mask; + invalidate_sig_words(); + } +} + size_t BigInt::Data::calc_sig_words() const { const size_t sz = m_reg.size(); size_t sig = sz; @@ -231,8 +243,8 @@ const uint32_t mask = 0xFFFFFFFF >> (32 - length); - const size_t word_offset = offset / BOTAN_MP_WORD_BITS; - const size_t wshift = (offset % BOTAN_MP_WORD_BITS); + const size_t word_offset = offset / WordInfo::bits; + const size_t wshift = (offset % WordInfo::bits); /* * The substring is contained within one or at most two words. The @@ -241,11 +253,11 @@ */ const word w0 = word_at(word_offset); - if(wshift == 0 || (offset + length) / BOTAN_MP_WORD_BITS == word_offset) { + if(wshift == 0 || (offset + length) / WordInfo::bits == word_offset) { return static_cast(w0 >> wshift) & mask; } else { const word w1 = word_at(word_offset + 1); - return static_cast((w0 >> wshift) | (w1 << (BOTAN_MP_WORD_BITS - wshift))) & mask; + return static_cast((w0 >> wshift) | (w1 << (WordInfo::bits - wshift))) & mask; } } @@ -253,7 +265,7 @@ * Convert this number to a uint32_t, if possible */ uint32_t BigInt::to_u32bit() const { - if(is_negative()) { + if(signum() < 0) { throw Encoding_Error("BigInt::to_u32bit: Number is negative"); } if(bits() > 32) { @@ -271,10 +283,10 @@ * Clear bit number n */ void BigInt::clear_bit(size_t n) { - const size_t which = n / BOTAN_MP_WORD_BITS; + const size_t which = n / WordInfo::bits; if(which < size()) { - const word mask = ~(static_cast(1) << (n % BOTAN_MP_WORD_BITS)); + const word mask = ~(static_cast(1) << (n % WordInfo::bits)); m_data.set_word_at(which, word_at(which) & mask); } } @@ -289,7 +301,7 @@ const word top_word = word_at(words - 1); const size_t bits_used = high_bit(CT::value_barrier(top_word)); CT::unpoison(bits_used); - return BOTAN_MP_WORD_BITS - bits_used; + return WordInfo::bits - bits_used; } size_t BigInt::bits() const { @@ -299,8 +311,8 @@ return 0; } - const size_t full_words = (words - 1) * BOTAN_MP_WORD_BITS; - const size_t top_bits = BOTAN_MP_WORD_BITS - top_bits_free(); + const size_t full_words = (words - 1) * WordInfo::bits; + const size_t top_bits = WordInfo::bits - top_bits_free(); return full_words + top_bits; } @@ -315,7 +327,7 @@ } size_t BigInt::reduce_below(const BigInt& p, secure_vector& ws) { - if(p.is_negative() || this->is_negative()) { + if(p.signum() < 0 || this->signum() < 0) { throw Invalid_Argument("BigInt::reduce_below both values must be positive"); } @@ -334,8 +346,8 @@ size_t reductions = 0; for(;;) { - word borrow = bigint_sub3(ws.data(), _data(), p_words + 1, p._data(), p_words); - if(borrow) { + const word borrow = bigint_sub3(ws.data(), _data(), p_words + 1, p._data(), p_words); + if(borrow > 0) { break; } @@ -347,7 +359,7 @@ } void BigInt::ct_reduce_below(const BigInt& mod, secure_vector& ws, size_t bound) { - if(mod.is_negative() || this->is_negative()) { + if(mod.signum() < 0 || this->signum() < 0) { throw Invalid_Argument("BigInt::ct_reduce_below both values must be positive"); } @@ -362,7 +374,7 @@ clear_mem(ws.data(), sz); for(size_t i = 0; i != bound; ++i) { - word borrow = bigint_sub3(ws.data(), _data(), sz, mod._data(), mod_words); + const word borrow = bigint_sub3(ws.data(), _data(), sz, mod._data(), mod_words); CT::Mask::is_zero(borrow).select_n(mutable_data(), ws.data(), _data(), sz); } @@ -435,29 +447,30 @@ } void BigInt::ct_cond_add(bool predicate, const BigInt& value) { - if(this->is_negative() || value.is_negative()) { + if(this->signum() < 0 || value.signum() < 0) { throw Invalid_Argument("BigInt::ct_cond_add requires both values to be positive"); } - this->grow_to(1 + value.sig_words()); + const size_t v_words = value.sig_words(); + + this->grow_to(1 + v_words); - bigint_cnd_add(static_cast(predicate), this->mutable_data(), this->size(), value._data(), value.sig_words()); + const auto mask = CT::Mask::expand(static_cast(predicate)).value(); + + word carry = 0; + + word* x = this->mutable_data(); + const word* y = value._data(); + + for(size_t i = 0; i != v_words; ++i) { + x[i] = word_add(x[i], y[i] & mask, &carry); + } + + for(size_t i = v_words; i != size(); ++i) { + x[i] = word_add(x[i], static_cast(0), &carry); + } } 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(), 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 */, BOTAN_MP_WORD_BITS); - // 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; @@ -465,15 +478,33 @@ 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 + const size_t n = size(); + + // Workspace 1 word larger to catch overflow from bigint_shl2 + secure_vector ws(n + 1); + // 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); + // Shift left by 1 bit, dropping overflow + bigint_shl2(ws.data(), _data(), n, 1); + ws[n] = 0; + + // Conditionally assign the bit-shift result + const auto bmask = CT::Mask::expand_bool(i < bit_shift); + for(size_t j = 0; j != n; ++j) { + m_data.set_word_at(j, bmask.select(ws[j], word_at(j))); + } + + // Shift left by 1 word, dropping the most significant word + bigint_shl2(ws.data(), _data(), n - 1 /* ignore msw */, WordInfo::bits); + ws[0] = 0; + + // Conditionally assign the word-shift result + const auto wmask = CT::Mask::expand_bool(i < word_shift); + for(size_t j = 0; j != n; ++j) { + m_data.set_word_at(j, wmask.select(ws[j], word_at(j))); + } } } @@ -488,7 +519,7 @@ void BigInt::cond_flip_sign(bool predicate) { // This code is assuming Negative == 0, Positive == 1 - const auto mask = CT::Mask::expand(predicate); + const auto mask = CT::Mask::expand_bool(predicate); const uint8_t current_sign = static_cast(sign()); @@ -501,13 +532,13 @@ const size_t t_words = size(); const size_t o_words = other.size(); - if(o_words < t_words) { + if(t_words < o_words) { grow_to(o_words); } const size_t r_words = std::max(t_words, o_words); - const auto mask = CT::Mask::expand(predicate); + const auto mask = CT::Mask::expand_bool(predicate); for(size_t i = 0; i != r_words; ++i) { const word o_word = other.word_at(i); @@ -519,7 +550,6 @@ cond_flip_sign((mask.as_choice() && !same_sign).as_bool()); } -#if defined(BOTAN_CT_POISON_ENABLED) void BigInt::_const_time_poison() const { CT::poison(m_data.const_data(), m_data.size()); } @@ -527,6 +557,5 @@ void BigInt::_const_time_unpoison() const { CT::unpoison(m_data.const_data(), m_data.size()); } -#endif } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/math/bigint/bigint.h botan3-3.12.0+dfsg/src/lib/math/bigint/bigint.h --- botan3-3.7.1+dfsg/src/lib/math/bigint/bigint.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/bigint/bigint.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,12 +9,12 @@ #ifndef BOTAN_BIGINT_H_ #define BOTAN_BIGINT_H_ -#include -#include #include #include #include #include +#include +#include namespace Botan { @@ -28,7 +28,7 @@ /** * Base enumerator for encoding and decoding */ - enum Base { + enum Base : uint16_t /* NOLINT(*-use-enum-class) */ { Decimal BOTAN_DEPRECATED("All functions using this enum are deprecated") = 10, Hexadecimal BOTAN_DEPRECATED("All functions using this enum are deprecated") = 16, Binary BOTAN_DEPRECATED("All functions using this enum are deprecated") = 256 @@ -37,7 +37,7 @@ /** * Sign symbol definitions for positive and negative numbers */ - enum Sign { Negative = 0, Positive = 1 }; + enum Sign : uint8_t /* NOLINT(*-use-enum-class) */ { Negative = 0, Positive = 1 }; /** * Create empty (zero) BigInt @@ -79,7 +79,7 @@ * * Prefer BigInt::from_u64 */ - BigInt(uint64_t n); + BigInt(uint64_t n); // NOLINT(*-explicit-conversions) TODO(Botan4) make this explicit /** * Copy Constructor @@ -95,7 +95,7 @@ * @param str the string to parse for an integer value */ //BOTAN_DEPRECATED("Use BigInt::from_string") - explicit BigInt(std::string_view str); + explicit BigInt(std::string_view str) { *this = BigInt::from_string(str); } /** * Create BigInt from a string. @@ -111,6 +111,21 @@ static BigInt from_string(std::string_view str); /** + * Create BigInt from a sequence of digits + * + * The string is interpreted as a sequence of digits in base @p radix. + * + * Each character must be interpretable as such a digit; there is no support + * for whitespace or prefixes (eg '0x' or '-'). + * + * Currently radix must be 10 or 16. + * + * @param digits the sequence of digits + * @param radix the base + */ + static BigInt from_radix_digits(std::string_view digits, size_t radix); + + /** * Create a BigInt from an integer in a byte array * @param buf the byte array holding the value * @param length size of buf @@ -165,14 +180,14 @@ /** * Move constructor */ - BigInt(BigInt&& other) { this->swap(other); } + BigInt(BigInt&& other) noexcept { this->swap(other); } ~BigInt() { _const_time_unpoison(); } /** * Move assignment */ - BigInt& operator=(BigInt&& other) { + BigInt& operator=(BigInt&& other) noexcept { if(this != &other) { this->swap(other); } @@ -189,12 +204,12 @@ * Swap this value with another * @param other BigInt to swap values with */ - void swap(BigInt& other) { + void swap(BigInt& other) noexcept { m_data.swap(other.m_data); std::swap(m_signedness, other.m_signedness); } - friend void swap(BigInt& x, BigInt& y) { x.swap(y); } + friend void swap(BigInt& x, BigInt& y) noexcept { x.swap(y); } BOTAN_DEPRECATED("Deprecated no replacement") void swap_reg(secure_vector& reg) { m_data.swap(reg); @@ -305,7 +320,7 @@ * ! operator * @return true iff this is zero, otherwise false */ - bool operator!() const { return (!is_nonzero()); } + bool operator!() const { return is_zero(); } //BOTAN_DEPRECATED("Just use operator+/operator-") static BigInt add2(const BigInt& x, const word y[], size_t y_words, Sign y_sign); @@ -437,25 +452,36 @@ * Test if the integer has an even value * @result true if the integer is even, false otherwise */ - bool is_even() const { return (get_bit(0) == 0); } + bool is_even() const { return !get_bit(0); } /** * Test if the integer has an odd value * @result true if the integer is odd, false otherwise */ - bool is_odd() const { return (get_bit(0) == 1); } + bool is_odd() const { return get_bit(0); } + + /** + * Return the signum of this integer + * @result -1 if negative, 0 if zero, 1 if positive + */ + int signum() const { + if(sig_words() == 0) { + return 0; + } + return (sign() == Negative) ? -1 : 1; + } /** * Test if the integer is not zero * @result true if the integer is non-zero, false otherwise */ - bool is_nonzero() const { return (!is_zero()); } + BOTAN_DEPRECATED("Use signum() != 0") bool is_nonzero() const { return signum() != 0; } /** * Test if the integer is zero * @result true if the integer is zero, false otherwise */ - bool is_zero() const { return (sig_words() == 0); } + bool is_zero() const { return sig_words() == 0; } /** * Set bit at specified position @@ -472,8 +498,8 @@ * @param set_it if the bit should be set */ void conditionally_set_bit(size_t n, bool set_it) { - const size_t which = n / BOTAN_MP_WORD_BITS; - const word mask = static_cast(set_it) << (n % BOTAN_MP_WORD_BITS); + const size_t which = n / (sizeof(word) * 8); + const word mask = static_cast(set_it) << (n % (sizeof(word) * 8)); m_data.set_word_at(which, word_at(which) | mask); } @@ -487,14 +513,14 @@ * Clear all but the lowest n bits * @param n amount of bits to keep */ - void mask_bits(size_t n) { m_data.mask_bits(n); } + BOTAN_DEPRECATED("Deprecated no replacement") void mask_bits(size_t n) { m_data.mask_bits(n); } /** * Return bit value at specified position * @param n the bit offset to test * @result true, if the bit at position n is set, false otherwise */ - bool get_bit(size_t n) const { return ((word_at(n / BOTAN_MP_WORD_BITS) >> (n % BOTAN_MP_WORD_BITS)) & 1); } + bool get_bit(size_t n) const { return ((word_at(n / (sizeof(word) * 8)) >> (n % (sizeof(word) * 8))) & 1) == 1; } /** * Return (a maximum of) 32 bits of the complete value @@ -510,7 +536,7 @@ * [0 ... 2**32-1], or otherwise throw an exception. * @result the value as a uint32_t if conversion is possible */ - uint32_t to_u32bit() const; + BOTAN_DEPRECATED("Deprecated no replacement") uint32_t to_u32bit() const; /** * Convert this value to a decimal string. @@ -557,13 +583,19 @@ * Tests if the sign of the integer is negative * @result true, iff the integer has a negative sign */ - bool is_negative() const { return (sign() == Negative); } + BOTAN_DEPRECATED("Use signum() < 0") bool is_negative() const { return signum() < 0; } /** * Tests if the sign of the integer is positive + * + * Note that this is testing the sign, thus it returns true also for zero + * Prefer signum which is unambiguous + * * @result true, iff the integer has a positive sign */ - bool is_positive() const { return (sign() == Positive); } + BOTAN_DEPRECATED("Use signum() >= 0 or signum() > 0 as appropriate") bool is_positive() const { + return signum() >= 0; + } /** * Return the sign of the integer @@ -584,7 +616,7 @@ /** * Flip the sign of this BigInt */ - void flip_sign() { set_sign(reverse_sign()); } + BOTAN_DEPRECATED("Deprecated no replacement") void flip_sign() { set_sign(reverse_sign()); } /** * Set sign of the integer @@ -629,7 +661,7 @@ /** * Get the number of high bits unset in the top (allocated) word - * of this integer. Returns BOTAN_MP_WORD_BITS only iff *this is + * of this integer. Returns (sizeof(word) * 8) only iff *this is * zero. Ignores sign. */ BOTAN_DEPRECATED("Deprecated no replacement") size_t top_bits_free() const; @@ -777,24 +809,24 @@ * If predicate is true assign other to *this * Uses a masked operation to avoid side channels */ - void ct_cond_assign(bool predicate, const BigInt& other); + BOTAN_DEPRECATED("Deprecated no replacement") void ct_cond_assign(bool predicate, const BigInt& other); /** * If predicate is true swap *this and other * Uses a masked operation to avoid side channels */ - void ct_cond_swap(bool predicate, BigInt& other); + BOTAN_DEPRECATED("Deprecated no replacement") void ct_cond_swap(bool predicate, BigInt& other); /** * If predicate is true add value to *this */ - void ct_cond_add(bool predicate, const BigInt& value); + BOTAN_DEPRECATED("Deprecated no replacement") 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); + BOTAN_DEPRECATED("Deprecated no replacement") void ct_shift_left(size_t shift); /** * If predicate is true flip the sign of *this @@ -805,15 +837,6 @@ BOTAN_DEPRECATED("replaced by internal API") void const_time_unpoison() const { _const_time_unpoison(); } -#if defined(BOTAN_CT_POISON_ENABLED) - void _const_time_poison() const; - void _const_time_unpoison() const; -#else - constexpr void _const_time_poison() const {} - - constexpr void _const_time_unpoison() const {} -#endif - /** * @param rng a random number generator * @param min the minimum value (must be non-negative) @@ -926,6 +949,16 @@ static secure_vector encode_fixed_length_int_pair(const BigInt& n1, const BigInt& n2, size_t bytes); /** + * Return a span over the register + * + * @warning this is an implementation detail which is not for + * public use and not covered by SemVer. + * + * @result span over the internal register + */ + std::span _as_span() const { return m_data.const_span(); } + + /** * Return a const pointer to the register * * @warning this is an implementation detail which is not for @@ -946,6 +979,34 @@ */ void _assign_from_bytes(std::span bytes) { assign_from_bytes(bytes); } + /** + * Create a BigInt from a word vector + * + * @warning this is an implementation detail which is not for + * public use and not covered by SemVer. + */ + static BigInt _from_words(secure_vector& words) { + BigInt bn; + bn.m_data.swap(words); + return bn; + } + + /** + * Mark this BigInt as holding secret data + * + * @warning this is an implementation detail which is not for + * public use and not covered by SemVer. + */ + void _const_time_poison() const; + + /** + * Mark this BigInt as no longer holding secret data + * + * @warning this is an implementation detail which is not for + * public use and not covered by SemVer. + */ + void _const_time_unpoison() const; + private: /** * Read integer value from a byte vector (big endian) @@ -953,7 +1014,7 @@ */ void assign_from_bytes(std::span bytes); - class Data { + class Data final { public: word* mutable_data() { invalidate_sig_words(); @@ -962,6 +1023,8 @@ const word* const_data() const { return m_reg.data(); } + std::span const_span() const { return std::span{m_reg}; } + secure_vector& mutable_vector() { invalidate_sig_words(); return m_reg; @@ -992,36 +1055,9 @@ m_reg.assign(w, w + len); } - void set_to_zero() { - m_reg.resize(m_reg.capacity()); - clear_mem(m_reg.data(), m_reg.size()); - m_sig_words = 0; - } - - void set_size(size_t s) { - invalidate_sig_words(); - clear_mem(m_reg.data(), m_reg.size()); - m_reg.resize(s + (8 - (s % 8))); - } - - void mask_bits(size_t n) { - if(n == 0) { - return set_to_zero(); - } - - const size_t top_word = n / BOTAN_MP_WORD_BITS; + void set_to_zero(); - // if(top_word < sig_words()) ? - if(top_word < size()) { - const word mask = (static_cast(1) << (n % BOTAN_MP_WORD_BITS)) - 1; - const size_t len = size() - (top_word + 1); - if(len > 0) { - clear_mem(&m_reg[top_word + 1], len); - } - m_reg[top_word] &= mask; - invalidate_sig_words(); - } - } + void mask_bits(size_t n); void grow_to(size_t n) const { if(n > size()) { @@ -1042,23 +1078,21 @@ void resize(size_t s) { m_reg.resize(s); } - void swap(Data& other) { + void swap(Data& other) noexcept { m_reg.swap(other.m_reg); std::swap(m_sig_words, other.m_sig_words); } - void swap(secure_vector& reg) { + void swap(secure_vector& reg) noexcept { m_reg.swap(reg); invalidate_sig_words(); } - void invalidate_sig_words() const { m_sig_words = sig_words_npos; } + void invalidate_sig_words() const noexcept { m_sig_words = sig_words_npos; } size_t sig_words() const { if(m_sig_words == sig_words_npos) { m_sig_words = calc_sig_words(); - } else { - BOTAN_DEBUG_ASSERT(m_sig_words == calc_sig_words()); } return m_sig_words; } @@ -1168,9 +1202,10 @@ * I/O Operators */ BOTAN_DEPRECATED("Use BigInt::to_{hex,dec}_string") -BOTAN_PUBLIC_API(2, 0) std::ostream& operator<<(std::ostream&, const BigInt&); +BOTAN_PUBLIC_API(2, 0) std::ostream& operator<<(std::ostream& stream, const BigInt& n); -BOTAN_DEPRECATED("Use BigInt::from_string") BOTAN_PUBLIC_API(2, 0) std::istream& operator>>(std::istream&, BigInt&); +BOTAN_DEPRECATED("Use BigInt::from_string") +BOTAN_PUBLIC_API(2, 0) std::istream& operator>>(std::istream& stream, BigInt& n); } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/math/bigint/divide.cpp botan3-3.12.0+dfsg/src/lib/math/bigint/divide.cpp --- botan3-3.7.1+dfsg/src/lib/math/bigint/divide.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/bigint/divide.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,7 +7,7 @@ #include -#include +#include #include #include @@ -21,13 +21,13 @@ void sign_fixup(const BigInt& x, const BigInt& y, BigInt& q, BigInt& r) { q.cond_flip_sign(x.sign() != y.sign()); - if(x.is_negative() && r.is_nonzero()) { + if(x.signum() < 0 && r.signum() != 0) { q -= 1; r = y.abs() - r; } } -inline bool division_check(word q, word y2, word y1, word x3, word x2, word x1) { +inline bool division_check_vartime(word q, word y2, word y1, word x3, word x2, word x1) { /* Compute (y3,y2,y1) = (y2,y1) * q and return true if (y3,y2,y1) > (x3,x2,x1) @@ -37,10 +37,13 @@ y1 = word_madd2(q, y1, &y3); y2 = word_madd2(q, y2, &y3); - const word x[3] = {x1, x2, x3}; - const word y[3] = {y1, y2, y3}; - - return bigint_ct_is_lt(x, 3, y, 3).as_bool(); + if(x3 != y3) { + return (y3 > x3); + } + if(x2 != y2) { + return (y2 > x2); + } + return (y1 > x1); } } // namespace @@ -78,8 +81,8 @@ } BigInt ct_divide_pow2k(size_t k, const BigInt& y) { - BOTAN_ARG_CHECK(!y.is_zero(), "Cannot divide by zero"); - BOTAN_ARG_CHECK(!y.is_negative(), "Negative divisor not supported"); + BOTAN_ARG_CHECK(y.signum() != 0, "Cannot divide by zero"); + BOTAN_ARG_CHECK(y.signum() >= 0, "Negative divisor not supported"); BOTAN_ARG_CHECK(k > 1, "Invalid k"); const size_t x_bits = k + 1; @@ -90,7 +93,7 @@ } BOTAN_ASSERT_NOMSG(y_bits >= 1); - const size_t x_words = (x_bits + BOTAN_MP_WORD_BITS - 1) / BOTAN_MP_WORD_BITS; + const size_t x_words = (x_bits + WordInfo::bits - 1) / WordInfo::bits; const size_t y_words = y.sig_words(); BigInt q = BigInt::with_capacity(x_words); @@ -135,14 +138,14 @@ const auto r_carry = CT::Mask::expand_top_bit(r); r <<= 1; - r += x_b; + r += static_cast(x_b); const auto r_gte_y = CT::Mask::is_gte(r, y) | r_carry; q.conditionally_set_bit(b, r_gte_y.as_bool()); r = r_gte_y.select(r - y, r); } - if(x.is_negative()) { + if(x.signum() < 0) { q.flip_sign(); if(r != 0) { --q; @@ -154,8 +157,16 @@ q_out = q; } +BigInt ct_divide_word(const BigInt& x, word y) { + BigInt q; + word r = 0; + ct_divide_word(x, y, q, r); + BOTAN_UNUSED(r); + return q; +} + word ct_mod_word(const BigInt& x, word y) { - BOTAN_ARG_CHECK(x.is_positive(), "The argument x must be positive"); + BOTAN_ARG_CHECK(x.signum() >= 0, "The argument x must be non-negative"); BOTAN_ARG_CHECK(y != 0, "Cannot divide by zero"); const size_t x_bits = x.bits(); @@ -169,7 +180,7 @@ const auto r_carry = CT::Mask::expand_top_bit(r); r <<= 1; - r += x_b; + r += static_cast(x_b); const auto r_gte_y = CT::Mask::is_gte(r, y) | r_carry; r = r_gte_y.select(r - y, r); @@ -179,7 +190,7 @@ } BigInt ct_modulo(const BigInt& x, const BigInt& y) { - if(y.is_negative() || y.is_zero()) { + if(y.signum() <= 0) { throw Invalid_Argument("ct_modulo requires y > 0"); } @@ -202,8 +213,8 @@ r.ct_cond_swap(r_gte_y, t); } - if(x.is_negative()) { - if(r.is_nonzero()) { + if(x.signum() < 0) { + if(r.signum() != 0) { r = y - r; } } @@ -211,12 +222,109 @@ return r; } +BigInt vartime_divide_pow2k(size_t k, const BigInt& y_arg) { + constexpr size_t WB = WordInfo::bits; + + BOTAN_ARG_CHECK(y_arg.signum() != 0, "Cannot divide by zero"); + BOTAN_ARG_CHECK(y_arg.signum() >= 0, "Negative divisor not supported"); + BOTAN_ARG_CHECK(k > 1, "Invalid k"); + + BigInt y = y_arg; + + const size_t y_words = y.sig_words(); + + BOTAN_ASSERT_NOMSG(y_words > 0); + + // Calculate shifts needed to normalize y with high bit set + const size_t shifts = y.top_bits_free(); + + if(shifts > 0) { + y <<= shifts; + } + + BigInt r; + r.set_bit(k + shifts); // (2^k) << shifts + + // we know y has not changed size, since we only shifted up to set high bit + const size_t t = y_words - 1; + const size_t n = std::max(y_words, r.sig_words()) - 1; + + BOTAN_ASSERT_NOMSG(n >= t); + + BigInt q = BigInt::zero(); + q.grow_to(n - t + 1); + + word* q_words = q.mutable_data(); + + BigInt shifted_y = y << (WB * (n - t)); + + // Set q_{n-t} to number of times r > shifted_y + secure_vector ws; + q_words[n - t] = r.reduce_below(shifted_y, ws); + + const word y_t0 = y.word_at(t); + const word y_t1 = y.word_at(t - 1); + BOTAN_DEBUG_ASSERT((y_t0 >> (WB - 1)) == 1); + + const divide_precomp div_y_t0(y_t0); + + for(size_t i = n; i != t; --i) { + const word x_i0 = r.word_at(i); + const word x_i1 = r.word_at(i - 1); + const word x_i2 = r.word_at(i - 2); + + word qit = (x_i0 == y_t0) ? WordInfo::max : div_y_t0.vartime_div_2to1(x_i0, x_i1); + + // Per HAC 14.23, this operation is required at most twice + for(size_t j = 0; j != 2; ++j) { + if(division_check_vartime(qit, y_t0, y_t1, x_i0, x_i1, x_i2)) { + BOTAN_ASSERT_NOMSG(qit > 0); + qit--; + } else { + break; + } + } + + shifted_y >>= WB; + // Now shifted_y == y << (WB * (i-t-1)) + + /* + * Special case qit == 0 and qit == 1 which occurs relatively often here due to a + * combination of the fixed 2^k and in many cases the typical structure of + * public moduli (as this function is called by Barrett_Reduction::for_public_modulus). + * + * Over the test suite, about 5% of loop iterations have qit == 1 and 10% have qit == 0 + */ + + if(qit != 0) { + if(qit == 1) { + r -= shifted_y; + } else { + r -= qit * shifted_y; + } + + if(r.signum() < 0) { + BOTAN_ASSERT_NOMSG(qit > 0); + qit--; + r += shifted_y; + BOTAN_ASSERT_NOMSG(r.signum() >= 0); + } + } + + q_words[i - t - 1] = qit; + } + + return q; +} + /* * Solve x = q * y + r * -* See Handbook of Applied Cryptography section 14.2.5 +* See Handbook of Applied Cryptography algorithm 14.20 */ void vartime_divide(const BigInt& x, const BigInt& y_arg, BigInt& q_out, BigInt& r_out) { + constexpr size_t WB = WordInfo::bits; + if(y_arg.is_zero()) { throw Invalid_Argument("vartime_divide: cannot divide by zero"); } @@ -237,8 +345,10 @@ // Calculate shifts needed to normalize y with high bit set const size_t shifts = y.top_bits_free(); - y <<= shifts; - r <<= shifts; + if(shifts > 0) { + y <<= shifts; + r <<= shifts; + } // we know y has not changed size, since we only shifted up to set high bit const size_t t = y_words - 1; @@ -250,41 +360,53 @@ word* q_words = q.mutable_data(); - BigInt shifted_y = y << (BOTAN_MP_WORD_BITS * (n - t)); + BigInt shifted_y = y << (WB * (n - t)); // Set q_{n-t} to number of times r > shifted_y q_words[n - t] = r.reduce_below(shifted_y, ws); const word y_t0 = y.word_at(t); const word y_t1 = y.word_at(t - 1); - BOTAN_DEBUG_ASSERT((y_t0 >> (BOTAN_MP_WORD_BITS - 1)) == 1); + BOTAN_DEBUG_ASSERT((y_t0 >> (WB - 1)) == 1); - for(size_t j = n; j != t; --j) { - const word x_j0 = r.word_at(j); - const word x_j1 = r.word_at(j - 1); - const word x_j2 = r.word_at(j - 2); + const divide_precomp div_y_t0(y_t0); - word qjt = bigint_divop_vartime(x_j0, x_j1, y_t0); + for(size_t i = n; i != t; --i) { + const word x_i0 = r.word_at(i); + const word x_i1 = r.word_at(i - 1); + const word x_i2 = r.word_at(i - 2); - qjt = CT::Mask::is_equal(x_j0, y_t0).select(WordInfo::max, qjt); + word qit = (x_i0 == y_t0) ? WordInfo::max : div_y_t0.vartime_div_2to1(x_i0, x_i1); // Per HAC 14.23, this operation is required at most twice - qjt -= division_check(qjt, y_t0, y_t1, x_j0, x_j1, x_j2); - qjt -= division_check(qjt, y_t0, y_t1, x_j0, x_j1, x_j2); - BOTAN_DEBUG_ASSERT(division_check(qjt, y_t0, y_t1, x_j0, x_j1, x_j2) == false); + for(size_t j = 0; j != 2; ++j) { + if(division_check_vartime(qit, y_t0, y_t1, x_i0, x_i1, x_i2)) { + BOTAN_ASSERT_NOMSG(qit > 0); + qit--; + } else { + break; + } + } - shifted_y >>= BOTAN_MP_WORD_BITS; - // Now shifted_y == y << (BOTAN_MP_WORD_BITS * (j-t-1)) + shifted_y >>= WB; + // Now shifted_y == y << (WB * (i-t-1)) - // TODO this sequence could be better - r -= qjt * shifted_y; - qjt -= r.is_negative(); - r += static_cast(r.is_negative()) * shifted_y; + if(qit != 0) { + r -= qit * shifted_y; + if(r.signum() < 0) { + BOTAN_ASSERT_NOMSG(qit > 0); + qit--; + r += shifted_y; + BOTAN_ASSERT_NOMSG(r.signum() >= 0); + } + } - q_words[j - t - 1] = qjt; + q_words[i - t - 1] = qit; } - r >>= shifts; + if(shifts > 0) { + r >>= shifts; + } sign_fixup(x, y_arg, q, r); diff -Nru botan3-3.7.1+dfsg/src/lib/math/bigint/divide.h botan3-3.12.0+dfsg/src/lib/math/bigint/divide.h --- botan3-3.7.1+dfsg/src/lib/math/bigint/divide.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/bigint/divide.h 2026-05-07 01:38:28.000000000 +0000 @@ -5,8 +5,8 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#ifndef BOTAN_DIVISON_ALGORITHM_H_ -#define BOTAN_DIVISON_ALGORITHM_H_ +#ifndef BOTAN_BIGINT_DIVIDE_H_ +#define BOTAN_BIGINT_DIVIDE_H_ #include @@ -51,6 +51,20 @@ BigInt ct_divide_pow2k(size_t k, const BigInt& y); /** +* BigInt division, variable time, 2^k variant +* +* This is identical to ct_divide_pow2k in functionality, +* but leaks both k and y to side channels, so it should only +* be used with public inputs. +* +* @param k an integer +* @param y a positive integer +* @return q equal to 2**k / y +*/ +BOTAN_TEST_API +BigInt vartime_divide_pow2k(size_t k, const BigInt& y); + +/** * BigInt division, const time variant * * This runs with control flow independent of the values of x/y. @@ -61,7 +75,8 @@ * @return x/y with remainder discarded */ inline BigInt ct_divide(const BigInt& x, const BigInt& y) { - BigInt q, r; + BigInt q; + BigInt r; ct_divide(x, y, q, r); return q; } @@ -90,13 +105,7 @@ * @param y a non-zero word * @return quotient floor(x / y) */ -inline BigInt ct_divide_word(const BigInt& x, word y) { - BigInt q; - word r; - ct_divide_word(x, y, q, r); - BOTAN_UNUSED(r); - return q; -} +BigInt ct_divide_word(const BigInt& x, word y); /** * BigInt word modulo, const time variant diff -Nru botan3-3.7.1+dfsg/src/lib/math/mp/info.txt botan3-3.12.0+dfsg/src/lib/math/mp/info.txt --- botan3-3.7.1+dfsg/src/lib/math/mp/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/mp/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + BIGINT_MP -> 20151225 - + name -> "Big Integer (Low-Level)" diff -Nru botan3-3.7.1+dfsg/src/lib/math/mp/mp_asmi.h botan3-3.12.0+dfsg/src/lib/math/mp/mp_asmi.h --- botan3-3.7.1+dfsg/src/lib/math/mp/mp_asmi.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/mp/mp_asmi.h 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ /* * Lowest Level MPI Algorithms -* (C) 1999-2010 Jack Lloyd +* (C) 1999-2010,2025 Jack Lloyd * 2006 Luca Piccarreta * * Botan is released under the Simplified BSD License (see license.txt) @@ -11,6 +11,8 @@ #include #include +#include +#include #if !defined(BOTAN_TARGET_HAS_NATIVE_UINT128) #include @@ -18,10 +20,31 @@ namespace Botan { +// NOLINTBEGIN(*-macro-usage,*-no-assembler) + #if defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_X86_64) #define BOTAN_MP_USE_X86_64_ASM #endif +#if defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_ARM64) + #define BOTAN_MP_USE_AARCH64_ASM +#endif + +/* +* Expressing an add with carry is sadly quite difficult in standard C/C++. +* +* Compilers will recognize various idioms and generate a reasonable carry +* chain. Unfortunately which idioms the compiler will understand vary, so we +* have to decide what to do based on the compiler. This is fragile; what will +* work varies not just based on compiler but also version, target architecture, +* and optimization flags. +*/ +#if defined(__clang__) +static constexpr bool use_dword_for_word_add = false; +#else +static constexpr bool use_dword_for_word_add = true; +#endif + /* * Concept for allowed multiprecision word types */ @@ -78,6 +101,23 @@ return a; } +#elif defined(BOTAN_MP_USE_AARCH64_ASM) + if(std::same_as && !std::is_constant_evaluated()) { + W lo = 0; + W hi = 0; + asm(R"( + mul %[lo], %[a], %[b] + umulh %[hi], %[a], %[b] + adds %[lo], %[lo], %[c] + adc %[hi], %[hi], xzr + )" + : [lo] "=&r"(lo), [hi] "=&r"(hi) + : [a] "r"(a), [b] "r"(b), [c] "r"(*c) + : "cc"); + + *c = hi; + return lo; + } #endif typedef typename WordInfo::dword dword; @@ -108,6 +148,25 @@ return a; } +#elif defined(BOTAN_MP_USE_AARCH64_ASM) + if(std::same_as && !std::is_constant_evaluated()) { + W lo = 0; + W hi = 0; + asm(R"( + mul %[lo], %[a], %[b] + umulh %[hi], %[a], %[b] + adds %[lo], %[lo], %[c] + adc %[hi], %[hi], xzr + adds %[lo], %[lo], %[d] + adc %[hi], %[hi], xzr + )" + : [lo] "=&r"(lo), [hi] "=&r"(hi) + : [a] "r"(a), [b] "r"(b), [c] "r"(c), [d] "r"(*d) + : "cc"); + + *d = hi; + return lo; + } #endif typedef typename WordInfo::dword dword; @@ -120,12 +179,6 @@ #define ASM(x) x "\n\t" - #define DO_4_TIMES(MACRO, ARG) \ - MACRO(ARG, 0) \ - MACRO(ARG, 1) \ - MACRO(ARG, 2) \ - MACRO(ARG, 3) - #define DO_8_TIMES(MACRO, ARG) \ MACRO(ARG, 0) \ MACRO(ARG, 1) \ @@ -176,8 +229,8 @@ */ template inline constexpr auto word_add(W x, W y, W* carry) -> W { - if(!std::is_constant_evaluated()) { #if BOTAN_COMPILER_HAS_BUILTIN(__builtin_addc) + if(!std::is_constant_evaluated()) { if constexpr(std::same_as) { return __builtin_addc(x, y, *carry & 1, carry); } else if constexpr(std::same_as) { @@ -185,23 +238,27 @@ } else if constexpr(std::same_as) { return __builtin_addcll(x, y, *carry & 1, carry); } -#elif defined(BOTAN_MP_USE_X86_64_ASM) - if(std::same_as) { - asm(ADD_OR_SUBTRACT(ASM("adcq %[y],%[x]")) - : [x] "=r"(x), [carry] "=r"(*carry) - : "0"(x), [y] "rm"(y), "1"(*carry) - : "cc"); - return x; - } -#endif } +#endif - const W cb = *carry & 1; - W z = x + y; - W c1 = (z < x); - z += cb; - *carry = c1 | (z < cb); - return z; + if constexpr(WordInfo::dword_is_native && use_dword_for_word_add) { + /* + TODO(Botan4) this is largely a performance hack for GCCs that don't + support __builtin_addc, if we increase the minimum supported version of + GCC to GCC 14 then we can remove this and not worry about it + */ + const W cb = *carry & 1; + const auto s = typename WordInfo::dword(x) + y + cb; + *carry = static_cast(s >> WordInfo::bits); + return static_cast(s); + } else { + const W cb = *carry & 1; + W z = x + y; + W c1 = (z < x); + z += cb; + *carry = c1 | (z < cb); + return z; + } } /* @@ -256,32 +313,13 @@ return carry; } -template -inline constexpr auto word4_add3(W z[4], const W x[4], const W y[4], W carry) -> W { -#if defined(BOTAN_MP_USE_X86_64_ASM) - if(std::same_as && !std::is_constant_evaluated()) { - asm volatile(ADD_OR_SUBTRACT(DO_4_TIMES(ADDSUB3_OP, "adcq")) - : [carry] "=r"(carry) - : [x] "r"(x), [y] "r"(y), [z] "r"(z), "0"(carry) - : "cc", "memory"); - return carry; - } -#endif - - z[0] = word_add(x[0], y[0], &carry); - z[1] = word_add(x[1], y[1], &carry); - z[2] = word_add(x[2], y[2], &carry); - z[3] = word_add(x[3], y[3], &carry); - return carry; -} - /* * Word Subtraction */ template inline constexpr auto word_sub(W x, W y, W* carry) -> W { - if(!std::is_constant_evaluated()) { #if BOTAN_COMPILER_HAS_BUILTIN(__builtin_subc) + if(!std::is_constant_evaluated()) { if constexpr(std::same_as) { return __builtin_subc(x, y, *carry & 1, carry); } else if constexpr(std::same_as) { @@ -289,16 +327,8 @@ } else if constexpr(std::same_as) { return __builtin_subcll(x, y, *carry & 1, carry); } -#elif defined(BOTAN_MP_USE_X86_64_ASM) - if(std::same_as) { - asm(ADD_OR_SUBTRACT(ASM("sbbq %[y],%[x]")) - : [x] "=r"(x), [carry] "=r"(*carry) - : "0"(x), [y] "rm"(y), "1"(*carry) - : "cc"); - return x; - } -#endif } +#endif const W cb = *carry & 1; W t0 = x - y; @@ -335,32 +365,6 @@ } /* -* Eight Word Block Subtraction, Two Argument -*/ -template -inline constexpr auto word8_sub2_rev(W x[8], const W y[8], W carry) -> W { -#if defined(BOTAN_MP_USE_X86_64_ASM) - if(std::same_as && !std::is_constant_evaluated()) { - asm(ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB3_OP, "sbbq")) - : [carry] "=r"(carry) - : [x] "r"(y), [y] "r"(x), [z] "r"(x), "0"(carry) - : "cc", "memory"); - return carry; - } -#endif - - x[0] = word_sub(y[0], x[0], &carry); - x[1] = word_sub(y[1], x[1], &carry); - x[2] = word_sub(y[2], x[2], &carry); - x[3] = word_sub(y[3], x[3], &carry); - x[4] = word_sub(y[4], x[4], &carry); - x[5] = word_sub(y[5], x[5], &carry); - x[6] = word_sub(y[6], x[6], &carry); - x[7] = word_sub(y[7], x[7], &carry); - return carry; -} - -/* * Eight Word Block Subtraction, Three Argument */ template @@ -386,51 +390,6 @@ return carry; } -template -inline constexpr auto word4_sub3(W z[4], const W x[4], const W y[4], W carry) -> W { -#if defined(BOTAN_MP_USE_X86_64_ASM) - if(std::same_as && !std::is_constant_evaluated()) { - asm volatile(ADD_OR_SUBTRACT(DO_4_TIMES(ADDSUB3_OP, "sbbq")) - : [carry] "=r"(carry) - : [x] "r"(x), [y] "r"(y), [z] "r"(z), "0"(carry) - : "cc", "memory"); - return carry; - } -#endif - - z[0] = word_sub(x[0], y[0], &carry); - z[1] = word_sub(x[1], y[1], &carry); - z[2] = word_sub(x[2], y[2], &carry); - z[3] = word_sub(x[3], y[3], &carry); - return carry; -} - -/* -* Eight Word Block Linear Multiplication -*/ -template -inline constexpr auto word8_linmul2(W x[8], W y, W carry) -> W { -#if defined(BOTAN_MP_USE_X86_64_ASM) - if(std::same_as && !std::is_constant_evaluated()) { - asm(DO_8_TIMES(LINMUL_OP, "x") - : [carry] "=r"(carry) - : [x] "r"(x), [y] "rm"(y), "0"(carry) - : "cc", "%rax", "%rdx"); - return carry; - } -#endif - - x[0] = word_madd2(x[0], y, &carry); - x[1] = word_madd2(x[1], y, &carry); - x[2] = word_madd2(x[2], y, &carry); - x[3] = word_madd2(x[3], y, &carry); - x[4] = word_madd2(x[4], y, &carry); - x[5] = word_madd2(x[5], y, &carry); - x[6] = word_madd2(x[6], y, &carry); - x[7] = word_madd2(x[7], y, &carry); - return carry; -} - /* * Eight Word Block Linear Multiplication */ @@ -483,108 +442,6 @@ return carry; } -/* -* Multiply-Add Accumulator -* (w2,w1,w0) += x * y -*/ -template -inline constexpr void word3_muladd(W* w2, W* w1, W* w0, W x, W y) { -#if defined(BOTAN_MP_USE_X86_64_ASM) - if(std::same_as && !std::is_constant_evaluated()) { - W z0 = 0, z1 = 0; - - asm("mulq %[y]" : "=a"(z0), "=d"(z1) : "a"(x), [y] "rm"(y) : "cc"); - - asm(R"( - addq %[z0],%[w0] - adcq %[z1],%[w1] - adcq $0,%[w2] - )" - : [w0] "=r"(*w0), [w1] "=r"(*w1), [w2] "=r"(*w2) - : [z0] "r"(z0), [z1] "r"(z1), "0"(*w0), "1"(*w1), "2"(*w2) - : "cc"); - return; - } -#endif - - W carry = *w0; - *w0 = word_madd2(x, y, &carry); - *w1 += carry; - *w2 += (*w1 < carry); -} - -/* -* 3-word addition -* (w2,w1,w0) += x -*/ -template -inline constexpr void word3_add(W* w2, W* w1, W* w0, W x) { -#if defined(BOTAN_MP_USE_X86_64_ASM) - if(std::same_as && !std::is_constant_evaluated()) { - asm(R"( - addq %[x],%[w0] - adcq $0,%[w1] - adcq $0,%[w2] - )" - : [w0] "=r"(*w0), [w1] "=r"(*w1), [w2] "=r"(*w2) - : [x] "r"(x), "0"(*w0), "1"(*w1), "2"(*w2) - : "cc"); - return; - } -#endif - - *w0 += x; - W c1 = (*w0 < x); - *w1 += c1; - W c2 = (*w1 < c1); - *w2 += c2; -} - -/* -* Multiply-Add Accumulator -* (w2,w1,w0) += 2 * x * y -*/ -template -inline constexpr void word3_muladd_2(W* w2, W* w1, W* w0, W x, W y) { -#if defined(BOTAN_MP_USE_X86_64_ASM) - if(std::same_as && !std::is_constant_evaluated()) { - W z0 = 0, z1 = 0; - - asm("mulq %[y]" : "=a"(z0), "=d"(z1) : "a"(x), [y] "rm"(y) : "cc"); - - asm(R"( - addq %[z0],%[w0] - adcq %[z1],%[w1] - adcq $0,%[w2] - - addq %[z0],%[w0] - adcq %[z1],%[w1] - adcq $0,%[w2] - )" - : [w0] "=r"(*w0), [w1] "=r"(*w1), [w2] "=r"(*w2) - : [z0] "r"(z0), [z1] "r"(z1), "0"(*w0), "1"(*w1), "2"(*w2) - : "cc"); - return; - } -#endif - - W carry = 0; - x = word_madd2(x, y, &carry); - y = carry; - - const size_t top_bit_shift = WordInfo::bits - 1; - - W top = (y >> top_bit_shift); - y <<= 1; - y |= (x >> top_bit_shift); - x <<= 1; - - carry = 0; - *w0 = word_add(*w0, x, &carry); - *w1 = word_add(*w1, y, &carry); - *w2 = word_add(*w2, top, &carry); -} - /** * Helper for 3-word accumulators * @@ -598,7 +455,7 @@ #if defined(__BITINT_MAXWIDTH__) && (__BITINT_MAXWIDTH__ >= 3 * 64) public: - constexpr word3() { m_w = 0; } + constexpr word3() : m_w(0) {} inline constexpr void mul(W x, W y) { m_w += static_cast(x) * y; } @@ -633,17 +490,122 @@ #else public: - constexpr word3() { - m_w2 = 0; - m_w1 = 0; - m_w0 = 0; - } + constexpr word3() : m_w0(0), m_w1(0), m_w2(0) {} - inline constexpr void mul(W x, W y) { word3_muladd(&m_w2, &m_w1, &m_w0, x, y); } - - inline constexpr void mul_x2(W x, W y) { word3_muladd_2(&m_w2, &m_w1, &m_w0, x, y); } - - inline constexpr void add(W x) { word3_add(&m_w2, &m_w1, &m_w0, x); } + inline constexpr void mul(W x, W y) { + #if defined(BOTAN_MP_USE_X86_64_ASM) + if(std::same_as && !std::is_constant_evaluated()) { + W z0 = 0; + W z1 = 0; + + asm("mulq %[y]" : "=a"(z0), "=d"(z1) : "a"(x), [y] "rm"(y) : "cc"); + + asm(R"( + addq %[z0],%[w0] + adcq %[z1],%[w1] + adcq $0,%[w2] + )" + : [w0] "=r"(m_w0), [w1] "=r"(m_w1), [w2] "=r"(m_w2) + : [z0] "r"(z0), [z1] "r"(z1), "0"(m_w0), "1"(m_w1), "2"(m_w2) + : "cc"); + return; + } + #elif defined(BOTAN_MP_USE_AARCH64_ASM) + if(std::same_as && !std::is_constant_evaluated()) { + W t0 = 0; + W t1 = 0; + asm(R"( + mul %[t0], %[x], %[y] + umulh %[t1], %[x], %[y] + adds %[w0], %[w0], %[t0] + adcs %[w1], %[w1], %[t1] + adc %[w2], %[w2], xzr + )" + : [w0] "+r"(m_w0), [w1] "+r"(m_w1), [w2] "+r"(m_w2), [t0] "=&r"(t0), [t1] "=&r"(t1) + : [x] "r"(x), [y] "r"(y) + : "cc"); + return; + } + #endif + + typedef typename WordInfo::dword dword; + const auto z = dword(x) * y; + const auto z0 = static_cast(z); + const auto z1 = static_cast(z >> WordInfo::bits); + + W carry = 0; + m_w0 = word_add(m_w0, z0, &carry); + m_w1 = word_add(m_w1, z1, &carry); + m_w2 += carry; + } + + inline constexpr void mul_x2(W x, W y) { + #if defined(BOTAN_MP_USE_X86_64_ASM) + if(std::same_as && !std::is_constant_evaluated()) { + W z0 = 0; + W z1 = 0; + + asm("mulq %[y]" : "=a"(z0), "=d"(z1) : "a"(x), [y] "rm"(y) : "cc"); + + asm(R"( + addq %[z0],%[w0] + adcq %[z1],%[w1] + adcq $0,%[w2] + + addq %[z0],%[w0] + adcq %[z1],%[w1] + adcq $0,%[w2] + )" + : [w0] "=r"(m_w0), [w1] "=r"(m_w1), [w2] "=r"(m_w2) + : [z0] "r"(z0), [z1] "r"(z1), "0"(m_w0), "1"(m_w1), "2"(m_w2) + : "cc"); + return; + } + #elif defined(BOTAN_MP_USE_AARCH64_ASM) + if(std::same_as && !std::is_constant_evaluated()) { + W t0 = 0; + W t1 = 0; + asm(R"( + mul %[t0], %[x], %[y] + umulh %[t1], %[x], %[y] + adds %[w0], %[w0], %[t0] + adcs %[w1], %[w1], %[t1] + adc %[w2], %[w2], xzr + adds %[w0], %[w0], %[t0] + adcs %[w1], %[w1], %[t1] + adc %[w2], %[w2], xzr + )" + : [w0] "+r"(m_w0), [w1] "+r"(m_w1), [w2] "+r"(m_w2), [t0] "=&r"(t0), [t1] "=&r"(t1) + : [x] "r"(x), [y] "r"(y) + : "cc"); + return; + } + #endif + + typedef typename WordInfo::dword dword; + const auto z = dword(x) * y; + const auto z0 = static_cast(z); + const auto z1 = static_cast(z >> WordInfo::bits); + + W carry = 0; + m_w0 = word_add(m_w0, z0, &carry); + m_w1 = word_add(m_w1, z1, &carry); + m_w2 += carry; + + carry = 0; + m_w0 = word_add(m_w0, z0, &carry); + m_w1 = word_add(m_w1, z1, &carry); + m_w2 += carry; + } + + inline constexpr void add(W x) { + constexpr W z = 0; + + W carry = 0; + m_w0 = word_add(m_w0, x, &carry); + m_w1 = word_add(m_w1, z, &carry); + m_w2 += carry; + } inline constexpr W extract() { W r = m_w0; @@ -672,13 +634,14 @@ } private: - W m_w0, m_w1, m_w2; + W m_w0; + W m_w1; + W m_w2; #endif }; #if defined(ASM) #undef ASM - #undef DO_4_TIMES #undef DO_8_TIMES #undef ADD_OR_SUBTRACT #undef ADDSUB2_OP @@ -687,6 +650,8 @@ #undef MULADD_OP #endif +// NOLINTEND(*-macro-usage,*-no-assembler) + } // namespace Botan #endif diff -Nru botan3-3.7.1+dfsg/src/lib/math/mp/mp_comba.cpp botan3-3.12.0+dfsg/src/lib/math/mp/mp_comba.cpp --- botan3-3.7.1+dfsg/src/lib/math/mp/mp_comba.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/mp/mp_comba.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,7 +1,8 @@ /* * Comba Multiplication and Squaring * -* This file was automatically generated by ./src/scripts/dev_tools/gen_mp_comba.py on 2024-06-27 +* This file was automatically generated by ./src/scripts/dev_tools/gen_mp_comba.py on 2026-04-24 +* All manual changes will be lost. Edit the script instead. * * Botan is released under the Simplified BSD License (see license.txt) */ diff -Nru botan3-3.7.1+dfsg/src/lib/math/mp/mp_core.h botan3-3.12.0+dfsg/src/lib/math/mp/mp_core.h --- botan3-3.7.1+dfsg/src/lib/math/mp/mp_core.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/mp/mp_core.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,12 +11,10 @@ #define BOTAN_MP_CORE_OPS_H_ #include -#include -#include #include #include +#include #include -#include #include #include @@ -39,129 +37,38 @@ } } -template -inline constexpr W bigint_cnd_add(W cnd, W x[], size_t x_size, const W y[], size_t y_size) { - BOTAN_ASSERT(x_size >= y_size, "Expected sizes"); - - const auto mask = CT::Mask::expand(cnd).value(); - - W carry = 0; - - for(size_t i = 0; i != y_size; ++i) { - x[i] = word_add(x[i], y[i] & mask, &carry); - } - - for(size_t i = y_size; i != x_size; ++i) { - x[i] = word_add(x[i], static_cast(0), &carry); - } - - return (mask & carry); -} - /* * If cond > 0 adds x[0:size] and y[0:size] and returns carry * Runs in constant time */ template inline constexpr W bigint_cnd_add(W cnd, W x[], const W y[], size_t size) { - return bigint_cnd_add(cnd, x, size, y, size); -} - -/* -* If cond > 0 subtracts x[0:size] and y[0:size] and returns borrow -* Runs in constant time -*/ -template -inline constexpr auto bigint_cnd_sub(W cnd, W x[], size_t x_size, const W y[], size_t y_size) -> W { - BOTAN_ASSERT(x_size >= y_size, "Expected sizes"); - const auto mask = CT::Mask::expand(cnd).value(); W carry = 0; - for(size_t i = 0; i != y_size; ++i) { - x[i] = word_sub(x[i], y[i] & mask, &carry); - } - - for(size_t i = y_size; i != x_size; ++i) { - x[i] = word_sub(x[i], static_cast(0), &carry); + for(size_t i = 0; i != size; ++i) { + x[i] = word_add(x[i], y[i] & mask, &carry); } return (mask & carry); } /* -* If cond > 0 adds x[0:size] and y[0:size] and returns carry +* If cond > 0 subtracts y[0:size] from x[0:size] and returns borrow * Runs in constant time */ template inline constexpr auto bigint_cnd_sub(W cnd, W x[], const W y[], size_t size) -> W { - return bigint_cnd_sub(cnd, x, size, y, size); -} - -/* -* Equivalent to -* bigint_cnd_add( mask, x, y, size); -* bigint_cnd_sub(~mask, x, y, size); -* -* Mask must be either 0 or all 1 bits -*/ -template -inline constexpr void bigint_cnd_add_or_sub(CT::Mask mask, W x[], const W y[], size_t size) { - const size_t blocks = size - (size % 8); - - W carry = 0; - W borrow = 0; - - W t0[8] = {0}; - W t1[8] = {0}; - - for(size_t i = 0; i != blocks; i += 8) { - carry = word8_add3(t0, x + i, y + i, carry); - borrow = word8_sub3(t1, x + i, y + i, borrow); - mask.select_n(x + i, t0, t1, 8); - } - - for(size_t i = blocks; i != size; ++i) { - const W a = word_add(x[i], y[i], &carry); - const W s = word_sub(x[i], y[i], &borrow); - - x[i] = mask.select(a, s); - } -} - -/* -* Equivalent to -* bigint_cnd_add( mask, x, size, y, size); -* bigint_cnd_sub(~mask, x, size, z, size); -* -* Mask must be either 0 or all 1 bits -* -* Returns the carry or borrow resp -*/ -template -inline constexpr auto bigint_cnd_addsub(CT::Mask mask, W x[], const W y[], const W z[], size_t size) -> W { - const size_t blocks = size - (size % 8); + const auto mask = CT::Mask::expand(cnd).value(); W carry = 0; - W borrow = 0; - W t0[8] = {0}; - W t1[8] = {0}; - - for(size_t i = 0; i != blocks; i += 8) { - carry = word8_add3(t0, x + i, y + i, carry); - borrow = word8_sub3(t1, x + i, z + i, borrow); - mask.select_n(x + i, t0, t1, 8); - } - - for(size_t i = blocks; i != size; ++i) { - t0[0] = word_add(x[i], y[i], &carry); - t1[0] = word_sub(x[i], z[i], &borrow); - x[i] = mask.select(t0[0], t1[0]); + for(size_t i = 0; i != size; ++i) { + x[i] = word_sub(x[i], y[i] & mask, &carry); } - return mask.select(carry, borrow); + return (mask & carry); } /* @@ -184,7 +91,7 @@ * Two operand addition with carry out */ template -inline constexpr auto bigint_add2_nc(W x[], size_t x_size, const W y[], size_t y_size) -> W { +inline constexpr auto bigint_add2(W x[], size_t x_size, const W y[], size_t y_size) -> W { W carry = 0; BOTAN_ASSERT(x_size >= y_size, "Expected sizes"); @@ -210,9 +117,9 @@ * Three operand addition with carry out */ template -inline constexpr auto bigint_add3_nc(W z[], const W x[], size_t x_size, const W y[], size_t y_size) -> W { +inline constexpr auto bigint_add3(W z[], const W x[], size_t x_size, const W y[], size_t y_size) -> W { if(x_size < y_size) { - return bigint_add3_nc(z, y, y_size, x, x_size); + return bigint_add3(z, y, y_size, x, x_size); } W carry = 0; @@ -234,37 +141,6 @@ return carry; } -template -inline constexpr auto bigint_add(std::span z, std::span x, std::span y) -> W { - if constexpr(N == 4) { - return word4_add3(z.data(), x.data(), y.data(), 0); - } else if constexpr(N == 8) { - return word8_add3(z.data(), x.data(), y.data(), 0); - } else { - return bigint_add3_nc(z.data(), x.data(), N, y.data(), N); - } -} - -/** -* Two operand addition -* @param x the first operand (and output) -* @param x_size size of x -* @param y the second operand -* @param y_size size of y (must be <= x_size) -*/ -template -inline constexpr void bigint_add2(W x[], size_t x_size, const W y[], size_t y_size) { - x[x_size] += bigint_add2_nc(x, x_size, y, y_size); -} - -/** -* Three operand addition -*/ -template -inline constexpr void bigint_add3(W z[], const W x[], size_t x_size, const W y[], size_t y_size) { - z[x_size > y_size ? x_size : y_size] += bigint_add3_nc(z, x, x_size, y, y_size); -} - /** * Two operand subtraction */ @@ -298,13 +174,7 @@ inline constexpr void bigint_sub2_rev(W x[], const W y[], size_t y_size) { W borrow = 0; - const size_t blocks = y_size - (y_size % 8); - - for(size_t i = 0; i != blocks; i += 8) { - borrow = word8_sub2_rev(x + i, y + i, borrow); - } - - for(size_t i = blocks; i != y_size; ++i) { + for(size_t i = 0; i != y_size; ++i) { x[i] = word_sub(y[i], x[i], &borrow); } @@ -384,19 +254,8 @@ inline constexpr void bigint_monty_maybe_sub(W z[N], W x0, const W x[N], const W y[N]) { W borrow = 0; - if constexpr(N == 4) { - borrow = word4_sub3(z, x, y, borrow); - } else if constexpr(N == 8) { - borrow = word8_sub3(z, x, y, borrow); - } else { - const constexpr size_t blocks = N - (N % 8); - for(size_t i = 0; i != blocks; i += 8) { - borrow = word8_sub3(z + i, x + i, y + i, borrow); - } - - for(size_t i = blocks; i != N; ++i) { - z[i] = word_sub(x[i], y[i], &borrow); - } + for(size_t i = 0; i != N; ++i) { + z[i] = word_sub(x[i], y[i], &borrow); } borrow = (x0 - borrow) > x0; @@ -409,7 +268,7 @@ * Otherwise compute z = y - x * No borrow is possible since the result is always >= 0 * -* Returns ~0 if x >= y or 0 if x < y +* Returns a Mask: |1| if x >= y or |0| if x < y * @param z output array of at least N words * @param x input array of N words * @param y input array of N words @@ -449,8 +308,8 @@ const size_t word_shift = shift / WordInfo::bits; const size_t bit_shift = shift % WordInfo::bits; - copy_mem(x + word_shift, x, x_words); - clear_mem(x, word_shift); + unchecked_copy_memory(x + word_shift, x, x_words); + zeroize_buffer(x, word_shift); const auto carry_mask = CT::Mask::expand(bit_shift); const W carry_shift = carry_mask.if_set_return(WordInfo::bits - bit_shift); @@ -471,9 +330,9 @@ const size_t top = x_size >= word_shift ? (x_size - word_shift) : 0; if(top > 0) { - copy_mem(x, x + word_shift, top); + unchecked_copy_memory(x, x + word_shift, top); } - clear_mem(x + top, std::min(word_shift, x_size)); + zeroize_buffer(x + top, std::min(word_shift, x_size)); const auto carry_mask = CT::Mask::expand(bit_shift); const W carry_shift = carry_mask.if_set_return(WordInfo::bits - bit_shift); @@ -492,7 +351,7 @@ const size_t word_shift = shift / WordInfo::bits; const size_t bit_shift = shift % WordInfo::bits; - copy_mem(y + word_shift, x, x_size); + unchecked_copy_memory(y + word_shift, x, x_size); const auto carry_mask = CT::Mask::expand(bit_shift); const W carry_shift = carry_mask.if_set_return(WordInfo::bits - bit_shift); @@ -512,7 +371,7 @@ const size_t new_size = x_size < word_shift ? 0 : (x_size - word_shift); if(new_size > 0) { - copy_mem(y, x + word_shift, new_size); + unchecked_copy_memory(y, x + word_shift, new_size); } const auto carry_mask = CT::Mask::expand(bit_shift); @@ -531,15 +390,9 @@ */ template [[nodiscard]] inline constexpr auto bigint_linmul2(W x[], size_t x_size, W y) -> W { - const size_t blocks = x_size - (x_size % 8); - W carry = 0; - for(size_t i = 0; i != blocks; i += 8) { - carry = word8_linmul2(x + i, y, carry); - } - - for(size_t i = blocks; i != x_size; ++i) { + for(size_t i = 0; i != x_size; ++i) { x[i] = word_madd2(x[i], y, &carry); } @@ -613,8 +466,8 @@ /** * Compare x and y -* Return ~0 if x[0:x_size] < y[0:y_size] or 0 otherwise -* If lt_or_equal is true, returns ~0 also for x == y +* Returns a Mask: |1| if x[0:x_size] < y[0:y_size] or |0| otherwise +* If lt_or_equal is true, returns |1| also for x == y */ template inline constexpr auto bigint_ct_is_lt(const W x[], size_t x_size, const W y[], size_t y_size, bool lt_or_equal = false) @@ -673,109 +526,158 @@ return CT::Mask::is_zero(diff); } -/** -* Set z to abs(x-y), ie if x >= y, then compute z = x - y -* Otherwise compute z = y - x -* No borrow is possible since the result is always >= 0 -* -* Return the relative size of x vs y (-1, 0, 1) -* -* @param z output array of max(x_size,y_size) words -* @param x input param -* @param x_size length of x -* @param y input param -* @param y_size length of y -*/ -template -inline constexpr int32_t bigint_sub_abs(W z[], const W x[], size_t x_size, const W y[], size_t y_size) { - const int32_t relative_size = bigint_cmp(x, x_size, y, y_size); - - // Swap if relative_size == -1 - const bool need_swap = relative_size < 0; - CT::conditional_swap_ptr(need_swap, x, y); - CT::conditional_swap(need_swap, x_size, y_size); - - /* - * We know at this point that x >= y so if y_size is larger than - * x_size, we are guaranteed they are just leading zeros which can - * be ignored - */ - y_size = std::min(x_size, y_size); - - bigint_sub3(z, x, x_size, y, y_size); +template +consteval std::pair div_magic() + requires(div == 10) +{ + if constexpr(div == 10 && std::same_as) { + constexpr W magic = 0xCCCCCCCD; + constexpr size_t shift = 35; + return std::make_pair(magic, shift); + } else if constexpr(div == 10 && std::same_as) { + constexpr W magic = 0xCCCCCCCCCCCCCCCD; + constexpr size_t shift = 67; + return std::make_pair(magic, shift); + } +} - return relative_size; +template +inline constexpr W divide_10(W x) { + auto [magic, shift] = div_magic(); + const auto p = typename WordInfo::dword(magic) * x; + return static_cast(p >> shift); } /** -* Set t to t-s modulo mod +* Setup for variable-time word level division/modulo operations * -* @param t first integer -* @param s second integer -* @param mod the modulus -* @param mod_sw size of t, s, and mod -* @param ws workspace of size mod_sw +* Currently this just uses the compiler's support for a 2/1 word division, +* but likely could be improved by precomputed values based on the divisor, +* for example using the approaches outlined in Hacker's Delight chapter 10. */ template -inline constexpr void bigint_mod_sub(W t[], const W s[], const W mod[], size_t mod_sw, W ws[]) { - // ws = t - s - const W borrow = bigint_sub3(ws, t, mod_sw, s, mod_sw); +class divide_precomp final { + public: + explicit constexpr divide_precomp(W divisor) : m_divisor(divisor) { + BOTAN_ARG_CHECK(m_divisor != 0, "Division by zero"); + } - // Conditionally add back the modulus - bigint_cnd_add(borrow, ws, mod, mod_sw); + // Return floor((n1 || n0) / d) + // + // This assumes n1 < d so that the quotient fits in a word + inline constexpr W vartime_div_2to1(W n1, W n0) const { + BOTAN_ASSERT_NOMSG(n1 < m_divisor); - copy_mem(t, ws, mod_sw); -} + if(m_divisor == WordInfo::max) { + return vartime_div_2to1_max_d(n1, n0); + } -/** -* Compute ((n1< -inline constexpr auto bigint_divop_vartime(W n1, W n0, W d) -> W { - if(d == 0) { - throw Invalid_Argument("bigint_divop_vartime divide by zero"); - } + if(m_divisor == WordInfo::top_bit) { + // Simply a shift by N-1 bits + return (n1 << 1) | (n0 >> (WordInfo::bits - 1)); + } - if constexpr(WordInfo::dword_is_native) { - typename WordInfo::dword n = n1; - n <<= WordInfo::bits; - n |= n0; - return static_cast(n / d); - } else { - W high = n1 % d; - W quotient = 0; + if(!std::is_constant_evaluated()) { +#if defined(BOTAN_MP_USE_X86_64_ASM) + if constexpr(std::same_as) { + W quotient = 0; + W remainder = 0; + // NOLINTNEXTLINE(*-no-assembler) + asm("divq %[v]" : "=a"(quotient), "=d"(remainder) : [v] "r"(m_divisor), "a"(n0), "d"(n1)); + return quotient; + } +#endif + +#if !defined(BOTAN_BUILD_COMPILER_IS_CLANGCL) + + /* clang-cl has a bug where on encountering a 128/64 division it emits + * a call to __udivti3() but then fails to link the relevant builtin into + * the binary, causing a link failure. Work around this by simply omitting + * such code for clang-cl + * + * See https://github.com/llvm/llvm-project/issues/25679 + */ + if constexpr(WordInfo::dword_is_native) { + typename WordInfo::dword n = n1; + n <<= WordInfo::bits; + n |= n0; + return static_cast(n / m_divisor); + } +#endif + } + + W high = n1; + W quotient = 0; - for(size_t i = 0; i != WordInfo::bits; ++i) { - const W high_top_bit = high >> (WordInfo::bits - 1); + for(size_t i = 0; i != WordInfo::bits; ++i) { + const W high_top_bit = high >> (WordInfo::bits - 1); - high <<= 1; - high |= (n0 >> (WordInfo::bits - 1 - i)) & 1; - quotient <<= 1; - - if(high_top_bit || high >= d) { - high -= d; - quotient |= 1; + high <<= 1; + high |= (n0 >> (WordInfo::bits - 1 - i)) & 1; + quotient <<= 1; + + if(high_top_bit || high >= m_divisor) { + high -= m_divisor; + quotient |= 1; + } } + + return quotient; } - return quotient; - } -} + // Return floor((n1 || n0) % d) + // + // This assumes n1 < d so that the quotient fits in a word + inline constexpr W vartime_mod_2to1(W n1, W n0) const { + BOTAN_ASSERT_NOMSG(n1 < m_divisor); + W q = this->vartime_div_2to1(n1, n0); + W carry = 0; + q = word_madd2(q, m_divisor, &carry); + return (n0 - q); + } -/** -* Compute ((n1< -inline constexpr auto bigint_modop_vartime(W n1, W n0, W d) -> W { - if(d == 0) { - throw Invalid_Argument("bigint_modop_vartime divide by zero"); - } + private: + /* + * When the divisor is the maximum integer value, then a two word + * division becomes simple. + */ + static inline constexpr W vartime_div_2to1_max_d(W n1, W n0) { + /* + Use k to refer to WordInfo::bits - W z = bigint_divop_vartime(n1, n0, d); - W carry = 0; - z = word_madd2(z, d, &carry); - return (n0 - z); -} + We are dividing n = (n1 * 2^k) + n0 by 2^k - 1 + + Recall that 2^k = 1 (mod 2^k - 1) + + Rewrite n = n1*2^k + n0 as n1*(2^k - 1) + n1 + n0 + + The result of dividing n by (2^k - 1) will be equal to + (n1*(2^k-1) + n1 + n0) / (2^k-1) = + n1 + ((n1 + n0) / (2^k-1) + + Use c to refer to ((n1 + n0) / (2^k-1)) + + If (n1 + n0) < (2^k - 1) then c is 0 + If (n1 + n0) >= (2^k - 1) then c is 1 + + Since n1 < 2^k - 1 [*] and n0 <= 2^k - 1 it is impossible for (n1 + n0) / (2^k -1) + to be greater than 1. + + [*] We require n1 be strictly less than the divisor to ensure that the + output fits in a single word; this is checked at the start of vartime_div_2to1. + */ + + const W s = n0 + n1; + // did n0 + n1 overflow? or does (n0 + n1) == 2^k - 1? if either, c == 1 + if(s < n0 || s == WordInfo::max) { + n1 += 1; + } + + return n1; + } + + W m_divisor; +}; /* * Compute an integer x such that (a*x) == -1 (mod 2^n) @@ -786,25 +688,18 @@ */ template inline constexpr auto monty_inverse(W a) -> W { - if(a % 2 == 0) { - throw Invalid_Argument("monty_inverse only valid for odd integers"); - } + BOTAN_ARG_CHECK(a % 2 == 1, "Cannot compute Montgomery inverse of an even integer"); - /* - * From "A New Algorithm for Inversion mod p^k" by Çetin Kaya Koç - * https://eprint.iacr.org/2017/411.pdf sections 5 and 7. - */ + // Newton's Method, following https://lemire.me/blog/2017/09/18/computing-the-inverse-of-odd-integers/ - W b = 1; - W r = 0; + constexpr size_t iter = WordInfo::bits == 64 ? 4 : 3; - for(size_t i = 0; i != WordInfo::bits; ++i) { - const W bi = b % 2; - r >>= 1; - r += bi << (WordInfo::bits - 1); + // Initial guess provides 5 bits of accuracy + W r = (3 * a) ^ 2; - b -= a * bi; - b >>= 1; + // Each iteration doubles the accuracy + for(size_t i = 0; i != iter; ++i) { + r = r * (2 - r * a); } // Now invert in addition space @@ -815,28 +710,30 @@ template inline constexpr W shift_left(std::array& x) { + static_assert(N >= 1, "Invalid input size"); static_assert(S < WordInfo::bits, "Shift too large"); - W carry = 0; - for(size_t i = 0; i != N; ++i) { - const W w = x[i]; - x[i] = (w << S) | carry; - carry = w >> (WordInfo::bits - S); + const W carry = x[N - 1] >> (WordInfo::bits - S); + + for(size_t i = N - 1; i != 0; --i) { + x[i] = (x[i] << S) | (x[i - 1] >> (WordInfo::bits - S)); } + x[0] <<= S; return carry; } template inline constexpr W shift_right(std::array& x) { + static_assert(N >= 1, "Invalid input size"); static_assert(S < WordInfo::bits, "Shift too large"); - W carry = 0; - for(size_t i = 0; i != N; ++i) { - const W w = x[N - 1 - i]; - x[N - 1 - i] = (w >> S) | carry; - carry = w << (WordInfo::bits - S); + const W carry = x[0] << (WordInfo::bits - S); + + for(size_t i = 0; i != N - 1; ++i) { + x[i] = (x[i] >> S) | (x[i + 1] << (WordInfo::bits - S)); } + x[N - 1] >>= S; return carry; } @@ -975,53 +872,67 @@ /* * Montgomery reduction * -* Each of these functions makes the following assumptions: +* Sets r to the Montgomery reduction of z using parameters p / p_dash * -* z_size == 2*p_size -* ws_size >= p_size +* The workspace should be of size equal to the prime */ -BOTAN_FUZZER_API void bigint_monty_redc_4(word z[8], const word p[4], word p_dash, word ws[]); -BOTAN_FUZZER_API void bigint_monty_redc_6(word z[12], const word p[6], word p_dash, word ws[]); -BOTAN_FUZZER_API void bigint_monty_redc_8(word z[16], const word p[8], word p_dash, word ws[]); -BOTAN_FUZZER_API void bigint_monty_redc_16(word z[32], const word p[16], word p_dash, word ws[]); -BOTAN_FUZZER_API void bigint_monty_redc_24(word z[48], const word p[24], word p_dash, word ws[]); -BOTAN_FUZZER_API void bigint_monty_redc_32(word z[64], const word p[32], word p_dash, word ws[]); +BOTAN_FUZZER_API void bigint_monty_redc_4(word r[4], const word z[8], const word p[4], word p_dash, word ws[4]); +BOTAN_FUZZER_API void bigint_monty_redc_6(word r[6], const word z[12], const word p[6], word p_dash, word ws[6]); +BOTAN_FUZZER_API void bigint_monty_redc_8(word r[8], const word z[16], const word p[8], word p_dash, word ws[8]); +BOTAN_FUZZER_API void bigint_monty_redc_12(word r[12], const word z[24], const word p[12], word p_dash, word ws[12]); +BOTAN_FUZZER_API void bigint_monty_redc_16(word r[16], const word z[32], const word p[16], word p_dash, word ws[16]); +BOTAN_FUZZER_API void bigint_monty_redc_24(word r[24], const word z[48], const word p[24], word p_dash, word ws[24]); +BOTAN_FUZZER_API void bigint_monty_redc_32(word r[32], const word z[64], const word p[32], word p_dash, word ws[32]); BOTAN_FUZZER_API -void bigint_monty_redc_generic(word z[], size_t z_size, const word p[], size_t p_size, word p_dash, word ws[]); +void bigint_monty_redc_generic( + word r[], const word z[], size_t z_size, const word p[], size_t p_size, word p_dash, word ws[]); /** * Montgomery Reduction -* @param z integer to reduce, of size exactly 2*p_size. Output is in -* the first p_size words, higher words are set to zero. +* @param r result of exactly p_size words +* @param z integer to reduce, of size exactly 2*p_size. * @param p modulus * @param p_size size of p * @param p_dash Montgomery value * @param ws array of at least p_size words * @param ws_size size of ws in words +* +* It is allowed to set &r[0] == &z[0] however in this case note that only the +* first p_size words of r will be written to and the high p_size words of r/z +* will still hold the original inputs, these must be cleared after use. +* See bigint_monty_redc_inplace */ -inline void bigint_monty_redc(word z[], const word p[], size_t p_size, word p_dash, word ws[], size_t ws_size) { +inline void bigint_monty_redc( + word r[], const word z[], const word p[], size_t p_size, word p_dash, word ws[], size_t ws_size) { const size_t z_size = 2 * p_size; BOTAN_ARG_CHECK(ws_size >= p_size, "Montgomery reduction workspace too small"); if(p_size == 4) { - bigint_monty_redc_4(z, p, p_dash, ws); + bigint_monty_redc_4(r, z, p, p_dash, ws); } else if(p_size == 6) { - bigint_monty_redc_6(z, p, p_dash, ws); + bigint_monty_redc_6(r, z, p, p_dash, ws); } else if(p_size == 8) { - bigint_monty_redc_8(z, p, p_dash, ws); + bigint_monty_redc_8(r, z, p, p_dash, ws); + } else if(p_size == 12) { + bigint_monty_redc_12(r, z, p, p_dash, ws); } else if(p_size == 16) { - bigint_monty_redc_16(z, p, p_dash, ws); + bigint_monty_redc_16(r, z, p, p_dash, ws); } else if(p_size == 24) { - bigint_monty_redc_24(z, p, p_dash, ws); + bigint_monty_redc_24(r, z, p, p_dash, ws); } else if(p_size == 32) { - bigint_monty_redc_32(z, p, p_dash, ws); + bigint_monty_redc_32(r, z, p, p_dash, ws); } else { - bigint_monty_redc_generic(z, z_size, p, p_size, p_dash, ws); + bigint_monty_redc_generic(r, z, z_size, p, p_size, p_dash, ws); } } +inline void bigint_monty_redc_inplace(word z[], const word p[], size_t p_size, word p_dash, word ws[], size_t ws_size) { + bigint_monty_redc(z, z, p, p_size, p_dash, ws, ws_size); + zeroize_buffer(z + p_size, p_size); +} + /** * Basecase O(N^2) multiplication */ @@ -1051,20 +962,6 @@ void bigint_sqr(word z[], size_t z_size, const word x[], size_t x_size, size_t x_sw, word workspace[], size_t ws_size); /** -* Return 2**B - C -*/ -template -consteval std::array crandall_p() { - static_assert(C % 2 == 1); - std::array P; - for(size_t i = 0; i != N; ++i) { - P[i] = WordInfo::max; - } - P[0] = WordInfo::max - (C - 1); - return P; -} - -/** * Reduce z modulo p = 2**B - C where C is small * * z is assumed to be at most (p-1)**2 @@ -1091,24 +988,90 @@ word carry_c[2] = {0}; carry_c[0] = word_madd2(carry, C, &carry_c[1]); - carry = bigint_add2_nc(hi.data(), N, carry_c, 2); + carry = bigint_add2(hi.data(), N, carry_c, 2); - constexpr auto P = crandall_p(); + constexpr W P0 = WordInfo::max - (C - 1); std::array r = {}; - bigint_monty_maybe_sub(r.data(), carry, hi.data(), P.data()); + + W borrow = 0; + + /* + * For undetermined reasons, on GCC (only) removing this asm block causes + * massive (up to 20%) performance regressions in secp256k1. + * + * The generated code without the asm seems quite reasonable, and timing + * repeated calls to redc_crandall with the cycle counter show that GCC + * computes it in about the same number of cycles with or without the asm. + * + * So the cause of the regression is unclear. But it is reproducible across + * machines and GCC versions. + */ +#if defined(BOTAN_MP_USE_X86_64_ASM) && defined(__GNUC__) && !defined(__clang__) + if constexpr(N == 4 && std::same_as) { + if(!std::is_constant_evaluated()) { + asm volatile(R"( + movq 0(%[x]), %[borrow] + subq %[p0], %[borrow] + movq %[borrow], 0(%[r]) + movq 8(%[x]), %[borrow] + sbbq $-1, %[borrow] + movq %[borrow], 8(%[r]) + movq 16(%[x]), %[borrow] + sbbq $-1, %[borrow] + movq %[borrow], 16(%[r]) + movq 24(%[x]), %[borrow] + sbbq $-1, %[borrow] + movq %[borrow], 24(%[r]) + sbbq %[borrow],%[borrow] + negq %[borrow] + )" + : [borrow] "=r"(borrow) + : [x] "r"(hi.data()), [p0] "r"(P0), [r] "r"(r.data()), "0"(borrow) + : "cc", "memory"); + + borrow = (carry - borrow) > carry; + CT::conditional_assign_mem(borrow, r.data(), hi.data(), N); + return r; + } + } +#endif + + r[0] = word_sub(hi[0], P0, &borrow); + for(size_t i = 1; i != N; ++i) { + r[i] = word_sub(hi[i], WordInfo::max, &borrow); + } + + borrow = (carry - borrow) > carry; + + CT::conditional_assign_mem(borrow, r.data(), hi.data(), N); return r; } -/** -* Set r to r - C. Then if r < 0, add P to r -*/ -template -constexpr inline void bigint_correct_redc(std::array& r, const std::array& P, const std::array& C) { - // TODO look into combining the two operations for important values of N - W borrow = bigint_sub2(r.data(), N, C.data(), N); - bigint_cnd_add(borrow, r.data(), N, P.data(), N); +// Extract a WindowBits sized window out of s, depending on offset. +template +constexpr size_t read_window_bits(std::span words, size_t offset) { + static_assert(WindowBits >= 1 && WindowBits <= 7); + + constexpr uint8_t WindowMask = static_cast(1 << WindowBits) - 1; + + constexpr size_t W_bits = sizeof(W) * 8; + const auto bit_shift = offset % W_bits; + const auto word_offset = words.size() - 1 - (offset / W_bits); + + const bool single_byte_window = bit_shift <= (W_bits - WindowBits) || word_offset == 0; + + const auto w0 = words[word_offset]; + + if(single_byte_window) { + return (w0 >> bit_shift) & WindowMask; + } else { + // Otherwise we must join two words and extract the result + const auto w1 = words[word_offset - 1]; + const auto combined = ((w0 >> bit_shift) | (w1 << (W_bits - bit_shift))); + return combined & WindowMask; + } } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/math/mp/mp_karat.cpp botan3-3.12.0+dfsg/src/lib/math/mp/mp_karat.cpp --- botan3-3.7.1+dfsg/src/lib/math/mp/mp_karat.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/mp/mp_karat.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,8 +9,8 @@ #include #include -#include #include +#include namespace Botan { @@ -24,7 +24,7 @@ const size_t x_size_8 = x_size - (x_size % 8); - clear_mem(z, z_size); + zeroize_buffer(z, z_size); for(size_t i = 0; i != y_size; ++i) { const word y_i = y[i]; @@ -50,7 +50,7 @@ const size_t x_size_8 = x_size - (x_size % 8); - clear_mem(z, z_size); + zeroize_buffer(z, z_size); for(size_t i = 0; i != x_size; ++i) { const word x_i = x[i]; @@ -78,7 +78,7 @@ * Karatsuba Multiplication Operation */ void karatsuba_mul(word z[], const word x[], const word y[], size_t N, word workspace[]) { - if(N < KARATSUBA_MULTIPLY_THRESHOLD || N % 2) { + if(N < KARATSUBA_MULTIPLY_THRESHOLD || N % 2 != 0) { switch(N) { case 6: return bigint_comba_mul6(z, x, y); @@ -107,12 +107,12 @@ word* ws0 = workspace; word* ws1 = workspace + N; - clear_mem(workspace, 2 * N); + zeroize_buffer(workspace, 2 * N); /* * If either of cmp0 or cmp1 is zero then z0 or z1 resp is zero here, * resulting in a no-op - z0*z1 will be equal to zero so we don't need to do - * anything, clear_mem above already set the correct result. + * anything, zeroize_buffer above already set the correct result. * * However we ignore the result of the comparisons and always perform the * subtractions and recursively multiply to avoid the timing channel. @@ -131,22 +131,23 @@ // Compute X_hi * Y_hi karatsuba_mul(z1, x1, y1, N2, ws1); - const word ws_carry = bigint_add3_nc(ws1, z0, N, z1, N); - word z_carry = bigint_add2_nc(z + N2, N, ws1, N); + const word ws_carry = bigint_add3(ws1, z0, N, z1, N); + word z_carry = bigint_add2(z + N2, N, ws1, N); - z_carry += bigint_add2_nc(z + N + N2, N2, &ws_carry, 1); - bigint_add2_nc(z + N + N2, N2, &z_carry, 1); + z_carry += bigint_add2(z + N + N2, N2, &ws_carry, 1); + bigint_add2(z + N + N2, N2, &z_carry, 1); - clear_mem(workspace + N, N2); + zeroize_buffer(workspace + N, N2); - bigint_cnd_add_or_sub(neg_mask, z + N2, workspace, 2 * N - N2); + bigint_cnd_add(neg_mask.value(), z + N2, workspace, 2 * N - N2); + bigint_cnd_sub((~neg_mask).value(), z + N2, workspace, 2 * N - N2); } /* * Karatsuba Squaring Operation */ void karatsuba_sqr(word z[], const word x[], size_t N, word workspace[]) { - if(N < KARATSUBA_SQUARE_THRESHOLD || N % 2) { + if(N < KARATSUBA_SQUARE_THRESHOLD || N % 2 != 0) { switch(N) { case 6: return bigint_comba_sqr6(z, x); @@ -173,7 +174,7 @@ word* ws0 = workspace; word* ws1 = workspace + N; - clear_mem(workspace, 2 * N); + zeroize_buffer(workspace, 2 * N); // See comment in karatsuba_mul bigint_sub_abs(z0, x0, x1, N2, workspace); @@ -182,11 +183,11 @@ karatsuba_sqr(z0, x0, N2, ws1); karatsuba_sqr(z1, x1, N2, ws1); - const word ws_carry = bigint_add3_nc(ws1, z0, N, z1, N); - word z_carry = bigint_add2_nc(z + N2, N, ws1, N); + const word ws_carry = bigint_add3(ws1, z0, N, z1, N); + word z_carry = bigint_add2(z + N2, N, ws1, N); - z_carry += bigint_add2_nc(z + N + N2, N2, &ws_carry, 1); - bigint_add2_nc(z + N + N2, N2, &z_carry, 1); + z_carry += bigint_add2(z + N + N2, N2, &ws_carry, 1); + bigint_add2(z + N + N2, N2, &z_carry, 1); /* * This is only actually required if cmp (result of bigint_sub_abs) is != 0, @@ -204,7 +205,7 @@ return 0; } - if(((x_size == x_sw) && (x_size % 2)) || ((y_size == y_sw) && (y_size % 2))) { + if(((x_size == x_sw) && (x_size % 2 != 0)) || ((y_size == y_sw) && (y_size % 2 != 0))) { return 0; } @@ -212,14 +213,14 @@ const size_t end = (x_size < y_size) ? x_size : y_size; if(start == end) { - if(start % 2) { + if(start % 2 != 0) { return 0; } return start; } for(size_t j = start; j <= end; ++j) { - if(j % 2) { + if(j % 2 != 0) { continue; } @@ -243,14 +244,14 @@ */ size_t karatsuba_size(size_t z_size, size_t x_size, size_t x_sw) { if(x_sw == x_size) { - if(x_sw % 2) { + if(x_sw % 2 != 0) { return 0; } return x_sw; } for(size_t j = x_sw; j <= x_size; ++j) { - if(j % 2) { + if(j % 2 != 0) { continue; } @@ -289,7 +290,7 @@ size_t y_sw, word workspace[], size_t ws_size) { - clear_mem(z, z_size); + zeroize_buffer(z, z_size); if(x_sw == 1) { bigint_linmul3(z, y, y_sw, x[0]); @@ -307,12 +308,12 @@ bigint_comba_mul16(z, x, y); } else if(sized_for_comba_mul<24>(x_sw, x_size, y_sw, y_size, z_size)) { bigint_comba_mul24(z, x, y); - } else if(x_sw < KARATSUBA_MULTIPLY_THRESHOLD || y_sw < KARATSUBA_MULTIPLY_THRESHOLD || !workspace) { + } else if(x_sw < KARATSUBA_MULTIPLY_THRESHOLD || y_sw < KARATSUBA_MULTIPLY_THRESHOLD || workspace == nullptr) { basecase_mul(z, z_size, x, x_sw, y, y_sw); } else { const size_t N = karatsuba_size(z_size, x_size, x_sw, y_size, y_sw); - if(N && z_size >= 2 * N && ws_size >= 2 * N) { + if(N > 0 && z_size >= 2 * N && ws_size >= 2 * N) { karatsuba_mul(z, x, y, N, workspace); } else { basecase_mul(z, z_size, x, x_sw, y, y_sw); @@ -324,7 +325,7 @@ * Squaring Algorithm Dispatcher */ void bigint_sqr(word z[], size_t z_size, const word x[], size_t x_size, size_t x_sw, word workspace[], size_t ws_size) { - clear_mem(z, z_size); + zeroize_buffer(z, z_size); BOTAN_ASSERT(z_size / 2 >= x_sw, "Output size is sufficient"); @@ -342,12 +343,12 @@ bigint_comba_sqr16(z, x); } else if(sized_for_comba_sqr<24>(x_sw, x_size, z_size)) { bigint_comba_sqr24(z, x); - } else if(x_size < KARATSUBA_SQUARE_THRESHOLD || !workspace) { + } else if(x_size < KARATSUBA_SQUARE_THRESHOLD || workspace == nullptr) { basecase_sqr(z, z_size, x, x_sw); } else { const size_t N = karatsuba_size(z_size, x_size, x_sw); - if(N && z_size >= 2 * N && ws_size >= 2 * N) { + if(N > 0 && z_size >= 2 * N && ws_size >= 2 * N) { karatsuba_sqr(z, x, N, workspace); } else { basecase_sqr(z, z_size, x, x_sw); diff -Nru botan3-3.7.1+dfsg/src/lib/math/mp/mp_monty.cpp botan3-3.12.0+dfsg/src/lib/math/mp/mp_monty.cpp --- botan3-3.7.1+dfsg/src/lib/math/mp/mp_monty.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/mp/mp_monty.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ /* * Montgomery Reduction -* (C) 1999-2011 Jack Lloyd +* (C) 1999-2011,2025 Jack Lloyd * 2006 Luca Piccarreta * 2016 Matthias Gierlings * @@ -10,12 +10,71 @@ #include #include -#include -#include -#include namespace Botan { +namespace { + +BOTAN_FORCE_INLINE void mul_rev_range(word3& accum, const word ws[], const word p[], size_t bound) { + /* + Unrolled version of: + + for(size_t i = 0; i < bound; ++i) { + accum.mul(ws[i], p[bound - i]); + } + */ + + size_t lower = 0; + while(lower < bound) { + const size_t upper = bound - lower; + + if(upper >= 16) { + accum.mul(ws[lower], p[upper]); + accum.mul(ws[lower + 1], p[upper - 1]); + accum.mul(ws[lower + 2], p[upper - 2]); + accum.mul(ws[lower + 3], p[upper - 3]); + accum.mul(ws[lower + 4], p[upper - 4]); + accum.mul(ws[lower + 5], p[upper - 5]); + accum.mul(ws[lower + 6], p[upper - 6]); + accum.mul(ws[lower + 7], p[upper - 7]); + accum.mul(ws[lower + 8], p[upper - 8]); + accum.mul(ws[lower + 9], p[upper - 9]); + accum.mul(ws[lower + 10], p[upper - 10]); + accum.mul(ws[lower + 11], p[upper - 11]); + accum.mul(ws[lower + 12], p[upper - 12]); + accum.mul(ws[lower + 13], p[upper - 13]); + accum.mul(ws[lower + 14], p[upper - 14]); + accum.mul(ws[lower + 15], p[upper - 15]); + lower += 16; + } else if(upper >= 8) { + accum.mul(ws[lower], p[upper]); + accum.mul(ws[lower + 1], p[upper - 1]); + accum.mul(ws[lower + 2], p[upper - 2]); + accum.mul(ws[lower + 3], p[upper - 3]); + accum.mul(ws[lower + 4], p[upper - 4]); + accum.mul(ws[lower + 5], p[upper - 5]); + accum.mul(ws[lower + 6], p[upper - 6]); + accum.mul(ws[lower + 7], p[upper - 7]); + lower += 8; + } else if(upper >= 4) { + accum.mul(ws[lower], p[upper]); + accum.mul(ws[lower + 1], p[upper - 1]); + accum.mul(ws[lower + 2], p[upper - 2]); + accum.mul(ws[lower + 3], p[upper - 3]); + lower += 4; + } else if(upper >= 2) { + accum.mul(ws[lower], p[upper]); + accum.mul(ws[lower + 1], p[upper - 1]); + lower += 2; + } else { + accum.mul(ws[lower], p[upper]); + lower += 1; + } + } +} + +} // namespace + /* * Montgomery reduction - product scanning form * @@ -28,7 +87,8 @@ * https://eprint.iacr.org/2013/882.pdf * https://www.microsoft.com/en-us/research/wp-content/uploads/1996/01/j37acmon.pdf */ -void bigint_monty_redc_generic(word z[], size_t z_size, const word p[], size_t p_size, word p_dash, word ws[]) { +void bigint_monty_redc_generic( + word r[], const word z[], size_t z_size, const word p[], size_t p_size, word p_dash, word ws[]) { BOTAN_ARG_CHECK(z_size >= 2 * p_size && p_size > 0, "Invalid sizes for bigint_monty_redc_generic"); word3 accum; @@ -38,19 +98,13 @@ ws[0] = accum.monty_step(p[0], p_dash); for(size_t i = 1; i != p_size; ++i) { - for(size_t j = 0; j < i; ++j) { - accum.mul(ws[j], p[i - j]); - } - + mul_rev_range(accum, ws, p, i); accum.add(z[i]); ws[i] = accum.monty_step(p[0], p_dash); } for(size_t i = 0; i != p_size - 1; ++i) { - for(size_t j = i + 1; j != p_size; ++j) { - accum.mul(ws[j], p[p_size + i - j]); - } - + mul_rev_range(accum, &ws[i + 1], &p[i], p_size - (i + 1)); accum.add(z[p_size + i]); ws[i] = accum.extract(); } @@ -63,7 +117,7 @@ /* * The result might need to be reduced mod p. To avoid a timing - * channel, always perform the subtraction. If in the compution + * channel, always perform the subtraction. If in the computation * of x - p a borrow is required then x was already < p. * * x starts at ws[0] and is p_size bytes long plus a possible high @@ -79,10 +133,7 @@ * the Montgomery result is < P */ - bigint_monty_maybe_sub(p_size, z, w1, ws, p); - - // Clear the high words that contain the original input - clear_mem(z + p_size, z_size - p_size); + bigint_monty_maybe_sub(p_size, r, w1, ws, p); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/math/mp/mp_monty_n.cpp botan3-3.12.0+dfsg/src/lib/math/mp/mp_monty_n.cpp --- botan3-3.7.1+dfsg/src/lib/math/mp/mp_monty_n.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/mp/mp_monty_n.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,5 @@ /* -* This file was automatically generated by ./src/scripts/dev_tools/gen_mp_monty.py on 2024-04-09 +* This file was automatically generated by ./src/scripts/dev_tools/gen_mp_monty.py on 2026-04-24 * All manual changes will be lost. Edit the script instead. * * Botan is released under the Simplified BSD License (see license.txt) @@ -7,11 +7,9 @@ #include -#include - namespace Botan { -void bigint_monty_redc_4(word z[8], const word p[4], word p_dash, word ws[]) { +void bigint_monty_redc_4(word r[4], const word z[8], const word p[4], word p_dash, word ws[4]) { word3 accum; accum.add(z[0]); ws[0] = accum.monty_step(p[0], p_dash); @@ -41,12 +39,11 @@ ws[2] = accum.extract(); accum.add(z[7]); ws[3] = accum.extract(); - word w1 = accum.extract(); - bigint_monty_maybe_sub<4>(z, w1, ws, p); - clear_mem(z + 4, 4); + const word w1 = accum.extract(); + bigint_monty_maybe_sub<4>(r, w1, ws, p); } -void bigint_monty_redc_6(word z[12], const word p[6], word p_dash, word ws[]) { +void bigint_monty_redc_6(word r[6], const word z[12], const word p[6], word p_dash, word ws[6]) { word3 accum; accum.add(z[0]); ws[0] = accum.monty_step(p[0], p_dash); @@ -102,12 +99,11 @@ ws[4] = accum.extract(); accum.add(z[11]); ws[5] = accum.extract(); - word w1 = accum.extract(); - bigint_monty_maybe_sub<6>(z, w1, ws, p); - clear_mem(z + 6, 6); + const word w1 = accum.extract(); + bigint_monty_maybe_sub<6>(r, w1, ws, p); } -void bigint_monty_redc_8(word z[16], const word p[8], word p_dash, word ws[]) { +void bigint_monty_redc_8(word r[8], const word z[16], const word p[8], word p_dash, word ws[8]) { word3 accum; accum.add(z[0]); ws[0] = accum.monty_step(p[0], p_dash); @@ -197,12 +193,197 @@ ws[6] = accum.extract(); accum.add(z[15]); ws[7] = accum.extract(); - word w1 = accum.extract(); - bigint_monty_maybe_sub<8>(z, w1, ws, p); - clear_mem(z + 8, 8); + const word w1 = accum.extract(); + bigint_monty_maybe_sub<8>(r, w1, ws, p); +} + +void bigint_monty_redc_12(word r[12], const word z[24], const word p[12], word p_dash, word ws[12]) { + word3 accum; + accum.add(z[0]); + ws[0] = accum.monty_step(p[0], p_dash); + accum.mul(ws[0], p[1]); + accum.add(z[1]); + ws[1] = accum.monty_step(p[0], p_dash); + accum.mul(ws[0], p[2]); + accum.mul(ws[1], p[1]); + accum.add(z[2]); + ws[2] = accum.monty_step(p[0], p_dash); + accum.mul(ws[0], p[3]); + accum.mul(ws[1], p[2]); + accum.mul(ws[2], p[1]); + accum.add(z[3]); + ws[3] = accum.monty_step(p[0], p_dash); + accum.mul(ws[0], p[4]); + accum.mul(ws[1], p[3]); + accum.mul(ws[2], p[2]); + accum.mul(ws[3], p[1]); + accum.add(z[4]); + ws[4] = accum.monty_step(p[0], p_dash); + accum.mul(ws[0], p[5]); + accum.mul(ws[1], p[4]); + accum.mul(ws[2], p[3]); + accum.mul(ws[3], p[2]); + accum.mul(ws[4], p[1]); + accum.add(z[5]); + ws[5] = accum.monty_step(p[0], p_dash); + accum.mul(ws[0], p[6]); + accum.mul(ws[1], p[5]); + accum.mul(ws[2], p[4]); + accum.mul(ws[3], p[3]); + accum.mul(ws[4], p[2]); + accum.mul(ws[5], p[1]); + accum.add(z[6]); + ws[6] = accum.monty_step(p[0], p_dash); + accum.mul(ws[0], p[7]); + accum.mul(ws[1], p[6]); + accum.mul(ws[2], p[5]); + accum.mul(ws[3], p[4]); + accum.mul(ws[4], p[3]); + accum.mul(ws[5], p[2]); + accum.mul(ws[6], p[1]); + accum.add(z[7]); + ws[7] = accum.monty_step(p[0], p_dash); + accum.mul(ws[0], p[8]); + accum.mul(ws[1], p[7]); + accum.mul(ws[2], p[6]); + accum.mul(ws[3], p[5]); + accum.mul(ws[4], p[4]); + accum.mul(ws[5], p[3]); + accum.mul(ws[6], p[2]); + accum.mul(ws[7], p[1]); + accum.add(z[8]); + ws[8] = accum.monty_step(p[0], p_dash); + accum.mul(ws[0], p[9]); + accum.mul(ws[1], p[8]); + accum.mul(ws[2], p[7]); + accum.mul(ws[3], p[6]); + accum.mul(ws[4], p[5]); + accum.mul(ws[5], p[4]); + accum.mul(ws[6], p[3]); + accum.mul(ws[7], p[2]); + accum.mul(ws[8], p[1]); + accum.add(z[9]); + ws[9] = accum.monty_step(p[0], p_dash); + accum.mul(ws[0], p[10]); + accum.mul(ws[1], p[9]); + accum.mul(ws[2], p[8]); + accum.mul(ws[3], p[7]); + accum.mul(ws[4], p[6]); + accum.mul(ws[5], p[5]); + accum.mul(ws[6], p[4]); + accum.mul(ws[7], p[3]); + accum.mul(ws[8], p[2]); + accum.mul(ws[9], p[1]); + accum.add(z[10]); + ws[10] = accum.monty_step(p[0], p_dash); + accum.mul(ws[0], p[11]); + accum.mul(ws[1], p[10]); + accum.mul(ws[2], p[9]); + accum.mul(ws[3], p[8]); + accum.mul(ws[4], p[7]); + accum.mul(ws[5], p[6]); + accum.mul(ws[6], p[5]); + accum.mul(ws[7], p[4]); + accum.mul(ws[8], p[3]); + accum.mul(ws[9], p[2]); + accum.mul(ws[10], p[1]); + accum.add(z[11]); + ws[11] = accum.monty_step(p[0], p_dash); + accum.mul(ws[1], p[11]); + accum.mul(ws[2], p[10]); + accum.mul(ws[3], p[9]); + accum.mul(ws[4], p[8]); + accum.mul(ws[5], p[7]); + accum.mul(ws[6], p[6]); + accum.mul(ws[7], p[5]); + accum.mul(ws[8], p[4]); + accum.mul(ws[9], p[3]); + accum.mul(ws[10], p[2]); + accum.mul(ws[11], p[1]); + accum.add(z[12]); + ws[0] = accum.extract(); + accum.mul(ws[2], p[11]); + accum.mul(ws[3], p[10]); + accum.mul(ws[4], p[9]); + accum.mul(ws[5], p[8]); + accum.mul(ws[6], p[7]); + accum.mul(ws[7], p[6]); + accum.mul(ws[8], p[5]); + accum.mul(ws[9], p[4]); + accum.mul(ws[10], p[3]); + accum.mul(ws[11], p[2]); + accum.add(z[13]); + ws[1] = accum.extract(); + accum.mul(ws[3], p[11]); + accum.mul(ws[4], p[10]); + accum.mul(ws[5], p[9]); + accum.mul(ws[6], p[8]); + accum.mul(ws[7], p[7]); + accum.mul(ws[8], p[6]); + accum.mul(ws[9], p[5]); + accum.mul(ws[10], p[4]); + accum.mul(ws[11], p[3]); + accum.add(z[14]); + ws[2] = accum.extract(); + accum.mul(ws[4], p[11]); + accum.mul(ws[5], p[10]); + accum.mul(ws[6], p[9]); + accum.mul(ws[7], p[8]); + accum.mul(ws[8], p[7]); + accum.mul(ws[9], p[6]); + accum.mul(ws[10], p[5]); + accum.mul(ws[11], p[4]); + accum.add(z[15]); + ws[3] = accum.extract(); + accum.mul(ws[5], p[11]); + accum.mul(ws[6], p[10]); + accum.mul(ws[7], p[9]); + accum.mul(ws[8], p[8]); + accum.mul(ws[9], p[7]); + accum.mul(ws[10], p[6]); + accum.mul(ws[11], p[5]); + accum.add(z[16]); + ws[4] = accum.extract(); + accum.mul(ws[6], p[11]); + accum.mul(ws[7], p[10]); + accum.mul(ws[8], p[9]); + accum.mul(ws[9], p[8]); + accum.mul(ws[10], p[7]); + accum.mul(ws[11], p[6]); + accum.add(z[17]); + ws[5] = accum.extract(); + accum.mul(ws[7], p[11]); + accum.mul(ws[8], p[10]); + accum.mul(ws[9], p[9]); + accum.mul(ws[10], p[8]); + accum.mul(ws[11], p[7]); + accum.add(z[18]); + ws[6] = accum.extract(); + accum.mul(ws[8], p[11]); + accum.mul(ws[9], p[10]); + accum.mul(ws[10], p[9]); + accum.mul(ws[11], p[8]); + accum.add(z[19]); + ws[7] = accum.extract(); + accum.mul(ws[9], p[11]); + accum.mul(ws[10], p[10]); + accum.mul(ws[11], p[9]); + accum.add(z[20]); + ws[8] = accum.extract(); + accum.mul(ws[10], p[11]); + accum.mul(ws[11], p[10]); + accum.add(z[21]); + ws[9] = accum.extract(); + accum.mul(ws[11], p[11]); + accum.add(z[22]); + ws[10] = accum.extract(); + accum.add(z[23]); + ws[11] = accum.extract(); + const word w1 = accum.extract(); + bigint_monty_maybe_sub<12>(r, w1, ws, p); } -void bigint_monty_redc_16(word z[32], const word p[16], word p_dash, word ws[]) { +void bigint_monty_redc_16(word r[16], const word z[32], const word p[16], word p_dash, word ws[16]) { word3 accum; accum.add(z[0]); ws[0] = accum.monty_step(p[0], p_dash); @@ -508,12 +689,11 @@ ws[14] = accum.extract(); accum.add(z[31]); ws[15] = accum.extract(); - word w1 = accum.extract(); - bigint_monty_maybe_sub<16>(z, w1, ws, p); - clear_mem(z + 16, 16); + const word w1 = accum.extract(); + bigint_monty_maybe_sub<16>(r, w1, ws, p); } -void bigint_monty_redc_24(word z[48], const word p[24], word p_dash, word ws[]) { +void bigint_monty_redc_24(word r[24], const word z[48], const word p[24], word p_dash, word ws[24]) { word3 accum; accum.add(z[0]); ws[0] = accum.monty_step(p[0], p_dash); @@ -1163,12 +1343,11 @@ ws[22] = accum.extract(); accum.add(z[47]); ws[23] = accum.extract(); - word w1 = accum.extract(); - bigint_monty_maybe_sub<24>(z, w1, ws, p); - clear_mem(z + 24, 24); + const word w1 = accum.extract(); + bigint_monty_maybe_sub<24>(r, w1, ws, p); } -void bigint_monty_redc_32(word z[64], const word p[32], word p_dash, word ws[]) { +void bigint_monty_redc_32(word r[32], const word z[64], const word p[32], word p_dash, word ws[32]) { word3 accum; accum.add(z[0]); ws[0] = accum.monty_step(p[0], p_dash); @@ -2290,9 +2469,8 @@ ws[30] = accum.extract(); accum.add(z[63]); ws[31] = accum.extract(); - word w1 = accum.extract(); - bigint_monty_maybe_sub<32>(z, w1, ws, p); - clear_mem(z + 32, 32); + const word w1 = accum.extract(); + bigint_monty_maybe_sub<32>(r, w1, ws, p); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/math/numbertheory/barrett.cpp botan3-3.12.0+dfsg/src/lib/math/numbertheory/barrett.cpp --- botan3-3.7.1+dfsg/src/lib/math/numbertheory/barrett.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/numbertheory/barrett.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,203 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include + +namespace Botan { + +Barrett_Reduction::Barrett_Reduction(const BigInt& m, BigInt mu, size_t mw) : + m_modulus(m), m_mu(std::move(mu)), m_mod_words(mw), m_modulus_bits(m.bits()) { + // Give some extra space for Karatsuba + m_modulus.grow_to(m_mod_words + 8); + m_mu.grow_to(m_mod_words + 8); +} + +Barrett_Reduction Barrett_Reduction::for_secret_modulus(const BigInt& mod) { + BOTAN_ARG_CHECK(mod.signum() > 0, "Modulus must be positive"); + + const size_t mod_words = mod.sig_words(); + + // Compute mu = floor(2^{2k} / m) + const size_t mu_bits = 2 * WordInfo::bits * mod_words; + return Barrett_Reduction(mod, ct_divide_pow2k(mu_bits, mod), mod_words); +} + +Barrett_Reduction Barrett_Reduction::for_public_modulus(const BigInt& mod) { + BOTAN_ARG_CHECK(mod.signum() > 0, "Modulus must be positive"); + + const size_t mod_words = mod.sig_words(); + + // Compute mu = floor(2^{2k} / m) + const size_t mu_bits = 2 * WordInfo::bits * mod_words; + return Barrett_Reduction(mod, vartime_divide_pow2k(mu_bits, mod), mod_words); +} + +namespace { + +/* +* Barrett Reduction +* +* This function assumes that the significant size of x_words (ie the number of +* words with a value other than zero) is at most 2 * mod_words. In any case, any +* larger value cannot be reduced using Barrett reduction; callers should have +* already checked for this. +*/ +BigInt barrett_reduce( + size_t mod_words, const BigInt& modulus, const BigInt& mu, std::span x_words, secure_vector& ws) { + BOTAN_ASSERT_NOMSG(modulus.sig_words() == mod_words); + + // Caller must expand input to be at least this size + BOTAN_ASSERT_NOMSG(x_words.size() >= 2 * mod_words); + + // Normally mod_words + 1 but can be + 2 if the modulus is a power of 2 + const size_t mu_words = mu.sig_words(); + BOTAN_ASSERT_NOMSG(mu_words <= mod_words + 2); + + if(ws.size() < 2 * (mod_words + 2)) { + ws.resize(2 * (mod_words + 2)); + } + + CT::poison(x_words); + + /* + * Following the notation of Handbook of Applied Cryptography + * Algorithm 14.42 "Barrett modular reduction", page 604 + * + * + * Using `mu` for μ in the code + */ + + // Compute q1 = floor(x / 2^(k - 1)) which is equivalent to ignoring the low (k-1) words + + // 2 * mod_words + 1 is sufficient, extra is to enable Karatsuba + secure_vector r(2 * mu_words + 2); + + copy_mem(r.data(), x_words.data() + (mod_words - 1), mod_words + 1); + + // Now compute q2 = q1 * μ + + // We allocate more size than required since this allows Karatsuba more often; + // just `mu_words + (mod_words + 1)` is sufficient + const size_t q2_size = 2 * mu_words + 2; + + secure_vector q2(q2_size); + + bigint_mul( + q2.data(), q2.size(), r.data(), r.size(), mod_words + 1, mu._data(), mu.size(), mu_words, ws.data(), ws.size()); + + // Compute r2 = (floor(q2 / b^(k+1)) * m) mod 2^(k+1) + // The division/floor is again effected by just ignoring the low k + 1 words + bigint_mul(r.data(), + r.size(), + &q2[mod_words + 1], // ignoring the low mod_words + 1 words of the first product + q2.size() - (mod_words + 1), + mod_words + 1, + modulus._data(), + modulus.size(), + mod_words, + ws.data(), + ws.size()); + + // Clear the high words of the product, equivalent to computing mod 2^(k+1) + // TODO add masked mul to avoid computing high bits at all + clear_mem(std::span{r}.subspan(mod_words + 1)); + + // Compute r = r1 - r2 + + // The return value of bigint_sub_abs isn't quite right for what we need here so first compare + const int32_t relative_size = bigint_cmp(r.data(), mod_words + 1, x_words.data(), mod_words + 1); + + bigint_sub_abs(r.data(), r.data(), x_words.data(), mod_words + 1, ws.data()); + + /* + If r is negative then we have to set r to r + 2^(k+1) + + However for r negative computing this sum is equivalent to computing 2^(k+1) - abs(r) + */ + clear_mem(ws.data(), mod_words + 2); + ws[mod_words + 1] = 1; + bigint_sub2(ws.data(), mod_words + 2, r.data(), mod_words + 2); + + // If relative_size > 0 then assign r to 2^(k+1) - r + CT::Mask::is_equal(static_cast(relative_size), 1).select_n(r.data(), ws.data(), r.data(), mod_words + 2); + + /* + * Per HAC Note 14.44 (ii) "step 4 is repeated at most twice since 0 ≤ r < 3m" + */ + const size_t bound = 2; + + BOTAN_ASSERT_NOMSG(r.size() >= mod_words + 1); + for(size_t i = 0; i != bound; ++i) { + const word borrow = bigint_sub3(ws.data(), r.data(), mod_words + 1, modulus._data(), mod_words); + CT::Mask::is_zero(borrow).select_n(r.data(), ws.data(), r.data(), mod_words + 1); + } + + CT::unpoison(q2); + CT::unpoison(r); + CT::unpoison(ws); + CT::unpoison(x_words); + + return BigInt::_from_words(r); +} + +CT::Choice acceptable_barrett_input(const BigInt& x, const BigInt& modulus) { + auto x_is_positive = CT::Choice::from_int(static_cast(x.signum() >= 0)); + auto x_lt_mod = bigint_ct_is_lt(x._data(), x.size(), modulus._data(), modulus.sig_words()).as_choice(); + return x_is_positive && x_lt_mod; +} + +} // namespace + +BigInt Barrett_Reduction::multiply(const BigInt& x, const BigInt& y) const { + BOTAN_ARG_CHECK(acceptable_barrett_input(x, m_modulus).as_bool(), "Invalid x param for Barrett multiply"); + BOTAN_ARG_CHECK(acceptable_barrett_input(y, m_modulus).as_bool(), "Invalid y param for Barrett multiply"); + + secure_vector ws(2 * (m_mod_words + 2)); + secure_vector xy(2 * m_mod_words); + + bigint_mul(xy.data(), + xy.size(), + x._data(), + x.size(), + std::min(x.size(), m_mod_words), + y._data(), + y.size(), + std::min(y.size(), m_mod_words), + ws.data(), + ws.size()); + + return barrett_reduce(m_mod_words, m_modulus, m_mu, xy, ws); +} + +BigInt Barrett_Reduction::square(const BigInt& x) const { + BOTAN_ARG_CHECK(acceptable_barrett_input(x, m_modulus).as_bool(), "Invalid x param for Barrett square"); + + secure_vector ws(2 * (m_mod_words + 2)); + secure_vector x2(2 * m_mod_words); + + bigint_sqr(x2.data(), x2.size(), x._data(), x.size(), std::min(x.size(), m_mod_words), ws.data(), ws.size()); + + return barrett_reduce(m_mod_words, m_modulus, m_mu, x2, ws); +} + +BigInt Barrett_Reduction::reduce(const BigInt& x) const { + BOTAN_ARG_CHECK(x.signum() >= 0, "Argument must be non-negative"); + + const size_t x_sw = x.sig_words(); + BOTAN_ARG_CHECK(x_sw <= 2 * m_mod_words, "Argument is too large for Barrett reduction"); + + x.grow_to(2 * m_mod_words); + + secure_vector ws; + return barrett_reduce(m_mod_words, m_modulus, m_mu, x._as_span(), ws); +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/math/numbertheory/barrett.h botan3-3.12.0+dfsg/src/lib/math/numbertheory/barrett.h --- botan3-3.7.1+dfsg/src/lib/math/numbertheory/barrett.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/numbertheory/barrett.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,84 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_BARRETT_REDUCTION_H_ +#define BOTAN_BARRETT_REDUCTION_H_ + +#include + +namespace Botan { + +/** +* Barrett Reduction +*/ +class BOTAN_TEST_API Barrett_Reduction final { + public: + /** + * Setup for reduction where the modulus itself is public + * + * Requires that m > 0 + */ + static Barrett_Reduction for_public_modulus(const BigInt& m); + + /** + * Setup for reduction where the modulus itself is secret. + * + * This is slower than for_public_modulus since it must avoid using + * variable time division. + * + * Requires that m > 0 + */ + static Barrett_Reduction for_secret_modulus(const BigInt& m); + + /** + * Perform modular reduction of x + * + * The parameter must be greater than or equal to zero, and less than 2^(2*b), where + * b is the bitlength of the modulus. + */ + BigInt reduce(const BigInt& x) const; + + /** + * Multiply mod p + * @param x the first operand in [0..p) + * @param y the second operand in [0..p) + * @return (x * y) % p + */ + BigInt multiply(const BigInt& x, const BigInt& y) const; + + /** + * Square mod p + * @param x a value to square must be in [0..p) + * @return (x * x) % p + */ + BigInt square(const BigInt& x) const; + + /** + * Cube mod p + * @param x the value to cube + * @return (x * x * x) % p + * + * TODO(Botan4) remove this, last few remaining callers go away in Botan4 + */ + BigInt cube(const BigInt& x) const { return this->multiply(x, this->square(x)); } + + /** + * Return length of the modulus in bits + */ + size_t modulus_bits() const { return m_modulus_bits; } + + private: + Barrett_Reduction(const BigInt& m, BigInt mu, size_t mw); + + BigInt m_modulus; + BigInt m_mu; + size_t m_mod_words; + size_t m_modulus_bits; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/math/numbertheory/dsa_gen.cpp botan3-3.12.0+dfsg/src/lib/math/numbertheory/dsa_gen.cpp --- botan3-3.7.1+dfsg/src/lib/math/numbertheory/dsa_gen.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/numbertheory/dsa_gen.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,9 +8,9 @@ #include #include +#include #include #include -#include #include #include @@ -80,7 +80,8 @@ Seed& operator++() { for(size_t j = m_seed.size(); j > 0; --j) { - if(++m_seed[j - 1]) { + m_seed[j - 1] += 1; + if(m_seed[j - 1] != 0) { break; } } @@ -101,12 +102,13 @@ return false; } - const size_t n = (pbits - 1) / (HASH_SIZE * 8), b = (pbits - 1) % (HASH_SIZE * 8); + const size_t n = (pbits - 1) / (HASH_SIZE * 8); + const size_t b = (pbits - 1) % (HASH_SIZE * 8); BigInt X; std::vector V(HASH_SIZE * (n + 1)); - auto mod_2q = Modular_Reducer::for_public_modulus(2 * q); + const BigInt q2 = 2 * q; for(size_t j = 0; j != 4 * pbits; ++j) { for(size_t k = 0; k <= n; ++k) { @@ -119,7 +121,8 @@ X._assign_from_bytes(std::span{V}.subspan(HASH_SIZE - 1 - b / 8)); X.set_bit(pbits - 1); - p = X - (mod_2q.reduce(X) - 1); + // Variable time division is OK here since DSA primes are public anyway + p = X - ((X % q2) - 1); if(p.bits() == pbits && is_prime(p, rng, 128, true)) { return true; diff -Nru botan3-3.7.1+dfsg/src/lib/math/numbertheory/info.txt botan3-3.12.0+dfsg/src/lib/math/numbertheory/info.txt --- botan3-3.7.1+dfsg/src/lib/math/numbertheory/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/numbertheory/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -13,6 +13,7 @@ +barrett.h mod_inv.h monty.h monty_exp.h diff -Nru botan3-3.7.1+dfsg/src/lib/math/numbertheory/make_prm.cpp botan3-3.12.0+dfsg/src/lib/math/numbertheory/make_prm.cpp --- botan3-3.7.1+dfsg/src/lib/math/numbertheory/make_prm.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/numbertheory/make_prm.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,13 +7,15 @@ #include +#include #include -#include #include +#include #include #include +#include #include -#include +#include namespace Botan { @@ -24,7 +26,7 @@ Prime_Sieve(const BigInt& init_value, size_t sieve_size, word step, bool check_2p1) : m_sieve(std::min(sieve_size, PRIME_TABLE_SIZE)), m_step(step), m_check_2p1(check_2p1) { for(size_t i = 0; i != m_sieve.size(); ++i) { - m_sieve[i] = init_value % PRIMES[i]; + m_sieve[i] = ct_mod_word(init_value, PRIMES[i]); } } @@ -35,7 +37,7 @@ bool next() { auto passes = CT::Mask::set(); for(size_t i = 0; i != m_sieve.size(); ++i) { - m_sieve[i] = (m_sieve[i] + m_step) % PRIMES[i]; + m_sieve[i] = sieve_step_incr(m_sieve[i], m_step, PRIMES[i]); // If m_sieve[i] == 0 then val % p == 0 -> not prime passes &= CT::Mask::expand(m_sieve[i]); @@ -58,6 +60,19 @@ } private: + // Return (v + step) % mod + // + // This assumes v is already < mod, which is an invariant of the sieve + static constexpr word sieve_step_incr(word v, word step, word mod) { + BOTAN_DEBUG_ASSERT(v < mod); + // The sieve step and primes are public so this modulo is ok + const word stepmod = (step >= mod) ? (step % mod) : step; + + // This sum is at most 2*(mod-1) + const word next = (v + stepmod); + return next - CT::Mask::is_gte(next, mod).if_set_return(mod); + } + std::vector m_sieve; const word m_step; const bool m_check_2p1; @@ -99,9 +114,10 @@ if(bits <= 1) { throw Invalid_Argument("random_prime: Can't make a prime of " + std::to_string(bits) + " bits"); } - if(coprime.is_negative() || (!coprime.is_zero() && coprime.is_even()) || coprime.bits() >= bits) { + if(coprime.signum() < 0 || (coprime.signum() != 0 && coprime.is_even()) || coprime.bits() >= bits) { throw Invalid_Argument("random_prime: invalid coprime"); } + // TODO(Botan4) reduce this to ~1000 if(modulo == 0 || modulo >= 100000) { throw Invalid_Argument("random_prime: Invalid modulo value"); } @@ -120,11 +136,11 @@ } if(bits == 2) { - return BigInt::from_word(((rng.next_byte() % 2) ? 2 : 3)); + return BigInt::from_word(((rng.next_byte() % 2) == 0 ? 2 : 3)); } else if(bits == 3) { - return BigInt::from_word(((rng.next_byte() % 2) ? 5 : 7)); + return BigInt::from_word(((rng.next_byte() % 2) == 0 ? 5 : 7)); } else if(bits == 4) { - return BigInt::from_word(((rng.next_byte() % 2) ? 11 : 13)); + return BigInt::from_word(((rng.next_byte() % 2) == 0 ? 11 : 13)); } else { for(;;) { // This is slightly biased, but for small primes it does not seem to matter @@ -171,14 +187,15 @@ BOTAN_DEBUG_ASSERT(no_small_multiples(p, sieve)); - auto mod_p = Modular_Reducer::for_secret_modulus(p); + auto mod_p = Barrett_Reduction::for_secret_modulus(p); + const Montgomery_Params monty_p(p, mod_p); if(coprime > 1) { /* - First do a single M-R iteration to quickly elimate most non-primes, + First do a single M-R iteration to quickly eliminate most non-primes, before doing the coprimality check which is expensive */ - if(is_miller_rabin_probable_prime(p, mod_p, rng, 1) == false) { + if(!is_miller_rabin_probable_prime(p, mod_p, monty_p, rng, 1)) { continue; } @@ -195,7 +212,7 @@ break; } - if(is_miller_rabin_probable_prime(p, mod_p, rng, mr_trials) == false) { + if(!is_miller_rabin_probable_prime(p, mod_p, monty_p, rng, mr_trials)) { continue; } @@ -233,6 +250,8 @@ while(true) { BigInt p(keygen_rng, bits); + auto scope = CT::scoped_poison(p); + /* Force high two bits so multiplication always results in expected n bit integer @@ -259,14 +278,15 @@ BOTAN_DEBUG_ASSERT(no_small_multiples(p, sieve)); - auto mod_p = Modular_Reducer::for_secret_modulus(p); + auto mod_p = Barrett_Reduction::for_secret_modulus(p); + const Montgomery_Params monty_p(p, mod_p); /* * Do a single primality test first before checking coprimality, since * currently a single Miller-Rabin test is faster than computing gcd, * and this eliminates almost all wasted gcd computations. */ - if(is_miller_rabin_probable_prime(p, mod_p, prime_test_rng, 1) == false) { + if(!is_miller_rabin_probable_prime(p, mod_p, monty_p, prime_test_rng, 1)) { continue; } @@ -281,7 +301,7 @@ break; } - if(is_miller_rabin_probable_prime(p, mod_p, prime_test_rng, mr_trials) == true) { + if(is_miller_rabin_probable_prime(p, mod_p, monty_p, prime_test_rng, mr_trials)) { return p; } } @@ -298,7 +318,8 @@ const size_t error_bound = 128; - BigInt q, p; + BigInt q; + BigInt p; for(;;) { /* Generate q == 2 (mod 3), since otherwise [in the case of q == 1 (mod 3)], diff -Nru botan3-3.7.1+dfsg/src/lib/math/numbertheory/mod_inv.cpp botan3-3.12.0+dfsg/src/lib/math/numbertheory/mod_inv.cpp --- botan3-3.7.1+dfsg/src/lib/math/numbertheory/mod_inv.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/numbertheory/mod_inv.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,6 +6,8 @@ #include +#include +#include #include #include #include @@ -18,8 +20,8 @@ BigInt inverse_mod_odd_modulus(const BigInt& n, const BigInt& mod) { // Caller should assure these preconditions: - BOTAN_ASSERT_NOMSG(n.is_positive()); - BOTAN_ASSERT_NOMSG(mod.is_positive()); + BOTAN_ASSERT_NOMSG(n.signum() >= 0); + BOTAN_ASSERT_NOMSG(mod.signum() > 0); BOTAN_ASSERT_NOMSG(n < mod); BOTAN_ASSERT_NOMSG(mod >= 3 && mod.is_odd()); @@ -47,14 +49,12 @@ secure_vector tmp_mem(5 * mod_words); - word* v_w = &tmp_mem[0]; + word* v_w = &tmp_mem[0]; // NOLINT(readability-container-data-pointer) word* u_w = &tmp_mem[1 * mod_words]; word* b_w = &tmp_mem[2 * mod_words]; word* a_w = &tmp_mem[3 * mod_words]; word* mp1o2 = &tmp_mem[4 * mod_words]; - CT::poison(tmp_mem.data(), tmp_mem.size()); - copy_mem(a_w, n._data(), std::min(n.size(), mod_words)); copy_mem(b_w, mod._data(), std::min(mod.size(), mod_words)); u_w[0] = 1; @@ -64,9 +64,11 @@ // (mod / 2) + 1 copy_mem(mp1o2, mod._data(), std::min(mod.size(), mod_words)); bigint_shr1(mp1o2, mod_words, 1); - word carry = bigint_add2_nc(mp1o2, mod_words, u_w, 1); + const word carry = bigint_add2(mp1o2, mod_words, u_w, 1); BOTAN_ASSERT_NOMSG(carry == 0); + CT::poison(tmp_mem.data(), tmp_mem.size()); + // Only n.bits() + mod.bits() iterations are required, but avoid leaking the size of n const size_t execs = 2 * mod.bits(); @@ -74,7 +76,7 @@ const word odd_a = a_w[0] & 1; //if(odd_a) a -= b - word underflow = bigint_cnd_sub(odd_a, a_w, b_w, mod_words); + const word underflow = bigint_cnd_sub(odd_a, a_w, b_w, mod_words); //if(underflow) { b -= a; a = abs(a); swap(u, v); } bigint_cnd_add(underflow, b_w, a_w, mod_words); @@ -85,7 +87,7 @@ bigint_shr1(a_w, mod_words, 1); //if(odd_a) u -= v; - word borrow = bigint_cnd_sub(odd_a, u_w, v_w, mod_words); + const word borrow = bigint_cnd_sub(odd_a, u_w, v_w, mod_words); // if(borrow) u += p bigint_cnd_add(borrow, u_w, mod._data(), mod_words); @@ -148,7 +150,7 @@ const size_t a_words = a.sig_words(); - X.grow_to(round_up(k, BOTAN_MP_WORD_BITS) / BOTAN_MP_WORD_BITS); + X.grow_to(round_up(k, WordInfo::bits) / WordInfo::bits); b.grow_to(a_words); /* @@ -156,7 +158,7 @@ granularity because of the length of a, so no point in doing more than this. */ - const size_t iter = round_up(k, BOTAN_MP_WORD_BITS); + const size_t iter = round_up(k, WordInfo::bits); for(size_t i = 0; i != iter; ++i) { const bool b0 = b.get_bit(0); @@ -261,14 +263,14 @@ const BigInt c = inverse_mod_pow2(o, mod_lz); // This should never happen; o is odd so gcd is 1 and inverse mod 2^k exists - BOTAN_ASSERT_NOMSG(!c.is_zero()); + BOTAN_ASSERT_NOMSG(c.signum() != 0); // Compute h = c*(inv_2k-inv_o) mod 2^k BigInt h = c * (inv_2k - inv_o); - const bool h_neg = h.is_negative(); + const bool h_neg = h.signum() < 0; h.set_sign(BigInt::Positive); h.mask_bits(mod_lz); - const bool h_nonzero = h.is_nonzero(); + const bool h_nonzero = h.signum() != 0; h.ct_cond_assign(h_nonzero && h_neg, m2k - h); // Return result inv_o + h * o @@ -278,9 +280,10 @@ } BigInt inverse_mod_secret_prime(const BigInt& x, const BigInt& p) { - BOTAN_ARG_CHECK(x.is_positive() && p.is_positive(), "Parameters must be positive"); - BOTAN_ARG_CHECK(x < p, "x must be less than p"); - BOTAN_ARG_CHECK(p.is_odd() and p > 1, "Primes are odd integers greater than 1"); + BOTAN_ARG_CHECK(p.signum() > 0, "Modulus must be positive"); + BOTAN_ARG_CHECK(x.signum() > 0, "Input must be positive"); + BOTAN_ARG_CHECK(x < p, "Input must be less than modulus"); + BOTAN_ARG_CHECK(p.is_odd() && p > 1, "Primes are odd integers greater than 1"); // TODO possibly use FLT, or the algorithm presented for this case in // Handbook of Elliptic and Hyperelliptic Curve Cryptography @@ -289,14 +292,22 @@ } BigInt inverse_mod_public_prime(const BigInt& x, const BigInt& p) { - return inverse_mod_secret_prime(x, p); + BOTAN_ARG_CHECK(p.signum() > 0, "Modulus must be positive"); + BOTAN_ARG_CHECK(x.signum() > 0, "Input must be positive"); + BOTAN_ARG_CHECK(x < p, "Input must be less than modulus"); + BOTAN_ARG_CHECK(p.is_odd() && p > 1, "Primes are odd integers greater than 1"); + + // TODO possibly use FLT, or the algorithm presented for this case in + // Handbook of Elliptic and Hyperelliptic Curve Cryptography + + return inverse_mod_odd_modulus(x, p); } BigInt inverse_mod_rsa_public_modulus(const BigInt& x, const BigInt& n) { - BOTAN_ARG_CHECK(n.is_positive() && n.is_odd(), "RSA public modulus must be odd and positive"); - BOTAN_ARG_CHECK(x.is_positive() && x < n, "Input must be positive and less than RSA modulus"); + BOTAN_ARG_CHECK(n.signum() > 0 && n.is_odd(), "RSA public modulus must be odd and positive"); + BOTAN_ARG_CHECK(x.signum() > 0 && x < n, "Input must be positive and less than RSA modulus"); BigInt z = inverse_mod_odd_modulus(x, n); - BOTAN_ASSERT(!z.is_zero(), "Accidentally factored the public modulus"); // whoops + BOTAN_ASSERT(z.signum() != 0, "Accidentally factored the public modulus"); // whoops return z; } @@ -307,8 +318,8 @@ constexpr size_t s = 32; constexpr uint64_t c = (static_cast(1) << s) / mod; - uint64_t q = (x * c) >> s; - uint64_t r = x - q * mod; + const uint64_t q = (x * c) >> s; + const uint64_t r = x - q * mod; auto r_gt_mod = CT::Mask::is_gte(r, mod); return r - r_gt_mod.if_set_return(mod); @@ -367,9 +378,8 @@ } BigInt inverse_mod(const BigInt& n, const BigInt& mod) { - BOTAN_ARG_CHECK(!mod.is_zero(), "modulus cannot be zero"); - BOTAN_ARG_CHECK(!mod.is_negative(), "modulus cannot be negative"); - BOTAN_ARG_CHECK(!n.is_negative(), "value cannot be negative"); + BOTAN_ARG_CHECK(mod.signum() > 0, "Modulus must be positive"); + BOTAN_ARG_CHECK(n.signum() >= 0, "Value cannot be negative"); if(n.is_zero() || (n.is_even() && mod.is_even())) { return BigInt::zero(); diff -Nru botan3-3.7.1+dfsg/src/lib/math/numbertheory/mod_inv.h botan3-3.12.0+dfsg/src/lib/math/numbertheory/mod_inv.h --- botan3-3.7.1+dfsg/src/lib/math/numbertheory/mod_inv.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/numbertheory/mod_inv.h 2026-05-07 01:38:28.000000000 +0000 @@ -90,7 +90,7 @@ * * This always returns a result since any integer in [1,n) has an inverse modulo * a RSA public modulus n, unless you have happened to guess one of the factors -* at random. In the unlikely event of this occuring, Internal_Error will be thrown. +* at random. In the unlikely event of this occurring, Internal_Error will be thrown. */ BigInt inverse_mod_rsa_public_modulus(const BigInt& x, const BigInt& n); diff -Nru botan3-3.7.1+dfsg/src/lib/math/numbertheory/monty.cpp botan3-3.12.0+dfsg/src/lib/math/numbertheory/monty.cpp --- botan3-3.7.1+dfsg/src/lib/math/numbertheory/monty.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/numbertheory/monty.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,20 +1,28 @@ /* -* (C) 2018,2024 Jack Lloyd +* (C) 2018,2024,2025 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include -#include -#include +#include +#include +#include #include - -#include +#include namespace Botan { -Montgomery_Params::Montgomery_Params(const BigInt& p, const Modular_Reducer& mod_p) { +namespace { + +// If the modulus is at most this many words, then use the stack instead +// of a heap variable for some temporary values +constexpr size_t MontgomeryUseStackLimit = 32; + +} // namespace + +Montgomery_Params::Data::Data(const BigInt& p, const Barrett_Reduction& mod_p) { if(p.is_even() || p < 3) { throw Invalid_Argument("Montgomery_Params invalid modulus"); } @@ -23,192 +31,147 @@ m_p_words = m_p.sig_words(); m_p_dash = monty_inverse(m_p.word_at(0)); - const BigInt r = BigInt::power_of_2(m_p_words * BOTAN_MP_WORD_BITS); + const BigInt r = BigInt::power_of_2(m_p_words * WordInfo::bits); m_r1 = mod_p.reduce(r); m_r2 = mod_p.square(m_r1); m_r3 = mod_p.multiply(m_r1, m_r2); + + // Barrett should be at least zero prefixing up to modulus size + BOTAN_ASSERT_NOMSG(m_r1.size() >= m_p_words); + BOTAN_ASSERT_NOMSG(m_r2.size() >= m_p_words); + BOTAN_ASSERT_NOMSG(m_r3.size() >= m_p_words); } -Montgomery_Params::Montgomery_Params(const BigInt& p) { - if(p.is_even() || p < 3) { - throw Invalid_Argument("Montgomery_Params invalid modulus"); - } +Montgomery_Params::Montgomery_Params(const BigInt& p, const Barrett_Reduction& mod_p) : + m_data(std::make_shared(p, mod_p)) {} - m_p = p; - m_p_words = m_p.sig_words(); - m_p_dash = monty_inverse(m_p.word_at(0)); +Montgomery_Params::Montgomery_Params(const BigInt& p) : + Montgomery_Params(p, Barrett_Reduction::for_secret_modulus(p)) {} - const BigInt r = BigInt::power_of_2(m_p_words * BOTAN_MP_WORD_BITS); - - auto mod_p = Modular_Reducer::for_secret_modulus(p); +bool Montgomery_Params::operator==(const Montgomery_Params& other) const { + if(this->m_data == other.m_data) { + return true; + } - m_r1 = mod_p.reduce(r); - m_r2 = mod_p.square(m_r1); - m_r3 = mod_p.multiply(m_r1, m_r2); + return (this->m_data->p() == other.m_data->p()); } BigInt Montgomery_Params::redc(const BigInt& x, secure_vector& ws) const { - const size_t output_size = m_p_words + 1; + const size_t p_size = this->p_words(); - if(ws.size() < output_size) { - ws.resize(output_size); + if(ws.size() < p_size) { + ws.resize(p_size); } BigInt z = x; - z.grow_to(2 * m_p_words); + z.grow_to(2 * p_size); - bigint_monty_redc(z.mutable_data(), m_p._data(), m_p_words, m_p_dash, ws.data(), ws.size()); + bigint_monty_redc_inplace(z.mutable_data(), this->p()._data(), p_size, this->p_dash(), ws.data(), ws.size()); return z; } -void Montgomery_Params::redc_in_place(BigInt& x, secure_vector& ws) const { - const size_t output_size = 2 * m_p_words; - - if(ws.size() < output_size) { - ws.resize(output_size); - } - - x.grow_to(output_size); - - bigint_monty_redc(x.mutable_data(), m_p._data(), m_p_words, m_p_dash, ws.data(), ws.size()); -} - BigInt Montgomery_Params::mul(const BigInt& x, const BigInt& y, secure_vector& ws) const { - BigInt z = BigInt::with_capacity(2 * m_p_words); + const size_t p_size = this->p_words(); + BigInt z = BigInt::with_capacity(2 * p_size); this->mul(z, x, y, ws); return z; } void Montgomery_Params::mul(BigInt& z, const BigInt& x, const BigInt& y, secure_vector& ws) const { - const size_t output_size = 2 * m_p_words; + const size_t p_size = this->p_words(); - if(ws.size() < output_size) { - ws.resize(output_size); + if(ws.size() < 2 * p_size) { + ws.resize(2 * p_size); } - BOTAN_DEBUG_ASSERT(x.sig_words() <= m_p_words); - BOTAN_DEBUG_ASSERT(y.sig_words() <= m_p_words); + BOTAN_DEBUG_ASSERT(x.sig_words() <= p_size); + BOTAN_DEBUG_ASSERT(y.sig_words() <= p_size); - if(z.size() < output_size) { - z.grow_to(output_size); + if(z.size() < 2 * p_size) { + z.grow_to(2 * p_size); } bigint_mul(z.mutable_data(), z.size(), x._data(), x.size(), - std::min(m_p_words, x.size()), + std::min(p_size, x.size()), y._data(), y.size(), - std::min(m_p_words, y.size()), + std::min(p_size, y.size()), ws.data(), ws.size()); - bigint_monty_redc(z.mutable_data(), m_p._data(), m_p_words, m_p_dash, ws.data(), ws.size()); -} - -BigInt Montgomery_Params::mul(const BigInt& x, std::span y, secure_vector& ws) const { - BigInt z = BigInt::with_capacity(2 * m_p_words); - this->mul(z, x, y, ws); - return z; + bigint_monty_redc_inplace(z.mutable_data(), this->p()._data(), p_size, this->p_dash(), ws.data(), ws.size()); } void Montgomery_Params::mul(BigInt& z, const BigInt& x, std::span y, secure_vector& ws) const { - const size_t output_size = 2 * m_p_words; - if(ws.size() < output_size) { - ws.resize(output_size); + const size_t p_size = this->p_words(); + + if(ws.size() < 2 * p_size) { + ws.resize(2 * p_size); } - if(z.size() < output_size) { - z.grow_to(output_size); + if(z.size() < 2 * p_size) { + z.grow_to(2 * p_size); } - BOTAN_DEBUG_ASSERT(x.sig_words() <= m_p_words); + BOTAN_DEBUG_ASSERT(x.sig_words() <= p_size); bigint_mul(z.mutable_data(), z.size(), x._data(), x.size(), - std::min(m_p_words, x.size()), + std::min(p_size, x.size()), y.data(), y.size(), - std::min(m_p_words, y.size()), + std::min(p_size, y.size()), ws.data(), ws.size()); - bigint_monty_redc(z.mutable_data(), m_p._data(), m_p_words, m_p_dash, ws.data(), ws.size()); -} - -void Montgomery_Params::mul_by(BigInt& x, std::span y, secure_vector& ws) const { - const size_t output_size = 2 * m_p_words; - - if(ws.size() < 2 * output_size) { - ws.resize(2 * output_size); - } - - word* z_data = &ws[0]; - word* ws_data = &ws[output_size]; - - BOTAN_DEBUG_ASSERT(x.sig_words() <= m_p_words); - - bigint_mul(z_data, - output_size, - x._data(), - x.size(), - std::min(m_p_words, x.size()), - y.data(), - y.size(), - std::min(m_p_words, y.size()), - ws_data, - output_size); - - bigint_monty_redc(z_data, m_p._data(), m_p_words, m_p_dash, ws_data, output_size); - - if(x.size() < output_size) { - x.grow_to(output_size); - } - copy_mem(x.mutable_data(), z_data, output_size); + bigint_monty_redc_inplace(z.mutable_data(), this->p()._data(), p_size, this->p_dash(), ws.data(), ws.size()); } void Montgomery_Params::mul_by(BigInt& x, const BigInt& y, secure_vector& ws) const { - const size_t output_size = 2 * m_p_words; + const size_t p_size = this->p_words(); - if(ws.size() < 2 * output_size) { - ws.resize(2 * output_size); + if(ws.size() < 4 * p_size) { + ws.resize(4 * p_size); } - word* z_data = &ws[0]; - word* ws_data = &ws[output_size]; + word* z_data = ws.data(); + word* ws_data = &ws[2 * p_size]; - BOTAN_DEBUG_ASSERT(x.sig_words() <= m_p_words); + BOTAN_DEBUG_ASSERT(x.sig_words() <= p_size); bigint_mul(z_data, - output_size, + 2 * p_size, x._data(), x.size(), - std::min(m_p_words, x.size()), + std::min(p_size, x.size()), y._data(), y.size(), - std::min(m_p_words, y.size()), + std::min(p_size, y.size()), ws_data, - output_size); + 2 * p_size); - bigint_monty_redc(z_data, m_p._data(), m_p_words, m_p_dash, ws_data, output_size); + bigint_monty_redc_inplace(z_data, this->p()._data(), p_size, this->p_dash(), ws_data, 2 * p_size); - if(x.size() < output_size) { - x.grow_to(output_size); + if(x.size() < 2 * p_size) { + x.grow_to(2 * p_size); } - copy_mem(x.mutable_data(), z_data, output_size); + copy_mem(x.mutable_data(), z_data, 2 * p_size); } BigInt Montgomery_Params::sqr(const BigInt& x, secure_vector& ws) const { - BOTAN_DEBUG_ASSERT(x.sig_words() <= m_p_words); + BOTAN_DEBUG_ASSERT(x.sig_words() <= this->p_words()); return this->sqr(std::span{x._data(), x.size()}, ws); } BigInt Montgomery_Params::sqr(std::span x, secure_vector& ws) const { - BigInt z = BigInt::with_capacity(2 * m_p_words); + const size_t p_size = this->p_words(); + BigInt z = BigInt::with_capacity(2 * p_size); this->sqr(z, x, ws); return z; } @@ -218,240 +181,200 @@ } void Montgomery_Params::sqr(BigInt& z, std::span x, secure_vector& ws) const { - const size_t output_size = 2 * m_p_words; + const size_t p_size = this->p_words(); - if(ws.size() < output_size) { - ws.resize(output_size); + if(ws.size() < 2 * p_size) { + ws.resize(2 * p_size); } - if(z.size() < output_size) { - z.grow_to(output_size); + if(z.size() < 2 * p_size) { + z.grow_to(2 * p_size); } - bigint_sqr(z.mutable_data(), z.size(), x.data(), x.size(), std::min(m_p_words, x.size()), ws.data(), ws.size()); + bigint_sqr(z.mutable_data(), z.size(), x.data(), x.size(), std::min(p_size, x.size()), ws.data(), ws.size()); - bigint_monty_redc(z.mutable_data(), m_p._data(), m_p_words, m_p_dash, ws.data(), ws.size()); + bigint_monty_redc_inplace(z.mutable_data(), this->p()._data(), p_size, this->p_dash(), ws.data(), ws.size()); } -void Montgomery_Params::square_this(BigInt& x, secure_vector& ws) const { - const size_t output_size = 2 * m_p_words; - - if(ws.size() < 2 * output_size) { - ws.resize(2 * output_size); - } - - word* z_data = &ws[0]; - word* ws_data = &ws[output_size]; - - BOTAN_DEBUG_ASSERT(x.sig_words() <= m_p_words); - - bigint_sqr(z_data, output_size, x._data(), x.size(), std::min(m_p_words, x.size()), ws_data, output_size); - - bigint_monty_redc(z_data, m_p._data(), m_p_words, m_p_dash, ws_data, output_size); - - if(x.size() < output_size) { - x.grow_to(output_size); - } - copy_mem(x.mutable_data(), z_data, output_size); +Montgomery_Int::Montgomery_Int(const Montgomery_Params& params, secure_vector words) : + m_params(params), m_v(std::move(words)) { + BOTAN_ASSERT_NOMSG(m_v.size() == m_params.p_words()); } -Montgomery_Int Montgomery_Int::one(const std::shared_ptr& params) { - return Montgomery_Int(params, params->R1(), false); +Montgomery_Int Montgomery_Int::one(const Montgomery_Params& params) { + return Montgomery_Int(params, params.R1(), false); } -Montgomery_Int Montgomery_Int::from_wide_int(const std::shared_ptr& params, const BigInt& x) { - //BOTAN_ARG_CHECK(x < params->p() * params->p(), "Input too large"); - +Montgomery_Int Montgomery_Int::from_wide_int(const Montgomery_Params& params, const BigInt& x) { secure_vector ws; - auto redc_x = params->mul(params->redc(x, ws), params->R3(), ws); + auto redc_x = params.mul(params.redc(x, ws), params.R3(), ws); return Montgomery_Int(params, redc_x, false); } -Montgomery_Int::Montgomery_Int(const std::shared_ptr& params, - const BigInt& v, - bool redc_needed) : - m_params(params) { - if(redc_needed == false) { - m_v = v; - } else { - BOTAN_ASSERT_NOMSG(m_v < m_params->p()); - secure_vector ws; - m_v = m_params->mul(v, m_params->R2(), ws); - } -} +Montgomery_Int::Montgomery_Int(const Montgomery_Params& params, const BigInt& v, bool redc_needed) : + m_params(params), m_v(m_params.p_words()) { + BOTAN_ASSERT_NOMSG(v < m_params.p()); -Montgomery_Int::Montgomery_Int(const std::shared_ptr& params, - const uint8_t bits[], - size_t len, - bool redc_needed) : - m_params(params), m_v(bits, len) { - if(redc_needed) { - BOTAN_ASSERT_NOMSG(m_v < m_params->p()); - secure_vector ws; - m_v = m_params->mul(m_v, m_params->R2(), ws); + const size_t p_size = m_params.p_words(); + + auto v_span = v._as_span(); + + if(v_span.size() > p_size) { + // Safe to truncate the span since we already checked v < p + v_span = v_span.first(p_size); } -} -Montgomery_Int::Montgomery_Int(std::shared_ptr params, - const word words[], - size_t len, - bool redc_needed) : - m_params(std::move(params)) { - m_v.set_words(words, len); + BOTAN_ASSERT_NOMSG(m_v.size() >= v_span.size()); + + copy_mem(std::span{m_v}.first(v_span.size()), v_span); if(redc_needed) { - BOTAN_ASSERT_NOMSG(m_v < m_params->p()); secure_vector ws; - m_v = m_params->mul(m_v, m_params->R2(), ws); + this->mul_by(m_params.R2()._as_span().first(p_size), ws); } } -void Montgomery_Int::fix_size() { - const size_t p_words = m_params->p_words(); - BOTAN_DEBUG_ASSERT(m_v.sig_words() <= p_words); - m_v.grow_to(p_words); -} - -bool Montgomery_Int::operator==(const Montgomery_Int& other) const { - return m_v == other.m_v && m_params->p() == other.m_params->p(); +Montgomery_Int::Montgomery_Int(const Montgomery_Params& params, std::span words) : + m_params(params), m_v(words.begin(), words.end()) { + BOTAN_ARG_CHECK(m_v.size() == m_params.p_words(), "Invalid input span"); } std::vector Montgomery_Int::serialize() const { return value().serialize(); } -size_t Montgomery_Int::size() const { - return m_params->p().bytes(); -} +BigInt Montgomery_Int::value() const { + secure_vector ws(m_params.p_words()); -bool Montgomery_Int::is_one() const { - return m_v == m_params->R1(); -} + secure_vector z = m_v; + z.resize(2 * m_params.p_words()); // zero extend -bool Montgomery_Int::is_zero() const { - return m_v.is_zero(); -} + bigint_monty_redc_inplace( + z.data(), m_params.p()._data(), m_params.p_words(), m_params.p_dash(), ws.data(), ws.size()); -BigInt Montgomery_Int::value() const { - secure_vector ws; - return m_params->redc(m_v, ws); + return BigInt::_from_words(z); } Montgomery_Int Montgomery_Int::operator+(const Montgomery_Int& other) const { BOTAN_STATE_CHECK(other.m_params == m_params); - secure_vector ws; - BigInt z = m_v; - z.mod_add(other.m_v, m_params->p(), ws); - return Montgomery_Int(m_params, z, false); -} -Montgomery_Int Montgomery_Int::operator-(const Montgomery_Int& other) const { - BOTAN_STATE_CHECK(other.m_params == m_params); - secure_vector ws; - BigInt z = m_v; - z.mod_sub(other.m_v, m_params->p(), ws); - return Montgomery_Int(m_params, z, false); -} + const size_t p_size = m_params.p_words(); + BOTAN_ASSERT_NOMSG(m_v.size() == p_size && other.m_v.size() == p_size); -Montgomery_Int& Montgomery_Int::operator+=(const Montgomery_Int& other) { - BOTAN_STATE_CHECK(other.m_params == m_params); - secure_vector ws; - return this->add(other, ws); -} + secure_vector z(2 * p_size); -Montgomery_Int& Montgomery_Int::add(const Montgomery_Int& other, secure_vector& ws) { - BOTAN_STATE_CHECK(other.m_params == m_params); - m_v.mod_add(other.m_v, m_params->p(), ws); - return (*this); -} + word* r = std::span{z}.first(p_size).data(); + word* t = std::span{z}.last(p_size).data(); -Montgomery_Int& Montgomery_Int::operator-=(const Montgomery_Int& other) { - BOTAN_STATE_CHECK(other.m_params == m_params); - secure_vector ws; - return this->sub(other, ws); -} + // t = this + other + const word carry = bigint_add3(t, m_v.data(), p_size, other.m_v.data(), p_size); -Montgomery_Int& Montgomery_Int::sub(const Montgomery_Int& other, secure_vector& ws) { - BOTAN_STATE_CHECK(other.m_params == m_params); - m_v.mod_sub(other.m_v, m_params->p(), ws); - return (*this); + // Conditionally subtract r = t - p + bigint_monty_maybe_sub(p_size, r, carry, t, m_params.p()._data()); + + z.resize(p_size); // truncate leaving only r + return Montgomery_Int(m_params, std::move(z)); } -Montgomery_Int Montgomery_Int::operator*(const Montgomery_Int& other) const { +Montgomery_Int Montgomery_Int::operator-(const Montgomery_Int& other) const { BOTAN_STATE_CHECK(other.m_params == m_params); - secure_vector ws; - return Montgomery_Int(m_params, m_params->mul(m_v, other.m_v, ws), false); + + const size_t p_size = m_params.p_words(); + BOTAN_ASSERT_NOMSG(m_v.size() == p_size && other.m_v.size() == p_size); + + secure_vector t(p_size); + const word borrow = bigint_sub3(t.data(), m_v.data(), p_size, other.m_v.data(), p_size); + + bigint_cnd_add(borrow, t.data(), m_params.p()._data(), p_size); + + return Montgomery_Int(m_params, std::move(t)); } Montgomery_Int Montgomery_Int::mul(const Montgomery_Int& other, secure_vector& ws) const { BOTAN_STATE_CHECK(other.m_params == m_params); - return Montgomery_Int(m_params, m_params->mul(m_v, other.m_v, ws), false); + + const size_t p_size = m_params.p_words(); + BOTAN_ASSERT_NOMSG(m_v.size() == p_size && other.m_v.size() == p_size); + + if(ws.size() < 2 * p_size) { + ws.resize(2 * p_size); + } + + secure_vector z(2 * p_size); + + bigint_mul(z.data(), z.size(), m_v.data(), p_size, p_size, other.m_v.data(), p_size, p_size, ws.data(), ws.size()); + + bigint_monty_redc_inplace(z.data(), m_params.p()._data(), p_size, m_params.p_dash(), ws.data(), ws.size()); + z.resize(p_size); // truncate off high zero words + + return Montgomery_Int(m_params, std::move(z)); } Montgomery_Int& Montgomery_Int::mul_by(const Montgomery_Int& other, secure_vector& ws) { BOTAN_STATE_CHECK(other.m_params == m_params); - m_params->mul_by(m_v, other.m_v, ws); - return (*this); + return this->mul_by(std::span{other.m_v}, ws); } -Montgomery_Int& Montgomery_Int::mul_by(const secure_vector& other, secure_vector& ws) { - m_params->mul_by(m_v, other, ws); - return (*this); -} +Montgomery_Int& Montgomery_Int::mul_by(std::span other, secure_vector& ws) { + const size_t p_size = m_params.p_words(); + BOTAN_ASSERT_NOMSG(m_v.size() == p_size && other.size() == p_size); -Montgomery_Int& Montgomery_Int::operator*=(const Montgomery_Int& other) { - BOTAN_STATE_CHECK(other.m_params == m_params); - secure_vector ws; - return mul_by(other, ws); -} + if(ws.size() < 2 * p_size) { + ws.resize(2 * p_size); + } -Montgomery_Int& Montgomery_Int::operator*=(const secure_vector& other) { - secure_vector ws; - return mul_by(other, ws); -} + auto do_mul_by = [&](std::span z) { + bigint_mul(z.data(), z.size(), m_v.data(), p_size, p_size, other.data(), p_size, p_size, ws.data(), ws.size()); -Montgomery_Int& Montgomery_Int::square_this_n_times(secure_vector& ws, size_t n) { - for(size_t i = 0; i != n; ++i) { - m_params->square_this(m_v, ws); + bigint_monty_redc_inplace(z.data(), m_params.p()._data(), p_size, m_params.p_dash(), ws.data(), ws.size()); + + copy_mem(m_v, z.first(p_size)); + }; + + if(p_size <= MontgomeryUseStackLimit) { + std::array z{}; + do_mul_by(z); + } else { + secure_vector z(2 * p_size); + do_mul_by(z); } - return (*this); -} -Montgomery_Int& Montgomery_Int::square_this(secure_vector& ws) { - m_params->square_this(m_v, ws); return (*this); } -Montgomery_Int Montgomery_Int::square(secure_vector& ws) const { - return Montgomery_Int(m_params, m_params->sqr(m_v, ws), false); -} +Montgomery_Int& Montgomery_Int::square_this_n_times(secure_vector& ws, size_t n) { + const size_t p_size = m_params.p_words(); + BOTAN_ASSERT_NOMSG(m_v.size() == p_size); -Montgomery_Int Montgomery_Int::cube(secure_vector& ws) const { - return Montgomery_Int(m_params, m_params->sqr(m_v, ws), false); -} + if(ws.size() < 2 * p_size) { + ws.resize(2 * p_size); + } -Montgomery_Int Montgomery_Int::additive_inverse() const { - return Montgomery_Int(m_params, m_params->p()) - (*this); -} + auto do_sqr_n = [&](std::span z) { + for(size_t i = 0; i != n; ++i) { + bigint_sqr(z.data(), 2 * p_size, m_v.data(), p_size, p_size, ws.data(), ws.size()); -Montgomery_Int& Montgomery_Int::mul_by_2(secure_vector& ws) { - m_v.mod_mul(2, m_params->p(), ws); - return (*this); -} + bigint_monty_redc_inplace(z.data(), m_params.p()._data(), p_size, m_params.p_dash(), ws.data(), ws.size()); -Montgomery_Int& Montgomery_Int::mul_by_3(secure_vector& ws) { - m_v.mod_mul(3, m_params->p(), ws); - return (*this); -} + copy_mem(m_v, std::span{z}.first(p_size)); + } + }; + + if(p_size <= MontgomeryUseStackLimit) { + std::array z{}; + do_sqr_n(z); + } else { + secure_vector z(2 * p_size); + do_sqr_n(z); + } -Montgomery_Int& Montgomery_Int::mul_by_4(secure_vector& ws) { - m_v.mod_mul(4, m_params->p(), ws); return (*this); } -Montgomery_Int& Montgomery_Int::mul_by_8(secure_vector& ws) { - m_v.mod_mul(8, m_params->p(), ws); - return (*this); +Montgomery_Int Montgomery_Int::square(secure_vector& ws) const { + auto z = (*this); + z.square_this_n_times(ws, 1); + return z; } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/math/numbertheory/monty.h botan3-3.12.0+dfsg/src/lib/math/numbertheory/monty.h --- botan3-3.7.1+dfsg/src/lib/math/numbertheory/monty.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/numbertheory/monty.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,188 +10,163 @@ #include #include +#include +#include namespace Botan { -class Modular_Reducer; - -class Montgomery_Params; +class Barrett_Reduction; /** -* The Montgomery representation of an integer +* Parameters for Montgomery Reduction */ -class BOTAN_TEST_API Montgomery_Int final { +class BOTAN_TEST_API Montgomery_Params final { public: /** - * Create a zero-initialized Montgomery_Int - */ - Montgomery_Int(std::shared_ptr params) : m_params(std::move(params)) {} - - /** - * Create a Montgomery_Int - */ - Montgomery_Int(const std::shared_ptr& params, const BigInt& v, bool redc_needed = true); - - /** - * Create a Montgomery_Int - */ - Montgomery_Int(const std::shared_ptr& params, - const uint8_t bits[], - size_t len, - bool redc_needed = true); - - /** - * Create a Montgomery_Int - */ - Montgomery_Int(std::shared_ptr params, - const word words[], - size_t len, - bool redc_needed = true); - - static Montgomery_Int one(const std::shared_ptr& params); - - /** - * Wide reduction - input can be at most 2*bytes long + * Initialize a set of Montgomery reduction parameters. These values + * can be shared by all values in a specific Montgomery domain. */ - static Montgomery_Int from_wide_int(const std::shared_ptr& params, const BigInt& x); - - bool operator==(const Montgomery_Int& other) const; - - bool operator!=(const Montgomery_Int& other) const { return (m_v != other.m_v); } - - std::vector serialize() const; - - size_t size() const; - bool is_one() const; - bool is_zero() const; - - void fix_size(); + Montgomery_Params(const BigInt& p, const Barrett_Reduction& mod_p); /** - * Return the value to normal mod-p space + * Initialize a set of Montgomery reduction parameters. These values + * can be shared by all values in a specific Montgomery domain. */ - BigInt value() const; + explicit Montgomery_Params(const BigInt& p); - /** - * Return the Montgomery representation - */ - const BigInt& repr() const { return m_v; } + bool operator==(const Montgomery_Params& other) const; - Montgomery_Int operator+(const Montgomery_Int& other) const; + bool operator!=(const Montgomery_Params& other) const { return !((*this) == other); } - Montgomery_Int operator-(const Montgomery_Int& other) const; + const BigInt& p() const { return m_data->p(); } - Montgomery_Int& operator+=(const Montgomery_Int& other); + const BigInt& R1() const { return m_data->r1(); } - Montgomery_Int& operator-=(const Montgomery_Int& other); + const BigInt& R2() const { return m_data->r2(); } - Montgomery_Int operator*(const Montgomery_Int& other) const; + const BigInt& R3() const { return m_data->r3(); } - Montgomery_Int& operator*=(const Montgomery_Int& other); + word p_dash() const { return m_data->p_dash(); } - Montgomery_Int& operator*=(const secure_vector& other); + size_t p_words() const { return m_data->p_size(); } - Montgomery_Int& add(const Montgomery_Int& other, secure_vector& ws); + BigInt redc(const BigInt& x, secure_vector& ws) const; - Montgomery_Int& sub(const Montgomery_Int& other, secure_vector& ws); + void mul(BigInt& z, const BigInt& x, const BigInt& y, secure_vector& ws) const; - Montgomery_Int mul(const Montgomery_Int& other, secure_vector& ws) const; + void mul(BigInt& z, const BigInt& x, std::span y, secure_vector& ws) const; - Montgomery_Int& mul_by(const Montgomery_Int& other, secure_vector& ws); + BigInt mul(const BigInt& x, const BigInt& y, secure_vector& ws) const; - Montgomery_Int& mul_by(const secure_vector& other, secure_vector& ws); + void mul_by(BigInt& x, const BigInt& y, secure_vector& ws) const; - Montgomery_Int square(secure_vector& ws) const; + BigInt sqr(const BigInt& x, secure_vector& ws) const; - Montgomery_Int cube(secure_vector& ws) const; + void sqr(BigInt& z, const BigInt& x, secure_vector& ws) const; - Montgomery_Int& square_this(secure_vector& ws); + void sqr(BigInt& z, std::span x, secure_vector& ws) const; - Montgomery_Int& square_this_n_times(secure_vector& ws, size_t n); + private: + BigInt sqr(std::span x, secure_vector& ws) const; - Montgomery_Int additive_inverse() const; + class Data final { + public: + Data(const BigInt& p, const Barrett_Reduction& mod_p); - Montgomery_Int& mul_by_2(secure_vector& ws); + const BigInt& p() const { return m_p; } - Montgomery_Int& mul_by_3(secure_vector& ws); + const BigInt& r1() const { return m_r1; } - Montgomery_Int& mul_by_4(secure_vector& ws); + const BigInt& r2() const { return m_r2; } - Montgomery_Int& mul_by_8(secure_vector& ws); + const BigInt& r3() const { return m_r3; } - void _const_time_poison() const { CT::poison(m_v); } + word p_dash() const { return m_p_dash; } - void _const_time_unpoison() const { CT::unpoison(m_v); } + size_t p_size() const { return m_p_words; } - const std::shared_ptr& _params() const { return m_params; } + private: + BigInt m_p; + BigInt m_r1; + BigInt m_r2; + BigInt m_r3; + word m_p_dash; + size_t m_p_words; + }; - private: - std::shared_ptr m_params; - BigInt m_v; + std::shared_ptr m_data; }; /** -* Parameters for Montgomery Reduction +* The Montgomery representation of an integer */ -class BOTAN_TEST_API Montgomery_Params final { +class BOTAN_TEST_API Montgomery_Int final { public: /** - * Initialize a set of Montgomery reduction parameters. These values - * can be shared by all values in a specific Montgomery domain. + * Create a zero-initialized Montgomery_Int */ - Montgomery_Params(const BigInt& p, const Modular_Reducer& mod_p); + explicit Montgomery_Int(const Montgomery_Params& params) : m_params(params) {} /** - * Initialize a set of Montgomery reduction parameters. These values - * can be shared by all values in a specific Montgomery domain. + * Create a Montgomery_Int from a BigInt */ - Montgomery_Params(const BigInt& p); - - const BigInt& p() const { return m_p; } - - const BigInt& R1() const { return m_r1; } - - const BigInt& R2() const { return m_r2; } + Montgomery_Int(const Montgomery_Params& params, const BigInt& v, bool redc_needed = true); - const BigInt& R3() const { return m_r3; } + /** + * Create a Montgomery_Int + * + * The span must be exactly p_words long and encoding a value less than p already + * in Montgomery form + */ + Montgomery_Int(const Montgomery_Params& params, std::span words); - word p_dash() const { return m_p_dash; } + /** + * Return the value 1 in Montgomery form + */ + static Montgomery_Int one(const Montgomery_Params& params); - size_t p_words() const { return m_p_words; } + /** + * Wide reduction - input can be at most 2*bytes long + */ + static Montgomery_Int from_wide_int(const Montgomery_Params& params, const BigInt& x); - BigInt redc(const BigInt& x, secure_vector& ws) const; + std::vector serialize() const; - void redc_in_place(BigInt& x, secure_vector& ws) const; + /** + * Return the value to normal mod-p space + */ + BigInt value() const; - void mul(BigInt& z, const BigInt& x, const BigInt& y, secure_vector& ws) const; + /** + * Return the Montgomery representation + */ + const secure_vector& repr() const { return m_v; } - void mul(BigInt& z, const BigInt& x, std::span y, secure_vector& ws) const; + Montgomery_Int operator+(const Montgomery_Int& other) const; - BigInt mul(const BigInt& x, const BigInt& y, secure_vector& ws) const; + Montgomery_Int operator-(const Montgomery_Int& other) const; - BigInt mul(const BigInt& x, std::span y, secure_vector& ws) const; + Montgomery_Int mul(const Montgomery_Int& other, secure_vector& ws) const; - void mul_by(BigInt& x, std::span y, secure_vector& ws) const; + Montgomery_Int& mul_by(const Montgomery_Int& other, secure_vector& ws); - void mul_by(BigInt& x, const BigInt& y, secure_vector& ws) const; + Montgomery_Int& mul_by(std::span other, secure_vector& ws); - BigInt sqr(const BigInt& x, secure_vector& ws) const; + Montgomery_Int square(secure_vector& ws) const; - BigInt sqr(std::span x, secure_vector& ws) const; + Montgomery_Int& square_this_n_times(secure_vector& ws, size_t n); - void sqr(BigInt& z, const BigInt& x, secure_vector& ws) const; + void _const_time_poison() const { CT::poison(m_v); } - void sqr(BigInt& z, std::span x, secure_vector& ws) const; + void _const_time_unpoison() const { CT::unpoison(m_v); } - void square_this(BigInt& x, secure_vector& ws) const; + const Montgomery_Params& _params() const { return m_params; } private: - BigInt m_p; - BigInt m_r1; - BigInt m_r2; - BigInt m_r3; - word m_p_dash; - size_t m_p_words; + Montgomery_Int(const Montgomery_Params& params, secure_vector words); + + Montgomery_Params m_params; + secure_vector m_v; }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/math/numbertheory/monty_exp.cpp botan3-3.12.0+dfsg/src/lib/math/numbertheory/monty_exp.cpp --- botan3-3.7.1+dfsg/src/lib/math/numbertheory/monty_exp.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/numbertheory/monty_exp.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,29 +8,31 @@ #include +#include +#include #include #include #include namespace Botan { -class Montgomery_Exponentation_State final { +class Montgomery_Exponentiation_State final { public: - Montgomery_Exponentation_State(const Montgomery_Int& g, size_t window_bits, bool const_time); + Montgomery_Exponentiation_State(const Montgomery_Int& g, size_t window_bits, bool const_time); Montgomery_Int exponentiation(const BigInt& k, size_t max_k_bits) const; Montgomery_Int exponentiation_vartime(const BigInt& k) const; private: - std::shared_ptr m_params; + Montgomery_Params m_params; std::vector m_g; size_t m_window_bits; }; -Montgomery_Exponentation_State::Montgomery_Exponentation_State(const Montgomery_Int& g, - size_t window_bits, - bool const_time) : +Montgomery_Exponentiation_State::Montgomery_Exponentiation_State(const Montgomery_Int& g, + size_t window_bits, + bool const_time) : m_params(g._params()), m_window_bits(window_bits == 0 ? 4 : window_bits) { if(m_window_bits < 1 || m_window_bits > 12) { // really even 8 is too large ... throw Invalid_Argument("Invalid window bits for Montgomery exponentiation"); @@ -44,13 +46,14 @@ m_g.push_back(g); - for(size_t i = 2; i != window_size; ++i) { - m_g.push_back(m_g[1] * m_g[i - 1]); - } + secure_vector ws(2 * m_params.p_words()); - // Resize each element to exactly p words - for(auto& x : m_g) { - x.fix_size(); + for(size_t i = 2; i != window_size; ++i) { + if(i % 2 == 0) { + m_g.push_back(m_g[i / 2].square(ws)); + } else { + m_g.push_back(m_g[1].mul(m_g[i - 1], ws)); + } } if(const_time) { @@ -68,8 +71,8 @@ clear_mem(output.data(), output.size()); for(size_t i = 0; i != g.size(); i += 2) { - const secure_vector& vec_0 = g[i].repr().get_word_vector(); - const secure_vector& vec_1 = g[i + 1].repr().get_word_vector(); + const secure_vector& vec_0 = g[i].repr(); + const secure_vector& vec_1 = g[i + 1].repr(); BOTAN_ASSERT_NOMSG(vec_0.size() >= words && vec_1.size() >= words); @@ -85,7 +88,7 @@ } // namespace -Montgomery_Int Montgomery_Exponentation_State::exponentiation(const BigInt& scalar, size_t max_k_bits) const { +Montgomery_Int Montgomery_Exponentiation_State::exponentiation(const BigInt& scalar, size_t max_k_bits) const { BOTAN_DEBUG_ASSERT(scalar.bits() <= max_k_bits); // TODO add a const-time implementation of above assert and use it in release builds @@ -95,11 +98,11 @@ return Montgomery_Int::one(m_params); } - secure_vector e_bits(m_params->p_words()); - secure_vector ws; + secure_vector e_bits(m_params.p_words()); + secure_vector ws(2 * m_params.p_words()); const_time_lookup(e_bits, m_g, scalar.get_substring(m_window_bits * (exp_nibbles - 1), m_window_bits)); - Montgomery_Int x(m_params, e_bits.data(), e_bits.size(), false); + Montgomery_Int x(m_params, std::span{e_bits}); for(size_t i = exp_nibbles - 1; i > 0; --i) { x.square_this_n_times(ws, m_window_bits); @@ -111,10 +114,10 @@ return x; } -Montgomery_Int Montgomery_Exponentation_State::exponentiation_vartime(const BigInt& scalar) const { +Montgomery_Int Montgomery_Exponentiation_State::exponentiation_vartime(const BigInt& scalar) const { const size_t exp_nibbles = (scalar.bits() + m_window_bits - 1) / m_window_bits; - secure_vector ws; + secure_vector ws(2 * m_params.p_words()); if(exp_nibbles == 0) { return Montgomery_Int::one(m_params); @@ -135,44 +138,42 @@ return x; } -std::shared_ptr monty_precompute(const Montgomery_Int& g, - size_t window_bits, - bool const_time) { - return std::make_shared(g, window_bits, const_time); +std::shared_ptr monty_precompute(const Montgomery_Int& g, + size_t window_bits, + bool const_time) { + return std::make_shared(g, window_bits, const_time); } -std::shared_ptr monty_precompute( - const std::shared_ptr& params, const BigInt& g, size_t window_bits, bool const_time) { - BOTAN_ARG_CHECK(g < params->p(), "Montgomery base too big"); - Montgomery_Int monty_g(params, g); +std::shared_ptr monty_precompute(const Montgomery_Params& params, + const BigInt& g, + size_t window_bits, + bool const_time) { + BOTAN_ARG_CHECK(g < params.p(), "Montgomery base too big"); + const Montgomery_Int monty_g(params, g); return monty_precompute(monty_g, window_bits, const_time); } -Montgomery_Int monty_execute(const Montgomery_Exponentation_State& precomputed_state, +Montgomery_Int monty_execute(const Montgomery_Exponentiation_State& precomputed_state, const BigInt& k, size_t max_k_bits) { return precomputed_state.exponentiation(k, max_k_bits); } -Montgomery_Int monty_execute_vartime(const Montgomery_Exponentation_State& precomputed_state, const BigInt& k) { +Montgomery_Int monty_execute_vartime(const Montgomery_Exponentiation_State& precomputed_state, const BigInt& k) { return precomputed_state.exponentiation_vartime(k); } -Montgomery_Int monty_multi_exp(const std::shared_ptr& params_p, - const BigInt& x_bn, - const BigInt& z1, - const BigInt& y_bn, - const BigInt& z2) { - if(z1.is_negative() || z2.is_negative()) { +Montgomery_Int monty_multi_exp( + const Montgomery_Params& params_p, const BigInt& x_bn, const BigInt& z1, const BigInt& y_bn, const BigInt& z2) { + if(z1.signum() < 0 || z2.signum() < 0) { throw Invalid_Argument("multi_exponentiate exponents must be positive"); } const size_t z_bits = round_up(std::max(z1.bits(), z2.bits()), 2); - secure_vector ws; + secure_vector ws(2 * params_p.p_words()); - const Montgomery_Int one(params_p, params_p->R1(), false); - //const Montgomery_Int one(params_p, 1); + const Montgomery_Int one = Montgomery_Int::one(params_p); const Montgomery_Int x1(params_p, x_bn); const Montgomery_Int x2 = x1.square(ws); @@ -194,6 +195,7 @@ const Montgomery_Int y3x2 = y3.mul(x2, ws); const Montgomery_Int y3x3 = y3.mul(x3, ws); + // NOLINTNEXTLINE(*-const-correctness) bug in clang-tidy const Montgomery_Int* M[16] = {&one, &x1, // 0001 &x2, // 0010 @@ -215,8 +217,7 @@ for(size_t i = 0; i != z_bits; i += 2) { if(i > 0) { - H.square_this(ws); - H.square_this(ws); + H.square_this_n_times(ws, 2); } const uint32_t z1_b = z1.get_substring(z_bits - i - 2, 2); @@ -224,7 +225,9 @@ const uint32_t z12 = (4 * z2_b) + z1_b; - H.mul_by(*M[z12], ws); + if(z12 > 0) { + H.mul_by(*M[z12], ws); + } } return H; diff -Nru botan3-3.7.1+dfsg/src/lib/math/numbertheory/monty_exp.h botan3-3.12.0+dfsg/src/lib/math/numbertheory/monty_exp.h --- botan3-3.7.1+dfsg/src/lib/math/numbertheory/monty_exp.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/numbertheory/monty_exp.h 2026-05-07 01:38:28.000000000 +0000 @@ -13,29 +13,27 @@ namespace Botan { class BigInt; -class Modular_Reducer; -class Montgomery_Exponentation_State; +class Montgomery_Exponentiation_State; /* * Precompute for calculating values g^x mod p */ -std::shared_ptr monty_precompute( - const std::shared_ptr& params_p, - const BigInt& g, - size_t window_bits, - bool const_time = true); +std::shared_ptr monty_precompute(const Montgomery_Params& params_p, + const BigInt& g, + size_t window_bits, + bool const_time = true); /* * Precompute for calculating values g^x mod p */ -std::shared_ptr monty_precompute(const Montgomery_Int& g, - size_t window_bits, - bool const_time = true); +std::shared_ptr monty_precompute(const Montgomery_Int& g, + size_t window_bits, + bool const_time = true); /* * Return g^k mod p */ -Montgomery_Int monty_execute(const Montgomery_Exponentation_State& precomputed_state, +Montgomery_Int monty_execute(const Montgomery_Exponentiation_State& precomputed_state, const BigInt& k, size_t max_k_bits); @@ -43,9 +41,9 @@ * Return g^k mod p taking variable time depending on k * @warning only use this if k is public */ -Montgomery_Int monty_execute_vartime(const Montgomery_Exponentation_State& precomputed_state, const BigInt& k); +Montgomery_Int monty_execute_vartime(const Montgomery_Exponentiation_State& precomputed_state, const BigInt& k); -inline Montgomery_Int monty_exp(const std::shared_ptr& params_p, +inline Montgomery_Int monty_exp(const Montgomery_Params& params_p, const BigInt& g, const BigInt& k, size_t max_k_bits) { @@ -53,9 +51,7 @@ return monty_execute(*precomputed, k, max_k_bits); } -inline Montgomery_Int monty_exp_vartime(const std::shared_ptr& params_p, - const BigInt& g, - const BigInt& k) { +inline Montgomery_Int monty_exp_vartime(const Montgomery_Params& params_p, const BigInt& g, const BigInt& k) { auto precomputed = monty_precompute(params_p, g, 4, false); return monty_execute_vartime(*precomputed, k); } @@ -63,11 +59,8 @@ /** * Return (x^z1 * y^z2) % p */ -Montgomery_Int monty_multi_exp(const std::shared_ptr& params_p, - const BigInt& x, - const BigInt& z1, - const BigInt& y, - const BigInt& z2); +Montgomery_Int monty_multi_exp( + const Montgomery_Params& params_p, const BigInt& x, const BigInt& z1, const BigInt& y, const BigInt& z2); } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/math/numbertheory/numthry.cpp botan3-3.12.0+dfsg/src/lib/math/numbertheory/numthry.cpp --- botan3-3.7.1+dfsg/src/lib/math/numbertheory/numthry.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/numbertheory/numthry.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,8 +8,9 @@ #include -#include +#include #include +#include #include #include #include @@ -39,8 +40,8 @@ return BigInt::from_s32(-1); } - auto mod_p = Modular_Reducer::for_public_modulus(p); - auto monty_p = std::make_shared(p, mod_p); + auto mod_p = Barrett_Reduction::for_public_modulus(p); + const Montgomery_Params monty_p(p, mod_p); // If p == 3 (mod 4) there is a simple solution if(p % 4 == 3) { @@ -112,43 +113,69 @@ /* * Calculate the Jacobi symbol +* +* See Algorithm 2.149 in Handbook of Applied Cryptography */ -int32_t jacobi(const BigInt& a, const BigInt& n) { - if(n.is_even() || n < 2) { - throw Invalid_Argument("jacobi: second argument must be odd and > 1"); +int32_t jacobi(BigInt a, BigInt n) { + BOTAN_ARG_CHECK(n.is_odd() && n >= 3, "Argument n must be an odd integer >= 3"); + + if(a < 0 || a >= n) { + a %= n; } - BigInt x = a % n; - BigInt y = n; - int32_t J = 1; - - while(y > 1) { - x %= y; - if(x > y / 2) { - x = y - x; - if(y % 4 == 3) { - J = -J; - } + if(a == 0) { + return 0; + } + if(a == 1) { + return 1; + } + + int32_t s = 1; + + for(;;) { + const size_t e = low_zero_bits(a); + a >>= e; + const word n_mod_8 = n.word_at(0) % 8; + const word n_mod_4 = n_mod_8 % 4; + + if(e % 2 == 1 && (n_mod_8 == 3 || n_mod_8 == 5)) { + s = -s; } - if(x.is_zero()) { - return 0; + + if(n_mod_4 == 3 && a % 4 == 3) { + s = -s; } - size_t shifts = low_zero_bits(x); - x >>= shifts; - if(shifts % 2) { - word y_mod_8 = y % 8; - if(y_mod_8 == 3 || y_mod_8 == 5) { - J = -J; - } + /* + * The HAC presentation of the algorithm uses recursion, which is not + * desirable or necessary. + * + * Instead we loop accumulating the product of the various jacobi() + * subcomputations into s, until we reach algorithm termination, which + * occurs in one of two ways. + * + * If a == 1 then the recursion has completed; we can return the value of s. + * + * Otherwise, after swapping and reducing, check for a == 0 [this value is + * called `n1` in HAC's presentation]. This would imply that jacobi(n1,a1) + * would have the value 0, due to Line 1 in HAC 2.149, in which case the + * entire product is zero, and we can immediately return that result. + */ + + if(a == 1) { + return s; } - if(x % 4 == 3 && y % 4 == 3) { - J = -J; + std::swap(a, n); + + BOTAN_ASSERT_NOMSG(n.is_odd()); + + a %= n; + + if(a == 0) { + return 0; } - std::swap(x, y); } - return J; } /* @@ -213,25 +240,24 @@ // shifting so many times, we'll have reached the result for sure. const size_t loop_cnt = u.bits() + v.bits(); - using WordMask = CT::Mask; - // 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); + secure_vector ws(sz * 2); size_t factors_of_two = 0; for(size_t i = 0; i != loop_cnt; ++i) { - auto both_odd = WordMask::expand(u.is_odd()) & WordMask::expand(v.is_odd()); + auto both_odd = CT::Mask::expand_bool(u.is_odd()) & CT::Mask::expand_bool(v.is_odd()); // 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); + auto u_gt_v = CT::Mask::expand_bool(bigint_cmp(u._data(), u.size(), v._data(), v.size()) > 0); + bigint_sub_abs(tmp.mutable_data(), u._data(), v._data(), sz, ws.data()); u.ct_cond_assign((u_gt_v & both_odd).as_bool(), tmp); v.ct_cond_assign((~u_gt_v & both_odd).as_bool(), tmp); - const auto u_is_even = WordMask::expand(u.is_even()); - const auto v_is_even = WordMask::expand(v.is_even()); + const auto u_is_even = CT::Mask::expand_bool(u.is_even()); + const auto v_is_even = CT::Mask::expand_bool(v.is_even()); BOTAN_DEBUG_ASSERT((u_is_even | v_is_even).as_bool()); // When both are even, we're going to eliminate a factor of 2. @@ -282,7 +308,7 @@ * Modular Exponentiation */ BigInt power_mod(const BigInt& base, const BigInt& exp, const BigInt& mod) { - if(mod.is_negative() || mod == 1) { + if(mod.signum() < 0 || mod == 1) { return BigInt::zero(); } @@ -293,13 +319,13 @@ return BigInt::zero(); } - auto reduce_mod = Modular_Reducer::for_secret_modulus(mod); + auto reduce_mod = Barrett_Reduction::for_secret_modulus(mod); const size_t exp_bits = exp.bits(); if(mod.is_odd()) { - auto monty_params = std::make_shared(mod, reduce_mod); - return monty_exp(monty_params, reduce_mod.reduce(base), exp, exp_bits).value(); + const Montgomery_Params monty_params(mod, reduce_mod); + return monty_exp(monty_params, ct_modulo(base, mod), exp, exp_bits).value(); } /* @@ -307,7 +333,7 @@ cryptographically important, so this implementation is slow ... */ BigInt accum = BigInt::one(); - BigInt g = reduce_mod.reduce(base); + BigInt g = ct_modulo(base, mod); BigInt t; for(size_t i = 0; i != exp_bits; ++i) { @@ -369,12 +395,13 @@ return std::binary_search(PRIMES, PRIMES + PRIME_TABLE_SIZE, num); } - auto mod_n = Modular_Reducer::for_secret_modulus(n); + auto mod_n = Barrett_Reduction::for_secret_modulus(n); + const Montgomery_Params monty_n(n, mod_n); if(rng.is_seeded()) { const size_t t = miller_rabin_test_iterations(n_bits, prob, is_random); - if(is_miller_rabin_probable_prime(n, mod_n, rng, t) == false) { + if(!is_miller_rabin_probable_prime(n, mod_n, monty_n, rng, t)) { return false; } diff -Nru botan3-3.7.1+dfsg/src/lib/math/numbertheory/numthry.h botan3-3.12.0+dfsg/src/lib/math/numbertheory/numthry.h --- botan3-3.7.1+dfsg/src/lib/math/numbertheory/numthry.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/numbertheory/numthry.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,8 +10,6 @@ #include -BOTAN_FUTURE_INTERNAL_HEADER(numthry.h) - namespace Botan { class RandomNumberGenerator; @@ -39,13 +37,13 @@ * @param y a positive integer * @return z, smallest integer such that z % x == 0 and z % y == 0 */ -BigInt BOTAN_PUBLIC_API(2, 0) lcm(const BigInt& x, const BigInt& y); +BOTAN_DEPRECATED("Deprecated no replacement") BigInt BOTAN_PUBLIC_API(2, 0) lcm(const BigInt& x, const BigInt& y); /** * @param x an integer * @return (x*x) */ -BigInt BOTAN_PUBLIC_API(2, 0) square(const BigInt& x); +BOTAN_DEPRECATED("Just use x*x") BigInt BOTAN_PUBLIC_API(2, 0) square(const BigInt& x); /** * Modular inversion. This algorithm is const time with respect to x, @@ -70,10 +68,10 @@ * @param n is an odd integer > 1 * @return (n / m) */ -int32_t BOTAN_PUBLIC_API(2, 0) jacobi(const BigInt& a, const BigInt& n); +BOTAN_DEPRECATED("Deprecated no replacement") int32_t BOTAN_PUBLIC_API(2, 0) jacobi(BigInt a, BigInt n); /** -* Modular exponentation +* Modular exponentiation * @param b an integer base * @param x a positive exponent * @param m a positive modulus @@ -92,6 +90,7 @@ * @param p the prime modulus * @return y such that (y*y)%p == x, or -1 if no such integer */ +BOTAN_DEPRECATED("Deprecated no replacement") BigInt BOTAN_PUBLIC_API(3, 0) sqrt_modulo_prime(const BigInt& x, const BigInt& p); /** @@ -100,7 +99,7 @@ * largest value of n such that 2^n divides x evenly. Returns * zero if x is equal to zero. */ -size_t BOTAN_PUBLIC_API(2, 0) low_zero_bits(const BigInt& x); +BOTAN_DEPRECATED("Deprecated no replacement") size_t BOTAN_PUBLIC_API(2, 0) low_zero_bits(const BigInt& x); /** * Check for primality @@ -125,7 +124,7 @@ * @return 0 if the integer is not a perfect square, otherwise * returns the positive y st y*y == x */ -BigInt BOTAN_PUBLIC_API(2, 8) is_perfect_square(const BigInt& x); +BOTAN_DEPRECATED("Deprecated no replacement") BigInt BOTAN_PUBLIC_API(2, 8) is_perfect_square(const BigInt& x); /** * Randomly generate a prime suitable for discrete logarithm parameters @@ -154,6 +153,7 @@ * @param prob use test so false positive is bounded by 1/2**prob * @return random prime with the specified criteria */ +BOTAN_DEPRECATED("Deprecated no replacement") BigInt BOTAN_PUBLIC_API(2, 7) generate_rsa_prime(RandomNumberGenerator& keygen_rng, RandomNumberGenerator& prime_test_rng, size_t bits, @@ -166,12 +166,13 @@ * @param bits is how long the resulting prime should be * @return prime randomly chosen from safe primes of length bits */ +BOTAN_DEPRECATED("Deprecated no replacement") BigInt BOTAN_PUBLIC_API(2, 0) random_safe_prime(RandomNumberGenerator& rng, size_t bits); /** * The size of the PRIMES[] array */ -const size_t PRIME_TABLE_SIZE = 6541; +BOTAN_DEPRECATED("Deprecated no replacement") const size_t PRIME_TABLE_SIZE = 6541; /** * A const array of all odd primes less than 65535 diff -Nru botan3-3.7.1+dfsg/src/lib/math/numbertheory/primality.cpp botan3-3.12.0+dfsg/src/lib/math/numbertheory/primality.cpp --- botan3-3.7.1+dfsg/src/lib/math/numbertheory/primality.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/numbertheory/primality.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,15 +8,16 @@ #include #include -#include #include +#include #include #include -#include namespace Botan { -bool is_lucas_probable_prime(const BigInt& C, const Modular_Reducer& mod_C) { +bool is_lucas_probable_prime(const BigInt& C, const Barrett_Reduction& mod_C) { + BOTAN_ARG_CHECK(C.signum() >= 0, "Argument must be non-negative"); + if(C == 2 || C == 3 || C == 5 || C == 7 || C == 11 || C == 13) { return true; } @@ -28,7 +29,7 @@ BigInt D = BigInt::from_word(5); for(;;) { - int32_t j = jacobi(D, C); + const int32_t j = jacobi(D, C); if(j == 0) { return false; } @@ -38,7 +39,7 @@ } // Check 5, -7, 9, -11, 13, -15, 17, ... - if(D.is_negative()) { + if(D.signum() < 0) { D.flip_sign(); D += 2; } else { @@ -46,18 +47,25 @@ D.flip_sign(); } - if(D == 17 && is_perfect_square(C).is_nonzero()) { + if(D == 17 && is_perfect_square(C).signum() != 0) { return false; } } + if(D.signum() < 0) { + D += C; + } + const BigInt K = C + 1; const size_t K_bits = K.bits() - 1; BigInt U = BigInt::one(); BigInt V = BigInt::one(); - BigInt Ut, Vt, U2, V2; + BigInt Ut; + BigInt Vt; + BigInt U2; + BigInt V2; for(size_t i = 0; i != K_bits; ++i) { const bool k_bit = K.get_bit(K_bits - 1 - i); @@ -76,7 +84,7 @@ U2.ct_cond_add(U2.is_odd(), C); U2 >>= 1; - V2 = mod_C.reduce(Vt + Ut * D); + V2 = mod_C.reduce(Vt + mod_C.multiply(Ut, D)); V2.ct_cond_add(V2.is_odd(), C); V2 >>= 1; @@ -87,21 +95,21 @@ return (U == 0); } -bool is_bailie_psw_probable_prime(const BigInt& n, const Modular_Reducer& mod_n) { +bool is_bailie_psw_probable_prime(const BigInt& n, const Barrett_Reduction& mod_n) { if(n == 2) { return true; } else if(n <= 1 || n.is_even()) { return false; } - auto monty_n = std::make_shared(n, mod_n); + const Montgomery_Params monty_n(n, mod_n); const auto base = BigInt::from_word(2); return passes_miller_rabin_test(n, mod_n, monty_n, base) && is_lucas_probable_prime(n, mod_n); } bool passes_miller_rabin_test(const BigInt& n, - const Modular_Reducer& mod_n, - const std::shared_ptr& monty_n, + const Barrett_Reduction& mod_n, + const Montgomery_Params& monty_n, const BigInt& a) { if(n < 3 || n.is_even()) { return false; @@ -110,7 +118,13 @@ BOTAN_ASSERT_NOMSG(n > 1); const BigInt n_minus_1 = n - 1; - const size_t s = low_zero_bits(n_minus_1); + /* + * This unpoison is not ideal but realistically there is no way to + * hide the number of loop iterations (below). The main user of + * secret primes is RSA and we always generate RSA primes such that + * p == 3 (mod 4), which means s is always 1. + */ + const size_t s = CT::driveby_unpoison(low_zero_bits(n_minus_1)); const BigInt nm1_s = n_minus_1 >> s; const size_t n_bits = n.bits(); @@ -144,15 +158,14 @@ } bool is_miller_rabin_probable_prime(const BigInt& n, - const Modular_Reducer& mod_n, + const Barrett_Reduction& mod_n, + const Montgomery_Params& monty_n, RandomNumberGenerator& rng, size_t test_iterations) { if(n < 3 || n.is_even()) { return false; } - auto monty_n = std::make_shared(n, mod_n); - for(size_t i = 0; i != test_iterations; ++i) { const BigInt a = BigInt::random_integer(rng, BigInt::from_word(2), n); @@ -172,7 +185,7 @@ * If the candidate prime was maliciously constructed, we can't rely * on arguments based on p being random. */ - if(random == false) { + if(!random) { return base; } diff -Nru botan3-3.7.1+dfsg/src/lib/math/numbertheory/primality.h botan3-3.12.0+dfsg/src/lib/math/numbertheory/primality.h --- botan3-3.7.1+dfsg/src/lib/math/numbertheory/primality.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/numbertheory/primality.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,13 +8,12 @@ #define BOTAN_PRIMALITY_TEST_H_ #include -#include #include namespace Botan { class BigInt; -class Modular_Reducer; +class Barrett_Reduction; class Montgomery_Params; class RandomNumberGenerator; @@ -26,10 +25,10 @@ * this test alone. * * @param n the positive integer to test -* @param mod_n a pre-created Modular_Reducer for n +* @param mod_n a pre-created Barrett_Reduction for n * @return true if n seems probably prime, false if n is composite */ -bool BOTAN_TEST_API is_lucas_probable_prime(const BigInt& n, const Modular_Reducer& mod_n); +bool BOTAN_TEST_API is_lucas_probable_prime(const BigInt& n, const Barrett_Reduction& mod_n); /** * Perform Bailie-PSW primality test @@ -39,10 +38,10 @@ * many composite counterexamples exist. * * @param n the positive integer to test -* @param mod_n a pre-created Modular_Reducer for n +* @param mod_n a pre-created Barrett_Reduction for n * @return true if n seems probably prime, false if n is composite */ -bool BOTAN_TEST_API is_bailie_psw_probable_prime(const BigInt& n, const Modular_Reducer& mod_n); +bool BOTAN_TEST_API is_bailie_psw_probable_prime(const BigInt& n, const Barrett_Reduction& mod_n); /** * Return required number of Miller-Rabin tests in order to @@ -59,28 +58,30 @@ * Perform a single Miller-Rabin test with specified base * * @param n the positive integer to test -* @param mod_n a pre-created Modular_Reducer for n +* @param mod_n a pre-created Barrett_Reduction for n * @param monty_n Montgomery parameters for n * @param a the base to check * @return result of primality test */ bool passes_miller_rabin_test(const BigInt& n, - const Modular_Reducer& mod_n, - const std::shared_ptr& monty_n, + const Barrett_Reduction& mod_n, + const Montgomery_Params& monty_n, const BigInt& a); /** * Perform t iterations of a Miller-Rabin primality test with random bases * * @param n the positive integer to test -* @param mod_n a pre-created Modular_Reducer for n +* @param mod_n a pre-created Barrett_Reduction for n +* @param monty_n pre-created Montgomery parameters for n * @param rng a random number generator * @param t number of tests to perform * * @return result of primality test */ bool BOTAN_TEST_API is_miller_rabin_probable_prime(const BigInt& n, - const Modular_Reducer& mod_n, + const Barrett_Reduction& mod_n, + const Montgomery_Params& monty_n, RandomNumberGenerator& rng, size_t t); diff -Nru botan3-3.7.1+dfsg/src/lib/math/numbertheory/reducer.cpp botan3-3.12.0+dfsg/src/lib/math/numbertheory/reducer.cpp --- botan3-3.7.1+dfsg/src/lib/math/numbertheory/reducer.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/numbertheory/reducer.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,145 +1,27 @@ /* * Modular Reducer -* (C) 1999-2011,2018 Jack Lloyd +* (C) 1999-2011,2018,2025 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include -#include +#include #include -#include namespace Botan { -/* -* Modular_Reducer Constructor -*/ -Modular_Reducer::Modular_Reducer(const BigInt& mod) { +Modular_Reducer::Modular_Reducer(const BigInt& mod) : m_mod_words(mod.sig_words()) { if(mod < 0) { throw Invalid_Argument("Modular_Reducer: modulus must be positive"); } - // Left uninitialized if mod == 0 - m_mod_words = 0; - - if(mod > 0) { - *this = Modular_Reducer::for_secret_modulus(mod); - } -} - -Modular_Reducer Modular_Reducer::for_secret_modulus(const BigInt& mod) { - BOTAN_ARG_CHECK(!mod.is_zero(), "Modulus cannot be zero"); - BOTAN_ARG_CHECK(!mod.is_negative(), "Modulus cannot be negative"); - - size_t mod_words = mod.sig_words(); - - // Compute mu = floor(2^{2k} / m) - const size_t mu_bits = 2 * BOTAN_MP_WORD_BITS * mod_words; - return Modular_Reducer(mod, ct_divide_pow2k(mu_bits, mod), mod_words); -} - -Modular_Reducer Modular_Reducer::for_public_modulus(const BigInt& mod) { - BOTAN_ARG_CHECK(!mod.is_zero(), "Modulus cannot be zero"); - BOTAN_ARG_CHECK(!mod.is_negative(), "Modulus cannot be negative"); - - size_t mod_words = mod.sig_words(); - - // Compute mu = floor(2^{2k} / m) - const size_t mu_bits = 2 * BOTAN_MP_WORD_BITS * mod_words; - return Modular_Reducer(mod, BigInt::power_of_2(mu_bits) / mod, mod_words); + m_modulus = mod; } BigInt Modular_Reducer::reduce(const BigInt& x) const { - BigInt r; - secure_vector ws; - reduce(r, x, ws); - return r; -} - -BigInt Modular_Reducer::square(const BigInt& x) const { - secure_vector ws; - BigInt x2 = x; - x2.square(ws); - BigInt r; - reduce(r, x2, ws); - return r; -} - -namespace { - -/* -* Like if(cnd) x.rev_sub(...) but in const time -*/ -void cnd_rev_sub(bool cnd, BigInt& x, const word y[], size_t y_sw, secure_vector& ws) { - if(x.sign() != BigInt::Positive) { - throw Invalid_State("BigInt::sub_rev requires this is positive"); - } - - const size_t x_sw = x.sig_words(); - - const size_t max_words = std::max(x_sw, y_sw); - ws.resize(std::max(x_sw, y_sw)); - clear_mem(ws.data(), ws.size()); - x.grow_to(max_words); - - const int32_t relative_size = bigint_sub_abs(ws.data(), x._data(), x_sw, y, y_sw); - - x.cond_flip_sign((relative_size > 0) && cnd); - bigint_cnd_swap(static_cast(cnd), x.mutable_data(), ws.data(), max_words); -} - -} // namespace - -void Modular_Reducer::reduce(BigInt& t1, const BigInt& x, secure_vector& ws) const { - if(&t1 == &x) { - throw Invalid_State("Modular_Reducer arguments cannot alias"); - } - if(m_mod_words == 0) { - throw Invalid_State("Modular_Reducer: Never initalized"); - } - - const size_t x_sw = x.sig_words(); - - if(x_sw > 2 * m_mod_words) { - // too big, fall back to slow boat division - t1 = ct_modulo(x, m_modulus); - return; - } - - t1 = x; - t1.set_sign(BigInt::Positive); - t1 >>= (BOTAN_MP_WORD_BITS * (m_mod_words - 1)); - - t1.mul(m_mu, ws); - t1 >>= (BOTAN_MP_WORD_BITS * (m_mod_words + 1)); - - // TODO add masked mul to avoid computing high bits - t1.mul(m_modulus, ws); - t1.mask_bits(BOTAN_MP_WORD_BITS * (m_mod_words + 1)); - - t1.rev_sub(x._data(), std::min(x_sw, m_mod_words + 1), ws); - - /* - * If t1 < 0 then we must add b^(k+1) where b = 2^w. To avoid a - * side channel perform the addition unconditionally, with ws set - * to either b^(k+1) or else 0. - */ - const word t1_neg = t1.is_negative(); - - if(ws.size() < m_mod_words + 2) { - ws.resize(m_mod_words + 2); - } - clear_mem(ws.data(), ws.size()); - ws[m_mod_words + 1] = t1_neg; - - t1.add(ws.data(), m_mod_words + 2, BigInt::Positive); - - // Per HAC this step requires at most 2 subtractions - t1.ct_reduce_below(m_modulus, ws, 2); - - cnd_rev_sub(t1.is_nonzero() && x.is_negative(), t1, m_modulus._data(), m_modulus.size(), ws); + return ct_modulo(x, m_modulus); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/math/numbertheory/reducer.h botan3-3.12.0+dfsg/src/lib/math/numbertheory/reducer.h --- botan3-3.7.1+dfsg/src/lib/math/numbertheory/reducer.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/numbertheory/reducer.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,12 +10,14 @@ #include -BOTAN_FUTURE_INTERNAL_HEADER(reducer.h) +BOTAN_DEPRECATED_HEADER("reducer.h") namespace Botan { /** -* Modular Reducer (using Barrett's technique) +* Modular Reducer +* +* This class is deprecated without replacement */ class BOTAN_PUBLIC_API(2, 0) Modular_Reducer final { public: @@ -42,7 +44,7 @@ * @param x the value to square * @return (x * x) % p */ - BigInt square(const BigInt& x) const; + BigInt square(const BigInt& x) const { return reduce(x * x); } /** * Cube mod p @@ -58,28 +60,28 @@ * * @warning X and out must not reference each other * - * ws is a temporary workspace. + * ws is an (ignored) a temporary workspace. */ - void reduce(BigInt& out, const BigInt& x, secure_vector& ws) const; + void reduce(BigInt& out, const BigInt& x, secure_vector& /*ws*/) const { out = reduce(x); } bool initialized() const { return (m_mod_words != 0); } - BOTAN_DEPRECATED("Use for_public_modulus or for_secret_modulus") Modular_Reducer() { m_mod_words = 0; } + BOTAN_DEPRECATED("Use for_public_modulus or for_secret_modulus") Modular_Reducer() : m_mod_words(0) {} /** * Accepts m == 0 and leaves the Modular_Reducer in an uninitialized state */ - BOTAN_DEPRECATED("Use for_public_modulus or for_secret_modulus") explicit Modular_Reducer(const BigInt& mod); + explicit Modular_Reducer(const BigInt& mod); /** * Requires that m > 0 */ - static Modular_Reducer for_public_modulus(const BigInt& m); + static Modular_Reducer for_public_modulus(const BigInt& m) { return Modular_Reducer(m); } /** * Requires that m > 0 */ - static Modular_Reducer for_secret_modulus(const BigInt& m); + static Modular_Reducer for_secret_modulus(const BigInt& m) { return Modular_Reducer(m); } private: Modular_Reducer(const BigInt& m, BigInt mu, size_t mw) : m_modulus(m), m_mu(std::move(mu)), m_mod_words(mw) {} diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/info.txt botan3-3.12.0+dfsg/src/lib/math/pcurves/info.txt --- botan3-3.7.1+dfsg/src/lib/math/pcurves/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + PCURVES -> 20240404 - + name -> "Prime Order Curves" @@ -10,6 +10,7 @@ pcurves.h -pcurves_id.h +pcurves_algos.h pcurves_instance.h +pcurves_mul.h diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves.cpp botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves.cpp --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,207 +1,109 @@ /* -* (C) 2024 Jack Lloyd +* This file was automatically generated by ./src/scripts/dev_tools/gen_ec_groups.py on 2026-04-24 +* All manual changes will be lost. Edit the script instead. * * Botan is released under the Simplified BSD License (see license.txt) */ #include +#include +#include #include -#if defined(BOTAN_HAS_ASN1) - #include -#endif - namespace Botan::PCurve { -#if !defined(BOTAN_HAS_PCURVES_SECP192R1) -//static -std::shared_ptr PCurveInstance::secp192r1() { - return nullptr; +void PrimeOrderCurve::Scalar::_zeroize() { + secure_zeroize_buffer(m_value.data(), m_value.size() * sizeof(word)); } -#endif -#if !defined(BOTAN_HAS_PCURVES_SECP224R1) //static -std::shared_ptr PCurveInstance::secp224r1() { - return nullptr; -} +std::shared_ptr PrimeOrderCurve::from_params( + const BigInt& p, const BigInt& a, const BigInt& b, const BigInt& base_x, const BigInt& base_y, const BigInt& order) { +#if defined(BOTAN_HAS_PCURVES_GENERIC) + return PCurveInstance::from_params(p, a, b, base_x, base_y, order); #endif -#if !defined(BOTAN_HAS_PCURVES_SECP256R1) -//static -std::shared_ptr PCurveInstance::secp256r1() { - return nullptr; + BOTAN_UNUSED(p, a, b, base_x, base_y, order); + return {}; } -#endif -#if !defined(BOTAN_HAS_PCURVES_SECP384R1) //static -std::shared_ptr PCurveInstance::secp384r1() { - return nullptr; -} +std::shared_ptr PrimeOrderCurve::for_named_curve(std::string_view name) { +#if defined(BOTAN_HAS_PCURVES_SECP256R1) + if(name == "secp256r1") { + return PCurveInstance::secp256r1(); + } #endif -#if !defined(BOTAN_HAS_PCURVES_SECP521R1) -//static -std::shared_ptr PCurveInstance::secp521r1() { - return nullptr; -} +#if defined(BOTAN_HAS_PCURVES_SECP384R1) + if(name == "secp384r1") { + return PCurveInstance::secp384r1(); + } #endif -#if !defined(BOTAN_HAS_PCURVES_SECP256K1) -//static -std::shared_ptr PCurveInstance::secp256k1() { - return nullptr; -} +#if defined(BOTAN_HAS_PCURVES_SECP521R1) + if(name == "secp521r1") { + return PCurveInstance::secp521r1(); + } #endif -#if !defined(BOTAN_HAS_PCURVES_BRAINPOOL256R1) -//static -std::shared_ptr PCurveInstance::brainpool256r1() { - return nullptr; -} +#if defined(BOTAN_HAS_PCURVES_BRAINPOOL256R1) + if(name == "brainpool256r1") { + return PCurveInstance::brainpool256r1(); + } #endif -#if !defined(BOTAN_HAS_PCURVES_BRAINPOOL384R1) -//static -std::shared_ptr PCurveInstance::brainpool384r1() { - return nullptr; -} +#if defined(BOTAN_HAS_PCURVES_BRAINPOOL384R1) + if(name == "brainpool384r1") { + return PCurveInstance::brainpool384r1(); + } #endif -#if !defined(BOTAN_HAS_PCURVES_BRAINPOOL512R1) -//static -std::shared_ptr PCurveInstance::brainpool512r1() { - return nullptr; -} +#if defined(BOTAN_HAS_PCURVES_BRAINPOOL512R1) + if(name == "brainpool512r1") { + return PCurveInstance::brainpool512r1(); + } #endif -#if !defined(BOTAN_HAS_PCURVES_FRP256V1) -//static -std::shared_ptr PCurveInstance::frp256v1() { - return nullptr; -} +#if defined(BOTAN_HAS_PCURVES_FRP256V1) + if(name == "frp256v1") { + return PCurveInstance::frp256v1(); + } #endif -#if !defined(BOTAN_HAS_PCURVES_SM2P256V1) -//static -std::shared_ptr PCurveInstance::sm2p256v1() { - return nullptr; -} +#if defined(BOTAN_HAS_PCURVES_SECP192R1) + if(name == "secp192r1") { + return PCurveInstance::secp192r1(); + } #endif -#if !defined(BOTAN_HAS_PCURVES_NUMSP512D1) -//static -std::shared_ptr PCurveInstance::numsp512d1() { - return nullptr; -} +#if defined(BOTAN_HAS_PCURVES_SECP224R1) + if(name == "secp224r1") { + return PCurveInstance::secp224r1(); + } #endif -std::shared_ptr PrimeOrderCurve::from_id(PrimeOrderCurveId id) { - switch(id.code()) { - case PrimeOrderCurveId::secp192r1: - return PCurveInstance::secp192r1(); - case PrimeOrderCurveId::secp224r1: - return PCurveInstance::secp224r1(); - case PrimeOrderCurveId::secp256r1: - return PCurveInstance::secp256r1(); - case PrimeOrderCurveId::secp384r1: - return PCurveInstance::secp384r1(); - case PrimeOrderCurveId::secp521r1: - return PCurveInstance::secp521r1(); - case PrimeOrderCurveId::secp256k1: - return PCurveInstance::secp256k1(); - case PrimeOrderCurveId::brainpool256r1: - return PCurveInstance::brainpool256r1(); - case PrimeOrderCurveId::brainpool384r1: - return PCurveInstance::brainpool384r1(); - case PrimeOrderCurveId::brainpool512r1: - return PCurveInstance::brainpool512r1(); - case PrimeOrderCurveId::frp256v1: - return PCurveInstance::frp256v1(); - case PrimeOrderCurveId::sm2p256v1: - return PCurveInstance::sm2p256v1(); - case PrimeOrderCurveId::numsp512d1: - return PCurveInstance::numsp512d1(); +#if defined(BOTAN_HAS_PCURVES_SECP256K1) + if(name == "secp256k1") { + return PCurveInstance::secp256k1(); } - return {}; -} +#endif -std::string PrimeOrderCurveId::to_string() const { - switch(this->code()) { - case PrimeOrderCurveId::secp192r1: - return "secp192r1"; - case PrimeOrderCurveId::secp224r1: - return "secp224r1"; - case PrimeOrderCurveId::secp256r1: - return "secp256r1"; - case PrimeOrderCurveId::secp384r1: - return "secp384r1"; - case PrimeOrderCurveId::secp521r1: - return "secp521r1"; - case PrimeOrderCurveId::secp256k1: - return "secp256k1"; - case PrimeOrderCurveId::brainpool256r1: - return "brainpool256r1"; - case PrimeOrderCurveId::brainpool384r1: - return "brainpool384r1"; - case PrimeOrderCurveId::brainpool512r1: - return "brainpool512r1"; - case PrimeOrderCurveId::frp256v1: - return "frp256v1"; - case PrimeOrderCurveId::sm2p256v1: - return "sm2p256v1"; - case PrimeOrderCurveId::numsp512d1: - return "numsp512d1"; +#if defined(BOTAN_HAS_PCURVES_SM2P256V1) + if(name == "sm2p256v1") { + return PCurveInstance::sm2p256v1(); } +#endif - return "unknown"; -} - -//static -std::optional PrimeOrderCurveId::from_string(std::string_view name) { - if(name == "secp192r1") { - return PCurve::PrimeOrderCurveId::secp192r1; - } else if(name == "secp224r1") { - return PCurve::PrimeOrderCurveId::secp224r1; - } else if(name == "secp256r1") { - return PCurve::PrimeOrderCurveId::secp256r1; - } else if(name == "secp384r1") { - return PCurve::PrimeOrderCurveId::secp384r1; - } else if(name == "secp521r1") { - return PCurve::PrimeOrderCurveId::secp521r1; - } else if(name == "secp256k1") { - return PCurve::PrimeOrderCurveId::secp256k1; - } else if(name == "brainpool256r1") { - return PCurve::PrimeOrderCurveId::brainpool256r1; - } else if(name == "brainpool384r1") { - return PCurve::PrimeOrderCurveId::brainpool384r1; - } else if(name == "brainpool512r1") { - return PCurve::PrimeOrderCurveId::brainpool512r1; - } else if(name == "frp256v1") { - return PCurve::PrimeOrderCurveId::frp256v1; - } else if(name == "sm2p256v1") { - return PCurve::PrimeOrderCurveId::sm2p256v1; - } else if(name == "numsp512d1") { - return PCurve::PrimeOrderCurveId::numsp512d1; - } else { - return {}; +#if defined(BOTAN_HAS_PCURVES_NUMSP512D1) + if(name == "numsp512d1") { + return PCurveInstance::numsp512d1(); } -} - -#if defined(BOTAN_HAS_ASN1) +#endif -//static -std::optional PrimeOrderCurveId::from_oid(const OID& oid) { - const std::string name = oid.human_name_or_empty(); - if(name.empty()) { - return {}; - } else { - return PrimeOrderCurveId::from_string(name); - } + BOTAN_UNUSED(name); + return {}; } -#endif - } // namespace Botan::PCurve diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves.h botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves.h --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves.h 2026-05-07 01:38:28.000000000 +0000 @@ -7,19 +7,19 @@ #ifndef BOTAN_PCURVES_H_ #define BOTAN_PCURVES_H_ -#include - #include #include #include #include +#include +#include #include #include #include -#include namespace Botan { +class BigInt; class RandomNumberGenerator; } // namespace Botan @@ -29,27 +29,32 @@ /** * An elliptic curve without cofactor in Weierstrass form */ -class PrimeOrderCurve { +class PrimeOrderCurve /* NOLINT(*-special-member-functions) */ { public: /// Somewhat arbitrary maximum size for a field or scalar /// /// Sized to fit at least P-521 - static const size_t MaximumBitLength = 521; + static constexpr size_t MaximumBitLength = 521; - static const size_t MaximumByteLength = (MaximumBitLength + 7) / 8; + static constexpr size_t MaximumByteLength = (MaximumBitLength + 7) / 8; /// Number of words used to store MaximumByteLength - static const size_t StorageWords = (MaximumByteLength + sizeof(word) - 1) / sizeof(word); + static constexpr size_t StorageWords = (MaximumByteLength + sizeof(word) - 1) / sizeof(word); - static std::shared_ptr from_name(std::string_view name) { - if(auto id = PrimeOrderCurveId::from_string(name)) { - return PrimeOrderCurve::from_id(id.value()); - } else { - return {}; - } - } + /// @returns nullptr if the curve specified is not available + static std::shared_ptr for_named_curve(std::string_view name); - static std::shared_ptr from_id(PrimeOrderCurveId id); + /// @returns nullptr if the parameters seem unsuitable for pcurves + /// for example if the prime is too large + /// + /// This function *should* accept the same subset of curves as + /// the EC_Group constructor that accepts BigInts. + static std::shared_ptr from_params(const BigInt& p, + const BigInt& a, + const BigInt& b, + const BigInt& base_x, + const BigInt& base_y, + const BigInt& order); typedef std::array StorageUnit; typedef std::shared_ptr CurvePtr; @@ -66,69 +71,7 @@ Scalar& operator=(Scalar&& other) = default; ~Scalar() = default; - /** - * Return the size of the byte encoding of Scalars - */ - size_t bytes() const { return m_curve->scalar_bytes(); } - - /** - * Return the fixed length serialization of this scalar - */ - template > - T serialize() const { - T bytes(this->bytes()); - m_curve->serialize_scalar(bytes, *this); - return bytes; - } - - /** - * Perform integer multiplication modulo the group order - */ - friend Scalar operator*(const Scalar& a, const Scalar& b) { return a.m_curve->scalar_mul(a, b); } - - /** - * Perform integer addition modulo the group order - */ - friend Scalar operator+(const Scalar& a, const Scalar& b) { return a.m_curve->scalar_add(a, b); } - - /** - * Perform integer subtraction modulo the group order - */ - friend Scalar operator-(const Scalar& a, const Scalar& b) { return a.m_curve->scalar_sub(a, b); } - - /** - * Check for equality - */ - friend bool operator==(const Scalar& a, const Scalar& b) { return a.m_curve->scalar_equal(a, b); } - - /** - * Negate modulo the group order (ie return p - *this where p is the group order) - */ - Scalar negate() const { return m_curve->scalar_negate(*this); } - - /** - * Square modulo the group order - */ - Scalar square() const { return m_curve->scalar_square(*this); } - - /** - * Return the modular inverse of *this - * - * If *this is zero then returns zero. - */ - Scalar invert() const { return m_curve->scalar_invert(*this); } - - /** - * Return the modular inverse of *this (variable time) - * - * If *this is zero then returns zero. - */ - Scalar invert_vartime() const { return m_curve->scalar_invert_vartime(*this); } - - /** - * Returns true if this is equal to zero - */ - bool is_zero() const { return m_curve->scalar_is_zero(*this); } + void _zeroize(); const auto& _curve() const { return m_curve; } @@ -156,57 +99,7 @@ AffinePoint& operator=(AffinePoint&& other) = default; ~AffinePoint() = default; - static AffinePoint generator(CurvePtr curve) { return curve->generator(); } - - /** - * Return the size of the uncompressed encoding of points - */ - size_t bytes() const { return 1 + 2 * m_curve->field_element_bytes(); } - - /** - * Return the size of the compressed encoding of points - */ - size_t compressed_bytes() const { return 1 + m_curve->field_element_bytes(); } - - /** - * Return the serialization of the point in uncompressed form - */ - template > - T serialize() const { - T bytes(this->bytes()); - m_curve->serialize_point(bytes, *this); - return bytes; - } - - /** - * Return the serialization of the point in compressed form - */ - template > - T serialize_compressed() const { - T bytes(this->compressed_bytes()); - m_curve->serialize_point_compressed(bytes, *this); - return bytes; - } - - /** - * Return the serialization of the x coordinate - */ - template > - T x_bytes() const { - secure_vector bytes(m_curve->field_element_bytes()); - m_curve->serialize_point_x(bytes, *this); - return bytes; - } - - /** - * Point negation - */ - AffinePoint negate() const { return m_curve->point_negate(*this); } - - /** - * Return true if this is the curve identity element (aka the point at infinity) - */ - bool is_identity() const { return m_curve->affine_point_is_identity(*this); } + static AffinePoint generator(const CurvePtr& curve) { return curve->generator(); } const auto& _curve() const { return m_curve; } @@ -240,29 +133,6 @@ ProjectivePoint& operator=(ProjectivePoint&& other) = default; ~ProjectivePoint() = default; - /** - * Convert a point from affine to projective form - */ - static ProjectivePoint from_affine(const AffinePoint& pt) { return pt._curve()->point_to_projective(pt); } - - /** - * Convert a point from projective to affine form - * - * This operation is expensive; perform it only when required for - * serialization - */ - AffinePoint to_affine() const { return m_curve->point_to_affine(*this); } - - ProjectivePoint dbl() const { return m_curve->point_double(*this); } - - friend ProjectivePoint operator+(const ProjectivePoint& x, const ProjectivePoint& y) { - return x.m_curve->point_add(x, y); - } - - friend ProjectivePoint operator+(const ProjectivePoint& x, const AffinePoint& y) { - return x.m_curve->point_add_mixed(x, y); - } - const auto& _curve() const { return m_curve; } const auto& _x() const { return m_x; } @@ -285,7 +155,7 @@ StorageUnit m_z; }; - class PrecomputedMul2Table { + class PrecomputedMul2Table /* NOLINT(*-special-member-functions) */ { public: virtual ~PrecomputedMul2Table() = default; }; @@ -327,10 +197,6 @@ const Scalar& scalar, RandomNumberGenerator& rng) const = 0; - /// Setup a table for 2-ary multiplication - virtual std::unique_ptr mul2_setup(const AffinePoint& p, - const AffinePoint& pq) const = 0; - /// Setup a table for 2-ary multiplication where the first point is the generator virtual std::unique_ptr mul2_setup_g(const AffinePoint& q) const = 0; @@ -393,44 +259,46 @@ virtual AffinePoint point_to_affine(const ProjectivePoint& pt) const = 0; - virtual ProjectivePoint point_to_projective(const AffinePoint& pt) const = 0; - virtual bool affine_point_is_identity(const AffinePoint& pt) const = 0; - virtual ProjectivePoint point_double(const ProjectivePoint& pt) const = 0; - virtual AffinePoint point_negate(const AffinePoint& pt) const = 0; - virtual ProjectivePoint point_add(const ProjectivePoint& a, const ProjectivePoint& b) const = 0; - - virtual ProjectivePoint point_add_mixed(const ProjectivePoint& a, const AffinePoint& b) const = 0; + virtual ProjectivePoint point_add(const AffinePoint& a, const AffinePoint& b) const = 0; virtual void serialize_point(std::span bytes, const AffinePoint& pt) const = 0; - virtual void serialize_point_compressed(std::span bytes, const AffinePoint& pt) const = 0; - - virtual void serialize_point_x(std::span bytes, const AffinePoint& pt) const = 0; - virtual void serialize_scalar(std::span bytes, const Scalar& scalar) const = 0; /** - * Return the scalar zero - */ - virtual Scalar scalar_zero() const = 0; - - /** * Return the scalar one */ virtual Scalar scalar_one() const = 0; + /// Scalar addition virtual Scalar scalar_add(const Scalar& a, const Scalar& b) const = 0; + + /// Scalar subtraction virtual Scalar scalar_sub(const Scalar& a, const Scalar& b) const = 0; + + /// Scalar multiplication virtual Scalar scalar_mul(const Scalar& a, const Scalar& b) const = 0; + + /// Scalar squaring virtual Scalar scalar_square(const Scalar& s) const = 0; + + /// Scalar inversion virtual Scalar scalar_invert(const Scalar& s) const = 0; + + /// Scalar inversion (variable time) virtual Scalar scalar_invert_vartime(const Scalar& s) const = 0; + + /// Scalar negation virtual Scalar scalar_negate(const Scalar& s) const = 0; + + /// Test if scalar is zero virtual bool scalar_is_zero(const Scalar& s) const = 0; + + /// Test if two scalars are equal virtual bool scalar_equal(const Scalar& a, const Scalar& b) const = 0; /** @@ -442,19 +310,25 @@ * RFC 9380 hash to curve (NU variant) * * This is currently only supported for a few specific curves + * + * @param expand_message is a callback which must fill the provided output + * span with a sequence of uniform bytes, or if this is not possible due to + * length limitations or some other issue, throw an exception. It is + * invoked to produce the `uniform_bytes` value; see RFC 9380 section 5.2 */ - virtual AffinePoint hash_to_curve_nu(std::string_view hash, - std::span input, - std::span domain_sep) const = 0; + virtual AffinePoint hash_to_curve_nu(std::function)> expand_message) const = 0; /** * RFC 9380 hash to curve (RO variant) * * This is currently only supported for a few specific curves + * + * @param expand_message is a callback which must fill the provided output + * span with a sequence of uniform bytes, or if this is not possible due to + * length limitations or some other issue, throw an exception. It is + * invoked to produce the `uniform_bytes` value; see RFC 9380 section 5.2 */ - virtual ProjectivePoint hash_to_curve_ro(std::string_view hash, - std::span input, - std::span domain_sep) const = 0; + virtual ProjectivePoint hash_to_curve_ro(std::function)> expand_message) const = 0; }; } // namespace Botan::PCurve diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_algos.h botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_algos.h --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_algos.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_algos.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,503 @@ +/* +* (C) 2024,2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_PCURVES_ALGOS_H_ +#define BOTAN_PCURVES_ALGOS_H_ + +#include +#include +#include +#include +#include + +namespace Botan { + +/** +* Field inversion concept +* +* This concept checks if the curve class supports fe_invert2 +*/ +template +concept curve_supports_fe_invert2 = requires(const typename C::FieldElement& fe) { + { C::fe_invert2(fe) } -> std::same_as; +}; + +/** +* Field inversion +* +* Uses the specialized fe_invert2 if available, or otherwise the standard +* (FLT-based) field inversion. +*/ +template +inline constexpr auto invert_field_element(const typename C::FieldElement& fe) { + if constexpr(curve_supports_fe_invert2) { + return C::fe_invert2(fe) * fe; + } else { + return fe.invert(); + } +} + +/** +* Field square root +* +* This concept checks if the curve class supports fe_sqrt +*/ +template +concept curve_supports_fe_sqrt = requires(const typename C::FieldElement& fe) { + { C::fe_sqrt(fe) } -> std::same_as; +}; + +/** +* Field square root +* +* Uses the specialized fe_sqrt if available, or otherwise the standard +* square root +*/ +template +inline constexpr CT::Option sqrt_field_element(const typename C::FieldElement& fe) { + if constexpr(curve_supports_fe_sqrt) { + auto z = C::fe_sqrt(fe); + // Zero out the return value if it would otherwise be incorrect + const CT::Choice correct = (z.square() == fe); + z.conditional_assign(!correct, C::FieldElement::zero()); + return CT::Option(z, correct); + } else { + return fe.sqrt(); + } +} + +/** +* Convert a projective point into affine +*/ +template +inline constexpr auto to_affine(const typename C::ProjectivePoint& pt) { + // Not strictly required right? - default should work as long + // as (0,0) is identity and invert returns 0 on 0 + + if constexpr(curve_supports_fe_invert2) { + const auto z2_inv = C::fe_invert2(pt.z()); + const auto z3_inv = z2_inv.square() * pt.z(); + return typename C::AffinePoint(pt.x() * z2_inv, pt.y() * z3_inv); + } else { + const auto z_inv = invert_field_element(pt.z()); + const auto z2_inv = z_inv.square(); + const auto z3_inv = z_inv * z2_inv; + return typename C::AffinePoint(pt.x() * z2_inv, pt.y() * z3_inv); + } +} + +/** +* Convert a projective point into affine and return x coordinate only +*/ +template +auto to_affine_x(const typename C::ProjectivePoint& pt) { + if constexpr(curve_supports_fe_invert2) { + return pt.x() * C::fe_invert2(pt.z()); + } else { + const auto z_inv = invert_field_element(pt.z()); + const auto z2_inv = z_inv.square(); + return pt.x() * z2_inv; + } +} + +template +auto to_affine_batch(std::span projective) { + using AffinePoint = typename C::AffinePoint; + + const size_t N = projective.size(); + std::vector affine; + affine.reserve(N); + + CT::Choice any_identity = CT::Choice::no(); + + for(const auto& pt : projective) { + any_identity = any_identity || pt.is_identity(); + } + + // Conditional acceptable: N is public. State of points is not necessarily + // public, but we don't leak which point was the identity. In practice with + // the algorithms currently in use, the only time an identity can occur is + // during mul2 where the two points g/h have a small relation (ie h = g*k for + // some k < 16) + + if(N <= 2 || any_identity.as_bool()) { + // If there are identity elements, using the batch inversion gets + // tricky. It can be done, but this should be a rare situation so + // just punt to the serial conversion if it occurs + for(size_t i = 0; i != N; ++i) { + affine.push_back(to_affine(projective[i])); + } + } else { + std::vector c; + c.reserve(N); + + /* + Batch projective->affine using Montgomery's trick + + See Algorithm 2.26 in "Guide to Elliptic Curve Cryptography" + (Hankerson, Menezes, Vanstone) + */ + + c.push_back(projective[0].z()); + for(size_t i = 1; i != N; ++i) { + c.push_back(c[i - 1] * projective[i].z()); + } + + auto s_inv = [&]() { + if constexpr(VariableTime) { + return c[N - 1].invert_vartime(); + } else { + return invert_field_element(c[N - 1]); + } + }(); + + for(size_t i = N - 1; i > 0; --i) { + const auto& p = projective[i]; + + const auto z_inv = s_inv * c[i - 1]; + const auto z2_inv = z_inv.square(); + const auto z3_inv = z_inv * z2_inv; + + s_inv = s_inv * p.z(); + + affine.push_back(AffinePoint(p.x() * z2_inv, p.y() * z3_inv)); + } + + const auto z2_inv = s_inv.square(); + const auto z3_inv = s_inv * z2_inv; + affine.push_back(AffinePoint(projective[0].x() * z2_inv, projective[0].y() * z3_inv)); + std::reverse(affine.begin(), affine.end()); + return affine; + } + + return affine; +} + +/* +Projective point addition + +https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2 + +Cost: 12M + 4S + 6add + 1*2 +*/ +template +inline constexpr ProjectivePoint point_add(const ProjectivePoint& a, const ProjectivePoint& b) { + const auto a_is_identity = a.is_identity(); + const auto b_is_identity = b.is_identity(); + + const auto Z1Z1 = a.z().square(); + const auto Z2Z2 = b.z().square(); + const auto U1 = a.x() * Z2Z2; + const auto U2 = b.x() * Z1Z1; + const auto S1 = a.y() * b.z() * Z2Z2; + const auto S2 = b.y() * a.z() * Z1Z1; + const auto H = U2 - U1; + const auto r = S2 - S1; + + /* Risky conditional + * + * This implementation uses projective coordinates, which do not have an efficient complete + * addition formula. We rely on the design of the multiplication algorithms to avoid doublings. + * + * This conditional only comes into play for the actual doubling case, not x + (-x) which + * is another exceptional case in some circumstances. Here if a == -b then H == 0 && r != 0, + * in which case at the end we'll set z to a.z * b.z * H = 0, resulting in the correct + * output (the identity element) + */ + if((r.is_zero() && H.is_zero() && !(a_is_identity && b_is_identity)).as_bool()) { + return a.dbl(); + } + + const auto HH = H.square(); + const auto HHH = H * HH; + const auto V = U1 * HH; + const auto t2 = r.square(); + const auto t3 = V + V; + const auto t4 = t2 - HHH; + auto X3 = t4 - t3; + const auto t5 = V - X3; + const auto t6 = S1 * HHH; + const auto t7 = r * t5; + auto Y3 = t7 - t6; + const auto t8 = b.z() * H; + auto Z3 = a.z() * t8; + + // if a is identity then return b + FieldElement::conditional_assign(X3, Y3, Z3, a_is_identity, b.x(), b.y(), b.z()); + + // if b is identity then return a + FieldElement::conditional_assign(X3, Y3, Z3, b_is_identity, a.x(), a.y(), a.z()); + + return ProjectivePoint(X3, Y3, Z3); +} + +template +inline constexpr ProjectivePoint point_add_mixed(const ProjectivePoint& a, + const AffinePoint& b, + const FieldElement& one) { + const auto a_is_identity = a.is_identity(); + const auto b_is_identity = b.is_identity(); + + /* + https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2 + + Cost: 8M + 3S + 6add + 1*2 + */ + + const auto Z1Z1 = a.z().square(); + const auto U2 = b.x() * Z1Z1; + const auto S2 = b.y() * a.z() * Z1Z1; + const auto H = U2 - a.x(); + const auto r = S2 - a.y(); + + /* Risky conditional + * + * This implementation uses projective coordinates, which do not have an efficient complete + * addition formula. We rely on the design of the multiplication algorithms to avoid doublings. + * + * This conditional only comes into play for the actual doubling case, not x + (-x) which + * is another exceptional case in some circumstances. Here if a == -b then H == 0 && r != 0, + * in which case at the end we'll set z to a.z * H = 0, resulting in the correct output + * (the identity element) + */ + if((r.is_zero() && H.is_zero() && !(a_is_identity && b_is_identity)).as_bool()) { + return a.dbl(); + } + + const auto HH = H.square(); + const auto HHH = H * HH; + const auto V = a.x() * HH; + const auto t2 = r.square(); + const auto t3 = V + V; + const auto t4 = t2 - HHH; + auto X3 = t4 - t3; + const auto t5 = V - X3; + const auto t6 = a.y() * HHH; + const auto t7 = r * t5; + auto Y3 = t7 - t6; + auto Z3 = a.z() * H; + + // if a is identity then return b + FieldElement::conditional_assign(X3, Y3, Z3, a_is_identity, b.x(), b.y(), one); + + // if b is identity then return a + FieldElement::conditional_assign(X3, Y3, Z3, b_is_identity, a.x(), a.y(), a.z()); + + return ProjectivePoint(X3, Y3, Z3); +} + +template +inline constexpr ProjectivePoint point_add_or_sub_mixed(const ProjectivePoint& a, + const AffinePoint& b, + CT::Choice sub, + const FieldElement& one) { + const auto a_is_identity = a.is_identity(); + const auto b_is_identity = b.is_identity(); + + /* + https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2 + + Cost: 8M + 3S + 6add + 1*2 + */ + + auto by = b.y(); + by.conditional_assign(sub, by.negate()); + + const auto Z1Z1 = a.z().square(); + const auto U2 = b.x() * Z1Z1; + const auto S2 = by * a.z() * Z1Z1; + const auto H = U2 - a.x(); + const auto r = S2 - a.y(); + + /* Risky conditional + * + * This implementation uses projective coordinates, which do not have an efficient complete + * addition formula. We rely on the design of the multiplication algorithms to avoid doublings. + * + * This conditional only comes into play for the actual doubling case, not x + (-x) which + * is another exceptional case in some circumstances. Here if a == -b then H == 0 && r != 0, + * in which case at the end we'll set z to a.z * H = 0, resulting in the correct output + * (the identity element) + */ + if((r.is_zero() && H.is_zero() && !(a_is_identity && b_is_identity)).as_bool()) { + return a.dbl(); + } + + const auto HH = H.square(); + const auto HHH = H * HH; + const auto V = a.x() * HH; + const auto t2 = r.square(); + const auto t3 = V + V; + const auto t4 = t2 - HHH; + auto X3 = t4 - t3; + const auto t5 = V - X3; + const auto t6 = a.y() * HHH; + const auto t7 = r * t5; + auto Y3 = t7 - t6; + auto Z3 = a.z() * H; + + // if a is identity then return b + FieldElement::conditional_assign(X3, Y3, Z3, a_is_identity, b.x(), by, one); + + // if b is identity then return a + FieldElement::conditional_assign(X3, Y3, Z3, b_is_identity, a.x(), a.y(), a.z()); + + return ProjectivePoint(X3, Y3, Z3); +} + +/* +Point doubling + +Using https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-1998-cmo-2 + +Cost (generic A): 4M + 6S + 4A + 2*2 + 1*3 + 1*4 + 1*8 +Cost (A == -3): 4M + 4S + 5A + 2*2 + 1*3 + 1*4 + 1*8 +Cost (A == 0): 3M + 4S + 3A + 2*2 + 1*3 + 1*4 + 1*8 +*/ + +template +inline constexpr ProjectivePoint dbl_a_minus_3(const ProjectivePoint& pt) { + /* + if a == -3 then + 3*x^2 + a*z^4 == 3*x^2 - 3*z^4 == 3*(x^2-z^4) == 3*(x-z^2)*(x+z^2) + */ + const auto z2 = pt.z().square(); + const auto m = (pt.x() - z2).mul3() * (pt.x() + z2); + + // Remaining cost: 3M + 3S + 3A + 2*2 + 1*4 + 1*8 + const auto y2 = pt.y().square(); + const auto s = pt.x().mul4() * y2; + const auto nx = m.square() - s.mul2(); + const auto ny = m * (s - nx) - y2.square().mul8(); + const auto nz = pt.y().mul2() * pt.z(); + + return ProjectivePoint(nx, ny, nz); +} + +template +inline constexpr ProjectivePoint dbl_a_zero(const ProjectivePoint& pt) { + // If a == 0 then 3*x^2 + a*z^4 == 3*x^2 + // Cost: 1S + 1*3 + const auto m = pt.x().square().mul3(); + + // Remaining cost: 3M + 3S + 3A + 2*2 + 1*4 + 1*8 + const auto y2 = pt.y().square(); + const auto s = pt.x().mul4() * y2; + const auto nx = m.square() - s.mul2(); + const auto ny = m * (s - nx) - y2.square().mul8(); + const auto nz = pt.y().mul2() * pt.z(); + + return ProjectivePoint(nx, ny, nz); +} + +template +inline constexpr ProjectivePoint dbl_generic(const ProjectivePoint& pt, const FieldElement& A) { + // Cost: 1M + 3S + 1A + 1*3 + const auto z2 = pt.z().square(); + const auto m = pt.x().square().mul3() + A * z2.square(); + + // Remaining cost: 3M + 3S + 3A + 2*2 + 1*4 + 1*8 + const auto y2 = pt.y().square(); + const auto s = pt.x().mul4() * y2; + const auto nx = m.square() - s.mul2(); + const auto ny = m * (s - nx) - y2.square().mul8(); + const auto nz = pt.y().mul2() * pt.z(); + + return ProjectivePoint(nx, ny, nz); +} + +/* +Repeated doubling using an adaptation of Algorithm 3.23 in +"Guide To Elliptic Curve Cryptography" (Hankerson, Menezes, Vanstone) + +Curiously the book gives the algorithm only for A == -3, but +the largest gains come from applying it to the generic A case, +where it saves 2 squarings per iteration. + +For A == 0 +Pay 1*2 + 1half to save n*(1*4 + 1*8) + +For A == -3: +Pay 2S + 1*2 + 1half to save n*(1A + 1*4 + 1*8) + 1M + +For generic A: +Pay 2S + 1*2 + 1half to save n*(2S + 1*4 + 1*8) + +The value of n is assumed to be public and should be a constant +*/ +template +inline constexpr ProjectivePoint dbl_n_a_minus_3(const ProjectivePoint& pt, size_t n) { + auto nx = pt.x(); + auto ny = pt.y().mul2(); + auto nz = pt.z(); + auto w = nz.square().square(); + + // Conditional ok: loop iteration count is public + while(n > 0) { + const auto ny2 = ny.square(); + const auto ny4 = ny2.square(); + const auto t1 = (nx.square() - w).mul3(); + const auto t2 = nx * ny2; + nx = t1.square() - t2.mul2(); + nz *= ny; + ny = t1 * (t2 - nx).mul2() - ny4; + n--; + // Conditional ok: loop iteration count is public + if(n > 0) { + w *= ny4; + } + } + return ProjectivePoint(nx, ny.div2(), nz); +} + +template +inline constexpr ProjectivePoint dbl_n_a_zero(const ProjectivePoint& pt, size_t n) { + auto nx = pt.x(); + auto ny = pt.y().mul2(); + auto nz = pt.z(); + + // Conditional ok: loop iteration count is public + while(n > 0) { + const auto ny2 = ny.square(); + const auto ny4 = ny2.square(); + const auto t1 = nx.square().mul3(); + const auto t2 = nx * ny2; + nx = t1.square() - t2.mul2(); + nz *= ny; + ny = t1 * (t2 - nx).mul2() - ny4; + n--; + } + return ProjectivePoint(nx, ny.div2(), nz); +} + +template +inline constexpr ProjectivePoint dbl_n_generic(const ProjectivePoint& pt, const FieldElement& A, size_t n) { + auto nx = pt.x(); + auto ny = pt.y().mul2(); + auto nz = pt.z(); + auto w = nz.square().square() * A; + + // Conditional ok: loop iteration count is public + while(n > 0) { + const auto ny2 = ny.square(); + const auto ny4 = ny2.square(); + const auto t1 = nx.square().mul3() + w; + const auto t2 = nx * ny2; + nx = t1.square() - t2.mul2(); + nz *= ny; + ny = t1 * (t2 - nx).mul2() - ny4; + n--; + // Conditional ok: loop iteration count is public + if(n > 0) { + w *= ny4; + } + } + return ProjectivePoint(nx, ny.div2(), nz); +} + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_brainpool256r1/info.txt botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_brainpool256r1/info.txt --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_brainpool256r1/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_brainpool256r1/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + PCURVES_BRAINPOOL256R1 -> 20240608 - + name -> "PCurve brainpool256r1" diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_brainpool256r1/pcurves_brainpool256r1.cpp botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_brainpool256r1/pcurves_brainpool256r1.cpp --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_brainpool256r1/pcurves_brainpool256r1.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_brainpool256r1/pcurves_brainpool256r1.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,9 +12,10 @@ namespace { -// clang-format off namespace brainpool256r1 { +// clang-format off + class Params final : public EllipticCurveParameters< "A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377", "7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9", @@ -24,11 +25,11 @@ "547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997"> { }; -class Curve final : public EllipticCurve {}; +// clang-format on -} +class Curve final : public EllipticCurve {}; -// clang-format on +} // namespace brainpool256r1 } // namespace diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_brainpool384r1/info.txt botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_brainpool384r1/info.txt --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_brainpool384r1/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_brainpool384r1/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + PCURVES_BRAINPOOL384R1 -> 20240608 - + name -> "PCurve brainpool384r1" diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_brainpool384r1/pcurves_brainpool384r1.cpp botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_brainpool384r1/pcurves_brainpool384r1.cpp --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_brainpool384r1/pcurves_brainpool384r1.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_brainpool384r1/pcurves_brainpool384r1.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,8 +12,10 @@ namespace { -// clang-format off namespace brainpool384r1 { + +// clang-format off + class Params final : public EllipticCurveParameters< "8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53", "7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503AD4EB04A8C7DD22CE2826", @@ -23,11 +25,11 @@ "8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315"> { }; -class Curve final : public EllipticCurve {}; +// clang-format on -} +class Curve final : public EllipticCurve {}; -// clang-format on +} // namespace brainpool384r1 } // namespace diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_brainpool512r1/info.txt botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_brainpool512r1/info.txt --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_brainpool512r1/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_brainpool512r1/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + PCURVES_BRAINPOOL512R1 -> 20240608 - + name -> "PCurve brainpool512r1" diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_brainpool512r1/pcurves_brainpool512r1.cpp botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_brainpool512r1/pcurves_brainpool512r1.cpp --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_brainpool512r1/pcurves_brainpool512r1.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_brainpool512r1/pcurves_brainpool512r1.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,9 +12,10 @@ namespace { -// clang-format off namespace brainpool512r1 { +// clang-format off + class Params final : public EllipticCurveParameters< "AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3", "7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA", @@ -24,11 +25,11 @@ "7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892"> { }; -class Curve final : public EllipticCurve {}; +// clang-format on -} +class Curve final : public EllipticCurve {}; -// clang-format on +} // namespace brainpool512r1 } // namespace diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_frp256v1/info.txt botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_frp256v1/info.txt --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_frp256v1/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_frp256v1/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + PCURVES_FRP256V1 -> 20240608 - + name -> "PCurve frp256v1" diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_frp256v1/pcurves_frp256v1.cpp botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_frp256v1/pcurves_frp256v1.cpp --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_frp256v1/pcurves_frp256v1.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_frp256v1/pcurves_frp256v1.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,9 +12,10 @@ namespace { -// clang-format off namespace frp256v1 { +// clang-format off + class Params final : public EllipticCurveParameters< "F1FD178C0B3AD58F10126DE8CE42435B3961ADBCABC8CA6DE8FCF353D86E9C03", "F1FD178C0B3AD58F10126DE8CE42435B3961ADBCABC8CA6DE8FCF353D86E9C00", @@ -24,11 +25,11 @@ "6142E0F7C8B204911F9271F0F3ECEF8C2701C307E8E4C9E183115A1554062CFB"> { }; -class Curve final : public EllipticCurve {}; +// clang-format on -} +class Curve final : public EllipticCurve {}; -// clang-format on +} // namespace frp256v1 } // namespace diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_generic/info.txt botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_generic/info.txt --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_generic/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_generic/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,13 @@ + +PCURVES_GENERIC -> 20250112 + + + +name -> "PCurve generic" + + + +bigint +numbertheory +mp + diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_generic/pcurves_generic.cpp botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_generic/pcurves_generic.cpp --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_generic/pcurves_generic.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_generic/pcurves_generic.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,1751 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Botan::PCurve { + +namespace { + +template +constexpr std::optional> bytes_to_words(std::span bytes) { + if(bytes.size() > WordInfo::bytes * N) { + return std::nullopt; + } + + std::array r{}; + + const size_t full_words = bytes.size() / WordInfo::bytes; + const size_t extra_bytes = bytes.size() % WordInfo::bytes; + + for(size_t i = 0; i != full_words; ++i) { + r[i] = load_be(bytes.data(), full_words - 1 - i); + } + + if(extra_bytes > 0) { + const size_t shift = extra_bytes * 8; + bigint_shl1(r.data(), r.size(), r.size(), shift); + + for(size_t i = 0; i != extra_bytes; ++i) { + const word b0 = bytes[WordInfo::bytes * full_words + i]; + r[0] |= (b0 << (8 * (extra_bytes - 1 - i))); + } + } + + return r; +} + +template +T impl_pow_vartime(const T& elem, const T& one, size_t bits, std::span exp) { + constexpr size_t WindowBits = 4; + constexpr size_t WindowElements = (1 << WindowBits) - 1; + + const size_t Windows = (bits + WindowBits - 1) / WindowBits; + + std::vector tbl; + tbl.reserve(WindowElements); + + tbl.push_back(elem); + + for(size_t i = 1; i != WindowElements; ++i) { + if(i % 2 == 1) { + tbl.push_back(tbl[i / 2].square()); + } else { + tbl.push_back(tbl[i - 1] * tbl[0]); + } + } + + auto r = one; + + const size_t w0 = read_window_bits(exp, (Windows - 1) * WindowBits); + + if(w0 > 0) { + r = tbl[w0 - 1]; + } + + for(size_t i = 1; i != Windows; ++i) { + for(size_t j = 0; j != WindowBits; ++j) { + r = r.square(); + } + const size_t w = read_window_bits(exp, (Windows - i - 1) * WindowBits); + + if(w > 0) { + r *= tbl[w - 1]; + } + } + + return r; +} + +} // namespace + +class GenericCurveParams final { + public: + typedef PrimeOrderCurve::StorageUnit StorageUnit; + static constexpr size_t N = PrimeOrderCurve::StorageWords; + + GenericCurveParams(const BigInt& p, + const BigInt& a, + const BigInt& b, + const BigInt& base_x, + const BigInt& base_y, + const BigInt& order) : + m_words(p.sig_words()), + m_order_bits(order.bits()), + m_order_bytes(order.bytes()), + m_field_bits(p.bits()), + m_field_bytes(p.bytes()), + m_monty_order(order), + m_monty_field(p), + m_field(bn_to_fixed(p)), + m_field_minus_2(bn_to_fixed_rev(p - 2)), + m_field_monty_r1(bn_to_fixed(m_monty_field.R1())), + m_field_monty_r2(bn_to_fixed(m_monty_field.R2())), + m_field_p_plus_1_over_4(bn_to_fixed_rev((p + 1) / 4)), + m_field_inv_2(bn_to_fixed((p / 2) + 1)), + m_field_p_dash(m_monty_field.p_dash()), + + m_order(bn_to_fixed(order)), + m_order_minus_2(bn_to_fixed_rev(order - 2)), + m_order_monty_r1(bn_to_fixed(m_monty_order.R1())), + m_order_monty_r2(bn_to_fixed(m_monty_order.R2())), + m_order_monty_r3(bn_to_fixed(m_monty_order.R3())), + m_order_inv_2(bn_to_fixed((order / 2) + 1)), + m_order_p_dash(m_monty_order.p_dash()), + + m_a_is_minus_3(a + 3 == p), + m_a_is_zero(a.is_zero()), + m_order_is_lt_field(order < p) { + secure_vector ws; + m_monty_curve_a = bn_to_fixed(m_monty_field.mul(a, m_monty_field.R2(), ws)); + m_monty_curve_b = bn_to_fixed(m_monty_field.mul(b, m_monty_field.R2(), ws)); + + m_base_x = bn_to_fixed(m_monty_field.mul(base_x, m_monty_field.R2(), ws)); + m_base_y = bn_to_fixed(m_monty_field.mul(base_y, m_monty_field.R2(), ws)); + } + + size_t words() const { return m_words; } + + size_t order_bits() const { return m_order_bits; } + + size_t order_bytes() const { return m_order_bytes; } + + size_t field_bits() const { return m_field_bits; } + + size_t field_bytes() const { return m_field_bytes; } + + const Montgomery_Params& monty_order() const { return m_monty_order; } + + const Montgomery_Params& monty_field() const { return m_monty_field; } + + const StorageUnit& field() const { return m_field; } + + const StorageUnit& field_minus_2() const { return m_field_minus_2; } + + const StorageUnit& field_monty_r1() const { return m_field_monty_r1; } + + const StorageUnit& field_monty_r2() const { return m_field_monty_r2; } + + const StorageUnit& field_p_plus_1_over_4() const { return m_field_p_plus_1_over_4; } + + const StorageUnit& field_inv_2() const { return m_field_inv_2; } + + word field_p_dash() const { return m_field_p_dash; } + + const StorageUnit& order() const { return m_order; } + + const StorageUnit& order_minus_2() const { return m_order_minus_2; } + + const StorageUnit& order_monty_r1() const { return m_order_monty_r1; } + + const StorageUnit& order_monty_r2() const { return m_order_monty_r2; } + + const StorageUnit& order_monty_r3() const { return m_order_monty_r3; } + + const StorageUnit& order_inv_2() const { return m_order_inv_2; } + + word order_p_dash() const { return m_order_p_dash; } + + const StorageUnit& monty_curve_a() const { return m_monty_curve_a; } + + const StorageUnit& monty_curve_b() const { return m_monty_curve_b; } + + const StorageUnit& base_x() const { return m_base_x; } + + const StorageUnit& base_y() const { return m_base_y; } + + bool a_is_minus_3() const { return m_a_is_minus_3; } + + bool a_is_zero() const { return m_a_is_zero; } + + bool order_is_less_than_field() const { return m_order_is_lt_field; } + + void mul(std::array& z, const std::array& x, const std::array& y) const { + clear_mem(z); + + if(m_words == 4) { + bigint_comba_mul4(z.data(), x.data(), y.data()); + } else if(m_words == 6) { + bigint_comba_mul6(z.data(), x.data(), y.data()); + } else if(m_words == 8) { + bigint_comba_mul8(z.data(), x.data(), y.data()); + } else if(m_words == 9) { + bigint_comba_mul9(z.data(), x.data(), y.data()); + } else { + bigint_mul(z.data(), z.size(), x.data(), m_words, m_words, y.data(), m_words, m_words, nullptr, 0); + } + } + + void sqr(std::array& z, const std::array& x) const { + clear_mem(z); + + if(m_words == 4) { + bigint_comba_sqr4(z.data(), x.data()); + } else if(m_words == 6) { + bigint_comba_sqr6(z.data(), x.data()); + } else if(m_words == 8) { + bigint_comba_sqr8(z.data(), x.data()); + } else if(m_words == 9) { + bigint_comba_sqr9(z.data(), x.data()); + } else { + bigint_sqr(z.data(), z.size(), x.data(), m_words, m_words, nullptr, 0); + } + } + + private: + static std::array bn_to_fixed(const BigInt& n) { + const size_t n_words = n.sig_words(); + BOTAN_ASSERT_NOMSG(n_words <= PrimeOrderCurve::StorageWords); + + std::array r{}; + copy_mem(std::span{r}.first(n_words), n._as_span().first(n_words)); + return r; + } + + static std::array bn_to_fixed_rev(const BigInt& n) { + auto v = bn_to_fixed(n); + std::reverse(v.begin(), v.end()); + return v; + } + + private: + size_t m_words; + size_t m_order_bits; + size_t m_order_bytes; + size_t m_field_bits; + size_t m_field_bytes; + + Montgomery_Params m_monty_order; + Montgomery_Params m_monty_field; + + StorageUnit m_field; + StorageUnit m_field_minus_2; + StorageUnit m_field_monty_r1; + StorageUnit m_field_monty_r2; + StorageUnit m_field_p_plus_1_over_4; + StorageUnit m_field_inv_2; + word m_field_p_dash; + + StorageUnit m_order; + StorageUnit m_order_minus_2; + StorageUnit m_order_monty_r1; + StorageUnit m_order_monty_r2; + StorageUnit m_order_monty_r3; + StorageUnit m_order_inv_2; + word m_order_p_dash; + + StorageUnit m_monty_curve_a{}; + StorageUnit m_monty_curve_b{}; + + StorageUnit m_base_x{}; + StorageUnit m_base_y{}; + + bool m_a_is_minus_3; + bool m_a_is_zero; + bool m_order_is_lt_field; +}; + +class GenericScalar final { + public: + typedef word W; + typedef PrimeOrderCurve::StorageUnit StorageUnit; + static constexpr size_t N = PrimeOrderCurve::StorageWords; + + static std::optional from_wide_bytes(const GenericPrimeOrderCurve* curve, + std::span bytes) { + const size_t mlen = curve->_params().order_bytes(); + + if(bytes.size() > 2 * mlen) { + return {}; + } + + std::array padded_bytes{}; + copy_mem(std::span{padded_bytes}.last(bytes.size()), bytes); + + auto words = bytes_to_words<2 * N>(std::span{padded_bytes}); + if(words) { + auto in_rep = wide_to_rep(curve, words.value()); + return GenericScalar(curve, in_rep); + } else { + return {}; + } + } + + static std::optional deserialize(const GenericPrimeOrderCurve* curve, + std::span bytes) { + const size_t len = curve->_params().order_bytes(); + + if(bytes.size() != len) { + return {}; + } + + const auto words = bytes_to_words(bytes); + + if(words) { + if(!bigint_ct_is_lt(words->data(), N, curve->_params().order().data(), N).as_bool()) { + return {}; + } + + // Safe because we checked above that words is an integer < P + return GenericScalar(curve, to_rep(curve, *words)); + } else { + return {}; + } + } + + static GenericScalar zero(const GenericPrimeOrderCurve* curve) { + const StorageUnit zeros{}; + return GenericScalar(curve, zeros); + } + + static GenericScalar one(const GenericPrimeOrderCurve* curve) { + return GenericScalar(curve, curve->_params().order_monty_r1()); + } + + static GenericScalar random(const GenericPrimeOrderCurve* curve, RandomNumberGenerator& rng) { + constexpr size_t MAX_ATTEMPTS = 1000; + + const size_t bits = curve->_params().order_bits(); + + std::vector buf(curve->_params().order_bytes()); + + for(size_t i = 0; i != MAX_ATTEMPTS; ++i) { + rng.randomize(buf); + + // Zero off high bits that if set would certainly cause us + // to be out of range + if(bits % 8 != 0) { + const uint8_t mask = 0xFF >> (8 - (bits % 8)); + buf[0] &= mask; + } + + if(auto s = GenericScalar::deserialize(curve, buf)) { + if(s.value().is_nonzero().as_bool()) { + return s.value(); + } + } + } + + throw Internal_Error("Failed to generate random Scalar within bounded number of attempts"); + } + + friend GenericScalar operator+(const GenericScalar& a, const GenericScalar& b) { + const auto* curve = check_curve(a, b); + const size_t words = curve->_params().words(); + + StorageUnit t{}; + const W carry = bigint_add3(t.data(), a.data(), words, b.data(), words); + + StorageUnit r{}; + bigint_monty_maybe_sub(words, r.data(), carry, t.data(), curve->_params().order().data()); + return GenericScalar(curve, r); + } + + friend GenericScalar operator-(const GenericScalar& a, const GenericScalar& b) { return a + b.negate(); } + + friend GenericScalar operator*(const GenericScalar& a, const GenericScalar& b) { + const auto* curve = check_curve(a, b); + + std::array z; // NOLINT(*-member-init) + curve->_params().mul(z, a.value(), b.value()); + return GenericScalar(curve, redc(curve, z)); + } + + GenericScalar& operator*=(const GenericScalar& other) { + const auto* curve = check_curve(*this, other); + + std::array z; // NOLINT(*-member-init) + curve->_params().mul(z, value(), other.value()); + m_val = redc(curve, z); + return (*this); + } + + GenericScalar square() const { + const auto* curve = this->m_curve; + + std::array z; // NOLINT(*-member-init) + curve->_params().sqr(z, value()); + return GenericScalar(curve, redc(curve, z)); + } + + GenericScalar pow_vartime(const StorageUnit& exp) const { + auto one = GenericScalar::one(curve()); + auto bits = curve()->_params().order_bits(); + auto words = curve()->_params().words(); + return impl_pow_vartime(*this, one, bits, std::span{exp}.last(words)); + } + + GenericScalar negate() const { + auto x_is_zero = CT::all_zeros(this->data(), N); + + StorageUnit r; + bigint_sub3(r.data(), m_curve->_params().order().data(), N, this->data(), N); + x_is_zero.if_set_zero_out(r.data(), N); + return GenericScalar(m_curve, r); + } + + GenericScalar invert() const { return pow_vartime(m_curve->_params().order_minus_2()); } + + /** + * Helper for variable time BEEA + * + * Note this function assumes that its arguments are in the standard + * domain, not the Montgomery domain. invert_vartime converts its argument + * out of Montgomery, and then back to Montgomery when returning the result. + */ + static void _invert_vartime_div2_helper(GenericScalar& a, GenericScalar& x) { + const auto& inv_2 = a.curve()->_params().order_inv_2(); + + // Conditional ok: this function is variable time + while((a.m_val[0] & 1) != 1) { + shift_right<1>(a.m_val); + + const W borrow = shift_right<1>(x.m_val); + + // Conditional ok: this function is variable time + if(borrow > 0) { + bigint_add2(x.m_val.data(), N, inv_2.data(), N); + } + } + } + + /* + * See the comments on invert_vartime in pcurves_impl.h for background + */ + GenericScalar invert_vartime() const { + if(this->is_zero().as_bool()) { + return (*this); + } + + auto x = GenericScalar(m_curve, std::array{1}); + auto b = GenericScalar(m_curve, from_rep(m_curve, m_val)); + + // First loop iteration + GenericScalar::_invert_vartime_div2_helper(b, x); + + auto a = b.negate(); + // y += x but y is zero at the outset + auto y = x; + + // First half of second loop iteration + GenericScalar::_invert_vartime_div2_helper(a, y); + + for(;;) { + // Conditional ok: this function is variable time + if(a.m_val == b.m_val) { + // At this point it should be that a == b == 1 + auto r = y.negate(); + + // Convert back to Montgomery + return GenericScalar(curve(), to_rep(curve(), r.m_val)); + } + + auto nx = x + y; + + /* + * Otherwise either b > a or a > b + * + * If b > a we want to set b to b - a + * Otherwise we want to set a to a - b + * + * Compute r = b - a and check if it underflowed + * If it did not then we are in the b > a path + */ + std::array r{}; + const word carry = bigint_sub3(r.data(), b.data(), N, a.data(), N); + + // Conditional ok: this function is variable time + if(carry == 0) { + // b > a + b.m_val = r; + x = nx; + GenericScalar::_invert_vartime_div2_helper(b, x); + } else { + // We know this can't underflow because a > b + bigint_sub3(r.data(), a.data(), N, b.data(), N); + a.m_val = r; + y = nx; + GenericScalar::_invert_vartime_div2_helper(a, y); + } + } + } + + template + T serialize() const { + T bytes(m_curve->_params().order_bytes()); + this->serialize_to(bytes); + return bytes; + } + + void serialize_to(std::span bytes) const { + auto v = from_rep(m_curve, m_val); + std::reverse(v.begin(), v.end()); + + const size_t flen = m_curve->_params().order_bytes(); + BOTAN_ARG_CHECK(bytes.size() == flen, "Expected output span provided"); + + // Remove leading zero bytes + const auto padded_bytes = store_be(v); + const size_t extra = N * WordInfo::bytes - flen; + copy_mem(bytes, std::span{padded_bytes}.subspan(extra, flen)); + } + + CT::Choice is_zero() const { return CT::all_zeros(m_val.data(), m_curve->_params().words()).as_choice(); } + + CT::Choice is_nonzero() const { return !is_zero(); } + + CT::Choice operator==(const GenericScalar& other) const { + if(this->m_curve != other.m_curve) { + return CT::Choice::no(); + } + + return CT::is_equal(m_val.data(), other.m_val.data(), m_curve->_params().words()).as_choice(); + } + + /** + * Convert the integer to standard representation and return the sequence of words + */ + StorageUnit to_words() const { return from_rep(m_curve, m_val); } + + const StorageUnit& stash_value() const { return m_val; } + + const GenericPrimeOrderCurve* curve() const { return m_curve; } + + GenericScalar(const GenericPrimeOrderCurve* curve, StorageUnit val) : m_curve(curve), m_val(val) {} + + private: + const StorageUnit& value() const { return m_val; } + + const W* data() const { return m_val.data(); } + + static const GenericPrimeOrderCurve* check_curve(const GenericScalar& a, const GenericScalar& b) { + BOTAN_STATE_CHECK(a.m_curve == b.m_curve); + return a.m_curve; + } + + static StorageUnit redc(const GenericPrimeOrderCurve* curve, std::array z) { + const auto& mod = curve->_params().order(); + const size_t words = curve->_params().words(); + StorageUnit r{}; + StorageUnit ws{}; + bigint_monty_redc( + r.data(), z.data(), mod.data(), words, curve->_params().order_p_dash(), ws.data(), ws.size()); + return r; + } + + static StorageUnit from_rep(const GenericPrimeOrderCurve* curve, StorageUnit z) { + std::array ze{}; + copy_mem(std::span{ze}.template first(), z); + return redc(curve, ze); + } + + static StorageUnit to_rep(const GenericPrimeOrderCurve* curve, StorageUnit x) { + std::array z; // NOLINT(*-member-init) + curve->_params().mul(z, x, curve->_params().order_monty_r2()); + return redc(curve, z); + } + + static StorageUnit wide_to_rep(const GenericPrimeOrderCurve* curve, std::array x) { + auto redc_x = redc(curve, x); + std::array z; // NOLINT(*-member-init) + curve->_params().mul(z, redc_x, curve->_params().order_monty_r3()); + return redc(curve, z); + } + + const GenericPrimeOrderCurve* m_curve; + StorageUnit m_val; +}; + +namespace { + +class GenericField final { + public: + typedef word W; + typedef PrimeOrderCurve::StorageUnit StorageUnit; + static constexpr size_t N = PrimeOrderCurve::StorageWords; + + static std::optional deserialize(const GenericPrimeOrderCurve* curve, + std::span bytes) { + const size_t len = curve->_params().field_bytes(); + + if(bytes.size() != len) { + return {}; + } + + const auto words = bytes_to_words(bytes); + + if(words) { + if(!bigint_ct_is_lt(words->data(), N, curve->_params().field().data(), N).as_bool()) { + return {}; + } + + // Safe because we checked above that words is an integer < P + return GenericField::from_words(curve, *words); + } else { + return {}; + } + } + + static GenericField from_words(const GenericPrimeOrderCurve* curve, const std::array& words) { + return GenericField(curve, to_rep(curve, words)); + } + + static GenericField zero(const GenericPrimeOrderCurve* curve) { + const StorageUnit zeros{}; + return GenericField(curve, zeros); + } + + static GenericField one(const GenericPrimeOrderCurve* curve) { + return GenericField(curve, curve->_params().field_monty_r1()); + } + + static GenericField curve_a(const GenericPrimeOrderCurve* curve) { + return GenericField(curve, curve->_params().monty_curve_a()); + } + + static GenericField curve_b(const GenericPrimeOrderCurve* curve) { + return GenericField(curve, curve->_params().monty_curve_b()); + } + + static GenericField random(const GenericPrimeOrderCurve* curve, RandomNumberGenerator& rng) { + constexpr size_t MAX_ATTEMPTS = 1000; + + const size_t bits = curve->_params().field_bits(); + + std::vector buf(curve->_params().field_bytes()); + + for(size_t i = 0; i != MAX_ATTEMPTS; ++i) { + rng.randomize(buf); + + // Zero off high bits that if set would certainly cause us + // to be out of range + if(bits % 8 != 0) { + const uint8_t mask = 0xFF >> (8 - (bits % 8)); + buf[0] &= mask; + } + + if(auto s = GenericField::deserialize(curve, buf)) { + if(s.value().is_nonzero().as_bool()) { + return s.value(); + } + } + } + + throw Internal_Error("Failed to generate random Scalar within bounded number of attempts"); + } + + /** + * Return the value of this divided by 2 + */ + GenericField div2() const { + StorageUnit t = value(); + const W borrow = shift_right<1>(t); + + // If value was odd, add (P/2)+1 + bigint_cnd_add(borrow, t.data(), m_curve->_params().field_inv_2().data(), N); + + return GenericField(m_curve, t); + } + + /// Return (*this) multiplied by 2 + GenericField mul2() const { + StorageUnit t = value(); + const W carry = shift_left<1>(t); + + StorageUnit r; + bigint_monty_maybe_sub(r.data(), carry, t.data(), m_curve->_params().field().data()); + return GenericField(m_curve, r); + } + + /// Return (*this) multiplied by 3 + GenericField mul3() const { return mul2() + (*this); } + + /// Return (*this) multiplied by 4 + GenericField mul4() const { return mul2().mul2(); } + + /// Return (*this) multiplied by 8 + GenericField mul8() const { return mul2().mul2().mul2(); } + + friend GenericField operator+(const GenericField& a, const GenericField& b) { + const auto* curve = check_curve(a, b); + const size_t words = curve->_params().words(); + + StorageUnit t{}; + const W carry = bigint_add3(t.data(), a.data(), words, b.data(), words); + + StorageUnit r{}; + bigint_monty_maybe_sub(words, r.data(), carry, t.data(), curve->_params().field().data()); + return GenericField(curve, r); + } + + friend GenericField operator-(const GenericField& a, const GenericField& b) { return a + b.negate(); } + + friend GenericField operator*(const GenericField& a, const GenericField& b) { + const auto* curve = check_curve(a, b); + + std::array z; // NOLINT(*-member-init) + curve->_params().mul(z, a.value(), b.value()); + return GenericField(curve, redc(curve, z)); + } + + GenericField& operator*=(const GenericField& other) { + const auto* curve = check_curve(*this, other); + + std::array z; // NOLINT(*-member-init) + curve->_params().mul(z, value(), other.value()); + m_val = redc(curve, z); + return (*this); + } + + GenericField square() const { + std::array z; // NOLINT(*-member-init) + m_curve->_params().sqr(z, value()); + return GenericField(m_curve, redc(m_curve, z)); + } + + GenericField pow_vartime(const StorageUnit& exp) const { + auto one = GenericField::one(curve()); + auto bits = curve()->_params().field_bits(); + auto words = curve()->_params().words(); + return impl_pow_vartime(*this, one, bits, std::span{exp}.last(words)); + } + + GenericField negate() const { + auto x_is_zero = CT::all_zeros(this->data(), N); + + StorageUnit r; + bigint_sub3(r.data(), m_curve->_params().field().data(), N, this->data(), N); + x_is_zero.if_set_zero_out(r.data(), N); + return GenericField(m_curve, r); + } + + GenericField invert() const { return pow_vartime(m_curve->_params().field_minus_2()); } + + GenericField invert_vartime() const { + // TODO take advantage of variable time here using eg BEEA + // see IntMod::invert_vartime in pcurves_impl.h + return invert(); + } + + template + T serialize() const { + T bytes(m_curve->_params().field_bytes()); + serialize_to(bytes); + return bytes; + } + + void serialize_to(std::span bytes) const { + auto v = from_rep(m_curve, m_val); + std::reverse(v.begin(), v.end()); + + const size_t flen = m_curve->_params().field_bytes(); + BOTAN_ARG_CHECK(bytes.size() == flen, "Expected output span provided"); + + // Remove leading zero bytes + const auto padded_bytes = store_be(v); + const size_t extra = N * WordInfo::bytes - flen; + copy_mem(bytes, std::span{padded_bytes}.subspan(extra, flen)); + } + + CT::Choice is_zero() const { return CT::all_zeros(m_val.data(), m_curve->_params().words()).as_choice(); } + + CT::Choice is_nonzero() const { return !is_zero(); } + + CT::Choice operator==(const GenericField& other) const { + if(this->m_curve != other.m_curve) { + return CT::Choice::no(); + } + + return CT::is_equal(m_val.data(), other.m_val.data(), m_curve->_params().words()).as_choice(); + } + + const StorageUnit& stash_value() const { return m_val; } + + const GenericPrimeOrderCurve* curve() const { return m_curve; } + + CT::Choice is_even() const { + auto v = from_rep(m_curve, m_val); + return !CT::Choice::from_int(v[0] & 0x01); + } + + /** + * Convert the integer to standard representation and return the sequence of words + */ + StorageUnit to_words() const { return from_rep(m_curve, m_val); } + + void _const_time_poison() const { CT::poison(m_val); } + + void _const_time_unpoison() const { CT::unpoison(m_val); } + + static void conditional_swap(CT::Choice cond, GenericField& x, GenericField& y) { + const W mask = cond.into_bitmask(); + + for(size_t i = 0; i != N; ++i) { + auto nx = choose(mask, y.m_val[i], x.m_val[i]); + auto ny = choose(mask, x.m_val[i], y.m_val[i]); + x.m_val[i] = nx; + y.m_val[i] = ny; + } + } + + void conditional_assign(CT::Choice cond, const GenericField& nx) { + const W mask = cond.into_bitmask(); + + for(size_t i = 0; i != N; ++i) { + m_val[i] = choose(mask, nx.m_val[i], m_val[i]); + } + } + + /** + * Conditional assignment + * + * If `cond` is true, sets `x` to `nx` and `y` to `ny` + */ + static void conditional_assign( + GenericField& x, GenericField& y, CT::Choice cond, const GenericField& nx, const GenericField& ny) { + const W mask = cond.into_bitmask(); + + for(size_t i = 0; i != N; ++i) { + x.m_val[i] = choose(mask, nx.m_val[i], x.m_val[i]); + y.m_val[i] = choose(mask, ny.m_val[i], y.m_val[i]); + } + } + + /** + * Conditional assignment + * + * If `cond` is true, sets `x` to `nx`, `y` to `ny`, and `z` to `nz` + */ + static void conditional_assign(GenericField& x, + GenericField& y, + GenericField& z, + CT::Choice cond, + const GenericField& nx, + const GenericField& ny, + const GenericField& nz) { + const W mask = cond.into_bitmask(); + + for(size_t i = 0; i != N; ++i) { + x.m_val[i] = choose(mask, nx.m_val[i], x.m_val[i]); + y.m_val[i] = choose(mask, ny.m_val[i], y.m_val[i]); + z.m_val[i] = choose(mask, nz.m_val[i], z.m_val[i]); + } + } + + std::pair sqrt() const { + BOTAN_STATE_CHECK(m_curve->_params().field()[0] % 4 == 3); + + auto z = pow_vartime(m_curve->_params().field_p_plus_1_over_4()); + const CT::Choice correct = (z.square() == *this); + // Zero out the return value if it would otherwise be incorrect + z.conditional_assign(!correct, zero(m_curve)); + return {z, correct}; + } + + GenericField(const GenericPrimeOrderCurve* curve, StorageUnit val) : m_curve(curve), m_val(val) {} + + private: + const StorageUnit& value() const { return m_val; } + + const W* data() const { return m_val.data(); } + + static const GenericPrimeOrderCurve* check_curve(const GenericField& a, const GenericField& b) { + BOTAN_STATE_CHECK(a.m_curve == b.m_curve); + return a.m_curve; + } + + static StorageUnit redc(const GenericPrimeOrderCurve* curve, std::array z) { + const auto& mod = curve->_params().field(); + const size_t words = curve->_params().words(); + StorageUnit r{}; + StorageUnit ws{}; + bigint_monty_redc( + r.data(), z.data(), mod.data(), words, curve->_params().field_p_dash(), ws.data(), ws.size()); + return r; + } + + static StorageUnit from_rep(const GenericPrimeOrderCurve* curve, StorageUnit z) { + std::array ze{}; + copy_mem(std::span{ze}.template first(), z); + return redc(curve, ze); + } + + static StorageUnit to_rep(const GenericPrimeOrderCurve* curve, StorageUnit x) { + std::array z{}; + curve->_params().mul(z, x, curve->_params().field_monty_r2()); + return redc(curve, z); + } + + const GenericPrimeOrderCurve* m_curve; + StorageUnit m_val; +}; + +} // namespace + +/** +* Affine Curve Point +* +* This contains a pair of integers (x,y) which satisfy the curve equation +*/ +class GenericAffinePoint final { + public: + GenericAffinePoint(const GenericField& x, const GenericField& y) : m_x(x), m_y(y) {} + + explicit GenericAffinePoint(const GenericPrimeOrderCurve* curve) : + m_x(GenericField::zero(curve)), m_y(GenericField::zero(curve)) {} + + static GenericAffinePoint identity(const GenericPrimeOrderCurve* curve) { + return GenericAffinePoint(GenericField::zero(curve), GenericField::zero(curve)); + } + + static GenericAffinePoint identity(const GenericAffinePoint& pt) { return identity(pt.curve()); } + + CT::Choice is_identity() const { return x().is_zero() && y().is_zero(); } + + GenericAffinePoint negate() const { return GenericAffinePoint(x(), y().negate()); } + + /** + * Serialize the point in uncompressed format + */ + void serialize_to(std::span bytes) const { + const size_t fe_bytes = curve()->_params().field_bytes(); + BOTAN_ARG_CHECK(bytes.size() == 1 + 2 * fe_bytes, "Buffer size incorrect"); + BOTAN_STATE_CHECK(this->is_identity().as_bool() == false); + BufferStuffer pack(bytes); + pack.append(0x04); + x().serialize_to(pack.next(fe_bytes)); + y().serialize_to(pack.next(fe_bytes)); + BOTAN_DEBUG_ASSERT(pack.full()); + } + + /** + * If idx is zero then return the identity element. Otherwise return pts[idx - 1] + * + * Returns the identity element also if idx is out of range + */ + static auto ct_select(std::span pts, size_t idx) { + BOTAN_ARG_CHECK(!pts.empty(), "Cannot select from an empty set"); + auto result = GenericAffinePoint::identity(pts[0].curve()); + + // Intentionally wrapping; set to maximum size_t if idx == 0 + const size_t idx1 = static_cast(idx - 1); + for(size_t i = 0; i != pts.size(); ++i) { + const auto found = CT::Mask::is_equal(idx1, i).as_choice(); + result.conditional_assign(found, pts[i]); + } + + return result; + } + + /** + * Return (x^3 + A*x + B) mod p + */ + static GenericField x3_ax_b(const GenericField& x) { + return (x.square() + GenericField::curve_a(x.curve())) * x + GenericField::curve_b(x.curve()); + } + + /** + * Point deserialization + * + * This accepts compressed or uncompressed formats. + */ + static std::optional deserialize(const GenericPrimeOrderCurve* curve, + std::span bytes) { + const size_t fe_bytes = curve->_params().field_bytes(); + + if(bytes.size() == 1 + 2 * fe_bytes && bytes[0] == 0x04) { + auto x = GenericField::deserialize(curve, bytes.subspan(1, fe_bytes)); + auto y = GenericField::deserialize(curve, bytes.subspan(1 + fe_bytes, fe_bytes)); + + if(x && y) { + const auto lhs = (*y).square(); + const auto rhs = GenericAffinePoint::x3_ax_b(*x); + if((lhs == rhs).as_bool()) { + return GenericAffinePoint(*x, *y); + } + } + } else if(bytes.size() == 1 + fe_bytes && (bytes[0] == 0x02 || bytes[0] == 0x03)) { + const CT::Choice y_is_even = CT::Mask::is_equal(bytes[0], 0x02).as_choice(); + + if(auto x = GenericField::deserialize(curve, bytes.subspan(1, fe_bytes))) { + auto [y, is_square] = x3_ax_b(*x).sqrt(); + + if(is_square.as_bool()) { + const auto flip_y = y_is_even != y.is_even(); + y.conditional_assign(flip_y, y.negate()); + return GenericAffinePoint(*x, y); + } + } + } else if(bytes.size() == 1 && bytes[0] == 0x00) { + // See SEC1 section 2.3.4 + return GenericAffinePoint::identity(curve); + } + + return {}; + } + + /** + * Return the affine x coordinate + */ + const GenericField& x() const { return m_x; } + + /** + * Return the affine y coordinate + */ + const GenericField& y() const { return m_y; } + + /** + * Conditional assignment of an affine point + */ + void conditional_assign(CT::Choice cond, const GenericAffinePoint& pt) { + GenericField::conditional_assign(m_x, m_y, cond, pt.x(), pt.y()); + } + + const GenericPrimeOrderCurve* curve() const { return m_x.curve(); } + + void _const_time_poison() const { CT::poison_all(m_x, m_y); } + + void _const_time_unpoison() const { CT::unpoison_all(m_x, m_y); } + + private: + GenericField m_x; + GenericField m_y; +}; + +class GenericProjectivePoint final { + public: + typedef GenericProjectivePoint Self; + + using FieldElement = GenericField; + + /** + * Convert a point from affine to projective form + */ + static Self from_affine(const GenericAffinePoint& pt) { + auto x = pt.x(); + auto y = pt.y(); + auto z = GenericField::one(x.curve()); + + // If pt is identity (0,0) swap y/z to convert (0,0,1) into (0,1,0) + GenericField::conditional_swap(pt.is_identity(), y, z); + return GenericProjectivePoint(x, y, z); + } + + /** + * Return the identity element + */ + static Self identity(const GenericPrimeOrderCurve* curve) { + return Self(GenericField::zero(curve), GenericField::one(curve), GenericField::zero(curve)); + } + + /** + * Default constructor: the identity element + */ + explicit GenericProjectivePoint(const GenericPrimeOrderCurve* curve) : + m_x(GenericField::zero(curve)), m_y(GenericField::one(curve)), m_z(GenericField::zero(curve)) {} + + /** + * Affine constructor: take x/y coordinates + */ + GenericProjectivePoint(const GenericField& x, const GenericField& y) : + m_x(x), m_y(y), m_z(GenericField::one(m_x.curve())) {} + + /** + * Projective constructor: take x/y/z coordinates + */ + GenericProjectivePoint(const GenericField& x, const GenericField& y, const GenericField& z) : + m_x(x), m_y(y), m_z(z) {} + + friend Self operator+(const Self& a, const Self& b) { return Self::add(a, b); } + + friend Self operator+(const Self& a, const GenericAffinePoint& b) { return Self::add_mixed(a, b); } + + friend Self operator+(const GenericAffinePoint& a, const Self& b) { return Self::add_mixed(b, a); } + + Self& operator+=(const Self& other) { + (*this) = (*this) + other; + return (*this); + } + + Self& operator+=(const GenericAffinePoint& other) { + (*this) = (*this) + other; + return (*this); + } + + CT::Choice is_identity() const { return z().is_zero(); } + + void conditional_assign(CT::Choice cond, const Self& pt) { + GenericField::conditional_assign(m_x, m_y, m_z, cond, pt.x(), pt.y(), pt.z()); + } + + /** + * Mixed (projective + affine) point addition + */ + static Self add_mixed(const Self& a, const GenericAffinePoint& b) { + return point_add_mixed(a, b, GenericField::one(a.curve())); + } + + static Self add_or_sub(const Self& a, const GenericAffinePoint& b, CT::Choice sub) { + return point_add_or_sub_mixed(a, b, sub, GenericField::one(a.curve())); + } + + /** + * Projective point addition + */ + static Self add(const Self& a, const Self& b) { return point_add(a, b); } + + /** + * Iterated point doubling + */ + Self dbl_n(size_t n) const { + if(curve()->_params().a_is_minus_3()) { + return dbl_n_a_minus_3(*this, n); + } else if(curve()->_params().a_is_zero()) { + return dbl_n_a_zero(*this, n); + } else { + const auto A = GenericField::curve_a(curve()); + return dbl_n_generic(*this, A, n); + } + } + + /** + * Point doubling + */ + Self dbl() const { + if(curve()->_params().a_is_minus_3()) { + return dbl_a_minus_3(*this); + } else if(curve()->_params().a_is_zero()) { + return dbl_a_zero(*this); + } else { + const auto A = GenericField::curve_a(curve()); + return dbl_generic(*this, A); + } + } + + /** + * Point negation + */ + Self negate() const { return Self(x(), y().negate(), z()); } + + /** + * Randomize the point representation + * + * Projective coordinates are redundant; if (x,y,z) is a projective + * point then so is (x*r^2,y*r^3,z*r) for any non-zero r. + */ + void randomize_rep(RandomNumberGenerator& rng) { + // In certain contexts we may be called with a Null_RNG; in that case the + // caller is accepting that randomization will not occur + + if(rng.is_seeded()) { + auto r = GenericField::random(curve(), rng); + + auto r2 = r.square(); + auto r3 = r2 * r; + + m_x *= r2; + m_y *= r3; + m_z *= r; + } + } + + /** + * Return the projective x coordinate + */ + const GenericField& x() const { return m_x; } + + /** + * Return the projective y coordinate + */ + const GenericField& y() const { return m_y; } + + /** + * Return the projective z coordinate + */ + const GenericField& z() const { return m_z; } + + const GenericPrimeOrderCurve* curve() const { return m_x.curve(); } + + void _const_time_poison() const { CT::poison_all(m_x, m_y, m_z); } + + void _const_time_unpoison() const { CT::unpoison_all(m_x, m_y, m_z); } + + private: + GenericField m_x; + GenericField m_y; + GenericField m_z; +}; + +namespace { + +class GenericCurve final { + public: + typedef GenericField FieldElement; + typedef GenericScalar Scalar; + typedef GenericAffinePoint AffinePoint; + typedef GenericProjectivePoint ProjectivePoint; + + typedef word WordType; +}; + +class GenericBlindedScalarBits final { + public: + GenericBlindedScalarBits(const GenericScalar& scalar, RandomNumberGenerator& rng, size_t wb) { + BOTAN_ASSERT_NOMSG(wb == 1 || wb == 2 || wb == 3 || wb == 4 || wb == 5 || wb == 6 || wb == 7); + + const auto& params = scalar.curve()->_params(); + + const size_t order_bits = params.order_bits(); + m_window_bits = wb; + + const size_t blinder_bits = scalar_blinding_bits(order_bits); + + if(blinder_bits > 0 && rng.is_seeded()) { + const size_t mask_words = (blinder_bits + WordInfo::bits - 1) / WordInfo::bits; + const size_t mask_bytes = mask_words * WordInfo::bytes; + + const size_t words = params.words(); + + secure_vector maskb(mask_bytes); + rng.randomize(maskb); + + std::array mask{}; + load_le(mask.data(), maskb.data(), mask_words); + + // Mask to exactly blinder_bits and set MSB and LSB + const size_t excess = mask_words * WordInfo::bits - blinder_bits; + if(excess > 0) { + mask[mask_words - 1] &= (static_cast(1) << (WordInfo::bits - excess)) - 1; + } + const size_t msb_pos = (blinder_bits - 1) % WordInfo::bits; + mask[(blinder_bits - 1) / WordInfo::bits] |= static_cast(1) << msb_pos; + mask[0] |= 1; + + std::array mask_n{}; + + const auto sw = scalar.to_words(); + + // Compute masked scalar s + k*n + params.mul(mask_n, mask, params.order()); + bigint_add2(mask_n.data(), 2 * words, sw.data(), words); + + std::reverse(mask_n.begin(), mask_n.end()); + m_bytes = store_be>(mask_n); + m_bits = order_bits + blinder_bits; + } else { + // No RNG available, skip blinding + m_bytes = scalar.serialize>(); + m_bits = order_bits; + } + + m_windows = (m_bits + wb - 1) / wb; + } + + size_t windows() const { return m_windows; } + + size_t bits() const { return m_bits; } + + size_t get_window(size_t offset) const { + if(m_window_bits == 1) { + return read_window_bits<1>(std::span{m_bytes}, offset); + } else if(m_window_bits == 2) { + return read_window_bits<2>(std::span{m_bytes}, offset); + } else if(m_window_bits == 3) { + return read_window_bits<3>(std::span{m_bytes}, offset); + } else if(m_window_bits == 4) { + return read_window_bits<4>(std::span{m_bytes}, offset); + } else if(m_window_bits == 5) { + return read_window_bits<5>(std::span{m_bytes}, offset); + } else if(m_window_bits == 6) { + return read_window_bits<6>(std::span{m_bytes}, offset); + } else if(m_window_bits == 7) { + return read_window_bits<7>(std::span{m_bytes}, offset); + } else { + BOTAN_ASSERT_UNREACHABLE(); + } + } + + private: + std::vector m_bytes; + size_t m_bits; + size_t m_windows; + size_t m_window_bits; +}; + +class GenericWindowedMul final { + public: + static constexpr size_t WindowBits = VarPointWindowBits; + static constexpr size_t TableSize = (1 << WindowBits) - 1; + + explicit GenericWindowedMul(const GenericAffinePoint& pt) : + m_table(varpoint_setup(pt)) {} + + GenericProjectivePoint mul(const GenericScalar& s, RandomNumberGenerator& rng) { + const GenericBlindedScalarBits bits(s, rng, WindowBits); + + return varpoint_exec(m_table, bits, rng); + } + + private: + AffinePointTable m_table; +}; + +} // namespace + +class GenericBaseMulTable final { + public: + static constexpr size_t WindowBits = BasePointWindowBits; + + // +1 for Booth carry from the top window + explicit GenericBaseMulTable(const GenericAffinePoint& pt) : + m_table(basemul_booth_setup(pt, blinded_scalar_bits(*pt.curve()) + 1)) {} + + GenericProjectivePoint mul(const GenericScalar& s, RandomNumberGenerator& rng) { + // W+1 bit windows for Booth recoding overlap + const GenericBlindedScalarBits scalar(s, rng, WindowBits + 1); + return basemul_booth_exec(m_table, scalar, rng); + } + + private: + static size_t blinded_scalar_bits(const GenericPrimeOrderCurve& curve) { + const size_t order_bits = curve.order_bits(); + return order_bits + scalar_blinding_bits(order_bits); + } + + std::vector m_table; +}; + +namespace { + +class GenericWindowedMul2 final { + public: + static constexpr size_t WindowBits = Mul2PrecompWindowBits; + + GenericWindowedMul2(const GenericWindowedMul2& other) = delete; + GenericWindowedMul2(GenericWindowedMul2&& other) = delete; + GenericWindowedMul2& operator=(const GenericWindowedMul2& other) = delete; + GenericWindowedMul2& operator=(GenericWindowedMul2&& other) = delete; + + ~GenericWindowedMul2() = default; + + GenericWindowedMul2(const GenericAffinePoint& p, const GenericAffinePoint& q) : + m_table(mul2_setup(p, q)) {} + + GenericProjectivePoint mul2(const GenericScalar& x, const GenericScalar& y, RandomNumberGenerator& rng) const { + const GenericBlindedScalarBits x_bits(x, rng, WindowBits); + const GenericBlindedScalarBits y_bits(y, rng, WindowBits); + return mul2_exec(m_table, x_bits, y_bits, rng); + } + + private: + AffinePointTable m_table; +}; + +class GenericVartimeWindowedMul2 final : public PrimeOrderCurve::PrecomputedMul2Table { + public: + static constexpr size_t WindowBits = Mul2PrecompWindowBits; + + GenericVartimeWindowedMul2(const GenericVartimeWindowedMul2& other) = delete; + GenericVartimeWindowedMul2(GenericVartimeWindowedMul2&& other) = delete; + GenericVartimeWindowedMul2& operator=(const GenericVartimeWindowedMul2& other) = delete; + GenericVartimeWindowedMul2& operator=(GenericVartimeWindowedMul2&& other) = delete; + + ~GenericVartimeWindowedMul2() override = default; + + GenericVartimeWindowedMul2(const GenericAffinePoint& p, const GenericAffinePoint& q) : + m_table(to_affine_batch(mul2_setup(p, q))) {} + + GenericProjectivePoint mul2_vartime(const GenericScalar& x, const GenericScalar& y) const { + const auto x_bits = x.serialize>(); + const auto y_bits = y.serialize>(); + + const auto& curve = m_table[0].curve(); + auto accum = GenericProjectivePoint(curve); + + const size_t order_bits = curve->order_bits(); + + const size_t windows = (order_bits + WindowBits - 1) / WindowBits; + + for(size_t i = 0; i != windows; ++i) { + auto x_i = read_window_bits(std::span{x_bits}, (windows - i - 1) * WindowBits); + auto y_i = read_window_bits(std::span{y_bits}, (windows - i - 1) * WindowBits); + + if(i > 0) { + accum = accum.dbl_n(WindowBits); + } + + const size_t idx = (y_i << WindowBits) + x_i; + + if(idx > 0) { + accum += m_table[idx - 1]; + } + } + + return accum; + } + + private: + std::vector m_table; +}; + +} // namespace + +GenericPrimeOrderCurve::GenericPrimeOrderCurve( + const BigInt& p, const BigInt& a, const BigInt& b, const BigInt& base_x, const BigInt& base_y, const BigInt& order) : + m_params(std::make_unique(p, a, b, base_x, base_y, order)) {} + +void GenericPrimeOrderCurve::_precompute_base_mul() { + BOTAN_STATE_CHECK(m_basemul == nullptr); + m_basemul = std::make_unique(from_stash(generator())); +} + +size_t GenericPrimeOrderCurve::order_bits() const { + return _params().order_bits(); +} + +size_t GenericPrimeOrderCurve::scalar_bytes() const { + return _params().order_bytes(); +} + +size_t GenericPrimeOrderCurve::field_element_bytes() const { + return _params().field_bytes(); +} + +PrimeOrderCurve::ProjectivePoint GenericPrimeOrderCurve::mul_by_g(const Scalar& scalar, + RandomNumberGenerator& rng) const { + BOTAN_STATE_CHECK(m_basemul != nullptr); + return stash(m_basemul->mul(from_stash(scalar), rng)); +} + +PrimeOrderCurve::Scalar GenericPrimeOrderCurve::base_point_mul_x_mod_order(const Scalar& scalar, + RandomNumberGenerator& rng) const { + BOTAN_STATE_CHECK(m_basemul != nullptr); + auto pt_s = m_basemul->mul(from_stash(scalar), rng); + BOTAN_STATE_CHECK(!pt_s.is_identity().as_bool()); + const auto x_bytes = to_affine_x(pt_s).serialize>(); + if(auto s = GenericScalar::from_wide_bytes(this, x_bytes)) { + return stash(*s); + } else { + throw Internal_Error("Failed to convert x coordinate to integer modulo scalar"); + } +} + +PrimeOrderCurve::ProjectivePoint GenericPrimeOrderCurve::mul(const AffinePoint& pt, + const Scalar& scalar, + RandomNumberGenerator& rng) const { + GenericWindowedMul pt_table(from_stash(pt)); + return stash(pt_table.mul(from_stash(scalar), rng)); +} + +secure_vector GenericPrimeOrderCurve::mul_x_only(const AffinePoint& pt, + const Scalar& scalar, + RandomNumberGenerator& rng) const { + GenericWindowedMul pt_table(from_stash(pt)); + auto pt_s = pt_table.mul(from_stash(scalar), rng); + BOTAN_STATE_CHECK(!pt_s.is_identity().as_bool()); + return to_affine_x(pt_s).serialize>(); +} + +std::unique_ptr GenericPrimeOrderCurve::mul2_setup_g( + const AffinePoint& q) const { + return std::make_unique(from_stash(generator()), from_stash(q)); +} + +std::optional GenericPrimeOrderCurve::mul2_vartime(const PrecomputedMul2Table& tableb, + const Scalar& s1, + const Scalar& s2) const { + const auto& tbl = dynamic_cast(tableb); + auto pt = tbl.mul2_vartime(from_stash(s1), from_stash(s2)); + if(pt.is_identity().as_bool()) { + return {}; + } else { + return stash(pt); + } +} + +std::optional GenericPrimeOrderCurve::mul_px_qy( + const AffinePoint& p, const Scalar& x, const AffinePoint& q, const Scalar& y, RandomNumberGenerator& rng) const { + const GenericWindowedMul2 table(from_stash(p), from_stash(q)); + auto pt = table.mul2(from_stash(x), from_stash(y), rng); + if(pt.is_identity().as_bool()) { + return {}; + } else { + return stash(pt); + } +} + +bool GenericPrimeOrderCurve::mul2_vartime_x_mod_order_eq(const PrecomputedMul2Table& tableb, + const Scalar& v, + const Scalar& s1, + const Scalar& s2) const { + const auto& tbl = dynamic_cast(tableb); + auto pt = tbl.mul2_vartime(from_stash(s1), from_stash(s2)); + + if(!pt.is_identity().as_bool()) { + const auto z2 = pt.z().square(); + + const auto v_bytes = from_stash(v).serialize>(); + + if(auto fe_v = GenericField::deserialize(this, v_bytes)) { + if((*fe_v * z2 == pt.x()).as_bool()) { + return true; + } + + if(_params().order_is_less_than_field()) { + const auto n = GenericField::from_words(this, _params().order()); + const auto neg_n = n.negate().to_words(); + + const auto vw = fe_v->to_words(); + if(bigint_ct_is_lt(vw.data(), vw.size(), neg_n.data(), neg_n.size()).as_bool()) { + return (((*fe_v + n) * z2) == pt.x()).as_bool(); + } + } + } + } + + return false; +} + +PrimeOrderCurve::AffinePoint GenericPrimeOrderCurve::generator() const { + return PrimeOrderCurve::AffinePoint::_create(shared_from_this(), _params().base_x(), _params().base_y()); +} + +PrimeOrderCurve::AffinePoint GenericPrimeOrderCurve::point_to_affine(const ProjectivePoint& pt) const { + auto affine = to_affine(from_stash(pt)); + + const auto y2 = affine.y().square(); + const auto x3_ax_b = GenericCurve::AffinePoint::x3_ax_b(affine.x()); + const auto valid_point = affine.is_identity() || (y2 == x3_ax_b); + + BOTAN_ASSERT(valid_point.as_bool(), "Computed point is on the curve"); + + return stash(affine); +} + +PrimeOrderCurve::ProjectivePoint GenericPrimeOrderCurve::point_add(const AffinePoint& a, const AffinePoint& b) const { + return stash(GenericProjectivePoint::from_affine(from_stash(a)) + from_stash(b)); +} + +PrimeOrderCurve::AffinePoint GenericPrimeOrderCurve::point_negate(const AffinePoint& pt) const { + return stash(from_stash(pt).negate()); +} + +bool GenericPrimeOrderCurve::affine_point_is_identity(const AffinePoint& pt) const { + return from_stash(pt).is_identity().as_bool(); +} + +void GenericPrimeOrderCurve::serialize_point(std::span bytes, const AffinePoint& pt) const { + from_stash(pt).serialize_to(bytes); +} + +void GenericPrimeOrderCurve::serialize_scalar(std::span bytes, const Scalar& scalar) const { + BOTAN_ARG_CHECK(bytes.size() == _params().order_bytes(), "Invalid length to serialize_scalar"); + from_stash(scalar).serialize_to(bytes); +} + +std::optional GenericPrimeOrderCurve::deserialize_scalar( + std::span bytes) const { + if(auto s = GenericScalar::deserialize(this, bytes)) { + if(s->is_nonzero().as_bool()) { + return stash(s.value()); + } + } + + return {}; +} + +std::optional GenericPrimeOrderCurve::scalar_from_wide_bytes( + std::span bytes) const { + if(auto s = GenericScalar::from_wide_bytes(this, bytes)) { + return stash(s.value()); + } else { + return {}; + } +} + +std::optional GenericPrimeOrderCurve::deserialize_point( + std::span bytes) const { + if(auto pt = GenericAffinePoint::deserialize(this, bytes)) { + return stash(pt.value()); + } else { + return {}; + } +} + +PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_add(const Scalar& a, const Scalar& b) const { + return stash(from_stash(a) + from_stash(b)); +} + +PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_sub(const Scalar& a, const Scalar& b) const { + return stash(from_stash(a) - from_stash(b)); +} + +PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_mul(const Scalar& a, const Scalar& b) const { + return stash(from_stash(a) * from_stash(b)); +} + +PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_square(const Scalar& s) const { + return stash(from_stash(s).square()); +} + +PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_invert(const Scalar& s) const { + return stash(from_stash(s).invert()); +} + +PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_invert_vartime(const Scalar& s) const { + return stash(from_stash(s).invert_vartime()); +} + +PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_negate(const Scalar& s) const { + return stash(from_stash(s).negate()); +} + +bool GenericPrimeOrderCurve::scalar_is_zero(const Scalar& s) const { + return from_stash(s).is_zero().as_bool(); +} + +bool GenericPrimeOrderCurve::scalar_equal(const Scalar& a, const Scalar& b) const { + return (from_stash(a) == from_stash(b)).as_bool(); +} + +PrimeOrderCurve::Scalar GenericPrimeOrderCurve::scalar_one() const { + return stash(GenericScalar::one(this)); +} + +PrimeOrderCurve::Scalar GenericPrimeOrderCurve::random_scalar(RandomNumberGenerator& rng) const { + return stash(GenericScalar::random(this, rng)); +} + +PrimeOrderCurve::Scalar GenericPrimeOrderCurve::stash(const GenericScalar& s) const { + return Scalar::_create(shared_from_this(), s.stash_value()); +} + +GenericScalar GenericPrimeOrderCurve::from_stash(const PrimeOrderCurve::Scalar& s) const { + BOTAN_ARG_CHECK(s._curve().get() == this, "Curve mismatch"); + return GenericScalar(this, s._value()); +} + +PrimeOrderCurve::AffinePoint GenericPrimeOrderCurve::stash(const GenericAffinePoint& pt) const { + auto x_w = pt.x().stash_value(); + auto y_w = pt.y().stash_value(); + return AffinePoint::_create(shared_from_this(), x_w, y_w); +} + +GenericAffinePoint GenericPrimeOrderCurve::from_stash(const PrimeOrderCurve::AffinePoint& pt) const { + BOTAN_ARG_CHECK(pt._curve().get() == this, "Curve mismatch"); + auto x = GenericField(this, pt._x()); + auto y = GenericField(this, pt._y()); + return GenericAffinePoint(x, y); +} + +PrimeOrderCurve::ProjectivePoint GenericPrimeOrderCurve::stash(const GenericProjectivePoint& pt) const { + auto x_w = pt.x().stash_value(); + auto y_w = pt.y().stash_value(); + auto z_w = pt.z().stash_value(); + return ProjectivePoint::_create(shared_from_this(), x_w, y_w, z_w); +} + +GenericProjectivePoint GenericPrimeOrderCurve::from_stash(const PrimeOrderCurve::ProjectivePoint& pt) const { + BOTAN_ARG_CHECK(pt._curve().get() == this, "Curve mismatch"); + auto x = GenericField(this, pt._x()); + auto y = GenericField(this, pt._y()); + auto z = GenericField(this, pt._z()); + return GenericProjectivePoint(x, y, z); +} + +PrimeOrderCurve::AffinePoint GenericPrimeOrderCurve::hash_to_curve_nu( + std::function)> expand_message) const { + BOTAN_UNUSED(expand_message); + throw Not_Implemented("Hash to curve is not implemented for this curve"); +} + +PrimeOrderCurve::ProjectivePoint GenericPrimeOrderCurve::hash_to_curve_ro( + std::function)> expand_message) const { + BOTAN_UNUSED(expand_message); + throw Not_Implemented("Hash to curve is not implemented for this curve"); +} + +std::shared_ptr PCurveInstance::from_params( + const BigInt& p, const BigInt& a, const BigInt& b, const BigInt& base_x, const BigInt& base_y, const BigInt& order) { + // We don't check that p and order are prime here on the assumption this has + // been checked already by EC_Group + + BOTAN_ARG_CHECK(a >= 0 && a < p, "a is invalid"); + BOTAN_ARG_CHECK(b > 0 && b < p, "b is invalid"); + BOTAN_ARG_CHECK(base_x >= 0 && base_x < p, "base_x is invalid"); + BOTAN_ARG_CHECK(base_y >= 0 && base_y < p, "base_y is invalid"); + + const size_t p_bits = p.bits(); + + // Same size restrictions as EC_Group however here we do not require + // exactly the primes for the 521 or 239 bit exceptions; this code + // should work fine with any such prime and we are relying on the higher + // levels to prevent creating such a group in the first place + // + // TODO(Botan4) increase the 128 here to 192 when the corresponding EC_Group constructor is changed + // + if(p_bits != 521 && p_bits != 239 && (p_bits < 128 || p_bits > 512 || p_bits % 32 != 0)) { + return {}; + } + + // We don't want to deal with Shanks-Tonelli in the generic case + if(p % 4 != 3) { + return {}; + } + + // The bit length of the field and order being the same simplifies things + if(p_bits != order.bits()) { + return {}; + } + + auto gpoc = std::make_shared(p, a, b, base_x, base_y, order); + /* + The implementation of this needs to call shared_from_this which is not usable + until after the constructor has completed, so we have to do a two-stage + construction process. This is certainly not so clean but it is contained to + this single file so seems tolerable. + + Alternately we could lazily compute the base mul table but this brings in + locking issues which seem a worse alternative overall. + */ + gpoc->_precompute_base_mul(); + return gpoc; +} + +} // namespace Botan::PCurve diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_generic/pcurves_generic.h botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_generic/pcurves_generic.h --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_generic/pcurves_generic.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_generic/pcurves_generic.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,136 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_PCURVES_GENERIC_H_ +#define BOTAN_PCURVES_GENERIC_H_ + +#include + +#include +#include + +namespace Botan::PCurve { + +class GenericCurveParams; +class GenericScalar; +class GenericAffinePoint; +class GenericProjectivePoint; +class GenericBaseMulTable; + +class GenericPrimeOrderCurve final : public PrimeOrderCurve, + public std::enable_shared_from_this { + public: + // This class should only be created via PCurveInstance::from_params + GenericPrimeOrderCurve(const BigInt& p, + const BigInt& a, + const BigInt& b, + const BigInt& base_x, + const BigInt& base_y, + const BigInt& order); + + size_t order_bits() const override; + + size_t scalar_bytes() const override; + + size_t field_element_bytes() const override; + + ProjectivePoint mul_by_g(const Scalar& scalar, RandomNumberGenerator& rng) const override; + + ProjectivePoint mul(const AffinePoint& pt, const Scalar& scalar, RandomNumberGenerator& rng) const override; + + secure_vector mul_x_only(const AffinePoint& pt, + const Scalar& scalar, + RandomNumberGenerator& rng) const override; + + std::unique_ptr mul2_setup_g(const AffinePoint& q) const override; + + std::optional mul2_vartime(const PrecomputedMul2Table& tableb, + const Scalar& x, + const Scalar& y) const override; + + std::optional mul_px_qy(const AffinePoint& p, + const Scalar& x, + const AffinePoint& q, + const Scalar& y, + RandomNumberGenerator& rng) const override; + + bool mul2_vartime_x_mod_order_eq(const PrecomputedMul2Table& tableb, + const Scalar& v, + const Scalar& s1, + const Scalar& s2) const override; + + Scalar base_point_mul_x_mod_order(const Scalar& scalar, RandomNumberGenerator& rng) const override; + + AffinePoint generator() const override; + + AffinePoint point_to_affine(const ProjectivePoint& pt) const override; + + ProjectivePoint point_add(const AffinePoint& a, const AffinePoint& b) const override; + + AffinePoint point_negate(const AffinePoint& pt) const override; + + bool affine_point_is_identity(const AffinePoint& pt) const override; + + void serialize_point(std::span bytes, const AffinePoint& pt) const override; + + void serialize_scalar(std::span bytes, const Scalar& scalar) const override; + + std::optional deserialize_scalar(std::span bytes) const override; + + std::optional scalar_from_wide_bytes(std::span bytes) const override; + + std::optional deserialize_point(std::span bytes) const override; + + AffinePoint hash_to_curve_nu(std::function)> expand_message) const override; + + ProjectivePoint hash_to_curve_ro(std::function)> expand_message) const override; + + Scalar scalar_add(const Scalar& a, const Scalar& b) const override; + + Scalar scalar_sub(const Scalar& a, const Scalar& b) const override; + + Scalar scalar_mul(const Scalar& a, const Scalar& b) const override; + + Scalar scalar_square(const Scalar& s) const override; + + Scalar scalar_invert(const Scalar& s) const override; + + Scalar scalar_invert_vartime(const Scalar& s) const override; + + Scalar scalar_negate(const Scalar& s) const override; + + bool scalar_is_zero(const Scalar& s) const override; + + bool scalar_equal(const Scalar& a, const Scalar& b) const override; + + Scalar scalar_one() const override; + + Scalar random_scalar(RandomNumberGenerator& rng) const override; + + const GenericCurveParams& _params() const { return *m_params; } + + void _precompute_base_mul(); + + private: + PrimeOrderCurve::Scalar stash(const GenericScalar& s) const; + + PrimeOrderCurve::AffinePoint stash(const GenericAffinePoint& pt) const; + + PrimeOrderCurve::ProjectivePoint stash(const GenericProjectivePoint& pt) const; + + GenericScalar from_stash(const PrimeOrderCurve::Scalar& s) const; + + GenericAffinePoint from_stash(const PrimeOrderCurve::AffinePoint& pt) const; + + GenericProjectivePoint from_stash(const PrimeOrderCurve::ProjectivePoint& pt) const; + + std::unique_ptr m_params; + std::unique_ptr m_basemul; +}; + +} // namespace Botan::PCurve + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_id.h botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_id.h --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_id.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_id.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,76 +0,0 @@ -/* -* (C) 2024 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_PCURVES_ID_H_ -#define BOTAN_PCURVES_ID_H_ - -#include -#include -#include -#include -#include - -namespace Botan { - -#if defined(BOTAN_HAS_ASN1) -class OID; -#endif - -} // namespace Botan - -namespace Botan::PCurve { - -/// Identifier for a named prime order curve -class BOTAN_TEST_API PrimeOrderCurveId final { - public: - enum class Code : uint8_t { - /// secp192r1 aka P-192 - secp192r1, - /// secp224r1 aka P-224 - secp224r1, - /// secp256r1 aka P-256 - secp256r1, - /// secp384r1 aka P-384 - secp384r1, - /// secp521r1 aka P-521 - secp521r1, - /// secp256k1 - secp256k1, - /// brainpool256r1 - brainpool256r1, - brainpool384r1, - brainpool512r1, - frp256v1, - sm2p256v1, - numsp512d1, - }; - - using enum Code; - - Code code() const { return m_code; } - - /// Convert the ID to it's commonly used name (inverse of from_string) - std::string to_string() const; - - PrimeOrderCurveId(Code id) : m_code(id) {} - - /// Map a string to a curve identifier - static std::optional from_string(std::string_view name); - -#if defined(BOTAN_HAS_ASN1) - /// Map an OID to a curve identifier - /// - /// Uses the internal OID table - static std::optional from_oid(const OID& oid); -#endif - - private: - const Code m_code; -}; - -} // namespace Botan::PCurve - -#endif diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_impl/info.txt botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_impl/info.txt --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_impl/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_impl/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + PCURVES_IMPL -> 20240714 - + name -> "Prime Order Curves Implementation Helpers" diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_impl/pcurves_impl.h botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_impl/pcurves_impl.h --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_impl/pcurves_impl.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_impl/pcurves_impl.h 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,5 @@ /* -* (C) 2024,2025 Jack Lloyd +* (C) 2024,2025,2026 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -8,19 +8,39 @@ #define BOTAN_PCURVES_IMPL_H_ #include +#include #include #include +#include +#include +#include #include -#include +#include #include -#if defined(BOTAN_HAS_XMD) - #include -#endif - namespace Botan { /* + * @brief Helper class to pass literal strings to C++ templates + * + * This is a generic utility so it may make sense to move this into utils + * if someday such functionality is useful outside of pcurves. + */ +template +class StringLiteral final { + public: + // NOLINTNEXTLINE(*-explicit-conversions) + consteval StringLiteral(const char (&str)[N]) : value() { + for(size_t i = 0; i != N; ++i) { + value[i] = str[i]; + } + } + + // NOLINTNEXTLINE(*non-private-member-variable*) + char value[N]; +}; + +/* This file implements a system for compile-time instantiation of elliptic curve arithmetic. All computations including point multiplication are implemented to be constant time, @@ -42,8 +62,6 @@ the bells and whistles. */ -namespace { - /** * Montomgomery Representation of Integers * @@ -95,7 +113,7 @@ * Convert an integer into Montgomery representation */ constexpr static std::array to_rep(const std::array& x) { - std::array z; + std::array z; // NOLINT(*-member-init) comba_mul(z.data(), x.data(), R2.data()); return Self::redc(z); } @@ -108,7 +126,7 @@ */ constexpr static std::array wide_to_rep(const std::array& x) { auto redc_x = Self::redc(x); - std::array z; + std::array z; // NOLINT(*-member-init) comba_mul(z.data(), redc_x.data(), R3.data()); return Self::redc(z); } @@ -165,6 +183,7 @@ IntMod(Self&& other) = default; IntMod& operator=(const Self& other) = default; IntMod& operator=(Self&& other) = default; + ~IntMod() = default; /** * Return integer zero @@ -224,13 +243,34 @@ } /** + * Return either this or -this depending on which is even + */ + constexpr Self correct_sign(CT::Choice even) const { + const auto flip = (even != this->is_even()); + return Self::choose(flip, this->negate(), *this); + } + + /** + * Return x or y depending on if choice is set or not + */ + static constexpr Self choose(CT::Choice choice, const Self& x, const Self& y) { + auto r = y; + r.conditional_assign(choice, x); + return r; + } + + /** * Modular addition; return c = a + b */ - friend constexpr Self operator+(const Self& a, const Self& b) { - std::array t; - W carry = bigint_add(t, a.value(), b.value()); + friend constexpr BOTAN_FORCE_INLINE Self operator+(const Self& a, const Self& b) { + std::array t; // NOLINT(*-member-init) + + W carry = 0; + for(size_t i = 0; i != N; ++i) { + t[i] = word_add(a.m_val[i], b.m_val[i], &carry); + } - std::array r; + std::array r; // NOLINT(*-member-init) bigint_monty_maybe_sub(r.data(), carry, t.data(), P.data()); return Self(r); } @@ -238,10 +278,21 @@ /** * Modular subtraction; return c = a - b */ - friend constexpr Self operator-(const Self& a, const Self& b) { - std::array r; - word carry = bigint_sub3(r.data(), a.data(), N, b.data(), N); - bigint_cnd_add(carry, r.data(), N, P.data(), N); + friend constexpr BOTAN_FORCE_INLINE Self operator-(const Self& a, const Self& b) { + std::array r; // NOLINT(*-member-init) + W carry = 0; + for(size_t i = 0; i != N; ++i) { + r[i] = word_sub(a.m_val[i], b.m_val[i], &carry); + } + + const auto mask = CT::Mask::expand(carry).value(); + + carry = 0; + + for(size_t i = 0; i != N; ++i) { + r[i] = word_add(r[i], P[i] & mask, &carry); + } + return Self(r); } @@ -256,38 +307,44 @@ // We could multiply by INV_2 but there is a better way ... std::array t = value(); - W borrow = shift_right<1>(t); + const W borrow = shift_right<1>(t); // If value was odd, add (P/2)+1 - bigint_cnd_add(borrow, t.data(), N, INV_2.data(), N); + const auto mask = CT::Mask::expand(borrow).value(); + + W carry = 0; + + for(size_t i = 0; i != N; ++i) { + t[i] = word_add(t[i], INV_2[i] & mask, &carry); + } return Self(t); } /// Return (*this) multiplied by 2 - constexpr Self mul2() const { + constexpr BOTAN_FORCE_INLINE Self mul2() const { std::array t = value(); - W carry = shift_left<1>(t); + const W carry = shift_left<1>(t); - std::array r; + std::array r; // NOLINT(*-member-init) bigint_monty_maybe_sub(r.data(), carry, t.data(), P.data()); return Self(r); } /// Return (*this) multiplied by 3 - constexpr Self mul3() const { return mul2() + (*this); } + constexpr inline Self mul3() const { return mul2() + (*this); } /// Return (*this) multiplied by 4 - constexpr Self mul4() const { return mul2().mul2(); } + constexpr inline Self mul4() const { return mul2().mul2(); } /// Return (*this) multiplied by 8 - constexpr Self mul8() const { return mul2().mul2().mul2(); } + constexpr inline Self mul8() const { return mul2().mul2().mul2(); } /** * Modular multiplication; return c = a * b */ - friend constexpr Self operator*(const Self& a, const Self& b) { - std::array z; + friend constexpr BOTAN_FORCE_INLINE Self operator*(const Self& a, const Self& b) { + std::array z; // NOLINT(*-member-init) comba_mul(z.data(), a.data(), b.data()); return Self(Rep::redc(z)); } @@ -295,8 +352,8 @@ /** * Modular multiplication; set this to this * other */ - constexpr Self& operator*=(const Self& other) { - std::array z; + constexpr BOTAN_FORCE_INLINE Self& operator*=(const Self& other) { + std::array z; // NOLINT(*-member-init) comba_mul(z.data(), data(), other.data()); m_val = Rep::redc(z); return (*this); @@ -305,13 +362,13 @@ /** * Conditional assignment * - * If `cond` is true, sets `x` to `nx` + * If `cond` is true, sets *this to `nx` */ - static constexpr void conditional_assign(Self& x, CT::Choice cond, const Self& nx) { - const W mask = CT::Mask::from_choice(cond).value(); + constexpr void conditional_assign(CT::Choice cond, const Self& nx) { + const W mask = cond.into_bitmask(); for(size_t i = 0; i != N; ++i) { - x.m_val[i] = choose(mask, nx.m_val[i], x.m_val[i]); + m_val[i] = Botan::choose(mask, nx.m_val[i], m_val[i]); } } @@ -321,11 +378,11 @@ * If `cond` is true, sets `x` to `nx` and `y` to `ny` */ static constexpr void conditional_assign(Self& x, Self& y, CT::Choice cond, const Self& nx, const Self& ny) { - const W mask = CT::Mask::from_choice(cond).value(); + const W mask = cond.into_bitmask(); for(size_t i = 0; i != N; ++i) { - x.m_val[i] = choose(mask, nx.m_val[i], x.m_val[i]); - y.m_val[i] = choose(mask, ny.m_val[i], y.m_val[i]); + x.m_val[i] = Botan::choose(mask, nx.m_val[i], x.m_val[i]); + y.m_val[i] = Botan::choose(mask, ny.m_val[i], y.m_val[i]); } } @@ -336,12 +393,28 @@ */ static constexpr void conditional_assign( Self& x, Self& y, Self& z, CT::Choice cond, const Self& nx, const Self& ny, const Self& nz) { - const W mask = CT::Mask::from_choice(cond).value(); + const W mask = cond.into_bitmask(); for(size_t i = 0; i != N; ++i) { - x.m_val[i] = choose(mask, nx.m_val[i], x.m_val[i]); - y.m_val[i] = choose(mask, ny.m_val[i], y.m_val[i]); - z.m_val[i] = choose(mask, nz.m_val[i], z.m_val[i]); + x.m_val[i] = Botan::choose(mask, nx.m_val[i], x.m_val[i]); + y.m_val[i] = Botan::choose(mask, ny.m_val[i], y.m_val[i]); + z.m_val[i] = Botan::choose(mask, nz.m_val[i], z.m_val[i]); + } + } + + /** + * Conditional swap + * + * If `cond` is true, swaps the values of `x` and `y` + */ + static constexpr void conditional_swap(CT::Choice cond, Self& x, Self& y) { + const W mask = cond.into_bitmask(); + + for(size_t i = 0; i != N; ++i) { + auto nx = Botan::choose(mask, y.m_val[i], x.m_val[i]); + auto ny = Botan::choose(mask, x.m_val[i], y.m_val[i]); + x.m_val[i] = nx; + y.m_val[i] = ny; } } @@ -350,8 +423,8 @@ * * Returns the square of this after modular reduction */ - constexpr Self square() const { - std::array z; + constexpr BOTAN_FORCE_INLINE Self square() const { + std::array z; // NOLINT(*-member-init) comba_sqr(z.data(), this->data()); return Self(Rep::redc(z)); } @@ -364,7 +437,7 @@ * (Alternate view, returns this raised to the 2^nth power) */ constexpr void square_n(size_t n) { - std::array z; + std::array z; // NOLINT(*-member-init) for(size_t i = 0; i != n; ++i) { comba_sqr(z.data(), this->data()); m_val = Rep::redc(z); @@ -377,11 +450,14 @@ * Returns the additive inverse of (*this) */ constexpr Self negate() const { - auto x_is_zero = CT::all_zeros(this->data(), N); + const W x_is_zero = ~CT::all_zeros(this->data(), N).value(); + + std::array r; // NOLINT(*-member-init) + W carry = 0; + for(size_t i = 0; i != N; ++i) { + r[i] = word_sub(P[i] & x_is_zero, m_val[i], &carry); + } - std::array r; - bigint_sub3(r.data(), P.data(), N, this->data(), N); - x_is_zero.if_set_zero_out(r.data(), N); return Self(r); } @@ -415,6 +491,7 @@ tbl[0] = (*this); for(size_t i = 1; i != WindowElements; ++i) { + // Conditional ok: table indexes are public here if(i % 2 == 1) { tbl[i] = tbl[i / 2].square(); } else { @@ -426,6 +503,7 @@ const size_t w0 = read_window_bits(std::span{exp}, (Windows - 1) * WindowBits); + // Conditional ok: this function is variable time if(w0 > 0) { r = tbl[w0 - 1]; } @@ -435,6 +513,7 @@ const size_t w = read_window_bits(std::span{exp}, (Windows - i - 1) * WindowBits); + // Conditional ok: this function is variable time if(w > 0) { r *= tbl[w - 1]; } @@ -459,20 +538,139 @@ constexpr Self invert() const { return pow_vartime(Self::P_MINUS_2); } /** + * Helper for variable time BEEA + * + * Note this function assumes that its arguments are in the standard + * domain, not the Montgomery domain. invert_vartime converts its argument + * out of Montgomery, and then back to Montgomery when returning the result. + */ + static constexpr void _invert_vartime_div2_helper(Self& a, Self& x) { + constexpr auto INV_2 = p_div_2_plus_1(Rep::P); + + // Conditional ok: this function is variable time + while((a.m_val[0] & 1) != 1) { + shift_right<1>(a.m_val); + + const W borrow = shift_right<1>(x.m_val); + + // Conditional ok: this function is variable time + if(borrow) { + bigint_add2(x.m_val.data(), N, INV_2.data(), N); + } + } + } + + /** + * Returns the modular inverse, or 0 if no modular inverse exists. + * + * This function assumes that the modulus is prime + * + * This function does something a bit nasty and converts from the normal + * representation (for scalars, Montgomery) into the "standard" + * representation. This relies on the fact that we aren't doing any + * multiplications within this function, just additions, subtractions, + * division by 2, and comparisons. + * + * The reason is there is no good way to compare integers in the Montgomery + * domain; we could convert out for each comparison but this is slower than + * just doing a constant-time inversion. + * + * This is loosely based on the algorithm BoringSSL uses in + * BN_mod_inverse_odd, which is a variant of the Binary Extended Euclidean + * algorithm. It is optimized somewhat by taking advantage of a couple of + * observations. + * + * In the first two iterations, the control flow is known because `a` is + * less than the modulus and not zero, and we know that the modulus is + * odd. So we peel out those iterations. This also avoids having to + * initialize `a` with the modulus, because we instead set it directly to + * what the first loop iteration would have updated it to. This ensures + * that all values are always less than or equal to the modulus. + * + * Then we take advantage of the fact that in each iteration of the loop, + * at the end we update either b/x or a/y, but never both. In the next + * iteration of the loop, we attempt to modify b/x or a/y depending on the + * low zero bits of b or a. But if a or b were not updated in the previous + * iteration than they will still be odd, and nothing will happen. Instead + * update just the pair we need to update, right after writing to b/x or + * a/y resp. + */ + constexpr Self invert_vartime() const { + // Conditional ok: this function is variable time + if(this->is_zero().as_bool()) { + return Self::zero(); + } + + auto x = Self(std::array{1}); // 1 in standard domain + auto b = Self(this->to_words()); // *this in standard domain + + // First loop iteration + Self::_invert_vartime_div2_helper(b, x); + + auto a = b.negate(); + // y += x but y is zero at the outset + auto y = x; + + // First half of second loop iteration + Self::_invert_vartime_div2_helper(a, y); + + for(;;) { + // Conditional ok: this function is variable time + if(a.m_val == b.m_val) { + // At this point it should be that a == b == 1 + auto r = y.negate(); + + // Convert back to Montgomery if required + r.m_val = Rep::to_rep(r.m_val); + return r; + } + + auto nx = x + y; + + /* + * Otherwise either b > a or a > b + * + * If b > a we want to set b to b - a + * Otherwise we want to set a to a - b + * + * Compute r = b - a and check if it underflowed + * If it did not then we are in the b > a path + */ + std::array r; // NOLINT(*-member-init) + const word carry = bigint_sub3(r.data(), b.data(), N, a.data(), N); + + // Conditional ok: this function is variable time + if(carry == 0) { + // b > a + b.m_val = r; + x = nx; + Self::_invert_vartime_div2_helper(b, x); + } else { + // We know this can't underflow because a > b + bigint_sub3(r.data(), a.data(), N, b.data(), N); + a.m_val = r; + y = nx; + Self::_invert_vartime_div2_helper(a, y); + } + } + } + + /** * Return the modular square root if it exists * - * The CT::Choice indicates if the square root exists or not. + * The CT::Option will be unset if the square root does not exist */ - constexpr std::pair sqrt() const { + constexpr CT::Option sqrt() const { if constexpr(Self::P_MOD_4 == 3) { // The easy case for square root is when p == 3 (mod 4) constexpr auto P_PLUS_1_OVER_4 = p_plus_1_over_4(P); auto z = pow_vartime(P_PLUS_1_OVER_4); - const CT::Choice correct = (z.square() == *this); + // Zero out the return value if it would otherwise be incorrect - Self::conditional_assign(z, !correct, Self::zero()); - return {z, correct}; + const CT::Choice correct = (z.square() == *this); + z.conditional_assign(!correct, Self::zero()); + return CT::Option(z, correct); } else { // Shanks-Tonelli, following I.4 in RFC 9380 @@ -502,15 +700,16 @@ for(size_t i = C1_C2.first; i >= 2; i--) { b.square_n(i - 2); const CT::Choice e = b.is_one(); - Self::conditional_assign(z, !e, z * c); + z.conditional_assign(!e, z * c); c.square_n(1); - Self::conditional_assign(t, !e, t * c); + t.conditional_assign(!e, t * c); b = t; } + // Zero out the return value if it would otherwise be incorrect const CT::Choice correct = (z.square() == *this); - Self::conditional_assign(z, !correct, Self::zero()); - return {z, correct}; + z.conditional_assign(!correct, Self::zero()); + return CT::Option(z, correct); } } @@ -591,12 +790,14 @@ * also rejected. */ static std::optional deserialize(std::span bytes) { + // Conditional ok: input length is public if(bytes.size() != Self::BYTES) { return {}; } const auto words = bytes_to_words(bytes.first()); + // Conditional acceptable: std::optional is implicitly not constant time if(!bigint_ct_is_lt(words.data(), N, P.data(), N).as_bool()) { return {}; } @@ -626,6 +827,7 @@ * modular reduces it. */ static constexpr std::optional from_wide_bytes_varlen(std::span bytes) { + // Conditional ok: input length is public if(bytes.size() > 2 * Self::BYTES) { return {}; } @@ -649,7 +851,7 @@ static Self random(RandomNumberGenerator& rng) { constexpr size_t MAX_ATTEMPTS = 1000; - std::array buf; + std::array buf{}; for(size_t i = 0; i != MAX_ATTEMPTS; ++i) { rng.randomize(buf); @@ -661,6 +863,7 @@ buf[0] &= mask; } + // Conditionals ok: rejection sampling reveals only values we didn't use if(auto s = Self::deserialize(buf)) { if(s.value().is_nonzero().as_bool()) { return s.value(); @@ -677,7 +880,7 @@ * Notice this function is consteval, and so can only be called at compile time */ static consteval Self constant(int8_t x) { - std::array v; + std::array v{}; v[0] = (x >= 0) ? x : -x; auto s = Self::from_words(v); return (x >= 0) ? s : s.negate(); @@ -702,32 +905,33 @@ * * This contains a pair of integers (x,y) which satisfy the curve equation */ -template +template class AffineCurvePoint final { public: - // We can't pass a FieldElement directly because FieldElement is - // not "structural" due to having private members, so instead - // recreate it here from the words. - static constexpr FieldElement A = FieldElement::from_words(Params::AW); - static constexpr FieldElement B = FieldElement::from_words(Params::BW); - static constexpr size_t BYTES = 1 + 2 * FieldElement::BYTES; - static constexpr size_t COMPRESSED_BYTES = 1 + FieldElement::BYTES; - using Self = AffineCurvePoint; + using Self = AffineCurvePoint; + // Note this constructor does not check the validity of the x/y pair + // This must be verified prior to this constructor being called constexpr AffineCurvePoint(const FieldElement& x, const FieldElement& y) : m_x(x), m_y(y) {} constexpr AffineCurvePoint() : m_x(FieldElement::zero()), m_y(FieldElement::zero()) {} static constexpr Self identity() { return Self(FieldElement::zero(), FieldElement::zero()); } + // Helper for ct_select of pcurves_generic + static constexpr Self identity(const Self& /*unused*/) { + return Self(FieldElement::zero(), FieldElement::zero()); + } + constexpr CT::Choice is_identity() const { return x().is_zero() && y().is_zero(); } AffineCurvePoint(const Self& other) = default; AffineCurvePoint(Self&& other) = default; AffineCurvePoint& operator=(const Self& other) = default; AffineCurvePoint& operator=(Self&& other) = default; + ~AffineCurvePoint() = default; constexpr Self negate() const { return Self(x(), y().negate()); } @@ -744,33 +948,12 @@ } /** - * Serialize the point in compressed format - */ - constexpr void serialize_compressed_to(std::span bytes) const { - BOTAN_STATE_CHECK(this->is_identity().as_bool() == false); - const uint8_t hdr = CT::Mask::from_choice(y().is_even()).select(0x02, 0x03); - - BufferStuffer pack(bytes); - pack.append(hdr); - x().serialize_to(pack.next()); - BOTAN_DEBUG_ASSERT(pack.full()); - } - - /** - * Serialize the affine x coordinate only - */ - constexpr void serialize_x_to(std::span bytes) const { - BOTAN_STATE_CHECK(this->is_identity().as_bool() == false); - x().serialize_to(bytes); - } - - /** * If idx is zero then return the identity element. Otherwise return pts[idx - 1] * * Returns the identity element also if idx is out of range */ static constexpr auto ct_select(std::span pts, size_t idx) { - auto result = Self::identity(); + auto result = Self::identity(pts[0]); // Intentionally wrapping; set to maximum size_t if idx == 0 const size_t idx1 = static_cast(idx - 1); @@ -783,68 +966,6 @@ } /** - * Return (x^3 + A*x + B) mod p - */ - static constexpr FieldElement x3_ax_b(const FieldElement& x) { return (x.square() + Self::A) * x + Self::B; } - - /** - * Point deserialization - * - * This accepts compressed or uncompressed formats. - * - * It also currently accepts the deprecated hybrid format. - * TODO(Botan4): remove support for decoding hybrid points - */ - static std::optional deserialize(std::span bytes) { - if(bytes.size() == Self::BYTES) { - if(bytes[0] == 0x04) { - auto x = FieldElement::deserialize(bytes.subspan(1, FieldElement::BYTES)); - auto y = FieldElement::deserialize(bytes.subspan(1 + FieldElement::BYTES, FieldElement::BYTES)); - - if(x && y) { - const auto lhs = (*y).square(); - const auto rhs = Self::x3_ax_b(*x); - if((lhs == rhs).as_bool()) { - return Self(*x, *y); - } - } - } else if(bytes[0] == 0x06 || bytes[0] == 0x07) { - // Deprecated "hybrid" encoding - const CT::Choice y_is_even = CT::Mask::is_equal(bytes[0], 0x06).as_choice(); - auto x = FieldElement::deserialize(bytes.subspan(1, FieldElement::BYTES)); - auto y = FieldElement::deserialize(bytes.subspan(1 + FieldElement::BYTES, FieldElement::BYTES)); - - if(x && y && (y_is_even == y->is_even()).as_bool()) { - const auto lhs = (*y).square(); - const auto rhs = Self::x3_ax_b(*x); - if((lhs == rhs).as_bool()) { - return Self(*x, *y); - } - } - } - } else if(bytes.size() == Self::COMPRESSED_BYTES) { - if(bytes[0] == 0x02 || bytes[0] == 0x03) { - const CT::Choice y_is_even = CT::Mask::is_equal(bytes[0], 0x02).as_choice(); - - if(auto x = FieldElement::deserialize(bytes.subspan(1, FieldElement::BYTES))) { - auto [y, is_square] = x3_ax_b(*x).sqrt(); - - if(is_square.as_bool()) { - const auto flip_y = y_is_even != y.is_even(); - FieldElement::conditional_assign(y, flip_y, y.negate()); - return Self(*x, y); - } - } - } - } else if(bytes.size() == 1 && bytes[0] == 0x00) { - // See SEC1 section 2.3.4 - return Self::identity(); - } - - return {}; - } - - /** * Return the affine x coordinate */ constexpr const FieldElement& x() const { return m_x; } @@ -876,7 +997,7 @@ * This uses Jacobian coordinates */ template -class ProjectiveCurvePoint { +class ProjectiveCurvePoint final { public: // We can't pass a FieldElement directly because FieldElement is // not "structural" due to having private members, so instead @@ -887,17 +1008,28 @@ static constexpr bool A_is_minus_3 = (A == FieldElement::constant(-3)).as_bool(); using Self = ProjectiveCurvePoint; - using AffinePoint = AffineCurvePoint; + using AffinePoint = AffineCurvePoint; /** * Convert a point from affine to projective form */ static constexpr Self from_affine(const AffinePoint& pt) { - if(pt.is_identity().as_bool()) { - return Self::identity(); - } else { - return ProjectiveCurvePoint(pt.x(), pt.y()); - } + /* + * If the point is the identity element (x=0, y=0) then instead of + * creating (x, y, 1) = (0, 0, 1) we want our projective identity + * encoding of (0, 1, 0) + * + * Which we can achieve by a conditional swap of y and z if the + * affine point is the identity. + */ + + auto x = pt.x(); + auto y = pt.y(); + auto z = FieldElement::one(); + + FieldElement::conditional_swap(pt.is_identity(), y, z); + + return ProjectiveCurvePoint(x, y, z); } /** @@ -927,6 +1059,7 @@ ProjectiveCurvePoint(Self&& other) = default; ProjectiveCurvePoint& operator=(const Self& other) = default; ProjectiveCurvePoint& operator=(Self&& other) = default; + ~ProjectiveCurvePoint() = default; friend constexpr Self operator+(const Self& a, const Self& b) { return Self::add(a, b); } @@ -956,176 +1089,29 @@ * Mixed (projective + affine) point addition */ constexpr static Self add_mixed(const Self& a, const AffinePoint& b) { - const auto a_is_identity = a.is_identity(); - const auto b_is_identity = b.is_identity(); - if((a_is_identity && b_is_identity).as_bool()) { - return Self::identity(); - } - - /* - https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2 - - Cost: 8M + 3S + 6add + 1*2 - */ - - const auto Z1Z1 = a.z().square(); - const auto U2 = b.x() * Z1Z1; - const auto S2 = b.y() * a.z() * Z1Z1; - const auto H = U2 - a.x(); - const auto r = S2 - a.y(); - - // If r == H == 0 then we are in the doubling case - // For a == -b we compute the correct result because - // H will be zero, leading to Z3 being zero also - if((r.is_zero() && H.is_zero()).as_bool()) { - return a.dbl(); - } - - const auto HH = H.square(); - const auto HHH = H * HH; - const auto V = a.x() * HH; - const auto t2 = r.square(); - const auto t3 = V + V; - const auto t4 = t2 - HHH; - auto X3 = t4 - t3; - const auto t5 = V - X3; - const auto t6 = a.y() * HHH; - const auto t7 = r * t5; - auto Y3 = t7 - t6; - auto Z3 = a.z() * H; - - // if a is identity then return b - FieldElement::conditional_assign(X3, Y3, Z3, a_is_identity, b.x(), b.y(), FieldElement::one()); - - // if b is identity then return a - FieldElement::conditional_assign(X3, Y3, Z3, b_is_identity, a.x(), a.y(), a.z()); + return point_add_mixed(a, b, FieldElement::one()); + } - return Self(X3, Y3, Z3); + // Either add or subtract based on the CT::Choice + constexpr static Self add_or_sub(const Self& a, const AffinePoint& b, CT::Choice sub) { + return point_add_or_sub_mixed(a, b, sub, FieldElement::one()); } /** * Projective point addition */ - constexpr static Self add(const Self& a, const Self& b) { - const auto a_is_identity = a.is_identity(); - const auto b_is_identity = b.is_identity(); - - if((a_is_identity && b_is_identity).as_bool()) { - return Self::identity(); - } - - /* - https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2 - - Cost: 12M + 4S + 6add + 1*2 - */ - - const auto Z1Z1 = a.z().square(); - const auto Z2Z2 = b.z().square(); - const auto U1 = a.x() * Z2Z2; - const auto U2 = b.x() * Z1Z1; - const auto S1 = a.y() * b.z() * Z2Z2; - const auto S2 = b.y() * a.z() * Z1Z1; - const auto H = U2 - U1; - const auto r = S2 - S1; - - // If a == -b then H == 0 && r != 0, in which case - // at the end we'll set z = a.z * b.z * H = 0, resulting - // in the correct output (point at infinity) - if((r.is_zero() && H.is_zero()).as_bool()) { - return a.dbl(); - } - - const auto HH = H.square(); - const auto HHH = H * HH; - const auto V = U1 * HH; - const auto t2 = r.square(); - const auto t3 = V + V; - const auto t4 = t2 - HHH; - auto X3 = t4 - t3; - const auto t5 = V - X3; - const auto t6 = S1 * HHH; - const auto t7 = r * t5; - auto Y3 = t7 - t6; - const auto t8 = b.z() * H; - auto Z3 = a.z() * t8; - - // if a is identity then return b - FieldElement::conditional_assign(X3, Y3, Z3, a_is_identity, b.x(), b.y(), b.z()); - - // if b is identity then return a - FieldElement::conditional_assign(X3, Y3, Z3, b_is_identity, a.x(), a.y(), a.z()); - - return Self(X3, Y3, Z3); - } + constexpr static Self add(const Self& a, const Self& b) { return point_add(a, b); } /** * Iterated point doubling */ constexpr Self dbl_n(size_t n) const { - /* - Repeated doubling using an adaptation of Algorithm 3.23 in - "Guide To Elliptic Curve Cryptography" (Hankerson, Menezes, Vanstone) - - Curiously the book gives the algorithm only for A == -3, but - the largest gains come from applying it to the generic A case, - where it saves 2 squarings per iteration. - - For A == 0 - Pay 1*2 + 1half to save n*(1*4 + 1*8) - - For A == -3: - Pay 2S + 1*2 + 1half to save n*(1A + 1*4 + 1*8) + 1M - - For generic A: - Pay 2S + 1*2 + 1half to save n*(2S + 1*4 + 1*8) - */ - - if constexpr(Self::A_is_zero) { - auto nx = x(); - auto ny = y().mul2(); - auto nz = z(); - - while(n > 0) { - const auto ny2 = ny.square(); - const auto ny4 = ny2.square(); - const auto t1 = nx.square().mul3(); - const auto t2 = nx * ny2; - nx = t1.square() - t2.mul2(); - nz *= ny; - ny = t1 * (t2 - nx).mul2() - ny4; - n--; - } - return Self(nx, ny.div2(), nz); + if constexpr(Self::A_is_minus_3) { + return dbl_n_a_minus_3(*this, n); + } else if constexpr(Self::A_is_zero) { + return dbl_n_a_zero(*this, n); } else { - auto nx = x(); - auto ny = y().mul2(); - auto nz = z(); - auto w = nz.square().square(); - - if constexpr(!Self::A_is_minus_3) { - w *= A; - } - - while(n > 0) { - const auto ny2 = ny.square(); - const auto ny4 = ny2.square(); - FieldElement t1; - if constexpr(Self::A_is_minus_3) { - t1 = (nx.square() - w).mul3(); - } else { - t1 = nx.square().mul3() + w; - } - const auto t2 = nx * ny2; - nx = t1.square() - t2.mul2(); - nz *= ny; - ny = t1 * (t2 - nx).mul2() - ny4; - n--; - if(n > 0) { - w *= ny4; - } - } - return Self(nx, ny.div2(), nz); + return dbl_n_generic(*this, A, n); } } @@ -1133,43 +1119,13 @@ * Point doubling */ constexpr Self dbl() const { - /* - Using https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-1998-cmo-2 - - Cost (generic A): 4M + 6S + 4A + 2*2 + 1*3 + 1*4 + 1*8 - Cost (A == -3): 4M + 4S + 5A + 2*2 + 1*3 + 1*4 + 1*8 - Cost (A == 0): 3M + 4S + 3A + 2*2 + 1*3 + 1*4 + 1*8 - */ - - FieldElement m = FieldElement::zero(); - if constexpr(Self::A_is_minus_3) { - /* - if a == -3 then - 3*x^2 + a*z^4 == 3*x^2 - 3*z^4 == 3*(x^2-z^4) == 3*(x-z^2)*(x+z^2) - - Cost: 1M + 1S + 2A + 1*3 - */ - const auto z2 = z().square(); - m = (x() - z2).mul3() * (x() + z2); + return dbl_a_minus_3(*this); } else if constexpr(Self::A_is_zero) { - // If a == 0 then 3*x^2 + a*z^4 == 3*x^2 - // Cost: 1S + 1*3 - m = x().square().mul3(); + return dbl_a_zero(*this); } else { - // Cost: 1M + 3S + 1A + 1*3 - const auto z2 = z().square(); - m = x().square().mul3() + A * z2.square(); + return dbl_generic(*this, A); } - - // Remaining cost: 3M + 3S + 3A + 2*2 + 1*4 + 1*8 - const auto y2 = y().square(); - const auto s = x().mul4() * y2; - const auto nx = m.square() - s.mul2(); - const auto ny = m * (s - nx) - y2.square().mul8(); - const auto nz = y().mul2() * z(); - - return Self(nx, ny, nz); } /** @@ -1187,6 +1143,7 @@ // In certain contexts we may be called with a Null_RNG; in that case the // caller is accepting that randomization will not occur + // Conditional ok: caller's RNG state (seeded vs not) is presumed public if(rng.is_seeded()) { auto r = FieldElement::random(rng); @@ -1252,7 +1209,7 @@ }; /** -* This exists soley as a hack which somewhat reduces symbol lengths +* This exists solely as a hack which somewhat reduces symbol lengths */ template PI> struct IntParams { @@ -1272,6 +1229,8 @@ public: typedef typename Params::W W; + typedef W WordType; + static constexpr auto PW = Params::PW; static constexpr auto NW = Params::NW; static constexpr auto AW = Params::AW; @@ -1279,15 +1238,17 @@ // Simplifying assumption static_assert(PW.size() == NW.size()); - class ScalarParams final : public IntParams {}; + static constexpr size_t Words = PW.size(); + + class ScalarParams final : public IntParams {}; using Scalar = IntMod>; - class FieldParams final : public IntParams {}; + class FieldParams final : public IntParams {}; using FieldElement = IntMod>; - using AffinePoint = AffineCurvePoint; + using AffinePoint = AffineCurvePoint; using ProjectivePoint = ProjectiveCurvePoint; static constexpr size_t OrderBits = Scalar::BITS; @@ -1306,131 +1267,13 @@ static constexpr bool ValidForSswuHash = (Params::Z != 0 && A.is_nonzero().as_bool() && B.is_nonzero().as_bool() && FieldElement::P_MOD_4 == 3); - static constexpr bool OrderIsLessThanField = bigint_cmp(NW.data(), NW.size(), PW.data(), PW.size()) == -1; -}; - -/** -* Field inversion concept -* -* This concept checks if the FieldElement supports fe_invert2 -*/ -template -concept curve_supports_fe_invert2 = requires(const typename C::FieldElement& fe) { - { C::fe_invert2(fe) } -> std::same_as; -}; - -/** -* Field inversion -* -* Uses the specialized fe_invert2 if available, or otherwise the standard -* (FLT-based) field inversion. -*/ -template -inline constexpr auto invert_field_element(const typename C::FieldElement& fe) { - if constexpr(curve_supports_fe_invert2) { - return C::fe_invert2(fe) * fe; - } else { - return fe.invert(); - } -} - -/** -* Convert a projective point into affine -*/ -template -auto to_affine(const typename C::ProjectivePoint& pt) { - // Not strictly required right? - default should work as long - // as (0,0) is identity and invert returns 0 on 0 - if(pt.is_identity().as_bool()) { - return C::AffinePoint::identity(); - } - - if constexpr(curve_supports_fe_invert2) { - const auto z2_inv = C::fe_invert2(pt.z()); - const auto z3_inv = z2_inv.square() * pt.z(); - return typename C::AffinePoint(pt.x() * z2_inv, pt.y() * z3_inv); - } else { - const auto z_inv = invert_field_element(pt.z()); - const auto z2_inv = z_inv.square(); - const auto z3_inv = z_inv * z2_inv; - return typename C::AffinePoint(pt.x() * z2_inv, pt.y() * z3_inv); - } -} - -/** -* Convert a projective point into affine and return x coordinate only -*/ -template -auto to_affine_x(const typename C::ProjectivePoint& pt) { - if constexpr(curve_supports_fe_invert2) { - return pt.x() * C::fe_invert2(pt.z()); - } else { - const auto z_inv = invert_field_element(pt.z()); - const auto z2_inv = z_inv.square(); - return pt.x() * z2_inv; - } -} - -/** -* Batch projective->affine conversion -*/ -template -auto to_affine_batch(std::span projective) { - typedef typename C::AffinePoint AffinePoint; - typedef typename C::FieldElement FieldElement; - - const size_t N = projective.size(); - std::vector affine(N, AffinePoint::identity()); - - CT::Choice any_identity = CT::Choice::no(); - - for(const auto& pt : projective) { - any_identity = any_identity || pt.is_identity(); - } - - if(N <= 2 || any_identity.as_bool()) { - // If there are identity elements, using the batch inversion gets - // tricky. It can be done, but this should be a rare situation so - // just punt to the serial conversion if it occurs - for(size_t i = 0; i != N; ++i) { - affine[i] = to_affine(projective[i]); - } - } else { - std::vector c(N); - - /* - Batch projective->affine using Montgomery's trick + static constexpr bool OrderIsLessThanField = bigint_cmp(NW.data(), Words, PW.data(), Words) == -1; - See Algorithm 2.26 in "Guide to Elliptic Curve Cryptography" - (Hankerson, Menezes, Vanstone) + /** + * Return (x^3 + A*x + B) mod p */ - - c[0] = projective[0].z(); - for(size_t i = 1; i != N; ++i) { - c[i] = c[i - 1] * projective[i].z(); - } - - auto s_inv = invert_field_element(c[N - 1]); - - for(size_t i = N - 1; i > 0; --i) { - const auto& p = projective[i]; - - const auto z_inv = s_inv * c[i - 1]; - const auto z2_inv = z_inv.square(); - const auto z3_inv = z_inv * z2_inv; - - s_inv = s_inv * p.z(); - - affine[i] = AffinePoint(p.x() * z2_inv, p.y() * z3_inv); - } - - const auto z2_inv = s_inv.square(); - const auto z3_inv = s_inv * z2_inv; - affine[0] = AffinePoint(projective[0].x() * z2_inv, projective[0].y() * z3_inv); - } - - return affine; -} + static constexpr FieldElement x3_ax_b(const FieldElement& x) { return (x.square() + A) * x + B; } +}; /** * Blinded Scalar @@ -1443,75 +1286,47 @@ * an additional precaution to guard against compilers introducing conditional * jumps where not expected. * -* If you would like a "go faster" button, change the BlindingEnabled variable -* below to false. +* If the provided RNG is not seeded, blinding is skipped and the scalar +* is used directly. This allows blinding to be disabled at runtime. */ template class BlindedScalarBits final { private: typedef typename C::W W; - static constexpr bool BlindingEnabled = true; + static constexpr size_t BlindingBits = scalar_blinding_bits(C::OrderBits); - // Decide size of scalar blinding factor based on bitlength of the scalar - // - // This can return any value between 0 and the scalar bit length, as long - // as it is a multiple of the word size. - static constexpr size_t blinding_bits(size_t sb) { - constexpr size_t wb = WordInfo::bits; + static_assert(BlindingBits < C::Scalar::BITS); - static_assert(wb == 32 || wb == 64, "Unexpected W size"); + public: + // Maximum number of bits (used for table sizing) + static constexpr size_t Bits = C::Scalar::BITS + BlindingBits; - if(sb == 521) { - /* - Treat P-521 as if it was a 512 bit field; otherwise it is penalized - by the below computation, using either 160 or 192 bits of blinding - (depending on wb), vs 128 bits used for 512 bit groups. - */ - return blinding_bits(512); - } else { - // For blinding use 1/4 the order, rounded up to the next word - return ((sb / 4 + wb - 1) / wb) * wb; - } - } + size_t bits() const { return m_bits; } + + BlindedScalarBits(const typename C::Scalar& scalar, RandomNumberGenerator& rng) { + if(BlindingBits > 0 && rng.is_seeded()) { + constexpr size_t MaskWords = (BlindingBits + WordInfo::bits - 1) / WordInfo::bits; + constexpr size_t MaskBytes = MaskWords * WordInfo::bytes; - static constexpr size_t BlindingBits = blinding_bits(C::OrderBits); + constexpr size_t n_words = C::Words; - static_assert(BlindingBits % WordInfo::bits == 0); - static_assert(BlindingBits < C::Scalar::BITS); + uint8_t maskb[MaskBytes + (BlindingBits == 0 ? 1 : 0)] = {0}; + rng.randomize(maskb, MaskBytes); - public: - static constexpr size_t Bits = C::Scalar::BITS + (BlindingEnabled ? BlindingBits : 0); - static constexpr size_t Bytes = (Bits + 7) / 8; + W mask[n_words] = {0}; + load_le(mask, maskb, MaskWords); - BlindedScalarBits(const typename C::Scalar& scalar, RandomNumberGenerator& rng) { - if constexpr(BlindingEnabled) { - constexpr size_t mask_words = BlindingBits / WordInfo::bits; - constexpr size_t mask_bytes = mask_words * WordInfo::bytes; - - constexpr size_t n_words = C::NW.size(); - - uint8_t maskb[mask_bytes] = {0}; - if(rng.is_seeded()) { - rng.randomize(maskb, mask_bytes); - } else { - // If we don't have an RNG we don't have many good options. We - // could just omit the blinding entirely, but this changes the - // size of the blinded scalar, which we're expecting otherwise is - // knowable at compile time. So generate a mask by XORing the - // bytes of the scalar together. At worst, it's equivalent to - // omitting the blinding entirely. - - std::array sbytes; - scalar.serialize_to(sbytes); - for(size_t i = 0; i != sbytes.size(); ++i) { - maskb[i % mask_bytes] ^= sbytes[i]; - } + // Mask to exactly BlindingBits + constexpr size_t ExcessBits = MaskWords * WordInfo::bits - BlindingBits; + if constexpr(ExcessBits > 0) { + constexpr W ExcessMask = (static_cast(1) << (WordInfo::bits - ExcessBits)) - 1; + mask[MaskWords - 1] &= ExcessMask; } - W mask[n_words] = {0}; - load_le(mask, maskb, mask_words); - mask[mask_words - 1] |= WordInfo::top_bit; + // Set top and bottom bits of mask + constexpr size_t TopMaskBit = (BlindingBits - 1) % WordInfo::bits; + mask[(BlindingBits - 1) / WordInfo::bits] |= static_cast(1) << TopMaskBit; mask[0] |= 1; W mask_n[2 * n_words] = {0}; @@ -1520,14 +1335,16 @@ // Compute masked scalar s + k*n comba_mul(mask_n, mask, C::NW.data()); - bigint_add2_nc(mask_n, 2 * n_words, sw.data(), sw.size()); + bigint_add2(mask_n, 2 * n_words, sw.data(), sw.size()); std::reverse(mask_n, mask_n + 2 * n_words); m_bytes = store_be>(mask_n); + m_bits = C::Scalar::BITS + BlindingBits; } else { - static_assert(Bytes == C::Scalar::BYTES); - m_bytes.resize(Bytes); - scalar.serialize_to(std::span{m_bytes}.template first()); + // No RNG available, skip blinding + m_bytes.resize(C::Scalar::BYTES); + scalar.serialize_to(std::span{m_bytes}.template first()); + m_bits = C::Scalar::BITS; } CT::poison(m_bytes.data(), m_bytes.size()); @@ -1539,13 +1356,18 @@ } ~BlindedScalarBits() { - secure_scrub_memory(m_bytes.data(), m_bytes.size()); + secure_zeroize_buffer(m_bytes.data(), m_bytes.size()); CT::unpoison(m_bytes.data(), m_bytes.size()); } + BlindedScalarBits(const BlindedScalarBits& other) = delete; + BlindedScalarBits(BlindedScalarBits&& other) = delete; + BlindedScalarBits& operator=(const BlindedScalarBits& other) = delete; + BlindedScalarBits& operator=(BlindedScalarBits&& other) = delete; + private: - // TODO this could be a fixed size array std::vector m_bytes; + size_t m_bits; }; template @@ -1553,7 +1375,7 @@ public: static constexpr size_t Bits = C::Scalar::BITS; - UnblindedScalarBits(const typename C::Scalar& scalar) { scalar.serialize_to(std::span{m_bytes}); } + explicit UnblindedScalarBits(const typename C::Scalar& scalar) { scalar.serialize_to(std::span{m_bytes}); } size_t get_window(size_t offset) const { // Extract a WindowBits sized window out of s, depending on offset. @@ -1564,43 +1386,6 @@ std::array m_bytes; }; -/** -* Base point precomputation table -* -* This algorithm works by precomputing a set of points such that -* the online phase of the point multiplication can be effected by -* a sequence of point additions. -* -* The tables, even for W = 1, are large and costly to precompute, so -* this is only used for the base point. -* -* The online phase of the algorithm uess `ceil(SB/W)` additions, -* and no point doublings. The table is of size -* `ceil(SB + W - 1)/W * ((1 << W) - 1)` -* where SB is the bit length of the (blinded) scalar. -* -* Each window of the scalar is associated with a window in the table. -* The table windows are unique to that offset within the scalar. -* -* The simplest version to understand is when W = 1. There the table -* consists of [P, 2*P, 4*P, ..., 2^N*P] where N is the bit length of -* the group order. The online phase consists of conditionally adding -* table[i] depending on if bit i of the scalar is set or not. -* -* When W = 2, the scalar is examined 2 bits at a time, and the table -* for a window index `I` is [(2^I)*P, (2^(I+1))*P, (2^I+2^(I+1))*P]. -* -* This extends similarly for larger W -* -* At a certain point, the side channel silent table lookup becomes the -* dominating cost -* -* For all W, each window in the table has an implicit element of -* the identity element which is used if the scalar bits were all zero. -* This is omitted to save space; AffinePoint::ct_select is designed -* to assist in this by returning the identity element if its index -* argument is zero, or otherwise it returns table[idx - 1] -*/ template class PrecomputedBaseMulTable final { public: @@ -1611,73 +1396,17 @@ static constexpr size_t WindowBits = W; static_assert(WindowBits >= 1 && WindowBits <= 8); - using BlindedScalar = BlindedScalarBits; - - static constexpr size_t Windows = (BlindedScalar::Bits + WindowBits - 1) / WindowBits; + // W+1 bit extraction windows for Booth recoding overlap + using BlindedScalar = BlindedScalarBits; - static_assert(Windows > 1); - - // 2^W elements, less the identity element - static constexpr size_t WindowElements = (1 << WindowBits) - 1; - - static constexpr size_t TableSize = Windows * WindowElements; - - PrecomputedBaseMulTable(const AffinePoint& p) : m_table{} { - std::vector table; - table.reserve(TableSize); - - auto accum = ProjectivePoint::from_affine(p); - - for(size_t i = 0; i != TableSize; i += WindowElements) { - table.push_back(accum); - - for(size_t j = 1; j != WindowElements; ++j) { - if(j % 2 == 1) { - table.emplace_back(table[i + j / 2].dbl()); - } else { - table.emplace_back(table[i + j - 1] + table[i]); - } - } - - accum = table[i + (WindowElements / 2)].dbl(); - } - - m_table = to_affine_batch(table); - } + // +1 for Booth carry: if the top window's sign bit is set, the + // carry propagates into an extra window + explicit PrecomputedBaseMulTable(const AffinePoint& p) : + m_table(basemul_booth_setup(p, BlindedScalar::Bits + 1)) {} ProjectivePoint mul(const Scalar& s, RandomNumberGenerator& rng) const { - const BlindedScalar bits(s, rng); - - // TODO: C++23 - use std::mdspan to access m_table - auto table = std::span{m_table}; - - auto accum = [&]() { - const size_t w_0 = bits.get_window(0); - const auto tbl_0 = table.first(WindowElements); - auto pt = ProjectivePoint::from_affine(AffinePoint::ct_select(tbl_0, w_0)); - CT::poison(pt); - pt.randomize_rep(rng); - return pt; - }(); - - for(size_t i = 1; i != Windows; ++i) { - const size_t w_i = bits.get_window(WindowBits * i); - const auto tbl_i = table.subspan(WindowElements * i, WindowElements); - - /* - None of these additions can be doublings, because in each iteration, the - discrete logarithms of the points we're selecting out of the table are - larger than the largest possible dlog of accum. - */ - accum += AffinePoint::ct_select(tbl_i, w_i); - - if(i <= 3) { - accum.randomize_rep(rng); - } - } - - CT::unpoison(accum); - return accum; + const BlindedScalar scalar(s, rng); + return basemul_booth_exec(m_table, scalar, rng); } private: @@ -1708,99 +1437,103 @@ // 2^W elements, less the identity element static constexpr size_t TableSize = (1 << WindowBits) - 1; - WindowedMulTable(const AffinePoint& p) : m_table{} { - std::vector table; - table.reserve(TableSize); + explicit WindowedMulTable(const AffinePoint& p) : m_table(varpoint_setup(p)) {} - table.push_back(ProjectivePoint::from_affine(p)); - for(size_t i = 1; i != TableSize; ++i) { - if(i % 2 == 1) { - table.push_back(table[i / 2].dbl()); - } else { - table.push_back(table[i - 1] + p); - } + ProjectivePoint mul(const Scalar& s, RandomNumberGenerator& rng) const { + const BlindedScalar bits(s, rng); + return varpoint_exec(m_table, bits, rng); + } + + private: + std::vector m_table; +}; + +/** +* Precomputed point multiplication table with Booth +*/ +template +class WindowedBoothMulTable final { + public: + typedef typename C::Scalar Scalar; + typedef typename C::AffinePoint AffinePoint; + typedef typename C::ProjectivePoint ProjectivePoint; + + static constexpr size_t TableBits = W; + static_assert(TableBits >= 1 && TableBits <= 7); + + static constexpr size_t WindowBits = TableBits + 1; + + using BlindedScalar = BlindedScalarBits; + + static constexpr size_t compute_full_windows(size_t sb, size_t wb) { + if(sb % wb == 0) { + return (sb - 1) / wb; + } else { + return sb / wb; } + } - m_table = to_affine_batch(table); + static constexpr size_t compute_initial_shift(size_t sb, size_t wb) { + if(sb % wb == 0) { + return wb; + } else { + return sb - (sb / wb) * wb; + } } + // 2^W elements [1*P, 2*P, ..., 2^W*P] + static constexpr size_t TableSize = 1 << TableBits; + + explicit WindowedBoothMulTable(const AffinePoint& p) : m_table(varpoint_setup(p)) {} + ProjectivePoint mul(const Scalar& s, RandomNumberGenerator& rng) const { const BlindedScalar bits(s, rng); - auto accum = [&]() { - const size_t w_0 = bits.get_window((Windows - 1) * WindowBits); - // Guaranteed because we set the high bit of the randomizer - BOTAN_DEBUG_ASSERT(w_0 != 0); - auto pt = ProjectivePoint::from_affine(AffinePoint::ct_select(m_table, w_0)); - CT::poison(pt); - pt.randomize_rep(rng); - return pt; - }(); + const size_t scalar_bits = bits.bits(); + const size_t full_windows = compute_full_windows(scalar_bits + 1, WindowBits); + const size_t initial_shift = compute_initial_shift(scalar_bits + 1, WindowBits); - for(size_t i = 1; i != Windows; ++i) { - accum = accum.dbl_n(WindowBits); - const size_t w_i = bits.get_window((Windows - i - 1) * WindowBits); + BOTAN_DEBUG_ASSERT(full_windows * WindowBits + initial_shift == scalar_bits + 1); + BOTAN_DEBUG_ASSERT(initial_shift > 0); - /* - This point addition cannot be a doubling (except once) + auto accum = ProjectivePoint::identity(); + CT::poison(accum); - Consider the sequence of points that are operated on, and specifically - their discrete logarithms. We start out at the point at infinity - (dlog 0) and then add the initial window which is precisely P*w_0 - - We then perform WindowBits doublings, so accum's dlog at the point - of the addition in the first iteration of the loop (when i == 1) is - at least 2^W * w_0. - - Since we know w_0 > 0, then in every iteration of the loop, accums - dlog will always be greater than the dlog of the table element we - just looked up (something between 0 and 2^W-1), and thus the - addition into accum cannot be a doubling. - - However due to blinding this argument fails, since we perform - multiplications using a scalar that is larger than the group - order. In this case it's possible that the dlog of accum becomes - `order + x` (or, effectively, `x`) and `x` is smaller than 2^W. - In this case, a doubling may occur. Future iterations of the loop - cannot be doublings by the same argument above. Since the blinding - factor is always less than the group order (substantially so), - it is not possible for the dlog of accum to overflow a second time. - */ - accum += AffinePoint::ct_select(m_table, w_i); + for(size_t i = 0; i != full_windows; ++i) { + const size_t idx = scalar_bits - initial_shift - WindowBits * i; + + const size_t w_i = bits.get_window(idx); + const auto [tidx, tneg] = booth_recode(w_i); + + // Conditional ok: loop iteration count is public + if(i == 0) { + accum = ProjectivePoint::from_affine(m_table.ct_select(tidx)); + accum.conditional_assign(tneg, accum.negate()); + } else { + accum = ProjectivePoint::add_or_sub(accum, m_table.ct_select(tidx), tneg); + } + + accum = accum.dbl_n(WindowBits); + // Conditional ok: loop iteration count is public if(i <= 3) { accum.randomize_rep(rng); } } + // final window (note one bit shorter than previous reads) + const size_t w_l = bits.get_window(0) & ((1 << WindowBits) - 1); + const auto [tidx, tneg] = booth_recode(w_l << 1); + accum = ProjectivePoint::add_or_sub(accum, m_table.ct_select(tidx), tneg); + CT::unpoison(accum); return accum; } private: - std::vector m_table; + AffinePointTable m_table; }; -/** -* Effect 2-ary multiplication ie x*G + y*H -* -* This is done using a windowed variant of what is usually called -* Shamir's trick. -* -* The W = 1 case is simple; we precompute an extra point GH = G + H, -* and then examine 1 bit in each of x and y. If one or the other bits -* are set then add G or H resp. If both bits are set, add GH. -* -* The example below is a precomputed table for W=2. The flattened table -* begins at (x_i,y_i) = (1,0), i.e. the identity element is omitted. -* The indices in each cell refer to the cell's location in m_table. -* -* x-> 0 1 2 3 -* 0 |/ (ident) |0 x |1 2x |2 3x | -* 1 |3 y |4 x+y |5 2x+y |6 3x+y | -* y = 2 |7 2y |8 x+2y |9 2(x+y) |10 3x+2y | -* 3 |11 3y |12 x+3y |13 2x+3y |14 3x+3y | -*/ template class WindowedMul2Table final { public: @@ -1811,95 +1544,37 @@ typedef typename C::AffinePoint AffinePoint; typedef typename C::ProjectivePoint ProjectivePoint; - static constexpr size_t WindowBits = W; - - static constexpr size_t WindowSize = (1 << WindowBits); - - // 2^(2*W) elements, less the identity element - static constexpr size_t TableSize = (1 << (2 * WindowBits)) - 1; - - WindowedMul2Table(const AffinePoint& x, const AffinePoint& y) { - std::vector table; - table.reserve(TableSize); - - for(size_t i = 0; i != TableSize; ++i) { - const size_t t_i = (i + 1); - const size_t x_i = t_i % WindowSize; - const size_t y_i = (t_i >> WindowBits) % WindowSize; - - // Returns x_i * x + y_i * y - auto next_tbl_e = [&]() { - if(x_i % 2 == 0 && y_i % 2 == 0) { - // Where possible using doubling (eg indices 1, 7, 9 in - // the table above) - return table[(t_i / 2) - 1].dbl(); - } else if(x_i > 0 && y_i > 0) { - // A combination of x and y - if(x_i == 1) { - return x + table[(y_i << WindowBits) - 1]; - } else if(y_i == 1) { - return table[x_i - 1] + y; - } else { - return table[x_i - 1] + table[(y_i << WindowBits) - 1]; - } - } else if(x_i > 0 && y_i == 0) { - // A multiple of x without a y component - if(x_i == 1) { - // Just x - return ProjectivePoint::from_affine(x); - } else { - // x * x_{i-1} - return x + table[x_i - 1 - 1]; - } - } else if(x_i == 0 && y_i > 0) { - if(y_i == 1) { - // Just y - return ProjectivePoint::from_affine(y); - } else { - // y * y_{i-1} - return y + table[((y_i - 1) << WindowBits) - 1]; - } - } else { - BOTAN_ASSERT_UNREACHABLE(); - } - }; - - table.emplace_back(next_tbl_e()); - } - - m_table = to_affine_batch(table); - } + WindowedMul2Table(const AffinePoint& p, const AffinePoint& q) : m_table(mul2_setup(p, q)) {} /** * Constant time 2-ary multiplication */ ProjectivePoint mul2(const Scalar& s1, const Scalar& s2, RandomNumberGenerator& rng) const { - using BlindedScalar = BlindedScalarBits; + using BlindedScalar = BlindedScalarBits; + const BlindedScalar bits1(s1, rng); + const BlindedScalar bits2(s2, rng); - BlindedScalar bits1(s1, rng); - BlindedScalar bits2(s2, rng); - - constexpr size_t Windows = (BlindedScalar::Bits + WindowBits - 1) / WindowBits; + return mul2_exec(m_table, bits1, bits2, rng); + } - auto accum = ProjectivePoint::identity(); + private: + AffinePointTable m_table; +}; - for(size_t i = 0; i != Windows; ++i) { - if(i > 0) { - accum = accum.dbl_n(WindowBits); - } +template +class VartimeMul2Table final { + public: + // We look at W bits of each scalar per iteration + static_assert(W >= 1 && W <= 4); - const size_t w_1 = bits1.get_window((Windows - i - 1) * WindowBits); - const size_t w_2 = bits2.get_window((Windows - i - 1) * WindowBits); - const size_t window = w_1 + (w_2 << WindowBits); - accum += AffinePoint::ct_select(m_table, window); + static constexpr size_t WindowBits = W; - if(i <= 3) { - accum.randomize_rep(rng); - } - } + using Scalar = typename C::Scalar; + using AffinePoint = typename C::AffinePoint; + using ProjectivePoint = typename C::ProjectivePoint; - return accum; - } + VartimeMul2Table(const AffinePoint& p, const AffinePoint& q) : + m_table(to_affine_batch(mul2_setup(p, q))) {} /** * Variable time 2-ary multiplication @@ -1916,18 +1591,40 @@ const UnblindedScalarBits bits1(s1); const UnblindedScalarBits bits2(s2); - auto accum = ProjectivePoint::identity(); + const bool s1_is_zero = s1.is_zero().as_bool(); + const bool s2_is_zero = s2.is_zero().as_bool(); - for(size_t i = 0; i != Windows; ++i) { - if(i > 0) { - accum = accum.dbl_n(WindowBits); + // Conditional ok: this function is variable time + if(s1_is_zero && s2_is_zero) { + return ProjectivePoint::identity(); + } + + auto [w_0, first_nonempty_window] = [&]() { + for(size_t i = 0; i != Windows; ++i) { + const size_t w_1 = bits1.get_window((Windows - i - 1) * WindowBits); + const size_t w_2 = bits2.get_window((Windows - i - 1) * WindowBits); + const size_t window = w_1 + (w_2 << WindowBits); + // Conditional ok: this function is variable time + if(window > 0) { + return std::make_pair(window, i); + } } + // We checked for s1 == s2 == 0 above, so we must see a window eventually + BOTAN_ASSERT_UNREACHABLE(); + }(); + + BOTAN_ASSERT_NOMSG(w_0 > 0); + auto accum = ProjectivePoint::from_affine(m_table[w_0 - 1]); + + for(size_t i = first_nonempty_window + 1; i < Windows; ++i) { + accum = accum.dbl_n(WindowBits); const size_t w_1 = bits1.get_window((Windows - i - 1) * WindowBits); const size_t w_2 = bits2.get_window((Windows - i - 1) * WindowBits); const size_t window = w_1 + (w_2 << WindowBits); + // Conditional ok: this function is variable time if(window > 0) { accum += m_table[window - 1]; } @@ -1981,25 +1678,19 @@ const auto z2_u4 = z_u2.square(); const auto tv1 = invert_field_element(z2_u4 + z_u2); auto x1 = SSWU_C1() * (C::FieldElement::one() + tv1); - C::FieldElement::conditional_assign(x1, tv1.is_zero(), SSWU_C2()); - const auto gx1 = C::AffinePoint::x3_ax_b(x1); - + x1.conditional_assign(tv1.is_zero(), SSWU_C2()); const auto x2 = z_u2 * x1; - const auto gx2 = C::AffinePoint::x3_ax_b(x2); - // Will be zero if gx1 is not a square - const auto [gx1_sqrt, gx1_is_square] = gx1.sqrt(); - - auto x = x2; // By design one of gx1 and gx2 must be a quadratic residue - auto y = gx2.sqrt().first; + const CT::Option y1 = sqrt_field_element(C::x3_ax_b(x1)); + const CT::Option y2 = sqrt_field_element(C::x3_ax_b(x2)); - C::FieldElement::conditional_assign(x, y, gx1_is_square, x1, gx1_sqrt); + const auto use_y1 = y1.has_value(); - const auto flip_y = y.is_even() != u.is_even(); - C::FieldElement::conditional_assign(y, flip_y, y.negate()); + auto x = C::FieldElement::choose(use_y1, x1, x2); + auto y = C::FieldElement::choose(use_y1, y1.value_or(C::FieldElement::zero()), y2.value_or(C::FieldElement::zero())); - auto pt = typename C::AffinePoint(x, y); + auto pt = typename C::AffinePoint(x, y.correct_sign(u.is_even())); CT::unpoison(pt); return pt; @@ -2008,43 +1699,38 @@ /** * Hash to curve (SSWU); RFC 9380 * -* Hashes the input using XMD and the specified hash function, producing either one or -* two field elements `u`/(`u0`,`u1`) resp. These are then mapped to curve point(s) -* using SSWU, and if a pair of points were generated these are combined using point -* addition. +* This is the Simplified Shallue-van de Woestijne-Ulas (SSWU) map. +* +* The parameter expand_message models the function of RFC 9380 and is provided +* by higher levels. For the curves implemented here it will typically be XMD, +* but could also be an XOF (expand_message_xof) or a MHF like Argon2. +* +* For details see RFC 9380 sections 3, 5.2 and 6.6.2. */ -template +template > ExpandMsg> requires C::ValidForSswuHash -inline auto hash_to_curve_sswu(std::string_view hash, std::span pw, std::span dst) +inline auto hash_to_curve_sswu(const ExpandMsg& expand_message) -> std::conditional_t { -#if defined(BOTAN_HAS_XMD) constexpr size_t SecurityLevel = (C::OrderBits + 1) / 2; constexpr size_t L = (C::PrimeFieldBits + SecurityLevel + 7) / 8; constexpr size_t Cnt = RO ? 2 : 1; - std::array xmd; - - expand_message_xmd(hash, xmd, pw, dst); + std::array uniform_bytes = {}; + expand_message(uniform_bytes); if constexpr(RO) { - const auto u0 = C::FieldElement::from_wide_bytes(std::span(xmd.data(), L)); - const auto u1 = C::FieldElement::from_wide_bytes(std::span(xmd.data() + L, L)); + const auto u0 = C::FieldElement::from_wide_bytes(std::span(uniform_bytes.data(), L)); + const auto u1 = C::FieldElement::from_wide_bytes(std::span(uniform_bytes.data() + L, L)); auto accum = C::ProjectivePoint::from_affine(map_to_curve_sswu(u0)); accum += map_to_curve_sswu(u1); return accum; } else { - const auto u = C::FieldElement::from_wide_bytes(std::span(xmd.data(), L)); + const auto u = C::FieldElement::from_wide_bytes(std::span(uniform_bytes.data(), L)); return map_to_curve_sswu(u); } -#else - BOTAN_UNUSED(hash, pw, dst); - throw Not_Implemented("Hash to curve not available due to missing XMD"); -#endif } -} // namespace - } // namespace Botan #endif diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_impl/pcurves_solinas.h botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_impl/pcurves_solinas.h --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_impl/pcurves_solinas.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_impl/pcurves_solinas.h 2026-05-07 01:38:28.000000000 +0000 @@ -47,7 +47,7 @@ static constexpr size_t N32 = N * (WordInfo::bits / 32); - constexpr SolinasAccum(std::array& r) : m_r(r), m_S(0), m_idx(0) {} + constexpr explicit SolinasAccum(std::array& r) : m_r(r) {} constexpr void accum(int64_t v) { BOTAN_DEBUG_ASSERT(m_idx < N32); @@ -73,10 +73,30 @@ private: std::array& m_r; - int64_t m_S; - size_t m_idx; + int64_t m_S = 0; + size_t m_idx = 0; }; +/** +* Set r to r - C. Then if r < 0, add P to r +*/ +template +constexpr inline void solinas_correct_redc(std::array& r, const std::array& P, const std::array& C) { + W borrow = 0; + for(size_t i = 0; i != N; ++i) { + r[i] = word_sub(r[i], C[i], &borrow); + } + + // borrow is either 0 or 1, perfect for setting up a mask without extra work + const W mask = CT::value_barrier(0 - borrow); + + W carry = 0; + + for(size_t i = 0; i != N; ++i) { + r[i] = word_add(r[i], P[i] & mask, &carry); + } +} + } // namespace Botan #endif diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_impl/pcurves_util.h botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_impl/pcurves_util.h --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_impl/pcurves_util.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_impl/pcurves_util.h 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,5 @@ /* -* (C) 2024 Jack Lloyd +* (C) 2024,2025 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -12,8 +12,6 @@ namespace Botan { -namespace { - template inline consteval std::array reduce_mod(const std::array& x, const std::array& p) { std::array r = {0}; @@ -29,25 +27,27 @@ const bool x_b = (x[b_word] >> b_bit) & 1; shift_left<1>(r); + // Conditional ok: this function is consteval if(x_b) { r[0] += 1; } const W carry = bigint_sub3(t.data(), r.data(), N + 1, p.data(), N); + // Conditional ok: this function is consteval if(carry == 0) { std::swap(r, t); } } - std::array rs; + std::array rs = {}; copy_mem(rs, std::span{r}.template first()); return rs; } template inline consteval std::array montygomery_r(const std::array& p) { - std::array x = {0}; + std::array x = {}; x[N] = 1; return reduce_mod(x, p); } @@ -56,7 +56,7 @@ inline consteval std::array mul_mod(const std::array& x, const std::array& y, const std::array& p) { - std::array z; + std::array z = {}; comba_mul(z.data(), x.data(), y.data()); return reduce_mod(z, p); } @@ -65,7 +65,8 @@ inline constexpr auto monty_redc_pdash1(const std::array& z, const std::array& p) -> std::array { static_assert(N >= 1); - std::array ws; + std::array ws; // NOLINT(*-member-init); + std::array r; // NOLINT(*-member-init) word3 accum; @@ -99,7 +100,6 @@ // w1 is the final part, which is not stored in the workspace const W w1 = accum.extract(); - std::array r; bigint_monty_maybe_sub(r.data(), w1, ws.data(), p.data()); return r; @@ -110,7 +110,29 @@ -> std::array { static_assert(N >= 1); - std::array ws; + std::array ws; // NOLINT(*-member-init) + std::array r; // NOLINT(*-member-init) + + // Conditional ok: the parameter size is public + if(!std::is_constant_evaluated()) { + // This range ensures we cover fields of 256, 384 and 512 bits for both 32 and 64 bit words + if constexpr(N == 4) { + bigint_monty_redc_4(r.data(), z.data(), p.data(), p_dash, ws.data()); + return r; + } else if constexpr(N == 6) { + bigint_monty_redc_6(r.data(), z.data(), p.data(), p_dash, ws.data()); + return r; + } else if constexpr(N == 8) { + bigint_monty_redc_8(r.data(), z.data(), p.data(), p_dash, ws.data()); + return r; + } else if constexpr(N == 12) { + bigint_monty_redc_12(r.data(), z.data(), p.data(), p_dash, ws.data()); + return r; + } else if constexpr(N == 16) { + bigint_monty_redc_16(r.data(), z.data(), p.data(), p_dash, ws.data()); + return r; + } + } word3 accum; @@ -144,7 +166,6 @@ // w1 is the final part, which is not stored in the workspace const W w1 = accum.extract(); - std::array r; bigint_monty_maybe_sub(r.data(), w1, ws.data(), p.data()); return r; @@ -154,7 +175,7 @@ inline consteval std::array p_minus(const std::array& p) { // TODO combine into p_plus_x_over_y<-1, 1> static_assert(X > 0); - std::array r; + std::array r{}; W x = X; bigint_sub3(r.data(), p.data(), N, &x, 1); std::reverse(r.begin(), r.end()); @@ -164,8 +185,8 @@ template inline consteval std::array p_plus_1_over_4(const std::array& p) { const W one = 1; - std::array r; - bigint_add3_nc(r.data(), p.data(), N, &one, 1); + std::array r{}; + bigint_add3(r.data(), p.data(), N, &one, 1); shift_right<2>(r); std::reverse(r.begin(), r.end()); return r; @@ -174,7 +195,7 @@ template inline consteval std::array p_minus_1_over_2(const std::array& p) { const W one = 1; - std::array r; + std::array r{}; bigint_sub3(r.data(), p.data(), N, &one, 1); shift_right<1>(r); std::reverse(r.begin(), r.end()); @@ -186,7 +207,7 @@ const W one = 1; std::array r = p; shift_right<1>(r); - bigint_add2_nc(r.data(), N, &one, 1); + bigint_add2(r.data(), N, &one, 1); return r; } @@ -201,6 +222,7 @@ for(;;) { // If we found another one bit past the first, stop + // Conditional ok: this function is consteval if(c2[0] % 2 == 1) { break; } @@ -234,6 +256,7 @@ auto is_square = c.is_zero() || c.is_one(); + // Conditional ok: this function is consteval if(!is_square.as_bool()) { return z; } @@ -252,6 +275,7 @@ size_t b = WordInfo::bits * N; + // Conditional ok: this function is consteval while(get_bit(b - 1) == 0) { b -= 1; } @@ -287,33 +311,6 @@ return r; } -// Extract a WindowBits sized window out of s, depending on offset. -template -constexpr size_t read_window_bits(std::span words, size_t offset) { - static_assert(WindowBits >= 1 && WindowBits <= 7); - - constexpr uint8_t WindowMask = static_cast(1 << WindowBits) - 1; - - constexpr size_t W_bits = sizeof(W) * 8; - const auto bit_shift = offset % W_bits; - const auto word_offset = words.size() - 1 - (offset / W_bits); - - const bool single_byte_window = bit_shift <= (W_bits - WindowBits) || word_offset == 0; - - const auto w0 = words[word_offset]; - - if(single_byte_window) { - return (w0 >> bit_shift) & WindowMask; - } else { - // Otherwise we must join two words and extract the result - const auto w1 = words[word_offset - 1]; - const auto combined = ((w0 >> bit_shift) | (w1 << (W_bits - bit_shift))); - return combined & WindowMask; - } -} - -} // namespace - } // namespace Botan #endif diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_impl/pcurves_wrap.h botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_impl/pcurves_wrap.h --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_impl/pcurves_wrap.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_impl/pcurves_wrap.h 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,7 @@ #ifndef BOTAN_PCURVES_WRAP_H_ #define BOTAN_PCURVES_WRAP_H_ +#include #include #include @@ -25,11 +26,6 @@ template class PrimeOrderCurveImpl final : public PrimeOrderCurve { public: - static constexpr size_t BasePointWindowBits = 5; - static constexpr size_t VarPointWindowBits = 4; - static constexpr size_t Mul2PrecompWindowBits = 3; - static constexpr size_t Mul2WindowBits = 2; - static_assert(C::OrderBits <= PrimeOrderCurve::MaximumBitLength); static_assert(C::PrimeFieldBits <= PrimeOrderCurve::MaximumBitLength); @@ -44,15 +40,17 @@ } ProjectivePoint mul(const AffinePoint& pt, const Scalar& scalar, RandomNumberGenerator& rng) const override { - auto tbl = WindowedMulTable(from_stash(pt)); + auto tbl = WindowedBoothMulTable(from_stash(pt)); return stash(tbl.mul(from_stash(scalar), rng)); } secure_vector mul_x_only(const AffinePoint& pt, const Scalar& scalar, RandomNumberGenerator& rng) const override { - auto tbl = WindowedMulTable(from_stash(pt)); - auto pt_x = to_affine_x(tbl.mul(from_stash(scalar), rng)); + auto tbl = WindowedBoothMulTable(from_stash(pt)); + auto result = tbl.mul(from_stash(scalar), rng); + BOTAN_STATE_CHECK(!result.is_identity().as_bool()); + auto pt_x = to_affine_x(result); secure_vector x_bytes(C::FieldElement::BYTES); pt_x.serialize_to(std::span{x_bytes}); return x_bytes; @@ -66,14 +64,9 @@ m_table(x, y) {} private: - WindowedMul2Table m_table; + VartimeMul2Table m_table; }; - std::unique_ptr mul2_setup(const AffinePoint& p, - const AffinePoint& q) const override { - return std::make_unique(from_stash(p), from_stash(q)); - } - std::unique_ptr mul2_setup_g(const AffinePoint& q) const override { return std::make_unique(C::G, from_stash(q)); } @@ -99,7 +92,7 @@ const AffinePoint& q, const Scalar& y, RandomNumberGenerator& rng) const override { - WindowedMul2Table tbl(from_stash(p), from_stash(q)); + const WindowedMul2Table tbl(from_stash(p), from_stash(q)); auto pt = tbl.mul2(from_stash(x), from_stash(y), rng); if(pt.is_identity().as_bool()) { return {}; @@ -131,11 +124,11 @@ * With overwhelming probability, this conversion is correct. The * only time it is not is in the extremely unlikely case where the * signer actually reduced the x coordinate modulo the group order. - * That is handled seperately in a second step. + * That is handled separately in a second step. */ const auto z2 = pt.z().square(); - std::array v_bytes; + std::array v_bytes{}; from_stash(v).serialize_to(v_bytes); if(const auto fe_v = C::FieldElement::deserialize(v_bytes)) { @@ -187,7 +180,8 @@ Scalar base_point_mul_x_mod_order(const Scalar& scalar, RandomNumberGenerator& rng) const override { auto pt = m_mul_by_g.mul(from_stash(scalar), rng); - std::array x_bytes; + BOTAN_STATE_CHECK(!pt.is_identity().as_bool()); + std::array x_bytes{}; to_affine_x(pt).serialize_to(std::span{x_bytes}); // Reduction might be required (if unlikely) return stash(C::Scalar::from_wide_bytes(std::span{x_bytes})); @@ -196,21 +190,19 @@ AffinePoint generator() const override { return stash(C::G); } AffinePoint point_to_affine(const ProjectivePoint& pt) const override { - return stash(to_affine(from_stash(pt))); - } + auto affine = to_affine(from_stash(pt)); - ProjectivePoint point_to_projective(const AffinePoint& pt) const override { - return stash(C::ProjectivePoint::from_affine(from_stash(pt))); - } + const auto y2 = affine.y().square(); + const auto x3_ax_b = C::x3_ax_b(affine.x()); + const auto valid_point = affine.is_identity() || (y2 == x3_ax_b); - ProjectivePoint point_double(const ProjectivePoint& pt) const override { return stash(from_stash(pt).dbl()); } + BOTAN_ASSERT(valid_point.as_bool(), "Computed point is on the curve"); - ProjectivePoint point_add(const ProjectivePoint& a, const ProjectivePoint& b) const override { - return stash(from_stash(a) + from_stash(b)); + return stash(affine); } - ProjectivePoint point_add_mixed(const ProjectivePoint& a, const AffinePoint& b) const override { - return stash(from_stash(a) + from_stash(b)); + ProjectivePoint point_add(const AffinePoint& a, const AffinePoint& b) const override { + return stash(C::ProjectivePoint::from_affine(from_stash(a)) + from_stash(b)); } AffinePoint point_negate(const AffinePoint& pt) const override { return stash(from_stash(pt).negate()); } @@ -224,17 +216,6 @@ from_stash(pt).serialize_to(bytes.subspan<0, C::AffinePoint::BYTES>()); } - void serialize_point_compressed(std::span bytes, const AffinePoint& pt) const override { - BOTAN_ARG_CHECK(bytes.size() == C::AffinePoint::COMPRESSED_BYTES, - "Invalid length for serialize_point_compressed"); - from_stash(pt).serialize_compressed_to(bytes.subspan<0, C::AffinePoint::COMPRESSED_BYTES>()); - } - - void serialize_point_x(std::span bytes, const AffinePoint& pt) const override { - BOTAN_ARG_CHECK(bytes.size() == C::FieldElement::BYTES, "Invalid length for serialize_point_x"); - from_stash(pt).serialize_x_to(bytes.subspan<0, C::FieldElement::BYTES>()); - } - void serialize_scalar(std::span bytes, const Scalar& scalar) const override { BOTAN_ARG_CHECK(bytes.size() == C::Scalar::BYTES, "Invalid length to serialize_scalar"); return from_stash(scalar).serialize_to(bytes.subspan<0, C::Scalar::BYTES>()); @@ -259,28 +240,54 @@ } std::optional deserialize_point(std::span bytes) const override { - if(auto pt = C::AffinePoint::deserialize(bytes)) { - return stash(*pt); - } else { - return {}; + // The identity element (see SEC1 section 2.3.4) + // TODO(Botan4) remove this - we should reject the identity encoding + if(bytes.size() == 1 && bytes[0] == 0x00) { + return stash(C::AffinePoint::identity()); + } + + constexpr size_t FieldElementBytes = C::FieldElement::BYTES; + constexpr size_t CompressedBytes = C::FieldElement::BYTES + 1; + constexpr size_t UncompressedBytes = 2 * C::FieldElement::BYTES + 1; + + if(bytes.size() == UncompressedBytes && bytes[0] == 0x04) { + const auto encoded_point = bytes.subspan(1); + auto x = C::FieldElement::deserialize(encoded_point.first(FieldElementBytes)); + auto y = C::FieldElement::deserialize(encoded_point.last(FieldElementBytes)); + + if(x && y) { + // Check that y^2 = x^3 + ax + b + const auto lhs = (*y).square(); + const auto rhs = C::x3_ax_b(*x); + const auto valid = (lhs == rhs); + if(valid.as_bool()) { + return stash(typename C::AffinePoint(*x, *y)); + } + } + } else if(bytes.size() == CompressedBytes && (bytes[0] == 0x02 || bytes[0] == 0x03)) { + const CT::Choice y_is_even = CT::Mask::is_equal(bytes[0], 0x02).as_choice(); + + if(auto x = C::FieldElement::deserialize(bytes.subspan(1, FieldElementBytes))) { + if(auto y = sqrt_field_element(C::x3_ax_b(*x)).as_optional_vartime()) { + return stash(typename C::AffinePoint(*x, y->correct_sign(y_is_even))); + } + } } + + return {}; } - AffinePoint hash_to_curve_nu(std::string_view hash, - std::span input, - std::span domain_sep) const override { + AffinePoint hash_to_curve_nu(std::function)> expand_message) const override { if constexpr(C::ValidForSswuHash) { - return stash(hash_to_curve_sswu(hash, input, domain_sep)); + return stash(hash_to_curve_sswu(expand_message)); } else { throw Not_Implemented("Hash to curve is not implemented for this curve"); } } - ProjectivePoint hash_to_curve_ro(std::string_view hash, - std::span input, - std::span domain_sep) const override { + ProjectivePoint hash_to_curve_ro(std::function)> expand_message) const override { if constexpr(C::ValidForSswuHash) { - return stash(hash_to_curve_sswu(hash, input, domain_sep)); + return stash(hash_to_curve_sswu(expand_message)); } else { throw Not_Implemented("Hash to curve is not implemented for this curve"); } @@ -311,12 +318,7 @@ Scalar scalar_invert_vartime(const Scalar& ss) const override { auto s = from_stash(ss); - // TODO take advantage of variable time - if constexpr(curve_supports_scalar_invert) { - return stash(C::scalar_invert(s)); - } else { - return stash(s.invert()); - } + return stash(s.invert_vartime()); } Scalar scalar_negate(const Scalar& s) const override { return stash(from_stash(s).negate()); } @@ -327,8 +329,6 @@ return (from_stash(a) == from_stash(b)).as_bool(); } - Scalar scalar_zero() const override { return stash(C::Scalar::zero()); } - Scalar scalar_one() const override { return stash(C::Scalar::one()); } Scalar random_scalar(RandomNumberGenerator& rng) const override { return stash(C::Scalar::random(rng)); } diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_instance.h botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_instance.h --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_instance.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_instance.h 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,6 @@ /* -* (C) 2024 Jack Lloyd +* This file was automatically generated by ./src/scripts/dev_tools/gen_ec_groups.py on 2026-04-24 +* All manual changes will be lost. Edit the script instead. * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -7,43 +8,77 @@ #ifndef BOTAN_PCURVES_INSTANCE_H_ #define BOTAN_PCURVES_INSTANCE_H_ +#include #include +namespace Botan { + +class BigInt; + +} + namespace Botan::PCurve { class PrimeOrderCurve; class PCurveInstance final { public: - /* - * All functions here are always defined, however if the cooresponding - * curve is not available at build time a default implementation is - * provided in pcurves_instance.cpp that returns a nullptr - */ - - static std::shared_ptr secp192r1(); - - static std::shared_ptr secp224r1(); - +#if defined(BOTAN_HAS_PCURVES_SECP256R1) static std::shared_ptr secp256r1(); +#endif +#if defined(BOTAN_HAS_PCURVES_SECP384R1) static std::shared_ptr secp384r1(); +#endif +#if defined(BOTAN_HAS_PCURVES_SECP521R1) static std::shared_ptr secp521r1(); +#endif - static std::shared_ptr secp256k1(); - +#if defined(BOTAN_HAS_PCURVES_BRAINPOOL256R1) static std::shared_ptr brainpool256r1(); +#endif +#if defined(BOTAN_HAS_PCURVES_BRAINPOOL384R1) static std::shared_ptr brainpool384r1(); +#endif +#if defined(BOTAN_HAS_PCURVES_BRAINPOOL512R1) static std::shared_ptr brainpool512r1(); +#endif +#if defined(BOTAN_HAS_PCURVES_FRP256V1) static std::shared_ptr frp256v1(); +#endif + +#if defined(BOTAN_HAS_PCURVES_SECP192R1) + static std::shared_ptr secp192r1(); +#endif + +#if defined(BOTAN_HAS_PCURVES_SECP224R1) + static std::shared_ptr secp224r1(); +#endif + +#if defined(BOTAN_HAS_PCURVES_SECP256K1) + static std::shared_ptr secp256k1(); +#endif +#if defined(BOTAN_HAS_PCURVES_SM2P256V1) static std::shared_ptr sm2p256v1(); +#endif +#if defined(BOTAN_HAS_PCURVES_NUMSP512D1) static std::shared_ptr numsp512d1(); +#endif + +#if defined(BOTAN_HAS_PCURVES_GENERIC) + static std::shared_ptr from_params(const BigInt& p, + const BigInt& a, + const BigInt& b, + const BigInt& base_x, + const BigInt& base_y, + const BigInt& order); +#endif }; } // namespace Botan::PCurve diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_mul.h botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_mul.h --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_mul.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_mul.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,546 @@ +/* +* (C) 2024,2025,2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_PCURVES_MUL_H_ +#define BOTAN_PCURVES_MUL_H_ + +#include +#include +#include +#include + +namespace Botan { + +class RandomNumberGenerator; + +/* +* Multiplication algorithm window size parameters +*/ + +static constexpr size_t BasePointWindowBits = 6; +static constexpr size_t VarPointWindowBits = 4; +static constexpr size_t Mul2PrecompWindowBits = 3; +static constexpr size_t Mul2WindowBits = 2; + +/** +* Return number of blinding bits to use +* +* This can return any value between 0 and the scalar bit length. +* +* The field arithmetic and scalar multiplication algorithms are anyway written and tested +* to be constant time; blinding is just used as a safety net in the case that the compiler +* rewrites constant time code to include variable time behavior. If utmost performance is +* of concern and you are in a position to test that your specific compiler for your +* specific architecture is not inserting variable time behavior where not expected (for +* example by using the existing valgrind-based CT checking) it is safe to modify this +* function to just return 0, or some very small blinding factor of 1-4 bits. +*/ +constexpr size_t scalar_blinding_bits(size_t scalar_bits) { + // For blinding use 1/8 the order length for most curves; for P-521 we round down a bit + // so the masked scalar fits exactly in 9 or 18 words. + + if(scalar_bits == 521) { + return 55; + } else { + return scalar_bits / 8; + } +} + +/** +* A precomputed table of affine points with constant time lookup +* +* If R is zero then the entire table is scanned for each lookup. +* +* If R is not zero, then the table must be a multiple of R points long. +* Each lookup will be examine a range of length R, as in +* pts[0..R], pts[R..2*R], ... +*/ +template +class AffinePointTable final { + public: + using AffinePoint = typename C::AffinePoint; + using ProjectivePoint = typename C::ProjectivePoint; + using WordType = typename C::WordType; + + static constexpr bool WholeRangeSearch = (R == 0); + + explicit AffinePointTable(std::span pts) { + BOTAN_ASSERT_NOMSG(pts.size() > 1); + + if constexpr(R > 0) { + BOTAN_ASSERT_NOMSG(pts.size() % R == 0); + } + + // TODO scatter/gather with SIMD lookup + m_table = to_affine_batch(pts); + } + + /** + * If idx is zero then return the identity element. Otherwise return pts[idx - 1] + */ + inline AffinePoint ct_select(size_t idx) const + requires(WholeRangeSearch) + { + BOTAN_DEBUG_ASSERT(idx < m_table.size() + 1); + + auto result = AffinePoint::identity(m_table[0]); + + // Intentionally wrapping; set to maximum size_t if idx == 0 + const size_t idx1 = static_cast(idx - 1); + for(size_t i = 0; i != m_table.size(); ++i) { + const auto found = CT::Mask::is_equal(idx1, i).as_choice(); + result.conditional_assign(found, m_table[i]); + } + + return result; + } + + /** + * If idx is zero then return the identity element. Otherwise return pts[idx - 1] + * out of the table subrange pts[iter*R..(iter+1)*R] + */ + inline AffinePoint ct_select(size_t idx, size_t iter) const + requires(!WholeRangeSearch) + { + BOTAN_DEBUG_ASSERT(idx < R + 1); + BOTAN_DEBUG_ASSERT(R * (iter + 1) <= m_table.size()); + + auto result = AffinePoint::identity(m_table[R * iter]); + + // Intentionally wrapping; set to maximum size_t if idx == 0 + const size_t idx1 = static_cast(idx - 1); + for(size_t i = 0; i != R; ++i) { + const auto found = CT::Mask::is_equal(idx1, i).as_choice(); + result.conditional_assign(found, m_table[R * iter + i]); + } + + return result; + } + + private: + std::vector m_table; +}; + +/* +* Base point precomputation table +* +* This algorithm works by precomputing a set of points such that +* the online phase of the point multiplication can be effected by +* a sequence of point additions. +* +* The tables, even for W = 1, are large and costly to precompute, so +* this is only used for the base point. +* +* The online phase of the algorithm uess `ceil(SB/W)` additions, +* and no point doublings. The table is of size +* `ceil(SB + W - 1)/W * ((1 << W) - 1)` +* where SB is the bit length of the (blinded) scalar. +* +* Each window of the scalar is associated with a window in the table. +* The table windows are unique to that offset within the scalar. +* +* The simplest version to understand is when W = 1. There the table +* consists of [P, 2*P, 4*P, ..., 2^N*P] where N is the bit length of +* the group order. The online phase consists of conditionally adding +* table[i] depending on if bit i of the scalar is set or not. +* +* When W = 2, the scalar is examined 2 bits at a time, and the table +* for a window index `I` is [(2^I)*P, (2^(I+1))*P, (2^I+2^(I+1))*P]. +* +* This extends similarly for larger W +* +* At a certain point, the side channel silent table lookup becomes the +* dominating cost +* +* For all W, each window in the table has an implicit element of +* the identity element which is used if the scalar bits were all zero. +* This is omitted to save space; AffinePoint::ct_select is designed +* to assist in this by returning the identity element if its index +* argument is zero, or otherwise it returns table[idx - 1] +*/ +template +std::vector basemul_setup(const typename C::AffinePoint& p, size_t max_scalar_bits) { + static_assert(WindowBits >= 1 && WindowBits <= 8); + + // 2^W elements, less the identity element + constexpr size_t WindowElements = (1 << WindowBits) - 1; + + const size_t Windows = (max_scalar_bits + WindowBits - 1) / WindowBits; + + const size_t TableSize = Windows * WindowElements; + + std::vector table; + table.reserve(TableSize); + + auto accum = C::ProjectivePoint::from_affine(p); + + for(size_t i = 0; i != TableSize; i += WindowElements) { + table.push_back(accum); + + for(size_t j = 1; j != WindowElements; ++j) { + // Conditional ok: loop iteration count is public + if(j % 2 == 1) { + table.emplace_back(table[i + j / 2].dbl()); + } else { + table.emplace_back(table[i + j - 1] + table[i]); + } + } + + accum = table[i + (WindowElements / 2)].dbl(); + } + + // Variable time batch conversion is fine since generator is public + return to_affine_batch(table); +} + +template +typename C::ProjectivePoint basemul_exec(std::span table, + const BlindedScalar& scalar, + RandomNumberGenerator& rng) { + // 2^W elements, less the identity element + static constexpr size_t WindowElements = (1 << WindowBits) - 1; + + // TODO: C++23 - use std::mdspan to access table? + + auto accum = [&]() { + const size_t w_0 = scalar.get_window(0); + const auto tbl_0 = table.first(WindowElements); + auto pt = C::ProjectivePoint::from_affine(C::AffinePoint::ct_select(tbl_0, w_0)); + CT::poison(pt); + pt.randomize_rep(rng); + return pt; + }(); + + const size_t windows = (scalar.bits() + WindowBits - 1) / WindowBits; + + for(size_t i = 1; i != windows; ++i) { + const size_t w_i = scalar.get_window(WindowBits * i); + const auto tbl_i = table.subspan(WindowElements * i, WindowElements); + + /* + None of these additions can be doublings, because in each iteration, the + discrete logarithms of the points we're selecting out of the table are + larger than the largest possible dlog of accum. + */ + accum += C::AffinePoint::ct_select(tbl_i, w_i); + + // Conditional ok: loop iteration count is public + if(i <= 3) { + accum.randomize_rep(rng); + } + } + + CT::unpoison(accum); + return accum; +} + +/* +* Base point precomputation table with Booth recoding +* +* Same structure as basemul, but uses Booth recoding to halve the +* table size per window. Instead of storing 2^W - 1 entries per +* window, we store 2^(W-1) entries (multiples 1..2^(W-1) of the +* window base point). The sign is handled by conditional negation +* after the constant-time table lookup. +* +* The scalar is prepared with one extra blinding bit (WindowBits+1), +* and windows overlap by one bit to allow carry propagation from +* the Booth encoding. +*/ +template +std::vector basemul_booth_setup(const typename C::AffinePoint& p, size_t max_scalar_bits) { + static_assert(WindowBits >= 1 && WindowBits <= 8); + + // 2^(W-1) elements per window [1*base .. 2^(W-1)*base] + constexpr size_t WindowElements = 1 << (WindowBits - 1); + + const size_t Windows = (max_scalar_bits + WindowBits - 1) / WindowBits; + + const size_t TableSize = Windows * WindowElements; + + std::vector table; + table.reserve(TableSize); + + auto accum = C::ProjectivePoint::from_affine(p); + + for(size_t i = 0; i != TableSize; i += WindowElements) { + table.push_back(accum); + + for(size_t j = 1; j != WindowElements; ++j) { + // Conditional ok: loop iteration count is public + if(j % 2 == 1) { + table.emplace_back(table[i + j / 2].dbl()); + } else { + table.emplace_back(table[i + j - 1] + table[i]); + } + } + + // Advance to next window's base: 2^W * current_base + // The last entry is 2^(W-1) * base, so doubling gives 2^W * base + accum = table[i + WindowElements - 1].dbl(); + } + + // Variable time batch conversion is fine since generator is public + return to_affine_batch(table); +} + +/* +* Booth recoding for base point multiplication +*/ +template +constexpr std::pair booth_recode(T x) { + static_assert(WindowBits >= 1 && WindowBits <= 8); + + auto s_mask = CT::Mask::expand(x >> WindowBits); + const T neg_x = (1 << (WindowBits + 1)) - x - 1; + T d = s_mask.select(neg_x, x); + d = (d >> 1) + (d & 1); + + return std::make_pair(static_cast(d), s_mask.as_choice()); +} + +template +typename C::ProjectivePoint basemul_booth_exec(std::span table, + const BlindedScalar& scalar, + RandomNumberGenerator& rng) { + static constexpr size_t WindowElements = 1 << (WindowBits - 1); + + const size_t windows = (scalar.bits() + WindowBits) / WindowBits; + + auto accum = [&]() { + // First window: extract W bits, shift left 1 to insert implicit carry in of zero + const size_t w_bits = scalar.get_window(0) & ((1 << WindowBits) - 1); + const size_t raw = w_bits << 1; + const auto [tidx, tneg] = booth_recode(raw); + const auto tbl_0 = table.first(WindowElements); + + auto pt = C::ProjectivePoint::from_affine(C::AffinePoint::ct_select(tbl_0, tidx)); + pt.conditional_assign(tneg, pt.negate()); + CT::poison(pt); + pt.randomize_rep(rng); + return pt; + }(); + + for(size_t i = 1; i != windows; ++i) { + // Extract W+1 bits overlapping by 1 with the previous window + const size_t bit_pos = WindowBits * i - 1; + const size_t raw = scalar.get_window(bit_pos); + const auto [tidx, tneg] = booth_recode(raw); + + const auto tbl_i = table.subspan(WindowElements * i, WindowElements); + + accum = C::ProjectivePoint::add_or_sub(accum, C::AffinePoint::ct_select(tbl_i, tidx), tneg); + + // Conditional ok: loop iteration count is public + if(i <= 3) { + accum.randomize_rep(rng); + } + } + + CT::unpoison(accum); + return accum; +} + +/* +* Variable point table mul setup and online phase +*/ +template +AffinePointTable varpoint_setup(const typename C::AffinePoint& p) { + static_assert(TableSize > 2); + + std::vector table; + table.reserve(TableSize); + table.push_back(C::ProjectivePoint::from_affine(p)); + + for(size_t i = 1; i != TableSize; ++i) { + // Conditional ok: loop iteration count is public + if(i % 2 == 1) { + table.push_back(table[i / 2].dbl()); + } else { + table.push_back(table[i - 1] + p); + } + } + + return AffinePointTable(table); +} + +template +typename C::ProjectivePoint varpoint_exec(const AffinePointTable& table, + const BlindedScalar& scalar, + RandomNumberGenerator& rng) { + const size_t windows = (scalar.bits() + WindowBits - 1) / WindowBits; + + auto accum = [&]() { + const size_t w_0 = scalar.get_window((windows - 1) * WindowBits); + auto pt = C::ProjectivePoint::from_affine(table.ct_select(w_0)); + CT::poison(pt); + pt.randomize_rep(rng); + return pt; + }(); + + for(size_t i = 1; i != windows; ++i) { + accum = accum.dbl_n(WindowBits); + auto w_i = scalar.get_window((windows - i - 1) * WindowBits); + + /* + This point addition cannot be a doubling (except once) + + Consider the sequence of points that are operated on, and specifically + their discrete logarithms. We start out at the point at infinity + (dlog 0) and then add the initial window which is precisely P*w_0 + + We then perform WindowBits doublings, so accum's dlog at the point + of the addition in the first iteration of the loop (when i == 1) is + at least 2^W * w_0. + + Since we know w_0 > 0, then in every iteration of the loop, accums + dlog will always be greater than the dlog of the table element we + just looked up (something between 0 and 2^W-1), and thus the + addition into accum cannot be a doubling. + + However due to blinding this argument fails, since we perform + multiplications using a scalar that is larger than the group + order. In this case it's possible that the dlog of accum becomes + `order + x` (or, effectively, `x`) and `x` is smaller than 2^W. + In this case, a doubling may occur. Future iterations of the loop + cannot be doublings by the same argument above. Since the blinding + factor is always less than the group order (substantially so), + it is not possible for the dlog of accum to overflow a second time. + */ + + accum += table.ct_select(w_i); + + // Conditional ok: loop iteration count is public + if(i <= 3) { + accum.randomize_rep(rng); + } + } + + CT::unpoison(accum); + return accum; +} + +/* +* Effect 2-ary multiplication ie x*G + y*H +* +* This is done using a windowed variant of what is usually called +* Shamir's trick. +* +* The W = 1 case is simple; we precompute an extra point GH = G + H, +* and then examine 1 bit in each of x and y. If one or the other bits +* are set then add G or H resp. If both bits are set, add GH. +* +* The example below is a precomputed table for W=2. The flattened table +* begins at (x_i,y_i) = (1,0), i.e. the identity element is omitted. +* The indices in each cell refer to the cell's location in m_table. +* +* x-> 0 1 2 3 +* 0 |/ (ident) |0 x |1 2x |2 3x | +* 1 |3 y |4 x+y |5 2x+y |6 3x+y | +* y = 2 |7 2y |8 x+2y |9 2(x+y) |10 3x+2y | +* 3 |11 3y |12 x+3y |13 2x+3y |14 3x+3y | +*/ + +template +std::vector mul2_setup(const typename C::AffinePoint& p, + const typename C::AffinePoint& q) { + static_assert(WindowBits >= 1 && WindowBits <= 4); + + // 2^(2*W) elements, less the identity element + constexpr size_t TableSize = (1 << (2 * WindowBits)) - 1; + constexpr size_t WindowSize = (1 << WindowBits); + + std::vector table; + table.reserve(TableSize); + + for(size_t i = 0; i != TableSize; ++i) { + const size_t t_i = (i + 1); + const size_t p_i = t_i % WindowSize; + const size_t q_i = (t_i >> WindowBits) % WindowSize; + + // Conditionals ok: all based on t_i/p_i/q_i which in turn are derived from public i + + // Returns x_i * x + y_i * y + auto next_tbl_e = [&]() { + if(p_i % 2 == 0 && q_i % 2 == 0) { + // Where possible using doubling (eg indices 1, 7, 9 in + // the table above) + return table[(t_i / 2) - 1].dbl(); + } else if(p_i > 0 && q_i > 0) { + // A combination of p and q + if(p_i == 1) { + return p + table[(q_i << WindowBits) - 1]; + } else if(q_i == 1) { + return table[p_i - 1] + q; + } else { + return table[p_i - 1] + table[(q_i << WindowBits) - 1]; + } + } else if(p_i > 0 && q_i == 0) { + // A multiple of p without a q component + if(p_i == 1) { + // Just p + return C::ProjectivePoint::from_affine(p); + } else { + // p * p_{i-1} + return p + table[p_i - 1 - 1]; + } + } else if(p_i == 0 && q_i > 0) { + if(q_i == 1) { + // Just q + return C::ProjectivePoint::from_affine(q); + } else { + // q * q_{i-1} + return q + table[((q_i - 1) << WindowBits) - 1]; + } + } else { + BOTAN_ASSERT_UNREACHABLE(); + } + }; + + table.emplace_back(next_tbl_e()); + } + + return table; +} + +template +typename C::ProjectivePoint mul2_exec(const AffinePointTable& table, + const BlindedScalar& x, + const BlindedScalar& y, + RandomNumberGenerator& rng) { + const size_t Windows = (x.bits() + WindowBits - 1) / WindowBits; + + auto accum = [&]() { + const size_t w_1 = x.get_window((Windows - 1) * WindowBits); + const size_t w_2 = y.get_window((Windows - 1) * WindowBits); + const size_t window = w_1 + (w_2 << WindowBits); + auto pt = C::ProjectivePoint::from_affine(table.ct_select(window)); + CT::poison(pt); + pt.randomize_rep(rng); + return pt; + }(); + + for(size_t i = 1; i != Windows; ++i) { + accum = accum.dbl_n(WindowBits); + + const size_t w_1 = x.get_window((Windows - i - 1) * WindowBits); + const size_t w_2 = y.get_window((Windows - i - 1) * WindowBits); + const size_t window = w_1 + (w_2 << WindowBits); + accum += table.ct_select(window); + + // Conditional ok: loop iteration count is public + if(i <= 3) { + accum.randomize_rep(rng); + } + } + + CT::unpoison(accum); + return accum; +} + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_numsp512d1/info.txt botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_numsp512d1/info.txt --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_numsp512d1/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_numsp512d1/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + PCURVES_NUMSP512D1 -> 20240723 - + name -> "PCurve numsp512d1" diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_numsp512d1/pcurves_numsp512d1.cpp botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_numsp512d1/pcurves_numsp512d1.cpp --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_numsp512d1/pcurves_numsp512d1.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_numsp512d1/pcurves_numsp512d1.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -37,6 +37,7 @@ }; // clang-format off + class Params final : public EllipticCurveParameters< "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC4", @@ -50,7 +51,7 @@ class Curve final : public EllipticCurve { public: - static FieldElement fe_invert2(const FieldElement& x) { + static constexpr FieldElement fe_invert2(const FieldElement& x) { // Generated by https://github.com/mmcloughlin/addchain auto z = x.square(); z *= x; @@ -92,6 +93,49 @@ z.square_n(2); return z; } + + static constexpr FieldElement fe_sqrt(const FieldElement& x) { + // Generated by https://github.com/mmcloughlin/addchain + auto z = x.square(); + z *= x; + z = z.square(); + z *= x; + auto t0 = z; + t0.square_n(3); + t0 *= z; + t0.square_n(3); + auto t1 = t0 * z; + t0 = t1; + t0.square_n(9); + t0 *= t1; + t0.square_n(3); + t0 *= z; + auto t2 = t0; + t2.square_n(9); + t1 *= t2; + t2 = t1; + t2.square_n(30); + t1 *= t2; + t2 = t1; + t2.square_n(60); + t1 *= t2; + t2 = t1; + t2.square_n(120); + t1 *= t2; + t2 = t1; + t2.square_n(240); + t1 *= t2; + t1.square_n(21); + t0 *= t1; + t0 = t0.square(); + t0 *= x; + t0.square_n(4); + z *= t0; + z.square_n(3); + z *= x; + z = z.square(); + return z; + } }; } // namespace numsp512d1 diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp192r1/info.txt botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp192r1/info.txt --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp192r1/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp192r1/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + PCURVES_SECP192R1 -> 20240709 - + name -> "PCurve secp192r1" diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp192r1/pcurves_secp192r1.cpp botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp192r1/pcurves_secp192r1.cpp --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp192r1/pcurves_secp192r1.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp192r1/pcurves_secp192r1.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -23,6 +23,34 @@ typedef typename Params::W W; constexpr static std::array redc(const std::array& z) { + if constexpr(std::same_as && WordInfo::dword_is_native) { + using dword = typename WordInfo::dword; + + const dword S01 = dword(z[0]) + z[3] + z[5]; + const dword S23 = dword(z[1]) + z[3] + z[4] + z[5]; + const dword S45 = dword(z[2]) + z[4] + z[5]; + + std::array r = {}; + + dword S = S01; + r[0] = static_cast(S); + S >>= 64; + + S += S23; + r[1] = static_cast(S); + S >>= 64; + + S += S45; + r[2] = static_cast(S); + S >>= 64; + + BOTAN_DEBUG_ASSERT(S <= 3); + + solinas_correct_redc(r, P, p192_mul_mod_192(static_cast(S))); + + return r; + } + const int64_t X00 = get_uint32(z.data(), 0); const int64_t X01 = get_uint32(z.data(), 1); const int64_t X02 = get_uint32(z.data(), 2); @@ -57,7 +85,7 @@ BOTAN_DEBUG_ASSERT(S <= 3); - bigint_correct_redc(r, P, p192_mul_mod_192(S)); + solinas_correct_redc(r, P, p192_mul_mod_192(S)); return r; } @@ -94,6 +122,7 @@ }; // clang-format off + class Params final : public EllipticCurveParameters< "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", @@ -105,7 +134,44 @@ // clang-format on -class Curve final : public EllipticCurve {}; +class Curve final : public EllipticCurve { + public: + // Return the square of the inverse of x + static constexpr FieldElement fe_invert2(const FieldElement& x) { + // Generated using https://github.com/mmcloughlin/addchain + auto z = x.square(); + z *= x; + auto t0 = z.square(); + t0 *= x; + auto t2 = t0.square(); + auto t1 = t2.square(); + auto t3 = t1; + t3.square_n(3); + t1 *= t3; + t3 = t1; + t3.square_n(2); + t2 *= t3; + t2.square_n(7); + t1 *= t2; + t2 = t1; + t2.square_n(15); + t1 *= t2; + t2 = t1; + t2.square_n(30); + t1 *= t2; + z *= t1; + t1 = z; + t1.square_n(3); + t2 = t1; + t2.square_n(62); + t1 *= t2; + t0 *= t1; + t0.square_n(63); + z *= t0; + z.square_n(2); + return z; + } +}; } // namespace secp192r1 diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp224r1/info.txt botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp224r1/info.txt --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp224r1/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp224r1/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + PCURVES_SECP224R1 -> 20240716 - + name -> "PCurve secp224r1" diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp224r1/pcurves_secp224r1.cpp botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp224r1/pcurves_secp224r1.cpp --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp224r1/pcurves_secp224r1.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp224r1/pcurves_secp224r1.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -61,7 +61,7 @@ BOTAN_DEBUG_ASSERT(S <= 2); - bigint_correct_redc(r, P, p224_mul_mod_224(S)); + solinas_correct_redc(r, P, p224_mul_mod_224(S)); return r; } @@ -99,6 +99,7 @@ }; // clang-format off + class Params final : public EllipticCurveParameters< "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE", diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp256k1/info.txt botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp256k1/info.txt --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp256k1/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp256k1/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + PCURVES_SECP256K1 -> 20240608 - + name -> "PCurve secp256k1" diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp256k1/pcurves_secp256k1.cpp botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp256k1/pcurves_secp256k1.cpp --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp256k1/pcurves_secp256k1.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp256k1/pcurves_secp256k1.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -39,6 +39,7 @@ }; // clang-format off + class Params final : public EllipticCurveParameters< "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", "0", @@ -49,11 +50,9 @@ }; // clang-format on -#if BOTAN_MP_WORD_BITS == 64 -typedef EllipticCurve Secp256k1Base; -#else -typedef EllipticCurve Secp256k1Base; -#endif + +using Secp256k1Base = + std::conditional_t::bits >= 33, EllipticCurve, EllipticCurve>; class Curve final : public Secp256k1Base { public: @@ -97,6 +96,45 @@ z *= t0; z.square_n(2); return z; + } + + static constexpr FieldElement fe_sqrt(const FieldElement& x) { + auto z = x.square(); + z *= x; + auto t0 = z; + t0.square_n(2); + t0 *= z; + auto t1 = t0.square(); + auto t2 = t1 * x; + t1 = t2; + t1.square_n(2); + t1 *= z; + auto t3 = t1; + t3.square_n(4); + t0 *= t3; + t3 = t0; + t3.square_n(11); + t0 *= t3; + t3 = t0; + t3.square_n(5); + t2 *= t3; + t3 = t2; + t3.square_n(27); + t2 *= t3; + t3 = t2; + t3.square_n(54); + t2 *= t3; + t3 = t2; + t3.square_n(108); + t2 *= t3; + t2.square_n(7); + t1 *= t2; + t1.square_n(23); + t0 *= t1; + t0.square_n(6); + z *= t0; + z.square_n(2); + return z; } static constexpr Scalar scalar_invert(const Scalar& x) { diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp256r1/info.txt botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp256r1/info.txt --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp256r1/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp256r1/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + PCURVES_SECP256R1 -> 20240608 - + name -> "PCurve secp256r1" diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp256r1/pcurves_secp256r1.cpp botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp256r1/pcurves_secp256r1.cpp --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp256r1/pcurves_secp256r1.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp256r1/pcurves_secp256r1.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -69,7 +69,7 @@ BOTAN_DEBUG_ASSERT(S <= 8); - bigint_correct_redc(r, P, p256_mul_mod_256(S)); + solinas_correct_redc(r, P, p256_mul_mod_256(S)); return r; } @@ -112,6 +112,7 @@ namespace secp256r1 { // clang-format off + class Params final : public EllipticCurveParameters< "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", @@ -160,6 +161,31 @@ return z; } + // Return the square root of x + static constexpr FieldElement fe_sqrt(const FieldElement& x) { + // Generated using addchain + auto z = x.square(); + z *= x; + auto t0 = z; + t0.square_n(2); + z *= t0; + t0 = z; + t0.square_n(4); + z *= t0; + t0 = z; + t0.square_n(8); + z *= t0; + t0 = z; + t0.square_n(16); + z *= t0; + z.square_n(32); + z *= x; + z.square_n(96); + z *= x; + z.square_n(94); + return z; + } + static constexpr Scalar scalar_invert(const Scalar& x) { auto t1 = x.square(); auto t5 = t1.square(); diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp384r1/info.txt botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp384r1/info.txt --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp384r1/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp384r1/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + PCURVES_SECP384R1 -> 20240608 - + name -> "PCurve secp384r1" diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp384r1/pcurves_secp384r1.cpp botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp384r1/pcurves_secp384r1.cpp --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp384r1/pcurves_secp384r1.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp384r1/pcurves_secp384r1.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -80,7 +80,7 @@ BOTAN_DEBUG_ASSERT(S <= 4); - bigint_correct_redc(r, P, p384_mul_mod_384(S)); + solinas_correct_redc(r, P, p384_mul_mod_384(S)); return r; } @@ -120,9 +120,10 @@ } }; -// clang-format off namespace secp384r1 { +// clang-format off + class Params final : public EllipticCurveParameters< "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", @@ -179,6 +180,46 @@ return r; } + static constexpr FieldElement fe_sqrt(const FieldElement& x) { + // Generated using https://github.com/mmcloughlin/addchain + + auto z = x.square(); + z *= x; + z = z.square(); + auto t0 = x * z; + z = t0; + z.square_n(3); + auto t1 = t0 * z; + auto t2 = t1.square(); + z = t2 * x; + t2.square_n(5); + t1 *= t2; + t2 = t1; + t2.square_n(12); + t1 *= t2; + t1.square_n(7); + t1 *= z; + z = t1.square(); + z *= x; + t2 = z; + t2.square_n(31); + t1 *= t2; + t2 = t1; + t2.square_n(63); + t1 *= t2; + t2 = t1; + t2.square_n(126); + t1 *= t2; + t1.square_n(3); + t0 *= t1; + t0.square_n(33); + z *= t0; + z.square_n(64); + z *= x; + z.square_n(30); + return z; + } + static constexpr Scalar scalar_invert(const Scalar& x) { // Generated using https://github.com/mmcloughlin/addchain diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp521r1/info.txt botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp521r1/info.txt --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp521r1/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp521r1/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + PCURVES_SECP521R1 -> 20240608 - + name -> "PCurve secp521r1" diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp521r1/pcurves_secp521r1.cpp botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp521r1/pcurves_secp521r1.cpp --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_secp521r1/pcurves_secp521r1.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_secp521r1/pcurves_secp521r1.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -24,12 +24,13 @@ constexpr static std::array one() { return std::array{1}; } constexpr static std::array redc(const std::array& z) { - constexpr W TOP_MASK = static_cast(0x1FF); + // Regardless of word size (32 or 64) the top word is 9 bits long + constexpr W TOP_BITS = static_cast(0x1FF); /* * Extract the high part of z (z >> 521) */ - std::array t; + std::array t; // NOLINT(*-member-init) for(size_t i = 0; i != N; ++i) { t[i] = z[(N - 1) + i] >> 9; @@ -40,22 +41,36 @@ } // Now t += z & (2**521-1) - W carry = word8_add2(t.data(), z.data(), static_cast(0)); - - if constexpr(WordInfo::bits == 32) { - constexpr size_t HN = N / 2; - carry = word8_add2(t.data() + HN, z.data() + HN, carry); + W carry = 0; + for(size_t i = 0; i != N - 1; ++i) { + t[i] = word_add(t[i], z[i], &carry); } // Now add the (partial) top words; this can't carry out // since both inputs are at most 2**9-1 - t[N - 1] += (z[N - 1] & TOP_MASK) + carry; + t[N - 1] += (z[N - 1] & TOP_BITS) + carry; - // But might be greater than modulus: - std::array r; - bigint_monty_maybe_sub(r.data(), static_cast(0), t.data(), P.data()); + /* + Since the modulus P is exactly 2**521 - 1 the only way the computed + result can be larger than P is if the top word is larger than TOP_BITS - return r; + If this is the case then we need to conditionally subtract P + + Since TOP_BITS has the low 9 bits set, we can check if t[N - 1] > TOP_BITS + by checking if t[N - 1] >> 9 has any bits set. Doing it this way is + faster than a standard comparison since CT::Mask::is_gt requires + several bit operations. + */ + + const W need_sub = ~CT::Mask::is_zero(t[N - 1] >> 9).value(); + + W borrow = 0; + for(size_t i = 0; i != N - 1; ++i) { + t[i] = word_sub(t[i], need_sub & WordInfo::max, &borrow); + } + t[N - 1] = word_sub(t[N - 1], need_sub & TOP_BITS, &borrow); + + return t; } constexpr static std::array to_rep(const std::array& x) { return x; } @@ -66,6 +81,7 @@ }; // clang-format off + class Params final : public EllipticCurveParameters< "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", @@ -121,6 +137,12 @@ return r; } + static constexpr FieldElement fe_sqrt(const FieldElement& x) { + auto z = x; + z.square_n(519); + return z; + } + static constexpr Scalar scalar_invert(const Scalar& x) { // Generated using https://github.com/mmcloughlin/addchain diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_sm2p256v1/info.txt botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_sm2p256v1/info.txt --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_sm2p256v1/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_sm2p256v1/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + PCURVES_SM2P256V1 -> 20240608 - + name -> "PCurve sm2p256v1" diff -Nru botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_sm2p256v1/pcurves_sm2p256v1.cpp botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_sm2p256v1/pcurves_sm2p256v1.cpp --- botan3-3.7.1+dfsg/src/lib/math/pcurves/pcurves_sm2p256v1/pcurves_sm2p256v1.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/math/pcurves/pcurves_sm2p256v1/pcurves_sm2p256v1.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -63,7 +63,7 @@ sum.accum(S7); const auto S = sum.final_carry(0); - bigint_correct_redc(r, P, sm2_mul_mod_256(S)); + solinas_correct_redc(r, P, sm2_mul_mod_256(S)); return r; } @@ -119,7 +119,7 @@ class Curve final : public EllipticCurve { public: // Return the square of the inverse of x - static FieldElement fe_invert2(const FieldElement& x) { + static constexpr FieldElement fe_invert2(const FieldElement& x) { // Generated by https://github.com/mmcloughlin/addchain auto z = x.square(); auto t0 = x * z; @@ -154,6 +154,42 @@ z.square_n(2); return z; } + + static constexpr FieldElement fe_sqrt(const FieldElement& x) { + auto z = x.square(); + z *= x; + z = z.square(); + auto t0 = x * z; + z = t0.square(); + z *= x; + auto t2 = z.square(); + auto t3 = t2.square(); + auto t1 = t3.square(); + auto t4 = t1; + t4.square_n(3); + t3 *= t4; + t3.square_n(5); + t1 *= t3; + t3 = t1; + t3.square_n(2); + t2 *= t3; + t2.square_n(14); + t1 *= t2; + t0 *= t1; + t0.square_n(4); + t1 = t0; + t1.square_n(31); + t0 *= t1; + t1.square_n(32); + t1 *= t0; + t1.square_n(62); + t0 *= t1; + z *= t0; + z.square_n(32); + z *= x; + z.square_n(62); + return z; + } }; } // namespace sm2p256v1 diff -Nru botan3-3.7.1+dfsg/src/lib/misc/cryptobox/cryptobox.cpp botan3-3.12.0+dfsg/src/lib/misc/cryptobox/cryptobox.cpp --- botan3-3.7.1+dfsg/src/lib/misc/cryptobox/cryptobox.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/misc/cryptobox/cryptobox.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,13 +9,14 @@ #include #include +#include #include -#include #include #include #include #include #include +#include namespace Botan::CryptoBox { @@ -102,7 +103,7 @@ } for(size_t i = 0; i != VERSION_CODE_LEN; ++i) { - uint32_t version = load_be(ciphertext.data(), 0); + const uint32_t version = load_be(ciphertext.data(), 0); if(version != CRYPTOBOX_VERSION_CODE) { throw Decoding_Error("Bad CryptoBox version"); } @@ -149,18 +150,28 @@ BOTAN_DIAGNOSTIC_PUSH BOTAN_DIAGNOSTIC_IGNORE_DEPRECATED_DECLARATIONS +namespace { + +secure_vector decrypt_bin(std::span input, std::string_view passphrase) { + return CryptoBox::decrypt_bin(input.data(), input.size(), passphrase); +} + +std::string decrypt(std::span input, std::string_view passphrase) { + return CryptoBox::decrypt(input.data(), input.size(), passphrase); +} + +} // namespace + secure_vector decrypt_bin(std::string_view input, std::string_view passphrase) { - return decrypt_bin(cast_char_ptr_to_uint8(input.data()), input.size(), passphrase); + return decrypt_bin(as_span_of_bytes(input), passphrase); } std::string decrypt(const uint8_t input[], size_t input_len, std::string_view passphrase) { - const secure_vector bin = decrypt_bin(input, input_len, passphrase); - - return std::string(cast_uint8_ptr_to_char(&bin[0]), bin.size()); + return bytes_to_string(decrypt_bin(input, input_len, passphrase)); } std::string decrypt(std::string_view input, std::string_view passphrase) { - return decrypt(cast_char_ptr_to_uint8(input.data()), input.size(), passphrase); + return decrypt(as_span_of_bytes(input), passphrase); } BOTAN_DIAGNOSTIC_POP diff -Nru botan3-3.7.1+dfsg/src/lib/misc/cryptobox/info.txt botan3-3.12.0+dfsg/src/lib/misc/cryptobox/info.txt --- botan3-3.7.1+dfsg/src/lib/misc/cryptobox/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/misc/cryptobox/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -4,7 +4,8 @@ name -> "Crypto Box" -brief -> "High-Level API for password-based encryption" +brief -> "High-Level API for password-based encryption (deprecated)" +lifecycle -> "Deprecated" diff -Nru botan3-3.7.1+dfsg/src/lib/misc/fpe_fe1/fpe_fe1.cpp botan3-3.12.0+dfsg/src/lib/misc/fpe_fe1/fpe_fe1.cpp --- botan3-3.7.1+dfsg/src/lib/misc/fpe_fe1/fpe_fe1.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/misc/fpe_fe1/fpe_fe1.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,9 +7,9 @@ #include +#include #include #include -#include #include #include #include @@ -27,10 +27,21 @@ * typical uses of FPE (typically, n is a power of 10) */ void factor(BigInt n, BigInt& a, BigInt& b) { + BOTAN_ARG_CHECK(n >= 2, "Invalid FPE modulus"); + a = BigInt::one(); b = BigInt::one(); - size_t n_low_zero = low_zero_bits(n); + /* + * This algorithm was poorly designed. It should have fully factored n (to the + * extent possible) and then built a/b starting from the largest factor first. + * + * This can't be fixed now without breaking existing users but if some + * incompatible change (or new flag, etc) is added in the future, consider + * fixing the factoring for those users. + */ + + const size_t n_low_zero = low_zero_bits(n); a <<= (n_low_zero / 2); b <<= n_low_zero - (n_low_zero / 2); @@ -82,12 +93,10 @@ std::swap(m_a, m_b); } } - - // The modulus is usually a system parameter and anyway is easily deduced from - // the ciphertexts - mod_a = std::make_unique(Modular_Reducer::for_public_modulus(m_a)); } +FPE_FE1::FPE_FE1(FPE_FE1&& other) noexcept = default; + FPE_FE1::~FPE_FE1() = default; void FPE_FE1::clear() { @@ -145,11 +154,13 @@ secure_vector tmp; - BigInt L, R, Fi; + BigInt L; + BigInt R; + BigInt Fi; for(size_t i = 0; i != m_rounds; ++i) { ct_divide(X, m_b, L, R); Fi = F(R, i, tweak_mac, tmp); - X = m_a * R + mod_a->reduce(L + Fi); + X = m_a * R + ct_modulo(L + Fi, m_a); } return X; @@ -161,12 +172,14 @@ BigInt X = input; secure_vector tmp; - BigInt W, R, Fi; + BigInt W; + BigInt R; + BigInt Fi; for(size_t i = 0; i != m_rounds; ++i) { ct_divide(X, m_a, R, W); Fi = F(R, m_rounds - i - 1, tweak_mac, tmp); - X = m_b * mod_a->reduce(W - Fi) + R; + X = m_b * ct_modulo(W - Fi, m_a) + R; } return X; diff -Nru botan3-3.7.1+dfsg/src/lib/misc/fpe_fe1/fpe_fe1.h botan3-3.12.0+dfsg/src/lib/misc/fpe_fe1/fpe_fe1.h --- botan3-3.7.1+dfsg/src/lib/misc/fpe_fe1/fpe_fe1.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/misc/fpe_fe1/fpe_fe1.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,10 +10,11 @@ #include #include +#include +#include namespace Botan { -class Modular_Reducer; class MessageAuthenticationCode; /** @@ -31,10 +32,10 @@ * values for a and b. Set compat_mode to true to select this version. * @param mac_algo the PRF to use as the encryption function */ - FPE_FE1(const BigInt& n, - size_t rounds = 5, - bool compat_mode = false, - std::string_view mac_algo = "HMAC(SHA-256)"); + BOTAN_FUTURE_EXPLICIT FPE_FE1(const BigInt& n, + size_t rounds = 5, + bool compat_mode = false, + std::string_view mac_algo = "HMAC(SHA-256)"); ~FPE_FE1() override; @@ -66,6 +67,11 @@ BigInt decrypt(const BigInt& x, uint64_t tweak) const; + FPE_FE1(const FPE_FE1& other) = delete; + FPE_FE1(FPE_FE1&& other) noexcept; + FPE_FE1& operator=(const FPE_FE1& other) = delete; + FPE_FE1& operator=(FPE_FE1&& other) = delete; + private: void key_schedule(std::span key) override; @@ -74,13 +80,14 @@ secure_vector compute_tweak_mac(const uint8_t tweak[], size_t tweak_len) const; std::unique_ptr m_mac; - std::unique_ptr mod_a; std::vector m_n_bytes; BigInt m_a; BigInt m_b; size_t m_rounds; }; +class OctetString; + namespace FPE { /** @@ -98,7 +105,7 @@ * may be insecure for some values of n. Prefer FPE_FE1 class */ BigInt BOTAN_PUBLIC_API(2, 0) - fe1_encrypt(const BigInt& n, const BigInt& X, const SymmetricKey& key, const std::vector& tweak); + fe1_encrypt(const BigInt& n, const BigInt& X, const OctetString& key, const std::vector& tweak); /** * Decrypt X from and onto the group Z_n using key and tweak @@ -111,7 +118,7 @@ * may be insecure for some values of n. Prefer FPE_FE1 class */ BigInt BOTAN_PUBLIC_API(2, 0) - fe1_decrypt(const BigInt& n, const BigInt& X, const SymmetricKey& key, const std::vector& tweak); + fe1_decrypt(const BigInt& n, const BigInt& X, const OctetString& key, const std::vector& tweak); } // namespace FPE diff -Nru botan3-3.7.1+dfsg/src/lib/misc/hotp/hotp.cpp botan3-3.12.0+dfsg/src/lib/misc/hotp/hotp.cpp --- botan3-3.7.1+dfsg/src/lib/misc/hotp/hotp.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/misc/hotp/hotp.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ /* * HOTP -* (C) 2017 Jack Lloyd +* (C) 2017,2026 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -12,16 +12,27 @@ namespace Botan { -HOTP::HOTP(const uint8_t key[], size_t key_len, std::string_view hash_algo, size_t digits) { - BOTAN_ARG_CHECK(digits == 6 || digits == 7 || digits == 8, "Invalid HOTP digits"); +namespace { - if(digits == 6) { - m_digit_mod = 1000000; - } else if(digits == 7) { - m_digit_mod = 10000000; - } else if(digits == 8) { - m_digit_mod = 100000000; +// Use compile-time constant divisors to ensure the compiler emits a +// multiply+shift sequence instead of a variable-time division instruction +uint32_t hotp_truncate(uint32_t code, size_t digits) { + switch(digits) { + case 6: + return code % 1000000; + case 7: + return code % 10000000; + case 8: + return code % 100000000; + default: + BOTAN_ASSERT_UNREACHABLE(); } +} + +} // namespace + +HOTP::HOTP(const uint8_t key[], size_t key_len, std::string_view hash_algo, size_t digits) : m_digits(digits) { + BOTAN_ARG_CHECK(m_digits == 6 || m_digits == 7 || m_digits == 8, "Invalid HOTP digits"); /* RFC 4228 only supports SHA-1 but TOTP allows SHA-256 and SHA-512 @@ -46,7 +57,7 @@ const size_t offset = mac[mac.size() - 1] & 0x0F; const uint32_t code = load_be(mac.data() + offset, 0) & 0x7FFFFFFF; - return code % m_digit_mod; + return hotp_truncate(code, m_digits); } std::pair HOTP::verify_hotp(uint32_t otp, uint64_t starting_counter, size_t resync_range) { diff -Nru botan3-3.7.1+dfsg/src/lib/misc/hotp/otp.h botan3-3.12.0+dfsg/src/lib/misc/hotp/otp.h --- botan3-3.7.1+dfsg/src/lib/misc/hotp/otp.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/misc/hotp/otp.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #define BOTAN_ONE_TIME_PASSWORDS_H_ #include +#include #include namespace Botan { @@ -22,8 +23,9 @@ * @param key the secret key shared between client and server * @param hash_algo the hash algorithm to use, should be SHA-1 or SHA-256 * @param digits the number of digits in the OTP (must be 6, 7, or 8) + * TODO(Botan4) remove the default hash param here */ - HOTP(const SymmetricKey& key, std::string_view hash_algo = "SHA-1", size_t digits = 6) : + BOTAN_FUTURE_EXPLICIT HOTP(const SymmetricKey& key, std::string_view hash_algo = "SHA-1", size_t digits = 6) : HOTP(key.begin(), key.size(), hash_algo, digits) {} /** @@ -31,6 +33,7 @@ * @param key_len length of key param * @param hash_algo the hash algorithm to use, should be SHA-1 or SHA-256 * @param digits the number of digits in the OTP (must be 6, 7, or 8) + * TODO(Botan4) remove the default hash param here */ HOTP(const uint8_t key[], size_t key_len, std::string_view hash_algo = "SHA-1", size_t digits = 6); @@ -54,7 +57,7 @@ private: std::unique_ptr m_mac; - uint32_t m_digit_mod; + size_t m_digits; }; /** @@ -67,8 +70,12 @@ * @param hash_algo the hash algorithm to use, should be SHA-1, SHA-256 or SHA-512 * @param digits the number of digits in the OTP (must be 6, 7, or 8) * @param time_step granularity of OTP in seconds + * TODO(Botan4) remove the default hash param here */ - TOTP(const SymmetricKey& key, std::string_view hash_algo = "SHA-1", size_t digits = 6, size_t time_step = 30) : + BOTAN_FUTURE_EXPLICIT TOTP(const SymmetricKey& key, + std::string_view hash_algo = "SHA-1", + size_t digits = 6, + size_t time_step = 30) : TOTP(key.begin(), key.size(), hash_algo, digits, time_step) {} /** @@ -77,6 +84,7 @@ * @param hash_algo the hash algorithm to use, should be SHA-1, SHA-256 or SHA-512 * @param digits the number of digits in the OTP (must be 6, 7, or 8) * @param time_step granularity of OTP in seconds + * TODO(Botan4) remove the default hash param here */ TOTP(const uint8_t key[], size_t key_len, diff -Nru botan3-3.7.1+dfsg/src/lib/misc/hotp/totp.cpp botan3-3.12.0+dfsg/src/lib/misc/hotp/totp.cpp --- botan3-3.7.1+dfsg/src/lib/misc/hotp/totp.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/misc/hotp/totp.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,7 @@ #include +#include #include namespace Botan { @@ -37,7 +38,7 @@ } bool TOTP::verify_totp(uint32_t otp, uint64_t unix_time, size_t clock_drift_accepted) { - uint64_t t = unix_time / m_time_step; + const uint64_t t = unix_time / m_time_step; for(size_t i = 0; i <= clock_drift_accepted; ++i) { if(m_hotp.generate_hotp(t - i) == otp) { diff -Nru botan3-3.7.1+dfsg/src/lib/misc/nist_keywrap/nist_keywrap.cpp botan3-3.12.0+dfsg/src/lib/misc/nist_keywrap/nist_keywrap.cpp --- botan3-3.7.1+dfsg/src/lib/misc/nist_keywrap/nist_keywrap.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/misc/nist_keywrap/nist_keywrap.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -189,26 +189,30 @@ R = raw_nist_key_unwrap(input, input_len, bc, ICV_out); } - if((ICV_out >> 32) != 0xA65959A6) { - throw Invalid_Authentication_Tag("NIST key unwrap failed"); - } + /* + The padded key wrap ICV is 0xA65959A6 || uint32(plaintext_length). - const size_t len = (ICV_out & 0xFFFFFFFF); + We know the expected ICV almost entirely: the top 32 bits are the + fixed constant and the bottom 32 bits encode the original plaintext + length, which is R.size() minus 0 to 7 bytes of padding. Compute + the ICV we'd expect for the zero-padding case and subtract ICV_out; + for a valid unwrap the difference is at most 7, and equals the padding. + */ + const uint64_t expected_ICV_max = 0xA65959A600000000 | static_cast(R.size()); + const uint64_t padding = expected_ICV_max - ICV_out; - if(R.size() < 8 || len > R.size() || len <= R.size() - 8) { + if(padding > 7) { throw Invalid_Authentication_Tag("NIST key unwrap failed"); } - const size_t padding = R.size() - len; - - for(size_t i = 0; i != padding; ++i) { - if(R[R.size() - i - 1] != 0) { - throw Invalid_Authentication_Tag("NIST key unwrap failed"); - } + // Verify padding bytes are zero + const uint64_t last_block = load_be(R.data() + R.size() - 8, 0); + const uint64_t padding_mask = (static_cast(1) << (padding * 8)) - 1; + if((last_block & padding_mask) != 0) { + throw Invalid_Authentication_Tag("NIST key unwrap failed"); } - R.resize(R.size() - padding); - + R.resize(R.size() - static_cast(padding)); return R; } diff -Nru botan3-3.7.1+dfsg/src/lib/misc/nist_keywrap/nist_keywrap.h botan3-3.12.0+dfsg/src/lib/misc/nist_keywrap/nist_keywrap.h --- botan3-3.7.1+dfsg/src/lib/misc/nist_keywrap/nist_keywrap.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/misc/nist_keywrap/nist_keywrap.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #define BOTAN_NIST_KEY_WRAP_H_ #include +#include namespace Botan { @@ -24,6 +25,16 @@ nist_key_wrap(const uint8_t input[], size_t input_len, const BlockCipher& bc); /** +* Key wrap. See RFC 3394 and NIST SP800-38F +* @param input the value to be encrypted +* @param bc a keyed 128-bit block cipher that will be used to encrypt input +* @return input encrypted under NIST key wrap algorithm +*/ +inline std::vector nist_key_wrap(std::span input, const BlockCipher& bc) { + return nist_key_wrap(input.data(), input.size(), bc); +} + +/** * @param input the value to be decrypted, output of nist_key_wrap * @param input_len length of input * @param bc a keyed 128-bit block cipher that will be used to decrypt input @@ -34,6 +45,16 @@ nist_key_unwrap(const uint8_t input[], size_t input_len, const BlockCipher& bc); /** +* @param input the value to be decrypted, output of nist_key_wrap +* @param bc a keyed 128-bit block cipher that will be used to decrypt input +* @return input decrypted under NIST key wrap algorithm +* Throws an exception if decryption fails. +*/ +inline secure_vector nist_key_unwrap(std::span input, const BlockCipher& bc) { + return nist_key_unwrap(input.data(), input.size(), bc); +} + +/** * KWP (key wrap with padding). See RFC 5649 and NIST SP800-38F * @param input the value to be encrypted * @param input_len length of input @@ -44,6 +65,16 @@ nist_key_wrap_padded(const uint8_t input[], size_t input_len, const BlockCipher& bc); /** +* KWP (key wrap with padding). See RFC 5649 and NIST SP800-38F +* @param input the value to be encrypted +* @param bc a keyed 128-bit block cipher that will be used to encrypt input +* @return input encrypted under NIST key wrap algorithm +*/ +inline std::vector nist_key_wrap_padded(std::span input, const BlockCipher& bc) { + return nist_key_wrap_padded(input.data(), input.size(), bc); +} + +/** * @param input the value to be decrypted, output of nist_key_wrap * @param input_len length of input * @param bc a keyed 128-bit block cipher that will be used to decrypt input @@ -53,6 +84,16 @@ secure_vector BOTAN_PUBLIC_API(2, 4) nist_key_unwrap_padded(const uint8_t input[], size_t input_len, const BlockCipher& bc); +/** +* @param input the value to be decrypted, output of nist_key_wrap +* @param bc a keyed 128-bit block cipher that will be used to decrypt input +* @return input decrypted under NIST key wrap algorithm +* Throws an exception if decryption fails. +*/ +inline secure_vector nist_key_unwrap_padded(std::span input, const BlockCipher& bc) { + return nist_key_unwrap_padded(input.data(), input.size(), bc); +} + } // namespace Botan #endif diff -Nru botan3-3.7.1+dfsg/src/lib/misc/rfc3394/rfc3394.cpp botan3-3.12.0+dfsg/src/lib/misc/rfc3394/rfc3394.cpp --- botan3-3.7.1+dfsg/src/lib/misc/rfc3394/rfc3394.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/misc/rfc3394/rfc3394.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,7 @@ #include +#include #include #include diff -Nru botan3-3.7.1+dfsg/src/lib/misc/roughtime/roughtime.cpp botan3-3.12.0+dfsg/src/lib/misc/roughtime/roughtime.cpp --- botan3-3.7.1+dfsg/src/lib/misc/roughtime/roughtime.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/misc/roughtime/roughtime.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,11 +9,12 @@ #include #include +#include #include #include +#include #include -#include #include #include @@ -46,7 +47,7 @@ T copy(const uint8_t* t) requires(is_array::value) { - return typecast_copy(t); //arrays are endianess independent, so we do a memcpy + return typecast_copy(t); //arrays are endianness independent, so we do a memcpy } template @@ -113,10 +114,10 @@ bool verify_signature(const std::array& pk, const std::vector& payload, const std::array& signature) { - const char context[] = "RoughTime v1 response signature"; - Ed25519_PublicKey key(std::vector(pk.data(), pk.data() + pk.size())); + constexpr std::string_view context("RoughTime v1 response signature\0", 32); + const Ed25519_PublicKey key(std::vector(pk.data(), pk.data() + pk.size())); PK_Verifier verifier(key, "Pure"); - verifier.update(cast_char_ptr_to_uint8(context), sizeof(context)); //add context including \0 + verifier.update(context); verifier.update(payload); return verifier.check_signature(signature.data(), signature.size()); } @@ -130,7 +131,7 @@ return ret; } -void hashNode(std::array& hash, const std::array& node, bool reverse) { +void hashNode(std::span hash, std::span node, bool reverse) { auto h = HashFunction::create_or_throw("SHA-512"); h->update(1); if(reverse) { @@ -154,16 +155,14 @@ namespace Roughtime { -Nonce::Nonce(const std::vector& nonce) { +Nonce::Nonce(const std::vector& nonce) : m_nonce{} { if(nonce.size() != 64) { throw Invalid_Argument("Roughtime nonce must be 64 bytes long"); } m_nonce = typecast_copy>(nonce.data()); } -Nonce::Nonce(RandomNumberGenerator& rng) { - rng.randomize(m_nonce.data(), m_nonce.size()); -} +Nonce::Nonce(RandomNumberGenerator& rng) : m_nonce(rng.random_array<64>()) {} std::array encode_request(const Nonce& nonce) { std::array buf = {{2, 0, 0, 0, 64, 0, 0, 0, 'N', 'O', 'N', 'C', 'P', 'A', 'D', 0xff}}; @@ -193,19 +192,18 @@ const size_t size = path.size(); const size_t levels = size / 64; - if(size % 64) { + if(size % 64 != 0) { throw Roughtime_Error("Merkle tree path size must be multiple of 64 bytes"); } - if(indx >= (1U << levels)) { + if(levels >= 32 || indx >= (uint32_t(1) << levels)) { throw Roughtime_Error("Merkle tree path is too short"); } + BufferSlicer slicer(path); auto hash = hashLeaf(nonce.get_nonce()); auto index = indx; - size_t level = 0; - while(level < levels) { - hashNode(hash, typecast_copy>(path.data() + level * 64), index & 1); - ++level; + for(std::size_t level = 0; level < levels; ++level) { + hashNode(hash, slicer.take<64>(), index % 2 == 1); index >>= 1; } @@ -227,9 +225,9 @@ } bool Response::validate(const Ed25519_PublicKey& pk) const { - const char context[] = "RoughTime v1 delegation signature--"; + constexpr std::string_view context("RoughTime v1 delegation signature--\0", 36); PK_Verifier verifier(pk, "Pure"); - verifier.update(cast_char_ptr_to_uint8(context), sizeof(context)); //add context including \0 + verifier.update(context); verifier.update(m_cert_dele.data(), m_cert_dele.size()); return verifier.check_signature(m_cert_sig.data(), m_cert_sig.size()); } @@ -243,14 +241,15 @@ hash->update(blind_arr.data(), blind_arr.size()); hash->final(ret.data()); - return ret; + return Nonce(ret); } Chain::Chain(std::string_view str) { std::istringstream ss{std::string(str)}; // FIXME C++23 avoid copy const std::string ERROR_MESSAGE = "Line does not have 4 space separated fields"; for(std::string s; std::getline(ss, s);) { - size_t start = 0, end = 0; + size_t start = 0; + size_t end = 0; end = s.find(' ', start); if(end == std::string::npos) { throw Decoding_Error(ERROR_MESSAGE); @@ -291,9 +290,9 @@ std::vector Chain::responses() const { std::vector responses; - for(unsigned i = 0; i < m_links.size(); ++i) { + for(size_t i = 0; i < m_links.size(); ++i) { const auto& l = m_links[i]; - const auto nonce = i ? nonce_from_blind(m_links[i - 1].response(), l.nonce_or_blind()) : l.nonce_or_blind(); + const auto nonce = i > 0 ? nonce_from_blind(m_links[i - 1].response(), l.nonce_or_blind()) : l.nonce_or_blind(); const auto response = Response::from_bits(l.response(), nonce); if(!response.validate(l.public_key())) { throw Roughtime_Error("Invalid signature or public key"); @@ -365,7 +364,7 @@ //add one additional byte to be able to differentiate if datagram got truncated const auto n = socket->read(buffer.data(), buffer.size()); - if(!n || std::chrono::system_clock::now() - start_time > timeout) { + if(n == 0 || std::chrono::system_clock::now() - start_time > timeout) { throw System_Error("Timeout waiting for response"); } @@ -383,7 +382,8 @@ const std::string ERROR_MESSAGE = "Line does not have at least 5 space separated fields"; for(std::string s; std::getline(ss, s);) { - size_t start = 0, end = 0; + size_t start = 0; + size_t end = 0; end = s.find(' ', start); if(end == std::string::npos) { throw Decoding_Error(ERROR_MESSAGE); diff -Nru botan3-3.7.1+dfsg/src/lib/misc/roughtime/roughtime.h botan3-3.12.0+dfsg/src/lib/misc/roughtime/roughtime.h --- botan3-3.7.1+dfsg/src/lib/misc/roughtime/roughtime.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/misc/roughtime/roughtime.h 2026-05-07 01:38:28.000000000 +0000 @@ -32,10 +32,10 @@ class BOTAN_PUBLIC_API(2, 13) Nonce final { public: Nonce() = default; - Nonce(const std::vector& nonce); - Nonce(RandomNumberGenerator& rng); + explicit Nonce(const std::vector& nonce); + explicit Nonce(RandomNumberGenerator& rng); - Nonce(const std::array& nonce) { m_nonce = nonce; } + explicit Nonce(const std::array& nonce) : m_nonce(nonce) {} bool operator==(const Nonce& rhs) const { return m_nonce == rhs.m_nonce; } @@ -103,7 +103,7 @@ class BOTAN_PUBLIC_API(2, 13) Chain final { public: Chain() = default; //empty - Chain(std::string_view str); + explicit Chain(std::string_view str); const std::vector& links() const { return m_links; } diff -Nru botan3-3.7.1+dfsg/src/lib/misc/srp6/srp6.cpp botan3-3.12.0+dfsg/src/lib/misc/srp6/srp6.cpp --- botan3-3.7.1+dfsg/src/lib/misc/srp6/srp6.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/misc/srp6/srp6.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,5 @@ /* -* SRP-6a (RFC 5054 compatatible) +* SRP-6a (RFC 5054 compatible) * (C) 2011,2012,2019,2020 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) @@ -7,7 +7,9 @@ #include +#include #include +#include #include #include @@ -105,12 +107,13 @@ const BigInt A = group.power_g_p(a, a_bits); const BigInt u = hash_seq(*hash_fn, p_bytes, A, B); + BOTAN_ASSERT_NOMSG(!u.is_zero()); const BigInt x = compute_x(*hash_fn, identifier, password, salt); const BigInt g_x_p = group.power_g_p(x, hash_fn->output_length() * 8); - const BigInt B_k_g_x_p = group.mod_p(B - group.multiply_mod_p(k, g_x_p)); + const BigInt B_k_g_x_p = group.mod_p(B + group.mod_p(p - group.multiply_mod_p(k, g_x_p))); const BigInt a_ux = a + u * x; @@ -194,6 +197,7 @@ } const BigInt u = hash_seq(*hash_fn, m_group->p_bytes(), A, m_B); + BOTAN_ASSERT_NOMSG(!u.is_zero()); const BigInt vup = m_group->power_b_p(m_v, u, m_group->p_bits()); const BigInt S = m_group->power_b_p(m_group->multiply_mod_p(A, vup), m_b, m_group->p_bits()); diff -Nru botan3-3.7.1+dfsg/src/lib/misc/srp6/srp6.h botan3-3.12.0+dfsg/src/lib/misc/srp6/srp6.h --- botan3-3.7.1+dfsg/src/lib/misc/srp6/srp6.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/misc/srp6/srp6.h 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,5 @@ /* -* SRP-6a (RFC 5054 compatatible) +* SRP-6a (RFC 5054 compatible) * (C) 2011,2012,2019 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) diff -Nru botan3-3.7.1+dfsg/src/lib/misc/tss/tss.cpp botan3-3.12.0+dfsg/src/lib/misc/tss/tss.cpp --- botan3-3.7.1+dfsg/src/lib/misc/tss/tss.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/misc/tss/tss.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,12 +1,13 @@ /* * RTSS (threshold secret sharing) -* (C) 2009,2018 Jack Lloyd +* (C) 2009,2018,2026 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include +#include #include #include #include @@ -20,48 +21,38 @@ const size_t RTSS_HEADER_SIZE = 20; /** -Table for GF(2^8) arithmetic (exponentials) +* Constant-time multiplication in GF(2^8) mod 0x11B */ -alignas(64) const uint8_t RTSS_EXP[256] = { - 0x01, 0x03, 0x05, 0x0F, 0x11, 0x33, 0x55, 0xFF, 0x1A, 0x2E, 0x72, 0x96, 0xA1, 0xF8, 0x13, 0x35, 0x5F, 0xE1, 0x38, - 0x48, 0xD8, 0x73, 0x95, 0xA4, 0xF7, 0x02, 0x06, 0x0A, 0x1E, 0x22, 0x66, 0xAA, 0xE5, 0x34, 0x5C, 0xE4, 0x37, 0x59, - 0xEB, 0x26, 0x6A, 0xBE, 0xD9, 0x70, 0x90, 0xAB, 0xE6, 0x31, 0x53, 0xF5, 0x04, 0x0C, 0x14, 0x3C, 0x44, 0xCC, 0x4F, - 0xD1, 0x68, 0xB8, 0xD3, 0x6E, 0xB2, 0xCD, 0x4C, 0xD4, 0x67, 0xA9, 0xE0, 0x3B, 0x4D, 0xD7, 0x62, 0xA6, 0xF1, 0x08, - 0x18, 0x28, 0x78, 0x88, 0x83, 0x9E, 0xB9, 0xD0, 0x6B, 0xBD, 0xDC, 0x7F, 0x81, 0x98, 0xB3, 0xCE, 0x49, 0xDB, 0x76, - 0x9A, 0xB5, 0xC4, 0x57, 0xF9, 0x10, 0x30, 0x50, 0xF0, 0x0B, 0x1D, 0x27, 0x69, 0xBB, 0xD6, 0x61, 0xA3, 0xFE, 0x19, - 0x2B, 0x7D, 0x87, 0x92, 0xAD, 0xEC, 0x2F, 0x71, 0x93, 0xAE, 0xE9, 0x20, 0x60, 0xA0, 0xFB, 0x16, 0x3A, 0x4E, 0xD2, - 0x6D, 0xB7, 0xC2, 0x5D, 0xE7, 0x32, 0x56, 0xFA, 0x15, 0x3F, 0x41, 0xC3, 0x5E, 0xE2, 0x3D, 0x47, 0xC9, 0x40, 0xC0, - 0x5B, 0xED, 0x2C, 0x74, 0x9C, 0xBF, 0xDA, 0x75, 0x9F, 0xBA, 0xD5, 0x64, 0xAC, 0xEF, 0x2A, 0x7E, 0x82, 0x9D, 0xBC, - 0xDF, 0x7A, 0x8E, 0x89, 0x80, 0x9B, 0xB6, 0xC1, 0x58, 0xE8, 0x23, 0x65, 0xAF, 0xEA, 0x25, 0x6F, 0xB1, 0xC8, 0x43, - 0xC5, 0x54, 0xFC, 0x1F, 0x21, 0x63, 0xA5, 0xF4, 0x07, 0x09, 0x1B, 0x2D, 0x77, 0x99, 0xB0, 0xCB, 0x46, 0xCA, 0x45, - 0xCF, 0x4A, 0xDE, 0x79, 0x8B, 0x86, 0x91, 0xA8, 0xE3, 0x3E, 0x42, 0xC6, 0x51, 0xF3, 0x0E, 0x12, 0x36, 0x5A, 0xEE, - 0x29, 0x7B, 0x8D, 0x8C, 0x8F, 0x8A, 0x85, 0x94, 0xA7, 0xF2, 0x0D, 0x17, 0x39, 0x4B, 0xDD, 0x7C, 0x84, 0x97, 0xA2, - 0xFD, 0x1C, 0x24, 0x6C, 0xB4, 0xC7, 0x52, 0xF6, 0x01}; +uint8_t tss_gf_mul(uint8_t x, uint8_t y) { + uint8_t r = 0; + for(size_t i = 0; i != 8; ++i) { + r ^= CT::Mask::expand(y & 1).if_set_return(x); + x = (x << 1) ^ CT::Mask::expand_top_bit(x).if_set_return(0x1B); + y >>= 1; + } + return r; +} /** -Table for GF(2^8) arithmetic (logarithms) +* Inversion in GF(2^8) via Fermat's little theorem. +* +* Returns 0 for input 0 - a case which should not occur in our usage. +* +* Really this does not need to be constant-time - we only use inversion when +* computing the Lagrange coefficients, which is derived entirely from public data. */ -alignas(64) const uint8_t RTSS_LOG[] = { - 0x90, 0x00, 0x19, 0x01, 0x32, 0x02, 0x1A, 0xC6, 0x4B, 0xC7, 0x1B, 0x68, 0x33, 0xEE, 0xDF, 0x03, 0x64, 0x04, 0xE0, - 0x0E, 0x34, 0x8D, 0x81, 0xEF, 0x4C, 0x71, 0x08, 0xC8, 0xF8, 0x69, 0x1C, 0xC1, 0x7D, 0xC2, 0x1D, 0xB5, 0xF9, 0xB9, - 0x27, 0x6A, 0x4D, 0xE4, 0xA6, 0x72, 0x9A, 0xC9, 0x09, 0x78, 0x65, 0x2F, 0x8A, 0x05, 0x21, 0x0F, 0xE1, 0x24, 0x12, - 0xF0, 0x82, 0x45, 0x35, 0x93, 0xDA, 0x8E, 0x96, 0x8F, 0xDB, 0xBD, 0x36, 0xD0, 0xCE, 0x94, 0x13, 0x5C, 0xD2, 0xF1, - 0x40, 0x46, 0x83, 0x38, 0x66, 0xDD, 0xFD, 0x30, 0xBF, 0x06, 0x8B, 0x62, 0xB3, 0x25, 0xE2, 0x98, 0x22, 0x88, 0x91, - 0x10, 0x7E, 0x6E, 0x48, 0xC3, 0xA3, 0xB6, 0x1E, 0x42, 0x3A, 0x6B, 0x28, 0x54, 0xFA, 0x85, 0x3D, 0xBA, 0x2B, 0x79, - 0x0A, 0x15, 0x9B, 0x9F, 0x5E, 0xCA, 0x4E, 0xD4, 0xAC, 0xE5, 0xF3, 0x73, 0xA7, 0x57, 0xAF, 0x58, 0xA8, 0x50, 0xF4, - 0xEA, 0xD6, 0x74, 0x4F, 0xAE, 0xE9, 0xD5, 0xE7, 0xE6, 0xAD, 0xE8, 0x2C, 0xD7, 0x75, 0x7A, 0xEB, 0x16, 0x0B, 0xF5, - 0x59, 0xCB, 0x5F, 0xB0, 0x9C, 0xA9, 0x51, 0xA0, 0x7F, 0x0C, 0xF6, 0x6F, 0x17, 0xC4, 0x49, 0xEC, 0xD8, 0x43, 0x1F, - 0x2D, 0xA4, 0x76, 0x7B, 0xB7, 0xCC, 0xBB, 0x3E, 0x5A, 0xFB, 0x60, 0xB1, 0x86, 0x3B, 0x52, 0xA1, 0x6C, 0xAA, 0x55, - 0x29, 0x9D, 0x97, 0xB2, 0x87, 0x90, 0x61, 0xBE, 0xDC, 0xFC, 0xBC, 0x95, 0xCF, 0xCD, 0x37, 0x3F, 0x5B, 0xD1, 0x53, - 0x39, 0x84, 0x3C, 0x41, 0xA2, 0x6D, 0x47, 0x14, 0x2A, 0x9E, 0x5D, 0x56, 0xF2, 0xD3, 0xAB, 0x44, 0x11, 0x92, 0xD9, - 0x23, 0x20, 0x2E, 0x89, 0xB4, 0x7C, 0xB8, 0x26, 0x77, 0x99, 0xE3, 0xA5, 0x67, 0x4A, 0xED, 0xDE, 0xC5, 0x31, 0xFE, - 0x18, 0x0D, 0x63, 0x8C, 0x80, 0xC0, 0xF7, 0x70, 0x07}; - -uint8_t gfp_mul(uint8_t x, uint8_t y) { - if(x == 0 || y == 0) { - return 0; - } - return RTSS_EXP[(RTSS_LOG[x] + RTSS_LOG[y]) % 255]; +uint8_t tss_gf_inv(uint8_t x) { + const uint8_t x2 = tss_gf_mul(x, x); + const uint8_t x3 = tss_gf_mul(x2, x); + const uint8_t x6 = tss_gf_mul(x3, x3); + const uint8_t x12 = tss_gf_mul(x6, x6); + const uint8_t x15 = tss_gf_mul(x12, x3); + const uint8_t x30 = tss_gf_mul(x15, x15); + const uint8_t x60 = tss_gf_mul(x30, x30); + const uint8_t x120 = tss_gf_mul(x60, x60); + const uint8_t x126 = tss_gf_mul(x120, x6); + const uint8_t x127 = tss_gf_mul(x126, x); + return tss_gf_mul(x127, x127); } uint8_t rtss_hash_id(std::string_view hash_name) { @@ -156,7 +147,7 @@ const uint16_t share_len = static_cast(secret.size() + 1); secure_vector share_header(RTSS_HEADER_SIZE); - copy_mem(&share_header[0], identifier.data(), identifier.size()); + copy_mem(share_header.data(), identifier.data(), identifier.size()); share_header[16] = hash_id; share_header[17] = M; share_header[18] = get_byte<0>(share_len); @@ -175,19 +166,19 @@ shares[i].m_contents.push_back(i + 1); } - for(size_t i = 0; i != secret.size(); ++i) { + for(const uint8_t secret_byte : secret) { std::vector coefficients(M - 1); rng.randomize(coefficients.data(), coefficients.size()); for(uint8_t j = 0; j != N; ++j) { const uint8_t X = j + 1; - uint8_t sum = secret[i]; + uint8_t sum = secret_byte; uint8_t X_i = X; - for(size_t k = 0; k != coefficients.size(); ++k) { - sum ^= gfp_mul(X_i, coefficients[k]); - X_i = gfp_mul(X_i, X); + for(const uint8_t cb : coefficients) { + sum ^= tss_gf_mul(X_i, cb); + X_i = tss_gf_mul(X_i, X); } shares[j].m_contents.push_back(sum); @@ -216,7 +207,7 @@ throw Decoding_Error("Different sized RTSS shares detected"); } - if(!CT::is_equal(&shares[0].m_contents[0], &shares[i].m_contents[0], RTSS_HEADER_SIZE).as_bool()) { + if(!CT::is_equal(shares[0].m_contents.data(), shares[i].m_contents.data(), RTSS_HEADER_SIZE).as_bool()) { throw Decoding_Error("Different RTSS headers detected"); } } @@ -236,7 +227,7 @@ if(shares[0].size() != RTSS_HEADER_SIZE + share_len) { /* - * This second (laxer) check accomodates a bug in TSS that was + * This second (laxer) check accommodates a bug in TSS that was * fixed in 2.9.0 - previous versions used the length of the * *secret* here, instead of the length of the *share*, which is * precisely 1 + hash_len longer. @@ -249,33 +240,43 @@ std::vector V(shares.size()); secure_vector recovered; + // Compute the Lagrange coefficients + std::vector lagrange_coeffs(shares.size()); + for(size_t k = 0; k != shares.size(); ++k) { + uint8_t coeff = 1; + for(size_t l = 0; l != shares.size(); ++l) { + if(k == l) { + continue; + } + const uint8_t share_k = shares[k].share_id(); + const uint8_t share_l = shares[l].share_id(); + if(share_k == share_l) { + throw Decoding_Error("Duplicate shares found in RTSS recovery"); + } + // We already verified this earlier in the function + BOTAN_ASSERT_NOMSG(share_k > 0 && share_l > 0); + const uint8_t div = tss_gf_mul(share_l, tss_gf_inv(share_k ^ share_l)); + coeff = tss_gf_mul(coeff, div); + } + lagrange_coeffs[k] = coeff; + } + for(size_t i = RTSS_HEADER_SIZE + 1; i != shares[0].size(); ++i) { for(size_t j = 0; j != V.size(); ++j) { V[j] = shares[j].m_contents[i]; } + /* + * Interpolation step + * + * This is effectively a multi-scalar multiplication (aka sum-of-products) + * where one of the inputs, namely the Lagrange coefficients, are public. + * If optimizing this function further was useful, this would be the place + * to start, for example by using Pippeneger's algorithm. + */ uint8_t r = 0; for(size_t k = 0; k != shares.size(); ++k) { - // L_i function: - uint8_t r2 = 1; - for(size_t l = 0; l != shares.size(); ++l) { - if(k == l) { - continue; - } - - uint8_t share_k = shares[k].share_id(); - uint8_t share_l = shares[l].share_id(); - - if(share_k == share_l) { - throw Decoding_Error("Duplicate shares found in RTSS recovery"); - } - - uint8_t div = RTSS_EXP[(255 + RTSS_LOG[share_l] - RTSS_LOG[share_k ^ share_l]) % 255]; - - r2 = gfp_mul(r2, div); - } - - r ^= gfp_mul(V[k], r2); + r ^= tss_gf_mul(V[k], lagrange_coeffs[k]); } recovered.push_back(r); } diff -Nru botan3-3.7.1+dfsg/src/lib/misc/zfec/zfec.cpp botan3-3.12.0+dfsg/src/lib/misc/zfec/zfec.cpp --- botan3-3.7.1+dfsg/src/lib/misc/zfec/zfec.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/misc/zfec/zfec.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,15 +12,18 @@ #include #include -#include #include #include +#if defined(BOTAN_HAS_CPUID) + #include +#endif + namespace Botan { namespace { -/* Tables for arithetic in GF(2^8) using 1+x^2+x^3+x^4+x^8 +/* Tables for arithmetic in GF(2^8) using 1+x^2+x^3+x^4+x^8 * * See Lin & Costello, Appendix A, and Lee & Messerschmitt, p. 453. * @@ -99,7 +102,7 @@ std::vector m_table; }; - static GF_Table table; + static const GF_Table table; return table.ptr(y); } @@ -108,7 +111,7 @@ * (Gauss-Jordan algorithm, adapted from Numerical Recipes in C) */ void invert_matrix(uint8_t matrix[], size_t K) { - class pivot_searcher { + class pivot_searcher final { public: explicit pivot_searcher(size_t K) : m_ipiv(K) {} @@ -174,7 +177,7 @@ pivot_row[icol] = 1; if(c == 0) { - throw Invalid_Argument("ZFEC: singlar matrix"); + throw Invalid_Argument("ZFEC: singular matrix"); } if(c != 1) { @@ -289,7 +292,7 @@ const uint8_t* GF_MUL_Y = GF_MUL_TABLE(y); // first align z to 16 bytes - while(size > 0 && reinterpret_cast(z) % 16) { + while(size > 0 && reinterpret_cast(z) % 16 > 0) { z[0] ^= GF_MUL_Y[x[0]]; ++z; ++x; @@ -297,7 +300,7 @@ } #if defined(BOTAN_HAS_ZFEC_VPERM) - if(size >= 16 && CPUID::has_vperm()) { + if(size >= 16 && CPUID::has(CPUID::Feature::SIMD_4X32)) { const size_t consumed = addmul_vperm(z, x, y, size); z += consumed; x += consumed; @@ -305,15 +308,6 @@ } #endif -#if defined(BOTAN_HAS_ZFEC_SSE2) - if(size >= 64 && CPUID::has_sse2()) { - const size_t consumed = addmul_sse2(z, x, y, size); - z += consumed; - x += consumed; - size -= consumed; - } -#endif - while(size >= 16) { z[0] ^= GF_MUL_Y[x[0]]; z[1] ^= GF_MUL_Y[x[1]]; @@ -364,7 +358,7 @@ * K*K Vandermonde matrix, multiply right the bottom n-K rows * by the inverse, and construct the identity matrix at the top. */ - create_inverted_vdm(&temp_matrix[0], m_K); + create_inverted_vdm(temp_matrix.data(), m_K); for(size_t i = m_K * m_K; i != temp_matrix.size(); ++i) { temp_matrix[i] = GF_EXP[((i / m_K) * (i % m_K)) % 255]; @@ -429,10 +423,10 @@ clear_mem(fec_buf.data(), fec_buf.size()); for(size_t j = 0; j != m_K; ++j) { - addmul(&fec_buf[0], shares[j], m_enc_matrix[i * m_K + j], share_size); + addmul(fec_buf.data(), shares[j], m_enc_matrix[i * m_K + j], share_size); } - output_cb(i, &fec_buf[0], fec_buf.size()); + output_cb(i, fec_buf.data(), fec_buf.size()); } } @@ -506,35 +500,29 @@ // If we had the original data shares then no need to perform // a matrix inversion, return immediately. if(!missing_primary_share) { - for(size_t i = 0; i != indexes.size(); ++i) { - BOTAN_ASSERT_NOMSG(indexes[i] < m_K); + for(const size_t index : indexes) { + BOTAN_ASSERT_NOMSG(index < m_K); } return; } - invert_matrix(&decoding_matrix[0], m_K); + invert_matrix(decoding_matrix.data(), m_K); for(size_t i = 0; i != indexes.size(); ++i) { if(indexes[i] >= m_K) { std::vector buf(share_size); for(size_t col = 0; col != m_K; ++col) { - addmul(&buf[0], sharesv[col], decoding_matrix[i * m_K + col], share_size); + addmul(buf.data(), sharesv[col], decoding_matrix[i * m_K + col], share_size); } - output_cb(i, &buf[0], share_size); + output_cb(i, buf.data(), share_size); } } } std::string ZFEC::provider() const { #if defined(BOTAN_HAS_ZFEC_VPERM) - if(CPUID::has_vperm()) { - return "vperm"; - } -#endif - -#if defined(BOTAN_HAS_ZFEC_SSE2) - if(CPUID::has_sse2()) { - return "sse2"; + if(auto feat = CPUID::check(CPUID::Feature::SIMD_4X32)) { + return *feat; } #endif diff -Nru botan3-3.7.1+dfsg/src/lib/misc/zfec/zfec.h botan3-3.12.0+dfsg/src/lib/misc/zfec/zfec.h --- botan3-3.7.1+dfsg/src/lib/misc/zfec/zfec.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/misc/zfec/zfec.h 2026-05-07 01:38:28.000000000 +0000 @@ -72,10 +72,6 @@ private: static void addmul(uint8_t z[], const uint8_t x[], uint8_t y, size_t size); -#if defined(BOTAN_HAS_ZFEC_SSE2) - static size_t addmul_sse2(uint8_t z[], const uint8_t x[], uint8_t y, size_t size); -#endif - #if defined(BOTAN_HAS_ZFEC_VPERM) static size_t addmul_vperm(uint8_t z[], const uint8_t x[], uint8_t y, size_t size); #endif diff -Nru botan3-3.7.1+dfsg/src/lib/misc/zfec/zfec_sse2/info.txt botan3-3.12.0+dfsg/src/lib/misc/zfec/zfec_sse2/info.txt --- botan3-3.7.1+dfsg/src/lib/misc/zfec/zfec_sse2/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/misc/zfec/zfec_sse2/info.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ - -ZFEC_SSE2 -> 20211211 - - - -name -> "ZFEC SSE2" -brief -> "ZFEC using SSE2 instructions" - - - -sse2 - - - -simd - diff -Nru botan3-3.7.1+dfsg/src/lib/misc/zfec/zfec_sse2/zfec_sse2.cpp botan3-3.12.0+dfsg/src/lib/misc/zfec/zfec_sse2/zfec_sse2.cpp --- botan3-3.7.1+dfsg/src/lib/misc/zfec/zfec_sse2/zfec_sse2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/misc/zfec/zfec_sse2/zfec_sse2.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,93 +0,0 @@ -/* -* (C) 2009,2010,2021 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include - -namespace Botan { - -namespace { - -inline SIMD_4x32 rshift_1_u8(SIMD_4x32 v) { - return SIMD_4x32(_mm_add_epi8(v.raw(), v.raw())); -} - -inline SIMD_4x32 high_bit_set_u8(SIMD_4x32 v) { - return SIMD_4x32(_mm_cmpgt_epi8(_mm_setzero_si128(), v.raw())); -} - -} // namespace - -BOTAN_FUNC_ISA("sse2") size_t ZFEC::addmul_sse2(uint8_t z[], const uint8_t x[], uint8_t y, size_t size) { - const SIMD_4x32 polynomial = SIMD_4x32::splat_u8(0x1D); - - const size_t orig_size = size; - - // unrolled out to cache line size - while(size >= 64) { - SIMD_4x32 x_1 = SIMD_4x32::load_le(x); - SIMD_4x32 x_2 = SIMD_4x32::load_le(x + 16); - SIMD_4x32 x_3 = SIMD_4x32::load_le(x + 32); - SIMD_4x32 x_4 = SIMD_4x32::load_le(x + 48); - - SIMD_4x32 z_1 = SIMD_4x32::load_le(z); - SIMD_4x32 z_2 = SIMD_4x32::load_le(z + 16); - SIMD_4x32 z_3 = SIMD_4x32::load_le(z + 32); - SIMD_4x32 z_4 = SIMD_4x32::load_le(z + 48); - - if(y & 0x01) { - z_1 ^= x_1; - z_2 ^= x_2; - z_3 ^= x_3; - z_4 ^= x_4; - } - - for(size_t j = 1; j != 8; ++j) { - /* - * Each byte of each mask is either 0 or the polynomial 0x1D, - * depending on if the high bit of x_i is set or not. - */ - - const SIMD_4x32 mask_1(high_bit_set_u8(x_1)); - const SIMD_4x32 mask_2(high_bit_set_u8(x_2)); - const SIMD_4x32 mask_3(high_bit_set_u8(x_3)); - const SIMD_4x32 mask_4(high_bit_set_u8(x_4)); - - // x <<= 1 - x_1 = rshift_1_u8(x_1); - x_2 = rshift_1_u8(x_2); - x_3 = rshift_1_u8(x_3); - x_4 = rshift_1_u8(x_4); - - x_1 ^= mask_1 & polynomial; - x_2 ^= mask_2 & polynomial; - x_3 ^= mask_3 & polynomial; - x_4 ^= mask_4 & polynomial; - - if((y >> j) & 1) { - z_1 ^= x_1; - z_2 ^= x_2; - z_3 ^= x_3; - z_4 ^= x_4; - } - } - - z_1.store_le(z); - z_2.store_le(z + 16); - z_3.store_le(z + 32); - z_4.store_le(z + 48); - - x += 64; - z += 64; - size -= 64; - } - - return orig_size - size; -} - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/misc/zfec/zfec_vperm/info.txt botan3-3.12.0+dfsg/src/lib/misc/zfec/zfec_vperm/info.txt --- botan3-3.7.1+dfsg/src/lib/misc/zfec/zfec_vperm/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/misc/zfec/zfec_vperm/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + ZFEC_VPERM -> 20211211 - + name -> "ZFEC Vector Permutation" @@ -8,12 +8,12 @@ -x86_32:sse2 -x86_64:sse2 x86_32:ssse3 x86_64:ssse3 arm32:neon arm64:neon +loongarch64:lsx +wasm:simd128 @@ -21,8 +21,11 @@ x86_64 arm32 arm64 +loongarch64 +wasm -simd +cpuid +simd_4x32 diff -Nru botan3-3.7.1+dfsg/src/lib/misc/zfec/zfec_vperm/zfec_vperm.cpp botan3-3.12.0+dfsg/src/lib/misc/zfec/zfec_vperm/zfec_vperm.cpp --- botan3-3.7.1+dfsg/src/lib/misc/zfec/zfec_vperm/zfec_vperm.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/misc/zfec/zfec_vperm/zfec_vperm.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,11 +7,8 @@ #include -#include - -#if defined(BOTAN_SIMD_USE_SSE2) - #include -#endif +#include +#include namespace Botan { @@ -458,29 +455,9 @@ 0x48, 0xb7, 0x70, 0x8f, 0x93, 0x6c, 0x00, 0x4b, 0x96, 0xdd, 0x31, 0x7a, 0xa7, 0xec, 0x62, 0x29, 0xf4, 0xbf, 0x53, 0x18, 0xc5, 0x8e}; -inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) table_lookup(SIMD_4x32 t, SIMD_4x32 v) { -#if defined(BOTAN_SIMD_USE_SSE2) - return SIMD_4x32(_mm_shuffle_epi8(t.raw(), v.raw())); -#elif defined(BOTAN_SIMD_USE_NEON) - const uint8x16_t tbl = vreinterpretq_u8_u32(t.raw()); - const uint8x16_t idx = vreinterpretq_u8_u32(v.raw()); - - #if defined(BOTAN_TARGET_ARCH_IS_ARM32) - const uint8x8x2_t tbl2 = {vget_low_u8(tbl), vget_high_u8(tbl)}; - - return SIMD_4x32( - vreinterpretq_u32_u8(vcombine_u8(vtbl2_u8(tbl2, vget_low_u8(idx)), vtbl2_u8(tbl2, vget_high_u8(idx))))); - - #else - return SIMD_4x32(vreinterpretq_u32_u8(vqtbl1q_u8(tbl, idx))); - #endif - -#endif -} - } // namespace -BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) size_t ZFEC::addmul_vperm(uint8_t z[], const uint8_t x[], uint8_t y, size_t size) { +BOTAN_FN_ISA_SIMD_4X32 size_t ZFEC::addmul_vperm(uint8_t z[], const uint8_t x[], uint8_t y, size_t size) { const auto mask = SIMD_4x32::splat_u8(0x0F); // fetch the lookup tables for the given y @@ -499,8 +476,8 @@ const auto x_hi = x_1.shr<4>() & mask; // 16x parallel lookups - const auto r_lo = table_lookup(t_lo, x_lo); - const auto r_hi = table_lookup(t_hi, x_hi); + const auto r_lo = SIMD_4x32::byte_shuffle(t_lo, x_lo); + const auto r_hi = SIMD_4x32::byte_shuffle(t_hi, x_hi); // sum the outputs. z_1 ^= r_lo; diff -Nru botan3-3.7.1+dfsg/src/lib/modes/aead/aead.cpp botan3-3.12.0+dfsg/src/lib/modes/aead/aead.cpp --- botan3-3.7.1+dfsg/src/lib/modes/aead/aead.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/aead/aead.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,6 +6,8 @@ #include +#include +#include #include #include #include @@ -38,6 +40,10 @@ #include #endif +#if defined(BOTAN_HAS_ASCON_AEAD128) + #include +#endif + namespace Botan { std::unique_ptr AEAD_Mode::create_or_throw(std::string_view algo, @@ -62,9 +68,19 @@ } #endif +#if defined(BOTAN_HAS_ASCON_AEAD128) + if(algo == "Ascon-AEAD128") { + if(dir == Cipher_Dir::Encryption) { + return std::make_unique(); + } else { + return std::make_unique(); + } + } +#endif + if(algo.find('/') != std::string::npos) { const std::vector algo_parts = split_on(algo, '/'); - std::string_view cipher_name = algo_parts[0]; + const std::string_view cipher_name = algo_parts[0]; const std::vector mode_info = parse_algorithm_name(algo_parts[1]); if(mode_info.empty()) { @@ -87,7 +103,7 @@ #if defined(BOTAN_HAS_BLOCK_CIPHER) - SCAN_Name req(algo); + const SCAN_Name req(algo); if(req.arg_count() == 0) { return std::unique_ptr(); @@ -101,8 +117,8 @@ #if defined(BOTAN_HAS_AEAD_CCM) if(req.algo_name() == "CCM") { - size_t tag_len = req.arg_as_integer(1, 16); - size_t L_len = req.arg_as_integer(2, 3); + const size_t tag_len = req.arg_as_integer(1, 16); + const size_t L_len = req.arg_as_integer(2, 3); if(dir == Cipher_Dir::Encryption) { return std::make_unique(std::move(bc), tag_len, L_len); } else { @@ -113,7 +129,7 @@ #if defined(BOTAN_HAS_AEAD_GCM) if(req.algo_name() == "GCM") { - size_t tag_len = req.arg_as_integer(1, 16); + const size_t tag_len = req.arg_as_integer(1, 16); if(dir == Cipher_Dir::Encryption) { return std::make_unique(std::move(bc), tag_len); } else { @@ -124,7 +140,7 @@ #if defined(BOTAN_HAS_AEAD_OCB) if(req.algo_name() == "OCB") { - size_t tag_len = req.arg_as_integer(1, 16); + const size_t tag_len = req.arg_as_integer(1, 16); if(dir == Cipher_Dir::Encryption) { return std::make_unique(std::move(bc), tag_len); } else { @@ -135,7 +151,7 @@ #if defined(BOTAN_HAS_AEAD_EAX) if(req.algo_name() == "EAX") { - size_t tag_len = req.arg_as_integer(1, bc->block_size()); + const size_t tag_len = req.arg_as_integer(1, bc->block_size()); if(dir == Cipher_Dir::Encryption) { return std::make_unique(std::move(bc), tag_len); } else { diff -Nru botan3-3.7.1+dfsg/src/lib/modes/aead/aead.h botan3-3.12.0+dfsg/src/lib/modes/aead/aead.h --- botan3-3.7.1+dfsg/src/lib/modes/aead/aead.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/aead/aead.h 2026-05-07 01:38:28.000000000 +0000 @@ -128,8 +128,6 @@ * modes, and large enough that random collisions are unlikely) */ size_t default_nonce_length() const override { return 12; } - - ~AEAD_Mode() override = default; }; /** diff -Nru botan3-3.7.1+dfsg/src/lib/modes/aead/ascon_aead128/ascon_aead128.cpp botan3-3.12.0+dfsg/src/lib/modes/aead/ascon_aead128/ascon_aead128.cpp --- botan3-3.7.1+dfsg/src/lib/modes/aead/ascon_aead128/ascon_aead128.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/aead/ascon_aead128/ascon_aead128.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,156 @@ +/* +* Ascon-AEAD128 AEAD +* (C) 2025 Jack Lloyd +* 2025 René Meusel +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include + +namespace Botan { + +namespace { + +constexpr void xor2x64(std::span lhs, std::span rhs) { + lhs[0] ^= rhs[0]; + lhs[1] ^= rhs[1]; +} + +template +constexpr auto as_array_of_uint64(std::span in) { + BOTAN_DEBUG_ASSERT(in.size() == N * sizeof(uint64_t)); + return load_le>(in.first()); +} + +// NIST SP.800-232 Appendix B (Table 13) +constexpr Ascon_p initial_state_of_ascon_aead_permutation({ + .init_and_final_rounds = 12, + .processing_rounds = 8, + .bit_rate = 128, + .initial_state = {}, +}); + +// NIST SP.800-232 Section 5.1 +constexpr uint64_t ascon_aead_128_iv = 0x00001000808c0001; + +// NIST SP.800-232 Appendix A.2 +constexpr uint64_t ascon_aead_128_domain_sep = 0x8000000000000000; + +} // namespace + +Ascon_AEAD128_Mode::Ascon_AEAD128_Mode() : m_ascon_p(initial_state_of_ascon_aead_permutation) {} + +void Ascon_AEAD128_Mode::clear() { + m_key.reset(); + m_ad.clear(); + reset(); +} + +void Ascon_AEAD128_Mode::reset() { + m_ascon_p = initial_state_of_ascon_aead_permutation; + m_started = false; + m_has_nonce = false; +} + +void Ascon_AEAD128_Mode::key_schedule(std::span key) { + clear(); + m_key = as_array_of_uint64<2>(key); +} + +void Ascon_AEAD128_Mode::set_associated_data_n(size_t idx, std::span ad) { + BOTAN_ARG_CHECK(idx == 0, "Ascon-AEAD128: cannot handle non-zero index in set_associated_data_n"); + m_ad.assign(ad.begin(), ad.end()); +} + +void Ascon_AEAD128_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) { + BOTAN_ARG_CHECK(valid_nonce_length(nonce_len), "Invalid nonce length in Ascon-AEAD128"); + + BOTAN_STATE_CHECK(has_keying_material()); + BOTAN_STATE_CHECK(!m_started); + + m_ascon_p.state() = concat(std::array{ascon_aead_128_iv}, *m_key, as_array_of_uint64<2>({nonce, nonce_len})); + m_ascon_p.initial_permute(); + xor2x64(m_ascon_p.range_of_state<3, 2>(), *m_key); + + m_has_nonce = true; +} + +void Ascon_AEAD128_Mode::maybe_absorb_associated_data() { + BOTAN_DEBUG_ASSERT(has_keying_material()); + BOTAN_DEBUG_ASSERT(m_has_nonce); + + if(!m_started) { + if(!m_ad.empty()) { + m_ascon_p.absorb(m_ad); + m_ascon_p.intermediate_finish(); + } + m_ascon_p.state()[4] ^= ascon_aead_128_domain_sep; + + m_started = true; + } +} + +std::array Ascon_AEAD128_Mode::calculate_tag_and_finish() { + BOTAN_DEBUG_ASSERT(m_started); + + xor2x64(m_ascon_p.range_of_state<2, 2>(), *m_key); + m_ascon_p.finish(); + xor2x64(m_ascon_p.range_of_state<3, 2>(), *m_key); + + auto tag = store_le(m_ascon_p.range_of_state<3, 2>()); + + reset(); + return tag; +} + +size_t Ascon_AEAD128_Encryption::process_msg(uint8_t buf[], size_t size) { + BOTAN_STATE_CHECK(has_keying_material()); + BOTAN_STATE_CHECK(m_has_nonce); + + maybe_absorb_associated_data(); + m_ascon_p.percolate_in({buf, size}); + return size; +} + +void Ascon_AEAD128_Encryption::finish_msg(secure_vector& final_block, size_t offset) { + BOTAN_STATE_CHECK(has_keying_material()); + + const auto final_block_at_offset = std::span{final_block}.subspan(offset); + process_msg(final_block_at_offset.data(), final_block_at_offset.size()); + const auto tag = calculate_tag_and_finish(); + final_block.insert(final_block.end(), tag.begin(), tag.end()); +} + +size_t Ascon_AEAD128_Decryption::process_msg(uint8_t buf[], size_t size) { + BOTAN_STATE_CHECK(has_keying_material()); + BOTAN_STATE_CHECK(m_has_nonce); + + maybe_absorb_associated_data(); + m_ascon_p.percolate_out({buf, size}); + return size; +} + +void Ascon_AEAD128_Decryption::finish_msg(secure_vector& final_block, size_t offset) { + BOTAN_STATE_CHECK(has_keying_material()); + + const auto final_block_at_offset = std::span{final_block}.subspan(offset); + BOTAN_ARG_CHECK(final_block_at_offset.size() >= tag_size(), "input did not include the tag"); + const auto final_ciphertext_block = final_block_at_offset.first(final_block_at_offset.size() - tag_size()); + const auto expected_tag = final_block_at_offset.last(tag_size()); + + process_msg(final_ciphertext_block.data(), final_ciphertext_block.size()); + if(!constant_time_compare(calculate_tag_and_finish(), expected_tag)) { + clear_mem(std::span{final_block}.subspan(offset, final_ciphertext_block.size())); + throw Invalid_Authentication_Tag("Ascon-AEAD128 tag check failed"); + } + + final_block.resize(offset + final_ciphertext_block.size()); +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/modes/aead/ascon_aead128/ascon_aead128.h botan3-3.12.0+dfsg/src/lib/modes/aead/ascon_aead128/ascon_aead128.h --- botan3-3.7.1+dfsg/src/lib/modes/aead/ascon_aead128/ascon_aead128.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/aead/ascon_aead128/ascon_aead128.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,99 @@ +/* +* Ascon-AEAD128 AEAD +* (C) 2025 Jack Lloyd +* 2025 René Meusel +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_ASCON_AEAD128_H_ +#define BOTAN_ASCON_AEAD128_H_ + +#include + +#include +#include + +#include + +namespace Botan { + +class Ascon_AEAD128_Mode : public AEAD_Mode { + public: + void set_associated_data_n(size_t idx, std::span ad) final; + + bool associated_data_requires_key() const final { return false; } + + std::string name() const final { return "Ascon-AEAD128"; } + + size_t update_granularity() const final { return 1; } + + size_t ideal_granularity() const final { return 32; } + + Key_Length_Specification key_spec() const final { return Key_Length_Specification(16); } + + bool valid_nonce_length(size_t n) const final { return n == 16; } + + size_t default_nonce_length() const final { return 16; } + + size_t tag_size() const final { return 16; } + + void clear() final; + + void reset() final; + + bool has_keying_material() const final { return m_key.has_value(); } + + protected: + Ascon_AEAD128_Mode(); + + void start_msg(const uint8_t nonce[], size_t nonce_len) final; + void key_schedule(std::span key) final; + + void maybe_absorb_associated_data(); + std::array calculate_tag_and_finish(); + + protected: + std::optional> m_key; // NOLINT(*-non-private-member-*) + Ascon_p m_ascon_p; // NOLINT(*-non-private-member-*) + bool m_has_nonce = false; // NOLINT(*-non-private-member-*) + + private: + std::vector m_ad; + bool m_started = false; +}; + +/** +* Ascon-AEAD128 Encryption +*/ +class Ascon_AEAD128_Encryption final : public Ascon_AEAD128_Mode { + public: + size_t output_length(size_t input_length) const override { return input_length + tag_size(); } + + size_t minimum_final_size() const override { return 0; } + + private: + size_t process_msg(uint8_t buf[], size_t size) final; + void finish_msg(secure_vector& final_block, size_t offset = 0) override; +}; + +/** +* Ascon-AEAD128 Decryption +*/ +class Ascon_AEAD128_Decryption final : public Ascon_AEAD128_Mode { + public: + size_t output_length(size_t input_length) const override { + BOTAN_ARG_CHECK(input_length >= tag_size(), "Sufficient input"); + return input_length - tag_size(); + } + + size_t minimum_final_size() const override { return tag_size(); } + + private: + size_t process_msg(uint8_t buf[], size_t size) final; + void finish_msg(secure_vector& final_block, size_t offset = 0) override; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/modes/aead/ascon_aead128/info.txt botan3-3.12.0+dfsg/src/lib/modes/aead/ascon_aead128/info.txt --- botan3-3.7.1+dfsg/src/lib/modes/aead/ascon_aead128/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/aead/ascon_aead128/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,11 @@ + +ASCON_AEAD128 -> 20250823 + + + +name -> "Ascon-AEAD128" + + + +ascon_perm + diff -Nru botan3-3.7.1+dfsg/src/lib/modes/aead/ccm/ccm.cpp botan3-3.12.0+dfsg/src/lib/modes/aead/ccm/ccm.cpp --- botan3-3.7.1+dfsg/src/lib/modes/aead/ccm/ccm.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/aead/ccm/ccm.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,8 @@ #include +#include +#include #include #include #include @@ -50,8 +52,8 @@ return fmt("{}/CCM({},{})", m_cipher->name(), tag_size(), L()); } -bool CCM_Mode::valid_nonce_length(size_t n) const { - return (n == (15 - L())); +bool CCM_Mode::valid_nonce_length(size_t length) const { + return (length == (15 - L())); } size_t CCM_Mode::default_nonce_length() const { @@ -95,7 +97,7 @@ m_ad_buf.push_back(get_byte<0>(static_cast(ad.size()))); m_ad_buf.push_back(get_byte<1>(static_cast(ad.size()))); m_ad_buf.insert(m_ad_buf.end(), ad.begin(), ad.end()); - while(m_ad_buf.size() % CCM_BS) { + while(m_ad_buf.size() % CCM_BS != 0) { m_ad_buf.push_back(0); // pad with zeros to full block size } } @@ -113,6 +115,15 @@ size_t CCM_Mode::process_msg(uint8_t buf[], size_t sz) { BOTAN_STATE_CHECK(!m_nonce.empty()); m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz); + + // CCM message length is limited to 2^(8*L) - 1 bytes + if(L() < 8) { + const uint64_t max_msg_len = (static_cast(1) << (8 * L())) - 1; + if(m_msg_buf.size() > max_msg_len) { + throw Invalid_State("CCM message length exceeds the limit for L"); + } + } + return 0; // no output until finished } @@ -132,7 +143,9 @@ void CCM_Mode::inc(secure_vector& C) { for(size_t i = 0; i != C.size(); ++i) { - if(++C[C.size() - i - 1]) { + uint8_t& b = C[C.size() - i - 1]; + b += 1; + if(b > 0) { break; } } @@ -267,6 +280,7 @@ T ^= S0; if(!CT::is_equal(T.data(), buf_end, tag_size()).as_bool()) { + clear_mem(std::span{buffer}.subspan(offset, sz - tag_size())); throw Invalid_Authentication_Tag("CCM tag check failed"); } diff -Nru botan3-3.7.1+dfsg/src/lib/modes/aead/ccm/ccm.h botan3-3.12.0+dfsg/src/lib/modes/aead/ccm/ccm.h --- botan3-3.7.1+dfsg/src/lib/modes/aead/ccm/ccm.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/aead/ccm/ccm.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,8 @@ #define BOTAN_AEAD_CCM_H_ #include + +#include #include namespace Botan { @@ -34,7 +36,7 @@ Key_Length_Specification key_spec() const final; - bool valid_nonce_length(size_t) const final; + bool valid_nonce_length(size_t length) const final; size_t default_nonce_length() const final; @@ -89,7 +91,7 @@ * @param L length of L parameter. The total message length * must be less than 2**L bytes, and the nonce is 15-L bytes. */ - CCM_Encryption(std::unique_ptr cipher, size_t tag_size = 16, size_t L = 3) : + explicit CCM_Encryption(std::unique_ptr cipher, size_t tag_size = 16, size_t L = 3) : CCM_Mode(std::move(cipher), tag_size, L) {} size_t output_length(size_t input_length) const override { return input_length + tag_size(); } @@ -112,7 +114,7 @@ * @param L length of L parameter. The total message length * must be less than 2**L bytes, and the nonce is 15-L bytes. */ - CCM_Decryption(std::unique_ptr cipher, size_t tag_size = 16, size_t L = 3) : + explicit CCM_Decryption(std::unique_ptr cipher, size_t tag_size = 16, size_t L = 3) : CCM_Mode(std::move(cipher), tag_size, L) {} size_t output_length(size_t input_length) const override { diff -Nru botan3-3.7.1+dfsg/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp botan3-3.12.0+dfsg/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp --- botan3-3.7.1+dfsg/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,8 @@ #include +#include +#include #include #include @@ -60,9 +62,9 @@ m_ad.assign(ad.begin(), ad.end()); } -void ChaCha20Poly1305_Mode::update_len(size_t len) { +void ChaCha20Poly1305_Mode::update_len(uint64_t len) { uint8_t len8[8] = {0}; - store_le(static_cast(len), len8); + store_le(len, len8); m_poly1305->update(len8, 8); } @@ -86,7 +88,7 @@ m_poly1305->update(m_ad); if(cfrg_version()) { - if(m_ad.size() % 16) { + if(m_ad.size() % 16 != 0) { const uint8_t zeros[16] = {0}; m_poly1305->update(zeros, 16 - m_ad.size() % 16); } @@ -99,15 +101,23 @@ m_chacha->cipher1(buf, sz); m_poly1305->update(buf, sz); // poly1305 of ciphertext m_ctext_len += sz; + + // RFC 8439 limits messages to 2^38-64 bytes + constexpr uint64_t MAX_CHACHA20POLY1305_INPUT = (static_cast(1) << 38) - 64; + if(cfrg_version() && m_ctext_len > MAX_CHACHA20POLY1305_INPUT) { + throw Invalid_State("ChaCha20Poly1305 message length limit exceeded"); + } + return sz; } void ChaCha20Poly1305_Encryption::finish_msg(secure_vector& buffer, size_t offset) { update(buffer, offset); if(cfrg_version()) { - if(m_ctext_len % 16) { + if(m_ctext_len % 16 != 0) { const uint8_t zeros[16] = {0}; - m_poly1305->update(zeros, 16 - m_ctext_len % 16); + const size_t padding = static_cast(16 - m_ctext_len % 16); + m_poly1305->update(zeros, padding); } update_len(m_ad.size()); } @@ -123,6 +133,12 @@ m_poly1305->update(buf, sz); // poly1305 of ciphertext m_chacha->cipher1(buf, sz); m_ctext_len += sz; + + constexpr uint64_t MAX_CHACHA20POLY1305_INPUT = (static_cast(1) << 38) - 64; + if(cfrg_version() && m_ctext_len > MAX_CHACHA20POLY1305_INPUT) { + throw Invalid_State("ChaCha20Poly1305 message length limit exceeded"); + } + return sz; } @@ -135,16 +151,17 @@ const size_t remaining = sz - tag_size(); - if(remaining) { + if(remaining > 0) { m_poly1305->update(buf, remaining); // poly1305 of ciphertext m_chacha->cipher1(buf, remaining); m_ctext_len += remaining; } if(cfrg_version()) { - if(m_ctext_len % 16) { + if(m_ctext_len % 16 != 0) { const uint8_t zeros[16] = {0}; - m_poly1305->update(zeros, 16 - m_ctext_len % 16); + const size_t padding = static_cast(16 - m_ctext_len % 16); + m_poly1305->update(zeros, padding); } update_len(m_ad.size()); } @@ -160,6 +177,7 @@ m_nonce_len = 0; if(!CT::is_equal(mac, included_tag, tag_size()).as_bool()) { + clear_mem(std::span{buffer}.subspan(offset, remaining)); throw Invalid_Authentication_Tag("ChaCha20Poly1305 tag check failed"); } buffer.resize(offset + remaining); diff -Nru botan3-3.7.1+dfsg/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h botan3-3.12.0+dfsg/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h --- botan3-3.7.1+dfsg/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,8 @@ #define BOTAN_AEAD_CHACHA20_POLY1305_H_ #include + +#include #include #include @@ -47,18 +49,18 @@ bool has_keying_material() const final; protected: - std::unique_ptr m_chacha; - std::unique_ptr m_poly1305; + std::unique_ptr m_chacha; // NOLINT(*non-private-member-variable*) + std::unique_ptr m_poly1305; // NOLINT(*non-private-member-variable*) ChaCha20Poly1305_Mode(); - secure_vector m_ad; - size_t m_nonce_len = 0; - size_t m_ctext_len = 0; + secure_vector m_ad; // NOLINT(*non-private-member-variable*) + size_t m_nonce_len = 0; // NOLINT(*non-private-member-variable*) + uint64_t m_ctext_len = 0; // NOLINT(*non-private-member-variable*) bool cfrg_version() const { return m_nonce_len == 12 || m_nonce_len == 24; } - void update_len(size_t len); + void update_len(uint64_t len); private: void start_msg(const uint8_t nonce[], size_t nonce_len) override; diff -Nru botan3-3.7.1+dfsg/src/lib/modes/aead/eax/eax.cpp botan3-3.12.0+dfsg/src/lib/modes/aead/eax/eax.cpp --- botan3-3.7.1+dfsg/src/lib/modes/aead/eax/eax.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/aead/eax/eax.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,8 @@ #include +#include +#include #include #include #include @@ -99,7 +101,7 @@ */ void EAX_Mode::set_associated_data_n(size_t idx, std::span ad) { BOTAN_ARG_CHECK(idx == 0, "EAX: cannot handle non-zero index in set_associated_data_n"); - if(m_nonce_mac.empty() == false) { + if(!m_nonce_mac.empty()) { throw Invalid_State("Cannot set AD for EAX while processing a message"); } m_ad_mac = eax_prf(1, block_size(), *m_cmac, ad.data(), ad.size()); @@ -153,6 +155,7 @@ } void EAX_Decryption::finish_msg(secure_vector& buffer, size_t offset) { + BOTAN_STATE_CHECK(!m_nonce_mac.empty()); BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range"); const size_t sz = buffer.size() - offset; uint8_t* buf = buffer.data() + offset; @@ -161,7 +164,7 @@ const size_t remaining = sz - tag_size(); - if(remaining) { + if(remaining > 0) { m_cmac->update(buf, remaining); m_ctr->cipher(buf, buf, remaining); } @@ -184,6 +187,7 @@ m_nonce_mac.clear(); if(!accept_mac) { + clear_mem(std::span{buffer}.subspan(offset, remaining)); throw Invalid_Authentication_Tag("EAX tag check failed"); } } diff -Nru botan3-3.7.1+dfsg/src/lib/modes/aead/eax/eax.h botan3-3.12.0+dfsg/src/lib/modes/aead/eax/eax.h --- botan3-3.7.1+dfsg/src/lib/modes/aead/eax/eax.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/aead/eax/eax.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,8 @@ #define BOTAN_AEAD_EAX_H_ #include + +#include #include #include #include @@ -32,7 +34,7 @@ Key_Length_Specification key_spec() const final; // EAX supports arbitrary nonce lengths - bool valid_nonce_length(size_t) const final { return true; } + bool valid_nonce_length(size_t /*length*/) const final { return true; } size_t tag_size() const final { return m_tag_size; } @@ -51,15 +53,15 @@ size_t block_size() const { return m_cipher->block_size(); } - size_t m_tag_size; + size_t m_tag_size; // NOLINT(*non-private-member-variable*) - std::unique_ptr m_cipher; - std::unique_ptr m_ctr; - std::unique_ptr m_cmac; + std::unique_ptr m_cipher; // NOLINT(*non-private-member-variable*) + std::unique_ptr m_ctr; // NOLINT(*non-private-member-variable*) + std::unique_ptr m_cmac; // NOLINT(*non-private-member-variable*) - secure_vector m_ad_mac; + secure_vector m_ad_mac; // NOLINT(*non-private-member-variable*) - secure_vector m_nonce_mac; + secure_vector m_nonce_mac; // NOLINT(*non-private-member-variable*) private: void start_msg(const uint8_t nonce[], size_t nonce_len) final; @@ -76,7 +78,7 @@ * @param cipher a 128-bit block cipher * @param tag_size is how big the auth tag will be */ - EAX_Encryption(std::unique_ptr cipher, size_t tag_size = 0) : + explicit EAX_Encryption(std::unique_ptr cipher, size_t tag_size = 0) : EAX_Mode(std::move(cipher), tag_size) {} size_t output_length(size_t input_length) const override { return input_length + tag_size(); } @@ -97,7 +99,7 @@ * @param cipher a 128-bit block cipher * @param tag_size is how big the auth tag will be */ - EAX_Decryption(std::unique_ptr cipher, size_t tag_size = 0) : + explicit EAX_Decryption(std::unique_ptr cipher, size_t tag_size = 0) : EAX_Mode(std::move(cipher), tag_size) {} size_t output_length(size_t input_length) const override { diff -Nru botan3-3.7.1+dfsg/src/lib/modes/aead/gcm/gcm.cpp botan3-3.12.0+dfsg/src/lib/modes/aead/gcm/gcm.cpp --- botan3-3.7.1+dfsg/src/lib/modes/aead/gcm/gcm.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/aead/gcm/gcm.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,11 +9,12 @@ #include #include +#include +#include #include #include #include #include - #include namespace Botan { @@ -46,7 +47,7 @@ } void GCM_Mode::reset() { - m_ghash->reset(); + m_ghash->reset_state(); } std::string GCM_Mode::name() const { @@ -62,7 +63,7 @@ } size_t GCM_Mode::ideal_granularity() const { - return GCM_BS * std::max(2, BOTAN_BLOCK_CIPHER_PAR_MULT); + return GCM_BS * std::max(2, BlockCipher::ParallelismMult); } bool GCM_Mode::valid_nonce_length(size_t len) const { @@ -81,10 +82,10 @@ void GCM_Mode::key_schedule(std::span key) { m_ctr->set_key(key); - const std::vector zeros(GCM_BS); - m_ctr->set_iv(zeros.data(), zeros.size()); + std::array zeros{}; + m_ctr->set_iv(zeros); - secure_vector H(GCM_BS); + uint8_t H[GCM_BS] = {0}; m_ctr->encipher(H); m_ghash->set_key(H); } @@ -99,26 +100,22 @@ throw Invalid_IV_Length(name(), nonce_len); } - if(m_y0.size() != GCM_BS) { - m_y0.resize(GCM_BS); - } - - clear_mem(m_y0.data(), m_y0.size()); + std::array y0 = {}; if(nonce_len == 12) { - copy_mem(m_y0.data(), nonce, nonce_len); - m_y0[15] = 1; + copy_mem(y0.data(), nonce, nonce_len); + y0[15] = 1; } else { - m_ghash->nonce_hash(m_y0, {nonce, nonce_len}); + m_ghash->nonce_hash(std::span(y0), {nonce, nonce_len}); } - m_ctr->set_iv(m_y0.data(), m_y0.size()); + m_ctr->set_iv(y0.data(), y0.size()); - clear_mem(m_y0.data(), m_y0.size()); - m_ctr->encipher(m_y0); + clear_mem(y0.data(), y0.size()); + m_ctr->encipher(y0); - m_ghash->start(m_y0); - clear_mem(m_y0.data(), m_y0.size()); + m_ghash->start(y0); + secure_scrub_memory(y0); } size_t GCM_Encryption::process_msg(uint8_t buf[], size_t sz) { @@ -158,7 +155,7 @@ const size_t remaining = sz - tag_size(); // handle any final input before the tag - if(remaining) { + if(remaining > 0) { m_ghash->update({buf, remaining}); m_ctr->cipher(buf, buf, remaining); } @@ -169,6 +166,7 @@ const uint8_t* included_tag = &buffer[remaining + offset]; if(!CT::is_equal(mac.data(), included_tag, tag_size()).as_bool()) { + clear_mem(std::span{buffer}.subspan(offset, remaining)); throw Invalid_Authentication_Tag("GCM tag check failed"); } diff -Nru botan3-3.7.1+dfsg/src/lib/modes/aead/gcm/gcm.h botan3-3.12.0+dfsg/src/lib/modes/aead/gcm/gcm.h --- botan3-3.7.1+dfsg/src/lib/modes/aead/gcm/gcm.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/aead/gcm/gcm.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,8 @@ #define BOTAN_AEAD_GCM_H_ #include + +#include #include #include @@ -21,49 +23,47 @@ /** * GCM Mode */ -class GCM_Mode : public AEAD_Mode { +class GCM_Mode : public AEAD_Mode /* NOLINT(*-special-member-functions) */ { public: - void set_associated_data_n(size_t idx, std::span ad) override final; + void set_associated_data_n(size_t idx, std::span ad) final; - std::string name() const override final; + std::string name() const final; - size_t update_granularity() const override final; + size_t update_granularity() const final; - size_t ideal_granularity() const override final; + size_t ideal_granularity() const final; - Key_Length_Specification key_spec() const override final; + Key_Length_Specification key_spec() const final; - bool valid_nonce_length(size_t len) const override final; + bool valid_nonce_length(size_t len) const final; - size_t tag_size() const override final { return m_tag_size; } + size_t tag_size() const final { return m_tag_size; } - void clear() override final; + void clear() final; - void reset() override final; + void reset() final; - std::string provider() const override final; + std::string provider() const final; - bool has_keying_material() const override final; + bool has_keying_material() const final; - ~GCM_Mode(); + ~GCM_Mode() override; protected: GCM_Mode(std::unique_ptr cipher, size_t tag_size); static const size_t GCM_BS = 16; - const size_t m_tag_size; - const std::string m_cipher_name; + const size_t m_tag_size; // NOLINT(*non-private-member-variable*) + const std::string m_cipher_name; // NOLINT(*non-private-member-variable*) - std::unique_ptr m_ctr; - std::unique_ptr m_ghash; + std::unique_ptr m_ctr; // NOLINT(*non-private-member-variable*) + std::unique_ptr m_ghash; // NOLINT(*non-private-member-variable*) private: void start_msg(const uint8_t nonce[], size_t nonce_len) override; void key_schedule(std::span key) override; - - secure_vector m_y0; }; /** @@ -75,7 +75,7 @@ * @param cipher the 128 bit block cipher to use * @param tag_size is how big the auth tag will be */ - GCM_Encryption(std::unique_ptr cipher, size_t tag_size = 16) : + explicit GCM_Encryption(std::unique_ptr cipher, size_t tag_size = 16) : GCM_Mode(std::move(cipher), tag_size) {} size_t output_length(size_t input_length) const override { return input_length + tag_size(); } @@ -96,7 +96,7 @@ * @param cipher the 128 bit block cipher to use * @param tag_size is how big the auth tag will be */ - GCM_Decryption(std::unique_ptr cipher, size_t tag_size = 16) : + explicit GCM_Decryption(std::unique_ptr cipher, size_t tag_size = 16) : GCM_Mode(std::move(cipher), tag_size) {} size_t output_length(size_t input_length) const override { diff -Nru botan3-3.7.1+dfsg/src/lib/modes/aead/ocb/ocb.cpp botan3-3.12.0+dfsg/src/lib/modes/aead/ocb/ocb.cpp --- botan3-3.7.1+dfsg/src/lib/modes/aead/ocb/ocb.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/aead/ocb/ocb.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include #include @@ -33,7 +35,7 @@ // memory overhead is negligible. // // See also https://github.com/randombit/botan/issues/3812 - m_L.reserve(31); + m_L.reserve(65); m_L.push_back(poly_double(dollar())); while(m_L.size() < 8) { @@ -45,7 +47,7 @@ void init(const secure_vector& offset) { m_offset = offset; } - bool initialized() const { return m_offset.empty() == false; } + bool initialized() const { return !m_offset.empty(); } const secure_vector& star() const { return m_L_star; } @@ -61,7 +63,7 @@ return m_L[i]; } - const uint8_t* compute_offsets(size_t block_index, size_t blocks) { + const uint8_t* compute_offsets(uint64_t block_index, size_t blocks) { BOTAN_ASSERT(blocks <= m_max_blocks, "OCB offsets"); uint8_t* offsets = m_offset_buf.data(); @@ -75,7 +77,7 @@ // ntz(4*i+2) == 1 // ntz(4*i+3) == 0 block_index += 4; - const size_t ntz4 = var_ctz32(static_cast(block_index)); + const size_t ntz4 = var_ctz64(block_index); xor_buf(offsets, m_offset.data(), L0.data(), m_BS); offsets += m_BS; @@ -96,7 +98,7 @@ } for(size_t i = 0; i != blocks; ++i) { // could be done in parallel - const size_t ntz = var_ctz32(static_cast(block_index + i + 1)); + const size_t ntz = var_ctz64(block_index + i + 1); xor_buf(m_offset.data(), get(ntz).data(), m_BS); copy_mem(offsets, m_offset.data(), m_BS); offsets += m_BS; @@ -136,14 +138,14 @@ for(size_t i = 0; i != ad_blocks; ++i) { // this loop could run in parallel - offset ^= L.get(var_ctz32(static_cast(i + 1))); + offset ^= L.get(var_ctz64(i + 1)); buf = offset; xor_buf(buf.data(), &ad[BS * i], BS); cipher.encrypt(buf); sum ^= buf; } - if(ad_remainder) { + if(ad_remainder > 0) { offset ^= L.star(); buf = offset; xor_buf(buf.data(), &ad[BS * ad_blocks], ad_remainder); @@ -191,6 +193,8 @@ zeroise(m_checksum); m_last_nonce.clear(); m_stretch.clear(); + zeroise(m_nonce_buf); + zeroise(m_offset); } bool OCB_Mode::valid_nonce_length(size_t length) const { @@ -246,7 +250,7 @@ const uint8_t BOTTOM_MASK = static_cast((static_cast(1) << MASKLEN) - 1); m_nonce_buf.resize(BS); - clear_mem(&m_nonce_buf[0], m_nonce_buf.size()); + clear_mem(m_nonce_buf.data(), m_nonce_buf.size()); copy_mem(&m_nonce_buf[BS - nonce_len], nonce, nonce_len); m_nonce_buf[0] = static_cast(((tag_size() * 8) % (BS * 8)) << (BS <= 16 ? 1 : 0)); @@ -337,7 +341,7 @@ const size_t BS = block_size(); - while(blocks) { + while(blocks > 0) { const size_t proc_blocks = std::min(blocks, par_blocks()); const size_t proc_bytes = proc_blocks * BS; @@ -345,7 +349,9 @@ xor_buf(m_checksum.data(), buffer, proc_bytes); - m_cipher->encrypt_n_xex(buffer, offsets, proc_blocks); + xor_buf(buffer, offsets, proc_bytes); + m_cipher->encrypt_n(buffer, buffer, proc_blocks); + xor_buf(buffer, offsets, proc_bytes); buffer += proc_bytes; blocks -= proc_blocks; @@ -371,14 +377,14 @@ secure_vector mac(BS); - if(sz) { + if(sz > 0) { const size_t final_full_blocks = sz / BS; const size_t remainder_bytes = sz - (final_full_blocks * BS); encrypt(buf, final_full_blocks); mac = m_L->offset(); - if(remainder_bytes) { + if(remainder_bytes > 0) { BOTAN_ASSERT(remainder_bytes < BS, "Only a partial block left"); uint8_t* remainder = &buf[sz - remainder_bytes]; @@ -419,13 +425,15 @@ const size_t BS = block_size(); - while(blocks) { + while(blocks > 0) { const size_t proc_blocks = std::min(blocks, par_blocks()); const size_t proc_bytes = proc_blocks * BS; const uint8_t* offsets = m_L->compute_offsets(m_block_index, proc_blocks); - m_cipher->decrypt_n_xex(buffer, offsets, proc_blocks); + xor_buf(buffer, offsets, proc_bytes); + m_cipher->decrypt_n(buffer, buffer, proc_blocks); + xor_buf(buffer, offsets, proc_bytes); xor_buf(m_checksum.data(), buffer, proc_bytes); @@ -457,14 +465,14 @@ secure_vector mac(BS); - if(remaining) { + if(remaining > 0) { const size_t final_full_blocks = remaining / BS; const size_t final_bytes = remaining - (final_full_blocks * BS); decrypt(buf, final_full_blocks); mac ^= m_L->offset(); - if(final_bytes) { + if(final_bytes > 0) { BOTAN_ASSERT(final_bytes < BS, "Only a partial block left"); uint8_t* remainder = &buf[remaining - final_bytes]; @@ -500,6 +508,7 @@ const uint8_t* included_tag = &buf[remaining]; if(!CT::is_equal(mac.data(), included_tag, tag_size()).as_bool()) { + clear_mem(std::span{buffer}.subspan(offset, remaining)); throw Invalid_Authentication_Tag("OCB tag check failed"); } diff -Nru botan3-3.7.1+dfsg/src/lib/modes/aead/ocb/ocb.h botan3-3.12.0+dfsg/src/lib/modes/aead/ocb/ocb.h --- botan3-3.7.1+dfsg/src/lib/modes/aead/ocb/ocb.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/aead/ocb/ocb.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,8 @@ #define BOTAN_AEAD_OCB_H_ #include + +#include #include namespace Botan { @@ -28,29 +30,29 @@ * block ciphers with larger block sizes. * @see https://mailarchive.ietf.org/arch/msg/cfrg/qLTveWOdTJcLn4HP3ev-vrj05Vg/ */ -class BOTAN_TEST_API OCB_Mode : public AEAD_Mode { +class BOTAN_TEST_API OCB_Mode : public AEAD_Mode /* NOLINT(*-special-member-functions) */ { public: - void set_associated_data_n(size_t idx, std::span ad) override final; + void set_associated_data_n(size_t idx, std::span ad) final; - std::string name() const override final; + std::string name() const final; - size_t update_granularity() const override final; + size_t update_granularity() const final; - size_t ideal_granularity() const override final; + size_t ideal_granularity() const final; - Key_Length_Specification key_spec() const override final; + Key_Length_Specification key_spec() const final; - bool valid_nonce_length(size_t) const override final; + bool valid_nonce_length(size_t length) const final; - size_t tag_size() const override final { return m_tag_size; } + size_t tag_size() const final { return m_tag_size; } - void clear() override final; + void clear() final; - void reset() override final; + void reset() final; - bool has_keying_material() const override final; + bool has_keying_material() const final; - ~OCB_Mode(); + ~OCB_Mode() override; protected: /** @@ -66,18 +68,18 @@ size_t par_bytes() const { return m_checksum.size(); } // fixme make these private - std::unique_ptr m_cipher; - std::unique_ptr m_L; + std::unique_ptr m_cipher; // NOLINT(*non-private-member-variables*) + std::unique_ptr m_L; // NOLINT(*non-private-member-variables*) - size_t m_block_index = 0; + uint64_t m_block_index = 0; // NOLINT(*non-private-member-variables*) - secure_vector m_checksum; - secure_vector m_ad_hash; + secure_vector m_checksum; // NOLINT(*non-private-member-variables*) + secure_vector m_ad_hash; // NOLINT(*non-private-member-variables*) private: - void start_msg(const uint8_t nonce[], size_t nonce_len) override final; + void start_msg(const uint8_t nonce[], size_t nonce_len) final; - void key_schedule(std::span key) override final; + void key_schedule(std::span key) final; const secure_vector& update_nonce(const uint8_t nonce[], size_t nonce_len); @@ -96,7 +98,7 @@ * @param cipher the block cipher to use * @param tag_size is how big the auth tag will be */ - OCB_Encryption(std::unique_ptr cipher, size_t tag_size = 16) : + explicit OCB_Encryption(std::unique_ptr cipher, size_t tag_size = 16) : OCB_Mode(std::move(cipher), tag_size) {} size_t output_length(size_t input_length) const override { return input_length + tag_size(); } @@ -115,7 +117,7 @@ * @param cipher the block cipher to use * @param tag_size is how big the auth tag will be */ - OCB_Decryption(std::unique_ptr cipher, size_t tag_size = 16) : + explicit OCB_Decryption(std::unique_ptr cipher, size_t tag_size = 16) : OCB_Mode(std::move(cipher), tag_size) {} size_t output_length(size_t input_length) const override { diff -Nru botan3-3.7.1+dfsg/src/lib/modes/aead/siv/siv.cpp botan3-3.12.0+dfsg/src/lib/modes/aead/siv/siv.cpp --- botan3-3.7.1+dfsg/src/lib/modes/aead/siv/siv.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/aead/siv/siv.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include #include @@ -45,7 +47,7 @@ return m_name; } -bool SIV_Mode::valid_nonce_length(size_t /*nonce_len*/) const { +bool SIV_Mode::valid_nonce_length(size_t /*length*/) const { return true; } @@ -99,7 +101,7 @@ throw Invalid_IV_Length(name(), nonce_len); } - if(nonce_len) { + if(nonce_len > 0) { m_nonce = m_mac->process(nonce, nonce_len); } else { m_nonce.clear(); @@ -119,9 +121,9 @@ secure_vector V = m_mac->process(zeros.data(), zeros.size()); - for(size_t i = 0; i != m_ad_macs.size(); ++i) { + for(const auto& ad_mac : m_ad_macs) { poly_double_n(V.data(), V.size()); - V ^= m_ad_macs[i]; + V ^= ad_mac; } if(!m_nonce.empty()) { @@ -188,7 +190,8 @@ const secure_vector T = S2V(buffer.data() + offset, buffer.size() - offset - V.size()); - if(!CT::is_equal(T.data(), V.data(), T.size()).as_bool()) { + if(!CT::is_equal(T, V).as_bool()) { + clear_mem(std::span{buffer}.subspan(offset, buffer.size() - offset - V.size())); throw Invalid_Authentication_Tag("SIV tag check failed"); } diff -Nru botan3-3.7.1+dfsg/src/lib/modes/aead/siv/siv.h botan3-3.12.0+dfsg/src/lib/modes/aead/siv/siv.h --- botan3-3.7.1+dfsg/src/lib/modes/aead/siv/siv.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/aead/siv/siv.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,8 @@ #define BOTAN_AEAD_SIV_H_ #include + +#include #include #include @@ -20,38 +22,38 @@ /** * Base class for SIV encryption and decryption (@see RFC 5297) */ -class BOTAN_TEST_API SIV_Mode : public AEAD_Mode { +class BOTAN_TEST_API SIV_Mode : public AEAD_Mode /* NOLINT(*-special-member-functions) */ { public: /** * Sets the nth element of the vector of associated data * @param n index into the AD vector * @param ad associated data */ - void set_associated_data_n(size_t n, std::span ad) override final; + void set_associated_data_n(size_t n, std::span ad) final; - size_t maximum_associated_data_inputs() const override final; + size_t maximum_associated_data_inputs() const final; - std::string name() const override final; + std::string name() const final; - size_t update_granularity() const override final; + size_t update_granularity() const final; - size_t ideal_granularity() const override final; + size_t ideal_granularity() const final; - Key_Length_Specification key_spec() const override final; + Key_Length_Specification key_spec() const final; - bool valid_nonce_length(size_t) const override final; + bool valid_nonce_length(size_t length) const final; - bool requires_entire_message() const override final; + bool requires_entire_message() const final; - void clear() override final; + void clear() final; - void reset() override final; + void reset() final; - size_t tag_size() const override final { return 16; } + size_t tag_size() const final { return 16; } - bool has_keying_material() const override final; + bool has_keying_material() const final; - ~SIV_Mode(); + ~SIV_Mode() override; protected: explicit SIV_Mode(std::unique_ptr cipher); @@ -67,10 +69,10 @@ secure_vector S2V(const uint8_t text[], size_t text_len); private: - void start_msg(const uint8_t nonce[], size_t nonce_len) override final; - size_t process_msg(uint8_t buf[], size_t size) override final; + void start_msg(const uint8_t nonce[], size_t nonce_len) final; + size_t process_msg(uint8_t buf[], size_t size) final; - void key_schedule(std::span key) override final; + void key_schedule(std::span key) final; const std::string m_name; const size_t m_bs; diff -Nru botan3-3.7.1+dfsg/src/lib/modes/cbc/cbc.cpp botan3-3.12.0+dfsg/src/lib/modes/cbc/cbc.cpp --- botan3-3.7.1+dfsg/src/lib/modes/cbc/cbc.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/cbc/cbc.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,9 +9,10 @@ #include +#include +#include #include #include -#include namespace Botan { @@ -78,7 +79,7 @@ * as the new IV, as unfortunately some protocols require this. If * this is the first message then we use an IV of all zeros. */ - if(nonce_len) { + if(nonce_len > 0) { m_state.assign(nonce, nonce + nonce_len); } else if(m_state.empty()) { m_state.resize(m_cipher->block_size()); @@ -91,11 +92,7 @@ } size_t CBC_Encryption::output_length(size_t input_length) const { - if(input_length == 0) { - return block_size(); - } else { - return round_up(input_length, block_size()); - } + return padding().output_length(input_length, block_size()); } size_t CBC_Encryption::process_msg(uint8_t buf[], size_t sz) { @@ -126,9 +123,10 @@ const size_t BS = block_size(); + const size_t output_bytes = offset + padding().output_length(buffer.size() - offset, BS); const size_t bytes_in_final_block = (buffer.size() - offset) % BS; - - padding().add_padding(buffer, bytes_in_final_block, BS); + buffer.resize(output_bytes); + padding().add_padding(std::span(buffer).subspan(offset), bytes_in_final_block, BS); BOTAN_ASSERT_EQUAL(buffer.size() % BS, offset % BS, "Padded to block boundary"); @@ -205,7 +203,7 @@ BOTAN_ARG_CHECK(sz % BS == 0, "Input is not full blocks"); size_t blocks = sz / BS; - while(blocks) { + while(blocks > 0) { const size_t to_proc = std::min(BS * blocks, m_tempbuf.size()); cipher().decrypt_n(buf, m_tempbuf.data(), to_proc / BS); @@ -230,13 +228,13 @@ const size_t BS = block_size(); - if(sz == 0 || sz % BS) { + if(sz == 0 || sz % BS != 0) { throw Decoding_Error(name() + ": Ciphertext not a multiple of block size"); } update(buffer, offset); - const size_t pad_bytes = BS - padding().unpad(&buffer[buffer.size() - BS], BS); + const size_t pad_bytes = BS - padding().unpad(std::span{buffer}.last(BS)); buffer.resize(buffer.size() - pad_bytes); // remove padding if(pad_bytes == 0 && padding().name() != "NoPadding") { throw Decoding_Error("Invalid CBC padding"); diff -Nru botan3-3.7.1+dfsg/src/lib/modes/cbc/cbc.h botan3-3.12.0+dfsg/src/lib/modes/cbc/cbc.h --- botan3-3.7.1+dfsg/src/lib/modes/cbc/cbc.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/cbc/cbc.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #ifndef BOTAN_MODE_CBC_H_ #define BOTAN_MODE_CBC_H_ +#include #include #include #include diff -Nru botan3-3.7.1+dfsg/src/lib/modes/cfb/cfb.cpp botan3-3.12.0+dfsg/src/lib/modes/cfb/cfb.cpp --- botan3-3.7.1+dfsg/src/lib/modes/cfb/cfb.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/cfb/cfb.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,8 @@ #include +#include +#include #include namespace Botan { @@ -15,8 +17,8 @@ CFB_Mode::CFB_Mode(std::unique_ptr cipher, size_t feedback_bits) : m_cipher(std::move(cipher)), m_block_size(m_cipher->block_size()), - m_feedback_bytes(feedback_bits ? feedback_bits / 8 : m_block_size) { - if(feedback_bits % 8 || feedback() > m_block_size) { + m_feedback_bytes(feedback_bits != 0 ? feedback_bits / 8 : m_block_size) { + if(feedback_bits % 8 != 0 || feedback() > m_block_size) { throw Invalid_Argument(fmt("{} does not support feedback bits of {}", name(), feedback_bits)); } } @@ -30,6 +32,7 @@ void CFB_Mode::reset() { m_state.clear(); zeroise(m_keystream); + m_keystream_pos = 0; } std::string CFB_Mode::name() const { @@ -158,7 +161,7 @@ inline void xor_copy(uint8_t buf[], uint8_t key_buf[], size_t len) { for(size_t i = 0; i != len; ++i) { - uint8_t k = key_buf[i]; + const uint8_t k = key_buf[i]; key_buf[i] = buf[i]; buf[i] ^= k; } diff -Nru botan3-3.7.1+dfsg/src/lib/modes/cfb/cfb.h botan3-3.12.0+dfsg/src/lib/modes/cfb/cfb.h --- botan3-3.7.1+dfsg/src/lib/modes/cfb/cfb.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/cfb/cfb.h 2026-05-07 01:38:28.000000000 +0000 @@ -52,9 +52,9 @@ size_t block_size() const { return m_block_size; } - secure_vector m_state; - secure_vector m_keystream; - size_t m_keystream_pos = 0; + secure_vector m_state; // NOLINT(*non-private-member-variable*) + secure_vector m_keystream; // NOLINT(*non-private-member-variable*) + size_t m_keystream_pos = 0; // NOLINT(*non-private-member-variable*) private: void start_msg(const uint8_t nonce[], size_t nonce_len) override; diff -Nru botan3-3.7.1+dfsg/src/lib/modes/cipher_mode.cpp botan3-3.12.0+dfsg/src/lib/modes/cipher_mode.cpp --- botan3-3.7.1+dfsg/src/lib/modes/cipher_mode.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/cipher_mode.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,10 +7,13 @@ #include +#include #include #include #include +#include #include +#include #if defined(BOTAN_HAS_BLOCK_CIPHER) #include @@ -79,7 +82,7 @@ if(algo.find('/') != std::string::npos) { const std::vector algo_parts = split_on(algo, '/'); - std::string_view cipher_name = algo_parts[0]; + const std::string_view cipher_name = algo_parts[0]; const std::vector mode_info = parse_algorithm_name(algo_parts[1]); if(mode_info.empty()) { @@ -102,7 +105,7 @@ #if defined(BOTAN_HAS_BLOCK_CIPHER) - SCAN_Name spec(algo); + const SCAN_Name spec(algo); if(spec.arg_count() == 0) { return std::unique_ptr(); diff -Nru botan3-3.7.1+dfsg/src/lib/modes/cipher_mode.h botan3-3.12.0+dfsg/src/lib/modes/cipher_mode.h --- botan3-3.7.1+dfsg/src/lib/modes/cipher_mode.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/cipher_mode.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,9 +9,9 @@ #define BOTAN_CIPHER_MODE_H_ #include -#include #include #include +#include #include #include #include @@ -22,9 +22,9 @@ /** * The two possible directions a Cipher_Mode can operate in */ -enum class Cipher_Dir : int { - Encryption, - Decryption, +enum class Cipher_Dir : uint8_t { + Encryption = 0, + Decryption = 1, ENCRYPTION BOTAN_DEPRECATED("Use Cipher_Dir::Encryption") = Encryption, DECRYPTION BOTAN_DEPRECATED("Use Cipher_Dir::Decryption") = Decryption, @@ -146,7 +146,6 @@ */ template void update(T& buffer, size_t offset = 0) { - BOTAN_ASSERT(buffer.size() >= offset, "Offset ok"); const size_t written = process(std::span(buffer).subspan(offset)); buffer.resize(offset + written); } @@ -233,7 +232,7 @@ virtual bool requires_entire_message() const { return false; } /** - * @return required minimium size to finalize() - may be any + * @return required minimum size to finalize() - may be any * length larger than this. */ virtual size_t minimum_final_size() const = 0; diff -Nru botan3-3.7.1+dfsg/src/lib/modes/mode_pad/mode_pad.cpp botan3-3.12.0+dfsg/src/lib/modes/mode_pad/mode_pad.cpp --- botan3-3.7.1+dfsg/src/lib/modes/mode_pad/mode_pad.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/mode_pad/mode_pad.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -2,13 +2,13 @@ * CBC Padding Methods * (C) 1999-2007,2013,2018,2020 Jack Lloyd * (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity +* (C) 2025 René Meusel, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ #include -#include #include namespace Botan { @@ -40,10 +40,29 @@ return nullptr; } +void BlockCipherModePaddingMethod::add_padding(std::span buffer, size_t last_byte_pos, size_t BS) const { + BOTAN_ASSERT_NOMSG(valid_blocksize(BS)); + BOTAN_ASSERT_NOMSG(last_byte_pos < BS); + BOTAN_ASSERT_NOMSG(buffer.size() % BS == 0); + BOTAN_ASSERT_NOMSG(buffer.size() >= BS); + + auto poison = CT::scoped_poison(last_byte_pos, buffer); + apply_padding(buffer.last(BS), last_byte_pos); +} + +size_t BlockCipherModePaddingMethod::unpad(std::span last_block) const { + if(!valid_blocksize(last_block.size())) { + return last_block.size(); + } + + auto poison = CT::scoped_poison(last_block); + return CT::driveby_unpoison(remove_padding(last_block)); +} + /* * Pad with PKCS #7 Method */ -void PKCS7_Padding::add_padding(secure_vector& buffer, size_t last_byte_pos, size_t BS) const { +void PKCS7_Padding::apply_padding(std::span last_block, size_t padding_start_pos) const { /* Padding format is 01 @@ -51,52 +70,31 @@ 030303 ... */ - BOTAN_DEBUG_ASSERT(last_byte_pos < BS); - - const uint8_t padding_len = static_cast(BS - last_byte_pos); - - buffer.resize(buffer.size() + padding_len); - - CT::poison(&last_byte_pos, 1); - CT::poison(buffer.data(), buffer.size()); - - BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); - BOTAN_DEBUG_ASSERT(buffer.size() >= BS); - - const size_t start_of_last_block = buffer.size() - BS; - const size_t end_of_last_block = buffer.size(); - const size_t start_of_padding = buffer.size() - padding_len; - - for(size_t i = start_of_last_block; i != end_of_last_block; ++i) { - auto needs_padding = CT::Mask(CT::Mask::is_gte(i, start_of_padding)); - buffer[i] = needs_padding.select(padding_len, buffer[i]); + const uint8_t BS = static_cast(last_block.size()); + const uint8_t start_pos = static_cast(padding_start_pos); + const uint8_t padding_len = BS - start_pos; + for(uint8_t i = 0; i < BS; ++i) { + auto needs_padding = CT::Mask::is_gte(i, start_pos); + last_block[i] = needs_padding.select(padding_len, last_block[i]); } - - CT::unpoison(buffer.data(), buffer.size()); - CT::unpoison(last_byte_pos); } /* * Unpad with PKCS #7 Method */ -size_t PKCS7_Padding::unpad(const uint8_t input[], size_t input_length) const { - if(!valid_blocksize(input_length)) { - return input_length; - } - - CT::poison(input, input_length); - - const uint8_t last_byte = input[input_length - 1]; +size_t PKCS7_Padding::remove_padding(std::span input) const { + const size_t BS = input.size(); + const uint8_t last_byte = input.back(); /* The input should == the block size so if the last byte exceeds that then the padding is certainly invalid */ - auto bad_input = CT::Mask::is_gt(last_byte, input_length); + auto bad_input = CT::Mask::is_gt(last_byte, BS); - const size_t pad_pos = input_length - last_byte; + const size_t pad_pos = BS - last_byte; - for(size_t i = 0; i != input_length - 1; ++i) { + for(size_t i = 0; i != BS - 1; ++i) { // Does this byte equal the expected pad byte? const auto pad_eq = CT::Mask::is_equal(input[i], last_byte); @@ -105,15 +103,13 @@ bad_input |= in_range & (~pad_eq); } - CT::unpoison(input, input_length); - - return bad_input.select_and_unpoison(input_length, pad_pos); + return bad_input.select(BS, pad_pos); } /* * Pad with ANSI X9.23 Method */ -void ANSI_X923_Padding::add_padding(secure_vector& buffer, size_t last_byte_pos, size_t BS) const { +void ANSI_X923_Padding::apply_padding(std::span last_block, size_t padding_start_pos) const { /* Padding format is 01 @@ -121,64 +117,42 @@ 000003 ... */ - BOTAN_DEBUG_ASSERT(last_byte_pos < BS); - - const uint8_t padding_len = static_cast(BS - last_byte_pos); - - buffer.resize(buffer.size() + padding_len); - - CT::poison(&last_byte_pos, 1); - CT::poison(buffer.data(), buffer.size()); - - BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); - BOTAN_DEBUG_ASSERT(buffer.size() >= BS); - - const size_t start_of_last_block = buffer.size() - BS; - const size_t end_of_zero_padding = buffer.size() - 1; - const size_t start_of_padding = buffer.size() - padding_len; - - for(size_t i = start_of_last_block; i != end_of_zero_padding; ++i) { - auto needs_padding = CT::Mask(CT::Mask::is_gte(i, start_of_padding)); - buffer[i] = needs_padding.select(0, buffer[i]); + const uint8_t BS = static_cast(last_block.size()); + const uint8_t start_pos = static_cast(padding_start_pos); + const uint8_t padding_len = BS - start_pos; + for(uint8_t i = 0; i != BS - 1; ++i) { + auto needs_padding = CT::Mask::is_gte(i, start_pos); + last_block[i] = needs_padding.select(0, last_block[i]); } - buffer[buffer.size() - 1] = padding_len; - CT::unpoison(buffer.data(), buffer.size()); - CT::unpoison(last_byte_pos); + last_block.back() = padding_len; } /* * Unpad with ANSI X9.23 Method */ -size_t ANSI_X923_Padding::unpad(const uint8_t input[], size_t input_length) const { - if(!valid_blocksize(input_length)) { - return input_length; - } +size_t ANSI_X923_Padding::remove_padding(std::span input) const { + const size_t BS = input.size(); + const size_t last_byte = input.back(); - CT::poison(input, input_length); + auto bad_input = CT::Mask::is_gt(last_byte, BS); - const size_t last_byte = input[input_length - 1]; + const size_t pad_pos = BS - last_byte; - auto bad_input = CT::Mask::is_gt(last_byte, input_length); - - const size_t pad_pos = input_length - last_byte; - - for(size_t i = 0; i != input_length - 1; ++i) { + for(size_t i = 0; i != BS - 1; ++i) { // Ignore values that are not part of the padding const auto in_range = CT::Mask::is_gte(i, pad_pos); const auto pad_is_nonzero = CT::Mask::expand(input[i]); bad_input |= pad_is_nonzero & in_range; } - CT::unpoison(input, input_length); - - return bad_input.select_and_unpoison(input_length, pad_pos); + return bad_input.select(BS, pad_pos); } /* * Pad with One and Zeros Method */ -void OneAndZeros_Padding::add_padding(secure_vector& buffer, size_t last_byte_pos, size_t BS) const { +void OneAndZeros_Padding::apply_padding(std::span last_block, size_t padding_start_pos) const { /* Padding format is 80 @@ -186,69 +160,40 @@ 800000 ... */ - - BOTAN_DEBUG_ASSERT(last_byte_pos < BS); - - const uint8_t padding_len = static_cast(BS - last_byte_pos); - - buffer.resize(buffer.size() + padding_len); - - CT::poison(&last_byte_pos, 1); - CT::poison(buffer.data(), buffer.size()); - - BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); - BOTAN_DEBUG_ASSERT(buffer.size() >= BS); - - const size_t start_of_last_block = buffer.size() - BS; - const size_t end_of_last_block = buffer.size(); - const size_t start_of_padding = buffer.size() - padding_len; - - for(size_t i = start_of_last_block; i != end_of_last_block; ++i) { - auto needs_80 = CT::Mask(CT::Mask::is_equal(i, start_of_padding)); - auto needs_00 = CT::Mask(CT::Mask::is_gt(i, start_of_padding)); - buffer[i] = needs_00.select(0x00, needs_80.select(0x80, buffer[i])); + for(size_t i = 0; i != last_block.size(); ++i) { + auto needs_80 = CT::Mask(CT::Mask::is_equal(i, padding_start_pos)); + auto needs_00 = CT::Mask(CT::Mask::is_gt(i, padding_start_pos)); + last_block[i] = needs_00.select(0x00, needs_80.select(0x80, last_block[i])); } - - CT::unpoison(buffer.data(), buffer.size()); - CT::unpoison(last_byte_pos); } /* * Unpad with One and Zeros Method */ -size_t OneAndZeros_Padding::unpad(const uint8_t input[], size_t input_length) const { - if(!valid_blocksize(input_length)) { - return input_length; - } - - CT::poison(input, input_length); - +size_t OneAndZeros_Padding::remove_padding(std::span input) const { + const size_t BS = input.size(); auto bad_input = CT::Mask::cleared(); auto seen_0x80 = CT::Mask::cleared(); - size_t pad_pos = input_length - 1; - size_t i = input_length; + size_t pad_pos = BS - 1; - while(i) { + for(size_t i = BS; i != 0; --i) { const auto is_0x80 = CT::Mask::is_equal(input[i - 1], 0x80); const auto is_zero = CT::Mask::is_zero(input[i - 1]); seen_0x80 |= is_0x80; pad_pos -= seen_0x80.if_not_set_return(1); bad_input |= ~seen_0x80 & ~is_zero; - i--; } bad_input |= ~seen_0x80; - CT::unpoison(input, input_length); - - return CT::Mask::expand(bad_input).select_and_unpoison(input_length, pad_pos); + return CT::Mask::expand(bad_input).select(BS, pad_pos); } /* * Pad with ESP Padding Method */ -void ESP_Padding::add_padding(secure_vector& buffer, size_t last_byte_pos, size_t BS) const { +void ESP_Padding::apply_padding(std::span last_block, size_t padding_start_pos) const { /* Padding format is 01 @@ -256,61 +201,35 @@ 010203 ... */ - BOTAN_DEBUG_ASSERT(last_byte_pos < BS); - - const uint8_t padding_len = static_cast(BS - last_byte_pos); - - buffer.resize(buffer.size() + padding_len); - - CT::poison(&last_byte_pos, 1); - CT::poison(buffer.data(), buffer.size()); - - BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); - BOTAN_DEBUG_ASSERT(buffer.size() >= BS); - - const size_t start_of_last_block = buffer.size() - BS; - const size_t end_of_last_block = buffer.size(); - const size_t start_of_padding = buffer.size() - padding_len; + const uint8_t BS = static_cast(last_block.size()); + const uint8_t start_pos = static_cast(padding_start_pos); uint8_t pad_ctr = 0x01; - - for(size_t i = start_of_last_block; i != end_of_last_block; ++i) { - auto needs_padding = CT::Mask(CT::Mask::is_gte(i, start_of_padding)); - buffer[i] = needs_padding.select(pad_ctr, buffer[i]); + for(uint8_t i = 0; i != BS; ++i) { + auto needs_padding = CT::Mask::is_gte(i, start_pos); + last_block[i] = needs_padding.select(pad_ctr, last_block[i]); pad_ctr = needs_padding.select(pad_ctr + 1, pad_ctr); } - - CT::unpoison(buffer.data(), buffer.size()); - CT::unpoison(last_byte_pos); } /* * Unpad with ESP Padding Method */ -size_t ESP_Padding::unpad(const uint8_t input[], size_t input_length) const { - if(!valid_blocksize(input_length)) { - return input_length; - } - - CT::poison(input, input_length); - - const uint8_t input_length_8 = static_cast(input_length); - const uint8_t last_byte = input[input_length - 1]; +size_t ESP_Padding::remove_padding(std::span input) const { + const size_t BS = input.size(); + const uint8_t last_byte = input.back(); - auto bad_input = CT::Mask::is_zero(last_byte) | CT::Mask::is_gt(last_byte, input_length_8); + auto bad_input = CT::Mask::is_zero(last_byte) | CT::Mask::is_gt(last_byte, BS); - const uint8_t pad_pos = input_length_8 - last_byte; - size_t i = input_length_8 - 1; - while(i) { + const size_t pad_pos = BS - last_byte; + for(size_t i = BS - 1; i != 0; --i) { const auto in_range = CT::Mask::is_gt(i, pad_pos); - const auto incrementing = CT::Mask::is_equal(input[i - 1], input[i] - 1); + const auto incrementing = CT::Mask::is_equal(input[i - 1], input[i] - 1); - bad_input |= CT::Mask(in_range) & ~incrementing; - --i; + bad_input |= CT::Mask(in_range) & ~incrementing; } - CT::unpoison(input, input_length); - return bad_input.select_and_unpoison(input_length_8, pad_pos); + return bad_input.select(BS, pad_pos); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/modes/mode_pad/mode_pad.h botan3-3.12.0+dfsg/src/lib/modes/mode_pad/mode_pad.h --- botan3-3.7.1+dfsg/src/lib/modes/mode_pad/mode_pad.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/mode_pad/mode_pad.h 2026-05-07 01:38:28.000000000 +0000 @@ -2,6 +2,7 @@ * CBC Padding Methods * (C) 1999-2008,2013 Jack Lloyd * (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity +* (C) 2025 René Meusel, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -9,7 +10,10 @@ #ifndef BOTAN_MODE_PADDING_H_ #define BOTAN_MODE_PADDING_H_ +#include #include +#include +#include #include namespace Botan { @@ -23,7 +27,7 @@ * a padding mode for CBC, which happens to consume the last * two block (and requires use of the block cipher). */ -class BOTAN_TEST_API BlockCipherModePaddingMethod { +class BOTAN_TEST_API BlockCipherModePaddingMethod /* NOLINT(*-special-member-functions) */ { public: /** * Get a block cipher padding mode by name (eg "NoPadding" or "PKCS7") @@ -33,19 +37,20 @@ /** * Add padding bytes to buffer. - * @param buffer data to pad + * @param buffer data to pad, span must be large enough to hold the padding + * behind the final (partial) block * @param final_block_bytes size of the final block in bytes * @param block_size size of each block in bytes */ - virtual void add_padding(secure_vector& buffer, size_t final_block_bytes, size_t block_size) const = 0; + virtual void add_padding(std::span buffer, size_t final_block_bytes, size_t block_size) const; /** * Remove padding bytes from block - * @param block the last block - * @param len the size of the block in bytes - * @return number of data bytes, or if the padding is invalid returns len + * @param last_block the last block containing the padding + * @return number of data bytes, or if the padding is invalid returns the + * byte length of @p last_block (i.e. the block size) */ - virtual size_t unpad(const uint8_t block[], size_t len) const = 0; + size_t unpad(std::span last_block) const; /** * @param block_size of the cipher @@ -54,6 +59,15 @@ virtual bool valid_blocksize(size_t block_size) const = 0; /** + * @param input_length number of bytes to be padded + * @param block_size size of each block in bytes + * @return the total number of output bytes (including the padding) + */ + virtual size_t output_length(size_t input_length, size_t block_size) const { + return ((input_length + block_size) / block_size) * block_size; + } + + /** * @return name of the mode */ virtual std::string name() const = 0; @@ -62,6 +76,28 @@ * virtual destructor */ virtual ~BlockCipherModePaddingMethod() = default; + + protected: + /** + * Applies the concrete padding to the @p last_block assuming the padding + * bytes should start at @p padding_start_pos within the last block. + * + * Concrete implementations of this function must ensure not to leak + * @p padding_start_pos via side channels. Both the bytes of @p last_block + * and @p padding_start_pos are passed in with CT::poison applied. + */ + virtual void apply_padding(std::span last_block, size_t padding_start_pos) const = 0; + + /** + * Removes the padding from @p last_block and returns the number of data + * bytes. If the padding is invalid, this returns the byte length of + * @p last_block. + * + * Concrete implementations of this function must ensure not to leak + * the size or validity of the padding via side channels. The bytes of + * @p last_block are passed in with CT::poison applied to them. + */ + virtual size_t remove_padding(std::span last_block) const = 0; }; /** @@ -69,9 +105,9 @@ */ class BOTAN_FUZZER_API PKCS7_Padding final : public BlockCipherModePaddingMethod { public: - void add_padding(secure_vector& buffer, size_t final_block_bytes, size_t block_size) const override; + void apply_padding(std::span last_block, size_t final_block_bytes) const override; - size_t unpad(const uint8_t[], size_t) const override; + size_t remove_padding(std::span last_block) const override; bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); } @@ -83,9 +119,9 @@ */ class BOTAN_FUZZER_API ANSI_X923_Padding final : public BlockCipherModePaddingMethod { public: - void add_padding(secure_vector& buffer, size_t final_block_bytes, size_t block_size) const override; + void apply_padding(std::span last_block, size_t final_block_bytes) const override; - size_t unpad(const uint8_t[], size_t) const override; + size_t remove_padding(std::span last_block) const override; bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); } @@ -97,9 +133,9 @@ */ class BOTAN_FUZZER_API OneAndZeros_Padding final : public BlockCipherModePaddingMethod { public: - void add_padding(secure_vector& buffer, size_t final_block_bytes, size_t block_size) const override; + void apply_padding(std::span last_block, size_t final_block_bytes) const override; - size_t unpad(const uint8_t[], size_t) const override; + size_t remove_padding(std::span last_block) const override; bool valid_blocksize(size_t bs) const override { return (bs > 2); } @@ -111,9 +147,9 @@ */ class BOTAN_FUZZER_API ESP_Padding final : public BlockCipherModePaddingMethod { public: - void add_padding(secure_vector& buffer, size_t final_block_bytes, size_t block_size) const override; + void apply_padding(std::span last_block, size_t final_block_bytes) const override; - size_t unpad(const uint8_t[], size_t) const override; + size_t remove_padding(std::span last_block) const override; bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); } @@ -125,14 +161,26 @@ */ class Null_Padding final : public BlockCipherModePaddingMethod { public: - void add_padding(secure_vector&, size_t, size_t) const override { /* no padding */ + void add_padding(std::span /*buffer*/, + size_t /*final_block_bytes*/, + size_t /*block_size*/) const override { + // no padding } - size_t unpad(const uint8_t[], size_t size) const override { return size; } + size_t remove_padding(std::span last_block) const override { return last_block.size(); } + + bool valid_blocksize(size_t /*block_size*/) const override { return true; } - bool valid_blocksize(size_t) const override { return true; } + size_t output_length(size_t input_length, size_t /*block_size*/) const override { return input_length; } std::string name() const override { return "NoPadding"; } + + private: + void apply_padding(std::span /*last_block*/, size_t /*padding_start_pos*/) const override { + // This class overrides add_padding() as a NOOP, so this customization + // point can never be called by anyone. + BOTAN_ASSERT_UNREACHABLE(); + } }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/modes/stream_mode.h botan3-3.12.0+dfsg/src/lib/modes/stream_mode.h --- botan3-3.7.1+dfsg/src/lib/modes/stream_mode.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/stream_mode.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,8 @@ #include +#include + #if defined(BOTAN_HAS_STREAM_CIPHER) #include #endif @@ -21,7 +23,7 @@ class Stream_Cipher_Mode final : public Cipher_Mode { public: /** - * @param cipher underyling stream cipher + * @param cipher underlying stream cipher */ explicit Stream_Cipher_Mode(std::unique_ptr cipher) : m_cipher(std::move(cipher)) {} diff -Nru botan3-3.7.1+dfsg/src/lib/modes/xts/xts.cpp botan3-3.12.0+dfsg/src/lib/modes/xts/xts.cpp --- botan3-3.7.1+dfsg/src/lib/modes/xts/xts.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/xts/xts.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ /* * XTS Mode -* (C) 2009,2013 Jack Lloyd +* (C) 2009,2013,2026 Jack Lloyd * (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) @@ -8,9 +8,15 @@ #include +#include +#include #include #include +#if defined(BOTAN_HAS_MODE_XTS_AVX512_CLMUL) + #include +#endif + namespace Botan { XTS_Mode::XTS_Mode(std::unique_ptr cipher) : @@ -18,7 +24,7 @@ m_cipher_block_size(m_cipher->block_size()), m_cipher_parallelism(m_cipher->parallel_bytes()), m_tweak_blocks(m_cipher_parallelism / m_cipher_block_size) { - if(poly_double_supported_size(m_cipher_block_size) == false) { + if(!poly_double_supported_size(m_cipher_block_size)) { throw Invalid_Argument(fmt("Cannot use {} with XTS", m_cipher->name())); } @@ -88,19 +94,46 @@ copy_mem(m_tweak.data(), nonce, nonce_len); m_tweak_cipher->encrypt(m_tweak.data()); - update_tweak(0); + // Just repeated doubling from first, remaining contents are junk... + xts_compute_tweak_block(m_tweak.data(), m_tweak_cipher->block_size(), tweak_blocks()); } -void XTS_Mode::update_tweak(size_t which) { - const size_t BS = m_tweak_cipher->block_size(); - - if(which > 0) { - poly_double_n_le(m_tweak.data(), &m_tweak[(which - 1) * BS], BS); +//static +void XTS_Mode::update_tweak_block(uint8_t tweak[], size_t BS, size_t blocks_in_tweak) { +#if defined(BOTAN_HAS_MODE_XTS_AVX512_CLMUL) + if(BS == 16 && blocks_in_tweak % 8 == 0 && CPUID::has(CPUID::Feature::AVX512_CLMUL)) { + return update_tweak_block_avx512_clmul(tweak, BS, blocks_in_tweak); } +#endif + + /* + * If we don't have a fast method available, just set the first tweak block to + * the doubling of the last tweak block, and recompute all the rest via + * successive doublings. + */ + poly_double_n_le(tweak, &tweak[(blocks_in_tweak - 1) * BS], BS); + xts_compute_tweak_block(tweak, BS, blocks_in_tweak); +} +void XTS_Mode::update_tweak(size_t consumed) { + const size_t BS = m_tweak_cipher->block_size(); const size_t blocks_in_tweak = tweak_blocks(); - xts_update_tweak_block(m_tweak.data(), BS, blocks_in_tweak); + BOTAN_ASSERT_NOMSG(consumed > 0 && consumed <= blocks_in_tweak); + + if(consumed == blocks_in_tweak) { + // Update all in parallel + update_tweak_block(m_tweak.data(), BS, blocks_in_tweak); + } else { + /* + The last remaining tweaks can just be shifted over + + This could be a lot better though! We can copy all of the remaining tweaks + and just recompute the last few + */ + copy_mem(m_tweak.data(), &m_tweak[(consumed * BS)], BS); + xts_compute_tweak_block(m_tweak.data(), BS, blocks_in_tweak); + } } size_t XTS_Encryption::output_length(size_t input_length) const { @@ -116,12 +149,15 @@ const size_t blocks_in_tweak = tweak_blocks(); - while(blocks) { + while(blocks > 0) { const size_t to_proc = std::min(blocks, blocks_in_tweak); + const size_t proc_bytes = to_proc * BS; - cipher().encrypt_n_xex(buf, tweak(), to_proc); + xor_buf(buf, tweak(), proc_bytes); + cipher().encrypt_n(buf, buf, to_proc); + xor_buf(buf, tweak(), proc_bytes); - buf += to_proc * BS; + buf += proc_bytes; blocks -= to_proc; update_tweak(to_proc); @@ -182,12 +218,15 @@ const size_t blocks_in_tweak = tweak_blocks(); - while(blocks) { + while(blocks > 0) { const size_t to_proc = std::min(blocks, blocks_in_tweak); + const size_t proc_bytes = to_proc * BS; - cipher().decrypt_n_xex(buf, tweak(), to_proc); + xor_buf(buf, tweak(), proc_bytes); + cipher().decrypt_n(buf, buf, to_proc); + xor_buf(buf, tweak(), proc_bytes); - buf += to_proc * BS; + buf += proc_bytes; blocks -= to_proc; update_tweak(to_proc); diff -Nru botan3-3.7.1+dfsg/src/lib/modes/xts/xts.h botan3-3.12.0+dfsg/src/lib/modes/xts/xts.h --- botan3-3.7.1+dfsg/src/lib/modes/xts/xts.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/xts/xts.h 2026-05-07 01:38:28.000000000 +0000 @@ -44,13 +44,13 @@ const uint8_t* tweak() const { return m_tweak.data(); } - bool tweak_set() const { return m_tweak.empty() == false; } + bool tweak_set() const { return !m_tweak.empty(); } size_t tweak_blocks() const { return m_tweak_blocks; } const BlockCipher& cipher() const { return *m_cipher; } - void update_tweak(size_t last_used); + void update_tweak(size_t consumed); size_t cipher_block_size() const { return m_cipher_block_size; } @@ -58,6 +58,20 @@ void start_msg(const uint8_t nonce[], size_t nonce_len) override; void key_schedule(std::span key) override; + /* + * Tweak block update step for XTS + * + * Assumes tweak is BS * n bytes long. + * + * Assumes that each block of tweak is already set to the successive doublings + * of the block prior. + */ + static void update_tweak_block(uint8_t tweak[], size_t BS, size_t blocks_in_tweak); + +#if defined(BOTAN_HAS_MODE_XTS_AVX512_CLMUL) + static void update_tweak_block_avx512_clmul(uint8_t tweak[], size_t BS, size_t blocks_in_tweak); +#endif + std::unique_ptr m_cipher; std::unique_ptr m_tweak_cipher; secure_vector m_tweak; diff -Nru botan3-3.7.1+dfsg/src/lib/modes/xts/xts_avx512_clmul/info.txt botan3-3.12.0+dfsg/src/lib/modes/xts/xts_avx512_clmul/info.txt --- botan3-3.7.1+dfsg/src/lib/modes/xts/xts_avx512_clmul/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/xts/xts_avx512_clmul/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,15 @@ + +MODE_XTS_AVX512_CLMUL -> 20260119 + + + +name -> "XTS tweak computation using AVX-512/clmul" + + + +avx512_clmul + + + +cpuid + diff -Nru botan3-3.7.1+dfsg/src/lib/modes/xts/xts_avx512_clmul/xts_avx512_clmul.cpp botan3-3.12.0+dfsg/src/lib/modes/xts/xts_avx512_clmul/xts_avx512_clmul.cpp --- botan3-3.7.1+dfsg/src/lib/modes/xts/xts_avx512_clmul/xts_avx512_clmul.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/modes/xts/xts_avx512_clmul/xts_avx512_clmul.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,65 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include + +namespace Botan { + +void BOTAN_FN_ISA_AVX512_CLMUL XTS_Mode::update_tweak_block_avx512_clmul(uint8_t tweak[], size_t BS, size_t N) { + BOTAN_ASSERT_NOMSG(N > 0); + + if(BS == 16 && N % 8 == 0) { + constexpr uint64_t P128 = 0x87; + const __m512i poly = _mm512_set_epi64(0, P128, 0, P128, 0, P128, 0, P128); + + /* + * We need to perform N doublings on each block. + * + * We can compute the carryless multiplication with any size. Here, curiously, the + * constraint is that AVX2/AVX512 don't include an equivalent of psrldq (aka + * _mm_srli_si128), which allows shifting 128-bit lanes by any number of bits. + * Instead only byte-wide lane shifts are available, so we can only raise to powers + * where N is a multiple of 8. + */ + const size_t N_32 = N / 32; + const size_t N_8 = (N - N_32 * 32) / 8; + + // Since we must anyway require N % 8 == 0, unrolling once is free and allows better ILP + for(size_t i = 0; i != N; i += 8) { + __m512i W0 = _mm512_loadu_si512(&tweak[i * BS]); + __m512i W1 = _mm512_loadu_si512(&tweak[(i + 4) * BS]); + + for(size_t r = 0; r != N_32; ++r) { + // (W << 32) ^ compute_carry(W >> 96) + const auto C0 = _mm512_clmulepi64_epi128(_mm512_bsrli_epi128(W0, 12), poly, 0); + const auto C1 = _mm512_clmulepi64_epi128(_mm512_bsrli_epi128(W1, 12), poly, 0); + W0 = _mm512_xor_si512(_mm512_bslli_epi128(W0, 4), C0); + W1 = _mm512_xor_si512(_mm512_bslli_epi128(W1, 4), C1); + } + + for(size_t r = 0; r != N_8; ++r) { + // (W << 8) ^ compute_carry(W >> 120) + const auto C0 = _mm512_clmulepi64_epi128(_mm512_bsrli_epi128(W0, 15), poly, 0); + const auto C1 = _mm512_clmulepi64_epi128(_mm512_bsrli_epi128(W1, 15), poly, 0); + W0 = _mm512_xor_si512(_mm512_bslli_epi128(W0, 1), C0); + W1 = _mm512_xor_si512(_mm512_bslli_epi128(W1, 1), C1); + } + + _mm512_storeu_epi64(&tweak[i * BS], W0); + _mm512_storeu_epi64(&tweak[(i + 4) * BS], W1); + } + } else { + poly_double_n_le(tweak, &tweak[(N - 1) * BS], BS); + xts_compute_tweak_block(tweak, BS, N); + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/passhash/argon2fmt/argon2fmt.cpp botan3-3.12.0+dfsg/src/lib/passhash/argon2fmt/argon2fmt.cpp --- botan3-3.7.1+dfsg/src/lib/passhash/argon2fmt/argon2fmt.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/passhash/argon2fmt/argon2fmt.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include #include @@ -75,73 +75,79 @@ } bool argon2_check_pwhash(const char* password, size_t password_len, std::string_view input_hash) { - const std::vector parts = split_on(input_hash, '$'); + try { + const std::vector parts = split_on(input_hash, '$'); - if(parts.size() != 5) { - return false; - } + if(parts.size() != 5) { + return false; + } - uint8_t family = 0; + uint8_t family = 0; - if(parts[0] == "argon2d") { - family = 0; - } else if(parts[0] == "argon2i") { - family = 1; - } else if(parts[0] == "argon2id") { - family = 2; - } else { - return false; - } + if(parts[0] == "argon2d") { + family = 0; + } else if(parts[0] == "argon2i") { + family = 1; + } else if(parts[0] == "argon2id") { + family = 2; + } else { + return false; + } - if(parts[1] != "v=19") { - return false; - } + if(parts[1] != "v=19") { + return false; + } - const std::vector params = split_on(parts[2], ','); + const std::vector params = split_on(parts[2], ','); - if(params.size() != 3) { - return false; - } + if(params.size() != 3) { + return false; + } - size_t M = 0, t = 0, p = 0; + size_t M = 0; + size_t t = 0; + size_t p = 0; + + for(const auto& param_str : params) { + const std::vector param = split_on(param_str, '='); + + if(param.size() != 2) { + return false; + } + + const std::string_view key = param[0]; + const size_t val = to_u32bit(param[1]); + if(key == "m") { + M = val; + } else if(key == "t") { + t = val; + } else if(key == "p") { + p = val; + } else { + return false; + } + } - for(const auto& param_str : params) { - const std::vector param = split_on(param_str, '='); + std::vector salt(base64_decode_max_output(parts[3].size())); + salt.resize(base64_decode(salt.data(), parts[3], false)); - if(param.size() != 2) { - return false; - } + std::vector hash(base64_decode_max_output(parts[4].size())); + hash.resize(base64_decode(hash.data(), parts[4], false)); - std::string_view key = param[0]; - const size_t val = to_u32bit(param[1]); - if(key == "m") { - M = val; - } else if(key == "t") { - t = val; - } else if(key == "p") { - p = val; - } else { + if(hash.size() < 4) { return false; } - } - std::vector salt(base64_decode_max_output(parts[3].size())); - salt.resize(base64_decode(salt.data(), parts[3], false)); + std::vector generated(hash.size()); + auto pwdhash_fam = PasswordHashFamily::create_or_throw(argon2_family(family)); + auto pwdhash = pwdhash_fam->from_params(M, t, p); - std::vector hash(base64_decode_max_output(parts[4].size())); - hash.resize(base64_decode(hash.data(), parts[4], false)); + pwdhash->derive_key(generated.data(), generated.size(), password, password_len, salt.data(), salt.size()); - if(hash.size() < 4) { + return CT::is_equal(generated.data(), hash.data(), generated.size()).as_bool(); + } catch(...) { return false; } - - std::vector generated(hash.size()); - auto pwdhash_fam = PasswordHashFamily::create_or_throw(argon2_family(family)); - auto pwdhash = pwdhash_fam->from_params(M, t, p); - - pwdhash->derive_key(generated.data(), generated.size(), password, password_len, salt.data(), salt.size()); - - return CT::is_equal(generated.data(), hash.data(), generated.size()).as_bool(); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/passhash/bcrypt/bcrypt.cpp botan3-3.12.0+dfsg/src/lib/passhash/bcrypt/bcrypt.cpp --- botan3-3.7.1+dfsg/src/lib/passhash/bcrypt/bcrypt.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/passhash/bcrypt/bcrypt.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,10 +8,13 @@ #include #include +#include +#include #include #include #include #include +#include #include namespace Botan { @@ -73,15 +76,15 @@ return ret; } -std::string bcrypt_base64_encode(const uint8_t input[], size_t length) { - std::string b64 = base64_encode(input, length); +std::string bcrypt_base64_encode(std::span input) { + std::string b64 = base64_encode(input); - while(!b64.empty() && b64[b64.size() - 1] == '=') { - b64 = b64.substr(0, b64.size() - 1); + while(!b64.empty() && b64.back() == '=') { + b64.pop_back(); } - for(size_t i = 0; i != b64.size(); ++i) { - b64[i] = static_cast(base64_to_bcrypt_encoding(static_cast(b64[i]))); + for(char& c : b64) { + c = static_cast(base64_to_bcrypt_encoding(static_cast(c))); } return b64; @@ -89,15 +92,14 @@ std::vector bcrypt_base64_decode(std::string_view input) { std::string translated; - for(size_t i = 0; i != input.size(); ++i) { - char c = bcrypt_encoding_to_base64(static_cast(input[i])); - translated.push_back(c); + for(const char c : input) { + translated.push_back(bcrypt_encoding_to_base64(static_cast(c))); } return unlock(base64_decode(translated)); } -std::string make_bcrypt(std::string_view pass, const std::vector& salt, uint16_t work_factor, char version) { +std::string make_bcrypt(std::string_view pass, std::span salt, uint16_t work_factor, char version) { /* * On a 4 GHz Skylake, workfactor == 18 takes about 15 seconds to * hash a password. This seems like a reasonable upper bound for the @@ -112,12 +114,12 @@ Blowfish blowfish; - secure_vector pass_with_trailing_null(pass.size() + 1); - copy_mem(pass_with_trailing_null.data(), cast_char_ptr_to_uint8(pass.data()), pass.length()); + // Bcrypt is defined with the key including the trailing NULL so we must copy it to a local + // variable since std::string_view is not necessarily NULL terminated. + secure_vector pass_w_null(pass.size() + 1); + copy_mem(std::span{pass_w_null}.first(pass.size()), as_span_of_bytes(pass)); - // Include the trailing NULL byte, so we need c_str() not data() - blowfish.salted_set_key( - pass_with_trailing_null.data(), pass_with_trailing_null.size(), salt.data(), salt.size(), work_factor); + blowfish.salted_set_key(pass_w_null.data(), pass_w_null.size(), salt.data(), salt.size(), work_factor); std::vector ctext(BCRYPT_MAGIC, BCRYPT_MAGIC + 8 * 3); @@ -125,7 +127,7 @@ blowfish.encrypt_n(ctext.data(), ctext.data(), 3); } - std::string salt_b64 = bcrypt_base64_encode(salt.data(), salt.size()); + const std::string salt_b64 = bcrypt_base64_encode(salt); std::string work_factor_str = std::to_string(work_factor); if(work_factor_str.length() == 1) { @@ -136,7 +138,7 @@ version, work_factor_str, salt_b64.substr(0, 22), - bcrypt_base64_encode(ctext.data(), ctext.size() - 1)); + bcrypt_base64_encode(std::span{ctext}.first(ctext.size() - 1))); } } // namespace @@ -167,7 +169,17 @@ return false; } - const uint16_t workfactor = to_uint16(hash.substr(4, 2)); + // bcrypt workfactor spec is always two characters + const char wf0 = hash[4]; + const char wf1 = hash[5]; + if(wf0 < '0' || wf0 > '9' || wf1 < '0' || wf1 > '9') { + return false; + } + const uint16_t workfactor = static_cast((wf0 - '0') * 10 + (wf1 - '0')); + // bcrypt does support larger range of workfactors, this is what make_bcrypt allows + if(workfactor < 4 || workfactor > 18) { + return false; + } const std::vector salt = bcrypt_base64_decode(hash.substr(7, 22)); if(salt.size() != 16) { @@ -176,8 +188,7 @@ const std::string compare = make_bcrypt(pass, salt, workfactor, bcrypt_version); - return CT::is_equal(cast_char_ptr_to_uint8(hash.data()), cast_char_ptr_to_uint8(compare.data()), compare.size()) - .as_bool(); + return CT::is_equal(as_span_of_bytes(hash), as_span_of_bytes(compare)).as_bool(); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/passhash/passhash9/passhash9.cpp botan3-3.12.0+dfsg/src/lib/passhash/passhash9/passhash9.cpp --- botan3-3.7.1+dfsg/src/lib/passhash/passhash9/passhash9.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/passhash/passhash9/passhash9.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -17,7 +18,7 @@ namespace { -const std::string MAGIC_PREFIX = "$9$"; +const std::string_view MAGIC_PREFIX = "$9$"; const size_t WORKFACTOR_BYTES = 2; const size_t ALGID_BYTES = 1; @@ -55,7 +56,7 @@ throw Invalid_Argument("Passhash9: Algorithm id " + std::to_string(alg_id) + " is not defined"); } - PKCS5_PBKDF2 kdf(std::move(prf)); + const PKCS5_PBKDF2 kdf(std::move(prf)); secure_vector salt(SALT_BYTES); rng.randomize(salt.data(), salt.size()); @@ -69,7 +70,7 @@ blob += salt; blob += kdf.derive_key(PASSHASH9_PBKDF_OUTPUT_LEN, pass, salt.data(), salt.size(), kdf_iterations).bits_of(); - return MAGIC_PREFIX + base64_encode(blob); + return std::string(MAGIC_PREFIX) + base64_encode(blob); } bool check_passhash9(std::string_view pass, std::string_view hash) { @@ -93,7 +94,7 @@ return false; } - uint8_t alg_id = bin[0]; + const uint8_t alg_id = bin[0]; const size_t work_factor = load_be(&bin[ALGID_BYTES], 0); @@ -114,7 +115,7 @@ return false; // unknown algorithm, reject } - PKCS5_PBKDF2 kdf(std::move(pbkdf_prf)); + const PKCS5_PBKDF2 kdf(std::move(pbkdf_prf)); secure_vector cmp = kdf.derive_key(PASSHASH9_PBKDF_OUTPUT_LEN, pass, &bin[ALGID_BYTES + WORKFACTOR_BYTES], SALT_BYTES, kdf_iterations) diff -Nru botan3-3.7.1+dfsg/src/lib/pbkdf/argon2/argon2.cpp botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2.cpp --- botan3-3.7.1+dfsg/src/lib/pbkdf/argon2/argon2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,11 +6,12 @@ #include -#include #include #include +#include #include #include +#include #include #include @@ -18,7 +19,7 @@ #include #endif -#if defined(BOTAN_HAS_ARGON2_AVX2) || defined(BOTAN_HAS_ARGON2_SSSE3) +#if defined(BOTAN_HAS_CPUID) #include #endif @@ -53,7 +54,7 @@ blake2b.update_le(static_cast(y)); blake2b.update_le(static_cast(password_len)); - blake2b.update(cast_char_ptr_to_uint8(password), password_len); + blake2b.update(as_span_of_bytes(password, password_len)); blake2b.update_le(static_cast(salt_len)); blake2b.update(salt, salt_len); @@ -84,7 +85,7 @@ if(output_len <= 64) { auto blake2b = HashFunction::create_or_throw(fmt("BLAKE2b({})", output_len * 8)); blake2b->update_le(static_cast(output_len)); - for(size_t i = 0; i != 128; ++i) { + for(size_t i = 0; i != 128; ++i) { // NOLINT(modernize-loop-convert) blake2b->update_le(sum[i]); } blake2b->final(output); @@ -93,19 +94,19 @@ auto blake2b = HashFunction::create_or_throw("BLAKE2b(512)"); blake2b->update_le(static_cast(output_len)); - for(size_t i = 0; i != 128; ++i) { + for(size_t i = 0; i != 128; ++i) { // NOLINT(modernize-loop-convert) blake2b->update_le(sum[i]); } - blake2b->final(&T[0]); + blake2b->final(std::span{T}); while(output_len > 64) { - copy_mem(output, &T[0], 32); + copy_mem(output, T.data(), 32); output_len -= 32; output += 32; if(output_len > 64) { blake2b->update(T); - blake2b->final(&T[0]); + blake2b->final(std::span{T}); } } @@ -166,15 +167,21 @@ } // namespace void Argon2::blamka(uint64_t N[128], uint64_t T[128]) { +#if defined(BOTAN_HAS_ARGON2_AVX512) + if(CPUID::has(CPUID::Feature::AVX512)) { + return Argon2::blamka_avx512(N, T); + } +#endif + #if defined(BOTAN_HAS_ARGON2_AVX2) - if(CPUID::has_avx2()) { + if(CPUID::has(CPUID::Feature::AVX2)) { return Argon2::blamka_avx2(N, T); } #endif -#if defined(BOTAN_HAS_ARGON2_SSSE3) - if(CPUID::has_ssse3()) { - return Argon2::blamka_ssse3(N, T); +#if defined(BOTAN_HAS_ARGON2_SIMD64) + if(CPUID::has(CPUID::Feature::SIMD_2X64)) { + return Argon2::blamka_simd64(N, T); } #endif @@ -235,9 +242,27 @@ } } +// Reduce random modulo Argon2 thread count (normally a power of 2) +inline size_t mod_threads(uint32_t random, size_t threads) { + if(is_power_of_2(threads)) { + return random & static_cast(threads - 1); + } else { + return random % threads; + } +} + +// Reduce alpha modulo the lane length; always a multiple of 4 and commonly a power of 2 +inline size_t mod_lanes(uint64_t alpha, size_t lanes) { + if(is_power_of_2(lanes)) { + return static_cast(alpha & static_cast(lanes - 1)); + } else { + return alpha % lanes; + } +} + uint32_t index_alpha( uint64_t random, size_t lanes, size_t segments, size_t threads, size_t n, size_t slice, size_t lane, size_t index) { - size_t ref_lane = static_cast(random >> 32) % threads; + size_t ref_lane = mod_threads(static_cast(random >> 32), threads); if(n == 0 && slice == 0) { ref_lane = lane; @@ -266,7 +291,7 @@ p = (p * p) >> 32; p = (p * m) >> 32; - return static_cast(ref_lane * lanes + (s + m - (p + 1)) % lanes); + return static_cast(ref_lane * lanes + mod_lanes(s + m - (p + 1), lanes)); } void process_block(secure_vector& B, diff -Nru botan3-3.7.1+dfsg/src/lib/pbkdf/argon2/argon2.h botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2.h --- botan3-3.7.1+dfsg/src/lib/pbkdf/argon2/argon2.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2.h 2026-05-07 01:38:28.000000000 +0000 @@ -18,8 +18,6 @@ namespace Botan { -class RandomNumberGenerator; - /** * Argon2 key derivation function */ @@ -27,9 +25,6 @@ public: Argon2(uint8_t family, size_t M, size_t t, size_t p); - Argon2(const Argon2& other) = default; - Argon2& operator=(const Argon2&) = default; - /** * Derive a new key under the current Argon2 parameter set */ @@ -77,12 +72,16 @@ static void blamka(uint64_t N[128], uint64_t T[128]); private: +#if defined(BOTAN_HAS_ARGON2_AVX512) + static void blamka_avx512(uint64_t N[128], uint64_t T[128]); +#endif + #if defined(BOTAN_HAS_ARGON2_AVX2) static void blamka_avx2(uint64_t N[128], uint64_t T[128]); #endif -#if defined(BOTAN_HAS_ARGON2_SSSE3) - static void blamka_ssse3(uint64_t N[128], uint64_t T[128]); +#if defined(BOTAN_HAS_ARGON2_SIMD64) + static void blamka_simd64(uint64_t N[128], uint64_t T[128]); #endif void argon2(uint8_t output[], @@ -102,14 +101,14 @@ class BOTAN_PUBLIC_API(2, 11) Argon2_Family final : public PasswordHashFamily { public: - Argon2_Family(uint8_t family); + BOTAN_FUTURE_EXPLICIT Argon2_Family(uint8_t family); std::string name() const override; - std::unique_ptr tune(size_t output_length, - std::chrono::milliseconds msec, - size_t max_memory, - std::chrono::milliseconds tune_msec) const override; + std::unique_ptr tune_params(size_t output_len, + uint64_t desired_runtime_msec, + std::optional max_memory, + uint64_t tune_msec) const override; std::unique_ptr default_params() const override; diff -Nru botan3-3.7.1+dfsg/src/lib/pbkdf/argon2/argon2_avx2/argon2_avx2.cpp botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_avx2/argon2_avx2.cpp --- botan3-3.7.1+dfsg/src/lib/pbkdf/argon2/argon2_avx2/argon2_avx2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_avx2/argon2_avx2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,128 +7,14 @@ #include #include -#include +#include +#include namespace Botan { namespace { -class SIMD_4x64 final { - public: - SIMD_4x64& operator=(const SIMD_4x64& other) = default; - SIMD_4x64(const SIMD_4x64& other) = default; - - SIMD_4x64& operator=(SIMD_4x64&& other) = default; - SIMD_4x64(SIMD_4x64&& other) = default; - - ~SIMD_4x64() = default; - - // zero initialized - BOTAN_FUNC_ISA("avx2") SIMD_4x64() { m_simd = _mm256_setzero_si256(); } - - // Load two halves at different addresses - static BOTAN_FUNC_ISA("avx2") SIMD_4x64 load_le2(const void* inl, const void* inh) { - return SIMD_4x64( - _mm256_loadu2_m128i(reinterpret_cast(inl), reinterpret_cast(inh))); - } - - static BOTAN_FUNC_ISA("avx2") SIMD_4x64 load_le(const void* in) { - return SIMD_4x64(_mm256_loadu_si256(reinterpret_cast(in))); - } - - void store_le(uint64_t out[4]) const { this->store_le(reinterpret_cast(out)); } - - BOTAN_FUNC_ISA("avx2") void store_le(uint8_t out[]) const { - _mm256_storeu_si256(reinterpret_cast<__m256i*>(out), m_simd); - } - - BOTAN_FUNC_ISA("avx2") void store_le2(void* outh, void* outl) { - _mm256_storeu2_m128i(reinterpret_cast<__m128i*>(outh), reinterpret_cast<__m128i*>(outl), m_simd); - } - - SIMD_4x64 operator+(const SIMD_4x64& other) const { - SIMD_4x64 retval(*this); - retval += other; - return retval; - } - - SIMD_4x64 operator^(const SIMD_4x64& other) const { - SIMD_4x64 retval(*this); - retval ^= other; - return retval; - } - - BOTAN_FUNC_ISA("avx2") void operator+=(const SIMD_4x64& other) { - m_simd = _mm256_add_epi64(m_simd, other.m_simd); - } - - BOTAN_FUNC_ISA("avx2") void operator^=(const SIMD_4x64& other) { - m_simd = _mm256_xor_si256(m_simd, other.m_simd); - } - - template - BOTAN_FUNC_ISA("avx2") - SIMD_4x64 rotr() const - requires(ROT > 0 && ROT < 64) - { - if constexpr(ROT == 16) { - auto shuf_rot_16 = - _mm256_set_epi64x(0x09080f0e0d0c0b0a, 0x0100070605040302, 0x09080f0e0d0c0b0a, 0x0100070605040302); - - return SIMD_4x64(_mm256_shuffle_epi8(m_simd, shuf_rot_16)); - } else if constexpr(ROT == 24) { - auto shuf_rot_24 = - _mm256_set_epi64x(0x0a09080f0e0d0c0b, 0x0201000706050403, 0x0a09080f0e0d0c0b, 0x0201000706050403); - - return SIMD_4x64(_mm256_shuffle_epi8(m_simd, shuf_rot_24)); - } else if constexpr(ROT == 32) { - auto shuf_rot_32 = - _mm256_set_epi64x(0x0b0a09080f0e0d0c, 0x0302010007060504, 0x0b0a09080f0e0d0c, 0x0302010007060504); - - return SIMD_4x64(_mm256_shuffle_epi8(m_simd, shuf_rot_32)); - } else { - return SIMD_4x64(_mm256_or_si256(_mm256_srli_epi64(m_simd, static_cast(ROT)), - _mm256_slli_epi64(m_simd, static_cast(64 - ROT)))); - } - } - - template - SIMD_4x64 rotl() const { - return this->rotr<64 - ROT>(); - } - - // Argon2 specific operation - static BOTAN_FUNC_ISA("avx2") SIMD_4x64 mul2_32(SIMD_4x64 x, SIMD_4x64 y) { - const __m256i m = _mm256_mul_epu32(x.m_simd, y.m_simd); - return SIMD_4x64(_mm256_add_epi64(m, m)); - } - - template - static BOTAN_FUNC_ISA("avx2") SIMD_4x64 permute_4x64(SIMD_4x64 x) { - return SIMD_4x64(_mm256_permute4x64_epi64(x.m_simd, CTRL)); - } - - // Argon2 specific - static void twist(SIMD_4x64& B, SIMD_4x64& C, SIMD_4x64& D) { - B = SIMD_4x64::permute_4x64<0b00'11'10'01>(B); - C = SIMD_4x64::permute_4x64<0b01'00'11'10>(C); - D = SIMD_4x64::permute_4x64<0b10'01'00'11>(D); - } - - // Argon2 specific - static void untwist(SIMD_4x64& B, SIMD_4x64& C, SIMD_4x64& D) { - B = SIMD_4x64::permute_4x64<0b10'01'00'11>(B); - C = SIMD_4x64::permute_4x64<0b01'00'11'10>(C); - D = SIMD_4x64::permute_4x64<0b00'11'10'01>(D); - } - - explicit BOTAN_FUNC_ISA("avx2") SIMD_4x64(__m256i x) : m_simd(x) {} - - private: - __m256i m_simd; -}; - -BOTAN_FORCE_INLINE void blamka_G(SIMD_4x64& A, SIMD_4x64& B, SIMD_4x64& C, SIMD_4x64& D) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2 void blamka_G(SIMD_4x64& A, SIMD_4x64& B, SIMD_4x64& C, SIMD_4x64& D) { A += B + SIMD_4x64::mul2_32(A, B); D ^= A; D = D.rotr<32>(); @@ -146,7 +32,7 @@ B = B.rotr<63>(); } -BOTAN_FORCE_INLINE void blamka_R(SIMD_4x64& A, SIMD_4x64& B, SIMD_4x64& C, SIMD_4x64& D) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2 void blamka_R(SIMD_4x64& A, SIMD_4x64& B, SIMD_4x64& C, SIMD_4x64& D) { blamka_G(A, B, C, D); SIMD_4x64::twist(B, C, D); @@ -156,7 +42,7 @@ } // namespace -BOTAN_FUNC_ISA("avx2") void Argon2::blamka_avx2(uint64_t N[128], uint64_t T[128]) { +BOTAN_FN_ISA_AVX2 void Argon2::blamka_avx2(uint64_t N[128], uint64_t T[128]) { for(size_t i = 0; i != 8; ++i) { SIMD_4x64 A = SIMD_4x64::load_le(&N[16 * i + 4 * 0]); SIMD_4x64 B = SIMD_4x64::load_le(&N[16 * i + 4 * 1]); @@ -188,8 +74,8 @@ for(size_t i = 0; i != 128 / 8; ++i) { SIMD_4x64 n0 = SIMD_4x64::load_le(&N[8 * i]); SIMD_4x64 n1 = SIMD_4x64::load_le(&N[8 * i + 4]); - SIMD_4x64 t0 = SIMD_4x64::load_le(&T[8 * i]); - SIMD_4x64 t1 = SIMD_4x64::load_le(&T[8 * i + 4]); + const SIMD_4x64 t0 = SIMD_4x64::load_le(&T[8 * i]); + const SIMD_4x64 t1 = SIMD_4x64::load_le(&T[8 * i + 4]); n0 ^= t0; n1 ^= t1; diff -Nru botan3-3.7.1+dfsg/src/lib/pbkdf/argon2/argon2_avx2/info.txt botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_avx2/info.txt --- botan3-3.7.1+dfsg/src/lib/pbkdf/argon2/argon2_avx2/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_avx2/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + ARGON2_AVX2 -> 20221216 - + name -> "Argon2 AVX2" @@ -10,3 +10,8 @@ avx2 + + +cpuid +simd_4x64 + diff -Nru botan3-3.7.1+dfsg/src/lib/pbkdf/argon2/argon2_avx512/argon2_avx512.cpp botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_avx512/argon2_avx512.cpp --- botan3-3.7.1+dfsg/src/lib/pbkdf/argon2/argon2_avx512/argon2_avx512.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_avx512/argon2_avx512.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,88 @@ +/** +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan { + +namespace { + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512 void blamka_G(SIMD_8x64& A, SIMD_8x64& B, SIMD_8x64& C, SIMD_8x64& D) { + A += B + SIMD_8x64::mul2_32(A, B); + D ^= A; + D = D.rotr<32>(); + + C += D + SIMD_8x64::mul2_32(C, D); + B ^= C; + B = B.rotr<24>(); + + A += B + SIMD_8x64::mul2_32(A, B); + D ^= A; + D = D.rotr<16>(); + + C += D + SIMD_8x64::mul2_32(C, D); + B ^= C; + B = B.rotr<63>(); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512 void blamka_R(SIMD_8x64& A, SIMD_8x64& B, SIMD_8x64& C, SIMD_8x64& D) { + blamka_G(A, B, C, D); + + SIMD_8x64::twist(B, C, D); + blamka_G(A, B, C, D); + SIMD_8x64::untwist(B, C, D); +} + +} // namespace + +BOTAN_FN_ISA_AVX512 void Argon2::blamka_avx512(uint64_t N[128], uint64_t T[128]) { + for(size_t i = 0; i != 8; i += 2) { + SIMD_8x64 A = SIMD_8x64::load_le4( + &N[16 * i + 4 * 0], &N[16 * i + 4 * 0 + 2], &N[16 * (i + 1) + 4 * 0], &N[16 * (i + 1) + 4 * 0 + 2]); + SIMD_8x64 B = SIMD_8x64::load_le4( + &N[16 * i + 4 * 1], &N[16 * i + 4 * 1 + 2], &N[16 * (i + 1) + 4 * 1], &N[16 * (i + 1) + 4 * 1 + 2]); + SIMD_8x64 C = SIMD_8x64::load_le4( + &N[16 * i + 4 * 2], &N[16 * i + 4 * 2 + 2], &N[16 * (i + 1) + 4 * 2], &N[16 * (i + 1) + 4 * 2 + 2]); + SIMD_8x64 D = SIMD_8x64::load_le4( + &N[16 * i + 4 * 3], &N[16 * i + 4 * 3 + 2], &N[16 * (i + 1) + 4 * 3], &N[16 * (i + 1) + 4 * 3 + 2]); + + blamka_R(A, B, C, D); + + A.store_le4(&T[16 * i + 4 * 0], &T[16 * i + 4 * 0 + 2], &T[16 * (i + 1) + 4 * 0], &T[16 * (i + 1) + 4 * 0 + 2]); + B.store_le4(&T[16 * i + 4 * 1], &T[16 * i + 4 * 1 + 2], &T[16 * (i + 1) + 4 * 1], &T[16 * (i + 1) + 4 * 1 + 2]); + C.store_le4(&T[16 * i + 4 * 2], &T[16 * i + 4 * 2 + 2], &T[16 * (i + 1) + 4 * 2], &T[16 * (i + 1) + 4 * 2 + 2]); + D.store_le4(&T[16 * i + 4 * 3], &T[16 * i + 4 * 3 + 2], &T[16 * (i + 1) + 4 * 3], &T[16 * (i + 1) + 4 * 3 + 2]); + } + + for(size_t i = 0; i != 8; i += 2) { + SIMD_8x64 A = SIMD_8x64::load_le4( + &T[2 * i + 32 * 0], &T[2 * i + 32 * 0 + 16], &T[2 * (i + 1) + 32 * 0], &T[2 * (i + 1) + 32 * 0 + 16]); + SIMD_8x64 B = SIMD_8x64::load_le4( + &T[2 * i + 32 * 1], &T[2 * i + 32 * 1 + 16], &T[2 * (i + 1) + 32 * 1], &T[2 * (i + 1) + 32 * 1 + 16]); + SIMD_8x64 C = SIMD_8x64::load_le4( + &T[2 * i + 32 * 2], &T[2 * i + 32 * 2 + 16], &T[2 * (i + 1) + 32 * 2], &T[2 * (i + 1) + 32 * 2 + 16]); + SIMD_8x64 D = SIMD_8x64::load_le4( + &T[2 * i + 32 * 3], &T[2 * i + 32 * 3 + 16], &T[2 * (i + 1) + 32 * 3], &T[2 * (i + 1) + 32 * 3 + 16]); + + blamka_R(A, B, C, D); + + A.store_le4(&T[2 * i + 32 * 0], &T[2 * i + 32 * 0 + 16], &T[2 * (i + 1) + 32 * 0], &T[2 * (i + 1) + 32 * 0 + 16]); + B.store_le4(&T[2 * i + 32 * 1], &T[2 * i + 32 * 1 + 16], &T[2 * (i + 1) + 32 * 1], &T[2 * (i + 1) + 32 * 1 + 16]); + C.store_le4(&T[2 * i + 32 * 2], &T[2 * i + 32 * 2 + 16], &T[2 * (i + 1) + 32 * 2], &T[2 * (i + 1) + 32 * 2 + 16]); + D.store_le4(&T[2 * i + 32 * 3], &T[2 * i + 32 * 3 + 16], &T[2 * (i + 1) + 32 * 3], &T[2 * (i + 1) + 32 * 3 + 16]); + } + + for(size_t i = 0; i != 128 / 8; ++i) { + SIMD_8x64 n = SIMD_8x64::load_le(&N[8 * i]); + n ^= SIMD_8x64::load_le(&T[8 * i]); + n.store_le(&N[8 * i]); + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pbkdf/argon2/argon2_avx512/info.txt botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_avx512/info.txt --- botan3-3.7.1+dfsg/src/lib/pbkdf/argon2/argon2_avx512/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_avx512/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,17 @@ + +ARGON2_AVX512 -> 20260318 + + + +name -> "Argon2 AVX-512" +brief -> "Argon2 using AVX-512 instructions" + + + +avx512 + + + +cpuid +simd_8x64 + diff -Nru botan3-3.7.1+dfsg/src/lib/pbkdf/argon2/argon2_simd64/argon2_simd64.cpp botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_simd64/argon2_simd64.cpp --- botan3-3.7.1+dfsg/src/lib/pbkdf/argon2/argon2_simd64/argon2_simd64.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_simd64/argon2_simd64.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,114 @@ +/** +* (C) 2022 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan { + +namespace { + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_SIMD_2X64 void blamka_G(SIMD_2x64& A0, + SIMD_2x64& A1, + SIMD_2x64& B0, + SIMD_2x64& B1, + SIMD_2x64& C0, + SIMD_2x64& C1, + SIMD_2x64& D0, + SIMD_2x64& D1) { + A0 += B0 + SIMD_2x64::mul2_32(A0, B0); + A1 += B1 + SIMD_2x64::mul2_32(A1, B1); + D0 ^= A0; + D1 ^= A1; + D0 = D0.rotr<32>(); + D1 = D1.rotr<32>(); + + C0 += D0 + SIMD_2x64::mul2_32(C0, D0); + C1 += D1 + SIMD_2x64::mul2_32(C1, D1); + B0 ^= C0; + B1 ^= C1; + B0 = B0.rotr<24>(); + B1 = B1.rotr<24>(); + + A0 += B0 + SIMD_2x64::mul2_32(A0, B0); + A1 += B1 + SIMD_2x64::mul2_32(A1, B1); + D0 ^= A0; + D1 ^= A1; + D0 = D0.rotr<16>(); + D1 = D1.rotr<16>(); + + C0 += D0 + SIMD_2x64::mul2_32(C0, D0); + C1 += D1 + SIMD_2x64::mul2_32(C1, D1); + B0 ^= C0; + B1 ^= C1; + B0 = B0.rotr<63>(); + B1 = B1.rotr<63>(); +} + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_SIMD_2X64 void blamka_R(SIMD_2x64& A0, + SIMD_2x64& A1, + SIMD_2x64& B0, + SIMD_2x64& B1, + SIMD_2x64& C0, + SIMD_2x64& C1, + SIMD_2x64& D0, + SIMD_2x64& D1) { + blamka_G(A0, A1, B0, B1, C0, C1, D0, D1); + + SIMD_2x64::twist(B0, B1, C0, C1, D0, D1); + blamka_G(A0, A1, B0, B1, C0, C1, D0, D1); + SIMD_2x64::untwist(B0, B1, C0, C1, D0, D1); +} + +} // namespace + +void BOTAN_FN_ISA_SIMD_2X64 Argon2::blamka_simd64(uint64_t N[128], uint64_t T[128]) { + for(size_t i = 0; i != 8; ++i) { + SIMD_2x64 Tv[8]; + for(size_t j = 0; j != 4; ++j) { + Tv[2 * j] = SIMD_2x64::load_le(&N[16 * i + 4 * j]); + Tv[2 * j + 1] = SIMD_2x64::load_le(&N[16 * i + 4 * j + 2]); + } + + blamka_R(Tv[0], Tv[1], Tv[2], Tv[3], Tv[4], Tv[5], Tv[6], Tv[7]); + + for(size_t j = 0; j != 4; ++j) { + Tv[2 * j].store_le(&T[16 * i + 4 * j]); + Tv[2 * j + 1].store_le(&T[16 * i + 4 * j + 2]); + } + } + + for(size_t i = 0; i != 8; ++i) { + SIMD_2x64 Tv[8]; + for(size_t j = 0; j != 4; ++j) { + Tv[2 * j] = SIMD_2x64::load_le(&T[2 * i + 32 * j]); + Tv[2 * j + 1] = SIMD_2x64::load_le(&T[2 * i + 32 * j + 16]); + } + + blamka_R(Tv[0], Tv[1], Tv[2], Tv[3], Tv[4], Tv[5], Tv[6], Tv[7]); + + for(size_t j = 0; j != 4; ++j) { + Tv[2 * j].store_le(&T[2 * i + 32 * j]); + Tv[2 * j + 1].store_le(&T[2 * i + 32 * j + 16]); + } + } + + for(size_t i = 0; i != 128 / 4; ++i) { + SIMD_2x64 n0 = SIMD_2x64::load_le(&N[4 * i]); + SIMD_2x64 n1 = SIMD_2x64::load_le(&N[4 * i + 2]); + const SIMD_2x64 t0 = SIMD_2x64::load_le(&T[4 * i]); + const SIMD_2x64 t1 = SIMD_2x64::load_le(&T[4 * i + 2]); + + n0 ^= t0; + n1 ^= t1; + n0.store_le(&N[4 * i]); + n1.store_le(&N[4 * i + 2]); + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pbkdf/argon2/argon2_simd64/info.txt botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_simd64/info.txt --- botan3-3.7.1+dfsg/src/lib/pbkdf/argon2/argon2_simd64/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_simd64/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,18 @@ + +ARGON2_SIMD64 -> 20251107 + + + +name -> "Argon2 SIMD_2x64" +brief -> "Argon2 using SIMD_2x64" + + +# MSVC miscompiles this code on x86-32 + +!msvc + + + +cpuid +simd_2x64 + diff -Nru botan3-3.7.1+dfsg/src/lib/pbkdf/argon2/argon2_ssse3/argon2_ssse3.cpp botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_ssse3/argon2_ssse3.cpp --- botan3-3.7.1+dfsg/src/lib/pbkdf/argon2/argon2_ssse3/argon2_ssse3.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_ssse3/argon2_ssse3.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,234 +0,0 @@ -/** -* (C) 2022 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include - -namespace Botan { - -namespace { - -class SIMD_2x64 final { - public: - SIMD_2x64& operator=(const SIMD_2x64& other) = default; - SIMD_2x64(const SIMD_2x64& other) = default; - - SIMD_2x64& operator=(SIMD_2x64&& other) = default; - SIMD_2x64(SIMD_2x64&& other) = default; - - ~SIMD_2x64() = default; - - // zero initialized - SIMD_2x64() { m_simd = _mm_setzero_si128(); } - - static SIMD_2x64 load_le(const void* in) { - return SIMD_2x64(_mm_loadu_si128(reinterpret_cast(in))); - } - - void store_le(uint64_t out[2]) const { this->store_le(reinterpret_cast(out)); } - - void store_le(uint8_t out[]) const { _mm_storeu_si128(reinterpret_cast<__m128i*>(out), m_simd); } - - SIMD_2x64 operator+(const SIMD_2x64& other) const { - SIMD_2x64 retval(*this); - retval += other; - return retval; - } - - SIMD_2x64 operator^(const SIMD_2x64& other) const { - SIMD_2x64 retval(*this); - retval ^= other; - return retval; - } - - void operator+=(const SIMD_2x64& other) { m_simd = _mm_add_epi64(m_simd, other.m_simd); } - - void operator^=(const SIMD_2x64& other) { m_simd = _mm_xor_si128(m_simd, other.m_simd); } - - template - BOTAN_FUNC_ISA("ssse3") - SIMD_2x64 rotr() const - requires(ROT > 0 && ROT < 64) - { - if constexpr(ROT == 16) { - auto tab = _mm_setr_epi8(2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9); - return SIMD_2x64(_mm_shuffle_epi8(m_simd, tab)); - } else if constexpr(ROT == 24) { - auto tab = _mm_setr_epi8(3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10); - return SIMD_2x64(_mm_shuffle_epi8(m_simd, tab)); - } else if constexpr(ROT == 32) { - auto tab = _mm_setr_epi8(4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11); - return SIMD_2x64(_mm_shuffle_epi8(m_simd, tab)); - } else { - return SIMD_2x64(_mm_or_si128(_mm_srli_epi64(m_simd, static_cast(ROT)), - _mm_slli_epi64(m_simd, static_cast(64 - ROT)))); - } - } - - template - SIMD_2x64 rotl() const { - return this->rotr<64 - ROT>(); - } - - // Argon2 specific operation - static SIMD_2x64 mul2_32(SIMD_2x64 x, SIMD_2x64 y) { - const __m128i m = _mm_mul_epu32(x.m_simd, y.m_simd); - return SIMD_2x64(_mm_add_epi64(m, m)); - } - - template - BOTAN_FUNC_ISA("ssse3") - static SIMD_2x64 alignr(SIMD_2x64 a, SIMD_2x64 b) - requires(T > 0 && T < 16) - { - return SIMD_2x64(_mm_alignr_epi8(a.m_simd, b.m_simd, T)); - } - - // Argon2 specific - static void twist(SIMD_2x64& B0, SIMD_2x64& B1, SIMD_2x64& C0, SIMD_2x64& C1, SIMD_2x64& D0, SIMD_2x64& D1) { - SIMD_2x64 T0, T1; - - T0 = SIMD_2x64::alignr<8>(B1, B0); - T1 = SIMD_2x64::alignr<8>(B0, B1); - B0 = T0; - B1 = T1; - - T0 = C0; - C0 = C1; - C1 = T0; - - T0 = SIMD_2x64::alignr<8>(D0, D1); - T1 = SIMD_2x64::alignr<8>(D1, D0); - D0 = T0; - D1 = T1; - } - - // Argon2 specific - static void untwist(SIMD_2x64& B0, SIMD_2x64& B1, SIMD_2x64& C0, SIMD_2x64& C1, SIMD_2x64& D0, SIMD_2x64& D1) { - SIMD_2x64 T0, T1; - - T0 = SIMD_2x64::alignr<8>(B0, B1); - T1 = SIMD_2x64::alignr<8>(B1, B0); - B0 = T0; - B1 = T1; - - T0 = C0; - C0 = C1; - C1 = T0; - - T0 = SIMD_2x64::alignr<8>(D1, D0); - T1 = SIMD_2x64::alignr<8>(D0, D1); - D0 = T0; - D1 = T1; - } - - explicit SIMD_2x64(__m128i x) : m_simd(x) {} - - private: - __m128i m_simd; -}; - -BOTAN_FORCE_INLINE void blamka_G(SIMD_2x64& A0, - SIMD_2x64& A1, - SIMD_2x64& B0, - SIMD_2x64& B1, - SIMD_2x64& C0, - SIMD_2x64& C1, - SIMD_2x64& D0, - SIMD_2x64& D1) { - A0 += B0 + SIMD_2x64::mul2_32(A0, B0); - A1 += B1 + SIMD_2x64::mul2_32(A1, B1); - D0 ^= A0; - D1 ^= A1; - D0 = D0.rotr<32>(); - D1 = D1.rotr<32>(); - - C0 += D0 + SIMD_2x64::mul2_32(C0, D0); - C1 += D1 + SIMD_2x64::mul2_32(C1, D1); - B0 ^= C0; - B1 ^= C1; - B0 = B0.rotr<24>(); - B1 = B1.rotr<24>(); - - A0 += B0 + SIMD_2x64::mul2_32(A0, B0); - A1 += B1 + SIMD_2x64::mul2_32(A1, B1); - D0 ^= A0; - D1 ^= A1; - D0 = D0.rotr<16>(); - D1 = D1.rotr<16>(); - - C0 += D0 + SIMD_2x64::mul2_32(C0, D0); - C1 += D1 + SIMD_2x64::mul2_32(C1, D1); - B0 ^= C0; - B1 ^= C1; - B0 = B0.rotr<63>(); - B1 = B1.rotr<63>(); -} - -BOTAN_FORCE_INLINE void blamka_R(SIMD_2x64& A0, - SIMD_2x64& A1, - SIMD_2x64& B0, - SIMD_2x64& B1, - SIMD_2x64& C0, - SIMD_2x64& C1, - SIMD_2x64& D0, - SIMD_2x64& D1) { - blamka_G(A0, A1, B0, B1, C0, C1, D0, D1); - - SIMD_2x64::twist(B0, B1, C0, C1, D0, D1); - blamka_G(A0, A1, B0, B1, C0, C1, D0, D1); - SIMD_2x64::untwist(B0, B1, C0, C1, D0, D1); -} - -} // namespace - -void Argon2::blamka_ssse3(uint64_t N[128], uint64_t T[128]) { - for(size_t i = 0; i != 8; ++i) { - SIMD_2x64 Tv[8]; - for(size_t j = 0; j != 4; ++j) { - Tv[2 * j] = SIMD_2x64::load_le(&N[16 * i + 4 * j]); - Tv[2 * j + 1] = SIMD_2x64::load_le(&N[16 * i + 4 * j + 2]); - } - - blamka_R(Tv[0], Tv[1], Tv[2], Tv[3], Tv[4], Tv[5], Tv[6], Tv[7]); - - for(size_t j = 0; j != 4; ++j) { - Tv[2 * j].store_le(&T[16 * i + 4 * j]); - Tv[2 * j + 1].store_le(&T[16 * i + 4 * j + 2]); - } - } - - for(size_t i = 0; i != 8; ++i) { - SIMD_2x64 Tv[8]; - for(size_t j = 0; j != 4; ++j) { - Tv[2 * j] = SIMD_2x64::load_le(&T[2 * i + 32 * j]); - Tv[2 * j + 1] = SIMD_2x64::load_le(&T[2 * i + 32 * j + 16]); - } - - blamka_R(Tv[0], Tv[1], Tv[2], Tv[3], Tv[4], Tv[5], Tv[6], Tv[7]); - - for(size_t j = 0; j != 4; ++j) { - Tv[2 * j].store_le(&T[2 * i + 32 * j]); - Tv[2 * j + 1].store_le(&T[2 * i + 32 * j + 16]); - } - } - - for(size_t i = 0; i != 128 / 4; ++i) { - SIMD_2x64 n0 = SIMD_2x64::load_le(&N[4 * i]); - SIMD_2x64 n1 = SIMD_2x64::load_le(&N[4 * i + 2]); - SIMD_2x64 t0 = SIMD_2x64::load_le(&T[4 * i]); - SIMD_2x64 t1 = SIMD_2x64::load_le(&T[4 * i + 2]); - - n0 ^= t0; - n1 ^= t1; - n0.store_le(&N[4 * i]); - n1.store_le(&N[4 * i + 2]); - } -} - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pbkdf/argon2/argon2_ssse3/info.txt botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_ssse3/info.txt --- botan3-3.7.1+dfsg/src/lib/pbkdf/argon2/argon2_ssse3/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2_ssse3/info.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ - -ARGON2_SSSE3 -> 20220303 - - - -name -> "Argon2 SSSE3" -brief -> "Argon2 using SSSE3 instructions" - - - -ssse3 - - -# MSVC miscompiles this code on x86-32 - -!msvc - diff -Nru botan3-3.7.1+dfsg/src/lib/pbkdf/argon2/argon2pwhash.cpp botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2pwhash.cpp --- botan3-3.7.1+dfsg/src/lib/pbkdf/argon2/argon2pwhash.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pbkdf/argon2/argon2pwhash.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -74,15 +74,16 @@ return argon2_family_name(m_family); } -std::unique_ptr Argon2_Family::tune(size_t /*output_length*/, - std::chrono::milliseconds msec, - size_t max_memory, - std::chrono::milliseconds tune_time) const { - const size_t max_kib = (max_memory == 0) ? 256 * 1024 : max_memory * 1024; +std::unique_ptr Argon2_Family::tune_params(size_t /*output_length*/, + uint64_t desired_msec, + std::optional max_memory, + uint64_t tune_msec) const { + // If not set use 256 MB as default max + const size_t max_kib = max_memory.value_or(256) * 1024; // Tune with a large memory otherwise we measure cache vs RAM speeds and underestimate // costs for larger params. Default is 36 MiB, or use 128 for long times. - const size_t tune_M = (msec >= std::chrono::milliseconds(200) ? 128 : 36) * 1024; + const size_t tune_M = (desired_msec >= 200 ? 128 : 36) * 1024; const size_t p = 1; size_t t = 1; @@ -95,9 +96,9 @@ pwhash->derive_key(output, sizeof(output), "test", 4, nullptr, 0); }; - const uint64_t measured_time = measure_cost(tune_time, tune_fn) / (tune_M / M); + const uint64_t measured_time = measure_cost(tune_msec, tune_fn) / (tune_M / M); - const uint64_t target_nsec = msec.count() * static_cast(1000000); + const uint64_t target_nsec = desired_msec * static_cast(1000000); /* * Argon2 scaling rules: diff -Nru botan3-3.7.1+dfsg/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp botan3-3.12.0+dfsg/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp --- botan3-3.7.1+dfsg/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,7 @@ #include #include #include +#include #include namespace Botan { @@ -26,10 +27,10 @@ return "Bcrypt-PBKDF"; } -std::unique_ptr Bcrypt_PBKDF_Family::tune(size_t output_length, - std::chrono::milliseconds msec, - size_t /*max_memory*/, - std::chrono::milliseconds tune_time) const { +std::unique_ptr Bcrypt_PBKDF_Family::tune_params(size_t output_length, + uint64_t desired_msec, + std::optional /*max_memory*/, + uint64_t tune_msec) const { const size_t blocks = (output_length + 32 - 1) / 32; if(blocks == 0) { @@ -45,9 +46,9 @@ pwhash->derive_key(output, sizeof(output), "test", 4, nullptr, 0); }; - const uint64_t measured_time = measure_cost(tune_time, tune_fn) / blocks; + const uint64_t measured_time = measure_cost(tune_msec, tune_fn) / blocks; - const uint64_t target_nsec = msec.count() * static_cast(1000000); + const uint64_t target_nsec = desired_msec * static_cast(1000000); const uint64_t desired_increase = target_nsec / measured_time; @@ -62,12 +63,12 @@ return this->from_iterations(32); // About 100 ms on fast machine } -std::unique_ptr Bcrypt_PBKDF_Family::from_iterations(size_t iter) const { - return std::make_unique(iter); +std::unique_ptr Bcrypt_PBKDF_Family::from_iterations(size_t iterations) const { + return std::make_unique(iterations); } -std::unique_ptr Bcrypt_PBKDF_Family::from_params(size_t iter, size_t /*t*/, size_t /*p*/) const { - return this->from_iterations(iter); +std::unique_ptr Bcrypt_PBKDF_Family::from_params(size_t iterations, size_t /*t*/, size_t /*p*/) const { + return this->from_iterations(iterations); } namespace { @@ -128,7 +129,7 @@ const size_t blocks = (output_len + BCRYPT_BLOCK_SIZE - 1) / BCRYPT_BLOCK_SIZE; auto sha512 = HashFunction::create_or_throw("SHA-512"); - const auto pass_hash = sha512->process(reinterpret_cast(password), password_len); + const auto pass_hash = sha512->process(as_span_of_bytes(password, password_len)); secure_vector salt_hash(sha512->output_length()); diff -Nru botan3-3.7.1+dfsg/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h botan3-3.12.0+dfsg/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h --- botan3-3.7.1+dfsg/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h 2026-05-07 01:38:28.000000000 +0000 @@ -19,10 +19,7 @@ */ class BOTAN_PUBLIC_API(2, 11) Bcrypt_PBKDF final : public PasswordHash { public: - Bcrypt_PBKDF(size_t iterations); - - Bcrypt_PBKDF(const Bcrypt_PBKDF& other) = default; - Bcrypt_PBKDF& operator=(const Bcrypt_PBKDF&) = default; + BOTAN_FUTURE_EXPLICIT Bcrypt_PBKDF(size_t iterations); /** * Derive a new key under the current Bcrypt-PBKDF parameter set @@ -54,16 +51,16 @@ std::string name() const override; - std::unique_ptr tune(size_t output_length, - std::chrono::milliseconds msec, - size_t max_memory, - std::chrono::milliseconds tune_msec) const override; + std::unique_ptr tune_params(size_t output_len, + uint64_t desired_runtime_msec, + std::optional max_memory, + uint64_t tune_msec) const override; std::unique_ptr default_params() const override; - std::unique_ptr from_iterations(size_t iter) const override; + std::unique_ptr from_iterations(size_t iterations) const override; - std::unique_ptr from_params(size_t i, size_t, size_t) const override; + std::unique_ptr from_params(size_t iterations, size_t /*unused*/, size_t /*unused*/) const override; }; /** diff -Nru botan3-3.7.1+dfsg/src/lib/pbkdf/pbkdf.h botan3-3.12.0+dfsg/src/lib/pbkdf/pbkdf.h --- botan3-3.7.1+dfsg/src/lib/pbkdf/pbkdf.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pbkdf/pbkdf.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -26,9 +27,11 @@ * and iterated hashing to make brute force attacks harder. * * Starting in 2.8 this functionality is also offered by PasswordHash. -* The PBKDF interface may be removed in a future release. +* +* @warning +* This class will be removed in a future major release. Use PasswordHash */ -class BOTAN_PUBLIC_API(2, 0) PBKDF { +class BOTAN_PUBLIC_API(2, 0) PBKDF /* NOLINT(*-special-member-functions) */ { public: /** * Create an instance based on a name diff -Nru botan3-3.7.1+dfsg/src/lib/pbkdf/pbkdf2/pbkdf2.cpp botan3-3.12.0+dfsg/src/lib/pbkdf/pbkdf2/pbkdf2.cpp --- botan3-3.7.1+dfsg/src/lib/pbkdf/pbkdf2/pbkdf2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pbkdf/pbkdf2/pbkdf2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,7 +9,10 @@ #include #include +#include +#include #include +#include #include namespace Botan { @@ -18,7 +21,7 @@ void pbkdf2_set_key(MessageAuthenticationCode& prf, const char* password, size_t password_len) { try { - prf.set_key(cast_char_ptr_to_uint8(password), password_len); + prf.set_key(as_span_of_bytes(password, password_len)); } catch(Invalid_Key_Length&) { throw Invalid_Argument("PBKDF2 cannot accept passphrase of the given size"); } @@ -26,15 +29,15 @@ size_t tune_pbkdf2(MessageAuthenticationCode& prf, size_t output_length, - std::chrono::milliseconds msec, - std::chrono::milliseconds tune_time = std::chrono::milliseconds(10)) { + uint64_t desired_msec, + uint64_t tuning_msec = 10) { if(output_length == 0) { output_length = 1; } const size_t prf_sz = prf.output_length(); BOTAN_ASSERT_NOMSG(prf_sz > 0); - secure_vector U(prf_sz); + const secure_vector U(prf_sz); const size_t trial_iterations = 2000; @@ -42,13 +45,13 @@ prf.set_key(nullptr, 0); - const uint64_t duration_nsec = measure_cost(tune_time, [&]() { + const uint64_t duration_nsec = measure_cost(tuning_msec, [&]() { uint8_t out[12] = {0}; uint8_t salt[12] = {0}; pbkdf2(prf, out, sizeof(out), salt, sizeof(salt), trial_iterations); }); - const uint64_t desired_nsec = static_cast(msec.count()) * 1000000; + const uint64_t desired_nsec = desired_msec * 1000000; if(duration_nsec > desired_nsec) { return trial_iterations; @@ -76,10 +79,10 @@ size_t iterations, std::chrono::milliseconds msec) { if(iterations == 0) { - iterations = tune_pbkdf2(prf, out_len, msec); + iterations = tune_pbkdf2(prf, out_len, msec.count()); } - PBKDF2 pbkdf2(prf, iterations); + const PBKDF2 pbkdf2(prf, iterations); pbkdf2.derive_key(out, out_len, password.data(), password.size(), salt, salt_len); @@ -105,10 +108,14 @@ const size_t prf_sz = prf.output_length(); BOTAN_ASSERT_NOMSG(prf_sz > 0); + // RFC 2898 Section 5.2: derived key length limited to (2^32 - 1) * hLen + const auto blocks_required = ceil_division(out_len, prf_sz); + BOTAN_ARG_CHECK(blocks_required <= 0xFFFFFFFE, "PBKDF2 maximum output length exceeded"); + secure_vector U(prf_sz); uint32_t counter = 1; - while(out_len) { + while(out_len > 0) { const size_t prf_output = std::min(prf_sz, out_len); prf.update(salt, salt_len); @@ -137,10 +144,10 @@ size_t iterations, std::chrono::milliseconds msec) const { if(iterations == 0) { - iterations = tune_pbkdf2(*m_mac, key_len, msec); + iterations = tune_pbkdf2(*m_mac, key_len, msec.count()); } - PBKDF2 pbkdf2(*m_mac, iterations); + const PBKDF2 pbkdf2(*m_mac, iterations); pbkdf2.derive_key(key, key_len, password.data(), password.size(), salt, salt_len); @@ -158,7 +165,7 @@ // PasswordHash interface PBKDF2::PBKDF2(const MessageAuthenticationCode& prf, size_t olen, std::chrono::milliseconds msec) : - m_prf(prf.new_object()), m_iterations(tune_pbkdf2(*m_prf, olen, msec)) {} + m_prf(prf.new_object()), m_iterations(tune_pbkdf2(*m_prf, olen, msec.count())) {} std::string PBKDF2::to_string() const { return fmt("PBKDF2({},{})", m_prf->name(), m_iterations); @@ -178,11 +185,11 @@ return fmt("PBKDF2({})", m_prf->name()); } -std::unique_ptr PBKDF2_Family::tune(size_t output_len, - std::chrono::milliseconds msec, - size_t /*max_memory_usage_mb*/, - std::chrono::milliseconds tune_time) const { - auto iterations = tune_pbkdf2(*m_prf, output_len, msec, tune_time); +std::unique_ptr PBKDF2_Family::tune_params(size_t output_len, + uint64_t desired_runtime_msec, + std::optional /*max_memory*/, + uint64_t tune_msec) const { + auto iterations = tune_pbkdf2(*m_prf, output_len, desired_runtime_msec, tune_msec); return std::make_unique(*m_prf, iterations); } diff -Nru botan3-3.7.1+dfsg/src/lib/pbkdf/pbkdf2/pbkdf2.h botan3-3.12.0+dfsg/src/lib/pbkdf/pbkdf2/pbkdf2.h --- botan3-3.7.1+dfsg/src/lib/pbkdf/pbkdf2/pbkdf2.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pbkdf/pbkdf2/pbkdf2.h 2026-05-07 01:38:28.000000000 +0000 @@ -46,7 +46,7 @@ public: PBKDF2(const MessageAuthenticationCode& prf, size_t iter) : m_prf(prf.new_object()), m_iterations(iter) {} - BOTAN_DEPRECATED("For runtime tuning use PBKDF2_Family::tune") + BOTAN_DEPRECATED("For runtime tuning use PBKDF2_Family::tune_params") PBKDF2(const MessageAuthenticationCode& prf, size_t olen, std::chrono::milliseconds msec); size_t iterations() const override { return m_iterations; } @@ -70,14 +70,14 @@ */ class BOTAN_PUBLIC_API(2, 8) PBKDF2_Family final : public PasswordHashFamily { public: - PBKDF2_Family(std::unique_ptr prf) : m_prf(std::move(prf)) {} + BOTAN_FUTURE_EXPLICIT PBKDF2_Family(std::unique_ptr prf) : m_prf(std::move(prf)) {} std::string name() const override; - std::unique_ptr tune(size_t output_len, - std::chrono::milliseconds msec, - size_t max_memory, - std::chrono::milliseconds tune_msec) const override; + std::unique_ptr tune_params(size_t output_len, + uint64_t desired_runtime_msec, + std::optional max_memory, + uint64_t tune_msec) const override; /** * Return some default parameter set for this PBKDF that should be good @@ -88,7 +88,7 @@ std::unique_ptr from_iterations(size_t iter) const override; - std::unique_ptr from_params(size_t iter, size_t, size_t) const override; + std::unique_ptr from_params(size_t iter, size_t /*unused*/, size_t /*unused*/) const override; private: std::unique_ptr m_prf; diff -Nru botan3-3.7.1+dfsg/src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp botan3-3.12.0+dfsg/src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp --- botan3-3.7.1+dfsg/src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,7 +9,9 @@ #include #include +#include #include +#include #include #include @@ -31,10 +33,10 @@ secure_vector input_buf(salt_len + password_size); if(salt_len > 0) { - copy_mem(&input_buf[0], salt, salt_len); + copy_mem(input_buf.data(), salt, salt_len); } if(password_size > 0) { - copy_mem(&input_buf[salt_len], cast_char_ptr_to_uint8(password), password_size); + copy_mem(std::span(input_buf).subspan(salt_len), as_span_of_bytes(password, password_size)); } secure_vector hash_buf(hash.output_length()); @@ -50,7 +52,7 @@ hash.update(zero_padding); // The input is always fully processed even if iterations is very small - if(input_buf.empty() == false) { + if(!input_buf.empty()) { size_t left = std::max(iterations, input_buf.size()); while(left > 0) { const size_t input_to_take = std::min(left, input_buf.size()); @@ -74,12 +76,10 @@ const uint8_t salt[], size_t salt_len, size_t iterations, - std::chrono::milliseconds msec) const { - std::unique_ptr pwdhash; - + std::chrono::milliseconds desired_msec) const { if(iterations == 0) { - RFC4880_S2K_Family s2k_params(m_hash->new_object()); - iterations = s2k_params.tune(output_len, msec, 0, std::chrono::milliseconds(10))->iterations(); + const RFC4880_S2K_Family s2k_params(m_hash->new_object()); + iterations = s2k_params.tune_params(output_len, desired_msec.count(), {}, 10)->iterations(); } pgp_s2k(*m_hash, output_buf, output_len, password.data(), password.size(), salt, salt_len, iterations); @@ -91,17 +91,17 @@ return fmt("OpenPGP-S2K({})", m_hash->name()); } -std::unique_ptr RFC4880_S2K_Family::tune(size_t output_len, - std::chrono::milliseconds msec, - size_t /*max_memory_usage_mb*/, - std::chrono::milliseconds tune_time) const { +std::unique_ptr RFC4880_S2K_Family::tune_params(size_t output_len, + uint64_t desired_msec, + std::optional /*max_memory*/, + uint64_t tuning_msec) const { constexpr size_t buf_size = 1024; std::vector buffer(buf_size); - const uint64_t measured_nsec = measure_cost(tune_time, [&]() { m_hash->update(buffer); }); + const uint64_t measured_nsec = measure_cost(tuning_msec, [&]() { m_hash->update(buffer); }); const double hash_bytes_per_second = (buf_size * 1000000000.0) / measured_nsec; - const uint64_t desired_nsec = msec.count() * 1000000; + const uint64_t desired_nsec = desired_msec * 1000000; const size_t hash_size = m_hash->output_length(); const size_t blocks_required = (output_len <= hash_size ? 1 : (output_len + hash_size - 1) / hash_size); @@ -112,16 +112,18 @@ return std::make_unique(m_hash->new_object(), iterations); } -std::unique_ptr RFC4880_S2K_Family::from_params(size_t iter, size_t /*i2*/, size_t /*i3*/) const { - return std::make_unique(m_hash->new_object(), iter); +std::unique_ptr RFC4880_S2K_Family::from_params(size_t iterations, + size_t /*unused*/, + size_t /*unused*/) const { + return std::make_unique(m_hash->new_object(), iterations); } std::unique_ptr RFC4880_S2K_Family::default_params() const { return std::make_unique(m_hash->new_object(), 50331648); } -std::unique_ptr RFC4880_S2K_Family::from_iterations(size_t iter) const { - return std::make_unique(m_hash->new_object(), iter); +std::unique_ptr RFC4880_S2K_Family::from_iterations(size_t iterations) const { + return std::make_unique(m_hash->new_object(), iterations); } RFC4880_S2K::RFC4880_S2K(std::unique_ptr hash, size_t iterations) : diff -Nru botan3-3.7.1+dfsg/src/lib/pbkdf/pgp_s2k/pgp_s2k.h botan3-3.12.0+dfsg/src/lib/pbkdf/pgp_s2k/pgp_s2k.h --- botan3-3.7.1+dfsg/src/lib/pbkdf/pgp_s2k/pgp_s2k.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pbkdf/pgp_s2k/pgp_s2k.h 2026-05-07 01:38:28.000000000 +0000 @@ -103,14 +103,14 @@ class BOTAN_PUBLIC_API(2, 8) RFC4880_S2K_Family final : public PasswordHashFamily { public: - RFC4880_S2K_Family(std::unique_ptr hash) : m_hash(std::move(hash)) {} + BOTAN_FUTURE_EXPLICIT RFC4880_S2K_Family(std::unique_ptr hash) : m_hash(std::move(hash)) {} std::string name() const override; - std::unique_ptr tune(size_t output_len, - std::chrono::milliseconds msec, - size_t max_mem, - std::chrono::milliseconds tune_msec) const override; + std::unique_ptr tune_params(size_t output_len, + uint64_t desired_runtime_msec, + std::optional max_memory, + uint64_t tune_msec) const override; /** * Return some default parameter set for this PBKDF that should be good @@ -119,9 +119,9 @@ */ std::unique_ptr default_params() const override; - std::unique_ptr from_iterations(size_t iter) const override; + std::unique_ptr from_iterations(size_t iterations) const override; - std::unique_ptr from_params(size_t iter, size_t, size_t) const override; + std::unique_ptr from_params(size_t iterations, size_t /*unused*/, size_t /*unused*/) const override; private: std::unique_ptr m_hash; diff -Nru botan3-3.7.1+dfsg/src/lib/pbkdf/pwdhash.h botan3-3.12.0+dfsg/src/lib/pbkdf/pwdhash.h --- botan3-3.7.1+dfsg/src/lib/pbkdf/pwdhash.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pbkdf/pwdhash.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,12 +8,16 @@ #define BOTAN_PWDHASH_H_ #include -#include #include +#include #include #include #include +#if !defined(BOTAN_IS_BEING_BUILT) + #include +#endif + namespace Botan { /** @@ -22,7 +26,7 @@ * Converts a password into a key using a salt and iterated hashing to * make brute force attacks harder. */ -class BOTAN_PUBLIC_API(2, 8) PasswordHash { +class BOTAN_PUBLIC_API(2, 8) PasswordHash /* NOLINT(*-special-member-functions) */ { public: virtual ~PasswordHash() = default; @@ -171,7 +175,7 @@ size_t key_len) const; }; -class BOTAN_PUBLIC_API(2, 8) PasswordHashFamily { +class BOTAN_PUBLIC_API(2, 8) PasswordHashFamily /* NOLINT(*-special-member-functions) */ { public: /** * Create an instance based on a name @@ -213,7 +217,39 @@ * The parameters will be selected to use at most @p max_memory_usage_mb * megabytes of memory, or if left as zero any size is allowed. * - * This function works by runing a short tuning loop to estimate the + * This function works by running a short tuning loop to estimate the + * performance of the algorithm, then scaling the parameters appropriately + * to hit the target size. The length of time the tuning loop runs can be + * controlled using the @p tuning_msec parameter. + * + * @param output_length how long the output length will be + * @param desired_runtime_msec the desired execution time in milliseconds + * + * @param max_memory_usage_mb some password hash functions can use a + * tunable amount of memory, in this case max_memory_usage limits the + * amount of RAM the returned parameters will require, in mebibytes (2**20 + * bytes). It may require some small amount above the request. Set to nullopt + * to place no limit at all. + * @param tuning_msec how long to run the tuning loop + */ + virtual std::unique_ptr tune_params(size_t output_length, + uint64_t desired_runtime_msec, + std::optional max_memory_usage_mb = {}, + uint64_t tuning_msec = 10) const = 0; + +#if !defined(BOTAN_IS_BEING_BUILT) + /** + * Return a new parameter set tuned for this machine + * + * Return a password hash instance tuned to run for approximately @p msec + * milliseconds when producing an output of length @p output_length. + * (Accuracy may vary, use the command line utility ``botan pbkdf_tune`` to + * check.) + * + * The parameters will be selected to use at most @p max_memory_usage_mb + * megabytes of memory, or if left as zero any size is allowed. + * + * This function works by running a short tuning loop to estimate the * performance of the algorithm, then scaling the parameters appropriately * to hit the target size. The length of time the tuning loop runs can be * controlled using the @p tuning_msec parameter. @@ -227,13 +263,25 @@ * bytes). It may require some small amount above the request. Set to zero * to place no limit at all. * @param tuning_msec how long to run the tuning loop + * + * TODO(Botan4) remove this */ - virtual std::unique_ptr tune( - size_t output_length, - std::chrono::milliseconds msec, - size_t max_memory_usage_mb = 0, - std::chrono::milliseconds tuning_msec = std::chrono::milliseconds(10)) const = 0; - + BOTAN_DEPRECATED("Use tune_params instead") + std::unique_ptr tune(size_t output_length, + std::chrono::milliseconds msec, + size_t max_memory_usage_mb = 0, + std::chrono::milliseconds tuning_msec = std::chrono::milliseconds(10)) const { + std::optional max_memory_opt; + if(max_memory_usage_mb > 0) { + max_memory_opt = max_memory_usage_mb; + } + + return this->tune_params(output_length, + static_cast(msec.count()), + max_memory_opt, + static_cast(tuning_msec.count())); + } +#endif /** * Return some default parameter set for this PBKDF that should be good * enough for most users. The value returned may change over time as diff -Nru botan3-3.7.1+dfsg/src/lib/pbkdf/scrypt/scrypt.cpp botan3-3.12.0+dfsg/src/lib/pbkdf/scrypt/scrypt.cpp --- botan3-3.7.1+dfsg/src/lib/pbkdf/scrypt/scrypt.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pbkdf/scrypt/scrypt.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -33,17 +34,15 @@ return std::make_unique(32768, 8, 1); } -std::unique_ptr Scrypt_Family::tune(size_t output_length, - std::chrono::milliseconds msec, - size_t max_memory_usage_mb, - std::chrono::milliseconds tune_time) const { - BOTAN_UNUSED(output_length); - +std::unique_ptr Scrypt_Family::tune_params(size_t /*output_length*/, + uint64_t desired_msec, + std::optional max_memory, + uint64_t tuning_msec) const { /* * Some rough relations between scrypt parameters and runtime. * Denote here by stime(N,r,p) the msec it takes to run scrypt. * - * Emperically for smaller sizes: + * Empirically for smaller sizes: * stime(N,8*r,p) / stime(N,r,p) is ~ 6-7 * stime(N,r,8*p) / stime(N,r,8*p) is ~ 7 * stime(2*N,r,p) / stime(N,r,p) is ~ 2 @@ -51,8 +50,8 @@ * Compute stime(8192,1,1) as baseline and extrapolate */ - // This is zero if max_memory_usage_mb == 0 (unbounded) - const size_t max_memory_usage = max_memory_usage_mb * 1024 * 1024; + // If max_memory is nullopt or zero this becomes zero and is ignored + const size_t max_memory_bytes = max_memory.value_or(0) * 1024 * 1024; // Starting parameters size_t N = 8 * 1024; @@ -61,12 +60,12 @@ auto pwdhash = this->from_params(N, r, p); - const uint64_t measured_time = measure_cost(tune_time, [&]() { + const uint64_t measured_time = measure_cost(tuning_msec, [&]() { uint8_t output[32] = {0}; pwdhash->derive_key(output, sizeof(output), "test", 4, nullptr, 0); }); - const uint64_t target_nsec = msec.count() * static_cast(1000000); + const uint64_t target_nsec = desired_msec * static_cast(1000000); uint64_t est_nsec = measured_time; @@ -75,7 +74,7 @@ // Including p leads to using an N half as large as what the user would expect. // First increase r by 8x if possible - if(max_memory_usage == 0 || scrypt_memory_usage(N, r * 8, 0) <= max_memory_usage) { + if(max_memory_bytes == 0 || scrypt_memory_usage(N, r * 8, 0) <= max_memory_bytes) { if(target_nsec / est_nsec >= 5) { r *= 8; est_nsec *= 5; @@ -83,7 +82,7 @@ } // Now double N as many times as we can - while(max_memory_usage == 0 || scrypt_memory_usage(N * 2, r, 0) <= max_memory_usage) { + while(max_memory_bytes == 0 || scrypt_memory_usage(N * 2, r, 0) <= max_memory_bytes) { if(target_nsec / est_nsec >= 2) { N *= 2; est_nsec *= 2; @@ -198,6 +197,10 @@ size_t password_len, const uint8_t salt[], size_t salt_len) const { + if(output_len == 0) { + return; + } + const size_t N = memory_param(); const size_t p = parallelism(); const size_t r = iterations(); @@ -210,7 +213,7 @@ auto hmac_sha256 = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)"); try { - hmac_sha256->set_key(cast_char_ptr_to_uint8(password), password_len); + hmac_sha256->set_key(as_span_of_bytes(password, password_len)); } catch(Invalid_Key_Length&) { throw Invalid_Argument("Scrypt cannot accept passphrases of the provided length"); } diff -Nru botan3-3.7.1+dfsg/src/lib/pbkdf/scrypt/scrypt.h botan3-3.12.0+dfsg/src/lib/pbkdf/scrypt/scrypt.h --- botan3-3.7.1+dfsg/src/lib/pbkdf/scrypt/scrypt.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pbkdf/scrypt/scrypt.h 2026-05-07 01:38:28.000000000 +0000 @@ -22,9 +22,6 @@ public: Scrypt(size_t N, size_t r, size_t p); - Scrypt(const Scrypt& other) = default; - Scrypt& operator=(const Scrypt&) = default; - /** * Derive a new key under the current Scrypt parameter set */ @@ -53,10 +50,10 @@ public: std::string name() const override; - std::unique_ptr tune(size_t output_length, - std::chrono::milliseconds msec, - size_t max_memory, - std::chrono::milliseconds tune_msec) const override; + std::unique_ptr tune_params(size_t output_len, + uint64_t desired_runtime_msec, + std::optional max_memory, + uint64_t tune_msec) const override; std::unique_ptr default_params() const override; diff -Nru botan3-3.7.1+dfsg/src/lib/permutations/ascon_perm/ascon_perm.cpp botan3-3.12.0+dfsg/src/lib/permutations/ascon_perm/ascon_perm.cpp --- botan3-3.7.1+dfsg/src/lib/permutations/ascon_perm/ascon_perm.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/permutations/ascon_perm/ascon_perm.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,115 @@ +/* +* Permutation Ascon_p[rounds] as specified in NIST SP.800-232, Section 3 +* (C) 2025 Jack Lloyd +* 2025 René Meusel +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include + +namespace Botan { + +void Ascon_p::absorb(std::span input, std::optional permutation_rounds) { + const auto rounds = permutation_rounds.value_or(m_processing_rounds); + absorb_into_sponge(*this, input, [this, rounds] { permute(rounds); }); +} + +void Ascon_p::squeeze(std::span output) { + squeeze_from_sponge(*this, output); +} + +void Ascon_p::percolate_in(std::span data) { + BufferSlicer input_slicer(data); + BufferStuffer output_stuffer(data); + + process_bytes_in_sponge(*this, data.size(), [&](uint64_t state_word, auto bounds) { + state_word ^= bounds.read_from(input_slicer); + bounds.write_into(output_stuffer, state_word); + return state_word; + }); + + BOTAN_ASSERT_NOMSG(input_slicer.empty()); + BOTAN_ASSERT_NOMSG(output_stuffer.full()); +} + +void Ascon_p::percolate_out(std::span data) { + BufferSlicer input_slicer(data); + BufferStuffer output_stuffer(data); + + process_bytes_in_sponge(*this, data.size(), [&](uint64_t state_word, auto bounds) { + const auto input_word = bounds.read_from(input_slicer); + bounds.write_into(output_stuffer, state_word ^ input_word); + return bounds.masked_assignment(state_word, input_word); + }); + + BOTAN_ASSERT_NOMSG(input_slicer.empty()); + BOTAN_ASSERT_NOMSG(output_stuffer.full()); +} + +void Ascon_p::finish(uint8_t rounds) { + // NIST SP.800-232, Section 2.1 (Algorithm 2 "pad()") + + // The padding is defined as: + // 1. The first padding bit is set to 1 + // 2. The remaining bits are set to 0 + constexpr std::array padding{0x01}; + + // We must always add a padded final input block, if the last verbatim + // input block aligned with the byte rate, the final block may be just + // padding bytes, otherwise the final block is padded as needed. + + absorb(std::span{padding}.first(byte_rate() - cursor()), rounds); + BOTAN_ASSERT_NOMSG(cursor() == 0); +} + +void Ascon_p::permute(uint8_t rounds) { + BOTAN_DEBUG_ASSERT(rounds <= 16); + + auto& S = state(); + + // NIST SP.800-232, Table 5 + constexpr std::array round_constants = { + 0x3c, 0x2d, 0x1e, 0x0f, 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b}; + + for(uint8_t i = 0; i < rounds; ++i) { + // Constant addition layer p_C + // NIST SP.800-232, Section 3.2 + S[2] ^= round_constants[16 - rounds + i]; + + // Substitution layer p_S + // NIST SP.800-232, Section 3.3, most notably Figure 3 + S[0] ^= S[4]; + S[4] ^= S[3]; + S[2] ^= S[1]; + auto tmp = S; + tmp[0] = ~tmp[0] & S[1]; + tmp[1] = ~tmp[1] & S[2]; + tmp[2] = ~tmp[2] & S[3]; + tmp[3] = ~tmp[3] & S[4]; + tmp[4] = ~tmp[4] & S[0]; + S[0] ^= tmp[1]; + S[1] ^= tmp[2]; + S[2] ^= tmp[3]; + S[3] ^= tmp[4]; + S[4] ^= tmp[0]; + S[1] ^= S[0]; + S[0] ^= S[4]; + S[3] ^= S[2]; + S[2] = ~S[2]; + + // Linear diffusion layer p_L + // NIST SP.800-232, Section 3.4 + S[0] = S[0] ^ rotr<19>(S[0]) ^ rotr<28>(S[0]); + S[1] = S[1] ^ rotr<61>(S[1]) ^ rotr<39>(S[1]); + S[2] = S[2] ^ rotr<1>(S[2]) ^ rotr<6>(S[2]); + S[3] = S[3] ^ rotr<10>(S[3]) ^ rotr<17>(S[3]); + S[4] = S[4] ^ rotr<7>(S[4]) ^ rotr<41>(S[4]); + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/permutations/ascon_perm/ascon_perm.h botan3-3.12.0+dfsg/src/lib/permutations/ascon_perm/ascon_perm.h --- botan3-3.7.1+dfsg/src/lib/permutations/ascon_perm/ascon_perm.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/permutations/ascon_perm/ascon_perm.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,74 @@ +/* +* Permutation Ascon_p[rounds] as specified in NIST SP.800-232, Section 3 +* (C) 2025 Jack Lloyd +* 2025 René Meusel +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_ASCON_PERM_H_ +#define BOTAN_ASCON_PERM_H_ + +#include +#include +#include +#include + +namespace Botan { + +/** + * Ascon_p as specified in NIST SP.800-232, Section 3 + */ +class Ascon_p final : public Sponge<5, uint64_t> { + public: + struct Config { + uint8_t init_and_final_rounds; + uint8_t processing_rounds; + uint8_t bit_rate; + state_t initial_state; + }; + + public: + consteval explicit Ascon_p(Config config) : + Sponge({config.bit_rate, config.initial_state}), + m_init_final_rounds(config.init_and_final_rounds), + m_processing_rounds(config.processing_rounds) { + BOTAN_ARG_CHECK(m_init_final_rounds > 0 && m_init_final_rounds <= 16, + "Invalid Ascon initialization/finalization rounds"); + + BOTAN_ARG_CHECK(m_processing_rounds > 0 && m_processing_rounds <= 16, "Invalid Ascon processing rounds"); + } + + std::string provider() const { return "base"; } + + void absorb(std::span input, std::optional permutation_rounds = std::nullopt); + void squeeze(std::span output); + void percolate_in(std::span data); + void percolate_out(std::span data); + + void finish() { finish(m_init_final_rounds); } + + void intermediate_finish() { finish(m_processing_rounds); } + + void permute() { permute(m_processing_rounds); } + + void initial_permute() { permute(m_init_final_rounds); } + + template + requires(offset + count <= state_bytes()) + constexpr auto range_of_state() { + return std::span{state()}.template subspan(); + } + + private: + void finish(uint8_t rounds); + void permute(uint8_t rounds); + + private: + uint8_t m_init_final_rounds; + uint8_t m_processing_rounds; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/permutations/ascon_perm/info.txt botan3-3.12.0+dfsg/src/lib/permutations/ascon_perm/info.txt --- botan3-3.7.1+dfsg/src/lib/permutations/ascon_perm/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/permutations/ascon_perm/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,8 @@ + +name -> "Ascon-permutation" +type -> "Internal" + + + +sponge + diff -Nru botan3-3.7.1+dfsg/src/lib/permutations/keccak_perm/info.txt botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/info.txt --- botan3-3.7.1+dfsg/src/lib/permutations/keccak_perm/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,7 +1,11 @@ - + KECCAK_PERM -> 20230613 - + name -> "Keccak-permutation" + + +sponge + diff -Nru botan3-3.7.1+dfsg/src/lib/permutations/keccak_perm/keccak_helpers.cpp botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_helpers.cpp --- botan3-3.7.1+dfsg/src/lib/permutations/keccak_perm/keccak_helpers.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_helpers.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -24,14 +24,15 @@ uint8_t encode(std::span out, uint64_t x) { const auto bytes_needed = int_encoding_size(x); + BOTAN_ASSERT_NOMSG(sizeof(x) >= bytes_needed); BOTAN_ASSERT_NOMSG(out.size() >= bytes_needed); - std::array bigendian_x; + const size_t leading_zeros = sizeof(x) - bytes_needed; + + std::array bigendian_x{}; store_be(x, bigendian_x.data()); - auto begin = bigendian_x.begin(); - std::advance(begin, sizeof(x) - bytes_needed); - std::copy(begin, bigendian_x.end(), out.begin()); + std::copy(bigendian_x.begin() + leading_zeros, bigendian_x.end(), out.begin()); return static_cast(bytes_needed); } diff -Nru botan3-3.7.1+dfsg/src/lib/permutations/keccak_perm/keccak_helpers.h botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_helpers.h --- botan3-3.7.1+dfsg/src/lib/permutations/keccak_perm/keccak_helpers.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_helpers.h 2026-05-07 01:38:28.000000000 +0000 @@ -78,7 +78,7 @@ /** * This is a combination of the functions encode_string() and bytepad() defined * in NIST SP.800-185 Section 2.3. Additionally, the result is directly streamed - * into the provided XOF to avoid unneccessary memory allocation or a byte vector. + * into the provided XOF to avoid unnecessary memory allocation or a byte vector. * * @param sink the XOF or byte vector to absorb the @p byte_strings into * @param padding_mod the modulus value to create a padding for (NIST calls this 'w') @@ -92,7 +92,7 @@ BOTAN_ASSERT_NOMSG(padding_mod > 0); // used as temporary storage for all integer encodings in this function - std::array int_encoding_buffer; + std::array int_encoding_buffer{}; // absorbs byte strings and counts the number of absorbed bytes size_t bytes_absorbed = 0; diff -Nru botan3-3.7.1+dfsg/src/lib/permutations/keccak_perm/keccak_perm.cpp botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_perm.cpp --- botan3-3.7.1+dfsg/src/lib/permutations/keccak_perm/keccak_perm.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_perm.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -2,37 +2,32 @@ * Keccak Permutation * (C) 2010,2016 Jack Lloyd * (C) 2023 Falko Strenzke -* (C) 2023 René Meusel - Rohde & Schwarz Cybersecurity +* (C) 2023,2025 René Meusel - Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ #include -#include -#include -#include #include -#include -#include +#include -namespace Botan { +#if defined(BOTAN_HAS_CPUID) + #include +#endif -Keccak_Permutation::Keccak_Permutation(size_t capacity, uint64_t custom_padding, uint8_t custom_padding_bit_len) : - m_capacity(capacity), - m_byterate((1600 - capacity) / 8), - m_custom_padding(custom_padding), - m_custom_padding_bit_len(custom_padding_bit_len), - m_S(25), // 1600 bit - m_S_inpos(0), - m_S_outpos(0) { - BOTAN_ARG_CHECK(capacity % 64 == 0, "capacity must be a multiple of 64"); -} +namespace Botan { std::string Keccak_Permutation::provider() const { +#if defined(BOTAN_HAS_KECCAK_PERM_AVX512) + if(auto feat = CPUID::check(CPUID::Feature::AVX512)) { + return *feat; + } +#endif + #if defined(BOTAN_HAS_KECCAK_PERM_BMI2) - if(CPUID::has_bmi2()) { - return "bmi2"; + if(auto feat = CPUID::check(CPUID::Feature::BMI)) { + return *feat; } #endif @@ -40,89 +35,61 @@ } void Keccak_Permutation::clear() { - zeroise(m_S); - m_S_inpos = 0; - m_S_outpos = 0; + state() = {}; + reset_cursor(); } void Keccak_Permutation::absorb(std::span input) { - BufferSlicer input_slicer(input); - - // Block-wise incorporation of the input data into the sponge state until - // all input bytes are processed - while(!input_slicer.empty()) { - const size_t to_take_this_round = std::min(input_slicer.remaining(), m_byterate - m_S_inpos); - BufferSlicer input_this_round(input_slicer.take(to_take_this_round)); - - // If necessary, try to get aligned with the sponge state's 64-bit integer array - for(; !input_this_round.empty() && m_S_inpos % 8; ++m_S_inpos) { - m_S[m_S_inpos / 8] ^= static_cast(input_this_round.take_byte()) << (8 * (m_S_inpos % 8)); - } - - // Process as many aligned 64-bit integer values as possible - for(; input_this_round.remaining() >= 8; m_S_inpos += 8) { - m_S[m_S_inpos / 8] ^= load_le(input_this_round.take(8).data(), 0); - } - - // Read remaining output data, causing misalignment, if necessary - for(; !input_this_round.empty(); ++m_S_inpos) { - m_S[m_S_inpos / 8] ^= static_cast(input_this_round.take_byte()) << (8 * (m_S_inpos % 8)); - } - - // We reached the end of a sponge state block... permute() and start over - if(m_S_inpos == m_byterate) { - permute(); - m_S_inpos = 0; - } - } + absorb_into_sponge(*this, input); } void Keccak_Permutation::squeeze(std::span output) { - BufferStuffer output_stuffer(output); - - // Block-wise readout of the sponge state until enough bytes - // were filled into the output buffer - while(!output_stuffer.full()) { - const size_t bytes_in_this_round = std::min(output_stuffer.remaining_capacity(), m_byterate - m_S_outpos); - BufferStuffer output_this_round(output_stuffer.next(bytes_in_this_round)); - - // If necessary, try to get aligned with the sponge state's 64-bit integer array - for(; !output_this_round.full() && m_S_outpos % 8 != 0; ++m_S_outpos) { - output_this_round.next_byte() = static_cast(m_S[m_S_outpos / 8] >> (8 * (m_S_outpos % 8))); - } - - // Read out as many aligned 64-bit integer values as possible - for(; output_this_round.remaining_capacity() >= 8; m_S_outpos += 8) { - store_le(m_S[m_S_outpos / 8], output_this_round.next(8).data()); - } - - // Read remaining output data, causing misalignment, if necessary - for(; !output_this_round.full(); ++m_S_outpos) { - output_this_round.next_byte() = static_cast(m_S[m_S_outpos / 8] >> (8 * (m_S_outpos % 8))); - } - - // We reached the end of a sponge state block... permute() and start over - if(m_S_outpos == m_byterate) { - permute(); - m_S_outpos = 0; - } - } + squeeze_from_sponge(*this, output); } void Keccak_Permutation::finish() { - // append the first bit of the final padding after the custom padding - uint8_t init_pad = static_cast(m_custom_padding | uint64_t(1) << m_custom_padding_bit_len); - m_S[m_S_inpos / 8] ^= static_cast(init_pad) << (8 * (m_S_inpos % 8)); + // The padding for Keccak[c]-based functions spans the entire remaining + // byterate until the next permute() call. At most that could be an entire + // byterate. First are a few bits of "custom" padding defined by the using + // function (e.g. SHA-3 uses "01"), then the remaining space is filled with + // "pad10*1" (see NIST FIPS 202 Section 5.1) followed by a final permute(). + + auto& S = state(); + + // Apply the custom padding + the left-most 1-bit of "pad10*1" to the current + // (partial) word of the sponge state + + const uint64_t start_of_padding = (m_padding.padding | uint64_t(1) << m_padding.bit_len); + S[cursor() / word_bytes] ^= start_of_padding << (8 * (cursor() % word_bytes)); + + // XOR'ing the 0-bits of "pad10*1" into the state is a NOOP - // final bit of the padding of the last block - m_S[(m_byterate / 8) - 1] ^= static_cast(0x80) << 56; + // If the custom padding + the left-most 1-bit of "pad10*1" had resulted in a + // byte-aligned "partial padding", the final 1-bit of of "pad10*1" could + // potentially override parts of the already-appended "start_of_padding". + // In case we ever introduce a Keccak-based function with such a need, we + // have to modify this padding algorithm. + BOTAN_DEBUG_ASSERT(m_padding.bit_len % 8 != 7); + // Append the final bit of "pad10*1" into the last word of the input range + S[(byte_rate() / word_bytes) - 1] ^= uint64_t(0x8000000000000000); + + // Perform the final permutation and reset the state cursor permute(); + reset_cursor(); + + BOTAN_DEBUG_ASSERT(cursor() == 0); } void Keccak_Permutation::permute() { +#if defined(BOTAN_HAS_KECCAK_PERM_AVX512) + if(CPUID::has(CPUID::Feature::AVX512)) { + return permute_avx512(); + } +#endif + #if defined(BOTAN_HAS_KECCAK_PERM_BMI2) - if(CPUID::has_bmi2()) { + if(CPUID::has(CPUID::Feature::BMI)) { return permute_bmi2(); } #endif @@ -137,8 +104,8 @@ uint64_t T[25]; for(size_t i = 0; i != 24; i += 2) { - Keccak_Permutation_round(T, m_S.data(), RC[i + 0]); - Keccak_Permutation_round(m_S.data(), T, RC[i + 1]); + Keccak_Permutation_round(T, state().data(), RC[i + 0]); + Keccak_Permutation_round(state().data(), T, RC[i + 1]); } } diff -Nru botan3-3.7.1+dfsg/src/lib/permutations/keccak_perm/keccak_perm.h botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_perm.h --- botan3-3.7.1+dfsg/src/lib/permutations/keccak_perm/keccak_perm.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_perm.h 2026-05-07 01:38:28.000000000 +0000 @@ -2,7 +2,7 @@ * Keccak Permutation * (C) 2010,2016 Jack Lloyd * (C) 2023 Falko Strenzke -* (C) 2023 René Meusel - Rohde & Schwarz Cybersecurity +* (C) 2023,2025 René Meusel - Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -10,12 +10,29 @@ #ifndef BOTAN_KECCAK_PERM_H_ #define BOTAN_KECCAK_PERM_H_ -#include +#include #include #include namespace Botan { +struct KeccakPadding { + uint64_t padding; /// The padding bits in little-endian order + uint8_t bit_len; /// The number of relevant bits in 'padding' + + /// NIST FIPS 202 Section 6.1 + static constexpr KeccakPadding sha3() { return {.padding = 0b10 /* little-endian */, .bit_len = 2}; } + + /// NIST FIPS 202 Section 6.2 + static constexpr KeccakPadding shake() { return {.padding = 0b1111, .bit_len = 4}; } + + /// NIST SP.800-185 Section 3.3 + static constexpr KeccakPadding cshake() { return {.padding = 0b00, .bit_len = 2}; } + + /// Keccak submission, prior to the introduction of an algorithm specific padding + static constexpr KeccakPadding keccak1600() { return {.padding = 0, .bit_len = 0}; } +}; + /** * KECCAK FIPS * @@ -35,24 +52,21 @@ * https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf#page=28 * [2] https://csrc.nist.gov/projects/hash-functions/sha-3-project */ -class Keccak_Permutation final { +class Keccak_Permutation final : public Sponge<25, uint64_t> { + public: + struct Config { + size_t capacity_bits; + KeccakPadding padding; + }; + public: /** * @brief Instantiate a Keccak permutation * - * The @p custom_padding is assumed to be init_pad || 00... || fini_pad - * - * @param capacity_bits Keccak capacity - * @param custom_padding the custom bit padding that is to be appended on the call to finish - * @param custom_padding_bit_len the bit length of the custom_padd + * @param config Keccak parameter configuration */ - Keccak_Permutation(size_t capacity_bits, uint64_t custom_padding, uint8_t custom_padding_bit_len); - - size_t capacity() const { return m_capacity; } - - size_t bit_rate() const { return m_byterate * 8; } - - size_t byte_rate() const { return m_byterate; } + constexpr explicit Keccak_Permutation(Config config) : + Sponge({.bit_rate = state_bits() - config.capacity_bits, .initial_state = {}}), m_padding(config.padding) {} void clear(); std::string provider() const; @@ -80,21 +94,22 @@ */ void finish(); - private: + /** + * The Keccak permutation function + */ void permute(); + private: #if defined(BOTAN_HAS_KECCAK_PERM_BMI2) void permute_bmi2(); #endif +#if defined(BOTAN_HAS_KECCAK_PERM_AVX512) + void permute_avx512(); +#endif + private: - const size_t m_capacity; - const size_t m_byterate; - const uint64_t m_custom_padding; - const uint8_t m_custom_padding_bit_len; - secure_vector m_S; - uint8_t m_S_inpos; - uint8_t m_S_outpos; + KeccakPadding m_padding; }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/permutations/keccak_perm/keccak_perm_avx512/info.txt botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_perm_avx512/info.txt --- botan3-3.7.1+dfsg/src/lib/permutations/keccak_perm/keccak_perm_avx512/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_perm_avx512/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,15 @@ + +KECCAK_PERM_AVX512 -> 20250524 + + + +name -> "Keccak permutation using AVX512" + + + +avx512 + + + +cpuid + diff -Nru botan3-3.7.1+dfsg/src/lib/permutations/keccak_perm/keccak_perm_avx512/keccak_perm_avx512.cpp botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_perm_avx512/keccak_perm_avx512.cpp --- botan3-3.7.1+dfsg/src/lib/permutations/keccak_perm/keccak_perm_avx512/keccak_perm_avx512.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_perm_avx512/keccak_perm_avx512.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,153 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan { + +namespace { + +class SIMD_5x64 final { + public: + explicit BOTAN_FN_ISA_AVX512 SIMD_5x64() : SIMD_5x64(_mm512_setzero_si512()) {} + + static BOTAN_FN_ISA_AVX512 SIMD_5x64 rc(uint64_t RC) { + return SIMD_5x64(_mm512_maskz_set1_epi64(0b00000001, RC)); + } + + static BOTAN_FN_ISA_AVX512 SIMD_5x64 load(const uint64_t v[5]) { + return SIMD_5x64(_mm512_maskz_loadu_epi64(0b00011111, v)); + } + + template + inline BOTAN_FN_ISA_AVX512 SIMD_5x64 permute() const { + static_assert(I0 < 5 && I1 < 5 && I2 < 5 && I3 < 5 && I4 < 5); + const __m512i tbl = _mm512_setr_epi64(I0, I1, I2, I3, I4, 0, 0, 0); + return SIMD_5x64(_mm512_permutexvar_epi64(tbl, m_v)); + } + + static BOTAN_FN_ISA_AVX512 void transpose5( + SIMD_5x64& i0, SIMD_5x64& i1, SIMD_5x64& i2, SIMD_5x64& i3, SIMD_5x64& i4) { + // 5x5 u64 transpose using 7 permutex2var, 4 unpack, 1 blend, 5 constants + + const auto lo_01 = _mm512_unpacklo_epi64(i0.m_v, i1.m_v); + const auto lo_23 = _mm512_unpacklo_epi64(i2.m_v, i3.m_v); + + const auto hi_01 = _mm512_unpackhi_epi64(i0.m_v, i1.m_v); + const auto hi_23 = _mm512_unpackhi_epi64(i2.m_v, i3.m_v); + + // Insert the relevant words from i4 into the i0/i1 data + const auto i4_lo_idx = _mm512_setr_epi64(0, 1, 2, 3, 4, 5, 8, 10); + const auto i4_hi_idx = _mm512_setr_epi64(0, 1, 2, 3, -1, -1, 9, 11); + + auto t0 = _mm512_permutex2var_epi64(lo_01, i4_lo_idx, i4.m_v); + auto t2 = _mm512_permutex2var_epi64(hi_01, i4_hi_idx, i4.m_v); + + // Now merge the 0/1/4 and 2/3 vectors using permutes + const auto idx0 = _mm512_setr_epi64(0, 1, 8, 9, 6, -1, -1, -1); + const auto idx1 = _mm512_setr_epi64(2, 3, 10, 11, 7, -1, -1, -1); + const auto idx4 = _mm512_setr_epi64(4, 5, 12, 13, -1, -1, -1, -1); + + i0.m_v = _mm512_permutex2var_epi64(t0, idx0, lo_23); + i1.m_v = _mm512_permutex2var_epi64(t2, idx0, hi_23); + i2.m_v = _mm512_permutex2var_epi64(t0, idx1, lo_23); + i3.m_v = _mm512_permutex2var_epi64(t2, idx1, hi_23); + i4.m_v = _mm512_mask_blend_epi64(0b00010000, _mm512_permutex2var_epi64(t0, idx4, lo_23), i4.m_v); + } + + static BOTAN_FN_ISA_AVX512 SIMD_5x64 chi(const SIMD_5x64& x, const SIMD_5x64& y, const SIMD_5x64& z) { + constexpr uint8_t xor_not_and = 0b11010010; // (x ^ (~y & z)) + return SIMD_5x64(_mm512_ternarylogic_epi64(x.m_v, y.m_v, z.m_v, xor_not_and)); + } + + friend BOTAN_FN_ISA_AVX512 SIMD_5x64 operator^(const SIMD_5x64& x, const SIMD_5x64& y) { + return SIMD_5x64(_mm512_xor_epi64(x.m_v, y.m_v)); + } + + static BOTAN_FN_ISA_AVX512 SIMD_5x64 + xor5(const SIMD_5x64& i0, const SIMD_5x64& i1, const SIMD_5x64& i2, const SIMD_5x64& i3, const SIMD_5x64& i4) { + constexpr uint8_t tern_xor = 0b10010110; + auto t = _mm512_ternarylogic_epi64(i0.m_v, i1.m_v, i2.m_v, tern_xor); + return SIMD_5x64(_mm512_ternarylogic_epi64(i3.m_v, i4.m_v, t, tern_xor)); + } + + BOTAN_FN_ISA_AVX512 SIMD_5x64 rol1() const { return SIMD_5x64(_mm512_rol_epi64(m_v, 1)); } + + template + BOTAN_FN_ISA_AVX512 SIMD_5x64 rolv() const { + static_assert(R0 < 64 && R1 < 64 && R2 < 64 && R3 < 64 && R4 < 64); + const __m512i rot = _mm512_setr_epi64(R0, R1, R2, R3, R4, 0, 0, 0); + return SIMD_5x64(_mm512_rolv_epi64(m_v, rot)); + } + + BOTAN_FN_ISA_AVX512 void store(uint64_t v[5]) const { _mm512_mask_storeu_epi64(v, 0b00011111, m_v); } + + private: + explicit BOTAN_FN_ISA_AVX512 SIMD_5x64(__m512i v) : m_v(v) {} + + __m512i m_v; +}; + +inline void BOTAN_FN_ISA_AVX512 Keccak_Permutation_round_avx512(SIMD_5x64 A[5], uint64_t RC) { + const auto C = SIMD_5x64::xor5(A[0], A[1], A[2], A[3], A[4]); + + const auto D = C.permute<4, 0, 1, 2, 3>() ^ C.permute<1, 2, 3, 4, 0>().rol1(); + + const auto B0 = (A[0] ^ D).permute<0, 3, 1, 4, 2>().rolv<0, 28, 1, 27, 62>(); + const auto B1 = (A[1] ^ D).permute<1, 4, 2, 0, 3>().rolv<44, 20, 6, 36, 55>(); + const auto B2 = (A[2] ^ D).permute<2, 0, 3, 1, 4>().rolv<43, 3, 25, 10, 39>(); + const auto B3 = (A[3] ^ D).permute<3, 1, 4, 2, 0>().rolv<21, 45, 8, 15, 41>(); + const auto B4 = (A[4] ^ D).permute<4, 2, 0, 3, 1>().rolv<14, 61, 18, 56, 2>(); + + auto T0 = SIMD_5x64::chi(B0, B1, B2) ^ SIMD_5x64::rc(RC); + auto T1 = SIMD_5x64::chi(B1, B2, B3); + auto T2 = SIMD_5x64::chi(B2, B3, B4); + auto T3 = SIMD_5x64::chi(B3, B4, B0); + auto T4 = SIMD_5x64::chi(B4, B0, B1); + + SIMD_5x64::transpose5(T0, T1, T2, T3, T4); + + A[0] = T0; + A[1] = T1; + A[2] = T2; + A[3] = T3; + A[4] = T4; +} + +} // namespace + +void BOTAN_FN_ISA_AVX512 Keccak_Permutation::permute_avx512() { + static const uint64_t RC[24] = {0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000, + 0x000000000000808B, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009, + 0x000000000000008A, 0x0000000000000088, 0x0000000080008009, 0x000000008000000A, + 0x000000008000808B, 0x800000000000008B, 0x8000000000008089, 0x8000000000008003, + 0x8000000000008002, 0x8000000000000080, 0x000000000000800A, 0x800000008000000A, + 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008}; + + auto& S = state(); + + std::array X{ + SIMD_5x64::load(&S[0]), // NOLINT(*container-data-pointer) + SIMD_5x64::load(&S[5]), + SIMD_5x64::load(&S[10]), + SIMD_5x64::load(&S[15]), + SIMD_5x64::load(&S[20]), + }; + + // NOLINTNEXTLINE(modernize-loop-convert) + for(size_t i = 0; i != 24; ++i) { + Keccak_Permutation_round_avx512(X.data(), RC[i]); + } + + for(size_t i = 0; i != 5; ++i) { + X[i].store(&S[5 * i]); + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/permutations/keccak_perm/keccak_perm_bmi2/info.txt botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_perm_bmi2/info.txt --- botan3-3.7.1+dfsg/src/lib/permutations/keccak_perm/keccak_perm_bmi2/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_perm_bmi2/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + KECCAK_PERM_BMI2 -> 20230612 - + name -> "KECCAK-permutation BMI2" @@ -15,3 +15,13 @@ x86_64 + + +cpuid + + +# It doesn't make sense to use this on MSVC since it doesn't +# have any way of enabling BMI2 codegen + +!msvc + diff -Nru botan3-3.7.1+dfsg/src/lib/permutations/keccak_perm/keccak_perm_bmi2/keccak_perm_bmi2.cpp botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_perm_bmi2/keccak_perm_bmi2.cpp --- botan3-3.7.1+dfsg/src/lib/permutations/keccak_perm/keccak_perm_bmi2/keccak_perm_bmi2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_perm_bmi2/keccak_perm_bmi2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,11 +7,12 @@ #include +#include #include namespace Botan { -void Keccak_Permutation::permute_bmi2() { +void BOTAN_FN_ISA_BMI2 Keccak_Permutation::permute_bmi2() { static const uint64_t RC[24] = {0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000, 0x000000000000808B, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009, 0x000000000000008A, 0x0000000000000088, 0x0000000080008009, 0x000000008000000A, @@ -22,8 +23,8 @@ uint64_t T[25]; for(size_t i = 0; i != 24; i += 2) { - Keccak_Permutation_round(T, m_S.data(), RC[i + 0]); - Keccak_Permutation_round(m_S.data(), T, RC[i + 1]); + Keccak_Permutation_round(T, state().data(), RC[i + 0]); + Keccak_Permutation_round(state().data(), T, RC[i + 1]); } } diff -Nru botan3-3.7.1+dfsg/src/lib/permutations/keccak_perm/keccak_perm_round.h botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_perm_round.h --- botan3-3.7.1+dfsg/src/lib/permutations/keccak_perm/keccak_perm_round.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/permutations/keccak_perm/keccak_perm_round.h 2026-05-07 01:38:28.000000000 +0000 @@ -12,7 +12,7 @@ namespace Botan { -inline void Keccak_Permutation_round(uint64_t T[25], const uint64_t A[25], uint64_t RC) { +BOTAN_FORCE_INLINE void Keccak_Permutation_round(uint64_t T[25], const uint64_t A[25], uint64_t RC) { const uint64_t C0 = A[0] ^ A[5] ^ A[10] ^ A[15] ^ A[20]; const uint64_t C1 = A[1] ^ A[6] ^ A[11] ^ A[16] ^ A[21]; const uint64_t C2 = A[2] ^ A[7] ^ A[12] ^ A[17] ^ A[22]; diff -Nru botan3-3.7.1+dfsg/src/lib/permutations/sponge/info.txt botan3-3.12.0+dfsg/src/lib/permutations/sponge/info.txt --- botan3-3.7.1+dfsg/src/lib/permutations/sponge/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/permutations/sponge/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,4 @@ + +name -> "Sponge Helper" +type -> "Internal" + diff -Nru botan3-3.7.1+dfsg/src/lib/permutations/sponge/sponge.h botan3-3.12.0+dfsg/src/lib/permutations/sponge/sponge.h --- botan3-3.7.1+dfsg/src/lib/permutations/sponge/sponge.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/permutations/sponge/sponge.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,72 @@ +/* +* Base helper class for implementing sponge constructions like Keccak or Ascon +* (C) 2025 Jack Lloyd +* 2025 René Meusel +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SPONGE_CONSTRUCTION_H_ +#define BOTAN_SPONGE_CONSTRUCTION_H_ + +#include +#include +#include + +namespace Botan { + +/** + * A generic sponge construction with a fixed state size defined in terms of + * "words" of an unsigned integral type. + * + * This is meant to be used as a base class for specific sponge constructions + * like Keccak or Ascon. + */ +template +class Sponge { + public: + using word_t = word; + using state_t = std::array; + constexpr static size_t word_bytes = sizeof(word); + constexpr static size_t word_bits = word_bytes * 8; + + struct Config final { + size_t bit_rate; /// The number of bits that using algorithms can modify between permutations + state_t initial_state; /// The state of the sponge state at initialization + }; + + public: + constexpr explicit Sponge(Config config) : m_S(config.initial_state), m_S_cursor(0), m_bit_rate(config.bit_rate) { + BOTAN_ARG_CHECK(m_bit_rate % word_bits == 0 && m_bit_rate < words * word_bits, "Invalid sponge bit rate"); + } + + constexpr static size_t state_bytes() { return sizeof(state_t); } + + constexpr static size_t state_bits() { return state_bytes() * 8; } + + constexpr size_t bit_rate() const { return m_bit_rate; } + + constexpr size_t byte_rate() const { return m_bit_rate / 8; } + + constexpr size_t bit_capacity() const { return state_bits() - bit_rate(); } + + constexpr size_t byte_capacity() const { return state_bytes() - byte_rate(); } + + constexpr auto& state() { return m_S; } + + size_t cursor() const { return m_S_cursor; } + + size_t& _cursor() { return m_S_cursor; } + + protected: + void reset_cursor() { m_S_cursor = 0; } + + private: + state_t m_S; + size_t m_S_cursor; + size_t m_bit_rate; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/permutations/sponge/sponge_processing.h botan3-3.12.0+dfsg/src/lib/permutations/sponge/sponge_processing.h --- botan3-3.7.1+dfsg/src/lib/permutations/sponge/sponge_processing.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/permutations/sponge/sponge_processing.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,264 @@ +/* +* Byte-oriented Sponge processing helpers +* (C) 2025 Jack Lloyd +* 2025 René Meusel +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SPONGE_PROCESSING_H_ +#define BOTAN_SPONGE_PROCESSING_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace Botan { + +namespace detail { + +template +concept SpongeLike = std::unsigned_integral && requires(T a) { + typename T::word_t; + typename T::state_t; + { a.state() } -> std::same_as; + { a._cursor() } -> std::same_as; + { a.byte_rate() } -> std::same_as; +}; + +template +concept SpongeLikeWithTrivialPermute = SpongeLike && requires(T a) { + { a.permute() } -> std::same_as; +}; + +/** +* Represents the bounds of partial byte-oriented data within a word of +* the sponge state. Downstream algorithms can use this to conveniently +* modify the passed in partial state word with data written or read +* from an input or output byte buffer. +*/ +template +class PartialWordBounds final { + public: + size_t offset; // NOLINT(*-non-private-member-*) + size_t length; // NOLINT(*-non-private-member-*) + + private: + using word_t = typename SpongeT::word_t; + constexpr static auto word_bytes = SpongeT::word_bytes; + + public: + /** + * Reads '.length' bytes from the provided slicer and places them + * within a word at the specified '.offset' in little-endian order. + */ + word_t read_from(BufferSlicer& slicer) const { + std::array partial_word_bytes{}; + slicer.copy_into(std::span{partial_word_bytes}.subspan(offset, length)); + return load_le(partial_word_bytes); + } + + /** + * Writes '.length' bytes from the provided word at the specified + * '.offset' into the provided stuffer in little-endian order. + */ + void write_into(BufferStuffer& stuffer, word_t partial_word) const { + const auto partial_word_bytes = store_le(partial_word); + stuffer.append(std::span{partial_word_bytes}.subspan(offset, length)); + } + + /** + * Assigns the bits in 'partial_input_word' to their corresponding + * bits in 'state_word' at the specified '.offset' and '.length' + * while leaving all other bits in 'state_word' unchanged. + */ + word_t masked_assignment(word_t state_word, word_t partial_input_word) const { + BOTAN_DEBUG_ASSERT(length > 0); + const auto mask = ((word_t(0) - 1) >> ((word_bytes - length) * 8)) << (offset * 8); + return (state_word & ~mask) | (partial_input_word & mask); + } +}; + +/** +* A drop-in replacement for `PartialWordBounds` that is optimized for +* handling full words where no masking or offsetting is necessary. +*/ +template +class FullWordBounds final { + private: + using word_t = typename SpongeT::word_t; + constexpr static auto word_bytes = SpongeT::word_bytes; + + public: + word_t read_from(BufferSlicer& slicer) const { return load_le(slicer.take()); } + + void write_into(BufferStuffer& stuffer, word_t full_word) const { stuffer.append(store_le(full_word)); } + + word_t masked_assignment(word_t /*unused*/, word_t full_input_word) const { return full_input_word; } +}; + +template +concept PermutationFn = std::invocable || std::same_as; + +template +concept BaseModifierFn = requires(T fn, typename SpongeT::word_t word, ModifierT bounds) { + { std::invoke(fn, word, bounds) } -> std::same_as; +}; + +template +concept ModifierFn = + BaseModifierFn> || BaseModifierFn>; + +} // namespace detail + +/** +* Performs the core processing loop for ingesting or extracting data into/from +* the sponge state in a byte-oriented manner for the given number of +* @p bytes_to_process. The provided @p word_modifier_fn is called for each +* (partial) word of the sponge state that needs to be modified or read. +* +* The processing loop ensures efficient handling of unaligned input and output +* data. For that, it calls the provided permutation function either with an +* instance of `PartialWordBounds` or `FullWordBounds`. Hence @p word_modifier_fn +* must be able to handle both types of bounds and should use their respective +* methods to read from or write into input or output buffers. +* +* @param sponge the sponge instance to process data into or from +* @param bytes_to_process the number of sponge state bytes to traverse +* @param permutation_fn a function that performs the sponge's permutation +* @param modifier_fn a function that modifies the sponge state words +*/ +template +BOTAN_FORCE_INLINE void process_bytes_in_sponge(SpongeT& sponge, + size_t bytes_to_process, + const detail::PermutationFn auto& permutation_fn, + const detail::ModifierFn auto& modifier_fn) { + if(bytes_to_process == 0) { + return; + } + + constexpr auto word_bytes = SpongeT::word_bytes; + const auto byte_rate = sponge.byte_rate(); + auto& S = sponge.state(); + auto& cursor = sponge._cursor(); + + // If necessary, try to get aligned with the sponge state's words array + const auto bytes_out_of_word_alignment = static_cast(cursor % word_bytes); + if(bytes_out_of_word_alignment > 0) { + const auto bytes_until_word_alignment = word_bytes - bytes_out_of_word_alignment; + const auto bytes_from_input = std::min(bytes_to_process, bytes_until_word_alignment); + BOTAN_DEBUG_ASSERT(bytes_from_input < word_bytes); + + S[cursor / word_bytes] = modifier_fn(S[cursor / word_bytes], + detail::PartialWordBounds{ + .offset = bytes_out_of_word_alignment, + .length = bytes_from_input, + }); + cursor += bytes_from_input; + bytes_to_process -= bytes_from_input; + + if(cursor == byte_rate) { + permutation_fn(); + cursor = 0; + } + } + + // If we didn't exhaust the bytes to process for this invocation, we should + // be word-aligned with the sponge state now + BOTAN_DEBUG_ASSERT(bytes_to_process == 0 || cursor % word_bytes == 0); + + // Block-wise incorporation of the input data into the sponge state until + // all input bytes are processed + while(bytes_to_process >= word_bytes) { + // Process full words until we either run out of data or reach the + // end of the current sponge state block + while(bytes_to_process >= word_bytes && cursor < byte_rate) { + S[cursor / word_bytes] = modifier_fn(S[cursor / word_bytes], detail::FullWordBounds{}); + cursor += word_bytes; + bytes_to_process -= word_bytes; + } + + if(cursor == byte_rate) { + permutation_fn(); + cursor = 0; + } + } + + // Process the remaining bytes that don't fill an entire word. + // Therefore, leaving the sponge state in an unaligned state that won't + // need another permutation until the next call to process(). + BOTAN_DEBUG_ASSERT(bytes_to_process < word_bytes && cursor < byte_rate); + if(bytes_to_process > 0) { + S[cursor / word_bytes] = modifier_fn(S[cursor / word_bytes], + detail::PartialWordBounds{ + .offset = 0, + .length = bytes_to_process, + }); + cursor += bytes_to_process; + } +} + +template +inline void process_bytes_in_sponge(SpongeT& sponge, + size_t bytes_to_process, + const detail::ModifierFn auto& modifier_fn) { + process_bytes_in_sponge( + sponge, bytes_to_process, [&sponge] { sponge.permute(); }, modifier_fn); +} + +/** +* Absorbs @p input data into the @p sponge state. +* +* @param sponge The sponge state to absorb data into. +* @param input The input data to absorb. +* @param permutation_fn The function to call for the sponge's permutation. +*/ +template +inline void absorb_into_sponge(SpongeT& sponge, + std::span input, + const detail::PermutationFn auto& permutation_fn) { + using word_t = typename SpongeT::word_t; + + BufferSlicer input_slicer(input); + process_bytes_in_sponge(sponge, input.size(), permutation_fn, [&](word_t state_word, auto bounds) { + return state_word ^ bounds.read_from(input_slicer); + }); + BOTAN_ASSERT_NOMSG(input_slicer.empty()); +} + +inline void absorb_into_sponge(detail::SpongeLikeWithTrivialPermute auto& sponge, std::span input) { + absorb_into_sponge(sponge, input, [&sponge] { sponge.permute(); }); +} + +/** +* Squeezes @p output data from the @p sponge state. +* +* @param sponge The sponge state to squeeze data from. +* @param output The output buffer to write the squeezed data into. +* @param permutation_fn The function to call for the sponge's permutation. +*/ +template +inline void squeeze_from_sponge(SpongeT& sponge, + std::span output, + const detail::PermutationFn auto& permutation_fn) { + using word_t = typename SpongeT::word_t; + + BufferStuffer output_stuffer(output); + process_bytes_in_sponge(sponge, output.size(), permutation_fn, [&](word_t state_word, auto bounds) { + bounds.write_into(output_stuffer, state_word); + return state_word; + }); + BOTAN_ASSERT_NOMSG(output_stuffer.full()); +} + +inline void squeeze_from_sponge(detail::SpongeLikeWithTrivialPermute auto& sponge, std::span output) { + squeeze_from_sponge(sponge, output, [&sponge] { sponge.permute(); }); +} + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/eme.cpp botan3-3.12.0+dfsg/src/lib/pk_pad/eme.cpp --- botan3-3.7.1+dfsg/src/lib/pk_pad/eme.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/eme.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,71 +0,0 @@ -/* -* EME Base Class -* (C) 1999-2008 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include -#include - -#if defined(BOTAN_HAS_EME_OAEP) - #include -#endif - -#if defined(BOTAN_HAS_EME_PKCS1) - #include -#endif - -#if defined(BOTAN_HAS_EME_RAW) - #include -#endif - -namespace Botan { - -std::unique_ptr EME::create(std::string_view algo_spec) { -#if defined(BOTAN_HAS_EME_RAW) - if(algo_spec == "Raw") { - return std::make_unique(); - } -#endif - -#if defined(BOTAN_HAS_EME_PKCS1) - // TODO(Botan4) Remove all but "PKCS1v15" - if(algo_spec == "PKCS1v15" || algo_spec == "EME-PKCS1-v1_5") { - return std::make_unique(); - } -#endif - -#if defined(BOTAN_HAS_EME_OAEP) - SCAN_Name req(algo_spec); - - // TODO(Botan4) Remove all but "OAEP" - if(req.algo_name() == "OAEP" || req.algo_name() == "EME-OAEP" || req.algo_name() == "EME1") { - if(req.arg_count() == 1 || ((req.arg_count() == 2 || req.arg_count() == 3) && req.arg(1) == "MGF1")) { - if(auto hash = HashFunction::create(req.arg(0))) { - return std::make_unique(std::move(hash), req.arg(2, "")); - } - } else if(req.arg_count() == 2 || req.arg_count() == 3) { - auto mgf_params = parse_algorithm_name(req.arg(1)); - - if(mgf_params.size() == 2 && mgf_params[0] == "MGF1") { - auto hash = HashFunction::create(req.arg(0)); - auto mgf1_hash = HashFunction::create(mgf_params[1]); - - if(hash && mgf1_hash) { - return std::make_unique(std::move(hash), std::move(mgf1_hash), req.arg(2, "")); - } - } - } - } -#endif - - throw Algorithm_Not_Found(algo_spec); -} - -EME::~EME() = default; - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/eme.h botan3-3.12.0+dfsg/src/lib/pk_pad/eme.h --- botan3-3.7.1+dfsg/src/lib/pk_pad/eme.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/eme.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,67 +0,0 @@ -/* -* (C) 1999-2007,2024 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_PUBKEY_EME_H_ -#define BOTAN_PUBKEY_EME_H_ - -#include -#include -#include -#include -#include - -namespace Botan { - -class RandomNumberGenerator; - -/** -* Encoding Method for Encryption -*/ -class BOTAN_TEST_API EME { - public: - virtual ~EME(); - - /** - * Factory method for EME (message-encoding methods for encryption) objects - * @param algo_spec the name of the EME to create - * @return pointer to newly allocated object of that type - */ - static std::unique_ptr create(std::string_view algo_spec); - - /** - * Return the maximum input size in bytes we can support - * @param keybits the size of the key in bits - * @return upper bound of input in bytes - */ - virtual size_t maximum_input_size(size_t keybits) const = 0; - - /** - * Encode an input - * @param output buffer that is written to - * @param input the plaintext - * @param key_length length of the key in bits - * @param rng a random number generator - * @return number of bytes written to output - */ - virtual size_t pad(std::span output, - std::span input, - size_t key_length, - RandomNumberGenerator& rng) const = 0; - - /** - * Decode an input - * @param output buffer where output is placed - * @param input the encoded plaintext - * @return number of bytes written to output if valid, - * or an empty option if invalid. If an empty option is - * returned the contents of output are undefined - */ - virtual CT::Option unpad(std::span output, std::span input) const = 0; -}; - -} // namespace Botan - -#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/eme_oaep/info.txt botan3-3.12.0+dfsg/src/lib/pk_pad/eme_oaep/info.txt --- botan3-3.7.1+dfsg/src/lib/pk_pad/eme_oaep/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/eme_oaep/info.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ - -EME_OAEP -> 20180305 -OAEP -> 20250130 - - - -name -> "OAEP" - - - -mgf1 - diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/eme_oaep/oaep.cpp botan3-3.12.0+dfsg/src/lib/pk_pad/eme_oaep/oaep.cpp --- botan3-3.7.1+dfsg/src/lib/pk_pad/eme_oaep/oaep.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/eme_oaep/oaep.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,159 +0,0 @@ -/* -* OAEP -* (C) 1999-2010,2015,2018,2024 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include -#include -#include -#include - -namespace Botan { - -/* -* OAEP Pad Operation -*/ -size_t OAEP::pad(std::span output, - std::span input, - size_t key_length, - RandomNumberGenerator& rng) const { - key_length /= 8; - - if(input.size() > maximum_input_size(key_length * 8)) { - throw Invalid_Argument("OAEP: Input is too large"); - } - - const size_t output_size = key_length; - - output = output.first(output_size); // remainder ignored - - BufferStuffer stuffer(output); - - // We always use a seed len equal to the underlying hash - rng.randomize(stuffer.next(m_Phash.size())); - stuffer.append(m_Phash); - stuffer.append(0x00, stuffer.remaining_capacity() - (1 + input.size())); - stuffer.append(0x01); - stuffer.append(input); - BOTAN_ASSERT_NOMSG(stuffer.full()); - - const size_t hlen = m_Phash.size(); - - mgf1_mask(*m_mgf1_hash, output.first(hlen), output.subspan(hlen)); - - mgf1_mask(*m_mgf1_hash, output.subspan(hlen), output.first(hlen)); - - return key_length; -} - -/* -* OAEP Unpad Operation -*/ -CT::Option OAEP::unpad(std::span output, std::span input) const { - BOTAN_ASSERT_NOMSG(output.size() >= input.size()); - - /* - Must be careful about error messages here; if an attacker can - distinguish them, it is easy to use the differences as an oracle to - find the secret key, as described in "A Chosen Ciphertext Attack on - RSA Optimal Asymmetric Encryption Padding (OAEP) as Standardized in - PKCS #1 v2.0", James Manger, Crypto 2001 - - Also have to be careful about timing attacks! Pointed out by Falko - Strenzke. - - According to the standard (RFC 3447 Section 7.1.1), the encryptor always - creates a message as follows: - i. Concatenate a single octet with hexadecimal value 0x00, - maskedSeed, and maskedDB to form an encoded message EM of - length k octets as - EM = 0x00 || maskedSeed || maskedDB. - where k is the length of the modulus N. - Therefore, the first byte should always be zero. - */ - - if(input.empty()) { - return {}; - } - - auto scope = CT::scoped_poison(input); - - const auto has_leading_0 = CT::Mask::is_zero(input[0]).as_choice(); - - secure_vector decoded(input.begin() + 1, input.end()); - auto buf = std::span{decoded}; - - const size_t hlen = m_Phash.size(); - - mgf1_mask(*m_mgf1_hash, buf.subspan(hlen), buf.first(hlen)); - - mgf1_mask(*m_mgf1_hash, buf.first(hlen), buf.subspan(hlen)); - - auto delim = oaep_find_delim(buf, m_Phash); - - return CT::copy_output(delim.has_value() && has_leading_0, output, buf, delim.value_or(0)); -} - -CT::Option oaep_find_delim(std::span input, std::span phash) { - // Too short to be valid, reject immediately - if(input.size() < 1 + 2 * phash.size()) { - return {}; - } - - size_t delim_idx = 2 * phash.size(); - CT::Mask waiting_for_delim = CT::Mask::set(); - CT::Mask bad_input_m = CT::Mask::cleared(); - - for(uint8_t ib : input.subspan(2 * phash.size())) { - const auto zero_m = CT::Mask::is_zero(ib); - const auto one_m = CT::Mask::is_equal(ib, 1); - - const auto add_m = waiting_for_delim & zero_m; - - bad_input_m |= waiting_for_delim & ~(zero_m | one_m); - - delim_idx += add_m.if_set_return(1); - - waiting_for_delim &= zero_m; - } - - // If we never saw any non-zero byte, then it's not valid input - bad_input_m |= waiting_for_delim; - - // If the P hash is wrong, then it's not valid - bad_input_m |= CT::is_not_equal(&input[phash.size()], phash.data(), phash.size()); - - delim_idx += 1; - - const auto accept = !(bad_input_m.as_choice()); - - return CT::Option(delim_idx, accept); -} - -/* -* Return the max input size for a given key size -*/ -size_t OAEP::maximum_input_size(size_t keybits) const { - if(keybits / 8 > 2 * m_Phash.size() + 1) { - return ((keybits / 8) - 2 * m_Phash.size() - 1); - } else { - return 0; - } -} - -OAEP::OAEP(std::unique_ptr hash, std::string_view P) : m_mgf1_hash(std::move(hash)) { - m_Phash = m_mgf1_hash->process(P); -} - -OAEP::OAEP(std::unique_ptr hash, std::unique_ptr mgf1_hash, std::string_view P) : - m_mgf1_hash(std::move(mgf1_hash)) { - auto phash = std::move(hash); - m_Phash = phash->process(P); -} - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/eme_oaep/oaep.h botan3-3.12.0+dfsg/src/lib/pk_pad/eme_oaep/oaep.h --- botan3-3.7.1+dfsg/src/lib/pk_pad/eme_oaep/oaep.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/eme_oaep/oaep.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,55 +0,0 @@ -/* -* OAEP -* (C) 1999-2007,2018,2024 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_OAEP_H_ -#define BOTAN_OAEP_H_ - -#include - -#include -#include - -namespace Botan { - -/** -* OAEP (called EME1 in IEEE 1363 and in earlier versions of the library) -* as specified in PKCS#1 v2.0 (RFC 2437) or PKCS#1 v2.1 (RFC 3447) -*/ -class OAEP final : public EME { - public: - size_t maximum_input_size(size_t) const override; - - /** - * @param hash function to use for hashing (takes ownership) - * @param P an optional label. Normally empty. - */ - OAEP(std::unique_ptr hash, std::string_view P = ""); - - /** - * @param hash function to use for hashing (takes ownership) - * @param mgf1_hash function to use for MGF1 (takes ownership) - * @param P an optional label. Normally empty. - */ - OAEP(std::unique_ptr hash, std::unique_ptr mgf1_hash, std::string_view P = ""); - - private: - size_t pad(std::span output, - std::span input, - size_t key_length, - RandomNumberGenerator& rng) const override; - - CT::Option unpad(std::span output, std::span input) const override; - - secure_vector m_Phash; - std::unique_ptr m_mgf1_hash; -}; - -BOTAN_FUZZER_API CT::Option oaep_find_delim(std::span input, std::span phash); - -} // namespace Botan - -#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp botan3-3.12.0+dfsg/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp --- botan3-3.7.1+dfsg/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,106 +0,0 @@ -/* -* PKCS #1 v1.5 Type 2 (encryption) padding -* (C) 1999-2007,2015,2016,2024 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include -#include -#include -#include -#include - -namespace Botan { - -/* -* PKCS1 Pad Operation -*/ -size_t EME_PKCS1v15::pad(std::span output, - std::span input, - size_t key_length, - RandomNumberGenerator& rng) const { - key_length /= 8; - - if(input.size() > maximum_input_size(key_length * 8)) { - throw Invalid_Argument("PKCS1: Input is too large"); - } - - BufferStuffer stuffer(output); - - const size_t padding_bytes = [&]() { - auto d = checked_sub(key_length, input.size() + 2); - BOTAN_ASSERT_NOMSG(d.has_value()); - return *d; - }(); - - stuffer.append(0x02); - for(size_t i = 0; i != padding_bytes; ++i) { - stuffer.append(rng.next_nonzero_byte()); - } - stuffer.append(0x00); - stuffer.append(input); - - return output.size() - stuffer.remaining_capacity(); -} - -/* -* PKCS1 Unpad Operation -*/ -CT::Option EME_PKCS1v15::unpad(std::span output, std::span input) const { - BOTAN_ASSERT_NOMSG(output.size() >= input.size()); - - /* - * RSA decryption pads the ciphertext up to the modulus size, so this only - * occurs with very (!) small keys, or when fuzzing. - * - * 11 bytes == 00,02 + 8 bytes mandatory padding + 00 - */ - if(input.size() < 11) { - return {}; - } - - auto scope = CT::scoped_poison(input); - - CT::Mask bad_input_m = CT::Mask::cleared(); - CT::Mask seen_zero_m = CT::Mask::cleared(); - size_t delim_idx = 2; // initial 0002 - - bad_input_m |= ~CT::Mask::is_equal(input[0], 0); - bad_input_m |= ~CT::Mask::is_equal(input[1], 2); - - for(size_t i = 2; i < input.size(); ++i) { - const auto is_zero_m = CT::Mask::is_zero(input[i]); - delim_idx += seen_zero_m.if_not_set_return(1); - seen_zero_m |= is_zero_m; - } - - // no zero delim -> bad padding - bad_input_m |= ~seen_zero_m; - /* - delim indicates < 8 bytes padding -> bad padding - - We require 11 here because we are counting also the 00 delim byte - */ - bad_input_m |= CT::Mask(CT::Mask::is_lt(delim_idx, 11)); - - const CT::Choice accept = !(bad_input_m.as_choice()); - - return CT::copy_output(accept, output, input, delim_idx); -} - -/* -* Return the max input size for a given key size -*/ -size_t EME_PKCS1v15::maximum_input_size(size_t keybits) const { - if(keybits / 8 > 10) { - return ((keybits / 8) - 10); - } else { - return 0; - } -} - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/eme_pkcs1/eme_pkcs.h botan3-3.12.0+dfsg/src/lib/pk_pad/eme_pkcs1/eme_pkcs.h --- botan3-3.7.1+dfsg/src/lib/pk_pad/eme_pkcs1/eme_pkcs.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/eme_pkcs1/eme_pkcs.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -/* -* EME PKCS#1 v1.5 -* (C) 1999-2007 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_EME_PKCS1_H_ -#define BOTAN_EME_PKCS1_H_ - -#include - -namespace Botan { - -/** -* EME from PKCS #1 v1.5 -*/ -class BOTAN_FUZZER_API EME_PKCS1v15 final : public EME { - private: - size_t maximum_input_size(size_t) const override; - - size_t pad(std::span output, - std::span input, - size_t key_length, - RandomNumberGenerator& rng) const override; - - CT::Option unpad(std::span output, std::span input) const override; -}; - -} // namespace Botan - -#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/eme_pkcs1/info.txt botan3-3.12.0+dfsg/src/lib/pk_pad/eme_pkcs1/info.txt --- botan3-3.7.1+dfsg/src/lib/pk_pad/eme_pkcs1/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/eme_pkcs1/info.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - -EME_PKCS1v15 -> 20131128 -EME_PKCS1 -> 20190426 -PKCSV15_ENCRYPTION_PADDING -> 20250126 - - - -name -> "PKCS #1 v1.5 encryption padding" - diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/eme_raw/eme_raw.cpp botan3-3.12.0+dfsg/src/lib/pk_pad/eme_raw/eme_raw.cpp --- botan3-3.7.1+dfsg/src/lib/pk_pad/eme_raw/eme_raw.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/eme_raw/eme_raw.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -/* -* (C) 2015,2016,2024 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include - -namespace Botan { - -size_t EME_Raw::pad(std::span output, - std::span input, - size_t key_length, - RandomNumberGenerator& rng) const { - BOTAN_UNUSED(rng); - BOTAN_ASSERT_NOMSG(input.size() < maximum_input_size(8 * key_length)); - BOTAN_ASSERT_NOMSG(output.size() >= input.size()); - copy_mem(output.first(input.size()), input); - return input.size(); -} - -CT::Option EME_Raw::unpad(std::span output, std::span input) const { - BOTAN_ASSERT_NOMSG(output.size() >= input.size()); - - if(input.empty()) { - return CT::Option(0); - } - - const size_t leading_zeros = CT::count_leading_zero_bytes(input); - return CT::copy_output(CT::Choice::yes(), output, input, leading_zeros); -} - -size_t EME_Raw::maximum_input_size(size_t keybits) const { - return keybits / 8; -} - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/eme_raw/eme_raw.h botan3-3.12.0+dfsg/src/lib/pk_pad/eme_raw/eme_raw.h --- botan3-3.7.1+dfsg/src/lib/pk_pad/eme_raw/eme_raw.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/eme_raw/eme_raw.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -/* -* (C) 2015 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_EME_RAW_H_ -#define BOTAN_EME_RAW_H_ - -#include - -namespace Botan { - -class EME_Raw final : public EME { - public: - EME_Raw() = default; - - private: - size_t maximum_input_size(size_t i) const override; - - size_t pad(std::span output, - std::span input, - size_t key_length, - RandomNumberGenerator& rng) const override; - - CT::Option unpad(std::span output, std::span input) const override; -}; - -} // namespace Botan - -#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/eme_raw/info.txt botan3-3.12.0+dfsg/src/lib/pk_pad/eme_raw/info.txt --- botan3-3.7.1+dfsg/src/lib/pk_pad/eme_raw/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/eme_raw/info.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ - -EME_RAW -> 20150313 - - - -name -> "EME Raw Padding" - diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/emsa.cpp botan3-3.12.0+dfsg/src/lib/pk_pad/emsa.cpp --- botan3-3.7.1+dfsg/src/lib/pk_pad/emsa.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/emsa.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,143 +0,0 @@ -/* -* (C) 2015 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include -#include - -#if defined(BOTAN_HAS_EMSA_X931) - #include -#endif - -#if defined(BOTAN_HAS_EMSA_PKCS1) - #include -#endif - -#if defined(BOTAN_HAS_EMSA_PSSR) - #include -#endif - -#if defined(BOTAN_HAS_EMSA_RAW) - #include -#endif - -#if defined(BOTAN_HAS_ISO_9796) - #include -#endif - -namespace Botan { - -std::unique_ptr EMSA::create(std::string_view algo_spec) { - SCAN_Name req(algo_spec); - -#if defined(BOTAN_HAS_EMSA_PKCS1) - // TODO(Botan4) Remove all but "PKCS1v15" - if(req.algo_name() == "EMSA_PKCS1" || req.algo_name() == "PKCS1v15" || req.algo_name() == "EMSA-PKCS1-v1_5" || - req.algo_name() == "EMSA3") { - if(req.arg_count() == 2 && req.arg(0) == "Raw") { - return std::make_unique(req.arg(1)); - } else if(req.arg_count() == 1) { - if(req.arg(0) == "Raw") { - return std::make_unique(); - } else { - if(auto hash = HashFunction::create(req.arg(0))) { - return std::make_unique(std::move(hash)); - } - } - } - } -#endif - -#if defined(BOTAN_HAS_EMSA_PSSR) - // TODO(Botan4) Remove all but "PSS_Raw" - if(req.algo_name() == "PSS_Raw" || req.algo_name() == "PSSR_Raw") { - if(req.arg_count_between(1, 3) && req.arg(1, "MGF1") == "MGF1") { - if(auto hash = HashFunction::create(req.arg(0))) { - if(req.arg_count() == 3) { - const size_t salt_size = req.arg_as_integer(2, 0); - return std::make_unique(std::move(hash), salt_size); - } else { - return std::make_unique(std::move(hash)); - } - } - } - } - - // TODO(Botan4) Remove all but "PSS" - if(req.algo_name() == "PSS" || req.algo_name() == "PSSR" || req.algo_name() == "EMSA-PSS" || - req.algo_name() == "PSS-MGF1" || req.algo_name() == "EMSA4") { - if(req.arg_count_between(1, 3) && req.arg(1, "MGF1") == "MGF1") { - if(auto hash = HashFunction::create(req.arg(0))) { - if(req.arg_count() == 3) { - const size_t salt_size = req.arg_as_integer(2, 0); - return std::make_unique(std::move(hash), salt_size); - } else { - return std::make_unique(std::move(hash)); - } - } - } - } -#endif - -#if defined(BOTAN_HAS_ISO_9796) - if(req.algo_name() == "ISO_9796_DS2") { - if(req.arg_count_between(1, 3)) { - if(auto hash = HashFunction::create(req.arg(0))) { - const size_t salt_size = req.arg_as_integer(2, hash->output_length()); - const bool implicit = req.arg(1, "exp") == "imp"; - return std::make_unique(std::move(hash), implicit, salt_size); - } - } - } - //ISO-9796-2 DS 3 is deterministic and DS2 without a salt - if(req.algo_name() == "ISO_9796_DS3") { - if(req.arg_count_between(1, 2)) { - if(auto hash = HashFunction::create(req.arg(0))) { - const bool implicit = req.arg(1, "exp") == "imp"; - return std::make_unique(std::move(hash), implicit); - } - } - } -#endif - -#if defined(BOTAN_HAS_EMSA_X931) - // TODO(Botan4) Remove all but "X9.31" - if(req.algo_name() == "EMSA_X931" || req.algo_name() == "EMSA2" || req.algo_name() == "X9.31") { - if(req.arg_count() == 1) { - if(auto hash = HashFunction::create(req.arg(0))) { - return std::make_unique(std::move(hash)); - } - } - } -#endif - -#if defined(BOTAN_HAS_EMSA_RAW) - if(req.algo_name() == "Raw") { - if(req.arg_count() == 0) { - return std::make_unique(); - } else { - auto hash = HashFunction::create(req.arg(0)); - if(hash) { - return std::make_unique(hash->output_length()); - } - } - } -#endif - - return nullptr; -} - -std::unique_ptr EMSA::create_or_throw(std::string_view algo_spec) { - auto emsa = EMSA::create(algo_spec); - if(emsa) { - return emsa; - } - throw Algorithm_Not_Found(algo_spec); -} - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/emsa.h botan3-3.12.0+dfsg/src/lib/pk_pad/emsa.h --- botan3-3.7.1+dfsg/src/lib/pk_pad/emsa.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/emsa.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,88 +0,0 @@ -/* -* EMSA Classes -* (C) 1999-2007 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_PUBKEY_EMSA_H_ -#define BOTAN_PUBKEY_EMSA_H_ - -#include -#include - -namespace Botan { - -class RandomNumberGenerator; - -/** -* EMSA, from IEEE 1363s Encoding Method for Signatures, Appendix -* -* Any way of encoding/padding signatures -*/ -class BOTAN_TEST_API EMSA { - public: - virtual ~EMSA() = default; - - /** - * Factory method for EMSA (message-encoding methods for signatures - * with appendix) objects - * @param algo_spec the name of the EMSA to create - * @return pointer to newly allocated object of that type, or nullptr - */ - static std::unique_ptr create(std::string_view algo_spec); - - /** - * Factory method for EMSA (message-encoding methods for signatures - * with appendix) objects - * @param algo_spec the name of the EMSA to create - * @return pointer to newly allocated object of that type, or throws - */ - static std::unique_ptr create_or_throw(std::string_view algo_spec); - - /** - * Add more data to the signature computation - * @param input some data - * @param length length of input in bytes - */ - virtual void update(const uint8_t input[], size_t length) = 0; - - /** - * @return raw hash - */ - virtual std::vector raw_data() = 0; - - /** - * Return the encoding of a message - * @param msg the result of raw_data() - * @param output_bits the desired output bit size - * @param rng a random number generator - * @return encoded signature - */ - virtual std::vector encoding_of(const std::vector& msg, - size_t output_bits, - RandomNumberGenerator& rng) = 0; - - /** - * Verify the encoding - * @param coded the received (coded) message representative - * @param raw the computed (local, uncoded) message representative - * @param key_bits the size of the key in bits - * @return true if coded is a valid encoding of raw, otherwise false - */ - virtual bool verify(const std::vector& coded, const std::vector& raw, size_t key_bits) = 0; - - /** - * Return the hash function being used by this padding scheme - */ - virtual std::string hash_function() const = 0; - - /** - * @return the SCAN name of the encoding/padding scheme - */ - virtual std::string name() const = 0; -}; - -} // namespace Botan - -#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp --- botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,133 +0,0 @@ -/* -* PKCS #1 v1.5 signature padding -* (C) 1999-2008 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include -#include - -namespace Botan { - -namespace { - -std::vector pkcs1v15_sig_encoding(const std::vector& msg, - size_t output_bits, - std::span hash_id) { - const size_t output_length = output_bits / 8; - - if(output_length < hash_id.size() + msg.size() + 2 + 8) { - throw Encoding_Error("pkcs1v15_sig_encoding: Output length is too small"); - } - - std::vector padded(output_length); - BufferStuffer stuffer(padded); - - stuffer.append(0x01); - stuffer.append(0xFF, stuffer.remaining_capacity() - (1 + hash_id.size() + msg.size())); - stuffer.append(0x00); - stuffer.append(hash_id); - stuffer.append(msg); - BOTAN_ASSERT_NOMSG(stuffer.full()); - - return padded; -} - -} // namespace - -void EMSA_PKCS1v15::update(const uint8_t input[], size_t length) { - m_hash->update(input, length); -} - -std::vector EMSA_PKCS1v15::raw_data() { - return m_hash->final_stdvec(); -} - -std::vector EMSA_PKCS1v15::encoding_of(const std::vector& msg, - size_t output_bits, - RandomNumberGenerator& /*rng*/) { - if(msg.size() != m_hash->output_length()) { - throw Encoding_Error("EMSA_PKCS1v15::encoding_of: Bad input length"); - } - - return pkcs1v15_sig_encoding(msg, output_bits, m_hash_id); -} - -bool EMSA_PKCS1v15::verify(const std::vector& coded, const std::vector& raw, size_t key_bits) { - if(raw.size() != m_hash->output_length()) { - return false; - } - - try { - return coded == pkcs1v15_sig_encoding(raw, key_bits, m_hash_id); - } catch(...) { - return false; - } -} - -EMSA_PKCS1v15::EMSA_PKCS1v15(std::unique_ptr hash) : m_hash(std::move(hash)) { - m_hash_id = pkcs_hash_id(m_hash->name()); -} - -std::string EMSA_PKCS1v15::name() const { - return "PKCS1v15(" + m_hash->name() + ")"; -} - -std::string EMSA_PKCS1v15_Raw::name() const { - if(m_hash_name.empty()) { - return "PKCS1v15(Raw)"; - } else { - return "PKCS1v15(Raw," + m_hash_name + ")"; - } -} - -EMSA_PKCS1v15_Raw::EMSA_PKCS1v15_Raw() { - m_hash_output_len = 0; - // m_hash_id, m_hash_name left empty -} - -EMSA_PKCS1v15_Raw::EMSA_PKCS1v15_Raw(std::string_view hash_algo) { - std::unique_ptr hash(HashFunction::create_or_throw(hash_algo)); - m_hash_id = pkcs_hash_id(hash_algo); - m_hash_name = hash->name(); - m_hash_output_len = hash->output_length(); -} - -void EMSA_PKCS1v15_Raw::update(const uint8_t input[], size_t length) { - m_message += std::make_pair(input, length); -} - -std::vector EMSA_PKCS1v15_Raw::raw_data() { - std::vector ret; - std::swap(ret, m_message); - - if(m_hash_output_len > 0 && ret.size() != m_hash_output_len) { - throw Encoding_Error("EMSA_PKCS1v15_Raw::encoding_of: Bad input length"); - } - - return ret; -} - -std::vector EMSA_PKCS1v15_Raw::encoding_of(const std::vector& msg, - size_t output_bits, - RandomNumberGenerator& /*rng*/) { - return pkcs1v15_sig_encoding(msg, output_bits, m_hash_id); -} - -bool EMSA_PKCS1v15_Raw::verify(const std::vector& coded, const std::vector& raw, size_t key_bits) { - if(m_hash_output_len > 0 && raw.size() != m_hash_output_len) { - return false; - } - - try { - return coded == pkcs1v15_sig_encoding(raw, key_bits, m_hash_id); - } catch(...) { - return false; - } -} - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.h botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.h --- botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,81 +0,0 @@ -/* -* PKCS #1 v1.5 signature padding -* (C) 1999-2008 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_EMSA_PKCS1_H_ -#define BOTAN_EMSA_PKCS1_H_ - -#include -#include - -namespace Botan { - -/** -* PKCS #1 v1.5 signature padding -* aka PKCS #1 block type 1 -* aka EMSA3 from IEEE 1363 -*/ -class EMSA_PKCS1v15 final : public EMSA { - public: - /** - * @param hash the hash function to use - */ - explicit EMSA_PKCS1v15(std::unique_ptr hash); - - void update(const uint8_t[], size_t) override; - - std::vector raw_data() override; - - std::vector encoding_of(const std::vector&, size_t, RandomNumberGenerator& rng) override; - - bool verify(const std::vector&, const std::vector&, size_t) override; - - std::string name() const override; - - std::string hash_function() const override { return m_hash->name(); } - - private: - std::unique_ptr m_hash; - std::vector m_hash_id; -}; - -/** -* EMSA_PKCS1v15_Raw which is EMSA_PKCS1v15 without a hash or digest id -* (which according to QCA docs is "identical to PKCS#11's CKM_RSA_PKCS -* mechanism", something I have not confirmed) -*/ -class EMSA_PKCS1v15_Raw final : public EMSA { - public: - void update(const uint8_t[], size_t) override; - - std::vector raw_data() override; - - std::vector encoding_of(const std::vector&, size_t, RandomNumberGenerator& rng) override; - - bool verify(const std::vector&, const std::vector&, size_t) override; - - EMSA_PKCS1v15_Raw(); - - /** - * @param hash_algo the digest id for that hash is included in - * the signature. - */ - EMSA_PKCS1v15_Raw(std::string_view hash_algo); - - std::string hash_function() const override { return m_hash_name; } - - std::string name() const override; - - private: - size_t m_hash_output_len = 0; - std::string m_hash_name; - std::vector m_hash_id; - std::vector m_message; -}; - -} // namespace Botan - -#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_pkcs1/info.txt botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_pkcs1/info.txt --- botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_pkcs1/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_pkcs1/info.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ - -EMSA_PKCS1 -> 20140118 -PKCSV15_SIGNATURE_PADDING -> 20250126 - - - -name -> "PKCS #1 v1.5 signature padding" - - - -hash_id - diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_pssr/info.txt botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_pssr/info.txt --- botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_pssr/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_pssr/info.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ - -EMSA_PSSR -> 20131128 -PSS -> 20250130 - - - -name -> "PSS" -brief -> "PSS signature padding from PKCS1v2.0" - - - -mgf1 - diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_pssr/pssr.cpp botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_pssr/pssr.cpp --- botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_pssr/pssr.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_pssr/pssr.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,245 +0,0 @@ -/* -* PSSR -* (C) 1999-2007,2017,2023 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Botan { - -namespace { - -/* -* PSSR Encode Operation -*/ -std::vector pss_encode(HashFunction& hash, - const std::vector& msg, - const std::vector& salt, - size_t output_bits) { - const size_t HASH_SIZE = hash.output_length(); - - if(msg.size() != HASH_SIZE) { - throw Encoding_Error("Cannot encode PSS string, input length invalid for hash"); - } - if(output_bits < 8 * HASH_SIZE + 8 * salt.size() + 9) { - throw Encoding_Error("Cannot encode PSS string, output length too small"); - } - - const size_t output_length = ceil_tobytes(output_bits); - const uint8_t db0_mask = 0xFF >> (8 * output_length - output_bits); - - std::array padding = {0}; - hash.update(padding); - hash.update(msg); - hash.update(salt); - std::vector H = hash.final_stdvec(); - - const size_t db_len = output_length - HASH_SIZE - 1; - std::vector EM(output_length); - - BufferStuffer stuffer(EM); - stuffer.append(0x00, stuffer.remaining_capacity() - (1 + salt.size() + H.size() + 1)); - stuffer.append(0x01); - stuffer.append(salt); - - mgf1_mask(hash, H.data(), H.size(), EM.data(), db_len); - EM[0] &= db0_mask; - - stuffer.append(H); - stuffer.append(0xBC); - BOTAN_ASSERT_NOMSG(stuffer.full()); - - return EM; -} - -bool pss_verify(HashFunction& hash, - const std::vector& pss_repr, - const std::vector& message_hash, - size_t key_bits, - size_t* out_salt_size) { - const size_t HASH_SIZE = hash.output_length(); - const size_t key_bytes = ceil_tobytes(key_bits); - - if(key_bits < 8 * HASH_SIZE + 9) { - return false; - } - - if(message_hash.size() != HASH_SIZE) { - return false; - } - - if(pss_repr.size() > key_bytes || pss_repr.size() <= 1) { - return false; - } - - if(pss_repr[pss_repr.size() - 1] != 0xBC) { - return false; - } - - std::vector coded = pss_repr; - if(coded.size() < key_bytes) { - std::vector temp(key_bytes); - BufferStuffer stuffer(temp); - stuffer.append(0x00, stuffer.remaining_capacity() - coded.size()); - stuffer.append(coded); - coded = temp; - } - - const size_t TOP_BITS = 8 * ((key_bits + 7) / 8) - key_bits; - if(TOP_BITS > 8 - high_bit(coded[0])) { - return false; - } - - uint8_t* DB = coded.data(); - const size_t DB_size = coded.size() - HASH_SIZE - 1; - - const uint8_t* H = &coded[DB_size]; - const size_t H_size = HASH_SIZE; - - mgf1_mask(hash, H, H_size, DB, DB_size); - DB[0] &= 0xFF >> TOP_BITS; - - size_t salt_offset = 0; - for(size_t j = 0; j != DB_size; ++j) { - if(DB[j] == 0x01) { - salt_offset = j + 1; - break; - } - if(DB[j]) { - return false; - } - } - if(salt_offset == 0) { - return false; - } - - const size_t salt_size = DB_size - salt_offset; - - std::array padding = {0}; - hash.update(padding); - hash.update(message_hash); - hash.update(&DB[salt_offset], salt_size); - - const std::vector H2 = hash.final_stdvec(); - - const bool ok = CT::is_equal(H, H2.data(), HASH_SIZE).as_bool(); - - if(out_salt_size && ok) { - *out_salt_size = salt_size; - } - - return ok; -} - -} // namespace - -PSSR::PSSR(std::unique_ptr hash) : - m_hash(std::move(hash)), m_salt_size(m_hash->output_length()), m_required_salt_len(false) {} - -PSSR::PSSR(std::unique_ptr hash, size_t salt_size) : - m_hash(std::move(hash)), m_salt_size(salt_size), m_required_salt_len(true) {} - -/* -* PSSR Update Operation -*/ -void PSSR::update(const uint8_t input[], size_t length) { - m_hash->update(input, length); -} - -/* -* Return the raw (unencoded) data -*/ -std::vector PSSR::raw_data() { - return m_hash->final_stdvec(); -} - -std::vector PSSR::encoding_of(const std::vector& msg, - size_t output_bits, - RandomNumberGenerator& rng) { - const auto salt = rng.random_vec>(m_salt_size); - return pss_encode(*m_hash, msg, salt, output_bits); -} - -/* -* PSSR Decode/Verify Operation -*/ -bool PSSR::verify(const std::vector& coded, const std::vector& raw, size_t key_bits) { - size_t salt_size = 0; - const bool ok = pss_verify(*m_hash, coded, raw, key_bits, &salt_size); - - if(m_required_salt_len && salt_size != m_salt_size) { - return false; - } - - return ok; -} - -std::string PSSR::name() const { - return fmt("PSS({},MGF1,{})", m_hash->name(), m_salt_size); -} - -PSSR_Raw::PSSR_Raw(std::unique_ptr hash) : - m_hash(std::move(hash)), m_salt_size(m_hash->output_length()), m_required_salt_len(false) {} - -PSSR_Raw::PSSR_Raw(std::unique_ptr hash, size_t salt_size) : - m_hash(std::move(hash)), m_salt_size(salt_size), m_required_salt_len(true) {} - -/* -* PSSR_Raw Update Operation -*/ -void PSSR_Raw::update(const uint8_t input[], size_t length) { - m_msg.insert(m_msg.end(), input, input + length); -} - -/* -* Return the raw (unencoded) data -*/ -std::vector PSSR_Raw::raw_data() { - std::vector ret; - std::swap(ret, m_msg); - - if(ret.size() != m_hash->output_length()) { - throw Encoding_Error("PSSR_Raw Bad input length, did not match hash"); - } - - return ret; -} - -std::vector PSSR_Raw::encoding_of(const std::vector& msg, - size_t output_bits, - RandomNumberGenerator& rng) { - const auto salt = rng.random_vec>(m_salt_size); - return pss_encode(*m_hash, msg, salt, output_bits); -} - -/* -* PSSR_Raw Decode/Verify Operation -*/ -bool PSSR_Raw::verify(const std::vector& coded, const std::vector& raw, size_t key_bits) { - size_t salt_size = 0; - const bool ok = pss_verify(*m_hash, coded, raw, key_bits, &salt_size); - - if(m_required_salt_len && salt_size != m_salt_size) { - return false; - } - - return ok; -} - -std::string PSSR_Raw::name() const { - return fmt("PSS_Raw({},MGF1,{})", m_hash->name(), m_salt_size); -} - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_pssr/pssr.h botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_pssr/pssr.h --- botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_pssr/pssr.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_pssr/pssr.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,92 +0,0 @@ -/* -* PSSR -* (C) 1999-2007 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_PSSR_H_ -#define BOTAN_PSSR_H_ - -#include -#include - -namespace Botan { - -/** -* PSSR (called EMSA4 in IEEE 1363 and in old versions of the library) -*/ -class PSSR final : public EMSA { - public: - /** - * @param hash the hash function to use - */ - explicit PSSR(std::unique_ptr hash); - - /** - * @param hash the hash function to use - * @param salt_size the size of the salt to use in bytes - */ - PSSR(std::unique_ptr hash, size_t salt_size); - - std::string name() const override; - - std::string hash_function() const override { return m_hash->name(); } - - private: - void update(const uint8_t input[], size_t length) override; - - std::vector raw_data() override; - - std::vector encoding_of(const std::vector& msg, - size_t output_bits, - RandomNumberGenerator& rng) override; - - bool verify(const std::vector& coded, const std::vector& raw, size_t key_bits) override; - - std::unique_ptr m_hash; - size_t m_salt_size; - bool m_required_salt_len; -}; - -/** -* PSSR_Raw -* This accepts a pre-hashed buffer -*/ -class PSSR_Raw final : public EMSA { - public: - /** - * @param hash the hash function to use - */ - explicit PSSR_Raw(std::unique_ptr hash); - - /** - * @param hash the hash function to use - * @param salt_size the size of the salt to use in bytes - */ - PSSR_Raw(std::unique_ptr hash, size_t salt_size); - - std::string hash_function() const override { return m_hash->name(); } - - std::string name() const override; - - private: - void update(const uint8_t input[], size_t length) override; - - std::vector raw_data() override; - - std::vector encoding_of(const std::vector& msg, - size_t output_bits, - RandomNumberGenerator& rng) override; - - bool verify(const std::vector& coded, const std::vector& raw, size_t key_bits) override; - - std::unique_ptr m_hash; - std::vector m_msg; - size_t m_salt_size; - bool m_required_salt_len; -}; - -} // namespace Botan - -#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_raw/emsa_raw.cpp botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_raw/emsa_raw.cpp --- botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_raw/emsa_raw.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_raw/emsa_raw.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,92 +0,0 @@ -/* -* EMSA-Raw -* (C) 1999-2007 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include -#include - -namespace Botan { - -std::string EMSA_Raw::name() const { - if(m_expected_size > 0) { - return "Raw(" + std::to_string(m_expected_size) + ")"; - } - return "Raw"; -} - -/* -* EMSA-Raw Encode Operation -*/ -void EMSA_Raw::update(const uint8_t input[], size_t length) { - m_message += std::make_pair(input, length); -} - -/* -* Return the raw (unencoded) data -*/ -std::vector EMSA_Raw::raw_data() { - if(m_expected_size && m_message.size() != m_expected_size) { - throw Invalid_Argument("EMSA_Raw was configured to use a " + std::to_string(m_expected_size) + - " byte hash but instead was used for a " + std::to_string(m_message.size()) + " hash"); - } - - std::vector output; - std::swap(m_message, output); - return output; -} - -/* -* EMSA-Raw Encode Operation -*/ -std::vector EMSA_Raw::encoding_of(const std::vector& msg, - size_t /*output_bits*/, - RandomNumberGenerator& /*rng*/) { - if(m_expected_size && msg.size() != m_expected_size) { - throw Invalid_Argument("EMSA_Raw was configured to use a " + std::to_string(m_expected_size) + - " byte hash but instead was used for a " + std::to_string(msg.size()) + " hash"); - } - - return msg; -} - -/* -* EMSA-Raw Verify Operation -*/ -bool EMSA_Raw::verify(const std::vector& coded, const std::vector& raw, size_t /*key_bits*/) { - if(m_expected_size && raw.size() != m_expected_size) { - return false; - } - - if(coded.size() == raw.size()) { - return (coded == raw); - } - - if(coded.size() > raw.size()) { - return false; - } - - // handle zero padding differences - const size_t leading_zeros_expected = raw.size() - coded.size(); - - bool same_modulo_leading_zeros = true; - - for(size_t i = 0; i != leading_zeros_expected; ++i) { - if(raw[i]) { - same_modulo_leading_zeros = false; - } - } - - if(!CT::is_equal(coded.data(), raw.data() + leading_zeros_expected, coded.size()).as_bool()) { - same_modulo_leading_zeros = false; - } - - return same_modulo_leading_zeros; -} - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_raw/emsa_raw.h botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_raw/emsa_raw.h --- botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_raw/emsa_raw.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_raw/emsa_raw.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -/* -* EMSA-Raw -* (C) 1999-2007 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_EMSA_RAW_H_ -#define BOTAN_EMSA_RAW_H_ - -#include - -namespace Botan { - -/** -* EMSA-Raw - sign inputs directly -* Don't use this unless you know what you are doing. -*/ -class EMSA_Raw final : public EMSA { - public: - explicit EMSA_Raw(size_t expected_hash_size = 0) : m_expected_size(expected_hash_size) {} - - std::string hash_function() const override { return "Raw"; } - - std::string name() const override; - - private: - void update(const uint8_t[], size_t) override; - std::vector raw_data() override; - - std::vector encoding_of(const std::vector&, size_t, RandomNumberGenerator&) override; - - bool verify(const std::vector&, const std::vector&, size_t) override; - - const size_t m_expected_size; - std::vector m_message; -}; - -} // namespace Botan - -#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_raw/info.txt botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_raw/info.txt --- botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_raw/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_raw/info.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ - -EMSA_RAW -> 20131128 - - - -name -> "EMSA Raw Padding" - diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_x931/emsa_x931.cpp botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_x931/emsa_x931.cpp --- botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_x931/emsa_x931.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_x931/emsa_x931.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,97 +0,0 @@ -/* -* EMSA_X931 -* (C) 1999-2007 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include -#include -#include - -namespace Botan { - -namespace { - -std::vector emsa2_encoding(const std::vector& msg, - size_t output_bits, - const std::vector& empty_hash, - uint8_t hash_id) { - const size_t HASH_SIZE = empty_hash.size(); - - const size_t output_length = (output_bits + 1) / 8; - - if(msg.size() != HASH_SIZE) { - throw Encoding_Error("EMSA_X931::encoding_of: Bad input length"); - } - if(output_length < HASH_SIZE + 4) { - throw Encoding_Error("EMSA_X931::encoding_of: Output length is too small"); - } - - const bool empty_input = (msg == empty_hash); - - std::vector output(output_length); - BufferStuffer stuffer(output); - - stuffer.append(empty_input ? 0x4B : 0x6B); - stuffer.append(0xBB, stuffer.remaining_capacity() - (1 + msg.size() + 2)); - stuffer.append(0xBA); - stuffer.append(msg); - stuffer.append(hash_id); - stuffer.append(0xCC); - BOTAN_ASSERT_NOMSG(stuffer.full()); - - return output; -} - -} // namespace - -std::string EMSA_X931::name() const { - return fmt("X9.31({})", m_hash->name()); -} - -void EMSA_X931::update(const uint8_t input[], size_t length) { - m_hash->update(input, length); -} - -std::vector EMSA_X931::raw_data() { - return m_hash->final_stdvec(); -} - -/* -* EMSA_X931 Encode Operation -*/ -std::vector EMSA_X931::encoding_of(const std::vector& msg, - size_t output_bits, - RandomNumberGenerator& /*rng*/) { - return emsa2_encoding(msg, output_bits, m_empty_hash, m_hash_id); -} - -/* -* EMSA_X931 Verify Operation -*/ -bool EMSA_X931::verify(const std::vector& coded, const std::vector& raw, size_t key_bits) { - try { - return (coded == emsa2_encoding(raw, key_bits, m_empty_hash, m_hash_id)); - } catch(...) { - return false; - } -} - -/* -* EMSA_X931 Constructor -*/ -EMSA_X931::EMSA_X931(std::unique_ptr hash) : m_hash(std::move(hash)) { - m_empty_hash = m_hash->final_stdvec(); - - m_hash_id = ieee1363_hash_id(m_hash->name()); - - if(!m_hash_id) { - throw Encoding_Error("EMSA_X931 no hash identifier for " + m_hash->name()); - } -} - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_x931/emsa_x931.h botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_x931/emsa_x931.h --- botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_x931/emsa_x931.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_x931/emsa_x931.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,47 +0,0 @@ -/* -* X9.31 EMSA -* (C) 1999-2007 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_EMSA_X931_H_ -#define BOTAN_EMSA_X931_H_ - -#include -#include - -namespace Botan { - -/** -* EMSA from X9.31 (EMSA2 in IEEE 1363) -* Useful for Rabin-Williams, also sometimes used with RSA in -* odd protocols. -*/ -class EMSA_X931 final : public EMSA { - public: - /** - * @param hash the hash function to use - */ - explicit EMSA_X931(std::unique_ptr hash); - - std::string name() const override; - - std::string hash_function() const override { return m_hash->name(); } - - private: - void update(const uint8_t[], size_t) override; - std::vector raw_data() override; - - std::vector encoding_of(const std::vector&, size_t, RandomNumberGenerator& rng) override; - - bool verify(const std::vector&, const std::vector&, size_t) override; - - std::vector m_empty_hash; - std::unique_ptr m_hash; - uint8_t m_hash_id; -}; - -} // namespace Botan - -#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_x931/info.txt botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_x931/info.txt --- botan3-3.7.1+dfsg/src/lib/pk_pad/emsa_x931/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/emsa_x931/info.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ - -EMSA_X931 -> 20140118 - - - -name -> "X9.31" - - - -hash_id - diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/eme_oaep/info.txt botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_oaep/info.txt --- botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/eme_oaep/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_oaep/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ + +EME_OAEP -> 20180305 +OAEP -> 20250130 + + + +name -> "OAEP" + + + +mgf1 + diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/eme_oaep/oaep.cpp botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_oaep/oaep.cpp --- botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/eme_oaep/oaep.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_oaep/oaep.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,159 @@ +/* +* OAEP +* (C) 1999-2010,2015,2018,2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include + +namespace Botan { + +/* +* OAEP Pad Operation +*/ +size_t OAEP::pad(std::span output, + std::span input, + size_t key_length, + RandomNumberGenerator& rng) const { + key_length /= 8; + + if(input.size() > maximum_input_size(key_length * 8)) { + throw Invalid_Argument("OAEP: Input is too large"); + } + + const size_t output_size = key_length; + + output = output.first(output_size); // remainder ignored + + BufferStuffer stuffer(output); + + // We always use a seed len equal to the underlying hash + rng.randomize(stuffer.next(m_Phash.size())); + stuffer.append(m_Phash); + stuffer.append(0x00, stuffer.remaining_capacity() - (1 + input.size())); + stuffer.append(0x01); + stuffer.append(input); + BOTAN_ASSERT_NOMSG(stuffer.full()); + + const size_t hlen = m_Phash.size(); + + mgf1_mask(*m_mgf1_hash, output.first(hlen), output.subspan(hlen)); + + mgf1_mask(*m_mgf1_hash, output.subspan(hlen), output.first(hlen)); + + return key_length; +} + +/* +* OAEP Unpad Operation +*/ +CT::Option OAEP::unpad(std::span output, std::span input) const { + BOTAN_ASSERT_NOMSG(output.size() >= input.size()); + + /* + Must be careful about error messages here; if an attacker can + distinguish them, it is easy to use the differences as an oracle to + find the secret key, as described in "A Chosen Ciphertext Attack on + RSA Optimal Asymmetric Encryption Padding (OAEP) as Standardized in + PKCS #1 v2.0", James Manger, Crypto 2001 + + Also have to be careful about timing attacks! Pointed out by Falko + Strenzke. + + According to the standard (RFC 3447 Section 7.1.1), the encryptor always + creates a message as follows: + i. Concatenate a single octet with hexadecimal value 0x00, + maskedSeed, and maskedDB to form an encoded message EM of + length k octets as + EM = 0x00 || maskedSeed || maskedDB. + where k is the length of the modulus N. + Therefore, the first byte should always be zero. + */ + + const size_t hlen = m_Phash.size(); + + if(input.size() < 1 + 2 * hlen + 1) { + return {}; + } + + auto scope = CT::scoped_poison(input); + + const auto has_leading_0 = CT::Mask::is_zero(input[0]).as_choice(); + + secure_vector decoded(input.begin() + 1, input.end()); + auto buf = std::span{decoded}; + + mgf1_mask(*m_mgf1_hash, buf.subspan(hlen), buf.first(hlen)); + + mgf1_mask(*m_mgf1_hash, buf.first(hlen), buf.subspan(hlen)); + + auto delim = oaep_find_delim(buf, m_Phash); + + return CT::copy_output(delim.has_value() && has_leading_0, output, buf, delim.value_or(0)); +} + +CT::Option oaep_find_delim(std::span input, std::span phash) { + // Too short to be valid, reject immediately + if(input.size() < 1 + 2 * phash.size()) { + return {}; + } + + size_t delim_idx = 2 * phash.size(); + CT::Mask waiting_for_delim = CT::Mask::set(); + CT::Mask bad_input_m = CT::Mask::cleared(); + + for(const uint8_t ib : input.subspan(2 * phash.size())) { + const auto zero_m = CT::Mask::is_zero(ib); + const auto one_m = CT::Mask::is_equal(ib, 1); + + const auto add_m = waiting_for_delim & zero_m; + + bad_input_m |= waiting_for_delim & ~(zero_m | one_m); + + delim_idx += add_m.if_set_return(1); + + waiting_for_delim &= zero_m; + } + + // If we never saw any non-zero byte, then it's not valid input + bad_input_m |= waiting_for_delim; + + // If the P hash is wrong, then it's not valid + bad_input_m |= CT::is_not_equal(&input[phash.size()], phash.data(), phash.size()); + + delim_idx += 1; + + const auto accept = !(bad_input_m.as_choice()); + + return CT::Option(delim_idx, accept); +} + +/* +* Return the max input size for a given key size +*/ +size_t OAEP::maximum_input_size(size_t keybits) const { + if(keybits / 8 > 2 * m_Phash.size() + 1) { + return ((keybits / 8) - 2 * m_Phash.size() - 1); + } else { + return 0; + } +} + +OAEP::OAEP(std::unique_ptr hash, std::string_view P) : m_mgf1_hash(std::move(hash)) { + m_Phash = m_mgf1_hash->process(P); +} + +OAEP::OAEP(std::unique_ptr hash, std::unique_ptr mgf1_hash, std::string_view P) : + m_mgf1_hash(std::move(mgf1_hash)) { + auto phash = std::move(hash); + m_Phash = phash->process(P); +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/eme_oaep/oaep.h botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_oaep/oaep.h --- botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/eme_oaep/oaep.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_oaep/oaep.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,55 @@ +/* +* OAEP +* (C) 1999-2007,2018,2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_OAEP_H_ +#define BOTAN_OAEP_H_ + +#include + +#include +#include + +namespace Botan { + +/** +* OAEP (called EME1 in IEEE 1363 and in earlier versions of the library) +* as specified in PKCS#1 v2.0 (RFC 2437) or PKCS#1 v2.1 (RFC 3447) +*/ +class OAEP final : public EncryptionPaddingScheme { + public: + size_t maximum_input_size(size_t keybits) const override; + + /** + * @param hash function to use for hashing (takes ownership) + * @param P an optional label. Normally empty. + */ + explicit OAEP(std::unique_ptr hash, std::string_view P = ""); + + /** + * @param hash function to use for hashing (takes ownership) + * @param mgf1_hash function to use for MGF1 (takes ownership) + * @param P an optional label. Normally empty. + */ + OAEP(std::unique_ptr hash, std::unique_ptr mgf1_hash, std::string_view P = ""); + + private: + size_t pad(std::span output, + std::span input, + size_t key_length, + RandomNumberGenerator& rng) const override; + + CT::Option unpad(std::span output, std::span input) const override; + + secure_vector m_Phash; + std::unique_ptr m_mgf1_hash; +}; + +BOTAN_FUZZER_API CT::Option oaep_find_delim(std::span input, std::span phash); + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/eme_pkcs1/eme_pkcs.cpp botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_pkcs1/eme_pkcs.cpp --- botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/eme_pkcs1/eme_pkcs.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_pkcs1/eme_pkcs.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,105 @@ +/* +* PKCS #1 v1.5 Type 2 (encryption) padding +* (C) 1999-2007,2015,2016,2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include + +namespace Botan { + +/* +* PKCS1 Pad Operation +*/ +size_t EME_PKCS1v15::pad(std::span output, + std::span input, + size_t key_length, + RandomNumberGenerator& rng) const { + key_length /= 8; + + if(input.size() > maximum_input_size(key_length * 8)) { + throw Invalid_Argument("PKCS1: Input is too large"); + } + + BufferStuffer stuffer(output); + + const size_t padding_bytes = [&]() { + auto d = checked_sub(key_length, input.size() + 2); + BOTAN_ASSERT_NOMSG(d.has_value()); + return *d; + }(); + + stuffer.append(0x02); + for(size_t i = 0; i != padding_bytes; ++i) { + stuffer.append(rng.next_nonzero_byte()); + } + stuffer.append(0x00); + stuffer.append(input); + + return output.size() - stuffer.remaining_capacity(); +} + +/* +* PKCS1 Unpad Operation +*/ +CT::Option EME_PKCS1v15::unpad(std::span output, std::span input) const { + BOTAN_ASSERT_NOMSG(output.size() >= input.size()); + + /* + * RSA decryption pads the ciphertext up to the modulus size, so this only + * occurs with very (!) small keys, or when fuzzing. + * + * 11 bytes == 00,02 + 8 bytes mandatory padding + 00 + */ + if(input.size() < 11) { + return {}; + } + + auto scope = CT::scoped_poison(input); + + CT::Mask bad_input_m = CT::Mask::cleared(); + CT::Mask seen_zero_m = CT::Mask::cleared(); + size_t delim_idx = 2; // initial 0002 + + bad_input_m |= ~CT::Mask::is_equal(input[0], 0); + bad_input_m |= ~CT::Mask::is_equal(input[1], 2); + + for(size_t i = 2; i < input.size(); ++i) { + const auto is_zero_m = CT::Mask::is_zero(input[i]); + delim_idx += seen_zero_m.if_not_set_return(1); + seen_zero_m |= is_zero_m; + } + + // no zero delim -> bad padding + bad_input_m |= ~seen_zero_m; + /* + delim indicates < 8 bytes padding -> bad padding + + We require 11 here because we are counting also the 00 delim byte + */ + bad_input_m |= CT::Mask(CT::Mask::is_lt(delim_idx, 11)); + + const CT::Choice accept = !(bad_input_m.as_choice()); + + return CT::copy_output(accept, output, input, delim_idx); +} + +/* +* Return the max input size for a given key size +*/ +size_t EME_PKCS1v15::maximum_input_size(size_t keybits) const { + if(keybits / 8 > 10) { + return ((keybits / 8) - 10); + } else { + return 0; + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/eme_pkcs1/eme_pkcs.h botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_pkcs1/eme_pkcs.h --- botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/eme_pkcs1/eme_pkcs.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_pkcs1/eme_pkcs.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,32 @@ +/* +* EME PKCS#1 v1.5 +* (C) 1999-2007 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_EME_PKCS1_H_ +#define BOTAN_EME_PKCS1_H_ + +#include + +namespace Botan { + +/** +* EME from PKCS #1 v1.5 +*/ +class BOTAN_FUZZER_API EME_PKCS1v15 final : public EncryptionPaddingScheme { + private: + size_t maximum_input_size(size_t keybits) const override; + + size_t pad(std::span output, + std::span input, + size_t key_length, + RandomNumberGenerator& rng) const override; + + CT::Option unpad(std::span output, std::span input) const override; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/eme_pkcs1/info.txt botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_pkcs1/info.txt --- botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/eme_pkcs1/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_pkcs1/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,11 @@ + +PKCSV15_ENCRYPTION_PADDING -> 20250126 + +# TODO(Botan4) remove these macro +EME_PKCS1v15 -> 20131128 +EME_PKCS1 -> 20190426 + + + +name -> "PKCS #1 v1.5 encryption padding" + diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/eme_raw/eme_raw.cpp botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_raw/eme_raw.cpp --- botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/eme_raw/eme_raw.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_raw/eme_raw.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,40 @@ +/* +* (C) 2015,2016,2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan { + +size_t EME_Raw::pad(std::span output, + std::span input, + size_t key_length, + RandomNumberGenerator& rng) const { + BOTAN_UNUSED(rng); + BOTAN_ASSERT_NOMSG(input.size() < maximum_input_size(8 * key_length)); + BOTAN_ASSERT_NOMSG(output.size() >= input.size()); + copy_mem(output.first(input.size()), input); + return input.size(); +} + +CT::Option EME_Raw::unpad(std::span output, std::span input) const { + BOTAN_ASSERT_NOMSG(output.size() >= input.size()); + + if(input.empty()) { + return CT::Option(0); + } + + const size_t leading_zeros = CT::count_leading_zero_bytes(input); + return CT::copy_output(CT::Choice::yes(), output, input, leading_zeros); +} + +size_t EME_Raw::maximum_input_size(size_t keybits) const { + return keybits / 8; +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/eme_raw/eme_raw.h botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_raw/eme_raw.h --- botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/eme_raw/eme_raw.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_raw/eme_raw.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,31 @@ +/* +* (C) 2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_EME_RAW_H_ +#define BOTAN_EME_RAW_H_ + +#include + +namespace Botan { + +class EME_Raw final : public EncryptionPaddingScheme { + public: + EME_Raw() = default; + + private: + size_t maximum_input_size(size_t i) const override; + + size_t pad(std::span output, + std::span input, + size_t key_length, + RandomNumberGenerator& rng) const override; + + CT::Option unpad(std::span output, std::span input) const override; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/eme_raw/info.txt botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_raw/info.txt --- botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/eme_raw/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/eme_raw/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,7 @@ + +EME_RAW -> 20150313 + + + +name -> "EME Raw Padding" + diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/enc_padding.cpp botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/enc_padding.cpp --- botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/enc_padding.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/enc_padding.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,70 @@ +/* +* (C) 1999-2008 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include + +#if defined(BOTAN_HAS_EME_OAEP) + #include +#endif + +#if defined(BOTAN_HAS_EME_PKCS1) + #include +#endif + +#if defined(BOTAN_HAS_EME_RAW) + #include +#endif + +namespace Botan { + +std::unique_ptr EncryptionPaddingScheme::create(std::string_view algo_spec) { +#if defined(BOTAN_HAS_EME_RAW) + if(algo_spec == "Raw") { + return std::make_unique(); + } +#endif + +#if defined(BOTAN_HAS_EME_PKCS1) + // TODO(Botan4) Remove all but "PKCS1v15" + if(algo_spec == "PKCS1v15" || algo_spec == "EME-PKCS1-v1_5") { + return std::make_unique(); + } +#endif + +#if defined(BOTAN_HAS_EME_OAEP) + const SCAN_Name req(algo_spec); + + // TODO(Botan4) Remove all but "OAEP" + if(req.algo_name() == "OAEP" || req.algo_name() == "EME-OAEP" || req.algo_name() == "EME1") { + if(req.arg_count() == 1 || ((req.arg_count() == 2 || req.arg_count() == 3) && req.arg(1) == "MGF1")) { + if(auto hash = HashFunction::create(req.arg(0))) { + return std::make_unique(std::move(hash), req.arg(2, "")); + } + } else if(req.arg_count() == 2 || req.arg_count() == 3) { + auto mgf_params = parse_algorithm_name(req.arg(1)); + + if(mgf_params.size() == 2 && mgf_params[0] == "MGF1") { + auto hash = HashFunction::create(req.arg(0)); + auto mgf1_hash = HashFunction::create(mgf_params[1]); + + if(hash && mgf1_hash) { + return std::make_unique(std::move(hash), std::move(mgf1_hash), req.arg(2, "")); + } + } + } + } +#endif + + throw Algorithm_Not_Found(algo_spec); +} + +EncryptionPaddingScheme::~EncryptionPaddingScheme() = default; + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/enc_padding.h botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/enc_padding.h --- botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/enc_padding.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/enc_padding.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,68 @@ +/* +* (C) 1999-2007,2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_PUBKEY_ENCRYPTION_PADDING_H_ +#define BOTAN_PUBKEY_ENCRYPTION_PADDING_H_ + +#include +#include +#include +#include +#include + +namespace Botan { + +class RandomNumberGenerator; + +/** +* Encoding Method for Encryption +*/ +class BOTAN_TEST_API EncryptionPaddingScheme /* NOLINT(*-special-member-functions) */ { + public: + virtual ~EncryptionPaddingScheme(); + + /** + * Factory method for encryption padding schemes + * + * @param algo_spec the name of the EncryptionPaddingScheme to create + * @return pointer to newly allocated object of that type + */ + static std::unique_ptr create(std::string_view algo_spec); + + /** + * Return the maximum input size in bytes we can support + * @param keybits the size of the key in bits + * @return upper bound of input in bytes + */ + virtual size_t maximum_input_size(size_t keybits) const = 0; + + /** + * Encode an input + * @param output buffer that is written to + * @param input the plaintext + * @param key_length length of the key in bits + * @param rng a random number generator + * @return number of bytes written to output + */ + virtual size_t pad(std::span output, + std::span input, + size_t key_length, + RandomNumberGenerator& rng) const = 0; + + /** + * Decode an input + * @param output buffer where output is placed + * @param input the encoded plaintext + * @return number of bytes written to output if valid, + * or an empty option if invalid. If an empty option is + * returned the contents of output are undefined + */ + virtual CT::Option unpad(std::span output, std::span input) const = 0; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/info.txt botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/info.txt --- botan3-3.7.1+dfsg/src/lib/pk_pad/enc_padding/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/enc_padding/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,13 @@ + +RSA_ENCRYPTION_PADDING -> 20250720 + + + +name -> "RSA encryption padding schemes" +brief -> "Implementations of public key encryption padding schemes" + + + +hash +rng + diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/hash_id/info.txt botan3-3.12.0+dfsg/src/lib/pk_pad/hash_id/info.txt --- botan3-3.7.1+dfsg/src/lib/pk_pad/hash_id/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/hash_id/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + HASH_ID -> 20131128 - + name -> "Hash Function Identification" diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/info.txt botan3-3.12.0+dfsg/src/lib/pk_pad/info.txt --- botan3-3.7.1+dfsg/src/lib/pk_pad/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/info.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ - -PK_PADDING -> 20131128 - - - -name -> "Public Key Paddings" -brief -> "Implementations of public key padding schemes" - - - -hash -rng - diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/iso9796/info.txt botan3-3.12.0+dfsg/src/lib/pk_pad/iso9796/info.txt --- botan3-3.7.1+dfsg/src/lib/pk_pad/iso9796/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/iso9796/info.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ - -ISO_9796 -> 20161121 - - - -name -> "ISO-9796-2" - - - -mgf1 -hash_id - - diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/iso9796/iso9796.cpp botan3-3.12.0+dfsg/src/lib/pk_pad/iso9796/iso9796.cpp --- botan3-3.7.1+dfsg/src/lib/pk_pad/iso9796/iso9796.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/iso9796/iso9796.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,281 +0,0 @@ -/* - * ISO-9796-2 - Digital signature schemes giving message recovery schemes 2 and 3 - * (C) 2016 Tobias Niemann, Hackmanit GmbH - * - * Botan is released under the Simplified BSD License (see license.txt) - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Botan { - -namespace { - -std::vector iso9796_encoding(const std::vector& msg, - size_t output_bits, - std::unique_ptr& hash, - size_t SALT_SIZE, - bool implicit, - RandomNumberGenerator& rng) { - const size_t output_length = (output_bits + 7) / 8; - - //set trailer length - const size_t tLength = (implicit) ? 1 : 2; - - const size_t HASH_SIZE = hash->output_length(); - - if(output_length <= HASH_SIZE + SALT_SIZE + tLength) { - throw Encoding_Error("ISO9796-2::encoding_of: Output length is too small"); - } - - //calculate message capacity - const size_t capacity = output_length - HASH_SIZE - SALT_SIZE - tLength - 1; - - //msg1 is the recoverable and hmsg2 is the hash of the unrecoverable message part. - std::vector msg1; - if(msg.size() > capacity) { - msg1 = std::vector(msg.begin(), msg.begin() + capacity); - hash->update(std::span(msg).subspan(capacity)); - } else { - msg1 = msg; - } - const std::vector hmsg2 = hash->final_stdvec(); - - //compute H(C||msg1 ||H(msg2)||S) - const size_t msgLength = msg1.size(); - const auto salt = rng.random_vec>(SALT_SIZE); - hash->update_be(static_cast(msgLength) * 8); - hash->update(msg1); - hash->update(hmsg2); - hash->update(salt); - const std::vector H = hash->final_stdvec(); - - std::vector EM(output_length); - - BufferStuffer stuffer(EM); - stuffer.append(0x00, stuffer.remaining_capacity() - (HASH_SIZE + SALT_SIZE + tLength + msgLength + 1)); - stuffer.append(0x01); - stuffer.append(msg1); - stuffer.append(salt); - - //apply mask - mgf1_mask(*hash, H.data(), HASH_SIZE, EM.data(), output_length - HASH_SIZE - tLength); - - //clear the leftmost bit (confer bouncy castle) - EM[0] &= 0x7F; - - stuffer.append(H); - - // set implicit/ISO trailer - - if(implicit) { - stuffer.append(0xBC); - } else { - const uint8_t hash_id = ieee1363_hash_id(hash->name()); - if(!hash_id) { - throw Encoding_Error("ISO9796-2::encoding_of: no hash identifier for " + hash->name()); - } - stuffer.append(hash_id); - stuffer.append(0xCC); - } - - BOTAN_ASSERT_NOMSG(stuffer.full()); - - return EM; -} - -bool iso9796_verification(const std::vector& const_coded, - const std::vector& raw, - size_t key_bits, - std::unique_ptr& hash, - size_t SALT_SIZE) { - const size_t HASH_SIZE = hash->output_length(); - const size_t KEY_BYTES = (key_bits + 7) / 8; - - if(const_coded.size() != KEY_BYTES) { - return false; - } - //get trailer length - size_t tLength; - if(const_coded[const_coded.size() - 1] == 0xBC) { - tLength = 1; - } else { - uint8_t hash_id = ieee1363_hash_id(hash->name()); - if((!const_coded[const_coded.size() - 2]) || (const_coded[const_coded.size() - 2] != hash_id) || - (const_coded[const_coded.size() - 1] != 0xCC)) { - return false; //in case of wrong ISO trailer. - } - tLength = 2; - } - - std::vector coded = const_coded; - - CT::poison(coded.data(), coded.size()); - //remove mask - uint8_t* DB = coded.data(); - const size_t DB_size = coded.size() - HASH_SIZE - tLength; - - const uint8_t* H = &coded[DB_size]; - - mgf1_mask(*hash, H, HASH_SIZE, DB, DB_size); - //clear the leftmost bit (confer bouncy castle) - DB[0] &= 0x7F; - - //recover msg1 and salt - size_t msg1_offset = 1; - - auto waiting_for_delim = CT::Mask::set(); - auto bad_input = CT::Mask::cleared(); - - for(size_t j = 0; j < DB_size; ++j) { - const auto is_zero = CT::Mask::is_zero(DB[j]); - const auto is_one = CT::Mask::is_equal(DB[j], 0x01); - - const auto add_m = waiting_for_delim & is_zero; - - bad_input |= waiting_for_delim & ~(is_zero | is_one); - msg1_offset += add_m.if_set_return(1); - - waiting_for_delim &= is_zero; - } - - //invalid, if delimiter 0x01 was not found or msg1_offset is too big - bad_input |= waiting_for_delim; - bad_input |= CT::Mask::is_lt(coded.size(), tLength + HASH_SIZE + msg1_offset + SALT_SIZE); - - //in case that msg1_offset is too big, just continue with offset = 0. - msg1_offset = CT::Mask::expand(bad_input.value()).if_not_set_return(msg1_offset); - - CT::unpoison(coded.data(), coded.size()); - CT::unpoison(msg1_offset); - - std::vector msg1(coded.begin() + msg1_offset, coded.end() - tLength - HASH_SIZE - SALT_SIZE); - std::vector salt(coded.begin() + msg1_offset + msg1.size(), coded.end() - tLength - HASH_SIZE); - - //compute H2(C||msg1||H(msg2)||S*). * indicates a recovered value - const size_t capacity = (key_bits - 2 + 7) / 8 - HASH_SIZE - SALT_SIZE - tLength - 1; - std::vector msg1raw; - if(raw.size() > capacity) { - msg1raw = std::vector(raw.begin(), raw.begin() + capacity); - hash->update(std::span(raw).subspan(capacity)); - } else { - msg1raw = raw; - } - const std::vector hmsg2 = hash->final_stdvec(); - - const uint64_t msg1rawLength = msg1raw.size(); - hash->update_be(msg1rawLength * 8); - hash->update(msg1raw); - hash->update(hmsg2); - hash->update(salt); - std::vector H3 = hash->final_stdvec(); - - //compute H3(C*||msg1*||H(msg2)||S*) * indicates a recovered value - const uint64_t msgLength = msg1.size(); - hash->update_be(msgLength * 8); - hash->update(msg1); - hash->update(hmsg2); - hash->update(salt); - std::vector H2 = hash->final_stdvec(); - - //check if H3 == H2 - bad_input |= CT::is_not_equal(H3.data(), H2.data(), HASH_SIZE); - - CT::unpoison(bad_input); - return (bad_input.as_bool() == false); -} - -} // namespace - -/* - * ISO-9796-2 signature scheme 2 - * DS 2 is probabilistic - */ -void ISO_9796_DS2::update(const uint8_t input[], size_t length) { - //need to buffer message completely, before digest - m_msg_buffer.insert(m_msg_buffer.end(), input, input + length); -} - -/* - * Return the raw (unencoded) data - */ -std::vector ISO_9796_DS2::raw_data() { - std::vector retbuffer = m_msg_buffer; - m_msg_buffer.clear(); - return retbuffer; -} - -/* - * ISO-9796-2 scheme 2 encode operation - */ -std::vector ISO_9796_DS2::encoding_of(const std::vector& msg, - size_t output_bits, - RandomNumberGenerator& rng) { - return iso9796_encoding(msg, output_bits, m_hash, m_SALT_SIZE, m_implicit, rng); -} - -/* - * ISO-9796-2 scheme 2 verify operation - */ -bool ISO_9796_DS2::verify(const std::vector& const_coded, const std::vector& raw, size_t key_bits) { - return iso9796_verification(const_coded, raw, key_bits, m_hash, m_SALT_SIZE); -} - -/* - * Return the SCAN name - */ -std::string ISO_9796_DS2::name() const { - return fmt("ISO_9796_DS2({},{},{})", m_hash->name(), (m_implicit ? "imp" : "exp"), m_SALT_SIZE); -} - -/* - * ISO-9796-2 signature scheme 3 - * DS 3 is deterministic and equals DS2 without salt - */ -void ISO_9796_DS3::update(const uint8_t input[], size_t length) { - //need to buffer message completely, before digest - m_msg_buffer.insert(m_msg_buffer.end(), input, input + length); -} - -/* - * Return the raw (unencoded) data - */ -std::vector ISO_9796_DS3::raw_data() { - std::vector retbuffer = m_msg_buffer; - m_msg_buffer.clear(); - return retbuffer; -} - -/* - * ISO-9796-2 scheme 3 encode operation - */ -std::vector ISO_9796_DS3::encoding_of(const std::vector& msg, - size_t output_bits, - RandomNumberGenerator& rng) { - return iso9796_encoding(msg, output_bits, m_hash, 0, m_implicit, rng); -} - -/* - * ISO-9796-2 scheme 3 verify operation - */ -bool ISO_9796_DS3::verify(const std::vector& const_coded, const std::vector& raw, size_t key_bits) { - return iso9796_verification(const_coded, raw, key_bits, m_hash, 0); -} - -/* - * Return the SCAN name - */ -std::string ISO_9796_DS3::name() const { - return fmt("ISO_9796_DS3({},{})", m_hash->name(), (m_implicit ? "imp" : "exp")); -} - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/iso9796/iso9796.h botan3-3.12.0+dfsg/src/lib/pk_pad/iso9796/iso9796.h --- botan3-3.7.1+dfsg/src/lib/pk_pad/iso9796/iso9796.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/iso9796/iso9796.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,91 +0,0 @@ -/* - * ISO-9796-2 - Digital signature schemes giving message recovery schemes 2 and 3 - * (C) 2016 Tobias Niemann, Hackmanit GmbH - * - * Botan is released under the Simplified BSD License (see license.txt) - */ - -#ifndef BOTAN_ISO9796_H_ -#define BOTAN_ISO9796_H_ - -#include -#include - -namespace Botan { - -/** -* ISO-9796-2 - Digital signature scheme 2 (probabilistic) -*/ -class ISO_9796_DS2 final : public EMSA { - public: - /** - * @param hash function to use - * @param implicit whether or not the trailer is implicit - */ - explicit ISO_9796_DS2(std::unique_ptr hash, bool implicit = false) : - m_hash(std::move(hash)), m_implicit(implicit), m_SALT_SIZE(hash->output_length()) {} - - /** - * @param hash function to use - * @param implicit whether or not the trailer is implicit - * @param salt_size size of the salt to use in bytes - */ - ISO_9796_DS2(std::unique_ptr hash, bool implicit, size_t salt_size) : - m_hash(std::move(hash)), m_implicit(implicit), m_SALT_SIZE(salt_size) {} - - std::string hash_function() const override { return m_hash->name(); } - - std::string name() const override; - - private: - void update(const uint8_t input[], size_t length) override; - - std::vector raw_data() override; - - std::vector encoding_of(const std::vector& msg, - size_t output_bits, - RandomNumberGenerator& rng) override; - - bool verify(const std::vector& coded, const std::vector& raw, size_t key_bits) override; - - std::unique_ptr m_hash; - bool m_implicit; - size_t m_SALT_SIZE; - std::vector m_msg_buffer; -}; - -/** -* ISO-9796-2 - Digital signature scheme 3 (deterministic) -*/ -class ISO_9796_DS3 final : public EMSA { - public: - /** - * @param hash function to use - * @param implicit whether or not the trailer is implicit - */ - ISO_9796_DS3(std::unique_ptr hash, bool implicit = false) : - m_hash(std::move(hash)), m_implicit(implicit) {} - - std::string name() const override; - - std::string hash_function() const override { return m_hash->name(); } - - private: - void update(const uint8_t input[], size_t length) override; - - std::vector raw_data() override; - - std::vector encoding_of(const std::vector& msg, - size_t output_bits, - RandomNumberGenerator& rng) override; - - bool verify(const std::vector& coded, const std::vector& raw, size_t key_bits) override; - - std::unique_ptr m_hash; - bool m_implicit; - std::vector m_msg_buffer; -}; - -} // namespace Botan - -#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/mgf1/mgf1.cpp botan3-3.12.0+dfsg/src/lib/pk_pad/mgf1/mgf1.cpp --- botan3-3.7.1+dfsg/src/lib/pk_pad/mgf1/mgf1.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/mgf1/mgf1.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,5 @@ /* -* MGF1 -* (C) 1999-2007 Jack Lloyd +* (C) 1999-2007,2025 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -8,23 +7,27 @@ #include #include +#include #include namespace Botan { -void mgf1_mask(HashFunction& hash, const uint8_t in[], size_t in_len, uint8_t out[], size_t out_len) { +void mgf1_mask(HashFunction& hash, std::span input, std::span output) { uint32_t counter = 0; - std::vector buffer(hash.output_length()); - while(out_len) { - hash.update(in, in_len); + const size_t hlen = hash.output_length(); + + BOTAN_ASSERT_NOMSG(hlen > 0); + + std::vector buffer(hlen); + while(!output.empty()) { + hash.update(input); hash.update_be(counter); - hash.final(buffer.data()); + hash.final(buffer); - const size_t xored = std::min(buffer.size(), out_len); - xor_buf(out, buffer.data(), xored); - out += xored; - out_len -= xored; + const size_t xored = std::min(buffer.size(), output.size()); + xor_buf(output.first(xored), std::span{buffer}.first(xored)); + output = output.subspan(xored); ++counter; } diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/mgf1/mgf1.h botan3-3.12.0+dfsg/src/lib/pk_pad/mgf1/mgf1.h --- botan3-3.7.1+dfsg/src/lib/pk_pad/mgf1/mgf1.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/mgf1/mgf1.h 2026-05-07 01:38:28.000000000 +0000 @@ -18,16 +18,10 @@ /** * MGF1 from PKCS #1 v2.0 * @param hash hash function to use -* @param in input buffer -* @param in_len size of the input buffer in bytes -* @param out output buffer. The buffer is XORed with the output of MGF1. -* @param out_len size of the output buffer in bytes +* @param input - the input buffer +* @param output - the output buffer. The buffer is XORed with the output of MGF1. */ -void mgf1_mask(HashFunction& hash, const uint8_t in[], size_t in_len, uint8_t out[], size_t out_len); - -inline void mgf1_mask(HashFunction& hash, std::span input, std::span output) { - mgf1_mask(hash, input.data(), input.size(), output.data(), output.size()); -} +void mgf1_mask(HashFunction& hash, std::span input, std::span output); } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/raw_hash/info.txt botan3-3.12.0+dfsg/src/lib/pk_pad/raw_hash/info.txt --- botan3-3.7.1+dfsg/src/lib/pk_pad/raw_hash/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/raw_hash/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + RAW_HASH_FN -> 20230221 - + name -> "Raw Hash Function" diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/raw_hash/raw_hash.cpp botan3-3.12.0+dfsg/src/lib/pk_pad/raw_hash/raw_hash.cpp --- botan3-3.7.1+dfsg/src/lib/pk_pad/raw_hash/raw_hash.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/raw_hash/raw_hash.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,7 @@ #include #include +#include namespace Botan { diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/raw_hash/raw_hash.h botan3-3.12.0+dfsg/src/lib/pk_pad/raw_hash/raw_hash.h --- botan3-3.7.1+dfsg/src/lib/pk_pad/raw_hash/raw_hash.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/raw_hash/raw_hash.h 2026-05-07 01:38:28.000000000 +0000 @@ -22,7 +22,8 @@ */ class RawHashFunction final : public HashFunction { public: - RawHashFunction(std::unique_ptr hash) : RawHashFunction(hash->name(), hash->output_length()) {} + explicit RawHashFunction(std::unique_ptr hash) : + RawHashFunction(hash->name(), hash->output_length()) {} RawHashFunction(std::string_view name, size_t output_length) : m_name(name), m_output_length(output_length) {} diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_pkcs1/info.txt botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_pkcs1/info.txt --- botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_pkcs1/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_pkcs1/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ + +EMSA_PKCS1 -> 20140118 +PKCSV15_SIGNATURE_PADDING -> 20250126 + + + +name -> "PKCS #1 v1.5 signature padding" + + + +hash_id + diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_pkcs1/pkcs1_sig_padding.cpp botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_pkcs1/pkcs1_sig_padding.cpp --- botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_pkcs1/pkcs1_sig_padding.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_pkcs1/pkcs1_sig_padding.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,151 @@ +/* +* PKCS #1 v1.5 signature padding +* (C) 1999-2008 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include +#include + +namespace Botan { + +namespace { + +std::vector pkcs1v15_sig_encoding(std::span msg, + size_t output_bits, + std::span hash_id) { + const size_t output_length = output_bits / 8; + + if(output_length < hash_id.size() + msg.size() + 2 + 8) { + throw Encoding_Error("pkcs1v15_sig_encoding: Output length is too small"); + } + + std::vector padded(output_length); + BufferStuffer stuffer(padded); + + stuffer.append(0x01); + stuffer.append(0xFF, stuffer.remaining_capacity() - (1 + hash_id.size() + msg.size())); + stuffer.append(0x00); + stuffer.append(hash_id); + stuffer.append(msg); + BOTAN_ASSERT_NOMSG(stuffer.full()); + + return padded; +} + +} // namespace + +void PKCS1v15_SignaturePaddingScheme::update(const uint8_t input[], size_t length) { + m_hash->update(input, length); +} + +std::vector PKCS1v15_SignaturePaddingScheme::raw_data() { + return m_hash->final_stdvec(); +} + +std::vector PKCS1v15_SignaturePaddingScheme::encoding_of(std::span msg, + size_t output_bits, + RandomNumberGenerator& /*rng*/) { + if(msg.size() != m_hash->output_length()) { + throw Encoding_Error("PKCS1v15_SignaturePaddingScheme::encoding_of: Bad input length"); + } + + return pkcs1v15_sig_encoding(msg, output_bits, m_hash_id); +} + +bool PKCS1v15_SignaturePaddingScheme::verify(std::span coded, + std::span raw, + size_t key_bits) { + if(raw.size() != m_hash->output_length()) { + return false; + } + + try { + const auto pkcs1 = pkcs1v15_sig_encoding(raw, key_bits, m_hash_id); + return constant_time_compare(coded, pkcs1); + } catch(...) { + return false; + } +} + +PKCS1v15_SignaturePaddingScheme::PKCS1v15_SignaturePaddingScheme(std::unique_ptr hash) : + m_hash(std::move(hash)) { + m_hash_id = pkcs_hash_id(m_hash->name()); +} + +std::string PKCS1v15_SignaturePaddingScheme::hash_function() const { + return m_hash->name(); +} + +std::string PKCS1v15_SignaturePaddingScheme::name() const { + return fmt("PKCS1v15({})", m_hash->name()); +} + +std::string PKCS1v15_Raw_SignaturePaddingScheme::name() const { + if(m_hash_name.empty()) { + return "PKCS1v15(Raw)"; + } else { + return fmt("PKCS1v15(Raw,{})", m_hash_name); + } +} + +PKCS1v15_Raw_SignaturePaddingScheme::PKCS1v15_Raw_SignaturePaddingScheme() : m_hash_output_len(0) { + // m_hash_id, m_hash_name left empty +} + +PKCS1v15_Raw_SignaturePaddingScheme::PKCS1v15_Raw_SignaturePaddingScheme(std::string_view hash_algo) { + std::unique_ptr hash(HashFunction::create_or_throw(hash_algo)); + m_hash_id = pkcs_hash_id(hash_algo); + m_hash_name = hash->name(); + m_hash_output_len = hash->output_length(); +} + +void PKCS1v15_Raw_SignaturePaddingScheme::update(const uint8_t input[], size_t length) { + m_message += std::make_pair(input, length); + // A sanity check to prevent someone from accidentally feeding an entire message + // into PKCS1v15(Raw), which would have to be buffered in memory + if(m_message.size() > 16384 / 8) { + throw Invalid_Argument("PKCS1v15(Raw) message too long"); + } +} + +std::vector PKCS1v15_Raw_SignaturePaddingScheme::raw_data() { + std::vector ret; + std::swap(ret, m_message); + + if(m_hash_output_len > 0 && ret.size() != m_hash_output_len) { + throw Encoding_Error("PKCS1v15_Raw_SignaturePaddingScheme::encoding_of: Bad input length"); + } + + return ret; +} + +std::vector PKCS1v15_Raw_SignaturePaddingScheme::encoding_of(std::span msg, + size_t output_bits, + RandomNumberGenerator& /*rng*/) { + return pkcs1v15_sig_encoding(msg, output_bits, m_hash_id); +} + +bool PKCS1v15_Raw_SignaturePaddingScheme::verify(std::span coded, + std::span raw, + size_t key_bits) { + if(m_hash_output_len > 0 && raw.size() != m_hash_output_len) { + return false; + } + + try { + const auto pkcs1 = pkcs1v15_sig_encoding(raw, key_bits, m_hash_id); + return constant_time_compare(coded, pkcs1); + } catch(...) { + return false; + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_pkcs1/pkcs1_sig_padding.h botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_pkcs1/pkcs1_sig_padding.h --- botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_pkcs1/pkcs1_sig_padding.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_pkcs1/pkcs1_sig_padding.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,91 @@ +/* +* PKCS #1 v1.5 signature padding +* (C) 1999-2008 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_PKCS1V15_SIGNATURE_PADDING_H_ +#define BOTAN_PKCS1V15_SIGNATURE_PADDING_H_ + +#include + +#include +#include +#include +#include + +namespace Botan { + +class HashFunction; + +/** +* PKCS #1 v1.5 signature padding +* aka PKCS #1 block type 1 +* aka EMSA3 from IEEE 1363 +*/ +class PKCS1v15_SignaturePaddingScheme final : public SignaturePaddingScheme { + public: + /** + * @param hash the hash function to use + */ + explicit PKCS1v15_SignaturePaddingScheme(std::unique_ptr hash); + + void update(const uint8_t input[], size_t length) override; + + std::vector raw_data() override; + + std::vector encoding_of(std::span msg, + size_t output_bits, + RandomNumberGenerator& rng) override; + + bool verify(std::span coded, std::span raw, size_t key_bits) override; + + std::string name() const override; + + std::string hash_function() const override; + + private: + std::unique_ptr m_hash; + std::vector m_hash_id; +}; + +/** +* PKCS1v15_SignaturePaddingScheme_Raw which is PKCS1v15_SignaturePaddingScheme without a hash or digest id +* (which according to QCA docs is "identical to PKCS#11's CKM_RSA_PKCS +* mechanism", something I have not confirmed) +*/ +class PKCS1v15_Raw_SignaturePaddingScheme final : public SignaturePaddingScheme { + public: + void update(const uint8_t input[], size_t length) override; + + std::vector raw_data() override; + + std::vector encoding_of(std::span msg, + size_t output_bits, + RandomNumberGenerator& rng) override; + + bool verify(std::span coded, std::span raw, size_t key_bits) override; + + PKCS1v15_Raw_SignaturePaddingScheme(); + + /** + * @param hash_algo the digest id for that hash is included in + * the signature. + */ + explicit PKCS1v15_Raw_SignaturePaddingScheme(std::string_view hash_algo); + + std::string hash_function() const override { return m_hash_name; } + + std::string name() const override; + + private: + size_t m_hash_output_len = 0; + std::string m_hash_name; + std::vector m_hash_id; + std::vector m_message; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_pssr/info.txt botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_pssr/info.txt --- botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_pssr/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_pssr/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,13 @@ + +EMSA_PSSR -> 20131128 +PSS -> 20250130 + + + +name -> "PSS" +brief -> "PSS signature padding from PKCS1v2.0" + + + +mgf1 + diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_pssr/pssr.cpp botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_pssr/pssr.cpp --- botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_pssr/pssr.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_pssr/pssr.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,256 @@ +/* +* PSSR +* (C) 1999-2007,2017,2023 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Botan { + +namespace { + +/* +* PSSR Encode Operation +*/ +std::vector pss_encode(HashFunction& hash, + std::span msg, + std::span salt, + size_t output_bits) { + const size_t HASH_SIZE = hash.output_length(); + + if(msg.size() != HASH_SIZE) { + throw Encoding_Error("Cannot encode PSS string, input length invalid for hash"); + } + if(output_bits < 8 * HASH_SIZE + 8 * salt.size() + 9) { + throw Encoding_Error("Cannot encode PSS string, output length too small"); + } + + const size_t output_length = ceil_tobytes(output_bits); + const uint8_t db0_mask = 0xFF >> (8 * output_length - output_bits); + + std::array padding = {0}; + hash.update(padding); + hash.update(msg); + hash.update(salt); + std::vector H = hash.final_stdvec(); + + const size_t db_len = output_length - HASH_SIZE - 1; + std::vector EM(output_length); + + BufferStuffer stuffer(EM); + stuffer.append(0x00, stuffer.remaining_capacity() - (1 + salt.size() + H.size() + 1)); + stuffer.append(0x01); + stuffer.append(salt); + + mgf1_mask(hash, H, std::span{EM}.first(db_len)); + EM[0] &= db0_mask; + + stuffer.append(H); + stuffer.append(0xBC); + BOTAN_ASSERT_NOMSG(stuffer.full()); + + return EM; +} + +bool pss_verify(HashFunction& hash, + std::span pss_repr, + std::span message_hash, + size_t key_bits, + size_t* out_salt_size) { + const size_t HASH_SIZE = hash.output_length(); + const size_t key_bytes = ceil_tobytes(key_bits); + + if(key_bits < 8 * HASH_SIZE + 9) { + return false; + } + + if(message_hash.size() != HASH_SIZE) { + return false; + } + + if(pss_repr.size() > key_bytes || pss_repr.size() <= 1) { + return false; + } + + if(pss_repr[pss_repr.size() - 1] != 0xBC) { + return false; + } + + std::vector coded; + if(pss_repr.size() < key_bytes) { + coded.resize(key_bytes); + BufferStuffer stuffer(coded); + stuffer.append(0x00, key_bytes - pss_repr.size()); + stuffer.append(pss_repr); + } else { + coded.assign(pss_repr.begin(), pss_repr.end()); + } + + // We have to check this after potential zero padding above + const size_t top_bits = 8 * ((key_bits + 7) / 8) - key_bits; + if(top_bits > 8 - high_bit(coded[0])) { + return false; + } + + uint8_t* DB = coded.data(); + const size_t DB_size = coded.size() - HASH_SIZE - 1; + + const uint8_t* H = &coded[DB_size]; + const size_t H_size = HASH_SIZE; + + mgf1_mask(hash, {H, H_size}, {DB, DB_size}); + DB[0] &= 0xFF >> top_bits; + + size_t salt_offset = 0; + for(size_t j = 0; j != DB_size; ++j) { + if(DB[j] == 0x01) { + salt_offset = j + 1; + break; + } + if(DB[j] != 0x00) { + return false; + } + } + if(salt_offset == 0) { + return false; + } + + const size_t salt_size = DB_size - salt_offset; + + std::array padding = {0}; + hash.update(padding); + hash.update(message_hash); + hash.update(&DB[salt_offset], salt_size); + + const std::vector H2 = hash.final_stdvec(); + + const bool ok = CT::is_equal(H, H2.data(), HASH_SIZE).as_bool(); + + if(ok && out_salt_size != nullptr) { + *out_salt_size = salt_size; + } + + return ok; +} + +} // namespace + +PSSR::PSSR(std::unique_ptr hash) : + m_hash(std::move(hash)), m_salt_size(m_hash->output_length()), m_required_salt_len(false) {} + +PSSR::PSSR(std::unique_ptr hash, size_t salt_size) : + m_hash(std::move(hash)), m_salt_size(salt_size), m_required_salt_len(true) {} + +/* +* PSSR Update Operation +*/ +void PSSR::update(const uint8_t input[], size_t length) { + m_hash->update(input, length); +} + +/* +* Return the raw (unencoded) data +*/ +std::vector PSSR::raw_data() { + return m_hash->final_stdvec(); +} + +std::vector PSSR::encoding_of(std::span msg, size_t output_bits, RandomNumberGenerator& rng) { + const auto salt = rng.random_vec>(m_salt_size); + return pss_encode(*m_hash, msg, salt, output_bits); +} + +/* +* PSSR Decode/Verify Operation +*/ +bool PSSR::verify(std::span coded, std::span raw, size_t key_bits) { + size_t salt_size = 0; + const bool ok = pss_verify(*m_hash, coded, raw, key_bits, &salt_size); + + if(m_required_salt_len && salt_size != m_salt_size) { + return false; + } + + return ok; +} + +std::string PSSR::hash_function() const { + return m_hash->name(); +} + +std::string PSSR::name() const { + return fmt("PSS({},MGF1,{})", m_hash->name(), m_salt_size); +} + +PSS_Raw::PSS_Raw(std::unique_ptr hash) : + m_hash(std::move(hash)), m_salt_size(m_hash->output_length()), m_required_salt_len(false) {} + +PSS_Raw::PSS_Raw(std::unique_ptr hash, size_t salt_size) : + m_hash(std::move(hash)), m_salt_size(salt_size), m_required_salt_len(true) {} + +/* +* PSS_Raw Update Operation +*/ +void PSS_Raw::update(const uint8_t input[], size_t length) { + m_msg.insert(m_msg.end(), input, input + length); + + if(m_msg.size() > m_hash->output_length()) { + throw Encoding_Error("PSS_Raw: Input length exceeded hash output"); + } +} + +/* +* Return the raw (unencoded) data +*/ +std::vector PSS_Raw::raw_data() { + std::vector ret; + std::swap(ret, m_msg); + + if(ret.size() != m_hash->output_length()) { + throw Encoding_Error("PSS_Raw Bad input length, did not match hash"); + } + + return ret; +} + +std::vector PSS_Raw::encoding_of(std::span msg, + size_t output_bits, + RandomNumberGenerator& rng) { + const auto salt = rng.random_vec>(m_salt_size); + return pss_encode(*m_hash, msg, salt, output_bits); +} + +/* +* PSS_Raw Decode/Verify Operation +*/ +bool PSS_Raw::verify(std::span coded, std::span raw, size_t key_bits) { + size_t salt_size = 0; + const bool ok = pss_verify(*m_hash, coded, raw, key_bits, &salt_size); + + if(m_required_salt_len && salt_size != m_salt_size) { + return false; + } + + return ok; +} + +std::string PSS_Raw::hash_function() const { + return m_hash->name(); +} + +std::string PSS_Raw::name() const { + return fmt("PSS_Raw({},MGF1,{})", m_hash->name(), m_salt_size); +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_pssr/pssr.h botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_pssr/pssr.h --- botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_pssr/pssr.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_pssr/pssr.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,97 @@ +/* +* PSSR +* (C) 1999-2007 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_PSSR_H_ +#define BOTAN_PSSR_H_ + +#include +#include +#include +#include + +namespace Botan { + +class RandomNumberGenerator; +class HashFunction; + +/** +* PSSR (called EMSA4 in IEEE 1363 and in old versions of the library) +*/ +class PSSR final : public SignaturePaddingScheme { + public: + /** + * @param hash the hash function to use + */ + explicit PSSR(std::unique_ptr hash); + + /** + * @param hash the hash function to use + * @param salt_size the size of the salt to use in bytes + */ + PSSR(std::unique_ptr hash, size_t salt_size); + + std::string name() const override; + + std::string hash_function() const override; + + private: + void update(const uint8_t input[], size_t length) override; + + std::vector raw_data() override; + + std::vector encoding_of(std::span msg, + size_t output_bits, + RandomNumberGenerator& rng) override; + + bool verify(std::span coded, std::span raw, size_t key_bits) override; + + std::unique_ptr m_hash; + size_t m_salt_size; + bool m_required_salt_len; +}; + +/** +* PSS_Raw +* This accepts a pre-hashed buffer +*/ +class PSS_Raw final : public SignaturePaddingScheme { + public: + /** + * @param hash the hash function to use + */ + explicit PSS_Raw(std::unique_ptr hash); + + /** + * @param hash the hash function to use + * @param salt_size the size of the salt to use in bytes + */ + PSS_Raw(std::unique_ptr hash, size_t salt_size); + + std::string hash_function() const override; + + std::string name() const override; + + private: + void update(const uint8_t input[], size_t length) override; + + std::vector raw_data() override; + + std::vector encoding_of(std::span msg, + size_t output_bits, + RandomNumberGenerator& rng) override; + + bool verify(std::span coded, std::span raw, size_t key_bits) override; + + std::unique_ptr m_hash; + std::vector m_msg; + size_t m_salt_size; + bool m_required_salt_len; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_raw/info.txt botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_raw/info.txt --- botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_raw/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_raw/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,8 @@ + +EMSA_RAW -> 20131128 +RAW_SIGNATURE_PADDING -> 20250720 + + + +name -> "EMSA Raw Padding" + diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_raw/raw_sig_padding.cpp botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_raw/raw_sig_padding.cpp --- botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_raw/raw_sig_padding.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_raw/raw_sig_padding.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,75 @@ +/* +* (C) 1999-2007,2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include + +namespace Botan { + +std::string SignRawBytes::name() const { + if(m_expected_size > 0) { + return fmt("Raw({})", m_expected_size); + } + return "Raw"; +} + +void SignRawBytes::update(const uint8_t input[], size_t length) { + // The input is just accumulated into the buffer + m_message += std::make_pair(input, length); +} + +std::vector SignRawBytes::raw_data() { + /* + * Return the provided data. If a specific length was indicated (eg for a prehash), + * check that. + */ + + if(m_expected_size > 0 && m_message.size() != m_expected_size) { + throw Invalid_Argument( + fmt("SignRawBytes was configured to use a {} byte hash but instead was used for a {} byte hash", + m_expected_size, + m_message.size())); + } + + std::vector output; + std::swap(m_message, output); + return output; +} + +std::vector SignRawBytes::encoding_of(std::span msg, + size_t /*output_bits*/, + RandomNumberGenerator& /*rng*/) { + if(m_expected_size > 0 && msg.size() != m_expected_size) { + throw Invalid_Argument( + fmt("SignRawBytes was configured to use a {} byte hash but instead was used for a {} byte hash", + m_expected_size, + msg.size())); + } + + return std::vector(msg.begin(), msg.end()); +} + +bool SignRawBytes::verify(std::span coded, std::span raw, size_t /*key_bits*/) { + if(m_expected_size > 0 && raw.size() != m_expected_size) { + return false; + } + + if(raw.size() > coded.size()) { + // handle zero padding differences + const size_t expected_lz = raw.size() - coded.size(); + auto zeros_ok = CT::all_zeros(raw.data(), expected_lz); + auto contents_ok = CT::is_equal(coded.data(), raw.data() + expected_lz, coded.size()); + return (zeros_ok & contents_ok).as_bool(); + } + + return constant_time_compare(coded, raw); +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_raw/raw_sig_padding.h botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_raw/raw_sig_padding.h --- botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_raw/raw_sig_padding.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_raw/raw_sig_padding.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,47 @@ +/* +* (C) 1999-2007 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SIGN_RAW_BYTES_H_ +#define BOTAN_SIGN_RAW_BYTES_H_ + +#include +#include +#include + +namespace Botan { + +class RandomNumberGenerator; + +/** +* This class sign inputs directly with no intermediate hashing or padding. +* +* This is insecure unless used very carefully. +*/ +class SignRawBytes final : public SignaturePaddingScheme { + public: + explicit SignRawBytes(size_t expected_hash_size = 0) : m_expected_size(expected_hash_size) {} + + std::string hash_function() const override { return "Raw"; } + + std::string name() const override; + + private: + void update(const uint8_t input[], size_t length) override; + std::vector raw_data() override; + + std::vector encoding_of(std::span raw, + size_t key_bits, + RandomNumberGenerator& rng) override; + + bool verify(std::span coded, std::span raw, size_t key_bits) override; + + const size_t m_expected_size; + std::vector m_message; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_x931/info.txt botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_x931/info.txt --- botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_x931/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_x931/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ + +EMSA_X931 -> 20140118 +X931_SIGNATURE_PADDING -> 20250720 + + + +name -> "X9.31" + + + +hash_id + diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_x931/x931_sig_padding.cpp botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_x931/x931_sig_padding.cpp --- botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_x931/x931_sig_padding.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_x931/x931_sig_padding.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,103 @@ +/* +* (C) 1999-2007 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include +#include + +namespace Botan { + +namespace { + +std::vector x931_encoding(std::span msg, + size_t output_bits, + std::span empty_hash, + uint8_t hash_id) { + const size_t HASH_SIZE = empty_hash.size(); + + const size_t output_length = (output_bits + 1) / 8; + + if(msg.size() != HASH_SIZE) { + throw Encoding_Error("X931_SignaturePadding::encoding_of: Bad input length"); + } + if(output_length < HASH_SIZE + 4) { + throw Encoding_Error("X931_SignaturePadding::encoding_of: Output length is too small"); + } + + const bool empty_input = constant_time_compare(msg, empty_hash); + + std::vector output(output_length); + BufferStuffer stuffer(output); + + stuffer.append(empty_input ? 0x4B : 0x6B); + stuffer.append(0xBB, stuffer.remaining_capacity() - (1 + msg.size() + 2)); + stuffer.append(0xBA); + stuffer.append(msg); + stuffer.append(hash_id); + stuffer.append(0xCC); + BOTAN_ASSERT_NOMSG(stuffer.full()); + + return output; +} + +} // namespace + +std::string X931_SignaturePadding::hash_function() const { + return m_hash->name(); +} + +std::string X931_SignaturePadding::name() const { + return fmt("X9.31({})", m_hash->name()); +} + +void X931_SignaturePadding::update(const uint8_t input[], size_t length) { + m_hash->update(input, length); +} + +std::vector X931_SignaturePadding::raw_data() { + return m_hash->final_stdvec(); +} + +/* +* X931_SignaturePadding Encode Operation +*/ +std::vector X931_SignaturePadding::encoding_of(std::span msg, + size_t output_bits, + RandomNumberGenerator& /*rng*/) { + return x931_encoding(msg, output_bits, m_empty_hash, m_hash_id); +} + +/* +* X931_SignaturePadding Verify Operation +*/ +bool X931_SignaturePadding::verify(std::span coded, std::span raw, size_t key_bits) { + try { + const auto x931 = x931_encoding(raw, key_bits, m_empty_hash, m_hash_id); + return constant_time_compare(coded, x931); + } catch(...) { + return false; + } +} + +/* +* X931_SignaturePadding Constructor +*/ +X931_SignaturePadding::X931_SignaturePadding(std::unique_ptr hash) : m_hash(std::move(hash)) { + m_empty_hash = m_hash->final_stdvec(); + + m_hash_id = ieee1363_hash_id(m_hash->name()); + + if(m_hash_id == 0) { + throw Encoding_Error("X931_SignaturePadding no hash identifier for " + m_hash->name()); + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_x931/x931_sig_padding.h botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_x931/x931_sig_padding.h --- botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/emsa_x931/x931_sig_padding.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/emsa_x931/x931_sig_padding.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,52 @@ +/* +* (C) 1999-2007 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_X931_SIGNATURE_PADDING_SCHEME_H_ +#define BOTAN_X931_SIGNATURE_PADDING_SCHEME_H_ + +#include + +namespace Botan { + +class HashFunction; + +/** +* Padding scheme from X9.31 (aka EMSA2 in IEEE 1363) +* +* Historically used for signature padding with Rabin-Williams, +* which is not implemented by Botan anymore. +* +* Sometimes used with RSA in odd protocols. +*/ +class X931_SignaturePadding final : public SignaturePaddingScheme { + public: + /** + * @param hash the hash function to use + */ + explicit X931_SignaturePadding(std::unique_ptr hash); + + std::string name() const override; + + std::string hash_function() const override; + + private: + void update(const uint8_t input[], size_t length) override; + std::vector raw_data() override; + + std::vector encoding_of(std::span raw, + size_t key_bits, + RandomNumberGenerator& rng) override; + + bool verify(std::span coded, std::span raw, size_t key_bits) override; + + std::vector m_empty_hash; + std::unique_ptr m_hash; + uint8_t m_hash_id; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/info.txt botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/info.txt --- botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,13 @@ + +RSA_SIGNATURE_PADDING -> 20250720 + + + +name -> "RSA signature padding schemes" +brief -> "Implementations of public key signature padding schemes" + + + +hash +rng + diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/iso9796/info.txt botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/iso9796/info.txt --- botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/iso9796/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/iso9796/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,13 @@ + +ISO_9796 -> 20161121 + + + +name -> "ISO-9796-2" + + + +mgf1 +hash_id + + diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/iso9796/iso9796.cpp botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/iso9796/iso9796.cpp --- botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/iso9796/iso9796.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/iso9796/iso9796.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,304 @@ +/* + * ISO-9796-2 - Digital signature schemes giving message recovery schemes 2 and 3 + * (C) 2016 Tobias Niemann, Hackmanit GmbH + * 2025 Jack Lloyd + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Botan { + +namespace { + +std::vector iso9796_hash(HashFunction& hash, + std::span msg1, + std::span hmsg2, + std::span salt) { + // Compute H(C || msg1 || H(msg2) || S) as described in the ISO text + hash.update_be(static_cast(msg1.size()) * 8); + hash.update(msg1); + hash.update(hmsg2); + hash.update(salt); + return hash.final_stdvec(); +} + +std::vector iso9796_encoding(std::span msg, + size_t output_bits, + std::unique_ptr& hash, + size_t salt_len, + bool implicit, + RandomNumberGenerator& rng) { + const size_t output_length = (output_bits + 7) / 8; + + //set trailer length + const size_t trailer_len = (implicit) ? 1 : 2; + + const size_t hash_len = hash->output_length(); + + if(output_length <= hash_len + salt_len + trailer_len) { + throw Encoding_Error("ISO9796-2::encoding_of: Output length is too small"); + } + + //calculate message capacity + const size_t capacity = output_length - hash_len - salt_len - trailer_len - 1; + + // msg1 is the recoverable part and hmsg2 is the hash of the unrecoverable message part. + const size_t msg1_len = std::min(capacity, msg.size()); + const auto msg1 = msg.first(msg1_len); // the first capacity bytes + const auto msg2 = msg.subspan(msg1_len); // the rest; possibly empty + + const auto hmsg2 = hash->process>(msg2); + const auto salt = rng.random_vec>(salt_len); + + const auto H = iso9796_hash(*hash, msg1, hmsg2, salt); + + std::vector EM(output_length); + + BufferStuffer stuffer(EM); + stuffer.append(0x00, stuffer.remaining_capacity() - (hash_len + salt_len + trailer_len + msg1_len + 1)); + stuffer.append(0x01); + stuffer.append(msg1); + stuffer.append(salt); + + //apply mask + const size_t mgf1_bytes = EM.size() - hash_len - trailer_len; + mgf1_mask(*hash, H, std::span{EM}.first(mgf1_bytes)); + + //clear the leftmost bit (confer bouncy castle) + EM[0] &= 0x7F; + + stuffer.append(H); + + // set implicit/ISO trailer + + if(implicit) { + stuffer.append(0xBC); + } else { + const uint8_t hash_id = ieee1363_hash_id(hash->name()); + if(hash_id == 0) { + throw Encoding_Error("ISO-9796: no hash identifier for " + hash->name()); + } + stuffer.append(hash_id); + stuffer.append(0xCC); + } + + BOTAN_ASSERT_NOMSG(stuffer.full()); + + return EM; +} + +bool iso9796_verification(std::span repr, + std::span raw, + size_t key_bits, + std::unique_ptr& hash, + size_t salt_len) { + if(repr.size() != (key_bits + 7) / 8) { + return false; + } + //get trailer length + + const uint8_t last = repr[repr.size() - 1]; + + if(last != 0xBC && last != 0xCC) { + return false; + } + + const size_t trailer_len = last == 0xBC ? 1 : 2; + + if(trailer_len == 2) { + const uint8_t hash_id = ieee1363_hash_id(hash->name()); + if(hash_id == 0) { + throw Decoding_Error("ISO-9796: no hash identifier for " + hash->name()); + } + + const uint8_t trailer_0 = repr[repr.size() - 2]; + const uint8_t trailer_1 = repr[repr.size() - 1]; + + if(trailer_0 != hash_id || trailer_1 != 0xCC) { + return false; + } + } + + const size_t hash_len = hash->output_length(); + + if(repr.size() < hash_len + trailer_len + salt_len) { + return false; + } + + std::vector coded(repr.begin(), repr.end()); + + CT::poison(coded.data(), coded.size()); + //remove mask + uint8_t* DB = coded.data(); + const size_t DB_size = coded.size() - hash_len - trailer_len; + + const uint8_t* H = &coded[DB_size]; + + mgf1_mask(*hash, {H, hash_len}, {DB, DB_size}); + //clear the leftmost bit (confer bouncy castle) + DB[0] &= 0x7F; + + //recover msg1 and salt + size_t msg1_offset = 1; + + auto waiting_for_delim = CT::Mask::set(); + auto bad_input = CT::Mask::cleared(); + + for(size_t j = 0; j < DB_size; ++j) { + const auto is_zero = CT::Mask::is_zero(DB[j]); + const auto is_one = CT::Mask::is_equal(DB[j], 0x01); + + const auto add_m = waiting_for_delim & is_zero; + + bad_input |= waiting_for_delim & ~(is_zero | is_one); + msg1_offset += add_m.if_set_return(1); + + waiting_for_delim &= is_zero; + } + + //invalid, if delimiter 0x01 was not found or msg1_offset is too big + bad_input |= waiting_for_delim; + + const auto bad_offset = CT::Mask::is_lt(coded.size(), trailer_len + hash_len + msg1_offset + salt_len); + bad_input |= CT::Mask(bad_offset); + + //in case that msg1_offset is too big, just continue with offset = 0. + msg1_offset = CT::Mask::expand(bad_input.value()).if_not_set_return(msg1_offset); + + CT::unpoison(coded.data(), coded.size()); + CT::unpoison(msg1_offset); + + const size_t msg1_len = coded.size() - (trailer_len + hash_len + msg1_offset + salt_len); + + const auto msg1 = std::span(coded).subspan(msg1_offset, msg1_len); + const auto salt = std::span(coded).subspan(msg1_offset + msg1.size(), salt_len); + + //compute H2(C||msg1||H(msg2)||S*). * indicates a recovered value + const size_t capacity = (key_bits - 2 + 7) / 8 - hash_len - salt_len - trailer_len - 1; + + std::span msg1raw = raw; + if(msg1raw.size() > capacity) { + hash->update(msg1raw.subspan(capacity)); + msg1raw = msg1raw.first(capacity); + } + + const auto hmsg2 = hash->final_stdvec(); + + // Compute H(C*||msg1*||H(msg2)||S*) where '*' indicates a recovered value + const auto H2 = iso9796_hash(*hash, msg1, hmsg2, salt); + + // Check if H == H2 + bad_input |= CT::is_not_equal(H, H2.data(), hash_len); + + // Check that msg after MGF1 matches msg in the original + bad_input |= ~CT::Mask(CT::Mask::is_equal(msg1.size(), msg1raw.size())); + bad_input |= ~CT::is_equal(msg1.data(), msg1raw.data(), std::min(msg1.size(), msg1raw.size())); + + CT::unpoison(bad_input); + return (bad_input.as_bool() == false); +} + +} // namespace + +/* + * ISO-9796-2 signature scheme 2 + * DS 2 is probabilistic + */ +void ISO_9796_DS2::update(const uint8_t input[], size_t length) { + //need to buffer message completely, before digest + m_msg_buffer.insert(m_msg_buffer.end(), input, input + length); +} + +/* + * Return the raw (unencoded) data + */ +std::vector ISO_9796_DS2::raw_data() { + std::vector retbuffer = m_msg_buffer; + m_msg_buffer.clear(); + return retbuffer; +} + +/* + * ISO-9796-2 scheme 2 encode operation + */ +std::vector ISO_9796_DS2::encoding_of(std::span msg, + size_t output_bits, + RandomNumberGenerator& rng) { + return iso9796_encoding(msg, output_bits, m_hash, m_salt_len, m_implicit, rng); +} + +/* + * ISO-9796-2 scheme 2 verify operation + */ +bool ISO_9796_DS2::verify(std::span repr, std::span raw, size_t key_bits) { + return iso9796_verification(repr, raw, key_bits, m_hash, m_salt_len); +} + +std::string ISO_9796_DS2::hash_function() const { + return m_hash->name(); +} + +/* + * Return the SCAN name + */ +std::string ISO_9796_DS2::name() const { + return fmt("ISO_9796_DS2({},{},{})", m_hash->name(), (m_implicit ? "imp" : "exp"), m_salt_len); +} + +/* + * ISO-9796-2 signature scheme 3 + * DS 3 is deterministic and equals DS2 without salt + */ +void ISO_9796_DS3::update(const uint8_t input[], size_t length) { + //need to buffer message completely, before digest + m_msg_buffer.insert(m_msg_buffer.end(), input, input + length); +} + +/* + * Return the raw (unencoded) data + */ +std::vector ISO_9796_DS3::raw_data() { + std::vector retbuffer = m_msg_buffer; + m_msg_buffer.clear(); + return retbuffer; +} + +/* + * ISO-9796-2 scheme 3 encode operation + */ +std::vector ISO_9796_DS3::encoding_of(std::span msg, + size_t output_bits, + RandomNumberGenerator& rng) { + return iso9796_encoding(msg, output_bits, m_hash, 0, m_implicit, rng); +} + +/* + * ISO-9796-2 scheme 3 verify operation + */ +bool ISO_9796_DS3::verify(std::span repr, std::span raw, size_t key_bits) { + return iso9796_verification(repr, raw, key_bits, m_hash, 0); +} + +std::string ISO_9796_DS3::hash_function() const { + return m_hash->name(); +} + +/* + * Return the SCAN name + */ +std::string ISO_9796_DS3::name() const { + return fmt("ISO_9796_DS3({},{})", m_hash->name(), (m_implicit ? "imp" : "exp")); +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/iso9796/iso9796.h botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/iso9796/iso9796.h --- botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/iso9796/iso9796.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/iso9796/iso9796.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,88 @@ +/* + * ISO-9796-2 - Digital signature schemes giving message recovery schemes 2 and 3 + * (C) 2016 Tobias Niemann, Hackmanit GmbH + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#ifndef BOTAN_ISO9796_H_ +#define BOTAN_ISO9796_H_ + +#include +#include +#include +#include + +namespace Botan { + +class HashFunction; + +/** +* ISO-9796-2 - Digital signature scheme 2 (probabilistic) +*/ +class ISO_9796_DS2 final : public SignaturePaddingScheme { + public: + /** + * @param hash function to use + * @param implicit whether or not the trailer is implicit + * @param salt_size size of the salt to use in bytes + */ + ISO_9796_DS2(std::unique_ptr hash, bool implicit, size_t salt_size) : + m_hash(std::move(hash)), m_implicit(implicit), m_salt_len(salt_size) {} + + std::string hash_function() const override; + + std::string name() const override; + + private: + void update(const uint8_t input[], size_t length) override; + + std::vector raw_data() override; + + std::vector encoding_of(std::span msg, + size_t output_bits, + RandomNumberGenerator& rng) override; + + bool verify(std::span coded, std::span raw, size_t key_bits) override; + + std::unique_ptr m_hash; + bool m_implicit; + size_t m_salt_len; + std::vector m_msg_buffer; +}; + +/** +* ISO-9796-2 - Digital signature scheme 3 (deterministic) +*/ +class ISO_9796_DS3 final : public SignaturePaddingScheme { + public: + /** + * @param hash function to use + * @param implicit whether or not the trailer is implicit + */ + explicit ISO_9796_DS3(std::unique_ptr hash, bool implicit = false) : + m_hash(std::move(hash)), m_implicit(implicit) {} + + std::string name() const override; + + std::string hash_function() const override; + + private: + void update(const uint8_t input[], size_t length) override; + + std::vector raw_data() override; + + std::vector encoding_of(std::span msg, + size_t output_bits, + RandomNumberGenerator& rng) override; + + bool verify(std::span coded, std::span raw, size_t key_bits) override; + + std::unique_ptr m_hash; + bool m_implicit; + std::vector m_msg_buffer; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/sig_padding.cpp botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/sig_padding.cpp --- botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/sig_padding.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/sig_padding.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,143 @@ +/* +* (C) 2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include + +#if defined(BOTAN_HAS_X931_SIGNATURE_PADDING) + #include +#endif + +#if defined(BOTAN_HAS_PKCSV15_SIGNATURE_PADDING) + #include +#endif + +#if defined(BOTAN_HAS_PSS) + #include +#endif + +#if defined(BOTAN_HAS_RAW_SIGNATURE_PADDING) + #include +#endif + +#if defined(BOTAN_HAS_ISO_9796) + #include +#endif + +namespace Botan { + +std::unique_ptr SignaturePaddingScheme::create(std::string_view algo_spec) { + const SCAN_Name req(algo_spec); + +#if defined(BOTAN_HAS_EMSA_PKCS1) + // TODO(Botan4) Remove all but "PKCS1v15" + if(req.algo_name() == "EMSA_PKCS1" || req.algo_name() == "PKCS1v15" || req.algo_name() == "EMSA-PKCS1-v1_5" || + req.algo_name() == "EMSA3") { + if(req.arg_count() == 2 && req.arg(0) == "Raw") { + return std::make_unique(req.arg(1)); + } else if(req.arg_count() == 1) { + if(req.arg(0) == "Raw") { + return std::make_unique(); + } else { + if(auto hash = HashFunction::create(req.arg(0))) { + return std::make_unique(std::move(hash)); + } + } + } + } +#endif + +#if defined(BOTAN_HAS_EMSA_PSSR) + // TODO(Botan4) Remove all but "PSS_Raw" + if(req.algo_name() == "PSS_Raw" || req.algo_name() == "PSSR_Raw") { + if(req.arg_count_between(1, 3) && req.arg(1, "MGF1") == "MGF1") { + if(auto hash = HashFunction::create(req.arg(0))) { + if(req.arg_count() == 3) { + const size_t salt_size = req.arg_as_integer(2, 0); + return std::make_unique(std::move(hash), salt_size); + } else { + return std::make_unique(std::move(hash)); + } + } + } + } + + // TODO(Botan4) Remove all but "PSS" + if(req.algo_name() == "PSS" || req.algo_name() == "PSSR" || req.algo_name() == "EMSA-PSS" || + req.algo_name() == "PSS-MGF1" || req.algo_name() == "EMSA4") { + if(req.arg_count_between(1, 3) && req.arg(1, "MGF1") == "MGF1") { + if(auto hash = HashFunction::create(req.arg(0))) { + if(req.arg_count() == 3) { + const size_t salt_size = req.arg_as_integer(2, 0); + return std::make_unique(std::move(hash), salt_size); + } else { + return std::make_unique(std::move(hash)); + } + } + } + } +#endif + +#if defined(BOTAN_HAS_ISO_9796) + if(req.algo_name() == "ISO_9796_DS2") { + if(req.arg_count_between(1, 3)) { + if(auto hash = HashFunction::create(req.arg(0))) { + const size_t salt_size = req.arg_as_integer(2, hash->output_length()); + const bool implicit = req.arg(1, "exp") == "imp"; + return std::make_unique(std::move(hash), implicit, salt_size); + } + } + } + //ISO-9796-2 DS 3 is deterministic and DS2 without a salt + if(req.algo_name() == "ISO_9796_DS3") { + if(req.arg_count_between(1, 2)) { + if(auto hash = HashFunction::create(req.arg(0))) { + const bool implicit = req.arg(1, "exp") == "imp"; + return std::make_unique(std::move(hash), implicit); + } + } + } +#endif + +#if defined(BOTAN_HAS_X931_SIGNATURE_PADDING) + // TODO(Botan4) Remove all but "X9.31" + if(req.algo_name() == "EMSA_X931" || req.algo_name() == "EMSA2" || req.algo_name() == "X9.31") { + if(req.arg_count() == 1) { + if(auto hash = HashFunction::create(req.arg(0))) { + return std::make_unique(std::move(hash)); + } + } + } +#endif + +#if defined(BOTAN_HAS_RAW_SIGNATURE_PADDING) + if(req.algo_name() == "Raw") { + if(req.arg_count() == 0) { + return std::make_unique(); + } else { + auto hash = HashFunction::create(req.arg(0)); + if(hash) { + return std::make_unique(hash->output_length()); + } + } + } +#endif + + return nullptr; +} + +std::unique_ptr SignaturePaddingScheme::create_or_throw(std::string_view algo_spec) { + if(auto padding = SignaturePaddingScheme::create(algo_spec)) { + return padding; + } else { + throw Algorithm_Not_Found(algo_spec); + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/sig_padding.h botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/sig_padding.h --- botan3-3.7.1+dfsg/src/lib/pk_pad/sig_padding/sig_padding.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pk_pad/sig_padding/sig_padding.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,90 @@ +/* +* (C) 1999-2007 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SIGNATURE_PADDING_SCHEME_H_ +#define BOTAN_SIGNATURE_PADDING_SCHEME_H_ + +#include +#include +#include +#include +#include + +namespace Botan { + +class RandomNumberGenerator; + +/** +* RSA Signature Padding Scheme +* +* Previously called 'EMSA' from IEEE 1363's "Encoding Method for Signatures, Appendix" +*/ +class BOTAN_TEST_API SignaturePaddingScheme /* NOLINT(*-special-member-functions) */ { + public: + virtual ~SignaturePaddingScheme() = default; + + /** + * Factory method for SignaturePaddingScheme (message-encoding methods for signatures + * with appendix) objects + * @param algo_spec the name of the SignaturePaddingScheme to create + * @return pointer to newly allocated object of that type, or nullptr + */ + static std::unique_ptr create(std::string_view algo_spec); + + /** + * Factory method for SignaturePaddingScheme (message-encoding methods for signatures + * with appendix) objects + * @param algo_spec the name of the SignaturePaddingScheme to create + * @return pointer to newly allocated object of that type, or throws + */ + static std::unique_ptr create_or_throw(std::string_view algo_spec); + + /** + * Add more data to the signature computation + * @param input some data + * @param length length of input in bytes + */ + virtual void update(const uint8_t input[], size_t length) = 0; + + /** + * @return raw hash + */ + virtual std::vector raw_data() = 0; + + /** + * Return the encoding of a message + * @param msg the result of raw_data() + * @param output_bits the desired output bit size + * @param rng a random number generator + * @return encoded signature + */ + virtual std::vector encoding_of(std::span msg, + size_t output_bits, + RandomNumberGenerator& rng) = 0; + + /** + * Verify the encoding + * @param encoding the received (coded) message representative + * @param raw_hash the computed (local, uncoded) message representative + * @param key_bits the size of the key in bits + * @return true if coded is a valid encoding of raw, otherwise false + */ + virtual bool verify(std::span encoding, std::span raw_hash, size_t key_bits) = 0; + + /** + * Return the hash function being used by this padding scheme + */ + virtual std::string hash_function() const = 0; + + /** + * @return the SCAN name of the encoding/padding scheme + */ + virtual std::string name() const = 0; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/prov/commoncrypto/commoncrypto.h botan3-3.12.0+dfsg/src/lib/prov/commoncrypto/commoncrypto.h --- botan3-3.7.1+dfsg/src/lib/prov/commoncrypto/commoncrypto.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/commoncrypto/commoncrypto.h 2026-05-07 01:38:28.000000000 +0000 @@ -20,7 +20,7 @@ class Cipher_Mode; class BlockCipher; class HashFunction; -enum class Cipher_Dir : int; +enum class Cipher_Dir : uint8_t; typedef int32_t CCCryptorStatus; class BOTAN_PUBLIC_API(2, 0) CommonCrypto_Error final : public Exception { diff -Nru botan3-3.7.1+dfsg/src/lib/prov/commoncrypto/commoncrypto_hash.cpp botan3-3.12.0+dfsg/src/lib/prov/commoncrypto/commoncrypto_hash.cpp --- botan3-3.7.1+dfsg/src/lib/prov/commoncrypto/commoncrypto_hash.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/commoncrypto/commoncrypto_hash.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,8 +8,7 @@ #include #include -#include -#include +#include #include diff -Nru botan3-3.7.1+dfsg/src/lib/prov/commoncrypto/commoncrypto_mode.cpp botan3-3.12.0+dfsg/src/lib/prov/commoncrypto/commoncrypto_mode.cpp --- botan3-3.7.1+dfsg/src/lib/prov/commoncrypto/commoncrypto_mode.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/commoncrypto/commoncrypto_mode.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,7 @@ #include +#include #include #include #include @@ -67,11 +68,12 @@ } void CommonCrypto_Cipher_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) { - assert_key_material_set(); - if(!valid_nonce_length(nonce_len)) { throw Invalid_IV_Length(name(), nonce_len); } + + assert_key_material_set(); + if(nonce_len) { CCCryptorStatus status = CCCryptorReset(m_cipher, nonce); if(status != kCCSuccess) { @@ -139,7 +141,7 @@ } size_t CommonCrypto_Cipher_Mode::ideal_granularity() const { - return m_opts.block_size * BOTAN_BLOCK_CIPHER_PAR_MULT; + return m_opts.block_size * BlockCipher::ParallelismMult; } size_t CommonCrypto_Cipher_Mode::minimum_final_size() const { diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm/info.txt botan3-3.12.0+dfsg/src/lib/prov/tpm/info.txt --- botan3-3.7.1+dfsg/src/lib/prov/tpm/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -3,8 +3,8 @@ -name -> "TPM" -brief -> "Wrappers and Utilites to interact with TPMs" +name -> "TPM v1 Support (deprecated)" +brief -> "Wrappers and Utilities to interact with TPM v1" lifecycle -> "Deprecated" diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm/tpm.cpp botan3-3.12.0+dfsg/src/lib/prov/tpm/tpm.cpp --- botan3-3.7.1+dfsg/src/lib/prov/tpm/tpm.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm/tpm.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #include #include +#include #include #include #include diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm2/info.txt botan3-3.12.0+dfsg/src/lib/prov/tpm2/info.txt --- botan3-3.7.1+dfsg/src/lib/prov/tpm2/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm2/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -4,7 +4,7 @@ name -> "TPM2" -brief -> "Wrappers and Utilites to interact with TPM2" +brief -> "Wrappers and Utilities to interact with TPM2" load_on vendor diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_algo_mappings.h botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_algo_mappings.h --- botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_algo_mappings.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_algo_mappings.h 2026-05-07 01:38:28.000000000 +0000 @@ -294,7 +294,7 @@ } [[nodiscard]] inline std::optional cipher_botan_to_tss2(std::string_view algo_name) { - SCAN_Name spec(algo_name); + const SCAN_Name spec(algo_name); if(spec.arg_count() == 0) { return std::nullopt; } diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_context.cpp botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_context.cpp --- botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_context.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_context.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,12 +11,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -36,8 +38,8 @@ } // namespace struct Context::Impl { - ESYS_CONTEXT* m_ctx; /// m_ctx may be owned by the library user (see m_external) - bool m_external; + ESYS_CONTEXT* m_ctx{}; /// m_ctx may be owned by the library user (see m_external) + bool m_external{}; #if defined(BOTAN_HAS_TPM2_CRYPTO_BACKEND) std::unique_ptr m_crypto_callback_state; @@ -53,11 +55,9 @@ } std::shared_ptr Context::create(const std::string& tcti_nameconf) { - const auto nameconf_ptr = tcti_nameconf.c_str(); - TSS2_TCTI_CONTEXT* tcti_ctx = nullptr; ESYS_CONTEXT* esys_ctx = nullptr; - check_rc("TCTI Initialization", Tss2_TctiLdr_Initialize(nameconf_ptr, &tcti_ctx)); + check_rc("TCTI Initialization", Tss2_TctiLdr_Initialize(tcti_nameconf.c_str(), &tcti_ctx)); BOTAN_ASSERT_NONNULL(tcti_ctx); check_rc("TPM2 Initialization", Esys_Initialize(&esys_ctx, tcti_ctx, nullptr /* ABI version */)); BOTAN_ASSERT_NONNULL(esys_ctx); @@ -67,8 +67,8 @@ } std::shared_ptr Context::create(std::optional tcti, std::optional conf) { - const auto tcti_ptr = tcti.has_value() ? tcti->c_str() : nullptr; - const auto conf_ptr = conf.has_value() ? conf->c_str() : nullptr; + const char* const tcti_ptr = tcti.has_value() ? tcti->c_str() : nullptr; + const char* const conf_ptr = conf.has_value() ? conf->c_str() : nullptr; TSS2_TCTI_CONTEXT* tcti_ctx = nullptr; ESYS_CONTEXT* esys_ctx = nullptr; @@ -345,15 +345,10 @@ // 1. Decide on the location to persist the key to. // This uses either the handle provided by the caller or a free handle. - const TPMI_DH_PERSISTENT new_persistent_handle = [&] { - if(persistent_handle.has_value()) { - return persistent_handle.value(); - } else { - const auto free_persistent_handle = find_free_persistent_handle(); - BOTAN_STATE_CHECK(free_persistent_handle.has_value()); - return free_persistent_handle.value(); - } - }(); + const std::optional new_persistent_handle = + persistent_handle.has_value() ? persistent_handle : find_free_persistent_handle(); + + BOTAN_STATE_CHECK(new_persistent_handle.has_value()); // 2. Persist the transient key in the TPM's NV storage // This will flush the transient key handle and replace it with a new @@ -365,7 +360,7 @@ sessions[0], sessions[1], sessions[2], - new_persistent_handle, + *new_persistent_handle, out_transient_handle(handles))); BOTAN_ASSERT_NOMSG(handles.has_transient_handle()); @@ -384,9 +379,9 @@ Esys_TR_GetTpmHandle(m_impl->m_ctx, handles.transient_handle(), out_persistent_handle(handles))); BOTAN_ASSERT_NOMSG(handles.has_persistent_handle()); - BOTAN_ASSERT_EQUAL(new_persistent_handle, handles.persistent_handle(), "key was persisted at the correct location"); + BOTAN_ASSERT_EQUAL(*new_persistent_handle, handles.persistent_handle(), "key was persisted at the correct location"); - return new_persistent_handle; + return *new_persistent_handle; } void Context::evict(std::unique_ptr key, const SessionBundle& sessions) { @@ -448,7 +443,7 @@ // If the TCTI context was initialized explicitly, Esys_GetTcti() will // return a pointer to the TCTI context that then has to be finalized // explicitly. See ESAPI Specification Section 6.3 "Esys_GetTcti". - TSS2_TCTI_CONTEXT* tcti_ctx = nullptr; + TSS2_TCTI_CONTEXT* tcti_ctx = nullptr; // NOLINT(*-const-correctness) bug in clang-tidy Esys_GetTcti(m_impl->m_ctx, &tcti_ctx); // ignore error in destructor if(tcti_ctx != nullptr) { Tss2_TctiLdr_Finalize(&tcti_ctx); diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_context.h botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_context.h --- botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_context.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_context.h 2026-05-07 01:38:28.000000000 +0000 @@ -33,7 +33,7 @@ /** * Central class for interacting with a TPM2. Additional to managing the - * connection to the TPM, this provides authorative information about the TPM's + * connection to the TPM, this provides authoritative information about the TPM's * capabilities. Also, it allows to persist and evict keys generated by the TPM. */ class BOTAN_PUBLIC_API(3, 6) Context final : public std::enable_shared_from_this { @@ -56,7 +56,7 @@ * Create a TPM2::Context from an externally sourced TPM2-TSS ESYS * Context. Note that the input contexts need to remain alive for the * lifetime of the entire TPM2::Context! This allows to use Botan's TPM2 - * functionality within an exising ESAPI application. + * functionality within an existing ESAPI application. * * Note that Botan won't finalize an externally provided ESYS context, * this responsibility remains with the caller in this case. @@ -101,6 +101,7 @@ /// @return an ESYS_CONTEXT* for use in other TPM2 functions. ESYS_CONTEXT* esys_context() noexcept; + // NOLINTNEXTLINE(*-explicit-conversions) Intentional: enables transparent ESYS_CONTEXT* wrapper usage operator ESYS_CONTEXT*() noexcept { return esys_context(); } /// @return the Vendor of the TPM2 diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_crypto_backend/info.txt botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_crypto_backend/info.txt --- botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_crypto_backend/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_crypto_backend/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -11,7 +11,7 @@ hash hmac modes -pk_pad +enc_padding eme_raw diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_crypto_backend/tpm2_crypto_backend.h botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_crypto_backend/tpm2_crypto_backend.h --- botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_crypto_backend/tpm2_crypto_backend.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_crypto_backend/tpm2_crypto_backend.h 2026-05-07 01:38:28.000000000 +0000 @@ -26,7 +26,7 @@ * ESYS_CONTEXT pointer is valid. */ struct CryptoCallbackState { - std::shared_ptr rng; // NOLINT(misc-non-private-member-variables-in-classes) + std::shared_ptr rng; // NOLINT(*-non-private-member-variable*) }; /** diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_crypto_backend/tpm2_crypto_backend_impl.cpp botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_crypto_backend/tpm2_crypto_backend_impl.cpp --- botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_crypto_backend/tpm2_crypto_backend_impl.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_crypto_backend/tpm2_crypto_backend_impl.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -25,8 +25,9 @@ #include #endif -#include +#include #include +#include #include #include @@ -90,16 +91,11 @@ /// Safely converts the @p userdata to the Botan crypto context object. [[nodiscard]] std::optional> get(void* userdata) noexcept { - if(!userdata) { + if(auto* ccs = reinterpret_cast(userdata)) { + return *ccs; + } else { return std::nullopt; } - - auto ccs = reinterpret_cast(userdata); - if(!ccs) { - return std::nullopt; - } - - return *ccs; } /** @@ -140,13 +136,13 @@ size_t buffer_size, const uint8_t* iv) noexcept { return thunk([&] { - if(!key) { + if(key == nullptr) { return (direction == Botan::Cipher_Dir::Encryption) ? TSS2_ESYS_RC_NO_ENCRYPT_PARAM : TSS2_ESYS_RC_NO_DECRYPT_PARAM; } // nullptr buffer with size 0 is alright - if(!buffer && buffer_size != 0) { + if(buffer == nullptr && buffer_size != 0) { return TSS2_ESYS_RC_BAD_VALUE; } @@ -179,7 +175,7 @@ const auto s_data = std::span{buffer, buffer_size}; const auto s_key = std::span{key, keylength}; const auto s_iv = [&]() -> std::span { - if(iv) { + if(iv != nullptr) { return {iv, cipher->default_nonce_length()}; } else { return {}; @@ -207,7 +203,7 @@ TSS2_RC hash_start(ESYS_CRYPTO_CONTEXT_BLOB** context, TPM2_ALG_ID hash_alg, void* userdata) { BOTAN_UNUSED(userdata); return thunk([&] { - if(!context) { + if(context == nullptr) { return TSS2_ESYS_RC_BAD_REFERENCE; } @@ -217,12 +213,12 @@ } auto hash = Botan::HashFunction::create(hash_name.value()); - if(!hash) { + if(hash == nullptr) { return TSS2_ESYS_RC_NOT_IMPLEMENTED; } // Will be deleted in hash_abort() or hash_finish() - *context = new DigestCallbackState{std::move(hash)}; + *context = new DigestCallbackState{std::move(hash)}; // NOLINT(*-owning-memory) return TSS2_RC_SUCCESS; }); } @@ -247,7 +243,7 @@ } // nullptr buffer with size 0 is alright - if(!buffer && size != 0) { + if(buffer == nullptr && size != 0) { return TSS2_ESYS_RC_BAD_VALUE; } @@ -275,7 +271,7 @@ return thunk([&] { auto hash = get(context); - if(!hash || !buffer) { + if(!hash || buffer == nullptr) { return TSS2_ESYS_RC_BAD_REFERENCE; } @@ -285,7 +281,8 @@ *size = digest_size; } - delete *context; // allocated in hash_start() + // allocated in hash_start() + delete *context; // NOLINT(*-owning-memory) *context = nullptr; return TSS2_RC_SUCCESS; }); @@ -299,8 +296,9 @@ */ void hash_abort(ESYS_CRYPTO_CONTEXT_BLOB** context, void* userdata) { BOTAN_UNUSED(userdata); - if(context) { - delete *context; // allocated in hash_start() + if(context != nullptr) { + // allocated in hash_start() + delete *context; // NOLINT(*-owning-memory) *context = nullptr; } } @@ -321,7 +319,7 @@ ESYS_CRYPTO_CONTEXT_BLOB** context, TPM2_ALG_ID hash_alg, const uint8_t* key, size_t size, void* userdata) { BOTAN_UNUSED(userdata); return thunk([&] { - if(!context || !key) { + if(Botan::any_null_pointers(context, key)) { return TSS2_ESYS_RC_BAD_REFERENCE; } @@ -331,14 +329,14 @@ } auto hmac = Botan::MessageAuthenticationCode::create(Botan::fmt("HMAC({})", hash_name.value())); - if(!hmac) { + if(hmac == nullptr) { return TSS2_ESYS_RC_NOT_IMPLEMENTED; } hmac->set_key(std::span{key, size}); // Will be deleted in hmac_abort() or hmac_finish() - *context = new DigestCallbackState{std::move(hmac)}; + *context = new DigestCallbackState{std::move(hmac)}; // NOLINT(*-owning-memory) return TSS2_RC_SUCCESS; }); } @@ -363,7 +361,7 @@ } // nullptr buffer with size 0 is alright - if(!buffer && size != 0) { + if(buffer == nullptr && size != 0) { return TSS2_ESYS_RC_BAD_VALUE; } @@ -391,7 +389,7 @@ return thunk([&] { auto hmac = get(context); - if(!hmac || !buffer) { + if(!hmac || buffer == nullptr) { return TSS2_ESYS_RC_BAD_REFERENCE; } @@ -401,7 +399,8 @@ *size = digest_size; } - delete *context; // allocated in hmac_start() + // allocated in hmac_start() + delete *context; // NOLINT(*-owning-memory) *context = nullptr; return TSS2_RC_SUCCESS; }); @@ -415,8 +414,9 @@ */ void hmac_abort(ESYS_CRYPTO_CONTEXT_BLOB** context, void* userdata) { BOTAN_UNUSED(userdata); - if(context) { - delete *context; // allocated in hmac_start() + if(context != nullptr) { + // allocated in hmac_start() + delete *context; // NOLINT(*-owning-memory) *context = nullptr; } } @@ -434,7 +434,7 @@ TSS2_RC get_random2b(TPM2B_NONCE* nonce, size_t num_bytes, void* userdata) { return thunk([&] { auto ccs = get(userdata); - if(!ccs || !ccs->get().rng || !nonce) { + if(!ccs || !ccs->get().rng || Botan::any_null_pointers(nonce)) { return TSS2_ESYS_RC_BAD_REFERENCE; } @@ -478,14 +478,14 @@ // // https://github.com/randombit/botan/pull/4318#issuecomment-2297682058 #if defined(BOTAN_HAS_RSA) - auto create_eme = [&]( - const TPMT_RSA_SCHEME& scheme, - [[maybe_unused]] TPM2_ALG_ID name_algo, - [[maybe_unused]] TPMU_ASYM_SCHEME scheme_detail) -> std::optional> { + auto create_eme = [&](const TPMT_RSA_SCHEME& scheme, + [[maybe_unused]] TPM2_ALG_ID name_algo, + [[maybe_unused]] TPMU_ASYM_SCHEME scheme_detail) + -> std::optional> { // OAEP is more complex by requiring a hash function and an optional // label. To avoid marshalling this into Botan's algorithm descriptor // we create an OAEP instance manually. - auto create_oaep = [&]() -> std::optional> { + auto create_oaep = [&]() -> std::optional> { #if defined(BOTAN_HAS_EME_OAEP) // TPM Library, Part 1: Architecture, Annex B.4 // The RSA key's scheme hash algorithm (or, if it is TPM_ALG_NULL, @@ -512,7 +512,7 @@ // TPM Library, Part 1: Architecture, Annex B.4 // [...] is used to compute lhash := H(label), and the null // termination octet is included in the digest. - std::string_view label_with_zero_terminator{label, std::strlen(label) + 1}; + const std::string_view label_with_zero_terminator{label, std::strlen(label) + 1}; return std::make_unique(std::move(H_label), std::move(H_mgf1), label_with_zero_terminator); #else BOTAN_UNUSED(label); @@ -520,14 +520,14 @@ #endif }; - try { // EME::create throws if algorithm is not available + try { // EncryptionPaddingScheme::create throws if algorithm is not available switch(scheme.scheme) { case TPM2_ALG_OAEP: return create_oaep(); case TPM2_ALG_NULL: - return Botan::EME::create("Raw"); + return Botan::EncryptionPaddingScheme::create("Raw"); case TPM2_ALG_RSAES: - return Botan::EME::create("PKCS1v15"); + return Botan::EncryptionPaddingScheme::create("PKCS1v15"); default: return std::nullopt; // -> not supported } @@ -535,12 +535,12 @@ /* ignore */ } - return nullptr; // -> not implemented (EME::create() threw) + return nullptr; // -> not implemented (EncryptionPaddingScheme::create() threw) }; return thunk([&] { auto ccs = get(userdata); - if(!ccs || !pub_tpm_key || !in_buffer || !out_buffer || !ccs->get().rng) { + if(!ccs || !ccs->get().rng || Botan::any_null_pointers(pub_tpm_key, in_buffer, out_buffer)) { return TSS2_ESYS_RC_BAD_REFERENCE; } @@ -588,7 +588,7 @@ // PK_Encryptor_EME does not provide a way to pass in an output buffer. // TODO: provide an `.encrypt()` overload that accepts an output buffer. - Botan::PK_Encryptor_EME encryptor(pubkey, rng, "Raw"); + const Botan::PK_Encryptor_EME encryptor(pubkey, rng, "Raw"); const auto encrypted = encryptor.encrypt({out_buffer, padded_bytes}, rng); BOTAN_DEBUG_ASSERT(encrypted.size() == output_size); @@ -638,7 +638,7 @@ #if defined(BOTAN_HAS_ECDH) return thunk([&] { auto ccs = get(userdata); - if(!ccs || !key || !Z || !Q || !out_buffer | !ccs->get().rng) { + if(!ccs || !ccs->get().rng || Botan::any_null_pointers(key, Z, Q, out_buffer)) { return TSS2_ESYS_RC_BAD_REFERENCE; } @@ -646,7 +646,7 @@ // 1: Get TPM public key const auto [tpm_ec_group, tpm_ec_point] = Botan::TPM2::ecc_pubkey_from_tss2_public(key); - const auto tpm_sw_pubkey = Botan::ECDH_PublicKey(tpm_ec_group, tpm_ec_point.to_legacy_point()); + const auto tpm_sw_pubkey = Botan::ECDH_PublicKey(tpm_ec_group, tpm_ec_point); const auto curve_order_byte_size = tpm_sw_pubkey.domain().get_p_bytes(); @@ -660,7 +660,7 @@ eph_pub_point.serialize_y_to(Botan::TPM2::as_span(Q->y, curve_order_byte_size)); // 3: ECDH Key Agreement - Botan::PK_Key_Agreement ecdh(eph_key, rng, "Raw" /*No KDF used here*/); + const Botan::PK_Key_Agreement ecdh(eph_key, rng, "Raw" /*No KDF used here*/); const auto shared_secret = ecdh.derive_key(0 /*Ignored for raw KDF*/, tpm_sw_pubkey.public_value()).bits_of(); Botan::TPM2::copy_into(*Z, shared_secret); @@ -681,7 +681,7 @@ * @param[in] key key used for AES. * @param[in] tpm_sym_alg AES type in TSS2 notation (must be TPM2_ALG_AES). * @param[in] key_bits Key size in bits. - * @param[in] tpm_mode Block cipher mode of opertion in TSS2 notation (CFB). + * @param[in] tpm_mode Block cipher mode of operation in TSS2 notation (CFB). * For parameter encryption only CFB can be used. * @param[in,out] buffer Data to be encrypted. The encrypted date will be stored * in this buffer. @@ -712,7 +712,7 @@ * @param[in] key key used for AES. * @param[in] tpm_sym_alg AES type in TSS2 notation (must be TPM2_ALG_AES). * @param[in] key_bits Key size in bits. - * @param[in] tpm_mode Block cipher mode of opertion in TSS2 notation (CFB). + * @param[in] tpm_mode Block cipher mode of operation in TSS2 notation (CFB). * For parameter encryption only CFB can be used. * @param[in,out] buffer Data to be decrypted. The decrypted date will be stored * in this buffer. @@ -745,7 +745,7 @@ * @param[in] key key used for SM4. * @param[in] tpm_sym_alg SM4 type in TSS2 notation (must be TPM2_ALG_SM4). * @param[in] key_bits Key size in bits. - * @param[in] tpm_mode Block cipher mode of opertion in TSS2 notation (CFB). + * @param[in] tpm_mode Block cipher mode of operation in TSS2 notation (CFB). * For parameter encryption only CFB can be used. * @param[in,out] buffer Data to be encrypted. The encrypted date will be stored * in this buffer. @@ -776,7 +776,7 @@ * @param[in] key key used for SM4. * @param[in] tpm_sym_alg SM4 type in TSS2 notation (must be TPM2_ALG_SM4). * @param[in] key_bits Key size in bits. - * @param[in] tpm_mode Block cipher mode of opertion in TSS2 notation (CFB). + * @param[in] tpm_mode Block cipher mode of operation in TSS2 notation (CFB). * For parameter encryption only CFB can be used. * @param[in,out] buffer Data to be decrypted. The decrypted date will be stored * in this buffer. diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.cpp botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.cpp --- botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,7 +8,7 @@ #include -#include +#include #include #include #include @@ -21,9 +21,11 @@ EC_PublicKey::EC_PublicKey(Object handle, SessionBundle sessions, const TPM2B_PUBLIC* public_blob) : EC_PublicKey(std::move(handle), std::move(sessions), ecc_pubkey_from_tss2_public(public_blob)) {} -EC_PublicKey::EC_PublicKey(Object handle, SessionBundle sessions, std::pair public_key) : +EC_PublicKey::EC_PublicKey(Object handle, + SessionBundle sessions, + const std::pair& public_key) : Botan::TPM2::PublicKey(std::move(handle), std::move(sessions)), - Botan::EC_PublicKey(std::move(public_key.first), public_key.second) {} + Botan::EC_PublicKey(public_key.first, public_key.second) {} EC_PrivateKey::EC_PrivateKey(Object handle, SessionBundle sessions, @@ -33,13 +35,13 @@ EC_PrivateKey::EC_PrivateKey(Object handle, SessionBundle sessions, - std::pair public_key, + const std::pair& public_key, std::span private_blob) : Botan::TPM2::PrivateKey(std::move(handle), std::move(sessions), private_blob), - Botan::EC_PublicKey(std::move(public_key.first), public_key.second) {} + Botan::EC_PublicKey(public_key.first, public_key.second) {} std::unique_ptr EC_PrivateKey::public_key() const { - return std::make_unique(domain(), public_point()); + return std::make_unique(domain(), _public_ec_point()); } std::vector EC_PublicKey::public_key_bits() const { @@ -71,7 +73,7 @@ throw Invalid_Argument("Unsupported ECC curve"); } - TPM2B_SENSITIVE_CREATE sensitive_data = { + const TPM2B_SENSITIVE_CREATE sensitive_data = { .size = 0, // ignored .sensitive = { @@ -84,7 +86,7 @@ }, }; - TPMT_PUBLIC key_template = { + const TPMT_PUBLIC key_template = { .type = TPM2_ALG_ECC, // This is the algorithm for fingerprinting the newly created public key. @@ -171,7 +173,7 @@ }; } -size_t signature_length_for_key_handle(const SessionBundle& sessions, const Object& object) { +size_t signature_length_for_ecdsa_key_handle(const SessionBundle& sessions, const Object& object) { const auto curve_id = object._public_info(sessions, TPM2_ALG_ECDSA).pub->publicArea.parameters.eccDetail.curveID; const auto order_bytes = curve_id_order_byte_size(curve_id); @@ -186,7 +188,9 @@ EC_Signature_Operation(const Object& object, const SessionBundle& sessions, std::string_view hash) : Signature_Operation(object, sessions, make_signature_scheme(hash)) {} - size_t signature_length() const override { return signature_length_for_key_handle(sessions(), key_handle()); } + size_t signature_length() const override { + return signature_length_for_ecdsa_key_handle(sessions(), key_handle()); + } AlgorithmIdentifier algorithm_identifier() const override { // Copied from ECDSA @@ -201,7 +205,7 @@ const auto r = as_span(signature.signature.ecdsa.signatureR); const auto s = as_span(signature.signature.ecdsa.signatureS); - const auto sig_len = signature_length_for_key_handle(sessions(), key_handle()); + const auto sig_len = signature_length_for_ecdsa_key_handle(sessions(), key_handle()); BOTAN_ASSERT_NOMSG(sig_len % 2 == 0); BOTAN_ASSERT_NOMSG(r.size() == sig_len / 2 && s.size() == sig_len / 2); @@ -218,7 +222,7 @@ TPMT_SIGNATURE unmarshal_signature(std::span sig_data) const override { BOTAN_STATE_CHECK(scheme().scheme == TPM2_ALG_ECDSA); - const auto sig_len = signature_length_for_key_handle(sessions(), key_handle()); + const auto sig_len = signature_length_for_ecdsa_key_handle(sessions(), key_handle()); BOTAN_ARG_CHECK(sig_data.size() == sig_len, "Invalid signature length"); BOTAN_ASSERT_NOMSG(sig_len % 2 == 0); diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.h botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.h --- botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.h 2026-05-07 01:38:28.000000000 +0000 @@ -44,7 +44,7 @@ friend class TPM2::PublicKey; EC_PublicKey(Object handle, SessionBundle sessions, const TPM2B_PUBLIC* public_blob); - EC_PublicKey(Object handle, SessionBundle sessions, std::pair public_key); + EC_PublicKey(Object handle, SessionBundle sessions, const std::pair& public_key); }; class BOTAN_PUBLIC_API(3, 6) EC_PrivateKey final : public virtual Botan::TPM2::PrivateKey, @@ -59,7 +59,7 @@ return "ECDSA"; } - std::unique_ptr generate_another(Botan::RandomNumberGenerator&) const override { + std::unique_ptr generate_another(Botan::RandomNumberGenerator& /*rng*/) const override { throw Not_Implemented("Cannot generate a new TPM-based keypair from this asymmetric key"); } @@ -110,7 +110,7 @@ EC_PrivateKey(Object handle, SessionBundle sessions, - std::pair public_key, + const std::pair& public_key, std::span private_blob = {}); }; diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_hash.cpp botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_hash.cpp --- botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_hash.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_hash.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include +#include #include #include #include @@ -71,7 +72,7 @@ const auto auth = init_empty(); const auto rc = check_rc_expecting("Esys_HashSequenceStart", - Esys_HashSequenceStart(*m_handle.context(), + Esys_HashSequenceStart(m_handle.context()->esys_context(), m_sessions[0], m_sessions[1], m_sessions[2], @@ -91,10 +92,13 @@ while(slicer.remaining() > 0) { const size_t chunk = std::min(slicer.remaining(), size_t(TPM2_MAX_DIGEST_BUFFER)); const auto data = copy_into(slicer.take(chunk)); - check_rc( - "Esys_SequenceUpdate", - Esys_SequenceUpdate( - *m_handle.context(), m_handle.transient_handle(), m_sessions[0], m_sessions[1], m_sessions[2], &data)); + check_rc("Esys_SequenceUpdate", + Esys_SequenceUpdate(m_handle.context()->esys_context(), + m_handle.transient_handle(), + m_sessions[0], + m_sessions[1], + m_sessions[2], + &data)); } BOTAN_ASSERT_NOMSG(slicer.empty()); } @@ -106,7 +110,7 @@ const auto nodata = init_empty(); check_rc("Esys_SequenceComplete", - Esys_SequenceComplete(*m_handle.context(), + Esys_SequenceComplete(m_handle.context()->esys_context(), m_handle.transient_handle(), m_sessions[0], m_sessions[1], diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_key.cpp botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_key.cpp --- botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_key.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_key.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -15,6 +15,7 @@ #include #endif +#include #include #include #include @@ -76,16 +77,20 @@ Object object(ctx); check_rc("Esys_TR_FromTPMPublic", - Esys_TR_FromTPMPublic( - *ctx, persistent_object_handle, sessions[0], sessions[1], sessions[2], out_transient_handle(object))); + Esys_TR_FromTPMPublic(ctx->esys_context(), + persistent_object_handle, + sessions[0], + sessions[1], + sessions[2], + out_transient_handle(object))); if(!auth_value.empty()) { const auto user_auth = copy_into(auth_value); - check_rc("Esys_TR_SetAuth", Esys_TR_SetAuth(*ctx, object.transient_handle(), &user_auth)); + check_rc("Esys_TR_SetAuth", Esys_TR_SetAuth(ctx->esys_context(), object.transient_handle(), &user_auth)); } check_rc("Esys_TR_GetTpmHandle", - Esys_TR_GetTpmHandle(*ctx, object.transient_handle(), out_persistent_handle(object))); + Esys_TR_GetTpmHandle(ctx->esys_context(), object.transient_handle(), out_persistent_handle(object))); const auto key_type = object._public_info(sessions).pub->publicArea.type; BOTAN_ARG_CHECK(key_type == TPM2_ALG_RSA || key_type == TPM2_ALG_ECC, @@ -140,7 +145,7 @@ Object handle(ctx); check_rc("Esys_LoadExternal", - Esys_LoadExternal(*ctx, + Esys_LoadExternal(ctx->esys_context(), sessions[0], sessions[1], sessions[2], @@ -197,7 +202,7 @@ const auto private_data = copy_into(private_blob); check_rc("Esys_Load", - Esys_Load(*ctx, + Esys_Load(ctx->esys_context(), parent.handles().transient_handle(), sessions[0], sessions[1], @@ -208,7 +213,7 @@ if(!auth_value.empty()) { const auto user_auth = copy_into(auth_value); - check_rc("Esys_TR_SetAuth", Esys_TR_SetAuth(*ctx, handle.transient_handle(), &user_auth)); + check_rc("Esys_TR_SetAuth", Esys_TR_SetAuth(ctx->esys_context(), handle.transient_handle(), &user_auth)); } return create(std::move(handle), sessions, nullptr /* pull public info from handle */, private_blob); @@ -221,6 +226,8 @@ const TPM2B_SENSITIVE_CREATE& sensitive_data) { BOTAN_ASSERT_NONNULL(ctx); + // NOLINTBEGIN(*-branch-clone) + switch(key_template.type) { case TPM2_ALG_RSA: #if not defined(BOTAN_HAS_TPM2_RSA_ADAPTER) @@ -236,6 +243,8 @@ throw Invalid_Argument("Unsupported key type"); } + // NOLINTEND(*-branch-clone) + const auto marshalled_template = marshal_template(key_template); Object handle(ctx); @@ -250,7 +259,7 @@ // // See the Architecture Document, Section 27.1. check_rc("Esys_CreateLoaded", - Esys_CreateLoaded(*ctx, + Esys_CreateLoaded(ctx->esys_context(), parent, sessions[0], sessions[1], @@ -289,7 +298,7 @@ [[maybe_unused]] const SessionBundle& sessions, [[maybe_unused]] const TPM2B_PUBLIC* public_info, [[maybe_unused]] std::span private_blob) { - if(!public_info) { + if(public_info == nullptr) { public_info = handles._public_info(sessions).pub.get(); } diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_key.h botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_key.h --- botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_key.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_key.h 2026-05-07 01:38:28.000000000 +0000 @@ -87,7 +87,7 @@ const SessionBundle& sessions); public: - std::unique_ptr generate_another(Botan::RandomNumberGenerator&) const override { + std::unique_ptr generate_another(Botan::RandomNumberGenerator& /*rng*/) const override { throw Not_Implemented("Cannot generate a new TPM-based keypair from this asymmetric key"); } diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_object.cpp botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_object.cpp --- botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_object.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_object.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -82,9 +82,9 @@ // Only purely transient objects have to be flushed if(has_transient_handle()) { if(has_persistent_handle()) { - Esys_TR_Close(*m_ctx, &m_handles->transient); + Esys_TR_Close(m_ctx->esys_context(), &m_handles->transient); } else { - Esys_FlushContext(*m_ctx, m_handles->transient); + Esys_FlushContext(m_ctx->esys_context(), m_handles->transient); } } } @@ -136,7 +136,7 @@ m_public_info = std::make_unique(); check_rc("Esys_ReadPublic", - Esys_ReadPublic(*m_ctx, + Esys_ReadPublic(m_ctx->esys_context(), m_handles->transient, sessions[0], sessions[1], diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_pkops.cpp botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_pkops.cpp --- botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_pkops.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_pkops.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -54,7 +54,7 @@ auto do_sign = [this](const TPM2B_DIGEST& digest, const TPMT_TK_HASHCHECK& validation) { unique_esys_ptr signature; check_rc("Esys_Sign", - Esys_Sign(*key_handle().context(), + Esys_Sign(key_handle().context()->esys_context(), key_handle().transient_handle(), sessions()[0], sessions()[1], @@ -70,7 +70,7 @@ }; auto signature = [&] { - if(auto h = dynamic_cast(hash())) { + if(auto* h = dynamic_cast(hash())) { // This is a TPM2-based hash object that calculated the digest on // the TPM. We can use the validation ticket to create the signature. auto [digest, validation] = h->final_with_ticket(); @@ -108,7 +108,7 @@ // If the signature is not valid, this returns TPM2_RC_SIGNATURE. const auto rc = check_rc_expecting("Esys_VerifySignature", - Esys_VerifySignature(*key_handle().context(), + Esys_VerifySignature(key_handle().context()->esys_context(), key_handle().transient_handle(), sessions()[0], sessions()[1], diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_rng.cpp botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_rng.cpp --- botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_rng.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_rng.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,8 @@ #include +#include +#include #include #include @@ -29,7 +31,8 @@ const size_t chunk = std::min(in.remaining(), MAX_STIR_RANDOM_SIZE); const auto data = copy_into(in.take(chunk)); - check_rc("Esys_StirRandom", Esys_StirRandom(*m_ctx, m_sessions[0], m_sessions[1], m_sessions[2], &data)); + check_rc("Esys_StirRandom", + Esys_StirRandom(m_ctx->esys_context(), m_sessions[0], m_sessions[1], m_sessions[2], &data)); } BOTAN_ASSERT_NOMSG(in.empty()); @@ -38,7 +41,7 @@ unique_esys_ptr digest = nullptr; const auto requested_bytes = std::min(out.remaining_capacity(), m_max_tpm2_rng_bytes); check_rc("Esys_GetRandom", - Esys_GetRandom(*m_ctx, + Esys_GetRandom(m_ctx->esys_context(), m_sessions[0], m_sessions[1], m_sessions[2], diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_rng.h botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_rng.h --- botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_rng.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_rng.h 2026-05-07 01:38:28.000000000 +0000 @@ -21,7 +21,7 @@ */ class BOTAN_PUBLIC_API(3, 6) RandomNumberGenerator final : public Hardware_RNG { public: - RandomNumberGenerator(std::shared_ptr ctx, SessionBundle sessions = {}); + BOTAN_FUTURE_EXPLICIT RandomNumberGenerator(std::shared_ptr ctx, SessionBundle sessions = {}); bool accepts_input() const override { return true; } diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_rsa/info.txt botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_rsa/info.txt --- botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_rsa/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_rsa/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -9,7 +9,7 @@ rsa -pk_pad +sig_padding diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.cpp botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.cpp --- botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -13,9 +13,9 @@ #include #include -#include #include #include +#include #include #include #include @@ -26,17 +26,6 @@ namespace Botan::TPM2 { -std::pair rsa_pubkey_components_from_tss2_public(const TPM2B_PUBLIC* public_area) { - BOTAN_ASSERT_NONNULL(public_area); - const auto& pub = public_area->publicArea; - BOTAN_ARG_CHECK(pub.type == TPM2_ALG_RSA, "Public key is not an RSA key"); - - // TPM2 may report 0 when the exponent is 'the default' (2^16 + 1) - const auto exponent = (pub.parameters.rsaDetail.exponent == 0) ? 65537 : pub.parameters.rsaDetail.exponent; - - return {BigInt(as_span(pub.unique.rsa)), exponent}; -} - RSA_PublicKey::RSA_PublicKey(Object handle, SessionBundle session_bundle, const TPM2B_PUBLIC* public_blob) : Botan::TPM2::RSA_PublicKey( std::move(handle), std::move(session_bundle), rsa_pubkey_components_from_tss2_public(public_blob)) {} @@ -73,7 +62,7 @@ std::optional exponent) { BOTAN_ARG_CHECK(parent.is_parent(), "The passed key cannot be used as a parent key"); - TPM2B_SENSITIVE_CREATE sensitive_data = { + const TPM2B_SENSITIVE_CREATE sensitive_data = { .size = 0, // ignored .sensitive = { @@ -86,7 +75,7 @@ }, }; - TPMT_PUBLIC key_template = { + const TPMT_PUBLIC key_template = { .type = TPM2_ALG_RSA, // This is the algorithm for fingerprinting the newly created public key. @@ -171,7 +160,7 @@ }; } -size_t signature_length_for_key_handle(const SessionBundle& sessions, const Object& key_handle) { +size_t signature_length_for_rsa_key_handle(const SessionBundle& sessions, const Object& key_handle) { return key_handle._public_info(sessions, TPM2_ALG_RSA).pub->publicArea.parameters.rsaDetail.keyBits / 8; } @@ -180,7 +169,7 @@ RSA_Signature_Operation(const Object& object, const SessionBundle& sessions, std::string_view padding) : Signature_Operation(object, sessions, select_signature_algorithms(padding)) {} - size_t signature_length() const override { return signature_length_for_key_handle(sessions(), key_handle()); } + size_t signature_length() const override { return signature_length_for_rsa_key_handle(sessions(), key_handle()); } AlgorithmIdentifier algorithm_identifier() const override { // TODO: This is essentially a copy of the ::algorithm_identifier() @@ -192,21 +181,20 @@ // // TODO: This is a hack, and we should clean this up. BOTAN_STATE_CHECK(padding().has_value()); - const auto emsa = EMSA::create_or_throw(padding().value()); - const std::string emsa_name = emsa->name(); + const std::string padding_name = SignaturePaddingScheme::create_or_throw(padding().value())->name(); try { - const std::string full_name = "RSA/" + emsa_name; + const std::string full_name = "RSA/" + padding_name; const OID oid = OID::from_string(full_name); return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_EMPTY_PARAM); } catch(Lookup_Error&) {} - if(emsa_name.starts_with("PSS(")) { - auto parameters = PSS_Params::from_emsa_name(emsa_name).serialize(); + if(padding_name.starts_with("PSS(")) { + auto parameters = PSS_Params::from_padding_name(padding_name).serialize(); return AlgorithmIdentifier("RSA/PSS", parameters); } - throw Invalid_Argument(fmt("Signatures using RSA/{} are not supported", emsa_name)); + throw Invalid_Argument(fmt("Signatures using RSA/{} are not supported", padding_name)); } private: @@ -232,7 +220,7 @@ private: TPMT_SIGNATURE unmarshal_signature(std::span signature) const override { - BOTAN_ARG_CHECK(signature.size() == signature_length_for_key_handle(sessions(), key_handle()), + BOTAN_ARG_CHECK(signature.size() == signature_length_for_rsa_key_handle(sessions(), key_handle()), "Unexpected signature byte length"); TPMT_SIGNATURE sig; @@ -279,7 +267,7 @@ unique_esys_ptr ciphertext; check_rc("Esys_RSA_Encrypt", - Esys_RSA_Encrypt(*m_key_handle.context(), + Esys_RSA_Encrypt(m_key_handle.context()->esys_context(), m_key_handle.transient_handle(), m_sessions[0], m_sessions[1], @@ -358,7 +346,7 @@ // all cases here. It passed the test (with a faulty ciphertext), // but I didn't find this to be clearly documented. :-( auto rc = check_rc_expecting("Esys_RSA_Decrypt", - Esys_RSA_Decrypt(*m_key_handle.context(), + Esys_RSA_Decrypt(m_key_handle.context()->esys_context(), m_key_handle.transient_handle(), m_sessions[0], m_sessions[1], diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.h botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.h --- botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.h 2026-05-07 01:38:28.000000000 +0000 @@ -13,15 +13,6 @@ namespace Botan::TPM2 { -/** - * This helper function transforms a @p public_blob in a TPM2B_PUBLIC* format - * into the functional components of an RSA public key. Namely, a pair of - * modulus and exponent as big integers. - * - * @param public_blob The public blob to decompose into RSA pubkey components - */ -std::pair rsa_pubkey_components_from_tss2_public(const TPM2B_PUBLIC* public_blob); - BOTAN_DIAGNOSTIC_PUSH BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_session.cpp botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_session.cpp --- botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_session.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_session.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -49,7 +49,7 @@ BOTAN_ASSERT_NONNULL(ctx); check_rc("Esys_StartSession", - Esys_StartAuthSession(*ctx, + Esys_StartAuthSession(ctx->esys_context(), ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, @@ -80,7 +80,7 @@ BOTAN_ASSERT_NONNULL(ctx); check_rc("Esys_StartSession", - Esys_StartAuthSession(*ctx, + Esys_StartAuthSession(ctx->esys_context(), tpm_key.handles().transient_handle(), tpm_key.handles().transient_handle(), ESYS_TR_NONE, @@ -105,25 +105,38 @@ } SessionAttributes Session::attributes() const { - TPMA_SESSION attrs; + TPMA_SESSION attrs = 0; check_rc("Esys_TRSess_GetAttributes", - Esys_TRSess_GetAttributes(*m_session.context(), m_session.transient_handle(), &attrs)); + Esys_TRSess_GetAttributes(m_session.context()->esys_context(), m_session.transient_handle(), &attrs)); return SessionAttributes::read(attrs); } void Session::set_attributes(SessionAttributes attributes) { check_rc("Esys_TRSess_SetAttributes", - Esys_TRSess_SetAttributes( - *m_session.context(), m_session.transient_handle(), SessionAttributes::render(attributes), 0xFF)); + Esys_TRSess_SetAttributes(m_session.context()->esys_context(), + m_session.transient_handle(), + SessionAttributes::render(attributes), + 0xFF)); } secure_vector Session::tpm_nonce() const { unique_esys_ptr nonce; check_rc("Esys_TRSess_GetNonceTPM", - Esys_TRSess_GetNonceTPM(*m_session.context(), m_session.transient_handle(), out_ptr(nonce))); + Esys_TRSess_GetNonceTPM(m_session.context()->esys_context(), m_session.transient_handle(), out_ptr(nonce))); return copy_into>(*nonce); } +detail::SessionHandle::SessionHandle(Session& session) : + m_session(session), m_original_attributes(session.attributes()) {} + +detail::SessionHandle::~SessionHandle() { + try { + if(m_session) { + m_session->get().set_attributes(m_original_attributes); + } + } catch(...) {} +} + [[nodiscard]] detail::SessionHandle::operator ESYS_TR() && noexcept { if(m_session) { return m_session->get().transient_handle(); diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_session.h botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_session.h --- botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_session.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_session.h 2026-05-07 01:38:28.000000000 +0000 @@ -69,12 +69,14 @@ SessionHandle& operator=(SessionHandle&&) = delete; ~SessionHandle(); + + // NOLINTNEXTLINE(*-explicit-conversions) Intentional: SessionHandle is a wrapper around ESYS_TR for C API interop [[nodiscard]] operator ESYS_TR() && noexcept; private: friend class Botan::TPM2::Session; - SessionHandle(Session& session); + explicit SessionHandle(Session& session); private: std::optional> m_session; @@ -138,7 +140,7 @@ */ Session(std::shared_ptr ctx, ESYS_TR session_handle) : m_session(std::move(ctx), session_handle) {} - [[nodiscard]] detail::SessionHandle handle() { return *this; } + [[nodiscard]] detail::SessionHandle handle() { return detail::SessionHandle(*this); } SessionAttributes attributes() const; void set_attributes(SessionAttributes attributes); @@ -156,15 +158,6 @@ Object m_session; }; -inline detail::SessionHandle::~SessionHandle() { - if(m_session) { - m_session->get().set_attributes(m_original_attributes); - } -} - -inline detail::SessionHandle::SessionHandle(Session& session) : - m_session(session), m_original_attributes(session.attributes()) {} - /** * This bundles up to three sessions into a single object to be used in a * single TSS2 library function call to simplify passing the sessions around @@ -172,6 +165,7 @@ */ class SessionBundle { public: + // NOLINTNEXTLINE(*-explicit-conversions) Intentional: Allows convenient single-session usage without explicit SessionBundle construction SessionBundle(std::shared_ptr s1 = nullptr, std::shared_ptr s2 = nullptr, std::shared_ptr s3 = nullptr) : diff -Nru botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_util.h botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_util.h --- botan3-3.7.1+dfsg/src/lib/prov/tpm2/tpm2_util.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/prov/tpm2/tpm2_util.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #ifndef BOTAN_TPM2_UTIL_H_ #define BOTAN_TPM2_UTIL_H_ +#include #include #include #include @@ -47,9 +48,9 @@ namespace Botan::TPM2 { /** - * Check the return code and throw an exception if some error occured. + * Check the return code and throw an exception if some error occurred. * - * @throws TPM2::Error if an error occured. + * @throws TPM2::Error if an error occurred. */ constexpr void check_rc(std::string_view location, TSS2_RC rc) { if(rc != TSS2_RC_SUCCESS) { @@ -58,13 +59,13 @@ } /** - * Check the return code and throw an exception if an unexpected error occured. + * Check the return code and throw an exception if an unexpected error occurred. * * Errors that are listed in the `expected_errors` parameter are considered * expected and will not cause an exception to be thrown. Instead the error * code is decoded and returned to the caller for further processing. * - * @throws TPM2::Error if an unexpected error occured. + * @throws TPM2::Error if an unexpected error occurred. * @returns TSS2_RC_SUCCESS or one of the expected error codes. */ template @@ -75,7 +76,7 @@ return rc; } - // An error occured, we need to decode it to check if it was expected. + // An error occurred, we need to decode it to check if it was expected. const TSS2_RC decoded_rc = get_raw_rc(rc); // Check if the error is one of the expected and return those to the caller. @@ -122,7 +123,7 @@ /// provided @p data is not larger than the capacity of the buffer type. template constexpr T copy_into(std::span data) { - T result; + T result{}; copy_into(result, data); return result; } @@ -140,7 +141,7 @@ /// Create a TPM2 buffer of a given type and @p length. template constexpr T init_with_size(size_t length) { - T result; + T result{}; BOTAN_ASSERT_NOMSG(length <= sizeof(result.buffer)); result.size = static_cast(length); clear_bytes(result.buffer, length); @@ -181,7 +182,7 @@ */ class ObjectSetter { public: - constexpr ObjectSetter(Object& object, bool persistent = false) : + constexpr explicit ObjectSetter(Object& object, bool persistent = false) : m_object(object), m_persistent(persistent), m_handle(persistent ? 0 : ESYS_TR_NONE) {} constexpr ~ObjectSetter() noexcept { @@ -201,6 +202,7 @@ ObjectSetter& operator=(const ObjectSetter&) = delete; ObjectSetter& operator=(ObjectSetter&&) = delete; + // NOLINTNEXTLINE(*-explicit-conversions) FIXME [[nodiscard]] constexpr operator uint32_t*() && noexcept { return &m_handle; } private: @@ -248,7 +250,7 @@ * * @tparam UnderlyingT the TPMA_* bit field type * @tparam AttributeWrapperT the C++ struct type that wraps the TPMA_* bit field - * @tparam props a bunch of std::pair mappping boolean members of + * @tparam props a bunch of std::pair mapping boolean members of * AttributeWrapperT to the bit masks of the TPMA_* type */ template &> FnT> - static constexpr void for_all(FnT&& fn) { + static constexpr void for_all(const FnT& fn) { (fn(props), ...); } @@ -287,6 +289,28 @@ } }; +#if defined(BOTAN_HAS_RSA) + +/** + * This helper function transforms a @p public_blob in a TPM2B_PUBLIC* format + * into the functional components of an RSA public key. Namely, a pair of + * modulus and exponent as big integers. + * + * @param public_blob The public blob to decompose into RSA pubkey components + */ +inline std::pair rsa_pubkey_components_from_tss2_public(const TPM2B_PUBLIC* public_blob) { + BOTAN_ASSERT_NONNULL(public_blob); + const auto& pub = public_blob->publicArea; + BOTAN_ARG_CHECK(pub.type == TPM2_ALG_RSA, "Public key is not an RSA key"); + + // TPM2 may report 0 when the exponent is 'the default' (2^16 + 1) + const auto exponent = (pub.parameters.rsaDetail.exponent == 0) ? 65537 : pub.parameters.rsaDetail.exponent; + + return {BigInt(as_span(pub.unique.rsa)), exponent}; +} + +#endif + } // namespace Botan::TPM2 #endif diff -Nru botan3-3.7.1+dfsg/src/lib/psk_db/psk_db.cpp botan3-3.12.0+dfsg/src/lib/psk_db/psk_db.cpp --- botan3-3.7.1+dfsg/src/lib/psk_db/psk_db.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/psk_db/psk_db.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,18 +10,18 @@ #include #include #include -#include #include +#include namespace Botan { std::string PSK_Database::get_str(std::string_view name) const { - secure_vector psk = this->get(name); - return std::string(cast_uint8_ptr_to_char(psk.data()), psk.size()); + return bytes_to_string(this->get(name)); } void PSK_Database::set_str(std::string_view name, std::string_view psk) { - this->set(name, cast_char_ptr_to_uint8(psk.data()), psk.size()); + auto pskb = as_span_of_bytes(psk); + this->set(name, pskb.data(), pskb.size()); } Encrypted_PSK_Database::Encrypted_PSK_Database(const secure_vector& master_key) { @@ -45,7 +45,7 @@ const secure_vector raw_name = base64_decode(enc_name); const secure_vector name_bits = nist_key_unwrap_padded(raw_name.data(), raw_name.size(), *m_cipher); - std::string pt_name(cast_uint8_ptr_to_char(name_bits.data()), name_bits.size()); + const auto pt_name = bytes_to_string(name_bits); names.insert(pt_name); } catch(Invalid_Authentication_Tag&) {} } @@ -54,15 +54,13 @@ } void Encrypted_PSK_Database::remove(std::string_view name) { - const std::vector wrapped_name = - nist_key_wrap_padded(cast_char_ptr_to_uint8(name.data()), name.size(), *m_cipher); + const auto wrapped_name = nist_key_wrap_padded(as_span_of_bytes(name), *m_cipher); this->kv_del(base64_encode(wrapped_name)); } secure_vector Encrypted_PSK_Database::get(std::string_view name) const { - const std::vector wrapped_name = - nist_key_wrap_padded(cast_char_ptr_to_uint8(name.data()), name.size(), *m_cipher); + const auto wrapped_name = nist_key_wrap_padded(as_span_of_bytes(name), *m_cipher); const std::string val_base64 = kv_get(base64_encode(wrapped_name)); @@ -80,12 +78,11 @@ void Encrypted_PSK_Database::set(std::string_view name, const uint8_t val[], size_t len) { /* - * Both as a basic precaution wrt key seperation, and specifically to prevent + * Both as a basic precaution wrt key separation, and specifically to prevent * cut-and-paste attacks against the database, each PSK is encrypted with a * distinct key which is derived by hashing the wrapped key name with HMAC. */ - const std::vector wrapped_name = - nist_key_wrap_padded(cast_char_ptr_to_uint8(name.data()), name.size(), *m_cipher); + const auto wrapped_name = nist_key_wrap_padded(as_span_of_bytes(name), *m_cipher); auto wrap_cipher = m_cipher->new_object(); wrap_cipher->set_key(m_hmac->process(wrapped_name)); diff -Nru botan3-3.7.1+dfsg/src/lib/psk_db/psk_db.h botan3-3.12.0+dfsg/src/lib/psk_db/psk_db.h --- botan3-3.7.1+dfsg/src/lib/psk_db/psk_db.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/psk_db/psk_db.h 2026-05-07 01:38:28.000000000 +0000 @@ -23,7 +23,7 @@ * It might be implemented as a plaintext storage or via some mechanism * that encrypts the keys and/or values. */ -class BOTAN_PUBLIC_API(2, 4) PSK_Database { +class BOTAN_PUBLIC_API(2, 4) PSK_Database /* NOLINT(*-special-member-functions) */ { public: /** * @returns the set of names for which get() will return a value. @@ -89,7 +89,7 @@ * Subclasses must implement the virtual calls to handle storing and getting raw * (base64 encoded) values. */ -class BOTAN_PUBLIC_API(2, 4) Encrypted_PSK_Database : public PSK_Database { +class BOTAN_PUBLIC_API(2, 4) Encrypted_PSK_Database : public PSK_Database /* NOLINT(*-special-member-functions) */ { public: /** * Initializes or opens a PSK database. The @p master_key is used to secure @@ -113,7 +113,7 @@ * using a password, it is recommended to use Argon2id to derive the database * master key. */ - Encrypted_PSK_Database(const secure_vector& master_key); + BOTAN_FUTURE_EXPLICIT Encrypted_PSK_Database(const secure_vector& master_key); ~Encrypted_PSK_Database() override; @@ -171,6 +171,11 @@ ~Encrypted_PSK_Database_SQL() override; + Encrypted_PSK_Database_SQL(const Encrypted_PSK_Database_SQL& other) = delete; + Encrypted_PSK_Database_SQL(Encrypted_PSK_Database_SQL&& other) = delete; + Encrypted_PSK_Database_SQL& operator=(const Encrypted_PSK_Database_SQL& other) = delete; + Encrypted_PSK_Database_SQL& operator=(Encrypted_PSK_Database_SQL&& other) = delete; + private: void kv_set(std::string_view index, std::string_view value) override; std::string kv_get(std::string_view index) const override; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/blinding/blinding.cpp botan3-3.12.0+dfsg/src/lib/pubkey/blinding/blinding.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/blinding/blinding.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/blinding/blinding.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,7 +9,7 @@ namespace Botan { -Blinder::Blinder(const Modular_Reducer& reducer, +Blinder::Blinder(const Barrett_Reduction& reducer, RandomNumberGenerator& rng, std::function fwd, std::function inv) : @@ -17,9 +17,7 @@ m_rng(rng), m_fwd_fn(std::move(fwd)), m_inv_fn(std::move(inv)), - m_modulus_bits(reducer.get_modulus().bits()), - m_e{}, - m_d{}, + m_modulus_bits(reducer.modulus_bits()), m_counter{} { const BigInt k = blinding_nonce(); m_e = m_fwd_fn(k); @@ -33,7 +31,7 @@ BigInt Blinder::blind(const BigInt& i) const { ++m_counter; - if((BOTAN_BLINDING_REINIT_INTERVAL > 0) && (m_counter > BOTAN_BLINDING_REINIT_INTERVAL)) { + if((ReinitInterval > 0) && (m_counter > ReinitInterval)) { const BigInt k = blinding_nonce(); m_e = m_fwd_fn(k); m_d = m_inv_fn(k); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/blinding/blinding.h botan3-3.12.0+dfsg/src/lib/pubkey/blinding/blinding.h --- botan3-3.7.1+dfsg/src/lib/pubkey/blinding/blinding.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/blinding/blinding.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,7 +9,7 @@ #define BOTAN_BLINDER_H_ #include -#include +#include #include namespace Botan { @@ -22,12 +22,29 @@ class Blinder final { public: /** + * Normally blinding is performed by choosing a random starting point (plus + * its inverse, of a form appropriate to the algorithm being blinded), and + * then choosing new blinding operands by successive squaring of both + * values. This is much faster than computing a new starting point but + * introduces some possible correlation + * + * To avoid possible leakage problems in long-running processes, the blinder + * periodically reinitializes the sequence. This value specifies how often + * a new sequence should be started. + * + * If set to zero, reinitialization is disabled + */ + static constexpr size_t ReinitInterval = 64; + + /** * Blind a value. - * The blinding nonce k is freshly generated after - * BOTAN_BLINDING_REINIT_INTERVAL calls to blind(). - * BOTAN_BLINDING_REINIT_INTERVAL = 0 means a fresh - * nonce is only generated once. On every other call, - * an updated nonce is used for blinding: k' = k*k mod n. + * + * The blinding nonce k is freshly generated after ReinitInterval + * calls to blind(). + * + * ReinitInterval = 0 means a fresh nonce is only generated once. + * On every other call, the next nonce is derived via modular squaring. + * * @param x value to blind * @return blinded value */ @@ -51,21 +68,23 @@ * @note Lifetime: The rng and reducer arguments are captured by * reference and must live as long as the Blinder does */ - Blinder(const Modular_Reducer& reducer, + Blinder(const Barrett_Reduction& reducer, RandomNumberGenerator& rng, std::function fwd_func, std::function inv_func); Blinder(const Blinder&) = delete; - + Blinder(Blinder&&) = default; Blinder& operator=(const Blinder&) = delete; + Blinder& operator=(Blinder&&) = delete; + ~Blinder() = default; RandomNumberGenerator& rng() const { return m_rng; } private: BigInt blinding_nonce() const; - const Modular_Reducer& m_reducer; + const Barrett_Reduction& m_reducer; RandomNumberGenerator& m_rng; std::function m_fwd_fn; std::function m_inv_fn; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/blinding/info.txt botan3-3.12.0+dfsg/src/lib/pubkey/blinding/info.txt --- botan3-3.7.1+dfsg/src/lib/pubkey/blinding/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/blinding/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,7 +1,3 @@ - -PUBLIC_KEY_BLINDING -> 20250125 - - name -> "Public Key Blinding" brief -> "Helper for BigInt blinding" diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce.cpp botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,13 +11,9 @@ #include #include #include -#include #include #include #include -#include - -#include namespace Botan { @@ -69,7 +65,7 @@ return m_public->matrix().bytes(); } -bool Classic_McEliece_PublicKey::check_key(RandomNumberGenerator&, bool) const { +bool Classic_McEliece_PublicKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const { return true; } @@ -126,7 +122,7 @@ return m_private->serialize(); } -bool Classic_McEliece_PrivateKey::check_key(RandomNumberGenerator&, bool) const { +bool Classic_McEliece_PrivateKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const { return m_private->check_key(); } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce.h botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce.h --- botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce.h 2026-05-07 01:38:28.000000000 +0000 @@ -73,7 +73,7 @@ std::vector raw_public_key_bits() const override; - bool check_key(RandomNumberGenerator&, bool) const override; + bool check_key(RandomNumberGenerator& rng, bool strong) const override; bool supports_operation(PublicKeyOperation op) const override { return (op == PublicKeyOperation::KeyEncapsulation); @@ -88,8 +88,7 @@ Classic_McEliece_PublicKey() = default; protected: - std::shared_ptr - m_public; // NOLINT(misc-non-private-member-variables-in-classes) + std::shared_ptr m_public; // NOLINT(*-non-private-member-variable*) }; BOTAN_DIAGNOSTIC_PUSH @@ -128,7 +127,7 @@ secure_vector raw_private_key_bits() const override; - bool check_key(RandomNumberGenerator&, bool) const override; + bool check_key(RandomNumberGenerator& rng, bool strong) const override; std::unique_ptr create_kem_decryption_op(RandomNumberGenerator& rng, std::string_view params, diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_decaps.cpp botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_decaps.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_decaps.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_decaps.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,6 +11,9 @@ #include +#include +#include + namespace Botan { Classic_McEliece_Polynomial Classic_McEliece_Decryptor::compute_goppa_syndrome( @@ -97,9 +100,9 @@ const auto locator = berlekamp_massey(m_key->params(), syndrome); std::vector images; - const auto alphas = m_key->field_ordering().alphas(m_key->params().n()); - std::transform( - alphas.begin(), alphas.end(), std::back_inserter(images), [&](const auto& alpha) { return locator(alpha); }); + for(const auto& alpha : m_key->field_ordering().alphas(m_key->params().n())) { + images.push_back(locator(alpha)); + } // Obtain e and check whether wt(e) = t. locator(alpha_i) = 0 <=> error at position i CmceErrorVector e; @@ -117,7 +120,7 @@ syndromes_are_eq &= GF_Mask::is_equal(syndrome.coef_at(i), syndrome_from_e.coef_at(i)); } - decode_success &= syndromes_are_eq.elem_mask(); + decode_success &= CT::Mask(syndromes_are_eq.elem_mask()); return {decode_success, std::move(e)}; } @@ -153,7 +156,7 @@ hash_func->update(0x02); hash_func->update(e_bytes); const auto c1_p = hash_func->final_stdvec(); - const CT::Mask eq_mask = CT::is_equal(c1.data(), c1_p.data(), c1.size()); + const CT::Mask eq_mask = CT::is_equal(c1, c1_p); eq_mask.select_n(e_bytes.data(), e_bytes.data(), m_key->s().data(), m_key->s().size()); b = eq_mask.select(b, 0); } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_decaps.h botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_decaps.h --- botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_decaps.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_decaps.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,7 +11,6 @@ #include #include -#include #include #include #include diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_encaps.cpp botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_encaps.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_encaps.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_encaps.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,9 +8,12 @@ * * Botan is released under the Simplified BSD License (see license.txt) **/ + #include #include +#include +#include namespace Botan { @@ -24,7 +27,7 @@ const Classic_McEliece_Parameters& params, RandomNumberGenerator& rng) const { const auto rand = rng.random_vec((params.sigma1() / 8) * params.tau()); CT::poison(rand); - uint16_t mask_m = (uint32_t(1) << params.m()) - 1; // Only take m least significant bits + const uint16_t mask_m = (uint32_t(1) << params.m()) - 1; // Only take m least significant bits secure_vector a_values; a_values.reserve(params.tau()); BufferSlicer rand_slicer(rand); @@ -37,7 +40,7 @@ // This side channel only leaks which random elements are selected and which are dropped, // but no information about their content is leaked. d &= mask_m; - bool d_in_range = d < params.n(); + const bool d_in_range = d < params.n(); CT::unpoison(d_in_range); if(d_in_range && a_values.size() < params.t()) { a_values.push_back(d); @@ -51,7 +54,7 @@ // Step 4: Restart if not all a_i are distinct for(size_t i = 1; i < params.t(); ++i) { for(size_t j = 0; j < i; ++j) { - bool a_i_j_equal = a_values.at(i) == a_values.at(j); + const bool a_i_j_equal = a_values.at(i) == a_values.at(j); CT::unpoison(a_i_j_equal); if(a_i_j_equal) { return std::nullopt; @@ -95,8 +98,8 @@ const CmceErrorVector e = [&] { // Emergency abort in case unexpected logical error to prevent endless loops // Success probability: >24% per attempt (25% that elements are distinct * 96% enough elements are in range) - // => 203 attempts for 2^(-80) fail probability - constexpr size_t MAX_ATTEMPTS = 203; + // => 647 attempts for 2^(-256) fail probability + constexpr size_t MAX_ATTEMPTS = 647; for(size_t attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) { if(auto maybe_e = fixed_weight_vector_gen(params, rng)) { return maybe_e.value(); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_field_ordering.cpp botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_field_ordering.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_field_ordering.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_field_ordering.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,12 +8,12 @@ * * Botan is released under the Simplified BSD License (see license.txt) **/ + #include -#include -#include +#include #include - +#include #include #include #include @@ -287,7 +287,7 @@ const auto control_bits_as_words = generate_control_bits_internal(m_pi.get()); auto control_bits = secure_bitvector(control_bits_as_words.size()); for(size_t i = 0; i < control_bits.size(); ++i) { - control_bits.at(i) = control_bits_as_words.at(i); + control_bits.at(i) = control_bits_as_words.at(i) != 0; } return control_bits; @@ -303,9 +303,9 @@ std::iota(pi.begin(), pi.end(), static_cast(0)); for(size_t i = 0; i < 2 * params.m() - 1; ++i) { const size_t gap = size_t(1) << std::min(i, 2 * params.m() - 2 - i); - for(size_t j = 0; j < size_t(n / 2); ++j) { + for(size_t j = 0; j < size_t(n) / 2; ++j) { const size_t pos = (j % gap) + 2 * gap * (j / gap); - auto mask = CT::Mask::expand(control_bits[i * n / 2 + j]); + auto mask = CT::Mask::expand_bool(control_bits[i * n / 2 + j]); mask.conditional_swap(pi[pos], pi[pos + gap]); } } @@ -320,8 +320,8 @@ for(size_t p_idx = 1; p_idx <= Classic_McEliece_Parameters::mu(); ++p_idx) { size_t p_counter = 0; for(size_t col = 0; col < Classic_McEliece_Parameters::nu(); ++col) { - auto mask_is_pivot_set = CT::Mask::expand(pivots.at(col)); - p_counter += CT::Mask::expand(pivots.at(col)).if_set_return(1); + auto mask_is_pivot_set = CT::Mask::expand_bool(pivots.at(col)); + p_counter += mask_is_pivot_set.if_set_return(1); auto mask_is_current_pivot = CT::Mask::is_equal(p_idx, p_counter); (mask_is_pivot_set & mask_is_current_pivot) .conditional_swap(m_pi.get().at(col_offset + col), m_pi.get().at(col_offset + p_idx - 1)); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_field_ordering.h botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_field_ordering.h --- botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_field_ordering.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_field_ordering.h 2026-05-07 01:38:28.000000000 +0000 @@ -12,8 +12,6 @@ #include #include -#include - namespace Botan { /** @@ -22,7 +20,7 @@ * Field ordering corresponds to the permutation pi defining the alpha sequence in * the Classic McEliece specification (see Classic McEliece ISO Sec. 8.2.). */ -class BOTAN_TEST_API Classic_McEliece_Field_Ordering { +class BOTAN_TEST_API Classic_McEliece_Field_Ordering final { public: /** * @brief Creates a field ordering from a random bit sequence. Corresponds to diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_gf.cpp botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_gf.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_gf.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_gf.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -47,8 +47,8 @@ Classic_McEliece_GF Classic_McEliece_GF::operator*(Classic_McEliece_GF other) const { BOTAN_ASSERT_NOMSG(m_modulus == other.m_modulus); - uint32_t a = m_elem.get(); - uint32_t b = other.m_elem.get(); + const uint32_t a = m_elem.get(); + const uint32_t b = other.m_elem.get(); uint32_t acc = a * (b & CT::value_barrier(1)); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_gf.h botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_gf.h --- botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_gf.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_gf.h 2026-05-07 01:38:28.000000000 +0000 @@ -13,7 +13,6 @@ #include #include #include -#include namespace Botan { @@ -27,7 +26,7 @@ * the coefficient of z^i. For example, the element (z^3 + z^2 + 1) is represented * by the uint16_t 0b1101. */ -class BOTAN_TEST_API Classic_McEliece_GF { +class BOTAN_TEST_API Classic_McEliece_GF final { public: /** * @brief Creates an element of GF(q) from a uint16_t. @@ -171,7 +170,7 @@ static GF_Mask set() { return GF_Mask(CT::Mask::set()); } - GF_Mask(CT::Mask underlying_mask) : m_mask(underlying_mask) {} + explicit GF_Mask(CT::Mask underlying_mask) : m_mask(underlying_mask) {} Classic_McEliece_GF if_set_return(const Classic_McEliece_GF x) const { return Classic_McEliece_GF(CmceGfElem(m_mask.if_set_return(x.elem().get())), x.modulus()); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_keys_internal.cpp botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_keys_internal.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_keys_internal.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_keys_internal.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,9 @@ #include +#include +#include + namespace Botan { namespace { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_keys_internal.h botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_keys_internal.h --- botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_keys_internal.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_keys_internal.h 2026-05-07 01:38:28.000000000 +0000 @@ -27,7 +27,7 @@ * - The Classic McEliece parameters * - The public key matrix */ -class BOTAN_TEST_API Classic_McEliece_PublicKeyInternal { +class BOTAN_TEST_API Classic_McEliece_PublicKeyInternal final { public: /** * @brief Construct a Classic McEliece public key. @@ -86,7 +86,7 @@ * - The field ordering alpha * - The seed s for implicit rejection */ -class BOTAN_TEST_API Classic_McEliece_PrivateKeyInternal { +class BOTAN_TEST_API Classic_McEliece_PrivateKeyInternal final { public: /** * @brief Construct a Classic McEliece private key. @@ -187,8 +187,8 @@ * @brief Representation of a Classic McEliece key pair. */ struct BOTAN_TEST_API Classic_McEliece_KeyPair_Internal { - std::shared_ptr private_key; - std::shared_ptr public_key; + std::shared_ptr private_key; // NOLINT(*non-private-member-variable*) + std::shared_ptr public_key; // NOLINT(*non-private-member-variable*) /** * @brief Generate a Classic McEliece key pair using the algorithm described diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_matrix.cpp botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_matrix.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_matrix.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_matrix.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -13,6 +13,8 @@ #include #include +#include +#include namespace Botan { @@ -31,10 +33,10 @@ } /// Swaps bit i with bit j in val -void swap_bits(uint64_t& val, size_t i, size_t j) { - uint64_t bit_i = (val >> i) & CT::value_barrier(1); - uint64_t bit_j = (val >> j) & CT::value_barrier(1); - uint64_t xor_sum = bit_i ^ bit_j; +void swap_1_bit(uint64_t& val, size_t i, size_t j) { + const uint64_t bit_i = (val >> i) & CT::value_barrier(1); + const uint64_t bit_j = (val >> j) & CT::value_barrier(1); + const uint64_t xor_sum = bit_i ^ bit_j; val ^= (xor_sum << i); val ^= (xor_sum << j); } @@ -64,8 +66,9 @@ for(size_t i = 0; i < params.t(); ++i) { for(size_t j = 0; j < params.n(); ++j) { + const auto inv_g = inv_g_of_alpha[j].elem().get(); for(size_t alpha_i_j_bit = 0; alpha_i_j_bit < params.m(); ++alpha_i_j_bit) { - mat[i * params.m() + alpha_i_j_bit][j] = (uint16_t(1) << alpha_i_j_bit) & inv_g_of_alpha[j].elem().get(); + mat[i * params.m() + alpha_i_j_bit][j] = static_cast((inv_g >> alpha_i_j_bit) & 1); } } // Update for the next i so that: @@ -97,7 +100,7 @@ // To find which columns need to be swapped to allow for a systematic matrix form, we need to // investigate how a gauss algorithm affects the last mu rows of the swap area. - std::array sub_mat; + std::array sub_mat; // NOLINT(*-member-init) // Extract the bottom mu x nu matrix at offset pos_offset for(size_t i = 0; i < Classic_McEliece_Parameters::mu(); i++) { @@ -126,7 +129,7 @@ // Using the row accumulator we can predict the index of the pivot // bit for the current row, i.e., the first index where we can set // the bit to one row by adding any subsequent row - size_t current_pivot_idx = count_lsb_zeros(row_acc); + const size_t current_pivot_idx = count_lsb_zeros(row_acc); pivot_indices.at(row_idx) = current_pivot_idx; // Add subsequent rows to the current row, until the pivot @@ -155,14 +158,14 @@ for(auto pivot_idx : pivot_indices) { for(size_t i = 0; i < Classic_McEliece_Parameters::nu(); ++i) { auto mask_is_at_current_idx = Botan::CT::Mask::is_equal(i, pivot_idx); - pivots.at(i) = mask_is_at_current_idx.select(1, pivots.at(i).as()); + pivots.at(i) = static_cast(mask_is_at_current_idx.select(1, pivots.at(i).as())); } } // Swap the rows so the matrix can be transformed into systematic form for(size_t mat_row = 0; mat_row < params.pk_no_rows(); ++mat_row) { for(size_t col = 0; col < Classic_McEliece_Parameters::mu(); ++col) { - swap_bits(matrix_swap_area.at(mat_row), col, pivot_indices.at(col)); + swap_1_bit(matrix_swap_area.at(mat_row), col, pivot_indices.at(col)); } } @@ -185,7 +188,7 @@ for(size_t diag_pos = 0; diag_pos < params.pk_no_rows(); ++diag_pos) { if(params.is_f() && diag_pos == params.pk_no_rows() - params.mu()) { auto ret_pivots = move_columns(mat, params); - bool move_columns_failed = !ret_pivots.has_value(); + const bool move_columns_failed = !ret_pivots.has_value(); CT::unpoison(move_columns_failed); if(move_columns_failed) { return std::nullopt; @@ -204,14 +207,14 @@ // If the current bit on the diagonal is not set at this point // the matrix is not systematic. We abort the computation in this case. - bool diag_bit_zero = !mat[diag_pos].at(diag_pos); + const bool diag_bit_zero = !mat[diag_pos].at(diag_pos); CT::unpoison(diag_bit_zero); if(diag_bit_zero) { return std::nullopt; } // Now the new row is added to all other rows, where the - // bit in the column of the current postion on the diagonal + // bit in the column of the current position on the diagonal // is still one for(size_t row = 0; row < params.pk_no_rows(); ++row) { if(row != diag_pos) { @@ -263,7 +266,7 @@ const Classic_McEliece_Minimal_Polynomial& g) { auto pk_matrix_and_pivots = create_matrix(params, field_ordering, g); - bool matrix_creation_failed = !pk_matrix_and_pivots.has_value(); + const bool matrix_creation_failed = !pk_matrix_and_pivots.has_value(); CT::unpoison(matrix_creation_failed); if(matrix_creation_failed) { return std::nullopt; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_matrix.h botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_matrix.h --- botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_matrix.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_matrix.h 2026-05-07 01:38:28.000000000 +0000 @@ -23,7 +23,7 @@ * * Only the bytes of the submatrix T are stored. */ -class BOTAN_TEST_API Classic_McEliece_Matrix { +class BOTAN_TEST_API Classic_McEliece_Matrix final { public: /** * @brief Create the matrix H for a Classic McEliece instance given its @@ -90,7 +90,7 @@ // Check padding of mat_bytes rows BOTAN_ASSERT_NOMSG(m_mat_bytes.size() == params.pk_no_rows() * params.pk_row_size_bytes()); for(size_t row = 0; row < params.pk_no_rows(); ++row) { - uint8_t padded_byte = m_mat_bytes[(row + 1) * params.pk_row_size_bytes() - 1]; + const uint8_t padded_byte = m_mat_bytes[(row + 1) * params.pk_row_size_bytes() - 1]; CT::unpoison(padded_byte); BOTAN_ARG_CHECK(padded_byte >> (params.pk_no_cols() % 8) == 0, "Valid padding of unused bytes"); } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_parameter_set.cpp botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_parameter_set.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_parameter_set.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_parameter_set.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -14,7 +14,7 @@ namespace Botan { Classic_McEliece_Parameter_Set Classic_McEliece_Parameter_Set::from_string(std::string_view nm) { - Code code = [&] { + const Code code = [&] { if(nm == "ClassicMcEliece_348864" || nm == "348864") { return ClassicMcEliece_348864; } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_parameter_set.h botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_parameter_set.h --- botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_parameter_set.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_parameter_set.h 2026-05-07 01:38:28.000000000 +0000 @@ -23,9 +23,9 @@ * Instance with 'pc' use plaintext confirmation as defined in the ISO Draft. * Instance with 'f' use matrix reduction with the semi-systematic form. */ -class BOTAN_PUBLIC_API(3, 4) Classic_McEliece_Parameter_Set { +class BOTAN_PUBLIC_API(3, 4) Classic_McEliece_Parameter_Set final { public: - enum class Code { + enum class Code : uint8_t { ClassicMcEliece_348864, // NIST ClassicMcEliece_348864f, // NIST @@ -50,6 +50,7 @@ using enum Code; + // NOLINTNEXTLINE(*-explicit-conversions) Classic_McEliece_Parameter_Set(Code code) : m_code(code) {} /** diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_parameters.cpp botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_parameters.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_parameters.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_parameters.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -40,7 +40,7 @@ } Classic_McEliece_Polynomial_Ring determine_poly_ring(Classic_McEliece_Parameter_Set param_set) { - CmceGfMod poly_f = determine_poly_f(param_set); + const CmceGfMod poly_f = determine_poly_f(param_set); switch(param_set.code()) { case Classic_McEliece_Parameter_Set::ClassicMcEliece_348864: diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_parameters.h botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_parameters.h --- botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_parameters.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_parameters.h 2026-05-07 01:38:28.000000000 +0000 @@ -20,9 +20,6 @@ namespace Botan { -struct Classic_McEliece_Big_F_Coefficient; -class Classic_McEliece_Polynomial_Ring; - /** * Container for all Classic McEliece parameters. */ @@ -135,7 +132,7 @@ size_t tau() const { // Section 8.4 of ISO: // The integer tau is defined as t if n=q; as 2t if q/2<=n + +#include +#include #include -#include +#include namespace Botan { @@ -19,6 +22,8 @@ BOTAN_DEBUG_ASSERT(a.modulus() == coef_at(0).modulus()); Classic_McEliece_GF r(CmceGfElem(0), a.modulus()); + // TODO(Botan4) use std::ranges::reverse_view here once available (need newer Clang) + // NOLINTNEXTLINE(modernize-loop-convert) for(auto it = m_coef.rbegin(); it != m_coef.rend(); ++it) { r *= a; r += *it; @@ -38,7 +43,7 @@ } for(size_t i = (m_t - 1) * 2; i >= m_t; --i) { - for(auto& [idx, coef] : m_position_map) { + for(const auto& [idx, coef] : m_position_map) { prod.at(i - m_t + idx) += coef * prod.at(i); } } @@ -126,12 +131,12 @@ secure_vector Classic_McEliece_Minimal_Polynomial::serialize() const { BOTAN_ASSERT_NOMSG(!coef().empty()); - auto& all_coeffs = coef(); + const auto& all_coeffs = coef(); // Store all except coef for monomial x^t since polynomial is monic (ISO Spec Section 9.2.9) auto coeffs_to_store = std::span(all_coeffs).first(all_coeffs.size() - 1); secure_vector bytes(sizeof(uint16_t) * coeffs_to_store.size()); BufferStuffer bytes_stuf(bytes); - for(auto& coef : coeffs_to_store) { + for(const auto& coef : coeffs_to_store) { store_le(bytes_stuf.next(), coef.elem().get()); } BOTAN_ASSERT_NOMSG(bytes_stuf.full()); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_poly.h botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_poly.h --- botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_poly.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_poly.h 2026-05-07 01:38:28.000000000 +0000 @@ -36,7 +36,7 @@ * * @param coef The coefficients of the polynomial. The first element is the coefficient of the lowest monomial. */ - Classic_McEliece_Polynomial(std::vector coef) : m_coef(std::move(coef)) {} + explicit Classic_McEliece_Polynomial(std::vector coef) : m_coef(std::move(coef)) {} /** * @brief Evaluate the polynomial P(x) at a given point a, i.e., compute P(a). @@ -78,9 +78,9 @@ * * It represents the monic irreducible degree-t polynomial of the goppa code. */ -class BOTAN_TEST_API Classic_McEliece_Minimal_Polynomial : public Classic_McEliece_Polynomial { +class BOTAN_TEST_API Classic_McEliece_Minimal_Polynomial final : public Classic_McEliece_Polynomial { public: - Classic_McEliece_Minimal_Polynomial(std::vector coef) : + explicit Classic_McEliece_Minimal_Polynomial(std::vector coef) : Classic_McEliece_Polynomial(std::move(coef)) {} /** @@ -101,7 +101,7 @@ * This class contains a modulus polynomial F(y) and the GF(q) modulus f(z). It is used * to create and operate with Classic_McEliece_Polynomials. */ -class BOTAN_TEST_API Classic_McEliece_Polynomial_Ring { +class BOTAN_TEST_API Classic_McEliece_Polynomial_Ring final { public: /** * @brief Represents a non-zero coefficient of the modulus F(y) (which is in GF(q)[y]). diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_types.h botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_types.h --- botan3-3.7.1+dfsg/src/lib/pubkey/classic_mceliece/cmce_types.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/classic_mceliece/cmce_types.h 2026-05-07 01:38:28.000000000 +0000 @@ -21,7 +21,7 @@ /// Represents a GF(q) modulus using CmceGfMod = Strong; -/// Represents an element of a permuation (pi in spec). Used in field ordering creation. +/// Represents an element of a permutation (pi in spec). Used in field ordering creation. using CmcePermutationElement = Strong; /// Represents a permutation (pi in spec). Used in field ordering creation. diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/curve448/curve448_gf.cpp botan3-3.12.0+dfsg/src/lib/pubkey/curve448/curve448_gf.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/curve448/curve448_gf.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/curve448/curve448_gf.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,51 +12,11 @@ #include #include -#include - namespace Botan { namespace { /** - * @brief Compute (a + b). The carry is returned in the carry parameter. - * The carry is not included for the addition. - */ -inline uint64_t u64_add(uint64_t a, uint64_t b, bool* carry) { - // Let the compiler optimize this into fancy instructions - const uint64_t sum = a + b; - *carry = sum < a; - return sum; -} - -/** - * @brief Compute (a + b + carry), where carry is in {0, 1}. The carry of this computation - * is store in the in/out @p carry parameter. - */ -inline uint64_t u64_add_with_carry(uint64_t a, uint64_t b, bool* carry) { - // Let the compiler optimize this into fancy instructions - uint64_t sum = a + b; - const bool carry_a_plus_b = (sum < a); - sum += static_cast(*carry); - *carry = static_cast(carry_a_plus_b) | static_cast(sum < static_cast(*carry)); - return sum; -} - -/** - * @brief Compute (a - (b + borrow)). The borrow is returned in the carry parameter. - * - * I.e. borrow = 1 if a < b + borrow, else 0. - */ -inline uint64_t u64_sub_with_borrow(uint64_t a, uint64_t b, bool* borrow) { - // Let the compiler optimize this into fancy instructions - const uint64_t diff = a - b; - const bool borrow_a_min_b = diff > a; - const uint64_t z = diff - static_cast(*borrow); - *borrow = static_cast(borrow_a_min_b) | static_cast(z > diff); - return z; -} - -/** * @brief Reduce the result of a addition modulo 2^448 - 2^224 - 1. * * Algorithm 1 of paper "Reduction Modulo 2^448 - 2^224 - 1", from line 27. @@ -65,28 +25,30 @@ * @param h_1 Input */ void reduce_after_add(std::span h_3, std::span h_1) { - std::array h_2; - bool carry; + std::array h_2; /* NOLINT(*-member-init) */ + uint64_t carry = 0; - // Line 27+ (of the paper's algorithm 1) - h_2[0] = u64_add(h_1[0], h_1[7], &carry); + constexpr uint64_t zero = 0; - h_2[1] = u64_add(h_1[1], carry, &carry); - h_2[2] = u64_add(h_1[2], carry, &carry); + // Line 27+ (of the paper's algorithm 1) + h_2[0] = word_add(h_1[0], h_1[7], &carry); + h_2[1] = word_add(h_1[1], zero, &carry); + h_2[2] = word_add(h_1[2], zero, &carry); // Line 30 - h_2[3] = u64_add_with_carry(h_1[3], h_1[7] << 32, &carry); + h_2[3] = word_add(h_1[3], h_1[7] << 32, &carry); // Line 31+ - h_2[4] = u64_add(h_1[4], carry, &carry); - h_2[5] = u64_add(h_1[5], carry, &carry); - h_2[6] = u64_add(h_1[6], carry, &carry); + h_2[4] = word_add(h_1[4], zero, &carry); + h_2[5] = word_add(h_1[5], zero, &carry); + h_2[6] = word_add(h_1[6], zero, &carry); h_2[7] = carry; - h_3[0] = u64_add(h_2[0], h_2[7], &carry); - h_3[1] = u64_add(h_2[1], carry, &carry); - h_3[2] = u64_add(h_2[2], carry, &carry); + carry = 0; + h_3[0] = word_add(h_2[0], h_2[7], &carry); + h_3[1] = word_add(h_2[1], zero, &carry); + h_3[2] = word_add(h_2[2], zero, &carry); // Line 37 h_3[3] = h_2[3] + (h_2[7] << 32) + carry; @@ -102,62 +64,88 @@ * Algorithm 1 of paper "Reduction Modulo 2^448 - 2^224 - 1". */ void reduce_after_mul(std::span out, std::span in) { - std::array r; - std::array s; - std::array t_0; - std::array h_1; + std::array r; // NOLINT(*-member-init) + std::array s; // NOLINT(*-member-init) + std::array t_0; // NOLINT(*-member-init) + std::array h_1; // NOLINT(*-member-init) - bool carry; + uint64_t carry = 0; // Line 4 (of the paper's algorithm 1) - r[0] = u64_add(in[0], in[7], &carry); + r[0] = word_add(in[0], in[7], &carry); // Line 5-7 - for(size_t i = 1; i < 7; ++i) { - r[i] = u64_add_with_carry(in[i], in[i + 7], &carry); - } + r[1] = word_add(in[1], in[1 + 7], &carry); + r[2] = word_add(in[2], in[2 + 7], &carry); + r[3] = word_add(in[3], in[3 + 7], &carry); + r[4] = word_add(in[4], in[4 + 7], &carry); + r[5] = word_add(in[5], in[5 + 7], &carry); + r[6] = word_add(in[6], in[6 + 7], &carry); r[7] = carry; s[0] = r[0]; s[1] = r[1]; s[2] = r[2]; // Line 10 - s[3] = u64_add(r[3], in[10] & 0xFFFFFFFF00000000, &carry); + carry = 0; + s[3] = word_add(r[3], in[10] & 0xFFFFFFFF00000000, &carry); // Line 11-13 - for(size_t i = 4; i < 7; ++i) { - s[i] = u64_add_with_carry(r[i], in[i + 7], &carry); - } + s[4] = word_add(r[4], in[4 + 7], &carry); + s[5] = word_add(r[5], in[5 + 7], &carry); + s[6] = word_add(r[6], in[6 + 7], &carry); s[7] = r[7] + carry; // Line 15-17 - for(size_t i = 0; i < 3; ++i) { - t_0[i] = (in[i + 11] << 32) | (in[i + 10] >> 32); - } + t_0[0] = (in[0 + 11] << 32) | (in[0 + 10] >> 32); + t_0[1] = (in[1 + 11] << 32) | (in[1 + 10] >> 32); + t_0[2] = (in[2 + 11] << 32) | (in[2 + 10] >> 32); // Line 18 t_0[3] = (in[7] << 32) | (in[13] >> 32); // Line 19-21 - for(size_t i = 4; i < 7; ++i) { - t_0[i] = (in[i + 4] << 32) | (in[i + 3] >> 32); - } - h_1[0] = u64_add(s[0], t_0[0], &carry); + t_0[4] = (in[4 + 4] << 32) | (in[4 + 3] >> 32); + t_0[5] = (in[5 + 4] << 32) | (in[5 + 3] >> 32); + t_0[6] = (in[6 + 4] << 32) | (in[6 + 3] >> 32); + carry = 0; // Line 23-25 - for(size_t i = 1; i < 7; ++i) { - h_1[i] = u64_add_with_carry(s[i], t_0[i], &carry); - } + h_1[0] = word_add(s[0], t_0[0], &carry); + h_1[1] = word_add(s[1], t_0[1], &carry); + h_1[2] = word_add(s[2], t_0[2], &carry); + h_1[3] = word_add(s[3], t_0[3], &carry); + h_1[4] = word_add(s[4], t_0[4], &carry); + h_1[5] = word_add(s[5], t_0[5], &carry); + h_1[6] = word_add(s[6], t_0[6], &carry); h_1[7] = s[7] + carry; reduce_after_add(out, h_1); } +// Multiply by the Curve448 constant a24 = (a-2)/4 = 39081. +// Uses a 7-word × 1-word multiply (7 muls vs 49 for full comba_mul<7>), +// and the result fits in 8 words so only needs reduce_after_add. +void gf_mul_a24(std::span out, std::span a) { + constexpr uint64_t A24 = 39081; + std::array ws; // NOLINT(*-member-init) + uint64_t carry = 0; + ws[0] = word_madd2(a[0], A24, &carry); + ws[1] = word_madd2(a[1], A24, &carry); + ws[2] = word_madd2(a[2], A24, &carry); + ws[3] = word_madd2(a[3], A24, &carry); + ws[4] = word_madd2(a[4], A24, &carry); + ws[5] = word_madd2(a[5], A24, &carry); + ws[6] = word_madd2(a[6], A24, &carry); + ws[7] = carry; + reduce_after_add(out, ws); +} + void gf_mul(std::span out, std::span a, std::span b) { - std::array ws; + std::array ws; // NOLINT(*-member-init) comba_mul<7>(ws.data(), a.data(), b.data()); reduce_after_mul(out, ws); } void gf_square(std::span out, std::span a) { - std::array ws; + std::array ws; // NOLINT(*-member-init) comba_sqr<7>(ws.data(), a.data()); reduce_after_mul(out, ws); } @@ -165,15 +153,17 @@ void gf_add(std::span out, std::span a, std::span b) { - std::array ws; - copy_mem(std::span(ws).first(), a); - ws[WORDS_448] = 0; + std::array ws; // NOLINT(*-member-init) - bool carry = false; - for(size_t i = 0; i < WORDS_448; ++i) { - ws[i] = u64_add_with_carry(a[i], b[i], &carry); - } - ws[WORDS_448] = carry; + uint64_t carry = 0; + ws[0] = word_add(a[0], b[0], &carry); + ws[1] = word_add(a[1], b[1], &carry); + ws[2] = word_add(a[2], b[2], &carry); + ws[3] = word_add(a[3], b[3], &carry); + ws[4] = word_add(a[4], b[4], &carry); + ws[5] = word_add(a[5], b[5], &carry); + ws[6] = word_add(a[6], b[6], &carry); + ws[7] = carry; reduce_after_add(out, ws); } @@ -186,53 +176,154 @@ void gf_sub(std::span out, std::span a, std::span b) { - std::array h_0; - std::array h_1; + std::array h_0; // NOLINT(*-member-init) + std::array h_1; // NOLINT(*-member-init) - bool borrow = false; - for(size_t i = 0; i < WORDS_448; ++i) { - h_0[i] = u64_sub_with_borrow(a[i], b[i], &borrow); - } + uint64_t borrow = 0; + h_0[0] = word_sub(a[0], b[0], &borrow); + h_0[1] = word_sub(a[1], b[1], &borrow); + h_0[2] = word_sub(a[2], b[2], &borrow); + h_0[3] = word_sub(a[3], b[3], &borrow); + h_0[4] = word_sub(a[4], b[4], &borrow); + h_0[5] = word_sub(a[5], b[5], &borrow); + h_0[6] = word_sub(a[6], b[6], &borrow); uint64_t delta = borrow; uint64_t delta_p = delta << 32; - borrow = false; + borrow = 0; - h_1[0] = u64_sub_with_borrow(h_0[0], delta, &borrow); - h_1[1] = u64_sub_with_borrow(h_0[1], 0, &borrow); - h_1[2] = u64_sub_with_borrow(h_0[2], 0, &borrow); - h_1[3] = u64_sub_with_borrow(h_0[3], delta_p, &borrow); - h_1[4] = u64_sub_with_borrow(h_0[4], 0, &borrow); - h_1[5] = u64_sub_with_borrow(h_0[5], 0, &borrow); - h_1[6] = u64_sub_with_borrow(h_0[6], 0, &borrow); + constexpr uint64_t zero = 0; + + h_1[0] = word_sub(h_0[0], delta, &borrow); + h_1[1] = word_sub(h_0[1], zero, &borrow); + h_1[2] = word_sub(h_0[2], zero, &borrow); + h_1[3] = word_sub(h_0[3], delta_p, &borrow); + h_1[4] = word_sub(h_0[4], zero, &borrow); + h_1[5] = word_sub(h_0[5], zero, &borrow); + h_1[6] = word_sub(h_0[6], zero, &borrow); delta = borrow; delta_p = delta << 32; - borrow = false; + borrow = 0; - out[0] = u64_sub_with_borrow(h_1[0], delta, &borrow); - out[1] = u64_sub_with_borrow(h_1[1], 0, &borrow); - out[2] = u64_sub_with_borrow(h_1[2], 0, &borrow); - out[3] = u64_sub_with_borrow(h_1[3], delta_p, &borrow); + out[0] = word_sub(h_1[0], delta, &borrow); + out[1] = word_sub(h_1[1], zero, &borrow); + out[2] = word_sub(h_1[2], zero, &borrow); + out[3] = word_sub(h_1[3], delta_p, &borrow); out[4] = h_1[4]; out[5] = h_1[5]; out[6] = h_1[6]; } +/// Square a field element n times +void gf_sqr_n(std::span out, std::span a, size_t n) { + gf_square(out, a); + for(size_t i = 1; i < n; ++i) { + gf_square(out, out); + } +} + +/** + * @brief Compute x^(2^222 - 1) using an addition chain. + * + * This is the shared prefix of the addition chains for both + * inversion (x^(p-2)) and square root (x^((p-3)/4)). + * + * Addition chain from addchain tool (cost 446): + * _11 = 1 + _10 + * _111 = 1 + _110 + * _111111 = _111 + _111 << 3 + * x12 = _111111 << 6 + _111111 + * x24 = x12 << 12 + x12 + * x30 = _111111 + x24 << 6 + * x48 = x24 << 6 << 18 + x24 + * x96 = x48 << 48 + x48 + * x192 = x96 << 96 + x96 + * x222 = x192 << 30 + x30 + */ +void gf_pow_2_222m1(std::span x222, + std::span x223, + std::span a) { + std::array t; // NOLINT(*-member-init) + + // _10 = a^2 + std::array a2; // NOLINT(*-member-init) + gf_square(a2, a); + + // _11 = a^3 + std::array a3; // NOLINT(*-member-init) + gf_mul(a3, a, a2); + + // _111 = a^7 + std::array a7; // NOLINT(*-member-init) + gf_square(t, a3); + gf_mul(a7, a, t); + + // _111111 = a^63 + std::array a63; // NOLINT(*-member-init) + gf_sqr_n(t, a7, 3); + gf_mul(a63, a7, t); + + // x12 = a^(2^12 - 1) + std::array x12; // NOLINT(*-member-init) + gf_sqr_n(t, a63, 6); + gf_mul(x12, a63, t); + + // x24 = a^(2^24 - 1) + std::array x24; // NOLINT(*-member-init) + gf_sqr_n(t, x12, 12); + gf_mul(x24, x12, t); + + // i34 = x24 << 6 = a^((2^24 - 1) * 2^6) + std::array i34; // NOLINT(*-member-init) + gf_sqr_n(i34, x24, 6); + + // x30 = a^(2^30 - 1) + std::array x30; // NOLINT(*-member-init) + gf_mul(x30, a63, i34); + + // x48 = a^(2^48 - 1) + std::array x48; // NOLINT(*-member-init) + gf_sqr_n(t, i34, 18); + gf_mul(x48, x24, t); + + // x96 = a^(2^96 - 1) + std::array x96; // NOLINT(*-member-init) + gf_sqr_n(t, x48, 48); + gf_mul(x96, x48, t); + + // x192 = a^(2^192 - 1) + std::array x192; // NOLINT(*-member-init) + gf_sqr_n(t, x96, 96); + gf_mul(x192, x96, t); + + // x222 = a^(2^222 - 1) + gf_sqr_n(t, x192, 30); + gf_mul(x222, x30, t); + + // x223 = a^(2^223 - 1) + gf_square(t, x222); + gf_mul(x223, a, t); +} + /** * @brief Inversion in GF(P) using Fermat's little theorem: * x^-1 = x^(P-2) mod P + * + * Uses an optimized addition chain (cost 460) found by addchain. + * P-2 = 2^448 - 2^224 - 3 + * return = (x223 << 223 + x222) << 2 + 1 */ void gf_inv(std::span out, std::span a) { - clear_mem(out); - out[0] = 1; - // Square and multiply - for(int16_t t = 448; t >= 0; --t) { - gf_square(out, out); - // (P-2) has zero bits at indices 1, 224, 448. All others are one. - if(t != 448 && t != 224 && t != 1) { - gf_mul(out, out, a); - } - } + std::array x222; // NOLINT(*-member-init) + std::array x223; // NOLINT(*-member-init) + gf_pow_2_222m1(x222, x223, a); + + // (x223 << 223 + x222) << 2 + 1 + std::array t; // NOLINT(*-member-init) + gf_sqr_n(t, x223, 223); + gf_mul(t, t, x222); + gf_sqr_n(t, t, 2); + gf_mul(out, t, a); } /** @@ -250,23 +341,23 @@ 0xffffffffffffffff, 0xffffffffffffffff}; - std::array in_minus_p; - bool borrow = false; + std::array in_minus_p; // NOLINT(*-member-init) + uint64_t borrow = 0; for(size_t i = 0; i < WORDS_448; ++i) { - in_minus_p[i] = u64_sub_with_borrow(in[i], p[i], &borrow); + in_minus_p[i] = word_sub(in[i], p[i], &borrow); } - std::array out; + std::array out; // NOLINT(*-member-init) CT::Mask::expand(borrow).select_n(out.data(), in.data(), in_minus_p.data(), WORDS_448); return out; } } // namespace -Gf448Elem::Gf448Elem(std::span x) { +Gf448Elem::Gf448Elem(std::span x) /* NOLINT(*-member-init) */ { load_le(m_x, x); } -Gf448Elem::Gf448Elem(uint64_t least_sig_word) { +Gf448Elem::Gf448Elem(uint64_t least_sig_word) /* NOLINT(*-member-init) */ { clear_mem(m_x); m_x[0] = least_sig_word; } @@ -276,19 +367,19 @@ } std::array Gf448Elem::to_bytes() const { - std::array bytes; + std::array bytes{}; to_bytes(bytes); return bytes; } -void Gf448Elem::ct_cond_swap(bool b, Gf448Elem& other) { +void Gf448Elem::ct_cond_swap(CT::Mask mask, Gf448Elem& other) { for(size_t i = 0; i < WORDS_448; ++i) { - CT::conditional_swap(b, m_x[i], other.m_x[i]); + mask.conditional_swap(m_x[i], other.m_x[i]); } } -void Gf448Elem::ct_cond_assign(bool b, const Gf448Elem& other) { - CT::conditional_assign_mem(static_cast(b), m_x.data(), other.m_x.data(), WORDS_448); +void Gf448Elem::ct_cond_assign(CT::Mask mask, const Gf448Elem& other) { + mask.select_n(m_x.data(), other.m_x.data(), m_x.data(), WORDS_448); } Gf448Elem Gf448Elem::operator+(const Gf448Elem& other) const { @@ -345,6 +436,12 @@ return CT::is_equal(x_words.data(), x_words_canonical.data(), WORDS_448).as_bool(); } +Gf448Elem mul_a24(const Gf448Elem& a) { + Gf448Elem res(0); + gf_mul_a24(res.words(), a.words()); + return res; +} + Gf448Elem square(const Gf448Elem& elem) { Gf448Elem res(0); gf_square(res.words(), elem.words()); @@ -352,16 +449,16 @@ } Gf448Elem root(const Gf448Elem& elem) { - Gf448Elem res(1); - - // (P-3)/4 is an 445 bit integer with one zero bits at 222. All others are one. - for(int16_t t = 445; t >= 0; --t) { - gf_square(res.words(), res.words()); - if(t != 222) { - gf_mul(res.words(), res.words(), elem.words()); - } - } + // Compute elem^((P-3)/4) using an optimized addition chain (cost 457). + // (P-3)/4 = 2^446 - 2^222 - 1 + // return = x223 << 223 + x222 + std::array x222; // NOLINT(*-member-init) + std::array x223; // NOLINT(*-member-init) + gf_pow_2_222m1(x222, x223, elem.words()); + Gf448Elem res(0); + gf_sqr_n(res.words(), x223, 223); + gf_mul(res.words(), res.words(), x222); return res; } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/curve448/curve448_gf.h botan3-3.12.0+dfsg/src/lib/pubkey/curve448/curve448_gf.h --- botan3-3.7.1+dfsg/src/lib/pubkey/curve448/curve448_gf.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/curve448/curve448_gf.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,13 +11,14 @@ #include #include #include +#include #include #include namespace Botan { -constexpr size_t BYTES_448 = ceil_tobytes(448); +constexpr size_t BYTES_448 = ceil_tobytes(448); /* uint64_t words to store a 448 bit value */ constexpr size_t WORDS_448 = 7; @@ -38,19 +39,29 @@ * @brief Construct a GF element from a 448-bit integer gives as 56 bytes @p x in * little-endian order. */ - Gf448Elem(std::span x); + explicit Gf448Elem(std::span x); /** * @brief Construct a GF element from a 448-bit integer gives as 7 uint64_t words @p x in * little-endian order. */ - Gf448Elem(std::span data) { copy_mem(m_x, data); } + explicit Gf448Elem(std::span data) /* NOLINT(*-member-init) */ { copy_mem(m_x, data); } /** * @brief Construct a GF element by passing the least significant 64 bits as a word. * All other become zero. */ - Gf448Elem(uint64_t least_sig_word); + explicit Gf448Elem(uint64_t least_sig_word); + + /** + * Return the constant value zero + */ + static Gf448Elem zero() { return Gf448Elem(0); } + + /** + * Return the constant value one + */ + static Gf448Elem one() { return Gf448Elem(1); } /** * @brief Store the canonical representation of the GF element as 56 bytes in little-endian @@ -67,14 +78,14 @@ std::array to_bytes() const; /** - * @brief Swap this and other if b == true. Constant time for any b. + * @brief Swap this and @p other if @p mask is set. Constant time. */ - void ct_cond_swap(bool b, Gf448Elem& other); + void ct_cond_swap(CT::Mask mask, Gf448Elem& other); /** - * @brief Set this to @p other if b is true. Constant time for any b. + * @brief Set this to @p other if @p mask is true. Constant time. */ - void ct_cond_assign(bool b, const Gf448Elem& other); + void ct_cond_assign(CT::Mask mask, const Gf448Elem& other); Gf448Elem operator+(const Gf448Elem& other) const; @@ -127,6 +138,11 @@ }; /** + * @brief Multiply a field element by the Curve448 constant a24 = 39081. + */ +Gf448Elem mul_a24(const Gf448Elem& a); + +/** * @brief Computes elem^2. Faster than operator*. */ Gf448Elem square(const Gf448Elem& elem); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/curve448/curve448_scalar.cpp botan3-3.12.0+dfsg/src/lib/pubkey/curve448/curve448_scalar.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/curve448/curve448_scalar.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/curve448/curve448_scalar.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -24,12 +24,12 @@ copy_mem(std::span(r).template first(), x); return std::make_pair(std::array({0}), r); } else { - std::array r; + std::array r; // NOLINT(*-member-init) copy_mem(r, std::span(x).template first()); // Clear the two most significant bits r[Scalar448::WORDS - 1] &= ~(word(0b11) << (sizeof(word) * 8 - 2)); - std::array q; + std::array q; // NOLINT(*-member-init) bigint_shr2(q.data(), x.data(), x.size(), 446); return std::make_pair(q, r); @@ -56,8 +56,8 @@ /// @return c*x, with c = 0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d template std::array mul_c(std::span x) { - std::array res; - std::array ws; + std::array res; // NOLINT(*-member-init) + std::array ws; // NOLINT(*-member-init) constexpr std::array c = c_words(); bigint_mul(res.data(), res.size(), x.data(), x.size(), x.size(), c.data(), c.size(), c.size(), ws.data(), ws.size()); @@ -69,9 +69,9 @@ */ std::array add(std::span x, std::span y) { - std::array res; + std::array res; // NOLINT(*-member-init) copy_mem(res, x); - const word carry = bigint_add2_nc(res.data(), res.size(), y.data(), y.size()); + const word carry = bigint_add2(res.data(), res.size(), y.data(), y.size()); CT::unpoison(carry); BOTAN_ASSERT(carry == 0, "Result fits in output"); return res; @@ -80,10 +80,10 @@ /** * @brief x = (x >= L) ? x - L : x. Constant time. * - * @return true iff a reduction was performed + * @return a CT::Choice that is set iff a reduction was performed */ -bool ct_subtract_L_if_bigger(std::span x) { - std::array tmp; +CT::Choice ct_subtract_L_if_bigger(std::span x) { + std::array tmp; // NOLINT(*-member-init) copy_mem(tmp, x); constexpr auto big_l = big_l_words(); @@ -91,13 +91,13 @@ const auto smaller_than_L = CT::Mask::expand(borrow); smaller_than_L.select_n(x.data(), x.data(), tmp.data(), Scalar448::WORDS); - return !smaller_than_L.as_bool(); + return !smaller_than_L.as_choice(); } template std::array bytes_to_words(std::span x) { constexpr size_t words = words_for_bits(S * 8); - std::array x_word_bytes = {0}; + std::array x_word_bytes{}; copy_mem(std::span(x_word_bytes).template first(), x); return load_le>(x_word_bytes); } @@ -144,6 +144,7 @@ } // namespace +// NOLINTNEXTLINE(*-member-init) Scalar448::Scalar448(std::span in_bytes) { BOTAN_ARG_CHECK(in_bytes.size() <= 114, "Input must be at most 114 bytes long"); std::array max_bytes = {0}; @@ -156,7 +157,32 @@ bool Scalar448::get_bit(size_t bit_pos) const { BOTAN_ARG_CHECK(bit_pos < 446, "Bit position out of range"); constexpr size_t word_sz = sizeof(word) * 8; - return (m_scalar_words[bit_pos / word_sz] >> (bit_pos % word_sz)) & 1; + return (((m_scalar_words[bit_pos / word_sz] >> (bit_pos % word_sz)) & 1) == 1); +} + +uint32_t Scalar448::get_window(size_t starting_pos, size_t width) const { + BOTAN_ARG_CHECK(width <= 32, "Window too wide"); + constexpr size_t word_sz = sizeof(word) * 8; + + // Bits at or beyond position 446 are zero + if(starting_pos >= 446) { + return 0; + } + + // Clamp the effective width so we don't read past bit 445 + const size_t effective_bits = std::min(width, size_t(446) - starting_pos); + + const size_t word_idx = starting_pos / word_sz; + const size_t bit_idx = starting_pos % word_sz; + + const uint64_t mask = (effective_bits >= 64) ? ~uint64_t(0) : (uint64_t(1) << effective_bits) - 1; + + uint64_t val = m_scalar_words[word_idx] >> bit_idx; + if(bit_idx + effective_bits > word_sz && word_idx + 1 < WORDS) { + val |= m_scalar_words[word_idx + 1] << (word_sz - bit_idx); + } + + return static_cast(val & mask); } Scalar448 Scalar448::operator+(const Scalar448& other) const { @@ -188,7 +214,7 @@ const auto leading_zeros = x.subspan(BYTES); const auto leading_zeros_are_zero = CT::all_zeros(leading_zeros.data(), leading_zeros.size()); auto x_sig_words = bytes_to_words(x.first<56>()); - const auto least_56_bytes_smaller_L = CT::Mask::expand(!ct_subtract_L_if_bigger(x_sig_words)); + const auto least_56_bytes_smaller_L = CT::Mask::from_choice(!ct_subtract_L_if_bigger(x_sig_words)); return (leading_zeros_are_zero & least_56_bytes_smaller_L).as_bool(); } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/curve448/curve448_scalar.h botan3-3.12.0+dfsg/src/lib/pubkey/curve448/curve448_scalar.h --- botan3-3.7.1+dfsg/src/lib/pubkey/curve448/curve448_scalar.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/curve448/curve448_scalar.h 2026-05-07 01:38:28.000000000 +0000 @@ -34,10 +34,10 @@ class BOTAN_TEST_API Scalar448 final { public: constexpr static size_t WORDS = words_for_bits(446); - constexpr static size_t BYTES = ceil_tobytes(446); + constexpr static size_t BYTES = ceil_tobytes(446); /// @brief Construct a new scalar from (max. 114) bytes. Little endian. - Scalar448(std::span x); + explicit Scalar448(std::span x); /// @brief Convert the scalar to bytes in little endian. template @@ -52,6 +52,10 @@ /// @brief Access the i-th bit of the scalar. From 0 (lsb) to 445 (msb). bool get_bit(size_t i) const; + /// @brief Extract a window of @p width bits starting at bit position @p starting_pos. + /// Bits beyond position 445 are treated as zero. + uint32_t get_window(size_t starting_pos, size_t width) const; + /// @brief scalar = (scalar + other) mod L Scalar448 operator+(const Scalar448& other) const; @@ -62,7 +66,8 @@ static bool bytes_are_reduced(std::span x); private: - Scalar448(std::span scalar_words) { copy_mem(m_scalar_words, scalar_words); } + // NOLINTNEXTLINE(*-member-init) + explicit Scalar448(std::span scalar_words) { copy_mem(m_scalar_words, scalar_words); } std::array m_scalar_words; }; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/curve448/ed448/ed448.cpp botan3-3.12.0+dfsg/src/lib/pubkey/curve448/ed448/ed448.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/curve448/ed448/ed448.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/curve448/ed448/ed448.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -57,7 +57,7 @@ Ed448_PrivateKey::Ed448_PrivateKey(const AlgorithmIdentifier& /*unused*/, std::span key_bits) { secure_vector bits; - BER_Decoder(key_bits).decode(bits, ASN1_Type::OctetString).verify_end(); + BER_Decoder(key_bits, BER_Decoder::Limits::DER()).decode(bits, ASN1_Type::OctetString).verify_end(); if(bits.size() != ED448_LEN) { throw Decoding_Error("Invalid size for Ed448 private key"); @@ -88,7 +88,11 @@ } bool Ed448_PrivateKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const { - return true; + BOTAN_ASSERT_NOMSG(m_private.size() == ED448_LEN); + auto scope = CT::scoped_poison(m_private); + const auto public_point = create_pk_from_sk(std::span(m_private).first()); + CT::unpoison(public_point); + return public_point == m_public; } namespace { @@ -113,7 +117,7 @@ std::vector get_and_clear() override { return m_hash->final_stdvec(); } - Prehashed_Ed448_Message(std::string_view hash) : m_hash(HashFunction::create_or_throw(hash)) {} + explicit Prehashed_Ed448_Message(std::string_view hash) : m_hash(HashFunction::create_or_throw(hash)) {} private: std::unique_ptr m_hash; @@ -136,9 +140,7 @@ public: explicit Ed448_Verify_Operation(const Ed448_PublicKey& key, std::optional prehash_function = std::nullopt) : - m_prehash_function(std::move(prehash_function)) { - const auto pk_bits = key.public_key_bits(); - copy_mem(m_pk, std::span(pk_bits).first()); + m_pk(key.raw_public_key_bits()), m_prehash_function(std::move(prehash_function)) { if(m_prehash_function) { m_message = std::make_unique(*m_prehash_function); } else { @@ -151,7 +153,7 @@ bool is_valid_signature(std::span sig) override { const auto msg = m_message->get_and_clear(); try { - return verify_signature(m_pk, m_prehash_function.has_value(), {}, sig, msg); + return verify_signature(std::span(m_pk).first(), m_prehash_function.has_value(), {}, sig, msg); } catch(Decoding_Error&) { return false; } @@ -160,7 +162,7 @@ std::string hash_function() const override { return m_prehash_function.value_or("SHAKE-256(912)"); } private: - std::array m_pk; + std::vector m_pk; std::unique_ptr m_message; std::optional m_prehash_function; }; @@ -172,12 +174,9 @@ public: explicit Ed448_Sign_Operation(const Ed448_PrivateKey& key, std::optional prehash_function = std::nullopt) : + m_pk(key.raw_public_key_bits()), + m_sk(key.raw_private_key_bits()), m_prehash_function(std::move(prehash_function)) { - const auto pk_bits = key.public_key_bits(); - copy_mem(m_pk, std::span(pk_bits).first()); - const auto sk_bits = key.raw_private_key_bits(); - BOTAN_ASSERT_NOMSG(sk_bits.size() == ED448_LEN); - m_sk.assign(sk_bits.begin(), sk_bits.end()); if(m_prehash_function) { m_message = std::make_unique(*m_prehash_function); } else { @@ -190,8 +189,11 @@ std::vector sign(RandomNumberGenerator& /*rng*/) override { BOTAN_ASSERT_NOMSG(m_sk.size() == ED448_LEN); auto scope = CT::scoped_poison(m_sk); - const auto sig = sign_message( - std::span(m_sk).first(), m_pk, m_prehash_function.has_value(), {}, m_message->get_and_clear()); + const auto sig = sign_message(std::span(m_sk).first(), + std::span(m_pk).first(), + m_prehash_function.has_value(), + {}, + m_message->get_and_clear()); CT::unpoison(sig); return {sig.begin(), sig.end()}; } @@ -203,7 +205,7 @@ std::string hash_function() const override { return m_prehash_function.value_or("SHAKE-256(912)"); } private: - std::array m_pk; + std::vector m_pk; secure_vector m_sk; std::unique_ptr m_message; std::optional m_prehash_function; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/curve448/ed448/ed448.h botan3-3.12.0+dfsg/src/lib/pubkey/curve448/ed448/ed448.h --- botan3-3.7.1+dfsg/src/lib/pubkey/curve448/ed448/ed448.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/curve448/ed448/ed448.h 2026-05-07 01:38:28.000000000 +0000 @@ -54,7 +54,7 @@ /** * Create a Ed448 Public Key from bytes (57 Bytes). */ - Ed448_PublicKey(std::span key_bits); + BOTAN_FUTURE_EXPLICIT Ed448_PublicKey(std::span key_bits); std::unique_ptr create_verification_op(std::string_view params, std::string_view provider) const override; @@ -64,7 +64,7 @@ protected: Ed448_PublicKey() = default; - std::array m_public; + std::array m_public{}; // NOLINT(*non-private-member-variable*) }; BOTAN_DIAGNOSTIC_PUSH @@ -95,7 +95,7 @@ * * @param key_bits private key bytes (57 Bytes) */ - Ed448_PrivateKey(std::span key_bits); + BOTAN_FUTURE_EXPLICIT Ed448_PrivateKey(std::span key_bits); /** * Generate a new private key. diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/curve448/ed448/ed448_internal.cpp botan3-3.12.0+dfsg/src/lib/pubkey/curve448/ed448/ed448_internal.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/curve448/ed448/ed448_internal.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/curve448/ed448/ed448_internal.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,16 +10,16 @@ #include #include +#include +#include +#include +#include #include #include -#include -#include namespace Botan { namespace { -constexpr uint64_t MINUS_D = 39081; - std::vector dom4(uint8_t x, std::span y) { // RFC 8032 2. Notation and Conventions // dom4(x, y) The octet string "SigEd448" || octet(x) || @@ -35,11 +35,11 @@ template std::array shake(bool f, std::span context, Ts... xs) { - auto shake_xof = SHAKE_256_XOF(); - shake_xof.update(dom4(static_cast(f), context)); - (shake_xof.update(std::span(xs)), ...); - std::array res; - shake_xof.output(res); + auto shake_xof = XOF::create_or_throw("SHAKE-256"); + shake_xof->update(dom4(static_cast(f), context)); + (shake_xof->update(std::span(xs)), ...); + std::array res{}; + shake_xof->output(res); return res; } @@ -56,7 +56,7 @@ // 1. Hash the 57-byte private key using SHAKE256(x, 114), storing the // digest in a 114-octet large buffer, denoted h. Only the lower 57 // bytes are used for generating the public key. - std::array raw_s; + std::array raw_s{}; shake_xof.output(raw_s); // 2. Prune the buffer: The two least significant bits of the first // octet are cleared, all eight bits the last octet are cleared, and @@ -96,9 +96,8 @@ // inversion of v and the square root: // (p+1)/4 3 (p-3)/4 // x = (u/v) = u v (u^5 v^3) (mod p) - const auto d = -Gf448Elem(MINUS_D); - const auto u = square(Gf448Elem(y)) - 1; - const auto v = d * square(Gf448Elem(y)) - 1; + const auto u = square(Gf448Elem(y)) - Gf448Elem::one(); + const auto v = -mul_a24(square(Gf448Elem(y))) - Gf448Elem::one(); const auto maybe_x = (u * square(u)) * v * root((square(square(u)) * u) * square(v) * v); // 3. If v * x^2 = u, the recovered x-coordinate is x. Otherwise, no @@ -112,10 +111,10 @@ if(maybe_x.is_zero() && x_distinguisher) { throw Decoding_Error("Square root of zero cannot be odd"); } - bool maybe_x_parity = maybe_x.is_odd(); - std::array x_data; - CT::Mask::expand(maybe_x_parity == x_distinguisher) - .select_n(x_data.data(), maybe_x.words().data(), (-maybe_x).words().data(), 7); + const bool maybe_x_parity = maybe_x.is_odd(); + std::array x_data{}; + CT::Mask::expand_bool(maybe_x_parity == x_distinguisher) + .select_n(x_data.data(), maybe_x.words().data(), (-maybe_x).words().data(), WORDS_448); return {Gf448Elem(x_data), y}; } @@ -161,7 +160,7 @@ const Gf448Elem B = square(A); const Gf448Elem C = m_x * other.m_x; const Gf448Elem D = m_y * other.m_y; - const Gf448Elem E = (-Gf448Elem(MINUS_D)) * C * D; + const Gf448Elem E = -mul_a24(C * D); const Gf448Elem F = B - E; const Gf448Elem G = B + E; const Gf448Elem H = (m_x + m_y) * (other.m_x + other.m_y); @@ -188,33 +187,179 @@ } Ed448Point Ed448Point::scalar_mul(const Scalar448& s) const { - Ed448Point res(0, 1); + // 4-bit windowed scalar multiplication. + std::array table = {Ed448Point::identity(), + *this, + Ed448Point::identity(), + Ed448Point::identity(), + Ed448Point::identity(), + Ed448Point::identity(), + Ed448Point::identity(), + Ed448Point::identity(), + Ed448Point::identity(), + Ed448Point::identity(), + Ed448Point::identity(), + Ed448Point::identity(), + Ed448Point::identity(), + Ed448Point::identity(), + Ed448Point::identity(), + Ed448Point::identity()}; + + for(size_t i = 2; i < 16; ++i) { + if(i % 2 == 0) { + table[i] = table[i / 2].double_point(); + } else { + table[i] = table[i - 1] + *this; + } + } + + // Process 448 bits (446-bit scalar + 2 leading zero bits) in 112 4-bit windows + auto res = Ed448Point::identity(); + + for(int window = 111; window >= 0; --window) { + // Double 4 times + res = res.double_point(); + res = res.double_point(); + res = res.double_point(); + res = res.double_point(); + + // Extract 4-bit window value. Bits at position >= 446 are zero. + const uint64_t w = s.get_window(static_cast(window) * 4, 4); + + // Constant-time table lookup + auto selected = Ed448Point::identity(); + for(size_t i = 0; i < 16; ++i) { + const auto correct_idx = CT::Mask::is_equal(static_cast(i), w); + selected.ct_conditional_assign(correct_idx, table[i]); + } + + res = res + selected; + } + + return res; +} + +Ed448Point Ed448Point::base_point_mul(const Scalar448& scalar) { + /* + Fixed base point multiplication + + Same idea as base point multiply used in pcurves + */ + constexpr size_t W = 4; + constexpr size_t WindowElements = (1 << W) - 1; // 15 + constexpr size_t Windows = (448 + W - 1) / W; // 112 + constexpr size_t TableSize = Windows * WindowElements; // 1680 + + static const auto table = []() { + std::vector tbl(TableSize, Ed448Point::identity()); + + auto accum = Ed448Point::base_point(); + + for(size_t i = 0; i < TableSize; i += WindowElements) { + tbl[i] = accum; + + for(size_t j = 1; j < WindowElements; ++j) { + if(j % 2 == 1) { + tbl[i + j] = tbl[i + j / 2].double_point(); + } else { + tbl[i + j] = tbl[i + j - 1] + tbl[i]; + } + } + + accum = tbl[i + (WindowElements / 2)].double_point(); + } + + return tbl; + }(); + + auto res = Ed448Point::identity(); + + for(size_t i = 0; i != Windows; ++i) { + const uint8_t w = static_cast(scalar.get_window(i * W, W)); + + // Constant-time table lookup from this window's 15-entry subtable + auto selected = Ed448Point::identity(); + for(size_t j = 0; j != WindowElements; ++j) { + const auto assign = CT::Mask::is_equal(j + 1, w); + selected.ct_conditional_assign(assign, table[i * WindowElements + j]); + } + + res = res + selected; + } + + return res; +} - // Square and multiply (double and add) in constant time. - // TODO: Optimization potential. E.g. for a = *this precompute - // 0, a, 2a, 3a, ..., 15a and ct select and add the right one for - // each 4 bit window instead of conditional add. - for(int16_t i = 445; i >= 0; --i) { +Ed448Point Ed448Point::double_scalar_mul_vartime(const Scalar448& s1, + const Ed448Point& p1, + const Scalar448& s2, + const Ed448Point& p2) { + // 2-bit 2-ary Shamir's trick (variable time) + // Process 2 bits from each scalar per iteration, using a 16-entry table. + // table[w1 | (w2 << 2)] = w1*p1 + w2*p2, for w1,w2 in 0..3. + + // Precompute small multiples of each point + const auto p1x2 = p1.double_point(); + const auto p1x3 = p1x2 + p1; + const auto p2x2 = p2.double_point(); + const auto p2x3 = p2x2 + p2; + + // Build table indexed by (w2 << 2) | w1, excluding identity at index 0 + const std::array table = { + p1, // 1*p1 + 0*p2 + p1x2, // 2*p1 + 0*p2 + p1x3, // 3*p1 + 0*p2 + p2, // 0*p1 + 1*p2 + p1 + p2, // 1*p1 + 1*p2 + p1x2 + p2, // 2*p1 + 1*p2 + p1x3 + p2, // 3*p1 + 1*p2 + p2x2, // 0*p1 + 2*p2 + p1 + p2x2, // 1*p1 + 2*p2 + p1x2 + p2x2, // 2*p1 + 2*p2 + p1x3 + p2x2, // 3*p1 + 2*p2 + p2x3, // 0*p1 + 3*p2 + p1 + p2x3, // 1*p1 + 3*p2 + p1x2 + p2x3, // 2*p1 + 3*p2 + p1x3 + p2x3, // 3*p1 + 3*p2 + }; + + auto res = Ed448Point::identity(); + + // 446 bits / 2 = 223 windows, covering bit positions 0..445 + for(int window = 222; window >= 0; --window) { res = res.double_point(); - // Conditional add if bit is set - auto add_sum = res + *this; - res.ct_conditional_assign(s.get_bit(i), add_sum); + res = res.double_point(); + + const size_t bit_pos = static_cast(window) * 2; + const size_t idx = s1.get_window(bit_pos, 2) | (s2.get_window(bit_pos, 2) << 2); + + if(idx > 0) { + res = res + table[idx - 1]; + } } + return res; } bool Ed448Point::operator==(const Ed448Point& other) const { - // Note that the operator== of of Gf448Elem is constant time - const auto mask_x = CT::Mask::expand(x() == other.x()); - const auto mask_y = CT::Mask::expand(y() == other.y()); + // Compare in projective coordinates: (X1:Y1:Z1) == (X2:Y2:Z2) + // iff X1*Z2 == X2*Z1 && Y1*Z2 == Y2*Z1 + // This avoids two field inversions that x() and y() would require. + const auto lhs_x = m_x * other.m_z; + const auto rhs_x = other.m_x * m_z; + const auto lhs_y = m_y * other.m_z; + const auto rhs_y = other.m_y * m_z; + + const auto mask_x = CT::Mask::expand_bool(lhs_x == rhs_x); + const auto mask_y = CT::Mask::expand_bool(lhs_y == rhs_y); return (mask_x & mask_y).as_bool(); } -void Ed448Point::ct_conditional_assign(bool cond, const Ed448Point& other) { - m_x.ct_cond_assign(cond, other.m_x); - m_y.ct_cond_assign(cond, other.m_y); - m_z.ct_cond_assign(cond, other.m_z); +void Ed448Point::ct_conditional_assign(CT::Mask mask, const Ed448Point& other) { + m_x.ct_cond_assign(mask, other.m_x); + m_y.ct_cond_assign(mask, other.m_y); + m_z.ct_cond_assign(mask, other.m_z); } Ed448Point operator*(const Scalar448& lhs, const Ed448Point& rhs) { @@ -224,14 +369,14 @@ std::array create_pk_from_sk(std::span sk) { // 5.2.5. Key Generation // The 57-byte public key is generated by the following steps: - auto shake_xof = SHAKE_256_XOF(); - shake_xof.update(sk); + auto shake_xof = XOF::create_or_throw("SHAKE-256"); + shake_xof->update(sk); - const Scalar448 s = scalar_from_xof(shake_xof); + const Scalar448 s = scalar_from_xof(*shake_xof); // 3. Interpret the buffer as the little-endian integer, forming a // secret scalar s. Perform a known-base-point scalar // multiplication [s]B. - return (s * Ed448Point::base_point()).encode(); + return Ed448Point::base_point_mul(s).encode(); } std::array sign_message(std::span sk, @@ -248,11 +393,11 @@ // the first half of the digest, and the corresponding public key A, // as described in the previous section. Let prefix denote the // second half of the hash digest, h[57],...,h[113]. - auto shake_xof = SHAKE_256_XOF(); - shake_xof.update(sk); - const Scalar448 s = scalar_from_xof(shake_xof); - std::array prefix; - shake_xof.output(prefix); + auto shake_xof = XOF::create_or_throw("SHAKE-256"); + shake_xof->update(sk); + const Scalar448 s = scalar_from_xof(*shake_xof); + std::array prefix{}; + shake_xof->output(prefix); // 2. Compute SHAKE256(dom4(F, C) || prefix || PH(M), 114), where M is // the message to be signed, F is 1 for Ed448ph, 0 for Ed448, and C // is the context to use. Interpret the 114-octet digest as a @@ -261,7 +406,7 @@ // 3. Compute the point [r]B. For efficiency, do this by first // reducing r modulo L, the group order of B. Let the string R be // the encoding of this point. - const auto big_r = (r * Ed448Point::base_point()).encode(); + const auto big_r = Ed448Point::base_point_mul(r).encode(); // 4. Compute SHAKE256(dom4(F, C) || R || A || PH(M), 114), and // interpret the 114-octet digest as a little-endian integer k. const Scalar448 k(shake(pgflag, context, big_r, pk, msg)); @@ -271,7 +416,7 @@ // 6. Form the signature of the concatenation of R (57 octets) and the // little-endian encoding of S (57 octets; the ten most significant // bits of the final octets are always zero). - std::array sig; + std::array sig{}; BufferStuffer stuf(sig); stuf.append(big_r); stuf.append(big_s.to_bytes()); @@ -309,7 +454,9 @@ const Scalar448 k(shake(phflag, context, big_r_bytes, pk, msg)); // 3. Check the group equation [4][S]B = [4]R + [4][k]A’. It’s // sufficient, but not required, to instead check [S]B = R + [k]A’. - return (big_s * Ed448Point::base_point()) == (big_r + k * Ed448Point::decode(pk)); + // Rearranged as [S]B + [k](-A’) = R, computed via Shamir’s trick. + const auto neg_A = Ed448Point::decode(pk).negate(); + return Ed448Point::double_scalar_mul_vartime(big_s, Ed448Point::base_point(), k, neg_A) == big_r; } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/curve448/ed448/ed448_internal.h botan3-3.12.0+dfsg/src/lib/pubkey/curve448/ed448/ed448_internal.h --- botan3-3.7.1+dfsg/src/lib/pubkey/curve448/ed448/ed448_internal.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/curve448/ed448/ed448_internal.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #ifndef BOTAN_ED448_INTERNAL_H_ #define BOTAN_ED448_INTERNAL_H_ +#include #include #include @@ -36,6 +37,9 @@ /// Create a point from its coordinates x, y Ed448Point(const Gf448Elem& x, const Gf448Elem& y) : m_x(x), m_y(y), m_z(1) {} + /// Return the identity element + static Ed448Point identity() { return Ed448Point(Gf448Elem::zero(), Gf448Elem::one()); } + /// Encode the point to its 57-byte representation (RFC 8032 5.2.2) std::array encode() const; @@ -48,6 +52,18 @@ /// Scalar multiplication Ed448Point scalar_mul(const Scalar448& scalar) const; + /// Fixed base point scalar multiplication (precomputed table, no doublings) + static Ed448Point base_point_mul(const Scalar448& scalar); + + /// Variable-time double scalar multiplication using Shamir's trick: [s1]P + [s2]Q + static Ed448Point double_scalar_mul_vartime(const Scalar448& s1, + const Ed448Point& p1, + const Scalar448& s2, + const Ed448Point& p2); + + /// Negate the point + Ed448Point negate() const { return Ed448Point(-m_x, m_y, m_z); } + /// Getter for projective coordinate X Gf448Elem x_proj() const { return m_x; } @@ -66,8 +82,8 @@ /// Check if two points are equal (constant time) bool operator==(const Ed448Point& other) const; - /// Assign other to this if cond is true (constant time) - void ct_conditional_assign(bool cond, const Ed448Point& other); + /// Assign other to this if @p mask is set (constant time) + void ct_conditional_assign(CT::Mask mask, const Ed448Point& other); private: Gf448Elem m_x; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/curve448/info.txt botan3-3.12.0+dfsg/src/lib/pubkey/curve448/info.txt --- botan3-3.7.1+dfsg/src/lib/pubkey/curve448/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/curve448/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,10 +1,6 @@ - -CURVE_448_UTILS -> 20240301 - - -name -> "Curve_448_Utils" -brief -> "Utils for x448 and Ed448" +name -> "Curve448 Arithmetic" +brief -> "x448 and Ed448 Arithmetic" type -> "Internal" diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/curve448/x448/x448.cpp botan3-3.12.0+dfsg/src/lib/pubkey/curve448/x448/x448.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/curve448/x448/x448.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/curve448/x448/x448.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -26,8 +27,10 @@ secure_vector ber_decode_sk(std::span key_bits) { secure_vector decoded_bits; - BER_Decoder(key_bits).decode(decoded_bits, ASN1_Type::OctetString).verify_end(); - BOTAN_ASSERT_NOMSG(decoded_bits.size() == X448_LEN); + BER_Decoder(key_bits, BER_Decoder::Limits::DER()).decode(decoded_bits, ASN1_Type::OctetString).verify_end(); + if(decoded_bits.size() != X448_LEN) { + throw Decoding_Error("Invalid size for X448 private key"); + } return decoded_bits; } @@ -42,11 +45,11 @@ } std::vector X448_PublicKey::raw_public_key_bits() const { - return public_value(); + return {m_public.begin(), m_public.end()}; } std::vector X448_PublicKey::public_key_bits() const { - return public_value(); + return raw_public_key_bits(); } std::unique_ptr X448_PublicKey::generate_another(RandomNumberGenerator& rng) const { @@ -83,7 +86,7 @@ } bool X448_PrivateKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const { - std::array public_point; + std::array public_point{}; BOTAN_ASSERT_NOMSG(m_private.size() == X448_LEN); auto scope = CT::scoped_poison(m_private); x448_basepoint_from_data(public_point, std::span(m_private).first()); @@ -107,9 +110,10 @@ secure_vector raw_agree(const uint8_t w_data[], size_t w_len) override { auto scope = CT::scoped_poison(m_sk); - std::span w(w_data, w_len); - BOTAN_ARG_CHECK(w.size() == X448_LEN, "Invalid size for X448 private key"); - BOTAN_ASSERT_NOMSG(m_sk.size() == X448_LEN); + const std::span w(w_data, w_len); + if(w.size() != X448_LEN) { + throw Decoding_Error("Invalid size for X448 public key"); + } const auto k = decode_scalar(m_sk); const auto u = decode_point(w); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/curve448/x448/x448.h botan3-3.12.0+dfsg/src/lib/pubkey/curve448/x448/x448.h --- botan3-3.7.1+dfsg/src/lib/pubkey/curve448/x448/x448.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/curve448/x448/x448.h 2026-05-07 01:38:28.000000000 +0000 @@ -41,7 +41,9 @@ AlgorithmIdentifier algorithm_identifier() const override; - std::vector public_value() const { return {m_public.begin(), m_public.end()}; } + BOTAN_DEPRECATED("Use raw_public_key_bits") std::vector public_value() const { + return raw_public_key_bits(); + } std::vector raw_public_key_bits() const override; @@ -53,7 +55,7 @@ protected: X448_PublicKey() = default; - std::array m_public; + std::array m_public{}; // NOLINT(*non-private-member-variable*) }; BOTAN_DIAGNOSTIC_PUSH @@ -85,7 +87,7 @@ */ explicit X448_PrivateKey(std::span secret_key); - std::vector public_value() const override { return X448_PublicKey::public_key_bits(); } + std::vector public_value() const override { return raw_public_key_bits(); } secure_vector raw_private_key_bits() const override { return {m_private.begin(), m_private.end()}; } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/curve448/x448/x448_internal.cpp botan3-3.12.0+dfsg/src/lib/pubkey/curve448/x448/x448_internal.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/curve448/x448/x448_internal.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/curve448/x448/x448_internal.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -46,21 +46,19 @@ // Algorithm see RFC 7748, Section 5: // https://datatracker.ietf.org/doc/html/rfc7748#section-5 Point448 x448(const ScalarX448& k, const Point448& u) { - const Gf448Elem a24 = 39081; - - Gf448Elem x_1 = Gf448Elem(u.get()); - Gf448Elem x_2 = 1; - Gf448Elem z_2 = 0; + const Gf448Elem x_1 = Gf448Elem(u.get()); + Gf448Elem x_2 = Gf448Elem::one(); + Gf448Elem z_2 = Gf448Elem::zero(); Gf448Elem x_3 = Gf448Elem(u.get()); - Gf448Elem z_3 = 1; + Gf448Elem z_3 = Gf448Elem::one(); auto swap = CT::Mask::cleared(); for(int16_t t = 448 - 1; t >= 0; --t) { auto k_t = CT::Mask::expand(get_bit(k, t)); swap ^= k_t; - x_2.ct_cond_swap(swap.as_bool(), x_3); - z_2.ct_cond_swap(swap.as_bool(), z_3); + x_2.ct_cond_swap(swap, x_3); + z_2.ct_cond_swap(swap, z_3); swap = k_t; const auto A = x_2 + z_2; @@ -75,11 +73,11 @@ x_3 = square(DA + CB); z_3 = x_1 * square(DA - CB); x_2 = AA * BB; - z_2 = E * (AA + a24 * E); + z_2 = E * (AA + mul_a24(E)); } - x_2.ct_cond_swap(swap.as_bool(), x_3); - z_2.ct_cond_swap(swap.as_bool(), z_3); + x_2.ct_cond_swap(swap, x_3); + z_2.ct_cond_swap(swap, z_3); const auto res = x_2 / z_2; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dh/dh.cpp botan3-3.12.0+dfsg/src/lib/pubkey/dh/dh.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/dh/dh.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dh/dh.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -21,10 +21,6 @@ m_public_key = std::make_shared(group, y); } -std::vector DH_PublicKey::public_value() const { - return m_public_key->public_key_as_bytes(); -} - size_t DH_PublicKey::estimated_strength() const { return m_public_key->estimated_strength(); } @@ -46,7 +42,7 @@ } std::vector DH_PublicKey::raw_public_key_bits() const { - return public_value(); + return m_public_key->public_key_as_bytes(); } std::vector DH_PublicKey::public_key_bits() const { @@ -76,12 +72,16 @@ m_public_key = m_private_key->public_key(); } +bool DH_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const { + return m_private_key->check_key(rng, strong); +} + std::unique_ptr DH_PrivateKey::public_key() const { return std::unique_ptr(new DH_PublicKey(m_public_key)); } std::vector DH_PrivateKey::public_value() const { - return DH_PublicKey::public_value(); + return raw_public_key_bits(); } secure_vector DH_PrivateKey::private_key_bits() const { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dh/dh.h botan3-3.12.0+dfsg/src/lib/pubkey/dh/dh.h --- botan3-3.7.1+dfsg/src/lib/pubkey/dh/dh.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dh/dh.h 2026-05-07 01:38:28.000000000 +0000 @@ -48,7 +48,9 @@ size_t estimated_strength() const override; size_t key_length() const override; - std::vector public_value() const; + BOTAN_DEPRECATED("Use raw_public_key_bits") std::vector public_value() const { + return raw_public_key_bits(); + } std::string algo_name() const override { return "DH"; } @@ -65,7 +67,7 @@ DH_PublicKey() = default; - DH_PublicKey(std::shared_ptr key) : m_public_key(std::move(key)) {} + explicit DH_PublicKey(std::shared_ptr key) : m_public_key(std::move(key)) {} std::shared_ptr m_public_key; }; @@ -78,7 +80,7 @@ BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE class BOTAN_PUBLIC_API(2, 0) DH_PrivateKey final : public DH_PublicKey, - public PK_Key_Agreement_Key, + public virtual PK_Key_Agreement_Key, public virtual Private_Key { public: /** @@ -106,6 +108,8 @@ std::vector public_value() const override; + bool check_key(RandomNumberGenerator& rng, bool strong) const override; + secure_vector private_key_bits() const override; secure_vector raw_private_key_bits() const override; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -248,7 +249,7 @@ class Dilithium_Verification_Operation final : public PK_Ops::Verification { public: - Dilithium_Verification_Operation(std::shared_ptr pubkey) : + explicit Dilithium_Verification_Operation(std::shared_ptr pubkey) : m_pub_key(std::move(pubkey)), m_A(Dilithium_Algos::expand_A(m_pub_key->rho(), m_pub_key->mode())), m_t1_ntt_shifted(ntt(m_pub_key->t1() << DilithiumConstants::D)), @@ -269,7 +270,7 @@ bool is_valid_signature(std::span sig) override { const auto& mode = m_pub_key->mode(); const auto& sympri = mode.symmetric_primitives(); - StrongSpan sig_bytes(sig); + const StrongSpan sig_bytes(sig); const auto mu = m_h->final(); @@ -365,8 +366,14 @@ return raw_public_key_bits(); } -bool Dilithium_PublicKey::check_key(RandomNumberGenerator&, bool) const { - return true; // ??? +bool Dilithium_PublicKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const { + // The public key consists of (rho, t1). Length validation is performed in + // the constructor, and t1 coefficients are decoded via SimpleBitUnpack + // (FIPS 204 Algorithm 18) into a power-of-2 range that exactly covers all + // valid values, so no out-of-range coefficients are possible. For the + // private key, s1/s2 coefficient ranges are validated and t is recomputed + // from (A, s1, s2) and verified against the stored hash during decoding. + return true; } std::unique_ptr Dilithium_PublicKey::generate_another(RandomNumberGenerator& rng) const { @@ -446,6 +453,18 @@ throw Provider_Not_Found(algo_name(), provider); } +bool Dilithium_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const { + if(!Dilithium_PublicKey::check_key(rng, strong)) { + return false; + } + + if(strong) { + return KeyPair::signature_consistency_check(rng, *this, ""); + } + + return true; +} + std::unique_ptr Dilithium_PrivateKey::public_key() const { return std::make_unique(*this); } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium.h botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium.h --- botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium.h 2026-05-07 01:38:28.000000000 +0000 @@ -18,14 +18,14 @@ namespace Botan { -class BOTAN_PUBLIC_API(3, 0) DilithiumMode { +class BOTAN_PUBLIC_API(3, 0) DilithiumMode final { public: - enum Mode { - Dilithium4x4 = 1, + enum Mode : uint8_t /* NOLINT(*-use-enum-class) */ { + Dilithium4x4 BOTAN_DEPRECATED("Dilithium R3 is deprecated - use ML-DSA") = 1, Dilithium4x4_AES BOTAN_DEPRECATED("Dilithium AES mode is deprecated"), - Dilithium6x5, + Dilithium6x5 BOTAN_DEPRECATED("Dilithium R3 is deprecated - use ML-DSA"), Dilithium6x5_AES BOTAN_DEPRECATED("Dilithium AES mode is deprecated"), - Dilithium8x7, + Dilithium8x7 BOTAN_DEPRECATED("Dilithium R3 is deprecated - use ML-DSA"), Dilithium8x7_AES BOTAN_DEPRECATED("Dilithium AES mode is deprecated"), ML_DSA_4x4, ML_DSA_6x5, @@ -33,6 +33,7 @@ }; public: + // NOLINTNEXTLINE(*-explicit-conversions) DilithiumMode(Mode mode) : m_mode(mode) {} explicit DilithiumMode(const OID& oid); @@ -66,10 +67,6 @@ */ class BOTAN_PUBLIC_API(3, 0) Dilithium_PublicKey : public virtual Public_Key { public: - Dilithium_PublicKey& operator=(const Dilithium_PublicKey& other) = default; - - ~Dilithium_PublicKey() override = default; - std::string algo_name() const override; AlgorithmIdentifier algorithm_identifier() const override; @@ -84,7 +81,7 @@ std::vector public_key_bits() const override; - bool check_key(RandomNumberGenerator&, bool) const override; + bool check_key(RandomNumberGenerator& rng, bool strong) const override; bool supports_operation(PublicKeyOperation op) const override { return (op == PublicKeyOperation::Signature); } @@ -106,7 +103,7 @@ friend class Dilithium_Verification_Operation; friend class Dilithium_Signature_Operation; - std::shared_ptr m_public; + std::shared_ptr m_public; // NOLINT(*non-private-member-variable*) }; BOTAN_DIAGNOSTIC_PUSH @@ -117,6 +114,8 @@ public: std::unique_ptr public_key() const override; + bool check_key(RandomNumberGenerator& rng, bool strong) const override; + /** * Generates a new key pair */ @@ -141,7 +140,7 @@ * with "Randomized" or "Deterministic" rhoprime. Pass either of those * strings as @p params. Default (i.e. empty @p params is "Randomized"). */ - std::unique_ptr create_signature_op(RandomNumberGenerator&, + std::unique_ptr create_signature_op(RandomNumberGenerator& rng, std::string_view params, std::string_view provider) const override; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.cpp botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -18,15 +18,14 @@ #include #include +#include +#include #include #include #include -#include #include #include #include -#include - #include namespace Botan::Dilithium_Algos { @@ -113,10 +112,10 @@ using Gamma2 = DilithiumConstants::DilithiumGamma2; auto calculate_b = [](auto gamma2) { return ((DilithiumConstants::Q - 1) / (2 * gamma2)) - 1; }; switch(mode.gamma2()) { - case Gamma2::Qminus1DevidedBy88: - return poly_pack<0, calculate_b(Gamma2::Qminus1DevidedBy88)>(p, stuffer); - case Gamma2::Qminus1DevidedBy32: - return poly_pack<0, calculate_b(Gamma2::Qminus1DevidedBy32)>(p, stuffer); + case Gamma2::Qminus1DividedBy88: + return poly_pack<0, calculate_b(Gamma2::Qminus1DividedBy88)>(p, stuffer); + case Gamma2::Qminus1DividedBy32: + return poly_pack<0, calculate_b(Gamma2::Qminus1DividedBy32)>(p, stuffer); } BOTAN_ASSERT_UNREACHABLE(); @@ -287,8 +286,12 @@ } // Check that the remaining bit positions are all zero (strong unforgeability) - const auto remaining = bit_positions.take(bit_positions.remaining()); - if(!std::all_of(remaining.begin(), remaining.end(), [](auto b) { return b == 0; })) { + uint8_t sum = 0; + for(const uint8_t b : bit_positions.take(bit_positions.remaining())) { + sum |= b; + } + + if(sum != 0) { return std::nullopt; } @@ -366,7 +369,7 @@ * NIST FIPS 204, Algorithm 24 (skEncode) */ DilithiumSerializedPrivateKey encode_keypair(const DilithiumInternalKeypair& keypair) { - auto& [pk, sk] = keypair; + const auto& [pk, sk] = keypair; BOTAN_ASSERT_NONNULL(pk); BOTAN_ASSERT_NONNULL(sk); const auto& mode = sk->mode(); @@ -501,7 +504,7 @@ for(auto& p : response) { poly_unpack_gamma1(p, slicer, mode); } - BOTAN_ASSERT_NOMSG(slicer.remaining() == mode.omega() + mode.k()); + BOTAN_ASSERT_NOMSG(slicer.remaining() == size_t(mode.omega()) + mode.k()); auto hint = hint_unpack(slicer, mode); BOTAN_ASSERT_NOMSG(slicer.empty()); @@ -531,8 +534,8 @@ */ DilithiumPoly sample_in_ball(StrongSpan seed, const DilithiumConstants& mode) { // This generator resembles the while loop in the spec. - auto& xof = mode.symmetric_primitives().H(seed); - auto bounded_xof = Bounded_XOF(xof); + auto xof = mode.symmetric_primitives().H(seed); + auto bounded_xof = Bounded_XOF(*xof); DilithiumPoly c; uint64_t signs = load_le(bounded_xof.next<8>()); @@ -562,8 +565,8 @@ * A generator that returns the next coefficient sampled from the XOF, * according to: NIST FIPS 204, Algorithm 14 (CoeffFromThreeBytes). */ - auto& xof = mode.symmetric_primitives().H(rho, nonce); - auto bounded_xof = Bounded_XOF(xof); + auto xof = mode.symmetric_primitives().H(rho, nonce); + auto bounded_xof = Bounded_XOF(*xof); for(auto& coeff : p) { coeff = @@ -585,7 +588,7 @@ if constexpr(eta == DilithiumConstants::DilithiumEta::_2) { if(CT::driveby_unpoison(b < 15)) { - b = b - (205 * b >> 10) * 5; // b = b mod 5 + b = b - (205U * b >> 10) * 5; // b = b mod 5 return 2 - b; } } else if constexpr(eta == DilithiumConstants::DilithiumEta::_4) { @@ -638,13 +641,13 @@ const DilithiumConstants& mode) { using Eta = DilithiumConstants::DilithiumEta; - auto& xof = mode.symmetric_primitives().H(rhoprime, nonce); + auto xof = mode.symmetric_primitives().H(rhoprime, nonce); switch(mode.eta()) { case Eta::_2: - sample_uniform_eta(p, xof); + sample_uniform_eta(p, *xof); break; case Eta::_4: - sample_uniform_eta(p, xof); + sample_uniform_eta(p, *xof); break; } @@ -663,6 +666,9 @@ * encoding is deferred until the user explicitly invokes the encoding. */ DilithiumInternalKeypair expand_keypair(DilithiumSeedRandomness xi, DilithiumConstants mode) { + if(xi.size() != DilithiumConstants::SEED_RANDOMNESS_BYTES) { + throw Decoding_Error("Invalid ML-DSA seed size"); + } const auto& sympriv = mode.symmetric_primitives(); CT::poison(xi); @@ -684,7 +690,7 @@ CT::unpoison(*keypair.second); return keypair; -}; +} /** * NIST FIPS 204, Algorithm 32 (ExpandA) @@ -730,8 +736,8 @@ const DilithiumConstants& mode) { DilithiumPolyVec s(mode.l()); for(auto& p : s) { - auto& xof = mode.symmetric_primitives().H(rhoprime, nonce++); - poly_unpack_gamma1(p, xof, mode); + auto xof = mode.symmetric_primitives().H(rhoprime, nonce++); + poly_unpack_gamma1(p, *xof, mode); } return s; } @@ -775,10 +781,10 @@ std::pair decompose(int32_t r) { int32_t r1 = (r + 127) >> 7; - if constexpr(gamma2 == DilithiumConstants::DilithiumGamma2::Qminus1DevidedBy32) { + if constexpr(gamma2 == DilithiumConstants::DilithiumGamma2::Qminus1DividedBy32) { r1 = (r1 * 1025 + (1 << 21)) >> 22; r1 &= 15; - } else if constexpr(gamma2 == DilithiumConstants::DilithiumGamma2::Qminus1DevidedBy88) { + } else if constexpr(gamma2 == DilithiumConstants::DilithiumGamma2::Qminus1DividedBy88) { r1 = (r1 * 11275 + (1 << 23)) >> 24; r1 = is_negative_mask(43 - r1).if_not_set_return(r1); } @@ -796,7 +802,7 @@ * optimization given the statically known value of gamma2. */ template -std::pair decompose_all_coefficents(const DilithiumPolyVec& vec) { +std::pair decompose_all_coefficients(const DilithiumPolyVec& vec) { auto result = std::make_pair(DilithiumPolyVec(vec.size()), DilithiumPolyVec(vec.size())); for(size_t i = 0; i < vec.size(); ++i) { @@ -819,11 +825,11 @@ std::pair decompose(const DilithiumPolyVec& vec, const DilithiumConstants& mode) { using Gamma2 = DilithiumConstants::DilithiumGamma2; switch(mode.gamma2()) { - case Gamma2::Qminus1DevidedBy32: - return decompose_all_coefficents(vec); + case Gamma2::Qminus1DividedBy32: + return decompose_all_coefficients(vec); break; - case Gamma2::Qminus1DevidedBy88: - return decompose_all_coefficents(vec); + case Gamma2::Qminus1DividedBy88: + return decompose_all_coefficients(vec); break; } @@ -861,7 +867,7 @@ for(size_t i = 0; i < r.size(); ++i) { for(size_t j = 0; j < r[i].size(); ++j) { - hint[i][j] = make_hint(z[i][j], r[i][j]).as_bool(); + hint[i][j] = static_cast(make_hint(z[i][j], r[i][j]).as_bool()); } } @@ -922,11 +928,11 @@ using Gamma2 = DilithiumConstants::DilithiumGamma2; switch(mode.gamma2()) { - case Gamma2::Qminus1DevidedBy32: - use_hint_on_coefficients(hints, vec); + case Gamma2::Qminus1DividedBy32: + use_hint_on_coefficients(hints, vec); break; - case Gamma2::Qminus1DevidedBy88: - use_hint_on_coefficients(hints, vec); + case Gamma2::Qminus1DividedBy88: + use_hint_on_coefficients(hints, vec); break; } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.h botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.h --- botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.h 2026-05-07 01:38:28.000000000 +0000 @@ -16,6 +16,7 @@ // ML-DSA does encode the private key only by its random seeds. #if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES) + // NOLINTNEXTLINE(*-macro-usage) #define BOTAN_NEEDS_DILITHIUM_PRIVATE_KEY_ENCODING 1 #endif diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_constants.cpp botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_constants.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_constants.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_constants.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -67,7 +67,7 @@ m_tau = DilithiumTau::_39; m_lambda = DilithiumLambda::_128; m_gamma1 = DilithiumGamma1::ToThe17th; - m_gamma2 = DilithiumGamma2::Qminus1DevidedBy88; + m_gamma2 = DilithiumGamma2::Qminus1DividedBy88; m_k = 4; m_l = 4; m_eta = DilithiumEta::_2; @@ -80,7 +80,7 @@ m_tau = DilithiumTau::_49; m_lambda = DilithiumLambda::_192; m_gamma1 = DilithiumGamma1::ToThe19th; - m_gamma2 = DilithiumGamma2::Qminus1DevidedBy32; + m_gamma2 = DilithiumGamma2::Qminus1DividedBy32; m_k = 6; m_l = 5; m_eta = DilithiumEta::_4; @@ -93,7 +93,7 @@ m_tau = DilithiumTau::_60; m_lambda = DilithiumLambda::_256; m_gamma1 = DilithiumGamma1::ToThe19th; - m_gamma2 = DilithiumGamma2::Qminus1DevidedBy32; + m_gamma2 = DilithiumGamma2::Qminus1DividedBy32; m_k = 8; m_l = 7; m_eta = DilithiumEta::_2; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_constants.h botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_constants.h --- botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_constants.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_constants.h 2026-05-07 01:38:28.000000000 +0000 @@ -70,21 +70,25 @@ /// @} public: - enum DilithiumTau : uint32_t { _39 = 39, _49 = 49, _60 = 60 }; + // NOLINTBEGIN(*-use-enum-class) - enum DilithiumLambda : uint32_t { _128 = 128, _192 = 192, _256 = 256 }; + enum DilithiumTau : uint8_t { _39 = 39, _49 = 49, _60 = 60 }; + + enum DilithiumLambda : uint16_t { _128 = 128, _192 = 192, _256 = 256 }; enum DilithiumGamma1 : uint32_t { ToThe17th = (1 << 17), ToThe19th = (1 << 19) }; - enum DilithiumGamma2 : uint32_t { Qminus1DevidedBy88 = (Q - 1) / 88, Qminus1DevidedBy32 = (Q - 1) / 32 }; + enum DilithiumGamma2 : uint32_t { Qminus1DividedBy88 = (Q - 1) / 88, Qminus1DividedBy32 = (Q - 1) / 32 }; + + enum DilithiumEta : uint8_t { _2 = 2, _4 = 4 }; - enum DilithiumEta : uint32_t { _2 = 2, _4 = 4 }; + enum DilithiumBeta : uint8_t { _78 = 78, _196 = 196, _120 = 120 }; - enum DilithiumBeta : uint32_t { _78 = 78, _196 = 196, _120 = 120 }; + enum DilithiumOmega : uint8_t { _80 = 80, _55 = 55, _75 = 75 }; - enum DilithiumOmega : uint32_t { _80 = 80, _55 = 55, _75 = 75 }; + // NOLINTEND(*-use-enum-class) - DilithiumConstants(DilithiumMode dimension); + explicit DilithiumConstants(DilithiumMode dimension); ~DilithiumConstants(); DilithiumConstants(const DilithiumConstants& other) : DilithiumConstants(other.m_mode) {} diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_keys.h botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_keys.h --- botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_keys.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_keys.h 2026-05-07 01:38:28.000000000 +0000 @@ -20,7 +20,7 @@ namespace Botan { -class Dilithium_Keypair_Codec { +class Dilithium_Keypair_Codec /* NOLINT(*-special-member-functions) */ { public: static std::unique_ptr create(DilithiumMode mode); @@ -30,7 +30,7 @@ DilithiumConstants mode) const = 0; }; -class Dilithium_PublicKeyInternal { +class Dilithium_PublicKeyInternal final { public: static std::shared_ptr decode( DilithiumConstants mode, StrongSpan raw_pk) { @@ -65,7 +65,7 @@ DilithiumHashedPublicKey m_tr; }; -class Dilithium_PrivateKeyInternal { +class Dilithium_PrivateKeyInternal final { public: Dilithium_PrivateKeyInternal(DilithiumConstants mode, std::optional seed, diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_polynomial.h botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_polynomial.h --- botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_polynomial.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_polynomial.h 2026-05-07 01:38:28.000000000 +0000 @@ -25,7 +25,7 @@ static constexpr T montgomery_reduce_coefficient(T2 a) { const T2 t = static_cast(static_cast(static_cast(a)) * Q_inverse); - return (a - static_cast(t) * Q) >> (sizeof(T) * 8); + return static_cast((a - static_cast(t) * Q) >> (sizeof(T) * 8)); } static constexpr T barrett_reduce_coefficient(T a) { @@ -48,7 +48,7 @@ * factors in the coefficients. */ static constexpr void ntt(std::span coeffs) { - size_t j; + size_t j = 0; size_t k = 0; for(size_t len = N / 2; len > 0; len >>= 1) { @@ -56,7 +56,7 @@ const T zeta = zetas[++k]; for(j = start; j < start + len; ++j) { // Zetas contain the montgomery parameter 2^32 mod q - T t = fqmul(zeta, coeffs[j + len]); + const T t = fqmul(zeta, coeffs[j + len]); coeffs[j + len] = coeffs[j] - t; coeffs[j] = coeffs[j] + t; } @@ -76,13 +76,13 @@ * factor of (2^32 mod q) added (!). See above. */ static constexpr void inverse_ntt(std::span coeffs) { - size_t j; + size_t j = 0; size_t k = N; for(size_t len = 1; len < N; len <<= 1) { for(size_t start = 0; start < N; start = j + len) { const T zeta = -zetas[--k]; for(j = start; j < start + len; ++j) { - T t = coeffs[j]; + const T t = coeffs[j]; coeffs[j] = t + coeffs[j + len]; coeffs[j + len] = t - coeffs[j + len]; // Zetas contain the montgomery parameter 2^32 mod q diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_shake/dilithium_shake_xof.cpp botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_shake/dilithium_shake_xof.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_shake/dilithium_shake_xof.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_shake/dilithium_shake_xof.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,25 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include + +namespace Botan { + +DilithiumShakeXOF::~DilithiumShakeXOF() = default; + +//static +std::unique_ptr DilithiumShakeXOF::createXOF(std::string_view name, + std::span seed, + uint16_t nonce) { + auto xof = Botan::XOF::create_or_throw(name); + xof->update(seed); + xof->update(store_le(nonce)); + return xof; +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_shake/dilithium_shake_xof.h botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_shake/dilithium_shake_xof.h --- botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_shake/dilithium_shake_xof.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_shake/dilithium_shake_xof.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,32 +11,31 @@ #include -#include -#include - namespace Botan { class DilithiumShakeXOF final : public DilithiumXOF { public: - Botan::XOF& XOF128(std::span seed, uint16_t nonce) const override { - return XOF(m_xof_128, seed, nonce); - } + DilithiumShakeXOF() = default; + + ~DilithiumShakeXOF() override; - Botan::XOF& XOF256(std::span seed, uint16_t nonce) const override { - return XOF(m_xof_256, seed, nonce); + DilithiumShakeXOF(const DilithiumShakeXOF& other) = delete; + DilithiumShakeXOF(DilithiumShakeXOF&& other) = delete; + DilithiumShakeXOF& operator=(const DilithiumShakeXOF& other) = delete; + DilithiumShakeXOF& operator=(DilithiumShakeXOF&& other) = delete; + + std::unique_ptr XOF128(std::span seed, uint16_t nonce) const override { + return createXOF("SHAKE-128", seed, nonce); } - private: - static Botan::XOF& XOF(Botan::XOF& xof, std::span seed, uint16_t nonce) { - xof.clear(); - xof.update(seed); - xof.update(store_le(nonce)); - return xof; + std::unique_ptr XOF256(std::span seed, uint16_t nonce) const override { + return createXOF("SHAKE-256", seed, nonce); } private: - mutable SHAKE_256_XOF m_xof_256; - mutable SHAKE_128_XOF m_xof_128; + static std::unique_ptr createXOF(std::string_view name, + std::span seed, + uint16_t nonce); }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_symmetric_primitives.cpp botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_symmetric_primitives.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_symmetric_primitives.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_symmetric_primitives.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,6 +11,8 @@ #include +#include + #if defined(BOTAN_HAS_DILITHIUM) #include #endif @@ -25,6 +27,22 @@ namespace Botan { +DilithiumMessageHash::DilithiumMessageHash(DilithiumHashedPublicKey tr) : + m_tr(std::move(tr)), m_shake(XOF::create_or_throw("SHAKE-256")) {} + +DilithiumMessageHash::~DilithiumMessageHash() = default; + +std::string DilithiumMessageHash::name() const { + return Botan::fmt("{}({})", m_shake->name(), DilithiumConstants::MESSAGE_HASH_BYTES * 8); +} + +Dilithium_Symmetric_Primitives_Base::Dilithium_Symmetric_Primitives_Base(const DilithiumConstants& mode, + std::unique_ptr xof_adapter) : + m_commitment_hash_length_bytes(mode.commitment_hash_full_bytes()), + m_public_key_hash_bytes(mode.public_key_hash_bytes()), + m_mode(mode.mode()), + m_xof_adapter(std::move(xof_adapter)) {} + std::unique_ptr Dilithium_Symmetric_Primitives_Base::create( const DilithiumConstants& mode) { #if defined(BOTAN_HAS_DILITHIUM) diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_symmetric_primitives.h botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_symmetric_primitives.h --- botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_symmetric_primitives.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/dilithium_symmetric_primitives.h 2026-05-07 01:38:28.000000000 +0000 @@ -12,11 +12,8 @@ #define BOTAN_DILITHIUM_ASYM_PRIMITIVES_H_ #include - +#include #include -#include -#include -#include namespace Botan { @@ -28,15 +25,13 @@ * * Namely: mu = H(tr || M) */ -class DilithiumMessageHash { +class DilithiumMessageHash /* NOLINT(*-special-member-functions) */ { public: - DilithiumMessageHash(DilithiumHashedPublicKey tr) : m_tr(std::move(tr)) { clear(); } + explicit DilithiumMessageHash(DilithiumHashedPublicKey tr); - virtual ~DilithiumMessageHash() = default; + virtual ~DilithiumMessageHash(); - std::string name() const { - return Botan::fmt("{}({})", m_shake.name(), DilithiumConstants::MESSAGE_HASH_BYTES * 8); - } + std::string name() const; virtual bool is_valid_user_context(std::span user_context) const { // Only ML-DSA supports user contexts, for all other modes it must be empty. @@ -52,18 +47,18 @@ void update(std::span data) { ensure_started(); - m_shake.update(data); + m_shake->update(data); } DilithiumMessageRepresentative final() { ensure_started(); - scoped_cleanup clean([this]() { clear(); }); - return m_shake.output(DilithiumConstants::MESSAGE_HASH_BYTES); + const scoped_cleanup clean([this]() { clear(); }); + return m_shake->output(DilithiumConstants::MESSAGE_HASH_BYTES); } private: void clear() { - m_shake.clear(); + m_shake->clear(); m_was_started = false; } @@ -76,8 +71,8 @@ private: DilithiumHashedPublicKey m_tr; - bool m_was_started; - SHAKE_256_XOF m_shake; + bool m_was_started = false; + std::unique_ptr m_shake; }; /** @@ -86,12 +81,12 @@ * was not standardized in the FIPS 204; ML-DSA always uses SHAKE. Once we decide * to remove the AES variant, this can be removed. */ -class DilithiumXOF { +class DilithiumXOF /* NOLINT(*-special-member-functions) */ { public: virtual ~DilithiumXOF() = default; - virtual Botan::XOF& XOF128(std::span seed, uint16_t nonce) const = 0; - virtual Botan::XOF& XOF256(std::span seed, uint16_t nonce) const = 0; + virtual std::unique_ptr XOF128(std::span seed, uint16_t nonce) const = 0; + virtual std::unique_ptr XOF256(std::span seed, uint16_t nonce) const = 0; }; /** @@ -100,11 +95,7 @@ */ class Dilithium_Symmetric_Primitives_Base { protected: - Dilithium_Symmetric_Primitives_Base(const DilithiumConstants& mode, std::unique_ptr xof_adapter) : - m_commitment_hash_length_bytes(mode.commitment_hash_full_bytes()), - m_public_key_hash_bytes(mode.public_key_hash_bytes()), - m_mode(mode.mode()), - m_xof_adapter(std::move(xof_adapter)) {} + Dilithium_Symmetric_Primitives_Base(const DilithiumConstants& mode, std::unique_ptr xof_adapter); public: static std::unique_ptr create(const DilithiumConstants& mode); @@ -132,18 +123,18 @@ std::tuple H( StrongSpan seed) const { - m_xof.update(seed); + auto xof = XOF::create_or_throw("SHAKE-256"); + xof->update(seed); if(auto domsep = seed_expansion_domain_separator()) { - m_xof.update(domsep.value()); + xof->update(domsep.value()); } // Note: The order of invocations in an initializer list is not // guaranteed by the C++ standard. Hence, we have to store the // results in variables to ensure the correct order of execution. - auto rho = m_xof.output(DilithiumConstants::SEED_RHO_BYTES); - auto rhoprime = m_xof.output(DilithiumConstants::SEED_RHOPRIME_BYTES); - auto k = m_xof.output(DilithiumConstants::SEED_SIGNING_KEY_BYTES); - m_xof.clear(); + auto rho = xof->output(DilithiumConstants::SEED_RHO_BYTES); + auto rhoprime = xof->output(DilithiumConstants::SEED_RHOPRIME_BYTES); + auto k = xof->output(DilithiumConstants::SEED_SIGNING_KEY_BYTES); return {std::move(rho), std::move(rhoprime), std::move(k)}; } @@ -153,21 +144,17 @@ return H_256(m_commitment_hash_length_bytes, mu, w1); } - SHAKE_256_XOF& H(StrongSpan seed) const { - m_xof_external.clear(); - m_xof_external.update(truncate_commitment_hash(seed)); - return m_xof_external; + std::unique_ptr H(StrongSpan seed) const { + auto xof = XOF::create_or_throw("SHAKE-256"); + xof->update(truncate_commitment_hash(seed)); + return xof; } - // Once Dilithium AES is removed, this could return a SHAKE_256_XOF and - // avoid the virtual method call. - Botan::XOF& H(StrongSpan seed, uint16_t nonce) const { + std::unique_ptr H(StrongSpan seed, uint16_t nonce) const { return m_xof_adapter->XOF128(seed, nonce); } - // Once Dilithium AES is removed, this could return a SHAKE_128_XOF and - // avoid the virtual method call. - Botan::XOF& H(StrongSpan seed, uint16_t nonce) const { + std::unique_ptr H(StrongSpan seed, uint16_t nonce) const { return m_xof_adapter->XOF256(seed, nonce); } @@ -188,10 +175,10 @@ virtual std::optional> seed_expansion_domain_separator() const = 0; template - OutT H_256(size_t outbytes, InTs&&... ins) const { - scoped_cleanup clean([this]() { m_xof.clear(); }); - (m_xof.update(ins), ...); - return m_xof.output(outbytes); + OutT H_256(size_t outbytes, const InTs&... ins) const { + auto xof = XOF::create_or_throw("SHAKE-256"); + (xof->update(ins), ...); + return xof->output(outbytes); } private: @@ -200,8 +187,6 @@ DilithiumMode m_mode; std::unique_ptr m_xof_adapter; - mutable SHAKE_256_XOF m_xof; - mutable SHAKE_256_XOF m_xof_external; }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/info.txt botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/info.txt --- botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_common/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_common/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -22,6 +22,7 @@ +keypair pqcrystals pubkey rng diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_round3/dilithium/dilithium_round3.h botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_round3/dilithium/dilithium_round3.h --- botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_round3/dilithium/dilithium_round3.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_round3/dilithium/dilithium_round3.h 2026-05-07 01:38:28.000000000 +0000 @@ -17,7 +17,7 @@ class Dilithium_Symmetric_Primitives final : public Dilithium_Round3_Symmetric_Primitives { public: - Dilithium_Symmetric_Primitives(const DilithiumConstants& mode) : + explicit Dilithium_Symmetric_Primitives(const DilithiumConstants& mode) : Dilithium_Round3_Symmetric_Primitives(mode, std::make_unique()) {} }; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_round3/dilithium_aes/dilithium_aes.cpp botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_round3/dilithium_aes/dilithium_aes.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_round3/dilithium_aes/dilithium_aes.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_round3/dilithium_aes/dilithium_aes.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -19,16 +19,17 @@ class AES_XOF final : public DilithiumXOF { public: - Botan::XOF& XOF128(std::span seed, uint16_t nonce) const override { - return XOF(m_aes_xof, seed, nonce); + std::unique_ptr XOF128(std::span seed, uint16_t nonce) const override { + return create_xof(seed, nonce); } - Botan::XOF& XOF256(std::span seed, uint16_t nonce) const override { - return XOF(m_aes_xof, seed, nonce); + std::unique_ptr XOF256(std::span seed, uint16_t nonce) const override { + return create_xof(seed, nonce); } + private: // AES mode always uses AES-256, regardless of the XofType - static Botan::XOF& XOF(Botan::XOF& xof, std::span seed, uint16_t nonce) { + static std::unique_ptr create_xof(std::span seed, uint16_t nonce) { // Algorithm Spec V. 3.1 Section 5.3 // In the AES variant, the first 32 bytes of rhoprime are used as // the key and i is extended to a 12 byte nonce for AES-256 in @@ -41,13 +42,10 @@ const std::array iv{get_byte<1>(nonce), get_byte<0>(nonce), 0}; const auto key = seed.first(32); - xof.clear(); - xof.start(iv, key); + auto xof = std::make_unique(); + xof->start(iv, key); return xof; } - - private: - mutable AES_256_CTR_XOF m_aes_xof; }; } // namespace diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_round3/dilithium_aes/dilithium_aes.h botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_round3/dilithium_aes/dilithium_aes.h --- botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_round3/dilithium_aes/dilithium_aes.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_round3/dilithium_aes/dilithium_aes.h 2026-05-07 01:38:28.000000000 +0000 @@ -15,7 +15,7 @@ class Dilithium_AES_Symmetric_Primitives final : public Dilithium_Round3_Symmetric_Primitives { public: - Dilithium_AES_Symmetric_Primitives(const DilithiumConstants& mode); + explicit Dilithium_AES_Symmetric_Primitives(const DilithiumConstants& mode); }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_round3/dilithium_round3_symmetric_primitives.cpp botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_round3/dilithium_round3_symmetric_primitives.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/dilithium_round3/dilithium_round3_symmetric_primitives.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/dilithium_round3/dilithium_round3_symmetric_primitives.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,7 +10,6 @@ #include -#include #include namespace Botan { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/ml_dsa/ml_dsa.h botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/ml_dsa/ml_dsa.h --- botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/ml_dsa/ml_dsa.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/ml_dsa/ml_dsa.h 2026-05-07 01:38:28.000000000 +0000 @@ -24,4 +24,4 @@ } // namespace Botan -#endif \ No newline at end of file +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.h botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.h --- botan3-3.7.1+dfsg/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.h 2026-05-07 01:38:28.000000000 +0000 @@ -61,7 +61,7 @@ } public: - ML_DSA_Symmetric_Primitives(const DilithiumConstants& mode) : + explicit ML_DSA_Symmetric_Primitives(const DilithiumConstants& mode) : Dilithium_Symmetric_Primitives_Base(mode, std::make_unique()), m_seed_expansion_domain_separator({mode.k(), mode.l()}) {} diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dl_algo/dl_scheme.cpp botan3-3.12.0+dfsg/src/lib/pubkey/dl_algo/dl_scheme.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/dl_algo/dl_scheme.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dl_algo/dl_scheme.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -16,13 +16,13 @@ BigInt decode_single_bigint(std::span key_bits) { BigInt x; - BER_Decoder(key_bits).decode(x); + BER_Decoder(key_bits, BER_Decoder::Limits::DER()).decode(x).verify_end(); return x; } BigInt generate_private_dl_key(const DL_Group& group, RandomNumberGenerator& rng) { if(group.has_q() && group.q_bits() >= 160 && group.q_bits() <= 384) { - return BigInt::random_integer(rng, 2, group.get_q()); + return BigInt::random_integer(rng, BigInt::from_s32(2), group.get_q()); } else { return BigInt(rng, group.exponent_bits()); } @@ -35,13 +35,17 @@ } // namespace -DL_PublicKey::DL_PublicKey(const DL_Group& group, const BigInt& public_key) : - m_group(group), m_public_key(public_key) {} +DL_PublicKey::DL_PublicKey(const DL_Group& group, const BigInt& public_key) : m_group(group), m_public_key(public_key) { + // The subgroup check (y^q == 1 mod p) is deferred to check_key() since it can be expensive + BOTAN_ARG_CHECK(m_public_key > 1 && m_public_key < m_group.get_p(), "Invalid DL public key"); +} DL_PublicKey::DL_PublicKey(const AlgorithmIdentifier& alg_id, std::span key_bits, DL_Group_Format format) : - m_group(alg_id.parameters(), format), m_public_key(decode_single_bigint(key_bits)) {} + m_group(alg_id.parameters(), format), m_public_key(decode_single_bigint(key_bits)) { + BOTAN_ARG_CHECK(m_public_key > 1 && m_public_key < m_group.get_p(), "Invalid DL public key"); +} std::vector DL_PublicKey::public_key_as_bytes() const { return m_public_key.serialize(m_group.p_bytes()); @@ -80,7 +84,7 @@ DL_Group_Format format) : m_group(alg_id.parameters(), format), m_private_key(check_dl_private_key_input(decode_single_bigint(key_bits), m_group)), - m_public_key(m_group.power_g_p(m_private_key, m_group.p_bits())) {} + m_public_key(m_group.power_g_p(m_private_key, m_private_key.bits())) {} secure_vector DL_PrivateKey::DER_encode() const { return DER_Encoder().encode(m_private_key).get_contents(); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dl_group/dl_group.cpp botan3-3.12.0+dfsg/src/lib/pubkey/dl_group/dl_group.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/dl_group/dl_group.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dl_group/dl_group.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include @@ -23,15 +23,50 @@ namespace Botan { +namespace { + +void check_dl_group_params(const BigInt& p, const BigInt& g) { + if(p.signum() <= 0 || p.is_even() || p.bits() < 3 || p.bits() > 16384) { + throw Decoding_Error("Invalid DL group prime"); + } + if(g.signum() <= 0 || g < 2 || g >= p) { + throw Decoding_Error("Invalid DL group generator"); + } +} + +void check_dl_group_params(const BigInt& p, const BigInt& q, const BigInt& g) { + check_dl_group_params(p, g); + if(q.signum() <= 0 || q.is_even() || q.bits() >= p.bits()) { + throw Decoding_Error("Invalid DL group subgroup order"); + } +} + +} // namespace + class DL_Group_Data final { public: + static std::shared_ptr create(const BigInt& p, + const BigInt& q, + const BigInt& g, + DL_Group_Source source) { + check_dl_group_params(p, q, g); + return std::make_shared(p, q, g, source); + } + + static std::shared_ptr create(const BigInt& p, const BigInt& g, DL_Group_Source source) { + check_dl_group_params(p, g); + return std::make_shared(p, g, source); + } + + // This constructor is public because C++ is terrible but all DL_Group_Data should + // be created via DL_Group_Data::create DL_Group_Data(const BigInt& p, const BigInt& q, const BigInt& g, DL_Group_Source source) : m_p(p), m_q(q), m_g(g), - m_mod_p(Modular_Reducer::for_public_modulus(p)), - m_mod_q(Modular_Reducer::for_public_modulus(q)), - m_monty_params(std::make_shared(m_p, m_mod_p)), + m_mod_p(Barrett_Reduction::for_public_modulus(p)), + m_mod_q(Barrett_Reduction::for_public_modulus(q)), + m_monty_params(m_p, m_mod_p), m_monty(monty_precompute(m_monty_params, m_g, /*window bits=*/4)), m_p_bits(p.bits()), m_q_bits(q.bits()), @@ -39,11 +74,13 @@ m_exponent_bits(dl_exponent_size(m_p_bits)), m_source(source) {} + // This constructor is public because C++ is terrible but all DL_Group_Data should + // be created via DL_Group_Data::create DL_Group_Data(const BigInt& p, const BigInt& g, DL_Group_Source source) : m_p(p), m_g(g), - m_mod_p(Modular_Reducer::for_public_modulus(p)), - m_monty_params(std::make_shared(m_p, m_mod_p)), + m_mod_p(Barrett_Reduction::for_public_modulus(p)), + m_monty_params(m_p, m_mod_p), m_monty(monty_precompute(m_monty_params, m_g, /*window bits=*/4)), m_p_bits(p.bits()), m_q_bits(0), @@ -64,14 +101,14 @@ const BigInt& g() const { return m_g; } - const Modular_Reducer& reducer_mod_p() const { return m_mod_p; } + const Barrett_Reduction& reducer_mod_p() const { return m_mod_p; } - const Modular_Reducer& reducer_mod_q() const { + const Barrett_Reduction& reducer_mod_q() const { BOTAN_STATE_CHECK(m_mod_q); return *m_mod_q; } - std::shared_ptr monty_params_p() const { return m_monty_params; } + const Montgomery_Params& monty_params_p() const { return m_monty_params; } size_t p_bits() const { return m_p_bits; } @@ -102,7 +139,7 @@ bool q_is_set() const { return m_q_bits > 0; } void assert_q_is_set(std::string_view function) const { - if(q_is_set() == false) { + if(!q_is_set()) { throw Invalid_State(fmt("DL_Group::{}: q is not set for this group", function)); } } @@ -113,10 +150,10 @@ BigInt m_p; BigInt m_q; // zero if no q set BigInt m_g; - Modular_Reducer m_mod_p; - std::optional m_mod_q; - std::shared_ptr m_monty_params; - std::shared_ptr m_monty; + Barrett_Reduction m_mod_p; + std::optional m_mod_q; + Montgomery_Params m_monty_params; + std::shared_ptr m_monty; size_t m_p_bits; size_t m_q_bits; size_t m_estimated_strength; @@ -125,25 +162,38 @@ }; //static -std::shared_ptr DL_Group::BER_decode_DL_group(const uint8_t data[], - size_t data_len, +std::shared_ptr DL_Group::DER_decode_DL_group(const std::span data, DL_Group_Format format, DL_Group_Source source) { - BER_Decoder decoder(data, data_len); - BER_Decoder ber = decoder.start_sequence(); + BER_Decoder decoder(data, BER_Decoder::Limits::DER()); + BER_Decoder inner = decoder.start_sequence(); if(format == DL_Group_Format::ANSI_X9_57) { - BigInt p, q, g; - ber.decode(p).decode(q).decode(g).verify_end(); - return std::make_shared(p, q, g, source); + /* + This format is p, q, g with no additional data following + */ + BigInt p; + BigInt q; + BigInt g; + inner.decode(p).decode(q).decode(g).verify_end(); + return DL_Group_Data::create(p, q, g, source); } else if(format == DL_Group_Format::ANSI_X9_42) { - BigInt p, g, q; - ber.decode(p).decode(g).decode(q).discard_remaining(); - return std::make_shared(p, q, g, source); + /* + This format is p, g, q with optional cofactor and seed following + */ + BigInt p; + BigInt g; + BigInt q; + inner.decode(p).decode(g).decode(q).discard_remaining(); + return DL_Group_Data::create(p, q, g, source); } else if(format == DL_Group_Format::PKCS_3) { - BigInt p, g; - ber.decode(p).decode(g).discard_remaining(); - return std::make_shared(p, g, source); + /* + This format is p, g followed by optional privateValueLength (recommended exponent size) + */ + BigInt p; + BigInt g; + inner.decode(p).decode(g).discard_remaining(); + return DL_Group_Data::create(p, g, source); } else { throw Invalid_Argument("Unknown DL_Group encoding"); } @@ -156,9 +206,9 @@ const BigInt g(g_str); if(q.is_zero()) { - return std::make_shared(p, g, DL_Group_Source::Builtin); + return DL_Group_Data::create(p, g, DL_Group_Source::Builtin); } else { - return std::make_shared(p, q, g, DL_Group_Source::Builtin); + return DL_Group_Data::create(p, q, g, DL_Group_Source::Builtin); } } @@ -168,7 +218,7 @@ const BigInt q = (p - 1) / 2; const BigInt g(g_str); - return std::make_shared(p, q, g, DL_Group_Source::Builtin); + return DL_Group_Data::create(p, q, g, DL_Group_Source::Builtin); } namespace { @@ -197,10 +247,10 @@ if(m_data == nullptr) { try { std::string label; - const std::vector ber = unlock(PEM_Code::decode(str, label)); - DL_Group_Format format = pem_label_to_dl_format(label); + const std::vector der = unlock(PEM_Code::decode(str, label)); + const DL_Group_Format format = pem_label_to_dl_format(label); - m_data = BER_decode_DL_group(ber.data(), ber.size(), format, DL_Group_Source::ExternalSource); + m_data = DER_decode_DL_group(der, format, DL_Group_Source::ExternalSource); } catch(...) {} } @@ -223,7 +273,7 @@ DL_Group DL_Group::from_PEM(std::string_view pem) { std::string label; const std::vector ber = unlock(PEM_Code::decode(pem, label)); - DL_Group_Format format = pem_label_to_dl_format(label); + const DL_Group_Format format = pem_label_to_dl_format(label); return DL_Group(ber, format); } @@ -233,16 +283,20 @@ * Create generator of the q-sized subgroup (DSA style generator) */ BigInt make_dsa_generator(const BigInt& p, const BigInt& q) { - BigInt e, r; + BigInt e; + BigInt r; vartime_divide(p - 1, q, e, r); if(e == 0 || r > 0) { throw Invalid_Argument("make_dsa_generator q does not divide p-1"); } + // TODO we compute these, then throw them away and recompute in DL_Group_Data + auto mod_p = Barrett_Reduction::for_public_modulus(p); + const Montgomery_Params params(p, mod_p); + for(size_t i = 0; i != PRIME_TABLE_SIZE; ++i) { - // TODO precompute! - BigInt g = power_mod(BigInt::from_word(PRIMES[i]), e, p); + BigInt g = monty_exp_vartime(params, BigInt::from_word(PRIMES[i]), e).value(); if(g > 1) { return g; } @@ -274,46 +328,45 @@ const BigInt q = (p - 1) / 2; /* - Always choose a generator that is quadratic reside mod p, - this forces g to be a generator of the subgroup of size q. + Always choose a generator that is quadratic reside mod p, this forces g to + be a generator of the subgroup of size q. + + We use 2 by default, but if 2 is not a quadratic reside then use 4 which + is always a quadratic reside, being the square of 2 (or p - 2) */ BigInt g = BigInt::from_word(2); if(jacobi(g, p) != 1) { - // prime table does not contain 2 - for(size_t i = 0; i < PRIME_TABLE_SIZE; ++i) { - g = BigInt::from_word(PRIMES[i]); - if(jacobi(g, p) == 1) { - break; - } - } + g = BigInt::from_word(4); } - m_data = std::make_shared(p, q, g, DL_Group_Source::RandomlyGenerated); + m_data = DL_Group_Data::create(p, q, g, DL_Group_Source::RandomlyGenerated); } else if(type == Prime_Subgroup) { if(qbits == 0) { qbits = dl_exponent_size(pbits); } const BigInt q = random_prime(rng, qbits); - auto mod_2q = Modular_Reducer::for_public_modulus(2 * q); + const BigInt q2 = q * 2; BigInt X; BigInt p; while(p.bits() != pbits || !is_prime(p, rng, 128, true)) { X.randomize(rng, pbits); - p = X - mod_2q.reduce(X) + 1; + // Variable time division is OK here since DH groups are public anyway + p = X - (X % q2) + 1; } const BigInt g = make_dsa_generator(p, q); - m_data = std::make_shared(p, q, g, DL_Group_Source::RandomlyGenerated); + m_data = DL_Group_Data::create(p, q, g, DL_Group_Source::RandomlyGenerated); } else if(type == DSA_Kosherizer) { if(qbits == 0) { qbits = ((pbits <= 1024) ? 160 : 256); } - BigInt p, q; + BigInt p; + BigInt q; generate_dsa_primes(rng, p, q, pbits, qbits); const BigInt g = make_dsa_generator(p, q); - m_data = std::make_shared(p, q, g, DL_Group_Source::RandomlyGenerated); + m_data = DL_Group_Data::create(p, q, g, DL_Group_Source::RandomlyGenerated); } else { throw Invalid_Argument("DL_Group unknown PrimeType"); } @@ -323,22 +376,23 @@ * DL_Group Constructor */ DL_Group::DL_Group(RandomNumberGenerator& rng, const std::vector& seed, size_t pbits, size_t qbits) { - BigInt p, q; + BigInt p; + BigInt q; if(!generate_dsa_primes(rng, p, q, pbits, qbits, seed)) { throw Invalid_Argument("DL_Group: The seed given does not generate a DSA group"); } - BigInt g = make_dsa_generator(p, q); + const BigInt g = make_dsa_generator(p, q); - m_data = std::make_shared(p, q, g, DL_Group_Source::RandomlyGenerated); + m_data = DL_Group_Data::create(p, q, g, DL_Group_Source::RandomlyGenerated); } /* * DL_Group Constructor */ DL_Group::DL_Group(const BigInt& p, const BigInt& g) { - m_data = std::make_shared(p, g, DL_Group_Source::ExternalSource); + m_data = DL_Group_Data::create(p, g, DL_Group_Source::ExternalSource); } /* @@ -346,9 +400,9 @@ */ DL_Group::DL_Group(const BigInt& p, const BigInt& q, const BigInt& g) { if(q.is_zero()) { - m_data = std::make_shared(p, g, DL_Group_Source::ExternalSource); + m_data = DL_Group_Data::create(p, g, DL_Group_Source::ExternalSource); } else { - m_data = std::make_shared(p, q, g, DL_Group_Source::ExternalSource); + m_data = DL_Group_Data::create(p, q, g, DL_Group_Source::ExternalSource); } } @@ -368,7 +422,7 @@ return false; } - if(q.is_zero() == false) { + if(!q.is_zero()) { if(data().power_b_p_vartime(y, q) != 1) { return false; } @@ -480,7 +534,7 @@ return data().q(); } -std::shared_ptr DL_Group::monty_params_p() const { +const Montgomery_Params& DL_Group::_monty_params_p() const { return data().monty_params_p(); } @@ -527,7 +581,7 @@ return data().reducer_mod_p().multiply(x, y); } -const Modular_Reducer& DL_Group::_reducer_mod_p() const { +const Barrett_Reduction& DL_Group::_reducer_mod_p() const { return data().reducer_mod_p(); } @@ -618,8 +672,8 @@ } } -DL_Group::DL_Group(const uint8_t ber[], size_t ber_len, DL_Group_Format format) { - m_data = BER_decode_DL_group(ber, ber_len, format, DL_Group_Source::ExternalSource); +DL_Group::DL_Group(std::span der, DL_Group_Format format) { + m_data = DER_decode_DL_group(der, format, DL_Group_Source::ExternalSource); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dl_group/dl_group.h botan3-3.12.0+dfsg/src/lib/pubkey/dl_group/dl_group.h --- botan3-3.7.1+dfsg/src/lib/pubkey/dl_group/dl_group.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dl_group/dl_group.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,15 +9,16 @@ #define BOTAN_DL_PARAM_H_ #include +#include #include namespace Botan { -class Modular_Reducer; +class Barrett_Reduction; class Montgomery_Params; class DL_Group_Data; -enum class DL_Group_Source { +enum class DL_Group_Source : uint8_t { Builtin, RandomlyGenerated, ExternalSource, @@ -26,10 +27,10 @@ /** * The DL group encoding format variants. */ -enum class DL_Group_Format { - ANSI_X9_42, - ANSI_X9_57, - PKCS_3, +enum class DL_Group_Format : uint8_t { + ANSI_X9_42 = 0, + ANSI_X9_57 = 1, + PKCS_3 = 2, DSA_PARAMETERS = ANSI_X9_57, DH_PARAMETERS = ANSI_X9_42, @@ -47,7 +48,7 @@ /** * Determine the prime creation for DL groups. */ - enum PrimeType { Strong, Prime_Subgroup, DSA_Kosherizer }; + enum PrimeType : uint8_t /* NOLINT(*-use-enum-class) */ { Strong, Prime_Subgroup, DSA_Kosherizer }; using Format = DL_Group_Format; @@ -125,16 +126,14 @@ DL_Group(const BigInt& p, const BigInt& q, const BigInt& g); /** - * Decode a BER-encoded DL group param + * Decode a DER-encoded DL group param */ - DL_Group(const uint8_t ber[], size_t ber_len, DL_Group_Format format); + DL_Group(const uint8_t der[], size_t der_len, DL_Group_Format format) : DL_Group({der, der_len}, format) {} /** - * Decode a BER-encoded DL group param + * Decode a DER-encoded DL group param */ - template - DL_Group(const std::vector& ber, DL_Group_Format format) : - DL_Group(ber.data(), ber.size(), format) {} + DL_Group(std::span der, DL_Group_Format format); /** * Get the prime p. @@ -294,15 +293,12 @@ /** * Multi-exponentiate * Return (g^x * y^z) % p + * + * @warning this function is variable time and should not be used with secret inputs */ BigInt multi_exponentiate(const BigInt& x, const BigInt& y, const BigInt& z) const; /** - * Return parameters for Montgomery reduction/exponentiation mod p - */ - std::shared_ptr monty_params_p() const; - - /** * Return the size of p in bits * Same as get_p().bits() */ @@ -354,15 +350,15 @@ size_t estimated_strength() const; /** - * Decode a DER/BER encoded group into this instance. - * @param ber a vector containing the DER/BER encoded group + * Decode a DER encoded group into this instance. + * @param der a vector containing the DER encoded group * @param format the format of the encoded group * * @warning avoid this. Instead use the DL_Group constructor */ - BOTAN_DEPRECATED("Use DL_Group constructor taking BER encoding") - void BER_decode(const std::vector& ber, DL_Group_Format format) { - *this = DL_Group(ber, format); + BOTAN_DEPRECATED("Use DL_Group constructor taking DER encoding") + void BER_decode(const std::vector& der, DL_Group_Format format) { + *this = DL_Group(der, format); } DL_Group_Source source() const; @@ -373,20 +369,26 @@ */ static std::shared_ptr DL_group_info(std::string_view name); + /** + * Return parameters for Montgomery reduction/exponentiation mod p + * + * For internal use only + */ + const Montgomery_Params& _monty_params_p() const; + /* * For internal use only */ - const Modular_Reducer& _reducer_mod_p() const; + const Barrett_Reduction& _reducer_mod_p() const; private: - DL_Group(std::shared_ptr data) : m_data(std::move(data)) {} + explicit DL_Group(std::shared_ptr data) : m_data(std::move(data)) {} static std::shared_ptr load_DL_group_info(const char* p_str, const char* q_str, const char* g_str); static std::shared_ptr load_DL_group_info(const char* p_str, const char* g_str); - static std::shared_ptr BER_decode_DL_group(const uint8_t data[], - size_t data_len, + static std::shared_ptr DER_decode_DL_group(std::span data, DL_Group_Format format, DL_Group_Source source); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dlies/dlies.cpp botan3-3.12.0+dfsg/src/lib/pubkey/dlies/dlies.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/dlies/dlies.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dlies/dlies.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,9 +7,10 @@ */ #include +#include +#include #include -#include -#include +#include namespace Botan { @@ -27,15 +28,14 @@ size_t cipher_key_len, std::unique_ptr mac, size_t mac_key_length) : - m_other_pub_key(), + m_own_pub_key(own_priv_key.public_value()), m_ka(own_priv_key, rng, "Raw"), m_kdf(std::move(kdf)), m_cipher(std::move(cipher)), m_cipher_key_len(cipher_key_len), m_mac(std::move(mac)), - m_mac_keylen(mac_key_length), - m_iv() { + m_mac_keylen(mac_key_length) { BOTAN_ASSERT_NONNULL(m_kdf); BOTAN_ASSERT_NONNULL(m_mac); } @@ -60,7 +60,7 @@ const size_t cipher_key_len = m_cipher ? m_cipher_key_len : length; if(m_cipher) { - SymmetricKey enc_key(secret_keys.data(), cipher_key_len); + const SymmetricKey enc_key(secret_keys.data(), cipher_key_len); m_cipher->set_key(enc_key); if(m_iv.empty() && !m_cipher->valid_nonce_length(m_iv.size())) { @@ -106,8 +106,7 @@ m_cipher(std::move(cipher)), m_cipher_key_len(cipher_key_len), m_mac(std::move(mac)), - m_mac_keylen(mac_key_length), - m_iv() { + m_mac_keylen(mac_key_length) { BOTAN_ASSERT_NONNULL(m_kdf); BOTAN_ASSERT_NONNULL(m_mac); } @@ -137,7 +136,7 @@ const SymmetricKey secret_value = m_ka.derive_key(0, other_pub_key); const size_t ciphertext_len = length - m_pub_key_size - m_mac->output_length(); - size_t cipher_key_len = m_cipher ? m_cipher_key_len : ciphertext_len; + const size_t cipher_key_len = m_cipher ? m_cipher_key_len : ciphertext_len; // derive secret key from secret value const size_t required_key_length = cipher_key_len + m_mac_keylen; @@ -154,15 +153,15 @@ secure_vector calculated_tag = m_mac->process(ciphertext); // calculated tag == received tag ? - secure_vector tag(msg + m_pub_key_size + ciphertext_len, - msg + m_pub_key_size + ciphertext_len + m_mac->output_length()); - valid_mask = CT::is_equal(tag.data(), calculated_tag.data(), tag.size()).value(); + const std::span tag(msg + m_pub_key_size + ciphertext_len, m_mac->output_length()); + + valid_mask = CT::is_equal(tag, calculated_tag).value(); // decrypt if(m_cipher) { - if(valid_mask) { - SymmetricKey dec_key(secret_keys.data(), cipher_key_len); + if(valid_mask == 0xFF) { + const SymmetricKey dec_key(secret_keys.data(), cipher_key_len); m_cipher->set_key(dec_key); try { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dlies/dlies.h botan3-3.12.0+dfsg/src/lib/pubkey/dlies/dlies.h --- botan3-3.7.1+dfsg/src/lib/pubkey/dlies/dlies.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dlies/dlies.h 2026-05-07 01:38:28.000000000 +0000 @@ -69,7 +69,7 @@ inline void set_initialization_vector(const InitializationVector& iv) { m_iv = iv; } private: - std::vector enc(const uint8_t[], size_t, RandomNumberGenerator&) const override; + std::vector enc(const uint8_t in[], size_t length, RandomNumberGenerator& rng) const override; size_t maximum_input_size() const override; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dsa/dsa.cpp botan3-3.12.0+dfsg/src/lib/pubkey/dsa/dsa.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/dsa/dsa.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dsa/dsa.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,8 @@ #include +#include +#include #include #include #include @@ -125,10 +127,10 @@ class DSA_Signature_Operation final : public PK_Ops::Signature_with_Hash { public: DSA_Signature_Operation(const std::shared_ptr& key, - std::string_view emsa, + std::string_view hash_fn, RandomNumberGenerator& rng) : - PK_Ops::Signature_with_Hash(emsa), m_key(key) { - m_b = BigInt::random_integer(rng, 2, m_key->group().get_q()); + PK_Ops::Signature_with_Hash(hash_fn), m_key(key) { + m_b = BigInt::random_integer(rng, BigInt::from_s32(2), m_key->group().get_q()); m_b_inv = m_key->group().inverse_mod_q(m_b); } @@ -166,7 +168,7 @@ const BigInt k = BigInt::random_integer(rng, 1, q); #endif - const BigInt k_inv = group.inverse_mod_q(group.mod_q(m_b * k)) * m_b; + const BigInt k_inv = group.multiply_mod_q(group.inverse_mod_q(group.mod_q(m_b * k)), m_b); /* * It may not be strictly necessary for the reduction (g^k mod p) mod q to be @@ -195,7 +197,12 @@ throw Internal_Error("Computed zero r/s during DSA signature"); } - return unlock(BigInt::encode_fixed_length_int_pair(r, s, q.bytes())); + const size_t q_bytes = q.bytes(); + std::vector sig(2 * q_bytes); + BufferStuffer stuffer(sig); + r.serialize_to(stuffer.next(q_bytes)); + s.serialize_to(stuffer.next(q_bytes)); + return sig; } /** @@ -203,8 +210,8 @@ */ class DSA_Verification_Operation final : public PK_Ops::Verification_with_Hash { public: - DSA_Verification_Operation(const std::shared_ptr& key, std::string_view emsa) : - PK_Ops::Verification_with_Hash(emsa), m_key(key) {} + DSA_Verification_Operation(const std::shared_ptr& key, std::string_view hash_fn) : + PK_Ops::Verification_with_Hash(hash_fn), m_key(key) {} DSA_Verification_Operation(const std::shared_ptr& key, const AlgorithmIdentifier& alg_id) : PK_Ops::Verification_with_Hash(alg_id, "DSA"), m_key(key) {} @@ -225,7 +232,7 @@ return false; } - BigInt r(sig.first(q_bytes)); + const BigInt r(sig.first(q_bytes)); BigInt s(sig.last(q_bytes)); if(r == 0 || r >= q || s == 0 || s >= q) { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/dsa/dsa.h botan3-3.12.0+dfsg/src/lib/pubkey/dsa/dsa.h --- botan3-3.7.1+dfsg/src/lib/pubkey/dsa/dsa.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/dsa/dsa.h 2026-05-07 01:38:28.000000000 +0000 @@ -70,7 +70,7 @@ DSA_PublicKey() = default; - DSA_PublicKey(std::shared_ptr key) : m_public_key(std::move(key)) {} + explicit DSA_PublicKey(std::shared_ptr key) : m_public_key(std::move(key)) {} std::shared_ptr m_public_key; }; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_apoint.cpp botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_apoint.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_apoint.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_apoint.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #include #include #include +#include namespace Botan { @@ -82,18 +83,18 @@ EC_AffinePoint EC_AffinePoint::generator(const EC_Group& group) { // TODO it would be nice to improve this (pcurves supports returning generator directly) - try { - return EC_AffinePoint::from_bigint_xy(group, group.get_g_x(), group.get_g_y()).value(); - } catch(...) { + if(auto g = EC_AffinePoint::from_bigint_xy(group, group.get_g_x(), group.get_g_y())) { + return *g; + } else { throw Internal_Error("EC_AffinePoint::generator curve rejected generator"); } } std::optional EC_AffinePoint::from_bigint_xy(const EC_Group& group, const BigInt& x, const BigInt& y) { - if(x.is_negative() || x >= group.get_p()) { + if(x.signum() < 0 || x >= group.get_p()) { return {}; } - if(y.is_negative() || y >= group.get_p()) { + if(y.signum() < 0 || y >= group.get_p()) { return {}; } @@ -122,6 +123,13 @@ return EC_AffinePoint(std::move(pt)); } +EC_AffinePoint EC_AffinePoint::hash_to_curve_ro(const EC_Group& group, + std::string_view hash_fn, + std::span input, + std::string_view domain_sep) { + return EC_AffinePoint::hash_to_curve_ro(group, hash_fn, input, as_span_of_bytes(domain_sep)); +} + EC_AffinePoint EC_AffinePoint::hash_to_curve_nu(const EC_Group& group, std::string_view hash_fn, std::span input, @@ -130,6 +138,13 @@ return EC_AffinePoint(std::move(pt)); } +EC_AffinePoint EC_AffinePoint::hash_to_curve_nu(const EC_Group& group, + std::string_view hash_fn, + std::span input, + std::string_view domain_sep) { + return EC_AffinePoint::hash_to_curve_nu(group, hash_fn, input, as_span_of_bytes(domain_sep)); +} + EC_AffinePoint::~EC_AffinePoint() = default; std::optional EC_AffinePoint::deserialize(const EC_Group& group, std::span bytes) { @@ -140,19 +155,17 @@ } } -EC_AffinePoint EC_AffinePoint::g_mul(const EC_Scalar& scalar, RandomNumberGenerator& rng, std::vector& ws) { - auto pt = scalar._inner().group()->point_g_mul(scalar.inner(), rng, ws); +EC_AffinePoint EC_AffinePoint::g_mul(const EC_Scalar& scalar, RandomNumberGenerator& rng) { + auto pt = scalar._inner().group()->point_g_mul(scalar.inner(), rng); return EC_AffinePoint(std::move(pt)); } -EC_AffinePoint EC_AffinePoint::mul(const EC_Scalar& scalar, RandomNumberGenerator& rng, std::vector& ws) const { - return EC_AffinePoint(inner().mul(scalar._inner(), rng, ws)); +EC_AffinePoint EC_AffinePoint::mul(const EC_Scalar& scalar, RandomNumberGenerator& rng) const { + return EC_AffinePoint(inner().mul(scalar._inner(), rng)); } -secure_vector EC_AffinePoint::mul_x_only(const EC_Scalar& scalar, - RandomNumberGenerator& rng, - std::vector& ws) const { - return inner().mul_x_only(scalar._inner(), rng, ws); +secure_vector EC_AffinePoint::mul_x_only(const EC_Scalar& scalar, RandomNumberGenerator& rng) const { + return inner().mul_x_only(scalar._inner(), rng); } std::optional EC_AffinePoint::mul_px_qy(const EC_AffinePoint& p, diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_apoint.h botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_apoint.h --- botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_apoint.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_apoint.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -32,7 +33,7 @@ /// Elliptic Curve Point in Affine Representation /// -class BOTAN_UNSTABLE_API EC_AffinePoint final { +class BOTAN_PUBLIC_API(3, 6) EC_AffinePoint final { public: /// Point deserialization. Throws if wrong length or not a valid point /// @@ -51,9 +52,7 @@ static std::optional from_bigint_xy(const EC_Group& group, const BigInt& x, const BigInt& y); /// Multiply by the group generator returning a complete point - /// - /// Workspace argument is transitional - static EC_AffinePoint g_mul(const EC_Scalar& scalar, RandomNumberGenerator& rng, std::vector& ws); + static EC_AffinePoint g_mul(const EC_Scalar& scalar, RandomNumberGenerator& rng); /// Return the identity element static EC_AffinePoint identity(const EC_Group& group); @@ -69,6 +68,14 @@ std::span input, std::span domain_sep); + /// Hash to curve (RFC 9380), random oracle variant + /// + /// Only supported for specific groups + static EC_AffinePoint hash_to_curve_ro(const EC_Group& group, + std::string_view hash_fn, + std::span input, + std::string_view domain_sep); + /// Hash to curve (RFC 9380), non uniform variant /// /// Only supported for specific groups @@ -77,17 +84,19 @@ std::span input, std::span domain_sep); - /// Multiply a point by a scalar returning a complete point + /// Hash to curve (RFC 9380), non uniform variant /// - /// Workspace argument is transitional - EC_AffinePoint mul(const EC_Scalar& scalar, RandomNumberGenerator& rng, std::vector& ws) const; + /// Only supported for specific groups + static EC_AffinePoint hash_to_curve_nu(const EC_Group& group, + std::string_view hash_fn, + std::span input, + std::string_view domain_sep); + + /// Multiply a point by a scalar returning a complete point + EC_AffinePoint mul(const EC_Scalar& scalar, RandomNumberGenerator& rng) const; /// Multiply a point by a scalar, returning the byte encoding of the x coordinate only - /// - /// Workspace argument is transitional - secure_vector mul_x_only(const EC_Scalar& scalar, - RandomNumberGenerator& rng, - std::vector& ws) const; + secure_vector mul_x_only(const EC_Scalar& scalar, RandomNumberGenerator& rng) const; /// Compute 2-ary multiscalar multiplication - p*x + q*y /// @@ -107,7 +116,7 @@ /// field inversion. This can be sufficient when implementing protocols /// that just need to perform a few additions. /// - /// In the future a cooresponding EC_ProjectivePoint type may be added + /// In the future a corresponding EC_ProjectivePoint type may be added /// which would avoid the expensive affine conversions EC_AffinePoint add(const EC_AffinePoint& q) const; @@ -232,6 +241,23 @@ EC_Point to_legacy_point() const; #endif + BOTAN_DEPRECATED("Use version without workspace arg") + static EC_AffinePoint g_mul(const EC_Scalar& scalar, RandomNumberGenerator& rng, std::vector& /*ws*/) { + return EC_AffinePoint::g_mul(scalar, rng); + } + + BOTAN_DEPRECATED("Use version without workspace arg") + EC_AffinePoint mul(const EC_Scalar& scalar, RandomNumberGenerator& rng, std::vector& /*ws*/) const { + return this->mul(scalar, rng); + } + + /// Multiply a point by a scalar, returning the byte encoding of the x coordinate only + secure_vector mul_x_only(const EC_Scalar& scalar, + RandomNumberGenerator& rng, + std::vector& /*ws*/) const { + return this->mul_x_only(scalar, rng); + } + ~EC_AffinePoint(); const EC_AffinePoint_Data& _inner() const { return inner(); } @@ -243,7 +269,7 @@ private: friend class EC_Mul2Table; - EC_AffinePoint(std::unique_ptr point); + explicit EC_AffinePoint(std::unique_ptr point); const EC_AffinePoint_Data& inner() const { return *m_point; } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_group.cpp botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_group.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_group.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_group.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -15,8 +15,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -29,14 +29,30 @@ EC_Group_Data_Map() = default; size_t clear() { - lock_guard_type lock(m_mutex); - size_t count = m_registered_curves.size(); + const lock_guard_type lock(m_mutex); + const size_t count = m_registered_curves.size(); m_registered_curves.clear(); return count; } + bool unregister(const OID& oid) { + // TODO(Botan4) + if(oid.empty()) { + throw Invalid_Argument("OID must not be empty"); + } + + const lock_guard_type lock(m_mutex); + for(size_t i = 0; i < m_registered_curves.size(); i++) { + if(m_registered_curves[i]->oid() == oid) { + m_registered_curves.erase(m_registered_curves.begin() + i); + return true; + } + } + return false; + } + std::shared_ptr lookup(const OID& oid) { - lock_guard_type lock(m_mutex); + const lock_guard_type lock(m_mutex); for(auto i : m_registered_curves) { if(i->oid() == oid) { @@ -48,13 +64,6 @@ std::shared_ptr data = EC_Group::EC_group_info(oid); if(data) { - for(auto curve : m_registered_curves) { - if(curve->oid().empty() == true && curve->params_match(*data)) { - curve->set_oid(oid); - return curve; - } - } - m_registered_curves.push_back(data); return data; } @@ -72,97 +81,139 @@ const BigInt& cofactor, const OID& oid, EC_Group_Source source) { - lock_guard_type lock(m_mutex); - - for(auto i : m_registered_curves) { - /* - * The params may be the same but you are trying to register under a - * different OID than the one we are using, so using a different - * group, since EC_Group's model assumes a single OID per group. - */ - if(!oid.empty() && !i->oid().empty() && i->oid() != oid) { - continue; - } + BOTAN_ASSERT_NOMSG(oid.has_value()); - const bool same_oid = !oid.empty() && i->oid() == oid; - const bool same_params = i->params_match(p, a, b, g_x, g_y, order, cofactor); + const lock_guard_type lock(m_mutex); - /* - * If the params and OID are the same then we are done, just return - * the already registered curve obj. - */ - if(same_params && same_oid) { - return i; - } + for(auto i : m_registered_curves) { + if(i->oid() == oid) { + /* + * If both OID and params are the same then we are done, just return + * the already registered curve obj. + * + * First verify that the params match, to catch an application + * that is attempting to register a EC_Group under the same OID as + * another group currently in use + */ + if(!i->params_match(p, a, b, g_x, g_y, order, cofactor)) { + throw Invalid_Argument("Attempting to register a curve using OID " + oid.to_string() + + " but a distinct curve is already registered using that OID"); + } - /* - * If same params and the new OID is empty, then that's ok too - */ - if(same_params && oid.empty()) { return i; } /* - * Check for someone trying to reuse an already in-use OID - */ - if(same_oid && !same_params) { - throw Invalid_Argument("Attempting to register a curve using OID " + oid.to_string() + - " but a distinct curve is already registered using that OID"); - } - - /* * If the same curve was previously created without an OID but is now * being registered again using an OID, save that OID. + * + * TODO(Botan4) remove this block; this situation won't be possible since + * we will require all groups to have an OID */ - if(same_params && i->oid().empty() && !oid.empty()) { + if(i->oid().empty() && i->params_match(p, a, b, g_x, g_y, order, cofactor)) { i->set_oid(oid); return i; } } /* - Not found in current list, so we need to create a new entry - - If an OID is set, try to look up relative our static tables to detect a duplicate - registration under an OID + * Not found in current list, so we need to create a new entry */ - - auto new_group = EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, oid, source); - - if(oid.has_value()) { - std::shared_ptr data = EC_Group::EC_group_info(oid); - if(data != nullptr && !new_group->params_match(*data)) { - throw Invalid_Argument("Attempting to register an EC group under OID of hardcoded group"); - } - } else { - // Here try to use the order as a hint to look up the group id, to identify common groups - const OID oid_from_store = EC_Group::EC_group_identity_from_order(order); - if(oid_from_store.has_value()) { - std::shared_ptr data = EC_Group::EC_group_info(oid_from_store); - + auto new_group = [&] { + if(auto g = EC_Group::EC_group_info(oid); g != nullptr) { /* - If EC_group_identity_from_order returned an OID then looking up that OID - must always return a result. + * This turned out to be the OID of one of the builtin groups. Verify + * that all of the provided parameters match that builtin group. */ - BOTAN_ASSERT_NOMSG(data != nullptr); + BOTAN_ARG_CHECK(g->params_match(p, a, b, g_x, g_y, order, cofactor), + "Attempting to register an EC group under OID of hardcoded group"); + return g; + } else { /* - It is possible (if unlikely) that someone is registering another group - that happens to have an order equal to that of a well known group - - so verify all values before assigning the OID. + * This path is taken for an application registering a new EC_Group with an OID specified */ - if(new_group->params_match(*data)) { - new_group->set_oid(oid_from_store); - } + return EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, oid, source); + } + }(); + + m_registered_curves.push_back(new_group); + return new_group; + } + + std::shared_ptr lookup_from_params(const BigInt& p, + const BigInt& a, + const BigInt& b, + std::span base_pt, + const BigInt& order, + const BigInt& cofactor) { + const lock_guard_type lock(m_mutex); + + for(auto i : m_registered_curves) { + if(i->params_match(p, a, b, base_pt, order, cofactor)) { + return i; + } + } + + // Try to use the order as a hint to look up the group id + const OID oid_from_order = EC_Group::EC_group_identity_from_order(order); + if(oid_from_order.has_value()) { + auto new_group = EC_Group::EC_group_info(oid_from_order); + + // Have to check all params in the (unlikely/malicious) event of an order collision + if(new_group && new_group->params_match(p, a, b, base_pt, order, cofactor)) { + m_registered_curves.push_back(new_group); + return new_group; + } + } + + return {}; + } + + // TODO(Botan4) this entire function can be removed since OIDs will be required + std::shared_ptr lookup_or_create_without_oid(const BigInt& p, + const BigInt& a, + const BigInt& b, + const BigInt& g_x, + const BigInt& g_y, + const BigInt& order, + const BigInt& cofactor, + EC_Group_Source source) { + const lock_guard_type lock(m_mutex); + + for(auto i : m_registered_curves) { + if(i->params_match(p, a, b, g_x, g_y, order, cofactor)) { + return i; } } + // Try to use the order as a hint to look up the group id + const OID oid_from_order = EC_Group::EC_group_identity_from_order(order); + if(oid_from_order.has_value()) { + auto new_group = EC_Group::EC_group_info(oid_from_order); + + // Have to check all params in the (unlikely/malicious) event of an order collision + if(new_group && new_group->params_match(p, a, b, g_x, g_y, order, cofactor)) { + m_registered_curves.push_back(new_group); + return new_group; + } + } + + /* + * At this point we have failed to identify the group; it is not any of + * the builtin values, nor is it a group that the user had previously + * registered explicitly. We create the group data without an OID. + * + * TODO(Botan4) remove this; throw an exception instead + */ + auto new_group = EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, OID(), source); m_registered_curves.push_back(new_group); return new_group; } private: mutex_type m_mutex; + // TODO(Botan4): Once OID is required we could make this into a map std::vector> m_registered_curves; }; @@ -173,7 +224,7 @@ * which ensures that its destructor runs after ~g_ec_data is complete. */ - static Allocator_Initializer g_init_allocator; + static const Allocator_Initializer g_init_allocator; static EC_Group_Data_Map g_ec_data; return g_ec_data; } @@ -191,6 +242,8 @@ const char* g_y_str, const char* order_str, const OID& oid) { + BOTAN_ARG_CHECK(oid.has_value(), "EC_Group::load_EC_group_info OID must be set"); + const BigInt p(p_str); const BigInt a(a_str); const BigInt b(b_str); @@ -203,15 +256,15 @@ } //static -std::pair, bool> EC_Group::BER_decode_EC_group(std::span bits, +std::pair, bool> EC_Group::DER_decode_EC_group(std::span der, EC_Group_Source source) { - BER_Decoder ber(bits); + BER_Decoder dec(der, BER_Decoder::Limits::DER()); - auto next_obj_type = ber.peek_next_object().type_tag(); + auto next_obj_type = dec.peek_next_object().type_tag(); if(next_obj_type == ASN1_Type::ObjectId) { OID oid; - ber.decode(oid); + dec.decode(oid); auto data = ec_group_data().lookup(oid); if(!data) { @@ -220,20 +273,24 @@ return std::make_pair(data, false); } else if(next_obj_type == ASN1_Type::Sequence) { - BigInt p, a, b, order, cofactor; + BigInt p; + BigInt a; + BigInt b; + BigInt order; + BigInt cofactor; std::vector base_pt; std::vector seed; - ber.start_sequence() + dec.start_sequence() .decode_and_check(1, "Unknown ECC param version code") .start_sequence() - .decode_and_check(OID("1.2.840.10045.1.1"), "Only prime ECC fields supported") + .decode_and_check(OID({1, 2, 840, 10045, 1, 1}), "Only prime ECC fields supported") .decode(p) .end_cons() .start_sequence() .decode_octet_string_bigint(a) .decode_octet_string_bigint(b) - .decode_optional_string(seed, ASN1_Type::BitString, ASN1_Type::BitString) + .decode_optional_string(seed, ASN1_Type::BitString, ASN1_Type::BitString, ASN1_Class::Universal) .end_cons() .decode(base_pt, ASN1_Type::OctetString) .decode(order) @@ -241,34 +298,47 @@ .end_cons() .verify_end(); - if(p.bits() < 112 || p.bits() > 521 || p.is_negative()) { - throw Decoding_Error("ECC p parameter is invalid size"); + // TODO(Botan4) Require cofactor == 1 + if(cofactor <= 0 || cofactor >= 16) { + throw Decoding_Error("Invalid ECC cofactor parameter"); } - auto mod_p = Modular_Reducer::for_public_modulus(p); - if(!is_bailie_psw_probable_prime(p, mod_p)) { - throw Decoding_Error("ECC p parameter is not a prime"); + if(p.bits() < 112 || p.bits() > 521 || p.signum() < 0) { + throw Decoding_Error("ECC p parameter is invalid size"); } - if(a.is_negative() || a >= p) { + // A can be zero + if(a.signum() < 0 || a >= p) { throw Decoding_Error("Invalid ECC a parameter"); } - if(b <= 0 || b >= p) { + // B must be > 0 + if(b.signum() <= 0 || b >= p) { throw Decoding_Error("Invalid ECC b parameter"); } - if(order.is_negative() || order.is_zero() || order >= 2 * p) { + if(order.signum() <= 0 || order >= 2 * p) { throw Decoding_Error("Invalid ECC group order"); } - auto mod_order = Modular_Reducer::for_public_modulus(order); - if(!is_bailie_psw_probable_prime(order, mod_order)) { - throw Decoding_Error("Invalid ECC order parameter"); + if(auto data = ec_group_data().lookup_from_params(p, a, b, base_pt, order, cofactor)) { + return std::make_pair(data, true); } - if(cofactor <= 0 || cofactor >= 16) { - throw Decoding_Error("Invalid ECC cofactor parameter"); + /* + TODO(Botan4) the remaining code is used only to handle the case of decoding an EC_Group + which is neither a builtin group nor a group that was registered by the application. + It can all be removed and replaced with a throw + */ + + auto mod_p = Barrett_Reduction::for_public_modulus(p); + if(!is_bailie_psw_probable_prime(p, mod_p)) { + throw Decoding_Error("ECC p parameter is not a prime"); + } + + auto mod_order = Barrett_Reduction::for_public_modulus(order); + if(!is_bailie_psw_probable_prime(order, mod_order)) { + throw Decoding_Error("Invalid ECC order parameter"); } const size_t p_bytes = p.bytes(); @@ -280,14 +350,15 @@ const uint8_t hdr = base_pt[0]; if(hdr == 0x04 && base_pt.size() == 1 + 2 * p_bytes) { - BigInt x = BigInt::decode(&base_pt[1], p_bytes); - BigInt y = BigInt::decode(&base_pt[p_bytes + 1], p_bytes); + const BigInt x = BigInt::from_bytes(std::span{base_pt}.subspan(1, p_bytes)); + const BigInt y = BigInt::from_bytes(std::span{base_pt}.subspan(1 + p_bytes, p_bytes)); if(x < p && y < p) { return std::make_pair(x, y); } } else if((hdr == 0x02 || hdr == 0x03) && base_pt.size() == 1 + p_bytes) { - BigInt x = BigInt::decode(&base_pt[1], p_bytes); + // TODO(Botan4) remove this branch; we won't support compressed points + const BigInt x = BigInt::from_bytes(std::span{base_pt}.subspan(1, p_bytes)); BigInt y = sqrt_modulo_prime(((x * x + a) * x + b) % p, p); if(x < p && y >= 0) { @@ -303,13 +374,20 @@ throw Decoding_Error("Invalid ECC base point encoding"); }(); + // TODO(Botan4) we can remove this check since we'll only accept pre-registered groups auto y2 = mod_p.square(g_y); auto x3_ax_b = mod_p.reduce(mod_p.cube(g_x) + mod_p.multiply(a, g_x) + b); if(y2 != x3_ax_b) { throw Decoding_Error("Invalid ECC base point"); } - auto data = ec_group_data().lookup_or_create(p, a, b, g_x, g_y, order, cofactor, OID(), source); + /* + * Create the group data without registering it in the global map. + * + * Applications that need persistent custom groups should register them + * via the relevant EC_Group constructor + */ + auto data = EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, OID(), source); return std::make_pair(data, true); } else if(next_obj_type == ASN1_Type::Null) { throw Decoding_Error("Decoding ImplicitCA ECC parameters is not supported"); @@ -331,6 +409,33 @@ EC_Group::EC_Group(std::shared_ptr&& data) : m_data(std::move(data)) {} //static +bool EC_Group::supports_named_group(std::string_view name) { + if(name.empty()) { + return false; + } + + // Is it one of the groups compiled into the library? + if(EC_Group::known_named_groups().contains(std::string(name))) { + return true; + } + + // Is it a custom group registered by the application? + if(auto oid = OID::from_name(name)) { + try { + if(ec_group_data().lookup(oid.value()) != nullptr) { + return true; + } + } catch(Not_Implemented&) { + // This would be thrown for example if the group is a known curve + // but the relevant module that enables it is not compiled in + } + } + + // Not known + return false; +} + +//static bool EC_Group::supports_application_specific_group() { #if defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC) return true; @@ -340,6 +445,15 @@ } //static +bool EC_Group::supports_application_specific_group_with_cofactor() { +#if defined(BOTAN_HAS_LEGACY_EC_POINT) + return true; +#else + return false; +#endif +} + +//static EC_Group EC_Group::from_OID(const OID& oid) { auto data = ec_group_data().lookup(oid); @@ -378,11 +492,11 @@ } catch(...) {} if(m_data == nullptr) { - if(str.size() > 30 && str.substr(0, 29) == "-----BEGIN EC PARAMETERS-----") { + if(str.size() > 30 && str.starts_with("-----BEGIN EC PARAMETERS-----")) { // OK try it as PEM ... - const auto ber = PEM_Code::decode_check_label(str, "EC PARAMETERS"); + const auto der = PEM_Code::decode_check_label(str, "EC PARAMETERS"); - auto data = BER_decode_EC_group(ber, EC_Group_Source::ExternalSource); + auto data = DER_decode_EC_group(der, EC_Group_Source::ExternalSource); this->m_data = data.first; this->m_explicit_encoding = data.second; } @@ -395,8 +509,8 @@ //static EC_Group EC_Group::from_PEM(std::string_view pem) { - const auto ber = PEM_Code::decode_check_label(pem, "EC PARAMETERS"); - return EC_Group(ber); + const auto der = PEM_Code::decode_check_label(pem, "EC PARAMETERS"); + return EC_Group(der); } EC_Group::EC_Group(const BigInt& p, @@ -407,8 +521,13 @@ const BigInt& order, const BigInt& cofactor, const OID& oid) { - m_data = - ec_group_data().lookup_or_create(p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource); + if(oid.has_value()) { + m_data = ec_group_data().lookup_or_create( + p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource); + } else { + m_data = ec_group_data().lookup_or_create_without_oid( + p, a, b, base_x, base_y, order, cofactor, EC_Group_Source::ExternalSource); + } } EC_Group::EC_Group(const OID& oid, @@ -457,10 +576,10 @@ BOTAN_ARG_CHECK(base_y >= 0 && base_y < p, "EC_Group base_y is invalid"); BOTAN_ARG_CHECK(p.bits() == order.bits(), "EC_Group p and order must have the same number of bits"); - auto mod_p = Modular_Reducer::for_public_modulus(p); + auto mod_p = Barrett_Reduction::for_public_modulus(p); BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(p, mod_p), "EC_Group p is not prime"); - auto mod_order = Modular_Reducer::for_public_modulus(order); + auto mod_order = Barrett_Reduction::for_public_modulus(order); BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(order, mod_order), "EC_Group order is not prime"); // This catches someone "ignoring" a cofactor and just trying to @@ -468,7 +587,8 @@ BOTAN_ARG_CHECK((p - order).abs().bits() <= (p.bits() / 2) + 1, "Hasse bound invalid"); // Check that 4*a^3 + 27*b^2 != 0 - const auto discriminant = mod_p.reduce(mod_p.multiply(4, mod_p.cube(a)) + mod_p.multiply(27, mod_p.square(b))); + const auto discriminant = mod_p.reduce(mod_p.multiply(BigInt::from_s32(4), mod_p.cube(a)) + + mod_p.multiply(BigInt::from_s32(27), mod_p.square(b))); BOTAN_ARG_CHECK(discriminant != 0, "EC_Group discriminant is invalid"); // Check that the generator (base_x,base_y) is on the curve; y^2 = x^3 + a*x + b @@ -476,18 +596,23 @@ auto x3_ax_b = mod_p.reduce(mod_p.cube(base_x) + mod_p.multiply(a, base_x) + b); BOTAN_ARG_CHECK(y2 == x3_ax_b, "EC_Group generator is not on the curve"); - BigInt cofactor(1); + const BigInt cofactor(1); m_data = ec_group_data().lookup_or_create(p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource); } -EC_Group::EC_Group(std::span ber) { - auto data = BER_decode_EC_group(ber, EC_Group_Source::ExternalSource); +EC_Group::EC_Group(std::span der) { + auto data = DER_decode_EC_group(der, EC_Group_Source::ExternalSource); m_data = data.first; m_explicit_encoding = data.second; } +// static +bool EC_Group::unregister(const OID& oid) { + return ec_group_data().unregister(oid); +} + const EC_Group_Data& EC_Group::data() const { if(m_data == nullptr) { throw Invalid_State("EC_Group uninitialized"); @@ -636,8 +761,8 @@ } } -std::string EC_Group::PEM_encode() const { - const std::vector der = DER_encode(EC_Group_Encoding::Explicit); +std::string EC_Group::PEM_encode(EC_Group_Encoding form) const { + const std::vector der = DER_encode(form); return PEM_Code::encode(der, "EC PARAMETERS"); } @@ -691,9 +816,10 @@ } //compute the discriminant: 4*a^3 + 27*b^2 which must be nonzero - auto mod_p = Modular_Reducer::for_public_modulus(p); + auto mod_p = Barrett_Reduction::for_public_modulus(p); - const BigInt discriminant = mod_p.reduce(mod_p.multiply(4, mod_p.cube(a)) + mod_p.multiply(27, mod_p.square(b))); + const BigInt discriminant = mod_p.reduce(mod_p.multiply(BigInt::from_s32(4), mod_p.cube(a)) + + mod_p.multiply(BigInt::from_s32(27), mod_p.square(b))); if(discriminant == 0) { return false; @@ -727,6 +853,10 @@ return true; } +EC_Group::Mul2Table::Mul2Table(EC_Group::Mul2Table&& other) noexcept = default; + +EC_Group::Mul2Table& EC_Group::Mul2Table::operator=(EC_Group::Mul2Table&& other) noexcept = default; + EC_Group::Mul2Table::Mul2Table(const EC_AffinePoint& h) : m_tbl(h._group()->make_mul2_table(h._inner())) {} EC_Group::Mul2Table::~Mul2Table() = default; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_group.h botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_group.h --- botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_group.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_group.h 2026-05-07 01:38:28.000000000 +0000 @@ -26,24 +26,6 @@ namespace Botan { /** -* This enum indicates the method used to encode the EC parameters -* -* @warning All support for explicit or implicit domain encodings -* will be removed in Botan4. Only named curves will be supported. -* -* TODO(Botan4) remove this enum -*/ -enum class EC_Group_Encoding { - Explicit, - ImplicitCA, - NamedCurve, - - EC_DOMPAR_ENC_EXPLICIT = Explicit, - EC_DOMPAR_ENC_IMPLICITCA = ImplicitCA, - EC_DOMPAR_ENC_OID = NamedCurve -}; - -/** * This enum indicates the source of the elliptic curve parameters * in use. * @@ -53,7 +35,7 @@ * ExternalSource means the curve parameters came from either an explicit * curve encoding or an application defined curve. */ -enum class EC_Group_Source { +enum class EC_Group_Source : uint8_t { Builtin, ExternalSource, }; @@ -63,7 +45,7 @@ * * This is returned by EC_Group::engine */ -enum class EC_Group_Engine { +enum class EC_Group_Engine : uint8_t { /// Using per curve implementation; fastest available Optimized, /// A generic implementation that handles many curves in one implementation @@ -175,13 +157,13 @@ const BigInt& order); /** - * Decode a BER encoded ECC domain parameter set - * @param ber the bytes of the BER encoding + * Decode a DER encoded ECC domain parameter set + * @param der the bytes of the DER encoding */ - explicit EC_Group(std::span ber); + explicit EC_Group(std::span der); BOTAN_DEPRECATED("Use EC_Group(std::span)") - EC_Group(const uint8_t ber[], size_t ber_len) : EC_Group(std::span{ber, ber_len}) {} + EC_Group(const uint8_t der[], size_t der_len) : EC_Group(std::span{der, der_len}) {} /** * Create an EC domain by OID (or throw if unknown) @@ -223,6 +205,16 @@ */ BOTAN_DEPRECATED("Deprecated no replacement") EC_Group(); + /** + * Unregister a previously registered group. + * + * Using this is discouraged for normal use. This is only useful or necessary if + * you are registering a very large number of distinct groups, and need to worry about memory constraints. + * + * Returns true if the group was found and unregistered. + */ + static bool unregister(const OID& oid); + ~EC_Group(); EC_Group(const EC_Group&); @@ -250,7 +242,16 @@ static bool supports_application_specific_group(); /** - * Return true if in this build configuration EC_Group::from_name(name) will succeed + * Return true if in this build configuration it is possible to + * register an application specific elliptic curve with a cofactor + * larger than 1. + */ + static bool supports_application_specific_group_with_cofactor(); + + /** + * Return true if EC_Group::from_name(name) should succeed for this name + * either because it is a group compiled into the library or it is a group + * which has already been registered by the application at runtime. */ static bool supports_named_group(std::string_view name); @@ -272,9 +273,11 @@ /** * Return a set of known named EC groups * - * This returns the set of groups for which from_name should succeed - * Note that the set of included groups can vary based on the - * build configuration. + * This returns a set of groups for which from_name should succeed. + * + * Note that the set of included groups can vary based on the build + * configuration, and that this list does not include any groups registered + * by the application at runtime. */ static const std::set& known_named_groups(); @@ -293,10 +296,15 @@ std::vector DER_encode() const; /** - * Return the PEM encoding (always in explicit form) + * Return the PEM encoding * @return string containing PEM data + * + * @warning In Botan4 the form parameter will be removed and only + * namedCurve will be supported + * + * TODO(Botan4) remove the argument */ - std::string PEM_encode() const; + std::string PEM_encode(EC_Group_Encoding form = EC_Group_Encoding::Explicit) const; /** * Return the size of p in bits (same as get_p().bits()) @@ -324,7 +332,7 @@ /** * Create a table for computing g*x + h*y */ - Mul2Table(const EC_AffinePoint& h); + BOTAN_FUTURE_EXPLICIT Mul2Table(const EC_AffinePoint& h); /** * Return the elliptic curve point g*x + h*y @@ -363,6 +371,10 @@ const EC_Scalar& y) const; ~Mul2Table(); + Mul2Table(const Mul2Table& other) = delete; + Mul2Table(Mul2Table&& other) noexcept; + Mul2Table& operator=(const Mul2Table& other) = delete; + Mul2Table& operator=(Mul2Table&& other) noexcept; private: std::unique_ptr m_tbl; @@ -420,19 +432,23 @@ /* * For internal use only - * TODO(Botan4): Add underscore prefix + * TODO(Botan4): Move this to an internal header */ static std::shared_ptr EC_group_info(const OID& oid); /* * For internal use only - * TODO(Botan4): Add underscore prefix + * + * @warning this invalidates pointers and can cause memory corruption. + * This function exists only to be called in tests. + * + * TODO(Botan4): Move this to an internal header */ static size_t clear_registered_curve_data(); /* * For internal use only - * TODO(Botan4): Add underscore prefix + * TODO(Botan4): Move this to an internal header */ static OID EC_group_identity_from_order(const BigInt& order); @@ -488,7 +504,7 @@ auto y = EC_Scalar::from_bigint(*this, y_bn); auto h = EC_AffinePoint(*this, h_pt); - Mul2Table gh_mul(h); + const Mul2Table gh_mul(h); if(auto r = gh_mul.mul2_vartime(x, y)) { return r->to_legacy_point(); @@ -501,14 +517,14 @@ * Blinded point multiplication, attempts resistance to side channels * @param k_bn the scalar * @param rng a random number generator - * @param ws a temp workspace * @return base_point*k */ BOTAN_DEPRECATED("Use EC_AffinePoint and EC_Scalar") - EC_Point - blinded_base_point_multiply(const BigInt& k_bn, RandomNumberGenerator& rng, std::vector& ws) const { + EC_Point blinded_base_point_multiply(const BigInt& k_bn, + RandomNumberGenerator& rng, + std::vector& /*ws*/) const { auto k = EC_Scalar::from_bigint(*this, k_bn); - auto pt = EC_AffinePoint::g_mul(k, rng, ws); + auto pt = EC_AffinePoint::g_mul(k, rng); return pt.to_legacy_point(); } @@ -518,14 +534,14 @@ * * @param k_bn the scalar * @param rng a random number generator - * @param ws a temp workspace * @return x coordinate of base_point*k */ BOTAN_DEPRECATED("Use EC_AffinePoint and EC_Scalar") - BigInt - blinded_base_point_multiply_x(const BigInt& k_bn, RandomNumberGenerator& rng, std::vector& ws) const { + BigInt blinded_base_point_multiply_x(const BigInt& k_bn, + RandomNumberGenerator& rng, + std::vector& /*ws*/) const { auto k = EC_Scalar::from_bigint(*this, k_bn); - return BigInt(EC_AffinePoint::g_mul(k, rng, ws).x_bytes()); + return BigInt(EC_AffinePoint::g_mul(k, rng).x_bytes()); } /** @@ -533,17 +549,16 @@ * @param point input point * @param k_bn the scalar * @param rng a random number generator - * @param ws a temp workspace * @return point*k */ BOTAN_DEPRECATED("Use EC_AffinePoint and EC_Scalar") EC_Point blinded_var_point_multiply(const EC_Point& point, const BigInt& k_bn, RandomNumberGenerator& rng, - std::vector& ws) const { + std::vector& /*ws*/) const { auto k = EC_Scalar::from_bigint(*this, k_bn); auto pt = EC_AffinePoint(*this, point); - return pt.mul(k, rng, ws).to_legacy_point(); + return pt.mul(k, rng).to_legacy_point(); } /** @@ -561,7 +576,7 @@ * @param hash_fn the hash function to use (typically "SHA-256" or "SHA-512") * @param input the input to hash * @param input_len length of input in bytes - * @param domain_sep a domain seperator + * @param domain_sep a domain separator * @param domain_sep_len length of domain_sep in bytes * @param random_oracle if the mapped point must be uniform (use "true" here unless you know what you are doing) @@ -591,7 +606,7 @@ * @param hash_fn the hash function to use (typically "SHA-256" or "SHA-512") * @param input the input to hash * @param input_len length of input in bytes - * @param domain_sep a domain seperator + * @param domain_sep a domain separator * @param random_oracle if the mapped point must be uniform (use "true" here unless you know what you are doing) */ @@ -602,12 +617,11 @@ std::string_view domain_sep, bool random_oracle = true) const { auto inp = std::span{input, input_len}; - auto dst = std::span{reinterpret_cast(domain_sep.data()), domain_sep.size()}; if(random_oracle) { - return EC_AffinePoint::hash_to_curve_ro(*this, hash_fn, inp, dst).to_legacy_point(); + return EC_AffinePoint::hash_to_curve_ro(*this, hash_fn, inp, domain_sep).to_legacy_point(); } else { - return EC_AffinePoint::hash_to_curve_nu(*this, hash_fn, inp, dst).to_legacy_point(); + return EC_AffinePoint::hash_to_curve_nu(*this, hash_fn, inp, domain_sep).to_legacy_point(); } } @@ -703,9 +717,9 @@ private: static EC_Group_Data_Map& ec_group_data(); - EC_Group(std::shared_ptr&& data); + explicit EC_Group(std::shared_ptr&& data); - static std::pair, bool> BER_decode_EC_group(std::span ber, + static std::pair, bool> DER_decode_EC_group(std::span der, EC_Group_Source source); static std::shared_ptr load_EC_group_info(const char* p, diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_inner_data.cpp botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_inner_data.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_inner_data.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_inner_data.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,12 +10,17 @@ #include #include #include +#include #if defined(BOTAN_HAS_LEGACY_EC_POINT) #include #include #endif +#if defined(BOTAN_HAS_XMD) + #include +#endif + namespace Botan { EC_Group_Data::~EC_Group_Data() = default; @@ -38,8 +43,8 @@ m_order(order), m_cofactor(cofactor), #if defined(BOTAN_HAS_LEGACY_EC_POINT) - m_mod_field(Modular_Reducer::for_public_modulus(p)), - m_mod_order(Modular_Reducer::for_public_modulus(order)), + m_mod_field(Barrett_Reduction::for_public_modulus(p)), + m_mod_order(Barrett_Reduction::for_public_modulus(order)), m_monty(m_p, m_mod_field), #endif m_oid(oid), @@ -52,18 +57,29 @@ m_has_cofactor(m_cofactor != 1), m_order_is_less_than_p(m_order < p), m_source(source) { + // TODO(Botan4) we can assume/assert the OID is set if(!m_oid.empty()) { DER_Encoder der(m_der_named_curve); der.encode(m_oid); - if(const auto id = PCurve::PrimeOrderCurveId::from_oid(m_oid)) { - m_pcurve = PCurve::PrimeOrderCurve::from_id(*id); - if(m_pcurve) { - m_engine = EC_Group_Engine::Optimized; - } - // still possibly null, if the curve is supported in general but not - // available in the build + const std::string name = m_oid.human_name_or_empty(); + if(!name.empty()) { + // returns nullptr if unknown or not supported + m_pcurve = PCurve::PrimeOrderCurve::for_named_curve(name); + } + if(m_pcurve) { + m_engine = EC_Group_Engine::Optimized; + } + } + + // Try a generic pcurves instance + if(!m_pcurve && !m_has_cofactor) { + m_pcurve = PCurve::PrimeOrderCurve::from_params(p, a, b, g_x, g_y, order); + if(m_pcurve) { + m_engine = EC_Group_Engine::Generic; } + // possibly still null here, if parameters unsuitable or if the + // pcurves_generic module wasn't included in the build } #if defined(BOTAN_HAS_LEGACY_EC_POINT) @@ -114,8 +130,92 @@ const BigInt& g_y, const BigInt& order, const BigInt& cofactor) const { - return (this->p() == p && this->a() == a && this->b() == b && this->order() == order && - this->cofactor() == cofactor && this->g_x() == g_x && this->g_y() == g_y); + if(p != this->p()) { + return false; + } + if(a != this->a()) { + return false; + } + if(b != this->b()) { + return false; + } + if(order != this->order()) { + return false; + } + if(cofactor != this->cofactor()) { + return false; + } + if(g_x != this->g_x()) { + return false; + } + if(g_y != this->g_y()) { + return false; + } + + return true; +} + +bool EC_Group_Data::params_match(const BigInt& p, + const BigInt& a, + const BigInt& b, + std::span base_pt, + const BigInt& order, + const BigInt& cofactor) const { + if(p != this->p()) { + return false; + } + if(a != this->a()) { + return false; + } + if(b != this->b()) { + return false; + } + if(order != this->order()) { + return false; + } + if(cofactor != this->cofactor()) { + return false; + } + + const size_t field_len = this->p_bytes(); + + if(base_pt.size() == 1 + field_len && (base_pt[0] == 0x02 || base_pt[0] == 0x03)) { + // compressed + + const auto g_x = m_g_x.serialize(field_len); + const auto g_y = m_g_y.is_odd(); + + const auto sec1_x = base_pt.subspan(1, field_len); + const bool sec1_y = (base_pt[0] == 0x03); + + if(!std::ranges::equal(sec1_x, g_x)) { + return false; + } + + if(sec1_y != g_y) { + return false; + } + + return true; + } else if(base_pt.size() == 1 + 2 * field_len && base_pt[0] == 0x04) { + const auto g_x = m_g_x.serialize(field_len); + const auto g_y = m_g_y.serialize(field_len); + + const auto sec1_x = base_pt.subspan(1, field_len); + const auto sec1_y = base_pt.subspan(1 + field_len, field_len); + + if(!std::ranges::equal(sec1_x, g_x)) { + return false; + } + + if(!std::ranges::equal(sec1_y, g_y)) { + return false; + } + + return true; + } else { + throw Decoding_Error("Invalid base point encoding in explicit group"); + } } bool EC_Group_Data::params_match(const EC_Group_Data& other) const { @@ -194,18 +294,6 @@ } } -std::unique_ptr EC_Group_Data::scalar_zero() const { - if(m_pcurve) { - return std::make_unique(shared_from_this(), m_pcurve->scalar_zero()); - } else { -#if defined(BOTAN_HAS_LEGACY_EC_POINT) - return std::make_unique(shared_from_this(), BigInt::zero()); -#else - throw Not_Implemented("Legacy EC interfaces disabled in this build configuration"); -#endif - } -} - std::unique_ptr EC_Group_Data::scalar_one() const { if(m_pcurve) { return std::make_unique(shared_from_this(), m_pcurve->scalar_one()); @@ -235,8 +323,7 @@ } std::unique_ptr EC_Group_Data::gk_x_mod_order(const EC_Scalar_Data& scalar, - RandomNumberGenerator& rng, - std::vector& ws) const { + RandomNumberGenerator& rng) const { if(m_pcurve) { const auto& k = EC_Scalar_Data_PC::checked_ref(scalar); auto gk_x_mod_order = m_pcurve->base_point_mul_x_mod_order(k.value(), rng); @@ -245,15 +332,15 @@ #if defined(BOTAN_HAS_LEGACY_EC_POINT) const auto& k = EC_Scalar_Data_BN::checked_ref(scalar); BOTAN_STATE_CHECK(m_base_mult != nullptr); + std::vector ws; const auto pt = m_base_mult->mul(k.value(), rng, m_order, ws); if(pt.is_zero()) { - return scalar_zero(); + return std::make_unique(shared_from_this(), BigInt::zero()); } else { return std::make_unique(shared_from_this(), m_mod_order.reduce(pt.get_affine_x())); } #else - BOTAN_UNUSED(ws); throw Not_Implemented("Legacy EC interfaces disabled in this build configuration"); #endif } @@ -286,31 +373,70 @@ } std::unique_ptr EC_Group_Data::point_deserialize(std::span bytes) const { + // The deprecated "hybrid" point format + // TODO(Botan4) remove this + if(bytes.size() >= 1 + 2 * 4 && (bytes[0] == 0x06 || bytes[0] == 0x07)) { + const bool hdr_y_is_even = bytes[0] == 0x06; + const bool y_is_even = (bytes.back() & 0x01) == 0; + + if(hdr_y_is_even == y_is_even) { + std::vector sec1(bytes.begin(), bytes.end()); + sec1[0] = 0x04; + return this->point_deserialize(sec1); + } + } + try { if(m_pcurve) { if(auto pt = m_pcurve->deserialize_point(bytes)) { return std::make_unique(shared_from_this(), std::move(*pt)); } else { - return nullptr; + return {}; } } else { #if defined(BOTAN_HAS_LEGACY_EC_POINT) - return std::make_unique(shared_from_this(), bytes); + auto pt = Botan::OS2ECP(bytes, m_curve); + return std::make_unique(shared_from_this(), std::move(pt)); #else throw Not_Implemented("Legacy EC interfaces disabled in this build configuration"); #endif } } catch(...) { - return nullptr; + return {}; + } +} + +namespace { + +std::function)> h2c_expand_message(std::string_view hash_fn, + std::span input, + std::span domain_sep) { + /* + * This could be extended to support expand_message_xof or a MHF like Argon2 + */ + + if(hash_fn.starts_with("SHAKE")) { + throw Not_Implemented("Hash to curve currently does not support expand_message_xof"); } + + return [=](std::span uniform_bytes) { +#if defined(BOTAN_HAS_XMD) + expand_message_xmd(hash_fn, uniform_bytes, input, domain_sep); +#else + BOTAN_UNUSED(hash_fn, uniform_bytes, input, domain_sep); + throw Not_Implemented("Hash to curve is not implemented due to XMD being disabled"); +#endif + }; } +} // namespace + std::unique_ptr EC_Group_Data::point_hash_to_curve_ro(std::string_view hash_fn, std::span input, std::span domain_sep) const { if(m_pcurve) { - auto pt = m_pcurve->hash_to_curve_ro(hash_fn, input, domain_sep); - return std::make_unique(shared_from_this(), pt.to_affine()); + auto pt = m_pcurve->hash_to_curve_ro(h2c_expand_message(hash_fn, input, domain_sep)); + return std::make_unique(shared_from_this(), m_pcurve->point_to_affine(pt)); } else { throw Not_Implemented("Hash to curve is not implemented for this curve"); } @@ -320,7 +446,7 @@ std::span input, std::span domain_sep) const { if(m_pcurve) { - auto pt = m_pcurve->hash_to_curve_nu(hash_fn, input, domain_sep); + auto pt = m_pcurve->hash_to_curve_nu(h2c_expand_message(hash_fn, input, domain_sep)); return std::make_unique(shared_from_this(), std::move(pt)); } else { throw Not_Implemented("Hash to curve is not implemented for this curve"); @@ -328,11 +454,10 @@ } std::unique_ptr EC_Group_Data::point_g_mul(const EC_Scalar_Data& scalar, - RandomNumberGenerator& rng, - std::vector& ws) const { + RandomNumberGenerator& rng) const { if(m_pcurve) { const auto& k = EC_Scalar_Data_PC::checked_ref(scalar); - auto pt = m_pcurve->mul_by_g(k.value(), rng).to_affine(); + auto pt = m_pcurve->point_to_affine(m_pcurve->mul_by_g(k.value(), rng)); return std::make_unique(shared_from_this(), std::move(pt)); } else { #if defined(BOTAN_HAS_LEGACY_EC_POINT) @@ -340,10 +465,10 @@ const auto& bn = EC_Scalar_Data_BN::checked_ref(scalar); BOTAN_STATE_CHECK(group->m_base_mult != nullptr); + std::vector ws; auto pt = group->m_base_mult->mul(bn.value(), rng, m_order, ws); return std::make_unique(shared_from_this(), std::move(pt)); #else - BOTAN_UNUSED(ws); throw Not_Implemented("Legacy EC interfaces disabled in this build configuration"); #endif } @@ -362,7 +487,7 @@ rng); if(pt) { - return std::make_unique(shared_from_this(), pt->to_affine()); + return std::make_unique(shared_from_this(), m_pcurve->point_to_affine(*pt)); } else { return nullptr; } @@ -372,8 +497,8 @@ const auto& group = p.group(); // TODO this could be better! - EC_Point_Var_Point_Precompute p_mul(p.to_legacy_point(), rng, ws); - EC_Point_Var_Point_Precompute q_mul(q.to_legacy_point(), rng, ws); + const EC_Point_Var_Point_Precompute p_mul(p.to_legacy_point(), rng, ws); + const EC_Point_Var_Point_Precompute q_mul(q.to_legacy_point(), rng, ws); const auto order = group->order() * group->cofactor(); // See #3800 @@ -397,11 +522,10 @@ std::unique_ptr EC_Group_Data::affine_add(const EC_AffinePoint_Data& p, const EC_AffinePoint_Data& q) const { if(m_pcurve) { - auto pt = m_pcurve->point_add_mixed( - PCurve::PrimeOrderCurve::ProjectivePoint::from_affine(EC_AffinePoint_Data_PC::checked_ref(p).value()), - EC_AffinePoint_Data_PC::checked_ref(q).value()); + auto pt = m_pcurve->point_add(EC_AffinePoint_Data_PC::checked_ref(p).value(), + EC_AffinePoint_Data_PC::checked_ref(q).value()); - return std::make_unique(shared_from_this(), pt.to_affine()); + return std::make_unique(shared_from_this(), m_pcurve->point_to_affine(pt)); } else { #if defined(BOTAN_HAS_LEGACY_EC_POINT) auto pt = p.to_legacy_point() + q.to_legacy_point(); @@ -432,7 +556,7 @@ return std::make_unique(h); } else { #if defined(BOTAN_HAS_LEGACY_EC_POINT) - EC_AffinePoint_Data_BN g(shared_from_this(), this->base_point()); + const EC_AffinePoint_Data_BN g(shared_from_this(), this->base_point()); return std::make_unique(g, h); #else throw Not_Implemented("Legacy EC interfaces disabled in this build configuration"); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_inner_data.h botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_inner_data.h --- botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_inner_data.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_inner_data.h 2026-05-07 01:38:28.000000000 +0000 @@ -12,12 +12,11 @@ #include #include #include -#include #include #include #if defined(BOTAN_HAS_LEGACY_EC_POINT) - #include + #include #endif namespace Botan { @@ -34,7 +33,7 @@ class EC_Group_Data; -class EC_Scalar_Data { +class EC_Scalar_Data /* NOLINT(*-special-member-functions) */ { public: virtual ~EC_Scalar_Data() = default; @@ -50,6 +49,8 @@ virtual void assign(const EC_Scalar_Data& y) = 0; + virtual void zeroize() = 0; + virtual void square_self() = 0; virtual std::unique_ptr negate() const = 0; @@ -67,7 +68,7 @@ virtual void serialize_to(std::span bytes) const = 0; }; -class EC_AffinePoint_Data { +class EC_AffinePoint_Data /* NOLINT(*-special-member-functions) */ { public: virtual ~EC_AffinePoint_Data() = default; @@ -97,19 +98,16 @@ virtual void serialize_uncompressed_to(std::span bytes) const = 0; virtual std::unique_ptr mul(const EC_Scalar_Data& scalar, - RandomNumberGenerator& rng, - std::vector& ws) const = 0; + RandomNumberGenerator& rng) const = 0; - virtual secure_vector mul_x_only(const EC_Scalar_Data& scalar, - RandomNumberGenerator& rng, - std::vector& ws) const = 0; + virtual secure_vector mul_x_only(const EC_Scalar_Data& scalar, RandomNumberGenerator& rng) const = 0; #if defined(BOTAN_HAS_LEGACY_EC_POINT) virtual EC_Point to_legacy_point() const = 0; #endif }; -class EC_Mul2Table_Data { +class EC_Mul2Table_Data /* NOLINT(*-special-member-functions) */ { public: virtual ~EC_Mul2Table_Data() = default; @@ -139,6 +137,11 @@ ~EC_Group_Data(); + EC_Group_Data(const EC_Group_Data& other) = delete; + EC_Group_Data(EC_Group_Data&& other) = delete; + EC_Group_Data& operator=(const EC_Group_Data& other) = delete; + EC_Group_Data& operator=(EC_Group_Data&& other) = delete; + bool params_match(const BigInt& p, const BigInt& a, const BigInt& b, @@ -147,6 +150,14 @@ const BigInt& order, const BigInt& cofactor) const; + // Like the other params_match but accepting the base point in encoded form + bool params_match(const BigInt& p, + const BigInt& a, + const BigInt& b, + std::span base_pt, + const BigInt& order, + const BigInt& cofactor) const; + bool params_match(const EC_Group_Data& other) const; void set_oid(const OID& oid); @@ -176,7 +187,7 @@ const BigInt& monty_b() const { return m_b_r; } - const Modular_Reducer& mod_order() const { return m_mod_order; } + const Barrett_Reduction& mod_order() const { return m_mod_order; } #endif bool order_is_less_than_p() const { return m_order_is_less_than_p; } @@ -215,7 +226,7 @@ /// If the input is rejected then nullptr is returned std::unique_ptr scalar_deserialize(std::span bytes) const; - /// Scalar from bytes with ECDSA style trunction + /// Scalar from bytes with ECDSA style truncation /// /// This should always succeed std::unique_ptr scalar_from_bytes_with_trunc(std::span bytes) const; @@ -237,13 +248,9 @@ /// This will be in the range [1,n) where n is the group order std::unique_ptr scalar_random(RandomNumberGenerator& rng) const; - std::unique_ptr scalar_zero() const; - std::unique_ptr scalar_one() const; - std::unique_ptr gk_x_mod_order(const EC_Scalar_Data& scalar, - RandomNumberGenerator& rng, - std::vector& ws) const; + std::unique_ptr gk_x_mod_order(const EC_Scalar_Data& scalar, RandomNumberGenerator& rng) const; /// Deserialize a point /// @@ -258,9 +265,7 @@ std::span input, std::span domain_sep) const; - std::unique_ptr point_g_mul(const EC_Scalar_Data& scalar, - RandomNumberGenerator& rng, - std::vector& ws) const; + std::unique_ptr point_g_mul(const EC_Scalar_Data& scalar, RandomNumberGenerator& rng) const; std::unique_ptr mul_px_qy(const EC_AffinePoint_Data& p, const EC_Scalar_Data& x, @@ -315,8 +320,8 @@ CurveGFp m_curve; EC_Point m_base_point; - Modular_Reducer m_mod_field; - Modular_Reducer m_mod_order; + Barrett_Reduction m_mod_field; + Barrett_Reduction m_mod_order; // Montgomery parameters (only used for legacy EC_Point) Montgomery_Params m_monty; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_inner_pc.cpp botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_inner_pc.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_inner_pc.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_inner_pc.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,24 +6,14 @@ #include -namespace Botan { - -namespace { - -PCurve::PrimeOrderCurve::AffinePoint deserialize_pcurve_pt(const PCurve::PrimeOrderCurve& curve, - std::span bytes) { - if(auto pt = curve.deserialize_point(bytes)) { - return *pt; - } else { - throw Decoding_Error("Invalid elliptic curve point encoding"); - } -} +#include +#include -} // namespace +namespace Botan { const EC_Scalar_Data_PC& EC_Scalar_Data_PC::checked_ref(const EC_Scalar_Data& data) { const auto* p = dynamic_cast(&data); - if(!p) { + if(p == nullptr) { throw Invalid_State("Failed conversion to EC_Scalar_Data_PC"); } return *p; @@ -42,44 +32,50 @@ } bool EC_Scalar_Data_PC::is_zero() const { - return this->value().is_zero(); + const auto& pcurve = this->group()->pcurve(); + return pcurve.scalar_is_zero(m_v); } bool EC_Scalar_Data_PC::is_eq(const EC_Scalar_Data& other) const { - return (value() == checked_ref(other).value()); + const auto& pcurve = group()->pcurve(); + return pcurve.scalar_equal(m_v, checked_ref(other).m_v); } void EC_Scalar_Data_PC::assign(const EC_Scalar_Data& other) { m_v = checked_ref(other).value(); } +void EC_Scalar_Data_PC::zeroize() { + m_v._zeroize(); +} + void EC_Scalar_Data_PC::square_self() { // TODO square in place - m_v = m_v.square(); + m_v = m_group->pcurve().scalar_square(m_v); } std::unique_ptr EC_Scalar_Data_PC::negate() const { - return std::make_unique(m_group, m_v.negate()); + return std::make_unique(m_group, m_group->pcurve().scalar_negate(m_v)); } std::unique_ptr EC_Scalar_Data_PC::invert() const { - return std::make_unique(m_group, m_v.invert()); + return std::make_unique(m_group, m_group->pcurve().scalar_invert(m_v)); } std::unique_ptr EC_Scalar_Data_PC::invert_vartime() const { - return std::make_unique(m_group, m_v.invert_vartime()); + return std::make_unique(m_group, m_group->pcurve().scalar_invert_vartime(m_v)); } std::unique_ptr EC_Scalar_Data_PC::add(const EC_Scalar_Data& other) const { - return std::make_unique(m_group, m_v + checked_ref(other).value()); + return std::make_unique(m_group, group()->pcurve().scalar_add(m_v, checked_ref(other).m_v)); } std::unique_ptr EC_Scalar_Data_PC::sub(const EC_Scalar_Data& other) const { - return std::make_unique(m_group, m_v - checked_ref(other).value()); + return std::make_unique(m_group, group()->pcurve().scalar_sub(m_v, checked_ref(other).m_v)); } std::unique_ptr EC_Scalar_Data_PC::mul(const EC_Scalar_Data& other) const { - return std::make_unique(m_group, m_v * checked_ref(other).value()); + return std::make_unique(m_group, group()->pcurve().scalar_mul(m_v, checked_ref(other).m_v)); } void EC_Scalar_Data_PC::serialize_to(std::span bytes) const { @@ -90,24 +86,17 @@ EC_AffinePoint_Data_PC::EC_AffinePoint_Data_PC(std::shared_ptr group, PCurve::PrimeOrderCurve::AffinePoint pt) : m_group(std::move(group)), m_pt(std::move(pt)) { - if(!m_pt.is_identity()) { - m_xy = m_pt.serialize>(); - BOTAN_ASSERT_NOMSG(m_xy.size() == 1 + 2 * field_element_bytes()); - } -} + const auto& pcurve = m_group->pcurve(); -EC_AffinePoint_Data_PC::EC_AffinePoint_Data_PC(std::shared_ptr group, - std::span bytes) : - m_group(std::move(group)), m_pt(deserialize_pcurve_pt(m_group->pcurve(), bytes)) { - if(!m_pt.is_identity()) { - m_xy = m_pt.serialize>(); - BOTAN_ASSERT_NOMSG(m_xy.size() == 1 + 2 * field_element_bytes()); + if(!pcurve.affine_point_is_identity(m_pt)) { + m_xy.resize(1 + 2 * field_element_bytes()); + pcurve.serialize_point(m_xy, m_pt); } } const EC_AffinePoint_Data_PC& EC_AffinePoint_Data_PC::checked_ref(const EC_AffinePoint_Data& data) { const auto* p = dynamic_cast(&data); - if(!p) { + if(p == nullptr) { throw Invalid_State("Failed conversion to EC_AffinePoint_Data_PC"); } return *p; @@ -122,21 +111,16 @@ } std::unique_ptr EC_AffinePoint_Data_PC::mul(const EC_Scalar_Data& scalar, - RandomNumberGenerator& rng, - std::vector& ws) const { - BOTAN_UNUSED(ws); - + RandomNumberGenerator& rng) const { BOTAN_ARG_CHECK(scalar.group() == m_group, "Curve mismatch"); const auto& k = EC_Scalar_Data_PC::checked_ref(scalar).value(); - auto pt = m_group->pcurve().mul(m_pt, k, rng).to_affine(); + const auto& pcurve = m_group->pcurve(); + auto pt = pcurve.point_to_affine(pcurve.mul(m_pt, k, rng)); return std::make_unique(m_group, std::move(pt)); } secure_vector EC_AffinePoint_Data_PC::mul_x_only(const EC_Scalar_Data& scalar, - RandomNumberGenerator& rng, - std::vector& ws) const { - BOTAN_UNUSED(ws); - + RandomNumberGenerator& rng) const { BOTAN_ARG_CHECK(scalar.group() == m_group, "Curve mismatch"); const auto& k = EC_Scalar_Data_PC::checked_ref(scalar).value(); return m_group->pcurve().mul_x_only(m_pt, k, rng); @@ -217,8 +201,10 @@ const auto& x = EC_Scalar_Data_PC::checked_ref(xd); const auto& y = EC_Scalar_Data_PC::checked_ref(yd); - if(auto pt = m_group->pcurve().mul2_vartime(*m_tbl, x.value(), y.value())) { - return std::make_unique(m_group, pt->to_affine()); + const auto& pcurve = m_group->pcurve(); + + if(auto pt = pcurve.mul2_vartime(*m_tbl, x.value(), y.value())) { + return std::make_unique(m_group, pcurve.point_to_affine(*pt)); } else { return nullptr; } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_inner_pc.h botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_inner_pc.h --- botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_inner_pc.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_inner_pc.h 2026-05-07 01:38:28.000000000 +0000 @@ -32,6 +32,8 @@ void assign(const EC_Scalar_Data& y) override; + void zeroize() override; + void square_self() override; std::unique_ptr negate() const override; @@ -59,8 +61,6 @@ public: EC_AffinePoint_Data_PC(std::shared_ptr group, PCurve::PrimeOrderCurve::AffinePoint pt); - EC_AffinePoint_Data_PC(std::shared_ptr group, std::span pt); - static const EC_AffinePoint_Data_PC& checked_ref(const EC_AffinePoint_Data& data); const std::shared_ptr& group() const override; @@ -81,13 +81,9 @@ void serialize_uncompressed_to(std::span bytes) const override; - std::unique_ptr mul(const EC_Scalar_Data& scalar, - RandomNumberGenerator& rng, - std::vector& ws) const override; - - secure_vector mul_x_only(const EC_Scalar_Data& scalar, - RandomNumberGenerator& rng, - std::vector& ws) const override; + std::unique_ptr mul(const EC_Scalar_Data& scalar, RandomNumberGenerator& rng) const override; + + secure_vector mul_x_only(const EC_Scalar_Data& scalar, RandomNumberGenerator& rng) const override; const PCurve::PrimeOrderCurve::AffinePoint& value() const { return m_pt; } @@ -103,7 +99,7 @@ class EC_Mul2Table_Data_PC final : public EC_Mul2Table_Data { public: - EC_Mul2Table_Data_PC(const EC_AffinePoint_Data& q); + explicit EC_Mul2Table_Data_PC(const EC_AffinePoint_Data& q); std::unique_ptr mul2_vartime(const EC_Scalar_Data& x, const EC_Scalar_Data& y) const override; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_named.cpp botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_named.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_named.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_named.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,7 @@ /* * ECC Group Info -* This file was automatically generated by ./src/scripts/dev_tools/gen_ec_groups.py on 2023-05-30 +* This file was automatically generated by ./src/scripts/dev_tools/gen_ec_groups.py on 2026-04-24 +* All manual changes will be lost. Edit the script instead. * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -151,7 +152,7 @@ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97", "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD94", "0xA6", - "1", + "0x1", "0x8D91E471E0989CDA27DF505A453F2B7635294F2DDF23E3B122ACC99C9E9F1E14", "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C611070995AD10045841B09B761B893", OID{1, 2, 643, 7, 1, 2, 1, 1, 1}); @@ -163,7 +164,7 @@ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7", "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC4", "0xE8C2505DEDFC86DDC1BD0B2B6667F1DA34B82574761CB0E879BD081CFD0B6265EE3CB090F30D27614CB4574010DA90DD862EF9D4EBEE4761503190785A71C760", - "3", + "0x3", "0x7503CFE87A836AE3A61B8816E25450E6CE5E1C93ACF1ABC1778064FDCBEFA921DF1626BE4FD036E93D75E6A50E3A41E98028FE5FC235F5B889A589CB5215F2A4", "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27E69532F48D89116FF22B8D4E0560609B4B38ABFAD2B85DCACDB1411F10B275", oid); @@ -173,8 +174,8 @@ if(oid == OID{1, 3, 132, 0, 9}) { return load_EC_group_info( "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73", - "0", - "7", + "0x0", + "0x7", "0x3B4C382CE37AA192A4019E763036F4F5DD4D7EBB", "0x938CF935318FDCED6BC28286531733C3F03C4FEE", "0x100000000000000000001B8FA16DFAB9ACA16B6B3", @@ -209,8 +210,8 @@ if(oid == OID{1, 3, 132, 0, 31}) { return load_EC_group_info( "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37", - "0", - "3", + "0x0", + "0x3", "0xDB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D", "0x9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D", "0xFFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D", @@ -233,8 +234,8 @@ if(oid == OID{1, 3, 132, 0, 32}) { return load_EC_group_info( "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D", - "0", - "5", + "0x0", + "0x5", "0xA1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C", "0x7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5", "0x10000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7", @@ -257,8 +258,8 @@ if(oid == OID{1, 3, 132, 0, 10}) { return load_EC_group_info( "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", - "0", - "7", + "0x0", + "0x7", "0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", "0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", @@ -473,46 +474,53 @@ } //static -bool EC_Group::supports_named_group(std::string_view name) { - return EC_Group::known_named_groups().contains(std::string(name)); -} - -//static const std::set& EC_Group::known_named_groups() { static const std::set named_groups = { #if defined(BOTAN_HAS_PCURVES_BRAINPOOL256R1) || defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC) "brainpool256r1", #endif + #if defined(BOTAN_HAS_PCURVES_BRAINPOOL384R1) || defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC) "brainpool384r1", #endif + #if defined(BOTAN_HAS_PCURVES_BRAINPOOL512R1) || defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC) "brainpool512r1", #endif + #if defined(BOTAN_HAS_PCURVES_FRP256V1) || defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC) "frp256v1", #endif + #if defined(BOTAN_HAS_PCURVES_NUMSP512D1) || defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC) "numsp512d1", #endif + #if defined(BOTAN_HAS_PCURVES_SECP192R1) || defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC) "secp192r1", #endif + #if defined(BOTAN_HAS_PCURVES_SECP224R1) || defined(BOTAN_HAS_LEGACY_EC_POINT) - "secp224r1", // not supported by pcurves_generic + // Not supported by pcurves_generic + "secp224r1", #endif + #if defined(BOTAN_HAS_PCURVES_SECP256K1) || defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC) "secp256k1", #endif + #if defined(BOTAN_HAS_PCURVES_SECP256R1) || defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC) "secp256r1", #endif + #if defined(BOTAN_HAS_PCURVES_SECP384R1) || defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC) "secp384r1", #endif + #if defined(BOTAN_HAS_PCURVES_SECP521R1) || defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC) "secp521r1", #endif + #if defined(BOTAN_HAS_PCURVES_SM2P256V1) || defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC) "sm2p256v1", #endif @@ -524,7 +532,6 @@ "gost_256A", "gost_512A", "secp192k1", - "secp192r1", "x962_p192v2", "x962_p192v3", "x962_p239v1", @@ -540,9 +547,10 @@ "secp224k1", #endif }; + return named_groups; } -// clang-format on - } // namespace Botan + +// clang-format on diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_point_format.h botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_point_format.h --- botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_point_format.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_point_format.h 2026-05-07 01:38:28.000000000 +0000 @@ -12,13 +12,29 @@ /* * This header is public, but avoid including it directly. Instead * get the contents via ec_group.h. -* -* TODO(Botan4): Move EC_Point_Format to ec_group.h and delete this file */ namespace Botan { -enum class EC_Point_Format { +/** +* This enum indicates the method used to encode the EC parameters +* +* @warning All support for explicit or implicit domain encodings +* will be removed in Botan4. Only named curves will be supported. +* +* TODO(Botan4) remove this enum +*/ +enum class EC_Group_Encoding : uint8_t { + Explicit = 0, + ImplicitCA = 1, + NamedCurve = 2, + + EC_DOMPAR_ENC_EXPLICIT = Explicit, + EC_DOMPAR_ENC_IMPLICITCA = ImplicitCA, + EC_DOMPAR_ENC_OID = NamedCurve +}; + +enum class EC_Point_Format : uint8_t { Uncompressed = 0, Compressed = 1, diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_scalar.cpp botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_scalar.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_scalar.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_scalar.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,10 @@ #include #include +#if defined(BOTAN_HAS_XMD) + #include +#endif + namespace Botan { EC_Scalar EC_Scalar::_from_inner(std::unique_ptr inner) { @@ -76,9 +80,9 @@ return BigInt::from_bytes(bytes); } -EC_Scalar EC_Scalar::gk_x_mod_order(const EC_Scalar& scalar, RandomNumberGenerator& rng, std::vector& ws) { +EC_Scalar EC_Scalar::gk_x_mod_order(const EC_Scalar& scalar, RandomNumberGenerator& rng) { const auto& group = scalar._inner().group(); - return EC_Scalar(group->gk_x_mod_order(scalar.inner(), rng, ws)); + return EC_Scalar(group->gk_x_mod_order(scalar.inner(), rng)); } void EC_Scalar::serialize_to(std::span bytes) const { @@ -162,8 +166,38 @@ m_scalar->assign(x.inner()); } +void EC_Scalar::zeroize() { + m_scalar->zeroize(); +} + bool EC_Scalar::is_eq(const EC_Scalar& x) const { return inner().is_eq(x.inner()); } +//static +EC_Scalar EC_Scalar::hash(const EC_Group& group, + std::string_view hash_fn, + std::span input, + std::span domain_sep) { +#if defined(BOTAN_HAS_XMD) + + /* + * This could be extended to support expand_message_xof or a MHF like Argon2 + */ + if(hash_fn.starts_with("SHAKE")) { + throw Not_Implemented("Hash to scalar currently does not support expand_message_xof"); + } + + const size_t scalar_bits = group.get_order_bits(); + const size_t security_level = (scalar_bits + 1) / 2; + secure_vector uniform_bytes((scalar_bits + security_level + 7) / 8); + expand_message_xmd(hash_fn, uniform_bytes, input, domain_sep); + + return EC_Scalar::from_bytes_mod_order(group, uniform_bytes); +#else + BOTAN_UNUSED(group, hash_fn, input, domain_sep); + throw Not_Implemented("EC_Scalar::hash not available due to missing XMD"); +#endif +} + } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_scalar.h botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_scalar.h --- botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/ec_scalar.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/ec_scalar.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,8 +9,10 @@ #include #include +#include #include #include +#include #include namespace Botan { @@ -18,13 +20,12 @@ class BigInt; class RandomNumberGenerator; class EC_Group; -class EC_Group_Data; class EC_Scalar_Data; /** * Represents an integer modulo the prime group order of an elliptic curve */ -class BOTAN_UNSTABLE_API EC_Scalar final { +class BOTAN_PUBLIC_API(3, 6) EC_Scalar final { public: /** * Deserialize a scalar @@ -54,6 +55,16 @@ static EC_Scalar from_bytes_mod_order(const EC_Group& group, std::span bytes); /** + * Hash to scalar following RFC 9380 + * + * This requires XMD. Unlike hash2curve, any group is supported + */ + static EC_Scalar hash(const EC_Group& group, + std::string_view hash_fn, + std::span input, + std::span domain_sep); + + /** * Convert a bytestring to an EC_Scalar * * This is similar to deserialize but instead of returning nullopt if the input @@ -91,10 +102,14 @@ * Compute the elliptic curve scalar multiplication (g*k) where g is the * standard base point on the curve. Then extract the x coordinate of * the resulting point, and reduce it modulo the group order. - * - * Workspace argument is transitional */ - static EC_Scalar gk_x_mod_order(const EC_Scalar& scalar, RandomNumberGenerator& rng, std::vector& ws); + static EC_Scalar gk_x_mod_order(const EC_Scalar& scalar, RandomNumberGenerator& rng); + + BOTAN_DEPRECATED("Use version without workspace arg") + static EC_Scalar + gk_x_mod_order(const EC_Scalar& scalar, RandomNumberGenerator& rng, std::vector& /*ws*/) { + return EC_Scalar::gk_x_mod_order(scalar, rng); + } /** * Return the byte size of this scalar @@ -189,6 +204,13 @@ void assign(const EC_Scalar& x); /** + * Equivalent to assigning a zero value, but also does so in a way that + * attempts to ensure the write always occurs even if a compiler can deduce + * the assignment is otherwise unnecessary. + */ + void zeroize(); + + /** * Set *this to its own square modulo the group order */ void square_self(); @@ -226,7 +248,7 @@ private: friend class EC_AffinePoint; - EC_Scalar(std::unique_ptr scalar); + explicit EC_Scalar(std::unique_ptr scalar); const EC_Scalar_Data& inner() const { return *m_scalar; } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/curve_gfp.h botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/curve_gfp.h --- botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/curve_gfp.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/curve_gfp.h 2026-05-07 01:38:28.000000000 +0000 @@ -48,6 +48,9 @@ size_t get_p_words() const; CurveGFp(const CurveGFp&) = default; + CurveGFp(CurveGFp&&) = default; + + ~CurveGFp() = default; private: friend class EC_Point; @@ -58,19 +61,17 @@ */ CurveGFp() = default; - CurveGFp(const EC_Group_Data* group); + BOTAN_FUTURE_EXPLICIT CurveGFp(const EC_Group_Data* group); CurveGFp& operator=(const CurveGFp&) = default; + CurveGFp& operator=(CurveGFp&&) = default; - void swap(CurveGFp& other) { std::swap(m_group, other.m_group); } + void swap(CurveGFp& other) noexcept { std::swap(m_group, other.m_group); } bool operator==(const CurveGFp& other) const { return (m_group == other.m_group); } private: - const EC_Group_Data& group() const { - BOTAN_ASSERT_NONNULL(m_group); - return *m_group; - } + const EC_Group_Data& group() const; /** * Raw pointer diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/ec_inner_bn.cpp botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/ec_inner_bn.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/ec_inner_bn.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/ec_inner_bn.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,13 +6,15 @@ #include +#include +#include #include namespace Botan { const EC_Scalar_Data_BN& EC_Scalar_Data_BN::checked_ref(const EC_Scalar_Data& data) { const auto* p = dynamic_cast(&data); - if(!p) { + if(p == nullptr) { throw Invalid_State("Failed conversion to EC_Scalar_Data_BN"); } return *p; @@ -42,20 +44,35 @@ m_v = checked_ref(other).value(); } +void EC_Scalar_Data_BN::zeroize() { + // BigInt stores its value in a secure_vector, after swapping the existing + // value will go out of scope (inside `zero`) and be wiped properly. + BigInt zero; + std::swap(m_v, zero); +} + void EC_Scalar_Data_BN::square_self() { m_group->mod_order().square(m_v); } std::unique_ptr EC_Scalar_Data_BN::negate() const { - return std::make_unique(m_group, m_group->mod_order().reduce(-m_v)); + return std::make_unique(m_group, m_group->mod_order().reduce(m_group->order() - m_v)); } std::unique_ptr EC_Scalar_Data_BN::invert() const { - return std::make_unique(m_group, inverse_mod_public_prime(m_v, m_group->order())); + if(m_v.is_zero()) { + return std::make_unique(m_group, m_v); + } else { + return std::make_unique(m_group, inverse_mod_public_prime(m_v, m_group->order())); + } } std::unique_ptr EC_Scalar_Data_BN::invert_vartime() const { - return std::make_unique(m_group, inverse_mod_public_prime(m_v, m_group->order())); + if(m_v.is_zero()) { + return std::make_unique(m_group, m_v); + } else { + return std::make_unique(m_group, inverse_mod_public_prime(m_v, m_group->order())); + } } std::unique_ptr EC_Scalar_Data_BN::add(const EC_Scalar_Data& other) const { @@ -63,7 +80,8 @@ } std::unique_ptr EC_Scalar_Data_BN::sub(const EC_Scalar_Data& other) const { - return std::make_unique(m_group, m_group->mod_order().reduce(m_v - checked_ref(other).value())); + return std::make_unique( + m_group, m_group->mod_order().reduce(m_v + (m_group->order() - checked_ref(other).value()))); } std::unique_ptr EC_Scalar_Data_BN::mul(const EC_Scalar_Data& other) const { @@ -83,16 +101,6 @@ } } -EC_AffinePoint_Data_BN::EC_AffinePoint_Data_BN(std::shared_ptr group, - std::span pt) : - m_group(std::move(group)) { - BOTAN_ASSERT_NONNULL(m_group); - m_pt = Botan::OS2ECP(pt, m_group->curve()); - if(!m_pt.is_zero()) { - m_xy = m_pt.xy_bytes(); - } -} - std::unique_ptr EC_AffinePoint_Data_BN::clone() const { return std::make_unique(m_group, m_pt); } @@ -102,12 +110,12 @@ } std::unique_ptr EC_AffinePoint_Data_BN::mul(const EC_Scalar_Data& scalar, - RandomNumberGenerator& rng, - std::vector& ws) const { + RandomNumberGenerator& rng) const { BOTAN_ARG_CHECK(scalar.group() == m_group, "Curve mismatch"); const auto& bn = EC_Scalar_Data_BN::checked_ref(scalar); - EC_Point_Var_Point_Precompute mul(m_pt, rng, ws); + std::vector ws; + const EC_Point_Var_Point_Precompute mul(m_pt, rng, ws); // We pass order*cofactor here to "correctly" handle the case where the // point is on the curve but not in the prime order subgroup. This only @@ -120,12 +128,12 @@ } secure_vector EC_AffinePoint_Data_BN::mul_x_only(const EC_Scalar_Data& scalar, - RandomNumberGenerator& rng, - std::vector& ws) const { + RandomNumberGenerator& rng) const { BOTAN_ARG_CHECK(scalar.group() == m_group, "Curve mismatch"); const auto& bn = EC_Scalar_Data_BN::checked_ref(scalar); - EC_Point_Var_Point_Precompute mul(m_pt, rng, ws); + std::vector ws; + const EC_Point_Var_Point_Precompute mul(m_pt, rng, ws); // We pass order*cofactor here to "correctly" handle the case where the // point is on the curve but not in the prime order subgroup. This only diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/ec_inner_bn.h botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/ec_inner_bn.h --- botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/ec_inner_bn.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/ec_inner_bn.h 2026-05-07 01:38:28.000000000 +0000 @@ -31,6 +31,8 @@ void assign(const EC_Scalar_Data& y) override; + void zeroize() override; + void square_self() override; std::unique_ptr negate() const override; @@ -58,8 +60,6 @@ public: EC_AffinePoint_Data_BN(std::shared_ptr group, EC_Point pt); - EC_AffinePoint_Data_BN(std::shared_ptr group, std::span pt); - const std::shared_ptr& group() const override; std::unique_ptr clone() const override; @@ -78,13 +78,9 @@ void serialize_uncompressed_to(std::span bytes) const override; - std::unique_ptr mul(const EC_Scalar_Data& scalar, - RandomNumberGenerator& rng, - std::vector& ws) const override; - - secure_vector mul_x_only(const EC_Scalar_Data& scalar, - RandomNumberGenerator& rng, - std::vector& ws) const override; + std::unique_ptr mul(const EC_Scalar_Data& scalar, RandomNumberGenerator& rng) const override; + + secure_vector mul_x_only(const EC_Scalar_Data& scalar, RandomNumberGenerator& rng) const override; EC_Point to_legacy_point() const override { return m_pt; } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/ec_point.cpp botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/ec_point.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/ec_point.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/ec_point.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,14 +9,14 @@ #include +#include #include #include +#include #include #include #include #include -#include -#include namespace Botan { @@ -29,6 +29,11 @@ BOTAN_ASSERT_NONNULL(m_group); } +const EC_Group_Data& CurveGFp::group() const { + BOTAN_ASSERT_NONNULL(m_group); + return *m_group; +} + const BigInt& CurveGFp::get_a() const { return this->group().a(); } @@ -52,23 +57,29 @@ } void from_rep(const EC_Group_Data& group, BigInt& z, secure_vector& ws) { - group.monty().redc_in_place(z, ws); + z = group.monty().redc(z, ws); } BigInt from_rep_to_tmp(const EC_Group_Data& group, const BigInt& x, secure_vector& ws) { return group.monty().redc(x, ws); } -void fe_mul(const EC_Group_Data& group, BigInt& z, const BigInt& x, const BigInt& y, secure_vector& ws) { +inline void fe_mul(const EC_Group_Data& group, BigInt& z, const BigInt& x, const BigInt& y, secure_vector& ws) { group.monty().mul(z, x, y, ws); } -void fe_mul( +inline void fe_mul( const EC_Group_Data& group, BigInt& z, const word x_w[], size_t x_size, const BigInt& y, secure_vector& ws) { group.monty().mul(z, y, std::span{x_w, x_size}, ws); } -BigInt fe_mul(const EC_Group_Data& group, const BigInt& x, const BigInt& y, secure_vector& ws) { +template +inline void fe_smul(BigInt& z, const BigInt& p, secure_vector& ws) { + static_assert(M == 2 || M == 3 || M == 4 || M == 8); + z.mod_mul(M, p, ws); +} + +inline BigInt fe_mul(const EC_Group_Data& group, const BigInt& x, const BigInt& y, secure_vector& ws) { return group.monty().mul(x, y, ws); } @@ -130,7 +141,7 @@ const auto& group = m_curve.group(); - const BigInt mask = BigInt::random_integer(rng, 2, group.p()); + const BigInt mask = BigInt::random_integer(rng, BigInt::from_s32(2), group.p()); /* * No reason to convert this to Montgomery representation first, @@ -161,6 +172,18 @@ } // namespace +void EC_Point::add_affine(const EC_Point& other, std::vector& workspace) { + BOTAN_ASSERT_NOMSG(m_curve == other.m_curve); + BOTAN_DEBUG_ASSERT(other.is_affine()); + + const size_t p_words = m_curve.get_p_words(); + add_affine(other.m_x._data(), + std::min(p_words, other.m_x.size()), + other.m_y._data(), + std::min(p_words, other.m_y.size()), + workspace); +} + void EC_Point::add_affine( const word x_words[], size_t x_size, const word y_words[], size_t y_size, std::vector& ws_bn) { if((CT::all_zeros(x_words, x_size) & CT::all_zeros(y_words, y_size)).as_bool()) { @@ -240,6 +263,20 @@ m_z.swap(T0); } +void EC_Point::add(const EC_Point& other, std::vector& workspace) { + BOTAN_ARG_CHECK(m_curve == other.m_curve, "cannot add points on different curves"); + + const size_t p_words = m_curve.get_p_words(); + + add(other.m_x._data(), + std::min(p_words, other.m_x.size()), + other.m_y._data(), + std::min(p_words, other.m_y.size()), + other.m_z._data(), + std::min(p_words, other.m_z.size()), + workspace); +} + void EC_Point::add(const word x_words[], size_t x_size, const word y_words[], @@ -379,12 +416,12 @@ fe_sqr(group, T0, m_y, ws); fe_mul(group, T1, m_x, T0, ws); - T1.mod_mul(4, p, sub_ws); + fe_smul<4>(T1, p, sub_ws); if(group.a_is_zero()) { // if a == 0 then 3*x^2 + a*z^4 is just 3*x^2 fe_sqr(group, T4, m_x, ws); // x^2 - T4.mod_mul(3, p, sub_ws); // 3*x^2 + fe_smul<3>(T4, p, sub_ws); // 3*x^2 } else if(group.a_is_minus_3()) { /* if a == -3 then @@ -401,14 +438,14 @@ fe_mul(group, T4, T2, T3, ws); // (x-z^2)*(x+z^2) - T4.mod_mul(3, p, sub_ws); // 3*(x-z^2)*(x+z^2) + fe_smul<3>(T4, p, sub_ws); // 3*(x-z^2)*(x+z^2) } else { fe_sqr(group, T3, m_z, ws); // z^2 fe_sqr(group, T4, T3, ws); // z^4 fe_mul(group, T3, group.monty_a(), T4, ws); // a*z^4 fe_sqr(group, T4, m_x, ws); // x^2 - T4.mod_mul(3, p, sub_ws); + fe_smul<3>(T4, p, sub_ws); T4.mod_add(T3, p, sub_ws); // 3*x^2 + a*z^4 } @@ -417,7 +454,7 @@ T2.mod_sub(T1, p, sub_ws); fe_sqr(group, T3, T0, ws); - T3.mod_mul(8, p, sub_ws); + fe_smul<8>(T3, p, sub_ws); T1.mod_sub(T2, p, sub_ws); @@ -427,7 +464,7 @@ m_x.swap(T2); fe_mul(group, T2, m_y, m_z, ws); - T2.mod_mul(2, p, sub_ws); + fe_smul<2>(T2, p, sub_ws); m_y.swap(T0); m_z.swap(T2); @@ -441,7 +478,7 @@ } EC_Point& EC_Point::operator-=(const EC_Point& rhs) { - EC_Point minus_rhs = EC_Point(rhs).negate(); + const EC_Point minus_rhs = EC_Point(rhs).negate(); if(is_zero()) { *this = minus_rhs; @@ -465,12 +502,12 @@ EC_Point R[2] = {this->zero(), *this}; for(size_t i = scalar_bits; i > 0; i--) { - const size_t b = scalar.get_bit(i - 1); + const size_t b = scalar.get_bit(i - 1) ? 1 : 0; R[b ^ 1].add(R[b], ws); R[b].mult2(ws); } - if(scalar.is_negative()) { + if(scalar.signum() < 0) { R[0].negate(); } @@ -519,7 +556,9 @@ BigInt s_inv = invert_element(group, c[c.size() - 1], ws); - BigInt z_inv, z2_inv, z3_inv; + BigInt z_inv; + BigInt z2_inv; + BigInt z3_inv; for(size_t i = points.size() - 1; i != 0; i--) { EC_Point& point = points[i]; @@ -584,7 +623,7 @@ const auto& group = m_curve.group(); const size_t p_bytes = group.p_bytes(); secure_vector b(2 * p_bytes); - BigInt::encode_1363(&b[0], p_bytes, this->get_affine_x()); + BigInt::encode_1363(&b[0], p_bytes, this->get_affine_x()); // NOLINT(*container-data-pointer) BigInt::encode_1363(&b[p_bytes], p_bytes, this->get_affine_y()); return b; } @@ -673,12 +712,23 @@ } bool EC_Point::_is_x_eq_to_v_mod_order(const BigInt& v) const { + BOTAN_ASSERT_NOMSG(v.signum() >= 0); + if(this->is_zero()) { return false; } const auto& group = m_curve.group(); + // In this case v cannot possibly be valid, since it must be in [0..m) where + // m is the smaller of the field modulus or group order + if(v >= group.p()) { + return false; + } + if(v >= group.order()) { + return false; + } + /* * The trick used below doesn't work for curves with cofactors */ @@ -704,7 +754,8 @@ secure_vector ws; BigInt vr = v; to_rep(group, vr, ws); - BigInt z2, v_z2; + BigInt z2; + BigInt v_z2; fe_sqr(group, z2, this->get_z(), ws); fe_mul(group, v_z2, vr, z2, ws); @@ -841,7 +892,8 @@ const uint8_t pc = pt[0]; const size_t p_bytes = p.bytes(); - BigInt x, y; + BigInt x; + BigInt y; if(pc == 2 || pc == 3) { if(pt_len != 1 + p_bytes) { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/ec_point.h botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/ec_point.h --- botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/ec_point.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/ec_point.h 2026-05-07 01:38:28.000000000 +0000 @@ -14,7 +14,6 @@ #include #include -#include #include namespace Botan { @@ -25,7 +24,7 @@ * Use EC_AffinePoint in new code; this type is no longer used internally at all * except to support very unfortunate (and deprecated) curve types, specifically * those with a cofactor, or with unreasonable sizes (above 521 bits), which -* cannot be accomodated by the new faster EC library in math/pcurves. For +* cannot be accommodated by the new faster EC library in math/pcurves. For * normal curves EC_AffinePoint will typically be 2 or 3 times faster. * * This type will be completely removed in Botan4 @@ -39,7 +38,7 @@ typedef EC_Point_Format Compression_Type; using enum EC_Point_Format; - enum { WORKSPACE_SIZE = 8 }; + enum : uint8_t /* NOLINT(*-use-enum-class) */ { WORKSPACE_SIZE = 8 }; /** * Construct an uninitialized EC_Point @@ -60,7 +59,7 @@ /** * Move Constructor */ - EC_Point(EC_Point&& other) { this->swap(other); } + EC_Point(EC_Point&& other) noexcept { this->swap(other); } /** * Standard Assignment @@ -70,13 +69,15 @@ /** * Move Assignment */ - EC_Point& operator=(EC_Point&& other) { + EC_Point& operator=(EC_Point&& other) noexcept { if(this != &other) { this->swap(other); } return (*this); } + ~EC_Point() = default; + /** * Point multiplication operator * @@ -258,7 +259,7 @@ m_z.swap(new_z); } - friend void swap(EC_Point& x, EC_Point& y) { x.swap(y); } + friend void swap(EC_Point& x, EC_Point& y) noexcept { x.swap(y); } /** * Randomize the point representation @@ -271,19 +272,7 @@ * @param other the point to add to *this * @param workspace temp space, at least WORKSPACE_SIZE elements */ - void add(const EC_Point& other, std::vector& workspace) { - BOTAN_ARG_CHECK(m_curve == other.m_curve, "cannot add points on different curves"); - - const size_t p_words = m_curve.get_p_words(); - - add(other.m_x._data(), - std::min(p_words, other.m_x.size()), - other.m_y._data(), - std::min(p_words, other.m_y.size()), - other.m_z._data(), - std::min(p_words, other.m_z.size()), - workspace); - } + void add(const EC_Point& other, std::vector& workspace); /** * Point addition. Array version. @@ -313,17 +302,7 @@ * @param other affine point to add - assumed to be affine! * @param workspace temp space, at least WORKSPACE_SIZE elements */ - void add_affine(const EC_Point& other, std::vector& workspace) { - BOTAN_ASSERT_NOMSG(m_curve == other.m_curve); - BOTAN_DEBUG_ASSERT(other.is_affine()); - - const size_t p_words = m_curve.get_p_words(); - add_affine(other.m_x._data(), - std::min(p_words, other.m_x.size()), - other.m_y._data(), - std::min(p_words, other.m_y.size()), - workspace); - } + void add_affine(const EC_Point& other, std::vector& workspace); /** * Point addition - mixed J+A. Array version. diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/point_mul.cpp botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/point_mul.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/point_mul.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/point_mul.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,8 +6,10 @@ #include -#include +#include +#include #include +#include #include #include @@ -15,39 +17,41 @@ namespace { -size_t blinding_size(const BigInt& group_order) { - return (group_order.bits() + 1) / 2; +size_t blinding_size(size_t order_bits) { + return (order_bits + 1) / 2; } BigInt blinding_mask(const BigInt& group_order, RandomNumberGenerator& rng) { if(rng.is_seeded()) { - BigInt mask(rng, blinding_size(group_order)); + BigInt mask(rng, blinding_size(group_order.bits())); mask.set_bit(0); return mask; } else { - return 1; + return BigInt::one(); } } } // namespace EC_Point multi_exponentiate(const EC_Point& x, const BigInt& z1, const EC_Point& y, const BigInt& z2) { - EC_Point_Multi_Point_Precompute xy_mul(x, y); + const EC_Point_Multi_Point_Precompute xy_mul(x, y); return xy_mul.multi_exp(z1, z2); } -EC_Point_Base_Point_Precompute::EC_Point_Base_Point_Precompute(const EC_Point& base, const Modular_Reducer& mod_order) : +EC_Point_Base_Point_Precompute::EC_Point_Base_Point_Precompute(const EC_Point& base, + const Barrett_Reduction& mod_order) : m_base_point(base), m_mod_order(mod_order), m_p_words(base.get_curve().get_p_words()) { std::vector ws(EC_Point::WORKSPACE_SIZE); - const size_t order_bits = mod_order.get_modulus().bits(); + const size_t order_bits = mod_order.modulus_bits(); - const size_t T_bits = round_up(order_bits + blinding_size(mod_order.get_modulus()), WINDOW_BITS) / WINDOW_BITS; + const size_t T_bits = round_up(order_bits + blinding_size(order_bits), WindowBits) / WindowBits; - std::vector T(WINDOW_SIZE * T_bits); + std::vector T(WindowSize * T_bits); EC_Point g = base; - EC_Point g2, g4; + EC_Point g2; + EC_Point g4; for(size_t i = 0; i != T_bits; i++) { g2 = g; @@ -71,11 +75,11 @@ m_W.resize(T.size() * 2 * m_p_words); - word* p = &m_W[0]; - for(size_t i = 0; i != T.size(); ++i) { - T[i].get_x().encode_words(p, m_p_words); + word* p = m_W.data(); + for(const auto& pt : T) { + pt.get_x().encode_words(p, m_p_words); p += m_p_words; - T[i].get_y().encode_words(p, m_p_words); + pt.get_y().encode_words(p, m_p_words); p += m_p_words; } } @@ -84,7 +88,7 @@ RandomNumberGenerator& rng, const BigInt& group_order, std::vector& ws) const { - if(k.is_negative()) { + if(k.signum() < 0) { throw Invalid_Argument("EC_Point_Base_Point_Precompute scalar must be positive"); } @@ -108,7 +112,7 @@ BOTAN_DEBUG_ASSERT(scalar.bits() == group_order.bits() + 1); } - const size_t windows = round_up(scalar.bits(), WINDOW_BITS) / WINDOW_BITS; + const size_t windows = round_up(scalar.bits(), WindowBits) / WindowBits; const size_t elem_size = 2 * m_p_words; @@ -125,9 +129,9 @@ for(size_t i = 0; i != windows; ++i) { const size_t window = windows - i - 1; - const size_t base_addr = (WINDOW_SIZE * window) * elem_size; + const size_t base_addr = (WindowSize * window) * elem_size; - const word w = scalar.get_substring(WINDOW_BITS * window, WINDOW_BITS); + const word w = scalar.get_substring(WindowBits * window, WindowBits); const auto w_is_1 = CT::Mask::is_equal(w, 1); const auto w_is_2 = CT::Mask::is_equal(w, 2); @@ -149,7 +153,7 @@ Wt[j] = w1 | w2 | w3 | w4 | w5 | w6 | w7; } - R.add_affine(&Wt[0], m_p_words, &Wt[m_p_words], m_p_words, ws); + R.add_affine(Wt.data(), m_p_words, &Wt[m_p_words], m_p_words, ws); if(i == 0 && rng.is_seeded()) { /* @@ -170,7 +174,7 @@ EC_Point_Var_Point_Precompute::EC_Point_Var_Point_Precompute(const EC_Point& ipoint, RandomNumberGenerator& rng, std::vector& ws) : - m_curve(ipoint.get_curve()), m_p_words(m_curve.get_p_words()), m_window_bits(4) { + m_curve(ipoint.get_curve()), m_p_words(m_curve.get_p_words()) { if(ws.size() < EC_Point::WORKSPACE_SIZE) { ws.resize(EC_Point::WORKSPACE_SIZE); } @@ -178,7 +182,7 @@ auto point = ipoint; point.randomize_repr(rng); - std::vector U(static_cast(1) << m_window_bits); + std::vector U(static_cast(1) << WindowBits); U[0] = point.zero(); U[1] = point; @@ -197,11 +201,11 @@ m_T.resize(U.size() * 3 * m_p_words); - word* p = &m_T[0]; - for(size_t i = 0; i != U.size(); ++i) { - U[i].get_x().encode_words(p, m_p_words); - U[i].get_y().encode_words(p + m_p_words, m_p_words); - U[i].get_z().encode_words(p + 2 * m_p_words, m_p_words); + word* p = m_T.data(); + for(const auto& pt : U) { + pt.get_x().encode_words(p, m_p_words); + pt.get_y().encode_words(p + m_p_words, m_p_words); + pt.get_z().encode_words(p + 2 * m_p_words, m_p_words); p += 3 * m_p_words; } } @@ -210,7 +214,7 @@ RandomNumberGenerator& rng, const BigInt& group_order, std::vector& ws) const { - if(k.is_negative()) { + if(k.signum() < 0) { throw Invalid_Argument("EC_Point_Var_Point_Precompute scalar must be positive"); } if(ws.size() < EC_Point::WORKSPACE_SIZE) { @@ -221,16 +225,16 @@ const BigInt scalar = k + group_order * blinding_mask(group_order, rng); const size_t elem_size = 3 * m_p_words; - const size_t window_elems = static_cast(1) << m_window_bits; + const size_t window_elems = static_cast(1) << WindowBits; - size_t windows = round_up(scalar.bits(), m_window_bits) / m_window_bits; + size_t windows = round_up(scalar.bits(), WindowBits) / WindowBits; EC_Point R(m_curve); secure_vector e(elem_size); if(windows > 0) { windows--; - const uint32_t w = scalar.get_substring(windows * m_window_bits, m_window_bits); + const uint32_t w = scalar.get_substring(windows * WindowBits, WindowBits); clear_mem(e.data(), e.size()); for(size_t i = 1; i != window_elems; ++i) { @@ -241,7 +245,7 @@ } } - R.add(&e[0], m_p_words, &e[m_p_words], m_p_words, &e[2 * m_p_words], m_p_words, ws); + R.add(e.data(), m_p_words, &e[m_p_words], m_p_words, &e[2 * m_p_words], m_p_words, ws); /* Randomize after adding the first nibble as before the addition R @@ -251,10 +255,10 @@ R.randomize_repr(rng, ws[0].get_word_vector()); } - while(windows) { - R.mult2i(m_window_bits, ws); + while(windows > 0) { + R.mult2i(WindowBits, ws); - const uint32_t w = scalar.get_substring((windows - 1) * m_window_bits, m_window_bits); + const uint32_t w = scalar.get_substring((windows - 1) * WindowBits, WindowBits); clear_mem(e.data(), e.size()); for(size_t i = 1; i != window_elems; ++i) { @@ -265,7 +269,7 @@ } } - R.add(&e[0], m_p_words, &e[m_p_words], m_p_words, &e[2 * m_p_words], m_p_words, ws); + R.add(e.data(), m_p_words, &e[m_p_words], m_p_words, &e[2 * m_p_words], m_p_words, ws); windows--; } @@ -276,7 +280,7 @@ } EC_Point_Multi_Point_Precompute::EC_Point_Multi_Point_Precompute(const EC_Point& x, const EC_Point& y) { - if(x.on_the_curve() == false || y.on_the_curve() == false) { + if(!x.on_the_curve() || !y.on_the_curve()) { m_M.push_back(x.zero()); return; } @@ -350,7 +354,7 @@ const uint32_t z12 = (4 * z2_b) + z1_b; // This function is not intended to be const time - if(z12) { + if(z12 != 0) { if(m_no_infinity) { H.add_affine(m_M[z12 - 1], ws); } else { @@ -359,7 +363,7 @@ } } - if(z1.is_negative() != z2.is_negative()) { + if((z1.signum() < 0) != (z2.signum() < 0)) { H.negate(); } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/point_mul.h botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/point_mul.h --- botan3-3.7.1+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/point_mul.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ec_group/legacy_ec_point/point_mul.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,11 +11,11 @@ namespace Botan { -class Modular_Reducer; +class Barrett_Reduction; class EC_Point_Base_Point_Precompute final { public: - EC_Point_Base_Point_Precompute(const EC_Point& base_point, const Modular_Reducer& mod_order); + EC_Point_Base_Point_Precompute(const EC_Point& base_point, const Barrett_Reduction& mod_order); EC_Point mul(const BigInt& k, RandomNumberGenerator& rng, @@ -24,11 +24,10 @@ private: const EC_Point& m_base_point; - const Modular_Reducer& m_mod_order; + const Barrett_Reduction& m_mod_order; - enum { WINDOW_BITS = 3 }; - - enum { WINDOW_SIZE = (1 << WINDOW_BITS) - 1 }; + static constexpr size_t WindowBits = 3; + static constexpr size_t WindowSize = (1 << WindowBits) - 1; const size_t m_p_words; @@ -48,9 +47,10 @@ std::vector& ws) const; private: + static constexpr size_t WindowBits = 4; + const CurveGFp m_curve; const size_t m_p_words; - const size_t m_window_bits; /* * Table of 2^window_bits * 3*2*p_word words diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ecc_key/ec_key_data.cpp botan3-3.12.0+dfsg/src/lib/pubkey/ecc_key/ec_key_data.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/ecc_key/ec_key_data.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ecc_key/ec_key_data.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,19 +6,28 @@ #include +#include #include namespace Botan { -EC_PublicKey_Data::EC_PublicKey_Data(EC_Group group, std::span bytes) : - m_group(std::move(group)), m_point(m_group, bytes) { +EC_PublicKey_Data::EC_PublicKey_Data(EC_Group group, EC_AffinePoint pt) : + m_group(std::move(group)), m_point(std::move(pt)) { #if defined(BOTAN_HAS_LEGACY_EC_POINT) m_legacy_point = m_point.to_legacy_point(); #endif + + // Checking that the point lies on the curve is done in the deserialization + // of EC_AffinePoint. + BOTAN_ARG_CHECK(!m_point.is_identity(), "ECC public key cannot be point at infinity"); } EC_PrivateKey_Data::EC_PrivateKey_Data(EC_Group group, EC_Scalar x) : - m_group(std::move(group)), m_scalar(std::move(x)), m_legacy_x(m_scalar.to_bigint()) {} + m_group(std::move(group)), m_scalar(std::move(x)), m_legacy_x(m_scalar.to_bigint()) { + // Checking that the scalar is lower than the group order is ensured in the + // deserialization of the EC_Scalar or during the random generation respectively. + BOTAN_ARG_CHECK(m_scalar.is_nonzero(), "ECC private key cannot be zero"); +} namespace { @@ -33,7 +42,7 @@ * not have their high bit set and so can be encoded in 65 bytes, vs 66 * bytes for the full order. * - * To accomodate this, zero prefix the key if we see such a short input + * To accommodate this, zero prefix the key if we see such a short input */ secure_vector padded_sk(order_bytes); copy_mem(std::span{padded_sk}.last(bytes.size()), bytes); @@ -49,19 +58,20 @@ } // namespace -EC_PrivateKey_Data::EC_PrivateKey_Data(EC_Group group, std::span bytes) : - m_group(std::move(group)), - m_scalar(decode_ec_secret_key_scalar(m_group, bytes)), - m_legacy_x(m_scalar.to_bigint()) {} +EC_PrivateKey_Data::EC_PrivateKey_Data(const EC_Group& group, std::span bytes) : + Botan::EC_PrivateKey_Data(group, decode_ec_secret_key_scalar(group, bytes)) {} + +EC_PrivateKey_Data::~EC_PrivateKey_Data() { + m_scalar.zeroize(); +} std::shared_ptr EC_PrivateKey_Data::public_key(RandomNumberGenerator& rng, bool with_modular_inverse) const { auto public_point = [&] { - std::vector ws; if(with_modular_inverse) { - return EC_AffinePoint::g_mul(m_scalar.invert(), rng, ws); + return EC_AffinePoint::g_mul(m_scalar.invert(), rng); } else { - return EC_AffinePoint::g_mul(m_scalar, rng, ws); + return EC_AffinePoint::g_mul(m_scalar, rng); } }; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ecc_key/ec_key_data.h botan3-3.12.0+dfsg/src/lib/pubkey/ecc_key/ec_key_data.h --- botan3-3.7.1+dfsg/src/lib/pubkey/ecc_key/ec_key_data.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ecc_key/ec_key_data.h 2026-05-07 01:38:28.000000000 +0000 @@ -23,13 +23,10 @@ class EC_PublicKey_Data final { public: - EC_PublicKey_Data(EC_Group group, EC_AffinePoint pt) : m_group(std::move(group)), m_point(std::move(pt)) { -#if defined(BOTAN_HAS_LEGACY_EC_POINT) - m_legacy_point = m_point.to_legacy_point(); -#endif - } + EC_PublicKey_Data(EC_Group group, EC_AffinePoint pt); - EC_PublicKey_Data(EC_Group group, std::span bytes); + EC_PublicKey_Data(const EC_Group& group, std::span bytes) : + EC_PublicKey_Data(group, EC_AffinePoint(group, bytes)) {} const EC_Group& group() const { return m_group; } @@ -51,7 +48,13 @@ public: EC_PrivateKey_Data(EC_Group group, EC_Scalar x); - EC_PrivateKey_Data(EC_Group group, std::span bytes); + EC_PrivateKey_Data(const EC_Group& group, std::span bytes); + + EC_PrivateKey_Data(const EC_PrivateKey_Data&) = default; + EC_PrivateKey_Data(EC_PrivateKey_Data&&) = default; + EC_PrivateKey_Data& operator=(const EC_PrivateKey_Data&) = default; + EC_PrivateKey_Data& operator=(EC_PrivateKey_Data&&) = default; + ~EC_PrivateKey_Data(); std::shared_ptr public_key(RandomNumberGenerator& rng, bool with_modular_inverse) const; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ecc_key/ecc_key.cpp botan3-3.12.0+dfsg/src/lib/pubkey/ecc_key/ecc_key.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/ecc_key/ecc_key.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ecc_key/ecc_key.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,5 @@ /* -* ECC Key implemenation +* ECC Key implementation * (C) 2007 Manuel Hartl, FlexSecure GmbH * Falko Strenzke, FlexSecure GmbH * 2008-2010 Jack Lloyd @@ -9,6 +9,7 @@ #include +#include #include #include #include @@ -43,21 +44,21 @@ } // namespace #if defined(BOTAN_HAS_LEGACY_EC_POINT) -EC_PublicKey::EC_PublicKey(EC_Group group, const EC_Point& pub_point) { +EC_PublicKey::EC_PublicKey(const EC_Group& group, const EC_Point& pub_point) { auto pt = EC_AffinePoint(group, pub_point); - m_public_key = std::make_shared(std::move(group), std::move(pt)); - m_domain_encoding = default_encoding_for(domain()); + m_public_key = std::make_shared(group, std::move(pt)); + m_domain_encoding = default_encoding_for(domain()); // NOLINT(*-prefer-member-initializer) } #endif -EC_PublicKey::EC_PublicKey(EC_Group group, EC_AffinePoint pub_point) { - m_public_key = std::make_shared(std::move(group), std::move(pub_point)); - m_domain_encoding = default_encoding_for(domain()); +EC_PublicKey::EC_PublicKey(const EC_Group& group, const EC_AffinePoint& pub_point) { + m_public_key = std::make_shared(group, pub_point); + m_domain_encoding = default_encoding_for(domain()); // NOLINT(*-prefer-member-initializer) } EC_PublicKey::EC_PublicKey(const AlgorithmIdentifier& alg_id, std::span key_bits) { m_public_key = std::make_shared(EC_Group(alg_id.parameters()), key_bits); - m_domain_encoding = default_encoding_for(domain()); + m_domain_encoding = default_encoding_for(domain()); // NOLINT(*-prefer-member-initializer) } const EC_Group& EC_PublicKey::domain() const { @@ -128,24 +129,27 @@ * EC_PrivateKey constructor */ EC_PrivateKey::EC_PrivateKey(RandomNumberGenerator& rng, - EC_Group ec_group, + const EC_Group& ec_group, const BigInt& x, - bool with_modular_inverse) { + bool with_modular_inverse) : + m_with_modular_inverse(with_modular_inverse) { auto scalar = (x.is_zero()) ? EC_Scalar::random(ec_group, rng) : EC_Scalar::from_bigint(ec_group, x); - m_private_key = std::make_shared(std::move(ec_group), std::move(scalar)); + m_private_key = std::make_shared(ec_group, std::move(scalar)); m_public_key = m_private_key->public_key(rng, with_modular_inverse); m_domain_encoding = default_encoding_for(domain()); } -EC_PrivateKey::EC_PrivateKey(RandomNumberGenerator& rng, EC_Group ec_group, bool with_modular_inverse) { +EC_PrivateKey::EC_PrivateKey(RandomNumberGenerator& rng, const EC_Group& ec_group, bool with_modular_inverse) : + m_with_modular_inverse(with_modular_inverse) { auto scalar = EC_Scalar::random(ec_group, rng); - m_private_key = std::make_shared(std::move(ec_group), std::move(scalar)); + m_private_key = std::make_shared(ec_group, std::move(scalar)); m_public_key = m_private_key->public_key(rng, with_modular_inverse); m_domain_encoding = default_encoding_for(domain()); } -EC_PrivateKey::EC_PrivateKey(EC_Group ec_group, EC_Scalar x, bool with_modular_inverse) { - m_private_key = std::make_shared(std::move(ec_group), std::move(x)); +EC_PrivateKey::EC_PrivateKey(const EC_Group& ec_group, const EC_Scalar& x, bool with_modular_inverse) : + m_with_modular_inverse(with_modular_inverse) { + m_private_key = std::make_shared(ec_group, x); m_public_key = m_private_key->public_key(with_modular_inverse); m_domain_encoding = default_encoding_for(domain()); } @@ -171,20 +175,22 @@ EC_PrivateKey::EC_PrivateKey(const AlgorithmIdentifier& alg_id, std::span key_bits, - bool with_modular_inverse) { - EC_Group group(alg_id.parameters()); + bool with_modular_inverse) : + m_with_modular_inverse(with_modular_inverse) { + const EC_Group group(alg_id.parameters()); OID key_parameters; secure_vector private_key_bits; secure_vector public_key_bits; - BER_Decoder(key_bits) + BER_Decoder(key_bits, BER_Decoder::Limits::DER()) .start_sequence() .decode_and_check(1, "Unknown version code for ECC key") .decode(private_key_bits, ASN1_Type::OctetString) .decode_optional(key_parameters, ASN1_Type(0), ASN1_Class::ExplicitContextSpecific) .decode_optional_string(public_key_bits, ASN1_Type::BitString, 1, ASN1_Class::ExplicitContextSpecific) - .end_cons(); + .end_cons() + .verify_end(); m_private_key = std::make_shared(group, private_key_bits); @@ -202,7 +208,14 @@ return false; } - return EC_PublicKey::check_key(rng, strong); + if(!EC_PublicKey::check_key(rng, strong)) { + return false; + } + + // Verify that the public key is consistent with the private key. + // For ECKCDSA/ECGDSA the derivation is g^(x^-1), for all others it is g^x. + auto expected = m_private_key->public_key(m_with_modular_inverse); + return expected->public_key() == _public_ec_point(); } const BigInt& EC_PublicKey::get_int_field(std::string_view field) const { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ecc_key/ecc_key.h botan3-3.12.0+dfsg/src/lib/pubkey/ecc_key/ecc_key.h --- botan3-3.7.1+dfsg/src/lib/pubkey/ecc_key/ecc_key.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ecc_key/ecc_key.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,12 +10,16 @@ #ifndef BOTAN_ECC_PUBLIC_KEY_BASE_H_ #define BOTAN_ECC_PUBLIC_KEY_BASE_H_ -#include +#include #include #include namespace Botan { +class EC_AffinePoint; +class EC_Point; +class EC_Group; +class EC_Scalar; class EC_PublicKey_Data; class EC_PrivateKey_Data; @@ -112,7 +116,7 @@ * @param group EC domain parameters * @param pub_point public point on the curve */ - EC_PublicKey(EC_Group group, const EC_Point& pub_point); + EC_PublicKey(const EC_Group& group, const EC_Point& pub_point); #endif /** @@ -121,7 +125,7 @@ * @param group EC domain parameters * @param public_key public point on the curve */ - EC_PublicKey(EC_Group group, EC_AffinePoint public_key); + EC_PublicKey(const EC_Group& group, const EC_AffinePoint& public_key); /** * Load a public key. @@ -132,9 +136,9 @@ EC_PublicKey() = default; - std::shared_ptr m_public_key; - EC_Group_Encoding m_domain_encoding = EC_Group_Encoding::NamedCurve; - EC_Point_Format m_point_encoding = EC_Point_Format::Uncompressed; + std::shared_ptr m_public_key; // NOLINT(*non-private-member-variable*) + EC_Group_Encoding m_domain_encoding = EC_Group_Encoding::NamedCurve; // NOLINT(*non-private-member-variable*) + EC_Point_Format m_point_encoding = EC_Point_Format::Uncompressed; // NOLINT(*non-private-member-variable*) }; /** @@ -181,7 +185,10 @@ * TODO: Remove, once the respective deprecated constructors of the * concrete ECC algorithms is removed. */ - EC_PrivateKey(RandomNumberGenerator& rng, EC_Group group, const BigInt& x, bool with_modular_inverse = false); + EC_PrivateKey(RandomNumberGenerator& rng, + const EC_Group& group, + const BigInt& x, + bool with_modular_inverse = false); /** * Creates a new private key @@ -190,7 +197,7 @@ * multiplying the base point with the modular inverse of x (as in ECGDSA * and ECKCDSA), otherwise by multiplying directly with x (as in ECDSA). */ - EC_PrivateKey(RandomNumberGenerator& rng, EC_Group group, bool with_modular_inverse = false); + EC_PrivateKey(RandomNumberGenerator& rng, const EC_Group& group, bool with_modular_inverse = false); /** * Load a EC private key from the secret scalar @@ -199,7 +206,7 @@ * multiplying the base point with the modular inverse of x (as in ECGDSA * and ECKCDSA), otherwise by multiplying directly with x (as in ECDSA). */ - EC_PrivateKey(EC_Group group, EC_Scalar scalar, bool with_modular_inverse = false); + EC_PrivateKey(const EC_Group& group, const EC_Scalar& scalar, bool with_modular_inverse = false); /* * Creates a new private key object from the @@ -214,9 +221,10 @@ std::span key_bits, bool with_modular_inverse = false); - EC_PrivateKey() = default; + EC_PrivateKey() : m_with_modular_inverse(false) {} - std::shared_ptr m_private_key; + std::shared_ptr m_private_key; // NOLINT(*non-private-member-variable*) + bool m_with_modular_inverse; // NOLINT(*non-private-member-variable*) }; BOTAN_DIAGNOSTIC_POP diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ecdh/ecdh.cpp botan3-3.12.0+dfsg/src/lib/pubkey/ecdh/ecdh.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/ecdh/ecdh.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ecdh/ecdh.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,5 @@ /* -* ECDH implemenation +* ECDH implementation * (C) 2007 Manuel Hartl, FlexSecure GmbH * 2007 Falko Strenzke, FlexSecure GmbH * 2008-2010 Jack Lloyd @@ -9,6 +9,8 @@ #include +#include +#include #include namespace Botan { @@ -33,20 +35,33 @@ size_t agreed_value_size() const override { return m_group.get_p_bytes(); } secure_vector raw_agree(const uint8_t w[], size_t w_len) override { - if(m_group.has_cofactor()) { + const auto input_point = [&] { + if(m_group.has_cofactor()) { #if defined(BOTAN_HAS_LEGACY_EC_POINT) - EC_AffinePoint input_point(m_group, m_group.get_cofactor() * m_group.OS2ECP(w, w_len)); - return input_point.mul_x_only(m_l_times_priv, m_rng, m_ws); + return EC_AffinePoint(m_group, m_group.get_cofactor() * m_group.OS2ECP(w, w_len)); #else - throw Not_Implemented("Support for DH with cofactor adjustment not available in this build configuration"); + throw Not_Implemented( + "Support for DH with cofactor adjustment not available in this build configuration"); #endif - } else { - if(auto input_point = EC_AffinePoint::deserialize(m_group, {w, w_len})) { - return input_point->mul_x_only(m_l_times_priv, m_rng, m_ws); } else { - throw Decoding_Error("ECDH - Invalid elliptic curve point"); + if(auto point = EC_AffinePoint::deserialize(m_group, {w, w_len})) { + return *point; + } else { + throw Decoding_Error("ECDH - Invalid elliptic curve point: not on curve"); + } } + }(); + + // Typical specs (such as BSI's TR-03111 Section 4.3.1) require that + // we check the resulting point of the multiplication to not be the + // point at infinity. However, since we ensure that our ECC private + // scalar can never be zero, checking the peer's input point is + // equivalent. + if(input_point.is_identity()) { + throw Decoding_Error("ECDH - Invalid elliptic curve point: identity"); } + + return input_point.mul_x_only(m_l_times_priv, m_rng); } private: @@ -65,7 +80,6 @@ const EC_Group m_group; const EC_Scalar m_l_times_priv; RandomNumberGenerator& m_rng; - std::vector m_ws; }; } // namespace diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ecdh/ecdh.h botan3-3.12.0+dfsg/src/lib/pubkey/ecdh/ecdh.h --- botan3-3.7.1+dfsg/src/lib/pubkey/ecdh/ecdh.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ecdh/ecdh.h 2026-05-07 01:38:28.000000000 +0000 @@ -75,8 +75,8 @@ BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE class BOTAN_PUBLIC_API(2, 0) ECDH_PrivateKey final : public ECDH_PublicKey, - public EC_PrivateKey, - public PK_Key_Agreement_Key { + public virtual EC_PrivateKey, + public virtual PK_Key_Agreement_Key { public: /** * Load a private key. @@ -91,14 +91,14 @@ * @param group curve parameters to bu used for this key * @param x the private key */ - ECDH_PrivateKey(EC_Group group, EC_Scalar x) : EC_PrivateKey(std::move(group), std::move(x)) {} + ECDH_PrivateKey(const EC_Group& group, const EC_Scalar& x) : EC_PrivateKey(group, x) {} /** * Create a new private key * @param rng a random number generator * @param group parameters to used for this key */ - ECDH_PrivateKey(RandomNumberGenerator& rng, EC_Group group) : EC_PrivateKey(rng, std::move(group)) {} + ECDH_PrivateKey(RandomNumberGenerator& rng, const EC_Group& group) : EC_PrivateKey(rng, group) {} /** * Generate a new private key @@ -112,10 +112,12 @@ std::unique_ptr public_key() const override; + // NOLINTNEXTLINE(bugprone-derived-method-shadowing-base-method) std::vector public_value() const override { return ECDH_PublicKey::public_value(EC_Point_Format::Uncompressed); } + // NOLINTNEXTLINE(bugprone-derived-method-shadowing-base-method) std::vector public_value(EC_Point_Format type) const { return ECDH_PublicKey::public_value(type); } std::unique_ptr create_key_agreement_op(RandomNumberGenerator& rng, diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ecdsa/ecdsa.cpp botan3-3.12.0+dfsg/src/lib/pubkey/ecdsa/ecdsa.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/ecdsa/ecdsa.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ecdsa/ecdsa.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,5 @@ /* -* ECDSA implemenation +* ECDSA implementation * (C) 2007 Manuel Hartl, FlexSecure GmbH * 2007 Falko Strenzke, FlexSecure GmbH * 2008-2010,2015,2016,2018,2024 Jack Lloyd @@ -10,6 +10,7 @@ #include +#include #include #include @@ -38,7 +39,7 @@ } const uint8_t y_odd = v % 2; - const uint8_t add_order = v >> 1; + const bool add_order = (v >> 1) == 0x01; const size_t p_bytes = group.get_p_bytes(); BigInt x = r; @@ -60,7 +61,7 @@ const auto r_inv = EC_Scalar::from_bigint(group, r).invert_vartime(); - EC_Group::Mul2Table GR_mul(R.value()); + const EC_Group::Mul2Table GR_mul(R.value()); if(auto egsr = GR_mul.mul2_vartime(ne * r_inv, ss * r_inv)) { return egsr.value(); } @@ -76,6 +77,10 @@ const EC_Group& group, const std::vector& msg, const BigInt& r, const BigInt& s, uint8_t v) : EC_PublicKey(group, recover_ecdsa_public_key(group, msg, r, s, v)) {} +std::optional ECDSA_PublicKey::_signature_element_size_for_DER_encoding() const { + return domain().get_order_bytes(); +} + std::unique_ptr ECDSA_PublicKey::generate_another(RandomNumberGenerator& rng) const { return std::make_unique(rng, domain()); } @@ -125,8 +130,7 @@ PK_Ops::Signature_with_Hash(padding), m_group(ecdsa.domain()), m_x(ecdsa._private_key()), - m_b(EC_Scalar::random(m_group, rng)), - m_b_inv(m_b.invert()) { + m_b(EC_Scalar::random(m_group, rng)) { #if defined(BOTAN_HAS_RFC6979_GENERATOR) m_rfc6979 = std::make_unique( this->rfc6979_hash_function(), m_group.get_order_bits(), ecdsa._private_key()); @@ -147,10 +151,7 @@ std::unique_ptr m_rfc6979; #endif - std::vector m_ws; - EC_Scalar m_b; - EC_Scalar m_b_inv; }; AlgorithmIdentifier ECDSA_Signature_Operation::algorithm_identifier() const { @@ -168,20 +169,37 @@ const auto k = EC_Scalar::random(m_group, rng); #endif - const auto r = EC_Scalar::gk_x_mod_order(k, rng, m_ws); - - // Blind the inversion of k - const auto k_inv = (m_b * k).invert() * m_b; - /* - * Blind the input message and compute x*r+m as (x*r*b + m*b)/b + * Blind the inputs + * + * Here we are computing (x*r+m)/k + * + * Instead have a random b and compute (k*b)^-1 + * + * Then compute (x*r+m) as (x*r*b + m*b) + * + * Finally (x*r*b + m*b)/(k*b) = (x*r+m)/k + * + * This effectively blinds both the inversion as well as the various scalar + * multiplications. All of these operations should be constant-time anyway but + * blinding is very cheap and may help if either the compiler introduces + * variable-time behavior, or for the case of EM/power side channel attacks [1]. + * + * [1] But note that such attacks are currently outside of Botan's threat model. + * + * NOTE: if you change anything here also update ECDSA_Timing_Test + * in cli/timing_tests.cpp to use the same formulas */ - m_b.square_self(); - m_b_inv.square_self(); + const auto r = EC_Scalar::gk_x_mod_order(k, rng); + + const auto k_inv = (m_b * k).invert(); const auto xr_m = ((m_x * m_b) * r) + (m * m_b); - const auto s = (k_inv * xr_m) * m_b_inv; + const auto s = (k_inv * xr_m); + + // Generate the next blinding value via modular squaring + m_b.square_self(); // With overwhelming probability, a bug rather than actual zero r/s if(r.is_zero() || s.is_zero()) { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ecdsa/ecdsa.h botan3-3.12.0+dfsg/src/lib/pubkey/ecdsa/ecdsa.h --- botan3-3.7.1+dfsg/src/lib/pubkey/ecdsa/ecdsa.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ecdsa/ecdsa.h 2026-05-07 01:38:28.000000000 +0000 @@ -48,8 +48,8 @@ * See SEC section 4.6.1 * @param group the elliptic curve group * @param msg the message - * @param r the r paramter of the signature - * @param s the s paramter of the signature + * @param r the r parameter of the signature + * @param s the s parameter of the signature * @param v the recovery ID */ ECDSA_PublicKey( @@ -61,9 +61,7 @@ */ std::string algo_name() const override { return "ECDSA"; } - std::optional _signature_element_size_for_DER_encoding() const override { - return domain().get_order_bytes(); - } + std::optional _signature_element_size_for_DER_encoding() const override; bool supports_operation(PublicKeyOperation op) const override { return (op == PublicKeyOperation::Signature); } @@ -104,14 +102,14 @@ * @param group curve parameters to bu used for this key * @param x the private key */ - ECDSA_PrivateKey(EC_Group group, EC_Scalar x) : EC_PrivateKey(std::move(group), std::move(x)) {} + ECDSA_PrivateKey(const EC_Group& group, const EC_Scalar& x) : EC_PrivateKey(group, x) {} /** * Create a new private key * @param rng a random number generator * @param group parameters to used for this key */ - ECDSA_PrivateKey(RandomNumberGenerator& rng, EC_Group group) : EC_PrivateKey(rng, std::move(group)) {} + ECDSA_PrivateKey(RandomNumberGenerator& rng, const EC_Group& group) : EC_PrivateKey(rng, group) {} /** * Create a private key. @@ -123,7 +121,7 @@ ECDSA_PrivateKey(RandomNumberGenerator& rng, const EC_Group& group, const BigInt& x) : EC_PrivateKey(rng, group, x) {} - bool check_key(RandomNumberGenerator& rng, bool) const override; + bool check_key(RandomNumberGenerator& rng, bool strong) const override; std::unique_ptr public_key() const override; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ecgdsa/ecgdsa.cpp botan3-3.12.0+dfsg/src/lib/pubkey/ecgdsa/ecgdsa.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/ecgdsa/ecgdsa.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ecgdsa/ecgdsa.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include +#include #include #include @@ -36,8 +37,8 @@ */ class ECGDSA_Signature_Operation final : public PK_Ops::Signature_with_Hash { public: - ECGDSA_Signature_Operation(const ECGDSA_PrivateKey& ecgdsa, std::string_view emsa) : - PK_Ops::Signature_with_Hash(emsa), m_group(ecgdsa.domain()), m_x(ecgdsa._private_key()) {} + ECGDSA_Signature_Operation(const ECGDSA_PrivateKey& ecgdsa, std::string_view hash_fn) : + PK_Ops::Signature_with_Hash(hash_fn), m_group(ecgdsa.domain()), m_x(ecgdsa._private_key()) {} std::vector raw_sign(std::span msg, RandomNumberGenerator& rng) override; @@ -48,7 +49,6 @@ private: const EC_Group m_group; const EC_Scalar m_x; - std::vector m_ws; }; AlgorithmIdentifier ECGDSA_Signature_Operation::algorithm_identifier() const { @@ -62,7 +62,7 @@ const auto k = EC_Scalar::random(m_group, rng); - const auto r = EC_Scalar::gk_x_mod_order(k, rng, m_ws); + const auto r = EC_Scalar::gk_x_mod_order(k, rng); const auto s = m_x * ((k * r) - m); @@ -113,6 +113,10 @@ } // namespace +std::optional ECGDSA_PublicKey::_signature_element_size_for_DER_encoding() const { + return domain().get_order_bytes(); +} + std::unique_ptr ECGDSA_PublicKey::generate_another(RandomNumberGenerator& rng) const { return std::make_unique(rng, domain()); } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ecgdsa/ecgdsa.h botan3-3.12.0+dfsg/src/lib/pubkey/ecgdsa/ecgdsa.h --- botan3-3.7.1+dfsg/src/lib/pubkey/ecgdsa/ecgdsa.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ecgdsa/ecgdsa.h 2026-05-07 01:38:28.000000000 +0000 @@ -47,9 +47,7 @@ */ std::string algo_name() const override { return "ECGDSA"; } - std::optional _signature_element_size_for_DER_encoding() const override { - return domain().get_order_bytes(); - } + std::optional _signature_element_size_for_DER_encoding() const override; std::unique_ptr generate_another(RandomNumberGenerator& rng) const final; @@ -88,14 +86,14 @@ * @param group curve parameters to bu used for this key * @param x the private key */ - ECGDSA_PrivateKey(EC_Group group, EC_Scalar x) : EC_PrivateKey(std::move(group), std::move(x), true) {} + ECGDSA_PrivateKey(const EC_Group& group, const EC_Scalar& x) : EC_PrivateKey(group, x, true) {} /** * Create a new private key * @param rng a random number generator * @param group parameters to used for this key */ - ECGDSA_PrivateKey(RandomNumberGenerator& rng, EC_Group group) : EC_PrivateKey(rng, std::move(group), true) {} + ECGDSA_PrivateKey(RandomNumberGenerator& rng, const EC_Group& group) : EC_PrivateKey(rng, group, true) {} /** * Generate a new private key. @@ -109,7 +107,7 @@ std::unique_ptr public_key() const override; - bool check_key(RandomNumberGenerator& rng, bool) const override; + bool check_key(RandomNumberGenerator& rng, bool strong) const override; std::unique_ptr create_signature_op(RandomNumberGenerator& rng, std::string_view params, diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ecies/ecies.cpp botan3-3.12.0+dfsg/src/lib/pubkey/ecies/ecies.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/ecies/ecies.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ecies/ecies.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -13,11 +13,10 @@ #include #include #include -#include #include +#include #include #include -#include namespace Botan { @@ -25,15 +24,18 @@ /** * Private key type for ECIES_ECDH_KA_Operation +* +* TODO(Botan4) this can be removed once cofactor support is removed from ECDH */ BOTAN_DIAGNOSTIC_PUSH BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE -class ECIES_PrivateKey final : public EC_PrivateKey, - public PK_Key_Agreement_Key { +class ECIES_PrivateKey final : public virtual EC_PrivateKey, + public virtual PK_Key_Agreement_Key { public: explicit ECIES_PrivateKey(const ECDH_PrivateKey& private_key) : + // NOLINTNEXTLINE(*-slicing) EC_PublicKey(private_key), EC_PrivateKey(private_key), PK_Key_Agreement_Key(), m_key(private_key) {} std::vector public_value() const override { return m_key.public_value(); } @@ -60,6 +62,8 @@ /** * Implements ECDH key agreement without using the cofactor mode +* +* TODO(Botan4) this can be removed once cofactor support is removed from ECDH */ class ECIES_ECDH_KA_Operation final : public PK_Ops::Key_Agreement_with_KDF { public: @@ -71,7 +75,7 @@ secure_vector raw_agree(const uint8_t w[], size_t w_len) override { const EC_Group& group = m_key.domain(); if(auto input_point = EC_AffinePoint::deserialize(group, {w, w_len})) { - return input_point->mul(m_key._private_key(), m_rng, m_ws).x_bytes(); + return input_point->mul(m_key._private_key(), m_rng).x_bytes(); } else { throw Decoding_Error("ECIES - Invalid elliptic curve point"); } @@ -80,7 +84,6 @@ private: ECIES_PrivateKey m_key; RandomNumberGenerator& m_rng; - std::vector m_ws; }; std::unique_ptr ECIES_PrivateKey::create_key_agreement_op(RandomNumberGenerator& rng, @@ -97,6 +100,8 @@ * @param ecies_params settings for ecies * @param for_encryption disable cofactor mode if the secret will be used for encryption * (according to ISO 18033 cofactor mode is only used during decryption) +* +* TODO(Botan4) this entire function can be removed once cofactor support is gone */ PK_Key_Agreement create_key_agreement(const PK_Key_Agreement_Key& private_key, const ECIES_KA_Params& ecies_params, @@ -113,7 +118,7 @@ throw Invalid_Argument("ECIES: cofactor, old cofactor and check mode are only supported for ECDH_PrivateKey"); } - if(ecdh_key && (for_encryption || !ecies_params.cofactor_mode())) { + if(ecdh_key != nullptr && (for_encryption || !ecies_params.cofactor_mode())) { // ECDH_KA_Operation uses cofactor mode: use own key agreement method if cofactor should not be used. return PK_Key_Agreement(ECIES_PrivateKey(*ecdh_key), rng, "Raw"); } @@ -138,13 +143,14 @@ throw Invalid_Argument("ECIES: other public key point is zero"); } - auto kdf = KDF::create_or_throw(m_params.kdf_spec()); + auto kdf = KDF::create_or_throw(m_params.kdf()); EC_Point other_point = other_public_key_point; // ISO 18033: step b - if(m_params.old_cofactor_mode() && m_params.domain().has_cofactor()) { - other_point *= m_params.domain().get_cofactor(); + // TODO(Botan4) remove when cofactor support is removed + if(m_params.old_cofactor_mode() && m_params.group().has_cofactor()) { + other_point *= m_params.group().get_cofactor(); } secure_vector derivation_input; @@ -155,11 +161,11 @@ } // ISO 18033: encryption step f / decryption step h - std::vector other_public_key_bin = other_point.encode(m_params.compression_type()); + std::vector other_public_key_bin = other_point.encode(m_params.point_format()); // Note: the argument `m_params.secret_length()` passed for `key_len` will only be used by providers because // "Raw" is passed to the `PK_Key_Agreement` if the implementation of botan is used. const SymmetricKey peh = - m_ka.derive_key(m_params.domain().get_order_bytes(), other_public_key_bin.data(), other_public_key_bin.size()); + m_ka.derive_key(m_params.group().get_order_bytes(), other_public_key_bin.data(), other_public_key_bin.size()); derivation_input.insert(derivation_input.end(), peh.begin(), peh.end()); // ISO 18033: encryption step g / decryption step i @@ -174,16 +180,18 @@ const EC_AffinePoint& other_public_key_point) const { BOTAN_ARG_CHECK(!other_public_key_point.is_identity(), "ECIES: peer public key point is the identity element"); - auto kdf = KDF::create_or_throw(m_params.kdf_spec()); + auto kdf = KDF::create_or_throw(m_params.kdf()); auto other_point = other_public_key_point; + const auto& group = m_params.group(); + // ISO 18033: step b - if(m_params.old_cofactor_mode() && m_params.domain().has_cofactor()) { - std::vector ws; + // TODO(Botan4) remove when cofactor support is removed + if(m_params.old_cofactor_mode() && group.has_cofactor()) { Null_RNG null_rng; - auto cofactor = EC_Scalar::from_bigint(m_params.domain(), m_params.domain().get_cofactor()); - other_point = other_point.mul(cofactor, null_rng, ws); + auto cofactor = EC_Scalar::from_bigint(group, group.get_cofactor()); + other_point = other_point.mul(cofactor, null_rng); } secure_vector derivation_input; @@ -194,33 +202,48 @@ } // ISO 18033: encryption step f / decryption step h - std::vector other_public_key_bin = other_point.serialize(m_params.compression_type()); + std::vector other_public_key_bin = other_point.serialize(m_params.point_format()); // Note: the argument `m_params.secret_length()` passed for `key_len` will only be used by providers because // "Raw" is passed to the `PK_Key_Agreement` if the implementation of botan is used. const SymmetricKey peh = - m_ka.derive_key(m_params.domain().get_order_bytes(), other_public_key_bin.data(), other_public_key_bin.size()); + m_ka.derive_key(m_params.group().get_order_bytes(), other_public_key_bin.data(), other_public_key_bin.size()); derivation_input.insert(derivation_input.end(), peh.begin(), peh.end()); // ISO 18033: encryption step g / decryption step i return SymmetricKey(kdf->derive_key(m_params.secret_length(), derivation_input)); } -ECIES_KA_Params::ECIES_KA_Params(const EC_Group& domain, - std::string_view kdf_spec, - size_t length, - EC_Point_Format compression_type, - ECIES_Flags flags) : - m_domain(domain), m_kdf_spec(kdf_spec), m_length(length), m_compression_mode(compression_type), m_flags(flags) {} +ECIES_KA_Params::ECIES_KA_Params( + const EC_Group& group, std::string_view kdf, size_t length, EC_Point_Format point_format, ECIES_Flags flags) : + m_group(group), + m_kdf(kdf), + m_length(length), + m_point_format(point_format), + m_single_hash_mode((flags & ECIES_Flags::SingleHashMode) == ECIES_Flags::SingleHashMode), + m_check_mode((flags & ECIES_Flags::CheckMode) == ECIES_Flags::CheckMode), + m_cofactor_mode((flags & ECIES_Flags::CofactorMode) == ECIES_Flags::CofactorMode), + m_old_cofactor_mode((flags & ECIES_Flags::OldCofactorMode) == ECIES_Flags::OldCofactorMode) {} + +ECIES_KA_Params::ECIES_KA_Params( + const EC_Group& group, std::string_view kdf, size_t length, EC_Point_Format point_format, bool single_hash_mode) : + m_group(group), + m_kdf(kdf), + m_length(length), + m_point_format(point_format), + m_single_hash_mode(single_hash_mode), + m_check_mode(true), + m_cofactor_mode(false), + m_old_cofactor_mode(false) {} -ECIES_System_Params::ECIES_System_Params(const EC_Group& domain, - std::string_view kdf_spec, +ECIES_System_Params::ECIES_System_Params(const EC_Group& group, + std::string_view kdf, std::string_view dem_algo_spec, size_t dem_key_len, std::string_view mac_spec, size_t mac_key_len, - EC_Point_Format compression_type, + EC_Point_Format point_format, ECIES_Flags flags) : - ECIES_KA_Params(domain, kdf_spec, dem_key_len + mac_key_len, compression_type, flags), + ECIES_KA_Params(group, kdf, dem_key_len + mac_key_len, point_format, flags), m_dem_spec(dem_algo_spec), m_dem_keylen(dem_key_len), m_mac_spec(mac_spec), @@ -231,20 +254,19 @@ } } -ECIES_System_Params::ECIES_System_Params(const EC_Group& domain, - std::string_view kdf_spec, +ECIES_System_Params::ECIES_System_Params(const EC_Group& group, + std::string_view kdf, std::string_view dem_algo_spec, size_t dem_key_len, std::string_view mac_spec, - size_t mac_key_len) : - ECIES_System_Params(domain, - kdf_spec, - dem_algo_spec, - dem_key_len, - mac_spec, - mac_key_len, - EC_Point_Format::Uncompressed, - ECIES_Flags::None) {} + size_t mac_key_len, + EC_Point_Format point_format, + bool single_hash_mode) : + ECIES_KA_Params(group, kdf, dem_key_len + mac_key_len, point_format, single_hash_mode), + m_dem_spec(dem_algo_spec), + m_dem_keylen(dem_key_len), + m_mac_spec(mac_spec), + m_mac_keylen(mac_key_len) {} std::unique_ptr ECIES_System_Params::create_mac() const { return MessageAuthenticationCode::create_or_throw(m_mac_spec); @@ -262,15 +284,12 @@ RandomNumberGenerator& rng) : m_ka(private_key, ecies_params, true, rng), m_params(ecies_params), - m_eph_public_key_bin(private_key.public_value()), // returns the uncompressed public key, see conversion below - m_iv(), - m_other_point(), - m_label() { - if(ecies_params.compression_type() != EC_Point_Format::Uncompressed) { + m_eph_public_key_bin(private_key.public_value()) { + if(ecies_params.point_format() != EC_Point_Format::Uncompressed) { // ISO 18033: step d // convert only if necessary; m_eph_public_key_bin has been initialized with the uncompressed format m_eph_public_key_bin = - EC_AffinePoint(m_params.domain(), m_eph_public_key_bin).serialize(ecies_params.compression_type()); + EC_AffinePoint(m_params.group(), m_eph_public_key_bin).serialize(ecies_params.point_format()); } m_mac = m_params.create_mac(); m_cipher = m_params.create_cipher(Cipher_Dir::Encryption); @@ -280,7 +299,7 @@ * ECIES_Encryptor Constructor */ ECIES_Encryptor::ECIES_Encryptor(RandomNumberGenerator& rng, const ECIES_System_Params& ecies_params) : - ECIES_Encryptor(ECDH_PrivateKey(rng, ecies_params.domain()), ecies_params, rng) {} + ECIES_Encryptor(ECDH_PrivateKey(rng, ecies_params.group()), ecies_params, rng) {} size_t ECIES_Encryptor::maximum_input_size() const { /* @@ -333,14 +352,19 @@ ECIES_Decryptor::ECIES_Decryptor(const PK_Key_Agreement_Key& key, const ECIES_System_Params& ecies_params, RandomNumberGenerator& rng) : - m_ka(key, ecies_params, false, rng), m_params(ecies_params), m_iv(), m_label() { - // ISO 18033: "If v > 1 and CheckMode = 0, then we must have gcd(u, v) = 1." (v = index, u= order) - if(!ecies_params.check_mode()) { - const BigInt& cofactor = m_params.domain().get_cofactor(); - if(cofactor > 1 && gcd(cofactor, m_params.domain().get_order()) != 1) { - throw Invalid_Argument("ECIES: gcd of cofactor and order must be 1 if check_mode is 0"); - } - } + m_ka(key, ecies_params, false, rng), m_params(ecies_params) { + /* + ISO 18033: "If v > 1 and CheckMode = 0, then we must have gcd(u, v) = 1." (v = index, u= order) + + We skip this check because even if CheckMode = 0 we actually do check that + the point is valid. In addition the check from ISO 18033 is pretty odd; u is + the _prime_ order subgroup, and v is the cofactor. For gcd(u, v) > 1 to occur + the cofactor would have to be a multiple of the group order, implying that + the overall group was at least the square of the group order. Such a curve + would also break our assumption that one can check for membership in the + prime order subgroup by multiplying by the group order and checking for the + identity. + */ m_mac = m_params.create_mac(); m_cipher = m_params.create_cipher(Cipher_Dir::Decryption); @@ -360,7 +384,7 @@ } // namespace size_t ECIES_Decryptor::plaintext_length(size_t ctext_len) const { - const size_t point_size = compute_point_size(m_params.domain(), m_params.compression_type()); + const size_t point_size = compute_point_size(m_params.group(), m_params.point_format()); const size_t overhead = point_size + m_mac->output_length(); if(ctext_len < overhead) { @@ -374,7 +398,7 @@ * ECIES Decryption according to ISO 18033-2 */ secure_vector ECIES_Decryptor::do_decrypt(uint8_t& valid_mask, const uint8_t in[], size_t in_len) const { - const size_t point_size = compute_point_size(m_params.domain(), m_params.compression_type()); + const size_t point_size = compute_point_size(m_params.group(), m_params.point_format()); if(in_len < point_size + m_mac->output_length()) { throw Decoding_Error("ECIES decryption: ciphertext is too short"); @@ -386,7 +410,7 @@ const std::vector mac_data(in + in_len - m_mac->output_length(), in + in_len); // ISO 18033: step a - auto other_public_key = EC_AffinePoint(m_params.domain(), other_public_key_bin); + auto other_public_key = EC_AffinePoint(m_params.group(), other_public_key_bin); // ISO 18033: step b would check if other_public_key is on the curve iff check_mode is on // but we ignore this and always check if the point is on the curve @@ -402,9 +426,9 @@ m_mac->update(m_label); } const secure_vector calculated_mac = m_mac->final(); - valid_mask = CT::is_equal(mac_data.data(), calculated_mac.data(), mac_data.size()).value(); + valid_mask = CT::is_equal(mac_data, calculated_mac).value(); - if(valid_mask) { + if(valid_mask == 0xFF) { // decrypt data m_cipher->set_key(SymmetricKey(secret_key.begin(), m_params.dem_keylen())); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ecies/ecies.h botan3-3.12.0+dfsg/src/lib/pubkey/ecies/ecies.h --- botan3-3.7.1+dfsg/src/lib/pubkey/ecies/ecies.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ecies/ecies.h 2026-05-07 01:38:28.000000000 +0000 @@ -19,6 +19,7 @@ #include #include #include +#include #include #if defined(BOTAN_HAS_LEGACY_EC_POINT) @@ -29,15 +30,29 @@ class RandomNumberGenerator; -enum class ECIES_Flags : uint32_t { +/** +* Flags controlling ECIES operation +* +* Two of the flags are related to how cofactors are handled. +* Support for cofactors is deprecated and will be removed in Botan4. +* +* The CheckMode flag is completely ignored; we always check that the point is +* valid. +* +* TODO(Botan4) remove this enum +*/ +enum class ECIES_Flags : uint8_t { None = 0, /// if set: prefix the input of the (ecdh) key agreement with the encoded (ephemeral) public key SingleHashMode = 1, /// (decryption only) if set: use cofactor multiplication during (ecdh) key agreement + /// This only matters if the curve has a cofactor CofactorMode = 2, - /// if set: use ecdhc instead of ecdh + /// if set: use ecdhc instead of ecdh. + /// This only matters if the curve has a cofactor OldCofactorMode = 4, /// (decryption only) if set: test if the (ephemeral) public key is on the curve + /// Note that we actually ignore this flag and always check the key CheckMode = 8, NONE BOTAN_DEPRECATED("Use None") = None, @@ -62,89 +77,130 @@ class BOTAN_PUBLIC_API(2, 0) ECIES_KA_Params { public: /** - * @param domain ec domain parameters of the involved ec keys + * @param group ec domain parameters of the involved ec keys + * @param kdf_spec name of the key derivation function + * @param length length of the secret to be derived + * @param point_format format of encoded keys (affects the secret derivation if single_hash_mode is used) + * @param single_hash_mode prefix the KDF input with the ephemeral public key (recommended) + */ + ECIES_KA_Params(const EC_Group& group, + std::string_view kdf_spec, + size_t length, + EC_Point_Format point_format = EC_Point_Format::Uncompressed, + bool single_hash_mode = true); + + /** + * @param group ec domain parameters of the involved ec keys * @param kdf_spec name of the key derivation function * @param length length of the secret to be derived - * @param compression_type format of encoded keys (affects the secret derivation if single_hash_mode is used) + * @param point_format format of encoded keys (affects the secret derivation if single_hash_mode is used) * @param flags options, see documentation of ECIES_Flags + * + * This constructor makes sense only if you are using the CofactorMode or + * OldCofactorMode flags. Support for cofactors in EC_Group is deprecated + * and will be removed in Botan4. + * + * TODO(Botan4) remove this constructor when cofactor support is removed */ - ECIES_KA_Params(const EC_Group& domain, + BOTAN_DEPRECATED("Prefer other constructor, see header comment") + ECIES_KA_Params(const EC_Group& group, std::string_view kdf_spec, size_t length, - EC_Point_Format compression_type, + EC_Point_Format point_format, ECIES_Flags flags); ECIES_KA_Params(const ECIES_KA_Params&) = default; + ECIES_KA_Params(ECIES_KA_Params&&) = default; ECIES_KA_Params& operator=(const ECIES_KA_Params&) = delete; + ECIES_KA_Params& operator=(ECIES_KA_Params&&) = delete; virtual ~ECIES_KA_Params() = default; - inline const EC_Group& domain() const { return m_domain; } + const EC_Group& group() const { return m_group; } - inline size_t secret_length() const { return m_length; } + size_t secret_length() const { return m_length; } - inline bool single_hash_mode() const { - return (m_flags & ECIES_Flags::SingleHashMode) == ECIES_Flags::SingleHashMode; - } + bool single_hash_mode() const { return m_single_hash_mode; } - inline bool cofactor_mode() const { return (m_flags & ECIES_Flags::CofactorMode) == ECIES_Flags::CofactorMode; } + // TODO(Botan4) remove this when cofactor support is removed + bool cofactor_mode() const { return m_cofactor_mode; } - inline bool old_cofactor_mode() const { - return (m_flags & ECIES_Flags::OldCofactorMode) == ECIES_Flags::OldCofactorMode; - } + // TODO(Botan4) remove this when cofactor support is removed + bool old_cofactor_mode() const { return m_old_cofactor_mode; } + + // TODO(Botan4) remove this when cofactor support is removed + bool check_mode() const { return m_check_mode; } - inline bool check_mode() const { return (m_flags & ECIES_Flags::CheckMode) == ECIES_Flags::CheckMode; } + EC_Point_Format point_format() const { return m_point_format; } - inline EC_Point_Format compression_type() const { return m_compression_mode; } + const std::string& kdf() const { return m_kdf; } - const std::string& kdf_spec() const { return m_kdf_spec; } + BOTAN_DEPRECATED("Use kdf") const std::string& kdf_spec() const { return kdf(); } + + BOTAN_DEPRECATED("Use group") const EC_Group& domain() const { return group(); } + + BOTAN_DEPRECATED("Use point_format") EC_Point_Format compression_type() const { return point_format(); } private: - const EC_Group m_domain; - const std::string m_kdf_spec; + const EC_Group m_group; + const std::string m_kdf; const size_t m_length; - const EC_Point_Format m_compression_mode; - const ECIES_Flags m_flags; + const EC_Point_Format m_point_format; + const bool m_single_hash_mode; + const bool m_check_mode; // TODO(Botan4) remove this field + const bool m_cofactor_mode; // TODO(Botan4) remove this field + const bool m_old_cofactor_mode; // TODO(Botan4) remove this field }; class BOTAN_PUBLIC_API(2, 0) ECIES_System_Params final : public ECIES_KA_Params { public: /** - * @param domain ec domain parameters of the involved ec keys + * @param group ec domain parameters of the involved ec keys * @param kdf_spec name of the key derivation function * @param dem_algo_spec name of the data encryption method * @param dem_key_len length of the key used for the data encryption method * @param mac_spec name of the message authentication code * @param mac_key_len length of the key used for the message authentication code */ - ECIES_System_Params(const EC_Group& domain, + ECIES_System_Params(const EC_Group& group, std::string_view kdf_spec, std::string_view dem_algo_spec, size_t dem_key_len, std::string_view mac_spec, - size_t mac_key_len); + size_t mac_key_len, + EC_Point_Format point_format = EC_Point_Format::Uncompressed, + bool single_hash_mode = false); /** - * @param domain ec domain parameters of the involved ec keys + * @param group ec domain parameters of the involved ec keys * @param kdf_spec name of the key derivation function * @param dem_algo_spec name of the data encryption method * @param dem_key_len length of the key used for the data encryption method * @param mac_spec name of the message authentication code * @param mac_key_len length of the key used for the message authentication code - * @param compression_type format of encoded keys (affects the secret derivation if single_hash_mode is used) + * @param point_format format of encoded keys (affects the secret derivation if single_hash_mode is used) * @param flags options, see documentation of ECIES_Flags + * + * This constructor makes sense only if you are using the CofactorMode or + * OldCofactorMode flags. Support for cofactors in EC_Group is deprecated + * and will be removed in Botan4. + * + * TODO(Botan4) remove this constructor when cofactor support is removed */ - ECIES_System_Params(const EC_Group& domain, + BOTAN_DEPRECATED("Prefer other constructor, see header comment") + ECIES_System_Params(const EC_Group& group, std::string_view kdf_spec, std::string_view dem_algo_spec, size_t dem_key_len, std::string_view mac_spec, size_t mac_key_len, - EC_Point_Format compression_type, + EC_Point_Format point_format, ECIES_Flags flags); ECIES_System_Params(const ECIES_System_Params&) = default; + ECIES_System_Params(ECIES_System_Params&&) = default; ECIES_System_Params& operator=(const ECIES_System_Params&) = delete; + ECIES_System_Params& operator=(ECIES_System_Params&&) = delete; ~ECIES_System_Params() override = default; /// creates an instance of the message authentication code @@ -154,10 +210,10 @@ std::unique_ptr create_cipher(Cipher_Dir direction) const; /// returns the length of the key used by the data encryption method - inline size_t dem_keylen() const { return m_dem_keylen; } + size_t dem_keylen() const { return m_dem_keylen; } /// returns the length of the key used by the message authentication code - inline size_t mac_keylen() const { return m_mac_keylen; } + size_t mac_keylen() const { return m_mac_keylen; } private: const std::string m_dem_spec; @@ -168,8 +224,6 @@ /** * ECIES secret derivation according to ISO 18033-2 -* -* TODO(Botan4) hide this */ class BOTAN_PUBLIC_API(2, 0) ECIES_KA_Operation { public: @@ -180,7 +234,6 @@ * (according to ISO 18033 cofactor mode is only used during decryption) * @param rng the RNG to use */ - BOTAN_DEPRECATED("Deprecated no replacement") ECIES_KA_Operation(const PK_Key_Agreement_Key& private_key, const ECIES_KA_Params& ecies_params, bool for_encryption, @@ -232,8 +285,8 @@ #if defined(BOTAN_HAS_LEGACY_EC_POINT) /// Set the public key of the other party - inline void set_other_key(const EC_Point& public_point) { - m_other_point = EC_AffinePoint(m_params.domain(), public_point); + void set_other_key(const EC_Point& public_point) { + m_other_point = EC_AffinePoint(m_params.group(), public_point); } #endif @@ -247,7 +300,7 @@ void set_label(std::string_view label) { m_label.assign(label.begin(), label.end()); } private: - std::vector enc(const uint8_t data[], size_t length, RandomNumberGenerator&) const override; + std::vector enc(const uint8_t data[], size_t length, RandomNumberGenerator& rng) const override; size_t maximum_input_size() const override; @@ -278,10 +331,10 @@ RandomNumberGenerator& rng); /// Set the initialization vector for the data encryption method - inline void set_initialization_vector(const InitializationVector& iv) { m_iv = iv; } + void set_initialization_vector(const InitializationVector& iv) { m_iv = iv; } /// Set the label which is appended to the input for the message authentication code - inline void set_label(std::string_view label) { m_label = std::vector(label.begin(), label.end()); } + void set_label(std::string_view label) { m_label = std::vector(label.begin(), label.end()); } private: secure_vector do_decrypt(uint8_t& valid_mask, const uint8_t in[], size_t in_len) const override; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/eckcdsa/eckcdsa.cpp botan3-3.12.0+dfsg/src/lib/pubkey/eckcdsa/eckcdsa.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/eckcdsa/eckcdsa.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/eckcdsa/eckcdsa.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,14 +9,16 @@ #include +#include #include +#include #include +#include #include #include #include #include #include -#include namespace Botan { @@ -43,7 +45,7 @@ return hash; } - SCAN_Name req(padding); + const SCAN_Name req(padding); if(req.algo_name() == "EMSA1" && req.arg_count() == 1) { if(auto hash = HashFunction::create(req.arg(0))) { @@ -150,7 +152,6 @@ const EC_Scalar m_x; std::unique_ptr m_hash; std::vector m_prefix; - std::vector m_ws; bool m_prefix_used; }; @@ -165,7 +166,7 @@ // We cannot use gk_x_mod_order because ECKCDSA, unlike ECDSA or ECGDSA, does // not reduce the x coordinate modulo the group order. - m_hash->update(EC_AffinePoint::g_mul(k, rng, m_ws).x_bytes()); + m_hash->update(EC_AffinePoint::g_mul(k, rng).x_bytes()); auto c = m_hash->final_stdvec(); truncate_hash_if_needed(c, m_group.get_order_bytes()); @@ -260,6 +261,10 @@ } // namespace +std::optional ECKCDSA_PublicKey::_signature_element_size_for_DER_encoding() const { + return domain().get_order_bytes(); +} + std::unique_ptr ECKCDSA_PublicKey::generate_another(RandomNumberGenerator& rng) const { return std::make_unique(rng, domain()); } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/eckcdsa/eckcdsa.h botan3-3.12.0+dfsg/src/lib/pubkey/eckcdsa/eckcdsa.h --- botan3-3.7.1+dfsg/src/lib/pubkey/eckcdsa/eckcdsa.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/eckcdsa/eckcdsa.h 2026-05-07 01:38:28.000000000 +0000 @@ -46,9 +46,7 @@ */ std::string algo_name() const override { return "ECKCDSA"; } - std::optional _signature_element_size_for_DER_encoding() const override { - return domain().get_order_bytes(); - } + std::optional _signature_element_size_for_DER_encoding() const override; std::unique_ptr generate_another(RandomNumberGenerator& rng) const final; @@ -87,14 +85,14 @@ * @param group curve parameters to bu used for this key * @param x the private key */ - ECKCDSA_PrivateKey(EC_Group group, EC_Scalar x) : EC_PrivateKey(std::move(group), std::move(x), true) {} + ECKCDSA_PrivateKey(const EC_Group& group, const EC_Scalar& x) : EC_PrivateKey(group, x, true) {} /** * Create a new private key * @param rng a random number generator * @param group parameters to used for this key */ - ECKCDSA_PrivateKey(RandomNumberGenerator& rng, EC_Group group) : EC_PrivateKey(rng, std::move(group), true) {} + ECKCDSA_PrivateKey(RandomNumberGenerator& rng, const EC_Group& group) : EC_PrivateKey(rng, group, true) {} /** * Create a private key. @@ -106,7 +104,7 @@ ECKCDSA_PrivateKey(RandomNumberGenerator& rng, const EC_Group& group, const BigInt& x) : EC_PrivateKey(rng, group, x, true) {} - bool check_key(RandomNumberGenerator& rng, bool) const override; + bool check_key(RandomNumberGenerator& rng, bool strong) const override; std::unique_ptr public_key() const override; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ed25519/ed25519.cpp botan3-3.12.0+dfsg/src/lib/pubkey/ed25519/ed25519.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/ed25519/ed25519.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ed25519/ed25519.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,26 +10,23 @@ #include -#include -#include +#include #include -#include namespace Botan { -void ed25519_gen_keypair(uint8_t* pk, uint8_t* sk, const uint8_t seed[32]) { +void ed25519_gen_keypair(uint8_t pk[32], uint8_t sk[64], const uint8_t seed[32]) { uint8_t az[64]; - SHA_512 sha; - sha.update(seed, 32); - sha.final(az); + auto sha512 = HashFunction::create_or_throw("SHA-512"); + sha512->update(seed, 32); + sha512->final(az); az[0] &= 248; az[31] &= 63; az[31] |= 64; - ge_scalarmult_base(pk, az); + ed25519_basepoint_mul(std::span{pk, 32}, az); - // todo copy_mem copy_mem(sk, seed, 32); copy_mem(sk + 32, pk, 32); } @@ -44,27 +41,27 @@ uint8_t nonce[64]; uint8_t hram[64]; - SHA_512 sha; + auto sha512 = HashFunction::create_or_throw("SHA-512"); - sha.update(sk, 32); - sha.final(az); + sha512->update(sk, 32); + sha512->final(az); az[0] &= 248; az[31] &= 63; az[31] |= 64; - sha.update(domain_sep, domain_sep_len); - sha.update(az + 32, 32); - sha.update(m, mlen); - sha.final(nonce); + sha512->update(domain_sep, domain_sep_len); + sha512->update(az + 32, 32); + sha512->update(m, mlen); + sha512->final(nonce); sc_reduce(nonce); - ge_scalarmult_base(sig, nonce); + ed25519_basepoint_mul(std::span{sig, 32}, nonce); - sha.update(domain_sep, domain_sep_len); - sha.update(sig, 32); - sha.update(sk + 32, 32); - sha.update(m, mlen); - sha.final(hram); + sha512->update(domain_sep, domain_sep_len); + sha512->update(sig, 32); + sha512->update(sk + 32, 32); + sha512->update(m, mlen); + sha512->final(hram); sc_reduce(hram); sc_muladd(sig + 32, hram, az, nonce); @@ -76,15 +73,7 @@ const uint8_t* pk, const uint8_t domain_sep[], size_t domain_sep_len) { - uint8_t h[64]; - uint8_t rcheck[32]; - ge_p3 A; - SHA_512 sha; - - if(sig[63] & 224) { - return false; - } - if(ge_frombytes_negate_vartime(&A, pk) != 0) { + if((sig[63] & 0xE0) != 0x00) { return false; } @@ -114,16 +103,17 @@ } } - sha.update(domain_sep, domain_sep_len); - sha.update(sig, 32); - sha.update(pk, 32); - sha.update(m, mlen); - sha.final(h); - sc_reduce(h); + uint8_t h[64]; + auto sha512 = HashFunction::create_or_throw("SHA-512"); - ge_double_scalarmult_vartime(rcheck, h, &A, sig + 32); + sha512->update(domain_sep, domain_sep_len); + sha512->update(sig, 32); + sha512->update(pk, 32); + sha512->update(m, mlen); + sha512->final(h); + sc_reduce(h); - return CT::is_equal(rcheck, sig, 32).as_bool(); + return signature_check(std::span{pk, 32}, h, sig, sig + 32); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ed25519/ed25519.h botan3-3.12.0+dfsg/src/lib/pubkey/ed25519/ed25519.h --- botan3-3.7.1+dfsg/src/lib/pubkey/ed25519/ed25519.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ed25519/ed25519.h 2026-05-07 01:38:28.000000000 +0000 @@ -1,9 +1,7 @@ /* * Ed25519 * (C) 2017 Ribose Inc -* -* Based on the public domain code from SUPERCOP ref10 by -* Peter Schwabe, Daniel J. Bernstein, Niels Duif, Tanja Lange, Bo-Yin Yang +* 2025 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -12,6 +10,7 @@ #define BOTAN_ED25519_H_ #include +#include namespace Botan { @@ -35,7 +34,9 @@ bool supports_operation(PublicKeyOperation op) const override { return (op == PublicKeyOperation::Signature); } - const std::vector& get_public_key() const { return m_public; } + BOTAN_DEPRECATED("Use raw_public_key_bits") const std::vector& get_public_key() const { + return m_public; + } /** * Create a Ed25519 Public Key. @@ -44,7 +45,8 @@ */ Ed25519_PublicKey(const AlgorithmIdentifier& alg_id, std::span key_bits); - Ed25519_PublicKey(std::span pub) : Ed25519_PublicKey(pub.data(), pub.size()) {} + BOTAN_FUTURE_EXPLICIT Ed25519_PublicKey(std::span pub) : + Ed25519_PublicKey(pub.data(), pub.size()) {} Ed25519_PublicKey(const uint8_t pub_key[], size_t len); @@ -56,7 +58,7 @@ protected: Ed25519_PublicKey() = default; - std::vector m_public; + std::vector m_public; // NOLINT(*non-private-member-variable*) }; BOTAN_DIAGNOSTIC_PUSH @@ -73,16 +75,41 @@ Ed25519_PrivateKey(const AlgorithmIdentifier& alg_id, std::span key_bits); /** - * Generate a private key. + * Generate a new random private key. * @param rng the RNG to use */ explicit Ed25519_PrivateKey(RandomNumberGenerator& rng); /** * Construct a private key from the specified parameters. + * * @param secret_key the private key + * + * The behavior of this function depends on the input length. + * + * If the input is 32 bytes long then it is treated as a seed, and a new + * keypair is generated. + * + * If the input is 64 bytes long then it is treated as a pair of 32 byte + * values, first the private key and then the public key. + * + * This constructor is deprecated since the above behavior is + * quite surprising. If you are relying on it, please comment in #4666. + */ + BOTAN_DEPRECATED("Use from_seed or from_bytes") explicit Ed25519_PrivateKey(std::span secret_key); + + /** + * Generate a new Ed25519_PrivateKey from the provided 32-byte seed + */ + static Ed25519_PrivateKey from_seed(std::span seed); + + /** + * Decode the Ed25519_PrivateKey from the provided 64-byte value + * + * The first 32 bytes are the private key and the last 32 bytes + * are the precomputed public key. */ - explicit Ed25519_PrivateKey(const secure_vector& secret_key); + static Ed25519_PrivateKey from_bytes(std::span bytes); BOTAN_DEPRECATED("Use raw_private_key_bits") const secure_vector& get_private_key() const { return m_private; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ed25519/ed25519_fe.cpp botan3-3.12.0+dfsg/src/lib/pubkey/ed25519/ed25519_fe.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/ed25519/ed25519_fe.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ed25519/ed25519_fe.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -15,66 +15,57 @@ namespace Botan { //static -FE_25519 FE_25519::invert(const FE_25519& z) { - FE_25519 t0; - FE_25519 t1; - FE_25519 t2; - FE_25519 t3; - - fe_sq(t0, z); - fe_sq_iter(t1, t0, 2); - fe_mul(t1, z, t1); - fe_mul(t0, t0, t1); - fe_sq(t2, t0); - fe_mul(t1, t1, t2); - fe_sq_iter(t2, t1, 5); - fe_mul(t1, t2, t1); - fe_sq_iter(t2, t1, 10); - fe_mul(t2, t2, t1); - fe_sq_iter(t3, t2, 20); - fe_mul(t2, t3, t2); - fe_sq_iter(t2, t2, 10); - fe_mul(t1, t2, t1); - fe_sq_iter(t2, t1, 50); - fe_mul(t2, t2, t1); - fe_sq_iter(t3, t2, 100); - fe_mul(t2, t3, t2); - fe_sq_iter(t2, t2, 50); - fe_mul(t1, t2, t1); - fe_sq_iter(t1, t1, 5); +Ed25519_FieldElement Ed25519_FieldElement::invert() const { + auto t0 = this->sqr(); + auto t1 = t0.sqr_iter(2); + t1 = *this * t1; + t0 = t0 * t1; + auto t2 = t0.sqr(); + t1 = t1 * t2; + t2 = t1.sqr_iter(5); + t1 = t2 * t1; + t2 = t1.sqr_iter(10); + t2 = t2 * t1; + auto t3 = t2.sqr_iter(20); + t2 = t3 * t2; + t2 = t2.sqr_iter(10); + t1 = t2 * t1; + t2 = t1.sqr_iter(50); + t2 = t2 * t1; + t3 = t2.sqr_iter(100); + t2 = t3 * t2; + t2 = t2.sqr_iter(50); + t1 = t2 * t1; + t1 = t1.sqr_iter(5); - fe_mul(t0, t1, t0); + t0 = t1 * t0; return t0; } -FE_25519 FE_25519::pow_22523(const fe& z) { - FE_25519 t0; - FE_25519 t1; - FE_25519 t2; - - fe_sq(t0, z); - fe_sq_iter(t1, t0, 2); - fe_mul(t1, z, t1); - fe_mul(t0, t0, t1); - fe_sq(t0, t0); - fe_mul(t0, t1, t0); - fe_sq_iter(t1, t0, 5); - fe_mul(t0, t1, t0); - fe_sq_iter(t1, t0, 10); - fe_mul(t1, t1, t0); - fe_sq_iter(t2, t1, 20); - fe_mul(t1, t2, t1); - fe_sq_iter(t1, t1, 10); - fe_mul(t0, t1, t0); - fe_sq_iter(t1, t0, 50); - fe_mul(t1, t1, t0); - fe_sq_iter(t2, t1, 100); - fe_mul(t1, t2, t1); - fe_sq_iter(t1, t1, 50); - fe_mul(t0, t1, t0); - fe_sq_iter(t0, t0, 2); +Ed25519_FieldElement Ed25519_FieldElement::pow_22523() const { + auto t0 = this->sqr(); + auto t1 = t0.sqr_iter(2); + t1 = (*this) * t1; + t0 = t0 * t1; + t0 = t0.sqr(); + t0 = t1 * t0; + t1 = t0.sqr_iter(5); + t0 = t1 * t0; + t1 = t0.sqr_iter(10); + t1 = t1 * t0; + auto t2 = t1.sqr_iter(20); + t1 = t2 * t1; + t1 = t1.sqr_iter(10); + t0 = t1 * t0; + t1 = t0.sqr_iter(50); + t1 = t1 * t0; + t2 = t1.sqr_iter(100); + t1 = t2 * t1; + t1 = t1.sqr_iter(50); + t0 = t1 * t0; + t0 = t0.sqr_iter(2); - fe_mul(t0, t0, z); + t0 = t0 * (*this); return t0; } @@ -111,28 +102,28 @@ */ //static -FE_25519 FE_25519::mul(const FE_25519& f, const FE_25519& g) { - const int32_t f0 = f[0]; - const int32_t f1 = f[1]; - const int32_t f2 = f[2]; - const int32_t f3 = f[3]; - const int32_t f4 = f[4]; - const int32_t f5 = f[5]; - const int32_t f6 = f[6]; - const int32_t f7 = f[7]; - const int32_t f8 = f[8]; - const int32_t f9 = f[9]; - - const int32_t g0 = g[0]; - const int32_t g1 = g[1]; - const int32_t g2 = g[2]; - const int32_t g3 = g[3]; - const int32_t g4 = g[4]; - const int32_t g5 = g[5]; - const int32_t g6 = g[6]; - const int32_t g7 = g[7]; - const int32_t g8 = g[8]; - const int32_t g9 = g[9]; +Ed25519_FieldElement Ed25519_FieldElement::mul(const Ed25519_FieldElement& f, const Ed25519_FieldElement& g) { + const int32_t f0 = f.m_fe[0]; + const int32_t f1 = f.m_fe[1]; + const int32_t f2 = f.m_fe[2]; + const int32_t f3 = f.m_fe[3]; + const int32_t f4 = f.m_fe[4]; + const int32_t f5 = f.m_fe[5]; + const int32_t f6 = f.m_fe[6]; + const int32_t f7 = f.m_fe[7]; + const int32_t f8 = f.m_fe[8]; + const int32_t f9 = f.m_fe[9]; + + const int32_t g0 = g.m_fe[0]; + const int32_t g1 = g.m_fe[1]; + const int32_t g2 = g.m_fe[2]; + const int32_t g3 = g.m_fe[3]; + const int32_t g4 = g.m_fe[4]; + const int32_t g5 = g.m_fe[5]; + const int32_t g6 = g.m_fe[6]; + const int32_t g7 = g.m_fe[7]; + const int32_t g8 = g.m_fe[8]; + const int32_t g9 = g.m_fe[9]; const int32_t g1_19 = 19 * g1; /* 1.959375*2^29 */ const int32_t g2_19 = 19 * g2; /* 1.959375*2^30; still ok */ @@ -313,7 +304,7 @@ /* |h0| <= 2^25; from now on fits into int32 unchanged */ /* |h1| <= 1.01*2^24 */ - return FE_25519(h0, h1, h2, h3, h4, h5, h6, h7, h8, h9); + return Ed25519_FieldElement(h0, h1, h2, h3, h4, h5, h6, h7, h8, h9); } /* @@ -332,17 +323,17 @@ */ //static -FE_25519 FE_25519::sqr_iter(const FE_25519& f, size_t iter) { - int32_t f0 = f[0]; - int32_t f1 = f[1]; - int32_t f2 = f[2]; - int32_t f3 = f[3]; - int32_t f4 = f[4]; - int32_t f5 = f[5]; - int32_t f6 = f[6]; - int32_t f7 = f[7]; - int32_t f8 = f[8]; - int32_t f9 = f[9]; +Ed25519_FieldElement Ed25519_FieldElement::sqr_iter(size_t iter) const { + int32_t f0 = m_fe[0]; + int32_t f1 = m_fe[1]; + int32_t f2 = m_fe[2]; + int32_t f3 = m_fe[3]; + int32_t f4 = m_fe[4]; + int32_t f5 = m_fe[5]; + int32_t f6 = m_fe[6]; + int32_t f7 = m_fe[7]; + int32_t f8 = m_fe[8]; + int32_t f9 = m_fe[9]; for(size_t i = 0; i != iter; ++i) { const int32_t f0_2 = 2 * f0; @@ -453,7 +444,7 @@ f9 = static_cast(h9); } - return FE_25519(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9); + return Ed25519_FieldElement(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9); } /* @@ -472,17 +463,18 @@ */ //static -FE_25519 FE_25519::sqr2(const FE_25519& f) { - const int32_t f0 = f[0]; - const int32_t f1 = f[1]; - const int32_t f2 = f[2]; - const int32_t f3 = f[3]; - const int32_t f4 = f[4]; - const int32_t f5 = f[5]; - const int32_t f6 = f[6]; - const int32_t f7 = f[7]; - const int32_t f8 = f[8]; - const int32_t f9 = f[9]; +Ed25519_FieldElement Ed25519_FieldElement::sqr2() const { + const int32_t f0 = m_fe[0]; + const int32_t f1 = m_fe[1]; + const int32_t f2 = m_fe[2]; + const int32_t f3 = m_fe[3]; + const int32_t f4 = m_fe[4]; + const int32_t f5 = m_fe[5]; + const int32_t f6 = m_fe[6]; + const int32_t f7 = m_fe[7]; + const int32_t f8 = m_fe[8]; + const int32_t f9 = m_fe[9]; + const int32_t f0_2 = 2 * f0; const int32_t f1_2 = 2 * f1; const int32_t f2_2 = 2 * f2; @@ -590,14 +582,13 @@ carry<25, 19>(h9, h0); carry<26>(h0, h1); - return FE_25519(h0, h1, h2, h3, h4, h5, h6, h7, h8, h9); + return Ed25519_FieldElement(h0, h1, h2, h3, h4, h5, h6, h7, h8, h9); } /* Ignores top bit of h. */ - -void FE_25519::from_bytes(const uint8_t s[32]) { +Ed25519_FieldElement Ed25519_FieldElement::deserialize(const uint8_t s[32]) { int64_t h0 = load_4(s); int64_t h1 = load_3(s + 4) << 6; int64_t h2 = load_3(s + 7) << 5; @@ -621,16 +612,7 @@ carry<26>(h6, h7); carry<26>(h8, h9); - m_fe[0] = static_cast(h0); - m_fe[1] = static_cast(h1); - m_fe[2] = static_cast(h2); - m_fe[3] = static_cast(h3); - m_fe[4] = static_cast(h4); - m_fe[5] = static_cast(h5); - m_fe[6] = static_cast(h6); - m_fe[7] = static_cast(h7); - m_fe[8] = static_cast(h8); - m_fe[9] = static_cast(h9); + return Ed25519_FieldElement(h0, h1, h2, h3, h4, h5, h6, h7, h8, h9); } /* @@ -658,8 +640,8 @@ so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q. */ -void FE_25519::to_bytes(uint8_t s[32]) const { - const int64_t X25 = (1 << 25); +void Ed25519_FieldElement::serialize_to(std::span s) const { + const int32_t X25 = (1 << 25); int32_t h0 = m_fe[0]; int32_t h1 = m_fe[1]; @@ -671,9 +653,8 @@ int32_t h7 = m_fe[7]; int32_t h8 = m_fe[8]; int32_t h9 = m_fe[9]; - int32_t q; - q = (19 * h9 + ((static_cast(1) << 24))) >> 25; + int32_t q = (19 * h9 + ((static_cast(1) << 24))) >> 25; q = (h0 + q) >> 26; q = (h1 + q) >> 25; q = (h2 + q) >> 26; @@ -699,7 +680,7 @@ carry0<25>(h7, h8); carry0<26>(h8, h9); - int32_t carry9 = h9 >> 25; + const int32_t carry9 = h9 >> 25; h9 -= carry9 * X25; /* h10 = carry9 */ diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ed25519/ed25519_fe.h botan3-3.12.0+dfsg/src/lib/pubkey/ed25519/ed25519_fe.h --- botan3-3.7.1+dfsg/src/lib/pubkey/ed25519/ed25519_fe.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ed25519/ed25519_fe.h 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,7 @@ /* * Ed25519 field element * (C) 2017 Ribose Inc +* 2025 Jack Lloyd * * Based on the public domain code from SUPERCOP ref10 by * Peter Schwabe, Daniel J. Bernstein, Niels Duif, Tanja Lange, Bo-Yin Yang @@ -12,45 +13,51 @@ #define BOTAN_ED25519_FE_H_ #include -#include +#include +#include namespace Botan { /** * An element of the field \\Z/(2^255-19) +* +* An element t, entries t[0]...t[9], represents the integer +* t[0]+2^26 t[1]+2^51 t[2]+2^77 t[3]+2^102 t[4]+...+2^230 t[9]. +* Bounds on each t[i] vary depending on context. */ -class FE_25519 { +class Ed25519_FieldElement final { public: - ~FE_25519() { secure_scrub_memory(m_fe, sizeof(m_fe)); } - /** - * Zero element + * Default zero initialization */ - FE_25519(int init = 0) { - if(init != 0 && init != 1) { - throw Invalid_Argument("Invalid FE_25519 initial value"); - } - clear_mem(m_fe, 10); - m_fe[0] = init; + constexpr Ed25519_FieldElement() : m_fe{} {} + + constexpr static Ed25519_FieldElement zero() { return Ed25519_FieldElement(); } + + constexpr static Ed25519_FieldElement one() { + auto o = Ed25519_FieldElement(); + o.m_fe[0] = 1; + return o; } - FE_25519(std::initializer_list x) { - if(x.size() != 10) { - throw Invalid_Argument("Invalid FE_25519 initializer list"); + // NOLINTNEXTLINE(*-member-init) + constexpr explicit Ed25519_FieldElement(std::span fe) { + for(size_t i = 0; i != 10; ++i) { + m_fe[i] = fe[i]; } - copy_mem(m_fe, x.begin(), 10); } - FE_25519(int64_t h0, - int64_t h1, - int64_t h2, - int64_t h3, - int64_t h4, - int64_t h5, - int64_t h6, - int64_t h7, - int64_t h8, - int64_t h9) { + // NOLINTNEXTLINE(*-member-init) + constexpr Ed25519_FieldElement(int64_t h0, + int64_t h1, + int64_t h2, + int64_t h3, + int64_t h4, + int64_t h5, + int64_t h6, + int64_t h7, + int64_t h8, + int64_t h9) { m_fe[0] = static_cast(h0); m_fe[1] = static_cast(h1); m_fe[2] = static_cast(h2); @@ -63,25 +70,14 @@ m_fe[9] = static_cast(h9); } - FE_25519(const FE_25519& other) = default; - FE_25519& operator=(const FE_25519& other) = default; + static Ed25519_FieldElement deserialize(const uint8_t b[32]); - FE_25519(FE_25519&& other) = default; - FE_25519& operator=(FE_25519&& other) = default; - - void from_bytes(const uint8_t b[32]); - void to_bytes(uint8_t b[32]) const; + void serialize_to(std::span b) const; bool is_zero() const { - uint8_t s[32]; - to_bytes(s); - - uint8_t sum = 0; - for(size_t i = 0; i != 32; ++i) { - sum |= s[i]; - } - - return (sum == 0); + std::array value = {}; + this->serialize_to(value); + return CT::all_zeros(value.data(), value.size()).as_bool(); } /* @@ -89,44 +85,48 @@ return 0 if f is in {0,2,4,...,q-1} */ bool is_negative() const { - // TODO could avoid most of the to_bytes computation here - uint8_t s[32]; - to_bytes(s); - return s[0] & 1; + // TODO could avoid most of the serialize computation here + std::array s = {}; + this->serialize_to(s); + return (s[0] & 0x01) == 0x01; } - static FE_25519 add(const FE_25519& a, const FE_25519& b) { - FE_25519 z; + static Ed25519_FieldElement add(const Ed25519_FieldElement& a, const Ed25519_FieldElement& b) { + Ed25519_FieldElement z; for(size_t i = 0; i != 10; ++i) { - z[i] = a[i] + b[i]; + z.m_fe[i] = a.m_fe[i] + b.m_fe[i]; } return z; } - static FE_25519 sub(const FE_25519& a, const FE_25519& b) { - FE_25519 z; + static Ed25519_FieldElement sub(const Ed25519_FieldElement& a, const Ed25519_FieldElement& b) { + Ed25519_FieldElement z; for(size_t i = 0; i != 10; ++i) { - z[i] = a[i] - b[i]; + z.m_fe[i] = a.m_fe[i] - b.m_fe[i]; } return z; } - static FE_25519 negate(const FE_25519& a) { - FE_25519 z; + static Ed25519_FieldElement negate(const Ed25519_FieldElement& a) { + Ed25519_FieldElement z; for(size_t i = 0; i != 10; ++i) { - z[i] = -a[i]; + z.m_fe[i] = -a.m_fe[i]; } return z; } - static FE_25519 mul(const FE_25519& a, const FE_25519& b); - static FE_25519 sqr_iter(const FE_25519& a, size_t iter); + static Ed25519_FieldElement mul(const Ed25519_FieldElement& a, const Ed25519_FieldElement& b); + + Ed25519_FieldElement sqr_iter(size_t iter) const; - static FE_25519 sqr(const FE_25519& a) { return sqr_iter(a, 1); } + Ed25519_FieldElement sqr() const { return sqr_iter(1); } - static FE_25519 sqr2(const FE_25519& a); - static FE_25519 pow_22523(const FE_25519& a); - static FE_25519 invert(const FE_25519& a); + // Return 2*a^2 + Ed25519_FieldElement sqr2() const; + + Ed25519_FieldElement invert() const; + + Ed25519_FieldElement pow_22523() const; // TODO remove int32_t operator[](size_t i) const { return m_fe[i]; } @@ -134,81 +134,23 @@ int32_t& operator[](size_t i) { return m_fe[i]; } private: - int32_t m_fe[10]; + std::array m_fe; }; -typedef FE_25519 fe; - -/* -fe means field element. -Here the field is -An element t, entries t[0]...t[9], represents the integer -t[0]+2^26 t[1]+2^51 t[2]+2^77 t[3]+2^102 t[4]+...+2^230 t[9]. -Bounds on each t[i] vary depending on context. -*/ - -inline void fe_frombytes(fe& x, const uint8_t* b) { - x.from_bytes(b); -} - -inline void fe_tobytes(uint8_t* b, const fe& x) { - x.to_bytes(b); -} - -inline void fe_copy(fe& a, const fe& b) { - a = b; -} - -inline int fe_isnonzero(const fe& x) { - return x.is_zero() ? 0 : 1; -} - -inline int fe_isnegative(const fe& x) { - return x.is_negative(); -} - -inline void fe_0(fe& x) { - x = FE_25519(); -} - -inline void fe_1(fe& x) { - x = FE_25519(1); -} - -inline void fe_add(fe& x, const fe& a, const fe& b) { - x = FE_25519::add(a, b); -} - -inline void fe_sub(fe& x, const fe& a, const fe& b) { - x = FE_25519::sub(a, b); -} - -inline void fe_neg(fe& x, const fe& z) { - x = FE_25519::negate(z); -} - -inline void fe_mul(fe& x, const fe& a, const fe& b) { - x = FE_25519::mul(a, b); -} - -inline void fe_sq(fe& x, const fe& z) { - x = FE_25519::sqr(z); -} - -inline void fe_sq_iter(fe& x, const fe& z, size_t iter) { - x = FE_25519::sqr_iter(z, iter); +inline Ed25519_FieldElement operator+(const Ed25519_FieldElement& x, const Ed25519_FieldElement& y) { + return Ed25519_FieldElement::add(x, y); } -inline void fe_sq2(fe& x, const fe& z) { - x = FE_25519::sqr2(z); +inline Ed25519_FieldElement operator-(const Ed25519_FieldElement& x, const Ed25519_FieldElement& y) { + return Ed25519_FieldElement::sub(x, y); } -inline void fe_invert(fe& x, const fe& z) { - x = FE_25519::invert(z); +inline Ed25519_FieldElement operator*(const Ed25519_FieldElement& x, const Ed25519_FieldElement& y) { + return Ed25519_FieldElement::mul(x, y); } -inline void fe_pow22523(fe& x, const fe& y) { - x = FE_25519::pow_22523(y); +inline Ed25519_FieldElement operator-(const Ed25519_FieldElement& x) { + return Ed25519_FieldElement::negate(x); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ed25519/ed25519_internal.h botan3-3.12.0+dfsg/src/lib/pubkey/ed25519/ed25519_internal.h --- botan3-3.7.1+dfsg/src/lib/pubkey/ed25519/ed25519_internal.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ed25519/ed25519_internal.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,16 +11,15 @@ #ifndef BOTAN_ED25519_INT_H_ #define BOTAN_ED25519_INT_H_ -#include #include namespace Botan { -inline uint64_t load_3(const uint8_t in[3]) { - return static_cast(in[0]) | (static_cast(in[1]) << 8) | (static_cast(in[2]) << 16); +inline uint32_t load_3(const uint8_t in[3]) { + return static_cast(in[0]) | (static_cast(in[1]) << 8) | (static_cast(in[2]) << 16); } -inline uint64_t load_4(const uint8_t* in) { +inline uint32_t load_4(const uint8_t* in) { return load_le(in, 0); } @@ -30,7 +29,7 @@ { const int64_t X1 = (static_cast(1) << S); const int64_t X2 = (static_cast(1) << (S - 1)); - int64_t c = (h0 + X2) >> S; + const int64_t c = (h0 + X2) >> S; h1 += c * MUL; h0 -= c * X1; } @@ -40,7 +39,7 @@ requires(S > 0 && S < 64) { const int64_t X1 = (static_cast(1) << S); - int64_t c = h0 >> S; + const int64_t c = h0 >> S; h1 += c; h0 -= c * X1; } @@ -50,7 +49,7 @@ requires(S > 0 && S < 32) { const int32_t X1 = (static_cast(1) << S); - int32_t c = h0 >> S; + const int32_t c = h0 >> S; h1 += c; h0 -= c * X1; } @@ -65,36 +64,17 @@ X = 0; } -/* -ge means group element. - -Here the group is the set of pairs (x,y) of field elements (see fe.h) -satisfying -x^2 + y^2 = 1 + d x^2y^2 -where d = -121665/121666. - -Representations: - ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT -*/ - -struct ge_p3 { - FE_25519 X; - FE_25519 Y; - FE_25519 Z; - FE_25519 T; -}; - -int ge_frombytes_negate_vartime(ge_p3* v, const uint8_t*); -void ge_scalarmult_base(uint8_t out[32], const uint8_t in[32]); +void ed25519_basepoint_mul(std::span out, const uint8_t in[32]); -void ge_double_scalarmult_vartime(uint8_t out[32], const uint8_t a[], const ge_p3* A, const uint8_t b[]); +bool signature_check(std::span pk, const uint8_t h[32], const uint8_t r[32], const uint8_t s[32]); /* The set of scalars is \Z/l where l = 2^252 + 27742317777372353535851937790883648493. */ -void sc_reduce(uint8_t*); -void sc_muladd(uint8_t*, const uint8_t*, const uint8_t*, const uint8_t*); +void sc_reduce(uint8_t* s); +void sc_muladd(uint8_t* s, const uint8_t* a, const uint8_t* b, const uint8_t* c); } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ed25519/ed25519_key.cpp botan3-3.12.0+dfsg/src/lib/pubkey/ed25519/ed25519_key.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/ed25519/ed25519_key.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ed25519/ed25519_key.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,9 +1,7 @@ /* * Ed25519 * (C) 2017 Ribose Inc -* -* Based on the public domain code from SUPERCOP ref10 by -* Peter Schwabe, Daniel J. Bernstein, Niels Duif, Tanja Lange, Bo-Yin Yang +* 2025 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -49,19 +47,8 @@ copy_mem(pkcopy, m_public.data(), 32); pkcopy[31] ^= (1 << 7); // flip sign - ge_p3 point; - if(ge_frombytes_negate_vartime(&point, pkcopy) != 0) { - return false; - } - - uint8_t result[32]; - ge_double_scalarmult_vartime(result, modm_m, &point, zero); - - if(!CT::is_equal(result, identity_element, 32).as_bool()) { - return false; - } - return true; + return signature_check(pkcopy, modm_m, identity_element, zero); } Ed25519_PublicKey::Ed25519_PublicKey(const uint8_t pub_key[], size_t pub_len) { @@ -91,9 +78,9 @@ return std::make_unique(rng); } -Ed25519_PrivateKey::Ed25519_PrivateKey(const secure_vector& secret_key) { +Ed25519_PrivateKey::Ed25519_PrivateKey(std::span secret_key) { if(secret_key.size() == 64) { - m_private = secret_key; + m_private.assign(secret_key.begin(), secret_key.end()); m_public.assign(m_private.begin() + 32, m_private.end()); } else if(secret_key.size() == 32) { m_public.resize(32); @@ -104,6 +91,18 @@ } } +//static +Ed25519_PrivateKey Ed25519_PrivateKey::from_seed(std::span seed) { + BOTAN_ARG_CHECK(seed.size() == 32, "Ed25519 seed must be exactly 32 bytes long"); + return Ed25519_PrivateKey(seed); +} + +//static +Ed25519_PrivateKey Ed25519_PrivateKey::from_bytes(std::span bytes) { + BOTAN_ARG_CHECK(bytes.size() == 64, "Ed25519 private key must be exactly 64 bytes long"); + return Ed25519_PrivateKey(bytes); +} + Ed25519_PrivateKey::Ed25519_PrivateKey(RandomNumberGenerator& rng) { const secure_vector seed = rng.random_vec(32); m_public.resize(32); @@ -113,7 +112,7 @@ Ed25519_PrivateKey::Ed25519_PrivateKey(const AlgorithmIdentifier& /*unused*/, std::span key_bits) { secure_vector bits; - BER_Decoder(key_bits).decode(bits, ASN1_Type::OctetString).discard_remaining(); + BER_Decoder(key_bits, BER_Decoder::Limits::DER()).decode(bits, ASN1_Type::OctetString).discard_remaining(); if(bits.size() != 32) { throw Decoding_Error("Invalid size for Ed25519 private key"); @@ -128,12 +127,16 @@ } secure_vector Ed25519_PrivateKey::private_key_bits() const { - secure_vector bits(&m_private[0], &m_private[32]); + const secure_vector bits(m_private.data(), &m_private[32]); return DER_Encoder().encode(bits, ASN1_Type::OctetString).get_contents(); } bool Ed25519_PrivateKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const { - return true; // ??? + std::vector public_point(32); + secure_vector private_key(64); // discarded + ed25519_gen_keypair(public_point.data(), private_key.data(), m_private.data()); + // Variable time comparison is fine here + return public_point == m_public; } namespace { @@ -149,6 +152,7 @@ bool is_valid_signature(std::span sig) override { if(sig.size() != 64) { + m_msg.clear(); return false; } @@ -168,12 +172,10 @@ /** * Ed25519 verifying operation with pre-hash */ -class Ed25519_Hashed_Verify_Operation final : public PK_Ops::Verification { +class Ed25519_Hashed_Verify_Operation final : public PK_Ops::Verification_with_Hash { public: Ed25519_Hashed_Verify_Operation(const Ed25519_PublicKey& key, std::string_view hash, bool rfc8032) : - m_key(key.get_public_key()) { - m_hash = HashFunction::create_or_throw(hash); - + PK_Ops::Verification_with_Hash(hash), m_key(key.get_public_key()) { if(rfc8032) { m_domain_sep = {0x53, 0x69, 0x67, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x6E, 0x6F, 0x20, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x63, 0x6F, @@ -181,24 +183,17 @@ } } - void update(std::span msg) override { m_hash->update(msg); } - - bool is_valid_signature(std::span sig) override { + bool verify(std::span ph, std::span sig) override { if(sig.size() != 64) { return false; } - std::vector msg_hash(m_hash->output_length()); - m_hash->final(msg_hash.data()); BOTAN_ASSERT_EQUAL(m_key.size(), 32, "Expected size"); return ed25519_verify( - msg_hash.data(), msg_hash.size(), sig.data(), m_key.data(), m_domain_sep.data(), m_domain_sep.size()); + ph.data(), ph.size(), sig.data(), m_key.data(), m_domain_sep.data(), m_domain_sep.size()); } - std::string hash_function() const override { return m_hash->name(); } - private: - std::unique_ptr m_hash; std::vector m_key; std::vector m_domain_sep; }; @@ -237,12 +232,10 @@ /** * Ed25519 signing operation with pre-hash */ -class Ed25519_Hashed_Sign_Operation final : public PK_Ops::Signature { +class Ed25519_Hashed_Sign_Operation final : public PK_Ops::Signature_with_Hash { public: Ed25519_Hashed_Sign_Operation(const Ed25519_PrivateKey& key, std::string_view hash, bool rfc8032) : - m_key(key.raw_private_key_bits()) { - m_hash = HashFunction::create_or_throw(hash); - + PK_Ops::Signature_with_Hash(hash), m_key(key.raw_private_key_bits()) { if(rfc8032) { m_domain_sep = std::vector{0x53, 0x69, 0x67, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x6E, 0x6F, 0x20, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x63, 0x6F, @@ -252,21 +245,13 @@ size_t signature_length() const override { return 64; } - void update(std::span msg) override { m_hash->update(msg); } - - std::vector sign(RandomNumberGenerator& /*rng*/) override { + std::vector raw_sign(std::span ph, RandomNumberGenerator& /*rng*/) override { std::vector sig(64); - std::vector msg_hash(m_hash->output_length()); - m_hash->final(msg_hash.data()); - ed25519_sign( - sig.data(), msg_hash.data(), msg_hash.size(), m_key.data(), m_domain_sep.data(), m_domain_sep.size()); + ed25519_sign(sig.data(), ph.data(), ph.size(), m_key.data(), m_domain_sep.data(), m_domain_sep.size()); return sig; } - std::string hash_function() const override { return m_hash->name(); } - private: - std::unique_ptr m_hash; secure_vector m_key; std::vector m_domain_sep; }; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/ed25519/ge.cpp botan3-3.12.0+dfsg/src/lib/pubkey/ed25519/ge.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/ed25519/ge.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/ed25519/ge.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,7 @@ /* * Ed25519 group operations * (C) 2017 Ribose Inc +* 2025 Jack Lloyd * * Based on the public domain code from SUPERCOP ref10 by * Peter Schwabe, Daniel J. Bernstein, Niels Duif, Tanja Lange, Bo-Yin Yang @@ -10,384 +11,281 @@ #include +#include +#include +#include + namespace Botan { namespace { -/* -Representations: - ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z - ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT - ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T - ge_precomp (Duif): (y+x,y-x,2dxy) -*/ -struct ge_p2 { - FE_25519 X; - FE_25519 Y; - FE_25519 Z; -}; - -struct ge_p1p1 { - FE_25519 X; - FE_25519 Y; - FE_25519 Z; - FE_25519 T; -}; - -struct ge_precomp { - FE_25519 yplusx; - FE_25519 yminusx; - FE_25519 xy2d; -}; - -struct ge_cached { - FE_25519 YplusX; - FE_25519 YminusX; - FE_25519 Z; - FE_25519 T2d; -}; +/** +Here the group is the set of pairs (x,y) of field elements (see ed5519_fe.h) +satisfying -x^2 + y^2 = 1 + d x^2y^2 where d = -121665/121666. -/* -r = p + q +Several different point representations are used in this implementation */ -void ge_add(ge_p1p1* r, const ge_p3* p, const ge_cached* q) { - FE_25519 t0; - /* qhasm: YpX1 = Y1+X1 */ - /* asm 1: fe_add(>YpX1=fe#1,YpX1=r->X,Y,X); */ - fe_add(r->X, p->Y, p->X); - - /* qhasm: YmX1 = Y1-X1 */ - /* asm 1: fe_sub(>YmX1=fe#2,YmX1=r->Y,Y,X); */ - fe_sub(r->Y, p->Y, p->X); - - /* qhasm: A = YpX1*YpX2 */ - /* asm 1: fe_mul(>A=fe#3,A=r->Z,X,YplusX); */ - fe_mul(r->Z, r->X, q->YplusX); - - /* qhasm: B = YmX1*YmX2 */ - /* asm 1: fe_mul(>B=fe#2,B=r->Y,Y,YminusX); */ - fe_mul(r->Y, r->Y, q->YminusX); - - /* qhasm: C = T2d2*T1 */ - /* asm 1: fe_mul(>C=fe#4,C=r->T,T2d,T); */ - fe_mul(r->T, q->T2d, p->T); - - /* qhasm: ZZ = Z1*Z2 */ - /* asm 1: fe_mul(>ZZ=fe#1,ZZ=r->X,Z,Z); */ - fe_mul(r->X, p->Z, q->Z); - - /* qhasm: D = 2*ZZ */ - /* asm 1: fe_add(>D=fe#5,D=t0,X,X); */ - fe_add(t0, r->X, r->X); - - /* qhasm: X3 = A-B */ - /* asm 1: fe_sub(>X3=fe#1,X3=r->X,Z,Y); */ - fe_sub(r->X, r->Z, r->Y); - - /* qhasm: Y3 = A+B */ - /* asm 1: fe_add(>Y3=fe#2,Y3=r->Y,Z,Y); */ - fe_add(r->Y, r->Z, r->Y); - - /* qhasm: Z3 = D+C */ - /* asm 1: fe_add(>Z3=fe#3,Z3=r->Z,T); */ - fe_add(r->Z, t0, r->T); - - /* qhasm: T3 = D-C */ - /* asm 1: fe_sub(>T3=fe#4,T3=r->T,T); */ - fe_sub(r->T, t0, r->T); -} - -/* -r = p + q +/** +* Ed25519_Point_Completed +* +* ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T */ +class Ed25519_Point_Completed final { + public: + Ed25519_FieldElement X; // NOLINT(misc-non-private-member-variables-in-classes) + Ed25519_FieldElement Y; // NOLINT(misc-non-private-member-variables-in-classes) + Ed25519_FieldElement Z; // NOLINT(misc-non-private-member-variables-in-classes) + Ed25519_FieldElement T; // NOLINT(misc-non-private-member-variables-in-classes) +}; -void ge_madd(ge_p1p1* r, const ge_p3* p, const ge_precomp* q) { - FE_25519 t0; - /* qhasm: YpX1 = Y1+X1 */ - fe_add(r->X, p->Y, p->X); - - /* qhasm: YmX1 = Y1-X1 */ - fe_sub(r->Y, p->Y, p->X); - - /* qhasm: A = YpX1*ypx2 */ - fe_mul(r->Z, r->X, q->yplusx); - - /* qhasm: B = YmX1*ymx2 */ - fe_mul(r->Y, r->Y, q->yminusx); - - /* qhasm: C = xy2d2*T1 */ - fe_mul(r->T, q->xy2d, p->T); - - /* qhasm: D = 2*Z1 */ - fe_add(t0, p->Z, p->Z); +/** +* Ed25519_Point_Projective +* +* (X:Y:Z) satisfying x=X/Z, y=Y/Z +*/ +class Ed25519_Point_Projective final { + public: + Ed25519_FieldElement X; // NOLINT(misc-non-private-member-variables-in-classes) + Ed25519_FieldElement Y; // NOLINT(misc-non-private-member-variables-in-classes) + Ed25519_FieldElement Z; // NOLINT(misc-non-private-member-variables-in-classes) + + /* + * Point conversion + */ + static Ed25519_Point_Projective from(const Ed25519_Point_Completed& p) { + Ed25519_Point_Projective r; + r.X = p.X * p.T; + r.Y = p.Y * p.Z; + r.Z = p.Z * p.T; + return r; + } - /* qhasm: X3 = A-B */ - fe_sub(r->X, r->Z, r->Y); + static constexpr Ed25519_Point_Projective identity() { + Ed25519_Point_Projective h; + h.X = Ed25519_FieldElement::zero(); + h.Y = Ed25519_FieldElement::one(); + h.Z = Ed25519_FieldElement::one(); + return h; + } - /* qhasm: Y3 = A+B */ - fe_add(r->Y, r->Z, r->Y); + void serialize_to(std::span s) const { + auto recip = this->Z.invert(); + auto x = this->X * recip; + auto y = this->Y * recip; + y.serialize_to(s); + s[31] ^= x.is_negative() ? 0x80 : 0x00; + } - /* qhasm: Z3 = D+C */ - fe_add(r->Z, t0, r->T); + Ed25519_Point_Completed dbl() const; +}; - /* qhasm: T3 = D-C */ - fe_sub(r->T, t0, r->T); +Ed25519_Point_Completed Ed25519_Point_Projective::dbl() const { + Ed25519_Point_Completed r; + r.X = X.sqr(); // XX=X1^2 + r.Z = Y.sqr(); // YY=Y1^2 + r.T = Z.sqr2(); // B=2*Z1^2 + r.Y = X + Y; // A=X1+Y1 + auto t0 = r.Y.sqr(); // AA=A^2 + r.Y = r.Z + r.X; // Y3=YY+XX + r.Z = r.Z - r.X; // Z3=YY-XX + r.X = t0 - r.Y; // X3=AA-Y3 + r.T = r.T - r.Z; // T3=B-Z3 + return r; } -/* -r = p - q +/** +* Ed25519_Point_Extended +* +* (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT */ +class Ed25519_Point_Extended final { + public: + Ed25519_FieldElement X; // NOLINT(misc-non-private-member-variables-in-classes) + Ed25519_FieldElement Y; // NOLINT(misc-non-private-member-variables-in-classes) + Ed25519_FieldElement Z; // NOLINT(misc-non-private-member-variables-in-classes) + Ed25519_FieldElement T; // NOLINT(misc-non-private-member-variables-in-classes) + + static constexpr Ed25519_Point_Extended identity() { + Ed25519_Point_Extended h; + h.X = Ed25519_FieldElement::zero(); + h.Y = Ed25519_FieldElement::one(); + h.Z = Ed25519_FieldElement::one(); + h.T = Ed25519_FieldElement::zero(); + return h; + } -void ge_msub(ge_p1p1* r, const ge_p3* p, const ge_precomp* q) { - FE_25519 t0; - - /* qhasm: YpX1 = Y1+X1 */ - /* asm 1: fe_add(>YpX1=fe#1,YpX1=r->X,Y,X); */ - fe_add(r->X, p->Y, p->X); - - /* qhasm: YmX1 = Y1-X1 */ - /* asm 1: fe_sub(>YmX1=fe#2,YmX1=r->Y,Y,X); */ - fe_sub(r->Y, p->Y, p->X); - - /* qhasm: A = YpX1*ymx2 */ - /* asm 1: fe_mul(>A=fe#3,A=r->Z,X,yminusx); */ - fe_mul(r->Z, r->X, q->yminusx); - - /* qhasm: B = YmX1*ypx2 */ - /* asm 1: fe_mul(>B=fe#2,B=r->Y,Y,yplusx); */ - fe_mul(r->Y, r->Y, q->yplusx); - - /* qhasm: C = xy2d2*T1 */ - /* asm 1: fe_mul(>C=fe#4,C=r->T,xy2d,T); */ - fe_mul(r->T, q->xy2d, p->T); - - /* qhasm: D = 2*Z1 */ - /* asm 1: fe_add(>D=fe#5,D=t0,Z,Z); */ - fe_add(t0, p->Z, p->Z); - - /* qhasm: X3 = A-B */ - /* asm 1: fe_sub(>X3=fe#1,X3=r->X,Z,Y); */ - fe_sub(r->X, r->Z, r->Y); - - /* qhasm: Y3 = A+B */ - /* asm 1: fe_add(>Y3=fe#2,Y3=r->Y,Z,Y); */ - fe_add(r->Y, r->Z, r->Y); - - /* qhasm: Z3 = D-C */ - /* asm 1: fe_sub(>Z3=fe#3,Z3=r->Z,T); */ - fe_sub(r->Z, t0, r->T); - - /* qhasm: T3 = D+C */ - /* asm 1: fe_add(>T3=fe#4,T3=r->T,T); */ - fe_add(r->T, t0, r->T); -} + Ed25519_Point_Completed dbl() const { + Ed25519_Point_Projective q; + q.X = X; + q.Y = Y; + q.Z = Z; + return q.dbl(); + } -/* -r = p -*/ + /** + * Point conversion + */ + static Ed25519_Point_Extended from(const Ed25519_Point_Completed& p) { + Ed25519_Point_Extended r; + r.X = p.X * p.T; + r.Y = p.Y * p.Z; + r.Z = p.Z * p.T; + r.T = p.X * p.Y; + return r; + } -void ge_p1p1_to_p2(ge_p2* r, const ge_p1p1* p) { - fe_mul(r->X, p->X, p->T); - fe_mul(r->Y, p->Y, p->Z); - fe_mul(r->Z, p->Z, p->T); -} + void serialize_to(std::span out) const { + auto recip = this->Z.invert(); + auto x = this->X * recip; + auto y = this->Y * recip; + y.serialize_to(out); + out[31] ^= x.is_negative() ? 0x80 : 0x00; + } +}; -/* -r = p +/** +* Ed25519 Point in "Niels" coordinates +* +* y + x, y - x, 2d * x * y +* +* where d is the Edwards curve constant. */ +class Ed25519_Point_Niels final { + public: + Ed25519_FieldElement yplusx; // NOLINT(misc-non-private-member-variables-in-classes) + Ed25519_FieldElement yminusx; // NOLINT(misc-non-private-member-variables-in-classes) + Ed25519_FieldElement xy2d; // NOLINT(misc-non-private-member-variables-in-classes) + + static constexpr Ed25519_Point_Niels identity() { + Ed25519_Point_Niels h; + h.yplusx = Ed25519_FieldElement::one(); + h.yminusx = Ed25519_FieldElement::one(); + h.xy2d = Ed25519_FieldElement::zero(); + return h; + } +}; -void ge_p1p1_to_p3(ge_p3* r, const ge_p1p1* p) { - fe_mul(r->X, p->X, p->T); - fe_mul(r->Y, p->Y, p->Z); - fe_mul(r->Z, p->Z, p->T); - fe_mul(r->T, p->X, p->Y); -} +class Ed25519_Point_Cached final { + public: + Ed25519_FieldElement YplusX; // NOLINT(misc-non-private-member-variables-in-classes) + Ed25519_FieldElement YminusX; // NOLINT(misc-non-private-member-variables-in-classes) + Ed25519_FieldElement Z; // NOLINT(misc-non-private-member-variables-in-classes) + Ed25519_FieldElement T2d; // NOLINT(misc-non-private-member-variables-in-classes) + + /** + * Point conversion + */ + static Ed25519_Point_Cached from(const Ed25519_Point_Extended& p) { + static constexpr Ed25519_FieldElement d2 = { + -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199}; + Ed25519_Point_Cached r; + r.YplusX = p.Y + p.X; + r.YminusX = p.Y - p.X; + r.Z = p.Z; + r.T2d = p.T * d2; + return r; + } + + /** + * Point conversion + */ + static Ed25519_Point_Cached from(const Ed25519_Point_Completed& p) { + return Ed25519_Point_Cached::from(Ed25519_Point_Extended::from(p)); + } +}; /* -r = 2 * p +* Point addition */ - -void ge_p2_dbl(ge_p1p1* r, const ge_p2* p) { - FE_25519 t0; - /* qhasm: XX=X1^2 */ - /* asm 1: fe_sq(>XX=fe#1,XX=r->X,X); */ - fe_sq(r->X, p->X); - - /* qhasm: YY=Y1^2 */ - /* asm 1: fe_sq(>YY=fe#3,YY=r->Z,Y); */ - fe_sq(r->Z, p->Y); - - /* qhasm: B=2*Z1^2 */ - /* asm 1: fe_sq2(>B=fe#4,B=r->T,Z); */ - fe_sq2(r->T, p->Z); - - /* qhasm: A=X1+Y1 */ - /* asm 1: fe_add(>A=fe#2,A=r->Y,X,Y); */ - fe_add(r->Y, p->X, p->Y); - - /* qhasm: AA=A^2 */ - /* asm 1: fe_sq(>AA=fe#5,AA=t0,Y); */ - fe_sq(t0, r->Y); - - /* qhasm: Y3=YY+XX */ - /* asm 1: fe_add(>Y3=fe#2,Y3=r->Y,Z,X); */ - fe_add(r->Y, r->Z, r->X); - - /* qhasm: Z3=YY-XX */ - /* asm 1: fe_sub(>Z3=fe#3,Z3=r->Z,Z,X); */ - fe_sub(r->Z, r->Z, r->X); - - /* qhasm: X3=AA-Y3 */ - /* asm 1: fe_sub(>X3=fe#1,X3=r->X,Y); */ - fe_sub(r->X, t0, r->Y); - - /* qhasm: T3=B-Z3 */ - /* asm 1: fe_sub(>T3=fe#4,T3=r->T,T,Z); */ - fe_sub(r->T, r->T, r->Z); -} - -void ge_p3_0(ge_p3* h) { - fe_0(h->X); - fe_1(h->Y); - fe_1(h->Z); - fe_0(h->T); +inline Ed25519_Point_Completed operator+(const Ed25519_Point_Extended& p, const Ed25519_Point_Cached& q) { + Ed25519_Point_Completed r; + r.X = p.Y + p.X; // YpX1 = Y1+X1 + r.Y = p.Y - p.X; // YmX1 = Y1-X1 + r.Z = r.X * q.YplusX; // A = YpX1*YpX2 + r.Y = r.Y * q.YminusX; // B = YmX1*YmX2 + r.T = q.T2d * p.T; // C = T2d2*T1 + r.X = p.Z * q.Z; // ZZ = Z1*Z2 + auto t0 = r.X + r.X; // D = 2*ZZ + r.X = r.Z - r.Y; // X3 = A-B + r.Y = r.Z + r.Y; // Y3 = A+B + r.Z = t0 + r.T; // Z3 = D+C + r.T = t0 - r.T; // T3 = D-C + return r; } /* -r = 2 * p +* Point addition */ - -void ge_p3_dbl(ge_p1p1* r, const ge_p3* p) { - ge_p2 q; - // Convert to p2 rep - q.X = p->X; - q.Y = p->Y; - q.Z = p->Z; - ge_p2_dbl(r, &q); +inline Ed25519_Point_Completed operator+(const Ed25519_Point_Extended& p, const Ed25519_Point_Niels& q) { + Ed25519_Point_Completed r; + r.X = p.Y + p.X; // YpX1 = Y1+X1 + r.Y = p.Y - p.X; // YmX1 = Y1-X1 + r.Z = r.X * q.yplusx; // A = YpX1*ypx2 + r.Y = r.Y * q.yminusx; // B = YmX1*ymx2 + r.T = q.xy2d * p.T; // C = xy2d2*T1 + auto t0 = p.Z + p.Z; // D = 2*Z1 + r.X = r.Z - r.Y; // X3 = A-B + r.Y = r.Z + r.Y; // Y3 = A+B + r.Z = t0 + r.T; // Z3 = D+C + r.T = t0 - r.T; // T3 = D-C + return r; } /* -r = p +* Point subtraction */ - -void ge_p3_to_cached(ge_cached* r, const ge_p3* p) { - static const FE_25519 d2 = { - -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199}; - fe_add(r->YplusX, p->Y, p->X); - fe_sub(r->YminusX, p->Y, p->X); - fe_copy(r->Z, p->Z); - fe_mul(r->T2d, p->T, d2); +inline Ed25519_Point_Completed operator-(const Ed25519_Point_Extended& p, const Ed25519_Point_Niels& q) { + Ed25519_Point_Completed r; + r.X = p.Y + p.X; // YpX1 = Y1+X1 + r.Y = p.Y - p.X; // YmX1 = Y1-X1 + r.Z = r.X * q.yminusx; // A = YpX1*ymx2 + r.Y = r.Y * q.yplusx; // B = YmX1*ypx2 + r.T = q.xy2d * p.T; // C = xy2d2*T1 + auto t0 = p.Z + p.Z; // D = 2*Z1 + r.X = r.Z - r.Y; // X3 = A-B + r.Y = r.Z + r.Y; // Y3 = A+B + r.Z = t0 - r.T; // Z3 = D-C + r.T = t0 + r.T; // T3 = D+C + return r; } /* -r = p - q +* Point subtraction */ - -void ge_sub(ge_p1p1* r, const ge_p3* p, const ge_cached* q) { - FE_25519 t0; - /* qhasm: YpX1 = Y1+X1 */ - /* asm 1: fe_add(>YpX1=fe#1,YpX1=r->X,Y,X); */ - fe_add(r->X, p->Y, p->X); - - /* qhasm: YmX1 = Y1-X1 */ - /* asm 1: fe_sub(>YmX1=fe#2,YmX1=r->Y,Y,X); */ - fe_sub(r->Y, p->Y, p->X); - - /* qhasm: A = YpX1*YmX2 */ - /* asm 1: fe_mul(>A=fe#3,A=r->Z,X,YminusX); */ - fe_mul(r->Z, r->X, q->YminusX); - - /* qhasm: B = YmX1*YpX2 */ - /* asm 1: fe_mul(>B=fe#2,B=r->Y,Y,YplusX); */ - fe_mul(r->Y, r->Y, q->YplusX); - - /* qhasm: C = T2d2*T1 */ - /* asm 1: fe_mul(>C=fe#4,C=r->T,T2d,T); */ - fe_mul(r->T, q->T2d, p->T); - - /* qhasm: ZZ = Z1*Z2 */ - /* asm 1: fe_mul(>ZZ=fe#1,ZZ=r->X,Z,Z); */ - fe_mul(r->X, p->Z, q->Z); - - /* qhasm: D = 2*ZZ */ - /* asm 1: fe_add(>D=fe#5,D=t0,X,X); */ - fe_add(t0, r->X, r->X); - - /* qhasm: X3 = A-B */ - /* asm 1: fe_sub(>X3=fe#1,X3=r->X,Z,Y); */ - fe_sub(r->X, r->Z, r->Y); - - /* qhasm: Y3 = A+B */ - /* asm 1: fe_add(>Y3=fe#2,Y3=r->Y,Z,Y); */ - fe_add(r->Y, r->Z, r->Y); - - /* qhasm: Z3 = D-C */ - /* asm 1: fe_sub(>Z3=fe#3,Z3=r->Z,T); */ - fe_sub(r->Z, t0, r->T); - - /* qhasm: T3 = D+C */ - /* asm 1: fe_add(>T3=fe#4,T3=r->T,T); */ - fe_add(r->T, t0, r->T); +inline Ed25519_Point_Completed operator-(const Ed25519_Point_Extended& p, const Ed25519_Point_Cached& q) { + Ed25519_Point_Completed r; + r.X = p.Y + p.X; // YpX1 = Y1+X1 + r.Y = p.Y - p.X; // YmX1 = Y1-X1 + r.Z = r.X * q.YminusX; // A = YpX1*YmX2 + r.Y = r.Y * q.YplusX; // B = YmX1*YpX2 + r.T = q.T2d * p.T; // C = T2d2*T1 + r.X = p.Z * q.Z; // ZZ = Z1*Z2 + auto t0 = r.X + r.X; // D = 2*ZZ + r.X = r.Z - r.Y; // X3 = A-B + r.Y = r.Z + r.Y; // Y3 = A+B + r.Z = t0 - r.T; // Z3 = D-C + r.T = t0 + r.T; // T3 = D+C + return r; } -void slide(int8_t* r, const uint8_t* a) { +std::array slide(const uint8_t* a) { + std::array r{}; for(size_t i = 0; i < 256; ++i) { r[i] = 1 & (a[i >> 3] >> (i & 7)); } for(size_t i = 0; i < 256; ++i) { - if(r[i]) { + if(r[i] != 0) { for(size_t b = 1; b <= 6 && i + b < 256; ++b) { - if(r[i + b]) { + if(r[i + b] != 0) { if(r[i] + (r[i + b] << b) <= 15) { r[i] += r[i + b] << b; r[i + b] = 0; } else if(r[i] - (r[i + b] << b) >= -15) { r[i] -= r[i + b] << b; for(size_t k = i + b; k < 256; ++k) { - if(!r[k]) { + if(r[k] == 0) { r[k] = 1; break; } @@ -400,74 +298,50 @@ } } } -} - -void ge_tobytes(uint8_t* s, const ge_p2* h) { - FE_25519 recip; - FE_25519 x; - FE_25519 y; - - fe_invert(recip, h->Z); - fe_mul(x, h->X, recip); - fe_mul(y, h->Y, recip); - fe_tobytes(s, y); - s[31] ^= fe_isnegative(x) << 7; -} -void ge_p2_0(ge_p2* h) { - fe_0(h->X); - fe_1(h->Y); - fe_1(h->Z); + return r; } -} // namespace - -int ge_frombytes_negate_vartime(ge_p3* h, const uint8_t* s) { - static const FE_25519 d = { +std::optional frombytes_negate_vartime(std::span s) { + static constexpr Ed25519_FieldElement d = { -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116}; - static const FE_25519 sqrtm1 = { + static constexpr Ed25519_FieldElement sqrtm1 = { -32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482}; - FE_25519 u; - FE_25519 v; - FE_25519 v3; - FE_25519 vxx; - FE_25519 check; - - fe_frombytes(h->Y, s); - fe_1(h->Z); - fe_sq(u, h->Y); - fe_mul(v, u, d); - fe_sub(u, u, h->Z); /* u = y^2-1 */ - fe_add(v, v, h->Z); /* v = dy^2+1 */ - - fe_sq(v3, v); - fe_mul(v3, v3, v); /* v3 = v^3 */ - fe_sq(h->X, v3); - fe_mul(h->X, h->X, v); - fe_mul(h->X, h->X, u); /* x = uv^7 */ - - fe_pow22523(h->X, h->X); /* x = (uv^7)^((q-5)/8) */ - fe_mul(h->X, h->X, v3); - fe_mul(h->X, h->X, u); /* x = uv^3(uv^7)^((q-5)/8) */ - - fe_sq(vxx, h->X); - fe_mul(vxx, vxx, v); - fe_sub(check, vxx, u); /* vx^2-u */ - if(fe_isnonzero(check)) { - fe_add(check, vxx, u); /* vx^2+u */ - if(fe_isnonzero(check)) { - return -1; + auto h = Ed25519_Point_Extended::identity(); + h.Y = Ed25519_FieldElement::deserialize(s.data()); + h.Z = Ed25519_FieldElement::one(); + auto u = h.Y.sqr(); + auto v = u * d; + u = u - h.Z; /* u = y^2-1 */ + v = v + h.Z; /* v = dy^2+1 */ + + auto v3 = v.sqr() * v; + h.X = v3.sqr(); + h.X = h.X * v; + h.X = h.X * u; /* x = uv^7 */ + + h.X = h.X.pow_22523(); + h.X = h.X * v3; + h.X = h.X * u; /* x = uv^3(uv^7)^((q-5)/8) */ + + auto vxx = h.X.sqr(); + vxx = vxx * v; + auto check = vxx - u; /* vx^2-u */ + if(!check.is_zero()) { + check = vxx + u; /* vx^2+u */ + if(!check.is_zero()) { + return {}; } - fe_mul(h->X, h->X, sqrtm1); + h.X = h.X * sqrtm1; } - if(fe_isnegative(h->X) == (s[31] >> 7)) { - fe_neg(h->X, h->X); + if(h.X.is_negative() == bool(s[31] >> 7)) { + h.X = -h.X; } - fe_mul(h->T, h->X, h->Y); - return 0; + h.T = h.X * h.Y; + return h; } /* @@ -477,8 +351,11 @@ B is the Ed25519 base point (x,4/5) with x positive. */ -void ge_double_scalarmult_vartime(uint8_t out[32], const uint8_t* a, const ge_p3* A, const uint8_t* b) { - static const ge_precomp Bi[8] = { +void ge_double_scalarmult_vartime(std::span out, + const uint8_t* a, + const Ed25519_Point_Extended& A, + const uint8_t* b) { + static constexpr Ed25519_Point_Niels Bi[8] = { { {25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605}, {-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378}, @@ -521,78 +398,112 @@ }, }; - int8_t aslide[256]; - int8_t bslide[256]; - ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */ - ge_p1p1 t; - ge_p3 u; - ge_p3 A2; - ge_p2 r; - int i; - - slide(aslide, a); - slide(bslide, b); - - ge_p3_to_cached(&Ai[0], A); - ge_p3_dbl(&t, A); - ge_p1p1_to_p3(&A2, &t); - ge_add(&t, &A2, &Ai[0]); - ge_p1p1_to_p3(&u, &t); - ge_p3_to_cached(&Ai[1], &u); - ge_add(&t, &A2, &Ai[1]); - ge_p1p1_to_p3(&u, &t); - ge_p3_to_cached(&Ai[2], &u); - ge_add(&t, &A2, &Ai[2]); - ge_p1p1_to_p3(&u, &t); - ge_p3_to_cached(&Ai[3], &u); - ge_add(&t, &A2, &Ai[3]); - ge_p1p1_to_p3(&u, &t); - ge_p3_to_cached(&Ai[4], &u); - ge_add(&t, &A2, &Ai[4]); - ge_p1p1_to_p3(&u, &t); - ge_p3_to_cached(&Ai[5], &u); - ge_add(&t, &A2, &Ai[5]); - ge_p1p1_to_p3(&u, &t); - ge_p3_to_cached(&Ai[6], &u); - ge_add(&t, &A2, &Ai[6]); - ge_p1p1_to_p3(&u, &t); - ge_p3_to_cached(&Ai[7], &u); - - ge_p2_0(&r); - - for(i = 255; i >= 0; --i) { - if(aslide[i] || bslide[i]) { - break; - } + auto aslide = slide(a); + auto bslide = slide(b); + + Ed25519_Point_Cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */ + Ai[0] = Ed25519_Point_Cached::from(A); + const auto A2 = Ed25519_Point_Extended::from(A.dbl()); + + for(size_t i = 1; i != 8; ++i) { + Ai[i] = Ed25519_Point_Cached::from(A2 + Ai[i - 1]); } + auto r = Ed25519_Point_Projective::identity(); + + int i = [&]() -> int { + int w = 255; + while(w >= 0) { + if(aslide[w] != 0 || bslide[w] != 0) { + return w; + } else { + w--; + } + } + return 0; + }(); + for(; i >= 0; --i) { - ge_p2_dbl(&t, &r); + auto t = r.dbl(); if(aslide[i] > 0) { - ge_p1p1_to_p3(&u, &t); - ge_add(&t, &u, &Ai[aslide[i] >> 1]); + t = Ed25519_Point_Extended::from(t) + Ai[aslide[i] >> 1]; } else if(aslide[i] < 0) { - ge_p1p1_to_p3(&u, &t); - ge_sub(&t, &u, &Ai[(-aslide[i]) >> 1]); + t = Ed25519_Point_Extended::from(t) - Ai[(-aslide[i]) >> 1]; } if(bslide[i] > 0) { - ge_p1p1_to_p3(&u, &t); - ge_madd(&t, &u, &Bi[bslide[i] >> 1]); + t = Ed25519_Point_Extended::from(t) + Bi[bslide[i] >> 1]; } else if(bslide[i] < 0) { - ge_p1p1_to_p3(&u, &t); - ge_msub(&t, &u, &Bi[(-bslide[i]) >> 1]); + t = Ed25519_Point_Extended::from(t) - Bi[(-bslide[i]) >> 1]; } - ge_p1p1_to_p2(&r, &t); + r = Ed25519_Point_Projective::from(t); } - ge_tobytes(out, &r); + r.serialize_to(std::span{out}); +} + +inline uint32_t equal32(uint8_t b, uint8_t c) { + return CT::Mask::is_equal(b, c).value(); +} + +inline uint8_t negative(int8_t b) { + return static_cast(b) >> 7; +} + +Ed25519_Point_Niels select(const Ed25519_Point_Niels base[8], int8_t b) { + const uint8_t bnegative = negative(b); + const uint8_t babs = b - ((-static_cast(bnegative) & b) * 2); + const uint32_t neg_mask = equal32(bnegative, 1); + + const uint32_t mask1 = equal32(babs, 1); + const uint32_t mask2 = equal32(babs, 2); + const uint32_t mask3 = equal32(babs, 3); + const uint32_t mask4 = equal32(babs, 4); + const uint32_t mask5 = equal32(babs, 5); + const uint32_t mask6 = equal32(babs, 6); + const uint32_t mask7 = equal32(babs, 7); + const uint32_t mask8 = equal32(babs, 8); + + auto t = Ed25519_Point_Niels::identity(); + + for(size_t i = 0; i != 10; ++i) { + t.yplusx[i] = t.yplusx[i] ^ ((t.yplusx[i] ^ base[0].yplusx[i]) & mask1) ^ + ((t.yplusx[i] ^ base[1].yplusx[i]) & mask2) ^ ((t.yplusx[i] ^ base[2].yplusx[i]) & mask3) ^ + ((t.yplusx[i] ^ base[3].yplusx[i]) & mask4) ^ ((t.yplusx[i] ^ base[4].yplusx[i]) & mask5) ^ + ((t.yplusx[i] ^ base[5].yplusx[i]) & mask6) ^ ((t.yplusx[i] ^ base[6].yplusx[i]) & mask7) ^ + ((t.yplusx[i] ^ base[7].yplusx[i]) & mask8); + + t.yminusx[i] = t.yminusx[i] ^ ((t.yminusx[i] ^ base[0].yminusx[i]) & mask1) ^ + ((t.yminusx[i] ^ base[1].yminusx[i]) & mask2) ^ ((t.yminusx[i] ^ base[2].yminusx[i]) & mask3) ^ + ((t.yminusx[i] ^ base[3].yminusx[i]) & mask4) ^ ((t.yminusx[i] ^ base[4].yminusx[i]) & mask5) ^ + ((t.yminusx[i] ^ base[5].yminusx[i]) & mask6) ^ ((t.yminusx[i] ^ base[6].yminusx[i]) & mask7) ^ + ((t.yminusx[i] ^ base[7].yminusx[i]) & mask8); + + t.xy2d[i] = t.xy2d[i] ^ ((t.xy2d[i] ^ base[0].xy2d[i]) & mask1) ^ ((t.xy2d[i] ^ base[1].xy2d[i]) & mask2) ^ + ((t.xy2d[i] ^ base[2].xy2d[i]) & mask3) ^ ((t.xy2d[i] ^ base[3].xy2d[i]) & mask4) ^ + ((t.xy2d[i] ^ base[4].xy2d[i]) & mask5) ^ ((t.xy2d[i] ^ base[5].xy2d[i]) & mask6) ^ + ((t.xy2d[i] ^ base[6].xy2d[i]) & mask7) ^ ((t.xy2d[i] ^ base[7].xy2d[i]) & mask8); + } + + auto minus_xy2d = -t.xy2d; + + // If negative have to swap yminusx and yplusx + for(size_t i = 0; i != 10; ++i) { + const int32_t t_yplusx = t.yplusx[i] ^ ((t.yplusx[i] ^ t.yminusx[i]) & neg_mask); + const int32_t t_yminusx = t.yminusx[i] ^ ((t.yminusx[i] ^ t.yplusx[i]) & neg_mask); + + t.yplusx[i] = t_yplusx; + t.yminusx[i] = t_yminusx; + t.xy2d[i] = t.xy2d[i] ^ ((t.xy2d[i] ^ minus_xy2d[i]) & neg_mask); + } + + return t; } /* base[i][j] = (j+1)*256^i*B */ -static const ge_precomp B_precomp[32][8] = { +constexpr Ed25519_Point_Niels B_precomp[32][8] = { { { {25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605}, @@ -1939,96 +1850,6 @@ }, }; -namespace { - -inline uint8_t equal(int8_t b, int8_t c) { - uint8_t ub = b; - uint8_t uc = c; - uint8_t x = ub ^ uc; /* 0: yes; 1..255: no */ - uint32_t y = x; /* 0: yes; 1..255: no */ - y -= 1; /* 4294967295: yes; 0..254: no */ - y >>= 31; /* 1: yes; 0: no */ - return static_cast(y); -} - -inline int32_t equal32(int8_t b, int8_t c) { - return -static_cast(equal(b, c)); -} - -inline uint8_t negative(int8_t b) { - /* 18446744073709551361..18446744073709551615: yes; 0..255: no */ - uint64_t x = b; // NOLINT(bugprone-signed-char-misuse,cert-str34-c) - x >>= 63; /* 1: yes; 0: no */ - return static_cast(x); -} - -inline void ge_precomp_0(ge_precomp* h) { - fe_1(h->yplusx); - fe_1(h->yminusx); - fe_0(h->xy2d); -} - -inline void select(ge_precomp* t, const ge_precomp* base, int8_t b) { - const uint8_t bnegative = negative(b); - const uint8_t babs = b - ((-static_cast(bnegative) & b) * 2); - const int32_t neg_mask = equal32(bnegative, 1); - - const int32_t mask1 = equal32(babs, 1); - const int32_t mask2 = equal32(babs, 2); - const int32_t mask3 = equal32(babs, 3); - const int32_t mask4 = equal32(babs, 4); - const int32_t mask5 = equal32(babs, 5); - const int32_t mask6 = equal32(babs, 6); - const int32_t mask7 = equal32(babs, 7); - const int32_t mask8 = equal32(babs, 8); - - ge_precomp_0(t); - - for(size_t i = 0; i != 10; ++i) { - t->yplusx[i] = t->yplusx[i] ^ ((t->yplusx[i] ^ base[0].yplusx[i]) & mask1) ^ - ((t->yplusx[i] ^ base[1].yplusx[i]) & mask2) ^ ((t->yplusx[i] ^ base[2].yplusx[i]) & mask3) ^ - ((t->yplusx[i] ^ base[3].yplusx[i]) & mask4) ^ ((t->yplusx[i] ^ base[4].yplusx[i]) & mask5) ^ - ((t->yplusx[i] ^ base[5].yplusx[i]) & mask6) ^ ((t->yplusx[i] ^ base[6].yplusx[i]) & mask7) ^ - ((t->yplusx[i] ^ base[7].yplusx[i]) & mask8); - - t->yminusx[i] = t->yminusx[i] ^ ((t->yminusx[i] ^ base[0].yminusx[i]) & mask1) ^ - ((t->yminusx[i] ^ base[1].yminusx[i]) & mask2) ^ ((t->yminusx[i] ^ base[2].yminusx[i]) & mask3) ^ - ((t->yminusx[i] ^ base[3].yminusx[i]) & mask4) ^ ((t->yminusx[i] ^ base[4].yminusx[i]) & mask5) ^ - ((t->yminusx[i] ^ base[5].yminusx[i]) & mask6) ^ ((t->yminusx[i] ^ base[6].yminusx[i]) & mask7) ^ - ((t->yminusx[i] ^ base[7].yminusx[i]) & mask8); - - t->xy2d[i] = t->xy2d[i] ^ ((t->xy2d[i] ^ base[0].xy2d[i]) & mask1) ^ ((t->xy2d[i] ^ base[1].xy2d[i]) & mask2) ^ - ((t->xy2d[i] ^ base[2].xy2d[i]) & mask3) ^ ((t->xy2d[i] ^ base[3].xy2d[i]) & mask4) ^ - ((t->xy2d[i] ^ base[4].xy2d[i]) & mask5) ^ ((t->xy2d[i] ^ base[5].xy2d[i]) & mask6) ^ - ((t->xy2d[i] ^ base[6].xy2d[i]) & mask7) ^ ((t->xy2d[i] ^ base[7].xy2d[i]) & mask8); - } - - FE_25519 minus_xy2d; - fe_neg(minus_xy2d, t->xy2d); - - // If negative have to swap yminusx and yplusx - for(size_t i = 0; i != 10; ++i) { - int32_t t_yplusx = t->yplusx[i] ^ ((t->yplusx[i] ^ t->yminusx[i]) & neg_mask); - int32_t t_yminusx = t->yminusx[i] ^ ((t->yminusx[i] ^ t->yplusx[i]) & neg_mask); - - t->yplusx[i] = t_yplusx; - t->yminusx[i] = t_yminusx; - t->xy2d[i] = t->xy2d[i] ^ ((t->xy2d[i] ^ minus_xy2d[i]) & neg_mask); - } -} - -void ge_p3_tobytes(uint8_t* s, const ge_p3* h) { - FE_25519 recip; - FE_25519 x; - FE_25519 y; - - fe_invert(recip, h->Z); - fe_mul(x, h->X, recip); - fe_mul(y, h->Y, recip); - fe_tobytes(s, y); - s[31] ^= fe_isnegative(x) << 7; -} - } // namespace /* @@ -2039,25 +1860,19 @@ Preconditions: a[31] <= 127 */ +void ed25519_basepoint_mul(std::span out, const uint8_t a[32]) { + std::array e{}; + + CT::poison(a, 32); -void ge_scalarmult_base(uint8_t out[32], const uint8_t a[32]) { - int8_t e[64]; - int8_t carry; - ge_p1p1 r; - ge_p2 s; - ge_p3 h; - ge_precomp t; - int i; - - for(i = 0; i < 32; ++i) { - e[2 * i + 0] = (a[i] >> 0) & 15; - e[2 * i + 1] = (a[i] >> 4) & 15; + // each e[i] is between 0 and 15 except e[63] which is between 0 and 7 + for(size_t i = 0; i != 32; ++i) { + e[2 * i + 0] = (a[i] >> 0) & 0x0F; + e[2 * i + 1] = (a[i] >> 4) & 0x0F; } - /* each e[i] is between 0 and 15 */ - /* e[63] is between 0 and 7 */ - carry = 0; - for(i = 0; i < 63; ++i) { + int8_t carry = 0; + for(size_t i = 0; i < 63; ++i) { e[i] += carry; carry = e[i] + 8; carry >>= 4; @@ -2066,29 +1881,33 @@ e[63] += carry; /* each e[i] is between -8 and 8 */ - ge_p3_0(&h); - for(i = 1; i < 64; i += 2) { - select(&t, B_precomp[i / 2], e[i]); - ge_madd(&r, &h, &t); - ge_p1p1_to_p3(&h, &r); + auto h = Ed25519_Point_Extended::identity(); + for(size_t i = 1; i < 64; i += 2) { + h = Ed25519_Point_Extended::from(h + select(B_precomp[i / 2], e[i])); } - ge_p3_dbl(&r, &h); - ge_p1p1_to_p2(&s, &r); - ge_p2_dbl(&r, &s); - ge_p1p1_to_p2(&s, &r); - ge_p2_dbl(&r, &s); - ge_p1p1_to_p2(&s, &r); - ge_p2_dbl(&r, &s); - ge_p1p1_to_p3(&h, &r); - - for(i = 0; i < 64; i += 2) { - select(&t, B_precomp[i / 2], e[i]); - ge_madd(&r, &h, &t); - ge_p1p1_to_p3(&h, &r); + auto s = Ed25519_Point_Projective::from(h.dbl()); + s = Ed25519_Point_Projective::from(s.dbl()); + s = Ed25519_Point_Projective::from(s.dbl()); + h = Ed25519_Point_Extended::from(s.dbl()); + + for(size_t i = 0; i != 64; i += 2) { + h = Ed25519_Point_Extended::from(h + select(B_precomp[i / 2], e[i])); } - ge_p3_tobytes(out, &h); + h.serialize_to(out); + + CT::unpoison(a, 32); + CT::unpoison(out); +} + +bool signature_check(std::span pk, const uint8_t h[32], const uint8_t r[32], const uint8_t s[32]) { + if(auto A = frombytes_negate_vartime(pk)) { + std::array rcheck{}; + ge_double_scalarmult_vartime(rcheck, h, *A, s); + return CT::is_equal(rcheck.data(), r, 32).as_bool(); + } + return false; } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/elgamal/elgamal.cpp botan3-3.12.0+dfsg/src/lib/pubkey/elgamal/elgamal.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/elgamal/elgamal.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/elgamal/elgamal.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -105,12 +106,12 @@ /** * ElGamal encryption operation */ -class ElGamal_Encryption_Operation final : public PK_Ops::Encryption_with_EME { +class ElGamal_Encryption_Operation final : public PK_Ops::Encryption_with_Padding { public: - ElGamal_Encryption_Operation(const std::shared_ptr& key, std::string_view eme) : - PK_Ops::Encryption_with_EME(eme), m_key(key) { + ElGamal_Encryption_Operation(const std::shared_ptr& key, std::string_view padding) : + PK_Ops::Encryption_with_Padding(padding), m_key(key) { const size_t powm_window = 4; - m_monty_y_p = monty_precompute(m_key->group().monty_params_p(), m_key->public_key(), powm_window); + m_monty_y_p = monty_precompute(m_key->group()._monty_params_p(), m_key->public_key(), powm_window); } size_t ciphertext_length(size_t /*ptext_len*/) const override { return 2 * m_key->group().p_bytes(); } @@ -121,12 +122,12 @@ private: std::shared_ptr m_key; - std::shared_ptr m_monty_y_p; + std::shared_ptr m_monty_y_p; }; std::vector ElGamal_Encryption_Operation::raw_encrypt(std::span ptext, RandomNumberGenerator& rng) { - BigInt m(ptext); + const BigInt m(ptext); const auto& group = m_key->group(); @@ -148,18 +149,23 @@ const BigInt a = group.power_g_p(k, k_bits); const BigInt b = group.multiply_mod_p(m, monty_execute(*m_monty_y_p, k, k_bits).value()); - return unlock(BigInt::encode_fixed_length_int_pair(a, b, group.p_bytes())); + const size_t p_bytes = group.p_bytes(); + std::vector ctext(2 * p_bytes); + BufferStuffer stuffer(ctext); + a.serialize_to(stuffer.next(p_bytes)); + b.serialize_to(stuffer.next(p_bytes)); + return ctext; } /** * ElGamal decryption operation */ -class ElGamal_Decryption_Operation final : public PK_Ops::Decryption_with_EME { +class ElGamal_Decryption_Operation final : public PK_Ops::Decryption_with_Padding { public: ElGamal_Decryption_Operation(const std::shared_ptr& key, - std::string_view eme, + std::string_view padding, RandomNumberGenerator& rng) : - PK_Ops::Decryption_with_EME(eme), + PK_Ops::Decryption_with_Padding(padding), m_key(key), m_blinder( m_key->group()._reducer_mod_p(), diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/elgamal/elgamal.h botan3-3.12.0+dfsg/src/lib/pubkey/elgamal/elgamal.h --- botan3-3.7.1+dfsg/src/lib/pubkey/elgamal/elgamal.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/elgamal/elgamal.h 2026-05-07 01:38:28.000000000 +0000 @@ -63,7 +63,7 @@ ElGamal_PublicKey() = default; - ElGamal_PublicKey(std::shared_ptr key) : m_public_key(std::move(key)) {} + explicit ElGamal_PublicKey(std::shared_ptr key) : m_public_key(std::move(key)) {} std::shared_ptr m_public_key; }; @@ -99,7 +99,7 @@ */ ElGamal_PrivateKey(const DL_Group& group, const BigInt& private_key); - bool check_key(RandomNumberGenerator& rng, bool) const override; + bool check_key(RandomNumberGenerator& rng, bool strong) const override; std::unique_ptr public_key() const override; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/elgamal/info.txt botan3-3.12.0+dfsg/src/lib/pubkey/elgamal/info.txt --- botan3-3.7.1+dfsg/src/lib/pubkey/elgamal/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/elgamal/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -12,7 +12,7 @@ dl_group keypair numbertheory -pk_pad +enc_padding diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/frodokem/frodokem_aes/frodo_aes_generator.h botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_aes/frodo_aes_generator.h --- botan3-3.7.1+dfsg/src/lib/pubkey/frodokem/frodokem_aes/frodo_aes_generator.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_aes/frodo_aes_generator.h 2026-05-07 01:38:28.000000000 +0000 @@ -12,12 +12,10 @@ #define BOTAN_FRODOKEM_AES_GENERATOR_H_ #include +#include #include #include #include -#include - -#include #include namespace Botan { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_constants.cpp botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_constants.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_constants.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_constants.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,7 @@ #include +#include #include namespace Botan { @@ -96,9 +97,8 @@ FrodoKEMConstants::~FrodoKEMConstants() = default; -XOF& FrodoKEMConstants::SHAKE_XOF() const { - m_shake_xof->clear(); - return *m_shake_xof; +std::unique_ptr FrodoKEMConstants::create_xof() const { + return m_shake_xof->new_object(); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_constants.h botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_constants.h --- botan3-3.7.1+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_constants.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_constants.h 2026-05-07 01:38:28.000000000 +0000 @@ -24,7 +24,7 @@ class BOTAN_TEST_API FrodoKEMConstants final { public: - FrodoKEMConstants(FrodoKEMMode mode); + explicit FrodoKEMConstants(FrodoKEMMode mode); ~FrodoKEMConstants(); @@ -76,9 +76,7 @@ FrodoDomainSeparator keygen_domain_separator() const { return FrodoDomainSeparator({0x5F}); } - // TODO: those aren't actually const. We worked around some constness - // issues when playing with the XOFs that are residing in this class. - XOF& SHAKE_XOF() const; + std::unique_ptr create_xof() const; private: FrodoKEMMode m_mode; @@ -93,7 +91,7 @@ std::vector m_cdf_table; // Distribution table T_chi - mutable std::unique_ptr m_shake_xof; + std::unique_ptr m_shake_xof; std::string m_shake; }; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_matrix.cpp botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_matrix.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_matrix.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_matrix.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,7 +1,7 @@ /* * FrodoKEM matrix logic * Based on the MIT licensed reference implementation by the designers - * (https://github.com/microsoft/PQCrypto-LWEKE/tree/master/src) + * (https://github.com/microsoft/PQCrypto-LWEKE/tree/master) * * The Fellowship of the FrodoKEM: * (C) 2023 Jack Lloyd @@ -13,14 +13,15 @@ #include #include -#include -#include -#include #include #include +#include #include #include -#include +#include +#include +#include +#include #if defined(BOTAN_HAS_FRODOKEM_AES) #include @@ -30,14 +31,6 @@ #include #endif -#include -#include -#include -#include -#include -#include -#include - namespace Botan { namespace { @@ -130,8 +123,8 @@ std::vector a_row_data(4 * constants.n(), 0); // TODO: maybe use std::as_bytes() instead // (take extra care, as it produces a std::span) - std::span a_row_data_bytes(reinterpret_cast(a_row_data.data()), - sizeof(uint16_t) * a_row_data.size()); + const std::span a_row_data_bytes(reinterpret_cast(a_row_data.data()), + sizeof(uint16_t) * a_row_data.size()); for(size_t i = 0; i < constants.n(); i += 4) { auto a_row = BufferStuffer(a_row_data_bytes); @@ -189,8 +182,8 @@ */ std::vector a_row_data(8 * constants.n(), 0); // TODO: maybe use std::as_bytes() - std::span a_row_data_bytes(reinterpret_cast(a_row_data.data()), - sizeof(uint16_t) * a_row_data.size()); + const std::span a_row_data_bytes(reinterpret_cast(a_row_data.data()), + sizeof(uint16_t) * a_row_data.size()); // Start matrix multiplication for(size_t i = 0; i < constants.n(); i += 8) { @@ -211,7 +204,7 @@ for(size_t j = 0; j < constants.n_bar(); ++j) { uint16_t sum = 0; - std::array sp; + std::array sp{}; for(size_t p = 0; p < 8; ++p) { sp[p] = s.elements_at(j * constants.n() + i + p); } @@ -318,7 +311,7 @@ } FrodoMatrix FrodoMatrix::mul_bs(const FrodoKEMConstants& constants, const FrodoMatrix& b, const FrodoMatrix& s) { - Dimensions dimensions = {constants.n_bar(), constants.n_bar()}; + const Dimensions dimensions = {constants.n_bar(), constants.n_bar()}; auto elements = make_elements_vector(dimensions); for(size_t i = 0; i < constants.n_bar(); ++i) { @@ -453,7 +446,7 @@ while(b < lsb) { const uint8_t nbits = std::min(static_cast(lsb - b), bits); const uint16_t mask = static_cast(1 << nbits) - 1; - uint8_t t = (w >> (bits - nbits)) & mask; // the bits to copy from w to out + const uint8_t t = (w >> (bits - nbits)) & mask; // the bits to copy from w to out elements.at(i) = elements.at(i) + static_cast(t << (lsb - b - nbits)); b += nbits; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_matrix.h botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_matrix.h --- botan3-3.7.1+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_matrix.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_matrix.h 2026-05-07 01:38:28.000000000 +0000 @@ -1,7 +1,7 @@ /* * FrodoKEM matrix logic * Based on the MIT licensed reference implementation by the designers - * (https://github.com/microsoft/PQCrypto-LWEKE/tree/master/src) + * (https://github.com/microsoft/PQCrypto-LWEKE/tree/master) * * The Fellowship of the FrodoKEM: * (C) 2023 Jack Lloyd @@ -73,7 +73,7 @@ const Dimensions& dimensions, StrongSpan r); - // Helper function that calls FrodoMatrix::sample on initially provided consts and shake XOF. + // Helper function that calls FrodoMatrix::sample on initially provided constants and shake XOF. // The output function calls shake.output at each invocation. static std::function make_sample_generator( const FrodoKEMConstants& constants, Botan::XOF& shake); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_mode.cpp botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_mode.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_mode.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_mode.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,11 +12,6 @@ #include #include -#include - -#include -#include -#include namespace Botan { @@ -76,6 +71,26 @@ return OID::from_string(to_string()); } +bool FrodoKEMMode::is_available() const { + if(is_aes()) { +#if defined(BOTAN_HAS_FRODOKEM_AES) + return true; +#else + return false; +#endif + } + + if(is_shake()) { +#if defined(BOTAN_HAS_FRODOKEM_SHAKE) + return true; +#else + return false; +#endif + } + + return false; +} + std::string FrodoKEMMode::to_string() const { switch(m_mode) { case FrodoKEM640_SHAKE: diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_mode.h botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_mode.h --- botan3-3.7.1+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_mode.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_mode.h 2026-05-07 01:38:28.000000000 +0000 @@ -17,9 +17,9 @@ namespace Botan { -class BOTAN_PUBLIC_API(3, 3) FrodoKEMMode { +class BOTAN_PUBLIC_API(3, 3) FrodoKEMMode final { public: - enum Mode { + enum Mode : uint8_t /* NOLINT(*-use-enum-class) */ { FrodoKEM640_SHAKE, FrodoKEM976_SHAKE, FrodoKEM1344_SHAKE, @@ -34,7 +34,9 @@ eFrodoKEM1344_AES }; + // NOLINTNEXTLINE(*-explicit-conversions) FrodoKEMMode(Mode mode); + explicit FrodoKEMMode(const OID& oid); explicit FrodoKEMMode(std::string_view str); @@ -63,18 +65,7 @@ m_mode == FrodoKEM640_AES || m_mode == FrodoKEM976_AES || m_mode == FrodoKEM1344_AES; } - bool is_available() const { - return -#if defined(BOTAN_HAS_FRODOKEM_AES) - is_aes() || -#endif - -#if defined(BOTAN_HAS_FRODOKEM_SHAKE) - is_shake() || -#endif - - false; - } + bool is_available() const; bool operator==(const FrodoKEMMode& other) const { return m_mode == other.m_mode; } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_types.h botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_types.h --- botan3-3.7.1+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_types.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodo_types.h 2026-05-07 01:38:28.000000000 +0000 @@ -44,7 +44,7 @@ using FrodoSerializedMatrix = Strong, struct FrodoSerializedMatrix_>; // Constant byte 0x5F/0x96 given to SHAKE for domain separation -using FrodoDomainSeparator = Strong, struct FrodoDoaminSeparator_>; +using FrodoDomainSeparator = Strong, struct FrodoDomainSeparator_>; // Bytes of u/u' using FrodoPlaintext = Strong, struct FrodoPlaintext_>; @@ -52,7 +52,7 @@ // Bytes of salt using FrodoSalt = Strong, struct FrodoSalt_>; -// Bytes of k/k' aka intermediate shared secret in FO transform +// Bytes of k/k' aka intermediate shared secret in Fujisaki-Okamoto transform using FrodoIntermediateSharedSecret = Strong, struct FrodoIntermediateSharedSecret_>; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodokem.cpp botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodokem.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodokem.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodokem.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,7 +1,7 @@ /* - * FrodoKEM implemenation + * FrodoKEM implementation * Based on the MIT licensed reference implementation by the designers - * (https://github.com/microsoft/PQCrypto-LWEKE/tree/master/src) + * (https://github.com/microsoft/PQCrypto-LWEKE/tree/master) * * The Fellowship of the FrodoKEM: * (C) 2023 Jack Lloyd @@ -13,31 +13,31 @@ #include #include -#include +#include #include #include +#include +#include +#include #include #include #include #include -#include #include -#include -#include #include #include #include namespace Botan { -class FrodoKEM_PublicKeyInternal { +class FrodoKEM_PublicKeyInternal final { public: FrodoKEM_PublicKeyInternal(FrodoKEMConstants constants, FrodoSeedA seed_a, FrodoMatrix b) : m_constants(std::move(constants)), m_seed_a(std::move(seed_a)), m_b(std::move(b)) { - auto& shake = m_constants.SHAKE_XOF(); - shake.update(serialize()); - m_hash = shake.output(m_constants.len_sec_bytes()); + auto shake = m_constants.create_xof(); + shake->update(serialize()); + m_hash = shake->output(m_constants.len_sec_bytes()); } const FrodoKEMConstants& constants() const { return m_constants; } @@ -57,7 +57,7 @@ FrodoPublicKeyHash m_hash; }; -class FrodoKEM_PrivateKeyInternal { +class FrodoKEM_PrivateKeyInternal final { public: FrodoKEM_PrivateKeyInternal(FrodoSeedS s, FrodoMatrix s_trans) : m_s(std::move(s)), m_s_trans(std::move(s_trans)) {} @@ -79,6 +79,8 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // +namespace { + class Frodo_KEM_Encryptor final : public PK_Ops::KEM_Encryption_with_KDF { public: Frodo_KEM_Encryptor(std::shared_ptr key, std::string_view kdf) : @@ -91,55 +93,55 @@ void raw_kem_encrypt(std::span out_encapsulated_key, std::span out_shared_key, RandomNumberGenerator& rng) override { - const auto& consts = m_public_key->constants(); - auto& shake = consts.SHAKE_XOF(); - auto sample_generator = FrodoMatrix::make_sample_generator(consts, shake); + const auto& constants = m_public_key->constants(); + auto shake = constants.create_xof(); + auto sample_generator = FrodoMatrix::make_sample_generator(constants, *shake); BufferStuffer out_ct_bs(out_encapsulated_key); - auto c_1 = out_ct_bs.next(consts.len_packed_b_bytes()); - auto c_2 = out_ct_bs.next(consts.len_packed_c_bytes()); - auto salt = out_ct_bs.next(consts.len_salt_bytes()); + auto c_1 = out_ct_bs.next(constants.len_packed_b_bytes()); + auto c_2 = out_ct_bs.next(constants.len_packed_c_bytes()); + auto salt = out_ct_bs.next(constants.len_salt_bytes()); BOTAN_ASSERT_NOMSG(out_ct_bs.full()); - const auto u = rng.random_vec(consts.len_sec_bytes()); + const auto u = rng.random_vec(constants.len_sec_bytes()); rng.randomize(salt); CT::poison(u); - shake.update(m_public_key->hash()); - shake.update(u); - shake.update(salt); - const auto seed_se = shake.output(consts.len_se_bytes()); - const auto k = shake.output(consts.len_sec_bytes()); - shake.clear(); + shake->update(m_public_key->hash()); + shake->update(u); + shake->update(salt); + const auto seed_se = shake->output(constants.len_se_bytes()); + const auto k = shake->output(constants.len_sec_bytes()); + shake->clear(); - shake.update(consts.encapsulation_domain_separator()); - shake.update(seed_se); + shake->update(constants.encapsulation_domain_separator()); + shake->update(seed_se); - const auto s_p = sample_generator(std::tuple(consts.n_bar(), consts.n())); + const auto s_p = sample_generator(std::tuple(constants.n_bar(), constants.n())); - const auto e_p = sample_generator(std::tuple(consts.n_bar(), consts.n())); + const auto e_p = sample_generator(std::tuple(constants.n_bar(), constants.n())); - const auto b_p = FrodoMatrix::mul_add_sa_plus_e(consts, s_p, e_p, m_public_key->seed_a()); + const auto b_p = FrodoMatrix::mul_add_sa_plus_e(constants, s_p, e_p, m_public_key->seed_a()); - b_p.pack(consts, c_1); + b_p.pack(constants, c_1); - const auto e_pp = sample_generator(std::tuple(consts.n_bar(), consts.n_bar())); - shake.clear(); + const auto e_pp = sample_generator(std::tuple(constants.n_bar(), constants.n_bar())); + shake->clear(); - const auto v = FrodoMatrix::mul_add_sb_plus_e(consts, m_public_key->b(), s_p, e_pp); + const auto v = FrodoMatrix::mul_add_sb_plus_e(constants, m_public_key->b(), s_p, e_pp); - const auto encoded = FrodoMatrix::encode(consts, u); + const auto encoded = FrodoMatrix::encode(constants, u); - const auto c = FrodoMatrix::add(consts, v, encoded); + const auto c = FrodoMatrix::add(constants, v, encoded); - c.pack(consts, c_2); + c.pack(constants, c_2); - shake.update(out_encapsulated_key); - shake.update(k); - shake.output(out_shared_key); + shake->update(out_encapsulated_key); + shake->update(k); + shake->output(out_shared_key); CT::unpoison_all(out_shared_key, out_encapsulated_key); } @@ -162,69 +164,69 @@ void raw_kem_decrypt(std::span out_shared_key, std::span encapsulated_key) override { auto scope = CT::scoped_poison(*m_private_key); - const auto& consts = m_public_key->constants(); - auto& shake = consts.SHAKE_XOF(); - auto sample_generator = FrodoMatrix::make_sample_generator(consts, shake); + const auto& constants = m_public_key->constants(); + auto shake = constants.create_xof(); + auto sample_generator = FrodoMatrix::make_sample_generator(constants, *shake); - if(encapsulated_key.size() != consts.len_ct_bytes()) { + if(encapsulated_key.size() != constants.len_ct_bytes()) { throw Invalid_Argument("FrodoKEM ciphertext does not have the correct byte count"); } BufferSlicer ct_bs(encapsulated_key); - auto c_1 = ct_bs.take(consts.len_packed_b_bytes()); - auto c_2 = ct_bs.take(consts.len_packed_c_bytes()); - auto salt = ct_bs.take(consts.len_salt_bytes()); + auto c_1 = ct_bs.take(constants.len_packed_b_bytes()); + auto c_2 = ct_bs.take(constants.len_packed_c_bytes()); + auto salt = ct_bs.take(constants.len_salt_bytes()); BOTAN_ASSERT_NOMSG(ct_bs.empty()); - const auto b_p = FrodoMatrix::unpack(consts, {consts.n_bar(), consts.n()}, c_1); - const auto c = FrodoMatrix::unpack(consts, {consts.n_bar(), consts.n_bar()}, c_2); + const auto b_p = FrodoMatrix::unpack(constants, {constants.n_bar(), constants.n()}, c_1); + const auto c = FrodoMatrix::unpack(constants, {constants.n_bar(), constants.n_bar()}, c_2); - const auto w = FrodoMatrix::mul_bs(consts, b_p, m_private_key->s_trans()); - const auto m = FrodoMatrix::sub(consts, c, w); + const auto w = FrodoMatrix::mul_bs(constants, b_p, m_private_key->s_trans()); + const auto m = FrodoMatrix::sub(constants, c, w); - const auto seed_u_p = m.decode(consts); + const auto seed_u_p = m.decode(constants); - shake.update(m_public_key->hash()); - shake.update(seed_u_p); - shake.update(salt); + shake->update(m_public_key->hash()); + shake->update(seed_u_p); + shake->update(salt); - const auto seed_se_p = shake.output(consts.len_se_bytes()); - const auto k_p = shake.output(consts.len_sec_bytes()); - shake.clear(); + const auto seed_se_p = shake->output(constants.len_se_bytes()); + const auto k_p = shake->output(constants.len_sec_bytes()); + shake->clear(); - shake.update(consts.encapsulation_domain_separator()); - shake.update(seed_se_p); - const auto s_p = sample_generator(std::tuple(consts.n_bar(), consts.n())); + shake->update(constants.encapsulation_domain_separator()); + shake->update(seed_se_p); + const auto s_p = sample_generator(std::tuple(constants.n_bar(), constants.n())); - const auto e_p = sample_generator(std::tuple(consts.n_bar(), consts.n())); + const auto e_p = sample_generator(std::tuple(constants.n_bar(), constants.n())); - auto b_pp = FrodoMatrix::mul_add_sa_plus_e(consts, s_p, e_p, m_public_key->seed_a()); + auto b_pp = FrodoMatrix::mul_add_sa_plus_e(constants, s_p, e_p, m_public_key->seed_a()); - const auto e_pp = sample_generator(std::tuple(consts.n_bar(), consts.n_bar())); - shake.clear(); + const auto e_pp = sample_generator(std::tuple(constants.n_bar(), constants.n_bar())); + shake->clear(); - const auto v = FrodoMatrix::mul_add_sb_plus_e(consts, m_public_key->b(), s_p, e_pp); + const auto v = FrodoMatrix::mul_add_sb_plus_e(constants, m_public_key->b(), s_p, e_pp); - const auto encoded = FrodoMatrix::encode(consts, seed_u_p); - auto c_p = FrodoMatrix::add(consts, v, encoded); + const auto encoded = FrodoMatrix::encode(constants, seed_u_p); + auto c_p = FrodoMatrix::add(constants, v, encoded); // b_p and c are unpacked values that are reduced by definition. // b_pp and c_p are calculated values that need the reduction for // an unambiguous comparison that is required next. - b_pp.reduce(consts); - c_p.reduce(consts); + b_pp.reduce(constants); + c_p.reduce(constants); // The spec concats the matrices b_p and c (b_pp and c_p respectively) // and performs a single CT comparison. For convenience we compare the // matrices individually in CT and CT-&& the resulting masks. const auto cmp = b_p.constant_time_compare(b_pp) & c.constant_time_compare(c_p); - std::vector k_bar(consts.len_sec_bytes(), 0); - CT::conditional_copy_mem(cmp, k_bar.data(), k_p.data(), m_private_key->s().data(), consts.len_sec_bytes()); + secure_vector k_bar(constants.len_sec_bytes(), 0); + CT::conditional_copy_mem(cmp, k_bar.data(), k_p.data(), m_private_key->s().data(), constants.len_sec_bytes()); - shake.update(encapsulated_key); - shake.update(k_bar); - shake.output(out_shared_key); + shake->update(encapsulated_key); + shake->update(k_bar); + shake->output(out_shared_key); CT::unpoison(out_shared_key); } @@ -234,24 +236,26 @@ std::shared_ptr m_private_key; }; +} // namespace + // // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // FrodoKEM_PublicKey::FrodoKEM_PublicKey(std::span pub_key, FrodoKEMMode mode) { - FrodoKEMConstants consts(mode); - if(pub_key.size() != consts.len_public_key_bytes()) { + FrodoKEMConstants constants(mode); + if(pub_key.size() != constants.len_public_key_bytes()) { throw Invalid_Argument("FrodoKEM public key does not have the correct byte count"); } BufferSlicer pk_bs(pub_key); - auto seed_a = pk_bs.copy(consts.len_a_bytes()); - const auto packed_b = pk_bs.take(consts.d() * consts.n() * consts.n_bar() / 8); + auto seed_a = pk_bs.copy(constants.len_a_bytes()); + const auto packed_b = pk_bs.take(constants.d() * constants.n() * constants.n_bar() / 8); BOTAN_ASSERT_NOMSG(pk_bs.empty()); - auto b = FrodoMatrix::unpack(consts, std::tuple(consts.n(), consts.n_bar()), packed_b); + auto b = FrodoMatrix::unpack(constants, std::tuple(constants.n(), constants.n_bar()), packed_b); - m_public = std::make_shared(std::move(consts), std::move(seed_a), std::move(b)); + m_public = std::make_shared(std::move(constants), std::move(seed_a), std::move(b)); } FrodoKEM_PublicKey::FrodoKEM_PublicKey(const AlgorithmIdentifier& alg_id, std::span key_bits) : @@ -296,7 +300,11 @@ return raw_public_key_bits(); } -bool FrodoKEM_PublicKey::check_key(RandomNumberGenerator&, bool) const { +bool FrodoKEM_PublicKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const { + // The public key consists of (seed_a, b) where b is a matrix of elements + // mod q = 2^d. Length validation is performed in the constructor, and bit + // unpacking naturally constrains all matrix elements to [0, 2^d - 1] which + // is the full valid range, leaving no further structural checks to perform. return true; } @@ -317,54 +325,54 @@ // FrodoKEM_PrivateKey::FrodoKEM_PrivateKey(RandomNumberGenerator& rng, FrodoKEMMode mode) { - FrodoKEMConstants consts(mode); - auto& shake = consts.SHAKE_XOF(); + FrodoKEMConstants constants(mode); + auto shake = constants.create_xof(); - auto s = rng.random_vec(consts.len_sec_bytes()); - const auto seed_se = rng.random_vec(consts.len_se_bytes()); - const auto z = rng.random_vec(consts.len_a_bytes()); + auto s = rng.random_vec(constants.len_sec_bytes()); + const auto seed_se = rng.random_vec(constants.len_se_bytes()); + const auto z = rng.random_vec(constants.len_a_bytes()); CT::poison_all(s, seed_se); - shake.update(z); - auto seed_a = shake.output(consts.len_a_bytes()); - shake.clear(); - - shake.update(consts.keygen_domain_separator()); - shake.update(seed_se); - - auto sample_generator = FrodoMatrix::make_sample_generator(consts, shake); - auto s_trans = sample_generator(std::tuple(consts.n_bar(), consts.n())); - auto e = sample_generator(std::tuple(consts.n(), consts.n_bar())); - shake.clear(); + shake->update(z); + auto seed_a = shake->output(constants.len_a_bytes()); + shake->clear(); + + shake->update(constants.keygen_domain_separator()); + shake->update(seed_se); + + auto sample_generator = FrodoMatrix::make_sample_generator(constants, *shake); + auto s_trans = sample_generator(std::tuple(constants.n_bar(), constants.n())); + auto e = sample_generator(std::tuple(constants.n(), constants.n_bar())); + shake->clear(); - auto b = FrodoMatrix::mul_add_as_plus_e(consts, s_trans, e, seed_a); + auto b = FrodoMatrix::mul_add_as_plus_e(constants, s_trans, e, seed_a); CT::unpoison_all(s, s_trans, b); - m_public = std::make_shared(std::move(consts), std::move(seed_a), std::move(b)); + m_public = std::make_shared(std::move(constants), std::move(seed_a), std::move(b)); m_private = std::make_shared(std::move(s), std::move(s_trans)); } FrodoKEM_PrivateKey::FrodoKEM_PrivateKey(std::span sk, FrodoKEMMode mode) { - FrodoKEMConstants consts(mode); + FrodoKEMConstants constants(mode); - if(sk.size() != consts.len_private_key_bytes()) { + if(sk.size() != constants.len_private_key_bytes()) { throw Invalid_Argument("FrodoKEM private key does not have the correct byte count"); } BufferSlicer sk_bs(sk); - auto s = sk_bs.copy(consts.len_sec_bytes()); - auto seed_a = sk_bs.copy(consts.len_a_bytes()); - const auto packed_b = sk_bs.take(consts.d() * consts.n() * consts.n_bar() / 8); - const auto s_trans_bytes = sk_bs.take(consts.n_bar() * consts.n() * 2); - const auto pkh = sk_bs.copy(consts.len_sec_bytes()); + auto s = sk_bs.copy(constants.len_sec_bytes()); + auto seed_a = sk_bs.copy(constants.len_a_bytes()); + const auto packed_b = sk_bs.take(constants.d() * constants.n() * constants.n_bar() / 8); + const auto s_trans_bytes = sk_bs.take(constants.n_bar() * constants.n() * 2); + const auto pkh = sk_bs.copy(constants.len_sec_bytes()); BOTAN_ASSERT_NOMSG(sk_bs.empty()); - auto b = FrodoMatrix::unpack(consts, std::tuple(consts.n(), consts.n_bar()), packed_b); - auto s_trans = FrodoMatrix::deserialize({consts.n_bar(), consts.n()}, s_trans_bytes); + auto b = FrodoMatrix::unpack(constants, std::tuple(constants.n(), constants.n_bar()), packed_b); + auto s_trans = FrodoMatrix::deserialize({constants.n_bar(), constants.n()}, s_trans_bytes); - m_public = std::make_shared(std::move(consts), std::move(seed_a), std::move(b)); + m_public = std::make_shared(std::move(constants), std::move(seed_a), std::move(b)); m_private = std::make_shared(std::move(s), std::move(s_trans)); BOTAN_STATE_CHECK(pkh == m_public->hash()); @@ -377,6 +385,22 @@ return std::make_unique(*this); } +bool FrodoKEM_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const { + if(!FrodoKEM_PublicKey::check_key(rng, strong)) { + return false; + } + + if(strong) { + PK_KEM_Encryptor enc(*this, "Raw"); + PK_KEM_Decryptor dec(*this, rng, "Raw"); + const auto [c, K] = KEM_Encapsulation::destructure(enc.encrypt(rng)); + const auto K_prime = dec.decrypt(c); + return K == K_prime; + } + + return true; +} + secure_vector FrodoKEM_PrivateKey::private_key_bits() const { return raw_private_key_bits(); // TODO: check if we need to do something else here } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodokem.h botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodokem.h --- botan3-3.7.1+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodokem.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/frodokem/frodokem_common/frodokem.h 2026-05-07 01:38:28.000000000 +0000 @@ -1,7 +1,7 @@ /* * FrodoKEM implementation * Based on the MIT licensed reference implementation by the designers - * (https://github.com/microsoft/PQCrypto-LWEKE/tree/master/src) + * (https://github.com/microsoft/PQCrypto-LWEKE/tree/master) * * The Fellowship of the FrodoKEM: * (C) 2023 Jack Lloyd @@ -16,7 +16,6 @@ #include #include -#include #include namespace Botan { @@ -58,7 +57,7 @@ std::vector public_key_bits() const override; - bool check_key(RandomNumberGenerator&, bool) const override; + bool check_key(RandomNumberGenerator& rng, bool strong) const override; bool supports_operation(PublicKeyOperation op) const override { return (op == PublicKeyOperation::KeyEncapsulation); @@ -73,7 +72,7 @@ FrodoKEM_PublicKey() = default; protected: - std::shared_ptr m_public; // NOLINT(misc-non-private-member-variables-in-classes) + std::shared_ptr m_public; // NOLINT(*-non-private-member-variable*) }; BOTAN_DIAGNOSTIC_PUSH @@ -90,6 +89,8 @@ std::unique_ptr public_key() const override; + bool check_key(RandomNumberGenerator& rng, bool strong) const override; + secure_vector private_key_bits() const override; secure_vector raw_private_key_bits() const override; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/gost_3410/gost_3410.cpp botan3-3.12.0+dfsg/src/lib/pubkey/gost_3410/gost_3410.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/gost_3410/gost_3410.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/gost_3410/gost_3410.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -29,6 +29,10 @@ } // namespace +std::optional GOST_3410_PublicKey::_signature_element_size_for_DER_encoding() const { + return domain().get_order_bytes(); +} + std::vector GOST_3410_PublicKey::public_key_bits() const { auto bits = _public_ec_point().xy_bytes(); @@ -70,12 +74,17 @@ OID ecc_param_id; // The parameters also includes hash and cipher OIDs - BER_Decoder(alg_id.parameters()).start_sequence().decode(ecc_param_id); + BER_Decoder(alg_id.parameters(), BER_Decoder::Limits::DER()) + .start_sequence() + .decode(ecc_param_id) + .discard_remaining() + .end_cons() + .verify_end(); auto group = check_domain(EC_Group::from_OID(ecc_param_id)); std::vector bits; - BER_Decoder(key_bits).decode(bits, ASN1_Type::OctetString); + BER_Decoder(key_bits, BER_Decoder::Limits::DER()).decode(bits, ASN1_Type::OctetString).verify_end(); if(bits.size() != 2 * (group.get_p_bits() / 8)) { throw Decoding_Error("GOST-34.10-2012 invalid encoding of public key"); @@ -124,8 +133,8 @@ */ class GOST_3410_Signature_Operation final : public PK_Ops::Signature_with_Hash { public: - GOST_3410_Signature_Operation(const GOST_3410_PrivateKey& gost_3410, std::string_view emsa) : - PK_Ops::Signature_with_Hash(emsa), m_group(gost_3410.domain()), m_x(gost_3410._private_key()) {} + GOST_3410_Signature_Operation(const GOST_3410_PrivateKey& gost_3410, std::string_view hash_fn) : + PK_Ops::Signature_with_Hash(hash_fn), m_group(gost_3410.domain()), m_x(gost_3410._private_key()) {} size_t signature_length() const override { return 2 * m_group.get_order_bytes(); } @@ -136,7 +145,6 @@ private: const EC_Group m_group; const EC_Scalar m_x; - std::vector m_ws; }; AlgorithmIdentifier GOST_3410_Signature_Operation::algorithm_identifier() const { @@ -166,7 +174,7 @@ const auto e = gost_msg_to_scalar(m_group, msg); const auto k = EC_Scalar::random(m_group, rng); - const auto r = EC_Scalar::gk_x_mod_order(k, rng, m_ws); + const auto r = EC_Scalar::gk_x_mod_order(k, rng); const auto s = (r * m_x) + (k * e); if(r.is_zero() || s.is_zero()) { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/gost_3410/gost_3410.h botan3-3.12.0+dfsg/src/lib/pubkey/gost_3410/gost_3410.h --- botan3-3.7.1+dfsg/src/lib/pubkey/gost_3410/gost_3410.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/gost_3410/gost_3410.h 2026-05-07 01:38:28.000000000 +0000 @@ -54,9 +54,7 @@ std::vector public_key_bits() const override; - std::optional _signature_element_size_for_DER_encoding() const override { - return domain().get_order_bytes(); - } + std::optional _signature_element_size_for_DER_encoding() const override; Signature_Format _default_x509_signature_format() const override { return Signature_Format::Standard; } @@ -117,7 +115,9 @@ std::unique_ptr public_key() const override; - AlgorithmIdentifier pkcs8_algorithm_identifier() const override { return EC_PublicKey::algorithm_identifier(); } + AlgorithmIdentifier pkcs8_algorithm_identifier() const override { + return EC_PublicKey::algorithm_identifier(); // NOLINT(bugprone-parent-virtual-call) + } std::unique_ptr create_signature_op(RandomNumberGenerator& rng, std::string_view params, diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/hss_lms/hss.cpp botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/hss.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/hss_lms/hss.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/hss.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ /** * HSS - Hierarchical Signatures System (RFC 8554) - * (C) 2023 Jack Lloyd + * (C) 2023,2026 Jack Lloyd * 2023 Fabian Albert, Philippe Lieser - Rohde & Schwarz Cybersecurity GmbH * * Botan is released under the Simplified BSD License (see license.txt) @@ -8,13 +8,15 @@ #include +#include +#include +#include +#include #include #include #include #include -#include - -#include +#include #include namespace Botan { @@ -62,7 +64,7 @@ for(int32_t layer_ctr = hss_params.L().get() - 1; layer_ctr >= 0; --layer_ctr) { HSS_Level layer(layer_ctr); const HSS_LMS_Params::LMS_LMOTS_Params_Pair& layer_params = hss_params.params_at_level(layer); - size_t layer_h = layer_params.lms_params().h(); + const size_t layer_h = layer_params.lms_params().h(); q.at(layer.get()) = checked_cast_to(hss_idx.get() % checked_cast_to(1ULL << layer_h)); hss_idx = hss_idx >> layer_h; @@ -80,7 +82,7 @@ "Invalid number of levels"); } -HSS_LMS_Params::HSS_LMS_Params(std::string_view algo_params) { +HSS_LMS_Params::HSS_LMS_Params(std::string_view algo_params) : m_max_sig_count(0) { const auto wrap_in_hss_lms = [&]() { if(algo_params.starts_with("HSS-LMS(")) { return std::string(algo_params); @@ -88,14 +90,14 @@ return fmt("HSS-LMS({})", algo_params); } }(); - SCAN_Name scan(wrap_in_hss_lms); + const SCAN_Name scan(wrap_in_hss_lms); BOTAN_ARG_CHECK(scan.arg_count() >= 2 && scan.arg_count() <= HSS_MAX_LEVELS + 1, "Invalid number of arguments"); - std::string hash = scan.arg(0); + const std::string hash = scan.arg(0); BOTAN_ARG_CHECK(is_supported_hash_function(hash), "Supported HSS-LMS hash function"); for(size_t i = 1; i < scan.arg_count(); ++i) { - SCAN_Name scan_layer(scan.arg(i)); + const SCAN_Name scan_layer(scan.arg(i)); BOTAN_ARG_CHECK(scan_layer.algo_name() == "HW", "Invalid name for layer parameters"); BOTAN_ARG_CHECK(scan_layer.arg_count() == 2, "Invalid number of layer parameters"); const auto h = @@ -119,9 +121,26 @@ } HSS_LMS_PrivateKeyInternal::HSS_LMS_PrivateKeyInternal(const HSS_LMS_Params& hss_params, RandomNumberGenerator& rng) : - m_hss_params(hss_params), m_current_idx(0), m_sig_size(HSS_Signature::size(m_hss_params)) { - m_hss_seed = rng.random_vec(m_hss_params.params_at_level(HSS_Level(0)).lms_params().m()); - m_identifier = rng.random_vec(LMS_IDENTIFIER_LEN); + m_hss_params(hss_params), + m_hss_seed(rng.random_vec(m_hss_params.params_at_level(HSS_Level(0)).lms_params().m())), + m_identifier(rng.random_vec(LMS_IDENTIFIER_LEN)), + // LMS doesn't have a single unique parameter code that we can easily use, + // so algo_params is left as 0 + m_keyid("HSS-LMS", 0, m_hss_seed, m_identifier), + m_sig_size(HSS_Signature::size(m_hss_params)) {} + +HSS_LMS_PrivateKeyInternal::HSS_LMS_PrivateKeyInternal(HSS_LMS_Params hss_params, + LMS_Seed hss_seed, + LMS_Identifier identifier) : + m_hss_params(std::move(hss_params)), + m_hss_seed(std::move(hss_seed)), + m_identifier(std::move(identifier)), + // LMS doesn't have a single unique parameter code that we can easily use, so algo_params is left as 0 + m_keyid("HSS-LMS", 0, m_hss_seed, m_identifier), + m_sig_size(HSS_Signature::size(m_hss_params)) { + BOTAN_ARG_CHECK(m_hss_seed.size() == m_hss_params.params_at_level(HSS_Level(0)).lms_params().m(), + "Invalid HSS-LMS seed size"); + BOTAN_ARG_CHECK(m_identifier.size() == LMS_IDENTIFIER_LEN, "Invalid HSS-LMS identifier size"); } std::shared_ptr HSS_LMS_PrivateKeyInternal::from_bytes_or_throw( @@ -147,13 +166,14 @@ const auto lmots_type = load_be(slicer.take()); params.push_back({LMS_Params::create_or_throw(lms_type), LMOTS_Params::create_or_throw(lmots_type)}); } - std::string hash_name = params.at(0).lms_params().hash_name(); - if(std::any_of(params.begin(), params.end(), [&hash_name](HSS_LMS_Params::LMS_LMOTS_Params_Pair& lms_lmots_params) { - bool invalid_lmots_hash = lms_lmots_params.lmots_params().hash_name() != hash_name; - bool invalid_lms_hash = lms_lmots_params.lms_params().hash_name() != hash_name; - return invalid_lmots_hash || invalid_lms_hash; - })) { - throw Decoding_Error("Inconsistent hash functions are not allowed."); + const auto& hash_name = params.at(0).lms_params().hash_name(); + + for(const auto& param : params) { + const bool invalid_lmots_hash = param.lmots_params().hash_name() != hash_name; + const bool invalid_lms_hash = param.lms_params().hash_name() != hash_name; + if(invalid_lmots_hash || invalid_lms_hash) { + throw Decoding_Error("Inconsistent hash functions are not allowed."); + } } if(slicer.remaining() < params.at(0).lms_params().m() + LMS_IDENTIFIER_LEN) { @@ -176,8 +196,10 @@ secure_vector sk_bytes(size()); BufferStuffer stuffer(sk_bytes); + const uint64_t current_index = Stateful_Key_Index_Registry::global().current_index(m_keyid); + stuffer.append(store_be(hss_params().L())); - stuffer.append(store_be(get_idx())); + stuffer.append(store_be(current_index)); for(HSS_Level layer(1); layer <= hss_params().L(); ++layer) { const auto& params = hss_params().params_at_level(layer - 1); @@ -191,17 +213,20 @@ return sk_bytes; } +HSS_Sig_Idx HSS_LMS_PrivateKeyInternal::remaining_operations(HSS_Sig_Idx idx) const { + return HSS_Sig_Idx(Stateful_Key_Index_Registry::global().remaining_operations(m_keyid, idx.get())); +} + void HSS_LMS_PrivateKeyInternal::set_idx(HSS_Sig_Idx idx) { - m_current_idx = idx; + Stateful_Key_Index_Registry::global().set_index_lower_bound(m_keyid, idx.get()); } HSS_Sig_Idx HSS_LMS_PrivateKeyInternal::reserve_next_idx() { - HSS_Sig_Idx next_idx = m_current_idx; - if(next_idx >= m_hss_params.max_sig_count()) { + const auto idx = HSS_Sig_Idx(Stateful_Key_Index_Registry::global().reserve_next_index(m_keyid)); + if(idx >= m_hss_params.max_sig_count()) { throw Decoding_Error("HSS private key is exhausted"); } - set_idx(m_current_idx + 1); - return next_idx; + return idx; } size_t HSS_LMS_PrivateKeyInternal::size() const { @@ -212,19 +237,6 @@ return sk_size; } -HSS_LMS_PrivateKeyInternal::HSS_LMS_PrivateKeyInternal(HSS_LMS_Params hss_params, - LMS_Seed hss_seed, - LMS_Identifier identifier) : - m_hss_params(std::move(hss_params)), - m_hss_seed(std::move(hss_seed)), - m_identifier(std::move(identifier)), - m_current_idx(0), - m_sig_size(HSS_Signature::size(m_hss_params)) { - BOTAN_ARG_CHECK(m_hss_seed.size() == m_hss_params.params_at_level(HSS_Level(0)).lms_params().m(), - "Invalid seed size"); - BOTAN_ARG_CHECK(m_identifier.size() == LMS_IDENTIFIER_LEN, "Invalid identifier size"); -} - std::vector HSS_LMS_PrivateKeyInternal::sign(std::span msg) { std::vector sig(HSS_Signature::size(hss_params())); BufferStuffer sig_stuffer(sig); @@ -273,7 +285,7 @@ } LMS_PrivateKey HSS_LMS_PrivateKeyInternal::hss_derive_root_lms_private_key() const { - auto& top_params = hss_params().params_at_level(HSS_Level(0)); + const auto& top_params = hss_params().params_at_level(HSS_Level(0)); return LMS_PrivateKey(top_params.lms_params(), top_params.lmots_params(), m_identifier, m_hss_seed); } @@ -303,7 +315,7 @@ } HSS_LMS_PublicKeyInternal HSS_LMS_PublicKeyInternal::create(const HSS_LMS_PrivateKeyInternal& hss_sk) { - auto& hss_params = hss_sk.hss_params(); + const auto& hss_params = hss_sk.hss_params(); const auto root_sk = hss_sk.hss_derive_root_lms_private_key(); LMS_PublicKey top_pub_key = LMS_PublicKey(root_sk); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/hss_lms/hss.h botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/hss.h --- botan3-3.7.1+dfsg/src/lib/pubkey/hss_lms/hss.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/hss.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,11 +10,10 @@ #define BOTAN_HSS_H_ #include -#include #include #include #include - +#include #include #include #include @@ -23,6 +22,8 @@ namespace Botan { +class RandomNumberGenerator; + /** * @brief The index of a node within a specific LMS tree layer */ @@ -75,7 +76,7 @@ * @brief Construct the HSS-LMS parameters form an algorithm parameter string. * * The HSS/LMS instance to use for creating new keys is defined using an algorithm parameter string, - * i.e. to define which hash function (hash), LMS tree hights (h) + * i.e. to define which hash function (hash), LMS tree height (h) * and OTS Winternitz coefficient widths (w) to use. The syntax is the following: * * HSS-LMS(,HW(,),HW(,),...) @@ -155,13 +156,14 @@ /** * @brief Get the idx of the next signature to generate. */ - HSS_Sig_Idx get_idx() const { return m_current_idx; } + HSS_Sig_Idx remaining_operations(HSS_Sig_Idx idx) const; /** * @brief Set the idx of the next signature to generate. * * Note that creating two signatures with the same index is insecure. * The index must be lower than hss_params().max_sig_count(). + * The index will never go backward (highest value wins). */ void set_idx(HSS_Sig_Idx idx); @@ -230,7 +232,7 @@ HSS_LMS_Params m_hss_params; LMS_Seed m_hss_seed; LMS_Identifier m_identifier; - HSS_Sig_Idx m_current_idx; + Stateful_Key_Index_Registry::KeyId m_keyid; const size_t m_sig_size; }; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/hss_lms/hss_lms.cpp botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/hss_lms.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/hss_lms/hss_lms.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/hss_lms.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -43,7 +43,7 @@ return m_public->object_identifier(); } -bool HSS_LMS_PublicKey::check_key(RandomNumberGenerator&, bool) const { +bool HSS_LMS_PublicKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const { // Nothing to check. Only useful checks are already done during parsing. return true; } @@ -58,9 +58,11 @@ return raw_public_key_bits(); } +namespace { + class HSS_LMS_Verification_Operation final : public PK_Ops::Verification { public: - HSS_LMS_Verification_Operation(std::shared_ptr pub_key) : + explicit HSS_LMS_Verification_Operation(std::shared_ptr pub_key) : m_public(std::move(pub_key)) {} void update(std::span msg) override { @@ -85,6 +87,8 @@ std::vector m_msg_buffer; }; +} // namespace + std::unique_ptr HSS_LMS_PublicKey::create_verification_op(std::string_view /*params*/, std::string_view provider) const { if(provider.empty() || provider == "base") { @@ -108,7 +112,7 @@ return op == PublicKeyOperation::Signature; } -std::unique_ptr HSS_LMS_PublicKey::generate_another(RandomNumberGenerator&) const { +std::unique_ptr HSS_LMS_PublicKey::generate_another(RandomNumberGenerator& /*rng*/) const { // For this key type we cannot derive all required parameters from just // the public key. It is however possible to call HSS_LMS_PrivateKey::generate_another(). throw Not_Implemented("Cannot generate a new HSS/LMS keypair from a public key"); @@ -122,7 +126,7 @@ } HSS_LMS_PrivateKey::HSS_LMS_PrivateKey(RandomNumberGenerator& rng, std::string_view algo_params) { - HSS_LMS_Params hss_params(algo_params); + const HSS_LMS_Params hss_params(algo_params); m_private = std::make_shared(hss_params, rng); auto scope = CT::scoped_poison(*m_private); m_public = std::make_shared(HSS_LMS_PublicKeyInternal::create(*m_private)); @@ -158,7 +162,7 @@ } std::optional HSS_LMS_PrivateKey::remaining_operations() const { - return (m_private->hss_params().max_sig_count() - m_private->get_idx()).get(); + return m_private->remaining_operations(m_private->hss_params().max_sig_count()).get(); } std::unique_ptr HSS_LMS_PrivateKey::generate_another(RandomNumberGenerator& rng) const { @@ -167,6 +171,8 @@ new HSS_LMS_PrivateKey(std::make_shared(m_private->hss_params(), rng))); } +namespace { + class HSS_LMS_Signature_Operation final : public PK_Ops::Signature { public: HSS_LMS_Signature_Operation(std::shared_ptr private_key, @@ -177,7 +183,7 @@ m_msg_buffer.insert(m_msg_buffer.end(), msg.begin(), msg.end()); } - std::vector sign(RandomNumberGenerator&) override { + std::vector sign(RandomNumberGenerator& /*rng*/) override { std::vector message_to_sign = std::exchange(m_msg_buffer, {}); auto scope = CT::scoped_poison(*m_private); return CT::driveby_unpoison(m_private->sign(message_to_sign)); @@ -195,6 +201,8 @@ std::vector m_msg_buffer; }; +} // namespace + std::unique_ptr HSS_LMS_PrivateKey::create_signature_op(RandomNumberGenerator& rng, std::string_view params, std::string_view provider) const { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/hss_lms/hss_lms.h botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/hss_lms.h --- botan3-3.7.1+dfsg/src/lib/pubkey/hss_lms/hss_lms.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/hss_lms.h 2026-05-07 01:38:28.000000000 +0000 @@ -36,9 +36,13 @@ /** * @brief Load an existing public key using its bytes. */ - HSS_LMS_PublicKey(std::span pub_key_bytes); + BOTAN_FUTURE_EXPLICIT HSS_LMS_PublicKey(std::span pub_key_bytes); ~HSS_LMS_PublicKey() override; + HSS_LMS_PublicKey(const HSS_LMS_PublicKey& other) = default; + HSS_LMS_PublicKey(HSS_LMS_PublicKey&& other) = default; + HSS_LMS_PublicKey& operator=(const HSS_LMS_PublicKey& other) = delete; + HSS_LMS_PublicKey& operator=(HSS_LMS_PublicKey&& other) = delete; size_t key_length() const override; @@ -67,7 +71,7 @@ protected: HSS_LMS_PublicKey() = default; - std::shared_ptr m_public; + std::shared_ptr m_public; // NOLINT(*non-private-member-variable*) }; BOTAN_DIAGNOSTIC_PUSH @@ -104,7 +108,7 @@ * HSS-LMS(,HW(,),HW(,),...) * * e.g. 'HSS-LMS(SHA-256,HW(5,1),HW(5,1))' to use SHA-256 in a two-layer HSS instance - * with a LMS tree hights 5 and w=1. The following parameters are allowed (which are + * with a LMS tree height 5 and w=1. The following parameters are allowed (which are * specified in RFC 8554 and draft-fluhrer-lms-more-parm-sets-11): * * hash: 'SHA-256', 'Truncated(SHA-256,192)', 'SHAKE-256(256)', SHAKE-256(192) @@ -119,7 +123,7 @@ /** * @brief Load an existing LMS private key using its bytes */ - HSS_LMS_PrivateKey(std::span private_key_bytes); + BOTAN_FUTURE_EXPLICIT HSS_LMS_PrivateKey(std::span private_key_bytes); /** * @brief Construct a new hss lms privatekey object. @@ -130,6 +134,10 @@ HSS_LMS_PrivateKey(RandomNumberGenerator& rng, std::string_view algo_params); ~HSS_LMS_PrivateKey() override; + HSS_LMS_PrivateKey(const HSS_LMS_PrivateKey& other) = delete; + HSS_LMS_PrivateKey(HSS_LMS_PrivateKey&& other) = default; + HSS_LMS_PrivateKey& operator=(const HSS_LMS_PrivateKey& other) = delete; + HSS_LMS_PrivateKey& operator=(HSS_LMS_PrivateKey&& other) = delete; secure_vector private_key_bits() const override; secure_vector raw_private_key_bits() const override; @@ -151,7 +159,7 @@ std::string_view provider) const override; private: - HSS_LMS_PrivateKey(std::shared_ptr sk); + explicit HSS_LMS_PrivateKey(std::shared_ptr sk); std::shared_ptr m_private; }; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/hss_lms/hss_lms_utils.cpp botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/hss_lms_utils.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/hss_lms/hss_lms_utils.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/hss_lms_utils.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,8 +8,6 @@ #include -#include - namespace Botan { // The magic numbers in the initializer list below reflect the structure of the diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/hss_lms/hss_lms_utils.h botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/hss_lms_utils.h --- botan3-3.7.1+dfsg/src/lib/pubkey/hss_lms/hss_lms_utils.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/hss_lms_utils.h 2026-05-07 01:38:28.000000000 +0000 @@ -30,7 +30,7 @@ /** * @brief Create a PseudorandomKeyGeneration instance for a fixed @p identifier */ - PseudorandomKeyGeneration(std::span identifier); + explicit PseudorandomKeyGeneration(std::span identifier); /** * @brief Specify the value for the u32str(q) hash input field diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/hss_lms/info.txt botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/info.txt --- botan3-3.7.1+dfsg/src/lib/pubkey/hss_lms/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -21,6 +21,7 @@ rng sha2_32 shake +stateful_key_index trunc_hash tree_hash diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/hss_lms/lm_ots.cpp botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/lm_ots.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/hss_lms/lm_ots.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/lm_ots.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include #include @@ -97,6 +99,10 @@ } // namespace +std::unique_ptr LMOTS_Params::hash() const { + return HashFunction::create_or_throw(hash_name()); +} + LMOTS_Params LMOTS_Params::create_or_throw(LMOTS_Algorithm_Type type) { auto [hash_name, w] = [](const LMOTS_Algorithm_Type& lmots_type) -> std::pair { switch(lmots_type) { @@ -145,7 +151,7 @@ if(w != 1 && w != 2 && w != 4 && w != 8) { throw Decoding_Error("Invalid Winternitz parameter"); } - LMOTS_Algorithm_Type type = [](std::string_view hash, uint8_t w_p) -> LMOTS_Algorithm_Type { + const LMOTS_Algorithm_Type type = [](std::string_view hash, uint8_t w_p) -> LMOTS_Algorithm_Type { if(hash == "SHA-256") { switch(w_p) { case 1: @@ -223,7 +229,7 @@ std::vector C, std::vector y_buffer) : m_algorithm_type(lmots_type), m_C(std::move(C)), m_y_buffer(std::move(y_buffer)) { - LMOTS_Params params = LMOTS_Params::create_or_throw(m_algorithm_type); + const LMOTS_Params params = LMOTS_Params::create_or_throw(m_algorithm_type); BufferSlicer y_slicer(m_y_buffer); for(uint16_t i = 0; i < params.p(); ++i) { @@ -233,7 +239,7 @@ } LMOTS_Signature LMOTS_Signature::from_bytes_or_throw(BufferSlicer& slicer) { - size_t total_remaining_bytes = slicer.remaining(); + const size_t total_remaining_bytes = slicer.remaining(); // Alg. 6a. 1. (last 4 bytes) / Alg. 4b. 1. if(total_remaining_bytes < sizeof(LMOTS_Algorithm_Type)) { throw Decoding_Error("Too few signature bytes while parsing LMOTS signature."); @@ -242,7 +248,7 @@ auto algorithm_type = load_be(slicer.take()); // Alg. 6a. 2.d. / Alg. 4b. 2.c. - LMOTS_Params params = LMOTS_Params::create_or_throw(algorithm_type); + const LMOTS_Params params = LMOTS_Params::create_or_throw(algorithm_type); if(total_remaining_bytes < size(params)) { throw Decoding_Error("Too few signature bytes while parsing LMOTS signature."); @@ -307,7 +313,7 @@ gen.gen(out, hash, m_seed); } -LMOTS_Public_Key::LMOTS_Public_Key(const LMOTS_Private_Key& lmots_sk) : OTS_Instance(lmots_sk) { +LMOTS_Public_Key::LMOTS_Public_Key(const LMOTS_Private_Key& lmots_sk) : /* NOLINT(*-slicing) */ OTS_Instance(lmots_sk) { const auto pk_hash = lmots_sk.params().hash(); pk_hash->update(lmots_sk.identifier()); pk_hash->update(store_be(lmots_sk.q())); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/hss_lms/lm_ots.h botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/lm_ots.h --- botan3-3.7.1+dfsg/src/lib/pubkey/hss_lms/lm_ots.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/lm_ots.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,9 +9,9 @@ #ifndef BOTAN_LM_OTS_H_ #define BOTAN_LM_OTS_H_ -#include -#include - +#include +#include +#include #include #include #include @@ -19,6 +19,9 @@ namespace Botan { +class BufferSlicer; +class HashFunction; + /** * @brief Seed of the LMS tree, used to generate the LM-OTS private keys. */ @@ -62,7 +65,7 @@ * introduced in RFC 8554 Section 3.2. and their format specified in * Section 3.3. */ -enum class LMOTS_Algorithm_Type : uint32_t { +enum class LMOTS_Algorithm_Type : uint32_t /* NOLINT(*-enum-size) */ { // --- RFC 8554 --- RESERVED = 0x00, @@ -108,7 +111,7 @@ /** * @brief Create the LM-OTS parameters from a hash function and width. * - * @param hash_name tha name of the hash function to use. + * @param hash_name the name of the hash function to use. * @param w the width (in bits) of the Winternitz coefficients. * @throws Decoding_Error If the algorithm type is unknown */ @@ -152,7 +155,7 @@ /** * @brief Construct a new hash instance for the OTS instance. */ - std::unique_ptr hash() const { return HashFunction::create_or_throw(hash_name()); } + std::unique_ptr hash() const; private: /** @@ -309,7 +312,7 @@ * @brief Derivivation of an LMOTS public key using an LMOTS_Private_Key as defined * in RFC 8554 4.3 */ - LMOTS_Public_Key(const LMOTS_Private_Key& lmots_sk); + explicit LMOTS_Public_Key(const LMOTS_Private_Key& lmots_sk); /** * @brief Construct a new LMOTS public key object using the bytes. diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/hss_lms/lms.cpp botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/lms.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/hss_lms/lms.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/lms.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,7 +8,11 @@ #include -#include +#include +#include +#include +#include +#include #include #include @@ -109,6 +113,10 @@ } // namespace +std::unique_ptr LMS_Params::hash() const { + return HashFunction::create_or_throw(hash_name()); +} + LMS_Params LMS_Params::create_or_throw(LMS_Algorithm_Type type) { auto [hash_name, height] = [](const LMS_Algorithm_Type& lms_type) -> std::pair { switch(lms_type) { @@ -161,7 +169,7 @@ } LMS_Params LMS_Params::create_or_throw(std::string_view hash_name, uint8_t height) { - LMS_Algorithm_Type type = [](std::string_view hash, uint8_t h) -> LMS_Algorithm_Type { + const LMS_Algorithm_Type type = [](std::string_view hash, uint8_t h) -> LMS_Algorithm_Type { if(hash == "SHA-256") { switch(h) { case 5: @@ -253,7 +261,7 @@ BOTAN_ASSERT_NOMSG(sig_stuffer.full()); - TreeAddress lms_tree_address(lms_params().h()); + const TreeAddress lms_tree_address(lms_params().h()); LMS_Tree_Node pk_buffer(lms_params().m()); lms_treehash(StrongSpan(pk_buffer.get()), auth_path_buffer, q, *this); @@ -262,7 +270,7 @@ } LMS_PublicKey LMS_PublicKey::from_bytes_or_throw(BufferSlicer& slicer) { - size_t total_remaining_bytes = slicer.remaining(); + const size_t total_remaining_bytes = slicer.remaining(); // Alg. 6. 1. (4 bytes are sufficient until the next check) if(total_remaining_bytes < sizeof(LMS_Algorithm_Type)) { throw Decoding_Error("Too few bytes while parsing LMS public key."); @@ -315,7 +323,7 @@ } LMS_Signature LMS_Signature::from_bytes_or_throw(BufferSlicer& slicer) { - size_t total_remaining_bytes = slicer.remaining(); + const size_t total_remaining_bytes = slicer.remaining(); // Alg. 6a 1. (next 4 bytes are checked in LMOTS_Signature::from_bytes_or_throw) if(total_remaining_bytes < sizeof(LMS_Tree_Node_Idx)) { throw Decoding_Error("Too few signature bytes while parsing LMS signature."); @@ -325,7 +333,7 @@ // Alg. 6a 2.b.-e. auto lmots_sig = LMOTS_Signature::from_bytes_or_throw(slicer); - LMOTS_Params lmots_params = LMOTS_Params::create_or_throw(lmots_sig.algorithm_type()); + const LMOTS_Params lmots_params = LMOTS_Params::create_or_throw(lmots_sig.algorithm_type()); if(slicer.remaining() < sizeof(LMS_Algorithm_Type)) { throw Decoding_Error("Too few signature bytes while parsing LMS signature."); @@ -333,7 +341,7 @@ // Alg. 6a 2.f. auto lms_type = load_be(slicer.take()); // Alg. 6a 2.h. - LMS_Params lms_params = LMS_Params::create_or_throw(lms_type); + const LMS_Params lms_params = LMS_Params::create_or_throw(lms_type); // Alg. 6a 2.i. (signature is not exactly [...] bytes long) if(total_remaining_bytes < size(lms_params, lmots_params)) { throw Decoding_Error("Too few signature bytes while parsing LMS signature."); @@ -345,7 +353,8 @@ return LMS_Signature(q, std::move(lmots_sig), lms_type, std::move(auth_path)); } -LMS_PublicKey::LMS_PublicKey(const LMS_PrivateKey& sk) : LMS_Instance(sk), m_lms_root(sk.lms_params().m()) { +LMS_PublicKey::LMS_PublicKey(const LMS_PrivateKey& sk) : + /* NOLINT(*-slicing) */ LMS_Instance(sk), m_lms_root(sk.lms_params().m()) { lms_treehash(StrongSpan(m_lms_root), std::nullopt, std::nullopt, sk); } @@ -395,7 +404,7 @@ auto lms_address = TreeAddress(lms_params.h()); lms_address.set_address(LMS_TreeLayerIndex(0), LMS_Tree_Node_Idx(sig.q().get())); - LMOTS_Public_Key pk_candidate(lmots_params, identifier(), sig.q(), Kc); + const LMOTS_Public_Key pk_candidate(lmots_params, identifier(), sig.q(), Kc); LMS_Tree_Node tmp(lms_params.m()); lms_gen_leaf(tmp, pk_candidate, lms_address, *hash); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/hss_lms/lms.h botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/lms.h --- botan3-3.7.1+dfsg/src/lib/pubkey/hss_lms/lms.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/hss_lms/lms.h 2026-05-07 01:38:28.000000000 +0000 @@ -19,6 +19,8 @@ namespace Botan { +class BufferSlicer; + /** * @brief Enum of available LMS algorithm types. * @@ -27,7 +29,7 @@ * introduced in RFC 8554 Section 3.2. and their format specified in * Section 3.3. */ -enum class LMS_Algorithm_Type : uint32_t { +enum class LMS_Algorithm_Type : uint32_t /* NOLINT(*-enum-size) */ { // --- RFC 8554 --- RESERVED = 0x00, @@ -104,7 +106,7 @@ static LMS_Params create_or_throw(std::string_view hash_name, uint8_t h); /** - * @brief Retuns the LMS algorithm type. + * @brief Returns the LMS algorithm type. */ LMS_Algorithm_Type algorithm_type() const { return m_algorithm_type; } @@ -126,7 +128,7 @@ /** * @brief Construct a new hash instance for the LMS instance. */ - std::unique_ptr hash() const { return HashFunction::create_or_throw(hash_name()); } + std::unique_ptr hash() const; private: /** @@ -241,7 +243,7 @@ /** * @brief Construct a new public key from a given LMS private key (RFC 8554 5.3). */ - LMS_PublicKey(const LMS_PrivateKey& sk); + explicit LMS_PublicKey(const LMS_PrivateKey& sk); /** * @brief Bytes of the full lms public key according to 8554 5.3 diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/hybrid_kem/hybrid_kem.cpp botan3-3.12.0+dfsg/src/lib/pubkey/hybrid_kem/hybrid_kem.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/hybrid_kem/hybrid_kem.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/hybrid_kem/hybrid_kem.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,82 @@ +/** +* Abstraction for a combined KEM public and private key. +* +* (C) 2024 Jack Lloyd +* 2024 Fabian Albert, René Meusel - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include + +#include +#include +#include + +namespace Botan { + +Hybrid_PublicKey::Hybrid_PublicKey(std::vector> pks) : + m_pks(std::move(pks)), m_key_length(0), m_estimated_strength(0) { + BOTAN_ARG_CHECK(m_pks.size() >= 2, "List of public keys must include at least two keys"); + for(const auto& pk : m_pks) { + BOTAN_ARG_CHECK(pk != nullptr, "List of public keys contains a nullptr"); + BOTAN_ARG_CHECK(pk->supports_operation(PublicKeyOperation::KeyEncapsulation), + fmt("Public key type '{}' does not support key encapsulation", pk->algo_name()).c_str()); + m_key_length = std::max(m_key_length, pk->key_length()); + m_estimated_strength = std::max(m_estimated_strength, pk->estimated_strength()); + } +} + +bool Hybrid_PublicKey::check_key(RandomNumberGenerator& rng, bool strong) const { + return reduce(public_keys(), true, [&](bool ckr, const auto& key) { return ckr && key->check_key(rng, strong); }); +} + +std::vector Hybrid_PublicKey::raw_public_key_bits() const { + return reduce(public_keys(), std::vector(), [](auto pkb, const auto& key) { + return concat(pkb, key->raw_public_key_bits()); + }); +} + +bool Hybrid_PublicKey::supports_operation(PublicKeyOperation op) const { + return PublicKeyOperation::KeyEncapsulation == op; +} + +std::vector> Hybrid_PublicKey::generate_other_sks_from_pks( + RandomNumberGenerator& rng) const { + std::vector> new_private_keys; + new_private_keys.reserve(public_keys().size()); + for(const auto& pk : public_keys()) { + new_private_keys.push_back(pk->generate_another(rng)); + } + return new_private_keys; +} + +Hybrid_PrivateKey::Hybrid_PrivateKey(std::vector> private_keys) : + m_sks(std::move(private_keys)) { + BOTAN_ARG_CHECK(m_sks.size() >= 2, "List of secret keys must include at least two keys"); + for(const auto& sk : m_sks) { + BOTAN_ARG_CHECK(sk != nullptr, "List of secret keys contains a nullptr"); + BOTAN_ARG_CHECK(sk->supports_operation(PublicKeyOperation::KeyEncapsulation), + "Some provided secret key is not compatible with this hybrid wrapper"); + } +} + +secure_vector Hybrid_PrivateKey::private_key_bits() const { + throw Not_Implemented("Hybrid private keys cannot be serialized"); +} + +bool Hybrid_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const { + return reduce(private_keys(), true, [&](bool ckr, const auto& key) { return ckr && key->check_key(rng, strong); }); +} + +std::vector> Hybrid_PrivateKey::extract_public_keys( + const std::vector>& private_keys) { + std::vector> public_keys; + public_keys.reserve(private_keys.size()); + for(const auto& sk : private_keys) { + BOTAN_ARG_CHECK(sk != nullptr, "List of private keys contains a nullptr"); + public_keys.push_back(sk->public_key()); + } + return public_keys; +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/hybrid_kem/hybrid_kem.h botan3-3.12.0+dfsg/src/lib/pubkey/hybrid_kem/hybrid_kem.h --- botan3-3.7.1+dfsg/src/lib/pubkey/hybrid_kem/hybrid_kem.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/hybrid_kem/hybrid_kem.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,135 @@ +/** +* Abstraction for a combined KEM public and private key. +* +* (C) 2024 Jack Lloyd +* 2024 Fabian Albert, René Meusel - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_HYBRID_KEM_H_ +#define BOTAN_HYBRID_KEM_H_ + +#include +#include +#include + +#include +#include + +namespace Botan { + +/** + * @brief Abstraction for a combined KEM public key. + * + * Two or more KEM public keys are combined into a single KEM public key. Derived classes + * must implement the abstract methods to provide the encryption operation, e.g. by + * specifying how encryption results are combined to the ciphertext and how a KEM combiner + * is applied to derive the shared secret using the individual shared secrets, ciphertexts, + * and other context information. + */ +class BOTAN_TEST_API Hybrid_PublicKey : public virtual Public_Key { + public: + /** + * @brief Constructor for a list of multiple KEM public keys. + * + * To use KEX algorithms use the KEX_to_KEM_Adapter_PublicKey. + * @param public_keys List of public keys to combine + */ + explicit Hybrid_PublicKey(std::vector> public_keys); + + Hybrid_PublicKey(Hybrid_PublicKey&&) = default; + Hybrid_PublicKey(const Hybrid_PublicKey&) = delete; + Hybrid_PublicKey& operator=(Hybrid_PublicKey&&) = default; + Hybrid_PublicKey& operator=(const Hybrid_PublicKey&) = delete; + ~Hybrid_PublicKey() override = default; + + size_t estimated_strength() const override { return m_estimated_strength; } + + size_t key_length() const override { return m_key_length; } + + bool check_key(RandomNumberGenerator& rng, bool strong) const override; + + std::vector raw_public_key_bits() const override; + + /** + * @brief Return the public key bits of this hybrid key as the concatenated + * bytes of the individual public keys (without encoding). + * + * @return the public key bytes + */ + std::vector public_key_bits() const override { return raw_public_key_bits(); } + + bool supports_operation(PublicKeyOperation op) const override; + + /// @returns the public keys combined in this hybrid key + const std::vector>& public_keys() const { return m_pks; } + + protected: + // Default constructor used for virtual inheritance to prevent, that the derived class + // calls the constructor twice. + Hybrid_PublicKey() = default; + + /** + * @brief Helper function for generate_another. Generate a new private key for each + * public key in this hybrid key. + */ + std::vector> generate_other_sks_from_pks(RandomNumberGenerator& rng) const; + + private: + std::vector> m_pks; + + size_t m_key_length = 0; + size_t m_estimated_strength = 0; +}; + +BOTAN_DIAGNOSTIC_PUSH +BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE + +/** + * @brief Abstraction for a combined KEM private key. + * + * Two or more KEM private keys are combined into a single KEM private key. Derived classes + * must implement the abstract methods to provide the decryption operation, e.g. by + * specifying how a KEM combiner is applied to derive the shared secret using the + * individual shared secrets, ciphertexts, and other context information. + */ +class BOTAN_TEST_API Hybrid_PrivateKey : virtual public Private_Key { + public: + Hybrid_PrivateKey(const Hybrid_PrivateKey&) = delete; + Hybrid_PrivateKey& operator=(const Hybrid_PrivateKey&) = delete; + + Hybrid_PrivateKey(Hybrid_PrivateKey&&) = default; + Hybrid_PrivateKey& operator=(Hybrid_PrivateKey&&) = default; + + ~Hybrid_PrivateKey() override = default; + + /** + * @brief Constructor for a list of multiple KEM private keys. + * + * To use KEX algorithms use the KEX_to_KEM_Adapter_PrivateKey. + * @param private_keys List of private keys to combine + */ + explicit Hybrid_PrivateKey(std::vector> private_keys); + + /// Disabled by default + secure_vector private_key_bits() const override; + + /// @returns the private keys combined in this hybrid key + const std::vector>& private_keys() const { return m_sks; } + + bool check_key(RandomNumberGenerator& rng, bool strong) const override; + + protected: + static std::vector> extract_public_keys( + const std::vector>& private_keys); + + private: + std::vector> m_sks; +}; + +BOTAN_DIAGNOSTIC_POP + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/hybrid_kem/hybrid_kem_ops.cpp botan3-3.12.0+dfsg/src/lib/pubkey/hybrid_kem/hybrid_kem_ops.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/hybrid_kem/hybrid_kem_ops.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/hybrid_kem/hybrid_kem_ops.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,110 @@ +/** +* Abstraction for a combined KEM encryptors and decryptors. +* +* (C) 2024 Jack Lloyd +* 2024 Fabian Albert, René Meusel - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include + +#include +#include + +namespace Botan { + +KEM_Encryption_with_Combiner::KEM_Encryption_with_Combiner(const std::vector>& public_keys, + std::string_view provider) : + m_encapsulated_key_length(0) { + m_encryptors.reserve(public_keys.size()); + for(const auto& pk : public_keys) { + const auto& newenc = m_encryptors.emplace_back(*pk, "Raw", provider); + m_encapsulated_key_length += newenc.encapsulated_key_length(); + } +} + +void KEM_Encryption_with_Combiner::kem_encrypt(std::span out_encapsulated_key, + std::span out_shared_key, + RandomNumberGenerator& rng, + size_t desired_shared_key_len, + std::span salt) { + BOTAN_ARG_CHECK(out_encapsulated_key.size() == encapsulated_key_length(), + "Encapsulated key output buffer has wrong size"); + BOTAN_ARG_CHECK(out_shared_key.size() == shared_key_length(desired_shared_key_len), + "Shared key output buffer has wrong size"); + + std::vector> shared_secrets; + shared_secrets.reserve(m_encryptors.size()); + + std::vector> ciphertexts; + ciphertexts.reserve(m_encryptors.size()); + + for(auto& encryptor : m_encryptors) { + auto [ct, ss] = KEM_Encapsulation::destructure(encryptor.encrypt(rng, 0 /* no KDF */)); + shared_secrets.push_back(std::move(ss)); + ciphertexts.push_back(std::move(ct)); + } + combine_ciphertexts(out_encapsulated_key, ciphertexts, salt); + combine_shared_secrets(out_shared_key, shared_secrets, ciphertexts, desired_shared_key_len, salt); +} + +void KEM_Encryption_with_Combiner::combine_ciphertexts(std::span out_ciphertext, + const std::vector>& ciphertexts, + std::span salt) { + BOTAN_ARG_CHECK(salt.empty(), "Salt not supported by this KEM"); + BOTAN_ARG_CHECK(ciphertexts.size() == m_encryptors.size(), "Invalid number of ciphertexts"); + BOTAN_ARG_CHECK(out_ciphertext.size() == encapsulated_key_length(), "Invalid output buffer size"); + BufferStuffer ct_stuffer(out_ciphertext); + for(size_t idx = 0; idx < ciphertexts.size(); idx++) { + BOTAN_ARG_CHECK(ciphertexts.at(idx).size() == m_encryptors.at(idx).encapsulated_key_length(), + "Invalid ciphertext length"); + ct_stuffer.append(ciphertexts.at(idx)); + } + BOTAN_ASSERT_NOMSG(ct_stuffer.full()); +} + +KEM_Decryption_with_Combiner::KEM_Decryption_with_Combiner( + const std::vector>& private_keys, + RandomNumberGenerator& rng, + std::string_view provider) : + m_encapsulated_key_length(0) { + m_decryptors.reserve(private_keys.size()); + for(const auto& sk : private_keys) { + const auto& newenc = m_decryptors.emplace_back(*sk, rng, "Raw", provider); + m_encapsulated_key_length += newenc.encapsulated_key_length(); + } +} + +void KEM_Decryption_with_Combiner::kem_decrypt(std::span out_shared_key, + std::span encapsulated_key, + size_t desired_shared_key_len, + std::span salt) { + BOTAN_ARG_CHECK(encapsulated_key.size() == encapsulated_key_length(), "Invalid encapsulated key length"); + BOTAN_ARG_CHECK(out_shared_key.size() == shared_key_length(desired_shared_key_len), "Invalid output buffer size"); + + std::vector> shared_secrets; + shared_secrets.reserve(m_decryptors.size()); + auto ciphertexts = split_ciphertexts(encapsulated_key); + BOTAN_ASSERT(ciphertexts.size() == m_decryptors.size(), "Correct number of ciphertexts"); + + for(size_t idx = 0; idx < m_decryptors.size(); idx++) { + shared_secrets.push_back(m_decryptors.at(idx).decrypt(ciphertexts.at(idx), 0 /* no KDF */)); + } + + combine_shared_secrets(out_shared_key, shared_secrets, ciphertexts, desired_shared_key_len, salt); +} + +std::vector> KEM_Decryption_with_Combiner::split_ciphertexts( + std::span concat_ciphertext) { + BOTAN_ARG_CHECK(concat_ciphertext.size() == encapsulated_key_length(), "Wrong ciphertext length"); + std::vector> ciphertexts; + ciphertexts.reserve(m_decryptors.size()); + BufferSlicer ct_slicer(concat_ciphertext); + for(const auto& decryptor : m_decryptors) { + ciphertexts.push_back(ct_slicer.copy_as_vector(decryptor.encapsulated_key_length())); + } + BOTAN_ASSERT_NOMSG(ct_slicer.empty()); + return ciphertexts; +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/hybrid_kem/hybrid_kem_ops.h botan3-3.12.0+dfsg/src/lib/pubkey/hybrid_kem/hybrid_kem_ops.h --- botan3-3.7.1+dfsg/src/lib/pubkey/hybrid_kem/hybrid_kem_ops.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/hybrid_kem/hybrid_kem_ops.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,140 @@ +/** +* Abstraction for a combined KEM encryptors and decryptors. +* +* (C) 2024 Jack Lloyd +* 2024 Fabian Albert, René Meusel - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_HYBRID_KEM_OPS_H_ +#define BOTAN_HYBRID_KEM_OPS_H_ + +#include +#include +#include + +#include +#include + +namespace Botan { + +/** + * @brief Abstract interface for a KEM encryption operation for KEM combiners. + * + * Multiple public keys are used to encapsulate shared secrets. These shared + * secrets (and maybe the ciphertexts and public keys) are combined using the + * KEM combiner to derive the final shared secret. + * + */ +class KEM_Encryption_with_Combiner : public PK_Ops::KEM_Encryption { + public: + KEM_Encryption_with_Combiner(const std::vector>& public_keys, + std::string_view provider); + + void kem_encrypt(std::span out_encapsulated_key, + std::span out_shared_key, + RandomNumberGenerator& rng, + size_t desired_shared_key_len, + std::span salt) final; + + /// The default implementation returns the sum of the encapsulated key lengths of the underlying KEMs. + size_t encapsulated_key_length() const override { return m_encapsulated_key_length; } + + protected: + /** + * @brief Defines how multiple ciphertexts are combined into a single ciphertext. + * + * The default implementation concatenates the ciphertexts. + * + * @param out_ciphertext The output buffer for the combined ciphertext + * @param ciphertexts The ciphertexts to combine + * @param salt The salt. In this default implementation the salt must be empty. + */ + virtual void combine_ciphertexts(std::span out_ciphertext, + const std::vector>& ciphertexts, + std::span salt); + + /** + * @brief Describes how the shared secrets are combined to derive the final shared secret. + * + * @param out_shared_secret the output buffer for the shared secret + * @param shared_secrets a list of shared secrets corresponding to the public keys + * @param ciphertexts a list of encapsulated shared secrets + * @param desired_shared_key_len the desired shared key length + * @param salt the salt (input of kem_encrypt) + */ + virtual void combine_shared_secrets(std::span out_shared_secret, + const std::vector>& shared_secrets, + const std::vector>& ciphertexts, + size_t desired_shared_key_len, + std::span salt) = 0; + + std::vector& encryptors() { return m_encryptors; } + + const std::vector& encryptors() const { return m_encryptors; } + + private: + std::vector m_encryptors; + size_t m_encapsulated_key_length; +}; + +/** + * @brief Abstract interface for a KEM decryption operation for KEM combiners. + * + * Multiple private keys are used to decapsulate shared secrets from a combined + * ciphertext (concatenated in most cases). These shared + * secrets (and maybe the ciphertexts and public keys) are combined using the + * KEM combiner to derive the final shared secret. + */ +class KEM_Decryption_with_Combiner : public PK_Ops::KEM_Decryption { + public: + KEM_Decryption_with_Combiner(const std::vector>& private_keys, + RandomNumberGenerator& rng, + std::string_view provider); + + void kem_decrypt(std::span out_shared_key, + std::span encapsulated_key, + size_t desired_shared_key_len, + std::span salt) final; + + /// The default implementation returns the sum of the encapsulated key lengths of the underlying KEMs. + size_t encapsulated_key_length() const override { return m_encapsulated_key_length; } + + protected: + /** + * @brief Defines how the individual ciphertexts are extracted from the combined ciphertext. + * + * The default implementation splits concatenated ciphertexts. + * @param concat_ciphertext The combined ciphertext + * @returns The individual ciphertexts + */ + virtual std::vector> split_ciphertexts(std::span concat_ciphertext); + + /** + * @brief Describes how the shared secrets are combined to derive the final shared secret. + * + * @param out_shared_secret the output buffer for the shared secret + * @param shared_secrets a list of shared secrets corresponding to the public keys + * @param ciphertexts the list of encapsulated shared secrets + * @param desired_shared_key_len the desired shared key length + * @param salt the salt (input of kem_decrypt) + */ + virtual void combine_shared_secrets(std::span out_shared_secret, + const std::vector>& shared_secrets, + const std::vector>& ciphertexts, + size_t desired_shared_key_len, + std::span salt) = 0; + + std::vector& decryptors() { return m_decryptors; } + + const std::vector& decryptors() const { return m_decryptors; } + + private: + std::vector m_decryptors; + size_t m_encapsulated_key_length; +}; + +} // namespace Botan + +#endif // BOTAN_HYBRID_KEM_OPS_H_ diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/hybrid_kem/info.txt botan3-3.12.0+dfsg/src/lib/pubkey/hybrid_kem/info.txt --- botan3-3.7.1+dfsg/src/lib/pubkey/hybrid_kem/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/hybrid_kem/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,17 @@ + +HYBRID_KEM -> 20240425 + + + +name -> "Hybrid KEM" +type -> "Internal" + + + +hybrid_kem.h +hybrid_kem_ops.h + + + + + diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/info.txt botan3-3.12.0+dfsg/src/lib/pubkey/info.txt --- botan3-3.7.1+dfsg/src/lib/pubkey/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -25,9 +25,9 @@ asn1 bigint +enc_padding kdf pem -pk_pad numbertheory rng hash diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/kex_to_kem_adapter/info.txt botan3-3.12.0+dfsg/src/lib/pubkey/kex_to_kem_adapter/info.txt --- botan3-3.7.1+dfsg/src/lib/pubkey/kex_to_kem_adapter/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/kex_to_kem_adapter/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,18 @@ + +KEX_TO_KEM_ADAPTER -> 20240504 + + + +name -> "KEX to KEM adapter" +brief -> "Basic KEX to KEM key transformation" +type -> "Internal" + + + + +kex_to_kem_adapter.h + + + + + diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/kex_to_kem_adapter/kex_to_kem_adapter.cpp botan3-3.12.0+dfsg/src/lib/pubkey/kex_to_kem_adapter/kex_to_kem_adapter.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/kex_to_kem_adapter/kex_to_kem_adapter.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/kex_to_kem_adapter/kex_to_kem_adapter.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,270 @@ +/** + * Adapter that allows using a KEX key as a KEM, using an ephemeral + * key in the KEM encapsulation. + * + * (C) 2023 Jack Lloyd + * 2023,2024 Fabian Albert, René Meusel - Rohde & Schwarz Cybersecurity + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#include + +#include +#include +#include + +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) + #include + #include +#endif + +#if defined(BOTAN_HAS_ECDH) + #include + #include +#endif + +#if defined(BOTAN_HAS_X25519) + #include +#endif + +#if defined(BOTAN_HAS_X448) + #include +#endif + +namespace Botan { + +namespace { + +/** + * This helper determines the length of the agreed-upon value depending + * on the key agreement public key's algorithm type. It would be better + * to get this value via PK_Key_Agreement::agreed_value_size(), but + * instantiating a PK_Key_Agreement object requires a PrivateKey object + * which we don't have (yet) in the context this is used. + * + * TODO: Find a way to get this information without duplicating those + * implementation details of the key agreement algorithms. + */ +size_t kex_shared_key_length(const Public_Key& kex_public_key) { + BOTAN_ASSERT_NOMSG(kex_public_key.supports_operation(PublicKeyOperation::KeyAgreement)); + +#if defined(BOTAN_HAS_ECDH) + if(const auto* ecdh = dynamic_cast(&kex_public_key)) { + return ecdh->domain().get_p_bytes(); + } +#endif + +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) + if(const auto* dh = dynamic_cast(&kex_public_key)) { + return dh->group().p_bytes(); + } +#endif + +#if defined(BOTAN_HAS_X25519) + if(const auto* curve = dynamic_cast(&kex_public_key)) { + BOTAN_UNUSED(curve); + return 32; /* TODO: magic number */ + } +#endif + +#if defined(BOTAN_HAS_X448) + if(const auto* curve = dynamic_cast(&kex_public_key)) { + BOTAN_UNUSED(curve); + return 56; /* TODO: magic number */ + } +#endif + + throw Not_Implemented( + fmt("Cannot get shared kex key length from unknown key agreement public key of type '{}' in the hybrid KEM key", + kex_public_key.algo_name())); +} + +/** + * This helper generates an ephemeral key agreement private key given a + * public key instance of a certain key agreement algorithm. + */ +std::unique_ptr generate_key_agreement_private_key(const Public_Key& kex_public_key, + RandomNumberGenerator& rng) { + BOTAN_ASSERT_NOMSG(kex_public_key.supports_operation(PublicKeyOperation::KeyAgreement)); + + auto new_kex_key = [&] { + auto new_private_key = kex_public_key.generate_another(rng); + auto* const kex_key = dynamic_cast(new_private_key.get()); + if(kex_key != nullptr) [[likely]] { + // Intentionally leak new_private_key since we hold an alias of it in kex_key, + // which is captured in a unique_ptr below + // NOLINTNEXTLINE(*-unused-return-value) + (void)new_private_key.release(); + } + return std::unique_ptr(kex_key); + }(); + + BOTAN_ASSERT(new_kex_key, "Keys wrapped in this adapter are always key-agreement keys"); + return new_kex_key; +} + +std::unique_ptr maybe_get_public_key(const std::unique_ptr& private_key) { + BOTAN_ARG_CHECK(private_key != nullptr, "Private key is a nullptr"); + return private_key->public_key(); +} + +class KEX_to_KEM_Adapter_Encryption_Operation final : public PK_Ops::KEM_Encryption_with_KDF { + public: + KEX_to_KEM_Adapter_Encryption_Operation(const Public_Key& key, std::string_view kdf, std::string_view provider) : + PK_Ops::KEM_Encryption_with_KDF(kdf), m_provider(provider), m_public_key(key) {} + + size_t raw_kem_shared_key_length() const override { return kex_shared_key_length(m_public_key); } + + size_t encapsulated_key_length() const override { + // Serializing the public value into a short-lived heap-allocated + // vector is not ideal. + // + // TODO: Find a way to get the public value length without copying + // the public value into a vector. See GH #3706 (point 5). + return m_public_key.raw_public_key_bits().size(); + } + + void raw_kem_encrypt(std::span out_encapsulated_key, + std::span raw_shared_key, + Botan::RandomNumberGenerator& rng) override { + const auto sk = generate_key_agreement_private_key(m_public_key, rng); + const auto shared_key = PK_Key_Agreement(*sk, rng, "Raw", m_provider) + .derive_key(0 /* no KDF */, m_public_key.raw_public_key_bits()) + .bits_of(); + + const auto public_value = sk->public_value(); + + // TODO: perhaps avoid these copies by providing std::span out-params + // for `PK_Key_Agreement::derive_key()` and + // `PK_Key_Agreement_Key::public_value()` + BOTAN_ASSERT_EQUAL(public_value.size(), + out_encapsulated_key.size(), + "KEX-to-KEM Adapter: encapsulated key out-param has correct length"); + BOTAN_ASSERT_EQUAL( + shared_key.size(), raw_shared_key.size(), "KEX-to-KEM Adapter: shared key out-param has correct length"); + std::copy(public_value.begin(), public_value.end(), out_encapsulated_key.begin()); + std::copy(shared_key.begin(), shared_key.end(), raw_shared_key.begin()); + } + + private: + std::string m_provider; + const Public_Key& m_public_key; +}; + +class KEX_to_KEM_Decryption_Operation final : public PK_Ops::KEM_Decryption_with_KDF { + public: + KEX_to_KEM_Decryption_Operation(const PK_Key_Agreement_Key& key, + RandomNumberGenerator& rng, + const std::string_view kdf, + const std::string_view provider) : + PK_Ops::KEM_Decryption_with_KDF(kdf), + m_operation(key, rng, "Raw", provider), + m_encapsulated_key_length(key.public_value().size()) {} + + void raw_kem_decrypt(std::span out_shared_key, std::span encap_key) override { + secure_vector shared_secret = m_operation.derive_key(0 /* no KDF */, encap_key).bits_of(); + BOTAN_ASSERT_EQUAL( + shared_secret.size(), out_shared_key.size(), "KEX-to-KEM Adapter: shared key out-param has correct length"); + std::copy(shared_secret.begin(), shared_secret.end(), out_shared_key.begin()); + } + + size_t encapsulated_key_length() const override { return m_encapsulated_key_length; } + + size_t raw_kem_shared_key_length() const override { return m_operation.agreed_value_size(); } + + private: + PK_Key_Agreement m_operation; + size_t m_encapsulated_key_length; +}; + +} // namespace + +KEX_to_KEM_Adapter_PublicKey::KEX_to_KEM_Adapter_PublicKey(std::unique_ptr public_key) : + m_public_key(std::move(public_key)) { + BOTAN_ARG_CHECK(m_public_key != nullptr, "Public key is a nullptr"); + BOTAN_ARG_CHECK(m_public_key->supports_operation(PublicKeyOperation::KeyAgreement), "Public key is no KEX key"); +} + +std::string KEX_to_KEM_Adapter_PublicKey::algo_name() const { + return fmt("KEX-to-KEM({})", m_public_key->algo_name()); +} + +size_t KEX_to_KEM_Adapter_PublicKey::estimated_strength() const { + return m_public_key->estimated_strength(); +} + +size_t KEX_to_KEM_Adapter_PublicKey::key_length() const { + return m_public_key->key_length(); +} + +bool KEX_to_KEM_Adapter_PublicKey::check_key(RandomNumberGenerator& rng, bool strong) const { + return m_public_key->check_key(rng, strong); +} + +AlgorithmIdentifier KEX_to_KEM_Adapter_PublicKey::algorithm_identifier() const { + return m_public_key->algorithm_identifier(); +} + +std::vector KEX_to_KEM_Adapter_PublicKey::raw_public_key_bits() const { + return m_public_key->raw_public_key_bits(); +} + +std::vector KEX_to_KEM_Adapter_PublicKey::public_key_bits() const { + return m_public_key->public_key_bits(); +} + +std::unique_ptr KEX_to_KEM_Adapter_PublicKey::generate_another(RandomNumberGenerator& rng) const { + return std::make_unique(generate_key_agreement_private_key(*m_public_key, rng)); +} + +bool KEX_to_KEM_Adapter_PublicKey::supports_operation(PublicKeyOperation op) const { + return op == PublicKeyOperation::KeyEncapsulation; +} + +namespace { + +std::unique_ptr capture_as_ka_key(std::unique_ptr private_key) { + auto* raw_ptr = private_key.release(); + if(auto* sk = dynamic_cast(raw_ptr)) { + return std::unique_ptr(sk); + } else { + delete raw_ptr; // NOLINT(*-owning-memory) + throw_invalid_argument( + "Private key must implement PK_Key_Agreement_Key", "KEX_to_KEM_Adapter_PrivateKey", __FILE__); + } +} + +} // namespace + +KEX_to_KEM_Adapter_PrivateKey::KEX_to_KEM_Adapter_PrivateKey(std::unique_ptr private_key) : + KEX_to_KEM_Adapter_PublicKey(maybe_get_public_key(private_key)), + m_private_key(capture_as_ka_key(std::move(private_key))) {} + +secure_vector KEX_to_KEM_Adapter_PrivateKey::private_key_bits() const { + return m_private_key->private_key_bits(); +} + +secure_vector KEX_to_KEM_Adapter_PrivateKey::raw_private_key_bits() const { + return m_private_key->raw_private_key_bits(); +} + +std::unique_ptr KEX_to_KEM_Adapter_PrivateKey::public_key() const { + return std::make_unique(m_private_key->public_key()); +} + +bool KEX_to_KEM_Adapter_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const { + return m_private_key->check_key(rng, strong); +} + +std::unique_ptr KEX_to_KEM_Adapter_PublicKey::create_kem_encryption_op( + std::string_view kdf, std::string_view provider) const { + return std::make_unique(*m_public_key, kdf, provider); +} + +std::unique_ptr KEX_to_KEM_Adapter_PrivateKey::create_kem_decryption_op( + RandomNumberGenerator& rng, std::string_view kdf, std::string_view provider) const { + return std::make_unique(*m_private_key, rng, kdf, provider); +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/kex_to_kem_adapter/kex_to_kem_adapter.h botan3-3.12.0+dfsg/src/lib/pubkey/kex_to_kem_adapter/kex_to_kem_adapter.h --- botan3-3.7.1+dfsg/src/lib/pubkey/kex_to_kem_adapter/kex_to_kem_adapter.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/kex_to_kem_adapter/kex_to_kem_adapter.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,92 @@ +/** + * Adapter that allows using a KEX key as a KEM, using an ephemeral + * key in the KEM encapsulation. + * + * (C) 2023 Jack Lloyd + * 2023,2024 Fabian Albert, René Meusel - Rohde & Schwarz Cybersecurity + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#ifndef BOTAN_TLS_13_KEX_TO_KEM_ADAPTER_H_ +#define BOTAN_TLS_13_KEX_TO_KEM_ADAPTER_H_ + +#include + +#include + +namespace Botan { + +/** + * Adapter to use a key agreement key pair (e.g. ECDH) as a key encapsulation + * mechanism. + */ +class BOTAN_TEST_API KEX_to_KEM_Adapter_PublicKey : public virtual Public_Key { + public: + explicit KEX_to_KEM_Adapter_PublicKey(std::unique_ptr public_key); + + std::string algo_name() const override; + size_t estimated_strength() const override; + size_t key_length() const override; + bool check_key(RandomNumberGenerator& rng, bool strong) const override; + AlgorithmIdentifier algorithm_identifier() const override; + std::vector raw_public_key_bits() const override; + std::vector public_key_bits() const override; + std::unique_ptr generate_another(RandomNumberGenerator& rng) const final; + + bool supports_operation(PublicKeyOperation op) const override; + + std::unique_ptr create_kem_encryption_op( + std::string_view kdf, std::string_view provider = "base") const override; + + private: + std::unique_ptr m_public_key; +}; + +BOTAN_DIAGNOSTIC_PUSH +BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE + +/** + * Adapter to use a key agreement key pair (e.g. ECDH) as a key encapsulation + * mechanism. This works by generating an ephemeral key pair during the + * encapsulation. The following Botan key types are supported: + * ECDH, DH, X25519 and X448. + * + * The abstract interface of a key exchange mechanism (KEX) is mapped like so: + * + * * KEM-generate(rng) -> tuple[PublicKey, PrivateKey] + * => KEX-generate(rng) -> tuple[PublicKey, PrivateKey] + * + * * KEM-encapsulate(PublicKey, rng) -> tuple[SharedSecret, EncapsulatedSharedSecret] + * => eph_pk, eph_sk = KEX-generate(rng) + * secret = KEX-agree(eph_sk, PublicKey) + * [secret, eph_pk] + * + * * KEM-decapsulate(PrivateKey, EncapsulatedSharedSecret) -> SharedSecret + * => KEX-agree(PrivateKey, EncapsulatedSharedSecret) + */ +class BOTAN_TEST_API KEX_to_KEM_Adapter_PrivateKey final : public KEX_to_KEM_Adapter_PublicKey, + public virtual Private_Key { + public: + explicit KEX_to_KEM_Adapter_PrivateKey(std::unique_ptr private_key); + + secure_vector private_key_bits() const override; + + secure_vector raw_private_key_bits() const override; + + std::unique_ptr public_key() const override; + + bool check_key(RandomNumberGenerator& rng, bool strong) const override; + + std::unique_ptr create_kem_decryption_op( + RandomNumberGenerator& rng, std::string_view kdf, std::string_view provider = "base") const override; + + private: + std::unique_ptr m_private_key; +}; + +BOTAN_DIAGNOSTIC_POP + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/keypair/keypair.cpp botan3-3.12.0+dfsg/src/lib/pubkey/keypair/keypair.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/keypair/keypair.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/keypair/keypair.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -19,8 +19,8 @@ const Private_Key& private_key, const Public_Key& public_key, std::string_view padding) { - PK_Encryptor_EME encryptor(public_key, rng, padding); - PK_Decryptor_EME decryptor(private_key, rng, padding); + const PK_Encryptor_EME encryptor(public_key, rng, padding); + const PK_Decryptor_EME decryptor(private_key, rng, padding); /* Weird corner case, if the key is too small to encrypt anything at @@ -38,7 +38,7 @@ return false; } - std::vector decrypted = unlock(decryptor.decrypt(ciphertext)); + const std::vector decrypted = unlock(decryptor.decrypt(ciphertext)); return (plaintext == decrypted); } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber.cpp botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -15,27 +15,14 @@ #include #include -#include #include #include #include - -#include #include #include #include #include -#include #include -#include - -#if defined(BOTAN_HAS_KYBER) - #include -#endif - -#if defined(BOTAN_HAS_KYBER_90S) - #include -#endif #if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S) #include @@ -209,7 +196,7 @@ return m_public->mode().canonical_parameter_set_identifier(); } -bool Kyber_PublicKey::check_key(RandomNumberGenerator&, bool) const { +bool Kyber_PublicKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const { // The length checks described in FIPS 203, Section 7.2 are already performed // while decoding the public key. See constructor of Kyber_PublicKeyInternal. // The decoding function KyberAlgos::byte_decode() also checks the range of @@ -242,12 +229,15 @@ Kyber_PrivateKey::Kyber_PrivateKey(std::span sk, KyberMode m) { KyberConstants mode(m); - if(mode.private_key_bytes() != sk.size()) { + if(mode.mode().is_ml_kem() && sk.size() == mode.seed_private_key_bytes()) { + std::tie(m_public, m_private) = Seed_Expanding_Keypair_Codec().decode_keypair(sk, std::move(mode)); + } else if(sk.size() == mode.expanded_private_key_bytes()) { + std::tie(m_public, m_private) = Expanded_Keypair_Codec().decode_keypair(sk, std::move(mode)); + } else if(!mode.mode().is_ml_kem() && sk.size() == mode.seed_private_key_bytes()) { + throw Invalid_Argument("Kyber round 3 private keys do not support the seed format"); + } else { throw Invalid_Argument("Private key does not have the correct byte count"); } - - const auto& codec = mode.keypair_codec(); - std::tie(m_public, m_private) = codec.decode_keypair(sk, std::move(mode)); } std::unique_ptr Kyber_PrivateKey::public_key() const { @@ -259,7 +249,7 @@ } secure_vector Kyber_PrivateKey::private_key_bits() const { - return m_private->mode().keypair_codec().encode_keypair({m_public, m_private}); + return private_key_bits_with_format(private_key_format()); } bool Kyber_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const { @@ -325,4 +315,26 @@ throw Provider_Not_Found(algo_name(), provider); } +MlPrivateKeyFormat Kyber_PrivateKey::private_key_format() const { + if(mode().is_ml_kem() && m_private->seed().d.has_value()) { + return MlPrivateKeyFormat::Seed; + } + return MlPrivateKeyFormat::Expanded; +} + +secure_vector Kyber_PrivateKey::private_key_bits_with_format(MlPrivateKeyFormat format) const { + if(format == MlPrivateKeyFormat::Seed && private_key_format() != MlPrivateKeyFormat::Seed) { + throw Encoding_Error("Expanded private keys do not support the seed format"); + } + const auto codec = [&]() -> std::unique_ptr { + switch(format) { + case MlPrivateKeyFormat::Seed: + return std::make_unique(); + case MlPrivateKeyFormat::Expanded: + return std::make_unique(); + } + BOTAN_ASSERT_UNREACHABLE(); + }(); + return codec->encode_keypair({m_public, m_private}); +} } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber.h botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber.h --- botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber.h 2026-05-07 01:38:28.000000000 +0000 @@ -14,11 +14,7 @@ #ifndef BOTAN_KYBER_COMMON_H_ #define BOTAN_KYBER_COMMON_H_ -#include -#include -#include #include - #include #if !defined(BOTAN_HAS_KYBER_90S) && !defined(BOTAN_HAS_KYBER) && !defined(BOTAN_HAS_ML_KEM) @@ -29,30 +25,32 @@ namespace Botan { -class BOTAN_PUBLIC_API(3, 0) KyberMode { +class BOTAN_PUBLIC_API(3, 0) KyberMode final { public: - enum Mode { + enum Mode : uint8_t /* NOLINT(*-use-enum-class) */ { // Kyber512 as proposed in round 3 of the NIST competition - Kyber512_R3, + Kyber512_R3 BOTAN_DEPRECATED("Kyber R3 is deprecated - use ML-KEM") = 0, // Kyber768 as proposed in round 3 of the NIST competition - Kyber768_R3, + Kyber768_R3 BOTAN_DEPRECATED("Kyber R3 is deprecated - use ML-KEM") = 1, // Kyber1024 as proposed in round 3 of the NIST competition - Kyber1024_R3, + Kyber1024_R3 BOTAN_DEPRECATED("Kyber R3 is deprecated - use ML-KEM") = 2, - Kyber512 BOTAN_DEPRECATED("Use Kyber512_R3") = Kyber512_R3, - Kyber768 BOTAN_DEPRECATED("Use Kyber768_R3") = Kyber768_R3, - Kyber1024 BOTAN_DEPRECATED("Use Kyber1024_R3") = Kyber1024_R3, - - ML_KEM_512, - ML_KEM_768, - ML_KEM_1024, - - Kyber512_90s BOTAN_DEPRECATED("Kyber 90s mode is deprecated"), - Kyber768_90s BOTAN_DEPRECATED("Kyber 90s mode is deprecated"), - Kyber1024_90s BOTAN_DEPRECATED("Kyber 90s mode is deprecated"), + Kyber512 BOTAN_DEPRECATED("Kyber R3 is deprecated - use ML-KEM") = 0, + Kyber768 BOTAN_DEPRECATED("Kyber R3 is deprecated - use ML-KEM") = 1, + Kyber1024 BOTAN_DEPRECATED("Kyber R3 is deprecated - use ML-KEM") = 2, + + ML_KEM_512 = 3, + ML_KEM_768 = 4, + ML_KEM_1024 = 5, + + Kyber512_90s BOTAN_DEPRECATED("Kyber 90s mode is deprecated") = 6, + Kyber768_90s BOTAN_DEPRECATED("Kyber 90s mode is deprecated") = 7, + Kyber1024_90s BOTAN_DEPRECATED("Kyber 90s mode is deprecated") = 8, }; + // NOLINTNEXTLINE(*-explicit-conversions) KyberMode(Mode mode); + explicit KyberMode(const OID& oid); explicit KyberMode(std::string_view str); @@ -79,6 +77,17 @@ Mode m_mode; }; +/// Byte encoding format of ML-KEM and ML-DSA the private key +enum class MlPrivateKeyFormat : uint8_t { + /// Only supported for ML-KEM/ML-DSA keys: + /// - ML-KEM: 64-byte seed: d || z + /// - ML-DSA: 32-byte seed: xi (private_key_bits_with_format not yet + /// yet supported for ML-DSA) + Seed, + /// The expanded format, i.e., the format specified in FIPS-203/204. + Expanded, +}; + class Kyber_PublicKeyInternal; class Kyber_PrivateKeyInternal; @@ -89,8 +98,9 @@ Kyber_PublicKey(const AlgorithmIdentifier& alg_id, std::span key_bits); Kyber_PublicKey(const Kyber_PublicKey& other); - Kyber_PublicKey& operator=(const Kyber_PublicKey& other) = default; + Kyber_PublicKey(Kyber_PublicKey&& other) = default; + Kyber_PublicKey& operator=(Kyber_PublicKey&& other) = default; ~Kyber_PublicKey() override = default; @@ -131,7 +141,7 @@ friend class Kyber_KEM_Encryptor; friend class Kyber_KEM_Decryptor; - std::shared_ptr m_public; + std::shared_ptr m_public; // NOLINT(*non-private-member-variable*) }; BOTAN_DIAGNOSTIC_PUSH @@ -140,10 +150,28 @@ class BOTAN_PUBLIC_API(3, 0) Kyber_PrivateKey final : public virtual Kyber_PublicKey, public virtual Private_Key { public: + /** + * Create a new private key. The private key will be encoded as the 64 byte + * seed. + */ Kyber_PrivateKey(RandomNumberGenerator& rng, KyberMode mode); + /** + * Import a private key using its key bytes. Supported are key bytes as + * 64-byte seeds (not supported for Kyber Round 3 instances), + * as well as the expanded encoding specified by FIPS 203. Note that the + * encoding used in this constructor is reflected by the calls for + * private_key_bits, private_key_info, etc. + */ Kyber_PrivateKey(std::span sk, KyberMode mode); + /** + * Import a private key using its key bytes. Supported are key bytes as + * 64-byte seeds (not supported for Kyber Round 3 instances), + * as well as the expanded encoding specified by FIPS 203. Note that the + * encoding used in this constructor is reflected by the calls for + * private_key_bits, private_key_info, etc. + */ Kyber_PrivateKey(const AlgorithmIdentifier& alg_id, std::span key_bits); std::unique_ptr public_key() const override; @@ -158,6 +186,26 @@ std::string_view params, std::string_view provider) const override; + /** + * The private key format from which the key was loaded. It is the format + * used for the private_key_bits(), raw_private_key_bits() andFIPS + * private_key_info() methods. + * + * Note that keys in Seed format can be serialized to Expanded format + * using the method private_key_bits_with_format but NOT the other way + * around. + */ + MlPrivateKeyFormat private_key_format() const; + + /** + * Encode the private key in the specified format. Note that the seed + * format is only available for new ML-KEM keys and those loaded from + * seeds. + * @throws Encoding_Error if the private key cannot be encoded in the + * requested format. + */ + secure_vector private_key_bits_with_format(MlPrivateKeyFormat format) const; + private: friend class Kyber_KEM_Decryptor; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_algos.cpp botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_algos.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_algos.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_algos.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -14,6 +14,8 @@ #include +#include +#include #include #include #include @@ -183,7 +185,7 @@ void encode_polynomial_vector(std::span out, const KyberPolyVecNTT& vec) { BufferStuffer bs(out); - for(auto& v : vec) { + for(const auto& v : vec) { byte_encode(bs, v); } BOTAN_ASSERT_NOMSG(bs.full()); @@ -382,10 +384,14 @@ KyberPolyMat mat(mode.k(), mode.k()); + const auto& sym = mode.symmetric_primitives(); + std::unique_ptr xof; + for(uint8_t i = 0; i < mode.k(); ++i) { for(uint8_t j = 0; j < mode.k(); ++j) { const auto pos = (transposed) ? std::tuple(i, j) : std::tuple(j, i); - sample_ntt_uniform(mat[i][j], mode.symmetric_primitives().XOF(seed, pos)); + sym.setup_XOF(xof, seed, pos); + sample_ntt_uniform(mat[i][j], *xof); } } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_algos.h botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_algos.h --- botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_algos.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_algos.h 2026-05-07 01:38:28.000000000 +0000 @@ -15,11 +15,8 @@ #ifndef BOTAN_KYBER_ALGOS_H_ #define BOTAN_KYBER_ALGOS_H_ -#include -#include #include #include -#include namespace Botan::Kyber_Algos { @@ -94,7 +91,12 @@ } private: - KyberSamplingRandomness prf(size_t bytes) { return m_mode.symmetric_primitives().PRF(m_seed, m_nonce++, bytes); } + KyberSamplingRandomness prf(size_t bytes) { + const auto& sym = m_mode.symmetric_primitives(); + auto seed_span = m_seed.get(); + sym.setup_PRF(m_prf_xof, seed_span, m_nonce++); + return m_prf_xof->output(bytes); + } void sample_poly_cbd(KyberPoly& poly, KyberConstants::KyberEta eta) { const auto randomness = [&] { @@ -115,6 +117,7 @@ StrongSpan m_seed; const KyberConstants& m_mode; uint8_t m_nonce; + std::unique_ptr m_prf_xof; }; template diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_constants.cpp botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_constants.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_constants.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_constants.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -21,10 +21,6 @@ #include #endif -#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S) - #include -#endif - #if defined(BOTAN_HAS_ML_KEM) #include #endif @@ -70,21 +66,18 @@ #ifdef BOTAN_HAS_KYBER_90S if(mode.is_kyber_round3() && mode.is_90s()) { m_symmetric_primitives = std::make_unique(); - m_keypair_codec = std::make_unique(); } #endif #ifdef BOTAN_HAS_KYBER if(mode.is_kyber_round3() && mode.is_modern()) { m_symmetric_primitives = std::make_unique(); - m_keypair_codec = std::make_unique(); } #endif #ifdef BOTAN_HAS_ML_KEM if(mode.is_ml_kem()) { m_symmetric_primitives = std::make_unique(); - m_keypair_codec = std::make_unique(); } #endif @@ -92,14 +85,9 @@ m_polynomial_vector_bytes = (bitlen(Q) * (N / 8)) * k(); m_polynomial_vector_compressed_bytes = d_u() * k() * (N / 8); m_polynomial_compressed_bytes = d_v() * (N / 8); - m_private_key_bytes = static_cast([this]() -> size_t { - if(m_mode.is_ml_kem()) { - // ML-KEM's private keys are simply expanded from their seeds. - return 2 * SEED_BYTES; - } else { - return m_polynomial_vector_bytes + public_key_bytes() + PUBLIC_KEY_HASH_BYTES + SEED_BYTES; - } - }()); + m_expanded_private_key_bytes = + static_cast(m_polynomial_vector_bytes + public_key_bytes() + PUBLIC_KEY_HASH_BYTES + SEED_BYTES); + m_seed_private_key_bytes = 2 * SEED_BYTES; if(!m_symmetric_primitives) { throw Not_Implemented("requested Kyber mode is not enabled in this build"); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_constants.h botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_constants.h --- botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_constants.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_constants.h 2026-05-07 01:38:28.000000000 +0000 @@ -17,7 +17,6 @@ namespace Botan { class Kyber_Symmetric_Primitives; -class Kyber_Keypair_Codec; class KyberConstants final { public: @@ -48,16 +47,20 @@ static constexpr uint16_t SAMPLE_NTT_POLY_FROM_XOF_BOUND = 280 * 3 /* XOF bytes per while iteration */; public: + // NOLINTBEGIN(*-use-enum-class) + enum KyberEta : uint8_t { _2 = 2, _3 = 3 }; enum KyberDu : uint8_t { _10 = 10, _11 = 11 }; enum KyberDv : uint8_t { _4 = 4, _5 = 5 }; - enum KyberStrength : uint32_t { _128 = 128, _192 = 192, _256 = 256 }; + enum KyberStrength : uint16_t { _128 = 128, _192 = 192, _256 = 256 }; + + // NOLINTEND(*-use-enum-class) public: - KyberConstants(KyberMode mode); + /* NOLINT(*-explicit-conversions) */ KyberConstants(KyberMode mode); ~KyberConstants(); @@ -110,15 +113,17 @@ /// byte length of an encoded public key size_t public_key_bytes() const { return polynomial_vector_bytes() + SEED_BYTES; } - /// byte length of an encoded private key - size_t private_key_bytes() const { return m_private_key_bytes; } + /// byte length of a private key with expanded encoding as defined + // in FIPS 203 + size_t expanded_private_key_bytes() const { return m_expanded_private_key_bytes; } + + /// byte length of an private key encoded as the seed: d || z + size_t seed_private_key_bytes() const { return m_seed_private_key_bytes; } /// @} Kyber_Symmetric_Primitives& symmetric_primitives() const { return *m_symmetric_primitives; } - Kyber_Keypair_Codec& keypair_codec() const { return *m_keypair_codec; } - private: KyberMode m_mode; @@ -131,9 +136,10 @@ uint32_t m_polynomial_vector_bytes; uint32_t m_polynomial_vector_compressed_bytes; uint32_t m_polynomial_compressed_bytes; - uint32_t m_private_key_bytes; - std::unique_ptr m_keypair_codec; + uint32_t m_expanded_private_key_bytes; + uint32_t m_seed_private_key_bytes; + std::unique_ptr m_symmetric_primitives; }; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_encaps_base.h botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_encaps_base.h --- botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_encaps_base.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_encaps_base.h 2026-05-07 01:38:28.000000000 +0000 @@ -17,15 +17,19 @@ class Kyber_KEM_Operation_Base { protected: - Kyber_KEM_Operation_Base(const Kyber_PublicKeyInternal& pk) : - m_At(Kyber_Algos::sample_matrix(pk.rho(), true /* transposed */, pk.mode())) {} + explicit Kyber_KEM_Operation_Base(const Kyber_PublicKeyInternal& pk) : + m_mode(pk.mode()), m_At(Kyber_Algos::sample_matrix(pk.rho(), true /* transposed */, m_mode)) {} + + const KyberConstants& mode() const { return m_mode; } const KyberPolyMat& precomputed_matrix_At() const { return m_At; } private: + const KyberConstants& m_mode; + // The public key's matrix is pre-computed to avoid redundant work when // encapsulating multiple keys. This matrix is needed for encapsulation as - // well as for the FO transform in the decapsulation. + // well as for the Fujisaki-Okamoto transform in the decapsulation. KyberPolyMat m_At; }; @@ -51,8 +55,6 @@ virtual void encapsulate(StrongSpan out_encapsulated_key, StrongSpan out_shared_key, RandomNumberGenerator& rng) = 0; - - virtual const KyberConstants& mode() const = 0; }; class Kyber_KEM_Decryptor_Base : public PK_Ops::KEM_Decryption_with_KDF, @@ -73,8 +75,6 @@ virtual void decapsulate(StrongSpan out_shared_key, StrongSpan encapsulated_key) = 0; - - virtual const KyberConstants& mode() const = 0; }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_helpers.h botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_helpers.h --- botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_helpers.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_helpers.h 2026-05-07 01:38:28.000000000 +0000 @@ -54,7 +54,7 @@ constexpr size_t p = 33; constexpr unsigned_T mask = (1 << d) - 1; return static_cast((n * m) >> p) & mask; -}; +} /** * NIST FIPS 203, Formula 4.8 (Decompress) diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_keys.cpp botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_keys.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_keys.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_keys.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,8 +11,9 @@ #include +#include +#include #include -#include namespace Botan { @@ -27,6 +28,80 @@ } // namespace +/** + * Key decoding as specified in Crystals Kyber (Version 3.01), + * Algorithms 4 (CPAPKE.KeyGen()), and 7 (CCAKEM.KeyGen()) + * + * Public Key: pk := (encode(t) || rho) + * Secret Key: sk' := encode(s) + * + * Expanded Secret Key: sk := (sk' || pk || H(pk) || z) + */ +KyberInternalKeypair Expanded_Keypair_Codec::decode_keypair(std::span sk, KyberConstants mode) const { + auto scope = CT::scoped_poison(sk); + BufferSlicer s(sk); + + auto skpv = Kyber_Algos::decode_polynomial_vector(s.take(mode.polynomial_vector_bytes()), mode); + auto pub_key = s.copy(mode.public_key_bytes()); + auto puk_key_hash = s.take(KyberConstants::PUBLIC_KEY_HASH_BYTES); + auto z = s.copy(KyberConstants::SEED_BYTES); + + BOTAN_ASSERT_NOMSG(s.empty()); + + CT::unpoison_all(pub_key, puk_key_hash, skpv, z); + + KyberInternalKeypair keypair{ + std::make_shared(mode, std::move(pub_key)), + std::make_shared( + std::move(mode), + std::move(skpv), + KyberPrivateKeySeed{std::nullopt, // Reading from an expanded and encoded + // private key cannot reconstruct the + // original seed from key generation. + std::move(z)}), + }; + + BOTAN_ASSERT(keypair.first && keypair.second, "reading private key encoding"); + BOTAN_ARG_CHECK(keypair.first->H_public_key_bits_raw().size() == puk_key_hash.size() && + std::equal(keypair.first->H_public_key_bits_raw().begin(), + keypair.first->H_public_key_bits_raw().end(), + puk_key_hash.begin()), + "public key's hash does not match the stored hash"); + + return keypair; +} + +secure_vector Expanded_Keypair_Codec::encode_keypair(KyberInternalKeypair keypair) const { + BOTAN_ASSERT_NONNULL(keypair.first); + BOTAN_ASSERT_NONNULL(keypair.second); + const auto& mode = keypair.first->mode(); + auto scope = CT::scoped_poison(*keypair.second); + auto result = concat(Kyber_Algos::encode_polynomial_vector(keypair.second->s().reduce(), mode), + keypair.first->public_key_bits_raw(), + keypair.first->H_public_key_bits_raw(), + keypair.second->z()); + CT::unpoison(result); + return result; +} + +KyberInternalKeypair Seed_Expanding_Keypair_Codec::decode_keypair(std::span private_key, + KyberConstants mode) const { + BufferSlicer s(private_key); + auto seed = KyberPrivateKeySeed{ + s.copy(KyberConstants::SEED_BYTES), + s.copy(KyberConstants::SEED_BYTES), + }; + BOTAN_ASSERT_NOMSG(s.empty()); + return Kyber_Algos::expand_keypair(std::move(seed), std::move(mode)); +} + +secure_vector Seed_Expanding_Keypair_Codec::encode_keypair(KyberInternalKeypair keypair) const { + BOTAN_ASSERT_NONNULL(keypair.second); + const auto& seed = keypair.second->seed(); + BOTAN_ARG_CHECK(seed.d.has_value(), "Cannot encode keypair without the full private seed"); + return concat>(seed.d.value(), seed.z); +} + Kyber_PublicKeyInternal::Kyber_PublicKeyInternal(KyberConstants mode, KyberSerializedPublicKey public_key) : m_mode(std::move(mode)), m_public_key_bits_raw(validate_public_key_length(std::move(public_key), m_mode.public_key_bytes())), @@ -55,9 +130,10 @@ void Kyber_PublicKeyInternal::indcpa_encrypt(StrongSpan out_ct, StrongSpan m, StrongSpan r, - const KyberPolyMat& At) const { + const KyberPolyMat& At, + const KyberConstants& mode) const { // The nonce N is handled internally by the PolynomialSampler - Kyber_Algos::PolynomialSampler ps(r, m_mode); + Kyber_Algos::PolynomialSampler ps(r, mode); const auto y = ntt(ps.sample_polynomial_vector_cbd_eta1()); const auto e1 = ps.sample_polynomial_vector_cbd_eta2(); const auto e2 = ps.sample_polynomial_cbd_eta2(); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_keys.h botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_keys.h --- botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_keys.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_keys.h 2026-05-07 01:38:28.000000000 +0000 @@ -19,14 +19,28 @@ namespace Botan { -class Kyber_Keypair_Codec { +class Kyber_Keypair_Codec /* NOLINT(*-special-member-functions) */ { public: virtual ~Kyber_Keypair_Codec() = default; virtual secure_vector encode_keypair(KyberInternalKeypair keypair) const = 0; virtual KyberInternalKeypair decode_keypair(std::span private_key, KyberConstants mode) const = 0; }; -class Kyber_PublicKeyInternal { +/// Codec for expanded private keys (as specified in FIPS 203) +class Expanded_Keypair_Codec final : public Kyber_Keypair_Codec { + public: + KyberInternalKeypair decode_keypair(std::span buffer, KyberConstants mode) const override; + secure_vector encode_keypair(KyberInternalKeypair private_key) const override; +}; + +/// Codec for private keys as 64-byte seeds: d || z +class Seed_Expanding_Keypair_Codec final : public Kyber_Keypair_Codec { + public: + KyberInternalKeypair decode_keypair(std::span buffer, KyberConstants mode) const override; + secure_vector encode_keypair(KyberInternalKeypair keypair) const override; +}; + +class Kyber_PublicKeyInternal final { public: Kyber_PublicKeyInternal(KyberConstants mode, KyberSerializedPublicKey public_key); Kyber_PublicKeyInternal(KyberConstants mode, KyberPolyVecNTT polynomials, KyberSeedRho seed); @@ -34,13 +48,15 @@ void indcpa_encrypt(StrongSpan out_ct, StrongSpan m, StrongSpan r, - const KyberPolyMat& At) const; + const KyberPolyMat& At, + const KyberConstants& mode) const; KyberCompressedCiphertext indcpa_encrypt(const KyberMessage& m, const KyberEncryptionRandomness& r, - const KyberPolyMat& At) const { + const KyberPolyMat& At, + const KyberConstants& mode) const { KyberCompressedCiphertext ct(m_mode.ciphertext_bytes()); - indcpa_encrypt(ct, m, r, At); + indcpa_encrypt(ct, m, r, At, mode); return ct; } @@ -64,7 +80,7 @@ const KyberSeedRho m_rho; }; -class Kyber_PrivateKeyInternal { +class Kyber_PrivateKeyInternal final { public: Kyber_PrivateKeyInternal(KyberConstants mode, KyberPolyVecNTT s, KyberPrivateKeySeed seed) : m_mode(std::move(mode)), m_s(std::move(s)), m_seed(std::move(seed)) {} diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_polynomial.h botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_polynomial.h --- botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_polynomial.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_polynomial.h 2026-05-07 01:38:28.000000000 +0000 @@ -27,7 +27,7 @@ friend class CRYSTALS::Trait_Base; constexpr static T montgomery_reduce_coefficient(T2 a) { - const T u = static_cast(a) * Q_inverse; + const T u = static_cast(static_cast(a) * Q_inverse); auto t = static_cast(u) * Q; t = a - t; t >>= sizeof(T) * 8; @@ -36,8 +36,8 @@ constexpr static T barrett_reduce_coefficient(T a) { constexpr T2 v = ((1U << 26) + Q / 2) / Q; - const T t = (v * a >> 26) * Q; - return a - t; + const T t = static_cast(((v * a) >> 26) * Q); + return static_cast(a - t); } public: @@ -54,8 +54,8 @@ const auto zeta = zetas[++i]; for(j = start; j < start + len; ++j) { const auto t = fqmul(zeta, p[j + len]); - p[j + len] = p[j] - t; - p[j] = p[j] + t; + p[j + len] = static_cast(p[j] - t); + p[j] = static_cast(p[j] + t); } } } @@ -80,8 +80,8 @@ const auto zeta = zetas[i--]; for(j = start; j < start + len; ++j) { const auto t = p[j]; - p[j] = barrett_reduce_coefficient(t + p[j + len]); - p[j + len] = fqmul(zeta, p[j + len] - t); + p[j] = barrett_reduce_coefficient(static_cast(t + p[j + len])); + p[j + len] = fqmul(zeta, static_cast(p[j + len] - t)); } } } @@ -119,9 +119,10 @@ }; for(size_t i = 0; i < Tq_elem_count(result) / 2; ++i) { - const auto zeta = zetas[64 + i]; + const T zeta = zetas[64 + i]; + const T nzeta = static_cast(-zeta); Tq_elem(result, 2 * i) = basemul(Tq_elem(lhs, 2 * i), Tq_elem(rhs, 2 * i), zeta); - Tq_elem(result, 2 * i + 1) = basemul(Tq_elem(lhs, 2 * i + 1), Tq_elem(rhs, 2 * i + 1), -zeta); + Tq_elem(result, 2 * i + 1) = basemul(Tq_elem(lhs, 2 * i + 1), Tq_elem(rhs, 2 * i + 1), nzeta); } } }; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_symmetric_primitives.h botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_symmetric_primitives.h --- botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_symmetric_primitives.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_common/kyber_symmetric_primitives.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,12 +11,10 @@ #define BOTAN_KYBER_SYMMETRIC_PRIMITIVES_H_ #include -#include #include - +#include #include #include -#include #include #include @@ -27,20 +25,20 @@ * Adapter class that uses polymorphy to distinguish * Kyber "modern" from Kyber "90s" modes. */ -class Kyber_Symmetric_Primitives { +class Kyber_Symmetric_Primitives /* NOLINT(*-special-member-functions) */ { public: virtual ~Kyber_Symmetric_Primitives() = default; // TODO: remove this once Kyber-R3 is removed - KyberMessage H(StrongSpan m) const { return get_H().process(m); } + KyberMessage H(StrongSpan m) const { return create_H()->process(m); } // TODO: remove this once Kyber-R3 is removed KyberHashedCiphertext H(StrongSpan r) const { - return get_H().process(r); + return create_H()->process(r); } KyberHashedPublicKey H(StrongSpan pk) const { - return get_H().process(pk); + return create_H()->process(pk); } std::pair G(StrongSpan seed, @@ -59,41 +57,57 @@ KyberSharedSecret J(StrongSpan rejection_value, StrongSpan ciphertext) const { - auto& j = get_J(); - j.update(rejection_value); - j.update(ciphertext); - return j.final(); + auto j = create_J(); + j->update(rejection_value); + j->update(ciphertext); + return j->final(); } // TODO: remove this once Kyber-R3 is removed void KDF(StrongSpan out, StrongSpan shared_secret, StrongSpan hashed_ciphertext) const { - auto& kdf = get_KDF(); - kdf.update(shared_secret); - kdf.update(hashed_ciphertext); - kdf.final(out); + auto kdf = create_KDF(); + kdf->update(shared_secret); + kdf->update(hashed_ciphertext); + kdf->final(out); } KyberSamplingRandomness PRF(KyberSigmaOrEncryptionRandomness seed, const uint8_t nonce, const size_t outlen) const { auto bare_seed_span = std::visit([&](const auto s) { return s.get(); }, seed); - return get_PRF(bare_seed_span, nonce).output(outlen); + return create_PRF(bare_seed_span, nonce)->output(outlen); } - Botan::XOF& XOF(StrongSpan seed, std::tuple matrix_position) const { - return get_XOF(seed, matrix_position); + /// Setup an XOF object for matrix sampling + void setup_XOF(std::unique_ptr& xof, + StrongSpan seed, + std::tuple matrix_position) const { + if(!xof) { + xof = create_XOF(seed, matrix_position); + } else { + init_XOF(*xof, seed, matrix_position); + } + } + + /// Setup a seeded PRF XOF for polynomial sampling + void setup_PRF(std::unique_ptr& xof, std::span seed, uint8_t nonce) const { + if(!xof) { + xof = create_PRF(seed, nonce); + } else { + init_PRF(*xof, seed, nonce); + } } private: template - std::pair G_split(InputTs&&... inputs) const { - auto& g = get_G(); - (g.update(inputs), ...); - auto s = g.final(); + std::pair G_split(const InputTs&... inputs) const { + auto g = create_G(); + (g->update(inputs), ...); + const auto s = g->final(); BufferSlicer bs(s); std::pair result; @@ -107,13 +121,19 @@ virtual std::optional> seed_expansion_domain_separator( const KyberConstants& mode) const = 0; - virtual HashFunction& get_G() const = 0; - virtual HashFunction& get_H() const = 0; - virtual HashFunction& get_J() const = 0; - virtual HashFunction& get_KDF() const = 0; - virtual Botan::XOF& get_PRF(std::span seed, uint8_t nonce) const = 0; - virtual Botan::XOF& get_XOF(std::span seed, - std::tuple matrix_position) const = 0; + virtual std::unique_ptr create_G() const = 0; + virtual std::unique_ptr create_H() const = 0; + virtual std::unique_ptr create_J() const = 0; + virtual std::unique_ptr create_KDF() const = 0; + + virtual std::unique_ptr create_PRF(std::span seed, uint8_t nonce) const = 0; + virtual void init_PRF(Botan::XOF& xof, std::span seed, uint8_t nonce) const = 0; + + virtual std::unique_ptr create_XOF(std::span seed, + std::tuple matrix_position) const = 0; + virtual void init_XOF(Botan::XOF& xof, + std::span seed, + std::tuple matrix_position) const = 0; }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_round3/kyber/kyber_modern.h botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_round3/kyber/kyber_modern.h --- botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_round3/kyber/kyber_modern.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_round3/kyber/kyber_modern.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,58 +10,60 @@ #ifndef BOTAN_KYBER_MODERN_H_ #define BOTAN_KYBER_MODERN_H_ -#include -#include - #include -#include +#include +#include +#include #include namespace Botan { class Kyber_Modern_Symmetric_Primitives final : public Kyber_Symmetric_Primitives { - public: - Kyber_Modern_Symmetric_Primitives() : - m_sha3_512(HashFunction::create_or_throw("SHA-3(512)")), - m_sha3_256(HashFunction::create_or_throw("SHA-3(256)")), - m_shake256_256(HashFunction::create_or_throw("SHAKE-256(256)")), - m_shake128(Botan::XOF::create_or_throw("SHAKE-128")), - m_shake256(Botan::XOF::create_or_throw("SHAKE-256")) {} - protected: - std::optional> seed_expansion_domain_separator(const KyberConstants&) const override { + std::optional> seed_expansion_domain_separator( + const KyberConstants& /*constants*/) const override { return {}; } - HashFunction& get_G() const override { return *m_sha3_512; } + std::unique_ptr create_G() const override { return HashFunction::create_or_throw("SHA-3(512)"); } - HashFunction& get_H() const override { return *m_sha3_256; } + std::unique_ptr create_H() const override { return HashFunction::create_or_throw("SHA-3(256)"); } - HashFunction& get_J() const override { throw Invalid_State("Kyber-R3 does not support J()"); } + std::unique_ptr create_J() const override { throw Invalid_State("Kyber-R3 does not support J()"); } - HashFunction& get_KDF() const override { return *m_shake256_256; } + std::unique_ptr create_KDF() const override { + return HashFunction::create_or_throw("SHAKE-256(256)"); + } + + std::unique_ptr create_PRF(std::span seed, const uint8_t nonce) const override { + auto xof = Botan::XOF::create_or_throw("SHAKE-256"); + init_PRF(*xof, seed, nonce); + return xof; + } - Botan::XOF& get_PRF(std::span seed, const uint8_t nonce) const override { - m_shake256->clear(); - m_shake256->update(seed); - m_shake256->update(store_be(nonce)); - return *m_shake256; + void init_PRF(Botan::XOF& xof, std::span seed, const uint8_t nonce) const override { + xof.clear(); + xof.update(seed); + xof.update({&nonce, 1}); } - Botan::XOF& get_XOF(std::span seed, std::tuple matrix_position) const override { - m_shake128->clear(); - m_shake128->update(seed); - m_shake128->update(store_be(make_uint16(std::get<0>(matrix_position), std::get<1>(matrix_position)))); - return *m_shake128; + std::unique_ptr create_XOF(std::span seed, + std::tuple matrix_position) const override { + auto xof = Botan::XOF::create_or_throw("SHAKE-128"); + init_XOF(*xof, seed, matrix_position); + return xof; } - private: - std::unique_ptr m_sha3_512; - std::unique_ptr m_sha3_256; - std::unique_ptr m_shake256_256; - std::unique_ptr m_shake128; - std::unique_ptr m_shake256; + void init_XOF(Botan::XOF& xof, + std::span seed, + std::tuple matrix_position) const override { + xof.clear(); + xof.update(seed); + + const std::array pos = {std::get<0>(matrix_position), std::get<1>(matrix_position)}; + xof.update(pos); + } }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_round3/kyber_90s/kyber_90s.h botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_round3/kyber_90s/kyber_90s.h --- botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_round3/kyber_90s/kyber_90s.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_round3/kyber_90s/kyber_90s.h 2026-05-07 01:38:28.000000000 +0000 @@ -21,43 +21,45 @@ namespace Botan { class Kyber_90s_Symmetric_Primitives final : public Kyber_Symmetric_Primitives { - public: - Kyber_90s_Symmetric_Primitives() : - m_sha512(HashFunction::create_or_throw("SHA-512")), - m_sha256(HashFunction::create_or_throw("SHA-256")), - m_aes256_xof(std::make_unique()) {} - protected: - std::optional> seed_expansion_domain_separator(const KyberConstants&) const override { + std::optional> seed_expansion_domain_separator( + const KyberConstants& /*constants*/) const override { return {}; } - HashFunction& get_G() const override { return *m_sha512; } + std::unique_ptr create_G() const override { return HashFunction::create_or_throw("SHA-512"); } - HashFunction& get_H() const override { return *m_sha256; } + std::unique_ptr create_H() const override { return HashFunction::create_or_throw("SHA-256"); } - HashFunction& get_J() const override { throw Invalid_State("Kyber-R3 in 90s mode does not support J()"); } + std::unique_ptr create_J() const override { + throw Invalid_State("Kyber-R3 in 90s mode does not support J()"); + } - HashFunction& get_KDF() const override { return *m_sha256; } + std::unique_ptr create_KDF() const override { return HashFunction::create_or_throw("SHA-256"); } - Botan::XOF& get_PRF(std::span seed, const uint8_t nonce) const override { - m_aes256_xof->clear(); - const std::array nonce_buffer{nonce, 0}; - m_aes256_xof->start(nonce_buffer, seed); - return *m_aes256_xof; + std::unique_ptr create_PRF(std::span seed, const uint8_t nonce) const override { + auto xof = std::make_unique(); + init_PRF(*xof, seed, nonce); + return xof; } - Botan::XOF& get_XOF(std::span seed, std::tuple mpos) const override { - m_aes256_xof->clear(); - const std::array iv{std::get<0>(mpos), std::get<1>(mpos), 0}; - m_aes256_xof->start(iv, seed); - return *m_aes256_xof; + void init_PRF(Botan::XOF& xof, std::span seed, const uint8_t nonce) const override { + xof.clear(); + dynamic_cast(xof).start(std::array{nonce, 0}, seed); } - private: - std::unique_ptr m_sha512; - std::unique_ptr m_sha256; - mutable std::unique_ptr m_aes256_xof; + std::unique_ptr create_XOF(std::span seed, + std::tuple mpos) const override { + auto xof = std::make_unique(); + init_XOF(*xof, seed, mpos); + return xof; + } + + void init_XOF(Botan::XOF& xof, std::span seed, std::tuple mpos) const override { + xof.clear(); + dynamic_cast(xof).start(std::array{std::get<0>(mpos), std::get<1>(mpos), 0}, + seed); + } }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_round3/kyber_round3_impl.cpp botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_round3/kyber_round3_impl.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_round3/kyber_round3_impl.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_round3/kyber_round3_impl.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -22,14 +22,14 @@ void Kyber_KEM_Encryptor::encapsulate(StrongSpan out_encapsulated_key, StrongSpan out_shared_key, RandomNumberGenerator& rng) { - const auto& sym = m_public_key->mode().symmetric_primitives(); + const auto& sym = mode().symmetric_primitives(); const auto seed_m = rng.random_vec(KyberConstants::SEED_BYTES); CT::poison(seed_m); const auto m = sym.H(seed_m); const auto [K_bar, r] = sym.G(m, m_public_key->H_public_key_bits_raw()); - m_public_key->indcpa_encrypt(out_encapsulated_key, m, r, precomputed_matrix_At()); + m_public_key->indcpa_encrypt(out_encapsulated_key, m, r, precomputed_matrix_At(), mode()); sym.KDF(out_shared_key, K_bar, sym.H(out_encapsulated_key)); CT::unpoison_all(out_shared_key, out_encapsulated_key); @@ -42,7 +42,7 @@ StrongSpan encapsulated_key) { auto scope = CT::scoped_poison(*m_private_key); - const auto& sym = m_public_key->mode().symmetric_primitives(); + const auto& sym = mode().symmetric_primitives(); const auto& h = m_public_key->H_public_key_bits_raw(); const auto& z = m_private_key->z(); @@ -50,73 +50,16 @@ const auto m_prime = m_private_key->indcpa_decrypt(encapsulated_key); const auto [K_bar_prime, r_prime] = sym.G(m_prime, h); - const auto c_prime = m_public_key->indcpa_encrypt(m_prime, r_prime, precomputed_matrix_At()); + const auto c_prime = m_public_key->indcpa_encrypt(m_prime, r_prime, precomputed_matrix_At(), mode()); KyberSharedSecret K(KyberConstants::SEED_BYTES); BOTAN_ASSERT_NOMSG(encapsulated_key.size() == c_prime.size()); BOTAN_ASSERT_NOMSG(K_bar_prime.size() == K.size()); - const auto reencrypt_success = CT::is_equal(encapsulated_key.data(), c_prime.data(), encapsulated_key.size()); + const auto reencrypt_success = CT::is_equal(encapsulated_key, c_prime); CT::conditional_copy_mem(reencrypt_success, K.data(), K_bar_prime.data(), z.data(), K_bar_prime.size()); sym.KDF(out_shared_key, K, sym.H(encapsulated_key)); CT::unpoison(out_shared_key); } -/** - * Key decoding as specified in Crystals Kyber (Version 3.01), - * Algorithms 4 (CPAPKE.KeyGen()), and 7 (CCAKEM.KeyGen()) - * - * Public Key: pk := (encode(t) || rho) - * Secret Key: sk' := encode(s) - * - * Expanded Secret Key: sk := (sk' || pk || H(pk) || z) - */ -KyberInternalKeypair Kyber_Expanded_Keypair_Codec::decode_keypair(std::span sk, - KyberConstants mode) const { - auto scope = CT::scoped_poison(sk); - BufferSlicer s(sk); - - auto skpv = Kyber_Algos::decode_polynomial_vector(s.take(mode.polynomial_vector_bytes()), mode); - auto pub_key = s.copy(mode.public_key_bytes()); - auto puk_key_hash = s.take(KyberConstants::PUBLIC_KEY_HASH_BYTES); - auto z = s.copy(KyberConstants::SEED_BYTES); - - BOTAN_ASSERT_NOMSG(s.empty()); - - CT::unpoison_all(pub_key, puk_key_hash, skpv, z); - - KyberInternalKeypair keypair{ - std::make_shared(mode, std::move(pub_key)), - std::make_shared( - std::move(mode), - std::move(skpv), - KyberPrivateKeySeed{std::nullopt, // Reading from an expanded and encoded - // private key cannot reconstruct the - // original seed from key generation. - std::move(z)}), - }; - - BOTAN_ASSERT(keypair.first && keypair.second, "reading private key encoding"); - BOTAN_ARG_CHECK(keypair.first->H_public_key_bits_raw().size() == puk_key_hash.size() && - std::equal(keypair.first->H_public_key_bits_raw().begin(), - keypair.first->H_public_key_bits_raw().end(), - puk_key_hash.begin()), - "public key's hash does not match the stored hash"); - - return keypair; -} - -secure_vector Kyber_Expanded_Keypair_Codec::encode_keypair(KyberInternalKeypair keypair) const { - BOTAN_ASSERT_NONNULL(keypair.first); - BOTAN_ASSERT_NONNULL(keypair.second); - const auto& mode = keypair.first->mode(); - auto scope = CT::scoped_poison(*keypair.second); - auto result = concat(Kyber_Algos::encode_polynomial_vector(keypair.second->s().reduce(), mode), - keypair.first->public_key_bits_raw(), - keypair.first->H_public_key_bits_raw(), - keypair.second->z()); - CT::unpoison(result); - return result; -} - } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_round3/kyber_round3_impl.h botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_round3/kyber_round3_impl.h --- botan3-3.7.1+dfsg/src/lib/pubkey/kyber/kyber_round3/kyber_round3_impl.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/kyber/kyber_round3/kyber_round3_impl.h 2026-05-07 01:38:28.000000000 +0000 @@ -27,8 +27,6 @@ StrongSpan out_shared_key, RandomNumberGenerator& rng) override; - const KyberConstants& mode() const override { return m_public_key->mode(); } - private: std::shared_ptr m_public_key; }; @@ -46,19 +44,11 @@ void decapsulate(StrongSpan out_shared_key, StrongSpan encapsulated_key) override; - const KyberConstants& mode() const override { return m_private_key->mode(); } - private: std::shared_ptr m_public_key; std::shared_ptr m_private_key; }; -class Kyber_Expanded_Keypair_Codec final : public Kyber_Keypair_Codec { - public: - KyberInternalKeypair decode_keypair(std::span buffer, KyberConstants mode) const override; - secure_vector encode_keypair(KyberInternalKeypair private_key) const override; -}; - } // namespace Botan #endif diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/kyber/ml_kem/ml_kem_impl.cpp botan3-3.12.0+dfsg/src/lib/pubkey/kyber/ml_kem/ml_kem_impl.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/kyber/ml_kem/ml_kem_impl.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/kyber/ml_kem/ml_kem_impl.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,7 +10,6 @@ #include #include -#include #include #include @@ -25,13 +24,13 @@ void ML_KEM_Encryptor::encapsulate(StrongSpan out_encapsulated_key, StrongSpan out_shared_key, RandomNumberGenerator& rng) { - const auto& sym = m_public_key->mode().symmetric_primitives(); + const auto& sym = mode().symmetric_primitives(); const auto m = rng.random_vec(KyberConstants::SEED_BYTES); auto scope = CT::scoped_poison(m); const auto [K, r] = sym.G(m, m_public_key->H_public_key_bits_raw()); - m_public_key->indcpa_encrypt(out_encapsulated_key, m, r, precomputed_matrix_At()); + m_public_key->indcpa_encrypt(out_encapsulated_key, m, r, precomputed_matrix_At(), mode()); // TODO: avoid this copy by letting sym.G() directly write to the span. copy_mem(out_shared_key, K); @@ -49,7 +48,7 @@ StrongSpan c) { auto scope = CT::scoped_poison(*m_private_key); - const auto& sym = m_public_key->mode().symmetric_primitives(); + const auto& sym = mode().symmetric_primitives(); const auto& h = m_public_key->H_public_key_bits_raw(); const auto& z = m_private_key->z(); @@ -58,32 +57,13 @@ const auto [K_prime, r_prime] = sym.G(m_prime, h); const auto K_bar = sym.J(z, c); - const auto c_prime = m_public_key->indcpa_encrypt(m_prime, r_prime, precomputed_matrix_At()); + const auto c_prime = m_public_key->indcpa_encrypt(m_prime, r_prime, precomputed_matrix_At(), mode()); BOTAN_ASSERT_NOMSG(c.size() == c_prime.size()); BOTAN_ASSERT_NOMSG(K_prime.size() == K_bar.size() && out_shared_key.size() == K_bar.size()); - const auto reencrypt_success = CT::is_equal(c.data(), c_prime.data(), c.size()); + const auto reencrypt_success = CT::is_equal(c, c_prime); CT::conditional_copy_mem(reencrypt_success, out_shared_key.data(), K_prime.data(), K_bar.data(), K_prime.size()); CT::unpoison(out_shared_key); } - -KyberInternalKeypair ML_KEM_Expanding_Keypair_Codec::decode_keypair(std::span private_key, - KyberConstants mode) const { - BufferSlicer s(private_key); - auto seed = KyberPrivateKeySeed{ - s.copy(KyberConstants::SEED_BYTES), - s.copy(KyberConstants::SEED_BYTES), - }; - BOTAN_ASSERT_NOMSG(s.empty()); - return Kyber_Algos::expand_keypair(std::move(seed), std::move(mode)); -} - -secure_vector ML_KEM_Expanding_Keypair_Codec::encode_keypair(KyberInternalKeypair keypair) const { - BOTAN_ASSERT_NONNULL(keypair.second); - const auto& seed = keypair.second->seed(); - BOTAN_ARG_CHECK(seed.d.has_value(), "Cannot encode keypair without the full private seed"); - return concat>(seed.d.value(), seed.z); -}; - } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/kyber/ml_kem/ml_kem_impl.h botan3-3.12.0+dfsg/src/lib/pubkey/kyber/ml_kem/ml_kem_impl.h --- botan3-3.7.1+dfsg/src/lib/pubkey/kyber/ml_kem/ml_kem_impl.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/kyber/ml_kem/ml_kem_impl.h 2026-05-07 01:38:28.000000000 +0000 @@ -18,6 +18,7 @@ #include #include #include +#include namespace Botan { @@ -31,8 +32,6 @@ StrongSpan out_shared_key, RandomNumberGenerator& rng) override; - const KyberConstants& mode() const override { return m_public_key->mode(); } - private: std::shared_ptr m_public_key; }; @@ -50,22 +49,12 @@ void decapsulate(StrongSpan out_shared_key, StrongSpan encapsulated_key) override; - const KyberConstants& mode() const override { return m_private_key->mode(); } - private: std::shared_ptr m_public_key; std::shared_ptr m_private_key; }; class ML_KEM_Symmetric_Primitives final : public Kyber_Symmetric_Primitives { - public: - ML_KEM_Symmetric_Primitives() : - m_sha3_512(HashFunction::create_or_throw("SHA-3(512)")), - m_sha3_256(HashFunction::create_or_throw("SHA-3(256)")), - m_shake256_256(HashFunction::create_or_throw("SHAKE-256(256)")), - m_shake128(Botan::XOF::create_or_throw("SHAKE-128")), - m_shake256(Botan::XOF::create_or_throw("SHAKE-256")) {} - protected: std::optional> seed_expansion_domain_separator(const KyberConstants& mode) const override { // NIST FIPS 203, Algorithm 13 (K-PKE.KeyGen) @@ -75,40 +64,44 @@ return std::array{mode.k()}; } - HashFunction& get_G() const override { return *m_sha3_512; } + std::unique_ptr create_G() const override { return HashFunction::create_or_throw("SHA-3(512)"); } - HashFunction& get_H() const override { return *m_sha3_256; } + std::unique_ptr create_H() const override { return HashFunction::create_or_throw("SHA-3(256)"); } - HashFunction& get_J() const override { return *m_shake256_256; } + std::unique_ptr create_J() const override { + return HashFunction::create_or_throw("SHAKE-256(256)"); + } - HashFunction& get_KDF() const override { throw Invalid_State("ML-KEM does not support KDF()"); } + std::unique_ptr create_KDF() const override { + throw Invalid_State("ML-KEM does not support KDF()"); + } - Botan::XOF& get_PRF(std::span seed, const uint8_t nonce) const override { - m_shake256->clear(); - m_shake256->update(seed); - m_shake256->update(store_be(nonce)); - return *m_shake256; + std::unique_ptr create_PRF(std::span seed, const uint8_t nonce) const override { + auto xof = Botan::XOF::create_or_throw("SHAKE-256"); + init_PRF(*xof, seed, nonce); + return xof; } - Botan::XOF& get_XOF(std::span seed, std::tuple matrix_position) const override { - m_shake128->clear(); - m_shake128->update(seed); - m_shake128->update(store_be(make_uint16(std::get<0>(matrix_position), std::get<1>(matrix_position)))); - return *m_shake128; + void init_PRF(Botan::XOF& xof, std::span seed, const uint8_t nonce) const override { + xof.clear(); + xof.update(seed); + xof.update(store_be(nonce)); } - private: - std::unique_ptr m_sha3_512; - std::unique_ptr m_sha3_256; - std::unique_ptr m_shake256_256; - std::unique_ptr m_shake128; - std::unique_ptr m_shake256; -}; + std::unique_ptr create_XOF(std::span seed, + std::tuple matrix_position) const override { + auto xof = Botan::XOF::create_or_throw("SHAKE-128"); + init_XOF(*xof, seed, matrix_position); + return xof; + } -class ML_KEM_Expanding_Keypair_Codec final : public Kyber_Keypair_Codec { - public: - KyberInternalKeypair decode_keypair(std::span buffer, KyberConstants mode) const override; - secure_vector encode_keypair(KyberInternalKeypair keypair) const override; + void init_XOF(Botan::XOF& xof, + std::span seed, + std::tuple matrix_position) const override { + xof.clear(); + xof.update(seed); + xof.update(store_be(make_uint16(std::get<0>(matrix_position), std::get<1>(matrix_position)))); + } }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/mce/code_based_key_gen.cpp botan3-3.12.0+dfsg/src/lib/pubkey/mce/code_based_key_gen.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/mce/code_based_key_gen.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/mce/code_based_key_gen.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -54,10 +54,7 @@ std::vector m_elem; }; -binary_matrix::binary_matrix(size_t rown, size_t coln) { - m_coln = coln; - m_rown = rown; - m_rwdcnt = 1 + ((m_coln - 1) / 32); +binary_matrix::binary_matrix(size_t rown, size_t coln) : m_rown(rown), m_coln(coln), m_rwdcnt(1 + ((m_coln - 1) / 32)) { m_elem = std::vector(m_rown * m_rwdcnt); } @@ -81,7 +78,7 @@ bool found_row = false; for(size_t j = i; !found_row && j != m_rown; j++) { - if(coef(j, max)) { + if(coef(j, max) > 0) { if(i != j) //not needed as ith row is 0 and jth row is 1. { row_xor(i, j); //xor to the row.(swap)? @@ -95,7 +92,7 @@ if(!found_row) { perm[m_coln - m_rown - 1 - failcnt] = static_cast(max); failcnt++; - if(!max) { + if(max == 0) { perm.clear(); } i--; @@ -103,14 +100,14 @@ perm[i + m_coln - m_rown] = max; for(size_t j = i + 1; j < m_rown; j++) //fill the column downwards with 0's { - if(coef(j, max)) { + if(coef(j, max) > 0) { row_xor(j, i); //check the arg. order. } } //fill the column with 0's upwards too. for(size_t j = i; j != 0; --j) { - if(coef(j - 1, max)) { + if(coef(j - 1, max) > 0) { row_xor(j - 1, i); } } @@ -121,7 +118,7 @@ void randomize_support(std::vector& L, RandomNumberGenerator& rng) { for(size_t i = 0; i != L.size(); ++i) { - gf2m rnd = random_gf2m(rng); + const gf2m rnd = random_gf2m(rng); // no rejection sampling, but for useful code-based parameters with n <= 13 this seem tolerable std::swap(L[i], L[rnd % L.size()]); @@ -146,7 +143,7 @@ gf2m y = x; for(size_t j = 0; j < t; j++) { for(size_t k = 0; k < sp_field.get_extension_degree(); k++) { - if(y & (1 << k)) { + if((y & (1 << k)) != 0) { //the co-eff. are set in 2^0,...,2^11 ; 2^0,...,2^11 format along the rows/cols? H.set_coef_to_one(j * sp_field.get_extension_degree() + k, i); } @@ -163,7 +160,7 @@ auto result = std::make_unique(code_length - r, r); for(size_t i = 0; i < result->rows(); ++i) { for(size_t j = 0; j < result->columns(); ++j) { - if(H.coef(j, perm[i])) { + if(H.coef(j, perm[i]) > 0) { result->toggle_coeff(i, j); } } @@ -202,6 +199,7 @@ bool success = false; std::unique_ptr R; + // NOLINTNEXTLINE(*-avoid-do-while) do { // create a random irreducible polynomial g = polyn_gf2m(t, rng, sp_field); @@ -212,7 +210,7 @@ } catch(const Invalid_State&) {} } while(!success); - std::vector sqrtmod = polyn_gf2m::sqrt_mod_init(g); + const std::vector sqrtmod = polyn_gf2m::sqrt_mod_init(g); std::vector F = syndrome_init(g, L, static_cast(code_length)); // Each F[i] is the (precomputed) syndrome of the error vector with diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/mce/gf2m_rootfind_dcmp.cpp botan3-3.12.0+dfsg/src/lib/pubkey/mce/gf2m_rootfind_dcmp.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/mce/gf2m_rootfind_dcmp.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/mce/gf2m_rootfind_dcmp.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,7 +10,6 @@ #include #include -#include #include namespace Botan { @@ -19,7 +18,7 @@ void patch_root_array(gf2m res_root_arr[], size_t res_root_arr_len, size_t root_pos) { volatile gf2m patch_elem = 0x01; - volatile gf2m cond_mask = (root_pos == res_root_arr_len); + volatile gf2m cond_mask = static_cast(root_pos == res_root_arr_len); cond_mask = expand_mask_16bit(cond_mask); cond_mask = ~cond_mask; /* now cond = 1 if not enough roots */ patch_elem = patch_elem & cond_mask; @@ -55,11 +54,10 @@ * calculates ceil((t-4)/5) = outer_summands - 1 */ uint32_t brootf_decomp_calc_sum_limit(uint32_t t) { - uint32_t result; if(t < 4) { return 0; } - result = t - 4; + uint32_t result = t - 4; result += 4; result /= 5; return result; @@ -67,16 +65,14 @@ gf2m_decomp_rootfind_state::gf2m_decomp_rootfind_state(const polyn_gf2m& polyn, size_t code_length) : m_code_length(code_length), m_j(0), m_j_gray(0) { - gf2m coeff_3; - gf2m coeff_head; - std::shared_ptr sp_field = polyn.get_sp_field(); - int deg_sigma = polyn.get_degree(); + const std::shared_ptr sp_field = polyn.get_sp_field(); + const int deg_sigma = polyn.get_degree(); if(deg_sigma <= 3) { throw Internal_Error("Unexpected degree in gf2m_decomp_rootfind_state"); } - coeff_3 = polyn.get_coef(3); - coeff_head = polyn.get_coef(deg_sigma); /* dummy value for SCA CM */ + const gf2m coeff_3 = polyn.get_coef(3); + const gf2m coeff_head = polyn.get_coef(deg_sigma); /* dummy value for SCA CM */ if(coeff_3 != 0) { this->m_sigma_3_l = sp_field->gf_l_from_n(coeff_3); this->m_sigma_3_neq_0_mask = 0xFFFF; @@ -92,11 +88,10 @@ } void gf2m_decomp_rootfind_state::calc_Ai_zero(const polyn_gf2m& sigma) { - uint32_t i; /* * this function assumes this the first gray code element is zero */ - for(i = 0; i < this->m_outer_summands; i++) { + for(uint32_t i = 0; i < this->m_outer_summands; i++) { this->m_Aij[i] = sigma.get_coef(5 * i); } this->m_j = 0; @@ -109,32 +104,29 @@ * first thing, we declare Aij Aij_minusone and increase j. * Case j=0 upon function entry also included, then Aij contains A_{i,j=0}. */ - uint32_t i; - gf2m diff, new_j_gray; - uint32_t Lik_pos_base; + uint32_t Lik_pos_base = 0; this->m_j++; - new_j_gray = lex_to_gray(this->m_j); + const gf2m new_j_gray = lex_to_gray(this->m_j); - if(this->m_j & 1) /* half of the times */ - { + if((this->m_j & 1) != 0) { + /* half of the times */ Lik_pos_base = 0; - } else if(this->m_j & 2) /* one quarter of the times */ - { + } else if((this->m_j & 2) != 0) { + /* one quarter of the times */ Lik_pos_base = this->m_outer_summands; - } else if(this->m_j & 4) /* one eighth of the times */ - { + } else if((this->m_j & 4) != 0) { + /* one eighth of the times */ Lik_pos_base = this->m_outer_summands * 2; - } else if(this->m_j & 8) /* one sixteenth of the times */ - { + } else if((this->m_j & 8) != 0) { + /* one sixteenth of the times */ Lik_pos_base = this->m_outer_summands * 3; - } else if(this->m_j & 16) /* ... */ - { + } else if((this->m_j & 16) != 0) { Lik_pos_base = this->m_outer_summands * 4; } else { gf2m delta_offs = 5; - diff = this->m_j_gray ^ new_j_gray; + const gf2m diff = this->m_j_gray ^ new_j_gray; while(((static_cast(1) << delta_offs) & diff) == 0) { delta_offs++; } @@ -142,38 +134,34 @@ } this->m_j_gray = new_j_gray; - i = 0; - for(; i < this->m_outer_summands; i++) { + for(uint32_t i = 0; i < this->m_outer_summands; i++) { this->m_Aij[i] ^= this->m_Lik[Lik_pos_base + i]; } } void gf2m_decomp_rootfind_state::calc_LiK(const polyn_gf2m& sigma) { - std::shared_ptr sp_field = sigma.get_sp_field(); - uint32_t i, k, d; - d = sigma.get_degree(); - for(k = 0; k < sp_field->get_extension_degree(); k++) { - uint32_t Lik_pos_base = k * this->m_outer_summands; + const std::shared_ptr sp_field = sigma.get_sp_field(); + const uint32_t d = sigma.get_degree(); + for(uint32_t k = 0; k < sp_field->get_extension_degree(); k++) { + const uint32_t Lik_pos_base = k * this->m_outer_summands; gf2m alpha_l_k_tt2_ttj[4]; alpha_l_k_tt2_ttj[0] = sp_field->gf_l_from_n(static_cast(1) << k); alpha_l_k_tt2_ttj[1] = sp_field->gf_mul_rrr(alpha_l_k_tt2_ttj[0], alpha_l_k_tt2_ttj[0]); alpha_l_k_tt2_ttj[2] = sp_field->gf_mul_rrr(alpha_l_k_tt2_ttj[1], alpha_l_k_tt2_ttj[1]); alpha_l_k_tt2_ttj[3] = sp_field->gf_mul_rrr(alpha_l_k_tt2_ttj[2], alpha_l_k_tt2_ttj[2]); - for(i = 0; i < this->m_outer_summands; i++) { - uint32_t j; - uint32_t five_i = 5 * i; - uint32_t Lik_pos = Lik_pos_base + i; + for(uint32_t i = 0; i < this->m_outer_summands; i++) { + const uint32_t five_i = 5 * i; + const uint32_t Lik_pos = Lik_pos_base + i; this->m_Lik[Lik_pos] = 0; - for(j = 0; j <= 3; j++) { - gf2m f, x; - uint32_t f_ind = five_i + (static_cast(1) << j); + for(size_t j = 0; j <= 3; j++) { + const uint32_t f_ind = five_i + (static_cast(1) << j); if(f_ind > d) { break; } - f = sigma.get_coef(f_ind); + const gf2m f = sigma.get_coef(f_ind); - x = sp_field->gf_mul_zrz(alpha_l_k_tt2_ttj[j], f); + const gf2m x = sp_field->gf_mul_zrz(alpha_l_k_tt2_ttj[j], f); this->m_Lik[Lik_pos] ^= x; } } @@ -183,11 +171,10 @@ gf2m gf2m_decomp_rootfind_state::calc_Fxj_j_neq_0(const polyn_gf2m& sigma, gf2m j_gray) { //needs the A_{ij} to compute F(x)_j gf2m sum = 0; - uint32_t i; - std::shared_ptr sp_field = sigma.get_sp_field(); + const std::shared_ptr sp_field = sigma.get_sp_field(); const gf2m jl_gray = sp_field->gf_l_from_n(j_gray); gf2m xl_j_tt_5 = sp_field->gf_square_rr(jl_gray); - gf2m xl_gray_tt_3 = sp_field->gf_mul_rrr(xl_j_tt_5, jl_gray); + const gf2m xl_gray_tt_3 = sp_field->gf_mul_rrr(xl_j_tt_5, jl_gray); xl_j_tt_5 = sp_field->gf_mul_rrr(xl_j_tt_5, xl_gray_tt_3); sum = sp_field->gf_mul_nrr(xl_gray_tt_3, this->m_sigma_3_l); @@ -200,18 +187,16 @@ /* treat i = 1 special also */ if(this->m_outer_summands > 1) { - gf2m x; - x = sp_field->gf_mul_zrz(xl_j_tt_5, this->m_Aij[1]); /* x_j^{5i} A_i^j */ + const gf2m x = sp_field->gf_mul_zrz(xl_j_tt_5, this->m_Aij[1]); /* x_j^{5i} A_i^j */ sum ^= x; } gf2m xl_j_tt_5i = xl_j_tt_5; - for(i = 2; i < this->m_outer_summands; i++) { - gf2m x; + for(uint32_t i = 2; i < this->m_outer_summands; i++) { xl_j_tt_5i = sp_field->gf_mul_rrr(xl_j_tt_5i, xl_j_tt_5); // now x_j_tt_5i lives up to its name - x = sp_field->gf_mul_zrz(xl_j_tt_5i, this->m_Aij[i]); /* x_j^{5i} A_i^(j) */ + const gf2m x = sp_field->gf_mul_zrz(xl_j_tt_5i, this->m_Aij[i]); /* x_j^{5i} A_i^(j) */ sum ^= x; } return sum; @@ -226,7 +211,7 @@ this->calc_Ai_zero(sigma); this->calc_LiK(sigma); for(;;) { - gf2m eval_result; + gf2m eval_result = 0; if(this->m_j_gray == 0) { eval_result = sigma.get_coef(0); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/mce/gf2m_small_m.cpp botan3-3.12.0+dfsg/src/lib/pubkey/mce/gf2m_small_m.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/mce/gf2m_small_m.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/mce/gf2m_small_m.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,6 +11,7 @@ #include #include +#include #include namespace Botan { @@ -99,8 +100,7 @@ } gf2m decode_gf2m(const uint8_t* mem) { - gf2m result; - result = mem[0] << 8; + gf2m result = mem[0] << 8; result |= mem[1]; return result; } @@ -114,8 +114,8 @@ gf2m GF2m_Field::gf_div(gf2m x, gf2m y) const { const int32_t sub_res = static_cast(gf_log(x) - static_cast(gf_log(y))); const gf2m modq_res = _gf_modq_1(sub_res); - const int32_t div_res = static_cast(x) ? static_cast(gf_exp(modq_res)) : 0; - return static_cast(div_res); + const gf2m div = gf_exp(modq_res); + return (~CT::Mask::is_zero(x)).if_set_return(div); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/mce/gf2m_small_m.h botan3-3.12.0+dfsg/src/lib/pubkey/mce/gf2m_small_m.h --- botan3-3.7.1+dfsg/src/lib/pubkey/mce/gf2m_small_m.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/mce/gf2m_small_m.h 2026-05-07 01:38:28.000000000 +0000 @@ -26,13 +26,13 @@ public: explicit GF2m_Field(size_t extdeg); - gf2m gf_mul(gf2m x, gf2m y) const { return ((x) ? gf_mul_fast(x, y) : 0); } + gf2m gf_mul(gf2m x, gf2m y) const { return ((x != 0) ? gf_mul_fast(x, y) : 0); } - gf2m gf_square(gf2m x) const { return ((x) ? gf_exp(_gf_modq_1(gf_log(x) << 1)) : 0); } + gf2m gf_square(gf2m x) const { return ((x != 0) ? gf_exp(_gf_modq_1(gf_log(x) << 1)) : 0); } gf2m square_rr(gf2m x) const { return _gf_modq_1(x << 1); } - gf2m gf_mul_fast(gf2m x, gf2m y) const { return ((y) ? gf_exp(_gf_modq_1(gf_log(x) + gf_log(y))) : 0); } + gf2m gf_mul_fast(gf2m x, gf2m y) const { return ((y != 0) ? gf_exp(_gf_modq_1(gf_log(x) + gf_log(y))) : 0); } /* naming convention of GF(2^m) field operations: @@ -70,7 +70,9 @@ */ gf2m gf_mul_nnr(gf2m y, gf2m a) const { return gf_mul_nrn(a, y); } - gf2m gf_sqrt(gf2m x) const { return ((x) ? gf_exp(_gf_modq_1(gf_log(x) << (get_extension_degree() - 1))) : 0); } + gf2m gf_sqrt(gf2m x) const { + return ((x != 0) ? gf_exp(_gf_modq_1(gf_log(x) << (get_extension_degree() - 1))) : 0); + } gf2m gf_div_rnn(gf2m x, gf2m y) const { return _gf_modq_1(gf_log(x) - gf_log(y)); } @@ -78,7 +80,7 @@ gf2m gf_div_nrr(gf2m a, gf2m b) const { return gf_exp(_gf_modq_1(a - b)); } - gf2m gf_div_zzr(gf2m x, gf2m b) const { return ((x) ? gf_exp(_gf_modq_1(gf_log(x) - b)) : 0); } + gf2m gf_div_zzr(gf2m x, gf2m b) const { return ((x != 0) ? gf_exp(_gf_modq_1(gf_log(x) - b)) : 0); } gf2m gf_inv(gf2m x) const { return gf_exp(gf_ord() - gf_log(x)); } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/mce/goppa_code.cpp botan3-3.12.0+dfsg/src/lib/pubkey/mce/goppa_code.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/mce/goppa_code.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/mce/goppa_code.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -25,7 +25,7 @@ uint32_t output_vec[], size_t output_vec_len) { for(size_t j = 0; j < numo_rows; j++) { - if((input_vec[j / 8] >> (j % 8)) & 1) { + if(((input_vec[j / 8] >> (j % 8)) & 1) != 0) { for(size_t i = 0; i < output_vec_len; i++) { output_vec[i] ^= matrix[j * (words_per_row) + i]; } @@ -36,21 +36,20 @@ /** * returns the error vector to the syndrome */ -secure_vector goppa_decode(const polyn_gf2m& syndrom_polyn, +secure_vector goppa_decode(const polyn_gf2m& syndrome_polyn, const polyn_gf2m& g, const std::vector& sqrtmod, const std::vector& Linv) { const size_t code_length = Linv.size(); - gf2m a; - uint32_t t = g.get_degree(); + const uint32_t t = g.get_degree(); - std::shared_ptr sp_field = g.get_sp_field(); + const std::shared_ptr sp_field = g.get_sp_field(); - std::pair h_aux = polyn_gf2m::eea_with_coefficients(syndrom_polyn, g, 1); + std::pair h_aux = polyn_gf2m::eea_with_coefficients(syndrome_polyn, g, 1); polyn_gf2m& h = h_aux.first; - polyn_gf2m& aux = h_aux.second; - a = sp_field->gf_inv(aux.get_coef(0)); - gf2m log_a = sp_field->gf_log(a); + const polyn_gf2m& aux = h_aux.second; + gf2m a = sp_field->gf_inv(aux.get_coef(0)); + const gf2m log_a = sp_field->gf_log(a); for(int i = 0; i <= h.get_degree(); ++i) { h.set_coef(i, sp_field->gf_mul_zrz(log_a, h.get_coef(i))); } @@ -63,7 +62,7 @@ for(uint32_t i = 0; i < t; i++) { a = sp_field->gf_sqrt(h.get_coef(i)); - if(i & 1) { + if((i & 1) != 0) { for(uint32_t j = 0; j < t; j++) { S.add_to_coef(j, sp_field->gf_mul(a, sqrtmod[i / 2].get_coef(j))); } @@ -74,9 +73,9 @@ S.get_degree(); - std::pair v_u = polyn_gf2m::eea_with_coefficients(S, g, t / 2 + 1); - polyn_gf2m& u = v_u.second; - polyn_gf2m& v = v_u.first; + const std::pair v_u = polyn_gf2m::eea_with_coefficients(S, g, t / 2 + 1); + const polyn_gf2m& u = v_u.second; + const polyn_gf2m& v = v_u.first; // sigma = u^2+z*v^2 polyn_gf2m sigma(t, g.get_sp_field()); @@ -94,14 +93,13 @@ } secure_vector res = find_roots_gf2m_decomp(sigma, code_length); - size_t d = res.size(); + const size_t d = res.size(); secure_vector result(d); for(uint32_t i = 0; i < d; ++i) { - gf2m current = res[i]; + const gf2m current = res[i]; - gf2m tmp; - tmp = gray_to_lex(current); + const gf2m tmp = gray_to_lex(current); /// XXX double assignment, possible bug? if(tmp >= code_length) /* invalid root */ { @@ -191,7 +189,7 @@ copy_mem(cleartext.data(), ciphertext, cleartext_len); for(size_t i = 0; i < nb_err; i++) { - gf2m current = error_pos[i]; + const gf2m current = error_pos[i]; if(current >= cleartext_len * 8) { // an invalid position, this shouldn't happen @@ -200,7 +198,7 @@ cleartext[current / 8] ^= (1 << (current % 8)); } - if(unused_pt_bits) { + if(unused_pt_bits > 0) { cleartext[cleartext_len - 1] &= unused_pt_bits_mask; } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/mce/mce_workfactor.cpp botan3-3.12.0+dfsg/src/lib/pubkey/mce/mce_workfactor.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/mce/mce_workfactor.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/mce/mce_workfactor.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -92,7 +92,7 @@ double min = cout_total(n, k, t, 0, 0); // correspond a p=1 for(size_t p = 0; p != t / 2; ++p) { - double lwf = best_wf(n, k + 1, t, p); + const double lwf = best_wf(n, k + 1, t, p); if(lwf < 0) { break; } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/mce/mceliece.cpp botan3-3.12.0+dfsg/src/lib/pubkey/mce/mceliece.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/mce/mceliece.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/mce/mceliece.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -30,10 +30,10 @@ if(final_bits == 0) { const size_t dim_bytes = bit_size_to_byte_size(dimension); - copy_mem(&x[0], a.data(), dim_bytes); + copy_mem(&x[0], a.data(), dim_bytes); // NOLINT(*container-data-pointer) copy_mem(&x[dim_bytes], b.data(), bit_size_to_byte_size(codimension)); } else { - copy_mem(&x[0], a.data(), (dimension / 8)); + copy_mem(&x[0], a.data(), (dimension / 8)); // NOLINT(*container-data-pointer) size_t l = dimension / 8; x[l] = static_cast(a[l] & ((1 << final_bits) - 1)); @@ -61,7 +61,7 @@ for(size_t i = 0; i < dimension / 8; ++i) { for(size_t j = 0; j < 8; ++j) { - if(cleartext[i] & (1 << j)) { + if((cleartext[i] & (1 << j)) != 0) { xor_buf(cR.data(), pt, cR.size()); } pt += cR.size(); @@ -69,7 +69,7 @@ } for(size_t i = 0; i < dimension % 8; ++i) { - if(cleartext[dimension / 8] & (1 << i)) { + if((cleartext[dimension / 8] & (1 << i)) != 0) { xor_buf(cR.data(), pt, cR.size()); } pt += cR.size(); @@ -86,14 +86,14 @@ size_t bits_set = 0; while(bits_set < error_weight) { - gf2m x = random_code_element(static_cast(code_length), rng); + const gf2m x = random_code_element(static_cast(code_length), rng); const size_t byte_pos = x / 8; const size_t bit_pos = x % 8; const uint8_t mask = (1 << bit_pos); - if(result[byte_pos] & mask) { + if((result[byte_pos] & mask) != 0) { continue; // already set this bit } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/mce/mceliece.h botan3-3.12.0+dfsg/src/lib/pubkey/mce/mceliece.h --- botan3-3.7.1+dfsg/src/lib/pubkey/mce/mceliece.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/mce/mceliece.h 2026-05-07 01:38:28.000000000 +0000 @@ -22,7 +22,7 @@ class polyn_gf2m; -class BOTAN_PUBLIC_API(2, 0) McEliece_PublicKey : public virtual Public_Key { +class BOTAN_PUBLIC_API(2, 0) McEliece_PublicKey : public virtual Public_Key /* NOLINT(*-special-member-functions) */ { public: explicit McEliece_PublicKey(std::span key_bits); @@ -45,7 +45,7 @@ std::vector raw_public_key_bits() const override; std::vector public_key_bits() const override; - bool check_key(RandomNumberGenerator&, bool) const override { return true; } + bool check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const override { return true; } size_t get_t() const { return m_t; } @@ -71,9 +71,9 @@ protected: McEliece_PublicKey() : m_t(0), m_code_length(0) {} - std::vector m_public_matrix; - size_t m_t; - size_t m_code_length; + std::vector m_public_matrix; // NOLINT(*non-private-member-variable*) + size_t m_t; // NOLINT(*non-private-member-variable*) + size_t m_code_length; // NOLINT(*non-private-member-variable*) }; BOTAN_DIAGNOSTIC_PUSH diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/mce/mceliece_key.cpp botan3-3.12.0+dfsg/src/lib/pubkey/mce/mceliece_key.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/mce/mceliece_key.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/mce/mceliece_key.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -16,12 +16,12 @@ #include #include #include +#include #include #include #include #include #include -#include namespace Botan { @@ -44,8 +44,9 @@ m_codimension(static_cast(ceil_log2(inverse_support.size())) * goppa_polyn.get_degree()), m_dimension(inverse_support.size() - m_codimension) {} +// NOLINTNEXTLINE(*-member-init) McEliece_PrivateKey::McEliece_PrivateKey(RandomNumberGenerator& rng, size_t code_length, size_t t) { - uint32_t ext_deg = ceil_log2(code_length); + const uint32_t ext_deg = ceil_log2(code_length); *this = generate_mceliece_key(rng, ext_deg, code_length, t); } @@ -54,7 +55,7 @@ } size_t McEliece_PublicKey::get_message_word_bit_length() const { - size_t codimension = ceil_log2(m_code_length) * m_t; + const size_t codimension = ceil_log2(m_code_length) * m_t; return m_code_length - codimension; } @@ -65,7 +66,7 @@ rng.randomize(plaintext.data(), plaintext.size()); // unset unused bits in the last plaintext byte - if(uint32_t used = bits % 8) { + if(const uint32_t used = bits % 8) { const uint8_t mask = (1 << used) - 1; plaintext[plaintext.size() - 1] &= mask; } @@ -103,16 +104,48 @@ } McEliece_PublicKey::McEliece_PublicKey(std::span key_bits) { - BER_Decoder dec(key_bits); - size_t n; - size_t t; + BER_Decoder dec(key_bits, BER_Decoder::Limits::DER()); + size_t n = 0; + size_t t = 0; dec.start_sequence() .start_sequence() .decode(n) .decode(t) .end_cons() .decode(m_public_matrix, ASN1_Type::OctetString) - .end_cons(); + .end_cons() + .verify_end(); + + if(n == 0 || t == 0) { + throw Decoding_Error("Invalid McEliece parameters"); + } + + // GF(2^m) field requires extension degree in [2, 16] + const size_t ext_deg = ceil_log2(n); + if(ext_deg < 2 || ext_deg > 16) { + throw Decoding_Error("McEliece code length out of supported range"); + } + + // Since ext_deg >= 2, t >= n already implies ext_deg * t > n + if(t >= n) { + throw Decoding_Error("McEliece parameters are inconsistent"); + } + + const size_t codimension = ext_deg * t; + + // codimension must be strictly less than n, otherwise the code has no message bits + if(codimension >= n) { + throw Decoding_Error("McEliece parameters are inconsistent"); + } + + const size_t dimension = n - codimension; + + // public matrix is a dimension x codimension binary matrix stored as uint32_t rows + const size_t expected_pubmat_size = dimension * bit_size_to_32bit_size(codimension) * sizeof(uint32_t); + if(m_public_matrix.size() != expected_pubmat_size) { + throw Decoding_Error("McEliece public matrix size does not match parameters"); + } + m_t = t; m_code_length = n; } @@ -127,19 +160,19 @@ .encode(m_public_matrix, ASN1_Type::OctetString) .encode(m_g[0].encode(), ASN1_Type::OctetString); // g as octet string enc.start_sequence(); - for(size_t i = 0; i < m_sqrtmod.size(); i++) { - enc.encode(m_sqrtmod[i].encode(), ASN1_Type::OctetString); + for(const auto& x : m_sqrtmod) { + enc.encode(x.encode(), ASN1_Type::OctetString); } enc.end_cons(); secure_vector enc_support; - for(uint16_t Linv : m_Linv) { + for(const uint16_t Linv : m_Linv) { enc_support.push_back(get_byte<0>(Linv)); enc_support.push_back(get_byte<1>(Linv)); } enc.encode(enc_support, ASN1_Type::OctetString); secure_vector enc_H; - for(uint32_t coef : m_coeffs) { + for(const uint32_t coef : m_coeffs) { enc_H.push_back(get_byte<0>(coef)); enc_H.push_back(get_byte<1>(coef)); enc_H.push_back(get_byte<2>(coef)); @@ -169,26 +202,45 @@ } McEliece_PrivateKey::McEliece_PrivateKey(std::span key_bits) { - size_t n, t; + size_t n = 0; + size_t t = 0; secure_vector enc_g; - BER_Decoder dec_base(key_bits); - BER_Decoder dec = dec_base.start_sequence() - .start_sequence() - .decode(n) - .decode(t) - .end_cons() - .decode(m_public_matrix, ASN1_Type::OctetString) - .decode(enc_g, ASN1_Type::OctetString); + BER_Decoder dec_base(key_bits, BER_Decoder::Limits::DER()); + BER_Decoder dec = dec_base.start_sequence(); + dec.start_sequence().decode(n).decode(t).end_cons(); + dec.decode(m_public_matrix, ASN1_Type::OctetString).decode(enc_g, ASN1_Type::OctetString); if(t == 0 || n == 0) { throw Decoding_Error("invalid McEliece parameters"); } - uint32_t ext_deg = ceil_log2(n); + const uint32_t ext_deg = ceil_log2(n); + + if(ext_deg < 2 || ext_deg > 16) { + throw Decoding_Error("McEliece code length out of supported range"); + } + + // Since ext_deg >= 2, t >= n already implies ext_deg * t > n + if(t >= n) { + throw Decoding_Error("McEliece parameters are inconsistent"); + } + + const size_t codimension = ext_deg * t; + + if(codimension >= n) { + throw Decoding_Error("McEliece parameters are inconsistent"); + } + + const size_t dimension = n - codimension; + const size_t expected_pubmat_size = dimension * bit_size_to_32bit_size(codimension) * sizeof(uint32_t); + if(m_public_matrix.size() != expected_pubmat_size) { + throw Decoding_Error("McEliece public matrix size does not match parameters"); + } + m_code_length = n; m_t = t; - m_codimension = (ext_deg * t); - m_dimension = (n - m_codimension); + m_codimension = codimension; + m_dimension = dimension; auto sp_field = std::make_shared(ext_deg); m_g = {polyn_gf2m(enc_g, sp_field)}; @@ -210,20 +262,21 @@ m_sqrtmod.push_back(polyn_gf2m(sqrt_enc, sp_field)); } secure_vector enc_support; - BER_Decoder dec3 = dec2.end_cons().decode(enc_support, ASN1_Type::OctetString); - if(enc_support.size() % 2) { + dec2.end_cons(); + dec.decode(enc_support, ASN1_Type::OctetString); + if(enc_support.size() % 2 != 0) { throw Decoding_Error("encoded support has odd length"); } if(enc_support.size() / 2 != n) { throw Decoding_Error("encoded support has length different from code length"); } for(uint32_t i = 0; i < n * 2; i += 2) { - gf2m el = (enc_support[i] << 8) | enc_support[i + 1]; + const gf2m el = (enc_support[i] << 8) | enc_support[i + 1]; m_Linv.push_back(el); } secure_vector enc_H; - dec3.decode(enc_H, ASN1_Type::OctetString).end_cons(); - if(enc_H.size() % 4) { + dec.decode(enc_H, ASN1_Type::OctetString).end_cons().verify_end(); + if(enc_H.size() % 4 != 0) { throw Decoding_Error("encoded parity check matrix has length which is not a multiple of four"); } if(enc_H.size() / 4 != bit_size_to_32bit_size(m_codimension) * m_code_length) { @@ -231,7 +284,7 @@ } for(uint32_t i = 0; i < enc_H.size(); i += 4) { - uint32_t coeff = (enc_H[i] << 24) | (enc_H[i + 1] << 16) | (enc_H[i + 2] << 8) | enc_H[i + 3]; + const uint32_t coeff = (enc_H[i] << 24) | (enc_H[i + 1] << 16) | (enc_H[i + 2] << 8) | enc_H[i + 3]; m_coeffs.push_back(coeff); } } @@ -299,7 +352,8 @@ RandomNumberGenerator& rng) override { secure_vector plaintext = m_key.random_plaintext_element(rng); - secure_vector ciphertext, error_mask; + secure_vector ciphertext; + secure_vector error_mask; mceliece_encrypt(ciphertext, error_mask, plaintext, m_key, rng); // TODO: Perhaps avoid the copies below @@ -330,7 +384,8 @@ size_t encapsulated_key_length() const override { return (m_key.get_code_length() + 7) / 8; } void raw_kem_decrypt(std::span out_shared_key, std::span encapsulated_key) override { - secure_vector plaintext, error_mask; + secure_vector plaintext; + secure_vector error_mask; mceliece_decrypt(plaintext, error_mask, encapsulated_key.data(), encapsulated_key.size(), m_key); // TODO: perhaps avoid the copies below diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/mce/polyn_gf2m.cpp botan3-3.12.0+dfsg/src/lib/pubkey/mce/polyn_gf2m.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/mce/polyn_gf2m.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/mce/polyn_gf2m.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -14,16 +14,17 @@ #include #include -#include #include #include namespace Botan { +// NOLINTBEGIN(*-implicit-bool-conversion) + namespace { gf2m generate_gf2m_mask(gf2m a) { - gf2m result = (a != 0); + const gf2m result = (a != 0); return ~(result - 1); } @@ -31,11 +32,10 @@ * number of leading zeros */ unsigned nlz_16bit(uint16_t x) { - unsigned n; if(x == 0) { return 16; } - n = 0; + unsigned n = 0; if(x <= 0x00FF) { n = n + 8; x = x << 8; @@ -53,6 +53,7 @@ } return n; } + } // namespace int polyn_gf2m::calc_degree_secure() const { @@ -84,12 +85,12 @@ const unsigned nlz = nlz_16bit(code_length - 1); const gf2m mask = (1 << (16 - nlz)) - 1; - gf2m result; + gf2m result = random_gf2m(rng) & mask; - do { - result = random_gf2m(rng); - result &= mask; - } while(result >= code_length); // rejection sampling + while(result >= code_length) { + // rejection sampling + result = random_gf2m(rng) & mask; + } return result; } @@ -112,7 +113,7 @@ throw Decoding_Error("illegal length of memory to decode "); } - uint32_t size = (mem_len / sizeof(this->m_coeff[0])); + const uint32_t size = (mem_len / sizeof(this->m_coeff[0])); this->m_coeff = secure_vector(size); this->m_deg = -1; for(uint32_t i = 0; i < size; i++) { @@ -134,20 +135,17 @@ size_t mem_byte_len, const std::shared_ptr& sp_field) : m_sp_field(sp_field) { - uint32_t j, k, l; - gf2m a; - uint32_t polyn_size; - polyn_size = degree + 1; + const uint32_t polyn_size = degree + 1; if(polyn_size * sp_field->get_extension_degree() > 8 * mem_byte_len) { throw Decoding_Error("memory vector for polynomial has wrong size"); } this->m_coeff = secure_vector(degree + 1); - gf2m ext_deg = static_cast(this->m_sp_field->get_extension_degree()); - for(l = 0; l < polyn_size; l++) { - k = (l * ext_deg) / 8; + const gf2m ext_deg = static_cast(this->m_sp_field->get_extension_degree()); + for(uint32_t l = 0; l < polyn_size; l++) { + const uint32_t k = (l * ext_deg) / 8; - j = (l * ext_deg) % 8; - a = mem[k] >> j; + const uint32_t j = (l * ext_deg) % 8; + gf2m a = mem[k] >> j; if(j + ext_deg > 8) { a ^= mem[k + 1] << (8 - j); } @@ -162,7 +160,7 @@ } void polyn_gf2m::set_to_zero() { - clear_mem(&this->m_coeff[0], this->m_coeff.size()); + clear_mem(this->m_coeff.data(), this->m_coeff.size()); this->m_deg = -1; } @@ -178,8 +176,7 @@ namespace { gf2m eval_aux(const gf2m* /*restrict*/ coeff, gf2m a, int d, const std::shared_ptr& sp_field) { - gf2m b; - b = coeff[d--]; + gf2m b = coeff[d--]; for(; d >= 0; --d) { if(b != 0) { b = sp_field->gf_mul(b, a) ^ coeff[d]; @@ -193,16 +190,17 @@ } // namespace gf2m polyn_gf2m::eval(gf2m a) { - return eval_aux(&this->m_coeff[0], a, this->m_deg, this->m_sp_field); + return eval_aux(this->m_coeff.data(), a, this->m_deg, this->m_sp_field); } // p will contain it's remainder modulo g void polyn_gf2m::remainder(polyn_gf2m& p, const polyn_gf2m& g) { - int i, j, d; - std::shared_ptr m_sp_field = g.m_sp_field; - d = p.get_degree() - g.get_degree(); + int i = 0; + int j = 0; + const std::shared_ptr m_sp_field = g.m_sp_field; + int d = p.get_degree() - g.get_degree(); if(d >= 0) { - gf2m la = m_sp_field->gf_inv_rn(g.get_lead_coef()); + const gf2m la = m_sp_field->gf_inv_rn(g.get_lead_coef()); const int p_degree = p.get_degree(); @@ -210,7 +208,7 @@ for(i = p_degree; d >= 0; --i, --d) { if(p[i] != 0) { - gf2m lb = m_sp_field->gf_mul_rrn(la, p[i]); + const gf2m lb = m_sp_field->gf_mul_rrn(la, p[i]); for(j = 0; j < g.get_degree(); ++j) { p[j + d] ^= m_sp_field->gf_mul_zrz(lb, g[j]); } @@ -232,9 +230,9 @@ } const uint32_t d = static_cast(signed_deg); - uint32_t t = g.m_deg; + const uint32_t t = g.m_deg; // create t zero polynomials - uint32_t i; + uint32_t i = 0; for(i = 0; i < t; ++i) { sq.push_back(polyn_gf2m(t + 1, g.get_sp_field())); } @@ -244,8 +242,8 @@ } for(; i < d; ++i) { - clear_mem(&sq[i].m_coeff[0], 2); - copy_mem(&sq[i].m_coeff[0] + 2, &sq[i - 1].m_coeff[0], d); + clear_mem(sq[i].m_coeff.data(), 2); + copy_mem(sq[i].m_coeff.data() + 2, sq[i - 1].m_coeff.data(), d); sq[i].set_degree(sq[i - 1].get_degree() + 2); polyn_gf2m::remainder(sq[i], g); } @@ -256,9 +254,8 @@ Modulo g of the base canonical polynomials of degree < d, where d is the degree of G. The table sq[] will be calculated by polyn_gf2m_sqmod_init*/ polyn_gf2m polyn_gf2m::sqmod(const std::vector& sq, int d) { - int i, j; - gf2m la; - std::shared_ptr sp_field = this->m_sp_field; + int i = 0; + const std::shared_ptr sp_field = this->m_sp_field; polyn_gf2m result(d - 1, sp_field); // terms of low degree @@ -271,8 +268,8 @@ gf2m lpi = (*this)[i]; if(lpi != 0) { lpi = sp_field->gf_log(lpi); - la = sp_field->gf_mul_rrr(lpi, lpi); - for(j = 0; j < d; ++j) { + const gf2m la = sp_field->gf_mul_rrr(lpi, lpi); + for(int j = 0; j < d; ++j) { result[j] ^= sp_field->gf_mul_zrz(la, sq[i][j]); } } @@ -312,7 +309,7 @@ const size_t ext_deg = g.m_sp_field->get_extension_degree(); const int d = g.get_degree(); - std::vector u = polyn_gf2m::sqmod_init(g); + const std::vector u = polyn_gf2m::sqmod_init(g); polyn_gf2m p(d - 1, g.m_sp_field); @@ -343,15 +340,13 @@ } void polyn_gf2m::patchup_deg_secure(uint32_t trgt_deg, gf2m patch_elem) { - uint32_t i; if(this->m_coeff.size() < trgt_deg) { return; } - for(i = 0; i < this->m_coeff.size(); i++) { - uint32_t equal, equal_mask; + for(uint32_t i = 0; i < this->m_coeff.size(); i++) { this->m_coeff[i] |= patch_elem; - equal = (i == trgt_deg); - equal_mask = expand_mask_16bit(equal); + const uint32_t equal = (i == trgt_deg); + const uint32_t equal_mask = expand_mask_16bit(equal); patch_elem &= ~equal_mask; } this->calc_degree_secure(); @@ -362,14 +357,12 @@ std::pair polyn_gf2m::eea_with_coefficients(const polyn_gf2m& p, const polyn_gf2m& g, int break_deg) { - std::shared_ptr m_sp_field = g.m_sp_field; - int i, j, dr, du, delta; - gf2m a; + const std::shared_ptr m_sp_field = g.m_sp_field; polyn_gf2m aux; // initialisation of the local variables // r0 <- g, r1 <- p, u0 <- 0, u1 <- 1 - dr = g.get_degree(); + int dr = g.get_degree(); BOTAN_ASSERT(dr > 3, "Valid polynomial"); @@ -391,15 +384,17 @@ // and m_deg(u1) = m_deg(g) - m_deg(r0) // It stops when m_deg (r1) = t) // And therefore m_deg (u1) = m_deg (g) - m_deg (r0) = break_deg) { for(j = delta; j >= 0; --j) { - a = m_sp_field->gf_div(r0[dr + j], r1[dr]); + const gf2m a = m_sp_field->gf_div(r0[dr + j], r1[dr]); if(a != 0) { - gf2m la = m_sp_field->gf_log(a); + const gf2m la = m_sp_field->gf_log(a); // u0(z) <- u0(z) + a * u1(z) * z^j for(i = 0; i <= du; ++i) { u0[i + j] ^= m_sp_field->gf_mul_zrz(la, u1[i]); @@ -418,8 +413,9 @@ * */ volatile gf2m fake_elem = 0x01; - volatile gf2m cond1, cond2; - int trgt_deg = r1.get_degree() - 1; + volatile gf2m cond1 = 0; + volatile gf2m cond2 = 0; + const int trgt_deg = r1.get_degree() - 1; r0.calc_degree_secure(); u0.calc_degree_secure(); if(!(g.get_degree() % 2)) { @@ -432,7 +428,7 @@ cond1 = cond1 & cond2; } /* expand cond1 to a full mask */ - gf2m mask = generate_gf2m_mask(cond1); + const gf2m mask = generate_gf2m_mask(cond1); fake_elem = fake_elem & mask; r0.patchup_deg_secure(trgt_deg, fake_elem); } @@ -465,18 +461,18 @@ int cond_r = r0.get_degree() == 0; /** * Now come the conditions for all odd coefficients of this sigma - * candiate. If they are all fulfilled, then we know that we have a low + * candidate. If they are all fulfilled, then we know that we have a low * weight error vector, since the key-equation solving EEA is skipped if - * the degree of tau^2 is low (=m_deg(u0)) and all its odd cofficients are + * the degree of tau^2 is low (=m_deg(u0)) and all its odd coefficients are * zero (they would cause "full-length" contributions from the square * root computation). */ // Condition for the coefficient to Y to be cancelled out by the // addition of Y before the square root computation: - int cond_u1 = m_sp_field->gf_mul(u0.m_coeff[1], m_sp_field->gf_inv(r0.m_coeff[0])) == 1; + const int cond_u1 = m_sp_field->gf_mul(u0.m_coeff[1], m_sp_field->gf_inv(r0.m_coeff[0])) == 1; // Condition sigma_3 = 0: - int cond_u3 = u0.m_coeff[3] == 0; + const int cond_u3 = u0.m_coeff[3] == 0; // combine the conditions: cond_r &= (cond_u1 & cond_u3); // mask generation: @@ -486,10 +482,10 @@ } else if(u0.get_degree() == 6) { uint32_t mask = 0; int cond_r = r0.get_degree() == 0; - int cond_u1 = m_sp_field->gf_mul(u0.m_coeff[1], m_sp_field->gf_inv(r0.m_coeff[0])) == 1; - int cond_u3 = u0.m_coeff[3] == 0; + const int cond_u1 = m_sp_field->gf_mul(u0.m_coeff[1], m_sp_field->gf_inv(r0.m_coeff[0])) == 1; + const int cond_u3 = u0.m_coeff[3] == 0; - int cond_u5 = u0.m_coeff[5] == 0; + const int cond_u5 = u0.m_coeff[5] == 0; cond_r &= (cond_u1 & cond_u3 & cond_u5); mask = expand_mask_16bit(cond_r); @@ -498,12 +494,12 @@ } else if(u0.get_degree() == 8) { uint32_t mask = 0; int cond_r = r0.get_degree() == 0; - int cond_u1 = m_sp_field->gf_mul(u0[1], m_sp_field->gf_inv(r0[0])) == 1; - int cond_u3 = u0.m_coeff[3] == 0; + const int cond_u1 = m_sp_field->gf_mul(u0[1], m_sp_field->gf_inv(r0[0])) == 1; + const int cond_u3 = u0.m_coeff[3] == 0; - int cond_u5 = u0.m_coeff[5] == 0; + const int cond_u5 = u0.m_coeff[5] == 0; - int cond_u7 = u0.m_coeff[7] == 0; + const int cond_u7 = u0.m_coeff[7] == 0; cond_r &= (cond_u1 & cond_u3 & cond_u5 & cond_u7); mask = expand_mask_16bit(cond_r); @@ -555,10 +551,10 @@ if(g.get_degree() <= 1) { throw Invalid_Argument("shiftmod cannot be called on polynomials of degree 1 or less"); } - std::shared_ptr field = g.m_sp_field; + const std::shared_ptr field = g.m_sp_field; - int t = g.get_degree(); - gf2m a = field->gf_div(this->m_coeff[t - 1], g.m_coeff[t]); + const int t = g.get_degree(); + const gf2m a = field->gf_div(this->m_coeff[t - 1], g.m_coeff[t]); for(int i = t - 1; i > 0; --i) { this->m_coeff[i] = this->m_coeff[i - 1] ^ this->m_sp_field->gf_mul(a, g.m_coeff[i]); } @@ -566,14 +562,15 @@ } std::vector polyn_gf2m::sqrt_mod_init(const polyn_gf2m& g) { - uint32_t i, t; - uint32_t nb_polyn_sqrt_mat; - std::shared_ptr m_sp_field = g.m_sp_field; + uint32_t i = 0; + uint32_t t = 0; + uint32_t nb_polyn_sqrt_mat = 0; + const std::shared_ptr m_sp_field = g.m_sp_field; std::vector result; t = g.get_degree(); nb_polyn_sqrt_mat = t / 2; - std::vector sq_aux = polyn_gf2m::sqmod_init(g); + const std::vector sq_aux = polyn_gf2m::sqmod_init(g); polyn_gf2m p(t - 1, g.get_sp_field()); p.set_degree(1); @@ -584,7 +581,7 @@ // q(z) <- p(z)^2 mod g(z) polyn_gf2m q = p.sqmod(sq_aux, t); // q(z) <-> p(z) - polyn_gf2m aux = q; + const polyn_gf2m aux = q; q = p; p = aux; } @@ -606,10 +603,12 @@ } std::vector syndrome_init(const polyn_gf2m& generator, const std::vector& support, int n) { - int i, j, t; - gf2m a; + int i = 0; + int j = 0; + int t = 0; + gf2m a = 0; - std::shared_ptr m_sp_field = generator.get_sp_field(); + const std::shared_ptr m_sp_field = generator.get_sp_field(); std::vector result; t = generator.get_degree(); @@ -638,7 +637,7 @@ throw Decoding_Error("encoded polynomial has odd length"); } for(uint32_t i = 0; i < encoded.size(); i += 2) { - gf2m el = (encoded[i] << 8) | encoded[i + 1]; + const gf2m el = (encoded[i] << 8) | encoded[i + 1]; m_coeff.push_back(el); } get_degree(); @@ -653,7 +652,7 @@ return result; } - uint32_t len = m_deg + 1; + const uint32_t len = m_deg + 1; for(unsigned i = 0; i < len; i++) { // "big endian" encoding of the GF(2^m) elements result.push_back(get_byte<0>(m_coeff[i])); @@ -669,10 +668,9 @@ } bool polyn_gf2m::operator==(const polyn_gf2m& other) const { - if(m_deg != other.m_deg || m_coeff != other.m_coeff) { - return false; - } - return true; + return m_deg == other.m_deg && m_coeff == other.m_coeff; } +// NOLINTEND(*-implicit-bool-conversion) + } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/mce/polyn_gf2m.h botan3-3.12.0+dfsg/src/lib/pubkey/mce/polyn_gf2m.h --- botan3-3.7.1+dfsg/src/lib/pubkey/mce/polyn_gf2m.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/mce/polyn_gf2m.h 2026-05-07 01:38:28.000000000 +0000 @@ -13,6 +13,7 @@ #define BOTAN_POLYN_GF2M_H_ #include +#include #include namespace Botan { @@ -34,6 +35,8 @@ polyn_gf2m(const secure_vector& encoded, const std::shared_ptr& sp_field); + ~polyn_gf2m() = default; + polyn_gf2m& operator=(const polyn_gf2m&) = default; /** @@ -129,7 +132,7 @@ static polyn_gf2m gcd_aux(polyn_gf2m& p1, polyn_gf2m& p2); private: - int m_deg; + int m_deg = -1; secure_vector m_coeff; std::shared_ptr m_sp_field; }; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/pbes2/pbes2.cpp botan3-3.12.0+dfsg/src/lib/pubkey/pbes2/pbes2.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/pbes2/pbes2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/pbes2/pbes2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -30,10 +30,11 @@ size_t default_key_size) { if(kdf_algo.oid() == OID::from_string("PKCS5.PBKDF2")) { secure_vector salt; - size_t iterations = 0, key_length = 0; + size_t iterations = 0; + size_t key_length = 0; AlgorithmIdentifier prf_algo; - BER_Decoder(kdf_algo.parameters()) + BER_Decoder(kdf_algo.parameters(), BER_Decoder::Limits::DER()) .start_sequence() .decode(salt, ASN1_Type::OctetString) .decode(iterations) @@ -42,7 +43,12 @@ ASN1_Type::Sequence, ASN1_Class::Constructed, AlgorithmIdentifier("HMAC(SHA-1)", AlgorithmIdentifier::USE_NULL_PARAM)) - .end_cons(); + .end_cons() + .verify_end(); + + if(iterations == 0) { + throw Decoding_Error("PBE-PKCS5 v2.0: Iteration count must be positive"); + } if(salt.size() < 8) { throw Decoding_Error("PBE-PKCS5 v2.0: Encoded salt is too small"); @@ -65,18 +71,25 @@ return derived_key; } else if(kdf_algo.oid() == OID::from_string("Scrypt")) { secure_vector salt; - size_t N = 0, r = 0, p = 0; + size_t N = 0; + size_t r = 0; + size_t p = 0; size_t key_length = 0; - AlgorithmIdentifier prf_algo; - BER_Decoder(kdf_algo.parameters()) + const AlgorithmIdentifier prf_algo; + BER_Decoder(kdf_algo.parameters(), BER_Decoder::Limits::DER()) .start_sequence() .decode(salt, ASN1_Type::OctetString) .decode(N) .decode(r) .decode(p) .decode_optional(key_length, ASN1_Type::Integer, ASN1_Class::Universal) - .end_cons(); + .end_cons() + .verify_end(); + + if(N == 0 || r == 0 || p == 0) { + throw Decoding_Error("PBE-PKCS5 v2.0: Invalid Scrypt parameters"); + } if(key_length == 0) { key_length = default_key_size; @@ -110,9 +123,8 @@ std::unique_ptr pwhash; - if(msec_in_iterations_out) { - const std::chrono::milliseconds msec(*msec_in_iterations_out); - pwhash = pwhash_fam->tune(key_length, msec); + if(msec_in_iterations_out != nullptr) { + pwhash = pwhash_fam->tune_params(key_length, *msec_in_iterations_out); } else { pwhash = pwhash_fam->from_iterations(iterations_if_msec_null); } @@ -124,7 +136,7 @@ const size_t r = pwhash->iterations(); const size_t p = pwhash->parallelism(); - if(msec_in_iterations_out) { + if(msec_in_iterations_out != nullptr) { *msec_in_iterations_out = 0; } @@ -151,9 +163,8 @@ std::unique_ptr pwhash; - if(msec_in_iterations_out) { - const std::chrono::milliseconds msec(*msec_in_iterations_out); - pwhash = pwhash_fam->tune(key_length, msec); + if(msec_in_iterations_out != nullptr) { + pwhash = pwhash_fam->tune_params(key_length, *msec_in_iterations_out); } else { pwhash = pwhash_fam->from_iterations(iterations_if_msec_null); } @@ -165,7 +176,7 @@ const size_t iterations = pwhash->iterations(); - if(msec_in_iterations_out) { + if(msec_in_iterations_out != nullptr) { *msec_in_iterations_out = iterations; } @@ -232,7 +243,7 @@ .encode(AlgorithmIdentifier(cipher, encoded_iv)) .end_cons(); - AlgorithmIdentifier id(OID::from_string("PBE-PKCS5v20"), pbes2_params); + const AlgorithmIdentifier id(OID::from_string("PBE-PKCS5v20"), pbes2_params); return std::make_pair(id, unlock(ctext)); } @@ -261,7 +272,7 @@ auto ret = pbes2_encrypt_shared(key_bits, passphrase, &msec_in_iterations_out, 0, cipher, digest, rng); - if(out_iterations_if_nonnull) { + if(out_iterations_if_nonnull != nullptr) { *out_iterations_if_nonnull = msec_in_iterations_out; } @@ -280,9 +291,15 @@ secure_vector pbes2_decrypt(std::span key_bits, std::string_view passphrase, const std::vector& params) { - AlgorithmIdentifier kdf_algo, enc_algo; + AlgorithmIdentifier kdf_algo; + AlgorithmIdentifier enc_algo; - BER_Decoder(params).start_sequence().decode(kdf_algo).decode(enc_algo).end_cons(); + BER_Decoder(params, BER_Decoder::Limits::DER()) + .start_sequence() + .decode(kdf_algo) + .decode(enc_algo) + .end_cons() + .verify_end(); const std::string cipher = enc_algo.oid().human_name_or_empty(); const auto cipher_spec = split_on(cipher, '/'); @@ -291,7 +308,7 @@ } secure_vector iv; - BER_Decoder(enc_algo.parameters()).decode(iv, ASN1_Type::OctetString).verify_end(); + BER_Decoder(enc_algo.parameters(), BER_Decoder::Limits::DER()).decode(iv, ASN1_Type::OctetString).verify_end(); auto dec = Cipher_Mode::create(cipher, Cipher_Dir::Decryption); if(!dec) { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/pbes2/pbes2.h botan3-3.12.0+dfsg/src/lib/pubkey/pbes2/pbes2.h --- botan3-3.7.1+dfsg/src/lib/pubkey/pbes2/pbes2.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/pbes2/pbes2.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #define BOTAN_PBE_PKCS_V20_H_ #include +#include #include #include diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/pem/pem.cpp botan3-3.12.0+dfsg/src/lib/pubkey/pem/pem.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/pem/pem.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/pem/pem.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -69,11 +69,12 @@ size_t position = 0; while(position != PEM_HEADER1.length()) { - uint8_t b; - if(!source.read_byte(b)) { + auto b = source.read_byte(); + + if(!b) { throw Decoding_Error("PEM: No PEM header found"); } - if(static_cast(b) == PEM_HEADER1[position]) { + if(static_cast(*b) == PEM_HEADER1[position]) { ++position; } else if(position >= RANDOM_CHAR_LIMIT) { throw Decoding_Error("PEM: Malformed PEM header"); @@ -83,18 +84,22 @@ } position = 0; while(position != PEM_HEADER2.length()) { - uint8_t b; - if(!source.read_byte(b)) { + auto b = source.read_byte(); + + if(!b) { throw Decoding_Error("PEM: No PEM header found"); } - if(static_cast(b) == PEM_HEADER2[position]) { + if(static_cast(*b) == PEM_HEADER2[position]) { ++position; - } else if(position) { + } else if(position > 0) { throw Decoding_Error("PEM: Malformed PEM header"); } if(position == 0) { - label += static_cast(b); + if(label.size() >= 128) { + throw Decoding_Error("PEM: Label too long"); + } + label += static_cast(*b); } } @@ -103,18 +108,19 @@ const std::string PEM_TRAILER = fmt("-----END {}-----", label); position = 0; while(position != PEM_TRAILER.length()) { - uint8_t b; - if(!source.read_byte(b)) { + auto b = source.read_byte(); + + if(!b) { throw Decoding_Error("PEM: No PEM trailer found"); } - if(static_cast(b) == PEM_TRAILER[position]) { + if(static_cast(*b) == PEM_TRAILER[position]) { ++position; - } else if(position) { + } else if(position > 0) { throw Decoding_Error("PEM: Malformed PEM trailer"); } if(position == 0) { - b64.push_back(b); + b64.push_back(*b); } } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/pk_algs.cpp botan3-3.12.0+dfsg/src/lib/pubkey/pk_algs.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/pk_algs.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/pk_algs.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,7 +7,9 @@ #include +#include #include +#include #include #include @@ -35,6 +37,10 @@ #include #endif +#if defined(BOTAN_HAS_ECC_GROUP) + #include +#endif + #if defined(BOTAN_HAS_ECDSA) #include #endif @@ -125,7 +131,7 @@ [[maybe_unused]] std::span key_bits) { const std::string oid_str = alg_id.oid().to_formatted_string(); const std::vector alg_info = split_on(oid_str, '/'); - std::string_view alg_name = alg_info[0]; + const std::string_view alg_name = alg_info[0]; #if defined(BOTAN_HAS_RSA) if(alg_name == "RSA") { @@ -284,7 +290,7 @@ [[maybe_unused]] std::span key_bits) { const std::string oid_str = alg_id.oid().to_formatted_string(); const std::vector alg_info = split_on(oid_str, '/'); - std::string_view alg_name = alg_info[0]; + const std::string_view alg_name = alg_info[0]; #if defined(BOTAN_HAS_RSA) if(alg_name == "RSA") { @@ -489,7 +495,7 @@ std::string_view params, std::string_view provider) { /* - * Default paramaters are chosen for work factor > 2**128 where possible + * Default parameters are chosen for work factor > 2**128 where possible */ #if defined(BOTAN_HAS_X25519) @@ -634,7 +640,7 @@ if(params.empty()) { return XMSS_Parameters::XMSS_SHA2_10_512; } - return XMSS_Parameters(params).oid(); + return XMSS_Parameters::from_name(params).oid(); }(); return std::make_unique(xmss_oid, rng); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/pk_algs.h botan3-3.12.0+dfsg/src/lib/pubkey/pk_algs.h --- botan3-3.7.1+dfsg/src/lib/pubkey/pk_algs.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/pk_algs.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,12 +8,21 @@ #ifndef BOTAN_PK_KEY_FACTORY_H_ #define BOTAN_PK_KEY_FACTORY_H_ -#include -#include +#include #include +#include +#include +#include +#include namespace Botan { +class Public_Key; +class Private_Key; +class AlgorithmIdentifier; +class EC_Group; +class RandomNumberGenerator; + BOTAN_PUBLIC_API(2, 0) std::unique_ptr load_public_key(const AlgorithmIdentifier& alg_id, std::span key_bits); @@ -34,8 +43,6 @@ std::string_view algo_params = "", std::string_view provider = ""); -class EC_Group; - /** * Create a new ECC key */ diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/pk_keys.cpp botan3-3.12.0+dfsg/src/lib/pubkey/pk_keys.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/pk_keys.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/pk_keys.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -35,19 +35,26 @@ } } -std::string create_hex_fingerprint(const uint8_t bits[], size_t bits_len, std::string_view hash_name) { +std::string create_hex_fingerprint(std::span bits, std::string_view hash_name) { auto hash_fn = HashFunction::create_or_throw(hash_name); - const std::string hex_hash = hex_encode(hash_fn->process(bits, bits_len)); + hash_fn->update(bits); + auto digest = hash_fn->final_stdvec(); + return format_hex_fingerprint(digest); +} + +std::string format_hex_fingerprint(std::span bits) { + const std::string hex = hex_encode(bits); std::string fprint; + fprint.reserve(3 * bits.size()); - for(size_t i = 0; i != hex_hash.size(); i += 2) { + for(size_t i = 0; i != hex.size(); i += 2) { if(i != 0) { fprint.push_back(':'); } - fprint.push_back(hex_hash[i]); - fprint.push_back(hex_hash[i + 1]); + fprint.push_back(hex[i]); + fprint.push_back(hex[i + 1]); } return fprint; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/pk_keys.h botan3-3.12.0+dfsg/src/lib/pubkey/pk_keys.h --- botan3-3.7.1+dfsg/src/lib/pubkey/pk_keys.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/pk_keys.h 2026-05-07 01:38:28.000000000 +0000 @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -28,9 +29,9 @@ * This is mostly used for requesting DER encoding of ECDSA signatures; * most other algorithms only support "standard". */ -enum class Signature_Format { - Standard, - DerSequence, +enum class Signature_Format : uint8_t { + Standard = 0, + DerSequence = 1, IEEE_1363 BOTAN_DEPRECATED("Use Standard") = Standard, DER_SEQUENCE BOTAN_DEPRECATED("Use DerSequence") = DerSequence, @@ -42,7 +43,7 @@ * It is possible to query if a key supports a particular operation * type using Asymmetric_Key::supports_operation() */ -enum class PublicKeyOperation { +enum class PublicKeyOperation : uint8_t { Encryption, Signature, KeyEncapsulation, @@ -56,7 +57,7 @@ * * This is derived for both public and private keys */ -class BOTAN_PUBLIC_API(3, 0) Asymmetric_Key { +class BOTAN_PUBLIC_API(3, 0) Asymmetric_Key /* NOLINT(*special-member-functions) */ { public: virtual ~Asymmetric_Key() = default; @@ -116,9 +117,16 @@ /* * Test the key values for consistency. - * @param rng rng to use - * @param strong whether to perform strong and lengthy version of the test - * @return true if the test is passed + * + * Note this function is always "best effort"; for many algorithms it is + * not computationally possible to ensure the key is correctly formed in + * all respects. There is always the possibility a malformed key will be + * accepted; this is especially the case for public keys. + * + * @param rng rng to use for randomized testing (may be ignored) + * @param strong whether to perform strong and lengthy version of the test, + * however for many algorithms this has no effect + * @return true if the tests passed */ virtual bool check_key(RandomNumberGenerator& rng, bool strong) const = 0; @@ -209,7 +217,7 @@ } /** - * Returns how large each of the message parts refered to + * Returns how large each of the message parts referred to * by message_parts() is * * This function is public but applications should have few @@ -221,6 +229,8 @@ return _signature_element_size_for_DER_encoding().value_or(0); } + // NOLINTBEGIN(bugprone-virtual-near-miss) + /* * Return the format normally used by this algorithm for X.509 signatures */ @@ -228,6 +238,8 @@ return _default_x509_signature_format(); } + // NOLINTEND(bugprone-virtual-near-miss) + /** * This is an internal library function exposed on key types. * In almost all cases applications should use wrappers in pubkey.h @@ -418,10 +430,21 @@ virtual std::vector public_value() const = 0; }; -std::string BOTAN_PUBLIC_API(2, 4) create_hex_fingerprint(const uint8_t bits[], size_t len, std::string_view hash_name); +/** +* Hex encode the data and separate them in blocks with `:` characters +*/ +std::string BOTAN_PUBLIC_API(3, 12) format_hex_fingerprint(std::span bits); + +/** +* Hash the input then format that hash using format_hex_fingerprint +*/ +std::string BOTAN_PUBLIC_API(3, 0) create_hex_fingerprint(std::span bits, std::string_view hash_name); -inline std::string create_hex_fingerprint(std::span vec, std::string_view hash_name) { - return create_hex_fingerprint(vec.data(), vec.size(), hash_name); +/** +* Old interface for create_hex_fingerprint added in 2.4 pre-span +*/ +inline std::string create_hex_fingerprint(const uint8_t bits[], size_t len, std::string_view hash_name) { + return create_hex_fingerprint({bits, len}, hash_name); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/pk_ops.cpp botan3-3.12.0+dfsg/src/lib/pubkey/pk_ops.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/pk_ops.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/pk_ops.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,11 +7,12 @@ #include +#include #include #include #include -#include -#include +#include +#include #include #include #include @@ -26,33 +27,36 @@ throw Not_Implemented("This signature scheme does not have an algorithm identifier available"); } -PK_Ops::Encryption_with_EME::Encryption_with_EME(std::string_view eme) : m_eme(EME::create(eme)) {} +PK_Ops::Encryption_with_Padding::Encryption_with_Padding(std::string_view padding) : + m_padding(EncryptionPaddingScheme::create(padding)) {} -PK_Ops::Encryption_with_EME::~Encryption_with_EME() = default; +PK_Ops::Encryption_with_Padding::~Encryption_with_Padding() = default; -size_t PK_Ops::Encryption_with_EME::max_input_bits() const { - return 8 * m_eme->maximum_input_size(max_ptext_input_bits()); +size_t PK_Ops::Encryption_with_Padding::max_input_bits() const { + return 8 * m_padding->maximum_input_size(max_ptext_input_bits()); } -std::vector PK_Ops::Encryption_with_EME::encrypt(std::span msg, RandomNumberGenerator& rng) { +std::vector PK_Ops::Encryption_with_Padding::encrypt(std::span msg, + RandomNumberGenerator& rng) { const size_t max_input_bits = max_ptext_input_bits(); const size_t max_input_bytes = (max_input_bits + 7) / 8; BOTAN_ARG_CHECK(msg.size() <= max_input_bytes, "Plaintext too large"); - secure_vector eme_output(max_input_bits); - const size_t written = m_eme->pad(eme_output, msg, max_input_bits, rng); - return raw_encrypt(std::span{eme_output}.first(written), rng); + secure_vector padded_ptext(max_input_bits); + const size_t written = m_padding->pad(padded_ptext, msg, max_input_bits, rng); + return raw_encrypt(std::span{padded_ptext}.first(written), rng); } -PK_Ops::Decryption_with_EME::Decryption_with_EME(std::string_view eme) : m_eme(EME::create(eme)) {} +PK_Ops::Decryption_with_Padding::Decryption_with_Padding(std::string_view padding) : + m_padding(EncryptionPaddingScheme::create(padding)) {} -PK_Ops::Decryption_with_EME::~Decryption_with_EME() = default; +PK_Ops::Decryption_with_Padding::~Decryption_with_Padding() = default; -secure_vector PK_Ops::Decryption_with_EME::decrypt(uint8_t& valid_mask, std::span ctext) { +secure_vector PK_Ops::Decryption_with_Padding::decrypt(uint8_t& valid_mask, std::span ctext) { const secure_vector raw = raw_decrypt(ctext); secure_vector ptext(raw.size()); - auto len = m_eme->unpad(ptext, raw); + auto len = m_padding->unpad(ptext, raw); valid_mask = CT::Mask::from_choice(len.has_value()).if_set_return(0xFF); @@ -99,7 +103,7 @@ return hash; } - SCAN_Name req(padding); + const SCAN_Name req(padding); if(req.algo_name() == "EMSA1" && req.arg_count() == 1) { if(auto hash = HashFunction::create(req.arg(0))) { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/pk_ops.h botan3-3.12.0+dfsg/src/lib/pubkey/pk_ops.h --- botan3-3.7.1+dfsg/src/lib/pubkey/pk_ops.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/pk_ops.h 2026-05-07 01:38:28.000000000 +0000 @@ -30,9 +30,6 @@ namespace Botan { class RandomNumberGenerator; -class EME; -class KDF; -class EMSA; } // namespace Botan @@ -41,7 +38,7 @@ /** * Public key encryption interface */ -class BOTAN_UNSTABLE_API Encryption { +class BOTAN_UNSTABLE_API Encryption /* NOLINT(*special-member-functions) */ { public: /** * Encrypt a message returning the ciphertext @@ -65,7 +62,7 @@ /** * Public key decryption interface */ -class BOTAN_UNSTABLE_API Decryption { +class BOTAN_UNSTABLE_API Decryption /* NOLINT(*special-member-functions) */ { public: virtual secure_vector decrypt(uint8_t& valid_mask, std::span ctext) = 0; @@ -77,7 +74,7 @@ /** * Public key signature verification interface */ -class BOTAN_UNSTABLE_API Verification { +class BOTAN_UNSTABLE_API Verification /* NOLINT(*special-member-functions) */ { public: /** * Add more data to the message currently being signed @@ -102,7 +99,7 @@ /** * Public key signature creation interface */ -class BOTAN_UNSTABLE_API Signature { +class BOTAN_UNSTABLE_API Signature /* NOLINT(*special-member-functions) */ { public: /** * Add more data to the message currently being signed @@ -139,7 +136,7 @@ /** * A generic key agreement operation (eg DH or ECDH) */ -class BOTAN_UNSTABLE_API Key_Agreement { +class BOTAN_UNSTABLE_API Key_Agreement /* NOLINT(*special-member-functions) */ { public: virtual secure_vector agree(size_t key_len, std::span other_key, @@ -153,7 +150,7 @@ /** * KEM (key encapsulation) */ -class BOTAN_UNSTABLE_API KEM_Encryption { +class BOTAN_UNSTABLE_API KEM_Encryption /* NOLINT(*special-member-functions) */ { public: virtual void kem_encrypt(std::span out_encapsulated_key, std::span out_shared_key, @@ -168,7 +165,7 @@ virtual ~KEM_Encryption() = default; }; -class BOTAN_UNSTABLE_API KEM_Decryption { +class BOTAN_UNSTABLE_API KEM_Decryption /* NOLINT(*special-member-functions) */ { public: virtual void kem_decrypt(std::span out_shared_key, std::span encapsulated_key, diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/pk_ops_impl.h botan3-3.12.0+dfsg/src/lib/pubkey/pk_ops_impl.h --- botan3-3.7.1+dfsg/src/lib/pubkey/pk_ops_impl.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/pk_ops_impl.h 2026-05-07 01:38:28.000000000 +0000 @@ -14,42 +14,44 @@ class HashFunction; class KDF; -class EME; +class EncryptionPaddingScheme; } // namespace Botan namespace Botan::PK_Ops { -class Encryption_with_EME : public Encryption { +// NOLINTBEGIN(*-special-member-functions) + +class Encryption_with_Padding : public Encryption { public: - ~Encryption_with_EME() override; + ~Encryption_with_Padding() override; size_t max_input_bits() const override; std::vector encrypt(std::span ptext, RandomNumberGenerator& rng) override; protected: - explicit Encryption_with_EME(std::string_view eme); + explicit Encryption_with_Padding(std::string_view padding); private: virtual size_t max_ptext_input_bits() const = 0; virtual std::vector raw_encrypt(std::span msg, RandomNumberGenerator& rng) = 0; - std::unique_ptr m_eme; + std::unique_ptr m_padding; }; -class Decryption_with_EME : public Decryption { +class Decryption_with_Padding : public Decryption { public: - ~Decryption_with_EME() override; + ~Decryption_with_Padding() override; secure_vector decrypt(uint8_t& valid_mask, std::span ctext) override; protected: - explicit Decryption_with_EME(std::string_view eme); + explicit Decryption_with_Padding(std::string_view padding); private: virtual secure_vector raw_decrypt(std::span ctext) = 0; - std::unique_ptr m_eme; + std::unique_ptr m_padding; }; class Verification_with_Hash : public Verification { @@ -167,6 +169,8 @@ std::unique_ptr m_kdf; }; +// NOLINTEND(*-special-member-functions) + } // namespace Botan::PK_Ops #endif diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/pkcs8.cpp botan3-3.12.0+dfsg/src/lib/pubkey/pkcs8.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/pkcs8.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/pkcs8.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -30,7 +31,11 @@ secure_vector PKCS8_extract(DataSource& source, AlgorithmIdentifier& pbe_alg_id) { secure_vector key_data; - BER_Decoder(source).start_sequence().decode(pbe_alg_id).decode(key_data, ASN1_Type::OctetString).verify_end(); + BER_Decoder(source, BER_Decoder::Limits::DER()) + .start_sequence() + .decode(pbe_alg_id) + .decode(key_data, ASN1_Type::OctetString) + .verify_end(); return key_data; } @@ -43,7 +48,8 @@ AlgorithmIdentifier& pk_alg_id, bool is_encrypted) { AlgorithmIdentifier pbe_alg_id; - secure_vector key_data, key; + secure_vector key_data; + secure_vector key; try { if(ASN1::maybe_BER(source) && !PEM_Code::matches(source)) { @@ -51,12 +57,8 @@ key_data = PKCS8_extract(source, pbe_alg_id); } else { // todo read more efficiently - while(!source.end_of_data()) { - uint8_t b; - size_t read = source.read_byte(b); - if(read) { - key_data.push_back(b); - } + while(auto b = source.read_byte()) { + key_data.push_back(*b); } } } else { @@ -97,13 +99,14 @@ key = key_data; } - BER_Decoder(key) + BER_Decoder(key, BER_Decoder::Limits::DER()) .start_sequence() .decode_and_check(0, "Unknown PKCS #8 version number") .decode(pk_alg_id) .decode(key, ASN1_Type::OctetString) .discard_remaining() - .end_cons(); + .end_cons() + .verify_end(); } catch(std::exception& e) { throw Decoding_Error("PKCS #8 private key decoding", e); } @@ -144,7 +147,7 @@ return std::make_pair("AES-256/CBC", "SHA-256"); } - SCAN_Name request(pbe_algo); + const SCAN_Name request(pbe_algo); if(request.arg_count() != 2 || (request.algo_name() != "PBE-PKCS5v20" && request.algo_name() != "PBES2")) { throw Invalid_Argument(fmt("Unsupported PBE '{}'", pbe_algo)); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/pkcs8.h botan3-3.12.0+dfsg/src/lib/pubkey/pkcs8.h --- botan3-3.7.1+dfsg/src/lib/pubkey/pkcs8.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/pkcs8.h 2026-05-07 01:38:28.000000000 +0000 @@ -93,7 +93,7 @@ * @param key the key to encode * @param rng the rng to use * @param pass the password to use for encryption -* @param pbkdf_iter number of interations to run PBKDF2 +* @param pbkdf_iter number of iterations to run PBKDF2 * @param cipher if non-empty specifies the cipher to use. CBC and GCM modes * are supported, for example "AES-128/CBC", "AES-256/GCM", "Serpent/CBC". * If empty a suitable default is chosen. diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/pqcrystals/info.txt botan3-3.12.0+dfsg/src/lib/pubkey/pqcrystals/info.txt --- botan3-3.7.1+dfsg/src/lib/pubkey/pqcrystals/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/pqcrystals/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + PQCRYSTALS -> 20240228 - + name -> "CRYSTALS" diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/pqcrystals/pqcrystals.h botan3-3.12.0+dfsg/src/lib/pubkey/pqcrystals/pqcrystals.h --- botan3-3.7.1+dfsg/src/lib/pubkey/pqcrystals/pqcrystals.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/pqcrystals/pqcrystals.h 2026-05-07 01:38:28.000000000 +0000 @@ -25,7 +25,7 @@ namespace Botan::CRYSTALS { -enum class Domain { Normal, NTT }; +enum class Domain : uint8_t { Normal, NTT }; template concept crystals_constants = @@ -75,6 +75,8 @@ /// @} protected: + Trait_Base() = default; // NOLINT(*crtp-constructor-accessibility) + /// @returns the number of polynomials in the polynomial vector @p polyvec. static constexpr size_t polys_in_polyvec(std::span polyvec) { BOTAN_DEBUG_ASSERT(polyvec.size() % N == 0); @@ -128,7 +130,7 @@ std::span u, std::span v) { clear_mem(w); - std::array t; + std::array t{}; for(size_t i = 0; i < polys_in_polyvec(u); ++i) { DerivedT::poly_pointwise_montgomery(t, poly_in_polyvec(u, i), poly_in_polyvec(v, i)); poly_add(w, w, t); @@ -232,7 +234,7 @@ */ template requires(D != OtherD) - explicit Polynomial(Polynomial&& other) noexcept : + explicit Polynomial(Polynomial&& other) noexcept : // NOLINT(*-rvalue-reference-param-not-moved) m_coeffs_storage(std::move(other.m_coeffs_storage)), m_coeffs(owns_storage() ? std::span(m_coeffs_storage) : other.m_coeffs) {} @@ -372,7 +374,8 @@ */ template requires(D != OtherD) - explicit PolynomialVector(PolynomialVector&& other) noexcept : + /* NOLINTNEXTLINE(*rvalue-reference-param-not-moved) */ explicit PolynomialVector( + PolynomialVector&& other) noexcept : m_polys_storage(std::move(other.m_polys_storage)) { BOTAN_DEBUG_ASSERT(m_polys_storage.size() % Trait::N == 0); const size_t vecsize = m_polys_storage.size() / Trait::N; @@ -395,7 +398,7 @@ } public: - PolynomialVector(size_t vecsize) : m_polys_storage(vecsize * Trait::N) { + explicit PolynomialVector(size_t vecsize) : m_polys_storage(vecsize * Trait::N) { for(size_t i = 0; i < vecsize; ++i) { m_vec.emplace_back( Polynomial(std::span{m_polys_storage}.subspan(i * Trait::N).template first())); @@ -497,7 +500,7 @@ std::vector> m_mat; public: - PolynomialMatrix(std::vector> mat) : m_mat(std::move(mat)) {} + explicit PolynomialMatrix(std::vector> mat) : m_mat(std::move(mat)) {} PolynomialMatrix(const ThisPolynomialMatrix& other) = delete; PolynomialMatrix(ThisPolynomialMatrix&& other) noexcept = default; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/pqcrystals/pqcrystals_encoding.h botan3-3.12.0+dfsg/src/lib/pubkey/pqcrystals/pqcrystals_encoding.h --- botan3-3.7.1+dfsg/src/lib/pubkey/pqcrystals/pqcrystals_encoding.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/pqcrystals/pqcrystals_encoding.h 2026-05-07 01:38:28.000000000 +0000 @@ -15,10 +15,11 @@ #include #include +#include +#include #include #include #include -#include #if defined(BOTAN_HAS_XOF) #include @@ -75,7 +76,7 @@ constexpr static size_t bits_per_pack = [] { // Ensure that the bit-packing is byte-aligned and scale it // to utilize the collector's bit-width as much as possible. - size_t smallest_aligned_pack = std::lcm(bits_per_coeff, size_t(8)); + const size_t smallest_aligned_pack = std::lcm(bits_per_coeff, size_t(8)); return (smallest_aligned_pack < bits_in_collector) ? (bits_in_collector / smallest_aligned_pack) * smallest_aligned_pack : smallest_aligned_pack; @@ -107,7 +108,7 @@ * * Note that this bit-packing algorithm is inefficient if the bit-length of the * coefficients is a multiple of 8. In that case, a byte-level encoding (that - * might need to take endianess into account) would be more efficient. However, + * might need to take endianness into account) would be more efficient. However, * neither Kyber nor Dilithium instantiate bit-packings with such a value range. * * @tparam range the upper bound of the coefficient range. diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/pqcrystals/pqcrystals_helpers.h botan3-3.12.0+dfsg/src/lib/pubkey/pqcrystals/pqcrystals_helpers.h --- botan3-3.7.1+dfsg/src/lib/pubkey/pqcrystals/pqcrystals_helpers.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/pqcrystals/pqcrystals_helpers.h 2026-05-07 01:38:28.000000000 +0000 @@ -40,7 +40,7 @@ // clang-format on template - requires(size_t(sizeof(T)) <= 4) + requires(sizeof(T) <= 4) consteval T montgomery_R(T q) { using T_unsigned = std::make_unsigned_t; using T2 = next_longer_uint_t; @@ -48,7 +48,7 @@ } template - requires(size_t(sizeof(T)) <= 4) + requires(sizeof(T) <= 4) consteval T montgomery_R2(T q) { using T2 = next_longer_int_t; return (static_cast(montgomery_R(q)) * static_cast(montgomery_R(q))) % q; @@ -71,7 +71,10 @@ std::swap(a, b); } - T u1 = 0, v1 = 1, u2 = 1, v2 = 0; + T u1 = 0; + T v1 = 1; + T u2 = 1; + T v2 = 0; if(a != b) { while(a != 0) { @@ -97,7 +100,7 @@ constexpr auto bitlen(size_t x) { return ceil_log2(x + 1); -}; +} /** * Precompute the zeta-values for the NTT. Note that the pre-computed values @@ -158,7 +161,7 @@ } template - constexpr static bool default_predicate(T) { + constexpr static bool default_predicate(T /*v*/) { return true; } @@ -188,8 +191,8 @@ typename PredicateFnT = decltype(default_predicate>)> requires std::invocable> && std::invocable> - constexpr auto next(MapFnT&& transformer = default_transformer, - PredicateFnT&& predicate = default_predicate>) { + constexpr auto next(const MapFnT& transformer = default_transformer, + const PredicateFnT& predicate = default_predicate>) { while(true) { auto output = transformer(take()); if(predicate(output)) { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/pubkey.cpp botan3-3.12.0+dfsg/src/lib/pubkey/pubkey.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/pubkey.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/pubkey.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,14 +9,12 @@ #include #include #include -#include #include -#include #include +#include #include #include -#include -#include +#include namespace Botan { @@ -110,8 +108,8 @@ return m_op->ciphertext_length(ptext_len); } -std::vector PK_Encryptor_EME::enc(const uint8_t in[], size_t length, RandomNumberGenerator& rng) const { - return m_op->encrypt(std::span{in, length}, rng); +std::vector PK_Encryptor_EME::enc(const uint8_t ptext[], size_t len, RandomNumberGenerator& rng) const { + return m_op->encrypt(std::span{ptext, len}, rng); } size_t PK_Encryptor_EME::maximum_input_size() const { @@ -235,33 +233,32 @@ const uint8_t peer_key[], size_t peer_key_len, std::string_view salt) const { - return this->derive_key(key_len, peer_key, peer_key_len, cast_char_ptr_to_uint8(salt.data()), salt.length()); + return this->derive_key(key_len, {peer_key, peer_key_len}, as_span_of_bytes(salt)); } SymmetricKey PK_Key_Agreement::derive_key(size_t key_len, const std::span peer_key, std::string_view salt) const { - return this->derive_key( - key_len, peer_key.data(), peer_key.size(), cast_char_ptr_to_uint8(salt.data()), salt.length()); + return this->derive_key(key_len, peer_key, as_span_of_bytes(salt)); } -SymmetricKey PK_Key_Agreement::derive_key( - size_t key_len, const uint8_t peer_key[], size_t peer_key_len, const uint8_t salt[], size_t salt_len) const { - return SymmetricKey(m_op->agree(key_len, {peer_key, peer_key_len}, {salt, salt_len})); +SymmetricKey PK_Key_Agreement::derive_key(size_t key_len, + std::span peer_key, + std::span salt) const { + return SymmetricKey(m_op->agree(key_len, peer_key, salt)); } PK_Signer::PK_Signer(const Private_Key& key, RandomNumberGenerator& rng, - std::string_view emsa, + std::string_view padding, Signature_Format format, std::string_view provider) : - m_sig_format(format) { + m_sig_format(format), m_sig_element_size(key._signature_element_size_for_DER_encoding()) { if(m_sig_format == Signature_Format::DerSequence) { - m_sig_element_size = key._signature_element_size_for_DER_encoding(); BOTAN_ARG_CHECK(m_sig_element_size.has_value(), "This key does not support DER signatures"); } - m_op = key.create_signature_op(rng, emsa, provider); + m_op = key.create_signature_op(rng, padding, provider); if(!m_op) { throw Invalid_Argument(fmt("Key type {} does not support signature generation", key.algo_name())); } @@ -281,7 +278,7 @@ PK_Signer& PK_Signer::operator=(PK_Signer&&) noexcept = default; void PK_Signer::update(std::string_view in) { - this->update(cast_char_ptr_to_uint8(in.data()), in.size()); + this->update(as_span_of_bytes(in)); } void PK_Signer::update(const uint8_t in[], size_t length) { @@ -313,9 +310,9 @@ if(m_sig_format == Signature_Format::Standard) { return m_op->signature_length(); } else if(m_sig_format == Signature_Format::DerSequence) { - size_t sig_len = m_op->signature_length(); + const size_t sig_len = m_op->signature_length(); - size_t der_overhead = [sig_len]() { + const size_t der_overhead = [sig_len]() { /* This was computed by DER encoding of some maximal value signatures (since DER is variable length) @@ -367,10 +364,10 @@ } PK_Verifier::PK_Verifier(const Public_Key& key, - std::string_view emsa, + std::string_view padding, Signature_Format format, std::string_view provider) { - m_op = key.create_verification_op(emsa, provider); + m_op = key.create_verification_op(padding, provider); if(!m_op) { throw Invalid_Argument(fmt("Key type {} does not support signature verification", key.algo_name())); } @@ -417,7 +414,7 @@ } void PK_Verifier::update(std::string_view in) { - this->update(cast_char_ptr_to_uint8(in.data()), in.size()); + this->update(as_span_of_bytes(in)); } void PK_Verifier::update(const uint8_t in[], size_t length) { @@ -426,32 +423,27 @@ namespace { -std::vector decode_der_signature(const uint8_t sig[], size_t length, size_t sig_parts, size_t sig_part_size) { - std::vector real_sig; - BER_Decoder decoder(sig, length); - BER_Decoder ber_sig = decoder.start_sequence(); +std::vector decode_der_signature_pair(std::span der_sig, size_t sig_part_size) { + BOTAN_ASSERT_NOMSG(sig_part_size > 0); - BOTAN_ASSERT_NOMSG(sig_parts != 0 && sig_part_size != 0); + BigInt r; + BigInt s; - size_t count = 0; + // TODO should be able to just get the integer bytes directly from + // BER_Decoder without using BigInt here + BER_Decoder(der_sig, BER_Decoder::Limits::DER()).start_sequence().decode(r).decode(s).end_cons().verify_end(); - while(ber_sig.more_items()) { - BigInt sig_part; - ber_sig.decode(sig_part); - real_sig += sig_part.serialize(sig_part_size); - ++count; - } + const bool invalid_r = r.is_negative() || r.bytes() > sig_part_size; + const bool invalid_s = s.is_negative() || s.bytes() > sig_part_size; - if(count != sig_parts) { - throw Decoding_Error("PK_Verifier: signature size invalid"); + if(invalid_r || invalid_s) { + throw Decoding_Error("Invalid DER encoding of signature"); } - const std::vector reencoded = der_encode_signature(real_sig, sig_parts, sig_part_size); - - if(reencoded.size() != length || CT::is_equal(reencoded.data(), sig, reencoded.size()).as_bool() == false) { - throw Decoding_Error("PK_Verifier: signature is not the canonical DER encoding"); - } - return real_sig; + std::vector sig(2 * sig_part_size); + r.serialize_to(std::span{sig}.first(sig_part_size)); + s.serialize_to(std::span{sig}.last(sig_part_size)); + return sig; } } // namespace @@ -467,11 +459,13 @@ BOTAN_ASSERT_NOMSG(m_sig_element_size.has_value()); try { - real_sig = decode_der_signature(sig, length, 2, m_sig_element_size.value()); + real_sig = decode_der_signature_pair({sig, length}, m_sig_element_size.value()); decoding_success = true; - } catch(Decoding_Error&) {} + } catch(...) {} - bool accept = m_op->is_valid_signature(real_sig); + // It is critical that is_valid_signature is called even if DER decoding failed, since + // that is what resets the internal state (message hashes, etc) + const bool accept = m_op->is_valid_signature(real_sig); return accept && decoding_success; } else { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/pubkey.h botan3-3.12.0+dfsg/src/lib/pubkey/pubkey.h --- botan3-3.7.1+dfsg/src/lib/pubkey/pubkey.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/pubkey.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,7 +8,6 @@ #ifndef BOTAN_PUBKEY_H_ #define BOTAN_PUBKEY_H_ -#include #include #include #include @@ -420,12 +419,22 @@ * Perform Key Agreement Operation * @param key_len the desired key output size (ignored if "Raw" KDF is used) * @param peer_key the other parties key + * @param salt extra derivation salt + */ + SymmetricKey derive_key(size_t key_len, std::span peer_key, std::span salt) const; + + /** + * Perform Key Agreement Operation + * @param key_len the desired key output size (ignored if "Raw" KDF is used) + * @param peer_key the other parties key * @param peer_key_len the length of peer_key in bytes * @param salt extra derivation salt * @param salt_len the length of salt in bytes */ SymmetricKey derive_key( - size_t key_len, const uint8_t peer_key[], size_t peer_key_len, const uint8_t salt[], size_t salt_len) const; + size_t key_len, const uint8_t peer_key[], size_t peer_key_len, const uint8_t salt[], size_t salt_len) const { + return this->derive_key(key_len, {peer_key, peer_key_len}, {salt, salt_len}); + } /** * Perform Key Agreement Operation @@ -507,13 +516,15 @@ size_t ciphertext_length(size_t ptext_len) const override; private: - std::vector enc(const uint8_t[], size_t, RandomNumberGenerator& rng) const override; + std::vector enc(const uint8_t ptext[], size_t len, RandomNumberGenerator& rng) const override; std::unique_ptr m_op; }; /** -* Decryption with an MR algorithm and an EME. +* Decryption with a padding scheme. +* +* This is typically only used with RSA */ class BOTAN_PUBLIC_API(2, 0) PK_Decryptor_EME final : public PK_Decryptor { public: @@ -521,12 +532,12 @@ * Construct an instance. * @param key the key to use inside the decryptor * @param rng the random generator to use - * @param eme the EME to use + * @param padding the padding scheme to use * @param provider the provider to use */ PK_Decryptor_EME(const Private_Key& key, RandomNumberGenerator& rng, - std::string_view eme, + std::string_view padding, std::string_view provider = ""); size_t plaintext_length(size_t ptext_len) const override; @@ -566,7 +577,8 @@ /** * @returns the pair (encapsulated key, key) extracted from @p kem */ - static std::pair, secure_vector> destructure(KEM_Encapsulation&& kem) { + static std::pair, secure_vector> destructure( + KEM_Encapsulation&& kem) /* NOLINT(*param-not-moved*) */ { return std::make_pair(std::exchange(kem.m_encapsulated_shared_key, {}), std::exchange(kem.m_shared_key, {})); } @@ -592,7 +604,9 @@ * @param kem_param additional KEM parameters * @param provider the provider to use */ - PK_KEM_Encryptor(const Public_Key& key, std::string_view kem_param = "", std::string_view provider = ""); + BOTAN_FUTURE_EXPLICIT PK_KEM_Encryptor(const Public_Key& key, + std::string_view kem_param = "", + std::string_view provider = ""); /** * Construct an instance. @@ -703,7 +717,7 @@ this->encrypt(out_encapsulated_key, out_shared_key, rng, desired_shared_key_len, {salt, salt_len}); } - BOTAN_DEPRECATED("use overload where rng comes after the out-paramters") + BOTAN_DEPRECATED("use overload where rng comes after the out-parameters") void encrypt(secure_vector& out_encapsulated_key, secure_vector& out_shared_key, size_t desired_shared_key_len, diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/rfc6979/info.txt botan3-3.12.0+dfsg/src/lib/pubkey/rfc6979/info.txt --- botan3-3.7.1+dfsg/src/lib/pubkey/rfc6979/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/rfc6979/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + RFC6979_GENERATOR -> 20140321 - + name -> "RFC 6979" diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/rfc6979/rfc6979.cpp botan3-3.12.0+dfsg/src/lib/pubkey/rfc6979/rfc6979.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/rfc6979/rfc6979.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/rfc6979/rfc6979.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,12 +7,16 @@ #include +#include #include #include #include namespace Botan { +RFC6979_Nonce_Generator::RFC6979_Nonce_Generator(RFC6979_Nonce_Generator&& other) noexcept = default; +RFC6979_Nonce_Generator& RFC6979_Nonce_Generator::operator=(RFC6979_Nonce_Generator&& other) noexcept = default; + RFC6979_Nonce_Generator::~RFC6979_Nonce_Generator() = default; RFC6979_Nonce_Generator::RFC6979_Nonce_Generator(std::string_view hash, size_t order_bits, const BigInt& x) : @@ -34,14 +38,18 @@ BigInt k; - do { + for(;;) { m_hmac_drbg->randomize(m_rng_out); k._assign_from_bytes(m_rng_out); if(shift > 0) { k >>= shift; } - } while(k == 0 || k >= order); + + if(k > 0 && k < order) { + break; + } + } return k; } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/rfc6979/rfc6979.h botan3-3.12.0+dfsg/src/lib/pubkey/rfc6979/rfc6979.h --- botan3-3.7.1+dfsg/src/lib/pubkey/rfc6979/rfc6979.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/rfc6979/rfc6979.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,7 +10,6 @@ #include #include -#include #include #if defined(BOTAN_HAS_ECC_GROUP) @@ -33,6 +32,11 @@ EC_Scalar nonce_for(const EC_Group& group, const EC_Scalar& m); #endif + RFC6979_Nonce_Generator(const RFC6979_Nonce_Generator& other) = delete; + RFC6979_Nonce_Generator& operator=(const RFC6979_Nonce_Generator& other) = delete; + + RFC6979_Nonce_Generator(RFC6979_Nonce_Generator&& other) noexcept; + RFC6979_Nonce_Generator& operator=(RFC6979_Nonce_Generator&& other) noexcept; ~RFC6979_Nonce_Generator(); private: diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/rsa/info.txt botan3-3.12.0+dfsg/src/lib/pubkey/rsa/info.txt --- botan3-3.7.1+dfsg/src/lib/pubkey/rsa/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/rsa/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -10,7 +10,8 @@ blinding keypair numbertheory -pk_pad +sig_padding +enc_padding diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/rsa/rsa.cpp botan3-3.12.0+dfsg/src/lib/pubkey/rsa/rsa.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/rsa/rsa.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/rsa/rsa.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,17 +11,19 @@ #include #include #include -#include +#include #include #include -#include #include #include #include #include #include +#include #include #include +#include +#include #include #if defined(BOTAN_HAS_THREAD_UTILS) @@ -35,8 +37,8 @@ RSA_Public_Data(BigInt&& n, BigInt&& e) : m_n(std::move(n)), m_e(std::move(e)), - m_mod_n(Modular_Reducer::for_public_modulus(m_n)), - m_monty_n(std::make_shared(m_n, m_mod_n)), + m_mod_n(Barrett_Reduction::for_public_modulus(m_n)), + m_monty_n(m_n, m_mod_n), m_public_modulus_bits(m_n.bits()), m_public_modulus_bytes(m_n.bytes()) {} @@ -54,15 +56,15 @@ size_t public_modulus_bytes() const { return m_public_modulus_bytes; } - const std::shared_ptr& monty_n() const { return m_monty_n; } + const Montgomery_Params& monty_n() const { return m_monty_n; } - const Modular_Reducer& reducer_mod_n() const { return m_mod_n; } + const Barrett_Reduction& reducer_mod_n() const { return m_mod_n; } private: BigInt m_n; BigInt m_e; - Modular_Reducer m_mod_n; - std::shared_ptr m_monty_n; + Barrett_Reduction m_mod_n; + const Montgomery_Params m_monty_n; size_t m_public_modulus_bits; size_t m_public_modulus_bytes; }; @@ -76,8 +78,8 @@ m_d1(std::move(d1)), m_d2(std::move(d2)), m_c(std::move(c)), - m_monty_p(std::make_shared(m_p)), - m_monty_q(std::make_shared(m_q)), + m_monty_p(m_p), + m_monty_q(m_q), m_c_monty(m_monty_p, m_c), m_p_bits(m_p.bits()), m_q_bits(m_q.bits()) {} @@ -92,13 +94,17 @@ const BigInt& get_d2() const { return m_d2; } + BigInt blinded_d1(const BigInt& m) const { return m_d1 + m * (m_p - 1); } + + BigInt blinded_d2(const BigInt& m) const { return m_d2 + m * (m_q - 1); } + const BigInt& get_c() const { return m_c; } const Montgomery_Int& get_c_monty() const { return m_c_monty; } - const std::shared_ptr& monty_p() const { return m_monty_p; } + const Montgomery_Params& monty_p() const { return m_monty_p; } - const std::shared_ptr& monty_q() const { return m_monty_q; } + const Montgomery_Params& monty_q() const { return m_monty_q; } size_t p_bits() const { return m_p_bits; } @@ -114,8 +120,8 @@ BigInt m_d2; BigInt m_c; - std::shared_ptr m_monty_p; - std::shared_ptr m_monty_q; + const Montgomery_Params m_monty_p; + const Montgomery_Params m_monty_q; Montgomery_Int m_c_monty; size_t m_p_bits; size_t m_q_bits; @@ -136,7 +142,7 @@ } std::unique_ptr RSA_PublicKey::generate_another(RandomNumberGenerator& rng) const { - return std::make_unique(rng, m_public->public_modulus_bits(), m_public->get_e().to_u32bit()); + return std::make_unique(rng, m_public->public_modulus_bits(), 65537); } const BigInt& RSA_PublicKey::get_n() const { @@ -148,15 +154,19 @@ } void RSA_PublicKey::init(BigInt&& n, BigInt&& e) { - if(n.is_negative() || n.is_even() || n.bits() < 5 /* n >= 3*5 */ || e.is_negative() || e.is_even()) { - throw Decoding_Error("Invalid RSA public key parameters"); + if(n.signum() <= 0 || n.is_even() || n.bits() < 384 || n.bits() > 16384) { + throw Decoding_Error("Invalid RSA public key modulus"); + } + if(e.is_even() || e <= 1 || e >= n || e.bits() > 256) { + throw Decoding_Error("Invalid RSA public key exponent"); } m_public = std::make_shared(std::move(n), std::move(e)); } RSA_PublicKey::RSA_PublicKey(const AlgorithmIdentifier& /*unused*/, std::span key_bits) { - BigInt n, e; - BER_Decoder(key_bits).start_sequence().decode(n).decode(e).end_cons(); + BigInt n; + BigInt e; + BER_Decoder(key_bits, BER_Decoder::Limits::DER()).start_sequence().decode(n).decode(e).end_cons().verify_end(); init(std::move(n), std::move(e)); } @@ -251,14 +261,27 @@ } void RSA_PrivateKey::init(BigInt&& d, BigInt&& p, BigInt&& q, BigInt&& d1, BigInt&& d2, BigInt&& c) { + if(d < 2 || p < 3 || q < 3 || p == q) { + throw Decoding_Error("Invalid RSA private key parameters"); + } + if(p * q != get_n()) { + throw Decoding_Error("Invalid RSA private key: p * q != n"); + } m_private = std::make_shared( std::move(d), std::move(p), std::move(q), std::move(d1), std::move(d2), std::move(c)); } RSA_PrivateKey::RSA_PrivateKey(const AlgorithmIdentifier& /*unused*/, std::span key_bits) { - BigInt n, e, d, p, q, d1, d2, c; + BigInt n; + BigInt e; + BigInt d; + BigInt p; + BigInt q; + BigInt d1; + BigInt d2; + BigInt c; - BER_Decoder(key_bits) + BER_Decoder(key_bits, BER_Decoder::Limits::DER()) .start_sequence() .decode_and_check(0, "Unknown PKCS #1 key format version") .decode(n) @@ -269,7 +292,8 @@ .decode(d1) .decode(d2) .decode(c) - .end_cons(); + .end_cons() + .verify_end(); RSA_PublicKey::init(std::move(n), std::move(e)); @@ -310,8 +334,18 @@ * Create a RSA private key */ RSA_PrivateKey::RSA_PrivateKey(RandomNumberGenerator& rng, size_t bits, size_t exp) { - if(bits < 1024) { - throw Invalid_Argument(fmt("Cannot create an RSA key only {} bits long", bits)); + constexpr size_t MIN_RSA_BITS = 1024; + constexpr size_t MAX_RSA_BITS = 16384; + constexpr size_t MOD_RSA_BITS = 8; + + if(bits < MIN_RSA_BITS) { + throw Invalid_Argument(fmt("Cannot create an RSA key of {} bits: must be at least {} bits", bits, MIN_RSA_BITS)); + } else if(bits > MAX_RSA_BITS) { + throw Invalid_Argument( + fmt("Cannot create an RSA key of {} bits: must be no more than {} bits", bits, MAX_RSA_BITS)); + } else if(bits % MOD_RSA_BITS != 0) { + throw Invalid_Argument( + fmt("Cannot create an RSA key of {} bits: must be a multiple of {} bits", bits, MOD_RSA_BITS)); } if(exp < 3 || exp % 2 == 0) { @@ -321,7 +355,9 @@ const size_t p_bits = (bits + 1) / 2; const size_t q_bits = bits - p_bits; - BigInt p, q, n; + BigInt p; + BigInt q; + BigInt n; BigInt e = BigInt::from_u64(exp); for(size_t attempt = 0;; ++attempt) { @@ -444,6 +480,58 @@ namespace { +/* +* To recover the final value from the CRT representation (j1,j2) +* we use Garner's algorithm: +* c = q^-1 mod p (this is precomputed) +* h = c*(j1-j2) mod p +* r = h*q + j2 +*/ +BigInt crt_recombine(const Montgomery_Int& j1, + const Montgomery_Int& j2_p, + const BigInt& j2, + const Montgomery_Int& c_monty, + const BigInt& p, + const BigInt& q) { + // We skip CRT entirely if the primes are not balanced (same bitlength) so q is also of this size + const size_t p_words = p.sig_words(); + BOTAN_ASSERT_NOMSG(p_words == q.sig_words()); + + const size_t n_words = 2 * p_words; + + // Ensure sufficient storage + BOTAN_ASSERT_NOMSG(j1.repr().size() >= p_words); + BOTAN_ASSERT_NOMSG(j2_p.repr().size() >= p_words); + BOTAN_ASSERT_NOMSG(j2.size() >= p_words); + + /* + * Compute h = (j1 - j2) * c mod p + * + * This doesn't quite match up with the "Smooth-CRT" proposal; there we would + * multiply by a precomputed c * R2, which would have the effect of both + * multiplying by c and immediately converting from Montgomery to standard form. + */ + secure_vector ws(2 * p_words); + + const Montgomery_Int h_monty = (j1 - j2_p).mul(c_monty, ws); + + const BigInt h = h_monty.value(); + // Montgomery_Int always returns values sized to the modulus + BOTAN_ASSERT_NOMSG(h.size() >= p_words); + BOTAN_DEBUG_ASSERT(h.sig_words() <= p_words); + + // Compute r = h * q + secure_vector r(2 * p_words); + + bigint_mul(r.data(), r.size(), h._data(), h.size(), p_words, q._data(), q.size(), p_words, ws.data(), ws.size()); + + // r += j2 + const word carry = bigint_add2(r.data(), n_words, j2._data(), p_words); + BOTAN_ASSERT_NOMSG(carry == 0); // should not be possible since it would imply r > the public modulus + + return BigInt::_from_words(r); +} + /** * RSA private (decrypt/sign) operation */ @@ -470,8 +558,8 @@ throw Decoding_Error("RSA input is too long for this key"); } const BigInt input_bn(input.data(), input.size()); - if(input_bn >= m_public->get_n()) { - throw Decoding_Error("RSA input is too large for this key"); + if(input_bn.is_zero() || input_bn >= m_public->get_n()) { + throw Decoding_Error("RSA input is not in the valid range"); } // TODO: This should be a function on blinder // BigInt Blinder::run_blinded_function(std::function fn, const BigInt& input); @@ -504,14 +592,14 @@ #if defined(BOTAN_RSA_USE_ASYNC) /* * Precompute m.sig_words in the main thread before calling async. Otherwise - * the two threads race (during Modular_Reducer::reduce) and while the output + * the two threads race (during Barrett_Reduction::reduce) and while the output * is correct in both threads, helgrind warns. */ m.sig_words(); auto future_j1 = Thread_Pool::global_instance().run([this, &m, &d1_mask]() { #endif - const BigInt masked_d1 = m_private->get_d1() + (d1_mask * (m_private->get_p() - 1)); + const BigInt masked_d1 = m_private->blinded_d1(d1_mask); auto powm_d1_p = monty_precompute(Montgomery_Int::from_wide_int(m_private->monty_p(), m), powm_window); auto j1 = monty_execute(*powm_d1_p, masked_d1, m_max_d1_bits); @@ -521,7 +609,7 @@ #endif const BigInt d2_mask(m_blinder.rng(), m_blinding_bits); - const BigInt masked_d2 = m_private->get_d2() + (d2_mask * (m_private->get_q() - 1)); + const BigInt masked_d2 = m_private->blinded_d2(d2_mask); auto powm_d2_q = monty_precompute(Montgomery_Int::from_wide_int(m_private->monty_q(), m), powm_window); const auto j2 = monty_execute(*powm_d2_q, masked_d2, m_max_d2_bits).value(); @@ -529,23 +617,10 @@ auto j1 = future_j1.get(); #endif - /* - * To recover the final value from the CRT representation (j1,j2) - * we use Garner's algorithm: - * c = q^-1 mod p (this is precomputed) - * h = c*(j1-j2) mod p - * m = j2 + h*q - */ - + // Reduce j2 modulo p const auto j2_p = Montgomery_Int::from_wide_int(m_private->monty_p(), j2); - /** - * This doesn't quite match up with the "Smooth-CRT" proposal; there we - * would multiply by c * R2 so would have the effect of both multiplying - * by c and immediately converting from Montgomery to standard form. - */ - j1 = (j1 - j2_p) * m_private->get_c_monty(); - return j1.value() * m_private->get_q() + j2; + return crt_recombine(j1, j2_p, j2, m_private->get_c_monty(), m_private->get_p(), m_private->get_q()); } std::shared_ptr m_public; @@ -561,12 +636,12 @@ class RSA_Signature_Operation final : public PK_Ops::Signature, private RSA_Private_Operation { public: - void update(std::span msg) override { m_emsa->update(msg.data(), msg.size()); } + void update(std::span msg) override { m_padding->update(msg.data(), msg.size()); } std::vector sign(RandomNumberGenerator& rng) override { const size_t max_input_bits = public_modulus_bits() - 1; - const auto msg = m_emsa->raw_data(); - const auto padded = m_emsa->encoding_of(msg, max_input_bits, rng); + const auto msg = m_padding->raw_data(); + const auto padded = m_padding->encoding_of(msg, max_input_bits, rng); std::vector out(public_modulus_bytes()); raw_op(out, padded); @@ -577,37 +652,37 @@ AlgorithmIdentifier algorithm_identifier() const override; - std::string hash_function() const override { return m_emsa->hash_function(); } + std::string hash_function() const override { return m_padding->hash_function(); } RSA_Signature_Operation(const RSA_PrivateKey& rsa, std::string_view padding, RandomNumberGenerator& rng) : - RSA_Private_Operation(rsa, rng), m_emsa(EMSA::create_or_throw(padding)) {} + RSA_Private_Operation(rsa, rng), m_padding(SignaturePaddingScheme::create_or_throw(padding)) {} private: - std::unique_ptr m_emsa; + std::unique_ptr m_padding; }; AlgorithmIdentifier RSA_Signature_Operation::algorithm_identifier() const { - const std::string emsa_name = m_emsa->name(); + const std::string padding_name = m_padding->name(); try { - const std::string full_name = "RSA/" + emsa_name; + const std::string full_name = "RSA/" + padding_name; const OID oid = OID::from_string(full_name); return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_EMPTY_PARAM); } catch(Lookup_Error&) {} - if(emsa_name.starts_with("PSS(")) { - auto parameters = PSS_Params::from_emsa_name(m_emsa->name()).serialize(); + if(padding_name.starts_with("PSS(")) { + auto parameters = PSS_Params::from_padding_name(m_padding->name()).serialize(); return AlgorithmIdentifier("RSA/PSS", parameters); } - throw Invalid_Argument(fmt("Signatures using RSA/{} are not supported", emsa_name)); + throw Invalid_Argument(fmt("Signatures using RSA/{} are not supported", padding_name)); } -class RSA_Decryption_Operation final : public PK_Ops::Decryption_with_EME, +class RSA_Decryption_Operation final : public PK_Ops::Decryption_with_Padding, private RSA_Private_Operation { public: - RSA_Decryption_Operation(const RSA_PrivateKey& rsa, std::string_view eme, RandomNumberGenerator& rng) : - PK_Ops::Decryption_with_EME(eme), RSA_Private_Operation(rsa, rng) {} + RSA_Decryption_Operation(const RSA_PrivateKey& rsa, std::string_view padding, RandomNumberGenerator& rng) : + PK_Ops::Decryption_with_Padding(padding), RSA_Private_Operation(rsa, rng) {} size_t plaintext_length(size_t /*ctext_len*/) const override { return public_modulus_bytes(); } @@ -659,18 +734,18 @@ std::shared_ptr m_public; }; -class RSA_Encryption_Operation final : public PK_Ops::Encryption_with_EME, +class RSA_Encryption_Operation final : public PK_Ops::Encryption_with_Padding, private RSA_Public_Operation { public: - RSA_Encryption_Operation(const RSA_PublicKey& rsa, std::string_view eme) : - PK_Ops::Encryption_with_EME(eme), RSA_Public_Operation(rsa) {} + RSA_Encryption_Operation(const RSA_PublicKey& rsa, std::string_view padding) : + PK_Ops::Encryption_with_Padding(padding), RSA_Public_Operation(rsa) {} size_t ciphertext_length(size_t /*ptext_len*/) const override { return public_modulus_bytes(); } size_t max_ptext_input_bits() const override { return public_modulus_bits() - 1; } std::vector raw_encrypt(std::span input, RandomNumberGenerator& /*rng*/) override { - BigInt input_bn(input); + const BigInt input_bn(input); return public_op(input_bn).serialize(public_modulus_bytes()); } }; @@ -678,29 +753,29 @@ class RSA_Verify_Operation final : public PK_Ops::Verification, private RSA_Public_Operation { public: - void update(std::span msg) override { m_emsa->update(msg.data(), msg.size()); } + void update(std::span msg) override { m_padding->update(msg.data(), msg.size()); } bool is_valid_signature(std::span sig) override { - const auto msg = m_emsa->raw_data(); + const auto msg = m_padding->raw_data(); const auto message_repr = recover_message_repr(sig.data(), sig.size()); - return m_emsa->verify(message_repr, msg, public_modulus_bits() - 1); + return m_padding->verify(message_repr, msg, public_modulus_bits() - 1); } RSA_Verify_Operation(const RSA_PublicKey& rsa, std::string_view padding) : - RSA_Public_Operation(rsa), m_emsa(EMSA::create_or_throw(padding)) {} + RSA_Public_Operation(rsa), m_padding(SignaturePaddingScheme::create_or_throw(padding)) {} - std::string hash_function() const override { return m_emsa->hash_function(); } + std::string hash_function() const override { return m_padding->hash_function(); } private: std::vector recover_message_repr(const uint8_t input[], size_t input_len) { if(input_len > public_modulus_bytes()) { throw Decoding_Error("RSA signature too large to be valid for this key"); } - BigInt input_bn(input, input_len); + const BigInt input_bn(input, input_len); return public_op(input_bn).serialize(); } - std::unique_ptr m_emsa; + std::unique_ptr m_padding; }; class RSA_KEM_Encryption_Operation final : public PK_Ops::KEM_Encryption_with_KDF, @@ -717,7 +792,7 @@ void raw_kem_encrypt(std::span out_encapsulated_key, std::span raw_shared_key, RandomNumberGenerator& rng) override { - const BigInt r = BigInt::random_integer(rng, 1, get_n()); + const BigInt r = BigInt::random_integer(rng, BigInt::one(), get_n()); const BigInt c = public_op(r); c.serialize_to(out_encapsulated_key); @@ -764,13 +839,19 @@ std::string padding = sig_info[1]; + if(padding != "PSS") { + if(!alg_id.parameters_are_null_or_empty()) { + throw Decoding_Error("Non-PSS RSA signature algorithm OID has unexpected parameters"); + } + } + if(padding == "PSS") { // "MUST contain RSASSA-PSS-params" if(alg_id.parameters().empty()) { throw Decoding_Error("PSS params must be provided"); } - PSS_Params pss_params(alg_id.parameters()); + const PSS_Params pss_params(alg_id.parameters()); // hash_algo must be SHA1, SHA2-224, SHA2-256, SHA2-384 or SHA2-512 // We also support SHA-3 (is also supported by e.g. OpenSSL and bouncycastle) diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/rsa/rsa.h botan3-3.12.0+dfsg/src/lib/pubkey/rsa/rsa.h --- botan3-3.7.1+dfsg/src/lib/pubkey/rsa/rsa.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/rsa/rsa.h 2026-05-07 01:38:28.000000000 +0000 @@ -40,7 +40,7 @@ std::string algo_name() const override { return "RSA"; } - bool check_key(RandomNumberGenerator& rng, bool) const override; + bool check_key(RandomNumberGenerator& rng, bool strong) const override; AlgorithmIdentifier algorithm_identifier() const override; @@ -88,7 +88,7 @@ void init(BigInt&& n, BigInt&& e); - std::shared_ptr m_public; + std::shared_ptr m_public; // NOLINT(*non-private-member-variable*) }; /** @@ -98,8 +98,8 @@ BOTAN_DIAGNOSTIC_PUSH BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE -class BOTAN_PUBLIC_API(2, 0) RSA_PrivateKey final : public Private_Key, - public RSA_PublicKey { +class BOTAN_PUBLIC_API(2, 0) RSA_PrivateKey final : public virtual Private_Key, + public virtual RSA_PublicKey { public: /** * Load a private key. @@ -135,7 +135,7 @@ std::unique_ptr public_key() const override; - bool check_key(RandomNumberGenerator& rng, bool) const override; + bool check_key(RandomNumberGenerator& rng, bool strong) const override; const BigInt& get_int_field(std::string_view field) const override; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/sm2/sm2.cpp botan3-3.12.0+dfsg/src/lib/pubkey/sm2/sm2.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/sm2/sm2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/sm2/sm2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,10 +8,10 @@ #include +#include #include #include #include -#include #include namespace Botan { @@ -20,6 +20,10 @@ return "SM2"; } +std::optional SM2_PublicKey::_signature_element_size_for_DER_encoding() const { + return domain().get_order_bytes(); +} + std::unique_ptr SM2_PrivateKey::public_key() const { return std::make_unique(domain(), _public_ec_point()); } @@ -50,21 +54,31 @@ m_da_inv((this->_private_key() + EC_Scalar::one(domain())).invert()), m_da_inv_legacy(m_da_inv.to_bigint()) {} -SM2_PrivateKey::SM2_PrivateKey(EC_Group group, EC_Scalar x) : - EC_PrivateKey(std::move(group), std::move(x)), +SM2_PrivateKey::SM2_PrivateKey(const EC_Group& group, const EC_Scalar& x) : + EC_PrivateKey(group, x), m_da_inv((this->_private_key() + EC_Scalar::one(domain())).invert()), m_da_inv_legacy(m_da_inv.to_bigint()) {} -SM2_PrivateKey::SM2_PrivateKey(RandomNumberGenerator& rng, EC_Group group) : - EC_PrivateKey(rng, std::move(group)), +SM2_PrivateKey::SM2_PrivateKey(RandomNumberGenerator& rng, const EC_Group& group) : + EC_PrivateKey(rng, group), m_da_inv((this->_private_key() + EC_Scalar::one(domain())).invert()), m_da_inv_legacy(m_da_inv.to_bigint()) {} -SM2_PrivateKey::SM2_PrivateKey(RandomNumberGenerator& rng, EC_Group group, const BigInt& x) : - EC_PrivateKey(rng, std::move(group), x), +SM2_PrivateKey::SM2_PrivateKey(RandomNumberGenerator& rng, const EC_Group& group, const BigInt& x) : + EC_PrivateKey(rng, group, x), m_da_inv((this->_private_key() + EC_Scalar::one(domain())).invert()), m_da_inv_legacy(m_da_inv.to_bigint()) {} +#if defined(BOTAN_HAS_LEGACY_EC_POINT) +std::vector sm2_compute_za(HashFunction& hash, + std::string_view user_id, + const EC_Group& group, + const EC_Point& pubkey) { + auto apoint = EC_AffinePoint(group, pubkey); + return sm2_compute_za(hash, user_id, group, apoint); +} +#endif + std::vector sm2_compute_za(HashFunction& hash, std::string_view user_id, const EC_Group& group, @@ -131,7 +145,6 @@ std::vector m_za; secure_vector m_digest; std::unique_ptr m_hash; - std::vector m_ws; }; std::vector SM2_Signature_Operation::sign(RandomNumberGenerator& rng) { @@ -150,9 +163,14 @@ const auto k = EC_Scalar::random(m_group, rng); - const auto r = EC_Scalar::gk_x_mod_order(k, rng, m_ws) + e; + const auto r = EC_Scalar::gk_x_mod_order(k, rng) + e; const auto s = (k - r * m_x) * m_da_inv; + // With overwhelming probability, a bug rather than actual zero r/s + if(r.is_zero() || s.is_zero()) { + throw Internal_Error("During SM2 signature generated zero r/s"); + } + return EC_Scalar::serialize_pair(r, s); } @@ -253,7 +271,8 @@ std::unique_ptr SM2_PublicKey::create_verification_op(std::string_view params, std::string_view provider) const { if(provider == "base" || provider.empty()) { - std::string userid, hash; + std::string userid; + std::string hash; parse_sm2_param_string(params, userid, hash); return std::make_unique(*this, userid, hash); } @@ -265,7 +284,8 @@ std::string_view params, std::string_view provider) const { if(provider == "base" || provider.empty()) { - std::string userid, hash; + std::string userid; + std::string hash; parse_sm2_param_string(params, userid, hash); return std::make_unique(*this, userid, hash); } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/sm2/sm2.h botan3-3.12.0+dfsg/src/lib/pubkey/sm2/sm2.h --- botan3-3.7.1+dfsg/src/lib/pubkey/sm2/sm2.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/sm2/sm2.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,8 @@ #ifndef BOTAN_SM2_KEY_H_ #define BOTAN_SM2_KEY_H_ +#include +#include #include namespace Botan { @@ -53,9 +55,7 @@ return (op == PublicKeyOperation::Signature || op == PublicKeyOperation::Encryption); } - std::optional _signature_element_size_for_DER_encoding() const override { - return domain().get_order_bytes(); - } + std::optional _signature_element_size_for_DER_encoding() const override; std::unique_ptr create_verification_op(std::string_view params, std::string_view provider) const override; @@ -90,14 +90,14 @@ * @param group curve parameters to bu used for this key * @param x the private key */ - SM2_PrivateKey(EC_Group group, EC_Scalar x); + SM2_PrivateKey(const EC_Group& group, const EC_Scalar& x); /** * Create a new private key * @param rng a random number generator * @param group parameters to used for this key */ - SM2_PrivateKey(RandomNumberGenerator& rng, EC_Group group); + SM2_PrivateKey(RandomNumberGenerator& rng, const EC_Group& group); /** * Create a private key. @@ -106,9 +106,9 @@ * @param x the private key (if zero, generate a new random key) */ BOTAN_DEPRECATED("Use one of the other constructors") - SM2_PrivateKey(RandomNumberGenerator& rng, EC_Group group, const BigInt& x); + SM2_PrivateKey(RandomNumberGenerator& rng, const EC_Group& group, const BigInt& x); - bool check_key(RandomNumberGenerator& rng, bool) const override; + bool check_key(RandomNumberGenerator& rng, bool strong) const override; std::unique_ptr public_key() const override; @@ -120,6 +120,7 @@ std::string_view params, std::string_view provider) const override; + // TODO(Botan4) remove this and the member variable BOTAN_DEPRECATED("Deprecated no replacement") const BigInt& get_da_inv() const { return m_da_inv_legacy; } const EC_Scalar& _get_da_inv() const { return m_da_inv; } @@ -152,10 +153,7 @@ inline std::vector sm2_compute_za(HashFunction& hash, std::string_view user_id, const EC_Group& group, - const EC_Point& pubkey) { - auto apoint = EC_AffinePoint(group, pubkey); - return sm2_compute_za(hash, user_id, group, apoint); -} + const EC_Point& pubkey); #endif // For compat with versions 2.2 - 2.7 diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/sm2/sm2_enc.cpp botan3-3.12.0+dfsg/src/lib/pubkey/sm2/sm2_enc.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/sm2/sm2_enc.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/sm2/sm2_enc.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,8 +9,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -42,9 +44,9 @@ std::vector encrypt(std::span msg, RandomNumberGenerator& rng) override { const auto k = EC_Scalar::random(m_group, rng); - const EC_AffinePoint C1 = EC_AffinePoint::g_mul(k, rng, m_ws); + const EC_AffinePoint C1 = EC_AffinePoint::g_mul(k, rng); - const EC_AffinePoint kPB = m_peer.mul(k, rng, m_ws); + const EC_AffinePoint kPB = m_peer.mul(k, rng); const auto x2_bytes = kPB.x_bytes(); const auto y2_bytes = kPB.y_bytes(); @@ -80,7 +82,6 @@ const EC_AffinePoint m_peer; std::unique_ptr m_hash; std::unique_ptr m_kdf; - std::vector m_ws; }; class SM2_Decryption_Operation final : public PK_Ops::Decryption { @@ -119,10 +120,12 @@ return secure_vector(); } - BigInt x1, y1; - secure_vector C3, masked_msg; + BigInt x1; + BigInt y1; + secure_vector C3; + secure_vector masked_msg; - BER_Decoder(ctext) + BER_Decoder(ctext, BER_Decoder::Limits::DER()) .start_sequence() .decode(x1) .decode(y1) @@ -131,31 +134,19 @@ .end_cons() .verify_end(); - std::vector recode_ctext; - DER_Encoder(recode_ctext) - .start_sequence() - .encode(x1) - .encode(y1) - .encode(C3, ASN1_Type::OctetString) - .encode(masked_msg, ASN1_Type::OctetString) - .end_cons(); - - if(recode_ctext.size() != ctext.size()) { - return secure_vector(); - } - - if(CT::is_equal(recode_ctext.data(), ctext.data(), ctext.size()).as_bool() == false) { + // Wrong length so certainly invalid, reject immediately + if(C3.size() != m_hash->output_length()) { return secure_vector(); } auto C1 = EC_AffinePoint::from_bigint_xy(m_group, x1, y1); - // Here C1 is publically invalid, so no problem with early return: + // Here C1 is publicly invalid, so no problem with early return: if(!C1) { return secure_vector(); } - const auto dbC1 = C1->mul(m_x, m_rng, m_ws); + const auto dbC1 = C1->mul(m_x, m_rng); const auto x2_bytes = dbC1.x_bytes(); const auto y2_bytes = dbC1.y_bytes(); @@ -168,11 +159,13 @@ m_hash->update(y2_bytes); const auto u = m_hash->final(); - if(!CT::is_equal(u.data(), C3.data(), m_hash->output_length()).as_bool()) { - return secure_vector(); - } + const auto mac_ok = CT::is_equal(u, C3); + valid_mask = mac_ok.if_set_return(0xFF); - valid_mask = 0xFF; + // Zero the plaintext if the MAC check failed + (~mac_ok).if_set_zero_out(masked_msg.data(), masked_msg.size()); + const size_t output_len = CT::Mask::expand(mac_ok).if_set_return(masked_msg.size()); + masked_msg.resize(output_len); return masked_msg; } @@ -180,7 +173,6 @@ const EC_Group m_group; const EC_Scalar m_x; RandomNumberGenerator& m_rng; - std::vector m_ws; std::unique_ptr m_hash; std::unique_ptr m_kdf; }; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_address.h botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_address.h --- botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_address.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_address.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,15 +9,13 @@ #ifndef BOTAN_SPHINCS_PLUS_ADDRESS_H_ #define BOTAN_SPHINCS_PLUS_ADDRESS_H_ -#include - -#include #include #include +#include namespace Botan { -enum class Sphincs_Address_Type : uint32_t { +enum class Sphincs_Address_Type : uint32_t /* NOLINT(*-enum-size) */ { WotsHash = 0, WotsPublicKeyCompression = 1, HashTree = 2, @@ -46,12 +44,11 @@ public: using enum Sphincs_Address_Type; - Sphincs_Address(Sphincs_Address_Type type) { - m_address.fill(0); - set_type(type); - } + explicit Sphincs_Address(Sphincs_Address_Type type) : m_address{} { set_type(type); } - Sphincs_Address(std::array address) { std::copy(address.begin(), address.end(), m_address.begin()); } + explicit Sphincs_Address(std::array address) : m_address{} { + std::copy(address.begin(), address.end(), m_address.begin()); + } /* Setter member functions as specified in FIPS 205, Section 4.3 */ @@ -137,7 +134,7 @@ Sphincs_Address_Type get_type() const { return Sphincs_Address_Type(m_address[type_offset]); } std::array to_bytes() const { - std::array result; + std::array result{}; for(unsigned int i = 0; i < m_address.size(); ++i) { store_be(m_address[i], result.data() + (i * 4)); } @@ -145,7 +142,7 @@ } std::array to_bytes_compressed() const { - std::array result; + std::array result{}; result[0] = static_cast(m_address[layer_offset]); store_be(m_address[tree_offset + 1], &result[1]); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_fors.cpp botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_fors.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_fors.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_fors.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,14 +11,14 @@ #include #include -#include #include +#include +#include #include #include #include #include -#include namespace Botan { @@ -78,7 +78,7 @@ BufferStuffer roots(roots_buffer); BufferStuffer sig(sig_out); - // Buffer to hold the FORS leafs during tree traversal + // Buffer to hold the FORS leaves during tree traversal // (Avoids a secure_vector allocation/deallocation in the hot path) ForsLeafSecret fors_leaf_secret(params.n()); @@ -86,7 +86,7 @@ // and the trees' root and append the signature respectively BOTAN_ASSERT_NOMSG(indices.size() == params.k()); for(uint32_t i = 0; i < params.k(); ++i) { - uint32_t idx_offset = i * (1 << params.a()); + const uint32_t idx_offset = i * (1 << params.a()); // Compute the secret leaf given by the chunk of the message and append it to the signature fors_tree_addr.set_type(Sphincs_Address_Type::ForsKeyGeneration) @@ -98,7 +98,8 @@ // Compute the authentication path and root for this leaf node fors_tree_addr.set_type(Sphincs_Address_Type::ForsTree); - GenerateLeafFunction fors_gen_leaf = [&](StrongSpan out_root, TreeNodeIndex address_index) { + const GenerateLeafFunction fors_gen_leaf = [&](StrongSpan out_root, + TreeNodeIndex address_index) { fors_tree_addr.set_tree_index(address_index); fors_tree_addr.set_type(Sphincs_Address_Type::ForsKeyGeneration); @@ -145,7 +146,7 @@ // leaf and the authentication path offered in the FORS signature. BOTAN_ASSERT_NOMSG(indices.size() == params.k()); for(uint32_t i = 0; i < params.k(); ++i) { - uint32_t idx_offset = i * (1 << params.a()); + const uint32_t idx_offset = i * (1 << params.a()); // Compute the FORS leaf by using the secret leaf contained in the signature fors_tree_addr.set_tree_height(TreeLayerIndex(0)).set_tree_index(indices[i] + idx_offset); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hash.cpp botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hash.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hash.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hash.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,12 +8,12 @@ #include -#include - #include #include #include #include +#include +#include #if defined(BOTAN_HAS_SPHINCS_PLUS_SHAKE_BASE) #include @@ -23,8 +23,6 @@ #include #endif -#include - namespace Botan { Sphincs_Hash_Functions::Sphincs_Hash_Functions(const Sphincs_Parameters& sphincs_params, diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hash.h botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hash.h --- botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hash.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hash.h 2026-05-07 01:38:28.000000000 +0000 @@ -20,7 +20,7 @@ * A collection of pseudorandom hash functions required for SLH-DSA * computations. See FIPS 205, Section 11.2.1 and 11.2.2. **/ -class BOTAN_TEST_API Sphincs_Hash_Functions { +class BOTAN_TEST_API Sphincs_Hash_Functions /* NOLINT(*-special-member-functions) */ { public: virtual ~Sphincs_Hash_Functions() = default; @@ -54,16 +54,16 @@ const SphincsMessageInternal& msg) = 0; template - void T(std::span out, const Sphincs_Address& address, BufferTs&&... in) { - auto& hash = tweak_hash(address, (std::forward(in).size() + ...)); - (hash.update(std::forward(in)), ...); + void T(std::span out, const Sphincs_Address& address, const BufferTs&... in) { + auto& hash = tweak_hash(address, (in.size() + ...)); + (hash.update(in), ...); hash.final(out); } template , typename... BufferTs> - OutT T(const Sphincs_Address& address, BufferTs&&... in) { + OutT T(const Sphincs_Address& address, const BufferTs&... in) { OutT t(m_sphincs_params.n()); - T(t, address, std::forward(in)...); + T(t, address, in...); return t; } @@ -99,8 +99,8 @@ const SphincsTreeNode& root, const SphincsMessageInternal& message) = 0; - const Sphincs_Parameters& m_sphincs_params; - const SphincsPublicSeed& m_pub_seed; + const Sphincs_Parameters& m_sphincs_params; // NOLINT(*non-private-member-variable*) + const SphincsPublicSeed& m_pub_seed; // NOLINT(*non-private-member-variable*) }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hypertree.cpp botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hypertree.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hypertree.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hypertree.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,12 +9,13 @@ #include #include +#include +#include #include #include #include #include #include -#include namespace Botan { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hypertree.h botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hypertree.h --- botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hypertree.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hypertree.h 2026-05-07 01:38:28.000000000 +0000 @@ -15,7 +15,6 @@ namespace Botan { -class Sphincs_Address; class Sphincs_Hash_Functions; class Sphincs_Parameters; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_parameters.cpp botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_parameters.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_parameters.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_parameters.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,13 +8,11 @@ #include -#include +#include #include #include #include -#include - namespace Botan { namespace { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_parameters.h botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_parameters.h --- botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_parameters.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_parameters.h 2026-05-07 01:38:28.000000000 +0000 @@ -15,13 +15,13 @@ namespace Botan { -enum class Sphincs_Hash_Type { +enum class Sphincs_Hash_Type : uint8_t { Shake256, Sha256, Haraka BOTAN_DEPRECATED("Haraka is not and will not be supported"), ///< Haraka is currently not supported }; -enum class Sphincs_Parameter_Set { +enum class Sphincs_Parameter_Set : uint8_t { Sphincs128Small, Sphincs128Fast, Sphincs192Small, diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_treehash.cpp botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_treehash.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_treehash.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_treehash.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,9 +8,9 @@ #include +#include #include #include -#include namespace Botan { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_types.h botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_types.h --- botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_types.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_types.h 2026-05-07 01:38:28.000000000 +0000 @@ -99,4 +99,4 @@ } // namespace Botan -#endif \ No newline at end of file +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_wots.cpp botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_wots.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_wots.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_wots.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,8 +8,9 @@ #include +#include +#include #include -#include namespace Botan { namespace { @@ -78,7 +79,7 @@ // Convert checksum to base_w. csum = csum << ((8 - ((params.wots_len_2() * params.log_w()) % 8)) % 8); - std::array csum_bytes; + std::array csum_bytes{}; store_be(csum, csum_bytes.data()); const size_t csum_bytes_size = params.wots_checksum_bytes(); @@ -143,7 +144,7 @@ BOTAN_ASSERT_NOMSG(!sign_leaf_idx.has_value() || wots_steps.size() == params.wots_len()); BOTAN_ASSERT_NOMSG(pk_addr.get_type() == Sphincs_Address_Type::WotsPublicKeyCompression); - secure_vector wots_sig; + const secure_vector wots_sig; WotsPublicKey wots_pk_buffer(params.wots_bytes()); BufferStuffer wots_pk(wots_pk_buffer); @@ -154,7 +155,7 @@ for(WotsChainIndex i(0); i < params.wots_len(); i++) { // If the current leaf is part of the signature wots_k stores the chain index - // of the value neccessary for the signature. Otherwise: nullopt (no signature) + // of the value necessary for the signature. Otherwise: nullopt (no signature) const auto wots_k = [&]() -> std::optional { if(sign_leaf_idx.has_value() && leaf_idx == sign_leaf_idx.value()) { return wots_steps[i.get()]; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_xmss.cpp botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_xmss.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_xmss.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_xmss.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,10 +8,10 @@ #include +#include #include #include #include -#include #include namespace Botan { @@ -43,7 +43,7 @@ pk_addr.set_type(Sphincs_Address_Type::WotsPublicKeyCompression); - GenerateLeafFunction xmss_gen_leaf = [&](StrongSpan out_root, TreeNodeIndex address_index) { + const GenerateLeafFunction xmss_gen_leaf = [&](StrongSpan out_root, TreeNodeIndex address_index) { wots_sign_and_pkgen( wots_bytes_s, out_root, secret_seed, address_index, idx_leaf, steps, leaf_addr, pk_addr, params, hashes); }; @@ -62,7 +62,7 @@ // code to have just one treehash routine that computes both root and path // in one function. SphincsXmssSignature dummy_sig(params.xmss_tree_height() * params.n() + params.wots_bytes()); - SphincsTreeNode dummy_root(params.n()); + const SphincsTreeNode dummy_root(params.n()); Sphincs_Address top_tree_addr(Sphincs_Address_Type::HashTree); Sphincs_Address wots_addr(Sphincs_Address_Type::WotsPublicKeyCompression); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,9 @@ #include #include +#include +#include +#include #include #include #include @@ -18,7 +21,6 @@ #include #include #include -#include #include @@ -159,8 +161,10 @@ return m_public->parameters().object_identifier(); } -bool SphincsPlus_PublicKey::check_key(RandomNumberGenerator&, bool) const { - // Nothing to check. It's literally just hashes. :-) +bool SphincsPlus_PublicKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const { + // Nothing to check for the public key. It's literally just hashes. :-) + // A sign/verify roundtrip for the private key could be added for strong + // validation, but SLH-DSA signing is very expensive. return true; } @@ -178,9 +182,11 @@ return std::make_unique(rng, m_public->parameters()); } +namespace { + class SphincsPlus_Verification_Operation final : public PK_Ops::Verification { public: - SphincsPlus_Verification_Operation(std::shared_ptr pub_key) : + explicit SphincsPlus_Verification_Operation(std::shared_ptr pub_key) : m_public(std::move(pub_key)), m_hashes(Botan::Sphincs_Hash_Functions::create(m_public->parameters(), m_public->seed())), m_context(/* TODO: Add API */ {}) { @@ -241,6 +247,8 @@ SphincsContext m_context; }; +} // namespace + std::unique_ptr SphincsPlus_PublicKey::create_verification_op(std::string_view /*params*/, std::string_view provider) const { if(provider.empty() || provider == "base") { @@ -338,6 +346,8 @@ return std::make_unique(*this); } +namespace { + class SphincsPlus_Signature_Operation final : public PK_Ops::Signature { public: SphincsPlus_Signature_Operation(std::shared_ptr private_key, @@ -427,6 +437,8 @@ SphincsContext m_context; }; +} // namespace + std::unique_ptr SphincsPlus_PrivateKey::create_signature_op(RandomNumberGenerator& rng, std::string_view params, std::string_view provider) const { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.h botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.h --- botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.h 2026-05-07 01:38:28.000000000 +0000 @@ -36,6 +36,11 @@ ~SphincsPlus_PublicKey() override; + SphincsPlus_PublicKey(const SphincsPlus_PublicKey& other) = default; + SphincsPlus_PublicKey(SphincsPlus_PublicKey&& other) = default; + SphincsPlus_PublicKey& operator=(const SphincsPlus_PublicKey& other) = default; + SphincsPlus_PublicKey& operator=(SphincsPlus_PublicKey&& other) = default; + size_t key_length() const override; std::string algo_name() const override; @@ -60,7 +65,7 @@ protected: SphincsPlus_PublicKey() = default; - std::shared_ptr m_public; + std::shared_ptr m_public; // NOLINT(*non-private-member-variable*) }; BOTAN_DIAGNOSTIC_PUSH @@ -98,6 +103,11 @@ ~SphincsPlus_PrivateKey() override; + SphincsPlus_PrivateKey(const SphincsPlus_PrivateKey& other) = default; + SphincsPlus_PrivateKey(SphincsPlus_PrivateKey&& other) = default; + SphincsPlus_PrivateKey& operator=(const SphincsPlus_PrivateKey& other) = delete; + SphincsPlus_PrivateKey& operator=(SphincsPlus_PrivateKey&& other) = delete; + secure_vector private_key_bits() const override; secure_vector raw_private_key_bits() const override; std::unique_ptr public_key() const override; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus_sha2_base/sp_hash_sha2.h botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus_sha2_base/sp_hash_sha2.h --- botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus_sha2_base/sp_hash_sha2.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus_sha2_base/sp_hash_sha2.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,11 +11,11 @@ #include +#include #include #include #include #include -#include #include namespace Botan { @@ -55,7 +55,7 @@ std::vector mgf1_input = concat>(r, m_pub_seed, r_pk_buffer); std::vector digest(m_sphincs_params.h_msg_digest_bytes()); - mgf1_mask(*m_sha_x_full, mgf1_input.data(), mgf1_input.size(), digest.data(), digest.size()); + mgf1_mask(*m_sha_x_full, mgf1_input, digest); return digest; } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus_shake_base/sp_hash_shake.h botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus_shake_base/sp_hash_shake.h --- botan3-3.7.1+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus_shake_base/sp_hash_shake.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus_shake_base/sp_hash_shake.h 2026-05-07 01:38:28.000000000 +0000 @@ -42,11 +42,8 @@ public: Sphincs_Hash_Functions_Shake(const Sphincs_Parameters& sphincs_params, const SphincsPublicSeed& pub_seed) : Sphincs_Hash_Functions(sphincs_params, pub_seed), - m_seeded_hash(sphincs_params.n() * 8), m_hash(sphincs_params.n() * 8), - m_h_msg_hash(8 * sphincs_params.h_msg_digest_bytes()) { - m_seeded_hash.update(m_pub_seed); - } + m_h_msg_hash(8 * sphincs_params.h_msg_digest_bytes()) {} void PRF_msg(StrongSpan out, StrongSpan sk_prf, @@ -62,7 +59,6 @@ std::string msg_hash_function_name() const override { return m_h_msg_hash.name(); } private: - SHAKE_256 m_seeded_hash; SHAKE_256 m_hash; SHAKE_256 m_h_msg_hash; }; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/stateful_key_index/info.txt botan3-3.12.0+dfsg/src/lib/pubkey/stateful_key_index/info.txt --- botan3-3.7.1+dfsg/src/lib/pubkey/stateful_key_index/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/stateful_key_index/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,9 @@ + +name -> "Stateful Key Index" +brief -> "Tracks updates for stateful signing algorithms" +type -> "Internal" + + + +sha2_32 + diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/stateful_key_index/stateful_key_index_registry.cpp botan3-3.12.0+dfsg/src/lib/pubkey/stateful_key_index/stateful_key_index_registry.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/stateful_key_index/stateful_key_index_registry.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/stateful_key_index/stateful_key_index_registry.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,82 @@ +/* + * (C) 2016 Matthias Gierlings + * 2026 Jack Lloyd + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#include + +#include +#include + +namespace Botan { + +Stateful_Key_Index_Registry& Stateful_Key_Index_Registry::global() { + static Stateful_Key_Index_Registry g_registry; + return g_registry; +} + +Stateful_Key_Index_Registry::Stateful_Key_Index_Registry() = default; + +Stateful_Key_Index_Registry::~Stateful_Key_Index_Registry() = default; + +Stateful_Key_Index_Registry::KeyId::KeyId(std::string_view algo_name, + uint32_t algo_params, + std::span key_material_1, + std::span key_material_2) : + m_val() { + auto hash = HashFunction::create_or_throw("SHA-256"); + + hash->update("Botan Stateful_Key_Index_Registry KeyID"); + hash->update_be(static_cast(algo_name.size())); + hash->update(algo_name); + hash->update_be(algo_params); + hash->update_be(static_cast(key_material_1.size())); + hash->update(key_material_1); + hash->update_be(static_cast(key_material_2.size())); + hash->update(key_material_2); + + BOTAN_ASSERT_NOMSG(hash->output_length() == m_val.size()); + + hash->final(m_val); +} + +// Lock must be held while this function is called +Stateful_Key_Index_Registry::RegistryMap::iterator Stateful_Key_Index_Registry::lookup(const KeyId& key_id) { + auto [i, _inserted] = m_registry.emplace(key_id, 0); + return i; +} + +uint64_t Stateful_Key_Index_Registry::current_index(const KeyId& key_id) { + const lock_guard_type lock(m_mutex); + auto idx = this->lookup(key_id); + return idx->second; +} + +uint64_t Stateful_Key_Index_Registry::reserve_next_index(const KeyId& key_id) { + const lock_guard_type lock(m_mutex); + auto idx = this->lookup(key_id); + const uint64_t cur = idx->second; + idx->second += 1; + return cur; +} + +void Stateful_Key_Index_Registry::set_index_lower_bound(const KeyId& key_id, uint64_t min) { + const lock_guard_type lock(m_mutex); + auto idx = this->lookup(key_id); + idx->second = std::max(idx->second, min); +} + +uint64_t Stateful_Key_Index_Registry::remaining_operations(const KeyId& key_id, uint64_t max) { + const lock_guard_type lock(m_mutex); + const uint64_t idx = this->lookup(key_id)->second; + + if(idx >= max) { + return 0; + } else { + return max - idx; + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/stateful_key_index/stateful_key_index_registry.h botan3-3.12.0+dfsg/src/lib/pubkey/stateful_key_index/stateful_key_index_registry.h --- botan3-3.7.1+dfsg/src/lib/pubkey/stateful_key_index/stateful_key_index_registry.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/stateful_key_index/stateful_key_index_registry.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,97 @@ +/* + * (C) 2016 Matthias Gierlings + * 2026 Jack Lloyd + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#ifndef BOTAN_STATEFUL_KEY_INDEX_REGISTRY_H_ +#define BOTAN_STATEFUL_KEY_INDEX_REGISTRY_H_ + +#include +#include +#include +#include +#include +#include + +namespace Botan { + +/** + * A process-wide registry mapping stateful key identity to a shared + * atomic counter. Ensures that independent copies of the same key + * material (e.g. deserialized separately) share a single leaf index, + * preventing catastrophic one-time signature reuse. + * + * Used by XMSS and HSS-LMS. + */ +class Stateful_Key_Index_Registry final { + public: + class KeyId final { + public: + /** + * Create a KeyId for some kind of key material + * + * @param algo_name Algorithm name (ex "XMSS", "HSS-LMS") + * @param algo_params Algorithm specific parameters + * @param key_material_1 First part of key identifying material + * @param key_material_2 Second part of key identifying material (can be omitted) + */ + KeyId(std::string_view algo_name, + uint32_t algo_params, + std::span key_material_1, + std::span key_material_2); + + KeyId() = default; + + auto operator<=>(const KeyId& other) const = default; + + private: + std::array m_val; + }; + + Stateful_Key_Index_Registry(const Stateful_Key_Index_Registry&) = delete; + Stateful_Key_Index_Registry(Stateful_Key_Index_Registry&&) = delete; + Stateful_Key_Index_Registry& operator=(const Stateful_Key_Index_Registry&) = delete; + Stateful_Key_Index_Registry& operator=(Stateful_Key_Index_Registry&&) = delete; + ~Stateful_Key_Index_Registry(); + + /** + * Retrieve the process-wide instance + */ + static Stateful_Key_Index_Registry& global(); + + /** + * Return the current counter + */ + uint64_t current_index(const KeyId& key_id); + + /** + * Return a new counter + */ + uint64_t reserve_next_index(const KeyId& key_id); + + /** + * Set the counter to at least min (but if already higher it will retain its current value) + */ + void set_index_lower_bound(const KeyId& key_id, uint64_t min); + + /** + * If the current counter is >= max returns 0, otherwise max - counter + */ + uint64_t remaining_operations(const KeyId& key_id, uint64_t max); + + private: + typedef std::map RegistryMap; + + RegistryMap::iterator lookup(const KeyId& key_id); + + Stateful_Key_Index_Registry(); + + mutex_type m_mutex; + RegistryMap m_registry; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/workfactor.cpp botan3-3.12.0+dfsg/src/lib/pubkey/workfactor.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/workfactor.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/workfactor.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,11 +1,13 @@ /* * Public Key Work Factor Functions -* (C) 1999-2007,2012 Jack Lloyd +* (C) 1999-2007,2012,2026 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include + +#include #include #include @@ -19,7 +21,7 @@ size_t nfs_workfactor(size_t bits, double log2_k) { // approximates natural logarithm of an integer of given bitsize - const double log_p = bits / std::numbers::log2e; + const double log_p = static_cast(bits) / std::numbers::log2e; const double log_log_p = std::log(log_p); @@ -48,26 +50,54 @@ return if_work_factor(bits); } -size_t dl_exponent_size(size_t bits) { - if(bits == 0) { - return 0; - } - if(bits <= 256) { - return bits - 1; - } - if(bits <= 1024) { +size_t dl_exponent_size(size_t p_bits) { + BOTAN_ARG_CHECK(p_bits > 1, "Invalid prime length"); + + /* + For relevant sizes we follow the suggestions in + NIST SP 800-56B Rev 2 Appendix D + "Maximum Security Strength Estimates for IFC Modulus Lengths" + + For sizes outside the range considered in the SP we use some sensible values + + Note that we return twice the value given in Table 4 since we are choosing + the exponent size as twice the estimated security strength. + + See also NIST SP 800-56A Rev 3 Appendix D, Tables 25 and 26 + */ + + if(p_bits <= 256) { + /* + * For stupidly small groups we might return a value larger than the group + * size if we fell into the conditionals below. Just use the maximum + * possible exponent size - for all the good it will do you with a group + * this weak. + */ + return p_bits - 1; + } else if(p_bits <= 1024) { + /* + Not in the SP, but general estimates are that a 1024 bit group provides at + most 80 bits security, so using an exponent appropriate for 96 bit security + is more than sufficient. + */ return 192; + } else if(p_bits <= 2048) { + return 224; // SP 800-56B + } else if(p_bits <= 3072) { + return 256; // SP 800-56B + } else if(p_bits <= 4096) { + return 304; // SP 800-56B + } else if(p_bits <= 6144) { + return 352; // SP 800-56B + } else if(p_bits <= 8192) { + return 400; // SP 800-56B + } else { + // For values larger than we know about, just saturate to 256 bit security + // which is Good Enough for FFDH + // + // NIST puts 15360 bit groups at exactly 256 bits security + return 512; } - if(bits <= 1536) { - return 224; - } - if(bits <= 2048) { - return 256; - } - if(bits <= 4096) { - return 384; - } - return 512; } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/x25519/donna.cpp botan3-3.12.0+dfsg/src/lib/pubkey/x25519/donna.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/x25519/donna.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/x25519/donna.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -37,7 +37,6 @@ #include #include #include -#include namespace Botan { @@ -113,11 +112,11 @@ * On return, out[i] < 2**52 */ inline void fmul(uint64_t out[5], const uint64_t in[5], const uint64_t in2[5]) { - const uint128_t s0 = in2[0]; - const uint128_t s1 = in2[1]; - const uint128_t s2 = in2[2]; - const uint128_t s3 = in2[3]; - const uint128_t s4 = in2[4]; + const auto s0 = uint128_t(in2[0]); + const auto s1 = uint128_t(in2[1]); + const auto s2 = uint128_t(in2[2]); + const auto s3 = uint128_t(in2[3]); + const auto s4 = uint128_t(in2[4]); uint64_t r0 = in[0]; uint64_t r1 = in[1]; @@ -153,10 +152,10 @@ uint64_t c = carry_shift(t4, 51); r0 += c * 19; - c = r0 >> 51; + c = r0 >> 51U; r0 = r0 & MASK_63; r1 += c; - c = r1 >> 51; + c = r1 >> 51U; r1 = r1 & MASK_63; r2 += c; @@ -181,7 +180,7 @@ const uint64_t d419 = r4 * 19; const uint64_t d4 = d419 * 2; - uint128_t t0 = uint128_t(r0) * r0 + uint128_t(d4) * r1 + uint128_t(d2) * (r3); + const uint128_t t0 = uint128_t(r0) * r0 + uint128_t(d4) * r1 + uint128_t(d2) * (r3); uint128_t t1 = uint128_t(d0) * r1 + uint128_t(d4) * r2 + uint128_t(r3) * (r3 * 19); uint128_t t2 = uint128_t(d0) * r2 + uint128_t(r1) * r1 + uint128_t(d4) * (r3); uint128_t t3 = uint128_t(d0) * r3 + uint128_t(d1) * r2 + uint128_t(r4) * (d419); @@ -199,10 +198,10 @@ uint64_t c = carry_shift(t4, 51); r0 += c * 19; - c = r0 >> 51; + c = r0 >> 51U; r0 = r0 & MASK_63; r1 += c; - c = r1 >> 51; + c = r1 >> 51U; r1 = r1 & MASK_63; r2 += c; } @@ -227,22 +226,22 @@ * little-endian, 32-byte array */ inline void fcontract(uint8_t* out, const uint64_t input[5]) { - uint128_t t0 = input[0]; - uint128_t t1 = input[1]; - uint128_t t2 = input[2]; - uint128_t t3 = input[3]; - uint128_t t4 = input[4]; + auto t0 = uint128_t(input[0]); + auto t1 = uint128_t(input[1]); + auto t2 = uint128_t(input[2]); + auto t3 = uint128_t(input[3]); + auto t4 = uint128_t(input[4]); for(size_t i = 0; i != 2; ++i) { - t1 += t0 >> 51; + t1 += t0 >> 51U; t0 &= MASK_63; - t2 += t1 >> 51; + t2 += t1 >> 51U; t1 &= MASK_63; - t3 += t2 >> 51; + t3 += t2 >> 51U; t2 &= MASK_63; - t4 += t3 >> 51; + t4 += t3 >> 51U; t3 &= MASK_63; - t0 += (t4 >> 51) * 19; + t0 += (t4 >> 51U) * 19; t4 &= MASK_63; } @@ -251,15 +250,15 @@ t0 += 19; - t1 += t0 >> 51; + t1 += t0 >> 51U; t0 &= MASK_63; - t2 += t1 >> 51; + t2 += t1 >> 51U; t1 &= MASK_63; - t3 += t2 >> 51; + t3 += t2 >> 51U; t2 &= MASK_63; - t4 += t3 >> 51; + t4 += t3 >> 51U; t3 &= MASK_63; - t0 += (t4 >> 51) * 19; + t0 += (t4 >> 51U) * 19; t4 &= MASK_63; /* now between 19 and 2^255-1 in both cases, and offset by 19. */ @@ -272,13 +271,13 @@ /* now between 2^255 and 2^256-20, and offset by 2^255. */ - t1 += t0 >> 51; + t1 += t0 >> 51U; t0 &= MASK_63; - t2 += t1 >> 51; + t2 += t1 >> 51U; t1 &= MASK_63; - t3 += t2 >> 51; + t3 += t2 >> 51U; t2 &= MASK_63; - t4 += t3 >> 51; + t4 += t3 >> 51U; t3 &= MASK_63; t4 &= MASK_63; @@ -455,7 +454,10 @@ CT::poison(secret, 32); CT::poison(basepoint, 32); - uint64_t bp[5], x[5], z[5], zmone[5]; + uint64_t bp[5]; + uint64_t x[5]; + uint64_t z[5]; + uint64_t zmone[5]; uint8_t e[32]; copy_mem(e, secret, 32); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/x25519/x25519.cpp botan3-3.12.0+dfsg/src/lib/pubkey/x25519/x25519.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/x25519/x25519.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/x25519/x25519.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -66,13 +66,13 @@ return std::make_unique(rng); } -X25519_PrivateKey::X25519_PrivateKey(const secure_vector& secret_key) { +X25519_PrivateKey::X25519_PrivateKey(std::span secret_key) { if(secret_key.size() != 32) { throw Decoding_Error("Invalid size for X25519 private key"); } m_public.resize(32); - m_private = secret_key; + m_private.assign(secret_key.begin(), secret_key.end()); curve25519_basepoint(m_public.data(), m_private.data()); } @@ -83,7 +83,7 @@ } X25519_PrivateKey::X25519_PrivateKey(const AlgorithmIdentifier& /*unused*/, std::span key_bits) { - BER_Decoder(key_bits).decode(m_private, ASN1_Type::OctetString).discard_remaining(); + BER_Decoder(key_bits, BER_Decoder::Limits::DER()).decode(m_private, ASN1_Type::OctetString).discard_remaining(); size_check(m_private.size(), "private key"); m_public.resize(32); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/x25519/x25519.h botan3-3.12.0+dfsg/src/lib/pubkey/x25519/x25519.h --- botan3-3.7.1+dfsg/src/lib/pubkey/x25519/x25519.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/x25519/x25519.h 2026-05-07 01:38:28.000000000 +0000 @@ -27,7 +27,9 @@ std::vector public_key_bits() const override; - std::vector public_value() const { return m_public; } + BOTAN_DEPRECATED("Use raw_public_key_bits") std::vector public_value() const { + return raw_public_key_bits(); + } bool supports_operation(PublicKeyOperation op) const override { return (op == PublicKeyOperation::KeyAgreement); } @@ -48,7 +50,7 @@ protected: X25519_PublicKey() = default; - std::vector m_public; + std::vector m_public; // NOLINT(*non-private-member-variable*) }; BOTAN_DIAGNOSTIC_PUSH @@ -75,9 +77,9 @@ * Construct a private key from the specified parameters. * @param secret_key the private key */ - explicit X25519_PrivateKey(const secure_vector& secret_key); + explicit X25519_PrivateKey(std::span secret_key); - std::vector public_value() const override { return X25519_PublicKey::public_value(); } + std::vector public_value() const override { return raw_public_key_bits(); } secure_vector agree(const uint8_t w[], size_t w_len) const; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/x509_key.cpp botan3-3.12.0+dfsg/src/lib/pubkey/x509_key.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/x509_key.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/x509_key.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -31,11 +31,21 @@ std::vector key_bits; if(ASN1::maybe_BER(source) && !PEM_Code::matches(source)) { - BER_Decoder(source).start_sequence().decode(alg_id).decode(key_bits, ASN1_Type::BitString).end_cons(); + BER_Decoder(source, BER_Decoder::Limits::DER()) + .start_sequence() + .decode(alg_id) + .decode(key_bits, ASN1_Type::BitString) + .end_cons() + .verify_end(); } else { DataSource_Memory ber(PEM_Code::decode_check_label(source, "PUBLIC KEY")); - BER_Decoder(ber).start_sequence().decode(alg_id).decode(key_bits, ASN1_Type::BitString).end_cons(); + BER_Decoder(ber, BER_Decoder::Limits::DER()) + .start_sequence() + .decode(alg_id) + .decode(key_bits, ASN1_Type::BitString) + .end_cons() + .verify_end(); } if(key_bits.empty()) { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/x509_key.h botan3-3.12.0+dfsg/src/lib/pubkey/x509_key.h --- botan3-3.7.1+dfsg/src/lib/pubkey/x509_key.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/x509_key.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,7 @@ #include #include +#include #include #include diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/atomic.h botan3-3.12.0+dfsg/src/lib/pubkey/xmss/atomic.h --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/atomic.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/atomic.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ -/* - * Atomic - * (C) 2016 Matthias Gierlings - * - * Botan is released under the Simplified BSD License (see license.txt) - **/ - -#ifndef BOTAN_ATOMIC_H_ -#define BOTAN_ATOMIC_H_ - -#include -#include -#include - -namespace Botan { - -template -/** - * Simple helper class to expand std::atomic with copy constructor and copy - * assignment operator, i.e. for use as element in a container like - * std::vector. The construction of instances of this wrapper is NOT atomic - * and needs to be properly guarded. - **/ -class Atomic final { - public: - Atomic() = default; - - Atomic(const Atomic& data) : m_data(data.m_data.load()) {} - - Atomic(const std::atomic& data) : m_data(data.load()) {} - - ~Atomic() = default; - - Atomic& operator=(const Atomic& other) { - if(this != &other) { - m_data.store(other.m_data.load()); - } - return *this; - } - - Atomic& operator=(const std::atomic& a) { - m_data.store(a.load()); - return *this; - } - - operator std::atomic&() { return m_data; } - - operator T() { return m_data.load(); } - - private: - std::atomic m_data; -}; - -} // namespace Botan - -#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/info.txt botan3-3.12.0+dfsg/src/lib/pubkey/xmss/info.txt --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -12,11 +12,9 @@ -atomic.h xmss_address.h xmss_common_ops.h xmss_hash.h -xmss_index_registry.h xmss_signature.h xmss_signature_operation.h xmss_tools.h @@ -28,10 +26,6 @@ asn1 rng hash -sha2_32 +stateful_key_index trunc_hash - - -atomics - diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss.h botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss.h --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,18 +9,17 @@ #ifndef BOTAN_XMSS_H_ #define BOTAN_XMSS_H_ -#include -#include - -#include #include #include +#include +#include namespace Botan { class RandomNumberGenerator; class XMSS_Address; class XMSS_Hash; +class XMSS_PublicKey_Internal; class XMSS_PrivateKey_Internal; class XMSS_Verification_Operation; class XMSS_WOTS_PublicKey; @@ -55,7 +54,7 @@ * * @param key_bits DER encoded public key bits */ - XMSS_PublicKey(std::span key_bits); + BOTAN_FUTURE_EXPLICIT XMSS_PublicKey(std::span key_bits); /** * Creates a new XMSS public key for a chosen XMSS signature method as @@ -75,11 +74,11 @@ return AlgorithmIdentifier(object_identifier(), AlgorithmIdentifier::USE_EMPTY_PARAM); } - bool check_key(RandomNumberGenerator&, bool) const override { return true; } + bool check_key(RandomNumberGenerator& rng, bool strong) const override; - size_t estimated_strength() const override { return m_xmss_params.estimated_strength(); } + size_t estimated_strength() const override; - size_t key_length() const override { return m_xmss_params.estimated_strength(); } + size_t key_length() const override; /** * Generates a byte sequence representing the XMSS @@ -113,18 +112,16 @@ protected: friend class XMSS_Verification_Operation; - const secure_vector& public_seed() const { return m_public_seed; } + const secure_vector& public_seed() const; - const secure_vector& root() const { return m_root; } + const secure_vector& root() const; - const XMSS_Parameters& xmss_parameters() const { return m_xmss_params; } + const XMSS_Parameters& xmss_parameters() const; - protected: - std::vector m_raw_key; - XMSS_Parameters m_xmss_params; - XMSS_WOTS_Parameters m_wots_params; - secure_vector m_root; - secure_vector m_public_seed; + void set_root(secure_vector root); + + private: + std::shared_ptr m_public_key; }; template @@ -135,7 +132,7 @@ /** * Determines how WOTS+ private keys are derived from the XMSS private key */ -enum class WOTS_Derivation_Method { +enum class WOTS_Derivation_Method : uint8_t { /// This roughly followed the suggestions in RFC 8391 but is vulnerable /// to a multi-target attack. For new private keys, we recommend using /// the derivation as suggested in NIST SP.800-208. @@ -186,7 +183,7 @@ * * @param raw_key An XMSS private key serialized using raw_private_key(). **/ - XMSS_PrivateKey(std::span raw_key); + BOTAN_FUTURE_EXPLICIT XMSS_PrivateKey(std::span raw_key); /** * Creates a new XMSS private key for the chosen XMSS signature method @@ -232,19 +229,20 @@ std::optional remaining_operations() const override; - std::unique_ptr create_signature_op(RandomNumberGenerator&, - std::string_view, + std::unique_ptr create_signature_op(RandomNumberGenerator& rng, + std::string_view params, std::string_view provider) const override; secure_vector private_key_bits() const override; /** - * Generates a non standartized byte sequence representing the XMSS + * Generates a non standardized byte sequence representing the XMSS * private key. * * @return byte sequence consisting of the following elements in order: * 4-byte OID, n-byte root node, n-byte public seed, - * 8-byte unused leaf index, n-byte prf seed, n-byte private seed. + * 4-byte unused leaf index, n-byte prf seed, n-byte private seed. + * At last 1-byte that encodes the WOTS+ key derivation method. **/ secure_vector raw_private_key() const; @@ -257,8 +255,8 @@ const secure_vector& prf_value() const; - XMSS_WOTS_PublicKey wots_public_key_for(XMSS_Address& adrs, XMSS_Hash& hash) const; - XMSS_WOTS_PrivateKey wots_private_key_for(XMSS_Address& adrs, XMSS_Hash& hash) const; + XMSS_WOTS_PublicKey wots_public_key_for(const XMSS_Address& adrs, XMSS_Hash& hash) const; + XMSS_WOTS_PrivateKey wots_private_key_for(const XMSS_Address& adrs, XMSS_Hash& hash) const; /** * Algorithm 9: "treeHash" @@ -267,26 +265,22 @@ * @param start_idx The start index. * @param target_node_height Height of the target node. * @param adrs Address of the tree containing the target node. + * @param hash The hash function to use * * @return The root node of a tree of height target_node height with the * leftmost leaf being the hash of the WOTS+ pk with index * start_idx. **/ - secure_vector tree_hash(size_t start_idx, size_t target_node_height, XMSS_Address& adrs); + secure_vector tree_hash(size_t start_idx, + size_t target_node_height, + const XMSS_Address& adrs, + XMSS_Hash& hash) const; void tree_hash_subtree(secure_vector& result, size_t start_idx, size_t target_node_height, - XMSS_Address& adrs); - - /** - * Helper for multithreaded tree hashing. - */ - void tree_hash_subtree(secure_vector& result, - size_t start_idx, - size_t target_node_height, XMSS_Address& adrs, - XMSS_Hash& hash); + XMSS_Hash& hash) const; std::shared_ptr m_private; }; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_address.h botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_address.h --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_address.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_address.h 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,7 @@ /* * XMSS Address * (C) 2016 Matthias Gierlings + * 2026 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) **/ @@ -9,8 +10,9 @@ #define BOTAN_XMSS_ADDRESS_H_ #include -#include #include +#include +#include namespace Botan { @@ -277,29 +279,25 @@ set_hi32(3, value); } - const secure_vector& bytes() const { return m_data; } - - secure_vector& bytes() { return m_data; } + std::span bytes() const { return std::span{m_data}; } /** * @return the size of an XMSS_Address **/ size_t size() const { return m_data.size(); } - XMSS_Address() : m_data(m_address_size) { set_type(Type::None); } + XMSS_Address() : m_data{} { set_type(Type::None); } - XMSS_Address(Type type) : m_data(m_address_size) { set_type(type); } + ~XMSS_Address() = default; + XMSS_Address(const XMSS_Address& other) = default; + XMSS_Address(XMSS_Address&& other) = default; - XMSS_Address(secure_vector data) : m_data(std::move(data)) { - BOTAN_ASSERT(m_data.size() == m_address_size, "XMSS_Address must be of 256 bits size."); - } + XMSS_Address& operator=(const XMSS_Address& other) = default; + XMSS_Address& operator=(XMSS_Address&& other) = default; - protected: - secure_vector m_data; + explicit XMSS_Address(Type type) : m_data() { set_type(type); } private: - static const size_t m_address_size = 32; - inline uint32_t get_hi32(size_t offset) const { return ((0x000000FF & m_data[8 * offset + 3]) | (0x000000FF & m_data[8 * offset + 2]) << 8 | (0x000000FF & m_data[8 * offset + 1]) << 16 | (0x000000FF & m_data[8 * offset]) << 24); @@ -323,6 +321,8 @@ m_data[offset * 8 + 6] = ((value >> 8) & 0xFF); m_data[offset * 8 + 7] = ((value) & 0xFF); } + + std::array m_data; // NOLINT(*non-private-member-variable*) }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_common_ops.cpp botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_common_ops.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_common_ops.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_common_ops.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -15,7 +15,7 @@ void XMSS_Common_Ops::randomize_tree_hash(secure_vector& result, const secure_vector& left, const secure_vector& right, - XMSS_Address& adrs, + XMSS_Address adrs, const secure_vector& seed, XMSS_Hash& hash, const XMSS_Parameters& params) { @@ -45,7 +45,7 @@ void XMSS_Common_Ops::create_l_tree(secure_vector& result, wots_keysig_t pk, - XMSS_Address& adrs, + XMSS_Address adrs, const secure_vector& seed, XMSS_Hash& hash, const XMSS_Parameters& params) { @@ -57,7 +57,7 @@ adrs.set_tree_index(static_cast(i)); randomize_tree_hash(pk[i], pk[2 * i], pk[2 * i + 1], adrs, seed, hash, params); } - if(l & 0x01) { + if((l & 0x01) == 0x01) { pk[l >> 1] = pk[l - 1]; } l = (l >> 1) + (l & 0x01); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_common_ops.h botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_common_ops.h --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_common_ops.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_common_ops.h 2026-05-07 01:38:28.000000000 +0000 @@ -30,13 +30,13 @@ * Generates a randomized hash. * * This overload is used in multithreaded scenarios, where it is - * required to provide seperate instances of XMSS_Hash to each + * required to provide separate instances of XMSS_Hash to each * thread. * * @param[out] result The resulting randomized hash. * @param[in] left Left half of the hash function input. * @param[in] right Right half of the hash function input. - * @param[in] adrs Adress of the hash function call. + * @param[in] adrs Address of the hash function call. * @param[in] seed The seed for G. * @param[in] hash Instance of XMSS_Hash, that may only by the thread * executing generate_public_key. @@ -45,7 +45,7 @@ static void randomize_tree_hash(secure_vector& result, const secure_vector& left, const secure_vector& right, - XMSS_Address& adrs, + XMSS_Address adrs, const secure_vector& seed, XMSS_Hash& hash, const XMSS_Parameters& params); @@ -56,7 +56,7 @@ * Takes a WOTS+ public key and compresses it to a single n-byte value. * * This overload is used in multithreaded scenarios, where it is - * required to provide seperate instances of XMSS_Hash to each thread. + * required to provide separate instances of XMSS_Hash to each thread. * * @param[out] result Public key compressed to a single n-byte value * pk[0]. @@ -69,7 +69,7 @@ **/ static void create_l_tree(secure_vector& result, wots_keysig_t pk, - XMSS_Address& adrs, + XMSS_Address adrs, const secure_vector& seed, XMSS_Hash& hash, const XMSS_Parameters& params); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_hash.cpp botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_hash.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_hash.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_hash.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #include +#include #include #include #include diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_hash.h botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_hash.h --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_hash.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_hash.h 2026-05-07 01:38:28.000000000 +0000 @@ -22,7 +22,7 @@ **/ class XMSS_Hash final { public: - XMSS_Hash(const XMSS_Parameters& params); + explicit XMSS_Hash(const XMSS_Parameters& params); XMSS_Hash(const XMSS_Hash& hash); XMSS_Hash(XMSS_Hash&& hash) = default; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_index_registry.cpp botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_index_registry.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_index_registry.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_index_registry.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,71 +0,0 @@ -/* - * XMSS Index Registry - * A registry for XMSS private keys, keeps track of the leaf index for - * independend copies of the same key. - * (C) 2016 Matthias Gierlings - * - * Botan is released under the Simplified BSD License (see license.txt) - **/ - -#include - -#include -#include - -namespace Botan { - -const std::string XMSS_Index_Registry::m_index_hash_function = "SHA-256"; - -//static -uint64_t XMSS_Index_Registry::make_key_id(const secure_vector& private_seed, - const secure_vector& prf) { - std::unique_ptr hash = HashFunction::create(m_index_hash_function); - BOTAN_ASSERT(hash != nullptr, "XMSS_Index_Registry requires SHA-256"); - hash->update(private_seed); - hash->update(prf); - secure_vector result = hash->final(); - uint64_t key_id = 0; - for(size_t i = 0; i < sizeof(key_id); i++) { - key_id = ((key_id << 8) | result[i]); - } - - return key_id; -} - -std::shared_ptr> XMSS_Index_Registry::get(const secure_vector& private_seed, - const secure_vector& prf) { - size_t pos = get(make_key_id(private_seed, prf)); - - if(pos < std::numeric_limits::max()) { - return m_leaf_indices[pos]; - } else { - return m_leaf_indices[add(make_key_id(private_seed, prf))]; - } -} - -size_t XMSS_Index_Registry::get(uint64_t id) const { - for(size_t i = 0; i < m_key_ids.size(); i++) { - if(m_key_ids[i] == id) { - return i; - } - } - - return std::numeric_limits::max(); -} - -size_t XMSS_Index_Registry::add(uint64_t id, size_t last_unused) { - lock_guard_type lock(m_mutex); - size_t pos = get(id); - if(pos < m_key_ids.size()) { - if(last_unused > *(m_leaf_indices[pos])) { - m_leaf_indices[pos] = std::make_shared>(last_unused); - } - return pos; - } - - m_key_ids.push_back(id); - m_leaf_indices.push_back(std::make_shared>(last_unused)); - return m_key_ids.size() - 1; -} - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_index_registry.h botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_index_registry.h --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_index_registry.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_index_registry.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,99 +0,0 @@ -/* - * XMSS Index Registry - * (C) 2016 Matthias Gierlings - * - * Botan is released under the Simplified BSD License (see license.txt) - **/ - -#ifndef BOTAN_XMSS_INDEX_REGISTRY_H_ -#define BOTAN_XMSS_INDEX_REGISTRY_H_ - -#include - -#include -#include -#include - -namespace Botan { - -/** - * A registry for XMSS private keys, keeps track of the leaf index for - * independend copies of the same key. - **/ -class XMSS_Index_Registry final { - public: - XMSS_Index_Registry(const XMSS_Index_Registry&) = delete; - XMSS_Index_Registry& operator=(const XMSS_Index_Registry&) = delete; - - /** - * Retrieves a handle to the process-wide unique XMSS index registry. - * - * @return Reference to unique XMSS index registry. - **/ - static XMSS_Index_Registry& get_instance() { - static XMSS_Index_Registry self; - return self; - } - - /** - * Retrieves the last unused leaf index for the private key identified - * by private_seed and prf. The leaf index will be updated properly - * across independent copies of private_key. - * - * @param private_seed Part of the unique identifier for an - * XMSS_PrivateKey. - * @param prf Part of the unique identifier for an XMSS_PrivateKey. - * - * @return last unused leaf index for private_key. - **/ - std::shared_ptr> get(const secure_vector& private_seed, - const secure_vector& prf); - - private: - XMSS_Index_Registry() = default; - - static const std::string m_index_hash_function; - - /** - * Creates a unique 64-bit id for an XMSS_Private key, by interpreting - * the first 64-bit of HASH(PRIVATE_SEED || PRF) as 64 bit integer - * value. - * - * @return unique integral identifier for an XMSS private key. - **/ - static uint64_t make_key_id(const secure_vector& private_seed, const secure_vector& prf); - - /** - * Retrieves the index position of a key within the registry or - * max(size_t) if key has not been found. - * - * @param id unique id of the XMSS private key (see make_key_id()). - * - * @return index position of key or max(size_t) if key not found. - **/ - size_t get(uint64_t id) const; - - /** - * If XMSS_PrivateKey identified by id is already registered, the - * position of the according registry entry is returned. If last_unused - * is bigger than the last unused index stored for the key identified by - * id the unused leaf index for this key is set to last_unused. If no key - * matching id is registed yet, an entry of id is added, with the last - * unused leaf index initialized to the value of last_unused. - * - * @last_unused Initial value for the last unused leaf index of the - * registered key. - * - * @return positon of leaf index registry entry for key identified - * by id. - **/ - size_t add(uint64_t id, size_t last_unused = 0); - - std::vector m_key_ids; - std::vector>> m_leaf_indices; - mutex_type m_mutex; -}; - -} // namespace Botan - -#endif diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_parameters.cpp botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_parameters.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_parameters.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_parameters.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ /* * XMSS Parameters - * Descibes a signature method for XMSS, as defined in: + * Describes a signature method for XMSS, as defined in: * [1] XMSS: Extended Hash-Based Signatures, * Request for Comments: 8391 * Release: May 2018. @@ -13,6 +13,7 @@ #include +#include #include #include @@ -86,242 +87,197 @@ throw Lookup_Error(fmt("Unknown XMSS algorithm param '{}'", param_set)); } -XMSS_Parameters::XMSS_Parameters(std::string_view param_set) : - XMSS_Parameters(XMSS_Parameters::xmss_id_from_string(param_set)) {} +std::string_view XMSS_Parameters::hash_function_name() const { + switch(m_oid) { + case XMSS_SHA2_10_256: + case XMSS_SHA2_16_256: + case XMSS_SHA2_20_256: + return "SHA-256"; + + case XMSS_SHA2_10_512: + case XMSS_SHA2_16_512: + case XMSS_SHA2_20_512: + return "SHA-512"; + + case XMSS_SHAKE_10_256: + case XMSS_SHAKE_16_256: + case XMSS_SHAKE_20_256: + return "SHAKE-128(256)"; + + case XMSS_SHAKE_10_512: + case XMSS_SHAKE_16_512: + case XMSS_SHAKE_20_512: + return "SHAKE-256(512)"; + + case XMSS_SHA2_10_192: + case XMSS_SHA2_16_192: + case XMSS_SHA2_20_192: + return "Truncated(SHA-256,192)"; + + case XMSS_SHAKE256_10_256: + case XMSS_SHAKE256_16_256: + case XMSS_SHAKE256_20_256: + return "SHAKE-256(256)"; + + case XMSS_SHAKE256_10_192: + case XMSS_SHAKE256_16_192: + case XMSS_SHAKE256_20_192: + return "SHAKE-256(192)"; + + default: + BOTAN_ASSERT_UNREACHABLE(); + } +} + +std::string_view XMSS_Parameters::name() const { + switch(m_oid) { + case XMSS_SHA2_10_256: + return "XMSS-SHA2_10_256"; + + case XMSS_SHA2_16_256: + return "XMSS-SHA2_16_256"; + + case XMSS_SHA2_20_256: + return "XMSS-SHA2_20_256"; + + case XMSS_SHA2_10_512: + return "XMSS-SHA2_10_512"; + + case XMSS_SHA2_16_512: + return "XMSS-SHA2_16_512"; + + case XMSS_SHA2_20_512: + return "XMSS-SHA2_20_512"; + + case XMSS_SHAKE_10_256: + return "XMSS-SHAKE_10_256"; + + case XMSS_SHAKE_16_256: + return "XMSS-SHAKE_16_256"; + + case XMSS_SHAKE_20_256: + return "XMSS-SHAKE_20_256"; + + case XMSS_SHAKE_10_512: + return "XMSS-SHAKE_10_512"; + + case XMSS_SHAKE_16_512: + return "XMSS-SHAKE_16_512"; + + case XMSS_SHAKE_20_512: + return "XMSS-SHAKE_20_512"; + + case XMSS_SHA2_10_192: + return "XMSS-SHA2_10_192"; + + case XMSS_SHA2_16_192: + return "XMSS-SHA2_16_192"; + + case XMSS_SHA2_20_192: + return "XMSS-SHA2_20_192"; + + case XMSS_SHAKE256_10_256: + return "XMSS-SHAKE256_10_256"; + + case XMSS_SHAKE256_16_256: + return "XMSS-SHAKE256_16_256"; + + case XMSS_SHAKE256_20_256: + return "XMSS-SHAKE256_20_256"; + + case XMSS_SHAKE256_10_192: + return "XMSS-SHAKE256_10_192"; + + case XMSS_SHAKE256_16_192: + return "XMSS-SHAKE256_16_192"; + + case XMSS_SHAKE256_20_192: + return "XMSS-SHAKE256_20_192"; + + default: + BOTAN_ASSERT_UNREACHABLE(); + } +} + +// NOLINTBEGIN(*-member-init) +XMSS_Parameters::XMSS_Parameters(std::string_view algo_name) { + *this = XMSS_Parameters::from_name(algo_name); +} + +XMSS_Parameters::XMSS_Parameters(xmss_algorithm_t oid) { + *this = XMSS_Parameters::from_id(oid); +} + +// NOLINTEND(*-member-init) -XMSS_Parameters::XMSS_Parameters(xmss_algorithm_t oid) : m_oid(oid) { +XMSS_Parameters XMSS_Parameters::from_name(std::string_view param_set) { + return XMSS_Parameters::from_id(XMSS_Parameters::xmss_id_from_string(param_set)); +} + +XMSS_Parameters XMSS_Parameters::from_id(xmss_algorithm_t oid) { switch(oid) { case XMSS_SHA2_10_256: - m_element_size = 32; - m_hash_id_size = 32; - m_w = 16; - m_len = 67; - m_tree_height = 10; - m_name = "XMSS-SHA2_10_256"; - m_hash_name = "SHA-256"; - m_strength = 256; - m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_256; - break; + return XMSS_Parameters(oid, XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_256, 32, 32, 10, 67); + case XMSS_SHA2_16_256: - m_element_size = 32; - m_hash_id_size = 32; - m_w = 16; - m_len = 67; - m_tree_height = 16; - m_name = "XMSS-SHA2_16_256"; - m_hash_name = "SHA-256"; - m_strength = 256; - m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_256; - break; + return XMSS_Parameters(oid, XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_256, 32, 32, 16, 67); + case XMSS_SHA2_20_256: - m_element_size = 32; - m_hash_id_size = 32; - m_w = 16; - m_len = 67; - m_tree_height = 20; - m_name = "XMSS-SHA2_20_256"; - m_hash_name = "SHA-256"; - m_strength = 256; - m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_256; - break; + return XMSS_Parameters(oid, XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_256, 32, 32, 20, 67); + case XMSS_SHA2_10_512: - m_element_size = 64; - m_hash_id_size = 64; - m_w = 16; - m_len = 131; - m_tree_height = 10; - m_name = "XMSS-SHA2_10_512"; - m_hash_name = "SHA-512"; - m_strength = 512; - m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_512; - break; + return XMSS_Parameters(oid, XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_512, 64, 64, 10, 131); + case XMSS_SHA2_16_512: - m_element_size = 64; - m_hash_id_size = 64; - m_w = 16; - m_len = 131; - m_tree_height = 16; - m_name = "XMSS-SHA2_16_512"; - m_hash_name = "SHA-512"; - m_strength = 512; - m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_512; - break; + return XMSS_Parameters(oid, XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_512, 64, 64, 16, 131); + case XMSS_SHA2_20_512: - m_element_size = 64; - m_hash_id_size = 64; - m_w = 16; - m_len = 131; - m_tree_height = 20; - m_name = "XMSS-SHA2_20_512"; - m_hash_name = "SHA-512"; - m_strength = 512; - m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_512; - break; + return XMSS_Parameters(oid, XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_512, 64, 64, 20, 131); + case XMSS_SHAKE_10_256: - m_element_size = 32; - m_hash_id_size = 32; - m_w = 16; - m_len = 67; - m_tree_height = 10; - m_name = "XMSS-SHAKE_10_256"; - m_hash_name = "SHAKE-128(256)"; - m_strength = 256; - m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_256; - break; + return XMSS_Parameters(oid, XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_256, 32, 32, 10, 67); + case XMSS_SHAKE_16_256: - m_element_size = 32; - m_hash_id_size = 32; - m_w = 16; - m_len = 67; - m_tree_height = 16; - m_name = "XMSS-SHAKE_16_256"; - m_hash_name = "SHAKE-128(256)"; - m_strength = 256; - m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_256; - break; + return XMSS_Parameters(oid, XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_256, 32, 32, 16, 67); + case XMSS_SHAKE_20_256: - m_element_size = 32; - m_hash_id_size = 32; - m_w = 16; - m_len = 67; - m_tree_height = 20; - m_name = "XMSS-SHAKE_20_256"; - m_hash_name = "SHAKE-128(256)"; - m_strength = 256; - m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_256; - break; + return XMSS_Parameters(oid, XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_256, 32, 32, 20, 67); + case XMSS_SHAKE_10_512: - m_element_size = 64; - m_hash_id_size = 64; - m_w = 16; - m_len = 131; - m_tree_height = 10; - m_name = "XMSS-SHAKE_10_512"; - m_hash_name = "SHAKE-256(512)"; - m_strength = 512; - m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_512; - break; + return XMSS_Parameters(oid, XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_512, 64, 64, 10, 131); + case XMSS_SHAKE_16_512: - m_element_size = 64; - m_hash_id_size = 64; - m_w = 16; - m_len = 131; - m_tree_height = 16; - m_name = "XMSS-SHAKE_16_512"; - m_hash_name = "SHAKE-256(512)"; - m_strength = 512; - m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_512; - break; + return XMSS_Parameters(oid, XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_512, 64, 64, 16, 131); + case XMSS_SHAKE_20_512: - m_element_size = 64; - m_hash_id_size = 64; - m_w = 16; - m_len = 131; - m_tree_height = 20; - m_name = "XMSS-SHAKE_20_512"; - m_hash_name = "SHAKE-256(512)"; - m_strength = 512; - m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_512; - break; + return XMSS_Parameters(oid, XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_512, 64, 64, 20, 131); + case XMSS_SHA2_10_192: - m_element_size = 24; - m_hash_id_size = 4; - m_w = 16; - m_len = 51; - m_tree_height = 10; - m_name = "XMSS-SHA2_10_192"; - m_hash_name = "Truncated(SHA-256,192)"; - m_strength = 192; - m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_192; - break; + return XMSS_Parameters(oid, XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_192, 24, 4, 10, 51); + case XMSS_SHA2_16_192: - m_element_size = 24; - m_hash_id_size = 4; - m_w = 16; - m_len = 51; - m_tree_height = 16; - m_name = "XMSS-SHA2_16_192"; - m_hash_name = "Truncated(SHA-256,192)"; - m_strength = 192; - m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_192; - break; + return XMSS_Parameters(oid, XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_192, 24, 4, 16, 51); + case XMSS_SHA2_20_192: - m_element_size = 24; - m_hash_id_size = 4; - m_w = 16; - m_len = 51; - m_tree_height = 20; - m_name = "XMSS-SHA2_20_192"; - m_hash_name = "Truncated(SHA-256,192)"; - m_strength = 192; - m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_192; - break; + return XMSS_Parameters(oid, XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHA2_192, 24, 4, 20, 51); + case XMSS_SHAKE256_10_256: - m_element_size = 32; - m_hash_id_size = 32; - m_w = 16; - m_len = 67; - m_tree_height = 10; - m_name = "XMSS-SHAKE256_10_256"; - m_hash_name = "SHAKE-256(256)"; - m_strength = 256; - m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_256_256; - break; + return XMSS_Parameters(oid, XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_256_256, 32, 32, 10, 67); + case XMSS_SHAKE256_16_256: - m_element_size = 32; - m_hash_id_size = 32; - m_w = 16; - m_len = 67; - m_tree_height = 16; - m_name = "XMSS-SHAKE256_16_256"; - m_hash_name = "SHAKE-256(256)"; - m_strength = 256; - m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_256_256; - break; + return XMSS_Parameters(oid, XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_256_256, 32, 32, 16, 67); + case XMSS_SHAKE256_20_256: - m_element_size = 32; - m_hash_id_size = 32; - m_w = 16; - m_len = 67; - m_tree_height = 20; - m_name = "XMSS-SHAKE256_20_256"; - m_hash_name = "SHAKE-256(256)"; - m_strength = 256; - m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_256_256; - break; + return XMSS_Parameters(oid, XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_256_256, 32, 32, 20, 67); + case XMSS_SHAKE256_10_192: - m_element_size = 24; - m_hash_id_size = 4; - m_w = 16; - m_len = 51; - m_tree_height = 10; - m_name = "XMSS-SHAKE256_10_192"; - m_hash_name = "SHAKE-256(192)"; - m_strength = 192; - m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_256_192; - break; + return XMSS_Parameters(oid, XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_256_192, 24, 4, 10, 51); + case XMSS_SHAKE256_16_192: - m_element_size = 24; - m_hash_id_size = 4; - m_w = 16; - m_len = 51; - m_tree_height = 16; - m_name = "XMSS-SHAKE256_16_192"; - m_hash_name = "SHAKE-256(192)"; - m_strength = 192; - m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_256_192; - break; + return XMSS_Parameters(oid, XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_256_192, 24, 4, 16, 51); + case XMSS_SHAKE256_20_192: - m_element_size = 24; - m_hash_id_size = 4; - m_w = 16; - m_len = 51; - m_tree_height = 20; - m_name = "XMSS-SHAKE256_20_192"; - m_hash_name = "SHAKE-256(192)"; - m_strength = 192; - m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_256_192; - break; + return XMSS_Parameters(oid, XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_256_192, 24, 4, 20, 51); default: throw Not_Implemented("Algorithm id does not match any known XMSS algorithm id:" + std::to_string(oid)); diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_parameters.h botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_parameters.h --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_parameters.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_parameters.h 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,7 @@ /* * XMSS Parameters * (C) 2016,2018 Matthias Gierlings + * 2026 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) **/ @@ -8,16 +9,19 @@ #ifndef BOTAN_XMSS_PARAMETERS_H_ #define BOTAN_XMSS_PARAMETERS_H_ -#include -#include - #include #include +#include namespace Botan { +/* +* TODO(Botan4) this header is only needed by xmss.h due to xmss_algorithm_t +* Split xmss_algorithm_t out somehow, and make this header internal +*/ + /** - * Descibes a signature method for XMSS Winternitz One Time Signatures, + * Describes a signature method for XMSS Winternitz One Time Signatures, * as defined in: * [1] XMSS: Extended Hash-Based Signatures, * Request for Comments: 8391 @@ -30,7 +34,7 @@ **/ class BOTAN_PUBLIC_API(2, 0) XMSS_WOTS_Parameters final { public: - enum ots_algorithm_t { + enum ots_algorithm_t : uint32_t /* NOLINT(*-enum-size,*-use-enum-class) */ { // from RFC 8391 WOTSP_SHA2_256 = 0x00000001, @@ -46,29 +50,13 @@ WOTSP_SHAKE_256_192 = 0x00000007, }; - explicit XMSS_WOTS_Parameters(std::string_view algo_name); - XMSS_WOTS_Parameters(ots_algorithm_t ots_spec); + static XMSS_WOTS_Parameters from_id(ots_algorithm_t id); - static ots_algorithm_t xmss_wots_id_from_string(std::string_view param_set); - - /** - * Algorithm 1: convert input string to base. - * - * @param msg Input string (referred to as X in [1]). - * @param out_size size of message in base w. - * - * @return Input string converted to the given base. - **/ - secure_vector base_w(const secure_vector& msg, size_t out_size) const; - - secure_vector base_w(size_t value) const; - - void append_checksum(secure_vector& data) const; - - /** - * @return XMSS WOTS registry name for the chosen parameter set. - **/ - const std::string& name() const { return m_name; } + XMSS_WOTS_Parameters(const XMSS_WOTS_Parameters& other) = default; + XMSS_WOTS_Parameters(XMSS_WOTS_Parameters&& other) noexcept = default; + XMSS_WOTS_Parameters& operator=(const XMSS_WOTS_Parameters& other) = default; + XMSS_WOTS_Parameters& operator=(XMSS_WOTS_Parameters&& other) noexcept = default; + ~XMSS_WOTS_Parameters() = default; /** * Retrieves the uniform length of a message, and the size of @@ -82,10 +70,16 @@ /** * The Winternitz parameter. * - * @return numeric base used for internal representation of - * data. + * @return numeric base used for internal representation of data. + * + * Fixed at 16 for this implementation. **/ - size_t wots_parameter() const { return m_w; } + size_t wots_parameter() const { return 16; } + + /** + * The log2 of wots_parameter + */ + size_t lg_w() const { return 4; } size_t len() const { return m_len; } @@ -93,30 +87,28 @@ size_t len_2() const { return m_len_2; } - size_t lg_w() const { return m_lg_w; } - - ots_algorithm_t oid() const { return m_oid; } + ots_algorithm_t oid() const { return m_id; } - size_t estimated_strength() const { return m_strength; } + // Return estimated workfactor in bits + size_t estimated_strength() const { return 8 * m_element_size; } - bool operator==(const XMSS_WOTS_Parameters& p) const { return m_oid == p.m_oid; } + bool operator==(const XMSS_WOTS_Parameters& p) const { return m_id == p.m_id; } private: - static const std::map m_oid_name_lut; - ots_algorithm_t m_oid; - std::string m_name; - std::string m_hash_name; + static XMSS_WOTS_Parameters from_hash_len(ots_algorithm_t id, size_t hash_len); + + XMSS_WOTS_Parameters(ots_algorithm_t id, size_t hash_len, size_t len, size_t len1, size_t len2) : + m_id(id), m_element_size(hash_len), m_len(len), m_len_1(len1), m_len_2(len2) {} + + ots_algorithm_t m_id{}; size_t m_element_size; - size_t m_w; + size_t m_len; size_t m_len_1; size_t m_len_2; - size_t m_len; - size_t m_strength; - uint8_t m_lg_w; }; /** - * Descibes a signature method for XMSS, as defined in: + * Describes a signature method for XMSS, as defined in: * [1] XMSS: Extended Hash-Based Signatures, * Request for Comments: 8391 * Release: May 2018. @@ -126,9 +118,9 @@ * Release: October 2020. * https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-208.pdf **/ -class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters { +class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final { public: - enum xmss_algorithm_t { + enum xmss_algorithm_t : uint32_t /* NOLINT(*-enum-size,*-use-enum-class) */ { // from RFC 8391 XMSS_SHA2_10_256 = 0x00000001, XMSS_SHA2_16_256 = 0x00000002, @@ -160,15 +152,26 @@ static xmss_algorithm_t xmss_id_from_string(std::string_view algo_name); - explicit XMSS_Parameters(std::string_view algo_name); - explicit XMSS_Parameters(xmss_algorithm_t oid); + BOTAN_DEPRECATED("Use XMSS_Parameters::from_name") explicit XMSS_Parameters(std::string_view algo_name); + + BOTAN_DEPRECATED("Use XMSS_Parameters::from_id") explicit XMSS_Parameters(xmss_algorithm_t oid); + + static XMSS_Parameters from_name(std::string_view algo_name); + + static XMSS_Parameters from_id(xmss_algorithm_t id); + + XMSS_Parameters(const XMSS_Parameters& other) = default; + XMSS_Parameters(XMSS_Parameters&& other) noexcept = default; + XMSS_Parameters& operator=(const XMSS_Parameters& other) = default; + XMSS_Parameters& operator=(XMSS_Parameters&& other) noexcept = default; + ~XMSS_Parameters() = default; /** * @return XMSS registry name for the chosen parameter set. **/ - const std::string& name() const { return m_name; } + std::string_view name() const; - const std::string& hash_function_name() const { return m_hash_name; } + std::string_view hash_function_name() const; /** * Retrieves the uniform length of a message, and the size of @@ -197,7 +200,7 @@ /** * @returns total number of signatures allowed for this XMSS instance */ - size_t total_number_of_signatures() const { return size_t(1) << tree_height(); } + size_t total_number_of_signatures() const { return static_cast(1) << tree_height(); } /** * The Winternitz parameter. @@ -205,7 +208,7 @@ * @return numeric base used for internal representation of * data. **/ - size_t wots_parameter() const { return m_w; } + size_t wots_parameter() const { return 16; } size_t len() const { return m_len; } @@ -213,11 +216,13 @@ XMSS_WOTS_Parameters::ots_algorithm_t ots_oid() const { return m_wots_oid; } + XMSS_WOTS_Parameters wots_parameters() const { return XMSS_WOTS_Parameters::from_id(m_wots_oid); } + /** * Returns the estimated pre-quantum security level of * the chosen algorithm. **/ - size_t estimated_strength() const { return m_strength; } + size_t estimated_strength() const { return 8 * m_element_size; } size_t raw_public_key_size() const { return sizeof(uint32_t) + 2 * element_size(); } @@ -232,16 +237,25 @@ bool operator==(const XMSS_Parameters& p) const { return m_oid == p.m_oid; } private: - xmss_algorithm_t m_oid; + XMSS_Parameters(xmss_algorithm_t oid, + XMSS_WOTS_Parameters::ots_algorithm_t wots_oid, + size_t hash_len, + size_t hash_id_size, + size_t tree_height, + size_t len) : + m_oid(oid), + m_wots_oid(wots_oid), + m_element_size(hash_len), + m_hash_id_size(hash_id_size), + m_tree_height(tree_height), + m_len(len) {} + + xmss_algorithm_t m_oid{}; XMSS_WOTS_Parameters::ots_algorithm_t m_wots_oid; - std::string m_name; - std::string m_hash_name; size_t m_element_size; size_t m_hash_id_size; size_t m_tree_height; - size_t m_w; size_t m_len; - size_t m_strength; }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_privatekey.cpp botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_privatekey.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_privatekey.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_privatekey.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ /* * XMSS Private Key - * An XMSS: Extended Hash-Based Siganture private key. + * An XMSS: Extended Hash-Based Signature private key. * The XMSS private key does not support the X509 and PKCS7 standard. Instead * the raw format described in [1] is used. * @@ -10,7 +10,7 @@ * https://datatracker.ietf.org/doc/rfc8391/ * * (C) 2016,2017,2018 Matthias Gierlings - * (C) 2019 Jack Lloyd + * (C) 2019,2026 Jack Lloyd * (C) 2023 René Meusel - Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) @@ -20,12 +20,15 @@ #include #include +#include +#include +#include +#include #include -#include +#include #include -#include +#include #include -#include #if defined(BOTAN_HAS_THREAD_UTILS) #include @@ -45,8 +48,7 @@ key_bits.size() == xmss_params.raw_legacy_private_key_size()) { raw_key.assign(key_bits.begin(), key_bits.end()); } else { - DataSource_Memory src(key_bits); - BER_Decoder(src).decode(raw_key, ASN1_Type::OctetString).verify_end(); + BER_Decoder(key_bits, BER_Decoder::Limits::DER()).decode(raw_key, ASN1_Type::OctetString).verify_end(); } return raw_key; @@ -54,40 +56,33 @@ } // namespace -class XMSS_PrivateKey_Internal { +class XMSS_PrivateKey_Internal final { public: - XMSS_PrivateKey_Internal(const XMSS_Parameters& xmss_params, - const XMSS_WOTS_Parameters& wots_params, + XMSS_PrivateKey_Internal(XMSS_Parameters::xmss_algorithm_t xmss_algo_id, WOTS_Derivation_Method wots_derivation_method, RandomNumberGenerator& rng) : - m_xmss_params(xmss_params), - m_wots_params(wots_params), + m_xmss_params(XMSS_Parameters::from_id(xmss_algo_id)), + m_wots_params(m_xmss_params.wots_parameters()), m_wots_derivation_method(wots_derivation_method), - m_hash(xmss_params), - m_prf(rng.random_vec(xmss_params.element_size())), - m_private_seed(rng.random_vec(xmss_params.element_size())), - m_index_reg(XMSS_Index_Registry::get_instance()) {} + m_prf(rng.random_vec(m_xmss_params.element_size())), + m_private_seed(rng.random_vec(m_xmss_params.element_size())), + m_keyid(Stateful_Key_Index_Registry::KeyId("XMSS", m_xmss_params.oid(), m_private_seed, m_prf)) {} - XMSS_PrivateKey_Internal(const XMSS_Parameters& xmss_params, - const XMSS_WOTS_Parameters& wots_params, + XMSS_PrivateKey_Internal(XMSS_Parameters::xmss_algorithm_t xmss_algo_id, WOTS_Derivation_Method wots_derivation_method, secure_vector private_seed, secure_vector prf) : - m_xmss_params(xmss_params), - m_wots_params(wots_params), + m_xmss_params(XMSS_Parameters::from_id(xmss_algo_id)), + m_wots_params(m_xmss_params.wots_parameters()), m_wots_derivation_method(wots_derivation_method), - m_hash(m_xmss_params), m_prf(std::move(prf)), m_private_seed(std::move(private_seed)), - m_index_reg(XMSS_Index_Registry::get_instance()) {} + m_keyid(Stateful_Key_Index_Registry::KeyId("XMSS", m_xmss_params.oid(), m_private_seed, m_prf)) {} - XMSS_PrivateKey_Internal(const XMSS_Parameters& xmss_params, - const XMSS_WOTS_Parameters& wots_params, - std::span key_bits) : - m_xmss_params(xmss_params), - m_wots_params(wots_params), - m_hash(m_xmss_params), - m_index_reg(XMSS_Index_Registry::get_instance()) { + XMSS_PrivateKey_Internal(XMSS_Parameters::xmss_algorithm_t xmss_algo_id, std::span key_bits) : + m_xmss_params(XMSS_Parameters::from_id(xmss_algo_id)), + m_wots_params(m_xmss_params.wots_parameters()), + m_keyid(/* initialized later*/) { /* The code requires sizeof(size_t) >= ceil(tree_height / 8) @@ -98,7 +93,7 @@ */ static_assert(sizeof(size_t) >= 4, "size_t is big enough to support leaf index"); - const secure_vector raw_key = extract_raw_private_key(key_bits, xmss_params); + const secure_vector raw_key = extract_raw_private_key(key_bits, m_xmss_params); if(raw_key.size() != m_xmss_params.raw_private_key_size() && raw_key.size() != m_xmss_params.raw_legacy_private_key_size()) { @@ -111,13 +106,17 @@ s.skip(m_xmss_params.raw_public_key_size()); auto unused_leaf_bytes = s.take(sizeof(uint32_t)); - size_t unused_leaf = load_be(unused_leaf_bytes.data(), 0); + const size_t unused_leaf = load_be(unused_leaf_bytes.data(), 0); if(unused_leaf >= (1ULL << m_xmss_params.tree_height())) { throw Decoding_Error("XMSS private key leaf index out of bounds"); } m_prf = s.copy_as_secure_vector(m_xmss_params.element_size()); m_private_seed = s.copy_as_secure_vector(m_xmss_params.element_size()); + + m_keyid = Stateful_Key_Index_Registry::KeyId("XMSS", m_xmss_params.oid(), m_private_seed, m_prf); + + // Note m_keyid must be initialized before set_unused_leaf_index is called! set_unused_leaf_index(unused_leaf); // Legacy keys generated prior to Botan 3.x don't feature a @@ -139,8 +138,6 @@ raw_public_key, unused_index, m_prf, m_private_seed, wots_derivation_method); } - XMSS_Hash& hash() { return m_hash; } - const secure_vector& prf_value() const { return m_prf; } const secure_vector& private_seed() { return m_private_seed; } @@ -149,43 +146,31 @@ WOTS_Derivation_Method wots_derivation_method() const { return m_wots_derivation_method; } - XMSS_Index_Registry& index_registry() { return m_index_reg; } - - std::shared_ptr> recover_global_leaf_index() const { - BOTAN_ASSERT( - m_private_seed.size() == m_xmss_params.element_size() && m_prf.size() == m_xmss_params.element_size(), - "Trying to retrieve index for partially initialized key"); - return m_index_reg.get(m_private_seed, m_prf); - } - void set_unused_leaf_index(size_t idx) { if(idx >= (1ULL << m_xmss_params.tree_height())) { throw Decoding_Error("XMSS private key leaf index out of bounds"); } else { - std::atomic& index = static_cast&>(*recover_global_leaf_index()); - size_t current = 0; - - do { - current = index.load(); - if(current > idx) { - return; - } - } while(!index.compare_exchange_strong(current, idx)); + Stateful_Key_Index_Registry::global().set_index_lower_bound(m_keyid, idx); } } size_t reserve_unused_leaf_index() { - size_t idx = (static_cast&>(*recover_global_leaf_index())).fetch_add(1); + const uint64_t idx = Stateful_Key_Index_Registry::global().reserve_next_index(m_keyid); if(idx >= m_xmss_params.total_number_of_signatures()) { - throw Decoding_Error("XMSS private key, one time signatures exhaused"); + throw Decoding_Error("XMSS private key, one time signatures exhausted"); } - return idx; + // Cast is safe even on 32 bit since total_number_of_signatures will be less + return static_cast(idx); } - size_t unused_leaf_index() const { return *recover_global_leaf_index(); } + size_t unused_leaf_index() const { + const uint64_t idx = Stateful_Key_Index_Registry::global().current_index(m_keyid); + return checked_cast_to(idx); + } - size_t remaining_signatures() const { - return m_xmss_params.total_number_of_signatures() - *recover_global_leaf_index(); + uint64_t remaining_signatures() const { + const size_t max = m_xmss_params.total_number_of_signatures(); + return Stateful_Key_Index_Registry::global().remaining_operations(m_keyid, max); } private: @@ -193,23 +178,23 @@ XMSS_WOTS_Parameters m_wots_params; WOTS_Derivation_Method m_wots_derivation_method; - XMSS_Hash m_hash; secure_vector m_prf; secure_vector m_private_seed; - XMSS_Index_Registry& m_index_reg; + Stateful_Key_Index_Registry::KeyId m_keyid; }; XMSS_PrivateKey::XMSS_PrivateKey(std::span key_bits) : XMSS_PublicKey(key_bits), - m_private(std::make_shared(m_xmss_params, m_wots_params, key_bits)) {} + m_private(std::make_shared(xmss_parameters().oid(), key_bits)) {} XMSS_PrivateKey::XMSS_PrivateKey(XMSS_Parameters::xmss_algorithm_t xmss_algo_id, RandomNumberGenerator& rng, WOTS_Derivation_Method wots_derivation_method) : XMSS_PublicKey(xmss_algo_id, rng), - m_private(std::make_shared(m_xmss_params, m_wots_params, wots_derivation_method, rng)) { - XMSS_Address adrs; - m_root = tree_hash(0, XMSS_PublicKey::m_xmss_params.tree_height(), adrs); + m_private(std::make_shared(xmss_algo_id, wots_derivation_method, rng)) { + const XMSS_Address adrs; + XMSS_Hash hash(xmss_parameters()); + set_root(tree_hash(0, xmss_parameters().tree_height(), adrs, hash)); } XMSS_PrivateKey::XMSS_PrivateKey(XMSS_Parameters::xmss_algorithm_t xmss_algo_id, @@ -221,21 +206,24 @@ WOTS_Derivation_Method wots_derivation_method) : XMSS_PublicKey(xmss_algo_id, std::move(root), std::move(public_seed)), m_private(std::make_shared( - m_xmss_params, m_wots_params, wots_derivation_method, std::move(wots_priv_seed), std::move(prf))) { + xmss_algo_id, wots_derivation_method, std::move(wots_priv_seed), std::move(prf))) { m_private->set_unused_leaf_index(idx_leaf); - BOTAN_ARG_CHECK(m_private->prf_value().size() == m_xmss_params.element_size(), + BOTAN_ARG_CHECK(m_private->prf_value().size() == xmss_parameters().element_size(), "XMSS: unexpected byte length of PRF value"); - BOTAN_ARG_CHECK(m_private->private_seed().size() == m_xmss_params.element_size(), + BOTAN_ARG_CHECK(m_private->private_seed().size() == xmss_parameters().element_size(), "XMSS: unexpected byte length of private seed"); } -secure_vector XMSS_PrivateKey::tree_hash(size_t start_idx, size_t target_node_height, XMSS_Address& adrs) { +secure_vector XMSS_PrivateKey::tree_hash(size_t start_idx, + size_t target_node_height, + const XMSS_Address& adrs, + XMSS_Hash& hash) const { BOTAN_ASSERT_NOMSG(target_node_height <= 30); BOTAN_ASSERT((start_idx % (static_cast(1) << target_node_height)) == 0, "Start index must be divisible by 2^{target node height}."); #if defined(BOTAN_HAS_THREAD_UTILS) - // dertermine number of parallel tasks to split the tree_hashing into. + // determine number of parallel tasks to split the tree_hashing into. Thread_Pool& thread_pool = Thread_Pool::global_instance(); @@ -244,7 +232,8 @@ // skip parallelization overhead for leaf nodes. if(split_level == 0) { secure_vector result; - tree_hash_subtree(result, start_idx, target_node_height, adrs); + XMSS_Address subtree_addr(adrs); + tree_hash_subtree(result, start_idx, target_node_height, subtree_addr, hash); return result; } @@ -258,18 +247,17 @@ "Number of worker threads in tree_hash need to divide range " "of calculated nodes."); - std::vector> nodes(subtrees, - secure_vector(XMSS_PublicKey::m_xmss_params.element_size())); + std::vector> nodes(subtrees, secure_vector(xmss_parameters().element_size())); std::vector node_addresses(subtrees, adrs); - std::vector xmss_hash(subtrees, m_private->hash()); + std::vector xmss_hash(subtrees, hash); std::vector> work; // Calculate multiple subtrees in parallel. for(size_t i = 0; i < subtrees; i++) { using tree_hash_subtree_fn_t = - void (XMSS_PrivateKey::*)(secure_vector&, size_t, size_t, XMSS_Address&, XMSS_Hash&); + void (XMSS_PrivateKey::*)(secure_vector&, size_t, size_t, XMSS_Address&, XMSS_Hash&) const; - tree_hash_subtree_fn_t work_fn = &XMSS_PrivateKey::tree_hash_subtree; + const tree_hash_subtree_fn_t work_fn = &XMSS_PrivateKey::tree_hash_subtree; work.push_back(thread_pool.run(work_fn, this, @@ -300,10 +288,10 @@ std::ref(nodes[i]), std::cref(ro_nodes[2 * i]), std::cref(ro_nodes[2 * i + 1]), - std::ref(node_addresses[i]), + node_addresses[i], std::cref(this->public_seed()), std::ref(xmss_hash[i]), - std::cref(m_xmss_params))); + std::cref(xmss_parameters()))); } for(auto& w : work) { @@ -316,11 +304,12 @@ node_addresses[0].set_tree_height(static_cast(target_node_height - 1)); node_addresses[0].set_tree_index((node_addresses[1].get_tree_index() - 1) >> 1); XMSS_Common_Ops::randomize_tree_hash( - nodes[0], nodes[0], nodes[1], node_addresses[0], this->public_seed(), m_private->hash(), m_xmss_params); + nodes[0], nodes[0], nodes[1], node_addresses[0], this->public_seed(), hash, xmss_parameters()); return nodes[0]; #else secure_vector result; - tree_hash_subtree(result, start_idx, target_node_height, adrs, m_private->hash()); + XMSS_Address subtree_addr(adrs); + tree_hash_subtree(result, start_idx, target_node_height, subtree_addr, hash); return result; #endif } @@ -328,16 +317,12 @@ void XMSS_PrivateKey::tree_hash_subtree(secure_vector& result, size_t start_idx, size_t target_node_height, - XMSS_Address& adrs) { - return tree_hash_subtree(result, start_idx, target_node_height, adrs, m_private->hash()); -} - -void XMSS_PrivateKey::tree_hash_subtree( - secure_vector& result, size_t start_idx, size_t target_node_height, XMSS_Address& adrs, XMSS_Hash& hash) { + XMSS_Address& adrs, + XMSS_Hash& hash) const { const secure_vector& seed = this->public_seed(); std::vector> nodes(target_node_height + 1, - secure_vector(XMSS_PublicKey::m_xmss_params.element_size())); + secure_vector(xmss_parameters().element_size())); // node stack, holds all nodes on stack and one extra "pending" node. This // temporary node referred to as "node" in the XMSS standard document stays @@ -352,11 +337,11 @@ adrs.set_type(XMSS_Address::Type::OTS_Hash_Address); adrs.set_ots_address(static_cast(i)); - XMSS_WOTS_PublicKey pk = this->wots_public_key_for(adrs, hash); + const XMSS_WOTS_PublicKey pk = this->wots_public_key_for(adrs, hash); adrs.set_type(XMSS_Address::Type::LTree_Address); adrs.set_ltree_address(static_cast(i)); - XMSS_Common_Ops::create_l_tree(nodes[level], pk.key_data(), adrs, seed, hash, m_xmss_params); + XMSS_Common_Ops::create_l_tree(nodes[level], pk.key_data(), adrs, seed, hash, xmss_parameters()); node_levels[level] = 0; adrs.set_type(XMSS_Address::Type::Hash_Tree_Address); @@ -366,7 +351,7 @@ while(level > 0 && node_levels[level] == node_levels[level - 1]) { adrs.set_tree_index(((adrs.get_tree_index() - 1) >> 1)); XMSS_Common_Ops::randomize_tree_hash( - nodes[level - 1], nodes[level - 1], nodes[level], adrs, seed, hash, m_xmss_params); + nodes[level - 1], nodes[level - 1], nodes[level], adrs, seed, hash, xmss_parameters()); node_levels[level - 1]++; level--; //Pop stack top element adrs.set_tree_height(adrs.get_tree_height() + 1); @@ -376,16 +361,16 @@ result = nodes[level - 1]; } -XMSS_WOTS_PublicKey XMSS_PrivateKey::wots_public_key_for(XMSS_Address& adrs, XMSS_Hash& hash) const { +XMSS_WOTS_PublicKey XMSS_PrivateKey::wots_public_key_for(const XMSS_Address& adrs, XMSS_Hash& hash) const { const auto private_key = wots_private_key_for(adrs, hash); - return XMSS_WOTS_PublicKey(m_private->wots_parameters(), m_public_seed, private_key, adrs, hash); + return XMSS_WOTS_PublicKey(m_private->wots_parameters(), public_seed(), private_key, adrs, hash); } -XMSS_WOTS_PrivateKey XMSS_PrivateKey::wots_private_key_for(XMSS_Address& adrs, XMSS_Hash& hash) const { +XMSS_WOTS_PrivateKey XMSS_PrivateKey::wots_private_key_for(const XMSS_Address& adrs, XMSS_Hash& hash) const { switch(wots_derivation_method()) { case WOTS_Derivation_Method::NIST_SP800_208: return XMSS_WOTS_PrivateKey( - m_private->wots_parameters(), m_public_seed, m_private->private_seed(), adrs, hash); + m_private->wots_parameters(), public_seed(), m_private->private_seed(), adrs, hash); case WOTS_Derivation_Method::Botan2x: return XMSS_WOTS_PrivateKey(m_private->wots_parameters(), m_private->private_seed(), adrs, hash); } @@ -406,7 +391,7 @@ } size_t XMSS_PrivateKey::remaining_signatures() const { - return m_private->remaining_signatures(); + return checked_cast_to(m_private->remaining_signatures()); } std::optional XMSS_PrivateKey::remaining_operations() const { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_publickey.cpp botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_publickey.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_publickey.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_publickey.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ /* * XMSS Public Key - * An XMSS: Extended Hash-Based Siganture public key. + * An XMSS: Extended Hash-Based Signature public key. * The XMSS public key does not support the X509 standard. Instead the * raw format described in [1] is used. * @@ -18,12 +18,12 @@ #include #include +#include +#include +#include #include -#include #include -#include - namespace Botan { namespace { @@ -46,13 +46,12 @@ std::vector extract_raw_public_key(std::span key_bits) { std::vector raw_key; try { - DataSource_Memory src(key_bits); - BER_Decoder(src).decode(raw_key, ASN1_Type::OctetString).verify_end(); + BER_Decoder(key_bits, BER_Decoder::Limits::DER()).decode(raw_key, ASN1_Type::OctetString).verify_end(); - // Smoke check the decoded key. Valid raw keys might be decodeable as BER + // Smoke check the decoded key. Valid raw keys might be decodable as BER // and they might be either a sole public key or a concatenation of public // and private key (with the optional WOTS+ derivation identifier). - XMSS_Parameters params(deserialize_xmss_oid(raw_key)); + const XMSS_Parameters params = XMSS_Parameters::from_id(deserialize_xmss_oid(raw_key)); if(raw_key.size() != params.raw_public_key_size() && raw_key.size() != params.raw_private_key_size() && raw_key.size() != params.raw_legacy_private_key_size()) { throw Decoding_Error("unpacked XMSS key does not have the correct length"); @@ -68,36 +67,100 @@ } // namespace -XMSS_PublicKey::XMSS_PublicKey(XMSS_Parameters::xmss_algorithm_t xmss_oid, RandomNumberGenerator& rng) : - m_xmss_params(xmss_oid), - m_wots_params(m_xmss_params.ots_oid()), - m_root(m_xmss_params.element_size()), - m_public_seed(rng.random_vec(m_xmss_params.element_size())) {} - -XMSS_PublicKey::XMSS_PublicKey(std::span key_bits) : - m_raw_key(extract_raw_public_key(key_bits)), - m_xmss_params(deserialize_xmss_oid(m_raw_key)), - m_wots_params(m_xmss_params.ots_oid()) { - if(m_raw_key.size() < m_xmss_params.raw_public_key_size()) { +class XMSS_PublicKey_Internal final { + public: + XMSS_PublicKey_Internal(const XMSS_Parameters& params, + secure_vector root, + secure_vector public_seed) : + m_xmss_params(params), + m_wots_params(m_xmss_params.wots_parameters()), + m_root(std::move(root)), + m_public_seed(std::move(public_seed)) {} + + const XMSS_Parameters& xmss_parameters() const { return m_xmss_params; } + + const XMSS_WOTS_Parameters& wots_parameters() const { return m_wots_params; } + + const secure_vector& root() const { return m_root; } + + const secure_vector& public_seed() const { return m_public_seed; } + + void set_root(secure_vector root) { m_root = std::move(root); } + + std::vector raw_public_key_bits() const { + return concat>( + store_be(static_cast(m_xmss_params.oid())), m_root, m_public_seed); + } + + private: + XMSS_Parameters m_xmss_params; + XMSS_WOTS_Parameters m_wots_params; + secure_vector m_root; + secure_vector m_public_seed; +}; + +XMSS_PublicKey::XMSS_PublicKey(XMSS_Parameters::xmss_algorithm_t xmss_oid, RandomNumberGenerator& rng) { + const auto params = XMSS_Parameters::from_id(xmss_oid); + m_public_key = std::make_shared( + params, secure_vector(params.element_size()), rng.random_vec(params.element_size())); +} + +XMSS_PublicKey::XMSS_PublicKey(std::span key_bits) { + const auto raw_key = extract_raw_public_key(key_bits); + const auto xmss_oid = deserialize_xmss_oid(raw_key); + const auto params = XMSS_Parameters::from_id(xmss_oid); + if(raw_key.size() < params.raw_public_key_size()) { throw Decoding_Error("Invalid XMSS public key size detected"); } - BufferSlicer s(m_raw_key); + BufferSlicer s(raw_key); s.skip(4 /* algorithm ID -- already consumed by `deserialize_xmss_oid()` */); - m_root = s.copy_as_secure_vector(m_xmss_params.element_size()); - m_public_seed = s.copy_as_secure_vector(m_xmss_params.element_size()); + auto root = s.copy_as_secure_vector(params.element_size()); + auto public_seed = s.copy_as_secure_vector(params.element_size()); + + m_public_key = std::make_shared(params, std::move(root), std::move(public_seed)); } XMSS_PublicKey::XMSS_PublicKey(XMSS_Parameters::xmss_algorithm_t xmss_oid, secure_vector root, - secure_vector public_seed) : - m_xmss_params(xmss_oid), - m_wots_params(m_xmss_params.ots_oid()), - m_root(std::move(root)), - m_public_seed(std::move(public_seed)) { - BOTAN_ARG_CHECK(m_root.size() == m_xmss_params.element_size(), "XMSS: unexpected byte length of root hash"); - BOTAN_ARG_CHECK(m_public_seed.size() == m_xmss_params.element_size(), "XMSS: unexpected byte length of public seed"); + secure_vector public_seed) { + const auto params = XMSS_Parameters::from_id(xmss_oid); + BOTAN_ARG_CHECK(root.size() == params.element_size(), "XMSS: unexpected byte length of root hash"); + BOTAN_ARG_CHECK(public_seed.size() == params.element_size(), "XMSS: unexpected byte length of public seed"); + m_public_key = std::make_shared(params, std::move(root), std::move(public_seed)); +} + +const secure_vector& XMSS_PublicKey::public_seed() const { + return m_public_key->public_seed(); +} + +const secure_vector& XMSS_PublicKey::root() const { + return m_public_key->root(); +} + +const XMSS_Parameters& XMSS_PublicKey::xmss_parameters() const { + return m_public_key->xmss_parameters(); +} + +void XMSS_PublicKey::set_root(secure_vector root) { + m_public_key->set_root(std::move(root)); +} + +size_t XMSS_PublicKey::estimated_strength() const { + return xmss_parameters().estimated_strength(); +} + +size_t XMSS_PublicKey::key_length() const { + return xmss_parameters().estimated_strength(); +} + +bool XMSS_PublicKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const { + // The public key consists of (OID, root hash, public seed). The OID is + // validated and the byte lengths of root and public_seed are verified + // against the parameter set during deserialization. These are opaque + // hash outputs with no further structural invariants to check. + return true; } std::unique_ptr XMSS_PublicKey::create_verification_op(std::string_view /*params*/, @@ -120,7 +183,7 @@ } std::vector XMSS_PublicKey::raw_public_key_bits() const { - return concat>(store_be(static_cast(m_xmss_params.oid())), m_root, m_public_seed); + return m_public_key->raw_public_key_bits(); } std::vector XMSS_PublicKey::public_key_bits() const { @@ -137,7 +200,7 @@ // Note: Given only an XMSS public key we cannot know which WOTS key // derivation method was used to build the XMSS tree. Hence, we have to // use the default here. - return std::make_unique(m_xmss_params.oid(), rng); + return std::make_unique(xmss_parameters().oid(), rng); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_signature.cpp botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_signature.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_signature.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_signature.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,10 +12,9 @@ XMSS_Signature::XMSS_Signature(XMSS_Parameters::xmss_algorithm_t oid, std::span raw_sig) : m_leaf_idx(0), m_randomness(0, 0x00) { - XMSS_Parameters xmss_params(oid); + const auto params = XMSS_Parameters::from_id(oid); - if(raw_sig.size() != - (xmss_params.len() + xmss_params.tree_height() + 1) * xmss_params.element_size() + sizeof(uint32_t)) { + if(raw_sig.size() != (params.len() + params.tree_height() + 1) * params.element_size() + sizeof(uint32_t)) { throw Decoding_Error("XMSS signature size invalid."); } @@ -23,27 +22,27 @@ m_leaf_idx = ((m_leaf_idx << 8) | raw_sig[i]); } - if(m_leaf_idx >= xmss_params.total_number_of_signatures()) { + if(m_leaf_idx >= params.total_number_of_signatures()) { throw Decoding_Error("XMSS signature leaf index out of bounds."); } auto begin = raw_sig.begin() + sizeof(uint32_t); - auto end = begin + xmss_params.element_size(); + auto end = begin + params.element_size(); std::copy(begin, end, std::back_inserter(m_randomness)); - for(size_t i = 0; i < xmss_params.len(); i++) { + for(size_t i = 0; i < params.len(); i++) { begin = end; - end = begin + xmss_params.element_size(); + end = begin + params.element_size(); m_tree_sig.ots_signature.push_back(secure_vector(0)); - m_tree_sig.ots_signature.back().reserve(xmss_params.element_size()); + m_tree_sig.ots_signature.back().reserve(params.element_size()); std::copy(begin, end, std::back_inserter(m_tree_sig.ots_signature.back())); } - for(size_t i = 0; i < xmss_params.tree_height(); i++) { + for(size_t i = 0; i < params.tree_height(); i++) { begin = end; - end = begin + xmss_params.element_size(); + end = begin + params.element_size(); m_tree_sig.authentication_path.push_back(secure_vector(0)); - m_tree_sig.authentication_path.back().reserve(xmss_params.element_size()); + m_tree_sig.authentication_path.back().reserve(params.element_size()); std::copy(begin, end, std::back_inserter(m_tree_sig.authentication_path.back())); } } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_signature.h botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_signature.h --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_signature.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_signature.h 2026-05-07 01:38:28.000000000 +0000 @@ -13,7 +13,6 @@ #include #include #include -#include namespace Botan { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_signature_operation.cpp botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_signature_operation.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_signature_operation.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_signature_operation.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ * https://datatracker.ietf.org/doc/rfc8391/ * * (C) 2016,2017,2018 Matthias Gierlings + * 2026 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) **/ @@ -26,56 +27,44 @@ m_leaf_idx(0), m_is_initialized(false) {} -XMSS_Signature::TreeSignature XMSS_Signature_Operation::generate_tree_signature(const secure_vector& msg, - XMSS_PrivateKey& xmss_priv_key, - XMSS_Address& adrs) { - XMSS_Signature::TreeSignature result; - - result.authentication_path = build_auth_path(xmss_priv_key, adrs); - adrs.set_type(XMSS_Address::Type::OTS_Hash_Address); - adrs.set_ots_address(m_leaf_idx); - - result.ots_signature = - xmss_priv_key.wots_private_key_for(adrs, m_hash).sign(msg, xmss_priv_key.public_seed(), adrs, m_hash); - - return result; -} - -XMSS_Signature XMSS_Signature_Operation::sign(const secure_vector& msg_hash, XMSS_PrivateKey& xmss_priv_key) { - XMSS_Address adrs; - XMSS_Signature sig(m_leaf_idx, m_randomness, generate_tree_signature(msg_hash, xmss_priv_key, adrs)); - return sig; -} - size_t XMSS_Signature_Operation::signature_length() const { const auto& params = m_priv_key.xmss_parameters(); return sizeof(uint64_t) + // size of leaf index params.element_size() + params.len() * params.element_size() + params.tree_height() * params.element_size(); } -wots_keysig_t XMSS_Signature_Operation::build_auth_path(XMSS_PrivateKey& priv_key, XMSS_Address& adrs) { +void XMSS_Signature_Operation::update(std::span input) { + initialize(); + m_hash.h_msg_update(input); +} + +std::vector XMSS_Signature_Operation::sign(RandomNumberGenerator& /*rng*/) { + initialize(); + + const auto msg_hash = m_hash.h_msg_final(); + const auto& params = m_priv_key.xmss_parameters(); wots_keysig_t auth_path(params.tree_height()); + + XMSS_Address adrs; adrs.set_type(XMSS_Address::Type::Hash_Tree_Address); for(size_t j = 0; j < params.tree_height(); j++) { - size_t k = (m_leaf_idx / (static_cast(1) << j)) ^ 0x01; - auth_path[j] = priv_key.tree_hash(k * (static_cast(1) << j), j, adrs); + const size_t k = (m_leaf_idx / (static_cast(1) << j)) ^ 0x01; + auth_path[j] = m_priv_key.tree_hash(k * (static_cast(1) << j), j, adrs, m_hash); } - return auth_path; -} + adrs.set_type(XMSS_Address::Type::OTS_Hash_Address); + adrs.set_ots_address(m_leaf_idx); -void XMSS_Signature_Operation::update(std::span input) { - initialize(); - m_hash.h_msg_update(input); -} + XMSS_Signature::TreeSignature tree_sig; + tree_sig.authentication_path = auth_path; + tree_sig.ots_signature = + m_priv_key.wots_private_key_for(adrs, m_hash).sign(msg_hash, m_priv_key.public_seed(), adrs, m_hash); -std::vector XMSS_Signature_Operation::sign(RandomNumberGenerator& /*rng*/) { - initialize(); - auto sig = sign(m_hash.h_msg_final(), m_priv_key).bytes(); + const XMSS_Signature sig(m_leaf_idx, m_randomness, tree_sig); m_is_initialized = false; - return sig; + return sig.bytes(); } void XMSS_Signature_Operation::initialize() { @@ -90,10 +79,10 @@ m_leaf_idx = static_cast(m_priv_key.reserve_unused_leaf_index()); // write prefix for message hashing into buffer. - XMSS_Tools::concat(index_bytes, m_leaf_idx, 32); + xmss_concat(index_bytes, m_leaf_idx, 32); m_hash.prf(m_randomness, m_priv_key.prf_value(), index_bytes); index_bytes.clear(); - XMSS_Tools::concat(index_bytes, m_leaf_idx, m_priv_key.xmss_parameters().element_size()); + xmss_concat(index_bytes, m_leaf_idx, m_priv_key.xmss_parameters().element_size()); m_hash.h_msg_init(m_randomness, m_priv_key.root(), index_bytes); m_is_initialized = true; } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_signature_operation.h botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_signature_operation.h --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_signature_operation.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_signature_operation.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -27,7 +28,7 @@ **/ class XMSS_Signature_Operation final : public virtual PK_Ops::Signature { public: - XMSS_Signature_Operation(const XMSS_PrivateKey& private_key); + explicit XMSS_Signature_Operation(const XMSS_PrivateKey& private_key); /** * Creates an XMSS signature for the message provided through call to @@ -35,7 +36,7 @@ * * @return serialized XMSS signature. **/ - std::vector sign(RandomNumberGenerator&) override; + std::vector sign(RandomNumberGenerator& rng) override; void update(std::span input) override; @@ -46,32 +47,6 @@ std::string hash_function() const override { return m_hash.hash_function(); } private: - /** - * Algorithm 11: "treeSig" - * Generate a WOTS+ signature on a message with corresponding auth path. - * - * @param msg A message. - * @param xmss_priv_key A XMSS private key. - * @param adrs A XMSS Address. - **/ - XMSS_Signature::TreeSignature generate_tree_signature(const secure_vector& msg, - XMSS_PrivateKey& xmss_priv_key, - XMSS_Address& adrs); - - /** - * Algorithm 12: "XMSS_sign" - * Generate an XMSS signature and update the XMSS secret key - * - * @param msg A message to sign of arbitrary length. - * @param [out] xmss_priv_key A XMSS private key. The private key will be - * updated during the signing process. - * - * @return The signature of msg signed using xmss_priv_key. - **/ - XMSS_Signature sign(const secure_vector& msg, XMSS_PrivateKey& xmss_priv_key); - - wots_keysig_t build_auth_path(XMSS_PrivateKey& priv_key, XMSS_Address& adrs); - void initialize(); XMSS_PrivateKey m_priv_key; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_tools.h botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_tools.h --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_tools.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_tools.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,71 +9,56 @@ #define BOTAN_XMSS_TOOLS_H_ #include -#include +#include +#include +#include #include -#include namespace Botan { /** - * Helper tools for low level byte operations required - * for the XMSS implementation. - **/ -class XMSS_Tools final { - public: - XMSS_Tools() = delete; - XMSS_Tools(const XMSS_Tools&) = delete; - void operator=(const XMSS_Tools&) = delete; - - /** - * Concatenates the byte representation in big-endian order of any - * integral value to a secure_vector. - * - * @param target Vector to concatenate the byte representation of the - * integral value to. - * @param src integral value to concatenate. - **/ - template ::value, void>::type> - static void concat(secure_vector& target, const T& src); - - /** - * Concatenates the last n bytes of the byte representation in big-endian - * order of any integral value to a to a secure_vector. - * - * @param target Vector to concatenate the byte representation of the - * integral value to. - * @param src Integral value to concatenate. - * @param len number of bytes to concatenate. This value must be smaller - * or equal to the size of type T. - **/ - template ::value, void>::type> - static void concat(secure_vector& target, const T& src, size_t len); -}; - -template -void XMSS_Tools::concat(secure_vector& target, const T& src) { +* Concatenates the byte representation in big-endian order of any +* integral value to a secure_vector. +* +* @param target Vector to concatenate the byte representation of the +* integral value to. +* @param src integral value to concatenate. +**/ +template +void xmss_concat(secure_vector& target, const T& src) { const uint8_t* src_bytes = reinterpret_cast(&src); - if(CPUID::is_little_endian()) { + if constexpr(std::endian::native == std::endian::little) { std::reverse_copy(src_bytes, src_bytes + sizeof(src), std::back_inserter(target)); } else { std::copy(src_bytes, src_bytes + sizeof(src), std::back_inserter(target)); } } -template -void XMSS_Tools::concat(secure_vector& target, const T& src, size_t len) { - size_t c = static_cast(std::min(len, sizeof(src))); +/** +* Concatenates the last n bytes of the byte representation in big-endian +* order of any integral value to a to a secure_vector. +* +* @param target Vector to concatenate the byte representation of the +* integral value to. +* @param src Integral value to concatenate. +* @param len number of bytes to concatenate. This value must be smaller +* or equal to the size of type T. +**/ +template +void xmss_concat(secure_vector& target, const T& src, size_t len) { + const size_t c = static_cast(std::min(len, sizeof(src))); if(len > sizeof(src)) { target.resize(target.size() + len - sizeof(src), 0); } const uint8_t* src_bytes = reinterpret_cast(&src); - if(CPUID::is_little_endian()) { + if constexpr(std::endian::native == std::endian::little) { std::reverse_copy(src_bytes, src_bytes + c, std::back_inserter(target)); } else { std::copy(src_bytes + sizeof(src) - c, src_bytes + sizeof(src), std::back_inserter(target)); } } + } // namespace Botan #endif diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_verification_operation.cpp botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_verification_operation.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_verification_operation.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_verification_operation.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -21,15 +21,16 @@ secure_vector XMSS_Verification_Operation::root_from_signature(const XMSS_Signature& sig, const secure_vector& msg, - XMSS_Address& adrs, const secure_vector& seed) { const auto& params = m_pub_key.xmss_parameters(); const uint32_t next_index = static_cast(sig.unused_leaf_index()); + XMSS_Address adrs; adrs.set_type(XMSS_Address::Type::OTS_Hash_Address); adrs.set_ots_address(next_index); - XMSS_WOTS_PublicKey pub_key_ots(params.ots_oid(), seed, sig.tree().ots_signature, msg, adrs, m_hash); + const XMSS_WOTS_Parameters wots_params = params.wots_parameters(); + const XMSS_WOTS_PublicKey pub_key_ots(wots_params, seed, sig.tree().ots_signature, msg, adrs, m_hash); adrs.set_type(XMSS_Address::Type::LTree_Address); adrs.set_ltree_address(next_index); @@ -59,12 +60,11 @@ bool XMSS_Verification_Operation::verify(const XMSS_Signature& sig, const secure_vector& msg, const XMSS_PublicKey& public_key) { - XMSS_Address adrs; secure_vector index_bytes; - XMSS_Tools::concat(index_bytes, sig.unused_leaf_index(), m_pub_key.xmss_parameters().element_size()); - secure_vector msg_digest = m_hash.h_msg(sig.randomness(), public_key.root(), index_bytes, msg); + xmss_concat(index_bytes, sig.unused_leaf_index(), m_pub_key.xmss_parameters().element_size()); + const secure_vector msg_digest = m_hash.h_msg(sig.randomness(), public_key.root(), index_bytes, msg); - secure_vector node = root_from_signature(sig, msg_digest, adrs, public_key.public_seed()); + const secure_vector node = root_from_signature(sig, msg_digest, public_key.public_seed()); return (node == public_key.root()); } @@ -82,8 +82,8 @@ bool XMSS_Verification_Operation::is_valid_signature(std::span sig) { try { - XMSS_Signature signature(m_pub_key.xmss_parameters().oid(), sig); - bool result = verify(signature, m_msg_buf, m_pub_key); + const XMSS_Signature signature(m_pub_key.xmss_parameters().oid(), sig); + const bool result = verify(signature, m_msg_buf, m_pub_key); m_msg_buf.clear(); return result; } catch(...) { diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_verification_operation.h botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_verification_operation.h --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_verification_operation.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_verification_operation.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,7 @@ #include #include +#include #include namespace Botan { @@ -20,7 +21,7 @@ **/ class XMSS_Verification_Operation final : public virtual PK_Ops::Verification { public: - XMSS_Verification_Operation(const XMSS_PublicKey& public_key); + explicit XMSS_Verification_Operation(const XMSS_PublicKey& public_key); bool is_valid_signature(std::span sign) override; @@ -35,7 +36,6 @@ * * @param msg A message. * @param sig The XMSS signature for msg. - * @param ards A XMSS tree address. * @param seed A seed. * * @return An n-byte string holding the value of the root of a tree @@ -43,7 +43,6 @@ **/ secure_vector root_from_signature(const XMSS_Signature& sig, const secure_vector& msg, - XMSS_Address& ards, const secure_vector& seed); /** diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_wots.cpp botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_wots.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_wots.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_wots.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,19 +1,22 @@ /* * XMSS WOTS Public and Private Key - + * * A Winternitz One Time Signature public/private key for use with * Extended Hash-Based Signatures. * * (C) 2016,2017,2018 Matthias Gierlings * 2023 René Meusel - Rohde & Schwarz Cybersecurity + * 2026 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) **/ #include -#include +#include +#include #include +#include #include namespace Botan { @@ -21,6 +24,47 @@ namespace { /** +* Algorithm 1 (base_w) followed by the WOTS+ checksum, as used by the +* signing and verification routines in RFC 8391. The result is a single +* buffer of length params.len() holding the len_1 base-w digits of the +* message followed by the len_2 base-w digits of the checksum. +*/ +secure_vector base_w_with_checksum(const XMSS_WOTS_Parameters& params, std::span input) { + const size_t len_1 = params.len_1(); + const size_t len_2 = params.len_2(); + const size_t lg_w = params.lg_w(); + const uint8_t mask = static_cast(params.wots_parameter() - 1); + + BOTAN_ASSERT_NOMSG(input.size() * 8 >= len_1 * lg_w); + + secure_vector result(len_1 + len_2); + + size_t in = 0; + size_t total = 0; + size_t bits = 0; + for(size_t i = 0; i < len_1; ++i) { + if(bits == 0) { + total = input[in++]; + bits = 8; + } + bits -= lg_w; + result[i] = static_cast((total >> bits) & mask); + } + + size_t csum = 0; + for(size_t i = 0; i < len_1; ++i) { + csum += params.wots_parameter() - 1 - result[i]; + } + + for(size_t i = 0; i < len_2; ++i) { + const size_t shift = lg_w * (len_2 - 1 - i); + result[len_1 + i] = static_cast((csum >> shift) & mask); + } + + return result; +} + +/** * Algorithm 2: Chaining Function. * * Takes an n-byte input string and transforms it into a the function @@ -28,7 +72,7 @@ * the input x using the outputs of the PRNG "G". * * This overload is used in multithreaded scenarios, where it is - * required to provide seperate instances of XMSS_Hash to each + * required to provide separate instances of XMSS_Hash to each * thread. * * @param params The WOTS parameters to use @@ -45,7 +89,7 @@ secure_vector& result, size_t start_idx, size_t steps, - XMSS_Address& adrs, + XMSS_Address adrs, std::span seed, XMSS_Hash& hash) { BOTAN_ASSERT_NOMSG(result.size() == hash.output_length()); @@ -79,9 +123,9 @@ XMSS_WOTS_PublicKey::XMSS_WOTS_PublicKey(XMSS_WOTS_Parameters params, std::span public_seed, const XMSS_WOTS_PrivateKey& private_key, - XMSS_Address& adrs, + XMSS_Address adrs, XMSS_Hash& hash) : - XMSS_WOTS_Base(std::move(params), private_key.key_data()) { + XMSS_WOTS_Base(params, private_key.key_data()) { for(size_t i = 0; i < m_params.len(); ++i) { adrs.set_chain_address(static_cast(i)); chain(m_params, m_key_data[i], 0, m_params.wots_parameter() - 1, adrs, public_seed, hash); @@ -92,12 +136,10 @@ std::span public_seed, wots_keysig_t signature, const secure_vector& msg, - XMSS_Address& adrs, + XMSS_Address adrs, XMSS_Hash& hash) : - XMSS_WOTS_Base(std::move(params), std::move(signature)) { - secure_vector msg_digest{m_params.base_w(msg, m_params.len_1())}; - - m_params.append_checksum(msg_digest); + XMSS_WOTS_Base(params, std::move(signature)) { + const secure_vector msg_digest = base_w_with_checksum(m_params, msg); for(size_t i = 0; i < m_params.len(); i++) { adrs.set_chain_address(static_cast(i)); @@ -113,11 +155,9 @@ wots_keysig_t XMSS_WOTS_PrivateKey::sign(const secure_vector& msg, std::span public_seed, - XMSS_Address& adrs, + XMSS_Address adrs, XMSS_Hash& hash) { - secure_vector msg_digest{m_params.base_w(msg, m_params.len_1())}; - - m_params.append_checksum(msg_digest); + const secure_vector msg_digest = base_w_with_checksum(m_params, msg); auto sig = this->key_data(); for(size_t i = 0; i < m_params.len(); i++) { @@ -133,7 +173,7 @@ std::span private_seed, XMSS_Address adrs, XMSS_Hash& hash) : - XMSS_WOTS_Base(std::move(params)) { + XMSS_WOTS_Base(params) { m_key_data.resize(m_params.len()); for(size_t i = 0; i < m_params.len(); ++i) { adrs.set_chain_address(static_cast(i)); @@ -147,14 +187,14 @@ std::span private_seed, XMSS_Address adrs, XMSS_Hash& hash) : - XMSS_WOTS_Base(std::move(params)) { + XMSS_WOTS_Base(params) { m_key_data.resize(m_params.len()); secure_vector r; hash.prf(r, private_seed, adrs.bytes()); for(size_t i = 0; i < m_params.len(); ++i) { - XMSS_Tools::concat(m_key_data[i], i, 32); + xmss_concat(m_key_data[i], i, 32); hash.prf(m_key_data[i], r, m_key_data[i]); } } diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_wots.h botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_wots.h --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_wots.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_wots.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,37 +9,30 @@ #ifndef BOTAN_XMSS_WOTS_H_ #define BOTAN_XMSS_WOTS_H_ -#include -#include -#include -#include #include #include -#include -#include -#include -#include +#include #include namespace Botan { -class XMSS_Address; +class XMSS_Hash; class XMSS_WOTS_PrivateKey; typedef std::vector> wots_keysig_t; class XMSS_WOTS_Base { public: - XMSS_WOTS_Base(XMSS_WOTS_Parameters params) : m_params(std::move(params)) {} + explicit XMSS_WOTS_Base(XMSS_WOTS_Parameters params) : m_params(params) {} XMSS_WOTS_Base(XMSS_WOTS_Parameters params, wots_keysig_t key_data) : - m_params(std::move(params)), m_key_data(std::move(key_data)) {} + m_params(params), m_key_data(std::move(key_data)) {} const wots_keysig_t& key_data() const { return m_key_data; } protected: - XMSS_WOTS_Parameters m_params; - wots_keysig_t m_key_data; + XMSS_WOTS_Parameters m_params; // NOLINT(*non-private-member-variable*) + wots_keysig_t m_key_data; // NOLINT(*non-private-member-variable*) }; /** @@ -55,7 +48,7 @@ * function. * * This overload is used in multithreaded scenarios, where it is - * required to provide seperate instances of XMSS_Hash to each + * required to provide separate instances of XMSS_Hash to each * thread. * * @param params The WOTS parameters to use @@ -68,7 +61,7 @@ XMSS_WOTS_PublicKey(XMSS_WOTS_Parameters params, std::span public_seed, const XMSS_WOTS_PrivateKey& private_key, - XMSS_Address& adrs, + XMSS_Address adrs, XMSS_Hash& hash); /** @@ -88,7 +81,7 @@ std::span public_seed, wots_keysig_t signature, const secure_vector& msg, - XMSS_Address& adrs, + XMSS_Address adrs, XMSS_Hash& hash); }; @@ -108,7 +101,7 @@ * recommendation. * * This overload is used in multithreaded scenarios, where it is - * required to provide seperate instances of XMSS_Hash to each thread. + * required to provide separate instances of XMSS_Hash to each thread. * * @param params The WOTS parameters to use * @param public_seed The public seed for the private key generation @@ -146,7 +139,7 @@ * Generates a signature from a private key and a message. * * This overload is used in multithreaded scenarios, where it is - * required to provide seperate instances of XMSS_Hash to each + * required to provide separate instances of XMSS_Hash to each * thread. * * @param msg A message to sign. @@ -160,7 +153,7 @@ **/ wots_keysig_t sign(const secure_vector& msg, std::span public_seed, - XMSS_Address& adrs, + XMSS_Address adrs, XMSS_Hash& hash); }; diff -Nru botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_wots_parameters.cpp botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_wots_parameters.cpp --- botan3-3.7.1+dfsg/src/lib/pubkey/xmss/xmss_wots_parameters.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/pubkey/xmss/xmss_wots_parameters.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ /* * XMSS WOTS Parameters - * Descibes a signature method for XMSS Winternitz One Time Signatures, + * Describes a signature method for XMSS Winternitz One Time Signatures, * as defined in: * [1] XMSS: Extended Hash-Based Signatures, * Request for Comments: 8391 @@ -8,155 +8,53 @@ * https://datatracker.ietf.org/doc/rfc8391/ * * (C) 2016,2017,2018 Matthias Gierlings + * 2026 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) **/ -#include +#include +#include #include -#include -#include -#include namespace Botan { -XMSS_WOTS_Parameters::ots_algorithm_t XMSS_WOTS_Parameters::xmss_wots_id_from_string(std::string_view param_set) { - if(param_set == "WOTSP-SHA2_256") { - return WOTSP_SHA2_256; - } - if(param_set == "WOTSP-SHA2_512") { - return WOTSP_SHA2_512; - } - if(param_set == "WOTSP-SHAKE_256") { - return WOTSP_SHAKE_256; - } - if(param_set == "WOTSP-SHAKE_512") { - return WOTSP_SHAKE_512; - } - if(param_set == "WOTSP-SHA2_192") { - return WOTSP_SHA2_192; - } - if(param_set == "WOTSP-SHAKE_256_256") { - return WOTSP_SHAKE_256_256; - } - if(param_set == "WOTSP-SHAKE_256_192") { - return WOTSP_SHAKE_256_192; - } - - throw Lookup_Error(fmt("Unknown XMSS-WOTS algorithm param '{}'", param_set)); +XMSS_WOTS_Parameters XMSS_WOTS_Parameters::from_hash_len(ots_algorithm_t id, size_t hash_len) { + BOTAN_ASSERT_NOMSG(hash_len == 24 || hash_len == 32 || hash_len == 64); + const size_t len1 = 2 * hash_len; + // Theoretically this is a computed parameter based on the hash length and the Winternitz parameter + // We always use W=16, so len2 = 3 is correct for all hash lengths between 18 and 270 bytes. + const size_t len2 = 3; + return XMSS_WOTS_Parameters(id, hash_len, len1 + len2, len1, len2); } -XMSS_WOTS_Parameters::XMSS_WOTS_Parameters(std::string_view param_set) : - XMSS_WOTS_Parameters(xmss_wots_id_from_string(param_set)) {} - -XMSS_WOTS_Parameters::XMSS_WOTS_Parameters(ots_algorithm_t oid) : m_oid(oid) { - switch(oid) { +XMSS_WOTS_Parameters XMSS_WOTS_Parameters::from_id(ots_algorithm_t id) { + switch(id) { case WOTSP_SHA2_256: - m_element_size = 32; - m_w = 16; - m_len = 67; - m_name = "WOTSP-SHA2_256"; - m_hash_name = "SHA-256"; - m_strength = 256; - break; + return XMSS_WOTS_Parameters::from_hash_len(WOTSP_SHA2_256, 32); + case WOTSP_SHA2_512: - m_element_size = 64; - m_w = 16; - m_len = 131; - m_name = "WOTSP-SHA2_512"; - m_hash_name = "SHA-512"; - m_strength = 512; - break; + return XMSS_WOTS_Parameters::from_hash_len(WOTSP_SHA2_512, 64); + case WOTSP_SHAKE_256: - m_element_size = 32; - m_w = 16; - m_len = 67; - m_name = "WOTSP-SHAKE_256"; - m_hash_name = "SHAKE-128(256)"; - m_strength = 256; - break; + return XMSS_WOTS_Parameters::from_hash_len(WOTSP_SHAKE_256, 32); + case WOTSP_SHAKE_512: - m_element_size = 64; - m_w = 16; - m_len = 131; - m_name = "WOTSP-SHAKE_512"; - m_hash_name = "SHAKE-256(512)"; - m_strength = 512; - break; + return XMSS_WOTS_Parameters::from_hash_len(WOTSP_SHAKE_512, 64); + case WOTSP_SHA2_192: - m_element_size = 24; - m_w = 16; - m_len = 51; - m_name = "WOTSP-SHA2_192"; - m_hash_name = "Truncated(SHA-256,192)"; - m_strength = 192; - break; + return XMSS_WOTS_Parameters::from_hash_len(WOTSP_SHA2_192, 24); + case WOTSP_SHAKE_256_256: - m_element_size = 32; - m_w = 16; - m_len = 67; - m_name = "WOTSP-SHAKE_256_256"; - m_hash_name = "SHAKE-256(256)"; - m_strength = 256; - break; + return XMSS_WOTS_Parameters::from_hash_len(WOTSP_SHAKE_256_256, 32); + case WOTSP_SHAKE_256_192: - m_element_size = 24; - m_w = 16; - m_len = 51; - m_name = "WOTSP-SHAKE_256_192"; - m_hash_name = "SHAKE-256(192)"; - m_strength = 192; - break; + return XMSS_WOTS_Parameters::from_hash_len(WOTSP_SHAKE_256_192, 24); + default: throw Not_Implemented("Algorithm id does not match any known XMSS WOTS algorithm id."); } - - m_lg_w = (m_w == 16) ? 4 : 2; - m_len_1 = static_cast(std::ceil((8 * element_size()) / m_lg_w)); - m_len_2 = static_cast(floor(log2(m_len_1 * (wots_parameter() - 1)) / m_lg_w) + 1); - BOTAN_ASSERT(m_len == m_len_1 + m_len_2, - "Invalid XMSS WOTS parameter " - "\"len\" detected."); -} - -secure_vector XMSS_WOTS_Parameters::base_w(const secure_vector& msg, size_t out_size) const { - secure_vector result; - result.reserve(out_size); - - size_t in = 0; - size_t total = 0; - size_t bits = 0; - - for(size_t i = 0; i < out_size; i++) { - if(bits == 0) { - total = msg[in]; - in++; - bits += 8; - } - bits -= m_lg_w; - result.push_back(static_cast((total >> bits) & (m_w - 1))); - } - return result; -} - -secure_vector XMSS_WOTS_Parameters::base_w(size_t value) const { - value <<= (8 - ((m_len_2 * m_lg_w) % 8)); - size_t len_2_bytes = static_cast(std::ceil(static_cast(m_len_2 * m_lg_w) / 8.0)); - secure_vector result; - XMSS_Tools::concat(result, value, len_2_bytes); - return base_w(result, m_len_2); -} - -void XMSS_WOTS_Parameters::append_checksum(secure_vector& data) const { - size_t csum = 0; - - for(size_t i = 0; i < data.size(); i++) { - csum += wots_parameter() - 1 - data[i]; - } - - secure_vector csum_bytes = base_w(csum); - std::move(csum_bytes.begin(), csum_bytes.end(), std::back_inserter(data)); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/rng/auto_rng/auto_rng.cpp botan3-3.12.0+dfsg/src/lib/rng/auto_rng/auto_rng.cpp --- botan3-3.7.1+dfsg/src/lib/rng/auto_rng/auto_rng.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/rng/auto_rng/auto_rng.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,8 +6,10 @@ #include +#include +#include #include -#include +#include #if defined(BOTAN_HAS_ENTROPY_SOURCE) #include @@ -39,6 +41,8 @@ } // namespace +AutoSeeded_RNG::AutoSeeded_RNG(AutoSeeded_RNG&& other) noexcept = default; + AutoSeeded_RNG::~AutoSeeded_RNG() = default; AutoSeeded_RNG::AutoSeeded_RNG(RandomNumberGenerator& underlying_rng, size_t reseed_interval) { @@ -95,8 +99,8 @@ return m_rng->name(); } -size_t AutoSeeded_RNG::reseed(Entropy_Sources& srcs, size_t poll_bits, std::chrono::milliseconds poll_timeout) { - return m_rng->reseed(srcs, poll_bits, poll_timeout); +size_t AutoSeeded_RNG::reseed_from_sources(Entropy_Sources& srcs, size_t poll_bits) { + return m_rng->reseed_from_sources(srcs, poll_bits); } void AutoSeeded_RNG::fill_bytes_with_input(std::span out, std::span in) { diff -Nru botan3-3.7.1+dfsg/src/lib/rng/auto_rng/auto_rng.h botan3-3.12.0+dfsg/src/lib/rng/auto_rng/auto_rng.h --- botan3-3.7.1+dfsg/src/lib/rng/auto_rng/auto_rng.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/rng/auto_rng/auto_rng.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #define BOTAN_AUTO_SEEDING_RNG_H_ #include +#include namespace Botan { @@ -28,9 +29,8 @@ */ void force_reseed(); - size_t reseed(Entropy_Sources& srcs, - size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS, - std::chrono::milliseconds poll_timeout = BOTAN_RNG_RESEED_DEFAULT_TIMEOUT) override; + size_t reseed_from_sources(Entropy_Sources& srcs, + size_t poll_bits = RandomNumberGenerator::DefaultPollBits) override; std::string name() const override; @@ -43,7 +43,7 @@ * @param reseed_interval specifies a limit of how many times * the RNG will be called before automatic reseeding is performed */ - AutoSeeded_RNG(size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + BOTAN_FUTURE_EXPLICIT AutoSeeded_RNG(size_t reseed_interval = RandomNumberGenerator::DefaultReseedInterval); /** * Create an AutoSeeded_RNG which will get seed material from some other @@ -55,7 +55,8 @@ * @param reseed_interval specifies a limit of how many times * the RNG will be called before automatic reseeding is performed */ - AutoSeeded_RNG(RandomNumberGenerator& underlying_rng, size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + BOTAN_FUTURE_EXPLICIT AutoSeeded_RNG(RandomNumberGenerator& underlying_rng, + size_t reseed_interval = RandomNumberGenerator::DefaultReseedInterval); /** * Create an AutoSeeded_RNG which will get seed material from a set of @@ -65,7 +66,8 @@ * @param reseed_interval specifies a limit of how many times * the RNG will be called before automatic reseeding is performed */ - AutoSeeded_RNG(Entropy_Sources& entropy_sources, size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + BOTAN_FUTURE_EXPLICIT AutoSeeded_RNG(Entropy_Sources& entropy_sources, + size_t reseed_interval = RandomNumberGenerator::DefaultReseedInterval); /** * Create an AutoSeeded_RNG which will get seed material from both an @@ -79,7 +81,12 @@ */ AutoSeeded_RNG(RandomNumberGenerator& underlying_rng, Entropy_Sources& entropy_sources, - size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + size_t reseed_interval = RandomNumberGenerator::DefaultReseedInterval); + + AutoSeeded_RNG(const AutoSeeded_RNG& other) = delete; + AutoSeeded_RNG(AutoSeeded_RNG&& other) noexcept; + AutoSeeded_RNG& operator=(const AutoSeeded_RNG& other) = delete; + AutoSeeded_RNG& operator=(AutoSeeded_RNG&& other) = delete; ~AutoSeeded_RNG() override; diff -Nru botan3-3.7.1+dfsg/src/lib/rng/auto_rng/info.txt botan3-3.12.0+dfsg/src/lib/rng/auto_rng/info.txt --- botan3-3.7.1+dfsg/src/lib/rng/auto_rng/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/rng/auto_rng/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,8 @@ -AUTO_SEEDING_RNG -> 20160821 AUTO_RNG -> 20161126 + +# TODO(Botan4) remove this +AUTO_SEEDING_RNG -> 20160821 diff -Nru botan3-3.7.1+dfsg/src/lib/rng/chacha_rng/chacha_rng.cpp botan3-3.12.0+dfsg/src/lib/rng/chacha_rng/chacha_rng.cpp --- botan3-3.7.1+dfsg/src/lib/rng/chacha_rng/chacha_rng.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/rng/chacha_rng/chacha_rng.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,15 +7,17 @@ #include +#include + namespace Botan { -ChaCha_RNG::ChaCha_RNG() : Stateful_RNG() { +ChaCha_RNG::ChaCha_RNG() { m_hmac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)"); m_chacha = StreamCipher::create_or_throw("ChaCha(20)"); clear(); } -ChaCha_RNG::ChaCha_RNG(std::span seed) : Stateful_RNG() { +ChaCha_RNG::ChaCha_RNG(std::span seed) { m_hmac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)"); m_chacha = StreamCipher::create_or_throw("ChaCha(20)"); clear(); diff -Nru botan3-3.7.1+dfsg/src/lib/rng/chacha_rng/chacha_rng.h botan3-3.12.0+dfsg/src/lib/rng/chacha_rng/chacha_rng.h --- botan3-3.7.1+dfsg/src/lib/rng/chacha_rng/chacha_rng.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/rng/chacha_rng/chacha_rng.h 2026-05-07 01:38:28.000000000 +0000 @@ -61,7 +61,7 @@ * * @param seed the seed material, should be at least 256 bits */ - ChaCha_RNG(std::span seed); + BOTAN_FUTURE_EXPLICIT ChaCha_RNG(std::span seed); /** * Automatic reseeding from @p underlying_rng will take place after @@ -72,7 +72,8 @@ * @param reseed_interval specifies a limit of how many times * the RNG will be called before automatic reseeding is performed */ - ChaCha_RNG(RandomNumberGenerator& underlying_rng, size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + BOTAN_FUTURE_EXPLICIT ChaCha_RNG(RandomNumberGenerator& underlying_rng, + size_t reseed_interval = RandomNumberGenerator::DefaultReseedInterval); /** * Automatic reseeding from @p entropy_sources will take place after @@ -82,7 +83,8 @@ * @param reseed_interval specifies a limit of how many times * the RNG will be called before automatic reseeding is performed. */ - ChaCha_RNG(Entropy_Sources& entropy_sources, size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + BOTAN_FUTURE_EXPLICIT ChaCha_RNG(Entropy_Sources& entropy_sources, + size_t reseed_interval = RandomNumberGenerator::DefaultReseedInterval); /** * Automatic reseeding from @p underlying_rng and @p entropy_sources @@ -97,7 +99,7 @@ */ ChaCha_RNG(RandomNumberGenerator& underlying_rng, Entropy_Sources& entropy_sources, - size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + size_t reseed_interval = RandomNumberGenerator::DefaultReseedInterval); std::string name() const override { return "ChaCha_RNG"; } diff -Nru botan3-3.7.1+dfsg/src/lib/rng/esdm_rng/esdm_rng.cpp botan3-3.12.0+dfsg/src/lib/rng/esdm_rng/esdm_rng.cpp --- botan3-3.7.1+dfsg/src/lib/rng/esdm_rng/esdm_rng.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/rng/esdm_rng/esdm_rng.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,7 @@ #include +#include #include #include @@ -19,7 +20,7 @@ * as soon as all instances of ESDM_RNG are destructed. This may * happen multiple times in the lifetime of the process. */ -class ESDM_Context { +class ESDM_Context final { public: [[nodiscard]] static std::shared_ptr instance() { static ESDM_Context g_instance; diff -Nru botan3-3.7.1+dfsg/src/lib/rng/esdm_rng/esdm_rng.h botan3-3.12.0+dfsg/src/lib/rng/esdm_rng/esdm_rng.h --- botan3-3.7.1+dfsg/src/lib/rng/esdm_rng/esdm_rng.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/rng/esdm_rng/esdm_rng.h 2026-05-07 01:38:28.000000000 +0000 @@ -34,7 +34,7 @@ * esdm_rpcc_get_random_bytes_full (fully seeded) calls have to be used. * * Configurable modes: -* - fully seeded (-> fast): provide entropy from a DRBG/PRNG after beeing fully seeded, +* - fully seeded (-> fast): provide entropy from a DRBG/PRNG after being fully seeded, * block until this point is reached, reseed from after a time * and/or invocation limit, block again if reseeding is not possible * - prediction resistance (-> slow): reseed ESDM with fresh entropy after each invocation @@ -96,7 +96,7 @@ private: /** - * tracks if predicition resistant or fully seeded interface should be queried + * tracks if prediction resistant or fully seeded interface should be queried */ bool m_prediction_resistance; diff -Nru botan3-3.7.1+dfsg/src/lib/rng/hmac_drbg/hmac_drbg.cpp botan3-3.12.0+dfsg/src/lib/rng/hmac_drbg/hmac_drbg.cpp --- botan3-3.7.1+dfsg/src/lib/rng/hmac_drbg/hmac_drbg.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/rng/hmac_drbg/hmac_drbg.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,10 @@ #include +#include +#include +#include +#include #include #include @@ -22,6 +26,10 @@ // SHA-256, SHA-512/256, SHA-384, SHA-512: >= 256 bits // NIST SP 800-90A only supports up to 256 bits though + if(mac_output_length < 20) { + throw Invalid_Argument(fmt("HMAC_DRBG MAC output length {} is too small", mac_output_length)); + } + if(mac_output_length < 32) { return (mac_output_length - 4) * 8; } else { @@ -41,18 +49,24 @@ } } +template +std::unique_ptr check_not_null(std::unique_ptr obj) { + BOTAN_ARG_CHECK(obj != nullptr, "Argument must not be null"); + return obj; +} + } // namespace +HMAC_DRBG::~HMAC_DRBG() = default; + HMAC_DRBG::HMAC_DRBG(std::unique_ptr prf, RandomNumberGenerator& underlying_rng, size_t reseed_interval, size_t max_number_of_bytes_per_request) : Stateful_RNG(underlying_rng, reseed_interval), - m_mac(std::move(prf)), + m_mac(check_not_null(std::move(prf))), m_max_number_of_bytes_per_request(max_number_of_bytes_per_request), m_security_level(hmac_drbg_security_level(m_mac->output_length())) { - BOTAN_ASSERT_NONNULL(m_mac); - check_limits(reseed_interval, max_number_of_bytes_per_request); clear(); @@ -64,11 +78,9 @@ size_t reseed_interval, size_t max_number_of_bytes_per_request) : Stateful_RNG(underlying_rng, entropy_sources, reseed_interval), - m_mac(std::move(prf)), + m_mac(check_not_null(std::move(prf))), m_max_number_of_bytes_per_request(max_number_of_bytes_per_request), m_security_level(hmac_drbg_security_level(m_mac->output_length())) { - BOTAN_ASSERT_NONNULL(m_mac); - check_limits(reseed_interval, max_number_of_bytes_per_request); clear(); @@ -79,27 +91,22 @@ size_t reseed_interval, size_t max_number_of_bytes_per_request) : Stateful_RNG(entropy_sources, reseed_interval), - m_mac(std::move(prf)), + m_mac(check_not_null(std::move(prf))), m_max_number_of_bytes_per_request(max_number_of_bytes_per_request), m_security_level(hmac_drbg_security_level(m_mac->output_length())) { - BOTAN_ASSERT_NONNULL(m_mac); - check_limits(reseed_interval, max_number_of_bytes_per_request); clear(); } HMAC_DRBG::HMAC_DRBG(std::unique_ptr prf) : - Stateful_RNG(), - m_mac(std::move(prf)), + m_mac(check_not_null(std::move(prf))), m_max_number_of_bytes_per_request(64 * 1024), m_security_level(hmac_drbg_security_level(m_mac->output_length())) { - BOTAN_ASSERT_NONNULL(m_mac); clear(); } HMAC_DRBG::HMAC_DRBG(std::string_view hmac_hash) : - Stateful_RNG(), m_mac(MessageAuthenticationCode::create_or_throw(fmt("HMAC({})", hmac_hash))), m_max_number_of_bytes_per_request(64 * 1024), m_security_level(hmac_drbg_security_level(m_mac->output_length())) { @@ -113,9 +120,7 @@ m_T.resize(output_length); } - for(size_t i = 0; i != m_V.size(); ++i) { - m_V[i] = 0x01; - } + std::fill(m_V.begin(), m_V.end(), 0x01); m_mac->set_key(std::vector(m_V.size(), 0x00)); } @@ -128,6 +133,7 @@ * See NIST SP800-90A section 10.1.2.5 */ void HMAC_DRBG::generate_output(std::span output, std::span input) { + // This is an internal function, callers should have validated this beforehand BOTAN_ASSERT_NOMSG(!output.empty()); if(!input.empty()) { diff -Nru botan3-3.7.1+dfsg/src/lib/rng/hmac_drbg/hmac_drbg.h botan3-3.12.0+dfsg/src/lib/rng/hmac_drbg/hmac_drbg.h --- botan3-3.7.1+dfsg/src/lib/rng/hmac_drbg/hmac_drbg.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/rng/hmac_drbg/hmac_drbg.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,11 +8,12 @@ #ifndef BOTAN_HMAC_DRBG_H_ #define BOTAN_HMAC_DRBG_H_ -#include #include +#include namespace Botan { +class MessageAuthenticationCode; class Entropy_Sources; /** @@ -62,7 +63,7 @@ */ HMAC_DRBG(std::unique_ptr prf, RandomNumberGenerator& underlying_rng, - size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL, + size_t reseed_interval = RandomNumberGenerator::DefaultReseedInterval, size_t max_number_of_bytes_per_request = 64 * 1024); /** @@ -89,7 +90,7 @@ */ HMAC_DRBG(std::unique_ptr prf, Entropy_Sources& entropy_sources, - size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL, + size_t reseed_interval = RandomNumberGenerator::DefaultReseedInterval, size_t max_number_of_bytes_per_request = 64 * 1024); /** @@ -120,9 +121,17 @@ HMAC_DRBG(std::unique_ptr prf, RandomNumberGenerator& underlying_rng, Entropy_Sources& entropy_sources, - size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL, + size_t reseed_interval = RandomNumberGenerator::DefaultReseedInterval, size_t max_number_of_bytes_per_request = 64 * 1024); + ~HMAC_DRBG() override; + + HMAC_DRBG(const HMAC_DRBG& rng) = delete; + HMAC_DRBG& operator=(const HMAC_DRBG& rng) = delete; + + HMAC_DRBG(HMAC_DRBG&& rng) = delete; + HMAC_DRBG& operator=(HMAC_DRBG&& rng) = delete; + std::string name() const override; size_t security_level() const override; diff -Nru botan3-3.7.1+dfsg/src/lib/rng/jitter_rng/jitter_rng.cpp botan3-3.12.0+dfsg/src/lib/rng/jitter_rng/jitter_rng.cpp --- botan3-3.7.1+dfsg/src/lib/rng/jitter_rng/jitter_rng.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/rng/jitter_rng/jitter_rng.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,9 @@ #include +#include +#include + #include namespace Botan { @@ -21,16 +24,21 @@ }; Jitter_RNG_Internal::Jitter_RNG_Internal() { - static int result = jent_entropy_init(); + constexpr unsigned int oversampling_rate = 0; // use default oversampling + constexpr unsigned int flags = JENT_FORCE_FIPS; // enable health tests - // no further details documented regarding the return value - BOTAN_ASSERT(result == 0, "JitterRNG: initialization successful"); + // if flags and osr are used, use the same values for init and alloc + static int result = jent_entropy_init_ex(oversampling_rate, flags); - constexpr unsigned int oversampling_rate = 0; // use default oversampling - constexpr unsigned int flags = 0; + // no further details documented regarding the return value + if(result != 0) { + throw Internal_Error("Jitter_RNG_Internal initialization failed"); + } m_rand_data = jent_entropy_collector_alloc(oversampling_rate, flags); - BOTAN_ASSERT_NONNULL(m_rand_data); + if(m_rand_data == nullptr) { + throw Internal_Error("Jitter_RNG_Internal collector allocation failed"); + } } Jitter_RNG_Internal::~Jitter_RNG_Internal() { @@ -93,4 +101,5 @@ m_jitter->collect_into_buffer(out); } -}; // namespace Botan + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/rng/jitter_rng/jitter_rng.h botan3-3.12.0+dfsg/src/lib/rng/jitter_rng/jitter_rng.h --- botan3-3.7.1+dfsg/src/lib/rng/jitter_rng/jitter_rng.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/rng/jitter_rng/jitter_rng.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,8 @@ #define BOTAN_JITTER_RNG_H_ #include +#include +#include namespace Botan { @@ -35,6 +37,7 @@ std::unique_ptr m_jitter; }; + } // namespace Botan #endif diff -Nru botan3-3.7.1+dfsg/src/lib/rng/processor_rng/info.txt botan3-3.12.0+dfsg/src/lib/rng/processor_rng/info.txt --- botan3-3.7.1+dfsg/src/lib/rng/processor_rng/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/rng/processor_rng/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -17,8 +17,6 @@ processor_rng.h - -x86_32:rdrand -x86_64:rdrand -ppc64:power9 - + +cpuid + diff -Nru botan3-3.7.1+dfsg/src/lib/rng/processor_rng/processor_rng.cpp botan3-3.12.0+dfsg/src/lib/rng/processor_rng/processor_rng.cpp --- botan3-3.7.1+dfsg/src/lib/rng/processor_rng/processor_rng.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/rng/processor_rng/processor_rng.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,10 +6,13 @@ #include +#include #include +#include #include +#include -#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) +#if defined(BOTAN_TARGET_ARCH_IS_X86_FAMILY) && !defined(BOTAN_USE_GCC_INLINE_ASM) #include #endif @@ -17,14 +20,14 @@ namespace { -#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) +#if defined(BOTAN_TARGET_ARCH_IS_X86_FAMILY) /* * According to Intel, RDRAND is guaranteed to generate a random * number within 10 retries on a working CPU */ const size_t HWRNG_RETRIES = 10; -#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) +#elif defined(BOTAN_TARGET_ARCH_IS_PPC_FAMILY) /** * PowerISA 3.0 p.78: * When the error value is obtained, software is expected to repeat the @@ -47,14 +50,15 @@ typedef uint64_t hwrng_output; #endif -hwrng_output read_hwrng(bool& success) { - hwrng_output output = 0; +hwrng_output BOTAN_FN_ISA_RNG read_hwrng(bool& success) { + hwrng_output output = 0; // NOLINT(*-const-correctness) clang-tidy doesn't understand inline asm success = false; -#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) - int cf = 0; +#if defined(BOTAN_TARGET_ARCH_IS_X86_FAMILY) + int cf = 0; // NOLINT(*-const-correctness) clang-tidy doesn't understand inline asm #if defined(BOTAN_USE_GCC_INLINE_ASM) // same asm seq works for 32 and 64 bit + // NOLINTNEXTLINE(*-no-assembler) asm volatile("rdrand %0; adcl $0,%1" : "=r"(output), "=r"(cf) : "0"(output), "1"(cf) : "cc"); #elif defined(BOTAN_TARGET_ARCH_IS_X86_32) cf = _rdrand32_step(&output); @@ -63,17 +67,17 @@ #endif success = (1 == cf); -#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) +#elif defined(BOTAN_TARGET_ARCH_IS_PPC_FAMILY) /* DARN indicates error by returning 0xFF..FF, ie is biased. Which is crazy. Avoid the bias by invoking it twice and, assuming both succeed, returning the XOR of the two results, which should unbias the output. */ - uint64_t output2 = 0; + uint64_t output2 = 0; // NOLINT(*-const-correctness) clang-tidy doesn't understand inline asm // DARN codes are 0: 32-bit conditioned, 1: 64-bit conditioned, 2: 64-bit raw (ala RDSEED) - asm volatile("darn %0, 1" : "=r"(output)); - asm volatile("darn %0, 1" : "=r"(output2)); + asm volatile("darn %0, 1" : "=r"(output)); // NOLINT(*-no-assembler) + asm volatile("darn %0, 1" : "=r"(output2)); // NOLINT(*-no-assembler) if((~output) != 0 && (~output2) != 0) { output ^= output2; @@ -92,7 +96,7 @@ hwrng_output read_hwrng() { for(size_t i = 0; i < HWRNG_RETRIES; ++i) { bool success = false; - hwrng_output output = read_hwrng(success); + const hwrng_output output = read_hwrng(success); if(success) { return output; @@ -106,19 +110,19 @@ //static bool Processor_RNG::available() { -#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) - return CPUID::has_rdrand(); -#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) - return CPUID::has_darn_rng(); +#if defined(BOTAN_TARGET_ARCH_IS_X86_FAMILY) + return CPUID::has(CPUID::Feature::RDRAND); +#elif defined(BOTAN_TARGET_ARCH_IS_PPC_FAMILY) + return CPUID::has(CPUID::Feature::DARN); #else return false; #endif } std::string Processor_RNG::name() const { -#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) +#if defined(BOTAN_TARGET_ARCH_IS_X86_FAMILY) return "rdrand"; -#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) +#elif defined(BOTAN_TARGET_ARCH_IS_PPC_FAMILY) return "darn"; #else return "hwrng"; @@ -153,11 +157,4 @@ } } -size_t Processor_RNG::reseed(Entropy_Sources& /*srcs*/, - size_t /*poll_bits*/, - std::chrono::milliseconds /*poll_timeout*/) { - /* no way to add entropy */ - return 0; -} - } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/rng/processor_rng/processor_rng.h botan3-3.12.0+dfsg/src/lib/rng/processor_rng/processor_rng.h --- botan3-3.7.1+dfsg/src/lib/rng/processor_rng/processor_rng.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/rng/processor_rng/processor_rng.h 2026-05-07 01:38:28.000000000 +0000 @@ -32,11 +32,6 @@ bool is_seeded() const override { return true; } - /* - * No way to reseed processor provided generator, so reseed is ignored - */ - size_t reseed(Entropy_Sources&, size_t, std::chrono::milliseconds) override; - std::string name() const override; private: diff -Nru botan3-3.7.1+dfsg/src/lib/rng/rng.cpp botan3-3.12.0+dfsg/src/lib/rng/rng.cpp --- botan3-3.7.1+dfsg/src/lib/rng/rng.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/rng/rng.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,6 +6,7 @@ #include +#include #include #if defined(BOTAN_HAS_ENTROPY_SOURCE) @@ -48,12 +49,12 @@ } } -size_t RandomNumberGenerator::reseed(Entropy_Sources& srcs, size_t poll_bits, std::chrono::milliseconds poll_timeout) { +size_t RandomNumberGenerator::reseed_from_sources(Entropy_Sources& srcs, size_t poll_bits) { if(this->accepts_input()) { #if defined(BOTAN_HAS_ENTROPY_SOURCE) - return srcs.poll(*this, poll_bits, poll_timeout); + return srcs.poll(*this, poll_bits); #else - BOTAN_UNUSED(srcs, poll_bits, poll_timeout); + BOTAN_UNUSED(srcs, poll_bits); #endif } diff -Nru botan3-3.7.1+dfsg/src/lib/rng/rng.h botan3-3.12.0+dfsg/src/lib/rng/rng.h --- botan3-3.7.1+dfsg/src/lib/rng/rng.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/rng/rng.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,17 +10,25 @@ #define BOTAN_RANDOM_NUMBER_GENERATOR_H_ #include -#include -#include #include #include -#include #include #include #include #include +/* +* We only include in downstream applications to avoid +* breaking semver wrt RandomNumberGenerator::reseed. Within the +* library we avoid it because it slows down compilation significantly. +* +* TODO(Botan4): remove this entirely +*/ +#if !defined(BOTAN_IS_BEING_BUILT) + #include +#endif + namespace Botan { class Entropy_Sources; @@ -30,6 +38,17 @@ */ class BOTAN_PUBLIC_API(2, 0) RandomNumberGenerator { public: + /** + * Userspace RNGs like HMAC_DRBG will reseed after a specified number + * of outputs are generated. Set to zero to disable automatic reseeding. + */ + static constexpr size_t DefaultReseedInterval = 1024; + + /** + * Number of entropy bits polled for reseeding userspace RNGs like HMAC_DRBG + */ + static constexpr size_t DefaultPollBits = 256; + virtual ~RandomNumberGenerator() = default; RandomNumberGenerator() = default; @@ -40,6 +59,9 @@ RandomNumberGenerator(const RandomNumberGenerator& rng) = delete; RandomNumberGenerator& operator=(const RandomNumberGenerator& rng) = delete; + RandomNumberGenerator(RandomNumberGenerator&& rng) = default; + RandomNumberGenerator& operator=(RandomNumberGenerator&& rng) = default; + /** * Randomize a byte array. * @@ -81,7 +103,7 @@ * Incorporate some additional data into the RNG state. */ template - requires std::is_standard_layout::value && std::is_trivial::value + requires std::is_standard_layout_v && std::is_trivial_v void add_entropy_T(const T& t) { this->add_entropy(reinterpret_cast(&t), sizeof(T)); } @@ -148,15 +170,15 @@ virtual bool is_seeded() const = 0; /** - * Poll provided sources for up to poll_bits bits of entropy - * or until the timeout expires. Returns estimate of the number - * of bits collected. - * + * Poll provided sources for up to poll_bits bits of entropy. + * Returns estimate of the number of bits collected. * Sets the seeded state to true if enough entropy was added. + * + * @throws Exception if RNG accepts input but reseeding failed. */ - virtual size_t reseed(Entropy_Sources& srcs, - size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS, - std::chrono::milliseconds poll_timeout = BOTAN_RNG_RESEED_DEFAULT_TIMEOUT); + size_t reseed_from(Entropy_Sources& srcs, size_t poll_bits = RandomNumberGenerator::DefaultPollBits) { + return reseed_from_sources(srcs, poll_bits); + } /** * Reseed by reading specified bits from the RNG @@ -165,7 +187,9 @@ * * @throws Exception if RNG accepts input but reseeding failed. */ - virtual void reseed_from_rng(RandomNumberGenerator& rng, size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS); + void reseed_from(RandomNumberGenerator& rng, size_t poll_bits = RandomNumberGenerator::DefaultPollBits) { + return reseed_from_rng(rng, poll_bits); + } // Some utility functions built on the interface above: @@ -214,7 +238,7 @@ */ template std::array random_array() { - std::array result; + std::array result{}; random_vec(result); return result; } @@ -226,7 +250,7 @@ * @throws Exception if the RNG fails */ uint8_t next_byte() { - uint8_t b; + uint8_t b = 0; this->fill_bytes_with_input(std::span(&b, 1), {}); return b; } @@ -244,8 +268,50 @@ return b; } + /** + * Reseed by reading specified bits from the RNG + * + * Sets the seeded state to true if enough entropy was added. + * + * @throws Exception if RNG accepts input but reseeding failed. + */ + virtual void reseed_from_rng(RandomNumberGenerator& rng, + size_t poll_bits = RandomNumberGenerator::DefaultPollBits); + +#if !defined(BOTAN_IS_BEING_BUILT) + /** + * Default poll timeout + */ + static constexpr auto DefaultPollTimeout = std::chrono::milliseconds(50); + + /** + * Poll provided sources for up to poll_bits bits of entropy. + * Returns estimate of the number of bits collected. + * + * Sets the seeded state to true if enough entropy was added. + * + * TODO(Botan4) remove this function + */ + BOTAN_DEPRECATED("Use reseed_from_sources") + inline size_t reseed(Entropy_Sources& srcs, + size_t poll_bits = RandomNumberGenerator::DefaultPollBits, + std::chrono::milliseconds /*unused_timeout*/ = DefaultPollTimeout) { + return reseed_from(srcs, poll_bits); + } +#endif + protected: /** + * Poll provided sources for up to poll_bits bits of entropy. + * Returns estimate of the number of bits collected. + * Sets the seeded state to true if enough entropy was added. + * + * @throws Exception if RNG accepts input but reseeding failed. + */ + virtual size_t reseed_from_sources(Entropy_Sources& srcs, + size_t poll_bits = RandomNumberGenerator::DefaultPollBits); + + /** * Generic interface to provide entropy to a concrete implementation and to * fill a given buffer with random output. Both @p output and @p input may * be empty and should be ignored in that case. If both buffers are diff -Nru botan3-3.7.1+dfsg/src/lib/rng/stateful_rng/stateful_rng.cpp botan3-3.12.0+dfsg/src/lib/rng/stateful_rng/stateful_rng.cpp --- botan3-3.7.1+dfsg/src/lib/rng/stateful_rng/stateful_rng.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/rng/stateful_rng/stateful_rng.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,30 +6,31 @@ #include -#include +#include +#include #include namespace Botan { void Stateful_RNG::clear() { - lock_guard_type lock(m_mutex); + const lock_guard_type lock(m_mutex); m_reseed_counter = 0; m_last_pid = 0; clear_state(); } void Stateful_RNG::force_reseed() { - lock_guard_type lock(m_mutex); + const lock_guard_type lock(m_mutex); m_reseed_counter = 0; } bool Stateful_RNG::is_seeded() const { - lock_guard_type lock(m_mutex); + const lock_guard_type lock(m_mutex); return m_reseed_counter > 0; } void Stateful_RNG::initialize_with(std::span input) { - lock_guard_type lock(m_mutex); + const lock_guard_type lock(m_mutex); clear(); add_entropy(input); @@ -60,7 +61,7 @@ } void Stateful_RNG::fill_bytes_with_input(std::span output, std::span input) { - lock_guard_type lock(m_mutex); + const lock_guard_type lock(m_mutex); if(output.empty()) { // Special case for exclusively adding entropy to the stateful RNG. @@ -74,10 +75,10 @@ } } -size_t Stateful_RNG::reseed(Entropy_Sources& srcs, size_t poll_bits, std::chrono::milliseconds poll_timeout) { - lock_guard_type lock(m_mutex); +size_t Stateful_RNG::reseed_from_sources(Entropy_Sources& srcs, size_t poll_bits) { + const lock_guard_type lock(m_mutex); - const size_t bits_collected = RandomNumberGenerator::reseed(srcs, poll_bits, poll_timeout); + const size_t bits_collected = RandomNumberGenerator::reseed_from_sources(srcs, poll_bits); if(bits_collected >= security_level()) { reset_reseed_counter(); @@ -87,7 +88,7 @@ } void Stateful_RNG::reseed_from_rng(RandomNumberGenerator& rng, size_t poll_bits) { - lock_guard_type lock(m_mutex); + const lock_guard_type lock(m_mutex); RandomNumberGenerator::reseed_from_rng(rng, poll_bits); @@ -112,12 +113,12 @@ m_reseed_counter = 0; m_last_pid = cur_pid; - if(m_underlying_rng) { + if(m_underlying_rng != nullptr) { reseed_from_rng(*m_underlying_rng, security_level()); } - if(m_entropy_sources) { - reseed(*m_entropy_sources, security_level()); + if(m_entropy_sources != nullptr) { + reseed_from_sources(*m_entropy_sources, security_level()); } if(!is_seeded()) { diff -Nru botan3-3.7.1+dfsg/src/lib/rng/stateful_rng/stateful_rng.h botan3-3.12.0+dfsg/src/lib/rng/stateful_rng/stateful_rng.h --- botan3-3.7.1+dfsg/src/lib/rng/stateful_rng/stateful_rng.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/rng/stateful_rng/stateful_rng.h 2026-05-07 01:38:28.000000000 +0000 @@ -75,16 +75,14 @@ */ void force_reseed(); - void reseed_from_rng(RandomNumberGenerator& rng, size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS) final; + void reseed_from_rng(RandomNumberGenerator& rng, size_t poll_bits = RandomNumberGenerator::DefaultPollBits) final; /** - * Poll provided sources for up to poll_bits bits of entropy - * or until the timeout expires. Returns estimate of the number - * of bits collected. + * Poll provided sources for up to poll_bits bits of entropy. + * Returns estimate of the number of bits collected. */ - size_t reseed(Entropy_Sources& srcs, - size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS, - std::chrono::milliseconds poll_timeout = BOTAN_RNG_RESEED_DEFAULT_TIMEOUT) override; + size_t reseed_from_sources(Entropy_Sources& srcs, + size_t poll_bits = RandomNumberGenerator::DefaultPollBits) final; /** * @return intended security level of this DRBG diff -Nru botan3-3.7.1+dfsg/src/lib/rng/system_rng/info.txt botan3-3.12.0+dfsg/src/lib/rng/system_rng/info.txt --- botan3-3.7.1+dfsg/src/lib/rng/system_rng/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/rng/system_rng/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -8,8 +8,11 @@ +# Any one of these feature sets is sufficient dev_random,posix1 +ccrandom arc4random +getrandom rtlgenrandom crypto_ng diff -Nru botan3-3.7.1+dfsg/src/lib/rng/system_rng/system_rng.cpp botan3-3.12.0+dfsg/src/lib/rng/system_rng/system_rng.cpp --- botan3-3.7.1+dfsg/src/lib/rng/system_rng/system_rng.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/rng/system_rng/system_rng.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,10 @@ #include +#include +#include +#include + #if defined(BOTAN_TARGET_OS_HAS_WIN32) #define NOMINMAX 1 #define _WINSOCKAPI_ // stop windows.h including winsock.h @@ -16,6 +20,7 @@ #if defined(BOTAN_TARGET_OS_HAS_RTLGENRANDOM) #include + #include #elif defined(BOTAN_TARGET_OS_HAS_CRYPTO_NG) #include #include @@ -226,6 +231,10 @@ throw System_Error("System_RNG getrandom failed", errno); } + if(got == 0) { + throw System_Error("System_RNG getrandom unexpectedly returned 0"); + } + buf += got; len -= got; } diff -Nru botan3-3.7.1+dfsg/src/lib/stream/chacha/chacha.cpp botan3-3.12.0+dfsg/src/lib/stream/chacha/chacha.cpp --- botan3-3.7.1+dfsg/src/lib/stream/chacha/chacha.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/stream/chacha/chacha.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,11 +8,14 @@ #include #include -#include #include #include #include +#if defined(BOTAN_HAS_CPUID) + #include +#endif + namespace Botan { namespace { @@ -38,9 +41,22 @@ void hchacha(uint32_t output[8], const uint32_t input[16], size_t rounds) { BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds"); - uint32_t x00 = input[0], x01 = input[1], x02 = input[2], x03 = input[3], x04 = input[4], x05 = input[5], - x06 = input[6], x07 = input[7], x08 = input[8], x09 = input[9], x10 = input[10], x11 = input[11], - x12 = input[12], x13 = input[13], x14 = input[14], x15 = input[15]; + uint32_t x00 = input[0]; + uint32_t x01 = input[1]; + uint32_t x02 = input[2]; + uint32_t x03 = input[3]; + uint32_t x04 = input[4]; + uint32_t x05 = input[5]; + uint32_t x06 = input[6]; + uint32_t x07 = input[7]; + uint32_t x08 = input[8]; + uint32_t x09 = input[9]; + uint32_t x10 = input[10]; + uint32_t x11 = input[11]; + uint32_t x12 = input[12]; + uint32_t x13 = input[13]; + uint32_t x14 = input[14]; + uint32_t x15 = input[15]; for(size_t i = 0; i != rounds / 2; ++i) { chacha_quarter_round(x00, x04, x08, x12); @@ -72,13 +88,13 @@ size_t ChaCha::parallelism() { #if defined(BOTAN_HAS_CHACHA_AVX512) - if(CPUID::has_avx512()) { + if(CPUID::has(CPUID::Feature::AVX512)) { return 16; } #endif #if defined(BOTAN_HAS_CHACHA_AVX2) - if(CPUID::has_avx2()) { + if(CPUID::has(CPUID::Feature::AVX2)) { return 8; } #endif @@ -88,20 +104,20 @@ std::string ChaCha::provider() const { #if defined(BOTAN_HAS_CHACHA_AVX512) - if(CPUID::has_avx512()) { - return "avx512"; + if(auto feat = CPUID::check(CPUID::Feature::AVX512)) { + return *feat; } #endif #if defined(BOTAN_HAS_CHACHA_AVX2) - if(CPUID::has_avx2()) { - return "avx2"; + if(auto feat = CPUID::check(CPUID::Feature::AVX2)) { + return *feat; } #endif #if defined(BOTAN_HAS_CHACHA_SIMD32) - if(CPUID::has_simd_32()) { - return "simd32"; + if(auto feat = CPUID::check(CPUID::Feature::SIMD_4X32)) { + return *feat; } #endif @@ -112,7 +128,7 @@ BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds"); #if defined(BOTAN_HAS_CHACHA_AVX512) - if(CPUID::has_avx512()) { + if(CPUID::has(CPUID::Feature::AVX512)) { while(output_blocks >= 16) { ChaCha::chacha_avx512_x16(output, state, rounds); output += 16 * 64; @@ -122,7 +138,7 @@ #endif #if defined(BOTAN_HAS_CHACHA_AVX2) - if(CPUID::has_avx2()) { + if(CPUID::has(CPUID::Feature::AVX2)) { while(output_blocks >= 8) { ChaCha::chacha_avx2_x8(output, state, rounds); output += 8 * 64; @@ -132,7 +148,7 @@ #endif #if defined(BOTAN_HAS_CHACHA_SIMD32) - if(CPUID::has_simd_32()) { + if(CPUID::has(CPUID::Feature::SIMD_4X32)) { while(output_blocks >= 4) { ChaCha::chacha_simd32_x4(output, state, rounds); output += 4 * 64; @@ -143,9 +159,22 @@ // TODO interleave rounds for(size_t i = 0; i != output_blocks; ++i) { - uint32_t x00 = state[0], x01 = state[1], x02 = state[2], x03 = state[3], x04 = state[4], x05 = state[5], - x06 = state[6], x07 = state[7], x08 = state[8], x09 = state[9], x10 = state[10], x11 = state[11], - x12 = state[12], x13 = state[13], x14 = state[14], x15 = state[15]; + uint32_t x00 = state[0]; + uint32_t x01 = state[1]; + uint32_t x02 = state[2]; + uint32_t x03 = state[3]; + uint32_t x04 = state[4]; + uint32_t x05 = state[5]; + uint32_t x06 = state[6]; + uint32_t x07 = state[7]; + uint32_t x08 = state[8]; + uint32_t x09 = state[9]; + uint32_t x10 = state[10]; + uint32_t x11 = state[11]; + uint32_t x12 = state[12]; + uint32_t x13 = state[13]; + uint32_t x14 = state[14]; + uint32_t x15 = state[15]; for(size_t r = 0; r != rounds / 2; ++r) { chacha_quarter_round(x00, x04, x08, x12); @@ -194,7 +223,9 @@ store_le(x15, output + 64 * i + 4 * 15); state[12]++; - state[13] += (state[12] == 0); + if(state[12] == 0) { + state[13] += 1; + } } } diff -Nru botan3-3.7.1+dfsg/src/lib/stream/chacha/chacha_avx2/chacha_avx2.cpp botan3-3.12.0+dfsg/src/lib/stream/chacha/chacha_avx2/chacha_avx2.cpp --- botan3-3.7.1+dfsg/src/lib/stream/chacha/chacha_avx2/chacha_avx2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/stream/chacha/chacha_avx2/chacha_avx2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,19 +6,20 @@ #include +#include #include namespace Botan { //static -BOTAN_AVX2_FN -void ChaCha::chacha_avx2_x8(uint8_t output[64 * 8], uint32_t state[16], size_t rounds) { +void BOTAN_FN_ISA_AVX2 ChaCha::chacha_avx2_x8(uint8_t output[64 * 8], uint32_t state[16], size_t rounds) { SIMD_8x32::reset_registers(); BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds"); const SIMD_8x32 CTR0 = SIMD_8x32(0, 1, 2, 3, 4, 5, 6, 7); const uint32_t C = 0xFFFFFFFF - state[12]; + // NOLINTNEXTLINE(*-implicit-bool-conversion) const SIMD_8x32 CTR1 = SIMD_8x32(0, C < 1, C < 2, C < 3, C < 4, C < 5, C < 6, C < 7); SIMD_8x32 R00 = SIMD_8x32::splat(state[0]); diff -Nru botan3-3.7.1+dfsg/src/lib/stream/chacha/chacha_avx2/info.txt botan3-3.12.0+dfsg/src/lib/stream/chacha/chacha_avx2/info.txt --- botan3-3.7.1+dfsg/src/lib/stream/chacha/chacha_avx2/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/stream/chacha/chacha_avx2/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + CHACHA_AVX2 -> 20180418 - + name -> "ChaCha20 AVX2" @@ -13,4 +13,5 @@ simd_avx2 +cpuid diff -Nru botan3-3.7.1+dfsg/src/lib/stream/chacha/chacha_avx512/chacha_avx512.cpp botan3-3.12.0+dfsg/src/lib/stream/chacha/chacha_avx512/chacha_avx512.cpp --- botan3-3.7.1+dfsg/src/lib/stream/chacha/chacha_avx512/chacha_avx512.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/stream/chacha/chacha_avx512/chacha_avx512.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -5,19 +5,24 @@ */ #include + +#include #include namespace Botan { //static -BOTAN_AVX512_FN -void ChaCha::chacha_avx512_x16(uint8_t output[64 * 16], uint32_t state[16], size_t rounds) { +void BOTAN_FN_ISA_AVX512 ChaCha::chacha_avx512_x16(uint8_t output[64 * 16], uint32_t state[16], size_t rounds) { BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds"); const SIMD_16x32 CTR0 = SIMD_16x32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); const uint32_t C = 0xFFFFFFFF - state[12]; + + // clang-format off const SIMD_16x32 CTR1 = SIMD_16x32( + // NOLINTNEXTLINE(*-implicit-bool-conversion) 0, C < 1, C < 2, C < 3, C < 4, C < 5, C < 6, C < 7, C < 8, C < 9, C < 10, C < 11, C < 12, C < 13, C < 14, C < 15); + // clang-format on SIMD_16x32 R00 = SIMD_16x32::splat(state[0]); SIMD_16x32 R01 = SIMD_16x32::splat(state[1]); diff -Nru botan3-3.7.1+dfsg/src/lib/stream/chacha/chacha_avx512/info.txt botan3-3.12.0+dfsg/src/lib/stream/chacha/chacha_avx512/info.txt --- botan3-3.7.1+dfsg/src/lib/stream/chacha/chacha_avx512/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/stream/chacha/chacha_avx512/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + CHACHA_AVX512 -> 20230101 - + name -> "ChaCha20 AVX512" @@ -13,4 +13,5 @@ simd_avx512 +cpuid diff -Nru botan3-3.7.1+dfsg/src/lib/stream/chacha/chacha_simd32/chacha_simd32.cpp botan3-3.12.0+dfsg/src/lib/stream/chacha/chacha_simd32/chacha_simd32.cpp --- botan3-3.7.1+dfsg/src/lib/stream/chacha/chacha_simd32/chacha_simd32.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/stream/chacha/chacha_simd32/chacha_simd32.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,16 +6,19 @@ #include -#include +#include +#include namespace Botan { //static -void ChaCha::chacha_simd32_x4(uint8_t output[64 * 4], uint32_t state[16], size_t rounds) { +void BOTAN_FN_ISA_SIMD_4X32 ChaCha::chacha_simd32_x4(uint8_t output[64 * 4], uint32_t state[16], size_t rounds) { BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds"); const SIMD_4x32 CTR0 = SIMD_4x32(0, 1, 2, 3); const uint32_t C = 0xFFFFFFFF - state[12]; + + // NOLINTNEXTLINE(*-implicit-bool-conversion) const SIMD_4x32 CTR1 = SIMD_4x32(0, C < 1, C < 2, C < 3); SIMD_4x32 R00 = SIMD_4x32::splat(state[0]); diff -Nru botan3-3.7.1+dfsg/src/lib/stream/chacha/chacha_simd32/info.txt botan3-3.12.0+dfsg/src/lib/stream/chacha/chacha_simd32/info.txt --- botan3-3.7.1+dfsg/src/lib/stream/chacha/chacha_simd32/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/stream/chacha/chacha_simd32/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + CHACHA_SIMD32 -> 20181104 - + name -> "ChaCha20 SIMD" @@ -8,5 +8,18 @@ -simd +simd_4x32 +cpuid + + +x86_32:ssse3 +x86_64:ssse3 +x32:ssse3 +arm32:neon +arm64:neon +ppc32:altivec +ppc64:altivec +loongarch64:lsx +wasm:simd128 + diff -Nru botan3-3.7.1+dfsg/src/lib/stream/ctr/ctr.cpp botan3-3.12.0+dfsg/src/lib/stream/ctr/ctr.cpp --- botan3-3.7.1+dfsg/src/lib/stream/ctr/ctr.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/stream/ctr/ctr.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,6 +12,10 @@ #include #include +#if defined(BOTAN_HAS_CTR_BE_AVX2) || defined(BOTAN_HAS_CTR_BE_SIMD32) + #include +#endif + namespace Botan { CTR_BE::CTR_BE(std::unique_ptr cipher) : @@ -84,9 +88,11 @@ void CTR_BE::cipher_bytes(const uint8_t in[], uint8_t out[], size_t length) { assert_key_material_set(); - const uint8_t* pad_bits = &m_pad[0]; + const uint8_t* pad_bits = m_pad.data(); const size_t pad_size = m_pad.size(); + /* Consume any already computed keystream in m_pad */ + if(m_pad_pos > 0) { const size_t avail = pad_size - m_pad_pos; const size_t take = std::min(length, avail); @@ -103,6 +109,28 @@ } } + /* Bulk processing */ + + [[maybe_unused]] const bool can_use_bs16_ctr4_fastpath = m_block_size == 16 && m_ctr_size == 4 && pad_size % 64 == 0; + +#if defined(BOTAN_HAS_CTR_BE_AVX2) + if(length >= pad_size && can_use_bs16_ctr4_fastpath && CPUID::has(CPUID::Feature::AVX2)) { + const size_t consumed = ctr_proc_bs16_ctr4_avx2(in, out, length); + in += consumed; + out += consumed; + length -= consumed; + } +#endif + +#if defined(BOTAN_HAS_CTR_BE_SIMD32) + if(length >= pad_size && can_use_bs16_ctr4_fastpath && CPUID::has(CPUID::Feature::SIMD_4X32)) { + const size_t consumed = ctr_proc_bs16_ctr4_simd32(in, out, length); + in += consumed; + out += consumed; + length -= consumed; + } +#endif + while(length >= pad_size) { xor_buf(out, in, pad_bits, pad_size); length -= pad_size; @@ -113,8 +141,11 @@ m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks); } - xor_buf(out, in, pad_bits, length); - m_pad_pos += length; + /* Now if length > 0 then we have some remaining text, and m_pad is full - consume as required */ + if(length > 0) { + xor_buf(out, in, pad_bits, length); + m_pad_pos = length; + } } void CTR_BE::generate_keystream(uint8_t out[], size_t length) { @@ -141,7 +172,7 @@ m_pad_pos = 0; } - copy_mem(out, &m_pad[0], length); + copy_mem(out, m_pad.data(), length); m_pad_pos += length; BOTAN_ASSERT_NOMSG(m_pad_pos < m_pad.size()); } @@ -153,7 +184,7 @@ m_iv.resize(m_block_size); zeroise(m_iv); - copy_mem(&m_iv[0], iv, iv_len); + copy_mem(m_iv.data(), iv, iv_len); seek(0); } @@ -188,13 +219,15 @@ store_be(b0, &m_counter[i * BS + off]); store_be(b1, &m_counter[i * BS + off + 8]); b1 += 1; - b0 += (b1 == 0); // carry + if(b1 == 0) { + b0 += 1; // carry + } } } else { for(size_t i = 0; i != ctr_blocks; ++i) { uint64_t local_counter = counter; uint16_t carry = static_cast(local_counter); - for(size_t j = 0; (carry || local_counter) && j != ctr_size; ++j) { + for(size_t j = 0; (carry > 0 || local_counter > 0) && j != ctr_size; ++j) { const size_t off = i * BS + (BS - 1 - j); const uint16_t cnt = static_cast(m_counter[off]) + carry; m_counter[off] = static_cast(cnt); @@ -212,7 +245,7 @@ zeroise(m_counter); BOTAN_ASSERT_NOMSG(m_counter.size() >= m_iv.size()); - copy_mem(&m_counter[0], &m_iv[0], m_iv.size()); + copy_mem(m_counter.data(), m_iv.data(), m_iv.size()); const size_t BS = m_block_size; @@ -224,12 +257,12 @@ if(m_ctr_blocks >= 4 && is_power_of_2(m_ctr_blocks)) { size_t written = 1; while(written < m_ctr_blocks) { - copy_mem(&m_counter[written * BS], &m_counter[0], BS * written); + copy_mem(&m_counter[written * BS], &m_counter[0], BS * written); // NOLINT(*container-data-pointer) written *= 2; } } else { for(size_t i = 1; i != m_ctr_blocks; ++i) { - copy_mem(&m_counter[i * BS], &m_counter[0], BS - 4); + copy_mem(&m_counter[i * BS], &m_counter[0], BS - 4); // NOLINT(*container-data-pointer) } } @@ -243,7 +276,9 @@ copy_mem(&m_counter[i * BS], &m_counter[(i - 1) * BS], BS); for(size_t j = 0; j != m_ctr_size; ++j) { - if(++m_counter[i * BS + (BS - 1 - j)]) { + uint8_t& c = m_counter[i * BS + (BS - 1 - j)]; + c += 1; + if(c > 0) { break; } } diff -Nru botan3-3.7.1+dfsg/src/lib/stream/ctr/ctr.h botan3-3.12.0+dfsg/src/lib/stream/ctr/ctr.h --- botan3-3.7.1+dfsg/src/lib/stream/ctr/ctr.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/stream/ctr/ctr.h 2026-05-07 01:38:28.000000000 +0000 @@ -50,6 +50,14 @@ void set_iv_bytes(const uint8_t iv[], size_t iv_len) override; void add_counter(uint64_t counter); +#if defined(BOTAN_HAS_CTR_BE_AVX2) + size_t ctr_proc_bs16_ctr4_avx2(const uint8_t in[], uint8_t out[], size_t length); +#endif + +#if defined(BOTAN_HAS_CTR_BE_SIMD32) + size_t ctr_proc_bs16_ctr4_simd32(const uint8_t in[], uint8_t out[], size_t length); +#endif + std::unique_ptr m_cipher; const size_t m_block_size; diff -Nru botan3-3.7.1+dfsg/src/lib/stream/ctr/ctr_avx2/ctr_avx2.cpp botan3-3.12.0+dfsg/src/lib/stream/ctr/ctr_avx2/ctr_avx2.cpp --- botan3-3.7.1+dfsg/src/lib/stream/ctr/ctr_avx2/ctr_avx2.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/stream/ctr/ctr_avx2/ctr_avx2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,83 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan { + +BOTAN_FN_ISA_AVX2 +size_t CTR_BE::ctr_proc_bs16_ctr4_avx2(const uint8_t* in, uint8_t* out, size_t length) { + BOTAN_ASSERT_NOMSG(m_pad.size() % 64 == 0); + BOTAN_DEBUG_ASSERT(m_counter.size() == m_pad.size()); + + const size_t pad_size = m_pad.size(); + if(length < pad_size) { + return 0; + } + + const size_t ctr_blocks = m_ctr_blocks; + + /* + * Byte swap table that swaps only the counter bytes and not the nonce bytes + */ + const SIMD_8x32 bswap_ctr( + 0x03020100, 0x07060504, 0x0B0A0908, 0x0C0D0E0F, 0x03020100, 0x07060504, 0x0B0A0908, 0x0C0D0E0F); + + // Load the starting counter value, bswap the counter field itself so we can add + const SIMD_8x32 starting_ctr = SIMD_8x32::byte_shuffle(SIMD_8x32::load_le128(m_counter.data()), bswap_ctr); + + // Counter is incremented 4 blocks at a time (2 per register, 2 registers) + const SIMD_8x32 inc4(0, 0, 0, 4); + + const uint32_t N = static_cast(ctr_blocks); + SIMD_8x32 batch_ctr0 = starting_ctr + SIMD_8x32(0, 0, 0, N, 0, 0, 0, N + 1); + SIMD_8x32 batch_ctr1 = starting_ctr + SIMD_8x32(0, 0, 0, N + 2, 0, 0, 0, N + 3); + const uint8_t* pad_buf = m_pad.data(); + uint8_t* ctr_buf = m_counter.data(); + + const size_t ctr_block_quads = ctr_blocks / 4; + + size_t processed = 0; + + while(length >= pad_size) { + for(size_t i = 0; i != ctr_block_quads; ++i) { + const size_t off = i * 64; + + // Store and update the counters + SIMD_8x32::byte_shuffle(batch_ctr0, bswap_ctr).store_le(ctr_buf + off); + SIMD_8x32::byte_shuffle(batch_ctr1, bswap_ctr).store_le(ctr_buf + off + 32); + batch_ctr0 += inc4; + batch_ctr1 += inc4; + + const auto p0 = SIMD_8x32::load_le(pad_buf + off); + const auto p1 = SIMD_8x32::load_le(pad_buf + off + 32); + + auto i0 = SIMD_8x32::load_le(in + off); + auto i1 = SIMD_8x32::load_le(in + off + 32); + + i0 ^= p0; + i1 ^= p1; + + i0.store_le(out + off); + i1.store_le(out + off + 32); + } + + in += pad_size; + out += pad_size; + length -= pad_size; + processed += pad_size; + + // Regenerate the pad buffer + m_cipher->encrypt_n(m_counter.data(), m_pad.data(), ctr_blocks); + } + + return processed; +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/stream/ctr/ctr_avx2/info.txt botan3-3.12.0+dfsg/src/lib/stream/ctr/ctr_avx2/info.txt --- botan3-3.7.1+dfsg/src/lib/stream/ctr/ctr_avx2/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/stream/ctr/ctr_avx2/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,17 @@ + +CTR_BE_AVX2 -> 20260321 + + + +name -> "CTR-BE AVX2" +brief -> "AVX2-accelerated CTR-BE counter management and XOR" + + + +avx2 + + + +cpuid +simd_avx2 + diff -Nru botan3-3.7.1+dfsg/src/lib/stream/ctr/ctr_simd32/ctr_simd32.cpp botan3-3.12.0+dfsg/src/lib/stream/ctr/ctr_simd32/ctr_simd32.cpp --- botan3-3.7.1+dfsg/src/lib/stream/ctr/ctr_simd32/ctr_simd32.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/stream/ctr/ctr_simd32/ctr_simd32.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,89 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan { + +BOTAN_FN_ISA_SIMD_4X32 +size_t CTR_BE::ctr_proc_bs16_ctr4_simd32(const uint8_t* in, uint8_t* out, size_t length) { + BOTAN_ASSERT_NOMSG(m_pad.size() % 64 == 0); + BOTAN_DEBUG_ASSERT(m_counter.size() == m_pad.size()); + + const size_t pad_size = m_pad.size(); + if(length < pad_size) { + return 0; + } + + const size_t ctr_blocks = m_ctr_blocks; + + // Load the starting counter as big-endian 32-bit words. + // Word 3 (bytes 12-15) contains the counter value in native form. + const SIMD_4x32 starting_ctr = SIMD_4x32::load_be(m_counter.data()); + + const uint32_t N = static_cast(ctr_blocks); + + // Initialize 4 counter registers for 4-way unrolled processing + SIMD_4x32 ctr0 = starting_ctr + SIMD_4x32(0, 0, 0, N); + SIMD_4x32 ctr1 = starting_ctr + SIMD_4x32(0, 0, 0, N + 1); + SIMD_4x32 ctr2 = starting_ctr + SIMD_4x32(0, 0, 0, N + 2); + SIMD_4x32 ctr3 = starting_ctr + SIMD_4x32(0, 0, 0, N + 3); + const SIMD_4x32 inc4 = SIMD_4x32(0, 0, 0, 4); + + const uint8_t* pad_buf = m_pad.data(); + uint8_t* ctr_buf = m_counter.data(); + + const size_t ctr_block_quads = ctr_blocks / 4; + + size_t processed = 0; + + while(length >= pad_size) { + for(size_t i = 0; i != ctr_block_quads; ++i) { + const size_t off = i * 64; + + // Store and update the counter + ctr0.store_be(ctr_buf + off); + ctr1.store_be(ctr_buf + off + 16); + ctr2.store_be(ctr_buf + off + 32); + ctr3.store_be(ctr_buf + off + 48); + ctr0 += inc4; + ctr1 += inc4; + ctr2 += inc4; + ctr3 += inc4; + + // Load and XOR the pad with the input blocks + auto p0 = SIMD_4x32::load_le(pad_buf + off); + auto p1 = SIMD_4x32::load_le(pad_buf + off + 16); + auto p2 = SIMD_4x32::load_le(pad_buf + off + 32); + auto p3 = SIMD_4x32::load_le(pad_buf + off + 48); + + p0 ^= SIMD_4x32::load_le(in + off); + p1 ^= SIMD_4x32::load_le(in + off + 16); + p2 ^= SIMD_4x32::load_le(in + off + 32); + p3 ^= SIMD_4x32::load_le(in + off + 48); + + p0.store_le(out + off); + p1.store_le(out + off + 16); + p2.store_le(out + off + 32); + p3.store_le(out + off + 48); + } + + in += pad_size; + out += pad_size; + length -= pad_size; + processed += pad_size; + + // Regenerate the pad buffer + m_cipher->encrypt_n(m_counter.data(), m_pad.data(), ctr_blocks); + } + + return processed; +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/stream/ctr/ctr_simd32/info.txt botan3-3.12.0+dfsg/src/lib/stream/ctr/ctr_simd32/info.txt --- botan3-3.7.1+dfsg/src/lib/stream/ctr/ctr_simd32/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/stream/ctr/ctr_simd32/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,26 @@ + +CTR_BE_SIMD32 -> 20260323 + + + +name -> "CTR-BE SIMD" +brief -> "SIMD-accelerated CTR-BE counter management and XOR" + + + +cpuid +simd_4x32 + + + +x86_32:ssse3 +x86_64:ssse3 +x32:ssse3 +arm32:neon +arm64:neon + +# disabled until tested +#ppc64:altivec +#loongarch64:lsx +#wasm:simd128 + diff -Nru botan3-3.7.1+dfsg/src/lib/stream/ofb/ofb.cpp botan3-3.12.0+dfsg/src/lib/stream/ofb/ofb.cpp --- botan3-3.7.1+dfsg/src/lib/stream/ofb/ofb.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/stream/ofb/ofb.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include #include +#include #include namespace Botan { @@ -76,7 +77,7 @@ zeroise(m_buffer); BOTAN_ASSERT_NOMSG(m_buffer.size() >= iv_len); - copy_mem(&m_buffer[0], iv, iv_len); + copy_mem(m_buffer.data(), iv, iv_len); m_cipher->encrypt(m_buffer); m_buf_pos = 0; diff -Nru botan3-3.7.1+dfsg/src/lib/stream/rc4/rc4.cpp botan3-3.12.0+dfsg/src/lib/stream/rc4/rc4.cpp --- botan3-3.7.1+dfsg/src/lib/stream/rc4/rc4.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/stream/rc4/rc4.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -51,11 +51,10 @@ * Generate cipher stream */ void RC4::generate() { - uint8_t SX, SY; for(size_t i = 0; i != m_buffer.size(); i += 4) { - SX = m_state[m_X + 1]; + uint8_t SX = m_state[m_X + 1]; m_Y = (m_Y + SX) % 256; - SY = m_state[m_Y]; + uint8_t SY = m_state[m_Y]; m_state[m_X + 1] = SY; m_state[m_Y] = SX; m_buffer[i] = m_state[(SX + SY) % 256]; diff -Nru botan3-3.7.1+dfsg/src/lib/stream/rc4/rc4.h botan3-3.12.0+dfsg/src/lib/stream/rc4/rc4.h --- botan3-3.7.1+dfsg/src/lib/stream/rc4/rc4.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/stream/rc4/rc4.h 2026-05-07 01:38:28.000000000 +0000 @@ -36,8 +36,6 @@ */ explicit RC4(size_t skip = 0); - ~RC4() override { clear(); } - private: void key_schedule(std::span key) override; void cipher_bytes(const uint8_t in[], uint8_t out[], size_t length) override; diff -Nru botan3-3.7.1+dfsg/src/lib/stream/salsa20/salsa20.cpp botan3-3.12.0+dfsg/src/lib/stream/salsa20/salsa20.cpp --- botan3-3.7.1+dfsg/src/lib/stream/salsa20/salsa20.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/stream/salsa20/salsa20.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -29,9 +29,22 @@ */ //static void Salsa20::hsalsa20(uint32_t output[8], const uint32_t input[16]) { - uint32_t x00 = input[0], x01 = input[1], x02 = input[2], x03 = input[3], x04 = input[4], x05 = input[5], - x06 = input[6], x07 = input[7], x08 = input[8], x09 = input[9], x10 = input[10], x11 = input[11], - x12 = input[12], x13 = input[13], x14 = input[14], x15 = input[15]; + uint32_t x00 = input[0]; + uint32_t x01 = input[1]; + uint32_t x02 = input[2]; + uint32_t x03 = input[3]; + uint32_t x04 = input[4]; + uint32_t x05 = input[5]; + uint32_t x06 = input[6]; + uint32_t x07 = input[7]; + uint32_t x08 = input[8]; + uint32_t x09 = input[9]; + uint32_t x10 = input[10]; + uint32_t x11 = input[11]; + uint32_t x12 = input[12]; + uint32_t x13 = input[13]; + uint32_t x14 = input[14]; + uint32_t x15 = input[15]; for(size_t i = 0; i != 10; ++i) { salsa20_quarter_round(x00, x04, x08, x12); @@ -62,9 +75,22 @@ void Salsa20::salsa_core(uint8_t output[64], const uint32_t input[16], size_t rounds) { BOTAN_ASSERT_NOMSG(rounds % 2 == 0); - uint32_t x00 = input[0], x01 = input[1], x02 = input[2], x03 = input[3], x04 = input[4], x05 = input[5], - x06 = input[6], x07 = input[7], x08 = input[8], x09 = input[9], x10 = input[10], x11 = input[11], - x12 = input[12], x13 = input[13], x14 = input[14], x15 = input[15]; + uint32_t x00 = input[0]; + uint32_t x01 = input[1]; + uint32_t x02 = input[2]; + uint32_t x03 = input[3]; + uint32_t x04 = input[4]; + uint32_t x05 = input[5]; + uint32_t x06 = input[6]; + uint32_t x07 = input[7]; + uint32_t x08 = input[8]; + uint32_t x09 = input[9]; + uint32_t x10 = input[10]; + uint32_t x11 = input[11]; + uint32_t x12 = input[12]; + uint32_t x13 = input[13]; + uint32_t x14 = input[14]; + uint32_t x15 = input[15]; for(size_t i = 0; i != rounds / 2; ++i) { salsa20_quarter_round(x00, x04, x08, x12); @@ -109,7 +135,9 @@ salsa_core(m_buffer.data(), m_state.data(), 20); ++m_state[8]; - m_state[9] += (m_state[8] == 0); + if(m_state[8] == 0) { + m_state[9] += 1; + } length -= available; in += available; @@ -229,7 +257,9 @@ salsa_core(m_buffer.data(), m_state.data(), 20); ++m_state[8]; - m_state[9] += (m_state[8] == 0); + if(m_state[8] == 0) { + m_state[9] += 1; + } m_position = 0; } @@ -278,7 +308,9 @@ salsa_core(m_buffer.data(), m_state.data(), 20); ++m_state[8]; - m_state[9] += (m_state[8] == 0); + if(m_state[8] == 0) { + m_state[9] += 1; + } m_position = offset % 64; } diff -Nru botan3-3.7.1+dfsg/src/lib/stream/shake_cipher/shake_cipher.cpp botan3-3.12.0+dfsg/src/lib/stream/shake_cipher/shake_cipher.cpp --- botan3-3.7.1+dfsg/src/lib/stream/shake_cipher/shake_cipher.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/stream/shake_cipher/shake_cipher.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -14,7 +14,7 @@ namespace Botan { SHAKE_Cipher::SHAKE_Cipher(size_t keccak_capacity) : - m_keccak(keccak_capacity, 0xF, 4), + m_keccak({.capacity_bits = keccak_capacity, .padding = KeccakPadding::shake()}), m_has_keying_material(false), m_keystream_buffer(buffer_size()), m_bytes_generated(0) {} diff -Nru botan3-3.7.1+dfsg/src/lib/stream/stream_cipher.cpp botan3-3.12.0+dfsg/src/lib/stream/stream_cipher.cpp --- botan3-3.7.1+dfsg/src/lib/stream/stream_cipher.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/stream/stream_cipher.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -75,7 +75,7 @@ if(provider.empty() || provider == "base") { auto cipher = BlockCipher::create(req.arg(0)); if(cipher) { - size_t ctr_size = req.arg_as_integer(1, cipher->block_size()); + const size_t ctr_size = req.arg_as_integer(1, cipher->block_size()); return std::make_unique(std::move(cipher), ctr_size); } } @@ -130,6 +130,11 @@ return probe_providers_of(algo_spec); } +void StreamCipher::cipher(std::span in, std::span out) { + BOTAN_ARG_CHECK(in.size() <= out.size(), "Output buffer of stream cipher must be at least as long as input buffer"); + cipher_bytes(in.data(), out.data(), in.size()); +} + size_t StreamCipher::default_iv_length() const { return 0; } diff -Nru botan3-3.7.1+dfsg/src/lib/stream/stream_cipher.h botan3-3.12.0+dfsg/src/lib/stream/stream_cipher.h --- botan3-3.7.1+dfsg/src/lib/stream/stream_cipher.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/stream/stream_cipher.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #define BOTAN_STREAM_CIPHER_H_ #include +#include #include #include #include @@ -22,8 +23,6 @@ */ class BOTAN_PUBLIC_API(2, 0) StreamCipher : public SymmetricAlgorithm { public: - ~StreamCipher() override = default; - /** * Create an instance based on a name * If provider is empty then best available is chosen. @@ -65,11 +64,7 @@ * @param out the byte array to hold the output, i.e. the ciphertext * with at least the same size as @p in */ - void cipher(std::span in, std::span out) { - BOTAN_ARG_CHECK(in.size() <= out.size(), - "Output buffer of stream cipher must be at least as long as input buffer"); - cipher_bytes(in.data(), out.data(), in.size()); - } + void cipher(std::span in, std::span out); /** * Write keystream bytes to a buffer @@ -93,7 +88,7 @@ /** * Get @p bytes from the keystream * - * The bytes are written into a continous byte buffer of your choosing. + * The bytes are written into a continuous byte buffer of your choosing. * * @param bytes The number of bytes to be produced */ diff -Nru botan3-3.7.1+dfsg/src/lib/tls/asio/asio_async_ops.h botan3-3.12.0+dfsg/src/lib/tls/asio/asio_async_ops.h --- botan3-3.7.1+dfsg/src/lib/tls/asio/asio_async_ops.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/asio/asio_async_ops.h 2026-05-07 01:38:28.000000000 +0000 @@ -91,8 +91,8 @@ m_handler(std::forward(args)...); } - Handler m_handler; - boost::asio::executor_work_guard m_work_guard_1; + Handler m_handler; // NOLINT(*-non-private-member-variable*) + boost::asio::executor_work_guard m_work_guard_1; // NOLINT(*-non-private-member-variable*) }; template > @@ -119,7 +119,11 @@ this->operator()(ec, std::size_t(0), false); } - AsyncReadOperation(AsyncReadOperation&&) = default; + AsyncReadOperation(const AsyncReadOperation& other) = delete; + AsyncReadOperation(AsyncReadOperation&& other) = default; + AsyncReadOperation& operator=(const AsyncReadOperation& other) = delete; + AsyncReadOperation& operator=(AsyncReadOperation&& other) = delete; + ~AsyncReadOperation() = default; /** * Read and decrypt application data from the peer. Note, that this is @@ -148,7 +152,7 @@ // an application data record, the "input buffer" will become // non-empty. if(!ec && bytes_transferred > 0) { - boost::asio::const_buffer read_buffer{m_stream.input_buffer().data(), bytes_transferred}; + const boost::asio::const_buffer read_buffer{m_stream.input_buffer().data(), bytes_transferred}; m_stream.process_encrypted_data(read_buffer); } @@ -218,7 +222,11 @@ this->operator()(ec, std::size_t(0), false); } - AsyncWriteOperation(AsyncWriteOperation&&) = default; + AsyncWriteOperation(const AsyncWriteOperation& other) = delete; + AsyncWriteOperation(AsyncWriteOperation&& other) = default; + AsyncWriteOperation& operator=(const AsyncWriteOperation& other) = delete; + AsyncWriteOperation& operator=(AsyncWriteOperation&& other) = delete; + ~AsyncWriteOperation() = default; /** * Write (encrypted) TLS record data generated by us and bound to the peer @@ -288,7 +296,11 @@ this->operator()(ec, std::size_t(0), false); } - AsyncHandshakeOperation(AsyncHandshakeOperation&&) = default; + AsyncHandshakeOperation(const AsyncHandshakeOperation& other) = delete; + AsyncHandshakeOperation(AsyncHandshakeOperation&& other) = default; + AsyncHandshakeOperation& operator=(const AsyncHandshakeOperation& other) = delete; + AsyncHandshakeOperation& operator=(AsyncHandshakeOperation&& other) = delete; + ~AsyncHandshakeOperation() = default; /** * Perform a TLS handshake with the peer. @@ -316,7 +328,7 @@ // result in the advancement of the handshake state and produce data // in the output buffer. if(!ec && bytesTransferred > 0) { - boost::asio::const_buffer read_buffer{m_stream.input_buffer().data(), bytesTransferred}; + const boost::asio::const_buffer read_buffer{m_stream.input_buffer().data(), bytesTransferred}; m_stream.process_encrypted_data(read_buffer); } @@ -328,9 +340,9 @@ // operation will eventually call `*this` as its own handler, passing the 0 back to this call operator. // This is necessary because the check of `bytesTransferred > 0` assumes that `bytesTransferred` bytes // were just read and are available in input_buffer for further processing. - AsyncWriteOperation::type, Stream, Allocator>, - Stream, - Allocator> + const AsyncWriteOperation, Stream, Allocator>, + Stream, + Allocator> op{std::move(*this), m_stream, 0}; return; } diff -Nru botan3-3.7.1+dfsg/src/lib/tls/asio/asio_compat.h botan3-3.12.0+dfsg/src/lib/tls/asio/asio_compat.h --- botan3-3.7.1+dfsg/src/lib/tls/asio/asio_compat.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/asio/asio_compat.h 2026-05-07 01:38:28.000000000 +0000 @@ -13,6 +13,8 @@ #if defined(BOTAN_HAS_BOOST_ASIO) + // NOLINTBEGIN(*-macro-usage) + #include /** @brief minimum supported boost version for the TLS ASIO wrapper @@ -43,5 +45,8 @@ #endif + // NOLINTEND(*-macro-usage) + #endif + #endif diff -Nru botan3-3.7.1+dfsg/src/lib/tls/asio/asio_context.cpp botan3-3.12.0+dfsg/src/lib/tls/asio/asio_context.cpp --- botan3-3.7.1+dfsg/src/lib/tls/asio/asio_context.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/asio/asio_context.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,15 +8,16 @@ #include -#if defined(BOTAN_HAS_HAS_DEFAULT_TLS_CONTEXT) +#if defined(BOTAN_HAS_DEFAULT_TLS_CONTEXT) #include #include + #include #include #endif namespace Botan::TLS { -#if defined(BOTAN_HAS_HAS_DEFAULT_TLS_CONTEXT) +#if defined(BOTAN_HAS_DEFAULT_TLS_CONTEXT) namespace { @@ -34,7 +35,8 @@ } } - std::vector trusted_certificate_authorities(const std::string&, const std::string&) override { + std::vector trusted_certificate_authorities(const std::string& /*type*/, + const std::string& /*context*/) override { if(m_cert_store) { return {m_cert_store.get()}; } else { diff -Nru botan3-3.7.1+dfsg/src/lib/tls/asio/asio_context.h botan3-3.12.0+dfsg/src/lib/tls/asio/asio_context.h --- botan3-3.7.1+dfsg/src/lib/tls/asio/asio_context.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/asio/asio_context.h 2026-05-07 01:38:28.000000000 +0000 @@ -23,6 +23,9 @@ #include #if defined(BOTAN_HAS_AUTO_SEEDING_RNG) && defined(BOTAN_HAS_CERTSTOR_SYSTEM) + #define BOTAN_HAS_DEFAULT_TLS_CONTEXT + + // TODO(Botan4) remove this #define BOTAN_HAS_HAS_DEFAULT_TLS_CONTEXT #endif @@ -51,13 +54,13 @@ */ using Verify_Callback = detail::fn_signature_helper::type; - #if defined(BOTAN_HAS_HAS_DEFAULT_TLS_CONTEXT) + #if defined(BOTAN_HAS_DEFAULT_TLS_CONTEXT) /** * @brief Construct a TLS stream context with typical defaults * * @param server_info Basic information about the host to connect to (SNI) */ - Context(Server_Information server_info = Server_Information()); + BOTAN_FUTURE_EXPLICIT Context(Server_Information server_info = Server_Information()); #endif Context(std::shared_ptr credentials_manager, @@ -65,10 +68,10 @@ std::shared_ptr session_manager, std::shared_ptr policy, Server_Information server_info = Server_Information()) : - m_credentials_manager(credentials_manager), - m_rng(rng), - m_session_manager(session_manager), - m_policy(policy), + m_credentials_manager(std::move(credentials_manager)), + m_rng(std::move(rng)), + m_session_manager(std::move(session_manager)), + m_policy(std::move(policy)), m_server_info(std::move(server_info)) {} virtual ~Context() = default; @@ -95,10 +98,16 @@ void set_server_info(Server_Information server_info) { m_server_info = std::move(server_info); } + void set_app_protocols(std::vector app_protocols = {}) { + m_app_protocols = std::move(app_protocols); + } + protected: template friend class Stream; + friend class StreamCallbacks; + // NOLINTBEGIN(*-non-private-member-variable*) std::shared_ptr m_credentials_manager; std::shared_ptr m_rng; std::shared_ptr m_session_manager; @@ -106,6 +115,8 @@ Server_Information m_server_info; Verify_Callback m_verify_callback; + std::vector m_app_protocols; + // NOLINTEND(*-non-private-member-variable*) }; } // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/asio/asio_error.h botan3-3.12.0+dfsg/src/lib/tls/asio/asio_error.h --- botan3-3.7.1+dfsg/src/lib/tls/asio/asio_error.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/asio/asio_error.h 2026-05-07 01:38:28.000000000 +0000 @@ -26,14 +26,30 @@ */ namespace Botan { + +/** +* Generic base class wrapping boost::system::error_category and +* adding a (bizarrely missing) virtual destructor. +*/ +class BoostErrorCategory : public boost::system::error_category { + public: + virtual ~BoostErrorCategory() = default; + + BoostErrorCategory() = default; + BoostErrorCategory(const BoostErrorCategory& other) = delete; + BoostErrorCategory(BoostErrorCategory&& other) = delete; + BoostErrorCategory& operator=(const BoostErrorCategory& other) = delete; + BoostErrorCategory& operator=(BoostErrorCategory&& other) = delete; +}; + namespace TLS { -enum StreamError { StreamTruncated = 1 }; +// NOLINTNEXTLINE(*-use-enum-class) +enum StreamError : uint8_t { StreamTruncated = 1 }; //! @brief An error category for errors from the TLS::Stream -struct StreamCategory : public boost::system::error_category { - virtual ~StreamCategory() = default; - +class StreamCategory final : public BoostErrorCategory { + public: const char* name() const noexcept override { return "Botan TLS Stream"; } std::string message(int value) const override { @@ -46,7 +62,7 @@ }; inline const StreamCategory& botan_stream_category() { - static StreamCategory category; + static const StreamCategory category; return category; } @@ -55,19 +71,18 @@ } //! @brief An error category for TLS alerts -struct BotanAlertCategory : boost::system::error_category { - virtual ~BotanAlertCategory() = default; - +class BotanAlertCategory final : public BoostErrorCategory { + public: const char* name() const noexcept override { return "Botan TLS Alert"; } std::string message(int ev) const override { - Botan::TLS::Alert alert(static_cast(ev)); + const Botan::TLS::Alert alert(static_cast(ev)); return alert.type_string(); } }; inline const BotanAlertCategory& botan_alert_category() noexcept { - static BotanAlertCategory category; + static const BotanAlertCategory category; return category; } @@ -78,16 +93,15 @@ } // namespace TLS //! @brief An error category for errors from Botan (other than TLS alerts) -struct BotanErrorCategory : boost::system::error_category { - virtual ~BotanErrorCategory() = default; - +class BotanErrorCategory : public BoostErrorCategory { + public: const char* name() const noexcept override { return "Botan"; } std::string message(int ev) const override { return Botan::to_string(static_cast(ev)); } }; inline const BotanErrorCategory& botan_category() noexcept { - static BotanErrorCategory category; + static const BotanErrorCategory category; return category; } diff -Nru botan3-3.7.1+dfsg/src/lib/tls/asio/asio_stream.h botan3-3.12.0+dfsg/src/lib/tls/asio/asio_stream.h --- botan3-3.7.1+dfsg/src/lib/tls/asio/asio_stream.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/asio/asio_stream.h 2026-05-07 01:38:28.000000000 +0000 @@ -31,7 +31,6 @@ #include #include - #include #include #include @@ -57,14 +56,14 @@ */ class StreamCallbacks : public Callbacks { public: - StreamCallbacks() {} + StreamCallbacks() = default; void tls_emit_data(std::span data) final { m_send_buffer.commit(boost::asio::buffer_copy(m_send_buffer.prepare(data.size()), boost::asio::buffer(data.data(), data.size()))); } - void tls_record_received(uint64_t, std::span data) final { + void tls_record_received(uint64_t /*record_number*/, std::span data) final { m_receive_buffer.commit(boost::asio::buffer_copy(m_receive_buffer.prepare(data.size()), boost::asio::const_buffer(data.data(), data.size()))); } @@ -108,6 +107,29 @@ } } + std::string tls_server_choose_app_protocol(const std::vector& client_protos) override { + if(client_protos.empty()) { + return ""; + } + auto ctx = m_context.lock(); + + if(!ctx || ctx->m_app_protocols.empty()) { + return ""; + } + // Priority is to the server. + for(const auto& server_proto : ctx->m_app_protocols) { + for(const auto& client_proto : client_protos) { + if(server_proto == client_proto) { + return server_proto; + } + } + } + // No match. + throw TLS_Exception( + Alert::NoApplicationProtocol, + "Rejecting ALPN request: no overlap between client-offered and server-configured application protocols"); + } + private: // The members below are meant for the tightly-coupled Stream class only template @@ -214,7 +236,7 @@ std::shared_ptr callbacks = std::make_shared()) : Stream(std::move(context), std::move(callbacks), std::forward(arg)) {} - #if defined(BOTAN_HAS_HAS_DEFAULT_TLS_CONTEXT) + #if defined(BOTAN_HAS_DEFAULT_TLS_CONTEXT) /** * @brief Conveniently construct a new Stream with default settings * @@ -242,7 +264,7 @@ //! \name boost::asio accessor methods //! @{ - using next_layer_type = typename std::remove_reference::type; + using next_layer_type = std::remove_reference_t; const next_layer_type& next_layer() const { return m_nextLayer; } @@ -258,10 +280,12 @@ executor_type get_executor() noexcept { return m_nextLayer.get_executor(); } - using native_handle_type = typename std::add_pointer::type; + using native_handle_type = std::add_pointer_t; native_handle_type native_handle() { - BOTAN_STATE_CHECK(m_native_handle != nullptr); + if(m_native_handle == nullptr) { + throw Botan::Invalid_State("ASIO native handle unexpectedly null"); + } return m_native_handle.get(); } @@ -288,8 +312,7 @@ * @param callback the callback implementation * @param ec This parameter is unused. */ - void set_verify_callback(Context::Verify_Callback callback, boost::system::error_code& ec) { - BOTAN_UNUSED(ec); + void set_verify_callback(Context::Verify_Callback callback, [[maybe_unused]] boost::system::error_code& ec) { m_context->set_verify_callback(std::move(callback)); } @@ -298,8 +321,7 @@ * @param depth the desired verification depth * @throws Not_Implemented todo */ - void set_verify_depth(int depth) { - BOTAN_UNUSED(depth); + void set_verify_depth([[maybe_unused]] int depth) { throw Not_Implemented("set_verify_depth is not implemented"); } @@ -308,8 +330,7 @@ * @param depth the desired verification depth * @param ec Will be set to `Botan::ErrorType::NotImplemented` */ - void set_verify_depth(int depth, boost::system::error_code& ec) { - BOTAN_UNUSED(depth); + void set_verify_depth([[maybe_unused]] int depth, boost::system::error_code& ec) { ec = ErrorType::NotImplemented; } @@ -319,8 +340,7 @@ * @throws Not_Implemented todo */ template - void set_verify_mode(verify_mode v) { - BOTAN_UNUSED(v); + void set_verify_mode([[maybe_unused]] verify_mode v) { throw Not_Implemented("set_verify_mode is not implemented"); } @@ -330,8 +350,7 @@ * @param ec Will be set to `Botan::ErrorType::NotImplemented` */ template - void set_verify_mode(verify_mode v, boost::system::error_code& ec) { - BOTAN_UNUSED(v); + void set_verify_mode([[maybe_unused]] verify_mode v, boost::system::error_code& ec) { ec = ErrorType::NotImplemented; } @@ -345,7 +364,7 @@ * The function call will block until handshaking is complete or an error occurs. * * @param side The type of handshaking to be performed, i.e. as a client or as a server. - * @throws boost::system::system_error if error occured + * @throws boost::system::system_error if error occurred */ void handshake(Connection_Side side) { boost::system::error_code ec; @@ -365,7 +384,7 @@ setup_native_handle(side, ec); // We write to the socket if we have data to send and read from it - // otherwise, until either some error occured or we have successfully + // otherwise, until either some error occurred or we have successfully // performed the handshake. while(!ec) { // Send pending data to the peer and abort the handshake if that @@ -393,8 +412,6 @@ // handled by `handle_tls_protocol_errors()` in the next iteration. read_and_process_encrypted_data_from_peer(ec); } - - BOTAN_ASSERT_NOMSG(ec.failed()); } /** @@ -408,6 +425,7 @@ */ template auto async_handshake(Botan::TLS::Connection_Side side, + // NOLINTNEXTLINE(*-missing-std-forward) CompletionToken&& completion_token = default_completion_token{}) { return boost::asio::async_initiate( [this](auto&& completion_handler, TLS::Connection_Side connection_side) { @@ -416,7 +434,7 @@ boost::system::error_code ec; setup_native_handle(connection_side, ec); - detail::AsyncHandshakeOperation op{ + const detail::AsyncHandshakeOperation op{ std::forward(completion_handler), *this, ec}; }, completion_token, @@ -428,10 +446,9 @@ * @throws Not_Implemented todo */ template - auto async_handshake(Connection_Side side, - const ConstBufferSequence& buffers, - BufferedHandshakeHandler&& handler) { - BOTAN_UNUSED(side, buffers, handler); + auto async_handshake([[maybe_unused]] Connection_Side side, + [[maybe_unused]] const ConstBufferSequence& buffers, + [[maybe_unused]] BufferedHandshakeHandler&& handler /* NOLINT(*missing-std-forward) */) { throw Not_Implemented("buffered async handshake is not implemented"); } @@ -447,7 +464,7 @@ * * Note that this can be used in reaction of a received shutdown alert from the peer. * - * @param ec Set to indicate what error occured, if any. + * @param ec Set to indicate what error occurred, if any. */ void shutdown(boost::system::error_code& ec) { try_with_error_code([&] { native_handle()->close(); }, ec); @@ -463,7 +480,7 @@ * * Note that this can be used in reaction of a received shutdown alert from the peer. * - * @throws boost::system::system_error if error occured + * @throws boost::system::system_error if error occurred */ void shutdown() { boost::system::error_code ec; @@ -481,7 +498,7 @@ */ template struct Wrapper { - void operator()(boost::system::error_code ec, std::size_t) { handler(ec); } + void operator()(boost::system::error_code ec, std::size_t /*unused*/) { handler(ec); } using executor_type = boost::asio::associated_executor_t; @@ -493,8 +510,8 @@ allocator_type get_allocator() const noexcept { return boost::asio::get_associated_allocator(handler); } - Handler handler; - Executor io_executor; + Handler handler; // NOLINT(*-non-private-member-variable*) + Executor io_executor; // NOLINT(*-non-private-member-variable*) }; public: @@ -509,6 +526,7 @@ * The completion signature of the handler must be: void(boost::system::error_code). */ template + // NOLINTNEXTLINE(*-missing-std-forward) auto async_shutdown(CompletionToken&& completion_token = default_completion_token{}) { return boost::asio::async_initiate( [this](auto&& completion_handler) { @@ -519,7 +537,7 @@ using write_handler_t = Wrapper; - TLS::detail::AsyncWriteOperation op{ + const TLS::detail::AsyncWriteOperation op{ write_handler_t{std::forward(completion_handler), get_executor()}, *this, boost::asio::buffer_size(send_buffer()), @@ -545,7 +563,7 @@ */ template std::size_t read_some(const MutableBufferSequence& buffers, boost::system::error_code& ec) { - // We read from the socket until either some error occured or we have + // We read from the socket until either some error occurred or we have // decrypted at least one byte of application data. while(!ec) { // Some previous invocation of process_encrypted_data() generated @@ -570,7 +588,6 @@ read_and_process_encrypted_data_from_peer(ec); } - BOTAN_ASSERT_NOMSG(ec.failed()); return 0; } @@ -582,7 +599,7 @@ * * @param buffers The buffers into which the data will be read. * @return The number of bytes read. Returns 0 if an error occurred. - * @throws boost::system::system_error if error occured + * @throws boost::system::system_error if error occurred */ template std::size_t read_some(const MutableBufferSequence& buffers) { @@ -617,7 +634,7 @@ * * @param buffers The data to be written. * @return The number of bytes written. - * @throws boost::system::system_error if error occured + * @throws boost::system::system_error if error occurred */ template std::size_t write_some(const ConstBufferSequence& buffers) { @@ -638,6 +655,7 @@ template auto async_write_some(const ConstBufferSequence& buffers, + // NOLINTNEXTLINE(*-missing-std-forward) CompletionToken&& completion_token = default_completion_token{}) { return boost::asio::async_initiate( [this](auto&& completion_handler, const auto& bufs) { @@ -652,7 +670,7 @@ m_core->send_buffer().consume(m_core->send_buffer().size()); } - detail::AsyncWriteOperation op{ + const detail::AsyncWriteOperation op{ std::forward(completion_handler), *this, ec ? 0 : boost::asio::buffer_size(bufs), @@ -674,12 +692,13 @@ template auto async_read_some(const MutableBufferSequence& buffers, + // NOLINTNEXTLINE(*-missing-std-forward) CompletionToken&& completion_token = default_completion_token{}) { return boost::asio::async_initiate( [this](auto&& completion_handler, const auto& bufs) { using completion_handler_t = std::decay_t; - detail::AsyncReadOperation op{ + const detail::AsyncReadOperation op{ std::forward(completion_handler), *this, bufs}; }, completion_token, @@ -739,8 +758,10 @@ void setup_native_handle(Connection_Side side, boost::system::error_code& ec) { // Do not attempt to instantiate the native_handle when a custom (mocked) channel type template parameter has // been specified. This allows mocking the native_handle in test code. - if constexpr(std::is_same::value) { - BOTAN_STATE_CHECK(m_native_handle == nullptr); + if constexpr(std::is_same_v) { + if(m_native_handle != nullptr) { + throw Botan::Invalid_State("ASIO native handle unexpectedly set"); + } try_with_error_code( [&] { @@ -752,7 +773,8 @@ m_context->m_policy, m_context->m_rng, m_context->m_server_info, - m_context->m_policy->latest_supported_version(false /* no DTLS */))); + m_context->m_policy->latest_supported_version(false /* no DTLS */), + m_context->m_app_protocols)); } else { m_native_handle = std::unique_ptr(new Server(m_core, m_context->m_session_manager, @@ -829,14 +851,19 @@ // If we have received application data in a previous invocation, this // data needs to be passed to the application first. Otherwise, it // might get overwritten. - BOTAN_ASSERT(!has_received_data(), "receive buffer is empty"); - BOTAN_ASSERT(!error_from_us() && !alert_from_peer(), "TLS session is healthy"); + if(has_received_data()) { + throw Botan::Invalid_State("ASIO receive buffer not empty"); + } + + if(error_from_us() || alert_from_peer()) { + throw Botan::Invalid_State("ASIO TLS session no longer healthy"); + } // If there's no existing error condition, read and process data from // the peer and report any sort of network error. TLS related errors do // not immediately cause an abort, they are checked in the invocation // via `error_from_us()`. - boost::asio::const_buffer read_buffer{input_buffer().data(), m_nextLayer.read_some(input_buffer(), ec)}; + const boost::asio::const_buffer read_buffer{input_buffer().data(), m_nextLayer.read_some(input_buffer(), ec)}; if(!ec) { process_encrypted_data(read_buffer); } else if(ec == boost::asio::error::eof) { @@ -913,8 +940,9 @@ * @param read_buffer Input buffer containing the encrypted data. */ void process_encrypted_data(const boost::asio::const_buffer& read_buffer) { - BOTAN_ASSERT(!alert_from_peer() && !error_from_us(), - "no one sent an alert before (no data allowed after that)"); + if(alert_from_peer() || error_from_us()) { + throw Botan::Invalid_State("ASIO TLS session no longer healthy"); + } // If the local TLS implementation generates an alert, we are notified // with an exception that is caught in try_with_error_code(). The error @@ -958,16 +986,16 @@ boost::system::error_code error_from_us() const { return m_ec_from_last_read; } protected: - std::shared_ptr m_context; - StreamLayer m_nextLayer; + std::shared_ptr m_context; // NOLINT(*-non-private-member-variable*) + StreamLayer m_nextLayer; // NOLINT(*-non-private-member-variable*) - std::shared_ptr m_core; - std::unique_ptr m_native_handle; - boost::system::error_code m_ec_from_last_read; + std::shared_ptr m_core; // NOLINT(*-non-private-member-variable*) + std::unique_ptr m_native_handle; // NOLINT(*-non-private-member-variable*) + boost::system::error_code m_ec_from_last_read; // NOLINT(*-non-private-member-variable*) // Buffer space used to read input intended for the core - std::vector m_input_buffer_space; - const boost::asio::mutable_buffer m_input_buffer; + std::vector m_input_buffer_space; // NOLINT(*-non-private-member-variable*) + const boost::asio::mutable_buffer m_input_buffer; // NOLINT(*-non-private-member-variable*) }; // deduction guides for convenient construction from an existing diff -Nru botan3-3.7.1+dfsg/src/lib/tls/credentials_manager.cpp botan3-3.12.0+dfsg/src/lib/tls/credentials_manager.cpp --- botan3-3.7.1+dfsg/src/lib/tls/credentials_manager.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/credentials_manager.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,8 @@ #include #include +#include +#include #include namespace Botan { diff -Nru botan3-3.7.1+dfsg/src/lib/tls/credentials_manager.h botan3-3.12.0+dfsg/src/lib/tls/credentials_manager.h --- botan3-3.7.1+dfsg/src/lib/tls/credentials_manager.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/credentials_manager.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,20 +8,26 @@ #ifndef BOTAN_CREDENTIALS_MANAGER_H_ #define BOTAN_CREDENTIALS_MANAGER_H_ -#include -#include -#include -#include #include -#include #include -#include +#include +#include #include namespace Botan { +class AlgorithmIdentifier; +class Certificate_Store; +class Public_Key; +class Private_Key; +class X509_Certificate; class X509_DN; -class BigInt; + +namespace TLS { + +class ExternalPSK; + +} /** * Interface for a credentials manager. @@ -31,7 +37,7 @@ * and "tls-server". Context represents a hostname, email address, * username, or other identifier. */ -class BOTAN_PUBLIC_API(2, 0) Credentials_Manager { +class BOTAN_PUBLIC_API(2, 0) Credentials_Manager /* NOLINT(*-special-member-functions) */ { public: virtual ~Credentials_Manager() = default; @@ -236,6 +242,8 @@ * dtls_cookie_secret() respectively. New applications should implement * those methods and rely on the default implementation of psk(). * + * TODO(Botan4) remove this interface + * * @param type specifies the type of operation occurring * @param context specifies a context relative to type. * @param identity is a PSK identity previously returned by diff -Nru botan3-3.7.1+dfsg/src/lib/tls/info.txt botan3-3.12.0+dfsg/src/lib/tls/info.txt --- botan3-3.7.1+dfsg/src/lib/tls/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -27,6 +27,7 @@ tls_server.h tls_server_info.h tls_session.h +tls_session_id.h tls_session_manager.h tls_session_manager_noop.h tls_session_manager_memory.h @@ -39,10 +40,15 @@ tls_channel_impl.h tls_handshake_transitions.h +tls_messages_internal.h tls_reader.h +aead +aes +asn1 +dh ecdh ecdsa gcm @@ -50,11 +56,7 @@ rng sha2_32 sha2_64 -tls12 x509 -pcurves_secp256r1 -pcurves_secp384r1 -pcurves_secp521r1 diff -Nru botan3-3.7.1+dfsg/src/lib/tls/msg_cert_req.cpp botan3-3.12.0+dfsg/src/lib/tls/msg_cert_req.cpp --- botan3-3.7.1+dfsg/src/lib/tls/msg_cert_req.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/msg_cert_req.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,155 +0,0 @@ -/* -* Certificate Request Message -* (C) 2004-2006,2012 Jack Lloyd -* 2021 Elektrobit Automotive GmbH -* 2022 René Meusel, Hannes Rantzsch - neXenio GmbH -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include -#include -#include -#include -#include - -namespace Botan::TLS { - -Handshake_Type Certificate_Request_12::type() const { - return Handshake_Type::CertificateRequest; -} - -namespace { - -std::string cert_type_code_to_name(uint8_t code) { - switch(code) { - case 1: - return "RSA"; - case 64: - return "ECDSA"; - default: - return ""; // DH or something else - } -} - -uint8_t cert_type_name_to_code(std::string_view name) { - if(name == "RSA") { - return 1; - } - if(name == "ECDSA") { - return 64; - } - - throw Invalid_Argument(fmt("Unknown/unhandled TLS cert type {}", name)); -} - -} // namespace - -/** -* Create a new Certificate Request message -*/ -Certificate_Request_12::Certificate_Request_12(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - const std::vector& ca_certs) : - m_names(ca_certs), m_cert_key_types({"RSA", "ECDSA"}) { - m_schemes = policy.acceptable_signature_schemes(); - hash.update(io.send(*this)); -} - -/** -* Deserialize a Certificate Request message -*/ -Certificate_Request_12::Certificate_Request_12(const std::vector& buf) { - if(buf.size() < 4) { - throw Decoding_Error("Certificate_Req: Bad certificate request"); - } - - TLS_Data_Reader reader("CertificateRequest", buf); - - const auto cert_type_codes = reader.get_range_vector(1, 1, 255); - - for(const auto cert_type_code : cert_type_codes) { - const std::string cert_type_name = cert_type_code_to_name(cert_type_code); - - if(cert_type_name.empty()) { // something we don't know - continue; - } - - m_cert_key_types.emplace_back(cert_type_name); - } - - const std::vector algs = reader.get_range_vector(2, 2, 65534); - - if(algs.size() % 2 != 0) { - throw Decoding_Error("Bad length for signature IDs in certificate request"); - } - - for(size_t i = 0; i != algs.size(); i += 2) { - m_schemes.emplace_back(make_uint16(algs[i], algs[i + 1])); - } - - const uint16_t purported_size = reader.get_uint16_t(); - - if(reader.remaining_bytes() != purported_size) { - throw Decoding_Error("Inconsistent length in certificate request"); - } - - while(reader.has_remaining()) { - std::vector name_bits = reader.get_range_vector(2, 0, 65535); - - BER_Decoder decoder(name_bits.data(), name_bits.size()); - X509_DN name; - decoder.decode(name); - m_names.emplace_back(name); - } -} - -const std::vector& Certificate_Request_12::acceptable_cert_types() const { - return m_cert_key_types; -} - -const std::vector& Certificate_Request_12::acceptable_CAs() const { - return m_names; -} - -const std::vector& Certificate_Request_12::signature_schemes() const { - return m_schemes; -} - -/** -* Serialize a Certificate Request message -*/ -std::vector Certificate_Request_12::serialize() const { - std::vector buf; - - std::vector cert_types; - - cert_types.reserve(m_cert_key_types.size()); - for(const auto& cert_key_type : m_cert_key_types) { - cert_types.push_back(cert_type_name_to_code(cert_key_type)); - } - - append_tls_length_value(buf, cert_types, 1); - - if(!m_schemes.empty()) { - buf += Signature_Algorithms(m_schemes).serialize(Connection_Side::Server); - } - - std::vector encoded_names; - - for(const auto& name : m_names) { - DER_Encoder encoder; - encoder.encode(name); - - append_tls_length_value(encoded_names, encoder.get_contents(), 2); - } - - append_tls_length_value(buf, encoded_names, 2); - - return buf; -} -} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/msg_cert_status.cpp botan3-3.12.0+dfsg/src/lib/tls/msg_cert_status.cpp --- botan3-3.7.1+dfsg/src/lib/tls/msg_cert_status.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/msg_cert_status.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,54 @@ +/* +* Certificate Status +* (C) 2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include + +namespace Botan::TLS { + +Certificate_Status::Certificate_Status(const std::vector& buf, const Connection_Side /*side*/) { + if(buf.size() < 5) { + throw Decoding_Error("Invalid Certificate_Status message: too small"); + } + + if(buf[0] != 1) { // not OCSP + throw Decoding_Error("Unexpected Certificate_Status message: unexpected response type"); + } + + const size_t len = make_uint32(0, buf[1], buf[2], buf[3]); + + // Verify the redundant length field... + if(buf.size() != len + 4) { + throw Decoding_Error("Invalid Certificate_Status: invalid length field"); + } + + m_response.assign(buf.begin() + 4, buf.end()); +} + +Certificate_Status::Certificate_Status(std::vector raw_response_bytes) : + m_response(std::move(raw_response_bytes)) {} + +std::vector Certificate_Status::serialize() const { + if(m_response.size() > 0xFFFFFF) { // unlikely + throw Encoding_Error("OCSP response too long to encode in TLS"); + } + + const uint32_t response_len = static_cast(m_response.size()); + + std::vector buf; + buf.reserve(1 + 3 + m_response.size()); + buf.push_back(1); // type OCSP + for(size_t i = 1; i < 4; ++i) { + buf.push_back(get_byte_var(i, response_len)); + } + + buf.insert(buf.end(), m_response.begin(), m_response.end()); + return buf; +} + +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/msg_cert_verify.cpp botan3-3.12.0+dfsg/src/lib/tls/msg_cert_verify.cpp --- botan3-3.7.1+dfsg/src/lib/tls/msg_cert_verify.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/msg_cert_verify.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,42 +10,18 @@ #include -#include -#include -#include -#include -#include -#include -#include #include namespace Botan::TLS { /* -* Create a new Certificate Verify message for TLS 1.2 -*/ -Certificate_Verify_12::Certificate_Verify_12(Handshake_IO& io, - Handshake_State& state, - const Policy& policy, - RandomNumberGenerator& rng, - const Private_Key* priv_key) { - BOTAN_ASSERT_NONNULL(priv_key); - - std::pair format = state.choose_sig_format(*priv_key, m_scheme, true, policy); - - m_signature = - state.callbacks().tls_sign_message(*priv_key, rng, format.first, format.second, state.hash().get_contents()); - - state.hash().update(io.send(*this)); -} - -/* * Deserialize a Certificate Verify message */ Certificate_Verify::Certificate_Verify(const std::vector& buf) { TLS_Data_Reader reader("CertificateVerify", buf); m_scheme = Signature_Scheme(reader.get_uint16_t()); + // Somewhat oddly, the signature really is allowed to be empty in a CertificateVerify m_signature = reader.get_range(2, 0, 65535); reader.assert_done(); @@ -78,131 +54,4 @@ return buf; } -bool Certificate_Verify_12::verify(const X509_Certificate& cert, - const Handshake_State& state, - const Policy& policy) const { - auto key = cert.subject_public_key(); - - policy.check_peer_key_acceptable(*key); - - std::pair format = - state.parse_sig_format(*key, m_scheme, state.client_hello()->signature_schemes(), true, policy); - - const bool signature_valid = - state.callbacks().tls_verify_message(*key, format.first, format.second, state.hash().get_contents(), m_signature); - -#if defined(BOTAN_UNSAFE_FUZZER_MODE) - BOTAN_UNUSED(signature_valid); - return true; - -#else - return signature_valid; - -#endif -} - -#if defined(BOTAN_HAS_TLS_13) - -namespace { - -std::vector message(Connection_Side side, const Transcript_Hash& hash) { - std::vector msg(64, 0x20); - msg.reserve(64 + 33 + 1 + hash.size()); - - const std::string context_string = (side == TLS::Connection_Side::Server) ? "TLS 1.3, server CertificateVerify" - : "TLS 1.3, client CertificateVerify"; - - msg.insert(msg.end(), context_string.cbegin(), context_string.cend()); - msg.push_back(0x00); - - msg.insert(msg.end(), hash.cbegin(), hash.cend()); - return msg; -} - -Signature_Scheme choose_signature_scheme(const Private_Key& key, - const std::vector& allowed_schemes, - const std::vector& peer_allowed_schemes) { - for(Signature_Scheme scheme : allowed_schemes) { - if(scheme.is_available() && scheme.is_suitable_for(key) && value_exists(peer_allowed_schemes, scheme)) { - return scheme; - } - } - - throw TLS_Exception(Alert::HandshakeFailure, "Failed to agree on a signature algorithm"); -} - -} // namespace - -/* -* Create a new Certificate Verify message for TLS 1.3 -*/ -Certificate_Verify_13::Certificate_Verify_13(const Certificate_13& certificate_msg, - const std::vector& peer_allowed_schemes, - std::string_view hostname, - const Transcript_Hash& hash, - Connection_Side whoami, - Credentials_Manager& creds_mgr, - const Policy& policy, - Callbacks& callbacks, - RandomNumberGenerator& rng) : - m_side(whoami) { - BOTAN_ASSERT_NOMSG(!certificate_msg.empty()); - - const auto op_type = (m_side == Connection_Side::Client) ? "tls-client" : "tls-server"; - const auto context = std::string(hostname); - - const auto private_key = (certificate_msg.has_certificate_chain()) - ? creds_mgr.private_key_for(certificate_msg.leaf(), op_type, context) - : creds_mgr.private_key_for(*certificate_msg.public_key(), op_type, context); - if(!private_key) { - throw TLS_Exception(Alert::InternalError, "Application did not provide a private key for its credential"); - } - - m_scheme = choose_signature_scheme(*private_key, policy.allowed_signature_schemes(), peer_allowed_schemes); - BOTAN_ASSERT_NOMSG(m_scheme.is_available()); - BOTAN_ASSERT_NOMSG(m_scheme.is_compatible_with(Protocol_Version::TLS_V13)); - - m_signature = callbacks.tls_sign_message( - *private_key, rng, m_scheme.padding_string(), m_scheme.format().value(), message(m_side, hash)); -} - -Certificate_Verify_13::Certificate_Verify_13(const std::vector& buf, const Connection_Side side) : - Certificate_Verify(buf), m_side(side) { - if(!m_scheme.is_available()) { - throw TLS_Exception(Alert::IllegalParameter, "Peer sent unknown signature scheme"); - } - - if(!m_scheme.is_compatible_with(Protocol_Version::TLS_V13)) { - throw TLS_Exception(Alert::IllegalParameter, "Peer sent signature algorithm that is not suitable for TLS 1.3"); - } -} - -/* -* Verify a Certificate Verify message -*/ -bool Certificate_Verify_13::verify(const Public_Key& public_key, - Callbacks& callbacks, - const Transcript_Hash& transcript_hash) const { - BOTAN_ASSERT_NOMSG(m_scheme.is_available()); - - // RFC 8446 4.2.3 - // The keys found in certificates MUST [...] be of appropriate type for - // the signature algorithms they are used with. - if(m_scheme.key_algorithm_identifier() != public_key.algorithm_identifier()) { - throw TLS_Exception(Alert::IllegalParameter, "Signature algorithm does not match certificate's public key"); - } - - const bool signature_valid = callbacks.tls_verify_message( - public_key, m_scheme.padding_string(), m_scheme.format().value(), message(m_side, transcript_hash), m_signature); - - #if defined(BOTAN_UNSAFE_FUZZER_MODE) - BOTAN_UNUSED(signature_valid); - return true; - #else - return signature_valid; - #endif -} - -#endif // BOTAN_HAS_TLS_13 - } // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/msg_client_hello.cpp botan3-3.12.0+dfsg/src/lib/tls/msg_client_hello.cpp --- botan3-3.7.1+dfsg/src/lib/tls/msg_client_hello.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/msg_client_hello.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,37 +1,24 @@ /* -* TLS Hello Request and Client Hello Messages +* TLS Client Hello Messages * (C) 2004-2011,2015,2016 Jack Lloyd * 2016 Matthias Gierlings * 2017 Harry Reimann, Rohde & Schwarz Cybersecurity * 2021 Elektrobit Automotive GmbH * 2022 René Meusel, Hannes Rantzsch - neXenio GmbH +* 2026 René Meusel - Rohde & Schwarz Cybersecurity GmbH * * Botan is released under the Simplified BSD License (see license.txt) */ #include +#include -#include #include #include #include -#include -#include - -#include -#include -#include -#include +#include #include -#include - -#ifdef BOTAN_HAS_TLS_13 - #include - #include -#endif - -#include -#include +#include namespace Botan::TLS { @@ -56,112 +43,82 @@ return buf; } -/** - * Version-agnostic internal client hello data container that allows - * parsing Client_Hello messages without prior knowledge of the contained - * protocol version. - */ -class Client_Hello_Internal { - public: - Client_Hello_Internal() : m_comp_methods({0}) {} - - Client_Hello_Internal(const std::vector& buf) { - if(buf.size() < 41) { - throw Decoding_Error("Client_Hello: Packet corrupted"); - } - - TLS_Data_Reader reader("ClientHello", buf); - - const uint8_t major_version = reader.get_byte(); - const uint8_t minor_version = reader.get_byte(); - - m_legacy_version = Protocol_Version(major_version, minor_version); - m_random = reader.get_fixed(32); - m_session_id = Session_ID(reader.get_range(1, 0, 32)); - - if(m_legacy_version.is_datagram_protocol()) { - auto sha256 = HashFunction::create_or_throw("SHA-256"); - sha256->update(reader.get_data_read_so_far()); - - m_hello_cookie = reader.get_range(1, 0, 255); - - sha256->update(reader.get_remaining()); - m_cookie_input_bits = sha256->final_stdvec(); - } - - m_suites = reader.get_range_vector(2, 1, 32767); - m_comp_methods = reader.get_range_vector(1, 1, 255); - - m_extensions.deserialize(reader, Connection_Side::Client, Handshake_Type::ClientHello); - } - - /** - * This distinguishes between a TLS 1.3 compliant Client Hello (containing - * the "supported_version" extension) and legacy Client Hello messages. - * - * @return TLS 1.3 if the Client Hello contains "supported_versions", or - * the content of the "legacy_version" version field if it - * indicates (D)TLS 1.2 or older, or - * (D)TLS 1.2 if the "legacy_version" was some other odd value. - */ - Protocol_Version version() const { - // RFC 8446 4.2.1 - // If [the "supported_versions"] extension is not present, servers - // which are compliant with this specification and which also support - // TLS 1.2 MUST negotiate TLS 1.2 or prior as specified in [RFC5246], - // even if ClientHello.legacy_version is 0x0304 or later. - // - // RFC 8446 4.2.1 - // Servers MUST be prepared to receive ClientHellos that include - // [the supported_versions] extension but do not include 0x0304 in - // the list of versions. - // - // RFC 8446 4.1.2 - // TLS 1.3 ClientHellos are identified as having a legacy_version of - // 0x0303 and a supported_versions extension present with 0x0304 as - // the highest version indicated therein. - if(!extensions().has() || - !extensions().get()->supports(Protocol_Version::TLS_V13)) { - // The exact legacy_version is ignored we just inspect it to - // distinguish TLS and DTLS. - return (m_legacy_version.is_datagram_protocol()) ? Protocol_Version::DTLS_V12 : Protocol_Version::TLS_V12; - } - - // Note: The Client_Hello_13 class will make sure that legacy_version - // is exactly 0x0303 (aka ossified TLS 1.2) - return Protocol_Version::TLS_V13; - } - - Protocol_Version legacy_version() const { return m_legacy_version; } - - const Session_ID& session_id() const { return m_session_id; } - - const std::vector& random() const { return m_random; } - - const std::vector& ciphersuites() const { return m_suites; } - - const std::vector& comp_methods() const { return m_comp_methods; } - - const std::vector& hello_cookie() const { return m_hello_cookie; } - - const std::vector& hello_cookie_input_bits() const { return m_cookie_input_bits; } - - const Extensions& extensions() const { return m_extensions; } - - Extensions& extensions() { return m_extensions; } - - public: - Protocol_Version m_legacy_version; // NOLINT(*-non-private-member-variables-in-classes) - Session_ID m_session_id; // NOLINT(*-non-private-member-variables-in-classes) - std::vector m_random; // NOLINT(*-non-private-member-variables-in-classes) - std::vector m_suites; // NOLINT(*-non-private-member-variables-in-classes) - std::vector m_comp_methods; // NOLINT(*-non-private-member-variables-in-classes) - Extensions m_extensions; // NOLINT(*-non-private-member-variables-in-classes) - - // These fields are only for DTLS: - std::vector m_hello_cookie; // NOLINT(*-non-private-member-variables-in-classes) - std::vector m_cookie_input_bits; // NOLINT(*-non-private-member-variables-in-classes) -}; +Client_Hello_Internal::Client_Hello_Internal(const std::vector& buf) { + /* + Minimum possible client hello + + version: 2 bytes + random: 32 bytes + session_id len: 1 byte + ciphersuite_len: 2 + ciphersuite (single): 2 + compression_len: 1 + compression (single): 1 + */ + + constexpr size_t MinimumClientHelloBytes = 2 + 32 + 1 + 2 + 2 + 1 + 1; + if(buf.size() < MinimumClientHelloBytes) { + throw Decoding_Error("Client_Hello: Packet corrupted"); + } + + TLS_Data_Reader reader("ClientHello", buf); + + const uint8_t major_version = reader.get_byte(); + const uint8_t minor_version = reader.get_byte(); + + m_legacy_version = Protocol_Version(major_version, minor_version); + + // DTLS has an additional 1 byte cookie length field + if(m_legacy_version.is_datagram_protocol() && buf.size() < MinimumClientHelloBytes + 1) { + throw Decoding_Error("Client_Hello: DTLS packet corrupted"); + } + + m_random = reader.get_fixed(32); + m_session_id = Session_ID(reader.get_range(1, 0, 32)); + + if(m_legacy_version.is_datagram_protocol()) { + auto sha256 = HashFunction::create_or_throw("SHA-256"); + sha256->update(reader.get_data_read_so_far()); + + m_hello_cookie = reader.get_range(1, 0, 255); + + sha256->update(reader.get_remaining()); + m_cookie_input_bits = sha256->final_stdvec(); + } + + m_suites = reader.get_range_vector(2, 1, 32767); + m_comp_methods = reader.get_range_vector(1, 1, 255); + + m_extensions.deserialize(reader, Connection_Side::Client, Handshake_Type::ClientHello); +} + +Protocol_Version Client_Hello_Internal::version() const { + // RFC 8446 4.2.1 + // If [the "supported_versions"] extension is not present, servers + // which are compliant with this specification and which also support + // TLS 1.2 MUST negotiate TLS 1.2 or prior as specified in [RFC5246], + // even if ClientHello.legacy_version is 0x0304 or later. + // + // RFC 8446 4.2.1 + // Servers MUST be prepared to receive ClientHellos that include + // [the supported_versions] extension but do not include 0x0304 in + // the list of versions. + // + // RFC 8446 4.1.2 + // TLS 1.3 ClientHellos are identified as having a legacy_version of + // 0x0303 and a supported_versions extension present with 0x0304 as + // the highest version indicated therein. + if(!extensions().has() || + !extensions().get()->supports(Protocol_Version::TLS_V13)) { + // The exact legacy_version is ignored we just inspect it to + // distinguish TLS and DTLS. + return (m_legacy_version.is_datagram_protocol()) ? Protocol_Version::DTLS_V12 : Protocol_Version::TLS_V12; + } + + // Note: The Client_Hello_13 class will make sure that legacy_version + // is exactly 0x0303 (aka ossified TLS 1.2) + return Protocol_Version::TLS_V13; +} Client_Hello::Client_Hello(Client_Hello&&) noexcept = default; Client_Hello& Client_Hello::operator=(Client_Hello&&) noexcept = default; @@ -209,12 +166,6 @@ return m_data->extensions(); } -void Client_Hello_12::update_hello_cookie(const Hello_Verify_Request& hello_verify) { - BOTAN_STATE_CHECK(m_data->legacy_version().is_datagram_protocol()); - - m_data->m_hello_cookie = hello_verify.cookie(); -} - /* * Serialize a Client Hello message */ @@ -261,7 +212,7 @@ } std::vector Client_Hello::signature_schemes() const { - if(Signature_Algorithms* sigs = m_data->extensions().get()) { + if(const Signature_Algorithms* sigs = m_data->extensions().get()) { return sigs->supported_schemes(); } return {}; @@ -272,7 +223,7 @@ // If no "signature_algorithms_cert" extension is present, then the // "signature_algorithms" extension also applies to signatures appearing // in certificates. - if(Signature_Algorithms_Cert* sigs = m_data->extensions().get()) { + if(const Signature_Algorithms_Cert* sigs = m_data->extensions().get()) { return sigs->supported_schemes(); } else { return signature_schemes(); @@ -280,105 +231,50 @@ } std::vector Client_Hello::supported_ecc_curves() const { - if(Supported_Groups* groups = m_data->extensions().get()) { + if(const Supported_Groups* groups = m_data->extensions().get()) { return groups->ec_groups(); } return {}; } std::vector Client_Hello::supported_dh_groups() const { - if(Supported_Groups* groups = m_data->extensions().get()) { + if(const Supported_Groups* groups = m_data->extensions().get()) { return groups->dh_groups(); } return std::vector(); } -bool Client_Hello_12::prefers_compressed_ec_points() const { - if(Supported_Point_Formats* ecc_formats = m_data->extensions().get()) { - return ecc_formats->prefers_compressed(); - } - return false; -} - std::string Client_Hello::sni_hostname() const { - if(Server_Name_Indicator* sni = m_data->extensions().get()) { + if(const Server_Name_Indicator* sni = m_data->extensions().get()) { return sni->host_name(); } return ""; } -bool Client_Hello_12::secure_renegotiation() const { - return m_data->extensions().has(); -} - -std::vector Client_Hello_12::renegotiation_info() const { - if(Renegotiation_Extension* reneg = m_data->extensions().get()) { - return reneg->renegotiation_info(); - } - return {}; -} - std::vector Client_Hello::supported_versions() const { - if(Supported_Versions* versions = m_data->extensions().get()) { + if(const Supported_Versions* versions = m_data->extensions().get()) { return versions->versions(); } return {}; } -bool Client_Hello_12::supports_session_ticket() const { - return m_data->extensions().has(); -} - -Session_Ticket Client_Hello_12::session_ticket() const { - if(auto* ticket = m_data->extensions().get()) { - return ticket->contents(); - } - return {}; -} - -std::optional Client_Hello_12::session_handle() const { - // RFC 5077 3.4 - // If a ticket is presented by the client, the server MUST NOT attempt - // to use the Session ID in the ClientHello for stateful session - // resumption. - if(auto ticket = session_ticket(); !ticket.empty()) { - return ticket; - } else if(const auto& id = session_id(); !id.empty()) { - return id; - } else { - return std::nullopt; - } -} - bool Client_Hello::supports_alpn() const { return m_data->extensions().has(); } -bool Client_Hello_12::supports_extended_master_secret() const { - return m_data->extensions().has(); -} - -bool Client_Hello_12::supports_cert_status_message() const { - return m_data->extensions().has(); -} - -bool Client_Hello_12::supports_encrypt_then_mac() const { - return m_data->extensions().has(); -} - bool Client_Hello::sent_signature_algorithms() const { return m_data->extensions().has(); } std::vector Client_Hello::next_protocols() const { - if(auto alpn = m_data->extensions().get()) { + if(auto* alpn = m_data->extensions().get()) { return alpn->protocols(); } return {}; } std::vector Client_Hello::srtp_profiles() const { - if(SRTP_Protection_Profiles* srtp = m_data->extensions().get()) { + if(const SRTP_Protection_Profiles* srtp = m_data->extensions().get()) { return srtp->profiles(); } return {}; @@ -388,685 +284,10 @@ return m_data->hello_cookie(); } -/* -* Create a new Hello Request message -*/ -Hello_Request::Hello_Request(Handshake_IO& io) { - io.send(*this); -} - -/* -* Deserialize a Hello Request message -*/ -Hello_Request::Hello_Request(const std::vector& buf) { - if(!buf.empty()) { - throw Decoding_Error("Bad Hello_Request, has non-zero size"); - } -} - -/* -* Serialize a Hello Request message -*/ -std::vector Hello_Request::serialize() const { - return std::vector(); -} - -void Client_Hello_12::add_tls12_supported_groups_extensions(const Policy& policy) { - // RFC 7919 3. - // A client that offers a group MUST be able and willing to perform a DH - // key exchange using that group. - // - // We don't support hybrid key exchange in TLS 1.2 - const std::vector kex_groups = policy.key_exchange_groups(); - std::vector compatible_kex_groups; - std::copy_if(kex_groups.begin(), kex_groups.end(), std::back_inserter(compatible_kex_groups), [](const auto group) { - return !group.is_post_quantum(); - }); - - auto supported_groups = std::make_unique(std::move(compatible_kex_groups)); - - if(!supported_groups->ec_groups().empty()) { - m_data->extensions().add(new Supported_Point_Formats(policy.use_ecc_point_compression())); - } - - m_data->extensions().add(std::move(supported_groups)); -} - -Client_Hello_12::Client_Hello_12(std::unique_ptr data) : Client_Hello(std::move(data)) { - const uint16_t TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF; - - if(offered_suite(static_cast(TLS_EMPTY_RENEGOTIATION_INFO_SCSV))) { - if(Renegotiation_Extension* reneg = m_data->extensions().get()) { - if(!reneg->renegotiation_info().empty()) { - throw TLS_Exception(Alert::HandshakeFailure, "Client sent renegotiation SCSV and non-empty extension"); - } - } else { - // add fake extension - m_data->extensions().add(new Renegotiation_Extension()); - } - } -} - -namespace { - -// Avoid sending an IPv4/IPv6 address in SNI as this is prohibitied -bool hostname_acceptable_for_sni(std::string_view hostname) { - if(hostname.empty()) { - return false; - } - - if(string_to_ipv4(hostname).has_value()) { - return false; - } - - // IPv6? Anyway ':' is not valid in DNS - if(hostname.find(':') != std::string_view::npos) { - return false; - } - - return true; -} - -} // namespace - -// Note: This delegates to the Client_Hello_12 constructor to take advantage -// of the sanity checks there. -Client_Hello_12::Client_Hello_12(const std::vector& buf) : - Client_Hello_12(std::make_unique(buf)) {} - -/* -* Create a new Client Hello message -*/ -Client_Hello_12::Client_Hello_12(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& reneg_info, - const Client_Hello_12::Settings& client_settings, - const std::vector& next_protocols) { - m_data->m_legacy_version = client_settings.protocol_version(); - m_data->m_random = make_hello_random(rng, cb, policy); - m_data->m_suites = policy.ciphersuite_list(client_settings.protocol_version()); - - if(!policy.acceptable_protocol_version(m_data->legacy_version())) { - throw Internal_Error("Offering " + m_data->legacy_version().to_string() + - " but our own policy does not accept it"); - } - - /* - * Place all empty extensions in front to avoid a bug in some systems - * which reject hellos when the last extension in the list is empty. - */ - - // EMS must always be used with TLS 1.2, regardless of the policy used. - m_data->extensions().add(new Extended_Master_Secret); - - if(policy.negotiate_encrypt_then_mac()) { - m_data->extensions().add(new Encrypt_then_MAC); - } - - m_data->extensions().add(new Session_Ticket_Extension()); - - m_data->extensions().add(new Renegotiation_Extension(reneg_info)); - - m_data->extensions().add(new Supported_Versions(m_data->legacy_version(), policy)); - - if(hostname_acceptable_for_sni(client_settings.hostname())) { - m_data->extensions().add(new Server_Name_Indicator(client_settings.hostname())); - } - - if(policy.support_cert_status_message()) { - m_data->extensions().add(new Certificate_Status_Request({}, {})); - } - - add_tls12_supported_groups_extensions(policy); - - m_data->extensions().add(new Signature_Algorithms(policy.acceptable_signature_schemes())); - if(auto cert_signing_prefs = policy.acceptable_certificate_signature_schemes()) { - // RFC 8446 4.2.3 - // TLS 1.2 implementations SHOULD also process this extension. - // Implementations which have the same policy in both cases MAY omit - // the "signature_algorithms_cert" extension. - m_data->extensions().add(new Signature_Algorithms_Cert(std::move(cert_signing_prefs.value()))); - } - - if(reneg_info.empty() && !next_protocols.empty()) { - m_data->extensions().add(new Application_Layer_Protocol_Notification(next_protocols)); - } - - if(m_data->legacy_version().is_datagram_protocol()) { - m_data->extensions().add(new SRTP_Protection_Profiles(policy.srtp_profiles())); - } - - cb.tls_modify_extensions(m_data->extensions(), Connection_Side::Client, type()); - - hash.update(io.send(*this)); -} - -/* -* Create a new Client Hello message (session resumption case) -*/ -Client_Hello_12::Client_Hello_12(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& reneg_info, - const Session_with_Handle& session, - const std::vector& next_protocols) { - m_data->m_legacy_version = session.session.version(); - m_data->m_random = make_hello_random(rng, cb, policy); - - // RFC 5077 3.4 - // When presenting a ticket, the client MAY generate and include a - // Session ID in the TLS ClientHello. [...] If a ticket is presented by - // the client, the server MUST NOT attempt to use the Session ID in the - // ClientHello for stateful session resumption. - m_data->m_session_id = session.handle.id().value_or(Session_ID(make_hello_random(rng, cb, policy))); - m_data->m_suites = policy.ciphersuite_list(m_data->legacy_version()); - - if(!policy.acceptable_protocol_version(session.session.version())) { - throw Internal_Error("Offering " + m_data->legacy_version().to_string() + - " but our own policy does not accept it"); - } - - if(!value_exists(m_data->ciphersuites(), session.session.ciphersuite_code())) { - m_data->m_suites.push_back(session.session.ciphersuite_code()); - } - - /* - * As EMS must always be used with TLS 1.2, add it even if it wasn't used - * in the original session. If the server understands it and follows the - * RFC it should reject our resume attempt and upgrade us to a new session - * with the EMS protection. - */ - m_data->extensions().add(new Extended_Master_Secret); - - if(session.session.supports_encrypt_then_mac()) { - m_data->extensions().add(new Encrypt_then_MAC); - } - - if(session.handle.is_ticket()) { - m_data->extensions().add(new Session_Ticket_Extension(session.handle.ticket().value())); - } - - m_data->extensions().add(new Renegotiation_Extension(reneg_info)); - - const std::string hostname = session.session.server_info().hostname(); - - if(hostname_acceptable_for_sni(hostname)) { - m_data->extensions().add(new Server_Name_Indicator(hostname)); - } - - if(policy.support_cert_status_message()) { - m_data->extensions().add(new Certificate_Status_Request({}, {})); - } - - add_tls12_supported_groups_extensions(policy); - - m_data->extensions().add(new Signature_Algorithms(policy.acceptable_signature_schemes())); - if(auto cert_signing_prefs = policy.acceptable_certificate_signature_schemes()) { - // RFC 8446 4.2.3 - // TLS 1.2 implementations SHOULD also process this extension. - // Implementations which have the same policy in both cases MAY omit - // the "signature_algorithms_cert" extension. - m_data->extensions().add(new Signature_Algorithms_Cert(std::move(cert_signing_prefs.value()))); - } - - if(reneg_info.empty() && !next_protocols.empty()) { - m_data->extensions().add(new Application_Layer_Protocol_Notification(next_protocols)); - } - - cb.tls_modify_extensions(m_data->extensions(), Connection_Side::Client, type()); - - hash.update(io.send(*this)); -} - -#if defined(BOTAN_HAS_TLS_13) - -Client_Hello_13::Client_Hello_13(std::unique_ptr data) : Client_Hello(std::move(data)) { - const auto& exts = m_data->extensions(); - - // RFC 8446 4.1.2 - // TLS 1.3 ClientHellos are identified as having a legacy_version of - // 0x0303 and a "supported_versions" extension present with 0x0304 as the - // highest version indicated therein. - // - // Note that we already checked for "supported_versions" before entering this - // c'tor in `Client_Hello_13::parse()`. This is just to be doubly sure. - BOTAN_ASSERT_NOMSG(exts.has()); - - // RFC 8446 4.2.1 - // Servers MAY abort the handshake upon receiving a ClientHello with - // legacy_version 0x0304 or later. - if(m_data->legacy_version().is_tls_13_or_later()) { - throw TLS_Exception(Alert::DecodeError, "TLS 1.3 Client Hello has invalid legacy_version"); - } - - // RFC 8446 4.1.2 - // For every TLS 1.3 ClientHello, [the compression method] MUST contain - // exactly one byte, set to zero, [...]. If a TLS 1.3 ClientHello is - // received with any other value in this field, the server MUST abort the - // handshake with an "illegal_parameter" alert. - if(m_data->comp_methods().size() != 1 || m_data->comp_methods().front() != 0) { - throw TLS_Exception(Alert::IllegalParameter, "Client did not offer NULL compression"); - } - - // RFC 8446 4.2.9 - // A client MUST provide a "psk_key_exchange_modes" extension if it - // offers a "pre_shared_key" extension. If clients offer "pre_shared_key" - // without a "psk_key_exchange_modes" extension, servers MUST abort - // the handshake. - if(exts.has()) { - if(!exts.has()) { - throw TLS_Exception(Alert::MissingExtension, - "Client Hello offered a PSK without a psk_key_exchange_modes extension"); - } - - // RFC 8446 4.2.11 - // The "pre_shared_key" extension MUST be the last extension in the - // ClientHello [...]. Servers MUST check that it is the last extension - // and otherwise fail the handshake with an "illegal_parameter" alert. - if(exts.all().back()->type() != Extension_Code::PresharedKey) { - throw TLS_Exception(Alert::IllegalParameter, "PSK extension was not at the very end of the Client Hello"); - } - } - - // RFC 8446 9.2 - // [A TLS 1.3 ClientHello] message MUST meet the following requirements: - // - // - If not containing a "pre_shared_key" extension, it MUST contain - // both a "signature_algorithms" extension and a "supported_groups" - // extension. - // - // - If containing a "supported_groups" extension, it MUST also contain - // a "key_share" extension, and vice versa. An empty - // KeyShare.client_shares vector is permitted. - // - // Servers receiving a ClientHello which does not conform to these - // requirements MUST abort the handshake with a "missing_extension" - // alert. - if(!exts.has()) { - if(!exts.has() || !exts.has()) { - throw TLS_Exception( - Alert::MissingExtension, - "Non-PSK Client Hello did not contain supported_groups and signature_algorithms extensions"); - } - } - if(exts.has() != exts.has()) { - throw TLS_Exception(Alert::MissingExtension, - "Client Hello must either contain both key_share and supported_groups extensions or neither"); - } - - if(exts.has()) { - const auto supported_ext = exts.get(); - BOTAN_ASSERT_NONNULL(supported_ext); - const auto supports = supported_ext->groups(); - const auto offers = exts.get()->offered_groups(); - - // RFC 8446 4.2.8 - // Each KeyShareEntry value MUST correspond to a group offered in the - // "supported_groups" extension and MUST appear in the same order. - // [...] - // Clients MUST NOT offer any KeyShareEntry values for groups not - // listed in the client's "supported_groups" extension. - // - // Note: We can assume that both `offers` and `supports` are unique lists - // as this is ensured in the parsing code of the extensions. - auto found_in_supported_groups = [&supports, support_offset = -1](auto group) mutable { - const auto i = std::find(supports.begin(), supports.end(), group); - if(i == supports.end()) { - return false; - } - - const auto found_at = std::distance(supports.begin(), i); - if(found_at <= support_offset) { - return false; // The order that groups appear in "key_share" and - // "supported_groups" must be the same - } - - support_offset = static_cast(found_at); - return true; - }; - - for(const auto offered : offers) { - // RFC 8446 4.2.8 - // Servers MAY check for violations of these rules and abort the - // handshake with an "illegal_parameter" alert if one is violated. - if(!found_in_supported_groups(offered)) { - throw TLS_Exception(Alert::IllegalParameter, - "Offered key exchange groups do not align with claimed supported groups"); - } - } - } - - // TODO: Reject oid_filters extension if found (which is the only known extension that - // must not occur in the TLS 1.3 client hello. - // RFC 8446 4.2.5 - // [The oid_filters extension] MUST only be sent in the CertificateRequest message. -} - -/* -* Create a new Client Hello message -*/ -Client_Hello_13::Client_Hello_13(const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - std::string_view hostname, - const std::vector& next_protocols, - std::optional& session, - std::vector psks) { - // RFC 8446 4.1.2 - // In TLS 1.3, the client indicates its version preferences in the - // "supported_versions" extension (Section 4.2.1) and the - // legacy_version field MUST be set to 0x0303, which is the version - // number for TLS 1.2. - m_data->m_legacy_version = Protocol_Version::TLS_V12; - m_data->m_random = make_hello_random(rng, cb, policy); - m_data->m_suites = policy.ciphersuite_list(Protocol_Version::TLS_V13); - - if(policy.allow_tls12()) { - // Note: DTLS 1.3 is NYI, hence dtls_12 is not checked - const auto legacy_suites = policy.ciphersuite_list(Protocol_Version::TLS_V12); - m_data->m_suites.insert(m_data->m_suites.end(), legacy_suites.cbegin(), legacy_suites.cend()); - } - - if(policy.tls_13_middlebox_compatibility_mode()) { - // RFC 8446 4.1.2 - // In compatibility mode (see Appendix D.4), this field MUST be non-empty, - // so a client not offering a pre-TLS 1.3 session MUST generate a new - // 32-byte value. - // - // Note: we won't ever offer a TLS 1.2 session. In such a case we would - // have instantiated a TLS 1.2 client in the first place. - m_data->m_session_id = Session_ID(make_hello_random(rng, cb, policy)); - } - - if(hostname_acceptable_for_sni(hostname)) { - m_data->extensions().add(new Server_Name_Indicator(hostname)); - } - - m_data->extensions().add(new Supported_Groups(policy.key_exchange_groups())); - - m_data->extensions().add(new Key_Share(policy, cb, rng)); - - m_data->extensions().add(new Supported_Versions(Protocol_Version::TLS_V13, policy)); - - m_data->extensions().add(new Signature_Algorithms(policy.acceptable_signature_schemes())); - if(auto cert_signing_prefs = policy.acceptable_certificate_signature_schemes()) { - // RFC 8446 4.2.3 - // Implementations which have the same policy in both cases MAY omit - // the "signature_algorithms_cert" extension. - m_data->extensions().add(new Signature_Algorithms_Cert(std::move(cert_signing_prefs.value()))); - } - - // TODO: Support for PSK-only mode without a key exchange. - // This should be configurable in TLS::Policy and should allow no PSK - // support at all (e.g. to disable support for session resumption). - m_data->extensions().add(new PSK_Key_Exchange_Modes({PSK_Key_Exchange_Mode::PSK_DHE_KE})); - - if(policy.support_cert_status_message()) { - m_data->extensions().add(new Certificate_Status_Request({}, {})); - } - - // We currently support "record_size_limit" for TLS 1.3 exclusively. Hence, - // when TLS 1.2 is advertised as a supported protocol, we must not offer this - // extension. - if(policy.record_size_limit().has_value() && !policy.allow_tls12()) { - m_data->extensions().add(new Record_Size_Limit(policy.record_size_limit().value())); - } - - if(!next_protocols.empty()) { - m_data->extensions().add(new Application_Layer_Protocol_Notification(next_protocols)); - } - - // RFC 7250 4.1 - // In order to indicate the support of raw public keys, clients include - // the client_certificate_type and/or the server_certificate_type - // extensions in an extended client hello message. - m_data->extensions().add(new Client_Certificate_Type(policy.accepted_client_certificate_types())); - m_data->extensions().add(new Server_Certificate_Type(policy.accepted_server_certificate_types())); - - if(policy.allow_tls12()) { - m_data->extensions().add(new Renegotiation_Extension()); - m_data->extensions().add(new Session_Ticket_Extension()); - - // EMS must always be used with TLS 1.2, regardless of the policy - m_data->extensions().add(new Extended_Master_Secret); - - if(policy.negotiate_encrypt_then_mac()) { - m_data->extensions().add(new Encrypt_then_MAC); - } - - if(m_data->extensions().has() && - !m_data->extensions().get()->ec_groups().empty()) { - m_data->extensions().add(new Supported_Point_Formats(policy.use_ecc_point_compression())); - } - } - - if(session.has_value() || !psks.empty()) { - m_data->extensions().add(new PSK(session, std::move(psks), cb)); - } - - cb.tls_modify_extensions(m_data->extensions(), Connection_Side::Client, type()); - - if(m_data->extensions().has()) { - // RFC 8446 4.2.11 - // The "pre_shared_key" extension MUST be the last extension in the - // ClientHello (this facilitates implementation [...]). - if(m_data->extensions().all().back()->type() != Extension_Code::PresharedKey) { - throw TLS_Exception(Alert::InternalError, - "Application modified extensions of Client Hello, PSK is not last anymore"); - } - calculate_psk_binders({}); - } -} - -std::variant Client_Hello_13::parse(const std::vector& buf) { - auto data = std::make_unique(buf); - const auto version = data->version(); - - if(version.is_pre_tls_13()) { - return Client_Hello_12(std::move(data)); - } else { - return Client_Hello_13(std::move(data)); - } -} - -void Client_Hello_13::retry(const Hello_Retry_Request& hrr, - const Transcript_Hash_State& transcript_hash_state, - Callbacks& cb, - RandomNumberGenerator& rng) { - BOTAN_STATE_CHECK(m_data->extensions().has()); - BOTAN_STATE_CHECK(m_data->extensions().has()); - - auto hrr_ks = hrr.extensions().get(); - const auto& supported_groups = m_data->extensions().get()->groups(); - - if(hrr.extensions().has()) { - m_data->extensions().get()->retry_offer(*hrr_ks, supported_groups, cb, rng); - } - - // RFC 8446 4.2.2 - // When sending the new ClientHello, the client MUST copy - // the contents of the extension received in the HelloRetryRequest into - // a "cookie" extension in the new ClientHello. - // - // RFC 8446 4.2.2 - // Clients MUST NOT use cookies in their initial ClientHello in subsequent - // connections. - if(hrr.extensions().has()) { - BOTAN_STATE_CHECK(!m_data->extensions().has()); - m_data->extensions().add(new Cookie(hrr.extensions().get()->get_cookie())); - } - - // Note: the consumer of the TLS implementation won't be able to distinguish - // invocations to this callback due to the first Client_Hello or the - // retried Client_Hello after receiving a Hello_Retry_Request. We assume - // that the user keeps and detects this state themselves. - cb.tls_modify_extensions(m_data->extensions(), Connection_Side::Client, type()); - - auto psk = m_data->extensions().get(); - if(psk) { - // Cipher suite should always be a known suite as this is checked upstream - const auto cipher = Ciphersuite::by_id(hrr.ciphersuite()); - BOTAN_ASSERT_NOMSG(cipher.has_value()); - - // RFC 8446 4.1.4 - // In [...] its updated ClientHello, the client SHOULD NOT offer - // any pre-shared keys associated with a hash other than that of the - // selected cipher suite. - psk->filter(cipher.value()); - - // RFC 8446 4.2.11.2 - // If the server responds with a HelloRetryRequest and the client - // then sends ClientHello2, its binder will be computed over: [...]. - calculate_psk_binders(transcript_hash_state.clone()); - } -} - -void Client_Hello_13::validate_updates(const Client_Hello_13& new_ch) { - // RFC 8446 4.1.2 - // The client will also send a ClientHello when the server has responded - // to its ClientHello with a HelloRetryRequest. In that case, the client - // MUST send the same ClientHello without modification, except as follows: - - if(m_data->session_id() != new_ch.m_data->session_id() || m_data->random() != new_ch.m_data->random() || - m_data->ciphersuites() != new_ch.m_data->ciphersuites() || - m_data->comp_methods() != new_ch.m_data->comp_methods()) { - throw TLS_Exception(Alert::IllegalParameter, "Client Hello core values changed after Hello Retry Request"); - } - - const auto oldexts = extension_types(); - const auto newexts = new_ch.extension_types(); - - // Check that extension omissions are justified - for(const auto oldext : oldexts) { - if(!newexts.contains(oldext)) { - const auto ext = extensions().get(oldext); - - // We don't make any assumptions about unimplemented extensions. - if(!ext->is_implemented()) { - continue; - } - - // RFC 8446 4.1.2 - // Removing the "early_data" extension (Section 4.2.10) if one was - // present. Early data is not permitted after a HelloRetryRequest. - if(oldext == EarlyDataIndication::static_type()) { - continue; - } - - // RFC 8446 4.1.2 - // Optionally adding, removing, or changing the length of the - // "padding" extension. - // - // TODO: implement the Padding extension - // if(oldext == Padding::static_type()) - // continue; - - throw TLS_Exception(Alert::IllegalParameter, "Extension removed in updated Client Hello"); - } - } - - // Check that extension additions are justified - for(const auto newext : newexts) { - if(!oldexts.contains(newext)) { - const auto ext = new_ch.extensions().get(newext); - - // We don't make any assumptions about unimplemented extensions. - if(!ext->is_implemented()) { - continue; - } - - // RFC 8446 4.1.2 - // Including a "cookie" extension if one was provided in the - // HelloRetryRequest. - if(newext == Cookie::static_type()) { - continue; - } - - // RFC 8446 4.1.2 - // Optionally adding, removing, or changing the length of the - // "padding" extension. - // - // TODO: implement the Padding extension - // if(newext == Padding::static_type()) - // continue; - - throw TLS_Exception(Alert::UnsupportedExtension, "Added an extension in updated Client Hello"); - } - } - - // RFC 8446 4.1.2 - // Removing the "early_data" extension (Section 4.2.10) if one was - // present. Early data is not permitted after a HelloRetryRequest. - if(new_ch.extensions().has()) { - throw TLS_Exception(Alert::IllegalParameter, "Updated Client Hello indicates early data"); - } - - // TODO: Contents of extensions are not checked for update compatibility, see: - // - // RFC 8446 4.1.2 - // If a "key_share" extension was supplied in the HelloRetryRequest, - // replacing the list of shares with a list containing a single - // KeyShareEntry from the indicated group. - // - // Updating the "pre_shared_key" extension if present by recomputing - // the "obfuscated_ticket_age" and binder values and (optionally) - // removing any PSKs which are incompatible with the server's - // indicated cipher suite. - // - // Optionally adding, removing, or changing the length of the - // "padding" extension. -} - -void Client_Hello_13::calculate_psk_binders(Transcript_Hash_State ths) { - auto psk = m_data->extensions().get(); - if(!psk || psk->empty()) { - return; - } - - // RFC 8446 4.2.11.2 - // Each entry in the binders list is computed as an HMAC over a - // transcript hash (see Section 4.4.1) containing a partial ClientHello - // [...]. - // - // Therefore we marshal the entire message prematurely to obtain the - // (truncated) transcript hash, calculate the PSK binders with it, update - // the Client Hello thus finalizing the message. Down the road, it will be - // re-marshalled with the correct binders and sent over the wire. - Handshake_Layer::prepare_message(*this, ths); - psk->calculate_binders(ths); -} - -std::optional Client_Hello_13::highest_supported_version(const Policy& policy) const { - // RFC 8446 4.2.1 - // The "supported_versions" extension is used by the client to indicate - // which versions of TLS it supports and by the server to indicate which - // version it is using. The extension contains a list of supported - // versions in preference order, with the most preferred version first. - const auto supvers = m_data->extensions().get(); - BOTAN_ASSERT_NONNULL(supvers); - - std::optional result; - - for(const auto& v : supvers->versions()) { - // RFC 8446 4.2.1 - // Servers MUST only select a version of TLS present in that extension - // and MUST ignore any unknown versions that are present in that - // extension. - if(!v.known_version() || !policy.acceptable_protocol_version(v)) { - continue; - } - - result = (result.has_value()) ? std::optional(std::max(result.value(), v)) : std::optional(v); - } - - return result; -} +Client_Hello_12_Shim::Client_Hello_12_Shim(std::unique_ptr data) : + Client_Hello(std::move(data)) {} -#endif // BOTAN_HAS_TLS_13 +Client_Hello_12_Shim::Client_Hello_12_Shim(const std::vector& buf) : + Client_Hello_12_Shim(std::make_unique(buf)) {} } // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/msg_finished.cpp botan3-3.12.0+dfsg/src/lib/tls/msg_finished.cpp --- botan3-3.7.1+dfsg/src/lib/tls/msg_finished.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/msg_finished.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,88 +0,0 @@ -/* -* Finished Message -* (C) 2004-2006,2012 Jack Lloyd -* 2021 Elektrobit Automotive GmbH -* 2022 René Meusel, Hannes Rantzsch - neXenio GmbH -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include -#include -#include - -#if defined(BOTAN_HAS_TLS_13) - #include -#endif - -namespace Botan::TLS { - -namespace { - -/* -* Compute the verify_data for TLS 1.2 -*/ -std::vector finished_compute_verify_12(const Handshake_State& state, Connection_Side side) { - const uint8_t TLS_CLIENT_LABEL[] = { - 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x20, 0x66, 0x69, 0x6E, 0x69, 0x73, 0x68, 0x65, 0x64}; - - const uint8_t TLS_SERVER_LABEL[] = { - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x66, 0x69, 0x6E, 0x69, 0x73, 0x68, 0x65, 0x64}; - - auto prf = state.protocol_specific_prf(); - - std::vector input; - std::vector label; - label += (side == Connection_Side::Client) ? std::make_pair(TLS_CLIENT_LABEL, sizeof(TLS_CLIENT_LABEL)) - : std::make_pair(TLS_SERVER_LABEL, sizeof(TLS_SERVER_LABEL)); - - input += state.hash().final(state.ciphersuite().prf_algo()); - - return unlock(prf->derive_key(12, state.session_keys().master_secret(), input, label)); -} - -} // namespace - -std::vector Finished::serialize() const { - return m_verification_data; -} - -Finished::Finished(const std::vector& buf) : m_verification_data(buf) {} - -std::vector Finished::verify_data() const { - return m_verification_data; -} - -Finished_12::Finished_12(Handshake_IO& io, Handshake_State& state, Connection_Side side) { - m_verification_data = finished_compute_verify_12(state, side); - state.hash().update(io.send(*this)); -} - -bool Finished_12::verify(const Handshake_State& state, Connection_Side side) const { - std::vector computed_verify = finished_compute_verify_12(state, side); - -#if defined(BOTAN_UNSAFE_FUZZER_MODE) - return true; -#else - // first check the size: - if(m_verification_data.size() != computed_verify.size()) { - return false; - } - - return CT::is_equal(m_verification_data.data(), computed_verify.data(), computed_verify.size()).as_bool(); -#endif -} - -#if defined(BOTAN_HAS_TLS_13) -Finished_13::Finished_13(Cipher_State* cipher_state, const Transcript_Hash& transcript_hash) { - m_verification_data = cipher_state->finished_mac(transcript_hash); -} - -bool Finished_13::verify(Cipher_State* cipher_state, const Transcript_Hash& transcript_hash) const { - return cipher_state->verify_peer_finished_mac(transcript_hash, m_verification_data); -} -#endif -} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/msg_server_hello.cpp botan3-3.12.0+dfsg/src/lib/tls/msg_server_hello.cpp --- botan3-3.7.1+dfsg/src/lib/tls/msg_server_hello.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/msg_server_hello.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -5,47 +5,26 @@ * 2017 Harry Reimann, Rohde & Schwarz Cybersecurity * 2021 Elektrobit Automotive GmbH * 2022 René Meusel, Hannes Rantzsch - neXenio GmbH +* 2026 René Meusel - Rohde & Schwarz Cybersecurity GmbH * * Botan is released under the Simplified BSD License (see license.txt) */ #include +#include -#include -#include +#include #include -#include -#include +#include #include -#include -#include -#include #include -#include - -#include namespace Botan::TLS { -namespace { - -const uint64_t DOWNGRADE_TLS11 = 0x444F574E47524400; -const uint64_t DOWNGRADE_TLS12 = 0x444F574E47524401; - -// SHA-256("HelloRetryRequest") -const std::vector HELLO_RETRY_REQUEST_MARKER = { - 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91, - 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C}; - -bool random_signals_hello_retry_request(const std::vector& random) { - return CT::is_equal(random.data(), HELLO_RETRY_REQUEST_MARKER.data(), HELLO_RETRY_REQUEST_MARKER.size()).as_bool(); -} - std::vector make_server_hello_random(RandomNumberGenerator& rng, Protocol_Version offered_version, Callbacks& cb, const Policy& policy) { - BOTAN_UNUSED(offered_version); auto random = make_hello_random(rng, cb, policy); // RFC 8446 4.1.3 @@ -59,111 +38,58 @@ if(offered_version.is_pre_tls_13() && policy.allow_tls13()) { constexpr size_t downgrade_signal_length = sizeof(DOWNGRADE_TLS12); BOTAN_ASSERT_NOMSG(random.size() >= downgrade_signal_length); - auto lastbytes = random.data() + random.size() - downgrade_signal_length; + const auto lastbytes = std::span{random}.last(downgrade_signal_length); store_be(DOWNGRADE_TLS12, lastbytes); } return random; } -} // namespace +Server_Hello_Internal::Server_Hello_Internal(const std::vector& buf) { + if(buf.size() < 38) { + throw Decoding_Error("Server_Hello: Packet corrupted"); + } -/** -* Version-agnostic internal server hello data container that allows -* parsing Server_Hello messages without prior knowledge of the contained -* protocol version. -*/ -class Server_Hello_Internal { - public: - /** - * Deserialize a Server Hello message - */ - Server_Hello_Internal(const std::vector& buf) { - if(buf.size() < 38) { - throw Decoding_Error("Server_Hello: Packet corrupted"); - } - - TLS_Data_Reader reader("ServerHello", buf); - - const uint8_t major_version = reader.get_byte(); - const uint8_t minor_version = reader.get_byte(); - - m_legacy_version = Protocol_Version(major_version, minor_version); - - // RFC 8446 4.1.3 - // Upon receiving a message with type server_hello, implementations MUST - // first examine the Random value and, if it matches this value, process - // it as described in Section 4.1.4 [Hello Retry Request]). - m_random = reader.get_fixed(32); - m_is_hello_retry_request = random_signals_hello_retry_request(m_random); - - m_session_id = Session_ID(reader.get_range(1, 0, 32)); - m_ciphersuite = reader.get_uint16_t(); - m_comp_method = reader.get_byte(); - - // Note that this code path might parse a TLS 1.2 (or older) server hello message that - // is nevertheless marked as being a 'hello retry request' (potentially maliciously). - // Extension parsing will however not be affected by the associated flag. - // Only after parsing the extensions will the upstream code be able to decide - // whether we're dealing with TLS 1.3 or older. - m_extensions.deserialize( - reader, - Connection_Side::Server, - m_is_hello_retry_request ? Handshake_Type::HelloRetryRequest : Handshake_Type::ServerHello); - } - - Server_Hello_Internal(Protocol_Version lv, - Session_ID sid, - std::vector r, - const uint16_t cs, - const uint8_t cm, - bool is_hrr = false) : - m_legacy_version(lv), - m_session_id(std::move(sid)), - m_random(std::move(r)), - m_is_hello_retry_request(is_hrr), - m_ciphersuite(cs), - m_comp_method(cm) {} - - Protocol_Version version() const { - // RFC 8446 4.2.1 - // A server which negotiates a version of TLS prior to TLS 1.3 MUST set - // ServerHello.version and MUST NOT send the "supported_versions" - // extension. A server which negotiates TLS 1.3 MUST respond by sending - // a "supported_versions" extension containing the selected version - // value (0x0304). - // - // Note: Here we just take a message parsing decision, further validation of - // the extension's contents is done later. - return (extensions().has()) ? Protocol_Version::TLS_V13 : m_legacy_version; - } - - Protocol_Version legacy_version() const { return m_legacy_version; } - - const Session_ID& session_id() const { return m_session_id; } - - const std::vector& random() const { return m_random; } - - uint16_t ciphersuite() const { return m_ciphersuite; } - - uint8_t comp_method() const { return m_comp_method; } - - bool is_hello_retry_request() const { return m_is_hello_retry_request; } - - const Extensions& extensions() const { return m_extensions; } - - Extensions& extensions() { return m_extensions; } - - private: - Protocol_Version m_legacy_version; - Session_ID m_session_id; - std::vector m_random; - bool m_is_hello_retry_request; - uint16_t m_ciphersuite; - uint8_t m_comp_method; + TLS_Data_Reader reader("ServerHello", buf); + + const uint8_t major_version = reader.get_byte(); + const uint8_t minor_version = reader.get_byte(); + + m_legacy_version = Protocol_Version(major_version, minor_version); + + // RFC 8446 4.1.3 + // Upon receiving a message with type server_hello, implementations MUST + // first examine the Random value and, if it matches this value, process + // it as described in Section 4.1.4 [Hello Retry Request]). + m_random = reader.get_fixed(32); + m_is_hello_retry_request = CT::is_equal(m_random, HELLO_RETRY_REQUEST_MARKER).as_bool(); - Extensions m_extensions; -}; + m_session_id = Session_ID(reader.get_range(1, 0, 32)); + m_ciphersuite = reader.get_uint16_t(); + m_comp_method = reader.get_byte(); + + // Note that this code path might parse a TLS 1.2 (or older) server hello message that + // is nevertheless marked as being a 'hello retry request' (potentially maliciously). + // Extension parsing will however not be affected by the associated flag. + // Only after parsing the extensions will the upstream code be able to decide + // whether we're dealing with TLS 1.3 or older. + m_extensions.deserialize(reader, + Connection_Side::Server, + m_is_hello_retry_request ? Handshake_Type::HelloRetryRequest : Handshake_Type::ServerHello); +} + +Protocol_Version Server_Hello_Internal::version() const { + // RFC 8446 4.2.1 + // A server which negotiates a version of TLS prior to TLS 1.3 MUST set + // ServerHello.version and MUST NOT send the "supported_versions" + // extension. A server which negotiates TLS 1.3 MUST respond by sending + // a "supported_versions" extension containing the selected version + // value (0x0304). + // + // Note: Here we just take a message parsing decision, further validation of + // the extension's contents is done later. + return (extensions().has()) ? Protocol_Version::TLS_V13 : m_legacy_version; +} Server_Hello::Server_Hello(std::unique_ptr data) : m_data(std::move(data)) {} @@ -227,195 +153,21 @@ return m_data->extensions(); } -// New session case -Server_Hello_12::Server_Hello_12(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& reneg_info, - const Client_Hello_12& client_hello, - const Server_Hello_12::Settings& server_settings, - std::string_view next_protocol) : - Server_Hello(std::make_unique( - server_settings.protocol_version(), - server_settings.session_id(), - make_server_hello_random(rng, server_settings.protocol_version(), cb, policy), - server_settings.ciphersuite(), - uint8_t(0))) { - if(client_hello.supports_extended_master_secret()) { - m_data->extensions().add(new Extended_Master_Secret); - } - - // Sending the extension back does not commit us to sending a stapled response - if(client_hello.supports_cert_status_message() && policy.support_cert_status_message()) { - m_data->extensions().add(new Certificate_Status_Request); - } - - if(!next_protocol.empty() && client_hello.supports_alpn()) { - m_data->extensions().add(new Application_Layer_Protocol_Notification(next_protocol)); - } - - const auto c = Ciphersuite::by_id(m_data->ciphersuite()); - - if(c && c->cbc_ciphersuite() && client_hello.supports_encrypt_then_mac() && policy.negotiate_encrypt_then_mac()) { - m_data->extensions().add(new Encrypt_then_MAC); - } - - if(c && c->ecc_ciphersuite() && client_hello.extension_types().contains(Extension_Code::EcPointFormats)) { - m_data->extensions().add(new Supported_Point_Formats(policy.use_ecc_point_compression())); - } - - if(client_hello.secure_renegotiation()) { - m_data->extensions().add(new Renegotiation_Extension(reneg_info)); - } - - if(client_hello.supports_session_ticket() && server_settings.offer_session_ticket()) { - m_data->extensions().add(new Session_Ticket_Extension()); - } - - if(m_data->legacy_version().is_datagram_protocol()) { - const std::vector server_srtp = policy.srtp_profiles(); - const std::vector client_srtp = client_hello.srtp_profiles(); - - if(!server_srtp.empty() && !client_srtp.empty()) { - uint16_t shared = 0; - // always using server preferences for now - for(auto s_srtp : server_srtp) { - for(auto c_srtp : client_srtp) { - if(shared == 0 && s_srtp == c_srtp) { - shared = s_srtp; - } - } - } - - if(shared) { - m_data->extensions().add(new SRTP_Protection_Profiles(shared)); - } - } - } - - cb.tls_modify_extensions(m_data->extensions(), Connection_Side::Server, type()); - - hash.update(io.send(*this)); -} - -// Resuming -Server_Hello_12::Server_Hello_12(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& reneg_info, - const Client_Hello_12& client_hello, - const Session& resumed_session, - bool offer_session_ticket, - std::string_view next_protocol) : - Server_Hello(std::make_unique(resumed_session.version(), - client_hello.session_id(), - make_hello_random(rng, cb, policy), - resumed_session.ciphersuite_code(), - uint8_t(0))) { - if(client_hello.supports_extended_master_secret()) { - m_data->extensions().add(new Extended_Master_Secret); - } - - if(!next_protocol.empty() && client_hello.supports_alpn()) { - m_data->extensions().add(new Application_Layer_Protocol_Notification(next_protocol)); - } - - if(client_hello.supports_encrypt_then_mac() && policy.negotiate_encrypt_then_mac()) { - Ciphersuite c = resumed_session.ciphersuite(); - if(c.cbc_ciphersuite()) { - m_data->extensions().add(new Encrypt_then_MAC); - } - } - - if(resumed_session.ciphersuite().ecc_ciphersuite() && - client_hello.extension_types().contains(Extension_Code::EcPointFormats)) { - m_data->extensions().add(new Supported_Point_Formats(policy.use_ecc_point_compression())); - } - - if(client_hello.secure_renegotiation()) { - m_data->extensions().add(new Renegotiation_Extension(reneg_info)); - } - - if(client_hello.supports_session_ticket() && offer_session_ticket) { - m_data->extensions().add(new Session_Ticket_Extension()); - } - - cb.tls_modify_extensions(m_data->extensions(), Connection_Side::Server, type()); - - hash.update(io.send(*this)); -} - -Server_Hello_12::Server_Hello_12(const std::vector& buf) : - Server_Hello_12(std::make_unique(buf)) {} +Server_Hello_12_Shim::Server_Hello_12_Shim(const std::vector& buf) : + Server_Hello_12_Shim(std::make_unique(buf)) {} -Server_Hello_12::Server_Hello_12(std::unique_ptr data) : Server_Hello(std::move(data)) { +Server_Hello_12_Shim::Server_Hello_12_Shim(std::unique_ptr data) : + Server_Hello(std::move(data)) { if(!m_data->version().is_pre_tls_13()) { throw TLS_Exception(Alert::ProtocolVersion, "Expected server hello of (D)TLS 1.2 or lower"); } } -Protocol_Version Server_Hello_12::selected_version() const { +Protocol_Version Server_Hello_12_Shim::selected_version() const { return legacy_version(); } -bool Server_Hello_12::secure_renegotiation() const { - return m_data->extensions().has(); -} - -std::vector Server_Hello_12::renegotiation_info() const { - if(Renegotiation_Extension* reneg = m_data->extensions().get()) { - return reneg->renegotiation_info(); - } - return std::vector(); -} - -bool Server_Hello_12::supports_extended_master_secret() const { - return m_data->extensions().has(); -} - -bool Server_Hello_12::supports_encrypt_then_mac() const { - return m_data->extensions().has(); -} - -bool Server_Hello_12::supports_certificate_status_message() const { - return m_data->extensions().has(); -} - -bool Server_Hello_12::supports_session_ticket() const { - return m_data->extensions().has(); -} - -uint16_t Server_Hello_12::srtp_profile() const { - if(auto srtp = m_data->extensions().get()) { - auto prof = srtp->profiles(); - if(prof.size() != 1 || prof[0] == 0) { - throw Decoding_Error("Server sent malformed DTLS-SRTP extension"); - } - return prof[0]; - } - - return 0; -} - -std::string Server_Hello_12::next_protocol() const { - if(auto alpn = m_data->extensions().get()) { - return alpn->single_protocol(); - } - return ""; -} - -bool Server_Hello_12::prefers_compressed_ec_points() const { - if(auto ecc_formats = m_data->extensions().get()) { - return ecc_formats->prefers_compressed(); - } - return false; -} - -std::optional Server_Hello_12::random_signals_downgrade() const { +std::optional Server_Hello_12_Shim::random_signals_downgrade() const { const uint64_t last8 = load_be(m_data->random().data(), 3); if(last8 == DOWNGRADE_TLS11) { return Protocol_Version::TLS_V11; @@ -427,404 +179,4 @@ return std::nullopt; } -/* -* Create a new Server Hello Done message -*/ -Server_Hello_Done::Server_Hello_Done(Handshake_IO& io, Handshake_Hash& hash) { - hash.update(io.send(*this)); -} - -/* -* Deserialize a Server Hello Done message -*/ -Server_Hello_Done::Server_Hello_Done(const std::vector& buf) { - if(!buf.empty()) { - throw Decoding_Error("Server_Hello_Done: Must be empty, and is not"); - } -} - -/* -* Serialize a Server Hello Done message -*/ -std::vector Server_Hello_Done::serialize() const { - return std::vector(); -} - -#if defined(BOTAN_HAS_TLS_13) - -const Server_Hello_13::Server_Hello_Tag Server_Hello_13::as_server_hello; -const Server_Hello_13::Hello_Retry_Request_Tag Server_Hello_13::as_hello_retry_request; -const Server_Hello_13::Hello_Retry_Request_Creation_Tag Server_Hello_13::as_new_hello_retry_request; - -std::variant Server_Hello_13::create(const Client_Hello_13& ch, - bool hello_retry_request_allowed, - Session_Manager& session_mgr, - Credentials_Manager& credentials_mgr, - RandomNumberGenerator& rng, - const Policy& policy, - Callbacks& cb) { - const auto& exts = ch.extensions(); - - // RFC 8446 4.2.9 - // [With PSK with (EC)DHE key establishment], the client and server MUST - // supply "key_share" values [...]. - // - // Note: We currently do not support PSK without (EC)DHE, hence, we can - // assume that those extensions are available. - BOTAN_ASSERT_NOMSG(exts.has() && exts.has()); - const auto& supported_by_client = exts.get()->groups(); - const auto& offered_by_client = exts.get()->offered_groups(); - const auto selected_group = policy.choose_key_exchange_group(supported_by_client, offered_by_client); - - // RFC 8446 4.1.1 - // If there is no overlap between the received "supported_groups" and the - // groups supported by the server, then the server MUST abort the - // handshake with a "handshake_failure" or an "insufficient_security" alert. - if(selected_group == Named_Group::NONE) { - throw TLS_Exception(Alert::HandshakeFailure, "Client did not offer any acceptable group"); - } - - // RFC 8446 4.2.8: - // Servers MUST NOT send a KeyShareEntry for any group not indicated in the - // client's "supported_groups" extension [...] - if(!value_exists(supported_by_client, selected_group)) { - throw TLS_Exception(Alert::InternalError, "Application selected a group that is not supported by the client"); - } - - // RFC 8446 4.1.4 - // The server will send this message in response to a ClientHello - // message if it is able to find an acceptable set of parameters but the - // ClientHello does not contain sufficient information to proceed with - // the handshake. - // - // In this case, the Client Hello did not contain a key share offer for - // the group selected by the application. - if(!value_exists(offered_by_client, selected_group)) { - // RFC 8446 4.1.4 - // If a client receives a second HelloRetryRequest in the same - // connection (i.e., where the ClientHello was itself in response to a - // HelloRetryRequest), it MUST abort the handshake with an - // "unexpected_message" alert. - BOTAN_STATE_CHECK(hello_retry_request_allowed); - return Hello_Retry_Request(ch, selected_group, policy, cb); - } else { - return Server_Hello_13(ch, selected_group, session_mgr, credentials_mgr, rng, cb, policy); - } -} - -std::variant Server_Hello_13::parse( - const std::vector& buf) { - auto data = std::make_unique(buf); - const auto version = data->version(); - - // server hello that appears to be pre-TLS 1.3, takes precedence over... - if(version.is_pre_tls_13()) { - return Server_Hello_12(std::move(data)); - } - - // ... the TLS 1.3 "special case" aka. Hello_Retry_Request - if(version == Protocol_Version::TLS_V13) { - if(data->is_hello_retry_request()) { - return Hello_Retry_Request(std::move(data)); - } - - return Server_Hello_13(std::move(data)); - } - - throw TLS_Exception(Alert::ProtocolVersion, "unexpected server hello version: " + version.to_string()); -} - -/** - * Validation that applies to both Server Hello and Hello Retry Request - */ -void Server_Hello_13::basic_validation() const { - BOTAN_ASSERT_NOMSG(m_data->version() == Protocol_Version::TLS_V13); - - // Note: checks that cannot be performed without contextual information - // are done in the specific TLS client implementation. - // Note: The Supported_Version extension makes sure internally that - // exactly one entry is provided. - - // Note: Hello Retry Request basic validation is equivalent with the - // basic validations required for Server Hello - // - // RFC 8446 4.1.4 - // Upon receipt of a HelloRetryRequest, the client MUST check the - // legacy_version, [...], and legacy_compression_method as specified in - // Section 4.1.3 and then process the extensions, starting with determining - // the version using "supported_versions". - - // RFC 8446 4.1.3 - // In TLS 1.3, [...] the legacy_version field MUST be set to 0x0303 - if(legacy_version() != Protocol_Version::TLS_V12) { - throw TLS_Exception(Alert::ProtocolVersion, - "legacy_version '" + legacy_version().to_string() + "' is not allowed"); - } - - // RFC 8446 4.1.3 - // legacy_compression_method: A single byte which MUST have the value 0. - if(compression_method() != 0x00) { - throw TLS_Exception(Alert::DecodeError, "compression is not supported in TLS 1.3"); - } - - // RFC 8446 4.1.3 - // All TLS 1.3 ServerHello messages MUST contain the "supported_versions" extension. - if(!extensions().has()) { - throw TLS_Exception(Alert::MissingExtension, "server hello did not contain 'supported version' extension"); - } - - // RFC 8446 4.2.1 - // A server which negotiates TLS 1.3 MUST respond by sending - // a "supported_versions" extension containing the selected version - // value (0x0304). - if(selected_version() != Protocol_Version::TLS_V13) { - throw TLS_Exception(Alert::IllegalParameter, "TLS 1.3 Server Hello selected a different version"); - } -} - -Server_Hello_13::Server_Hello_13(std::unique_ptr data, Server_Hello_13::Server_Hello_Tag) : - Server_Hello(std::move(data)) { - BOTAN_ASSERT_NOMSG(!m_data->is_hello_retry_request()); - basic_validation(); - - const auto& exts = extensions(); - - // RFC 8446 4.1.3 - // The ServerHello MUST only include extensions which are required to - // establish the cryptographic context and negotiate the protocol version. - // [...] - // Other extensions (see Section 4.2) are sent separately in the - // EncryptedExtensions message. - // - // Note that further validation dependent on the client hello is done in the - // TLS client implementation. - const std::set allowed = { - Extension_Code::KeyShare, - Extension_Code::SupportedVersions, - Extension_Code::PresharedKey, - }; - - // As the ServerHello shall only contain essential extensions, we don't give - // any slack for extensions not implemented by Botan here. - if(exts.contains_other_than(allowed)) { - throw TLS_Exception(Alert::UnsupportedExtension, "Server Hello contained an extension that is not allowed"); - } - - // RFC 8446 4.1.3 - // Current ServerHello messages additionally contain - // either the "pre_shared_key" extension or the "key_share" - // extension, or both [...]. - if(!exts.has() && !exts.has()) { - throw TLS_Exception(Alert::MissingExtension, "server hello must contain key exchange information"); - } -} - -Server_Hello_13::Server_Hello_13(std::unique_ptr data, - Server_Hello_13::Hello_Retry_Request_Tag) : - Server_Hello(std::move(data)) { - BOTAN_ASSERT_NOMSG(m_data->is_hello_retry_request()); - basic_validation(); - - const auto& exts = extensions(); - - // RFC 8446 4.1.4 - // The HelloRetryRequest extensions defined in this specification are: - // - supported_versions (see Section 4.2.1) - // - cookie (see Section 4.2.2) - // - key_share (see Section 4.2.8) - const std::set allowed = { - Extension_Code::Cookie, - Extension_Code::SupportedVersions, - Extension_Code::KeyShare, - }; - - // As the Hello Retry Request shall only contain essential extensions, we - // don't give any slack for extensions not implemented by Botan here. - if(exts.contains_other_than(allowed)) { - throw TLS_Exception(Alert::UnsupportedExtension, - "Hello Retry Request contained an extension that is not allowed"); - } - - // RFC 8446 4.1.4 - // Clients MUST abort the handshake with an "illegal_parameter" alert if - // the HelloRetryRequest would not result in any change in the ClientHello. - if(!exts.has() && !exts.has()) { - throw TLS_Exception(Alert::IllegalParameter, "Hello Retry Request does not request any changes to Client Hello"); - } -} - -Server_Hello_13::Server_Hello_13(std::unique_ptr data, Hello_Retry_Request_Creation_Tag) : - Server_Hello(std::move(data)) {} - -namespace { - -uint16_t choose_ciphersuite(const Client_Hello_13& ch, const Policy& policy) { - auto pref_list = ch.ciphersuites(); - // TODO: DTLS might need to make this version dynamic - auto other_list = policy.ciphersuite_list(Protocol_Version::TLS_V13); - - if(policy.server_uses_own_ciphersuite_preferences()) { - std::swap(pref_list, other_list); - } - - for(auto suite_id : pref_list) { - // TODO: take potentially available PSKs into account to select a - // compatible ciphersuite. - // - // Assuming the client sent one or more PSKs, we would first need to find - // the hash functions they are associated to. For session tickets, that - // would mean decrypting the ticket and comparing the cipher suite used in - // those tickets. For (currently not yet supported) pre-assigned PSKs, the - // hash function needs to be specified along with them. - // - // Then we could refine the ciphersuite selection using the required hash - // function for the PSK(s) we are wishing to use down the road. - // - // For now, we just negotiate the cipher suite blindly and hope for the - // best. As long as PSKs are used for session resumption only, this has a - // high chance of success. Previous handshakes with this client have very - // likely selected the same ciphersuite anyway. - // - // See also RFC 8446 4.2.11 - // When session resumption is the primary use case of PSKs, the most - // straightforward way to implement the PSK/cipher suite matching - // requirements is to negotiate the cipher suite first [...]. - if(value_exists(other_list, suite_id)) { - return suite_id; - } - } - - // RFC 8446 4.1.1 - // If the server is unable to negotiate a supported set of parameters - // [...], it MUST abort the handshake with either a "handshake_failure" - // or "insufficient_security" fatal alert [...]. - throw TLS_Exception(Alert::HandshakeFailure, "Can't agree on a ciphersuite with client"); -} -} // namespace - -Server_Hello_13::Server_Hello_13(const Client_Hello_13& ch, - std::optional key_exchange_group, - Session_Manager& session_mgr, - Credentials_Manager& credentials_mgr, - RandomNumberGenerator& rng, - Callbacks& cb, - const Policy& policy) : - Server_Hello(std::make_unique( - Protocol_Version::TLS_V12, - ch.session_id(), - make_server_hello_random(rng, Protocol_Version::TLS_V13, cb, policy), - choose_ciphersuite(ch, policy), - uint8_t(0) /* compression method */ - )) { - // RFC 8446 4.2.1 - // A server which negotiates TLS 1.3 MUST respond by sending a - // "supported_versions" extension containing the selected version - // value (0x0304). It MUST set the ServerHello.legacy_version field to - // 0x0303 (TLS 1.2). - // - // Note that the legacy version (TLS 1.2) is set in this constructor's - // initializer list, accordingly. - m_data->extensions().add(new Supported_Versions(Protocol_Version::TLS_V13)); - - if(key_exchange_group.has_value()) { - BOTAN_ASSERT_NOMSG(ch.extensions().has()); - m_data->extensions().add(Key_Share::create_as_encapsulation( - key_exchange_group.value(), *ch.extensions().get(), policy, cb, rng)); - } - - auto& ch_exts = ch.extensions(); - - if(ch_exts.has()) { - const auto cs = Ciphersuite::by_id(m_data->ciphersuite()); - BOTAN_ASSERT_NOMSG(cs); - - // RFC 8446 4.2.9 - // A client MUST provide a "psk_key_exchange_modes" extension if it - // offers a "pre_shared_key" extension. - // - // Note: Client_Hello_13 constructor already performed a graceful check. - const auto psk_modes = ch_exts.get(); - BOTAN_ASSERT_NONNULL(psk_modes); - - // TODO: also support PSK_Key_Exchange_Mode::PSK_KE - // (PSK-based handshake without an additional ephemeral key exchange) - if(value_exists(psk_modes->modes(), PSK_Key_Exchange_Mode::PSK_DHE_KE)) { - if(auto server_psk = ch_exts.get()->select_offered_psk( - ch.sni_hostname(), cs.value(), session_mgr, credentials_mgr, cb, policy)) { - // RFC 8446 4.2.11 - // In order to accept PSK key establishment, the server sends a - // "pre_shared_key" extension indicating the selected identity. - m_data->extensions().add(std::move(server_psk)); - } - } - } - - cb.tls_modify_extensions(m_data->extensions(), Connection_Side::Server, type()); -} - -std::optional Server_Hello_13::random_signals_downgrade() const { - const uint64_t last8 = load_be(m_data->random().data(), 3); - if(last8 == DOWNGRADE_TLS11) { - return Protocol_Version::TLS_V11; - } - if(last8 == DOWNGRADE_TLS12) { - return Protocol_Version::TLS_V12; - } - - return std::nullopt; -} - -Protocol_Version Server_Hello_13::selected_version() const { - const auto versions_ext = m_data->extensions().get(); - BOTAN_ASSERT_NOMSG(versions_ext); - const auto& versions = versions_ext->versions(); - BOTAN_ASSERT_NOMSG(versions.size() == 1); - return versions.front(); -} - -Hello_Retry_Request::Hello_Retry_Request(std::unique_ptr data) : - Server_Hello_13(std::move(data), Server_Hello_13::as_hello_retry_request) {} - -Hello_Retry_Request::Hello_Retry_Request(const Client_Hello_13& ch, - Named_Group selected_group, - const Policy& policy, - Callbacks& cb) : - Server_Hello_13(std::make_unique(Protocol_Version::TLS_V12 /* legacy_version */, - ch.session_id(), - HELLO_RETRY_REQUEST_MARKER, - choose_ciphersuite(ch, policy), - uint8_t(0) /* compression method */, - true /* is Hello Retry Request */ - ), - as_new_hello_retry_request) { - // RFC 8446 4.1.4 - // As with the ServerHello, a HelloRetryRequest MUST NOT contain any - // extensions that were not first offered by the client in its - // ClientHello, with the exception of optionally the "cookie" [...] - // extension. - BOTAN_STATE_CHECK(ch.extensions().has()); - BOTAN_STATE_CHECK(ch.extensions().has()); - - BOTAN_STATE_CHECK(!value_exists(ch.extensions().get()->offered_groups(), selected_group)); - - // RFC 8446 4.1.4 - // The server's extensions MUST contain "supported_versions". - // - // RFC 8446 4.2.1 - // A server which negotiates TLS 1.3 MUST respond by sending a - // "supported_versions" extension containing the selected version - // value (0x0304). It MUST set the ServerHello.legacy_version field to - // 0x0303 (TLS 1.2). - // - // Note that the legacy version (TLS 1.2) is set in this constructor's - // initializer list, accordingly. - m_data->extensions().add(new Supported_Versions(Protocol_Version::TLS_V13)); - - m_data->extensions().add(new Key_Share(selected_group)); - - cb.tls_modify_extensions(m_data->extensions(), Connection_Side::Server, type()); -} - -#endif // BOTAN_HAS_TLS_13 - } // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/msg_session_ticket.cpp botan3-3.12.0+dfsg/src/lib/tls/msg_session_ticket.cpp --- botan3-3.7.1+dfsg/src/lib/tls/msg_session_ticket.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/msg_session_ticket.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,140 +0,0 @@ -/* -* Session Tickets -* (C) 2012 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -namespace Botan::TLS { - -New_Session_Ticket_12::New_Session_Ticket_12(Handshake_IO& io, - Handshake_Hash& hash, - Session_Ticket ticket, - std::chrono::seconds lifetime) : - m_ticket_lifetime_hint(lifetime), m_ticket(std::move(ticket)) { - hash.update(io.send(*this)); -} - -New_Session_Ticket_12::New_Session_Ticket_12(Handshake_IO& io, Handshake_Hash& hash) { - hash.update(io.send(*this)); -} - -New_Session_Ticket_12::New_Session_Ticket_12(const std::vector& buf) { - if(buf.size() < 6) { - throw Decoding_Error("Session ticket message too short to be valid"); - } - - TLS_Data_Reader reader("SessionTicket", buf); - - m_ticket_lifetime_hint = std::chrono::seconds(reader.get_uint32_t()); - m_ticket = Session_Ticket(reader.get_range(2, 0, 65535)); - reader.assert_done(); -} - -namespace { - -template -void store_lifetime(std::span sink, std::chrono::seconds lifetime) { - BOTAN_ARG_CHECK(lifetime.count() >= 0 && lifetime.count() <= std::numeric_limits::max(), - "Ticket lifetime is out of range"); - store_be(static_cast(lifetime.count()), sink.data()); -} - -} // namespace - -std::vector New_Session_Ticket_12::serialize() const { - std::vector buf(4); - store_be(static_cast(m_ticket_lifetime_hint.count()), buf.data()); - append_tls_length_value(buf, m_ticket.get(), 2); - return buf; -} - -#if defined(BOTAN_HAS_TLS_13) - -New_Session_Ticket_13::New_Session_Ticket_13(Ticket_Nonce nonce, - const Session& session, - const Session_Handle& handle, - Callbacks& callbacks) : - m_ticket_lifetime_hint(session.lifetime_hint()), - m_ticket_age_add(session.session_age_add()), - m_ticket_nonce(std::move(nonce)), - m_handle(handle.opaque_handle()) { - callbacks.tls_modify_extensions(m_extensions, Connection_Side::Server, type()); -} - -New_Session_Ticket_13::New_Session_Ticket_13(const std::vector& buf, Connection_Side from) { - TLS_Data_Reader reader("New_Session_Ticket_13", buf); - - m_ticket_lifetime_hint = std::chrono::seconds(reader.get_uint32_t()); - - // RFC 8446 4.6.1 - // Servers MUST NOT use any value [of ticket_lifetime] greater than 604800 - // seconds (7 days). - if(m_ticket_lifetime_hint > std::chrono::days(7)) { - throw TLS_Exception(Alert::IllegalParameter, "Received a session ticket with lifetime longer than one week."); - } - - m_ticket_age_add = reader.get_uint32_t(); - m_ticket_nonce = Ticket_Nonce(reader.get_tls_length_value(1)); - m_handle = Opaque_Session_Handle(reader.get_tls_length_value(2)); - - m_extensions.deserialize(reader, from, type()); - - // RFC 8446 4.6.1 - // The sole extension currently defined for NewSessionTicket is - // "early_data", indicating that the ticket may be used to send 0-RTT - // data [...]. Clients MUST ignore unrecognized extensions. - if(m_extensions.contains_implemented_extensions_other_than({Extension_Code::EarlyData})) { - throw TLS_Exception(Alert::IllegalParameter, "NewSessionTicket message contained unexpected extension"); - } - - reader.assert_done(); -} - -std::optional New_Session_Ticket_13::early_data_byte_limit() const { - if(!m_extensions.has()) { - return std::nullopt; - } - - const EarlyDataIndication* ext = m_extensions.get(); - BOTAN_ASSERT_NOMSG(ext->max_early_data_size().has_value()); - return ext->max_early_data_size(); -} - -std::vector New_Session_Ticket_13::serialize() const { - std::vector result(8); - - store_lifetime(std::span(result.data(), 4), m_ticket_lifetime_hint); - store_be(m_ticket_age_add, result.data() + 4); - append_tls_length_value(result, m_ticket_nonce.get(), 1); - append_tls_length_value(result, m_handle.get(), 2); - - // TODO: re-evaluate this construction when reworking message marshalling - if(m_extensions.empty()) { - result.push_back(0x00); - result.push_back(0x00); - } else { - result += m_extensions.serialize(Connection_Side::Server); - } - - return result; -} - -#endif - -} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/sessions_sql/tls_session_manager_sql.cpp botan3-3.12.0+dfsg/src/lib/tls/sessions_sql/tls_session_manager_sql.cpp --- botan3-3.7.1+dfsg/src/lib/tls/sessions_sql/tls_session_manager_sql.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/sessions_sql/tls_session_manager_sql.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,8 +11,8 @@ #include #include #include +#include #include -#include namespace Botan::TLS { @@ -93,11 +93,11 @@ secure_vector derived_key(32 + 2); - const auto pbkdf_name = "PBKDF2(SHA-512)"; + const std::string pbkdf_name = "PBKDF2(SHA-512)"; auto pbkdf_fam = PasswordHashFamily::create_or_throw(pbkdf_name); - auto desired_runtime = std::chrono::milliseconds(100); - auto pbkdf = pbkdf_fam->tune(derived_key.size(), desired_runtime); + constexpr uint32_t desired_runtime_msec = 100; + auto pbkdf = pbkdf_fam->tune_params(derived_key.size(), desired_runtime_msec); pbkdf->derive_key( derived_key.data(), derived_key.size(), passphrase.data(), passphrase.size(), salt.data(), salt.size()); @@ -123,7 +123,7 @@ throw Internal_Error("Failed to initialize TLS session database"); } - std::pair salt = stmt->get_blob(0); + const std::pair salt = stmt->get_blob(0); const size_t iterations = stmt->get_size_t(1); const size_t check_val_db = stmt->get_size_t(2); const std::string pbkdf_name = stmt->get_str(3); @@ -188,7 +188,7 @@ stmt->bind(1, hex_encode(session_id->get())); while(stmt->step()) { - std::pair blob = stmt->get_blob(0); + const std::pair blob = stmt->get_blob(0); try { return Session::decrypt(blob.first, blob.second, m_session_key); @@ -221,13 +221,13 @@ auto handle = [&]() -> Session_Handle { auto ticket_blob = stmt->get_blob(1); if(ticket_blob.second > 0) { - return Session_Ticket(std::span(ticket_blob.first, ticket_blob.second)); + return Session_Handle(Session_Ticket(std::span(ticket_blob.first, ticket_blob.second))); } else { - return Session_ID(Botan::hex_decode(stmt->get_str(0))); + return Session_Handle(Session_ID(Botan::hex_decode(stmt->get_str(0)))); } }(); - std::pair blob = stmt->get_blob(2); + const std::pair blob = stmt->get_blob(2); try { found_sessions.emplace_back( @@ -241,7 +241,7 @@ size_t Session_Manager_SQL::remove(const Session_Handle& handle) { // The number of deleted rows is taken globally from the database connection, // therefore we need to serialize this implementation. - lock_guard_type lk(mutex()); + const lock_guard_type lk(mutex()); if(const auto id = handle.id()) { auto stmt = m_db->new_statement("DELETE FROM tls_sessions WHERE session_id = ?1"); @@ -262,7 +262,7 @@ size_t Session_Manager_SQL::remove_all() { // The number of deleted rows is taken globally from the database connection, // therefore we need to serialize this implementation. - lock_guard_type lk(mutex()); + const lock_guard_type lk(mutex()); m_db->exec("DELETE FROM tls_sessions"); return m_db->rows_changed_by_last_statement(); diff -Nru botan3-3.7.1+dfsg/src/lib/tls/sessions_sql/tls_session_manager_sql.h botan3-3.12.0+dfsg/src/lib/tls/sessions_sql/tls_session_manager_sql.h --- botan3-3.7.1+dfsg/src/lib/tls/sessions_sql/tls_session_manager_sql.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/sessions_sql/tls_session_manager_sql.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #define BOTAN_TLS_SQL_SESSION_MANAGER_H_ #include +#include #include namespace Botan { @@ -43,6 +44,9 @@ Session_Manager_SQL(const Session_Manager_SQL&) = delete; Session_Manager_SQL& operator=(const Session_Manager_SQL&) = delete; + Session_Manager_SQL(Session_Manager_SQL&&) = delete; + Session_Manager_SQL& operator=(Session_Manager_SQL&&) = delete; + ~Session_Manager_SQL() override = default; void store(const Session& session, const Session_Handle& handle) override; size_t remove(const Session_Handle& handle) override; @@ -69,7 +73,7 @@ // 20120609 - older (Botan 2.0) database scheme // 20230113 - adapt to Botan 3.0 Session_Manager API // (Session objects don't contain Session_ID, Session_Ticket) - enum Schema_Revision { + enum Schema_Revision /* NOLINT(*-use-enum-class) */ { EMPTY = 0, CORRUPTED = 1, PRE_BOTAN_3_0 = 20120609, diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/info.txt botan3-3.12.0+dfsg/src/lib/tls/tls12/info.txt --- botan3-3.7.1+dfsg/src/lib/tls/tls12/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -8,10 +8,13 @@ +tls_messages_12.h +tls_extensions_12.h tls_channel_impl_12.h +tls_connection_state_12.h tls_client_impl_12.h tls_record.h tls_server_impl_12.h @@ -23,17 +26,9 @@ -aead -aes -asn1 -dh eme_pkcs1 emsa_pkcs1 -gcm -hmac prf_tls -rng rsa -x509 tls diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_cert_status.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_cert_status.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_cert_status.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_cert_status.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,72 +0,0 @@ -/* -* Certificate Status -* (C) 2016 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace Botan::TLS { - -Certificate_Status::Certificate_Status(const std::vector& buf, const Connection_Side) { - if(buf.size() < 5) { - throw Decoding_Error("Invalid Certificate_Status message: too small"); - } - - if(buf[0] != 1) { // not OCSP - throw Decoding_Error("Unexpected Certificate_Status message: unexpected response type"); - } - - size_t len = make_uint32(0, buf[1], buf[2], buf[3]); - - // Verify the redundant length field... - if(buf.size() != len + 4) { - throw Decoding_Error("Invalid Certificate_Status: invalid length field"); - } - - m_response.assign(buf.begin() + 4, buf.end()); -} - -Certificate_Status::Certificate_Status(Handshake_IO& io, Handshake_Hash& hash, const OCSP::Response& ocsp) : - m_response(ocsp.raw_bits()) { - hash.update(io.send(*this)); -} - -Certificate_Status::Certificate_Status(Handshake_IO& io, - Handshake_Hash& hash, - std::vector raw_response_bytes) : - Certificate_Status(std::move(raw_response_bytes)) { - hash.update(io.send(*this)); -} - -Certificate_Status::Certificate_Status(std::vector raw_response_bytes) : - m_response(std::move(raw_response_bytes)) {} - -std::vector Certificate_Status::serialize() const { - if(m_response.size() > 0xFFFFFF) { // unlikely - throw Encoding_Error("OCSP response too long to encode in TLS"); - } - - const uint32_t response_len = static_cast(m_response.size()); - - std::vector buf; - buf.reserve(1 + 3 + m_response.size()); - buf.push_back(1); // type OCSP - for(size_t i = 1; i < 4; ++i) { - buf.push_back(get_byte_var(i, response_len)); - } - - buf += m_response; - return buf; -} - -} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_cert_status_12.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_cert_status_12.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_cert_status_12.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_cert_status_12.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,22 @@ +/* +* Certificate Status +* (C) 2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan::TLS { + +Certificate_Status_12::Certificate_Status_12(Handshake_IO& io, + Handshake_Hash& hash, + std::vector raw_response_bytes) : + Certificate_Status(std::move(raw_response_bytes)) { + hash.update(io.send(*this)); +} + +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_cert_verify_12.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_cert_verify_12.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_cert_verify_12.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_cert_verify_12.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,61 @@ +/* +* Certificate Verify Message +* (C) 2004,2006,2011,2012 Jack Lloyd +* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include +#include + +namespace Botan::TLS { + +/* +* Create a new Certificate Verify message for TLS 1.2 +*/ +Certificate_Verify_12::Certificate_Verify_12(Handshake_IO& io, + Handshake_State& state, + const Policy& policy, + RandomNumberGenerator& rng, + const Private_Key* priv_key) { + BOTAN_ASSERT_NONNULL(priv_key); + + const std::pair format = state.choose_sig_format(*priv_key, m_scheme, true, policy); + + m_signature = + state.callbacks().tls_sign_message(*priv_key, rng, format.first, format.second, state.hash().get_contents()); + + state.hash().update(io.send(*this)); +} + +bool Certificate_Verify_12::verify(const X509_Certificate& cert, + const Handshake_State& state, + const Policy& policy) const { + auto key = cert.subject_public_key(); + + policy.check_peer_key_acceptable(*key); + + const std::pair format = + state.parse_sig_format(*key, m_scheme, state.client_hello()->signature_schemes(), true, policy); + + const bool signature_valid = + state.callbacks().tls_verify_message(*key, format.first, format.second, state.hash().get_contents(), m_signature); + +#if defined(BOTAN_UNSAFE_FUZZER_MODE) + BOTAN_UNUSED(signature_valid); + return true; + +#else + return signature_valid; + +#endif +} + +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_certificate_12.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_certificate_12.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_certificate_12.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_certificate_12.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -5,19 +5,22 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include +#include #include #include #include #include +#include +#include #include #include #include -#include namespace Botan::TLS { +Certificate_12::~Certificate_12() = default; + /** * Create a new Certificate message */ @@ -47,7 +50,7 @@ const uint8_t* certs = buf.data() + 3; - while(size_t remaining_bytes = buf.data() + buf.size() - certs) { + while(const size_t remaining_bytes = buf.data() + buf.size() - certs) { if(remaining_bytes < 3) { throw Decoding_Error("Certificate: Message malformed"); } @@ -59,7 +62,12 @@ } DataSource_Memory cert_buf(&certs[3], cert_size); - m_certs.push_back(X509_Certificate(cert_buf)); + try { + m_certs.push_back(X509_Certificate(cert_buf)); + } catch(Exception& e) { + // bad_certificate would make more sense but BoGo expects decoding_error + throw TLS_Exception(Alert::DecodeError, e.what()); + } certs += cert_size + 3; } @@ -99,4 +107,8 @@ return buf; } +size_t Certificate_12::count() const { + return m_certs.size(); +} + } // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_certificate_req_12.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_certificate_req_12.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_certificate_req_12.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_certificate_req_12.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,164 @@ +/* +* Certificate Request Message +* (C) 2004-2006,2012 Jack Lloyd +* 2021 Elektrobit Automotive GmbH +* 2022 René Meusel, Hannes Rantzsch - neXenio GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Botan::TLS { + +Certificate_Request_12::~Certificate_Request_12() = default; + +Handshake_Type Certificate_Request_12::type() const { + return Handshake_Type::CertificateRequest; +} + +namespace { + +std::string cert_type_code_to_name(uint8_t code) { + switch(code) { + case 1: + return "RSA"; + case 64: + return "ECDSA"; + default: + return ""; // DH or something else + } +} + +uint8_t cert_type_name_to_code(std::string_view name) { + if(name == "RSA") { + return 1; + } + if(name == "ECDSA") { + return 64; + } + + throw Invalid_Argument(fmt("Unknown/unhandled TLS cert type {}", name)); +} + +} // namespace + +/** +* Create a new Certificate Request message +*/ +Certificate_Request_12::Certificate_Request_12(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + const std::vector& ca_certs) : + m_names(ca_certs), m_cert_key_types({"RSA", "ECDSA"}) { + m_schemes = policy.acceptable_signature_schemes(); + // RFC 5246 7.4.4: supported_signature_algorithms<2..2^16-2> + if(m_schemes.empty()) { + throw Internal_Error("Policy returned no acceptable signature schemes for CertificateRequest"); + } + hash.update(io.send(*this)); +} + +/** +* Deserialize a Certificate Request message +*/ +Certificate_Request_12::Certificate_Request_12(const std::vector& buf) { + if(buf.size() < 4) { + throw Decoding_Error("Certificate_Req: Bad certificate request"); + } + + TLS_Data_Reader reader("CertificateRequest", buf); + + const auto cert_type_codes = reader.get_range_vector(1, 1, 255); + + for(const auto cert_type_code : cert_type_codes) { + const std::string cert_type_name = cert_type_code_to_name(cert_type_code); + + if(cert_type_name.empty()) { // something we don't know + continue; + } + + m_cert_key_types.emplace_back(cert_type_name); + } + + const std::vector algs = reader.get_range_vector(2, 2, 65534); + + if(algs.size() % 2 != 0) { + throw Decoding_Error("Bad length for signature IDs in certificate request"); + } + + for(size_t i = 0; i != algs.size(); i += 2) { + m_schemes.emplace_back(make_uint16(algs[i], algs[i + 1])); + } + + const uint16_t purported_size = reader.get_uint16_t(); + + if(reader.remaining_bytes() != purported_size) { + throw Decoding_Error("Inconsistent length in certificate request"); + } + + while(reader.has_remaining()) { + // RFC 5246 7.4.4: opaque DistinguishedName<1..2^16-1> + std::vector name_bits = reader.get_range_vector(2, 1, 65535); + + BER_Decoder decoder(name_bits, BER_Decoder::Limits::DER()); + X509_DN name; + decoder.decode(name).verify_end(); + m_names.emplace_back(name); + } +} + +const std::vector& Certificate_Request_12::acceptable_cert_types() const { + return m_cert_key_types; +} + +const std::vector& Certificate_Request_12::acceptable_CAs() const { + return m_names; +} + +const std::vector& Certificate_Request_12::signature_schemes() const { + return m_schemes; +} + +/** +* Serialize a Certificate Request message +*/ +std::vector Certificate_Request_12::serialize() const { + std::vector buf; + + std::vector cert_types; + + cert_types.reserve(m_cert_key_types.size()); + for(const auto& cert_key_type : m_cert_key_types) { + cert_types.push_back(cert_type_name_to_code(cert_key_type)); + } + + append_tls_length_value(buf, cert_types, 1); + + // RFC 5246 7.4.4: supported_signature_algorithms<2..2^16-2> + buf += Signature_Algorithms(m_schemes).serialize(Connection_Side::Server); + + std::vector encoded_names; + + for(const auto& name : m_names) { + DER_Encoder encoder; + encoder.encode(name); + + append_tls_length_value(encoded_names, encoder.get_contents(), 2); + } + + append_tls_length_value(buf, encoded_names, 2); + + return buf; +} +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_client_hello_12.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_client_hello_12.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_client_hello_12.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_client_hello_12.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,297 @@ +/* +* TLS Hello Request and Client Hello Messages +* (C) 2004-2011,2015,2016 Jack Lloyd +* 2016 Matthias Gierlings +* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Botan::TLS { + +void Client_Hello_12::update_hello_cookie(const Hello_Verify_Request& hello_verify) { + BOTAN_STATE_CHECK(m_data->legacy_version().is_datagram_protocol()); + + m_data->m_hello_cookie = hello_verify.cookie(); +} + +bool Client_Hello_12::prefers_compressed_ec_points() const { + if(const Supported_Point_Formats* ecc_formats = m_data->extensions().get()) { + return ecc_formats->prefers_compressed(); + } + return false; +} + +bool Client_Hello_12::secure_renegotiation() const { + return m_data->extensions().has(); +} + +std::vector Client_Hello_12::renegotiation_info() const { + if(const Renegotiation_Extension* reneg = m_data->extensions().get()) { + return reneg->renegotiation_info(); + } + return {}; +} + +bool Client_Hello_12::supports_session_ticket() const { + return m_data->extensions().has(); +} + +Session_Ticket Client_Hello_12::session_ticket() const { + if(auto* ticket = m_data->extensions().get()) { + return ticket->contents(); + } + return {}; +} + +std::optional Client_Hello_12::session_handle() const { + // RFC 5077 3.4 + // If a ticket is presented by the client, the server MUST NOT attempt + // to use the Session ID in the ClientHello for stateful session + // resumption. + if(auto ticket = session_ticket(); !ticket.empty()) { + return Session_Handle(ticket); + } else if(const auto& id = session_id(); !id.empty()) { + return Session_Handle(id); + } else { + return std::nullopt; + } +} + +bool Client_Hello_12::supports_extended_master_secret() const { + return m_data->extensions().has(); +} + +bool Client_Hello_12::supports_cert_status_message() const { + return m_data->extensions().has(); +} + +bool Client_Hello_12::supports_encrypt_then_mac() const { + return m_data->extensions().has(); +} + +void Client_Hello_12::add_tls12_supported_groups_extensions(const Policy& policy) { + // RFC 7919 3. + // A client that offers a group MUST be able and willing to perform a DH + // key exchange using that group. + // + // We don't support hybrid key exchange in TLS 1.2 + + std::vector compatible_kex_groups; + for(const auto& group : policy.key_exchange_groups()) { + if(!group.is_post_quantum()) { + compatible_kex_groups.push_back(group); + } + } + + auto supported_groups = std::make_unique(std::move(compatible_kex_groups)); + + if(!supported_groups->ec_groups().empty()) { + // NOLINTNEXTLINE(*-owning-memory) + m_data->extensions().add(new Supported_Point_Formats(policy.use_ecc_point_compression())); + } + + m_data->extensions().add(std::move(supported_groups)); +} + +/* +* Create a new Client Hello message +*/ +Client_Hello_12::Client_Hello_12(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + const std::vector& reneg_info, + const Client_Hello_12::Settings& client_settings, + const std::vector& next_protocols) { + m_data->m_legacy_version = client_settings.protocol_version(); + m_data->m_random = make_hello_random(rng, cb, policy); + m_data->m_suites = policy.ciphersuite_list(client_settings.protocol_version()); + + if(!policy.acceptable_protocol_version(m_data->legacy_version())) { + throw Internal_Error("Offering " + m_data->legacy_version().to_string() + + " but our own policy does not accept it"); + } + + /* + * Place all empty extensions in front to avoid a bug in some systems + * which reject hellos when the last extension in the list is empty. + */ + + // NOLINTBEGIN(*-owning-memory) + + // EMS must always be used with TLS 1.2, regardless of the policy used. + + m_data->extensions().add(new Extended_Master_Secret); + + if(policy.negotiate_encrypt_then_mac()) { + m_data->extensions().add(new Encrypt_then_MAC); + } + + m_data->extensions().add(new Session_Ticket_Extension()); + + m_data->extensions().add(new Renegotiation_Extension(reneg_info)); + + m_data->extensions().add(new Supported_Versions(m_data->legacy_version(), policy)); + + if(Server_Name_Indicator::hostname_acceptable_for_sni(client_settings.hostname())) { + m_data->extensions().add(new Server_Name_Indicator(client_settings.hostname())); + } + + if(policy.support_cert_status_message()) { + m_data->extensions().add(new Certificate_Status_Request({}, {})); + } + + add_tls12_supported_groups_extensions(policy); + + m_data->extensions().add(new Signature_Algorithms(policy.acceptable_signature_schemes())); + if(auto cert_signing_prefs = policy.acceptable_certificate_signature_schemes()) { + // RFC 8446 4.2.3 + // TLS 1.2 implementations SHOULD also process this extension. + // Implementations which have the same policy in both cases MAY omit + // the "signature_algorithms_cert" extension. + m_data->extensions().add(new Signature_Algorithms_Cert(std::move(cert_signing_prefs.value()))); + } + + if(reneg_info.empty() && !next_protocols.empty()) { + m_data->extensions().add(new Application_Layer_Protocol_Notification(next_protocols)); + } + + if(m_data->legacy_version().is_datagram_protocol()) { + m_data->extensions().add(new SRTP_Protection_Profiles(policy.srtp_profiles())); + } + + // NOLINTEND(*-owning-memory) + + cb.tls_modify_extensions(m_data->extensions(), Connection_Side::Client, type()); + + hash.update(io.send(*this)); +} + +/* +* Create a new Client Hello message (session resumption case) +*/ +Client_Hello_12::Client_Hello_12(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + const std::vector& reneg_info, + const Session_with_Handle& session, + const std::vector& next_protocols) { + m_data->m_legacy_version = session.session.version(); + m_data->m_random = make_hello_random(rng, cb, policy); + + // RFC 5077 3.4 + // When presenting a ticket, the client MAY generate and include a + // Session ID in the TLS ClientHello. [...] If a ticket is presented by + // the client, the server MUST NOT attempt to use the Session ID in the + // ClientHello for stateful session resumption. + m_data->m_session_id = session.handle.id().value_or(Session_ID(make_hello_random(rng, cb, policy))); + m_data->m_suites = policy.ciphersuite_list(m_data->legacy_version()); + + if(!policy.acceptable_protocol_version(session.session.version())) { + throw Internal_Error("Offering " + m_data->legacy_version().to_string() + + " but our own policy does not accept it"); + } + + if(!value_exists(m_data->ciphersuites(), session.session.ciphersuite_code())) { + m_data->m_suites.push_back(session.session.ciphersuite_code()); + } + + /* + * As EMS must always be used with TLS 1.2, add it even if it wasn't used + * in the original session. If the server understands it and follows the + * RFC it should reject our resume attempt and upgrade us to a new session + * with the EMS protection. + */ + // NOLINTBEGIN(*-owning-memory) + m_data->extensions().add(new Extended_Master_Secret); + + if(session.session.supports_encrypt_then_mac()) { + m_data->extensions().add(new Encrypt_then_MAC); + } + + if(session.handle.is_ticket()) { + m_data->extensions().add(new Session_Ticket_Extension(session.handle.ticket().value())); + } + + m_data->extensions().add(new Renegotiation_Extension(reneg_info)); + + const std::string hostname = session.session.server_info().hostname(); + + if(Server_Name_Indicator::hostname_acceptable_for_sni(hostname)) { + m_data->extensions().add(new Server_Name_Indicator(hostname)); + } + + if(policy.support_cert_status_message()) { + m_data->extensions().add(new Certificate_Status_Request({}, {})); + } + + add_tls12_supported_groups_extensions(policy); + + m_data->extensions().add(new Signature_Algorithms(policy.acceptable_signature_schemes())); + if(auto cert_signing_prefs = policy.acceptable_certificate_signature_schemes()) { + // RFC 8446 4.2.3 + // TLS 1.2 implementations SHOULD also process this extension. + // Implementations which have the same policy in both cases MAY omit + // the "signature_algorithms_cert" extension. + m_data->extensions().add(new Signature_Algorithms_Cert(std::move(cert_signing_prefs.value()))); + } + + if(reneg_info.empty() && !next_protocols.empty()) { + m_data->extensions().add(new Application_Layer_Protocol_Notification(next_protocols)); + } + + // NOLINTEND(*-owning-memory) + + cb.tls_modify_extensions(m_data->extensions(), Connection_Side::Client, type()); + + hash.update(io.send(*this)); +} + +Client_Hello_12::Client_Hello_12(const std::vector& buf) : + Client_Hello_12(std::make_unique(buf)) {} + +Client_Hello_12::Client_Hello_12(std::unique_ptr data) : Client_Hello_12_Shim(std::move(data)) { + const uint16_t TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF; + + if(offered_suite(static_cast(TLS_EMPTY_RENEGOTIATION_INFO_SCSV))) { + if(const Renegotiation_Extension* reneg = m_data->extensions().get()) { + if(!reneg->renegotiation_info().empty()) { + throw TLS_Exception(Alert::HandshakeFailure, "Client sent renegotiation SCSV and non-empty extension"); + } + } else { + // add fake extension + m_data->extensions().add(new Renegotiation_Extension()); // NOLINT(*-owning-memory) + } + } +} + +Hello_Request::Hello_Request(Handshake_IO& io) { + io.send(*this); +} + +Hello_Request::Hello_Request(const std::vector& buf) { + if(!buf.empty()) { + throw Decoding_Error("Bad Hello_Request, has non-zero size"); + } +} + +std::vector Hello_Request::serialize() const { + return std::vector(); +} + +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_client_kex.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_client_kex.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_client_kex.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_client_kex.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,24 +6,51 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include +#include +#include +#include #include +#include +#include #include - -#include +#include #include +#include #include #include #include #include #include -#include -#include - namespace Botan::TLS { +namespace { + +/* +* If the (p, g) pair the server sent corresponds to a known group +* (RFC 7919 or RFC 3526), return that group. +*/ +std::optional match_well_known_dh_group(const BigInt& p, const BigInt& g) { + const size_t p_bits = p.bits(); + + if(p_bits != 2048 && p_bits != 3072 && p_bits != 4096 && p_bits != 6144 && p_bits != 8192) { + return {}; + } + + for(const char* prefix : {"ffdhe/ietf", "modp/ietf"}) { + const std::string name = fmt("{}/{}", prefix, p_bits); + auto candidate = DL_Group::from_name(name); + if(candidate.get_p() == p && candidate.get_g() == g) { + return candidate; + } + } + + return std::nullopt; +} + +} // namespace + /* * Create a new Client Key Exchange message */ @@ -39,34 +66,42 @@ if(kex_algo == Kex_Algo::PSK) { std::string identity_hint; - if(state.server_kex()) { + if(state.server_kex() != nullptr) { TLS_Data_Reader reader("ClientKeyExchange", state.server_kex()->params()); identity_hint = reader.get_string(2, 0, 65535); } m_psk_identity = creds.psk_identity("tls-client", std::string(hostname), identity_hint); - append_tls_length_value(m_key_material, to_byte_vector(m_psk_identity.value()), 2); + append_tls_length_value(m_key_material, as_span_of_bytes(m_psk_identity.value()), 2); - SymmetricKey psk = creds.psk("tls-client", std::string(hostname), m_psk_identity.value()); + const SymmetricKey psk = creds.psk("tls-client", std::string(hostname), m_psk_identity.value()); - std::vector zeros(psk.length()); + if(psk.empty()) { + throw TLS_Exception(Alert::InternalError, "Application did not provide a PSK for the negotiated identity"); + } + + const std::vector zeros(psk.length()); append_tls_length_value(m_pre_master, zeros, 2); append_tls_length_value(m_pre_master, psk.bits_of(), 2); - } else if(state.server_kex()) { + } else if(state.server_kex() != nullptr) { TLS_Data_Reader reader("ClientKeyExchange", state.server_kex()->params()); SymmetricKey psk; if(kex_algo == Kex_Algo::ECDHE_PSK) { - std::string identity_hint = reader.get_string(2, 0, 65535); + const std::string identity_hint = reader.get_string(2, 0, 65535); m_psk_identity = creds.psk_identity("tls-client", std::string(hostname), identity_hint); - append_tls_length_value(m_key_material, to_byte_vector(m_psk_identity.value()), 2); + append_tls_length_value(m_key_material, as_span_of_bytes(m_psk_identity.value()), 2); psk = creds.psk("tls-client", std::string(hostname), m_psk_identity.value()); + + if(psk.empty()) { + throw TLS_Exception(Alert::InternalError, "Application did not provide a PSK for the negotiated identity"); + } } if(kex_algo == Kex_Algo::DH) { @@ -74,27 +109,36 @@ const auto generator = BigInt::from_bytes(reader.get_range(2, 1, 65535)); const std::vector peer_public_value = reader.get_range(2, 1, 65535); - if(reader.remaining_bytes()) { + if(reader.remaining_bytes() > 0) { throw Decoding_Error("Bad params size for DH key exchange"); } - DL_Group group(modulus, generator); - - if(!group.verify_group(rng, false)) { - throw TLS_Exception(Alert::InsufficientSecurity, "DH group validation failed"); + if(modulus.bits() < policy.minimum_dh_group_size()) { + throw TLS_Exception(Alert::InsufficientSecurity, "DH prime too small for policy"); } + if(modulus.bits() > policy.maximum_dh_group_size()) { + throw TLS_Exception(Alert::IllegalParameter, "DH prime too large for policy"); + } + + const auto group = [&] { + if(auto matched = match_well_known_dh_group(modulus, generator)) { + return std::move(*matched); + } else { + /* + * Even if we sent ffdhe groups in the supported_groups extension + * a server may have replied with some other group. + */ + DL_Group ad_hoc(modulus, generator); + if(!ad_hoc.verify_group(rng, false)) { + throw TLS_Exception(Alert::InsufficientSecurity, "DH group validation failed"); + } + return ad_hoc; + } + }(); const auto private_key = state.callbacks().tls_generate_ephemeral_key(group, rng); - auto shared_secret = CT::strip_leading_zeros( + m_pre_master = CT::strip_leading_zeros( state.callbacks().tls_ephemeral_key_agreement(group, *private_key, peer_public_value, rng, policy)); - - if(kex_algo == Kex_Algo::DH) { - m_pre_master = std::move(shared_secret); - } else { - append_tls_length_value(m_pre_master, shared_secret, 2); - append_tls_length_value(m_pre_master, psk.bits_of(), 2); - } - append_tls_length_value(m_key_material, private_key->public_value(), 2); } else if(kex_algo == Kex_Algo::ECDH || kex_algo == Kex_Algo::ECDHE_PSK) { const uint8_t curve_type = reader.get_byte(); @@ -110,11 +154,34 @@ "Server selected a group that is not compatible with the negotiated ciphersuite"); } + // RFC 8422 5.1: the server MUST select a curve from the + // supported_groups list the client offered. Check against the actual + // offered list (which may be a strict subset of the policy's + // key_exchange_groups() if the application narrowed it via + // tls_modify_extensions) rather than just the policy. + if(!value_exists(state.client_hello()->supported_ecc_curves(), curve_id)) { + throw TLS_Exception(Alert::IllegalParameter, "Server selected a curve we did not offer"); + } + if(policy.choose_key_exchange_group({curve_id}, {}) != curve_id) { throw TLS_Exception(Alert::HandshakeFailure, "Server sent ECC curve prohibited by policy"); } - const auto private_key = state.callbacks().tls_generate_ephemeral_key(curve_id, rng); + const auto private_key = [&] { + if(curve_id.is_ecdh_named_curve()) { + const auto pubkey_point_format = state.server_hello()->prefers_compressed_ec_points() + ? EC_Point_Format::Compressed + : EC_Point_Format::Uncompressed; + return state.callbacks().tls12_generate_ephemeral_ecdh_key(curve_id, rng, pubkey_point_format); + } else { + return state.callbacks().tls_generate_ephemeral_key(curve_id, rng); + } + }(); + + if(!private_key) { + throw TLS_Exception(Alert::InternalError, "Application did not provide an EC key"); + } + auto shared_secret = state.callbacks().tls_ephemeral_key_agreement(curve_id, *private_key, peer_public_value, rng, policy); @@ -125,19 +192,10 @@ append_tls_length_value(m_pre_master, psk.bits_of(), 2); } - if(curve_id.is_ecdh_named_curve()) { - auto ecdh_key = dynamic_cast(private_key.get()); - if(!ecdh_key) { - throw TLS_Exception(Alert::InternalError, "Application did not provide a ECDH_PublicKey"); - } - append_tls_length_value(m_key_material, - ecdh_key->public_value(state.server_hello()->prefers_compressed_ec_points() - ? EC_Point_Format::Compressed - : EC_Point_Format::Uncompressed), - 1); - } else { - append_tls_length_value(m_key_material, private_key->public_value(), 1); - } + // Note: In contrast to public_value(), raw_public_key_bits() takes the + // point format (compressed vs. uncompressed) into account that was set + // in its construction within tls_generate_ephemeral_key(). + append_tls_length_value(m_key_material, private_key->raw_public_key_bits(), 1); } else { throw Internal_Error("Client_Key_Exchange: Unknown key exchange method was negotiated"); } @@ -150,18 +208,18 @@ throw Unexpected_Message("No server kex message, but negotiated a key exchange that required it"); } - if(!server_public_key) { + if(server_public_key == nullptr) { throw Internal_Error("No server public key for RSA exchange"); } - if(auto rsa_pub = dynamic_cast(server_public_key)) { + if(const auto* rsa_pub = dynamic_cast(server_public_key)) { const Protocol_Version offered_version = state.client_hello()->legacy_version(); rng.random_vec(m_pre_master, 48); m_pre_master[0] = offered_version.major_version(); m_pre_master[1] = offered_version.minor_version(); - PK_Encryptor_EME encryptor(*rsa_pub, rng, "PKCS1v15"); + const PK_Encryptor_EME encryptor(*rsa_pub, rng, "PKCS1v15"); const std::vector encrypted_key = encryptor.encrypt(m_pre_master, rng); @@ -190,7 +248,7 @@ BOTAN_ASSERT(state.server_certs() && !state.server_certs()->cert_chain().empty(), "RSA key exchange negotiated so server sent a certificate"); - if(!server_rsa_kex_key) { + if(server_rsa_kex_key == nullptr) { throw Internal_Error("Expected RSA kex but no server kex key set"); } @@ -199,10 +257,11 @@ } TLS_Data_Reader reader("ClientKeyExchange", contents); - const std::vector encrypted_pre_master = reader.get_range(2, 0, 65535); + // RFC 5246 7.4.7.1: encrypted_pre_master_secret<1..2^16-1>. + const std::vector encrypted_pre_master = reader.get_range(2, 1, 65535); reader.assert_done(); - PK_Decryptor_EME decryptor(*server_rsa_kex_key, rng, "PKCS1v15"); + const PK_Decryptor_EME decryptor(*server_rsa_kex_key, rng, "PKCS1v15"); const uint8_t client_major = state.client_hello()->legacy_version().major_version(); const uint8_t client_minor = state.client_hello()->legacy_version().minor_version(); @@ -233,7 +292,12 @@ if(key_exchange_is_psk(kex_algo)) { m_psk_identity = reader.get_string(2, 0, 65535); - psk = creds.psk("tls-server", state.client_hello()->sni_hostname(), m_psk_identity.value()); + try { + psk = creds.psk("tls-server", state.client_hello()->sni_hostname(), m_psk_identity.value()); + } catch(...) { + // Treat any lookup failure for the identity sent by the client as + // "no PSK for this identity" and let the logic below handle it + } if(psk.empty()) { if(policy.hide_unknown_users()) { @@ -245,14 +309,15 @@ } if(kex_algo == Kex_Algo::PSK) { - std::vector zeros(psk.length()); + reader.assert_done(); + const std::vector zeros(psk.length()); append_tls_length_value(m_pre_master, zeros, 2); append_tls_length_value(m_pre_master, psk.bits_of(), 2); } else if(kex_algo == Kex_Algo::DH || kex_algo == Kex_Algo::ECDH || kex_algo == Kex_Algo::ECDHE_PSK) { const PK_Key_Agreement_Key& ka_key = state.server_kex()->server_kex_key(); const std::vector client_pubkey = (ka_key.algo_name() == "DH") - ? reader.get_range(2, 0, 65535) + ? reader.get_range(2, 1, 65535) : reader.get_range(1, 1, 255); const auto shared_group = state.server_kex()->shared_group(); @@ -274,9 +339,8 @@ } } catch(Invalid_Argument& e) { throw TLS_Exception(Alert::IllegalParameter, e.what()); - } catch(TLS_Exception& e) { - // NOLINTNEXTLINE(cert-err60-cpp) - throw e; + } catch(TLS_Exception&) { + throw; // rethrow } catch(std::exception&) { /* * Something failed in the DH/ECDH computation. To avoid possible diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_finished_12.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_finished_12.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_finished_12.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_finished_12.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,59 @@ +/* +* Finished Message +* (C) 2004-2006,2012 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include + +namespace Botan::TLS { + +namespace { + +/* +* Compute the verify_data for TLS 1.2 +*/ +std::vector finished_compute_verify_12(const Handshake_State& state, Connection_Side side) { + const uint8_t TLS_CLIENT_LABEL[] = { + 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x20, 0x66, 0x69, 0x6E, 0x69, 0x73, 0x68, 0x65, 0x64}; + + const uint8_t TLS_SERVER_LABEL[] = { + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x66, 0x69, 0x6E, 0x69, 0x73, 0x68, 0x65, 0x64}; + + auto prf = state.protocol_specific_prf(); + + std::vector input; + std::vector label; + label += (side == Connection_Side::Client) ? std::make_pair(TLS_CLIENT_LABEL, sizeof(TLS_CLIENT_LABEL)) + : std::make_pair(TLS_SERVER_LABEL, sizeof(TLS_SERVER_LABEL)); + + input += state.hash().final(state.ciphersuite().prf_algo()); + + return unlock(prf->derive_key(12, state.session_keys().master_secret(), input, label)); +} + +} // namespace + +Finished_12::Finished_12(Handshake_IO& io, Handshake_State& state, Connection_Side side) { + m_verification_data = finished_compute_verify_12(state, side); + state.hash().update(io.send(*this)); +} + +bool Finished_12::verify(const Handshake_State& state, Connection_Side side) const { + std::vector computed_verify = finished_compute_verify_12(state, side); + +#if defined(BOTAN_UNSAFE_FUZZER_MODE) + return true; +#else + return CT::is_equal(m_verification_data, computed_verify).as_bool(); +#endif +} + +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_hello_verify.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_hello_verify.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_hello_verify.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_hello_verify.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include #include +#include namespace Botan::TLS { @@ -16,7 +17,7 @@ throw Decoding_Error("Hello verify request too small"); } - Protocol_Version version(buf[0], buf[1]); + const Protocol_Version version(buf[0], buf[1]); if(!version.is_datagram_protocol()) { throw Decoding_Error("Unknown version from server in hello verify request"); @@ -50,13 +51,12 @@ negotiated (RFC 6347, section 4.2.1) */ - Protocol_Version format_version(254, 255); // DTLS 1.0 + const Protocol_Version format_version(254, 255); // DTLS 1.0 std::vector bits; bits.push_back(format_version.major_version()); bits.push_back(format_version.minor_version()); - bits.push_back(static_cast(m_cookie.size())); - bits += m_cookie; + append_tls_length_value(bits, m_cookie, 1); return bits; } diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_server_hello_12.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_server_hello_12.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_server_hello_12.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_server_hello_12.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,229 @@ +/* +* TLS Server Hello and Server Hello Done +* (C) 2004-2011,2015,2016,2019 Jack Lloyd +* 2016 Matthias Gierlings +* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include +#include + +namespace Botan::TLS { + +// New session case +Server_Hello_12::Server_Hello_12(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + const std::vector& reneg_info, + const Client_Hello_12& client_hello, + const Server_Hello_12::Settings& server_settings, + std::string_view next_protocol) : + Server_Hello_12(std::make_unique( + server_settings.protocol_version(), + server_settings.session_id(), + make_server_hello_random(rng, server_settings.protocol_version(), cb, policy), + server_settings.ciphersuite(), + uint8_t(0))) { + // NOLINTBEGIN(*-owning-memory) + if(client_hello.supports_extended_master_secret()) { + m_data->extensions().add(new Extended_Master_Secret); + } + + // Sending the extension back does not commit us to sending a stapled response + if(client_hello.supports_cert_status_message() && policy.support_cert_status_message()) { + m_data->extensions().add(new Certificate_Status_Request); + } + + if(!next_protocol.empty() && client_hello.supports_alpn()) { + m_data->extensions().add(new Application_Layer_Protocol_Notification(next_protocol)); + } + + const auto c = Ciphersuite::by_id(m_data->ciphersuite()); + + if(c && c->cbc_ciphersuite() && client_hello.supports_encrypt_then_mac() && policy.negotiate_encrypt_then_mac()) { + m_data->extensions().add(new Encrypt_then_MAC); + } + + if(c && c->ecc_ciphersuite() && client_hello.extension_types().contains(Extension_Code::EcPointFormats)) { + m_data->extensions().add(new Supported_Point_Formats(policy.use_ecc_point_compression())); + } + + if(client_hello.secure_renegotiation()) { + m_data->extensions().add(new Renegotiation_Extension(reneg_info)); + } + + if(client_hello.supports_session_ticket() && server_settings.offer_session_ticket()) { + m_data->extensions().add(new Session_Ticket_Extension()); + } + + if(m_data->legacy_version().is_datagram_protocol()) { + const std::vector server_srtp = policy.srtp_profiles(); + const std::vector client_srtp = client_hello.srtp_profiles(); + + if(!server_srtp.empty() && !client_srtp.empty()) { + uint16_t shared = 0; + // always using server preferences for now + for(auto s_srtp : server_srtp) { + for(auto c_srtp : client_srtp) { + if(shared == 0 && s_srtp == c_srtp) { + shared = s_srtp; + } + } + } + + if(shared != 0) { + m_data->extensions().add(new SRTP_Protection_Profiles(shared)); + } + } + } + // NOLINTEND(*-owning-memory) + + cb.tls_modify_extensions(m_data->extensions(), Connection_Side::Server, type()); + + hash.update(io.send(*this)); +} + +// Resuming +Server_Hello_12::Server_Hello_12(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + const std::vector& reneg_info, + const Client_Hello_12& client_hello, + const Session& resumed_session, + bool offer_session_ticket, + std::string_view next_protocol) : + Server_Hello_12( + std::make_unique(resumed_session.version(), + client_hello.session_id(), + make_server_hello_random(rng, resumed_session.version(), cb, policy), + resumed_session.ciphersuite_code(), + uint8_t(0))) { + // NOLINTBEGIN(*-owning-memory) + if(client_hello.supports_extended_master_secret()) { + m_data->extensions().add(new Extended_Master_Secret); + } + + if(!next_protocol.empty() && client_hello.supports_alpn()) { + m_data->extensions().add(new Application_Layer_Protocol_Notification(next_protocol)); + } + + if(client_hello.supports_encrypt_then_mac() && policy.negotiate_encrypt_then_mac()) { + const Ciphersuite c = resumed_session.ciphersuite(); + if(c.cbc_ciphersuite()) { + m_data->extensions().add(new Encrypt_then_MAC); + } + } + + if(resumed_session.ciphersuite().ecc_ciphersuite() && + client_hello.extension_types().contains(Extension_Code::EcPointFormats)) { + m_data->extensions().add(new Supported_Point_Formats(policy.use_ecc_point_compression())); + } + + if(client_hello.secure_renegotiation()) { + m_data->extensions().add(new Renegotiation_Extension(reneg_info)); + } + + if(client_hello.supports_session_ticket() && offer_session_ticket) { + m_data->extensions().add(new Session_Ticket_Extension()); + } + // NOLINTEND(*-owning-memory) + + cb.tls_modify_extensions(m_data->extensions(), Connection_Side::Server, type()); + + hash.update(io.send(*this)); +} + +Server_Hello_12::Server_Hello_12(const std::vector& buf) : + Server_Hello_12(std::make_unique(buf)) {} + +Server_Hello_12::Server_Hello_12(std::unique_ptr data) : Server_Hello_12_Shim(std::move(data)) {} + +bool Server_Hello_12::secure_renegotiation() const { + return m_data->extensions().has(); +} + +std::vector Server_Hello_12::renegotiation_info() const { + if(const Renegotiation_Extension* reneg = m_data->extensions().get()) { + return reneg->renegotiation_info(); + } + return std::vector(); +} + +bool Server_Hello_12::supports_extended_master_secret() const { + return m_data->extensions().has(); +} + +bool Server_Hello_12::supports_encrypt_then_mac() const { + return m_data->extensions().has(); +} + +bool Server_Hello_12::supports_certificate_status_message() const { + return m_data->extensions().has(); +} + +bool Server_Hello_12::supports_session_ticket() const { + return m_data->extensions().has(); +} + +uint16_t Server_Hello_12::srtp_profile() const { + if(auto* srtp = m_data->extensions().get()) { + auto prof = srtp->profiles(); + if(prof.size() != 1 || prof[0] == 0) { + throw Decoding_Error("Server sent malformed DTLS-SRTP extension"); + } + return prof[0]; + } + + return 0; +} + +std::string Server_Hello_12::next_protocol() const { + if(auto* alpn = m_data->extensions().get()) { + return alpn->single_protocol(); + } + return ""; +} + +bool Server_Hello_12::prefers_compressed_ec_points() const { + if(auto* ecc_formats = m_data->extensions().get()) { + return ecc_formats->prefers_compressed(); + } + return false; +} + +/* +* Create a new Server Hello Done message +*/ +Server_Hello_Done::Server_Hello_Done(Handshake_IO& io, Handshake_Hash& hash) { + hash.update(io.send(*this)); +} + +/* +* Deserialize a Server Hello Done message +*/ +Server_Hello_Done::Server_Hello_Done(const std::vector& buf) { + if(!buf.empty()) { + throw Decoding_Error("Server_Hello_Done: Must be empty, and is not"); + } +} + +/* +* Serialize a Server Hello Done message +*/ +std::vector Server_Hello_Done::serialize() const { + return std::vector(); +} + +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_server_kex.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_server_kex.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_server_kex.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_server_kex.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,29 +6,25 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include +#include +#include #include -#include -#include +#include +#include +#include #include +#include #include #include #include #include -#include -#include - -#if defined(BOTAN_HAS_X25519) - #include -#endif -#if defined(BOTAN_HAS_X448) - #include -#endif namespace Botan::TLS { +Server_Key_Exchange::~Server_Key_Exchange() = default; + /** * Create a new Server Key Exchange message */ @@ -42,7 +38,7 @@ const Kex_Algo kex_algo = state.ciphersuite().kex_method(); if(kex_algo == Kex_Algo::PSK || kex_algo == Kex_Algo::ECDHE_PSK) { - std::string identity_hint = creds.psk_identity_hint("tls-server", hostname); + const std::string identity_hint = creds.psk_identity_hint("tls-server", hostname); append_tls_length_value(m_params, identity_hint, 2); } @@ -83,8 +79,8 @@ // `Policy::default_dh_group()` could return a `std::variant`, allowing it to define arbitrary groups. m_kex_key = state.callbacks().tls_generate_ephemeral_key(m_shared_group.value(), rng); - auto dh = dynamic_cast(m_kex_key.get()); - if(!dh) { + auto* dh = dynamic_cast(m_kex_key.get()); + if(dh == nullptr) { throw TLS_Exception(Alert::InternalError, "Application did not provide a Diffie-Hellman key"); } @@ -104,25 +100,19 @@ throw TLS_Exception(Alert::HandshakeFailure, "No shared ECC group with client"); } - std::vector ecdh_public_val; - - if(m_shared_group.value() == Group_Params::X25519 || m_shared_group.value() == Group_Params::X448) { - m_kex_key = state.callbacks().tls_generate_ephemeral_key(m_shared_group.value(), rng); - if(!m_kex_key) { - throw TLS_Exception(Alert::InternalError, "Application did not provide an EC key"); - } - ecdh_public_val = m_kex_key->public_value(); - } else { - m_kex_key = state.callbacks().tls_generate_ephemeral_key(m_shared_group.value(), rng); - auto ecdh = dynamic_cast(m_kex_key.get()); - if(!ecdh) { - throw TLS_Exception(Alert::InternalError, "Application did not provide a EC-Diffie-Hellman key"); + m_kex_key = [&] { + if(m_shared_group->is_ecdh_named_curve()) { + const auto pubkey_point_format = state.client_hello()->prefers_compressed_ec_points() + ? EC_Point_Format::Compressed + : EC_Point_Format::Uncompressed; + return state.callbacks().tls12_generate_ephemeral_ecdh_key(*m_shared_group, rng, pubkey_point_format); + } else { + return state.callbacks().tls_generate_ephemeral_key(*m_shared_group, rng); } + }(); - // follow client's preference for point compression - ecdh_public_val = - ecdh->public_value(state.client_hello()->prefers_compressed_ec_points() ? EC_Point_Format::Compressed - : EC_Point_Format::Uncompressed); + if(!m_kex_key) { + throw TLS_Exception(Alert::InternalError, "Application did not provide an EC key"); } const uint16_t named_curve_id = m_shared_group.value().wire_code(); @@ -130,7 +120,10 @@ m_params.push_back(get_byte<0>(named_curve_id)); m_params.push_back(get_byte<1>(named_curve_id)); - append_tls_length_value(m_params, ecdh_public_val, 1); + // Note: In contrast to public_value(), raw_public_key_bits() takes the + // point format (compressed vs. uncompressed) into account that was set + // in its construction within tls_generate_ephemeral_key(). + append_tls_length_value(m_params, m_kex_key->raw_public_key_bits(), 1); } else if(kex_algo != Kex_Algo::PSK) { throw Internal_Error("Server_Key_Exchange: Unknown kex type " + kex_method_to_string(kex_algo)); } @@ -138,7 +131,8 @@ if(state.ciphersuite().signature_used()) { BOTAN_ASSERT(signing_key, "Signing key was set"); - std::pair format = state.choose_sig_format(*signing_key, m_scheme, false, policy); + const std::pair format = + state.choose_sig_format(*signing_key, m_scheme, false, policy); std::vector buf = state.client_hello()->random(); @@ -189,7 +183,9 @@ if(auth_method != Auth_Method::IMPLICIT) { m_scheme = Signature_Scheme(reader.get_uint16_t()); - m_signature = reader.get_range(2, 0, 65535); + // RFC 5246 4.7: digitally-signed signatures are opaque<1..2^16-1>. + // Matches the parallel check in Certificate_Verify. + m_signature = reader.get_range(2, 1, 65535); } reader.assert_done(); @@ -221,7 +217,7 @@ const Policy& policy) const { policy.check_peer_key_acceptable(server_key); - std::pair format = + const std::pair format = state.parse_sig_format(server_key, m_scheme, state.client_hello()->signature_schemes(), false, policy); std::vector buf = state.client_hello()->random(); diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_session_ticket_12.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_session_ticket_12.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/msg_session_ticket_12.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/msg_session_ticket_12.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,46 @@ +/* +* Session Tickets +* (C) 2012 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include + +namespace Botan::TLS { + +New_Session_Ticket_12::New_Session_Ticket_12(Handshake_IO& io, + Handshake_Hash& hash, + Session_Ticket ticket, + uint32_t lifetime) : + m_ticket_lifetime_hint(lifetime), m_ticket(std::move(ticket)) { + hash.update(io.send(*this)); +} + +New_Session_Ticket_12::New_Session_Ticket_12(Handshake_IO& io, Handshake_Hash& hash) { + hash.update(io.send(*this)); +} + +New_Session_Ticket_12::New_Session_Ticket_12(const std::vector& buf) { + if(buf.size() < 6) { + throw Decoding_Error("Session ticket message too short to be valid"); + } + + TLS_Data_Reader reader("SessionTicket", buf); + + m_ticket_lifetime_hint = reader.get_uint32_t(); + m_ticket = Session_Ticket(reader.get_range(2, 0, 65535)); + reader.assert_done(); +} + +std::vector New_Session_Ticket_12::serialize() const { + auto buf = store_be>(m_ticket_lifetime_hint); + append_tls_length_value(buf, m_ticket.get(), 2); + return buf; +} + +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_cbc/tls_cbc.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_cbc/tls_cbc.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_cbc/tls_cbc.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_cbc/tls_cbc.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,16 +10,20 @@ #include -#include - +#include +#include #include #include +#include +#include #include #include #include namespace Botan::TLS { +TLS_CBC_HMAC_AEAD_Mode::~TLS_CBC_HMAC_AEAD_Mode() = default; + /* * TLS_CBC_HMAC_AEAD_Mode Constructor */ @@ -28,21 +32,20 @@ std::unique_ptr mac, size_t cipher_keylen, size_t mac_keylen, - Protocol_Version version, + const Protocol_Version& version, bool use_encrypt_then_mac) : + m_mac(std::move(mac)), m_cipher_name(cipher->name()), - m_mac_name(mac->name()), + m_mac_name(m_mac->name()), m_cipher_keylen(cipher_keylen), + m_block_size(cipher->block_size()), + m_iv_size(m_block_size), m_mac_keylen(mac_keylen), - m_use_encrypt_then_mac(use_encrypt_then_mac) { - m_tag_size = mac->output_length(); - m_block_size = cipher->block_size(); - - m_iv_size = m_block_size; - - m_is_datagram = version.is_datagram_protocol(); - - m_mac = std::move(mac); + m_tag_size(m_mac->output_length()), + m_use_encrypt_then_mac(use_encrypt_then_mac), + m_is_datagram(version.is_datagram_protocol()) { + BOTAN_ASSERT_NOMSG(m_mac->valid_keylength(m_mac_keylen)); + BOTAN_ASSERT_NOMSG(cipher->valid_keylength(m_cipher_keylen)); auto null_padding = std::make_unique(); if(dir == Cipher_Dir::Encryption) { @@ -135,6 +138,20 @@ m_ad.assign(ad.begin(), ad.end()); } +TLS_CBC_HMAC_AEAD_Encryption::TLS_CBC_HMAC_AEAD_Encryption(std::unique_ptr cipher, + std::unique_ptr mac, + const size_t cipher_keylen, + const size_t mac_keylen, + const Protocol_Version& version, + bool use_encrypt_then_mac) : + TLS_CBC_HMAC_AEAD_Mode(Cipher_Dir::Encryption, + std::move(cipher), + std::move(mac), + cipher_keylen, + mac_keylen, + version, + use_encrypt_then_mac) {} + void TLS_CBC_HMAC_AEAD_Encryption::set_associated_data_n(size_t idx, std::span ad) { TLS_CBC_HMAC_AEAD_Mode::set_associated_data_n(idx, ad); @@ -262,6 +279,20 @@ return pad_invalid.if_not_set_return(pad_bytes); } +TLS_CBC_HMAC_AEAD_Decryption::TLS_CBC_HMAC_AEAD_Decryption(std::unique_ptr cipher, + std::unique_ptr mac, + const size_t cipher_keylen, + const size_t mac_keylen, + const Protocol_Version& version, + bool use_encrypt_then_mac) : + TLS_CBC_HMAC_AEAD_Mode(Cipher_Dir::Decryption, + std::move(cipher), + std::move(mac), + cipher_keylen, + mac_keylen, + version, + use_encrypt_then_mac) {} + void TLS_CBC_HMAC_AEAD_Decryption::cbc_decrypt_record(uint8_t record_contents[], size_t record_len) { if(record_len == 0 || record_len % block_size() != 0) { throw Decoding_Error("Received TLS CBC ciphertext with invalid length"); @@ -323,15 +354,10 @@ * */ void TLS_CBC_HMAC_AEAD_Decryption::perform_additional_compressions(size_t plen, size_t padlen) { - uint16_t block_size; - uint16_t max_bytes_in_first_block; - if(mac().name() == "HMAC(SHA-384)") { - block_size = 128; - max_bytes_in_first_block = 111; - } else { - block_size = 64; - max_bytes_in_first_block = 55; - } + const bool is_sha384 = mac().name() == "HMAC(SHA-384)"; + const uint16_t block_size = is_sha384 ? 128 : 64; + const uint16_t max_bytes_in_first_block = is_sha384 ? 111 : 55; + // number of maximum MACed bytes const uint16_t L1 = static_cast(13 + plen - tag_size()); // number of current MACed bytes (L1 - padlen) diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_cbc/tls_cbc.h botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_cbc/tls_cbc.h --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_cbc/tls_cbc.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_cbc/tls_cbc.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,12 +10,18 @@ #define BOTAN_TLS_CBC_HMAC_AEAD_H_ #include -#include -#include -#include + +namespace Botan { + +class BlockCipher; +class MessageAuthenticationCode; + +} // namespace Botan namespace Botan::TLS { +class Protocol_Version; + /** * TLS CBC+HMAC AEAD base class (GenericBlockCipher in TLS spec) * This is the weird TLS-specific mode, not for general consumption. @@ -44,13 +50,21 @@ bool has_keying_material() const final; + ~TLS_CBC_HMAC_AEAD_Mode() override; + + TLS_CBC_HMAC_AEAD_Mode(const TLS_CBC_HMAC_AEAD_Mode& other) = delete; + TLS_CBC_HMAC_AEAD_Mode(TLS_CBC_HMAC_AEAD_Mode&& other) = delete; + + TLS_CBC_HMAC_AEAD_Mode& operator=(const TLS_CBC_HMAC_AEAD_Mode& other) = delete; + TLS_CBC_HMAC_AEAD_Mode& operator=(TLS_CBC_HMAC_AEAD_Mode&& other) = delete; + protected: TLS_CBC_HMAC_AEAD_Mode(Cipher_Dir direction, std::unique_ptr cipher, std::unique_ptr mac, size_t cipher_keylen, size_t mac_keylen, - Protocol_Version version, + const Protocol_Version& version, bool use_encrypt_then_mac); size_t cipher_keylen() const { return m_cipher_keylen; } @@ -67,10 +81,7 @@ Cipher_Mode& cbc() const { return *m_cbc; } - MessageAuthenticationCode& mac() const { - BOTAN_ASSERT_NONNULL(m_mac); - return *m_mac; - } + MessageAuthenticationCode& mac() const { return *m_mac; } secure_vector& cbc_state() { return m_cbc_state; } @@ -86,19 +97,19 @@ void key_schedule(std::span key) final; + std::unique_ptr m_cbc; + std::unique_ptr m_mac; + const std::string m_cipher_name; const std::string m_mac_name; size_t m_cipher_keylen; - size_t m_mac_keylen; + size_t m_block_size; size_t m_iv_size; + size_t m_mac_keylen; size_t m_tag_size; - size_t m_block_size; bool m_use_encrypt_then_mac; bool m_is_datagram; - std::unique_ptr m_cbc; - std::unique_ptr m_mac; - secure_vector m_cbc_state; std::vector m_ad; secure_vector m_msg; @@ -113,17 +124,10 @@ */ TLS_CBC_HMAC_AEAD_Encryption(std::unique_ptr cipher, std::unique_ptr mac, - const size_t cipher_keylen, - const size_t mac_keylen, - const Protocol_Version version, - bool use_encrypt_then_mac) : - TLS_CBC_HMAC_AEAD_Mode(Cipher_Dir::Encryption, - std::move(cipher), - std::move(mac), - cipher_keylen, - mac_keylen, - version, - use_encrypt_then_mac) {} + size_t cipher_keylen, + size_t mac_keylen, + const Protocol_Version& version, + bool use_encrypt_then_mac); void set_associated_data_n(size_t idx, std::span ad) override; @@ -145,17 +149,10 @@ */ TLS_CBC_HMAC_AEAD_Decryption(std::unique_ptr cipher, std::unique_ptr mac, - const size_t cipher_keylen, - const size_t mac_keylen, - const Protocol_Version version, - bool use_encrypt_then_mac) : - TLS_CBC_HMAC_AEAD_Mode(Cipher_Dir::Decryption, - std::move(cipher), - std::move(mac), - cipher_keylen, - mac_keylen, - version, - use_encrypt_then_mac) {} + size_t cipher_keylen, + size_t mac_keylen, + const Protocol_Version& version, + bool use_encrypt_then_mac); size_t output_length(size_t input_length) const override; diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_channel_impl_12.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_channel_impl_12.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_channel_impl_12.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_channel_impl_12.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,10 +9,14 @@ #include #include -#include +#include +#include #include #include +#include +#include #include +#include #include #include #include @@ -66,7 +70,7 @@ m_read_cipher_states[0] = nullptr; if(m_sequence_numbers) { - m_sequence_numbers->reset(); + m_sequence_numbers->reset(); // NOLINT(*-ambiguous-smartptr-reset-call) } } @@ -94,28 +98,29 @@ } std::vector Channel_Impl_12::peer_cert_chain() const { - if(auto active = active_state()) { - return get_peer_cert_chain(*active); + if(m_active_state.has_value()) { + return m_active_state->peer_certs(); } return std::vector(); } std::optional Channel_Impl_12::external_psk_identity() const { - const auto* state = (active_state() != nullptr) ? active_state() : pending_state(); - if(state) { + if(m_active_state.has_value()) { + return m_active_state->psk_identity(); + } + if(const auto* state = pending_state()) { return state->psk_identity(); - } else { - return std::nullopt; } + return std::nullopt; } Handshake_State& Channel_Impl_12::create_handshake_state(Protocol_Version version) { - if(pending_state()) { + if(pending_state() != nullptr) { throw Internal_Error("create_handshake_state called during handshake"); } - if(auto active = active_state()) { - Protocol_Version active_version = active->version(); + if(m_active_state.has_value()) { + const Protocol_Version active_version = m_active_state->version(); if(active_version.is_datagram_protocol() != version.is_datagram_protocol()) { throw TLS_Exception(Alert::ProtocolVersion, @@ -136,20 +141,30 @@ std::unique_ptr io; if(version.is_datagram_protocol()) { - io = - std::make_unique(std::bind(&Channel_Impl_12::send_record_under_epoch, this, _1, _2, _3), - sequence_numbers(), - static_cast(policy().dtls_default_mtu()), - policy().dtls_initial_timeout(), - policy().dtls_maximum_timeout()); + const uint16_t mtu = static_cast(policy().dtls_default_mtu()); + const size_t initial_timeout_ms = policy().dtls_initial_timeout(); + const size_t max_timeout_ms = policy().dtls_maximum_timeout(); + + auto send_record_f = [this](uint16_t epoch, Record_Type record_type, const std::vector& record) { + send_record_under_epoch(epoch, record_type, record); + }; + io = std::make_unique(send_record_f, + sequence_numbers(), + mtu, + initial_timeout_ms, + max_timeout_ms, + policy().maximum_handshake_message_size()); } else { - io = std::make_unique(std::bind(&Channel_Impl_12::send_record, this, _1, _2)); + auto send_record_f = [this](Record_Type rec_type, const std::vector& record) { + send_record(rec_type, record); + }; + io = std::make_unique(send_record_f); } m_pending_state = new_handshake_state(std::move(io)); - if(auto active = active_state()) { - m_pending_state->set_version(active->version()); + if(m_active_state.has_value()) { + m_pending_state->set_version(m_active_state->version()); } return *m_pending_state; @@ -165,27 +180,27 @@ } void Channel_Impl_12::renegotiate(bool force_full_renegotiation) { - if(pending_state()) { // currently in handshake? + if(pending_state() != nullptr) { // currently in handshake? return; } - if(auto active = active_state()) { - if(force_full_renegotiation == false) { + if(m_active_state.has_value()) { + if(!force_full_renegotiation) { force_full_renegotiation = !policy().allow_resumption_for_renegotiation(); } - initiate_handshake(create_handshake_state(active->version()), force_full_renegotiation); + initiate_handshake(create_handshake_state(m_active_state->version()), force_full_renegotiation); } else { throw Invalid_State("Cannot renegotiate on inactive connection"); } } -void Channel_Impl_12::update_traffic_keys(bool) { +void Channel_Impl_12::update_traffic_keys(bool /*update_requested*/) { throw Invalid_Argument("cannot update traffic keys on a TLS 1.2 channel"); } void Channel_Impl_12::change_cipher_spec_reader(Connection_Side side) { - auto pending = pending_state(); + const auto* pending = pending_state(); BOTAN_ASSERT(pending && pending->server_hello(), "Have received server hello"); @@ -200,19 +215,19 @@ BOTAN_ASSERT(!m_read_cipher_states.contains(epoch), "No read cipher state currently set for next epoch"); // flip side as we are reading - std::shared_ptr read_state( - new Connection_Cipher_State(pending->version(), - (side == Connection_Side::Client) ? Connection_Side::Server : Connection_Side::Client, - false, - pending->ciphersuite(), - pending->session_keys(), - pending->server_hello()->supports_encrypt_then_mac())); + auto read_state = std::make_shared( + pending->version(), + (side == Connection_Side::Client) ? Connection_Side::Server : Connection_Side::Client, + false, + pending->ciphersuite(), + pending->session_keys(), + pending->server_hello()->supports_encrypt_then_mac()); m_read_cipher_states[epoch] = read_state; } void Channel_Impl_12::change_cipher_spec_writer(Connection_Side side) { - auto pending = pending_state(); + const auto* pending = pending_state(); BOTAN_ASSERT(pending && pending->server_hello(), "Have received server hello"); @@ -226,19 +241,18 @@ BOTAN_ASSERT(!m_write_cipher_states.contains(epoch), "No write cipher state currently set for next epoch"); - std::shared_ptr write_state( - new Connection_Cipher_State(pending->version(), - side, - true, - pending->ciphersuite(), - pending->session_keys(), - pending->server_hello()->supports_encrypt_then_mac())); + auto write_state = std::make_shared(pending->version(), + side, + true, + pending->ciphersuite(), + pending->session_keys(), + pending->server_hello()->supports_encrypt_then_mac()); m_write_cipher_states[epoch] = write_state; } bool Channel_Impl_12::is_handshake_complete() const { - return (active_state() != nullptr); + return m_active_state.has_value(); } bool Channel_Impl_12::is_active() const { @@ -250,10 +264,11 @@ } void Channel_Impl_12::activate_session() { - std::swap(m_active_state, m_pending_state); - m_pending_state.reset(); + BOTAN_ASSERT_NONNULL(m_pending_state); + + const auto& state = *m_pending_state; - if(!m_active_state->version().is_datagram_protocol()) { + if(!state.version().is_datagram_protocol()) { // TLS is easy just remove all but the current state const uint16_t current_epoch = sequence_numbers().current_write_epoch(); @@ -263,17 +278,26 @@ map_remove_if(not_current_epoch, m_read_cipher_states); } + // For DTLS, keep the handshake IO for last-flight retransmission. + if(m_is_datagram) { + m_active_state = Active_Connection_State_12(state, application_protocol(), m_pending_state->take_handshake_io()); + } else { + m_active_state = Active_Connection_State_12(state, application_protocol()); + } + + m_pending_state.reset(); + callbacks().tls_session_activated(); } size_t Channel_Impl_12::from_peer(std::span data) { const bool allow_epoch0_restart = m_is_datagram && m_is_server && policy().allow_dtls_epoch0_restart(); - auto input = data.data(); + const auto* input = data.data(); auto input_size = data.size(); try { - while(input_size) { + while(input_size > 0) { size_t consumed = 0; auto get_epoch = [this](uint16_t epoch) { return read_cipher_state_epoch(epoch); }; @@ -312,13 +336,13 @@ throw TLS_Exception(Alert::RecordOverflow, "TLS plaintext record is larger than allowed maximum"); } - const bool epoch0_restart = m_is_datagram && record.epoch() == 0 && active_state(); + const bool epoch0_restart = m_is_datagram && record.epoch() == 0 && m_active_state.has_value(); BOTAN_ASSERT_IMPLICATION(epoch0_restart, allow_epoch0_restart, "Allowed state"); - const bool initial_record = epoch0_restart || (!pending_state() && !active_state()); + const bool initial_record = epoch0_restart || (pending_state() == nullptr && !m_active_state.has_value()); bool initial_handshake_message = false; if(record.type() == Record_Type::Handshake && !m_record_buf.empty()) { - Handshake_Type type = static_cast(m_record_buf[0]); + const Handshake_Type type = static_cast(m_record_buf[0]); initial_handshake_message = (type == Handshake_Type::ClientHello); } @@ -328,13 +352,13 @@ if(record.version().major_version() != 3 && record.version().major_version() != 0xFE) { throw TLS_Exception(Alert::ProtocolVersion, "Received unexpected record version in initial record"); } - } else if(auto pending = pending_state()) { + } else if(const auto* pending = pending_state()) { if(pending->server_hello() != nullptr && !initial_handshake_message && record.version() != pending->version()) { throw TLS_Exception(Alert::ProtocolVersion, "Received unexpected record version"); } - } else if(auto active = active_state()) { - if(record.version() != active->version() && !initial_handshake_message) { + } else if(m_active_state.has_value()) { + if(record.version() != m_active_state->version() && !initial_handshake_message) { throw TLS_Exception(Alert::ProtocolVersion, "Received unexpected record version"); } } @@ -394,11 +418,14 @@ const uint16_t epoch = record_sequence >> 48; - if(epoch == sequence_numbers().current_read_epoch()) { + const uint16_t current_epoch = sequence_numbers().current_read_epoch(); + if(epoch == current_epoch) { create_handshake_state(record_version); - } else if(epoch == sequence_numbers().current_read_epoch() - 1) { - BOTAN_ASSERT(m_active_state, "Have active state here"); - m_active_state->handshake_io().add_record(record.data(), record.size(), record_type, record_sequence); + } else if(current_epoch > 0 && epoch == current_epoch - 1) { + BOTAN_ASSERT(m_active_state.has_value() && m_active_state->dtls_handshake_io(), + "Have DTLS handshake IO for retransmission"); + m_active_state->dtls_handshake_io()->add_record( + record.data(), record.size(), record_type, record_sequence); } } else { create_handshake_state(record_version); @@ -412,14 +439,14 @@ if(m_pending_state) { m_pending_state->handshake_io().add_record(record.data(), record.size(), record_type, record_sequence); - while(auto pending = m_pending_state.get()) { - auto msg = pending->get_next_handshake_msg(); + while(auto* pending = m_pending_state.get()) { + auto msg = pending->get_next_handshake_msg(policy().maximum_handshake_message_size()); if(msg.first == Handshake_Type::None) { // no full handshake yet break; } - process_handshake_msg(active_state(), *pending, msg.first, msg.second, epoch0_restart); + process_handshake_msg(*pending, msg.first, msg.second, epoch0_restart); if(!m_pending_state) { break; @@ -429,28 +456,38 @@ } void Channel_Impl_12::process_application_data(uint64_t seq_no, const secure_vector& record) { - if(!active_state()) { + if(!m_active_state.has_value()) { throw Unexpected_Message("Application data before handshake done"); } + // ApplicationData must arrive under a non-zero read epoch + const uint16_t read_epoch = + m_is_datagram ? static_cast(seq_no >> 48) : sequence_numbers().current_read_epoch(); + if(read_epoch == 0) { + throw Unexpected_Message("Application data received in unexpected read epoch"); + } + callbacks().tls_record_received(seq_no, record); } void Channel_Impl_12::process_alert(const secure_vector& record) { - Alert alert_msg(record); + const Alert alert_msg(record); - if(alert_msg.type() == Alert::NoRenegotiation) { + // RFC 5246 7.2.2: + // no_renegotiation + // Sent by the client in response to a hello request or by the + // server in response to a client hello after initial handshaking. + if(alert_msg.type() == Alert::NoRenegotiation && m_active_state.has_value()) { m_pending_state.reset(); } callbacks().tls_alert(alert_msg); - if(alert_msg.is_fatal()) { - if(auto active = active_state()) { - const auto& session_id = active->server_hello()->session_id(); - if(!session_id.empty()) { - session_manager().remove(session_id); - } + // If the alert is fatal on an active session, prevent later resumptions + if(alert_msg.is_fatal() && m_active_state.has_value()) { + const auto& sid = m_active_state->session_id(); + if(!sid.empty()) { + session_manager().remove(Session_Handle(sid)); } } @@ -471,10 +508,9 @@ Record_Type record_type, const uint8_t input[], size_t length) { - BOTAN_ASSERT(m_pending_state || m_active_state, "Some connection state exists"); + BOTAN_ASSERT(m_pending_state || m_active_state.has_value(), "Some connection state exists"); - const Protocol_Version record_version = - (m_pending_state) ? (m_pending_state->version()) : (m_active_state->version()); + const Protocol_Version record_version = (m_pending_state) ? (m_pending_state->version()) : m_active_state->version(); const uint64_t next_seq = sequence_numbers().next_write_sequence(epoch); @@ -494,7 +530,7 @@ auto cipher_state = write_cipher_state_epoch(epoch); - while(length) { + while(length > 0) { const size_t sending = std::min(length, MAX_PLAINTEXT_SIZE); write_record(cipher_state.get(), epoch, type, input, sending); @@ -530,15 +566,15 @@ } } - if(alert.type() == Alert::NoRenegotiation) { + if(alert.type() == Alert::NoRenegotiation && m_active_state.has_value()) { m_pending_state.reset(); } if(alert.is_fatal()) { - if(auto active = active_state()) { - const auto& session_id = active->server_hello()->session_id(); - if(!session_id.empty()) { - session_manager().remove(Session_ID(session_id)); + if(m_active_state.has_value()) { + const auto& sid = m_active_state->session_id(); + if(!sid.empty()) { + session_manager().remove(Session_Handle(sid)); } } reset_state(); @@ -550,69 +586,63 @@ } void Channel_Impl_12::secure_renegotiation_check(const Client_Hello_12* client_hello) { + BOTAN_ASSERT_NONNULL(client_hello); const bool secure_renegotiation = client_hello->secure_renegotiation(); - if(auto active = active_state()) { - const bool active_sr = active->client_hello()->secure_renegotiation(); - - if(active_sr != secure_renegotiation) { - throw TLS_Exception(Alert::HandshakeFailure, "Client changed its mind about secure renegotiation"); - } + if(m_active_state && m_active_state->client_supports_secure_renegotiation() != secure_renegotiation) { + throw TLS_Exception(Alert::HandshakeFailure, "Client changed its mind about secure renegotiation"); } if(secure_renegotiation) { const std::vector& data = client_hello->renegotiation_info(); - if(data != secure_renegotiation_data_for_client_hello()) { + const auto expected = secure_renegotiation_data_for_client_hello(); + if(!CT::is_equal(data, expected).as_bool()) { throw TLS_Exception(Alert::HandshakeFailure, "Client sent bad values for secure renegotiation"); } } } void Channel_Impl_12::secure_renegotiation_check(const Server_Hello_12* server_hello) { + BOTAN_ASSERT_NONNULL(server_hello); const bool secure_renegotiation = server_hello->secure_renegotiation(); - if(auto active = active_state()) { - const bool active_sr = active->server_hello()->secure_renegotiation(); - - if(active_sr != secure_renegotiation) { - throw TLS_Exception(Alert::HandshakeFailure, "Server changed its mind about secure renegotiation"); - } + if(m_active_state && m_active_state->server_supports_secure_renegotiation() != secure_renegotiation) { + throw TLS_Exception(Alert::HandshakeFailure, "Server changed its mind about secure renegotiation"); } if(secure_renegotiation) { const std::vector& data = server_hello->renegotiation_info(); - if(data != secure_renegotiation_data_for_server_hello()) { + const auto expected = secure_renegotiation_data_for_server_hello(); + if(!CT::is_equal(data, expected).as_bool()) { throw TLS_Exception(Alert::HandshakeFailure, "Server sent bad values for secure renegotiation"); } } } std::vector Channel_Impl_12::secure_renegotiation_data_for_client_hello() const { - if(auto active = active_state()) { - return active->client_finished()->verify_data(); + if(m_active_state.has_value()) { + return m_active_state->client_finished_verify_data(); } return std::vector(); } std::vector Channel_Impl_12::secure_renegotiation_data_for_server_hello() const { - if(auto active = active_state()) { - std::vector buf = active->client_finished()->verify_data(); - buf += active->server_finished()->verify_data(); - return buf; + if(m_active_state.has_value()) { + return concat(m_active_state->client_finished_verify_data(), m_active_state->server_finished_verify_data()); + } else { + return {}; } - - return std::vector(); } bool Channel_Impl_12::secure_renegotiation_supported() const { - if(auto active = active_state()) { - return active->server_hello()->secure_renegotiation(); + if(m_active_state.has_value()) { + return m_active_state->server_supports_secure_renegotiation(); } - if(auto pending = pending_state()) { - if(auto hello = pending->server_hello()) { + if(const auto* pending = pending_state()) { + if(const auto* hello = pending->server_hello()) { return hello->secure_renegotiation(); } } @@ -623,33 +653,28 @@ SymmetricKey Channel_Impl_12::key_material_export(std::string_view label, std::string_view context, size_t length) const { - if(auto active = active_state()) { - if(pending_state() != nullptr) { - throw Invalid_State("Channel_Impl_12::key_material_export cannot export during renegotiation"); - } - - auto prf = active->protocol_specific_prf(); + if(!m_active_state.has_value()) { + throw Invalid_State("Channel_Impl_12::key_material_export connection not active"); + } - const secure_vector& master_secret = active->session_keys().master_secret(); + if(pending_state() != nullptr) { + throw Invalid_State("Channel_Impl_12::key_material_export cannot export during renegotiation"); + } - std::vector salt; - salt += active->client_hello()->random(); - salt += active->server_hello()->random(); + auto prf = callbacks().tls12_protocol_specific_kdf(m_active_state->prf_algo()); - if(!context.empty()) { - size_t context_size = context.length(); - if(context_size > 0xFFFF) { - throw Invalid_Argument("key_material_export context is too long"); - } - salt.push_back(get_byte<0>(static_cast(context_size))); - salt.push_back(get_byte<1>(static_cast(context_size))); - salt += to_byte_vector(context); + const auto salt = [&] { + if(context.empty()) { + return concat(m_active_state->client_random(), m_active_state->server_random()); + } else { + return concat(m_active_state->client_random(), + m_active_state->server_random(), + store_be(static_cast(context.size())), + as_span_of_bytes(context)); } + }(); - return SymmetricKey(prf->derive_key(length, master_secret, salt, to_byte_vector(label))); - } else { - throw Invalid_State("Channel_Impl_12::key_material_export connection not active"); - } + return SymmetricKey(prf->derive_key(length, m_active_state->master_secret(), salt, as_span_of_bytes(label))); } } // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_channel_impl_12.h botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_channel_impl_12.h --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_channel_impl_12.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_channel_impl_12.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,11 +10,9 @@ #define BOTAN_TLS_CHANNEL_IMPL_12_H_ #include -#include -#include #include #include -#include +#include #include #include #include @@ -26,6 +24,7 @@ namespace TLS { +class Callbacks; class Connection_Cipher_State; class Connection_Sequence_Numbers; class Handshake_State; @@ -39,12 +38,6 @@ */ class Channel_Impl_12 : public Channel_Impl { public: - typedef std::function output_fn; - typedef std::function data_cb; - typedef std::function alert_cb; - typedef std::function handshake_cb; - typedef std::function handshake_msg_cb; - /** * Set up a new TLS session * @@ -67,9 +60,10 @@ bool is_datagram, size_t io_buf_sz = TLS::Channel::IO_BUF_DEFAULT_SIZE); - explicit Channel_Impl_12(const Channel_Impl_12&) = delete; - - Channel_Impl_12& operator=(const Channel_Impl_12&) = delete; + Channel_Impl_12(const Channel_Impl_12& other) = delete; + Channel_Impl_12(Channel_Impl_12&& other) = delete; + Channel_Impl_12& operator=(const Channel_Impl_12& other) = delete; + Channel_Impl_12& operator=(Channel_Impl_12&& other) = delete; ~Channel_Impl_12() override; @@ -156,8 +150,9 @@ bool timeout_check() override; protected: - virtual void process_handshake_msg(const Handshake_State* active_state, - Handshake_State& pending_state, + const std::optional& active_state() const { return m_active_state; } + + virtual void process_handshake_msg(Handshake_State& pending_state, Handshake_Type type, const std::vector& contents, bool epoch0_restart) = 0; @@ -193,8 +188,6 @@ virtual void initiate_handshake(Handshake_State& state, bool force_full_renegotiation) = 0; - virtual std::vector get_peer_cert_chain(const Handshake_State& state) const = 0; - private: void send_record(Record_Type record_type, const std::vector& record); @@ -213,8 +206,6 @@ std::shared_ptr write_cipher_state_epoch(uint16_t epoch) const; - const Handshake_State* active_state() const { return m_active_state.get(); } - const Handshake_State* pending_state() const { return m_pending_state.get(); } /* methods to handle incoming traffic through Channel_Impl_12::receive_data. */ @@ -242,8 +233,7 @@ /* sequence number state */ std::unique_ptr m_sequence_numbers; - /* pending and active connection states */ - std::unique_ptr m_active_state; + /* pending handshake state (null when no handshake is in progress) */ std::unique_ptr m_pending_state; /* cipher states for each epoch */ @@ -256,6 +246,8 @@ secure_vector m_record_buf; bool m_has_been_closed; + + std::optional m_active_state; }; } // namespace TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_client_impl_12.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_client_impl_12.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_client_impl_12.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_client_impl_12.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,11 +10,12 @@ #include #include -#include -#include +#include +#include +#include #include #include - +#include #include #include #include @@ -68,6 +69,21 @@ return m_resumed_session->supports_extended_master_secret(); } + uint16_t resumed_session_ciphersuite_code() const { + BOTAN_STATE_CHECK(is_a_resumption()); + return m_resumed_session->ciphersuite_code(); + } + + std::vector peer_cert_chain() const override { + if(is_a_resumption()) { + return resume_peer_certs(); + } + if(server_certs() != nullptr) { + return server_certs()->cert_chain(); + } + return {}; + } + private: std::unique_ptr m_server_public_key; @@ -114,11 +130,11 @@ if(!downgrade_info.client_hello_message.empty()) { // Downgrade detected after receiving a TLS 1.2 server hello. We need to // recreate the state as if this implementation issued the client hello. - std::vector client_hello_msg( + const std::vector client_hello_msg( downgrade_info.client_hello_message.begin() + 4 /* handshake header length */, downgrade_info.client_hello_message.end()); - state.client_hello(new Client_Hello_12(client_hello_msg)); + state.client_hello(std::make_unique(client_hello_msg)); state.hash().update(downgrade_info.client_hello_message); secure_renegotiation_check(state.client_hello()); @@ -140,19 +156,6 @@ return std::make_unique(std::move(io), callbacks()); } -std::vector Client_Impl_12::get_peer_cert_chain(const Handshake_State& state) const { - const Client_Handshake_State_12& cstate = dynamic_cast(state); - - if(cstate.is_a_resumption()) { - return cstate.resume_peer_certs(); - } - - if(state.server_certs()) { - return state.server_certs()->cert_chain(); - } - return std::vector(); -} - /* * Send a new client hello to renegotiate */ @@ -195,31 +198,31 @@ const bool session_version_ok = policy().only_resume_with_exact_version() ? exact_version : ok_version; if(policy().acceptable_ciphersuite(session_info.ciphersuite()) && session_version_ok) { - state.client_hello(new Client_Hello_12(state.handshake_io(), - state.hash(), - policy(), - callbacks(), - rng(), - secure_renegotiation_data_for_client_hello(), - session_and_handle.value(), - next_protocols)); + state.client_hello(std::make_unique(state.handshake_io(), + state.hash(), + policy(), + callbacks(), + rng(), + secure_renegotiation_data_for_client_hello(), + session_and_handle.value(), + next_protocols)); state.record_resumption_info(std::move(session_info)); } } } - if(!state.client_hello()) { + if(state.client_hello() == nullptr) { // not resuming - Client_Hello_12::Settings client_settings(version, m_info.hostname()); - state.client_hello(new Client_Hello_12(state.handshake_io(), - state.hash(), - policy(), - callbacks(), - rng(), - secure_renegotiation_data_for_client_hello(), - client_settings, - next_protocols)); + const Client_Hello_12::Settings client_settings(version, m_info.hostname()); + state.client_hello(std::make_unique(state.handshake_io(), + state.hash(), + policy(), + callbacks(), + rng(), + secure_renegotiation_data_for_client_hello(), + client_settings, + next_protocols)); } secure_renegotiation_check(state.client_hello()); @@ -244,8 +247,7 @@ /* * Process a handshake message */ -void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state, - Handshake_State& state_base, +void Client_Impl_12::process_handshake_msg(Handshake_State& state_base, Handshake_Type type, const std::vector& contents, bool epoch0_restart) { @@ -253,10 +255,10 @@ Client_Handshake_State_12& state = dynamic_cast(state_base); - if(type == Handshake_Type::HelloRequest && active_state) { - Hello_Request hello_request(contents); + if(type == Handshake_Type::HelloRequest && active_state().has_value()) { + const Hello_Request hello_request(contents); - if(state.client_hello()) { + if(state.client_hello() != nullptr) { throw TLS_Exception(Alert::HandshakeFailure, "Cannot renegotiate during a handshake"); } @@ -290,10 +292,10 @@ state.set_expected_next(Handshake_Type::ServerHello); state.set_expected_next(Handshake_Type::HelloVerifyRequest); // might get it again - Hello_Verify_Request hello_verify_request(contents); + const Hello_Verify_Request hello_verify_request(contents); state.hello_verify_request(hello_verify_request); } else if(type == Handshake_Type::ServerHello) { - state.server_hello(new Server_Hello_12(contents)); + state.server_hello(std::make_unique(contents)); if(!state.server_hello()->legacy_version().valid()) { throw TLS_Exception(Alert::ProtocolVersion, "Server replied with an invalid version"); @@ -362,7 +364,7 @@ throw TLS_Exception(Alert::UnsupportedExtension, msg.str()); } - if(uint16_t srtp = state.server_hello()->srtp_profile()) { + if(const uint16_t srtp = state.server_hello()->srtp_profile()) { if(!value_exists(state.client_hello()->srtp_profiles(), srtp)) { throw TLS_Exception(Alert::HandshakeFailure, "Server replied with DTLS-SRTP alg we did not send"); } @@ -372,10 +374,27 @@ state.server_hello()->extensions(), Connection_Side::Server, Handshake_Type::ServerHello); state.set_version(state.server_hello()->legacy_version()); + + if(state.server_hello()->extensions().has()) { + const auto* server_alpn = state.server_hello()->extensions().get(); + const auto selected = server_alpn->single_protocol(); + const auto* client_alpn = state.client_hello()->extensions().get(); + BOTAN_ASSERT_NONNULL(client_alpn); + const auto& offered = client_alpn->protocols(); + if(!value_exists(offered, selected)) { + throw TLS_Exception(Alert::IllegalParameter, "Server selected an ALPN protocol not offered by the client"); + } + } m_application_protocol = state.server_hello()->next_protocol(); secure_renegotiation_check(state.server_hello()); + // RFC 7627 / RFC 9325 4.4: optionally require Extended Master Secret. + if(policy().require_extended_master_secret() && !state.server_hello()->supports_extended_master_secret()) { + throw TLS_Exception(Alert::HandshakeFailure, + "Policy requires the Extended Master Secret extension but the server did not send it"); + } + const bool server_returned_same_session_id = !state.server_hello()->session_id().empty() && (state.server_hello()->session_id() == state.client_hello()->session_id()); @@ -392,6 +411,12 @@ throw TLS_Exception(Alert::HandshakeFailure, "Server resumed session but with wrong version"); } + // RFC 5246 7.4.1.2: when resuming a session, the server MUST use + // the same cipher suite that was negotiated in the original session. + if(state.server_hello()->ciphersuite() != state.resumed_session_ciphersuite_code()) { + throw TLS_Exception(Alert::HandshakeFailure, "Server resumed session with a different ciphersuite"); + } + if(state.server_hello()->supports_extended_master_secret() && !state.resumed_session_supports_extended_master_secret()) { throw TLS_Exception(Alert::HandshakeFailure, "Server resumed session but added extended master secret"); @@ -420,17 +445,17 @@ } else { // new session - if(active_state) { - // Here we are testing things that should not change during a renegotation, - // even if the server creates a new session. Howerver they might change + if(active_state().has_value()) { + // Here we are testing things that should not change during a renegotiation, + // even if the server creates a new session. However they might change // in a resumption scenario. - if(active_state->version() != state.server_hello()->legacy_version()) { + if(active_state()->version() != state.server_hello()->legacy_version()) { throw TLS_Exception(Alert::ProtocolVersion, "Server changed version after renegotiation"); } if(state.server_hello()->supports_extended_master_secret() != - active_state->server_hello()->supports_extended_master_secret()) { + active_state()->supports_extended_master_secret()) { throw TLS_Exception(Alert::HandshakeFailure, "Server changed its mind about extended master secret"); } } @@ -455,7 +480,7 @@ "Server version " + state.version().to_string() + " is unacceptable by policy"); } - if(state.ciphersuite().signature_used() || state.ciphersuite().kex_method() == Kex_Algo::STATIC_RSA) { + if(state.ciphersuite().is_certificate_required()) { state.set_expected_next(Handshake_Type::Certificate); } else if(state.ciphersuite().kex_method() == Kex_Algo::PSK) { /* PSK is anonymous so no certificate/cert req message is @@ -468,15 +493,17 @@ state.set_expected_next(Handshake_Type::ServerKeyExchange); state.set_expected_next(Handshake_Type::ServerHelloDone); - } else if(state.ciphersuite().kex_method() != Kex_Algo::STATIC_RSA) { - state.set_expected_next(Handshake_Type::ServerKeyExchange); } else { - state.set_expected_next(Handshake_Type::CertificateRequest); // optional - state.set_expected_next(Handshake_Type::ServerHelloDone); + // ECDHE_PSK ServerKeyExchange carries the ECDH parameters and + // immediately follows ServerHello. + // + // Suites using RSA key exchange or signature-authenticated ECDH + // were already routed to expect Certificate above. + state.set_expected_next(Handshake_Type::ServerKeyExchange); } } } else if(type == Handshake_Type::Certificate) { - state.server_certs(new Certificate_12(contents, policy())); + state.server_certs(std::make_unique(contents, policy())); const std::vector& server_certs = state.server_certs()->cert_chain(); @@ -490,10 +517,10 @@ in case an OCSP response was also available */ - X509_Certificate server_cert = server_certs[0]; + const X509_Certificate server_cert = server_certs[0]; - if(active_state && active_state->server_certs()) { - X509_Certificate current_cert = active_state->server_certs()->cert_chain().at(0); + if(active_state().has_value() && !active_state()->peer_certs().empty()) { + const X509_Certificate& current_cert = active_state()->peer_certs().at(0); if(current_cert != server_cert) { throw TLS_Exception(Alert::BadCertificate, "Server certificate changed during renegotiation"); @@ -537,7 +564,7 @@ } } } else if(type == Handshake_Type::CertificateStatus) { - state.server_cert_status(new Certificate_Status(contents, Connection_Side::Server)); + state.server_cert_status(std::make_unique(contents, Connection_Side::Server)); if(state.ciphersuite().kex_method() != Kex_Algo::STATIC_RSA) { state.set_expected_next(Handshake_Type::ServerKeyExchange); @@ -546,12 +573,12 @@ state.set_expected_next(Handshake_Type::ServerHelloDone); } } else if(type == Handshake_Type::ServerKeyExchange) { - if(state.ciphersuite().psk_ciphersuite() == false) { + if(!state.ciphersuite().psk_ciphersuite()) { state.set_expected_next(Handshake_Type::CertificateRequest); // optional } state.set_expected_next(Handshake_Type::ServerHelloDone); - state.server_kex(new Server_Key_Exchange( + state.server_kex(std::make_unique( contents, state.ciphersuite().kex_method(), state.ciphersuite().auth_method(), state.version())); if(state.ciphersuite().signature_used()) { @@ -563,9 +590,9 @@ } } else if(type == Handshake_Type::CertificateRequest) { state.set_expected_next(Handshake_Type::ServerHelloDone); - state.cert_req(new Certificate_Request_12(contents)); + state.cert_req(std::make_unique(contents)); } else if(type == Handshake_Type::ServerHelloDone) { - state.server_hello_done(new Server_Hello_Done(contents)); + state.server_hello_done(std::make_unique(contents)); if(state.handshake_io().have_more_data()) { throw TLS_Exception(Alert::UnexpectedMessage, "Have data remaining in buffer after ServerHelloDone"); @@ -596,13 +623,13 @@ if(state.received_handshake_msg(Handshake_Type::CertificateRequest)) { const auto& types = state.cert_req()->acceptable_cert_types(); - std::vector client_certs = + const std::vector client_certs = m_creds->find_cert_chain(types, {}, state.cert_req()->acceptable_CAs(), "tls-client", m_info.hostname()); - state.client_certs(new Certificate_12(state.handshake_io(), state.hash(), client_certs)); + state.client_certs(std::make_unique(state.handshake_io(), state.hash(), client_certs)); } - state.client_kex(new Client_Key_Exchange( + state.client_kex(std::make_unique( state.handshake_io(), state, policy(), *m_creds, state.maybe_server_public_key(), m_info.hostname(), rng())); state.compute_session_keys(); @@ -624,14 +651,14 @@ } state.client_verify( - new Certificate_Verify_12(state.handshake_io(), state, policy(), rng(), private_key.get())); + std::make_unique(state.handshake_io(), state, policy(), rng(), private_key.get())); } state.handshake_io().send(Change_Cipher_Spec()); change_cipher_spec_writer(Connection_Side::Client); - state.client_finished(new Finished_12(state.handshake_io(), state, Connection_Side::Client)); + state.client_finished(std::make_unique(state.handshake_io(), state, Connection_Side::Client)); if(state.server_hello()->supports_session_ticket()) { state.set_expected_next(Handshake_Type::NewSessionTicket); @@ -639,7 +666,7 @@ state.set_expected_next(Handshake_Type::HandshakeCCS); } } else if(type == Handshake_Type::NewSessionTicket) { - state.new_session_ticket(new New_Session_Ticket_12(contents)); + state.new_session_ticket(std::make_unique(contents)); state.set_expected_next(Handshake_Type::HandshakeCCS); } else if(type == Handshake_Type::HandshakeCCS) { @@ -651,7 +678,7 @@ throw TLS_Exception(Alert::UnexpectedMessage, "Have data remaining in buffer after Finished"); } - state.server_finished(new Finished_12(contents)); + state.server_finished(std::make_unique(contents)); if(!state.server_finished()->verify(state, Connection_Side::Server)) { throw TLS_Exception(Alert::DecryptError, "Finished message didn't verify"); @@ -659,40 +686,56 @@ state.hash().update(state.handshake_io().format(contents, type)); - if(!state.client_finished()) { + if(state.client_finished() == nullptr) { // session resume case state.handshake_io().send(Change_Cipher_Spec()); change_cipher_spec_writer(Connection_Side::Client); - state.client_finished(new Finished_12(state.handshake_io(), state, Connection_Side::Client)); + state.client_finished(std::make_unique(state.handshake_io(), state, Connection_Side::Client)); } + // Session Tickets (as defined in RFC 5077) contain a lifetime_hint, + // sessions identified via a Session_ID do not. + const std::chrono::seconds session_lifetime_hint = [&] { + if(state.new_session_ticket() != nullptr) { + return std::chrono::seconds(state.new_session_ticket()->ticket_lifetime_hint()); + } else { + return std::chrono::seconds::max(); + } + }(); + Session session_info(state.session_keys().master_secret(), state.server_hello()->legacy_version(), state.server_hello()->ciphersuite(), Connection_Side::Client, state.server_hello()->supports_extended_master_secret(), state.server_hello()->supports_encrypt_then_mac(), - get_peer_cert_chain(state), + state.peer_cert_chain(), m_info, state.server_hello()->srtp_profile(), callbacks().tls_current_timestamp(), - - // Session Tickets (as defined in RFC 5077) contain a lifetime_hint, - // sessions identified via a Session_ID do not. - ((state.new_session_ticket()) ? state.new_session_ticket()->ticket_lifetime_hint() - : std::chrono::seconds::max())); + session_lifetime_hint); // RFC 5077 3.4 // If the client receives a session ticket from the server, then it // discards any Session ID that was sent in the ServerHello. const auto handle = [&]() -> std::optional { - if(const auto& session_ticket = state.session_ticket(); !session_ticket.empty()) { - return session_ticket; - } else if(const auto& session_id = state.server_hello()->session_id(); !session_id.empty()) { - return session_id; - } else { - return std::nullopt; + /* + On successful resumption an empty (or absent) NewSessionTicket means "keep using + the old ticket" so we inherit it from the ClientHello. On a fresh negotiation + an empty NewSessionTicket means "no ticket for this session", so inheriting the + ClientHello's old ticket would store the new master secret under a ticket the + server has discarded. + */ + if(const auto* nst = state.new_session_ticket(); nst != nullptr && !nst->ticket().empty()) { + return Session_Handle(nst->ticket()); + } + if(state.is_a_resumption() && !state.client_hello()->session_ticket().empty()) { + return Session_Handle(state.client_hello()->session_ticket()); + } + if(const auto& session_id = state.server_hello()->session_id(); !session_id.empty()) { + return Session_Handle(session_id); } + return std::nullopt; }(); // Give the application a chance for a final veto before fully @@ -700,7 +743,7 @@ callbacks().tls_session_established([&, this] { Session_Summary summary(session_info, state.is_a_resumption(), external_psk_identity()); summary.set_session_id(state.server_hello()->session_id()); - if(auto nst = state.new_session_ticket()) { + if(const auto* nst = state.new_session_ticket()) { summary.set_session_ticket(nst->ticket()); } return summary; @@ -720,7 +763,7 @@ should_save) { // renew the session ticket by removing the one we used to establish // this connection and replace it with the one we just received - session_manager().remove(state.client_hello()->session_ticket()); + session_manager().remove(Session_Handle(state.client_hello()->session_ticket())); session_manager().store(session_info, handle.value()); } diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_client_impl_12.h botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_client_impl_12.h --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_client_impl_12.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_client_impl_12.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,7 +11,6 @@ #include #include -#include #include #include #include @@ -21,7 +20,7 @@ /** * SSL/TLS Client 1.2 implementation */ -class Client_Impl_12 : public Channel_Impl_12 { +class Client_Impl_12 final : public Channel_Impl_12 { public: /** * Set up a new TLS client session @@ -65,8 +64,6 @@ std::string application_protocol() const override { return m_application_protocol; } private: - std::vector get_peer_cert_chain(const Handshake_State& state) const override; - void initiate_handshake(Handshake_State& state, bool force_full_renegotiation) override; void send_client_hello(Handshake_State& state, @@ -75,8 +72,7 @@ std::optional session_and_handle = std::nullopt, const std::vector& next_protocols = {}); - void process_handshake_msg(const Handshake_State* active_state, - Handshake_State& pending_state, + void process_handshake_msg(Handshake_State& pending_state, Handshake_Type type, const std::vector& contents, bool epoch0_restart) override; diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_connection_state_12.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_connection_state_12.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_connection_state_12.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_connection_state_12.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,47 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include + +namespace Botan::TLS { + +Active_Connection_State_12::~Active_Connection_State_12() = default; +Active_Connection_State_12::Active_Connection_State_12(Active_Connection_State_12&&) noexcept = default; +Active_Connection_State_12& Active_Connection_State_12::operator=(Active_Connection_State_12&&) noexcept = default; + +Active_Connection_State_12::Active_Connection_State_12(const Handshake_State& state, std::string application_protocol) : + m_version(state.version()), + m_ciphersuite_code(state.server_hello()->ciphersuite()), + m_application_protocol(std::move(application_protocol)), + m_peer_certs(state.peer_cert_chain()), + m_client_random(state.client_hello()->random()), + m_psk_identity(state.psk_identity()), + m_server_random(state.server_hello()->random()), + m_session_id(state.server_hello()->session_id()), + m_master_secret(state.session_keys().master_secret()), + m_prf_algo(state.ciphersuite().prf_algo()), + m_client_supports_secure_renegotiation(state.client_hello()->secure_renegotiation()), + m_server_supports_secure_renegotiation(state.server_hello()->secure_renegotiation()), + m_client_finished_verify_data(state.client_finished()->verify_data()), + m_server_finished_verify_data(state.server_finished()->verify_data()), + m_supports_extended_master_secret(state.server_hello()->supports_extended_master_secret()) {} + +Active_Connection_State_12::Active_Connection_State_12(const Handshake_State& state, + std::string application_protocol, + std::unique_ptr io) : + Active_Connection_State_12(state, std::move(application_protocol)) { + BOTAN_ASSERT_NOMSG(m_version.is_datagram_protocol()); + auto* dtls_io = dynamic_cast(io.get()); + BOTAN_ASSERT_NOMSG(dtls_io != nullptr); + m_dtls_handshake_io.reset(dtls_io); + io.release(); // NOLINT(*-unused-return-value) +} + +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_connection_state_12.h botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_connection_state_12.h --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_connection_state_12.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_connection_state_12.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,105 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TLS_CONNECTION_STATE_12_H_ +#define BOTAN_TLS_CONNECTION_STATE_12_H_ + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace Botan::TLS { + +class Handshake_IO; +class Datagram_Handshake_IO; +class Handshake_State; + +/** +* Captures the state of a completed TLS 1.2 handshake that is needed +* for the lifetime of an active connection. + */ +class Active_Connection_State_12 final { + public: + ~Active_Connection_State_12(); + + Active_Connection_State_12(Active_Connection_State_12&&) noexcept; + Active_Connection_State_12& operator=(Active_Connection_State_12&&) noexcept; + + Active_Connection_State_12(const Active_Connection_State_12&) = delete; + Active_Connection_State_12& operator=(const Active_Connection_State_12&) = delete; + + Active_Connection_State_12(const Handshake_State& state, std::string application_protocol); + + // DTLS variant: takes the handshake IO for replay of final flight + Active_Connection_State_12(const Handshake_State& state, + std::string application_protocol, + std::unique_ptr io); + + Protocol_Version version() const { return m_version; } + + uint16_t ciphersuite_code() const { return m_ciphersuite_code; } + + const std::string& application_protocol() const { return m_application_protocol; } + + const std::vector& peer_certs() const { return m_peer_certs; } + + const std::vector& client_random() const { return m_client_random; } + + const std::optional& psk_identity() const { return m_psk_identity; } + + const std::vector& server_random() const { return m_server_random; } + + const Session_ID& session_id() const { return m_session_id; } + + const secure_vector& master_secret() const { return m_master_secret; } + + const std::string& prf_algo() const { return m_prf_algo; } + + bool client_supports_secure_renegotiation() const { return m_client_supports_secure_renegotiation; } + + bool server_supports_secure_renegotiation() const { return m_server_supports_secure_renegotiation; } + + const std::vector& client_finished_verify_data() const { return m_client_finished_verify_data; } + + const std::vector& server_finished_verify_data() const { return m_server_finished_verify_data; } + + bool supports_extended_master_secret() const { return m_supports_extended_master_secret; } + + /** + * For DTLS: the handshake IO from the completed handshake, needed + * to retransmit the last flight when records arrive under the + * previous epoch. Null for stream TLS. + */ + Datagram_Handshake_IO* dtls_handshake_io() { return m_dtls_handshake_io.get(); } + + private: + Protocol_Version m_version; + uint16_t m_ciphersuite_code = 0; + std::string m_application_protocol; + std::vector m_peer_certs; + std::vector m_client_random; + std::optional m_psk_identity; + std::vector m_server_random; + Session_ID m_session_id; + secure_vector m_master_secret; + std::string m_prf_algo; + bool m_client_supports_secure_renegotiation = false; + bool m_server_supports_secure_renegotiation = false; + std::vector m_client_finished_verify_data; + std::vector m_server_finished_verify_data; + bool m_supports_extended_master_secret = false; + std::unique_ptr m_dtls_handshake_io; +}; + +} // namespace Botan::TLS + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_extensions_12.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_extensions_12.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_extensions_12.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_extensions_12.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,118 @@ +/* +* TLS 1.2 Specific Extensions +* (C) 2011,2012,2015,2016 Jack Lloyd +* 2016 Juraj Somorovsky +* 2021 Elektrobit Automotive GmbH +* 2022 René Meusel, Hannes Rantzsch - neXenio GmbH +* 2023 Mateusz Berezecki +* 2023 Fabian Albert, René Meusel - Rohde & Schwarz Cybersecurity +* 2026 René Meusel - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include + +namespace Botan::TLS { + +Renegotiation_Extension::Renegotiation_Extension(TLS_Data_Reader& reader, uint16_t extension_size) : + m_reneg_data(reader.get_range(1, 0, 255)) { + if(m_reneg_data.size() + 1 != extension_size) { + throw Decoding_Error("Bad encoding for secure renegotiation extn"); + } +} + +std::vector Renegotiation_Extension::serialize(Connection_Side /*whoami*/) const { + std::vector buf; + append_tls_length_value(buf, m_reneg_data, 1); + return buf; +} + +std::vector Supported_Point_Formats::serialize(Connection_Side /*whoami*/) const { + // if this extension is sent, it MUST include uncompressed (RFC 4492, section 5.1) + if(m_prefers_compressed) { + return std::vector{2, ANSIX962_COMPRESSED_PRIME, UNCOMPRESSED}; + } else { + return std::vector{1, UNCOMPRESSED}; + } +} + +Supported_Point_Formats::Supported_Point_Formats(TLS_Data_Reader& reader, uint16_t extension_size) { + const uint8_t len = reader.get_byte(); + + if(len + 1 != extension_size) { + throw Decoding_Error("Inconsistent length field in supported point formats list"); + } + + bool includes_uncompressed = false; + for(size_t i = 0; i != len; ++i) { + const uint8_t format = reader.get_byte(); + + if(static_cast(format) == UNCOMPRESSED) { + m_prefers_compressed = false; + reader.discard_next(len - i - 1); + return; + } else if(static_cast(format) == ANSIX962_COMPRESSED_PRIME) { + m_prefers_compressed = true; + std::vector remaining_formats = reader.get_fixed(len - i - 1); + includes_uncompressed = + std::any_of(std::begin(remaining_formats), std::end(remaining_formats), [](uint8_t remaining_format) { + return static_cast(remaining_format) == UNCOMPRESSED; + }); + break; + } + + // ignore ANSIX962_COMPRESSED_CHAR2, we don't support these curves + } + + // RFC 4492 5.1.: + // If the Supported Point Formats Extension is indeed sent, it MUST contain the value 0 (uncompressed) + // as one of the items in the list of point formats. + // Note: + // RFC 8422 5.1.2. explicitly requires this check, + // but only if the Supported Groups extension was sent. + if(!includes_uncompressed) { + throw TLS_Exception(Alert::IllegalParameter, + "Supported Point Formats Extension must contain the uncompressed point format"); + } +} + +Session_Ticket_Extension::Session_Ticket_Extension(TLS_Data_Reader& reader, + uint16_t extension_size, + Connection_Side from) { + // RFC 5077 3.2: in a ServerHello the SessionTicket extension is just a + // flag indicating that a NewSessionTicket handshake message will follow; + // its extension_data MUST be empty. A ticket body is only valid in a + // ClientHello. + if(from == Connection_Side::Server && extension_size != 0) { + throw Decoding_Error("Server sent a non-empty SessionTicket extension"); + } + m_ticket = Session_Ticket(reader.get_elem>(extension_size)); +} + +Extended_Master_Secret::Extended_Master_Secret(TLS_Data_Reader& /*unused*/, uint16_t extension_size) { + if(extension_size != 0) { + throw Decoding_Error("Invalid extended_master_secret extension"); + } +} + +std::vector Extended_Master_Secret::serialize(Connection_Side /*whoami*/) const { + return std::vector(); +} + +Encrypt_then_MAC::Encrypt_then_MAC(TLS_Data_Reader& /*unused*/, uint16_t extension_size) { + if(extension_size != 0) { + throw Decoding_Error("Invalid encrypt_then_mac extension"); + } +} + +std::vector Encrypt_then_MAC::serialize(Connection_Side /*whoami*/) const { + return std::vector(); +} + +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_extensions_12.h botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_extensions_12.h --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_extensions_12.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_extensions_12.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,155 @@ +/* +* TLS 1.2 Specific Extensions +* (C) 2011,2012,2016,2018,2019 Jack Lloyd +* (C) 2016 Juraj Somorovsky +* (C) 2016 Matthias Gierlings +* (C) 2021 Elektrobit Automotive GmbH +* (C) 2022 René Meusel, Hannes Rantzsch - neXenio GmbH +* (C) 2023 Fabian Albert, René Meusel - Rohde & Schwarz Cybersecurity +* (C) 2026 René Meusel - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TLS_EXTENSIONS_12_H_ +#define BOTAN_TLS_EXTENSIONS_12_H_ + +#include +#include + +#include + +namespace Botan::TLS { + +class TLS_Data_Reader; + +/** +* Renegotiation Indication Extension (RFC 5746) +*/ +class BOTAN_UNSTABLE_API Renegotiation_Extension final : public Extension { + public: + static Extension_Code static_type() { return Extension_Code::SafeRenegotiation; } + + Extension_Code type() const override { return static_type(); } + + Renegotiation_Extension() = default; + + explicit Renegotiation_Extension(const std::vector& bits) : m_reneg_data(bits) {} + + Renegotiation_Extension(TLS_Data_Reader& reader, uint16_t extension_size); + + const std::vector& renegotiation_info() const { return m_reneg_data; } + + std::vector serialize(Connection_Side whoami) const override; + + bool empty() const override { return false; } // always send this + + private: + std::vector m_reneg_data; +}; + +/** +* Session Ticket Extension (RFC 5077) +*/ +class BOTAN_UNSTABLE_API Session_Ticket_Extension final : public Extension { + public: + static Extension_Code static_type() { return Extension_Code::SessionTicket; } + + Extension_Code type() const override { return static_type(); } + + /** + * @return contents of the session ticket + */ + const Session_Ticket& contents() const { return m_ticket; } + + /** + * Create empty extension, used by both client and server + */ + Session_Ticket_Extension() = default; + + /** + * Extension with ticket, used by client + */ + explicit Session_Ticket_Extension(Session_Ticket session_ticket) : m_ticket(std::move(session_ticket)) {} + + /** + * Deserialize a session ticket + */ + Session_Ticket_Extension(TLS_Data_Reader& reader, uint16_t extension_size, Connection_Side from); + + std::vector serialize(Connection_Side /*whoami*/) const override { return m_ticket.get(); } + + bool empty() const override { return false; } + + private: + Session_Ticket m_ticket; +}; + +/** +* Supported Point Formats Extension (RFC 4492) +*/ +class BOTAN_UNSTABLE_API Supported_Point_Formats final : public Extension { + public: + enum ECPointFormat : uint8_t /* NOLINT(*-use-enum-class) */ { + UNCOMPRESSED = 0, + ANSIX962_COMPRESSED_PRIME = 1, + ANSIX962_COMPRESSED_CHAR2 = 2, // don't support these curves + }; + + static Extension_Code static_type() { return Extension_Code::EcPointFormats; } + + Extension_Code type() const override { return static_type(); } + + std::vector serialize(Connection_Side whoami) const override; + + explicit Supported_Point_Formats(bool prefer_compressed) : m_prefers_compressed(prefer_compressed) {} + + Supported_Point_Formats(TLS_Data_Reader& reader, uint16_t extension_size); + + bool empty() const override { return false; } + + bool prefers_compressed() const { return m_prefers_compressed; } + + private: + bool m_prefers_compressed = false; +}; + +/** +* Extended Master Secret Extension (RFC 7627) +*/ +class BOTAN_UNSTABLE_API Extended_Master_Secret final : public Extension { + public: + static Extension_Code static_type() { return Extension_Code::ExtendedMasterSecret; } + + Extension_Code type() const override { return static_type(); } + + std::vector serialize(Connection_Side whoami) const override; + + bool empty() const override { return false; } + + Extended_Master_Secret() = default; + + Extended_Master_Secret(TLS_Data_Reader& reader, uint16_t extension_size); +}; + +/** +* Encrypt-then-MAC Extension (RFC 7366) +*/ +class BOTAN_UNSTABLE_API Encrypt_then_MAC final : public Extension { + public: + static Extension_Code static_type() { return Extension_Code::EncryptThenMac; } + + Extension_Code type() const override { return static_type(); } + + std::vector serialize(Connection_Side whoami) const override; + + bool empty() const override { return false; } + + Encrypt_then_MAC() = default; + + Encrypt_then_MAC(TLS_Data_Reader& reader, uint16_t extension_size); +}; + +} // namespace Botan::TLS + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_handshake_io.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_handshake_io.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_handshake_io.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_handshake_io.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,7 +8,9 @@ #include #include -#include +#include +#include +#include #include #include #include @@ -22,6 +24,18 @@ return make_uint32(0, q[0], q[1], q[2]); } +// Reject handshake type values that are internal sentinels, not wire values +void verify_is_expected_wire_handshake_type(Handshake_Type type) { + switch(type) { + case Handshake_Type::HelloRetryRequest: + case Handshake_Type::HandshakeCCS: + case Handshake_Type::None: + throw TLS_Exception(Alert::UnexpectedMessage, "Invalid handshake message type"); + default: + break; + } +} + void store_be24(uint8_t out[3], size_t val) { out[0] = get_byte<1>(static_cast(val)); out[1] = get_byte<2>(static_cast(val)); @@ -59,18 +73,36 @@ } } -std::pair> Stream_Handshake_IO::get_next_record(bool /*expecting_ccs*/) { +std::pair> Stream_Handshake_IO::get_next_record(bool expecting_ccs, + size_t max_message_size) { if(m_queue.size() >= 4) { - const size_t length = 4 + make_uint32(0, m_queue[1], m_queue[2], m_queue[3]); + const Handshake_Type type = static_cast(m_queue[0]); - if(m_queue.size() >= length) { - Handshake_Type type = static_cast(m_queue[0]); + const size_t rec_length = make_uint32(0, m_queue[1], m_queue[2], m_queue[3]); - if(type == Handshake_Type::None) { - throw Decoding_Error("Invalid handshake message type"); + // If we are expecting a CCS but the next queued message is not a CCS, + // the peer has skipped the CCS message. This can happen when the peer + // sends an encrypted Finished without the preceding CCS, in which case + // the encrypted bytes are misinterpreted as a handshake message. + if(expecting_ccs) { + const bool is_ccs = (type == Handshake_Type::HandshakeCCS && rec_length == 0); + if(!is_ccs) { + throw TLS_Exception(Alert::UnexpectedMessage, "Expected ChangeCipherSpec but got a handshake message"); } + } else { + verify_is_expected_wire_handshake_type(type); + + if(max_message_size > 0 && rec_length > max_message_size) { + throw TLS_Exception( + Alert::HandshakeFailure, + Botan::fmt("Handshake message is {} bytes, policy maximum is {}", rec_length, max_message_size)); + } + } - std::vector contents(m_queue.begin() + 4, m_queue.begin() + length); + const size_t length = 4 + rec_length; + + if(m_queue.size() >= length) { + const std::vector contents(m_queue.begin() + 4, m_queue.begin() + length); m_queue.erase(m_queue.begin(), m_queue.begin() + length); @@ -135,7 +167,7 @@ if(msg.epoch != epoch) { // Epoch gap: insert the CCS - std::vector ccs(1, 1); + const std::vector ccs(1, 1); m_send_hs(epoch, Record_Type::ChangeCipherSpec, ccs); } @@ -187,13 +219,23 @@ const size_t DTLS_HANDSHAKE_HEADER_LEN = 12; - while(record_len) { + while(record_len > 0) { if(record_len < DTLS_HANDSHAKE_HEADER_LEN) { return; // completely bogus? at least degenerate/weird } const Handshake_Type msg_type = static_cast(record[0]); + + verify_is_expected_wire_handshake_type(msg_type); + const size_t msg_len = load_be24(&record[1]); + + if(m_max_handshake_msg_size > 0 && msg_len > m_max_handshake_msg_size) { + throw TLS_Exception( + Alert::HandshakeFailure, + Botan::fmt("Handshake message is {} bytes, policy maximum is {}", msg_len, m_max_handshake_msg_size)); + } + const uint16_t message_seq = load_be(&record[4], 0); const size_t fragment_offset = load_be24(&record[6]); const size_t fragment_length = load_be24(&record[9]); @@ -204,8 +246,24 @@ throw Decoding_Error("Bad lengths in DTLS header"); } - if(message_seq >= m_in_message_seq) { - m_messages[message_seq].add_fragment( + // Bound the out-of-order reassembly window. + constexpr uint16_t reassembly_window = 16; + + // Independently cap total bytes committed to in-flight reassembly slots + const size_t max_pending = 4 * m_max_handshake_msg_size; + + if(message_seq >= m_in_message_seq && (message_seq - m_in_message_seq) < reassembly_window) { + auto [it, inserted] = m_messages.try_emplace(message_seq); + if(inserted) { + if(m_max_handshake_msg_size > 0 && m_pending_reassembly_bytes + msg_len > max_pending) { + m_messages.erase(it); + record += total_size; + record_len -= total_size; + continue; + } + m_pending_reassembly_bytes += msg_len; + } + it->second.add_fragment( &record[DTLS_HANDSHAKE_HEADER_LEN], fragment_length, fragment_offset, epoch, msg_type, msg_len); } else { // TODO: detect retransmitted flight @@ -216,7 +274,8 @@ } } -std::pair> Datagram_Handshake_IO::get_next_record(bool expecting_ccs) { +std::pair> Datagram_Handshake_IO::get_next_record(bool expecting_ccs, + size_t /*max_message_size*/) { // Expecting a message means the last flight is concluded if(!m_flights.rbegin()->empty()) { m_flights.push_back(std::vector()); @@ -241,7 +300,24 @@ m_in_message_seq += 1; - return i->second.message(); + auto result = i->second.message(); + + // Free the reassembly buffer for this delivered slot and uncommit its + // bytes against the cap. The entry itself stays in m_messages because + // the expecting_ccs branch above uses m_messages.begin()->second.epoch() + // as an epoch-0 sentinel; it only needs the metadata, not the buffers. + BOTAN_ASSERT_NOMSG(m_pending_reassembly_bytes >= i->second.msg_length()); + m_pending_reassembly_bytes -= i->second.msg_length(); + i->second.release_buffers(); + + return result; +} + +void Datagram_Handshake_IO::Handshake_Reassembly::release_buffers() { + m_received_mask.clear(); + m_received_mask.shrink_to_fit(); + m_message.clear(); + m_message.shrink_to_fit(); } void Datagram_Handshake_IO::Handshake_Reassembly::add_fragment(const uint8_t fragment[], @@ -250,18 +326,21 @@ uint16_t epoch, Handshake_Type msg_type, size_t msg_length) { - if(complete()) { - return; // already have entire message, ignore this - } - if(m_msg_type == Handshake_Type::None) { + // First fragment for this message_seq m_epoch = epoch; m_msg_type = msg_type; m_msg_length = msg_length; - } + m_message.resize(msg_length); + m_received_mask.assign(msg_length, 0); + } else { + if(msg_type != m_msg_type || msg_length != m_msg_length || epoch != m_epoch) { + throw Decoding_Error("Inconsistent values in fragmented DTLS handshake header"); + } - if(msg_type != m_msg_type || msg_length != m_msg_length || epoch != m_epoch) { - throw Decoding_Error("Inconsistent values in fragmented DTLS handshake header"); + if(complete()) { + return; // already have entire message, ignore this + } } if(fragment_offset > m_msg_length) { @@ -272,34 +351,26 @@ throw Decoding_Error("Fragment overlaps past end of message"); } - if(fragment_offset == 0 && fragment_length == m_msg_length) { - m_fragments.clear(); - m_message.assign(fragment, fragment + fragment_length); - } else { - /* - * FIXME. This is a pretty lame way to do defragmentation, huge - * overhead with a tree node per byte. - * - * Also should confirm that all overlaps have no changes, - * otherwise we expose ourselves to the classic fingerprinting - * and IDS evasion attacks on IP fragmentation. - */ - for(size_t i = 0; i != fragment_length; ++i) { - m_fragments[fragment_offset + i] = fragment[i]; - } + BOTAN_ASSERT_NOMSG(m_received_mask.size() == m_msg_length); - if(m_fragments.size() == m_msg_length) { - m_message.resize(m_msg_length); - for(size_t i = 0; i != m_msg_length; ++i) { - m_message[i] = m_fragments[i]; + for(size_t i = 0; i != fragment_length; ++i) { + const size_t off = fragment_offset + i; + if(m_received_mask[off] != 0) { + // RFC 6347 4.2.3 permits overlapping retransmissions, but the + // overlapping bytes must agree. + if(m_message[off] != fragment[i]) { + throw Decoding_Error("Inconsistent overlapping DTLS handshake fragment"); } - m_fragments.clear(); + } else { + m_message[off] = fragment[i]; + m_received_mask[off] = 1; + ++m_bytes_received; } } } bool Datagram_Handshake_IO::Handshake_Reassembly::complete() const { - return (m_msg_type != Handshake_Type::None && m_message.size() == m_msg_length); + return (m_msg_type != Handshake_Type::None && m_bytes_received == m_msg_length); } std::pair> Datagram_Handshake_IO::Handshake_Reassembly::message() const { @@ -312,8 +383,8 @@ std::vector Datagram_Handshake_IO::format_fragment(const uint8_t fragment[], size_t frag_len, - uint16_t frag_offset, - uint16_t msg_len, + uint32_t frag_offset, + uint32_t msg_len, Handshake_Type type, uint16_t msg_sequence) const { std::vector send_buf(12 + frag_len); @@ -337,10 +408,11 @@ std::vector Datagram_Handshake_IO::format_w_seq(const std::vector& msg, Handshake_Type type, uint16_t msg_sequence) const { - return format_fragment(msg.data(), msg.size(), 0, static_cast(msg.size()), type, msg_sequence); + return format_fragment(msg.data(), msg.size(), 0, static_cast(msg.size()), type, msg_sequence); } std::vector Datagram_Handshake_IO::format(const std::vector& msg, Handshake_Type type) const { + BOTAN_ASSERT_NOMSG(m_in_message_seq > 0); return format_w_seq(msg, type, m_in_message_seq - 1); } @@ -407,8 +479,8 @@ const std::vector frag = format_fragment(&msg_bits[frag_offset], frag_len, - static_cast(frag_offset), - static_cast(msg_bits.size()), + static_cast(frag_offset), + static_cast(msg_bits.size()), msg_type, msg_seq); diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_handshake_io.h botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_handshake_io.h --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_handshake_io.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_handshake_io.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,7 +10,6 @@ #include #include -#include #include #include #include @@ -64,13 +63,15 @@ /** * Returns (HANDSHAKE_NONE, std::vector<>()) if no message currently available */ - virtual std::pair> get_next_record(bool expecting_ccs) = 0; + virtual std::pair> get_next_record(bool expecting_ccs, + size_t max_message_size) = 0; Handshake_IO() = default; Handshake_IO(const Handshake_IO&) = delete; - + Handshake_IO(Handshake_IO&&) = delete; Handshake_IO& operator=(const Handshake_IO&) = delete; + Handshake_IO& operator=(Handshake_IO&&) = delete; virtual ~Handshake_IO() = default; }; @@ -82,13 +83,13 @@ public: typedef std::function&)> writer_fn; - explicit Stream_Handshake_IO(writer_fn writer) : m_send_hs(writer) {} + explicit Stream_Handshake_IO(writer_fn writer) : m_send_hs(std::move(writer)) {} Protocol_Version initial_record_version() const override; bool timeout_check() override { return false; } - bool have_more_data() const override { return m_queue.empty() == false; } + bool have_more_data() const override { return !m_queue.empty(); } std::vector send(const Handshake_Message& msg) override; @@ -99,7 +100,8 @@ void add_record(const uint8_t record[], size_t record_len, Record_Type type, uint64_t sequence_number) override; - std::pair> get_next_record(bool expecting_ccs) override; + std::pair> get_next_record(bool expecting_ccs, + size_t max_message_size) override; private: std::deque m_queue; @@ -117,13 +119,15 @@ class Connection_Sequence_Numbers& seq, uint16_t mtu, uint64_t initial_timeout_ms, - uint64_t max_timeout_ms) : + uint64_t max_timeout_ms, + size_t max_handshake_msg_size) : m_seqs(seq), m_flights(1), m_initial_timeout(initial_timeout_ms), m_max_timeout(max_timeout_ms), - m_send_hs(writer), - m_mtu(mtu) {} + m_send_hs(std::move(writer)), + m_mtu(mtu), + m_max_handshake_msg_size(max_handshake_msg_size) {} Protocol_Version initial_record_version() const override; @@ -140,7 +144,8 @@ void add_record(const uint8_t record[], size_t record_len, Record_Type type, uint64_t sequence_number) override; - std::pair> get_next_record(bool expecting_ccs) override; + std::pair> get_next_record(bool expecting_ccs, + size_t max_message_size) override; private: void retransmit_flight(size_t flight); @@ -148,8 +153,8 @@ std::vector format_fragment(const uint8_t fragment[], size_t fragment_len, - uint16_t frag_offset, - uint16_t msg_len, + uint32_t frag_offset, + uint32_t msg_len, Handshake_Type type, uint16_t msg_sequence) const; @@ -175,16 +180,23 @@ uint16_t epoch() const { return m_epoch; } + // 0 until the first fragment has set the declared msg_length. + size_t msg_length() const { return m_msg_length; } + std::pair> message() const; + // Release the memory buffers; called after reassembly has completed + void release_buffers(); + private: Handshake_Type m_msg_type = Handshake_Type::None; size_t m_msg_length = 0; + size_t m_bytes_received = 0; uint16_t m_epoch = 0; - // vector m_seen; - // vector m_fragments - std::map m_fragments; + // Reassembly buffer (sized to m_msg_length once known) and a parallel + // byte-mask marking which positions have already been seen. + std::vector m_received_mask; std::vector m_message; }; @@ -194,13 +206,14 @@ Message_Info() : epoch(0xFFFF), msg_type(Handshake_Type::None) {} - uint16_t epoch; - Handshake_Type msg_type; - std::vector msg_bits; + uint16_t epoch; // NOLINT(*non-private-member-variable*) + Handshake_Type msg_type; // NOLINT(*non-private-member-variable*) + std::vector msg_bits; // NOLINT(*non-private-member-variable*) }; class Connection_Sequence_Numbers& m_seqs; std::map m_messages; + size_t m_pending_reassembly_bytes = 0; std::set m_ccs_epochs; std::vector> m_flights; std::map m_flight_data; @@ -216,6 +229,7 @@ writer_fn m_send_hs; uint16_t m_mtu; + size_t m_max_handshake_msg_size; }; } // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_handshake_state.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_handshake_state.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_handshake_state.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_handshake_state.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,10 +9,12 @@ #include #include -#include +#include +#include +#include +#include #include #include -#include namespace Botan::TLS { @@ -20,73 +22,6 @@ return handshake_type_to_string(type()); } -const char* handshake_type_to_string(Handshake_Type type) { - switch(type) { - case Handshake_Type::HelloVerifyRequest: - return "hello_verify_request"; - - case Handshake_Type::HelloRequest: - return "hello_request"; - - case Handshake_Type::ClientHello: - return "client_hello"; - - case Handshake_Type::ServerHello: - return "server_hello"; - - case Handshake_Type::HelloRetryRequest: - return "hello_retry_request"; - - case Handshake_Type::Certificate: - return "certificate"; - - case Handshake_Type::CertificateUrl: - return "certificate_url"; - - case Handshake_Type::CertificateStatus: - return "certificate_status"; - - case Handshake_Type::ServerKeyExchange: - return "server_key_exchange"; - - case Handshake_Type::CertificateRequest: - return "certificate_request"; - - case Handshake_Type::ServerHelloDone: - return "server_hello_done"; - - case Handshake_Type::CertificateVerify: - return "certificate_verify"; - - case Handshake_Type::ClientKeyExchange: - return "client_key_exchange"; - - case Handshake_Type::NewSessionTicket: - return "new_session_ticket"; - - case Handshake_Type::HandshakeCCS: - return "change_cipher_spec"; - - case Handshake_Type::Finished: - return "finished"; - - case Handshake_Type::EndOfEarlyData: - return "end_of_early_data"; - - case Handshake_Type::EncryptedExtensions: - return "encrypted_extensions"; - - case Handshake_Type::KeyUpdate: - return "key_update"; - - case Handshake_Type::None: - return "invalid"; - } - - throw TLS_Exception(Alert::UnexpectedMessage, - "Unknown TLS handshake message type " + std::to_string(static_cast(type))); -} - /* * Initialize the SSL/TLS Handshake State */ @@ -102,85 +37,100 @@ void Handshake_State::hello_verify_request(const Hello_Verify_Request& hello_verify) { note_message(hello_verify); + BOTAN_ASSERT_NONNULL(m_client_hello); m_client_hello->update_hello_cookie(hello_verify); hash().reset(); hash().update(handshake_io().send(*m_client_hello)); note_message(*m_client_hello); } -void Handshake_State::client_hello(Client_Hello_12* client_hello) { +void Handshake_State::client_hello(std::unique_ptr client_hello) { + // Legacy behavior (exception to the rule): Allow client_hello to be nullptr to reset state. if(client_hello == nullptr) { m_client_hello.reset(); hash().reset(); } else { - m_client_hello.reset(client_hello); + m_client_hello = std::move(client_hello); note_message(*m_client_hello); } } -void Handshake_State::server_hello(Server_Hello_12* server_hello) { - m_server_hello.reset(server_hello); +void Handshake_State::server_hello(std::unique_ptr server_hello) { + BOTAN_ASSERT_NONNULL(server_hello); + m_server_hello = std::move(server_hello); m_ciphersuite = Ciphersuite::by_id(m_server_hello->ciphersuite()); note_message(*m_server_hello); } -void Handshake_State::server_certs(Certificate_12* server_certs) { - m_server_certs.reset(server_certs); +void Handshake_State::server_certs(std::unique_ptr server_certs) { + BOTAN_ASSERT_NONNULL(server_certs); + m_server_certs = std::move(server_certs); note_message(*m_server_certs); } -void Handshake_State::server_cert_status(Certificate_Status* server_cert_status) { - m_server_cert_status.reset(server_cert_status); +void Handshake_State::server_cert_status(std::unique_ptr server_cert_status) { + BOTAN_ASSERT_NONNULL(server_cert_status); + m_server_cert_status = std::move(server_cert_status); note_message(*m_server_cert_status); } -void Handshake_State::server_kex(Server_Key_Exchange* server_kex) { - m_server_kex.reset(server_kex); +void Handshake_State::server_kex(std::unique_ptr server_kex) { + BOTAN_ASSERT_NONNULL(server_kex); + m_server_kex = std::move(server_kex); note_message(*m_server_kex); } -void Handshake_State::cert_req(Certificate_Request_12* cert_req) { - m_cert_req.reset(cert_req); +void Handshake_State::cert_req(std::unique_ptr cert_req) { + BOTAN_ASSERT_NONNULL(cert_req); + m_cert_req = std::move(cert_req); note_message(*m_cert_req); } -void Handshake_State::server_hello_done(Server_Hello_Done* server_hello_done) { - m_server_hello_done.reset(server_hello_done); +void Handshake_State::server_hello_done(std::unique_ptr server_hello_done) { + BOTAN_ASSERT_NONNULL(server_hello_done); + m_server_hello_done = std::move(server_hello_done); note_message(*m_server_hello_done); } -void Handshake_State::client_certs(Certificate_12* client_certs) { - m_client_certs.reset(client_certs); +void Handshake_State::client_certs(std::unique_ptr client_certs) { + BOTAN_ASSERT_NONNULL(client_certs); + m_client_certs = std::move(client_certs); note_message(*m_client_certs); } -void Handshake_State::client_kex(Client_Key_Exchange* client_kex) { - m_client_kex.reset(client_kex); +void Handshake_State::client_kex(std::unique_ptr client_kex) { + BOTAN_ASSERT_NONNULL(client_kex); + m_client_kex = std::move(client_kex); note_message(*m_client_kex); } -void Handshake_State::client_verify(Certificate_Verify_12* client_verify) { - m_client_verify.reset(client_verify); +void Handshake_State::client_verify(std::unique_ptr client_verify) { + BOTAN_ASSERT_NONNULL(client_verify); + m_client_verify = std::move(client_verify); note_message(*m_client_verify); } -void Handshake_State::server_verify(Certificate_Verify_12* server_verify) { - m_server_verify.reset(server_verify); +void Handshake_State::server_verify(std::unique_ptr server_verify) { + BOTAN_ASSERT_NONNULL(server_verify); + m_server_verify = std::move(server_verify); note_message(*m_server_verify); } -void Handshake_State::new_session_ticket(New_Session_Ticket_12* new_session_ticket) { - m_new_session_ticket.reset(new_session_ticket); +void Handshake_State::new_session_ticket(std::unique_ptr new_session_ticket) { + BOTAN_ASSERT_NONNULL(new_session_ticket); + m_new_session_ticket = std::move(new_session_ticket); note_message(*m_new_session_ticket); } -void Handshake_State::server_finished(Finished_12* server_finished) { - m_server_finished.reset(server_finished); +void Handshake_State::server_finished(std::unique_ptr server_finished) { + BOTAN_ASSERT_NONNULL(server_finished); + m_server_finished = std::move(server_finished); note_message(*m_server_finished); } -void Handshake_State::client_finished(Finished_12* client_finished) { - m_client_finished.reset(client_finished); +void Handshake_State::client_finished(std::unique_ptr client_finished) { + BOTAN_ASSERT_NONNULL(client_finished); + m_client_finished = std::move(client_finished); note_message(*m_client_finished); } @@ -203,6 +153,7 @@ } void Handshake_State::compute_session_keys() { + BOTAN_ASSERT_NONNULL(client_kex()); m_session_keys = Session_Keys(this, client_kex()->pre_master_secret(), false); } @@ -222,26 +173,24 @@ return m_transitions.received_handshake_msg(handshake_msg); } -std::pair> Handshake_State::get_next_handshake_msg() { - return m_handshake_io->get_next_record(m_transitions.change_cipher_spec_expected()); +std::pair> Handshake_State::get_next_handshake_msg(size_t max_handshake_msg_size) { + return m_handshake_io->get_next_record(m_transitions.change_cipher_spec_expected(), max_handshake_msg_size); } Session_Ticket Handshake_State::session_ticket() const { - if(new_session_ticket() && !new_session_ticket()->ticket().empty()) { - return new_session_ticket()->ticket(); + if(const auto* nst = new_session_ticket()) { + const auto& ticket = nst->ticket(); + if(!ticket.empty()) { + return ticket; + } } + BOTAN_ASSERT_NONNULL(client_hello()); return client_hello()->session_ticket(); } std::unique_ptr Handshake_State::protocol_specific_prf() const { - const std::string prf_algo = ciphersuite().prf_algo(); - - if(prf_algo == "MD5" || prf_algo == "SHA-1") { - return KDF::create_or_throw("TLS-12-PRF(SHA-256)"); - } - - return KDF::create_or_throw("TLS-12-PRF(" + prf_algo + ")"); + return m_callbacks.tls12_protocol_specific_kdf(ciphersuite().prf_algo()); } std::pair Handshake_State::choose_sig_format(const Private_Key& key, @@ -252,10 +201,16 @@ const std::vector allowed = policy.allowed_signature_schemes(); + if(for_client_auth) { + BOTAN_ASSERT_NONNULL(cert_req()); + } else { + BOTAN_ASSERT_NONNULL(client_hello()); + } + std::vector requested = (for_client_auth) ? cert_req()->signature_schemes() : client_hello()->signature_schemes(); - for(Signature_Scheme scheme : allowed) { + for(const Signature_Scheme scheme : allowed) { if(!scheme.is_available()) { continue; } @@ -268,6 +223,11 @@ } } + if(!chosen_scheme.is_set()) { + throw TLS_Exception(Alert::HandshakeFailure, + "Could not agree on a signature scheme with peer for " + sig_algo + " key"); + } + const std::string hash = chosen_scheme.hash_function_name(); if(!policy.allowed_signature_hash(hash)) { @@ -283,11 +243,9 @@ namespace { -bool supported_algos_include(const std::vector& schemes, - std::string_view key_type, - std::string_view hash_type) { - for(Signature_Scheme scheme : schemes) { - if(scheme.is_available() && hash_type == scheme.hash_function_name() && key_type == scheme.algorithm_name()) { +bool supported_algos_include(const std::vector& schemes, Signature_Scheme received_scheme) { + for(const Signature_Scheme scheme : schemes) { + if(scheme == received_scheme && scheme.is_available()) { return true; } } @@ -317,8 +275,8 @@ throw Decoding_Error("Counterparty sent inconsistent key and sig types"); } - if(for_client_auth && !cert_req()) { - throw TLS_Exception(Alert::HandshakeFailure, "No certificate verify set"); + if(for_client_auth && cert_req() == nullptr) { + throw TLS_Exception(Alert::HandshakeFailure, "No CertificateVerify message received"); } /* @@ -329,15 +287,13 @@ const std::vector supported_algos = for_client_auth ? cert_req()->signature_schemes() : offered_schemes; - const std::string hash_algo = scheme.hash_function_name(); - if(!scheme.is_compatible_with(Protocol_Version::TLS_V12)) { - throw TLS_Exception(Alert::IllegalParameter, "Peer sent unexceptable signature scheme"); + throw TLS_Exception(Alert::IllegalParameter, "Peer sent unacceptable signature scheme"); } - if(!supported_algos_include(supported_algos, key_type, hash_algo)) { + if(!supported_algos_include(supported_algos, scheme)) { throw TLS_Exception(Alert::IllegalParameter, - "TLS signature extension did not allow for " + key_type + "/" + hash_algo + " signature"); + "TLS signature extension did not allow for " + scheme.to_string() + " signature"); } if(!scheme.format().has_value()) { diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_handshake_state.h botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_handshake_state.h --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_handshake_state.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_handshake_state.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,21 +9,23 @@ #ifndef BOTAN_TLS_HANDSHAKE_STATE_H_ #define BOTAN_TLS_HANDSHAKE_STATE_H_ -#include -#include -#include #include #include +#include #include +#include +#include #include #include #include #include -#include #include +#include namespace Botan { +enum class Signature_Format : uint8_t; +class Public_Key; class KDF; namespace TLS { @@ -61,11 +63,15 @@ Handshake_State(std::unique_ptr io, Callbacks& callbacks); virtual ~Handshake_State(); - Handshake_State(const Handshake_State&) = delete; - Handshake_State& operator=(const Handshake_State&) = delete; + Handshake_State(const Handshake_State& other) = delete; + Handshake_State(Handshake_State&& other) = delete; + Handshake_State& operator=(const Handshake_State& other) = delete; + Handshake_State& operator=(Handshake_State&& other) = delete; Handshake_IO& handshake_io() { return *m_handshake_io; } + std::unique_ptr take_handshake_io() { return std::move(m_handshake_io); } + /** * Return true iff we have received a particular message already * @param msg_type the message type @@ -84,7 +90,7 @@ */ void set_expected_next(Handshake_Type msg_type); - std::pair> get_next_handshake_msg(); + std::pair> get_next_handshake_msg(size_t max_handshake_msg_size); Session_Ticket session_ticket() const; @@ -107,26 +113,24 @@ void hello_verify_request(const Hello_Verify_Request& hello_verify); - // TODO: take unique_ptr instead of raw pointers for all of these, as - // we're taking the ownership - void client_hello(Client_Hello_12* client_hello); - void server_hello(Server_Hello_12* server_hello); - void server_cert_status(Certificate_Status* server_cert_status); - void server_kex(Server_Key_Exchange* server_kex); - void cert_req(Certificate_Request_12* cert_req); - void server_hello_done(Server_Hello_Done* server_hello_done); - void client_kex(Client_Key_Exchange* client_kex); - - void client_certs(Certificate_12* client_certs); - void server_certs(Certificate_12* server_certs); + void client_hello(std::unique_ptr client_hello); + void server_hello(std::unique_ptr server_hello); + void server_cert_status(std::unique_ptr server_cert_status); + void server_kex(std::unique_ptr server_kex); + void cert_req(std::unique_ptr cert_req); + void server_hello_done(std::unique_ptr server_hello_done); + void client_kex(std::unique_ptr client_kex); + + void client_certs(std::unique_ptr client_certs); + void server_certs(std::unique_ptr server_certs); - void client_verify(Certificate_Verify_12* client_verify); - void server_verify(Certificate_Verify_12* server_verify); + void client_verify(std::unique_ptr client_verify); + void server_verify(std::unique_ptr server_verify); - void server_finished(Finished_12* server_finished); - void client_finished(Finished_12* client_finished); + void server_finished(std::unique_ptr server_finished); + void client_finished(std::unique_ptr client_finished); - void new_session_ticket(New_Session_Ticket_12* new_session_ticket); + void new_session_ticket(std::unique_ptr new_session_ticket); const Client_Hello_12* client_hello() const { return m_client_hello.get(); } @@ -156,6 +160,8 @@ const Finished_12* client_finished() const { return m_client_finished.get(); } + virtual std::vector peer_cert_chain() const = 0; + const Ciphersuite& ciphersuite() const; std::optional psk_identity() const; diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_messages_12.h botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_messages_12.h --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_messages_12.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_messages_12.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,424 @@ +/* +* TLS Messages +* (C) 2004-2011,2015 Jack Lloyd +* 2016 Matthias Gierlings +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TLS_MESSAGES_12_H_ +#define BOTAN_TLS_MESSAGES_12_H_ + +#include +#include + +namespace Botan { + +class PK_Key_Agreement_Key; + +namespace TLS { + +class BOTAN_UNSTABLE_API Client_Hello_12 final : public Client_Hello_12_Shim { + public: + class Settings final { + public: + explicit Settings(const Protocol_Version version, std::string_view hostname = "") : + m_new_session_version(version), m_hostname(hostname) {} + + Protocol_Version protocol_version() const { return m_new_session_version; } + + const std::string& hostname() const { return m_hostname; } + + private: + const Protocol_Version m_new_session_version; + const std::string m_hostname; + }; + + public: + Client_Hello_12(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + const std::vector& reneg_info, + const Settings& client_settings, + const std::vector& next_protocols); + + Client_Hello_12(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + const std::vector& reneg_info, + const Session_with_Handle& session_and_handle, + const std::vector& next_protocols); + + explicit Client_Hello_12(const std::vector& buf); + + private: + explicit Client_Hello_12(std::unique_ptr data); + + public: + using Client_Hello::compression_methods; + using Client_Hello::random; + + bool prefers_compressed_ec_points() const; + + bool secure_renegotiation() const; + + std::vector renegotiation_info() const; + + bool supports_session_ticket() const; + + Session_Ticket session_ticket() const; + + std::optional session_handle() const; + + bool supports_extended_master_secret() const; + + bool supports_cert_status_message() const; + + bool supports_encrypt_then_mac() const; + + void update_hello_cookie(const Hello_Verify_Request& hello_verify); + + private: + void add_tls12_supported_groups_extensions(const Policy& policy); +}; + +class BOTAN_UNSTABLE_API Server_Hello_12 final : public Server_Hello_12_Shim { + public: + class Settings final { + public: + Settings(Session_ID new_session_id, + Protocol_Version new_session_version, + uint16_t ciphersuite, + bool offer_session_ticket) : + m_new_session_id(std::move(new_session_id)), + m_new_session_version(new_session_version), + m_ciphersuite(ciphersuite), + m_offer_session_ticket(offer_session_ticket) {} + + const Session_ID& session_id() const { return m_new_session_id; } + + Protocol_Version protocol_version() const { return m_new_session_version; } + + uint16_t ciphersuite() const { return m_ciphersuite; } + + bool offer_session_ticket() const { return m_offer_session_ticket; } + + private: + const Session_ID m_new_session_id; + Protocol_Version m_new_session_version; + uint16_t m_ciphersuite; + bool m_offer_session_ticket; + }; + + Server_Hello_12(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + const std::vector& secure_reneg_info, + const Client_Hello_12& client_hello, + const Settings& settings, + std::string_view next_protocol); + + Server_Hello_12(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + const std::vector& secure_reneg_info, + const Client_Hello_12& client_hello, + const Session& resumed_session, + bool offer_session_ticket, + std::string_view next_protocol); + + explicit Server_Hello_12(const std::vector& buf); + + private: + explicit Server_Hello_12(std::unique_ptr data); + + public: + using Server_Hello::compression_method; + using Server_Hello::extension_types; + using Server_Hello::legacy_version; + using Server_Hello::random; + + bool secure_renegotiation() const; + + std::vector renegotiation_info() const; + + std::string next_protocol() const; + + bool supports_extended_master_secret() const; + + bool supports_encrypt_then_mac() const; + + bool supports_certificate_status_message() const; + + bool supports_session_ticket() const; + + uint16_t srtp_profile() const; + bool prefers_compressed_ec_points() const; +}; + +/** +* Client Key Exchange Message +*/ +class BOTAN_UNSTABLE_API Client_Key_Exchange final : public Handshake_Message { + public: + Handshake_Type type() const override { return Handshake_Type::ClientKeyExchange; } + + const secure_vector& pre_master_secret() const { return m_pre_master; } + + /** + * @returns the agreed upon PSK identity or std::nullopt if not applicable + */ + const std::optional& psk_identity() const { return m_psk_identity; } + + Client_Key_Exchange(Handshake_IO& io, + Handshake_State& state, + const Policy& policy, + Credentials_Manager& creds, + const Public_Key* server_public_key, + std::string_view hostname, + RandomNumberGenerator& rng); + + Client_Key_Exchange(const std::vector& buf, + const Handshake_State& state, + const Private_Key* server_rsa_kex_key, + Credentials_Manager& creds, + const Policy& policy, + RandomNumberGenerator& rng); + + private: + std::vector serialize() const override { return m_key_material; } + + std::vector m_key_material; + secure_vector m_pre_master; + std::optional m_psk_identity; +}; + +/** +* Certificate Message of TLS 1.2 +*/ +class BOTAN_UNSTABLE_API Certificate_12 final : public Handshake_Message /* NOLINT(*-special-member-functions) */ { + public: + Handshake_Type type() const override { return Handshake_Type::Certificate; } + + const std::vector& cert_chain() const { return m_certs; } + + size_t count() const; + + bool empty() const { return m_certs.empty(); } + + Certificate_12(Handshake_IO& io, Handshake_Hash& hash, const std::vector& certs); + + Certificate_12(const std::vector& buf, const Policy& policy); + + ~Certificate_12() override; + + std::vector serialize() const override; + + private: + std::vector m_certs; +}; + +/** +* Certificate Request Message (TLS 1.2) +*/ +class BOTAN_UNSTABLE_API Certificate_Request_12 final : public Handshake_Message { + public: + Handshake_Type type() const override; + + const std::vector& acceptable_cert_types() const; + + const std::vector& acceptable_CAs() const; + + const std::vector& signature_schemes() const; + + Certificate_Request_12(Handshake_IO& io, + Handshake_Hash& hash, + const Policy& policy, + const std::vector& allowed_cas); + + explicit Certificate_Request_12(const std::vector& buf); + + ~Certificate_Request_12() override; + + Certificate_Request_12(const Certificate_Request_12&) = delete; + Certificate_Request_12(Certificate_Request_12&&) = delete; + Certificate_Request_12& operator=(const Certificate_Request_12& other) = delete; + Certificate_Request_12& operator=(Certificate_Request_12&& other) = delete; + + std::vector serialize() const override; + + private: + std::vector m_names; + std::vector m_cert_key_types; + std::vector m_schemes; +}; + +/** +* Certificate Verify Message +*/ +class BOTAN_UNSTABLE_API Certificate_Verify_12 final : public Certificate_Verify { + public: + using Certificate_Verify::Certificate_Verify; + + Certificate_Verify_12(Handshake_IO& io, + Handshake_State& state, + const Policy& policy, + RandomNumberGenerator& rng, + const Private_Key* key); + + /** + * Check the signature on a certificate verify message + * @param cert the purported certificate + * @param state the handshake state + * @param policy the TLS policy + */ + bool verify(const X509_Certificate& cert, const Handshake_State& state, const Policy& policy) const; +}; + +/** +* Certificate Status (RFC 6066) +*/ +class BOTAN_UNSTABLE_API Certificate_Status_12 final : public Certificate_Status { + public: + /* + * Create a Certificate_Status message using an already DER encoded OCSP response. + */ + Certificate_Status_12(Handshake_IO& io, Handshake_Hash& hash, std::vector raw_response_bytes); +}; + +class BOTAN_UNSTABLE_API Finished_12 final : public Finished { + public: + using Finished::Finished; + Finished_12(Handshake_IO& io, Handshake_State& state, Connection_Side side); + + bool verify(const Handshake_State& state, Connection_Side side) const; +}; + +/** +* Hello Request Message +*/ +class BOTAN_UNSTABLE_API Hello_Request final : public Handshake_Message { + public: + Handshake_Type type() const override { return Handshake_Type::HelloRequest; } + + explicit Hello_Request(Handshake_IO& io); + explicit Hello_Request(const std::vector& buf); + + private: + std::vector serialize() const override; +}; + +/** +* Server Key Exchange Message +*/ +class BOTAN_UNSTABLE_API Server_Key_Exchange final : public Handshake_Message { + public: + Handshake_Type type() const override { return Handshake_Type::ServerKeyExchange; } + + const std::vector& params() const { return m_params; } + + bool verify(const Public_Key& server_key, const Handshake_State& state, const Policy& policy) const; + + // Only valid for certain kex types + const PK_Key_Agreement_Key& server_kex_key() const; + + /** + * @returns the agreed upon KEX group or std::nullopt if the KEX type does + * not depend on a group + */ + const std::optional& shared_group() const { return m_shared_group; } + + Server_Key_Exchange(Handshake_IO& io, + Handshake_State& state, + const Policy& policy, + Credentials_Manager& creds, + RandomNumberGenerator& rng, + const Private_Key* signing_key = nullptr); + + Server_Key_Exchange(const std::vector& buf, + Kex_Algo kex_alg, + Auth_Method sig_alg, + Protocol_Version version); + + ~Server_Key_Exchange() override; + + Server_Key_Exchange(const Server_Key_Exchange& other) = delete; + Server_Key_Exchange(Server_Key_Exchange&& other) = delete; + Server_Key_Exchange& operator=(const Server_Key_Exchange& other) = delete; + Server_Key_Exchange& operator=(Server_Key_Exchange&& other) = delete; + + private: + std::vector serialize() const override; + + std::unique_ptr m_kex_key; + std::optional m_shared_group; + + std::vector m_params; + + std::vector m_signature; + Signature_Scheme m_scheme; +}; + +/** +* Server Hello Done Message +*/ +class BOTAN_UNSTABLE_API Server_Hello_Done final : public Handshake_Message { + public: + Handshake_Type type() const override { return Handshake_Type::ServerHelloDone; } + + explicit Server_Hello_Done(Handshake_IO& io, Handshake_Hash& hash); + explicit Server_Hello_Done(const std::vector& buf); + + private: + std::vector serialize() const override; +}; + +/** +* New Session Ticket Message +*/ +class BOTAN_UNSTABLE_API New_Session_Ticket_12 final : public Handshake_Message { + public: + Handshake_Type type() const override { return Handshake_Type::NewSessionTicket; } + + uint32_t ticket_lifetime_hint() const { return m_ticket_lifetime_hint; } + + const Session_Ticket& ticket() const { return m_ticket; } + + New_Session_Ticket_12(Handshake_IO& io, + Handshake_Hash& hash, + Session_Ticket ticket, + uint32_t lifetime_in_seconds); + + New_Session_Ticket_12(Handshake_IO& io, Handshake_Hash& hash); + + explicit New_Session_Ticket_12(const std::vector& buf); + + std::vector serialize() const override; + + private: + uint32_t m_ticket_lifetime_hint = 0; + Session_Ticket m_ticket; +}; + +/** +* Change Cipher Spec +*/ +class BOTAN_UNSTABLE_API Change_Cipher_Spec final : public Handshake_Message { + public: + Handshake_Type type() const override { return Handshake_Type::HandshakeCCS; } + + std::vector serialize() const override { return std::vector(1, 1); } +}; + +} // namespace TLS +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_null/info.txt botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_null/info.txt --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_null/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_null/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,19 @@ +# Disable this module by default +load_on request + + +TLS_NULL -> 20240830 + + + +name -> "TLS 1.2 Null cipher" +brief -> "Null cipher + HMAC AEAD mode of operation for TLS 1.2" + + + +tls_null.h + + + +hmac + diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_null/tls_null.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_null/tls_null.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_null/tls_null.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_null/tls_null.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,154 @@ +/* +* TLS Null Cipher Handling +* (C) 2024 Sebastian Ahrens, Dirk Dobkowitz, André Schomburg (Volkswagen AG) +* (C) 2024 Lars Dürkop (CARIAD SE) +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include + +namespace Botan::TLS { + +/* +* TLS_NULL_HMAC_AEAD_Mode Constructor +*/ +TLS_NULL_HMAC_AEAD_Mode::TLS_NULL_HMAC_AEAD_Mode(std::unique_ptr mac, size_t mac_keylen) : + m_mac_name(mac->name()), m_mac_keylen(mac_keylen), m_tag_size(mac->output_length()), m_mac(std::move(mac)) {} + +void TLS_NULL_HMAC_AEAD_Mode::clear() { + m_key.clear(); + m_ad.clear(); + mac().clear(); +} + +void TLS_NULL_HMAC_AEAD_Mode::reset() { + m_ad.clear(); + // The base AEAD_Mode contract permits reset() before the first key has + // been set; only re-key the MAC if there is a key to re-key with. + if(!m_key.empty()) { + mac().set_key(m_key); + } +} + +std::string TLS_NULL_HMAC_AEAD_Mode::name() const { + return fmt("TLS_NULL({})", m_mac_name); +} + +size_t TLS_NULL_HMAC_AEAD_Mode::update_granularity() const { + return 1; +} + +size_t TLS_NULL_HMAC_AEAD_Mode::ideal_granularity() const { + return 1; +} + +bool TLS_NULL_HMAC_AEAD_Mode::valid_nonce_length(size_t nl) const { + return nl == 0; +} + +Key_Length_Specification TLS_NULL_HMAC_AEAD_Mode::key_spec() const { + return Key_Length_Specification(m_mac_keylen); +} + +bool TLS_NULL_HMAC_AEAD_Mode::has_keying_material() const { + return mac().has_keying_material(); +} + +size_t TLS_NULL_HMAC_AEAD_Mode::mac_keylen() const { + return m_mac_keylen; +} + +MessageAuthenticationCode& TLS_NULL_HMAC_AEAD_Mode::mac() const { + BOTAN_ASSERT_NONNULL(m_mac); + return *m_mac; +} + +void TLS_NULL_HMAC_AEAD_Mode::key_schedule(std::span key) { + if(key.size() != m_mac_keylen) { + throw Invalid_Key_Length(name(), key.size()); + } + m_key.assign(key.begin(), key.end()); + reset(); +} + +void TLS_NULL_HMAC_AEAD_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) { + BOTAN_UNUSED(nonce); + + if(!valid_nonce_length(nonce_len)) { + throw Invalid_IV_Length(name(), nonce_len); + } + + m_processed = false; + + // AEAD_Mode contract: AD set via set_associated_data persists across + // messages until reset. finish_msg calls mac().final() which clears the + // internal state, so we re-feed the cached AD at the start of each + // message rather than once at set_associated_data time. + if(!m_ad.empty()) { + mac().update(m_ad); + } +} + +size_t TLS_NULL_HMAC_AEAD_Mode::process_msg(uint8_t buf[], size_t sz) { + // The TLS record code path MACs each record in a single call (via + // finish_msg -> process). A second invocation between start_msg and + // finish_msg would feed additional bytes into the same HMAC instance, + // producing a tag covering more than the intended record body. + BOTAN_ASSERT_NOMSG(!m_processed); + m_processed = true; + + mac().update(buf, sz); + return sz; +} + +void TLS_NULL_HMAC_AEAD_Mode::set_associated_data_n(size_t idx, std::span ad) { + BOTAN_ARG_CHECK(idx == 0, "TLS 1.2 NULL/HMAC: cannot handle non-zero index in set_associated_data_n"); + BOTAN_ARG_CHECK(ad.size() == 13, "TLS 1.2 NULL/HMAC: invalid TLS AEAD associated data length"); + + // Cache the AD; the actual MAC update happens at start_msg so the AD + // persists across messages per the AEAD_Mode contract. + m_ad.assign(ad.begin(), ad.end()); +} + +void TLS_NULL_HMAC_AEAD_Encryption::set_associated_data_n(size_t idx, std::span ad) { + TLS_NULL_HMAC_AEAD_Mode::set_associated_data_n(idx, ad); +} + +size_t TLS_NULL_HMAC_AEAD_Encryption::output_length(size_t input_length) const { + return input_length + tag_size(); +} + +void TLS_NULL_HMAC_AEAD_Encryption::finish_msg(secure_vector& buffer, size_t offset) { + process(std::span{buffer}.subspan(offset)); + buffer.resize(buffer.size() + tag_size()); + mac().final(std::span{buffer}.last(tag_size())); +} + +size_t TLS_NULL_HMAC_AEAD_Decryption::output_length(size_t input_length) const { + return input_length - tag_size(); +} + +void TLS_NULL_HMAC_AEAD_Decryption::finish_msg(secure_vector& buffer, size_t offset) { + BOTAN_ARG_CHECK(buffer.size() >= tag_size() + offset, + "TLS_NULL_HMAC_AEAD_Decryption needs at least tag_size() bytes in final buffer"); + + const auto data_and_tag = std::span{buffer}.subspan(offset); + const auto data = data_and_tag.first(data_and_tag.size() - tag_size()); + const auto tag = data_and_tag.subspan(data.size()); + + process(data); + if(!mac().verify_mac(tag)) { + throw TLS_Exception(Alert::BadRecordMac, "Message authentication failure"); + } + + buffer.resize(buffer.size() - tag_size()); +} + +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_null/tls_null.h botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_null/tls_null.h --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_null/tls_null.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_null/tls_null.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,112 @@ +/* +* TLS Null Cipher Handling +* (C) 2024 Sebastian Ahrens, Dirk Dobkowitz, André Schomburg (Volkswagen AG) +* (C) 2024 Lars Dürkop (CARIAD SE) +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TLS_NULL_HMAC_AEAD_H_ +#define BOTAN_TLS_NULL_HMAC_AEAD_H_ + +#include +#include +#include + +namespace Botan::TLS { + +/** +* TLS NULL+HMAC AEAD base class (GenericStreamCipher in TLS spec) +*/ +class BOTAN_TEST_API TLS_NULL_HMAC_AEAD_Mode : public AEAD_Mode { + public: + std::string name() const final; + + void set_associated_data_n(size_t idx, std::span ad) override; + + size_t update_granularity() const final; + + size_t ideal_granularity() const final; + + Key_Length_Specification key_spec() const final; + + bool valid_nonce_length(size_t nl) const final; + + size_t tag_size() const final { return m_tag_size; } + + void clear() final; + + void reset() final; + + bool has_keying_material() const final; + + protected: + TLS_NULL_HMAC_AEAD_Mode(std::unique_ptr mac, size_t mac_keylen); + + size_t mac_keylen() const; + + MessageAuthenticationCode& mac() const; + + private: + void start_msg(const uint8_t nonce[], size_t nonce_len) final; + size_t process_msg(uint8_t buf[], size_t sz) final; + + void key_schedule(std::span key) final; + + const std::string m_mac_name; + size_t m_mac_keylen; + size_t m_tag_size; + + secure_vector m_key; + std::unique_ptr m_mac; + + // Per the AEAD_Mode contract, associated data set via + // set_associated_data persists across messages until reset. finish_msg + // calls mac().final() which clears the internal state, so we cache the + // AD here and re-feed it at start_msg time. + std::vector m_ad; + + // Single-call contract for process_msg: the TLS record code path is + // expected to MAC the entire record in one shot via finish_msg. A second + // process_msg call between start_msg and finish_msg would re-feed bytes + // to the MAC and produce a tag covering them twice; this flag is + // asserted to catch any such future misuse. + bool m_processed = false; +}; + +/** +* TLS_NULL_HMAC_AEAD Encryption +*/ +class BOTAN_TEST_API TLS_NULL_HMAC_AEAD_Encryption final : public TLS_NULL_HMAC_AEAD_Mode { + public: + TLS_NULL_HMAC_AEAD_Encryption(std::unique_ptr mac, const size_t mac_keylen) : + TLS_NULL_HMAC_AEAD_Mode(std::move(mac), mac_keylen) {} + + void set_associated_data_n(size_t idx, std::span ad) override; + + size_t output_length(size_t input_length) const override; + + size_t minimum_final_size() const override { return 0; } + + private: + void finish_msg(secure_vector& final_block, size_t offset = 0) override; +}; + +/** +* TLS_NULL_HMAC_AEAD Decryption +*/ +class BOTAN_TEST_API TLS_NULL_HMAC_AEAD_Decryption final : public TLS_NULL_HMAC_AEAD_Mode { + public: + TLS_NULL_HMAC_AEAD_Decryption(std::unique_ptr mac, const size_t mac_keylen) : + TLS_NULL_HMAC_AEAD_Mode(std::move(mac), mac_keylen) {} + + size_t output_length(size_t input_length) const override; + + size_t minimum_final_size() const override { return tag_size(); } + + void finish_msg(secure_vector& final_block, size_t offset = 0) override; +}; + +} // namespace Botan::TLS + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_record.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_record.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_record.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_record.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,41 +9,50 @@ #include +#include +#include +#include #include -#include #include #include -#include +#include #include #include #include -#include #if defined(BOTAN_HAS_TLS_CBC) #include #endif +#if defined(BOTAN_HAS_TLS_NULL) + #include +#endif + namespace Botan::TLS { +Connection_Cipher_State::~Connection_Cipher_State() = default; + Connection_Cipher_State::Connection_Cipher_State(Protocol_Version version, Connection_Side side, bool our_side, const Ciphersuite& suite, const Session_Keys& keys, bool uses_encrypt_then_mac) { + // NOLINTBEGIN(*-prefer-member-initializer) m_nonce_format = suite.nonce_format(); m_nonce_bytes_from_record = suite.nonce_bytes_from_record(version); m_nonce_bytes_from_handshake = suite.nonce_bytes_from_handshake(); const secure_vector& aead_key = keys.aead_key(side); m_nonce = keys.nonce(side); + // NOLINTEND(*-prefer-member-initializer) BOTAN_ASSERT_NOMSG(m_nonce.size() == m_nonce_bytes_from_handshake); if(nonce_format() == Nonce_Format::CBC_MODE) { #if defined(BOTAN_HAS_TLS_CBC) // legacy CBC+HMAC mode - auto mac = MessageAuthenticationCode::create_or_throw("HMAC(" + suite.mac_algo() + ")"); + auto mac = MessageAuthenticationCode::create_or_throw(fmt("HMAC({})", suite.mac_algo())); auto cipher = BlockCipher::create_or_throw(suite.cipher_algo()); if(our_side) { @@ -66,6 +75,18 @@ BOTAN_UNUSED(uses_encrypt_then_mac); throw Internal_Error("Negotiated disabled TLS CBC+HMAC ciphersuite"); #endif + } else if(nonce_format() == Nonce_Format::NULL_CIPHER) { +#if defined(BOTAN_HAS_TLS_NULL) + auto mac = MessageAuthenticationCode::create_or_throw(fmt("HMAC({})", suite.mac_algo())); + + if(our_side) { + m_aead = std::make_unique(std::move(mac), suite.mac_keylen()); + } else { + m_aead = std::make_unique(std::move(mac), suite.mac_keylen()); + } +#else + throw Internal_Error("Negotiated disabled TLS NULL ciphersuite"); +#endif } else { m_aead = AEAD_Mode::create_or_throw(suite.cipher_algo(), our_side ? Cipher_Dir::Encryption : Cipher_Dir::Decryption); @@ -76,17 +97,16 @@ std::vector Connection_Cipher_State::aead_nonce(uint64_t seq, RandomNumberGenerator& rng) { switch(m_nonce_format) { + case Nonce_Format::NULL_CIPHER: { + return std::vector{}; + } case Nonce_Format::CBC_MODE: { - if(!m_nonce.empty()) { - std::vector nonce; - nonce.swap(m_nonce); - return nonce; - } std::vector nonce(nonce_bytes_from_record()); rng.randomize(nonce.data(), nonce.size()); return nonce; } case Nonce_Format::AEAD_XOR_12: { + BOTAN_ASSERT_NOMSG(m_nonce.size() == 12); std::vector nonce(12); store_be(seq, nonce.data() + 4); xor_buf(nonce, m_nonce.data(), m_nonce.size()); @@ -95,7 +115,7 @@ case Nonce_Format::AEAD_IMPLICIT_4: { BOTAN_ASSERT_NOMSG(m_nonce.size() == 4); std::vector nonce(12); - copy_mem(&nonce[0], m_nonce.data(), 4); + copy_mem(&nonce[0], m_nonce.data(), 4); // NOLINT(*container-data-pointer) store_be(seq, &nonce[nonce_bytes_from_handshake()]); return nonce; } @@ -106,12 +126,10 @@ std::vector Connection_Cipher_State::aead_nonce(const uint8_t record[], size_t record_len, uint64_t seq) { switch(m_nonce_format) { + case Nonce_Format::NULL_CIPHER: { + return std::vector{}; + } case Nonce_Format::CBC_MODE: { - if(nonce_bytes_from_record() == 0 && !m_nonce.empty()) { - std::vector nonce; - nonce.swap(m_nonce); - return nonce; - } if(record_len < nonce_bytes_from_record()) { throw Decoding_Error("Invalid CBC packet too short to be valid"); } @@ -119,6 +137,7 @@ return nonce; } case Nonce_Format::AEAD_XOR_12: { + BOTAN_ASSERT_NOMSG(m_nonce.size() == 12); std::vector nonce(12); store_be(seq, nonce.data() + 4); xor_buf(nonce, m_nonce.data(), m_nonce.size()); @@ -130,7 +149,7 @@ throw Decoding_Error("Invalid AEAD packet too short to be valid"); } std::vector nonce(12); - copy_mem(&nonce[0], m_nonce.data(), 4); + copy_mem(&nonce[0], m_nonce.data(), 4); // NOLINT(*container-data-pointer) copy_mem(&nonce[nonce_bytes_from_handshake()], record, nonce_bytes_from_record()); return nonce; } @@ -145,7 +164,7 @@ uint16_t msg_length) { std::vector ad(13); - store_be(msg_sequence, &ad[0]); + store_be(msg_sequence, &ad[0]); // NOLINT(*container-data-pointer) ad[8] = static_cast(msg_type); ad[9] = version.major_version(); ad[10] = version.minor_version(); @@ -299,7 +318,7 @@ const get_cipherstate_fn& get_cipherstate) { if(readbuf.size() < TLS_HEADER_SIZE) { // header incomplete - if(size_t needed = fill_buffer_to(readbuf, input, input_len, consumed, TLS_HEADER_SIZE)) { + if(const size_t needed = fill_buffer_to(readbuf, input, input_len, consumed, TLS_HEADER_SIZE)) { return Record_Header(needed); } @@ -310,16 +329,15 @@ Verify that the record type and record version are within some expected range, so we can quickly reject totally invalid packets. - The version check is a little hacky but given how TLS 1.3 versioning works - this is probably safe + Unfortunately we cannot be more strict about the record number than just + checking the major version, at least at this level, due to this requirement + in RFC 7568 - - The first byte is the record version which in TLS 1.2 is always in [20..23) - - The second byte is the TLS major version which is effectively fossilized at 3 - - The third byte is the TLS minor version which (due to TLS 1.3 versioning changes) - will never be more than 3 (signifying TLS 1.2) + TLS servers MUST accept any value {03,XX} (including {03,00}) as + the record layer version number for ClientHello */ const bool bad_record_type = readbuf[0] < 20 || readbuf[0] > 23; - const bool bad_record_version = readbuf[1] != 3 || readbuf[2] >= 4; + const bool bad_record_version = readbuf[1] != 3; if(bad_record_type || bad_record_version) { // We know we read up to at least the 5 byte TLS header @@ -359,7 +377,7 @@ throw TLS_Exception(Alert::DecodeError, "Received a completely empty record"); } - if(size_t needed = fill_buffer_to(readbuf, input, input_len, consumed, TLS_HEADER_SIZE + record_size)) { + if(const size_t needed = fill_buffer_to(readbuf, input, input_len, consumed, TLS_HEADER_SIZE + record_size)) { return Record_Header(needed); } @@ -370,7 +388,7 @@ uint16_t epoch = 0; uint64_t sequence = 0; - if(sequence_numbers) { + if(sequence_numbers != nullptr) { sequence = sequence_numbers->next_read_sequence(); epoch = sequence_numbers->current_read_epoch(); } else { @@ -392,7 +410,7 @@ decrypt_record(recbuf, &readbuf[TLS_HEADER_SIZE], record_size, sequence, version, type, *cs); - if(sequence_numbers) { + if(sequence_numbers != nullptr) { sequence_numbers->read_accept(sequence); } @@ -410,7 +428,7 @@ bool allow_epoch0_restart) { if(readbuf.size() < DTLS_HEADER_SIZE) { // header incomplete - if(fill_buffer_to(readbuf, input, input_len, consumed, DTLS_HEADER_SIZE)) { + if(fill_buffer_to(readbuf, input, input_len, consumed, DTLS_HEADER_SIZE) != 0) { readbuf.clear(); return Record_Header(0); } @@ -433,7 +451,7 @@ return Record_Header(0); } - if(fill_buffer_to(readbuf, input, input_len, consumed, DTLS_HEADER_SIZE + record_size)) { + if(fill_buffer_to(readbuf, input, input_len, consumed, DTLS_HEADER_SIZE + record_size) != 0) { // Truncated packet? readbuf.clear(); return Record_Header(0); @@ -446,7 +464,7 @@ const uint64_t sequence = load_be(&readbuf[3], 0); const uint16_t epoch = (sequence >> 48); - const bool already_seen = sequence_numbers && sequence_numbers->already_seen(sequence); + const bool already_seen = sequence_numbers != nullptr && sequence_numbers->already_seen(sequence); if(already_seen && !(epoch == 0 && allow_epoch0_restart)) { readbuf.clear(); @@ -457,7 +475,7 @@ // Unencrypted initial handshake recbuf.assign(readbuf.begin() + DTLS_HEADER_SIZE, readbuf.begin() + DTLS_HEADER_SIZE + record_size); readbuf.clear(); - if(sequence_numbers) { + if(sequence_numbers != nullptr) { sequence_numbers->read_accept(sequence); } return Record_Header(sequence, version, type); @@ -475,7 +493,7 @@ return Record_Header(0); } - if(sequence_numbers) { + if(sequence_numbers != nullptr) { sequence_numbers->read_accept(sequence); } diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_record.h botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_record.h --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_record.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_record.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,18 +9,24 @@ #ifndef BOTAN_TLS_RECORDS_H_ #define BOTAN_TLS_RECORDS_H_ -#include +#include +#include #include #include #include -#include -#include #include +#include #include +namespace Botan { + +class AEAD_Mode; +class RandomNumberGenerator; + +} // namespace Botan + namespace Botan::TLS { -class Callbacks; class Ciphersuite; class Session_Keys; @@ -41,6 +47,14 @@ const Session_Keys& keys, bool uses_encrypt_then_mac); + ~Connection_Cipher_State(); + + Connection_Cipher_State(const Connection_Cipher_State& other) = delete; + Connection_Cipher_State(Connection_Cipher_State&& other) = delete; + + Connection_Cipher_State& operator=(const Connection_Cipher_State& other) = delete; + Connection_Cipher_State& operator=(Connection_Cipher_State&& other) = delete; + AEAD_Mode& aead() { BOTAN_ASSERT_NONNULL(m_aead.get()); return *m_aead; @@ -72,8 +86,7 @@ Record_Header(uint64_t sequence, Protocol_Version version, Record_Type type) : m_needed(0), m_sequence(sequence), m_version(version), m_type(type) {} - Record_Header(size_t needed) : - m_needed(needed), m_sequence(0), m_version(Protocol_Version()), m_type(Record_Type::Invalid) {} + explicit Record_Header(size_t needed) : m_needed(needed), m_sequence(0), m_type(Record_Type::Invalid) {} size_t needed() const { return m_needed; } diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_seq_numbers.h botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_seq_numbers.h --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_seq_numbers.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_seq_numbers.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,12 +8,14 @@ #ifndef BOTAN_TLS_SEQ_NUMBERS_H_ #define BOTAN_TLS_SEQ_NUMBERS_H_ +#include #include +#include #include namespace Botan::TLS { -class Connection_Sequence_Numbers { +class Connection_Sequence_Numbers /* NOLINT(*-special-member-functions) */ { public: virtual ~Connection_Sequence_Numbers() = default; @@ -34,7 +36,7 @@ class Stream_Sequence_Numbers final : public Connection_Sequence_Numbers { public: - Stream_Sequence_Numbers() { Stream_Sequence_Numbers::reset(); } + Stream_Sequence_Numbers() : m_write_seq_no(0), m_read_seq_no(0), m_read_epoch(0), m_write_epoch(0) {} void reset() override { m_write_seq_no = 0; @@ -57,13 +59,23 @@ uint16_t current_write_epoch() const override { return m_write_epoch; } - uint64_t next_write_sequence(uint16_t) override { return m_write_seq_no++; } + uint64_t next_write_sequence(uint16_t /*epoch*/) override { + if(m_write_seq_no == std::numeric_limits::max()) { + throw Invalid_State("TLS 1.2 write sequence number overflow"); + } + return m_write_seq_no++; + } uint64_t next_read_sequence() override { return m_read_seq_no; } - bool already_seen(uint64_t) const override { return false; } + bool already_seen(uint64_t /*seq*/) const override { return false; } - void read_accept(uint64_t) override { m_read_seq_no++; } + void read_accept(uint64_t /*seq*/) override { + if(m_read_seq_no == std::numeric_limits::max()) { + throw Invalid_State("TLS 1.2 read sequence number overflow"); + } + m_read_seq_no++; + } private: uint64_t m_write_seq_no; @@ -99,6 +111,9 @@ uint64_t next_write_sequence(uint16_t epoch) override { auto i = m_write_seqs.find(epoch); BOTAN_ASSERT(i != m_write_seqs.end(), "Found epoch"); + if(i->second > 0x0000FFFFFFFFFFFF) { + throw Invalid_State("DTLS write sequence number overflow"); + } return (static_cast(epoch) << 48) | i->second++; } diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_server_impl_12.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_server_impl_12.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_server_impl_12.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_server_impl_12.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,13 +8,18 @@ #include +#include #include +#include #include -#include -#include +#include +#include #include +#include #include #include +#include +#include namespace Botan::TLS { @@ -40,6 +45,16 @@ bool is_a_resumption() const { return m_is_a_resumption; } + std::vector peer_cert_chain() const override { + if(!m_resume_peer_certs.empty()) { + return m_resume_peer_certs; + } + if(client_certs() != nullptr) { + return client_certs()->cert_chain(); + } + return {}; + } + private: // Used by the server only, in case of RSA key exchange. std::shared_ptr m_server_rsa_kex_key; @@ -130,18 +145,47 @@ const bool have_shared_dh_group = (policy.choose_key_exchange_group(client_hello.supported_dh_groups(), {}) != Group_Params::NONE); + const std::unordered_set client_suite_set(client_suites.begin(), client_suites.end()); + + const std::vector allowed_sig_schemes = policy.allowed_signature_schemes(); + const std::vector client_sig_methods = client_hello.signature_schemes(); + + // Algorithm names (eg "RSA", "ECDSA") for which the client offered at least + // one signature_scheme that is available, is in our policy, and uses a hash we accept. + const std::unordered_set client_sig_algs = [&] { + std::unordered_set allowed_codes; + allowed_codes.reserve(allowed_sig_schemes.size()); + for(auto s : allowed_sig_schemes) { + allowed_codes.insert(static_cast(s.wire_code())); + } + std::unordered_set result; + for(const Signature_Scheme scheme : client_sig_methods) { + if(!scheme.is_available()) { + continue; + } + if(!allowed_codes.contains(static_cast(scheme.wire_code()))) { + continue; + } + if(!policy.allowed_signature_hash(scheme.hash_function_name())) { + continue; + } + result.insert(scheme.algorithm_name()); + } + return result; + }(); + /* Walk down one list in preference order */ - std::vector pref_list = server_suites; - std::vector other_list = client_suites; + const std::vector& pref_list = our_choice ? server_suites : client_suites; - if(!our_choice) { - std::swap(pref_list, other_list); - } + auto in_other_list = [&](uint16_t suite_id) { + // server_suites is small and policy-controlled + return our_choice ? client_suite_set.contains(suite_id) : value_exists(server_suites, suite_id); + }; for(auto suite_id : pref_list) { - if(!value_exists(other_list, suite_id)) { + if(!in_other_list(suite_id)) { continue; } @@ -160,39 +204,24 @@ } // For non-anon ciphersuites - if(suite->signature_used()) { - const std::string sig_algo = suite->sig_algo(); + if(suite->is_certificate_required()) { + const std::string cert_algo = suite->signature_used() ? suite->sig_algo() : "RSA"; // Do we have any certificates for this sig? - if(!cert_chains.contains(sig_algo)) { + if(!cert_chains.contains(cert_algo)) { continue; } + } - const std::vector allowed = policy.allowed_signature_schemes(); - - std::vector client_sig_methods = client_hello.signature_schemes(); - - /* - Contrary to the wording of draft-ietf-tls-md5-sha1-deprecate we do - not enforce that clients do not offer support SHA-1 or MD5 - signatures; we just ignore it. - */ - bool we_support_some_hash_by_client = false; - - for(Signature_Scheme scheme : client_sig_methods) { - if(!scheme.is_available()) { - continue; - } - - if(scheme.algorithm_name() == suite->sig_algo() && - policy.allowed_signature_hash(scheme.hash_function_name())) { - we_support_some_hash_by_client = true; - } - } - - if(we_support_some_hash_by_client == false) { - throw TLS_Exception(Alert::HandshakeFailure, - "Policy does not accept any hash function supported by client"); + if(suite->signature_used()) { + // The client's signature_algorithms list might not include a scheme + // matching this suite's sig_algo (e.g. the client offered ECDSA + // schemes but we're considering an RSA suite). That's just a + // mismatch on this candidate, not a handshake-fatal condition - try + // the next suite. The final "Can't agree on a ciphersuite" throw + // below fires only if no candidate works. + if(!client_sig_algs.contains(suite->sig_algo())) { + continue; } } @@ -214,16 +243,16 @@ std::map> get_server_certs( std::string_view hostname, const std::vector& cert_sig_schemes, Credentials_Manager& creds) { - const char* cert_types[] = {"RSA", "ECDSA", "DSA", nullptr}; + const std::vector cert_types = {"RSA", "ECDSA"}; std::map> cert_chains; - for(size_t i = 0; cert_types[i]; ++i) { + for(const auto& cert_type : cert_types) { const std::vector certs = creds.cert_chain_single_type( - cert_types[i], to_algorithm_identifiers(cert_sig_schemes), "tls-server", std::string(hostname)); + cert_type, to_algorithm_identifiers(cert_sig_schemes), "tls-server", std::string(hostname)); if(!certs.empty()) { - cert_chains[cert_types[i]] = certs; + cert_chains[cert_type] = certs; } } @@ -259,25 +288,13 @@ return state; } -std::vector Server_Impl_12::get_peer_cert_chain(const Handshake_State& state_base) const { - const Server_Handshake_State& state = dynamic_cast(state_base); - if(!state.resume_peer_certs().empty()) { - return state.resume_peer_certs(); - } - - if(state.client_certs()) { - return state.client_certs()->cert_chain(); - } - return std::vector(); -} - /* * Send a hello request to the client */ void Server_Impl_12::initiate_handshake(Handshake_State& state, bool force_full_renegotiation) { dynamic_cast(state).set_allow_session_resumption(!force_full_renegotiation); - Hello_Request hello_req(state.handshake_io()); + const Hello_Request hello_req(state.handshake_io()); } namespace { @@ -338,13 +355,12 @@ /* * Process a Client Hello Message */ -void Server_Impl_12::process_client_hello_msg(const Handshake_State* active_state, - Server_Handshake_State& pending_state, +void Server_Impl_12::process_client_hello_msg(Server_Handshake_State& pending_state, const std::vector& contents, bool epoch0_restart) { - BOTAN_ASSERT_IMPLICATION(epoch0_restart, active_state != nullptr, "Can't restart with a dead connection"); + BOTAN_ASSERT_IMPLICATION(epoch0_restart, active_state().has_value(), "Can't restart with a dead connection"); - const bool initial_handshake = epoch0_restart || !active_state; + const bool initial_handshake = epoch0_restart || !active_state().has_value(); if(initial_handshake == false && policy().allow_client_initiated_renegotiation() == false) { if(policy().abort_connection_on_undesired_renegotiation()) { @@ -364,7 +380,7 @@ throw TLS_Exception(Alert::UnexpectedMessage, "Have data remaining in buffer after ClientHello"); } - pending_state.client_hello(new Client_Hello_12(contents)); + pending_state.client_hello(std::make_unique(contents)); const Protocol_Version client_offer = pending_state.client_hello()->legacy_version(); const bool datagram = client_offer.is_datagram_protocol(); @@ -394,7 +410,7 @@ const Protocol_Version negotiated_version = select_version(policy(), client_offer, - active_state ? active_state->version() : Protocol_Version(), + active_state().has_value() ? active_state()->version() : Protocol_Version(), pending_state.client_hello()->supported_versions()); pending_state.set_version(negotiated_version); @@ -413,16 +429,17 @@ if(!cookie_secret.empty()) { const std::string client_identity = callbacks().tls_peer_network_identity(); - Hello_Verify_Request verify(pending_state.client_hello()->cookie_input_data(), client_identity, cookie_secret); + const Hello_Verify_Request verify( + pending_state.client_hello()->cookie_input_data(), client_identity, cookie_secret); - if(pending_state.client_hello()->cookie() != verify.cookie()) { + if(!CT::is_equal(pending_state.client_hello()->cookie(), verify.cookie()).as_bool()) { if(epoch0_restart) { pending_state.handshake_io().send_under_epoch(verify, 0); } else { pending_state.handshake_io().send(verify); } - pending_state.client_hello(static_cast(nullptr)); + pending_state.client_hello(nullptr); pending_state.set_expected_next(Handshake_Type::ClientHello); return; } @@ -438,6 +455,29 @@ secure_renegotiation_check(pending_state.client_hello()); + // RFC 7627 / RFC 9325 4.4: optionally require Extended Master Secret + if(policy().require_extended_master_secret() && !pending_state.client_hello()->supports_extended_master_secret()) { + throw TLS_Exception(Alert::HandshakeFailure, + "Policy requires the Extended Master Secret extension but the client did not send it"); + } + + // RFC 7627 5.3 has an explicit MUST regarding EMS mismatch on resumption + // + // "If the original session used the 'extended_master_secret' + // extension but the new ClientHello does not contain it, the + // server MUST abort the abbreviated handshake." + // + // There is apparently no RFC requirement that a client must not drop EMS between the + // initial negotiation and a renegotiation... but there is also no RFC requirement + // that we must accept it. So we don't. + if(const auto& active = active_state()) { + const bool ems_pending = pending_state.client_hello()->supports_extended_master_secret(); + if(active->supports_extended_master_secret() == true && ems_pending == false) { + throw TLS_Exception(Alert::HandshakeFailure, + "Renegotiation ClientHello dropped the Extended Master Secret extension"); + } + } + callbacks().tls_examine_extensions( pending_state.client_hello()->extensions(), Connection_Side::Client, Handshake_Type::ClientHello); @@ -451,7 +491,14 @@ m_next_protocol = ""; if(pending_state.client_hello()->supports_alpn()) { - m_next_protocol = callbacks().tls_server_choose_app_protocol(pending_state.client_hello()->next_protocols()); + const auto offered = pending_state.client_hello()->next_protocols(); + m_next_protocol = callbacks().tls_server_choose_app_protocol(offered); + // RFC 7301 3.2: if a protocol is selected, the server MUST select one + // of the protocols advertised by the client. An empty return signals + // "no ALPN" and is allowed. + if(!m_next_protocol.empty() && !value_exists(offered, m_next_protocol)) { + throw TLS_Exception(Alert::InternalError, "Application chose an ALPN protocol that the client did not offer"); + } } if(session_info.has_value()) { @@ -464,7 +511,7 @@ void Server_Impl_12::process_certificate_msg(Server_Handshake_State& pending_state, const std::vector& contents) { - pending_state.client_certs(new Certificate_12(contents, policy())); + pending_state.client_certs(std::make_unique(contents, policy())); // CERTIFICATE_REQUIRED would make more sense but BoGo expects handshake failure alert if(pending_state.client_certs()->empty() && policy().require_client_certificate_authentication()) { @@ -482,8 +529,8 @@ pending_state.set_expected_next(Handshake_Type::HandshakeCCS); } - pending_state.client_kex( - new Client_Key_Exchange(contents, pending_state, pending_state.server_rsa_kex_key(), *m_creds, policy(), rng())); + pending_state.client_kex(std::make_unique( + contents, pending_state, pending_state.server_rsa_kex_key(), *m_creds, policy(), rng())); pending_state.compute_session_keys(); if(policy().allow_ssl_key_log_file()) { @@ -504,7 +551,7 @@ void Server_Impl_12::process_certificate_verify_msg(Server_Handshake_State& pending_state, Handshake_Type type, const std::vector& contents) { - pending_state.client_verify(new Certificate_Verify_12(contents)); + pending_state.client_verify(std::make_unique(contents)); const std::vector& client_certs = pending_state.client_certs()->cert_chain(); @@ -512,8 +559,11 @@ throw TLS_Exception(Alert::DecodeError, "No client certificate sent"); } - if(!client_certs[0].allowed_usage(Key_Constraints::DigitalSignature)) { - throw TLS_Exception(Alert::BadCertificate, "Client certificate does not support signing"); + const auto cert_constraints = client_certs[0].constraints(); + if(!cert_constraints.empty()) { + if(!cert_constraints.includes_any(Key_Constraints::DigitalSignature, Key_Constraints::NonRepudiation)) { + throw TLS_Exception(Alert::BadCertificate, "Client certificate does not support signing"); + } } const bool sig_valid = pending_state.client_verify()->verify(client_certs[0], pending_state, policy()); @@ -555,13 +605,13 @@ throw TLS_Exception(Alert::UnexpectedMessage, "Have data remaining in buffer after Finished"); } - pending_state.client_finished(new Finished_12(contents)); + pending_state.client_finished(std::make_unique(contents)); if(!pending_state.client_finished()->verify(pending_state, Connection_Side::Client)) { throw TLS_Exception(Alert::DecryptError, "Finished message didn't verify"); } - if(!pending_state.server_finished()) { + if(pending_state.server_finished() == nullptr) { // already sent finished if resuming, so this is a new session pending_state.hash().update(pending_state.handshake_io().format(contents, type)); @@ -572,7 +622,7 @@ Connection_Side::Server, pending_state.server_hello()->supports_extended_master_secret(), pending_state.server_hello()->supports_encrypt_then_mac(), - get_peer_cert_chain(pending_state), + pending_state.peer_cert_chain(), Server_Information(pending_state.client_hello()->sni_hostname()), pending_state.server_hello()->srtp_profile(), callbacks().tls_current_timestamp()); @@ -591,16 +641,17 @@ !pending_state.server_hello()->supports_session_ticket()); if(pending_state.server_hello()->supports_session_ticket() && handle.has_value() && handle->is_ticket()) { - pending_state.new_session_ticket(new New_Session_Ticket_12(pending_state.handshake_io(), - pending_state.hash(), - handle->ticket().value(), - policy().session_ticket_lifetime())); + pending_state.new_session_ticket(std::make_unique( + pending_state.handshake_io(), + pending_state.hash(), + handle->ticket().value(), + static_cast(policy().session_ticket_lifetime().count()))); } } - if(!pending_state.new_session_ticket() && pending_state.server_hello()->supports_session_ticket()) { + if(pending_state.new_session_ticket() == nullptr && pending_state.server_hello()->supports_session_ticket()) { pending_state.new_session_ticket( - new New_Session_Ticket_12(pending_state.handshake_io(), pending_state.hash())); + std::make_unique(pending_state.handshake_io(), pending_state.hash())); } pending_state.handshake_io().send(Change_Cipher_Spec()); @@ -608,7 +659,7 @@ change_cipher_spec_writer(Connection_Side::Server); pending_state.server_finished( - new Finished_12(pending_state.handshake_io(), pending_state, Connection_Side::Server)); + std::make_unique(pending_state.handshake_io(), pending_state, Connection_Side::Server)); } activate_session(); @@ -617,8 +668,7 @@ /* * Process a handshake message */ -void Server_Impl_12::process_handshake_msg(const Handshake_State* active_state, - Handshake_State& state_base, +void Server_Impl_12::process_handshake_msg(Handshake_State& state_base, Handshake_Type type, const std::vector& contents, bool epoch0_restart) { @@ -639,7 +689,7 @@ switch(type) { case Handshake_Type::ClientHello: - return this->process_client_hello_msg(active_state, state, contents, epoch0_restart); + return this->process_client_hello_msg(state, contents, epoch0_restart); case Handshake_Type::Certificate: return this->process_certificate_msg(state, contents); @@ -669,16 +719,16 @@ pending_state.client_hello()->session_ticket().empty() && session_manager().emits_session_tickets(); - pending_state.server_hello(new Server_Hello_12(pending_state.handshake_io(), - pending_state.hash(), - policy(), - callbacks(), - rng(), - secure_renegotiation_data_for_server_hello(), - *pending_state.client_hello(), - session.session, - offer_new_session_ticket, - m_next_protocol)); + pending_state.server_hello(std::make_unique(pending_state.handshake_io(), + pending_state.hash(), + policy(), + callbacks(), + rng(), + secure_renegotiation_data_for_server_hello(), + *pending_state.client_hello(), + session.session, + offer_new_session_ticket, + m_next_protocol)); secure_renegotiation_check(pending_state.server_hello()); @@ -716,13 +766,12 @@ if(pending_state.server_hello()->supports_session_ticket()) { if(new_handle.has_value() && new_handle->is_ticket()) { - pending_state.new_session_ticket(new New_Session_Ticket_12(pending_state.handshake_io(), - pending_state.hash(), - new_handle->ticket().value(), - policy().session_ticket_lifetime())); + const uint32_t lifetime = static_cast(policy().session_ticket_lifetime().count()); + pending_state.new_session_ticket(std::make_unique( + pending_state.handshake_io(), pending_state.hash(), new_handle->ticket().value(), lifetime)); } else { pending_state.new_session_ticket( - new New_Session_Ticket_12(pending_state.handshake_io(), pending_state.hash())); + std::make_unique(pending_state.handshake_io(), pending_state.hash())); } } @@ -730,7 +779,8 @@ change_cipher_spec_writer(Connection_Side::Server); - pending_state.server_finished(new Finished_12(pending_state.handshake_io(), pending_state, Connection_Side::Server)); + pending_state.server_finished( + std::make_unique(pending_state.handshake_io(), pending_state, Connection_Side::Server)); pending_state.set_expected_next(Handshake_Type::HandshakeCCS); } @@ -766,20 +816,20 @@ const uint16_t ciphersuite = choose_ciphersuite(policy(), pending_state.version(), cert_chains, *pending_state.client_hello()); - Server_Hello_12::Settings srv_settings(Session_ID(make_hello_random(rng(), callbacks(), policy())), - pending_state.version(), - ciphersuite, - session_manager().emits_session_tickets()); - - pending_state.server_hello(new Server_Hello_12(pending_state.handshake_io(), - pending_state.hash(), - policy(), - callbacks(), - rng(), - secure_renegotiation_data_for_server_hello(), - *pending_state.client_hello(), - srv_settings, - m_next_protocol)); + const Server_Hello_12::Settings srv_settings(Session_ID(make_hello_random(rng(), callbacks(), policy())), + pending_state.version(), + ciphersuite, + session_manager().emits_session_tickets()); + + pending_state.server_hello(std::make_unique(pending_state.handshake_io(), + pending_state.hash(), + policy(), + callbacks(), + rng(), + secure_renegotiation_data_for_server_hello(), + *pending_state.client_hello(), + srv_settings, + m_next_protocol)); secure_renegotiation_check(pending_state.server_hello()); @@ -787,13 +837,13 @@ std::shared_ptr private_key; - if(pending_suite.signature_used() || pending_suite.kex_method() == Kex_Algo::STATIC_RSA) { + if(pending_suite.is_certificate_required()) { const std::string algo_used = pending_suite.signature_used() ? pending_suite.sig_algo() : "RSA"; BOTAN_ASSERT(!cert_chains[algo_used].empty(), "Attempting to send empty certificate chain"); pending_state.server_certs( - new Certificate_12(pending_state.handshake_io(), pending_state.hash(), cert_chains[algo_used])); + std::make_unique(pending_state.handshake_io(), pending_state.hash(), cert_chains[algo_used])); if(pending_state.client_hello()->supports_cert_status_message() && pending_state.is_a_resumption() == false) { auto* csr = pending_state.client_hello()->extensions().get(); @@ -802,7 +852,7 @@ const auto resp_bytes = callbacks().tls_provide_cert_status(cert_chains[algo_used], *csr); if(!resp_bytes.empty()) { pending_state.server_cert_status( - new Certificate_Status(pending_state.handshake_io(), pending_state.hash(), resp_bytes)); + std::make_unique(pending_state.handshake_io(), pending_state.hash(), resp_bytes)); } } @@ -816,7 +866,7 @@ if(pending_suite.kex_method() == Kex_Algo::STATIC_RSA) { pending_state.set_server_rsa_kex_key(private_key); } else { - pending_state.server_kex(new Server_Key_Exchange( + pending_state.server_kex(std::make_unique( pending_state.handshake_io(), pending_state, policy(), *m_creds, rng(), private_key.get())); } @@ -831,9 +881,14 @@ const bool request_cert = (client_auth_CAs.empty() == false) || policy().request_client_certificate_authentication(); - if(request_cert && pending_state.ciphersuite().signature_used()) { - pending_state.cert_req( - new Certificate_Request_12(pending_state.handshake_io(), pending_state.hash(), policy(), client_auth_CAs)); + // RFC 5246 7.4.4: supported_signature_algorithms<2..2^16-2> + // Without at least one acceptable scheme we cannot construct a valid + // CertificateRequest, so client cert auth is unreachable regardless. + const bool can_request_cert = !policy().acceptable_signature_schemes().empty(); + + if(request_cert && can_request_cert && pending_state.ciphersuite().is_certificate_required()) { + pending_state.cert_req(std::make_unique( + pending_state.handshake_io(), pending_state.hash(), policy(), client_auth_CAs)); /* SSLv3 allowed clients to skip the Certificate message entirely @@ -845,6 +900,7 @@ pending_state.set_expected_next(Handshake_Type::ClientKeyExchange); } - pending_state.server_hello_done(new Server_Hello_Done(pending_state.handshake_io(), pending_state.hash())); + pending_state.server_hello_done( + std::make_unique(pending_state.handshake_io(), pending_state.hash())); } } // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_server_impl_12.h botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_server_impl_12.h --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_server_impl_12.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_server_impl_12.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,21 +10,19 @@ #define BOTAN_TLS_SERVER_IMPL_12_H_ #include -#include #include #include namespace Botan::TLS { +class Policy; class Server_Handshake_State; /** * SSL/TLS Server 1.2 implementation */ -class Server_Impl_12 : public Channel_Impl_12 { +class Server_Impl_12 final : public Channel_Impl_12 { public: - typedef std::function)> next_protocol_fn; - /** * Server initialization * @@ -65,18 +63,14 @@ */ std::string application_protocol() const override { return m_next_protocol; } - std::vector get_peer_cert_chain(const Handshake_State& state) const override; - void initiate_handshake(Handshake_State& state, bool force_full_renegotiation) override; - void process_handshake_msg(const Handshake_State* active_state, - Handshake_State& pending_state, + void process_handshake_msg(Handshake_State& pending_state, Handshake_Type type, const std::vector& contents, bool epoch0_restart) override; - void process_client_hello_msg(const Handshake_State* active_state, - Server_Handshake_State& pending_state, + void process_client_hello_msg(Server_Handshake_State& pending_state, const std::vector& contents, bool epoch0_restart); diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_session_key.cpp botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_session_key.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls12/tls_session_key.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls12/tls_session_key.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,7 +8,8 @@ #include #include -#include +#include +#include #include namespace Botan::TLS { @@ -19,9 +20,16 @@ Session_Keys::Session_Keys(const Handshake_State* state, const secure_vector& pre_master_secret, bool resuming) { - const size_t cipher_keylen = state->ciphersuite().cipher_keylen(); - const size_t mac_keylen = state->ciphersuite().mac_keylen(); - const size_t cipher_nonce_bytes = state->ciphersuite().nonce_bytes_from_handshake(); + BOTAN_ASSERT_NONNULL(state); + BOTAN_ASSERT_NONNULL(state->client_hello()); + BOTAN_ASSERT_NONNULL(state->server_hello()); + + const auto& suite = state->ciphersuite(); + BOTAN_STATE_CHECK(suite.valid()); + + const size_t cipher_keylen = suite.cipher_keylen(); + const size_t mac_keylen = suite.mac_keylen(); + const size_t cipher_nonce_bytes = suite.nonce_bytes_from_handshake(); const bool extended_master_secret = state->server_hello()->supports_extended_master_secret(); @@ -44,7 +52,7 @@ std::vector label; if(extended_master_secret) { label.assign(EXT_MASTER_SECRET_MAGIC, EXT_MASTER_SECRET_MAGIC + sizeof(EXT_MASTER_SECRET_MAGIC)); - salt += state->hash().final(state->ciphersuite().prf_algo()); + salt += state->hash().final(suite.prf_algo()); } else { label.assign(MASTER_SECRET_MAGIC, MASTER_SECRET_MAGIC + sizeof(MASTER_SECRET_MAGIC)); salt += state->client_hello()->random(); @@ -68,17 +76,26 @@ m_c_aead.resize(mac_keylen + cipher_keylen); m_s_aead.resize(mac_keylen + cipher_keylen); + // NOLINTBEGIN(readability-container-data-pointer) copy_mem(&m_c_aead[0], key_data, mac_keylen); copy_mem(&m_s_aead[0], key_data + mac_keylen, mac_keylen); + // NOLINTEND(readability-container-data-pointer) - copy_mem(&m_c_aead[mac_keylen], key_data + 2 * mac_keylen, cipher_keylen); - copy_mem(&m_s_aead[mac_keylen], key_data + 2 * mac_keylen + cipher_keylen, cipher_keylen); + // Key is not used for NULL suites + if(cipher_keylen > 0) { + copy_mem(&m_c_aead[mac_keylen], key_data + 2 * mac_keylen, cipher_keylen); + copy_mem(&m_s_aead[mac_keylen], key_data + 2 * mac_keylen + cipher_keylen, cipher_keylen); + } else { + BOTAN_STATE_CHECK(suite.null_ciphersuite()); + } - m_c_nonce.resize(cipher_nonce_bytes); - m_s_nonce.resize(cipher_nonce_bytes); + if(cipher_nonce_bytes > 0) { + const uint8_t* c_nonce_bytes = key_data + 2 * (mac_keylen + cipher_keylen); + m_c_nonce.assign(c_nonce_bytes, c_nonce_bytes + cipher_nonce_bytes); - copy_mem(&m_c_nonce[0], key_data + 2 * (mac_keylen + cipher_keylen), cipher_nonce_bytes); - copy_mem(&m_s_nonce[0], key_data + 2 * (mac_keylen + cipher_keylen) + cipher_nonce_bytes, cipher_nonce_bytes); + const uint8_t* s_nonce_bytes = key_data + 2 * (mac_keylen + cipher_keylen) + cipher_nonce_bytes; + m_s_nonce.assign(s_nonce_bytes, s_nonce_bytes + cipher_nonce_bytes); + } } } // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/info.txt botan3-3.12.0+dfsg/src/lib/tls/tls13/info.txt --- botan3-3.7.1+dfsg/src/lib/tls/tls13/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -8,11 +8,15 @@ +tls_messages_13.h +tls_extensions_13.h +tls_psk_13.h tls_psk_identity_13.h tls_channel_impl_13.h +tls_connection_state_13.h tls_cipher_state.h tls_client_impl_13.h tls_handshake_layer_13.h @@ -25,5 +29,4 @@ hkdf tls -tls12 diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/msg_cert_verify_13.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_cert_verify_13.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/msg_cert_verify_13.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_cert_verify_13.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,123 @@ +/* +* Certificate Verify Message +* (C) 2021-2022 Jack Lloyd +* 2021 Elektrobit Automotive GmbH +* 2022 René Meusel, Hannes Rantzsch - neXenio GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include +#include + +namespace Botan::TLS { + +namespace { + +std::vector message(Connection_Side side, const Transcript_Hash& hash) { + std::vector msg(64, 0x20); + msg.reserve(64 + 33 + 1 + hash.size()); + + const std::string context_string = (side == TLS::Connection_Side::Server) ? "TLS 1.3, server CertificateVerify" + : "TLS 1.3, client CertificateVerify"; + + msg.insert(msg.end(), context_string.cbegin(), context_string.cend()); + msg.push_back(0x00); + + msg.insert(msg.end(), hash.cbegin(), hash.cend()); + return msg; +} + +Signature_Scheme choose_signature_scheme(const Private_Key& key, + const std::vector& allowed_schemes, + const std::vector& peer_allowed_schemes) { + for(Signature_Scheme scheme : allowed_schemes) { + // RFC 8446 4.4.3 forbids the rsa_pkcs1_* schemes in CertificateVerify, those are TLS 1.2 only. + if(scheme.is_available() && scheme.is_compatible_with(Protocol_Version::TLS_V13) && scheme.is_suitable_for(key) && + value_exists(peer_allowed_schemes, scheme)) { + return scheme; + } + } + + throw TLS_Exception(Alert::HandshakeFailure, "Failed to agree on a signature algorithm"); +} + +} // namespace + +/* +* Create a new Certificate Verify message for TLS 1.3 +*/ +Certificate_Verify_13::Certificate_Verify_13(const Certificate_13& certificate_msg, + const std::vector& peer_allowed_schemes, + std::string_view hostname, + const Transcript_Hash& hash, + Connection_Side whoami, + Credentials_Manager& creds_mgr, + const Policy& policy, + Callbacks& callbacks, + RandomNumberGenerator& rng) : + m_side(whoami) { + BOTAN_ASSERT_NOMSG(!certificate_msg.empty()); + + const std::string op_type((m_side == Connection_Side::Client) ? "tls-client" : "tls-server"); + const auto context = std::string(hostname); + + const auto private_key = (certificate_msg.has_certificate_chain()) + ? creds_mgr.private_key_for(certificate_msg.leaf(), op_type, context) + : creds_mgr.private_key_for(*certificate_msg.public_key(), op_type, context); + if(!private_key) { + throw TLS_Exception(Alert::InternalError, "Application did not provide a private key for its credential"); + } + + m_scheme = choose_signature_scheme(*private_key, policy.allowed_signature_schemes(), peer_allowed_schemes); + BOTAN_ASSERT_NOMSG(m_scheme.is_available()); + BOTAN_ASSERT_NOMSG(m_scheme.is_compatible_with(Protocol_Version::TLS_V13)); + + m_signature = callbacks.tls_sign_message( + *private_key, rng, m_scheme.padding_string(), m_scheme.format().value(), message(m_side, hash)); +} + +Certificate_Verify_13::Certificate_Verify_13(const std::vector& buf, const Connection_Side side) : + Certificate_Verify(buf), m_side(side) { + if(!m_scheme.is_available()) { + throw TLS_Exception(Alert::IllegalParameter, "Peer sent unknown signature scheme"); + } + + if(!m_scheme.is_compatible_with(Protocol_Version::TLS_V13)) { + throw TLS_Exception(Alert::IllegalParameter, "Peer sent signature algorithm that is not suitable for TLS 1.3"); + } +} + +/* +* Verify a Certificate Verify message +*/ +bool Certificate_Verify_13::verify(const Public_Key& public_key, + Callbacks& callbacks, + const Transcript_Hash& transcript_hash) const { + BOTAN_ASSERT_NOMSG(m_scheme.is_available()); + + // RFC 8446 4.2.3 + // The keys found in certificates MUST [...] be of appropriate type for + // the signature algorithms they are used with. + if(m_scheme.key_algorithm_identifier() != public_key.algorithm_identifier()) { + throw TLS_Exception(Alert::IllegalParameter, "Signature algorithm does not match certificate's public key"); + } + + const bool signature_valid = callbacks.tls_verify_message( + public_key, m_scheme.padding_string(), m_scheme.format().value(), message(m_side, transcript_hash), m_signature); + +#if defined(BOTAN_UNSAFE_FUZZER_MODE) + BOTAN_UNUSED(signature_valid); + return true; +#else + return signature_valid; +#endif +} + +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/msg_certificate_13.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_certificate_13.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/msg_certificate_13.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_certificate_13.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,22 +7,18 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include +#include #include -#include #include #include #include #include #include +#include #include -#include -#include -#include -#include #include - +#include #include #include @@ -161,7 +157,7 @@ for(size_t i = 0; i < cert_chain.size(); ++i) { auto& entry = m_entries.emplace_back(cert_chain[i]); if(!ocsp_responses[i].empty()) { - entry.extensions().add(new Certificate_Status_Request(ocsp_responses[i])); + entry.extensions().add(new Certificate_Status_Request(ocsp_responses[i])); // NOLINT(*-owning-memory) } // This will call the modification callback multiple times. Once for @@ -192,7 +188,7 @@ Certificate_Type cert_type) : m_request_context(cert_request.context()), m_side(Connection_Side::Client) { const auto key_types = filter_signature_schemes(cert_request.signature_schemes()); - const auto op_type = "tls-client"; + const std::string op_type = "tls-client"; if(cert_type == Certificate_Type::X509) { setup_entries( @@ -227,14 +223,24 @@ Callbacks& callbacks, Certificate_Type cert_type) : // RFC 8446 4.4.2: - // [In the case of server authentication], this field + // [In the case of server authentication], the request context // SHALL be zero length - m_request_context(), m_side(Connection_Side::Server) { - BOTAN_ASSERT_NOMSG(client_hello.extensions().has()); + m_request_context(/* NOLINT(*-redundant-member-init) */), m_side(Connection_Side::Server) { + /* + RFC 8446 4.2.3: + Clients which desire the server to authenticate itself via a + certificate MUST send the "signature_algorithms" extension. If a + server is authenticating via a certificate and the client has not sent + a "signature_algorithms" extension, then the server MUST abort the + handshake with a "missing_extension" alert. + */ + if(!client_hello.extensions().has()) { + throw TLS_Exception(Alert::MissingExtension, "Client Hello is missing required signature_algorithms extension"); + } const auto key_types = filter_signature_schemes(client_hello.signature_schemes()); - const auto op_type = "tls-server"; - const auto context = client_hello.sni_hostname(); + const std::string op_type = "tls-server"; + const std::string context = client_hello.sni_hostname(); if(cert_type == Certificate_Type::X509) { auto cert_chain = credentials_manager.find_cert_chain( @@ -264,31 +270,38 @@ } Certificate_13::Certificate_Entry::Certificate_Entry(TLS_Data_Reader& reader, - const Connection_Side side, - const Certificate_Type cert_type) { - switch(cert_type) { - case Certificate_Type::X509: - // RFC 8446 4.2.2 - // [...] each CertificateEntry contains a DER-encoded X.509 - // certificate. - m_certificate = X509_Certificate(reader.get_tls_length_value(3)); + Connection_Side side, + Certificate_Type cert_type) { + if(cert_type == Certificate_Type::X509) { + // RFC 8446 4.2.2 + // [...] each CertificateEntry contains a DER-encoded X.509 + // certificate. + const auto cert_bytes = reader.get_tls_length_value(3); + try { + m_certificate = std::make_unique(cert_bytes); m_raw_public_key = m_certificate->subject_public_key(); - break; - case Certificate_Type::RawPublicKey: - // RFC 7250 3. - // This specification uses raw public keys whereby the already - // available encoding used in a PKIX certificate in the form of a - // SubjectPublicKeyInfo structure is reused. + } catch(Exception& e) { + // bad_certificate would make more sense but BoGo expects decoding_error + throw TLS_Exception(Alert::DecodeError, e.what()); + } + } else if(cert_type == Certificate_Type::RawPublicKey) { + // RFC 7250 3. + // This specification uses raw public keys whereby the already + // available encoding used in a PKIX certificate in the form of a + // SubjectPublicKeyInfo structure is reused. + try { m_raw_public_key = X509::load_key(reader.get_tls_length_value(3)); - break; - default: - throw TLS_Exception(Alert::InternalError, "Unknown certificate type"); + } catch(Exception& e) { + throw TLS_Exception(Alert::DecodeError, e.what()); + } + } else { + throw TLS_Exception(Alert::InternalError, "Unknown certificate type"); } // Extensions are simply tacked at the end of the certificate entry. This // is a departure from the typical "tag-length-value" in a sense that the // Extensions deserializer needs the length value of the extensions. - const auto extensions_length = reader.peek_uint16_t(); + const size_t extensions_length = reader.peek_uint16_t(); const auto exts_buf = reader.get_fixed(extensions_length + 2); TLS_Data_Reader exts_reader("extensions reader", exts_buf); m_extensions.deserialize(exts_reader, side, Handshake_Type::Certificate); @@ -317,17 +330,23 @@ } } -Certificate_13::Certificate_Entry::Certificate_Entry(X509_Certificate cert) : - m_certificate(std::move(cert)), m_raw_public_key(m_certificate->subject_public_key()) {} +Certificate_13::Certificate_Entry::~Certificate_Entry() = default; + +Certificate_13::Certificate_Entry::Certificate_Entry(Certificate_13::Certificate_Entry&& other) noexcept = default; +Certificate_13::Certificate_Entry& Certificate_13::Certificate_Entry::operator=( + Certificate_13::Certificate_Entry&& other) noexcept = default; + +Certificate_13::Certificate_Entry::Certificate_Entry(const X509_Certificate& cert) : + m_certificate(std::make_unique(cert)), m_raw_public_key(m_certificate->subject_public_key()) {} Certificate_13::Certificate_Entry::Certificate_Entry(std::shared_ptr raw_public_key) : - m_certificate(std::nullopt), m_raw_public_key(std::move(raw_public_key)) { + m_raw_public_key(std::move(raw_public_key)) { BOTAN_ASSERT_NONNULL(m_raw_public_key); } const X509_Certificate& Certificate_13::Certificate_Entry::certificate() const { BOTAN_STATE_CHECK(has_certificate()); - return m_certificate.value(); + return *m_certificate; } std::shared_ptr Certificate_13::Certificate_Entry::public_key() const { diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/msg_certificate_req_13.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_certificate_req_13.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/msg_certificate_req_13.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_certificate_req_13.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -5,11 +5,15 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include +#include +#include #include +#include #include #include +#include +#include #include namespace Botan::TLS { @@ -50,7 +54,7 @@ // For Certificate Request said table states: // "status_request", "signature_algorithms", "signed_certificate_timestamp", // "certificate_authorities", "oid_filters", "signature_algorithms_cert", - std::set allowed_extensions = { + const std::set allowed_extensions = { Extension_Code::CertificateStatusRequest, Extension_Code::SignatureAlgorithms, // Extension_Code::SignedCertificateTimestamp, // NYI @@ -62,9 +66,11 @@ if(m_extensions.contains_implemented_extensions_other_than(allowed_extensions)) { throw TLS_Exception(Alert::IllegalParameter, "Certificate Request contained an extension that is not allowed"); } + + reader.assert_done(); } -Certificate_Request_13::Certificate_Request_13(std::vector acceptable_CAs, +Certificate_Request_13::Certificate_Request_13(const std::vector& acceptable_CAs, const Policy& policy, Callbacks& callbacks) { // RFC 8446 4.3.2 @@ -93,12 +99,18 @@ } if(!acceptable_CAs.empty()) { - m_extensions.add(std::make_unique(std::move(acceptable_CAs))); + m_extensions.add(std::make_unique(acceptable_CAs)); } // TODO: Support cert_status_request for OCSP stapling callbacks.tls_modify_extensions(m_extensions, Connection_Side::Server, type()); + + if(!m_extensions.has()) { + throw TLS_Exception( + Alert::InternalError, + "Application tls_modify_extensions callback removed Signature_Algorithms from the CertificateRequest"); + } } std::optional Certificate_Request_13::maybe_create(const Client_Hello_13& client_hello, @@ -108,7 +120,7 @@ const auto trusted_CAs = cred_mgr.trusted_certificate_authorities("tls-server", client_hello.sni_hostname()); std::vector client_auth_CAs; - for(const auto store : trusted_CAs) { + for(auto* const store : trusted_CAs) { const auto subjects = store->all_subjects(); client_auth_CAs.insert(client_auth_CAs.end(), subjects.begin(), subjects.end()); } @@ -117,7 +129,7 @@ return std::nullopt; } - return Certificate_Request_13(std::move(client_auth_CAs), policy, callbacks); + return Certificate_Request_13(client_auth_CAs, policy, callbacks); } std::vector Certificate_Request_13::acceptable_CAs() const { @@ -140,7 +152,7 @@ // If no "signature_algorithms_cert" extension is present, then the // "signature_algorithms" extension also applies to signatures appearing // in certificates. - if(auto sig_schemes_cert = m_extensions.get()) { + if(auto* sig_schemes_cert = m_extensions.get()) { return sig_schemes_cert->supported_schemes(); } else { return signature_schemes(); diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/msg_client_hello_13.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_client_hello_13.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/msg_client_hello_13.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_client_hello_13.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,512 @@ +/* +* TLS Client Hello Messages +* (C) 2004-2011,2015,2016 Jack Lloyd +* 2021 Elektrobit Automotive GmbH +* 2022 René Meusel, Hannes Rantzsch - neXenio GmbH +* 2026 René Meusel - Rohde & Schwarz Cybersecurity GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(BOTAN_HAS_TLS_12) + #include +#endif + +namespace Botan::TLS { + +Client_Hello_13::Client_Hello_13(std::unique_ptr data) : Client_Hello(std::move(data)) { + const auto& exts = m_data->extensions(); + + // RFC 8446 4.1.2 + // TLS 1.3 ClientHellos are identified as having a legacy_version of + // 0x0303 and a "supported_versions" extension present with 0x0304 as the + // highest version indicated therein. + // + // Note that we already checked for "supported_versions" before entering this + // c'tor in `Client_Hello_13::parse()`. This is just to be doubly sure. + BOTAN_ASSERT_NOMSG(exts.has()); + + // RFC 8446 4.2.1 + // Servers MAY abort the handshake upon receiving a ClientHello with + // legacy_version 0x0304 or later. + if(m_data->legacy_version().is_tls_13_or_later()) { + throw TLS_Exception(Alert::DecodeError, "TLS 1.3 Client Hello has invalid legacy_version"); + } + + // RFC 8446 D.5 + // Any endpoint receiving a Hello message with ClientHello.legacy_version [...] + // set to 0x0300 MUST abort the handshake with a "protocol_version" alert. + if(m_data->legacy_version().major_version() == 3 && m_data->legacy_version().minor_version() == 0) { + throw TLS_Exception(Alert::ProtocolVersion, "TLS 1.3 Client Hello has invalid legacy_version"); + } + + // RFC 8446 4.1.2 + // For every TLS 1.3 ClientHello, [the compression method] MUST contain + // exactly one byte, set to zero, [...]. If a TLS 1.3 ClientHello is + // received with any other value in this field, the server MUST abort the + // handshake with an "illegal_parameter" alert. + if(m_data->comp_methods().size() != 1 || m_data->comp_methods().front() != 0) { + throw TLS_Exception(Alert::IllegalParameter, "Client did not offer NULL compression"); + } + + // RFC 8446 4.2.9 + // A client MUST provide a "psk_key_exchange_modes" extension if it + // offers a "pre_shared_key" extension. If clients offer "pre_shared_key" + // without a "psk_key_exchange_modes" extension, servers MUST abort + // the handshake. + if(exts.has()) { + if(!exts.has()) { + throw TLS_Exception(Alert::MissingExtension, + "Client Hello offered a PSK without a psk_key_exchange_modes extension"); + } + + // RFC 8446 4.2.11 + // The "pre_shared_key" extension MUST be the last extension in the + // ClientHello [...]. Servers MUST check that it is the last extension + // and otherwise fail the handshake with an "illegal_parameter" alert. + if(exts.last_added() != Extension_Code::PresharedKey) { + throw TLS_Exception(Alert::IllegalParameter, "PSK extension was not at the very end of the Client Hello"); + } + } + + // RFC 8446 9.2 + // [A TLS 1.3 ClientHello] message MUST meet the following requirements: + // + // - If not containing a "pre_shared_key" extension, it MUST contain + // both a "signature_algorithms" extension and a "supported_groups" + // extension. + // + // - If containing a "supported_groups" extension, it MUST also contain + // a "key_share" extension, and vice versa. An empty + // KeyShare.client_shares vector is permitted. + // + // Servers receiving a ClientHello which does not conform to these + // requirements MUST abort the handshake with a "missing_extension" + // alert. + if(!exts.has()) { + if(!exts.has() || !exts.has()) { + throw TLS_Exception( + Alert::MissingExtension, + "Non-PSK Client Hello did not contain supported_groups and signature_algorithms extensions"); + } + } + if(exts.has() != exts.has()) { + throw TLS_Exception(Alert::MissingExtension, + "Client Hello must either contain both key_share and supported_groups extensions or neither"); + } + + if(exts.has()) { + auto* const supported_ext = exts.get(); + BOTAN_ASSERT_NONNULL(supported_ext); + const auto supports = supported_ext->groups(); + const auto offers = exts.get()->offered_groups(); + + // RFC 8446 4.2.8 + // Each KeyShareEntry value MUST correspond to a group offered in the + // "supported_groups" extension and MUST appear in the same order. + // [...] + // Clients MUST NOT offer any KeyShareEntry values for groups not + // listed in the client's "supported_groups" extension. + // + // Servers MAY check for violations of these rules and abort the + // handshake with an "illegal_parameter" alert if one is violated. + // + // Note: We can assume that both `offers` and `supports` are unique lists + // as this is ensured in the parsing code of the extensions. + // + // Since offers must appear in the same order as supports, a single + // forward sweep of `supports` suffices: after finding each offered group + // we advance past its position so the next offered group is searched for + // only in the remaining suffix. + auto supports_it = supports.begin(); + for(const auto offered : offers) { + supports_it = std::find(supports_it, supports.end(), offered); + if(supports_it == supports.end()) { + throw TLS_Exception(Alert::IllegalParameter, + "Offered key exchange groups do not align with claimed supported groups"); + } + ++supports_it; + } + } + + // TODO: Reject oid_filters extension if found (which is the only known extension that + // must not occur in the TLS 1.3 client hello. + // RFC 8446 4.2.5 + // [The oid_filters extension] MUST only be sent in the CertificateRequest message. +} + +/* + * Create a new Client Hello message + */ +Client_Hello_13::Client_Hello_13(const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + std::string_view hostname, + const std::vector& next_protocols, + std::optional& session, + std::vector psks) { + // RFC 8446 4.1.2 + // In TLS 1.3, the client indicates its version preferences in the + // "supported_versions" extension (Section 4.2.1) and the + // legacy_version field MUST be set to 0x0303, which is the version + // number for TLS 1.2. + m_data->m_legacy_version = Protocol_Version::TLS_V12; + m_data->m_random = make_hello_random(rng, cb, policy); + m_data->m_suites = policy.ciphersuite_list(Protocol_Version::TLS_V13); + + if(policy.allow_tls12()) { + // Note: DTLS 1.3 is NYI, hence dtls_12 is not checked + const auto legacy_suites = policy.ciphersuite_list(Protocol_Version::TLS_V12); + m_data->m_suites.insert(m_data->m_suites.end(), legacy_suites.cbegin(), legacy_suites.cend()); + } + + if(policy.tls_13_middlebox_compatibility_mode()) { + // RFC 8446 4.1.2 + // In compatibility mode (see Appendix D.4), this field MUST be non-empty, + // so a client not offering a pre-TLS 1.3 session MUST generate a new + // 32-byte value. + // + // Note: we won't ever offer a TLS 1.2 session. In such a case we would + // have instantiated a TLS 1.2 client in the first place. + m_data->m_session_id = Session_ID(make_hello_random(rng, cb, policy)); + } + + // NOLINTBEGIN(*-owning-memory) + if(Server_Name_Indicator::hostname_acceptable_for_sni(hostname)) { + m_data->extensions().add(new Server_Name_Indicator(hostname)); + } + + m_data->extensions().add(new Supported_Groups(policy.key_exchange_groups())); + + m_data->extensions().add(new Key_Share(policy, cb, rng)); + + m_data->extensions().add(new Supported_Versions(Protocol_Version::TLS_V13, policy)); + + m_data->extensions().add(new Signature_Algorithms(policy.acceptable_signature_schemes())); + if(auto cert_signing_prefs = policy.acceptable_certificate_signature_schemes()) { + // RFC 8446 4.2.3 + // Implementations which have the same policy in both cases MAY omit + // the "signature_algorithms_cert" extension. + m_data->extensions().add(new Signature_Algorithms_Cert(std::move(cert_signing_prefs.value()))); + } + + // TODO: Support for PSK-only mode without a key exchange. + // This should be configurable in TLS::Policy and should allow no PSK + // support at all (e.g. to disable support for session resumption). + m_data->extensions().add(new PSK_Key_Exchange_Modes({PSK_Key_Exchange_Mode::PSK_DHE_KE})); + + if(policy.support_cert_status_message()) { + m_data->extensions().add(new Certificate_Status_Request({}, {})); + } + + // We currently support "record_size_limit" for TLS 1.3 exclusively. Hence, + // when TLS 1.2 is advertised as a supported protocol, we must not offer this + // extension. + if(policy.record_size_limit().has_value() && !policy.allow_tls12()) { + m_data->extensions().add(new Record_Size_Limit(policy.record_size_limit().value())); + } + + /* + * Right now raw public key support is not implemented for TLS 1.2, so we only offer + * certificate_types (which is used to request raw public key) if additionally TLS 1.2 + * support is disabled. Otherwise a peer might reply with a 1.2 server hello + a certificate_type + * extension indicating it wishes to use RPK, which would lead to errors later. + */ + if(!policy.allow_tls12()) { + m_data->extensions().add(new Client_Certificate_Type(policy.accepted_client_certificate_types())); + m_data->extensions().add(new Server_Certificate_Type(policy.accepted_server_certificate_types())); + } + + if(!next_protocols.empty()) { + m_data->extensions().add(new Application_Layer_Protocol_Notification(next_protocols)); + } + +#if defined(BOTAN_HAS_TLS_12) + if(policy.allow_tls12()) { + m_data->extensions().add(new Renegotiation_Extension()); + m_data->extensions().add(new Session_Ticket_Extension()); + + // EMS must always be used with TLS 1.2, regardless of the policy + m_data->extensions().add(new Extended_Master_Secret); + + if(policy.negotiate_encrypt_then_mac()) { + m_data->extensions().add(new Encrypt_then_MAC); + } + + if(m_data->extensions().has() && + !m_data->extensions().get()->ec_groups().empty()) { + m_data->extensions().add(new Supported_Point_Formats(policy.use_ecc_point_compression())); + } + } +#endif + + if(session.has_value() || !psks.empty()) { + m_data->extensions().add(new PSK(session, std::move(psks), cb)); + } + // NOLINTEND(*-owning-memory) + + cb.tls_modify_extensions(m_data->extensions(), Connection_Side::Client, type()); + + // The application's tls_modify_extensions callback could have stripped + // Supported_Groups or Key_Share, which must be there. + if(!m_data->extensions().has()) { + throw TLS_Exception(Alert::InternalError, + "Application tls_modify_extensions callback removed Supported_Groups from the ClientHello"); + } + if(!m_data->extensions().has()) { + throw TLS_Exception(Alert::InternalError, + "Application tls_modify_extensions callback removed Key_Share from the ClientHello"); + } + + if(m_data->extensions().has()) { + // RFC 8446 4.2.11 + // The "pre_shared_key" extension MUST be the last extension in the + // ClientHello (this facilitates implementation [...]). + if(m_data->extensions().last_added() != Extension_Code::PresharedKey) { + throw TLS_Exception(Alert::InternalError, + "Application modified extensions of Client Hello, PSK is not last anymore"); + } + calculate_psk_binders({}); + } +} + +std::variant Client_Hello_13::parse(const std::vector& buf) { + auto data = std::make_unique(buf); + const auto version = data->version(); + + if(version.is_pre_tls_13()) { + return Client_Hello_12_Shim(std::move(data)); + } else { + return Client_Hello_13(std::move(data)); + } +} + +void Client_Hello_13::retry(const Hello_Retry_Request& hrr, + const Transcript_Hash_State& transcript_hash_state, + Callbacks& cb, + RandomNumberGenerator& rng) { + BOTAN_STATE_CHECK(m_data->extensions().has()); + BOTAN_STATE_CHECK(m_data->extensions().has()); + + auto* hrr_ks = hrr.extensions().get(); + const auto& supported_groups = m_data->extensions().get()->groups(); + + if(hrr.extensions().has()) { + m_data->extensions().get()->retry_offer(*hrr_ks, supported_groups, cb, rng); + } + + // RFC 8446 4.2.2 + // When sending the new ClientHello, the client MUST copy + // the contents of the extension received in the HelloRetryRequest into + // a "cookie" extension in the new ClientHello. + // + // RFC 8446 4.2.2 + // Clients MUST NOT use cookies in their initial ClientHello in subsequent + // connections. + if(hrr.extensions().has()) { + BOTAN_STATE_CHECK(!m_data->extensions().has()); + m_data->extensions().add(new Cookie(hrr.extensions().get()->get_cookie())); // NOLINT(*-owning-memory) + } + + // Note: the consumer of the TLS implementation won't be able to distinguish + // invocations to this callback due to the first Client_Hello or the + // retried Client_Hello after receiving a Hello_Retry_Request. We assume + // that the user keeps and detects this state themselves. + cb.tls_modify_extensions(m_data->extensions(), Connection_Side::Client, type()); + + // Same invariants as in the constructor: the callback must not strip + // Supported_Groups or Key_Share + if(!m_data->extensions().has()) { + throw TLS_Exception( + Alert::InternalError, + "Application tls_modify_extensions callback removed Supported_Groups from the retried ClientHello"); + } + if(!m_data->extensions().has()) { + throw TLS_Exception(Alert::InternalError, + "Application tls_modify_extensions callback removed Key_Share from the retried ClientHello"); + } + + auto* psk = m_data->extensions().get(); + if(psk != nullptr) { + // RFC 8446 4.2.11 + // The "pre_shared_key" extension MUST be the last extension in the + // ClientHello (this facilitates implementation [...]). + m_data->extensions().reorder({Extension_Code::PresharedKey}); + + // Cipher suite should always be a known suite as this is checked upstream + const auto cipher = Ciphersuite::by_id(hrr.ciphersuite()); + BOTAN_ASSERT_NOMSG(cipher.has_value()); + + // RFC 8446 4.1.4 + // In [...] its updated ClientHello, the client SHOULD NOT offer + // any pre-shared keys associated with a hash other than that of the + // selected cipher suite. + psk->filter(cipher.value()); + + // RFC 8446 4.2.11.2 + // If the server responds with a HelloRetryRequest and the client + // then sends ClientHello2, its binder will be computed over: [...]. + calculate_psk_binders(transcript_hash_state.clone()); + } +} + +void Client_Hello_13::validate_updates(const Client_Hello_13& new_ch) { + // RFC 8446 4.1.2 + // The client will also send a ClientHello when the server has responded + // to its ClientHello with a HelloRetryRequest. In that case, the client + // MUST send the same ClientHello without modification, except as follows: + + if(m_data->session_id() != new_ch.m_data->session_id() || m_data->random() != new_ch.m_data->random() || + m_data->ciphersuites() != new_ch.m_data->ciphersuites() || + m_data->comp_methods() != new_ch.m_data->comp_methods()) { + throw TLS_Exception(Alert::IllegalParameter, "Client Hello core values changed after Hello Retry Request"); + } + + const auto oldexts = extension_types(); + const auto newexts = new_ch.extension_types(); + + // Check that extension omissions are justified. RFC 8446 4.1.2 lists the + // only mutations the client may make between CH1 and CH2; any other + // extension removal is an illegal parameter regardless of whether the + // extension is one this implementation recognizes. + for(const auto oldext : oldexts) { + if(!newexts.contains(oldext)) { + // RFC 8446 4.1.2 + // Removing the "early_data" extension (Section 4.2.10) if one was + // present. Early data is not permitted after a HelloRetryRequest. + if(oldext == EarlyDataIndication::static_type()) { + continue; + } + + // RFC 8446 4.1.2 + // Optionally adding, removing, or changing the length of the + // "padding" extension. + if(oldext == Extension_Code::Padding) { + continue; + } + + throw TLS_Exception(Alert::IllegalParameter, "Extension removed in updated Client Hello"); + } + } + + // Check that extension additions are justified. Same reasoning: only the + // RFC-listed mutations are allowed, including for unknown extension codes. + for(const auto newext : newexts) { + if(!oldexts.contains(newext)) { + // RFC 8446 4.1.2 + // Including a "cookie" extension if one was provided in the + // HelloRetryRequest. + if(newext == Cookie::static_type()) { + continue; + } + + // RFC 8446 4.1.2 + // Optionally adding, removing, or changing the length of the + // "padding" extension. + if(newext == Extension_Code::Padding) { + continue; + } + + throw TLS_Exception(Alert::UnsupportedExtension, "Added an extension in updated Client Hello"); + } + } + + // RFC 8446 4.1.2 + // Removing the "early_data" extension (Section 4.2.10) if one was + // present. Early data is not permitted after a HelloRetryRequest. + if(new_ch.extensions().has()) { + throw TLS_Exception(Alert::IllegalParameter, "Updated Client Hello indicates early data"); + } + + // RFC 8446 4.1.2 + // The client MUST send the same ClientHello without modification, + // except as follows: [key_share, pre_shared_key, early_data, cookie, padding] + // + // Verify that extensions whose content must not change between the + // initial and retried Client Hello have identical wire encodings. + const std::set extensions_allowed_to_change = { + Extension_Code::KeyShare, + Extension_Code::PresharedKey, + Extension_Code::EarlyData, + Extension_Code::Cookie, + Extension_Code::Padding, + }; + + for(const auto ext_type : oldexts) { + if(extensions_allowed_to_change.contains(ext_type)) { + continue; + } + + const auto old_bytes = extensions().extension_raw_bytes(ext_type); + const auto new_bytes = new_ch.extensions().extension_raw_bytes(ext_type); + + // Both Client Hellos validated here are received from the peer and went + // through Extensions::deserialize, which records raw bytes for every + // parsed extension. A missing raw_bytes on either side would mean an + // extension was added by us programmatically - which shouldn't happen + BOTAN_ASSERT_NOMSG(old_bytes.has_value() && new_bytes.has_value()); + if(old_bytes.value() != new_bytes.value()) { + throw TLS_Exception(Alert::IllegalParameter, "Extension content changed in updated Client Hello"); + } + } +} + +void Client_Hello_13::calculate_psk_binders(Transcript_Hash_State transcript_hash) { + auto* psk = m_data->extensions().get(); + if(psk == nullptr || psk->empty()) { + return; + } + + // RFC 8446 4.2.11.2 + // Each entry in the binders list is computed as an HMAC over a + // transcript hash (see Section 4.4.1) containing a partial ClientHello + // [...]. + // + // Therefore we marshal the entire message prematurely to obtain the + // (truncated) transcript hash, calculate the PSK binders with it, update + // the Client Hello thus finalizing the message. Down the road, it will be + // re-marshalled with the correct binders and sent over the wire. + Handshake_Layer::prepare_message(*this, transcript_hash); + psk->calculate_binders(transcript_hash); +} + +std::optional Client_Hello_13::highest_supported_version(const Policy& policy) const { + // RFC 8446 4.2.1 + // The "supported_versions" extension is used by the client to indicate + // which versions of TLS it supports and by the server to indicate which + // version it is using. The extension contains a list of supported + // versions in preference order, with the most preferred version first. + auto* const supvers = m_data->extensions().get(); + BOTAN_ASSERT_NONNULL(supvers); + + std::optional result; + + for(const auto& v : supvers->versions()) { + // RFC 8446 4.2.1 + // Servers MUST only select a version of TLS present in that extension + // and MUST ignore any unknown versions that are present in that + // extension. + if(!v.known_version() || !policy.acceptable_protocol_version(v)) { + continue; + } + + result = (result.has_value()) ? std::optional(std::max(result.value(), v)) : std::optional(v); + } + + return result; +} + +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/msg_encrypted_extensions.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_encrypted_extensions.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/msg_encrypted_extensions.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_encrypted_extensions.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,17 +6,25 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include +#include #include #include +#include +#include #include namespace Botan::TLS { -Encrypted_Extensions::Encrypted_Extensions(const Client_Hello_13& client_hello, const Policy& policy, Callbacks& cb) { +Encrypted_Extensions::Encrypted_Extensions(const Client_Hello_13& client_hello, + const Policy& policy, + Callbacks& cb, + bool is_resumption, + bool requesting_client_auth) { const auto& exts = client_hello.extensions(); + // NOLINTBEGIN(*-owning-memory) + // RFC 8446 4.2.7 // As of TLS 1.3, servers are permitted to send the "supported_groups" // extension to the client. Clients [...] MAY use the information @@ -52,8 +60,14 @@ // If the server does not send a certificate_request payload [...], // then the client_certificate_type payload in the server hello MUST be // omitted. - if(auto ch_client_cert_types = exts.get(); - ch_client_cert_types && policy.request_client_certificate_authentication()) { + // + // Note: requesting_client_auth tracks whether the caller will actually + // emit a CertificateRequest. The server-side decision in + // Certificate_Request_13::maybe_create depends on both the policy flag + // *and* the credentials manager's CA list, so re-checking just the + // policy flag here would miss the trusted-CAs-only configuration. + if(auto* ch_client_cert_types = exts.get(); + ch_client_cert_types != nullptr && requesting_client_auth) { m_extensions.add(new Client_Certificate_Type(*ch_client_cert_types, policy)); } @@ -63,7 +77,7 @@ // the server in a subsequent certificate payload. [...] With the // server_certificate_type extension in the server hello, the TLS server // indicates the certificate type carried in the Certificate payload. - if(auto ch_server_cert_types = exts.get()) { + if(auto* ch_server_cert_types = exts.get()) { m_extensions.add(new Server_Certificate_Type(*ch_server_cert_types, policy)); } @@ -72,21 +86,49 @@ // extension [...] SHALL include an extension of type "server_name" in the // (extended) server hello. The "extension_data" field of this extension // SHALL be empty. - if(exts.has()) { + // + // When resuming a session, the server MUST NOT include a server_name + // extension in the server hello. + if(exts.has() && !is_resumption) { m_extensions.add(new Server_Name_Indicator("")); } - if(auto alpn_ext = exts.get()) { - const auto next_protocol = cb.tls_server_choose_app_protocol(alpn_ext->protocols()); + if(auto* alpn_ext = exts.get()) { + const auto& offered = alpn_ext->protocols(); + const auto next_protocol = cb.tls_server_choose_app_protocol(offered); if(!next_protocol.empty()) { + // RFC 7301 3.2: if a protocol is selected, the server MUST select + // one of the protocols advertised by the client. + if(!value_exists(offered, next_protocol)) { + throw TLS_Exception(Alert::InternalError, + "Application chose an ALPN protocol that the client did not offer"); + } m_extensions.add(new Application_Layer_Protocol_Notification(next_protocol)); } } + // NOLINTEND(*-owning-memory) + // TODO: Implement handling for (at least) // * SRTP cb.tls_modify_extensions(m_extensions, Connection_Side::Server, type()); + + // After the application's tls_modify_extensions callback runs, re-check the + // RFC-MUST invariants we just established above. The application can add or + // reorder extensions, but shouldn't remove ones required direct response to + // ClientHello extensions would put us out of spec. + if(exts.has() && !m_extensions.has()) { + throw TLS_Exception( + Alert::InternalError, + "Application tls_modify_extensions callback removed Server_Certificate_Type from EncryptedExtensions"); + } + + if(requesting_client_auth && exts.has() && !m_extensions.has()) { + throw TLS_Exception( + Alert::InternalError, + "Application tls_modify_extensions callback removed Client_Certificate_Type from EncryptedExtensions"); + } } Encrypted_Extensions::Encrypted_Extensions(const std::vector& buf) { @@ -127,9 +169,19 @@ if(m_extensions.contains_implemented_extensions_other_than(allowed_exts)) { throw TLS_Exception(Alert::IllegalParameter, "Encrypted Extensions contained an extension that is not allowed"); } + + reader.assert_done(); } std::vector Encrypted_Extensions::serialize() const { + // RFC 8446 4.3.1: EncryptedExtensions carries Extension extensions<0..2^16-1>; + // an empty list still requires a 2-byte length-prefix on the wire. + // Extensions::serialize collapses empty to {} to suit other contexts, so + // emit the explicit length here. Mirrors the same fallback in + // New_Session_Ticket_13::serialize. + if(m_extensions.empty()) { + return {0x00, 0x00}; + } return m_extensions.serialize(Connection_Side::Server); } diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/msg_finished_13.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_finished_13.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/msg_finished_13.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_finished_13.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,24 @@ +/* +* Finished Message +* (C) 2021-2022 Jack Lloyd +* 2021 Elektrobit Automotive GmbH +* 2022 René Meusel, Hannes Rantzsch - neXenio GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include + +namespace Botan::TLS { + +Finished_13::Finished_13(Cipher_State* cipher_state, const Transcript_Hash& transcript_hash) { + m_verification_data = cipher_state->finished_mac(transcript_hash); +} + +bool Finished_13::verify(Cipher_State* cipher_state, const Transcript_Hash& transcript_hash) const { + return cipher_state->verify_peer_finished_mac(transcript_hash, m_verification_data); +} + +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/msg_key_update.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_key_update.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/msg_key_update.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_key_update.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,7 +6,7 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include +#include #include diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/msg_server_hello_13.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_server_hello_13.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/msg_server_hello_13.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_server_hello_13.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,412 @@ +/* +* TLS Server Hello and Server Hello Done +* (C) 2004-2011,2015,2016,2019 Jack Lloyd +* 2021 Elektrobit Automotive GmbH +* 2022 René Meusel, Hannes Rantzsch - neXenio GmbH +* 2026 René Meusel - Rohde & Schwarz Cybersecurity GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Botan::TLS { + +const Server_Hello_13::Server_Hello_Tag Server_Hello_13::as_server_hello; +const Server_Hello_13::Hello_Retry_Request_Tag Server_Hello_13::as_hello_retry_request; +const Server_Hello_13::Hello_Retry_Request_Creation_Tag Server_Hello_13::as_new_hello_retry_request; + +std::variant Server_Hello_13::create(const Client_Hello_13& ch, + bool hello_retry_request_allowed, + Session_Manager& session_mgr, + Credentials_Manager& credentials_mgr, + RandomNumberGenerator& rng, + const Policy& policy, + Callbacks& cb) { + const auto& exts = ch.extensions(); + + // RFC 8446 4.2.9 + // [With PSK with (EC)DHE key establishment], the client and server MUST + // supply "key_share" values [...]. + // + // Note: We currently do not support PSK without (EC)DHE, hence, we can + // assume that those extensions are available. + BOTAN_ASSERT_NOMSG(exts.has() && exts.has()); + const auto& supported_by_client = exts.get()->groups(); + const auto& offered_by_client = exts.get()->offered_groups(); + const auto selected_group = policy.choose_key_exchange_group(supported_by_client, offered_by_client); + + // RFC 8446 4.1.1 + // If there is no overlap between the received "supported_groups" and the + // groups supported by the server, then the server MUST abort the + // handshake with a "handshake_failure" or an "insufficient_security" alert. + if(selected_group == Named_Group::NONE) { + throw TLS_Exception(Alert::HandshakeFailure, "Client did not offer any acceptable group"); + } + + // RFC 8446 4.2.8: + // Servers MUST NOT send a KeyShareEntry for any group not indicated in the + // client's "supported_groups" extension [...] + if(!value_exists(supported_by_client, selected_group)) { + throw TLS_Exception(Alert::InternalError, "Application selected a group that is not supported by the client"); + } + + // RFC 8446 4.1.4 + // The server will send this message in response to a ClientHello + // message if it is able to find an acceptable set of parameters but the + // ClientHello does not contain sufficient information to proceed with + // the handshake. + // + // In this case, the Client Hello did not contain a key share offer for + // the group selected by the application. + if(!value_exists(offered_by_client, selected_group)) { + // RFC 8446 4.1.4 + // If a client receives a second HelloRetryRequest in the same + // connection (i.e., where the ClientHello was itself in response to a + // HelloRetryRequest), it MUST abort the handshake with an + // "unexpected_message" alert. + BOTAN_STATE_CHECK(hello_retry_request_allowed); + return Hello_Retry_Request(ch, selected_group, policy, cb); + } else { + return Server_Hello_13(ch, selected_group, session_mgr, credentials_mgr, rng, cb, policy); + } +} + +std::variant Server_Hello_13::parse( + const std::vector& buf) { + auto data = std::make_unique(buf); + const auto version = data->version(); + + // server hello that appears to be pre-TLS 1.3, takes precedence over... + if(version.is_pre_tls_13()) { + return Server_Hello_12_Shim(std::move(data)); + } + + // ... the TLS 1.3 "special case" aka. Hello_Retry_Request + if(version == Protocol_Version::TLS_V13) { + if(data->is_hello_retry_request()) { + return Hello_Retry_Request(std::move(data)); + } + + return Server_Hello_13(std::move(data)); + } + + throw TLS_Exception(Alert::ProtocolVersion, "unexpected server hello version: " + version.to_string()); +} + +/** + * Validation that applies to both Server Hello and Hello Retry Request + */ +void Server_Hello_13::basic_validation() const { + BOTAN_ASSERT_NOMSG(m_data->version() == Protocol_Version::TLS_V13); + + // Note: checks that cannot be performed without contextual information + // are done in the specific TLS client implementation. + // Note: The Supported_Version extension makes sure internally that + // exactly one entry is provided. + + // Note: Hello Retry Request basic validation is equivalent with the + // basic validations required for Server Hello + // + // RFC 8446 4.1.4 + // Upon receipt of a HelloRetryRequest, the client MUST check the + // legacy_version, [...], and legacy_compression_method as specified in + // Section 4.1.3 and then process the extensions, starting with determining + // the version using "supported_versions". + + // RFC 8446 4.1.3 + // In TLS 1.3, [...] the legacy_version field MUST be set to 0x0303 + if(legacy_version() != Protocol_Version::TLS_V12) { + throw TLS_Exception(Alert::ProtocolVersion, + "legacy_version '" + legacy_version().to_string() + "' is not allowed"); + } + + // RFC 8446 4.1.3 + // legacy_compression_method: A single byte which MUST have the value 0. + if(compression_method() != 0x00) { + throw TLS_Exception(Alert::DecodeError, "compression is not supported in TLS 1.3"); + } + + // RFC 8446 4.1.3 + // All TLS 1.3 ServerHello messages MUST contain the "supported_versions" extension. + if(!extensions().has()) { + throw TLS_Exception(Alert::MissingExtension, "server hello did not contain 'supported version' extension"); + } + + // RFC 8446 4.2.1 + // A server which negotiates TLS 1.3 MUST respond by sending + // a "supported_versions" extension containing the selected version + // value (0x0304). + if(selected_version() != Protocol_Version::TLS_V13) { + throw TLS_Exception(Alert::IllegalParameter, "TLS 1.3 Server Hello selected a different version"); + } +} + +Server_Hello_13::Server_Hello_13(std::unique_ptr data, + Server_Hello_13::Server_Hello_Tag /*tag*/) : + Server_Hello(std::move(data)) { + BOTAN_ASSERT_NOMSG(!m_data->is_hello_retry_request()); + basic_validation(); + + const auto& exts = extensions(); + + // RFC 8446 4.1.3 + // The ServerHello MUST only include extensions which are required to + // establish the cryptographic context and negotiate the protocol version. + // [...] + // Other extensions (see Section 4.2) are sent separately in the + // EncryptedExtensions message. + // + // Note that further validation dependent on the client hello is done in the + // TLS client implementation. + const std::set allowed = { + Extension_Code::KeyShare, + Extension_Code::SupportedVersions, + Extension_Code::PresharedKey, + }; + + // As the ServerHello shall only contain essential extensions, we don't give + // any slack for extensions not implemented by Botan here. + if(exts.contains_other_than(allowed)) { + throw TLS_Exception(Alert::UnsupportedExtension, "Server Hello contained an extension that is not allowed"); + } + + // RFC 8446 4.1.3 + // Current ServerHello messages additionally contain + // either the "pre_shared_key" extension or the "key_share" + // extension, or both [...]. + if(!exts.has() && !exts.has()) { + throw TLS_Exception(Alert::MissingExtension, "server hello must contain key exchange information"); + } +} + +Server_Hello_13::Server_Hello_13(std::unique_ptr data, + Server_Hello_13::Hello_Retry_Request_Tag /*tag*/) : + Server_Hello(std::move(data)) { + BOTAN_ASSERT_NOMSG(m_data->is_hello_retry_request()); + basic_validation(); + + const auto& exts = extensions(); + + // RFC 8446 4.1.4 + // The HelloRetryRequest extensions defined in this specification are: + // - supported_versions (see Section 4.2.1) + // - cookie (see Section 4.2.2) + // - key_share (see Section 4.2.8) + const std::set allowed = { + Extension_Code::Cookie, + Extension_Code::SupportedVersions, + Extension_Code::KeyShare, + }; + + // As the Hello Retry Request shall only contain essential extensions, we + // don't give any slack for extensions not implemented by Botan here. + if(exts.contains_other_than(allowed)) { + throw TLS_Exception(Alert::UnsupportedExtension, + "Hello Retry Request contained an extension that is not allowed"); + } + + // RFC 8446 4.1.4 + // Clients MUST abort the handshake with an "illegal_parameter" alert if + // the HelloRetryRequest would not result in any change in the ClientHello. + if(!exts.has() && !exts.has()) { + throw TLS_Exception(Alert::IllegalParameter, "Hello Retry Request does not request any changes to Client Hello"); + } +} + +Server_Hello_13::Server_Hello_13(std::unique_ptr data, + Hello_Retry_Request_Creation_Tag /*tag*/) : + Server_Hello(std::move(data)) {} + +namespace { + +uint16_t choose_ciphersuite(const Client_Hello_13& ch, const Policy& policy) { + auto pref_list = ch.ciphersuites(); + // TODO: DTLS might need to make this version dynamic + auto other_list = policy.ciphersuite_list(Protocol_Version::TLS_V13); + + if(policy.server_uses_own_ciphersuite_preferences()) { + std::swap(pref_list, other_list); + } + + for(auto suite_id : pref_list) { + // TODO: take potentially available PSKs into account to select a + // compatible ciphersuite. + // + // Assuming the client sent one or more PSKs, we would first need to find + // the hash functions they are associated to. For session tickets, that + // would mean decrypting the ticket and comparing the cipher suite used in + // those tickets. For (currently not yet supported) pre-assigned PSKs, the + // hash function needs to be specified along with them. + // + // Then we could refine the ciphersuite selection using the required hash + // function for the PSK(s) we are wishing to use down the road. + // + // For now, we just negotiate the cipher suite blindly and hope for the + // best. As long as PSKs are used for session resumption only, this has a + // high chance of success. Previous handshakes with this client have very + // likely selected the same ciphersuite anyway. + // + // See also RFC 8446 4.2.11 + // When session resumption is the primary use case of PSKs, the most + // straightforward way to implement the PSK/cipher suite matching + // requirements is to negotiate the cipher suite first [...]. + if(value_exists(other_list, suite_id)) { + return suite_id; + } + } + + // RFC 8446 4.1.1 + // If the server is unable to negotiate a supported set of parameters + // [...], it MUST abort the handshake with either a "handshake_failure" + // or "insufficient_security" fatal alert [...]. + throw TLS_Exception(Alert::HandshakeFailure, "Can't agree on a ciphersuite with client"); +} +} // namespace + +Server_Hello_13::Server_Hello_13(const Client_Hello_13& ch, + std::optional key_exchange_group, + Session_Manager& session_mgr, + Credentials_Manager& credentials_mgr, + RandomNumberGenerator& rng, + Callbacks& cb, + const Policy& policy) : + Server_Hello(std::make_unique( + Protocol_Version::TLS_V12, + ch.session_id(), + make_server_hello_random(rng, Protocol_Version::TLS_V13, cb, policy), + choose_ciphersuite(ch, policy), + uint8_t(0) /* compression method */ + )) { + // RFC 8446 4.2.1 + // A server which negotiates TLS 1.3 MUST respond by sending a + // "supported_versions" extension containing the selected version + // value (0x0304). It MUST set the ServerHello.legacy_version field to + // 0x0303 (TLS 1.2). + // + // Note that the legacy version (TLS 1.2) is set in this constructor's + // initializer list, accordingly. + m_data->extensions().add(new Supported_Versions(Protocol_Version::TLS_V13)); // NOLINT(*-owning-memory) + + if(key_exchange_group.has_value()) { + BOTAN_ASSERT_NOMSG(ch.extensions().has()); + m_data->extensions().add(Key_Share::create_as_encapsulation( + key_exchange_group.value(), *ch.extensions().get(), policy, cb, rng)); + } + + const auto& ch_exts = ch.extensions(); + + if(ch_exts.has()) { + const auto cs = Ciphersuite::by_id(m_data->ciphersuite()); + BOTAN_ASSERT_NOMSG(cs); + + // RFC 8446 4.2.9 + // A client MUST provide a "psk_key_exchange_modes" extension if it + // offers a "pre_shared_key" extension. + // + // Note: Client_Hello_13 constructor already performed a graceful check. + auto* const psk_modes = ch_exts.get(); + BOTAN_ASSERT_NONNULL(psk_modes); + + // TODO: also support PSK_Key_Exchange_Mode::PSK_KE + // (PSK-based handshake without an additional ephemeral key exchange) + if(value_exists(psk_modes->modes(), PSK_Key_Exchange_Mode::PSK_DHE_KE)) { + if(auto server_psk = ch_exts.get()->select_offered_psk( + ch.sni_hostname(), cs.value(), session_mgr, credentials_mgr, cb, policy)) { + // RFC 8446 4.2.11 + // In order to accept PSK key establishment, the server sends a + // "pre_shared_key" extension indicating the selected identity. + m_data->extensions().add(std::move(server_psk)); + } + } + } + + cb.tls_modify_extensions(m_data->extensions(), Connection_Side::Server, type()); + + if(!m_data->extensions().has()) { + throw TLS_Exception(Alert::InternalError, + "Application tls_modify_extensions callback removed Key_Share from the ServerHello"); + } +} + +std::optional Server_Hello_13::random_signals_downgrade() const { + const uint64_t last8 = load_be(m_data->random().data(), 3); + if(last8 == DOWNGRADE_TLS11) { + return Protocol_Version::TLS_V11; + } + if(last8 == DOWNGRADE_TLS12) { + return Protocol_Version::TLS_V12; + } + + return std::nullopt; +} + +Protocol_Version Server_Hello_13::selected_version() const { + auto* const versions_ext = m_data->extensions().get(); + BOTAN_ASSERT_NOMSG(versions_ext); + const auto& versions = versions_ext->versions(); + BOTAN_ASSERT_NOMSG(versions.size() == 1); + return versions.front(); +} + +Hello_Retry_Request::Hello_Retry_Request(std::unique_ptr data) : + Server_Hello_13(std::move(data), Server_Hello_13::as_hello_retry_request) {} + +Hello_Retry_Request::Hello_Retry_Request(const Client_Hello_13& ch, + Named_Group selected_group, + const Policy& policy, + Callbacks& cb) : + Server_Hello_13(std::make_unique( + Protocol_Version::TLS_V12 /* legacy_version */, + ch.session_id(), + std::vector(HELLO_RETRY_REQUEST_MARKER.begin(), HELLO_RETRY_REQUEST_MARKER.end()), + choose_ciphersuite(ch, policy), + uint8_t(0) /* compression method */, + true /* is Hello Retry Request */ + ), + as_new_hello_retry_request) { + // RFC 8446 4.1.4 + // As with the ServerHello, a HelloRetryRequest MUST NOT contain any + // extensions that were not first offered by the client in its + // ClientHello, with the exception of optionally the "cookie" [...] + // extension. + BOTAN_STATE_CHECK(ch.extensions().has()); + BOTAN_STATE_CHECK(ch.extensions().has()); + + BOTAN_STATE_CHECK(!value_exists(ch.extensions().get()->offered_groups(), selected_group)); + + // RFC 8446 4.1.4 + // The server's extensions MUST contain "supported_versions". + // + // RFC 8446 4.2.1 + // A server which negotiates TLS 1.3 MUST respond by sending a + // "supported_versions" extension containing the selected version + // value (0x0304). It MUST set the ServerHello.legacy_version field to + // 0x0303 (TLS 1.2). + // + // Note that the legacy version (TLS 1.2) is set in this constructor's + // initializer list, accordingly. + // NOLINTBEGIN(*-owning-memory) + m_data->extensions().add(new Supported_Versions(Protocol_Version::TLS_V13)); + + m_data->extensions().add(new Key_Share(selected_group)); + // NOLINTEND(*-owning-memory) + + cb.tls_modify_extensions(m_data->extensions(), Connection_Side::Server, type()); + + if(!m_data->extensions().has()) { + throw TLS_Exception(Alert::InternalError, + "Application tls_modify_extensions callback removed Key_Share from the HelloRetryRequest"); + } +} + +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/msg_session_ticket_13.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_session_ticket_13.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/msg_session_ticket_13.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/msg_session_ticket_13.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,104 @@ +/* +* Session Tickets +* (C) 2021-2022 Jack Lloyd +* 2021 Elektrobit Automotive GmbH +* 2022 René Meusel, Hannes Rantzsch - neXenio GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace Botan::TLS { + +namespace { + +template +void store_lifetime(std::span sink, std::chrono::seconds lifetime) { + BOTAN_ARG_CHECK(lifetime.count() >= 0 && lifetime.count() <= std::numeric_limits::max(), + "Ticket lifetime is out of range"); + store_be(static_cast(lifetime.count()), sink.data()); +} + +} // namespace + +New_Session_Ticket_13::New_Session_Ticket_13(Ticket_Nonce nonce, + const Session& session, + const Session_Handle& handle, + Callbacks& callbacks) : + m_ticket_lifetime_hint(session.lifetime_hint()), + m_ticket_age_add(session.session_age_add()), + m_ticket_nonce(std::move(nonce)), + m_handle(handle.opaque_handle()) { + callbacks.tls_modify_extensions(m_extensions, Connection_Side::Server, type()); +} + +New_Session_Ticket_13::New_Session_Ticket_13(const std::vector& buf, Connection_Side from) { + TLS_Data_Reader reader("New_Session_Ticket_13", buf); + + m_ticket_lifetime_hint = std::chrono::seconds(reader.get_uint32_t()); + + // RFC 8446 4.6.1 + // Servers MUST NOT use any value [of ticket_lifetime] greater than 604800 + // seconds (7 days). + if(m_ticket_lifetime_hint > std::chrono::days(7)) { + throw TLS_Exception(Alert::IllegalParameter, "Received a session ticket with lifetime longer than one week."); + } + + m_ticket_age_add = reader.get_uint32_t(); + m_ticket_nonce = Ticket_Nonce(reader.get_tls_length_value(1)); + // RFC 8446 4.6.1: opaque ticket<1..2^16-1> + m_handle = Opaque_Session_Handle(reader.get_range(2, 1, 65535)); + + m_extensions.deserialize(reader, from, type()); + + // RFC 8446 4.6.1 + // The sole extension currently defined for NewSessionTicket is + // "early_data", indicating that the ticket may be used to send 0-RTT + // data [...]. Clients MUST ignore unrecognized extensions. + if(m_extensions.contains_implemented_extensions_other_than({Extension_Code::EarlyData})) { + throw TLS_Exception(Alert::IllegalParameter, "NewSessionTicket message contained unexpected extension"); + } + + reader.assert_done(); +} + +std::optional New_Session_Ticket_13::early_data_byte_limit() const { + if(!m_extensions.has()) { + return std::nullopt; + } + + const EarlyDataIndication* ext = m_extensions.get(); + BOTAN_ASSERT_NOMSG(ext->max_early_data_size().has_value()); + return ext->max_early_data_size(); +} + +std::vector New_Session_Ticket_13::serialize() const { + std::vector result(8); + + store_lifetime(std::span(result.data(), 4), m_ticket_lifetime_hint); + store_be(m_ticket_age_add, result.data() + 4); + append_tls_length_value(result, m_ticket_nonce.get(), 1); + append_tls_length_value(result, m_handle.get(), 2); + + // TODO: re-evaluate this construction when reworking message marshalling + if(m_extensions.empty()) { + result.push_back(0x00); + result.push_back(0x00); + } else { + result += m_extensions.serialize(Connection_Side::Server); + } + + return result; +} + +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_channel_impl_13.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_channel_impl_13.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_channel_impl_13.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_channel_impl_13.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,15 +9,15 @@ #include -#include -#include -#include +#include +#include +#include +#include +#include #include -#include -#include -#include #include +#include namespace { bool is_user_canceled_alert(const Botan::TLS::Alert& alert) { @@ -124,7 +124,7 @@ // Note: Server_Hello_12 was deliberately not included in the check below because in TLS 1.2 Server Hello and // other handshake messages can be legally coalesced in a single record. // - if(holds_any_ofcan_decrypt_application_traffic()) { + throw Unexpected_Message("Application data received before handshake completion"); + } + /* + The record sequence number is set in Record_Layer::next_record only when + the record contents are decrypted under the current set of traffic keys + */ + if(!record.seq_no.has_value()) { + throw Unexpected_Message("Application data must have a sequence number"); + } callbacks().tls_record_received(record.seq_no.value(), record.fragment); } else if(record.type == Record_Type::Alert) { process_alert(record.fragment); @@ -198,10 +208,20 @@ throw Unexpected_Message("Unexpected additional post-handshake message data found in record"); } - m_cipher_state->update_read_keys(*this); + if(const uint64_t min_interval = policy().minimum_key_update_interval_ms(); min_interval > 0) { + const uint64_t now = + std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()) + .count(); + + if(m_last_key_update_ms != 0 && (now - m_last_key_update_ms) < min_interval) { + throw TLS_Exception(Alert::UnexpectedMessage, "Peer is requesting KeyUpdates too frequently"); + } - // TODO: introduce some kind of rate limit of key updates, otherwise we - // might be forced into an endless loop of key updates. + m_last_key_update_ms = now; + } + + BOTAN_ASSERT_NONNULL(m_cipher_state); + m_cipher_state->update_read_keys(*this); // RFC 8446 4.6.3 // If the request_update field is set to "update_requested", then the @@ -315,8 +335,7 @@ } void Channel_Impl_13::update_traffic_keys(bool request_peer_update) { - BOTAN_STATE_CHECK(!is_downgrading()); - BOTAN_STATE_CHECK(is_handshake_complete()); + BOTAN_STATE_CHECK(!is_downgrading() && is_handshake_complete() && is_active()); BOTAN_ASSERT_NONNULL(m_cipher_state); send_post_handshake_message(Key_Update(request_peer_update)); m_cipher_state->update_write_keys(*this); @@ -349,7 +368,7 @@ } void Channel_Impl_13::process_alert(const secure_vector& record) { - Alert alert(record); + const Alert alert(record); if(is_close_notify_alert(alert)) { m_can_read = false; @@ -397,6 +416,7 @@ m_can_read = false; m_can_write = false; m_cipher_state.reset(); + m_active_state.reset(); } void Channel_Impl_13::expect_downgrade(const Server_Information& server_info, diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_channel_impl_13.h botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_channel_impl_13.h --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_channel_impl_13.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_channel_impl_13.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,8 +10,10 @@ #ifndef BOTAN_TLS_CHANNEL_IMPL_13_H_ #define BOTAN_TLS_CHANNEL_IMPL_13_H_ +#include #include #include +#include #include #include #include @@ -27,7 +29,7 @@ * * The class is split from the rest of the Channel_Impl_13 for mockability. */ -class Secret_Logger { +class Secret_Logger /* NOLINT(*-special-member-functions) */ { public: virtual ~Secret_Logger() = default; @@ -78,10 +80,10 @@ bool contains_messages() const { return !m_message_buffer.empty(); } protected: - std::vector m_message_buffer; + std::vector m_message_buffer; // NOLINT(*non-private-member-variable*) - Channel_Impl_13& m_channel; - Handshake_Layer& m_handshake_layer; + Channel_Impl_13& m_channel; // NOLINT(*non-private-member-variable*) + Handshake_Layer& m_handshake_layer; // NOLINT(*non-private-member-variable*) }; /** @@ -136,9 +138,10 @@ const std::shared_ptr& policy, bool is_server); - explicit Channel_Impl_13(const Channel_Impl_13&) = delete; - - Channel_Impl_13& operator=(const Channel_Impl_13&) = delete; + Channel_Impl_13(const Channel_Impl_13& other) = delete; + Channel_Impl_13(Channel_Impl_13&& other) = delete; + Channel_Impl_13& operator=(const Channel_Impl_13& other) = delete; + Channel_Impl_13& operator=(Channel_Impl_13&& other) = delete; ~Channel_Impl_13() override; @@ -283,9 +286,10 @@ void shutdown(); protected: - const Connection_Side m_side; - Transcript_Hash_State m_transcript_hash; - std::unique_ptr m_cipher_state; + const Connection_Side m_side; // NOLINT(*non-private-member-variable*) + Transcript_Hash_State m_transcript_hash; // NOLINT(*non-private-member-variable*) + std::unique_ptr m_cipher_state; // NOLINT(*non-private-member-variable*) + std::optional m_active_state; // NOLINT(*non-private-member-variable*) /** * Indicate that we have to expect a downgrade to TLS 1.2. In which case the current @@ -336,6 +340,8 @@ bool m_opportunistic_key_update; bool m_first_message_sent; bool m_first_message_received; + + uint64_t m_last_key_update_ms = 0; }; } // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_cipher_state.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_cipher_state.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_cipher_state.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_cipher_state.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -14,7 +14,7 @@ * v * PSK -> HKDF-Extract = Early Secret * | - * +-----> Derive-Secret(., "ext binder" | "res binder", "") + * +-----> Derive-Secret(., "ext binder" | "res binder" | "imp binder", "") * | = binder_key * STATE PSK BINDER * This state is reached by constructing the Cipher_State using init_with_psk(). @@ -122,10 +122,10 @@ secure_vector&& shared_secret, const Ciphersuite& cipher, const Transcript_Hash& transcript_hash, - const Secret_Logger& loggger) { + const Secret_Logger& logger) { auto cs = std::unique_ptr(new Cipher_State(side, cipher.prf_algo())); cs->advance_without_psk(); - cs->advance_with_server_hello(cipher, std::move(shared_secret), transcript_hash, loggger); + cs->advance_with_server_hello(cipher, std::move(shared_secret), transcript_hash, logger); return cs; } @@ -138,7 +138,7 @@ return cs; } -void Cipher_State::advance_with_client_hello(const Transcript_Hash& transcript_hash, const Secret_Logger& loggger) { +void Cipher_State::advance_with_client_hello(const Transcript_Hash& transcript_hash, const Secret_Logger& logger) { BOTAN_ASSERT_NOMSG(m_state == State::PskBinder); zap(m_binder_key); @@ -155,7 +155,7 @@ // An implementation of TLS 1.3 use the label // "EARLY_EXPORTER_MASTER_SECRET" to identify the secret that is using for // early exporters - loggger.maybe_log_secret("EARLY_EXPORTER_MASTER_SECRET", m_exporter_master_secret); + logger.maybe_log_secret("EARLY_EXPORTER_MASTER_SECRET", m_exporter_master_secret); m_salt = derive_secret(m_early_secret, "derived", empty_hash()); zap(m_early_secret); @@ -163,7 +163,7 @@ m_state = State::EarlyTraffic; } -void Cipher_State::advance_with_server_finished(const Transcript_Hash& transcript_hash, const Secret_Logger& loggger) { +void Cipher_State::advance_with_server_finished(const Transcript_Hash& transcript_hash, const Secret_Logger& logger) { BOTAN_ASSERT_NOMSG(m_state == State::HandshakeTraffic); const auto master_secret = hkdf_extract(secure_vector(m_hash->output_length(), 0x00)); @@ -175,8 +175,8 @@ // An implementation of TLS 1.3 use the label "CLIENT_TRAFFIC_SECRET_0" // and "SERVER_TRAFFIC_SECRET_0" to identify the secrets are using to // protect the connection. - loggger.maybe_log_secret("CLIENT_TRAFFIC_SECRET_0", client_application_traffic_secret); - loggger.maybe_log_secret("SERVER_TRAFFIC_SECRET_0", server_application_traffic_secret); + logger.maybe_log_secret("CLIENT_TRAFFIC_SECRET_0", client_application_traffic_secret); + logger.maybe_log_secret("SERVER_TRAFFIC_SECRET_0", server_application_traffic_secret); // Note: the secrets for processing client's application data // are not derived before the client's Finished message @@ -197,7 +197,7 @@ // An implementation of TLS 1.3 use the label "EXPORTER_SECRET" to // identify the secret that is used in generating exporters(rfc8446 // Section 7.5). - loggger.maybe_log_secret("EXPORTER_SECRET", m_exporter_master_secret); + logger.maybe_log_secret("EXPORTER_SECRET", m_exporter_master_secret); m_state = State::ServerApplicationTraffic; } @@ -248,7 +248,12 @@ uint64_t Cipher_State::encrypt_record_fragment(const std::vector& header, secure_vector& fragment) { BOTAN_ASSERT_NONNULL(m_encrypt); - m_encrypt->set_key(m_write_key); + // RFC 8446 5.3 + // Sequence numbers MUST NOT wrap. + if(m_write_seq_no == std::numeric_limits::max()) { + throw Invalid_State("TLS write sequence number overflow"); + } + m_encrypt->set_associated_data(header); m_encrypt->start(current_nonce(m_write_seq_no, m_write_iv)); m_encrypt->finish(fragment); @@ -261,7 +266,12 @@ BOTAN_ASSERT_NONNULL(m_decrypt); BOTAN_ARG_CHECK(encrypted_fragment.size() >= m_decrypt->minimum_final_size(), "fragment too short to decrypt"); - m_decrypt->set_key(m_read_key); + // RFC 8446 5.3 + // Sequence numbers MUST NOT wrap. + if(m_read_seq_no == std::numeric_limits::max()) { + throw Invalid_State("TLS read sequence number overflow"); + } + m_decrypt->set_associated_data(header); m_decrypt->start(current_nonce(m_read_seq_no, m_read_iv)); @@ -357,12 +367,17 @@ } BOTAN_ASSERT_NOMSG((m_encrypt == nullptr) == (m_decrypt == nullptr)); - // TODO: Find a better way to check that the instantiated cipher algorithm - // is compatible with the one required by the cipher suite. - // AEAD_Mode::create() sets defaults the tag length to 16 which is then - // reported via AEAD_Mode::name() and hinders the trivial string comparison. - if(m_encrypt && m_encrypt->name() != cipher.cipher_algo() && m_encrypt->name() != cipher.cipher_algo() + "(16)") { - return false; + // Compare canonical AEAD names rather than substring-matching cipher_algo + // against m_encrypt->name(). starts_with() is both too permissive (an + // AES-128/CCM-8 instance starts with "AES-128/CCM" so it would accept the + // CCM-16 suite) and too restrictive (cipher_algo "AES-128/CCM(8)" does not + // prefix the canonical "AES-128/CCM(8,3)"). Re-instantiating the AEAD from + // cipher_algo yields the same canonical name() the suite would produce. + if(m_encrypt) { + auto canonical = AEAD_Mode::create(cipher.cipher_algo(), Cipher_Dir::Encryption); + if(!canonical || canonical->name() != m_encrypt->name()) { + return false; + } } return true; @@ -409,12 +424,17 @@ Ticket_Nonce Cipher_State::next_ticket_nonce() { BOTAN_STATE_CHECK(m_state == State::Completed); - if(m_ticket_nonce == std::numeric_limits::max()) { + if(m_ticket_nonce_exhausted) { throw Botan::Invalid_State("ticket nonce pool exhausted"); } - Ticket_Nonce retval(std::vector(sizeof(m_ticket_nonce))); - store_be(m_ticket_nonce++, retval.data()); + auto retval = store_be(m_ticket_nonce); + + if(m_ticket_nonce == std::numeric_limits::max()) { + m_ticket_nonce_exhausted = true; + } else { + ++m_ticket_nonce; + } return retval; } @@ -468,7 +488,18 @@ m_early_secret = hkdf_extract(std::move(psk)); - const char* binder_label = (type == PSK_Type::Resumption) ? "res binder" : "ext binder"; + // RFC 8446 and RFC 9258 specify these strings + const char* binder_label = [type]() -> const char* { + switch(type) { + case PSK_Type::Resumption: + return "res binder"; + case PSK_Type::External: + return "ext binder"; + case PSK_Type::Imported: + return "imp binder"; + } + BOTAN_ASSERT_UNREACHABLE(); + }(); // RFC 8446 4.2.11.2 // The PskBinderEntry is computed in the same way as the Finished message @@ -485,7 +516,7 @@ void Cipher_State::advance_with_server_hello(const Ciphersuite& cipher, secure_vector&& shared_secret, const Transcript_Hash& transcript_hash, - const Secret_Logger& loggger) { + const Secret_Logger& logger) { BOTAN_ASSERT_NOMSG(m_state == State::EarlyTraffic); BOTAN_ASSERT_NOMSG(!m_encrypt); BOTAN_ASSERT_NOMSG(!m_decrypt); @@ -503,8 +534,8 @@ // An implementation of TLS 1.3 use the label // "CLIENT_HANDSHAKE_TRAFFIC_SECRET" and "SERVER_HANDSHAKE_TRAFFIC_SECRET" // to identify the secrets are using to protect handshake messages. - loggger.maybe_log_secret("CLIENT_HANDSHAKE_TRAFFIC_SECRET", client_handshake_traffic_secret); - loggger.maybe_log_secret("SERVER_HANDSHAKE_TRAFFIC_SECRET", server_handshake_traffic_secret); + logger.maybe_log_secret("CLIENT_HANDSHAKE_TRAFFIC_SECRET", client_handshake_traffic_secret); + logger.maybe_log_secret("SERVER_HANDSHAKE_TRAFFIC_SECRET", server_handshake_traffic_secret); if(m_connection_side == Connection_Side::Server) { derive_read_traffic_key(client_handshake_traffic_secret, true); @@ -527,6 +558,8 @@ m_write_iv = hkdf_expand_label(traffic_secret, "iv", {}, NONCE_LENGTH); m_write_seq_no = 0; + m_encrypt->set_key(m_write_key); + if(handshake_traffic_secret) { // Key derivation for the MAC in the "Finished" handshake message as described in RFC 8446 4.4.4 // (will be cleared in advance_with_server_finished()) @@ -536,12 +569,14 @@ void Cipher_State::derive_read_traffic_key(const secure_vector& traffic_secret, const bool handshake_traffic_secret) { - BOTAN_ASSERT_NONNULL(m_encrypt); + BOTAN_ASSERT_NONNULL(m_decrypt); - m_read_key = hkdf_expand_label(traffic_secret, "key", {}, m_encrypt->minimum_keylength()); + m_read_key = hkdf_expand_label(traffic_secret, "key", {}, m_decrypt->minimum_keylength()); m_read_iv = hkdf_expand_label(traffic_secret, "iv", {}, NONCE_LENGTH); m_read_seq_no = 0; + m_decrypt->set_key(m_read_key); + if(handshake_traffic_secret) { // Key derivation for the MAC in the "Finished" handshake message as described in RFC 8446 4.4.4 // (will be cleared in advance_with_client_finished()) diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_cipher_state.h botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_cipher_state.h --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_cipher_state.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_cipher_state.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,7 +11,6 @@ #include #include -#include #include @@ -61,14 +60,20 @@ */ class BOTAN_TEST_API Cipher_State { public: - enum class PSK_Type { - Resumption, - External, // currently not implemented + enum class PSK_Type : uint8_t { + Resumption, // RFC 8446 + External, // RFC 8446 + Imported, // RFC 9258 PSK importer - uses "imp binder" label }; public: ~Cipher_State(); + Cipher_State(const Cipher_State& other) = delete; + Cipher_State(Cipher_State&& other) = delete; + Cipher_State& operator=(const Cipher_State& other) = delete; + Cipher_State& operator=(Cipher_State&& other) = delete; + /** * Construct a Cipher_State from a Pre-Shared-Key. */ @@ -295,7 +300,7 @@ std::vector empty_hash() const; private: - enum class State { + enum class State : uint8_t { Uninitialized, PskBinder, EarlyTraffic, @@ -332,6 +337,7 @@ uint32_t m_read_key_update_count; uint16_t m_ticket_nonce; + bool m_ticket_nonce_exhausted = false; secure_vector m_finished_key; secure_vector m_peer_finished_key; diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_client_impl_13.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_client_impl_13.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_client_impl_13.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_client_impl_13.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,18 +6,19 @@ * * Botan is released under the Simplified BSD License (see license.txt) */ + #include #include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include #include -#include #include namespace Botan::TLS { @@ -31,7 +32,7 @@ const std::vector& next_protocols) : Channel_Impl_13(callbacks, session_manager, creds, rng, policy, false /* is_server */), m_info(std::move(info)), - m_should_send_ccs(false) { + m_handshake(std::make_unique()) { #if defined(BOTAN_HAS_TLS_12) if(policy->allow_tls12()) { expect_downgrade(m_info, next_protocols); @@ -40,7 +41,7 @@ if(auto session = find_session_for_resumption()) { if(!session->session.version().is_pre_tls_13()) { - m_resumed_session = std::move(session); + m_handshake->resumed_session = std::move(session); } else if(expects_downgrade()) { // If we found a session that was created with TLS 1.2, we downgrade // the implementation right away, before even issuing a Client Hello. @@ -49,13 +50,13 @@ } } - auto msg = send_handshake_message(m_handshake_state.sending( + auto msg = send_handshake_message(m_handshake->state.sending( Client_Hello_13(*policy, *callbacks, *rng, m_info.hostname(), next_protocols, - m_resumed_session, + m_handshake->resumed_session, creds->find_preshared_keys(m_info.hostname(), Connection_Side::Client)))); if(expects_downgrade()) { @@ -69,17 +70,19 @@ // // TODO: don't schedule ccs here when early data is used if(policy->tls_13_middlebox_compatibility_mode()) { - m_should_send_ccs = true; + m_handshake->should_send_ccs = true; } - m_transitions.set_expected_next({Handshake_Type::ServerHello, Handshake_Type::HelloRetryRequest}); + m_handshake->transitions.set_expected_next({Handshake_Type::ServerHello, Handshake_Type::HelloRetryRequest}); } void Client_Impl_13::process_handshake_msg(Handshake_Message_13 message) { + BOTAN_STATE_CHECK(m_handshake != nullptr); + std::visit( [&](auto msg) { // first verify that the message was expected by the state machine... - m_transitions.confirm_transition_to(msg.get().type()); + m_handshake->transitions.confirm_transition_to(msg.get().type()); // ... then allow the library user to abort on their discretion callbacks().tls_inspect_handshake_msg(msg.get()); @@ -87,13 +90,18 @@ // ... finally handle the message handle(msg.get()); }, - m_handshake_state.received(std::move(message))); + m_handshake->state.received(std::move(message))); } void Client_Impl_13::process_post_handshake_msg(Post_Handshake_Message_13 message) { BOTAN_STATE_CHECK(is_handshake_complete()); - std::visit([&](auto msg) { handle(msg); }, m_handshake_state.received(std::move(message))); + const auto msg = specialize_to(std::move(message)); + if(!msg) { + throw TLS_Exception(Alert::UnexpectedMessage, "received an unexpected post-handshake message"); + } + + std::visit([&](auto&& m) { handle(m); }, *msg); } void Client_Impl_13::process_dummy_change_cipher_spec() { @@ -101,7 +109,7 @@ // If an implementation detects a change_cipher_spec record received before // the first ClientHello message or after the peer's Finished message, it MUST be // treated as an unexpected record type [("unexpected_message" alert)]. - if(!m_handshake_state.has_client_hello() || m_handshake_state.has_server_finished()) { + if(!m_handshake || !m_handshake->state.has_client_hello() || m_handshake->state.has_server_finished()) { throw TLS_Exception(Alert::UnexpectedMessage, "Received an unexpected dummy Change Cipher Spec"); } @@ -115,7 +123,7 @@ } bool Client_Impl_13::is_handshake_complete() const { - return m_handshake_state.handshake_finished(); + return m_active_state.has_value(); } std::optional Client_Impl_13::find_session_for_resumption() { @@ -147,8 +155,10 @@ return std::move(session_to_resume); } -void Client_Impl_13::handle(const Server_Hello_12& server_hello_msg) { - if(m_handshake_state.has_hello_retry_request()) { +void Client_Impl_13::handle(const Server_Hello_12_Shim& server_hello_msg) { + BOTAN_ASSERT_NONNULL(m_handshake); + + if(m_handshake->state.has_hello_retry_request()) { throw TLS_Exception(Alert::UnexpectedMessage, "Version downgrade received after Hello Retry"); } @@ -179,7 +189,7 @@ // MUST NOT send the "supported_versions" extension. // // Note that this condition should never happen, as the Server_Hello parsing - // code decides to create a Server_Hello_12 based on the absense of this extension. + // code decides to create a Server_Hello_12 based on the absence of this extension. if(server_hello_msg.extensions().has()) { throw TLS_Exception(Alert::IllegalParameter, "Unexpected extension received"); } @@ -188,14 +198,14 @@ // If the version chosen by the server is not supported by the client // (or is not acceptable), the client MUST abort the handshake with a // "protocol_version" alert. - const auto& client_hello_exts = m_handshake_state.client_hello().extensions(); + const auto& client_hello_exts = m_handshake->state.client_hello().extensions(); BOTAN_ASSERT_NOMSG(client_hello_exts.has()); if(!client_hello_exts.get()->supports(server_hello_msg.selected_version())) { throw TLS_Exception(Alert::ProtocolVersion, "Protocol version was not offered"); } if(policy().tls_13_middlebox_compatibility_mode() && - m_handshake_state.client_hello().session_id() == server_hello_msg.session_id()) { + m_handshake->state.client_hello().session_id() == server_hello_msg.session_id()) { // In compatibility mode, the server will reflect the session ID we sent in the client hello. // However, a TLS 1.2 server that wants to downgrade cannot have found the random session ID // we sent. Therefore, we have to consider this as an attack. @@ -207,7 +217,7 @@ // After this, no further messages are expected here because this instance will be replaced // by a Client_Impl_12. - m_transitions.set_expected_next({}); + m_handshake->transitions.set_expected_next({}); } namespace { @@ -243,11 +253,22 @@ void Client_Impl_13::handle(const Server_Hello_13& sh) { // Note: Basic checks (that do not require contextual information) were already // performed during the construction of the Server_Hello_13 object. + BOTAN_ASSERT_NONNULL(m_handshake); - const auto& ch = m_handshake_state.client_hello(); + const auto& ch = m_handshake->state.client_hello(); validate_server_hello_ish(ch, sh); + // RFC 8446 4.1.3: TLS 1.3 servers downgrading to TLS 1.2 or below set + // the last 8 bytes of ServerHello.random to a magic value so the client + // can detect a stripped-supported_versions downgrade attack. The Shim + // path (Server_Hello_12_Shim) already enforces this; catch it here too + // as defense in depth in case a misbehaving server writes the sentinel + // into an actual TLS 1.3 ServerHello. + if(sh.random_signals_downgrade().has_value()) { + throw TLS_Exception(Alert::IllegalParameter, "Downgrade attack detected"); + } + // RFC 8446 4.2 // Implementations MUST NOT send extension responses if the remote // endpoint did not send the corresponding extension requests, [...]. Upon @@ -257,8 +278,8 @@ throw TLS_Exception(Alert::UnsupportedExtension, "Unsupported extension found in Server Hello"); } - if(m_handshake_state.has_hello_retry_request()) { - const auto& hrr = m_handshake_state.hello_retry_request(); + if(m_handshake->state.has_hello_retry_request()) { + const auto& hrr = m_handshake->state.hello_retry_request(); // RFC 8446 4.1.4 // Upon receiving the ServerHello, clients MUST check that the cipher suite @@ -303,20 +324,20 @@ throw TLS_Exception(Alert::IllegalParameter, "Server Hello did not contain a key share extension"); } - auto my_keyshare = ch.extensions().get(); + auto* my_keyshare = ch.extensions().get(); auto shared_secret = my_keyshare->decapsulate(*sh.extensions().get(), policy(), callbacks(), rng()); m_transcript_hash.set_algorithm(cipher.value().prf_algo()); if(sh.extensions().has()) { - std::tie(m_psk_identity, m_cipher_state) = + std::tie(m_handshake->psk_identity, m_cipher_state) = ch.extensions().get()->take_selected_psk_info(*sh.extensions().get(), cipher.value()); // If we offered a session for resumption *and* an externally provided PSK // and the latter was chosen by the server over the offered resumption, we - // want to invalidate the now-outdated session in m_resumed_session. - if(m_psk_identity.has_value() && m_resumed_session.has_value()) { - m_resumed_session.reset(); + // want to invalidate the now-outdated session in m_handshake->resumed_session. + if(m_handshake->psk_identity.has_value() && m_handshake->resumed_session.has_value()) { + m_handshake->resumed_session.reset(); } // TODO: When implementing early data, `advance_with_client_hello` must @@ -326,22 +347,23 @@ m_cipher_state->advance_with_server_hello( cipher.value(), std::move(shared_secret), m_transcript_hash.current(), *this); } else { - m_resumed_session.reset(); // might have been set if we attempted a resumption + m_handshake->resumed_session.reset(); // might have been set if we attempted a resumption m_cipher_state = Cipher_State::init_with_server_hello( m_side, std::move(shared_secret), cipher.value(), m_transcript_hash.current(), *this); } callbacks().tls_examine_extensions(sh.extensions(), Connection_Side::Server, Handshake_Type::ServerHello); - m_transitions.set_expected_next(Handshake_Type::EncryptedExtensions); + m_handshake->transitions.set_expected_next(Handshake_Type::EncryptedExtensions); } void Client_Impl_13::handle(const Hello_Retry_Request& hrr) { // Note: Basic checks (that do not require contextual information) were already // performed during the construction of the Hello_Retry_Request object as // a subclass of Server_Hello_13. + BOTAN_ASSERT_NONNULL(m_handshake); - auto& ch = m_handshake_state.client_hello(); + auto& ch = m_handshake->state.client_hello(); validate_server_hello_ish(ch, hrr); @@ -358,6 +380,13 @@ auto cipher = Ciphersuite::by_id(hrr.ciphersuite()); BOTAN_ASSERT_NOMSG(cipher.has_value()); // should work, since we offered this suite + // RFC 8446 4.1.4 / Appendix B.4 + // Similarly, cipher suites for TLS 1.2 and lower cannot be used with + // TLS 1.3. + if(!cipher->usable_in_version(Protocol_Version::TLS_V13)) { + throw TLS_Exception(Alert::IllegalParameter, "HelloRetryRequest selected a cipher suite not usable in TLS 1.3"); + } + m_transcript_hash = Transcript_Hash_State::recreate_after_hello_retry_request(cipher.value().prf_algo(), m_transcript_hash); @@ -370,10 +399,12 @@ // RFC 8446 4.1.4 // If a client receives a second HelloRetryRequest in the same connection [...], // it MUST abort the handshake with an "unexpected_message" alert. - m_transitions.set_expected_next(Handshake_Type::ServerHello); + m_handshake->transitions.set_expected_next(Handshake_Type::ServerHello); } void Client_Impl_13::handle(const Encrypted_Extensions& encrypted_extensions_msg) { + BOTAN_ASSERT_NONNULL(m_handshake); + const auto& exts = encrypted_extensions_msg.extensions(); // RFC 8446 4.2 @@ -381,7 +412,7 @@ // endpoint did not send the corresponding extension requests, [...]. Upon // receiving such an extension, an endpoint MUST abort the handshake // with an "unsupported_extension" alert. - const auto& requested_exts = m_handshake_state.client_hello().extensions().extension_types(); + const auto& requested_exts = m_handshake->state.client_hello().extensions().extension_types(); if(exts.contains_other_than(requested_exts)) { throw TLS_Exception(Alert::UnsupportedExtension, "Encrypted Extensions contained an extension that was not offered"); @@ -390,7 +421,23 @@ // Note: As per RFC 6066 3. we can check for an empty SNI extensions to // determine if the server used the SNI we sent here. - if(exts.has() && m_handshake_state.client_hello().extensions().has()) { + if(exts.has()) { + // RFC 7301 3.2 + // The "extension_data" field of the [...] "application_layer_protocol_negotiation" + // extension [...] SHALL include the server's selection of a protocol from among + // the list that was advertised by the client. + const auto* server_alpn = exts.get(); + const auto selected = server_alpn->single_protocol(); + const auto* client_alpn = + m_handshake->state.client_hello().extensions().get(); + BOTAN_ASSERT_NONNULL(client_alpn); // unrequested extension check above ensures this + const auto& offered = client_alpn->protocols(); + if(!value_exists(offered, selected)) { + throw TLS_Exception(Alert::IllegalParameter, "Server selected an ALPN protocol not offered by the client"); + } + } + + if(exts.has() && m_handshake->state.client_hello().extensions().has()) { // RFC 8449 4. // The record size limit only applies to records sent toward the // endpoint that advertises the limit. An endpoint can send records @@ -398,15 +445,16 @@ // // Hence, the "outgoing" limit is what the server requested and the // "incoming" limit is what we requested in the Client Hello. - const auto outgoing_limit = exts.get(); - const auto incoming_limit = m_handshake_state.client_hello().extensions().get(); + auto* const outgoing_limit = exts.get(); + auto* const incoming_limit = m_handshake->state.client_hello().extensions().get(); set_record_size_limits(outgoing_limit->limit(), incoming_limit->limit()); } - if(exts.has() && - m_handshake_state.client_hello().extensions().has()) { + if(exts.has()) { + // The unrequested-extension check above ensures the client offered this. + BOTAN_ASSERT_NOMSG(m_handshake->state.client_hello().extensions().has()); const auto* server_cert_type = exts.get(); - const auto* our_server_cert_types = m_handshake_state.client_hello().extensions().get(); + const auto* our_server_cert_types = m_handshake->state.client_hello().extensions().get(); our_server_cert_types->validate_selection(*server_cert_type); // RFC 7250 4.2 @@ -421,17 +469,19 @@ callbacks().tls_examine_extensions(exts, Connection_Side::Server, Handshake_Type::EncryptedExtensions); - if(m_handshake_state.server_hello().extensions().has()) { + if(m_handshake->state.server_hello().extensions().has()) { // RFC 8446 2.2 // As the server is authenticating via a PSK, it does not send a // Certificate or a CertificateVerify message. - m_transitions.set_expected_next(Handshake_Type::Finished); + m_handshake->transitions.set_expected_next(Handshake_Type::Finished); } else { - m_transitions.set_expected_next({Handshake_Type::Certificate, Handshake_Type::CertificateRequest}); + m_handshake->transitions.set_expected_next({Handshake_Type::Certificate, Handshake_Type::CertificateRequest}); } } void Client_Impl_13::handle(const Certificate_Request_13& certificate_request_msg) { + BOTAN_ASSERT_NONNULL(m_handshake); + // RFC 8446 4.3.2 // [The 'context' field] SHALL be zero length unless used for the // post-handshake authentication exchanges described in Section 4.6.2. @@ -441,10 +491,12 @@ callbacks().tls_examine_extensions( certificate_request_msg.extensions(), Connection_Side::Server, Handshake_Type::CertificateRequest); - m_transitions.set_expected_next(Handshake_Type::Certificate); + m_handshake->transitions.set_expected_next(Handshake_Type::Certificate); } void Client_Impl_13::handle(const Certificate_13& certificate_msg) { + BOTAN_ASSERT_NONNULL(m_handshake); + // RFC 8446 4.4.2 // certificate_request_context: [...] In the case of server authentication, // this field SHALL be zero length. @@ -455,17 +507,19 @@ // RFC 8446 4.4.2 // Extensions in the Certificate message from the server MUST correspond // to ones from the ClientHello message. - certificate_msg.validate_extensions(m_handshake_state.client_hello().extensions().extension_types(), callbacks()); + certificate_msg.validate_extensions(m_handshake->state.client_hello().extensions().extension_types(), callbacks()); certificate_msg.verify(callbacks(), policy(), credentials_manager(), m_info.hostname(), - m_handshake_state.client_hello().extensions().has()); + m_handshake->state.client_hello().extensions().has()); - m_transitions.set_expected_next(Handshake_Type::CertificateVerify); + m_handshake->transitions.set_expected_next(Handshake_Type::CertificateVerify); } void Client_Impl_13::handle(const Certificate_Verify_13& certificate_verify_msg) { + BOTAN_ASSERT_NONNULL(m_handshake); + // RFC 8446 4.4.3 // If the CertificateVerify message is sent by a server, the signature // algorithm MUST be one offered in the client's "signature_algorithms" @@ -474,31 +528,33 @@ // // Note: if the server failed to produce a certificate chain without using // an unsupported signature scheme, we opt to abort the handshake. - const auto offered = m_handshake_state.client_hello().signature_schemes(); + const auto offered = m_handshake->state.client_hello().signature_schemes(); if(!value_exists(offered, certificate_verify_msg.signature_scheme())) { throw TLS_Exception(Alert::IllegalParameter, "We did not offer the usage of " + certificate_verify_msg.signature_scheme().to_string() + " as a signature scheme"); } - bool sig_valid = certificate_verify_msg.verify( - *m_handshake_state.server_certificate().public_key(), callbacks(), m_transcript_hash.previous()); + const bool sig_valid = certificate_verify_msg.verify( + *m_handshake->state.server_certificate().public_key(), callbacks(), m_transcript_hash.previous()); if(!sig_valid) { throw TLS_Exception(Alert::DecryptError, "Server certificate verification failed"); } - m_transitions.set_expected_next(Handshake_Type::Finished); + m_handshake->transitions.set_expected_next(Handshake_Type::Finished); } void Client_Impl_13::send_client_authentication(Channel_Impl_13::AggregatedHandshakeMessages& flight) { - BOTAN_ASSERT_NOMSG(m_handshake_state.has_certificate_request()); - const auto& cert_request = m_handshake_state.certificate_request(); + BOTAN_ASSERT_NOMSG(m_handshake->state.has_certificate_request()); + const auto& cert_request = m_handshake->state.certificate_request(); const auto cert_type = [&] { - const auto& exts = m_handshake_state.encrypted_extensions().extensions(); - const auto& chexts = m_handshake_state.client_hello().extensions(); - if(exts.has() && chexts.has()) { + const auto& exts = m_handshake->state.encrypted_extensions().extensions(); + const auto& chexts = m_handshake->state.client_hello().extensions(); + if(exts.has()) { + // The unrequested-extension check in handle(Encrypted_Extensions) ensures the client offered this. + BOTAN_ASSERT_NOMSG(chexts.has()); const auto* client_cert_type = exts.get(); chexts.get()->validate_selection(*client_cert_type); @@ -523,7 +579,7 @@ // certificate_request_context: If this message is in response to a // CertificateRequest, the value of certificate_request_context in // that message. - flight.add(m_handshake_state.sending( + flight.add(m_handshake->state.sending( Certificate_13(cert_request, m_info.hostname(), credentials_manager(), callbacks(), cert_type))); // RFC 8446 4.4.2 @@ -532,20 +588,22 @@ // certificates. // // In that case, no Certificate Verify message will be sent. - if(!m_handshake_state.client_certificate().empty()) { - flight.add(m_handshake_state.sending(Certificate_Verify_13(m_handshake_state.client_certificate(), - cert_request.signature_schemes(), - m_info.hostname(), - m_transcript_hash.current(), - Connection_Side::Client, - credentials_manager(), - policy(), - callbacks(), - rng()))); + if(!m_handshake->state.client_certificate().empty()) { + flight.add(m_handshake->state.sending(Certificate_Verify_13(m_handshake->state.client_certificate(), + cert_request.signature_schemes(), + m_info.hostname(), + m_transcript_hash.current(), + Connection_Side::Client, + credentials_manager(), + policy(), + callbacks(), + rng()))); } } void Client_Impl_13::handle(const Finished_13& finished_msg) { + BOTAN_ASSERT_NONNULL(m_handshake); + // RFC 8446 4.4.4 // Recipients of Finished messages MUST verify that the contents are // correct and if incorrect MUST terminate the connection with a @@ -554,14 +612,16 @@ throw TLS_Exception(Alert::DecryptError, "Finished message didn't verify"); } + m_handshake->state.confirm_peer_finished_verified(); + // Give the application a chance for a final veto before fully // establishing the connection. - callbacks().tls_session_established(Session_Summary(m_handshake_state.server_hello(), - Connection_Side::Server, + callbacks().tls_session_established(Session_Summary(m_handshake->state.server_hello(), + Connection_Side::Client, peer_cert_chain(), peer_raw_public_key(), external_psk_identity(), - m_resumed_session.has_value(), + m_handshake->resumed_session.has_value(), m_info, callbacks().tls_current_timestamp())); @@ -574,12 +634,12 @@ // RFC 8446 4.4.2 // The client MUST send a Certificate message if and only if the server // has requested client authentication via a CertificateRequest message. - if(m_handshake_state.has_certificate_request()) { + if(m_handshake->state.has_certificate_request()) { send_client_authentication(flight); } // send client finished handshake message (still using handshake traffic secrets) - flight.add(m_handshake_state.sending(Finished_13(m_cipher_state.get(), m_transcript_hash.current()))); + flight.add(m_handshake->state.sending(Finished_13(m_cipher_state.get(), m_transcript_hash.current()))); flight.send(); @@ -591,77 +651,145 @@ // callback's doc string. // no more handshake messages expected - m_transitions.set_expected_next({}); + m_handshake->transitions.set_expected_next({}); + // Extract post-handshake state before signaling activation. + // After this point, only m_active_state should be consulted + // for connection properties. + { + auto extract_certs = [&]() -> std::vector { + if(m_handshake->state.has_server_certificate_msg() && + m_handshake->state.server_certificate().has_certificate_chain()) { + return m_handshake->state.server_certificate().cert_chain(); + } + if(m_handshake->resumed_session.has_value()) { + return m_handshake->resumed_session->session.peer_certs(); + } + return {}; + }; + + auto extract_raw_pk = [&]() -> std::shared_ptr { + if(m_handshake->state.has_server_certificate_msg() && + m_handshake->state.server_certificate().is_raw_public_key()) { + return m_handshake->state.server_certificate().public_key(); + } + if(m_handshake->resumed_session.has_value()) { + return m_handshake->resumed_session->session.peer_raw_public_key(); + } + return nullptr; + }; + + m_active_state = Active_Connection_State_13(m_handshake->state, + extract_certs(), + extract_raw_pk(), + m_handshake->psk_identity, + m_info.hostname(), + false /* peer_supports_psk_dhe_ke - client doesn't need this */); + } + + m_handshake.reset(); + m_transcript_hash = Transcript_Hash_State(); callbacks().tls_session_activated(); } void TLS::Client_Impl_13::handle(const New_Session_Ticket_13& new_session_ticket) { + BOTAN_STATE_CHECK(m_active_state.has_value()); + + if(const size_t max_tickets = policy().maximum_session_tickets_per_connection(); + max_tickets > 0 && m_session_tickets_received >= max_tickets) { + // Silently ignore excess tickets rather than terminating the connection, + // since the server may have legitimate reasons to send many tickets. + return; + } + ++m_session_tickets_received; + callbacks().tls_examine_extensions( new_session_ticket.extensions(), Connection_Side::Server, Handshake_Type::NewSessionTicket); - Session session(m_cipher_state->psk(new_session_ticket.nonce()), - new_session_ticket.early_data_byte_limit(), - new_session_ticket.ticket_age_add(), - new_session_ticket.lifetime_hint(), - m_handshake_state.server_hello().selected_version(), - m_handshake_state.server_hello().ciphersuite(), - Connection_Side::Client, - peer_cert_chain(), - peer_raw_public_key(), - m_info, - callbacks().tls_current_timestamp()); + const Session session(m_cipher_state->psk(new_session_ticket.nonce()), + new_session_ticket.early_data_byte_limit(), + new_session_ticket.ticket_age_add(), + new_session_ticket.lifetime_hint(), + m_active_state->version(), + m_active_state->ciphersuite_code(), + Connection_Side::Client, + peer_cert_chain(), + peer_raw_public_key(), + m_info, + callbacks().tls_current_timestamp()); if(callbacks().tls_should_persist_resumption_information(session)) { - session_manager().store(session, new_session_ticket.handle()); + session_manager().store(session, Session_Handle(new_session_ticket.handle())); } } std::vector Client_Impl_13::peer_cert_chain() const { - if(m_handshake_state.has_server_certificate_msg() && - m_handshake_state.server_certificate().has_certificate_chain()) { - return m_handshake_state.server_certificate().cert_chain(); + if(m_active_state.has_value()) { + return m_active_state->peer_certs(); } - if(m_resumed_session.has_value()) { - return m_resumed_session->session.peer_certs(); + // During handshake, before m_active_state is populated + if(m_handshake) { + if(m_handshake->state.has_server_certificate_msg() && + m_handshake->state.server_certificate().has_certificate_chain()) { + return m_handshake->state.server_certificate().cert_chain(); + } + + if(m_handshake->resumed_session.has_value()) { + return m_handshake->resumed_session->session.peer_certs(); + } } return {}; } std::shared_ptr Client_Impl_13::peer_raw_public_key() const { - if(m_handshake_state.has_server_certificate_msg() && m_handshake_state.server_certificate().is_raw_public_key()) { - return m_handshake_state.server_certificate().public_key(); + if(m_active_state.has_value()) { + return m_active_state->peer_raw_public_key(); } - if(m_resumed_session.has_value()) { - return m_resumed_session->session.peer_raw_public_key(); + // During handshake, before m_active_state is populated + if(m_handshake) { + if(m_handshake->state.has_server_certificate_msg() && + m_handshake->state.server_certificate().is_raw_public_key()) { + return m_handshake->state.server_certificate().public_key(); + } + + if(m_handshake->resumed_session.has_value()) { + return m_handshake->resumed_session->session.peer_raw_public_key(); + } } return nullptr; } std::optional Client_Impl_13::external_psk_identity() const { - return m_psk_identity; + if(m_active_state.has_value()) { + return m_active_state->psk_identity(); + } + if(m_handshake) { + return m_handshake->psk_identity; + } + return std::nullopt; } bool Client_Impl_13::prepend_ccs() { - return std::exchange(m_should_send_ccs, false); // test-and-set + return m_handshake && std::exchange(m_handshake->should_send_ccs, false); } void Client_Impl_13::maybe_log_secret(std::string_view label, std::span secret) const { if(policy().allow_ssl_key_log_file()) { - callbacks().tls_ssl_key_log_data(label, m_handshake_state.client_hello().random(), secret); + if(m_active_state.has_value()) { + callbacks().tls_ssl_key_log_data(label, m_active_state->client_random(), secret); + } else { + callbacks().tls_ssl_key_log_data(label, m_handshake->state.client_hello().random(), secret); + } } } std::string Client_Impl_13::application_protocol() const { - if(is_handshake_complete()) { - const auto& eee = m_handshake_state.encrypted_extensions().extensions(); - if(eee.has()) { - return eee.get()->single_protocol(); - } + if(m_active_state.has_value()) { + return m_active_state->application_protocol(); } return ""; diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_client_impl_13.h botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_client_impl_13.h --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_client_impl_13.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_client_impl_13.h 2026-05-07 01:38:28.000000000 +0000 @@ -25,7 +25,7 @@ /** * SSL/TLS Client 1.3 implementation */ -class Client_Impl_13 : public Channel_Impl_13 { +class Client_Impl_13 final : public Channel_Impl_13 { public: /** * Set up a new TLS client session @@ -88,7 +88,7 @@ bool prepend_ccs() override; using Channel_Impl_13::handle; - void handle(const Server_Hello_12& server_hello_msg); + void handle(const Server_Hello_12_Shim& server_hello_msg); void handle(const Server_Hello_13& server_hello_msg); void handle(const Hello_Retry_Request& hrr_msg); void handle(const Encrypted_Extensions& encrypted_extensions_msg); @@ -104,13 +104,16 @@ private: const Server_Information m_info; - Client_Handshake_State_13 m_handshake_state; - Handshake_Transitions m_transitions; + struct Pending_Handshake { + Client_Handshake_State_13 state; + Handshake_Transitions transitions; + bool should_send_ccs = false; + std::optional resumed_session; + std::optional psk_identity; + }; - bool m_should_send_ccs; - - std::optional m_resumed_session; - std::optional m_psk_identity; + std::unique_ptr m_handshake; + size_t m_session_tickets_received = 0; }; } // namespace TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_connection_state_13.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_connection_state_13.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_connection_state_13.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_connection_state_13.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,46 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan::TLS { + +namespace { + +std::string extract_alpn(const Internal::Handshake_State_13_Base& state) { + const auto& eee = state.encrypted_extensions().extensions(); + if(const auto* alpn = eee.get()) { + return alpn->single_protocol(); + } + return {}; +} + +} // namespace + +Active_Connection_State_13::~Active_Connection_State_13() = default; +Active_Connection_State_13::Active_Connection_State_13(Active_Connection_State_13&&) noexcept = default; +Active_Connection_State_13& Active_Connection_State_13::operator=(Active_Connection_State_13&&) noexcept = default; + +Active_Connection_State_13::Active_Connection_State_13(const Internal::Handshake_State_13_Base& state, + std::vector peer_certs, + std::shared_ptr peer_raw_public_key, + std::optional psk_identity, + std::string sni_hostname, + bool peer_supports_psk_dhe_ke) : + m_version(state.server_hello().selected_version()), + m_ciphersuite_code(state.server_hello().ciphersuite()), + m_application_protocol(extract_alpn(state)), + m_peer_certs(std::move(peer_certs)), + m_client_random(state.client_hello().random()), + m_psk_identity(std::move(psk_identity)), + m_peer_raw_public_key(std::move(peer_raw_public_key)), + m_sni_hostname(std::move(sni_hostname)), + m_peer_supports_psk_dhe_ke(peer_supports_psk_dhe_ke) {} + +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_connection_state_13.h botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_connection_state_13.h --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_connection_state_13.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_connection_state_13.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,82 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TLS_CONNECTION_STATE_13_H_ +#define BOTAN_TLS_CONNECTION_STATE_13_H_ + +#include +#include + +#include +#include +#include +#include + +namespace Botan { + +class Public_Key; + +} // namespace Botan + +namespace Botan::TLS { + +namespace Internal { +class Handshake_State_13_Base; +} + +/** +* Captures the state of a completed TLS 1.3 handshake that is needed +* for the lifetime of an active connection. +*/ +class Active_Connection_State_13 final { + public: + Active_Connection_State_13(const Internal::Handshake_State_13_Base& state, + std::vector peer_certs, + std::shared_ptr peer_raw_public_key, + std::optional psk_identity, + std::string sni_hostname, + bool peer_supports_psk_dhe_ke); + + ~Active_Connection_State_13(); + Active_Connection_State_13(Active_Connection_State_13&&) noexcept; + Active_Connection_State_13& operator=(Active_Connection_State_13&&) noexcept; + + Active_Connection_State_13(const Active_Connection_State_13&) = delete; + Active_Connection_State_13& operator=(const Active_Connection_State_13&) = delete; + + Protocol_Version version() const { return m_version; } + + uint16_t ciphersuite_code() const { return m_ciphersuite_code; } + + const std::string& application_protocol() const { return m_application_protocol; } + + const std::vector& peer_certs() const { return m_peer_certs; } + + const std::vector& client_random() const { return m_client_random; } + + const std::optional& psk_identity() const { return m_psk_identity; } + + const std::shared_ptr& peer_raw_public_key() const { return m_peer_raw_public_key; } + + const std::string& sni_hostname() const { return m_sni_hostname; } + + bool peer_supports_psk_dhe_ke() const { return m_peer_supports_psk_dhe_ke; } + + private: + Protocol_Version m_version; + uint16_t m_ciphersuite_code = 0; + std::string m_application_protocol; + std::vector m_peer_certs; + std::vector m_client_random; + std::optional m_psk_identity; + std::shared_ptr m_peer_raw_public_key; + std::string m_sni_hostname; + bool m_peer_supports_psk_dhe_ke = false; +}; + +} // namespace Botan::TLS + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_extensions_13.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_extensions_13.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_extensions_13.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_extensions_13.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,170 @@ +/* +* TLS 1.3 Specific Extensions +* (C) 2011,2012,2015,2016 Jack Lloyd +* 2016 Juraj Somorovsky +* 2021 Elektrobit Automotive GmbH +* 2022 René Meusel, Hannes Rantzsch - neXenio GmbH +* 2023 Mateusz Berezecki +* 2023 Fabian Albert, René Meusel - Rohde & Schwarz Cybersecurity +* 2026 René Meusel - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include + +namespace Botan::TLS { + +Cookie::Cookie(const std::vector& cookie) : m_cookie(cookie) {} + +Cookie::Cookie(TLS_Data_Reader& reader, uint16_t extension_size) { + // RFC 8446 4.2.2 + // struct { + // opaque cookie<1..2^16-1>; + // } Cookie; + // + // The wire form requires a 2-byte length field plus at least one byte of + // cookie data, so the minimum extension size is 3 bytes. + if(extension_size < 3) { + throw Decoding_Error("Empty cookie extension is illegal"); + } + + const uint16_t len = reader.get_uint16_t(); + + if(static_cast(len) + 2 != extension_size) { + throw Decoding_Error("Inconsistent length in cookie extension"); + } + + m_cookie = reader.get_fixed(len); +} + +std::vector Cookie::serialize(Connection_Side /*whoami*/) const { + std::vector buf; + append_tls_length_value(buf, m_cookie, 2); + return buf; +} + +std::vector PSK_Key_Exchange_Modes::serialize(Connection_Side /*whoami*/) const { + std::vector buf; + + BOTAN_ASSERT_NOMSG(m_modes.size() < 256); + buf.push_back(static_cast(m_modes.size())); + for(const auto& mode : m_modes) { + buf.push_back(static_cast(mode)); + } + + return buf; +} + +PSK_Key_Exchange_Modes::PSK_Key_Exchange_Modes(TLS_Data_Reader& reader, uint16_t extension_size) { + // RFC 8446 4.2.9 + // struct { + // PskKeyExchangeMode ke_modes<1..255>; + // } PskKeyExchangeModes; + // + // The wire form is a 1-byte length followed by mode_count mode bytes, + // with mode_count in [1, 255], so the extension size is in [2, 256]. + if(extension_size < 2) { + throw Decoding_Error("Empty psk_key_exchange_modes extension is illegal"); + } + + const auto mode_count = reader.get_byte(); + if(static_cast(mode_count) + 1 != extension_size) { + throw Decoding_Error("Inconsistent length in psk_key_exchange_modes extension"); + } + + for(uint16_t i = 0; i < mode_count; ++i) { + const auto mode = static_cast(reader.get_byte()); + if(mode == PSK_Key_Exchange_Mode::PSK_KE || mode == PSK_Key_Exchange_Mode::PSK_DHE_KE) { + m_modes.push_back(mode); + } + } +} + +std::vector Certificate_Authorities::serialize(Connection_Side /*whoami*/) const { + std::vector out; + std::vector dn_list; + + for(const auto& dn : m_distinguished_names) { + std::vector encoded_dn; + auto encoder = DER_Encoder(encoded_dn); + dn.encode_into(encoder); + append_tls_length_value(dn_list, encoded_dn, 2); + } + + append_tls_length_value(out, dn_list, 2); + + return out; +} + +Certificate_Authorities::Certificate_Authorities(TLS_Data_Reader& reader, uint16_t extension_size) { + if(extension_size < 2) { + throw Decoding_Error("Empty certificate_authorities extension is illegal"); + } + + const uint16_t purported_size = reader.get_uint16_t(); + + if(reader.remaining_bytes() != purported_size) { + throw Decoding_Error("Inconsistent length in certificate_authorities extension"); + } + + // RFC 8446 4.2.4: DistinguishedName authorities<3..2^16-1>; + if(purported_size < 3) { + throw Decoding_Error("Empty certificate_authorities list is illegal"); + } + + while(reader.has_remaining()) { + // RFC 8446 4.2.4: opaque DistinguishedName<1..2^16-1> + const std::vector name_bits = reader.get_range(2, 1, 65535); + + BER_Decoder decoder(name_bits, BER_Decoder::Limits::DER()); + m_distinguished_names.emplace_back(); + decoder.decode(m_distinguished_names.back()).verify_end(); + } +} + +Certificate_Authorities::Certificate_Authorities(std::vector acceptable_DNs) : + m_distinguished_names(std::move(acceptable_DNs)) {} + +std::vector EarlyDataIndication::serialize(Connection_Side /*whoami*/) const { + std::vector result; + if(m_max_early_data_size.has_value()) { + const auto max_data = m_max_early_data_size.value(); + result.push_back(get_byte<0>(max_data)); + result.push_back(get_byte<1>(max_data)); + result.push_back(get_byte<2>(max_data)); + result.push_back(get_byte<3>(max_data)); + } + return result; +} + +EarlyDataIndication::EarlyDataIndication(TLS_Data_Reader& reader, + uint16_t extension_size, + Handshake_Type message_type) { + if(message_type == Handshake_Type::NewSessionTicket) { + if(extension_size != 4) { + throw TLS_Exception(Alert::DecodeError, + "Received an early_data extension in a NewSessionTicket message " + "without maximum early data size indication"); + } + + m_max_early_data_size = reader.get_uint32_t(); + } else if(extension_size != 0) { + throw TLS_Exception(Alert::DecodeError, + "Received an early_data extension containing an unexpected data " + "size indication"); + } +} + +bool EarlyDataIndication::empty() const { + // This extension may be empty by definition but still carry information + return false; +} + +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_extensions_13.h botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_extensions_13.h --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_extensions_13.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_extensions_13.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,334 @@ +/* +* TLS 1.3 Specific Extensions +* (C) 2011,2012,2016,2018,2019 Jack Lloyd +* (C) 2016 Juraj Somorovsky +* (C) 2016 Matthias Gierlings +* (C) 2021 Elektrobit Automotive GmbH +* (C) 2022 René Meusel, Hannes Rantzsch - neXenio GmbH +* (C) 2023 Fabian Albert, René Meusel - Rohde & Schwarz Cybersecurity +* (C) 2026 René Meusel - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TLS_EXTENSIONS_13_H_ +#define BOTAN_TLS_EXTENSIONS_13_H_ + +#include +#include +#include +#include + +namespace Botan { + +class RandomNumberGenerator; +class Credentials_Manager; + +namespace TLS { + +class Callbacks; +class Cipher_State; +class Ciphersuite; +class Policy; +class Session_Manager; +class TLS_Data_Reader; +class Transcript_Hash_State; + +enum class PSK_Key_Exchange_Mode : uint8_t { PSK_KE = 0, PSK_DHE_KE = 1 }; + +/** +* Cookie from RFC 8446 4.2.2 +*/ +class BOTAN_UNSTABLE_API Cookie final : public Extension { + public: + static Extension_Code static_type() { return Extension_Code::Cookie; } + + Extension_Code type() const override { return static_type(); } + + std::vector serialize(Connection_Side whoami) const override; + + bool empty() const override { return m_cookie.empty(); } + + const std::vector& get_cookie() const { return m_cookie; } + + explicit Cookie(const std::vector& cookie); + + explicit Cookie(TLS_Data_Reader& reader, uint16_t extension_size); + + private: + std::vector m_cookie; +}; + +/** +* Pre-Shared Key Exchange Modes from RFC 8446 4.2.9 +*/ +class BOTAN_UNSTABLE_API PSK_Key_Exchange_Modes final : public Extension { + public: + static Extension_Code static_type() { return Extension_Code::PskKeyExchangeModes; } + + Extension_Code type() const override { return static_type(); } + + std::vector serialize(Connection_Side whoami) const override; + + bool empty() const override { return m_modes.empty(); } + + const std::vector& modes() const { return m_modes; } + + explicit PSK_Key_Exchange_Modes(std::vector modes) : m_modes(std::move(modes)) {} + + explicit PSK_Key_Exchange_Modes(TLS_Data_Reader& reader, uint16_t extension_size); + + private: + std::vector m_modes; +}; + +/** + * Certificate Authorities Extension from RFC 8446 4.2.4 + */ +class BOTAN_UNSTABLE_API Certificate_Authorities final : public Extension { + public: + static Extension_Code static_type() { return Extension_Code::CertificateAuthorities; } + + Extension_Code type() const override { return static_type(); } + + std::vector serialize(Connection_Side whoami) const override; + + bool empty() const override { return m_distinguished_names.empty(); } + + const std::vector& distinguished_names() const { return m_distinguished_names; } + + Certificate_Authorities(TLS_Data_Reader& reader, uint16_t extension_size); + explicit Certificate_Authorities(std::vector acceptable_DNs); + + private: + std::vector m_distinguished_names; +}; + +/** + * Pre-Shared Key extension from RFC 8446 4.2.11 + */ +class BOTAN_UNSTABLE_API PSK final : public Extension /* NOLINT(*-special-member-functions) */ { + public: + static Extension_Code static_type() { return Extension_Code::PresharedKey; } + + Extension_Code type() const override { return static_type(); } + + std::vector serialize(Connection_Side side) const override; + + /** + * Returns the PSK identity (in case of an externally provided PSK) and + * the cipher state representing the PSK selected by the server. Note that + * this destructs the list of offered PSKs and its cipher states and must + * therefore not be called more than once. + * + * @note Technically, PSKs used for resumption also carry an identity. + * Though, typically, this is an opaque value meaningful only to the + * peer and of no authoritative value for the user. We therefore + * report the identity of externally provided PSKs only. + */ + std::pair, std::unique_ptr> take_selected_psk_info( + const PSK& server_psk, const Ciphersuite& cipher); + + /** + * Selects one of the offered PSKs that is compatible with \p cipher. + * @retval PSK extension object that can be added to the Server Hello response + * @retval std::nullptr if no PSK offered by the client is convenient + */ + std::unique_ptr select_offered_psk(std::string_view host, + const Ciphersuite& cipher, + Session_Manager& session_mgr, + Credentials_Manager& credentials_mgr, + Callbacks& callbacks, + const Policy& policy); + + /** + * Remove PSK identities from the list in \p m_psk that are not compatible + * with the passed in \p cipher suite. + * This is useful to react to Hello Retry Requests. See RFC 8446 4.1.4. + */ + void filter(const Ciphersuite& cipher); + + /** + * Pulls the preshared key or the Session to resume from a PSK extension + * in Server Hello. + */ + std::variant take_session_to_resume_or_psk(); + + bool empty() const override; + + PSK(TLS_Data_Reader& reader, uint16_t extension_size, Handshake_Type message_type); + + /** + * Creates a PSK extension with a TLS 1.3 session object containing a + * master_secret. Note that it will extract that secret from the session, + * and won't create a copy of it. + * + * @param session_to_resume the session to be resumed; note that the + * master secret will be taken away from the + * session object. + * @param psks a list of non-resumption PSKs that should be + * offered to the server + * @param callbacks the application's callbacks + */ + PSK(std::optional& session_to_resume, std::vector psks, Callbacks& callbacks); + + ~PSK() override; + + void calculate_binders(const Transcript_Hash_State& truncated_transcript_hash); + bool validate_binder(const PSK& server_psk, const std::vector& binder) const; + + // TODO: Implement pure PSK negotiation that is not used for session + // resumption. + + private: + /** + * Creates a PSK extension that specifies the server's selection of an + * offered client PSK. The @p session_to_resume is kept internally + * and used later for the initialization of the Cipher_State object. + * + * Note: This constructor is called internally in PSK::select_offered_psk(). + */ + PSK(Session session_to_resume, uint16_t psk_index); + + /** + * Creates a PSK extension that specifies the server's selection of an + * externally provided PSK offered by the client. The @p psk is kept + * internally and used later for the initialization of the Cipher_State object. + * + * Note: This constructor is called internally in PSK::select_offered_psk(). + */ + PSK(ExternalPSK psk, uint16_t psk_index); + + private: + class PSK_Internal; + std::unique_ptr m_impl; +}; + +/** +* Key_Share from RFC 8446 4.2.8 +*/ +class BOTAN_UNSTABLE_API Key_Share final : public Extension /* NOLINT(*-special-member-functions) */ { + public: + static Extension_Code static_type() { return Extension_Code::KeyShare; } + + Extension_Code type() const override { return static_type(); } + + std::vector serialize(Connection_Side whoami) const override; + + bool empty() const override; + + /** + * Creates a Key_Share extension meant for the Server Hello that + * performs a key encapsulation with the selected public key from + * the client. + * + * @note This will retain the shared secret in the Key_Share extension + * until it is retrieved via take_shared_secret(). + */ + static std::unique_ptr create_as_encapsulation(Group_Params selected_group, + const Key_Share& client_keyshare, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng); + + /** + * Decapsulate the shared secret with the peer's key share. This method + * can be called on a ClientHello's Key_Share with a ServerHello's + * Key_Share. + * + * @note After the decapsulation the client's private key is destroyed. + * Multiple calls will result in an exception. + */ + secure_vector decapsulate(const Key_Share& server_keyshare, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng); + + /** + * Update a ClientHello's Key_Share to comply with a HelloRetryRequest. + * + * This will create new Key_Share_Entries and should only be called on a ClientHello Key_Share with a HelloRetryRequest Key_Share. + */ + void retry_offer(const Key_Share& retry_request_keyshare, + const std::vector& supported_groups, + Callbacks& cb, + RandomNumberGenerator& rng); + + /** + * @return key exchange groups the peer offered key share entries for + */ + std::vector offered_groups() const; + + /** + * @return key exchange group that was selected by a Hello Retry Request + */ + Named_Group selected_group() const; + + /** + * @returns the shared secret that was obtained by constructing this + * Key_Share object with the peer's. + * + * @note the shared secret value is std:move'd out. Multiple calls will + * result in an exception. + */ + secure_vector take_shared_secret(); + + Key_Share(TLS_Data_Reader& reader, uint16_t extension_size, Handshake_Type message_type); + + // constructor used for ClientHello msg + Key_Share(const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng); + + // constructor used for HelloRetryRequest msg + explicit Key_Share(Named_Group selected_group); + + // destructor implemented in .cpp to hide Key_Share_Impl + ~Key_Share() override; + + private: + // constructor used for ServerHello + // (called via create_as_encapsulation()) + Key_Share(Group_Params selected_group, + const Key_Share& client_keyshare, + const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng); + + private: + class Key_Share_Impl; + std::unique_ptr m_impl; +}; + +/** + * Indicates usage or support of early data as described in RFC 8446 4.2.10. + */ +class BOTAN_UNSTABLE_API EarlyDataIndication final : public Extension { + public: + static Extension_Code static_type() { return Extension_Code::EarlyData; } + + Extension_Code type() const override { return static_type(); } + + std::vector serialize(Connection_Side whoami) const override; + + bool empty() const override; + + std::optional max_early_data_size() const { return m_max_early_data_size; } + + EarlyDataIndication(TLS_Data_Reader& reader, uint16_t extension_size, Handshake_Type message_type); + + /** + * The max_early_data_size is exclusively provided by servers when using + * this extension in the NewSessionTicket message! Otherwise it stays + * std::nullopt and results in an empty extension. (RFC 8446 4.2.10). + */ + explicit EarlyDataIndication(std::optional max_early_data_size = std::nullopt) : + m_max_early_data_size(max_early_data_size) {} + + private: + std::optional m_max_early_data_size; +}; + +} // namespace TLS + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_extensions_key_share.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_extensions_key_share.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_extensions_key_share.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_extensions_key_share.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,47 +8,52 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include +#include -#include +#include #include #include #include -#include +#include #include #include - -#include -#include +#include +#include #include -#if defined(BOTAN_HAS_X25519) - #include -#endif - -#if defined(BOTAN_HAS_X448) - #include -#endif - -#include -#include -#include - namespace Botan::TLS { namespace { +// RFC 8446 4.2.8.2: TLS 1.3 removes ec_point_formats negotiation and +// requires that ECDH key shares are uncompressed. +// +// This logic happens to also work for the existing PQ shares since they +// place the ECDH part of the key share first +void check_ecdh_uncompressed_format(Group_Params group, std::span bytes) { + const auto hybrid_ecc = group.pqc_hybrid_ecc(); + const bool has_ecdh = + group.is_ecdh_named_curve() || (hybrid_ecc.has_value() && Group_Params(hybrid_ecc.value()).is_ecdh_named_curve()); + if(!has_ecdh) { + return; + } + if(bytes.empty() || bytes[0] != 0x04) { + throw TLS_Exception(Alert::IllegalParameter, "TLS 1.3 ECDH key share must use uncompressed point format"); + } +} + class Key_Share_Entry { public: - Key_Share_Entry(TLS_Data_Reader& reader) { + explicit Key_Share_Entry(TLS_Data_Reader& reader) { // TODO check that the group actually exists before casting... m_group = static_cast(reader.get_uint16_t()); - m_key_exchange = reader.get_tls_length_value(2); + // RFC 8446 4.2.8: opaque key_exchange<1..2^16-1> + m_key_exchange = reader.get_range(2, 1, 65535); } // Create an empty Key_Share_Entry with the selected group // but don't pre-generate a keypair, yet. - Key_Share_Entry(const TLS::Group_Params group) : m_group(group) {} + explicit Key_Share_Entry(const TLS::Group_Params group) : m_group(group) {} Key_Share_Entry(const TLS::Group_Params group, Callbacks& cb, RandomNumberGenerator& rng) : m_group(group), m_private_key(cb.tls_kem_generate_key(group, rng)) { @@ -59,8 +64,8 @@ if(group.is_kem()) { m_key_exchange = m_private_key->public_key_bits(); } else if(group.is_ecdh_named_curve()) { - auto pkey = dynamic_cast(m_private_key.get()); - if(!pkey) { + auto* pkey = dynamic_cast(m_private_key.get()); + if(pkey == nullptr) { throw TLS_Exception(Alert::InternalError, "Application did not provide a ECDH_PublicKey"); } @@ -74,8 +79,8 @@ // ClientHello::prefers_compressed_ec_points() into account here. m_key_exchange = pkey->public_value(EC_Point_Format::Uncompressed); } else { - auto pkey = dynamic_cast(m_private_key.get()); - if(!pkey) { + auto* pkey = dynamic_cast(m_private_key.get()); + if(pkey == nullptr) { throw TLS_Exception(Alert::InternalError, "Application did not provide a key-agreement key"); } @@ -103,6 +108,7 @@ const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng) { + check_ecdh_uncompressed_format(m_group, client_share.m_key_exchange); auto [encapsulated_shared_key, shared_key] = KEM_Encapsulation::destructure(cb.tls_kem_encapsulate(m_group, client_share.m_key_exchange, rng, policy)); m_key_exchange = std::move(encapsulated_shared_key); @@ -122,6 +128,7 @@ auto scope = scoped_cleanup([&] { m_private_key.reset(); }); BOTAN_ASSERT_NOMSG(m_group == received.m_group); BOTAN_STATE_CHECK(m_private_key != nullptr); + check_ecdh_uncompressed_format(m_group, received.m_key_exchange); return cb.tls_kem_decapsulate(m_group, *m_private_key, received.m_key_exchange, rng, policy); } @@ -135,7 +142,7 @@ class Key_Share_ServerHello { public: - Key_Share_ServerHello(TLS_Data_Reader& reader, uint16_t) : m_server_share(reader) {} + Key_Share_ServerHello(TLS_Data_Reader& reader, uint16_t /*len*/) : m_server_share(reader) {} Key_Share_ServerHello(Named_Group group, const Key_Share_ClientHello& client_keyshare, @@ -176,25 +183,22 @@ class Key_Share_ClientHello { public: Key_Share_ClientHello(TLS_Data_Reader& reader, uint16_t /* extension_size */) { - // This construction is a crutch to make working with the incoming - // TLS_Data_Reader bearable. Currently, this reader spans the entire - // Client_Hello message. Hence, if offset or length fields are skewed - // or maliciously fabricated, it is possible to read further than the - // bounds of the current extension. - // Note that this aplies to many locations in the code base. - // - // TODO: Overhaul the TLS_Data_Reader to allow for cheap "sub-readers" - // that enforce read bounds of sub-structures while parsing. + // The reader is per-extension (Extensions::deserialize binds it to + // exactly extension_size bytes). Enforce that the inner + // client_shares length matches what the outer extension has left, + // then let the entry loop consume everything; extn_reader's + // assert_done() at the deserialize call site catches any leftover. const auto client_key_share_length = reader.get_uint16_t(); - const auto read_bytes_so_far_begin = reader.read_so_far(); - auto remaining = [&] { - const auto read_so_far = reader.read_so_far() - read_bytes_so_far_begin; - BOTAN_STATE_CHECK(read_so_far <= client_key_share_length); - return client_key_share_length - read_so_far; - }; + if(reader.remaining_bytes() != client_key_share_length) { + throw TLS_Exception(Alert::DecodeError, "Inconsistent length in client KeyShare extension"); + } - while(reader.has_remaining() && remaining() > 0) { - if(remaining() < 4) { + std::unordered_set seen_groups; + while(reader.has_remaining()) { + // Each KeyShareEntry is at least 4 bytes (group + 2-byte length). + // Cleaner failure than the reader underflow we'd otherwise hit + // when the inner buffer ends mid-entry. + if(reader.remaining_bytes() < 4) { throw TLS_Exception(Alert::DecodeError, "Not enough data to read another KeyShareEntry"); } @@ -205,18 +209,12 @@ // group. [...] // Servers MAY check for violations of these rules and abort the // handshake with an "illegal_parameter" alert if one is violated. - if(std::find_if(m_client_shares.begin(), m_client_shares.end(), [&](const auto& entry) { - return entry.group() == new_entry.group(); - }) != m_client_shares.end()) { + if(!seen_groups.insert(new_entry.group().wire_code()).second) { throw TLS_Exception(Alert::IllegalParameter, "Received multiple key share entries for the same group"); } m_client_shares.emplace_back(std::move(new_entry)); } - - if((reader.read_so_far() - read_bytes_so_far_begin) != client_key_share_length) { - throw Decoding_Error("Read bytes are not equal client KeyShare length"); - } } Key_Share_ClientHello(const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng) { @@ -362,7 +360,7 @@ m_selected_group = static_cast(reader.get_uint16_t()); } - Key_Share_HelloRetryRequest(Named_Group selected_group) : m_selected_group(selected_group) {} + explicit Key_Share_HelloRetryRequest(Named_Group selected_group) : m_selected_group(selected_group) {} ~Key_Share_HelloRetryRequest() = default; @@ -395,10 +393,9 @@ public: using Key_Share_Type = std::variant; - Key_Share_Impl(Key_Share_Type ks) : key_share(std::move(ks)) {} + explicit Key_Share_Impl(Key_Share_Type ks) : key_share(std::move(ks)) {} - // NOLINTNEXTLINE(*-non-private-member-variables-in-classes) - Key_Share_Type key_share; + Key_Share_Type key_share; // NOLINT(*-non-private-member-variable*) }; Key_Share::Key_Share(TLS_Data_Reader& reader, uint16_t extension_size, Handshake_Type message_type) { diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_extensions_psk.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_extensions_psk.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_extensions_psk.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_extensions_psk.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,22 +7,22 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include +#include #include +#include #include #include +#include #include #include +#include #include #include #include - #include #include -#if defined(BOTAN_HAS_TLS_13) - namespace Botan::TLS { namespace { @@ -46,11 +46,11 @@ Cipher_State::PSK_Type::Resumption) {} // NOLINTNEXTLINE(*-rvalue-reference-param-not-moved) - Client_PSK(ExternalPSK&& psk) : + explicit Client_PSK(ExternalPSK&& psk) : Client_PSK(PskIdentity(PresharedKeyID(psk.identity())), psk.prf_algo(), psk.extract_master_secret(), - Cipher_State::PSK_Type::External) {} + psk.is_imported() ? Cipher_State::PSK_Type::Imported : Cipher_State::PSK_Type::External) {} Client_PSK(PskIdentity id, std::vector bndr) : m_identity(std::move(id)), m_binder(std::move(bndr)), m_is_resumption(false) {} @@ -106,7 +106,7 @@ class Server_PSK { public: - Server_PSK(uint16_t id) : m_selected_identity(id), m_session_to_resume_or_psk(std::monostate()) {} + explicit Server_PSK(uint16_t id) : m_selected_identity(id), m_session_to_resume_or_psk(std::monostate()) {} Server_PSK(uint16_t id, Session session) : m_selected_identity(id), m_session_to_resume_or_psk(std::move(session)) {} @@ -131,11 +131,11 @@ class PSK::PSK_Internal { public: - PSK_Internal(Server_PSK srv_psk) : psk(std::move(srv_psk)) {} + explicit PSK_Internal(Server_PSK srv_psk) : psk(std::move(srv_psk)) {} - PSK_Internal(std::vector clt_psks) : psk(std::move(clt_psks)) {} + explicit PSK_Internal(std::vector clt_psks) : psk(std::move(clt_psks)) {} - // NOLINTNEXTLINE(*-non-private-member-variables-in-classes) + // NOLINTNEXTLINE(*-non-private-member-variable*) std::variant, Server_PSK> psk; }; @@ -153,6 +153,17 @@ std::vector psk_identities; while(reader.has_remaining() && (reader.read_so_far() - identities_offset) < identities_length) { + /* Per RFC 8446 PskIdentity is + + struct { + opaque identity<1..2^16-1>; + uint32 obfuscated_ticket_age; + } PskIdentity; + + so we should reject an empty identity. However BoGo seems to expect + being able to send us such an identity, so for now we accept it. + */ + auto identity = reader.get_tls_length_value(2); const auto obfuscated_ticket_age = reader.get_uint32_t(); psk_identities.emplace_back(std::move(identity), obfuscated_ticket_age); @@ -179,6 +190,11 @@ throw TLS_Exception(Alert::IllegalParameter, "Not enough PSK binders"); } + // RFC 8446 4.2.11 declares PskBinderEntry opaque<32..255>, but we accept any + // 0..255 length here and let validate_binder reject, which yields a bad_record_mac + // alert rather than decode_error. BoringSSL behaves the same way and BoGo has + // tests that specifically expect this. + psks.emplace_back(std::move(psk_identity), reader.get_tls_length_value(1)); } @@ -206,10 +222,10 @@ m_impl = std::make_unique(std::move(cpsk)); } -PSK::PSK(Session session_to_resume, const uint16_t psk_index) : +PSK::PSK(Session session_to_resume, uint16_t psk_index) : m_impl(std::make_unique(Server_PSK(psk_index, std::move(session_to_resume)))) {} -PSK::PSK(ExternalPSK psk, const uint16_t psk_index) : +PSK::PSK(ExternalPSK psk, uint16_t psk_index) : m_impl(std::make_unique(Server_PSK(psk_index, std::move(psk)))) {} PSK::~PSK() = default; @@ -287,16 +303,22 @@ session_mgr.choose_from_offered_tickets(psk_identities, cipher.prf_algo(), callbacks, policy)) { auto& [session, psk_index] = selected_session.value(); - // RFC 8446 4.6.1 - // Any ticket MUST only be resumed with a cipher suite that has the - // same KDF hash algorithm as that used to establish the original - // connection. - if(session.ciphersuite().prf_algo() != cipher.prf_algo()) { - throw TLS_Exception(Alert::InternalError, - "Application chose a ticket that is not compatible with the negotiated ciphersuite"); - } + // Refuse to resume a ticket across SNI: a session minted for one + // virtual host must not be presentable against another. Treat as a + // cache miss and fall through to the external PSK path rather than + // failing the connection. + if(session.server_info().hostname() == host) { + // RFC 8446 4.6.1 + // Any ticket MUST only be resumed with a cipher suite that has the + // same KDF hash algorithm as that used to establish the original + // connection. + if(session.ciphersuite().prf_algo() != cipher.prf_algo()) { + throw TLS_Exception(Alert::InternalError, + "Application chose a ticket that is not compatible with the negotiated ciphersuite"); + } - return std::unique_ptr(new PSK(std::move(session), psk_index)); + return std::unique_ptr(new PSK(std::move(session), psk_index)); + } } // @@ -428,9 +450,8 @@ const auto& psks = std::get>(m_impl->psk); BOTAN_STATE_CHECK(index < psks.size()); - return psks[index].binder() == binder; + const auto& expected_binder = psks[index].binder(); + return CT::is_equal(binder, expected_binder).as_bool(); } } // namespace Botan::TLS - -#endif // HAS_TLS_13 diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_handshake_layer_13.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_handshake_layer_13.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_handshake_layer_13.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_handshake_layer_13.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,9 @@ #include #include +#include +#include +#include #include #include #include @@ -17,6 +20,13 @@ namespace Botan::TLS { void Handshake_Layer::copy_data(std::span data_from_peer) { + // Compact consumed data before appending new data + BOTAN_ASSERT_NOMSG(m_read_offset <= m_read_buffer.size()); + if(m_read_offset > 0) { + m_read_buffer.erase(m_read_buffer.begin(), m_read_buffer.begin() + m_read_offset); + m_read_offset = 0; + } + m_read_buffer.insert(m_read_buffer.end(), data_from_peer.begin(), data_from_peer.end()); } @@ -54,6 +64,13 @@ } } +void verify_handshake_message_size(size_t msg_len, size_t max_size) { + if(max_size > 0 && msg_len > max_size) { + throw TLS_Exception(Alert::HandshakeFailure, + Botan::fmt("Handshake message is {} bytes, policy maximum is {}", msg_len, max_size)); + } +} + template std::optional parse_message(TLS::TLS_Data_Reader& reader, const Policy& policy, @@ -64,10 +81,14 @@ return std::nullopt; } - Handshake_Type type = handshake_type_from_byte(reader.get_byte()); + const Handshake_Type type = handshake_type_from_byte(reader.get_byte()); // make sure we have received the full message const size_t msg_len = reader.get_uint24_t(); + + // TODO(Botan4) this is split out due to a GCC 11 ICE, can be inlined + verify_handshake_message_size(msg_len, policy.maximum_handshake_message_size()); + if(reader.remaining_bytes() < msg_len) { return std::nullopt; } @@ -118,24 +139,39 @@ std::optional Handshake_Layer::next_message(const Policy& policy, Transcript_Hash_State& transcript_hash) { - TLS::TLS_Data_Reader reader("handshake message", m_read_buffer); + BOTAN_ASSERT_NOMSG(m_read_offset <= m_read_buffer.size()); + auto pending = std::span{m_read_buffer}.subspan(m_read_offset); + TLS::TLS_Data_Reader reader("handshake message", pending); auto msg = parse_message(reader, policy, m_peer, m_certificate_type); if(msg.has_value()) { - BOTAN_ASSERT_NOMSG(m_read_buffer.size() >= reader.read_so_far()); - transcript_hash.update(std::span{m_read_buffer.data(), reader.read_so_far()}); - m_read_buffer.erase(m_read_buffer.cbegin(), m_read_buffer.cbegin() + reader.read_so_far()); + transcript_hash.update(pending.first(reader.read_so_far())); + m_read_offset += reader.read_so_far(); + BOTAN_ASSERT_NOMSG(m_read_offset <= m_read_buffer.size()); + + if(m_read_offset == m_read_buffer.size()) { + m_read_buffer.clear(); + m_read_offset = 0; + } } return msg; } std::optional Handshake_Layer::next_post_handshake_message(const Policy& policy) { - TLS::TLS_Data_Reader reader("post handshake message", m_read_buffer); + BOTAN_ASSERT_NOMSG(m_read_offset <= m_read_buffer.size()); + auto pending = std::span{m_read_buffer}.subspan(m_read_offset); + TLS::TLS_Data_Reader reader("post handshake message", pending); auto msg = parse_message(reader, policy, m_peer, m_certificate_type); if(msg.has_value()) { - m_read_buffer.erase(m_read_buffer.cbegin(), m_read_buffer.cbegin() + reader.read_so_far()); + m_read_offset += reader.read_so_far(); + BOTAN_ASSERT_NOMSG(m_read_offset <= m_read_buffer.size()); + + if(m_read_offset == m_read_buffer.size()) { + m_read_buffer.clear(); + m_read_offset = 0; + } } return msg; diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_handshake_layer_13.h botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_handshake_layer_13.h --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_handshake_layer_13.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_handshake_layer_13.h 2026-05-07 01:38:28.000000000 +0000 @@ -13,7 +13,7 @@ #include #include -#include +#include namespace Botan::TLS { @@ -27,7 +27,7 @@ */ class BOTAN_TEST_API Handshake_Layer { public: - Handshake_Layer(Connection_Side whoami) : + explicit Handshake_Layer(Connection_Side whoami) : m_peer(whoami == Connection_Side::Server ? Connection_Side::Client : Connection_Side::Server) // RFC 8446 4.4.2 // If the corresponding certificate type extension @@ -68,7 +68,7 @@ std::optional next_post_handshake_message(const Policy& policy); /** - * Marshalls one handshake message for sending in an (encrypted) record and updates the + * Marshals one handshake message for sending in an (encrypted) record and updates the * provided transcript hash state accordingly. * * @param message the handshake message to be marshalled @@ -80,7 +80,7 @@ Transcript_Hash_State& transcript_hash); /** - * Marshalls one post-handshake message for sending in an (encrypted) record. + * Marshals one post-handshake message for sending in an (encrypted) record. * * @param message the post handshake message to be marshalled * @@ -92,7 +92,7 @@ * Check if the Handshake_Layer has stored a partial message in its internal buffer. * This can happen if a handshake message spans multiple records. */ - bool has_pending_data() const { return !m_read_buffer.empty(); } + bool has_pending_data() const { return m_read_offset < m_read_buffer.size(); } /** * Set the certificate_type used for parsing Certificate messages. This @@ -113,6 +113,7 @@ private: std::vector m_read_buffer; + size_t m_read_offset = 0; Connection_Side m_peer; Certificate_Type m_certificate_type; }; diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_handshake_state_13.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_handshake_state_13.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_handshake_state_13.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_handshake_state_13.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,9 +8,11 @@ #include +#include + namespace Botan::TLS::Internal { -Client_Hello_13& Handshake_State_13_Base::store(Client_Hello_13 client_hello, const bool) { +Client_Hello_13& Handshake_State_13_Base::store(Client_Hello_13 client_hello, const bool /*from_peer*/) { if(m_client_hello) { // Make sure that the updated Client Hello is compatible to the initial one. BOTAN_STATE_CHECK(has_hello_retry_request()); @@ -21,32 +23,34 @@ return m_client_hello.value(); } -Client_Hello_12& Handshake_State_13_Base::store(Client_Hello_12 client_hello, const bool) { +Client_Hello_12_Shim& Handshake_State_13_Base::store(Client_Hello_12_Shim client_hello, const bool /*from_peer*/) { m_client_hello_12 = std::move(client_hello); return m_client_hello_12.value(); } -Server_Hello_13& Handshake_State_13_Base::store(Server_Hello_13 server_hello, const bool) { +Server_Hello_13& Handshake_State_13_Base::store(Server_Hello_13 server_hello, const bool /*from_peer*/) { m_server_hello = std::move(server_hello); return m_server_hello.value(); } -Server_Hello_12& Handshake_State_13_Base::store(Server_Hello_12 server_hello, const bool) { +Server_Hello_12_Shim& Handshake_State_13_Base::store(Server_Hello_12_Shim server_hello, const bool /*from_peer*/) { m_server_hello_12 = std::move(server_hello); return m_server_hello_12.value(); } -Hello_Retry_Request& Handshake_State_13_Base::store(Hello_Retry_Request hello_retry_request, const bool) { +Hello_Retry_Request& Handshake_State_13_Base::store(Hello_Retry_Request hello_retry_request, const bool /*from_peer*/) { m_hello_retry_request = std::move(hello_retry_request); return m_hello_retry_request.value(); } -Encrypted_Extensions& Handshake_State_13_Base::store(Encrypted_Extensions encrypted_extensions, const bool) { +Encrypted_Extensions& Handshake_State_13_Base::store(Encrypted_Extensions encrypted_extensions, + const bool /*from_peer*/) { m_encrypted_extensions = std::move(encrypted_extensions); return m_encrypted_extensions.value(); } -Certificate_Request_13& Handshake_State_13_Base::store(Certificate_Request_13 certificate_request, const bool) { +Certificate_Request_13& Handshake_State_13_Base::store(Certificate_Request_13 certificate_request, + const bool /*from_peer*/) { m_certificate_request = std::move(certificate_request); return m_certificate_request.value(); } diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_handshake_state_13.h botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_handshake_state_13.h --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_handshake_state_13.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_handshake_state_13.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,15 +9,12 @@ #ifndef BOTAN_TLS_HANDSHAKE_STATE_13_H_ #define BOTAN_TLS_HANDSHAKE_STATE_13_H_ -#include -#include -#include -#include - #include #include -#include +#include #include +#include +#include namespace Botan::TLS { @@ -40,7 +37,15 @@ bool has_client_finished() const { return m_client_finished.has_value(); } - bool handshake_finished() const { return has_server_finished() && has_client_finished(); } + bool handshake_finished() const { + return has_server_finished() && has_client_finished() && m_peer_finished_verified; + } + + /** + * Once the implementation has successfully verified the peer's Finished + * message, the handshake is considered complete and successful. + */ + void confirm_peer_finished_verified() { m_peer_finished_verified = true; } // Client_Hello_13 cannot be const because it might need modification due to a Hello_Retry_Request Client_Hello_13& client_hello() { return get(m_client_hello); } @@ -68,12 +73,12 @@ const Finished_13& client_finished() const { return get(m_client_finished); } protected: - Handshake_State_13_Base(Connection_Side whoami) : m_side(whoami) {} + explicit Handshake_State_13_Base(Connection_Side whoami) : m_side(whoami) {} Client_Hello_13& store(Client_Hello_13 client_hello, bool from_peer); - Client_Hello_12& store(Client_Hello_12 client_hello, bool from_peer); + Client_Hello_12_Shim& store(Client_Hello_12_Shim client_hello, bool from_peer); Server_Hello_13& store(Server_Hello_13 server_hello, bool from_peer); - Server_Hello_12& store(Server_Hello_12 server_hello, bool from_peer); + Server_Hello_12_Shim& store(Server_Hello_12_Shim server_hello, bool from_peer); Hello_Retry_Request& store(Hello_Retry_Request hello_retry_request, bool from_peer); Encrypted_Extensions& store(Encrypted_Extensions encrypted_extensions, bool from_peer); Certificate_Request_13& store(Certificate_Request_13 certificate_request, bool from_peer); @@ -99,11 +104,12 @@ } Connection_Side m_side; + bool m_peer_finished_verified = false; std::optional m_client_hello; - std::optional m_client_hello_12; + std::optional m_client_hello_12; std::optional m_server_hello; - std::optional m_server_hello_12; + std::optional m_server_hello_12; std::optional m_hello_retry_request; std::optional m_encrypted_extensions; std::optional m_certificate_request; @@ -147,13 +153,15 @@ requires(is_generalizable_to(message)) { return std::visit( - [&](auto msg) -> as_wrapped_references_t> { return sending(std::move(msg)); }, + [&](auto msg) -> detail::as_wrapped_references_t> { + return sending(std::move(msg)); + }, std::move(message)); } decltype(auto) received(Handshake_Message_13 message) { return std::visit( - [&](auto msg) -> as_wrapped_references_t { + [&](auto msg) -> detail::as_wrapped_references_t { if constexpr(std::is_constructible_v) { return std::reference_wrapper(store(std::move(msg), true)); } else { diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_messages_13.h botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_messages_13.h --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_messages_13.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_messages_13.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,475 @@ +/* +* TLS Messages +* (C) 2021-2022 Jack Lloyd +* 2021 Elektrobit Automotive GmbH +* 2022 René Meusel, Hannes Rantzsch - neXenio GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TLS_MESSAGES_13_H_ +#define BOTAN_TLS_MESSAGES_13_H_ + +#include +#include +#include +#include + +namespace Botan { + +enum class Usage_Type : uint8_t; +class X509_Certificate; + +} // namespace Botan + +namespace Botan::TLS { + +class Transcript_Hash_State; + +class BOTAN_UNSTABLE_API Client_Hello_13 final : public Client_Hello { + public: + /** + * Creates a client hello which might optionally use the passed-in + * @p session for resumption. In that case, this will "extract" the + * master secret from the passed-in @p session. + */ + Client_Hello_13(const Policy& policy, + Callbacks& cb, + RandomNumberGenerator& rng, + std::string_view hostname, + const std::vector& next_protocols, + std::optional& session, + std::vector psks); + + static std::variant parse(const std::vector& buf); + + void retry(const Hello_Retry_Request& hrr, + const Transcript_Hash_State& transcript_hash_state, + Callbacks& cb, + RandomNumberGenerator& rng); + + /** + * Select the highest protocol version from the list of versions + * supported by the client. If no such version can be determined this + * returns std::nullopt. + */ + std::optional highest_supported_version(const Policy& policy) const; + + /** + * This validates that a Client Hello received after sending a Hello + * Retry Request was updated in accordance with RFC 8446 4.1.2. If issues + * are found, this method throws accordingly. + */ + void validate_updates(const Client_Hello_13& new_ch); + + private: + explicit Client_Hello_13(std::unique_ptr data); + + /** + * If the Client Hello contains a PSK extensions with identities this will + * generate the PSK binders as described in RFC 8446 4.2.11.2. + * Note that the passed in \p transcript_hash_state might be virgin for + * the initial Client Hello and should be primed with ClientHello1 and + * HelloRetryRequest for an updated Client Hello. + */ + void calculate_psk_binders(Transcript_Hash_State transcript_hash_state); +}; + +class Hello_Retry_Request; + +class BOTAN_UNSTABLE_API Server_Hello_13 : public Server_Hello { + protected: + static const struct Server_Hello_Tag { + } as_server_hello; + + static const struct Hello_Retry_Request_Tag { + } as_hello_retry_request; + + static const struct Hello_Retry_Request_Creation_Tag { + } as_new_hello_retry_request; + + // These constructors are meant for instantiating Server Hellos + // after parsing a peer's message. They perform basic validation + // and are therefore not suitable for constructing a message to + // be sent to a client. + explicit Server_Hello_13(std::unique_ptr data, Server_Hello_Tag tag = as_server_hello); + explicit Server_Hello_13(std::unique_ptr data, Hello_Retry_Request_Tag tag); + void basic_validation() const; + + // Instantiate a Server Hello as response to a client's Client Hello + // (called from Server_Hello_13::create()) + Server_Hello_13(const Client_Hello_13& ch, + std::optional key_exchange_group, + Session_Manager& session_mgr, + Credentials_Manager& credentials_mgr, + RandomNumberGenerator& rng, + Callbacks& cb, + const Policy& policy); + + explicit Server_Hello_13(std::unique_ptr data, Hello_Retry_Request_Creation_Tag tag); + + public: + static std::variant create(const Client_Hello_13& ch, + bool hello_retry_request_allowed, + Session_Manager& session_mgr, + Credentials_Manager& credentials_mgr, + RandomNumberGenerator& rng, + const Policy& policy, + Callbacks& cb); + + static std::variant parse( + const std::vector& buf); + + /** + * Return desired downgrade version indicated by hello random, if any. + */ + std::optional random_signals_downgrade() const; + + /** + * @returns the selected version as indicated by the supported_versions extension + */ + Protocol_Version selected_version() const final; +}; + +class BOTAN_UNSTABLE_API Hello_Retry_Request final : public Server_Hello_13 { + protected: + friend class Server_Hello_13; // to allow construction by Server_Hello_13::parse() and ::create() + explicit Hello_Retry_Request(std::unique_ptr data); + Hello_Retry_Request(const Client_Hello_13& ch, Named_Group selected_group, const Policy& policy, Callbacks& cb); + + public: + Handshake_Type type() const override { return Handshake_Type::HelloRetryRequest; } + + Handshake_Type wire_type() const override { return Handshake_Type::ServerHello; } +}; + +class BOTAN_UNSTABLE_API Encrypted_Extensions final : public Handshake_Message { + public: + explicit Encrypted_Extensions(const std::vector& buf); + Encrypted_Extensions(const Client_Hello_13& client_hello, + const Policy& policy, + Callbacks& cb, + bool is_resumption, + bool requesting_client_auth); + + Handshake_Type type() const override { return Handshake_Type::EncryptedExtensions; } + + const Extensions& extensions() const { return m_extensions; } + + std::vector serialize() const override; + + private: + Extensions m_extensions; +}; + +class Certificate_Request_13; + +/** +* Certificate Message of TLS 1.3 +*/ +class BOTAN_UNSTABLE_API Certificate_13 final : public Handshake_Message { + public: + class Certificate_Entry { + public: + Certificate_Entry(TLS_Data_Reader& reader, Connection_Side side, Certificate_Type cert_type); + explicit Certificate_Entry(const X509_Certificate& cert); + explicit Certificate_Entry(std::shared_ptr raw_public_key); + + bool has_certificate() const { return m_certificate != nullptr; } + + const X509_Certificate& certificate() const; + std::shared_ptr public_key() const; + + std::vector serialize() const; + + Extensions& extensions() { return m_extensions; } + + const Extensions& extensions() const { return m_extensions; } + + Certificate_Entry(const Certificate_Entry& other) = delete; + Certificate_Entry& operator=(const Certificate_Entry& other) = delete; + + Certificate_Entry(Certificate_Entry&& other) noexcept; + Certificate_Entry& operator=(Certificate_Entry&& other) noexcept; + + ~Certificate_Entry(); + + private: + std::unique_ptr m_certificate; // possibly null if raw public key in use + std::shared_ptr m_raw_public_key; + Extensions m_extensions; + }; + + public: + Handshake_Type type() const override { return Handshake_Type::Certificate; } + + std::vector cert_chain() const; + + bool has_certificate_chain() const; + bool is_raw_public_key() const; + + size_t count() const { return m_entries.size(); } + + bool empty() const { return m_entries.empty(); } + + std::shared_ptr public_key() const; + const X509_Certificate& leaf() const; + + const std::vector& request_context() const { return m_request_context; } + + /** + * Create a Client Certificate message + * ... in response to a Certificate Request message. + */ + Certificate_13(const Certificate_Request_13& cert_request, + std::string_view hostname, + Credentials_Manager& credentials_manager, + Callbacks& callbacks, + Certificate_Type cert_type); + + /** + * Create a Server Certificate message + * ... in response to a Client Hello indicating the need to authenticate + * with a server certificate. + */ + Certificate_13(const Client_Hello_13& client_hello, + Credentials_Manager& credentials_manager, + Callbacks& callbacks, + Certificate_Type cert_type); + + /** + * Deserialize a Certificate message + * @param buf the serialized message + * @param policy the TLS policy + * @param side is this a Connection_Side::Server or Connection_Side::Client certificate message + * @param cert_type is the certificate type that was negotiated during the handshake + */ + Certificate_13(const std::vector& buf, + const Policy& policy, + Connection_Side side, + Certificate_Type cert_type); + + /** + * Validate a Certificate message regarding what extensions are expected based on + * previous handshake messages. Also call the tls_examine_extensions() callback + * for each entry. + * + * @param requested_extensions Extensions of Client_Hello or Certificate_Request messages + * @param cb Callback that will be called for each extension. + */ + void validate_extensions(const std::set& requested_extensions, Callbacks& cb) const; + + /** + * Verify the certificate chain + * + * @throws if verification fails. + */ + void verify(Callbacks& callbacks, + const Policy& policy, + Credentials_Manager& creds, + std::string_view hostname, + bool use_ocsp) const; + + std::vector serialize() const override; + + private: + void setup_entries(std::vector cert_chain, + const Certificate_Status_Request* csr, + Callbacks& callbacks); + void setup_entry(std::shared_ptr raw_public_key, Callbacks& callbacks); + + void verify_certificate_chain(Callbacks& callbacks, + const Policy& policy, + Credentials_Manager& creds, + std::string_view hostname, + bool use_ocsp, + Usage_Type usage_type) const; + + private: + std::vector m_request_context; + std::vector m_entries; + Connection_Side m_side; +}; + +class BOTAN_UNSTABLE_API Certificate_Request_13 final : public Handshake_Message { + public: + Handshake_Type type() const override; + + Certificate_Request_13(const std::vector& buf, Connection_Side side); + + //! Creates a Certificate_Request message if it is required by the configuration + //! @return std::nullopt if configuration does not require client authentication + static std::optional maybe_create(const Client_Hello_13& sni_hostname, + Credentials_Manager& cred_mgr, + Callbacks& callbacks, + const Policy& policy); + + std::vector acceptable_CAs() const; + const std::vector& signature_schemes() const; + const std::vector& certificate_signature_schemes() const; + + const Extensions& extensions() const { return m_extensions; } + + std::vector serialize() const override; + + const std::vector& context() const { return m_context; } + + private: + Certificate_Request_13(const std::vector& acceptable_CAs, const Policy& policy, Callbacks& callbacks); + + private: + std::vector m_context; + Extensions m_extensions; +}; + +/** +* Certificate Verify Message +*/ +class BOTAN_UNSTABLE_API Certificate_Verify_13 final : public Certificate_Verify { + public: + /** + * Deserialize a Certificate message + * @param buf the serialized message + * @param side is this a Connection_Side::Server or Connection_Side::Client certificate message + */ + Certificate_Verify_13(const std::vector& buf, Connection_Side side); + + Certificate_Verify_13(const Certificate_13& certificate_message, + const std::vector& peer_allowed_schemes, + std::string_view hostname, + const Transcript_Hash& hash, + Connection_Side whoami, + Credentials_Manager& creds_mgr, + const Policy& policy, + Callbacks& callbacks, + RandomNumberGenerator& rng); + + bool verify(const Public_Key& public_key, Callbacks& callbacks, const Transcript_Hash& transcript_hash) const; + + private: + Connection_Side m_side; +}; + +class BOTAN_UNSTABLE_API Finished_13 final : public Finished { + public: + using Finished::Finished; + Finished_13(Cipher_State* cipher_state, const Transcript_Hash& transcript_hash); + + bool verify(Cipher_State* cipher_state, const Transcript_Hash& transcript_hash) const; +}; + +class BOTAN_UNSTABLE_API New_Session_Ticket_13 final : public Handshake_Message { + public: + Handshake_Type type() const override { return Handshake_Type::NewSessionTicket; } + + New_Session_Ticket_13(Ticket_Nonce nonce, + const Session& session, + const Session_Handle& handle, + Callbacks& callbacks); + + New_Session_Ticket_13(const std::vector& buf, Connection_Side from); + + std::vector serialize() const override; + + const Extensions& extensions() const { return m_extensions; } + + const Opaque_Session_Handle& handle() const { return m_handle; } + + const Ticket_Nonce& nonce() const { return m_ticket_nonce; } + + uint32_t ticket_age_add() const { return m_ticket_age_add; } + + std::chrono::seconds lifetime_hint() const { return m_ticket_lifetime_hint; } + + /** + * @return the number of bytes allowed for early data or std::nullopt + * when early data is not allowed at all + */ + std::optional early_data_byte_limit() const; + + private: + // RFC 8446 4.6.1 + // Clients MUST NOT cache tickets for longer than 7 days, regardless of + // the ticket_lifetime, and MAY delete tickets earlier based on local + // policy. A server MAY treat a ticket as valid for a shorter period + // of time than what is stated in the ticket_lifetime. + // + // ... hence we call it 'lifetime hint'. + std::chrono::seconds m_ticket_lifetime_hint{}; + uint32_t m_ticket_age_add; + Ticket_Nonce m_ticket_nonce; + Opaque_Session_Handle m_handle; + Extensions m_extensions; +}; + +class BOTAN_UNSTABLE_API Key_Update final : public Handshake_Message { + public: + Handshake_Type type() const override { return Handshake_Type::KeyUpdate; } + + explicit Key_Update(bool request_peer_update); + explicit Key_Update(const std::vector& buf); + + std::vector serialize() const override; + + bool expects_reciprocation() const { return m_update_requested; } + + private: + bool m_update_requested; +}; + +namespace detail { +template +struct as_wrapped_references {}; + +template +struct as_wrapped_references> { + using type = std::variant...>; +}; + +template +using as_wrapped_references_t = typename as_wrapped_references::type; +} // namespace detail + +// Handshake message types from RFC 8446 4. +using Handshake_Message_13 = std::variant; +using Handshake_Message_13_Ref = detail::as_wrapped_references_t; + +using Post_Handshake_Message_13 = std::variant; + +// Key_Update is handled generically by the Channel. The messages assigned +// to those variants are the ones that need to be handled by the specific +// client and/or server implementations. +using Server_Post_Handshake_13_Message = std::variant; +using Client_Post_Handshake_13_Message = std::variant; + +using Server_Handshake_13_Message = std::variant; +using Server_Handshake_13_Message_Ref = detail::as_wrapped_references_t; + +using Client_Handshake_13_Message = + std::variant; +using Client_Handshake_13_Message_Ref = detail::as_wrapped_references_t; + +} // namespace Botan::TLS + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_psk_13.h botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_psk_13.h --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_psk_13.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_psk_13.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,120 @@ +/** + * TLS 1.3 Preshared Key identity and importer + * (C) 2023 Jack Lloyd + * 2023 René Meusel - Rohde & Schwarz Cybersecurity + * 2023 Fabian Albert, René Meusel - Rohde & Schwarz Cybersecurity + * 2025,2026 Jack Lloyd + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#ifndef BOTAN_TLS_PSK_13_H_ +#define BOTAN_TLS_PSK_13_H_ + +#include +#include +#include // TODO remove this dep +#include +#include +#include +#include +#include +#include +#include + +namespace Botan::TLS { + +/// @brief holds a PSK identity as used in TLS 1.3 +using PresharedKeyID = Strong; + +/** + * Represents a TLS 1.3 PSK identity as found in the Preshared Key extension + * with an opaque identity and an associated (obfuscated) ticket age. The latter + * is not applicable for externally provided PSKs. + */ +class BOTAN_PUBLIC_API(3, 1) PskIdentity { + public: + /** + * Construct from information provided in the peer's ClientHello + */ + PskIdentity(std::vector identity, const uint32_t obfuscated_age) : + m_identity(std::move(identity)), m_obfuscated_age(obfuscated_age) {} + + /** + * Construct from a session stored by the client + */ + PskIdentity(Opaque_Session_Handle identity, std::chrono::milliseconds age, uint32_t ticket_age_add); + + /** + * Construct from an externally provided PSK in the client + */ + BOTAN_FUTURE_EXPLICIT PskIdentity(PresharedKeyID identity); + + const std::vector& identity() const { return m_identity; } + + std::string identity_as_string() const; + + /** + * If this represents a PSK for session resumption, it returns the + * session's age given the de-obfuscation parameter @p ticket_age_add. For + * externally provided PSKs this method does not provide any meaningful + * information. + */ + std::chrono::milliseconds age(uint32_t ticket_age_add) const; + + uint32_t obfuscated_age() const { return m_obfuscated_age; } + + private: + std::vector m_identity; + uint32_t m_obfuscated_age; +}; + +/** + * Botan 3.0.0 used the class name "Ticket". In Botan 3.1.0 we decided to + * re-name it to the more generic term "PskIdentity" to better reflect its dual + * use case for resumption and externally provided PSKs. + */ +BOTAN_DEPRECATED("Use PskIdentity") typedef PskIdentity Ticket; + +/** + * RFC 9258 PSK Importer. + * + * Holds the base key material and identity for a pre-shared key and + * derives imported PSKs for specific TLS protocol versions and cipher + * suite hash algorithms using the PSK importer mechanism + */ +class BOTAN_PUBLIC_API(3, 12) PSKImporter { + public: + /** + * @param key the base pre-shared key + * @param identity the external PSK identity + * @param context optional importer context + * @param hash the hash algorithm provisioned with this PSK ("SHA-256" or "SHA-384") + * which defaults to SHA-256 due to RFC 9258's "If the EPSK does not have [...] + * an associated hash function, SHA-256 SHOULD be used." + */ + PSKImporter(std::span key, + std::span identity, + std::span context, + std::string_view hash = "SHA-256"); + + /** + * Derive an imported PSK for the given target protocol version and + * cipher suite hash algorithm. + * + * @param version target TLS protocol version (must be TLS 1.3) + * @param target_hash hash algorithm of the target cipher suite ("SHA-256" or "SHA-384") + * @return an ExternalPSK ready for use in a TLS 1.3 handshake + */ + ExternalPSK derive_imported_psk(Protocol_Version version, std::string_view target_hash) const; + + private: + secure_vector m_key; + std::vector m_identity; + std::vector m_context; + std::string m_hash; +}; + +} // namespace Botan::TLS + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_psk_identity_13.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_psk_identity_13.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_psk_identity_13.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_psk_identity_13.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,9 +6,9 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include +#include -#include +#include namespace Botan::TLS { @@ -23,6 +23,10 @@ return static_cast(in + ticket_age_add); } +inline std::vector to_byte_vector(std::string_view s) { + return std::vector(s.cbegin(), s.cend()); +} + } // namespace PskIdentity::PskIdentity(Opaque_Session_Handle identity, @@ -39,11 +43,12 @@ m_obfuscated_age(0) {} std::chrono::milliseconds PskIdentity::age(const uint32_t ticket_age_add) const { - return std::chrono::milliseconds(obfuscate_ticket_age(m_obfuscated_age, ticket_age_add)); + // De-obfuscate: subtract ticket_age_add (inverse of obfuscate_ticket_age) + return std::chrono::milliseconds(static_cast(m_obfuscated_age - ticket_age_add)); } std::string PskIdentity::identity_as_string() const { - return Botan::to_string(m_identity); + return bytes_to_string(m_identity); } } // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_psk_identity_13.h botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_psk_identity_13.h --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_psk_identity_13.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_psk_identity_13.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,68 +9,8 @@ #ifndef BOTAN_TLS_13_TICKET_H_ #define BOTAN_TLS_13_TICKET_H_ -#include -#include -#include +#include -#include -#include -#include - -namespace Botan::TLS { - -/// @brief holds a PSK identity as used in TLS 1.3 -using PresharedKeyID = Strong; - -/** - * Represents a TLS 1.3 PSK identity as found in the Preshared Key extension - * with an opaque identity and an associated (obfuscated) ticket age. The latter - * is not applicable for externally provided PSKs. - */ -class BOTAN_PUBLIC_API(3, 1) PskIdentity { - public: - /** - * Construct from information provided in the peer's ClientHello - */ - PskIdentity(std::vector identity, const uint32_t obfuscated_age) : - m_identity(std::move(identity)), m_obfuscated_age(obfuscated_age) {} - - /** - * Construct from a session stored by the client - */ - PskIdentity(Opaque_Session_Handle identity, std::chrono::milliseconds age, uint32_t ticket_age_add); - - /** - * Construct from an externally provided PSK in the client - */ - PskIdentity(PresharedKeyID identity); - - const std::vector& identity() const { return m_identity; } - - std::string identity_as_string() const; - - /** - * If this represents a PSK for session resumption, it returns the - * session's age given the de-obfuscation parameter @p ticket_age_add. For - * externally provided PSKs this method does not provide any meaningful - * information. - */ - std::chrono::milliseconds age(uint32_t ticket_age_add) const; - - uint32_t obfuscated_age() const { return m_obfuscated_age; } - - private: - std::vector m_identity; - uint32_t m_obfuscated_age; -}; - -/** - * Botan 3.0.0 used the class name "Ticket". In Botan 3.1.0 we decided to - * re-name it to the more generic term "PskIdentity" to better reflect its dual - * use case for resumption and externally provided PSKs. - */ -BOTAN_DEPRECATED("Use PskIdentity") typedef PskIdentity Ticket; - -} // namespace Botan::TLS +BOTAN_DEPRECATED_HEADER("tls_psk_identity_13.h") #endif diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_psk_importer_13.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_psk_importer_13.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_psk_importer_13.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_psk_importer_13.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,121 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace Botan::TLS { + +PSKImporter::PSKImporter(std::span key, + std::span identity, + std::span context, + std::string_view hash) : + m_key(key.begin(), key.end()), + m_identity(identity.begin(), identity.end()), + m_context(context.begin(), context.end()), + m_hash(hash) { + BOTAN_ARG_CHECK(m_hash == "SHA-256" || m_hash == "SHA-384", "PSK importer hash must be SHA-256 or SHA-384"); + // RFC 9258 5.1: + // struct { + // opaque external_identity<1...2^16-1>; + // opaque context<0..2^16-1>; + // uint16 target_protocol; + // uint16 target_kdf; + // } ImportedIdentity; + + BOTAN_ARG_CHECK(!m_identity.empty(), "PSK importer identity must not be empty"); + + // The derived imported PSK identity (above) ends up as a TLS PSK identity + // (opaque<1..2^16-1>), so the whole assembled value must fit in. + BOTAN_ARG_CHECK(m_identity.size() + m_context.size() + 8 <= std::numeric_limits::max(), + "PSK importer identity + context too long for a TLS PSK identity"); +} + +ExternalPSK PSKImporter::derive_imported_psk(Protocol_Version version, std::string_view target_hash) const { + BOTAN_ARG_CHECK(version == Protocol_Version::TLS_V13, "PSK importer is only defined for TLS 1.3"); + BOTAN_ARG_CHECK(target_hash == "SHA-256" || target_hash == "SHA-384", + "PSK importer target hash must be SHA-256 or SHA-384"); + + // TODO(DTLS1.3): This duplicates Cipher_State::hkdf_expand_label + + const uint16_t target_protocol = version.version_code(); + const uint16_t target_kdf = (target_hash == "SHA-256") ? uint16_t(0x0001) : uint16_t(0x0002); + + // Build imported PSK identity (RFC 9258, Section 5.1): + // external_identity (length-prefixed) || context (length-prefixed) || + // target_protocol (2 bytes) || target_kdf (2 bytes) + const auto id_len = static_cast(m_identity.size()); + const auto ctx_len = static_cast(m_context.size()); + + const auto imported_identity = concat>( + store_be(id_len), m_identity, store_be(ctx_len), m_context, store_be(target_protocol), store_be(target_kdf)); + + // RFC 9258 5.1: "The hash function used for HKDF is that which is + // associated with the EPSK. It is not the hash function associated + // with ImportedIdentity.target_kdf." + auto hash_fn = HashFunction::create_or_throw(m_hash); + hash_fn->update(imported_identity); + const auto identity_hash = hash_fn->final_stdvec(); + + // HKDF-Extract(0, epsk) -- using the EPSK's hash per above + const size_t psk_hash_len = hash_fn->output_length(); + auto hkdf_extract = KDF::create_or_throw("HKDF-Extract(" + m_hash + ")"); + + const std::vector salt(psk_hash_len, 0); + const auto epskx = hkdf_extract->derive_key(psk_hash_len, m_key, salt, {}); + + // HKDF-Expand-Label(epskx, "derived psk", Hash(ImportedIdentity), L) + // + // Two distinct hashes are in play here and it is easy to conflate them: + // + // * The HKDF used for Extract and Expand is the one associated with the + // EPSK (m_hash). RFC 9258 5.1: "The hash function used for HKDF is + // that which is associated with the EPSK. It is not the hash function + // associated with ImportedIdentity.target_kdf." + // + // * The output length L, by contrast, is taken from the *target* KDF, + // not the EPSK's hash. RFC 9258 5.1: "L corresponds to the KDF + // output length of ImportedIdentity.target_kdf [...] For hash-based + // KDFs, such as HKDF_SHA256 (0x0001), this is the length of the + // hash function output, e.g., 32 octets for SHA256." + // + // So e.g. a SHA-256 EPSK imported for a SHA-384 target cipher suite runs + // HKDF-SHA-256 (driven by m_hash) and emits 48 bytes (driven by target_hash). + const std::string target_hash_str(target_hash); + auto target_hash_fn = HashFunction::create_or_throw(target_hash_str); + const size_t target_hash_len = target_hash_fn->output_length(); + const auto expand_out_len = static_cast(target_hash_len); + + auto hkdf_expand = KDF::create_or_throw("HKDF-Expand(" + m_hash + ")"); + // "tls13 derived psk" as bytes + const std::array prefixed_label = { + 't', 'l', 's', '1', '3', ' ', 'd', 'e', 'r', 'i', 'v', 'e', 'd', ' ', 'p', 's', 'k'}; + + // TLS 1.3 HkdfLabel: length (2) || label length (1) || label || context length (1) || context + + const auto prefixed_label_len = static_cast(prefixed_label.size()); + const auto identity_hash_len = static_cast(identity_hash.size()); + const auto hkdf_label = concat>(store_be(expand_out_len), + store_be(prefixed_label_len), + prefixed_label, + store_be(identity_hash_len), + identity_hash); + + secure_vector ipskx(target_hash_len); + hkdf_expand->derive_key(ipskx, epskx, hkdf_label, {}); + + const std::string wire_identity(imported_identity.begin(), imported_identity.end()); + return ExternalPSK(wire_identity, target_hash_str, std::move(ipskx), true); +} + +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_record_layer_13.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_record_layer_13.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_record_layer_13.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_record_layer_13.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,8 +11,10 @@ #include #include #include +#include +#include #include -#include +#include namespace Botan::TLS { @@ -51,10 +53,12 @@ class TLSPlaintext_Header final { public: TLSPlaintext_Header(std::vector hdr, const bool check_tls13_version) { + // NOLINTBEGIN(*-prefer-member-initializer) m_type = read_record_type(hdr[0]); m_legacy_version = Protocol_Version(make_uint16(hdr[1], hdr[2])); m_fragment_length = make_uint16(hdr[3], hdr[4]); m_serialized = std::move(hdr); + // NOLINTEND(*-prefer-member-initializer) // If no full version check is requested, we just verify the practically // ossified major version number. @@ -168,6 +172,13 @@ m_receiving_compat_mode(true) {} void Record_Layer::copy_data(std::span data) { + // Compact consumed data before appending new data + BOTAN_ASSERT_NOMSG(m_read_offset <= m_read_buffer.size()); + if(m_read_offset > 0) { + m_read_buffer.erase(m_read_buffer.begin(), m_read_buffer.begin() + m_read_offset); + m_read_offset = 0; + } + m_read_buffer.insert(m_read_buffer.end(), data.begin(), data.end()); } @@ -236,6 +247,7 @@ // even if the plaintext size is zero. This happens only for Application // Data types. BOTAN_ASSERT_NOMSG(to_process != 0 || protect); + // NOLINTNEXTLINE(*-avoid-do-while) do { const size_t pt_size = std::min(to_process, max_plaintext_size); const size_t ct_size = @@ -277,17 +289,19 @@ } Record_Layer::ReadResult Record_Layer::next_record(Cipher_State* cipher_state) { - if(m_read_buffer.size() < TLS_HEADER_SIZE) { - return TLS_HEADER_SIZE - m_read_buffer.size(); + const auto remaining = m_read_buffer.size() - m_read_offset; + + if(remaining < TLS_HEADER_SIZE) { + return TLS_HEADER_SIZE - remaining; } - const auto header_begin = m_read_buffer.cbegin(); + const auto header_begin = m_read_buffer.cbegin() + m_read_offset; const auto header_end = header_begin + TLS_HEADER_SIZE; // The first received record(s) are likely a client or server hello. To be able to // perform protocol downgrades we must be less vigorous with the record's // legacy version. Hence, `check_tls13_version` is `false` for the first record(s). - TLSPlaintext_Header plaintext_header({header_begin, header_end}, !m_receiving_compat_mode); + const TLSPlaintext_Header plaintext_header({header_begin, header_end}, !m_receiving_compat_mode); // After the key exchange phase of the handshake is completed and record protection is engaged, // cipher_state is set. At this point, only protected traffic (and CCS) is allowed. @@ -304,8 +318,8 @@ throw TLS_Exception(Alert::UnexpectedMessage, "unprotected record received where protected traffic was expected"); } - if(m_read_buffer.size() < TLS_HEADER_SIZE + plaintext_header.fragment_length()) { - return TLS_HEADER_SIZE + plaintext_header.fragment_length() - m_read_buffer.size(); + if(remaining < TLS_HEADER_SIZE + plaintext_header.fragment_length()) { + return TLS_HEADER_SIZE + plaintext_header.fragment_length() - remaining; } const auto fragment_begin = header_end; @@ -317,7 +331,14 @@ } Record record(plaintext_header.type(), secure_vector(fragment_begin, fragment_end)); - m_read_buffer.erase(header_begin, fragment_end); + m_read_offset += TLS_HEADER_SIZE + plaintext_header.fragment_length(); + + // If all buffered data has been consumed, release the buffer memory + // to avoid retaining peak allocation on idle connections. + if(m_read_offset == m_read_buffer.size()) { + zap(m_read_buffer); + m_read_offset = 0; + } if(record.type == Record_Type::ApplicationData) { if(cipher_state == nullptr) { @@ -336,11 +357,22 @@ record.seq_no = cipher_state->decrypt_record_fragment(plaintext_header.serialized(), record.fragment); - // Remove record padding (RFC 8446 5.4). - const auto end_of_content = - std::find_if(record.fragment.crbegin(), record.fragment.crend(), [](auto byte) { return byte != 0x00; }); + // Remove record padding (RFC 8446 5.4). The TLSInnerPlaintext layout is + // content || content_type || zero_padding + auto seen_nonzero = CT::Mask::cleared(); + uint8_t content_type_byte = 0; + size_t content_index = 0; + for(size_t i = record.fragment.size(); i-- > 0;) { + const uint8_t b = record.fragment[i]; + const auto byte_is_nonzero = CT::Mask::expand(b); + // Set on the first non-zero byte we encounter scanning right-to-left. + const auto first_nonzero = byte_is_nonzero & ~seen_nonzero; + content_type_byte = first_nonzero.select(b, content_type_byte); + content_index = CT::Mask::expand(first_nonzero.value()).select(i, content_index); + seen_nonzero |= byte_is_nonzero; + } - if(end_of_content == record.fragment.crend()) { + if(!seen_nonzero.as_bool()) { // RFC 8446 5.4 // If a receiving implementation does not // find a non-zero octet in the cleartext, it MUST terminate the @@ -349,7 +381,7 @@ } // hydrate the actual content type from TLSInnerPlaintext - record.type = read_record_type(*end_of_content); + record.type = read_record_type(content_type_byte); if(record.type == Record_Type::ChangeCipherSpec) { // RFC 8446 5 @@ -358,8 +390,20 @@ throw TLS_Exception(Alert::UnexpectedMessage, "protected change cipher spec received"); } - // erase content type and padding - record.fragment.erase((end_of_content + 1).base(), record.fragment.cend()); + // Truncate to drop the content_type byte and padding. resize() on a + // vector of trivially-destructible elements is bookkeeping-only and + // does not allocate or iterate over the dropped suffix. + record.fragment.resize(content_index); + + // RFC 8446 5.4 + // Implementations MUST NOT send Handshake and Alert records that have + // a zero-length TLSInnerPlaintext.content; if such a message is + // received, the receiving implementation MUST terminate the connection + // with an "unexpected_message" alert. + if(record.fragment.empty() && record.type != Record_Type::ApplicationData) { + throw TLS_Exception(Alert::UnexpectedMessage, + "Received a protected record with empty TLSInnerPlaintext content"); + } } return record; diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_record_layer_13.h botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_record_layer_13.h --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_record_layer_13.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_record_layer_13.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,15 +9,13 @@ #ifndef BOTAN_TLS_RECORD_LAYER_13_H_ #define BOTAN_TLS_RECORD_LAYER_13_H_ +#include +#include #include #include #include #include -#include -#include -#include - namespace Botan::TLS { /** @@ -25,9 +23,11 @@ * minus the record protocol specifics and ossified bytes. */ struct Record { - Record_Type type; - secure_vector fragment; - std::optional seq_no; // unprotected records have no sequence number + Record_Type type; // NOLINT(*non-private-member-variable*) + secure_vector fragment; // NOLINT(*non-private-member-variable*) + + // unprotected records have no sequence number + std::optional seq_no; // NOLINT(*non-private-member-variable*) Record(Record_Type record_type, secure_vector frgmnt) : type(record_type), fragment(std::move(frgmnt)), seq_no(std::nullopt) {} @@ -45,7 +45,7 @@ */ class BOTAN_TEST_API Record_Layer { public: - Record_Layer(Connection_Side side); + explicit Record_Layer(Connection_Side side); template using ReadResult = std::variant; @@ -79,7 +79,10 @@ * Clears any data currently stored in the read buffer. This is typically * used for memory cleanup when the peer sent a CloseNotify alert. */ - void clear_read_buffer() { zap(m_read_buffer); } + void clear_read_buffer() { + zap(m_read_buffer); + m_read_offset = 0; + } /** * Set the record size limits as negotiated by the "record_size_limit" @@ -102,6 +105,7 @@ private: std::vector m_read_buffer; + size_t m_read_offset = 0; Connection_Side m_side; // Those are either the limits set by the TLS 1.3 specification (RFC 8446), diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_server_impl_13.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_server_impl_13.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_server_impl_13.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_server_impl_13.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,10 @@ #include #include +#include +#include +#include +#include #include #include #include @@ -21,54 +25,71 @@ const std::shared_ptr& credentials_manager, const std::shared_ptr& policy, const std::shared_ptr& rng) : - Channel_Impl_13(callbacks, session_manager, credentials_manager, rng, policy, true /* is_server */) { + Channel_Impl_13(callbacks, session_manager, credentials_manager, rng, policy, true /* is_server */), + m_handshake(std::make_unique()) { #if defined(BOTAN_HAS_TLS_12) if(policy->allow_tls12()) { expect_downgrade({}, {}); } #endif - m_transitions.set_expected_next(Handshake_Type::ClientHello); + m_handshake->transitions.set_expected_next(Handshake_Type::ClientHello); } std::string Server_Impl_13::application_protocol() const { - if(is_handshake_complete()) { - const auto& eee = m_handshake_state.encrypted_extensions().extensions(); - if(const auto alpn = eee.get()) { - return alpn->single_protocol(); - } + if(m_active_state.has_value()) { + return m_active_state->application_protocol(); } return ""; } std::vector Server_Impl_13::peer_cert_chain() const { - if(m_handshake_state.has_client_certificate_msg() && - m_handshake_state.client_certificate().has_certificate_chain()) { - return m_handshake_state.client_certificate().cert_chain(); + if(m_active_state.has_value()) { + return m_active_state->peer_certs(); } - if(m_resumed_session.has_value()) { - return m_resumed_session->peer_certs(); + if(m_handshake) { + if(m_handshake->state.has_client_certificate_msg() && + m_handshake->state.client_certificate().has_certificate_chain()) { + return m_handshake->state.client_certificate().cert_chain(); + } + + if(m_handshake->resumed_session.has_value()) { + return m_handshake->resumed_session->peer_certs(); + } } return {}; } std::shared_ptr Server_Impl_13::peer_raw_public_key() const { - if(m_handshake_state.has_client_certificate_msg() && m_handshake_state.client_certificate().is_raw_public_key()) { - return m_handshake_state.client_certificate().public_key(); + if(m_active_state.has_value()) { + return m_active_state->peer_raw_public_key(); } - if(m_resumed_session.has_value()) { - return m_resumed_session->peer_raw_public_key(); + if(m_handshake) { + if(m_handshake->state.has_client_certificate_msg() && + m_handshake->state.client_certificate().is_raw_public_key()) { + return m_handshake->state.client_certificate().public_key(); + } + + if(m_handshake->resumed_session.has_value()) { + return m_handshake->resumed_session->peer_raw_public_key(); + } } return nullptr; } std::optional Server_Impl_13::external_psk_identity() const { - return m_psk_identity; + if(m_active_state.has_value()) { + return m_active_state->psk_identity(); + } else if(m_handshake) { + return m_handshake->psk_identity; + } else { + return std::nullopt; + } } bool Server_Impl_13::new_session_ticket_supported() const { @@ -82,13 +103,12 @@ // regardless of this method indicating no support for tickets. // // TODO: Implement other PSK KE modes than PSK_DHE_KE - return is_handshake_complete() && m_handshake_state.client_hello().extensions().has() && - value_exists(m_handshake_state.client_hello().extensions().get()->modes(), - PSK_Key_Exchange_Mode::PSK_DHE_KE); + return is_handshake_complete() && m_active_state.has_value() && m_active_state->peer_supports_psk_dhe_ke(); } size_t Server_Impl_13::send_new_session_tickets(const size_t tickets) { BOTAN_STATE_CHECK(is_handshake_complete()); + BOTAN_STATE_CHECK(m_cipher_state != nullptr); if(tickets == 0) { return 0; @@ -97,17 +117,22 @@ auto flight = aggregate_post_handshake_messages(); size_t tickets_created = 0; + BOTAN_STATE_CHECK(m_active_state.has_value()); + for(size_t i = 0; i < tickets; ++i) { auto nonce = m_cipher_state->next_ticket_nonce(); + const uint32_t ticket_age_add = load_be(rng().random_array<4>()); const Session session(m_cipher_state->psk(nonce), std::nullopt, // early data not yet implemented + ticket_age_add, policy().session_ticket_lifetime(), + m_active_state->version(), + m_active_state->ciphersuite_code(), + Connection_Side::Server, peer_cert_chain(), peer_raw_public_key(), - m_handshake_state.client_hello(), - m_handshake_state.server_hello(), - callbacks(), - rng()); + Server_Information(m_active_state->sni_hostname()), + callbacks().tls_current_timestamp()); if(callbacks().tls_should_persist_resumption_information(session)) { if(auto handle = session_manager().establish(session)) { @@ -125,10 +150,12 @@ } void Server_Impl_13::process_handshake_msg(Handshake_Message_13 message) { + BOTAN_STATE_CHECK(m_handshake != nullptr); + std::visit( [&](auto msg) { // first verify that the message was expected by the state machine... - m_transitions.confirm_transition_to(msg.get().type()); + m_handshake->transitions.confirm_transition_to(msg.get().type()); // ... then allow the library user to abort on their discretion callbacks().tls_inspect_handshake_msg(msg.get()); @@ -136,13 +163,18 @@ // ... finally handle the message handle(msg.get()); }, - m_handshake_state.received(std::move(message))); + m_handshake->state.received(std::move(message))); } void Server_Impl_13::process_post_handshake_msg(Post_Handshake_Message_13 message) { BOTAN_STATE_CHECK(is_handshake_complete()); - std::visit([&](auto msg) { handle(msg); }, m_handshake_state.received(std::move(message))); + const auto msg = specialize_to(std::move(message)); + if(!msg) { + throw TLS_Exception(Alert::UnexpectedMessage, "Received an unexpected post-handshake message"); + } + + std::visit([&](auto&& m) { handle(m); }, *msg); } void Server_Impl_13::process_dummy_change_cipher_spec() { @@ -150,7 +182,7 @@ // If an implementation detects a change_cipher_spec record received before // the first ClientHello message or after the peer's Finished message, it MUST be // treated as an unexpected record type [("unexpected_message" alert)]. - if(!m_handshake_state.has_client_hello() || m_handshake_state.has_client_finished()) { + if(!m_handshake || !m_handshake->state.has_client_hello() || m_handshake->state.has_client_finished()) { throw TLS_Exception(Alert::UnexpectedMessage, "Received an unexpected dummy Change Cipher Spec"); } @@ -164,12 +196,16 @@ } bool Server_Impl_13::is_handshake_complete() const { - return m_handshake_state.handshake_finished(); + return m_active_state.has_value() || (m_handshake != nullptr && m_handshake->state.has_client_finished()); } void Server_Impl_13::maybe_log_secret(std::string_view label, std::span secret) const { if(policy().allow_ssl_key_log_file()) { - callbacks().tls_ssl_key_log_data(label, m_handshake_state.client_hello().random(), secret); + if(m_active_state.has_value()) { + callbacks().tls_ssl_key_log_data(label, m_active_state->client_random(), secret); + } else { + callbacks().tls_ssl_key_log_data(label, m_handshake->state.client_hello().random(), secret); + } } } @@ -180,12 +216,12 @@ // After this, no further messages are expected here because this instance // will be replaced by a Server_Impl_12. - m_transitions.set_expected_next({}); + m_handshake->transitions.set_expected_next({}); } void Server_Impl_13::maybe_handle_compatibility_mode() { - BOTAN_ASSERT_NOMSG(m_handshake_state.has_client_hello()); - BOTAN_ASSERT_NOMSG(m_handshake_state.has_hello_retry_request() || m_handshake_state.has_server_hello()); + BOTAN_ASSERT_NOMSG(m_handshake->state.has_client_hello()); + BOTAN_ASSERT_NOMSG(m_handshake->state.has_hello_retry_request() || m_handshake->state.has_server_hello()); // RFC 8446 Appendix D.4 (Middlebox Compatibility Mode) // The server sends a dummy change_cipher_spec record immediately after @@ -208,8 +244,8 @@ // after Hello Retry Request (exclusively) or after a Server Hello that was // not preseded by a Hello Retry Request. const bool just_after_first_handshake_message = - m_handshake_state.has_hello_retry_request() ^ m_handshake_state.has_server_hello(); - const bool client_requested_compatibility_mode = !m_handshake_state.client_hello().session_id().empty(); + m_handshake->state.has_hello_retry_request() ^ m_handshake->state.has_server_hello(); + const bool client_requested_compatibility_mode = !m_handshake->state.client_hello().session_id().empty(); if(just_after_first_handshake_message && (policy().tls_13_middlebox_compatibility_mode() || client_requested_compatibility_mode)) { @@ -218,7 +254,7 @@ } void Server_Impl_13::handle_reply_to_client_hello(Server_Hello_13 server_hello) { - const auto& client_hello = m_handshake_state.client_hello(); + const auto& client_hello = m_handshake->state.client_hello(); const auto& exts = client_hello.extensions(); const bool uses_psk = server_hello.extensions().has(); @@ -230,24 +266,24 @@ std::unique_ptr psk_cipher_state; if(uses_psk) { - auto psk_extension = server_hello.extensions().get(); + auto* psk_extension = server_hello.extensions().get(); - psk_cipher_state = - std::visit(overloaded{[&, this](Session session) { - m_resumed_session = std::move(session); - return Cipher_State::init_with_psk(Connection_Side::Server, - Cipher_State::PSK_Type::Resumption, - m_resumed_session->extract_master_secret(), - cipher.prf_algo()); - }, - [&, this](ExternalPSK psk) { - m_psk_identity = psk.identity(); - return Cipher_State::init_with_psk(Connection_Side::Server, - Cipher_State::PSK_Type::External, - psk.extract_master_secret(), - cipher.prf_algo()); - }}, - psk_extension->take_session_to_resume_or_psk()); + psk_cipher_state = std::visit( + overloaded{[&, this](Session session) { + m_handshake->resumed_session = std::move(session); + return Cipher_State::init_with_psk(Connection_Side::Server, + Cipher_State::PSK_Type::Resumption, + m_handshake->resumed_session->extract_master_secret(), + cipher.prf_algo()); + }, + [&, this](ExternalPSK psk) { + m_handshake->psk_identity = psk.identity(); + const auto psk_type = + psk.is_imported() ? Cipher_State::PSK_Type::Imported : Cipher_State::PSK_Type::External; + return Cipher_State::init_with_psk( + Connection_Side::Server, psk_type, psk.extract_master_secret(), cipher.prf_algo()); + }}, + psk_extension->take_session_to_resume_or_psk()); // RFC 8446 4.2.11 // Prior to accepting PSK key establishment, the server MUST validate @@ -282,13 +318,13 @@ // NOTE: the server_hello variable is moved into the handshake state. Later // references to the Server Hello will need to consult the handshake // state object! - send_handshake_message(m_handshake_state.sending(std::move(server_hello))); + send_handshake_message(m_handshake->state.sending(std::move(server_hello))); maybe_handle_compatibility_mode(); // Setup encryption for all the remaining handshake messages m_cipher_state = [&] { // Currently, PSK without DHE is not implemented... - const auto my_keyshare = m_handshake_state.server_hello().extensions().get(); + auto* const my_keyshare = m_handshake->state.server_hello().extensions().get(); BOTAN_ASSERT_NONNULL(my_keyshare); if(uses_psk) { @@ -304,20 +340,30 @@ } }(); + // Decide up front whether we will request client authentication so the + // EncryptedExtensions can attach client_certificate_type when applicable + // (RFC 7250 4.2 requires the two messages to agree). + auto certificate_request = + uses_psk ? std::nullopt + : Certificate_Request_13::maybe_create(client_hello, credentials_manager(), callbacks(), policy()); + auto flight = aggregate_handshake_messages(); - flight.add(m_handshake_state.sending(Encrypted_Extensions(client_hello, policy(), callbacks()))); + const bool is_resumption = m_handshake->resumed_session.has_value(); + const bool requesting_client_auth = certificate_request.has_value(); + + flight.add(m_handshake->state.sending( + Encrypted_Extensions(client_hello, policy(), callbacks(), is_resumption, requesting_client_auth))); if(!uses_psk) { // RFC 8446 4.3.2 // A server which is authenticating with a certificate MAY optionally // request a certificate from the client. This message, if sent, MUST // follow EncryptedExtensions. - if(auto certificate_request = - Certificate_Request_13::maybe_create(client_hello, credentials_manager(), callbacks(), policy())) { - flight.add(m_handshake_state.sending(std::move(certificate_request.value()))); + if(certificate_request.has_value()) { + flight.add(m_handshake->state.sending(std::move(certificate_request.value()))); } - const auto& enc_exts = m_handshake_state.encrypted_extensions().extensions(); + const auto& enc_exts = m_handshake->state.encrypted_extensions().extensions(); // RFC 7250 4.2 // This client_certificate_type extension in the server hello then @@ -326,7 +372,7 @@ // // Note: TLS 1.3 carries this extension in the Encrypted Extensions // message instead of the Server Hello. - if(auto client_cert_type = enc_exts.get()) { + if(auto* client_cert_type = enc_exts.get()) { set_selected_certificate_type(client_cert_type->selected_certificate_type()); } @@ -336,29 +382,30 @@ // was negotiated, then each CertificateEntry contains a DER-encoded // X.509 certificate. const auto cert_type = [&] { - if(auto server_cert_type = enc_exts.get()) { + if(auto* server_cert_type = enc_exts.get()) { return server_cert_type->selected_certificate_type(); } else { return Certificate_Type::X509; } }(); - flight.add(m_handshake_state.sending(Certificate_13(client_hello, credentials_manager(), callbacks(), cert_type))) - .add(m_handshake_state.sending(Certificate_Verify_13(m_handshake_state.server_certificate(), - client_hello.signature_schemes(), - client_hello.sni_hostname(), - m_transcript_hash.current(), - Connection_Side::Server, - credentials_manager(), - policy(), - callbacks(), - rng()))); + flight + .add(m_handshake->state.sending(Certificate_13(client_hello, credentials_manager(), callbacks(), cert_type))) + .add(m_handshake->state.sending(Certificate_Verify_13(m_handshake->state.server_certificate(), + client_hello.signature_schemes(), + client_hello.sni_hostname(), + m_transcript_hash.current(), + Connection_Side::Server, + credentials_manager(), + policy(), + callbacks(), + rng()))); } - flight.add(m_handshake_state.sending(Finished_13(m_cipher_state.get(), m_transcript_hash.current()))); + flight.add(m_handshake->state.sending(Finished_13(m_cipher_state.get(), m_transcript_hash.current()))); if(client_hello.extensions().has() && - m_handshake_state.encrypted_extensions().extensions().has()) { + m_handshake->state.encrypted_extensions().extensions().has()) { // RFC 8449 4. // When the "record_size_limit" extension is negotiated, an endpoint // MUST NOT generate a protected record with plaintext that is larger @@ -374,8 +421,8 @@ // // Hence, the "outgoing" limit is what the client requested and the // "incoming" limit is what we will request in the Encrypted Extensions. - const auto outgoing_limit = client_hello.extensions().get(); - const auto incoming_limit = m_handshake_state.encrypted_extensions().extensions().get(); + auto* const outgoing_limit = client_hello.extensions().get(); + auto* const incoming_limit = m_handshake->state.encrypted_extensions().extensions().get(); set_record_size_limits(outgoing_limit->limit(), incoming_limit->limit()); } @@ -383,16 +430,16 @@ m_cipher_state->advance_with_server_finished(m_transcript_hash.current(), *this); - if(m_handshake_state.has_certificate_request()) { + if(m_handshake->state.has_certificate_request()) { // RFC 8446 4.4.2 // The client MUST send a Certificate message if and only if the server // has requested client authentication via a CertificateRequest message // [...]. If the server requests client authentication but no // suitable certificate is available, the client MUST send a Certificate // message containing no certificates [...]. - m_transitions.set_expected_next(Handshake_Type::Certificate); + m_handshake->transitions.set_expected_next(Handshake_Type::Certificate); } else { - m_transitions.set_expected_next(Handshake_Type::Finished); + m_handshake->transitions.set_expected_next(Handshake_Type::Finished); } } @@ -400,21 +447,22 @@ auto cipher = Ciphersuite::by_id(hello_retry_request.ciphersuite()); BOTAN_ASSERT_NOMSG(cipher.has_value()); // should work, since we chose that suite - send_handshake_message(m_handshake_state.sending(std::move(hello_retry_request))); + send_handshake_message(m_handshake->state.sending(std::move(hello_retry_request))); maybe_handle_compatibility_mode(); m_transcript_hash = Transcript_Hash_State::recreate_after_hello_retry_request(cipher->prf_algo(), m_transcript_hash); - m_transitions.set_expected_next(Handshake_Type::ClientHello); + m_handshake->transitions.set_expected_next(Handshake_Type::ClientHello); } -void Server_Impl_13::handle(const Client_Hello_12& ch) { +void Server_Impl_13::handle(const Client_Hello_12_Shim& ch) { // The detailed handling of the TLS 1.2 compliant Client Hello is left to // the TLS 1.2 server implementation. BOTAN_UNUSED(ch); + BOTAN_ASSERT_NONNULL(m_handshake); // After we sent a Hello Retry Request we must not accept a downgrade. - if(m_handshake_state.has_hello_retry_request()) { + if(m_handshake->state.has_hello_retry_request()) { throw TLS_Exception(Alert::UnexpectedMessage, "Received a TLS 1.2 Client Hello after Hello Retry Request"); } @@ -432,9 +480,11 @@ } void Server_Impl_13::handle(const Client_Hello_13& client_hello) { + BOTAN_ASSERT_NONNULL(m_handshake); + const auto& exts = client_hello.extensions(); - const bool is_initial_client_hello = !m_handshake_state.has_hello_retry_request(); + const bool is_initial_client_hello = !m_handshake->state.has_hello_retry_request(); if(is_initial_client_hello) { const auto preferred_version = client_hello.highest_supported_version(policy()); @@ -464,9 +514,11 @@ BOTAN_ASSERT_NOMSG(exts.has()); if(!is_initial_client_hello) { - const auto& hrr_exts = m_handshake_state.hello_retry_request().extensions(); + const auto& hrr_exts = m_handshake->state.hello_retry_request().extensions(); const auto offered_groups = exts.get()->offered_groups(); - const auto selected_group = hrr_exts.get()->selected_group(); + const auto* hrr_key_share = hrr_exts.get(); + BOTAN_ASSERT_NONNULL(hrr_key_share); + const auto selected_group = hrr_key_share->selected_group(); if(offered_groups.size() != 1 || offered_groups.at(0) != selected_group) { throw TLS_Exception(Alert::IllegalParameter, "Client did not comply with the requested key exchange group"); } @@ -484,6 +536,8 @@ } void Server_Impl_13::handle(const Certificate_13& certificate_msg) { + BOTAN_ASSERT_NONNULL(m_handshake); + // RFC 8446 4.3.2 // certificate_request_context: [...] This field SHALL be zero length // unless used for the post-handshake authentication exchanges [...]. @@ -494,7 +548,7 @@ // RFC 8446 4.4.2 // Extensions in the Certificate message from the client MUST correspond // to extensions in the CertificateRequest message from the server. - certificate_msg.validate_extensions(m_handshake_state.certificate_request().extensions().extension_types(), + certificate_msg.validate_extensions(m_handshake->state.certificate_request().extensions().extension_types(), callbacks()); // RFC 8446 4.4.2.4 @@ -510,7 +564,7 @@ // RFC 8446 4.4.2 // A Finished message MUST be sent regardless of whether the // Certificate message is empty. - m_transitions.set_expected_next(Handshake_Type::Finished); + m_handshake->transitions.set_expected_next(Handshake_Type::Finished); } else { // RFC 8446 4.4.2.4 // [...], if some aspect of the certificate chain was unacceptable @@ -521,38 +575,39 @@ // TODO: We could make this dependent on Policy::require_client_auth(). // Though, apps may also override Callbacks::tls_verify_cert_chain() // and 'ignore' validation issues to a certain extent. - certificate_msg.verify(callbacks(), - policy(), - credentials_manager(), - m_handshake_state.client_hello().sni_hostname(), - m_handshake_state.client_hello().extensions().has()); + + const bool use_ocsp = m_handshake->state.certificate_request().extensions().has(); + certificate_msg.verify( + callbacks(), policy(), credentials_manager(), m_handshake->state.client_hello().sni_hostname(), use_ocsp); // RFC 8446 4.4.3 // Clients MUST send this message whenever authenticating via a // certificate (i.e., when the Certificate message // is non-empty). When sent, this message MUST appear immediately after // the Certificate message [...]. - m_transitions.set_expected_next(Handshake_Type::CertificateVerify); + m_handshake->transitions.set_expected_next(Handshake_Type::CertificateVerify); } } void Server_Impl_13::handle(const Certificate_Verify_13& certificate_verify_msg) { + BOTAN_ASSERT_NONNULL(m_handshake); + // RFC 8446 4.4.3 // If sent by a client, the signature algorithm used in the signature // MUST be one of those present in the supported_signature_algorithms // field of the "signature_algorithms" extension in the // CertificateRequest message. - const auto offered = m_handshake_state.certificate_request().signature_schemes(); + const auto offered = m_handshake->state.certificate_request().signature_schemes(); if(!value_exists(offered, certificate_verify_msg.signature_scheme())) { throw TLS_Exception(Alert::IllegalParameter, "We did not offer the usage of " + certificate_verify_msg.signature_scheme().to_string() + " as a signature scheme"); } - BOTAN_ASSERT_NOMSG(m_handshake_state.has_client_certificate_msg() && - !m_handshake_state.client_certificate().empty()); - bool sig_valid = certificate_verify_msg.verify( - *m_handshake_state.client_certificate().public_key(), callbacks(), m_transcript_hash.previous()); + BOTAN_ASSERT_NOMSG(m_handshake->state.has_client_certificate_msg() && + !m_handshake->state.client_certificate().empty()); + const bool sig_valid = certificate_verify_msg.verify( + *m_handshake->state.client_certificate().public_key(), callbacks(), m_transcript_hash.previous()); // RFC 8446 4.4.3 // If the verification fails, the receiver MUST terminate the handshake @@ -561,10 +616,12 @@ throw TLS_Exception(Alert::DecryptError, "Client certificate verification failed"); } - m_transitions.set_expected_next(Handshake_Type::Finished); + m_handshake->transitions.set_expected_next(Handshake_Type::Finished); } void Server_Impl_13::handle(const Finished_13& finished_msg) { + BOTAN_ASSERT_NONNULL(m_handshake); + // RFC 8446 4.4.4 // Recipients of Finished messages MUST verify that the contents are // correct and if incorrect MUST terminate the connection with a @@ -573,23 +630,64 @@ throw TLS_Exception(Alert::DecryptError, "Finished message didn't verify"); } + m_handshake->state.confirm_peer_finished_verified(); + // Give the application a chance for a final veto before fully // establishing the connection. callbacks().tls_session_established( - Session_Summary(m_handshake_state.server_hello(), + Session_Summary(m_handshake->state.server_hello(), Connection_Side::Server, peer_cert_chain(), peer_raw_public_key(), - m_psk_identity, - m_resumed_session.has_value(), - Server_Information(m_handshake_state.client_hello().sni_hostname()), + m_handshake->psk_identity, + m_handshake->resumed_session.has_value(), + Server_Information(m_handshake->state.client_hello().sni_hostname()), callbacks().tls_current_timestamp())); m_cipher_state->advance_with_client_finished(m_transcript_hash.current()); // no more handshake messages expected - m_transitions.set_expected_next({}); + m_handshake->transitions.set_expected_next({}); + + // Extract post-handshake state before signaling activation. + { + auto extract_certs = [&]() -> std::vector { + if(m_handshake->state.has_client_certificate_msg() && + m_handshake->state.client_certificate().has_certificate_chain()) { + return m_handshake->state.client_certificate().cert_chain(); + } + if(m_handshake->resumed_session.has_value()) { + return m_handshake->resumed_session->peer_certs(); + } + return {}; + }; + + auto extract_raw_pk = [&]() -> std::shared_ptr { + if(m_handshake->state.has_client_certificate_msg() && + m_handshake->state.client_certificate().is_raw_public_key()) { + return m_handshake->state.client_certificate().public_key(); + } + if(m_handshake->resumed_session.has_value()) { + return m_handshake->resumed_session->peer_raw_public_key(); + } + return nullptr; + }; + + const bool supports_psk_dhe = + m_handshake->state.client_hello().extensions().has() && + value_exists(m_handshake->state.client_hello().extensions().get()->modes(), + PSK_Key_Exchange_Mode::PSK_DHE_KE); + + m_active_state = Active_Connection_State_13(m_handshake->state, + extract_certs(), + extract_raw_pk(), + m_handshake->psk_identity, + m_handshake->state.client_hello().sni_hostname(), + supports_psk_dhe); + } + m_handshake.reset(); + m_transcript_hash = Transcript_Hash_State(); callbacks().tls_session_activated(); if(new_session_ticket_supported()) { diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_server_impl_13.h botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_server_impl_13.h --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_server_impl_13.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_server_impl_13.h 2026-05-07 01:38:28.000000000 +0000 @@ -19,7 +19,7 @@ /** * SSL/TLS Server 1.3 implementation */ -class Server_Impl_13 : public Channel_Impl_13 { +class Server_Impl_13 final : public Channel_Impl_13 { public: explicit Server_Impl_13(const std::shared_ptr& callbacks, const std::shared_ptr& session_manager, @@ -43,7 +43,7 @@ void process_dummy_change_cipher_spec() override; using Channel_Impl_13::handle; - void handle(const Client_Hello_12& client_hello_msg); + void handle(const Client_Hello_12_Shim& client_hello_msg); void handle(const Client_Hello_13& client_hello_msg); void handle(const Certificate_13& certificate_msg); void handle(const Certificate_Verify_13& certificate_verify_msg); @@ -58,11 +58,14 @@ void downgrade(); private: - Server_Handshake_State_13 m_handshake_state; - Handshake_Transitions m_transitions; + struct Pending_Handshake { + Server_Handshake_State_13 state; + Handshake_Transitions transitions; + std::optional resumed_session; + std::optional psk_identity; + }; - std::optional m_resumed_session; - std::optional m_psk_identity; + std::unique_ptr m_handshake; }; } // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_transcript_hash_13.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_transcript_hash_13.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_transcript_hash_13.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_transcript_hash_13.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,8 +8,9 @@ #include +#include #include -#include +#include #include #include @@ -20,6 +21,13 @@ set_algorithm(algo_spec); } +Transcript_Hash_State::Transcript_Hash_State() = default; + +Transcript_Hash_State::~Transcript_Hash_State() = default; + +Transcript_Hash_State::Transcript_Hash_State(Transcript_Hash_State&& other) noexcept = default; +Transcript_Hash_State& Transcript_Hash_State::operator=(Transcript_Hash_State&& other) noexcept = default; + Transcript_Hash_State::Transcript_Hash_State(const Transcript_Hash_State& other) : m_hash((other.m_hash != nullptr) ? other.m_hash->copy_state() : nullptr), m_unprocessed_transcript(other.m_unprocessed_transcript), @@ -34,12 +42,12 @@ BOTAN_STATE_CHECK(prev_transcript_hash_state.m_hash == nullptr); BOTAN_STATE_CHECK(prev_transcript_hash_state.m_unprocessed_transcript.size() == 2); - Transcript_Hash_State ths(algo_spec); + Transcript_Hash_State transcript_hash(algo_spec); const auto& client_hello_1 = prev_transcript_hash_state.m_unprocessed_transcript.front(); const auto& hello_retry_request = prev_transcript_hash_state.m_unprocessed_transcript.back(); - const size_t hash_length = ths.m_hash->output_length(); + const size_t hash_length = transcript_hash.m_hash->output_length(); BOTAN_ASSERT_NOMSG(hash_length < 256); // RFC 8446 4.4.1 @@ -52,12 +60,12 @@ message_hash.push_back(0x00); message_hash.push_back(0x00); message_hash.push_back(static_cast(hash_length)); - message_hash += ths.m_hash->process(client_hello_1); + message_hash += transcript_hash.m_hash->process(client_hello_1); - ths.update(message_hash); - ths.update(hello_retry_request); + transcript_hash.update(message_hash); + transcript_hash.update(hello_retry_request); - return ths; + return transcript_hash; } namespace { @@ -145,7 +153,7 @@ } // namespace void Transcript_Hash_State::update(std::span serialized_message_s) { - auto serialized_message = serialized_message_s.data(); + const auto* serialized_message = serialized_message_s.data(); auto serialized_message_length = serialized_message_s.size(); if(m_hash != nullptr) { auto truncation_mark = serialized_message_length; diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_transcript_hash_13.h botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_transcript_hash_13.h --- botan3-3.7.1+dfsg/src/lib/tls/tls13/tls_transcript_hash_13.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13/tls_transcript_hash_13.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,14 +9,18 @@ #ifndef BOTAN_TLS_TRANSCRIPT_HASH_13_H_ #define BOTAN_TLS_TRANSCRIPT_HASH_13_H_ -#include #include - #include #include -#include +#include #include +namespace Botan { + +class HashFunction; + +} // namespace Botan + namespace Botan::TLS { /** @@ -27,9 +31,9 @@ */ class BOTAN_TEST_API Transcript_Hash_State { public: - Transcript_Hash_State() = default; - Transcript_Hash_State(std::string_view algo_spec); - ~Transcript_Hash_State() = default; + Transcript_Hash_State(); + explicit Transcript_Hash_State(std::string_view algo_spec); + ~Transcript_Hash_State(); /** * Recreates a Transcript_Hash_State after receiving a Hello Retry Request. @@ -45,8 +49,8 @@ Transcript_Hash_State& operator=(const Transcript_Hash_State&) = delete; - Transcript_Hash_State(Transcript_Hash_State&&) = default; - Transcript_Hash_State& operator=(Transcript_Hash_State&&) = default; + Transcript_Hash_State(Transcript_Hash_State&& other) noexcept; + Transcript_Hash_State& operator=(Transcript_Hash_State&& other) noexcept; void update(std::span serialized_message_s); @@ -79,6 +83,7 @@ Transcript_Hash_State clone() const; private: + // called by clone Transcript_Hash_State(const Transcript_Hash_State& other); private: diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13_pqc/hybrid_public_key.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13_pqc/hybrid_public_key.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13_pqc/hybrid_public_key.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13_pqc/hybrid_public_key.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,12 +10,16 @@ #include +#include #include - -#include +#include +#include +#include +#include #include -#include #include +#include +#include namespace Botan::TLS { @@ -40,6 +44,8 @@ return {{"ML-KEM", "ML-KEM-768"}, {"X25519", "X25519"}}; case Group_Params::HYBRID_SECP256R1_ML_KEM_768: return {{"ECDH", "secp256r1"}, {"ML-KEM", "ML-KEM-768"}}; + case Group_Params::HYBRID_SECP384R1_ML_KEM_1024: + return {{"ECDH", "secp384r1"}, {"ML-KEM", "ML-KEM-1024"}}; case Group_Params::HYBRID_X25519_eFRODOKEM_640_SHAKE_OQS: return {{"X25519", "X25519"}, {"FrodoKEM", "eFrodoKEM-640-SHAKE"}}; @@ -85,13 +91,17 @@ // TODO: This is inconvenient, confusing and error-prone. Find a better way // to load arbitrary public keys. for(const auto& spec : specs) { - result.push_back(AlgorithmIdentifier(spec.second, AlgorithmIdentifier::USE_EMPTY_PARAM)); + if(spec.first == "ECDH") { + result.push_back(AlgorithmIdentifier("ECDH", EC_Group::from_name(spec.second).DER_encode())); + } else { + result.push_back(AlgorithmIdentifier(spec.second, AlgorithmIdentifier::USE_EMPTY_PARAM)); + } } return result; } -std::vector public_value_lengths_for_group(Group_Params group) { +std::vector public_key_lengths_for_group(Group_Params group) { BOTAN_ASSERT_NOMSG(group.is_pqc_hybrid()); // This duplicates information of the algorithm internals. @@ -102,113 +112,171 @@ case Group_Params::HYBRID_X25519_ML_KEM_768: return {1184, 32}; case Group_Params::HYBRID_SECP256R1_ML_KEM_768: - return {32, 1184}; + return {65, 1184}; + case Group_Params::HYBRID_SECP384R1_ML_KEM_1024: + return {97, 1568}; case Group_Params::HYBRID_X25519_eFRODOKEM_640_SHAKE_OQS: - return {32, 9616}; case Group_Params::HYBRID_X25519_eFRODOKEM_640_AES_OQS: return {32, 9616}; + case Group_Params::HYBRID_X448_eFRODOKEM_976_SHAKE_OQS: - return {56, 15632}; case Group_Params::HYBRID_X448_eFRODOKEM_976_AES_OQS: return {56, 15632}; case Group_Params::HYBRID_SECP256R1_eFRODOKEM_640_SHAKE_OQS: - return {32, 9616}; case Group_Params::HYBRID_SECP256R1_eFRODOKEM_640_AES_OQS: - return {32, 9616}; + return {65, 9616}; case Group_Params::HYBRID_SECP384R1_eFRODOKEM_976_SHAKE_OQS: - return {48, 15632}; case Group_Params::HYBRID_SECP384R1_eFRODOKEM_976_AES_OQS: - return {48, 15632}; + return {97, 15632}; case Group_Params::HYBRID_SECP521R1_eFRODOKEM_1344_SHAKE_OQS: - return {66, 21520}; case Group_Params::HYBRID_SECP521R1_eFRODOKEM_1344_AES_OQS: - return {66, 21520}; + return {133, 21520}; default: return {}; } } +std::vector> convert_kex_to_kem_pks(std::vector> pks) { + std::vector> result; + std::transform(pks.begin(), pks.end(), std::back_inserter(result), [](auto& key) -> std::unique_ptr { + BOTAN_ARG_CHECK(key != nullptr, "Public key list contains a nullptr"); + if(key->supports_operation(PublicKeyOperation::KeyAgreement) && + !key->supports_operation(PublicKeyOperation::KeyEncapsulation)) { + return std::make_unique(std::move(key)); + } else { + return std::move(key); + } + }); + return result; +} + +std::vector> convert_kex_to_kem_sks(std::vector> sks) { + std::vector> result; + std::transform(sks.begin(), sks.end(), std::back_inserter(result), [](auto& key) -> std::unique_ptr { + BOTAN_ARG_CHECK(key != nullptr, "Private key list contains a nullptr"); + if(key->supports_operation(PublicKeyOperation::KeyAgreement) && + !key->supports_operation(PublicKeyOperation::KeyEncapsulation)) { + auto* ka_key = dynamic_cast(key.get()); + BOTAN_ASSERT_NONNULL(ka_key); + (void)key.release(); + return std::make_unique(std::unique_ptr(ka_key)); + } else { + return std::move(key); + } + }); + return result; +} + +template +void concat_secret_combiner(KEM_Operation& op, + std::span out_shared_secret, + const std::vector>& shared_secrets, + size_t desired_shared_key_len) { + BOTAN_ARG_CHECK(out_shared_secret.size() == op.shared_key_length(desired_shared_key_len), + "Invalid output buffer size"); + + BufferStuffer shared_secret_stuffer(out_shared_secret); + for(const auto& ss : shared_secrets) { + shared_secret_stuffer.append(ss); + } + BOTAN_ASSERT_NOMSG(shared_secret_stuffer.full()); +} + +template +size_t concat_shared_key_length(const std::vector& operation) { + return reduce( + operation, size_t(0), [](size_t acc, const auto& op) { return acc + op.shared_key_length(0 /*no KDF*/); }); +} + +/// Encryptor that simply concatenates the multiple shared secrets +class Hybrid_TLS_KEM_Encryptor final : public KEM_Encryption_with_Combiner { + public: + Hybrid_TLS_KEM_Encryptor(const std::vector>& public_keys, std::string_view provider) : + KEM_Encryption_with_Combiner(public_keys, provider) {} + + void combine_shared_secrets(std::span out_shared_secret, + const std::vector>& shared_secrets, + const std::vector>& /*ciphertexts*/, + size_t desired_shared_key_len, + std::span /*salt*/) override { + concat_secret_combiner(*this, out_shared_secret, shared_secrets, desired_shared_key_len); + } + + size_t shared_key_length(size_t /*desired_shared_key_len*/) const override { + return concat_shared_key_length(encryptors()); + } +}; + +/// Decryptor that simply concatenates the multiple shared secrets +class Hybrid_TLS_KEM_Decryptor final : public KEM_Decryption_with_Combiner { + public: + Hybrid_TLS_KEM_Decryptor(const std::vector>& private_keys, + RandomNumberGenerator& rng, + const std::string_view provider) : + KEM_Decryption_with_Combiner(private_keys, rng, provider) {} + + void combine_shared_secrets(std::span out_shared_secret, + const std::vector>& shared_secrets, + const std::vector>& /*ciphertexts*/, + size_t desired_shared_key_len, + std::span /*salt*/) override { + concat_secret_combiner(*this, out_shared_secret, shared_secrets, desired_shared_key_len); + } + + size_t shared_key_length(size_t /*desired_shared_key_len*/) const override { + return concat_shared_key_length(decryptors()); + } +}; + } // namespace std::unique_ptr Hybrid_KEM_PublicKey::load_for_group( - Group_Params group, std::span concatenated_public_values) { - const auto public_value_lengths = public_value_lengths_for_group(group); + Group_Params group, std::span concatenated_public_keys) { + const auto public_key_lengths = public_key_lengths_for_group(group); auto alg_ids = algorithm_identifiers_for_group(group); - BOTAN_ASSERT_NOMSG(public_value_lengths.size() == alg_ids.size()); + BOTAN_ASSERT_NOMSG(public_key_lengths.size() == alg_ids.size()); - const auto expected_public_values_length = - reduce(public_value_lengths, size_t(0), [](size_t acc, size_t len) { return acc + len; }); - if(expected_public_values_length != concatenated_public_values.size()) { + const auto expected_public_keys_length = + reduce(public_key_lengths, size_t(0), [](size_t acc, size_t len) { return acc + len; }); + if(expected_public_keys_length != concatenated_public_keys.size()) { throw Decoding_Error("Concatenated public values have an unexpected length"); } - BufferSlicer public_value_slicer(concatenated_public_values); + BufferSlicer public_key_slicer(concatenated_public_keys); std::vector> pks; pks.reserve(alg_ids.size()); for(size_t idx = 0; idx < alg_ids.size(); ++idx) { - pks.emplace_back(load_public_key(alg_ids[idx], public_value_slicer.take(public_value_lengths[idx]))); + pks.emplace_back(load_public_key(alg_ids[idx], public_key_slicer.take(public_key_lengths[idx]))); } - BOTAN_ASSERT_NOMSG(public_value_slicer.empty()); + BOTAN_ASSERT_NOMSG(public_key_slicer.empty()); return std::make_unique(std::move(pks)); } -Hybrid_KEM_PublicKey::Hybrid_KEM_PublicKey(std::vector> pks) { - BOTAN_ARG_CHECK(pks.size() >= 2, "List of public keys must include at least two keys"); - BOTAN_ARG_CHECK(std::all_of(pks.begin(), pks.end(), [](const auto& pk) { return pk != nullptr; }), - "List of public keys contains a nullptr"); - BOTAN_ARG_CHECK(std::all_of(pks.begin(), - pks.end(), - [](const auto& pk) { - return pk->supports_operation(PublicKeyOperation::KeyEncapsulation) || - pk->supports_operation(PublicKeyOperation::KeyAgreement); - }), - "Some provided public key is not compatible with this hybrid wrapper"); - - std::transform( - pks.begin(), pks.end(), std::back_inserter(m_public_keys), [](auto& key) -> std::unique_ptr { - if(key->supports_operation(PublicKeyOperation::KeyAgreement) && - !key->supports_operation(PublicKeyOperation::KeyEncapsulation)) { - return std::make_unique(std::move(key)); - } else { - return std::move(key); - } - }); - - m_key_length = - reduce(m_public_keys, size_t(0), [](size_t kl, const auto& key) { return std::max(kl, key->key_length()); }); - m_estimated_strength = reduce( - m_public_keys, size_t(0), [](size_t es, const auto& key) { return std::max(es, key->estimated_strength()); }); -} +Hybrid_KEM_PublicKey::Hybrid_KEM_PublicKey(std::vector> pks) : + Hybrid_PublicKey(convert_kex_to_kem_pks(std::move(pks))) {} + +Hybrid_KEM_PrivateKey::Hybrid_KEM_PrivateKey(std::vector> sks) : + Hybrid_PublicKey(convert_kex_to_kem_pks(extract_public_keys(sks))), + Hybrid_PrivateKey(convert_kex_to_kem_sks(std::move(sks))) {} std::string Hybrid_KEM_PublicKey::algo_name() const { - std::ostringstream algo_name("Hybrid("); - for(size_t i = 0; i < m_public_keys.size(); ++i) { + std::ostringstream algo_name; + algo_name << "Hybrid("; + for(size_t i = 0; i < public_keys().size(); ++i) { if(i > 0) { algo_name << ","; } - algo_name << m_public_keys[i]->algo_name(); + algo_name << public_keys().at(i)->algo_name(); } algo_name << ")"; return algo_name.str(); } -size_t Hybrid_KEM_PublicKey::estimated_strength() const { - return m_estimated_strength; -} - -size_t Hybrid_KEM_PublicKey::key_length() const { - return m_key_length; -} - -bool Hybrid_KEM_PublicKey::check_key(RandomNumberGenerator& rng, bool strong) const { - return reduce(m_public_keys, true, [&](bool ckr, const auto& key) { return ckr && key->check_key(rng, strong); }); -} - AlgorithmIdentifier Hybrid_KEM_PublicKey::algorithm_identifier() const { throw Botan::Not_Implemented("Hybrid keys don't have an algorithm identifier"); } @@ -225,87 +293,23 @@ // to be used with values that are not fixed-length, a length prefix or // other unambiguous encoding must be used to ensure that the composition // of the two values is injective. - return reduce(m_public_keys, std::vector(), [](auto pkb, const auto& key) { + return reduce(public_keys(), std::vector(), [](auto pkb, const auto& key) { return concat(pkb, key->raw_public_key_bits()); }); } std::unique_ptr Hybrid_KEM_PublicKey::generate_another(RandomNumberGenerator& rng) const { - std::vector> new_private_keys; - std::transform( - m_public_keys.begin(), m_public_keys.end(), std::back_inserter(new_private_keys), [&](const auto& public_key) { - return public_key->generate_another(rng); - }); - return std::make_unique(std::move(new_private_keys)); -} - -bool Hybrid_KEM_PublicKey::supports_operation(PublicKeyOperation op) const { - return PublicKeyOperation::KeyEncapsulation == op; + return std::make_unique(generate_other_sks_from_pks(rng)); } -namespace { - -class Hybrid_KEM_Encryption_Operation final : public PK_Ops::KEM_Encryption_with_KDF { - public: - Hybrid_KEM_Encryption_Operation(const Hybrid_KEM_PublicKey& key, - std::string_view kdf, - std::string_view provider) : - PK_Ops::KEM_Encryption_with_KDF(kdf), m_raw_kem_shared_key_length(0), m_encapsulated_key_length(0) { - m_kem_encryptors.reserve(key.public_keys().size()); - for(const auto& k : key.public_keys()) { - const auto& newenc = m_kem_encryptors.emplace_back(*k, "Raw", provider); - m_raw_kem_shared_key_length += newenc.shared_key_length(0 /* no KDF */); - m_encapsulated_key_length += newenc.encapsulated_key_length(); - } - } - - size_t raw_kem_shared_key_length() const override { return m_raw_kem_shared_key_length; } - - size_t encapsulated_key_length() const override { return m_encapsulated_key_length; } - - void raw_kem_encrypt(std::span out_encapsulated_key, - std::span raw_shared_key, - Botan::RandomNumberGenerator& rng) override { - BOTAN_ASSERT_NOMSG(out_encapsulated_key.size() == encapsulated_key_length()); - BOTAN_ASSERT_NOMSG(raw_shared_key.size() == raw_kem_shared_key_length()); - - BufferStuffer encaps_key_stuffer(out_encapsulated_key); - BufferStuffer shared_key_stuffer(raw_shared_key); - - for(auto& kem_enc : m_kem_encryptors) { - kem_enc.encrypt(encaps_key_stuffer.next(kem_enc.encapsulated_key_length()), - shared_key_stuffer.next(kem_enc.shared_key_length(0 /* no KDF */)), - rng); - } - } - - private: - std::vector m_kem_encryptors; - size_t m_raw_kem_shared_key_length; - size_t m_encapsulated_key_length; -}; - -} // namespace - std::unique_ptr Hybrid_KEM_PublicKey::create_kem_encryption_op( - std::string_view kdf, std::string_view provider) const { - return std::make_unique(*this, kdf, provider); -} - -namespace { - -auto extract_public_keys(const std::vector>& private_keys) { - std::vector> public_keys; - public_keys.reserve(private_keys.size()); - for(const auto& private_key : private_keys) { - BOTAN_ARG_CHECK(private_key != nullptr, "List of private keys contains a nullptr"); - public_keys.push_back(private_key->public_key()); + std::string_view params, std::string_view provider) const { + if(params != "Raw" && !params.empty()) { + throw Botan::Invalid_Argument("Hybrid KEM encryption does not support KDFs"); } - return public_keys; + return std::make_unique(public_keys(), provider); } -} // namespace - std::unique_ptr Hybrid_KEM_PrivateKey::generate_from_group(Group_Params group, RandomNumberGenerator& rng) { const auto algo_spec = algorithm_specs_for_group(group); @@ -317,88 +321,12 @@ return std::make_unique(std::move(private_keys)); } -Hybrid_KEM_PrivateKey::Hybrid_KEM_PrivateKey(std::vector> sks) : - Hybrid_KEM_PublicKey(extract_public_keys(sks)) { - BOTAN_ARG_CHECK(sks.size() >= 2, "List of private keys must include at least two keys"); - BOTAN_ARG_CHECK(std::all_of(sks.begin(), - sks.end(), - [](const auto& sk) { - return sk->supports_operation(PublicKeyOperation::KeyEncapsulation) || - sk->supports_operation(PublicKeyOperation::KeyAgreement); - }), - "Some provided private key is not compatible with this hybrid wrapper"); - - std::transform( - sks.begin(), sks.end(), std::back_inserter(m_private_keys), [](auto& key) -> std::unique_ptr { - if(key->supports_operation(PublicKeyOperation::KeyAgreement) && - !key->supports_operation(PublicKeyOperation::KeyEncapsulation)) { - auto ka_key = dynamic_cast(key.get()); - BOTAN_ASSERT_NONNULL(ka_key); - (void)key.release(); - return std::make_unique(std::unique_ptr(ka_key)); - } else { - return std::move(key); - } - }); -} - -secure_vector Hybrid_KEM_PrivateKey::private_key_bits() const { - throw Not_Implemented("Hybrid private keys cannot be serialized"); -} - -std::unique_ptr Hybrid_KEM_PrivateKey::public_key() const { - return std::make_unique(extract_public_keys(m_private_keys)); -} - -bool Hybrid_KEM_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const { - return reduce(m_public_keys, true, [&](bool ckr, const auto& key) { return ckr && key->check_key(rng, strong); }); -} - -namespace { - -class Hybrid_KEM_Decryption final : public PK_Ops::KEM_Decryption_with_KDF { - public: - Hybrid_KEM_Decryption(const Hybrid_KEM_PrivateKey& key, - RandomNumberGenerator& rng, - const std::string_view kdf, - const std::string_view provider) : - PK_Ops::KEM_Decryption_with_KDF(kdf), m_encapsulated_key_length(0), m_raw_kem_shared_key_length(0) { - m_decryptors.reserve(key.private_keys().size()); - for(const auto& private_key : key.private_keys()) { - const auto& newdec = m_decryptors.emplace_back(*private_key, rng, "Raw", provider); - m_encapsulated_key_length += newdec.encapsulated_key_length(); - m_raw_kem_shared_key_length += newdec.shared_key_length(0 /* no KDF */); - } - } - - void raw_kem_decrypt(std::span out_shared_key, std::span encap_key) override { - BOTAN_ASSERT_NOMSG(out_shared_key.size() == raw_kem_shared_key_length()); - BOTAN_ASSERT_NOMSG(encap_key.size() == encapsulated_key_length()); - - BufferSlicer encap_key_slicer(encap_key); - BufferStuffer shared_secret_stuffer(out_shared_key); - - for(auto& decryptor : m_decryptors) { - decryptor.decrypt(shared_secret_stuffer.next(decryptor.shared_key_length(0 /* no KDF */)), - encap_key_slicer.take(decryptor.encapsulated_key_length())); - } - } - - size_t encapsulated_key_length() const override { return m_encapsulated_key_length; } - - size_t raw_kem_shared_key_length() const override { return m_raw_kem_shared_key_length; } - - private: - std::vector m_decryptors; - size_t m_encapsulated_key_length; - size_t m_raw_kem_shared_key_length; -}; - -} // namespace - std::unique_ptr Hybrid_KEM_PrivateKey::create_kem_decryption_op( - RandomNumberGenerator& rng, std::string_view kdf, std::string_view provider) const { - return std::make_unique(*this, rng, kdf, provider); + RandomNumberGenerator& rng, std::string_view params, std::string_view provider) const { + if(params != "Raw" && !params.empty()) { + throw Botan::Invalid_Argument("Hybrid KEM decryption does not support KDFs"); + } + return std::make_unique(private_keys(), rng, provider); } } // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13_pqc/hybrid_public_key.h botan3-3.12.0+dfsg/src/lib/tls/tls13_pqc/hybrid_public_key.h --- botan3-3.7.1+dfsg/src/lib/tls/tls13_pqc/hybrid_public_key.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13_pqc/hybrid_public_key.h 2026-05-07 01:38:28.000000000 +0000 @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -37,7 +38,7 @@ * serializes and parses keys and ciphertexts as described in the * above-mentioned IETF draft for a post-quantum TLS 1.3. */ -class BOTAN_TEST_API Hybrid_KEM_PublicKey : public virtual Public_Key { +class BOTAN_TEST_API Hybrid_KEM_PublicKey : public virtual Hybrid_PublicKey { public: static std::unique_ptr load_for_group(Group_Params group, std::span concatenated_public_values); @@ -45,34 +46,18 @@ public: explicit Hybrid_KEM_PublicKey(std::vector> pks); - Hybrid_KEM_PublicKey(Hybrid_KEM_PublicKey&&) = default; - Hybrid_KEM_PublicKey(const Hybrid_KEM_PublicKey&) = delete; - Hybrid_KEM_PublicKey& operator=(Hybrid_KEM_PublicKey&&) = default; - Hybrid_KEM_PublicKey& operator=(const Hybrid_KEM_PublicKey&) = delete; - ~Hybrid_KEM_PublicKey() = default; - std::string algo_name() const override; - size_t estimated_strength() const override; - size_t key_length() const override; - bool check_key(RandomNumberGenerator& rng, bool strong) const override; AlgorithmIdentifier algorithm_identifier() const override; std::vector raw_public_key_bits() const override; std::vector public_key_bits() const override; std::unique_ptr generate_another(RandomNumberGenerator& rng) const final; - bool supports_operation(PublicKeyOperation op) const override; - + // no KDF support std::unique_ptr create_kem_encryption_op( - std::string_view kdf, std::string_view provider = "base") const override; - - const auto& public_keys() const { return m_public_keys; } + std::string_view params, std::string_view provider = "base") const override; protected: - std::vector> m_public_keys; - - private: - size_t m_key_length; - size_t m_estimated_strength; + Hybrid_KEM_PublicKey() = default; }; BOTAN_DIAGNOSTIC_PUSH @@ -82,8 +67,8 @@ * Composes a number of private keys for hybrid key agreement as defined in this * IETF draft: https://datatracker.ietf.org/doc/html/draft-ietf-tls-hybrid-design-04 */ -class BOTAN_TEST_API Hybrid_KEM_PrivateKey final : public Private_Key, - public Hybrid_KEM_PublicKey { +class BOTAN_TEST_API Hybrid_KEM_PrivateKey final : public virtual Hybrid_KEM_PublicKey, + public virtual Hybrid_PrivateKey { public: /** * Generate a hybrid private key for the given TLS code point. @@ -91,25 +76,21 @@ static std::unique_ptr generate_from_group(Group_Params group, RandomNumberGenerator& rng); public: - Hybrid_KEM_PrivateKey(std::vector> private_keys); + explicit Hybrid_KEM_PrivateKey(std::vector> private_keys); - secure_vector private_key_bits() const override; + std::unique_ptr public_key() const override { + return std::make_unique(extract_public_keys(private_keys())); + } - std::unique_ptr public_key() const override; - - bool check_key(RandomNumberGenerator& rng, bool strong) const override; + bool check_key(RandomNumberGenerator& rng, bool strong) const override { + return Hybrid_PrivateKey::check_key(rng, strong); + } + // no KDF support std::unique_ptr create_kem_decryption_op( - RandomNumberGenerator& rng, std::string_view kdf, std::string_view provider = "base") const override; - - const auto& private_keys() const { return m_private_keys; } - - private: - std::vector> m_private_keys; + RandomNumberGenerator& rng, std::string_view params, std::string_view provider = "base") const override; }; -BOTAN_DIAGNOSTIC_POP - } // namespace Botan::TLS #endif diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13_pqc/info.txt botan3-3.12.0+dfsg/src/lib/tls/tls13_pqc/info.txt --- botan3-3.7.1+dfsg/src/lib/tls/tls13_pqc/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13_pqc/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -12,9 +12,10 @@ hybrid_public_key.h -kex_to_kem_adapter.h tls13 +hybrid_kem +kex_to_kem_adapter diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13_pqc/kex_to_kem_adapter.cpp botan3-3.12.0+dfsg/src/lib/tls/tls13_pqc/kex_to_kem_adapter.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls13_pqc/kex_to_kem_adapter.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13_pqc/kex_to_kem_adapter.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,251 +0,0 @@ -/** - * Adapter that allows using a KEX key as a KEM, using an ephemeral - * key in the KEM encapsulation. - * - * (C) 2023 Jack Lloyd - * 2023 Fabian Albert, René Meusel - Rohde & Schwarz Cybersecurity - * - * Botan is released under the Simplified BSD License (see license.txt) - */ - -#include - -#include -#include -#include - -#if defined(BOTAN_HAS_DIFFIE_HELLMAN) - #include - #include -#endif - -#if defined(BOTAN_HAS_ECDH) - #include -#endif - -#if defined(BOTAN_HAS_X25519) - #include -#endif - -#if defined(BOTAN_HAS_X448) - #include -#endif - -namespace Botan::TLS { - -namespace { - -/** - * This helper determines the length of the agreed-upon value depending - * on the key agreement public key's algorithm type. It would be better - * to get this value via PK_Key_Agreement::agreed_value_size(), but - * instantiating a PK_Key_Agreement object requires a PrivateKey object - * which we don't have (yet) in the context this is used. - * - * TODO: Find a way to get this information without duplicating those - * implementation details of the key agreement algorithms. - */ -size_t kex_shared_key_length(const Public_Key& kex_public_key) { - BOTAN_ASSERT_NOMSG(kex_public_key.supports_operation(PublicKeyOperation::KeyAgreement)); - -#if defined(BOTAN_HAS_ECDH) - if(const auto* ecdh = dynamic_cast(&kex_public_key)) { - return ecdh->domain().get_p_bytes(); - } -#endif - -#if defined(BOTAN_HAS_DIFFIE_HELLMAN) - if(const auto* dh = dynamic_cast(&kex_public_key)) { - return dh->group().p_bytes(); - } -#endif - -#if defined(BOTAN_HAS_X25519) - if(const auto* curve = dynamic_cast(&kex_public_key)) { - BOTAN_UNUSED(curve); - return 32; /* TODO: magic number */ - } -#endif - -#if defined(BOTAN_HAS_X448) - if(const auto* curve = dynamic_cast(&kex_public_key)) { - BOTAN_UNUSED(curve); - return 56; /* TODO: magic number */ - } -#endif - - throw Not_Implemented( - fmt("Cannot get shared kex key length from unknown key agreement public key of type '{}' in the hybrid KEM key", - kex_public_key.algo_name())); -} - -/** - * This helper generates an ephemeral key agreement private key given a - * public key instance of a certain key agreement algorithm. - */ -std::unique_ptr generate_key_agreement_private_key(const Public_Key& kex_public_key, - RandomNumberGenerator& rng) { - BOTAN_ASSERT_NOMSG(kex_public_key.supports_operation(PublicKeyOperation::KeyAgreement)); - - auto new_kex_key = [&] { - auto new_private_key = kex_public_key.generate_another(rng); - const auto kex_key = dynamic_cast(new_private_key.get()); - if(kex_key) [[likely]] { - // Intentionally leak new_private_key since we hold an alias of it in kex_key, - // which is captured in a unique_ptr below - // NOLINTNEXTLINE(*-unused-return-value) - (void)new_private_key.release(); - } - return std::unique_ptr(kex_key); - }(); - - BOTAN_ASSERT(new_kex_key, "Keys wrapped in this adapter are always key-agreement keys"); - return new_kex_key; -} - -std::unique_ptr maybe_get_public_key(const std::unique_ptr& private_key) { - BOTAN_ARG_CHECK(private_key != nullptr, "Private key is a nullptr"); - return private_key->public_key(); -} - -class KEX_to_KEM_Adapter_Encryption_Operation final : public PK_Ops::KEM_Encryption_with_KDF { - public: - KEX_to_KEM_Adapter_Encryption_Operation(const Public_Key& key, std::string_view kdf, std::string_view provider) : - PK_Ops::KEM_Encryption_with_KDF(kdf), m_provider(provider), m_public_key(key) {} - - size_t raw_kem_shared_key_length() const override { return kex_shared_key_length(m_public_key); } - - size_t encapsulated_key_length() const override { - // Serializing the public value into a short-lived heap-allocated - // vector is not ideal. - // - // TODO: Find a way to get the public value length without copying - // the public value into a vector. See GH #3706 (point 5). - return m_public_key.raw_public_key_bits().size(); - } - - void raw_kem_encrypt(std::span out_encapsulated_key, - std::span raw_shared_key, - Botan::RandomNumberGenerator& rng) override { - const auto sk = generate_key_agreement_private_key(m_public_key, rng); - const auto shared_key = PK_Key_Agreement(*sk, rng, "Raw", m_provider) - .derive_key(0 /* no KDF */, m_public_key.raw_public_key_bits()) - .bits_of(); - - const auto public_value = sk->public_value(); - - // TODO: perhaps avoid these copies by providing std::span out-params - // for `PK_Key_Agreement::derive_key()` and - // `PK_Key_Agreement_Key::public_value()` - BOTAN_ASSERT_EQUAL(public_value.size(), - out_encapsulated_key.size(), - "KEX-to-KEM Adapter: encapsulated key out-param has correct length"); - BOTAN_ASSERT_EQUAL( - shared_key.size(), raw_shared_key.size(), "KEX-to-KEM Adapter: shared key out-param has correct length"); - std::copy(public_value.begin(), public_value.end(), out_encapsulated_key.begin()); - std::copy(shared_key.begin(), shared_key.end(), raw_shared_key.begin()); - } - - private: - std::string m_provider; - const Public_Key& m_public_key; -}; - -class KEX_to_KEM_Decryption_Operation final : public PK_Ops::KEM_Decryption_with_KDF { - public: - KEX_to_KEM_Decryption_Operation(const PK_Key_Agreement_Key& key, - RandomNumberGenerator& rng, - const std::string_view kdf, - const std::string_view provider) : - PK_Ops::KEM_Decryption_with_KDF(kdf), - m_operation(key, rng, "Raw", provider), - m_encapsulated_key_length(key.public_value().size()) {} - - void raw_kem_decrypt(std::span out_shared_key, std::span encap_key) override { - secure_vector shared_secret = m_operation.derive_key(0 /* no KDF */, encap_key).bits_of(); - BOTAN_ASSERT_EQUAL( - shared_secret.size(), out_shared_key.size(), "KEX-to-KEM Adapter: shared key out-param has correct length"); - std::copy(shared_secret.begin(), shared_secret.end(), out_shared_key.begin()); - } - - size_t encapsulated_key_length() const override { return m_encapsulated_key_length; } - - size_t raw_kem_shared_key_length() const override { return m_operation.agreed_value_size(); } - - private: - PK_Key_Agreement m_operation; - size_t m_encapsulated_key_length; -}; - -} // namespace - -KEX_to_KEM_Adapter_PublicKey::KEX_to_KEM_Adapter_PublicKey(std::unique_ptr public_key) : - m_public_key(std::move(public_key)) { - BOTAN_ARG_CHECK(m_public_key != nullptr, "Public key is a nullptr"); - BOTAN_ARG_CHECK(m_public_key->supports_operation(PublicKeyOperation::KeyAgreement), "Public key is no KEX key"); -} - -std::string KEX_to_KEM_Adapter_PublicKey::algo_name() const { - return fmt("KEX-to-KEM({})", m_public_key->algo_name()); -} - -size_t KEX_to_KEM_Adapter_PublicKey::estimated_strength() const { - return m_public_key->estimated_strength(); -} - -size_t KEX_to_KEM_Adapter_PublicKey::key_length() const { - return m_public_key->key_length(); -} - -bool KEX_to_KEM_Adapter_PublicKey::check_key(RandomNumberGenerator& rng, bool strong) const { - return m_public_key->check_key(rng, strong); -} - -AlgorithmIdentifier KEX_to_KEM_Adapter_PublicKey::algorithm_identifier() const { - return m_public_key->algorithm_identifier(); -} - -std::vector KEX_to_KEM_Adapter_PublicKey::raw_public_key_bits() const { - return m_public_key->raw_public_key_bits(); -} - -std::vector KEX_to_KEM_Adapter_PublicKey::public_key_bits() const { - throw Not_Implemented("The KEX-to-KEM adapter does not support ASN.1-based public key serialization"); -} - -std::unique_ptr KEX_to_KEM_Adapter_PublicKey::generate_another(RandomNumberGenerator& rng) const { - return std::make_unique(generate_key_agreement_private_key(*m_public_key, rng)); -} - -bool KEX_to_KEM_Adapter_PublicKey::supports_operation(PublicKeyOperation op) const { - return op == PublicKeyOperation::KeyEncapsulation; -} - -KEX_to_KEM_Adapter_PrivateKey::KEX_to_KEM_Adapter_PrivateKey(std::unique_ptr private_key) : - KEX_to_KEM_Adapter_PublicKey(maybe_get_public_key(private_key)), m_private_key(std::move(private_key)) { - BOTAN_ARG_CHECK(m_private_key->supports_operation(PublicKeyOperation::KeyAgreement), "Private key is no KEX key"); -} - -secure_vector KEX_to_KEM_Adapter_PrivateKey::private_key_bits() const { - return m_private_key->private_key_bits(); -} - -std::unique_ptr KEX_to_KEM_Adapter_PrivateKey::public_key() const { - return std::make_unique(m_private_key->public_key()); -} - -bool KEX_to_KEM_Adapter_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const { - return m_private_key->check_key(rng, strong); -} - -std::unique_ptr KEX_to_KEM_Adapter_PublicKey::create_kem_encryption_op( - std::string_view kdf, std::string_view provider) const { - return std::make_unique(*m_public_key, kdf, provider); -} - -std::unique_ptr KEX_to_KEM_Adapter_PrivateKey::create_kem_decryption_op( - RandomNumberGenerator& rng, std::string_view kdf, std::string_view provider) const { - return std::make_unique(*m_private_key, rng, kdf, provider); -} - -} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls13_pqc/kex_to_kem_adapter.h botan3-3.12.0+dfsg/src/lib/tls/tls13_pqc/kex_to_kem_adapter.h --- botan3-3.7.1+dfsg/src/lib/tls/tls13_pqc/kex_to_kem_adapter.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls13_pqc/kex_to_kem_adapter.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,89 +0,0 @@ -/** - * Adapter that allows using a KEX key as a KEM, using an ephemeral - * key in the KEM encapsulation. - * - * (C) 2023 Jack Lloyd - * 2023 Fabian Albert, René Meusel - Rohde & Schwarz Cybersecurity - * - * Botan is released under the Simplified BSD License (see license.txt) - */ - -#ifndef BOTAN_TLS_13_KEX_TO_KEM_ADAPTER_H_ -#define BOTAN_TLS_13_KEX_TO_KEM_ADAPTER_H_ - -#include - -#include - -namespace Botan::TLS { - -/** - * Adapter to use a key agreement key pair (e.g. ECDH) as a key encapsulation - * mechanism. - */ -class BOTAN_TEST_API KEX_to_KEM_Adapter_PublicKey : public virtual Public_Key { - public: - KEX_to_KEM_Adapter_PublicKey(std::unique_ptr public_key); - - std::string algo_name() const override; - size_t estimated_strength() const override; - size_t key_length() const override; - bool check_key(RandomNumberGenerator& rng, bool strong) const override; - AlgorithmIdentifier algorithm_identifier() const override; - std::vector raw_public_key_bits() const override; - std::vector public_key_bits() const override; - std::unique_ptr generate_another(RandomNumberGenerator& rng) const final; - - bool supports_operation(PublicKeyOperation op) const override; - - std::unique_ptr create_kem_encryption_op( - std::string_view kdf, std::string_view provider = "base") const override; - - private: - std::unique_ptr m_public_key; -}; - -BOTAN_DIAGNOSTIC_PUSH -BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE - -/** - * Adapter to use a key agreement key pair (e.g. ECDH) as a key encapsulation - * mechanism. This works by generating an ephemeral key pair during the - * encapsulation. - * - * The abstract interface of a key exchange mechanism (KEX) is mapped like so: - * - * * KEM-generate(rng) -> tuple[PublicKey, PrivateKey] - * => KEX-generate(rng) -> tuple[PublicKey, PrivateKey] - * - * * KEM-encapsulate(PublicKey, rng) -> tuple[SharedSecret, EncapsulatedSharedSecret] - * => eph_pk, eph_sk = KEX-generate(rng) - * secret = KEX-agree(eph_sk, PublicKey) - * [secret, eph_pk] - * - * * KEM-decapsulate(PrivateKey, EncapsulatedSharedSecret) -> SharedSecret - * => KEX-agree(PrivateKey, EncapsulatedSharedSecret) - */ -class BOTAN_TEST_API KEX_to_KEM_Adapter_PrivateKey final : public KEX_to_KEM_Adapter_PublicKey, - public virtual Private_Key { - public: - KEX_to_KEM_Adapter_PrivateKey(std::unique_ptr private_key); - - secure_vector private_key_bits() const override; - - std::unique_ptr public_key() const override; - - bool check_key(RandomNumberGenerator& rng, bool strong) const override; - - std::unique_ptr create_kem_decryption_op( - RandomNumberGenerator& rng, std::string_view kdf, std::string_view provider = "base") const override; - - private: - std::unique_ptr m_private_key; -}; - -BOTAN_DIAGNOSTIC_POP - -} // namespace Botan::TLS - -#endif diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_alert.h botan3-3.12.0+dfsg/src/lib/tls/tls_alert.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_alert.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_alert.h 2026-05-07 01:38:28.000000000 +0000 @@ -18,7 +18,7 @@ * * The enumeration value matches the wire encoding */ -enum class AlertType { +enum class AlertType : uint16_t { CloseNotify = 0, UnexpectedMessage = 10, BadRecordMac = 20, @@ -58,6 +58,7 @@ None = 256, // Compat enum variants, will be removed in a future major release + // TODO(Botan4): remove these CLOSE_NOTIFY BOTAN_DEPRECATED("Use CloseNotify") = CloseNotify, NO_APPLICATION_PROTOCOL BOTAN_DEPRECATED("Use NoApplicationProtocol") = NoApplicationProtocol, PROTOCOL_VERSION BOTAN_DEPRECATED("Use ProtocolVersion") = ProtocolVersion, @@ -121,7 +122,8 @@ * @param type_code the type of alert * @param fatal specifies if this is a fatal alert */ - Alert(Type type_code, bool fatal = false) : m_fatal(fatal), m_type_code(type_code) {} + Alert(Type type_code, bool fatal = false) : // NOLINT(*-explicit-conversions) + m_fatal(fatal), m_type_code(type_code) {} Alert() : m_fatal(false), m_type_code(AlertType::None) {} diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_algos.cpp botan3-3.12.0+dfsg/src/lib/tls/tls_algos.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls_algos.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_algos.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,10 +6,12 @@ #include -#include #include #include +#include +#include + namespace Botan::TLS { std::string kdf_algo_to_string(KDF_Algo algo) { @@ -134,43 +136,108 @@ throw Invalid_Argument(fmt("Unknown TLS signature method '{}'", str)); } -bool Group_Params::is_available() const { -#if !defined(BOTAN_HAS_X25519) - if(is_x25519()) { - return false; - } - if(is_pqc_hybrid() && pqc_hybrid_ecc() == Group_Params_Code::X25519) { - return false; - } +namespace { + +consteval auto available_group_params() { + auto codes = std::array { +#if defined(BOTAN_HAS_PCURVES_SECP256R1) || defined(BOTAN_HAS_PCURVES_GENERIC) + Group_Params_Code::SECP256R1, #endif -#if !defined(BOTAN_HAS_X448) - if(is_x448()) { - return false; - } - if(is_pqc_hybrid() && pqc_hybrid_ecc() == Group_Params_Code::X448) { - return false; - } +#if defined(BOTAN_HAS_PCURVES_SECP384R1) || defined(BOTAN_HAS_PCURVES_GENERIC) + Group_Params_Code::SECP384R1, #endif -#if !defined(BOTAN_HAS_DIFFIE_HELLMAN) - if(is_in_ffdhe_range()) { - return false; - } +#if defined(BOTAN_HAS_PCURVES_SECP521R1) || defined(BOTAN_HAS_PCURVES_GENERIC) + Group_Params_Code::SECP521R1, #endif -#if !defined(BOTAN_HAS_ML_KEM) - if(is_pure_ml_kem() || is_pqc_hybrid_ml_kem()) { - return false; - } +#if defined(BOTAN_HAS_PCURVES_BRAINPOOL256R1) || defined(BOTAN_HAS_PCURVES_GENERIC) + Group_Params_Code::BRAINPOOL256R1, #endif -#if !defined(BOTAN_HAS_FRODOKEM) - if(is_pure_frodokem() || is_pqc_hybrid_frodokem()) { - return false; - } +#if defined(BOTAN_HAS_PCURVES_BRAINPOOL384R1) || defined(BOTAN_HAS_PCURVES_GENERIC) + Group_Params_Code::BRAINPOOL384R1, #endif +#if defined(BOTAN_HAS_PCURVES_BRAINPOOL512R1) || defined(BOTAN_HAS_PCURVES_GENERIC) + Group_Params_Code::BRAINPOOL512R1, +#endif + +#if defined(BOTAN_HAS_X25519) + Group_Params_Code::X25519, +#endif + +#if defined(BOTAN_HAS_X448) + Group_Params_Code::X448, +#endif + +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) + Group_Params_Code::FFDHE_2048, Group_Params_Code::FFDHE_3072, Group_Params_Code::FFDHE_4096, + Group_Params_Code::FFDHE_6144, Group_Params_Code::FFDHE_8192, +#endif + +#if defined(BOTAN_HAS_ML_KEM) + Group_Params_Code::ML_KEM_512, Group_Params_Code::ML_KEM_768, Group_Params_Code::ML_KEM_1024, + + #if defined(BOTAN_HAS_PCURVES_SECP256R1) || defined(BOTAN_HAS_PCURVES_GENERIC) + Group_Params_Code::HYBRID_SECP256R1_ML_KEM_768, + #endif + + #if defined(BOTAN_HAS_PCURVES_SECP384R1) || defined(BOTAN_HAS_PCURVES_GENERIC) + Group_Params_Code::HYBRID_SECP384R1_ML_KEM_1024, + #endif + + #if defined(BOTAN_HAS_X25519) + Group_Params_Code::HYBRID_X25519_ML_KEM_768, + #endif +#endif + +#if defined(BOTAN_HAS_FRODOKEM) + Group_Params_Code::eFRODOKEM_640_SHAKE_OQS, Group_Params_Code::eFRODOKEM_976_SHAKE_OQS, + Group_Params_Code::eFRODOKEM_1344_SHAKE_OQS, Group_Params_Code::eFRODOKEM_640_AES_OQS, + Group_Params_Code::eFRODOKEM_976_AES_OQS, Group_Params_Code::eFRODOKEM_1344_AES_OQS, + + #if defined(BOTAN_HAS_PCURVES_SECP256R1) || defined(BOTAN_HAS_PCURVES_GENERIC) + Group_Params_Code::HYBRID_SECP256R1_eFRODOKEM_640_SHAKE_OQS, + Group_Params_Code::HYBRID_SECP256R1_eFRODOKEM_640_AES_OQS, + #endif + + #if defined(BOTAN_HAS_PCURVES_SECP384R1) || defined(BOTAN_HAS_PCURVES_GENERIC) + Group_Params_Code::HYBRID_SECP384R1_eFRODOKEM_976_SHAKE_OQS, + Group_Params_Code::HYBRID_SECP384R1_eFRODOKEM_976_AES_OQS, + #endif + + #if defined(BOTAN_HAS_PCURVES_SECP521R1) || defined(BOTAN_HAS_PCURVES_GENERIC) + Group_Params_Code::HYBRID_SECP521R1_eFRODOKEM_1344_SHAKE_OQS, + Group_Params_Code::HYBRID_SECP521R1_eFRODOKEM_1344_AES_OQS, + #endif + + #if defined(BOTAN_HAS_X25519) + Group_Params_Code::HYBRID_X25519_eFRODOKEM_640_SHAKE_OQS, + Group_Params_Code::HYBRID_X25519_eFRODOKEM_640_AES_OQS, + #endif + + #if defined(BOTAN_HAS_X448) + Group_Params_Code::HYBRID_X448_eFRODOKEM_976_SHAKE_OQS, Group_Params_Code::HYBRID_X448_eFRODOKEM_976_AES_OQS, + #endif +#endif + }; + + std::sort(codes.begin(), codes.end()); + + return codes; +} + +} // namespace + +bool Group_Params::is_available() const { + // For group codes we recognize, check the build-time availability table. + // Unknown codes may be user-supplied custom groups handled via callbacks. + if(to_string().has_value()) { + static constexpr auto codes = available_group_params(); + return std::binary_search(codes.begin(), codes.end(), this->code()); + } return true; } @@ -191,6 +258,7 @@ case Group_Params_Code::HYBRID_SECP256R1_eFRODOKEM_640_AES_OQS: return Group_Params_Code::SECP256R1; + case Group_Params_Code::HYBRID_SECP384R1_ML_KEM_1024: case Group_Params_Code::HYBRID_SECP384R1_eFRODOKEM_976_SHAKE_OQS: case Group_Params_Code::HYBRID_SECP384R1_eFRODOKEM_976_AES_OQS: return Group_Params_Code::SECP384R1; @@ -281,6 +349,9 @@ if(group_name == "secp256r1/ML-KEM-768") { return Group_Params::HYBRID_SECP256R1_ML_KEM_768; } + if(group_name == "secp384r1/ML-KEM-1024") { + return Group_Params::HYBRID_SECP384R1_ML_KEM_1024; + } if(group_name == "x25519/eFrodoKEM-640-SHAKE") { return Group_Params::HYBRID_X25519_eFRODOKEM_640_SHAKE_OQS; @@ -394,10 +465,33 @@ return "x25519/ML-KEM-768"; case Group_Params::HYBRID_SECP256R1_ML_KEM_768: return "secp256r1/ML-KEM-768"; + case Group_Params::HYBRID_SECP384R1_ML_KEM_1024: + return "secp384r1/ML-KEM-1024"; default: return std::nullopt; } } +std::string certificate_type_to_string(Certificate_Type type) { + switch(type) { + case Certificate_Type::X509: + return "X509"; + case Certificate_Type::RawPublicKey: + return "RawPublicKey"; + } + + return "Unknown"; +} + +Certificate_Type certificate_type_from_string(const std::string& type_str) { + if(type_str == "X509") { + return Certificate_Type::X509; + } else if(type_str == "RawPublicKey") { + return Certificate_Type::RawPublicKey; + } else { + throw Decoding_Error("Unknown certificate type: " + type_str); + } +} + } // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_algos.h botan3-3.12.0+dfsg/src/lib/tls/tls_algos.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_algos.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_algos.h 2026-05-07 01:38:28.000000000 +0000 @@ -7,18 +7,15 @@ #ifndef BOTAN_TLS_ALGO_IDS_H_ #define BOTAN_TLS_ALGO_IDS_H_ -#include -#include #include #include #include -#include //BOTAN_FUTURE_INTERNAL_HEADER(tls_algos.h) namespace Botan::TLS { -enum class Cipher_Algo { +enum class Cipher_Algo : uint8_t { CHACHA20_POLY1305, AES_128_GCM, @@ -46,7 +43,7 @@ DES_EDE_CBC_HMAC_SHA1, }; -enum class KDF_Algo { +enum class KDF_Algo : uint8_t { SHA_1, SHA_256, SHA_384, @@ -54,20 +51,21 @@ std::string BOTAN_DLL kdf_algo_to_string(KDF_Algo algo); -enum class Nonce_Format { +enum class Nonce_Format : uint8_t { CBC_MODE, AEAD_IMPLICIT_4, AEAD_XOR_12, + NULL_CIPHER, }; // TODO encoding should match signature_algorithms extension // TODO this should include hash etc as in TLS v1.3 -enum class Auth_Method { - RSA, - ECDSA, +enum class Auth_Method : uint32_t { + RSA = 0, + ECDSA = 1, // To support TLS 1.3 ciphersuites, which do not determine the auth method - UNDEFINED, + UNDEFINED = 2, // These are placed outside the encodable range IMPLICIT = 0x10000 @@ -105,32 +103,35 @@ // libOQS defines those in: // https://github.com/open-quantum-safe/oqs-provider/blob/main/ALGORITHMS.md - eFRODOKEM_640_SHAKE_OQS = 0xFE01, - eFRODOKEM_976_SHAKE_OQS = 0x0203, - eFRODOKEM_1344_SHAKE_OQS = 0x0205, + // (last update: 6th June 2025 - matching oqs commit 9447f68) + eFRODOKEM_640_SHAKE_OQS = 0xFE03, + eFRODOKEM_976_SHAKE_OQS = 0xFE09, + eFRODOKEM_1344_SHAKE_OQS = 0xFE0E, eFRODOKEM_640_AES_OQS = 0xFE00, - eFRODOKEM_976_AES_OQS = 0xFE02, - eFRODOKEM_1344_AES_OQS = 0x0204, + eFRODOKEM_976_AES_OQS = 0xFE06, + eFRODOKEM_1344_AES_OQS = 0xFE0C, // https://datatracker.ietf.org/doc/draft-kwiatkowski-tls-ecdhe-mlkem/03/ HYBRID_SECP256R1_ML_KEM_768 = 0x11EB, + HYBRID_SECP384R1_ML_KEM_1024 = 0x11ED, HYBRID_X25519_ML_KEM_768 = 0x11EC, // https://github.com/open-quantum-safe/oqs-provider/blob/main/ALGORITHMS.md - HYBRID_X25519_eFRODOKEM_640_SHAKE_OQS = 0x2F81, - HYBRID_X25519_eFRODOKEM_640_AES_OQS = 0x2F80, + // (last update: 6th June 2025 - matching oqs commit 9447f68) + HYBRID_X25519_eFRODOKEM_640_SHAKE_OQS = 0xFE05, + HYBRID_X25519_eFRODOKEM_640_AES_OQS = 0xFE02, - HYBRID_X448_eFRODOKEM_976_SHAKE_OQS = 0x2F83, - HYBRID_X448_eFRODOKEM_976_AES_OQS = 0x2F82, + HYBRID_X448_eFRODOKEM_976_SHAKE_OQS = 0xFE0B, + HYBRID_X448_eFRODOKEM_976_AES_OQS = 0xFE08, - HYBRID_SECP256R1_eFRODOKEM_640_SHAKE_OQS = 0x2F01, - HYBRID_SECP256R1_eFRODOKEM_640_AES_OQS = 0x2F00, + HYBRID_SECP256R1_eFRODOKEM_640_SHAKE_OQS = 0xFE04, + HYBRID_SECP256R1_eFRODOKEM_640_AES_OQS = 0xFE01, - HYBRID_SECP384R1_eFRODOKEM_976_SHAKE_OQS = 0x2F03, - HYBRID_SECP384R1_eFRODOKEM_976_AES_OQS = 0x2F02, + HYBRID_SECP384R1_eFRODOKEM_976_SHAKE_OQS = 0xFE0A, + HYBRID_SECP384R1_eFRODOKEM_976_AES_OQS = 0xFE07, - HYBRID_SECP521R1_eFRODOKEM_1344_SHAKE_OQS = 0x2F05, - HYBRID_SECP521R1_eFRODOKEM_1344_AES_OQS = 0x2F04, + HYBRID_SECP521R1_eFRODOKEM_1344_SHAKE_OQS = 0xFE0F, + HYBRID_SECP521R1_eFRODOKEM_1344_AES_OQS = 0xFE0D, }; class BOTAN_PUBLIC_API(3, 2) Group_Params final { @@ -139,8 +140,10 @@ constexpr Group_Params() : m_code(Group_Params_Code::NONE) {} + // NOLINTNEXTLINE(*-explicit-conversions) constexpr Group_Params(Group_Params_Code code) : m_code(code) {} + // NOLINTNEXTLINE(*-explicit-conversions) constexpr Group_Params(uint16_t code) : m_code(static_cast(code)) {} /** @@ -211,6 +214,7 @@ constexpr bool is_pqc_hybrid_ml_kem() const { return m_code == Group_Params_Code::HYBRID_SECP256R1_ML_KEM_768 || + m_code == Group_Params_Code::HYBRID_SECP384R1_ML_KEM_1024 || m_code == Group_Params_Code::HYBRID_X25519_ML_KEM_768; } @@ -248,7 +252,7 @@ Group_Params_Code m_code; }; -enum class Kex_Algo { +enum class Kex_Algo : uint8_t { STATIC_RSA, DH, ECDH, @@ -271,6 +275,12 @@ return (m == Kex_Algo::PSK || m == Kex_Algo::ECDHE_PSK || m == Kex_Algo::DHE_PSK); } +// As defined in RFC 8446 4.4.2 +enum class Certificate_Type : uint8_t { X509 = 0, RawPublicKey = 2 }; + +std::string certificate_type_to_string(Certificate_Type type); +Certificate_Type certificate_type_from_string(const std::string& type_str); + } // namespace Botan::TLS #endif diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_callbacks.cpp botan3-3.12.0+dfsg/src/lib/tls/tls_callbacks.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls_callbacks.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_callbacks.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,14 +12,16 @@ #include #include +#include #include #include #include #include #include #include +#include #include -#include +#include #include #if defined(BOTAN_HAS_X25519) @@ -93,17 +95,24 @@ throw Invalid_Argument("Certificate chain was empty"); } - Path_Validation_Restrictions restrictions(policy.require_cert_revocation_info(), - policy.minimum_signature_strength()); + const Path_Validation_Restrictions restrictions(policy.require_cert_revocation_info(), + policy.minimum_signature_strength()); - Path_Validation_Result result = x509_path_validate(cert_chain, - restrictions, - trusted_roots, - hostname, - usage, - tls_current_timestamp(), - tls_verify_cert_chain_ocsp_timeout(), - ocsp_responses); + /* + Hostname is always provided in order to allow host-specific logic if required, + but it should not be passed to x509_path_validate unless we are verifying + the server. + */ + const std::string_view name_to_match = (usage == Usage_Type::TLS_CLIENT_AUTH) ? std::string_view{} : hostname; + + const Path_Validation_Result result = x509_path_validate(cert_chain, + restrictions, + trusted_roots, + name_to_match, + usage, + tls_current_timestamp(), + tls_verify_cert_chain_ocsp_timeout(), + ocsp_responses); if(!result.successful_validation()) { throw TLS_Exception(Alert::BadCertificate, "Certificate validation failure: " + result.result_string()); @@ -277,6 +286,8 @@ // This exception means that the public key was invalid. However, // TLS' DecodeError would imply that a protocol message was invalid. throw TLS_Exception(Alert::IllegalParameter, ex.what()); + } catch(const Invalid_Argument& ex) { + throw TLS_Exception(Alert::IllegalParameter, ex.what()); } }(); @@ -285,6 +296,8 @@ try { return PK_KEM_Encryptor(*kem_pub_key, "Raw").encrypt(rng); + } catch(const Decoding_Error& ex) { + throw TLS_Exception(Alert::IllegalParameter, ex.what()); } catch(const Invalid_Argument& ex) { throw TLS_Exception(Alert::IllegalParameter, ex.what()); } @@ -309,11 +322,17 @@ if(encapsulated_bytes.size() != kemdec.encapsulated_key_length()) { throw TLS_Exception(Alert::IllegalParameter, "Invalid encapsulated key length"); } - return kemdec.decrypt(encapsulated_bytes, 0, {}); + try { + return kemdec.decrypt(encapsulated_bytes, 0, {}); + } catch(const Decoding_Error& ex) { + throw TLS_Exception(Alert::IllegalParameter, ex.what()); + } catch(const Invalid_Argument& ex) { + throw TLS_Exception(Alert::IllegalParameter, ex.what()); + } } try { - auto& key_agreement_key = dynamic_cast(private_key); + const auto& key_agreement_key = dynamic_cast(private_key); return tls_ephemeral_key_agreement(group, key_agreement_key, encapsulated_bytes, rng, policy); } catch(const std::bad_cast&) { throw Invalid_Argument("provided ephemeral key is not a PK_Key_Agreement_Key"); @@ -354,6 +373,25 @@ throw TLS_Exception(Alert::DecodeError, "cannot create a key offering without a group definition"); } +std::unique_ptr TLS::Callbacks::tls12_generate_ephemeral_ecdh_key( + TLS::Group_Params group, RandomNumberGenerator& rng, EC_Point_Format tls12_ecc_pubkey_encoding_format) { + // Delegating to the "universal" callback to obtain an ECDH key pair + auto key = tls_generate_ephemeral_key(group, rng); + + // For ordinary ECDH key pairs (that are derived from `ECDH_PublicKey`), we + // set the internal point encoding flag for the key before passing it on into + // the TLS 1.2 implementation. For user-defined keypair types (e.g. to + // offload to some crypto hardware) inheriting from Botan's `ECDH_PublicKey` + // might not be feasible. Such users should consider overriding this + // ECDH-specific callback and ensure that their custom class handles the + // public point encoding as requested by `tls12_ecc_pubkey_encoding_format`. + if(auto* ecc_key = dynamic_cast(key.get())) { + ecc_key->set_point_encoding(tls12_ecc_pubkey_encoding_format); + } + + return key; +} + secure_vector TLS::Callbacks::tls_ephemeral_key_agreement( const std::variant& group, const PK_Key_Agreement_Key& private_key, @@ -367,6 +405,8 @@ // This exception means that the public key was invalid. However, // TLS' DecodeError would imply that a protocol message was invalid. throw TLS_Exception(Alert::IllegalParameter, ex.what()); + } catch(const Invalid_Argument& ex) { + throw TLS_Exception(Alert::IllegalParameter, ex.what()); } }(); @@ -381,11 +421,47 @@ // This is done within the key agreement operation and throws // an Invalid_Argument exception if the shared secret is all-zero. try { - PK_Key_Agreement ka(private_key, rng, "Raw"); + const PK_Key_Agreement ka(private_key, rng, "Raw"); return ka.derive_key(0, kex_pub_key->raw_public_key_bits()).bits_of(); } catch(const Invalid_Argument& ex) { throw TLS_Exception(Alert::IllegalParameter, ex.what()); } } +void TLS::Callbacks::tls_session_established(const Session_Summary& session) { + BOTAN_UNUSED(session); +} + +std::vector TLS::Callbacks::tls_provide_cert_status(const std::vector& chain, + const Certificate_Status_Request& csr) { + BOTAN_UNUSED(chain, csr); + return std::vector(); +} + +void TLS::Callbacks::tls_log_error(const char* err) { + BOTAN_UNUSED(err); +} + +void TLS::Callbacks::tls_log_debug(const char* what) { + BOTAN_UNUSED(what); +} + +void TLS::Callbacks::tls_log_debug_bin(const char* descr, const uint8_t val[], size_t val_len) { + BOTAN_UNUSED(descr, val, val_len); +} + +void TLS::Callbacks::tls_ssl_key_log_data(std::string_view label, + std::span client_random, + std::span secret) const { + BOTAN_UNUSED(label, client_random, secret); +} + +std::unique_ptr TLS::Callbacks::tls12_protocol_specific_kdf(std::string_view prf_algo) const { + if(prf_algo == "MD5" || prf_algo == "SHA-1") { + return KDF::create_or_throw("TLS-12-PRF(SHA-256)"); + } + + return KDF::create_or_throw(Botan::fmt("TLS-12-PRF({})", prf_algo)); +} + } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_callbacks.h botan3-3.12.0+dfsg/src/lib/tls/tls_callbacks.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_callbacks.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_callbacks.h 2026-05-07 01:38:28.000000000 +0000 @@ -4,6 +4,7 @@ * 2016 Jack Lloyd * 2017 Harry Reimann, Rohde & Schwarz Cybersecurity * 2022 René Meusel, Rohde & Schwarz Cybersecurity +* 2025 Frederik Dornemann, CARIAD SE * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -11,18 +12,28 @@ #ifndef BOTAN_TLS_CALLBACKS_H_ #define BOTAN_TLS_CALLBACKS_H_ -#include -#include +#include +#include #include #include -#include +#include +#include #include +#include #include +#include namespace Botan { +enum class Signature_Format : uint8_t; +enum class Usage_Type : uint8_t; +class DL_Group; +class PK_Key_Agreement_Key; class Certificate_Store; class X509_Certificate; +class Public_Key; +class Private_Key; +class RandomNumberGenerator; namespace OCSP { @@ -36,12 +47,14 @@ class Policy; class Extensions; class Certificate_Status_Request; +class Session_Summary; +class Session; /** * Encapsulates the callbacks that a TLS channel will make which are due to * channel specific operations. */ -class BOTAN_PUBLIC_API(2, 0) Callbacks { +class BOTAN_PUBLIC_API(2, 0) Callbacks /* NOLINT(*-special-member-functions) */ { public: virtual ~Callbacks() = default; @@ -128,7 +141,7 @@ * * @param session the session descriptor */ - virtual void tls_session_established(const Session_Summary& session) { BOTAN_UNUSED(session); } + virtual void tls_session_established(const Session_Summary& session); /** * Optional callback: session activated @@ -256,6 +269,8 @@ * * This function should not be "const" since the implementation might need * to perform some side effecting operation to compute the result. + * + * TODO(Botan4) change return type to uint64_t */ virtual std::chrono::milliseconds tls_verify_cert_chain_ocsp_timeout() const { return std::chrono::milliseconds(0); @@ -270,13 +285,11 @@ * indicates the revocation status of the server certificate. Return an * empty vector to indicate that no response is available, and thus * suppress the Certificate_Status message. + * + * Default implementation returns an empty vector, disabling certificate status */ virtual std::vector tls_provide_cert_status(const std::vector& chain, - const Certificate_Status_Request& csr) { - BOTAN_UNUSED(chain); - BOTAN_UNUSED(csr); - return std::vector(); - } + const Certificate_Status_Request& csr); /** * Called by TLS 1.3 client or server whenever the peer indicated that @@ -343,7 +356,7 @@ * * If deserialization fails, the default implementation throws a * Botan::Decoding_Error exception that will be translated into a - * TLS_Exception with an Alert::IllegalParamter. + * TLS_Exception with an Alert::IllegalParameter. * * @param group the group identifier or (in case of TLS 1.2) an explicit * discrete-log group of the public key @@ -404,6 +417,10 @@ * * @returns the shared secret both in plaintext and encapsulated with * @p encoded_public_key. + * + * TODO(Botan4) change this return type to something else so the pubkey.h + * dependency is removed + * TODO(Botan4) change encoded_public_key to a span */ virtual KEM_Encapsulation tls_kem_encapsulate(TLS::Group_Params group, const std::vector& encoded_public_key, @@ -434,6 +451,8 @@ * * @returns the plaintext shared secret from @p encapsulated_bytes after * decapsulation with @p private_key. + * + * TODO(Botan4) change encapsulated_bytes to a std::span */ virtual secure_vector tls_kem_decapsulate(TLS::Group_Params group, const Private_Key& private_key, @@ -466,6 +485,36 @@ const std::variant& group, RandomNumberGenerator& rng); /** + * Generate an ECDH key pair for the TLS 1.2 handshake. + * + * Note that this callback is called exclusively by TLS 1.2 to handle the + * ECDH public key serialization format explicitly. TLS 1.3 fixes this + * format to 'uncompressed' and does not allow negotiating anything else. + * X25519 and X448 feature a defined and fixed public key encoding and are + * therefore not explicitly handled by this callback either. + * + * Users may override this if they want to provide a custom keypair type + * to offload TLS 1.2's ECDH handling to custom hardware, for instance. It + * is worth noting that support for compressed points in Botan is + * deprecated and this callback will disappear when it is removed in a + * future release. + * + * Typical use cases of the library don't need to do that and serious + * security risks are associated with customizing TLS's key exchange + * mechanism. + * + * @throws TLS_Exception(Alert::DecodeError) if the @p group is not known. + * + * @param group ECDH group identifier to generate an ephemeral keypair for + * @param rng a random number generator + * @param tls12_ecc_pubkey_encoding_format the key's serialization format + * + * @return an ECDH private key of an algorithm usable for key agreement + */ + virtual std::unique_ptr tls12_generate_ephemeral_ecdh_key( + TLS::Group_Params group, RandomNumberGenerator& rng, EC_Point_Format tls12_ecc_pubkey_encoding_format); + + /** * Agree on a shared secret with the peer's ephemeral public key for * the TLS handshake. * @@ -488,6 +537,8 @@ * @param policy a TLS policy object * * @return the shared secret derived from public_value and private_key + * + * TODO(Botan4) change public_value to a std::span */ virtual secure_vector tls_ephemeral_key_agreement(const std::variant& group, const PK_Key_Agreement_Key& private_key, @@ -581,6 +632,8 @@ * * @param raw_response raw OCSP response buffer * @returns the parsed OCSP response or std::nullopt on error + * + * TODO(Botan4) change raw_response to a std::span */ virtual std::optional tls_parse_ocsp_response(const std::vector& raw_response); @@ -604,30 +657,36 @@ * * Note that typical usages will not need to override this callback but it * is useful for testing purposes to allow for deterministic test outcomes. + * + * TODO(Botan4) change return type to uint64_t */ virtual std::chrono::system_clock::time_point tls_current_timestamp(); /** * Optional callback: error logging. (not currently called) * @param err An error message related to this connection. + * + * TODO(Botan4) remove this */ - virtual void tls_log_error(const char* err) { BOTAN_UNUSED(err); } + virtual void tls_log_error(const char* err); /** * Optional callback: debug logging. (not currently called) * @param what Some hopefully informative string + * + * TODO(Botan4) remove this */ - virtual void tls_log_debug(const char* what) { BOTAN_UNUSED(what); } + virtual void tls_log_debug(const char* what); /** * Optional callback: debug logging taking a buffer. (not currently called) * @param descr What this buffer is * @param val the bytes * @param val_len length of val + * + * TODO(Botan4) remove this */ - virtual void tls_log_debug_bin(const char* descr, const uint8_t val[], size_t val_len) { - BOTAN_UNUSED(descr, val, val_len); - } + virtual void tls_log_debug_bin(const char* descr, const uint8_t val[], size_t val_len); /** * Optional callback: Allows access to a connection's secret data @@ -647,9 +706,21 @@ */ virtual void tls_ssl_key_log_data(std::string_view label, std::span client_random, - std::span secret) const { - BOTAN_UNUSED(label, client_random, secret); - } + std::span secret) const; + + /** + * Returns the key derivation function to be used for TLS 1.2 + * + * The default implementation can be overridden to provide a user-defined + * key derivation function, for example to delegate key derivation to a + * hardware-protected environment when a pre-shared key must remain + * inaccessible to the non-secure world. + * + * @param prf_algo name of the hash function (e.g. "SHA-256") + * + * @return TLS 1.2 KDF implementation + */ + virtual std::unique_ptr tls12_protocol_specific_kdf(std::string_view prf_algo) const; }; } // namespace TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_channel.h botan3-3.12.0+dfsg/src/lib/tls/tls_channel.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_channel.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_channel.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,17 +11,22 @@ #ifndef BOTAN_TLS_CHANNEL_H_ #define BOTAN_TLS_CHANNEL_H_ +#include #include -#include -#include -#include -#include - +#include +#include #include #include #include #include +namespace Botan { + +class Public_Key; +class X509_Certificate; + +} // namespace Botan + namespace Botan::TLS { /** @@ -33,7 +38,14 @@ virtual ~Channel() = default; + Channel(const Channel& other) = delete; + Channel(Channel&& other) = default; + Channel& operator=(const Channel& other) = delete; + Channel& operator=(Channel&& other) = delete; + protected: + Channel() = default; + virtual size_t from_peer(std::span data) = 0; virtual void to_peer(std::span data) = 0; @@ -59,7 +71,7 @@ * Inject plaintext intended for counterparty * Throws an exception if is_active() is false */ - void send(std::string_view val) { this->send(std::span(cast_char_ptr_to_uint8(val.data()), val.size())); } + void send(std::string_view s) { this->send({reinterpret_cast(s.data()), s.size()}); } /** * Inject plaintext intended for counterparty diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_channel_impl.h botan3-3.12.0+dfsg/src/lib/tls/tls_channel_impl.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_channel_impl.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_channel_impl.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,10 +11,12 @@ #ifndef BOTAN_TLS_CHANNEL_IMPL_H_ #define BOTAN_TLS_CHANNEL_IMPL_H_ +#include #include #include +#include // TODO remove this dep +#include #include - #include #include #include @@ -29,21 +31,15 @@ class Client; class Server; -enum class Record_Type : uint8_t { - Invalid = 0, // RFC 8446 (TLS 1.3) - - ChangeCipherSpec = 20, - Alert = 21, - Handshake = 22, - ApplicationData = 23, - - Heartbeat = 24, // RFC 6520 (TLS 1.3) -}; - class Channel_Impl { public: virtual ~Channel_Impl() = default; + Channel_Impl(const Channel_Impl& other) = delete; + Channel_Impl(Channel_Impl&& other) = default; + Channel_Impl& operator=(const Channel_Impl& other) = delete; + Channel_Impl& operator=(Channel_Impl&& other) = delete; + /** * Inject TLS traffic received from counterparty * @return a hint as the how many more bytes we need to q the @@ -186,6 +182,8 @@ virtual std::string application_protocol() const = 0; protected: + Channel_Impl() = default; + /** * This struct collect all information required to perform a downgrade from TLS 1.3 to TLS 1.2. * @@ -221,7 +219,7 @@ bool will_downgrade; }; - std::unique_ptr m_downgrade_info; + std::unique_ptr m_downgrade_info; // NOLINT(*non-private-member-variable*) void preserve_peer_transcript(std::span input) { BOTAN_STATE_CHECK(m_downgrade_info); @@ -244,7 +242,7 @@ /** * Implementations use this to signal that the peer indicated a protocol * version downgrade. After calling `request_downgrade()` no further - * state changes must be perfomed by the implementation. Particularly, no + * state changes must be performed by the implementation. Particularly, no * further handshake messages must be emitted. Instead, they must yield * control flow back to the underlying Channel implementation to perform * the protocol version downgrade. diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_ciphersuite.cpp botan3-3.12.0+dfsg/src/lib/tls/tls_ciphersuite.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls_ciphersuite.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_ciphersuite.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,28 +7,22 @@ #include -#include +#include #include -#include -#include -#include #include namespace Botan::TLS { size_t Ciphersuite::nonce_bytes_from_handshake() const { switch(m_nonce_format) { - case Nonce_Format::CBC_MODE: { - if(cipher_algo() == "3DES") { - return 8; - } else { - return 16; - } - } + case Nonce_Format::CBC_MODE: + return 0; case Nonce_Format::AEAD_IMPLICIT_4: return 4; case Nonce_Format::AEAD_XOR_12: return 12; + case Nonce_Format::NULL_CIPHER: + return 0; } throw Invalid_State("In Ciphersuite::nonce_bytes_from_handshake invalid enum value"); @@ -42,6 +36,7 @@ case Nonce_Format::AEAD_IMPLICIT_4: return 8; case Nonce_Format::AEAD_XOR_12: + case Nonce_Format::NULL_CIPHER: return 0; } @@ -49,6 +44,24 @@ } bool Ciphersuite::is_scsv(uint16_t suite) { + // Both signaling cipher suite values - skip them when iterating + // negotiable ciphersuites. The two callers are: + // + // - 0x00FF: TLS_EMPTY_RENEGOTIATION_INFO_SCSV (RFC 5746). Consumed by + // Client_Hello_12::Client_Hello_12 to set secure_renegotiation when + // the renegotiation_info extension is absent. + // + // - 0x5600: TLS_FALLBACK_SCSV (RFC 7507). Recognized so it is filtered + // out of negotiation, but the inappropriate_fallback enforcement is + // intentionally not implemented: + // * Botan does not support TLS 1.0 / 1.1, so the 1.2 -> 1.0/1.1 + // fallback that SCSV was originally designed to detect cannot + // occur here. + // * The 1.3 -> 1.2 downgrade is already protected by the + // ServerHello.random sentinel (RFC 8446 4.1.3, DOWNGRADE_TLS12), + // which Botan's TLS 1.3 client enforces at + // tls_client_impl_13.cpp via random_signals_downgrade(). + // // TODO: derive from IANA file in script return (suite == 0x00FF || suite == 0x5600); } @@ -76,7 +89,11 @@ } bool Ciphersuite::cbc_ciphersuite() const { - return (mac_algo() != "AEAD"); + return (mac_algo() != "AEAD" && cipher_algo() != "NULL"); +} + +bool Ciphersuite::null_ciphersuite() const { + return (cipher_algo() == "NULL"); } bool Ciphersuite::aead_ciphersuite() const { @@ -87,6 +104,10 @@ return auth_method() != Auth_Method::IMPLICIT; } +bool Ciphersuite::is_certificate_required() const { + return signature_used() || kex_method() == Kex_Algo::STATIC_RSA; +} + std::optional Ciphersuite::by_id(uint16_t suite) { const std::vector& all_suites = all_known_ciphersuites(); auto s = std::lower_bound(all_suites.begin(), all_suites.end(), suite); @@ -101,7 +122,7 @@ std::optional Ciphersuite::from_name(std::string_view name) { const std::vector& all_suites = all_known_ciphersuites(); - for(auto suite : all_suites) { + for(const auto& suite : all_suites) { if(suite.to_string() == name) { return suite; } @@ -110,95 +131,4 @@ return std::nullopt; // some unknown ciphersuite } -namespace { - -bool have_hash(std::string_view prf) { - return (!HashFunction::providers(prf).empty()); -} - -bool have_cipher(std::string_view cipher) { - return (!BlockCipher::providers(cipher).empty()) || (!StreamCipher::providers(cipher).empty()); -} - -} // namespace - -bool Ciphersuite::is_usable() const { - if(!m_cipher_keylen) { // uninitialized object - return false; - } - - if(!have_hash(prf_algo())) { - return false; - } - -#if !defined(BOTAN_HAS_TLS_CBC) - if(cbc_ciphersuite()) - return false; -#endif - - if(mac_algo() == "AEAD") { - if(cipher_algo() == "ChaCha20Poly1305") { -#if !defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305) - return false; -#endif - } else { - auto cipher_and_mode = split_on(cipher_algo(), '/'); - BOTAN_ASSERT(cipher_and_mode.size() == 2, "Expected format for AEAD algo"); - if(!have_cipher(cipher_and_mode[0])) { - return false; - } - - const auto& mode = cipher_and_mode[1]; - -#if !defined(BOTAN_HAS_AEAD_CCM) - if(mode == "CCM" || mode == "CCM-8") - return false; -#endif - -#if !defined(BOTAN_HAS_AEAD_GCM) - if(mode == "GCM") - return false; -#endif - -#if !defined(BOTAN_HAS_AEAD_OCB) - if(mode == "OCB(12)" || mode == "OCB") - return false; -#endif - - // Potentially unused if all AEADs are available - BOTAN_UNUSED(mode); - } - } else { - // Old non-AEAD schemes - if(!have_cipher(cipher_algo())) { - return false; - } - if(!have_hash(mac_algo())) { // HMAC - return false; - } - } - - if(kex_method() == Kex_Algo::ECDH || kex_method() == Kex_Algo::ECDHE_PSK) { -#if !defined(BOTAN_HAS_ECDH) - return false; -#endif - } else if(kex_method() == Kex_Algo::DH) { -#if !defined(BOTAN_HAS_DIFFIE_HELLMAN) - return false; -#endif - } - - if(auth_method() == Auth_Method::ECDSA) { -#if !defined(BOTAN_HAS_ECDSA) - return false; -#endif - } else if(auth_method() == Auth_Method::RSA) { -#if !defined(BOTAN_HAS_RSA) - return false; -#endif - } - - return true; -} - } // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_ciphersuite.h botan3-3.12.0+dfsg/src/lib/tls/tls_ciphersuite.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_ciphersuite.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_ciphersuite.h 2026-05-07 01:38:28.000000000 +0000 @@ -54,7 +54,7 @@ * e.g "RSA_WITH_RC4_128_SHA" or "ECDHE_RSA_WITH_AES_128_GCM_SHA256" * @return RFC ciphersuite string identifier */ - std::string to_string() const { return (!m_iana_id) ? "unknown cipher suite" : m_iana_id; } + std::string to_string() const { return (m_iana_id == nullptr) ? "unknown cipher suite" : m_iana_id; } /** * @return ciphersuite number @@ -77,6 +77,11 @@ bool cbc_ciphersuite() const; /** + * @return true if this suite uses a NULL cipher + */ + bool null_ciphersuite() const; + + /** * @return true if this suite uses a AEAD cipher */ bool aead_ciphersuite() const; @@ -84,6 +89,14 @@ bool signature_used() const; /** + * @return true if this ciphersuite requires the server to present + * a certificate. True for both signature-authenticated suites and + * static RSA key exchange (which uses the server's RSA cert for + * key transport). + */ + bool is_certificate_required() const; + + /** * @return key exchange algorithm used by this ciphersuite */ std::string kex_algo() const { return kex_method_to_string(kex_method()); } @@ -134,7 +147,7 @@ bool operator<(const uint16_t c) const { return ciphersuite_code() < c; } private: - bool is_usable() const; + static bool is_known_usable(uint16_t code); Ciphersuite(uint16_t ciphersuite_code, const char* iana_id, @@ -155,9 +168,8 @@ m_cipher_algo(cipher_algo), m_mac_algo(mac_algo), m_cipher_keylen(cipher_keylen), - m_mac_keylen(mac_keylen) { - m_usable = is_usable(); - } + m_mac_keylen(mac_keylen), + m_usable(is_known_usable(ciphersuite_code)) {} uint16_t m_ciphersuite_code = 0; diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_client.cpp botan3-3.12.0+dfsg/src/lib/tls/tls_client.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls_client.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_client.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,18 +10,18 @@ #include -#include -#include -#include +#include +#include +#include + +#if defined(BOTAN_HAS_TLS_12) + #include +#endif -#include #if defined(BOTAN_HAS_TLS_13) #include #endif -#include -#include - namespace Botan::TLS { /* @@ -58,15 +58,23 @@ } #endif - m_impl = std::make_unique(callbacks, - session_manager, - creds, - policy, - rng, - std::move(info), - offer_version.is_datagram_protocol(), - next_protocols, - io_buf_sz); +#if defined(BOTAN_HAS_TLS_12) + if(offer_version.is_pre_tls_13()) { + m_impl = std::make_unique(callbacks, + session_manager, + creds, + policy, + rng, + std::move(info), + offer_version.is_datagram_protocol(), + next_protocols, + io_buf_sz); + return; + } +#endif + + BOTAN_UNUSED(callbacks, session_manager, creds, policy, rng, info, offer_version, next_protocols, io_buf_sz); + throw Not_Implemented("Requested TLS version to be offered is not available in this build"); } Client::~Client() = default; @@ -74,6 +82,7 @@ size_t Client::downgrade() { BOTAN_ASSERT_NOMSG(m_impl->is_downgrading()); +#if defined(BOTAN_HAS_TLS_12) auto info = m_impl->extract_downgrade_info(); m_impl = std::make_unique(*info); @@ -85,6 +94,11 @@ // before any data was transferred return 0; } +#else + // If TLS 1.2 is not available, we will never downgrade, the downgrade info + // won't even be created and `is_downgrading()` would always return false. + BOTAN_ASSERT_UNREACHABLE(); +#endif } size_t Client::from_peer(std::span data) { diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_client.h botan3-3.12.0+dfsg/src/lib/tls/tls_client.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_client.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_client.h 2026-05-07 01:38:28.000000000 +0000 @@ -12,15 +12,20 @@ #define BOTAN_TLS_CLIENT_H_ #include +#include // TODO(Botan4) not necessary here, remove #include -#include +#include // TODO(Botan4) not necessary here, remove +#include +#include #include #include namespace Botan::TLS { +class Callbacks; +class Session_Manager; class Channel_Impl; -class Handshake_IO; +class Policy; /** * SSL/TLS Client @@ -124,6 +129,11 @@ bool timeout_check() override; + Client(const Client& other) = delete; + Client(Client&& other) = default; + Client& operator=(const Client& other) = delete; + Client& operator=(Client&& other) = delete; + private: size_t downgrade(); diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_exceptn.h botan3-3.12.0+dfsg/src/lib/tls/tls_exceptn.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_exceptn.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_exceptn.h 2026-05-07 01:38:28.000000000 +0000 @@ -20,7 +20,7 @@ public: Alert::Type type() const { return m_alert_type; } - TLS_Exception(Alert::Type type, std::string_view err_msg = "Unknown error") : + explicit TLS_Exception(Alert::Type type, std::string_view err_msg = "Unknown error") : Exception(err_msg), m_alert_type(type) {} int error_code() const noexcept override { return static_cast(m_alert_type); } diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_extensions.cpp botan3-3.12.0+dfsg/src/lib/tls/tls_extensions.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls_extensions.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_extensions.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,14 +12,22 @@ #include -#include -#include #include #include +#include +#include #include #include +#include +#include -#include +#if defined(BOTAN_HAS_TLS_13) + #include +#endif + +#if defined(BOTAN_HAS_TLS_12) + #include +#endif namespace Botan::TLS { @@ -34,7 +42,7 @@ const uint16_t size = static_cast(reader.remaining_bytes()); switch(code) { case Extension_Code::ServerNameIndication: - return std::make_unique(reader, size); + return std::make_unique(reader, size, from); case Extension_Code::SupportedGroups: return std::make_unique(reader, size); @@ -42,12 +50,6 @@ case Extension_Code::CertificateStatusRequest: return std::make_unique(reader, size, message_type, from); - case Extension_Code::EcPointFormats: - return std::make_unique(reader, size); - - case Extension_Code::SafeRenegotiation: - return std::make_unique(reader, size); - case Extension_Code::SignatureAlgorithms: return std::make_unique(reader, size); @@ -66,20 +68,38 @@ case Extension_Code::ServerCertificateType: return std::make_unique(reader, size, from); - case Extension_Code::ExtendedMasterSecret: - return std::make_unique(reader, size); - case Extension_Code::RecordSizeLimit: return std::make_unique(reader, size, from); + case Extension_Code::SupportedVersions: + return std::make_unique(reader, size, from); + + case Extension_Code::Padding: + break; // RFC 7685, recognized but not implemented; falls through to Unknown_Extension + +#if defined(BOTAN_HAS_TLS_12) + case Extension_Code::EcPointFormats: + return std::make_unique(reader, size); + + case Extension_Code::SafeRenegotiation: + return std::make_unique(reader, size); + + case Extension_Code::ExtendedMasterSecret: + return std::make_unique(reader, size); + case Extension_Code::EncryptThenMac: return std::make_unique(reader, size); case Extension_Code::SessionTicket: - return std::make_unique(reader, size); - - case Extension_Code::SupportedVersions: - return std::make_unique(reader, size, from); + return std::make_unique(reader, size, from); +#else + case Extension_Code::EcPointFormats: + case Extension_Code::SafeRenegotiation: + case Extension_Code::ExtendedMasterSecret: + case Extension_Code::EncryptThenMac: + case Extension_Code::SessionTicket: + break; // considered as 'unknown extension' +#endif #if defined(BOTAN_HAS_TLS_13) case Extension_Code::PresharedKey: @@ -99,6 +119,14 @@ case Extension_Code::KeyShare: return std::make_unique(reader, size, message_type); +#else + case Extension_Code::PresharedKey: + case Extension_Code::EarlyData: + case Extension_Code::Cookie: + case Extension_Code::PskKeyExchangeModes: + case Extension_Code::CertificateAuthorities: + case Extension_Code::KeyShare: + break; // considered as 'unknown extension' #endif } @@ -107,13 +135,30 @@ } // namespace +Extensions::~Extensions() = default; + +bool Extensions::has(Extension_Code type) const { + return m_extensions.contains(type); +} + +Extension* Extensions::get(Extension_Code type) const { + const auto i = m_extensions.find(type); + + if(i == m_extensions.end()) { + return nullptr; + } else { + return i->second.get(); + } +} + void Extensions::add(std::unique_ptr extn) { - if(has(extn->type())) { - throw Invalid_Argument("cannot add the same extension twice: " + - std::to_string(static_cast(extn->type()))); + const auto type = extn->type(); + if(has(type)) { + throw Invalid_Argument("cannot add the same extension twice: " + std::to_string(static_cast(type))); } - m_extensions.emplace_back(extn.release()); + m_extension_codes.push_back(type); + m_extensions.emplace(type, std::move(extn)); } void Extensions::deserialize(TLS_Data_Reader& reader, const Connection_Side from, const Handshake_Type message_type) { @@ -137,6 +182,7 @@ // TODO offer a function on reader that returns a byte range as a reference // to avoid this copy of the extension data const std::vector extn_data = reader.get_fixed(extension_size); + m_raw_extension_data[type] = extn_data; TLS_Data_Reader extn_reader("Extension", extn_data); this->add(make_extension(extn_reader, type, from, message_type)); extn_reader.assert_done(); @@ -167,31 +213,37 @@ return !diff.empty(); } -std::unique_ptr Extensions::take(Extension_Code type) { - const auto i = - std::find_if(m_extensions.begin(), m_extensions.end(), [type](const auto& ext) { return ext->type() == type; }); - - std::unique_ptr result; - if(i != m_extensions.end()) { - std::swap(result, *i); +bool Extensions::remove_extension(Extension_Code type) { + auto i = m_extensions.find(type); + + if(i == m_extensions.end()) { + return false; + } else { m_extensions.erase(i); + std::erase(m_extension_codes, type); + m_raw_extension_data.erase(type); + return true; } - - return result; } std::vector Extensions::serialize(Connection_Side whoami) const { std::vector buf(2); // 2 bytes for length field - for(const auto& extn : m_extensions) { + // Serialize in the order extensions were added, which matters for TLS 1.3 + for(const auto extn_type : m_extension_codes) { + const auto& extn = m_extensions.at(extn_type); + if(extn->empty()) { continue; } - const uint16_t extn_code = static_cast(extn->type()); + const uint16_t extn_code = static_cast(extn_type); const std::vector extn_val = extn->serialize(whoami); + // Each extension carries a uint16 length prefix. + BOTAN_ASSERT_NOMSG(extn_val.size() <= 0xFFFF); + buf.push_back(get_byte<0>(extn_code)); buf.push_back(get_byte<1>(extn_code)); @@ -201,6 +253,8 @@ buf += extn_val; } + // The outer extensions block is itself uint16-length-prefixed. + BOTAN_ASSERT_NOMSG(buf.size() - 2 <= 0xFFFF); const uint16_t extn_size = static_cast(buf.size() - 2); buf[0] = get_byte<0>(extn_size); @@ -216,13 +270,43 @@ std::set Extensions::extension_types() const { std::set offers; - std::transform( - m_extensions.cbegin(), m_extensions.cend(), std::inserter(offers, offers.begin()), [](const auto& ext) { - return ext->type(); - }); + for(const auto& [extn_type, extn] : m_extensions) { + // Consistent with serialize(): empty extensions are not placed on + // the wire so they must not appear in the "offered" set either. + if(!extn->empty()) { + offers.insert(extn_type); + } + } return offers; } +void Extensions::reorder(const std::vector& order) { + const std::set in_order(order.begin(), order.end()); + + std::vector new_codes; + new_codes.reserve(m_extension_codes.size()); + + // First: extensions not mentioned in the order (preserving their relative order) + for(auto code : m_extension_codes) { + if(!in_order.contains(code)) { + new_codes.push_back(code); + } + } + + // Then: extensions in the specified order. Deduplicate so a caller that + // accidentally lists the same code twice doesn't cause it to be + // serialized twice (which would also break peers that reject duplicate + // extension codes per RFC 8446 4.2 / RFC 5246 7.4.1.4). + std::unordered_set already_pushed; + for(auto code : order) { + if(m_extensions.contains(code) && already_pushed.insert(code).second) { + new_codes.push_back(code); + } + } + + m_extension_codes = std::move(new_codes); +} + Unknown_Extension::Unknown_Extension(Extension_Code type, TLS_Data_Reader& reader, uint16_t extension_size) : m_type(type), m_value(reader.get_fixed(extension_size)) {} @@ -230,32 +314,62 @@ return m_value; } -Server_Name_Indicator::Server_Name_Indicator(TLS_Data_Reader& reader, uint16_t extension_size) { +Server_Name_Indicator::Server_Name_Indicator(TLS_Data_Reader& reader, uint16_t extension_size, Connection_Side from) { /* - * This is used by the server to confirm that it knew the name + RFC 6066 Section 3 + + A server that receives a client hello containing the "server_name" + extension MAY use the information contained in the extension to guide + its selection of an appropriate certificate to return to the client, + and/or other aspects of security policy. In this event, the server + SHALL include an extension of type "server_name" in the (extended) + server hello. The "extension_data" field of this extension SHALL be + empty. */ - if(extension_size == 0) { - return; - } + if(from == Connection_Side::Server) { + if(extension_size != 0) { + throw TLS_Exception(Alert::IllegalParameter, "Server sent non-empty SNI extension"); + } + } else { + // Clients are required to send at least one name in the SNI + if(extension_size == 0) { + throw TLS_Exception(Alert::IllegalParameter, "Client sent empty SNI extension"); + } - uint16_t name_bytes = reader.get_uint16_t(); + const uint16_t name_bytes = reader.get_uint16_t(); - if(name_bytes + 2 != extension_size) { - throw Decoding_Error("Bad encoding of SNI extension"); - } + // RFC 6066 3: a ServerName carrying a host_name (the only NameType + // currently defined and the only one this implementation acts on) + // requires at least 1 byte name_type + 2 byte length + 1 byte HostName. + if(name_bytes + 2 != extension_size || name_bytes < 4) { + throw Decoding_Error("Bad encoding of SNI extension"); + } - while(name_bytes) { - uint8_t name_type = reader.get_byte(); - name_bytes--; + BOTAN_ASSERT_NOMSG(reader.remaining_bytes() == name_bytes); - if(name_type == 0) { - // DNS - m_sni_host_name = reader.get_string(2, 1, 65535); - name_bytes -= static_cast(2 + m_sni_host_name.size()); - } else { - // some other unknown name type, which we will ignore - reader.discard_next(name_bytes); - name_bytes = 0; + while(reader.has_remaining()) { + const uint8_t name_type = reader.get_byte(); + + if(name_type == 0) { + /* + RFC 6066 Section 3 + The ServerNameList MUST NOT contain more than one name of the same name_type. + */ + if(!m_sni_host_name.empty()) { + throw Decoding_Error("TLS ServerNameIndicator contains more than one host_name"); + } + m_sni_host_name = reader.get_string(2, 1, 65535); + } else { + /* + Unknown name type - skip its length-prefixed value and continue + + RFC 6066 Section 3 + For backward compatibility, all future data structures associated + with new NameTypes MUST begin with a 16-bit length field. + */ + const uint16_t unknown_name_len = reader.get_uint16_t(); + reader.discard_next(unknown_name_len); + } } } } @@ -271,7 +385,12 @@ std::vector buf; - size_t name_len = m_sni_host_name.size(); + const size_t name_len = m_sni_host_name.size(); + + // RFC 6066 3: HostName<1..2^16-1>; the outer ServerNameList wraps a + // 1-byte name_type and a 2-byte length so the whole entry must fit in + // a uint16_t too. + BOTAN_ASSERT_NOMSG(name_len + 3 <= 0xFFFF); buf.push_back(get_byte<0>(static_cast(name_len + 3))); buf.push_back(get_byte<1>(static_cast(name_len + 3))); @@ -280,29 +399,50 @@ buf.push_back(get_byte<0>(static_cast(name_len))); buf.push_back(get_byte<1>(static_cast(name_len))); - buf += std::make_pair(cast_char_ptr_to_uint8(m_sni_host_name.data()), m_sni_host_name.size()); + buf += as_span_of_bytes(m_sni_host_name); return buf; } -Renegotiation_Extension::Renegotiation_Extension(TLS_Data_Reader& reader, uint16_t extension_size) : - m_reneg_data(reader.get_range(1, 0, 255)) { - if(m_reneg_data.size() + 1 != extension_size) { - throw Decoding_Error("Bad encoding for secure renegotiation extn"); +bool Server_Name_Indicator::hostname_acceptable_for_sni(std::string_view hostname) { + // Avoid sending an IPv4/IPv6 address in SNI as this is prohibited + + if(hostname.empty()) { + return false; + } + + if(string_to_ipv4(hostname).has_value()) { + return false; } + + // IPv6? Anyway ':' is not valid in DNS + if(hostname.find(':') != std::string_view::npos) { + return false; + } + + return true; } -std::vector Renegotiation_Extension::serialize(Connection_Side /*whoami*/) const { - std::vector buf; - append_tls_length_value(buf, m_reneg_data, 1); - return buf; +Application_Layer_Protocol_Notification::Application_Layer_Protocol_Notification(std::string_view protocol) { + BOTAN_ARG_CHECK(!protocol.empty(), "ALPN protocol name must not be empty"); + BOTAN_ARG_CHECK(protocol.size() < 256, "ALPN protocol name too long"); + m_protocols.emplace_back(protocol); +} + +Application_Layer_Protocol_Notification::Application_Layer_Protocol_Notification( + const std::vector& protocols) : + m_protocols(protocols) { + for(const auto& protocol : protocols) { + BOTAN_ARG_CHECK(!protocol.empty(), "ALPN protocol name must not be empty"); + BOTAN_ARG_CHECK(protocol.size() < 256, "ALPN protocol name too long"); + } } Application_Layer_Protocol_Notification::Application_Layer_Protocol_Notification(TLS_Data_Reader& reader, uint16_t extension_size, Connection_Side from) { - if(extension_size == 0) { - return; // empty extension + if(extension_size < 2) { + throw Decoding_Error("ALPN extension cannot be empty"); } const uint16_t name_bytes = reader.get_uint16_t(); @@ -313,7 +453,12 @@ throw Decoding_Error("Bad encoding of ALPN extension, bad length field"); } - while(bytes_remaining) { + // RFC 7301 3.1: ProtocolName protocol_name_list<2..2^16-1> + if(name_bytes == 0) { + throw Decoding_Error("Empty ALPN protocol_name_list not allowed"); + } + + while(bytes_remaining > 0) { const std::string p = reader.get_string(1, 0, 255); if(bytes_remaining < p.size() + 1) { @@ -348,42 +493,23 @@ std::vector Application_Layer_Protocol_Notification::serialize(Connection_Side /*whoami*/) const { std::vector buf(2); - for(auto&& p : m_protocols) { - if(p.length() >= 256) { + for(auto&& proto : m_protocols) { + if(proto.length() >= 256) { throw TLS_Exception(Alert::InternalError, "ALPN name too long"); } - if(!p.empty()) { - append_tls_length_value(buf, cast_char_ptr_to_uint8(p.data()), p.size(), 1); + if(!proto.empty()) { + append_tls_length_value(buf, proto, 1); } } + // RFC 7301 3.1: ProtocolName protocol_name_list<2..2^16-1>; + BOTAN_ASSERT_NOMSG(buf.size() - 2 <= 0xFFFF); buf[0] = get_byte<0>(static_cast(buf.size() - 2)); buf[1] = get_byte<1>(static_cast(buf.size() - 2)); return buf; } -std::string certificate_type_to_string(Certificate_Type type) { - switch(type) { - case Certificate_Type::X509: - return "X509"; - case Certificate_Type::RawPublicKey: - return "RawPublicKey"; - } - - return "Unknown"; -} - -Certificate_Type certificate_type_from_string(const std::string& type_str) { - if(type_str == "X509") { - return Certificate_Type::X509; - } else if(type_str == "RawPublicKey") { - return Certificate_Type::RawPublicKey; - } else { - throw Decoding_Error("Unknown certificate type: " + type_str); - } -} - Certificate_Type_Base::Certificate_Type_Base(std::vector supported_cert_types) : m_certificate_types(std::move(supported_cert_types)), m_from(Connection_Side::Client) { BOTAN_ARG_CHECK(!m_certificate_types.empty(), "at least one certificate type must be supported"); @@ -430,6 +556,10 @@ if(static_cast(extension_size) != type_bytes.size() + 1) { throw Decoding_Error("certificate type extension had inconsistent length"); } + // RFC 7250 4: {client,server}_certificate_types<1..2^8-1> so must be non-empty + if(type_bytes.empty()) { + throw Decoding_Error("Certificate type extension contains no types"); + } std::transform( type_bytes.begin(), type_bytes.end(), std::back_inserter(m_certificate_types), [](const auto type_byte) { return static_cast(type_byte); @@ -521,6 +651,8 @@ } } + // RFC 8446 4.2.7: NamedGroup named_group_list<2..2^16-1>; + BOTAN_ASSERT_NOMSG(buf.size() - 2 <= 0xFFFF); buf[0] = get_byte<0>(static_cast(buf.size() - 2)); buf[1] = get_byte<1>(static_cast(buf.size() - 2)); @@ -534,70 +666,27 @@ throw Decoding_Error("Inconsistent length field in supported groups list"); } + // RFC 8446 4.2.7: NamedGroup named_group_list<2..2^16-1>; + if(len == 0) { + throw Decoding_Error("Empty supported groups list"); + } + if(len % 2 == 1) { throw Decoding_Error("Supported groups list of strange size"); } const size_t elems = len / 2; + std::unordered_set seen; for(size_t i = 0; i != elems; ++i) { const auto group = static_cast(reader.get_uint16_t()); // Note: RFC 8446 does not explicitly enforce that groups must be unique. - if(!value_exists(m_groups, group)) { + if(seen.insert(group.wire_code()).second) { m_groups.push_back(group); } } } -std::vector Supported_Point_Formats::serialize(Connection_Side /*whoami*/) const { - // if this extension is sent, it MUST include uncompressed (RFC 4492, section 5.1) - if(m_prefers_compressed) { - return std::vector{2, ANSIX962_COMPRESSED_PRIME, UNCOMPRESSED}; - } else { - return std::vector{1, UNCOMPRESSED}; - } -} - -Supported_Point_Formats::Supported_Point_Formats(TLS_Data_Reader& reader, uint16_t extension_size) { - uint8_t len = reader.get_byte(); - - if(len + 1 != extension_size) { - throw Decoding_Error("Inconsistent length field in supported point formats list"); - } - - bool includes_uncompressed = false; - for(size_t i = 0; i != len; ++i) { - uint8_t format = reader.get_byte(); - - if(static_cast(format) == UNCOMPRESSED) { - m_prefers_compressed = false; - reader.discard_next(len - i - 1); - return; - } else if(static_cast(format) == ANSIX962_COMPRESSED_PRIME) { - m_prefers_compressed = true; - std::vector remaining_formats = reader.get_fixed(len - i - 1); - includes_uncompressed = - std::any_of(std::begin(remaining_formats), std::end(remaining_formats), [](uint8_t remaining_format) { - return static_cast(remaining_format) == UNCOMPRESSED; - }); - break; - } - - // ignore ANSIX962_COMPRESSED_CHAR2, we don't support these curves - } - - // RFC 4492 5.1.: - // If the Supported Point Formats Extension is indeed sent, it MUST contain the value 0 (uncompressed) - // as one of the items in the list of point formats. - // Note: - // RFC 8422 5.1.2. explicitly requires this check, - // but only if the Supported Groups extension was sent. - if(!includes_uncompressed) { - throw TLS_Exception(Alert::IllegalParameter, - "Supported Point Formats Extension must contain the uncompressed point format"); - } -} - namespace { std::vector serialize_signature_algorithms(const std::vector& schemes) { @@ -610,7 +699,7 @@ buf.push_back(get_byte<0>(len)); buf.push_back(get_byte<1>(len)); - for(Signature_Scheme scheme : schemes) { + for(const Signature_Scheme scheme : schemes) { buf.push_back(get_byte<0>(scheme.wire_code())); buf.push_back(get_byte<1>(scheme.wire_code())); } @@ -627,7 +716,7 @@ std::vector schemes; schemes.reserve(len / 2); - while(len) { + while(len > 0) { schemes.emplace_back(reader.get_uint16_t()); len -= 2; } @@ -651,11 +740,17 @@ Signature_Algorithms_Cert::Signature_Algorithms_Cert(TLS_Data_Reader& reader, uint16_t extension_size) : m_schemes(parse_signature_algorithms(reader, extension_size)) {} -Session_Ticket_Extension::Session_Ticket_Extension(TLS_Data_Reader& reader, uint16_t extension_size) : - m_ticket(Session_Ticket(reader.get_elem>(extension_size))) {} - -SRTP_Protection_Profiles::SRTP_Protection_Profiles(TLS_Data_Reader& reader, uint16_t extension_size) : - m_pp(reader.get_range(2, 0, 65535)) { +SRTP_Protection_Profiles::SRTP_Protection_Profiles(TLS_Data_Reader& reader, uint16_t extension_size) { + // RFC 5764 4.1.1: UseSRTPData consists of + // SRTPProtectionProfile SRTPProtectionProfiles<2..2^16-1>; + // opaque srtp_mki<0..255>; + // for a wire size of 2 (profiles len) + 2*N + 1 (mki len) + mki_bytes, + // with N >= 1. + if(extension_size < 5) { + throw Decoding_Error("Truncated SRTP protection extension"); + } + const size_t max_profile_pairs = (static_cast(extension_size) - 3) / 2; + m_pp = reader.get_range(2, 1, max_profile_pairs); const std::vector mki = reader.get_range(1, 0, 255); if(m_pp.size() * 2 + mki.size() + 3 != extension_size) { @@ -674,7 +769,7 @@ buf.push_back(get_byte<0>(pp_len)); buf.push_back(get_byte<1>(pp_len)); - for(uint16_t pp : m_pp) { + for(const uint16_t pp : m_pp) { buf.push_back(get_byte<0>(pp)); buf.push_back(get_byte<1>(pp)); } @@ -684,26 +779,6 @@ return buf; } -Extended_Master_Secret::Extended_Master_Secret(TLS_Data_Reader& /*unused*/, uint16_t extension_size) { - if(extension_size != 0) { - throw Decoding_Error("Invalid extended_master_secret extension"); - } -} - -std::vector Extended_Master_Secret::serialize(Connection_Side /*whoami*/) const { - return std::vector(); -} - -Encrypt_then_MAC::Encrypt_then_MAC(TLS_Data_Reader& /*unused*/, uint16_t extension_size) { - if(extension_size != 0) { - throw Decoding_Error("Invalid encrypt_then_mac extension"); - } -} - -std::vector Encrypt_then_MAC::serialize(Connection_Side /*whoami*/) const { - return std::vector(); -} - std::vector Supported_Versions::serialize(Connection_Side whoami) const { std::vector buf; @@ -712,12 +787,14 @@ buf.push_back(m_versions[0].major_version()); buf.push_back(m_versions[0].minor_version()); } else { + // RFC 8446 4.2.1: ProtocolVersion versions<2..254>; - up to 127 entries. BOTAN_ASSERT_NOMSG(!m_versions.empty()); + BOTAN_ASSERT_NOMSG(m_versions.size() <= 127); const uint8_t len = static_cast(m_versions.size() * 2); buf.push_back(len); - for(Protocol_Version version : m_versions) { + for(const Protocol_Version version : m_versions) { buf.push_back(version.major_version()); buf.push_back(version.minor_version()); } @@ -727,24 +804,35 @@ } Supported_Versions::Supported_Versions(Protocol_Version offer, const Policy& policy) { - if(offer.is_datagram_protocol()) { -#if defined(BOTAN_HAS_TLS_12) - if(offer >= Protocol_Version::DTLS_V12 && policy.allow_dtls12()) { - m_versions.push_back(Protocol_Version::DTLS_V12); - } -#endif - } else { + // RFC 8446 4.2.1 + // The extension contains a list of supported versions in preference order, + // with the most preferred version first. Implementations [...] MUST send + // this extension in the ClientHello containing all versions of TLS which + // they are prepared to negotiate. + // + // We simply assume that we always want the newest available TLS version. #if defined(BOTAN_HAS_TLS_13) + if(!offer.is_datagram_protocol()) { if(offer >= Protocol_Version::TLS_V13 && policy.allow_tls13()) { m_versions.push_back(Protocol_Version::TLS_V13); } + } #endif + #if defined(BOTAN_HAS_TLS_12) + if(offer.is_datagram_protocol()) { + if(offer >= Protocol_Version::DTLS_V12 && policy.allow_dtls12()) { + m_versions.push_back(Protocol_Version::DTLS_V12); + } + } else { if(offer >= Protocol_Version::TLS_V12 && policy.allow_tls12()) { m_versions.push_back(Protocol_Version::TLS_V12); } -#endif } +#endif + + // if no versions are supported, the input variables are not used + BOTAN_UNUSED(offer, policy); } Supported_Versions::Supported_Versions(TLS_Data_Reader& reader, uint16_t extension_size, Connection_Side from) { @@ -776,9 +864,9 @@ } Record_Size_Limit::Record_Size_Limit(const uint16_t limit) : m_limit(limit) { - BOTAN_ASSERT(limit >= 64, "RFC 8449 does not allow record size limits smaller than 64 bytes"); - BOTAN_ASSERT(limit <= MAX_PLAINTEXT_SIZE + 1 /* encrypted content type byte */, - "RFC 8449 does not allow record size limits larger than 2^14+1"); + BOTAN_ARG_CHECK(limit >= 64, "RFC 8449 does not allow record size limits smaller than 64 bytes"); + BOTAN_ARG_CHECK(limit <= MAX_PLAINTEXT_SIZE + 1 /* encrypted content type byte */, + "RFC 8449 does not allow record size limits larger than 2^14+1"); } Record_Size_Limit::Record_Size_Limit(TLS_Data_Reader& reader, uint16_t extension_size, Connection_Side from) { @@ -815,7 +903,7 @@ } } -std::vector Record_Size_Limit::serialize(Connection_Side) const { +std::vector Record_Size_Limit::serialize(Connection_Side /*whoami*/) const { std::vector buf; buf.push_back(get_byte<0>(m_limit)); @@ -824,144 +912,4 @@ return buf; } -#if defined(BOTAN_HAS_TLS_13) -Cookie::Cookie(const std::vector& cookie) : m_cookie(cookie) {} - -Cookie::Cookie(TLS_Data_Reader& reader, uint16_t extension_size) { - if(extension_size == 0) { - return; - } - - const uint16_t len = reader.get_uint16_t(); - - if(len == 0) { - // Based on RFC 8446 4.2.2, len of the Cookie buffer must be at least 1 - throw Decoding_Error("Cookie length must be at least 1 byte"); - } - - if(len > reader.remaining_bytes()) { - throw Decoding_Error("Not enough bytes in the buffer to decode Cookie"); - } - - for(size_t i = 0; i < len; ++i) { - m_cookie.push_back(reader.get_byte()); - } -} - -std::vector Cookie::serialize(Connection_Side /*whoami*/) const { - std::vector buf; - - const uint16_t len = static_cast(m_cookie.size()); - - buf.push_back(get_byte<0>(len)); - buf.push_back(get_byte<1>(len)); - - for(const auto& cookie_byte : m_cookie) { - buf.push_back(cookie_byte); - } - - return buf; -} - -std::vector PSK_Key_Exchange_Modes::serialize(Connection_Side) const { - std::vector buf; - - BOTAN_ASSERT_NOMSG(m_modes.size() < 256); - buf.push_back(static_cast(m_modes.size())); - for(const auto& mode : m_modes) { - buf.push_back(static_cast(mode)); - } - - return buf; -} - -PSK_Key_Exchange_Modes::PSK_Key_Exchange_Modes(TLS_Data_Reader& reader, uint16_t extension_size) { - if(extension_size < 2) { - throw Decoding_Error("Empty psk_key_exchange_modes extension is illegal"); - } - - const auto mode_count = reader.get_byte(); - for(uint16_t i = 0; i < mode_count; ++i) { - const auto mode = static_cast(reader.get_byte()); - if(mode == PSK_Key_Exchange_Mode::PSK_KE || mode == PSK_Key_Exchange_Mode::PSK_DHE_KE) { - m_modes.push_back(mode); - } - } -} - -std::vector Certificate_Authorities::serialize(Connection_Side) const { - std::vector out; - std::vector dn_list; - - for(const auto& dn : m_distinguished_names) { - std::vector encoded_dn; - auto encoder = DER_Encoder(encoded_dn); - dn.encode_into(encoder); - append_tls_length_value(dn_list, encoded_dn, 2); - } - - append_tls_length_value(out, dn_list, 2); - - return out; -} - -Certificate_Authorities::Certificate_Authorities(TLS_Data_Reader& reader, uint16_t extension_size) { - if(extension_size < 2) { - throw Decoding_Error("Empty certificate_authorities extension is illegal"); - } - - const uint16_t purported_size = reader.get_uint16_t(); - - if(reader.remaining_bytes() != purported_size) { - throw Decoding_Error("Inconsistent length in certificate_authorities extension"); - } - - while(reader.has_remaining()) { - std::vector name_bits = reader.get_tls_length_value(2); - - BER_Decoder decoder(name_bits.data(), name_bits.size()); - m_distinguished_names.emplace_back(); - decoder.decode(m_distinguished_names.back()); - } -} - -Certificate_Authorities::Certificate_Authorities(std::vector acceptable_DNs) : - m_distinguished_names(std::move(acceptable_DNs)) {} - -std::vector EarlyDataIndication::serialize(Connection_Side) const { - std::vector result; - if(m_max_early_data_size.has_value()) { - const auto max_data = m_max_early_data_size.value(); - result.push_back(get_byte<0>(max_data)); - result.push_back(get_byte<1>(max_data)); - result.push_back(get_byte<2>(max_data)); - result.push_back(get_byte<3>(max_data)); - } - return result; -} - -EarlyDataIndication::EarlyDataIndication(TLS_Data_Reader& reader, - uint16_t extension_size, - Handshake_Type message_type) { - if(message_type == Handshake_Type::NewSessionTicket) { - if(extension_size != 4) { - throw TLS_Exception(Alert::DecodeError, - "Received an early_data extension in a NewSessionTicket message " - "without maximum early data size indication"); - } - - m_max_early_data_size = reader.get_uint32_t(); - } else if(extension_size != 0) { - throw TLS_Exception(Alert::DecodeError, - "Received an early_data extension containing an unexpected data " - "size indication"); - } -} - -bool EarlyDataIndication::empty() const { - // This extension may be empty by definition but still carry information - return false; -} - -#endif } // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_extensions.h botan3-3.12.0+dfsg/src/lib/tls/tls_extensions.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_extensions.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_extensions.h 2026-05-07 01:38:28.000000000 +0000 @@ -13,40 +13,25 @@ #ifndef BOTAN_TLS_EXTENSIONS_H_ #define BOTAN_TLS_EXTENSIONS_H_ -#include -#include -#include +#include #include #include -#include #include #include -#include +#include #include #include #include -#include -#include -#include namespace Botan { class RandomNumberGenerator; class Credentials_Manager; +class X509_DN; namespace TLS { -#if defined(BOTAN_HAS_TLS_13) -class Callbacks; -class Session_Manager; -class Cipher_State; -class Ciphersuite; -class Transcript_Hash_State; - -enum class PSK_Key_Exchange_Mode : uint8_t { PSK_KE = 0, PSK_DHE_KE = 1 }; - -#endif class Policy; class TLS_Data_Reader; @@ -55,7 +40,7 @@ CertificateStatusRequest = 5, SupportedGroups = 10, - EcPointFormats = 11, + EcPointFormats = 11, // TLS 1.2 exclusive SignatureAlgorithms = 13, CertSignatureAlgorithms = 50, UseSrtp = 14, @@ -67,33 +52,32 @@ ClientCertificateType = 19, ServerCertificateType = 20, - EncryptThenMac = 22, - ExtendedMasterSecret = 23, + Padding = 21, // RFC 7685; not implemented but recognized so it can be + // explicitly carved out of strict-mutation checks. + + EncryptThenMac = 22, // TLS 1.2 exclusive + ExtendedMasterSecret = 23, // TLS 1.2 exclusive RecordSizeLimit = 28, - SessionTicket = 35, + SessionTicket = 35, // TLS 1.2 exclusive SupportedVersions = 43, -#if defined(BOTAN_HAS_TLS_13) - PresharedKey = 41, - EarlyData = 42, - Cookie = 44, - - PskKeyExchangeModes = 45, - CertificateAuthorities = 47, - // OidFilters = 48, // NYI - KeyShare = 51, -#endif + PresharedKey = 41, // TLS 1.3 exclusive + EarlyData = 42, // TLS 1.3 exclusive + Cookie = 44, // TLS 1.3 exclusive + PskKeyExchangeModes = 45, // TLS 1.3 exclusive + CertificateAuthorities = 47, // TLS 1.3 exclusive + KeyShare = 51, // TLS 1.3 exclusive - SafeRenegotiation = 65281, + SafeRenegotiation = 65281, // TLS 1.2 exclusive }; /** * Base class representing a TLS extension of some kind */ -class BOTAN_UNSTABLE_API Extension { +class BOTAN_UNSTABLE_API Extension /* NOLINT(*-special-member-functions) */ { public: /** * @return code number of the extension @@ -129,7 +113,7 @@ explicit Server_Name_Indicator(std::string_view host_name) : m_sni_host_name(host_name) {} - Server_Name_Indicator(TLS_Data_Reader& reader, uint16_t extension_size); + Server_Name_Indicator(TLS_Data_Reader& reader, uint16_t extension_size, Connection_Side from); std::string host_name() const { return m_sni_host_name; } @@ -137,33 +121,10 @@ bool empty() const override { return false; } - private: - std::string m_sni_host_name; -}; - -/** -* Renegotiation Indication Extension (RFC 5746) -*/ -class BOTAN_UNSTABLE_API Renegotiation_Extension final : public Extension { - public: - static Extension_Code static_type() { return Extension_Code::SafeRenegotiation; } - - Extension_Code type() const override { return static_type(); } - - Renegotiation_Extension() = default; - - explicit Renegotiation_Extension(const std::vector& bits) : m_reneg_data(bits) {} - - Renegotiation_Extension(TLS_Data_Reader& reader, uint16_t extension_size); - - const std::vector& renegotiation_info() const { return m_reneg_data; } - - std::vector serialize(Connection_Side whoami) const override; - - bool empty() const override { return false; } // always send this + static bool hostname_acceptable_for_sni(std::string_view hostname); private: - std::vector m_reneg_data; + std::string m_sni_host_name; }; /** @@ -182,14 +143,12 @@ /** * Single protocol, used by server */ - explicit Application_Layer_Protocol_Notification(std::string_view protocol) : - m_protocols(1, std::string(protocol)) {} + explicit Application_Layer_Protocol_Notification(std::string_view protocol); /** * List of protocols, used by client */ - explicit Application_Layer_Protocol_Notification(const std::vector& protocols) : - m_protocols(protocols) {} + explicit Application_Layer_Protocol_Notification(const std::vector& protocols); Application_Layer_Protocol_Notification(TLS_Data_Reader& reader, uint16_t extension_size, Connection_Side from); @@ -201,12 +160,6 @@ std::vector m_protocols; }; -// As defined in RFC 8446 4.4.2 -enum class Certificate_Type : uint8_t { X509 = 0, RawPublicKey = 2 }; - -std::string certificate_type_to_string(Certificate_Type type); -Certificate_Type certificate_type_from_string(const std::string& type_str); - /** * RFC 7250 * Base class for 'client_certificate_type' and 'server_certificate_type' extensions. @@ -216,7 +169,7 @@ /** * Called by the client to advertise support for a number of cert types. */ - Certificate_Type_Base(std::vector supported_cert_types); + explicit Certificate_Type_Base(std::vector supported_cert_types); protected: /** @@ -276,43 +229,6 @@ }; /** -* Session Ticket Extension (RFC 5077) -*/ -class BOTAN_UNSTABLE_API Session_Ticket_Extension final : public Extension { - public: - static Extension_Code static_type() { return Extension_Code::SessionTicket; } - - Extension_Code type() const override { return static_type(); } - - /** - * @return contents of the session ticket - */ - const Session_Ticket& contents() const { return m_ticket; } - - /** - * Create empty extension, used by both client and server - */ - Session_Ticket_Extension() = default; - - /** - * Extension with ticket, used by client - */ - explicit Session_Ticket_Extension(Session_Ticket session_ticket) : m_ticket(std::move(session_ticket)) {} - - /** - * Deserialize a session ticket - */ - Session_Ticket_Extension(TLS_Data_Reader& reader, uint16_t extension_size); - - std::vector serialize(Connection_Side) const override { return m_ticket.get(); } - - bool empty() const override { return false; } - - private: - Session_Ticket m_ticket; -}; - -/** * Supported Groups Extension (RFC 7919) */ class BOTAN_UNSTABLE_API Supported_Groups final : public Extension { @@ -341,38 +257,6 @@ std::vector m_groups; }; -// previously Supported Elliptic Curves Extension (RFC 4492) -//using Supported_Elliptic_Curves = Supported_Groups; - -/** -* Supported Point Formats Extension (RFC 4492) -*/ -class BOTAN_UNSTABLE_API Supported_Point_Formats final : public Extension { - public: - enum ECPointFormat : uint8_t { - UNCOMPRESSED = 0, - ANSIX962_COMPRESSED_PRIME = 1, - ANSIX962_COMPRESSED_CHAR2 = 2, // don't support these curves - }; - - static Extension_Code static_type() { return Extension_Code::EcPointFormats; } - - Extension_Code type() const override { return static_type(); } - - std::vector serialize(Connection_Side whoami) const override; - - explicit Supported_Point_Formats(bool prefer_compressed) : m_prefers_compressed(prefer_compressed) {} - - Supported_Point_Formats(TLS_Data_Reader& reader, uint16_t extension_size); - - bool empty() const override { return false; } - - bool prefers_compressed() const { return m_prefers_compressed; } - - private: - bool m_prefers_compressed = false; -}; - /** * Signature Algorithms Extension for TLS 1.2 (RFC 5246) */ @@ -454,48 +338,12 @@ std::vector m_pp; }; -/** -* Extended Master Secret Extension (RFC 7627) -*/ -class BOTAN_UNSTABLE_API Extended_Master_Secret final : public Extension { - public: - static Extension_Code static_type() { return Extension_Code::ExtendedMasterSecret; } - - Extension_Code type() const override { return static_type(); } - - std::vector serialize(Connection_Side whoami) const override; - - bool empty() const override { return false; } - - Extended_Master_Secret() = default; - - Extended_Master_Secret(TLS_Data_Reader& reader, uint16_t extension_size); -}; - -/** -* Encrypt-then-MAC Extension (RFC 7366) -*/ -class BOTAN_UNSTABLE_API Encrypt_then_MAC final : public Extension { - public: - static Extension_Code static_type() { return Extension_Code::EncryptThenMac; } - - Extension_Code type() const override { return static_type(); } - - std::vector serialize(Connection_Side whoami) const override; - - bool empty() const override { return false; } - - Encrypt_then_MAC() = default; - - Encrypt_then_MAC(TLS_Data_Reader& reader, uint16_t extension_size); -}; - class Certificate_Status_Request_Internal; /** * Certificate Status Request (RFC 6066) */ -class BOTAN_UNSTABLE_API Certificate_Status_Request final : public Extension { +class BOTAN_UNSTABLE_API Certificate_Status_Request final : public Extension /* NOLINT(*-special-member-functions) */ { public: static Extension_Code static_type() { return Extension_Code::CertificateStatusRequest; } @@ -517,7 +365,7 @@ std::vector> ocsp_key_ids); // TLS 1.3 version - Certificate_Status_Request(std::vector response); + explicit Certificate_Status_Request(std::vector response); Certificate_Status_Request(TLS_Data_Reader& reader, uint16_t extension_size, @@ -545,7 +393,7 @@ Supported_Versions(Protocol_Version version, const Policy& policy); - Supported_Versions(Protocol_Version version) { m_versions.push_back(version); } + explicit Supported_Versions(Protocol_Version version) { m_versions.push_back(version); } Supported_Versions(TLS_Data_Reader& reader, uint16_t extension_size, Connection_Side from); @@ -584,302 +432,6 @@ uint16_t m_limit; }; -using Named_Group = Group_Params; - -#if defined(BOTAN_HAS_TLS_13) -/** -* Cookie from RFC 8446 4.2.2 -*/ -class BOTAN_UNSTABLE_API Cookie final : public Extension { - public: - static Extension_Code static_type() { return Extension_Code::Cookie; } - - Extension_Code type() const override { return static_type(); } - - std::vector serialize(Connection_Side whoami) const override; - - bool empty() const override { return m_cookie.empty(); } - - const std::vector& get_cookie() const { return m_cookie; } - - explicit Cookie(const std::vector& cookie); - - explicit Cookie(TLS_Data_Reader& reader, uint16_t extension_size); - - private: - std::vector m_cookie; -}; - -/** -* Pre-Shared Key Exchange Modes from RFC 8446 4.2.9 -*/ -class BOTAN_UNSTABLE_API PSK_Key_Exchange_Modes final : public Extension { - public: - static Extension_Code static_type() { return Extension_Code::PskKeyExchangeModes; } - - Extension_Code type() const override { return static_type(); } - - std::vector serialize(Connection_Side whoami) const override; - - bool empty() const override { return m_modes.empty(); } - - const std::vector& modes() const { return m_modes; } - - explicit PSK_Key_Exchange_Modes(std::vector modes) : m_modes(std::move(modes)) {} - - explicit PSK_Key_Exchange_Modes(TLS_Data_Reader& reader, uint16_t extension_size); - - private: - std::vector m_modes; -}; - -/** - * Certificate Authorities Extension from RFC 8446 4.2.4 - */ -class BOTAN_UNSTABLE_API Certificate_Authorities final : public Extension { - public: - static Extension_Code static_type() { return Extension_Code::CertificateAuthorities; } - - Extension_Code type() const override { return static_type(); } - - std::vector serialize(Connection_Side whoami) const override; - - bool empty() const override { return m_distinguished_names.empty(); } - - const std::vector& distinguished_names() const { return m_distinguished_names; } - - Certificate_Authorities(TLS_Data_Reader& reader, uint16_t extension_size); - explicit Certificate_Authorities(std::vector acceptable_DNs); - - private: - std::vector m_distinguished_names; -}; - -/** - * Pre-Shared Key extension from RFC 8446 4.2.11 - */ -class BOTAN_UNSTABLE_API PSK final : public Extension { - public: - static Extension_Code static_type() { return Extension_Code::PresharedKey; } - - Extension_Code type() const override { return static_type(); } - - std::vector serialize(Connection_Side side) const override; - - /** - * Returns the PSK identity (in case of an externally provided PSK) and - * the cipher state representing the PSK selected by the server. Note that - * this destructs the list of offered PSKs and its cipher states and must - * therefore not be called more than once. - * - * @note Technically, PSKs used for resumption also carry an identity. - * Though, typically, this is an opaque value meaningful only to the - * peer and of no authorative value for the user. We therefore - * report the identity of externally provided PSKs only. - */ - std::pair, std::unique_ptr> take_selected_psk_info( - const PSK& server_psk, const Ciphersuite& cipher); - - /** - * Selects one of the offered PSKs that is compatible with \p cipher. - * @retval PSK extension object that can be added to the Server Hello response - * @retval std::nullptr if no PSK offered by the client is convenient - */ - std::unique_ptr select_offered_psk(std::string_view host, - const Ciphersuite& cipher, - Session_Manager& session_mgr, - Credentials_Manager& credentials_mgr, - Callbacks& callbacks, - const Policy& policy); - - /** - * Remove PSK identities from the list in \p m_psk that are not compatible - * with the passed in \p cipher suite. - * This is useful to react to Hello Retry Requests. See RFC 8446 4.1.4. - */ - void filter(const Ciphersuite& cipher); - - /** - * Pulls the preshared key or the Session to resume from a PSK extension - * in Server Hello. - */ - std::variant take_session_to_resume_or_psk(); - - bool empty() const override; - - PSK(TLS_Data_Reader& reader, uint16_t extension_size, Handshake_Type message_type); - - /** - * Creates a PSK extension with a TLS 1.3 session object containing a - * master_secret. Note that it will extract that secret from the session, - * and won't create a copy of it. - * - * @param session_to_resume the session to be resumed; note that the - * master secret will be taken away from the - * session object. - * @param psks a list of non-resumption PSKs that should be - * offered to the server - * @param callbacks the application's callbacks - */ - PSK(std::optional& session_to_resume, std::vector psks, Callbacks& callbacks); - - ~PSK() override; - - void calculate_binders(const Transcript_Hash_State& truncated_transcript_hash); - bool validate_binder(const PSK& server_psk, const std::vector& binder) const; - - // TODO: Implement pure PSK negotiation that is not used for session - // resumption. - - private: - /** - * Creates a PSK extension that specifies the server's selection of an - * offered client PSK. The @p session_to_resume is kept internally - * and used later for the initialization of the Cipher_State object. - * - * Note: This constructor is called internally in PSK::select_offered_psk(). - */ - PSK(Session session_to_resume, uint16_t psk_index); - - /** - * Creates a PSK extension that specifies the server's selection of an - * externally provided PSK offered by the client. The @p psk is kept - * internally and used later for the initialization of the Cipher_State object. - * - * Note: This constructor is called internally in PSK::select_offered_psk(). - */ - PSK(ExternalPSK psk, const uint16_t psk_index); - - private: - class PSK_Internal; - std::unique_ptr m_impl; -}; - -/** -* Key_Share from RFC 8446 4.2.8 -*/ -class BOTAN_UNSTABLE_API Key_Share final : public Extension { - public: - static Extension_Code static_type() { return Extension_Code::KeyShare; } - - Extension_Code type() const override { return static_type(); } - - std::vector serialize(Connection_Side whoami) const override; - - bool empty() const override; - - /** - * Creates a Key_Share extension meant for the Server Hello that - * performs a key encapsulation with the selected public key from - * the client. - * - * @note This will retain the shared secret in the Key_Share extension - * until it is retrieved via take_shared_secret(). - */ - static std::unique_ptr create_as_encapsulation(Group_Params selected_group, - const Key_Share& client_keyshare, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng); - - /** - * Decapsulate the shared secret with the peer's key share. This method - * can be called on a ClientHello's Key_Share with a ServerHello's - * Key_Share. - * - * @note After the decapsulation the client's private key is destroyed. - * Multiple calls will result in an exception. - */ - secure_vector decapsulate(const Key_Share& server_keyshare, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng); - - /** - * Update a ClientHello's Key_Share to comply with a HelloRetryRequest. - * - * This will create new Key_Share_Entries and should only be called on a ClientHello Key_Share with a HelloRetryRequest Key_Share. - */ - void retry_offer(const Key_Share& retry_request_keyshare, - const std::vector& supported_groups, - Callbacks& cb, - RandomNumberGenerator& rng); - - /** - * @return key exchange groups the peer offered key share entries for - */ - std::vector offered_groups() const; - - /** - * @return key exchange group that was selected by a Hello Retry Request - */ - Named_Group selected_group() const; - - /** - * @returns the shared secret that was obtained by constructing this - * Key_Share object with the peer's. - * - * @note the shared secret value is std:move'd out. Multiple calls will - * result in an exception. - */ - secure_vector take_shared_secret(); - - Key_Share(TLS_Data_Reader& reader, uint16_t extension_size, Handshake_Type message_type); - - // constructor used for ClientHello msg - Key_Share(const Policy& policy, Callbacks& cb, RandomNumberGenerator& rng); - - // constructor used for HelloRetryRequest msg - explicit Key_Share(Named_Group selected_group); - - // destructor implemented in .cpp to hide Key_Share_Impl - ~Key_Share() override; - - private: - // constructor used for ServerHello - // (called via create_as_encapsulation()) - Key_Share(Group_Params selected_group, - const Key_Share& client_keyshare, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng); - - private: - class Key_Share_Impl; - std::unique_ptr m_impl; -}; - -/** - * Indicates usage or support of early data as described in RFC 8446 4.2.10. - */ -class BOTAN_UNSTABLE_API EarlyDataIndication final : public Extension { - public: - static Extension_Code static_type() { return Extension_Code::EarlyData; } - - Extension_Code type() const override { return static_type(); } - - std::vector serialize(Connection_Side whoami) const override; - - bool empty() const override; - - std::optional max_early_data_size() const { return m_max_early_data_size; } - - EarlyDataIndication(TLS_Data_Reader& reader, uint16_t extension_size, Handshake_Type message_type); - - /** - * The max_early_data_size is exclusively provided by servers when using - * this extension in the NewSessionTicket message! Otherwise it stays - * std::nullopt and results in an empty extension. (RFC 8446 4.2.10). - */ - EarlyDataIndication(std::optional max_early_data_size = std::nullopt) : - m_max_early_data_size(std::move(max_early_data_size)) {} - - private: - std::optional m_max_early_data_size; -}; - -#endif - /** * Unknown extensions are deserialized as this type */ @@ -909,8 +461,6 @@ public: std::set extension_types() const; - const std::vector>& all() const { return m_extensions; } - template T* get() const { return dynamic_cast(get(T::static_type())); @@ -921,7 +471,7 @@ return get() != nullptr; } - bool has(Extension_Code type) const { return get(type) != nullptr; } + bool has(Extension_Code type) const; size_t size() const { return m_extensions.size(); } @@ -931,12 +481,7 @@ void add(Extension* extn) { add(std::unique_ptr(extn)); } - Extension* get(Extension_Code type) const { - const auto i = std::find_if( - m_extensions.cbegin(), m_extensions.cend(), [type](const auto& ext) { return ext->type() == type; }); - - return (i != m_extensions.end()) ? i->get() : nullptr; - } + Extension* get(Extension_Code type) const; std::vector serialize(Connection_Side whoami) const; @@ -960,50 +505,62 @@ } /** - * Take the extension with the given type out of the extensions list. - * Returns a nullptr if the extension didn't exist. - */ - template - decltype(auto) take() { - std::unique_ptr out_ptr; - - auto ext = take(T::static_type()); - if(ext != nullptr) { - out_ptr.reset(dynamic_cast(ext.get())); - BOTAN_ASSERT_NOMSG(out_ptr != nullptr); - ext.release(); - } - - return out_ptr; - } - - /** - * Take the extension with the given type out of the extensions list. - * Returns a nullptr if the extension didn't exist. - */ - std::unique_ptr take(Extension_Code type); - - /** * Remove an extension from this extensions object, if it exists. * Returns true if the extension existed (and thus is now removed), * otherwise false (the extension wasn't set in the first place). * * Note: not used internally, might be used in Callbacks::tls_modify_extensions() */ - bool remove_extension(Extension_Code type) { return take(type) != nullptr; } + bool remove_extension(Extension_Code type); + + /** + * Reorder extensions for serialization. Extensions not mentioned in + * @p order retain their relative position at the front; extensions in + * @p order are appended in the given order. + */ + void reorder(const std::vector& order); + + /** + * Return the code of the extension that appears last in the encoding + * This is used for checking the position of PSK extension in TLS 1.3 + */ + std::optional last_added() const { + if(m_extension_codes.empty()) { + return {}; + } else { + return m_extension_codes.back(); + } + } Extensions() = default; Extensions(const Extensions&) = delete; Extensions& operator=(const Extensions&) = delete; Extensions(Extensions&&) = default; Extensions& operator=(Extensions&&) = default; + ~Extensions(); Extensions(TLS_Data_Reader& reader, Connection_Side side, Handshake_Type message_type) { deserialize(reader, side, message_type); } + /** + * @returns the raw bytes of the extension with the given type as they + * appeared on the wire during deserialization, or std::nullopt + * if the extension was not present or was added programmatically. + */ + std::optional> extension_raw_bytes(Extension_Code type) const { + auto it = m_raw_extension_data.find(type); + if(it != m_raw_extension_data.end()) { + return it->second; + } + return std::nullopt; + } + private: - std::vector> m_extensions; + // Kept in the order they were added + std::vector m_extension_codes; + std::map> m_extensions; + std::map> m_raw_extension_data; }; } // namespace TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_extensions_cert_status_req.cpp botan3-3.12.0+dfsg/src/lib/tls/tls_extensions_cert_status_req.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls_extensions_cert_status_req.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_extensions_cert_status_req.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -21,7 +21,7 @@ public: RFC6066_Empty_Certificate_Status_Request() = default; - RFC6066_Empty_Certificate_Status_Request(uint16_t extension_size) { + explicit RFC6066_Empty_Certificate_Status_Request(uint16_t extension_size) { if(extension_size != 0) { throw Decoding_Error("Received an unexpectedly non-empty Certificate_Status_Request"); } @@ -42,9 +42,24 @@ const uint8_t type = reader.get_byte(); if(type == 1 /* ocsp */) { + // RFC 6066 Section 8: OCSP CertificateStatusRequest is + // ResponderID responder_id_list<0..2^16-1>; + // Extensions request_extensions; + // + // for a total wire size of 1 (status_type) + 2 (resp_id_list len) + // + len_resp_id_list + 2 (request_ext len) + len_requ_ext. + if(extension_size < 5) { + throw Decoding_Error("Truncated OCSP CertificateStatusRequest"); + } const size_t len_resp_id_list = reader.get_uint16_t(); + if(len_resp_id_list > static_cast(extension_size) - 5) { + throw Decoding_Error("Inconsistent length in OCSP CertificateStatusRequest"); + } ocsp_names = reader.get_fixed(len_resp_id_list); const size_t len_requ_ext = reader.get_uint16_t(); + if(len_resp_id_list + len_requ_ext + 5 != extension_size) { + throw Decoding_Error("Inconsistent length in OCSP CertificateStatusRequest"); + } extension_bytes = reader.get_fixed(len_requ_ext); } else { // RFC 6066 does not specify anything but 'ocsp' and we @@ -65,9 +80,9 @@ }; } - std::vector ocsp_names; // NOLINT(*-non-private-member-variables-in-classes) - std::vector> ocsp_keys; // NOLINT(*-non-private-member-variables-in-classes) - std::vector extension_bytes; // NOLINT(*-non-private-member-variables-in-classes) + std::vector ocsp_names; // NOLINT(*-non-private-member-variable*) + std::vector> ocsp_keys; // NOLINT(*-non-private-member-variable*) + std::vector extension_bytes; // NOLINT(*-non-private-member-variable*) }; } // namespace @@ -78,9 +93,9 @@ std::variant; public: - Certificate_Status_Request_Internal(Contents c) : content(std::move(c)) {} + explicit Certificate_Status_Request_Internal(Contents c) : content(std::move(c)) {} - Contents content; // NOLINT(*-non-private-member-variables-in-classes) + Contents content; // NOLINT(*-non-private-member-variable*) }; Certificate_Status_Request::Certificate_Status_Request(TLS_Data_Reader& reader, @@ -166,7 +181,7 @@ return std::get(m_impl->content).response(); } -std::vector Certificate_Status_Request::serialize(Connection_Side) const { +std::vector Certificate_Status_Request::serialize(Connection_Side /*side*/) const { BOTAN_ASSERT_NONNULL(m_impl); return std::visit([](const auto& c) { return c.serialize(); }, m_impl->content); } diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_external_psk.cpp botan3-3.12.0+dfsg/src/lib/tls/tls_external_psk.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls_external_psk.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_external_psk.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,21 @@ +/* + * TLS 1.3 Preshared Key Container + * (C) 2023 Fabian Albert, René Meusel - Rohde & Schwarz Cybersecurity + * 2025,2026 Jack Lloyd + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#include + +#include +#include + +namespace Botan::TLS { + +secure_vector ExternalPSK::extract_master_secret() { + BOTAN_STATE_CHECK(!m_master_secret.empty()); + return std::exchange(m_master_secret, {}); +} + +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_external_psk.h botan3-3.12.0+dfsg/src/lib/tls/tls_external_psk.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_external_psk.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_external_psk.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,10 +10,8 @@ #define BOTAN_TLS_EXTERNAL_PSK_H_ #include -#include - -#include -#include +#include +#include namespace Botan::TLS { @@ -21,7 +19,7 @@ * This is an externally provided PreSharedKey along with its identity, master * secret and (in case of TLS 1.3) a pre-provisioned Pseudo Random Function. */ -class ExternalPSK { +class BOTAN_PUBLIC_API(3, 2) ExternalPSK final { public: ExternalPSK(const ExternalPSK&) = delete; ExternalPSK& operator=(const ExternalPSK&) = delete; @@ -30,7 +28,10 @@ ~ExternalPSK() = default; ExternalPSK(std::string_view identity, std::string_view prf_algo, secure_vector psk) : - m_identity(identity), m_prf_algo(prf_algo), m_master_secret(std::move(psk)) {} + m_identity(identity), m_prf_algo(prf_algo), m_master_secret(std::move(psk)), m_is_imported(false) {} + + ExternalPSK(std::string_view identity, std::string_view prf_algo, secure_vector psk, bool imported) : + m_identity(identity), m_prf_algo(prf_algo), m_master_secret(std::move(psk)), m_is_imported(imported) {} /** * Identity (e.g. username of the PSK owner) of the preshared key. @@ -43,10 +44,7 @@ * Returns the master secret by moving it out of this object. Do not call * this method more than once. */ - secure_vector extract_master_secret() { - BOTAN_STATE_CHECK(!m_master_secret.empty()); - return std::exchange(m_master_secret, {}); - } + secure_vector extract_master_secret(); /** * External preshared keys in TLS 1.3 must be provisioned with a @@ -55,10 +53,18 @@ */ const std::string& prf_algo() const { return m_prf_algo; } + /** + * Returns true if this PSK was derived using the PSK importer + * mechanism from RFC 9258. Imported PSKs use the "imp binder" + * label for binder computation instead of "ext binder". + */ + bool is_imported() const { return m_is_imported; } + private: std::string m_identity; std::string m_prf_algo; secure_vector m_master_secret; + bool m_is_imported; }; } // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_handshake_transitions.cpp botan3-3.12.0+dfsg/src/lib/tls/tls_handshake_transitions.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls_handshake_transitions.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_handshake_transitions.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -108,7 +108,7 @@ bool empty = true; for(auto&& t : types) { - if(mask & bitmask_for_handshake_type(t)) { + if((mask & bitmask_for_handshake_type(t)) != 0) { if(!empty) { o << combiner; } diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_handshake_transitions.h botan3-3.12.0+dfsg/src/lib/tls/tls_handshake_transitions.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_handshake_transitions.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_handshake_transitions.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,9 +10,8 @@ #ifndef BOTAN_TLS_HANDSHAKE_TRANSITIONS_H_ #define BOTAN_TLS_HANDSHAKE_TRANSITIONS_H_ -#include - #include +#include namespace Botan::TLS { diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_magic.cpp botan3-3.12.0+dfsg/src/lib/tls/tls_magic.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls_magic.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_magic.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,82 @@ +/* +* TLS Magic Values +* (C) 2004-2006,2011,2012,2015,2016 Jack Lloyd +* 2026 René Meusel - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include + +namespace Botan::TLS { + +const char* handshake_type_to_string(Handshake_Type type) { + switch(type) { + case Handshake_Type::HelloVerifyRequest: + return "hello_verify_request"; + + case Handshake_Type::HelloRequest: + return "hello_request"; + + case Handshake_Type::ClientHello: + return "client_hello"; + + case Handshake_Type::ServerHello: + return "server_hello"; + + case Handshake_Type::HelloRetryRequest: + return "hello_retry_request"; + + case Handshake_Type::Certificate: + return "certificate"; + + case Handshake_Type::CertificateUrl: + return "certificate_url"; + + case Handshake_Type::CertificateStatus: + return "certificate_status"; + + case Handshake_Type::ServerKeyExchange: + return "server_key_exchange"; + + case Handshake_Type::CertificateRequest: + return "certificate_request"; + + case Handshake_Type::ServerHelloDone: + return "server_hello_done"; + + case Handshake_Type::CertificateVerify: + return "certificate_verify"; + + case Handshake_Type::ClientKeyExchange: + return "client_key_exchange"; + + case Handshake_Type::NewSessionTicket: + return "new_session_ticket"; + + case Handshake_Type::HandshakeCCS: + return "change_cipher_spec"; + + case Handshake_Type::Finished: + return "finished"; + + case Handshake_Type::EndOfEarlyData: + return "end_of_early_data"; + + case Handshake_Type::EncryptedExtensions: + return "encrypted_extensions"; + + case Handshake_Type::KeyUpdate: + return "key_update"; + + case Handshake_Type::None: + return "invalid"; + } + + throw TLS_Exception(Alert::UnexpectedMessage, + "Unknown TLS handshake message type " + std::to_string(static_cast(type))); +} + +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_magic.h botan3-3.12.0+dfsg/src/lib/tls/tls_magic.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_magic.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_magic.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,9 +8,10 @@ #ifndef BOTAN_TLS_PROTOCOL_MAGIC_H_ #define BOTAN_TLS_PROTOCOL_MAGIC_H_ -#include - +#include #include +#include +#include //BOTAN_FUTURE_INTERNAL_HEADER(tls_magic.h) @@ -19,9 +20,9 @@ /** * Protocol Constants for SSL/TLS * -* TODO: this should not be an enum +* TODO(Botan4): this should not be an enum at all */ -enum Size_Limits : size_t { +enum Size_Limits : size_t /* NOLINT(*-enum-size,*-use-enum-class) */ { TLS_HEADER_SIZE = 5, DTLS_HEADER_SIZE = TLS_HEADER_SIZE + 8, @@ -40,7 +41,7 @@ MAX_CIPHERTEXT_SIZE_TLS13 = MAX_PLAINTEXT_SIZE + MAX_AEAD_EXPANSION_SIZE_TLS13 + 1 }; -enum class Connection_Side { +enum class Connection_Side : uint8_t { Client = 1, Server = 2, @@ -48,7 +49,18 @@ SERVER BOTAN_DEPRECATED("Use Connection_Side::Server") = Server, }; -enum class Handshake_Type { +enum class Record_Type : uint8_t { + Invalid = 0, // RFC 8446 (TLS 1.3) + + ChangeCipherSpec = 20, + Alert = 21, + Handshake = 22, + ApplicationData = 23, + + Heartbeat = 24, // RFC 6520 (TLS 1.3) +}; + +enum class Handshake_Type : uint8_t { HelloRequest = 0, ClientHello = 1, ServerHello = 2, @@ -80,6 +92,41 @@ using Transcript_Hash = std::vector; +/// @brief Used to derive the ticket's PSK from the resumption_master_secret +using Ticket_Nonce = Strong, struct Ticket_Nonce_>; + +/** + * Magic values used to signal a downgrade request to TLS 1.1. + * + * RFC 8446 4.1.3: + * TLS 1.3 has a downgrade protection mechanism embedded in the server's + * random value. TLS 1.3 servers which negotiate TLS 1.2 or below in + * response to a ClientHello MUST set the last 8 bytes of their Random + * value specially in their ServerHello. + */ +constexpr uint64_t DOWNGRADE_TLS11 = 0x444F574E47524400; + +/** + * Magic values used to signal a downgrade request to TLS 1.2. + * + * RFC 8446 4.1.3: + * TLS 1.3 has a downgrade protection mechanism embedded in the server's + * random value. TLS 1.3 servers which negotiate TLS 1.2 or below in + * response to a ClientHello MUST set the last 8 bytes of their Random + * value specially in their ServerHello. + */ +constexpr uint64_t DOWNGRADE_TLS12 = 0x444F574E47524401; + +/** + * RFC 8446 4.1.3: + * For reasons of backward compatibility with middleboxes, the + * HelloRetryRequest message uses the same structure as the ServerHello, but + * with Random set to the special value of the SHA-256 of "HelloRetryRequest": + */ +constexpr std::array HELLO_RETRY_REQUEST_MARKER = { + 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91, + 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C}; + } // namespace Botan::TLS #endif diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_messages.h botan3-3.12.0+dfsg/src/lib/tls/tls_messages.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_messages.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_messages.h 2026-05-07 01:38:28.000000000 +0000 @@ -2,6 +2,8 @@ * TLS Messages * (C) 2004-2011,2015 Jack Lloyd * 2016 Matthias Gierlings +* 2021 Elektrobit Automotive GmbH +* 2022 René Meusel, Hannes Rantzsch - neXenio GmbH * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -9,27 +11,26 @@ #ifndef BOTAN_TLS_MESSAGES_H_ #define BOTAN_TLS_MESSAGES_H_ -#include +#include +#include +#include +#include +#include #include -#include #include #include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include - namespace Botan { class Public_Key; class Credentials_Manager; +class X509_Certificate; +class X509_DN; +class RandomNumberGenerator; + +class OctetString; +typedef OctetString SymmetricKey; namespace OCSP { class Response; @@ -37,16 +38,19 @@ namespace TLS { +enum class Extension_Code : uint16_t; + class Session_Manager; +class Extensions; class Handshake_IO; class Handshake_State; class Hello_Retry_Request; class Callbacks; class Cipher_State; +class Session_with_Handle; +class Session; class Policy; -std::vector make_hello_random(RandomNumberGenerator& rng, Callbacks& cb, const Policy& policy); - /** * DTLS Hello Verify Request */ @@ -141,131 +145,25 @@ const std::vector& compression_methods() const; protected: - std::unique_ptr m_data; + std::unique_ptr m_data; // NOLINT(*non-private-member-variable*) }; -class BOTAN_UNSTABLE_API Client_Hello_12 final : public Client_Hello { - public: - class Settings final { - public: - Settings(const Protocol_Version version, std::string_view hostname = "") : - m_new_session_version(version), m_hostname(hostname) {} - - Protocol_Version protocol_version() const { return m_new_session_version; } - - const std::string& hostname() const { return m_hostname; } - - private: - const Protocol_Version m_new_session_version; - const std::string m_hostname; - }; - +/** + * Basic implementation of Client_Hello from TLS 1.2. The full implementation + * is in Client_Hello_12 in the tls12 module. This is meant to be used by the + * TLS 1.3 implementation to parse, validate and understand a downgrade request. + */ +class BOTAN_UNSTABLE_API Client_Hello_12_Shim : public Client_Hello { public: - explicit Client_Hello_12(const std::vector& buf); - - Client_Hello_12(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& reneg_info, - const Settings& client_settings, - const std::vector& next_protocols); - - Client_Hello_12(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& reneg_info, - const Session_with_Handle& session_and_handle, - const std::vector& next_protocols); + explicit Client_Hello_12_Shim(const std::vector& buf); protected: - friend class Client_Hello_13; // to allow construction by Client_Hello_13::parse() - Client_Hello_12(std::unique_ptr data); - - public: - using Client_Hello::compression_methods; - using Client_Hello::random; - - bool prefers_compressed_ec_points() const; - - bool secure_renegotiation() const; - - std::vector renegotiation_info() const; - - bool supports_session_ticket() const; - - Session_Ticket session_ticket() const; - - std::optional session_handle() const; - - bool supports_extended_master_secret() const; + using Client_Hello::Client_Hello; - bool supports_cert_status_message() const; - - bool supports_encrypt_then_mac() const; - - void update_hello_cookie(const Hello_Verify_Request& hello_verify); - - private: - void add_tls12_supported_groups_extensions(const Policy& policy); -}; - -#if defined(BOTAN_HAS_TLS_13) - -class BOTAN_UNSTABLE_API Client_Hello_13 final : public Client_Hello { - public: - /** - * Creates a client hello which might optionally use the passed-in - * @p session for resumption. In that case, this will "extract" the - * master secret from the passed-in @p session. - */ - Client_Hello_13(const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - std::string_view hostname, - const std::vector& next_protocols, - std::optional& session, - std::vector psks); - - static std::variant parse(const std::vector& buf); - - void retry(const Hello_Retry_Request& hrr, - const Transcript_Hash_State& transcript_hash_state, - Callbacks& cb, - RandomNumberGenerator& rng); - - /** - * Select the highest protocol version from the list of versions - * supported by the client. If no such version can be determind this - * returns std::nullopt. - */ - std::optional highest_supported_version(const Policy& policy) const; - - /** - * This validates that a Client Hello received after sending a Hello - * Retry Request was updated in accordance with RFC 8446 4.1.2. If issues - * are found, this method throws accordingly. - */ - void validate_updates(const Client_Hello_13& new_ch); - - private: - Client_Hello_13(std::unique_ptr data); - - /** - * If the Client Hello contains a PSK extensions with identities this will - * generate the PSK binders as described in RFC 8446 4.2.11.2. - * Note that the passed in \p transcript_hash_state might be virgin for - * the initial Client Hello and should be primed with ClientHello1 and - * HelloRetryRequest for an updated Client Hello. - */ - void calculate_psk_binders(Transcript_Hash_State transcript_hash_state); + friend class Client_Hello_13; // to allow construction by Client_Hello_13::parse() + explicit Client_Hello_12_Shim(std::unique_ptr data); }; -#endif // BOTAN_HAS_TLS_13 - class Server_Hello_Internal; /** @@ -301,375 +199,40 @@ Protocol_Version legacy_version() const; protected: - std::unique_ptr m_data; + std::unique_ptr m_data; // NOLINT(*non-private-member-variable*) }; -class BOTAN_UNSTABLE_API Server_Hello_12 final : public Server_Hello { +/** + * Basic implementation of Server_Hello from TLS 1.2. The full implementation + * is in Server_Hello_12 in the tls12 module. This is meant to be used by the + * TLS 1.3 implementation to parse, validate and understand a downgrade request. + */ +class BOTAN_UNSTABLE_API Server_Hello_12_Shim : public Server_Hello { public: - class Settings final { - public: - Settings(Session_ID new_session_id, - Protocol_Version new_session_version, - uint16_t ciphersuite, - bool offer_session_ticket) : - m_new_session_id(std::move(new_session_id)), - m_new_session_version(new_session_version), - m_ciphersuite(ciphersuite), - m_offer_session_ticket(offer_session_ticket) {} - - const Session_ID& session_id() const { return m_new_session_id; } - - Protocol_Version protocol_version() const { return m_new_session_version; } - - uint16_t ciphersuite() const { return m_ciphersuite; } - - bool offer_session_ticket() const { return m_offer_session_ticket; } - - private: - const Session_ID m_new_session_id; - Protocol_Version m_new_session_version; - uint16_t m_ciphersuite; - bool m_offer_session_ticket; - }; - - Server_Hello_12(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& secure_reneg_info, - const Client_Hello_12& client_hello, - const Settings& settings, - std::string_view next_protocol); - - Server_Hello_12(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - Callbacks& cb, - RandomNumberGenerator& rng, - const std::vector& secure_reneg_info, - const Client_Hello_12& client_hello, - const Session& resumed_session, - bool offer_session_ticket, - std::string_view next_protocol); - - explicit Server_Hello_12(const std::vector& buf); + explicit Server_Hello_12_Shim(const std::vector& buf); protected: friend class Server_Hello_13; // to allow construction by Server_Hello_13::parse() - explicit Server_Hello_12(std::unique_ptr data); + explicit Server_Hello_12_Shim(std::unique_ptr data); public: - using Server_Hello::compression_method; - using Server_Hello::extension_types; - using Server_Hello::legacy_version; - using Server_Hello::random; - /** * @returns the selected version as indicated in the legacy_version field */ - Protocol_Version selected_version() const override; - - bool secure_renegotiation() const; - - std::vector renegotiation_info() const; - - std::string next_protocol() const; - - bool supports_extended_master_secret() const; - - bool supports_encrypt_then_mac() const; - - bool supports_certificate_status_message() const; - - bool supports_session_ticket() const; - - uint16_t srtp_profile() const; - bool prefers_compressed_ec_points() const; - - /** - * Return desired downgrade version indicated by hello random, if any. - */ - std::optional random_signals_downgrade() const; -}; - -#if defined(BOTAN_HAS_TLS_13) - -class Hello_Retry_Request; - -class BOTAN_UNSTABLE_API Server_Hello_13 : public Server_Hello { - protected: - static const struct Server_Hello_Tag { - } as_server_hello; - - static const struct Hello_Retry_Request_Tag { - } as_hello_retry_request; - - static const struct Hello_Retry_Request_Creation_Tag { - } as_new_hello_retry_request; - - // These constructors are meant for instantiating Server Hellos - // after parsing a peer's message. They perform basic validation - // and are therefore not suitable for constructing a message to - // be sent to a client. - explicit Server_Hello_13(std::unique_ptr data, Server_Hello_Tag tag = as_server_hello); - explicit Server_Hello_13(std::unique_ptr data, Hello_Retry_Request_Tag tag); - void basic_validation() const; - - // Instantiate a Server Hello as response to a client's Client Hello - // (called from Server_Hello_13::create()) - Server_Hello_13(const Client_Hello_13& ch, - std::optional key_exchange_group, - Session_Manager& session_mgr, - Credentials_Manager& credentials_mgr, - RandomNumberGenerator& rng, - Callbacks& cb, - const Policy& policy); - - explicit Server_Hello_13(std::unique_ptr data, Hello_Retry_Request_Creation_Tag tag); - - public: - static std::variant create(const Client_Hello_13& ch, - bool hello_retry_request_allowed, - Session_Manager& session_mgr, - Credentials_Manager& credentials_mgr, - RandomNumberGenerator& rng, - const Policy& policy, - Callbacks& cb); - - static std::variant parse(const std::vector& buf); + Protocol_Version selected_version() const final; /** * Return desired downgrade version indicated by hello random, if any. */ std::optional random_signals_downgrade() const; - - /** - * @returns the selected version as indicated by the supported_versions extension - */ - Protocol_Version selected_version() const final; -}; - -class BOTAN_UNSTABLE_API Hello_Retry_Request final : public Server_Hello_13 { - protected: - friend class Server_Hello_13; // to allow construction by Server_Hello_13::parse() and ::create() - explicit Hello_Retry_Request(std::unique_ptr data); - Hello_Retry_Request(const Client_Hello_13& ch, Named_Group selected_group, const Policy& policy, Callbacks& cb); - - public: - Handshake_Type type() const override { return Handshake_Type::HelloRetryRequest; } - - Handshake_Type wire_type() const override { return Handshake_Type::ServerHello; } -}; - -class BOTAN_UNSTABLE_API Encrypted_Extensions final : public Handshake_Message { - public: - explicit Encrypted_Extensions(const std::vector& buf); - Encrypted_Extensions(const Client_Hello_13& client_hello, const Policy& policy, Callbacks& cb); - - Handshake_Type type() const override { return Handshake_Type::EncryptedExtensions; } - - const Extensions& extensions() const { return m_extensions; } - - std::vector serialize() const override; - - private: - Extensions m_extensions; -}; - -#endif // BOTAN_HAS_TLS_13 - -/** -* Client Key Exchange Message -*/ -class BOTAN_UNSTABLE_API Client_Key_Exchange final : public Handshake_Message { - public: - Handshake_Type type() const override { return Handshake_Type::ClientKeyExchange; } - - const secure_vector& pre_master_secret() const { return m_pre_master; } - - /** - * @returns the agreed upon PSK identity or std::nullopt if not applicable - */ - const std::optional& psk_identity() const { return m_psk_identity; } - - Client_Key_Exchange(Handshake_IO& io, - Handshake_State& state, - const Policy& policy, - Credentials_Manager& creds, - const Public_Key* server_public_key, - std::string_view hostname, - RandomNumberGenerator& rng); - - Client_Key_Exchange(const std::vector& buf, - const Handshake_State& state, - const Private_Key* server_rsa_kex_key, - Credentials_Manager& creds, - const Policy& policy, - RandomNumberGenerator& rng); - - private: - std::vector serialize() const override { return m_key_material; } - - std::vector m_key_material; - secure_vector m_pre_master; - std::optional m_psk_identity; }; /** -* Certificate Message of TLS 1.2 -*/ -class BOTAN_UNSTABLE_API Certificate_12 final : public Handshake_Message { - public: - Handshake_Type type() const override { return Handshake_Type::Certificate; } - - const std::vector& cert_chain() const { return m_certs; } - - size_t count() const { return m_certs.size(); } - - bool empty() const { return m_certs.empty(); } - - Certificate_12(Handshake_IO& io, Handshake_Hash& hash, const std::vector& certs); - - Certificate_12(const std::vector& buf, const Policy& policy); - - std::vector serialize() const override; - - private: - std::vector m_certs; -}; - -#if defined(BOTAN_HAS_TLS_13) - -class Certificate_Request_13; - -/** -* Certificate Message of TLS 1.3 -*/ -class BOTAN_UNSTABLE_API Certificate_13 final : public Handshake_Message { - public: - class Certificate_Entry { - public: - Certificate_Entry(TLS_Data_Reader& reader, const Connection_Side side, const Certificate_Type cert_type); - Certificate_Entry(X509_Certificate cert); - Certificate_Entry(std::shared_ptr raw_public_key); - - bool has_certificate() const { return m_certificate.has_value(); } - - const X509_Certificate& certificate() const; - std::shared_ptr public_key() const; - - std::vector serialize() const; - - Extensions& extensions() { return m_extensions; } - - const Extensions& extensions() const { return m_extensions; } - - private: - std::optional m_certificate; - std::shared_ptr m_raw_public_key; - Extensions m_extensions; - }; - - public: - Handshake_Type type() const override { return Handshake_Type::Certificate; } - - std::vector cert_chain() const; - - bool has_certificate_chain() const; - bool is_raw_public_key() const; - - size_t count() const { return m_entries.size(); } - - bool empty() const { return m_entries.empty(); } - - std::shared_ptr public_key() const; - const X509_Certificate& leaf() const; - - const std::vector& request_context() const { return m_request_context; } - - /** - * Create a Client Certificate message - * ... in response to a Certificate Request message. - */ - Certificate_13(const Certificate_Request_13& cert_request, - std::string_view hostname, - Credentials_Manager& credentials_manager, - Callbacks& callbacks, - Certificate_Type cert_type); - - /** - * Create a Server Certificate message - * ... in response to a Client Hello indicating the need to authenticate - * with a server certificate. - */ - Certificate_13(const Client_Hello_13& client_hello, - Credentials_Manager& credentials_manager, - Callbacks& callbacks, - Certificate_Type cert_type); - - /** - * Deserialize a Certificate message - * @param buf the serialized message - * @param policy the TLS policy - * @param side is this a Connection_Side::Server or Connection_Side::Client certificate message - * @param cert_type is the certificate type that was negotiated during the handshake - */ - Certificate_13(const std::vector& buf, - const Policy& policy, - Connection_Side side, - Certificate_Type cert_type); - - /** - * Validate a Certificate message regarding what extensions are expected based on - * previous handshake messages. Also call the tls_examine_extenions() callback - * for each entry. - * - * @param requested_extensions Extensions of Client_Hello or Certificate_Request messages - * @param cb Callback that will be called for each extension. - */ - void validate_extensions(const std::set& requested_extensions, Callbacks& cb) const; - - /** - * Verify the certificate chain - * - * @throws if verification fails. - */ - void verify(Callbacks& callbacks, - const Policy& policy, - Credentials_Manager& creds, - std::string_view hostname, - bool use_ocsp) const; - - std::vector serialize() const override; - - private: - void setup_entries(std::vector cert_chain, - const Certificate_Status_Request* csr, - Callbacks& callbacks); - void setup_entry(std::shared_ptr raw_public_key, Callbacks& callbacks); - - void verify_certificate_chain(Callbacks& callbacks, - const Policy& policy, - Credentials_Manager& creds, - std::string_view hostname, - bool use_ocsp, - Usage_Type usage_type) const; - - private: - std::vector m_request_context; - std::vector m_entries; - Connection_Side m_side; -}; - -#endif // BOTAN_HAS_TLS_13 - -/** * Certificate Status (RFC 6066) */ -class BOTAN_UNSTABLE_API Certificate_Status final : public Handshake_Message { +class BOTAN_UNSTABLE_API Certificate_Status : public Handshake_Message { public: - Handshake_Type type() const override { return Handshake_Type::CertificateStatus; } + Handshake_Type type() const final { return Handshake_Type::CertificateStatus; } //std::shared_ptr response() const { return m_response; } @@ -677,421 +240,48 @@ explicit Certificate_Status(const std::vector& buf, Connection_Side from); - Certificate_Status(Handshake_IO& io, Handshake_Hash& hash, const OCSP::Response& response); - - /* - * Create a Certificate_Status message using an already DER encoded OCSP response. - */ - Certificate_Status(Handshake_IO& io, Handshake_Hash& hash, std::vector raw_response_bytes); - - Certificate_Status(std::vector raw_response_bytes); + explicit Certificate_Status(std::vector raw_response_bytes); - std::vector serialize() const override; + std::vector serialize() const final; private: std::vector m_response; }; -/** -* Certificate Request Message (TLS 1.2) -*/ -class BOTAN_UNSTABLE_API Certificate_Request_12 final : public Handshake_Message { - public: - Handshake_Type type() const override; - - const std::vector& acceptable_cert_types() const; - - const std::vector& acceptable_CAs() const; - - const std::vector& signature_schemes() const; - - Certificate_Request_12(Handshake_IO& io, - Handshake_Hash& hash, - const Policy& policy, - const std::vector& allowed_cas); - - explicit Certificate_Request_12(const std::vector& buf); - - std::vector serialize() const override; - - private: - std::vector m_names; - std::vector m_cert_key_types; - std::vector m_schemes; -}; - -#if defined(BOTAN_HAS_TLS_13) - -class BOTAN_UNSTABLE_API Certificate_Request_13 final : public Handshake_Message { - public: - Handshake_Type type() const override; - - Certificate_Request_13(const std::vector& buf, Connection_Side side); - - //! Creates a Certificate_Request message if it is required by the configuration - //! @return std::nullopt if configuration does not require client authentication - static std::optional maybe_create(const Client_Hello_13& sni_hostname, - Credentials_Manager& cred_mgr, - Callbacks& callbacks, - const Policy& policy); - - std::vector acceptable_CAs() const; - const std::vector& signature_schemes() const; - const std::vector& certificate_signature_schemes() const; - - const Extensions& extensions() const { return m_extensions; } - - std::vector serialize() const override; - - const std::vector& context() const { return m_context; } - - private: - Certificate_Request_13(std::vector acceptable_CAs, const Policy& policy, Callbacks& callbacks); - - private: - std::vector m_context; - Extensions m_extensions; -}; - -#endif - class BOTAN_UNSTABLE_API Certificate_Verify : public Handshake_Message { public: Handshake_Type type() const override { return Handshake_Type::CertificateVerify; } Signature_Scheme signature_scheme() const { return m_scheme; } - Certificate_Verify(const std::vector& buf); + explicit Certificate_Verify(const std::vector& buf); Certificate_Verify() = default; std::vector serialize() const override; protected: - std::vector m_signature; - Signature_Scheme m_scheme; + std::vector m_signature; // NOLINT(*non-private-member-variable*) + Signature_Scheme m_scheme; // NOLINT(*non-private-member-variable*) }; /** -* Certificate Verify Message -*/ -class BOTAN_UNSTABLE_API Certificate_Verify_12 final : public Certificate_Verify { - public: - using Certificate_Verify::Certificate_Verify; - - Certificate_Verify_12(Handshake_IO& io, - Handshake_State& state, - const Policy& policy, - RandomNumberGenerator& rng, - const Private_Key* key); - - /** - * Check the signature on a certificate verify message - * @param cert the purported certificate - * @param state the handshake state - * @param policy the TLS policy - */ - bool verify(const X509_Certificate& cert, const Handshake_State& state, const Policy& policy) const; -}; - -#if defined(BOTAN_HAS_TLS_13) - -/** -* Certificate Verify Message -*/ -class BOTAN_UNSTABLE_API Certificate_Verify_13 final : public Certificate_Verify { - public: - /** - * Deserialize a Certificate message - * @param buf the serialized message - * @param side is this a Connection_Side::Server or Connection_Side::Client certificate message - */ - Certificate_Verify_13(const std::vector& buf, Connection_Side side); - - Certificate_Verify_13(const Certificate_13& certificate_message, - const std::vector& peer_allowed_schemes, - std::string_view hostname, - const Transcript_Hash& hash, - Connection_Side whoami, - Credentials_Manager& creds_mgr, - const Policy& policy, - Callbacks& callbacks, - RandomNumberGenerator& rng); - - bool verify(const Public_Key& public_key, Callbacks& callbacks, const Transcript_Hash& transcript_hash) const; - - private: - Connection_Side m_side; -}; - -#endif - -/** * Finished Message */ class BOTAN_UNSTABLE_API Finished : public Handshake_Message { public: - explicit Finished(const std::vector& buf); + explicit Finished(const std::vector& buf) : m_verification_data(buf) {} Handshake_Type type() const override { return Handshake_Type::Finished; } - std::vector verify_data() const; + std::vector verify_data() const { return m_verification_data; } - std::vector serialize() const override; + std::vector serialize() const override { return m_verification_data; } protected: using Handshake_Message::Handshake_Message; - std::vector m_verification_data; + std::vector m_verification_data; // NOLINT(*non-private-member-variable*) }; -class BOTAN_UNSTABLE_API Finished_12 final : public Finished { - public: - using Finished::Finished; - Finished_12(Handshake_IO& io, Handshake_State& state, Connection_Side side); - - bool verify(const Handshake_State& state, Connection_Side side) const; -}; - -#if defined(BOTAN_HAS_TLS_13) -class BOTAN_UNSTABLE_API Finished_13 final : public Finished { - public: - using Finished::Finished; - Finished_13(Cipher_State* cipher_state, const Transcript_Hash& transcript_hash); - - bool verify(Cipher_State* cipher_state, const Transcript_Hash& transcript_hash) const; -}; -#endif - -/** -* Hello Request Message -*/ -class BOTAN_UNSTABLE_API Hello_Request final : public Handshake_Message { - public: - Handshake_Type type() const override { return Handshake_Type::HelloRequest; } - - explicit Hello_Request(Handshake_IO& io); - explicit Hello_Request(const std::vector& buf); - - private: - std::vector serialize() const override; -}; - -/** -* Server Key Exchange Message -*/ -class BOTAN_UNSTABLE_API Server_Key_Exchange final : public Handshake_Message { - public: - Handshake_Type type() const override { return Handshake_Type::ServerKeyExchange; } - - const std::vector& params() const { return m_params; } - - bool verify(const Public_Key& server_key, const Handshake_State& state, const Policy& policy) const; - - // Only valid for certain kex types - const PK_Key_Agreement_Key& server_kex_key() const; - - /** - * @returns the agreed upon KEX group or std::nullopt if the KEX type does - * not depend on a group - */ - const std::optional& shared_group() const { return m_shared_group; } - - Server_Key_Exchange(Handshake_IO& io, - Handshake_State& state, - const Policy& policy, - Credentials_Manager& creds, - RandomNumberGenerator& rng, - const Private_Key* signing_key = nullptr); - - Server_Key_Exchange(const std::vector& buf, - Kex_Algo kex_alg, - Auth_Method sig_alg, - Protocol_Version version); - - private: - std::vector serialize() const override; - - std::unique_ptr m_kex_key; - std::optional m_shared_group; - - std::vector m_params; - - std::vector m_signature; - Signature_Scheme m_scheme; -}; - -/** -* Server Hello Done Message -*/ -class BOTAN_UNSTABLE_API Server_Hello_Done final : public Handshake_Message { - public: - Handshake_Type type() const override { return Handshake_Type::ServerHelloDone; } - - explicit Server_Hello_Done(Handshake_IO& io, Handshake_Hash& hash); - explicit Server_Hello_Done(const std::vector& buf); - - private: - std::vector serialize() const override; -}; - -/** -* New Session Ticket Message -*/ -class BOTAN_UNSTABLE_API New_Session_Ticket_12 final : public Handshake_Message { - public: - Handshake_Type type() const override { return Handshake_Type::NewSessionTicket; } - - std::chrono::seconds ticket_lifetime_hint() const { return m_ticket_lifetime_hint; } - - const Session_Ticket& ticket() const { return m_ticket; } - - New_Session_Ticket_12(Handshake_IO& io, - Handshake_Hash& hash, - Session_Ticket ticket, - std::chrono::seconds lifetime); - - New_Session_Ticket_12(Handshake_IO& io, Handshake_Hash& hash); - - explicit New_Session_Ticket_12(const std::vector& buf); - - std::vector serialize() const override; - - private: - std::chrono::seconds m_ticket_lifetime_hint; - Session_Ticket m_ticket; -}; - -#if defined(BOTAN_HAS_TLS_13) - -/// @brief Used to derive the ticket's PSK from the resumption_master_secret -using Ticket_Nonce = Strong, struct Ticket_Nonce_>; - -class BOTAN_UNSTABLE_API New_Session_Ticket_13 final : public Handshake_Message { - public: - Handshake_Type type() const override { return Handshake_Type::NewSessionTicket; } - - New_Session_Ticket_13(Ticket_Nonce nonce, - const Session& session, - const Session_Handle& handle, - Callbacks& callbacks); - - New_Session_Ticket_13(const std::vector& buf, Connection_Side from); - - std::vector serialize() const override; - - const Extensions& extensions() const { return m_extensions; } - - const Opaque_Session_Handle& handle() const { return m_handle; } - - const Ticket_Nonce& nonce() const { return m_ticket_nonce; } - - uint32_t ticket_age_add() const { return m_ticket_age_add; } - - std::chrono::seconds lifetime_hint() const { return m_ticket_lifetime_hint; } - - /** - * @return the number of bytes allowed for early data or std::nullopt - * when early data is not allowed at all - */ - std::optional early_data_byte_limit() const; - - private: - // RFC 8446 4.6.1 - // Clients MUST NOT cache tickets for longer than 7 days, regardless of - // the ticket_lifetime, and MAY delete tickets earlier based on local - // policy. A server MAY treat a ticket as valid for a shorter period - // of time than what is stated in the ticket_lifetime. - // - // ... hence we call it 'lifetime hint'. - std::chrono::seconds m_ticket_lifetime_hint; - uint32_t m_ticket_age_add; - Ticket_Nonce m_ticket_nonce; - Opaque_Session_Handle m_handle; - Extensions m_extensions; -}; - -#endif - -/** -* Change Cipher Spec -*/ -class BOTAN_UNSTABLE_API Change_Cipher_Spec final : public Handshake_Message { - public: - Handshake_Type type() const override { return Handshake_Type::HandshakeCCS; } - - std::vector serialize() const override { return std::vector(1, 1); } -}; - -#if defined(BOTAN_HAS_TLS_13) - -class BOTAN_UNSTABLE_API Key_Update final : public Handshake_Message { - public: - Handshake_Type type() const override { return Handshake_Type::KeyUpdate; } - - explicit Key_Update(bool request_peer_update); - explicit Key_Update(const std::vector& buf); - - std::vector serialize() const override; - - bool expects_reciprocation() const { return m_update_requested; } - - private: - bool m_update_requested; -}; - -namespace { -template -struct as_wrapped_references {}; - -template -struct as_wrapped_references> { - using type = std::variant...>; -}; - -template -using as_wrapped_references_t = typename as_wrapped_references::type; -} // namespace - -// Handshake message types from RFC 8446 4. -using Handshake_Message_13 = std::variant; -using Handshake_Message_13_Ref = as_wrapped_references_t; - -using Post_Handshake_Message_13 = std::variant; - -// Key_Update is handled generically by the Channel. The messages assigned -// to those variants are the ones that need to be handled by the specific -// client and/or server implementations. -using Server_Post_Handshake_13_Message = std::variant; -using Client_Post_Handshake_13_Message = std::variant; - -using Server_Handshake_13_Message = std::variant; -using Server_Handshake_13_Message_Ref = as_wrapped_references_t; - -using Client_Handshake_13_Message = std::variant; -using Client_Handshake_13_Message_Ref = as_wrapped_references_t; - -#endif // BOTAN_HAS_TLS_13 - } // namespace TLS } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_messages_internal.h botan3-3.12.0+dfsg/src/lib/tls/tls_messages_internal.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_messages_internal.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_messages_internal.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,161 @@ +/* +* TLS Client/Server Hello Internal Data Containers +* (C) 2004-2026 Jack Lloyd +* 2021 Elektrobit Automotive GmbH +* 2022 René Meusel, Hannes Rantzsch - neXenio GmbH +* 2026 René Meusel - Rohde & Schwarz Cybersecurity GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TLS_MESSAGES_INTERNAL_H_ +#define BOTAN_TLS_MESSAGES_INTERNAL_H_ + +#include +#include +#include +#include + +namespace Botan { +class RandomNumberGenerator; + +namespace TLS { +class Callbacks; +class Policy; +} // namespace TLS + +} // namespace Botan + +namespace Botan::TLS { + +/** + * Generate a (client) hello random value. + * + * Depending on the policy, the RNG output may be hashed and if TLS 1.2 is + * offered, the random value may contain a timestamp. + */ +std::vector make_hello_random(RandomNumberGenerator& rng, Callbacks& cb, const Policy& policy); + +/** + * Generate a server hello random value for the given protocol version. + * + * Depending on the protocol version, the random value is generated differently. + * For instance, TLS 1.2 requested a timestamp in the random value. Also, when + * downgrading to TLS 1.2 from a peer that could also negotiate TLS 1.3, the + * random value must be slightly modified to signal the downgrade. + */ +std::vector make_server_hello_random(RandomNumberGenerator& rng, + Protocol_Version offered_version, + Callbacks& cb, + const Policy& policy); + +/** + * Version-agnostic internal client hello data container that allows + * parsing Client_Hello messages without prior knowledge of the contained + * protocol version. + */ +class Client_Hello_Internal { + public: + Client_Hello_Internal() : m_comp_methods({0}) {} + + explicit Client_Hello_Internal(const std::vector& buf); + + /** + * This distinguishes between a TLS 1.3 compliant Client Hello (containing + * the "supported_version" extension) and legacy Client Hello messages. + * + * @return TLS 1.3 if the Client Hello contains "supported_versions", or + * the content of the "legacy_version" version field if it + * indicates (D)TLS 1.2 or older, or + * (D)TLS 1.2 if the "legacy_version" was some other odd value. + */ + Protocol_Version version() const; + + Protocol_Version legacy_version() const { return m_legacy_version; } + + const Session_ID& session_id() const { return m_session_id; } + + const std::vector& random() const { return m_random; } + + const std::vector& ciphersuites() const { return m_suites; } + + const std::vector& comp_methods() const { return m_comp_methods; } + + const std::vector& hello_cookie() const { return m_hello_cookie; } + + const std::vector& hello_cookie_input_bits() const { return m_cookie_input_bits; } + + const Extensions& extensions() const { return m_extensions; } + + Extensions& extensions() { return m_extensions; } + + public: + Protocol_Version m_legacy_version; // NOLINT(*-non-private-member-variable*) + Session_ID m_session_id; // NOLINT(*-non-private-member-variable*) + std::vector m_random; // NOLINT(*-non-private-member-variable*) + std::vector m_suites; // NOLINT(*-non-private-member-variable*) + std::vector m_comp_methods; // NOLINT(*-non-private-member-variable*) + Extensions m_extensions; // NOLINT(*-non-private-member-variable*) + + // These fields are only for DTLS: + std::vector m_hello_cookie; // NOLINT(*-non-private-member-variable*) + std::vector m_cookie_input_bits; // NOLINT(*-non-private-member-variable*) +}; + +/** +* Version-agnostic internal server hello data container that allows +* parsing Server_Hello messages without prior knowledge of the contained +* protocol version. +*/ +class Server_Hello_Internal final { + public: + /** + * Deserialize a Server Hello message + */ + explicit Server_Hello_Internal(const std::vector& buf); + + Server_Hello_Internal(Protocol_Version lv, + Session_ID sid, + std::vector r, + const uint16_t cs, + const uint8_t cm, + bool is_hrr = false) : + m_legacy_version(lv), + m_session_id(std::move(sid)), + m_random(std::move(r)), + m_is_hello_retry_request(is_hrr), + m_ciphersuite(cs), + m_comp_method(cm) {} + + Protocol_Version version() const; + + Protocol_Version legacy_version() const { return m_legacy_version; } + + const Session_ID& session_id() const { return m_session_id; } + + const std::vector& random() const { return m_random; } + + uint16_t ciphersuite() const { return m_ciphersuite; } + + uint8_t comp_method() const { return m_comp_method; } + + bool is_hello_retry_request() const { return m_is_hello_retry_request; } + + const Extensions& extensions() const { return m_extensions; } + + Extensions& extensions() { return m_extensions; } + + private: + Protocol_Version m_legacy_version; + Session_ID m_session_id; + std::vector m_random; + bool m_is_hello_retry_request; + uint16_t m_ciphersuite; + uint8_t m_comp_method; + + Extensions m_extensions; +}; + +} // namespace Botan::TLS + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_policy.cpp botan3-3.12.0+dfsg/src/lib/tls/tls_policy.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls_policy.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_policy.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -14,7 +14,9 @@ #include #include #include +#include #include +#include #include #include @@ -27,7 +29,7 @@ std::vector Policy::allowed_signature_schemes() const { std::vector schemes; - for(Signature_Scheme scheme : Signature_Scheme::all_available_schemes()) { + for(const Signature_Scheme scheme : Signature_Scheme::all_available_schemes()) { const bool sig_allowed = allowed_signature_method(scheme.algorithm_name()); const bool hash_allowed = allowed_signature_hash(scheme.hash_function_name()); @@ -51,9 +53,9 @@ std::vector Policy::allowed_ciphers() const { return { //"AES-256/OCB(12)", - "ChaCha20Poly1305", "AES-256/GCM", "AES-128/GCM", + "ChaCha20Poly1305", //"AES-256/CCM", //"AES-128/CCM", //"AES-256/CCM(8)", @@ -182,10 +184,16 @@ Group_Params::SECP256R1, -#if defined(BOTAN_HAS_X25519) && defined(BOTAN_HAS_ML_KEM) && defined(BOTAN_HAS_TLS_13_PQC) +#if defined(BOTAN_HAS_ML_KEM) && defined(BOTAN_HAS_TLS_13_PQC) + +#if defined(BOTAN_HAS_X25519) Group_Params_Code::HYBRID_X25519_ML_KEM_768, #endif + Group_Params_Code::HYBRID_SECP256R1_ML_KEM_768, + Group_Params_Code::HYBRID_SECP384R1_ML_KEM_1024, +#endif + #if defined(BOTAN_HAS_X448) Group_Params::X448, #endif @@ -224,7 +232,7 @@ /* * If for some reason no pure ECC groups are enabled then simply - * send a share of whatever the policys top preference is. + * send a share of whatever the policy's top preference is. */ if(groups_to_offer.empty()) { groups_to_offer.push_back(supported_groups.front()); @@ -237,6 +245,10 @@ return 2048; } +size_t Policy::maximum_dh_group_size() const { + return 8192; +} + size_t Policy::minimum_ecdsa_group_size() const { // Here we are at the mercy of whatever the CA signed, but most certs should be 256 bit by now return 256; @@ -325,6 +337,7 @@ } #endif + BOTAN_UNUSED(version); return false; } @@ -333,18 +346,16 @@ if(acceptable_protocol_version(Protocol_Version::DTLS_V12)) { return Protocol_Version::DTLS_V12; } - throw Invalid_State("Policy forbids all available DTLS version"); } else { -#if defined(BOTAN_HAS_TLS_13) if(acceptable_protocol_version(Protocol_Version::TLS_V13)) { return Protocol_Version::TLS_V13; } -#endif if(acceptable_protocol_version(Protocol_Version::TLS_V12)) { return Protocol_Version::TLS_V12; } - throw Invalid_State("Policy forbids all available TLS version"); } + + throw Invalid_State("Policy forbids all available TLS version"); } bool Policy::acceptable_ciphersuite(const Ciphersuite& ciphersuite) const { @@ -404,6 +415,10 @@ return true; } +bool Policy::require_extended_master_secret() const { + return true; +} + std::optional Policy::record_size_limit() const { return std::nullopt; } @@ -452,8 +467,20 @@ return false; } +size_t Policy::maximum_handshake_message_size() const { + return 65536; +} + size_t Policy::maximum_certificate_chain_size() const { - return 0; + return 65536; +} + +uint64_t Policy::minimum_key_update_interval_ms() const { + return 1000; +} + +size_t Policy::maximum_session_tickets_per_connection() const { + return 10; } // 1 second initial timeout, 60 second max - see RFC 6347 sec 4.2.4.1 @@ -602,12 +629,12 @@ throw Invalid_State("Policy does not allow any available cipher suite"); } - Ciphersuite_Preference_Ordering order(ciphers, macs, kex, sigs); + const Ciphersuite_Preference_Ordering order(ciphers, macs, kex, sigs); std::sort(ciphersuites.begin(), ciphersuites.end(), order); std::vector ciphersuite_codes; ciphersuite_codes.reserve(ciphersuites.size()); - for(auto i : ciphersuites) { + for(const auto& i : ciphersuites) { ciphersuite_codes.push_back(i.ciphersuite_code()); } return ciphersuite_codes; @@ -686,6 +713,7 @@ print_bool(o, "hide_unknown_users", hide_unknown_users()); print_bool(o, "server_uses_own_ciphersuite_preferences", server_uses_own_ciphersuite_preferences()); print_bool(o, "negotiate_encrypt_then_mac", negotiate_encrypt_then_mac()); + print_bool(o, "require_extended_master_secret", require_extended_master_secret()); print_bool(o, "support_cert_status_message", support_cert_status_message()); print_bool(o, "tls_13_middlebox_compatibility_mode", tls_13_middlebox_compatibility_mode()); print_vec(o, "accepted_client_certificate_types", accepted_client_certificate_types()); @@ -711,7 +739,7 @@ } std::vector Strict_Policy::allowed_ciphers() const { - return {"ChaCha20Poly1305", "AES-256/GCM", "AES-128/GCM"}; + return {"AES-256/GCM", "AES-128/GCM", "ChaCha20Poly1305"}; } std::vector Strict_Policy::allowed_signature_hashes() const { diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_policy.h botan3-3.12.0+dfsg/src/lib/tls/tls_policy.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_policy.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_policy.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,9 +10,7 @@ #ifndef BOTAN_TLS_POLICY_H_ #define BOTAN_TLS_POLICY_H_ -#include -#include -#include +#include #include #include #include @@ -25,11 +23,14 @@ namespace TLS { +class Ciphersuite; +class Signature_Scheme; + /** * TLS Policy Base Class * Inherit and overload as desired to suit local policy concerns */ -class BOTAN_PUBLIC_API(2, 0) Policy { +class BOTAN_PUBLIC_API(2, 0) Policy /* NOLINT(*-special-member-functions) */ { public: /** * Allow ssl key log file @@ -276,10 +277,21 @@ * to reconnect after disabling ephemeral Diffie-Hellman. * * Default: 2048 bits + * + * This only affects the TLS 1.2 client */ virtual size_t minimum_dh_group_size() const; /** + * Largest DH group size (in bits) the client will accept from a server. + * + * Default: 8192 bits (the largest FFDHE group) + * + * This only affects the TLS 1.2 client + */ + virtual size_t maximum_dh_group_size() const; + + /** * For ECDSA authenticated ciphersuites, the smallest key size the * client will accept. * This policy is currently only enforced on the server by the client. @@ -430,6 +442,17 @@ virtual bool negotiate_encrypt_then_mac() const; /** + * Require that TLS 1.2 / DTLS 1.2 handshakes use the Extended Master + * Secret extension (RFC 7627). When true, both the server and the client + * abort fresh handshakes whose peer did not negotiate EMS. RFC 9325 4.4 + * recommends requiring this extension. + * + * @note Has no effect for TLS 1.3 connections, where the equivalent + * binding is built in. + */ + virtual bool require_extended_master_secret() const; + + /** * Defines the maximum TLS record length for TLS connections. * This is based on the Record Size Limit extension described in RFC 8449. * By default (i.e. if std::nullopt is returned), TLS clients will omit @@ -514,12 +537,35 @@ virtual size_t dtls_maximum_timeout() const; /** + * @return the maximum size of a single handshake message, in bytes. + * Messages larger than this will be rejected prior to processing. + * Return 0 to disable this and accept any size. + */ + virtual size_t maximum_handshake_message_size() const; + + /** * @return the maximum size of the certificate chain, in bytes. * Return 0 to disable this and accept any size. */ virtual size_t maximum_certificate_chain_size() const; /** + * @return the minimum number of milliseconds that must elapse between + * two received KeyUpdate messages. If a KeyUpdate arrives sooner than + * this interval after the previous one, the connection is terminated. + * Return 0 to disable rate limiting. + * @note Only applies to TLS 1.3 connections. + */ + virtual uint64_t minimum_key_update_interval_ms() const; + + /** + * @return the maximum number of NewSessionTicket messages to accept + * from a server on a single connection. Return 0 to disable the limit. + * @note Only applies to TLS 1.3 client connections. + */ + virtual size_t maximum_session_tickets_per_connection() const; + + /** * @note Has no effect for TLS 1.3 connections. */ virtual bool allow_resumption_for_renegotiation() const; @@ -766,6 +812,8 @@ bool negotiate_encrypt_then_mac() const override; + bool require_extended_master_secret() const override; + std::optional record_size_limit() const override; bool support_cert_status_message() const override; diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_reader.cpp botan3-3.12.0+dfsg/src/lib/tls/tls_reader.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls_reader.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_reader.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,25 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan::TLS { + +void TLS_Data_Reader::assert_at_least(size_t n) const { + const size_t left = remaining_bytes(); + if(left < n) { + throw_decode_error(fmt("Expected {} bytes remaining, only {} left", n, left)); + } +} + +void TLS_Data_Reader::throw_decode_error(std::string_view why) const { + throw Decoding_Error(fmt("Invalid {}: {}", m_typename, why)); +} + +} // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_reader.h botan3-3.12.0+dfsg/src/lib/tls/tls_reader.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_reader.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_reader.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,10 +8,10 @@ #ifndef BOTAN_TLS_READER_H_ #define BOTAN_TLS_READER_H_ -#include +#include #include -#include #include +#include #include #include #include @@ -21,7 +21,7 @@ /** * Helper class for decoding TLS protocol messages */ -class TLS_Data_Reader final { +class BOTAN_TEST_API TLS_Data_Reader final { public: TLS_Data_Reader(const char* type, std::span buf_in) : m_typename(type), m_buf(buf_in), m_offset(0) {} @@ -38,10 +38,14 @@ bool has_remaining() const { return (remaining_bytes() > 0); } - std::vector get_remaining() { return std::vector(m_buf.begin() + m_offset, m_buf.end()); } + std::vector get_remaining() { + const std::span rest = m_buf.subspan(m_offset); + return std::vector(rest.begin(), rest.end()); + } std::vector get_data_read_so_far() { - return std::vector(m_buf.begin(), m_buf.begin() + m_offset); + const std::span first = m_buf.first(m_offset); + return std::vector(first.begin(), first.end()); } void discard_next(size_t bytes) { @@ -51,21 +55,22 @@ uint32_t get_uint32_t() { assert_at_least(4); - uint32_t result = make_uint32(m_buf[m_offset], m_buf[m_offset + 1], m_buf[m_offset + 2], m_buf[m_offset + 3]); + const uint32_t result = + make_uint32(m_buf[m_offset], m_buf[m_offset + 1], m_buf[m_offset + 2], m_buf[m_offset + 3]); m_offset += 4; return result; } uint32_t get_uint24_t() { assert_at_least(3); - uint32_t result = make_uint32(0, m_buf[m_offset], m_buf[m_offset + 1], m_buf[m_offset + 2]); + const uint32_t result = make_uint32(0, m_buf[m_offset], m_buf[m_offset + 1], m_buf[m_offset + 2]); m_offset += 3; return result; } uint16_t get_uint16_t() { assert_at_least(2); - uint16_t result = make_uint16(m_buf[m_offset], m_buf[m_offset + 1]); + const uint16_t result = make_uint16(m_buf[m_offset], m_buf[m_offset + 1]); m_offset += 2; return result; } @@ -77,7 +82,7 @@ uint8_t get_byte() { assert_at_least(1); - uint8_t result = m_buf[m_offset]; + const uint8_t result = m_buf[m_offset]; m_offset += 1; return result; } @@ -117,8 +122,7 @@ std::string get_string(size_t len_bytes, size_t min_bytes, size_t max_bytes) { std::vector v = get_range_vector(len_bytes, min_bytes, max_bytes); - - return std::string(cast_uint8_ptr_to_char(v.data()), v.size()); + return bytes_to_string(v); } template @@ -157,16 +161,9 @@ return num_elems; } - void assert_at_least(size_t n) const { - if(m_buf.size() - m_offset < n) { - throw_decode_error("Expected " + std::to_string(n) + " bytes remaining, only " + - std::to_string(m_buf.size() - m_offset) + " left"); - } - } + void assert_at_least(size_t n) const; - [[noreturn]] void throw_decode_error(std::string_view why) const { - throw Decoding_Error(fmt("Invalid {}: {}", m_typename, why)); - } + [[noreturn]] void throw_decode_error(std::string_view why) const; const char* m_typename; std::span m_buf; @@ -177,18 +174,18 @@ * Helper function for encoding length-tagged vectors */ template -void append_tls_length_value(std::vector& buf, const T* vals, size_t vals_size, size_t tag_size) { +inline void append_tls_length_value(std::vector& buf, + const T* vals, + size_t vals_size, + size_t tag_size) { const size_t T_size = sizeof(T); const size_t val_bytes = T_size * vals_size; - if(tag_size != 1 && tag_size != 2 && tag_size != 3) { - throw Invalid_Argument("append_tls_length_value: invalid tag size"); - } + BOTAN_ARG_CHECK(tag_size == 1 || tag_size == 2 || tag_size == 3, "Invalid TLS tag size"); - if((tag_size == 1 && val_bytes > 255) || (tag_size == 2 && val_bytes > 65535) || - (tag_size == 3 && val_bytes > 16777215)) { - throw Invalid_Argument("append_tls_length_value: value too large"); - } + const size_t max_possible_size = (1 << (8 * tag_size)) - 1; + + BOTAN_ARG_CHECK(val_bytes <= max_possible_size, "Value too large to encode"); for(size_t i = 0; i != tag_size; ++i) { buf.push_back(get_byte_var(sizeof(val_bytes) - tag_size + i, val_bytes)); @@ -201,14 +198,21 @@ } } -template -void append_tls_length_value(std::vector& buf, const std::vector& vals, size_t tag_size) { +template +inline void append_tls_length_value(std::vector& buf, std::span vals, size_t tag_size) { append_tls_length_value(buf, vals.data(), vals.size(), tag_size); } +template +inline void append_tls_length_value(std::vector& buf, + const std::vector& vals, + size_t tag_size) { + append_tls_length_value(buf, std::span{vals}, tag_size); +} + template -void append_tls_length_value(std::vector& buf, std::string_view str, size_t tag_size) { - append_tls_length_value(buf, cast_char_ptr_to_uint8(str.data()), str.size(), tag_size); +inline void append_tls_length_value(std::vector& buf, std::string_view str, size_t tag_size) { + append_tls_length_value(buf, as_span_of_bytes(str), tag_size); } } // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_server.cpp botan3-3.12.0+dfsg/src/lib/tls/tls_server.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls_server.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_server.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,12 +10,14 @@ #include -#include -#include -#include -#include +#include +#include +#include + +#if defined(BOTAN_HAS_TLS_12) + #include +#endif -#include #if defined(BOTAN_HAS_TLS_13) #include #endif @@ -34,19 +36,27 @@ size_t io_buf_sz) { const auto max_version = policy->latest_supported_version(is_datagram); - if(!max_version.is_pre_tls_13()) { #if defined(BOTAN_HAS_TLS_13) + if(!max_version.is_pre_tls_13()) { m_impl = std::make_unique(callbacks, session_manager, creds, policy, rng); if(m_impl->expects_downgrade()) { m_impl->set_io_buffer_size(io_buf_sz); } -#else - throw Not_Implemented("TLS 1.3 server is not available in this build"); + + return; + } #endif - } else { + +#if defined(BOTAN_HAS_TLS_12) + if(max_version.is_pre_tls_13()) { m_impl = std::make_unique(callbacks, session_manager, creds, policy, rng, is_datagram, io_buf_sz); + return; } +#endif + + BOTAN_UNUSED(max_version, callbacks, session_manager, creds, policy, rng, is_datagram, io_buf_sz); + throw Not_Implemented("Requested TLS server version is not available in this build"); } Server::~Server() = default; @@ -54,6 +64,9 @@ size_t Server::from_peer(std::span data) { auto read = m_impl->from_peer(data); +#if defined(BOTAN_HAS_TLS_12) + // If TLS 1.2 is not available, we will never downgrade, the downgrade info + // won't even be created and `is_downgrading()` would always return false. if(m_impl->is_downgrading()) { auto info = m_impl->extract_downgrade_info(); m_impl = std::make_unique(*info); @@ -61,6 +74,7 @@ // replay peer data received so far read = m_impl->from_peer(info->peer_transcript); } +#endif return read; } diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_server.h botan3-3.12.0+dfsg/src/lib/tls/tls_server.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_server.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_server.h 2026-05-07 01:38:28.000000000 +0000 @@ -12,13 +12,17 @@ #define BOTAN_TLS_SERVER_H_ #include +#include // TODO(Botan4) not necessary here, remove #include -#include +#include // TODO(Botan4) not necessary here, remove #include namespace Botan::TLS { +class Callbacks; +class Session_Manager; class Channel_Impl; +class Policy; /** * TLS Server @@ -118,6 +122,11 @@ bool timeout_check() override; + Server(const Server& other) = delete; + Server(Server&& other) = default; + Server& operator=(const Server& other) = delete; + Server& operator=(Server&& other) = delete; + private: std::unique_ptr m_impl; }; diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_server_info.h botan3-3.12.0+dfsg/src/lib/tls/tls_server_info.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_server_info.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_server_info.h 2026-05-07 01:38:28.000000000 +0000 @@ -21,15 +21,15 @@ /** * An empty server info - nothing known */ - Server_Information() : m_hostname(), m_service(), m_port(0) {} + Server_Information() = default; /** * @param hostname the host's DNS name, if known * @param port specifies the protocol port of the server (eg for * TCP/UDP). Zero represents unknown. */ - Server_Information(std::string_view hostname, uint16_t port = 0) : - m_hostname(hostname), m_service(), m_port(port) {} + BOTAN_FUTURE_EXPLICIT Server_Information(std::string_view hostname, uint16_t port = 0) : + m_hostname(hostname), m_port(port) {} /** * @param hostname the host's DNS name, if known @@ -63,8 +63,9 @@ bool empty() const { return m_hostname.empty(); } private: - std::string m_hostname, m_service; - uint16_t m_port; + std::string m_hostname; + std::string m_service; + uint16_t m_port = 0; }; inline bool operator==(const Server_Information& a, const Server_Information& b) { diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_session.cpp botan3-3.12.0+dfsg/src/lib/tls/tls_session.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls_session.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_session.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -15,12 +15,18 @@ #include #include #include -#include #include +#include +#include #include #include #include +#if defined(BOTAN_HAS_TLS_13) + #include + #include +#endif + #include namespace Botan::TLS { @@ -59,7 +65,7 @@ return std::get(m_handle); } - // Opaque handles can mimick as a Session_ID if they are short enough + // Opaque handles can mimic as a Session_ID if they are short enough if(is_opaque_handle()) { const auto& handle = std::get(m_handle); if(handle.size() <= 32) { @@ -75,7 +81,7 @@ return std::get(m_handle); } - // Opaque handles can mimick 'normal' Session_Tickets at any time + // Opaque handles can mimic 'normal' Session_Tickets at any time if(is_opaque_handle()) { return Session_Ticket(std::get(m_handle).get()); } @@ -83,6 +89,37 @@ return std::nullopt; } +Session_Base::Session_Base() = default; + +Session_Base::~Session_Base() = default; + +Session_Base::Session_Base(const Session_Base& other) = default; +Session_Base& Session_Base::operator=(const Session_Base& other) = default; + +Session_Base::Session_Base(Session_Base&& other) noexcept = default; +Session_Base& Session_Base::operator=(Session_Base&& other) noexcept = default; + +Session_Base::Session_Base(std::chrono::system_clock::time_point start_time, + Protocol_Version version, + uint16_t ciphersuite, + Connection_Side connection_side, + uint16_t srtp_profile, + bool extended_master_secret, + bool encrypt_then_mac, + const std::vector& peer_certs, + std::shared_ptr peer_raw_public_key, + Server_Information server_info) : + m_start_time(start_time), + m_version(version), + m_ciphersuite(ciphersuite), + m_connection_side(connection_side), + m_srtp_profile(srtp_profile), + m_extended_master_secret(extended_master_secret), + m_encrypt_then_mac(encrypt_then_mac), + m_peer_certs(peer_certs), + m_peer_raw_public_key(std::move(peer_raw_public_key)), + m_server_info(std::move(server_info)) {} + Ciphersuite Session_Base::ciphersuite() const { auto suite = Ciphersuite::by_id(m_ciphersuite); if(!suite.has_value()) { @@ -142,7 +179,7 @@ Session_Summary::Session_Summary(const Server_Hello_13& server_hello, Connection_Side side, - std::vector peer_certs, + const std::vector& peer_certs, std::shared_ptr peer_raw_public_key, std::optional psk_identity, bool session_was_resumed, @@ -165,7 +202,7 @@ // TLS 1.3 uses AEADs, so technically encrypt-then-MAC is not applicable. false, - std::move(peer_certs), + peer_certs, std::move(peer_raw_public_key), std::move(server_info)), m_external_psk_identity(std::move(psk_identity)), @@ -178,13 +215,13 @@ std::optional group = [&]() -> std::optional { if(psk_used() || was_resumption()) { - if(const auto keyshare = server_hello.extensions().get()) { + if(auto* const keyshare = server_hello.extensions().get()) { return keyshare->selected_group(); } else { return {}; } } else { - const auto keyshare = server_hello.extensions().get(); + auto* const keyshare = server_hello.extensions().get(); BOTAN_ASSERT_NONNULL(keyshare); return keyshare->selected_group(); } @@ -269,58 +306,30 @@ BOTAN_ARG_CHECK(!version.is_pre_tls_13(), "Instantiated a TLS 1.3 session object with a TLS version older than 1.3"); } -Session::Session(secure_vector&& session_psk, - const std::optional& max_early_data_bytes, - std::chrono::seconds lifetime_hint, - const std::vector& peer_certs, - std::shared_ptr peer_raw_public_key, - const Client_Hello_13& client_hello, - const Server_Hello_13& server_hello, - Callbacks& callbacks, - RandomNumberGenerator& rng) : - Session_Base(callbacks.tls_current_timestamp(), - server_hello.selected_version(), - server_hello.ciphersuite(), - Connection_Side::Server, - 0, - true, - false, // see constructor above for rationales - peer_certs, - std::move(peer_raw_public_key), - Server_Information(client_hello.sni_hostname())), - m_master_secret(std::move(session_psk)), - m_early_data_allowed(max_early_data_bytes.has_value()), - m_max_early_data_bytes(max_early_data_bytes.value_or(0)), - m_ticket_age_add(load_be(rng.random_vec(4).data(), 0)), - m_lifetime_hint(lifetime_hint) { - BOTAN_ARG_CHECK(!m_version.is_pre_tls_13(), - "Instantiated a TLS 1.3 session object with a TLS version older than 1.3"); -} - #endif Session::Session(std::string_view pem) : Session(PEM_Code::decode_check_label(pem, "TLS SESSION")) {} -Session::Session(std::span ber_data) { +Session::Session(std::span ber_data) /* NOLINT(*-member-init) */ { uint8_t side_code = 0; std::vector raw_pubkey_or_empty; ASN1_String server_hostname; ASN1_String server_service; - size_t server_port; + size_t server_port = 0; - uint8_t major_version = 0, minor_version = 0; + uint8_t major_version = 0; + uint8_t minor_version = 0; size_t start_time = 0; size_t srtp_profile = 0; uint16_t ciphersuite_code = 0; uint64_t lifetime_hint = 0; - BER_Decoder(ber_data.data(), ber_data.size()) + BER_Decoder(ber_data, BER_Decoder::Limits::DER()) .start_sequence() - .decode_and_check(static_cast(TLS_SESSION_PARAM_STRUCT_VERSION), - "Unknown version in serialized TLS session") + .decode_and_check(TLS_SESSION_PARAM_STRUCT_VERSION, "Unknown version in serialized TLS session") .decode_integer_type(start_time) .decode_integer_type(major_version) .decode_integer_type(minor_version) @@ -352,7 +361,18 @@ m_ciphersuite = ciphersuite_code; m_version = Protocol_Version(major_version, minor_version); m_start_time = std::chrono::system_clock::from_time_t(start_time); + if(side_code != static_cast(Connection_Side::Client) && + side_code != static_cast(Connection_Side::Server)) { + throw Decoding_Error("Serialized TLS session contains unknown connection side " + std::to_string(side_code)); + } m_connection_side = static_cast(side_code); + + const bool valid_secret_size = m_version.is_pre_tls_13() + ? (m_master_secret.size() == 48) + : (m_master_secret.size() == 32 || m_master_secret.size() == 48); + if(!valid_secret_size) { + throw Decoding_Error("Serialized TLS session has master_secret of unexpected length"); + } m_srtp_profile = static_cast(srtp_profile); m_server_info = @@ -371,7 +391,7 @@ return DER_Encoder() .start_sequence() - .encode(static_cast(TLS_SESSION_PARAM_STRUCT_VERSION)) + .encode(TLS_SESSION_PARAM_STRUCT_VERSION) .encode(static_cast(std::chrono::system_clock::to_time_t(m_start_time))) .encode(static_cast(m_version.major_version())) .encode(static_cast(m_version.minor_version())) @@ -453,7 +473,7 @@ std::vector buf; buf.reserve(TLS_SESSION_CRYPT_OVERHEAD + bits.size()); buf.resize(TLS_SESSION_CRYPT_MAGIC_LEN); - store_be(TLS_SESSION_CRYPT_MAGIC, &buf[0]); + store_be(TLS_SESSION_CRYPT_MAGIC, &buf[0]); // NOLINT(*container-data-pointer) buf += key_name; buf += key_seed; buf += aead_nonce; @@ -479,10 +499,10 @@ } BufferSlicer sub(in); - const auto magic = sub.take(TLS_SESSION_CRYPT_MAGIC_LEN).data(); - const auto key_name = sub.take(TLS_SESSION_CRYPT_KEY_NAME_LEN).data(); - const auto key_seed = sub.take(TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN).data(); - const auto aead_nonce = sub.take(TLS_SESSION_CRYPT_AEAD_NONCE_LEN).data(); + const auto* const magic = sub.take(TLS_SESSION_CRYPT_MAGIC_LEN).data(); + const auto* const key_name = sub.take(TLS_SESSION_CRYPT_KEY_NAME_LEN).data(); + const auto* const key_seed = sub.take(TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN).data(); + const auto* const aead_nonce = sub.take(TLS_SESSION_CRYPT_AEAD_NONCE_LEN).data(); auto ctext = sub.copy_as_secure_vector(sub.remaining()); if(load_be(magic, 0) != TLS_SESSION_CRYPT_MAGIC) { diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_session.h botan3-3.12.0+dfsg/src/lib/tls/tls_session.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_session.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_session.h 2026-05-07 01:38:28.000000000 +0000 @@ -15,117 +15,21 @@ #include #include #include +#include #include -#include - -#include #include +#include #include -#include - -namespace Botan::TLS { - -// Different flavors of session handles are used, depending on the usage -// scenario and the TLS protocol version. - -/// @brief holds a TLS 1.2 session ID for stateful resumption -using Session_ID = Strong, struct Session_ID_>; - -/// @brief holds a TLS 1.2 session ticket for stateless resumption -using Session_Ticket = Strong, struct Session_Ticket_>; - -/// @brief holds an opaque session handle as used in TLS 1.3 that could be -/// either a ticket for stateless resumption or a database handle. -using Opaque_Session_Handle = Strong, struct Opaque_Session_Handle_>; - -inline auto operator<(const Session_ID& id1, const Session_ID& id2) { - // TODO: C++20 better use std::lexicographical_compare_three_way - // that was not available on all target platforms at the time - // of this writing. - return std::lexicographical_compare(id1.begin(), id1.end(), id2.begin(), id2.end()); -} - -/** - * @brief Helper class to embody a session handle in all protocol versions - * - * Sessions in TLS 1.2 are identified by an arbitrary and unique ID of up to - * 32 bytes or by a self-contained arbitrary-length ticket (RFC 5077). - * - * TLS 1.3 does not distinct between the two and handles both as tickets. Also - * a TLS 1.3 server can issue multiple tickets in one connection and the - * resumption mechanism is compatible with the PSK establishment. - * - * Concrete implementations of Session_Manager use this helper to distinguish - * the different states and manage sessions for TLS 1.2 and 1.3 connections. - * - * Note that all information stored in a Session_Handle might be transmitted in - * unprotected form. Hence, it should not contain any confidential information. - */ -class BOTAN_PUBLIC_API(3, 0) Session_Handle { - public: - /** - * Constructs a Session_Handle from a session ID which is an - * arbitrary byte vector that must be 32 bytes long at most. - */ - Session_Handle(Session_ID id) : m_handle(std::move(id)) { validate_constraints(); } - - /** - * Constructs a Session_Handle from a session ticket which is a - * non-empty byte vector that must be 64kB long at most. - * Typically, tickets facilitate stateless server implementations - * and contain all relevant context in encrypted/authenticated form. - * - * Note that (for technical reasons) we enforce that tickets are - * longer than 32 bytes. - */ - Session_Handle(Session_Ticket ticket) : m_handle(std::move(ticket)) { validate_constraints(); } - - /** - * Constructs a Session_Handle from an Opaque_Handle such as TLS 1.3 - * uses them in its resumption mechanism. This could be either a - * Session_ID or a Session_Ticket and it is up to the Session_Manager - * to figure out what it actually is. - */ - Session_Handle(Opaque_Session_Handle ticket) : m_handle(std::move(ticket)) { validate_constraints(); } - - bool is_id() const { return std::holds_alternative(m_handle); } - bool is_ticket() const { return std::holds_alternative(m_handle); } +namespace Botan { - bool is_opaque_handle() const { return std::holds_alternative(m_handle); } +class Public_Key; +class X509_Certificate; - /** - * Returns the Session_Handle as an opaque handle. If the object was not - * constructed as an Opaque_Session_Handle, the contained value is - * converted. - */ - Opaque_Session_Handle opaque_handle() const; +} // namespace Botan - /** - * If the Session_Handle was constructed with a Session_ID or an - * Opaque_Session_Handle that can be converted to a Session_ID (up to - * 32 bytes long), this returns the handle as a Session_ID. Otherwise, - * std::nullopt is returned. - */ - std::optional id() const; - - /** - * If the Session_Handle was constructed with a Session_Ticket or an - * Opaque_Session_Handle this returns the handle as a Session_ID. - * Otherwise, std::nullopt is returned. - */ - std::optional ticket() const; - - decltype(auto) get() const { return m_handle; } - - private: - void validate_constraints() const; - - private: - std::variant m_handle; -}; +namespace Botan::TLS { -class Client_Hello_13; class Server_Hello_13; class Callbacks; @@ -143,22 +47,20 @@ uint16_t srtp_profile, bool extended_master_secret, bool encrypt_then_mac, - std::vector peer_certs, + const std::vector& peer_certs, std::shared_ptr peer_raw_public_key, - Server_Information server_info) : - m_start_time(start_time), - m_version(version), - m_ciphersuite(ciphersuite), - m_connection_side(connection_side), - m_srtp_profile(srtp_profile), - m_extended_master_secret(extended_master_secret), - m_encrypt_then_mac(encrypt_then_mac), - m_peer_certs(std::move(peer_certs)), - m_peer_raw_public_key(std::move(peer_raw_public_key)), - m_server_info(std::move(server_info)) {} + Server_Information server_info); + + Session_Base(const Session_Base& other); + Session_Base& operator=(const Session_Base& other); + + Session_Base(Session_Base&& other) noexcept; + Session_Base& operator=(Session_Base&& other) noexcept; + + ~Session_Base(); protected: - Session_Base() = default; + Session_Base(); public: /** @@ -224,26 +126,26 @@ const Server_Information& server_info() const { return m_server_info; } protected: - std::chrono::system_clock::time_point m_start_time; + std::chrono::system_clock::time_point m_start_time; // NOLINT(*non-private-member-variable*) - Protocol_Version m_version; - uint16_t m_ciphersuite; - Connection_Side m_connection_side; - uint16_t m_srtp_profile; - - bool m_extended_master_secret; - bool m_encrypt_then_mac; - - std::vector m_peer_certs; - std::shared_ptr m_peer_raw_public_key; - Server_Information m_server_info; + Protocol_Version m_version; // NOLINT(*non-private-member-variable*) + uint16_t m_ciphersuite = 0; // NOLINT(*non-private-member-variable*) + Connection_Side m_connection_side = Connection_Side::Client; // NOLINT(*non-private-member-variable*) + uint16_t m_srtp_profile = 0; // NOLINT(*non-private-member-variable*) + + bool m_extended_master_secret = false; // NOLINT(*non-private-member-variable*) + bool m_encrypt_then_mac = false; // NOLINT(*non-private-member-variable*) + + std::vector m_peer_certs; // NOLINT(*non-private-member-variable*) + std::shared_ptr m_peer_raw_public_key; // NOLINT(*non-private-member-variable*) + Server_Information m_server_info; // NOLINT(*non-private-member-variable*) }; /** * Summarizes the negotiated features after a TLS handshake. Applications may * query those in Callbacks::tls_session_established(). */ -class BOTAN_PUBLIC_API(3, 0) Session_Summary : public Session_Base { +class BOTAN_PUBLIC_API(3, 0) Session_Summary final : public Session_Base { public: /** * The Session_ID negotiated during the handshake. @@ -307,7 +209,7 @@ #if defined(BOTAN_HAS_TLS_13) Session_Summary(const Server_Hello_13& server_hello, Connection_Side side, - std::vector peer_certs, + const std::vector& peer_certs, std::shared_ptr peer_raw_public_key, std::optional psk_identity, bool session_was_resumed, @@ -367,27 +269,13 @@ const Server_Information& server_info, std::chrono::system_clock::time_point current_timestamp); - /** - * Create a new TLS 1.3 session object from server data structures - * after a successful handshake with a TLS 1.3 client - */ - Session(secure_vector&& session_psk, - const std::optional& max_early_data_bytes, - std::chrono::seconds lifetime_hint, - const std::vector& peer_certs, - std::shared_ptr peer_raw_public_key, - const Client_Hello_13& client_hello, - const Server_Hello_13& server_hello, - Callbacks& callbacks, - RandomNumberGenerator& rng); - #endif /** * Load a session from DER representation (created by DER_encode) * @param ber_data DER representation buffer */ - Session(std::span ber_data); + BOTAN_FUTURE_EXPLICIT Session(std::span ber_data); /** * Load a session from PEM representation (created by PEM_encode) @@ -462,24 +350,26 @@ std::chrono::seconds lifetime_hint() const { return m_lifetime_hint; } private: - // Struct Version history - // - // 20160812 - Pre TLS 1.3 - // 20220505 - Introduction of TLS 1.3 sessions - // - added fields: - // - m_early_data_allowed - // - m_max_early_data_bytes - // - m_ticket_age_add - // - m_lifetime_hint - // 20230112 - Remove Session_ID and Session_Ticket from this object - // (association is now in the hands of the Session_Manager) - // - Peer certificates are now stored as a SEQUENCE - // 20230222 - Remove deprecated and unused fields - // - compression method (always 0) - // - fragment size (always 0) - // - SRP identifier (always "") - // 20231031 - Allow storage of peer's raw public key - enum { TLS_SESSION_PARAM_STRUCT_VERSION = 20231031 }; + /* + * Struct Version history + * + * 20160812 - Pre TLS 1.3 + * 20220505 - Introduction of TLS 1.3 sessions + * - added fields: + * - m_early_data_allowed + * - m_max_early_data_bytes + * - m_ticket_age_add + * - m_lifetime_hint + * 20230112 - Remove Session_ID and Session_Ticket from this object + * (association is now in the hands of the Session_Manager) + * - Peer certificates are now stored as a SEQUENCE + * 20230222 - Remove deprecated and unused fields + * - compression method (always 0) + * - fragment size (always 0) + * - SRP identifier (always "") + * 20231031 - Allow storage of peer's raw public key + */ + static constexpr size_t TLS_SESSION_PARAM_STRUCT_VERSION = 20231031; secure_vector m_master_secret; @@ -492,7 +382,8 @@ /** * Helper struct to conveniently pass a Session and its Session_Handle around */ -struct BOTAN_PUBLIC_API(3, 0) Session_with_Handle { +class BOTAN_PUBLIC_API(3, 0) Session_with_Handle final { + public: Session session; Session_Handle handle; }; diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_session_id.h botan3-3.12.0+dfsg/src/lib/tls/tls_session_id.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_session_id.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_session_id.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,121 @@ +/* +* (C) 2022 René Meusel - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TLS_SESSION_ID_H_ +#define BOTAN_TLS_SESSION_ID_H_ + +#include +#include +#include +#include +#include + +namespace Botan::TLS { + +// Different flavors of session handles are used, depending on the usage +// scenario and the TLS protocol version. + +/// @brief holds a TLS 1.2 session ID for stateful resumption +using Session_ID = Strong, struct Session_ID_>; + +/// @brief holds a TLS 1.2 session ticket for stateless resumption +using Session_Ticket = Strong, struct Session_Ticket_>; + +/// @brief holds an opaque session handle as used in TLS 1.3 that could be +/// either a ticket for stateless resumption or a database handle. +using Opaque_Session_Handle = Strong, struct Opaque_Session_Handle_>; + +inline auto operator<(const Session_ID& id1, const Session_ID& id2) { + return id1.get() < id2.get(); +} + +/** + * @brief Helper class to embody a session handle in all protocol versions + * + * Sessions in TLS 1.2 are identified by an arbitrary and unique ID of up to + * 32 bytes or by a self-contained arbitrary-length ticket (RFC 5077). + * + * TLS 1.3 does not distinct between the two and handles both as tickets. Also + * a TLS 1.3 server can issue multiple tickets in one connection and the + * resumption mechanism is compatible with the PSK establishment. + * + * Concrete implementations of Session_Manager use this helper to distinguish + * the different states and manage sessions for TLS 1.2 and 1.3 connections. + * + * Note that all information stored in a Session_Handle might be transmitted in + * unprotected form. Hence, it should not contain any confidential information. + */ +class BOTAN_PUBLIC_API(3, 0) Session_Handle final { + public: + // NOLINTBEGIN(*-explicit-conversions) + + /** + * Constructs a Session_Handle from a session ID which is an + * arbitrary byte vector that must be 32 bytes long at most. + */ + Session_Handle(Session_ID id) : m_handle(std::move(id)) { validate_constraints(); } + + /** + * Constructs a Session_Handle from a session ticket which is a + * non-empty byte vector that must be 64kB long at most. + * Typically, tickets facilitate stateless server implementations + * and contain all relevant context in encrypted/authenticated form. + * + * Note that (for technical reasons) we enforce that tickets are + * longer than 32 bytes. + */ + Session_Handle(Session_Ticket ticket) : m_handle(std::move(ticket)) { validate_constraints(); } + + /** + * Constructs a Session_Handle from an Opaque_Handle such as TLS 1.3 + * uses them in its resumption mechanism. This could be either a + * Session_ID or a Session_Ticket and it is up to the Session_Manager + * to figure out what it actually is. + */ + Session_Handle(Opaque_Session_Handle ticket) : m_handle(std::move(ticket)) { validate_constraints(); } + + // NOLINTEND(*-explicit-conversions) + + bool is_id() const { return std::holds_alternative(m_handle); } + + bool is_ticket() const { return std::holds_alternative(m_handle); } + + bool is_opaque_handle() const { return std::holds_alternative(m_handle); } + + /** + * Returns the Session_Handle as an opaque handle. If the object was not + * constructed as an Opaque_Session_Handle, the contained value is + * converted. + */ + Opaque_Session_Handle opaque_handle() const; + + /** + * If the Session_Handle was constructed with a Session_ID or an + * Opaque_Session_Handle that can be converted to a Session_ID (up to + * 32 bytes long), this returns the handle as a Session_ID. Otherwise, + * std::nullopt is returned. + */ + std::optional id() const; + + /** + * If the Session_Handle was constructed with a Session_Ticket or an + * Opaque_Session_Handle this returns the handle as a Session_ID. + * Otherwise, std::nullopt is returned. + */ + std::optional ticket() const; + + decltype(auto) get() const { return m_handle; } + + private: + void validate_constraints() const; + + private: + std::variant m_handle; +}; + +} // namespace Botan::TLS + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_session_manager.cpp botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls_session_manager.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,9 +8,16 @@ #include +#include #include #include #include +#include +#include + +#if defined(BOTAN_HAS_TLS_13) + #include +#endif namespace Botan::TLS { @@ -105,54 +112,55 @@ break; } - // TODO: C++20, use std::ranges::remove_if() once XCode and Android NDK caught up. - sessions_and_handles.erase( - std::remove_if(sessions_and_handles.begin(), - sessions_and_handles.end(), - [&](const auto& session) { - const auto age = - std::chrono::duration_cast(now - session.session.start_time()); - - // RFC 5077 3.3 -- "Old Session Tickets" - // The ticket_lifetime_hint field contains a hint from the - // server about how long the ticket should be stored. [...] - // A client SHOULD delete the ticket and associated state when - // the time expires. It MAY delete the ticket earlier based on - // local policy. - // - // RFC 5246 F.1.4 -- TLS 1.2 - // If either party suspects that the session may have been - // compromised, or that certificates may have expired or been - // revoked, it should force a full handshake. An upper limit of - // 24 hours is suggested for session ID lifetimes. - // - // RFC 8446 4.2.11.1 -- TLS 1.3 - // The client's view of the age of a ticket is the time since the - // receipt of the NewSessionTicket message. Clients MUST NOT - // attempt to use tickets which have ages greater than the - // "ticket_lifetime" value which was provided with the ticket. - // - // RFC 8446 4.6.1 -- TLS 1.3 - // Clients MUST NOT cache tickets for longer than 7 days, - // regardless of the ticket_lifetime, and MAY delete tickets - // earlier based on local policy. - // - // Note: TLS 1.3 tickets with a lifetime longer than 7 days are - // rejected during parsing with an "Illegal Parameter" alert. - // Other suggestions are left to the application via - // Policy::session_ticket_lifetime(). Session lifetimes as - // communicated by the server via the "lifetime_hint" are - // obeyed regardless of the policy setting. - const auto session_lifetime_hint = session.session.lifetime_hint(); - const bool expired = age > std::min(policy_lifetime, session_lifetime_hint); - - if(expired) { - remove(session.handle); - } - - return expired; - }), - sessions_and_handles.end()); + std::erase_if(sessions_and_handles, [&](const auto& session) { + const auto age = std::chrono::duration_cast(now - session.session.start_time()); + + // RFC 5077 3.3 -- "Old Session Tickets" + // The ticket_lifetime_hint field contains a hint from the + // server about how long the ticket should be stored. [...] + // A client SHOULD delete the ticket and associated state when + // the time expires. It MAY delete the ticket earlier based on + // local policy. + // + // A value [in ticket_lifetime_hint] of zero is reserved to indicate + // that the lifetime of the ticket is unspecified. + // + // RFC 5246 F.1.4 -- TLS 1.2 + // If either party suspects that the session may have been + // compromised, or that certificates may have expired or been + // revoked, it should force a full handshake. An upper limit of + // 24 hours is suggested for session ID lifetimes. + // + // RFC 8446 4.2.11.1 -- TLS 1.3 + // The client's view of the age of a ticket is the time since the + // receipt of the NewSessionTicket message. Clients MUST NOT + // attempt to use tickets which have ages greater than the + // "ticket_lifetime" value which was provided with the ticket. + // + // RFC 8446 4.6.1 -- TLS 1.3 + // Clients MUST NOT cache tickets for longer than 7 days, + // regardless of the ticket_lifetime, and MAY delete tickets + // earlier based on local policy. + // + // Note: TLS 1.3 tickets with a lifetime longer than 7 days are + // rejected during parsing with an "Illegal Parameter" alert. + // Other suggestions are left to the application via + // Policy::session_ticket_lifetime(). Session lifetimes as + // communicated by the server via the "lifetime_hint" are + // obeyed regardless of the policy setting. + const auto session_lifetime_hint = session.session.lifetime_hint(); + + const bool is_rfc5077_unspecified = + (session_lifetime_hint.count() == 0 && session.session.version().is_pre_tls_13()); + const auto effective_hint = is_rfc5077_unspecified ? std::chrono::seconds::max() : session_lifetime_hint; + const bool expired = age > std::min(policy_lifetime, effective_hint); + + if(expired) { + remove(session.handle); + } + + return expired; + }); } return sessions_and_handles; @@ -211,11 +219,16 @@ // Note that the TLS server currently does not ensure that tickets aren't // reused. As a result, no locking is required on this level. - for(uint16_t i = 0; const auto& ticket : tickets) { - auto session = retrieve(Opaque_Session_Handle(ticket.identity()), callbacks, policy); + // Limit how many identities we attempt to look up to prevent a client + // from forcing excessive session store queries. + const size_t max_attempts = std::min(tickets.size(), 5); + + for(size_t i = 0; i < max_attempts; ++i) { + const auto& ticket = tickets[i]; + auto session = retrieve(Session_Handle(Opaque_Session_Handle(ticket.identity())), callbacks, policy); if(session.has_value() && session->ciphersuite().prf_algo() == hash_function && session->version().is_tls_13_or_later()) { - return std::pair{std::move(session.value()), i}; + return std::pair{std::move(session.value()), static_cast(i)}; } // RFC 8446 4.2.10 @@ -229,8 +242,6 @@ // TODO: The ticket-age is currently not checked (as 0-RTT is not // implemented) and we simply take the SHOULD at face value. // Instead we could add a policy check letting the user decide. - - ++i; } return std::nullopt; diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_session_manager.h botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_session_manager.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,26 +10,27 @@ #define BOTAN_TLS_SESSION_MANAGER_H_ #include -#include +#include #include - -#if defined(BOTAN_HAS_TLS_13) - #include -#endif - -#include -#include +#include +#include #include -#include +#include namespace Botan { + class RandomNumberGenerator; + } namespace Botan::TLS { class Callbacks; class Policy; +class PskIdentity; +class Server_Information; +class Session; +class Session_with_Handle; /** * Session_Manager is an interface to systems which can save session parameters @@ -42,9 +43,9 @@ * recursive mutex (via Session_Manager::mutex()). Derived classes may simply * reuse this for their own locking. */ -class BOTAN_PUBLIC_API(3, 0) Session_Manager { +class BOTAN_PUBLIC_API(3, 0) Session_Manager /* NOLINT(*-special-member-functions) */ { public: - Session_Manager(const std::shared_ptr& rng); + BOTAN_FUTURE_EXPLICIT Session_Manager(const std::shared_ptr& rng); /** * @brief Save a new Session and assign a Session_Handle (TLS Server) @@ -85,7 +86,7 @@ * This method is only called on TLS clients. * * @param session to save - * @param handle a Session_Handle on which this session shoud by stored + * @param handle a Session_Handle on which this session should by stored */ virtual void store(const Session& session, const Session_Handle& handle) = 0; @@ -111,7 +112,7 @@ * @param callbacks callbacks to be used for session policy decisions * @param policy policy to be used for session policy decisions * - * @return a std::pair of the Session associated to the choosen PSK and + * @return a std::pair of the Session associated to the chosen PSK and * the index of the selected ticket; std::nullopt if no PSK was * chosen for usage (will result in a full handshake) * @@ -272,7 +273,7 @@ const Policy& policy); protected: - std::shared_ptr m_rng; + std::shared_ptr m_rng; // NOLINT(*non-private-member-variable*) private: recursive_mutex_type m_mutex; diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_session_manager_hybrid.cpp botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_hybrid.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls_session_manager_hybrid.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_hybrid.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,8 +8,8 @@ #include -#include - +#include +#include #include namespace Botan::TLS { @@ -25,6 +25,12 @@ BOTAN_ASSERT_NONNULL(m_stateful); } +std::vector Session_Manager_Hybrid::find(const Server_Information& info, + Callbacks& callbacks, + const Policy& policy) { + return m_stateful->find(info, callbacks, policy); +} + std::optional Session_Manager_Hybrid::establish(const Session& session, const std::optional& id, bool tls12_no_ticket) { @@ -84,4 +90,13 @@ return m_stateless.emits_session_tickets() || m_stateful->emits_session_tickets(); } +std::optional Session_Manager_Hybrid::retrieve_one(const Session_Handle& /*handle*/) { + BOTAN_ASSERT(false, "This should never be called"); +} + +std::vector Session_Manager_Hybrid::find_some(const Server_Information& /*info*/, + size_t /*max_sessions_hint*/) { + BOTAN_ASSERT(false, "This should never be called"); +} + } // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_session_manager_hybrid.h botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_hybrid.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_session_manager_hybrid.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_hybrid.h 2026-05-07 01:38:28.000000000 +0000 @@ -64,9 +64,7 @@ std::vector find(const Server_Information& info, Callbacks& callbacks, - const Policy& policy) override { - return m_stateful->find(info, callbacks, policy); - } + const Policy& policy) override; void store(const Session& session, const Session_Handle& handle) override { m_stateful->store(session, handle); } @@ -82,13 +80,9 @@ // The Hybrid_Session_Manager just delegates to its underlying managers // via the public retrieval API. Its own "storage interface" is therefore // never called. - std::optional retrieve_one(const Session_Handle&) override { - BOTAN_ASSERT(false, "This should never be called"); - } - - std::vector find_some(const Server_Information&, const size_t) override { - BOTAN_ASSERT(false, "This should never be called"); - } + std::optional retrieve_one(const Session_Handle& handle) override; + + std::vector find_some(const Server_Information& info, size_t max_sessions_hint) override; private: std::unique_ptr m_stateful; diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_session_manager_memory.cpp botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_memory.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls_session_manager_memory.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_memory.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,8 +11,6 @@ #include #include -#include - namespace Botan::TLS { Session_Manager_In_Memory::Session_Manager_In_Memory(const std::shared_ptr& rng, @@ -26,8 +24,8 @@ void Session_Manager_In_Memory::store(const Session& session, const Session_Handle& handle) { // TODO: C++20 allows CTAD for template aliases (read: lock_guard_type), so // technically we should be able to omit the explicit mutex type. - // Unfortuately clang does not agree, yet. - lock_guard_type lk(mutex()); + // Unfortunately clang does not agree, yet. + const lock_guard_type lk(mutex()); if(m_fifo.has_value()) { while(m_sessions.size() >= capacity()) { @@ -48,7 +46,7 @@ } std::optional Session_Manager_In_Memory::retrieve_one(const Session_Handle& handle) { - lock_guard_type lk(mutex()); + const lock_guard_type lk(mutex()); if(auto id = handle.id()) { const auto session = m_sessions.find(id.value()); @@ -64,7 +62,7 @@ const size_t max_sessions_hint) { BOTAN_UNUSED(max_sessions_hint); - lock_guard_type lk(mutex()); + const lock_guard_type lk(mutex()); std::vector found_sessions; // TODO: std::copy_if? @@ -78,7 +76,7 @@ } size_t Session_Manager_In_Memory::remove(const Session_Handle& handle) { - lock_guard_type lk(mutex()); + const lock_guard_type lk(mutex()); return remove_internal(handle); } @@ -91,30 +89,23 @@ // TODO: This is an O(n) operation. Typically, the Session_Manager will // not contain a plethora of sessions and this should be fine. If // it's not, we'll need to consider another index on tickets. - // - // TODO: C++20's std::erase_if should return the number of erased items - // - // Unfortunately, at the time of this writing Android NDK shipped with - // a std::erase_if that returns void. Hence, the workaround. - const auto before = m_sessions.size(); - std::erase_if(m_sessions, [&](const auto& item) { + return std::erase_if(m_sessions, [&](const auto& item) { const auto& [_unused1, session_and_handle] = item; const auto& [_unused2, this_handle] = session_and_handle; return this_handle.is_ticket() && this_handle.ticket().value() == ticket; }); - return before - m_sessions.size(); }, [&](const Session_ID& id) -> size_t { return m_sessions.erase(id); }, [&](const Opaque_Session_Handle&) -> size_t { if(auto id = handle.id()) { - auto removed = remove_internal(id.value()); + auto removed = remove_internal(Session_Handle(id.value())); if(removed > 0) { return removed; } } if(auto ticket = handle.ticket()) { - return remove_internal(ticket.value()); + return remove_internal(Session_Handle(ticket.value())); } return 0; @@ -124,7 +115,7 @@ } size_t Session_Manager_In_Memory::remove_all() { - lock_guard_type lk(mutex()); + const lock_guard_type lk(mutex()); const auto sessions = m_sessions.size(); m_sessions.clear(); diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_session_manager_memory.h botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_memory.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_session_manager_memory.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_memory.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,7 +9,6 @@ #ifndef BOTAN_TLS_SESSION_MANAGER_IN_MEMORY_H_ #define BOTAN_TLS_SESSION_MANAGER_IN_MEMORY_H_ -#include #include #include @@ -36,7 +35,7 @@ * For applications that implement a TLS client and that do not want to persist * sessions to non-volatile memory, this is typically a good default option. */ -class BOTAN_PUBLIC_API(3, 0) Session_Manager_In_Memory : public Session_Manager { +class BOTAN_PUBLIC_API(3, 0) Session_Manager_In_Memory final : public Session_Manager { public: /** * @param rng a RNG used for generating session key and for @@ -44,7 +43,8 @@ * @param max_sessions a hint on the maximum number of sessions * to keep in memory at any one time. (If zero, don't cap) */ - Session_Manager_In_Memory(const std::shared_ptr& rng, size_t max_sessions = 1000); + BOTAN_FUTURE_EXPLICIT Session_Manager_In_Memory(const std::shared_ptr& rng, + size_t max_sessions = 1000); void store(const Session& session, const Session_Handle& handle) override; size_t remove(const Session_Handle& handle) override; diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_session_manager_noop.cpp botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_noop.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls_session_manager_noop.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_noop.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,9 +9,25 @@ #include #include +#include namespace Botan::TLS { Session_Manager_Noop::Session_Manager_Noop() : Session_Manager(std::make_shared()) {} +std::optional Session_Manager_Noop::establish(const Session& /*session*/, + const std::optional& /*session_id*/, + bool /*tls12_no_ticket*/) { + return {}; +} + +std::optional Session_Manager_Noop::retrieve_one(const Session_Handle& /*handle*/) { + return {}; +} + +std::vector Session_Manager_Noop::find_some(const Server_Information& /*info*/, + size_t /*max_sessions_hint*/) { + return {}; +} + } // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_session_manager_noop.h botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_noop.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_session_manager_noop.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_noop.h 2026-05-07 01:38:28.000000000 +0000 @@ -24,22 +24,20 @@ public: Session_Manager_Noop(); - std::optional establish(const Session&, - const std::optional& = std::nullopt, - bool = false) override { - return std::nullopt; - } + std::optional establish(const Session& session, + const std::optional& session_id = std::nullopt, + bool tls12_no_ticket = false) override; - void store(const Session&, const Session_Handle&) override {} + void store(const Session& /*session*/, const Session_Handle& /*handle*/) override {} - size_t remove(const Session_Handle&) override { return 0; } + size_t remove(const Session_Handle& /*session*/) override { return 0; } size_t remove_all() override { return 0; } protected: - std::optional retrieve_one(const Session_Handle&) override { return std::nullopt; } + std::optional retrieve_one(const Session_Handle& handle) override; - std::vector find_some(const Server_Information&, const size_t) override { return {}; } + std::vector find_some(const Server_Information& info, size_t max_sessions_hint) override; }; } // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_session_manager_stateless.cpp botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_stateless.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls_session_manager_stateless.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_stateless.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,11 +8,11 @@ #include +#include #include #include #include - -#include +#include namespace Botan::TLS { @@ -22,8 +22,13 @@ BOTAN_ASSERT_NONNULL(m_credentials_manager); } +std::vector Session_Manager_Stateless::find_some(const Server_Information& /*info*/, + size_t /*max_sessions_hint*/) { + return {}; +} + std::optional Session_Manager_Stateless::establish(const Session& session, - const std::optional&, + const std::optional& /*session_id*/, bool tls12_no_ticket) { BOTAN_ASSERT(session.side() == Connection_Side::Server, "Client tried to establish a session"); if(tls12_no_ticket) { @@ -35,10 +40,10 @@ return std::nullopt; } - return Session_Ticket{session.encrypt(key.value(), *m_rng)}; + return Session_Handle(Session_Ticket{session.encrypt(key.value(), *m_rng)}); } -void Session_Manager_Stateless::store(const Session&, const Session_Handle&) { +void Session_Manager_Stateless::store(const Session& /*session*/, const Session_Handle& /*handle*/) { throw Invalid_Argument("A stateless Session Manager cannot store Sessions with their handle"); } diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_session_manager_stateless.h botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_stateless.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_session_manager_stateless.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_session_manager_stateless.h 2026-05-07 01:38:28.000000000 +0000 @@ -15,6 +15,8 @@ class RandomNumberGenerator; class Credentials_Manager; +class OctetString; +typedef OctetString SymmetricKey; namespace TLS { @@ -28,7 +30,7 @@ * sessions via Session_ID will never return a session. Neither will searching * sessions by server information yield any result. */ -class BOTAN_PUBLIC_API(3, 0) Session_Manager_Stateless : public Session_Manager { +class BOTAN_PUBLIC_API(3, 0) Session_Manager_Stateless final : public Session_Manager { public: /** * The key to encrypt and authenticate session information will be drawn @@ -45,7 +47,7 @@ void store(const Session& session, const Session_Handle& handle) override; - size_t remove(const Session_Handle&) override { return 0; } + size_t remove(const Session_Handle& /*handle*/) override { return 0; } size_t remove_all() override { return 0; } @@ -54,7 +56,8 @@ protected: std::optional retrieve_one(const Session_Handle& handle) override; - std::vector find_some(const Server_Information&, const size_t) override { return {}; } + // Returns empty by default + std::vector find_some(const Server_Information& info, size_t max_sessions_hint) override; private: std::optional get_ticket_key() noexcept; diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_signature_scheme.cpp botan3-3.12.0+dfsg/src/lib/tls/tls_signature_scheme.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls_signature_scheme.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_signature_scheme.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,12 +7,8 @@ #include -#include -#include -#include -#include +#include #include -#include #include #include @@ -157,7 +153,6 @@ return "PSS(SHA-512,MGF1,64)"; case EDDSA_25519: - return "Pure"; case EDDSA_448: return "Pure"; @@ -195,14 +190,23 @@ } AlgorithmIdentifier Signature_Scheme::key_algorithm_identifier() const noexcept { + const auto der_encode_oid = [](const std::string_view oid_name) { + try { + if(auto oid = OID::from_name(oid_name)) { + return oid->BER_encode(); + } + } catch(...) {} + BOTAN_ASSERT_UNREACHABLE(); + }; + switch(m_code) { // case ECDSA_SHA1: not defined case ECDSA_SHA256: - return {"ECDSA", EC_Group::from_name("secp256r1").DER_encode()}; + return {"ECDSA", der_encode_oid("secp256r1")}; case ECDSA_SHA384: - return {"ECDSA", EC_Group::from_name("secp384r1").DER_encode()}; + return {"ECDSA", der_encode_oid("secp384r1")}; case ECDSA_SHA512: - return {"ECDSA", EC_Group::from_name("secp521r1").DER_encode()}; + return {"ECDSA", der_encode_oid("secp521r1")}; case EDDSA_25519: return {"Ed25519", AlgorithmIdentifier::USE_EMPTY_PARAM}; @@ -265,14 +269,14 @@ case RSA_PSS_SHA256: case RSA_PSS_SHA384: case RSA_PSS_SHA512: + case EDDSA_25519: + case EDDSA_448: return Signature_Format::Standard; case ECDSA_SHA1: case ECDSA_SHA256: case ECDSA_SHA384: case ECDSA_SHA512: - case EDDSA_25519: - case EDDSA_448: return Signature_Format::DerSequence; default: @@ -331,9 +335,10 @@ std::vector to_algorithm_identifiers(const std::vector& schemes) { std::vector result; - std::transform(schemes.begin(), schemes.end(), std::back_inserter(result), [](const auto& scheme) { - return scheme.algorithm_identifier(); - }); + result.reserve(schemes.size()); + for(const auto& scheme : schemes) { + result.push_back(scheme.algorithm_identifier()); + } return result; } diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_signature_scheme.h botan3-3.12.0+dfsg/src/lib/tls/tls_signature_scheme.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_signature_scheme.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_signature_scheme.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,17 +10,23 @@ #define BOTAN_TLS_SIGNATURE_SCHEME_H_ #include -#include #include - #include #include +#include + +namespace Botan { + +enum class Signature_Format : uint8_t; +class Private_Key; + +} // namespace Botan namespace Botan::TLS { class Protocol_Version; -class BOTAN_PUBLIC_API(3, 0) Signature_Scheme { +class BOTAN_PUBLIC_API(3, 0) Signature_Scheme final { public: /** * Matches with wire encoding @@ -29,7 +35,7 @@ * API where `Signature_Scheme` was an enum class with associated free-standing * functions. Leaving it as a bare enum resembles the legacy user-facing API. */ - enum Code : uint16_t { + enum Code : uint16_t /* NOLINT(*-use-enum-class) */ { NONE = 0x0000, RSA_PKCS1_SHA1 = 0x0201, // not implemented @@ -61,9 +67,9 @@ */ Signature_Scheme(); - Signature_Scheme(uint16_t wire_code); + /* NOLINT(*-explicit-conversions) */ Signature_Scheme(uint16_t wire_code); - Signature_Scheme(Signature_Scheme::Code wire_code); + /* NOLINT(*-explicit-conversions) */ Signature_Scheme(Signature_Scheme::Code wire_code); Signature_Scheme::Code wire_code() const noexcept { return m_code; } @@ -86,6 +92,15 @@ std::optional format() const noexcept; bool is_compatible_with(const Protocol_Version& protocol_version) const noexcept; + + /** + * Checks that @p private_key is suitable for use with this signature + * scheme, enforcing the curve-hash binding required by TLS 1.3 (e.g. + * ECDSA_SHA256 only with P-256 keys). This must not be used for TLS + * 1.2 scheme selection, where signature schemes are (hash, algorithm) + * pairs with no curve binding -- any hash may be used with any ECDSA + * curve per RFC 5246. + */ bool is_suitable_for(const Private_Key& private_key) const noexcept; bool operator==(const Signature_Scheme& rhs) const { return m_code == rhs.m_code; } diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_suite_info.cpp botan3-3.12.0+dfsg/src/lib/tls/tls_suite_info.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls_suite_info.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_suite_info.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,16 +1,345 @@ /* * TLS cipher suite information * -* This file was automatically generated by ./src/scripts/dev_tools/gen_tls_suite_info.py on 2023-05-29 -* using the IANA assignments (tls-parameters.txt sha256 0aefcb3ed4a33f4f9fd401d63ecd80d50f223b9547ac1b25b705ec14ef8bbbbf) +* This file was automatically generated by ./src/scripts/dev_tools/gen_tls_suite_info.py on 2026-04-29 +* using the IANA assignments (tls-parameters.txt sha256 f466647ca9b2d2b357af29d93ecb19e55d0eb99a160a221c125e5cf3a7cfd9bb) +* All manual changes will be lost. Edit the script instead. * * Botan is released under the Simplified BSD License (see license.txt) */ #include +#include +#include + namespace Botan::TLS { +namespace { + +consteval auto available_ciphersuites() { + // clang-format off + auto codes = std::array { +#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_PKCSV15_ENCRYPTION_PADDING) && defined(BOTAN_HAS_DES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA1) + uint16_t{0x000A}, // RSA_WITH_3DES_EDE_CBC_SHA +#endif +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_DES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA1) + uint16_t{0x0016}, // DHE_RSA_WITH_3DES_EDE_CBC_SHA +#endif +#if defined(BOTAN_HAS_TLS_NULL) && defined(BOTAN_HAS_SHA1) + uint16_t{0x002C}, // PSK_WITH_NULL_SHA +#endif +#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_PKCSV15_ENCRYPTION_PADDING) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA1) + uint16_t{0x002F}, // RSA_WITH_AES_128_CBC_SHA +#endif +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA1) + uint16_t{0x0033}, // DHE_RSA_WITH_AES_128_CBC_SHA +#endif +#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_PKCSV15_ENCRYPTION_PADDING) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA1) + uint16_t{0x0035}, // RSA_WITH_AES_256_CBC_SHA +#endif +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA1) + uint16_t{0x0039}, // DHE_RSA_WITH_AES_256_CBC_SHA +#endif +#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_PKCSV15_ENCRYPTION_PADDING) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0x003C}, // RSA_WITH_AES_128_CBC_SHA256 +#endif +#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_PKCSV15_ENCRYPTION_PADDING) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0x003D}, // RSA_WITH_AES_256_CBC_SHA256 +#endif +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0x0067}, // DHE_RSA_WITH_AES_128_CBC_SHA256 +#endif +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0x006B}, // DHE_RSA_WITH_AES_256_CBC_SHA256 +#endif +#if defined(BOTAN_HAS_DES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA1) + uint16_t{0x008B}, // PSK_WITH_3DES_EDE_CBC_SHA +#endif +#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA1) + uint16_t{0x008C}, // PSK_WITH_AES_128_CBC_SHA +#endif +#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA1) + uint16_t{0x008D}, // PSK_WITH_AES_256_CBC_SHA +#endif +#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_PKCSV15_ENCRYPTION_PADDING) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0x009C}, // RSA_WITH_AES_128_GCM_SHA256 +#endif +#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_PKCSV15_ENCRYPTION_PADDING) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0x009D}, // RSA_WITH_AES_256_GCM_SHA384 +#endif +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0x009E}, // DHE_RSA_WITH_AES_128_GCM_SHA256 +#endif +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0x009F}, // DHE_RSA_WITH_AES_256_GCM_SHA384 +#endif +#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0x00A8}, // PSK_WITH_AES_128_GCM_SHA256 +#endif +#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0x00A9}, // PSK_WITH_AES_256_GCM_SHA384 +#endif +#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0x00AE}, // PSK_WITH_AES_128_CBC_SHA256 +#endif +#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0x00AF}, // PSK_WITH_AES_256_CBC_SHA384 +#endif +#if defined(BOTAN_HAS_TLS_NULL) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0x00B0}, // PSK_WITH_NULL_SHA256 +#endif +#if defined(BOTAN_HAS_TLS_NULL) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0x00B1}, // PSK_WITH_NULL_SHA384 +#endif +#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0x1301}, // AES_128_GCM_SHA256 +#endif +#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0x1302}, // AES_256_GCM_SHA384 +#endif +#if defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0x1303}, // CHACHA20_POLY1305_SHA256 +#endif +#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_CCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0x1304}, // AES_128_CCM_SHA256 +#endif +#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_CCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0x1305}, // AES_128_CCM_8_SHA256 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_TLS_NULL) && defined(BOTAN_HAS_SHA1) + uint16_t{0xC006}, // ECDHE_ECDSA_WITH_NULL_SHA +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_DES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA1) + uint16_t{0xC008}, // ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA1) + uint16_t{0xC009}, // ECDHE_ECDSA_WITH_AES_128_CBC_SHA +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA1) + uint16_t{0xC00A}, // ECDHE_ECDSA_WITH_AES_256_CBC_SHA +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_TLS_NULL) && defined(BOTAN_HAS_SHA1) + uint16_t{0xC010}, // ECDHE_RSA_WITH_NULL_SHA +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_DES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA1) + uint16_t{0xC012}, // ECDHE_RSA_WITH_3DES_EDE_CBC_SHA +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA1) + uint16_t{0xC013}, // ECDHE_RSA_WITH_AES_128_CBC_SHA +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA1) + uint16_t{0xC014}, // ECDHE_RSA_WITH_AES_256_CBC_SHA +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC023}, // ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0xC024}, // ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC027}, // ECDHE_RSA_WITH_AES_128_CBC_SHA256 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0xC028}, // ECDHE_RSA_WITH_AES_256_CBC_SHA384 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC02B}, // ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0xC02C}, // ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC02F}, // ECDHE_RSA_WITH_AES_128_GCM_SHA256 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0xC030}, // ECDHE_RSA_WITH_AES_256_GCM_SHA384 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_DES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA1) + uint16_t{0xC034}, // ECDHE_PSK_WITH_3DES_EDE_CBC_SHA +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA1) + uint16_t{0xC035}, // ECDHE_PSK_WITH_AES_128_CBC_SHA +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA1) + uint16_t{0xC036}, // ECDHE_PSK_WITH_AES_256_CBC_SHA +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC037}, // ECDHE_PSK_WITH_AES_128_CBC_SHA256 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_TLS_CBC) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0xC038}, // ECDHE_PSK_WITH_AES_256_CBC_SHA384 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_TLS_NULL) && defined(BOTAN_HAS_SHA1) + uint16_t{0xC039}, // ECDHE_PSK_WITH_NULL_SHA +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_TLS_NULL) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC03A}, // ECDHE_PSK_WITH_NULL_SHA256 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_TLS_NULL) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0xC03B}, // ECDHE_PSK_WITH_NULL_SHA384 +#endif +#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_PKCSV15_ENCRYPTION_PADDING) && defined(BOTAN_HAS_ARIA) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC050}, // RSA_WITH_ARIA_128_GCM_SHA256 +#endif +#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_PKCSV15_ENCRYPTION_PADDING) && defined(BOTAN_HAS_ARIA) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0xC051}, // RSA_WITH_ARIA_256_GCM_SHA384 +#endif +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_ARIA) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC052}, // DHE_RSA_WITH_ARIA_128_GCM_SHA256 +#endif +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_ARIA) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0xC053}, // DHE_RSA_WITH_ARIA_256_GCM_SHA384 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_ARIA) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC05C}, // ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_ARIA) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0xC05D}, // ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_ARIA) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC060}, // ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_ARIA) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0xC061}, // ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 +#endif +#if defined(BOTAN_HAS_ARIA) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC06A}, // PSK_WITH_ARIA_128_GCM_SHA256 +#endif +#if defined(BOTAN_HAS_ARIA) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0xC06B}, // PSK_WITH_ARIA_256_GCM_SHA384 +#endif +#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_PKCSV15_ENCRYPTION_PADDING) && defined(BOTAN_HAS_CAMELLIA) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC07A}, // RSA_WITH_CAMELLIA_128_GCM_SHA256 +#endif +#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_PKCSV15_ENCRYPTION_PADDING) && defined(BOTAN_HAS_CAMELLIA) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0xC07B}, // RSA_WITH_CAMELLIA_256_GCM_SHA384 +#endif +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_CAMELLIA) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC07C}, // DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 +#endif +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_CAMELLIA) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0xC07D}, // DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_CAMELLIA) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC086}, // ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_CAMELLIA) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0xC087}, // ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_CAMELLIA) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC08A}, // ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_CAMELLIA) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0xC08B}, // ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 +#endif +#if defined(BOTAN_HAS_CAMELLIA) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC08E}, // PSK_WITH_CAMELLIA_128_GCM_SHA256 +#endif +#if defined(BOTAN_HAS_CAMELLIA) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0xC08F}, // PSK_WITH_CAMELLIA_256_GCM_SHA384 +#endif +#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_PKCSV15_ENCRYPTION_PADDING) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_CCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC09C}, // RSA_WITH_AES_128_CCM +#endif +#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_PKCSV15_ENCRYPTION_PADDING) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_CCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC09D}, // RSA_WITH_AES_256_CCM +#endif +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_CCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC09E}, // DHE_RSA_WITH_AES_128_CCM +#endif +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_CCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC09F}, // DHE_RSA_WITH_AES_256_CCM +#endif +#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_PKCSV15_ENCRYPTION_PADDING) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_CCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC0A0}, // RSA_WITH_AES_128_CCM_8 +#endif +#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_PKCSV15_ENCRYPTION_PADDING) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_CCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC0A1}, // RSA_WITH_AES_256_CCM_8 +#endif +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_CCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC0A2}, // DHE_RSA_WITH_AES_128_CCM_8 +#endif +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_CCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC0A3}, // DHE_RSA_WITH_AES_256_CCM_8 +#endif +#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_CCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC0A4}, // PSK_WITH_AES_128_CCM +#endif +#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_CCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC0A5}, // PSK_WITH_AES_256_CCM +#endif +#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_CCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC0A8}, // PSK_WITH_AES_128_CCM_8 +#endif +#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_CCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC0A9}, // PSK_WITH_AES_256_CCM_8 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_CCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC0AC}, // ECDHE_ECDSA_WITH_AES_128_CCM +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_CCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC0AD}, // ECDHE_ECDSA_WITH_AES_256_CCM +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_CCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC0AE}, // ECDHE_ECDSA_WITH_AES_128_CCM_8 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_CCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xC0AF}, // ECDHE_ECDSA_WITH_AES_256_CCM_8 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xCCA8}, // ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xCCA9}, // ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 +#endif +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xCCAA}, // DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 +#endif +#if defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xCCAB}, // PSK_WITH_CHACHA20_POLY1305_SHA256 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xCCAC}, // ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xD001}, // ECDHE_PSK_WITH_AES_128_GCM_SHA256 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64) + uint16_t{0xD002}, // ECDHE_PSK_WITH_AES_256_GCM_SHA384 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_CCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xD003}, // ECDHE_PSK_WITH_AES_128_CCM_8_SHA256 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_CCM) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xD005}, // ECDHE_PSK_WITH_AES_128_CCM_SHA256 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_OCB) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xFFC3}, // ECDHE_RSA_WITH_AES_256_OCB_SHA256 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_OCB) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xFFC5}, // ECDHE_ECDSA_WITH_AES_256_OCB_SHA256 +#endif +#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_OCB) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xFFC7}, // PSK_WITH_AES_256_OCB_SHA256 +#endif +#if defined(BOTAN_HAS_ECDH) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_OCB) && defined(BOTAN_HAS_SHA2_32) + uint16_t{0xFFCB}, // ECDHE_PSK_WITH_AES_256_OCB_SHA256 +#endif + }; + // clang-format on + + return codes; +} + +} // namespace + +//static +bool Ciphersuite::is_known_usable(uint16_t code) { + static constexpr auto codes = available_ciphersuites(); + return std::binary_search(codes.begin(), codes.end(), code); +} + //static const std::vector& Ciphersuite::all_known_ciphersuites() { // clang-format off @@ -19,6 +348,7 @@ static const std::vector g_ciphersuite_list = { Ciphersuite(0x000A, "RSA_WITH_3DES_EDE_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE), Ciphersuite(0x0016, "DHE_RSA_WITH_3DES_EDE_CBC_SHA", Auth_Method::RSA, Kex_Algo::DH, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE), + Ciphersuite(0x002C, "PSK_WITH_NULL_SHA", Auth_Method::IMPLICIT, Kex_Algo::PSK, "NULL", 0, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::NULL_CIPHER), Ciphersuite(0x002F, "RSA_WITH_AES_128_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE), Ciphersuite(0x0033, "DHE_RSA_WITH_AES_128_CBC_SHA", Auth_Method::RSA, Kex_Algo::DH, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE), Ciphersuite(0x0035, "RSA_WITH_AES_256_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE), @@ -38,14 +368,18 @@ Ciphersuite(0x00A9, "PSK_WITH_AES_256_GCM_SHA384", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4), Ciphersuite(0x00AE, "PSK_WITH_AES_128_CBC_SHA256", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE), Ciphersuite(0x00AF, "PSK_WITH_AES_256_CBC_SHA384", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-256", 32, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::CBC_MODE), + Ciphersuite(0x00B0, "PSK_WITH_NULL_SHA256", Auth_Method::IMPLICIT, Kex_Algo::PSK, "NULL", 0, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::NULL_CIPHER), + Ciphersuite(0x00B1, "PSK_WITH_NULL_SHA384", Auth_Method::IMPLICIT, Kex_Algo::PSK, "NULL", 0, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::NULL_CIPHER), Ciphersuite(0x1301, "AES_128_GCM_SHA256", Auth_Method::UNDEFINED, Kex_Algo::UNDEFINED, "AES-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4), Ciphersuite(0x1302, "AES_256_GCM_SHA384", Auth_Method::UNDEFINED, Kex_Algo::UNDEFINED, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4), Ciphersuite(0x1303, "CHACHA20_POLY1305_SHA256", Auth_Method::UNDEFINED, Kex_Algo::UNDEFINED, "ChaCha20Poly1305", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12), Ciphersuite(0x1304, "AES_128_CCM_SHA256", Auth_Method::UNDEFINED, Kex_Algo::UNDEFINED, "AES-128/CCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4), Ciphersuite(0x1305, "AES_128_CCM_8_SHA256", Auth_Method::UNDEFINED, Kex_Algo::UNDEFINED, "AES-128/CCM(8)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4), + Ciphersuite(0xC006, "ECDHE_ECDSA_WITH_NULL_SHA", Auth_Method::ECDSA, Kex_Algo::ECDH, "NULL", 0, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::NULL_CIPHER), Ciphersuite(0xC008, "ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", Auth_Method::ECDSA, Kex_Algo::ECDH, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE), Ciphersuite(0xC009, "ECDHE_ECDSA_WITH_AES_128_CBC_SHA", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE), Ciphersuite(0xC00A, "ECDHE_ECDSA_WITH_AES_256_CBC_SHA", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE), + Ciphersuite(0xC010, "ECDHE_RSA_WITH_NULL_SHA", Auth_Method::RSA, Kex_Algo::ECDH, "NULL", 0, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::NULL_CIPHER), Ciphersuite(0xC012, "ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", Auth_Method::RSA, Kex_Algo::ECDH, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE), Ciphersuite(0xC013, "ECDHE_RSA_WITH_AES_128_CBC_SHA", Auth_Method::RSA, Kex_Algo::ECDH, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE), Ciphersuite(0xC014, "ECDHE_RSA_WITH_AES_256_CBC_SHA", Auth_Method::RSA, Kex_Algo::ECDH, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE), @@ -62,6 +396,9 @@ Ciphersuite(0xC036, "ECDHE_PSK_WITH_AES_256_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE), Ciphersuite(0xC037, "ECDHE_PSK_WITH_AES_128_CBC_SHA256", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "AES-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE), Ciphersuite(0xC038, "ECDHE_PSK_WITH_AES_256_CBC_SHA384", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "AES-256", 32, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::CBC_MODE), + Ciphersuite(0xC039, "ECDHE_PSK_WITH_NULL_SHA", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "NULL", 0, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::NULL_CIPHER), + Ciphersuite(0xC03A, "ECDHE_PSK_WITH_NULL_SHA256", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "NULL", 0, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::NULL_CIPHER), + Ciphersuite(0xC03B, "ECDHE_PSK_WITH_NULL_SHA384", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "NULL", 0, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::NULL_CIPHER), Ciphersuite(0xC050, "RSA_WITH_ARIA_128_GCM_SHA256", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "ARIA-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4), Ciphersuite(0xC051, "RSA_WITH_ARIA_256_GCM_SHA384", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "ARIA-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4), Ciphersuite(0xC052, "DHE_RSA_WITH_ARIA_128_GCM_SHA256", Auth_Method::RSA, Kex_Algo::DH, "ARIA-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4), diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_text_policy.cpp botan3-3.12.0+dfsg/src/lib/tls/tls_text_policy.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls_text_policy.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_text_policy.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include +#include #include #include #include @@ -93,6 +94,10 @@ return get_bool("negotiate_encrypt_then_mac", Policy::negotiate_encrypt_then_mac()); } +bool Text_Policy::require_extended_master_secret() const { + return get_bool("require_extended_master_secret", Policy::require_extended_master_secret()); +} + std::optional Text_Policy::record_size_limit() const { const auto limit = get_len("record_size_limit", 0); // RFC 8449 4. @@ -121,7 +126,7 @@ } std::vector Text_Policy::key_exchange_groups_to_offer() const { - std::string group_str = get_str("key_exchange_groups_to_offer", "notset"); + const std::string group_str = get_str("key_exchange_groups_to_offer", "notset"); if(group_str.empty() || group_str == "notset") { // policy was not set, fall back to default behaviour @@ -240,7 +245,7 @@ if(group_id == Group_Params::NONE) { try { size_t consumed = 0; - unsigned long ll_id = std::stoul(group_name, &consumed, 0); + const unsigned long ll_id = std::stoul(group_name, &consumed, 0); if(consumed != group_name.size()) { continue; // some other cruft } diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_version.cpp botan3-3.12.0+dfsg/src/lib/tls/tls_version.cpp --- botan3-3.7.1+dfsg/src/lib/tls/tls_version.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_version.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -13,6 +13,24 @@ namespace Botan::TLS { +Protocol_Version Protocol_Version::latest_tls_version() { +#if defined(BOTAN_HAS_TLS_13) + return Protocol_Version::TLS_V13; +#elif defined(BOTAN_HAS_TLS_12) + return Protocol_Version::TLS_V12; +#else + throw Not_Implemented("This build contains no usable TLS version"); +#endif +} + +Protocol_Version Protocol_Version::latest_dtls_version() { +#if defined(BOTAN_HAS_TLS_12) + return Protocol_Version::DTLS_V12; +#else + throw Not_Implemented("This build contains no usable DTLS version"); +#endif +} + std::string Protocol_Version::to_string() const { const uint8_t maj = major_version(); const uint8_t min = minor_version(); @@ -82,11 +100,22 @@ } bool Protocol_Version::known_version() const { - return (m_version == static_cast(Protocol_Version::TLS_V12) || #if defined(BOTAN_HAS_TLS_13) - m_version == static_cast(Protocol_Version::TLS_V13) || + if(m_version == static_cast(Protocol_Version::TLS_V13)) { + return true; + } #endif - m_version == static_cast(Protocol_Version::DTLS_V12)); + +#if defined(BOTAN_HAS_TLS_12) + if(m_version == static_cast(Protocol_Version::TLS_V12)) { + return true; + } + if(m_version == static_cast(Protocol_Version::DTLS_V12)) { + return true; + } +#endif + + return false; } } // namespace Botan::TLS diff -Nru botan3-3.7.1+dfsg/src/lib/tls/tls_version.h botan3-3.12.0+dfsg/src/lib/tls/tls_version.h --- botan3-3.7.1+dfsg/src/lib/tls/tls_version.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/tls/tls_version.h 2026-05-07 01:38:28.000000000 +0000 @@ -36,26 +36,20 @@ using enum Version_Code; /** - * Returns the latest version of the TLS protocol known to the library - * (currently TLS v1.3) + * Returns the latest version of the TLS protocol known to the library and + * available in the current build. * * @return latest known TLS version */ - static Protocol_Version latest_tls_version() { -#if defined(BOTAN_HAS_TLS_13) - return Protocol_Version(TLS_V13); -#else - return Protocol_Version(TLS_V12); -#endif - } + static Protocol_Version latest_tls_version(); /** * Returns the latest version of the DTLS protocol known to the library - * (currently DTLS v1.2) + * and available in the current build. * * @return latest known DTLS version */ - static Protocol_Version latest_dtls_version() { return Protocol_Version(DTLS_V12); } + static Protocol_Version latest_dtls_version(); Protocol_Version() : m_version(0) {} @@ -64,7 +58,8 @@ /** * @param named_version a specific named version of the protocol */ - Protocol_Version(Version_Code named_version) : Protocol_Version(static_cast(named_version)) {} + Protocol_Version(Version_Code named_version) : // NOLINT(*-explicit-conversions) + Protocol_Version(static_cast(named_version)) {} /** * @param major the major version diff -Nru botan3-3.7.1+dfsg/src/lib/utils/alignment_buffer.h botan3-3.12.0+dfsg/src/lib/utils/alignment_buffer.h --- botan3-3.7.1+dfsg/src/lib/utils/alignment_buffer.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/alignment_buffer.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,13 +9,12 @@ #ifndef BOTAN_ALIGNMENT_BUFFER_H_ #define BOTAN_ALIGNMENT_BUFFER_H_ -#include -#include -#include - +#include +#include #include #include #include +#include namespace Botan { @@ -31,7 +30,7 @@ * input data is available in the BufferSlicer<>. This might result in some * performance overhead when using the must_be_deferred strategy. */ -enum class AlignmentBufferFinalBlock : size_t { +enum class AlignmentBufferFinalBlock : uint8_t { is_not_special = 0, must_be_deferred = 1, }; @@ -58,11 +57,11 @@ size_t BLOCK_SIZE, AlignmentBufferFinalBlock FINAL_BLOCK_STRATEGY = AlignmentBufferFinalBlock::is_not_special> requires(BLOCK_SIZE > 0) -class AlignmentBuffer { +class AlignmentBuffer final { public: - AlignmentBuffer() : m_position(0) {} + AlignmentBuffer() = default; - ~AlignmentBuffer() { secure_scrub_memory(m_buffer.data(), m_buffer.size()); } + ~AlignmentBuffer() { secure_zeroize_buffer(m_buffer.data(), sizeof(T) * m_buffer.size()); } AlignmentBuffer(const AlignmentBuffer& other) = default; AlignmentBuffer(AlignmentBuffer&& other) noexcept = default; @@ -70,7 +69,7 @@ AlignmentBuffer& operator=(AlignmentBuffer&& other) noexcept = default; void clear() { - clear_mem(m_buffer.data(), m_buffer.size()); + zeroize_buffer(m_buffer.data(), m_buffer.size()); m_position = 0; } @@ -79,7 +78,7 @@ */ void fill_up_with_zeros() { if(!ready_to_consume()) { - clear_mem(&m_buffer[m_position], elements_until_alignment()); + zeroize_buffer(&m_buffer[m_position], elements_until_alignment()); m_position = m_buffer.size(); } } @@ -236,8 +235,8 @@ } private: - std::array m_buffer; - size_t m_position; + std::array m_buffer = {}; + size_t m_position = 0; }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/allocator.cpp botan3-3.12.0+dfsg/src/lib/utils/allocator.cpp --- botan3-3.7.1+dfsg/src/lib/utils/allocator.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/allocator.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -28,6 +29,7 @@ } #if defined(BOTAN_HAS_LOCKING_ALLOCATOR) + // NOLINTNEXTLINE(*-const-correctness) bug in clang-tidy if(void* p = mlock_allocator::instance().allocate(elems, elem_size)) { return p; } @@ -36,9 +38,10 @@ #if defined(BOTAN_TARGET_OS_HAS_ALLOC_CONCEAL) void* ptr = ::calloc_conceal(elems, elem_size); #else - void* ptr = std::calloc(elems, elem_size); // NOLINT(*-no-malloc) + // NOLINTNEXTLINE(*-const-correctness) bug in clang-tidy + void* ptr = std::calloc(elems, elem_size); // NOLINT(*-no-malloc,*-owning-memory) #endif - if(!ptr) { + if(ptr == nullptr) { [[unlikely]] throw std::bad_alloc(); } return ptr; @@ -57,7 +60,7 @@ } #endif - std::free(p); // NOLINT(*-no-malloc) + std::free(p); // NOLINT(*-no-malloc,*-owning-memory) } void initialize_allocator() { diff -Nru botan3-3.7.1+dfsg/src/lib/utils/allocator.h botan3-3.12.0+dfsg/src/lib/utils/allocator.h --- botan3-3.7.1+dfsg/src/lib/utils/allocator.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/allocator.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,7 +8,6 @@ #define BOTAN_ALLOCATOR_HELPERS_H_ #include -#include namespace Botan { diff -Nru botan3-3.7.1+dfsg/src/lib/utils/api.h botan3-3.12.0+dfsg/src/lib/utils/api.h --- botan3-3.7.1+dfsg/src/lib/utils/api.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/api.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,8 @@ #include +// NOLINTBEGIN(*-macro-usage) + /** * Used to annotate API exports which are public and supported. * These APIs will not be broken/removed unless strictly required for @@ -39,6 +41,18 @@ #define BOTAN_TEST_API BOTAN_DLL /** +* This is used to mark constructors which are currently not `explicit` +* but which in a future major release be modified as such. +* +* TODO(Botan4) remove this macro and replace with `explicit` +*/ +#if defined(__clang_analyzer__) || defined(BOTAN_DISABLE_DEPRECATED_FEATURES) + #define BOTAN_FUTURE_EXPLICIT explicit +#else + #define BOTAN_FUTURE_EXPLICIT +#endif + +/** * Used to annotate API exports which are exported but only for the * purposes of fuzzing. They should not be used by applications and * may be removed or changed without notice. @@ -108,4 +122,6 @@ #define BOTAN_DIAGNOSTIC_POP #endif +// NOLINTEND(*-macro-usage) + #endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/assert.cpp botan3-3.12.0+dfsg/src/lib/utils/assert.cpp --- botan3-3.7.1+dfsg/src/lib/utils/assert.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/assert.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,7 +8,9 @@ #include #include +#include #include +#include #include #if defined(BOTAN_TERMINATE_ON_ASSERTS) @@ -26,18 +28,23 @@ throw Invalid_State(fmt("Invalid state: expr {} was false in {}:{}", expr, func, file)); } +// Declared in concepts.h +void ranges::memory_region_size_violation() { + throw Invalid_Argument("Memory regions did not have expected byte lengths"); +} + void assertion_failure(const char* expr_str, const char* assertion_made, const char* func, const char* file, int line) { std::ostringstream format; format << "False assertion "; - if(assertion_made && assertion_made[0] != 0) { + if(assertion_made != nullptr && assertion_made[0] != 0) { format << "'" << assertion_made << "' (expression " << expr_str << ") "; } else { format << expr_str << " "; } - if(func) { + if(func != nullptr) { format << "in " << func << " "; } diff -Nru botan3-3.7.1+dfsg/src/lib/utils/assert.h botan3-3.12.0+dfsg/src/lib/utils/assert.h --- botan3-3.7.1+dfsg/src/lib/utils/assert.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/assert.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,8 +11,12 @@ #include +BOTAN_FUTURE_INTERNAL_HEADER(assert.h) + namespace Botan { +// NOLINTBEGIN(*-macro-usage) + /** * Called when an assertion fails * Throws an Exception object @@ -27,9 +31,13 @@ [[noreturn]] void BOTAN_UNSTABLE_API throw_invalid_argument(const char* message, const char* func, const char* file); #define BOTAN_ARG_CHECK(expr, msg) \ + /* NOLINTNEXTLINE(*-avoid-do-while) */ \ do { \ - if(!(expr)) \ + /* NOLINTNEXTLINE(*-simplify-boolean-expr) */ \ + if(!(expr)) { \ + /* NOLINTNEXTLINE(bugprone-lambda-function-name) */ \ Botan::throw_invalid_argument(msg, __func__, __FILE__); \ + } \ } while(0) /** @@ -39,54 +47,77 @@ [[noreturn]] void BOTAN_UNSTABLE_API throw_invalid_state(const char* message, const char* func, const char* file); #define BOTAN_STATE_CHECK(expr) \ + /* NOLINTNEXTLINE(*-avoid-do-while) */ \ do { \ - if(!(expr)) \ + /* NOLINTNEXTLINE(*-simplify-boolean-expr) */ \ + if(!(expr)) { \ + /* NOLINTNEXTLINE(bugprone-lambda-function-name) */ \ Botan::throw_invalid_state(#expr, __func__, __FILE__); \ + } \ } while(0) /** * Make an assertion */ #define BOTAN_ASSERT(expr, assertion_made) \ + /* NOLINTNEXTLINE(*-avoid-do-while) */ \ do { \ - if(!(expr)) \ + /* NOLINTNEXTLINE(*-simplify-boolean-expr) */ \ + if(!(expr)) { \ + /* NOLINTNEXTLINE(bugprone-lambda-function-name) */ \ Botan::assertion_failure(#expr, assertion_made, __func__, __FILE__, __LINE__); \ + } \ } while(0) /** * Make an assertion */ #define BOTAN_ASSERT_NOMSG(expr) \ + /* NOLINTNEXTLINE(*-avoid-do-while) */ \ do { \ - if(!(expr)) \ + /* NOLINTNEXTLINE(*-simplify-boolean-expr) */ \ + if(!(expr)) { \ + /* NOLINTNEXTLINE(bugprone-lambda-function-name) */ \ Botan::assertion_failure(#expr, "", __func__, __FILE__, __LINE__); \ + } \ } while(0) /** * Assert that value1 == value2 */ #define BOTAN_ASSERT_EQUAL(expr1, expr2, assertion_made) \ + /* NOLINTNEXTLINE(*-avoid-do-while) */ \ do { \ - if((expr1) != (expr2)) \ + /* NOLINTNEXTLINE(*-simplify-boolean-expr) */ \ + if((expr1) != (expr2)) { \ + /* NOLINTNEXTLINE(bugprone-lambda-function-name) */ \ Botan::assertion_failure(#expr1 " == " #expr2, assertion_made, __func__, __FILE__, __LINE__); \ + } \ } while(0) /** * Assert that expr1 (if true) implies expr2 is also true */ #define BOTAN_ASSERT_IMPLICATION(expr1, expr2, msg) \ + /* NOLINTNEXTLINE(*-avoid-do-while) */ \ do { \ - if((expr1) && !(expr2)) \ + /* NOLINTNEXTLINE(*-simplify-boolean-expr) */ \ + if((expr1) && !(expr2)) { \ + /* NOLINTNEXTLINE(bugprone-lambda-function-name) */ \ Botan::assertion_failure(#expr1 " implies " #expr2, msg, __func__, __FILE__, __LINE__); \ + } \ } while(0) /** * Assert that a pointer is not null */ #define BOTAN_ASSERT_NONNULL(ptr) \ + /* NOLINTNEXTLINE(*-avoid-do-while) */ \ do { \ - if((ptr) == nullptr) \ + if((ptr) == nullptr) { \ + /* NOLINTNEXTLINE(bugprone-lambda-function-name) */ \ Botan::assertion_failure(#ptr " is not null", "", __func__, __FILE__, __LINE__); \ + } \ } while(0) #if defined(BOTAN_ENABLE_DEBUG_ASSERTS) @@ -95,8 +126,8 @@ #else - #define BOTAN_DEBUG_ASSERT(expr) \ - do { \ + #define BOTAN_DEBUG_ASSERT(expr) \ + do { /* NOLINT(*-avoid-do-while) */ \ } while(0) #endif @@ -107,13 +138,8 @@ * Takes any number of arguments and marks all as unused, for instance * BOTAN_UNUSED(a); or BOTAN_UNUSED(x, y, z); */ -template -constexpr void ignore_param(T&&) {} - template -constexpr void ignore_params(T&&... args) { - (ignore_param(args), ...); -} +constexpr void ignore_params([[maybe_unused]] const T&... args) {} #define BOTAN_UNUSED Botan::ignore_params @@ -136,6 +162,8 @@ #define BOTAN_ASSERT_UNREACHABLE() Botan::assert_unreachable(__FILE__, __LINE__) +// NOLINTEND(*-macro-usage) + } // namespace Botan #endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/bit_ops.h botan3-3.12.0+dfsg/src/lib/utils/bit_ops.h --- botan3-3.7.1+dfsg/src/lib/utils/bit_ops.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/bit_ops.h 2026-05-07 01:38:28.000000000 +0000 @@ -16,27 +16,41 @@ #include #include +#include +#include namespace Botan { /** -* If top bit of arg is set, return ~0. Otherwise return 0. +* If top bit of arg is set, return |1| (all bits set). Otherwise return |0| (all bits unset) */ -template -inline constexpr T expand_top_bit(T a) - requires(std::is_integral::value) -{ - return static_cast(0) - (a >> (sizeof(T) * 8 - 1)); +template +BOTAN_FORCE_INLINE constexpr T ct_expand_top_bit(T a) { + const T top = CT::value_barrier(a >> (sizeof(T) * 8 - 1)); + return static_cast(0) - top; } /** -* If arg is zero, return ~0. Otherwise return 0 +* If arg is zero, return |1|. Otherwise return |0| */ -template -inline constexpr T ct_is_zero(T x) - requires(std::is_integral::value) -{ - return expand_top_bit(~x & (x - 1)); +template +BOTAN_FORCE_INLINE constexpr T ct_is_zero(T x) { + return ct_expand_top_bit(~x & (x - 1)); +} + +/** +* If arg is zero, return the size_t `s`. Otherwise return the size_t zero. +*/ +template +BOTAN_FORCE_INLINE constexpr size_t ct_if_is_zero_ret(T x, size_t s) { + /* + Similar to `return ct_is_zero(x) & s` but has to account for possibility that + sizeof(T) is smaller than sizeof(size_t) which would lead to incomplete masking + */ + const T a = ~x & (x - 1); + const size_t a_top = static_cast(CT::value_barrier(a >> (sizeof(T) * 8 - 1))); + const size_t mask = static_cast(0) - a_top; + return mask & s; } /** @@ -44,10 +58,8 @@ * @param arg an integer value * @return true iff arg is 2^n for some n > 0 */ -template -inline constexpr bool is_power_of_2(T arg) - requires(std::is_unsigned::value) -{ +template +BOTAN_FORCE_INLINE constexpr bool is_power_of_2(T arg) { return (arg != 0) && (arg != 1) && ((arg & static_cast(arg - 1)) == 0); } @@ -57,14 +69,13 @@ * @param n an integer value * @return index of the highest set bit in n */ -template -inline constexpr size_t high_bit(T n) - requires(std::is_unsigned::value) -{ +template +BOTAN_FORCE_INLINE constexpr size_t high_bit(T n) { size_t hb = 0; for(size_t s = 8 * sizeof(T) / 2; s > 0; s /= 2) { - const size_t z = s * ((~ct_is_zero(n >> s)) & 1); + // Equivalent to: ((n >> s) == 0) ? 0 : s; + const size_t z = s - ct_if_is_zero_ret(n >> s, s); hb += z; n >>= z; } @@ -79,14 +90,13 @@ * @param n an integer value * @return number of significant bytes in n */ -template -inline constexpr size_t significant_bytes(T n) - requires(std::is_integral::value) -{ +template +BOTAN_FORCE_INLINE constexpr size_t significant_bytes(T n) { size_t b = 0; - for(size_t s = 8 * sizeof(n) / 2; s >= 8; s /= 2) { - const size_t z = s * (~ct_is_zero(n >> s) & 1); + for(size_t s = 8 * sizeof(T) / 2; s >= 8; s /= 2) { + // Equivalent to: ((n >> s) == 0) ? 0 : s; + const size_t z = s - ct_if_is_zero_ret(n >> s, s); b += z / 8; n >>= z; } @@ -101,19 +111,18 @@ * @param n an integer value * @return maximum x st 2^x divides n */ -template -inline constexpr size_t ctz(T n) - requires(std::is_integral::value) -{ +template +BOTAN_FORCE_INLINE constexpr size_t ctz(T n) { /* * If n == 0 then this function will compute 8*sizeof(T)-1, so * initialize lb to 1 if n == 0 to produce the expected result. */ - size_t lb = ct_is_zero(n) & 1; + size_t lb = ct_if_is_zero_ret(n, 1); for(size_t s = 8 * sizeof(T) / 2; s > 0; s /= 2) { - const T mask = (static_cast(1) << s) - 1; - const size_t z = s * (ct_is_zero(n & mask) & 1); + const T range = (static_cast(1) << s) - 1; + // Equivalent to: ((n & range) == 0) ? s : 0; + const size_t z = ct_if_is_zero_ret(n & range, s); lb += z; n >>= z; } @@ -121,17 +130,15 @@ return lb; } -template -inline constexpr T floor_log2(T n) - requires(std::is_unsigned::value) -{ +template +BOTAN_FORCE_INLINE constexpr T floor_log2(T n) { BOTAN_ARG_CHECK(n != 0, "log2(0) is not defined"); return static_cast(high_bit(n) - 1); } -template +template constexpr uint8_t ceil_log2(T x) - requires(std::is_integral::value && sizeof(T) < 32) + requires(sizeof(T) < 32) { if(x >> (sizeof(T) * 8 - 1)) { return sizeof(T) * 8; @@ -157,34 +164,32 @@ * @returns ceil(a/b) */ template -inline constexpr T ceil_division(T a, T b) { +BOTAN_FORCE_INLINE constexpr T ceil_division(T a, T b) { return (a + b - 1) / b; } /** * Return the number of bytes necessary to contain @p bits bits. */ -template -inline constexpr T ceil_tobytes(T bits) - requires(std::is_integral::value) -{ +template +BOTAN_FORCE_INLINE constexpr T ceil_tobytes(T bits) { return (bits + 7) / 8; } // Potentially variable time ctz used for OCB -inline constexpr size_t var_ctz32(uint32_t n) { -#if BOTAN_COMPILER_HAS_BUILTIN(__builtin_ctz) +BOTAN_FORCE_INLINE constexpr size_t var_ctz64(uint64_t n) { +#if BOTAN_COMPILER_HAS_BUILTIN(__builtin_ctzll) if(n == 0) { - return 32; + return 64; } - return __builtin_ctz(n); + return __builtin_ctzll(n); #else - return ctz(n); + return ctz(n); #endif } -template -inline constexpr T bit_permute_step(T x, T mask, size_t shift) { +template +BOTAN_FORCE_INLINE constexpr T bit_permute_step(T x, T mask, size_t shift) { /* See https://reflectionsonsecurity.wordpress.com/2014/05/11/efficient-bit-permutation-using-delta-swaps/ and http://programming.sirrida.de/bit_perm.html @@ -193,21 +198,28 @@ return (x ^ swap) ^ (swap << shift); } -template -inline constexpr void swap_bits(T& x, T& y, T mask, size_t shift) { +template +BOTAN_FORCE_INLINE constexpr void swap_bits(T& x, T& y, T mask, size_t shift) { const T swap = ((x >> shift) ^ y) & mask; x ^= swap << shift; y ^= swap; } -template -inline constexpr T choose(T mask, T a, T b) { +/** +* Bitwise selection +* +* If mask is |1| returns a +* If mask is |0| returns b +* If mask is some other value returns a or b depending on the bit +*/ +template +BOTAN_FORCE_INLINE constexpr T choose(T mask, T a, T b) { //return (mask & a) | (~mask & b); return (b ^ (mask & (a ^ b))); } -template -inline constexpr T majority(T a, T b, T c) { +template +BOTAN_FORCE_INLINE constexpr T majority(T a, T b, T c) { /* Considering each bit of a, b, c individually @@ -224,7 +236,7 @@ * @returns the reversed bits in @p b. */ template -constexpr T ct_reverse_bits(T b) { +inline constexpr T ct_reverse_bits(T b) { auto extend = [](uint8_t m) -> T { T mask = 0; for(size_t i = 0; i < sizeof(T); ++i) { @@ -258,7 +270,7 @@ * @returns the number of 1-bits in the provided value */ template -inline constexpr uint8_t ct_popcount(T x) { +BOTAN_FORCE_INLINE constexpr uint8_t ct_popcount(T x) { constexpr size_t s = sizeof(T); static_assert(s <= 8, "T is not a suitable unsigned integer value"); if constexpr(s == 8) { @@ -277,6 +289,41 @@ } } +/** +* Compile-time polynomial multiplication in GF(2^8) modulo an irreducible +* polynomial POLY +* +* When T is larger than a byte, the function computes the product of each byte +* of T and returns the packed result. +* +* This function is intended only for use at compile-time, and in particular +* should not be used with secret inputs. +* +* TODO(Botan4) this function should be consteval, but that hits bugs in +* older versions of GCC and Clang. +*/ +template +constexpr T poly_mul(T x, uint8_t y) { + // The constant 0x010101... as a T + constexpr T lo_bit = (static_cast(-1) / 255); + + // The constant 0x7F7F7F... as a T + constexpr T mask = static_cast(~(lo_bit << 7)); + + constexpr T poly = POLY; + + T r = 0; + while(x > 0 && y > 0) { + if((y & 1) != 0) { + r ^= x; + } + const T carry = ((x >> 7) & lo_bit) * poly; + x = ((x & mask) << 1) ^ carry; + y >>= 1; + } + return r; +} + } // namespace Botan #endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/bitvector/bitvector.h botan3-3.12.0+dfsg/src/lib/utils/bitvector/bitvector.h --- botan3-3.7.1+dfsg/src/lib/utils/bitvector/bitvector.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/bitvector/bitvector.h 2026-05-07 01:38:28.000000000 +0000 @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -65,7 +66,7 @@ // TODO: C++26 will bring Parameter Pack indexing: // auto first = s...[0]; template -constexpr static first_t first(T0&& t, Ts&&...) { +constexpr static first_t first(T0&& t, Ts&&... /*rest*/) { return std::forward(t); } @@ -278,11 +279,12 @@ ~bitref_base() = default; public: + // NOLINTNEXTLINE(*-explicit-conversions) constexpr operator bool() const noexcept { return is_set(); } constexpr bool is_set() const noexcept { return (m_block & m_mask) > 0; } - template + template constexpr T as() const noexcept { return static_cast(is_set()); } @@ -292,8 +294,8 @@ } protected: - BlockT& m_block; // NOLINT(*-non-private-member-variables-in-classes) - BlockT m_mask; // NOLINT(*-non-private-member-variables-in-classes) + BlockT& m_block; // NOLINT(*-non-private-member-variable*) + BlockT m_mask; // NOLINT(*-non-private-member-variable*) }; public: @@ -372,7 +374,7 @@ public: bitvector_base() : m_bits(0) {} - bitvector_base(size_type bits) : m_bits(bits), m_blocks(ceil_toblocks(bits)) {} + explicit bitvector_base(size_type bits) : m_bits(bits), m_blocks(ceil_toblocks(bits)) {} /** * Initialize the bitvector from a byte-array. Bits are taken byte-wise @@ -387,7 +389,9 @@ * @param bits The number of bits to be loaded. This must not be more * than the number of bytes in @p bytes. */ - bitvector_base(std::span bytes, std::optional bits = std::nullopt) { + bitvector_base(std::span bytes, /* NOLINT(*-explicit-conversions) */ + std::optional bits = std::nullopt) : + m_bits() { from_bytes(bytes, bits); } @@ -599,7 +603,9 @@ */ bitvector_base& set() { full_range_operation( - [](std::unsigned_integral auto block) -> decltype(block) { return static_cast(~0); }, + [](std::unsigned_integral auto block) -> decltype(block) { + return static_cast(~static_cast(0)); + }, *this); zero_unused_bits(); return *this; @@ -695,7 +701,7 @@ */ template > auto subvector(size_type pos, std::optional length = std::nullopt) const { - size_type bitlen = length.value_or(size() - pos); + const size_type bitlen = length.value_or(size() - pos); BOTAN_ARG_CHECK(pos + bitlen <= size(), "Not enough bits to copy"); OutT newvector(bitlen); @@ -709,9 +715,9 @@ newvector_unwrapped.m_blocks, std::span{m_blocks}.subspan(block_index(pos), block_index(pos + bitlen - 1) - block_index(pos) + 1)); } else { - BitRangeOperator, BitRangeAlignment::no_alignment> from_op( + const BitRangeOperator, BitRangeAlignment::no_alignment> from_op( *this, pos, bitlen); - BitRangeOperator> to_op( + const BitRangeOperator> to_op( unwrap_strong_type(newvector_unwrapped), 0, bitlen); range_operation([](auto /* to */, auto from) { return from; }, to_op, from_op); } @@ -743,7 +749,8 @@ if(pos % 8 == 0) { out = load_le(std::span{m_blocks}.subspan(block_index(pos)).template first()); } else { - BitRangeOperator, BitRangeAlignment::no_alignment> op(*this, pos, bits); + const BitRangeOperator, BitRangeAlignment::no_alignment> op( + *this, pos, bits); range_operation( [&](std::unsigned_integral auto integer) { if constexpr(std::same_as) { @@ -779,7 +786,7 @@ store_le(std::span{m_blocks}.subspan(block_index(pos)).template first(), unwrap_strong_type(value)); } else { - BitRangeOperator, BitRangeAlignment::no_alignment> op(*this, pos, bits); + const BitRangeOperator, BitRangeAlignment::no_alignment> op(*this, pos, bits); range_operation( [&](BlockT block) -> BlockT { if constexpr(std::same_as) { @@ -922,7 +929,7 @@ auto ref(size_type pos) { return bitref(m_blocks, pos); } private: - enum class BitRangeAlignment { byte_aligned, no_alignment }; + enum class BitRangeAlignment : uint8_t { byte_aligned, no_alignment }; /** * Helper construction to implement bit range operations on the bitvector. @@ -959,7 +966,7 @@ BOTAN_ASSERT(m_source.size() >= m_start_bitoffset + m_bitlength, "enough bytes in underlying source"); } - BitRangeOperator(BitvectorT& source) : BitRangeOperator(source, 0, source.size()) {} + explicit BitRangeOperator(BitvectorT& source) : BitRangeOperator(source, 0, source.size()) {} static constexpr bool is_byte_aligned() { return alignment == BitRangeAlignment::byte_aligned; } @@ -1088,7 +1095,7 @@ // std::align takes `ptr` as a reference (!), i.e. `void*&` and // uses it as an out-param. Though, `cptr` is const because this // method is const-qualified, hence the const_cast<>. - void* ptr = const_cast(cptr); + void* ptr = const_cast(cptr); // NOLINT(*-const-correctness) size_t size = sizeof(BlockT); return ptr_before != nullptr && std::align(alignof(BlockT), size, ptr, size) == ptr_before; } @@ -1178,7 +1185,7 @@ private: template - requires(all_same_v) + requires(all_same_v...>) constexpr static auto apply(FnT fn, size_type bits, BlockTs... blocks) { if constexpr(needs_mask) { return fn(blocks..., make_mask>(bits)); @@ -1304,7 +1311,7 @@ * prefer it. Otherwise, the allocator of @p lhs will be used as a default. */ template -constexpr auto copy_lhs_allocator_aware(const T1& lhs, const T2&) { +constexpr auto copy_lhs_allocator_aware(const T1& lhs, const T2& /*rhs*/) { constexpr bool needs_secure_allocator = strong_type_wrapped_type::uses_secure_allocator || strong_type_wrapped_type::uses_secure_allocator; @@ -1343,11 +1350,6 @@ return lhs.equals_vartime(rhs); } -template -bool operator!=(const T1& lhs, const T2& rhs) { - return lhs.equals_vartime(rhs); -} - namespace detail { /** diff -Nru botan3-3.7.1+dfsg/src/lib/utils/bitvector/info.txt botan3-3.12.0+dfsg/src/lib/utils/bitvector/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/bitvector/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/bitvector/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + BITVECTOR -> 20241202 - + name -> "Bitvector utility" diff -Nru botan3-3.7.1+dfsg/src/lib/utils/boost/info.txt botan3-3.12.0+dfsg/src/lib/utils/boost/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/boost/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/boost/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + BOOST_ASIO -> 20131228 - + name -> "Boost" diff -Nru botan3-3.7.1+dfsg/src/lib/utils/bswap.h botan3-3.12.0+dfsg/src/lib/utils/bswap.h --- botan3-3.7.1+dfsg/src/lib/utils/bswap.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/bswap.h 2026-05-07 01:38:28.000000000 +0000 @@ -15,6 +15,7 @@ #include #include +#include namespace Botan { diff -Nru botan3-3.7.1+dfsg/src/lib/utils/buffer_slicer.h botan3-3.12.0+dfsg/src/lib/utils/buffer_slicer.h --- botan3-3.7.1+dfsg/src/lib/utils/buffer_slicer.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/buffer_slicer.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,76 @@ +/* +* (C) 2023-2024 René Meusel - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_BUFFER_SLICER_H_ +#define BOTAN_BUFFER_SLICER_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace Botan { + +/** + * Helper class to ease unmarshalling of concatenated fixed-length values + */ +class BufferSlicer final { + public: + explicit BufferSlicer(std::span buffer) : m_remaining(buffer) {} + + template + auto copy(const size_t count) { + const auto result = take(count); + return ContainerT(result.begin(), result.end()); + } + + auto copy_as_vector(const size_t count) { return copy>(count); } + + auto copy_as_secure_vector(const size_t count) { return copy>(count); } + + std::span take(const size_t count) { + BOTAN_STATE_CHECK(remaining() >= count); + auto result = m_remaining.first(count); + m_remaining = m_remaining.subspan(count); + return result; + } + + template + std::span take() { + BOTAN_STATE_CHECK(remaining() >= count); + auto result = m_remaining.first(); + m_remaining = m_remaining.subspan(count); + return result; + } + + template + StrongSpan take(const size_t count) { + return StrongSpan(take(count)); + } + + uint8_t take_byte() { return take(1)[0]; } + + void copy_into(std::span sink) { + const auto data = take(sink.size()); + std::copy(data.begin(), data.end(), sink.begin()); + } + + void skip(const size_t count) { take(count); } + + size_t remaining() const { return m_remaining.size(); } + + bool empty() const { return m_remaining.empty(); } + + private: + std::span m_remaining; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/buffer_stuffer.h botan3-3.12.0+dfsg/src/lib/utils/buffer_stuffer.h --- botan3-3.7.1+dfsg/src/lib/utils/buffer_stuffer.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/buffer_stuffer.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,79 @@ +/* +* (C) 2023-2024 René Meusel - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_BUFFER_STUFFER_H_ +#define BOTAN_BUFFER_STUFFER_H_ + +#include +#include +#include +#include + +namespace Botan { + +/** + * @brief Helper class to ease in-place marshalling of concatenated fixed-length + * values. + * + * The size of the final buffer must be known from the start, reallocations are + * not performed. + */ +class BufferStuffer final { + public: + constexpr explicit BufferStuffer(std::span buffer) : m_buffer(buffer) {} + + /** + * @returns a span for the next @p bytes bytes in the concatenated buffer. + * Checks that the buffer is not exceeded. + */ + constexpr std::span next(size_t bytes) { + BOTAN_STATE_CHECK(m_buffer.size() >= bytes); + + auto result = m_buffer.first(bytes); + m_buffer = m_buffer.subspan(bytes); + return result; + } + + template + constexpr std::span next() { + BOTAN_STATE_CHECK(m_buffer.size() >= bytes); + + auto result = m_buffer.first(); + m_buffer = m_buffer.subspan(bytes); + return result; + } + + template + StrongSpan next(size_t bytes) { + return StrongSpan(next(bytes)); + } + + /** + * @returns a reference to the next single byte in the buffer + */ + constexpr uint8_t& next_byte() { return next(1)[0]; } + + constexpr void append(std::span buffer) { + auto sink = next(buffer.size()); + std::copy(buffer.begin(), buffer.end(), sink.begin()); + } + + constexpr void append(uint8_t b, size_t repeat = 1) { + auto sink = next(repeat); + std::fill(sink.begin(), sink.end(), b); + } + + constexpr bool full() const { return m_buffer.empty(); } + + constexpr size_t remaining_capacity() const { return m_buffer.size(); } + + private: + std::span m_buffer; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/calendar.cpp botan3-3.12.0+dfsg/src/lib/utils/calendar.cpp --- botan3-3.7.1+dfsg/src/lib/utils/calendar.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/calendar.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -18,17 +19,21 @@ namespace { +// TODO replace this with https://howardhinnant.github.io/date_algorithms.html#civil_from_days std::tm do_gmtime(std::time_t time_val) { - std::tm tm; + std::tm tm{}; #if defined(BOTAN_TARGET_OS_HAS_WIN32) ::gmtime_s(&tm, &time_val); // Windows #elif defined(BOTAN_TARGET_OS_HAS_POSIX1) - ::gmtime_r(&time_val, &tm); // Unix/SUSv2 + if(::gmtime_r(&time_val, &tm) == nullptr) { + throw Encoding_Error("do_gmtime could not convert"); + } #else std::tm* tm_p = std::gmtime(&time_val); - if(tm_p == nullptr) - throw Encoding_Error("time_t_to_tm could not convert"); + if(tm_p == nullptr) { + throw Encoding_Error("do_gmtime could not convert"); + } tm = *tm_p; #endif @@ -41,7 +46,7 @@ Algorithm due to Howard Hinnant See https://howardhinnant.github.io/date_algorithms.html#days_from_civil -for details and explaination. The code is slightly simplified by our assumption +for details and explanation. The code is slightly simplified by our assumption that the date is at least 1970, which is sufficient for our purposes. */ uint64_t days_since_epoch(uint32_t year, uint32_t month, uint32_t day) { @@ -84,7 +89,7 @@ } calendar_point::calendar_point(const std::chrono::system_clock::time_point& time_point) { - std::tm tm = do_gmtime(std::chrono::system_clock::to_time_t(time_point)); + const std::tm tm = do_gmtime(std::chrono::system_clock::to_time_t(time_point)); m_year = tm.tm_year + 1900; m_month = tm.tm_mon + 1; diff -Nru botan3-3.7.1+dfsg/src/lib/utils/calendar.h botan3-3.12.0+dfsg/src/lib/utils/calendar.h --- botan3-3.7.1+dfsg/src/lib/utils/calendar.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/calendar.h 2026-05-07 01:38:28.000000000 +0000 @@ -18,7 +18,7 @@ /** * Struct representing a particular date and time */ -class BOTAN_TEST_API calendar_point { +class BOTAN_TEST_API calendar_point final { public: /** The year */ uint32_t year() const { return m_year; } @@ -56,7 +56,7 @@ * Convert a time_point to a calendar_point * @param time_point a time point from the system clock */ - calendar_point(const std::chrono::system_clock::time_point& time_point); + explicit calendar_point(const std::chrono::system_clock::time_point& time_point); /** * Return seconds since epoch diff -Nru botan3-3.7.1+dfsg/src/lib/utils/charset.cpp botan3-3.12.0+dfsg/src/lib/utils/charset.cpp --- botan3-3.7.1+dfsg/src/lib/utils/charset.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/charset.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -49,8 +49,73 @@ } } +uint32_t next_utf8_codepoint(const std::string& utf8, size_t& pos) { + auto read_continuation = [&]() -> uint32_t { + if(pos >= utf8.size()) { + throw Decoding_Error("Invalid UTF-8 sequence"); + } + const uint8_t b = static_cast(utf8[pos++]); + if((b & 0xC0) != 0x80) { + throw Decoding_Error("Invalid UTF-8 sequence"); + } + return b & 0x3F; + }; + + const uint8_t lead = static_cast(utf8[pos++]); + uint32_t c = 0; + + if(lead <= 0x7F) { + c = lead; + } else if((lead & 0xE0) == 0xC0) { + c = (lead & 0x1F) << 6; + c |= read_continuation(); + if(c < 0x80) { + throw Decoding_Error("Overlong UTF-8 sequence"); + } + } else if((lead & 0xF0) == 0xE0) { + c = (lead & 0x0F) << 12; + c |= read_continuation() << 6; + c |= read_continuation(); + if(c < 0x800) { + throw Decoding_Error("Overlong UTF-8 sequence"); + } + } else if((lead & 0xF8) == 0xF0) { + c = (lead & 0x07) << 18; + c |= read_continuation() << 12; + c |= read_continuation() << 6; + c |= read_continuation(); + if(c < 0x10000) { + throw Decoding_Error("Overlong UTF-8 sequence"); + } + } else { + throw Decoding_Error("Invalid UTF-8 sequence"); + } + + if(c > 0x10FFFF) { + throw Decoding_Error("UTF-8 sequence encodes value outside Unicode range"); + } + if(c >= 0xD800 && c < 0xE000) { + throw Decoding_Error("UTF-8 sequence encodes surrogate code point"); + } + + return c; +} + } // namespace +bool is_valid_utf8(const std::string& utf8) { + try { + size_t pos = 0; + while(pos < utf8.size()) { + const uint32_t c = next_utf8_codepoint(utf8, pos); + BOTAN_UNUSED(c); + } + } catch(Decoding_Error&) { + return false; + } + return true; +} + std::string ucs2_to_utf8(const uint8_t ucs2[], size_t len) { if(len % 2 != 0) { throw Decoding_Error("Invalid length for UCS-2 string"); @@ -67,6 +132,24 @@ return s; } +std::vector utf8_to_ucs2(const std::string& utf8) { + std::vector out; + out.reserve(utf8.size() * 2); + + size_t pos = 0; + while(pos < utf8.size()) { + const uint32_t c = next_utf8_codepoint(utf8, pos); + if(c > 0xFFFF) { + throw Decoding_Error("Cannot encode character in UCS-2"); + } + const uint16_t val = static_cast(c); + out.push_back(get_byte<0>(val)); + out.push_back(get_byte<1>(val)); + } + + return out; +} + std::string ucs4_to_utf8(const uint8_t ucs4[], size_t len) { if(len % 4 != 0) { throw Decoding_Error("Invalid length for UCS-4 string"); @@ -83,6 +166,22 @@ return s; } +std::vector utf8_to_ucs4(const std::string& utf8) { + std::vector out; + out.reserve(utf8.size() * 4); + + size_t pos = 0; + while(pos < utf8.size()) { + const uint32_t val = next_utf8_codepoint(utf8, pos); + out.push_back(get_byte<0>(val)); + out.push_back(get_byte<1>(val)); + out.push_back(get_byte<2>(val)); + out.push_back(get_byte<3>(val)); + } + + return out; +} + /* * Convert from ISO 8859-1 to UTF-8 */ @@ -107,7 +206,7 @@ } else if(c == '\r') { oss << "\\r"; } else if(static_cast(c) >= 128) { - unsigned char z = static_cast(c); + const unsigned char z = static_cast(c); oss << "\\x" << std::hex << std::uppercase << static_cast(z); } else { oss << c; diff -Nru botan3-3.7.1+dfsg/src/lib/utils/charset.h botan3-3.12.0+dfsg/src/lib/utils/charset.h --- botan3-3.7.1+dfsg/src/lib/utils/charset.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/charset.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,9 +10,13 @@ #include #include +#include +#include namespace Botan { +// TODO convert these to take arguments as spans or std::string_view + /** * Convert a sequence of UCS-2 (big endian) characters to a UTF-8 string * This is used for ASN.1 BMPString type @@ -22,6 +26,17 @@ BOTAN_TEST_API std::string ucs2_to_utf8(const uint8_t ucs2[], size_t len); /** + * Convert a UTF-8 string to a sequence of UCS-2 (big endian) characters + * This is used for ASN.1 BMPString type + * @param utf8 the UTF-8 string + * @return a vector of bytes containing the UCS-2 (big endian) encoding + * @throws Decoding_Error if the input is not valid UTF-8 (including overlong encodings, + * surrogate code points, or values outside Unicode), or if a code point exceeds + * U+FFFF and cannot be represented in UCS-2 + */ +BOTAN_TEST_API std::vector utf8_to_ucs2(const std::string& utf8); + +/** * Convert a sequence of UCS-4 (big endian) characters to a UTF-8 string * This is used for ASN.1 UniversalString type * @param ucs4 the sequence of UCS-4 characters @@ -29,9 +44,24 @@ */ BOTAN_TEST_API std::string ucs4_to_utf8(const uint8_t ucs4[], size_t len); +/** + * Convert a UTF-8 string to a sequence of UCS-4 (big endian) characters + * This is used for ASN.1 UniversalString type + * @param utf8 the UTF-8 string + * @return a vector of bytes containing the UCS-4 (big endian) encoding + * @throws Decoding_Error if the input is not valid UTF-8 (including overlong encodings, + * surrogate code points, or values outside the Unicode scalar value range U+0000..U+10FFFF) + */ +BOTAN_TEST_API std::vector utf8_to_ucs4(const std::string& utf8); + BOTAN_TEST_API std::string latin1_to_utf8(const uint8_t latin1[], size_t len); /** +* Return true if this string seems to contain a valid sequence of UTF-8 +*/ +bool is_valid_utf8(const std::string& str); + +/** * Return a string containing 'c', quoted and possibly escaped * * This is used when creating an error message nothing an invalid character diff -Nru botan3-3.7.1+dfsg/src/lib/utils/codec_base.h botan3-3.12.0+dfsg/src/lib/utils/codec_base.h --- botan3-3.7.1+dfsg/src/lib/utils/codec_base.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/codec_base.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,8 +11,9 @@ #include #include +#include #include -#include +#include namespace Botan { @@ -31,12 +32,17 @@ * @return number of bytes written to output */ template -size_t base_encode( - Base&& base, char output[], const uint8_t input[], size_t input_length, size_t& input_consumed, bool final_inputs) { +size_t base_encode(const Base& base, + char output[], + const uint8_t input[], + size_t input_length, + size_t& input_consumed, + bool final_inputs) { input_consumed = 0; - const size_t encoding_bytes_in = base.encoding_bytes_in(); - const size_t encoding_bytes_out = base.encoding_bytes_out(); + // TODO(Botan4) Check if we can use just base. or Base:: here instead + constexpr size_t encoding_bytes_in = std::remove_reference_t::encoding_bytes_in(); + constexpr size_t encoding_bytes_out = std::remove_reference_t::encoding_bytes_out(); size_t input_remaining = input_length; size_t output_produced = 0; @@ -50,7 +56,7 @@ } if(final_inputs && input_remaining) { - std::vector remainder(encoding_bytes_in, 0); + std::array remainder{}; for(size_t i = 0; i != input_remaining; ++i) { remainder[i] = input[input_consumed + i]; } @@ -75,7 +81,7 @@ } template -std::string base_encode_to_string(Base&& base, const uint8_t input[], size_t input_length) { +std::string base_encode_to_string(const Base& base, const uint8_t input[], size_t input_length) { const size_t output_length = base.encode_max_output(input_length); std::string output(output_length, 0); @@ -109,18 +115,19 @@ * @return number of bytes written to output */ template -size_t base_decode(Base&& base, +size_t base_decode(const Base& base, uint8_t output[], const char input[], size_t input_length, size_t& input_consumed, bool final_inputs, bool ignore_ws = true) { - const size_t decoding_bytes_in = base.decoding_bytes_in(); - const size_t decoding_bytes_out = base.decoding_bytes_out(); + // TODO(Botan4) Check if we can use just base. or Base:: here instead + constexpr size_t decoding_bytes_in = std::remove_reference_t::decoding_bytes_in(); + constexpr size_t decoding_bytes_out = std::remove_reference_t::decoding_bytes_out(); uint8_t* out_ptr = output; - std::vector decode_buf(decoding_bytes_in, 0); + std::array decode_buf{}; size_t decode_buf_pos = 0; size_t final_truncate = 0; @@ -162,13 +169,13 @@ ++input_consumed; } - size_t written = (out_ptr - output) - base.bytes_to_remove(final_truncate); + const size_t written = (out_ptr - output) - base.bytes_to_remove(final_truncate); return written; } template -size_t base_decode_full(Base&& base, uint8_t output[], const char input[], size_t input_length, bool ignore_ws) { +size_t base_decode_full(const Base& base, uint8_t output[], const char input[], size_t input_length, bool ignore_ws) { size_t consumed = 0; const size_t written = base_decode(base, output, input, input_length, consumed, true, ignore_ws); @@ -180,7 +187,7 @@ } template -Vector base_decode_to_vec(Base&& base, const char input[], size_t input_length, bool ignore_ws) { +Vector base_decode_to_vec(const Base& base, const char input[], size_t input_length, bool ignore_ws) { const size_t output_length = base.decode_max_output(input_length); Vector bin(output_length); diff -Nru botan3-3.7.1+dfsg/src/lib/utils/compiler.h botan3-3.12.0+dfsg/src/lib/utils/compiler.h --- botan3-3.7.1+dfsg/src/lib/utils/compiler.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/compiler.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,6 +11,8 @@ #include #include +// NOLINTBEGIN(*-macro-usage) + BOTAN_FUTURE_INTERNAL_HEADER(compiler.h) /* @@ -34,9 +36,21 @@ #endif /* +* Hack for Loongarch64 GCC bug +* +* For some reason __has_attribute(target) is true, but it does not support the +* target attribute... this supposedly is fixed in GCC 15 but this is untested. +*/ +#if defined(__GNUC__) && defined(__loongarch64) && (__GNUC__ <= 14) + #define BOTAN_COMPILER_DOES_NOT_HAVE_TARGET_ATTRIBUTE +#endif + +/* * Define BOTAN_FUNC_ISA +* +* TODO(Botan4) Move this to isa_extn.h */ -#if BOTAN_COMPILER_HAS_ATTRIBUTE(target) +#if BOTAN_COMPILER_HAS_ATTRIBUTE(target) && !defined(BOTAN_COMPILER_DOES_NOT_HAVE_TARGET_ATTRIBUTE) #define BOTAN_FUNC_ISA(isa) BOTAN_COMPILER_ATTRIBUTE(target(isa)) #else #define BOTAN_FUNC_ISA(isa) @@ -44,6 +58,8 @@ /* * Define BOTAN_FUNC_ISA_INLINE +* +* TODO(Botan4) Remove this */ #define BOTAN_FUNC_ISA_INLINE(isa) BOTAN_FUNC_ISA(isa) BOTAN_FORCE_INLINE @@ -73,4 +89,6 @@ #endif +// NOLINTEND(*-macro-usage) + #endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/concat_util.h botan3-3.12.0+dfsg/src/lib/utils/concat_util.h --- botan3-3.7.1+dfsg/src/lib/utils/concat_util.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/concat_util.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,121 @@ +/* +* (C) 2023-2024 René Meusel - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_CONCAT_UTIL_H_ +#define BOTAN_CONCAT_UTIL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Botan { + +namespace detail { + +/** + * Helper function that performs range size-checks as required given the + * selected output and input range types. If all lengths are known at compile + * time, this check will be performed at compile time as well. It will then + * instantiate an output range and concatenate the input ranges' contents. + */ +template +constexpr OutR concatenate(Rs&&... ranges) + requires(concepts::reservable_container || ranges::statically_spanable_range) +{ + OutR result{}; + + // Prepare and validate the output range and construct a lambda that does the + // actual filling of the result buffer. + // (if no input ranges are given, GCC claims that fill_fn is unused) + [[maybe_unused]] auto fill_fn = [&] { + if constexpr(concepts::reservable_container) { + // dynamically allocate the correct result byte length + const size_t total_size = (ranges.size() + ... + 0); + result.reserve(total_size); + + // fill the result buffer using a back-inserter + return [&result](auto&& range) { + std::copy( + std::ranges::begin(range), std::ranges::end(range), std::back_inserter(unwrap_strong_type(result))); + }; + } else { + if constexpr((ranges::statically_spanable_range && ... && true)) { + // all input ranges have a static extent, so check the total size at compile time + // (work around an issue in MSVC that warns `total_size` is unused) + [[maybe_unused]] constexpr size_t total_size = (decltype(std::span{ranges})::extent + ... + 0); + static_assert(result.size() == total_size, "size of result buffer does not match the sum of input buffers"); + } else { + // at least one input range has a dynamic extent, so check the total size at runtime + const size_t total_size = (ranges.size() + ... + 0); + BOTAN_ARG_CHECK(result.size() == total_size, + "result buffer has static extent that does not match the sum of input buffers"); + } + + // fill the result buffer and hold the current output-iterator position + return [itr = std::ranges::begin(result)](auto&& range) mutable { + std::copy(std::ranges::begin(range), std::ranges::end(range), itr); + std::advance(itr, std::ranges::size(range)); + }; + } + }(); + + // perform the actual concatenation + (fill_fn(std::forward(ranges)), ...); + + return result; +} + +} // namespace detail + +/** + * Concatenate an arbitrary number of buffers. Performs range-checks as needed. + * + * The output type can be auto-detected based on the input ranges, or explicitly + * specified by the caller. If all input ranges have a static extent, the total + * size is calculated at compile time and a statically sized std::array<> is used. + * Otherwise this tries to use the type of the first input range as output type. + * + * Alternatively, the output container type can be specified explicitly. + */ +template +constexpr auto concat(Rs&&... ranges) + requires(all_same_v...>) +{ + if constexpr(std::same_as) { + // Try to auto-detect a reasonable output type given the input ranges + static_assert(sizeof...(Rs) > 0, "Cannot auto-detect the output type if not a single input range is provided."); + using candidate_result_t = std::remove_cvref_t>>; + using result_range_value_t = std::remove_cvref_t>; + + if constexpr((ranges::statically_spanable_range && ...)) { + // If all input ranges have a static extent, we can calculate the total size at compile time + // and therefore can use a statically sized output container. This is constexpr. + constexpr size_t total_size = (decltype(std::span{ranges})::extent + ... + 0); + using out_array_t = std::array; + return detail::concatenate(std::forward(ranges)...); + } else { + // If at least one input range has a dynamic extent, we must use a dynamically allocated output container. + // We assume that the user wants to use the first input range's container type as output type. + static_assert( + concepts::reservable_container, + "First input range has static extent, but a dynamically allocated output range is required. Please explicitly specify a dynamically allocatable output type."); + return detail::concatenate(std::forward(ranges)...); + } + } else { + // The caller has explicitly specified the output type + return detail::concatenate(std::forward(ranges)...); + } +} + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/concepts.h botan3-3.12.0+dfsg/src/lib/utils/concepts.h --- botan3-3.7.1+dfsg/src/lib/utils/concepts.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/concepts.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,30 +9,11 @@ #ifndef BOTAN_CONCEPTS_H_ #define BOTAN_CONCEPTS_H_ -#include - -#include +#include #include -#include -#include -#include -#include -#include namespace Botan { -template -class Strong; - -template -struct is_strong_type : std::false_type {}; - -template -struct is_strong_type> : std::true_type {}; - -template -constexpr bool is_strong_type_v = is_strong_type...>::value; - template struct all_same { static constexpr bool value = (std::is_same_v && ... && true); @@ -53,95 +34,6 @@ } // namespace detail -namespace ranges { - -/** - * Models a std::ranges::contiguous_range that (optionally) restricts its - * value_type to ValueT. In other words: a stretch of contiguous memory of - * a certain type (optional ValueT). - */ -template > -concept contiguous_range = std::ranges::contiguous_range && std::same_as>; - -/** - * Models a std::ranges::contiguous_range that satisfies - * std::ranges::output_range with an arbitrary value_type. In other words: a - * stretch of contiguous memory of a certain type (optional ValueT) that can be - * written to. - */ -template > -concept contiguous_output_range = contiguous_range && std::ranges::output_range; - -/** - * Models a range that can be turned into a std::span<>. Typically, this is some - * form of ranges::contiguous_range. - */ -template -concept spanable_range = std::constructible_from>, T>; - -/** - * Models a range that can be turned into a std::span<> with a static extent. - * Typically, this is a std::array or a std::span derived from an array. - */ -// clang-format off -template -concept statically_spanable_range = spanable_range && - decltype(std::span{std::declval()})::extent != std::dynamic_extent; - -// clang-format on - -/** - * Find the length in bytes of a given contiguous range @p r. - */ -inline constexpr size_t size_bytes(spanable_range auto&& r) { - return std::span{r}.size_bytes(); -} - -/** - * Check that a given range @p r has a certain statically-known byte length. If - * the range's extent is known at compile time, this is a static check, - * otherwise a runtime argument check will be added. - * - * @throws Invalid_Argument if range @p r has a dynamic extent and does not - * feature the expected byte length. - */ -template -inline constexpr void assert_exact_byte_length(R&& r) { - const std::span s{r}; - if constexpr(statically_spanable_range) { - static_assert(s.size_bytes() == expected, "memory region does not have expected byte lengths"); - } else { - BOTAN_ASSERT(s.size_bytes() == expected, "memory region does not have expected byte lengths"); - } -} - -/** - * Check that a list of ranges (in @p r0 and @p rs) all have the same byte - * lengths. If the first range's extent is known at compile time, this will be a - * static check for all other ranges whose extents are known at compile time, - * otherwise a runtime argument check will be added. - * - * @throws Invalid_Argument if any range has a dynamic extent and not all - * ranges feature the same byte length. - */ -template -inline constexpr void assert_equal_byte_lengths(R0&& r0, Rs&&... rs) - requires(sizeof...(Rs) > 0) -{ - const std::span s0{r0}; - - if constexpr(statically_spanable_range) { - constexpr size_t expected_size = s0.size_bytes(); - (assert_exact_byte_length(rs), ...); - } else { - const size_t expected_size = s0.size_bytes(); - BOTAN_ARG_CHECK(((std::span>{rs}.size_bytes() == expected_size) && ...), - "memory regions don't have equal lengths"); - } -} - -} // namespace ranges - namespace concepts { // TODO: C++20 provides concepts like std::ranges::range or ::sized_range @@ -176,19 +68,6 @@ { a.empty() } -> std::same_as; }; -// clang-format off -template -concept has_bounds_checked_accessors = container && ( - requires(T a, const T ac, typename T::size_type s) { - { a.at(s) } -> std::same_as; - { ac.at(s) } -> std::same_as; - } || - requires(T a, const T ac, typename T::key_type k) { - { a.at(k) } -> std::same_as; - { ac.at(k) } -> std::same_as; - }); -// clang-format on - template concept resizable_container = container && requires(T& c, typename T::size_type s) { T(s); @@ -202,24 +81,6 @@ concept resizable_byte_buffer = contiguous_container && resizable_container && std::same_as; -template -concept streamable = requires(std::ostream& os, T a) { os << a; }; - -template -concept strong_type = is_strong_type_v; - -template -concept contiguous_strong_type = strong_type && contiguous_container; - -template -concept integral_strong_type = strong_type && std::integral; - -template -concept unsigned_integral_strong_type = strong_type && std::unsigned_integral; - -template -concept strong_type_with_capability = T::template has_capability(); - } // namespace concepts } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid.cpp botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid.cpp --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,11 +7,8 @@ #include -#include #include -#include #include -#include #if defined(BOTAN_HAS_OS_UTILS) #include @@ -19,68 +16,33 @@ namespace Botan { -//static -std::string CPUID::to_string() { - std::vector flags; +#if !defined(BOTAN_HAS_CPUID_DETECTION) +uint32_t CPUFeature::as_u32() const { + throw Invalid_State("CPUFeature invalid bit"); +} - auto append_fn = [&](bool flag, const char* flag_name) { - if(flag) { - flags.push_back(flag_name); - } - }; +std::optional CPUFeature::from_string(std::string_view) { + return {}; +} - // NOLINTNEXTLINE(*-macro-usage) -#define CPUID_PRINT(flag) append_fn(has_##flag(), #flag) +std::string CPUFeature::to_string() const { + throw Invalid_State("CPUFeature invalid bit"); +} +#endif -#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) - CPUID_PRINT(rdtsc); +//static +std::string CPUID::to_string() { + std::vector flags; - CPUID_PRINT(sse2); - CPUID_PRINT(ssse3); - CPUID_PRINT(avx2); - - CPUID_PRINT(bmi2); - CPUID_PRINT(adx); - CPUID_PRINT(gfni); - - CPUID_PRINT(aes_ni); - CPUID_PRINT(clmul); - CPUID_PRINT(rdrand); - CPUID_PRINT(rdseed); - CPUID_PRINT(intel_sha); - CPUID_PRINT(intel_sha512); - - CPUID_PRINT(avx2_vaes); - CPUID_PRINT(avx2_clmul); - - CPUID_PRINT(avx512); - CPUID_PRINT(avx512_aes); - CPUID_PRINT(avx512_clmul); - - CPUID_PRINT(intel_sm3); - CPUID_PRINT(intel_sm4); - -#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) - CPUID_PRINT(altivec); - CPUID_PRINT(power_crypto); - CPUID_PRINT(darn_rng); -#elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) - CPUID_PRINT(neon); - CPUID_PRINT(arm_sve); - - CPUID_PRINT(arm_sha1); - CPUID_PRINT(arm_sha2); - CPUID_PRINT(arm_aes); - CPUID_PRINT(arm_pmull); - CPUID_PRINT(arm_sha2_512); - CPUID_PRINT(arm_sha3); - CPUID_PRINT(arm_sm3); - CPUID_PRINT(arm_sm4); -#else - BOTAN_UNUSED(append_fn); -#endif + const uint32_t bitset = state().bitset(); -#undef CPUID_PRINT + for(size_t i = 0; i != 32; ++i) { + const uint32_t b = static_cast(1) << i; + if((bitset & b) == b) { + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) + flags.push_back(CPUFeature(static_cast(b)).to_string()); + } + } return string_join(flags, ' '); } @@ -90,35 +52,10 @@ state() = CPUID_Data(); } -namespace { +#if defined(BOTAN_HAS_CPUID_DETECTION) -// Returns true if big-endian -bool runtime_check_if_big_endian() { - // Check runtime endian - const uint32_t endian32 = 0x01234567; - const uint8_t* e8 = reinterpret_cast(&endian32); - - bool is_big_endian = false; - - if(e8[0] == 0x01 && e8[1] == 0x23 && e8[2] == 0x45 && e8[3] == 0x67) { - is_big_endian = true; - } else if(e8[0] == 0x67 && e8[1] == 0x45 && e8[2] == 0x23 && e8[3] == 0x01) { - is_big_endian = false; - } else { - throw Internal_Error("Unexpected endian at runtime, neither big nor little"); - } - - // If we were compiled with a known endian, verify it matches at runtime -#if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) - BOTAN_ASSERT(!is_big_endian, "Build and runtime endian match"); -#elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) - BOTAN_ASSERT(is_big_endian, "Build and runtime endian match"); -#endif - - return is_big_endian; -} +namespace { -#if defined(BOTAN_CPUID_HAS_DETECTION) uint32_t cleared_cpuid_bits() { uint32_t cleared = 0; @@ -126,8 +63,8 @@ std::string clear_cpuid_env; if(OS::read_env_variable(clear_cpuid_env, "BOTAN_CLEAR_CPUID")) { for(const auto& cpuid : split_on(clear_cpuid_env, ',')) { - for(auto& bit : CPUID::bit_from_string(cpuid)) { - cleared |= bit; + if(auto bit = CPUID::bit_from_string(cpuid)) { + cleared |= bit->as_u32(); } } } @@ -135,106 +72,23 @@ return cleared; } -#endif } // namespace -CPUID::CPUID_Data::CPUID_Data() { - m_processor_features = 0; - -#if defined(BOTAN_CPUID_HAS_DETECTION) - m_processor_features = detect_cpu_features(~cleared_cpuid_bits()); #endif - m_processor_features |= CPUID::CPUID_INITIALIZED_BIT; - - if(runtime_check_if_big_endian()) { - m_processor_features |= CPUID::CPUID_IS_BIG_ENDIAN_BIT; - } -} - -std::vector CPUID::bit_from_string(std::string_view tok) { -#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) - if(tok == "sse2" || tok == "simd") { - return {CPUID::CPUID_SSE2_BIT}; - } else if(tok == "ssse3") { - return {CPUID::CPUID_SSSE3_BIT}; - } else if(tok == "aesni" || tok == "aes_ni") { - // aes_ni is the string printed on the console when running "botan cpuid" - return {CPUID::CPUID_AESNI_BIT}; - } else if(tok == "clmul") { - return {CPUID::CPUID_CLMUL_BIT}; - } else if(tok == "avx2") { - return {CPUID::CPUID_AVX2_BIT}; - } else if(tok == "avx512") { - return {CPUID::CPUID_AVX512_BIT}; - } - // there were two if statements testing "sha" and "intel_sha" separately; combined - else if(tok == "sha" || tok == "intel_sha") { - return {CPUID::CPUID_SHA_BIT}; - } else if(tok == "rdtsc") { - return {CPUID::CPUID_RDTSC_BIT}; - } else if(tok == "bmi2") { - return {CPUID::CPUID_BMI_BIT}; - } else if(tok == "adx") { - return {CPUID::CPUID_ADX_BIT}; - } else if(tok == "gfni") { - return {CPUID::CPUID_GFNI_BIT}; - } else if(tok == "rdrand") { - return {CPUID::CPUID_RDRAND_BIT}; - } else if(tok == "rdseed") { - return {CPUID::CPUID_RDSEED_BIT}; - } else if(tok == "avx512_aes") { - return {CPUID::CPUID_AVX512_AES_BIT}; - } else if(tok == "avx512_clmul") { - return {CPUID::CPUID_AVX512_CLMUL_BIT}; - } else if(tok == "avx2_vaes") { - return {CPUID::CPUID_AVX2_AES_BIT}; - } else if(tok == "avx2_clmul") { - return {CPUID::CPUID_AVX2_CLMUL_BIT}; - } else if(tok == "intel_sm3") { - return {CPUID::CPUID_SM3_BIT}; - } else if(tok == "intel_sm4") { - return {CPUID::CPUID_SM4_BIT}; - } - -#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) - if(tok == "altivec" || tok == "simd") { - return {CPUID::CPUID_ALTIVEC_BIT}; - } else if(tok == "power_crypto") { - return {CPUID::CPUID_POWER_CRYPTO_BIT}; - } else if(tok == "darn_rng") { - return {CPUID::CPUID_DARN_BIT}; - } - -#elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) - if(tok == "neon" || tok == "simd") { - return {CPUID::CPUID_ARM_NEON_BIT}; - } else if(tok == "arm_sve") { - return {CPUID::CPUID_ARM_SVE_BIT}; - } else if(tok == "armv8sha1" || tok == "arm_sha1") { - return {CPUID::CPUID_ARM_SHA1_BIT}; - } else if(tok == "armv8sha2" || tok == "arm_sha2") { - return {CPUID::CPUID_ARM_SHA2_BIT}; - } else if(tok == "armv8aes" || tok == "arm_aes") { - return {CPUID::CPUID_ARM_AES_BIT}; - } else if(tok == "armv8pmull" || tok == "arm_pmull") { - return {CPUID::CPUID_ARM_PMULL_BIT}; - } else if(tok == "armv8sha3" || tok == "arm_sha3") { - return {CPUID::CPUID_ARM_SHA3_BIT}; - } else if(tok == "armv8sha2_512" || tok == "arm_sha2_512") { - return {CPUID::CPUID_ARM_SHA2_512_BIT}; - } else if(tok == "armv8sm3" || tok == "arm_sm3") { - return {CPUID::CPUID_ARM_SM3_BIT}; - } else if(tok == "armv8sm4" || tok == "arm_sm4") { - return {CPUID::CPUID_ARM_SM4_BIT}; - } - +CPUID::CPUID_Data::CPUID_Data() { + // NOLINTBEGIN(*-prefer-member-initializer) +#if defined(BOTAN_HAS_CPUID_DETECTION) + m_processor_features = detect_cpu_features(~cleared_cpuid_bits()); #else - BOTAN_UNUSED(tok); + m_processor_features = 0; #endif + // NOLINTEND(*-prefer-member-initializer) +} - return {}; +std::optional CPUID::bit_from_string(std::string_view tok) { + return CPUFeature::from_string(tok); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid.h botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid.h --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,39 +9,41 @@ #define BOTAN_CPUID_H_ #include -#include +#include +#include #include -#include + +#if defined(BOTAN_HAS_CPUID_DETECTION) + #include +#endif namespace Botan { -#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) || defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) || \ - defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) +#if !defined(BOTAN_HAS_CPUID_DETECTION) +// A no-op CPUFeature +class BOTAN_TEST_API CPUFeature final { + public: + enum Bit : uint32_t {}; + + uint32_t as_u32() const; + + CPUFeature(Bit) {} - #define BOTAN_CPUID_HAS_DETECTION + static std::optional from_string(std::string_view); + std::string to_string() const; +}; #endif /** * A class handling runtime CPU feature detection. It is limited to * just the features necessary to implement CPU specific code in Botan, * rather than being a general purpose utility. -* -* This class supports: -* -* - x86 features using CPUID. x86 is also the only processor with -* accurate cache line detection currently. -* -* - PowerPC AltiVec detection on Linux, NetBSD, OpenBSD, and macOS -* -* - ARM NEON and crypto extensions detection. On Linux and Android -* systems which support getauxval, that is used to access CPU -* feature information. Otherwise a relatively portable but -* thread-unsafe mechanism involving executing probe functions which -* catching SIGILL signal is used. */ class BOTAN_TEST_API CPUID final { public: + typedef CPUFeature Feature; + /** * Probe the CPU and see what extensions are supported */ @@ -49,7 +51,7 @@ /** * Return a possibly empty string containing list of known CPU - * extensions. Each name will be seperated by a space, and the ordering + * extensions. Each name will be separated by a space, and the ordering * will be arbitrary. This list only contains values that are useful to * Botan (for example FMA instructions are not checked). * @@ -57,371 +59,94 @@ */ static std::string to_string(); - static bool is_little_endian() { -#if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) - return true; -#elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) - return false; -#else - return !has_cpuid_bit(CPUID_IS_BIG_ENDIAN_BIT); -#endif - } - - static bool is_big_endian() { -#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) - return true; -#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) - return false; -#else - return has_cpuid_bit(CPUID_IS_BIG_ENDIAN_BIT); -#endif - } - - /** - * Return true if a 4x32 SIMD instruction set is available - * (SSE2, NEON, or Altivec/VMX) - */ - static bool has_simd_32() { -#if defined(BOTAN_TARGET_SUPPORTS_SSE2) - return CPUID::has_sse2(); -#elif defined(BOTAN_TARGET_SUPPORTS_ALTIVEC) - return CPUID::has_altivec(); -#elif defined(BOTAN_TARGET_SUPPORTS_NEON) - return CPUID::has_neon(); -#else - return false; -#endif - } - - enum CPUID_bits : uint32_t { -#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) - // These values have no relation to cpuid bitfields - - // SIMD instruction sets - CPUID_SSE2_BIT = (1U << 0), - CPUID_SSSE3_BIT = (1U << 1), - CPUID_AVX2_BIT = (1U << 2), - CPUID_AVX512_BIT = (1U << 3), - - // Misc useful instructions - CPUID_RDTSC_BIT = (1U << 10), - CPUID_ADX_BIT = (1U << 11), - CPUID_BMI_BIT = (1U << 12), - CPUID_GFNI_BIT = (1U << 13), - - // Crypto-specific ISAs - CPUID_AESNI_BIT = (1U << 16), - CPUID_CLMUL_BIT = (1U << 17), - CPUID_RDRAND_BIT = (1U << 18), - CPUID_RDSEED_BIT = (1U << 19), - CPUID_SHA_BIT = (1U << 20), - CPUID_AVX512_AES_BIT = (1U << 21), - CPUID_AVX512_CLMUL_BIT = (1U << 22), - CPUID_AVX2_AES_BIT = (1U << 23), - CPUID_AVX2_CLMUL_BIT = (1U << 24), - CPUID_SHA512_BIT = (1U << 25), - CPUID_SM3_BIT = (1U << 26), - CPUID_SM4_BIT = (1U << 27), -#endif - -#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) - CPUID_ALTIVEC_BIT = (1U << 0), - CPUID_POWER_CRYPTO_BIT = (1U << 1), - CPUID_DARN_BIT = (1U << 2), -#endif - -#if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) - CPUID_ARM_NEON_BIT = (1U << 0), - CPUID_ARM_SVE_BIT = (1U << 1), - CPUID_ARM_AES_BIT = (1U << 16), - CPUID_ARM_PMULL_BIT = (1U << 17), - CPUID_ARM_SHA1_BIT = (1U << 18), - CPUID_ARM_SHA2_BIT = (1U << 19), - CPUID_ARM_SHA3_BIT = (1U << 20), - CPUID_ARM_SHA2_512_BIT = (1U << 21), - CPUID_ARM_SM3_BIT = (1U << 22), - CPUID_ARM_SM4_BIT = (1U << 23), -#endif - - CPUID_IS_BIG_ENDIAN_BIT = (1U << 30), - CPUID_INITIALIZED_BIT = (1U << 31) - }; - -#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) - /** - * Check if the processor supports AltiVec/VMX - */ - static bool has_altivec() { return has_cpuid_bit(CPUID_ALTIVEC_BIT); } - - /** - * Check if the processor supports POWER8 crypto extensions - */ - static bool has_power_crypto() { return has_cpuid_bit(CPUID_POWER_CRYPTO_BIT); } - - /** - * Check if the processor supports POWER9 DARN RNG - */ - static bool has_darn_rng() { return has_cpuid_bit(CPUID_DARN_BIT); } - -#endif - -#if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) - /** - * Check if the processor supports NEON SIMD - */ - static bool has_neon() { return has_cpuid_bit(CPUID_ARM_NEON_BIT); } - - /** - * Check if the processor supports ARMv8 SVE - */ - static bool has_arm_sve() { return has_cpuid_bit(CPUID_ARM_SVE_BIT); } - - /** - * Check if the processor supports ARMv8 SHA1 - */ - static bool has_arm_sha1() { return has_cpuid_bit(CPUID_ARM_SHA1_BIT); } - - /** - * Check if the processor supports ARMv8 SHA2 - */ - static bool has_arm_sha2() { return has_cpuid_bit(CPUID_ARM_SHA2_BIT); } - - /** - * Check if the processor supports ARMv8 AES - */ - static bool has_arm_aes() { return has_cpuid_bit(CPUID_ARM_AES_BIT); } - - /** - * Check if the processor supports ARMv8 PMULL - */ - static bool has_arm_pmull() { return has_cpuid_bit(CPUID_ARM_PMULL_BIT); } - - /** - * Check if the processor supports ARMv8 SHA-512 - */ - static bool has_arm_sha2_512() { return has_cpuid_bit(CPUID_ARM_SHA2_512_BIT); } - - /** - * Check if the processor supports ARMv8 SHA-3 - */ - static bool has_arm_sha3() { return has_cpuid_bit(CPUID_ARM_SHA3_BIT); } - - /** - * Check if the processor supports ARMv8 SM3 - */ - static bool has_arm_sm3() { return has_cpuid_bit(CPUID_ARM_SM3_BIT); } - - /** - * Check if the processor supports ARMv8 SM4 - */ - static bool has_arm_sm4() { return has_cpuid_bit(CPUID_ARM_SM4_BIT); } - -#endif - -#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) - /** - * Check if the processor supports RDTSC - */ - static bool has_rdtsc() { return has_cpuid_bit(CPUID_RDTSC_BIT); } - - /** - * Check if the processor supports SSE2 - */ - static bool has_sse2() { return has_cpuid_bit(CPUID_SSE2_BIT); } - - /** - * Check if the processor supports SSSE3 - */ - static bool has_ssse3() { return has_cpuid_bit(CPUID_SSSE3_BIT); } - - /** - * Check if the processor supports AVX2 - */ - static bool has_avx2() { return has_cpuid_bit(CPUID_AVX2_BIT); } - - /** - * Check if the processor supports our AVX-512 minimum profile - * - * Namely AVX-512 F, DQ, BW, VL, IFMA, VBMI, VBMI2, BITALG - */ - static bool has_avx512() { return has_cpuid_bit(CPUID_AVX512_BIT); } - - /** - * Check if the processor supports AVX-512 AES (VAES) + * Check if a feature is supported returning the associated string if so * - * Only set if the baseline AVX-512 profile is also satisfied - */ - static bool has_avx512_aes() { return has_cpuid_bit(CPUID_AVX512_AES_BIT); } - - /** - * Check if the processor supports AVX2 AES (VAES) - */ - static bool has_avx2_vaes() { return has_cpuid_bit(CPUID_AVX2_AES_BIT); } - - /** - * Check if the processor supports AVX2 CLMUL + * This is a helper function used to implement provider() */ - static bool has_avx2_clmul() { return has_cpuid_bit(CPUID_AVX2_CLMUL_BIT); } - - /** - * Check if the processor supports AVX-512 VPCLMULQDQ - */ - static bool has_avx512_clmul() { return has_cpuid_bit(CPUID_AVX512_CLMUL_BIT); } - - /** - * Check if the processor supports BMI2 (and BMI1) - */ - static bool has_bmi2() { return has_cpuid_bit(CPUID_BMI_BIT); } + static std::optional check(CPUID::Feature feat) { + if(state().has_bit(feat.as_u32())) { + return feat.to_string(); + } else { + return {}; + } + } /** - * Check if the processor supports GFNI + * Check if a feature is supported returning the associated string if so * - * A few Atom processors supported GFNI only for SSE; we gate this bit - * on the processor also supporting GFNI-AVX2 - */ - static bool has_gfni() { return has_cpuid_bit(CPUID_GFNI_BIT); } - - /** - * Check if the processor supports AES-NI - */ - static bool has_aes_ni() { return has_cpuid_bit(CPUID_AESNI_BIT); } - - /** - * Check if the processor supports CLMUL - */ - static bool has_clmul() { return has_cpuid_bit(CPUID_CLMUL_BIT); } - - /** - * Check if the processor supports Intel SHA extension + * This is a helper function used to implement provider() */ - static bool has_intel_sha() { return has_cpuid_bit(CPUID_SHA_BIT); } - - /** - * Check if the processor supports Intel SHA-512 extension - */ - static bool has_intel_sha512() { return has_cpuid_bit(CPUID_SHA512_BIT); } - - /** - * Check if the processor supports Intel SM3 - */ - static bool has_intel_sm3() { return has_cpuid_bit(CPUID_SM3_BIT); } - - /** - * Check if the processor supports Intel SM4 - */ - static bool has_intel_sm4() { return has_cpuid_bit(CPUID_SM4_BIT); } - - /** - * Check if the processor supports ADX extension - */ - static bool has_adx() { return has_cpuid_bit(CPUID_ADX_BIT); } - - /** - * Check if the processor supports RDRAND - */ - static bool has_rdrand() { return has_cpuid_bit(CPUID_RDRAND_BIT); } - - /** - * Check if the processor supports RDSEED - */ - static bool has_rdseed() { return has_cpuid_bit(CPUID_RDSEED_BIT); } -#endif - - /** - * Check if the processor supports byte-level vector permutes - * (SSSE3, NEON, Altivec) - */ - static bool has_vperm() { -#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) - return has_ssse3(); -#elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) - return has_neon(); -#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) - return has_altivec(); -#else - return false; -#endif + static std::optional check(CPUID::Feature feat1, CPUID::Feature feat2) { + if(state().has_bit((feat1.as_u32() | feat2.as_u32()))) { + // Typically feat2 is a secondary feature that is almost but not + // completely implied by feat1 (ex: AVX2 + BMI2) which we have to + // check for completeness, but don't reflect into the provider name. + return feat1.to_string(); + } else { + return {}; + } } /** - * Check if the processor supports hardware AES instructions + * Check if a feature is supported */ - static bool has_hw_aes() { -#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) - return has_aes_ni(); -#elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) - return has_arm_aes(); -#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) - return has_power_crypto(); -#else - return false; -#endif - } + static bool has(CPUID::Feature feat) { return state().has_bit(feat.as_u32()); } /** - * Check if the processor supports carryless multiply - * (CLMUL, PMULL) + * Check if two features are both supported */ - static bool has_carryless_multiply() { -#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) - return has_clmul(); -#elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) - return has_arm_pmull(); -#elif defined(BOTAN_TARGET_ARCH_IS_PPC64) - return has_power_crypto(); -#else - return false; -#endif + static bool has(CPUID::Feature feat1, CPUID::Feature feat2) { + return state().has_bit(feat1.as_u32() | feat2.as_u32()); } /* * Clear a CPUID bit * Call CPUID::initialize to reset * - * This is only exposed for testing, don't use unless you know - * what you are doing. - */ - static void clear_cpuid_bit(CPUID_bits bit) { state().clear_cpuid_bit(static_cast(bit)); } - - /* - * Don't call this function, use CPUID::has_xxx above - * It is only exposed for the tests. + * This is only exposed for testing and should never be called within the library */ - static bool has_cpuid_bit(CPUID_bits elem) { - const uint32_t elem32 = static_cast(elem); - return state().has_bit(elem32); - } + static void clear_cpuid_bit(CPUID::Feature bit) { state().clear_cpuid_bit(bit.as_u32()); } - static std::vector bit_from_string(std::string_view tok); + static std::optional bit_from_string(std::string_view tok); /** * A common helper for the various CPUID implementations */ template - static inline uint32_t if_set(uint64_t cpuid, T flag, CPUID::CPUID_bits bit, uint32_t allowed) { + static inline uint32_t if_set(uint64_t cpuid, T flag, CPUID::Feature bit, uint32_t allowed) { const uint64_t flag64 = static_cast(flag); if((cpuid & flag64) == flag64) { - return (bit & allowed); + return (bit.as_u32() & allowed); } else { return 0; } } private: + static inline bool is_set(uint32_t allowed, CPUID::Feature bit) { + const uint32_t feat_bit = bit.as_u32(); + return ((allowed & feat_bit) == feat_bit); + } + struct CPUID_Data { public: CPUID_Data(); CPUID_Data(const CPUID_Data& other) = default; + CPUID_Data(CPUID_Data&& other) = default; CPUID_Data& operator=(const CPUID_Data& other) = default; + CPUID_Data& operator=(CPUID_Data&& other) = default; + ~CPUID_Data() = default; void clear_cpuid_bit(uint32_t bit) { m_processor_features &= ~bit; } bool has_bit(uint32_t bit) const { return (m_processor_features & bit) == bit; } + uint32_t bitset() const { return m_processor_features; } + private: -#if defined(BOTAN_CPUID_HAS_DETECTION) +#if defined(BOTAN_HAS_CPUID_DETECTION) static uint32_t detect_cpu_features(uint32_t allowed_bits); #endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_aarch64/cpuid_aarch64.cpp botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_aarch64/cpuid_aarch64.cpp --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_aarch64/cpuid_aarch64.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_aarch64/cpuid_aarch64.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,192 @@ +/* +* Runtime CPU detection for Aarch64 +* (C) 2009,2010,2013,2017,2020,2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include + +#if defined(BOTAN_HAS_OS_UTILS) + #include +#endif + +#if defined(BOTAN_TARGET_OS_HAS_SYSCTLBYNAME) + #include + #include +#endif + +namespace Botan { + +namespace { + +std::optional aarch64_feat_via_auxval(uint32_t allowed) { +#if defined(BOTAN_HAS_OS_UTILS) + + if(auto auxval = OS::get_auxval_hwcap()) { + uint32_t feat = 0; + + /* + * On systems with getauxval these bits should normally be defined + * in bits/auxv.h but some buggy? glibc installs seem to miss them. + * These following values are all fixed, for the Linux ELF format, + * so we just hardcode them in ARM_hwcap_bit enum. + */ + enum class ARM_hwcap_bit : uint64_t /* NOLINT(*-enum-size) */ { + NEON_bit = (1 << 1), + AES_bit = (1 << 3), + PMULL_bit = (1 << 4), + SHA1_bit = (1 << 5), + SHA2_bit = (1 << 6), + SHA3_bit = (1 << 17), + SM3_bit = (1 << 18), + SM4_bit = (1 << 19), + SHA2_512_bit = (1 << 21), + SVE_bit = (1 << 22), + }; + + const auto hwcap = auxval->first; + + feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::NEON_bit, CPUFeature::Bit::NEON, allowed); + + if((feat & CPUFeature::Bit::NEON) == CPUFeature::Bit::NEON) { + feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::AES_bit, CPUFeature::Bit::AES, allowed); + feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::PMULL_bit, CPUFeature::Bit::PMULL, allowed); + feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::SHA1_bit, CPUFeature::Bit::SHA1, allowed); + feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::SHA2_bit, CPUFeature::Bit::SHA2, allowed); + feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::SHA3_bit, CPUFeature::Bit::SHA3, allowed); + feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::SM3_bit, CPUFeature::Bit::SM3, allowed); + feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::SM4_bit, CPUFeature::Bit::SM4, allowed); + feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::SHA2_512_bit, CPUFeature::Bit::SHA2_512, allowed); + feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::SVE_bit, CPUFeature::Bit::SVE, allowed); + } + + return feat; + } +#else + BOTAN_UNUSED(allowed); +#endif + + return {}; +} + +std::optional aarch64_feat_using_mac_api(uint32_t allowed) { +#if defined(BOTAN_TARGET_OS_IS_IOS) || defined(BOTAN_TARGET_OS_IS_MACOS) + uint32_t feat = 0; + + auto sysctlbyname_has_feature = [](const char* feature_name) -> bool { + unsigned int feature; + size_t size = sizeof(feature); + ::sysctlbyname(feature_name, &feature, &size, nullptr, 0); + return (feature == 1); + }; + + // All 64-bit Apple ARM chips have NEON, AES, and SHA support + feat |= CPUFeature::Bit::NEON & allowed; + if((feat & CPUFeature::Bit::NEON) == CPUFeature::Bit::NEON) { + feat |= CPUFeature::Bit::AES & allowed; + feat |= CPUFeature::Bit::PMULL & allowed; + feat |= CPUFeature::Bit::SHA1 & allowed; + feat |= CPUFeature::Bit::SHA2 & allowed; + + if(sysctlbyname_has_feature("hw.optional.armv8_2_sha3")) { + feat |= CPUFeature::Bit::SHA3 & allowed; + } + if(sysctlbyname_has_feature("hw.optional.armv8_2_sha512")) { + feat |= CPUFeature::Bit::SHA2_512 & allowed; + } + } + + return feat; +#else + BOTAN_UNUSED(allowed); + return {}; +#endif +} + +std::optional aarch64_feat_using_instr_probe(uint32_t allowed) { +#if defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_HAS_OS_UTILS) + + // NOLINTBEGIN(*-no-assembler) + + /* + No getauxval API available, fall back on probe functions. + NEON registers v0-v7 are caller saved in Aarch64 + */ + + auto neon_probe = []() noexcept -> int { + asm("and v0.16b, v0.16b, v0.16b"); + return 1; + }; + auto aes_probe = []() noexcept -> int { + asm(".word 0x4e284800"); + return 1; + }; + auto pmull_probe = []() noexcept -> int { + asm(".word 0x0ee0e000"); + return 1; + }; + auto sha1_probe = []() noexcept -> int { + asm(".word 0x5e280800"); + return 1; + }; + auto sha2_probe = []() noexcept -> int { + asm(".word 0x5e282800"); + return 1; + }; + auto sha512_probe = []() noexcept -> int { + asm(".long 0xcec08000"); + return 1; + }; + + // NOLINTEND(*-no-assembler) + + uint32_t feat = 0; + if((allowed & CPUFeature::Bit::NEON) == CPUFeature::Bit::NEON) { + if(OS::run_cpu_instruction_probe(neon_probe) == 1) { + feat |= CPUFeature::Bit::NEON; + + if(OS::run_cpu_instruction_probe(aes_probe) == 1) { + feat |= CPUFeature::Bit::AES & allowed; + } + if(OS::run_cpu_instruction_probe(pmull_probe) == 1) { + feat |= CPUFeature::Bit::PMULL & allowed; + } + if(OS::run_cpu_instruction_probe(sha1_probe) == 1) { + feat |= CPUFeature::Bit::SHA1 & allowed; + } + if(OS::run_cpu_instruction_probe(sha2_probe) == 1) { + feat |= CPUFeature::Bit::SHA2 & allowed; + } + if(OS::run_cpu_instruction_probe(sha512_probe) == 1) { + feat |= CPUFeature::Bit::SHA2_512 & allowed; + } + } + } + + return feat; +#else + BOTAN_UNUSED(allowed); + return {}; +#endif +} + +} // namespace + +uint32_t CPUID::CPUID_Data::detect_cpu_features(uint32_t allowed) { + if(auto feat_aux = aarch64_feat_via_auxval(allowed)) { + return feat_aux.value(); + } else if(auto feat_mac = aarch64_feat_using_mac_api(allowed)) { + return feat_mac.value(); + } else if(auto feat_instr = aarch64_feat_using_instr_probe(allowed)) { + return feat_instr.value(); + } else { + return 0; + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_aarch64/cpuid_features.cpp botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_aarch64/cpuid_features.cpp --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_aarch64/cpuid_features.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_aarch64/cpuid_features.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,67 @@ +/** +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include + +namespace Botan { + +std::string CPUFeature::to_string() const { + switch(m_bit) { + case CPUFeature::Bit::NEON: + return "neon"; + case CPUFeature::Bit::SVE: + return "sve"; + case CPUFeature::Bit::SHA1: + return "armv8sha1"; + case CPUFeature::Bit::SHA2: + return "armv8sha2"; + case CPUFeature::Bit::AES: + return "armv8aes"; + case CPUFeature::Bit::PMULL: + return "armv8pmull"; + case CPUFeature::Bit::SHA3: + return "armv8sha3"; + case CPUFeature::Bit::SHA2_512: + return "armv8sha2_512"; + case CPUFeature::Bit::SM3: + return "armv8sm3"; + case CPUFeature::Bit::SM4: + return "armv8sm4"; + } + throw Invalid_State("CPUFeature invalid bit"); +} + +//static +std::optional CPUFeature::from_string(std::string_view tok) { + // TODO(Botan4) remove the "arm_xxx" strings here + if(tok == "neon" || tok == "simd") { + return CPUFeature::Bit::NEON; + } else if(tok == "sve" || tok == "arm_sve") { + return CPUFeature::Bit::SVE; + } else if(tok == "armv8sha1" || tok == "arm_sha1") { + return CPUFeature::Bit::SHA1; + } else if(tok == "armv8sha2" || tok == "arm_sha2") { + return CPUFeature::Bit::SHA2; + } else if(tok == "armv8aes" || tok == "arm_aes") { + return CPUFeature::Bit::AES; + } else if(tok == "armv8pmull" || tok == "arm_pmull") { + return CPUFeature::Bit::PMULL; + } else if(tok == "armv8sha3" || tok == "arm_sha3") { + return CPUFeature::Bit::SHA3; + } else if(tok == "armv8sha2_512" || tok == "arm_sha2_512") { + return CPUFeature::Bit::SHA2_512; + } else if(tok == "armv8sm3" || tok == "arm_sm3") { + return CPUFeature::Bit::SM3; + } else if(tok == "armv8sm4" || tok == "arm_sm4") { + return CPUFeature::Bit::SM4; + } else { + return {}; + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_aarch64/cpuid_features.h botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_aarch64/cpuid_features.h --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_aarch64/cpuid_features.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_aarch64/cpuid_features.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,51 @@ +/** +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_CPUID_FEATURES_H_ +#define BOTAN_CPUID_FEATURES_H_ + +#include +#include +#include +#include +#include + +namespace Botan { + +class BOTAN_TEST_API CPUFeature final { + public: + enum Bit : uint32_t /* NOLINT(*-use-enum-class) */ { + NEON = (1U << 0), + SVE = (1U << 1), + AES = (1U << 16), + PMULL = (1U << 17), + SHA1 = (1U << 18), + SHA2 = (1U << 19), + SHA3 = (1U << 20), + SHA2_512 = (1U << 21), + SM3 = (1U << 22), + SM4 = (1U << 23), + + SIMD_4X32 = NEON, + HW_AES = AES, + HW_CLMUL = PMULL, + }; + + CPUFeature(Bit b) : m_bit(b) {} // NOLINT(*-explicit-conversions) + + uint32_t as_u32() const { return static_cast(m_bit); } + + std::string to_string() const; + + static std::optional from_string(std::string_view s); + + private: + Bit m_bit; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_aarch64/info.txt botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_aarch64/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_aarch64/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_aarch64/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,11 @@ + +CPUID_DETECTION -> 20250327 + + + +name -> "CPUID for Aarch64" + + + +arm64 + diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_aarch64.cpp botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_aarch64.cpp --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_aarch64.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_aarch64.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,193 +0,0 @@ -/* -* Runtime CPU detection for Aarch64 -* (C) 2009,2010,2013,2017,2020,2024 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include - -#if defined(BOTAN_HAS_OS_UTILS) - #include -#endif - -#if defined(BOTAN_TARGET_OS_HAS_SYSCTLBYNAME) - #include - #include -#endif - -namespace Botan { - -#if defined(BOTAN_TARGET_ARCH_IS_ARM64) - -namespace { - -std::optional aarch64_feat_via_auxval(uint32_t allowed) { - #if defined(BOTAN_HAS_OS_UTILS) - - if(auto auxval = OS::get_auxval_hwcap()) { - uint32_t feat = 0; - - /* - * On systems with getauxval these bits should normally be defined - * in bits/auxv.h but some buggy? glibc installs seem to miss them. - * These following values are all fixed, for the Linux ELF format, - * so we just hardcode them in ARM_hwcap_bit enum. - */ - enum class ARM_hwcap_bit : uint64_t { - NEON_bit = (1 << 1), - AES_bit = (1 << 3), - PMULL_bit = (1 << 4), - SHA1_bit = (1 << 5), - SHA2_bit = (1 << 6), - SHA3_bit = (1 << 17), - SM3_bit = (1 << 18), - SM4_bit = (1 << 19), - SHA2_512_bit = (1 << 21), - SVE_bit = (1 << 22), - }; - - const auto hwcap = auxval->first; - - feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::NEON_bit, CPUID::CPUID_ARM_NEON_BIT, allowed); - - if(feat & CPUID::CPUID_ARM_NEON_BIT) { - feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::AES_bit, CPUID::CPUID_ARM_AES_BIT, allowed); - feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::PMULL_bit, CPUID::CPUID_ARM_PMULL_BIT, allowed); - feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::SHA1_bit, CPUID::CPUID_ARM_SHA1_BIT, allowed); - feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::SHA2_bit, CPUID::CPUID_ARM_SHA2_BIT, allowed); - feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::SHA3_bit, CPUID::CPUID_ARM_SHA3_BIT, allowed); - feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::SM3_bit, CPUID::CPUID_ARM_SM3_BIT, allowed); - feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::SM4_bit, CPUID::CPUID_ARM_SM4_BIT, allowed); - feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::SHA2_512_bit, CPUID::CPUID_ARM_SHA2_512_BIT, allowed); - feat |= CPUID::if_set(hwcap, ARM_hwcap_bit::SVE_bit, CPUID::CPUID_ARM_SVE_BIT, allowed); - } - - return feat; - } - #else - BOTAN_UNUSED(allowed); - #endif - - return {}; -} - -std::optional aarch64_feat_using_mac_api(uint32_t allowed) { - #if defined(BOTAN_TARGET_OS_IS_IOS) || defined(BOTAN_TARGET_OS_IS_MACOS) - uint32_t feat = 0; - - auto sysctlbyname_has_feature = [](const char* feature_name) -> bool { - unsigned int feature; - size_t size = sizeof(feature); - ::sysctlbyname(feature_name, &feature, &size, nullptr, 0); - return (feature == 1); - }; - - // All 64-bit Apple ARM chips have NEON, AES, and SHA support - feat |= CPUID::CPUID_ARM_NEON_BIT & allowed; - if(feat & CPUID::CPUID_ARM_NEON_BIT) { - feat |= CPUID::CPUID_ARM_AES_BIT & allowed; - feat |= CPUID::CPUID_ARM_PMULL_BIT & allowed; - feat |= CPUID::CPUID_ARM_SHA1_BIT & allowed; - feat |= CPUID::CPUID_ARM_SHA2_BIT & allowed; - - if(sysctlbyname_has_feature("hw.optional.armv8_2_sha3")) { - feat |= CPUID::CPUID_ARM_SHA3_BIT & allowed; - } - if(sysctlbyname_has_feature("hw.optional.armv8_2_sha512")) { - feat |= CPUID::CPUID_ARM_SHA2_512_BIT & allowed; - } - } - - return feat; - #else - BOTAN_UNUSED(allowed); - return {}; - #endif -} - -std::optional aarch64_feat_using_instr_probe(uint32_t allowed) { - #if defined(BOTAN_USE_GCC_INLINE_ASM) - - /* - No getauxval API available, fall back on probe functions. - NEON registers v0-v7 are caller saved in Aarch64 - */ - - auto neon_probe = []() noexcept -> int { - asm("and v0.16b, v0.16b, v0.16b"); - return 1; - }; - auto aes_probe = []() noexcept -> int { - asm(".word 0x4e284800"); - return 1; - }; - auto pmull_probe = []() noexcept -> int { - asm(".word 0x0ee0e000"); - return 1; - }; - auto sha1_probe = []() noexcept -> int { - asm(".word 0x5e280800"); - return 1; - }; - auto sha2_probe = []() noexcept -> int { - asm(".word 0x5e282800"); - return 1; - }; - auto sha512_probe = []() noexcept -> int { - asm(".long 0xcec08000"); - return 1; - }; - - uint32_t feat = 0; - if(allowed & CPUID::CPUID_ARM_NEON_BIT) { - if(OS::run_cpu_instruction_probe(neon_probe) == 1) { - feat |= CPUID::CPUID_ARM_NEON_BIT; - } - - if(feat & CPUID::CPUID_ARM_NEON_BIT) { - if(OS::run_cpu_instruction_probe(aes_probe) == 1) { - feat |= CPUID::CPUID_ARM_AES_BIT & allowed; - } - if(OS::run_cpu_instruction_probe(pmull_probe) == 1) { - feat |= CPUID::CPUID_ARM_PMULL_BIT & allowed; - } - if(OS::run_cpu_instruction_probe(sha1_probe) == 1) { - feat |= CPUID::CPUID_ARM_SHA1_BIT & allowed; - } - if(OS::run_cpu_instruction_probe(sha2_probe) == 1) { - feat |= CPUID::CPUID_ARM_SHA2_BIT & allowed; - } - if(OS::run_cpu_instruction_probe(sha512_probe) == 1) { - feat |= CPUID::CPUID_ARM_SHA2_512_BIT & allowed; - } - } - } - - return feat; - #else - BOTAN_UNUSED(allowed); - return {}; - #endif -} - -} // namespace - -uint32_t CPUID::CPUID_Data::detect_cpu_features(uint32_t allowed) { - if(auto feat_aux = aarch64_feat_via_auxval(allowed)) { - return feat_aux.value(); - } else if(auto feat_mac = aarch64_feat_using_mac_api(allowed)) { - return feat_mac.value(); - } else if(auto feat_instr = aarch64_feat_using_instr_probe(allowed)) { - return feat_instr.value(); - } else { - return 0; - } -} - -#endif - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_arm32/cpuid_arm32.cpp botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_arm32/cpuid_arm32.cpp --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_arm32/cpuid_arm32.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_arm32/cpuid_arm32.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,55 @@ +/* +* Runtime CPU detection for 32-bit ARM +* (C) 2009,2010,2013,2017,2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include + +#if defined(BOTAN_HAS_OS_UTILS) + #include +#endif + +namespace Botan { + +uint32_t CPUID::CPUID_Data::detect_cpu_features(uint32_t allowed) { + uint32_t feat = 0; + +#if defined(BOTAN_HAS_OS_UTILS) + + if(auto auxval = OS::get_auxval_hwcap()) { + const auto [hwcap_neon, hwcap_crypto] = *auxval; + + /* + * On systems with getauxval these bits should normally be defined + * in bits/auxv.h but some buggy? glibc installs seem to miss them. + * These following values are all fixed, for the Linux ELF format, + * so we just hardcode them in ARM_hwcap_bit enum. + */ + + enum class ARM_hwcap_bit : uint64_t { + NEON_bit = (1 << 12), + AES_bit = (1 << 0), + PMULL_bit = (1 << 1), + SHA1_bit = (1 << 2), + SHA2_bit = (1 << 3), + }; + + feat |= if_set(hwcap_neon, ARM_hwcap_bit::NEON_bit, CPUFeature::Bit::NEON, allowed); + + if(is_set(feat, CPUFeature::Bit::NEON)) { + feat |= if_set(hwcap_crypto, ARM_hwcap_bit::AES_bit, CPUFeature::Bit::AES, allowed); + feat |= if_set(hwcap_crypto, ARM_hwcap_bit::PMULL_bit, CPUFeature::Bit::PMULL, allowed); + feat |= if_set(hwcap_crypto, ARM_hwcap_bit::SHA1_bit, CPUFeature::Bit::SHA1, allowed); + feat |= if_set(hwcap_crypto, ARM_hwcap_bit::SHA2_bit, CPUFeature::Bit::SHA2, allowed); + } + } +#endif + + return feat; +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_arm32/cpuid_features.cpp botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_arm32/cpuid_features.cpp --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_arm32/cpuid_features.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_arm32/cpuid_features.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,47 @@ +/** +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include + +namespace Botan { + +std::string CPUFeature::to_string() const { + switch(m_bit) { + case CPUFeature::Bit::NEON: + return "neon"; + case CPUFeature::Bit::SHA1: + return "armv8sha1"; + case CPUFeature::Bit::SHA2: + return "armv8sha2"; + case CPUFeature::Bit::AES: + return "armv8aes"; + case CPUFeature::Bit::PMULL: + return "armv8pmull"; + } + throw Invalid_State("CPUFeature invalid bit"); +} + +//static +std::optional CPUFeature::from_string(std::string_view tok) { + // TODO(Botan4) remove the "armv8" strings here + if(tok == "neon" || tok == "simd") { + return CPUFeature::Bit::NEON; + } else if(tok == "armv8sha1" || tok == "arm_sha1") { + return CPUFeature::Bit::SHA1; + } else if(tok == "armv8sha2" || tok == "arm_sha2") { + return CPUFeature::Bit::SHA2; + } else if(tok == "armv8aes" || tok == "arm_aes") { + return CPUFeature::Bit::AES; + } else if(tok == "armv8pmull" || tok == "arm_pmull") { + return CPUFeature::Bit::PMULL; + } else { + return {}; + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_arm32/cpuid_features.h botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_arm32/cpuid_features.h --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_arm32/cpuid_features.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_arm32/cpuid_features.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,46 @@ +/** +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_CPUID_FEATURES_H_ +#define BOTAN_CPUID_FEATURES_H_ + +#include +#include +#include +#include +#include + +namespace Botan { + +class BOTAN_TEST_API CPUFeature final { + public: + enum Bit : uint32_t /* NOLINT(*-use-enum-class) */ { + NEON = (1U << 0), + AES = (1U << 16), + PMULL = (1U << 17), + SHA1 = (1U << 18), + SHA2 = (1U << 19), + + SIMD_4X32 = NEON, + HW_AES = AES, + HW_CLMUL = PMULL, + }; + + CPUFeature(Bit b) : m_bit(b) {} // NOLINT(*-explicit-conversions) + + uint32_t as_u32() const { return static_cast(m_bit); } + + std::string to_string() const; + + static std::optional from_string(std::string_view s); + + private: + Bit m_bit; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_arm32/info.txt botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_arm32/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_arm32/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_arm32/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,11 @@ + +CPUID_DETECTION -> 20250327 + + + +name -> "CPUID for ARMv7" + + + +arm32 + diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_arm32.cpp botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_arm32.cpp --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_arm32.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_arm32.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -/* -* Runtime CPU detection for 32-bit ARM -* (C) 2009,2010,2013,2017,2024 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#if defined(BOTAN_HAS_OS_UTILS) - #include -#endif - -namespace Botan { - -#if defined(BOTAN_TARGET_ARCH_IS_ARM32) - -uint32_t CPUID::CPUID_Data::detect_cpu_features(uint32_t allowed) { - uint32_t feat = 0; - - #if defined(BOTAN_HAS_OS_UTILS) - - if(auto auxval = OS::get_auxval_hwcap()) { - const auto [hwcap_neon, hwcap_crypto] = *auxval; - - /* - * On systems with getauxval these bits should normally be defined - * in bits/auxv.h but some buggy? glibc installs seem to miss them. - * These following values are all fixed, for the Linux ELF format, - * so we just hardcode them in ARM_hwcap_bit enum. - */ - - enum class ARM_hwcap_bit : uint64_t { - NEON_bit = (1 << 12), - AES_bit = (1 << 0), - PMULL_bit = (1 << 1), - SHA1_bit = (1 << 2), - SHA2_bit = (1 << 3), - }; - - feat |= if_set(hwcap_neon, ARM_hwcap_bit::NEON_bit, CPUID::CPUID_ARM_NEON_BIT, allowed); - - if(feat & CPUID::CPUID_ARM_NEON_BIT) { - feat |= if_set(hwcap_crypto, ARM_hwcap_bit::AES_bit, CPUID::CPUID_ARM_AES_BIT, allowed); - feat |= if_set(hwcap_crypto, ARM_hwcap_bit::PMULL_bit, CPUID::CPUID_ARM_PMULL_BIT, allowed); - feat |= if_set(hwcap_crypto, ARM_hwcap_bit::SHA1_bit, CPUID::CPUID_ARM_SHA1_BIT, allowed); - feat |= if_set(hwcap_crypto, ARM_hwcap_bit::SHA2_bit, CPUID::CPUID_ARM_SHA2_BIT, allowed); - } - } - #endif - - return feat; -} - -#endif - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_loongarch64/cpuid_features.cpp botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_loongarch64/cpuid_features.cpp --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_loongarch64/cpuid_features.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_loongarch64/cpuid_features.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,38 @@ +/** +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include + +namespace Botan { + +std::string CPUFeature::to_string() const { + switch(m_bit) { + case CPUFeature::Bit::LSX: + return "lsx"; + case CPUFeature::Bit::LASX: + return "lasx"; + case CPUFeature::Bit::CRYPTO: + return "crypto"; + } + throw Invalid_State("CPUFeature invalid bit"); +} + +//static +std::optional CPUFeature::from_string(std::string_view tok) { + if(tok == "lsx") { + return CPUFeature::Bit::LSX; + } else if(tok == "lasx") { + return CPUFeature::Bit::LASX; + } else if(tok == "crypto") { + return CPUFeature::Bit::CRYPTO; + } else { + return {}; + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_loongarch64/cpuid_features.h botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_loongarch64/cpuid_features.h --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_loongarch64/cpuid_features.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_loongarch64/cpuid_features.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,42 @@ +/** +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_CPUID_FEATURES_H_ +#define BOTAN_CPUID_FEATURES_H_ + +#include +#include +#include +#include +#include + +namespace Botan { + +class BOTAN_TEST_API CPUFeature final { + public: + enum Bit : uint32_t /* NOLINT(*-use-enum-class) */ { + LSX = (1U << 0), + LASX = (1U << 1), + CRYPTO = (1U << 2), + + SIMD_4X32 = LSX, + }; + + CPUFeature(Bit b) : m_bit(b) {} // NOLINT(*-explicit-conversions) + + uint32_t as_u32() const { return static_cast(m_bit); } + + std::string to_string() const; + + static std::optional from_string(std::string_view s); + + private: + Bit m_bit; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_loongarch64/cpuid_loongarch64.cpp botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_loongarch64/cpuid_loongarch64.cpp --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_loongarch64/cpuid_loongarch64.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_loongarch64/cpuid_loongarch64.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,41 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +#if defined(BOTAN_HAS_OS_UTILS) + #include +#endif + +namespace Botan { + +uint32_t CPUID::CPUID_Data::detect_cpu_features(uint32_t allowed) { + uint32_t feat = 0; + +#if defined(BOTAN_HAS_OS_UTILS) + + if(auto auxval = OS::get_auxval_hwcap()) { + enum class LoongArch64_hwcap_bit : uint64_t { + LSX_bit = (1 << 4), + LASX_bit = (1 << 5), + CRYPTO_bit = (1 << 8), + }; + + const auto hwcap = auxval->first; + + feat |= if_set(hwcap, LoongArch64_hwcap_bit::LSX_bit, CPUFeature::Bit::LSX, allowed); + feat |= if_set(hwcap, LoongArch64_hwcap_bit::LASX_bit, CPUFeature::Bit::LASX, allowed); + feat |= if_set(hwcap, LoongArch64_hwcap_bit::CRYPTO_bit, CPUFeature::Bit::CRYPTO, allowed); + } +#endif + + return feat; +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_loongarch64/info.txt botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_loongarch64/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_loongarch64/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_loongarch64/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,11 @@ + +CPUID_DETECTION -> 20250327 + + + +name -> "CPUID for LoongArch64" + + + +loongarch64 + diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_ppc/cpuid_features.cpp botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_ppc/cpuid_features.cpp --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_ppc/cpuid_features.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_ppc/cpuid_features.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,39 @@ +/** +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include + +namespace Botan { + +std::string CPUFeature::to_string() const { + switch(m_bit) { + case CPUFeature::Bit::ALTIVEC: + return "altivec"; + case CPUFeature::Bit::POWER_CRYPTO: + return "power_crypto"; + case CPUFeature::Bit::DARN: + return "darn"; + } + throw Invalid_State("CPUFeature invalid bit"); +} + +//static +std::optional CPUFeature::from_string(std::string_view tok) { + if(tok == "altivec" || tok == "simd") { + return CPUFeature::Bit::ALTIVEC; + } else if(tok == "power_crypto") { + return CPUFeature::Bit::POWER_CRYPTO; + } else if(tok == "darn" || tok == "darn_rng") { + // TODO(Botan4) remove "darn_rng" + return CPUFeature::Bit::DARN; + } else { + return {}; + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_ppc/cpuid_features.h botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_ppc/cpuid_features.h --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_ppc/cpuid_features.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_ppc/cpuid_features.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,44 @@ +/** +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_CPUID_FEATURES_H_ +#define BOTAN_CPUID_FEATURES_H_ + +#include +#include +#include +#include +#include + +namespace Botan { + +class BOTAN_TEST_API CPUFeature final { + public: + enum Bit : uint32_t /* NOLINT(*-use-enum-class,*-enum-size) */ { + ALTIVEC = (1U << 0), + POWER_CRYPTO = (1U << 1), + DARN = (1U << 2), + + SIMD_4X32 = ALTIVEC, + HW_AES = POWER_CRYPTO, + HW_CLMUL = POWER_CRYPTO, + }; + + CPUFeature(Bit b) : m_bit(b) {} // NOLINT(*-explicit-conversions) + + uint32_t as_u32() const { return static_cast(m_bit); } + + std::string to_string() const; + + static std::optional from_string(std::string_view s); + + private: + Bit m_bit; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_ppc/cpuid_ppc.cpp botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_ppc/cpuid_ppc.cpp --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_ppc/cpuid_ppc.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_ppc/cpuid_ppc.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,90 @@ +/* +* Runtime CPU detection for POWER/PowerPC +* (C) 2009,2010,2013,2017,2021,2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include + +#if defined(BOTAN_HAS_OS_UTILS) + #include +#endif + +namespace Botan { + +uint32_t CPUID::CPUID_Data::detect_cpu_features([[maybe_unused]] uint32_t allowed) { + uint32_t feat = 0; + +#if defined(BOTAN_HAS_OS_UTILS) + + if(auto auxval = OS::get_auxval_hwcap()) { + const auto [hwcap_altivec, hwcap_crypto] = *auxval; + + enum class PPC_hwcap_bit : uint64_t /* NOLINT(performance-enum-size) */ { + ALTIVEC_bit = (1 << 28), + CRYPTO_bit = (1 << 25), + DARN_bit = (1 << 21), + }; + + feat |= if_set(hwcap_altivec, PPC_hwcap_bit::ALTIVEC_bit, CPUFeature::Bit::ALTIVEC, allowed); + + #if defined(BOTAN_TARGET_ARCH_IS_PPC64) + if(is_set(feat, CPUFeature::Bit::ALTIVEC)) { + feat |= if_set(hwcap_crypto, PPC_hwcap_bit::CRYPTO_bit, CPUFeature::Bit::POWER_CRYPTO, allowed); + feat |= if_set(hwcap_crypto, PPC_hwcap_bit::DARN_bit, CPUFeature::Bit::DARN, allowed); + } + #endif + + return feat; + } +#endif + +#if defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_HAS_OS_UTILS) + + // NOLINTBEGIN(*-no-assembler) + + auto vmx_probe = []() noexcept -> int { + asm("vor 0, 0, 0"); + return 1; + }; + + if(is_set(allowed, CPUFeature::Bit::ALTIVEC)) { + if(OS::run_cpu_instruction_probe(vmx_probe) == 1) { + feat |= CPUFeature::Bit::ALTIVEC; + } + + #if defined(BOTAN_TARGET_ARCH_IS_PPC64) + auto vcipher_probe = []() noexcept -> int { + asm("vcipher 0, 0, 0"); + return 1; + }; + + auto darn_probe = []() noexcept -> int { + uint64_t output = 0; + asm volatile("darn %0, 1" : "=r"(output)); + return (~output) != 0; + }; + + if(is_set(feat, CPUFeature::Bit::ALTIVEC)) { + if(OS::run_cpu_instruction_probe(vcipher_probe) == 1) { + feat |= CPUFeature::Bit::POWER_CRYPTO & allowed; + } + + if(OS::run_cpu_instruction_probe(darn_probe) == 1) { + feat |= CPUFeature::Bit::DARN & allowed; + } + } + #endif + } + + // NOLINTBEGIN(*-no-assembler) + +#endif + + return feat; +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_ppc/info.txt botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_ppc/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_ppc/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_ppc/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ + +CPUID_DETECTION -> 20250327 + + + +name -> "CPUID for POWER/PowerPC" + + + +ppc32 +ppc64 + diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_ppc.cpp botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_ppc.cpp --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_ppc.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_ppc.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,87 +0,0 @@ -/* -* Runtime CPU detection for POWER/PowerPC -* (C) 2009,2010,2013,2017,2021,2024 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#if defined(BOTAN_HAS_OS_UTILS) - #include -#endif - -namespace Botan { - -#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) - -uint32_t CPUID::CPUID_Data::detect_cpu_features(uint32_t allowed) { - uint32_t feat = 0; - - #if defined(BOTAN_HAS_OS_UTILS) - - if(auto auxval = OS::get_auxval_hwcap()) { - const auto [hwcap_altivec, hwcap_crypto] = *auxval; - - enum class PPC_hwcap_bit : uint64_t { - ALTIVEC_bit = (1 << 28), - CRYPTO_bit = (1 << 25), - DARN_bit = (1 << 21), - }; - - feat |= if_set(hwcap_altivec, PPC_hwcap_bit::ALTIVEC_bit, CPUID::CPUID_ALTIVEC_BIT, allowed); - - #if defined(BOTAN_TARGET_ARCH_IS_PPC64) - if(feat & CPUID::CPUID_ALTIVEC_BIT) { - feat |= if_set(hwcap_crypto, PPC_hwcap_bit::CRYPTO_bit, CPUID::CPUID_POWER_CRYPTO_BIT, allowed); - feat |= if_set(hwcap_crypto, PPC_hwcap_bit::DARN_bit, CPUID::CPUID_DARN_BIT, allowed); - } - #endif - - return feat; - } - #endif - - #if defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_HAS_OS_UTILS) - auto vmx_probe = []() noexcept -> int { - asm("vor 0, 0, 0"); - return 1; - }; - - if(allowed & CPUID::CPUID_ALTIVEC_BIT) { - if(OS::run_cpu_instruction_probe(vmx_probe) == 1) { - feat |= CPUID::CPUID_ALTIVEC_BIT; - } - - #if defined(BOTAN_TARGET_CPU_IS_PPC64) - auto vcipher_probe = []() noexcept -> int { - asm("vcipher 0, 0, 0"); - return 1; - }; - - auto darn_probe = []() noexcept -> int { - uint64_t output = 0; - asm volatile("darn %0, 1" : "=r"(output)); - return (~output) != 0; - }; - - if(feat & CPUID::CPUID_ALTIVEC_BIT) { - if(OS::run_cpu_instruction_probe(vcipher_probe) == 1) { - feat |= CPUID::CPUID_POWER_CRYPTO_BIT & allowed; - } - - if(OS::run_cpu_instruction_probe(darn_probe) == 1) { - feat |= CPUID::CPUID_DARN_BIT & allowed; - } - } - #endif - } - - #endif - - return feat; -} - -#endif - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_riscv64/cpuid_features.cpp botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_riscv64/cpuid_features.cpp --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_riscv64/cpuid_features.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_riscv64/cpuid_features.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,62 @@ +/** +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include + +namespace Botan { + +std::string CPUFeature::to_string() const { + switch(m_bit) { + case SCALAR_AES: + return "scalar_aes"; + case SCALAR_SHA256: + return "scalar_sha256"; + case SCALAR_SM3: + return "scalar_sm3"; + case SCALAR_SM4: + return "scalar_sm4"; + case VECTOR: + return "vector"; + case VECTOR_AES: + return "vector_aes"; + case VECTOR_SHA256: + return "vector_sha256"; + case VECTOR_SM3: + return "vector_sm3"; + case VECTOR_SM4: + return "vector_sm4"; + } + throw Invalid_State("CPUFeature invalid bit"); +} + +//static +std::optional CPUFeature::from_string(std::string_view tok) { + if(tok == "scalar_aes") { + return SCALAR_AES; + } else if(tok == "scalar_sha256") { + return SCALAR_SHA256; + } else if(tok == "scalar_sm3") { + return SCALAR_SM3; + } else if(tok == "scalar_sm4") { + return SCALAR_SM4; + } else if(tok == "vector") { + return VECTOR; + } else if(tok == "vector_aes") { + return VECTOR_AES; + } else if(tok == "vector_sha256") { + return VECTOR_SHA256; + } else if(tok == "vector_sm3") { + return VECTOR_SM3; + } else if(tok == "vector_sm4") { + return VECTOR_SM4; + } else { + return {}; + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_riscv64/cpuid_features.h botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_riscv64/cpuid_features.h --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_riscv64/cpuid_features.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_riscv64/cpuid_features.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,47 @@ +/** +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_CPUID_FEATURES_H_ +#define BOTAN_CPUID_FEATURES_H_ + +#include +#include +#include +#include +#include + +namespace Botan { + +class BOTAN_TEST_API CPUFeature final { + public: + enum Bit : uint32_t /* NOLINT(*-use-enum-class) */ { + SCALAR_AES = (1U << 0), + SCALAR_SHA256 = (1U << 1), + SCALAR_SM3 = (1U << 2), + SCALAR_SM4 = (1U << 3), + + VECTOR = (1 << 16), + VECTOR_AES = (1U << 17), + VECTOR_SHA256 = (1U << 18), + VECTOR_SM3 = (1U << 19), + VECTOR_SM4 = (1U << 20), + }; + + CPUFeature(Bit b) : m_bit(b) {} + + uint32_t as_u32() const { return static_cast(m_bit); } + + std::string to_string() const; + + static std::optional from_string(std::string_view s); + + private: + Bit m_bit; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_riscv64/cpuid_riscv64.cpp botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_riscv64/cpuid_riscv64.cpp --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_riscv64/cpuid_riscv64.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_riscv64/cpuid_riscv64.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,83 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +#if defined(BOTAN_TARGET_OS_IS_LINUX) && __has_include() + #include + #include + + #define BOTAN_TARGET_HAS_RISCV_HWPROBE +#endif + +namespace Botan { + +namespace { + +template ... Bs> + requires(sizeof...(Bs) > 0) +constexpr uint64_t bitflag(Bs... bs) { + return ((uint64_t(1) << bs) | ...); +} + +} // namespace + +uint32_t CPUID::CPUID_Data::detect_cpu_features(uint32_t allowed) { + uint32_t feat = 0; + +#if defined(BOTAN_TARGET_HAS_RISCV_HWPROBE) + /* + * For scalar operations we require additionally + * Zba (bit 3), Zbb (bit 4), Zkt (bit 16) + * + * For vector operations we require + * V (bit 2), Vbb (bit 17), VZkt (bit 26), + */ + enum class RISCV_HWPROBE_bit : uint64_t { + Scalar_Aes = bitflag(3, 4, 16, 11, 12), + Scalar_Sha256 = bitflag(3, 4, 16, 13), + Scalar_SM4 = bitflag(3, 4, 16, 14), + Scalar_SM3 = bitflag(3, 4, 16, 15), + + Vector = bitflag(2, 17, 26), + Vector_Aes = bitflag(2, 17, 26, 21), + Vector_Sha256 = bitflag(2, 17, 26, 22, 23), + Vector_SM4 = bitflag(2, 17, 26, 24), + Vector_SM3 = bitflag(2, 17, 26, 25), + Vector_GCM = bitflag(2, 17, 26, 20), + }; + + struct riscv_hwprobe p; + p.key = RISCV_HWPROBE_KEY_IMA_EXT_0; + + if(__riscv_hwprobe(&p, 1, 0, nullptr, 0) == 0) { + const uint64_t riscv_features = p.value; + + feat |= if_set(riscv_features, RISCV_HWPROBE_bit::Scalar_Aes, CPUFeature::Bit::SCALAR_AES, allowed); + feat |= if_set(riscv_features, RISCV_HWPROBE_bit::Scalar_Sha256, CPUFeature::Bit::SCALAR_SHA256, allowed); + feat |= if_set(riscv_features, RISCV_HWPROBE_bit::Scalar_SM3, CPUFeature::Bit::SCALAR_SM3, allowed); + feat |= if_set(riscv_features, RISCV_HWPROBE_bit::Scalar_SM4, CPUFeature::Bit::SCALAR_SM4, allowed); + + feat |= if_set(riscv_features, RISCV_HWPROBE_bit::Vector, CPUFeature::Bit::VECTOR, allowed); + + if(is_set(feat, CPUFeature::Bit::VECTOR)) { + feat |= if_set(riscv_features, RISCV_HWPROBE_bit::Vector_Aes, CPUFeature::Bit::VECTOR_AES, allowed); + feat |= if_set(riscv_features, RISCV_HWPROBE_bit::Vector_Sha256, CPUFeature::Bit::VECTOR_SHA256, allowed); + feat |= if_set(riscv_features, RISCV_HWPROBE_bit::Vector_SM3, CPUFeature::Bit::VECTOR_SM3, allowed); + feat |= if_set(riscv_features, RISCV_HWPROBE_bit::Vector_SM4, CPUFeature::Bit::VECTOR_SM4, allowed); + } + } +#else + BOTAN_UNUSED(allowed); +#endif + + return feat; +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_riscv64/info.txt botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_riscv64/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_riscv64/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_riscv64/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,11 @@ + +CPUID_DETECTION -> 20250327 + + + +name -> "CPUID for RISCV64" + + + +riscv64 + diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_wasm/cpuid_features.cpp botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_wasm/cpuid_features.cpp --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_wasm/cpuid_features.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_wasm/cpuid_features.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,31 @@ +/** +* (C) 2025 Jack Lloyd +* (C) 2025 polarnis +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include + +namespace Botan { + +std::string CPUFeature::to_string() const { + switch(m_bit) { + case CPUFeature::Bit::SIMD128: + return "simd128"; + } + throw Invalid_State("CPUFeature invalid bit"); +} + +//static +std::optional CPUFeature::from_string(std::string_view tok) { + if(tok == "simd128") { + return CPUFeature::Bit::SIMD128; + } + + return {}; +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_wasm/cpuid_features.h botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_wasm/cpuid_features.h --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_wasm/cpuid_features.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_wasm/cpuid_features.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,42 @@ +/** +* (C) 2025 Jack Lloyd +* (C) 2025 polarnis +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_CPUID_FEATURES_H_ +#define BOTAN_CPUID_FEATURES_H_ + +#include +#include +#include +#include +#include + +namespace Botan { + +class BOTAN_TEST_API CPUFeature final { + public: + enum Bit : uint32_t /* NOLINT(*-use-enum-class) */ { + SIMD128 = (1U << 0), + + SIMD_4X32 = SIMD128, + SIMD_2X64 = SIMD128, + }; + + CPUFeature(Bit b) : m_bit(b) {} // NOLINT(*-explicit-conversions) + + uint32_t as_u32() const { return static_cast(m_bit); } + + std::string to_string() const; + + static std::optional from_string(std::string_view s); + + private: + Bit m_bit; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_wasm/cpuid_wasm.cpp botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_wasm/cpuid_wasm.cpp --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_wasm/cpuid_wasm.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_wasm/cpuid_wasm.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,30 @@ +/* +* (C) 2025 Jack Lloyd +* (C) 2025 polarnis +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +namespace Botan { + +uint32_t CPUID::CPUID_Data::detect_cpu_features(uint32_t allowed) { + // There's no cpuid equivalent for Wasm, but we can detect some VM capabilities (like SIMD128 or Relaxed SIMD) + // at compile time either way. + enum class Wasm_vmcap_bit : uint64_t { + SIMD128_bit = (1 << 0), + }; + + uint64_t flags = 0; +#ifdef __wasm_simd128__ + flags |= static_cast>(Wasm_vmcap_bit::SIMD128_bit); +#endif + + uint32_t feat = 0; + feat |= if_set(flags, Wasm_vmcap_bit::SIMD128_bit, CPUFeature::Bit::SIMD128, allowed); + + return feat; +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_wasm/info.txt botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_wasm/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_wasm/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_wasm/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,11 @@ + +CPUID_DETECTION -> 20251105 + + + +name -> "CPUID for Wasm" + + + +wasm + diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_x86/cpuid_features.cpp botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_x86/cpuid_features.cpp --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_x86/cpuid_features.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_x86/cpuid_features.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,108 @@ +/** +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include + +namespace Botan { + +std::string CPUFeature::to_string() const { + switch(m_bit) { + case Bit::SSE2: + return "sse2"; + case Bit::SSSE3: + return "ssse3"; + case Bit::AVX2: + return "avx2"; + case Bit::AVX512: + return "avx512"; + case Bit::RDTSC: + return "rdtsc"; + case Bit::ADX: + return "adx"; + case Bit::BMI: + return "bmi2"; + case Bit::GFNI: + return "gfni"; + case Bit::RDRAND: + return "rdrand"; + case Bit::RDSEED: + return "rdseed"; + case Bit::AESNI: + return "aesni"; + case Bit::CLMUL: + return "clmul"; + case Bit::SHA: + return "intel_sha"; + case Bit::SHA512: + return "intel_sha512"; + case Bit::AVX2_AES: + return "avx2_aes"; + case Bit::AVX512_AES: + return "avx512_aes"; + case Bit::AVX2_CLMUL: + return "avx2_clmul"; + case Bit::AVX512_CLMUL: + return "avx512_clmul"; + case Bit::SM3: + return "intel_sm3"; + case Bit::SM4: + return "intel_sm4"; + } + throw Invalid_State("CPUFeature invalid bit"); +} + +//static +std::optional CPUFeature::from_string(std::string_view tok) { + if(tok == "sse2" || tok == "simd") { + return CPUFeature(Bit::SSE2); + } else if(tok == "ssse3") { + return CPUFeature(Bit::SSSE3); + } else if(tok == "aesni" || tok == "aes_ni") { + // aes_ni is the string printed on the console when running "botan cpuid" + return CPUFeature(Bit::AESNI); + } else if(tok == "clmul") { + return CPUFeature(Bit::CLMUL); + } else if(tok == "avx2") { + return CPUFeature(Bit::AVX2); + } else if(tok == "avx512") { + return CPUFeature(Bit::AVX512); + } else if(tok == "sha" || tok == "intel_sha") { + // TODO(Botan4) remove "sha" match here + return CPUFeature(Bit::SHA); + } else if(tok == "intel_sha512") { + return CPUFeature(Bit::SHA512); + } else if(tok == "rdtsc") { + return CPUFeature(Bit::RDTSC); + } else if(tok == "bmi2") { + return CPUFeature(Bit::BMI); + } else if(tok == "adx") { + return CPUFeature(Bit::ADX); + } else if(tok == "gfni") { + return CPUFeature(Bit::GFNI); + } else if(tok == "rdrand") { + return CPUFeature(Bit::RDRAND); + } else if(tok == "rdseed") { + return CPUFeature(Bit::RDSEED); + } else if(tok == "avx512_aes") { + return CPUFeature(Bit::AVX512_AES); + } else if(tok == "avx512_clmul") { + return CPUFeature(Bit::AVX512_CLMUL); + } else if(tok == "avx2_vaes" || tok == "avx2_aes") { + return CPUFeature(Bit::AVX2_AES); + } else if(tok == "avx2_clmul") { + return CPUFeature(Bit::AVX2_CLMUL); + } else if(tok == "intel_sm3") { + return CPUFeature(Bit::SM3); + } else if(tok == "intel_sm4") { + return CPUFeature(Bit::SM4); + } else { + return {}; + } +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_x86/cpuid_features.h botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_x86/cpuid_features.h --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_x86/cpuid_features.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_x86/cpuid_features.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,65 @@ +/** +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_CPUID_FEATURES_H_ +#define BOTAN_CPUID_FEATURES_H_ + +#include +#include +#include +#include +#include + +namespace Botan { + +class BOTAN_TEST_API CPUFeature final { + public: + enum Bit : uint32_t /* NOLINT(*-use-enum-class) */ { + SSE2 = (1U << 0), + SSSE3 = (1U << 1), + AVX2 = (1U << 2), + AVX512 = (1U << 3), + + RDTSC = (1U << 6), + ADX = (1U << 7), + BMI = (1U << 8), + GFNI = (1U << 9), + RDRAND = (1U << 10), + RDSEED = (1U << 11), + + // Crypto-specific ISAs + AESNI = (1U << 16), + CLMUL = (1U << 17), + SHA = (1U << 20), + SHA512 = (1U << 21), + AVX2_AES = (1U << 22), + AVX512_AES = (1U << 23), + AVX2_CLMUL = (1U << 24), + AVX512_CLMUL = (1U << 25), + SM3 = (1U << 26), + SM4 = (1U << 27), + + SIMD_4X32 = SSSE3, + SIMD_2X64 = SSSE3, + HW_AES = AESNI, + HW_CLMUL = CLMUL, + }; + + CPUFeature(Bit b) : m_bit(b) {} // NOLINT(*-explicit-conversions) + + uint32_t as_u32() const { return static_cast(m_bit); } + + std::string to_string() const; + + static std::optional from_string(std::string_view s); + + private: + Bit m_bit; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_x86/cpuid_x86.cpp botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_x86/cpuid_x86.cpp --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_x86/cpuid_x86.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_x86/cpuid_x86.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,224 @@ +/* +* Runtime CPU detection for x86 +* (C) 2009,2010,2013,2017,2023,2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include + +#include + +#if defined(BOTAN_BUILD_COMPILER_IS_MSVC) + #include +#endif + +namespace Botan { + +namespace { + +void invoke_cpuid(uint32_t type, uint32_t out[4]) { + clear_mem(out, 4); + +#if defined(BOTAN_USE_GCC_INLINE_ASM) + // NOLINTNEXTLINE(*-no-assembler) + asm volatile("cpuid\n\t" : "=a"(out[0]), "=b"(out[1]), "=c"(out[2]), "=d"(out[3]) : "0"(type)); + +#elif defined(BOTAN_BUILD_COMPILER_IS_MSVC) + __cpuid((int*)out, type); + +#else + BOTAN_UNUSED(type); + #warning "No way of calling x86 cpuid instruction for this compiler" +#endif +} + +void invoke_cpuid_sublevel(uint32_t type, uint32_t level, uint32_t out[4]) { + clear_mem(out, 4); + +#if defined(BOTAN_USE_GCC_INLINE_ASM) + // NOLINTNEXTLINE(*-no-assembler) + asm volatile("cpuid\n\t" : "=a"(out[0]), "=b"(out[1]), "=c"(out[2]), "=d"(out[3]) : "0"(type), "2"(level)); + +#elif defined(BOTAN_BUILD_COMPILER_IS_MSVC) + __cpuidex((int*)out, type, level); + +#else + BOTAN_UNUSED(type, level); + #warning "No way of calling x86 cpuid instruction for this compiler" +#endif +} + +BOTAN_FUNC_ISA("xsave") uint64_t xgetbv() { + return _xgetbv(0); +} + +} // namespace + +uint32_t CPUID::CPUID_Data::detect_cpu_features(uint32_t allowed) { + enum class x86_CPUID_1_bits : uint64_t { + RDTSC = (1ULL << 4), + SSE2 = (1ULL << 26), + CLMUL = (1ULL << 33), + SSSE3 = (1ULL << 41), + SSE41 = (1ULL << 51), + AESNI = (1ULL << 57), + // AVX + OSXSAVE + OSXSAVE = (1ULL << 59) | (1ULL << 60), + RDRAND = (1ULL << 62) + }; + + enum class x86_CPUID_7_bits : uint64_t { + BMI1 = (1ULL << 3), + AVX2 = (1ULL << 5), + BMI2 = (1ULL << 8), + BMI_1_AND_2 = BMI1 | BMI2, + AVX512_F = (1ULL << 16), + AVX512_DQ = (1ULL << 17), + RDSEED = (1ULL << 18), + ADX = (1ULL << 19), + AVX512_IFMA = (1ULL << 21), + SHA = (1ULL << 29), + AVX512_BW = (1ULL << 30), + AVX512_VL = (1ULL << 31), + AVX512_VBMI = (1ULL << 33), + AVX512_VBMI2 = (1ULL << 38), + GFNI = (1ULL << 40), + AVX512_VAES = (1ULL << 41), + AVX512_VCLMUL = (1ULL << 42), + AVX512_VBITALG = (1ULL << 44), + + /* + We only enable AVX512 support if all of the below flags are available + + This is more than we strictly need for most uses, however it also has + the effect of preventing execution of AVX512 codepaths on cores that + have serious downclocking problems when AVX512 code executes, + especially Intel Skylake. + + VBMI2/VBITALG are the key flags here as they restrict us to Intel Ice + Lake/Rocket Lake, or AMD Zen4, all of which do not have penalties for + executing AVX512. + + There is nothing stopping some future processor from supporting the + above flags and having AVX512 penalties, but maybe you should not have + bought such a processor. + */ + AVX512_PROFILE = + AVX512_F | AVX512_DQ | AVX512_IFMA | AVX512_BW | AVX512_VL | AVX512_VBMI | AVX512_VBMI2 | AVX512_VBITALG, + }; + + // NOLINTNEXTLINE(performance-enum-size) + enum class x86_CPUID_7_1_bits : uint64_t { + SHA512 = (1 << 0), + SM3 = (1 << 1), + SM4 = (1 << 2), + }; + + uint32_t feat = 0; + uint32_t cpuid[4] = {0}; + bool has_os_ymm_support = false; + bool has_os_zmm_support = false; + + // CPUID 0: vendor identification, max sublevel + invoke_cpuid(0, cpuid); + + const uint32_t max_supported_sublevel = cpuid[0]; + + if(max_supported_sublevel >= 1) { + // CPUID 1: feature bits + invoke_cpuid(1, cpuid); + const uint64_t flags0 = (static_cast(cpuid[2]) << 32) | cpuid[3]; + + feat |= if_set(flags0, x86_CPUID_1_bits::RDTSC, CPUFeature::Bit::RDTSC, allowed); + + feat |= if_set(flags0, x86_CPUID_1_bits::RDRAND, CPUFeature::Bit::RDRAND, allowed); + + feat |= if_set(flags0, x86_CPUID_1_bits::SSE2, CPUFeature::Bit::SSE2, allowed); + + if(is_set(feat, CPUFeature::Bit::SSE2)) { + feat |= if_set(flags0, x86_CPUID_1_bits::SSSE3, CPUFeature::Bit::SSSE3, allowed); + + if(is_set(feat, CPUFeature::Bit::SSSE3)) { + feat |= if_set(flags0, x86_CPUID_1_bits::CLMUL, CPUFeature::Bit::CLMUL, allowed); + feat |= if_set(flags0, x86_CPUID_1_bits::AESNI, CPUFeature::Bit::AESNI, allowed); + } + + const uint64_t osxsave64 = static_cast(x86_CPUID_1_bits::OSXSAVE); + if((flags0 & osxsave64) == osxsave64) { + const uint64_t xcr_flags = xgetbv(); + if((xcr_flags & 0x6) == 0x6) { + has_os_ymm_support = true; + has_os_zmm_support = (xcr_flags & 0xE0) == 0xE0; + } + } + } + } + + if(max_supported_sublevel >= 7) { + clear_mem(cpuid, 4); + invoke_cpuid_sublevel(7, 0, cpuid); + + const uint64_t flags7 = (static_cast(cpuid[2]) << 32) | cpuid[1]; + + clear_mem(cpuid, 4); + invoke_cpuid_sublevel(7, 1, cpuid); + const uint32_t flags7_1 = cpuid[0]; + + feat |= if_set(flags7, x86_CPUID_7_bits::RDSEED, CPUFeature::Bit::RDSEED, allowed); + feat |= if_set(flags7, x86_CPUID_7_bits::ADX, CPUFeature::Bit::ADX, allowed); + + /* + We only set the BMI bit if both BMI1 and BMI2 are supported, since + typically we want to use both extensions in the same code. + */ + feat |= if_set(flags7, x86_CPUID_7_bits::BMI_1_AND_2, CPUFeature::Bit::BMI, allowed); + + if(is_set(feat, CPUFeature::Bit::SSSE3)) { + feat |= if_set(flags7, x86_CPUID_7_bits::SHA, CPUFeature::Bit::SHA, allowed); + feat |= if_set(flags7_1, x86_CPUID_7_1_bits::SM3, CPUFeature::Bit::SM3, allowed); + + // We only consider AVX2 if SSSE3 is supported + if(has_os_ymm_support) { + feat |= if_set(flags7, x86_CPUID_7_bits::AVX2, CPUFeature::Bit::AVX2, allowed); + + if(is_set(feat, CPUFeature::Bit::AVX2)) { + feat |= if_set(flags7, x86_CPUID_7_bits::GFNI, CPUFeature::Bit::GFNI, allowed); + feat |= if_set(flags7, x86_CPUID_7_bits::AVX512_VAES, CPUFeature::Bit::AVX2_AES, allowed); + feat |= if_set(flags7, x86_CPUID_7_bits::AVX512_VCLMUL, CPUFeature::Bit::AVX2_CLMUL, allowed); + feat |= if_set(flags7_1, x86_CPUID_7_1_bits::SHA512, CPUFeature::Bit::SHA512, allowed); + feat |= if_set(flags7_1, x86_CPUID_7_1_bits::SM4, CPUFeature::Bit::SM4, allowed); + + // Likewise we only consider AVX-512 if AVX2 is supported + if(has_os_zmm_support) { + feat |= if_set(flags7, x86_CPUID_7_bits::AVX512_PROFILE, CPUFeature::Bit::AVX512, allowed); + + if(is_set(feat, CPUFeature::Bit::AVX512)) { + feat |= if_set(flags7, x86_CPUID_7_bits::AVX512_VAES, CPUFeature::Bit::AVX512_AES, allowed); + feat |= if_set(flags7, x86_CPUID_7_bits::AVX512_VCLMUL, CPUFeature::Bit::AVX512_CLMUL, allowed); + } + } + } + } + } + } + +/* + * If we don't have access to CPUID, we can still safely assume that + * any x86-64 processor has SSE2 and RDTSC + */ +#if defined(BOTAN_TARGET_ARCH_IS_X86_64) + if(feat == 0) { + feat |= CPUFeature::Bit::SSE2 & allowed; + feat |= CPUFeature::Bit::RDTSC & allowed; + } +#endif + + return feat; +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_x86/info.txt botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_x86/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_x86/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_x86/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,13 @@ + +CPUID_DETECTION -> 20250327 + + + +name -> "CPUID for x86" + + + +x86_32 +x86_64 +x32 + diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_x86.cpp botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_x86.cpp --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/cpuid_x86.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/cpuid_x86.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,225 +0,0 @@ -/* -* Runtime CPU detection for x86 -* (C) 2009,2010,2013,2017,2023,2024 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include - -#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) - #include -#endif - -#if defined(BOTAN_BUILD_COMPILER_IS_MSVC) - #include -#endif - -namespace Botan { - -#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) - -namespace { - -void invoke_cpuid(uint32_t type, uint32_t out[4]) { - clear_mem(out, 4); - - #if defined(BOTAN_USE_GCC_INLINE_ASM) - asm volatile("cpuid\n\t" : "=a"(out[0]), "=b"(out[1]), "=c"(out[2]), "=d"(out[3]) : "0"(type)); - - #elif defined(BOTAN_BUILD_COMPILER_IS_MSVC) - __cpuid((int*)out, type); - - #else - BOTAN_UNUSED(type); - #warning "No way of calling x86 cpuid instruction for this compiler" - #endif -} - -void invoke_cpuid_sublevel(uint32_t type, uint32_t level, uint32_t out[4]) { - clear_mem(out, 4); - - #if defined(BOTAN_USE_GCC_INLINE_ASM) - asm volatile("cpuid\n\t" : "=a"(out[0]), "=b"(out[1]), "=c"(out[2]), "=d"(out[3]) : "0"(type), "2"(level)); - - #elif defined(BOTAN_BUILD_COMPILER_IS_MSVC) - __cpuidex((int*)out, type, level); - - #else - BOTAN_UNUSED(type, level); - #warning "No way of calling x86 cpuid instruction for this compiler" - #endif -} - -BOTAN_FUNC_ISA("xsave") uint64_t xgetbv() { - return _xgetbv(0); -} - -} // namespace - -uint32_t CPUID::CPUID_Data::detect_cpu_features(uint32_t allowed) { - enum class x86_CPUID_1_bits : uint64_t { - RDTSC = (1ULL << 4), - SSE2 = (1ULL << 26), - CLMUL = (1ULL << 33), - SSSE3 = (1ULL << 41), - SSE41 = (1ULL << 51), - AESNI = (1ULL << 57), - // AVX + OSXSAVE - OSXSAVE = (1ULL << 59) | (1ULL << 60), - RDRAND = (1ULL << 62) - }; - - enum class x86_CPUID_7_bits : uint64_t { - BMI1 = (1ULL << 3), - AVX2 = (1ULL << 5), - BMI2 = (1ULL << 8), - BMI_1_AND_2 = BMI1 | BMI2, - AVX512_F = (1ULL << 16), - AVX512_DQ = (1ULL << 17), - RDSEED = (1ULL << 18), - ADX = (1ULL << 19), - AVX512_IFMA = (1ULL << 21), - SHA = (1ULL << 29), - AVX512_BW = (1ULL << 30), - AVX512_VL = (1ULL << 31), - AVX512_VBMI = (1ULL << 33), - AVX512_VBMI2 = (1ULL << 38), - GFNI = (1ULL << 40), - AVX512_VAES = (1ULL << 41), - AVX512_VCLMUL = (1ULL << 42), - AVX512_VBITALG = (1ULL << 44), - - /* - We only enable AVX512 support if all of the below flags are available - - This is more than we strictly need for most uses, however it also has - the effect of preventing execution of AVX512 codepaths on cores that - have serious downclocking problems when AVX512 code executes, - especially Intel Skylake. - - VBMI2/VBITALG are the key flags here as they restrict us to Intel Ice - Lake/Rocket Lake, or AMD Zen4, all of which do not have penalties for - executing AVX512. - - There is nothing stopping some future processor from supporting the - above flags and having AVX512 penalties, but maybe you should not have - bought such a processor. - */ - AVX512_PROFILE = - AVX512_F | AVX512_DQ | AVX512_IFMA | AVX512_BW | AVX512_VL | AVX512_VBMI | AVX512_VBMI2 | AVX512_VBITALG, - }; - - // NOLINTNEXTLINE(performance-enum-size) - enum class x86_CPUID_7_1_bits : uint64_t { - SHA512 = (1 << 0), - SM3 = (1 << 1), - SM4 = (1 << 2), - }; - - uint32_t feat = 0; - uint32_t cpuid[4] = {0}; - bool has_os_ymm_support = false; - bool has_os_zmm_support = false; - - // CPUID 0: vendor identification, max sublevel - invoke_cpuid(0, cpuid); - - const uint32_t max_supported_sublevel = cpuid[0]; - - if(max_supported_sublevel >= 1) { - // CPUID 1: feature bits - invoke_cpuid(1, cpuid); - const uint64_t flags0 = (static_cast(cpuid[2]) << 32) | cpuid[3]; - - feat |= if_set(flags0, x86_CPUID_1_bits::RDTSC, CPUID::CPUID_RDTSC_BIT, allowed); - - feat |= if_set(flags0, x86_CPUID_1_bits::RDRAND, CPUID::CPUID_RDRAND_BIT, allowed); - - feat |= if_set(flags0, x86_CPUID_1_bits::SSE2, CPUID::CPUID_SSE2_BIT, allowed); - - if(feat & CPUID::CPUID_SSE2_BIT) { - feat |= if_set(flags0, x86_CPUID_1_bits::SSSE3, CPUID::CPUID_SSSE3_BIT, allowed); - - if(feat & CPUID::CPUID_SSSE3_BIT) { - feat |= if_set(flags0, x86_CPUID_1_bits::CLMUL, CPUID::CPUID_CLMUL_BIT, allowed); - feat |= if_set(flags0, x86_CPUID_1_bits::AESNI, CPUID::CPUID_AESNI_BIT, allowed); - } - - const uint64_t osxsave64 = static_cast(x86_CPUID_1_bits::OSXSAVE); - if((flags0 & osxsave64) == osxsave64) { - const uint64_t xcr_flags = xgetbv(); - if((xcr_flags & 0x6) == 0x6) { - has_os_ymm_support = true; - has_os_zmm_support = (xcr_flags & 0xE0) == 0xE0; - } - } - } - } - - if(max_supported_sublevel >= 7) { - clear_mem(cpuid, 4); - invoke_cpuid_sublevel(7, 0, cpuid); - - const uint64_t flags7 = (static_cast(cpuid[2]) << 32) | cpuid[1]; - - clear_mem(cpuid, 4); - invoke_cpuid_sublevel(7, 1, cpuid); - const uint32_t flags7_1 = cpuid[0]; - - feat |= if_set(flags7, x86_CPUID_7_bits::RDSEED, CPUID::CPUID_RDSEED_BIT, allowed); - feat |= if_set(flags7, x86_CPUID_7_bits::ADX, CPUID::CPUID_ADX_BIT, allowed); - - /* - We only set the BMI bit if both BMI1 and BMI2 are supported, since - typically we want to use both extensions in the same code. - */ - feat |= if_set(flags7, x86_CPUID_7_bits::BMI_1_AND_2, CPUID::CPUID_BMI_BIT, allowed); - - if(feat & CPUID::CPUID_SSSE3_BIT) { - feat |= if_set(flags7, x86_CPUID_7_bits::SHA, CPUID::CPUID_SHA_BIT, allowed); - feat |= if_set(flags7_1, x86_CPUID_7_1_bits::SM3, CPUID::CPUID_SM3_BIT, allowed); - } - - if(has_os_ymm_support) { - feat |= if_set(flags7, x86_CPUID_7_bits::AVX2, CPUID::CPUID_AVX2_BIT, allowed); - - if(feat & CPUID::CPUID_AVX2_BIT) { - feat |= if_set(flags7, x86_CPUID_7_bits::GFNI, CPUID::CPUID_GFNI_BIT, allowed); - feat |= if_set(flags7, x86_CPUID_7_bits::AVX512_VAES, CPUID::CPUID_AVX2_AES_BIT, allowed); - feat |= if_set(flags7, x86_CPUID_7_bits::AVX512_VCLMUL, CPUID::CPUID_AVX2_CLMUL_BIT, allowed); - feat |= if_set(flags7_1, x86_CPUID_7_1_bits::SHA512, CPUID::CPUID_SHA512_BIT, allowed); - feat |= if_set(flags7_1, x86_CPUID_7_1_bits::SM4, CPUID::CPUID_SM4_BIT, allowed); - - if(has_os_zmm_support) { - feat |= if_set(flags7, x86_CPUID_7_bits::AVX512_PROFILE, CPUID::CPUID_AVX512_BIT, allowed); - - if(feat & CPUID::CPUID_AVX512_BIT) { - feat |= if_set(flags7, x86_CPUID_7_bits::AVX512_VAES, CPUID::CPUID_AVX512_AES_BIT, allowed); - feat |= if_set(flags7, x86_CPUID_7_bits::AVX512_VCLMUL, CPUID::CPUID_AVX512_CLMUL_BIT, allowed); - } - } - } - } - } - - /* - * If we don't have access to CPUID, we can still safely assume that - * any x86-64 processor has SSE2 and RDTSC - */ - #if defined(BOTAN_TARGET_ARCH_IS_X86_64) - if(feat == 0) { - feat |= CPUID::CPUID_SSE2_BIT & allowed; - feat |= CPUID::CPUID_RDTSC_BIT & allowed; - } - #endif - - return feat; -} - -#endif - -} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/cpuid/info.txt botan3-3.12.0+dfsg/src/lib/utils/cpuid/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/cpuid/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/cpuid/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,8 +1,26 @@ - + CPUID -> 20170917 - + name -> "CPUID" brief -> "Handle runtime feature detection of the current CPU" + + +arm32?cpuid_arm32 +arm64?cpuid_aarch64 + +loongarch64?cpuid_loongarch64 + +ppc32?cpuid_ppc +ppc64?cpuid_ppc + +x86_32?cpuid_x86 +x86_64?cpuid_x86 +x32?cpuid_x86 + +riscv64?cpuid_riscv64 + +wasm?cpuid_wasm + diff -Nru botan3-3.7.1+dfsg/src/lib/utils/ct_utils.cpp botan3-3.12.0+dfsg/src/lib/utils/ct_utils.cpp --- botan3-3.7.1+dfsg/src/lib/utils/ct_utils.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/ct_utils.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -84,8 +84,8 @@ size_t CT::count_leading_zero_bytes(std::span input) { size_t leading_zeros = 0; auto only_zeros = Mask::set(); - for(size_t i = 0; i != input.size(); ++i) { - only_zeros &= CT::Mask::is_zero(input[i]); + for(const uint8_t b : input) { + only_zeros &= CT::Mask::is_zero(b); leading_zeros += only_zeros.if_set_return(1); } return leading_zeros; diff -Nru botan3-3.7.1+dfsg/src/lib/utils/ct_utils.h botan3-3.12.0+dfsg/src/lib/utils/ct_utils.h --- botan3-3.7.1+dfsg/src/lib/utils/ct_utils.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/ct_utils.h 2026-05-07 01:38:28.000000000 +0000 @@ -14,10 +14,13 @@ #ifndef BOTAN_CT_UTILS_H_ #define BOTAN_CT_UTILS_H_ -#include +#include +#include #include #include -#include +#include +#include +#include #include #include @@ -92,16 +95,21 @@ /// @name Constant Time Check Annotation Convenience overloads /// @{ +template +concept custom_poisonable = requires(const T& v) { v._const_time_poison(); }; +template +concept custom_unpoisonable = requires(const T& v) { v._const_time_unpoison(); }; + /** * Poison a single integral object */ template -constexpr void poison(T& p) { +constexpr void poison(const T& p) { poison(&p, 1); } template -constexpr void unpoison(T& p) { +constexpr void unpoison(const T& p) { unpoison(&p, 1); } @@ -109,16 +117,16 @@ * Poison a contiguous buffer of trivial objects (e.g. integers and such) */ template - requires std::is_trivially_copyable_v> -constexpr void poison(R&& r) { - std::span s{r}; + requires std::is_trivially_copyable_v> && (!custom_poisonable) +constexpr void poison(const R& r) { + const std::span s{r}; poison(s.data(), s.size()); } template - requires std::is_trivially_copyable_v> -constexpr void unpoison(R&& r) { - std::span s{r}; + requires std::is_trivially_copyable_v> && (!custom_unpoisonable) +constexpr void unpoison(const R& r) { + const std::span s{r}; unpoison(s.data(), s.size()); } @@ -126,14 +134,12 @@ * Poison a class type that provides a public `_const_time_poison()` method * For instance: BigInt, CT::Mask<>, FrodoMatrix, ... */ -template - requires requires(const T& x) { x._const_time_poison(); } +template constexpr void poison(const T& x) { x._const_time_poison(); } -template - requires requires(const T& x) { x._const_time_unpoison(); } +template constexpr void unpoison(const T& x) { x._const_time_unpoison(); } @@ -145,7 +151,7 @@ requires requires(const T& v) { ::Botan::CT::poison(v); } constexpr void poison(const std::optional& x) { if(x.has_value()) { - poison(x.value()); + poison(*x); } } @@ -153,7 +159,7 @@ requires requires(const T& v) { ::Botan::CT::unpoison(v); } constexpr void unpoison(const std::optional& x) { if(x.has_value()) { - unpoison(x.value()); + unpoison(*x); } } @@ -172,7 +178,7 @@ */ template requires poisonable> -constexpr void poison_range(R&& r) { +constexpr void poison_range(const R& r) { for(const auto& v : r) { poison(v); } @@ -180,7 +186,7 @@ template requires unpoisonable> -constexpr void unpoison_range(R&& r) { +constexpr void unpoison_range(const R& r) { for(const auto& v : r) { unpoison(v); } @@ -192,13 +198,13 @@ */ template requires(sizeof...(Ts) > 0) -constexpr void poison_all(Ts&&... ts) { +constexpr void poison_all(const Ts&... ts) { (poison(ts), ...); } template requires(sizeof...(Ts) > 0) -constexpr void unpoison_all(Ts&&... ts) { +constexpr void unpoison_all(const Ts&... ts) { (unpoison(ts), ...); } @@ -244,57 +250,6 @@ /// @} /** -* This function returns its argument, but (if called in a non-constexpr context) -* attempts to prevent the compiler from reasoning about the value or the possible -* range of values. Such optimizations have a way of breaking constant time code. -* -* The method that is use is decided at configuration time based on the target -* compiler and architecture (see `ct_value_barrier` blocks in `src/build-data/cc`). -* The decision can be overridden by the user with the configure.py option -* `--ct-value-barrier-type=` -* -* There are three options currently possible in the data files and with the -* option: -* -* * `asm`: Use an inline assembly expression which (currently) prevents Clang -* and GCC from optimizing based on the possible value of the input expression. -* -* * `volatile`: Launder the input through a volatile variable. This is likely -* to cause significant performance regressions since the value must be -* actually stored and loaded back from memory each time. -* -* * `none`: disable constant time barriers entirely. This is used -* with MSVC, which is not known to perform optimizations that break -* constant time code and which does not support GCC-style inline asm. -* -*/ -template -constexpr inline T value_barrier(T x) - requires std::unsigned_integral && (!std::same_as) -{ - if(std::is_constant_evaluated()) { - return x; - } else { -#if defined(BOTAN_CT_VALUE_BARRIER_USE_ASM) - /* - * We may want a "stronger" statement such as - * asm volatile("" : "+r,m"(x) : : "memory); - * (see https://theunixzoo.co.uk/blog/2021-10-14-preventing-optimisations.html) - * however the current approach seems sufficient with current compilers, - * and is minimally damaging with regards to degrading code generation. - */ - asm("" : "+r"(x) : /* no input */); - return x; -#elif defined(BOTAN_CT_VALUE_BARRIER_USE_VOLATILE) - volatile T vx = x; - return vx; -#else - return x; -#endif - } -} - -/** * A Choice is used for constant-time conditionals. * * Internally it always is either |0| (all 0 bits) or |1| (all 1 bits) @@ -303,26 +258,51 @@ */ class Choice final { public: + using underlying_type = word; + /** * If v == 0 return an unset (false) Choice, otherwise a set Choice */ template requires std::unsigned_integral && (!std::same_as) constexpr static Choice from_int(T v) { - // Mask of T that is either |0| or |1| - const T v_is_0 = ct_is_zero(value_barrier(v)); + if constexpr(sizeof(T) <= sizeof(underlying_type)) { + return !Choice(ct_is_zero(v)); + } else { + // Mask of T that is either |0| or |1| + const T v_is_0 = ct_is_zero(value_barrier(v)); - // We want the mask to be set if v != 0 so we must check that - // v_is_0 is itself zero. - // - // Also sizeof(T) may not equal sizeof(uint32_t) so we must - // use ct_is_zero. It's ok to either truncate or - // zero extend v_is_0 to 32 bits since we know it is |0| or |1| - // so even just the low bit is sufficient. - return Choice(ct_is_zero(static_cast(v_is_0))); + // We want the mask to be set if v != 0 so we must check that + // v_is_0 is itself zero. + // + // Also sizeof(T) may not equal sizeof(underlying_type) so we must + // use ct_is_zero. It's ok to either truncate or + // zero extend v_is_0 to 32 bits since we know it is |0| or |1| + // so even just the low bit is sufficient. + return Choice(ct_is_zero(static_cast(v_is_0))); + } } - constexpr static Choice yes() { return Choice(static_cast(-1)); } + /** + * Return a bitmask |1| if the choice is set, or |0| otherwise + */ + template + requires std::unsigned_integral && (!std::same_as) + constexpr T into_bitmask() const { + if constexpr(sizeof(T) <= sizeof(underlying_type)) { + // The inner mask is already |0| or |1| so just truncate + return static_cast(value()); + } else { + return ~ct_is_zero(value()); + } + } + + /** + * Create a Choice directly from a mask value - this assumes v is either |0| or |1| + */ + constexpr static Choice from_mask(underlying_type v) { return Choice(v); } + + constexpr static Choice yes() { return !no(); } constexpr static Choice no() { return Choice(0); } @@ -349,17 +329,18 @@ constexpr bool as_bool() const { return m_value != 0; } /// Return the masked value - constexpr uint32_t value() const { return value_barrier(m_value); } + constexpr underlying_type value() const { return value_barrier(m_value); } constexpr Choice(const Choice& other) = default; constexpr Choice(Choice&& other) = default; constexpr Choice& operator=(const Choice& other) noexcept = default; constexpr Choice& operator=(Choice&& other) noexcept = default; + constexpr ~Choice() = default; private: - constexpr explicit Choice(uint32_t v) : m_value(v) {} + constexpr explicit Choice(underlying_type v) : m_value(CT::value_barrier(v)) {} - uint32_t m_value; + underlying_type m_value; }; /** @@ -378,17 +359,20 @@ template class Mask final { public: - static_assert(std::is_unsigned::value && !std::is_same::value, + static_assert(std::is_unsigned_v && !std::is_same_v, "Only unsigned integer types are supported by CT::Mask"); Mask(const Mask& other) = default; + Mask(Mask&& other) = default; Mask& operator=(const Mask& other) = default; + Mask& operator=(Mask&& other) = default; + ~Mask() = default; /** * Derive a Mask from a Mask of a larger type */ template - constexpr Mask(Mask o) : m_mask(static_cast(o.value())) { + constexpr explicit Mask(Mask o) : m_mask(static_cast(o.value())) { static_assert(sizeof(U) > sizeof(T), "sizes ok"); } @@ -408,10 +392,15 @@ static constexpr Mask expand(T v) { return ~Mask::is_zero(value_barrier(v)); } /** + * Return a Mask which is set if v is true + */ + static constexpr Mask expand_bool(bool v) { return Mask::expand(static_cast(v)); } + + /** * Return a Mask which is set if choice is set */ static constexpr Mask from_choice(Choice c) { - if constexpr(sizeof(T) <= sizeof(uint32_t)) { + if constexpr(sizeof(T) <= sizeof(Choice::underlying_type)) { // Take advantage of the fact that Choice's mask is always // either |0| or |1| return Mask(static_cast(c.value())); @@ -423,7 +412,7 @@ /** * Return a Mask which is set if the top bit of v is set */ - static constexpr Mask expand_top_bit(T v) { return Mask(Botan::expand_top_bit(value_barrier(v))); } + static constexpr Mask expand_top_bit(T v) { return Mask(ct_expand_top_bit(v)); } /** * Return a Mask which is set if the given @p bit of @p v is set. @@ -627,7 +616,13 @@ /** * Return a Choice based on this mask */ - constexpr CT::Choice as_choice() const { return CT::Choice::from_int(unpoisoned_value()); } + constexpr CT::Choice as_choice() const { + if constexpr(sizeof(T) >= sizeof(Choice::underlying_type)) { + return CT::Choice::from_mask(static_cast(unpoisoned_value())); + } else { + return CT::Choice::from_int(unpoisoned_value()); + } + } /** * Return the underlying value of the mask @@ -639,7 +634,7 @@ constexpr void _const_time_unpoison() const { CT::unpoison(m_mask); } private: - constexpr Mask(T m) : m_mask(m) {} + constexpr explicit Mask(T m) : m_mask(m) {} T m_mask; }; @@ -658,7 +653,7 @@ constexpr Option(T v, Choice valid) : m_has_value(valid), m_value(std::move(v)) {} /// Construct a set option with the provided value - constexpr Option(T v) : Option(std::move(v), Choice::yes()) {} + constexpr explicit Option(T v) : Option(std::move(v), Choice::yes()) {} /// Construct an unset option with a default inner value constexpr Option() @@ -728,29 +723,44 @@ T m_value; }; +/** +* Conditional memory copy (constant time) +* +* If mask is set, then sets dest to if_set, otherwise sets dest to if_unset +*/ template -constexpr inline Mask conditional_copy_mem(Mask mask, T* to, const T* from0, const T* from1, size_t elems) { - mask.select_n(to, from0, from1, elems); +constexpr inline Mask conditional_copy_mem(Mask mask, T* dest, const T* if_set, const T* if_unset, size_t elems) { + mask.select_n(dest, if_set, if_unset, elems); return mask; } template -constexpr inline Mask conditional_copy_mem(T cnd, T* to, const T* from0, const T* from1, size_t elems) { +constexpr inline Mask conditional_copy_mem(T cnd, T* dest, const T* if_set, const T* if_unset, size_t elems) { const auto mask = CT::Mask::expand(cnd); - return CT::conditional_copy_mem(mask, to, from0, from1, elems); + return CT::conditional_copy_mem(mask, dest, if_set, if_unset, elems); } +/** +* Conditional memory assignment (constant time) +* +* If mask is set overwrites dest with src +*/ template -constexpr inline Mask conditional_assign_mem(T cnd, T* sink, const T* src, size_t elems) { +constexpr inline Mask conditional_assign_mem(T cnd, T* dest, const T* src, size_t elems) { const auto mask = CT::Mask::expand(cnd); - mask.select_n(sink, src, sink, elems); + mask.select_n(dest, src, dest, elems); return mask; } +/** +* Conditional memory assignment (constant time) +* +* If mask is set overwrites dest with src +*/ template -constexpr inline Mask conditional_assign_mem(Choice cnd, T* sink, const T* src, size_t elems) { +constexpr inline Mask conditional_assign_mem(Choice cnd, T* dest, const T* src, size_t elems) { const auto mask = CT::Mask::from_choice(cnd); - mask.select_n(sink, src, sink, elems); + mask.select_n(dest, src, dest, elems); return mask; } @@ -767,8 +777,8 @@ conditional_swap(cnd, xp, yp); - x = reinterpret_cast(xp); - y = reinterpret_cast(yp); + x = reinterpret_cast(xp); // NOLINT(*-no-int-to-ptr) + y = reinterpret_cast(yp); // NOLINT(*-no-int-to-ptr) } template @@ -806,6 +816,21 @@ } /** +* Compare two spans and return a Mask which is set iff they were identical. +* +* If the spans are of different length then the function returns early without +* looking at either span +*/ +template +constexpr inline CT::Mask is_equal(std::span x, std::span y) { + if(x.size() != y.size()) { + return CT::Mask::cleared(); + } + + return is_equal(x.data(), y.data(), x.size()); +} + +/** * Compare two arrays of equal size and return a Mask indicating if * they are equal or not. The mask is set if they differ. */ diff -Nru botan3-3.7.1+dfsg/src/lib/utils/data_src.cpp botan3-3.12.0+dfsg/src/lib/utils/data_src.cpp --- botan3-3.7.1+dfsg/src/lib/utils/data_src.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/data_src.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -28,6 +29,18 @@ } /* +* Read a single byte from the DataSource +*/ +std::optional DataSource::read_byte() { + uint8_t b = 0; + if(this->read(&b, 1) == 1) { + return b; + } else { + return {}; + } +} + +/* * Peek a single byte from the DataSource */ size_t DataSource::peek_byte(uint8_t& out) const { @@ -41,7 +54,7 @@ uint8_t buf[64] = {0}; size_t discarded = 0; - while(n) { + while(n > 0) { const size_t got = this->read(buf, std::min(n, sizeof(buf))); discarded += got; n -= got; @@ -92,8 +105,7 @@ /* * DataSource_Memory Constructor */ -DataSource_Memory::DataSource_Memory(std::string_view in) : - m_source(cast_char_ptr_to_uint8(in.data()), cast_char_ptr_to_uint8(in.data()) + in.length()), m_offset(0) {} +DataSource_Memory::DataSource_Memory(std::string_view in) : DataSource_Memory(as_span_of_bytes(in)) {} /* * Read from a stream @@ -127,22 +139,20 @@ size_t got = 0; - if(offset) { - secure_vector buf(offset); - m_source.read(cast_uint8_ptr_to_char(buf.data()), buf.size()); - if(m_source.bad()) { - throw Stream_IO_Error("DataSource_Stream::peek: Source failure"); + if(offset > 0) { + m_source.seekg(offset, std::ios::cur); + if(!m_source.good()) { + m_source.clear(); + m_source.seekg(m_total_read, std::ios::beg); + return 0; } - got = static_cast(m_source.gcount()); } - if(got == offset) { - m_source.read(cast_uint8_ptr_to_char(out), length); - if(m_source.bad()) { - throw Stream_IO_Error("DataSource_Stream::peek: Source failure"); - } - got = static_cast(m_source.gcount()); + m_source.read(cast_uint8_ptr_to_char(out), length); + if(m_source.bad()) { + throw Stream_IO_Error("DataSource_Stream::peek: Source failure"); } + got = static_cast(m_source.gcount()); if(m_source.eof()) { m_source.clear(); @@ -156,6 +166,11 @@ * Check if the stream is empty or in error */ bool DataSource_Stream::end_of_data() const { + /* + Peek to trigger EOF indicator if positioned at the end of the stream. + Without this, good() returns true even when all data has been read. + */ + m_source.peek(); return (!m_source.good()); } diff -Nru botan3-3.7.1+dfsg/src/lib/utils/data_src.h botan3-3.12.0+dfsg/src/lib/utils/data_src.h --- botan3-3.7.1+dfsg/src/lib/utils/data_src.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/data_src.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,6 +11,8 @@ #include #include +#include +#include #include #include #include @@ -69,6 +71,13 @@ size_t read_byte(uint8_t& out); /** + * Read one byte. + * + * Returns nullopt if no further bytes are available + */ + std::optional read_byte(); + + /** * Peek at one byte. * @param out an output byte * @return length in bytes that was actually read and put @@ -90,8 +99,10 @@ DataSource() = default; virtual ~DataSource() = default; - DataSource& operator=(const DataSource&) = delete; DataSource(const DataSource&) = delete; + DataSource(DataSource&&) = default; + DataSource& operator=(const DataSource&) = delete; + DataSource& operator=(DataSource&&) = default; }; /** @@ -99,8 +110,8 @@ */ class BOTAN_PUBLIC_API(2, 0) DataSource_Memory final : public DataSource { public: - size_t read(uint8_t[], size_t) override; - size_t peek(uint8_t[], size_t, size_t) const override; + size_t read(uint8_t buf[], size_t length) override; + size_t peek(uint8_t buf[], size_t length, size_t offset) const override; bool check_available(size_t n) override; bool end_of_data() const override; @@ -147,13 +158,13 @@ */ class BOTAN_PUBLIC_API(2, 0) DataSource_Stream final : public DataSource { public: - size_t read(uint8_t[], size_t) override; - size_t peek(uint8_t[], size_t, size_t) const override; + size_t read(uint8_t buf[], size_t length) override; + size_t peek(uint8_t buf[], size_t length, size_t offset) const override; bool check_available(size_t n) override; bool end_of_data() const override; std::string id() const override; - DataSource_Stream(std::istream&, std::string_view id = ""); + BOTAN_FUTURE_EXPLICIT DataSource_Stream(std::istream& in, std::string_view id = ""); #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) /** @@ -161,12 +172,13 @@ * @param filename the path to the file * @param use_binary whether to treat the file as binary or not */ - DataSource_Stream(std::string_view filename, bool use_binary = false); + BOTAN_FUTURE_EXPLICIT DataSource_Stream(std::string_view filename, bool use_binary = false); #endif DataSource_Stream(const DataSource_Stream&) = delete; - + DataSource_Stream(DataSource_Stream&&) = delete; DataSource_Stream& operator=(const DataSource_Stream&) = delete; + DataSource_Stream& operator=(DataSource_Stream&&) = delete; ~DataSource_Stream() override; diff -Nru botan3-3.7.1+dfsg/src/lib/utils/database.h botan3-3.12.0+dfsg/src/lib/utils/database.h --- botan3-3.7.1+dfsg/src/lib/utils/database.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/database.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,12 +11,13 @@ #include #include #include +#include #include #include namespace Botan { -class BOTAN_PUBLIC_API(2, 0) SQL_Database { +class BOTAN_PUBLIC_API(2, 0) SQL_Database /* NOLINT(*-special-member-functions) */ { public: class BOTAN_PUBLIC_API(2, 0) SQL_DB_Error final : public Exception { public: @@ -32,7 +33,7 @@ int m_rc; }; - class BOTAN_PUBLIC_API(2, 0) Statement { + class BOTAN_PUBLIC_API(2, 0) Statement /* NOLINT(*-special-member-functions) */ { public: /* Bind statement parameters */ virtual void bind(int column, std::string_view str) = 0; @@ -45,6 +46,7 @@ virtual void bind(int column, const uint8_t* data, size_t len) = 0; + // TODO(Botan4) change this return type to a span /* Get output */ virtual std::pair get_blob(int column) = 0; diff -Nru botan3-3.7.1+dfsg/src/lib/utils/donna128.h botan3-3.12.0+dfsg/src/lib/utils/donna128.h --- botan3-3.7.1+dfsg/src/lib/utils/donna128.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/donna128.h 2026-05-07 01:38:28.000000000 +0000 @@ -1,111 +1,107 @@ /* -* A minimal 128-bit integer type for curve25519-donna +* A minimal 128-bit integer type * (C) 2014 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ -#ifndef BOTAN_CURVE25519_DONNA128_H_ -#define BOTAN_CURVE25519_DONNA128_H_ +#ifndef BOTAN_DONNA128_H_ +#define BOTAN_DONNA128_H_ #include #include -#include +#include namespace Botan { class donna128 final { public: - constexpr donna128(uint64_t ll = 0, uint64_t hh = 0) { - l = ll; - h = hh; - } - - donna128(const donna128&) = default; - donna128& operator=(const donna128&) = default; + constexpr explicit donna128(uint64_t l = 0, uint64_t h = 0) : m_lo(l), m_hi(h) {} - template + template constexpr friend donna128 operator>>(const donna128& x, T shift) { donna128 z = x; if(shift > 64) { - z.l = z.h >> (shift - 64); - z.h = 0; + z.m_lo = z.m_hi >> (shift - 64); + z.m_hi = 0; } else if(shift == 64) { - z.l = z.h; - z.h = 0; + z.m_lo = z.m_hi; + z.m_hi = 0; } else if(shift > 0) { - const uint64_t carry = z.h << static_cast(64 - shift); - z.h >>= shift; - z.l >>= shift; - z.l |= carry; + const uint64_t carry = z.m_hi << static_cast(64 - shift); + z.m_hi >>= shift; + z.m_lo >>= shift; + z.m_lo |= carry; } return z; } - template + template constexpr friend donna128 operator<<(const donna128& x, T shift) { donna128 z = x; if(shift > 64) { - z.h = z.l << (shift - 64); - z.l = 0; + z.m_hi = z.m_lo << (shift - 64); + z.m_lo = 0; } else if(shift == 64) { - z.h = z.l; - z.l = 0; + z.m_hi = z.m_lo; + z.m_lo = 0; } else if(shift > 0) { - const uint64_t carry = z.l >> static_cast(64 - shift); - z.l = (z.l << shift); - z.h = (z.h << shift) | carry; + const uint64_t carry = z.m_lo >> static_cast(64 - shift); + z.m_lo = (z.m_lo << shift); + z.m_hi = (z.m_hi << shift) | carry; } return z; } - constexpr friend uint64_t operator&(const donna128& x, uint64_t mask) { return x.l & mask; } + constexpr friend uint64_t operator&(const donna128& x, uint64_t mask) { return x.m_lo & mask; } constexpr uint64_t operator&=(uint64_t mask) { - h = 0; - l &= mask; - return l; + m_hi = 0; + m_lo &= mask; + return m_lo; } constexpr donna128& operator+=(const donna128& x) { - l += x.l; - h += x.h; + m_lo += x.m_lo; + m_hi += x.m_hi; - const uint64_t carry = CT::Mask::is_lt(l, x.l).if_set_return(1); - h += carry; + const uint64_t carry = CT::Mask::is_lt(m_lo, x.m_lo).if_set_return(1); + m_hi += carry; return *this; } constexpr donna128& operator+=(uint64_t x) { - l += x; - const uint64_t carry = CT::Mask::is_lt(l, x).if_set_return(1); - h += carry; + m_lo += x; + const uint64_t carry = CT::Mask::is_lt(m_lo, x).if_set_return(1); + m_hi += carry; return *this; } - constexpr uint64_t lo() const { return l; } + constexpr uint64_t lo() const { return m_lo; } - constexpr uint64_t hi() const { return h; } + constexpr uint64_t hi() const { return m_hi; } - constexpr operator uint64_t() const { return l; } + constexpr explicit operator uint64_t() const { return lo(); } private: - uint64_t h = 0, l = 0; + uint64_t m_lo = 0; + uint64_t m_hi = 0; }; -template +template constexpr inline donna128 operator*(const donna128& x, T y) { BOTAN_ARG_CHECK(x.hi() == 0, "High 64 bits of donna128 set to zero during multiply"); - uint64_t lo = 0, hi = 0; + uint64_t lo = 0; + uint64_t hi = 0; mul64x64_128(x.lo(), static_cast(y), &lo, &hi); return donna128(lo, hi); } -template +template constexpr inline donna128 operator*(T y, const donna128& x) { return x * y; } @@ -135,7 +131,7 @@ } constexpr inline uint64_t combine_lower(const donna128& a, size_t s1, const donna128& b, size_t s2) { - donna128 z = (a >> s1) | (b << s2); + const donna128 z = (a >> s1) | (b << s2); return z.lo(); } diff -Nru botan3-3.7.1+dfsg/src/lib/utils/dyn_load/dyn_load.cpp botan3-3.12.0+dfsg/src/lib/utils/dyn_load/dyn_load.cpp --- botan3-3.7.1+dfsg/src/lib/utils/dyn_load/dyn_load.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/dyn_load/dyn_load.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #include #include +#include #include #if defined(BOTAN_TARGET_OS_HAS_POSIX1) @@ -23,10 +24,10 @@ namespace { -void raise_runtime_loader_exception(std::string_view lib_name, const char* msg) { +[[noreturn]] void raise_runtime_loader_exception(std::string_view lib_name, const char* msg) { std::ostringstream err; err << "Failed to load " << lib_name << ": "; - if(msg) { + if(msg != nullptr) { err << msg; } else { err << "Unknown error"; @@ -35,28 +36,34 @@ throw System_Error(err.str(), 0); } -} // namespace - -Dynamically_Loaded_Library::Dynamically_Loaded_Library(std::string_view library) : m_lib_name(library), m_lib(nullptr) { +void* open_shared_library(const std::string& library) { #if defined(BOTAN_TARGET_OS_HAS_POSIX1) - m_lib = ::dlopen(m_lib_name.c_str(), RTLD_LAZY); + void* lib = ::dlopen(library.c_str(), RTLD_LAZY); // NOLINT(*-const-correctness) - if(!m_lib) { - raise_runtime_loader_exception(m_lib_name, ::dlerror()); + if(lib != nullptr) { + return lib; + } else { + raise_runtime_loader_exception(library, ::dlerror()); } #elif defined(BOTAN_TARGET_OS_HAS_WIN32) - m_lib = ::LoadLibraryA(m_lib_name.c_str()); - - if(!m_lib) - raise_runtime_loader_exception(m_lib_name, "LoadLibrary failed"); -#endif + void* lib = ::LoadLibraryA(library.c_str()); // NOLINT(*-const-correctness) - if(!m_lib) { - raise_runtime_loader_exception(m_lib_name, "Dynamic load not supported"); + if(lib != nullptr) { + return lib; + } else { + raise_runtime_loader_exception(library, "LoadLibrary failed"); } +#else + raise_runtime_loader_exception(library, "Dynamic loading not supported"); +#endif } +} // namespace + +Dynamically_Loaded_Library::Dynamically_Loaded_Library(std::string_view library) : + m_lib_name(library), m_lib(open_shared_library(m_lib_name)) {} + Dynamically_Loaded_Library::~Dynamically_Loaded_Library() { #if defined(BOTAN_TARGET_OS_HAS_POSIX1) ::dlclose(m_lib); @@ -65,19 +72,23 @@ #endif } -void* Dynamically_Loaded_Library::resolve_symbol(const std::string& symbol) { - void* addr = nullptr; +void* Dynamically_Loaded_Library::resolve_symbol(const std::string& symbol) const { + // NOLINTNEXTLINE(*-const-correctness) bug in clang-tidy + if(void* addr = resolve_symbol_internal(symbol)) { + return addr; + } + throw Invalid_Argument(fmt("Failed to resolve symbol {} in {}", symbol, m_lib_name)); +} +void* Dynamically_Loaded_Library::resolve_symbol_internal(const std::string& symbol) const { + // NOLINTNEXTLINE(*-const-correctness) bug in clang-tidy + void* addr = nullptr; #if defined(BOTAN_TARGET_OS_HAS_POSIX1) addr = ::dlsym(m_lib, symbol.c_str()); #elif defined(BOTAN_TARGET_OS_HAS_WIN32) addr = reinterpret_cast(::GetProcAddress(reinterpret_cast(m_lib), symbol.c_str())); #endif - if(!addr) { - throw Invalid_Argument(fmt("Failed to resolve symbol {} in {}", symbol, m_lib_name)); - } - return addr; } diff -Nru botan3-3.7.1+dfsg/src/lib/utils/dyn_load/dyn_load.h botan3-3.12.0+dfsg/src/lib/utils/dyn_load/dyn_load.h --- botan3-3.7.1+dfsg/src/lib/utils/dyn_load/dyn_load.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/dyn_load/dyn_load.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #define BOTAN_DYNAMIC_LOADER_H_ #include +#include #include namespace Botan { @@ -27,7 +28,7 @@ * qualified pathnames can help prevent code injection attacks (eg * via manipulation of LD_LIBRARY_PATH on Linux) */ - Dynamically_Loaded_Library(std::string_view lib_name); + explicit Dynamically_Loaded_Library(std::string_view lib_name); /** * Unload the DLL @@ -37,25 +38,48 @@ ~Dynamically_Loaded_Library(); /** + * Try to load a symbol + * @param symbol names the symbol to load + * @return address of the loaded symbol or std::nullopt if the symbol + * was not found + */ + template + std::optional try_resolve_symbol(const std::string& symbol) const + requires(std::is_pointer_v) + { + void* addr = resolve_symbol_internal(symbol); + return addr ? std::optional(reinterpret_cast(addr)) : std::nullopt; + } + + /** * Load a symbol (or fail with an exception) * @param symbol names the symbol to load * @return address of the loaded symbol + * @throws Invalid_Argument if the symbol is not found */ - void* resolve_symbol(const std::string& symbol); + void* resolve_symbol(const std::string& symbol) const; /** * Convenience function for casting symbol to the right type * @param symbol names the symbol to load * @return address of the loaded symbol + * @throws Invalid_Argument if the symbol is not found */ - template - T resolve(const std::string& symbol) { - return reinterpret_cast(resolve_symbol(symbol)); + template + PtrT resolve(const std::string& symbol) const + requires(std::is_pointer_v) + { + return reinterpret_cast(resolve_symbol(symbol)); } + Dynamically_Loaded_Library(const Dynamically_Loaded_Library&) = delete; + Dynamically_Loaded_Library(Dynamically_Loaded_Library&&) = default; + Dynamically_Loaded_Library& operator=(const Dynamically_Loaded_Library&) = delete; + Dynamically_Loaded_Library& operator=(Dynamically_Loaded_Library&&) = default; + private: - Dynamically_Loaded_Library(const Dynamically_Loaded_Library&); - Dynamically_Loaded_Library& operator=(const Dynamically_Loaded_Library&); + /// Returns a pointer to the symbol or nullptr if the symbol is not found. + void* resolve_symbol_internal(const std::string& symbol) const; std::string m_lib_name; void* m_lib; diff -Nru botan3-3.7.1+dfsg/src/lib/utils/dyn_load/info.txt botan3-3.12.0+dfsg/src/lib/utils/dyn_load/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/dyn_load/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/dyn_load/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + DYNAMIC_LOADER -> 20160310 - + name -> "Dynamic Loader" diff -Nru botan3-3.7.1+dfsg/src/lib/utils/exceptn.h botan3-3.12.0+dfsg/src/lib/utils/exceptn.h --- botan3-3.7.1+dfsg/src/lib/utils/exceptn.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/exceptn.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,66 +11,67 @@ #include #include #include +#include namespace Botan { /** * Different types of errors that might occur */ -enum class ErrorType { +enum class ErrorType : uint16_t { /** Some unknown error */ Unknown = 1, /** An error while calling a system interface */ - SystemError, + SystemError = 2, /** An operation seems valid, but not supported by the current version */ - NotImplemented, + NotImplemented = 3, /** Memory allocation failure */ - OutOfMemory, + OutOfMemory = 4, /** An internal error occurred */ - InternalError, + InternalError = 5, /** An I/O error occurred */ - IoError, + IoError = 6, /** Invalid object state */ InvalidObjectState = 100, /** A key was not set on an object when this is required */ - KeyNotSet, + KeyNotSet = 101, /** The application provided an argument which is invalid */ - InvalidArgument, + InvalidArgument = 102, /** A key with invalid length was provided */ - InvalidKeyLength, + InvalidKeyLength = 103, /** A nonce with invalid length was provided */ - InvalidNonceLength, + InvalidNonceLength = 104, /** An object type was requested but cannot be found */ - LookupError, + LookupError = 105, /** Encoding a message or datum failed */ - EncodingFailure, + EncodingFailure = 106, /** Decoding a message or datum failed */ - DecodingFailure, + DecodingFailure = 107, /** A TLS error (error_code will be the alert type) */ - TLSError, + TLSError = 108, /** An error during an HTTP operation */ - HttpError, + HttpError = 109, /** A message with an invalid authentication tag was detected */ - InvalidTag, + InvalidTag = 110, /** An error during Roughtime validation */ - RoughtimeError, + RoughtimeError = 111, /** An error when interacting with CommonCrypto API */ CommonCryptoError = 201, /** An error when interacting with a PKCS11 device */ - Pkcs11Error, + Pkcs11Error = 202, /** An error when interacting with a TPM device */ - TPMError, + TPMError = 203, /** An error when interacting with a database */ - DatabaseError, + DatabaseError = 204, /** An error when interacting with zlib */ ZlibError = 300, /** An error when interacting with bzip2 */ - Bzip2Error, + Bzip2Error = 301, /** An error when interacting with lzma */ - LzmaError, + LzmaError = 302, }; @@ -303,7 +304,7 @@ */ class BOTAN_PUBLIC_API(2, 9) System_Error : public Exception { public: - System_Error(std::string_view msg) : Exception(msg), m_error_code(0) {} + explicit System_Error(std::string_view msg) : Exception(msg), m_error_code(0) {} System_Error(std::string_view msg, int err_code); diff -Nru botan3-3.7.1+dfsg/src/lib/utils/filesystem.cpp botan3-3.12.0+dfsg/src/lib/utils/filesystem.cpp --- botan3-3.7.1+dfsg/src/lib/utils/filesystem.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/filesystem.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -5,10 +5,11 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include +#include #include -#include +#include +#include #include #include #include @@ -40,7 +41,7 @@ const std::string cur_path = dir_list[0]; dir_list.pop_front(); - std::unique_ptr> dir(::opendir(cur_path.c_str()), ::closedir); + const std::unique_ptr> dir(::opendir(cur_path.c_str()), ::closedir); if(dir) { while(struct dirent* dirent = ::readdir(dir.get())) { @@ -53,7 +54,7 @@ full_path_sstr << cur_path << "/" << filename; const std::string full_path = full_path_sstr.str(); - struct stat stat_buf; + struct stat stat_buf {}; if(::stat(full_path.c_str(), &stat_buf) == -1) { continue; diff -Nru botan3-3.7.1+dfsg/src/lib/utils/gfni_utils.h botan3-3.12.0+dfsg/src/lib/utils/gfni_utils.h --- botan3-3.7.1+dfsg/src/lib/utils/gfni_utils.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/gfni_utils.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,51 @@ +/* +* (C) 2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SIMD_GFNI_UTILS_H_ +#define BOTAN_SIMD_GFNI_UTILS_H_ + +#include +#include +#include + +namespace Botan { + +// Helper for defining GFNI constants +consteval uint64_t gfni_matrix(std::string_view s) { + uint64_t matrix = 0; + size_t bit_cnt = 0; + uint8_t row = 0; + + for(const char c : s) { + if(c == ' ' || c == '\n') { + continue; + } + if(c != '0' && c != '1') { + throw std::runtime_error("gfni_matrix: invalid bit value"); + } + + if(c == '1') { + row |= 0x80 >> (7 - bit_cnt % 8); + } + bit_cnt++; + + if(bit_cnt % 8 == 0) { + matrix <<= 8; + matrix |= row; + row = 0; + } + } + + if(bit_cnt != 64) { + throw std::runtime_error("gfni_matrix: invalid bit count"); + } + + return matrix; +} + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/ghash/ghash.cpp botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash.cpp --- botan3-3.7.1+dfsg/src/lib/utils/ghash/ghash.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,23 +10,31 @@ #include #include -#include #include #include -#include + +#if defined(BOTAN_HAS_CPUID) + #include +#endif namespace Botan { std::string GHASH::provider() const { +#if defined(BOTAN_HAS_GHASH_AVX512_CLMUL) + if(auto feat = CPUID::check(CPUID::Feature::AVX512_CLMUL)) { + return *feat; + } +#endif + #if defined(BOTAN_HAS_GHASH_CLMUL_CPU) - if(CPUID::has_carryless_multiply()) { - return "clmul"; + if(auto feat = CPUID::check(CPUID::Feature::HW_CLMUL)) { + return *feat; } #endif #if defined(BOTAN_HAS_GHASH_CLMUL_VPERM) - if(CPUID::has_vperm()) { - return "vperm"; + if(auto feat = CPUID::check(CPUID::Feature::SIMD_4X32)) { + return *feat; } #endif @@ -36,15 +44,22 @@ void GHASH::ghash_multiply(std::span x, std::span input, size_t blocks) { BOTAN_ASSERT_NOMSG(input.size() % GCM_BS == 0); +#if defined(BOTAN_HAS_GHASH_AVX512_CLMUL) + if(CPUID::has(CPUID::Feature::AVX512_CLMUL)) { + BOTAN_ASSERT_NOMSG(!m_H_pow.empty()); + return ghash_multiply_avx512_clmul(x.data(), m_H_pow.data(), input.data(), blocks); + } +#endif + #if defined(BOTAN_HAS_GHASH_CLMUL_CPU) - if(CPUID::has_carryless_multiply()) { + if(CPUID::has(CPUID::Feature::HW_CLMUL)) { BOTAN_ASSERT_NOMSG(!m_H_pow.empty()); - return ghash_multiply_cpu(x.data(), m_H_pow.data(), input.data(), blocks); + return ghash_multiply_cpu(x.data(), m_H_pow, input.data(), blocks); } #endif #if defined(BOTAN_HAS_GHASH_CLMUL_VPERM) - if(CPUID::has_vperm()) { + if(CPUID::has(CPUID::Feature::SIMD_2X64)) { return ghash_multiply_vperm(x.data(), m_HM.data(), input.data(), blocks); } #endif @@ -83,7 +98,7 @@ } bool GHASH::has_keying_material() const { - return !m_HM.empty(); + return !m_HM.empty() || !m_H_pow.empty(); } void GHASH::key_schedule(std::span key) { @@ -94,9 +109,32 @@ BOTAN_ASSERT_NOMSG(key.size() == GCM_BS); auto H = load_be>(key.first()); +#if defined(BOTAN_HAS_GHASH_AVX512_CLMUL) + if(CPUID::has(CPUID::Feature::AVX512_CLMUL)) { + zap(m_HM); + if(m_H_pow.size() != 32) { + m_H_pow.resize(32); + } + ghash_precompute_avx512_clmul(key.data(), m_H_pow.data()); + // m_HM left empty + return; + } +#endif + +#if defined(BOTAN_HAS_GHASH_CLMUL_CPU) + if(CPUID::has(CPUID::Feature::HW_CLMUL)) { + zap(m_HM); + ghash_precompute_cpu(key.data(), m_H_pow); + // m_HM left empty + return; + } +#endif + const uint64_t R = 0xE100000000000000; - m_HM.resize(256); + if(m_HM.size() != 256) { + m_HM.resize(256); + } // precompute the multiples of H for(size_t i = 0; i != 2; ++i) { @@ -114,13 +152,6 @@ H[0] = (H[0] >> 1) ^ carry; } } - -#if defined(BOTAN_HAS_GHASH_CLMUL_CPU) - if(CPUID::has_carryless_multiply()) { - m_H_pow.resize(8); - ghash_precompute_cpu(key.data(), m_H_pow.data()); - } -#endif } void GHASH::start(std::span nonce) { @@ -128,6 +159,8 @@ auto& n = m_nonce.emplace(); copy_mem(n, nonce); copy_mem(m_ghash, m_H_ad); + m_buffer.clear(); + m_text_len = 0; } void GHASH::set_associated_data(std::span input) { @@ -140,6 +173,14 @@ m_ad_len = input.size(); } +void GHASH::reset_associated_data() { + // This should only be called in GMAC context + BOTAN_STATE_CHECK(m_text_len == 0); + assert_key_material_set(); + m_H_ad = {0}; + m_ad_len = 0; +} + void GHASH::update_associated_data(std::span ad) { assert_key_material_set(); ghash_update(m_ghash, ad); @@ -151,6 +192,12 @@ BOTAN_STATE_CHECK(m_nonce); ghash_update(m_ghash, input); m_text_len += input.size(); + + // NIST SP 800-38D limits plaintext/ciphertext to 2^39 - 256 bits + constexpr uint64_t GHASH_MAX_BYTES = (((static_cast(1) << 39)) - 256) / 8; + if(m_text_len > GHASH_MAX_BYTES) { + throw Invalid_State("GCM message length limit exceeded"); + } } void GHASH::final(std::span mac) { @@ -168,23 +215,22 @@ m_nonce.reset(); } -void GHASH::nonce_hash(secure_vector& y0, std::span nonce) { +void GHASH::nonce_hash(std::span y0, std::span nonce) { assert_key_material_set(); BOTAN_STATE_CHECK(!m_nonce); - BOTAN_ARG_CHECK(y0.size() == GCM_BS, "ghash state must be 16 bytes"); - auto sy0 = std::span{y0}; - ghash_update(sy0, nonce); - ghash_zeropad(sy0); - ghash_final_block(sy0, 0, nonce.size()); + ghash_update(y0, nonce); + ghash_zeropad(y0); + ghash_final_block(y0, 0, nonce.size()); } void GHASH::clear() { zap(m_HM); - reset(); + zap(m_H_pow); + this->reset_state(); } -void GHASH::reset() { +void GHASH::reset_state() { m_H_ad = {0}; secure_scrub_memory(m_ghash); if(m_nonce) { @@ -192,7 +238,8 @@ m_nonce.reset(); } m_buffer.clear(); - m_text_len = m_ad_len = 0; + m_text_len = 0; + m_ad_len = 0; } void GHASH::ghash_update(std::span x, std::span input) { diff -Nru botan3-3.7.1+dfsg/src/lib/utils/ghash/ghash.h botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash.h --- botan3-3.7.1+dfsg/src/lib/utils/ghash/ghash.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash.h 2026-05-07 01:38:28.000000000 +0000 @@ -22,7 +22,7 @@ public: /// Hashing of non-default length nonce values for both GCM and GMAC use-cases - void nonce_hash(secure_vector& y0, std::span nonce); + void nonce_hash(std::span y0, std::span nonce); void start(std::span nonce); @@ -34,6 +34,9 @@ /// Incremental update of associated data used in the GMAC use-case void update_associated_data(std::span ad); + /// Reset the AAD state without resetting the key (used in GMAC::final_result) + void reset_associated_data(); + void final(std::span out); Key_Length_Specification key_spec() const override { return Key_Length_Specification(16); } @@ -42,7 +45,7 @@ void clear() override; - void reset(); + void reset_state(); std::string name() const override { return "GHASH"; } @@ -54,9 +57,21 @@ void ghash_final_block(std::span x, uint64_t ad_len, uint64_t pt_len); #if defined(BOTAN_HAS_GHASH_CLMUL_CPU) - static void ghash_precompute_cpu(const uint8_t H[16], uint64_t H_pow[4 * 2]); + static void ghash_precompute_cpu(const uint8_t H[16], secure_vector& H_pow); + + static void ghash_multiply_cpu(uint8_t x[16], + secure_vector& H_pow, + const uint8_t input[], + size_t blocks); +#endif + +#if defined(BOTAN_HAS_GHASH_AVX512_CLMUL) + static void ghash_precompute_avx512_clmul(const uint8_t H[16], uint64_t H_pow[16 * 2]); - static void ghash_multiply_cpu(uint8_t x[16], const uint64_t H_pow[4 * 2], const uint8_t input[], size_t blocks); + static void ghash_multiply_avx512_clmul(uint8_t x[16], + const uint64_t H_pow[16 * 2], + const uint8_t input[], + size_t blocks); #endif #if defined(BOTAN_HAS_GHASH_CLMUL_VPERM) @@ -70,14 +85,16 @@ private: AlignmentBuffer m_buffer; - std::array m_H_ad; /// cache of hash state after consuming the AD, reused for multiple messages - std::array m_ghash; /// hash state used for update() or update_associated_data() + /// cache of hash state after consuming the AD, reused for multiple messages + std::array m_H_ad{}; + /// hash state used for update() or update_associated_data() + std::array m_ghash{}; secure_vector m_HM; secure_vector m_H_pow; std::optional> m_nonce; - size_t m_ad_len = 0; - size_t m_text_len = 0; + uint64_t m_ad_len = 0; + uint64_t m_text_len = 0; }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/ghash/ghash_avx512_clmul/ghash_avx512_clmul.cpp botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash_avx512_clmul/ghash_avx512_clmul.cpp --- botan3-3.7.1+dfsg/src/lib/utils/ghash/ghash_avx512_clmul/ghash_avx512_clmul.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash_avx512_clmul/ghash_avx512_clmul.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,207 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include + +namespace Botan { + +namespace { + +BOTAN_FORCE_INLINE __m512i BOTAN_FN_ISA_AVX512_CLMUL fold(__m512i H) { + return _mm512_xor_si512(H, _mm512_bsrli_epi128(H, 8)); +} + +BOTAN_FORCE_INLINE SIMD_4x32 BOTAN_FN_ISA_AVX512_CLMUL reduce_xor(__m512i z) { + auto y = _mm256_xor_si256(_mm512_castsi512_si256(z), _mm512_extracti64x4_epi64(z, 0x1)); + auto x = _mm_xor_si128(_mm256_castsi256_si128(y), _mm256_extracti32x4_epi32(y, 0x1)); + return SIMD_4x32(x); +} + +BOTAN_FORCE_INLINE void BOTAN_FN_ISA_AVX512_CLMUL +ghash_x4_accum(__m512i H, __m512i H_fold, __m512i M, __m512i& lo, __m512i& hi, __m512i& mid) { + lo = _mm512_xor_si512(lo, _mm512_clmulepi64_epi128(H, M, 0x00)); + hi = _mm512_xor_si512(hi, _mm512_clmulepi64_epi128(H, M, 0x11)); + mid = _mm512_xor_si512(mid, _mm512_clmulepi64_epi128(H_fold, fold(M), 0x00)); +} + +BOTAN_FORCE_INLINE SIMD_4x32 BOTAN_FN_ISA_AVX512_CLMUL ghash_reduce(__m512i lo, __m512i hi, __m512i mid) { + mid = _mm512_ternarylogic_epi64(lo, mid, hi, 0x96); // mid ^= lo ^ hi + hi = _mm512_xor_si512(hi, _mm512_bsrli_epi128(mid, 8)); + lo = _mm512_xor_si512(lo, _mm512_bslli_epi128(mid, 8)); + return polyval_reduce(reduce_xor(hi), reduce_xor(lo)); +} + +BOTAN_FORCE_INLINE __m512i BOTAN_FN_ISA_AVX512_CLMUL insert_a(__m512i M, const SIMD_4x32& a) { + return _mm512_xor_epi64(M, _mm512_inserti64x2(_mm512_setzero_si512(), a.raw(), 0)); +} + +} // namespace + +void BOTAN_FN_ISA_AVX512_CLMUL GHASH::ghash_precompute_avx512_clmul(const uint8_t H_bytes[16], uint64_t H_pow[16 * 2]) { + const SIMD_4x32 H1 = mulx_polyval(reverse_vector(SIMD_4x32::load_le(H_bytes))); + + const SIMD_4x32 H2 = polyval_multiply(H1, H1); + const SIMD_4x32 H3 = polyval_multiply(H1, H2); + const SIMD_4x32 H4 = polyval_multiply(H2, H2); + + const SIMD_4x32 H5 = polyval_multiply(H4, H1); + const SIMD_4x32 H6 = polyval_multiply(H4, H2); + const SIMD_4x32 H7 = polyval_multiply(H4, H3); + const SIMD_4x32 H8 = polyval_multiply(H4, H4); + + const SIMD_4x32 H9 = polyval_multiply(H8, H1); + const SIMD_4x32 H10 = polyval_multiply(H8, H2); + const SIMD_4x32 H11 = polyval_multiply(H8, H3); + const SIMD_4x32 H12 = polyval_multiply(H8, H4); + + const SIMD_4x32 H13 = polyval_multiply(H8, H5); + const SIMD_4x32 H14 = polyval_multiply(H8, H6); + const SIMD_4x32 H15 = polyval_multiply(H8, H7); + const SIMD_4x32 H16 = polyval_multiply(H8, H8); + + // Store in reversed order in blocks of 4 so that the zmm load + // of H powers matches up with the message blocks + H4.store_le(H_pow); + H3.store_le(H_pow + 2); + H2.store_le(H_pow + 4); + H1.store_le(H_pow + 6); + + H8.store_le(H_pow + 8); + H7.store_le(H_pow + 10); + H6.store_le(H_pow + 12); + H5.store_le(H_pow + 14); + + H12.store_le(H_pow + 16); + H11.store_le(H_pow + 18); + H10.store_le(H_pow + 20); + H9.store_le(H_pow + 22); + + H16.store_le(H_pow + 24); + H15.store_le(H_pow + 26); + H14.store_le(H_pow + 28); + H13.store_le(H_pow + 30); +} + +void BOTAN_FN_ISA_AVX512_CLMUL GHASH::ghash_multiply_avx512_clmul(uint8_t x[16], + const uint64_t H_pow[16 * 2], + const uint8_t input[], + size_t blocks) { + SIMD_4x32 a = reverse_vector(SIMD_4x32::load_le(x)); + + // Byte swap each lane + const auto BSWAP = _mm512_set_epi64(0x0001020304050607, + 0x08090A0B0C0D0E0F, + 0x0001020304050607, + 0x08090A0B0C0D0E0F, + 0x0001020304050607, + 0x08090A0B0C0D0E0F, + 0x0001020304050607, + 0x08090A0B0C0D0E0F); + + if(blocks >= 16) { + const auto H1 = _mm512_loadu_si512(H_pow); // [H4,H3,H2,H1] + const auto H2 = _mm512_loadu_si512(H_pow + 8); // [H8,H7,H6,H5] + const auto H3 = _mm512_loadu_si512(H_pow + 16); // [H12,H11,H10,H9] + const auto H4 = _mm512_loadu_si512(H_pow + 24); // [H16,H15,H14,H13] + + // Precompute H folds (H ^ (H >> 64)) for Karatsuba - loop invariant + const auto H1_fold = fold(H1); + const auto H2_fold = fold(H2); + const auto H3_fold = fold(H3); + const auto H4_fold = fold(H4); + + while(blocks >= 16) { + __m512i M1 = _mm512_shuffle_epi8(_mm512_loadu_si512(input), BSWAP); + const auto M2 = _mm512_shuffle_epi8(_mm512_loadu_si512(input + 64), BSWAP); + const auto M3 = _mm512_shuffle_epi8(_mm512_loadu_si512(input + 128), BSWAP); + const auto M4 = _mm512_shuffle_epi8(_mm512_loadu_si512(input + 192), BSWAP); + + M1 = insert_a(M1, a); + + auto lo = _mm512_setzero_si512(); + auto hi = _mm512_setzero_si512(); + auto mid = _mm512_setzero_si512(); + + ghash_x4_accum(H4, H4_fold, M1, lo, hi, mid); + ghash_x4_accum(H3, H3_fold, M2, lo, hi, mid); + ghash_x4_accum(H2, H2_fold, M3, lo, hi, mid); + ghash_x4_accum(H1, H1_fold, M4, lo, hi, mid); + + a = ghash_reduce(lo, hi, mid); + + input += 16 * 16; + blocks -= 16; + } + } + + if(blocks >= 8) { + const auto H1 = _mm512_loadu_si512(H_pow); // [H4,H3,H2,H1] + const auto H2 = _mm512_loadu_si512(H_pow + 8); // [H8,H7,H6,H5] + + const auto H1_fold = fold(H1); + const auto H2_fold = fold(H2); + + while(blocks >= 8) { + __m512i M1 = _mm512_shuffle_epi8(_mm512_loadu_si512(input), BSWAP); + const __m512i M2 = _mm512_shuffle_epi8(_mm512_loadu_si512(input + 64), BSWAP); + + M1 = insert_a(M1, a); + + auto lo = _mm512_setzero_si512(); + auto hi = _mm512_setzero_si512(); + auto mid = _mm512_setzero_si512(); + + ghash_x4_accum(H2, H2_fold, M1, lo, hi, mid); + ghash_x4_accum(H1, H1_fold, M2, lo, hi, mid); + + a = ghash_reduce(lo, hi, mid); + + input += 8 * 16; + blocks -= 8; + } + } + + if(blocks >= 4) { + const auto H1 = _mm512_loadu_si512(H_pow); // [H4,H3,H2,H1] + const auto H1_fold = fold(H1); + + while(blocks >= 4) { + __m512i M = _mm512_shuffle_epi8(_mm512_loadu_si512(input), BSWAP); + M = insert_a(M, a); + + auto lo = _mm512_clmulepi64_epi128(H1, M, 0x00); + auto hi = _mm512_clmulepi64_epi128(H1, M, 0x11); + auto mid = _mm512_clmulepi64_epi128(H1_fold, fold(M), 0x00); + + a = ghash_reduce(lo, hi, mid); + + input += 4 * 16; + blocks -= 4; + } + } + + if(blocks > 0) { + // H1 is at offset 6 in the reversed layout [H4,H3,H2,H1,...] + const SIMD_4x32 H1 = SIMD_4x32::load_le(H_pow + 6); + + for(size_t i = 0; i != blocks; ++i) { + const SIMD_4x32 m = reverse_vector(SIMD_4x32::load_le(input + 16 * i)); + a ^= m; + a = polyval_multiply(H1, a); + } + } + + a = reverse_vector(a); + a.store_le(x); +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/ghash/ghash_avx512_clmul/info.txt botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash_avx512_clmul/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/ghash/ghash_avx512_clmul/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash_avx512_clmul/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,16 @@ + +GHASH_AVX512_CLMUL -> 20260120 + + + +name -> "GHASH AVX-512 CLMUL" + + + +cpuid +ghash_cpu # for polyval_fn.h + + + +avx512_clmul + diff -Nru botan3-3.7.1+dfsg/src/lib/utils/ghash/ghash_cpu/ghash_cpu.cpp botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash_cpu/ghash_cpu.cpp --- botan3-3.7.1+dfsg/src/lib/utils/ghash/ghash_cpu/ghash_cpu.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash_cpu/ghash_cpu.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,167 +7,144 @@ #include -#include - -#if defined(BOTAN_SIMD_USE_SSE2) - #include - #include -#endif +#include +#include +#include namespace Botan { namespace { -BOTAN_FUNC_ISA_INLINE(BOTAN_VPERM_ISA) SIMD_4x32 reverse_vector(const SIMD_4x32& in) { -#if defined(BOTAN_SIMD_USE_SSE2) - const __m128i BSWAP_MASK = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); - return SIMD_4x32(_mm_shuffle_epi8(in.raw(), BSWAP_MASK)); -#elif defined(BOTAN_SIMD_USE_NEON) - const uint8_t maskb[16] = {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; - const uint8x16_t mask = vld1q_u8(maskb); - return SIMD_4x32(vreinterpretq_u32_u8(vqtbl1q_u8(vreinterpretq_u8_u32(in.raw()), mask))); -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - const __vector unsigned char mask = {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; - return SIMD_4x32(vec_perm(in.raw(), in.raw(), mask)); -#endif -} - -template -BOTAN_FORCE_INLINE SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_CLMUL_ISA) clmul(const SIMD_4x32& H, const SIMD_4x32& x) { - static_assert(M == 0x00 || M == 0x01 || M == 0x10 || M == 0x11, "Valid clmul mode"); - -#if defined(BOTAN_SIMD_USE_SSE2) - return SIMD_4x32(_mm_clmulepi64_si128(x.raw(), H.raw(), M)); -#elif defined(BOTAN_SIMD_USE_NEON) - const uint64_t a = vgetq_lane_u64(vreinterpretq_u64_u32(x.raw()), M & 0x01); - const uint64_t b = vgetq_lane_u64(vreinterpretq_u64_u32(H.raw()), (M & 0x10) >> 4); - - #if defined(BOTAN_BUILD_COMPILER_IS_MSVC) - __n64 a1 = {a}, b1 = {b}; - return SIMD_4x32(vmull_p64(a1, b1)); - #else - return SIMD_4x32(reinterpret_cast(vmull_p64(a, b))); - #endif - -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - const SIMD_4x32 mask_lo = SIMD_4x32(0, 0, 0xFFFFFFFF, 0xFFFFFFFF); - - SIMD_4x32 i1 = x; - SIMD_4x32 i2 = H; - - if(M == 0x11) { - i1 &= mask_lo; - i2 &= mask_lo; - } else if(M == 0x10) { - i1 = i1.shift_elems_left<2>(); - } else if(M == 0x01) { - i2 = i2.shift_elems_left<2>(); - } else if(M == 0x00) { - i1 = mask_lo.andc(i1); - i2 = mask_lo.andc(i2); - } - - auto i1v = reinterpret_cast<__vector unsigned long long>(i1.raw()); - auto i2v = reinterpret_cast<__vector unsigned long long>(i2.raw()); - - #if BOTAN_COMPILER_HAS_BUILTIN(__builtin_crypto_vpmsumd) - auto rv = __builtin_crypto_vpmsumd(i1v, i2v); - #else - auto rv = __builtin_altivec_crypto_vpmsumd(i1v, i2v); - #endif - - return SIMD_4x32(reinterpret_cast<__vector unsigned int>(rv)); -#endif -} +inline SIMD_4x32 BOTAN_FN_ISA_CLMUL polyval_multiply_x4(const SIMD_4x32& H1, + const SIMD_4x32& H2, + const SIMD_4x32& H3, + const SIMD_4x32& H4, + const SIMD_4x32& X1, + const SIMD_4x32& X2, + const SIMD_4x32& X3, + const SIMD_4x32& X4) { + const SIMD_4x32 lo = (clmul<0x00>(H1, X1) ^ clmul<0x00>(H2, X2)) ^ (clmul<0x00>(H3, X3) ^ clmul<0x00>(H4, X4)); + const SIMD_4x32 hi = (clmul<0x11>(H1, X1) ^ clmul<0x11>(H2, X2)) ^ (clmul<0x11>(H3, X3) ^ clmul<0x11>(H4, X4)); -inline SIMD_4x32 gcm_reduce(const SIMD_4x32& B0, const SIMD_4x32& B1) { - SIMD_4x32 X0 = B1.shr<31>(); - SIMD_4x32 X1 = B1.shl<1>(); - SIMD_4x32 X2 = B0.shr<31>(); - SIMD_4x32 X3 = B0.shl<1>(); - - X3 |= X0.shift_elems_right<3>(); - X3 |= X2.shift_elems_left<1>(); - X1 |= X0.shift_elems_left<1>(); - - X0 = X1.shl<31>() ^ X1.shl<30>() ^ X1.shl<25>(); - - X1 ^= X0.shift_elems_left<3>(); - - X0 = X1 ^ X3 ^ X0.shift_elems_right<1>(); - X0 ^= X1.shr<7>() ^ X1.shr<2>() ^ X1.shr<1>(); - return X0; -} + SIMD_4x32 mid; -inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_CLMUL_ISA) gcm_multiply(const SIMD_4x32& H, const SIMD_4x32& x) { - SIMD_4x32 T0 = clmul<0x11>(H, x); - SIMD_4x32 T1 = clmul<0x10>(H, x); - SIMD_4x32 T2 = clmul<0x01>(H, x); - SIMD_4x32 T3 = clmul<0x00>(H, x); - - T1 ^= T2; - T0 ^= T1.shift_elems_right<2>(); - T3 ^= T1.shift_elems_left<2>(); + mid ^= clmul<0x00>(H1 ^ H1.shift_elems_right<2>(), X1 ^ X1.shift_elems_right<2>()); + mid ^= clmul<0x00>(H2 ^ H2.shift_elems_right<2>(), X2 ^ X2.shift_elems_right<2>()); + mid ^= clmul<0x00>(H3 ^ H3.shift_elems_right<2>(), X3 ^ X3.shift_elems_right<2>()); + mid ^= clmul<0x00>(H4 ^ H4.shift_elems_right<2>(), X4 ^ X4.shift_elems_right<2>()); + mid ^= lo; + mid ^= hi; + + return polyval_reduce(hi ^ mid.shift_elems_right<2>(), lo ^ mid.shift_elems_left<2>()); +} + +inline SIMD_4x32 BOTAN_FN_ISA_CLMUL polyval_multiply_x8(const SIMD_4x32& H1, + const SIMD_4x32& H2, + const SIMD_4x32& H3, + const SIMD_4x32& H4, + const SIMD_4x32& H5, + const SIMD_4x32& H6, + const SIMD_4x32& H7, + const SIMD_4x32& H8, + const SIMD_4x32& X1, + const SIMD_4x32& X2, + const SIMD_4x32& X3, + const SIMD_4x32& X4, + const SIMD_4x32& X5, + const SIMD_4x32& X6, + const SIMD_4x32& X7, + const SIMD_4x32& X8) { + const SIMD_4x32 lo = clmul<0x00>(H1, X1) ^ clmul<0x00>(H2, X2) ^ clmul<0x00>(H3, X3) ^ clmul<0x00>(H4, X4) ^ + clmul<0x00>(H5, X5) ^ clmul<0x00>(H6, X6) ^ clmul<0x00>(H7, X7) ^ clmul<0x00>(H8, X8); + + const SIMD_4x32 hi = clmul<0x11>(H1, X1) ^ clmul<0x11>(H2, X2) ^ clmul<0x11>(H3, X3) ^ clmul<0x11>(H4, X4) ^ + clmul<0x11>(H5, X5) ^ clmul<0x11>(H6, X6) ^ clmul<0x11>(H7, X7) ^ clmul<0x11>(H8, X8); + + SIMD_4x32 mid; + + mid ^= clmul<0x00>(H1 ^ H1.shift_elems_right<2>(), X1 ^ X1.shift_elems_right<2>()); + mid ^= clmul<0x00>(H2 ^ H2.shift_elems_right<2>(), X2 ^ X2.shift_elems_right<2>()); + mid ^= clmul<0x00>(H3 ^ H3.shift_elems_right<2>(), X3 ^ X3.shift_elems_right<2>()); + mid ^= clmul<0x00>(H4 ^ H4.shift_elems_right<2>(), X4 ^ X4.shift_elems_right<2>()); + mid ^= clmul<0x00>(H5 ^ H5.shift_elems_right<2>(), X5 ^ X5.shift_elems_right<2>()); + mid ^= clmul<0x00>(H6 ^ H6.shift_elems_right<2>(), X6 ^ X6.shift_elems_right<2>()); + mid ^= clmul<0x00>(H7 ^ H7.shift_elems_right<2>(), X7 ^ X7.shift_elems_right<2>()); + mid ^= clmul<0x00>(H8 ^ H8.shift_elems_right<2>(), X8 ^ X8.shift_elems_right<2>()); + mid ^= lo; + mid ^= hi; - return gcm_reduce(T0, T3); + return polyval_reduce(hi ^ mid.shift_elems_right<2>(), lo ^ mid.shift_elems_left<2>()); } -inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_CLMUL_ISA) gcm_multiply_x4(const SIMD_4x32& H1, - const SIMD_4x32& H2, - const SIMD_4x32& H3, - const SIMD_4x32& H4, - const SIMD_4x32& X1, - const SIMD_4x32& X2, - const SIMD_4x32& X3, - const SIMD_4x32& X4) { - /* - * Mutiply with delayed reduction, algorithm by Krzysztof Jankowski - * and Pierre Laurent of Intel - */ +} // namespace - const SIMD_4x32 lo = (clmul<0x00>(H1, X1) ^ clmul<0x00>(H2, X2)) ^ (clmul<0x00>(H3, X3) ^ clmul<0x00>(H4, X4)); +void BOTAN_FN_ISA_CLMUL GHASH::ghash_precompute_cpu(const uint8_t H_bytes[16], secure_vector& H_pow) { + const SIMD_4x32 H1 = mulx_polyval(reverse_vector(SIMD_4x32::load_le(H_bytes))); + const SIMD_4x32 H2 = polyval_multiply(H1, H1); + const SIMD_4x32 H3 = polyval_multiply(H1, H2); + const SIMD_4x32 H4 = polyval_multiply(H2, H2); + + H_pow.reserve(2 * 8); + H_pow.resize(2 * 4); + H1.store_le(&H_pow[0]); // NOLINT(*-container-data-pointer) + H2.store_le(&H_pow[2]); + H3.store_le(&H_pow[4]); + H4.store_le(&H_pow[6]); +} + +void BOTAN_FN_ISA_CLMUL GHASH::ghash_multiply_cpu(uint8_t x[16], + secure_vector& H_pow, + const uint8_t input[], + size_t blocks) { + BOTAN_ASSERT_NOMSG(H_pow.size() == 2 * 4 || H_pow.size() == 2 * 8); - const SIMD_4x32 hi = (clmul<0x11>(H1, X1) ^ clmul<0x11>(H2, X2)) ^ (clmul<0x11>(H3, X3) ^ clmul<0x11>(H4, X4)); + const SIMD_4x32 H1 = SIMD_4x32::load_le(&H_pow[0]); // NOLINT(*-container-data-pointer) - SIMD_4x32 T; + SIMD_4x32 a = reverse_vector(SIMD_4x32::load_le(x)); - T ^= clmul<0x00>(H1 ^ H1.shift_elems_right<2>(), X1 ^ X1.shift_elems_right<2>()); - T ^= clmul<0x00>(H2 ^ H2.shift_elems_right<2>(), X2 ^ X2.shift_elems_right<2>()); - T ^= clmul<0x00>(H3 ^ H3.shift_elems_right<2>(), X3 ^ X3.shift_elems_right<2>()); - T ^= clmul<0x00>(H4 ^ H4.shift_elems_right<2>(), X4 ^ X4.shift_elems_right<2>()); - T ^= lo; - T ^= hi; + if(blocks >= 8) { + const SIMD_4x32 H2 = SIMD_4x32::load_le(&H_pow[2]); + const SIMD_4x32 H3 = SIMD_4x32::load_le(&H_pow[4]); + const SIMD_4x32 H4 = SIMD_4x32::load_le(&H_pow[6]); + + if(H_pow.size() < 2 * 8) { + H_pow.resize(2 * 8); + const SIMD_4x32 H5 = polyval_multiply(H4, H1); + const SIMD_4x32 H6 = polyval_multiply(H4, H2); + const SIMD_4x32 H7 = polyval_multiply(H4, H3); + const SIMD_4x32 H8 = polyval_multiply(H4, H4); + H5.store_le(&H_pow[8]); + H6.store_le(&H_pow[10]); + H7.store_le(&H_pow[12]); + H8.store_le(&H_pow[14]); + } - return gcm_reduce(hi ^ T.shift_elems_right<2>(), lo ^ T.shift_elems_left<2>()); -} + const SIMD_4x32 H5 = SIMD_4x32::load_le(&H_pow[8]); + const SIMD_4x32 H6 = SIMD_4x32::load_le(&H_pow[10]); + const SIMD_4x32 H7 = SIMD_4x32::load_le(&H_pow[12]); + const SIMD_4x32 H8 = SIMD_4x32::load_le(&H_pow[14]); -} // namespace - -BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) void GHASH::ghash_precompute_cpu(const uint8_t H_bytes[16], uint64_t H_pow[4 * 2]) { - const SIMD_4x32 H1 = reverse_vector(SIMD_4x32::load_le(H_bytes)); - const SIMD_4x32 H2 = gcm_multiply(H1, H1); - const SIMD_4x32 H3 = gcm_multiply(H1, H2); - const SIMD_4x32 H4 = gcm_multiply(H2, H2); - - H1.store_le(H_pow); - H2.store_le(H_pow + 2); - H3.store_le(H_pow + 4); - H4.store_le(H_pow + 6); -} + while(blocks >= 8) { + const SIMD_4x32 m0 = reverse_vector(SIMD_4x32::load_le(input)); + const SIMD_4x32 m1 = reverse_vector(SIMD_4x32::load_le(input + 16 * 1)); + const SIMD_4x32 m2 = reverse_vector(SIMD_4x32::load_le(input + 16 * 2)); + const SIMD_4x32 m3 = reverse_vector(SIMD_4x32::load_le(input + 16 * 3)); + const SIMD_4x32 m4 = reverse_vector(SIMD_4x32::load_le(input + 16 * 4)); + const SIMD_4x32 m5 = reverse_vector(SIMD_4x32::load_le(input + 16 * 5)); + const SIMD_4x32 m6 = reverse_vector(SIMD_4x32::load_le(input + 16 * 6)); + const SIMD_4x32 m7 = reverse_vector(SIMD_4x32::load_le(input + 16 * 7)); -BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) -void GHASH::ghash_multiply_cpu(uint8_t x[16], const uint64_t H_pow[8], const uint8_t input[], size_t blocks) { - /* - * Algorithms 1 and 5 from Intel's CLMUL guide - */ - const SIMD_4x32 H1 = SIMD_4x32::load_le(H_pow); + a = polyval_multiply_x8(H1, H2, H3, H4, H5, H6, H7, H8, m7, m6, m5, m4, m3, m2, m1, m0 ^ a); - SIMD_4x32 a = reverse_vector(SIMD_4x32::load_le(x)); + input += 8 * 16; + blocks -= 8; + } + } if(blocks >= 4) { - const SIMD_4x32 H2 = SIMD_4x32::load_le(H_pow + 2); - const SIMD_4x32 H3 = SIMD_4x32::load_le(H_pow + 4); - const SIMD_4x32 H4 = SIMD_4x32::load_le(H_pow + 6); + const SIMD_4x32 H2 = SIMD_4x32::load_le(&H_pow[2]); + const SIMD_4x32 H3 = SIMD_4x32::load_le(&H_pow[4]); + const SIMD_4x32 H4 = SIMD_4x32::load_le(&H_pow[6]); while(blocks >= 4) { const SIMD_4x32 m0 = reverse_vector(SIMD_4x32::load_le(input)); @@ -176,7 +153,7 @@ const SIMD_4x32 m3 = reverse_vector(SIMD_4x32::load_le(input + 16 * 3)); a ^= m0; - a = gcm_multiply_x4(H1, H2, H3, H4, m3, m2, m1, a); + a = polyval_multiply_x4(H1, H2, H3, H4, m3, m2, m1, a); input += 4 * 16; blocks -= 4; @@ -187,7 +164,7 @@ const SIMD_4x32 m = reverse_vector(SIMD_4x32::load_le(input + 16 * i)); a ^= m; - a = gcm_multiply(H1, a); + a = polyval_multiply(H1, a); } a = reverse_vector(a); diff -Nru botan3-3.7.1+dfsg/src/lib/utils/ghash/ghash_cpu/info.txt botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash_cpu/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/ghash/ghash_cpu/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash_cpu/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,16 +1,15 @@ - + GHASH_CLMUL_CPU -> 20201002 - + name -> "GHASH SIMD" brief -> "GHASH using SIMD instructions" -endian little - -simd +cpuid +simd_4x32 diff -Nru botan3-3.7.1+dfsg/src/lib/utils/ghash/ghash_cpu/polyval_fn.h botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash_cpu/polyval_fn.h --- botan3-3.7.1+dfsg/src/lib/utils/ghash/ghash_cpu/polyval_fn.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash_cpu/polyval_fn.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,141 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_POLYVAL_FN_H_ +#define BOTAN_POLYVAL_FN_H_ + +#include + +namespace Botan { + +// NOLINTBEGIN(portability-simd-intrinsics) + +BOTAN_FORCE_INLINE BOTAN_FN_ISA_SIMD_4X32 SIMD_4x32 reverse_vector(const SIMD_4x32& in) { +#if defined(BOTAN_SIMD_USE_SSSE3) + const __m128i BSWAP_MASK = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + return SIMD_4x32(_mm_shuffle_epi8(in.raw(), BSWAP_MASK)); +#elif defined(BOTAN_SIMD_USE_NEON) + const uint8_t maskb[16] = {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; + const uint8x16_t mask = vld1q_u8(maskb); + return SIMD_4x32(vreinterpretq_u32_u8(vqtbl1q_u8(vreinterpretq_u8_u32(in.raw()), mask))); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + const __vector unsigned char mask = {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; + return SIMD_4x32(vec_perm(in.raw(), in.raw(), mask)); +#endif +} + +template +BOTAN_FORCE_INLINE BOTAN_FN_ISA_CLMUL SIMD_4x32 clmul(const SIMD_4x32& H, const SIMD_4x32& x) { + static_assert(M == 0x00 || M == 0x01 || M == 0x10 || M == 0x11, "Valid clmul mode"); + +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_4x32(_mm_clmulepi64_si128(x.raw(), H.raw(), M)); +#elif defined(BOTAN_SIMD_USE_NEON) + const uint64_t a = vgetq_lane_u64(vreinterpretq_u64_u32(x.raw()), M & 0x01); + const uint64_t b = vgetq_lane_u64(vreinterpretq_u64_u32(H.raw()), (M & 0x10) >> 4); + + #if defined(BOTAN_BUILD_COMPILER_IS_MSVC) + __n64 a1 = {a}, b1 = {b}; + return SIMD_4x32(vmull_p64(a1, b1)); + #else + return SIMD_4x32(reinterpret_cast(vmull_p64(a, b))); + #endif + +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + const SIMD_4x32 mask_lo = SIMD_4x32(0, 0, 0xFFFFFFFF, 0xFFFFFFFF); + constexpr uint8_t flip = (std::endian::native == std::endian::big) ? 0x11 : 0x00; + + SIMD_4x32 i1 = x; + SIMD_4x32 i2 = H; + + if constexpr(std::endian::native == std::endian::big) { + i1 = reverse_vector(i1).bswap(); + i2 = reverse_vector(i2).bswap(); + } + + if constexpr(M == (0x11 ^ flip)) { + i1 &= mask_lo; + i2 &= mask_lo; + } else if constexpr(M == (0x10 ^ flip)) { + i1 = i1.shift_elems_left<2>(); + } else if constexpr(M == (0x01 ^ flip)) { + i2 = i2.shift_elems_left<2>(); + } else if constexpr(M == (0x00 ^ flip)) { + i1 = mask_lo.andc(i1); + i2 = mask_lo.andc(i2); + } + + auto i1v = reinterpret_cast<__vector unsigned long long>(i1.raw()); + auto i2v = reinterpret_cast<__vector unsigned long long>(i2.raw()); + + #if BOTAN_COMPILER_HAS_BUILTIN(__builtin_crypto_vpmsumd) + auto rv = __builtin_crypto_vpmsumd(i1v, i2v); + #else + auto rv = __builtin_altivec_crypto_vpmsumd(i1v, i2v); + #endif + + auto z = SIMD_4x32(reinterpret_cast<__vector unsigned int>(rv)); + + if constexpr(std::endian::native == std::endian::big) { + z = reverse_vector(z).bswap(); + } + + return z; +#endif +} + +// NOLINTEND(portability-simd-intrinsics) + +BOTAN_FORCE_INLINE SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 mulx_polyval(const SIMD_4x32& h) { + const auto V = SIMD_4x32(0x00000001, 0x00000000, 0x00000000, 0xc2000000); + + // Bitmask set iff the top bit of h is set + const auto mask = h.top_bit_mask(); + + // Extract the top bits of the words and move them into place as the low bit of the next word + auto top_bits = h.shr<31>().shift_elems_left<1>(); + + // The main shift, adding back in the top bits that are otherwise lost + auto shifted_h = h.shl<1>() | top_bits; + + return shifted_h ^ (mask & V); +} + +BOTAN_FORCE_INLINE SIMD_4x32 BOTAN_FN_ISA_CLMUL polyval_reduce(const SIMD_4x32& hi, const SIMD_4x32& lo) { + const SIMD_4x32 V(0, 0xC2000000, 0, 0); + + /* + Montgomery reduction + Input: 256-bit operand [X3 : X2 : X1 : X0] + [A1 : A0] = X0 • 0xc200000000000000 + [B1 : B0] = [X0 ⨁ A1 : X1 ⨁ A0] + [C1 : C0] = B0 • 0xc200000000000000 + [D1 : D0] = [B0 ⨁ C1 : B1 ⨁ C0] + Output: [D1 ⨁ X3 : D0 ⨁ X2] + */ + + const auto A = clmul<0x00>(lo, V); + const auto B = A ^ lo.swap_halves(); + const auto C = clmul<0x00>(B, V); + const auto D = C ^ B.swap_halves(); + + return D ^ hi; +} + +BOTAN_FORCE_INLINE SIMD_4x32 BOTAN_FN_ISA_CLMUL polyval_multiply(const SIMD_4x32& H, const SIMD_4x32& x) { + SIMD_4x32 hi = clmul<0x11>(H, x); + const SIMD_4x32 mid = clmul<0x10>(H, x) ^ clmul<0x01>(H, x); + SIMD_4x32 lo = clmul<0x00>(H, x); + + hi ^= mid.shift_elems_right<2>(); + lo ^= mid.shift_elems_left<2>(); + + return polyval_reduce(hi, lo); +} + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/ghash/ghash_vperm/ghash_vperm.cpp botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash_vperm/ghash_vperm.cpp --- botan3-3.7.1+dfsg/src/lib/utils/ghash/ghash_vperm/ghash_vperm.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash_vperm/ghash_vperm.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,59 +1,51 @@ /* * (C) 2017 Jack Lloyd +* (C) 2025 polarnis * * Botan is released under the Simplified BSD License (see license.txt) */ #include -#include -#include +#include +#include namespace Botan { -// TODO: extend this to support NEON and AltiVec - -BOTAN_FUNC_ISA("ssse3") +BOTAN_FN_ISA_SIMD_2X64 void GHASH::ghash_multiply_vperm(uint8_t x[16], const uint64_t HM[256], const uint8_t input_bytes[], size_t blocks) { - const __m128i BSWAP_MASK = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); - - const __m128i* HM_mm = reinterpret_cast(HM); + auto X = SIMD_2x64::load_le(x).reverse_all_bytes(); - __m128i X = _mm_loadu_si128(reinterpret_cast<__m128i*>(x)); - X = _mm_shuffle_epi8(X, BSWAP_MASK); - - const __m128i ones = _mm_set1_epi8(-1); + const auto* HM_mm = reinterpret_cast(HM); + const auto ones = SIMD_2x64::all_ones(); for(size_t b = 0; b != blocks; ++b) { - __m128i M = _mm_loadu_si128(reinterpret_cast(input_bytes) + b); - M = _mm_shuffle_epi8(M, BSWAP_MASK); - - X = _mm_xor_si128(X, M); + const auto M = SIMD_2x64::load_le(input_bytes + b * 16).reverse_all_bytes(); + X ^= M; - __m128i Z = _mm_setzero_si128(); + SIMD_2x64 Z = {}; for(size_t i = 0; i != 64; i += 2) { - const __m128i HM0 = _mm_loadu_si128(HM_mm + 2 * i); - const __m128i HM1 = _mm_loadu_si128(HM_mm + 2 * i + 1); - const __m128i HM2 = _mm_loadu_si128(HM_mm + 2 * i + 2); - const __m128i HM3 = _mm_loadu_si128(HM_mm + 2 * i + 3); - - const __m128i XMASK1 = _mm_add_epi64(_mm_srli_epi64(X, 63), ones); - X = _mm_slli_epi64(X, 1); - const __m128i XMASK2 = _mm_add_epi64(_mm_srli_epi64(X, 63), ones); - X = _mm_slli_epi64(X, 1); - - Z = _mm_xor_si128(Z, _mm_andnot_si128(_mm_unpackhi_epi64(XMASK1, XMASK1), HM0)); - Z = _mm_xor_si128(Z, _mm_andnot_si128(_mm_unpacklo_epi64(XMASK1, XMASK1), HM1)); - Z = _mm_xor_si128(Z, _mm_andnot_si128(_mm_unpackhi_epi64(XMASK2, XMASK2), HM2)); - Z = _mm_xor_si128(Z, _mm_andnot_si128(_mm_unpacklo_epi64(XMASK2, XMASK2), HM3)); + const auto HM0 = SIMD_2x64::load_le(HM_mm + 2 * i); + const auto HM1 = SIMD_2x64::load_le(HM_mm + 2 * i + 1); + const auto HM2 = SIMD_2x64::load_le(HM_mm + 2 * i + 2); + const auto HM3 = SIMD_2x64::load_le(HM_mm + 2 * i + 3); + + const auto XMASK1 = X.shr<63>() + ones; + X = X.shl<1>(); + const auto XMASK2 = X.shr<63>() + ones; + X = X.shl<1>(); + + Z ^= SIMD_2x64::interleave_high(XMASK1, XMASK1).andc(HM0); + Z ^= SIMD_2x64::interleave_low(XMASK1, XMASK1).andc(HM1); + Z ^= SIMD_2x64::interleave_high(XMASK2, XMASK2).andc(HM2); + Z ^= SIMD_2x64::interleave_low(XMASK2, XMASK2).andc(HM3); } - X = _mm_shuffle_epi32(Z, _MM_SHUFFLE(1, 0, 3, 2)); + X = Z.swap_lanes(); } - X = _mm_shuffle_epi8(X, BSWAP_MASK); - _mm_storeu_si128(reinterpret_cast<__m128i*>(x), X); + X.reverse_all_bytes().store_le(x); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/ghash/ghash_vperm/info.txt botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash_vperm/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/ghash/ghash_vperm/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/ghash/ghash_vperm/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,13 +1,13 @@ - + GHASH_CLMUL_VPERM -> 20201002 - + name -> "GHASH Vector Permutations" brief -> "GHASH using Vector Permutation instructions" - -sse2 -ssse3 - + +cpuid +simd_2x64 + diff -Nru botan3-3.7.1+dfsg/src/lib/utils/ghash/info.txt botan3-3.12.0+dfsg/src/lib/utils/ghash/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/ghash/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/ghash/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + GHASH -> 20201002 - + name -> "GHASH" diff -Nru botan3-3.7.1+dfsg/src/lib/utils/http_util/http_util.cpp botan3-3.12.0+dfsg/src/lib/utils/http_util/http_util.cpp --- botan3-3.7.1+dfsg/src/lib/utils/http_util/http_util.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/http_util/http_util.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,12 +8,12 @@ #include -#include #include #include +#include #include #include -#include +#include #include namespace Botan::HTTP { @@ -42,14 +42,14 @@ } // Blocks until entire message has been written - socket->write(cast_char_ptr_to_uint8(message.data()), message.size()); + socket->write(as_span_of_bytes(message)); if(std::chrono::system_clock::now() - start_time > timeout) { throw HTTP_Error("Timeout during writing message body"); } std::ostringstream oss; - std::vector buf(BOTAN_DEFAULT_BUFFER_SIZE); + std::vector buf(DefaultBufferSize); while(true) { const size_t got = socket->read(buf.data(), buf.size()); if(got == 0) { // EOF @@ -82,6 +82,14 @@ return true; } +void check_no_crlf_nul(std::string_view field, std::string_view value) { + for(const char c : value) { + if(c == '\r' || c == '\n' || c == '\0') { + throw HTTP_Error(fmt("Invalid character in HTTP {}", field)); + } + } +} + } // namespace std::string url_encode(std::string_view in) { @@ -89,7 +97,8 @@ for(auto c : in) { if(needs_url_encoding(c)) { - out << '%' << hex_encode(cast_char_ptr_to_uint8(&c), 1); + out << '%' << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << static_cast(c); + out << std::dec << std::nouppercase; // reset flags } else { out << c; } @@ -125,14 +134,16 @@ const auto host_loc_sep = url.find('/', protocol_host_sep + 3); - std::string hostname, loc, service; + std::string hostname; + std::string loc; + std::string service; if(host_loc_sep == std::string::npos) { - hostname = url.substr(protocol_host_sep + 3, std::string::npos); + hostname = url.substr(protocol_host_sep + 3); loc = "/"; } else { hostname = url.substr(protocol_host_sep + 3, host_loc_sep - protocol_host_sep - 3); - loc = url.substr(host_loc_sep, std::string::npos); + loc = url.substr(host_loc_sep); } const auto port_sep = hostname.find(':'); @@ -144,6 +155,12 @@ hostname = hostname.substr(0, port_sep); } + check_no_crlf_nul("verb", verb); + check_no_crlf_nul("hostname", hostname); + check_no_crlf_nul("port", service); + check_no_crlf_nul("path", loc); + check_no_crlf_nul("content type", content_type); + std::ostringstream outbuf; outbuf << verb << " " << loc << " HTTP/1.0\r\n"; @@ -172,14 +189,14 @@ std::stringstream response_stream(line1); std::string http_version; - unsigned int status_code; + unsigned int status_code = 0; std::string status_message; response_stream >> http_version >> status_code; std::getline(response_stream, status_message); - if(!response_stream || http_version.substr(0, 5) != "HTTP/") { + if(!response_stream || !http_version.starts_with("HTTP/")) { throw HTTP_Error("Not an HTTP response"); } diff -Nru botan3-3.7.1+dfsg/src/lib/utils/http_util/info.txt botan3-3.12.0+dfsg/src/lib/utils/http_util/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/http_util/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/http_util/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + HTTP_UTIL -> 20171003 - + name -> "HTTP" diff -Nru botan3-3.7.1+dfsg/src/lib/utils/info.txt botan3-3.12.0+dfsg/src/lib/utils/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,7 +1,3 @@ - -UTIL_FUNCTIONS -> 20180903 - - name -> "Utilities" brief -> "Various utility functions and types" @@ -10,9 +6,9 @@ load_on always +allocator.h api.h assert.h -allocator.h compiler.h concepts.h data_src.h @@ -20,8 +16,10 @@ exceptn.h mem_ops.h mutex.h -types.h +# TODO(Botan4) move this to internal +range_concepts.h strong_type.h +types.h version.h @@ -29,25 +27,30 @@ alignment_buffer.h bit_ops.h bswap.h +buffer_slicer.h +buffer_stuffer.h calendar.h charset.h codec_base.h +concat_util.h ct_utils.h donna128.h filesystem.h fmt.h +isa_extn.h int_utils.h +gfni_utils.h loadstor.h mul128.h +mem_utils.h parsing.h prefetch.h rotate.h rounding.h scan_name.h +scoped_cleanup.h +stack_scrubbing.h stl_util.h time_utils.h +value_barrier.h - - -cpuid - diff -Nru botan3-3.7.1+dfsg/src/lib/utils/int_utils.h botan3-3.12.0+dfsg/src/lib/utils/int_utils.h --- botan3-3.7.1+dfsg/src/lib/utils/int_utils.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/int_utils.h 2026-05-07 01:38:28.000000000 +0000 @@ -49,7 +49,7 @@ // https://stackoverflow.com/questions/24795651 const T r = (1U * a) * b; // If a == 0 then the multiply certainly did not overflow - // Otherwise r / a == b unless overflow occured + // Otherwise r / a == b unless overflow occurred if(a != 0 && r / a != b) { return {}; } diff -Nru botan3-3.7.1+dfsg/src/lib/utils/ip_address/info.txt botan3-3.12.0+dfsg/src/lib/utils/ip_address/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/ip_address/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/ip_address/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,8 @@ + +name -> "IP address types" + + + +ipv4_address.h +ipv6_address.h + diff -Nru botan3-3.7.1+dfsg/src/lib/utils/ip_address/ipv4_address.cpp botan3-3.12.0+dfsg/src/lib/utils/ip_address/ipv4_address.cpp --- botan3-3.7.1+dfsg/src/lib/utils/ip_address/ipv4_address.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/ip_address/ipv4_address.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,131 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include + +namespace Botan { + +//static +std::optional IPv4Address::from_string(std::string_view str) { + if(auto ipv4 = string_to_ipv4(str)) { + return IPv4Address(*ipv4); + } else { + return {}; + } +} + +//static +IPv4Address IPv4Address::netmask(size_t bits) { + BOTAN_ARG_CHECK(bits <= 32, "IPv4 netmask prefix length must be at most 32"); + if(bits == 0) { + return IPv4Address(0); + } + return IPv4Address(0xFFFFFFFF << (32 - bits)); +} + +std::array IPv4Address::to_bytes() const { + std::array out{}; + store_be(m_ip, out); + return out; +} + +std::string IPv4Address::to_string() const { + return ipv4_to_string(m_ip); +} + +std::optional IPv4Address::prefix_length() const { + // A 32-bit mask m is a CIDR prefix iff (~m) + 1 is a power of two or zero, + // i.e. (~m) & (~m + 1) == 0. If so, the prefix length is the leading-one count. + const uint32_t inv = ~m_ip; + if((inv & (inv + 1)) != 0) { + return std::nullopt; + } + return std::countl_one(m_ip); +} + +IPv4Subnet::IPv4Subnet(IPv4Address address, size_t prefix_length) : + m_address(address & IPv4Address::netmask(prefix_length)), m_prefix_length(static_cast(prefix_length)) { + // IPv4Address::netmask validates prefix_length <= 32, so by this point + // the static_cast is in range. +} + +//static +std::optional IPv4Subnet::from_address_and_mask(uint32_t addr, uint32_t mask) { + std::array addr_and_mask{}; + store_be(&addr_and_mask[0], addr); // NOLINT(*-container-data-pointer) + store_be(&addr_and_mask[4], mask); + return IPv4Subnet::from_address_and_mask(addr_and_mask); +} + +//static +std::optional IPv4Subnet::from_address_and_mask(std::span addr_and_mask) { + const IPv4Address addr(load_be(addr_and_mask.data(), 0)); + const IPv4Address mask(load_be(addr_and_mask.data(), 1)); + + if(const auto plen = mask.prefix_length()) { + return IPv4Subnet(addr, *plen); + } else { + return {}; + } +} + +//static +std::optional IPv4Subnet::from_string(std::string_view str) { + const auto slash = str.find('/'); + if(slash == std::string_view::npos) { + return std::nullopt; + } + + auto addr = IPv4Address::from_string(str.substr(0, slash)); + if(!addr.has_value()) { + return std::nullopt; + } + + const auto plen_str = str.substr(slash + 1); + if(plen_str.empty() || plen_str.size() > 2) { + return std::nullopt; + } + size_t plen = 0; + for(const char c : plen_str) { + if(c < '0' || c > '9') { + return std::nullopt; + } + plen = plen * 10 + static_cast(c - '0'); + } + if(plen > 32) { + return std::nullopt; + } + + return IPv4Subnet(*addr, plen); +} + +bool IPv4Subnet::contains(const IPv4Address& ip) const { + return (ip & IPv4Address::netmask(m_prefix_length)) == m_address; +} + +std::string IPv4Subnet::to_string() const { + return fmt("{}/{}", m_address.to_string(), static_cast(m_prefix_length)); +} + +std::vector IPv4Subnet::serialize() const { + std::vector out; + if(is_host()) { + out.resize(4); + store_be(m_address.value(), out.data()); + return out; + } + out.resize(8); + store_be(m_address.value(), out.data()); + store_be(IPv4Address::netmask(m_prefix_length).value(), out.data() + 4); + return out; +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/ip_address/ipv4_address.h botan3-3.12.0+dfsg/src/lib/utils/ip_address/ipv4_address.h --- botan3-3.7.1+dfsg/src/lib/utils/ip_address/ipv4_address.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/ip_address/ipv4_address.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,133 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_IPV4_ADDRESS_H_ +#define BOTAN_IPV4_ADDRESS_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace Botan { + +/** +* IPv4 Address +*/ +class BOTAN_PUBLIC_API(3, 12) IPv4Address final { + public: + explicit IPv4Address(uint32_t ip) : m_ip(ip) {} + + static std::optional from_string(std::string_view str); + + /** + * Return an address with the leading @p bits set to one and the remainder + * zero. Throws Invalid_Argument if @p bits > 32. + */ + static IPv4Address netmask(size_t bits); + + static IPv4Address host_mask() { return netmask(32); } + + IPv4Address operator&(const IPv4Address& other) const { return IPv4Address(m_ip & other.m_ip); } + + auto operator<=>(const IPv4Address&) const = default; + + /// The address as a 32-bit big-endian integer + uint32_t value() const { return m_ip; } + + /// The address as four bytes, network-byte-order. + std::array to_bytes() const; + + /// Dotted-decimal form, e.g. "10.0.0.1". + std::string to_string() const; + + /** + * If this value is a netmask consisting of a run of one bits followed by + * a run of zero bits, return the number of one bits. + * + * Otherwise return nullopt. + */ + std::optional prefix_length() const; + + private: + uint32_t m_ip; +}; + +/** +* An IPv4 subnet in CIDR form: a network address paired with a prefix length +*/ +class BOTAN_PUBLIC_API(3, 12) IPv4Subnet final { + public: + /** + * Construct from a network address and a prefix length in [0, 32]. + * Host bits of @p address are cleared. + * + * Throws Invalid_Argument if @p prefix_length > 32. + */ + IPv4Subnet(IPv4Address address, size_t prefix_length); + + /** + * Construct from a network address and a netmask (4 bytes each) + * Returns nullopt if netmask is not a valid contiguous CIDR prefix. + */ + static std::optional from_address_and_mask(std::span addr_and_mask); + + /** + * Construct from a network address and a netmask (4 bytes each) + * Returns nullopt if netmask is not a valid contiguous CIDR prefix. + */ + static std::optional from_address_and_mask(uint32_t addr, uint32_t mask); + + /** + * Parse CIDR-style form "10.0.0.0/8". + * + * The "/N" suffix is required: bare addresses should be parsed via + * IPv4Address::from_string and wrapped with IPv4Subnet::host if needed. + * + * Returns nullopt on parse failure or out-of-range prefix length. + */ + static std::optional from_string(std::string_view str); + + /** + * A single-host subnet (prefix length 32) covering exactly @p address. + */ + static IPv4Subnet host(IPv4Address address) { return IPv4Subnet(address, 32); } + + /// The network address (host bits already zeroed). + const IPv4Address& address() const { return m_address; } + + /// Prefix length in [0, 32]. + size_t prefix_length() const { return m_prefix_length; } + + /// True iff prefix_length() == 32. + bool is_host() const { return m_prefix_length == 32; } + + /// True iff @p ip falls within this subnet. + bool contains(const IPv4Address& ip) const; + + /// CIDR-style "10.0.0.0/8". + std::string to_string() const; + + /** + * Bytes for use in a DER-encoded GeneralName iPAddress field: + * - 4 bytes (the address) if is_host() — SAN form per RFC 5280 4.2.1.6. + * - 8 bytes (address || netmask) otherwise — name constraint form per RFC 5280 4.2.1.10. + */ + std::vector serialize() const; + + friend bool operator==(const IPv4Subnet&, const IPv4Subnet&) = default; + + private: + IPv4Address m_address; + uint8_t m_prefix_length; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/ip_address/ipv6_address.cpp botan3-3.12.0+dfsg/src/lib/utils/ip_address/ipv6_address.cpp --- botan3-3.7.1+dfsg/src/lib/utils/ip_address/ipv6_address.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/ip_address/ipv6_address.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,164 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include + +namespace Botan { + +IPv6Address::IPv6Address(std::span ip) : m_ip{} { + for(size_t i = 0; i != 16; ++i) { + m_ip[i] = ip[i]; + } +} + +//static +std::optional IPv6Address::from_string(std::string_view str) { + if(auto ipv6 = string_to_ipv6(str)) { + return IPv6Address(*ipv6); + } else { + return {}; + } +} + +//static +IPv6Address IPv6Address::netmask(size_t bits) { + BOTAN_ARG_CHECK(bits <= 128, "IPv6 netmask prefix length must be at most 128"); + + const size_t full_bytes = bits / 8; + const size_t leftover = bits % 8; + + std::array m{}; + for(size_t i = 0; i != full_bytes; ++i) { + m[i] = 0xFF; + } + + if(leftover > 0) { + m[full_bytes] = static_cast(0xFF << (8 - leftover)); + } + + return IPv6Address(m); +} + +std::string IPv6Address::to_string() const { + return ipv6_to_string(m_ip); +} + +IPv6Address IPv6Address::operator&(const IPv6Address& other) const { + std::array masked{}; + for(size_t i = 0; i != 16; ++i) { + masked[i] = m_ip[i] & other.m_ip[i]; + } + return IPv6Address(masked); +} + +std::optional IPv6Address::prefix_length() const { + // Count leading one bits, stopping at the first byte that isn't fully set. + size_t leading = 0; + for(size_t i = 0; i != 16; ++i) { + const size_t hw = (m_ip[i] == 0xFF) ? 8 : std::countl_one(m_ip[i]); + leading += hw; + if(hw != 8) { + break; + } + } + + // Verify this is exactly equal to a netmask of that size + if(*this != netmask(leading)) { + return std::nullopt; + } + return leading; +} + +std::optional IPv6Address::as_ipv4() const { + const uint32_t ip0 = load_be(m_ip.data(), 0); + const uint32_t ip1 = load_be(m_ip.data(), 1); + const uint32_t ip2 = load_be(m_ip.data(), 2); + const uint32_t ip3 = load_be(m_ip.data(), 3); + + if(ip0 == 0x00000000 && ip1 == 0x00000000 && (ip2 == 0x00000000 || ip2 == 0x0000FFFF)) { + return IPv4Address(ip3); + } else { + return {}; + } +} + +IPv6Subnet::IPv6Subnet(IPv6Address address, size_t prefix_length) : + m_address(address & IPv6Address::netmask(prefix_length)), m_prefix_length(static_cast(prefix_length)) { + // IPv6Address::netmask validates prefix_length <= 128, so by this point + // the static_cast is in range. +} + +//static +std::optional IPv6Subnet::from_address_and_mask(std::span addr_and_mask) { + const auto addr = IPv6Address(addr_and_mask.first<16>()); + const auto mask = IPv6Address(addr_and_mask.last<16>()); + + if(const auto plen = mask.prefix_length()) { + return IPv6Subnet(addr, *plen); + } else { + return {}; + } +} + +//static +std::optional IPv6Subnet::from_string(std::string_view str) { + const auto slash = str.find('/'); + if(slash == std::string_view::npos) { + return std::nullopt; + } + + auto addr = IPv6Address::from_string(str.substr(0, slash)); + if(!addr.has_value()) { + return std::nullopt; + } + + // Parse the prefix length as a decimal integer in [0, 128]. + const auto plen_str = str.substr(slash + 1); + if(plen_str.empty() || plen_str.size() > 3) { + return std::nullopt; + } + size_t plen = 0; + for(const char c : plen_str) { + if(c < '0' || c > '9') { + return std::nullopt; + } + plen = plen * 10 + static_cast(c - '0'); + } + if(plen > 128) { + return std::nullopt; + } + + return IPv6Subnet(*addr, plen); +} + +bool IPv6Subnet::contains(const IPv6Address& ip) const { + return (ip & IPv6Address::netmask(m_prefix_length)) == m_address; +} + +std::string IPv6Subnet::to_string() const { + return fmt("{}/{}", m_address.to_string(), static_cast(m_prefix_length)); +} + +std::vector IPv6Subnet::serialize() const { + const auto addr = m_address.address(); + if(is_host()) { + return std::vector(addr.begin(), addr.end()); + } + const auto mask = IPv6Address::netmask(m_prefix_length).address(); + std::vector out; + out.reserve(32); + out.insert(out.end(), addr.begin(), addr.end()); + out.insert(out.end(), mask.begin(), mask.end()); + return out; +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/ip_address/ipv6_address.h botan3-3.12.0+dfsg/src/lib/utils/ip_address/ipv6_address.h --- botan3-3.7.1+dfsg/src/lib/utils/ip_address/ipv6_address.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/ip_address/ipv6_address.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,134 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_IPV6_ADDRESS_H_ +#define BOTAN_IPV6_ADDRESS_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace Botan { + +class IPv4Address; + +/** +* IPv6 Address +*/ +class BOTAN_PUBLIC_API(3, 12) IPv6Address final { + public: + explicit IPv6Address(std::span ip); + + explicit IPv6Address(std::array ip) : m_ip(ip) {} + + static std::optional from_string(std::string_view str); + + /** + * Return an address with the leading @p bits set to one and the remainder + * zero. Throws Invalid_Argument if @p bits > 128. + */ + static IPv6Address netmask(size_t bits); + + static IPv6Address host_mask() { return netmask(128); } + + IPv6Address operator&(const IPv6Address& other) const; + + auto operator<=>(const IPv6Address&) const = default; + + std::span address() const { return m_ip; } + + std::string to_string() const; + + /** + * If this value is a netmask consisting of a run of one bits followed by + * a run of zero bits, return the number of one bits. + * + * Otherwise return nullopt. + */ + std::optional prefix_length() const; + + /** + * If this IPv6 address is an IPv4-compatible IPv6 address (RFC 4291 2.5.5.1) + * or an IPv4-mapped IPv6 address (RFC 4291 2.5.5.2), return the embedded + * IPv4 address. + */ + std::optional as_ipv4() const; + + private: + std::array m_ip; +}; + +/** +* An IPv6 subnet in CIDR form: a network address paired with a prefix length +*/ +class BOTAN_PUBLIC_API(3, 12) IPv6Subnet final { + public: + /** + * Construct from a network address and a prefix length in [0, 128]. + * Host bits of @p address are cleared. + * + * Throws Invalid_Argument if @p prefix_length > 128. + */ + IPv6Subnet(IPv6Address address, size_t prefix_length); + + /** + * Construct from a network address and a 16-byte CIDR netmask. + * Returns nullopt if netmask is not a valid contiguous CIDR prefix. + */ + static std::optional from_address_and_mask(std::span addr_and_mask); + + /** + * Parse the CIDR-style form "2001:db8::/32". + * + * The "/N" suffix is required: bare addresses should be parsed via + * IPv6Address::from_string and wrapped with IPv6Subnet::host if needed. + * + * Returns nullopt on parse failure or out-of-range prefix length. + */ + static std::optional from_string(std::string_view str); + + /** + * A single-host subnet (prefix length 128) covering exactly @p address. + */ + static IPv6Subnet host(IPv6Address address) { return IPv6Subnet(address, 128); } + + /// The network address (host bits already zeroed). + const IPv6Address& address() const { return m_address; } + + /// Prefix length in [0, 128]. + size_t prefix_length() const { return m_prefix_length; } + + /// True iff prefix_length() == 128. + bool is_host() const { return m_prefix_length == 128; } + + /// True iff @p ip falls within this subnet. + bool contains(const IPv6Address& ip) const; + + /// CIDR-style "2001:db8::/32". + std::string to_string() const; + + /** + * Bytes for use in a DER-encoded GeneralName iPAddress field: + * - 16 bytes (the address) if is_host(); the SAN form per RFC 5280 4.2.1.6. + * - 32 bytes (address || netmask) otherwise; the name constraint form + * per RFC 5280 4.2.1.10. + */ + std::vector serialize() const; + + friend bool operator==(const IPv6Subnet&, const IPv6Subnet&) = default; + + private: + IPv6Address m_address; + uint8_t m_prefix_length; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/isa_extn.h botan3-3.12.0+dfsg/src/lib/utils/isa_extn.h --- botan3-3.7.1+dfsg/src/lib/utils/isa_extn.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/isa_extn.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,91 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_UTIL_ISA_EXTN_H_ +#define BOTAN_UTIL_ISA_EXTN_H_ + +#include +#include + +/* +* GCC and Clang use string identifiers to tag ISA extensions (eg using the +* `target` function attribute). +* +* This file consolidates the actual definition of such target attributes +*/ + +#if defined(BOTAN_TARGET_ARCH_IS_X86_FAMILY) + + #define BOTAN_FN_ISA_SIMD_4X32 BOTAN_FUNC_ISA("ssse3") + #define BOTAN_FN_ISA_SIMD_2X64 BOTAN_FUNC_ISA("ssse3") + #define BOTAN_FN_ISA_SIMD_4X64 BOTAN_FUNC_ISA("avx2") + #define BOTAN_FN_ISA_SIMD_8X64 BOTAN_FN_ISA_AVX512 + #define BOTAN_FN_ISA_CLMUL BOTAN_FUNC_ISA("pclmul,ssse3") + #define BOTAN_FN_ISA_AESNI BOTAN_FUNC_ISA("aes,ssse3") + #define BOTAN_FN_ISA_SHANI BOTAN_FUNC_ISA("sha,ssse3,sse4.1") + #define BOTAN_FN_ISA_SHA512 BOTAN_FUNC_ISA("sha512,avx2") + #define BOTAN_FN_ISA_BMI2 BOTAN_FUNC_ISA("bmi,bmi2") + #define BOTAN_FN_ISA_RNG BOTAN_FUNC_ISA("rdrnd") + #define BOTAN_FN_ISA_SSE2 BOTAN_FUNC_ISA("sse2") + #define BOTAN_FN_ISA_AVX2 BOTAN_FUNC_ISA("avx2") + #define BOTAN_FN_ISA_AVX2_BMI2 BOTAN_FUNC_ISA("avx2,bmi,bmi2") + #define BOTAN_FN_ISA_AVX2_GFNI BOTAN_FUNC_ISA("avx2,gfni") + #define BOTAN_FN_ISA_AVX2_VAES BOTAN_FUNC_ISA("vaes,avx2") + #define BOTAN_FN_ISA_AVX2_SM3 BOTAN_FUNC_ISA("sm3,avx2") + #define BOTAN_FN_ISA_AVX2_SM4 BOTAN_FUNC_ISA("sm4,avx2") + #define BOTAN_FN_ISA_AVX512 \ + BOTAN_FUNC_ISA("avx512f,avx512dq,avx512bw,avx512vl,avx512vbmi,avx512vbmi2,avx512bitalg,avx512ifma") + #define BOTAN_FN_ISA_AVX512_CLMUL \ + BOTAN_FUNC_ISA("avx512f,avx512dq,avx512bw,avx512vl,avx512vbmi,avx512vbmi2,avx512bitalg,pclmul,vpclmulqdq") + #define BOTAN_FN_ISA_AVX512_BMI2 \ + BOTAN_FUNC_ISA("avx512f,avx512dq,avx512bw,avx512vl,avx512vbmi,avx512vbmi2,avx512bitalg,avx512ifma,bmi,bmi2") + #define BOTAN_FN_ISA_AVX512_GFNI \ + BOTAN_FUNC_ISA("avx512f,avx512dq,avx512bw,avx512vl,avx512vbmi,avx512vbmi2,avx512bitalg,avx512ifma,gfni") + + #define BOTAN_FN_ISA_HWAES BOTAN_FN_ISA_AESNI +#endif + +#if defined(BOTAN_TARGET_ARCH_IS_ARM64) + + #define BOTAN_FN_ISA_SIMD_4X32 BOTAN_FUNC_ISA("+simd") + #define BOTAN_FN_ISA_CLMUL BOTAN_FUNC_ISA("+crypto+aes") + #define BOTAN_FN_ISA_AES BOTAN_FUNC_ISA("+crypto+aes") + #define BOTAN_FN_ISA_SHA2 BOTAN_FUNC_ISA("+crypto+sha2") + #define BOTAN_FN_ISA_SM3 BOTAN_FUNC_ISA("arch=armv8.2-a+sm4") + #define BOTAN_FN_ISA_SM4 BOTAN_FUNC_ISA("arch=armv8.2-a+sm4") + #define BOTAN_FN_ISA_SHA512 BOTAN_FUNC_ISA("arch=armv8.2-a+sha3") + + #define BOTAN_FN_ISA_HWAES BOTAN_FN_ISA_AES +#endif + +#if defined(BOTAN_TARGET_ARCH_IS_ARM32) + #define BOTAN_FN_ISA_SIMD_4X32 BOTAN_FUNC_ISA("fpu=neon") +#endif + +#if defined(BOTAN_TARGET_ARCH_IS_PPC_FAMILY) + + #define BOTAN_FN_ISA_SIMD_4X32 BOTAN_FUNC_ISA("altivec") + #define BOTAN_FN_ISA_CLMUL BOTAN_FUNC_ISA("vsx,crypto") + #define BOTAN_FN_ISA_AES BOTAN_FUNC_ISA("vsx,crypto") + #define BOTAN_FN_ISA_RNG BOTAN_FUNC_ISA("cpu=power9") + + #define BOTAN_FN_ISA_HWAES BOTAN_FN_ISA_AES +#endif + +#if defined(BOTAN_TARGET_ARCH_IS_LOONGARCH64) + + #define BOTAN_FN_ISA_SIMD_4X32 BOTAN_FUNC_ISA("lsx") + +#endif + +#if defined(BOTAN_TARGET_ARCH_IS_WASM) + + #define BOTAN_FN_ISA_SIMD_4X32 BOTAN_FUNC_ISA("simd128") + #define BOTAN_FN_ISA_SIMD_2X64 BOTAN_FUNC_ISA("simd128") + +#endif + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/loadstor.h botan3-3.12.0+dfsg/src/lib/utils/loadstor.h --- botan3-3.7.1+dfsg/src/lib/utils/loadstor.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/loadstor.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,11 +10,12 @@ #ifndef BOTAN_LOAD_STORE_H_ #define BOTAN_LOAD_STORE_H_ -#include #include +#include #include #include #include +#include /** * @file loadstor.h @@ -55,6 +56,9 @@ namespace Botan { +static_assert(std::endian::native == std::endian::big || std::endian::native == std::endian::little, + "Mixed endian systems are not supported"); + /** * Byte extraction * @param byte_num which byte to extract, 0 == highest byte @@ -123,48 +127,20 @@ namespace detail { -enum class Endianness : bool { - Big, - Little, -}; - /** - * @warning This function may return false if the native endianness is unknown - * @returns true iff the native endianness matches the given endianness + * @returns the opposite endianness of the specified endianness + * + * Note this assumes that there are only two endian orderings; we + * do not supported mixed endian systems */ -constexpr bool is_native(Endianness endianness) { -#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) - return endianness == Endianness::Big; -#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) - return endianness == Endianness::Little; -#else - BOTAN_UNUSED(endianness); - return false; -#endif -} - -/** - * @warning This function may return false if the native endianness is unknown - * @returns true iff the native endianness does not match the given endianness - */ -constexpr bool is_opposite(Endianness endianness) { -#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) - return endianness == Endianness::Little; -#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) - return endianness == Endianness::Big; -#else - BOTAN_UNUSED(endianness); - return false; -#endif -} - -template -constexpr bool native_endianness_is_unknown() { -#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) || defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) - return false; -#else - return true; -#endif +consteval std::endian opposite(std::endian endianness) { + if(endianness == std::endian::big) { + return std::endian::little; + } else { + // We already verified via static assert earlier in this file that we are + // running on either a big endian or little endian system + return std::endian::big; + } } /** @@ -231,19 +207,19 @@ /** * Manually load a word from a range in either big or little endian byte order. - * This will be used only if the endianness of the target platform is unknown at - * compile time. + * + * This is only used at compile time. */ -template InR> -inline constexpr OutT fallback_load_any(InR&& in_range) { +template InR> +inline constexpr OutT fallback_load_any(const InR& in_range) { std::span in{in_range}; // clang-format off - if constexpr(endianness == Endianness::Big) { + if constexpr(endianness == std::endian::big) { return [&](std::index_sequence) { return static_cast(((static_cast(in[i]) << ((sizeof(OutT) - i - 1) * 8)) | ...)); } (std::make_index_sequence()); } else { - static_assert(endianness == Endianness::Little); + static_assert(endianness == std::endian::little); return [&](std::index_sequence) { return static_cast(((static_cast(in[i]) << (i * 8)) | ...)); } (std::make_index_sequence()); @@ -253,19 +229,19 @@ /** * Manually store a word into a range in either big or little endian byte order. - * This will be used only if the endianness of the target platform is unknown at - * compile time. + * + * This will be used only at compile time. */ -template OutR> -inline constexpr void fallback_store_any(InT in, OutR&& out_range) { +template OutR> +inline constexpr void fallback_store_any(InT in, OutR&& out_range /* NOLINT(*-std-forward) */) { std::span out{out_range}; // clang-format off - if constexpr(endianness == Endianness::Big) { + if constexpr(endianness == std::endian::big) { [&](std::index_sequence) { ((out[i] = get_byte(in)), ...); } (std::make_index_sequence()); } else { - static_assert(endianness == Endianness::Little); + static_assert(endianness == std::endian::little); [&](std::index_sequence) { ((out[i] = get_byte(in)), ...); } (std::make_index_sequence()); @@ -281,7 +257,7 @@ * * Template arguments of all overloads of load_any() share the same semantics: * - * 1. Endianness Either `Endianness::Big` or `Endianness::Little`, that + * 1. std::endian Either `std::endian::big` or `std::endian::little`, that * will eventually select the byte order translation mode * implemented in this base function. * @@ -297,7 +273,7 @@ * @param in_range a fixed-length byte range * @return T loaded from @p in_range, as a big-endian value */ -template InR> +template InR> requires(!custom_loadable>) inline constexpr WrappedOutT load_any(InR&& in_range) { using OutT = detail::wrapped_type; @@ -310,16 +286,14 @@ if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ { return fallback_load_any(std::forward(in_range)); } else { - std::span in{in_range}; + const std::span in{in_range}; if constexpr(sizeof(OutT) == 1) { return static_cast(in[0]); - } else if constexpr(is_native(endianness)) { + } else if constexpr(endianness == std::endian::native) { return typecast_copy(in); - } else if constexpr(is_opposite(endianness)) { - return reverse_bytes(typecast_copy(in)); } else { - static_assert(native_endianness_is_unknown()); - return fallback_load_any(std::forward(in_range)); + static_assert(opposite(endianness) == std::endian::native); + return reverse_bytes(typecast_copy(in)); } } }()); @@ -334,13 +308,13 @@ * @param in_range a fixed-length byte range * @return T loaded from @p in_range, as a big-endian value */ -template InR> +template InR> requires(custom_loadable>) -inline constexpr WrappedOutT load_any(InR&& in_range) { +inline constexpr WrappedOutT load_any(const InR& in_range) { using OutT = detail::wrapped_type; ranges::assert_exact_byte_length(in_range); - std::span ins{in_range}; - if constexpr(endianness == Endianness::Big) { + const std::span ins{in_range}; + if constexpr(endianness == std::endian::big) { return wrap_strong_type(OutT::load_be(ins)); } else { return wrap_strong_type(OutT::load_le(ins)); @@ -352,10 +326,10 @@ * @param in a fixed-length span to some bytes * @param outs a arbitrary-length parameter list of unsigned integers to be loaded */ -template InR, unsigned_integralish... Ts> +template InR, unsigned_integralish... Ts> requires(sizeof...(Ts) > 0) && ((std::same_as && all_same_v) || (unsigned_integralish && all_same_v)) -inline constexpr void load_any(InR&& in, Ts&... outs) { +inline constexpr void load_any(const InR& in, Ts&... outs) { ranges::assert_exact_byte_length<(sizeof(Ts) + ...)>(in); auto load_one = [off = 0](auto i, T& o) mutable { o = load_any(i.subspan(off).template first()); @@ -372,13 +346,13 @@ * @param out the output range of words * @param in the input range of bytes */ -template InR> requires(unsigned_integralish> && (std::same_as || std::same_as>)) -inline constexpr void load_any(OutR&& out, InR&& in) { +inline constexpr void load_any(OutR&& out /* NOLINT(*-std-forward) */, const InR& in) { ranges::assert_equal_byte_lengths(out, in); using element_type = std::ranges::range_value_t; @@ -397,7 +371,7 @@ if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ { load_elementwise(); } else { - if constexpr(is_native(endianness) && !custom_loadable) { + if constexpr(endianness == std::endian::native && !custom_loadable) { typecast_copy(out, in); } else { load_elementwise(); @@ -416,7 +390,7 @@ * @param in_range a statically-sized range with some bytes * @return T loaded from in */ -template InR> +template InR> requires(std::same_as || ((ranges::statically_spanable_range || concepts::resizable_container) && unsigned_integralish)) @@ -476,7 +450,7 @@ * @param off an offset into the array * @return off'th T of in, as a big-endian value */ -template +template inline constexpr OutT load_any(const uint8_t in[], size_t off) { // asserts that *in points to enough bytes to read at offset off constexpr size_t out_size = sizeof(OutT); @@ -488,7 +462,7 @@ * @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 && ((std::same_as && all_same_v) || (unsigned_integralish && all_same_v))) @@ -504,7 +478,7 @@ * @param in the input array of bytes * @param count how many words are in in */ -template +template requires(std::same_as || std::same_as) inline constexpr void load_any(T out[], const uint8_t in[], size_t count) { // asserts that *in and *out point to the correct amount of memory @@ -519,7 +493,7 @@ */ template inline constexpr auto load_le(ParamTs&&... params) { - return detail::load_any(std::forward(params)...); + return detail::load_any(std::forward(params)...); } /** @@ -528,7 +502,7 @@ */ template inline constexpr auto load_be(ParamTs&&... params) { - return detail::load_any(std::forward(params)...); + return detail::load_any(std::forward(params)...); } namespace detail { @@ -546,13 +520,13 @@ * @param wrapped_in an unsigned integral to be stored * @param out_range a byte range to store the word into */ -template OutR> +template OutR> requires(!custom_storable>) inline constexpr void store_any(WrappedInT wrapped_in, OutR&& out_range) { const auto in = detail::unwrap_strong_type_or_enum(wrapped_in); using InT = decltype(in); ranges::assert_exact_byte_length(out_range); - std::span out{out_range}; + const std::span out{out_range}; // At compile time we cannot use `typecast_copy` as it uses `std::memcpy` // internally to copy ranges on a byte-by-byte basis, which is not allowed @@ -562,13 +536,11 @@ } else { if constexpr(sizeof(InT) == 1) { out[0] = static_cast(in); - } else if constexpr(is_native(endianness)) { + } else if constexpr(endianness == std::endian::native) { typecast_copy(out, in); - } else if constexpr(is_opposite(endianness)) { - typecast_copy(out, reverse_bytes(in)); } else { - static_assert(native_endianness_is_unknown()); - return fallback_store_any(in, std::forward(out_range)); + static_assert(opposite(endianness) == std::endian::native); + typecast_copy(out, reverse_bytes(in)); } } } @@ -582,14 +554,14 @@ * @param wrapped_in a custom object to be stored * @param out_range a byte range to store the word into */ -template OutR> +template OutR> requires(custom_storable>) -inline constexpr void store_any(WrappedInT wrapped_in, OutR&& out_range) { +inline constexpr void store_any(WrappedInT wrapped_in, const OutR& out_range) { const auto in = detail::unwrap_strong_type_or_enum(wrapped_in); using InT = decltype(in); ranges::assert_exact_byte_length(out_range); - std::span outs{out_range}; - if constexpr(endianness == Endianness::Big) { + const std::span outs{out_range}; + if constexpr(endianness == std::endian::big) { in.store_be(outs); } else { in.store_le(outs); @@ -601,13 +573,13 @@ * @param out a sized range of some bytes * @param ins a arbitrary-length parameter list of unsigned integers to be stored */ -template OutR, unsigned_integralish... Ts> requires(sizeof...(Ts) > 0) && ((std::same_as && all_same_v) || (unsigned_integralish && all_same_v)) -inline constexpr void store_any(OutR&& out, Ts... ins) { +inline constexpr void store_any(OutR&& out /* NOLINT(*-std-forward) */, Ts... ins) { ranges::assert_exact_byte_length<(sizeof(Ts) + ...)>(out); auto store_one = [off = 0](auto o, T i) mutable { store_any(i, o.subspan(off).template first()); @@ -623,12 +595,12 @@ * @param out the output range of bytes * @param in the input range of words */ -template OutR, ranges::spanable_range InR> requires(std::same_as || std::same_as>) -inline constexpr void store_any(OutR&& out, InR&& in) { +inline constexpr void store_any(OutR&& out /* NOLINT(*-std-forward) */, const InR& in) { ranges::assert_equal_byte_lengths(out, in); using element_type = std::ranges::range_value_t; @@ -647,7 +619,7 @@ if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ { store_elementwise(); } else { - if constexpr(is_native(endianness) && !custom_storable) { + if constexpr(endianness == std::endian::native && !custom_storable) { typecast_copy(out, in); } else { store_elementwise(); @@ -669,7 +641,7 @@ * @param in an unsigned integer to be stored * @param out_range a range of bytes to store the word into */ -template OutR> +template OutR> requires std::same_as inline constexpr void store_any(T in, OutR&& out_range) { store_any(in, std::forward(out_range)); @@ -684,7 +656,7 @@ * @param in_range a range of words that should be stored * @return a container of bytes that contains the stored words */ -template +template requires(std::same_as || (ranges::statically_spanable_range && std::default_initializable) || concepts::resizable_byte_buffer) @@ -719,7 +691,7 @@ * @param ins some words that should be stored * @return a container of bytes that contains the stored words */ -template +template requires all_same_v inline constexpr auto store_any(Ts... ins) { return store_any(std::array{ins...}); @@ -734,7 +706,7 @@ * @param in the input unsigned integer * @param out the byte array to write to */ -template +template requires(std::same_as || std::same_as) inline constexpr void store_any(T in, uint8_t out[]) { // asserts that *out points to enough bytes to write into @@ -746,7 +718,7 @@ * @param ins a arbitrary-length parameter list of unsigned integers to be stored * @param out the byte array to write to */ -template +template requires(std::same_as || std::same_as) && all_same_v inline constexpr void store_any(uint8_t out[], T0 in0, Ts... ins) { constexpr auto bytes = sizeof(in0) + (sizeof(ins) + ... + 0); @@ -762,7 +734,7 @@ */ template inline constexpr auto store_le(ParamTs&&... params) { - return detail::store_any(std::forward(params)...); + return detail::store_any(std::forward(params)...); } /** @@ -771,12 +743,12 @@ */ template inline constexpr auto store_be(ParamTs&&... params) { - return detail::store_any(std::forward(params)...); + return detail::store_any(std::forward(params)...); } namespace detail { -template +template inline size_t copy_out_any_word_aligned_portion(std::span& out, std::span& in) { const size_t full_words = out.size() / sizeof(T); const size_t full_word_bytes = full_words * sizeof(T); @@ -798,10 +770,10 @@ * byte order. */ template -inline void copy_out_be(std::span out, InR&& in) { +inline void copy_out_be(std::span out, const InR& in) { using T = std::ranges::range_value_t; std::span in_s{in}; - const auto remaining_bytes = detail::copy_out_any_word_aligned_portion(out, in_s); + const auto remaining_bytes = detail::copy_out_any_word_aligned_portion(out, in_s); // copy remaining bytes as a partial word for(size_t i = 0; i < remaining_bytes; ++i) { @@ -814,10 +786,10 @@ * byte order. */ template -inline void copy_out_le(std::span out, InR&& in) { +inline void copy_out_le(std::span out, const InR& in) { using T = std::ranges::range_value_t; std::span in_s{in}; - const auto remaining_bytes = detail::copy_out_any_word_aligned_portion(out, in_s); + const auto remaining_bytes = detail::copy_out_any_word_aligned_portion(out, in_s); // copy remaining bytes as a partial word for(size_t i = 0; i < remaining_bytes; ++i) { diff -Nru botan3-3.7.1+dfsg/src/lib/utils/locking_allocator/info.txt botan3-3.12.0+dfsg/src/lib/utils/locking_allocator/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/locking_allocator/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/locking_allocator/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + LOCKING_ALLOCATOR -> 20131128 - + name -> "Locking Allocator" diff -Nru botan3-3.7.1+dfsg/src/lib/utils/locking_allocator/locking_allocator.cpp botan3-3.12.0+dfsg/src/lib/utils/locking_allocator/locking_allocator.cpp --- botan3-3.7.1+dfsg/src/lib/utils/locking_allocator/locking_allocator.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/locking_allocator/locking_allocator.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -20,7 +20,7 @@ } if(auto n = checked_mul(num_elems, elem_size)) { - return m_pool->allocate(n.value()); + return m_pool->allocate(*n); } else { // overflow! return nullptr; @@ -33,7 +33,7 @@ } if(auto n = checked_mul(num_elems, elem_size)) { - return m_pool->deallocate(p, n.value()); + return m_pool->deallocate(p, *n); } else { /* We return nullptr in allocate if there was an overflow, so if an @@ -43,7 +43,7 @@ } } -mlock_allocator::mlock_allocator() { +mlock_allocator::mlock_allocator() noexcept { const size_t mem_to_lock = OS::get_memory_locking_limit(); const size_t page_size = OS::system_page_size(); @@ -71,7 +71,7 @@ } // namespace -mlock_allocator& mlock_allocator::instance() { +mlock_allocator& mlock_allocator::instance() noexcept { return g_mlock_allocator; } diff -Nru botan3-3.7.1+dfsg/src/lib/utils/locking_allocator/locking_allocator.h botan3-3.12.0+dfsg/src/lib/utils/locking_allocator/locking_allocator.h --- botan3-3.7.1+dfsg/src/lib/utils/locking_allocator/locking_allocator.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/locking_allocator/locking_allocator.h 2026-05-07 01:38:28.000000000 +0000 @@ -18,17 +18,18 @@ class mlock_allocator final { public: - static mlock_allocator& instance(); + static mlock_allocator& instance() noexcept; void* allocate(size_t num_elems, size_t elem_size); bool deallocate(void* p, size_t num_elems, size_t elem_size) noexcept; mlock_allocator(const mlock_allocator&) = delete; - + mlock_allocator(mlock_allocator&&) = delete; mlock_allocator& operator=(const mlock_allocator&) = delete; + mlock_allocator& operator=(mlock_allocator&&) = delete; - mlock_allocator(); + mlock_allocator() noexcept; ~mlock_allocator(); diff -Nru botan3-3.7.1+dfsg/src/lib/utils/mem_ops.h botan3-3.12.0+dfsg/src/lib/utils/mem_ops.h --- botan3-3.7.1+dfsg/src/lib/utils/mem_ops.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/mem_ops.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,7 +9,7 @@ #define BOTAN_MEMORY_OPS_H_ #include -#include +#include #include #include #include @@ -18,6 +18,8 @@ #include #include +BOTAN_FUTURE_INTERNAL_HEADER(mem_ops.h) + /* The header mem_ops.h previously included the contents of allocator.h @@ -49,9 +51,8 @@ BOTAN_PUBLIC_API(2, 0) void secure_scrub_memory(void* ptr, size_t n); /** -* Scrub memory contents in a way that a compiler should not elide, -* using some system specific technique. Note that this function might -* not zero the memory. +* Zero memory contents in a way that a compiler should not elide, +* using some system specific technique. * * @param data the data region to be scrubbed */ @@ -59,8 +60,6 @@ secure_scrub_memory(std::ranges::data(data), ranges::size_bytes(data)); } -#if !defined(BOTAN_IS_BEGIN_BUILT) - /** * Memory comparison, input insensitive * @param x a pointer to an array @@ -71,8 +70,6 @@ BOTAN_DEPRECATED("This function is deprecated, use constant_time_compare()") BOTAN_PUBLIC_API(2, 9) uint8_t ct_compare_u8(const uint8_t x[], const uint8_t y[], size_t len); -#endif - /** * Memory comparison, input insensitive * @param x a range of bytes @@ -130,7 +127,7 @@ * @param mem a contiguous range of Ts to zero */ template -inline constexpr void clear_mem(R&& mem) +inline constexpr void clear_mem(R&& mem) // NOLINT(*-missing-std-forward) requires std::is_trivially_copyable_v> { clear_bytes(std::ranges::data(mem), ranges::size_bytes(mem)); @@ -143,7 +140,7 @@ * @param n the number of elements of in/out */ template - requires std::is_trivial::type>::value + requires std::is_trivial_v> inline constexpr void copy_mem(T* out, const T* in, size_t n) { BOTAN_ASSERT_IMPLICATION(n > 0, in != nullptr && out != nullptr, "If n > 0 then args are not null"); @@ -160,7 +157,7 @@ template requires std::is_same_v, std::ranges::range_value_t> && std::is_trivially_copyable_v> -inline constexpr void copy_mem(OutR&& out, InR&& in) { +inline constexpr void copy_mem(OutR&& out /* NOLINT(*-std-forward) */, const InR& in) { ranges::assert_equal_byte_lengths(out, in); if(std::is_constant_evaluated()) { std::copy(std::ranges::begin(in), std::ranges::end(in), std::ranges::begin(out)); @@ -176,7 +173,7 @@ template requires std::is_trivially_copyable_v> && std::is_trivially_copyable_v> -inline constexpr void typecast_copy(ToR&& out, FromR&& in) { +inline constexpr void typecast_copy(ToR&& out /* NOLINT(*-std-forward) */, const FromR& in) { ranges::assert_equal_byte_lengths(out, in); std::memcpy(std::ranges::data(out), std::ranges::data(in), ranges::size_bytes(out)); } @@ -188,7 +185,7 @@ template requires std::is_trivially_copyable_v> && std::is_trivially_copyable_v && (!std::ranges::range) -inline constexpr void typecast_copy(ToT& out, FromR&& in) noexcept { +inline constexpr void typecast_copy(ToT& out, const FromR& in) { typecast_copy(std::span(&out, 1), in); } @@ -199,7 +196,7 @@ template requires std::is_trivially_copyable_v && (!std::ranges::range) && std::is_trivially_copyable_v> -inline constexpr void typecast_copy(ToR&& out, const FromT& in) { +inline constexpr void typecast_copy(ToR&& out /* NOLINT(*-std-forward) */, const FromT& in) { typecast_copy(out, std::span(&in, 1)); } @@ -210,8 +207,8 @@ template requires std::is_default_constructible_v && std::is_trivially_copyable_v && std::is_trivially_copyable_v> -inline constexpr ToT typecast_copy(FromR&& src) noexcept { - ToT dst; +inline constexpr ToT typecast_copy(const FromR& src) { + ToT dst; // NOLINT(*-member-init) typecast_copy(dst, src); return dst; } @@ -219,7 +216,7 @@ // TODO: deprecate and replace template inline constexpr void typecast_copy(uint8_t out[], T in[], size_t N) - requires std::is_trivially_copyable::value + requires std::is_trivially_copyable_v { // asserts that *in and *out point to the correct amount of memory typecast_copy(std::span(out, sizeof(T) * N), std::span(in, N)); @@ -228,7 +225,7 @@ // TODO: deprecate and replace template inline constexpr void typecast_copy(T out[], const uint8_t in[], size_t N) - requires std::is_trivial::value + requires std::is_trivial_v { // asserts that *in and *out point to the correct amount of memory typecast_copy(std::span(out, N), std::span(in, N * sizeof(T))); @@ -243,7 +240,7 @@ // TODO: deprecate and replace template - requires std::is_trivial::type>::value + requires std::is_trivial_v> inline constexpr void typecast_copy(T& out, const uint8_t in[]) { // asserts that *in points to the correct amount of memory typecast_copy(out, std::span(in, sizeof(T))); @@ -251,13 +248,13 @@ // TODO: deprecate and replace template - requires std::is_trivial::value + requires std::is_trivial_v inline constexpr To typecast_copy(const uint8_t src[]) noexcept { // asserts that *src points to the correct amount of memory return typecast_copy(std::span(src, sizeof(To))); } -#if !defined(BOTAN_IS_BEGIN_BUILT) +#if !defined(BOTAN_IS_BEING_BUILT) /** * Set memory to a fixed value * @param ptr a pointer to an array of bytes @@ -271,17 +268,19 @@ } #endif +#if !defined(BOTAN_IS_BEING_BUILT) inline const uint8_t* cast_char_ptr_to_uint8(const char* s) { return reinterpret_cast(s); } -inline const char* cast_uint8_ptr_to_char(const uint8_t* b) { - return reinterpret_cast(b); -} - inline uint8_t* cast_char_ptr_to_uint8(char* s) { return reinterpret_cast(s); } +#endif + +inline const char* cast_uint8_ptr_to_char(const uint8_t* b) { + return reinterpret_cast(b); +} inline char* cast_uint8_ptr_to_char(uint8_t* b) { return reinterpret_cast(b); diff -Nru botan3-3.7.1+dfsg/src/lib/utils/mem_pool/info.txt botan3-3.12.0+dfsg/src/lib/utils/mem_pool/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/mem_pool/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/mem_pool/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + MEM_POOL -> 20180309 - + name -> "Memory Pool" diff -Nru botan3-3.7.1+dfsg/src/lib/utils/mem_pool/mem_pool.cpp botan3-3.12.0+dfsg/src/lib/utils/mem_pool/mem_pool.cpp --- botan3-3.7.1+dfsg/src/lib/utils/mem_pool/mem_pool.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/mem_pool/mem_pool.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,7 +7,26 @@ #include #include +#include #include +#include + +#if defined(BOTAN_HAS_VALGRIND) || defined(BOTAN_ENABLE_DEBUG_ASSERTS) + /** + * @brief Prohibits access to unused memory pages in Botan's memory pool + * + * If BOTAN_MEM_POOL_USE_MMU_PROTECTIONS is defined, the Memory_Pool + * class used for mlock'ed memory will use OS calls to set page + * permissions so as to prohibit access to pages on the free list, then + * enable read/write access when the page is set to be used. This will + * turn (some) use after free bugs into a crash. + * + * The additional syscalls have a substantial performance impact, which + * is why this option is not enabled by default. It is used when built for + * running in valgrind or debug assertions are enabled. + */ + #define BOTAN_MEM_POOL_USE_MMU_PROTECTIONS +#endif #if defined(BOTAN_MEM_POOL_USE_MMU_PROTECTIONS) && defined(BOTAN_HAS_OS_UTILS) #include @@ -90,8 +109,8 @@ namespace { size_t choose_bucket(size_t n) { - const size_t MINIMUM_ALLOCATION = 16; - const size_t MAXIMUM_ALLOCATION = 256; + constexpr size_t MINIMUM_ALLOCATION = 16; + constexpr size_t MAXIMUM_ALLOCATION = 256; if(n < MINIMUM_ALLOCATION || n > MAXIMUM_ALLOCATION) { return 0; @@ -99,7 +118,7 @@ // Need to tune these - const size_t buckets[] = { + constexpr size_t buckets[] = { 16, 24, 32, @@ -115,7 +134,7 @@ 0, }; - for(size_t i = 0; buckets[i]; ++i) { + for(size_t i = 0; buckets[i] != 0; ++i) { if(n <= buckets[i]) { return buckets[i]; } @@ -153,19 +172,16 @@ public: explicit BitMap(size_t bits) : m_len(bits) { m_bits.resize((bits + BITMASK_BITS - 1) / BITMASK_BITS); - // MSVC warns if the cast isn't there, clang-tidy warns that the cast is pointless - m_main_mask = static_cast(~0); // NOLINT(bugprone-misplaced-widening-cast) - m_last_mask = m_main_mask; if(bits % BITMASK_BITS != 0) { m_last_mask = (static_cast(1) << (bits % BITMASK_BITS)) - 1; } } - bool find_free(size_t* bit); + std::optional find_free(); void free(size_t bit) { - BOTAN_ASSERT_NOMSG(bit <= m_len); + BOTAN_ASSERT_NOMSG(bit < m_len); const size_t w = bit / BITMASK_BITS; BOTAN_ASSERT_NOMSG(w < m_bits.size()); const bitmask_type mask = static_cast(1) << (bit % BITMASK_BITS); @@ -192,12 +208,13 @@ static const size_t BITMASK_BITS = sizeof(bitmask_type) * 8; size_t m_len; - bitmask_type m_main_mask; - bitmask_type m_last_mask; + // MSVC warns if the cast isn't there, clang-tidy warns that the cast is pointless + bitmask_type m_main_mask = static_cast(~0); // NOLINT(bugprone-misplaced-widening-cast) + bitmask_type m_last_mask = static_cast(~0); // NOLINT(bugprone-misplaced-widening-cast) std::vector m_bits; }; -bool BitMap::find_free(size_t* bit) { +std::optional BitMap::find_free() { for(size_t i = 0; i != m_bits.size(); ++i) { const bitmask_type mask = (i == m_bits.size() - 1) ? m_last_mask : m_main_mask; if((m_bits[i] & mask) != mask) { @@ -205,12 +222,11 @@ const bitmask_type bmask = static_cast(1) << (free_bit % BITMASK_BITS); BOTAN_ASSERT_NOMSG((m_bits[i] & bmask) == 0); m_bits[i] |= bmask; - *bit = BITMASK_BITS * i + free_bit; - return true; + return BITMASK_BITS * i + free_bit; } } - return false; + return {}; } } // namespace @@ -230,15 +246,15 @@ return nullptr; } - size_t offset; - if(!m_bitmap.find_free(&offset)) { + auto offset = m_bitmap.find_free(); + if(!offset) { // I just found out I am full m_is_full = true; return nullptr; + } else { + BOTAN_ASSERT(*offset * m_item_size < m_page_size, "Offset is in range"); + return m_range + m_item_size * (*offset); } - - BOTAN_ASSERT(offset * m_item_size < m_page_size, "Offset is in range"); - return m_range + m_item_size * offset; } bool free(void* p) { @@ -274,11 +290,8 @@ bool m_is_full; }; -Memory_Pool::Memory_Pool(const std::vector& pages, size_t page_size) : m_page_size(page_size) { - m_min_page_ptr = ~static_cast(0); - m_max_page_ptr = 0; - - for(auto page : pages) { +Memory_Pool::Memory_Pool(const std::vector& pages, size_t page_size) noexcept : m_page_size(page_size) { + for(auto* page : pages) { const uintptr_t p = reinterpret_cast(page); m_min_page_ptr = std::min(p, m_min_page_ptr); @@ -298,7 +311,7 @@ m_max_page_ptr += page_size; } -Memory_Pool::~Memory_Pool() // NOLINT(*-use-equals-default) +Memory_Pool::~Memory_Pool() noexcept // NOLINT(*-use-equals-default) { #if defined(BOTAN_MEM_POOL_USE_MMU_PROTECTIONS) for(size_t i = 0; i != m_free_pages.size(); ++i) { @@ -315,7 +328,7 @@ const size_t n_bucket = choose_bucket(n); if(n_bucket > 0) { - lock_guard_type lock(m_mutex); + const lock_guard_type lock(m_mutex); std::deque& buckets = m_buckets_for[n_bucket]; @@ -326,6 +339,7 @@ recycled. */ for(auto& bucket : buckets) { + // NOLINTNEXTLINE(*-const-correctness) bug in clang-tidy if(uint8_t* p = bucket.alloc()) { return p; } @@ -341,6 +355,7 @@ OS::page_allow_access(ptr); #endif buckets.push_front(Bucket(ptr, m_page_size, n_bucket)); + // NOLINTNEXTLINE(*-const-correctness) bug in clang-tidy void* p = buckets[0].alloc(); BOTAN_ASSERT_NOMSG(p != nullptr); return p; @@ -362,7 +377,7 @@ if(n_bucket != 0) { try { - lock_guard_type lock(m_mutex); + const lock_guard_type lock(m_mutex); std::deque& buckets = m_buckets_for[n_bucket]; diff -Nru botan3-3.7.1+dfsg/src/lib/utils/mem_pool/mem_pool.h botan3-3.12.0+dfsg/src/lib/utils/mem_pool/mem_pool.h --- botan3-3.7.1+dfsg/src/lib/utils/mem_pool/mem_pool.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/mem_pool/mem_pool.h 2026-05-07 01:38:28.000000000 +0000 @@ -26,9 +26,9 @@ * @param page_size the system page size, each page should * point to exactly this much memory. */ - Memory_Pool(const std::vector& pages, size_t page_size); + Memory_Pool(const std::vector& pages, size_t page_size) noexcept; - ~Memory_Pool(); + ~Memory_Pool() noexcept; void* allocate(size_t size); @@ -47,8 +47,8 @@ std::deque m_free_pages; std::map> m_buckets_for; - uintptr_t m_min_page_ptr; - uintptr_t m_max_page_ptr; + uintptr_t m_min_page_ptr = ~static_cast(0); + uintptr_t m_max_page_ptr = 0; }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/mem_utils.cpp botan3-3.12.0+dfsg/src/lib/utils/mem_utils.cpp --- botan3-3.7.1+dfsg/src/lib/utils/mem_utils.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/mem_utils.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -4,10 +4,16 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include +#include +#include +#include #include +#if defined(BOTAN_TARGET_OS_HAS_EXPLICIT_BZERO) + #include +#endif + #if defined(BOTAN_TARGET_OS_HAS_RTLSECUREZEROMEMORY) #define NOMINMAX 1 #define _WINSOCKAPI_ // stop windows.h including winsock.h @@ -17,6 +23,14 @@ namespace Botan { void secure_scrub_memory(void* ptr, size_t n) { + return secure_zeroize_buffer(ptr, n); +} + +void secure_zeroize_buffer(void* ptr, size_t n) { + if(n == 0) { + return; + } + #if defined(BOTAN_TARGET_OS_HAS_RTLSECUREZEROMEMORY) ::RtlSecureZeroMemory(ptr, n); @@ -26,22 +40,25 @@ #elif defined(BOTAN_TARGET_OS_HAS_EXPLICIT_MEMSET) (void)::explicit_memset(ptr, 0, n); -#elif defined(BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO) && (BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO == 1) +#else /* - Call memset through a static volatile pointer, which the compiler - should not elide. This construct should be safe in conforming - compilers, but who knows. I did confirm that on x86-64 GCC 6.1 and - Clang 3.8 both create code that saves the memset address in the - data segment and unconditionally loads and jumps to that address. + * Call memset through a static volatile pointer, which the compiler should + * not elide. This construct should be safe in conforming compilers, but who + * knows. This has been checked to generate the expected code, which saves the + * memset address in the data segment and unconditionally loads and jumps to + * that address, with the following targets: + * + * x86-64: Clang 19, GCC 6, 11, 13, 14 + * riscv64: GCC 14 + * aarch64: GCC 14 + * armv7: GCC 14 + * + * Actually all of them generated the expected jump even without marking the + * function pointer as volatile. However this seems worth including as an + * additional precaution. */ static void* (*const volatile memset_ptr)(void*, int, size_t) = std::memset; (memset_ptr)(ptr, 0, n); -#else - - volatile uint8_t* p = reinterpret_cast(ptr); - - for(size_t i = 0; i != n; ++i) - p[i] = 0; #endif } diff -Nru botan3-3.7.1+dfsg/src/lib/utils/mem_utils.h botan3-3.12.0+dfsg/src/lib/utils/mem_utils.h --- botan3-3.7.1+dfsg/src/lib/utils/mem_utils.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/mem_utils.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,82 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_MEM_UTILS_H_ +#define BOTAN_MEM_UTILS_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace Botan { + +/** +* Zeroize memory contents in a way that a compiler should not elide, +* using some system specific technique. +* +* Use this function to scrub memory just before deallocating it, or on +* a stack buffer before returning from the function. +* +* @param ptr a pointer to memory to scrub +* @param n the number of bytes pointed to by ptr +*/ +BOTAN_TEST_API void secure_zeroize_buffer(void* ptr, size_t n); + +/** + * @param buf a pointer to the start of the region + * @param n the number of elements in buf + */ +template +inline void zeroize_buffer(T buf[], size_t n) { + if(n > 0) { + std::memset(buf, 0, sizeof(T) * n); + } +} + +template +inline void unchecked_copy_memory(T* out, const T* in, size_t n) { + if(in != nullptr && out != nullptr && n > 0) { + std::memmove(out, in, sizeof(T) * n); + } +} + +/** +* Return true if any of the provided arguments are null +*/ +template +bool any_null_pointers(Ptrs... ptr) { + static_assert((... && std::is_pointer_v), "All arguments must be pointers"); + return (... || (ptr == nullptr)); +} + +inline std::span as_span_of_bytes(const char* s, size_t len) { + const uint8_t* b = reinterpret_cast(s); + return std::span{b, len}; +} + +inline std::span as_span_of_bytes(const std::string& s) { + return as_span_of_bytes(s.data(), s.size()); +} + +inline std::span as_span_of_bytes(std::string_view s) { + return as_span_of_bytes(s.data(), s.size()); +} + +inline std::span cstr_as_span_of_bytes(const char* s) { + return as_span_of_bytes(s, std::strlen(s)); +} + +inline std::string bytes_to_string(std::span bytes) { + return std::string(reinterpret_cast(bytes.data()), bytes.size()); +} + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/mul128.h botan3-3.12.0+dfsg/src/lib/utils/mul128.h --- botan3-3.7.1+dfsg/src/lib/utils/mul128.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/mul128.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,9 +9,10 @@ #define BOTAN_UTIL_MUL128_H_ #include +#include #include -#if defined(BOTAN_BUILD_COMPILER_IS_MSVC) && defined(BOTAN_TARGET_CPU_HAS_NATIVE_64BIT) +#if defined(BOTAN_BUILD_COMPILER_IS_MSVC) #include #endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/mutex.h botan3-3.12.0+dfsg/src/lib/utils/mutex.h --- botan3-3.7.1+dfsg/src/lib/utils/mutex.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/mutex.h 2026-05-07 01:38:28.000000000 +0000 @@ -21,7 +21,7 @@ using recursive_mutex_type = std::recursive_mutex; template -using lock_guard_type = std::lock_guard; +using lock_guard_type = std::scoped_lock; #else diff -Nru botan3-3.7.1+dfsg/src/lib/utils/os_utils/info.txt botan3-3.12.0+dfsg/src/lib/utils/os_utils/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/os_utils/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/os_utils/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + OS_UTILS -> 20241202 - + name -> "Operating System Utils" diff -Nru botan3-3.7.1+dfsg/src/lib/utils/os_utils/os_utils.cpp botan3-3.12.0+dfsg/src/lib/utils/os_utils/os_utils.cpp --- botan3-3.7.1+dfsg/src/lib/utils/os_utils/os_utils.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/os_utils/os_utils.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,7 +10,11 @@ #include #include -#include +#include + +#if defined(BOTAN_HAS_CPUID) + #include +#endif #include #include @@ -18,16 +22,11 @@ #include #include -#if defined(BOTAN_TARGET_OS_HAS_EXPLICIT_BZERO) - #include -#endif - #if defined(BOTAN_TARGET_OS_HAS_POSIX1) #include #include #include #include - #include #include #include #include @@ -44,11 +43,6 @@ #include #endif -#if defined(BOTAN_TARGET_OS_HAS_AUXINFO) - #include - #include -#endif - #if defined(BOTAN_TARGET_OS_HAS_WIN32) #define NOMINMAX 1 #define _WINSOCKAPI_ // stop windows.h including winsock.h @@ -93,9 +87,7 @@ namespace { -#if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) || defined(BOTAN_TARGET_OS_HAS_ELF_AUX_INFO) || \ - defined(BOTAN_TARGET_OS_HAS_AUXINFO) - +#if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) || defined(BOTAN_TARGET_OS_HAS_ELF_AUX_INFO) #define BOTAN_TARGET_HAS_AUXVAL_INTERFACE #endif @@ -132,13 +124,6 @@ if(::elf_aux_info(static_cast(*id), &auxinfo, sizeof(auxinfo)) == 0) { return auxinfo; } -#elif defined(BOTAN_TARGET_OS_HAS_AUXINFO) - for(const AuxInfo* auxinfo = static_cast(::_dlauxinfo()); auxinfo != AT_NULL; ++auxinfo) { - if(*id == auxinfo->a_type) { - return auxinfo->a_v; - } - } - // no match; fall off the end and return nullopt #endif } @@ -190,10 +175,20 @@ #elif defined(BOTAN_USE_GCC_INLINE_ASM) - #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + // NOLINTBEGIN(*-no-assembler) + + #if defined(BOTAN_TARGET_ARCH_IS_X86_64) - if(CPUID::has_rdtsc()) { - uint32_t rtc_low = 0, rtc_high = 0; + uint32_t rtc_low = 0; // NOLINT(*-const-correctness) clang-tidy doesn't understand inline asm + uint32_t rtc_high = 0; // NOLINT(*-const-correctness) clang-tidy doesn't understand inline asm + asm volatile("rdtsc" : "=d"(rtc_high), "=a"(rtc_low)); + rtc = (static_cast(rtc_high) << 32) | rtc_low; + + #elif defined(BOTAN_TARGET_ARCH_IS_X86_FAMILY) && defined(BOTAN_HAS_CPUID) + + if(CPUID::has(CPUID::Feature::RDTSC)) { + uint32_t rtc_low = 0; + uint32_t rtc_high = 0; asm volatile("rdtsc" : "=d"(rtc_high), "=a"(rtc_low)); rtc = (static_cast(rtc_high) << 32) | rtc_low; } @@ -201,7 +196,9 @@ #elif defined(BOTAN_TARGET_ARCH_IS_PPC64) for(;;) { - uint32_t rtc_low = 0, rtc_high = 0, rtc_high2 = 0; + uint32_t rtc_low = 0; + uint32_t rtc_high = 0; + uint32_t rtc_high2 = 0; asm volatile("mftbu %0" : "=r"(rtc_high)); asm volatile("mftb %0" : "=r"(rtc_low)); asm volatile("mftbu %0" : "=r"(rtc_high2)); @@ -215,8 +212,8 @@ #elif defined(BOTAN_TARGET_ARCH_IS_ALPHA) asm volatile("rpcc %0" : "=r"(rtc)); - // OpenBSD does not trap access to the %tick register #elif defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD) + // OpenBSD does not trap access to the %tick register so we avoid it there asm volatile("rd %%tick, %0" : "=r"(rtc)); #elif defined(BOTAN_TARGET_ARCH_IS_IA64) @@ -232,6 +229,8 @@ //#warning "OS::get_cpu_cycle_counter not implemented" #endif + // NOLINTEND(*-no-assembler) + #endif return rtc; @@ -270,7 +269,7 @@ } uint64_t OS::get_high_resolution_clock() { - if(uint64_t cpu_clock = OS::get_cpu_cycle_counter()) { + if(const uint64_t cpu_clock = OS::get_cpu_cycle_counter()) { return cpu_clock; } @@ -306,8 +305,9 @@ #endif }; - for(clockid_t clock : clock_types) { - struct timespec ts; + for(const clockid_t clock : clock_types) { + struct timespec ts {}; + if(::clock_gettime(clock, &ts) == 0) { return (static_cast(ts.tv_sec) * 1000000000) + static_cast(ts.tv_nsec); } @@ -325,7 +325,8 @@ uint64_t OS::get_system_timestamp_ns() { #if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME) - struct timespec ts; + struct timespec ts {}; + if(::clock_gettime(CLOCK_REALTIME, &ts) == 0) { return (static_cast(ts.tv_sec) * 1000000000) + static_cast(ts.tv_nsec); } @@ -340,12 +341,16 @@ } std::string OS::format_time(time_t time, const std::string& format) { - std::tm tm; + std::tm tm{}; #if defined(BOTAN_TARGET_OS_HAS_WIN32) - localtime_s(&tm, &time); + if(::localtime_s(&tm, &time) != 0) { + throw Encoding_Error("Could not convert time_t to localtime"); + } #elif defined(BOTAN_TARGET_OS_HAS_POSIX1) - localtime_r(&time, &tm); + if(::localtime_r(&time, &tm) == nullptr) { + throw Encoding_Error("Could not convert time_t to localtime"); + } #else if(auto tmp = std::localtime(&time)) { tm = *tmp; @@ -363,7 +368,7 @@ const size_t default_page_size = 4096; #if defined(BOTAN_TARGET_OS_HAS_POSIX1) - long p = ::sysconf(_SC_PAGESIZE); + const long p = ::sysconf(_SC_PAGESIZE); if(p > 1) { return static_cast(p); } else { @@ -399,7 +404,7 @@ std::min(read_env_variable_sz("BOTAN_MLOCK_POOL_SIZE", max_locked_kb), max_locked_kb); if(mlock_requested > 0) { - struct ::rlimit limits; + struct ::rlimit limits {}; ::getrlimit(RLIMIT_MEMLOCK, &limits); @@ -447,7 +452,8 @@ return false; } -#if defined(BOTAN_TARGET_OS_HAS_WIN32) && defined(BOTAN_BUILD_COMPILER_IS_MSVC) +#if defined(BOTAN_TARGET_OS_HAS_WIN32) && \ + (defined(BOTAN_BUILD_COMPILER_IS_MSVC) || defined(BOTAN_BUILD_COMPILER_IS_CLANGCL)) const std::string name(name_view); char val[128] = {0}; size_t req_size = 0; @@ -509,6 +515,34 @@ #endif } +int mmap_flags() { + int flags = MAP_PRIVATE; + + #if defined(MAP_ANONYMOUS) + flags |= MAP_ANONYMOUS; + #elif defined(MAP_ANON) + flags |= MAP_ANON; + #endif + + #if defined(MAP_CONCEAL) + flags |= MAP_CONCEAL; + #elif defined(MAP_NOCORE) + flags |= MAP_NOCORE; + #endif + + return flags; +} + +int mmap_prot() { + int prot = PROT_READ | PROT_WRITE; // NOLINT(*-const-correctness) + + #if defined(PROT_MAX) + prot |= PROT_MAX(prot); + #endif + + return prot; +} + } // namespace #endif @@ -531,31 +565,10 @@ void* ptr = nullptr; #if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK) - - int mmap_flags = MAP_PRIVATE; - - #if defined(MAP_ANONYMOUS) - mmap_flags |= MAP_ANONYMOUS; - #elif defined(MAP_ANON) - mmap_flags |= MAP_ANON; - #endif - - #if defined(MAP_CONCEAL) - mmap_flags |= MAP_CONCEAL; - #elif defined(MAP_NOCORE) - mmap_flags |= MAP_NOCORE; - #endif - - int mmap_prot = PROT_READ | PROT_WRITE; - - #if defined(PROT_MAX) - mmap_prot |= PROT_MAX(mmap_prot); - #endif - ptr = ::mmap(nullptr, 3 * page_size, - mmap_prot, - mmap_flags, + mmap_prot(), + mmap_flags(), /*fd=*/locked_fd, /*offset=*/0); @@ -590,7 +603,7 @@ // Attempts to name the data page page_named(ptr, 3 * page_size); - // Make guard page preceeding the data page + // Make guard page preceding the data page page_prohibit_access(static_cast(ptr)); // Make guard page following the data page page_prohibit_access(static_cast(ptr) + 2 * page_size); @@ -635,9 +648,7 @@ void OS::free_locked_pages(const std::vector& pages) { const size_t page_size = OS::system_page_size(); - for(size_t i = 0; i != pages.size(); ++i) { - void* ptr = pages[i]; - + for(void* ptr : pages) { secure_scrub_memory(ptr, page_size); // ptr points to the data page, guard pages are before and after @@ -657,7 +668,8 @@ void OS::page_named(void* page, size_t size) { #if defined(BOTAN_TARGET_OS_HAS_PRCTL) && defined(PR_SET_VMA) && defined(PR_SET_VMA_ANON_NAME) static constexpr char name[] = "Botan mlock pool"; - int r = prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast(page), size, name); + // NOLINTNEXTLINE(*-vararg) + const int r = prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast(page), size, name); BOTAN_UNUSED(r); #else BOTAN_UNUSED(page, size); @@ -718,8 +730,9 @@ volatile int probe_result = -3; #if defined(BOTAN_TARGET_OS_HAS_POSIX1) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN) - struct sigaction old_sigaction; - struct sigaction sigaction; + struct sigaction old_sigaction {}; + + struct sigaction sigaction {}; sigaction.sa_handler = botan_sigill_handler; sigemptyset(&sigaction.sa_mask); @@ -756,10 +769,9 @@ std::unique_ptr OS::suppress_echo_on_terminal() { #if defined(BOTAN_TARGET_OS_HAS_POSIX1) - class POSIX_Echo_Suppression : public Echo_Suppression { + class POSIX_Echo_Suppression final : public Echo_Suppression { public: - POSIX_Echo_Suppression() { - m_stdin_fd = fileno(stdin); + POSIX_Echo_Suppression() : m_stdin_fd(fileno(stdin)), m_old_termios{} { if(::tcgetattr(m_stdin_fd, &m_old_termios) != 0) { throw System_Error("Getting terminal status failed", errno); } @@ -802,7 +814,7 @@ #elif defined(BOTAN_TARGET_OS_HAS_WIN32) - class Win32_Echo_Suppression : public Echo_Suppression { + class Win32_Echo_Suppression final : public Echo_Suppression { public: Win32_Echo_Suppression() { m_input_handle = ::GetStdHandle(STD_INPUT_HANDLE); diff -Nru botan3-3.7.1+dfsg/src/lib/utils/os_utils/os_utils.h botan3-3.12.0+dfsg/src/lib/utils/os_utils/os_utils.h --- botan3-3.7.1+dfsg/src/lib/utils/os_utils/os_utils.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/os_utils/os_utils.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -95,7 +96,7 @@ /** * @return maximum amount of memory (in bytes) Botan could/should -* hyptothetically allocate for the memory poool. Reads environment +* hypothetically allocate for the memory poool. Reads environment * variable "BOTAN_MLOCK_POOL_SIZE", set to "0" to disable pool. */ size_t get_memory_locking_limit(); @@ -119,7 +120,7 @@ * integer. If not set or conversion fails, returns the default value. * * If the process seems to be running in a privileged state (such as setuid) -* then always returns nullptr, similiar to glibc's secure_getenv. +* then always returns nullptr, similar to glibc's secure_getenv. */ size_t read_env_variable_sz(std::string_view var_name, size_t def_value = 0); @@ -191,7 +192,7 @@ /** * Represents a terminal state */ -class BOTAN_UNSTABLE_API Echo_Suppression { +class BOTAN_UNSTABLE_API Echo_Suppression /* NOLINT(*special-member-functions) */ { public: /** * Reenable echo on this terminal. Can be safely called diff -Nru botan3-3.7.1+dfsg/src/lib/utils/parsing.cpp botan3-3.12.0+dfsg/src/lib/utils/parsing.cpp --- botan3-3.7.1+dfsg/src/lib/utils/parsing.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/parsing.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -13,7 +13,6 @@ #include #include #include -#include #include #include @@ -22,7 +21,7 @@ uint16_t to_uint16(std::string_view str) { const uint32_t x = to_u32bit(str); - if(x >> 16) { + if(x != static_cast(x)) { throw Invalid_Argument("Integer value exceeds 16 bit range"); } @@ -54,12 +53,12 @@ /* * Parse a SCAN-style algorithm name */ -std::vector parse_algorithm_name(std::string_view namex) { - if(namex.find('(') == std::string::npos && namex.find(')') == std::string::npos) { - return {std::string(namex)}; +std::vector parse_algorithm_name(std::string_view scan_name) { + if(scan_name.find('(') == std::string::npos && scan_name.find(')') == std::string::npos) { + return {std::string(scan_name)}; } - std::string name(namex); + std::string name(scan_name); std::string substring; std::vector elems; size_t level = 0; @@ -68,7 +67,7 @@ name = name.substr(name.find('(')); for(auto i = name.begin(); i != name.end(); ++i) { - char c = *i; + const char c = *i; if(c == '(') { ++level; @@ -84,7 +83,7 @@ } if(level == 0 || (level == 1 && i != name.end() - 1)) { - throw Invalid_Algorithm_Name(namex); + throw Invalid_Algorithm_Name(scan_name); } --level; } @@ -102,7 +101,7 @@ } if(!substring.empty()) { - throw Invalid_Algorithm_Name(namex); + throw Invalid_Algorithm_Name(scan_name); } return elems; @@ -115,14 +114,14 @@ } std::string substr; - for(auto i = str.begin(); i != str.end(); ++i) { - if(*i == delim) { + for(const char c : str) { + if(c == delim) { if(!substr.empty()) { elems.push_back(substr); } substr.clear(); } else { - substr += *i; + substr += c; } } @@ -169,7 +168,7 @@ // # of digits pushed to accum since last dot size_t cur_digits = 0; - for(char c : str) { + for(const char c : str) { if(c == '.') { // . without preceding digit is invalid if(cur_digits == 0) { @@ -219,6 +218,143 @@ return ip; } +std::optional> string_to_ipv6(std::string_view str) { + if(str.empty()) { + return {}; + } + + // Parsed hex groups, split by whether they appeared before or after a "::". + // If no "::" appears, only `pre` is populated and must reach exactly 8 groups. + std::array pre{}; + std::array post{}; + size_t pre_count = 0; + size_t post_count = 0; + bool seen_double_colon = false; + + auto hex_value = [](char c) -> std::optional { + if(c >= '0' && c <= '9') { + return c - '0'; + } else if(c >= 'a' && c <= 'f') { + return 10 + (c - 'a'); + } else if(c >= 'A' && c <= 'F') { + return 10 + (c - 'A'); + } else { + return {}; + } + }; + + size_t idx = 0; + bool expect_group = true; // set after any separator, cleared after a group + + while(idx < str.size()) { + if(str[idx] == ':') { + if(idx + 1 < str.size() && str[idx + 1] == ':') { + if(seen_double_colon) { + return {}; // at most one "::" + } + seen_double_colon = true; + idx += 2; + expect_group = (idx < str.size()); + continue; + } + // single ':' separator between groups — only valid after a group + if(expect_group) { + return {}; + } + expect_group = true; + idx += 1; + continue; + } + + // Parse a hex group of 1..4 digits + uint32_t group = 0; + size_t hex_chars = 0; + while(idx < str.size() && hex_chars < 4) { + const auto digit = hex_value(str[idx]); + if(digit.has_value() == false) { + break; + } + group = (group << 4) | static_cast(digit.value()); + idx += 1; + hex_chars += 1; + } + if(hex_chars == 0) { + return {}; + } + // If a 5th hex digit follows, the group is oversized. + if(hex_chars == 4 && idx < str.size() && hex_value(str[idx]).has_value()) { + return {}; + } + + if(seen_double_colon) { + if(post_count >= 8) { + return {}; + } + post[post_count++] = static_cast(group); + } else { + if(pre_count >= 8) { + return {}; + } + pre[pre_count++] = static_cast(group); + } + expect_group = false; + } + + // Trailing single ':' is invalid + if(expect_group) { + return {}; + } + + const size_t total_groups = pre_count + post_count; + if(seen_double_colon) { + // "::" has to cover at least one zero group + if(total_groups > 7) { + return {}; + } + } else { + if(total_groups != 8) { + return {}; + } + } + + std::array out{}; + for(size_t i = 0; i != pre_count; ++i) { + out[2 * i] = get_byte<0>(pre[i]); + out[2 * i + 1] = get_byte<1>(pre[i]); + } + const size_t gap = 8 - total_groups; + for(size_t i = 0; i != post_count; ++i) { + const size_t target = pre_count + gap + i; + out[2 * target] = get_byte<0>(post[i]); + out[2 * target + 1] = get_byte<1>(post[i]); + } + return out; +} + +std::string ipv6_to_string(std::span a) { + static const char* hex = "0123456789abcdef"; + + std::string out; + out.reserve(39); + + for(size_t i = 0; i != 16; i += 2) { + if(i != 0) { + out.push_back(':'); + } + const uint16_t group = make_uint16(a[i], a[i + 1]); + bool started = false; + // Write each nibble omitting leading 0s + for(int s = 12; s >= 0; s -= 4) { + const auto nibble = (group >> s) & 0xF; + if(nibble != 0 || started || s == 0) { + out.push_back(hex[nibble]); + started = true; + } + } + } + return out; +} + /* * Convert an IP address to decimal-dotted string */ @@ -238,73 +374,97 @@ return str; } -std::string tolower_string(std::string_view in) { - std::string s(in); - for(size_t i = 0; i != s.size(); ++i) { - const int cu = static_cast(s[i]); - if(std::isalpha(cu)) { - s[i] = static_cast(std::tolower(cu)); +std::string tolower_string(std::string_view str) { + // Locale-independent ASCII fold; the only callers (DNS name canonicalization + // for SAN/name-constraints) work on ASCII strings per RFC 1035. + std::string lower(str); + for(char& c : lower) { + if(c >= 'A' && c <= 'Z') { + c = static_cast(c + ('a' - 'A')); } } - return s; + return lower; } -bool host_wildcard_match(std::string_view issued_, std::string_view host_) { - const std::string issued = tolower_string(issued_); - const std::string host = tolower_string(host_); - +bool host_wildcard_match(std::string_view issued, std::string_view host) { if(host.empty() || issued.empty()) { return false; } + // Maximum valid DNS name + if(host.size() > 253) { + return false; + } + /* - If there are embedded nulls in your issued name - Well I feel bad for you son + The wildcard if existing absorbs (host.size() - issued.size() + 1) chars, + which must be non-negative. So issued cannot possibly exceed host.size() + 1. */ - if(std::count(issued.begin(), issued.end(), char(0)) > 0) { + if(issued.size() > host.size() + 1) { return false; } - // If more than one wildcard, then issued name is invalid - const size_t stars = std::count(issued.begin(), issued.end(), '*'); - if(stars > 1) { + /* + If there are embedded nulls in your issued name + Well I feel bad for you son + */ + if(issued.find('\0') != std::string_view::npos) { return false; } // '*' is not a valid character in DNS names so should not appear on the host side - if(std::count(host.begin(), host.end(), '*') != 0) { + if(host.find('*') != std::string_view::npos) { return false; } // Similarly a DNS name can't end in . - if(host[host.size() - 1] == '.') { + if(host.back() == '.') { return false; } // And a host can't have an empty name component, so reject that - if(host.find("..") != std::string::npos) { + if(host.find("..") != std::string_view::npos) { return false; } + // ASCII-only case-insensitive char equality, avoids locale overhead from tolower + auto dns_char_eq = [](char a, char b) -> bool { + if(a == b) { + return true; + } + const auto la = static_cast(a | 0x20); + const auto lb = static_cast(b | 0x20); + return la == lb && la >= 'a' && la <= 'z'; + }; + + auto dns_char_eq_range = [&](std::string_view a, std::string_view b) -> bool { + if(a.size() != b.size()) { + return false; + } + for(size_t i = 0; i != a.size(); ++i) { + if(!dns_char_eq(a[i], b[i])) { + return false; + } + } + return true; + }; + // Exact match: accept - if(issued == host) { + if(dns_char_eq_range(issued, host)) { return true; } - /* - Otherwise it might be a wildcard + // First detect offset of wildcard '*' if included + const size_t first_star = issued.find('*'); + const bool has_wildcard = (first_star != std::string_view::npos); - If the issued size is strictly longer than the hostname size it - couldn't possibly be a match, even if the issued value is a - wildcard. The only exception is when the wildcard ends up empty - (eg www.example.com matches www*.example.com) - */ - if(issued.size() > host.size() + 1) { + // At most one wildcard is allowed + if(has_wildcard && issued.find('*', first_star + 1) != std::string_view::npos) { return false; } // If no * at all then not a wildcard, and so not a match - if(stars != 1) { + if(!has_wildcard) { return false; } @@ -322,7 +482,9 @@ size_t host_idx = 0; for(size_t i = 0; i != issued.size(); ++i) { - dots_seen += (issued[i] == '.'); + if(issued[i] == '.') { + dots_seen += 1; + } if(issued[i] == '*') { // Fail: wildcard can only come in leftmost component @@ -342,13 +504,15 @@ } // Can't be any intervening .s that we would have skipped - if(std::count(host.begin() + host_idx, host.begin() + host_idx + advance, '.') != 0) { - return false; + for(size_t k = host_idx; k != host_idx + advance; ++k) { + if(host[k] == '.') { + return false; + } } host_idx += advance; } else { - if(issued[i] != host[host_idx]) { + if(!dns_char_eq(issued[i], host[host_idx])) { return false; } @@ -373,13 +537,13 @@ throw Decoding_Error("DNS name cannot be empty"); } - if(name.starts_with(".")) { - throw Decoding_Error("DNS name cannot start with a dot"); + if(name.starts_with(".") || name.ends_with(".")) { + throw Decoding_Error("DNS name cannot start or end with a dot"); } /* * Table mapping uppercase to lowercase and only including values for valid DNS names - * namely A-Z, a-z, 0-9, hypen, and dot, plus '*' for wildcarding. + * namely A-Z, a-z, 0-9, hyphen, and dot, plus '*' for wildcarding. (RFC 1035) */ // clang-format off constexpr uint8_t DNS_CHAR_MAPPING[128] = { @@ -396,15 +560,26 @@ std::string canon; canon.reserve(name.size()); + // RFC 1035: DNS labels must not exceed 63 characters + size_t current_label_length = 0; + for(size_t i = 0; i != name.size(); ++i) { - char c = name[i]; + const char c = name[i]; if(c == '.') { - if(name[i - 1] == '.') { + if(i > 0 && name[i - 1] == '.') { throw Decoding_Error("DNS name contains sequential period chars"); } - if(i == name.size() - 1) { - throw Decoding_Error("DNS name cannot end in a period"); + + if(current_label_length == 0) { + throw Decoding_Error("DNS name contains empty label"); + } + current_label_length = 0; // Reset for next label + } else { + current_label_length++; + + if(current_label_length > 63) { // RFC 1035 Maximum DNS label length + throw Decoding_Error("DNS name label exceeds maximum length of 63 characters"); } } @@ -416,10 +591,20 @@ if(mapped == 0) { throw Decoding_Error("DNS name includes invalid character"); } - // TODO check label lengths + + if(mapped == '-') { + if(i == 0 || (i > 0 && name[i - 1] == '.')) { + throw Decoding_Error("DNS name has label with leading hyphen"); + } else if(i == name.size() - 1 || (i < name.size() - 1 && name[i + 1] == '.')) { + throw Decoding_Error("DNS name has label with trailing hyphen"); + } + } canon.push_back(static_cast(mapped)); } + if(current_label_length == 0) { + throw Decoding_Error("DNS name contains empty label"); + } return canon; } diff -Nru botan3-3.7.1+dfsg/src/lib/utils/parsing.h botan3-3.12.0+dfsg/src/lib/utils/parsing.h --- botan3-3.7.1+dfsg/src/lib/utils/parsing.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/parsing.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,9 +9,11 @@ #define BOTAN_PARSING_UTILS_H_ #include -#include +#include +#include #include #include +#include #include #include #include @@ -28,7 +30,7 @@ /** * Split a string * @param str the input string -* @param delim the delimitor +* @param delim the delimiter * @return string split by delim */ BOTAN_TEST_API std::vector split_on(std::string_view str, char delim); @@ -36,7 +38,7 @@ /** * Join a string * @param strs strings to join -* @param delim the delimitor +* @param delim the delimiter * @return string joined by delim */ std::string string_join(const std::vector& strs, char delim); @@ -69,10 +71,25 @@ */ std::string BOTAN_TEST_API ipv4_to_string(uint32_t ip_addr); +/** +* Convert a string representation of an IPv6 address to a 16-byte big-endian +* array. Accepts the full form (eight colon-separated hex groups), the +* "::"-compressed form (exactly one run of zero groups elided), and combinations +* such as "2001:db8::1". Does not currently accept the IPv4-in-IPv6 trailing +* dotted-quad form (e.g. "::ffff:192.0.2.1") or surrounding brackets. +*/ +std::optional> BOTAN_TEST_API string_to_ipv6(std::string_view ip_str); + +/** +* Convert an IPv6 address to normalized string format. Zero compression ("::") +* is not applied. +*/ +std::string BOTAN_TEST_API ipv6_to_string(std::span ip_addr); + std::map read_cfg(std::istream& is); /** -* Accepts key value pairs deliminated by commas: +* Accepts key value pairs delimited by commas: * * "" (returns empty map) * "K=V" (returns map {'K': 'V'}) @@ -89,7 +106,7 @@ BOTAN_TEST_API std::map read_kv(std::string_view kv); -std::string tolower_string(std::string_view s); +std::string tolower_string(std::string_view str); /** * Check if the given hostname is a match for the specified wildcard @@ -102,7 +119,7 @@ * * Otherwise throws Decoding_Error */ -std::string check_and_canonicalize_dns_name(std::string_view name); +BOTAN_TEST_API std::string check_and_canonicalize_dns_name(std::string_view name); } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/poly_dbl/info.txt botan3-3.12.0+dfsg/src/lib/utils/poly_dbl/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/poly_dbl/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/poly_dbl/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + POLY_DBL -> 20170927 - + name -> "Polynomial Doubling" diff -Nru botan3-3.7.1+dfsg/src/lib/utils/poly_dbl/poly_dbl.cpp botan3-3.12.0+dfsg/src/lib/utils/poly_dbl/poly_dbl.cpp --- botan3-3.7.1+dfsg/src/lib/utils/poly_dbl/poly_dbl.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/poly_dbl/poly_dbl.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -19,7 +19,7 @@ * * See "Table of Low-Weight Binary Irreducible Polynomials" * by Gadiel Seroussi, HP Labs Tech Report HPL-98-135 -* http://www.hpl.hp.com/techreports/98/HPL-98-135.pdf +* https://shiftleft.com/mirrors/www.hpl.hp.com/techreports/98/HPL-98-135.pdf */ enum class MinWeightPolynomial : uint32_t { P64 = 0x1B, @@ -116,7 +116,9 @@ } } -void xts_update_tweak_block(uint8_t tweak[], size_t BS, size_t blocks_in_tweak) { +void xts_compute_tweak_block(uint8_t tweak[], size_t BS, size_t blocks_in_tweak) { + BOTAN_ASSERT_NOMSG(blocks_in_tweak > 0); + if(BS == 16) { constexpr size_t LIMBS = 2; diff -Nru botan3-3.7.1+dfsg/src/lib/utils/poly_dbl/poly_dbl.h botan3-3.12.0+dfsg/src/lib/utils/poly_dbl/poly_dbl.h --- botan3-3.7.1+dfsg/src/lib/utils/poly_dbl/poly_dbl.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/poly_dbl/poly_dbl.h 2026-05-07 01:38:28.000000000 +0000 @@ -34,8 +34,13 @@ /* * Tweak block update step for XTS +* +* Assumes tweak is BS * n bytes long. +* +* The first block remains unmodified. +* The remaining n-1 blocks are set to the successive doublings of the first block */ -void xts_update_tweak_block(uint8_t tweak[], size_t BS, size_t n); +void xts_compute_tweak_block(uint8_t tweak[], size_t BS, size_t n); } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/prefetch.h botan3-3.12.0+dfsg/src/lib/utils/prefetch.h --- botan3-3.7.1+dfsg/src/lib/utils/prefetch.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/prefetch.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,7 +8,7 @@ #define BOTAN_PREFETCH_UTILS_H_ #include -#include +#include namespace Botan { @@ -30,10 +30,8 @@ * to not elide otherwise "useless" reads. The return value will always * be zero. */ -template -T prefetch_arrays(T (&... arr)[Ns]) noexcept - requires std::is_integral::value -{ +template +T prefetch_arrays(T (&... arr)[Ns]) noexcept { return (static_cast(prefetch_array_raw(sizeof(T) * Ns, arr)) & ...); } diff -Nru botan3-3.7.1+dfsg/src/lib/utils/range_concepts.h botan3-3.12.0+dfsg/src/lib/utils/range_concepts.h --- botan3-3.7.1+dfsg/src/lib/utils/range_concepts.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/range_concepts.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,119 @@ +/** + * (C) 2023 Jack Lloyd + * 2023 René Meusel - Rohde & Schwarz Cybersecurity + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#ifndef BOTAN_RANGE_CONCEPTS_H_ +#define BOTAN_RANGE_CONCEPTS_H_ + +#include +#include +#include +#include +#include + +BOTAN_FUTURE_INTERNAL_HEADER(range_concepts.h) + +namespace Botan::ranges { + +/** + * Models a std::ranges::contiguous_range that (optionally) restricts its + * value_type to ValueT. In other words: a stretch of contiguous memory of + * a certain type (optional ValueT). + */ +template > +concept contiguous_range = std::ranges::contiguous_range && std::same_as>; + +/** + * Models a std::ranges::contiguous_range that satisfies + * std::ranges::output_range with an arbitrary value_type. In other words: a + * stretch of contiguous memory of a certain type (optional ValueT) that can be + * written to. + */ +template > +concept contiguous_output_range = contiguous_range && std::ranges::output_range; + +/** + * Models a range that can be turned into a std::span<>. Typically, this is some + * form of ranges::contiguous_range. + */ +template +concept spanable_range = std::constructible_from>, T>; + +/** + * Models a range that can be turned into a std::span<> with a static extent. + * Typically, this is a std::array or a std::span derived from an array. + */ +// clang-format off +template +concept statically_spanable_range = spanable_range && + decltype(std::span{std::declval()})::extent != std::dynamic_extent; + +// clang-format on + +/** + * Find the length in bytes of a given contiguous range @p r. + */ +inline constexpr size_t size_bytes(const spanable_range auto& r) { + return std::span{r}.size_bytes(); +} + +/** +* Throws an exception indicating that the attempted read or write was invalid +*/ +[[noreturn]] void BOTAN_UNSTABLE_API memory_region_size_violation(); + +/** + * Check that a given range @p r has a certain statically-known byte length. If + * the range's extent is known at compile time, this is a static check, + * otherwise a runtime argument check will be added. + * + * @throws Invalid_Argument if range @p r has a dynamic extent and does not + * feature the expected byte length. + */ +template +inline constexpr void assert_exact_byte_length(const R& r) { + const std::span s{r}; + if constexpr(statically_spanable_range) { + static_assert(s.size_bytes() == expected, "memory region does not have expected byte lengths"); + } else { + if(s.size_bytes() != expected) { + memory_region_size_violation(); + } + } +} + +/** + * Check that a list of ranges (in @p r0 and @p rs) all have the same byte + * lengths. If the first range's extent is known at compile time, this will be a + * static check for all other ranges whose extents are known at compile time, + * otherwise a runtime argument check will be added. + * + * @throws Invalid_Argument if any range has a dynamic extent and not all + * ranges feature the same byte length. + */ +template +inline constexpr void assert_equal_byte_lengths(const R0& r0, const Rs&... rs) + requires(sizeof...(Rs) > 0) +{ + const std::span s0{r0}; + + if constexpr(statically_spanable_range) { + constexpr size_t expected_size = s0.size_bytes(); + (assert_exact_byte_length(rs), ...); + } else { + const size_t expected_size = s0.size_bytes(); + const bool correct_size = + ((std::span>{rs}.size_bytes() == expected_size) && ...); + + if(!correct_size) { + memory_region_size_violation(); + } + } +} + +} // namespace Botan::ranges + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/read_cfg.cpp botan3-3.12.0+dfsg/src/lib/utils/read_cfg.cpp --- botan3-3.7.1+dfsg/src/lib/utils/read_cfg.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/read_cfg.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include #include +#include namespace Botan { diff -Nru botan3-3.7.1+dfsg/src/lib/utils/read_kv.cpp botan3-3.12.0+dfsg/src/lib/utils/read_kv.cpp --- botan3-3.7.1+dfsg/src/lib/utils/read_kv.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/read_kv.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -29,7 +29,7 @@ std::string cur_key; std::string cur_val; - for(char c : kv) { + for(const char c : kv) { if(c == '\\' && !escaped) { escaped = true; } else if(c == ',' && !escaped) { @@ -37,7 +37,7 @@ throw Invalid_Argument("Bad KV spec empty key"); } - if(m.find(cur_key) != m.end()) { + if(m.contains(cur_key)) { throw Invalid_Argument("Bad KV spec duplicated key"); } m[cur_key] = cur_val; @@ -45,7 +45,7 @@ cur_val = ""; reading_key = true; } else if(c == '=' && !escaped) { - if(reading_key == false) { + if(!reading_key) { throw Invalid_Argument("Bad KV spec unexpected equals sign"); } reading_key = false; @@ -63,8 +63,8 @@ } if(!cur_key.empty()) { - if(reading_key == false) { - if(m.find(cur_key) != m.end()) { + if(!reading_key) { + if(m.contains(cur_key)) { throw Invalid_Argument("Bad KV spec duplicated key"); } m[cur_key] = cur_val; diff -Nru botan3-3.7.1+dfsg/src/lib/utils/rotate.h botan3-3.12.0+dfsg/src/lib/utils/rotate.h --- botan3-3.7.1+dfsg/src/lib/utils/rotate.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/rotate.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,7 +8,9 @@ #ifndef BOTAN_WORD_ROTATE_H_ #define BOTAN_WORD_ROTATE_H_ +#include #include +#include namespace Botan { @@ -17,8 +19,8 @@ * @param input the input word * @return input rotated left by ROT bits */ -template -inline constexpr T rotl(T input) +template +BOTAN_FORCE_INLINE constexpr T rotl(T input) requires(ROT > 0 && ROT < 8 * sizeof(T)) { return static_cast((input << ROT) | (input >> (8 * sizeof(T) - ROT))); @@ -29,8 +31,8 @@ * @param input the input word * @return input rotated right by ROT bits */ -template -inline constexpr T rotr(T input) +template +BOTAN_FORCE_INLINE constexpr T rotr(T input) requires(ROT > 0 && ROT < 8 * sizeof(T)) { return static_cast((input >> ROT) | (input << (8 * sizeof(T) - ROT))); @@ -39,16 +41,16 @@ /** * SHA-2 Sigma style function */ -template -inline constexpr T sigma(T x) { +template +BOTAN_FORCE_INLINE constexpr T sigma(T x) { return rotr(x) ^ rotr(x) ^ (x >> S); } /** * SHA-2 Sigma style function */ -template -inline constexpr T rho(T x) { +template +BOTAN_FORCE_INLINE constexpr T rho(T x) { return rotr(x) ^ rotr(x) ^ rotr(x); } @@ -58,8 +60,8 @@ * @param rot the number of bits to rotate, must be between 0 and sizeof(T)*8-1 * @return input rotated left by rot bits */ -template -inline constexpr T rotl_var(T input, size_t rot) { +template +BOTAN_FORCE_INLINE constexpr T rotl_var(T input, size_t rot) { return rot ? static_cast((input << rot) | (input >> (sizeof(T) * 8 - rot))) : input; } @@ -69,27 +71,11 @@ * @param rot the number of bits to rotate, must be between 0 and sizeof(T)*8-1 * @return input rotated right by rot bits */ -template -inline constexpr T rotr_var(T input, size_t rot) { +template +BOTAN_FORCE_INLINE constexpr T rotr_var(T input, size_t rot) { return rot ? static_cast((input >> rot) | (input << (sizeof(T) * 8 - rot))) : input; } -#if defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) - -template <> -inline uint32_t rotl_var(uint32_t input, size_t rot) { - asm("roll %1,%0" : "+r"(input) : "c"(static_cast(rot)) : "cc"); - return input; -} - -template <> -inline uint32_t rotr_var(uint32_t input, size_t rot) { - asm("rorl %1,%0" : "+r"(input) : "c"(static_cast(rot)) : "cc"); - return input; -} - -#endif - } // namespace Botan #endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/rounding.h botan3-3.12.0+dfsg/src/lib/utils/rounding.h --- botan3-3.7.1+dfsg/src/lib/utils/rounding.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/rounding.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #ifndef BOTAN_ROUNDING_H_ #define BOTAN_ROUNDING_H_ +#include #include namespace Botan { diff -Nru botan3-3.7.1+dfsg/src/lib/utils/scan_name.cpp botan3-3.12.0+dfsg/src/lib/utils/scan_name.cpp --- botan3-3.7.1+dfsg/src/lib/utils/scan_name.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/scan_name.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -55,7 +55,7 @@ SCAN_Name::SCAN_Name(const char* algo_spec) : SCAN_Name(std::string(algo_spec)) {} -SCAN_Name::SCAN_Name(std::string_view algo_spec) : m_orig_algo_spec(algo_spec), m_alg_name(), m_args(), m_mode_info() { +SCAN_Name::SCAN_Name(std::string_view algo_spec) : m_orig_algo_spec(algo_spec) { if(algo_spec.empty()) { throw Invalid_Argument("Expected algorithm name, got empty string"); } @@ -66,7 +66,7 @@ const std::string decoding_error = "Bad SCAN name '" + m_orig_algo_spec + "': "; - for(char c : algo_spec) { + for(const char c : algo_spec) { if(c == '/' || c == ',' || c == '(' || c == ')') { if(c == '(') { ++level; diff -Nru botan3-3.7.1+dfsg/src/lib/utils/scoped_cleanup.h botan3-3.12.0+dfsg/src/lib/utils/scoped_cleanup.h --- botan3-3.7.1+dfsg/src/lib/utils/scoped_cleanup.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/scoped_cleanup.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,58 @@ +/* +* (C) 2023-2024 René Meusel - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SCOPED_CLEANUP_H_ +#define BOTAN_SCOPED_CLEANUP_H_ + +#include +#include +#include + +namespace Botan { + +/** + * @brief Helper class to create a RAII-style cleanup callback + * + * Ensures that the cleanup callback given in the object's constructor is called + * when the object is destroyed. Use this to ensure some cleanup code runs when + * leaving the current scope. + */ +template +class scoped_cleanup final { + public: + explicit scoped_cleanup(FunT cleanup) : m_cleanup(std::move(cleanup)) {} + + scoped_cleanup(const scoped_cleanup&) = delete; + scoped_cleanup& operator=(const scoped_cleanup&) = delete; + + scoped_cleanup(scoped_cleanup&& other) noexcept : m_cleanup(std::move(other.m_cleanup)) { other.disengage(); } + + scoped_cleanup& operator=(scoped_cleanup&& other) noexcept { + if(this != &other) { + m_cleanup = std::move(other.m_cleanup); + other.disengage(); + } + return *this; + } + + ~scoped_cleanup() { + if(m_cleanup.has_value()) { + (*m_cleanup)(); // NOLINT(bugprone-exception-escape) clang-tidy bug + } + } + + /** + * Disengage the cleanup callback, i.e., prevent it from being called + */ + void disengage() noexcept { m_cleanup.reset(); } + + private: + std::optional m_cleanup; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/simd/info.txt botan3-3.12.0+dfsg/src/lib/utils/simd/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/simd/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/simd/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,32 +1,5 @@ - -SIMD_32 -> 20131128 - - name -> "SIMD" -brief -> "Helpers for working with SIMD instructions" +brief -> "Wrappers for SIMD operations" +type -> "Virtual" - - -simd_32.h - - - -x86_32:sse2 -x86_64:sse2 -x32:sse2 -arm32:neon -arm64:neon -ppc32:altivec -ppc64:altivec - - - -x86_32 -x86_64 -x32 -arm32 -arm64 -ppc32 -ppc64 - diff -Nru botan3-3.7.1+dfsg/src/lib/utils/simd/simd_2x64/info.txt botan3-3.12.0+dfsg/src/lib/utils/simd/simd_2x64/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/simd/simd_2x64/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/simd/simd_2x64/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,28 @@ + +SIMD_2X64 -> 20250405 + + + +name -> "SIMD 2x64" +brief -> "Lightweight wrappers for SIMD 2x64" + + + +simd_2x64.h + + +# TODO support NEON/VMX/LSX here + + +x86_32:ssse3 +x86_64:ssse3 +x32:ssse3 +wasm:simd128 + + + +x86_32 +x86_64 +x32 +wasm + diff -Nru botan3-3.7.1+dfsg/src/lib/utils/simd/simd_2x64/simd_2x64.h botan3-3.12.0+dfsg/src/lib/utils/simd/simd_2x64/simd_2x64.h --- botan3-3.7.1+dfsg/src/lib/utils/simd/simd_2x64/simd_2x64.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/simd/simd_2x64/simd_2x64.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,326 @@ +/* +* (C) 2022,2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SIMD_2X64_H_ +#define BOTAN_SIMD_2X64_H_ + +#include +#include +#include +#include +#include + +// TODO: extend this to support NEON / AltiVec / LSX + +#if defined(BOTAN_TARGET_ARCH_SUPPORTS_SSSE3) + #include + #include + #define BOTAN_SIMD_USE_SSSE3 +#elif defined(BOTAN_TARGET_ARCH_SUPPORTS_SIMD128) + #include + #define BOTAN_SIMD_USE_SIMD128 +#endif + +namespace Botan { + +// NOLINTBEGIN(portability-simd-intrinsics) + +class SIMD_2x64 final { + public: +#if defined(BOTAN_SIMD_USE_SSSE3) + using native_simd_type = __m128i; +#elif defined(BOTAN_SIMD_USE_SIMD128) + using native_simd_type = v128_t; +#endif + + SIMD_2x64& operator=(const SIMD_2x64& other) = default; + SIMD_2x64(const SIMD_2x64& other) = default; + + SIMD_2x64& operator=(SIMD_2x64&& other) = default; + SIMD_2x64(SIMD_2x64&& other) = default; + + ~SIMD_2x64() = default; + + // zero initialized + BOTAN_FN_ISA_SIMD_2X64 SIMD_2x64() : +#if defined(BOTAN_SIMD_USE_SSSE3) + m_simd(_mm_setzero_si128()) +#elif defined(BOTAN_SIMD_USE_SIMD128) + m_simd(wasm_u64x2_const_splat(0)) +#endif + { + } + + static SIMD_2x64 BOTAN_FN_ISA_SIMD_2X64 splat(uint64_t v) { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_2x64(_mm_set1_epi64x(v)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_2x64(wasm_u64x2_splat(v)); +#endif + } + + static SIMD_2x64 BOTAN_FN_ISA_SIMD_2X64 all_ones() { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_2x64(_mm_set1_epi8(-1)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_2x64(wasm_i8x16_splat(0xFF)); +#endif + } + + BOTAN_FN_ISA_SIMD_2X64 SIMD_2x64(uint64_t low, uint64_t high) : +#if defined(BOTAN_SIMD_USE_SSSE3) + m_simd(_mm_set_epi64x(high, low)) +#elif defined(BOTAN_SIMD_USE_SIMD128) + m_simd(wasm_u64x2_make(low, high)) +#endif + { + } + + static SIMD_2x64 BOTAN_FN_ISA_SIMD_2X64 load_le(const void* in) { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_2x64(_mm_loadu_si128(reinterpret_cast(in))); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_2x64(wasm_v128_load(in)); +#endif + } + + static SIMD_2x64 BOTAN_FN_ISA_SIMD_2X64 load_be(const void* in) { return SIMD_2x64::load_le(in).bswap(); } + + static SIMD_2x64 BOTAN_FN_ISA_SIMD_2X64 load_le(std::span in) { + return SIMD_2x64::load_le(in.data()); + } + + static SIMD_2x64 BOTAN_FN_ISA_SIMD_2X64 load_be(std::span in) { + return SIMD_2x64::load_be(in.data()); + } + + SIMD_2x64 BOTAN_FN_ISA_SIMD_2X64 bswap() const { +#if defined(BOTAN_SIMD_USE_SSSE3) + const auto idx = _mm_set_epi8(8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7); + return SIMD_2x64(_mm_shuffle_epi8(m_simd, idx)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_2x64(wasm_i8x16_shuffle(m_simd, m_simd, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8)); +#endif + } + + SIMD_2x64 BOTAN_FN_ISA_SIMD_2X64 swap_lanes() const { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_2x64(_mm_shuffle_epi32(m_simd, _MM_SHUFFLE(1, 0, 3, 2))); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_2x64(wasm_i64x2_shuffle(m_simd, m_simd, 1, 0)); +#endif + } + + SIMD_2x64 BOTAN_FN_ISA_SIMD_2X64 reverse_all_bytes() const { +#if defined(BOTAN_SIMD_USE_SSSE3) + const auto idx = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + return SIMD_2x64(_mm_shuffle_epi8(m_simd, idx)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_2x64(wasm_i8x16_shuffle(m_simd, m_simd, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)); +#endif + } + + void BOTAN_FN_ISA_SIMD_2X64 store_le(uint64_t out[2]) const { this->store_le(reinterpret_cast(out)); } + + void BOTAN_FN_ISA_SIMD_2X64 store_le(uint8_t out[]) const { +#if defined(BOTAN_SIMD_USE_SSSE3) + _mm_storeu_si128(reinterpret_cast<__m128i*>(out), m_simd); +#elif defined(BOTAN_SIMD_USE_SIMD128) + wasm_v128_store(out, m_simd); +#endif + } + + void BOTAN_FN_ISA_SIMD_2X64 store_be(uint64_t out[2]) const { this->store_be(reinterpret_cast(out)); } + + void BOTAN_FN_ISA_SIMD_2X64 store_be(uint8_t out[]) const { bswap().store_le(out); } + + void BOTAN_FN_ISA_SIMD_2X64 store_be(std::span out) const { this->store_be(out.data()); } + + void BOTAN_FN_ISA_SIMD_2X64 store_le(std::span out) const { this->store_le(out.data()); } + + SIMD_2x64 BOTAN_FN_ISA_SIMD_2X64 operator+(const SIMD_2x64& other) const { + SIMD_2x64 retval(*this); + retval += other; + return retval; + } + + SIMD_2x64 BOTAN_FN_ISA_SIMD_2X64 operator^(const SIMD_2x64& other) const { + SIMD_2x64 retval(*this); + retval ^= other; + return retval; + } + + void BOTAN_FN_ISA_SIMD_2X64 operator+=(const SIMD_2x64& other) { +#if defined(BOTAN_SIMD_USE_SSSE3) + m_simd = _mm_add_epi64(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_SIMD128) + m_simd = wasm_i64x2_add(m_simd, other.m_simd); +#endif + } + + void BOTAN_FN_ISA_SIMD_2X64 operator^=(const SIMD_2x64& other) { +#if defined(BOTAN_SIMD_USE_SSSE3) + m_simd = _mm_xor_si128(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_SIMD128) + m_simd = wasm_v128_xor(m_simd, other.m_simd); +#endif + } + + SIMD_2x64 BOTAN_FN_ISA_SIMD_2X64 andc(const SIMD_2x64& other) const noexcept { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_2x64(_mm_andnot_si128(m_simd, other.m_simd)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + // SIMD128 is a & ~b + return SIMD_2x64(wasm_v128_andnot(other.m_simd, m_simd)); +#endif + } + + template + BOTAN_FN_ISA_SIMD_2X64 SIMD_2x64 rotr() const + requires(ROT > 0 && ROT < 64) + { +#if defined(BOTAN_SIMD_USE_SSSE3) + if constexpr(ROT == 8) { + auto tab = _mm_setr_epi8(1, 2, 3, 4, 5, 6, 7, 0, 9, 10, 11, 12, 13, 14, 15, 8); + return SIMD_2x64(_mm_shuffle_epi8(m_simd, tab)); + } else if constexpr(ROT == 16) { + auto tab = _mm_setr_epi8(2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9); + return SIMD_2x64(_mm_shuffle_epi8(m_simd, tab)); + } else if constexpr(ROT == 24) { + auto tab = _mm_setr_epi8(3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10); + return SIMD_2x64(_mm_shuffle_epi8(m_simd, tab)); + } else if constexpr(ROT == 32) { + auto tab = _mm_setr_epi8(4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11); + return SIMD_2x64(_mm_shuffle_epi8(m_simd, tab)); + } else { + return SIMD_2x64(_mm_or_si128(_mm_srli_epi64(m_simd, static_cast(ROT)), + _mm_slli_epi64(m_simd, static_cast(64 - ROT)))); + } +#elif defined(BOTAN_SIMD_USE_SIMD128) + if constexpr(ROT == 8) { + return SIMD_2x64(wasm_i8x16_shuffle(m_simd, m_simd, 1, 2, 3, 4, 5, 6, 7, 0, 9, 10, 11, 12, 13, 14, 15, 8)); + } else if constexpr(ROT == 16) { + return SIMD_2x64(wasm_i8x16_shuffle(m_simd, m_simd, 2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9)); + } else if constexpr(ROT == 24) { + return SIMD_2x64(wasm_i8x16_shuffle(m_simd, m_simd, 3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10)); + } else if constexpr(ROT == 32) { + return SIMD_2x64(wasm_i8x16_shuffle(m_simd, m_simd, 4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11)); + } else { + return SIMD_2x64(wasm_v128_or(wasm_u64x2_shr(m_simd, ROT), wasm_i64x2_shl(m_simd, 64 - ROT))); + } +#endif + } + + template + SIMD_2x64 BOTAN_FN_ISA_SIMD_2X64 rotl() const { + return this->rotr<64 - ROT>(); + } + + template + SIMD_2x64 BOTAN_FN_ISA_SIMD_2X64 shr() const noexcept { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_2x64(_mm_srli_epi64(m_simd, SHIFT)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_2x64(wasm_u64x2_shr(m_simd, SHIFT)); +#endif + } + + template + SIMD_2x64 BOTAN_FN_ISA_SIMD_2X64 shl() const noexcept { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_2x64(_mm_slli_epi64(m_simd, SHIFT)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_2x64(wasm_i64x2_shl(m_simd, SHIFT)); +#endif + } + + static SIMD_2x64 BOTAN_FN_ISA_SIMD_2X64 alignr8(const SIMD_2x64& a, const SIMD_2x64& b) { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_2x64(_mm_alignr_epi8(a.m_simd, b.m_simd, 8)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_2x64( + wasm_i8x16_shuffle(b.m_simd, a.m_simd, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23)); +#endif + } + + static SIMD_2x64 BOTAN_FN_ISA_SIMD_2X64 interleave_low(const SIMD_2x64& a, const SIMD_2x64& b) { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_2x64(_mm_unpacklo_epi64(a.m_simd, b.m_simd)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_2x64(wasm_u64x2_extract_lane(a.m_simd, 0), wasm_u64x2_extract_lane(b.m_simd, 0)); +#endif + } + + static SIMD_2x64 BOTAN_FN_ISA_SIMD_2X64 interleave_high(const SIMD_2x64& a, const SIMD_2x64& b) { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_2x64(_mm_unpackhi_epi64(a.m_simd, b.m_simd)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_2x64(wasm_u64x2_extract_lane(a.m_simd, 1), wasm_u64x2_extract_lane(b.m_simd, 1)); +#endif + } + + // Argon2 specific operation + static void BOTAN_FN_ISA_SIMD_2X64 + twist(SIMD_2x64& B0, SIMD_2x64& B1, SIMD_2x64& C0, SIMD_2x64& C1, SIMD_2x64& D0, SIMD_2x64& D1) { + auto T0 = SIMD_2x64::alignr8(B1, B0); + auto T1 = SIMD_2x64::alignr8(B0, B1); + B0 = T0; + B1 = T1; + + T0 = C0; + C0 = C1; + C1 = T0; + + T0 = SIMD_2x64::alignr8(D0, D1); + T1 = SIMD_2x64::alignr8(D1, D0); + D0 = T0; + D1 = T1; + } + + // Argon2 specific operation + static void BOTAN_FN_ISA_SIMD_2X64 + untwist(SIMD_2x64& B0, SIMD_2x64& B1, SIMD_2x64& C0, SIMD_2x64& C1, SIMD_2x64& D0, SIMD_2x64& D1) { + auto T0 = SIMD_2x64::alignr8(B0, B1); + auto T1 = SIMD_2x64::alignr8(B1, B0); + B0 = T0; + B1 = T1; + + T0 = C0; + C0 = C1; + C1 = T0; + + T0 = SIMD_2x64::alignr8(D1, D0); + T1 = SIMD_2x64::alignr8(D0, D1); + D0 = T0; + D1 = T1; + } + + // Argon2 specific operation + static SIMD_2x64 BOTAN_FN_ISA_SIMD_2X64 mul2_32(SIMD_2x64 x, SIMD_2x64 y) { +#if defined(BOTAN_SIMD_USE_SSSE3) + const __m128i m = _mm_mul_epu32(x.m_simd, y.m_simd); + return SIMD_2x64(_mm_add_epi64(m, m)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + const auto m = wasm_u64x2_extmul_low_u32x4(wasm_i32x4_shuffle(x.m_simd, x.m_simd, 0, 2, 0, 2), + wasm_i32x4_shuffle(y.m_simd, y.m_simd, 0, 2, 0, 2)); + + return SIMD_2x64(wasm_i64x2_add(m, m)); +#endif + } + + native_simd_type BOTAN_FN_ISA_SIMD_2X64 raw() const noexcept { return m_simd; } + + explicit BOTAN_FN_ISA_SIMD_2X64 SIMD_2x64(native_simd_type x) : m_simd(x) {} + + private: + native_simd_type m_simd; +}; + +// NOLINTEND(portability-simd-intrinsics) + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/simd/simd_32.h botan3-3.12.0+dfsg/src/lib/utils/simd/simd_32.h --- botan3-3.7.1+dfsg/src/lib/utils/simd/simd_32.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/simd/simd_32.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,640 +0,0 @@ -/* -* Lightweight wrappers for SIMD operations -* (C) 2009,2011,2016,2017,2019 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_SIMD_32_H_ -#define BOTAN_SIMD_32_H_ - -#include -#include -#include - -#if defined(BOTAN_TARGET_SUPPORTS_SSE2) - #include - #define BOTAN_SIMD_USE_SSE2 - -#elif defined(BOTAN_TARGET_SUPPORTS_ALTIVEC) - #include - #include - #undef vector - #undef bool - #define BOTAN_SIMD_USE_ALTIVEC - #ifdef __VSX__ - #define BOTAN_SIMD_USE_VSX - #endif - -#elif defined(BOTAN_TARGET_SUPPORTS_NEON) - #include - #include - #define BOTAN_SIMD_USE_NEON - -#else - #error "No SIMD instruction set enabled" -#endif - -#if defined(BOTAN_SIMD_USE_SSE2) - #define BOTAN_SIMD_ISA "sse2" - #define BOTAN_VPERM_ISA "ssse3" - #define BOTAN_CLMUL_ISA "pclmul" -#elif defined(BOTAN_SIMD_USE_NEON) - #if defined(BOTAN_TARGET_ARCH_IS_ARM64) - #define BOTAN_SIMD_ISA "+simd" - #define BOTAN_CLMUL_ISA "+crypto+aes" - #else - #define BOTAN_SIMD_ISA "fpu=neon" - #endif - #define BOTAN_VPERM_ISA BOTAN_SIMD_ISA -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - #define BOTAN_SIMD_ISA "altivec" - #define BOTAN_VPERM_ISA "altivec" - #define BOTAN_CLMUL_ISA "crypto" -#endif - -namespace Botan { - -#if defined(BOTAN_SIMD_USE_SSE2) -using native_simd_type = __m128i; -#elif defined(BOTAN_SIMD_USE_ALTIVEC) -using native_simd_type = __vector unsigned int; -#elif defined(BOTAN_SIMD_USE_NEON) -using native_simd_type = uint32x4_t; -#endif - -/** -* 4x32 bit SIMD register -* -* This class is not a general purpose SIMD type, and only offers -* instructions needed for evaluation of specific crypto primitives. -* For example it does not currently have equality operators of any -* kind. -* -* Implemented for SSE2, VMX (Altivec), and NEON. -*/ -class SIMD_4x32 final { - public: - SIMD_4x32& operator=(const SIMD_4x32& other) = default; - SIMD_4x32(const SIMD_4x32& other) = default; - - SIMD_4x32& operator=(SIMD_4x32&& other) = default; - SIMD_4x32(SIMD_4x32&& other) = default; - - ~SIMD_4x32() = default; - - /** - * Zero initialize SIMD register with 4 32-bit elements - */ - SIMD_4x32() noexcept { -#if defined(BOTAN_SIMD_USE_SSE2) - m_simd = _mm_setzero_si128(); -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - m_simd = vec_splat_u32(0); -#elif defined(BOTAN_SIMD_USE_NEON) - m_simd = vdupq_n_u32(0); -#endif - } - - /** - * Load SIMD register with 4 32-bit elements - */ - explicit SIMD_4x32(const uint32_t B[4]) noexcept { -#if defined(BOTAN_SIMD_USE_SSE2) - m_simd = _mm_loadu_si128(reinterpret_cast(B)); -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - __vector unsigned int val = {B[0], B[1], B[2], B[3]}; - m_simd = val; -#elif defined(BOTAN_SIMD_USE_NEON) - m_simd = vld1q_u32(B); -#endif - } - - /** - * Load SIMD register with 4 32-bit elements - */ - SIMD_4x32(uint32_t B0, uint32_t B1, uint32_t B2, uint32_t B3) noexcept { -#if defined(BOTAN_SIMD_USE_SSE2) - m_simd = _mm_set_epi32(B3, B2, B1, B0); -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - __vector unsigned int val = {B0, B1, B2, B3}; - m_simd = val; -#elif defined(BOTAN_SIMD_USE_NEON) - // Better way to do this? - const uint32_t B[4] = {B0, B1, B2, B3}; - m_simd = vld1q_u32(B); -#endif - } - - /** - * Load SIMD register with one 32-bit element repeated - */ - static SIMD_4x32 splat(uint32_t B) noexcept { -#if defined(BOTAN_SIMD_USE_SSE2) - return SIMD_4x32(_mm_set1_epi32(B)); -#elif defined(BOTAN_SIMD_USE_NEON) - return SIMD_4x32(vdupq_n_u32(B)); -#else - return SIMD_4x32(B, B, B, B); -#endif - } - - /** - * Load SIMD register with one 8-bit element repeated - */ - static SIMD_4x32 splat_u8(uint8_t B) noexcept { -#if defined(BOTAN_SIMD_USE_SSE2) - return SIMD_4x32(_mm_set1_epi8(B)); -#elif defined(BOTAN_SIMD_USE_NEON) - return SIMD_4x32(vreinterpretq_u32_u8(vdupq_n_u8(B))); -#else - const uint32_t B4 = make_uint32(B, B, B, B); - return SIMD_4x32(B4, B4, B4, B4); -#endif - } - - /** - * Load a SIMD register with little-endian convention - */ - static SIMD_4x32 load_le(const void* in) noexcept { -#if defined(BOTAN_SIMD_USE_SSE2) - return SIMD_4x32(_mm_loadu_si128(reinterpret_cast(in))); -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - uint32_t R[4]; - Botan::load_le(R, static_cast(in), 4); - return SIMD_4x32(R); -#elif defined(BOTAN_SIMD_USE_NEON) - SIMD_4x32 l(vld1q_u32(static_cast(in))); - return CPUID::is_big_endian() ? l.bswap() : l; -#endif - } - - /** - * Load a SIMD register with big-endian convention - */ - static SIMD_4x32 load_be(const void* in) noexcept { -#if defined(BOTAN_SIMD_USE_SSE2) - return load_le(in).bswap(); - -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - uint32_t R[4]; - Botan::load_be(R, static_cast(in), 4); - return SIMD_4x32(R); - -#elif defined(BOTAN_SIMD_USE_NEON) - SIMD_4x32 l(vld1q_u32(static_cast(in))); - return CPUID::is_little_endian() ? l.bswap() : l; -#endif - } - - static SIMD_4x32 load_le(std::span in) { return SIMD_4x32::load_le(in.data()); } - - static SIMD_4x32 load_be(std::span in) { return SIMD_4x32::load_be(in.data()); } - - void store_le(uint32_t out[4]) const noexcept { this->store_le(reinterpret_cast(out)); } - - void store_be(uint32_t out[4]) const noexcept { this->store_be(reinterpret_cast(out)); } - - void store_le(uint64_t out[2]) const noexcept { this->store_le(reinterpret_cast(out)); } - - /** - * Load a SIMD register with little-endian convention - */ - void store_le(uint8_t out[]) const noexcept { -#if defined(BOTAN_SIMD_USE_SSE2) - - _mm_storeu_si128(reinterpret_cast<__m128i*>(out), raw()); - -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - - union { - __vector unsigned int V; - uint32_t R[4]; - } vec; - - vec.V = raw(); - Botan::store_le(out, vec.R[0], vec.R[1], vec.R[2], vec.R[3]); - -#elif defined(BOTAN_SIMD_USE_NEON) - if(CPUID::is_little_endian()) { - vst1q_u8(out, vreinterpretq_u8_u32(m_simd)); - } else { - vst1q_u8(out, vreinterpretq_u8_u32(bswap().m_simd)); - } -#endif - } - - /** - * Load a SIMD register with big-endian convention - */ - void store_be(uint8_t out[]) const noexcept { -#if defined(BOTAN_SIMD_USE_SSE2) - - bswap().store_le(out); - -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - - union { - __vector unsigned int V; - uint32_t R[4]; - } vec; - - vec.V = m_simd; - Botan::store_be(out, vec.R[0], vec.R[1], vec.R[2], vec.R[3]); - -#elif defined(BOTAN_SIMD_USE_NEON) - if(CPUID::is_little_endian()) { - vst1q_u8(out, vreinterpretq_u8_u32(bswap().m_simd)); - } else { - vst1q_u8(out, vreinterpretq_u8_u32(m_simd)); - } -#endif - } - - void store_be(std::span out) const { this->store_be(out.data()); } - - void store_le(std::span out) const { this->store_le(out.data()); } - - /* - * This is used for SHA-2/SHACAL2 - */ - SIMD_4x32 sigma0() const noexcept { -#if BOTAN_COMPILER_HAS_BUILTIN(__builtin_crypto_vshasigmaw) && defined(_ARCH_PWR8) - return SIMD_4x32(__builtin_crypto_vshasigmaw(raw(), 1, 0)); -#else - const SIMD_4x32 rot1 = this->rotr<2>(); - const SIMD_4x32 rot2 = this->rotr<13>(); - const SIMD_4x32 rot3 = this->rotr<22>(); - return (rot1 ^ rot2 ^ rot3); -#endif - } - - /* - * This is used for SHA-2/SHACAL2 - */ - SIMD_4x32 sigma1() const noexcept { -#if BOTAN_COMPILER_HAS_BUILTIN(__builtin_crypto_vshasigmaw) && defined(_ARCH_PWR8) - return SIMD_4x32(__builtin_crypto_vshasigmaw(raw(), 1, 0xF)); -#else - const SIMD_4x32 rot1 = this->rotr<6>(); - const SIMD_4x32 rot2 = this->rotr<11>(); - const SIMD_4x32 rot3 = this->rotr<25>(); - return (rot1 ^ rot2 ^ rot3); -#endif - } - - /** - * Left rotation by a compile time constant - */ - template - SIMD_4x32 rotl() const noexcept - requires(ROT > 0 && ROT < 32) - { -#if defined(BOTAN_SIMD_USE_SSE2) - - return SIMD_4x32(_mm_or_si128(_mm_slli_epi32(m_simd, static_cast(ROT)), - _mm_srli_epi32(m_simd, static_cast(32 - ROT)))); - -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - - const unsigned int r = static_cast(ROT); - __vector unsigned int rot = {r, r, r, r}; - return SIMD_4x32(vec_rl(m_simd, rot)); - -#elif defined(BOTAN_SIMD_USE_NEON) - - #if defined(BOTAN_TARGET_ARCH_IS_ARM64) - - if constexpr(ROT == 8) { - const uint8_t maskb[16] = {3, 0, 1, 2, 7, 4, 5, 6, 11, 8, 9, 10, 15, 12, 13, 14}; - const uint8x16_t mask = vld1q_u8(maskb); - return SIMD_4x32(vreinterpretq_u32_u8(vqtbl1q_u8(vreinterpretq_u8_u32(m_simd), mask))); - } else if constexpr(ROT == 16) { - return SIMD_4x32(vreinterpretq_u32_u16(vrev32q_u16(vreinterpretq_u16_u32(m_simd)))); - } - #endif - return SIMD_4x32( - vorrq_u32(vshlq_n_u32(m_simd, static_cast(ROT)), vshrq_n_u32(m_simd, static_cast(32 - ROT)))); -#endif - } - - /** - * Right rotation by a compile time constant - */ - template - SIMD_4x32 rotr() const noexcept { - return this->rotl<32 - ROT>(); - } - - /** - * Add elements of a SIMD vector - */ - SIMD_4x32 operator+(const SIMD_4x32& other) const noexcept { - SIMD_4x32 retval(*this); - retval += other; - return retval; - } - - /** - * Subtract elements of a SIMD vector - */ - SIMD_4x32 operator-(const SIMD_4x32& other) const noexcept { - SIMD_4x32 retval(*this); - retval -= other; - return retval; - } - - /** - * XOR elements of a SIMD vector - */ - SIMD_4x32 operator^(const SIMD_4x32& other) const noexcept { - SIMD_4x32 retval(*this); - retval ^= other; - return retval; - } - - /** - * Binary OR elements of a SIMD vector - */ - SIMD_4x32 operator|(const SIMD_4x32& other) const noexcept { - SIMD_4x32 retval(*this); - retval |= other; - return retval; - } - - /** - * Binary AND elements of a SIMD vector - */ - SIMD_4x32 operator&(const SIMD_4x32& other) const noexcept { - SIMD_4x32 retval(*this); - retval &= other; - return retval; - } - - void operator+=(const SIMD_4x32& other) noexcept { -#if defined(BOTAN_SIMD_USE_SSE2) - m_simd = _mm_add_epi32(m_simd, other.m_simd); -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - m_simd = vec_add(m_simd, other.m_simd); -#elif defined(BOTAN_SIMD_USE_NEON) - m_simd = vaddq_u32(m_simd, other.m_simd); -#endif - } - - void operator-=(const SIMD_4x32& other) noexcept { -#if defined(BOTAN_SIMD_USE_SSE2) - m_simd = _mm_sub_epi32(m_simd, other.m_simd); -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - m_simd = vec_sub(m_simd, other.m_simd); -#elif defined(BOTAN_SIMD_USE_NEON) - m_simd = vsubq_u32(m_simd, other.m_simd); -#endif - } - - void operator^=(const SIMD_4x32& other) noexcept { -#if defined(BOTAN_SIMD_USE_SSE2) - m_simd = _mm_xor_si128(m_simd, other.m_simd); -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - m_simd = vec_xor(m_simd, other.m_simd); -#elif defined(BOTAN_SIMD_USE_NEON) - m_simd = veorq_u32(m_simd, other.m_simd); -#endif - } - - void operator^=(uint32_t other) noexcept { *this ^= SIMD_4x32::splat(other); } - - void operator|=(const SIMD_4x32& other) noexcept { -#if defined(BOTAN_SIMD_USE_SSE2) - m_simd = _mm_or_si128(m_simd, other.m_simd); -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - m_simd = vec_or(m_simd, other.m_simd); -#elif defined(BOTAN_SIMD_USE_NEON) - m_simd = vorrq_u32(m_simd, other.m_simd); -#endif - } - - void operator&=(const SIMD_4x32& other) noexcept { -#if defined(BOTAN_SIMD_USE_SSE2) - m_simd = _mm_and_si128(m_simd, other.m_simd); -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - m_simd = vec_and(m_simd, other.m_simd); -#elif defined(BOTAN_SIMD_USE_NEON) - m_simd = vandq_u32(m_simd, other.m_simd); -#endif - } - - template - SIMD_4x32 shl() const noexcept - requires(SHIFT > 0 && SHIFT < 32) - { -#if defined(BOTAN_SIMD_USE_SSE2) - return SIMD_4x32(_mm_slli_epi32(m_simd, SHIFT)); - -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - const unsigned int s = static_cast(SHIFT); - const __vector unsigned int shifts = {s, s, s, s}; - return SIMD_4x32(vec_sl(m_simd, shifts)); -#elif defined(BOTAN_SIMD_USE_NEON) - return SIMD_4x32(vshlq_n_u32(m_simd, SHIFT)); -#endif - } - - template - SIMD_4x32 shr() const noexcept { -#if defined(BOTAN_SIMD_USE_SSE2) - return SIMD_4x32(_mm_srli_epi32(m_simd, SHIFT)); - -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - const unsigned int s = static_cast(SHIFT); - const __vector unsigned int shifts = {s, s, s, s}; - return SIMD_4x32(vec_sr(m_simd, shifts)); -#elif defined(BOTAN_SIMD_USE_NEON) - return SIMD_4x32(vshrq_n_u32(m_simd, SHIFT)); -#endif - } - - SIMD_4x32 operator~() const noexcept { -#if defined(BOTAN_SIMD_USE_SSE2) - return SIMD_4x32(_mm_xor_si128(m_simd, _mm_set1_epi32(0xFFFFFFFF))); -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - return SIMD_4x32(vec_nor(m_simd, m_simd)); -#elif defined(BOTAN_SIMD_USE_NEON) - return SIMD_4x32(vmvnq_u32(m_simd)); -#endif - } - - // (~reg) & other - SIMD_4x32 andc(const SIMD_4x32& other) const noexcept { -#if defined(BOTAN_SIMD_USE_SSE2) - return SIMD_4x32(_mm_andnot_si128(m_simd, other.m_simd)); -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - /* - AltiVec does arg1 & ~arg2 rather than SSE's ~arg1 & arg2 - so swap the arguments - */ - return SIMD_4x32(vec_andc(other.m_simd, m_simd)); -#elif defined(BOTAN_SIMD_USE_NEON) - // NEON is also a & ~b - return SIMD_4x32(vbicq_u32(other.m_simd, m_simd)); -#endif - } - - /** - * Return copy *this with each word byte swapped - */ - SIMD_4x32 bswap() const noexcept { -#if defined(BOTAN_SIMD_USE_SSE2) - - __m128i T = m_simd; - T = _mm_shufflehi_epi16(T, _MM_SHUFFLE(2, 3, 0, 1)); - T = _mm_shufflelo_epi16(T, _MM_SHUFFLE(2, 3, 0, 1)); - return SIMD_4x32(_mm_or_si128(_mm_srli_epi16(T, 8), _mm_slli_epi16(T, 8))); - -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - #ifdef BOTAN_SIMD_USE_VSX - return SIMD_4x32(vec_revb(m_simd)); - #else - const __vector unsigned char rev[1] = { - {3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12}, - }; - - return SIMD_4x32(vec_perm(m_simd, m_simd, rev[0])); - #endif - -#elif defined(BOTAN_SIMD_USE_NEON) - return SIMD_4x32(vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(m_simd)))); -#endif - } - - template - SIMD_4x32 shift_elems_left() const noexcept - requires(I <= 3) - { -#if defined(BOTAN_SIMD_USE_SSE2) - return SIMD_4x32(_mm_slli_si128(raw(), 4 * I)); -#elif defined(BOTAN_SIMD_USE_NEON) - return SIMD_4x32(vextq_u32(vdupq_n_u32(0), raw(), 4 - I)); -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - const __vector unsigned int zero = vec_splat_u32(0); - - const __vector unsigned char shuf[3] = { - {16, 17, 18, 19, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, - {16, 17, 18, 19, 20, 21, 22, 23, 0, 1, 2, 3, 4, 5, 6, 7}, - {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 0, 1, 2, 3}, - }; - - return SIMD_4x32(vec_perm(raw(), zero, shuf[I - 1])); -#endif - } - - template - SIMD_4x32 shift_elems_right() const noexcept - requires(I <= 3) - { -#if defined(BOTAN_SIMD_USE_SSE2) - return SIMD_4x32(_mm_srli_si128(raw(), 4 * I)); -#elif defined(BOTAN_SIMD_USE_NEON) - return SIMD_4x32(vextq_u32(raw(), vdupq_n_u32(0), I)); -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - const __vector unsigned int zero = vec_splat_u32(0); - - const __vector unsigned char shuf[3] = { - {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, - {8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, - {12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}, - }; - - return SIMD_4x32(vec_perm(raw(), zero, shuf[I - 1])); -#endif - } - - /** - * 4x4 Transposition on SIMD registers - */ - static void transpose(SIMD_4x32& B0, SIMD_4x32& B1, SIMD_4x32& B2, SIMD_4x32& B3) noexcept { -#if defined(BOTAN_SIMD_USE_SSE2) - const __m128i T0 = _mm_unpacklo_epi32(B0.m_simd, B1.m_simd); - const __m128i T1 = _mm_unpacklo_epi32(B2.m_simd, B3.m_simd); - const __m128i T2 = _mm_unpackhi_epi32(B0.m_simd, B1.m_simd); - const __m128i T3 = _mm_unpackhi_epi32(B2.m_simd, B3.m_simd); - - B0.m_simd = _mm_unpacklo_epi64(T0, T1); - B1.m_simd = _mm_unpackhi_epi64(T0, T1); - B2.m_simd = _mm_unpacklo_epi64(T2, T3); - B3.m_simd = _mm_unpackhi_epi64(T2, T3); -#elif defined(BOTAN_SIMD_USE_ALTIVEC) - const __vector unsigned int T0 = vec_mergeh(B0.m_simd, B2.m_simd); - const __vector unsigned int T1 = vec_mergeh(B1.m_simd, B3.m_simd); - const __vector unsigned int T2 = vec_mergel(B0.m_simd, B2.m_simd); - const __vector unsigned int T3 = vec_mergel(B1.m_simd, B3.m_simd); - - B0.m_simd = vec_mergeh(T0, T1); - B1.m_simd = vec_mergel(T0, T1); - B2.m_simd = vec_mergeh(T2, T3); - B3.m_simd = vec_mergel(T2, T3); - -#elif defined(BOTAN_SIMD_USE_NEON) && defined(BOTAN_TARGET_ARCH_IS_ARM32) - const uint32x4x2_t T0 = vzipq_u32(B0.m_simd, B2.m_simd); - const uint32x4x2_t T1 = vzipq_u32(B1.m_simd, B3.m_simd); - const uint32x4x2_t O0 = vzipq_u32(T0.val[0], T1.val[0]); - const uint32x4x2_t O1 = vzipq_u32(T0.val[1], T1.val[1]); - - B0.m_simd = O0.val[0]; - B1.m_simd = O0.val[1]; - B2.m_simd = O1.val[0]; - B3.m_simd = O1.val[1]; - -#elif defined(BOTAN_SIMD_USE_NEON) && defined(BOTAN_TARGET_ARCH_IS_ARM64) - const uint32x4_t T0 = vzip1q_u32(B0.m_simd, B2.m_simd); - const uint32x4_t T2 = vzip2q_u32(B0.m_simd, B2.m_simd); - const uint32x4_t T1 = vzip1q_u32(B1.m_simd, B3.m_simd); - const uint32x4_t T3 = vzip2q_u32(B1.m_simd, B3.m_simd); - - B0.m_simd = vzip1q_u32(T0, T1); - B1.m_simd = vzip2q_u32(T0, T1); - B2.m_simd = vzip1q_u32(T2, T3); - B3.m_simd = vzip2q_u32(T2, T3); -#endif - } - - static inline SIMD_4x32 choose(const SIMD_4x32& mask, const SIMD_4x32& a, const SIMD_4x32& b) noexcept { -#if defined(BOTAN_SIMD_USE_ALTIVEC) - return SIMD_4x32(vec_sel(b.raw(), a.raw(), mask.raw())); -#elif defined(BOTAN_SIMD_USE_NEON) - return SIMD_4x32(vbslq_u32(mask.raw(), a.raw(), b.raw())); -#else - return (mask & a) ^ mask.andc(b); -#endif - } - - static inline SIMD_4x32 majority(const SIMD_4x32& x, const SIMD_4x32& y, const SIMD_4x32& z) noexcept { - return SIMD_4x32::choose(x ^ y, z, y); - } - - native_simd_type raw() const noexcept { return m_simd; } - - explicit SIMD_4x32(native_simd_type x) noexcept : m_simd(x) {} - - private: - native_simd_type m_simd; -}; - -template -inline SIMD_4x32 rotl(SIMD_4x32 input) { - return input.rotl(); -} - -template -inline SIMD_4x32 rotr(SIMD_4x32 input) { - return input.rotr(); -} - -// For Serpent: -template -inline SIMD_4x32 shl(SIMD_4x32 input) { - return input.shl(); -} - -} // namespace Botan - -#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/simd/simd_4x32/info.txt botan3-3.12.0+dfsg/src/lib/utils/simd/simd_4x32/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/simd/simd_4x32/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/simd/simd_4x32/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,34 @@ + +SIMD_4X32 -> 20131128 + + + +name -> "SIMD 4x32" +brief -> "Lightweight wrappers for SIMD 4x32" + + + +simd_4x32.h + + + +x86_32:ssse3 +x86_64:ssse3 +x32:ssse3 +arm32:neon +arm64:neon +ppc64:altivec +loongarch64:lsx +wasm:simd128 + + + +x86_32 +x86_64 +x32 +arm32 +arm64 +ppc64 +loongarch64 +wasm + diff -Nru botan3-3.7.1+dfsg/src/lib/utils/simd/simd_4x32/simd_4x32.h botan3-3.12.0+dfsg/src/lib/utils/simd/simd_4x32/simd_4x32.h --- botan3-3.7.1+dfsg/src/lib/utils/simd/simd_4x32/simd_4x32.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/simd/simd_4x32/simd_4x32.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,970 @@ +/* +* Lightweight wrappers for SIMD (4x32 bit) operations +* (C) 2009,2011,2016,2017,2019,2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SIMD_4X32_H_ +#define BOTAN_SIMD_4X32_H_ + +#include +#include +#include +#include +#include + +#if defined(BOTAN_TARGET_ARCH_SUPPORTS_SSSE3) + #include + #define BOTAN_SIMD_USE_SSSE3 + +#elif defined(BOTAN_TARGET_ARCH_SUPPORTS_ALTIVEC) + #include + #include + #undef vector + #undef bool + #define BOTAN_SIMD_USE_ALTIVEC + #ifdef __VSX__ + #define BOTAN_SIMD_USE_VSX + #endif + +#elif defined(BOTAN_TARGET_ARCH_SUPPORTS_NEON) + #include + #include + #define BOTAN_SIMD_USE_NEON + +#elif defined(BOTAN_TARGET_ARCH_SUPPORTS_LSX) + #include + #define BOTAN_SIMD_USE_LSX + +#elif defined(BOTAN_TARGET_ARCH_SUPPORTS_SIMD128) + #include + #define BOTAN_SIMD_USE_SIMD128 + +#else + #error "No SIMD instruction set enabled" +#endif + +namespace Botan { + +// NOLINTBEGIN(portability-simd-intrinsics) + +/** +* 4x32 bit SIMD register +* +* This class is not a general purpose SIMD type, and only offers instructions +* needed for evaluation of specific crypto primitives. For example it does not +* currently have equality operators of any kind. +* +* Implemented for SSE2, VMX (Altivec), ARMv7/Aarch64 NEON, LoongArch LSX and Wasm SIMD128 +*/ +class SIMD_4x32 final { + public: +#if defined(BOTAN_SIMD_USE_SSSE3) || defined(BOTAN_SIMD_USE_LSX) + using native_simd_type = __m128i; +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + using native_simd_type = __vector unsigned int; +#elif defined(BOTAN_SIMD_USE_NEON) + using native_simd_type = uint32x4_t; +#elif defined(BOTAN_SIMD_USE_SIMD128) + using native_simd_type = v128_t; +#endif + + SIMD_4x32& operator=(const SIMD_4x32& other) = default; + SIMD_4x32(const SIMD_4x32& other) = default; + + SIMD_4x32& operator=(SIMD_4x32&& other) = default; + SIMD_4x32(SIMD_4x32&& other) = default; + + ~SIMD_4x32() = default; + + /* NOLINTBEGIN(*-prefer-member-initializer) */ + + /** + * Zero initialize SIMD register with 4 32-bit elements + */ + BOTAN_FN_ISA_SIMD_4X32 SIMD_4x32() noexcept { +#if defined(BOTAN_SIMD_USE_SSSE3) + m_simd = _mm_setzero_si128(); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + m_simd = vec_splat_u32(0); +#elif defined(BOTAN_SIMD_USE_NEON) + m_simd = vdupq_n_u32(0); +#elif defined(BOTAN_SIMD_USE_LSX) + m_simd = __lsx_vldi(0); +#elif defined(BOTAN_SIMD_USE_SIMD128) + m_simd = wasm_u32x4_const_splat(0); +#endif + } + + /** + * Load SIMD register with 4 32-bit elements + */ + BOTAN_FN_ISA_SIMD_4X32 SIMD_4x32(uint32_t B0, uint32_t B1, uint32_t B2, uint32_t B3) noexcept { +#if defined(BOTAN_SIMD_USE_SSSE3) + m_simd = _mm_set_epi32(B3, B2, B1, B0); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + __vector unsigned int val = {B0, B1, B2, B3}; + m_simd = val; +#elif defined(BOTAN_SIMD_USE_NEON) + // Better way to do this? + const uint32_t B[4] = {B0, B1, B2, B3}; + m_simd = vld1q_u32(B); +#elif defined(BOTAN_SIMD_USE_LSX) + // Better way to do this? + const uint32_t B[4] = {B0, B1, B2, B3}; + m_simd = __lsx_vld(B, 0); +#elif defined(BOTAN_SIMD_USE_SIMD128) + m_simd = wasm_u32x4_make(B0, B1, B2, B3); +#endif + } + + /* NOLINTEND(*-prefer-member-initializer) */ + + /** + * Load SIMD register with one 32-bit element repeated + */ + static SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 splat(uint32_t B) noexcept { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_4x32(_mm_set1_epi32(B)); +#elif defined(BOTAN_SIMD_USE_NEON) + return SIMD_4x32(vdupq_n_u32(B)); +#elif defined(BOTAN_SIMD_USE_LSX) + return SIMD_4x32(__lsx_vreplgr2vr_w(B)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_4x32(wasm_u32x4_splat(B)); +#else + return SIMD_4x32(B, B, B, B); +#endif + } + + /** + * Load SIMD register with one 8-bit element repeated + */ + static SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 splat_u8(uint8_t B) noexcept { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_4x32(_mm_set1_epi8(B)); +#elif defined(BOTAN_SIMD_USE_NEON) + return SIMD_4x32(vreinterpretq_u32_u8(vdupq_n_u8(B))); +#elif defined(BOTAN_SIMD_USE_LSX) + return SIMD_4x32(__lsx_vreplgr2vr_b(B)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_4x32(wasm_u8x16_splat(B)); +#else + const uint32_t B4 = make_uint32(B, B, B, B); + return SIMD_4x32(B4, B4, B4, B4); +#endif + } + + /** + * Load a SIMD register with little-endian convention + */ + static SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 load_le(const void* in) noexcept { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_4x32(_mm_loadu_si128(reinterpret_cast(in))); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + uint32_t R0 = Botan::load_le(reinterpret_cast(in), 0); + uint32_t R1 = Botan::load_le(reinterpret_cast(in), 1); + uint32_t R2 = Botan::load_le(reinterpret_cast(in), 2); + uint32_t R3 = Botan::load_le(reinterpret_cast(in), 3); + __vector unsigned int val = {R0, R1, R2, R3}; + return SIMD_4x32(val); +#elif defined(BOTAN_SIMD_USE_NEON) + SIMD_4x32 l(vld1q_u32(static_cast(in))); + if constexpr(std::endian::native == std::endian::big) { + return l.bswap(); + } else { + return l; + } +#elif defined(BOTAN_SIMD_USE_LSX) + return SIMD_4x32(__lsx_vld(in, 0)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_4x32(wasm_v128_load(in)); +#endif + } + + /** + * Load a SIMD register with big-endian convention + */ + static SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 load_be(const void* in) noexcept { +#if defined(BOTAN_SIMD_USE_SSSE3) || defined(BOTAN_SIMD_USE_LSX) || defined(BOTAN_SIMD_USE_SIMD128) + return load_le(in).bswap(); + +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + uint32_t R0 = Botan::load_be(reinterpret_cast(in), 0); + uint32_t R1 = Botan::load_be(reinterpret_cast(in), 1); + uint32_t R2 = Botan::load_be(reinterpret_cast(in), 2); + uint32_t R3 = Botan::load_be(reinterpret_cast(in), 3); + __vector unsigned int val = {R0, R1, R2, R3}; + return SIMD_4x32(val); + +#elif defined(BOTAN_SIMD_USE_NEON) + SIMD_4x32 l(vld1q_u32(static_cast(in))); + if constexpr(std::endian::native == std::endian::little) { + return l.bswap(); + } else { + return l; + } +#endif + } + + static SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 load_le(std::span in) { + return SIMD_4x32::load_le(in.data()); + } + + static SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 load_be(std::span in) { + return SIMD_4x32::load_be(in.data()); + } + + void BOTAN_FN_ISA_SIMD_4X32 store_le(uint32_t out[4]) const noexcept { + this->store_le(reinterpret_cast(out)); + } + + void BOTAN_FN_ISA_SIMD_4X32 store_be(uint32_t out[4]) const noexcept { + this->store_be(reinterpret_cast(out)); + } + + void BOTAN_FN_ISA_SIMD_4X32 store_le(uint64_t out[2]) const noexcept { + this->store_le(reinterpret_cast(out)); + } + + /** + * Load a SIMD register with little-endian convention + */ + void BOTAN_FN_ISA_SIMD_4X32 store_le(uint8_t out[]) const noexcept { +#if defined(BOTAN_SIMD_USE_SSSE3) + + _mm_storeu_si128(reinterpret_cast<__m128i*>(out), raw()); + +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + + union { + __vector unsigned int V; + uint32_t R[4]; + } vec{}; + + // NOLINTNEXTLINE(*-union-access) + vec.V = raw(); + // NOLINTNEXTLINE(*-union-access) + Botan::store_le(out, vec.R[0], vec.R[1], vec.R[2], vec.R[3]); + +#elif defined(BOTAN_SIMD_USE_NEON) + if constexpr(std::endian::native == std::endian::little) { + vst1q_u8(out, vreinterpretq_u8_u32(m_simd)); + } else { + vst1q_u8(out, vreinterpretq_u8_u32(bswap().m_simd)); + } +#elif defined(BOTAN_SIMD_USE_LSX) + __lsx_vst(raw(), out, 0); +#elif defined(BOTAN_SIMD_USE_SIMD128) + wasm_v128_store(out, m_simd); +#endif + } + + /** + * Load a SIMD register with big-endian convention + */ + BOTAN_FN_ISA_SIMD_4X32 void store_be(uint8_t out[]) const noexcept { +#if defined(BOTAN_SIMD_USE_SSSE3) || defined(BOTAN_SIMD_USE_LSX) || defined(BOTAN_SIMD_USE_SIMD128) + + bswap().store_le(out); + +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + + union { + __vector unsigned int V; + uint32_t R[4]; + } vec{}; + + // NOLINTNEXTLINE(*-union-access) + vec.V = m_simd; + // NOLINTNEXTLINE(*-union-access) + Botan::store_be(out, vec.R[0], vec.R[1], vec.R[2], vec.R[3]); + +#elif defined(BOTAN_SIMD_USE_NEON) + if constexpr(std::endian::native == std::endian::little) { + vst1q_u8(out, vreinterpretq_u8_u32(bswap().m_simd)); + } else { + vst1q_u8(out, vreinterpretq_u8_u32(m_simd)); + } +#endif + } + + void BOTAN_FN_ISA_SIMD_4X32 store_be(std::span out) const { this->store_be(out.data()); } + + void BOTAN_FN_ISA_SIMD_4X32 store_le(std::span out) const { this->store_le(out.data()); } + + /* + * This is used for SHA-2/SHACAL2 + */ + SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 sigma0() const noexcept { +#if BOTAN_COMPILER_HAS_BUILTIN(__builtin_crypto_vshasigmaw) && defined(_ARCH_PWR8) + return SIMD_4x32(__builtin_crypto_vshasigmaw(raw(), 1, 0)); +#else + const SIMD_4x32 r1 = this->rotr<2>(); + const SIMD_4x32 r2 = this->rotr<13>(); + const SIMD_4x32 r3 = this->rotr<22>(); + return (r1 ^ r2 ^ r3); +#endif + } + + /* + * This is used for SHA-2/SHACAL2 + */ + SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 sigma1() const noexcept { +#if BOTAN_COMPILER_HAS_BUILTIN(__builtin_crypto_vshasigmaw) && defined(_ARCH_PWR8) + return SIMD_4x32(__builtin_crypto_vshasigmaw(raw(), 1, 0xF)); +#else + const SIMD_4x32 r1 = this->rotr<6>(); + const SIMD_4x32 r2 = this->rotr<11>(); + const SIMD_4x32 r3 = this->rotr<25>(); + return (r1 ^ r2 ^ r3); +#endif + } + + /** + * Left rotation by a compile time constant + */ + template + BOTAN_FN_ISA_SIMD_4X32 SIMD_4x32 rotl() const noexcept + requires(ROT > 0 && ROT < 32) + { +#if defined(BOTAN_SIMD_USE_SSSE3) + if constexpr(ROT == 8) { + const auto shuf_rotl_8 = _mm_set_epi64x(0x0e0d0c0f0a09080b, 0x0605040702010003); + return SIMD_4x32(_mm_shuffle_epi8(raw(), shuf_rotl_8)); + } else if constexpr(ROT == 16) { + const auto shuf_rotl_16 = _mm_set_epi64x(0x0d0c0f0e09080b0a, 0x0504070601000302); + return SIMD_4x32(_mm_shuffle_epi8(raw(), shuf_rotl_16)); + } else if constexpr(ROT == 24) { + const auto shuf_rotl_24 = _mm_set_epi64x(0x0c0f0e0d080b0a09, 0x0407060500030201); + return SIMD_4x32(_mm_shuffle_epi8(raw(), shuf_rotl_24)); + } else { + return SIMD_4x32(_mm_xor_si128(_mm_slli_epi32(raw(), static_cast(ROT)), + _mm_srli_epi32(raw(), static_cast(32 - ROT)))); + } + +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + + const unsigned int r = static_cast(ROT); + __vector unsigned int rot = {r, r, r, r}; + return SIMD_4x32(vec_rl(m_simd, rot)); + +#elif defined(BOTAN_SIMD_USE_NEON) + + #if defined(BOTAN_TARGET_ARCH_IS_ARM64) + + if constexpr(ROT == 8) { + const uint8_t maskb[16] = {3, 0, 1, 2, 7, 4, 5, 6, 11, 8, 9, 10, 15, 12, 13, 14}; + const uint8x16_t mask = vld1q_u8(maskb); + return SIMD_4x32(vreinterpretq_u32_u8(vqtbl1q_u8(vreinterpretq_u8_u32(m_simd), mask))); + } else if constexpr(ROT == 16) { + return SIMD_4x32(vreinterpretq_u32_u16(vrev32q_u16(vreinterpretq_u16_u32(m_simd)))); + } + #endif + return SIMD_4x32( + vorrq_u32(vshlq_n_u32(m_simd, static_cast(ROT)), vshrq_n_u32(m_simd, static_cast(32 - ROT)))); +#elif defined(BOTAN_SIMD_USE_LSX) + return SIMD_4x32(__lsx_vrotri_w(raw(), 32 - ROT)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_4x32(wasm_v128_or(wasm_i32x4_shl(m_simd, ROT), wasm_u32x4_shr(m_simd, 32 - ROT))); +#endif + } + + /** + * Right rotation by a compile time constant + */ + template + SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 rotr() const noexcept { + return this->rotl<32 - ROT>(); + } + + /** + * Add elements of a SIMD vector + */ + SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 operator+(const SIMD_4x32& other) const noexcept { + SIMD_4x32 retval(*this); + retval += other; + return retval; + } + + /** + * Subtract elements of a SIMD vector + */ + SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 operator-(const SIMD_4x32& other) const noexcept { + SIMD_4x32 retval(*this); + retval -= other; + return retval; + } + + /** + * XOR elements of a SIMD vector + */ + SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 operator^(const SIMD_4x32& other) const noexcept { + SIMD_4x32 retval(*this); + retval ^= other; + return retval; + } + + /** + * Binary OR elements of a SIMD vector + */ + SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 operator|(const SIMD_4x32& other) const noexcept { + SIMD_4x32 retval(*this); + retval |= other; + return retval; + } + + /** + * Binary AND elements of a SIMD vector + */ + SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 operator&(const SIMD_4x32& other) const noexcept { + SIMD_4x32 retval(*this); + retval &= other; + return retval; + } + + void BOTAN_FN_ISA_SIMD_4X32 operator+=(const SIMD_4x32& other) noexcept { +#if defined(BOTAN_SIMD_USE_SSSE3) + m_simd = _mm_add_epi32(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + m_simd = vec_add(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_NEON) + m_simd = vaddq_u32(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_LSX) + m_simd = __lsx_vadd_w(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_SIMD128) + m_simd = wasm_i32x4_add(m_simd, other.m_simd); +#endif + } + + void BOTAN_FN_ISA_SIMD_4X32 operator-=(const SIMD_4x32& other) noexcept { +#if defined(BOTAN_SIMD_USE_SSSE3) + m_simd = _mm_sub_epi32(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + m_simd = vec_sub(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_NEON) + m_simd = vsubq_u32(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_LSX) + m_simd = __lsx_vsub_w(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_SIMD128) + m_simd = wasm_i32x4_sub(m_simd, other.m_simd); +#endif + } + + void BOTAN_FN_ISA_SIMD_4X32 operator^=(const SIMD_4x32& other) noexcept { +#if defined(BOTAN_SIMD_USE_SSSE3) + m_simd = _mm_xor_si128(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + m_simd = vec_xor(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_NEON) + m_simd = veorq_u32(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_LSX) + m_simd = __lsx_vxor_v(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_SIMD128) + m_simd = wasm_v128_xor(m_simd, other.m_simd); +#endif + } + + void BOTAN_FN_ISA_SIMD_4X32 operator^=(uint32_t other) noexcept { *this ^= SIMD_4x32::splat(other); } + + void BOTAN_FN_ISA_SIMD_4X32 operator|=(const SIMD_4x32& other) noexcept { +#if defined(BOTAN_SIMD_USE_SSSE3) + m_simd = _mm_or_si128(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + m_simd = vec_or(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_NEON) + m_simd = vorrq_u32(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_LSX) + m_simd = __lsx_vor_v(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_SIMD128) + m_simd = wasm_v128_or(m_simd, other.m_simd); +#endif + } + + void BOTAN_FN_ISA_SIMD_4X32 operator&=(const SIMD_4x32& other) noexcept { +#if defined(BOTAN_SIMD_USE_SSSE3) + m_simd = _mm_and_si128(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + m_simd = vec_and(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_NEON) + m_simd = vandq_u32(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_LSX) + m_simd = __lsx_vand_v(m_simd, other.m_simd); +#elif defined(BOTAN_SIMD_USE_SIMD128) + m_simd = wasm_v128_and(m_simd, other.m_simd); +#endif + } + + template + SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 shl() const noexcept + requires(SHIFT > 0 && SHIFT < 32) + { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_4x32(_mm_slli_epi32(m_simd, SHIFT)); + +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + const unsigned int s = static_cast(SHIFT); + const __vector unsigned int shifts = {s, s, s, s}; + return SIMD_4x32(vec_sl(m_simd, shifts)); +#elif defined(BOTAN_SIMD_USE_NEON) + return SIMD_4x32(vshlq_n_u32(m_simd, SHIFT)); +#elif defined(BOTAN_SIMD_USE_LSX) + return SIMD_4x32(__lsx_vslli_w(m_simd, SHIFT)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_4x32(wasm_i32x4_shl(m_simd, SHIFT)); +#endif + } + + template + SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 shr() const noexcept { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_4x32(_mm_srli_epi32(m_simd, SHIFT)); + +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + const unsigned int s = static_cast(SHIFT); + const __vector unsigned int shifts = {s, s, s, s}; + return SIMD_4x32(vec_sr(m_simd, shifts)); +#elif defined(BOTAN_SIMD_USE_NEON) + return SIMD_4x32(vshrq_n_u32(m_simd, SHIFT)); +#elif defined(BOTAN_SIMD_USE_LSX) + return SIMD_4x32(__lsx_vsrli_w(m_simd, SHIFT)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_4x32(wasm_u32x4_shr(m_simd, SHIFT)); +#endif + } + + SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 operator~() const noexcept { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_4x32(_mm_xor_si128(m_simd, _mm_set1_epi32(0xFFFFFFFF))); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + return SIMD_4x32(vec_nor(m_simd, m_simd)); +#elif defined(BOTAN_SIMD_USE_NEON) + return SIMD_4x32(vmvnq_u32(m_simd)); +#elif defined(BOTAN_SIMD_USE_LSX) + return SIMD_4x32(__lsx_vnor_v(m_simd, m_simd)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_4x32(wasm_v128_not(m_simd)); +#endif + } + + // (~reg) & other + SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 andc(const SIMD_4x32& other) const noexcept { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_4x32(_mm_andnot_si128(m_simd, other.m_simd)); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + /* + AltiVec does arg1 & ~arg2 rather than SSE's ~arg1 & arg2 + so swap the arguments + */ + return SIMD_4x32(vec_andc(other.m_simd, m_simd)); +#elif defined(BOTAN_SIMD_USE_NEON) + // NEON is also a & ~b + return SIMD_4x32(vbicq_u32(other.m_simd, m_simd)); +#elif defined(BOTAN_SIMD_USE_LSX) + // LSX is ~a & b + return SIMD_4x32(__lsx_vandn_v(m_simd, other.m_simd)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + // SIMD128 is a & ~b + return SIMD_4x32(wasm_v128_andnot(other.m_simd, m_simd)); +#endif + } + + /** + * Return copy *this with each word byte swapped + */ + BOTAN_FN_ISA_SIMD_4X32 SIMD_4x32 bswap() const noexcept { +#if defined(BOTAN_SIMD_USE_SSSE3) + const auto idx = _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3); + + return SIMD_4x32(_mm_shuffle_epi8(raw(), idx)); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + #ifdef BOTAN_SIMD_USE_VSX + return SIMD_4x32(vec_revb(m_simd)); + #else + const __vector unsigned char rev[1] = { + {3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12}, + }; + + return SIMD_4x32(vec_perm(m_simd, m_simd, rev[0])); + #endif + +#elif defined(BOTAN_SIMD_USE_NEON) + return SIMD_4x32(vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(m_simd)))); +#elif defined(BOTAN_SIMD_USE_LSX) + return SIMD_4x32(__lsx_vshuf4i_b(m_simd, 0b00011011)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_4x32(wasm_i8x16_shuffle(m_simd, m_simd, 3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12)); +#endif + } + + template + SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 shift_elems_left() const noexcept + requires(I <= 3) + { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_4x32(_mm_slli_si128(raw(), 4 * I)); +#elif defined(BOTAN_SIMD_USE_NEON) + return SIMD_4x32(vextq_u32(vdupq_n_u32(0), raw(), 4 - I)); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + const __vector unsigned int zero = vec_splat_u32(0); + + const __vector unsigned char shuf[3] = { + {16, 17, 18, 19, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, + {16, 17, 18, 19, 20, 21, 22, 23, 0, 1, 2, 3, 4, 5, 6, 7}, + {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 0, 1, 2, 3}, + }; + + return SIMD_4x32(vec_perm(raw(), zero, shuf[I - 1])); +#elif defined(BOTAN_SIMD_USE_LSX) + return SIMD_4x32(__lsx_vbsll_v(raw(), 4 * I)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + if constexpr(I == 0) { + return SIMD_4x32(m_simd); + } + + const auto zero = wasm_u32x4_const_splat(0); + if constexpr(I == 1) { + return SIMD_4x32(wasm_i8x16_shuffle(m_simd, zero, 16, 16, 16, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)); + } + if constexpr(I == 2) { + return SIMD_4x32(wasm_i8x16_shuffle(m_simd, zero, 16, 16, 16, 16, 16, 16, 16, 16, 0, 1, 2, 3, 4, 5, 6, 7)); + } + + return SIMD_4x32(wasm_i8x16_shuffle(m_simd, zero, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 0, 1, 2, 3)); +#endif + } + + template + SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 shift_elems_right() const noexcept + requires(I <= 3) + { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_4x32(_mm_srli_si128(raw(), 4 * I)); +#elif defined(BOTAN_SIMD_USE_NEON) + return SIMD_4x32(vextq_u32(raw(), vdupq_n_u32(0), I)); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + const __vector unsigned int zero = vec_splat_u32(0); + + const __vector unsigned char shuf[3] = { + {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, + {8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, + {12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}, + }; + + return SIMD_4x32(vec_perm(raw(), zero, shuf[I - 1])); +#elif defined(BOTAN_SIMD_USE_LSX) + return SIMD_4x32(__lsx_vbsrl_v(raw(), 4 * I)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + if constexpr(I == 0) { + return SIMD_4x32(m_simd); + } + + const auto zero = wasm_u32x4_const_splat(0); + if constexpr(I == 1) { + return SIMD_4x32( + wasm_i8x16_shuffle(m_simd, zero, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 16, 16, 16)); + } + if constexpr(I == 2) { + return SIMD_4x32( + wasm_i8x16_shuffle(m_simd, zero, 8, 9, 10, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16)); + } + + return SIMD_4x32( + wasm_i8x16_shuffle(m_simd, zero, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16)); +#endif + } + + /** + * 4x4 Transposition on SIMD registers + */ + static void BOTAN_FN_ISA_SIMD_4X32 transpose(SIMD_4x32& B0, + SIMD_4x32& B1, + SIMD_4x32& B2, + SIMD_4x32& B3) noexcept { +#if defined(BOTAN_SIMD_USE_SSSE3) + const __m128i T0 = _mm_unpacklo_epi32(B0.m_simd, B1.m_simd); + const __m128i T1 = _mm_unpacklo_epi32(B2.m_simd, B3.m_simd); + const __m128i T2 = _mm_unpackhi_epi32(B0.m_simd, B1.m_simd); + const __m128i T3 = _mm_unpackhi_epi32(B2.m_simd, B3.m_simd); + + B0.m_simd = _mm_unpacklo_epi64(T0, T1); + B1.m_simd = _mm_unpackhi_epi64(T0, T1); + B2.m_simd = _mm_unpacklo_epi64(T2, T3); + B3.m_simd = _mm_unpackhi_epi64(T2, T3); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + const __vector unsigned int T0 = vec_mergeh(B0.m_simd, B2.m_simd); + const __vector unsigned int T1 = vec_mergeh(B1.m_simd, B3.m_simd); + const __vector unsigned int T2 = vec_mergel(B0.m_simd, B2.m_simd); + const __vector unsigned int T3 = vec_mergel(B1.m_simd, B3.m_simd); + + B0.m_simd = vec_mergeh(T0, T1); + B1.m_simd = vec_mergel(T0, T1); + B2.m_simd = vec_mergeh(T2, T3); + B3.m_simd = vec_mergel(T2, T3); + +#elif defined(BOTAN_SIMD_USE_NEON) && defined(BOTAN_TARGET_ARCH_IS_ARM32) + const uint32x4x2_t T0 = vzipq_u32(B0.m_simd, B2.m_simd); + const uint32x4x2_t T1 = vzipq_u32(B1.m_simd, B3.m_simd); + const uint32x4x2_t O0 = vzipq_u32(T0.val[0], T1.val[0]); + const uint32x4x2_t O1 = vzipq_u32(T0.val[1], T1.val[1]); + + B0.m_simd = O0.val[0]; + B1.m_simd = O0.val[1]; + B2.m_simd = O1.val[0]; + B3.m_simd = O1.val[1]; + +#elif defined(BOTAN_SIMD_USE_NEON) && defined(BOTAN_TARGET_ARCH_IS_ARM64) + const uint32x4_t T0 = vzip1q_u32(B0.m_simd, B2.m_simd); + const uint32x4_t T2 = vzip2q_u32(B0.m_simd, B2.m_simd); + const uint32x4_t T1 = vzip1q_u32(B1.m_simd, B3.m_simd); + const uint32x4_t T3 = vzip2q_u32(B1.m_simd, B3.m_simd); + + B0.m_simd = vzip1q_u32(T0, T1); + B1.m_simd = vzip2q_u32(T0, T1); + B2.m_simd = vzip1q_u32(T2, T3); + B3.m_simd = vzip2q_u32(T2, T3); +#elif defined(BOTAN_SIMD_USE_LSX) + const __m128i T0 = __lsx_vilvl_w(B2.raw(), B0.raw()); + const __m128i T1 = __lsx_vilvh_w(B2.raw(), B0.raw()); + const __m128i T2 = __lsx_vilvl_w(B3.raw(), B1.raw()); + const __m128i T3 = __lsx_vilvh_w(B3.raw(), B1.raw()); + B0.m_simd = __lsx_vilvl_w(T2, T0); + B1.m_simd = __lsx_vilvh_w(T2, T0); + B2.m_simd = __lsx_vilvl_w(T3, T1); + B3.m_simd = __lsx_vilvh_w(T3, T1); +#elif defined(BOTAN_SIMD_USE_SIMD128) + const auto T0 = wasm_i32x4_shuffle(B0.m_simd, B2.m_simd, 0, 4, 1, 5); + const auto T2 = wasm_i32x4_shuffle(B0.m_simd, B2.m_simd, 2, 6, 3, 7); + const auto T1 = wasm_i32x4_shuffle(B1.m_simd, B3.m_simd, 0, 4, 1, 5); + const auto T3 = wasm_i32x4_shuffle(B1.m_simd, B3.m_simd, 2, 6, 3, 7); + + B0.m_simd = wasm_i32x4_shuffle(T0, T1, 0, 4, 1, 5); + B1.m_simd = wasm_i32x4_shuffle(T0, T1, 2, 6, 3, 7); + B2.m_simd = wasm_i32x4_shuffle(T2, T3, 0, 4, 1, 5); + B3.m_simd = wasm_i32x4_shuffle(T2, T3, 2, 6, 3, 7); +#endif + } + + static inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 choose(const SIMD_4x32& mask, + const SIMD_4x32& a, + const SIMD_4x32& b) noexcept { +#if defined(BOTAN_SIMD_USE_ALTIVEC) + return SIMD_4x32(vec_sel(b.raw(), a.raw(), mask.raw())); +#elif defined(BOTAN_SIMD_USE_NEON) + return SIMD_4x32(vbslq_u32(mask.raw(), a.raw(), b.raw())); +#elif defined(BOTAN_SIMD_USE_LSX) + return SIMD_4x32(__lsx_vbitsel_v(b.raw(), a.raw(), mask.raw())); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_4x32(wasm_v128_bitselect(a.raw(), b.raw(), mask.raw())); +#else + return (mask & a) ^ mask.andc(b); +#endif + } + + /** + * Byte-granularity blend: for each byte position, select from @p a where + * the corresponding mask byte is 0xFF, or from @p b where it is 0x00. + * + * Each byte of @p mask must be either 0x00 or 0xFF; other values produce + * undefined results. + */ + static inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 byte_blend(const SIMD_4x32& mask, + const SIMD_4x32& a, + const SIMD_4x32& b) noexcept { + return SIMD_4x32::choose(mask, a, b); + } + + /** + * Byte-granularity blend: for each byte position, select from @p a where + * the corresponding mask byte is 0xFF, or from @p b where it is 0x00. + * + * Each byte of @p mask must be either 0x00 or 0xFF; other values produce + * undefined results. + */ + static inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 byte_blend(uint32_t mask, + const SIMD_4x32& a, + const SIMD_4x32& b) noexcept { + return SIMD_4x32::byte_blend(SIMD_4x32::splat(mask), a, b); + } + + static inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 majority(const SIMD_4x32& x, + const SIMD_4x32& y, + const SIMD_4x32& z) noexcept { + return SIMD_4x32::choose(x ^ y, z, y); + } + + /** + * Byte shuffle + * + * This function assumes that each byte of idx is <= 16; it may produce incorrect + * results if this does not hold. + */ + static inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 byte_shuffle(const SIMD_4x32& tbl, const SIMD_4x32& idx) { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_4x32(_mm_shuffle_epi8(tbl.raw(), idx.raw())); +#elif defined(BOTAN_SIMD_USE_NEON) + const uint8x16_t tbl8 = vreinterpretq_u8_u32(tbl.raw()); + const uint8x16_t idx8 = vreinterpretq_u8_u32(idx.raw()); + + #if defined(BOTAN_TARGET_ARCH_IS_ARM32) + const uint8x8x2_t tbl2 = {vget_low_u8(tbl8), vget_high_u8(tbl8)}; + + return SIMD_4x32( + vreinterpretq_u32_u8(vcombine_u8(vtbl2_u8(tbl2, vget_low_u8(idx8)), vtbl2_u8(tbl2, vget_high_u8(idx8))))); + #else + return SIMD_4x32(vreinterpretq_u32_u8(vqtbl1q_u8(tbl8, idx8))); + #endif + +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + const auto r = vec_perm(reinterpret_cast<__vector signed char>(tbl.raw()), + reinterpret_cast<__vector signed char>(tbl.raw()), + reinterpret_cast<__vector unsigned char>(idx.raw())); + return SIMD_4x32(reinterpret_cast<__vector unsigned int>(r)); +#elif defined(BOTAN_SIMD_USE_LSX) + return SIMD_4x32(__lsx_vshuf_b(tbl.raw(), tbl.raw(), idx.raw())); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_4x32(wasm_i8x16_swizzle(tbl.raw(), idx.raw())); +#endif + } + + /** + * Byte shuffle with masking + * + * If the index is >= 128 then the output byte is set to zero. + * + * Warning: for indices between 16 and 128 this function may have different + * behaviors depending on the CPU; possibly the output is zero, tbl[idx % 16], + * or even undefined. + */ + inline static SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 masked_byte_shuffle(const SIMD_4x32& tbl, const SIMD_4x32& idx) { +#if defined(BOTAN_SIMD_USE_ALTIVEC) + const auto zero = vec_splat_s8(0x00); + const auto mask = vec_cmplt(reinterpret_cast<__vector signed char>(idx.raw()), zero); + const auto r = vec_perm(reinterpret_cast<__vector signed char>(tbl.raw()), + reinterpret_cast<__vector signed char>(tbl.raw()), + reinterpret_cast<__vector unsigned char>(idx.raw())); + return SIMD_4x32(reinterpret_cast<__vector unsigned int>(vec_sel(r, zero, mask))); +#elif defined(BOTAN_SIMD_USE_LSX) + /* + * The behavior of vshuf.b unfortunately differs among microarchitectures + * when the index is larger than the available elements. In LA664 CPUs, + * larger indices result in a zero byte, which is exactly what we want. + * Unfortunately on LA464 machines, the output is instead undefined. + * + * So we must use a slower sequence that handles the larger indices. + * If we had a way of knowing at compile time that we are on an LA664 + * or later, we could use __lsx_vshuf_b without the comparison or select. + */ + const auto zero = __lsx_vldi(0); + const auto r = __lsx_vshuf_b(zero, tbl.raw(), idx.raw()); + const auto mask = __lsx_vslti_bu(idx.raw(), 16); + return SIMD_4x32(__lsx_vbitsel_v(zero, r, mask)); +#else + // ARM, x86 and Wasm byte shuffles have the behavior we want for out of range idx + return SIMD_4x32::byte_shuffle(tbl, idx); +#endif + } + + static inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 alignr4(const SIMD_4x32& a, const SIMD_4x32& b) { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_4x32(_mm_alignr_epi8(a.raw(), b.raw(), 4)); +#elif defined(BOTAN_SIMD_USE_NEON) + return SIMD_4x32(vextq_u32(b.raw(), a.raw(), 1)); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + const __vector unsigned char mask = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; + return SIMD_4x32(vec_perm(b.raw(), a.raw(), mask)); +#elif defined(BOTAN_SIMD_USE_LSX) + const auto mask = SIMD_4x32(0x07060504, 0x0B0A0908, 0x0F0E0D0C, 0x13121110); + return SIMD_4x32(__lsx_vshuf_b(a.raw(), b.raw(), mask.raw())); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_4x32( + wasm_i8x16_shuffle(b.raw(), a.raw(), 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19)); +#endif + } + + static inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 alignr8(const SIMD_4x32& a, const SIMD_4x32& b) { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_4x32(_mm_alignr_epi8(a.raw(), b.raw(), 8)); +#elif defined(BOTAN_SIMD_USE_NEON) + return SIMD_4x32(vextq_u32(b.raw(), a.raw(), 2)); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + const __vector unsigned char mask = {8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; + return SIMD_4x32(vec_perm(b.raw(), a.raw(), mask)); +#elif defined(BOTAN_SIMD_USE_LSX) + return SIMD_4x32(__lsx_vshuf4i_d(a.raw(), b.raw(), 0b0011)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_4x32( + wasm_i8x16_shuffle(b.raw(), a.raw(), 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23)); +#endif + } + + /** + If the topmost bit of x is set, return a vector of all ones, otherwise a vector of all zeros + ie: (v >> 127) ? splat(0xFFFFFFFF) : zero; + + Most of the implementations work by doing an arithmetic shift of 31 to smear the top bits + of each word, followed by a broadcast of the top word. + */ + inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 top_bit_mask() const { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_4x32(_mm_shuffle_epi32(_mm_srai_epi32(raw(), 31), 0b11111111)); +#elif defined(BOTAN_SIMD_USE_NEON) + #if defined(BOTAN_TARGET_ARCH_IS_ARM32) + int32x4_t v = vshrq_n_s32(vreinterpretq_s32_u32(raw()), 31); + int32x2_t hi = vget_high_s32(v); + return SIMD_4x32(vreinterpretq_u32_s32(vdupq_lane_s32(hi, 1))); + #else + return SIMD_4x32(vreinterpretq_u32_s32(vdupq_laneq_s32(vshrq_n_s32(vreinterpretq_s32_u32(raw()), 31), 3))); + #endif +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + const __vector unsigned int shift = vec_splats(31U); + const __vector signed int shifted = vec_sra(reinterpret_cast<__vector signed int>(raw()), shift); + return SIMD_4x32(reinterpret_cast<__vector unsigned int>(vec_splat(shifted, 3))); +#elif defined(BOTAN_SIMD_USE_LSX) + return SIMD_4x32(__lsx_vshuf4i_w(__lsx_vsrai_w(raw(), 31), 0xFF)); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_4x32(wasm_i32x4_splat(wasm_i32x4_extract_lane(wasm_i32x4_shr(raw(), 31), 3))); +#endif + } + + /** + * Swap the upper and lower 64-bit halves of the vector + */ + inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 swap_halves() const { +#if defined(BOTAN_SIMD_USE_SSSE3) + return SIMD_4x32(_mm_shuffle_epi32(raw(), 0b01001110)); +#else + return SIMD_4x32::alignr8(*this, *this); +#endif + } + + native_simd_type BOTAN_FN_ISA_SIMD_4X32 raw() const noexcept { return m_simd; } + + explicit BOTAN_FN_ISA_SIMD_4X32 SIMD_4x32(native_simd_type x) noexcept : m_simd(x) {} + + private: + native_simd_type m_simd; +}; + +// NOLINTEND(portability-simd-intrinsics) + +template +inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 rotl(SIMD_4x32 input) { + return input.rotl(); +} + +template +inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 rotr(SIMD_4x32 input) { + return input.rotr(); +} + +// For Serpent: +template +inline SIMD_4x32 shl(SIMD_4x32 input) { + return input.shl(); +} + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/simd/simd_4x64/info.txt botan3-3.12.0+dfsg/src/lib/utils/simd/simd_4x64/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/simd/simd_4x64/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/simd/simd_4x64/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,21 @@ + +SIMD_4X64 -> 20250405 + + + +name -> "SIMD 4x64" +brief -> "Lightweight wrappers for SIMD 4x64" + + + +simd_4x64.h + + + +avx2 + + + +x86_64 +x32 + diff -Nru botan3-3.7.1+dfsg/src/lib/utils/simd/simd_4x64/simd_4x64.h botan3-3.12.0+dfsg/src/lib/utils/simd/simd_4x64/simd_4x64.h --- botan3-3.7.1+dfsg/src/lib/utils/simd/simd_4x64/simd_4x64.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/simd/simd_4x64/simd_4x64.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,206 @@ +/* +* (C) 2022,2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SIMD_4X64_H_ +#define BOTAN_SIMD_4X64_H_ + +#include +#include +#include +#include + +#if defined(BOTAN_TARGET_ARCH_SUPPORTS_AVX2) + #include +#endif + +namespace Botan { + +// NOLINTBEGIN(portability-simd-intrinsics) + +class SIMD_4x64 final { + public: + SIMD_4x64& operator=(const SIMD_4x64& other) = default; + SIMD_4x64(const SIMD_4x64& other) = default; + + SIMD_4x64& operator=(SIMD_4x64&& other) = default; + SIMD_4x64(SIMD_4x64&& other) = default; + + ~SIMD_4x64() = default; + + // zero initialized + BOTAN_FN_ISA_SIMD_4X64 SIMD_4x64() : m_simd(_mm256_setzero_si256()) {} + + // Load two halves at different addresses + static BOTAN_FN_ISA_SIMD_4X64 SIMD_4x64 load_le2(const void* lo, const void* hi) { + return SIMD_4x64( + _mm256_loadu2_m128i(reinterpret_cast(lo), reinterpret_cast(hi))); + } + + static BOTAN_FN_ISA_SIMD_4X64 SIMD_4x64 load_be2(const void* lo, const void* hi) { + return SIMD_4x64::load_le2(lo, hi).bswap(); + } + + static BOTAN_FN_ISA_SIMD_4X64 SIMD_4x64 load_le(const void* in) { + return SIMD_4x64(_mm256_loadu_si256(reinterpret_cast(in))); + } + + static BOTAN_FN_ISA_SIMD_4X64 SIMD_4x64 load_be(const void* in) { return SIMD_4x64::load_le(in).bswap(); } + + static BOTAN_FN_ISA_SIMD_4X64 SIMD_4x64 broadcast_2x64(const uint64_t* in) { + return SIMD_4x64(_mm256_broadcastsi128_si256(_mm_loadu_si128(reinterpret_cast(in)))); + } + + SIMD_4x64 BOTAN_FN_ISA_SIMD_4X64 bswap() const { + const auto idx = _mm256_set_epi8( + 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7); + + return SIMD_4x64(_mm256_shuffle_epi8(m_simd, idx)); + } + + void BOTAN_FN_ISA_SIMD_4X64 store_le(uint64_t out[4]) const { this->store_le(reinterpret_cast(out)); } + + BOTAN_FN_ISA_SIMD_4X64 void store_le(uint8_t out[]) const { + _mm256_storeu_si256(reinterpret_cast<__m256i*>(out), m_simd); + } + + BOTAN_FN_ISA_SIMD_4X64 void store_le2(void* outh, void* outl) { + _mm256_storeu2_m128i(reinterpret_cast<__m128i*>(outh), reinterpret_cast<__m128i*>(outl), m_simd); + } + + BOTAN_FN_ISA_SIMD_4X64 void store_be(uint8_t out[]) const { bswap().store_le(out); } + + SIMD_4x64 BOTAN_FN_ISA_SIMD_4X64 operator+(const SIMD_4x64& other) const { + SIMD_4x64 retval(*this); + retval += other; + return retval; + } + + SIMD_4x64 BOTAN_FN_ISA_SIMD_4X64 operator^(const SIMD_4x64& other) const { + SIMD_4x64 retval(*this); + retval ^= other; + return retval; + } + + SIMD_4x64 BOTAN_FN_ISA_SIMD_4X64 operator&(const SIMD_4x64& other) const { + SIMD_4x64 retval(*this); + retval &= other; + return retval; + } + + SIMD_4x64 BOTAN_FN_ISA_SIMD_4X64 operator|(const SIMD_4x64& other) const { + SIMD_4x64 retval(*this); + retval |= other; + return retval; + } + + BOTAN_FN_ISA_SIMD_4X64 void operator+=(const SIMD_4x64& other) { + m_simd = _mm256_add_epi64(m_simd, other.m_simd); + } + + BOTAN_FN_ISA_SIMD_4X64 void operator^=(const SIMD_4x64& other) { + m_simd = _mm256_xor_si256(m_simd, other.m_simd); + } + + BOTAN_FN_ISA_SIMD_4X64 void operator&=(const SIMD_4x64& other) { + m_simd = _mm256_and_si256(m_simd, other.m_simd); + } + + BOTAN_FN_ISA_SIMD_4X64 void operator|=(const SIMD_4x64& other) { m_simd = _mm256_or_si256(m_simd, other.m_simd); } + + template + BOTAN_FN_ISA_SIMD_4X64 SIMD_4x64 rotr() const + requires(ROT > 0 && ROT < 64) + { +#if defined(__AVX512VL__) + return SIMD_4x64(_mm256_ror_epi64(m_simd, ROT)); +#else + if constexpr(ROT == 8) { + auto shuf_rot_8 = + _mm256_set_epi64x(0x080f0e0d0c0b0a09, 0x0007060504030201, 0x080f0e0d0c0b0a09, 0x0007060504030201); + + return SIMD_4x64(_mm256_shuffle_epi8(m_simd, shuf_rot_8)); + } else if constexpr(ROT == 16) { + auto shuf_rot_16 = + _mm256_set_epi64x(0x09080f0e0d0c0b0a, 0x0100070605040302, 0x09080f0e0d0c0b0a, 0x0100070605040302); + + return SIMD_4x64(_mm256_shuffle_epi8(m_simd, shuf_rot_16)); + } else if constexpr(ROT == 24) { + auto shuf_rot_24 = + _mm256_set_epi64x(0x0a09080f0e0d0c0b, 0x0201000706050403, 0x0a09080f0e0d0c0b, 0x0201000706050403); + + return SIMD_4x64(_mm256_shuffle_epi8(m_simd, shuf_rot_24)); + } else if constexpr(ROT == 32) { + auto shuf_rot_32 = + _mm256_set_epi64x(0x0b0a09080f0e0d0c, 0x0302010007060504, 0x0b0a09080f0e0d0c, 0x0302010007060504); + + return SIMD_4x64(_mm256_shuffle_epi8(m_simd, shuf_rot_32)); + } else { + return SIMD_4x64(_mm256_or_si256(_mm256_srli_epi64(m_simd, static_cast(ROT)), + _mm256_slli_epi64(m_simd, static_cast(64 - ROT)))); + } +#endif + } + + template + SIMD_4x64 BOTAN_FN_ISA_SIMD_4X64 rotl() const { + return this->rotr<64 - ROT>(); + } + + template + SIMD_4x64 BOTAN_FN_ISA_SIMD_4X64 shr() const noexcept { + return SIMD_4x64(_mm256_srli_epi64(m_simd, SHIFT)); + } + + template + SIMD_4x64 BOTAN_FN_ISA_SIMD_4X64 shl() const noexcept { + return SIMD_4x64(_mm256_slli_epi64(m_simd, SHIFT)); + } + + static SIMD_4x64 BOTAN_FN_ISA_SIMD_4X64 alignr8(const SIMD_4x64& a, const SIMD_4x64& b) { + return SIMD_4x64(_mm256_alignr_epi8(a.m_simd, b.m_simd, 8)); + } + + // Argon2 specific operation + static BOTAN_FN_ISA_SIMD_4X64 SIMD_4x64 mul2_32(SIMD_4x64 x, SIMD_4x64 y) { + const __m256i m = _mm256_mul_epu32(x.m_simd, y.m_simd); + return SIMD_4x64(_mm256_add_epi64(m, m)); + } + + template + static BOTAN_FN_ISA_SIMD_4X64 SIMD_4x64 permute_4x64(SIMD_4x64 x) { + return SIMD_4x64(_mm256_permute4x64_epi64(x.m_simd, CTRL)); + } + + // Argon2 specific + static void BOTAN_FN_ISA_SIMD_4X64 twist(SIMD_4x64& B, SIMD_4x64& C, SIMD_4x64& D) { + B = SIMD_4x64::permute_4x64<0b00'11'10'01>(B); + C = SIMD_4x64::permute_4x64<0b01'00'11'10>(C); + D = SIMD_4x64::permute_4x64<0b10'01'00'11>(D); + } + + // Argon2 specific + static void BOTAN_FN_ISA_SIMD_4X64 untwist(SIMD_4x64& B, SIMD_4x64& C, SIMD_4x64& D) { + B = SIMD_4x64::permute_4x64<0b10'01'00'11>(B); + C = SIMD_4x64::permute_4x64<0b01'00'11'10>(C); + D = SIMD_4x64::permute_4x64<0b00'11'10'01>(D); + } + + BOTAN_FN_ISA_SIMD_4X64 + static SIMD_4x64 splat(uint64_t v) { return SIMD_4x64(_mm256_set1_epi64x(v)); } + + __m256i BOTAN_FN_ISA_SIMD_4X64 raw() const noexcept { return m_simd; } + + explicit BOTAN_FN_ISA_SIMD_4X64 SIMD_4x64(__m256i x) : m_simd(x) {} + + private: + __m256i m_simd; +}; + +// NOLINTEND(portability-simd-intrinsics) + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/simd/simd_8x64/info.txt botan3-3.12.0+dfsg/src/lib/utils/simd/simd_8x64/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/simd/simd_8x64/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/simd/simd_8x64/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,21 @@ + +SIMD_8X64 -> 20250427 + + + +name -> "SIMD 8x64" +brief -> "Lightweight wrappers for SIMD 8x64" + + + +simd_8x64.h + + + +avx512 + + + +x86_64 +x32 + diff -Nru botan3-3.7.1+dfsg/src/lib/utils/simd/simd_8x64/simd_8x64.h botan3-3.12.0+dfsg/src/lib/utils/simd/simd_8x64/simd_8x64.h --- botan3-3.7.1+dfsg/src/lib/utils/simd/simd_8x64/simd_8x64.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/simd/simd_8x64/simd_8x64.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,193 @@ +/* +* (C) 2022,2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SIMD_8X64_H_ +#define BOTAN_SIMD_8X64_H_ + +#include +#include +#include +#include +#include + +namespace Botan { + +// NOLINTBEGIN(portability-simd-intrinsics) + +class SIMD_8x64 final { + public: + SIMD_8x64& operator=(const SIMD_8x64& other) = default; + SIMD_8x64(const SIMD_8x64& other) = default; + + SIMD_8x64& operator=(SIMD_8x64&& other) = default; + SIMD_8x64(SIMD_8x64&& other) = default; + + ~SIMD_8x64() = default; + + // zero initialized + BOTAN_FN_ISA_SIMD_8X64 SIMD_8x64() : m_simd(_mm512_setzero_si512()) {} + + // Load two halves at different addresses + static BOTAN_FN_ISA_SIMD_8X64 SIMD_8x64 load_le4(const void* in0, + const void* in1, + const void* in2, + const void* in3) { + auto r = _mm512_setzero_si512(); + r = _mm512_inserti32x4(r, _mm_loadu_si128(reinterpret_cast(in0)), 3); + r = _mm512_inserti32x4(r, _mm_loadu_si128(reinterpret_cast(in1)), 2); + r = _mm512_inserti32x4(r, _mm_loadu_si128(reinterpret_cast(in2)), 1); + r = _mm512_inserti32x4(r, _mm_loadu_si128(reinterpret_cast(in3)), 0); + return SIMD_8x64(r); + } + + static BOTAN_FN_ISA_SIMD_8X64 SIMD_8x64 load_be4(const void* in0, + const void* in1, + const void* in2, + const void* in3) { + return SIMD_8x64::load_le4(in0, in1, in2, in3).bswap(); + } + + static BOTAN_FN_ISA_SIMD_8X64 SIMD_8x64 load_le(const void* in) { + return SIMD_8x64(_mm512_loadu_si512(reinterpret_cast(in))); + } + + BOTAN_FN_ISA_AVX512 + static SIMD_8x64 broadcast_2x64(const uint64_t* in) { + return SIMD_8x64(_mm512_broadcast_i64x2(_mm_loadu_si128(reinterpret_cast(in)))); + } + + static BOTAN_FN_ISA_SIMD_8X64 SIMD_8x64 load_be(const void* in) { return SIMD_8x64::load_le(in).bswap(); } + + SIMD_8x64 BOTAN_FN_ISA_SIMD_8X64 bswap() const { + // clang-format off + const auto idx = _mm512_set_epi8( + 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7); + // clang-format on + + return SIMD_8x64(_mm512_shuffle_epi8(m_simd, idx)); + } + + void store_le(uint64_t out[8]) const { this->store_le(reinterpret_cast(out)); } + + BOTAN_FN_ISA_SIMD_8X64 void store_le(uint8_t out[]) const { + _mm512_storeu_si512(reinterpret_cast<__m512i*>(out), m_simd); + } + + BOTAN_FN_ISA_SIMD_8X64 void store_be(uint8_t out[]) const { bswap().store_le(out); } + + BOTAN_FN_ISA_SIMD_8X64 void store_le4(void* out0, void* out1, void* out2, void* out3) { + _mm_storeu_si128(reinterpret_cast<__m128i*>(out0), _mm512_extracti32x4_epi32(m_simd, 3)); + _mm_storeu_si128(reinterpret_cast<__m128i*>(out1), _mm512_extracti32x4_epi32(m_simd, 2)); + _mm_storeu_si128(reinterpret_cast<__m128i*>(out2), _mm512_extracti32x4_epi32(m_simd, 1)); + _mm_storeu_si128(reinterpret_cast<__m128i*>(out3), _mm512_extracti32x4_epi32(m_simd, 0)); + } + + SIMD_8x64 BOTAN_FN_ISA_SIMD_8X64 operator+(const SIMD_8x64& other) const { + SIMD_8x64 retval(*this); + retval += other; + return retval; + } + + SIMD_8x64 BOTAN_FN_ISA_SIMD_8X64 operator^(const SIMD_8x64& other) const { + SIMD_8x64 retval(*this); + retval ^= other; + return retval; + } + + SIMD_8x64 BOTAN_FN_ISA_SIMD_8X64 operator&(const SIMD_8x64& other) const { + SIMD_8x64 retval(*this); + retval &= other; + return retval; + } + + SIMD_8x64 BOTAN_FN_ISA_SIMD_8X64 operator|(const SIMD_8x64& other) const { + SIMD_8x64 retval(*this); + retval |= other; + return retval; + } + + BOTAN_FN_ISA_SIMD_8X64 void operator+=(const SIMD_8x64& other) { + m_simd = _mm512_add_epi64(m_simd, other.m_simd); + } + + BOTAN_FN_ISA_SIMD_8X64 void operator^=(const SIMD_8x64& other) { + m_simd = _mm512_xor_si512(m_simd, other.m_simd); + } + + BOTAN_FN_ISA_SIMD_8X64 void operator&=(const SIMD_8x64& other) { + m_simd = _mm512_and_si512(m_simd, other.m_simd); + } + + BOTAN_FN_ISA_SIMD_8X64 void operator|=(const SIMD_8x64& other) { m_simd = _mm512_or_si512(m_simd, other.m_simd); } + + template + BOTAN_FN_ISA_SIMD_8X64 SIMD_8x64 rotr() const + requires(ROT > 0 && ROT < 64) + { + return SIMD_8x64(_mm512_ror_epi64(m_simd, ROT)); + } + + template + BOTAN_FN_ISA_SIMD_8X64 SIMD_8x64 rotl() const { + return this->rotr<64 - ROT>(); + } + + template + SIMD_8x64 BOTAN_FN_ISA_SIMD_8X64 shr() const noexcept { + return SIMD_8x64(_mm512_srli_epi64(m_simd, SHIFT)); + } + + template + SIMD_8x64 BOTAN_FN_ISA_SIMD_8X64 shl() const noexcept { + return SIMD_8x64(_mm512_slli_epi64(m_simd, SHIFT)); + } + + static SIMD_8x64 BOTAN_FN_ISA_SIMD_8X64 alignr8(const SIMD_8x64& a, const SIMD_8x64& b) { + return SIMD_8x64(_mm512_alignr_epi8(a.m_simd, b.m_simd, 8)); + } + + BOTAN_FN_ISA_SIMD_8X64 + static SIMD_8x64 splat(uint64_t v) { return SIMD_8x64(_mm512_set1_epi64(v)); } + + // Argon2 specific operation + static BOTAN_FN_ISA_SIMD_8X64 SIMD_8x64 mul2_32(SIMD_8x64 x, SIMD_8x64 y) { + const __m512i m = _mm512_mul_epu32(x.m_simd, y.m_simd); + return SIMD_8x64(_mm512_add_epi64(m, m)); + } + + // Argon2 specific - twist/untwist permutes within two independent 4-lane groups + static void BOTAN_FN_ISA_SIMD_8X64 twist(SIMD_8x64& B, SIMD_8x64& C, SIMD_8x64& D) { + const auto b_perm = _mm512_set_epi64(4, 7, 6, 5, 0, 3, 2, 1); + const auto c_perm = _mm512_set_epi64(5, 4, 7, 6, 1, 0, 3, 2); + const auto d_perm = _mm512_set_epi64(6, 5, 4, 7, 2, 1, 0, 3); + B = SIMD_8x64(_mm512_permutexvar_epi64(b_perm, B.m_simd)); + C = SIMD_8x64(_mm512_permutexvar_epi64(c_perm, C.m_simd)); + D = SIMD_8x64(_mm512_permutexvar_epi64(d_perm, D.m_simd)); + } + + static void BOTAN_FN_ISA_SIMD_8X64 untwist(SIMD_8x64& B, SIMD_8x64& C, SIMD_8x64& D) { + const auto b_perm = _mm512_set_epi64(6, 5, 4, 7, 2, 1, 0, 3); + const auto c_perm = _mm512_set_epi64(5, 4, 7, 6, 1, 0, 3, 2); + const auto d_perm = _mm512_set_epi64(4, 7, 6, 5, 0, 3, 2, 1); + B = SIMD_8x64(_mm512_permutexvar_epi64(b_perm, B.m_simd)); + C = SIMD_8x64(_mm512_permutexvar_epi64(c_perm, C.m_simd)); + D = SIMD_8x64(_mm512_permutexvar_epi64(d_perm, D.m_simd)); + } + + __m512i BOTAN_FN_ISA_SIMD_8X64 raw() const noexcept { return m_simd; } + + explicit BOTAN_FN_ISA_SIMD_8X64 SIMD_8x64(__m512i x) : m_simd(x) {} + + private: + __m512i m_simd; +}; + +// NOLINTEND(portability-simd-intrinsics) + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/simd/simd_avx2/info.txt botan3-3.12.0+dfsg/src/lib/utils/simd/simd_avx2/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/simd/simd_avx2/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/simd/simd_avx2/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + SIMD_AVX2 -> 20180824 - + name -> "AVX2" diff -Nru botan3-3.7.1+dfsg/src/lib/utils/simd/simd_avx2/simd_avx2.h botan3-3.12.0+dfsg/src/lib/utils/simd/simd_avx2/simd_avx2.h --- botan3-3.7.1+dfsg/src/lib/utils/simd/simd_avx2/simd_avx2.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/simd/simd_avx2/simd_avx2.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,11 +9,12 @@ #include #include +#include #include namespace Botan { -#define BOTAN_AVX2_FN BOTAN_FUNC_ISA("avx2") +// NOLINTBEGIN(portability-simd-intrinsics) class SIMD_8x32 final { public: @@ -25,15 +26,16 @@ ~SIMD_8x32() = default; - BOTAN_AVX2_FN - BOTAN_FORCE_INLINE SIMD_8x32() noexcept { m_avx2 = _mm256_setzero_si256(); } + BOTAN_FN_ISA_AVX2 + BOTAN_FORCE_INLINE SIMD_8x32() noexcept : m_avx2(_mm256_setzero_si256()) {} - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 explicit SIMD_8x32(const uint32_t B[8]) noexcept { + // NOLINTNEXTLINE(*-prefer-member-initializer) m_avx2 = _mm256_loadu_si256(reinterpret_cast(B)); } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 explicit SIMD_8x32(uint32_t B0, uint32_t B1, uint32_t B2, @@ -42,43 +44,76 @@ uint32_t B5, uint32_t B6, uint32_t B7) noexcept { + // NOLINTNEXTLINE(*-prefer-member-initializer) m_avx2 = _mm256_set_epi32(B7, B6, B5, B4, B3, B2, B1, B0); } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 + explicit SIMD_8x32(uint32_t B0, uint32_t B1, uint32_t B2, uint32_t B3) noexcept { + // NOLINTNEXTLINE(*-prefer-member-initializer) + m_avx2 = _mm256_set_epi32(B3, B2, B1, B0, B3, B2, B1, B0); + } + + BOTAN_FN_ISA_AVX2 static SIMD_8x32 splat(uint32_t B) noexcept { return SIMD_8x32(_mm256_set1_epi32(B)); } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 static SIMD_8x32 load_le(const uint8_t* in) noexcept { return SIMD_8x32(_mm256_loadu_si256(reinterpret_cast(in))); } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 + static SIMD_8x32 load_le(const uint32_t* in) noexcept { + return SIMD_8x32(_mm256_loadu_si256(reinterpret_cast(in))); + } + + BOTAN_FN_ISA_AVX2 static SIMD_8x32 load_le128(const uint8_t* in) noexcept { return SIMD_8x32(_mm256_broadcastsi128_si256(_mm_loadu_si128(reinterpret_cast(in)))); } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 static SIMD_8x32 load_le128(const uint32_t* in) noexcept { return SIMD_8x32(_mm256_broadcastsi128_si256(_mm_loadu_si128(reinterpret_cast(in)))); } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 static SIMD_8x32 load_be(const uint8_t* in) noexcept { return load_le(in).bswap(); } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 void store_le(uint8_t out[]) const noexcept { _mm256_storeu_si256(reinterpret_cast<__m256i*>(out), m_avx2); } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 + void store_le(uint32_t out[]) const noexcept { _mm256_storeu_si256(reinterpret_cast<__m256i*>(out), m_avx2); } + + BOTAN_FN_ISA_AVX2 void store_le128(uint8_t out[]) const noexcept { _mm_storeu_si128(reinterpret_cast<__m128i*>(out), _mm256_extracti128_si256(raw(), 0)); } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 + static SIMD_8x32 load_le128(const uint32_t in1[], const uint32_t in2[]) noexcept { + return SIMD_8x32( + _mm256_loadu2_m128i(reinterpret_cast(in2), reinterpret_cast(in1))); + } + + BOTAN_FN_ISA_AVX2 + static SIMD_8x32 load_be128(const uint8_t in1[], const uint8_t in2[]) noexcept { + return SIMD_8x32( + _mm256_loadu2_m128i(reinterpret_cast(in2), reinterpret_cast(in1))) + .bswap(); + } + + BOTAN_FN_ISA_AVX2 + void store_le128(uint32_t out1[], uint32_t out2[]) const noexcept { + _mm256_storeu2_m128i(reinterpret_cast<__m128i*>(out2), reinterpret_cast<__m128i*>(out1), raw()); + } + + BOTAN_FN_ISA_AVX2 void store_be(uint8_t out[]) const noexcept { bswap().store_le(out); } template - BOTAN_AVX2_FN SIMD_8x32 rotl() const noexcept + BOTAN_FN_ISA_AVX2 SIMD_8x32 rotl() const noexcept requires(ROT > 0 && ROT < 32) { #if defined(__AVX512VL__) @@ -100,121 +135,132 @@ return SIMD_8x32(_mm256_shuffle_epi8(m_avx2, shuf_rotl_24)); } else { - return SIMD_8x32(_mm256_or_si256(_mm256_slli_epi32(m_avx2, static_cast(ROT)), - _mm256_srli_epi32(m_avx2, static_cast(32 - ROT)))); + return SIMD_8x32(_mm256_xor_si256(_mm256_slli_epi32(m_avx2, static_cast(ROT)), + _mm256_srli_epi32(m_avx2, static_cast(32 - ROT)))); } #endif } template - BOTAN_AVX2_FN SIMD_8x32 rotr() const noexcept { + BOTAN_FN_ISA_AVX2 SIMD_8x32 rotr() const noexcept { return this->rotl<32 - ROT>(); } - SIMD_8x32 BOTAN_AVX2_FN sigma0() const noexcept { - const SIMD_8x32 rot1 = this->rotr<2>(); - const SIMD_8x32 rot2 = this->rotr<13>(); - const SIMD_8x32 rot3 = this->rotr<22>(); - return rot1 ^ rot2 ^ rot3; + SIMD_8x32 BOTAN_FN_ISA_AVX2 sigma0() const noexcept { + const SIMD_8x32 r1 = this->rotr<2>(); + const SIMD_8x32 r2 = this->rotr<13>(); + const SIMD_8x32 r3 = this->rotr<22>(); + return r1 ^ r2 ^ r3; } - SIMD_8x32 BOTAN_AVX2_FN sigma1() const noexcept { - const SIMD_8x32 rot1 = this->rotr<6>(); - const SIMD_8x32 rot2 = this->rotr<11>(); - const SIMD_8x32 rot3 = this->rotr<25>(); - return rot1 ^ rot2 ^ rot3; + SIMD_8x32 BOTAN_FN_ISA_AVX2 sigma1() const noexcept { + const SIMD_8x32 r1 = this->rotr<6>(); + const SIMD_8x32 r2 = this->rotr<11>(); + const SIMD_8x32 r3 = this->rotr<25>(); + return r1 ^ r2 ^ r3; } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 SIMD_8x32 operator+(const SIMD_8x32& other) const noexcept { SIMD_8x32 retval(*this); retval += other; return retval; } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 SIMD_8x32 operator-(const SIMD_8x32& other) const noexcept { SIMD_8x32 retval(*this); retval -= other; return retval; } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 SIMD_8x32 operator^(const SIMD_8x32& other) const noexcept { SIMD_8x32 retval(*this); retval ^= other; return retval; } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 SIMD_8x32 operator|(const SIMD_8x32& other) const noexcept { SIMD_8x32 retval(*this); retval |= other; return retval; } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 SIMD_8x32 operator&(const SIMD_8x32& other) const noexcept { SIMD_8x32 retval(*this); retval &= other; return retval; } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 void operator+=(const SIMD_8x32& other) { m_avx2 = _mm256_add_epi32(m_avx2, other.m_avx2); } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 void operator-=(const SIMD_8x32& other) { m_avx2 = _mm256_sub_epi32(m_avx2, other.m_avx2); } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 void operator^=(const SIMD_8x32& other) { m_avx2 = _mm256_xor_si256(m_avx2, other.m_avx2); } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 void operator^=(uint32_t other) { *this ^= SIMD_8x32::splat(other); } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 void operator|=(const SIMD_8x32& other) { m_avx2 = _mm256_or_si256(m_avx2, other.m_avx2); } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 void operator&=(const SIMD_8x32& other) { m_avx2 = _mm256_and_si256(m_avx2, other.m_avx2); } template - BOTAN_AVX2_FN SIMD_8x32 shl() const noexcept { + BOTAN_FN_ISA_AVX2 SIMD_8x32 shl() const noexcept { return SIMD_8x32(_mm256_slli_epi32(m_avx2, SHIFT)); } template - BOTAN_AVX2_FN SIMD_8x32 shr() const noexcept { + BOTAN_FN_ISA_AVX2 SIMD_8x32 shr() const noexcept { return SIMD_8x32(_mm256_srli_epi32(m_avx2, SHIFT)); } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 SIMD_8x32 operator~() const noexcept { return SIMD_8x32(_mm256_xor_si256(m_avx2, _mm256_set1_epi32(0xFFFFFFFF))); } // (~reg) & other - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 SIMD_8x32 andc(const SIMD_8x32& other) const noexcept { return SIMD_8x32(_mm256_andnot_si256(m_avx2, other.m_avx2)); } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 SIMD_8x32 bswap() const noexcept { - const uint8_t BSWAP_MASK[32] = {3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12, - 19, 18, 17, 16, 23, 22, 21, 20, 27, 26, 25, 24, 31, 30, 29, 28}; + alignas(32) const uint8_t BSWAP_TBL[32] = {3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12, + 19, 18, 17, 16, 23, 22, 21, 20, 27, 26, 25, 24, 31, 30, 29, 28}; - const __m256i bswap = _mm256_loadu_si256(reinterpret_cast(BSWAP_MASK)); + const __m256i bswap = _mm256_load_si256(reinterpret_cast(BSWAP_TBL)); const __m256i output = _mm256_shuffle_epi8(m_avx2, bswap); return SIMD_8x32(output); } - BOTAN_AVX2_FN + // Equivalent to rev_words().bswap() + BOTAN_FN_ISA_AVX2 + SIMD_8x32 reverse() const noexcept { + alignas(32) const uint8_t REV_TBL[32] = {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; + + const __m256i bswap = _mm256_load_si256(reinterpret_cast(REV_TBL)); + const __m256i output = _mm256_shuffle_epi8(m_avx2, bswap); + return SIMD_8x32(output); + } + + BOTAN_FN_ISA_AVX2 SIMD_8x32 rev_words() const noexcept { return SIMD_8x32(_mm256_shuffle_epi32(raw(), 0b00011011)); } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 static void transpose(SIMD_8x32& B0, SIMD_8x32& B1, SIMD_8x32& B2, SIMD_8x32& B3) noexcept { const __m256i T0 = _mm256_unpacklo_epi32(B0.m_avx2, B1.m_avx2); const __m256i T1 = _mm256_unpacklo_epi32(B2.m_avx2, B3.m_avx2); @@ -227,7 +273,25 @@ B3.m_avx2 = _mm256_unpackhi_epi64(T2, T3); } - BOTAN_AVX2_FN + static inline SIMD_8x32 BOTAN_FN_ISA_AVX2 alignr8(const SIMD_8x32& a, const SIMD_8x32& b) { + return SIMD_8x32(_mm256_alignr_epi8(a.raw(), b.raw(), 8)); + } + + template + BOTAN_FN_ISA_AVX2 SIMD_8x32 shift_elems_left() const noexcept + requires(I > 0 && I <= 3) + { + return SIMD_8x32(_mm256_slli_si256(raw(), 4 * I)); + } + + template + BOTAN_FN_ISA_AVX2 SIMD_8x32 shift_elems_right() const noexcept + requires(I > 0 && I <= 3) + { + return SIMD_8x32(_mm256_srli_si256(raw(), 4 * I)); + } + + BOTAN_FN_ISA_AVX2 static void transpose(SIMD_8x32& B0, SIMD_8x32& B1, SIMD_8x32& B2, @@ -245,40 +309,44 @@ swap_tops(B3, B7); } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 static SIMD_8x32 choose(const SIMD_8x32& mask, const SIMD_8x32& a, const SIMD_8x32& b) noexcept { #if defined(__AVX512VL__) - return _mm256_ternarylogic_epi32(mask.raw(), a.raw(), b.raw(), 0xca); + return SIMD_8x32(_mm256_ternarylogic_epi32(mask.raw(), a.raw(), b.raw(), 0xca)); #else return (mask & a) ^ mask.andc(b); #endif } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 static SIMD_8x32 majority(const SIMD_8x32& x, const SIMD_8x32& y, const SIMD_8x32& z) noexcept { #if defined(__AVX512VL__) - return _mm256_ternarylogic_epi32(x.raw(), y.raw(), z.raw(), 0xe8); + return SIMD_8x32(_mm256_ternarylogic_epi32(x.raw(), y.raw(), z.raw(), 0xe8)); #else return SIMD_8x32::choose(x ^ y, z, y); #endif } - BOTAN_AVX2_FN + static inline SIMD_8x32 BOTAN_FN_ISA_AVX2 byte_shuffle(const SIMD_8x32& tbl, const SIMD_8x32& idx) { + return SIMD_8x32(_mm256_shuffle_epi8(tbl.raw(), idx.raw())); + } + + BOTAN_FN_ISA_AVX2 static void reset_registers() noexcept { _mm256_zeroupper(); } - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 static void zero_registers() noexcept { _mm256_zeroall(); } - __m256i BOTAN_AVX2_FN raw() const noexcept { return m_avx2; } + __m256i BOTAN_FN_ISA_AVX2 raw() const noexcept { return m_avx2; } - BOTAN_AVX2_FN - SIMD_8x32(__m256i x) noexcept : m_avx2(x) {} + BOTAN_FN_ISA_AVX2 + explicit SIMD_8x32(__m256i x) noexcept : m_avx2(x) {} private: - BOTAN_AVX2_FN + BOTAN_FN_ISA_AVX2 static void swap_tops(SIMD_8x32& A, SIMD_8x32& B) { - SIMD_8x32 T0 = _mm256_permute2x128_si256(A.raw(), B.raw(), 0 + (2 << 4)); - SIMD_8x32 T1 = _mm256_permute2x128_si256(A.raw(), B.raw(), 1 + (3 << 4)); + auto T0 = SIMD_8x32(_mm256_permute2x128_si256(A.raw(), B.raw(), 0 + (2 << 4))); + auto T1 = SIMD_8x32(_mm256_permute2x128_si256(A.raw(), B.raw(), 1 + (3 << 4))); A = T0; B = T1; } @@ -286,19 +354,21 @@ __m256i m_avx2; }; +// NOLINTEND(portability-simd-intrinsics) + template -inline SIMD_8x32 rotl(SIMD_8x32 input) { +inline SIMD_8x32 BOTAN_FN_ISA_AVX2 rotl(SIMD_8x32 input) { return input.rotl(); } template -inline SIMD_8x32 rotr(SIMD_8x32 input) { +inline SIMD_8x32 BOTAN_FN_ISA_AVX2 rotr(SIMD_8x32 input) { return input.rotr(); } // For Serpent: template -inline SIMD_8x32 shl(SIMD_8x32 input) { +inline SIMD_8x32 BOTAN_FN_ISA_AVX2 shl(SIMD_8x32 input) { return input.shl(); } diff -Nru botan3-3.7.1+dfsg/src/lib/utils/simd/simd_avx2/simd_avx2_gfni.h botan3-3.12.0+dfsg/src/lib/utils/simd/simd_avx2/simd_avx2_gfni.h --- botan3-3.7.1+dfsg/src/lib/utils/simd/simd_avx2/simd_avx2_gfni.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/simd/simd_avx2/simd_avx2_gfni.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,59 +8,23 @@ #define BOTAN_SIMD_AVX2_GFNI_H_ #include -#include -#include -namespace Botan { - -#define BOTAN_GFNI_ISA "gfni,avx2" - -// Helper for defining GFNI constants -consteval uint64_t gfni_matrix(std::string_view s) { - uint64_t matrix = 0; - size_t bit_cnt = 0; - uint8_t row = 0; - - for(char c : s) { - if(c == ' ' || c == '\n') { - continue; - } - if(c != '0' && c != '1') { - throw std::runtime_error("gfni_matrix: invalid bit value"); - } - - if(c == '1') { - row |= 0x80 >> (7 - bit_cnt % 8); - } - bit_cnt++; - - if(bit_cnt % 8 == 0) { - matrix <<= 8; - matrix |= row; - row = 0; - } - } - - if(bit_cnt != 64) { - throw std::runtime_error("gfni_matrix: invalid bit count"); - } +#include +#include - return matrix; -} +namespace Botan { template -BOTAN_FUNC_ISA_INLINE(BOTAN_GFNI_ISA) -SIMD_8x32 gf2p8affine(const SIMD_8x32& x) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_GFNI SIMD_8x32 gf2p8affine(const SIMD_8x32& x) { return SIMD_8x32(_mm256_gf2p8affine_epi64_epi8(x.raw(), _mm256_set1_epi64x(A), B)); } template -BOTAN_FUNC_ISA_INLINE(BOTAN_GFNI_ISA) -SIMD_8x32 gf2p8affineinv(const SIMD_8x32& x) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_GFNI SIMD_8x32 gf2p8affineinv(const SIMD_8x32& x) { return SIMD_8x32(_mm256_gf2p8affineinv_epi64_epi8(x.raw(), _mm256_set1_epi64x(A), B)); } -BOTAN_FUNC_ISA_INLINE(BOTAN_GFNI_ISA) SIMD_8x32 gf2p8mul(const SIMD_8x32& a, const SIMD_8x32& b) { +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX2_GFNI SIMD_8x32 gf2p8mul(const SIMD_8x32& a, const SIMD_8x32& b) { return SIMD_8x32(_mm256_gf2p8mul_epi8(a.raw(), b.raw())); } diff -Nru botan3-3.7.1+dfsg/src/lib/utils/simd/simd_avx512/info.txt botan3-3.12.0+dfsg/src/lib/utils/simd/simd_avx512/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/simd/simd_avx512/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/simd/simd_avx512/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + SIMD_AVX512 -> 20230101 - + name -> "AVX512" @@ -13,4 +13,5 @@ simd_avx512.h +simd_avx512_gfni.h diff -Nru botan3-3.7.1+dfsg/src/lib/utils/simd/simd_avx512/simd_avx512.h botan3-3.12.0+dfsg/src/lib/utils/simd/simd_avx512/simd_avx512.h --- botan3-3.7.1+dfsg/src/lib/utils/simd/simd_avx512/simd_avx512.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/simd/simd_avx512/simd_avx512.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,11 +9,12 @@ #include #include +#include #include namespace Botan { -#define BOTAN_AVX512_FN BOTAN_FUNC_ISA("avx512f,avx512dq,avx512bw") +// NOLINTBEGIN(portability-simd-intrinsics) class SIMD_16x32 final { public: @@ -23,13 +24,18 @@ SIMD_16x32& operator=(SIMD_16x32&& other) = default; SIMD_16x32(SIMD_16x32&& other) = default; - BOTAN_AVX512_FN - BOTAN_FORCE_INLINE SIMD_16x32() { m_avx512 = _mm512_setzero_si512(); } + ~SIMD_16x32() = default; - BOTAN_AVX512_FN - explicit SIMD_16x32(const uint32_t B[16]) { m_avx512 = _mm512_loadu_si512(reinterpret_cast(B)); } + BOTAN_FN_ISA_AVX512 + BOTAN_FORCE_INLINE SIMD_16x32() : m_avx512(_mm512_setzero_si512()) {} - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 + explicit SIMD_16x32(const uint32_t B[16]) { + // NOLINTNEXTLINE(*-prefer-member-initializer) + m_avx512 = _mm512_loadu_si512(reinterpret_cast(B)); + } + + BOTAN_FN_ISA_AVX512 explicit SIMD_16x32(uint32_t B0, uint32_t B1, uint32_t B2, @@ -46,130 +52,131 @@ uint32_t BD, uint32_t BE, uint32_t BF) { + // NOLINTNEXTLINE(*-prefer-member-initializer) m_avx512 = _mm512_set_epi32(BF, BE, BD, BC, BB, BA, B9, B8, B7, B6, B5, B4, B3, B2, B1, B0); } - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 static SIMD_16x32 splat(uint32_t B) { return SIMD_16x32(_mm512_set1_epi32(B)); } - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 static SIMD_16x32 load_le(const uint8_t* in) { return SIMD_16x32(_mm512_loadu_si512(reinterpret_cast(in))); } - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 static SIMD_16x32 load_be(const uint8_t* in) { return load_le(in).bswap(); } - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 void store_le(uint8_t out[]) const { _mm512_storeu_si512(reinterpret_cast<__m512i*>(out), m_avx512); } - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 void store_be(uint8_t out[]) const { bswap().store_le(out); } template - BOTAN_AVX512_FN SIMD_16x32 rotl() const + BOTAN_FN_ISA_AVX512 SIMD_16x32 rotl() const requires(ROT > 0 && ROT < 32) { return SIMD_16x32(_mm512_rol_epi32(m_avx512, ROT)); } template - BOTAN_AVX512_FN SIMD_16x32 rotr() const { + BOTAN_FN_ISA_AVX512 SIMD_16x32 rotr() const { return this->rotl<32 - ROT>(); } - SIMD_16x32 BOTAN_AVX512_FN sigma0() const { - const SIMD_16x32 rot1 = this->rotr<2>(); - const SIMD_16x32 rot2 = this->rotr<13>(); - const SIMD_16x32 rot3 = this->rotr<22>(); - return rot1 ^ rot2 ^ rot3; + SIMD_16x32 BOTAN_FN_ISA_AVX512 sigma0() const { + const SIMD_16x32 r1 = this->rotr<2>(); + const SIMD_16x32 r2 = this->rotr<13>(); + const SIMD_16x32 r3 = this->rotr<22>(); + return r1 ^ r2 ^ r3; } - SIMD_16x32 BOTAN_AVX512_FN sigma1() const { - const SIMD_16x32 rot1 = this->rotr<6>(); - const SIMD_16x32 rot2 = this->rotr<11>(); - const SIMD_16x32 rot3 = this->rotr<25>(); - return rot1 ^ rot2 ^ rot3; + SIMD_16x32 BOTAN_FN_ISA_AVX512 sigma1() const { + const SIMD_16x32 r1 = this->rotr<6>(); + const SIMD_16x32 r2 = this->rotr<11>(); + const SIMD_16x32 r3 = this->rotr<25>(); + return r1 ^ r2 ^ r3; } - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 SIMD_16x32 operator+(const SIMD_16x32& other) const { SIMD_16x32 retval(*this); retval += other; return retval; } - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 SIMD_16x32 operator-(const SIMD_16x32& other) const { SIMD_16x32 retval(*this); retval -= other; return retval; } - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 SIMD_16x32 operator^(const SIMD_16x32& other) const { SIMD_16x32 retval(*this); retval ^= other; return retval; } - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 SIMD_16x32 operator|(const SIMD_16x32& other) const { SIMD_16x32 retval(*this); retval |= other; return retval; } - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 SIMD_16x32 operator&(const SIMD_16x32& other) const { SIMD_16x32 retval(*this); retval &= other; return retval; } - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 void operator+=(const SIMD_16x32& other) { m_avx512 = _mm512_add_epi32(m_avx512, other.m_avx512); } - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 void operator-=(const SIMD_16x32& other) { m_avx512 = _mm512_sub_epi32(m_avx512, other.m_avx512); } - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 void operator^=(const SIMD_16x32& other) { m_avx512 = _mm512_xor_si512(m_avx512, other.m_avx512); } - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 void operator^=(uint32_t other) { *this ^= SIMD_16x32::splat(other); } - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 void operator|=(const SIMD_16x32& other) { m_avx512 = _mm512_or_si512(m_avx512, other.m_avx512); } - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 void operator&=(const SIMD_16x32& other) { m_avx512 = _mm512_and_si512(m_avx512, other.m_avx512); } template - BOTAN_AVX512_FN SIMD_16x32 shl() const { + BOTAN_FN_ISA_AVX512 SIMD_16x32 shl() const { return SIMD_16x32(_mm512_slli_epi32(m_avx512, SHIFT)); } template - BOTAN_AVX512_FN SIMD_16x32 shr() const { + BOTAN_FN_ISA_AVX512 SIMD_16x32 shr() const { return SIMD_16x32(_mm512_srli_epi32(m_avx512, SHIFT)); } - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 SIMD_16x32 operator~() const { return SIMD_16x32(_mm512_xor_si512(m_avx512, _mm512_set1_epi32(0xFFFFFFFF))); } // (~reg) & other - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 SIMD_16x32 andc(const SIMD_16x32& other) const { return SIMD_16x32(_mm512_andnot_si512(m_avx512, other.m_avx512)); } template - BOTAN_AVX512_FN static SIMD_16x32 ternary_fn(const SIMD_16x32& a, const SIMD_16x32& b, const SIMD_16x32& c) { - return _mm512_ternarylogic_epi32(a.raw(), b.raw(), c.raw(), TBL); + BOTAN_FN_ISA_AVX512 static SIMD_16x32 ternary_fn(const SIMD_16x32& a, const SIMD_16x32& b, const SIMD_16x32& c) { + return SIMD_16x32(_mm512_ternarylogic_epi32(a.raw(), b.raw(), c.raw(), TBL)); } - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 SIMD_16x32 bswap() const { const uint8_t BSWAP_MASK[64] = { 3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12, 19, 18, 17, 16, 23, 22, @@ -184,7 +191,10 @@ return SIMD_16x32(output); } - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 + SIMD_16x32 rev_words() const noexcept { return SIMD_16x32(_mm512_shuffle_epi32(raw(), _MM_PERM_ABCD)); } + + BOTAN_FN_ISA_AVX512 static void transpose(SIMD_16x32& B0, SIMD_16x32& B1, SIMD_16x32& B2, SIMD_16x32& B3) { const __m512i T0 = _mm512_unpacklo_epi32(B0.m_avx512, B1.m_avx512); const __m512i T1 = _mm512_unpacklo_epi32(B2.m_avx512, B3.m_avx512); @@ -197,7 +207,7 @@ B3.m_avx512 = _mm512_unpackhi_epi64(T2, T3); } - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 static void transpose(SIMD_16x32& B0, SIMD_16x32& B1, SIMD_16x32& B2, @@ -283,43 +293,45 @@ BF.m_avx512 = _mm512_shuffle_i32x4(t7, tf, 0xdd); } - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 static SIMD_16x32 choose(const SIMD_16x32& mask, const SIMD_16x32& a, const SIMD_16x32& b) { return SIMD_16x32::ternary_fn<0xca>(mask, a, b); } - BOTAN_AVX512_FN + BOTAN_FN_ISA_AVX512 static SIMD_16x32 majority(const SIMD_16x32& x, const SIMD_16x32& y, const SIMD_16x32& z) { return SIMD_16x32::ternary_fn<0xe8>(x, y, z); } - BOTAN_FUNC_ISA("avx2") static void zero_registers() { + BOTAN_FN_ISA_AVX512 static void zero_registers() { // Unfortunately this only zeros zmm0-zmm15 and not zmm16-zmm32 _mm256_zeroall(); } - __m512i BOTAN_AVX512_FN raw() const { return m_avx512; } + __m512i BOTAN_FN_ISA_AVX512 raw() const { return m_avx512; } - BOTAN_AVX512_FN - SIMD_16x32(__m512i x) : m_avx512(x) {} + BOTAN_FN_ISA_AVX512 + explicit SIMD_16x32(__m512i x) noexcept : m_avx512(x) {} private: __m512i m_avx512; }; +// NOLINTEND(portability-simd-intrinsics) + template -inline SIMD_16x32 rotl(SIMD_16x32 input) { +inline SIMD_16x32 BOTAN_FN_ISA_AVX512 rotl(SIMD_16x32 input) { return input.rotl(); } template -inline SIMD_16x32 rotr(SIMD_16x32 input) { +inline SIMD_16x32 BOTAN_FN_ISA_AVX512 rotr(SIMD_16x32 input) { return input.rotr(); } // For Serpent: template -inline SIMD_16x32 shl(SIMD_16x32 input) { +inline SIMD_16x32 BOTAN_FN_ISA_AVX512 shl(SIMD_16x32 input) { return input.shl(); } diff -Nru botan3-3.7.1+dfsg/src/lib/utils/simd/simd_avx512/simd_avx512_gfni.h botan3-3.12.0+dfsg/src/lib/utils/simd/simd_avx512/simd_avx512_gfni.h --- botan3-3.7.1+dfsg/src/lib/utils/simd/simd_avx512/simd_avx512_gfni.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/simd/simd_avx512/simd_avx512_gfni.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,29 @@ +/* +* (C) 2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SIMD_AVX512_GFNI_H_ +#define BOTAN_SIMD_AVX512_GFNI_H_ + +#include + +#include +#include + +namespace Botan { + +template +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI SIMD_16x32 gf2p8affine(const SIMD_16x32& x) { + return SIMD_16x32(_mm512_gf2p8affine_epi64_epi8(x.raw(), _mm512_set1_epi64(A), B)); +} + +template +BOTAN_FORCE_INLINE BOTAN_FN_ISA_AVX512_GFNI SIMD_16x32 gf2p8affineinv(const SIMD_16x32& x) { + return SIMD_16x32(_mm512_gf2p8affineinv_epi64_epi8(x.raw(), _mm512_set1_epi64(A), B)); +} + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/simd/simd_hwaes/info.txt botan3-3.12.0+dfsg/src/lib/utils/simd/simd_hwaes/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/simd/simd_hwaes/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/simd/simd_hwaes/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,29 @@ + +SIMD_HWAES -> 20260322 + + + +name -> "SIMD hardware AES support" + + + +simd_hwaes.h + + + +simd_4x32 + + + +x86_32:aesni +x86_64:aesni +x32:aesni +arm64:armv8crypto + + + +x86_32 +x86_64 +x32 +arm64 + diff -Nru botan3-3.7.1+dfsg/src/lib/utils/simd/simd_hwaes/simd_hwaes.h botan3-3.12.0+dfsg/src/lib/utils/simd/simd_hwaes/simd_hwaes.h --- botan3-3.7.1+dfsg/src/lib/utils/simd/simd_hwaes/simd_hwaes.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/simd/simd_hwaes/simd_hwaes.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,170 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SIMD_HWAES_H_ +#define BOTAN_SIMD_HWAES_H_ + +#include +#include +#include + +namespace Botan { + +/** +* Apply the AES S-box to each byte of the input vector. +*/ +inline SIMD_4x32 BOTAN_FN_ISA_HWAES hw_aes_sbox(SIMD_4x32 x) { + // Undo the ShiftRows with a byte shuffle implementing InvShiftRows + const auto inv_sr = SIMD_4x32(0x070A0D00, 0x0B0E0104, 0x0F020508, 0x0306090C); + +#if defined(BOTAN_TARGET_ARCH_IS_X86_FAMILY) + auto enc = SIMD_4x32(_mm_aesenclast_si128(x.raw(), _mm_setzero_si128())); +#elif defined(BOTAN_TARGET_ARCH_IS_ARM64) + auto enc = SIMD_4x32(vreinterpretq_u32_u8(vaeseq_u8(vreinterpretq_u8_u32(x.raw()), vdupq_n_u8(0)))); +#else + #error "hw_aes_sbox not implemented for this architecture" +#endif + + return SIMD_4x32::byte_shuffle(enc, inv_sr); +} + +/** +* Apply the AES inverse S-box to each byte of the input vector. +*/ +inline SIMD_4x32 BOTAN_FN_ISA_HWAES hw_aes_inv_sbox(SIMD_4x32 x) { + // Undo the InvShiftRows with a byte shuffle implementing ShiftRows + const auto sr = SIMD_4x32(0x0F0A0500, 0x030E0904, 0x07020D08, 0x0B06010C); + +#if defined(BOTAN_TARGET_ARCH_IS_X86_FAMILY) + auto dec = SIMD_4x32(_mm_aesdeclast_si128(x.raw(), _mm_setzero_si128())); +#elif defined(BOTAN_TARGET_ARCH_IS_ARM64) + auto dec = SIMD_4x32(vreinterpretq_u32_u8(vaesdq_u8(vreinterpretq_u8_u32(x.raw()), vdupq_n_u8(0)))); +#else + #error "hw_aes_inv_sbox not implemented for this architecture" +#endif + + return SIMD_4x32::byte_shuffle(dec, sr); +} + +namespace detail { + +/* +* GF(2) matrix-vector multiply: returns M*x where M is a GFNI matrix +* and x is an 8-bit vector. Both use GFNI bit numbering convention. +*/ +consteval uint8_t gf2_mat_vec(uint64_t M, uint8_t x) { + uint8_t result = 0; + for(size_t i = 0; i != 8; ++i) { + uint8_t bit = 0; + for(size_t j = 0; j != 8; ++j) { + if(((M >> (56 - 8 * i + j)) & 1) == 1) { + bit ^= (x >> j) & 1; + } + } + result |= bit << i; + } + return result; +} + +/* +* GF(2) 8x8 matrix multiplication: returns A*B in GFNI format. +*/ +consteval uint64_t gf2_mat_mul(uint64_t A, uint64_t B) { + uint64_t result = 0; + for(size_t i = 0; i != 8; ++i) { + for(size_t j = 0; j != 8; ++j) { + uint8_t bit = 0; + for(size_t k = 0; k != 8; ++k) { + auto a_ik = static_cast((A >> (56 - 8 * i + k)) & 1); + auto b_kj = static_cast((B >> (56 - 8 * k + j)) & 1); + bit ^= a_ik & b_kj; + } + if(bit != 0) { + result |= uint64_t(1) << (56 - 8 * i + j); + } + } + } + return result; +} + +// AES affine matrix in GFNI format +constexpr uint64_t AES_AFF = gfni_matrix(R"( + 1 0 0 0 1 1 1 1 + 1 1 0 0 0 1 1 1 + 1 1 1 0 0 0 1 1 + 1 1 1 1 0 0 0 1 + 1 1 1 1 1 0 0 0 + 0 1 1 1 1 1 0 0 + 0 0 1 1 1 1 1 0 + 0 0 0 1 1 1 1 1)"); +constexpr uint8_t AES_C = 0x63; + +// AES inverse affine matrix in GFNI format +constexpr uint64_t AES_AFF_INV = gfni_matrix(R"( + 0 0 1 0 0 1 0 1 + 1 0 0 1 0 0 1 0 + 0 1 0 0 1 0 0 1 + 1 0 1 0 0 1 0 0 + 0 1 0 1 0 0 1 0 + 0 0 1 0 1 0 0 1 + 1 0 0 1 0 1 0 0 + 0 1 0 0 1 0 1 0)"); +constexpr uint8_t AES_C_INV = 0x05; + +} // namespace detail + +/** +* Lookup tables for GF(2) affine transformations +*/ +class Gf2AffineTransformation final { + public: + consteval Gf2AffineTransformation(uint64_t M, uint8_t c) : lo{}, hi{} { + for(size_t i = 0; i != 16; ++i) { + // Low nibble table includes the constant addition + const uint8_t lo_val = detail::gf2_mat_vec(M, static_cast(i)) ^ c; + const uint8_t hi_val = detail::gf2_mat_vec(M, static_cast(i << 4)); + + lo[i / 4] |= static_cast(lo_val) << (8 * (i % 4)); + hi[i / 4] |= static_cast(hi_val) << (8 * (i % 4)); + } + } + + /** + * Derive tables used for computing an affine transform after the application of an + * AES sbox. + */ + static consteval Gf2AffineTransformation post_sbox(uint64_t M, uint8_t c) { + const auto comb_M = detail::gf2_mat_mul(M, detail::AES_AFF_INV); + const auto comb_c = static_cast(detail::gf2_mat_vec(comb_M, detail::AES_C) ^ c); + return Gf2AffineTransformation(comb_M, comb_c); + } + + /** + * Derive tables used for computing an affine transform after the application of an + * AES inverse sbox. + */ + static consteval Gf2AffineTransformation post_inv_sbox(uint64_t M, uint8_t c) { + const auto comb_mat = detail::gf2_mat_mul(detail::AES_AFF, M); + const auto comb_c = detail::gf2_mat_vec(detail::AES_AFF, static_cast(c ^ detail::AES_C_INV)); + return Gf2AffineTransformation(comb_mat, comb_c); + } + + inline SIMD_4x32 BOTAN_FN_ISA_HWAES affine_transform(SIMD_4x32 x) const { + const SIMD_4x32 tbl_lo(lo[0], lo[1], lo[2], lo[3]); + const SIMD_4x32 tbl_hi(hi[0], hi[1], hi[2], hi[3]); + const auto lo_mask = SIMD_4x32::splat_u8(0x0F); + + return SIMD_4x32::byte_shuffle(tbl_lo, lo_mask & x) ^ SIMD_4x32::byte_shuffle(tbl_hi, lo_mask & x.shr<4>()); + } + + private: + uint32_t lo[4]; + uint32_t hi[4]; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/socket/info.txt botan3-3.12.0+dfsg/src/lib/utils/socket/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/socket/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/socket/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + SOCKETS -> 20171216 - + name -> "Socket" diff -Nru botan3-3.7.1+dfsg/src/lib/utils/socket/socket.cpp botan3-3.12.0+dfsg/src/lib/utils/socket/socket.cpp --- botan3-3.7.1+dfsg/src/lib/utils/socket/socket.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/socket/socket.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #if defined(BOTAN_HAS_BOOST_ASIO) @@ -50,7 +52,7 @@ check_timeout(); boost::asio::ip::tcp::resolver resolver(m_io); - boost::asio::ip::tcp::resolver::results_type dns_iter = + const boost::asio::ip::tcp::resolver::results_type dns_iter = resolver.resolve(std::string{hostname}, std::string{service}); boost::system::error_code ec = boost::asio::error::would_block; @@ -66,17 +68,19 @@ if(ec) { throw boost::system::system_error(ec); } - if(m_tcp.is_open() == false) { + if(!m_tcp.is_open()) { throw System_Error(fmt("Connection to host {} failed", hostname)); } } - void write(const uint8_t buf[], size_t len) override { + void write(std::span buf) override { m_timer.expires_after(m_timeout); boost::system::error_code ec = boost::asio::error::would_block; - m_tcp.async_send(boost::asio::buffer(buf, len), [&ec](boost::system::error_code e, size_t) { ec = e; }); + // Some versions of asio don't know about span... + m_tcp.async_send(boost::asio::buffer(buf.data(), buf.size()), + [&ec](boost::system::error_code e, size_t) { ec = e; }); while(ec == boost::asio::error::would_block) { m_io.run_one(); @@ -121,6 +125,7 @@ m_tcp.close(err); } + // NOLINTNEXTLINE(*-avoid-bind) FIXME - unclear why we can't use a lambda here m_timer.async_wait(std::bind(&Asio_Socket::check_timeout, this)); } @@ -144,10 +149,14 @@ static void close_socket(socket_type s) { ::closesocket(s); } + static int last_socket_error() { return ::WSAGetLastError(); } + static std::string get_last_socket_error() { return std::to_string(::WSAGetLastError()); } static bool nonblocking_connect_in_progress() { return (::WSAGetLastError() == WSAEWOULDBLOCK); } + static bool select_error_is_retryable() { return (::WSAGetLastError() == WSAEINTR); } + static void set_nonblocking(socket_type s) { u_long nonblocking = 1; ::ioctlsocket(s, FIONBIO, &nonblocking); @@ -178,11 +187,16 @@ static void close_socket(socket_type s) { ::close(s); } + static int last_socket_error() { return errno; } + static std::string get_last_socket_error() { return ::strerror(errno); } static bool nonblocking_connect_in_progress() { return (errno == EINPROGRESS); } + static bool select_error_is_retryable() { return (errno == EINTR); } + static void set_nonblocking(socket_type s) { + // NOLINTNEXTLINE(*-vararg) if(::fcntl(s, F_SETFL, O_NONBLOCK) < 0) { throw System_Error("Setting socket to non-blocking state failed", errno); } @@ -195,27 +209,24 @@ public: BSD_Socket(std::string_view hostname, std::string_view service, std::chrono::microseconds timeout) : - m_timeout(timeout) { + m_timeout(timeout), m_socket(invalid_socket()) { socket_init(); - m_socket = invalid_socket(); + const std::string hostname_str(hostname); + const std::string service_str(service); - addrinfo hints; - clear_mem(&hints, 1); + addrinfo hints{}; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; - addrinfo* res; - const std::string hostname_str(hostname); - const std::string service_str(service); - - int rc = ::getaddrinfo(hostname_str.c_str(), service_str.c_str(), &hints, &res); + unique_addr_info_ptr res = nullptr; + const int rc = ::getaddrinfo(hostname_str.c_str(), service_str.c_str(), &hints, Botan::out_ptr(res)); if(rc != 0) { throw System_Error(fmt("Name resolution failed for {}", hostname), rc); } - for(addrinfo* rp = res; (m_socket == invalid_socket()) && (rp != nullptr); rp = rp->ai_next) { + for(const addrinfo* rp = res.get(); (m_socket == invalid_socket()) && (rp != nullptr); rp = rp->ai_next) { if(rp->ai_family != AF_INET && rp->ai_family != AF_INET6) { continue; } @@ -229,7 +240,7 @@ set_nonblocking(m_socket); - int err = ::connect(m_socket, rp->ai_addr, static_cast(rp->ai_addrlen)); + const int err = ::connect(m_socket, rp->ai_addr, static_cast(rp->ai_addrlen)); if(err == -1) { int active = 0; @@ -237,18 +248,17 @@ struct timeval timeout_tv = make_timeout_tv(); fd_set write_set; FD_ZERO(&write_set); - // Weirdly, Winsock uses a SOCKET type but wants FD_SET to get an int instead - FD_SET(static_cast(m_socket), &write_set); + FD_SET(m_socket, &write_set); active = ::select(static_cast(m_socket + 1), nullptr, &write_set, nullptr, &timeout_tv); - if(active) { + if(active > 0) { int socket_error = 0; socklen_t len = sizeof(socket_error); if(::getsockopt(m_socket, SOL_SOCKET, SO_ERROR, reinterpret_cast(&socket_error), &len) < 0) { - throw System_Error("Error calling getsockopt", errno); + throw System_Error("Error calling getsockopt", last_socket_error()); } if(socket_error != 0) { @@ -265,11 +275,10 @@ } } - ::freeaddrinfo(res); - if(m_socket == invalid_socket()) { - throw System_Error(fmt("Connecting to {} for service {} failed with errno {}", hostname, service, errno), - errno); + throw System_Error( + fmt("Connecting to {} for service {} failed with errno {}", hostname, service, last_socket_error()), + last_socket_error()); } } @@ -284,25 +293,34 @@ BSD_Socket& operator=(const BSD_Socket& other) = delete; BSD_Socket& operator=(BSD_Socket&& other) = delete; - void write(const uint8_t buf[], size_t len) override { - fd_set write_set; - FD_ZERO(&write_set); - FD_SET(m_socket, &write_set); + void write(std::span buf) override { + const size_t len = buf.size(); size_t sent_so_far = 0; while(sent_so_far != len) { + fd_set write_set; + FD_ZERO(&write_set); + FD_SET(m_socket, &write_set); + struct timeval timeout = make_timeout_tv(); - int active = ::select(static_cast(m_socket + 1), nullptr, &write_set, nullptr, &timeout); + const int active = ::select(static_cast(m_socket + 1), nullptr, &write_set, nullptr, &timeout); + + if(active < 0) { + if(select_error_is_retryable()) { + continue; + } + throw System_Error("Socket select failed", last_socket_error()); + } if(active == 0) { throw System_Error("Timeout during socket write"); } const size_t left = len - sent_so_far; - socket_op_ret_type sent = + const socket_op_ret_type sent = ::send(m_socket, cast_uint8_ptr_to_char(&buf[sent_so_far]), static_cast(left), 0); if(sent < 0) { - throw System_Error("Socket write failed", errno); + throw System_Error("Socket write failed", last_socket_error()); } else { sent_so_far += static_cast(sent); } @@ -310,29 +328,40 @@ } size_t read(uint8_t buf[], size_t len) override { - fd_set read_set; - FD_ZERO(&read_set); - FD_SET(m_socket, &read_set); + for(;;) { + fd_set read_set; + FD_ZERO(&read_set); + FD_SET(m_socket, &read_set); - struct timeval timeout = make_timeout_tv(); - int active = ::select(static_cast(m_socket + 1), &read_set, nullptr, nullptr, &timeout); + struct timeval timeout = make_timeout_tv(); + const int active = ::select(static_cast(m_socket + 1), &read_set, nullptr, nullptr, &timeout); - if(active == 0) { - throw System_Error("Timeout during socket read"); - } + if(active < 0) { + if(select_error_is_retryable()) { + continue; + } + throw System_Error("Socket select failed", last_socket_error()); + } + + if(active == 0) { + throw System_Error("Timeout during socket read"); + } - socket_op_ret_type got = ::recv(m_socket, cast_uint8_ptr_to_char(buf), static_cast(len), 0); + const socket_op_ret_type got = + ::recv(m_socket, cast_uint8_ptr_to_char(buf), static_cast(len), 0); - if(got < 0) { - throw System_Error("Socket read failed", errno); - } + if(got < 0) { + throw System_Error("Socket read failed", last_socket_error()); + } - return static_cast(got); + return static_cast(got); + } } private: struct timeval make_timeout_tv() const { - struct timeval tv; + struct timeval tv {}; + tv.tv_sec = static_cast(m_timeout.count() / 1000000); tv.tv_usec = static_cast(m_timeout.count() % 1000000); return tv; @@ -340,6 +369,12 @@ const std::chrono::microseconds m_timeout; socket_type m_socket; + + using unique_addr_info_ptr = std::unique_ptr; }; #endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/socket/socket.h botan3-3.12.0+dfsg/src/lib/utils/socket/socket.h --- botan3-3.7.1+dfsg/src/lib/utils/socket/socket.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/socket/socket.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,7 +10,9 @@ #include #include -#include +#include +#include +#include namespace Botan::OS { @@ -25,7 +27,7 @@ /** * A wrapper around a simple blocking TCP socket */ -class BOTAN_TEST_API Socket { +class BOTAN_TEST_API Socket /* NOLINT(*-special-member-functions) */ { public: /** * The socket will be closed upon destruction @@ -36,7 +38,7 @@ * Write to the socket. Blocks until all bytes sent. * Throws on error. */ - virtual void write(const uint8_t buf[], size_t len) = 0; + virtual void write(std::span bytes) = 0; /** * Reads up to len bytes, returns bytes written to buf. diff -Nru botan3-3.7.1+dfsg/src/lib/utils/socket/socket_udp.cpp botan3-3.12.0+dfsg/src/lib/utils/socket/socket_udp.cpp --- botan3-3.7.1+dfsg/src/lib/utils/socket/socket_udp.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/socket/socket_udp.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include @@ -50,7 +52,7 @@ check_timeout(); boost::asio::ip::udp::resolver resolver(m_io); - boost::asio::ip::udp::resolver::results_type dns_iter = + const boost::asio::ip::udp::resolver::results_type dns_iter = resolver.resolve(std::string{hostname}, std::string{service}); boost::system::error_code ec = boost::asio::error::would_block; @@ -67,7 +69,7 @@ if(ec) { throw boost::system::system_error(ec); } - if(m_udp.is_open() == false) { + if(!m_udp.is_open()) { throw System_Error(fmt("Connection to host {} failed", hostname)); } } @@ -122,6 +124,7 @@ m_udp.close(err); } + // NOLINTNEXTLINE(*-avoid-bind) FIXME - unclear why we can't use a lambda here m_timer.async_wait(std::bind(&Asio_SocketUDP::check_timeout, this)); } @@ -134,27 +137,24 @@ class BSD_SocketUDP final : public OS::SocketUDP { public: BSD_SocketUDP(std::string_view hostname, std::string_view service, std::chrono::microseconds timeout) : - m_timeout(timeout) { + m_timeout(timeout), m_socket(invalid_socket()) { socket_init(); - m_socket = invalid_socket(); + const std::string hostname_str(hostname); + const std::string service_str(service); - addrinfo* res; - addrinfo hints; - clear_mem(&hints, 1); + addrinfo hints{}; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; - const std::string hostname_str(hostname); - const std::string service_str(service); - - int rc = ::getaddrinfo(hostname_str.c_str(), service_str.c_str(), &hints, &res); + unique_addr_info_ptr res = nullptr; + const int rc = ::getaddrinfo(hostname_str.c_str(), service_str.c_str(), &hints, Botan::out_ptr(res)); if(rc != 0) { throw System_Error(fmt("Name resolution failed for {}", hostname), rc); } - for(addrinfo* rp = res; (m_socket == invalid_socket()) && (rp != nullptr); rp = rp->ai_next) { + for(const addrinfo* rp = res.get(); (m_socket == invalid_socket()) && (rp != nullptr); rp = rp->ai_next) { if(rp->ai_family != AF_INET && rp->ai_family != AF_INET6) { continue; } @@ -168,14 +168,13 @@ set_nonblocking(m_socket); memcpy(&sa, res->ai_addr, res->ai_addrlen); - salen = static_cast(res->ai_addrlen); + salen = static_cast(res->ai_addrlen); // NOLINT(*-redundant-casting) } - ::freeaddrinfo(res); - if(m_socket == invalid_socket()) { - throw System_Error(fmt("Connecting to {} for service {} failed with errno {}", hostname, service, errno), - errno); + throw System_Error( + fmt("Connecting to {} for service {} failed with errno {}", hostname, service, last_socket_error()), + last_socket_error()); } } @@ -191,28 +190,35 @@ BSD_SocketUDP& operator=(BSD_SocketUDP&& other) = delete; void write(const uint8_t buf[], size_t len) override { - fd_set write_set; - FD_ZERO(&write_set); - FD_SET(m_socket, &write_set); - size_t sent_so_far = 0; while(sent_so_far != len) { + fd_set write_set; + FD_ZERO(&write_set); + FD_SET(m_socket, &write_set); + struct timeval timeout = make_timeout_tv(); - int active = ::select(static_cast(m_socket + 1), nullptr, &write_set, nullptr, &timeout); + const int active = ::select(static_cast(m_socket + 1), nullptr, &write_set, nullptr, &timeout); + + if(active < 0) { + if(select_error_is_retryable()) { + continue; + } + throw System_Error("Socket select failed", last_socket_error()); + } if(active == 0) { throw System_Error("Timeout during socket write"); } const size_t left = len - sent_so_far; - socket_op_ret_type sent = ::sendto(m_socket, - cast_uint8_ptr_to_char(buf + sent_so_far), - static_cast(left), - 0, - reinterpret_cast(&sa), - salen); + const socket_op_ret_type sent = ::sendto(m_socket, + cast_uint8_ptr_to_char(buf + sent_so_far), + static_cast(left), + 0, + reinterpret_cast(&sa), + salen); if(sent < 0) { - throw System_Error("Socket write failed", errno); + throw System_Error("Socket write failed", last_socket_error()); } else { sent_so_far += static_cast(sent); } @@ -220,25 +226,34 @@ } size_t read(uint8_t buf[], size_t len) override { - fd_set read_set; - FD_ZERO(&read_set); - FD_SET(m_socket, &read_set); + for(;;) { + fd_set read_set; + FD_ZERO(&read_set); + FD_SET(m_socket, &read_set); - struct timeval timeout = make_timeout_tv(); - int active = ::select(static_cast(m_socket + 1), &read_set, nullptr, nullptr, &timeout); + struct timeval timeout = make_timeout_tv(); + const int active = ::select(static_cast(m_socket + 1), &read_set, nullptr, nullptr, &timeout); - if(active == 0) { - throw System_Error("Timeout during socket read"); - } + if(active < 0) { + if(select_error_is_retryable()) { + continue; + } + throw System_Error("Socket select failed", last_socket_error()); + } - socket_op_ret_type got = - ::recvfrom(m_socket, cast_uint8_ptr_to_char(buf), static_cast(len), 0, nullptr, nullptr); + if(active == 0) { + throw System_Error("Timeout during socket read"); + } - if(got < 0) { - throw System_Error("Socket read failed", errno); - } + const socket_op_ret_type got = ::recvfrom( + m_socket, cast_uint8_ptr_to_char(buf), static_cast(len), 0, nullptr, nullptr); - return static_cast(got); + if(got < 0) { + throw System_Error("Socket read failed", last_socket_error()); + } + + return static_cast(got); + } } private: @@ -251,10 +266,14 @@ static void close_socket(socket_type s) { ::closesocket(s); } + static int last_socket_error() { return ::WSAGetLastError(); } + static std::string get_last_socket_error() { return std::to_string(::WSAGetLastError()); } static bool nonblocking_connect_in_progress() { return (::WSAGetLastError() == WSAEWOULDBLOCK); } + static bool select_error_is_retryable() { return (::WSAGetLastError() == WSAEINTR); } + static void set_nonblocking(socket_type s) { u_long nonblocking = 1; ::ioctlsocket(s, FIONBIO, &nonblocking); @@ -284,11 +303,16 @@ static void close_socket(socket_type s) { ::close(s); } + static int last_socket_error() { return errno; } + static std::string get_last_socket_error() { return ::strerror(errno); } static bool nonblocking_connect_in_progress() { return (errno == EINPROGRESS); } + static bool select_error_is_retryable() { return (errno == EINTR); } + static void set_nonblocking(socket_type s) { + // NOLINTNEXTLINE(*-vararg) if(::fcntl(s, F_SETFL, O_NONBLOCK) < 0) { throw System_Error("Setting socket to non-blocking state failed", errno); } @@ -298,11 +322,12 @@ static void socket_fini() {} #endif - sockaddr_storage sa; + sockaddr_storage sa = {}; socklen_t salen; struct timeval make_timeout_tv() const { - struct timeval tv; + struct timeval tv {}; + tv.tv_sec = static_cast(m_timeout.count() / 1000000); tv.tv_usec = static_cast(m_timeout.count() % 1000000); return tv; @@ -310,6 +335,12 @@ const std::chrono::microseconds m_timeout; socket_type m_socket; + + using unique_addr_info_ptr = std::unique_ptr; }; #endif } // namespace diff -Nru botan3-3.7.1+dfsg/src/lib/utils/socket/socket_udp.h botan3-3.12.0+dfsg/src/lib/utils/socket/socket_udp.h --- botan3-3.7.1+dfsg/src/lib/utils/socket/socket_udp.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/socket/socket_udp.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,7 +10,8 @@ #include #include -#include +#include +#include namespace Botan::OS { @@ -25,7 +26,7 @@ /** * A wrapper around a simple blocking UDP socket */ -class BOTAN_TEST_API SocketUDP { +class BOTAN_TEST_API SocketUDP /* NOLINT(*-special-member-functions) */ { public: /** * The socket will be closed upon destruction diff -Nru botan3-3.7.1+dfsg/src/lib/utils/socket/uri.cpp botan3-3.12.0+dfsg/src/lib/utils/socket/uri.cpp --- botan3-3.7.1+dfsg/src/lib/utils/socket/uri.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/socket/uri.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,6 +11,7 @@ #include #include #include +#include #if defined(BOTAN_TARGET_OS_HAS_SOCKETS) #include @@ -36,15 +37,11 @@ } bool is_ipv4(std::string_view ip) { - std::string ip_str(ip); - sockaddr_storage inaddr; - return !!inet_pton(AF_INET, ip_str.c_str(), &inaddr); + return string_to_ipv4(ip).has_value(); } bool is_ipv6(std::string_view ip) { - std::string ip_str(ip); - sockaddr_storage in6addr; - return !!inet_pton(AF_INET6, ip_str.c_str(), &in6addr); + return string_to_ipv6(ip).has_value(); } uint16_t parse_port_number(const char* func_name, std::string_view uri, size_t pos) { @@ -56,8 +53,8 @@ uint32_t port = 0; - for(char c : uri.substr(pos + 1)) { - size_t digit = c - '0'; + for(const char c : uri.substr(pos + 1)) { + const size_t digit = c - '0'; if(digit >= 10) { throw Invalid_Argument(fmt("URI::{} invalid port field in {}", func_name, uri)); } @@ -120,7 +117,8 @@ port = parse_port_number("from_ipv6", uri, port_pos + 1); } - const auto ip = uri.substr((with_braces ? 1 : 0), port_pos - with_braces); + const auto ip = with_braces ? uri.substr(1, port_pos - 1) : uri.substr(0, port_pos); + if(!is_ipv6(ip)) { throw Invalid_Argument("URI::from_ipv6 URI has invalid IPv6 address"); } diff -Nru botan3-3.7.1+dfsg/src/lib/utils/socket/uri.h botan3-3.12.0+dfsg/src/lib/utils/socket/uri.h --- botan3-3.7.1+dfsg/src/lib/utils/socket/uri.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/socket/uri.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,13 +9,12 @@ #define BOTAN_URI_H_ #include -#include #include #include namespace Botan { -class BOTAN_TEST_API URI { +class BOTAN_TEST_API URI final { public: enum class Type : uint8_t { IPv4, diff -Nru botan3-3.7.1+dfsg/src/lib/utils/sqlite3/info.txt botan3-3.12.0+dfsg/src/lib/utils/sqlite3/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/sqlite3/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/sqlite3/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + SQLITE3 -> 20171118 - + name -> "SQLite 3" diff -Nru botan3-3.7.1+dfsg/src/lib/utils/sqlite3/sqlite3.cpp botan3-3.12.0+dfsg/src/lib/utils/sqlite3/sqlite3.cpp --- botan3-3.7.1+dfsg/src/lib/utils/sqlite3/sqlite3.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/sqlite3/sqlite3.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,7 @@ #include #include #include +#include #include namespace Botan { @@ -19,9 +20,9 @@ // concurrently from multiple threads. const int open_flags = sqlite_open_flags.value_or(SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX); - int rc = ::sqlite3_open_v2(std::string(db_filename).c_str(), &m_db, open_flags, nullptr); + const int rc = ::sqlite3_open_v2(std::string(db_filename).c_str(), &m_db, open_flags, nullptr); - if(rc) [[unlikely]] { + if(rc != 0) [[unlikely]] { const std::string err_msg = ::sqlite3_errmsg(m_db); ::sqlite3_close(m_db); m_db = nullptr; @@ -30,7 +31,7 @@ } Sqlite3_Database::~Sqlite3_Database() { - if(m_db) [[likely]] { + if(m_db != nullptr) [[likely]] { ::sqlite3_close(m_db); } m_db = nullptr; @@ -52,7 +53,7 @@ void Sqlite3_Database::create_table(std::string_view table_schema) { char* errmsg = nullptr; - int rc = ::sqlite3_exec(m_db, std::string(table_schema).c_str(), nullptr, nullptr, &errmsg); + const int rc = ::sqlite3_exec(m_db, std::string(table_schema).c_str(), nullptr, nullptr, &errmsg); if(rc != SQLITE_OK) { const std::string err_msg = errmsg; @@ -87,8 +88,8 @@ return flag >= 1; } -Sqlite3_Database::Sqlite3_Statement::Sqlite3_Statement(sqlite3* db, std::string_view base_sql) { - int rc = ::sqlite3_prepare_v2(db, base_sql.data(), static_cast(base_sql.size()), &m_stmt, nullptr); +Sqlite3_Database::Sqlite3_Statement::Sqlite3_Statement(sqlite3* db, std::string_view base_sql) : m_stmt{} { + const int rc = ::sqlite3_prepare_v2(db, base_sql.data(), static_cast(base_sql.size()), &m_stmt, nullptr); if(rc != SQLITE_OK) { throw SQL_DB_Error(fmt("sqlite3_prepare failed on '{}' with err {}", base_sql, rc), rc); @@ -96,14 +97,14 @@ } void Sqlite3_Database::Sqlite3_Statement::bind(int column, std::string_view val) { - int rc = ::sqlite3_bind_text64(m_stmt, column, val.data(), val.size(), SQLITE_TRANSIENT, SQLITE_UTF8); + const int rc = ::sqlite3_bind_text64(m_stmt, column, val.data(), val.size(), SQLITE_TRANSIENT, SQLITE_UTF8); if(rc != SQLITE_OK) { throw SQL_DB_Error("sqlite3_bind_text failed", rc); } } void Sqlite3_Database::Sqlite3_Statement::bind(int column, size_t val) { - int rc = ::sqlite3_bind_int64(m_stmt, column, val); + const int rc = ::sqlite3_bind_int64(m_stmt, column, val); if(rc != SQLITE_OK) { throw SQL_DB_Error("sqlite3_bind_int failed", rc); } @@ -115,14 +116,14 @@ } void Sqlite3_Database::Sqlite3_Statement::bind(int column, const std::vector& val) { - int rc = ::sqlite3_bind_blob64(m_stmt, column, val.data(), val.size(), SQLITE_TRANSIENT); + const int rc = ::sqlite3_bind_blob64(m_stmt, column, val.data(), val.size(), SQLITE_TRANSIENT); if(rc != SQLITE_OK) { throw SQL_DB_Error("sqlite3_bind_text failed", rc); } } void Sqlite3_Database::Sqlite3_Statement::bind(int column, const uint8_t* p, size_t len) { - int rc = ::sqlite3_bind_blob64(m_stmt, column, p, len, SQLITE_TRANSIENT); + const int rc = ::sqlite3_bind_blob64(m_stmt, column, p, len, SQLITE_TRANSIENT); if(rc != SQLITE_OK) { throw SQL_DB_Error("sqlite3_bind_text failed", rc); } @@ -155,9 +156,7 @@ size_t Sqlite3_Database::Sqlite3_Statement::get_size_t(int column) { BOTAN_ASSERT(::sqlite3_column_type(m_stmt, column) == SQLITE_INTEGER, "Return count is an integer"); - const size_t sessions_int = ::sqlite3_column_int64(m_stmt, column); - - return sessions_int; + return checked_cast_to(::sqlite3_column_int64(m_stmt, column)); } size_t Sqlite3_Database::Sqlite3_Statement::spin() { diff -Nru botan3-3.7.1+dfsg/src/lib/utils/sqlite3/sqlite3.h botan3-3.12.0+dfsg/src/lib/utils/sqlite3/sqlite3.h --- botan3-3.7.1+dfsg/src/lib/utils/sqlite3/sqlite3.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/sqlite3/sqlite3.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,7 @@ #include +#include #include struct sqlite3; @@ -26,10 +27,16 @@ * @param sqlite_open_flags flags that will be passed to sqlite3_open_v2() * (default: SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX) */ - Sqlite3_Database(std::string_view file, std::optional sqlite_open_flags = std::nullopt); + BOTAN_FUTURE_EXPLICIT Sqlite3_Database(std::string_view file, + std::optional sqlite_open_flags = std::nullopt); ~Sqlite3_Database() override; + Sqlite3_Database(const Sqlite3_Database& other) = delete; + Sqlite3_Database(Sqlite3_Database&& other) = delete; + Sqlite3_Database& operator=(const Sqlite3_Database& other) = delete; + Sqlite3_Database& operator=(Sqlite3_Database&& other) = delete; + size_t row_count(std::string_view table_name) override; void create_table(std::string_view table_schema) override; @@ -59,6 +66,11 @@ Sqlite3_Statement(sqlite3* db, std::string_view base_sql); ~Sqlite3_Statement() override; + Sqlite3_Statement(const Sqlite3_Statement& other) = delete; + Sqlite3_Statement(Sqlite3_Statement&& other) = delete; + Sqlite3_Statement& operator=(const Sqlite3_Statement& other) = delete; + Sqlite3_Statement& operator=(Sqlite3_Statement&& other) = delete; + private: sqlite3_stmt* m_stmt; }; diff -Nru botan3-3.7.1+dfsg/src/lib/utils/stack_scrubbing.h botan3-3.12.0+dfsg/src/lib/utils/stack_scrubbing.h --- botan3-3.7.1+dfsg/src/lib/utils/stack_scrubbing.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/stack_scrubbing.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,37 @@ +/* +* Helpers for compiler-assisted stack scrubbing +* (C) 2025 Jack Lloyd +* 2025 René Meusel - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_UTIL_STACK_SCRUBBING_H_ +#define BOTAN_UTIL_STACK_SCRUBBING_H_ + +#include +#include + +// TODO(Botan4): Move this to compiler.h (currently still a public header) + +#if !defined(BOTAN_SCRUB_STACK_AFTER_RETURN) + #if BOTAN_COMPILER_HAS_ATTRIBUTE(strub) && defined(BOTAN_USE_COMPILER_ASSISTED_STACK_SCRUBBING) + /** + * When a function definition is annotated with this macro, the compiler + * generates a wrapper for the function's body to handle stack scrubbing + * in the wrapper. In contrast to 'strub("at-calls")' this does not alter + * the function's ABI. + * + * It is okay to use this annotation on C++ method definitions (in *.cpp), + * even if the function is a public API. + * + * Currently this is supported on GCC 14+ only + * See: https://gcc.gnu.org/onlinedocs/gcc-14.2.0/gcc/Common-Type-Attributes.html#index-strub-type-attribute + */ + #define BOTAN_SCRUB_STACK_AFTER_RETURN BOTAN_COMPILER_ATTRIBUTE(strub("internal")) + #else + #define BOTAN_SCRUB_STACK_AFTER_RETURN + #endif +#endif + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/stl_util.h botan3-3.12.0+dfsg/src/lib/utils/stl_util.h --- botan3-3.7.1+dfsg/src/lib/utils/stl_util.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/stl_util.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,30 +10,13 @@ #ifndef BOTAN_STL_UTIL_H_ #define BOTAN_STL_UTIL_H_ -#include -#include +#include #include -#include -#include -#include #include #include -#include -#include -#include - namespace Botan { -template > -inline T to_byte_vector(std::string_view s) { - return T(s.cbegin(), s.cend()); -} - -inline std::string to_string(std::span bytes) { - return std::string(bytes.begin(), bytes.end()); -} - /** * Reduce the values of @p keys into an accumulator initialized with @p acc using * the reducer function @p reducer. @@ -45,7 +28,8 @@ */ template RetT reduce(const std::vector& keys, RetT acc, ReducerT reducer) - requires std::is_convertible_v> + requires std::invocable && + std::convertible_to, RetT> { for(const KeyT& key : keys) { acc = reducer(std::move(acc), key); @@ -56,10 +40,10 @@ /** * Existence check for values */ -template -bool value_exists(const std::vector& vec, const OT& val) { - for(size_t i = 0; i != vec.size(); ++i) { - if(vec[i] == val) { +template +bool value_exists(const std::vector& vec, const V& val) { + for(const auto& elem : vec) { + if(elem == val) { return true; } } @@ -78,229 +62,18 @@ } } -/** - * Helper class to ease unmarshalling of concatenated fixed-length values - */ -class BufferSlicer final { - public: - BufferSlicer(std::span buffer) : m_remaining(buffer) {} - - template - auto copy(const size_t count) { - const auto result = take(count); - return ContainerT(result.begin(), result.end()); - } - - auto copy_as_vector(const size_t count) { return copy>(count); } - - auto copy_as_secure_vector(const size_t count) { return copy>(count); } - - std::span take(const size_t count) { - BOTAN_STATE_CHECK(remaining() >= count); - auto result = m_remaining.first(count); - m_remaining = m_remaining.subspan(count); - return result; - } - - template - std::span take() { - BOTAN_STATE_CHECK(remaining() >= count); - auto result = m_remaining.first(); - m_remaining = m_remaining.subspan(count); - return result; - } - - template - StrongSpan take(const size_t count) { - return StrongSpan(take(count)); - } - - uint8_t take_byte() { return take(1)[0]; } - - void copy_into(std::span sink) { - const auto data = take(sink.size()); - std::copy(data.begin(), data.end(), sink.begin()); - } - - void skip(const size_t count) { take(count); } - - size_t remaining() const { return m_remaining.size(); } - - bool empty() const { return m_remaining.empty(); } - - private: - std::span m_remaining; -}; - -/** - * @brief Helper class to ease in-place marshalling of concatenated fixed-length - * values. - * - * The size of the final buffer must be known from the start, reallocations are - * not performed. - */ -class BufferStuffer final { - public: - constexpr BufferStuffer(std::span buffer) : m_buffer(buffer) {} - - /** - * @returns a span for the next @p bytes bytes in the concatenated buffer. - * Checks that the buffer is not exceded. - */ - constexpr std::span next(size_t bytes) { - BOTAN_STATE_CHECK(m_buffer.size() >= bytes); - - auto result = m_buffer.first(bytes); - m_buffer = m_buffer.subspan(bytes); - return result; - } - - template - constexpr std::span next() { - BOTAN_STATE_CHECK(m_buffer.size() >= bytes); - - auto result = m_buffer.first(); - m_buffer = m_buffer.subspan(bytes); - return result; - } - - template - StrongSpan next(size_t bytes) { - return StrongSpan(next(bytes)); - } - - /** - * @returns a reference to the next single byte in the buffer - */ - constexpr uint8_t& next_byte() { return next(1)[0]; } - - constexpr void append(std::span buffer) { - auto sink = next(buffer.size()); - std::copy(buffer.begin(), buffer.end(), sink.begin()); - } - - constexpr void append(uint8_t b, size_t repeat = 1) { - auto sink = next(repeat); - std::fill(sink.begin(), sink.end(), b); - } - - constexpr bool full() const { return m_buffer.empty(); } - - constexpr size_t remaining_capacity() const { return m_buffer.size(); } - - private: - std::span m_buffer; -}; - -namespace detail { - -/** - * Helper function that performs range size-checks as required given the - * selected output and input range types. If all lengths are known at compile - * time, this check will be performed at compile time as well. It will then - * instantiate an output range and concatenate the input ranges' contents. - */ -template -constexpr OutR concatenate(Rs&&... ranges) - requires(concepts::reservable_container || ranges::statically_spanable_range) -{ - OutR result; - - // Prepare and validate the output range and construct a lambda that does the - // actual filling of the result buffer. - // (if no input ranges are given, GCC claims that fill_fn is unused) - [[maybe_unused]] auto fill_fn = [&] { - if constexpr(concepts::reservable_container) { - // dynamically allocate the correct result byte length - const size_t total_size = (ranges.size() + ... + 0); - result.reserve(total_size); - - // fill the result buffer using a back-inserter - return [&result](auto&& range) { - std::copy( - std::ranges::begin(range), std::ranges::end(range), std::back_inserter(unwrap_strong_type(result))); - }; - } else { - if constexpr((ranges::statically_spanable_range && ... && true)) { - // all input ranges have a static extent, so check the total size at compile time - // (work around an issue in MSVC that warns `total_size` is unused) - [[maybe_unused]] constexpr size_t total_size = (decltype(std::span{ranges})::extent + ... + 0); - static_assert(result.size() == total_size, "size of result buffer does not match the sum of input buffers"); - } else { - // at least one input range has a dynamic extent, so check the total size at runtime - const size_t total_size = (ranges.size() + ... + 0); - BOTAN_ARG_CHECK(result.size() == total_size, - "result buffer has static extent that does not match the sum of input buffers"); - } - - // fill the result buffer and hold the current output-iterator position - return [itr = std::ranges::begin(result)](auto&& range) mutable { - std::copy(std::ranges::begin(range), std::ranges::end(range), itr); - std::advance(itr, std::ranges::size(range)); - }; - } - }(); - - // perform the actual concatenation - (fill_fn(std::forward(ranges)), ...); - - return result; -} - -} // namespace detail - -/** - * Concatenate an arbitrary number of buffers. Performs range-checks as needed. - * - * The output type can be auto-detected based on the input ranges, or explicitly - * specified by the caller. If all input ranges have a static extent, the total - * size is calculated at compile time and a statically sized std::array<> is used. - * Otherwise this tries to use the type of the first input range as output type. - * - * Alternatively, the output container type can be specified explicitly. - */ -template -constexpr auto concat(Rs&&... ranges) - requires(all_same_v...>) -{ - if constexpr(std::same_as) { - // Try to auto-detect a reasonable output type given the input ranges - static_assert(sizeof...(Rs) > 0, "Cannot auto-detect the output type if not a single input range is provided."); - using candidate_result_t = std::remove_cvref_t>>; - using result_range_value_t = std::remove_cvref_t>; - - if constexpr((ranges::statically_spanable_range && ...)) { - // If all input ranges have a static extent, we can calculate the total size at compile time - // and therefore can use a statically sized output container. This is constexpr. - constexpr size_t total_size = (decltype(std::span{ranges})::extent + ... + 0); - using out_array_t = std::array; - return detail::concatenate(std::forward(ranges)...); - } else { - // If at least one input range has a dynamic extent, we must use a dynamically allocated output container. - // We assume that the user wants to use the first input range's container type as output type. - static_assert( - concepts::reservable_container, - "First input range has static extent, but a dynamically allocated output range is required. Please explicitly specify a dynamically allocatable output type."); - return detail::concatenate(std::forward(ranges)...); - } - } else { - // The caller has explicitly specified the output type - return detail::concatenate(std::forward(ranges)...); - } -} - template constexpr bool holds_any_of(const std::variant& v) noexcept { return (std::holds_alternative(v) || ...); } template -constexpr bool is_generalizable_to(const SpecialT&) noexcept { +constexpr bool is_generalizable_to(const SpecialT& /*unnamed*/) noexcept { return std::is_constructible_v; } template -constexpr bool is_generalizable_to(const std::variant&) noexcept { +constexpr bool is_generalizable_to(const std::variant& /*unnamed*/) noexcept { return (std::is_constructible_v && ...); } @@ -312,7 +85,7 @@ * variants types. */ template -constexpr GeneralVariantT generalize_to(SpecialT&& specific) noexcept +constexpr GeneralVariantT generalize_to(SpecialT&& specific) requires(std::is_constructible_v>) { return std::forward(specific); @@ -326,13 +99,33 @@ * variants types. */ template -constexpr GeneralVariantT generalize_to(std::variant specific) noexcept { +constexpr GeneralVariantT generalize_to(std::variant specific) { static_assert( is_generalizable_to(specific), "Desired general type must be implicitly constructible by all types of the specialized std::variant<>"); return std::visit([](auto s) -> GeneralVariantT { return s; }, std::move(specific)); } +/** + * @brief Converts a given variant into another variant whose type states + * are a subset of the given variant. + * + * @returns a variant of type SpecificVariantT if the given variant holds a + * type in SpecificVariantT, std::nullopt otherwise. + */ +template +constexpr std::optional specialize_to(GeneralVariantT&& v) { + return std::visit( + [](AlternativeT&& obj) -> std::optional { + if constexpr(std::is_constructible_v) { + return std::forward(obj); + } else { + return std::nullopt; + } + }, + std::forward(v)); +} + // This is a helper utility to emulate pattern matching with std::visit. // See https://en.cppreference.com/w/cpp/utility/variant/visit for more info. template @@ -343,71 +136,6 @@ template overloaded(Ts...) -> overloaded; -/** - * @brief Helper class to create a RAII-style cleanup callback - * - * Ensures that the cleanup callback given in the object's constructor is called - * when the object is destroyed. Use this to ensure some cleanup code runs when - * leaving the current scope. - */ -template -class scoped_cleanup final { - public: - explicit scoped_cleanup(FunT cleanup) : m_cleanup(std::move(cleanup)) {} - - scoped_cleanup(const scoped_cleanup&) = delete; - scoped_cleanup& operator=(const scoped_cleanup&) = delete; - - scoped_cleanup(scoped_cleanup&& other) : m_cleanup(std::move(other.m_cleanup)) { other.disengage(); } - - scoped_cleanup& operator=(scoped_cleanup&& other) { - if(this != &other) { - m_cleanup = std::move(other.m_cleanup); - other.disengage(); - } - return *this; - } - - ~scoped_cleanup() { - if(m_cleanup.has_value()) { - m_cleanup.value()(); - } - } - - /** - * Disengage the cleanup callback, i.e., prevent it from being called - */ - void disengage() { m_cleanup.reset(); } - - private: - std::optional m_cleanup; -}; - -/** -* Define BOTAN_ASSERT_IS_SOME -*/ -template -T assert_is_some(std::optional v, const char* expr, const char* func, const char* file, int line) { - if(v) { - return *v; - } else { - Botan::assertion_failure(expr, "optional had value", func, file, line); - } -} - -#define BOTAN_ASSERT_IS_SOME(v) assert_is_some(v, #v, __func__, __FILE__, __LINE__) - -/* - * @brief Helper class to pass literal strings to C++ templates - */ -template -class StringLiteral final { - public: - constexpr StringLiteral(const char (&str)[N]) { std::copy_n(str, N, value); } - - char value[N]; -}; - // TODO: C++23: replace with std::to_underlying template requires std::is_enum_v @@ -425,13 +153,14 @@ m_rawptr = nullptr; } - constexpr out_ptr_t(T& outptr) noexcept : m_ptr(outptr), m_rawptr(nullptr) {} + constexpr explicit out_ptr_t(T& outptr) noexcept : m_ptr(outptr), m_rawptr(nullptr) {} out_ptr_t(const out_ptr_t&) = delete; out_ptr_t(out_ptr_t&&) = delete; out_ptr_t& operator=(const out_ptr_t&) = delete; out_ptr_t& operator=(out_ptr_t&&) = delete; + // NOLINTNEXTLINE(*-explicit-conversions) - Implicit by design for C API interop [[nodiscard]] constexpr operator typename T::element_type **() && noexcept { return &m_rawptr; } private: @@ -442,30 +171,6 @@ return out_ptr_t{outptr}; } -template - requires std::is_default_constructible_v -[[nodiscard]] constexpr auto out_opt(std::optional& outopt) noexcept { - class out_opt_t { - public: - constexpr ~out_opt_t() noexcept { m_opt = m_raw; } - - constexpr out_opt_t(std::optional& outopt) noexcept : m_opt(outopt) {} - - out_opt_t(const out_opt_t&) = delete; - out_opt_t(out_opt_t&&) = delete; - out_opt_t& operator=(const out_opt_t&) = delete; - out_opt_t& operator=(out_opt_t&&) = delete; - - [[nodiscard]] constexpr operator T*() && noexcept { return &m_raw; } - - private: - std::optional& m_opt; - T m_raw; - }; - - return out_opt_t{outopt}; -} - } // namespace Botan #endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/strong_type.h botan3-3.12.0+dfsg/src/lib/utils/strong_type.h --- botan3-3.7.1+dfsg/src/lib/utils/strong_type.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/strong_type.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,13 +9,47 @@ #ifndef BOTAN_STRONG_TYPE_H_ #define BOTAN_STRONG_TYPE_H_ -#include -#include - #include +#include +#include +#include namespace Botan { +template +class Strong; + +template +struct is_strong_type : std::false_type {}; + +template +struct is_strong_type> : std::true_type {}; + +template +constexpr bool is_strong_type_v = is_strong_type...>::value; + +namespace concepts { + +template +concept streamable = requires(std::ostream& os, T a) { os << a; }; + +template +concept strong_type = is_strong_type_v; + +template +concept contiguous_strong_type = strong_type && contiguous_container; + +template +concept integral_strong_type = strong_type && std::integral; + +template +concept unsigned_integral_strong_type = strong_type && std::unsigned_integral; + +template +concept strong_type_with_capability = T::template has_capability(); + +} // namespace concepts + /** * Added as an additional "capability tag" to enable arithmetic operators with * plain numbers for Strong<> types that wrap a number. @@ -44,6 +78,7 @@ Strong_Base(Strong_Base&&) noexcept = default; Strong_Base& operator=(const Strong_Base&) = default; Strong_Base& operator=(Strong_Base&&) noexcept = default; + ~Strong_Base() = default; constexpr explicit Strong_Base(T v) : m_value(std::move(v)) {} @@ -132,20 +167,6 @@ decltype(auto) operator[](U&& i) noexcept(noexcept(this->get().operator[](i))) { return this->get()[std::forward(i)]; } - - template - decltype(auto) at(U&& i) const noexcept(noexcept(this->get().at(i))) - requires(concepts::has_bounds_checked_accessors) - { - return this->get().at(std::forward(i)); - } - - template - decltype(auto) at(U&& i) noexcept(noexcept(this->get().at(i))) - requires(concepts::has_bounds_checked_accessors) - { - return this->get().at(std::forward(i)); - } }; template @@ -193,7 +214,7 @@ * https://stackoverflow.com/a/69030899 */ template -class Strong : public detail::Strong_Adapter { +class Strong final : public detail::Strong_Adapter { public: using detail::Strong_Adapter::Strong_Adapter; @@ -620,7 +641,7 @@ } /** - * This mimmicks a std::span but keeps track of the strong-type information. Use + * This mimics a std::span but keeps track of the strong-type information. Use * this when you would want to use `const Strong<...>&` as a parameter * declaration. In particular this allows assigning strong-type information to * slices of a bigger buffer without copying the bytes. E.g: @@ -635,7 +656,7 @@ * // just annotates the 'Foo' strong-type info. */ template -class StrongSpan { +class StrongSpan final { using underlying_span = std:: conditional_t, std::span, std::span>; @@ -650,6 +671,7 @@ explicit StrongSpan(underlying_span span) : m_span(span) {} + // NOLINTNEXTLINE(*-explicit-conversions) StrongSpan(T& strong) : m_span(strong) {} // Allows implicit conversion from `StrongSpan` to `StrongSpan`. @@ -659,14 +681,19 @@ // TODO: Technically, we should be able to phrase this with a `requires std::is_const_v` // instead of the `std::enable_if` constructions. clang-tidy (14 or 15) doesn't seem // to pick up on that (yet?). As a result, for a non-const T it assumes this to be - // a declaration of an ordinary copy constructor. The existance of a copy constructor + // a declaration of an ordinary copy constructor. The existence of a copy constructor // is interpreted as "not cheap to copy", setting off the `performance-unnecessary-value-param` check. // See also: https://github.com/randombit/botan/issues/3591 - template >>> - StrongSpan(const StrongSpan& other) : m_span(other.get()) {} + template + // NOLINTNEXTLINE(*-explicit-conversions) + StrongSpan(const StrongSpan& other) + requires(std::is_same_v>) + : m_span(other.get()) {} StrongSpan(const StrongSpan& other) = default; + StrongSpan(StrongSpan&& other) = default; + StrongSpan& operator=(const StrongSpan& other) = default; + StrongSpan& operator=(StrongSpan&& other) = default; ~StrongSpan() = default; diff -Nru botan3-3.7.1+dfsg/src/lib/utils/thread_utils/barrier.cpp botan3-3.12.0+dfsg/src/lib/utils/thread_utils/barrier.cpp --- botan3-3.7.1+dfsg/src/lib/utils/thread_utils/barrier.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/thread_utils/barrier.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,11 +6,12 @@ */ #include +#include namespace Botan { void Barrier::wait(size_t delta) { - std::lock_guard lock(m_mutex); + const std::scoped_lock lock(m_mutex); m_value += delta; } diff -Nru botan3-3.7.1+dfsg/src/lib/utils/thread_utils/barrier.h botan3-3.12.0+dfsg/src/lib/utils/thread_utils/barrier.h --- botan3-3.7.1+dfsg/src/lib/utils/thread_utils/barrier.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/thread_utils/barrier.h 2026-05-07 01:38:28.000000000 +0000 @@ -23,7 +23,7 @@ */ class Barrier final { public: - explicit Barrier(int value = 0) : m_value(value), m_syncs(0) {} + explicit Barrier(int value = 0) : m_value(value) {} void wait(size_t delta); @@ -31,7 +31,7 @@ private: size_t m_value; - size_t m_syncs; + size_t m_syncs = 0; std::mutex m_mutex; std::condition_variable m_cond; }; diff -Nru botan3-3.7.1+dfsg/src/lib/utils/thread_utils/info.txt botan3-3.12.0+dfsg/src/lib/utils/thread_utils/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/thread_utils/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/thread_utils/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + THREAD_UTILS -> 20190922 - + name -> "Thread Utilities" diff -Nru botan3-3.7.1+dfsg/src/lib/utils/thread_utils/rwlock.cpp botan3-3.12.0+dfsg/src/lib/utils/thread_utils/rwlock.cpp --- botan3-3.7.1+dfsg/src/lib/utils/thread_utils/rwlock.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/thread_utils/rwlock.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,24 +12,24 @@ void RWLock::lock() { std::unique_lock lock(m_mutex); - while(m_state & is_writing) { + while((m_state & is_writing) == is_writing) { m_gate1.wait(lock); } m_state |= is_writing; - while(m_state & readers_mask) { + while((m_state & readers_mask) > 0) { m_gate2.wait(lock); } } void RWLock::unlock() { - std::unique_lock lock(m_mutex); + const std::unique_lock lock(m_mutex); m_state = 0; m_gate1.notify_all(); } void RWLock::lock_shared() { std::unique_lock lock(m_mutex); - while((m_state & is_writing) || (m_state & readers_mask) == readers_mask) { + while(((m_state & is_writing) == is_writing) || ((m_state & readers_mask) == readers_mask)) { m_gate1.wait(lock); } const uint32_t num_readers = (m_state & readers_mask) + 1; @@ -38,11 +38,11 @@ } void RWLock::unlock_shared() { - std::unique_lock lock(m_mutex); + const std::unique_lock lock(m_mutex); const uint32_t num_readers = (m_state & readers_mask) - 1; m_state &= ~readers_mask; m_state |= num_readers; - if(m_state & is_writing) { + if((m_state & is_writing) == is_writing) { if(num_readers == 0) { m_gate2.notify_one(); } diff -Nru botan3-3.7.1+dfsg/src/lib/utils/thread_utils/rwlock.h botan3-3.12.0+dfsg/src/lib/utils/thread_utils/rwlock.h --- botan3-3.7.1+dfsg/src/lib/utils/thread_utils/rwlock.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/thread_utils/rwlock.h 2026-05-07 01:38:28.000000000 +0000 @@ -33,8 +33,8 @@ uint32_t m_state; // 2**31 concurrent readers should be enough for anyone - static const uint32_t is_writing = static_cast(1) << 31; - static const uint32_t readers_mask = ~is_writing; + static constexpr uint32_t is_writing = static_cast(1) << 31; + static constexpr uint32_t readers_mask = ~is_writing; }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/utils/thread_utils/semaphore.cpp botan3-3.12.0+dfsg/src/lib/utils/thread_utils/semaphore.cpp --- botan3-3.7.1+dfsg/src/lib/utils/thread_utils/semaphore.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/thread_utils/semaphore.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,13 +7,13 @@ #include -// Based on code by Pierre Gaston (http://p9as.blogspot.com/2012/06/c11-semaphores.html) +// Based on code by Pierre Gaston namespace Botan { void Semaphore::release(size_t n) { for(size_t i = 0; i != n; ++i) { - std::lock_guard lock(m_mutex); + const std::scoped_lock lock(m_mutex); if(m_value++ < 0) { ++m_wakeups; diff -Nru botan3-3.7.1+dfsg/src/lib/utils/thread_utils/semaphore.h botan3-3.12.0+dfsg/src/lib/utils/thread_utils/semaphore.h --- botan3-3.7.1+dfsg/src/lib/utils/thread_utils/semaphore.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/thread_utils/semaphore.h 2026-05-07 01:38:28.000000000 +0000 @@ -15,7 +15,7 @@ class Semaphore final { public: - explicit Semaphore(int value = 0) : m_value(value), m_wakeups(0) {} + explicit Semaphore(int value = 0) : m_value(value) {} void acquire(); @@ -23,7 +23,7 @@ private: int m_value; - int m_wakeups; + int m_wakeups = 0; std::mutex m_mutex; std::condition_variable m_cond; }; diff -Nru botan3-3.7.1+dfsg/src/lib/utils/thread_utils/thread_pool.cpp botan3-3.12.0+dfsg/src/lib/utils/thread_utils/thread_pool.cpp --- botan3-3.7.1+dfsg/src/lib/utils/thread_utils/thread_pool.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/thread_utils/thread_pool.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,8 @@ #include #include +#include +#include #include namespace Botan { @@ -56,8 +58,7 @@ return g_thread_pool; } -Thread_Pool::Thread_Pool(std::optional opt_pool_size) { - m_shutdown = false; +Thread_Pool::Thread_Pool(std::optional opt_pool_size) : m_shutdown(false) { // On Linux, it is 16 length max, including terminator const std::string tname = "Botan thread"; @@ -68,20 +69,12 @@ size_t pool_size = opt_pool_size.value(); if(pool_size == 0) { - pool_size = OS::get_cpu_available(); - - // Unclear if this can happen, but be defensive - if(pool_size == 0) { - pool_size = 2; - } - /* * For large machines don't create too many threads, unless * explicitly asked to by the caller. */ - if(pool_size > 16) { - pool_size = 16; - } + const size_t cores = OS::get_cpu_available(); + pool_size = std::clamp(cores, 2, 16); } m_workers.resize(pool_size); @@ -94,9 +87,9 @@ void Thread_Pool::shutdown() { { - std::unique_lock lock(m_mutex); + const std::unique_lock lock(m_mutex); - if(m_shutdown == true) { + if(m_shutdown) { return; } @@ -111,7 +104,7 @@ m_workers.clear(); } -void Thread_Pool::queue_thunk(const std::function& fn) { +void Thread_Pool::queue_thunk(const std::function& work) { std::unique_lock lock(m_mutex); if(m_shutdown) { @@ -119,10 +112,11 @@ } if(m_workers.empty()) { - return fn(); + lock.unlock(); + return work(); } - m_tasks.push_back(fn); + m_tasks.push_back(work); m_more_tasks.notify_one(); } diff -Nru botan3-3.7.1+dfsg/src/lib/utils/thread_utils/thread_pool.h botan3-3.12.0+dfsg/src/lib/utils/thread_utils/thread_pool.h --- botan3-3.7.1+dfsg/src/lib/utils/thread_utils/thread_pool.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/thread_utils/thread_pool.h 2026-05-07 01:38:28.000000000 +0000 @@ -36,14 +36,14 @@ * is nullopt then the thread pool is disabled; all * work is executed immediately when queued. */ - Thread_Pool(std::optional pool_size); + explicit Thread_Pool(std::optional pool_size); /** * Initialize a thread pool with some number of threads * @param pool_size number of threads in the pool, if 0 * then some default value is chosen. */ - Thread_Pool(size_t pool_size = 0) : Thread_Pool(std::optional(pool_size)) {} + explicit Thread_Pool(size_t pool_size = 0) : Thread_Pool(std::optional(pool_size)) {} ~Thread_Pool() { shutdown(); } @@ -60,13 +60,13 @@ /* * Enqueue some work */ - void queue_thunk(const std::function&); + void queue_thunk(const std::function& work); template - auto run(F&& f, Args&&... args) -> std::future::type> { - using return_type = typename std::invoke_result::type; + auto run(F&& f, Args&&... args) -> std::future> { + using return_type = std::invoke_result_t; - auto future_work = std::bind(std::forward(f), std::forward(args)...); + auto future_work = std::bind(std::forward(f), std::forward(args)...); // NOLINT(*-avoid-bind) auto task = std::make_shared>(future_work); auto future_result = task->get_future(); queue_thunk([task]() { (*task)(); }); diff -Nru botan3-3.7.1+dfsg/src/lib/utils/time_utils.h botan3-3.12.0+dfsg/src/lib/utils/time_utils.h --- botan3-3.7.1+dfsg/src/lib/utils/time_utils.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/time_utils.h 2026-05-07 01:38:28.000000000 +0000 @@ -13,14 +13,12 @@ #include #endif -#include - namespace Botan { template -uint64_t measure_cost(std::chrono::milliseconds trial_msec, F func) { +uint64_t measure_cost(uint64_t trial_msec, F func) { #if defined(BOTAN_HAS_OS_UTILS) - const uint64_t trial_nsec = std::chrono::duration_cast(trial_msec).count(); + const uint64_t trial_nsec = trial_msec * 1000000; uint64_t total_nsec = 0; uint64_t trials = 0; diff -Nru botan3-3.7.1+dfsg/src/lib/utils/tree_hash/info.txt botan3-3.12.0+dfsg/src/lib/utils/tree_hash/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/tree_hash/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/tree_hash/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,7 +1,3 @@ - -TREE_HASH -> 20231006 - - name -> "Tree Hash" brief -> "Generic implementation of Merkle Tree Hashing" diff -Nru botan3-3.7.1+dfsg/src/lib/utils/tree_hash/tree_hash.h botan3-3.12.0+dfsg/src/lib/utils/tree_hash/tree_hash.h --- botan3-3.7.1+dfsg/src/lib/utils/tree_hash/tree_hash.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/tree_hash/tree_hash.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,12 +10,8 @@ #ifndef BOTAN_TREE_HASH_H_ #define BOTAN_TREE_HASH_H_ -#include #include -#include - -#include -#include +#include #include #include @@ -47,7 +43,7 @@ concept strong_span = is_strong_span_v; /** - * @brief An adress in a Tree. + * @brief An address in a Tree. */ template concept tree_address = requires(T a, TreeLayerIndex tree_layer, TreeNodeIndex tree_index) { @@ -96,7 +92,7 @@ * @param leaf_idx The optional index of the leaf used to sign in the bottom tree layer beginning with index 0. * nullopt if no node is signed, so we need no auth path. * @param node_size The size of each node in the tree. - * @param total_tree_height The hight of the merkle tree to construct. + * @param total_tree_height The height of the merkle tree to construct. * @param idx_offset If we compute a subtree this marks the index of the leftmost leaf node in the bottom layer * @param node_pair_hash The function to process two child nodes to compute their parent node. * @param gen_leaf The logic to create a leaf node given the address in the tree. Probably this function @@ -198,7 +194,7 @@ * @param leaf_idx The index of the leaf used to sig in the bottom layer beginning with 0. * @param leaf The leaf node used to sig. * @param node_size The size of each node in the tree. - * @param total_tree_height The hight of the merkle tree to construct. + * @param total_tree_height The height of the merkle tree to construct. * @param idx_offset If we compute a subtree this marks the index of the leftmost leaf node in the bottom layer. * @param node_pair_hash The function to process two child nodes to compute their parent node. * @param tree_address The address that is passed to node_pair hash. This function will update the diff -Nru botan3-3.7.1+dfsg/src/lib/utils/types.h botan3-3.12.0+dfsg/src/lib/utils/types.h --- botan3-3.7.1+dfsg/src/lib/utils/types.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/types.h 2026-05-07 01:38:28.000000000 +0000 @@ -14,7 +14,7 @@ #include // IWYU pragma: export #include // IWYU pragma: export #include // IWYU pragma: export -#include // IWYU pragma: export +#include /** * MSVC does define __cplusplus but pins it at 199711L, because "legacy". @@ -45,8 +45,8 @@ *
Public Key Interface Classes
* PK_Key_Agreement, PK_Signer, PK_Verifier, PK_Encryptor, PK_Decryptor, PK_KEM_Encryptor, PK_KEM_Decryptor *
Authenticated Encryption Modes
-* @ref CCM_Mode "CCM", @ref ChaCha20Poly1305_Mode "ChaCha20Poly1305", @ref EAX_Mode "EAX", -* @ref GCM_Mode "GCM", @ref OCB_Mode "OCB", @ref SIV_Mode "SIV" +* @ref Ascon_AEAD128 "Ascon-AEAD128" @ref CCM_Mode "CCM", @ref ChaCha20Poly1305_Mode "ChaCha20Poly1305", +* @ref EAX_Mode "EAX", @ref GCM_Mode "GCM", @ref OCB_Mode "OCB", @ref SIV_Mode "SIV" *
Block Ciphers
* @ref aria.h "ARIA", @ref aes.h "AES", @ref Blowfish, @ref camellia.h "Camellia", @ref Cascade_Cipher "Cascade", * @ref CAST_128 "CAST-128", @ref CAST_128 DES, @ref TripleDES "3DES", @@ -55,9 +55,9 @@ *
Stream Ciphers
* ChaCha, @ref CTR_BE "CTR", OFB, RC4, Salsa20 *
Hash Functions
-* BLAKE2b, @ref GOST_34_11 "GOST 34.11", @ref Keccak_1600 "Keccak", MD4, MD5, @ref RIPEMD_160 "RIPEMD-160", -* @ref SHA_1 "SHA-1", @ref SHA_224 "SHA-224", @ref SHA_256 "SHA-256", @ref SHA_384 "SHA-384", -* @ref SHA_512 "SHA-512", @ref Skein_512 "Skein-512", SM3, Streebog, Whirlpool +* @ref Ascon_Hash256 "Ascon-Hash256", BLAKE2b, @ref GOST_34_11 "GOST 34.11", @ref Keccak_1600 "Keccak", MD4, +* MD5, @ref RIPEMD_160 "RIPEMD-160", @ref SHA_1 "SHA-1", @ref SHA_224 "SHA-224", @ref SHA_256 "SHA-256", +* @ref SHA_384 "SHA-384", @ref SHA_512 "SHA-512", @ref Skein_512 "Skein-512", SM3, Streebog, Whirlpool *
Non-Cryptographic Checksums
* Adler32, CRC24, CRC32 *
Message Authentication Codes
@@ -89,7 +89,7 @@ * X509_Certificate, X509_CRL, X509_CA, Certificate_Extension, PKCS10_Request, X509_Cert_Options, * Certificate_Store, Certificate_Store_In_SQL, Certificate_Store_In_SQLite *
eXtendable Output Functions
-* @ref SHAKE_XOF "SHAKE" +* @ref Ascon_XOF128 "Ascon-XOF128", @ref SHAKE_XOF "SHAKE" * */ @@ -114,15 +114,11 @@ using s32bit = std::int32_t; #endif -#if(BOTAN_MP_WORD_BITS == 32) -typedef uint32_t word; -#elif(BOTAN_MP_WORD_BITS == 64) -typedef uint64_t word; -#else - #error BOTAN_MP_WORD_BITS must be 32 or 64 -#endif +static constexpr bool HasNative64BitRegisters = sizeof(void*) >= 8; + +using word = std::conditional_t; -#if defined(__SIZEOF_INT128__) && defined(BOTAN_TARGET_CPU_HAS_NATIVE_64BIT) +#if defined(__SIZEOF_INT128__) #define BOTAN_TARGET_HAS_NATIVE_UINT128 // GCC complains if this isn't marked with __extension__ @@ -135,6 +131,11 @@ */ static_assert(sizeof(std::size_t) == 8 || sizeof(std::size_t) == 4, "This platform has an unexpected size for size_t"); +/** +* How much to allocate for a buffer of no particular size +*/ +constexpr size_t DefaultBufferSize = 4096; + } // namespace Botan #endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/uuid/info.txt botan3-3.12.0+dfsg/src/lib/utils/uuid/info.txt --- botan3-3.7.1+dfsg/src/lib/utils/uuid/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/uuid/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + UUID -> 20180930 - + name -> "UUID" diff -Nru botan3-3.7.1+dfsg/src/lib/utils/uuid/uuid.cpp botan3-3.12.0+dfsg/src/lib/utils/uuid/uuid.cpp --- botan3-3.7.1+dfsg/src/lib/utils/uuid/uuid.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/uuid/uuid.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,7 @@ #include +#include #include #include #include @@ -40,7 +41,7 @@ } std::string just_hex; - for(char c : uuid_str) { + for(const char c : uuid_str) { if(c == '-') { continue; } @@ -56,7 +57,7 @@ } std::string UUID::to_string() const { - if(is_valid() == false) { + if(!is_valid()) { throw Invalid_State("UUID object is empty cannot convert to string"); } diff -Nru botan3-3.7.1+dfsg/src/lib/utils/uuid/uuid.h botan3-3.12.0+dfsg/src/lib/utils/uuid/uuid.h --- botan3-3.7.1+dfsg/src/lib/utils/uuid/uuid.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/uuid/uuid.h 2026-05-07 01:38:28.000000000 +0000 @@ -23,25 +23,22 @@ /** * Create an uninitialized UUID object */ - UUID() : m_uuid() {} + UUID() = default; /** * Create a random UUID */ - UUID(RandomNumberGenerator& rng); + BOTAN_FUTURE_EXPLICIT UUID(RandomNumberGenerator& rng); /** * Load a UUID from a 16 byte vector */ - UUID(const std::vector& blob); - - UUID& operator=(const UUID& other) = default; - UUID(const UUID& other) = default; + BOTAN_FUTURE_EXPLICIT UUID(const std::vector& blob); /** * Decode a UUID string */ - UUID(std::string_view uuid_str); + BOTAN_FUTURE_EXPLICIT UUID(std::string_view uuid_str); /** * Convert the UUID to a string diff -Nru botan3-3.7.1+dfsg/src/lib/utils/value_barrier.h botan3-3.12.0+dfsg/src/lib/utils/value_barrier.h --- botan3-3.7.1+dfsg/src/lib/utils/value_barrier.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/value_barrier.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,68 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_VALUE_BARRIER_H_ +#define BOTAN_VALUE_BARRIER_H_ + +#include +#include +#include + +namespace Botan::CT { + +/** +* This function returns its argument, but (if called in a non-constexpr context) +* attempts to prevent the compiler from reasoning about the value or the possible +* range of values. Such optimizations have a way of breaking constant time code. +* +* The method that is use is decided at configuration time based on the target +* compiler and architecture (see `ct_value_barrier` blocks in `src/build-data/cc`). +* The decision can be overridden by the user with the configure.py option +* `--ct-value-barrier-type=` +* +* There are three options currently possible in the data files and with the +* option: +* +* * `asm`: Use an inline assembly expression which (currently) prevents Clang +* and GCC from optimizing based on the possible value of the input expression. +* +* * `volatile`: Launder the input through a volatile variable. This is likely +* to cause significant performance regressions since the value must be +* actually stored and loaded back from memory each time. +* +* * `none`: disable constant time barriers entirely. This is used +* with MSVC, which is not known to perform optimizations that break +* constant time code and which does not support GCC-style inline asm. +* +*/ +template + requires(!std::same_as) +constexpr inline T value_barrier(T x) { + if(std::is_constant_evaluated()) { + return x; + } else { +#if defined(BOTAN_CT_VALUE_BARRIER_USE_ASM) + /* + * We may want a "stronger" statement such as + * asm volatile("" : "+r,m"(x) : : "memory); + * (see https://theunixzoo.co.uk/blog/2021-10-14-preventing-optimisations.html) + * however the current approach seems sufficient with current compilers, + * and is minimally damaging with regards to degrading code generation. + */ + asm("" : "+r"(x) : /* no input */); // NOLINT(*-no-assembler) + return x; +#elif defined(BOTAN_CT_VALUE_BARRIER_USE_VOLATILE) + volatile T vx = x; + return vx; +#else + return x; +#endif + } +} + +} // namespace Botan::CT + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/utils/version.cpp botan3-3.12.0+dfsg/src/lib/utils/version.cpp --- botan3-3.7.1+dfsg/src/lib/utils/version.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/version.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,62 +8,19 @@ #include #include +#include +#include namespace Botan { -/* - These are intentionally compiled rather than inlined, so an - application running against a shared library can test the true - version they are running against. -*/ - -// NOLINTNEXTLINE(*-macro-usage) -#define QUOTE(name) #name -// NOLINTNEXTLINE(*-macro-usage) -#define STR(macro) QUOTE(macro) - const char* short_version_cstr() { - return STR(BOTAN_VERSION_MAJOR) "." STR(BOTAN_VERSION_MINOR) "." STR(BOTAN_VERSION_PATCH) -#if defined(BOTAN_VERSION_SUFFIX) - STR(BOTAN_VERSION_SUFFIX) -#endif - ; + return BOTAN_SHORT_VERSION_STRING; } const char* version_cstr() { - /* - It is intentional that this string is a compile-time constant; - it makes it much easier to find in binaries. - */ - - return "Botan " STR(BOTAN_VERSION_MAJOR) "." STR(BOTAN_VERSION_MINOR) "." STR(BOTAN_VERSION_PATCH) -#if defined(BOTAN_VERSION_SUFFIX) - STR(BOTAN_VERSION_SUFFIX) -#endif - " (" -#if defined(BOTAN_UNSAFE_FUZZER_MODE) || defined(BOTAN_TERMINATE_ON_ASSERTS) - "UNSAFE " - #if defined(BOTAN_UNSAFE_FUZZER_MODE) - "FUZZER MODE " - #endif - #if defined(BOTAN_TERMINATE_ON_ASSERTS) - "TERMINATE ON ASSERTS " - #endif - "BUILD " -#endif - BOTAN_VERSION_RELEASE_TYPE -#if(BOTAN_VERSION_DATESTAMP != 0) - ", dated " STR(BOTAN_VERSION_DATESTAMP) -#endif - ", revision " BOTAN_VERSION_VC_REVISION ", distribution " BOTAN_DISTRIBUTION_INFO ")"; + return BOTAN_FULL_VERSION_STRING; } -#undef STR -#undef QUOTE - -/* -* Return the version as a string -*/ std::string version_string() { return std::string(version_cstr()); } @@ -76,6 +33,22 @@ return BOTAN_VERSION_DATESTAMP; } +std::optional version_vc_revision() { +#if defined(BOTAN_VC_REVISION) + return std::string(BOTAN_VC_REVISION); +#else + return std::nullopt; +#endif +} + +std::optional version_distribution_info() { +#if defined(BOTAN_DISTRIBUTION_INFO_STRING) + return std::string(BOTAN_DISTRIBUTION_INFO_STRING); +#else + return std::nullopt; +#endif +} + /* * Return parts of the version as integers */ @@ -91,6 +64,14 @@ return BOTAN_VERSION_PATCH; } +bool unsafe_for_production_build() { +#if defined(BOTAN_UNSAFE_FUZZER_MODE) || defined(BOTAN_TERMINATE_ON_ASSERTS) + return true; +#else + return false; +#endif +} + std::string runtime_version_check(uint32_t major, uint32_t minor, uint32_t patch) { if(major != version_major() || minor != version_minor() || patch != version_patch()) { return fmt("Warning: linked version ({}) does not match version built against ({}.{}.{})\n", diff -Nru botan3-3.7.1+dfsg/src/lib/utils/version.h botan3-3.12.0+dfsg/src/lib/utils/version.h --- botan3-3.7.1+dfsg/src/lib/utils/version.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/utils/version.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #define BOTAN_VERSION_H_ #include +#include #include namespace Botan { @@ -71,6 +72,24 @@ BOTAN_PUBLIC_API(2, 0) uint32_t version_patch(); /** +* Returns a string that is set to a revision identifier corresponding to the +* source, or `nullopt` if this could not be determined. It is set for all +* official releases, and for builds that originated from within a git checkout. +* +* @return VC revision +*/ +BOTAN_PUBLIC_API(3, 8) std::optional version_vc_revision(); + +/** +* Return any string that is set at build time using the `--distribution-info` +* option. It allows a packager of the library to specify any distribution-specific +* patches. If no value is given at build time, returns `nullopt`. +* +* @return distribution info +*/ +BOTAN_PUBLIC_API(3, 8) std::optional version_distribution_info(); + +/** * Usable for checking that the DLL version loaded at runtime exactly matches the * compile-time version. Call using BOTAN_VERSION_* macro values, like so: * @@ -84,6 +103,22 @@ */ BOTAN_PUBLIC_API(2, 0) std::string runtime_version_check(uint32_t major, uint32_t minor, uint32_t patch); +/** +* Certain build-time options, used for testing, result in a binary which is not +* safe for use in a production system. This function can be used to test for such +* a configuration at runtime. +* +* Currently these unsafe conditions include: +* +* - Unsafe fuzzer mode (--unsafe-fuzzer-mode) which intentionally disables various +* checks in order to improve the effectiveness of fuzzing. +* - Terminate on asserts (--unsafe-terminate-on-asserts) which intentionally aborts +* if any internal assertion failure occurs, rather than throwing an exception. +*/ +BOTAN_PUBLIC_API(3, 8) bool unsafe_for_production_build(); + +// NOLINTBEGIN(*-macro-usage) + /* * Macros for compile-time version checks * @@ -100,7 +135,7 @@ * #endif * ``` */ -#define BOTAN_VERSION_CODE_FOR(a, b, c) ((a << 16) | (b << 8) | (c)) +#define BOTAN_VERSION_CODE_FOR(a, b, c) (((a) << 16) | ((b) << 8) | (c)) /** * Compare using BOTAN_VERSION_CODE_FOR, as in @@ -110,6 +145,8 @@ */ #define BOTAN_VERSION_CODE BOTAN_VERSION_CODE_FOR(BOTAN_VERSION_MAJOR, BOTAN_VERSION_MINOR, BOTAN_VERSION_PATCH) +// NOLINTEND(*-macro-usage) + } // namespace Botan #endif diff -Nru botan3-3.7.1+dfsg/src/lib/x509/alt_name.cpp botan3-3.12.0+dfsg/src/lib/x509/alt_name.cpp --- botan3-3.7.1+dfsg/src/lib/x509/alt_name.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/alt_name.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,7 +11,6 @@ #include #include #include -#include namespace Botan { @@ -45,11 +44,21 @@ m_ipv4_addr.insert(ip); } +void AlternativeName::add_ipv6_address(const IPv6Address& ip) { + m_ipv6_addr.insert(ip); +} + size_t AlternativeName::count() const { - const auto sum = checked_add( - m_dns.size(), m_uri.size(), m_email.size(), m_ipv4_addr.size(), m_dn_names.size(), m_othernames.size()); + const auto sum = checked_add(m_dns.size(), + m_uri.size(), + m_email.size(), + m_ipv4_addr.size(), + m_ipv6_addr.size(), + m_dn_names.size(), + m_othernames.size()); - return BOTAN_ASSERT_IS_SOME(sum); + BOTAN_ASSERT_NOMSG(sum.has_value()); + return sum.value(); } bool AlternativeName::has_items() const { @@ -82,12 +91,12 @@ } for(const auto& name : m_email) { - ASN1_String str(name, ASN1_Type::Ia5String); + const ASN1_String str(name, ASN1_Type::Ia5String); der.add_object(ASN1_Type(1), ASN1_Class::ContextSpecific, str.value()); } for(const auto& name : m_dns) { - ASN1_String str(name, ASN1_Type::Ia5String); + const ASN1_String str(name, ASN1_Type::Ia5String); der.add_object(ASN1_Type(2), ASN1_Class::ContextSpecific, str.value()); } @@ -96,16 +105,21 @@ } for(const auto& name : m_uri) { - ASN1_String str(name, ASN1_Type::Ia5String); + const ASN1_String str(name, ASN1_Type::Ia5String); der.add_object(ASN1_Type(6), ASN1_Class::ContextSpecific, str.value()); } - for(uint32_t ip : m_ipv4_addr) { + for(const uint32_t ip : m_ipv4_addr) { auto ip_buf = store_be(ip); // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) der.add_object(ASN1_Type(7), ASN1_Class::ContextSpecific, ip_buf.data(), 4); } + for(const auto& ip : m_ipv6_addr) { + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) + der.add_object(ASN1_Type(7), ASN1_Class::ContextSpecific, ip.address().data(), ip.address().size()); + } + der.end_cons(); } @@ -113,24 +127,24 @@ BER_Decoder names = source.start_sequence(); while(names.more_items()) { - BER_Object obj = names.get_next_object(); + const BER_Object obj = names.get_next_object(); if(obj.is_a(0, ASN1_Class::ExplicitContextSpecific)) { - BER_Decoder othername(obj); + BER_Decoder othername(obj, names.limits()); OID oid; othername.decode(oid); if(othername.more_items()) { - BER_Object othername_value_outer = othername.get_next_object(); + const BER_Object othername_value_outer = othername.get_next_object(); othername.verify_end(); if(!othername_value_outer.is_a(0, ASN1_Class::ExplicitContextSpecific)) { throw Decoding_Error("Invalid tags on otherName value"); } - BER_Decoder othername_value_inner(othername_value_outer); + BER_Decoder othername_value_inner(othername_value_outer, names.limits()); - BER_Object value = othername_value_inner.get_next_object(); + const BER_Object value = othername_value_inner.get_next_object(); othername_value_inner.verify_end(); if(ASN1_String::is_string_type(value.type()) && value.get_class() == ASN1_Class::Universal) { @@ -142,7 +156,7 @@ } else if(obj.is_a(2, ASN1_Class::ContextSpecific)) { m_dns.insert(check_and_canonicalize_dns_name(ASN1::to_string(obj))); } else if(obj.is_a(4, ASN1_Class::ContextSpecific | ASN1_Class::Constructed)) { - BER_Decoder dec(obj); + BER_Decoder dec(obj, names.limits()); X509_DN dn; dec.decode(dn); this->add_dn(dn); @@ -152,7 +166,10 @@ if(obj.length() == 4) { const uint32_t ip = load_be(obj.bits(), 0); this->add_ipv4_address(ip); - } else if(obj.length() != 16) { + } else if(obj.length() == 16) { + const IPv6Address ip(std::span{obj.bits(), 16}); + this->add_ipv6_address(ip); + } else { throw Decoding_Error("Invalid IP constraint neither IPv4 or IPv6"); } } diff -Nru botan3-3.7.1+dfsg/src/lib/x509/asn1_alt_name.cpp botan3-3.12.0+dfsg/src/lib/x509/asn1_alt_name.cpp --- botan3-3.7.1+dfsg/src/lib/x509/asn1_alt_name.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/asn1_alt_name.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -97,10 +97,14 @@ names.emplace("URI", nm); } - for(uint32_t ipv4 : this->ipv4_address()) { + for(const uint32_t ipv4 : this->ipv4_address()) { names.emplace("IP", ipv4_to_string(ipv4)); } + for(const auto& ipv6 : this->ipv6_address()) { + names.emplace("IPv6", ipv6.to_string()); + } + for(const auto& nm : this->directory_names()) { names.emplace("DN", nm.to_string()); } @@ -155,7 +159,7 @@ return ret; } else if(attr == "IP") { std::vector ip_str; - for(uint32_t ipv4 : this->ipv4_address()) { + for(const uint32_t ipv4 : this->ipv4_address()) { ip_str.push_back(ipv4_to_string(ipv4)); } return ip_str; diff -Nru botan3-3.7.1+dfsg/src/lib/x509/cert_status.cpp botan3-3.12.0+dfsg/src/lib/x509/cert_status.cpp --- botan3-3.7.1+dfsg/src/lib/x509/cert_status.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/cert_status.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -71,6 +71,8 @@ return "Certificate policy error"; case Certificate_Status_Code::DUPLICATE_CERT_POLICY: return "Certificate contains duplicate policy"; + case Certificate_Status_Code::EXTENSION_ENCODING_ERROR: + return "Certificate extension encoding error"; case Certificate_Status_Code::INVALID_USAGE: return "Certificate does not allow the requested usage"; case Certificate_Status_Code::CERT_CHAIN_TOO_LONG: @@ -89,6 +91,10 @@ return "Certificate does not match provided name"; case Certificate_Status_Code::NAME_CONSTRAINT_ERROR: return "Certificate does not pass name constraint"; + case Certificate_Status_Code::IPADDR_BLOCKS_ERROR: + return "IP Address Blocks extension invalid"; + case Certificate_Status_Code::AS_BLOCKS_ERROR: + return "AS Number Blocks extension invalid"; case Certificate_Status_Code::UNKNOWN_CRITICAL_EXTENSION: return "Unknown critical extension encountered"; case Certificate_Status_Code::DUPLICATE_CERT_EXTENSION: @@ -100,11 +106,11 @@ case Certificate_Status_Code::OCSP_SIGNATURE_ERROR: return "OCSP signature error"; case Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND: - return "Unable to find certificate issusing OCSP response"; + return "Unable to find certificate issuing OCSP response"; case Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE: return "OCSP issuer's keyusage prohibits OCSP"; case Certificate_Status_Code::OCSP_RESPONSE_INVALID: - return "OCSP parsing valid"; + return "OCSP response was unparsable or had invalid encoding"; case Certificate_Status_Code::OCSP_NO_HTTP: return "OCSP requests not available, no HTTP support compiled in"; case Certificate_Status_Code::CERT_IS_REVOKED: @@ -119,6 +125,8 @@ return "Certificate signed with unknown/unavailable algorithm"; case Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS: return "Certificate signature has invalid parameters"; + case Certificate_Status_Code::EXCEEDED_SEARCH_LIMITS: + return "Exceeded search limitations while pathfinding"; // intentionally no default so we are warned if new enum values are added } diff -Nru botan3-3.7.1+dfsg/src/lib/x509/certstor.cpp botan3-3.12.0+dfsg/src/lib/x509/certstor.cpp --- botan3-3.7.1+dfsg/src/lib/x509/certstor.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/certstor.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,15 +8,33 @@ #include +#include +#include #include -#include #include #include +#include +#include +#include namespace Botan { Certificate_Store::~Certificate_Store() = default; +bool Certificate_Store::certificate_known(const X509_Certificate& cert) const { + return contains(cert); +} + +bool Certificate_Store::contains(const X509_Certificate& searching) const { + for(const auto& cert : find_all_certs(searching.subject_dn(), searching.subject_key_id())) { + if(cert == searching) { + return true; + } + } + + return false; +} + std::optional Certificate_Store::find_cert(const X509_DN& subject_dn, const std::vector& key_id) const { const auto certs = find_all_certs(subject_dn, key_id); @@ -33,20 +51,53 @@ return std::nullopt; } +class Certificate_Store_In_Memory::Impl final { + public: + std::vector m_certs; + std::set m_cert_tags; + std::map> m_dn_to_indices; + std::vector m_crls; + std::map m_issuer_dn_to_crl_idx; +}; + +Certificate_Store_In_Memory::Certificate_Store_In_Memory() : m_impl(std::make_unique()) {} + +Certificate_Store_In_Memory::Certificate_Store_In_Memory(const Certificate_Store_In_Memory& other) : + m_impl(std::make_unique(other.impl())) {} + +Certificate_Store_In_Memory::Certificate_Store_In_Memory(Certificate_Store_In_Memory&& other) noexcept = default; + +Certificate_Store_In_Memory& Certificate_Store_In_Memory::operator=(Certificate_Store_In_Memory&& other) noexcept = + default; + +Certificate_Store_In_Memory::~Certificate_Store_In_Memory() = default; + +Certificate_Store_In_Memory::Impl& Certificate_Store_In_Memory::impl() { + BOTAN_STATE_CHECK(m_impl != nullptr); + return *m_impl; +} + +const Certificate_Store_In_Memory::Impl& Certificate_Store_In_Memory::impl() const { + BOTAN_STATE_CHECK(m_impl != nullptr); + return *m_impl; +} + void Certificate_Store_In_Memory::add_certificate(const X509_Certificate& cert) { - for(const auto& c : m_certs) { - if(c == cert) { - return; - } + auto& store = impl(); + const auto tag = cert.tag(); + if(!store.m_cert_tags.contains(tag)) { + store.m_cert_tags.insert(tag); + const size_t idx = store.m_certs.size(); + store.m_certs.push_back(cert); + store.m_dn_to_indices[cert.subject_dn()].push_back(idx); } - - m_certs.push_back(cert); } std::vector Certificate_Store_In_Memory::all_subjects() const { + const auto& store = impl(); std::vector subjects; - subjects.reserve(m_certs.size()); - for(const auto& cert : m_certs) { + subjects.reserve(store.m_certs.size()); + for(const auto& cert : store.m_certs) { subjects.push_back(cert.subject_dn()); } return subjects; @@ -54,19 +105,24 @@ std::optional Certificate_Store_In_Memory::find_cert(const X509_DN& subject_dn, const std::vector& key_id) const { - for(const auto& cert : m_certs) { - // Only compare key ids if set in both call and in the cert + const auto& store = impl(); + const auto it = store.m_dn_to_indices.find(subject_dn); + if(it == store.m_dn_to_indices.end()) { + return std::nullopt; + } + + for(const size_t idx : it->second) { + const auto& cert = store.m_certs[idx]; + BOTAN_ASSERT_NOMSG(cert.subject_dn() == subject_dn); + if(!key_id.empty()) { const std::vector& skid = cert.subject_key_id(); - if(!skid.empty() && skid != key_id) { // no match continue; } } - if(cert.subject_dn() == subject_dn) { - return cert; - } + return cert; } return std::nullopt; @@ -74,20 +130,26 @@ std::vector Certificate_Store_In_Memory::find_all_certs(const X509_DN& subject_dn, const std::vector& key_id) const { + const auto& store = impl(); std::vector matches; - for(const auto& cert : m_certs) { + const auto it = store.m_dn_to_indices.find(subject_dn); + if(it == store.m_dn_to_indices.end()) { + return matches; + } + + for(const size_t idx : it->second) { + const auto& cert = store.m_certs[idx]; + BOTAN_ASSERT_NOMSG(cert.subject_dn() == subject_dn); + if(!key_id.empty()) { const std::vector& skid = cert.subject_key_id(); - if(!skid.empty() && skid != key_id) { // no match continue; } } - if(cert.subject_dn() == subject_dn) { - matches.push_back(cert); - } + matches.push_back(cert); } return matches; @@ -99,11 +161,8 @@ throw Invalid_Argument("Certificate_Store_In_Memory::find_cert_by_pubkey_sha1 invalid hash"); } - auto hash = HashFunction::create("SHA-1"); - - for(const auto& cert : m_certs) { - hash->update(cert.subject_public_key_bitstring()); - if(key_hash == hash->final_stdvec()) { //final_stdvec also clears the hash to initial state + for(const auto& cert : impl().m_certs) { + if(key_hash == cert.subject_public_key_bitstring_sha1()) { return cert; } } @@ -117,11 +176,19 @@ throw Invalid_Argument("Certificate_Store_In_Memory::find_cert_by_raw_subject_dn_sha256 invalid hash"); } - auto hash = HashFunction::create("SHA-256"); + for(const auto& cert : impl().m_certs) { + if(subject_hash == cert.raw_subject_dn_sha256()) { + return cert; + } + } + + return std::nullopt; +} - for(const auto& cert : m_certs) { - hash->update(cert.raw_subject_dn()); - if(subject_hash == hash->final_stdvec()) { //final_stdvec also clears the hash to initial state +std::optional Certificate_Store_In_Memory::find_cert_by_issuer_dn_and_serial_number( + const X509_DN& issuer_dn, std::span serial_number) const { + for(const auto& cert : impl().m_certs) { + if(cert.issuer_dn() == issuer_dn && std::ranges::equal(cert.serial_number(), serial_number)) { return cert; } } @@ -130,49 +197,64 @@ } void Certificate_Store_In_Memory::add_crl(const X509_CRL& crl) { + auto& store = impl(); const X509_DN& crl_issuer = crl.issuer_dn(); - for(auto& c : m_crls) { + if(const auto it = store.m_issuer_dn_to_crl_idx.find(crl_issuer); it != store.m_issuer_dn_to_crl_idx.end()) { + auto& current_crl = store.m_crls.at(it->second); + // Found an update of a previously existing one; replace it - if(c.issuer_dn() == crl_issuer) { - if(c.this_update() <= crl.this_update()) { - c = crl; - } - return; + if(current_crl.this_update() <= crl.this_update()) { + current_crl = crl; } + + return; } // Totally new CRL, add to the list - m_crls.push_back(crl); + store.m_issuer_dn_to_crl_idx.emplace(crl_issuer, store.m_crls.size()); + store.m_crls.push_back(crl); } std::optional Certificate_Store_In_Memory::find_crl_for(const X509_Certificate& subject) const { + const auto& store = impl(); const std::vector& key_id = subject.authority_key_id(); - for(const auto& c : m_crls) { - // Only compare key ids if set in both call and in the CRL - if(!key_id.empty()) { - const std::vector& akid = c.authority_key_id(); + const auto it = store.m_issuer_dn_to_crl_idx.find(subject.issuer_dn()); + if(it == store.m_issuer_dn_to_crl_idx.end()) { + return std::nullopt; + } - if(!akid.empty() && akid != key_id) { // no match - continue; - } - } + const auto& crl = store.m_crls.at(it->second); - if(c.issuer_dn() == subject.issuer_dn()) { - return c; + // Only compare key ids if set in both call and in the CRL + if(!key_id.empty()) { + const std::vector& akid = crl.authority_key_id(); + + if(!akid.empty() && akid != key_id) { + return std::nullopt; } } - return {}; + return crl; +} + +bool Certificate_Store_In_Memory::contains(const X509_Certificate& cert) const { + return impl().m_cert_tags.contains(cert.tag()); +} + +Certificate_Store_In_Memory::Certificate_Store_In_Memory(const X509_Certificate& cert) : Certificate_Store_In_Memory() { + add_certificate(cert); } -Certificate_Store_In_Memory::Certificate_Store_In_Memory(const X509_Certificate& cert) { +Certificate_Store_In_Memory::Certificate_Store_In_Memory(const X509_Certificate& cert, const X509_CRL& crl) : + Certificate_Store_In_Memory() { add_certificate(cert); + add_crl(crl); } #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) -Certificate_Store_In_Memory::Certificate_Store_In_Memory(std::string_view dir) { +Certificate_Store_In_Memory::Certificate_Store_In_Memory(std::string_view dir) : Certificate_Store_In_Memory() { if(dir.empty()) { return; } @@ -188,8 +270,7 @@ DataSource_Stream src(cert_file, true); while(!src.end_of_data()) { try { - X509_Certificate cert(src); - m_certs.push_back(cert); + add_certificate(X509_Certificate(src)); } catch(std::exception&) { // stop searching for other certificate at first exception break; diff -Nru botan3-3.7.1+dfsg/src/lib/x509/certstor.h botan3-3.12.0+dfsg/src/lib/x509/certstor.h --- botan3-3.7.1+dfsg/src/lib/x509/certstor.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/certstor.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,16 +8,19 @@ #ifndef BOTAN_CERT_STORE_H_ #define BOTAN_CERT_STORE_H_ +#include #include #include +#include #include +#include namespace Botan { /** * Certificate Store Interface */ -class BOTAN_PUBLIC_API(2, 0) Certificate_Store { +class BOTAN_PUBLIC_API(2, 0) Certificate_Store /* NOLINT(*-special-member-functions) */ { public: virtual ~Certificate_Store(); @@ -57,6 +60,16 @@ const std::vector& subject_hash) const = 0; /** + * Find a certificate by searching for one with a matching issuer DN and + * serial number. Used for CMS or PKCS#7. + * @param issuer_dn the distinguished name of the issuer + * @param serial_number the certificate's serial number + * @return a matching certificate or nullopt otherwise + */ + virtual std::optional find_cert_by_issuer_dn_and_serial_number( + const X509_DN& issuer_dn, std::span serial_number) const = 0; + + /** * Finds a CRL for the given certificate * @param subject the subject certificate * @return the CRL for subject or nullopt otherwise @@ -64,12 +77,17 @@ virtual std::optional find_crl_for(const X509_Certificate& subject) const; /** - * @return whether the certificate is known - * @param cert certififcate to be searched + * @return whether this certificate is contained within the store + * @param cert certificate to be searched + * + * Default implementation uses find_all_certs */ - bool certificate_known(const X509_Certificate& cert) const { - return find_cert(cert.subject_dn(), cert.subject_key_id()).has_value(); - } + virtual bool contains(const X509_Certificate& cert) const; + + /** + * Old version of contains + */ + bool certificate_known(const X509_Certificate& cert) const; // remove this (used by TLS::Server) virtual std::vector all_subjects() const = 0; @@ -94,9 +112,22 @@ explicit Certificate_Store_In_Memory(const X509_Certificate& cert); /** + * Adds given certificate and CRL to the store. + */ + Certificate_Store_In_Memory(const X509_Certificate& cert, const X509_CRL& crl); + + /** * Create an empty store. */ - Certificate_Store_In_Memory() = default; + Certificate_Store_In_Memory(); + + Certificate_Store_In_Memory(const Certificate_Store_In_Memory& other); + Certificate_Store_In_Memory(Certificate_Store_In_Memory&& other) noexcept; + + Certificate_Store_In_Memory& operator=(const Certificate_Store_In_Memory& other) = delete; + Certificate_Store_In_Memory& operator=(Certificate_Store_In_Memory&& other) noexcept; + + ~Certificate_Store_In_Memory() override; /** * Add a certificate to the store. @@ -134,15 +165,23 @@ std::optional find_cert_by_raw_subject_dn_sha256( const std::vector& subject_hash) const override; + std::optional find_cert_by_issuer_dn_and_serial_number( + const X509_DN& issuer_dn, std::span serial_number) const override; + /** * Finds a CRL for the given certificate */ std::optional find_crl_for(const X509_Certificate& subject) const override; + bool contains(const X509_Certificate& cert) const override; + private: - // TODO: Add indexing on the DN and key id to avoid linear search - std::vector m_certs; - std::vector m_crls; + class Impl; + + Impl& impl(); + const Impl& impl() const; + + std::unique_ptr m_impl; }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/x509/certstor_flatfile/certstor_flatfile.cpp botan3-3.12.0+dfsg/src/lib/x509/certstor_flatfile/certstor_flatfile.cpp --- botan3-3.7.1+dfsg/src/lib/x509/certstor_flatfile/certstor_flatfile.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/certstor_flatfile/certstor_flatfile.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,10 +12,12 @@ #include #include #include -#include +#include namespace Botan { + namespace { + std::vector> decode_all_certificates(DataSource& source) { std::vector> pems; @@ -33,6 +35,7 @@ return pems; } + } // namespace Flatfile_Certificate_Store::Flatfile_Certificate_Store(std::string_view file, bool ignore_non_ca) { @@ -52,10 +55,17 @@ * but we cannot fix the trust store. So instead just ignore any such certificate. */ if(cert.is_self_signed() && cert.is_CA_cert()) { - m_all_subjects.push_back(cert.subject_dn()); - m_dn_to_cert[cert.subject_dn()].push_back(cert); - m_pubkey_sha1_to_cert.emplace(cert.subject_public_key_bitstring_sha1(), cert); - m_subject_dn_sha256_to_cert.emplace(cert.raw_subject_dn_sha256(), cert); + const auto tag = cert.tag(); + + // dedup + if(!m_cert_tags.contains(tag)) { + m_cert_tags.insert(tag); + m_all_subjects.push_back(cert.subject_dn()); + m_dn_to_cert[cert.subject_dn()].push_back(cert); + m_pubkey_sha1_to_cert.emplace(cert.subject_public_key_bitstring_sha1(), cert); + m_subject_dn_sha256_to_cert.emplace(cert.raw_subject_dn_sha256(), cert); + m_issuer_dn_to_cert[cert.issuer_dn()].push_back(cert); + } } else if(!ignore_non_ca) { throw Invalid_Argument("Flatfile_Certificate_Store received non CA cert " + cert.subject_dn().to_string()); } @@ -70,19 +80,27 @@ return m_all_subjects; } +bool Flatfile_Certificate_Store::contains(const X509_Certificate& cert) const { + return m_cert_tags.contains(cert.tag()); +} + std::vector Flatfile_Certificate_Store::find_all_certs(const X509_DN& subject_dn, const std::vector& key_id) const { - std::vector found_certs; - try { - const auto certs = m_dn_to_cert.at(subject_dn); + if(!m_dn_to_cert.contains(subject_dn)) { + return {}; + } + + const auto& certs = m_dn_to_cert.at(subject_dn); - for(const auto& cert : certs) { - if(key_id.empty() || key_id == cert.subject_key_id()) { - found_certs.push_back(cert); + std::vector found_certs; + for(const auto& cert : certs) { + if(!key_id.empty()) { + const std::vector& skid = cert.subject_key_id(); + if(!skid.empty() && skid != key_id) { + continue; } } - } catch(const std::out_of_range&) { - return {}; + found_certs.push_back(cert); } return found_certs; @@ -118,8 +136,26 @@ return std::nullopt; } +std::optional Flatfile_Certificate_Store::find_cert_by_issuer_dn_and_serial_number( + const X509_DN& issuer_dn, std::span serial_number) const { + if(!m_issuer_dn_to_cert.contains(issuer_dn)) { + return std::nullopt; + } + + const auto& certs = m_issuer_dn_to_cert.at(issuer_dn); + + for(const auto& cert : certs) { + if(std::ranges::equal(cert.serial_number(), serial_number)) { + return cert; + } + } + + return std::nullopt; +} + std::optional Flatfile_Certificate_Store::find_crl_for(const X509_Certificate& subject) const { BOTAN_UNUSED(subject); return {}; } + } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/x509/certstor_flatfile/certstor_flatfile.h botan3-3.12.0+dfsg/src/lib/x509/certstor_flatfile/certstor_flatfile.h --- botan3-3.7.1+dfsg/src/lib/x509/certstor_flatfile/certstor_flatfile.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/certstor_flatfile/certstor_flatfile.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,9 +10,10 @@ #define BOTAN_CERT_STORE_FLATFILE_H_ #include +#include #include -#include +#include #include namespace Botan { @@ -29,12 +30,13 @@ * @param ignore_non_ca if true, certs that are not self-signed CA certs will * be ignored. Otherwise (if false), an exception will be thrown instead. */ - Flatfile_Certificate_Store(std::string_view file, bool ignore_non_ca = false); + BOTAN_FUTURE_EXPLICIT Flatfile_Certificate_Store(std::string_view file, bool ignore_non_ca = false); Flatfile_Certificate_Store(const Flatfile_Certificate_Store&) = default; Flatfile_Certificate_Store(Flatfile_Certificate_Store&&) = default; Flatfile_Certificate_Store& operator=(const Flatfile_Certificate_Store&) = default; Flatfile_Certificate_Store& operator=(Flatfile_Certificate_Store&&) = default; + ~Flatfile_Certificate_Store() override = default; /** * @return DNs for all certificates managed by the store @@ -58,17 +60,24 @@ std::optional find_cert_by_raw_subject_dn_sha256( const std::vector& subject_hash) const override; + std::optional find_cert_by_issuer_dn_and_serial_number( + const X509_DN& issuer_dn, std::span serial_number) const override; + /** * Fetching CRLs is not supported by this certificate store. This will * always return an empty list. */ std::optional find_crl_for(const X509_Certificate& subject) const override; + bool contains(const X509_Certificate& cert) const override; + private: std::vector m_all_subjects; + std::set m_cert_tags; std::map> m_dn_to_cert; std::map, std::optional> m_pubkey_sha1_to_cert; std::map, std::optional> m_subject_dn_sha256_to_cert; + std::map> m_issuer_dn_to_cert; }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/x509/certstor_sql/certstor_sql.cpp botan3-3.12.0+dfsg/src/lib/x509/certstor_sql/certstor_sql.cpp --- botan3-3.7.1+dfsg/src/lib/x509/certstor_sql/certstor_sql.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/certstor_sql/certstor_sql.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,8 @@ #include +#include +#include #include #include #include @@ -56,7 +58,7 @@ } else { stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix + "certificates WHERE\ - subject_dn == ?1 AND (key_id == NULL OR key_id == ?2) LIMIT 1"); + subject_dn == ?1 AND (key_id IS NULL OR key_id == ?2) LIMIT 1"); stmt->bind(1, dn_encoding); stmt->bind(2, key_id); } @@ -83,12 +85,11 @@ } else { stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix + "certificates WHERE\ - subject_dn == ?1 AND (key_id == NULL OR key_id == ?2)"); + subject_dn == ?1 AND (key_id IS NULL OR key_id == ?2)"); stmt->bind(1, dn_encoding); stmt->bind(2, key_id); } - std::optional cert; while(stmt->step()) { auto blob = stmt->get_blob(0); certs.push_back(X509_Certificate(blob.first, blob.second)); @@ -107,10 +108,15 @@ throw Not_Implemented("Certificate_Store_In_SQL::find_cert_by_raw_subject_dn_sha256"); } +std::optional Certificate_Store_In_SQL::find_cert_by_issuer_dn_and_serial_number( + const X509_DN& /*issuer_dn*/, std::span /*serial_number*/) const { + throw Not_Implemented("Certificate_Store_In_SQL::find_cert_by_issuer_dn_and_serial_number"); +} + std::optional Certificate_Store_In_SQL::find_crl_for(const X509_Certificate& subject) const { - auto all_crls = generate_crls(); + const auto all_crls = generate_crls(); - for(auto crl : all_crls) { + for(const auto& crl : all_crls) { if(!crl.get_revoked().empty() && crl.issuer_dn() == subject.issuer_dn()) { return crl; } @@ -125,7 +131,7 @@ while(stmt->step()) { auto blob = stmt->get_blob(0); - BER_Decoder dec(blob.first, blob.second); + BER_Decoder dec(std::span{blob.first, blob.second}, BER_Decoder::Limits::DER()); X509_DN dn; dn.decode_from(dec); @@ -159,6 +165,12 @@ return true; } +bool Certificate_Store_In_SQL::contains(const X509_Certificate& cert) const { + auto stmt = m_database->new_statement("SELECT 1 FROM " + m_prefix + "certificates WHERE fingerprint == ?1"); + stmt->bind(1, cert.fingerprint("SHA-256")); + return stmt->step(); +} + bool Certificate_Store_In_SQL::remove_cert(const X509_Certificate& cert) { if(!find_cert(cert.subject_dn(), cert.subject_key_id())) { return false; @@ -194,11 +206,11 @@ } std::vector Certificate_Store_In_SQL::find_certs_for_key(const Private_Key& key) const { - auto fpr = key.fingerprint_private("SHA-256"); + auto fprint = key.fingerprint_private("SHA-256"); auto stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix + "certificates WHERE priv_fingerprint == ?1"); - stmt->bind(1, fpr); + stmt->bind(1, fprint); std::vector certs; while(stmt->step()) { @@ -217,19 +229,19 @@ } auto pkcs8 = PKCS8::BER_encode(key, m_rng, m_password); - auto fpr = key.fingerprint_private("SHA-256"); + auto fprint = key.fingerprint_private("SHA-256"); auto stmt1 = m_database->new_statement("INSERT OR REPLACE INTO " + m_prefix + "keys ( fingerprint, key ) VALUES ( ?1, ?2 )"); - stmt1->bind(1, fpr); + stmt1->bind(1, fprint); stmt1->bind(2, pkcs8.data(), pkcs8.size()); stmt1->spin(); auto stmt2 = m_database->new_statement("UPDATE " + m_prefix + "certificates SET priv_fingerprint = ?1 WHERE fingerprint == ?2"); - stmt2->bind(1, fpr); + stmt2->bind(1, fprint); stmt2->bind(2, cert.fingerprint("SHA-256")); stmt2->spin(); @@ -237,15 +249,16 @@ } void Certificate_Store_In_SQL::remove_key(const Private_Key& key) { - auto fpr = key.fingerprint_private("SHA-256"); + auto fprint = key.fingerprint_private("SHA-256"); auto stmt = m_database->new_statement("DELETE FROM " + m_prefix + "keys WHERE fingerprint == ?1"); - stmt->bind(1, fpr); + stmt->bind(1, fprint); stmt->spin(); } // Revocation void Certificate_Store_In_SQL::revoke_cert(const X509_Certificate& cert, CRL_Code code, const X509_Time& time) { + // TODO(Botan4) require that time be valid insert_cert(cert); auto stmt1 = m_database->new_statement("INSERT OR REPLACE INTO " + m_prefix + @@ -263,6 +276,20 @@ stmt1->spin(); } +// Revocation +void Certificate_Store_In_SQL::revoke_cert(const X509_Certificate& cert, CRL_Code code) { + insert_cert(cert); + + auto stmt1 = m_database->new_statement("INSERT OR REPLACE INTO " + m_prefix + + "revoked ( fingerprint, reason, time ) VALUES ( ?1, ?2, ?3 )"); + + stmt1->bind(1, cert.fingerprint("SHA-256")); + stmt1->bind(2, static_cast(code)); + stmt1->bind(3, static_cast(-1)); + + stmt1->spin(); +} + void Certificate_Store_In_SQL::affirm_cert(const X509_Certificate& cert) { auto stmt = m_database->new_statement("DELETE FROM " + m_prefix + "revoked WHERE fingerprint == ?1"); @@ -292,7 +319,7 @@ } } - X509_Time t(std::chrono::system_clock::now()); + const X509_Time t(std::chrono::system_clock::now()); std::vector ret; ret.reserve(crls.size()); diff -Nru botan3-3.7.1+dfsg/src/lib/x509/certstor_sql/certstor_sql.h botan3-3.12.0+dfsg/src/lib/x509/certstor_sql/certstor_sql.h --- botan3-3.7.1+dfsg/src/lib/x509/certstor_sql/certstor_sql.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/certstor_sql/certstor_sql.h 2026-05-07 01:38:28.000000000 +0000 @@ -53,6 +53,9 @@ std::optional find_cert_by_raw_subject_dn_sha256( const std::vector& subject_hash) const override; + std::optional find_cert_by_issuer_dn_and_serial_number( + const X509_DN& issuer_dn, std::span serial_number) const override; + /** * Returns all subject DNs known to the store instance. */ @@ -71,7 +74,7 @@ bool remove_cert(const X509_Certificate& cert); /// Returns the private key for "cert" or an empty shared_ptr if none was found. - std::shared_ptr find_key(const X509_Certificate&) const; + std::shared_ptr find_key(const X509_Certificate& cert) const; /// Returns all certificates for private key "key". std::vector find_certs_for_key(const Private_Key& key) const; @@ -86,10 +89,13 @@ void remove_key(const Private_Key& key); /// Marks "cert" as revoked starting from "time". - void revoke_cert(const X509_Certificate&, CRL_Code, const X509_Time& time = X509_Time()); + void revoke_cert(const X509_Certificate& cert, CRL_Code reason, const X509_Time& time); + + /// Marks "cert" as revoked with no time specified + void revoke_cert(const X509_Certificate& cert, CRL_Code reason); - /// Reverses the revokation for "cert". - void affirm_cert(const X509_Certificate&); + /// Reverses the revocation for "cert". + void affirm_cert(const X509_Certificate& cert); /** * Generates Certificate Revocation Lists for all certificates marked as revoked. @@ -102,6 +108,8 @@ */ std::optional find_crl_for(const X509_Certificate& issuer) const override; + bool contains(const X509_Certificate& cert) const override; + private: RandomNumberGenerator& m_rng; std::shared_ptr m_database; diff -Nru botan3-3.7.1+dfsg/src/lib/x509/certstor_system/certstor_system.cpp botan3-3.12.0+dfsg/src/lib/x509/certstor_system/certstor_system.cpp --- botan3-3.7.1+dfsg/src/lib/x509/certstor_system/certstor_system.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/certstor_system/certstor_system.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include #include +#include #if defined(BOTAN_HAS_CERTSTOR_MACOS) #include @@ -51,10 +52,19 @@ return m_system_store->find_cert_by_raw_subject_dn_sha256(subject_hash); } +std::optional System_Certificate_Store::find_cert_by_issuer_dn_and_serial_number( + const X509_DN& issuer_dn, std::span serial_number) const { + return m_system_store->find_cert_by_issuer_dn_and_serial_number(issuer_dn, serial_number); +} + std::optional System_Certificate_Store::find_crl_for(const X509_Certificate& subject) const { return m_system_store->find_crl_for(subject); } +bool System_Certificate_Store::contains(const X509_Certificate& cert) const { + return m_system_store->contains(cert); +} + std::vector System_Certificate_Store::all_subjects() const { return m_system_store->all_subjects(); } diff -Nru botan3-3.7.1+dfsg/src/lib/x509/certstor_system/certstor_system.h botan3-3.12.0+dfsg/src/lib/x509/certstor_system/certstor_system.h --- botan3-3.7.1+dfsg/src/lib/x509/certstor_system/certstor_system.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/certstor_system/certstor_system.h 2026-05-07 01:38:28.000000000 +0000 @@ -26,8 +26,13 @@ std::optional find_cert_by_raw_subject_dn_sha256( const std::vector& subject_hash) const override; + std::optional find_cert_by_issuer_dn_and_serial_number( + const X509_DN& issuer_dn, std::span serial_number) const override; + std::optional find_crl_for(const X509_Certificate& subject) const override; + bool contains(const X509_Certificate& cert) const override; + std::vector all_subjects() const override; private: diff -Nru botan3-3.7.1+dfsg/src/lib/x509/certstor_system_macos/certstor_macos.cpp botan3-3.12.0+dfsg/src/lib/x509/certstor_system_macos/certstor_macos.cpp --- botan3-3.7.1+dfsg/src/lib/x509/certstor_system_macos/certstor_macos.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/certstor_system_macos/certstor_macos.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include +#include #include #include #include @@ -64,37 +65,34 @@ * See: opensource.apple.com/source/Security/Security-55471/sec/Security/SecCertificate.c.auto.html */ X509_DN normalize(const X509_DN& dn) { - X509_DN result; - - for(const auto& rdn : dn.dn_info()) { - // TODO: C++14 - use std::get(), resp. std::get() - const auto oid = rdn.first; - auto str = rdn.second; + X509_DN result{}; + for(const auto& [oid, str] : dn.dn_info()) { if(str.tagging() == ASN1_Type::PrintableString) { std::string normalized; normalized.reserve(str.value().size()); + for(const char c : str.value()) { if(c != ' ') { - // store all 'normal' characters as upper case - normalized.push_back(::toupper(c)); + // PrintableString is ASCII-only; locale-independent fold to upper case + const char up = (c >= 'a' && c <= 'z') ? static_cast(c - ('a' - 'A')) : c; + normalized.push_back(up); } else if(!normalized.empty() && normalized.back() != ' ') { // remove leading and squash multiple white spaces normalized.push_back(c); } } - if(normalized.back() == ' ') { + if(!normalized.empty() && normalized.back() == ' ') { // remove potential remaining single trailing white space char - normalized.erase(normalized.end() - 1); + normalized.pop_back(); } - str = ASN1_String(normalized, str.tagging()); + result.add_attribute(oid, ASN1_String(normalized, str.tagging())); + } else { + result.add_attribute(oid, str); } - - result.add_attribute(oid, str); } - return result; } @@ -155,8 +153,8 @@ public: /** * Wraps a list of search query parameters that are later passed into - * Apple's certifificate store API. The class provides some convenience - * functionality and handles the query paramenter's data lifetime. + * Apple's certificate store API. The class provides some convenience + * functionality and handles the query parameter's data lifetime. */ class Query { public: @@ -217,7 +215,7 @@ using Values = std::vector; Data m_data_store; //! makes sure that data parameters are kept alive - DataRefs m_data_refs; //! keeps track of CFDataRef objects refering into \p m_data_store + DataRefs m_data_refs; //! keeps track of CFDataRef objects referring into \p m_data_store Keys m_keys; //! ordered list of search parameter keys Values m_values; //! ordered list of search parameter values }; @@ -395,6 +393,30 @@ throw Not_Implemented("Certificate_Store_MacOS::find_cert_by_raw_subject_dn_sha256"); } +std::optional Certificate_Store_MacOS::find_cert_by_issuer_dn_and_serial_number( + const X509_DN& issuer_dn, std::span serial_number) const { + Certificate_Store_MacOS_Impl::Query query; + /* + Directly using kSecAttrSerialNumber can't find the certificate + Maybe macOS has a special encoding for the serial number + + query.addParameter(kSecAttrSerialNumber, serial_number); + */ + query.addParameter(kSecAttrIssuer, normalizeAndSerialize(issuer_dn)); + + /* + This is a temporary solution + Use only the issuer DN to find all certificates and filters the serial number, but may affect performance + */ + for(const auto& cert : m_impl->findAll(std::move(query))) { + if(std::ranges::equal(cert.serial_number(), serial_number)) { + return cert; + } + } + + return std::nullopt; +} + std::optional Certificate_Store_MacOS::find_crl_for(const X509_Certificate& subject) const { BOTAN_UNUSED(subject); return {}; diff -Nru botan3-3.7.1+dfsg/src/lib/x509/certstor_system_macos/certstor_macos.h botan3-3.12.0+dfsg/src/lib/x509/certstor_system_macos/certstor_macos.h --- botan3-3.7.1+dfsg/src/lib/x509/certstor_system_macos/certstor_macos.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/certstor_system_macos/certstor_macos.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,9 +9,11 @@ #ifndef BOTAN_CERT_STORE_SYSTEM_MACOS_H_ #define BOTAN_CERT_STORE_SYSTEM_MACOS_H_ +#include #include -#include +// Use Certificate_Store_System instead +BOTAN_FUTURE_INTERNAL_HEADER(certstor_macos.h) namespace Botan { @@ -58,11 +60,15 @@ std::optional find_cert_by_pubkey_sha1(const std::vector& key_hash) const override; /** - * @throws Not_Implemented + * @throws Not_Implemented as this functionality is not available in the + * macOS certificate interface */ std::optional find_cert_by_raw_subject_dn_sha256( const std::vector& subject_hash) const override; + std::optional find_cert_by_issuer_dn_and_serial_number( + const X509_DN& issuer_dn, std::span serial_number) const override; + /** * Fetching CRLs is not supported by the keychain on macOS. This will * always return an empty list. @@ -75,4 +81,4 @@ } // namespace Botan -#endif \ No newline at end of file +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/x509/certstor_system_windows/certstor_windows.cpp botan3-3.12.0+dfsg/src/lib/x509/certstor_system_windows/certstor_windows.cpp --- botan3-3.7.1+dfsg/src/lib/x509/certstor_system_windows/certstor_windows.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/certstor_system_windows/certstor_windows.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ /* * Certificate Store -* (C) 1999-2021 Jack Lloyd +* (C) 1999-2021,2026 Jack Lloyd * (C) 2018-2019 Patrik Fiedler, Tim Oesterreich * (C) 2021 René Meusel * @@ -9,11 +9,18 @@ #include +#include #include #include +#include #include +#include +#include +#include #include #include +#include +#include #include #define NOMINMAX 1 @@ -22,193 +29,369 @@ #include -#define WINCRYPT_UNUSED_PARAM \ - 0 // for avoiding warnings when passing NULL to unused params in win32 api that accept integer types - namespace Botan { namespace { -const std::array cert_store_names{"Root", "CA"}; +constexpr std::array cert_store_names{"Root", "CA"}; /** - * Abstract RAII wrapper for PCCERT_CONTEXT and HCERTSTORE - * The Windows API partly takes care of those pointers destructions itself. - * Especially, iteratively calling `CertFindCertificateInStore` with the previous PCCERT_CONTEXT - * will free the context and return a new one. In this case, this guard takes care of freeing the context - * in case of an exception and at the end of the iterative process. + * RAII wrapper for PCCERT_CONTEXT used as iteration state in + * CertFindCertificateInStore / CertEnumCertificatesInStore loops. + * + * The Windows API takes ownership of the previous context when the next one + * is requested: passing a non-null PCCERT_CONTEXT as the iteration state + * causes the API to free it and return a new one. This wrapper takes care of + * freeing the trailing context after the loop ends or on early return. */ -template -class Handle_Guard { +class Cert_Context final { public: - Handle_Guard(T context) : m_context(context) {} + Cert_Context() : m_ctx(nullptr) {} + + ~Cert_Context() { + if(m_ctx != nullptr) { + CertFreeCertificateContext(m_ctx); + } + } + + Cert_Context(const Cert_Context&) = delete; + Cert_Context(Cert_Context&&) = delete; + Cert_Context& operator=(const Cert_Context&) = delete; + Cert_Context& operator=(Cert_Context&&) = delete; + + bool assign(PCCERT_CONTEXT ctx) { + m_ctx = ctx; + return m_ctx != nullptr; + } - Handle_Guard(const Handle_Guard& rhs) = delete; + PCCERT_CONTEXT get() const { return m_ctx; } - Handle_Guard(Handle_Guard&& rhs) : m_context(std::move(rhs.m_context)) { rhs.m_context = nullptr; } + PCCERT_CONTEXT operator->() const { return m_ctx; } - ~Handle_Guard() { close(); } + private: + PCCERT_CONTEXT m_ctx; +}; + +/** + * Iterate every certificate returned by a Windows API walk function across + * a sequence of already-open stores. The walk function is called with the + * current store and the previously returned context; returning nullptr ends + * the current store and advances to the next. The final non-null context is + * owned by this object and freed on destruction or on the terminating walk + * call that returns nullptr. + */ +class Cert_Enumerator final { + public: + using Next_Fn = std::function; - operator bool() const { return m_context != nullptr; } + Cert_Enumerator(std::span stores, Next_Fn fn) : m_stores(stores), m_get_next(std::move(fn)) {} + + Cert_Enumerator(const Cert_Enumerator&) = delete; + Cert_Enumerator(Cert_Enumerator&&) = delete; + Cert_Enumerator& operator=(const Cert_Enumerator&) = delete; + Cert_Enumerator& operator=(Cert_Enumerator&&) = delete; + ~Cert_Enumerator() = default; + + PCCERT_CONTEXT next() { + while(m_store_idx < m_stores.size()) { + if(m_ctx.assign(m_get_next(m_stores[m_store_idx], m_ctx.get()))) { + return m_ctx.get(); + } + // The Windows API freed the previous context when it returned + // nullptr, so m_ctx now holds null and we can start the next + // store with a null prev. + ++m_store_idx; + } + return nullptr; + } + + private: + std::span m_stores; + Next_Fn m_get_next; + size_t m_store_idx = 0; + Cert_Context m_ctx; +}; + +/** + * A 20-byte SHA-1 hash of a certificate's SubjectPublicKey, suitable for use + * as a key in an unordered associative container. + */ +class Pubkey_SHA1 final { + public: + static constexpr size_t LEN = 20; - bool assign(T context) { - m_context = context; - return m_context != nullptr; + explicit Pubkey_SHA1(std::span bytes) { + BOTAN_ARG_CHECK(bytes.size() == LEN, "invalid SHA-1 pubkey hash length"); + std::copy(bytes.begin(), bytes.end(), m_hash.begin()); } - T& get() { return m_context; } + static Pubkey_SHA1 compute(HashFunction& sha1, std::span data) { + Pubkey_SHA1 result; + sha1.update(data); + sha1.final(result.m_hash); + return result; + } - const T& get() const { return m_context; } + auto operator<=>(const Pubkey_SHA1&) const = default; - T operator->() { return m_context; } + size_t hash() const noexcept { + size_t h = 0; + std::memcpy(&h, m_hash.data(), sizeof(h)); + return h; + } private: - template - typename std::enable_if::value>::type close() { - if(m_context) { - CertFreeCertificateContext(m_context); + Pubkey_SHA1() = default; + std::array m_hash = {}; +}; + +struct Pubkey_SHA1_Hasher { + size_t operator()(const Pubkey_SHA1& h) const noexcept { return h.hash(); } +}; + +} // namespace + +/** + * Pimpl for Certificate_Store_Windows. + */ +class Certificate_Store_Windows_Impl final { + public: + // This cache size is arbitrary but probably is sufficient so that the vast + // majority of accesses hit the cache + static constexpr size_t SystemStore_CertCacheSize = 128; + + Certificate_Store_Windows_Impl() : m_cert_cache(SystemStore_CertCacheSize) { + for(const auto* cert_store_name : cert_store_names) { + auto* store = CertOpenSystemStoreA(0, cert_store_name); + if(store == nullptr) { + const auto err = ::GetLastError(); + close_stores(); + throw System_Error(fmt("Failed to open Windows certificate store '{}'", cert_store_name), err); + } + + CertControlStore(store, 0, CERT_STORE_CTRL_AUTO_RESYNC, nullptr); + + m_stores.push_back(store); } } - template - typename std::enable_if::value>::type close() { - if(m_context) { - // second parameter is a flag that tells the store how to deallocate memory - // using the default "0", this function works like decreasing the reference counter - // in a shared_ptr - CertCloseStore(m_context, 0); + ~Certificate_Store_Windows_Impl() { close_stores(); } + + Certificate_Store_Windows_Impl(const Certificate_Store_Windows_Impl&) = delete; + Certificate_Store_Windows_Impl(Certificate_Store_Windows_Impl&&) = delete; + Certificate_Store_Windows_Impl& operator=(const Certificate_Store_Windows_Impl&) = delete; + Certificate_Store_Windows_Impl& operator=(Certificate_Store_Windows_Impl&&) = delete; + + std::optional find_cert(const X509_DN& subject_dn, const std::vector& key_id) { + const lock_guard_type lock(m_mutex); + + const auto certs = find_cert_by_dn_and_key_id(subject_dn, key_id, true); + if(certs.empty()) { + return std::nullopt; } + return certs.front(); } - T m_context; -}; + std::vector find_all_certs(const X509_DN& subject_dn, const std::vector& key_id) { + const lock_guard_type lock(m_mutex); -HCERTSTORE open_cert_store(const char* cert_store_name) { - auto store = CertOpenSystemStoreA(WINCRYPT_UNUSED_PARAM, cert_store_name); - if(!store) { - throw Internal_Error("failed to open windows certificate store '" + std::string(cert_store_name) + - "' (Error Code: " + std::to_string(::GetLastError()) + ")"); - } - return store; -} + return find_cert_by_dn_and_key_id(subject_dn, key_id, false); + } -std::vector search_cert_stores( - const _CRYPTOAPI_BLOB& blob, - const DWORD& find_type, - std::function& certs, const X509_Certificate& cert)> filter, - bool return_on_first_found) { - std::vector certs; - for(const auto store_name : cert_store_names) { - Handle_Guard windows_cert_store = open_cert_store(store_name); - Handle_Guard cert_context = nullptr; - while(cert_context.assign(CertFindCertificateInStore(windows_cert_store.get(), - PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, - WINCRYPT_UNUSED_PARAM, - find_type, - &blob, - cert_context.get()))) { - X509_Certificate cert(cert_context->pbCertEncoded, cert_context->cbCertEncoded); - if(filter(certs, cert)) { - if(return_on_first_found) { - return {cert}; + std::optional find_cert_by_pubkey_sha1(const std::vector& key_hash) { + if(key_hash.size() != Pubkey_SHA1::LEN) { + throw Invalid_Argument("Certificate_Store_Windows::find_cert_by_pubkey_sha1 invalid hash"); + } + + const Pubkey_SHA1 target(key_hash); + + const lock_guard_type lock(m_mutex); + + if(const auto hit = m_sha1_pubkey_to_cert.find(target); hit != m_sha1_pubkey_to_cert.end()) { + return hit->second; + } + + // Upper bound the cache, random eviction based on the hashes + while(m_sha1_pubkey_to_cert.size() >= 1024) { + m_sha1_pubkey_to_cert.erase(m_sha1_pubkey_to_cert.begin()); + } + + auto sha1 = HashFunction::create_or_throw("SHA-1"); + + Cert_Enumerator enumerator( + m_stores, [](HCERTSTORE store, PCCERT_CONTEXT prev) { return CertEnumCertificatesInStore(store, prev); }); + + while(const auto* ctx = enumerator.next()) { + const auto pubkey_blob = ctx->pCertInfo->SubjectPublicKeyInfo.PublicKey; + const auto candidate = + Pubkey_SHA1::compute(*sha1, {static_cast(pubkey_blob.pbData), pubkey_blob.cbData}); + + if(candidate == target) { + auto result = materialize(ctx->pbCertEncoded, ctx->cbCertEncoded); + m_sha1_pubkey_to_cert.emplace(target, result); + return result; } - certs.push_back(cert); } + + // insert a negative query result into the cache + m_sha1_pubkey_to_cert.emplace(target, std::nullopt); + return std::nullopt; } - } - return certs; -} + std::optional find_cert_by_issuer_dn_and_serial_number(const X509_DN& issuer_dn, + std::span serial_number) { + const lock_guard_type lock(m_mutex); + + const std::vector dn_data = issuer_dn.BER_encode(); + + const _CRYPTOAPI_BLOB blob{ + .cbData = static_cast(dn_data.size()), + .pbData = const_cast(dn_data.data()), + }; + + auto filter = [&](const X509_Certificate& cert) { + return std::ranges::equal(cert.serial_number(), serial_number); + }; + + const auto certs = search_cert_stores(blob, CERT_FIND_ISSUER_NAME, filter, true); + if(certs.empty()) { + return std::nullopt; + } + return certs.front(); + } -bool already_contains_certificate(const std::vector& certs, X509_Certificate cert) { - return std::any_of(certs.begin(), certs.end(), [&](const X509_Certificate& c) { return c == cert; }); -} + bool contains(const X509_Certificate& cert) { + const auto cert_sha1 = cert.certificate_data_sha1(); + const auto cert_sha256 = cert.certificate_data_sha256(); + + const CRYPT_HASH_BLOB sha1_blob{ + .cbData = static_cast(cert_sha1.size()), + .pbData = const_cast(cert_sha1.data()), + }; + + const lock_guard_type lock(m_mutex); + + Cert_Enumerator enumerator(m_stores, [&sha1_blob](HCERTSTORE store, PCCERT_CONTEXT prev) { + return CertFindCertificateInStore( + store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SHA1_HASH, &sha1_blob, prev); + }); + + while(const auto* ctx = enumerator.next()) { + const auto found = materialize(ctx->pbCertEncoded, ctx->cbCertEncoded); + if(std::ranges::equal(found.certificate_data_sha256(), cert_sha256)) { + return true; + } + } -std::vector find_cert_by_dn_and_key_id(const X509_DN& subject_dn, - const std::vector& key_id, - bool return_on_first_found) { - _CRYPTOAPI_BLOB blob; - DWORD find_type; - std::vector dn_data; // has to live until search completes - - // if key_id is available, prefer searching that, as it should be "more unique" than the subject DN - if(key_id.empty()) { - find_type = CERT_FIND_SUBJECT_NAME; - dn_data = subject_dn.DER_encode(); - blob.cbData = static_cast(dn_data.size()); - blob.pbData = reinterpret_cast(dn_data.data()); - } else { - find_type = CERT_FIND_KEY_IDENTIFIER; - blob.cbData = static_cast(key_id.size()); - blob.pbData = const_cast(key_id.data()); - } - - auto filter = [&](const std::vector& certs, const X509_Certificate& cert) { - return !already_contains_certificate(certs, cert) && (key_id.empty() || cert.subject_dn() == subject_dn); - }; + return false; + } - return search_cert_stores(blob, find_type, filter, return_on_first_found); -} + std::vector all_subjects() { + const lock_guard_type lock(m_mutex); -} // namespace + std::vector subject_dns; -Certificate_Store_Windows::Certificate_Store_Windows() {} + Cert_Enumerator enumerator( + m_stores, [](HCERTSTORE store, PCCERT_CONTEXT prev) { return CertEnumCertificatesInStore(store, prev); }); -std::vector Certificate_Store_Windows::all_subjects() const { - std::vector subject_dns; - for(const auto store_name : cert_store_names) { - Handle_Guard windows_cert_store = open_cert_store(store_name); - Handle_Guard cert_context = nullptr; - - // Handle_Guard::assign exchanges the underlying pointer. No RAII is needed here, because the Windows API takes care of - // freeing the previous context. - while(cert_context.assign(CertEnumCertificatesInStore(windows_cert_store.get(), cert_context.get()))) { - BER_Decoder dec(cert_context->pCertInfo->Subject.pbData, cert_context->pCertInfo->Subject.cbData); - - X509_DN dn; - dn.decode_from(dec); - subject_dns.emplace_back(std::move(dn)); + while(const auto* ctx = enumerator.next()) { + BER_Decoder dec(ctx->pCertInfo->Subject.pbData, ctx->pCertInfo->Subject.cbData); + X509_DN dn; + dn.decode_from(dec); + subject_dns.emplace_back(std::move(dn)); + } + + return subject_dns; } - } - return subject_dns; + private: + void close_stores() { + for(auto* store : m_stores) { + CertCloseStore(store, 0); + } + m_stores.clear(); + } + + X509_Certificate materialize(const BYTE* der, DWORD len) { return m_cert_cache.find_or_insert({der, len}); } + + // Caller must hold m_mutex. + std::vector find_cert_by_dn_and_key_id(const X509_DN& subject_dn, + const std::vector& key_id, + bool return_on_first_found) { + _CRYPTOAPI_BLOB blob{}; + DWORD find_type = 0; + std::vector dn_data; // has to live until search completes + + // if key_id is available, prefer searching that, as it should be "more unique" than the subject DN + if(key_id.empty()) { + find_type = CERT_FIND_SUBJECT_NAME; + dn_data = subject_dn.DER_encode(); + blob.cbData = static_cast(dn_data.size()); + blob.pbData = reinterpret_cast(dn_data.data()); + } else { + find_type = CERT_FIND_KEY_IDENTIFIER; + blob.cbData = static_cast(key_id.size()); + blob.pbData = const_cast(key_id.data()); + } + + auto filter = [&](const X509_Certificate& cert) { return key_id.empty() || cert.subject_dn() == subject_dn; }; + + return search_cert_stores(blob, find_type, filter, return_on_first_found); + } + + // Caller must hold m_mutex. + std::vector search_cert_stores(const _CRYPTOAPI_BLOB& blob, + DWORD find_type, + const std::function& filter, + bool return_on_first_found) { + std::vector certs; + std::unordered_set seen; + + Cert_Enumerator enumerator(m_stores, [&blob, find_type](HCERTSTORE store, PCCERT_CONTEXT prev) { + return CertFindCertificateInStore( + store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, find_type, &blob, prev); + }); + + while(const auto* ctx = enumerator.next()) { + auto cert = materialize(ctx->pbCertEncoded, ctx->cbCertEncoded); + if(!seen.insert(cert.tag()).second) { + continue; + } + if(filter(cert)) { + certs.push_back(std::move(cert)); + if(return_on_first_found) { + break; + } + } + } + + return certs; + } + + mutex_type m_mutex; + std::vector m_stores; + std::unordered_map, Pubkey_SHA1_Hasher> m_sha1_pubkey_to_cert; + X509_Certificate_Cache m_cert_cache; +}; + +Certificate_Store_Windows::Certificate_Store_Windows() : m_impl(std::make_shared()) {} + +std::vector Certificate_Store_Windows::all_subjects() const { + return m_impl->all_subjects(); } std::optional Certificate_Store_Windows::find_cert(const X509_DN& subject_dn, const std::vector& key_id) const { - const auto certs = find_cert_by_dn_and_key_id(subject_dn, key_id, true); - if(certs.empty()) - return std::nullopt; - else - return certs.front(); + return m_impl->find_cert(subject_dn, key_id); } std::vector Certificate_Store_Windows::find_all_certs(const X509_DN& subject_dn, const std::vector& key_id) const { - return find_cert_by_dn_and_key_id(subject_dn, key_id, false); + return m_impl->find_all_certs(subject_dn, key_id); } std::optional Certificate_Store_Windows::find_cert_by_pubkey_sha1( const std::vector& key_hash) const { - if(key_hash.size() != 20) { - throw Invalid_Argument("Certificate_Store_Windows::find_cert_by_pubkey_sha1 invalid hash"); - } - - CRYPT_HASH_BLOB blob; - blob.cbData = static_cast(key_hash.size()); - blob.pbData = const_cast(key_hash.data()); - - auto filter = [&](const std::vector&, const X509_Certificate&) { return true; }; - - // This assumes that the to-be-found certificate adheres to RFC 3280 (Section 4.2.1.2), because - // the windows API does not allow to search for the SHA-1 of the certificate's public key directly. - // Most certificates adhere to the above mentioned convention... - const auto certs = search_cert_stores(blob, CERT_FIND_KEY_IDENTIFIER, filter, true); - if(!certs.empty()) - return certs.front(); - - // ... for certificates that don't adhere to RFC 3280 (Section 4.2.1.2), we will need to use a - // fallback implementation that does an exhaustive search of all available certificates. - return find_cert_by_pubkey_sha1_via_exhaustive_search(key_hash); + return m_impl->find_cert_by_pubkey_sha1(key_hash); } std::optional Certificate_Store_Windows::find_cert_by_raw_subject_dn_sha256( @@ -217,40 +400,19 @@ throw Not_Implemented("Certificate_Store_Windows::find_cert_by_raw_subject_dn_sha256"); } +std::optional Certificate_Store_Windows::find_cert_by_issuer_dn_and_serial_number( + const X509_DN& issuer_dn, std::span serial_number) const { + return m_impl->find_cert_by_issuer_dn_and_serial_number(issuer_dn, serial_number); +} + std::optional Certificate_Store_Windows::find_crl_for(const X509_Certificate& subject) const { // TODO: this could be implemented by using the CertFindCRLInStore function BOTAN_UNUSED(subject); return std::nullopt; } -std::optional Certificate_Store_Windows::find_cert_by_pubkey_sha1_via_exhaustive_search( - const std::vector& key_hash) const { - if(const auto cache_hit = m_non_rfc3289_certs.find(key_hash); cache_hit != m_non_rfc3289_certs.end()) { - return cache_hit->second; - } - - auto sha1 = HashFunction::create_or_throw("SHA-1"); - for(const auto store_name : cert_store_names) { - Handle_Guard windows_cert_store = open_cert_store(store_name); - Handle_Guard cert_context = nullptr; - - // Handle_Guard::assign exchanges the underlying pointer. No RAII is needed here, because the Windows API takes care of - // freeing the previous context. - while(cert_context.assign(CertEnumCertificatesInStore(windows_cert_store.get(), cert_context.get()))) { - const auto pubkey_blob = cert_context->pCertInfo->SubjectPublicKeyInfo.PublicKey; - const auto key_hash_candidate = sha1->process(static_cast(pubkey_blob.pbData), pubkey_blob.cbData); - - if(std::equal(key_hash.begin(), key_hash.end(), key_hash_candidate.begin())) { - X509_Certificate result(cert_context->pbCertEncoded, cert_context->cbCertEncoded); - m_non_rfc3289_certs[key_hash] = result; - return result; - } - } - } - - // insert a negative query result into the cache - m_non_rfc3289_certs[key_hash] = std::nullopt; - return std::nullopt; +bool Certificate_Store_Windows::contains(const X509_Certificate& cert) const { + return m_impl->contains(cert); } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/x509/certstor_system_windows/certstor_windows.h botan3-3.12.0+dfsg/src/lib/x509/certstor_system_windows/certstor_windows.h --- botan3-3.7.1+dfsg/src/lib/x509/certstor_system_windows/certstor_windows.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/certstor_system_windows/certstor_windows.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,10 +11,15 @@ #define BOTAN_CERT_STORE_SYSTEM_WINDOWS_H_ #include +#include -#include +// Use Certificate_Store_System instead +BOTAN_FUTURE_INTERNAL_HEADER(certstor_windows.h) namespace Botan { + +class Certificate_Store_Windows_Impl; + /** * Certificate Store that is backed by the system trust store on Windows. */ @@ -59,28 +64,21 @@ std::optional find_cert_by_raw_subject_dn_sha256( const std::vector& subject_hash) const override; + std::optional find_cert_by_issuer_dn_and_serial_number( + const X509_DN& issuer_dn, std::span serial_number) const override; + /** * Not Yet Implemented * @return nullptr; */ std::optional find_crl_for(const X509_Certificate& subject) const override; - private: - /** - * Handle certificates that do not adhere to RFC 3280 using a subject key identifier - * that is not equal to the SHA-1 of the public key (w/o algorithm identifier) - * - * This method lazily builds a cache of certificates found in previous queries as well - * as negative results for @p key_hash queries that didn't find a certificate. - * - * See here for further details: https://github.com/randombit/botan/issues/2779 - */ - std::optional find_cert_by_pubkey_sha1_via_exhaustive_search( - const std::vector& key_hash) const; + bool contains(const X509_Certificate& cert) const override; private: - mutable std::map, std::optional> m_non_rfc3289_certs; + std::shared_ptr m_impl; }; + } // namespace Botan #endif diff -Nru botan3-3.7.1+dfsg/src/lib/x509/crl_ent.cpp botan3-3.12.0+dfsg/src/lib/x509/crl_ent.cpp --- botan3-3.7.1+dfsg/src/lib/x509/crl_ent.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/crl_ent.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,8 @@ #include +#include +#include #include #include #include @@ -15,29 +17,34 @@ namespace Botan { -struct CRL_Entry_Data { +class CRL_Entry_Data final { + public: + CRL_Entry_Data(const X509_Certificate& cert, CRL_Code why) : + m_serial(cert.serial_number()), m_time(X509_Time(std::chrono::system_clock::now())), m_reason(why) { + if(why != CRL_Code::Unspecified) { + m_extensions.add(std::make_unique(why)); + } + } + + CRL_Entry_Data() = default; + + // NOLINTBEGIN(*non-private-member-variables-in-classes) std::vector m_serial; X509_Time m_time; CRL_Code m_reason = CRL_Code::Unspecified; Extensions m_extensions; + // NOLINTEND(*non-private-member-variables-in-classes) }; /* * Create a CRL_Entry */ CRL_Entry::CRL_Entry(const X509_Certificate& cert, CRL_Code why) { - m_data = std::make_shared(); - m_data->m_serial = cert.serial_number(); - m_data->m_time = X509_Time(std::chrono::system_clock::now()); - m_data->m_reason = why; - - if(why != CRL_Code::Unspecified) { - m_data->m_extensions.add(std::make_unique(why)); - } + m_data = std::make_shared(cert, why); } /* -* Compare two CRL_Entrys for equality +* Compare two CRL_Entry structs for equality */ bool operator==(const CRL_Entry& a1, const CRL_Entry& a2) { if(a1.serial_number() != a2.serial_number()) { @@ -53,7 +60,7 @@ } /* -* Compare two CRL_Entrys for inequality +* Compare two CRL_Entry structs for inequality */ bool operator!=(const CRL_Entry& a1, const CRL_Entry& a2) { return !(a1 == a2); @@ -76,18 +83,19 @@ * Decode a BER encoded CRL_Entry */ void CRL_Entry::decode_from(BER_Decoder& source) { - BigInt serial_number_bn; - auto data = std::make_unique(); BER_Decoder entry = source.start_sequence(); - entry.decode(serial_number_bn).decode(data->m_time); - data->m_serial = serial_number_bn.serialize(); + BigInt serial; + entry.decode(serial); + data->m_serial = serial.serialize(); + + entry.decode(data->m_time); if(entry.more_items()) { entry.decode(data->m_extensions); - if(auto ext = data->m_extensions.get_extension_object_as()) { + if(const auto* ext = data->m_extensions.get_extension_object_as()) { data->m_reason = ext->get_reason(); } else { data->m_reason = CRL_Code::Unspecified; diff -Nru botan3-3.7.1+dfsg/src/lib/x509/info.txt botan3-3.12.0+dfsg/src/lib/x509/info.txt --- botan3-3.7.1+dfsg/src/lib/x509/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -11,6 +11,7 @@ asn1 +ip_address pubkey sha1 sha2_32 @@ -31,6 +32,11 @@ x509self.h + +x509_utils.h +x509_cert_cache.h + + # Almost all of the X509 code works fine without a clock, but supporting this diff -Nru botan3-3.7.1+dfsg/src/lib/x509/key_constraint.cpp botan3-3.12.0+dfsg/src/lib/x509/key_constraint.cpp --- botan3-3.7.1+dfsg/src/lib/x509/key_constraint.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/key_constraint.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -20,39 +20,41 @@ std::vector str; - if(this->m_value & Key_Constraints::DigitalSignature) { + auto usage_set = [value = m_value](const Key_Constraints::Bits usage) { return ((value & usage) == usage); }; + + if(usage_set(Key_Constraints::DigitalSignature)) { str.push_back("digital_signature"); } - if(this->m_value & Key_Constraints::NonRepudiation) { + if(usage_set(Key_Constraints::NonRepudiation)) { str.push_back("non_repudiation"); } - if(this->m_value & Key_Constraints::KeyEncipherment) { + if(usage_set(Key_Constraints::KeyEncipherment)) { str.push_back("key_encipherment"); } - if(this->m_value & Key_Constraints::DataEncipherment) { + if(usage_set(Key_Constraints::DataEncipherment)) { str.push_back("data_encipherment"); } - if(this->m_value & Key_Constraints::KeyAgreement) { + if(usage_set(Key_Constraints::KeyAgreement)) { str.push_back("key_agreement"); } - if(this->m_value & Key_Constraints::KeyCertSign) { + if(usage_set(Key_Constraints::KeyCertSign)) { str.push_back("key_cert_sign"); } - if(this->m_value & Key_Constraints::CrlSign) { + if(usage_set(Key_Constraints::CrlSign)) { str.push_back("crl_sign"); } - if(this->m_value & Key_Constraints::EncipherOnly) { + if(usage_set(Key_Constraints::EncipherOnly)) { str.push_back("encipher_only"); } - if(this->m_value & Key_Constraints::DecipherOnly) { + if(usage_set(Key_Constraints::DecipherOnly)) { str.push_back("decipher_only"); } diff -Nru botan3-3.7.1+dfsg/src/lib/x509/name_constraint.cpp botan3-3.12.0+dfsg/src/lib/x509/name_constraint.cpp --- botan3-3.7.1+dfsg/src/lib/x509/name_constraint.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/name_constraint.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,7 +1,7 @@ /* * X.509 Name Constraint * (C) 2015 Kai Michaelis -* 2024 Jack Lloyd +* 2024,2026 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -10,17 +10,27 @@ #include #include +#include #include #include #include #include -#include -#include +#include +#include +#include namespace Botan { class DER_Encoder; +namespace { + +std::string canonicalize_dns_name(std::string_view name) { + return tolower_string(name); +} + +} // namespace + std::string GeneralName::type() const { switch(m_type) { case NameType::Unknown: @@ -35,6 +45,8 @@ return "DN"; case NameType::IPv4: return "IP"; + case NameType::IPv6: + return "IPv6"; case NameType::Other: return "Other"; } @@ -42,6 +54,50 @@ BOTAN_ASSERT_UNREACHABLE(); } +GeneralName GeneralName::email(std::string_view email) { + return GeneralName::make(email); +} + +GeneralName GeneralName::dns(std::string_view dns) { + return GeneralName::make(dns); +} + +GeneralName GeneralName::uri(std::string_view uri) { + return GeneralName::make(uri); +} + +GeneralName GeneralName::directory_name(Botan::X509_DN dn) { + return GeneralName::make(std::move(dn)); +} + +GeneralName GeneralName::ipv4_address(uint32_t ipv4) { + return GeneralName::ipv4_address(IPv4Address(ipv4)); +} + +GeneralName GeneralName::ipv4_address(uint32_t ipv4, uint32_t mask) { + auto subnet = IPv4Subnet::from_address_and_mask(ipv4, mask); + if(!subnet.has_value()) { + throw Invalid_Argument("IPv4 subnet mask is not a contiguous CIDR prefix"); + } + return GeneralName::make(*subnet); +} + +GeneralName GeneralName::ipv4_address(IPv4Address ipv4) { + return GeneralName::make(IPv4Subnet::host(ipv4)); +} + +GeneralName GeneralName::ipv4_address(const IPv4Subnet& subnet) { + return GeneralName::make(subnet); +} + +GeneralName GeneralName::ipv6_address(const IPv6Address& ipv6) { + return GeneralName::make(IPv6Subnet::host(ipv6)); +} + +GeneralName GeneralName::ipv6_address(const IPv6Subnet& subnet) { + return GeneralName::make(subnet); +} + std::string GeneralName::name() const { const size_t index = m_name.index(); @@ -54,19 +110,34 @@ } else if(index == DN_IDX) { return std::get(m_name).to_string(); } else if(index == IPV4_IDX) { - auto [net, mask] = std::get(m_name); - return fmt("{}/{}", ipv4_to_string(net), ipv4_to_string(mask)); + const auto& subnet = std::get(m_name); + return subnet.is_host() ? subnet.address().to_string() : subnet.to_string(); + } else if(index == IPV6_IDX) { + const auto& subnet = std::get(m_name); + return subnet.is_host() ? subnet.address().to_string() : subnet.to_string(); } else { BOTAN_ASSERT_UNREACHABLE(); } } +std::vector GeneralName::binary_name() const { + return std::visit(Botan::overloaded{ + [](const Botan::X509_DN& dn) { return Botan::ASN1::put_in_sequence(dn.get_bits()); }, + [](const IPv4Subnet& subnet) { return subnet.serialize(); }, + [](const IPv6Subnet& subnet) { return subnet.serialize(); }, + [](const auto&) -> std::vector { + throw Invalid_State("Cannot convert GeneralName to binary string"); + }, + }, + m_name); +} + void GeneralName::encode_into(DER_Encoder& /*to*/) const { throw Not_Implemented("GeneralName encoding"); } void GeneralName::decode_from(BER_Decoder& ber) { - BER_Object obj = ber.get_next_object(); + const BER_Object obj = ber.get_next_object(); if(obj.is_a(0, ASN1_Class::ExplicitContextSpecific)) { m_type = NameType::Other; @@ -74,29 +145,43 @@ m_type = NameType::RFC822; m_name.emplace(ASN1::to_string(obj)); } else if(obj.is_a(2, ASN1_Class::ContextSpecific)) { - m_type = NameType::DNS; // Store it in case insensitive form so we don't have to do it // again while matching - m_name.emplace(tolower_string(ASN1::to_string(obj))); + auto dns = canonicalize_dns_name(ASN1::to_string(obj)); + // An empty DNS subtree has no clear meaning, reject immediately + if(dns.empty()) { + throw Decoding_Error("Empty DNS name in GeneralName"); + } + m_type = NameType::DNS; + m_name.emplace(std::move(dns)); } else if(obj.is_a(6, ASN1_Class::ContextSpecific)) { m_type = NameType::URI; m_name.emplace(ASN1::to_string(obj)); } else if(obj.is_a(4, ASN1_Class::ContextSpecific | ASN1_Class::Constructed)) { X509_DN dn; - BER_Decoder dec(obj); + BER_Decoder dec(obj, ber.limits()); dn.decode_from(dec); m_type = NameType::DN; m_name.emplace(dn); } else if(obj.is_a(7, ASN1_Class::ContextSpecific)) { if(obj.length() == 8) { - const uint32_t net = load_be(obj.bits(), 0); - const uint32_t mask = load_be(obj.bits(), 1); + const auto addr_and_mask = std::span{obj.bits(), 8}; + auto subnet = IPv4Subnet::from_address_and_mask(addr_and_mask); + if(!subnet.has_value()) { + throw Decoding_Error("IPv4 name constraint mask is not a contiguous CIDR prefix"); + } m_type = NameType::IPv4; - m_name.emplace(std::make_pair(net, mask)); + m_name.emplace(*subnet); } else if(obj.length() == 32) { - // IPv6 name constraints are not implemented - m_type = NameType::Unknown; + const auto addr_and_mask = std::span{obj.bits(), 32}; + auto subnet = IPv6Subnet::from_address_and_mask(addr_and_mask); + if(!subnet.has_value()) { + throw Decoding_Error("IPv6 name constraint mask is not a contiguous CIDR prefix"); + } + + m_type = NameType::IPv6; + m_name.emplace(*subnet); } else { throw Decoding_Error("Invalid IP name constraint size " + std::to_string(obj.length())); } @@ -115,8 +200,14 @@ bool GeneralName::matches_ipv4(uint32_t ip) const { if(m_type == NameType::IPv4) { - auto [net, mask] = std::get(m_name); - return (ip & mask) == net; + return std::get(m_name).contains(IPv4Address(ip)); + } + return false; +} + +bool GeneralName::matches_ipv6(const IPv6Address& ip) const { + if(m_type == NameType::IPv6) { + return std::get(m_name).contains(ip); } return false; } @@ -176,7 +267,7 @@ // Check CN instead... for(const std::string& cn : dn.get_attribute("CN")) { if(!string_to_ipv4(cn).has_value()) { - score.add(matches_dns(cn, constraint)); + score.add(matches_dns(canonicalize_dns_name(cn), constraint)); } } } @@ -188,22 +279,24 @@ score.add(matches_dn(alt_dn, constraint)); } } else if(m_type == NameType::IPv4) { - auto [net, mask] = std::get(m_name); + const auto& subnet = std::get(m_name); if(alt_name.count() == 0) { // Check CN instead... for(const std::string& cn : dn.get_attribute("CN")) { if(auto ipv4 = string_to_ipv4(cn)) { - bool match = (ipv4.value() & mask) == net; - score.add(match); + score.add(subnet.contains(IPv4Address(*ipv4))); } } } else { - for(uint32_t ipv4 : alt_name.ipv4_address()) { - bool match = (ipv4 & mask) == net; - score.add(match); + for(const uint32_t ipv4 : alt_name.ipv4_address()) { + score.add(subnet.contains(IPv4Address(ipv4))); } } + } else if(m_type == NameType::IPv6) { + for(const auto& ipv6 : alt_name.ipv6_address()) { + score.add(matches_ipv6(ipv6)); + } } else { // URI and email name constraint matching not implemented return MatchResult::UnknownType; @@ -227,7 +320,7 @@ return true; } - std::string_view substr = name.substr(name.size() - constraint.size(), constraint.size()); + const std::string_view substr = name.substr(name.size() - constraint.size(), constraint.size()); if(constraint.front() == '.') { return substr == constraint; @@ -241,20 +334,23 @@ //static bool GeneralName::matches_dn(const X509_DN& name, const X509_DN& constraint) { - const auto attr = name.get_attributes(); - bool ret = true; - size_t trys = 0; + // Perform DN matching by comparing RDNs in sequence, i.e., + // whether the constraint is a prefix of the name. + const auto& name_info = name.dn_info(); + const auto& constraint_info = constraint.dn_info(); - for(const auto& c : constraint.dn_info()) { - auto i = attr.equal_range(c.first); + if(constraint_info.size() > name_info.size()) { + return false; + } - if(i.first != i.second) { - trys += 1; - ret = ret && (i.first->second == c.second.value()); + for(size_t i = 0; i < constraint_info.size(); ++i) { + if(name_info[i].first != constraint_info[i].first || + !x500_name_cmp(name_info[i].second.value(), constraint_info[i].second.value())) { + return false; } } - return trys > 0 && ret; + return !constraint_info.empty(); } std::ostream& operator<<(std::ostream& os, const GeneralName& gn) { @@ -262,23 +358,33 @@ return os; } -GeneralSubtree::GeneralSubtree() : m_base() {} +GeneralSubtree::GeneralSubtree() = default; void GeneralSubtree::encode_into(DER_Encoder& /*to*/) const { throw Not_Implemented("GeneralSubtree encoding"); } void GeneralSubtree::decode_from(BER_Decoder& ber) { - size_t minimum; + /* + * RFC 5280 Section 4.2.1.10: + * Within this profile, the minimum and maximum fields are not used with any + * name forms, thus, the minimum MUST be zero, and maximum MUST be absent. + */ + size_t minimum = 0; + std::optional maximum; ber.start_sequence() .decode(m_base) .decode_optional(minimum, ASN1_Type(0), ASN1_Class::ContextSpecific, size_t(0)) + .decode_optional(maximum, ASN1_Type(1), ASN1_Class::ContextSpecific) .end_cons(); if(minimum != 0) { throw Decoding_Error("GeneralSubtree minimum must be 0"); } + if(maximum.has_value()) { + throw Decoding_Error("GeneralSubtree maximum must be absent"); + } } std::ostream& operator<<(std::ostream& os, const GeneralSubtree& gs) { @@ -304,7 +410,7 @@ * OpenSSL uses a similar limit, but applies it to the total number of * constraints, while we apply it to permitted and excluded independently. */ - constexpr size_t MAX_NC_CHECKS = (1 << 20); + constexpr size_t MAX_NC_CHECKS = (1 << 16); if(auto names = checked_add(dn_count, alt_count)) { if(auto product = checked_mul(*names, constraint_count)) { @@ -377,9 +483,18 @@ return false; }; + /* + RFC 5280 4.2.1.10: iPAddress is a single GeneralName element where + IPv4 and IPv6 are distinguished only by the length. + + An iPAddress subtree of either version therefore restricts the iPAddress name + form for both versions. + */ + const bool ip_form_restricted = m_permitted_name_types.contains(GeneralName::NameType::IPv4) || + m_permitted_name_types.contains(GeneralName::NameType::IPv6); + auto is_permitted_ipv4 = [&](uint32_t ipv4) { - // If no restrictions, then immediate accept - if(!m_permitted_name_types.contains(GeneralName::NameType::IPv4)) { + if(!ip_form_restricted) { return true; } @@ -389,11 +504,43 @@ } } - // There is at least one permitted name and we didn't match + // We might here check if there are any IPv6 permitted names which are + // mapped IPv4 addresses, and if so check if any of those apply. It's not + // clear if this is desirable, and RFC 5280 is completely silent on the issue. + + // There is at least one permitted iPAddress name and we didn't match return false; }; - if(!is_permitted_dn(cert.subject_dn())) { + auto is_permitted_ipv6 = [&](const IPv6Address& ipv6) { + if(!ip_form_restricted) { + return true; + } + + for(const auto& c : m_permitted_subtrees) { + if(c.base().matches_ipv6(ipv6)) { + return true; + } + } + + // There is at least one permitted iPAddress name and we didn't match + return false; + }; + + /* + RFC 5280 4.1.2.6: + If subject naming information is present only in the + subjectAltName extension (e.g., a key bound only to an email + address or URI), then the subject name MUST be an empty + sequence and the subjectAltName extension MUST be critical. + + RFC 5280 4.2.1.10: + Restrictions of the form directoryName MUST be applied to the subject + field in the certificate (when the certificate includes a non-empty + subject field) and to any names of type directoryName in the + subjectAltName extension. + */ + if(!cert.subject_dn().empty() && !is_permitted_dn(cert.subject_dn())) { return false; } @@ -415,6 +562,12 @@ } } + for(const auto& alt_ipv6 : alt_name.ipv6_address()) { + if(!is_permitted_ipv6(alt_ipv6)) { + return false; + } + } + if(alt_name.count() == 0) { for(const auto& cn : cert.subject_info("Name")) { if(cn.find(".") != std::string::npos) { @@ -423,7 +576,7 @@ return false; } } else { - if(!is_permitted_dns_name(cn)) { + if(!is_permitted_dns_name(canonicalize_dns_name(cn))) { return false; } } @@ -486,10 +639,26 @@ return false; } + const bool name_has_wildcard = (name.find('*') != std::string::npos); + for(const auto& c : m_excluded_subtrees) { if(c.base().matches_dns(name)) { return true; } + + /* + RFC 5280 4.2.1.10 - "any name matching a restriction in the + excludedSubtrees field is invalid". + + If the cert has a wildcard SAN (*.example.com), and that wildcard + could be matched against an excluded name, it must be rejected. + */ + if(name_has_wildcard && c.base().m_type == GeneralName::NameType::DNS) { + const auto& constraint = std::get(c.base().m_name); + if(host_wildcard_match(name, constraint)) { + return true; + } + } } // There is at least one excluded name and we didn't match @@ -497,18 +666,40 @@ }; auto is_excluded_ipv4 = [&](uint32_t ipv4) { - // If no restrictions, then immediate accept - if(!m_excluded_name_types.contains(GeneralName::NameType::IPv4)) { - return false; + if(m_excluded_name_types.contains(GeneralName::NameType::IPv4)) { + for(const auto& c : m_excluded_subtrees) { + if(c.base().matches_ipv4(ipv4)) { + return true; + } + } } - for(const auto& c : m_excluded_subtrees) { - if(c.base().matches_ipv4(ipv4)) { - return true; + // This name did not match any of the excluded names + return false; + }; + + auto is_excluded_ipv6 = [&](const IPv6Address& ipv6) { + if(m_excluded_name_types.contains(GeneralName::NameType::IPv6)) { + for(const auto& c : m_excluded_subtrees) { + if(c.base().matches_ipv6(ipv6)) { + return true; + } } } - // There is at least one excluded name and we didn't match + // An IPv4-mapped IPv6 address names an IPv4 address so verify that + // address is not restricted by an IPv4 excludes rule + if(m_excluded_name_types.contains(GeneralName::NameType::IPv4)) { + if(auto embedded_v4 = ipv6.as_ipv4()) { + for(const auto& c : m_excluded_subtrees) { + if(c.base().matches_ipv4(*embedded_v4)) { + return true; + } + } + } + } + + // This name did not match any of the excluded names return false; }; @@ -534,6 +725,12 @@ } } + for(const auto& alt_ipv6 : alt_name.ipv6_address()) { + if(is_excluded_ipv6(alt_ipv6)) { + return true; + } + } + if(alt_name.count() == 0) { for(const auto& cn : cert.subject_info("Name")) { if(cn.find(".") != std::string::npos) { @@ -542,7 +739,7 @@ return true; } } else { - if(is_excluded_dns_name(cn)) { + if(is_excluded_dns_name(canonicalize_dns_name(cn))) { return true; } } diff -Nru botan3-3.7.1+dfsg/src/lib/x509/ocsp.cpp botan3-3.12.0+dfsg/src/lib/x509/ocsp.cpp --- botan3-3.7.1+dfsg/src/lib/x509/ocsp.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/ocsp.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,11 +11,10 @@ #include #include #include +#include #include #include -#include - -#include +#include #if defined(BOTAN_HAS_HTTP_UTIL) #include @@ -23,18 +22,142 @@ namespace Botan::OCSP { +CertID::CertID(const X509_Certificate& issuer, const BigInt& subject_serial) : m_subject_serial(subject_serial) { + /* + In practice it seems some responders, including, notably, + ocsp.verisign.com, will reject anything but SHA-1 here + */ + auto hash = HashFunction::create_or_throw("SHA-1"); + + m_hash_id = AlgorithmIdentifier(hash->name(), AlgorithmIdentifier::USE_NULL_PARAM); + m_issuer_key_hash = hash->process>(issuer.subject_public_key_bitstring()); + m_issuer_dn_hash = hash->process>(issuer.raw_subject_dn()); +} + +bool CertID::is_id_for(const X509_Certificate& issuer, const X509_Certificate& subject) const { + try { + if(BigInt::from_bytes(subject.serial_number()) != m_subject_serial) { + return false; + } + + const std::string hash_algo = m_hash_id.oid().to_formatted_string(); + + if(hash_algo != "SHA-1" && hash_algo != "SHA-256") { + return false; + } + + auto hash = HashFunction::create_or_throw(hash_algo); + + /* + RFC 6960 4.1.1 + issuerNameHash is the hash of the issuer's distinguished name (DN). + The hash shall be calculated over the DER encoding of the issuer's name + field in the certificate being checked. + */ + if(m_issuer_dn_hash != hash->process>(subject.raw_issuer_dn())) { + return false; + } + + if(m_issuer_key_hash != hash->process>(issuer.subject_public_key_bitstring())) { + return false; + } + } catch(...) { + return false; + } + + return true; +} + +void CertID::encode_into(DER_Encoder& to) const { + to.start_sequence() + .encode(m_hash_id) + .encode(m_issuer_dn_hash, ASN1_Type::OctetString) + .encode(m_issuer_key_hash, ASN1_Type::OctetString) + .encode(m_subject_serial) + .end_cons(); +} + +void CertID::decode_from(BER_Decoder& from) { + /* + * RFC 6960 Section 4.1.1 + * + * CertID ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * issuerNameHash OCTET STRING, + * issuerKeyHash OCTET STRING, + * serialNumber CertificateSerialNumber } + */ + from.start_sequence() + .decode(m_hash_id) + .decode(m_issuer_dn_hash, ASN1_Type::OctetString) + .decode(m_issuer_key_hash, ASN1_Type::OctetString) + .decode(m_subject_serial) + .end_cons(); +} + +void SingleResponse::encode_into(DER_Encoder& /*to*/) const { + throw Not_Implemented("SingleResponse::encode_into"); +} + +void SingleResponse::decode_from(BER_Decoder& from) { + /* + * RFC 6960 Section 4.2.1 + * + * SingleResponse ::= SEQUENCE { + * certID CertID, + * certStatus CertStatus, + * thisUpdate GeneralizedTime, + * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, + * singleExtensions [1] EXPLICIT Extensions OPTIONAL } + * + * CertStatus ::= CHOICE { + * good [0] IMPLICIT NULL, + * revoked [1] IMPLICIT RevokedInfo, + * unknown [2] IMPLICIT UnknownInfo } + * + * RevokedInfo ::= SEQUENCE { + * revocationTime GeneralizedTime, + * revocationReason [0] EXPLICIT CRLReason OPTIONAL } + */ + BER_Object cert_status; + Extensions extensions; + + from.start_sequence() + .decode(m_certid) + .get_next(cert_status) + .decode(m_thisupdate) + .decode_optional(m_nextupdate, ASN1_Type(0), ASN1_Class::ContextSpecific | ASN1_Class::Constructed) + .decode_optional(extensions, ASN1_Type(1), ASN1_Class::ContextSpecific | ASN1_Class::Constructed) + .end_cons(); + + const auto cert_status_class = cert_status.get_class(); + if(cert_status_class != ASN1_Class::ContextSpecific && + cert_status_class != (ASN1_Class::ContextSpecific | ASN1_Class::Constructed)) { + throw Decoding_Error("OCSP::SingleResponse: certStatus has unexpected class tag"); + } + + // TODO: should verify the cert_status body and decode RevokedInfo + m_cert_status = static_cast(cert_status.type()); + if(m_cert_status > 2) { + throw Decoding_Error("Unknown OCSP CertStatus tag"); + } + + // We don't currently recognize any extensions here so if any are critical we should reject + m_has_unknown_critical_ext = !extensions.critical_extensions().empty(); +} + namespace { // TODO: should this be in a header somewhere? void decode_optional_list(BER_Decoder& ber, ASN1_Type tag, std::vector& output) { - BER_Object obj = ber.get_next_object(); + const BER_Object obj = ber.get_next_object(); - if(obj.is_a(tag, ASN1_Class::ContextSpecific | ASN1_Class::Constructed) == false) { + if(!obj.is_a(tag, ASN1_Class::ContextSpecific | ASN1_Class::Constructed)) { ber.push_back(obj); return; } - BER_Decoder list(obj); + BER_Decoder list(obj, BER_Decoder::Limits::DER()); auto seq = list.start_sequence(); while(seq.more_items()) { output.push_back([&] { @@ -59,6 +182,20 @@ m_issuer(issuer_cert), m_certid(m_issuer, subject_serial) {} std::vector Request::BER_encode() const { + /* + * RFC 6960 Section 4.1.1 + * + * OCSPRequest ::= SEQUENCE { + * tbsRequest TBSRequest, + * optionalSignature [0] EXPLICIT Signature OPTIONAL } + * + * TBSRequest ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * requestList SEQUENCE OF Request } + * + * Request ::= SEQUENCE { + * reqCert CertID } + */ std::vector output; DER_Encoder(output) .start_sequence() @@ -86,12 +223,43 @@ Response::Response(const uint8_t response_bits[], size_t response_bits_len) : m_response_bits(response_bits, response_bits + response_bits_len) { - BER_Decoder response_outer = BER_Decoder(m_response_bits).start_sequence(); + /* + * RFC 6960 Section 4.2.1 + * + * OCSPResponse ::= SEQUENCE { + * responseStatus OCSPResponseStatus, + * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } + * + * OCSPResponseStatus ::= ENUMERATED { ... } + * + * ResponseBytes ::= SEQUENCE { + * responseType OBJECT IDENTIFIER, + * response OCTET STRING } + */ + BER_Decoder outer_decoder(m_response_bits, BER_Decoder::Limits::DER()); + BER_Decoder response_outer = outer_decoder.start_sequence(); size_t resp_status = 0; response_outer.decode(resp_status, ASN1_Type::Enumerated, ASN1_Class::Universal); + /* + RFC 6960 4.2.1 + + OCSPResponseStatus ::= ENUMERATED { + successful (0), -- Response has valid confirmations + malformedRequest (1), -- Illegal confirmation request + internalError (2), -- Internal error in issuer + tryLater (3), -- Try again later + -- (4) is not used + sigRequired (5), -- Must sign the request + unauthorized (6) -- Request unauthorized + } + */ + if(resp_status >= 7) { + throw Decoding_Error("Unknown OCSPResponseStatus code"); + } + m_status = static_cast(resp_status); if(m_status != Response_Status_Code::Successful) { @@ -99,11 +267,22 @@ } if(response_outer.more_items()) { - BER_Decoder response_bytes = response_outer.start_context_specific(0).start_sequence(); + BER_Decoder response_bytes_ctx = response_outer.start_context_specific(0); + BER_Decoder response_bytes = response_bytes_ctx.start_sequence(); - response_bytes.decode_and_check(OID("1.3.6.1.5.5.7.48.1.1"), "Unknown response type in OCSP response"); + response_bytes.decode_and_check(OID({1, 3, 6, 1, 5, 5, 7, 48, 1, 1}), "Unknown response type in OCSP response"); - BER_Decoder basicresponse = BER_Decoder(response_bytes.get_next_octet_string()).start_sequence(); + /* + * RFC 6960 Section 4.2.1 + * + * BasicOCSPResponse ::= SEQUENCE { + * tbsResponseData ResponseData, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING, + * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + */ + BER_Decoder basic_response_decoder(response_bytes.get_next_octet_string(), BER_Decoder::Limits::DER()); + BER_Decoder basicresponse = basic_response_decoder.start_sequence(); basicresponse.start_sequence() .raw_bytes(m_tbs_bits) @@ -112,10 +291,27 @@ .decode(m_signature, ASN1_Type::BitString); decode_optional_list(basicresponse, ASN1_Type(0), m_certs); + basicresponse.verify_end(); + basic_response_decoder.verify_end(); + + /* + * RFC 6960 Section 4.2.1 + * + * ResponseData ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * responderID ResponderID, + * producedAt GeneralizedTime, + * responses SEQUENCE OF SingleResponse, + * responseExtensions [1] EXPLICIT Extensions OPTIONAL } + * + * ResponderID ::= CHOICE { + * byName [1] Name, + * byKey [2] KeyHash } + */ size_t responsedata_version = 0; Extensions extensions; - BER_Decoder(m_tbs_bits) + BER_Decoder(m_tbs_bits, BER_Decoder::Limits::DER()) .decode_optional(responsedata_version, ASN1_Type(0), ASN1_Class::ContextSpecific | ASN1_Class::Constructed) .decode_optional(m_signer_name, ASN1_Type(1), ASN1_Class::ContextSpecific | ASN1_Class::Constructed) @@ -127,7 +323,9 @@ .decode_list(m_responses) - .decode_optional(extensions, ASN1_Type(1), ASN1_Class::ContextSpecific | ASN1_Class::Constructed); + .decode_optional(extensions, ASN1_Type(1), ASN1_Class::ContextSpecific | ASN1_Class::Constructed) + + .verify_end(); const bool has_signer = !m_signer_name.empty(); const bool has_key_hash = !m_key_hash.empty(); @@ -138,9 +336,30 @@ if(!has_signer && !has_key_hash) { throw Decoding_Error("OCSP response contains neither byName nor byKey in responderID field"); } + if(has_key_hash && m_key_hash.size() != 20) { + // KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key + throw Decoding_Error("OCSP response contains a byKey with invalid length"); + } + + response_bytes.verify_end(); + response_bytes_ctx.verify_end(); + + // We don't currently recognize any extensions here so if any are critical we should reject + m_has_unknown_critical_ext = !extensions.critical_extensions().empty(); } - response_outer.end_cons(); + response_outer.verify_end(); + outer_decoder.verify_end(); + + if(m_has_unknown_critical_ext == false) { + // Check all of the SingleResponse extensions + for(const auto& sr : m_responses) { + if(sr.has_unknown_critical_extension()) { + m_has_unknown_critical_ext = true; + break; + } + } + } } bool Response::is_issued_by(const X509_Certificate& candidate) const { @@ -156,6 +375,13 @@ } Certificate_Status_Code Response::verify_signature(const X509_Certificate& issuer) const { + const Path_Validation_Restrictions restrictions; + + return this->verify_signature(issuer, restrictions); +} + +Certificate_Status_Code Response::verify_signature(const X509_Certificate& issuer, + const Path_Validation_Restrictions& restrictions) const { if(m_dummy_response_status) { return m_dummy_response_status.value(); } @@ -173,11 +399,26 @@ PK_Verifier verifier(*pub_key, m_sig_algo); - if(verifier.verify_message(ASN1::put_in_sequence(m_tbs_bits), m_signature)) { - return Certificate_Status_Code::OCSP_SIGNATURE_OK; - } else { + const bool valid_signature = verifier.verify_message(ASN1::put_in_sequence(m_tbs_bits), m_signature); + + if(valid_signature == false) { return Certificate_Status_Code::OCSP_SIGNATURE_ERROR; } + + if(m_has_unknown_critical_ext) { + return Certificate_Status_Code::UNKNOWN_CRITICAL_EXTENSION; + } + + const auto& trusted_hashes = restrictions.trusted_hashes(); + if(!trusted_hashes.empty() && !trusted_hashes.contains(verifier.hash_function())) { + return Certificate_Status_Code::UNTRUSTED_HASH; + } + + if(pub_key->estimated_strength() < restrictions.minimum_key_strength()) { + return Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK; + } + + return Certificate_Status_Code::OCSP_SIGNATURE_OK; } catch(Exception&) { return Certificate_Status_Code::OCSP_SIGNATURE_ERROR; } @@ -193,13 +434,14 @@ } // Then try to find a delegated responder certificate in the stapled certs - auto match = std::find_if(m_certs.begin(), m_certs.end(), std::bind(&Response::is_issued_by, this, _1)); - if(match != m_certs.end()) { - return *match; + for(const auto& cert : m_certs) { + if(this->is_issued_by(cert)) { + return cert; + } } // Last resort: check the additionally provides trusted OCSP responders - if(trusted_ocsp_responders) { + if(trusted_ocsp_responders != nullptr) { if(!m_key_hash.empty()) { auto signing_cert = trusted_ocsp_responders->find_cert_by_pubkey_sha1(m_key_hash); if(signing_cert) { @@ -228,23 +470,35 @@ for(const auto& response : m_responses) { if(response.certid().is_id_for(issuer, subject)) { - X509_Time x509_ref_time(ref_time); + const X509_Time x509_ref_time(ref_time); + + /* + * We check certificate status prior to checking expiration, since otherwise it's + * possible to take an OCSP response indicating revocation, wait for it to expire, + * and then staple it. If such a response was reported as "expired" rather than + * "revoked" it's easy to dismiss as a clock issue or other misconfiguration. + */ if(response.cert_status() == 1) { return Certificate_Status_Code::CERT_IS_REVOKED; } - if(response.this_update() > x509_ref_time) { - return Certificate_Status_Code::OCSP_NOT_YET_VALID; - } + try { + if(response.this_update() > x509_ref_time) { + return Certificate_Status_Code::OCSP_NOT_YET_VALID; + } - if(response.next_update().time_is_set()) { - if(x509_ref_time > response.next_update()) { - return Certificate_Status_Code::OCSP_HAS_EXPIRED; + if(response.next_update().time_is_set()) { + if(x509_ref_time > response.next_update()) { + return Certificate_Status_Code::OCSP_HAS_EXPIRED; + } + } else if(max_age > std::chrono::seconds::zero() && + ref_time - response.this_update().to_std_timepoint() > max_age) { + return Certificate_Status_Code::OCSP_IS_TOO_OLD; } - } else if(max_age > std::chrono::seconds::zero() && - ref_time - response.this_update().to_std_timepoint() > max_age) { - return Certificate_Status_Code::OCSP_IS_TOO_OLD; + } catch(Exception&) { + // This can occur if eg the OCSP time is not representable by the system clock + return Certificate_Status_Code::OCSP_RESPONSE_INVALID; } if(response.cert_status() == 0) { @@ -268,7 +522,7 @@ throw Invalid_Argument("No OCSP responder specified"); } - OCSP::Request req(issuer, subject_serial); + const OCSP::Request req(issuer, subject_serial); auto http = HTTP::POST_sync(ocsp_responder, "application/ocsp-request", req.BER_encode(), 1, timeout); diff -Nru botan3-3.7.1+dfsg/src/lib/x509/ocsp.h botan3-3.12.0+dfsg/src/lib/x509/ocsp.h --- botan3-3.7.1+dfsg/src/lib/x509/ocsp.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/ocsp.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #define BOTAN_OCSP_H_ #include +#include #include #include #include @@ -18,6 +19,7 @@ namespace Botan { +class Path_Validation_Restrictions; class Certificate_Store; namespace OCSP { @@ -49,19 +51,22 @@ size_t cert_status() const { return m_cert_status; } - X509_Time this_update() const { return m_thisupdate; } + const X509_Time& this_update() const { return m_thisupdate; } - X509_Time next_update() const { return m_nextupdate; } + const X509_Time& next_update() const { return m_nextupdate; } void encode_into(DER_Encoder& to) const override; void decode_from(BER_Decoder& from) override; + bool has_unknown_critical_extension() const { return m_has_unknown_critical_ext; } + private: CertID m_certid; size_t m_cert_status = 2; // unknown X509_Time m_thisupdate; X509_Time m_nextupdate; + bool m_has_unknown_critical_ext = false; }; /** @@ -95,8 +100,11 @@ /** * @return subject certificate + * TODO(Botan4) remove this function */ - const X509_Certificate& subject() const { throw Not_Implemented("Method have been deprecated"); } + const X509_Certificate& subject() const { // NOLINT(*-convert-member-functions-to-static) + throw Not_Implemented("Method have been deprecated"); + } const std::vector& issuer_key_hash() const { return m_certid.issuer_key_hash(); } @@ -110,7 +118,7 @@ * * see https://tools.ietf.org/html/rfc6960#section-4.2.1 */ -enum class Response_Status_Code { +enum class Response_Status_Code : uint8_t { Successful = 0, Malformed_Request = 1, Internal_Error = 2, @@ -130,13 +138,14 @@ * Create a fake OCSP response from a given status code. * @param status the status code the check functions will return */ - Response(Certificate_Status_Code status); + BOTAN_FUTURE_EXPLICIT Response(Certificate_Status_Code status); /** * Parses an OCSP response. * @param response_bits response bits received */ - Response(const std::vector& response_bits) : Response(response_bits.data(), response_bits.size()) {} + BOTAN_FUTURE_EXPLICIT Response(const std::vector& response_bits) : + Response(response_bits.data(), response_bits.size()) {} /** * Parses an OCSP response. @@ -172,6 +181,21 @@ Certificate_Status_Code verify_signature(const X509_Certificate& signing_certificate) const; /** + * Check signature of the OCSP response. + * + * Note: It is the responsibility of the caller to verify that signing + * certificate is trustworthy and authorized to do so. + * + * @param signing_certificate the certificate that signed this response + * (@sa Response::find_signing_certificate) + * @param restrictions on the signature validation + * + * @return status code indicating the validity of the signature + */ + Certificate_Status_Code verify_signature(const X509_Certificate& signing_certificate, + const Path_Validation_Restrictions& restrictions) const; + + /** * @return the status of the response */ Response_Status_Code status() const { return m_status; } @@ -242,6 +266,8 @@ std::vector m_responses; std::optional m_dummy_response_status; + + bool m_has_unknown_critical_ext = false; }; #if defined(BOTAN_HAS_HTTP_UTIL) diff -Nru botan3-3.7.1+dfsg/src/lib/x509/ocsp_types.cpp botan3-3.12.0+dfsg/src/lib/x509/ocsp_types.cpp --- botan3-3.7.1+dfsg/src/lib/x509/ocsp_types.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/ocsp_types.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,103 +0,0 @@ -/* -* OCSP subtypes -* (C) 2012 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include -#include -#include - -namespace Botan::OCSP { - -CertID::CertID(const X509_Certificate& issuer, const BigInt& subject_serial) { - /* - In practice it seems some responders, including, notably, - ocsp.verisign.com, will reject anything but SHA-1 here - */ - auto hash = HashFunction::create_or_throw("SHA-1"); - - m_hash_id = AlgorithmIdentifier(hash->name(), AlgorithmIdentifier::USE_NULL_PARAM); - m_issuer_key_hash = unlock(hash->process(issuer.subject_public_key_bitstring())); - m_issuer_dn_hash = unlock(hash->process(issuer.raw_subject_dn())); - m_subject_serial = subject_serial; -} - -bool CertID::is_id_for(const X509_Certificate& issuer, const X509_Certificate& subject) const { - try { - if(BigInt::from_bytes(subject.serial_number()) != m_subject_serial) { - return false; - } - - const std::string hash_algo = m_hash_id.oid().to_formatted_string(); - auto hash = HashFunction::create_or_throw(hash_algo); - - if(m_issuer_dn_hash != unlock(hash->process(subject.raw_issuer_dn()))) { - return false; - } - - if(m_issuer_key_hash != unlock(hash->process(issuer.subject_public_key_bitstring()))) { - return false; - } - } catch(...) { - return false; - } - - return true; -} - -void CertID::encode_into(DER_Encoder& to) const { - to.start_sequence() - .encode(m_hash_id) - .encode(m_issuer_dn_hash, ASN1_Type::OctetString) - .encode(m_issuer_key_hash, ASN1_Type::OctetString) - .encode(m_subject_serial) - .end_cons(); -} - -void CertID::decode_from(BER_Decoder& from) { - from.start_sequence() - .decode(m_hash_id) - .decode(m_issuer_dn_hash, ASN1_Type::OctetString) - .decode(m_issuer_key_hash, ASN1_Type::OctetString) - .decode(m_subject_serial) - .end_cons(); -} - -void SingleResponse::encode_into(DER_Encoder& /*to*/) const { - throw Not_Implemented("SingleResponse::encode_into"); -} - -void SingleResponse::decode_from(BER_Decoder& from) { - BER_Object cert_status; - Extensions extensions; - - from.start_sequence() - .decode(m_certid) - .get_next(cert_status) - .decode(m_thisupdate) - .decode_optional(m_nextupdate, ASN1_Type(0), ASN1_Class::ContextSpecific | ASN1_Class::Constructed) - .decode_optional(extensions, ASN1_Type(1), ASN1_Class::ContextSpecific | ASN1_Class::Constructed) - .end_cons(); - - /* CertStatus ::= CHOICE { - good [0] IMPLICIT NULL, - revoked [1] IMPLICIT RevokedInfo, - unknown [2] IMPLICIT UnknownInfo } - - RevokedInfo ::= SEQUENCE { - revocationTime GeneralizedTime, - revocationReason [0] EXPLICIT CRLReason OPTIONAL } - - UnknownInfo ::= NULL - - We should verify the expected body and decode the RevokedInfo - */ - m_cert_status = static_cast(cert_status.type()); -} - -} // namespace Botan::OCSP diff -Nru botan3-3.7.1+dfsg/src/lib/x509/pkcs10.cpp botan3-3.12.0+dfsg/src/lib/x509/pkcs10.cpp --- botan3-3.7.1+dfsg/src/lib/x509/pkcs10.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/pkcs10.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -17,7 +17,8 @@ namespace Botan { -struct PKCS10_Data { +class PKCS10_Data final { + public: X509_DN m_subject_dn; std::vector m_public_key_bits; AlternativeName m_alt_name; @@ -70,7 +71,7 @@ .raw_bytes(key.subject_public_key()) .start_explicit(0); - if(challenge.empty() == false) { + if(!challenge.empty()) { std::vector value; DER_Encoder(value).encode(ASN1_String(challenge)); tbs_req.encode(Attribute("PKCS9.ChallengePassword", value)); @@ -83,7 +84,7 @@ // end the start_explicit above tbs_req.end_explicit().end_cons(); - const std::vector req = X509_Object::make_signed(*signer, rng, sig_algo, tbs_req.get_contents()); + const std::vector req = X509_Object::make_signed(*signer, rng, sig_algo, tbs_req.get_contents_unlocked()); return PKCS10_Request(req); } @@ -96,9 +97,9 @@ std::unique_ptr decode_pkcs10(const std::vector& body) { auto data = std::make_unique(); - BER_Decoder cert_req_info(body); + BER_Decoder cert_req_info(body, BER_Decoder::Limits::DER()); - size_t version; + size_t version = 0; cert_req_info.decode(version); if(version != 0) { throw Decoding_Error("Unknown version code in PKCS #10 request: " + std::to_string(version)); @@ -106,25 +107,25 @@ cert_req_info.decode(data->m_subject_dn); - BER_Object public_key = cert_req_info.get_next_object(); - if(public_key.is_a(ASN1_Type::Sequence, ASN1_Class::Constructed) == false) { + const BER_Object public_key = cert_req_info.get_next_object(); + if(!public_key.is_a(ASN1_Type::Sequence, ASN1_Class::Constructed)) { throw BER_Bad_Tag("PKCS10_Request: Unexpected tag for public key", public_key.tagging()); } data->m_public_key_bits = ASN1::put_in_sequence(public_key.bits(), public_key.length()); - BER_Object attr_bits = cert_req_info.get_next_object(); + const BER_Object attr_bits = cert_req_info.get_next_object(); std::set pkcs9_email; if(attr_bits.is_a(0, ASN1_Class::Constructed | ASN1_Class::ContextSpecific)) { - BER_Decoder attributes(attr_bits); + BER_Decoder attributes(attr_bits, cert_req_info.limits()); while(attributes.more_items()) { Attribute attr; attributes.decode(attr); const OID& oid = attr.object_identifier(); - BER_Decoder value(attr.get_parameters()); + BER_Decoder value(attr.get_parameters(), cert_req_info.limits()); if(oid == OID::from_string("PKCS9.EmailAddress")) { ASN1_String email; @@ -145,7 +146,7 @@ cert_req_info.verify_end(); - if(auto ext = data->m_extensions.get_extension_object_as()) { + if(const auto* ext = data->m_extensions.get_extension_object_as()) { data->m_alt_name = ext->get_alt_name(); } @@ -223,47 +224,46 @@ * Return the key constraints (if any) */ Key_Constraints PKCS10_Request::constraints() const { - if(auto ext = extensions().get(OID::from_string("X509v3.KeyUsage"))) { - return dynamic_cast(*ext).get_constraints(); + if(const auto* ext = extensions().get_extension_object_as()) { + return ext->get_constraints(); + } else { + return Key_Constraints::None; } - - return Key_Constraints::None; } /* * Return the extendend key constraints (if any) */ std::vector PKCS10_Request::ex_constraints() const { - if(auto ext = extensions().get(OID::from_string("X509v3.ExtendedKeyUsage"))) { - return dynamic_cast(*ext).object_identifiers(); + if(const auto* ext = extensions().get_extension_object_as()) { + return ext->object_identifiers(); + } else { + return {}; } - - return {}; } /* * Return is a CA certificate is requested */ bool PKCS10_Request::is_CA() const { - if(auto ext = extensions().get(OID::from_string("X509v3.BasicConstraints"))) { - return dynamic_cast(*ext).get_is_ca(); + if(const auto* ext = extensions().get_extension_object_as()) { + return ext->is_ca(); + } else { + return false; } - - return false; } /* -* Return the desired path limit (if any) +* Return the requested path limit */ -size_t PKCS10_Request::path_limit() const { - if(auto ext = extensions().get(OID::from_string("X509v3.BasicConstraints"))) { - Cert_Extension::Basic_Constraints& basic_constraints = dynamic_cast(*ext); - if(basic_constraints.get_is_ca()) { - return basic_constraints.get_path_limit(); +std::optional PKCS10_Request::path_length_constraint() const { + if(const auto* ext = extensions().get_extension_object_as()) { + if(ext->is_ca()) { + return ext->path_length_constraint(); } } - return 0; + return std::nullopt; } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/x509/pkcs10.h botan3-3.12.0+dfsg/src/lib/x509/pkcs10.h --- botan3-3.7.1+dfsg/src/lib/x509/pkcs10.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/pkcs10.h 2026-05-07 01:38:28.000000000 +0000 @@ -15,7 +15,7 @@ namespace Botan { -struct PKCS10_Data; +class PKCS10_Data; class Private_Key; class Extensions; @@ -71,11 +71,22 @@ bool is_CA() const; /** - * Return the constraint on the path length defined - * in the BasicConstraints extension. + * Return the constraint on the path length defined in the BasicConstraints extension. + * + * Note this returns 0 if the extension is not set + * * @return path limit */ - size_t path_limit() const; + BOTAN_DEPRECATED("Use path_length_constraint") size_t path_limit() const { + return path_length_constraint().value_or(0); + } + + /** + * Return the constraint on the path length defined in the BasicConstraints extension. + * + * @return path limit (or nullopt if not set) + */ + std::optional path_length_constraint() const; /** * Get the challenge password for this request diff -Nru botan3-3.7.1+dfsg/src/lib/x509/pkix_enums.h botan3-3.12.0+dfsg/src/lib/x509/pkix_enums.h --- botan3-3.7.1+dfsg/src/lib/x509/pkix_enums.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/pkix_enums.h 2026-05-07 01:38:28.000000000 +0000 @@ -17,7 +17,9 @@ /** * Certificate validation status code */ -enum class Certificate_Status_Code { +enum class Certificate_Status_Code : uint16_t { + // TODO(Botan4) renumber this, e.g. Validation Errors -> IP_ADDR_BLOCKS_ERROR + // TODO(Botan4) rename variants to CamelCase OK = 0, VERIFIED = 0, @@ -54,6 +56,10 @@ CRL_HAS_EXPIRED = 2005, OCSP_IS_TOO_OLD = 2006, + // Revocation checks are skipped for chains which have an error more + // serious than this because they are anyway invalid + FIRST_ERROR_STATUS_TO_SKIP_REVOCATION = 3000, + // Chain generation problems CERT_ISSUER_NOT_FOUND = 3000, CANNOT_ESTABLISH_TRUST = 3001, @@ -67,6 +73,8 @@ CERT_CHAIN_TOO_LONG = 4002, CA_CERT_NOT_FOR_CERT_ISSUER = 4003, NAME_CONSTRAINT_ERROR = 4004, + IPADDR_BLOCKS_ERROR = 4011, + AS_BLOCKS_ERROR = 4012, // Revocation errors CA_CERT_NOT_FOR_CRL_ISSUER = 4005, @@ -84,6 +92,7 @@ EXT_IN_V1_V2_CERT = 4505, DUPLICATE_CERT_POLICY = 4506, V2_IDENTIFIERS_IN_V1_CERT = 4507, + EXTENSION_ENCODING_ERROR = 4508, // Hard failures CERT_IS_REVOKED = 5000, @@ -91,12 +100,13 @@ SIGNATURE_ERROR = 5002, CERT_PUBKEY_INVALID = 5003, SIGNATURE_ALGO_UNKNOWN = 5004, - SIGNATURE_ALGO_BAD_PARAMS = 5005 + SIGNATURE_ALGO_BAD_PARAMS = 5005, + EXCEEDED_SEARCH_LIMITS = 5006, }; /** * Convert a status code to a human readable diagnostic message -* @param code the certifcate status +* @param code the certificate status * @return string literal constant, or nullptr if code unknown */ BOTAN_PUBLIC_API(2, 0) const char* to_string(Certificate_Status_Code code); @@ -105,9 +115,9 @@ * X.509v3 Key Constraints. * If updating update copy in ffi.h */ -class BOTAN_PUBLIC_API(3, 0) Key_Constraints { +class BOTAN_PUBLIC_API(3, 0) Key_Constraints final { public: - enum Bits : uint32_t { + enum Bits : uint16_t /* NOLINT(*-use-enum-class) */ { None = 0, DigitalSignature = 1 << 15, NonRepudiation = 1 << 14, @@ -137,7 +147,9 @@ Key_Constraints(Key_Constraints&& other) = default; Key_Constraints& operator=(const Key_Constraints& other) = default; Key_Constraints& operator=(Key_Constraints&& other) = default; + ~Key_Constraints() = default; + // NOLINTNEXTLINE(*-explicit-conversions) Key_Constraints(Key_Constraints::Bits bits) : m_value(bits) {} explicit Key_Constraints(uint32_t bits) : m_value(bits) {} @@ -145,11 +157,16 @@ Key_Constraints() : m_value(0) {} /** - * Return typical constraints for a CA certificate, namely - * KeyCertSign and CrlSign + * Return typical constraints for a CA certificate. + * + * The reasons for KeyCertSign and CrlSign should be obvious + * + * CAB baseline requirements are that DigitalSignature should be set + * if the certificate is used to sign OCSP responses. */ static Key_Constraints ca_constraints() { - return Key_Constraints(Key_Constraints::KeyCertSign | Key_Constraints::CrlSign); + return Key_Constraints(Key_Constraints::KeyCertSign | Key_Constraints::CrlSign | + Key_Constraints::DigitalSignature); } bool operator==(const Key_Constraints&) const = default; @@ -184,7 +201,7 @@ /** * X.509v2 CRL Reason Code. */ -enum class CRL_Code : uint32_t { +enum class CRL_Code : uint8_t { Unspecified = 0, KeyCompromise = 1, CaCompromise = 2, @@ -197,6 +214,15 @@ AaCompromise = 10, }; +enum class Usage_Type : uint8_t { + UNSPECIFIED, // no restrictions + TLS_SERVER_AUTH, + TLS_CLIENT_AUTH, + CERTIFICATE_AUTHORITY, + OCSP_RESPONDER, + ENCRYPTION +}; + } // namespace Botan #endif diff -Nru botan3-3.7.1+dfsg/src/lib/x509/pkix_types.h botan3-3.12.0+dfsg/src/lib/x509/pkix_types.h --- botan3-3.7.1+dfsg/src/lib/x509/pkix_types.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/pkix_types.h 2026-05-07 01:38:28.000000000 +0000 @@ -13,10 +13,13 @@ #include -#include +#include +#include #include +#include #include #include +#include #include #include #include @@ -41,20 +44,36 @@ public: X509_DN() = default; + X509_DN(std::initializer_list> args) { + for(const auto& i : args) { + add_attribute(i.first, i.second); + } + } + + /** + * Since DN matching for Name Constraints requires preserving order and + * multimaps have sorted keys, this constructor is deprecated. + */ + BOTAN_DEPRECATED("Deprecated use initializer list constructor") explicit X509_DN(const std::multimap& args) { for(const auto& i : args) { add_attribute(i.first, i.second); } } + /** + * Since DN matching for Name Constraints requires preserving order and + * multimaps have sorted keys, this constructor is deprecated. + */ + BOTAN_DEPRECATED("Deprecated use initializer list constructor") explicit X509_DN(const std::multimap& args) { for(const auto& i : args) { add_attribute(i.first, i.second); } } - void encode_into(DER_Encoder&) const override; - void decode_from(BER_Decoder&) override; + void encode_into(DER_Encoder& to) const override; + void decode_from(BER_Decoder& from) override; bool has_field(const OID& oid) const; ASN1_String get_first_attribute(const OID& oid) const; @@ -72,6 +91,10 @@ std::string to_string() const; + /** + * Return the DN components as a vector. Note that the order of the components is + * preserved only when using the initializer list constructor. + */ const std::vector>& dn_info() const { return m_rdn; } std::multimap get_attributes() const; @@ -108,7 +131,7 @@ /* The ordering here is arbitrary and may change from release to release. -It is intended for allowing DNs as keys in std::map and similiar containers +It is intended for allowing DNs as keys in std::map and similar containers */ BOTAN_PUBLIC_API(2, 0) bool operator<(const X509_DN& dn1, const X509_DN& dn2); @@ -120,11 +143,11 @@ */ class BOTAN_PUBLIC_API(2, 0) AlternativeName final : public ASN1_Object { public: - void encode_into(DER_Encoder&) const override; - void decode_from(BER_Decoder&) override; + void encode_into(DER_Encoder& to) const override; + void decode_from(BER_Decoder& from) override; /// Create an empty name - AlternativeName() {} + AlternativeName() = default; /// Add a URI to this AlternativeName void add_uri(std::string_view uri); @@ -144,6 +167,12 @@ /// Add an IP address to this alternative name void add_ipv4_address(uint32_t ipv4); + /// Add an IP address to this alternative name + void add_ipv4_address(IPv4Address ipv4) { add_ipv4_address(ipv4.value()); } + + /// Add an IPv6 address to this alternative name + void add_ipv6_address(const IPv6Address& ipv6); + /// Return the set of URIs included in this alternative name const std::set& uris() const { return m_uri; } @@ -156,6 +185,9 @@ /// Return the set of IPv4 addresses included in this alternative name const std::set& ipv4_address() const { return m_ipv4_addr; } + /// Return the set of IPv6 addresses included in this alternative name + const std::set& ipv6_address() const { return m_ipv6_addr; } + /// Return the set of "other names" included in this alternative name BOTAN_DEPRECATED("Support for other names is deprecated") const std::set>& other_names() const { @@ -201,16 +233,17 @@ BOTAN_DEPRECATED("Use AlternativeName::directory_names") X509_DN dn() const; BOTAN_DEPRECATED("Use plain constructor plus add_{uri,dns,email,ipv4_address}") - AlternativeName(std::string_view email_addr, - std::string_view uri = "", - std::string_view dns = "", - std::string_view ip_address = ""); + BOTAN_FUTURE_EXPLICIT AlternativeName(std::string_view email_addr, + std::string_view uri = "", + std::string_view dns = "", + std::string_view ip_address = ""); private: std::set m_dns; std::set m_uri; std::set m_email; std::set m_ipv4_addr; + std::set m_ipv6_addr; std::set m_dn_names; std::set> m_othernames; }; @@ -245,14 +278,14 @@ * * Handles parsing GeneralName types in their BER and canonical string * encoding. Allows matching GeneralNames against each other using -* the rules laid out in the RFC 5280, sec. 4.2.1.10 (Name Contraints). +* the rules laid out in the RFC 5280, sec. 4.2.1.10 (Name Constraints). * * This entire class is deprecated and will be removed in a future * major release */ class BOTAN_PUBLIC_API(2, 0) GeneralName final : public ASN1_Object { public: - enum MatchResult : int { + enum MatchResult : uint8_t /* NOLINT(*-use-enum-class) */ { All, Some, None, @@ -267,15 +300,27 @@ URI = 3, DN = 4, IPv4 = 5, - Other = 6, + IPv6 = 6, + Other = 7, }; BOTAN_DEPRECATED("Deprecated use NameConstraints") GeneralName() = default; + static GeneralName email(std::string_view email); + static GeneralName dns(std::string_view dns); + static GeneralName uri(std::string_view uri); + static GeneralName directory_name(Botan::X509_DN dn); + static GeneralName ipv4_address(uint32_t ipv4); + static GeneralName ipv4_address(uint32_t ipv4, uint32_t mask); + static GeneralName ipv4_address(IPv4Address ipv4); + static GeneralName ipv4_address(const IPv4Subnet& subnet); + static GeneralName ipv6_address(const IPv6Address& ipv6); + static GeneralName ipv6_address(const IPv6Subnet& subnet); + // Encoding is not implemented - void encode_into(DER_Encoder&) const override; + void encode_into(DER_Encoder& to) const override; - void decode_from(BER_Decoder&) override; + void decode_from(BER_Decoder& from) override; /** * @return Type of the name expressed in this restriction @@ -293,6 +338,11 @@ BOTAN_DEPRECATED("Deprecated no replacement") std::string name() const; /** + * @return The name as binary string. Format depends on type. + */ + BOTAN_DEPRECATED("Deprecated no replacement") std::vector binary_name() const; + + /** * Checks whether a given certificate (partially) matches this name. * @param cert certificate to be matched * @return the match result @@ -301,20 +351,41 @@ bool matches_dns(const std::string& dns_name) const; bool matches_ipv4(uint32_t ip) const; + + bool matches_ipv4(IPv4Address ip) const { return matches_ipv4(ip.value()); } + + bool matches_ipv6(const IPv6Address& ip) const; bool matches_dn(const X509_DN& dn) const; private: + friend class NameConstraints; static constexpr size_t RFC822_IDX = 0; static constexpr size_t DNS_IDX = 1; static constexpr size_t URI_IDX = 2; static constexpr size_t DN_IDX = 3; static constexpr size_t IPV4_IDX = 4; + static constexpr size_t IPV6_IDX = 5; - NameType m_type; - std::variant> m_name; + using NameVariant = std::variant; + + GeneralName(NameType type, NameVariant name) : m_type(type), m_name(std::move(name)) {} + + template + requires(idx < 6) + static GeneralName make(T&& value) { + return {NameType(idx + 1 /* implicit enum relationship! */), + NameVariant(std::in_place_index_t(), std::forward(value))}; + } + + NameType m_type = NameType::Unknown; + NameVariant m_name; static bool matches_dns(std::string_view name, std::string_view constraint); + /** + * Partial DN matching according to RFC 5280, Section 7.1, i.e., + * whether the constraint is a prefix of the name. + */ static bool matches_dn(const X509_DN& name, const X509_DN& constraint); }; @@ -337,9 +408,9 @@ */ BOTAN_DEPRECATED("Deprecated use NameConstraints") GeneralSubtree(); - void encode_into(DER_Encoder&) const override; + void encode_into(DER_Encoder& to) const override; - void decode_from(BER_Decoder&) override; + void decode_from(BER_Decoder& from) override; /** * @return name @@ -362,7 +433,7 @@ /** * Creates an empty name NameConstraints. */ - NameConstraints() : m_permitted_subtrees(), m_excluded_subtrees() {} + NameConstraints() = default; /** * Creates NameConstraints from a list of permitted and excluded subtrees. @@ -407,7 +478,7 @@ /** * X.509 Certificate Extension */ -class BOTAN_PUBLIC_API(2, 0) Certificate_Extension { +class BOTAN_PUBLIC_API(2, 0) Certificate_Extension /* NOLINT(*-special-member-functions) */ { public: /** * @return OID representing this extension @@ -439,13 +510,14 @@ * an appropriate status code shall be added to cert_status. * * @param subject Subject certificate that contains this extension - * @param issuer Issuer certificate - * @param status Certificate validation status codes for subject certificate + * @param issuer Issuer certificate. nullopt for certificates with no + * available issuer (e.g. non self-signed trust anchors). * @param cert_path Certificate path which is currently validated + * @param cert_status Certificate validation status codes for subject certificate * @param pos Position of subject certificate in cert_path */ virtual void validate(const X509_Certificate& subject, - const X509_Certificate& issuer, + const std::optional& issuer, const std::vector& cert_path, std::vector>& cert_status, size_t pos); @@ -498,12 +570,18 @@ const std::vector& get_extension_oids() const { return m_extension_oids; } /** + * Return the set of critical extensions in the order they appeared in the extension list + * (This may be an empty vector) + */ + std::vector critical_extensions() const; + + /** * Return true if an extension was set */ bool extension_set(const OID& oid) const; /** - * Return true if an extesion was set and marked critical + * Return true if an extension was set and marked critical */ bool critical_extension_set(const OID& oid) const; @@ -513,8 +591,8 @@ */ std::vector get_extension_bits(const OID& oid) const; - void encode_into(DER_Encoder&) const override; - void decode_from(BER_Decoder&) override; + void encode_into(DER_Encoder& to) const override; + void decode_from(BER_Decoder& from) override; /** * Adds a new extension to the list. @@ -604,12 +682,14 @@ Extensions(Extensions&&) = default; Extensions& operator=(Extensions&&) = default; + ~Extensions() override = default; + private: static std::unique_ptr create_extn_obj(const OID& oid, bool critical, const std::vector& body); - class Extensions_Info { + class BOTAN_UNSTABLE_API Extensions_Info final { public: Extensions_Info(bool critical, std::unique_ptr ext) : m_obj(std::move(ext)), m_bits(m_obj->encode_inner()), m_critical(critical) {} @@ -623,10 +703,7 @@ const std::vector& bits() const { return m_bits; } - const Certificate_Extension& obj() const { - BOTAN_ASSERT_NONNULL(m_obj.get()); - return *m_obj; - } + const Certificate_Extension& obj() const; private: std::shared_ptr m_obj; diff -Nru botan3-3.7.1+dfsg/src/lib/x509/x509_ca.cpp botan3-3.12.0+dfsg/src/lib/x509/x509_ca.cpp --- botan3-3.7.1+dfsg/src/lib/x509/x509_ca.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/x509_ca.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,12 +7,13 @@ #include +#include +#include #include #include #include #include #include -#include namespace Botan { @@ -34,6 +35,9 @@ m_hash_fn = m_signer->hash_function(); } +X509_CA::X509_CA(X509_CA&&) noexcept = default; +X509_CA& X509_CA::operator=(X509_CA&&) noexcept = default; + X509_CA::~X509_CA() = default; Extensions X509_CA::choose_extensions(const PKCS10_Request& req, @@ -48,7 +52,8 @@ Extensions extensions = req.extensions(); - extensions.replace(std::make_unique(req.is_CA(), req.path_limit()), true); + extensions.replace(std::make_unique(req.is_CA(), req.path_length_constraint()), + true); if(!constraints.empty()) { extensions.replace(std::make_unique(constraints), true); @@ -113,7 +118,7 @@ const X509_DN& subject_dn, const Extensions& extensions) { const size_t SERIAL_BITS = 128; - BigInt serial_no(rng, SERIAL_BITS); + const BigInt serial_no(rng, SERIAL_BITS); return make_cert( signer, rng, serial_no, sig_algo, pub_key, not_before, not_after, issuer_dn, subject_dn, extensions); @@ -161,7 +166,7 @@ .end_cons() .end_explicit() .end_cons() - .get_contents() + .get_contents_unlocked() )); // clang-format on } @@ -186,7 +191,7 @@ X509_CRL X509_CA::new_crl(RandomNumberGenerator& rng, std::chrono::system_clock::time_point issue_time, std::chrono::seconds next_update) const { - std::vector empty; + const std::vector empty; return make_crl(empty, 1, rng, issue_time, next_update); } @@ -239,7 +244,7 @@ .end_cons() .end_explicit() .end_cons() - .get_contents()); + .get_contents_unlocked()); // clang-format on return X509_CRL(crl); diff -Nru botan3-3.7.1+dfsg/src/lib/x509/x509_ca.h botan3-3.12.0+dfsg/src/lib/x509/x509_ca.h --- botan3-3.7.1+dfsg/src/lib/x509/x509_ca.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/x509_ca.h 2026-05-07 01:38:28.000000000 +0000 @@ -233,8 +233,8 @@ X509_CA(const X509_CA&) = delete; X509_CA& operator=(const X509_CA&) = delete; - X509_CA(X509_CA&&) = default; - X509_CA& operator=(X509_CA&&) = default; + X509_CA(X509_CA&&) noexcept; + X509_CA& operator=(X509_CA&&) noexcept; ~X509_CA(); diff -Nru botan3-3.7.1+dfsg/src/lib/x509/x509_cert_cache.cpp botan3-3.12.0+dfsg/src/lib/x509/x509_cert_cache.cpp --- botan3-3.7.1+dfsg/src/lib/x509/x509_cert_cache.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/x509_cert_cache.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,57 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include + +namespace Botan { + +X509_Certificate_Cache::X509_Certificate_Cache(size_t max_entries) : m_max_entries(max_entries) {} + +X509_Certificate X509_Certificate_Cache::find_or_insert(std::span encoding) { + if(m_max_entries == 0) { + return X509_Certificate(encoding); + } + + // Hash the DER + auto sha256 = HashFunction::create_or_throw("SHA-256"); + DER_Hash hash; + sha256->update(encoding); + sha256->final(hash.m_hash); + + // Check for a cache hit + { + const lock_guard_type lock(m_mutex); + if(auto it = m_cache.find(hash); it != m_cache.end()) { + return it->second; + } + } + + // Deserialize the certificate + X509_Certificate cert(encoding); + + // Lock again + const lock_guard_type lock(m_mutex); + + // Check for a cache hit (possibly racing with another thread) + if(auto it = m_cache.find(hash); it != m_cache.end()) { + return it->second; + } + + // Evict if required + // + // Effectively this is just a random drop, might make sense to add LRU here + while(m_cache.size() >= m_max_entries) { + m_cache.erase(m_cache.begin()); + } + + // Add the newly deserialized cert to the cache + auto it = m_cache.emplace(hash, std::move(cert)).first; + return it->second; +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/x509/x509_cert_cache.h botan3-3.12.0+dfsg/src/lib/x509/x509_cert_cache.h --- botan3-3.7.1+dfsg/src/lib/x509/x509_cert_cache.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/x509_cert_cache.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,87 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_X509_CERT_CACHE_H_ +#define BOTAN_X509_CERT_CACHE_H_ + +#include + +#include +#include +#include +#include +#include + +namespace Botan { + +class HashFunction; + +/** +* A cache for X.509 certificates +* +* This is primarily useful for system certificate stores (Windows, macOS) +* where repeated lookups via native APIs return raw DER bytes that must +* be parsed each time. The cache deduplicates these by keying on the +* SHA-256 hash of the DER encoding. +*/ +class X509_Certificate_Cache final { + public: + /** + * @param max_entries maximum number of certificates to cache. + * When the cache is full, an entry is evicted to make room. + * If 0, caching is disabled entirely. + */ + explicit X509_Certificate_Cache(size_t max_entries = 64); + + /** + * Look up a certificate by its DER encoding, or parse and cache it. + * + * If a certificate with the same DER encoding (by SHA-256 hash) is + * already in the cache, returns a (cheap, shared_ptr-backed) copy. + * Otherwise, parses the DER encoding into an X509_Certificate, + * inserts it into the cache, and returns it. + * + * If the cache was constructed with max_entries == 0, always parses + * and never caches. + * + * @param encoding DER-encoded certificate + * @return the cached or newly parsed certificate + * @throws Decoding_Error if the encoding is not a valid certificate + */ + X509_Certificate find_or_insert(std::span encoding); + + private: + class DER_Hash final { + public: + static constexpr size_t LEN = 32; + + auto operator<=>(const DER_Hash&) const = default; + + size_t hash() const noexcept { + size_t h = 0; + std::memcpy(&h, m_hash.data(), sizeof(h)); + return h; + } + + private: + DER_Hash() : m_hash{} {} + + friend class X509_Certificate_Cache; + std::array m_hash; + }; + + struct DER_Hash_Fn { + size_t operator()(const DER_Hash& h) const noexcept { return h.hash(); } + }; + + size_t m_max_entries; + mutex_type m_mutex; + std::unordered_map m_cache; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/x509/x509_crl.cpp botan3-3.12.0+dfsg/src/lib/x509/x509_crl.cpp --- botan3-3.7.1+dfsg/src/lib/x509/x509_crl.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/x509_crl.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,26 +7,56 @@ #include +#include +#include #include +#include #include #include - -#include +#include +#include namespace Botan { -struct CRL_Data { +class CRL_Data final { + public: + CRL_Data(const X509_DN& issuer, + const X509_Time& this_update, + const X509_Time& next_update, + const std::vector& revoked) : + m_issuer(issuer), m_this_update(this_update), m_next_update(next_update), m_entries(revoked) { + this->update_index(); + } + + CRL_Data() = default; + + void update_index() { + m_revoked_serials.clear(); + for(const auto& entry : m_entries) { + if(entry.reason_code() == CRL_Code::RemoveFromCrl) { + m_revoked_serials.erase(entry.serial_number()); + } else { + m_revoked_serials.insert(entry.serial_number()); + } + } + } + + // NOLINTBEGIN(*non-private-member-variables-in-classes) X509_DN m_issuer; - size_t m_version; + size_t m_version{}; X509_Time m_this_update; X509_Time m_next_update; std::vector m_entries; Extensions m_extensions; + // cached values from entries + std::set> m_revoked_serials; + // cached values from extensions size_t m_crl_number = 0; std::vector m_auth_key_id; std::vector m_idp_urls; + // NOLINTEND(*non-private-member-variables-in-classes) }; std::string X509_CRL::PEM_label() const { @@ -56,13 +86,8 @@ X509_CRL::X509_CRL(const X509_DN& issuer, const X509_Time& this_update, const X509_Time& next_update, - const std::vector& revoked) : - X509_Object() { - m_data = std::make_shared(); - m_data->m_issuer = issuer; - m_data->m_this_update = this_update; - m_data->m_next_update = next_update; - m_data->m_entries = revoked; + const std::vector& revoked) { + m_data = std::make_shared(issuer, this_update, next_update, revoked); } /** @@ -77,7 +102,7 @@ return false; } - std::vector crl_akid = authority_key_id(); + const std::vector crl_akid = authority_key_id(); const std::vector& cert_akid = cert.authority_key_id(); if(!crl_akid.empty() && !cert_akid.empty()) { @@ -86,22 +111,7 @@ } } - const std::vector& cert_serial = cert.serial_number(); - - bool is_revoked = false; - - // FIXME would be nice to avoid a linear scan here - maybe sort the entries? - for(const CRL_Entry& entry : get_revoked()) { - if(cert_serial == entry.serial_number()) { - if(entry.reason_code() == CRL_Code::RemoveFromCrl) { - is_revoked = false; - } else { - is_revoked = true; - } - } - } - - return is_revoked; + return data().m_revoked_serials.contains(cert.serial_number()); } /* @@ -112,7 +122,7 @@ std::unique_ptr decode_crl_body(const std::vector& body, const AlgorithmIdentifier& sig_algo) { auto data = std::make_unique(); - BER_Decoder tbs_crl(body); + BER_Decoder tbs_crl(body, BER_Decoder::Limits::DER()); tbs_crl.decode_optional(data->m_version, ASN1_Type::Integer, ASN1_Class::Universal); data->m_version += 1; // wire-format is 0-based @@ -128,12 +138,26 @@ throw Decoding_Error("Algorithm identifier mismatch in CRL"); } - tbs_crl.decode(data->m_issuer).decode(data->m_this_update).decode(data->m_next_update); + tbs_crl.decode(data->m_issuer).decode(data->m_this_update); + + // According to RFC 5280 Section 5.1, nextUpdate is OPTIONAL and may be + // encoded as either a UTCTime or a GeneralizedTime. Section 5.1.2.5 + // further states that "[c]onforming CRL issuers MUST include the nextUpdate + // field in all CRLs". Obviously, not everyone complies... + // + // See https://github.com/randombit/botan/issues/4722 for more details. + { + const auto& next_update = tbs_crl.peek_next_object(); + if(next_update.is_a(ASN1_Type::UtcTime, ASN1_Class::Universal) || + next_update.is_a(ASN1_Type::GeneralizedTime, ASN1_Class::Universal)) { + tbs_crl.decode(data->m_next_update); + } + } BER_Object next = tbs_crl.get_next_object(); if(next.is_a(ASN1_Type::Sequence, ASN1_Class::Constructed)) { - BER_Decoder cert_list(std::move(next)); + BER_Decoder cert_list(next, tbs_crl.limits()); while(cert_list.more_items()) { CRL_Entry entry; @@ -144,7 +168,7 @@ } if(next.is_a(0, ASN1_Class::Constructed | ASN1_Class::ContextSpecific)) { - BER_Decoder crl_options(std::move(next)); + BER_Decoder crl_options(next, tbs_crl.limits()); crl_options.decode(data->m_extensions).verify_end(); next = tbs_crl.get_next_object(); } @@ -156,16 +180,20 @@ tbs_crl.verify_end(); // Now cache some fields from the extensions - if(auto ext = data->m_extensions.get_extension_object_as()) { + if(const auto* ext = data->m_extensions.get_extension_object_as()) { data->m_crl_number = ext->get_crl_number(); } - if(auto ext = data->m_extensions.get_extension_object_as()) { + if(const auto* ext = data->m_extensions.get_extension_object_as()) { data->m_auth_key_id = ext->get_key_id(); } - if(auto ext = data->m_extensions.get_extension_object_as()) { - data->m_idp_urls = ext->get_point().get_attribute("URL"); + if(const auto* ext = data->m_extensions.get_extension_object_as()) { + for(const auto& uri : ext->get_point().uris()) { + data->m_idp_urls.push_back(uri); + } } + data->update_index(); + return data; } @@ -249,4 +277,37 @@ return data().m_idp_urls; } +namespace { + +/* +* Compare two distribution point names for overlap, per RFC 5280 section 6.3.3 +* step (b)(2). In practice CRLDP/IDP general names are either uniformResourceIdentifier +* or directoryName; the other GeneralName variants have no defined semantics for a +* distribution point (RFC 5280 4.2.1.13 and 5.2.5) so they are ignored here. +*/ +bool dp_names_overlap(const AlternativeName& a, const AlternativeName& b) { + auto has_common = [](const auto& s1, const auto& s2) { + return std::ranges::any_of(s1, [&](const auto& e) { return s2.contains(e); }); + }; + + return has_common(a.uris(), b.uris()) || has_common(a.directory_names(), b.directory_names()); +} + +} // namespace + +bool X509_CRL::has_matching_distribution_point(const X509_Certificate& cert) const { + const auto* cdp_ext = cert.v3_extensions().get_extension_object_as(); + if(cdp_ext == nullptr || cdp_ext->distribution_points().empty()) { + return true; + } + + const auto* idp_ext = this->extensions().get_extension_object_as(); + if(idp_ext == nullptr) { + return false; + } + + return std::ranges::any_of(cdp_ext->distribution_points(), + [&](const auto& dp) { return dp_names_overlap(dp.point(), idp_ext->get_point()); }); +} + } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/x509/x509_crl.h botan3-3.12.0+dfsg/src/lib/x509/x509_crl.h --- botan3-3.7.1+dfsg/src/lib/x509/x509_crl.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/x509_crl.h 2026-05-07 01:38:28.000000000 +0000 @@ -11,6 +11,7 @@ #include #include #include +#include #include namespace Botan { @@ -19,16 +20,16 @@ class X509_Certificate; class X509_DN; -struct CRL_Entry_Data; -struct CRL_Data; +class CRL_Entry_Data; +class CRL_Data; /** * This class represents CRL entries */ class BOTAN_PUBLIC_API(2, 0) CRL_Entry final : public ASN1_Object { public: - void encode_into(DER_Encoder&) const override; - void decode_from(BER_Decoder&) override; + void encode_into(DER_Encoder& to) const override; + void decode_from(BER_Decoder& from) override; /** * Get the serial number of the certificate associated with this entry. @@ -63,25 +64,25 @@ * @param cert the certificate to revoke * @param reason the reason code to set in the entry */ - CRL_Entry(const X509_Certificate& cert, CRL_Code reason = CRL_Code::Unspecified); + BOTAN_FUTURE_EXPLICIT CRL_Entry(const X509_Certificate& cert, CRL_Code reason = CRL_Code::Unspecified); private: friend class X509_CRL; const CRL_Entry_Data& data() const; - std::shared_ptr m_data; + std::shared_ptr m_data; }; /** * Test two CRL entries for equality in all fields. */ -BOTAN_PUBLIC_API(2, 0) bool operator==(const CRL_Entry&, const CRL_Entry&); +BOTAN_PUBLIC_API(2, 0) bool operator==(const CRL_Entry& lhs, const CRL_Entry& rhs); /** * Test two CRL entries for inequality in at least one field. */ -BOTAN_PUBLIC_API(2, 0) bool operator!=(const CRL_Entry&, const CRL_Entry&); +BOTAN_PUBLIC_API(2, 0) bool operator!=(const CRL_Entry& lhs, const CRL_Entry& rhs); /** * This class represents X.509 Certificate Revocation Lists (CRLs). @@ -136,7 +137,14 @@ /** * Get the CRL's nextUpdate value. - * @return CRLs nextdUpdate + * + * Technically nextUpdate is optional in the X.509 spec and may be omitted, + * despite RFC 5280 requiring it. If the nextUpdate field is not set, this + * will return a time object with time_is_set() returning false. + * + * TODO(Botan4) return a `const std::optional&` instead + * + * @return CRLs nextUpdate */ const X509_Time& next_update() const; @@ -153,6 +161,25 @@ std::vector issuing_distribution_points() const; /** + * Check if this CRL's scope covers the given certificate's CRL distribution points. + * + * Per RFC 5280 6.3.3 step (b)(2), if the certificate has a CRL Distribution Points + * extension (4.2.1.13) and this CRL has an Issuing Distribution Point extension + * (5.2.5), at least one general name from the IDP must match a general name in one + * of the certificate's distribution points. + * + * Returns true if the certificate has no CRLDP extension (this CRL's scope is + * unconstrained from the certificate's perspective), or if both extensions are + * present and their distribution point names overlap. Returns false otherwise, + * including when the certificate has a CRLDP but this CRL has no IDP. + * + * The nameRelativeToCRLIssuer RDN form of DistributionPointName is not currently + * parsed by Botan's CRLDP/IDP decoders, so this comparison operates only on the + * fullName (GeneralNames) form. + */ + bool has_matching_distribution_point(const X509_Certificate& cert) const; + + /** * Create an uninitialized CRL object. Any attempts to access * this object will throw an exception. */ @@ -162,21 +189,21 @@ * Construct a CRL from a data source. * @param source the data source providing the DER or PEM encoded CRL. */ - X509_CRL(DataSource& source); + BOTAN_FUTURE_EXPLICIT X509_CRL(DataSource& source); #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) /** * Construct a CRL from a file containing the DER or PEM encoded CRL. * @param filename the name of the CRL file */ - X509_CRL(std::string_view filename); + BOTAN_FUTURE_EXPLICIT X509_CRL(std::string_view filename); #endif /** * Construct a CRL from a binary vector * @param vec the binary (DER) representation of the CRL */ - X509_CRL(const std::vector& vec); + BOTAN_FUTURE_EXPLICIT X509_CRL(const std::vector& vec); /** * Construct a CRL @@ -199,7 +226,7 @@ const CRL_Data& data() const; - std::shared_ptr m_data; + std::shared_ptr m_data; }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/x509/x509_dn.cpp botan3-3.12.0+dfsg/src/lib/x509/x509_dn.cpp --- botan3-3.7.1+dfsg/src/lib/x509/x509_dn.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/x509_dn.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,10 +7,10 @@ #include +#include #include #include -#include -#include +#include #include #include @@ -18,75 +18,84 @@ namespace { -namespace { - -bool caseless_cmp(char a, char b) { - return (std::tolower(static_cast(a)) == std::tolower(static_cast(b))); -} - bool is_space(char c) { - return std::isspace(static_cast(c)); + return c == ' ' || c == '\t'; } -} // namespace - /* -* X.500 String Comparison +* Yields the X.500 canonical form of a name component one character at a time */ -bool x500_name_cmp(std::string_view name1, std::string_view name2) { - auto p1 = name1.begin(); - auto p2 = name2.begin(); - - while((p1 != name1.end()) && is_space(*p1)) { - ++p1; - } - while((p2 != name2.end()) && is_space(*p2)) { - ++p2; - } - - while(p1 != name1.end() && p2 != name2.end()) { - if(is_space(*p1)) { - if(!is_space(*p2)) { - return false; +class X500_Char_Iterator final { + public: + explicit X500_Char_Iterator(std::string_view s) : m_str(s), m_pos(0) { + // Skip leading whitespace + while(m_pos < m_str.size() && is_space(m_str[m_pos])) { + ++m_pos; } + } - while((p1 != name1.end()) && is_space(*p1)) { - ++p1; - } - while((p2 != name2.end()) && is_space(*p2)) { - ++p2; + // Returns next canonical character, or nullopt when exhausted. + std::optional next() { + if(m_pos >= m_str.size()) { + return std::nullopt; } - if(p1 == name1.end() && p2 == name2.end()) { - return true; + if(is_space(m_str[m_pos])) { + // Skip the entire whitespace run + while(m_pos < m_str.size() && is_space(m_str[m_pos])) { + ++m_pos; + } + // Emit a single space only if more content follows (strip trailing ws) + if(m_pos < m_str.size()) { + return ' '; + } + return std::nullopt; } - if(p1 == name1.end() || p2 == name2.end()) { - return false; + + const char c = m_str[m_pos++]; + // Locale-independent ASCII fold; RFC 5280 DN matching does not depend on libc locale + if(c >= 'A' && c <= 'Z') { + return static_cast(c + ('a' - 'A')); } + return c; } - if(!caseless_cmp(*p1, *p2)) { - return false; + static std::string canonicalize(std::string_view name) { + std::string result; + result.reserve(name.size()); + + X500_Char_Iterator it(name); + while(auto c = it.next()) { + result += *c; + } + + return result; } - ++p1; - ++p2; - } - while((p1 != name1.end()) && is_space(*p1)) { - ++p1; - } - while((p2 != name2.end()) && is_space(*p2)) { - ++p2; - } + private: + std::string_view m_str; + size_t m_pos; +}; - if((p1 != name1.end()) || (p2 != name2.end())) { - return false; +} // namespace + +bool x500_name_cmp(std::string_view name1, std::string_view name2) { + X500_Char_Iterator it1(name1); + X500_Char_Iterator it2(name2); + + while(true) { + const auto c1 = it1.next(); + const auto c2 = it2.next(); + + if(c1 != c2) { + return false; + } + if(!c1.has_value() && !c2.has_value()) { + return true; + } } - return true; } -} // namespace - /* * Add an attribute to a X509_DN */ @@ -112,7 +121,7 @@ std::multimap X509_DN::get_attributes() const { std::multimap retval; - for(auto& i : m_rdn) { + for(const auto& i : m_rdn) { retval.emplace(i.first, i.second.value()); } return retval; @@ -124,7 +133,7 @@ std::multimap X509_DN::contents() const { std::multimap retval; - for(auto& i : m_rdn) { + for(const auto& i : m_rdn) { retval.emplace(i.first.to_formatted_string(), i.second.value()); } return retval; @@ -142,7 +151,7 @@ } bool X509_DN::has_field(const OID& oid) const { - for(auto& i : m_rdn) { + for(const auto& i : m_rdn) { if(i.first == oid) { return true; } @@ -157,7 +166,7 @@ } ASN1_String X509_DN::get_first_attribute(const OID& oid) const { - for(auto& i : m_rdn) { + for(const auto& i : m_rdn) { if(i.first == oid) { return i.second; } @@ -174,7 +183,7 @@ std::vector values; - for(auto& i : m_rdn) { + for(const auto& i : m_rdn) { if(i.first == oid) { values.push_back(i.second.value()); } @@ -265,11 +274,8 @@ auto attr2 = dn2.get_attributes(); // If they are not the same size, choose the smaller as the "lessor" - if(attr1.size() < attr2.size()) { - return true; - } - if(attr1.size() > attr2.size()) { - return false; + if(attr1.size() != attr2.size()) { + return attr1.size() < attr2.size(); } // We know they are the same # of elements, now compare the OIDs: @@ -281,27 +287,12 @@ return (p1->first < p2->first); } - ++p1; - ++p2; - } - - // We know this is true because maps have the same size - BOTAN_ASSERT_NOMSG(p1 == attr1.end()); - BOTAN_ASSERT_NOMSG(p2 == attr2.end()); - - // Now we know all elements have the same OIDs, compare - // their string values: - - p1 = attr1.begin(); - p2 = attr2.begin(); - while(p1 != attr1.end() && p2 != attr2.end()) { - BOTAN_DEBUG_ASSERT(p1->first == p2->first); - - // They may be binary different but same by X.500 rules, check this - if(!x500_name_cmp(p1->second, p2->second)) { - // If they are not (by X.500) the same string, pick the - // lexicographic first as the lessor - return (p1->second < p2->second); + // If they are not (by X.500) the same string, pick the + // lexicographic first as the lessor + const std::string c1 = X500_Char_Iterator::canonicalize(p1->second); + const std::string c2 = X500_Char_Iterator::canonicalize(p2->second); + if(c1 != c2) { + return c1 < c2; } ++p1; @@ -349,7 +340,7 @@ source.start_sequence().raw_bytes(bits).end_cons(); - BER_Decoder sequence(bits); + BER_Decoder sequence(bits, source.limits()); m_rdn.clear(); @@ -410,7 +401,7 @@ for(size_t i = 0; i != info.size(); ++i) { out << to_short_form(info[i].first) << "=\""; - for(char c : info[i].second.value()) { + for(const char c : info[i].second.value()) { if(c == '\\' || c == '\"') { out << "\\"; } @@ -427,17 +418,18 @@ std::istream& operator>>(std::istream& in, X509_DN& dn) { in >> std::noskipws; + // NOLINTNEXTLINE(*-avoid-do-while) do { std::string key; std::string val; - char c; + char c = 0; while(in.good()) { in >> c; - if(std::isspace(c) && key.empty()) { + if(is_space(c) && key.empty()) { continue; - } else if(!std::isspace(c)) { + } else if(!is_space(c)) { key.push_back(c); break; } else { @@ -448,7 +440,7 @@ while(in.good()) { in >> c; - if(!std::isspace(c) && c != '=') { + if(!is_space(c) && c != '=') { key.push_back(c); } else if(c == '=') { break; @@ -461,7 +453,7 @@ while(in.good()) { in >> c; - if(std::isspace(c)) { + if(is_space(c)) { if(!in_quotes && !val.empty()) { break; } else if(in_quotes) { diff -Nru botan3-3.7.1+dfsg/src/lib/x509/x509_dn_ub.cpp botan3-3.12.0+dfsg/src/lib/x509/x509_dn_ub.cpp --- botan3-3.7.1+dfsg/src/lib/x509/x509_dn_ub.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/x509_dn_ub.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,10 +1,6 @@ /* -* DN_UB maps: Upper bounds on the length of DN strings -* -* This file was automatically generated by ./src/scripts/dev_tools/gen_oids.py on 2023-05-29 -* -* All manual edits to this file will be lost. Edit the script -* then regenerate this source file. +* (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity +* 2025 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -12,46 +8,72 @@ #include #include -#include +#include namespace Botan { -namespace { - -/** - * Upper bounds for the length of distinguished name fields as given in RFC 5280, Appendix A. - * Only OIDS recognized by botan are considered, so far. - * Maps OID string representations instead of human readable strings in order - * to avoid an additional lookup. - */ -const std::map DN_UB = { - {OID({2, 5, 4, 10}), 64}, // X520.Organization - {OID({2, 5, 4, 11}), 64}, // X520.OrganizationalUnit - {OID({2, 5, 4, 12}), 64}, // X520.Title - {OID({2, 5, 4, 3}), 64}, // X520.CommonName - {OID({2, 5, 4, 4}), 40}, // X520.Surname - {OID({2, 5, 4, 42}), 32768}, // X520.GivenName - {OID({2, 5, 4, 43}), 32768}, // X520.Initials - {OID({2, 5, 4, 44}), 32768}, // X520.GenerationalQualifier - {OID({2, 5, 4, 46}), 64}, // X520.DNQualifier - {OID({2, 5, 4, 5}), 64}, // X520.SerialNumber - {OID({2, 5, 4, 6}), 3}, // X520.Country - {OID({2, 5, 4, 65}), 128}, // X520.Pseudonym - {OID({2, 5, 4, 7}), 128}, // X520.Locality - {OID({2, 5, 4, 8}), 128}, // X520.State - {OID({2, 5, 4, 9}), 128} // X520.StreetAddress -}; - -} // namespace - //static size_t X509_DN::lookup_ub(const OID& oid) { - auto ub_entry = DN_UB.find(oid); - if(ub_entry != DN_UB.end()) { - return ub_entry->second; - } else { - return 0; + /* + * See RFC 5280 Appendix A.1 starting with comment "-- Upper Bounds" + */ + + // NOLINTBEGIN(*-branch-clone) + if(auto iso_dn = is_sub_element_of(oid, {2, 5, 4})) { + switch(*iso_dn) { + case 3: + // X520.CommonName + return 64; + case 4: + // X520.Surname + return 40; + case 5: + // X520.SerialNumber + return 64; + case 6: + // X520.Country + return 3; + case 7: + // X520.Locality + return 128; + case 8: + // X520.State + return 128; + case 9: + // X520.StreetAddress + return 128; + case 10: + // X520.Organization + return 64; + case 11: + // X520.OrganizationalUnit + return 64; + case 12: + // X520.Title + return 64; + case 42: + // X520.GivenName + return 16; + case 43: + // X520.Initials + return 5; + case 44: + // X520.GenerationalQualifier + return 3; + case 46: + // X520.DNQualifier + return 64; + case 65: + // X520.Pseudonym + return 128; + default: + return 0; + } } + + // NOLINTEND(*-branch-clone) + + return 0; } } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/x509/x509_ext.cpp botan3-3.12.0+dfsg/src/lib/x509/x509_ext.cpp --- botan3-3.7.1+dfsg/src/lib/x509/x509_ext.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/x509_ext.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -3,86 +3,87 @@ * (C) 1999-2010,2012 Jack Lloyd * (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity * (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity +* (C) 2024 Anton Einax, Dominik Schricker * * Botan is released under the Simplified BSD License (see license.txt) */ #include +#include #include #include #include #include #include #include +#include #include +#include #include #include -#include namespace Botan { namespace { -std::unique_ptr extension_from_oid(const OID& oid) { - if(oid == Cert_Extension::Subject_Key_ID::static_oid()) { - return std::make_unique(); - } - - if(oid == Cert_Extension::Key_Usage::static_oid()) { - return std::make_unique(); - } - - if(oid == Cert_Extension::Subject_Alternative_Name::static_oid()) { - return std::make_unique(); - } - - if(oid == Cert_Extension::Issuer_Alternative_Name::static_oid()) { - return std::make_unique(); - } - - if(oid == Cert_Extension::Basic_Constraints::static_oid()) { - return std::make_unique(); - } - - if(oid == Cert_Extension::CRL_Number::static_oid()) { - return std::make_unique(); - } - - if(oid == Cert_Extension::CRL_ReasonCode::static_oid()) { - return std::make_unique(); - } - - if(oid == Cert_Extension::Authority_Key_ID::static_oid()) { - return std::make_unique(); - } - - if(oid == Cert_Extension::Name_Constraints::static_oid()) { - return std::make_unique(); - } - - if(oid == Cert_Extension::CRL_Distribution_Points::static_oid()) { - return std::make_unique(); - } - - if(oid == Cert_Extension::CRL_Issuing_Distribution_Point::static_oid()) { - return std::make_unique(); - } +constexpr size_t MaximumKeyIdentifierLength = 64; - if(oid == Cert_Extension::Certificate_Policies::static_oid()) { - return std::make_unique(); - } +template T> +auto make_extension([[maybe_unused]] const OID& oid) { + BOTAN_DEBUG_ASSERT(oid == T::static_oid()); + return std::make_unique(); +} - if(oid == Cert_Extension::Extended_Key_Usage::static_oid()) { - return std::make_unique(); +std::unique_ptr extension_from_oid(const OID& oid) { + if(auto iso_ext = is_sub_element_of(oid, {2, 5, 29})) { + // NOLINTNEXTLINE(*-switch-missing-default-case) + switch(*iso_ext) { + case 14: + return make_extension(oid); + case 15: + return make_extension(oid); + case 17: + return make_extension(oid); + case 18: + return make_extension(oid); + case 19: + return make_extension(oid); + case 20: + return make_extension(oid); + case 21: + return make_extension(oid); + case 28: + return make_extension(oid); + case 30: + return make_extension(oid); + case 31: + return make_extension(oid); + case 32: + return make_extension(oid); + case 35: + return make_extension(oid); + case 37: + return make_extension(oid); + } } - if(oid == Cert_Extension::Authority_Information_Access::static_oid()) { - return std::make_unique(); + if(auto pkix_ext = is_sub_element_of(oid, {1, 3, 6, 1, 5, 5, 7, 1})) { + // NOLINTNEXTLINE(*-switch-missing-default-case) + switch(*pkix_ext) { + case 1: + return make_extension(oid); + case 7: + return make_extension(oid); + case 8: + return make_extension(oid); + case 26: + return make_extension(oid); + } } - if(oid == Cert_Extension::TNAuthList::static_oid()) { - return std::make_unique(); + if(oid == Cert_Extension::OCSP_NoCheck::static_oid()) { + return make_extension(oid); } return nullptr; // unknown @@ -90,7 +91,7 @@ bool is_valid_telephone_number(const ASN1_String& tn) { //TelephoneNumber ::= IA5String (SIZE (1..15)) (FROM ("0123456789#*")) - static std::string valid_tn_chars("0123456789#*"); + const std::string valid_tn_chars("0123456789#*"); if(tn.empty() || (tn.size() > 15)) { return false; @@ -105,6 +106,20 @@ } // namespace +std::vector Extensions::critical_extensions() const { + std::vector crit; + + for(const auto& oid : m_extension_oids) { + auto ext_info = m_extension_info.find(oid); + BOTAN_ASSERT_NOMSG(ext_info != m_extension_info.end()); + if(ext_info->second.is_critical()) { + crit.push_back(oid); + } + } + + return crit; +} + /* * Create a Certificate_Extension object of some kind to handle */ @@ -116,22 +131,31 @@ if(!extn) { // some other unknown extension type extn = std::make_unique(oid, critical); + } else { + try { + extn->decode_inner(body); + return extn; + } catch(const Exception&) { + // OID was recognized but contents failed to decode + extn = std::make_unique(oid, critical, /*failed_to_decode=*/true); + } } - try { - extn->decode_inner(body); - } catch(Decoding_Error&) { - extn = std::make_unique(oid, critical); - extn->decode_inner(body); - } + // This is always Unknown_Extension: + extn->decode_inner(body); return extn; } +const Certificate_Extension& Extensions::Extensions_Info::obj() const { + BOTAN_ASSERT_NONNULL(m_obj.get()); + return *m_obj; +} + /* * Validate the extension (the default implementation is a NOP) */ void Certificate_Extension::validate(const X509_Certificate& /*unused*/, - const X509_Certificate& /*unused*/, + const std::optional& /*unused*/, const std::vector& /*unused*/, std::vector>& /*unused*/, size_t /*unused*/) {} @@ -185,7 +209,7 @@ } bool Extensions::extension_set(const OID& oid) const { - return (m_extension_info.find(oid) != m_extension_info.end()); + return m_extension_info.contains(oid); } bool Extensions::critical_extension_set(const OID& oid) const { @@ -242,17 +266,16 @@ * Encode an Extensions list */ void Extensions::encode_into(DER_Encoder& to_object) const { - for(const auto& ext_info : m_extension_info) { - const OID& oid = ext_info.first; - const bool should_encode = ext_info.second.obj().should_encode(); + for(const auto& [oid, extn] : m_extension_info) { + const bool should_encode = extn.obj().should_encode(); if(should_encode) { - const bool is_critical = ext_info.second.is_critical(); - const std::vector& ext_value = ext_info.second.bits(); + const auto is_critical = extn.is_critical() ? std::optional{true} : std::nullopt; + const std::vector& ext_value = extn.bits(); to_object.start_sequence() .encode(oid) - .encode_optional(is_critical, false) + .encode_optional(is_critical) .encode(ext_value, ASN1_Type::OctetString) .end_cons(); } @@ -270,7 +293,7 @@ while(sequence.more_items()) { OID oid; - bool critical; + bool critical = false; std::vector bits; sequence.start_sequence() @@ -282,22 +305,39 @@ auto obj = create_extn_obj(oid, critical, bits); Extensions_Info info(critical, bits, std::move(obj)); + // RFC 5280 4.2: "A certificate MUST NOT include more than one + // instance of a particular extension." + if(!m_extension_info.emplace(oid, info).second) { + throw Decoding_Error("Duplicate certificate extension encountered"); + } m_extension_oids.push_back(oid); - m_extension_info.emplace(oid, info); } sequence.verify_end(); } namespace Cert_Extension { +Basic_Constraints::Basic_Constraints(bool is_ca, size_t path_length_constraint) : + Basic_Constraints(is_ca, is_ca ? std::optional(path_length_constraint) : std::nullopt) {} + +Basic_Constraints::Basic_Constraints(bool is_ca, std::optional path_length_constraint) : + m_is_ca(is_ca), m_path_length_constraint(path_length_constraint) { + if(!m_is_ca && m_path_length_constraint.has_value()) { + // RFC 5280 Sec 4.2.1.9 "CAs MUST NOT include the pathLenConstraint field unless the cA boolean is asserted" + throw Invalid_Argument( + "Basic_Constraints nonsensical to set a path length constraint for a non-CA basicConstraints"); + } +} + /* -* Checked accessor for the path_limit member +* Checked accessor for the path_length_constraint member */ size_t Basic_Constraints::get_path_limit() const { - if(!m_is_ca) { + if(m_is_ca) { + return m_path_length_constraint.value_or(NO_CERT_PATH_LIMIT); + } else { throw Invalid_State("Basic_Constraints::get_path_limit: Not a CA"); } - return m_path_limit; } /* @@ -305,10 +345,13 @@ */ std::vector Basic_Constraints::encode_inner() const { std::vector output; - DER_Encoder(output) - .start_sequence() - .encode_if(m_is_ca, DER_Encoder().encode(m_is_ca).encode_optional(m_path_limit, NO_CERT_PATH_LIMIT)) - .end_cons(); + + if(m_is_ca) { + DER_Encoder(output).start_sequence().encode(m_is_ca).encode_optional(m_path_length_constraint).end_cons(); + } else { + DER_Encoder(output).start_sequence().end_cons(); + } + return output; } @@ -316,14 +359,25 @@ * Decode the extension */ void Basic_Constraints::decode_inner(const std::vector& in) { - BER_Decoder(in) + /* + * RFC 5280 Section 4.2.1.9 + * + * BasicConstraints ::= SEQUENCE { + * cA BOOLEAN DEFAULT FALSE, + * pathLenConstraint INTEGER (0..MAX) OPTIONAL } + */ + BER_Decoder(in, BER_Decoder::Limits::DER()) .start_sequence() .decode_optional(m_is_ca, ASN1_Type::Boolean, ASN1_Class::Universal, false) - .decode_optional(m_path_limit, ASN1_Type::Integer, ASN1_Class::Universal, NO_CERT_PATH_LIMIT) - .end_cons(); + .decode_optional(m_path_length_constraint, ASN1_Type::Integer, ASN1_Class::Universal) + .end_cons() + .verify_end(); - if(m_is_ca == false) { - m_path_limit = 0; + /* RFC 5280 Section 4.2.1.9: + * "CAs MUST NOT include the pathLenConstraint field unless the cA boolean + * is asserted and the key usage extension asserts the keyCertSign bit" */ + if(!m_is_ca && m_path_length_constraint.has_value()) { + throw Decoding_Error("BasicConstraints pathLenConstraint must not be present when cA is FALSE"); } } @@ -342,9 +396,9 @@ der.push_back(static_cast(ASN1_Type::BitString)); der.push_back(2 + ((unused_bits < 8) ? 1 : 0)); der.push_back(unused_bits % 8); - der.push_back((constraint_bits >> 8) & 0xFF); - if(constraint_bits & 0xFF) { - der.push_back(constraint_bits & 0xFF); + der.push_back(static_cast((constraint_bits >> 8) & 0xFF)); + if((constraint_bits & 0xFF) != 0) { + der.push_back(static_cast(constraint_bits & 0xFF)); } return der; @@ -354,33 +408,33 @@ * Decode the extension */ void Key_Usage::decode_inner(const std::vector& in) { - BER_Decoder ber(in); - - BER_Object obj = ber.get_next_object(); - - obj.assert_is_a(ASN1_Type::BitString, ASN1_Class::Universal, "usage constraint"); - - if(obj.length() == 2 || obj.length() == 3) { - uint16_t usage = 0; - - const uint8_t* bits = obj.bits(); - - if(bits[0] >= 8) { - throw BER_Decoding_Error("Invalid unused bits in usage constraint"); + /* RFC 5280 Section 4.2.1.3 - KeyUsage ::= BIT STRING */ + std::vector bits; + BER_Decoder(in, BER_Decoder::Limits::DER()) + .decode(bits, ASN1_Type::BitString, ASN1_Type::BitString, ASN1_Class::Universal) + .verify_end(); + + const uint16_t usage = [&bits]() -> uint16_t { + switch(bits.size()) { + case 0: + return 0; + case 1: + return make_uint16(bits[0], 0); + case 2: + return make_uint16(bits[0], bits[1]); + default: + throw Decoding_Error("Invalid KeyUsage bitstring encoding"); } + }(); - const uint8_t mask = static_cast(0xFF << bits[0]); - - if(obj.length() == 2) { - usage = make_uint16(bits[1] & mask, 0); - } else if(obj.length() == 3) { - usage = make_uint16(bits[1], bits[2] & mask); - } - - m_constraints = Key_Constraints(usage); - } else { - m_constraints = Key_Constraints(0); + /* RFC 5280 Section 4.2.1.3: + * "When the keyUsage extension appears in a certificate, at least one of + * the bits MUST be set to 1." */ + if(usage == 0) { + throw Decoding_Error("KeyUsage extension must have at least one bit set"); } + + m_constraints = Key_Constraints(usage); } /* @@ -396,7 +450,16 @@ * Decode the extension */ void Subject_Key_ID::decode_inner(const std::vector& in) { - BER_Decoder(in).decode(m_key_id, ASN1_Type::OctetString).verify_end(); + /* RFC 5280 Section 4.2.1.2 - SubjectKeyIdentifier ::= KeyIdentifier */ + BER_Decoder(in, BER_Decoder::Limits::DER()).decode(m_key_id, ASN1_Type::OctetString).verify_end(); + + if(m_key_id.empty()) { + throw Decoding_Error("SubjectKeyIdentifier must not be empty"); + } + if(m_key_id.size() > MaximumKeyIdentifierLength) { + throw Decoding_Error( + fmt("SubjectKeyIdentifier length {} exceeds limit of {} bytes", m_key_id.size(), MaximumKeyIdentifierLength)); + } } /* @@ -433,7 +496,32 @@ * Decode the extension */ void Authority_Key_ID::decode_inner(const std::vector& in) { - BER_Decoder(in).start_sequence().decode_optional_string(m_key_id, ASN1_Type::OctetString, 0); + /* + * RFC 5280 Section 4.2.1.1 + * + * AuthorityKeyIdentifier ::= SEQUENCE { + * keyIdentifier [0] KeyIdentifier OPTIONAL, + * authorityCertIssuer [1] GeneralNames OPTIONAL, + * authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } + */ + BER_Decoder ber(in, BER_Decoder::Limits::DER()); + BER_Decoder seq = ber.start_sequence(); + + const bool key_id_present = seq.peek_next_object().is_a(0, ASN1_Class::ContextSpecific); + + seq.decode_optional_string(m_key_id, ASN1_Type::OctetString, 0).discard_remaining().end_cons(); + ber.verify_end(); + + if(key_id_present) { + if(m_key_id.empty()) { + throw Decoding_Error("AuthorityKeyIdentifier keyIdentifier must not be empty"); + } + if(m_key_id.size() > MaximumKeyIdentifierLength) { + throw Decoding_Error(fmt("AuthorityKeyIdentifier keyIdentifier length {} exceeds limit of {} bytes", + m_key_id.size(), + MaximumKeyIdentifierLength)); + } + } } /* @@ -458,14 +546,24 @@ * Decode the extension */ void Subject_Alternative_Name::decode_inner(const std::vector& in) { - BER_Decoder(in).decode(m_alt_name); + /* RFC 5280 Section 4.2.1.6 - SubjectAltName ::= GeneralNames + * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName */ + BER_Decoder(in, BER_Decoder::Limits::DER()).decode(m_alt_name).verify_end(); + if(!m_alt_name.has_items()) { + throw Decoding_Error("SubjectAlternativeName extension must contain at least one GeneralName"); + } } /* * Decode the extension */ void Issuer_Alternative_Name::decode_inner(const std::vector& in) { - BER_Decoder(in).decode(m_alt_name); + /* RFC 5280 Section 4.2.1.7 - IssuerAltName ::= GeneralNames + * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName */ + BER_Decoder(in, BER_Decoder::Limits::DER()).decode(m_alt_name).verify_end(); + if(!m_alt_name.has_items()) { + throw Decoding_Error("IssuerAlternativeName extension must contain at least one GeneralName"); + } } /* @@ -481,7 +579,11 @@ * Decode the extension */ void Extended_Key_Usage::decode_inner(const std::vector& in) { - BER_Decoder(in).decode_list(m_oids); + /* RFC 5280 Section 4.2.1.12 - ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId */ + BER_Decoder(in, BER_Decoder::Limits::DER()).decode_list(m_oids).verify_end(); + if(m_oids.empty()) { + throw Decoding_Error("ExtendedKeyUsage extension must contain at least one KeyPurposeId"); + } } /* @@ -495,7 +597,14 @@ * Decode the extension */ void Name_Constraints::decode_inner(const std::vector& in) { - BER_Decoder ber(in); + /* + * RFC 5280 Section 4.2.1.10 + * + * NameConstraints ::= SEQUENCE { + * permittedSubtrees [0] GeneralSubtrees OPTIONAL, + * excludedSubtrees [1] GeneralSubtrees OPTIONAL } + */ + BER_Decoder ber(in, BER_Decoder::Limits::DER()); BER_Decoder inner = ber.start_sequence(); std::vector permitted; @@ -513,6 +622,7 @@ } inner.end_cons(); + ber.verify_end(); if(permitted.empty() && excluded.empty()) { throw Decoding_Error("Empty NameConstraint extension"); @@ -522,7 +632,7 @@ } void Name_Constraints::validate(const X509_Certificate& subject, - const X509_Certificate& /*issuer*/, + const std::optional& /*issuer*/, const std::vector& cert_path, std::vector>& cert_status, size_t pos) { @@ -537,6 +647,13 @@ for(size_t j = 0; j < pos; ++j) { const auto& cert = cert_path.at(j); + // RFC 5280 6.1.4(b): "Name constraints are not applied to self-issued + // certificates (unless the certificate is the final certificate in the path)" + // Position 0 is the end entity (final certificate); skip self-issued intermediates. + if(j > 0 && cert.issuer_dn() == cert.subject_dn()) { + continue; + } + if(!m_name_constraints.is_permitted(cert, issuer_name_constraint_critical)) { cert_status.at(j).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR); continue; @@ -595,9 +712,13 @@ * Decode the extension */ void Certificate_Policies::decode_inner(const std::vector& in) { + /* RFC 5280 Section 4.2.1.4 - CertificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation */ std::vector policies; - BER_Decoder(in).decode_list(policies); + BER_Decoder(in, BER_Decoder::Limits::DER()).decode_list(policies).verify_end(); + if(policies.empty()) { + throw Decoding_Error("CertificatePolicies extension must contain at least one PolicyInformation"); + } m_oids.clear(); for(const auto& policy : policies) { m_oids.push_back(policy.oid()); @@ -605,11 +726,11 @@ } void Certificate_Policies::validate(const X509_Certificate& /*subject*/, - const X509_Certificate& /*issuer*/, + const std::optional& /*issuer*/, const std::vector& /*cert_path*/, std::vector>& cert_status, size_t pos) { - std::set oid_set(m_oids.begin(), m_oids.end()); + const std::set oid_set(m_oids.begin(), m_oids.end()); if(oid_set.size() != m_oids.size()) { cert_status.at(pos).insert(Certificate_Status_Code::DUPLICATE_CERT_POLICY); } @@ -620,9 +741,9 @@ DER_Encoder der(output); der.start_sequence(); - // OCSP - if(!m_ocsp_responder.empty()) { - ASN1_String url(m_ocsp_responder, ASN1_Type::Ia5String); + // OCSP Responders + for(const auto& ocsp_responder : m_ocsp_responders) { + const ASN1_String url(ocsp_responder, ASN1_Type::Ia5String); der.start_sequence() .encode(OID::from_string("PKIX.OCSP")) .add_object(ASN1_Type(6), ASN1_Class::ContextSpecific, url.value()) @@ -630,8 +751,8 @@ } // CA Issuers - for(const auto& ca_isser : m_ca_issuers) { - ASN1_String asn1_ca_issuer(ca_isser, ASN1_Type::Ia5String); + for(const auto& ca_issuer : m_ca_issuers) { + const ASN1_String asn1_ca_issuer(ca_issuer, ASN1_Type::Ia5String); der.start_sequence() .encode(OID::from_string("PKIX.CertificateAuthorityIssuers")) .add_object(ASN1_Type(6), ASN1_Class::ContextSpecific, asn1_ca_issuer.value()) @@ -643,29 +764,44 @@ } void Authority_Information_Access::decode_inner(const std::vector& in) { - BER_Decoder ber = BER_Decoder(in).start_sequence(); + /* + * RFC 5280 Section 4.2.2.1 + * + * AuthorityInfoAccessSyntax ::= SEQUENCE SIZE (1..MAX) OF AccessDescription + * AccessDescription ::= SEQUENCE { + * accessMethod OBJECT IDENTIFIER, + * accessLocation GeneralName } + */ + BER_Decoder outer(in, BER_Decoder::Limits::DER()); + BER_Decoder ber = outer.start_sequence(); + + const OID ocsp_responder = OID::from_string("PKIX.OCSP"); + const OID ca_issuer = OID::from_string("PKIX.CertificateAuthorityIssuers"); + size_t access_descriptions_seen = 0; while(ber.more_items()) { OID oid; BER_Decoder info = ber.start_sequence(); info.decode(oid); + const BER_Object name = info.get_next_object(); + info.end_cons(); - if(oid == OID::from_string("PKIX.OCSP")) { - BER_Object name = info.get_next_object(); + access_descriptions_seen += 1; - if(name.is_a(6, ASN1_Class::ContextSpecific)) { - m_ocsp_responder = ASN1::to_string(name); - } + if(oid == ocsp_responder && name.is_a(6, ASN1_Class::ContextSpecific)) { + m_ocsp_responders.push_back(ASN1::to_string(name)); + } else if(oid == ca_issuer && name.is_a(6, ASN1_Class::ContextSpecific)) { + m_ca_issuers.push_back(ASN1::to_string(name)); } - if(oid == OID::from_string("PKIX.CertificateAuthorityIssuers")) { - BER_Object name = info.get_next_object(); + } - if(name.is_a(6, ASN1_Class::ContextSpecific)) { - m_ca_issuers.push_back(ASN1::to_string(name)); - } - } + ber.end_cons(); + outer.verify_end(); + + if(access_descriptions_seen == 0) { + throw Decoding_Error("AuthorityInformationAccess extension must contain at least one AccessDescription"); } } @@ -702,7 +838,8 @@ * Decode the extension */ void CRL_Number::decode_inner(const std::vector& in) { - BER_Decoder(in).decode(m_crl_number); + /* RFC 5280 Section 5.2.3 - CRLNumber ::= INTEGER (0..MAX) */ + BER_Decoder(in, BER_Decoder::Limits::DER()).decode(m_crl_number).verify_end(); m_has_value = true; } @@ -719,8 +856,31 @@ * Decode the extension */ void CRL_ReasonCode::decode_inner(const std::vector& in) { + /* + * RFC 5280 Section 5.3.1 + * + * CRLReason ::= ENUMERATED { + * unspecified (0), + * keyCompromise (1), + * cACompromise (2), + * affiliationChanged (3), + * superseded (4), + * cessationOfOperation (5), + * certificateHold (6), + * -- value 7 is not used + * removeFromCRL (8), + * privilegeWithdrawn (9), + * aACompromise (10) } + */ size_t reason_code = 0; - BER_Decoder(in).decode(reason_code, ASN1_Type::Enumerated, ASN1_Class::Universal); + BER_Decoder(in, BER_Decoder::Limits::DER()) + .decode(reason_code, ASN1_Type::Enumerated, ASN1_Class::Universal) + .verify_end(); + + if(reason_code == 7 || reason_code > 10) { + throw Decoding_Error(fmt("CRLReason has unknown enumeration value {}", reason_code)); + } + m_reason = static_cast(reason_code); } @@ -731,19 +891,22 @@ } void CRL_Distribution_Points::decode_inner(const std::vector& buf) { - BER_Decoder(buf).decode_list(m_distribution_points).verify_end(); + /* + * RFC 5280 Section 4.2.1.13 + * + * CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint + */ + BER_Decoder(buf, BER_Decoder::Limits::DER()).decode_list(m_distribution_points).verify_end(); - std::stringstream ss; + if(m_distribution_points.empty()) { + throw Decoding_Error("CRLDistributionPoints extension must contain at least one DistributionPoint"); + } for(const auto& distribution_point : m_distribution_points) { - auto contents = distribution_point.point().contents(); - - for(const auto& pair : contents) { - ss << pair.first << ": " << pair.second << " "; + for(const auto& uri : distribution_point.point().uris()) { + m_crl_distribution_urls.push_back(uri); } } - - m_crl_distribution_urls.push_back(ss.str()); } void CRL_Distribution_Points::Distribution_Point::encode_into(DER_Encoder& der) const { @@ -781,28 +944,34 @@ } void CRL_Issuing_Distribution_Point::decode_inner(const std::vector& buf) { - BER_Decoder(buf).decode(m_distribution_point).verify_end(); + /* RFC 5280 Section 5.2.5 - IssuingDistributionPoint ::= SEQUENCE { ... } */ + BER_Decoder(buf, BER_Decoder::Limits::DER()).decode(m_distribution_point).verify_end(); } -void TNAuthList::Entry::encode_into(DER_Encoder&) const { +void TNAuthList::Entry::encode_into(DER_Encoder& /*to*/) const { throw Not_Implemented("TNAuthList extension entry serialization is not supported"); } void TNAuthList::Entry::decode_from(class BER_Decoder& ber) { - BER_Object obj = ber.get_next_object(); + const BER_Object obj = ber.get_next_object(); + + if(obj.get_class() != (ASN1_Class::ContextSpecific | ASN1_Class::Constructed)) { + throw Decoding_Error(fmt("Unexpected TNEntry class tag {}", static_cast(obj.get_class()))); + } - const uint32_t type_tag = static_cast(obj.type_tag()); + const uint32_t type_tag = static_cast(obj.type_tag()); if(type_tag == ServiceProviderCode) { m_type = ServiceProviderCode; ASN1_String spc_string; - BER_Decoder(obj).decode(spc_string); + BER_Decoder(obj, ber.limits()).decode(spc_string); m_data = std::move(spc_string); } else if(type_tag == TelephoneNumberRange) { m_type = TelephoneNumberRange; m_data = RangeContainer(); auto& range_items = std::get(m_data); - BER_Decoder list = BER_Decoder(obj).start_sequence(); + BER_Decoder outer(obj, ber.limits()); + BER_Decoder list = outer.start_sequence(); while(list.more_items()) { TelephoneNumberRangeData entry; @@ -826,7 +995,7 @@ } else if(type_tag == TelephoneNumber) { m_type = TelephoneNumber; ASN1_String one_string; - BER_Decoder(obj).decode(one_string); + BER_Decoder(obj, ber.limits()).decode(one_string); if(!is_valid_telephone_number(one_string)) { throw Decoding_Error(fmt("Invalid TelephoneNumber {}", one_string.value())); } @@ -841,14 +1010,865 @@ } void TNAuthList::decode_inner(const std::vector& in) { - BER_Decoder(in).decode_list(m_tn_entries).verify_end(); + /* RFC 8226 Section 9 - TNAuthorizationList ::= SEQUENCE SIZE (1..MAX) OF TNEntry */ + BER_Decoder(in, BER_Decoder::Limits::DER()).decode_list(m_tn_entries).verify_end(); if(m_tn_entries.empty()) { throw Decoding_Error("TNAuthorizationList is empty"); } } +const std::string& TNAuthList::Entry::service_provider_code() const { + BOTAN_STATE_CHECK(type() == Type::ServiceProviderCode); + return std::get(m_data).value(); +} + +const TNAuthList::Entry::RangeContainer& TNAuthList::Entry::telephone_number_range() const { + BOTAN_STATE_CHECK(type() == Type::TelephoneNumberRange); + return std::get(m_data); +} + +const std::string& TNAuthList::Entry::telephone_number() const { + BOTAN_STATE_CHECK(type() == Type::TelephoneNumber); + return std::get(m_data).value(); +} + +std::vector IPAddressBlocks::encode_inner() const { + std::vector output; + DER_Encoder(output).start_sequence().encode_list(m_ip_addr_blocks).end_cons(); + return output; +} + +void IPAddressBlocks::decode_inner(const std::vector& in) { + /* RFC 3779 Section 2.2.3.1 - IPAddrBlocks ::= SEQUENCE OF IPAddressFamily */ + BER_Decoder(in, BER_Decoder::Limits::DER()).decode_list(m_ip_addr_blocks).verify_end(); + sort_and_merge(); +} + +void IPAddressBlocks::IPAddressFamily::encode_into(Botan::DER_Encoder& into) const { + into.start_sequence(); + + std::vector afam = {get_byte<0>(m_afi), get_byte<1>(m_afi)}; + + if(m_safi.has_value()) { + afam.push_back(m_safi.value()); + } + + into.add_object(ASN1_Type::OctetString, ASN1_Class::Universal, afam); + + if(std::holds_alternative>(m_ip_addr_choice)) { + into.encode(std::get>(m_ip_addr_choice)); + } else { + into.encode(std::get>(m_ip_addr_choice)); + } + into.end_cons(); +} + +void IPAddressBlocks::IPAddressFamily::decode_from(Botan::BER_Decoder& from) { + const ASN1_Type next_tag = from.peek_next_object().type_tag(); + if(next_tag != ASN1_Type::Sequence) { + throw Decoding_Error(fmt("Unexpected type for IPAddressFamily {}", static_cast(next_tag))); + } + + BER_Decoder seq_dec = from.start_sequence(); + + std::vector addr_family; + seq_dec.decode(addr_family, ASN1_Type::OctetString); + const size_t addr_family_length = addr_family.size(); + + if(addr_family_length != 2 && addr_family_length != 3) { + throw Decoding_Error("(S)AFI can only contain 2 or 3 bytes"); + } + + m_afi = (addr_family[0] << 8) | addr_family[1]; + + if(addr_family_length == 3) { + m_safi = addr_family[2]; + } + + if(m_afi == 1) { + IPAddressChoice addr_choice; + seq_dec.decode(addr_choice); + m_ip_addr_choice = addr_choice; + } else if(m_afi == 2) { + IPAddressChoice addr_choice; + seq_dec.decode(addr_choice); + m_ip_addr_choice = addr_choice; + } else { + throw Decoding_Error("Only AFI IPv4 and IPv6 are supported."); + } + + seq_dec.end_cons(); +} + +void IPAddressBlocks::sort_and_merge() { + // Sort IPAddressFamilies by afi/safi values + // + // see: https://www.rfc-editor.org/rfc/rfc3779.html#section-2.2.3.3 + // + // v4 families are ordered before v6 families (i.e. they are sorted by afis, primarily), + // families with no safis are ordered before families with safis + // + // families with the same afi/safi combination are then merged + + // std::map is ordered, so using a pair (afi, optional(safi)) here works - std::nullopt is sorted before any actual values + std::map>, std::vector> afam_map; + for(const IPAddressFamily& block : m_ip_addr_blocks) { + auto key = std::make_pair(block.afi(), block.safi()); + std::vector& fams = afam_map[key]; + fams.push_back(block); + } + + std::vector merged_blocks; + for(auto& it : afam_map) { + // fams consists of families with the same afi/safi combination + std::vector& fams = it.second; + // since at least 1 block has to belong to a afi/safi combination for it to appear in the map, + // fams cannot be empty + BOTAN_ASSERT_NOMSG(!fams.empty()); + + // fams[0] has to have the same choice type as the fams in the same bucket + if(std::holds_alternative>(fams[0].addr_choice())) { + merged_blocks.push_back(merge(fams)); + } else { + merged_blocks.push_back(merge(fams)); + } + } + m_ip_addr_blocks = merged_blocks; +} + +template +IPAddressBlocks::IPAddressFamily IPAddressBlocks::merge(std::vector& blocks) { + // Merge IPAddressFamilies that have the same afi/safi combination + // + // see: https://www.rfc-editor.org/rfc/rfc3779.html#section-2.2.3.3 + + BOTAN_ASSERT(!blocks.empty(), "Cannot merge an empty set of IP address blocks into a single family"); + + // nothing to merge + if(blocks.size() == 1) { + return blocks[0]; + } + + bool all_inherit = true; + bool none_inherit = true; + for(const IPAddressFamily& block : blocks) { + const IPAddressChoice choice = std::get>(block.addr_choice()); + all_inherit = !choice.ranges().has_value() && all_inherit; // all the blocks have the 'inherit' value + none_inherit = choice.ranges().has_value() && none_inherit; + } + + // they are all 'inherit', short-circuit using default constructor for nullopt + if(all_inherit) { + return IPAddressFamily(IPAddressChoice(), blocks[0].safi()); + } + + // some are inherit, and some have values - no sensible way to merge them + if(!all_inherit && !none_inherit) { + throw Decoding_Error("Invalid IPAddressBlocks: Only one of 'inherit' or 'do not inherit' is allowed per family"); + } + + std::vector> merged_ranges; + for(const IPAddressFamily& block : blocks) { + const IPAddressChoice choice = std::get>(block.addr_choice()); + const std::vector> ranges = choice.ranges().value(); + for(const IPAddressOrRange& r : ranges) { + merged_ranges.push_back(r); + } + } + + // we have extracted all the ranges, and now rely on the constructor of IPAddressChoice to merge them + IPAddressChoice choice(merged_ranges); + IPAddressFamily fam(choice, blocks[0].safi()); + return fam; +} + +namespace { + +constexpr auto IPv4 = IPAddressBlocks::Version::IPv4; +constexpr auto IPv6 = IPAddressBlocks::Version::IPv6; + +template +using IPRangeVec = std::vector>; + +// (S)AFI -> (needs_check, ptr to IPRangeVec) +// the pointer can be null, in which case the boolean will be false, as such the pointer's value will never be looked at +template +using IPValidationMap = std::map*>>; + +template +std::optional> sort_and_merge_ranges(std::optional> ranges) { + // Sort and merge overlapping/adjacent IPAddressOrRange or ASIdOrRange objects. + // cf. https://www.rfc-editor.org/rfc/rfc3779.html#section-2.2.3.6 and https://www.rfc-editor.org/rfc/rfc3779.html#section-3.2.3.4 + // This implementation uses only min-max ranges internally, so sorting by the prefix length is not necessary / impossible here. + + if(!ranges.has_value()) { + return std::nullopt; + } + + std::vector sorted(ranges.value().begin(), ranges.value().end()); + + if(sorted.empty()) { + return sorted; + } + + // sort by the min value + std::sort(sorted.begin(), sorted.end(), [](T& a, T& b) { return a.min() < b.min(); }); + + // Single-pass merge: extend the last merged range or start a new one + std::vector merged; + merged.reserve(sorted.size()); + merged.push_back(sorted[0]); + + for(size_t i = 1; i < sorted.size(); ++i) { + auto& back = merged.back(); + // they either overlap or are adjacent + if(sorted[i].min() <= back.max() || sorted[i].min() == (back.max() + 1)) { + back = T(back.min(), std::max(back.max(), sorted[i].max())); + } else { + merged.push_back(sorted[i]); + } + } + + return merged; +} + +template +bool validate_subject_in_issuer(std::span subject, std::span issuer) { + // ensures that the subject ranges are enclosed by the issuer ranges + // both vectors are already sorted, so we can do this in O(n+m) + + // the issuer has 0 ranges to validate against, so this can only work if the subject also has none + if(issuer.empty()) { + return subject.empty(); + } + for(auto subj = subject.begin(), issu = issuer.begin(); subj != subject.end();) { + // the issuer range is smaller than the subject range, step to the next issuer range to check next round + if(subj->min() > issu->max()) { + issu++; + // we have run out of issuer ranges, but still have subject ranges left to validate + if(issu == issuer.end() && subj != subject.end()) { + return false; + } + } else { + // the subject is outside of the closest issuer range on the left (min) side + if(subj->min() < issu->min()) { + return false; + } + // the subject is outside of the closest issuer range on the right (max) side + if(subj->max() > issu->max()) { + return false; + } + // this range is contained within the issuer, advance to the next subject range + subj++; + } + } + return true; +} + +template +void populate_validation_map(uint32_t afam, + const IPAddressBlocks::IPAddressFamily::AddrChoice& choice, + IPValidationMap& map) { + const std::optional>& ranges = std::get>(choice).ranges(); + const bool has_value = ranges.has_value(); + const IPRangeVec* value = has_value ? &ranges.value() : nullptr; + map.emplace(afam, std::make_pair(has_value, std::move(value))); +} + +std::pair, IPValidationMap> create_validation_map( + const std::vector& addr_blocks) { + IPValidationMap v4_map; + IPValidationMap v6_map; + + for(const IPAddressBlocks::IPAddressFamily& block : addr_blocks) { + uint32_t afam = block.afi(); + if(block.safi().has_value()) { + afam = static_cast(afam << 8) | block.safi().value(); + } + + const IPAddressBlocks::IPAddressFamily::AddrChoice& a_choice = block.addr_choice(); + if(std::holds_alternative>(a_choice)) { + populate_validation_map(afam, a_choice, v4_map); + } else { + populate_validation_map(afam, a_choice, v6_map); + } + } + + return std::make_pair(v4_map, v6_map); +} + +} // namespace + +template +IPAddressBlocks::IPAddressChoice::IPAddressChoice( + std::optional>> ranges) { + // NOLINTNEXTLINE(*-prefer-member-initializer) + m_ip_addr_ranges = sort_and_merge_ranges>(ranges); +} + +template +void IPAddressBlocks::IPAddressChoice::encode_into(Botan::DER_Encoder& into) const { + if(m_ip_addr_ranges.has_value()) { + into.start_sequence().encode_list(m_ip_addr_ranges.value()).end_cons(); + } else { + into.encode_null(); + } +} + +template +void IPAddressBlocks::IPAddressChoice::decode_from(Botan::BER_Decoder& from) { + const ASN1_Type next_tag = from.peek_next_object().type_tag(); + + if(next_tag == ASN1_Type::Null) { + from.decode_null(); + m_ip_addr_ranges = std::nullopt; + } else if(next_tag == ASN1_Type::Sequence) { + std::vector> ip_ranges; + from.decode_list(ip_ranges); + m_ip_addr_ranges = sort_and_merge_ranges>(ip_ranges); + } else { + throw Decoding_Error(fmt("Unexpected type for IPAddressChoice {}", static_cast(next_tag))); + } +} + +template +void IPAddressBlocks::IPAddressOrRange::encode_into(Botan::DER_Encoder& into) const { + // Compress IPAddressOrRange as much as possible + // cf. https://www.rfc-editor.org/rfc/rfc3779.html#section-2.2.3.7 - https://www.rfc-editor.org/rfc/rfc3779.html#section-2.2.3.9 + // + // If possible encode as a prefix x.x.x.x/x, else encode as a range of min-max. + // Single addresses are encoded as is (technically a /32 or /128 prefix). + // + // A range can be encoded as a prefix if the lowest n bits of the min address are 0 + // and the highest n bits of the max address are 1, or in other words, contiguous sequences of 0s and 1s are omitted. + // To make reconstruction possible, an 'unused' octet is included at the start, since in the case of e.g. /25 only + // the highest bit of the last octet is actually meaningful. + // + // If encoding requires a range, the individual elements can still be compressed using the above method, + // but the number of used bits varies between them. + + const size_t version_octets = static_cast(V); + + std::array min = m_min.value(); + std::array max = m_max.value(); + + uint8_t zeros = 0; + uint8_t ones = 0; + + bool zeros_done = false; + bool ones_done = false; + + // count contiguous 0s/1s from the right of the min/max addresses + for(size_t i = version_octets; i > 0; i--) { + if(!zeros_done) { + const uint8_t local_zeros = static_cast(std::countr_zero(min[i - 1])); + zeros += local_zeros; + zeros_done = (local_zeros != 8); + } + + if(!ones_done) { + const uint8_t local_ones = static_cast(std::countr_one(max[i - 1])); + ones += local_ones; + ones_done = (local_ones != 8); + } + + if(zeros_done && ones_done) { + break; + } + } + + // the part we want to compress + const uint8_t host = std::min(zeros, ones); + + // these we can outright drop + const uint8_t discarded_octets = host / 8; + // in a partially used octet + const uint8_t unused_bits = host % 8; + + bool octets_match = true; + bool used_bits_match = true; + + // we have octets to check + if(discarded_octets < version_octets) { + // check all but the last octet + for(size_t i = 0; i < static_cast(version_octets - discarded_octets - 1); i++) { + if(min[i] != max[i]) { + octets_match = false; + break; + } + } + // check the last significant octet if we have matched so far + if(octets_match) { + const uint8_t shifted_min = (min[version_octets - 1 - discarded_octets] >> unused_bits); + const uint8_t shifted_max = (max[version_octets - 1 - discarded_octets] >> unused_bits); + used_bits_match = (shifted_min == shifted_max); + } + } + + // both the full octets and the partially used one match + if(octets_match && used_bits_match) { + // at this point the range can be encoded as a prefix + std::vector prefix; + + prefix.push_back(unused_bits); + for(size_t i = 0; i < static_cast(version_octets - discarded_octets); i++) { + prefix.push_back(min[i]); + } + + into.add_object(ASN1_Type::BitString, ASN1_Class::Universal, prefix); + } else { + const uint8_t discarded_octets_min = zeros / 8; + const uint8_t unused_bits_min = zeros % 8; + + const uint8_t discarded_octets_max = ones / 8; + const uint8_t unused_bits_max = ones % 8; + + // compress the max address by setting unused bits to 0, for the min address these are already 0 + if(unused_bits_max != 0) { + BOTAN_ASSERT_NOMSG(discarded_octets_max < version_octets); + max[version_octets - 1 - discarded_octets_max] >>= unused_bits_max; + max[version_octets - 1 - discarded_octets_max] <<= unused_bits_max; + } + + std::vector compressed_min; + std::vector compressed_max; + + // construct the address as a byte sequence of the unused bits followed by the compressed address + compressed_min.push_back(unused_bits_min); + for(size_t i = 0; i < static_cast(version_octets - discarded_octets_min); i++) { + compressed_min.push_back(min[i]); + } + + compressed_max.push_back(unused_bits_max); + for(size_t i = 0; i < static_cast(version_octets - discarded_octets_max); i++) { + compressed_max.push_back(max[i]); + } + + into.start_sequence() + .add_object(ASN1_Type::BitString, ASN1_Class::Universal, compressed_min) + .add_object(ASN1_Type::BitString, ASN1_Class::Universal, compressed_max) + .end_cons(); + } +} + +template +void IPAddressBlocks::IPAddressOrRange::decode_from(Botan::BER_Decoder& from) { + const ASN1_Type next_tag = from.peek_next_object().type_tag(); + + // this can either be a prefix or a single address + if(next_tag == ASN1_Type::BitString) { + // construct a min and a max address from the prefix + + std::vector prefix_min; + from.decode(prefix_min, ASN1_Type::OctetString, ASN1_Type::BitString, ASN1_Class::Universal); + + // copy because we modify the address in `decode_single_address`, but we need it twice for min and max + std::vector prefix_max(prefix_min); + + // min address gets filled with 0's + m_min = decode_single_address(std::move(prefix_min), true); + // max address with 1's + m_max = decode_single_address(std::move(prefix_max), false); + } else if(next_tag == ASN1_Type::Sequence) { + // this is a range + + std::vector addr_min; + std::vector addr_max; + + from.start_sequence() + .decode(addr_min, ASN1_Type::OctetString, ASN1_Type::BitString, ASN1_Class::Universal) + .decode(addr_max, ASN1_Type::OctetString, ASN1_Type::BitString, ASN1_Class::Universal) + .end_cons(); + + m_min = decode_single_address(std::move(addr_min), true); + m_max = decode_single_address(std::move(addr_max), false); + + if(m_min > m_max) { + throw Decoding_Error("IP address ranges must be sorted."); + } + } else { + throw Decoding_Error(fmt("Unexpected type for IPAddressOrRange {}", static_cast(next_tag))); + } +} + +template +IPAddressBlocks::IPAddress IPAddressBlocks::IPAddressOrRange::decode_single_address(std::vector decoded, + bool min) { + const size_t version_octets = static_cast(V); + + // decode a single address according to https://datatracker.ietf.org/doc/html/rfc3779#section-2.1.1 and following + + // we have to account for the octet at the beginning that specifies how many bits are unused in the last octet + if(decoded.empty() || decoded.size() > version_octets + 1) { + throw Decoding_Error(fmt("IP address range entries must have a length between 1 and {} bytes.", version_octets)); + } + + const uint8_t unused = decoded.front(); + const uint8_t discarded_octets = version_octets - (static_cast(decoded.size()) - 1); + + decoded.erase(decoded.begin()); + + if(decoded.empty() && unused != 0) { + throw Decoding_Error("IP address range entry specified unused bits, but did not provide any octets."); + } + + // if they were 8, the entire octet should have been discarded + if(unused > 7) { + throw Decoding_Error("IP address range entry specified invalid number of unused bits."); + } + + // pad to version length with 0's for min addresses, 255's (0xff) for max addresses + const uint8_t fill_discarded = min ? 0 : 0xff; + for(size_t i = 0; i < discarded_octets; i++) { + decoded.push_back(fill_discarded); + } + + // for min addresses they should already be 0, but we set them to zero regardless + // for max addresses this turns the unused bits to 1 + for(size_t i = 0; i < unused; i++) { + if(min) { + decoded[version_octets - 1 - discarded_octets] &= ~(1 << i); + } else { + decoded[version_octets - 1 - discarded_octets] |= (1 << i); + } + } + + return IPAddressBlocks::IPAddress(decoded); +} + +template +IPAddressBlocks::IPAddress::IPAddress(std::span v) { + if(v.size() != Length) { + throw Decoding_Error("number of bytes does not match IP version used"); + } + + for(size_t i = 0; i < Length; i++) { + m_value[i] = v[i]; + } +} + +void IPAddressBlocks::validate(const X509_Certificate& /* unused */, + const std::optional& /* unused */, + const std::vector& cert_path, + std::vector>& cert_status, + size_t pos) { + // maps in the form of (s)afi -> (needs_checking, ranges) + auto [v4_needs_check, v6_needs_check] = create_validation_map(m_ip_addr_blocks); + + if(pos == cert_path.size() - 1) { + // checks if any range / family has 'inherit' as a value somewhere, not allowed for the root cert + auto validate_root_cert_ext = [&](const auto& map) { + // check if any range has a value of 'false', indicating 'inherit' + return std::any_of(map.begin(), map.end(), [&](const auto& it) { + const auto& [_1, validation_info] = it; + const auto& [needs_checking, _2] = validation_info; + return !needs_checking; + }); + }; + if(validate_root_cert_ext(v4_needs_check) || validate_root_cert_ext(v6_needs_check)) { + cert_status.at(pos).insert(Certificate_Status_Code::IPADDR_BLOCKS_ERROR); + } + return; + } + + // traverse the chain until we find a cert with concrete values for the extension (so not 'inherit') + for(auto cert_path_it = cert_path.begin() + pos + 1; cert_path_it != cert_path.end(); cert_path_it++) { + const IPAddressBlocks* const parent_ip = cert_path_it->v3_extensions().get_extension_object_as(); + // extension not present for parent + if(parent_ip == nullptr) { + cert_status.at(pos).insert(Certificate_Status_Code::IPADDR_BLOCKS_ERROR); + return; + } + auto [issuer_v4, issuer_v6] = create_validation_map(parent_ip->addr_blocks()); + + auto validate_against_issuer = [&](auto& subject_map, const auto& issuer_map) { + for(auto map_it = subject_map.begin(); map_it != subject_map.end(); map_it++) { + auto& [afam, validation_info] = *map_it; + + // the issuer does not have this combination of afi/safi + if(issuer_map.count(afam) == 0) { + cert_status.at(pos).insert(Certificate_Status_Code::IPADDR_BLOCKS_ERROR); + return false; + } + + auto& [needs_check, subject_value] = validation_info; + const auto& [issuer_has_value, issuer_value] = issuer_map.at(afam); + BOTAN_ASSERT_NOMSG(!needs_check || subject_value != nullptr); + BOTAN_ASSERT_NOMSG(!issuer_has_value || issuer_value != nullptr); + + // we still need to check this range and the issuer has an actual value for it (so not 'inherit') + if(needs_check && issuer_has_value) { + if(!validate_subject_in_issuer(std::span(*subject_value), std::span(*issuer_value))) { + cert_status.at(pos).insert(Certificate_Status_Code::IPADDR_BLOCKS_ERROR); + return false; + } + needs_check = false; + } + } + return true; + }; + + if(!validate_against_issuer(v4_needs_check, issuer_v4) || !validate_against_issuer(v6_needs_check, issuer_v6)) { + return; + } + + auto validate_no_checks_left = [&](const auto& map) { + // check if all ranges have been checked, either by comparing their ranges if they have any, + // or if they are inherit, their parent(s) will be validated later + return std::all_of(map.begin(), map.end(), [&](const auto& it) { + const auto& [_1, validation_info] = it; + const auto& [needs_checking, _2] = validation_info; + return !needs_checking; + }); + }; + + if(validate_no_checks_left(v4_needs_check) && validate_no_checks_left(v6_needs_check)) { + // we've validated what we need to and can stop traversing the cert chain + return; + } + } +} + +template class IPAddressBlocks::IPAddress; +template class IPAddressBlocks::IPAddress; +template class IPAddressBlocks::IPAddressOrRange; +template class IPAddressBlocks::IPAddressOrRange; +template class IPAddressBlocks::IPAddressChoice; +template class IPAddressBlocks::IPAddressChoice; + +std::vector ASBlocks::encode_inner() const { + std::vector output; + DER_Encoder(output).encode(m_as_identifiers); + return output; +} + +void ASBlocks::decode_inner(const std::vector& in) { + /* RFC 3779 Section 3.2.3.1 - ASIdentifiers ::= SEQUENCE { ... } */ + BER_Decoder(in, BER_Decoder::Limits::DER()).decode(m_as_identifiers).verify_end(); +} + +ASBlocks::ASIdentifierChoice ASBlocks::add_new(const std::optional& old, asnum_t min, asnum_t max) { + std::vector range; + if(!old.has_value() || !old.value().ranges().has_value()) { + range = {ASIdOrRange(min, max)}; + } else { + range = old.value().ranges().value(); + range.push_back(ASIdOrRange(min, max)); + } + return ASIdentifierChoice(range); +} + +void ASBlocks::ASIdentifiers::encode_into(Botan::DER_Encoder& into) const { + into.start_sequence(); + + if(!m_asnum.has_value() && !m_rdi.has_value()) { + throw Encoding_Error("One of asnum, rdi must be present"); + } + + if(m_asnum.has_value()) { + into.start_explicit(0); + into.encode(m_asnum.value()); + into.end_explicit(); + } + + if(m_rdi.has_value()) { + into.start_explicit(1); + into.encode(m_rdi.value()); + into.end_explicit(); + } + + into.end_cons(); +} + +void ASBlocks::ASIdentifiers::decode_from(Botan::BER_Decoder& from) { + const ASN1_Type next_tag = from.peek_next_object().type_tag(); + if(next_tag != ASN1_Type::Sequence) { + throw Decoding_Error(fmt("Unexpected type for ASIdentifiers {}", static_cast(next_tag))); + } + + BER_Decoder seq_dec = from.start_sequence(); + + const BER_Object elem_obj = seq_dec.get_next_object(); + const uint32_t elem_type_tag = static_cast(elem_obj.type_tag()); + + // asnum, potentially followed by an rdi + if(elem_type_tag == 0) { + BER_Decoder as_obj_ber = BER_Decoder(elem_obj, seq_dec.limits()); + ASIdentifierChoice asnum; + as_obj_ber.decode(asnum).verify_end(); + m_asnum = asnum; + + const BER_Object rdi_obj = seq_dec.get_next_object(); + const ASN1_Type rdi_type_tag = rdi_obj.type_tag(); + if(static_cast(rdi_type_tag) == 1) { + BER_Decoder rdi_obj_ber = BER_Decoder(rdi_obj, seq_dec.limits()); + ASIdentifierChoice rdi; + rdi_obj_ber.decode(rdi).verify_end(); + m_rdi = rdi; + } else if(rdi_type_tag != ASN1_Type::NoObject) { + throw Decoding_Error(fmt("Unexpected type for ASIdentifiers rdi: {}", static_cast(rdi_type_tag))); + } + } + + // just an rdi + if(elem_type_tag == 1) { + BER_Decoder rdi_obj_ber = BER_Decoder(elem_obj, seq_dec.limits()); + ASIdentifierChoice rdi; + rdi_obj_ber.decode(rdi).verify_end(); + m_rdi = rdi; + const BER_Object end = seq_dec.get_next_object(); + const ASN1_Type end_type_tag = end.type_tag(); + if(end_type_tag != ASN1_Type::NoObject) { + throw Decoding_Error( + fmt("Unexpected element with type {} in ASIdentifiers", static_cast(end_type_tag))); + } + } + + seq_dec.end_cons(); + + if(!m_asnum.has_value() && !m_rdi.has_value()) { + throw Decoding_Error("Invalid encoding for ASIdentifiers"); + } +} + +void ASBlocks::ASIdentifierChoice::encode_into(Botan::DER_Encoder& into) const { + if(m_as_ranges.has_value()) { + into.start_sequence().encode_list(m_as_ranges.value()).end_cons(); + } else { + into.encode_null(); + } +} + +ASBlocks::ASIdentifierChoice::ASIdentifierChoice(const std::optional>& ranges) { + m_as_ranges = sort_and_merge_ranges(ranges); +} + +void ASBlocks::ASIdentifierChoice::decode_from(Botan::BER_Decoder& from) { + const ASN1_Type next_tag = from.peek_next_object().type_tag(); + + if(next_tag == ASN1_Type::Null) { + from.decode_null(); + m_as_ranges = std::nullopt; + } else if(next_tag == ASN1_Type::Sequence) { + std::vector as_ranges; + from.decode_list(as_ranges); + + m_as_ranges = sort_and_merge_ranges(as_ranges); + } else { + throw Decoding_Error(fmt("Unexpected type for ASIdentifierChoice {}", static_cast(next_tag))); + } +} + +void ASBlocks::ASIdOrRange::encode_into(Botan::DER_Encoder& into) const { + if(m_min == m_max) { + into.encode(static_cast(m_min)); + } else { + if(m_min >= m_max) { + throw Encoding_Error("AS range numbers must be sorted"); + } + into.start_sequence().encode(static_cast(m_min)).encode(static_cast(m_max)).end_cons(); + } +} + +void ASBlocks::ASIdOrRange::decode_from(BER_Decoder& from) { + const ASN1_Type next_tag = from.peek_next_object().type_tag(); + + size_t min = 0; + size_t max = 0; + + if(next_tag == ASN1_Type::Integer) { + from.decode(min); + m_min = checked_cast_to(min); + m_max = m_min; + } else if(next_tag == ASN1_Type::Sequence) { + from.start_sequence().decode(min).decode(max).end_cons(); + m_min = checked_cast_to(min); + m_max = checked_cast_to(max); + if(m_min >= m_max) { + throw Decoding_Error("ASIdOrRange has min greater than max"); + } + } else { + throw Decoding_Error(fmt("Unexpected type for ASIdOrRange {}", static_cast(next_tag))); + } +} + +void ASBlocks::validate(const X509_Certificate& /* unused */, + const std::optional& /* unused */, + const std::vector& cert_path, + std::vector>& cert_status, + size_t pos) { + // the extension may not contain asnums or rdis, but one of them is always present + const bool asnum_present = m_as_identifiers.asnum().has_value(); + const bool rdi_present = m_as_identifiers.rdi().has_value(); + + if(!asnum_present && !rdi_present) { + // Invalid, should have been caught during decoding + cert_status.at(pos).insert(Certificate_Status_Code::AS_BLOCKS_ERROR); + return; + } + + bool asnum_needs_check = asnum_present ? m_as_identifiers.asnum().value().ranges().has_value() : false; + bool rdi_needs_check = rdi_present ? m_as_identifiers.rdi().value().ranges().has_value() : false; + + // we are at the (trusted) root cert, there is no parent to verify against + if(pos == cert_path.size() - 1) { + // asnum / rdi is present, but has 'inherit' value, but there is nothing to inherit from + if((asnum_present && !asnum_needs_check) || (rdi_present && !rdi_needs_check)) { + cert_status.at(pos).insert(Certificate_Status_Code::AS_BLOCKS_ERROR); + } + return; + } + + // traverse the chain until we find a cert with concrete values for the extension (so not 'inherit') + for(auto it = cert_path.begin() + pos + 1; it != cert_path.end(); it++) { + const ASBlocks* const parent_as = it->v3_extensions().get_extension_object_as(); + // no extension at all or no asnums or no rdis (if needed) + if(parent_as == nullptr || (asnum_present && !parent_as->as_identifiers().asnum().has_value()) || + (rdi_present && !parent_as->as_identifiers().rdi().has_value())) { + cert_status.at(pos).insert(Certificate_Status_Code::AS_BLOCKS_ERROR); + return; + } + const auto as_identifiers = parent_as->as_identifiers(); + + // only something to validate if the subject does not have 'inherit' as a value + if(asnum_needs_check && as_identifiers.asnum().value().ranges().has_value()) { + const std::vector& subject_asnums = m_as_identifiers.asnum()->ranges().value(); + const std::vector& issuer_asnums = as_identifiers.asnum()->ranges().value(); + + if(!validate_subject_in_issuer(subject_asnums, issuer_asnums)) { + cert_status.at(pos).insert(Certificate_Status_Code::AS_BLOCKS_ERROR); + return; + } + // successfully validated the asnums, but we may need to step further for rdis + asnum_needs_check = false; + } + + if(rdi_needs_check && as_identifiers.rdi().value().ranges().has_value()) { + const std::vector& subject_rdis = m_as_identifiers.rdi()->ranges().value(); + const std::vector& issuer_rdis = as_identifiers.rdi()->ranges().value(); + + if(!validate_subject_in_issuer(subject_rdis, issuer_rdis)) { + cert_status.at(pos).insert(Certificate_Status_Code::AS_BLOCKS_ERROR); + return; + } + // successfully validated the rdis, but we may need to step further for asnums + rdi_needs_check = false; + } + + if(!asnum_needs_check && !rdi_needs_check) { + // we've validated what we need to and can stop traversing the cert chain + return; + } + } +} + +std::vector OCSP_NoCheck::encode_inner() const { + return {0x05, 0x00}; // NULL +} + void OCSP_NoCheck::decode_inner(const std::vector& buf) { - BER_Decoder(buf).verify_end(); + /* RFC 6960 Section 4.2.2.2.1 - id-pkix-ocsp-nocheck (value SHALL be NULL) */ + BER_Decoder(buf, BER_Decoder::Limits::DER()).decode_null().verify_end(); } std::vector Unknown_Extension::encode_inner() const { diff -Nru botan3-3.7.1+dfsg/src/lib/x509/x509_ext.h botan3-3.12.0+dfsg/src/lib/x509/x509_ext.h --- botan3-3.7.1+dfsg/src/lib/x509/x509_ext.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/x509_ext.h 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,7 @@ /* * X.509 Certificate Extensions * (C) 1999-2007,2012 Jack Lloyd +* (C) 2024 Anton Einax, Dominik Schricker * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -10,8 +11,13 @@ #include -#include +#include +#include +#include #include +#include +#include +#include namespace Botan { @@ -27,16 +33,26 @@ class BOTAN_PUBLIC_API(2, 0) Basic_Constraints final : public Certificate_Extension { public: std::unique_ptr copy() const override { - return std::make_unique(m_is_ca, m_path_limit); + return std::make_unique(m_is_ca, m_path_length_constraint); } - Basic_Constraints(bool ca = false, size_t limit = 0) : m_is_ca(ca), m_path_limit(limit) {} + BOTAN_FUTURE_EXPLICIT Basic_Constraints(bool is_ca = false, size_t path_length_constraint = 0); - bool get_is_ca() const { return m_is_ca; } + Basic_Constraints(bool is_ca, std::optional path_length_constraint); - size_t get_path_limit() const; + BOTAN_DEPRECATED("Use is_ca") bool get_is_ca() const { return m_is_ca; } - static OID static_oid() { return OID("2.5.29.19"); } + /** + * Note that this function returns NO_CERT_PATH_LIMIT if the value was not set + * in the extension. + */ + BOTAN_DEPRECATED("Use path_length_constraint") size_t get_path_limit() const; + + bool is_ca() const { return m_is_ca; } + + std::optional path_length_constraint() const { return m_path_length_constraint; } + + static OID static_oid() { return OID({2, 5, 29, 19}); } OID oid_of() const override { return static_oid(); } @@ -44,10 +60,10 @@ std::string oid_name() const override { return "X509v3.BasicConstraints"; } std::vector encode_inner() const override; - void decode_inner(const std::vector&) override; + void decode_inner(const std::vector& in) override; bool m_is_ca; - size_t m_path_limit; + std::optional m_path_length_constraint; }; /** @@ -65,7 +81,7 @@ Key_Constraints get_constraints() const { return m_constraints; } - static OID static_oid() { return OID("2.5.29.15"); } + static OID static_oid() { return OID({2, 5, 29, 15}); } OID oid_of() const override { return static_oid(); } @@ -75,7 +91,7 @@ bool should_encode() const override { return !m_constraints.empty(); } std::vector encode_inner() const override; - void decode_inner(const std::vector&) override; + void decode_inner(const std::vector& in) override; Key_Constraints m_constraints; }; @@ -97,7 +113,7 @@ const std::vector& get_key_id() const { return m_key_id; } - static OID static_oid() { return OID("2.5.29.14"); } + static OID static_oid() { return OID({2, 5, 29, 14}); } OID oid_of() const override { return static_oid(); } @@ -107,7 +123,7 @@ bool should_encode() const override { return (!m_key_id.empty()); } std::vector encode_inner() const override; - void decode_inner(const std::vector&) override; + void decode_inner(const std::vector& in) override; std::vector m_key_id; }; @@ -127,7 +143,7 @@ const std::vector& get_key_id() const { return m_key_id; } - static OID static_oid() { return OID("2.5.29.35"); } + static OID static_oid() { return OID({2, 5, 29, 35}); } OID oid_of() const override { return static_oid(); } @@ -137,7 +153,7 @@ bool should_encode() const override { return (!m_key_id.empty()); } std::vector encode_inner() const override; - void decode_inner(const std::vector&) override; + void decode_inner(const std::vector& in) override; std::vector m_key_id; }; @@ -149,7 +165,7 @@ public: const AlternativeName& get_alt_name() const { return m_alt_name; } - static OID static_oid() { return OID("2.5.29.17"); } + static OID static_oid() { return OID({2, 5, 29, 17}); } OID oid_of() const override { return static_oid(); } @@ -165,7 +181,7 @@ bool should_encode() const override { return m_alt_name.has_items(); } std::vector encode_inner() const override; - void decode_inner(const std::vector&) override; + void decode_inner(const std::vector& in) override; AlternativeName m_alt_name; }; @@ -177,7 +193,7 @@ public: const AlternativeName& get_alt_name() const { return m_alt_name; } - static OID static_oid() { return OID("2.5.29.18"); } + static OID static_oid() { return OID({2, 5, 29, 18}); } OID oid_of() const override { return static_oid(); } @@ -193,7 +209,7 @@ bool should_encode() const override { return m_alt_name.has_items(); } std::vector encode_inner() const override; - void decode_inner(const std::vector&) override; + void decode_inner(const std::vector& in) override; AlternativeName m_alt_name; }; @@ -213,7 +229,7 @@ const std::vector& object_identifiers() const { return m_oids; } - static OID static_oid() { return OID("2.5.29.37"); } + static OID static_oid() { return OID({2, 5, 29, 37}); } OID oid_of() const override { return static_oid(); } @@ -223,7 +239,7 @@ bool should_encode() const override { return (!m_oids.empty()); } std::vector encode_inner() const override; - void decode_inner(const std::vector&) override; + void decode_inner(const std::vector& in) override; std::vector m_oids; }; @@ -239,17 +255,17 @@ Name_Constraints() = default; - Name_Constraints(const NameConstraints& nc) : m_name_constraints(nc) {} + BOTAN_FUTURE_EXPLICIT Name_Constraints(const NameConstraints& nc) : m_name_constraints(nc) {} void validate(const X509_Certificate& subject, - const X509_Certificate& issuer, + const std::optional& issuer, const std::vector& cert_path, std::vector>& cert_status, size_t pos) override; const NameConstraints& get_name_constraints() const { return m_name_constraints; } - static OID static_oid() { return OID("2.5.29.30"); } + static OID static_oid() { return OID({2, 5, 29, 30}); } OID oid_of() const override { return static_oid(); } @@ -259,7 +275,7 @@ bool should_encode() const override { return true; } std::vector encode_inner() const override; - void decode_inner(const std::vector&) override; + void decode_inner(const std::vector& in) override; NameConstraints m_name_constraints; }; @@ -279,12 +295,12 @@ const std::vector& get_policy_oids() const { return m_oids; } - static OID static_oid() { return OID("2.5.29.32"); } + static OID static_oid() { return OID({2, 5, 29, 32}); } OID oid_of() const override { return static_oid(); } void validate(const X509_Certificate& subject, - const X509_Certificate& issuer, + const std::optional& issuer, const std::vector& cert_path, std::vector>& cert_status, size_t pos) override; @@ -295,7 +311,7 @@ bool should_encode() const override { return (!m_oids.empty()); } std::vector encode_inner() const override; - void decode_inner(const std::vector&) override; + void decode_inner(const std::vector& in) override; std::vector m_oids; }; @@ -306,18 +322,30 @@ class BOTAN_PUBLIC_API(2, 0) Authority_Information_Access final : public Certificate_Extension { public: std::unique_ptr copy() const override { - return std::make_unique(m_ocsp_responder, m_ca_issuers); + return std::make_unique(m_ocsp_responders, m_ca_issuers); } Authority_Information_Access() = default; + BOTAN_DEPRECATED("Use constructor with list of OCSP responders") explicit Authority_Information_Access(std::string_view ocsp, const std::vector& ca_issuers = std::vector()) : - m_ocsp_responder(ocsp), m_ca_issuers(ca_issuers) {} + m_ocsp_responders{std::string(ocsp)}, m_ca_issuers(ca_issuers) {} - std::string ocsp_responder() const { return m_ocsp_responder; } + explicit Authority_Information_Access(std::vector ocsp_responders, + std::vector ca_issuers = std::vector()) : + m_ocsp_responders(std::move(ocsp_responders)), m_ca_issuers(std::move(ca_issuers)) {} + + BOTAN_DEPRECATED("Use ocsp_responders") std::string ocsp_responder() const { + if(m_ocsp_responders.empty()) { + return {}; + } + return m_ocsp_responders[0]; + } + + const std::vector& ocsp_responders() const { return m_ocsp_responders; } - static OID static_oid() { return OID("1.3.6.1.5.5.7.1.1"); } + static OID static_oid() { return OID({1, 3, 6, 1, 5, 5, 7, 1, 1}); } OID oid_of() const override { return static_oid(); } @@ -326,12 +354,12 @@ private: std::string oid_name() const override { return "PKIX.AuthorityInformationAccess"; } - bool should_encode() const override { return (!m_ocsp_responder.empty() || !m_ca_issuers.empty()); } + bool should_encode() const override { return (!m_ocsp_responders.empty() || !m_ca_issuers.empty()); } std::vector encode_inner() const override; - void decode_inner(const std::vector&) override; + void decode_inner(const std::vector& in) override; - std::string m_ocsp_responder; + std::vector m_ocsp_responders; std::vector m_ca_issuers; }; @@ -344,11 +372,11 @@ CRL_Number() : m_has_value(false), m_crl_number(0) {} - CRL_Number(size_t n) : m_has_value(true), m_crl_number(n) {} + BOTAN_FUTURE_EXPLICIT CRL_Number(size_t n) : m_has_value(true), m_crl_number(n) {} size_t get_crl_number() const; - static OID static_oid() { return OID("2.5.29.20"); } + static OID static_oid() { return OID({2, 5, 29, 20}); } OID oid_of() const override { return static_oid(); } @@ -358,7 +386,7 @@ bool should_encode() const override { return m_has_value; } std::vector encode_inner() const override; - void decode_inner(const std::vector&) override; + void decode_inner(const std::vector& in) override; bool m_has_value; size_t m_crl_number; @@ -377,7 +405,7 @@ CRL_Code get_reason() const { return m_reason; } - static OID static_oid() { return OID("2.5.29.21"); } + static OID static_oid() { return OID({2, 5, 29, 21}); } OID oid_of() const override { return static_oid(); } @@ -387,7 +415,7 @@ bool should_encode() const override { return (m_reason != CRL_Code::Unspecified); } std::vector encode_inner() const override; - void decode_inner(const std::vector&) override; + void decode_inner(const std::vector& in) override; CRL_Code m_reason; }; @@ -400,8 +428,8 @@ public: class BOTAN_PUBLIC_API(2, 0) Distribution_Point final : public ASN1_Object { public: - void encode_into(DER_Encoder&) const override; - void decode_from(BER_Decoder&) override; + void encode_into(DER_Encoder& to) const override; + void decode_from(BER_Decoder& from) override; explicit Distribution_Point(const AlternativeName& name = AlternativeName()) : m_point(name) {} @@ -423,7 +451,7 @@ const std::vector& crl_distribution_urls() const { return m_crl_distribution_urls; } - static OID static_oid() { return OID("2.5.29.31"); } + static OID static_oid() { return OID({2, 5, 29, 31}); } OID oid_of() const override { return static_oid(); } @@ -433,7 +461,7 @@ bool should_encode() const override { return !m_distribution_points.empty(); } std::vector encode_inner() const override; - void decode_inner(const std::vector&) override; + void decode_inner(const std::vector& in) override; std::vector m_distribution_points; std::vector m_crl_distribution_urls; @@ -456,7 +484,7 @@ const AlternativeName& get_point() const { return m_distribution_point.point(); } - static OID static_oid() { return OID("2.5.29.28"); } + static OID static_oid() { return OID({2, 5, 29, 28}); } OID oid_of() const override { return static_oid(); } @@ -466,7 +494,7 @@ bool should_encode() const override { return true; } std::vector encode_inner() const override; - void decode_inner(const std::vector&) override; + void decode_inner(const std::vector& in) override; CRL_Distribution_Points::Distribution_Point m_distribution_point; }; @@ -488,7 +516,7 @@ std::unique_ptr copy() const override { return std::make_unique(); } - static OID static_oid() { return OID("1.3.6.1.5.5.7.48.1.5"); } + static OID static_oid() { return OID({1, 3, 6, 1, 5, 5, 7, 48, 1, 5}); } OID oid_of() const override { return static_oid(); } @@ -497,9 +525,9 @@ bool should_encode() const override { return true; } - std::vector encode_inner() const override { return {}; } + std::vector encode_inner() const override; - void decode_inner(const std::vector&) override; + void decode_inner(const std::vector& in) override; }; /** @@ -510,42 +538,37 @@ */ class BOTAN_PUBLIC_API(3, 5) TNAuthList final : public Certificate_Extension { public: - class Entry final : public ASN1_Object { + class BOTAN_PUBLIC_API(3, 5) Entry final : public ASN1_Object { public: /* TNEntry choice values * see: https://datatracker.ietf.org/doc/html/rfc8226#section-9 */ - enum Type { ServiceProviderCode = 0, TelephoneNumberRange = 1, TelephoneNumber = 2 }; + enum Type : uint8_t /* NOLINT(*-use-enum-class) */ { + ServiceProviderCode = 0, + TelephoneNumberRange = 1, + TelephoneNumber = 2 + }; struct TelephoneNumberRangeData { ASN1_String start; //TelephoneNumber (IA5String) - size_t count; //2..MAX + size_t count{}; //2..MAX }; using RangeContainer = std::vector; using DataContainer = std::variant; - void encode_into(DER_Encoder&) const override; + void encode_into(DER_Encoder& to) const override; void decode_from(class BER_Decoder& from) override; Type type() const { return m_type; } - const std::string& service_provider_code() const { - BOTAN_STATE_CHECK(type() == Type::ServiceProviderCode); - return std::get(m_data).value(); - } + const std::string& service_provider_code() const; - const RangeContainer& telephone_number_range() const { - BOTAN_STATE_CHECK(type() == Type::TelephoneNumberRange); - return std::get(m_data); - } + const RangeContainer& telephone_number_range() const; - const std::string& telephone_number() const { - BOTAN_STATE_CHECK(type() == Type::TelephoneNumber); - return std::get(m_data).value(); - } + const std::string& telephone_number() const; private: - Type m_type; + Type m_type{}; DataContainer m_data; }; @@ -553,7 +576,7 @@ std::unique_ptr copy() const override { return std::make_unique(*this); } - static OID static_oid() { return OID("1.3.6.1.5.5.7.1.26"); } + static OID static_oid() { return OID({1, 3, 6, 1, 5, 5, 7, 1, 26}); } OID oid_of() const override { return static_oid(); } @@ -565,21 +588,364 @@ bool should_encode() const override { return true; } std::vector encode_inner() const override; - void decode_inner(const std::vector&) override; + void decode_inner(const std::vector& in) override; std::vector m_tn_entries; }; /** + * IP Address Blocks Extension + * + * RFC 3779 X.509 Extensions for IP Addr + * +*/ +class BOTAN_PUBLIC_API(3, 9) IPAddressBlocks final : public Certificate_Extension { + public: + enum class Version : uint8_t { + IPv4 = 4, + IPv6 = 16, + }; + + template + class BOTAN_PUBLIC_API(3, 9) IPAddress final { + static constexpr size_t Length = static_cast(V); + + public: + explicit IPAddress(std::span v); + + std::array value() const { return m_value; } + + private: + friend class IPAddressBlocks; + IPAddress() = default; + + void next() { + for(auto it = m_value.rbegin(); it != m_value.rend(); it++) { + // we increment the current octet + (*it)++; + // if it did not wrap around we are done, else look at the next octet + if(*it != 0) { + break; + } + } + } + + friend IPAddress operator+(IPAddress lhs, size_t rhs) { + // we only really need to be able to compute +1, so this is fine + for(size_t i = 0; i < rhs; i++) { + lhs.next(); + } + return IPAddress(lhs); + } + + friend std::strong_ordering operator<=>(const IPAddress lhs, const IPAddress& rhs) { + for(size_t i = 0; i < Length; i++) { + if(lhs.value()[i] < rhs.value()[i]) { + return std::strong_ordering::less; + } else if(lhs.value()[i] > rhs.value()[i]) { + return std::strong_ordering::greater; + } + } + return std::strong_ordering::equal; + } + + friend bool operator==(const IPAddress& lhs, const IPAddress& rhs) { + return lhs.value() == rhs.value(); + } + + std::array m_value; + }; + + template + class BOTAN_PUBLIC_API(3, 9) IPAddressOrRange final : public ASN1_Object { + public: + void encode_into(DER_Encoder& to) const override; + void decode_from(BER_Decoder& from) override; + + IPAddressOrRange() = default; + + explicit IPAddressOrRange(const IPAddress& addr) : m_min(addr), m_max(addr) {} + + IPAddressOrRange(const IPAddress& min, const IPAddress& max) : m_min(min), m_max(max) { + if(max < min) { + throw Decoding_Error("IP address ranges must be sorted"); + } + } + + IPAddress min() const { return m_min; } + + IPAddress max() const { return m_max; } + + private: + IPAddress m_min{}; + IPAddress m_max{}; + + IPAddress decode_single_address(std::vector decoded, bool min); + }; + + template + class BOTAN_PUBLIC_API(3, 9) IPAddressChoice final : public ASN1_Object { + public: + void encode_into(DER_Encoder& to) const override; + void decode_from(BER_Decoder& from) override; + + const std::optional>>& ranges() const { return m_ip_addr_ranges; } + + IPAddressChoice() = default; + + explicit IPAddressChoice(std::optional>> ranges); + + private: + std::optional>> m_ip_addr_ranges; + }; + + class BOTAN_PUBLIC_API(3, 9) IPAddressFamily final : public ASN1_Object { + public: + typedef std::variant, IPAddressChoice> AddrChoice; + + void encode_into(DER_Encoder& to) const override; + void decode_from(BER_Decoder& from) override; + + IPAddressFamily() = default; + + explicit IPAddressFamily(const AddrChoice& choice, std::optional safi = std::nullopt) : + m_safi(safi), m_ip_addr_choice(choice) { + if(std::holds_alternative>(choice)) { + m_afi = 1; + } else { + m_afi = 2; + } + } + + uint16_t afi() const { return m_afi; } + + std::optional safi() const { return m_safi; } + + const AddrChoice& addr_choice() const { return m_ip_addr_choice; } + + private: + uint16_t m_afi = 1; + std::optional m_safi; + AddrChoice m_ip_addr_choice; + }; + + IPAddressBlocks() = default; + + explicit IPAddressBlocks(const std::vector& blocks) : m_ip_addr_blocks(blocks) { + this->sort_and_merge(); + } + + std::unique_ptr copy() const override { return std::make_unique(*this); } + + static OID static_oid() { return OID({1, 3, 6, 1, 5, 5, 7, 1, 7}); } + + OID oid_of() const override { return static_oid(); } + + void validate(const X509_Certificate& subject, + const std::optional& issuer, + const std::vector& cert_path, + std::vector>& cert_status, + size_t pos) override; + + /// Add a single IP address to this extension (for the specified SAFI, if any) + template + void add_address(const std::array(V)>& address, + std::optional safi = std::nullopt) { + add_address(address, address, safi); + } + + /// Add an IP address range to this extension (for the specified SAFI, if any) + template + void add_address(const std::array(V)>& min, + const std::array(V)>& max, + std::optional safi = std::nullopt) { + std::vector> addresses = {IPAddressOrRange(IPAddress(min), IPAddress(max))}; + m_ip_addr_blocks.push_back(IPAddressFamily(IPAddressChoice(addresses), safi)); + sort_and_merge(); + } + + /// Make the extension contain no allowed IP addresses for the specified IP version (and SAFI, if any) + template + void restrict(std::optional safi = std::nullopt) { + std::vector> addresses = {}; + m_ip_addr_blocks.push_back(IPAddressFamily(IPAddressChoice(addresses), safi)); + sort_and_merge(); + } + + /// Mark the specified IP version as 'inherit' (for the specified SAFI, if any) + template + void inherit(std::optional safi = std::nullopt) { + m_ip_addr_blocks.push_back(IPAddressFamily(IPAddressChoice(), safi)); + sort_and_merge(); + } + + const std::vector& addr_blocks() const { return m_ip_addr_blocks; } + + private: + std::string oid_name() const override { return "PKIX.IpAddrBlocks"; } + + bool should_encode() const override { return true; } + + std::vector encode_inner() const override; + void decode_inner(const std::vector& in) override; + + std::vector m_ip_addr_blocks; + + void sort_and_merge(); + template + IPAddressFamily merge(std::vector& blocks); +}; + +/** + * AS Blocks Extension + * + * RFC 3779 X.509 Extensions for AS ID + * +*/ +class BOTAN_PUBLIC_API(3, 9) ASBlocks final : public Certificate_Extension { + public: + typedef uint32_t asnum_t; + + class BOTAN_PUBLIC_API(3, 9) ASIdOrRange final : public ASN1_Object { + public: + void encode_into(DER_Encoder& to) const override; + void decode_from(BER_Decoder& from) override; + + asnum_t min() const { return m_min; } + + asnum_t max() const { return m_max; } + + ASIdOrRange() = default; + + explicit ASIdOrRange(asnum_t id) : m_min(id), m_max(id) {} + + ASIdOrRange(asnum_t min, asnum_t max) : m_min(min), m_max(max) { + if(max < min) { + throw Decoding_Error("AS range numbers must be sorted"); + } + } + + private: + asnum_t m_min = 0; + asnum_t m_max = 0; + }; + + class BOTAN_PUBLIC_API(3, 9) ASIdentifierChoice final : public ASN1_Object { + public: + void encode_into(DER_Encoder& to) const override; + void decode_from(BER_Decoder& from) override; + + ASIdentifierChoice() = default; + + explicit ASIdentifierChoice(const std::optional>& ranges); + + const std::optional>& ranges() const { return m_as_ranges; } + + private: + std::optional> m_as_ranges; + }; + + class BOTAN_PUBLIC_API(3, 9) ASIdentifiers final : public ASN1_Object { + public: + void encode_into(DER_Encoder& to) const override; + void decode_from(BER_Decoder& from) override; + + explicit ASIdentifiers(const std::optional& asnum, + const std::optional& rdi) : + m_asnum(asnum), m_rdi(rdi) { + if(!m_asnum.has_value() && !m_rdi.has_value()) { + throw Decoding_Error("One of asnum, rdi must be present"); + } + } + + const std::optional& asnum() const { return m_asnum; } + + const std::optional& rdi() const { return m_rdi; } + + private: + friend class ASBlocks; + ASIdentifiers() = default; + + std::optional m_asnum; + std::optional m_rdi; + }; + + ASBlocks() = default; + + explicit ASBlocks(const ASIdentifiers& as_idents) : m_as_identifiers(as_idents) {} + + std::unique_ptr copy() const override { return std::make_unique(*this); } + + static OID static_oid() { return OID({1, 3, 6, 1, 5, 5, 7, 1, 8}); } + + OID oid_of() const override { return static_oid(); } + + void validate(const X509_Certificate& subject, + const std::optional& issuer, + const std::vector& cert_path, + std::vector>& cert_status, + size_t pos) override; + + /// Add a single asnum to this extension + void add_asnum(asnum_t asnum) { add_asnum(asnum, asnum); } + + /// Add an asnum range to this extension + void add_asnum(asnum_t min, asnum_t max) { + m_as_identifiers = ASIdentifiers(add_new(m_as_identifiers.asnum(), min, max), m_as_identifiers.rdi()); + } + + /// Make the extension contain no allowed asnum's + void restrict_asnum() { + std::vector empty; + m_as_identifiers = ASIdentifiers(ASIdentifierChoice(empty), m_as_identifiers.rdi()); + } + + /// Mark the asnum entry as 'inherit' + void inherit_asnum() { m_as_identifiers = ASIdentifiers(ASIdentifierChoice(), m_as_identifiers.rdi()); } + + /// Add a single rdi to this extension + void add_rdi(asnum_t rdi) { add_rdi(rdi, rdi); } + + /// Add an rdi range to this extension + void add_rdi(asnum_t min, asnum_t max) { + m_as_identifiers = ASIdentifiers(m_as_identifiers.asnum(), add_new(m_as_identifiers.rdi(), min, max)); + } + + /// Make the extension contain no allowed rdi's + void restrict_rdi() { + std::vector empty; + m_as_identifiers = ASIdentifiers(m_as_identifiers.asnum(), ASIdentifierChoice(empty)); + } + + /// Mark the rdi entry as 'inherit' + void inherit_rdi() { m_as_identifiers = ASIdentifiers(m_as_identifiers.asnum(), ASIdentifierChoice()); } + + const ASIdentifiers& as_identifiers() const { return m_as_identifiers; } + + private: + ASIdentifiers m_as_identifiers; + + std::string oid_name() const override { return "PKIX.AutonomousSysIds"; } + + bool should_encode() const override { return true; } + + static ASIdentifierChoice add_new(const std::optional& old, asnum_t min, asnum_t max); + + std::vector encode_inner() const override; + void decode_inner(const std::vector& in) override; +}; + +/** * An unknown X.509 extension * Will add a failure to the path validation result, if critical */ class BOTAN_PUBLIC_API(2, 4) Unknown_Extension final : public Certificate_Extension { public: - Unknown_Extension(const OID& oid, bool critical) : m_oid(oid), m_critical(critical) {} + Unknown_Extension(const OID& oid, bool critical, bool failed_to_decode = false) : + m_oid(oid), m_critical(critical), m_failed_to_decode(failed_to_decode) {} std::unique_ptr copy() const override { - return std::make_unique(m_oid, m_critical); + return std::make_unique(m_oid, m_critical, m_failed_to_decode); } /** @@ -599,12 +965,20 @@ */ bool is_critical_extension() const { return m_critical; } - void validate(const X509_Certificate&, - const X509_Certificate&, - const std::vector&, + /** + * Return true if this extension's OID was recognized but the contents + * failed to decode. + */ + bool failed_to_decode() const { return m_failed_to_decode; } + + void validate(const X509_Certificate& /*subject*/, + const std::optional& /*issuer*/, + const std::vector& /*cert_path*/, std::vector>& cert_status, size_t pos) override { - if(m_critical) { + if(m_failed_to_decode) { + cert_status.at(pos).insert(Certificate_Status_Code::EXTENSION_ENCODING_ERROR); + } else if(m_critical) { cert_status.at(pos).insert(Certificate_Status_Code::UNKNOWN_CRITICAL_EXTENSION); } } @@ -615,10 +989,11 @@ bool should_encode() const override { return true; } std::vector encode_inner() const override; - void decode_inner(const std::vector&) override; + void decode_inner(const std::vector& in) override; OID m_oid; bool m_critical; + bool m_failed_to_decode; std::vector m_bytes; }; diff -Nru botan3-3.7.1+dfsg/src/lib/x509/x509_obj.cpp botan3-3.12.0+dfsg/src/lib/x509/x509_obj.cpp --- botan3-3.7.1+dfsg/src/lib/x509/x509_obj.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/x509_obj.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,12 +7,13 @@ #include +#include #include +#include #include #include #include #include -#include #include namespace Botan { @@ -23,15 +24,17 @@ void X509_Object::load_data(DataSource& in) { try { if(ASN1::maybe_BER(in) && !PEM_Code::matches(in)) { - BER_Decoder dec(in); + BER_Decoder dec(in, BER_Decoder::Limits::DER()); decode_from(dec); + // Call to verify_end omitted here since we have to sometimes decode + // multiple certificates encoded sequentially in a DataSource } else { std::string got_label; DataSource_Memory ber(PEM_Code::decode(in, got_label)); if(got_label != PEM_label()) { bool is_alternate = false; - for(std::string_view alt_label : alternate_PEM_labels()) { + for(const std::string_view alt_label : alternate_PEM_labels()) { if(got_label == alt_label) { is_alternate = true; break; @@ -43,14 +46,37 @@ } } - BER_Decoder dec(ber); + BER_Decoder dec(ber, BER_Decoder::Limits::DER()); decode_from(dec); + // Call to verify_end omitted here since we have to sometimes decode + // multiple certificates encoded sequentially in a DataSource } } catch(Decoding_Error& e) { throw Decoding_Error(PEM_label() + " decoding", e); } } +const std::vector& X509_Object::signature() const { + if(!m_signed_data) { + throw Invalid_State("X509_Object uninitialized"); + } + return m_signed_data->m_sig; +} + +const std::vector& X509_Object::signed_body() const { + if(!m_signed_data) { + throw Invalid_State("X509_Object uninitialized"); + } + return m_signed_data->m_tbs_bits; +} + +const AlgorithmIdentifier& X509_Object::signature_algorithm() const { + if(!m_signed_data) { + throw Invalid_State("X509_Object uninitialized"); + } + return m_signed_data->m_sig_algo; +} + void X509_Object::encode_into(DER_Encoder& to) const { to.start_sequence() .start_sequence() @@ -65,14 +91,17 @@ * Read a BER encoded X.509 object */ void X509_Object::decode_from(BER_Decoder& from) { + auto data = std::make_shared(); + from.start_sequence() .start_sequence() - .raw_bytes(m_tbs_bits) + .raw_bytes(data->m_tbs_bits) .end_cons() - .decode(m_sig_algo) - .decode(m_sig, ASN1_Type::BitString) + .decode(data->m_sig_algo) + .decode(data->m_sig, ASN1_Type::BitString) .end_cons(); + m_signed_data = std::move(data); force_decode(); } @@ -87,7 +116,7 @@ * Return the TBS data */ std::vector X509_Object::tbs_data() const { - return ASN1::put_in_sequence(m_tbs_bits); + return ASN1::put_in_sequence(signed_body()); } /* @@ -124,7 +153,7 @@ std::vector X509_Object::make_signed(PK_Signer& signer, RandomNumberGenerator& rng, const AlgorithmIdentifier& algo, - const secure_vector& tbs_bits) { + std::span tbs_bits) { const std::vector signature = signer.sign_message(tbs_bits, rng); std::vector output; diff -Nru botan3-3.7.1+dfsg/src/lib/x509/x509_obj.h botan3-3.12.0+dfsg/src/lib/x509/x509_obj.h --- botan3-3.7.1+dfsg/src/lib/x509/x509_obj.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/x509_obj.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,10 @@ #include #include +#include +#include +#include +#include #include namespace Botan { @@ -34,17 +38,17 @@ /** * @return signature on tbs_data() */ - const std::vector& signature() const { return m_sig; } + const std::vector& signature() const; /** * @return signed body */ - const std::vector& signed_body() const { return m_tbs_bits; } + const std::vector& signed_body() const; /** * @return signature algorithm that was used to generate signature */ - const AlgorithmIdentifier& signature_algorithm() const { return m_sig_algo; } + const AlgorithmIdentifier& signature_algorithm() const; /** * Create a signed X509 object. @@ -57,7 +61,7 @@ static std::vector make_signed(PK_Signer& signer, RandomNumberGenerator& rng, const AlgorithmIdentifier& alg_id, - const secure_vector& tbs); + std::span tbs); /** * Check the signature on this data @@ -65,7 +69,7 @@ * @return status of the signature - OK if verified or otherwise an indicator of * the problem preventing verification, along with the hash function that * was used, for further policy checks. The second parameter is empty - * unless the validation was sucessful. + * unless the validation was successful. */ std::pair verify_signature(const Public_Key& key) const; @@ -93,15 +97,10 @@ */ std::string PEM_encode() const; - X509_Object(const X509_Object&) = default; - X509_Object& operator=(const X509_Object&) = default; - virtual std::string PEM_label() const = 0; virtual std::vector alternate_PEM_labels() const { return std::vector(); } - ~X509_Object() override = default; - /** * Choose and return a signature scheme appropriate for X.509 signing * using the provided parameters. @@ -128,9 +127,14 @@ private: virtual void force_decode() = 0; - AlgorithmIdentifier m_sig_algo; - std::vector m_tbs_bits; - std::vector m_sig; + class Signed_Data final { + public: + AlgorithmIdentifier m_sig_algo; + std::vector m_tbs_bits; + std::vector m_sig; + }; + + std::shared_ptr m_signed_data; }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/x509/x509_utils.h botan3-3.12.0+dfsg/src/lib/x509/x509_utils.h --- botan3-3.7.1+dfsg/src/lib/x509/x509_utils.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/x509_utils.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,37 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_X509_UTILS_H_ +#define BOTAN_X509_UTILS_H_ + +#include +#include +#include + +namespace Botan { + +inline std::optional is_sub_element_of(const OID& oid, std::initializer_list prefix) { + const auto& c = oid.get_components(); + + if(c.size() != prefix.size() + 1) { + return {}; + } + + if(!std::equal(c.begin(), c.end() - 1, prefix.begin(), prefix.end())) { + return {}; + } + + return c[c.size() - 1]; +} + +/* +* X.500 String Comparison +*/ +bool x500_name_cmp(std::string_view name1, std::string_view name2); + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/x509/x509cert.cpp botan3-3.12.0+dfsg/src/lib/x509/x509cert.cpp --- botan3-3.7.1+dfsg/src/lib/x509/x509cert.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/x509cert.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,8 @@ #include +#include +#include #include #include #include @@ -16,12 +18,12 @@ #include #include #include -#include #include namespace Botan { -struct X509_Certificate_Data { +class X509_Certificate_Data final { + public: std::vector m_serial; AlgorithmIdentifier m_sig_algo_inner; X509_DN m_issuer_dn; @@ -46,7 +48,7 @@ std::vector m_cert_policies; std::vector m_crl_distribution_points; - std::string m_ocsp_responder; + std::vector m_ocsp_responders; std::vector m_ca_issuers; std::vector m_issuer_dn_bits_sha256; @@ -55,12 +57,15 @@ std::string m_fingerprint_sha1; std::string m_fingerprint_sha256; + std::array m_cert_data_sha1 = {}; + std::array m_cert_data_sha256 = {}; + AlternativeName m_subject_alt_name; AlternativeName m_issuer_alt_name; NameConstraints m_name_constraints; size_t m_version = 0; - size_t m_path_len_constraint = 0; + std::optional m_path_len_constraint; Key_Constraints m_key_constraints; bool m_self_signed = false; bool m_is_ca_certificate = false; @@ -68,6 +73,8 @@ bool m_subject_alt_name_exists = false; }; +X509_Certificate::~X509_Certificate() = default; + std::string X509_Certificate::PEM_label() const { return "CERTIFICATE"; } @@ -80,13 +87,8 @@ load_data(src); } -X509_Certificate::X509_Certificate(const std::vector& vec) { - DataSource_Memory src(vec.data(), vec.size()); - load_data(src); -} - -X509_Certificate::X509_Certificate(const uint8_t data[], size_t len) { - DataSource_Memory src(data, len); +X509_Certificate::X509_Certificate(std::span in) { + DataSource_Memory src(in); load_data(src); } @@ -106,7 +108,7 @@ BER_Object public_key; BER_Object v3_exts_data; - BER_Decoder(obj.signed_body()) + BER_Decoder(obj.signed_body(), BER_Decoder::Limits::DER()) .decode_optional(data->m_version, ASN1_Type(0), ASN1_Class::Constructed | ASN1_Class::ContextSpecific) .decode(serial_bn) .decode(data->m_sig_algo_inner) @@ -126,7 +128,7 @@ throw Decoding_Error("Unknown X.509 cert version " + std::to_string(data->m_version)); } if(obj.signature_algorithm() != data->m_sig_algo_inner) { - throw Decoding_Error("X.509 Certificate had differing algorithm identifers in inner and outer ID fields"); + throw Decoding_Error("X.509 Certificate had differing algorithm identifiers in inner and outer ID fields"); } public_key.assert_is_a(ASN1_Type::Sequence, ASN1_Class::Constructed, "X.509 certificate public key"); @@ -136,7 +138,7 @@ data->m_serial = serial_bn.serialize(); // crude method to save the serial's sign; will get lost during decoding, otherwise - data->m_serial_negative = serial_bn.is_negative(); + data->m_serial_negative = serial_bn.signum() < 0; data->m_subject_dn_bits = ASN1::put_in_sequence(data->m_subject_dn.get_bits()); data->m_issuer_dn_bits = ASN1::put_in_sequence(data->m_issuer_dn.get_bits()); @@ -144,19 +146,20 @@ data->m_subject_public_key_bits_seq = ASN1::put_in_sequence(data->m_subject_public_key_bits); - BER_Decoder(data->m_subject_public_key_bits) + BER_Decoder(data->m_subject_public_key_bits, BER_Decoder::Limits::DER()) .decode(data->m_subject_public_key_algid) - .decode(data->m_subject_public_key_bitstring, ASN1_Type::BitString); + .decode(data->m_subject_public_key_bitstring, ASN1_Type::BitString) + .verify_end(); if(v3_exts_data.is_a(3, ASN1_Class::Constructed | ASN1_Class::ContextSpecific)) { // Path validation will reject a v1/v2 cert with v3 extensions - BER_Decoder(v3_exts_data).decode(data->m_v3_extensions).verify_end(); + BER_Decoder(v3_exts_data, BER_Decoder::Limits::DER()).decode(data->m_v3_extensions).verify_end(); } else if(v3_exts_data.is_set()) { throw BER_Bad_Tag("Unknown tag in X.509 cert", v3_exts_data.tagging()); } // Now cache some fields from the extensions - if(auto ext = data->m_v3_extensions.get_extension_object_as()) { + if(const auto* ext = data->m_v3_extensions.get_extension_object_as()) { data->m_key_constraints = ext->get_constraints(); /* RFC 5280: When the keyUsage extension appears in a certificate, @@ -167,19 +170,19 @@ } } - if(auto ext = data->m_v3_extensions.get_extension_object_as()) { + if(const auto* ext = data->m_v3_extensions.get_extension_object_as()) { data->m_subject_key_id = ext->get_key_id(); } - if(auto ext = data->m_v3_extensions.get_extension_object_as()) { + if(const auto* ext = data->m_v3_extensions.get_extension_object_as()) { data->m_authority_key_id = ext->get_key_id(); } - if(auto ext = data->m_v3_extensions.get_extension_object_as()) { + if(const auto* ext = data->m_v3_extensions.get_extension_object_as()) { data->m_name_constraints = ext->get_name_constraints(); } - if(auto ext = data->m_v3_extensions.get_extension_object_as()) { + if(const auto* ext = data->m_v3_extensions.get_extension_object_as()) { data->m_extended_key_usage = ext->object_identifiers(); /* RFC 5280 section 4.2.1.12 @@ -197,8 +200,13 @@ } } - if(auto ext = data->m_v3_extensions.get_extension_object_as()) { - if(ext->get_is_ca() == true) { + if(const auto* ext = data->m_v3_extensions.get_extension_object_as()) { + /* + * RFC 5280 4.2.1.9 requires that conforming CAs "MUST mark the + * extension [basicConstraints] as critical in such certificates" + * but places no such requirement on validators. + */ + if(ext->is_ca() == true) { /* * RFC 5280 section 4.2.1.3 requires that CAs include KeyUsage in all * intermediate CA certificates they issue. Currently we accept it being @@ -210,12 +218,16 @@ data->m_key_constraints.includes(Key_Constraints::KeyCertSign) || data->m_key_constraints.empty(); /* - * If the extended key usages are set then we must restrict the - * usage in accordance with it as well. + * If the extended key usages are set then we must restrict the usage in + * accordance with it as well. * - * RFC 5280 does not define any extended key usages compatible - * with certificate signing, but some CAs seem to use serverAuth - * or clientAuth here. + * RFC 5280 does not define any extended key usages compatible with certificate + * signing, but some CAs use serverAuth, clientAuth, OCSPSigning, or AnyExtendedKeyUsage + * for this purpose, even though clearly all of these (besides AEKU) are invalid. + * This check at least allows excluding a certificate which is set for only eg + * timestamping or code signing, and that seems about the best we can possibly enforce. + * OpenSSL, BoringSSL, and Go all completely ignore EKUs in determining ability to + * issue certs. */ const bool allowed_by_ext_ku = [](const std::vector& ext_ku) -> bool { if(ext_ku.empty()) { @@ -225,9 +237,10 @@ const auto server_auth = OID::from_name("PKIX.ServerAuth"); const auto client_auth = OID::from_name("PKIX.ClientAuth"); const auto ocsp_sign = OID::from_name("PKIX.OCSPSigning"); + const auto any_eku = OID::from_name("X509v3.AnyExtendedKeyUsage"); for(const auto& oid : ext_ku) { - if(oid == server_auth || oid == client_auth || oid == ocsp_sign) { + if(oid == any_eku || oid == server_auth || oid == client_auth || oid == ocsp_sign) { return true; } } @@ -237,16 +250,16 @@ if(allowed_by_ku && allowed_by_ext_ku) { data->m_is_ca_certificate = true; - data->m_path_len_constraint = ext->get_path_limit(); + data->m_path_len_constraint = ext->path_length_constraint(); } } } - if(auto ext = data->m_v3_extensions.get_extension_object_as()) { + if(const auto* ext = data->m_v3_extensions.get_extension_object_as()) { data->m_issuer_alt_name = ext->get_alt_name(); } - if(auto ext = data->m_v3_extensions.get_extension_object_as()) { + if(const auto* ext = data->m_v3_extensions.get_extension_object_as()) { data->m_subject_alt_name = ext->get_alt_name(); } @@ -256,45 +269,37 @@ const auto san_oid = OID::from_string("X509v3.SubjectAlternativeName"); data->m_subject_alt_name_exists = data->m_v3_extensions.extension_set(san_oid); - if(auto ext = data->m_v3_extensions.get_extension_object_as()) { + if(const auto* ext = data->m_v3_extensions.get_extension_object_as()) { data->m_cert_policies = ext->get_policy_oids(); } - if(auto ext = data->m_v3_extensions.get_extension_object_as()) { - data->m_ocsp_responder = ext->ocsp_responder(); + if(const auto* ext = data->m_v3_extensions.get_extension_object_as()) { + data->m_ocsp_responders = ext->ocsp_responders(); data->m_ca_issuers = ext->ca_issuers(); } - if(auto ext = data->m_v3_extensions.get_extension_object_as()) { + if(const auto* ext = data->m_v3_extensions.get_extension_object_as()) { data->m_crl_distribution_points = ext->crl_distribution_urls(); } - // Check for self-signed vs self-issued certificates + /* + Determine if this certificate appears to be self-issued (subject == issuer). + This is only a heuristic used for path building so it's ok it is not precise. + The self-signature is verified during path validation. + */ if(data->m_subject_dn == data->m_issuer_dn) { - if(data->m_subject_key_id.empty() == false && data->m_authority_key_id.empty() == false) { + if(!data->m_subject_key_id.empty() && !data->m_authority_key_id.empty()) { + /* + Both SKID and AKID are set so we can reliably determine self-signed vs + self-issued by comparing the two + */ data->m_self_signed = (data->m_subject_key_id == data->m_authority_key_id); } else { /* - If a parse error or unknown algorithm is encountered, default - to assuming it is self signed. We have no way of being certain but - that is usually the default case (self-issued is rare in practice). + Without both SKID and AKID we can't determine with certainty. Assume + self-signed since that's by far the common case. */ data->m_self_signed = true; - - try { - auto pub_key = X509::load_key(data->m_subject_public_key_bits_seq); - - const auto sig_status = obj.verify_signature(*pub_key); - - if(sig_status.first == Certificate_Status_Code::OK || - sig_status.first == Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN) { - data->m_self_signed = true; - } else { - data->m_self_signed = false; - } - } catch(...) { - // ignore errors here to allow parsing to continue - } } } @@ -305,18 +310,22 @@ data->m_subject_public_key_bitstring_sha1 = sha1->final_stdvec(); // otherwise left as empty, and we will throw if subject_public_key_bitstring_sha1 is called - data->m_fingerprint_sha1 = create_hex_fingerprint(full_encoding, "SHA-1"); - } - - if(auto sha256 = HashFunction::create("SHA-256")) { - sha256->update(data->m_issuer_dn_bits); - data->m_issuer_dn_bits_sha256 = sha256->final_stdvec(); - - sha256->update(data->m_subject_dn_bits); - data->m_subject_dn_bits_sha256 = sha256->final_stdvec(); - - data->m_fingerprint_sha256 = create_hex_fingerprint(full_encoding, "SHA-256"); - } + sha1->update(full_encoding); + sha1->final(data->m_cert_data_sha1); + data->m_fingerprint_sha1 = format_hex_fingerprint(data->m_cert_data_sha1); + } + + // SHA-256 is a hard dependency of this module + auto sha256 = HashFunction::create_or_throw("SHA-256"); + sha256->update(data->m_issuer_dn_bits); + data->m_issuer_dn_bits_sha256 = sha256->final_stdvec(); + + sha256->update(data->m_subject_dn_bits); + data->m_subject_dn_bits_sha256 = sha256->final_stdvec(); + + sha256->update(full_encoding); + sha256->final(data->m_cert_data_sha256); + data->m_fingerprint_sha256 = format_hex_fingerprint(data->m_cert_data_sha256); return data; } @@ -418,6 +427,17 @@ return data().m_subject_dn_bits; } +std::span X509_Certificate::certificate_data_sha1() const { + if(data().m_fingerprint_sha1.empty()) { + throw Not_Implemented("SHA-1 not available"); + } + return data().m_cert_data_sha1; +} + +std::span X509_Certificate::certificate_data_sha256() const { + return data().m_cert_data_sha256; +} + bool X509_Certificate::is_CA_cert() const { if(data().m_version < 3 && data().m_self_signed) { return true; @@ -431,7 +451,11 @@ return 32; // in theory infinite, but this is more than enough } - return static_cast(data().m_path_len_constraint); + return static_cast(data().m_path_len_constraint.value_or(Cert_Extension::NO_CERT_PATH_LIMIT)); +} + +std::optional X509_Certificate::path_length_constraint() const { + return data().m_path_len_constraint; } Key_Constraints X509_Certificate::constraints() const { @@ -476,7 +500,7 @@ return true; } - if(std::find(ex.begin(), ex.end(), usage) != ex.end()) { + if(has_ex_constraint(usage)) { return true; } @@ -518,8 +542,27 @@ } bool X509_Certificate::has_ex_constraint(const OID& usage) const { - const std::vector& ex = extended_key_usage(); - return (std::find(ex.begin(), ex.end(), usage) != ex.end()); + const auto any_eku = OID::from_name("X509v3.AnyExtendedKeyUsage"); + const auto ocsp_eku = OID::from_name("PKIX.OCSPSigning"); + + for(const auto& ext_ku : extended_key_usage()) { + if(ext_ku == usage) { + return true; + } + + /* + Do not accept AnyExtendedKeyUsage for OCSP due to RFC 6960 4.2.2.2: + + OCSP signing delegation SHALL be designated by the inclusion of + id-kp-OCSPSigning in an extended key usage certificate extension + included in the OCSP response signer's certificate. + */ + if(ext_ku == any_eku && usage != ocsp_eku) { + return true; + } + } + + return false; } /* @@ -530,7 +573,14 @@ } std::string X509_Certificate::ocsp_responder() const { - return data().m_ocsp_responder; + if(data().m_ocsp_responders.empty()) { + return {}; + } + return data().m_ocsp_responders[0]; +} + +const std::vector& X509_Certificate::ocsp_responders() const { + return data().m_ocsp_responders; } std::vector X509_Certificate::ca_issuers() const { @@ -572,10 +622,16 @@ return set_to_vector(alt_name.uris()); } else if(req == "IP") { std::vector ip_str; - for(uint32_t ipv4 : alt_name.ipv4_address()) { + for(const uint32_t ipv4 : alt_name.ipv4_address()) { ip_str.push_back(ipv4_to_string(ipv4)); } return ip_str; + } else if(req == "IPv6") { + std::vector ip_str; + for(const auto& ipv6 : alt_name.ipv6_address()) { + ip_str.push_back(ipv6.to_string()); + } + return ip_str; } else { return {}; } @@ -612,14 +668,14 @@ return this->subject_public_key(); } -std::vector X509_Certificate::raw_issuer_dn_sha256() const { +const std::vector& X509_Certificate::raw_issuer_dn_sha256() const { if(data().m_issuer_dn_bits_sha256.empty()) { throw Encoding_Error("X509_Certificate::raw_issuer_dn_sha256 called but SHA-256 disabled in build"); } return data().m_issuer_dn_bits_sha256; } -std::vector X509_Certificate::raw_subject_dn_sha256() const { +const std::vector& X509_Certificate::raw_subject_dn_sha256() const { if(data().m_subject_dn_bits_sha256.empty()) { throw Encoding_Error("X509_Certificate::raw_subject_dn_sha256 called but SHA-256 disabled in build"); } @@ -645,6 +701,10 @@ } } +X509_Certificate::Tag X509_Certificate::tag() const { + return Tag(data().m_cert_data_sha256); +} + bool X509_Certificate::matches_dns_name(std::string_view name) const { if(name.empty()) { return false; @@ -653,18 +713,23 @@ if(auto req_ipv4 = string_to_ipv4(name)) { const auto& ipv4_names = subject_alt_name().ipv4_address(); return ipv4_names.contains(req_ipv4.value()); - } else { - auto issued_names = subject_info("DNS"); + } - // Fall back to CN only if no SAN is included - if(!data().m_subject_alt_name_exists) { - issued_names = subject_info("Name"); - } + if(auto req_ipv6 = IPv6Address::from_string(name)) { + const auto& ipv6_names = subject_alt_name().ipv6_address(); + return ipv6_names.contains(req_ipv6.value()); + } - for(const auto& issued_name : issued_names) { - if(host_wildcard_match(issued_name, name)) { - return true; - } + auto issued_names = subject_info("DNS"); + + // Fall back to CN only if no SAN is included + if(!data().m_subject_alt_name_exists) { + issued_names = subject_info("Name"); + } + + for(const auto& issued_name : issued_names) { + if(host_wildcard_match(issued_name, name)) { + return true; } } @@ -718,7 +783,7 @@ } out << "Constraints:\n"; - Key_Constraints constraints = this->constraints(); + const Key_Constraints constraints = this->constraints(); if(constraints.empty()) { out << " No key constraints set\n"; } else { @@ -790,8 +855,12 @@ } } - if(!ocsp_responder().empty()) { - out << "OCSP responder " << ocsp_responder() << "\n"; + const auto& ocsp_responders = this->ocsp_responders(); + if(!ocsp_responders.empty()) { + out << "OCSP Responders:\n"; + for(const auto& ocsp_responder : ocsp_responders) { + out << " URI: " << ocsp_responder << "\n"; + } } const std::vector ca_issuers = this->ca_issuers(); diff -Nru botan3-3.7.1+dfsg/src/lib/x509/x509cert.h botan3-3.12.0+dfsg/src/lib/x509/x509cert.h --- botan3-3.7.1+dfsg/src/lib/x509/x509cert.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/x509cert.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,29 +9,25 @@ #define BOTAN_X509_CERTS_H_ #include +#include +#include #include +#include namespace Botan { -class Public_Key; -class X509_DN; -class Extensions; class AlternativeName; +class Extensions; class NameConstraints; +class Public_Key; +class X509_DN; -enum class Usage_Type { - UNSPECIFIED, // no restrictions - TLS_SERVER_AUTH, - TLS_CLIENT_AUTH, - CERTIFICATE_AUTHORITY, - OCSP_RESPONDER, - ENCRYPTION -}; - -struct X509_Certificate_Data; +class X509_Certificate_Data; /** * This class represents an X.509 Certificate +* +* TODO(Botan4) mark this final once PKCS11_X509_Certificate is fixed */ class BOTAN_PUBLIC_API(2, 0) X509_Certificate : public X509_Object { public: @@ -133,7 +129,7 @@ /** * SHA-256 of Raw issuer DN */ - std::vector raw_issuer_dn_sha256() const; + const std::vector& raw_issuer_dn_sha256() const; /** * Raw subject DN @@ -143,7 +139,17 @@ /** * SHA-256 of Raw subject DN */ - std::vector raw_subject_dn_sha256() const; + const std::vector& raw_subject_dn_sha256() const; + + /** + * SHA-1 of the entire certificate DER encoding + */ + std::span certificate_data_sha1() const; + + /** + * SHA-256 of the entire certificate DER encoding + */ + std::span certificate_data_sha256() const; /** * Get the notBefore of the certificate as X509_Time @@ -252,11 +258,27 @@ bool has_ex_constraint(const OID& ex_constraint) const; /** - * Get the path limit as defined in the BasicConstraints extension of - * this certificate. + * Get the path length constraint as defined in the BasicConstraints extension. + * + * This returns an arbitrary value if the extension is not set (either 32 for v1 + * self-signed certificates, or else Cert_Extension::NO_CERT_PATH_LIMIT for v3 + * certificates without the extension) + * + * Prefer path_length_constraint + * + * @return path limit + */ + BOTAN_DEPRECATED("Use X509_Certificate::path_length_constraint") uint32_t path_limit() const; + + /** + * Get the path length constraint as defined in the BasicConstraints extension. + * + * Returns nullopt if either the extension is not set in the certificate, + * or if the pathLenConstraint field was absent from the extension. + * * @return path limit */ - uint32_t path_limit() const; + std::optional path_length_constraint() const; /** * Check whenever a given X509 Extension is marked critical in this @@ -323,7 +345,12 @@ /** * Return the listed address of an OCSP responder, or empty if not set */ - std::string ocsp_responder() const; + BOTAN_DEPRECATED("Use ocsp_responders") std::string ocsp_responder() const; + + /** + * Return the listed addresses of OCSP responders, or empty if not set + */ + const std::vector& ocsp_responders() const; /** * Return the listed addresses of ca issuers, or empty if not set @@ -352,6 +379,44 @@ std::string fingerprint(std::string_view hash_name = "SHA-1") const; /** + * A collision resistant binary "tag" of a certificate + * + * The actual value is deliberately not exposed; a Tag can only be hashed + * to a size_t, or compared with another Tag. This type is intended for use + * as a key in std::map and std::unordered_map, or to be saved in a + * std::set or std::unordered_set. + */ + class Tag final { + public: + static constexpr size_t TagLen = 32; + + auto operator<=>(const Tag&) const = default; + + size_t hash() const noexcept { + size_t h = 0; + std::memcpy(&h, m_tag.data(), sizeof(h)); + return h; + } + + private: + friend X509_Certificate; + + explicit Tag(std::array tag) : m_tag(tag) {} + + std::array m_tag; + }; + + class TagHash final { + public: + size_t operator()(const X509_Certificate::Tag& tag) const noexcept { return tag.hash(); } + }; + + /** + * Return a collision resistant binary "tag" of this certificate + */ + Tag tag() const; + + /** * Check if a certain DNS name matches up with the information in * the cert * @param name DNS name to match @@ -394,14 +459,14 @@ * Create a certificate from a buffer * @param in the buffer containing the DER-encoded certificate */ - explicit X509_Certificate(const std::vector& in); + explicit X509_Certificate(std::span in); /** * Create a certificate from a buffer * @param data the buffer containing the DER-encoded certificate * @param length length of data in bytes */ - X509_Certificate(const uint8_t data[], size_t length); + X509_Certificate(const uint8_t data[], size_t length) : X509_Certificate(std::span{data, length}) {} /** * Create an uninitialized certificate object. Any attempts to @@ -410,8 +475,10 @@ X509_Certificate() = default; X509_Certificate(const X509_Certificate& other) = default; - + X509_Certificate(X509_Certificate&& other) = default; X509_Certificate& operator=(const X509_Certificate& other) = default; + X509_Certificate& operator=(X509_Certificate&& other) = default; + ~X509_Certificate() override; private: std::string PEM_label() const override; @@ -422,7 +489,7 @@ const X509_Certificate_Data& data() const; - std::shared_ptr m_data; + std::shared_ptr m_data; }; /** diff -Nru botan3-3.7.1+dfsg/src/lib/x509/x509opt.cpp botan3-3.12.0+dfsg/src/lib/x509/x509opt.cpp --- botan3-3.7.1+dfsg/src/lib/x509/x509opt.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/x509opt.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -63,11 +63,6 @@ * Initialize the certificate options */ X509_Cert_Options::X509_Cert_Options(std::string_view initial_opts, uint32_t expiration_time) { - is_CA = false; - path_limit = 0; - // use default for chosen algorithm - padding_scheme = ""; - auto now = std::chrono::system_clock::now(); start = X509_Time(now); diff -Nru botan3-3.7.1+dfsg/src/lib/x509/x509path.cpp botan3-3.12.0+dfsg/src/lib/x509/x509path.cpp --- botan3-3.7.1+dfsg/src/lib/x509/x509path.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/x509path.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ /* * X.509 Certificate Path Validation -* (C) 2010,2011,2012,2014,2016 Jack Lloyd +* (C) 2010,2011,2012,2014,2016,2026 Jack Lloyd * (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) @@ -8,15 +8,17 @@ #include +#include #include #include #include -#include +#include #include #include #include #include #include +#include #include #if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS) @@ -24,12 +26,183 @@ #include #endif -#if defined(BOTAN_HAS_ECC_KEY) - #include -#endif - namespace Botan { +namespace { + +/** + * Lazy DFS iterator that yields certificate paths one at a time. + * + * Build all possible certificate paths from the end certificate to self-signed trusted roots. + * + * Basically, a DFS is performed starting from the end certificate. A stack (vector) + * serves to control the DFS. At the beginning of each iteration, a pair is popped from + * the stack that contains (1) the next certificate to add to the path (2) a bool that + * indicates if the certificate is part of a trusted certstore. Ideally, we follow the + * unique issuer of the current certificate until a trusted root is reached. However, the + * issuer DN + authority key id need not be unique among the certificates used for + * building the path. In such a case, we consider all the matching issuers by pushing + * on the stack for each of them. + * + * Each call to next() resumes the search and returns the next discovered path, or nullopt + * when the search space is exhausted. +*/ +class CertificatePathBuilder final { + public: + CertificatePathBuilder(const std::vector& trusted_certstores, + const X509_Certificate& end_entity, + const std::vector& end_entity_extra, + bool require_self_signed = false) : + m_trusted_certstores(trusted_certstores), m_require_self_signed(require_self_signed) { + if(std::ranges::any_of(trusted_certstores, [](auto* ptr) { return ptr == nullptr; })) { + throw Invalid_Argument("Certificate store list must not contain nullptr"); + } + + for(const auto& cert : end_entity_extra) { + if(!cert_in_any_trusted_store(cert)) { + m_ee_extras.add_certificate(cert); + } + } + + m_stack.push_back({end_entity, cert_in_any_trusted_store(end_entity)}); + } + + std::optional> next() { + size_t steps = 0; + + while(!m_stack.empty()) { + constexpr size_t MAX_DFS_STEPS = 1000; + + steps++; + + if(steps > MAX_DFS_STEPS) { + // Intentionally overwrite any previous builder error + m_error = Certificate_Status_Code::CERT_ISSUER_NOT_FOUND; + return std::nullopt; + } + + auto [last, trusted] = std::move(m_stack.back()); // move before pop_back + m_stack.pop_back(); + + // Found a deletion marker that guides the DFS, backtracking + if(!last.has_value()) { + m_certs_seen.erase(m_path_so_far.back().tag()); + m_path_so_far.pop_back(); + continue; + } + + // Certificate already seen in this path? + const auto tag = last->tag(); + if(m_certs_seen.contains(tag)) { + if(!m_error.has_value()) { + m_error = Certificate_Status_Code::CERT_CHAIN_LOOP; + } + continue; + } + + // A valid path has been discovered. It includes endpoints that may end + // with either a self-signed or a non-self-signed certificate. For + // certificates that are not self-signed, additional paths could + // potentially extend from the current one. + if(trusted) { + auto path = m_path_so_far; + path.push_back(*last); + push_issuers(*last); + + if(!m_require_self_signed || last->is_self_signed()) { + return path; + } + + /* + This unconditionally overwrites the error because it's likely the most + informative error in this context - we found a path that seemed entirely + suitable, except that self-signed roots are required so it was skipped. + */ + m_error = Certificate_Status_Code::CANNOT_ESTABLISH_TRUST; + continue; + } + + if(last->is_self_signed()) { + if(!m_error.has_value()) { + m_error = Certificate_Status_Code::CANNOT_ESTABLISH_TRUST; + } + continue; + } + + push_issuers(*last); + } + + return std::nullopt; + } + + /** + * Return the first error encountered during path building + * + * Only used as a last resort if there were no successful paths + */ + Certificate_Status_Code error() const { + if(m_error.has_value()) { + // Confirm it is an actual error code and not accidentally OK... + BOTAN_ASSERT_NOMSG(static_cast(m_error.value()) >= 3000); + return m_error.value(); + } else { + return Certificate_Status_Code::CERT_ISSUER_NOT_FOUND; + } + } + + private: + bool cert_in_any_trusted_store(const X509_Certificate& cert) const { + return std::ranges::any_of(m_trusted_certstores, + [&](const Certificate_Store* store) { return store->contains(cert); }); + } + + void push_issuers(const X509_Certificate& cert) { + const X509_DN& issuer_dn = cert.issuer_dn(); + const std::vector& auth_key_id = cert.authority_key_id(); + + // Search for trusted issuers + std::vector trusted_issuers; + for(const Certificate_Store* store : m_trusted_certstores) { + auto new_issuers = store->find_all_certs(issuer_dn, auth_key_id); + trusted_issuers.insert(trusted_issuers.end(), new_issuers.begin(), new_issuers.end()); + } + + // Search the supplemental certs + const std::vector misc_issuers = m_ee_extras.find_all_certs(issuer_dn, auth_key_id); + + // If we could not find any issuers, the current path ends here + if(trusted_issuers.empty() && misc_issuers.empty()) { + if(!m_error.has_value()) { + m_error = Certificate_Status_Code::CERT_ISSUER_NOT_FOUND; + } + return; + } + + m_path_so_far.push_back(cert); + m_certs_seen.emplace(cert.tag()); + + // Push a deletion marker on the stack for backtracking later + m_stack.push_back({std::nullopt, false}); + + for(const auto& trusted_cert : trusted_issuers) { + m_stack.push_back({trusted_cert, true}); + } + for(const auto& misc : misc_issuers) { + m_stack.push_back({misc, false}); + } + } + + const std::vector m_trusted_certstores; + const bool m_require_self_signed; + Certificate_Store_In_Memory m_ee_extras; + std::vector, bool>> m_stack; + std::vector m_path_so_far; + std::unordered_set m_certs_seen; + std::optional m_error; +}; + +} // namespace + /* * PKIX path validation */ @@ -42,9 +215,9 @@ throw Invalid_Argument("PKIX::check_chain cert_path empty"); } - const bool self_signed_ee_cert = (cert_path.size() == 1); + const bool is_end_entity_trust_anchor = (cert_path.size() == 1); - X509_Time validation_time(ref_time); + const X509_Time validation_time(ref_time); CertificatePathStatusCodes cert_status(cert_path.size()); @@ -52,10 +225,18 @@ for(size_t i = 0; i != cert_path.size(); ++i) { std::set& status = cert_status.at(i); - const bool at_self_signed_root = (i == cert_path.size() - 1); + const bool at_trust_anchor = (i == cert_path.size() - 1); const X509_Certificate& subject = cert_path[i]; - const X509_Certificate& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)]; + + // If using intermediate CAs as trust anchors, the signature of the trust + // anchor cannot be verified since the issuer is not part of the + // certificate chain + if(!restrictions.require_self_signed_trust_anchors() && at_trust_anchor && !subject.is_self_signed()) { + continue; + } + + const X509_Certificate& issuer = cert_path[at_trust_anchor ? (i) : (i + 1)]; // Check the signature algorithm is known if(!subject.signature_algorithm().oid().registered_oid()) { @@ -84,7 +265,7 @@ const auto& trusted_hashes = restrictions.trusted_hashes(); // Ignore untrusted hashes on self-signed roots - if(!trusted_hashes.empty() && !at_self_signed_root) { + if(!trusted_hashes.empty() && !at_trust_anchor) { if(!trusted_hashes.contains(hash_used_for_signature)) { status.insert(Certificate_Status_Code::UNTRUSTED_HASH); } @@ -136,17 +317,25 @@ for(size_t i = 0; i != cert_path.size(); ++i) { std::set& status = cert_status.at(i); - const bool at_self_signed_root = (i == cert_path.size() - 1); + const bool at_trust_anchor = (i == cert_path.size() - 1); const X509_Certificate& subject = cert_path[i]; - const X509_Certificate& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)]; + const auto issuer = [&]() -> std::optional { + if(!at_trust_anchor) { + return cert_path[i + 1]; + } else if(subject.is_self_signed()) { + return cert_path[i]; + } else { + return {}; // Non self-signed trust anchors have no checkable issuers. + } + }(); - if(at_self_signed_root && (issuer.is_self_signed() == false)) { + if(restrictions.require_self_signed_trust_anchors() && !issuer.has_value()) { status.insert(Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT); } // This should never happen; it indicates a bug in path building - if(subject.issuer_dn() != issuer.subject_dn()) { + if(issuer.has_value() && subject.issuer_dn() != issuer->subject_dn()) { status.insert(Certificate_Status_Code::CHAIN_NAME_MISMATCH); } @@ -165,28 +354,28 @@ } } - // Only warn, if trusted root is not in time range if configured this way - const bool is_trusted_root_and_time_ignored = - restrictions.ignore_trusted_root_time_range() && at_self_signed_root; + // If so configured, allow trust anchors outside the validity period with + // a warning rather than a hard error + const bool enforce_validity_period = !at_trust_anchor || !restrictions.ignore_trusted_root_time_range(); // Check all certs for valid time range if(validation_time < subject.not_before()) { - if(is_trusted_root_and_time_ignored) { - status.insert(Certificate_Status_Code::TRUSTED_CERT_NOT_YET_VALID); // only warn - } else { + if(enforce_validity_period) { status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID); + } else { + status.insert(Certificate_Status_Code::TRUSTED_CERT_NOT_YET_VALID); // only warn } } if(validation_time > subject.not_after()) { - if(is_trusted_root_and_time_ignored) { - status.insert(Certificate_Status_Code::TRUSTED_CERT_HAS_EXPIRED); // only warn - } else { + if(enforce_validity_period) { status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED); + } else { + status.insert(Certificate_Status_Code::TRUSTED_CERT_HAS_EXPIRED); // only warn } } // Check issuer constraints - if(!issuer.is_CA_cert() && !self_signed_ee_cert) { + if(issuer.has_value() && !issuer->is_CA_cert() && !is_end_entity_trust_anchor) { status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER); } @@ -203,9 +392,11 @@ if(subject.x509_version() < 3 && !extensions_vec.empty()) { status.insert(Certificate_Status_Code::EXT_IN_V1_V2_CERT); } - for(auto& extension : extensions_vec) { + + for(const auto& extension : extensions_vec) { extension.first->validate(subject, issuer, cert_path, cert_status, i); } + if(extensions_vec.size() != extensions.get_extension_oids().size()) { status.insert(Certificate_Status_Code::DUPLICATE_CERT_EXTENSION); } @@ -223,7 +414,7 @@ */ if(subject.subject_dn() != subject.issuer_dn()) { if(max_path_length > 0) { - --max_path_length; + max_path_length -= 1; } else { status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG); } @@ -233,8 +424,8 @@ * If pathLenConstraint is present in the certificate and is less than max_path_length, * set max_path_length to the value of pathLenConstraint. */ - if(subject.path_limit() != Cert_Extension::NO_CERT_PATH_LIMIT && subject.path_limit() < max_path_length) { - max_path_length = subject.path_limit(); + if(auto path_len_constraint = subject.path_length_constraint()) { + max_path_length = std::min(max_path_length, *path_len_constraint); } } @@ -256,7 +447,8 @@ // // 1. Matches a local configuration of OCSP signing authority // for the certificate in question, or - if(restrictions.trusted_ocsp_responders()->certificate_known(signing_cert)) { + if(restrictions.trusted_ocsp_responders() != nullptr && + restrictions.trusted_ocsp_responders()->contains(signing_cert)) { return Certificate_Status_Code::OK; } @@ -274,6 +466,22 @@ // usage extension and is issued by the CA that issued the // certificate in question as stated above. + // Verify the delegated responder was issued by the CA that issued + // the certificate in question (the EKU and signature chain are + // verified by the path validation below). + if(signing_cert.issuer_dn() != ca.subject_dn()) { + return Certificate_Status_Code::OCSP_ISSUER_NOT_TRUSTED; + } else { + // If both key identifiers are available, verify they match to + // handle CAs that share a subject DN but have different keys + // (eg re-keyed or cross-certified CAs). + const auto& aki = signing_cert.authority_key_id(); + const auto& ski = ca.subject_key_id(); + if(!aki.empty() && !ski.empty() && aki != ski) { + return Certificate_Status_Code::OCSP_ISSUER_NOT_TRUSTED; + } + } + // TODO: Implement OCSP revocation check of OCSP signer certificate // Note: This needs special care to prevent endless loops on specifically // forged chains of OCSP responses referring to each other. @@ -302,6 +510,41 @@ return validation_result.result(); } +std::set evaluate_ocsp_response(const OCSP::Response& ocsp_response, + const X509_Certificate& subject, + const X509_Certificate& ca, + const std::vector& cert_path, + const std::vector& certstores, + std::chrono::system_clock::time_point ref_time, + const Path_Validation_Restrictions& restrictions) { + // Handle softfail conditions (eg. OCSP unavailable) + if(auto dummy_status = ocsp_response.dummy_status()) { + return {dummy_status.value()}; + } + + // Find the certificate that signed this OCSP response + auto signing_cert = ocsp_response.find_signing_certificate(ca, restrictions.trusted_ocsp_responders()); + if(!signing_cert) { + return {Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND}; + } + + // Verify the signing certificate is trusted + auto cert_status = verify_ocsp_signing_cert( + signing_cert.value(), ca, concat(ocsp_response.certificates(), cert_path), certstores, ref_time, restrictions); + if(cert_status >= Certificate_Status_Code::FIRST_ERROR_STATUS) { + return {cert_status, Certificate_Status_Code::OCSP_ISSUER_NOT_TRUSTED}; + } + + // Verify the cryptographic signature on the OCSP response + auto sig_status = ocsp_response.verify_signature(signing_cert.value(), restrictions); + if(sig_status != Certificate_Status_Code::OCSP_SIGNATURE_OK) { + return {sig_status}; + } + + // All checks passed, return the certificate's revocation status + return {ocsp_response.status_for(ca, subject, ref_time, restrictions.max_ocsp_age())}; +} + } // namespace CertificatePathStatusCodes PKIX::check_ocsp(const std::vector& cert_path, @@ -316,46 +559,20 @@ CertificatePathStatusCodes cert_status(cert_path.size() - 1); for(size_t i = 0; i != cert_path.size() - 1; ++i) { - std::set& status = cert_status.at(i); - const X509_Certificate& subject = cert_path.at(i); const X509_Certificate& ca = cert_path.at(i + 1); - if(i < ocsp_responses.size() && (ocsp_responses.at(i) != std::nullopt) && - (ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful)) { + if(i < ocsp_responses.size() && ocsp_responses.at(i).has_value() && + ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful) { try { - const auto& ocsp_response = ocsp_responses.at(i); - - if(auto dummy_status = ocsp_response->dummy_status()) { - // handle softfail conditions - status.insert(dummy_status.value()); - } else if(auto signing_cert = - ocsp_response->find_signing_certificate(ca, restrictions.trusted_ocsp_responders()); - !signing_cert) { - status.insert(Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND); - } else if(auto ocsp_signing_cert_status = - verify_ocsp_signing_cert(signing_cert.value(), - ca, - concat(ocsp_response->certificates(), cert_path), - certstores, - ref_time, - restrictions); - ocsp_signing_cert_status > Certificate_Status_Code::FIRST_ERROR_STATUS) { - status.insert(ocsp_signing_cert_status); - status.insert(Certificate_Status_Code::OCSP_ISSUER_NOT_TRUSTED); - } else { - status.insert(ocsp_response->status_for(ca, subject, ref_time, restrictions.max_ocsp_age())); - } + cert_status.at(i) = evaluate_ocsp_response( + ocsp_responses.at(i).value(), subject, ca, cert_path, certstores, ref_time, restrictions); } catch(Exception&) { - status.insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID); + cert_status.at(i).insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID); } } } - while(!cert_status.empty() && cert_status.back().empty()) { - cert_status.pop_back(); - } - return cert_status; } @@ -384,40 +601,34 @@ status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID); } - if(validation_time > crls[i]->next_update()) { + if(crls[i]->next_update().time_is_set() && validation_time > crls[i]->next_update()) { status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED); } auto ca_key = ca.subject_public_key(); if(crls[i]->check_signature(*ca_key) == false) { status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE); - } - - status.insert(Certificate_Status_Code::VALID_CRL_CHECKED); - - if(crls[i]->is_revoked(subject)) { - status.insert(Certificate_Status_Code::CERT_IS_REVOKED); - } + } else { + status.insert(Certificate_Status_Code::VALID_CRL_CHECKED); - const auto dp = subject.crl_distribution_points(); - if(!dp.empty()) { - const auto crl_idp = crls[i]->crl_issuing_distribution_point(); + if(crls[i]->is_revoked(subject)) { + status.insert(Certificate_Status_Code::CERT_IS_REVOKED); + } - if(std::find(dp.begin(), dp.end(), crl_idp) == dp.end()) { + if(!crls[i]->has_matching_distribution_point(subject)) { status.insert(Certificate_Status_Code::NO_MATCHING_CRLDP); } - } - for(const auto& extension : crls[i]->extensions().extensions()) { - // XXX this is wrong - the OID might be defined but the extention not full parsed - // for example see #1652 - - // is the extension critical and unknown? - if(extension.second && !extension.first->oid_of().registered_oid()) { - /* NIST Certificate Path Valiadation Testing document: "When an implementation does not recognize a critical extension in the - * crlExtensions field, it shall assume that identified certificates have been revoked and are no longer valid" - */ - status.insert(Certificate_Status_Code::CERT_IS_REVOKED); + for(const auto& [extension, critical] : crls[i]->extensions().extensions()) { + if(critical) { + /* NIST Certificate Path Validation Testing document: "When an implementation does + * not recognize a critical extension in the crlExtensions field, it shall assume + * that identified certificates have been revoked and are no longer valid" + */ + if(dynamic_cast(extension.get()) != nullptr) { + status.insert(Certificate_Status_Code::CERT_IS_REVOKED); + } + } } } } @@ -444,7 +655,7 @@ std::vector> crls(cert_path.size()); for(size_t i = 0; i != cert_path.size(); ++i) { - for(auto certstore : certstores) { + for(auto* certstore : certstores) { crls[i] = certstore->find_crl_for(cert_path[i]); if(crls[i]) { break; @@ -478,34 +689,35 @@ } for(size_t i = 0; i < to_ocsp; ++i) { - const X509_Certificate& subject = cert_path.at(i); - const X509_Certificate& issuer = cert_path.at(i + 1); + const auto& subject = cert_path.at(i); + const auto& issuer = cert_path.at(i + 1); if(subject.ocsp_responder().empty()) { - ocsp_response_futures.emplace_back(std::async(std::launch::deferred, [&]() -> std::optional { + ocsp_response_futures.emplace_back(std::async(std::launch::deferred, []() -> std::optional { return OCSP::Response(Certificate_Status_Code::OCSP_NO_REVOCATION_URL); })); } else { - ocsp_response_futures.emplace_back(std::async(std::launch::async, [&]() -> std::optional { - OCSP::Request req(issuer, BigInt::from_bytes(subject.serial_number())); + auto ocsp_url = subject.ocsp_responder(); + auto ocsp_req = OCSP::Request(issuer, BigInt::from_bytes(subject.serial_number())); + ocsp_response_futures.emplace_back( + std::async(std::launch::async, [ocsp_url, ocsp_req, timeout]() -> std::optional { + HTTP::Response http; + try { + http = HTTP::POST_sync(ocsp_url, + "application/ocsp-request", + ocsp_req.BER_encode(), + /*redirects*/ 1, + timeout); - HTTP::Response http; - try { - http = HTTP::POST_sync(subject.ocsp_responder(), - "application/ocsp-request", - req.BER_encode(), - /*redirects*/ 1, - timeout); - } catch(std::exception&) { - // log e.what() ? - } - if(http.status_code() != 200) { - return OCSP::Response(Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE); - } - // Check the MIME type? + if(http.status_code() != 200) { + return OCSP::Response(Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE); + } - return OCSP::Response(http.body()); - })); + return OCSP::Response(http.body()); + } catch(std::exception&) { + return OCSP::Response(Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE); + } + })); } } @@ -535,9 +747,9 @@ std::vector> crls(cert_path.size()); for(size_t i = 0; i != cert_path.size(); ++i) { - const std::optional& cert = cert_path.at(i); - for(auto certstore : certstores) { - crls[i] = certstore->find_crl_for(*cert); + const auto& cert = cert_path.at(i); + for(auto* certstore : certstores) { + crls[i] = certstore->find_crl_for(cert); if(crls[i].has_value()) { break; } @@ -552,14 +764,15 @@ so that indexes match up */ future_crls.emplace_back(std::future>()); - } else if(cert->crl_distribution_point().empty()) { + } else if(cert.crl_distribution_point().empty()) { // Avoid creating a thread for this case - future_crls.emplace_back(std::async(std::launch::deferred, [&]() -> std::optional { + future_crls.emplace_back(std::async(std::launch::deferred, []() -> std::optional { throw Not_Implemented("No CRL distribution point for this certificate"); })); } else { - future_crls.emplace_back(std::async(std::launch::async, [&]() -> std::optional { - auto http = HTTP::GET_sync(cert->crl_distribution_point(), + auto cdp = cert.crl_distribution_point(); + future_crls.emplace_back(std::async(std::launch::async, [cdp, timeout]() -> std::optional { + auto http = HTTP::GET_sync(cdp, /*redirects*/ 1, timeout); @@ -583,7 +796,7 @@ auto crl_status = PKIX::check_crl(cert_path, crls, ref_time); - if(crl_store) { + if(crl_store != nullptr) { for(size_t i = 0; i != crl_status.size(); ++i) { if(crl_status[i].contains(Certificate_Status_Code::VALID_CRL_CHECKED)) { // better be non-null, we supposedly validated it @@ -602,234 +815,72 @@ const std::vector& trusted_certstores, const X509_Certificate& end_entity, const std::vector& end_entity_extra) { - if(end_entity.is_self_signed()) { - return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST; - } + CertificatePathBuilder builder(trusted_certstores, end_entity, end_entity_extra); - /* - * This is an inelegant but functional way of preventing path loops - * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate - * fingerprints in the path. If there is a duplicate, we error out. - * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc. - */ - std::set certs_seen; - - cert_path.push_back(end_entity); - certs_seen.insert(end_entity.fingerprint("SHA-256")); - - Certificate_Store_In_Memory ee_extras; - for(const auto& cert : end_entity_extra) { - ee_extras.add_certificate(cert); - } - - // iterate until we reach a root or cannot find the issuer - for(;;) { - const X509_Certificate& last = cert_path.back(); - const X509_DN issuer_dn = last.issuer_dn(); - const std::vector auth_key_id = last.authority_key_id(); - - std::optional issuer; - bool trusted_issuer = false; - - for(Certificate_Store* store : trusted_certstores) { - issuer = store->find_cert(issuer_dn, auth_key_id); - if(issuer) { - trusted_issuer = true; - break; - } - } + std::vector first_path; - if(!issuer) { - // fall back to searching supplemental certs - issuer = ee_extras.find_cert(issuer_dn, auth_key_id); - } + while(auto path = builder.next()) { + BOTAN_ASSERT_NOMSG(path->empty() == false); - if(!issuer) { - return Certificate_Status_Code::CERT_ISSUER_NOT_FOUND; + // Prefer paths ending in self-signed certificates. + if(path->back().is_self_signed()) { + cert_path.insert(cert_path.end(), path->begin(), path->end()); + return Certificate_Status_Code::OK; } - const std::string fprint = issuer->fingerprint("SHA-256"); - - if(certs_seen.contains(fprint)) { - // we already saw this certificate -> loop - return Certificate_Status_Code::CERT_CHAIN_LOOP; + // Save the first path for later just in case we find nothing better + if(first_path.empty()) { + first_path = std::move(*path); } + } - certs_seen.insert(fprint); - cert_path.push_back(*issuer); - - if(issuer->is_self_signed()) { - if(trusted_issuer) { - return Certificate_Status_Code::OK; - } else { - return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST; - } - } + if(!first_path.empty()) { + // We found a path, it's not self-signed but it's as good as can be formed... + cert_path.insert(cert_path.end(), first_path.begin(), first_path.end()); + return Certificate_Status_Code::OK; } -} -/** - * utilities for PKIX::build_all_certificate_paths - */ -namespace { -// -using cert_maybe_trusted = std::pair, bool>; -} // namespace + // Failed to build any path at all + return builder.error(); +} -/** - * Build all possible certificate paths from the end certificate to self-signed trusted roots. - * - * All potentially valid paths are put into the cert_paths vector. If no potentially valid paths are found, - * one of the encountered errors is returned arbitrarily. - * - * todo add a path building function that returns detailed information on errors encountered while building - * the potentially numerous path candidates. - * - * Basically, a DFS is performed starting from the end certificate. A stack (vector) serves to control the DFS. - * At the beginning of each iteration, a pair is popped from the stack that contains (1) the next certificate - * to add to the path (2) a bool that indicates if the certificate is part of a trusted certstore. Ideally, we - * follow the unique issuer of the current certificate until a trusted root is reached. However, the issuer DN + - * authority key id need not be unique among the certificates used for building the path. In such a case, - * we consider all the matching issuers by pushing on the stack for each of them. - * - */ Certificate_Status_Code PKIX::build_all_certificate_paths(std::vector>& cert_paths_out, const std::vector& trusted_certstores, - const std::optional& end_entity, + const X509_Certificate& end_entity, const std::vector& end_entity_extra) { if(!cert_paths_out.empty()) { throw Invalid_Argument("PKIX::build_all_certificate_paths: cert_paths_out must be empty"); } + CertificatePathBuilder builder(trusted_certstores, end_entity, end_entity_extra); - if(end_entity->is_self_signed()) { - return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST; + while(auto path = builder.next()) { + BOTAN_ASSERT_NOMSG(path->empty() == false); + cert_paths_out.push_back(std::move(*path)); } - /* - * Pile up error messages - */ - std::vector stats; - - Certificate_Store_In_Memory ee_extras; - for(const auto& cert : end_entity_extra) { - ee_extras.add_certificate(cert); - } - - /* - * This is an inelegant but functional way of preventing path loops - * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate - * fingerprints in the path. If there is a duplicate, we error out. - * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc. - */ - std::set certs_seen; - - // new certs are added and removed from the path during the DFS - // it is copied into cert_paths_out when we encounter a trusted root - std::vector path_so_far; - - // todo can we assume that the end certificate is not trusted? - std::vector stack = {{end_entity, false}}; - - while(!stack.empty()) { - std::optional last = stack.back().first; - // found a deletion marker that guides the DFS, backtracing - if(last == std::nullopt) { - stack.pop_back(); - std::string fprint = path_so_far.back().fingerprint("SHA-256"); - certs_seen.erase(fprint); - path_so_far.pop_back(); - } - // process next cert on the path - else { - const bool trusted = stack.back().second; - stack.pop_back(); - - // certificate already seen? - const std::string fprint = last->fingerprint("SHA-256"); - if(certs_seen.count(fprint) == 1) { - stats.push_back(Certificate_Status_Code::CERT_CHAIN_LOOP); - // the current path ended in a loop - continue; - } - - // the current path ends here - if(last->is_self_signed()) { - // found a trust anchor - if(trusted) { - cert_paths_out.push_back(path_so_far); - cert_paths_out.back().push_back(*last); - - continue; - } - // found an untrustworthy root - else { - stats.push_back(Certificate_Status_Code::CANNOT_ESTABLISH_TRUST); - continue; - } - } - - const X509_DN issuer_dn = last->issuer_dn(); - const std::vector auth_key_id = last->authority_key_id(); - - // search for trusted issuers - std::vector trusted_issuers; - for(Certificate_Store* store : trusted_certstores) { - auto new_issuers = store->find_all_certs(issuer_dn, auth_key_id); - trusted_issuers.insert(trusted_issuers.end(), new_issuers.begin(), new_issuers.end()); - } - - // search the supplemental certs - std::vector misc_issuers = ee_extras.find_all_certs(issuer_dn, auth_key_id); - - // if we could not find any issuers, the current path ends here - if(trusted_issuers.empty() && misc_issuers.empty()) { - stats.push_back(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND); - continue; - } - - // push the latest certificate onto the path_so_far - path_so_far.push_back(*last); - certs_seen.emplace(fprint); - - // push a deletion marker on the stack for backtracing later - stack.push_back({std::optional(), false}); - - for(const auto& trusted_cert : trusted_issuers) { - stack.push_back({trusted_cert, true}); - } - - for(const auto& misc : misc_issuers) { - stack.push_back({misc, false}); - } - } - } - - // could not construct any potentially valid path - if(cert_paths_out.empty()) { - if(stats.empty()) { - throw Internal_Error("X509 path building failed for unknown reasons"); - } else { - // arbitrarily return the first error - return stats[0]; - } - } else { + if(!cert_paths_out.empty()) { + // Was able to generate at least one potential path return Certificate_Status_Code::OK; + } else { + // Could not construct any potentially valid path... + return builder.error(); } } void PKIX::merge_revocation_status(CertificatePathStatusCodes& chain_status, - const CertificatePathStatusCodes& crl, - const CertificatePathStatusCodes& ocsp, + const CertificatePathStatusCodes& crl_status, + const CertificatePathStatusCodes& ocsp_status, const Path_Validation_Restrictions& restrictions) { if(chain_status.empty()) { throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty"); } for(size_t i = 0; i != chain_status.size() - 1; ++i) { - bool had_crl = false, had_ocsp = false; + bool had_crl = false; + bool had_ocsp = false; - if(i < crl.size() && !crl[i].empty()) { - for(auto&& code : crl[i]) { + if(i < crl_status.size() && !crl_status[i].empty()) { + for(auto&& code : crl_status[i]) { if(code == Certificate_Status_Code::VALID_CRL_CHECKED) { had_crl = true; } @@ -837,8 +888,8 @@ } } - if(i < ocsp.size() && !ocsp[i].empty()) { - for(auto&& code : ocsp[i]) { + if(i < ocsp_status.size() && !ocsp_status[i].empty()) { + for(auto&& code : ocsp_status[i]) { // NO_REVOCATION_URL and OCSP_SERVER_NOT_AVAILABLE are softfail if(code == Certificate_Status_Code::OCSP_RESPONSE_GOOD || code == Certificate_Status_Code::OCSP_NO_REVOCATION_URL || @@ -891,53 +942,100 @@ throw Invalid_Argument("x509_path_validate called with no subjects"); } - X509_Certificate end_entity = end_certs[0]; + const X509_Certificate& end_entity = end_certs[0]; std::vector end_entity_extra; for(size_t i = 1; i < end_certs.size(); ++i) { end_entity_extra.push_back(end_certs[i]); } - std::vector> cert_paths; - Certificate_Status_Code path_building_result = - PKIX::build_all_certificate_paths(cert_paths, trusted_roots, end_entity, end_entity_extra); + const bool require_self_signed = restrictions.require_self_signed_trust_anchors(); - // If we cannot successfully build a chain to a trusted self-signed root, stop now - if(path_building_result != Certificate_Status_Code::OK) { - return Path_Validation_Result(path_building_result); - } + CertificatePathBuilder builder(trusted_roots, end_entity, end_entity_extra, require_self_signed); - std::vector error_results; - // Try validating all the potentially valid paths and return the first one to validate properly - for(auto cert_path : cert_paths) { - CertificatePathStatusCodes status = PKIX::check_chain(cert_path, ref_time, hostname, usage, restrictions); + constexpr size_t max_paths = 50; + constexpr size_t max_verifications = 200; - CertificatePathStatusCodes crl_status = PKIX::check_crl(cert_path, trusted_roots, ref_time); + std::optional first_path_error; + size_t paths_checked = 0; + size_t certs_checked = 0; - CertificatePathStatusCodes ocsp_status; + while(auto cert_path = builder.next()) { + BOTAN_ASSERT_NOMSG(cert_path->empty() == false); - if(!ocsp_resp.empty()) { - ocsp_status = PKIX::check_ocsp(cert_path, ocsp_resp, trusted_roots, ref_time, restrictions); + paths_checked += 1; + certs_checked += cert_path->size(); + if(paths_checked > max_paths || certs_checked > max_verifications) { + first_path_error = Path_Validation_Result(Certificate_Status_Code::EXCEEDED_SEARCH_LIMITS); + break; } - if(ocsp_status.empty() && ocsp_timeout != std::chrono::milliseconds(0)) { + CertificatePathStatusCodes status = PKIX::check_chain(*cert_path, ref_time, hostname, usage, restrictions); + + // Skip revocation checks if the chain already has fatal errors. + if(PKIX::overall_status(status) < Certificate_Status_Code::FIRST_ERROR_STATUS_TO_SKIP_REVOCATION) { + const CertificatePathStatusCodes crl_status = PKIX::check_crl(*cert_path, trusted_roots, ref_time); + + CertificatePathStatusCodes ocsp_status; + + if(!ocsp_resp.empty()) { + ocsp_status = PKIX::check_ocsp(*cert_path, ocsp_resp, trusted_roots, ref_time, restrictions); + } + + if(ocsp_timeout != std::chrono::milliseconds(0)) { + const size_t to_online = restrictions.ocsp_all_intermediates() ? (cert_path->size() - 1) : 1; + bool need_online = false; + for(size_t i = 0; i < to_online; ++i) { + if(i >= ocsp_status.size() || ocsp_status[i].empty()) { + need_online = true; + break; + } + } + + if(need_online) { #if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL) - ocsp_status = PKIX::check_ocsp_online(cert_path, trusted_roots, ref_time, ocsp_timeout, restrictions); + auto online_status = + PKIX::check_ocsp_online(*cert_path, trusted_roots, ref_time, ocsp_timeout, restrictions); + if(ocsp_status.size() < online_status.size()) { + ocsp_status.resize(online_status.size()); + } + for(size_t i = 0; i < online_status.size(); ++i) { + if(ocsp_status[i].empty()) { + ocsp_status[i] = std::move(online_status[i]); + } + } #else - ocsp_status.resize(1); - ocsp_status[0].insert(Certificate_Status_Code::OCSP_NO_HTTP); + if(ocsp_status.size() < to_online) { + ocsp_status.resize(to_online); + } + for(size_t i = 0; i < to_online; ++i) { + if(ocsp_status[i].empty()) { + ocsp_status[i].insert(Certificate_Status_Code::OCSP_NO_HTTP); + } + } #endif - } + } + } - PKIX::merge_revocation_status(status, crl_status, ocsp_status, restrictions); + PKIX::merge_revocation_status(status, crl_status, ocsp_status, restrictions); + } - Path_Validation_Result pvd(status, std::move(cert_path)); + Path_Validation_Result pvd(status, std::move(*cert_path)); if(pvd.successful_validation()) { return pvd; - } else { - error_results.push_back(std::move(pvd)); + } else if(!first_path_error.has_value()) { + // Save the errors from the first path we attempted + first_path_error = std::move(pvd); } } - return error_results[0]; + + if(first_path_error.has_value()) { + // We found at least one path, but none of them verified + // Return arbitrarily the error from the first path attempted + return first_path_error.value(); + } else { + // Failed to build any path at all + return Path_Validation_Result(builder.error()); + } } Path_Validation_Result x509_path_validate(const X509_Certificate& end_cert, @@ -989,13 +1087,15 @@ bool ocsp_intermediates, std::chrono::seconds max_ocsp_age, std::unique_ptr trusted_ocsp_responders, - bool ignore_trusted_root_time_range) : + bool ignore_trusted_root_time_range, + bool require_self_signed_trust_anchors) : m_require_revocation_information(require_rev), m_ocsp_all_intermediates(ocsp_intermediates), m_minimum_key_strength(key_strength), m_max_ocsp_age(max_ocsp_age), m_trusted_ocsp_responders(std::move(trusted_ocsp_responders)), - m_ignore_trusted_root_time_range(ignore_trusted_root_time_range) { + m_ignore_trusted_root_time_range(ignore_trusted_root_time_range), + m_require_self_signed_trust_anchors(require_self_signed_trust_anchors) { if(key_strength <= 80) { m_trusted_hashes.insert("SHA-1"); } diff -Nru botan3-3.7.1+dfsg/src/lib/x509/x509path.h botan3-3.12.0+dfsg/src/lib/x509/x509path.h --- botan3-3.7.1+dfsg/src/lib/x509/x509path.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/x509path.h 2026-05-07 01:38:28.000000000 +0000 @@ -13,7 +13,6 @@ #include #include #include -#include #include #if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL) @@ -52,14 +51,18 @@ * of trusted OCSP responders (additionally to the CA's responders) * @param ignore_trusted_root_time_range if true, validity checks on the * time range of the trusted root certificate only produce warnings + * @param require_self_signed_trust_anchors if true, only self-signed certificates + * are allowed as trust anchors. Trust anchors based on intermediate + * and leaf certificates are forbidden in this case. */ - Path_Validation_Restrictions( + BOTAN_FUTURE_EXPLICIT Path_Validation_Restrictions( bool require_rev = false, size_t minimum_key_strength = 110, bool ocsp_all_intermediates = false, std::chrono::seconds max_ocsp_age = std::chrono::seconds::zero(), std::unique_ptr trusted_ocsp_responders = std::make_unique(), - bool ignore_trusted_root_time_range = false); + bool ignore_trusted_root_time_range = false, + bool require_self_signed_trust_anchors = true); /** * @param require_rev if true, revocation information is required @@ -77,6 +80,9 @@ * of trusted OCSP responders (additionally to the CA's responders) * @param ignore_trusted_root_time_range if true, validity checks on the * time range of the trusted root certificate only produce warnings + * @param require_self_signed_trust_anchors if true, only self-signed certificates + * are allowed as trust anchors. Trust anchors based on intermediate + * and leaf certificates are forbidden in this case. */ Path_Validation_Restrictions( bool require_rev, @@ -85,14 +91,16 @@ const std::set& trusted_hashes, std::chrono::seconds max_ocsp_age = std::chrono::seconds::zero(), std::unique_ptr trusted_ocsp_responders = std::make_unique(), - bool ignore_trusted_root_time_range = false) : + bool ignore_trusted_root_time_range = false, + bool require_self_signed_trust_anchors = true) : m_require_revocation_information(require_rev), m_ocsp_all_intermediates(ocsp_all_intermediates), m_trusted_hashes(trusted_hashes), m_minimum_key_strength(minimum_key_strength), m_max_ocsp_age(max_ocsp_age), m_trusted_ocsp_responders(std::move(trusted_ocsp_responders)), - m_ignore_trusted_root_time_range(ignore_trusted_root_time_range) {} + m_ignore_trusted_root_time_range(ignore_trusted_root_time_range), + m_require_self_signed_trust_anchors(require_self_signed_trust_anchors) {} /** * @return whether revocation information is required @@ -141,6 +149,15 @@ */ bool ignore_trusted_root_time_range() const { return m_ignore_trusted_root_time_range; } + /** + * By default Botan requires trust anchors to be self-signed. + * This prevents using intermediate CA certificates and leaf certificates + * as trust anchors, even if they are included in the Certificate Store. + * This restriction can be removed by setting + * require_self_signed_trust_anchors=false in the constructor. + */ + bool require_self_signed_trust_anchors() const { return m_require_self_signed_trust_anchors; } + private: bool m_require_revocation_information; bool m_ocsp_all_intermediates; @@ -149,6 +166,7 @@ std::chrono::seconds m_max_ocsp_age; std::unique_ptr m_trusted_ocsp_responders; bool m_ignore_trusted_root_time_range; + bool m_require_self_signed_trust_anchors; }; /** @@ -176,7 +194,7 @@ bool successful_validation() const; /** - * @return true iff no warnings occured during validation + * @return true iff no warnings occurred during validation */ bool no_warnings() const; @@ -330,19 +348,42 @@ */ namespace PKIX { -Certificate_Status_Code build_all_certificate_paths(std::vector>& cert_paths, - const std::vector& trusted_certstores, - const std::optional& end_entity, - const std::vector& end_entity_extra); +/** +* Create all certificate paths by identifying all possible routes from the +* end-entity certificate to any certificate in the certificate store list. Paths +* may also end in intermediate or leaf certificates found in the certificate +* stores. +* +* WARNING: The validity (e.g. signatures or constraints) of the output path IS +* NOT checked. +* +* @param cert_paths output parameter to be filled with all discovered certificate paths +* @param trusted_certstores list of certificate stores that contain trusted certificates +* @param end_entity the cert to be validated +* @param end_entity_extra optional list of additional untrusted certs for path building +* @return result of the path building operation (OK or error) +*/ +Certificate_Status_Code BOTAN_PUBLIC_API(3, 11) + build_all_certificate_paths(std::vector>& cert_paths, + const std::vector& trusted_certstores, + const X509_Certificate& end_entity, + const std::vector& end_entity_extra); /** -* Build certificate path +* Same as build_all_certificate_paths but only outputs a single path. If there are +* paths ending in self-signed certificates, these are prioritized over paths ending +* in intermediate or leaf certificates of the certificate store. +* +* WARNING: The validity (e.g. signatures or constraints) of the output path IS +* NOT checked. +* * @param cert_path_out output parameter, cert_path will be appended to this vector * @param trusted_certstores list of certificate stores that contain trusted certificates * @param end_entity the cert to be validated * @param end_entity_extra optional list of additional untrusted certs for path building * @return result of the path building operation (OK or error) */ +BOTAN_DEPRECATED("Use build_all_certificate_paths") Certificate_Status_Code BOTAN_PUBLIC_API(2, 0) build_certificate_path(std::vector& cert_path_out, const std::vector& trusted_certstores, @@ -441,7 +482,7 @@ * @param cert_path path already validated by check_chain * @param trusted_certstores a list of certstores with trusted certs * @param certstore_to_recv_crls optional (nullptr to disable), all CRLs -* retreived will be saved to this cert store. +* retrieved will be saved to this cert store. * @param ref_time whatever time you want to perform the validation against * (normally current system clock) * @param timeout for timing out the responses, though actually this function diff -Nru botan3-3.7.1+dfsg/src/lib/x509/x509self.cpp botan3-3.12.0+dfsg/src/lib/x509/x509self.cpp --- botan3-3.7.1+dfsg/src/lib/x509/x509self.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/x509self.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,8 +7,7 @@ #include -#include -#include +#include #include #include #include @@ -41,14 +40,14 @@ return subject_dn; } -auto create_alt_name_ext(const X509_Cert_Options& opts, Extensions& extensions) { +auto create_alt_name_ext(const X509_Cert_Options& opts, const Extensions& extensions) { AlternativeName subject_alt; /* If the extension was already created in opts.extension we need to - merge the values provied in opts with the values set in the extension. + merge the values provided in opts with the values set in the extension. */ - if(auto ext = extensions.get_extension_object_as()) { + if(const auto* ext = extensions.get_extension_object_as()) { subject_alt = ext->get_alt_name(); } @@ -142,7 +141,9 @@ extensions.replace(create_alt_name_ext(opts, extensions)); - create_alt_name_ext(opts, extensions); + if(!opts.ex_constraints.empty()) { + extensions.add_new(std::make_unique(opts.ex_constraints)); + } return PKCS10_Request::create(key, subject_dn, extensions, hash_fn, rng, opts.padding_scheme, opts.challenge); } diff -Nru botan3-3.7.1+dfsg/src/lib/x509/x509self.h botan3-3.12.0+dfsg/src/lib/x509/x509self.h --- botan3-3.7.1+dfsg/src/lib/x509/x509self.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/x509/x509self.h 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,8 @@ #ifndef BOTAN_X509_SELF_H_ #define BOTAN_X509_SELF_H_ +#include +#include #include #include #include @@ -25,113 +27,116 @@ /** * the subject common name */ - std::string common_name; + std::string common_name; // NOLINT(*non-private-member-variable*) /** - * the subject counry + * the subject country */ - std::string country; + std::string country; // NOLINT(*non-private-member-variable*) /** * the subject organization */ - std::string organization; + std::string organization; // NOLINT(*non-private-member-variable*) /** * the subject organizational unit */ - std::string org_unit; + std::string org_unit; // NOLINT(*non-private-member-variable*) /** * additional subject organizational units. */ - std::vector more_org_units; + std::vector more_org_units; // NOLINT(*non-private-member-variable*) /** * the subject locality */ - std::string locality; + std::string locality; // NOLINT(*non-private-member-variable*) /** * the subject state */ - std::string state; + std::string state; // NOLINT(*non-private-member-variable*) /** * the subject serial number */ - std::string serial_number; + std::string serial_number; // NOLINT(*non-private-member-variable*) /** - * the subject email adress + * the subject email address */ - std::string email; + std::string email; // NOLINT(*non-private-member-variable*) /** * the subject URI */ - std::string uri; + std::string uri; // NOLINT(*non-private-member-variable*) /** * the subject IPv4 address */ - std::string ip; + std::string ip; // NOLINT(*non-private-member-variable*) /** * the subject DNS */ - std::string dns; + std::string dns; // NOLINT(*non-private-member-variable*) /** * additional subject DNS entries. */ - std::vector more_dns; + std::vector more_dns; // NOLINT(*non-private-member-variable*) /** * the subject XMPP */ - std::string xmpp; + std::string xmpp; // NOLINT(*non-private-member-variable*) /** * the subject challenge password */ - std::string challenge; + std::string challenge; // NOLINT(*non-private-member-variable*) /** * the subject notBefore */ - X509_Time start; + X509_Time start; // NOLINT(*non-private-member-variable*) /** * the subject notAfter */ - X509_Time end; + X509_Time end; // NOLINT(*non-private-member-variable*) /** * Indicates whether the certificate request */ - bool is_CA; + bool is_CA = false; // NOLINT(*non-private-member-variable*) /** * Indicates the BasicConstraints path limit */ - size_t path_limit; + size_t path_limit = 0; // NOLINT(*non-private-member-variable*) - std::string padding_scheme; + /** + * Padding scheme to use. If empty uses a default + */ + std::string padding_scheme; // NOLINT(*non-private-member-variable*) /** * The key constraints for the subject public key */ - Key_Constraints constraints; + Key_Constraints constraints; // NOLINT(*non-private-member-variable*) /** * The key extended constraints for the subject public key */ - std::vector ex_constraints; + std::vector ex_constraints; // NOLINT(*non-private-member-variable*) /** * Additional X.509 extensions */ - Extensions extensions; + Extensions extensions; // NOLINT(*non-private-member-variable*) /** * Mark the certificate as a CA certificate and set the path limit. @@ -180,7 +185,7 @@ * parameter would be "common_name/country/organization/organizational_unit". * @param expire_time the expiration time (from the current clock in seconds) */ - X509_Cert_Options(std::string_view opts = "", uint32_t expire_time = 365 * 24 * 60 * 60); + BOTAN_FUTURE_EXPLICIT X509_Cert_Options(std::string_view opts = "", uint32_t expire_time = 365 * 24 * 60 * 60); }; namespace X509 { diff -Nru botan3-3.7.1+dfsg/src/lib/xof/aes_crystals_xof/aes_crystals_xof.h botan3-3.12.0+dfsg/src/lib/xof/aes_crystals_xof/aes_crystals_xof.h --- botan3-3.7.1+dfsg/src/lib/xof/aes_crystals_xof/aes_crystals_xof.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/xof/aes_crystals_xof/aes_crystals_xof.h 2026-05-07 01:38:28.000000000 +0000 @@ -23,7 +23,7 @@ * This is an internal class that is not meant for consumption * by library users. It is therefore not registered in XOF::create(). */ -class BOTAN_TEST_API AES_256_CTR_XOF final : public XOF { +class BOTAN_TEST_API AES_256_CTR_XOF final : public XOF /* NOLINT(*-special-member-functions) */ { public: AES_256_CTR_XOF(); ~AES_256_CTR_XOF() override; @@ -56,7 +56,7 @@ /** * @throws Not_Implemented, use XOF::start() instead of XOF::update() */ - void add_data(std::span) override; + void add_data(std::span input) override; void generate_bytes(std::span output) override; diff -Nru botan3-3.7.1+dfsg/src/lib/xof/aes_crystals_xof/info.txt botan3-3.12.0+dfsg/src/lib/xof/aes_crystals_xof/info.txt --- botan3-3.7.1+dfsg/src/lib/xof/aes_crystals_xof/info.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/xof/aes_crystals_xof/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ - + AES_CRYSTALS_XOF -> 20230816 - + name -> "CRYSTALS XOF" diff -Nru botan3-3.7.1+dfsg/src/lib/xof/ascon_xof128/ascon_xof128.cpp botan3-3.12.0+dfsg/src/lib/xof/ascon_xof128/ascon_xof128.cpp --- botan3-3.7.1+dfsg/src/lib/xof/ascon_xof128/ascon_xof128.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/xof/ascon_xof128/ascon_xof128.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,64 @@ +/* + * Ascon-XOF128 (NIST SP.800-232 Section 5.2) + * + * (C) 2025 Jack Lloyd + * 2025 René Meusel + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#include + +#include + +namespace Botan { + +namespace { + +// NIST SP.800-232 Appendix A (Table 12) +constexpr Ascon_p initial_state_of_ascon_xof_permutation({ + .init_and_final_rounds = 12, + .processing_rounds = 12, + .bit_rate = 64, + .initial_state = + { + 0xda82ce768d9447eb, + 0xcc7ce6c75f1ef969, + 0xe7508fd780085631, + 0x0ee0ea53416b58cc, + 0xe0547524db6f0bde, + }, +}); + +} // namespace + +Ascon_XOF128::Ascon_XOF128() : m_ascon_p(initial_state_of_ascon_xof_permutation) {} + +std::unique_ptr Ascon_XOF128::copy_state() const { + return std::make_unique(*this); +} + +std::unique_ptr Ascon_XOF128::new_object() const { + return std::make_unique(); +} + +void Ascon_XOF128::add_data(std::span input) { + BOTAN_STATE_CHECK(!m_output_generated); + m_ascon_p.absorb(input); +} + +void Ascon_XOF128::generate_bytes(std::span output) { + if(!m_output_generated) { + m_output_generated = true; + m_ascon_p.finish(); + } + + m_ascon_p.squeeze(output); +} + +void Ascon_XOF128::reset() { + m_ascon_p = initial_state_of_ascon_xof_permutation; + m_output_generated = false; +} + +} // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/lib/xof/ascon_xof128/ascon_xof128.h botan3-3.12.0+dfsg/src/lib/xof/ascon_xof128/ascon_xof128.h --- botan3-3.7.1+dfsg/src/lib/xof/ascon_xof128/ascon_xof128.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/xof/ascon_xof128/ascon_xof128.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,48 @@ +/* + * Ascon-XOF128 (NIST SP.800-232 Section 5.2) + * + * (C) 2025 Jack Lloyd + * 2025 René Meusel + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#ifndef BOTAN_ASCON_XOF128_H_ +#define BOTAN_ASCON_XOF128_H_ + +#include +#include + +namespace Botan { + +/** +* Ascon-XOF128 (NIST SP.800-232 Section 5.2) +*/ +class Ascon_XOF128 final : public XOF { + public: + Ascon_XOF128(); + + std::string name() const override { return "Ascon-XOF128"; } + + std::string provider() const override { return m_ascon_p.provider(); } + + size_t block_size() const override { return m_ascon_p.byte_rate(); } + + bool accepts_input() const override { return !m_output_generated; } + + std::unique_ptr copy_state() const override; + std::unique_ptr new_object() const override; + + private: + void add_data(std::span input) override; + void generate_bytes(std::span output) override; + void reset() override; + + private: + Ascon_p m_ascon_p; + bool m_output_generated = false; +}; + +} // namespace Botan + +#endif diff -Nru botan3-3.7.1+dfsg/src/lib/xof/ascon_xof128/info.txt botan3-3.12.0+dfsg/src/lib/xof/ascon_xof128/info.txt --- botan3-3.7.1+dfsg/src/lib/xof/ascon_xof128/info.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/xof/ascon_xof128/info.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,11 @@ + +ASCON_XOF128 -> 20250817 + + + +name -> "Ascon-XOF128" + + + +ascon_perm + diff -Nru botan3-3.7.1+dfsg/src/lib/xof/cshake_xof/cshake_xof.cpp botan3-3.12.0+dfsg/src/lib/xof/cshake_xof/cshake_xof.cpp --- botan3-3.7.1+dfsg/src/lib/xof/cshake_xof/cshake_xof.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/xof/cshake_xof/cshake_xof.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,15 +9,15 @@ #include -#include -#include #include -#include +#include namespace Botan { cSHAKE_XOF::cSHAKE_XOF(size_t capacity, std::vector function_name) : - m_keccak(capacity, 0b00, 2), m_function_name(std::move(function_name)), m_output_generated(false) { + m_keccak({.capacity_bits = capacity, .padding = KeccakPadding::cshake()}), + m_function_name(std::move(function_name)), + m_output_generated(false) { BOTAN_ASSERT_NOMSG(capacity == 256 || capacity == 512); } @@ -25,9 +25,7 @@ cSHAKE_XOF(capacity, std::vector{function_name.begin(), function_name.end()}) {} cSHAKE_XOF::cSHAKE_XOF(size_t capacity, std::string_view function_name) : - cSHAKE_XOF(capacity, - std::vector{cast_char_ptr_to_uint8(function_name.data()), - cast_char_ptr_to_uint8(function_name.data()) + function_name.size()}) {} + cSHAKE_XOF(capacity, as_span_of_bytes(function_name)) {} void cSHAKE_XOF::reset() { m_keccak.clear(); diff -Nru botan3-3.7.1+dfsg/src/lib/xof/cshake_xof/cshake_xof.h botan3-3.12.0+dfsg/src/lib/xof/cshake_xof/cshake_xof.h --- botan3-3.7.1+dfsg/src/lib/xof/cshake_xof/cshake_xof.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/xof/cshake_xof/cshake_xof.h 2026-05-07 01:38:28.000000000 +0000 @@ -67,11 +67,11 @@ */ class BOTAN_TEST_API cSHAKE_128_XOF final : public cSHAKE_XOF { public: - cSHAKE_128_XOF(std::vector function_name) : cSHAKE_XOF(256, std::move(function_name)) {} + explicit cSHAKE_128_XOF(std::vector function_name) : cSHAKE_XOF(256, std::move(function_name)) {} - cSHAKE_128_XOF(std::span function_name) : cSHAKE_XOF(256, function_name) {} + explicit cSHAKE_128_XOF(std::span function_name) : cSHAKE_XOF(256, function_name) {} - cSHAKE_128_XOF(std::string_view function_name) : cSHAKE_XOF(256, function_name) {} + explicit cSHAKE_128_XOF(std::string_view function_name) : cSHAKE_XOF(256, function_name) {} std::string name() const final { return "cSHAKE-128"; } @@ -86,11 +86,11 @@ */ class BOTAN_TEST_API cSHAKE_256_XOF final : public cSHAKE_XOF { public: - cSHAKE_256_XOF(std::vector function_name) : cSHAKE_XOF(512, std::move(function_name)) {} + explicit cSHAKE_256_XOF(std::vector function_name) : cSHAKE_XOF(512, std::move(function_name)) {} - cSHAKE_256_XOF(std::span function_name) : cSHAKE_XOF(512, function_name) {} + explicit cSHAKE_256_XOF(std::span function_name) : cSHAKE_XOF(512, function_name) {} - cSHAKE_256_XOF(std::string_view function_name) : cSHAKE_XOF(512, function_name) {} + explicit cSHAKE_256_XOF(std::string_view function_name) : cSHAKE_XOF(512, function_name) {} std::string name() const final { return "cSHAKE-256"; } diff -Nru botan3-3.7.1+dfsg/src/lib/xof/shake_xof/shake_xof.cpp botan3-3.12.0+dfsg/src/lib/xof/shake_xof/shake_xof.cpp --- botan3-3.7.1+dfsg/src/lib/xof/shake_xof/shake_xof.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/xof/shake_xof/shake_xof.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,9 +9,12 @@ #include +#include + namespace Botan { -SHAKE_XOF::SHAKE_XOF(size_t capacity) : m_keccak(capacity, 0b1111, 4), m_output_generated(false) { +SHAKE_XOF::SHAKE_XOF(size_t capacity) : + m_keccak({.capacity_bits = capacity, .padding = KeccakPadding::shake()}), m_output_generated(false) { BOTAN_ASSERT_NOMSG(capacity == 256 || capacity == 512); } diff -Nru botan3-3.7.1+dfsg/src/lib/xof/shake_xof/shake_xof.h botan3-3.12.0+dfsg/src/lib/xof/shake_xof/shake_xof.h --- botan3-3.7.1+dfsg/src/lib/xof/shake_xof/shake_xof.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/xof/shake_xof/shake_xof.h 2026-05-07 01:38:28.000000000 +0000 @@ -13,8 +13,6 @@ #include #include -#include - namespace Botan { /** @@ -27,7 +25,7 @@ * * @param capacity either 256 or 512 */ - SHAKE_XOF(size_t capacity); + explicit SHAKE_XOF(size_t capacity); public: std::string provider() const final { return m_keccak.provider(); } diff -Nru botan3-3.7.1+dfsg/src/lib/xof/xof.cpp botan3-3.12.0+dfsg/src/lib/xof/xof.cpp --- botan3-3.7.1+dfsg/src/lib/xof/xof.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/xof/xof.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,12 +7,18 @@ */ #include + +#include #include #if defined(BOTAN_HAS_SHAKE_XOF) #include #endif +#if defined(BOTAN_HAS_ASCON_XOF128) + #include +#endif + #include #include @@ -35,6 +41,12 @@ } #endif +#if defined(BOTAN_HAS_ASCON_XOF128) + if(req.algo_name() == "Ascon-XOF128" && req.arg_count() == 0) { + return std::make_unique(); + } +#endif + return nullptr; } diff -Nru botan3-3.7.1+dfsg/src/lib/xof/xof.h botan3-3.12.0+dfsg/src/lib/xof/xof.h --- botan3-3.7.1+dfsg/src/lib/xof/xof.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/lib/xof/xof.h 2026-05-07 01:38:28.000000000 +0000 @@ -12,10 +12,10 @@ #include #include #include - #include #include #include +#include namespace Botan { @@ -26,10 +26,8 @@ * stream of output bits. Typically, it is illegal to call `update()` after * the first call to `output()`. */ -class BOTAN_PUBLIC_API(3, 2) XOF { +class BOTAN_PUBLIC_API(3, 2) XOF /* NOLINT(*special-member-functions) */ { public: - XOF() : m_xof_started(false) {} - virtual ~XOF() = default; /** @@ -163,7 +161,7 @@ */ template std::array output() { - std::array out; + std::array out; // NOLINT(*-member-init) generate_bytes(out); return out; } @@ -186,7 +184,7 @@ * @return the next single output byte */ uint8_t output_next_byte() { - uint8_t out; + uint8_t out = 0; generate_bytes({&out, 1}); return out; } @@ -232,7 +230,7 @@ virtual void reset() = 0; private: - bool m_xof_started; + bool m_xof_started = false; }; } // namespace Botan diff -Nru botan3-3.7.1+dfsg/src/python/botan3.py botan3-3.12.0+dfsg/src/python/botan3.py --- botan3-3.7.1+dfsg/src/python/botan3.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/python/botan3.py 2026-05-07 01:38:28.000000000 +0000 @@ -1,35 +1,40 @@ -#!/usr/bin/env python3 - """ Python wrapper of the botan crypto library https://botan.randombit.net (C) 2015,2017,2018,2019,2023 Jack Lloyd (C) 2015 Uri Blumenthal (extensions and patches) -(C) 2024 Amos Treiber, René Meusel - Rohde & Schwarz Cybersecurity +(C) 2024 Amos Treiber - Rohde & Schwarz Cybersecurity +(C) 2024,2026 René Meusel - Rohde & Schwarz Cybersecurity +(C) 2025 Dominik Schricker Botan is released under the Simplified BSD License (see license.txt) This module uses the ctypes module and is usable by programs running under at least CPython 3.x, and PyPy -It uses botan's ffi module, which exposes a C API. This version -of the Python wrapper requires FFI version 20230403, which was -introduced in Botan 3.0.0 - +It uses botan's ffi module, which exposes a C API. """ +from __future__ import annotations from ctypes import CDLL, CFUNCTYPE, POINTER, byref, create_string_buffer, \ - c_void_p, c_size_t, c_uint8, c_uint32, c_uint64, c_int, c_uint, c_char, c_char_p, addressof -from typing import Callable + c_void_p, c_size_t, c_uint8, c_uint32, c_uint64, c_int, c_uint, c_char, c_char_p, addressof, Array, \ + cast, memmove, py_object, string_at +from typing import Callable, Any, Union from sys import platform from time import strptime, mktime, time as system_time from binascii import hexlify from datetime import datetime from collections.abc import Iterable +from enum import IntEnum -BOTAN_FFI_VERSION = 20240408 +# This Python module requires the FFI API version introduced in Botan 3.11.0 +# +# 3.12.0 - EcScalar/EcPoint, DRBG +# 3.11.0 - XOF API +# 3.10.0 - introduced botan_pubkey_load_ec*_sec1() +BOTAN_FFI_VERSION = 20260506 # # Base exception for all exceptions raised from this module @@ -52,7 +57,7 @@ super().__init__(formatted_msg) - def error_code(self): + def error_code(self) -> int: return self.__rc # @@ -72,7 +77,10 @@ else: # assumed to be some Unix/Linux system possible_dll_names.append('libbotan-3.so') - possible_dll_names += ['libbotan-3.so.%d' % (v) for v in reversed(range(0, 16))] + + min_minor = 8 # minimum supported FFI + max_minor = 32 # arbitrary but probably large enough + possible_dll_names += ['libbotan-3.so.%d' % (v) for v in reversed(range(min_minor, max_minor))] for dll_name in possible_dll_names: try: @@ -89,6 +97,9 @@ VIEW_BIN_CALLBACK = CFUNCTYPE(c_int, c_void_p, POINTER(c_char), c_size_t) VIEW_STR_CALLBACK = CFUNCTYPE(c_int, c_void_p, c_char_p, c_size_t) +RNG_GET_CALLBACK = CFUNCTYPE(c_int, c_void_p, POINTER(c_uint8), c_size_t) +RNG_ADD_ENTROPY_CALLBACK = CFUNCTYPE(c_int, c_void_p, POINTER(c_uint8), c_size_t) +RNG_DESTROY_CALLBACK = CFUNCTYPE(None, c_void_p) def _errcheck(rc, fn, _args): # This errcheck should only be used for int-returning functions @@ -131,7 +142,7 @@ dll.botan_error_last_exception_message.argtypes = [] dll.botan_error_last_exception_message.restype = c_char_p - # These are generated using src/scripts/ffi_decls.py: + # These are generated using src/scripts/dev_tools/gen_ffi_decls.py: ffi_api(dll.botan_constant_time_compare, [c_void_p, c_void_p, c_size_t], [-1]) ffi_api(dll.botan_scrub_mem, [c_void_p, c_size_t]) @@ -143,10 +154,13 @@ # RNG ffi_api(dll.botan_rng_init, [c_void_p, c_char_p]) + ffi_api(dll.botan_rng_init_custom, [c_void_p, c_char_p, c_void_p, RNG_GET_CALLBACK, RNG_ADD_ENTROPY_CALLBACK, RNG_DESTROY_CALLBACK]) ffi_api(dll.botan_rng_get, [c_void_p, c_char_p, c_size_t]) ffi_api(dll.botan_rng_reseed, [c_void_p, c_size_t]) ffi_api(dll.botan_rng_reseed_from_rng, [c_void_p, c_void_p, c_size_t]) ffi_api(dll.botan_rng_add_entropy, [c_void_p, c_char_p, c_size_t]) + ffi_api(dll.botan_rng_init_drbg, [c_void_p, c_char_p, c_char_p, c_size_t]) + ffi_api(dll.botan_rng_generate_with_input, [c_void_p, c_char_p, c_size_t, c_char_p, c_size_t]) ffi_api(dll.botan_rng_destroy, [c_void_p]) # HASH @@ -160,6 +174,17 @@ ffi_api(dll.botan_hash_destroy, [c_void_p]) ffi_api(dll.botan_hash_name, [c_void_p, c_char_p, POINTER(c_size_t)]) + # XOF + ffi_api(dll.botan_xof_init, [c_void_p, c_char_p, c_uint32]) + ffi_api(dll.botan_xof_copy_state, [c_void_p, c_void_p]) + ffi_api(dll.botan_xof_block_size, [c_void_p, POINTER(c_size_t)]) + ffi_api(dll.botan_xof_name, [c_void_p, c_char_p, POINTER(c_size_t)]) + ffi_api(dll.botan_xof_accepts_input, [c_void_p]) + ffi_api(dll.botan_xof_clear, [c_void_p]) + ffi_api(dll.botan_xof_update, [c_void_p, c_char_p, c_size_t]) + ffi_api(dll.botan_xof_output, [c_void_p, c_char_p, c_size_t]) + ffi_api(dll.botan_xof_destroy, [c_void_p]) + # MAC ffi_api(dll.botan_mac_init, [c_void_p, c_char_p, c_uint32]) ffi_api(dll.botan_mac_output_length, [c_void_p, POINTER(c_size_t)]) @@ -225,7 +250,9 @@ ffi_api(dll.botan_mp_init, [c_void_p]) ffi_api(dll.botan_mp_destroy, [c_void_p]) ffi_api(dll.botan_mp_to_hex, [c_void_p, c_char_p]) + ffi_api(dll.botan_mp_view_hex, [c_void_p, c_void_p, VIEW_STR_CALLBACK]) ffi_api(dll.botan_mp_to_str, [c_void_p, c_uint8, c_char_p, POINTER(c_size_t)]) + ffi_api(dll.botan_mp_view_str, [c_void_p, c_uint8, c_void_p, VIEW_STR_CALLBACK]) ffi_api(dll.botan_mp_clear, [c_void_p]) ffi_api(dll.botan_mp_set_from_int, [c_void_p, c_int]) ffi_api(dll.botan_mp_set_from_mp, [c_void_p, c_void_p]) @@ -268,8 +295,61 @@ [c_char_p, POINTER(c_size_t), c_char_p, c_void_p, c_size_t, c_uint32]) ffi_api(dll.botan_bcrypt_is_valid, [c_char_p, c_char_p]) + # OID + ffi_api(dll.botan_oid_destroy, [c_void_p]) + ffi_api(dll.botan_oid_from_string, [c_void_p, c_char_p]) + ffi_api(dll.botan_oid_register, [c_void_p, c_char_p]) + ffi_api(dll.botan_oid_view_string, [c_void_p, c_void_p, VIEW_STR_CALLBACK]) + ffi_api(dll.botan_oid_view_name, [c_void_p, c_void_p, VIEW_STR_CALLBACK]) + ffi_api(dll.botan_oid_equal, [c_void_p, c_void_p]) + ffi_api(dll.botan_oid_cmp, [POINTER(c_int), c_void_p, c_void_p]) + + # EC Group + ffi_api(dll.botan_ec_group_destroy, [c_void_p]) + ffi_api(dll.botan_ec_group_supports_application_specific_group, [POINTER(c_int)]) + ffi_api(dll.botan_ec_group_supports_named_group, [c_char_p, POINTER(c_int)]) + ffi_api(dll.botan_ec_group_from_params, + [c_void_p, c_void_p, c_void_p, c_void_p, c_void_p, c_void_p, c_void_p, c_void_p]) + ffi_api(dll.botan_ec_group_from_ber, [c_void_p, c_char_p, c_size_t]) + ffi_api(dll.botan_ec_group_from_pem, [c_void_p, c_char_p]) + ffi_api(dll.botan_ec_group_from_oid, [c_void_p, c_void_p]) + ffi_api(dll.botan_ec_group_from_name, [c_void_p, c_char_p]) + ffi_api(dll.botan_ec_group_unregister, [c_void_p]) + ffi_api(dll.botan_ec_group_view_der, [c_void_p, c_void_p, VIEW_BIN_CALLBACK]) + ffi_api(dll.botan_ec_group_view_pem, [c_void_p, c_void_p, VIEW_STR_CALLBACK]) + ffi_api(dll.botan_ec_group_get_curve_oid, [c_void_p, c_void_p]) + ffi_api(dll.botan_ec_group_get_p, [c_void_p, c_void_p]) + ffi_api(dll.botan_ec_group_get_a, [c_void_p, c_void_p]) + ffi_api(dll.botan_ec_group_get_b, [c_void_p, c_void_p]) + ffi_api(dll.botan_ec_group_get_g_x, [c_void_p, c_void_p]) + ffi_api(dll.botan_ec_group_get_g_y, [c_void_p, c_void_p]) + ffi_api(dll.botan_ec_group_get_order, [c_void_p, c_void_p]) + ffi_api(dll.botan_ec_group_equal, [c_void_p, c_void_p]) + + # EC Points and Scalars + ffi_api(dll.botan_ec_scalar_destroy, [c_void_p]) + ffi_api(dll.botan_ec_scalar_random, [c_void_p, c_void_p, c_void_p]) + ffi_api(dll.botan_ec_scalar_from_mp, [c_void_p, c_void_p, c_void_p]) + ffi_api(dll.botan_ec_scalar_to_mp, [c_void_p, c_void_p]) + ffi_api(dll.botan_ec_point_destroy, [c_void_p]) + ffi_api(dll.botan_ec_point_identity, [c_void_p, c_void_p]) + ffi_api(dll.botan_ec_point_generator, [c_void_p, c_void_p]) + ffi_api(dll.botan_ec_point_from_xy, [c_void_p, c_void_p, c_void_p, c_void_p]) + ffi_api(dll.botan_ec_point_from_bytes, [c_void_p, c_void_p, c_char_p, c_size_t]) + ffi_api(dll.botan_ec_point_view_x_bytes, [c_void_p, c_void_p, VIEW_BIN_CALLBACK]) + ffi_api(dll.botan_ec_point_view_y_bytes, [c_void_p, c_void_p, VIEW_BIN_CALLBACK]) + ffi_api(dll.botan_ec_point_view_xy_bytes, [c_void_p, c_void_p, VIEW_BIN_CALLBACK]) + ffi_api(dll.botan_ec_point_view_uncompressed, [c_void_p, c_void_p, VIEW_BIN_CALLBACK]) + ffi_api(dll.botan_ec_point_view_compressed, [c_void_p, c_void_p, VIEW_BIN_CALLBACK]) + ffi_api(dll.botan_ec_point_is_identity, [c_void_p]) + ffi_api(dll.botan_ec_point_equal, [c_void_p, c_void_p]) + ffi_api(dll.botan_ec_point_negate, [c_void_p, c_void_p]) + ffi_api(dll.botan_ec_point_add, [c_void_p, c_void_p, c_void_p]) + ffi_api(dll.botan_ec_point_mul, [c_void_p, c_void_p, c_void_p, c_void_p]) + # PUBKEY ffi_api(dll.botan_privkey_create, [c_void_p, c_char_p, c_char_p, c_void_p]) + ffi_api(dll.botan_ec_privkey_create, [c_void_p, c_char_p, c_void_p, c_void_p]) ffi_api(dll.botan_privkey_check_key, [c_void_p, c_void_p, c_uint32], [-1]) ffi_api(dll.botan_privkey_create_rsa, [c_void_p, c_void_p, c_size_t]) ffi_api(dll.botan_privkey_create_ecdsa, [c_void_p, c_void_p, c_char_p]) @@ -319,6 +399,10 @@ ffi_api(dll.botan_pubkey_destroy, [c_void_p]) ffi_api(dll.botan_pubkey_get_field, [c_void_p, c_void_p, c_char_p]) ffi_api(dll.botan_privkey_get_field, [c_void_p, c_void_p, c_char_p]) + ffi_api(dll.botan_pubkey_oid, [c_void_p, c_void_p]) + ffi_api(dll.botan_privkey_oid, [c_void_p, c_void_p]) + ffi_api(dll.botan_privkey_stateful_operation, [c_void_p, POINTER(c_int)]) + ffi_api(dll.botan_privkey_remaining_operations, [c_void_p, POINTER(c_uint64)]) ffi_api(dll.botan_privkey_load_rsa, [c_void_p, c_void_p, c_void_p, c_void_p]) ffi_api(dll.botan_privkey_load_rsa_pkcs1, [c_void_p, c_char_p, c_size_t]) ffi_api(dll.botan_privkey_rsa_get_p, [c_void_p, c_void_p]) @@ -328,6 +412,7 @@ ffi_api(dll.botan_privkey_rsa_get_e, [c_void_p, c_void_p]) ffi_api(dll.botan_privkey_rsa_get_privkey, [c_void_p, c_char_p, POINTER(c_size_t), c_uint32]) ffi_api(dll.botan_pubkey_load_rsa, [c_void_p, c_void_p, c_void_p]) + ffi_api(dll.botan_pubkey_load_rsa_pkcs1, [c_void_p, c_char_p, c_size_t]) ffi_api(dll.botan_pubkey_rsa_get_e, [c_void_p, c_void_p]) ffi_api(dll.botan_pubkey_rsa_get_n, [c_void_p, c_void_p]) ffi_api(dll.botan_privkey_load_dsa, @@ -343,6 +428,9 @@ ffi_api(dll.botan_pubkey_load_dh, [c_void_p, c_void_p, c_void_p, c_void_p]) ffi_api(dll.botan_pubkey_load_elgamal, [c_void_p, c_void_p, c_void_p, c_void_p]) ffi_api(dll.botan_privkey_load_elgamal, [c_void_p, c_void_p, c_void_p, c_void_p]) + ffi_api(dll.botan_ec_privkey_get_private_key, [c_void_p, c_void_p]) + ffi_api(dll.botan_ec_privkey_get_group, [c_void_p, c_void_p]) + ffi_api(dll.botan_ec_pubkey_get_group, [c_void_p, c_void_p]) ffi_api(dll.botan_privkey_load_ed25519, [c_void_p, c_char_p]) ffi_api(dll.botan_pubkey_load_ed25519, [c_void_p, c_char_p]) ffi_api(dll.botan_privkey_ed25519_get_privkey, [c_void_p, c_char_p]) @@ -375,9 +463,12 @@ ffi_api(dll.botan_pubkey_load_classic_mceliece, [c_void_p, c_void_p, c_int, c_char_p]) ffi_api(dll.botan_privkey_load_ecdsa, [c_void_p, c_void_p, c_char_p]) ffi_api(dll.botan_pubkey_load_ecdsa, [c_void_p, c_void_p, c_void_p, c_char_p]) + ffi_api(dll.botan_pubkey_load_ecdsa_sec1, [c_void_p, c_void_p, c_size_t, c_char_p]) ffi_api(dll.botan_pubkey_load_ecdh, [c_void_p, c_void_p, c_void_p, c_char_p]) ffi_api(dll.botan_privkey_load_ecdh, [c_void_p, c_void_p, c_char_p]) + ffi_api(dll.botan_pubkey_load_ecdh_sec1, [c_void_p, c_void_p, c_size_t, c_char_p]) ffi_api(dll.botan_pubkey_load_sm2, [c_void_p, c_void_p, c_void_p, c_char_p]) + ffi_api(dll.botan_pubkey_load_sm2_sec1, [c_void_p, c_void_p, c_size_t, c_char_p]) ffi_api(dll.botan_privkey_load_sm2, [c_void_p, c_void_p, c_char_p]) ffi_api(dll.botan_pubkey_load_sm2_enc, [c_void_p, c_void_p, c_void_p, c_char_p]) ffi_api(dll.botan_privkey_load_sm2_enc, [c_void_p, c_void_p, c_char_p]) @@ -385,6 +476,7 @@ [c_char_p, POINTER(c_size_t), c_char_p, c_char_p, c_void_p]) ffi_api(dll.botan_pubkey_view_ec_public_point, [c_void_p, c_void_p, VIEW_BIN_CALLBACK]) + ffi_api(dll.botan_pubkey_ecc_key_used_explicit_encoding, [c_void_p], [-32]) # PK ffi_api(dll.botan_pk_op_encrypt_create, [c_void_p, c_void_p, c_char_p, c_uint32]) @@ -465,8 +557,23 @@ # X509 CRL ffi_api(dll.botan_x509_crl_load, [c_void_p, c_char_p, c_size_t]) ffi_api(dll.botan_x509_crl_load_file, [c_void_p, c_char_p]) + ffi_api(dll.botan_x509_crl_this_update, [c_void_p, POINTER(c_uint64)]) + ffi_api(dll.botan_x509_crl_next_update, [c_void_p, POINTER(c_uint64)]) + ffi_api(dll.botan_x509_crl_create, + [c_void_p, c_void_p, c_void_p, c_void_p, c_uint64, c_uint32, c_char_p, c_char_p]) + ffi_api(dll.botan_x509_crl_entry_create, [c_void_p, c_void_p, c_int]) + ffi_api(dll.botan_x509_crl_update, + [c_void_p, c_void_p, c_void_p, c_void_p, c_void_p, c_uint64, c_uint32, c_void_p, c_size_t, c_char_p, c_char_p]) + ffi_api(dll.botan_x509_crl_verify_signature, [c_void_p, c_void_p]) ffi_api(dll.botan_x509_crl_destroy, [c_void_p]) ffi_api(dll.botan_x509_is_revoked, [c_void_p, c_void_p], [-1]) + ffi_api(dll.botan_x509_crl_entries, [c_void_p, c_size_t, c_void_p]) + ffi_api(dll.botan_x509_crl_entries_count, [c_void_p, POINTER(c_size_t)]) + ffi_api(dll.botan_x509_crl_entry_reason, [c_void_p, POINTER(c_int)]) + ffi_api(dll.botan_x509_crl_entry_revocation_date, [c_void_p, POINTER(c_uint64)]) + ffi_api(dll.botan_x509_crl_entry_serial_number, [c_void_p, c_void_p]) + ffi_api(dll.botan_x509_crl_entry_view_serial_number, [c_void_p, c_void_p, VIEW_BIN_CALLBACK]) + ffi_api(dll.botan_x509_crl_entry_destroy, [c_void_p]) ffi_api(dll.botan_x509_cert_verify_with_crl, [POINTER(c_int), c_void_p, c_void_p, c_size_t, c_void_p, c_size_t, c_void_p, c_size_t, c_char_p, c_size_t, c_char_p, c_uint64]) @@ -537,12 +644,16 @@ # # Internal utilities # -def _call_fn_returning_sz(fn): +def _call_fn_returning_sz(fn) -> int: sz = c_size_t(0) fn(byref(sz)) return int(sz.value) -def _call_fn_returning_vec(guess, fn): +def _call_fn_returning_bool(fn) -> bool: + res = fn() + return res > 0 + +def _call_fn_returning_vec(guess, fn) -> bytes: buf = create_string_buffer(guess) buf_len = c_size_t(len(buf)) @@ -554,7 +665,7 @@ assert buf_len.value <= len(buf) return buf.raw[0:int(buf_len.value)] -def _call_fn_returning_vec_pair(guess1, guess2, fn): +def _call_fn_returning_vec_pair(guess1, guess2, fn) -> tuple[bytes, bytes]: buf1 = create_string_buffer(guess1) buf1_len = c_size_t(len(buf1)) @@ -585,7 +696,7 @@ _view_bin_fn.output = buf_val[0:buf_len] return 0 -def _call_fn_viewing_vec(fn): +def _call_fn_viewing_vec(fn) -> bytes: fn(None, _view_bin_fn) result = _view_bin_fn.output _view_bin_fn.output = None @@ -596,22 +707,22 @@ _view_str_fn.output = str_val return 0 -def _call_fn_viewing_str(fn): +def _call_fn_viewing_str(fn) -> str: fn(None, _view_str_fn) result = _view_str_fn.output.decode('utf8') _view_str_fn.output = None return result -def _ctype_str(s): +def _ctype_str(s: str | None) -> bytes | None: if s is None: return None assert isinstance(s, str) return s.encode('utf-8') -def _ctype_to_str(s): +def _ctype_to_str(s: bytes) -> str: return s.decode('utf-8') -def _ctype_bits(s): +def _ctype_bits(s: str | bytes) -> bytes: if isinstance(s, bytes): return s elif isinstance(s, str): @@ -625,28 +736,34 @@ def _hex_encode(buf): return hexlify(buf).decode('ascii') + + # # Versioning # -def version_major(): +def version_major() -> int: + """Returns the major number of the library version.""" return int(_DLL.botan_version_major()) -def version_minor(): +def version_minor() -> int: + """Returns the minor number of the library version.""" return int(_DLL.botan_version_minor()) -def version_patch(): +def version_patch() -> int: + """Returns the patch number of the library version.""" return int(_DLL.botan_version_patch()) -def ffi_api_version(): +def ffi_api_version() -> int: return int(_DLL.botan_ffi_api_version()) -def version_string(): +def version_string() -> str: + """Returns a free form version string for the library""" return _DLL.botan_version_string().decode('ascii') # # Utilities # -def const_time_compare(x, y): +def const_time_compare(x: str | bytes, y: str | bytes) -> bool: xbits = _ctype_bits(x) ybits = _ctype_bits(y) len_x = len(xbits) @@ -656,6 +773,9 @@ rc = _DLL.botan_constant_time_compare(xbits, ybits, c_size_t(len_x)) return rc == 0 + +MPILike = Union[str, "MPI", Any, None] #: Alias for parameters that get turned into an MPI. + # # TPM2 # @@ -673,9 +793,13 @@ return self.__obj class TPM2Context(TPM2Object): - """TPM 2.0 Context object""" + """TPM 2.0 Context object + + Create a TPM 2.0 context optionally with a TCTI name and configuration, + separated by a colon, or as separate parameters. + """ - def __init__(self, tcti_name_maybe_with_conf: str = None, tcti_conf: str = None): + def __init__(self, tcti_name_maybe_with_conf: str | None = None, tcti_conf: str | None = None): """Construct a TPM2Context object with optional TCTI name and configuration.""" obj = c_void_p(0) @@ -694,7 +818,7 @@ rc = _DLL.botan_tpm2_supports_crypto_backend() return rc == 1 - def enable_botan_crypto_backend(self, rng): + def enable_botan_crypto_backend(self, rng: RandomNumberGenerator): """Enables the Botan-based crypto backend. The passed rng MUST NOT be dependent on the TPM.""" # By keeping a reference to the passed-in RNG object, we make sure @@ -728,19 +852,81 @@ class TPM2UnauthenticatedSession(TPM2Session): - """Session object that is not bound to any authenication credential. + """Session object that is not bound to any authentication credential. It provides basic parameter encryption between the application and the TPM.""" + def __init__(self, ctx: TPM2Context): obj = c_void_p(0) _DLL.botan_tpm2_unauthenticated_session_init(byref(obj), ctx.handle_()) super().__init__(obj) +class _CustomRngContext: + def __init__(self, get_cb: Callable[[int], bytes], add_entropy_cb: Callable[[bytes], None] | None): + if not callable(get_cb): + raise BotanException("Custom RNG requires a callable get_callback= argument") + if add_entropy_cb is not None and not callable(add_entropy_cb): + raise BotanException("add_entropy_callback must be callable if provided") + + self.get_callback = get_cb + self.add_entropy_callback = add_entropy_cb + + @staticmethod + def _translate_exceptions(fn): + def wrapper(*args, **kwargs): + try: + return fn(*args, **kwargs) + except BotanException as e: + return e.error_code() + except Exception: + return -100 # internal error + return wrapper + + @staticmethod + @RNG_GET_CALLBACK + @_translate_exceptions + def _custom_get(ctx, out, out_len): + py_ctx = cast(ctx, POINTER(py_object)).contents.value + result = bytes(py_ctx.get_callback(out_len)) + if len(result) != out_len: + return -1 # Invalid input + memmove(out, result, out_len) + return 0 # success + + @staticmethod + @RNG_ADD_ENTROPY_CALLBACK + @_translate_exceptions + def _custom_add_entropy(ctx, entropy, length): + py_ctx = cast(ctx, POINTER(py_object)).contents.value + if py_ctx.add_entropy_callback is not None: + data = bytes(string_at(entropy, length)) + py_ctx.add_entropy_callback(data) + return 0 # success + + @staticmethod + @RNG_DESTROY_CALLBACK + @_translate_exceptions + def _custom_destroy(ctx): + pass + # # RNG # class RandomNumberGenerator: + """Previously ``rng`` + + Type 'user' also allowed (userspace HMAC_DRBG seeded from system + rng). The system RNG is very cheap to create, as just a single file + handle or CSP handle is kept open, from first use until shutdown, + no matter how many 'system' rng instances are created. Thus it is + easy to use the RNG in a one-off way, with `botan.RandomNumberGenerator().get(32)`. + + When Botan is configured with TPM 2.0 support, also 'tpm2' is allowed + to instantiate a TPM-backed RNG. Note that this requires passing + additional named arguments ``tpm2_context=`` with a ``TPM2Context`` and + (optionally) ``tpm2_sessions=`` with one or more ``TPM2Session`` objects.""" + # Can also use type "system" - def __init__(self, rng_type='system', **kwargs): + def __init__(self, rng_type: str = 'system', **kwargs): """Constructs a RandomNumberGenerator of type rng_type Available RNG types are:: @@ -751,9 +937,29 @@ * 'hwrng': Adapter to an available hardware RNG (platform dependent) * 'tpm2': Adapter to a TPM 2.0 RNG (needs additional named arguments tpm2_context= and, optionally, tpm2_sessions=) + * 'custom': Adapter to user-defined callbacks + (needs additional named arguments get_callback= and, optionally, add_entropy_callback=) """ + obj = kwargs.pop("_obj", None) + if isinstance(obj, c_void_p): + self.__obj = obj + return + self.__obj = c_void_p(0) - if rng_type == 'tpm2': + if rng_type == 'custom': + custom_rng_ctx = _CustomRngContext(kwargs.pop("get_callback", None), kwargs.pop("add_entropy_callback", None)) + if kwargs: + raise BotanException("Unexpected arguments for custom RNG: %s" % (", ".join(kwargs.keys()))) + self._custom_rng_ctx_ref = py_object(custom_rng_ctx) + _DLL.botan_rng_init_custom( + byref(self.__obj), + _ctype_str("Python Custom RNG"), + cast(byref(self._custom_rng_ctx_ref), c_void_p), + _CustomRngContext._custom_get, + _CustomRngContext._custom_add_entropy, + _CustomRngContext._custom_destroy, + ) + elif rng_type == 'tpm2': ctx = kwargs.pop("tpm2_context", None) if not ctx or not isinstance(ctx, TPM2Context): raise BotanException("Cannot instantiate a TPM2-based RNG without a TPM2 context, pass tpm2_context= argument?") @@ -772,27 +978,48 @@ def handle_(self): return self.__obj - def reseed(self, bits=256): + def reseed(self, bits: int = 256): + """Meaningless on system RNG, on userspace RNG causes a reseed/rekey""" _DLL.botan_rng_reseed(self.__obj, bits) - def reseed_from_rng(self, source_rng, bits=256): + def reseed_from_rng(self, source_rng: RandomNumberGenerator, bits: int = 256): + """Take bits from the source RNG and use it to seed ``self``""" _DLL.botan_rng_reseed_from_rng(self.__obj, source_rng.handle_(), bits) - def add_entropy(self, seed): + def add_entropy(self, seed: str | bytes): + """Add some unpredictable seed data to the RNG""" seedbits = _ctype_bits(seed) _DLL.botan_rng_add_entropy(self.__obj, seedbits, len(seedbits)) - def get(self, length): + def get(self, length: int) -> bytes: + """Return some bytes""" out = create_string_buffer(length) - l = c_size_t(length) - _DLL.botan_rng_get(self.__obj, out, l) + _DLL.botan_rng_get(self.__obj, out, c_size_t(length)) return _ctype_bufout(out) + def generate_with_input(self, length: int, additional_input: bytes) -> bytes: + """Generate random bytes with additional input mixed in (for DRBGs)""" + out = create_string_buffer(length) + _DLL.botan_rng_generate_with_input( + self.__obj, out, c_size_t(length), + additional_input, c_size_t(len(additional_input))) + return _ctype_bufout(out) + + @staticmethod + def drbg(drbg_name: str, seed: bytes) -> RandomNumberGenerator: + """Create a seeded DRBG (e.g. "HMAC_DRBG(SHA-256)") + + The seed should be the concatenation of entropy, nonce, and + personalization string.""" + obj = c_void_p(0) + _DLL.botan_rng_init_drbg(byref(obj), _ctype_str(drbg_name), seed, c_size_t(len(seed))) + return RandomNumberGenerator(_obj=obj) + # # Block cipher # class BlockCipher: - def __init__(self, algo): + def __init__(self, algo: str | c_void_p): if isinstance(algo, c_void_p): self.__obj = algo @@ -815,10 +1042,10 @@ def __del__(self): _DLL.botan_block_cipher_destroy(self.__obj) - def set_key(self, key): + def set_key(self, key: bytes): _DLL.botan_block_cipher_set_key(self.__obj, key, len(key)) - def encrypt(self, pt): + def encrypt(self, pt: bytes) -> Array[c_char]: if len(pt) % self.block_size() != 0: raise Exception("Invalid input must be multiple of block size") @@ -827,7 +1054,7 @@ _DLL.botan_block_cipher_encrypt_blocks(self.__obj, pt, output, blocks) return output - def decrypt(self, ct): + def decrypt(self, ct: bytes) -> Array[c_char]: if len(ct) % self.block_size() != 0: raise Exception("Invalid input must be multiple of block size") @@ -836,22 +1063,22 @@ _DLL.botan_block_cipher_decrypt_blocks(self.__obj, ct, output, blocks) return output - def algo_name(self): + def algo_name(self) -> str: return _call_fn_returning_str(32, lambda b, bl: _DLL.botan_block_cipher_name(self.__obj, b, bl)) def clear(self): _DLL.botan_block_cipher_clear(self.__obj) - def block_size(self): + def block_size(self) -> int: return self.__block_size - def minimum_keylength(self): + def minimum_keylength(self) -> int: return self.__min_keylen - def maximum_keylength(self): + def maximum_keylength(self) -> int: return self.__max_keylen - def keylength_modulo(self): + def keylength_modulo(self) -> int: return self.__mod_keylen @@ -859,7 +1086,10 @@ # Hash function # class HashFunction: - def __init__(self, algo): + """Previously ``hash_function``""" + + def __init__(self, algo: str | c_void_p): + """The ``algo`` param is a string (eg 'SHA-1', 'SHA-384', 'BLAKE2b')""" if isinstance(algo, c_void_p): self.__obj = algo @@ -868,43 +1098,103 @@ self.__obj = c_void_p(0) _DLL.botan_hash_init(byref(self.__obj), _ctype_str(algo), flags) - self.__output_length = _call_fn_returning_sz(lambda l: _DLL.botan_hash_output_length(self.__obj, l)) - self.__block_size = _call_fn_returning_sz(lambda l: _DLL.botan_hash_block_size(self.__obj, l)) + self.__output_length = _call_fn_returning_sz(lambda length: _DLL.botan_hash_output_length(self.__obj, length)) + self.__block_size = _call_fn_returning_sz(lambda length: _DLL.botan_hash_block_size(self.__obj, length)) def __del__(self): _DLL.botan_hash_destroy(self.__obj) - def copy_state(self): + def copy_state(self) -> HashFunction: copy = c_void_p(0) _DLL.botan_hash_copy_state(byref(copy), self.__obj) return HashFunction(copy) - def algo_name(self): + def algo_name(self) -> str: + """Returns the name of this algorithm""" return _call_fn_returning_str(32, lambda b, bl: _DLL.botan_hash_name(self.__obj, b, bl)) def clear(self): + """Clear state""" _DLL.botan_hash_clear(self.__obj) - def output_length(self): + def output_length(self) -> int: + """Return output length in bytes""" return self.__output_length - def block_size(self): + def block_size(self) -> int: + """Return block size in bytes""" return self.__block_size - def update(self, x): + def update(self, x: str | bytes): + """Add some input""" bits = _ctype_bits(x) _DLL.botan_hash_update(self.__obj, bits, len(bits)) - def final(self): + def final(self) -> bytes: + """Returns the hash of all input provided, resets for another message.""" out = create_string_buffer(self.output_length()) _DLL.botan_hash_final(self.__obj, out) return _ctype_bufout(out) # +# eXtensible Output Functions +# +class XOF: + """eXtensible Output Function (XOF). The ``algo`` param is a string (e.g 'SHAKE-256', 'Ascon-XOF128')""" + + def __init__(self, algo: str | c_void_p): + if isinstance(algo, c_void_p): + self.__obj = algo + else: + flags = c_uint32(0) # always zero in this API version + self.__obj = c_void_p(0) + _DLL.botan_xof_init(byref(self.__obj), _ctype_str(algo), flags) + + def __del__(self): + _DLL.botan_xof_destroy(self.__obj) + + def copy_state(self) -> XOF: + copy = c_void_p(0) + _DLL.botan_xof_copy_state(byref(copy), self.__obj) + return XOF(copy) + + def clear(self): + """Clear state""" + _DLL.botan_xof_clear(self.__obj) + + def update(self, x: str | bytes): + """Add some input""" + bits = _ctype_bits(x) + _DLL.botan_xof_update(self.__obj, bits, len(bits)) + + def output(self, length: int) -> bytes: + """Returns `length` bytes of output from the XOF after all input was provided""" + if length <= 0: + return b'' + buf = create_string_buffer(length) + _DLL.botan_xof_output(self.__obj, buf, c_size_t(length)) + return _ctype_bufout(buf) + + def accepts_input(self) -> bool: + """Returns True if the XOF can accept more input, False if it is in output-only mode.""" + return _call_fn_returning_bool(lambda: _DLL.botan_xof_accepts_input(self.__obj)) + + def algo_name(self) -> str: + """Returns the name of this algorithm""" + return _call_fn_returning_str(32, lambda b, bl: _DLL.botan_xof_name(self.__obj, b, bl)) + + def block_size(self) -> int: + """Return block size in bytes""" + return _call_fn_returning_sz(lambda length: _DLL.botan_xof_block_size(self.__obj, length)) + +# # Message authentication codes # class MsgAuthCode: - def __init__(self, algo): + """Previously ``message_authentication_code``""" + + def __init__(self, algo: str): + """Algo is a string (eg 'HMAC(SHA-256)', 'Poly1305', 'CMAC(AES-256)')""" flags = c_uint32(0) # always zero in this API version self.__obj = c_void_p(0) _DLL.botan_mac_init(byref(self.__obj), _ctype_str(algo), flags) @@ -926,40 +1216,52 @@ _DLL.botan_mac_destroy(self.__obj) def clear(self): + """Clear internal state including the key""" _DLL.botan_mac_clear(self.__obj) - def algo_name(self): + def algo_name(self) -> str: + """Returns the name of this algorithm""" return _call_fn_returning_str(32, lambda b, bl: _DLL.botan_mac_name(self.__obj, b, bl)) - def output_length(self): + def output_length(self) -> int: + """Return the output length in bytes""" return self.__output_length - def minimum_keylength(self): + def minimum_keylength(self) -> int: return self.__min_keylen - def maximum_keylength(self): + def maximum_keylength(self) -> int: return self.__max_keylen - def keylength_modulo(self): + def keylength_modulo(self) -> int: return self.__mod_keylen - def set_key(self, key): + def set_key(self, key: bytes): + """Set the key""" _DLL.botan_mac_set_key(self.__obj, key, len(key)) - def set_nonce(self, nonce): + def set_nonce(self, nonce: bytes): _DLL.botan_mac_set_nonce(self.__obj, nonce, len(nonce)) - def update(self, x): + def update(self, x: str | bytes): + """Add some input""" bits = _ctype_bits(x) _DLL.botan_mac_update(self.__obj, bits, len(bits)) - def final(self): + def final(self) -> bytes: + """Returns the MAC of all input provided, resets for another message with the same key.""" out = create_string_buffer(self.output_length()) _DLL.botan_mac_final(self.__obj, out) return _ctype_bufout(out) class SymmetricCipher: - def __init__(self, algo, encrypt=True): + """Previously ``cipher``""" + + def __init__(self, algo: str, encrypt: bool = True): + """The algorithm is specified as a string (eg 'AES-128/GCM', + 'Serpent/OCB(12)', 'Threefish-512/EAX'). + + Set `encrypt` to False for decryption""" flags = 0 if encrypt else 1 self.__obj = c_void_p(0) _DLL.botan_cipher_init(byref(self.__obj), _ctype_str(algo), flags) @@ -969,50 +1271,56 @@ def __del__(self): _DLL.botan_cipher_destroy(self.__obj) - def algo_name(self): + def algo_name(self) -> str: + """Returns the name of this algorithm""" return _call_fn_returning_str(32, lambda b, bl: _DLL.botan_cipher_name(self.__obj, b, bl)) - def default_nonce_length(self): - l = c_size_t(0) - _DLL.botan_cipher_get_default_nonce_length(self.__obj, byref(l)) - return l.value - - def update_granularity(self): - l = c_size_t(0) - _DLL.botan_cipher_get_update_granularity(self.__obj, byref(l)) - return l.value - - def ideal_update_granularity(self): - l = c_size_t(0) - _DLL.botan_cipher_get_ideal_update_granularity(self.__obj, byref(l)) - return l.value + def default_nonce_length(self) -> int: + """Returns default nonce length""" + length = c_size_t(0) + _DLL.botan_cipher_get_default_nonce_length(self.__obj, byref(length)) + return length.value + + def update_granularity(self) -> int: + """Returns update block size. Call to update() must provide input of exactly this many bytes""" + length = c_size_t(0) + _DLL.botan_cipher_get_update_granularity(self.__obj, byref(length)) + return length.value + + def ideal_update_granularity(self) -> int: + length = c_size_t(0) + _DLL.botan_cipher_get_ideal_update_granularity(self.__obj, byref(length)) + return length.value - def key_length(self): + def key_length(self) -> tuple[int, int]: kmin = c_size_t(0) kmax = c_size_t(0) _DLL.botan_cipher_query_keylen(self.__obj, byref(kmin), byref(kmax)) return kmin.value, kmax.value - def minimum_keylength(self): - l = c_size_t(0) - _DLL.botan_cipher_get_keyspec(self.__obj, byref(l), None, None) - return l.value - - def maximum_keylength(self): - l = c_size_t(0) - _DLL.botan_cipher_get_keyspec(self.__obj, None, byref(l), None) - return l.value - - def tag_length(self): - l = c_size_t(0) - _DLL.botan_cipher_get_tag_length(self.__obj, byref(l)) - return l.value + def minimum_keylength(self) -> int: + length = c_size_t(0) + _DLL.botan_cipher_get_keyspec(self.__obj, byref(length), None, None) + return length.value + + def maximum_keylength(self) -> int: + length = c_size_t(0) + _DLL.botan_cipher_get_keyspec(self.__obj, None, byref(length), None) + return length.value + + def tag_length(self) -> int: + """Returns the tag length (0 for unauthenticated modes)""" + length = c_size_t(0) + _DLL.botan_cipher_get_tag_length(self.__obj, byref(length)) + return length.value - def is_authenticated(self): + def is_authenticated(self) -> bool: + """Returns True if this is an AEAD mode""" rc = _DLL.botan_cipher_is_authenticated(self.__obj) return rc == 1 - def valid_nonce_length(self, nonce_len): + def valid_nonce_length(self, nonce_len) -> bool: + """Returns True if nonce_len is a valid nonce len for this mode""" rc = _DLL.botan_cipher_valid_nonce_length(self.__obj, nonce_len) return rc == 1 @@ -1020,18 +1328,22 @@ _DLL.botan_cipher_reset(self.__obj) def clear(self): + """Resets all state""" _DLL.botan_cipher_clear(self.__obj) - def set_key(self, key): + def set_key(self, key: bytes): + """Set the key""" _DLL.botan_cipher_set_key(self.__obj, key, len(key)) - def set_assoc_data(self, ad): + def set_assoc_data(self, ad: bytes): + """Sets the associated data. Fails if this is not an AEAD mode""" _DLL.botan_cipher_set_associated_data(self.__obj, ad, len(ad)) - def start(self, nonce): + def start(self, nonce: bytes): + """Start processing a message using nonce""" _DLL.botan_cipher_start(self.__obj, nonce, len(nonce)) - def _update(self, txt, final): + def _update(self, txt: str | bytes | None, final: bool): inp = txt if txt else '' bits = _ctype_bits(inp) @@ -1059,15 +1371,20 @@ assert inp_consumed.value == inp_sz.value return out.raw[0:int(out_written.value)] - def update(self, txt): + def update(self, txt: str | bytes): + """Consumes input text and returns output. Input text must be of update_granularity() length. + Alternately, always call finish with the entire message, avoiding calls to update entirely""" return self._update(txt, False) - def finish(self, txt=None): + def finish(self, txt: str | bytes | None = None): + """Finish processing (with an optional final input). May throw if message authentication checks fail, + in which case all plaintext previously processed must be discarded. + You may call finish() with the entire message""" return self._update(txt, True) -def bcrypt(passwd, rng_obj, work_factor=10): +def bcrypt(passwd: str, rng_obj: RandomNumberGenerator, work_factor=10): """ - Bcrypt password hashing + Provided the password and an RNG object, returns a bcrypt string """ out_len = c_size_t(64) out = create_string_buffer(out_len.value) @@ -1079,14 +1396,25 @@ b = b[:-1] return _ctype_to_str(b) -def check_bcrypt(passwd, passwd_hash): +def check_bcrypt(passwd: str, passwd_hash: str): + """ Check a bcrypt hash against the provided password, returning True iff the password matches.""" rc = _DLL.botan_bcrypt_is_valid(_ctype_str(passwd), _ctype_str(passwd_hash)) return rc == 0 # # PBKDF # -def pbkdf(algo, password, out_len, iterations=100000, salt=None): +def pbkdf(algo: str, password: str, out_len: int, iterations: int = 100000, salt: bytes | None = None) -> tuple[bytes, int, bytes]: + """Runs a PBKDF2 algo specified as a string (eg 'PBKDF2(SHA-256)', + 'PBKDF2(CMAC(Blowfish))'). Runs with specified iterations, with + meaning depending on the algorithm. The salt can be provided or + otherwise is randomly chosen. In any case it is returned from the + call. + + Returns out_len bytes of output (or potentially less depending on + the algorithm and the size of the request). + + Returns tuple of salt, iterations, and psk""" if salt is None: salt = RandomNumberGenerator().get(12) @@ -1098,7 +1426,10 @@ salt, len(salt)) return (salt, iterations, out_buf.raw) -def pbkdf_timed(algo, password, out_len, ms_to_run=300, salt=None): +def pbkdf_timed(algo: str, password: str, out_len: int, ms_to_run: int = 300, salt: bytes | None = None) -> tuple[bytes, int, bytes]: + """Runs for as many iterations as needed to consumed ms_to_run + milliseconds on whatever we're running on. Returns tuple of salt, + iterations, and psk""" if salt is None: salt = RandomNumberGenerator().get(12) @@ -1115,9 +1446,11 @@ # # Scrypt # -def scrypt(out_len, password, salt, n=1024, r=8, p=8): +def scrypt(out_len: int, password: str, salt: str | bytes, n: int = 1024, r: int = 8, p: int = 8) -> bytes: + """Runs Scrypt key derivation function over the specified password + and salt using Scrypt parameters N, r, p.""" out_buf = create_string_buffer(out_len) - passbits = _ctype_str(password) + passbits = _ctype_bits(password) saltbits = _ctype_bits(salt) _DLL.botan_pwdhash(_ctype_str("Scrypt"), n, r, p, @@ -1136,9 +1469,9 @@ # p specifies the parallelism # # returns an output of out_len bytes -def argon2(variant, out_len, password, salt, m=256, t=1, p=1): +def argon2(variant: str, out_len: int, password: str, salt: str | bytes, m: int = 256, t: int = 1, p: int = 1) -> bytes: out_buf = create_string_buffer(out_len) - passbits = _ctype_str(password) + passbits = _ctype_bits(password) saltbits = _ctype_bits(salt) _DLL.botan_pwdhash(_ctype_str(variant), m, t, p, @@ -1151,7 +1484,9 @@ # # KDF # -def kdf(algo, secret, out_len, salt, label): +def kdf(algo: str, secret: bytes, out_len: int, salt: bytes, label: bytes) -> bytes: + """Performs a key derivation function (such as "HKDF(SHA-384)") over the provided secret + and salt values. Returns a value of the specified length.""" out_buf = create_string_buffer(out_len) out_sz = c_size_t(out_len) _DLL.botan_kdf(_ctype_str(algo), out_buf, out_sz, @@ -1164,113 +1499,151 @@ # Public key # class PublicKey: # pylint: disable=invalid-name + """Previously ``public_key``""" - def __init__(self, obj=c_void_p(0)): + def __init__(self, obj: c_void_p | None = None): + if not obj: + obj = c_void_p(0) self.__obj = obj @classmethod - def load(cls, val): - obj = c_void_p(0) + def load(cls, val: str | bytes) -> PublicKey: + """Load a public key. The value should be a PEM or DER blob.""" + pub = PublicKey() bits = _ctype_bits(val) - _DLL.botan_pubkey_load(byref(obj), bits, len(bits)) - return PublicKey(obj) + _DLL.botan_pubkey_load(byref(pub.handle_()), bits, len(bits)) + return pub @classmethod - def load_rsa(cls, n, e): - obj = c_void_p(0) + def load_rsa(cls, n: MPILike, e: MPILike) -> PublicKey: + """Load an RSA public key giving the modulus and public exponent as integers.""" + pub = PublicKey() n = MPI(n) e = MPI(e) - _DLL.botan_pubkey_load_rsa(byref(obj), n.handle_(), e.handle_()) - return PublicKey(obj) + _DLL.botan_pubkey_load_rsa(byref(pub.handle_()), n.handle_(), e.handle_()) + return pub @classmethod - def load_dsa(cls, p, q, g, y): - obj = c_void_p(0) + def load_dsa(cls, p: MPILike, q: MPILike, g: MPILike, y: MPILike) -> PublicKey: + """Load a DSA public key giving the parameters and public value as integers.""" + pub = PublicKey() p = MPI(p) q = MPI(q) g = MPI(g) y = MPI(y) - _DLL.botan_pubkey_load_dsa(byref(obj), p.handle_(), q.handle_(), g.handle_(), y.handle_()) - return PublicKey(obj) + _DLL.botan_pubkey_load_dsa(byref(pub.handle_()), p.handle_(), q.handle_(), g.handle_(), y.handle_()) + return pub @classmethod - def load_dh(cls, p, g, y): - obj = c_void_p(0) + def load_dh(cls, p: MPILike, g: MPILike, y: MPILike) -> PublicKey: + """Load a Diffie-Hellman public key giving the parameters and public value as integers.""" + pub = PublicKey() p = MPI(p) g = MPI(g) y = MPI(y) - _DLL.botan_pubkey_load_dh(byref(obj), p.handle_(), g.handle_(), y.handle_()) - return PublicKey(obj) + _DLL.botan_pubkey_load_dh(byref(pub.handle_()), p.handle_(), g.handle_(), y.handle_()) + return pub @classmethod - def load_elgamal(cls, p, q, g, y): - obj = c_void_p(0) + def load_elgamal(cls, p: MPILike, q: MPILike, g: MPILike, y: MPILike) -> PublicKey: + """Load an ElGamal public key giving the parameters and public value as integers.""" + pub = PublicKey() p = MPI(p) q = MPI(q) g = MPI(g) y = MPI(y) - _DLL.botan_pubkey_load_elgamal(byref(obj), p.handle_(), q.handle_(), g.handle_(), y.handle_()) - return PublicKey(obj) + _DLL.botan_pubkey_load_elgamal(byref(pub.handle_()), p.handle_(), q.handle_(), g.handle_(), y.handle_()) + return pub @classmethod - def load_ecdsa(cls, curve, pub_x, pub_y): - obj = c_void_p(0) + def load_ecdsa(cls, curve: str, pub_x: MPILike, pub_y: MPILike) -> PublicKey: + """Load an ECDSA public key giving the curve as a string (like "secp256r1") and the public point + as a pair of integers giving the affine coordinates.""" + pub = PublicKey() pub_x = MPI(pub_x) pub_y = MPI(pub_y) - _DLL.botan_pubkey_load_ecdsa(byref(obj), pub_x.handle_(), pub_y.handle_(), _ctype_str(curve)) - return PublicKey(obj) + _DLL.botan_pubkey_load_ecdsa(byref(pub.handle_()), pub_x.handle_(), pub_y.handle_(), _ctype_str(curve)) + return pub @classmethod - def load_ecdh(cls, curve, pub_x, pub_y): - obj = c_void_p(0) + def load_ecdsa_sec1(cls, curve: str, sec1_encoding: str | bytes) -> PublicKey: + pub = PublicKey() + _DLL.botan_pubkey_load_ecdsa_sec1(byref(pub.handle_()), _ctype_bits(sec1_encoding), len(sec1_encoding), _ctype_str(curve)) + return pub + + @classmethod + def load_ecdh(cls, curve: str, pub_x: MPILike, pub_y: MPILike) -> PublicKey: + """Load an ECDH public key giving the curve as a string (like "secp256r1") and the public point + as a pair of integers giving the affine coordinates.""" + pub = PublicKey() pub_x = MPI(pub_x) pub_y = MPI(pub_y) - _DLL.botan_pubkey_load_ecdh(byref(obj), pub_x.handle_(), pub_y.handle_(), _ctype_str(curve)) - return PublicKey(obj) + _DLL.botan_pubkey_load_ecdh(byref(pub.handle_()), pub_x.handle_(), pub_y.handle_(), _ctype_str(curve)) + return pub @classmethod - def load_sm2(cls, curve, pub_x, pub_y): - obj = c_void_p(0) + def load_ecdh_sec1(cls, curve: str, sec1_encoding: str | bytes) -> PublicKey: + pub = PublicKey() + _DLL.botan_pubkey_load_ecdh_sec1(byref(pub.handle_()), _ctype_bits(sec1_encoding), len(sec1_encoding), _ctype_str(curve)) + return pub + + @classmethod + def load_sm2(cls, curve: str, pub_x: MPILike, pub_y: MPILike) -> PublicKey: + """Load a SM2 public key giving the curve as a string (like "sm2p256v1") and the public point + as a pair of integers giving the affine coordinates.""" + pub = PublicKey() pub_x = MPI(pub_x) pub_y = MPI(pub_y) - _DLL.botan_pubkey_load_sm2(byref(obj), pub_x.handle_(), pub_y.handle_(), _ctype_str(curve)) - return PublicKey(obj) + _DLL.botan_pubkey_load_sm2(byref(pub.handle_()), pub_x.handle_(), pub_y.handle_(), _ctype_str(curve)) + return pub @classmethod - def load_kyber(cls, key): - obj = c_void_p(0) - _DLL.botan_pubkey_load_kyber(byref(obj), key, len(key)) - return PublicKey(obj) + def load_sm2_sec1(cls, curve: str, sec1_encoding: str | bytes) -> PublicKey: + pub = PublicKey() + _DLL.botan_pubkey_load_sm2_sec1(byref(pub.handle_()), _ctype_bits(sec1_encoding), len(sec1_encoding), _ctype_str(curve)) + return pub @classmethod - def load_ml_kem(cls, mlkem_mode, key): - obj = c_void_p(0) - _DLL.botan_pubkey_load_ml_kem(byref(obj), key, len(key), _ctype_str(mlkem_mode)) - return PublicKey(obj) + def load_kyber(cls, key: bytes) -> PublicKey: + pub = PublicKey() + _DLL.botan_pubkey_load_kyber(byref(pub.handle_()), key, len(key)) + return pub @classmethod - def load_ml_dsa(cls, mldsa_mode, key): - obj = c_void_p(0) - _DLL.botan_pubkey_load_ml_dsa(byref(obj), key, len(key), _ctype_str(mldsa_mode)) - return PublicKey(obj) + def load_ml_kem(cls, mlkem_mode: str, key: bytes) -> PublicKey: + """Load an ML-KEM public key giving the mode as a string (like "ML-KEM-512") + and the raw encoding of the public key.""" + pub = PublicKey() + _DLL.botan_pubkey_load_ml_kem(byref(pub.handle_()), key, len(key), _ctype_str(mlkem_mode)) + return pub @classmethod - def load_slh_dsa(cls, slhdsa_mode, key): - obj = c_void_p(0) - _DLL.botan_pubkey_load_slh_dsa(byref(obj), key, len(key), _ctype_str(slhdsa_mode)) - return PublicKey(obj) + def load_ml_dsa(cls, mldsa_mode: str, key: bytes) -> PublicKey: + """Load an ML-DSA public key giving the mode as a string (like "ML-DSA-4x4") + and the raw encoding of the public key.""" + pub = PublicKey() + _DLL.botan_pubkey_load_ml_dsa(byref(pub.handle_()), key, len(key), _ctype_str(mldsa_mode)) + return pub @classmethod - def load_frodokem(cls, frodo_mode, key): - obj = c_void_p(0) - _DLL.botan_pubkey_load_frodokem(byref(obj), key, len(key), _ctype_str(frodo_mode)) - return PublicKey(obj) + def load_slh_dsa(cls, slhdsa_mode: str, key: bytes) -> PublicKey: + """Load an SLH-DSA public key giving the mode as a string (like "SLH-DSA-SHAKE-128f") + and the raw encoding of the public key.""" + pub = PublicKey() + _DLL.botan_pubkey_load_slh_dsa(byref(pub.handle_()), key, len(key), _ctype_str(slhdsa_mode)) + return pub @classmethod - def load_classic_mceliece(cls, cmce_mode, key): - obj = c_void_p(0) - _DLL.botan_pubkey_load_classic_mceliece(byref(obj), key, len(key), _ctype_str(cmce_mode)) - return PublicKey(obj) + def load_frodokem(cls, frodo_mode: str, key: bytes) -> PublicKey: + pub = PublicKey() + _DLL.botan_pubkey_load_frodokem(byref(pub.handle_()), key, len(key), _ctype_str(frodo_mode)) + return pub + + @classmethod + def load_classic_mceliece(cls, cmce_mode: str, key: bytes) -> PublicKey: + pub = PublicKey() + _DLL.botan_pubkey_load_classic_mceliece(byref(pub.handle_()), key, len(key), _ctype_str(cmce_mode)) + return pub def __del__(self): _DLL.botan_pubkey_destroy(self.__obj) @@ -1278,39 +1651,51 @@ def handle_(self): return self.__obj - def check_key(self, rng_obj, strong=True): + def check_key(self, rng_obj: RandomNumberGenerator, strong: bool = True) -> bool: + """Test the key for consistency. If ``strong`` is ``True`` then more expensive tests are performed.""" flags = 1 if strong else 0 rc = _DLL.botan_pubkey_check_key(self.__obj, rng_obj.handle_(), flags) return rc == 0 - def estimated_strength(self): + def estimated_strength(self) -> int: + """Returns the estimated strength of this key against known attacks + (NFS, Pollard's rho, etc)""" r = c_size_t(0) _DLL.botan_pubkey_estimated_strength(self.__obj, byref(r)) return r.value - def algo_name(self): + def algo_name(self) -> str: + """Returns the algorithm name""" return _call_fn_returning_str(32, lambda b, bl: _DLL.botan_pubkey_algo_name(self.__obj, b, bl)) - def export(self, pem=False): + def export(self, pem: bool = False) -> str | bytes: + """Exports the public key using the usual X.509 SPKI representation. + If ``pem`` is True, the result is a PEM encoded string. Otherwise + it is a binary DER value.""" if pem: return self.to_pem() else: return self.to_der() - def to_der(self): + def to_der(self) -> bytes: + """Like ``self.export(False)``""" return _call_fn_viewing_vec(lambda vc, vfn: _DLL.botan_pubkey_view_der(self.__obj, vc, vfn)) - def to_pem(self): + def to_pem(self) -> str: + """Like ``self.export(True)``""" return _call_fn_viewing_str(lambda vc, vfn: _DLL.botan_pubkey_view_pem(self.__obj, vc, vfn)) - def to_raw(self): + def to_raw(self) -> bytes: + """Exports the key in its canonical raw encoding. + This might not be available for all key types and raise an exception in that case.""" return _call_fn_viewing_vec(lambda vc, vfn: _DLL.botan_pubkey_view_raw(self.__obj, vc, vfn)) - def view_kyber_raw_key(self): + def view_kyber_raw_key(self) -> bytes: """Deprecated: use to_raw() instead""" return _call_fn_viewing_vec(lambda vc, vfn: _DLL.botan_pubkey_view_kyber_raw_key(self.__obj, vc, vfn)) - def fingerprint(self, hash_algorithm='SHA-256'): + def fingerprint(self, hash_algorithm: str = 'SHA-256') -> str: + """Returns a hash of the public key""" n = HashFunction(hash_algorithm).output_length() buf = create_string_buffer(n) buf_len = c_size_t(n) @@ -1318,32 +1703,66 @@ _DLL.botan_pubkey_fingerprint(self.__obj, _ctype_str(hash_algorithm), buf, byref(buf_len)) return _hex_encode(buf[0:int(buf_len.value)]) - def get_field(self, field_name): + def get_field(self, field_name: str) -> int: + """Return an integer field related to the public key. The valid field names + vary depending on the algorithm. For example RSA public modulus can be + extracted with ``rsa_key.get_field("n")``.""" v = MPI() _DLL.botan_pubkey_get_field(v.handle_(), self.__obj, _ctype_str(field_name)) return int(v) - def get_public_point(self): + def object_identifier(self) -> OID: + """Returns the associated OID""" + oid = OID() + _DLL.botan_pubkey_oid(byref(oid.handle_()), self.__obj) + return oid + + def get_public_point(self) -> bytes: return _call_fn_viewing_vec(lambda vc, vfn: _DLL.botan_pubkey_view_ec_public_point(self.__obj, vc, vfn)) + def used_explicit_encoding(self) -> bool: + rc = _DLL.botan_pubkey_ecc_key_used_explicit_encoding(self.__obj) + if rc == -32: + raise BotanException("Only ECC keys have a notion of explicit encoding") + return rc == 1 + + def get_group(self) -> ECGroup: + """Return the group associated with the key if the key is an EC key. + Raises an exception if the key is not an EC key.""" + group = ECGroup() + _DLL.botan_ec_pubkey_get_group(self.__obj, byref(group.handle_())) + return group + + # # Private Key # class PrivateKey: + """Previously ``private_key``""" - def __init__(self, obj=c_void_p(0)): + def __init__(self, obj: c_void_p | None = None): + if not obj: + obj = c_void_p(0) self.__obj = obj @classmethod - def load(cls, val, passphrase=""): - obj = c_void_p(0) + def load(cls, val: str | bytes, passphrase: str | None = None) -> PrivateKey: + """Return a private key (DER or PEM formats accepted)""" + priv = PrivateKey() rng_obj = c_void_p(0) # unused in recent versions bits = _ctype_bits(val) - _DLL.botan_privkey_load(byref(obj), rng_obj, bits, len(bits), _ctype_str(passphrase)) - return PrivateKey(obj) + passwd = None if passphrase is None else _ctype_str(passphrase) + _DLL.botan_privkey_load(byref(priv.handle_()), rng_obj, bits, len(bits), passwd) + return priv @classmethod - def create(cls, algo, params, rng_obj): + def create(cls, algo: str, params: str | int | tuple[int, int], rng_obj: RandomNumberGenerator) -> PrivateKey: + """Creates a new private key. The parameter type/value depends on + the algorithm. For "rsa" is is the size of the key in bits. + For "ecdsa" and "ecdh" it is a group name (for instance + "secp256r1"). For "ecdh" there is also a special case for groups + "curve25519" and "x448" (which are actually completely distinct key types + with a non-standard encoding).""" if algo == 'rsa': algo = 'RSA' params = "%d" % (params) @@ -1359,107 +1778,143 @@ else: algo = 'ECDH' elif algo in ['mce', 'mceliece']: + # TODO(Botan4) remove this case algo = 'McEliece' params = "%d,%d" % (params[0], params[1]) + priv = PrivateKey() + _DLL.botan_privkey_create(byref(priv.handle_()), _ctype_str(algo), _ctype_str(params), rng_obj.handle_()) + return priv + + @classmethod + def create_ec(cls, algo: str, ec_group: ECGroup, rng_obj: RandomNumberGenerator) -> PrivateKey: + """Creates a new ec private key.""" obj = c_void_p(0) - _DLL.botan_privkey_create(byref(obj), _ctype_str(algo), _ctype_str(params), rng_obj.handle_()) + _DLL.botan_ec_privkey_create(byref(obj), _ctype_str(algo), ec_group.handle_(), rng_obj.handle_()) return PrivateKey(obj) @classmethod - def load_rsa(cls, p, q, e): - obj = c_void_p(0) + def load_rsa(cls, p: MPILike, q: MPILike, e: MPILike) -> PrivateKey: + """Return a private RSA key""" + priv = PrivateKey() p = MPI(p) q = MPI(q) e = MPI(e) - _DLL.botan_privkey_load_rsa(byref(obj), p.handle_(), q.handle_(), e.handle_()) - return PrivateKey(obj) + _DLL.botan_privkey_load_rsa(byref(priv.handle_()), p.handle_(), q.handle_(), e.handle_()) + return priv @classmethod - def load_dsa(cls, p, q, g, x): - obj = c_void_p(0) + def load_dsa(cls, p: MPILike, q: MPILike, g: MPILike, x: MPILike) -> PrivateKey: + """Return a private DSA key""" + priv = PrivateKey() p = MPI(p) q = MPI(q) g = MPI(g) x = MPI(x) - _DLL.botan_privkey_load_dsa(byref(obj), p.handle_(), q.handle_(), g.handle_(), x.handle_()) - return PrivateKey(obj) + _DLL.botan_privkey_load_dsa(byref(priv.handle_()), p.handle_(), q.handle_(), g.handle_(), x.handle_()) + return priv @classmethod - def load_dh(cls, p, g, x): - obj = c_void_p(0) + def load_dh(cls, p: MPILike, g: MPILike, x: MPILike) -> PrivateKey: + """Return a private DH key""" + priv = PrivateKey() p = MPI(p) g = MPI(g) x = MPI(x) - _DLL.botan_privkey_load_dh(byref(obj), p.handle_(), g.handle_(), x.handle_()) - return PrivateKey(obj) + _DLL.botan_privkey_load_dh(byref(priv.handle_()), p.handle_(), g.handle_(), x.handle_()) + return priv @classmethod - def load_elgamal(cls, p, q, g, x): - obj = c_void_p(0) + def load_elgamal(cls, p: MPILike, q: MPILike, g: MPILike, x: MPILike) -> PrivateKey: + """Return a private ElGamal key""" + priv = PrivateKey() p = MPI(p) q = MPI(q) g = MPI(g) x = MPI(x) - _DLL.botan_privkey_load_elgamal(byref(obj), p.handle_(), q.handle_(), g.handle_(), x.handle_()) - return PrivateKey(obj) + _DLL.botan_privkey_load_elgamal(byref(priv.handle_()), p.handle_(), q.handle_(), g.handle_(), x.handle_()) + return priv @classmethod - def load_ecdsa(cls, curve, x): - obj = c_void_p(0) + def load_ecdsa(cls, curve: str, x: MPILike) -> PrivateKey: + """Return a private ECDSA key""" + priv = PrivateKey() x = MPI(x) - _DLL.botan_privkey_load_ecdsa(byref(obj), x.handle_(), _ctype_str(curve)) - return PrivateKey(obj) + _DLL.botan_privkey_load_ecdsa(byref(priv.handle_()), x.handle_(), _ctype_str(curve)) + return priv @classmethod - def load_ecdh(cls, curve, x): - obj = c_void_p(0) + def load_ecdh(cls, curve: str, x: MPILike) -> PrivateKey: + """Return a private ECDH key""" + priv = PrivateKey() x = MPI(x) - _DLL.botan_privkey_load_ecdh(byref(obj), x.handle_(), _ctype_str(curve)) - return PrivateKey(obj) + _DLL.botan_privkey_load_ecdh(byref(priv.handle_()), x.handle_(), _ctype_str(curve)) + return priv @classmethod - def load_sm2(cls, curve, x): - obj = c_void_p(0) + def load_sm2(cls, curve: str, x: MPILike) -> PrivateKey: + """Return a private SM2 key""" + priv = PrivateKey() x = MPI(x) - _DLL.botan_privkey_load_sm2(byref(obj), x.handle_(), _ctype_str(curve)) - return PrivateKey(obj) + _DLL.botan_privkey_load_sm2(byref(priv.handle_()), x.handle_(), _ctype_str(curve)) + return priv @classmethod - def load_kyber(cls, key): - obj = c_void_p(0) - _DLL.botan_privkey_load_kyber(byref(obj), key, len(key)) - return PrivateKey(obj) + def load_kyber(cls, key: bytes) -> PrivateKey: + priv = PrivateKey() + _DLL.botan_privkey_load_kyber(byref(priv.handle_()), key, len(key)) + return priv @classmethod - def load_ml_kem(cls, mlkem_mode, key): - obj = c_void_p(0) - _DLL.botan_privkey_load_ml_kem(byref(obj), key, len(key), _ctype_str(mlkem_mode)) - return PrivateKey(obj) + def load_ml_kem(cls, mlkem_mode: str, key: bytes) -> PrivateKey: + """Return a private ML-KEM key""" + priv = PrivateKey() + _DLL.botan_privkey_load_ml_kem(byref(priv.handle_()), key, len(key), _ctype_str(mlkem_mode)) + return priv @classmethod - def load_ml_dsa(cls, mldsa_mode, key): - obj = c_void_p(0) - _DLL.botan_privkey_load_ml_dsa(byref(obj), key, len(key), _ctype_str(mldsa_mode)) - return PrivateKey(obj) + def load_ml_dsa(cls, mldsa_mode: str, key: bytes) -> PrivateKey: + """Return a private ML-DSA key""" + priv = PrivateKey() + _DLL.botan_privkey_load_ml_dsa(byref(priv.handle_()), key, len(key), _ctype_str(mldsa_mode)) + return priv @classmethod - def load_slh_dsa(cls, slh_dsa, key): - obj = c_void_p(0) - _DLL.botan_privkey_load_slh_dsa(byref(obj), key, len(key), _ctype_str(slh_dsa)) - return PrivateKey(obj) + def load_slh_dsa(cls, slh_dsa: str, key: bytes) -> PrivateKey: + """Return a private SLH-DSA key""" + priv = PrivateKey() + _DLL.botan_privkey_load_slh_dsa(byref(priv.handle_()), key, len(key), _ctype_str(slh_dsa)) + return priv @classmethod - def load_frodokem(cls, frodo_mode, key): - obj = c_void_p(0) - _DLL.botan_privkey_load_frodokem(byref(obj), key, len(key), _ctype_str(frodo_mode)) - return PrivateKey(obj) + def load_frodokem(cls, frodo_mode: str, key: bytes) -> PrivateKey: + priv = PrivateKey() + _DLL.botan_privkey_load_frodokem(byref(priv.handle_()), key, len(key), _ctype_str(frodo_mode)) + return priv @classmethod - def load_classic_mceliece(cls, cmce_mode, key): - obj = c_void_p(0) - _DLL.botan_privkey_load_classic_mceliece(byref(obj), key, len(key), _ctype_str(cmce_mode)) - return PrivateKey(obj) + def load_classic_mceliece(cls, cmce_mode: str, key: bytes) -> PrivateKey: + priv = PrivateKey() + _DLL.botan_privkey_load_classic_mceliece(byref(priv.handle_()), key, len(key), _ctype_str(cmce_mode)) + return priv + + @classmethod + def load_x25519(cls, key: bytes) -> PrivateKey: + """Return a private X25519 key from 32 raw bytes""" + if len(key) != 32: + raise BotanException("Invalid input length to load_x25519") + priv = PrivateKey() + _DLL.botan_privkey_load_x25519(byref(priv.handle_()), key) + return priv + + @classmethod + def load_x448(cls, key: bytes) -> PrivateKey: + """Return a private X448 key from 56 raw bytes""" + if len(key) != 56: + raise BotanException("Invalid input length to load_x448") + priv = PrivateKey() + _DLL.botan_privkey_load_x448(byref(priv.handle_()), key) + return priv def __del__(self): _DLL.botan_privkey_destroy(self.__obj) @@ -1467,39 +1922,52 @@ def handle_(self): return self.__obj - def check_key(self, rng_obj, strong=True): + def check_key(self, rng_obj: RandomNumberGenerator, strong: bool = True) -> bool: + """Test the key for consistency. If ``strong`` is ``True`` then more expensive tests are performed.""" flags = 1 if strong else 0 rc = _DLL.botan_privkey_check_key(self.__obj, rng_obj.handle_(), flags) return rc == 0 - def algo_name(self): + def algo_name(self) -> str: + """Returns the algorithm name""" return _call_fn_returning_str(32, lambda b, bl: _DLL.botan_privkey_algo_name(self.__obj, b, bl)) - def get_public_key(self): - pub = c_void_p(0) - _DLL.botan_privkey_export_pubkey(byref(pub), self.__obj) - return PublicKey(pub) + def get_public_key(self) -> PublicKey: + """Return a public_key object""" + pub = PublicKey() + _DLL.botan_privkey_export_pubkey(byref(pub.handle_()), self.__obj) + return pub - def to_der(self): + def to_der(self) -> bytes: + """Return the DER encoded private key (unencrypted). Like ``self.export(False)``""" return _call_fn_viewing_vec(lambda vc, vfn: _DLL.botan_privkey_view_der(self.__obj, vc, vfn)) - def to_pem(self): + def to_pem(self) -> str: + """Return the PEM encoded private key (unencrypted). Like ``self.export(True)``""" return _call_fn_viewing_str(lambda vc, vfn: _DLL.botan_privkey_view_pem(self.__obj, vc, vfn)) - def to_raw(self): + def to_raw(self) -> bytes: + """Exports the key in its canonical raw encoding. + This might not be available for all key types and raise an exception in that case.""" return _call_fn_viewing_vec(lambda vc, vfn: _DLL.botan_privkey_view_raw(self.__obj, vc, vfn)) - def view_kyber_raw_key(self): + def view_kyber_raw_key(self) -> bytes: """Deprecated: use to_raw() instead""" return _call_fn_viewing_vec(lambda vc, vfn: _DLL.botan_privkey_view_kyber_raw_key(self.__obj, vc, vfn)) - def export(self, pem=False): + def export(self, pem: bool = False) -> str | bytes: + """Exports the private key in PKCS8 format. If ``pem`` is True, the + result is a PEM encoded string. Otherwise it is a binary DER + value. The key will not be encrypted.""" if pem: return self.to_pem() else: return self.to_der() - def export_encrypted(self, passphrase, rng, pem=False, msec=300, cipher=None, pbkdf=None): # pylint: disable=redefined-outer-name + def export_encrypted(self, passphrase: str, rng: RandomNumberGenerator, pem: bool = False, msec: int = 300, cipher: str | None = None, pbkdf: str | None = None): # pylint: disable=redefined-outer-name + """Exports the private key in PKCS8 format, encrypted using the + provided passphrase. If ``pem`` is True, the result is a PEM + encoded string. Otherwise it is a binary DER value.""" if pem: return _call_fn_viewing_str( lambda vc, vfn: _DLL.botan_privkey_view_encrypted_pem_timed( @@ -1511,13 +1979,54 @@ self.__obj, rng.handle_(), _ctype_str(passphrase), _ctype_str(cipher), _ctype_str(pbkdf), c_size_t(msec), vc, vfn)) - def get_field(self, field_name): + def get_field(self, field_name: str) -> int: + """Return an integer field related to the public key. The valid field names + vary depending on the algorithm. For example first RSA secret prime can be + extracted with ``rsa_key.get_field("p")``. This function can also be + used to extract the public parameters.""" v = MPI() _DLL.botan_privkey_get_field(v.handle_(), self.__obj, _ctype_str(field_name)) return int(v) + def object_identifier(self) -> OID: + """Return the associated OID""" + oid = OID() + _DLL.botan_privkey_oid(byref(oid.handle_()), self.__obj) + return oid + + def stateful_operation(self) -> bool: + """Return whether the key is stateful or not.""" + r = c_int(0) + _DLL.botan_privkey_stateful_operation(self.__obj, byref(r)) + if r.value == 0: + return False + return True + + def remaining_operations(self) -> int: + """If the key is stateful, return the number of remaining operations. + Raises an exception if the key is not stateful.""" + r = c_uint64(0) + _DLL.botan_privkey_remaining_operations(self.__obj, byref(r)) + return r.value + + def get_private_key(self) -> ECScalar: + """Return the private value if the key is an EC key.""" + scalar = ECScalar() + _DLL.botan_ec_privkey_get_private_key(self.__obj, byref(scalar.handle_())) + return scalar + + def get_group(self) -> ECGroup: + """Return the group associated with the key if the key is an EC key. + Raises an exception if the key is not an EC key.""" + group = ECGroup() + _DLL.botan_ec_privkey_get_group(self.__obj, byref(group.handle_())) + return group + + class PKEncrypt: - def __init__(self, key, padding): + """Previously ``pk_op_encrypt``""" + + def __init__(self, key: PublicKey, padding: str): self.__obj = c_void_p(0) flags = c_uint32(0) # always zero in this ABI _DLL.botan_pk_op_encrypt_create(byref(self.__obj), key.handle_(), _ctype_str(padding), flags) @@ -1525,7 +2034,7 @@ def __del__(self): _DLL.botan_pk_op_encrypt_destroy(self.__obj) - def encrypt(self, msg, rng_obj): + def encrypt(self, msg: bytes, rng_obj: RandomNumberGenerator) -> bytes: outbuf_sz = c_size_t(0) _DLL.botan_pk_op_encrypt_output_length(self.__obj, len(msg), byref(outbuf_sz)) outbuf = create_string_buffer(outbuf_sz.value) @@ -1534,7 +2043,9 @@ class PKDecrypt: - def __init__(self, key, padding): + """Previously ``pk_op_decrypt``""" + + def __init__(self, key: PrivateKey, padding: str): self.__obj = c_void_p(0) flags = c_uint32(0) # always zero in this ABI _DLL.botan_pk_op_decrypt_create(byref(self.__obj), key.handle_(), _ctype_str(padding), flags) @@ -1542,7 +2053,7 @@ def __del__(self): _DLL.botan_pk_op_decrypt_destroy(self.__obj) - def decrypt(self, msg): + def decrypt(self, msg: bytes) -> bytes: outbuf_sz = c_size_t(0) _DLL.botan_pk_op_decrypt_output_length(self.__obj, len(msg), byref(outbuf_sz)) outbuf = create_string_buffer(outbuf_sz.value) @@ -1551,7 +2062,9 @@ return outbuf.raw[0:int(outbuf_sz.value)] class PKSign: # pylint: disable=invalid-name - def __init__(self, key, padding, der=False): + """Previously ``pk_op_sign``""" + + def __init__(self, key: PrivateKey, padding: str, der: bool = False): self.__obj = c_void_p(0) flags = c_uint32(1) if der else c_uint32(0) _DLL.botan_pk_op_sign_create(byref(self.__obj), key.handle_(), _ctype_str(padding), flags) @@ -1559,10 +2072,10 @@ def __del__(self): _DLL.botan_pk_op_sign_destroy(self.__obj) - def update(self, msg): - _DLL.botan_pk_op_sign_update(self.__obj, _ctype_str(msg), len(msg)) + def update(self, msg: str | bytes): + _DLL.botan_pk_op_sign_update(self.__obj, _ctype_bits(msg), len(msg)) - def finish(self, rng_obj): + def finish(self, rng_obj: RandomNumberGenerator) -> bytes: outbuf_sz = c_size_t(0) _DLL.botan_pk_op_sign_output_length(self.__obj, byref(outbuf_sz)) outbuf = create_string_buffer(outbuf_sz.value) @@ -1570,7 +2083,9 @@ return outbuf.raw[0:int(outbuf_sz.value)] class PKVerify: - def __init__(self, key, padding, der=False): + """Previously ``pk_op_verify``""" + + def __init__(self, key: PublicKey, padding: str, der: bool = False): self.__obj = c_void_p(0) flags = c_uint32(1) if der else c_uint32(0) _DLL.botan_pk_op_verify_create(byref(self.__obj), key.handle_(), _ctype_str(padding), flags) @@ -1578,11 +2093,11 @@ def __del__(self): _DLL.botan_pk_op_verify_destroy(self.__obj) - def update(self, msg): + def update(self, msg: str | bytes): bits = _ctype_bits(msg) _DLL.botan_pk_op_verify_update(self.__obj, bits, len(bits)) - def check_signature(self, signature): + def check_signature(self, signature: str | bytes) -> bool: bits = _ctype_bits(signature) rc = _DLL.botan_pk_op_verify_finish(self.__obj, bits, len(bits)) if rc == 0: @@ -1590,7 +2105,9 @@ return False class PKKeyAgreement: - def __init__(self, key, kdf_name): + """Previously ``pk_op_key_agreement``""" + + def __init__(self, key: PrivateKey, kdf_name: str): self.__obj = c_void_p(0) flags = c_uint32(0) # always zero in this ABI _DLL.botan_pk_op_key_agreement_create(byref(self.__obj), key.handle_(), _ctype_str(kdf_name), flags) @@ -1601,15 +2118,17 @@ def __del__(self): _DLL.botan_pk_op_key_agreement_destroy(self.__obj) - def public_value(self): + def public_value(self) -> bytes: + """Returns the public value to be passed to the other party""" return self.m_public_value - def underlying_output_length(self): + def underlying_output_length(self) -> int: out_len = c_size_t(0) _DLL.botan_pk_op_key_agreement_size(self.__obj, byref(out_len)) return out_len.value - def agree(self, other, key_len, salt): + def agree(self, other: bytes, key_len: int, salt: bytes) -> bytes: + """Returns a key derived by the KDF.""" if key_len == 0: key_len = self.underlying_output_length() return _call_fn_returning_vec(key_len, lambda b, bl: @@ -1618,22 +2137,22 @@ salt, len(salt))) class KemEncrypt: - def __init__(self, key, params): + def __init__(self, key: PublicKey, params: str): self.__obj = c_void_p(0) _DLL.botan_pk_op_kem_encrypt_create(byref(self.__obj), key.handle_(), _ctype_str(params)) def __del__(self): _DLL.botan_pk_op_kem_encrypt_destroy(self.__obj) - def shared_key_length(self, desired_key_len): + def shared_key_length(self, desired_key_len: int) -> int: return _call_fn_returning_sz( - lambda l: _DLL.botan_pk_op_kem_encrypt_shared_key_length(self.__obj, desired_key_len, l)) + lambda len: _DLL.botan_pk_op_kem_encrypt_shared_key_length(self.__obj, desired_key_len, len)) - def encapsulated_key_length(self): + def encapsulated_key_length(self) -> int: return _call_fn_returning_sz( - lambda l: _DLL.botan_pk_op_kem_encrypt_encapsulated_key_length(self.__obj, l)) + lambda len: _DLL.botan_pk_op_kem_encrypt_encapsulated_key_length(self.__obj, len)) - def create_shared_key(self, rng, salt, desired_key_len): + def create_shared_key(self, rng: RandomNumberGenerator, salt: bytes, desired_key_len: int) -> tuple[bytes, bytes]: shared_key_len = self.shared_key_length(desired_key_len) shared_key_buf = create_string_buffer(shared_key_len) @@ -1658,18 +2177,18 @@ return (shared_key, encapsulated_key) class KemDecrypt: - def __init__(self, key, params): + def __init__(self, key: PrivateKey, params: str): self.__obj = c_void_p(0) _DLL.botan_pk_op_kem_decrypt_create(byref(self.__obj), key.handle_(), _ctype_str(params)) def __del__(self): _DLL.botan_pk_op_kem_decrypt_destroy(self.__obj) - def shared_key_length(self, desired_key_len): + def shared_key_length(self, desired_key_len: int) -> int: return _call_fn_returning_sz( - lambda l: _DLL.botan_pk_op_kem_decrypt_shared_key_length(self.__obj, desired_key_len, l)) + lambda len: _DLL.botan_pk_op_kem_decrypt_shared_key_length(self.__obj, desired_key_len, len)) - def decrypt_shared_key(self, salt, desired_key_len, encapsulated_key): + def decrypt_shared_key(self, salt: bytes, desired_key_len: int, encapsulated_key: bytes) -> bytes: shared_key_len = self.shared_key_length(desired_key_len) return _call_fn_returning_vec( @@ -1706,14 +2225,17 @@ # X.509 certificates # class X509Cert: # pylint: disable=invalid-name - def __init__(self, filename=None, buf=None): + def __init__(self, filename: str | None = None, buf: bytes | None = None): self.__obj = c_void_p(0) self.__obj = _load_buf_or_file(filename, buf, _DLL.botan_x509_cert_load_file, _DLL.botan_x509_cert_load) def __del__(self): _DLL.botan_x509_cert_destroy(self.__obj) - def time_starts(self): + def time_starts(self) -> datetime: + """Return the time the certificate becomes valid, as a string in form + "YYYYMMDDHHMMSSZ" where Z is a literal character reflecting that this time is + relative to UTC.""" starts = _call_fn_returning_str( 16, lambda b, bl: _DLL.botan_x509_cert_get_time_starts(self.__obj, b, bl)) if len(starts) == 13: @@ -1727,7 +2249,10 @@ return datetime.fromtimestamp(mktime(struct_time)) - def time_expires(self): + def time_expires(self) -> datetime: + """Return the time the certificate expires, as a string in form + "YYYYMMDDHHMMSSZ" where Z is a literal character reflecting that this time is + relative to UTC.""" expires = _call_fn_returning_str( 16, lambda b, bl: _DLL.botan_x509_cert_get_time_expires(self.__obj, b, bl)) if len(expires) == 13: @@ -1741,59 +2266,80 @@ return datetime.fromtimestamp(mktime(struct_time)) - def to_string(self): + def to_string(self) -> str: + """Format the certificate as a free-form string.""" return _call_fn_viewing_str( lambda vc, vfn: _DLL.botan_x509_cert_view_as_string(self.__obj, vc, vfn)) - def fingerprint(self, hash_algo='SHA-256'): + def fingerprint(self, hash_algo: str = 'SHA-256') -> str: + """Return a fingerprint for the certificate, which is basically just a hash + of the binary contents. Normally SHA-1 or SHA-256 is used, but any hash + function is allowed.""" n = HashFunction(hash_algo).output_length() * 3 return _call_fn_returning_str( n, lambda b, bl: _DLL.botan_x509_cert_get_fingerprint(self.__obj, _ctype_str(hash_algo), b, bl)) - def serial_number(self): + def serial_number(self) -> bytes: + """Return the serial number of the certificate.""" return _call_fn_returning_vec( 32, lambda b, bl: _DLL.botan_x509_cert_get_serial_number(self.__obj, b, bl)) - def authority_key_id(self): + def authority_key_id(self) -> bytes: + """Return the authority key ID set in the certificate, which may be empty.""" return _call_fn_returning_vec( 32, lambda b, bl: _DLL.botan_x509_cert_get_authority_key_id(self.__obj, b, bl)) - def subject_key_id(self): + def subject_key_id(self) -> bytes: + """Return the subject key ID set in the certificate, which may be empty.""" return _call_fn_returning_vec( 32, lambda b, bl: _DLL.botan_x509_cert_get_subject_key_id(self.__obj, b, bl)) - def subject_public_key_bits(self): + def subject_public_key_bits(self) -> bytes: + """Get the serialized representation of the public key included in this certificate.""" return _call_fn_viewing_vec( lambda vc, vfn: _DLL.botan_x509_cert_view_public_key_bits(self.__obj, vc, vfn)) - def subject_public_key(self): + def subject_public_key(self) -> PublicKey: + """Get the public key included in this certificate as an object of class ``PublicKey``.""" pub = c_void_p(0) _DLL.botan_x509_cert_get_public_key(self.__obj, byref(pub)) return PublicKey(pub) - def subject_dn(self, key, index): + def subject_dn(self, key: str, index: int) -> str: + """Get a value from the subject DN field. + + ``key`` specifies a value to get, for instance ``"Name"`` or `"Country"`.""" return _call_fn_returning_str( 0, lambda b, bl: _DLL.botan_x509_cert_get_subject_dn(self.__obj, _ctype_str(key), index, b, bl)) - def issuer_dn(self, key, index): + def issuer_dn(self, key: str, index: int) -> str: + """Get a value from the issuer DN field. + + ``key`` specifies a value to get, for instance ``"Name"`` or `"Country"`.""" return _call_fn_returning_str( 0, lambda b, bl: _DLL.botan_x509_cert_get_issuer_dn(self.__obj, _ctype_str(key), index, b, bl)) - def hostname_match(self, hostname): + def hostname_match(self, hostname: str) -> bool: + """Return True if the Common Name (CN) field of the certificate matches a given ``hostname``.""" rc = _DLL.botan_x509_cert_hostname_match(self.__obj, _ctype_str(hostname)) return rc == 0 - def not_before(self): + def not_before(self) -> int: + """Return the time the certificate becomes valid, as seconds since epoch.""" time = c_uint64(0) _DLL.botan_x509_cert_not_before(self.__obj, byref(time)) return time.value - def not_after(self): + def not_after(self) -> int: + """Return the time the certificate expires, as seconds since epoch.""" time = c_uint64(0) _DLL.botan_x509_cert_not_after(self.__obj, byref(time)) return time.value - def allowed_usage(self, usage_list): + def allowed_usage(self, usage_list: list[str]) -> bool: + """Return True if the certificates Key Usage extension contains all constraints given in ``usage_list``. + Also return True if the certificate doesn't have this extension. + Example usage constraints are: ``"DIGITAL_SIGNATURE"``, ``"KEY_CERT_SIGN"``, ``"CRL_SIGN"``.""" usage_values = {"NO_CONSTRAINTS": 0, "DIGITAL_SIGNATURE": 32768, "NON_REPUDIATION": 16384, @@ -1817,13 +2363,34 @@ return self.__obj def verify(self, - intermediates=None, - trusted=None, - trusted_path=None, - required_strength=0, - hostname=None, - reference_time=0, - crls=None): + intermediates: list[X509Cert] | None = None, + trusted: list[X509Cert] | None = None, + trusted_path: str | None = None, + required_strength: int = 0, + hostname: str | None = None, + reference_time: int = 0, + crls: list[X509CRL] | None = None) -> int: + """Verify a certificate. Returns 0 if validation was successful, returns a positive error code + if the validation was unsuccessful. + + ``intermediates`` is a list of untrusted subauthorities. + + ``trusted`` is a list of trusted root CAs. + + The `trusted_path` refers to a directory where one or more trusted CA + certificates are stored. + + Set ``required_strength`` to indicate the minimum key and hash strength + that is allowed. For instance setting to 80 allows 1024-bit RSA and SHA-1. + Setting to 110 requires 2048-bit RSA and SHA-256 or higher. Set to zero + to accept a default. + + If ``hostname`` is given, it will be checked against the certificates CN field. + + Set ``reference_time`` to be the time which the certificate chain is + validated against. Use zero (default) to use the current system clock. + + ``crls`` is a list of CRLs issued by either trusted or untrusted authorities.""" if intermediates is not None: c_intermediates = len(intermediates) * c_void_p @@ -1873,10 +2440,12 @@ return error_code.value @classmethod - def validation_status(cls, error_code): + def validation_status(cls, error_code: int) -> str: + """Return an informative string associated with the verification return code.""" return _ctype_to_str(_DLL.botan_x509_cert_validation_status(c_int(error_code))) - def is_revoked(self, crl): + def is_revoked(self, crl: X509CRL) -> bool: + """Check if the certificate (``self``) is revoked on the given ``crl``.""" rc = _DLL.botan_x509_is_revoked(crl.handle_(), self.__obj) return rc == 0 @@ -1884,10 +2453,70 @@ # # X.509 Certificate revocation lists # -class X509CRL: - def __init__(self, filename=None, buf=None): + +class X509CRLReason(IntEnum): + UNSPECIFIED = 0 + KEY_COMPROMISE = 1 + CA_COMPROMISE = 2 + AFFILIATION_CHANGED = 3 + SUPERSEDED = 4 + CESSATION_OF_OPERATION = 5 + CERTIFICATE_HOLD = 6 + REMOVE_FROM_CRL = 8 + PRIVILEGE_WITHDRAWN = 9 + AA_COMPROMISE = 10 + + @classmethod + def to_bits(cls, reason: X509CRLReason) -> int: + return reason.value + + @classmethod + def from_bits(cls, reason: int) -> X509CRLReason: + return cls(reason) + + +class X509CRLEntry: + def __init__(self): self.__obj = c_void_p(0) - self.__obj = _load_buf_or_file(filename, buf, _DLL.botan_x509_crl_load_file, _DLL.botan_x509_crl_load) + + def __del__(self): + _DLL.botan_x509_crl_entry_destroy(self.__obj) + + def handle_(self): + return self.__obj + + @classmethod + def create(cls, cert: X509Cert, reason: X509CRLReason): + entry = X509CRLEntry() + _DLL.botan_x509_crl_entry_create(byref(entry.handle_()), cert.handle_(), X509CRLReason.to_bits(reason)) + return entry + + def serial_number(self) -> MPI: + sn = c_void_p(0) + _DLL.botan_x509_crl_entry_serial_number(self.__obj, byref(sn)) + return MPI(sn) + + def revocation_date(self) -> int: + time = c_uint64(0) + _DLL.botan_x509_crl_entry_revocation_date(self.__obj, byref(time)) + return time.value + + def reason(self) -> X509CRLReason: + reason = c_int(0) + _DLL.botan_x509_crl_entry_reason(self.__obj, byref(reason)) + return X509CRLReason.from_bits(reason.value) + + +class X509CRL: + """Class representing an X.509 Certificate Revocation List.""" + + def __init__(self, filename: str | None = None, buf: bytes | None = None): + """A CRL in PEM or DER format can be loaded from a file, with the ``filename`` argument, + or from a bytestring, with the ``buf`` argument.""" + if not filename and not buf: + self.__obj = c_void_p(0) + else: + self.__obj = _load_buf_or_file(filename, buf, _DLL.botan_x509_crl_load_file, _DLL.botan_x509_crl_load) def __del__(self): _DLL.botan_x509_crl_destroy(self.__obj) @@ -1895,10 +2524,89 @@ def handle_(self): return self.__obj + @classmethod + def create( + cls, + rng: RandomNumberGenerator, + ca_cert: X509Cert, + ca_key: PrivateKey, + issue_time: int, + next_update: int, + hash_fn: str | None = None, + padding: str | None = None + ) -> X509CRL: + crl = X509CRL() + _DLL.botan_x509_crl_create( + byref(crl.handle_()), + rng.handle_(), + ca_cert.handle_(), + ca_key.handle_(), + issue_time, + next_update, + _ctype_str(hash_fn), + _ctype_str(padding) + ) + return crl + + def revoke( + self, + rng: RandomNumberGenerator, + ca_cert: X509Cert, + ca_key: PrivateKey, + issue_time: int, + next_update: int, + new_entries: list[X509CRLEntry], + hash_fn: str | None = None, + padding: str | None = None + ) -> X509CRL: + crl = X509CRL() + c_revoked = len(new_entries) * c_void_p + arr_new_entries = c_revoked() + for i, entry in enumerate(new_entries): + arr_new_entries[i] = entry.handle_() + new_entries_len = c_size_t(len(new_entries)) + + _DLL.botan_x509_crl_update( + byref(crl.handle_()), + self.__obj, + rng.handle_(), + ca_cert.handle_(), + ca_key.handle_(), + issue_time, + next_update, + arr_new_entries, + new_entries_len, + _ctype_str(hash_fn), + _ctype_str(padding) + ) + return crl + + def revoked(self) -> list[X509CRLEntry]: + count = c_size_t(0) + _DLL.botan_x509_crl_entries_count(self.__obj, byref(count)) + revoked = [] + for i in range(count.value): + entry = X509CRLEntry() + _DLL.botan_x509_crl_entries(self.__obj, c_size_t(i), byref(entry.handle_())) + revoked.append(entry) + return revoked + + def verify(self, key: PublicKey) -> bool: + rc = _DLL.botan_x509_crl_verify_signature(self.__obj, key.handle_()) + return rc == 1 + class MPI: + """Most of the usual arithmetic operators (``__add__``, ``__mul__``, etc) are defined.""" - def __init__(self, initial_value=None, radix=None): + def __init__(self, initial_value: MPILike | c_void_p = None, radix: int | None = None): + """Initialize an MPI object with specified value, left as zero otherwise. The + ``initial_value`` should be an ``int``, ``str``, or ``MPI``. + The ``radix`` value should be set to 16 when initializing from a base 16 `str` value.""" + + if isinstance(initial_value, c_void_p): + self.__obj = initial_value + return self.__obj = c_void_p(0) _DLL.botan_mp_init(byref(self.__obj)) @@ -1916,13 +2624,13 @@ _DLL.botan_mp_set_from_str(self.__obj, _ctype_str(str(initial_value))) @classmethod - def random(cls, rng_obj, bits): + def random(cls, rng_obj: RandomNumberGenerator, bits: int) -> MPI: bn = MPI() _DLL.botan_mp_rand_bits(bn.handle_(), rng_obj.handle_(), c_size_t(bits)) return bn @classmethod - def random_range(cls, rng_obj, lower, upper): + def random_range(cls, rng_obj: RandomNumberGenerator, lower: MPI, upper: MPI): bn = MPI() _DLL.botan_mp_rand_range(bn.handle_(), rng_obj.handle_(), lower.handle_(), upper.handle_()) return bn @@ -1934,27 +2642,13 @@ return self.__obj def __int__(self): - out = create_string_buffer(2*self.byte_count() + 3) - _DLL.botan_mp_to_hex(self.__obj, out) - return int(out.value, 16) + hexv = _call_fn_viewing_str(lambda vc, vfn: _DLL.botan_mp_view_hex(self.__obj, vc, vfn)) + return int(hexv, 16) def __repr__(self): - # Should have a better size estimate than this ... - out_len = c_size_t(self.bit_count() // 2) - out = create_string_buffer(out_len.value) - - _DLL.botan_mp_to_str(self.__obj, c_uint8(10), out, byref(out_len)) - - out = out.raw[0:int(out_len.value)] - if out[-1] == '\x00': - out = out[:-1] - s = _ctype_to_str(out) - if s[0] == '0': - return s[1:] - else: - return s + return _call_fn_viewing_str(lambda vc, vfn: _DLL.botan_mp_view_str(self.__obj, 10, vc, vfn)) - def to_bytes(self): + def to_bytes(self) -> Array[c_char]: byte_count = self.byte_count() out_len = c_size_t(byte_count) out = create_string_buffer(out_len.value) @@ -1962,28 +2656,28 @@ assert out_len.value == byte_count return out - def is_negative(self): + def is_negative(self) -> bool: rc = _DLL.botan_mp_is_negative(self.__obj) return rc == 1 - def is_positive(self): + def is_positive(self) -> bool: rc = _DLL.botan_mp_is_positive(self.__obj) return rc == 1 - def is_zero(self): + def is_zero(self) -> bool: rc = _DLL.botan_mp_is_zero(self.__obj) return rc == 1 - def is_odd(self): + def is_odd(self) -> bool: return self.get_bit(0) == 1 - def is_even(self): + def is_even(self) -> bool: return self.get_bit(0) == 0 def flip_sign(self): _DLL.botan_mp_flip_sign(self.__obj) - def cmp(self, other): + def cmp(self, other: MPI) -> int: r = c_int(0) _DLL.botan_mp_cmp(byref(r), self.__obj, other.handle_()) return r.value @@ -1991,130 +2685,495 @@ def __hash__(self): return hash(self.to_bytes()) - def __eq__(self, other): - return self.cmp(other) == 0 + def __eq__(self, other: MPI | object) -> bool: + if isinstance(other, MPI): + return self.cmp(other) == 0 + else: + return False - def __ne__(self, other): - return self.cmp(other) != 0 + def __ne__(self, other: MPI | object) -> bool: + if isinstance(other, MPI): + return self.cmp(other) != 0 + else: + return False - def __lt__(self, other): - return self.cmp(other) < 0 + def __lt__(self, other: MPI | object) -> bool: + if isinstance(other, MPI): + return self.cmp(other) < 0 + else: + return False - def __le__(self, other): - return self.cmp(other) <= 0 + def __le__(self, other: MPI | object) -> bool: + if isinstance(other, MPI): + return self.cmp(other) <= 0 + else: + return False - def __gt__(self, other): - return self.cmp(other) > 0 + def __gt__(self, other: MPI | object) -> bool: + if isinstance(other, MPI): + return self.cmp(other) > 0 + else: + return False - def __ge__(self, other): - return self.cmp(other) >= 0 + def __ge__(self, other: MPI | object) -> bool: + if isinstance(other, MPI): + return self.cmp(other) >= 0 + else: + return False - def __add__(self, other): + def __add__(self, other: MPI): r = MPI() _DLL.botan_mp_add(r.handle_(), self.__obj, other.handle_()) return r - def __iadd__(self, other): + def __iadd__(self, other: MPI): _DLL.botan_mp_add(self.__obj, self.__obj, other.handle_()) return self - def __sub__(self, other): + def __sub__(self, other: MPI): r = MPI() _DLL.botan_mp_sub(r.handle_(), self.__obj, other.handle_()) return r - def __isub__(self, other): + def __isub__(self, other: MPI): _DLL.botan_mp_sub(self.__obj, self.__obj, other.handle_()) return self - def __mul__(self, other): + def __mul__(self, other: MPI): r = MPI() _DLL.botan_mp_mul(r.handle_(), self.__obj, other.handle_()) return r - def __imul__(self, other): + def __imul__(self, other: MPI): _DLL.botan_mp_mul(self.__obj, self.__obj, other.handle_()) return self - def __divmod__(self, other): + def __divmod__(self, other: MPI): d = MPI() q = MPI() _DLL.botan_mp_div(d.handle_(), q.handle_(), self.__obj, other.handle_()) return (d, q) - def __mod__(self, other): + def __mod__(self, other: MPI): d = MPI() q = MPI() _DLL.botan_mp_div(d.handle_(), q.handle_(), self.__obj, other.handle_()) return q - def __lshift__(self, shift): - shift = c_size_t(shift) + def __lshift__(self, shift: int): r = MPI() - _DLL.botan_mp_lshift(r.handle_(), self.__obj, shift) + _DLL.botan_mp_lshift(r.handle_(), self.__obj, c_size_t(shift)) return r - def __ilshift__(self, shift): - shift = c_size_t(shift) - _DLL.botan_mp_lshift(self.__obj, self.__obj, shift) + def __ilshift__(self, shift: int): + _DLL.botan_mp_lshift(self.__obj, self.__obj, c_size_t(shift)) return self - def __rshift__(self, shift): - shift = c_size_t(shift) + def __rshift__(self, shift: int): r = MPI() - _DLL.botan_mp_rshift(r.handle_(), self.__obj, shift) + _DLL.botan_mp_rshift(r.handle_(), self.__obj, c_size_t(shift)) return r - def __irshift__(self, shift): - shift = c_size_t(shift) - _DLL.botan_mp_rshift(self.__obj, self.__obj, shift) + def __irshift__(self, shift: int): + _DLL.botan_mp_rshift(self.__obj, self.__obj, c_size_t(shift)) return self - def mod_mul(self, other, modulus): + def mod_mul(self, other: MPI, modulus: MPI) -> MPI: + """Return the multiplication product of ``self`` and ``other`` modulo ``modulus``""" r = MPI() _DLL.botan_mp_mod_mul(r.handle_(), self.__obj, other.handle_(), modulus.handle_()) return r - def gcd(self, other): + def gcd(self, other: MPI) -> MPI: + """Return the greatest common divisor of ``self`` and ``other``""" r = MPI() _DLL.botan_mp_gcd(r.handle_(), self.__obj, other.handle_()) return r - def pow_mod(self, exponent, modulus): + def pow_mod(self, exponent: MPI, modulus: MPI) -> MPI: + """Return ``self`` to the ``exponent`` power modulo ``modulus``""" r = MPI() _DLL.botan_mp_powmod(r.handle_(), self.__obj, exponent.handle_(), modulus.handle_()) return r - def is_prime(self, rng_obj, prob=128): + def is_prime(self, rng_obj: RandomNumberGenerator, prob: int = 128) -> bool: + """Test if ``self`` is prime""" return _DLL.botan_mp_is_prime(self.__obj, rng_obj.handle_(), c_size_t(prob)) == 1 - def inverse_mod(self, modulus): + def inverse_mod(self, modulus: MPI) -> MPI: + """Return the inverse of ``self`` modulo ``modulus``, or zero if no inverse exists""" r = MPI() _DLL.botan_mp_mod_inverse(r.handle_(), self.__obj, modulus.handle_()) return r - def bit_count(self): + def bit_count(self) -> int: b = c_size_t(0) _DLL.botan_mp_num_bits(self.__obj, byref(b)) return b.value - def byte_count(self): + def byte_count(self) -> int: b = c_size_t(0) _DLL.botan_mp_num_bytes(self.__obj, byref(b)) return b.value - def get_bit(self, bit): + def get_bit(self, bit: int) -> bool: return _DLL.botan_mp_get_bit(self.__obj, c_size_t(bit)) == 1 - def clear_bit(self, bit): + def clear_bit(self, bit: int): _DLL.botan_mp_clear_bit(self.__obj, c_size_t(bit)) - def set_bit(self, bit): + def set_bit(self, bit: int): _DLL.botan_mp_set_bit(self.__obj, c_size_t(bit)) + +class OID: + def __init__(self, obj: c_void_p | None = None): + if not obj: + obj = c_void_p(0) + self.__obj = obj + + def __del__(self): + _DLL.botan_oid_destroy(self.__obj) + + def handle_(self): + return self.__obj + + @classmethod + def from_string(cls, value: str) -> OID: + """Create a new OID from dot notation or from a known name""" + oid = OID() + _DLL.botan_oid_from_string(byref(oid.handle_()), _ctype_str(value)) + return oid + + def to_string(self) -> str: + """Export the OID in dot notation""" + return _call_fn_viewing_str(lambda vc, vfn: _DLL.botan_oid_view_string(self.__obj, vc, vfn)) + + def to_name(self) -> str: + """Export the OID as a name if it has one, else in dot notation""" + return _call_fn_viewing_str(lambda vc, vfn: _DLL.botan_oid_view_name(self.__obj, vc, vfn)) + + def register(self, name: str): + """Register the OID so that it may later be retrieved by the given name""" + _DLL.botan_oid_register(self.__obj, _ctype_str(name)) + + def cmp(self, other: OID) -> int: + r = c_int(0) + _DLL.botan_oid_cmp(byref(r), self.__obj, other.handle_()) + return r.value + + def __eq__(self, other: OID | object) -> bool: + if isinstance(other, OID): + return self.cmp(other) == 0 + else: + return False + + def __ne__(self, other: OID | object) -> bool: + if isinstance(other, OID): + return self.cmp(other) != 0 + else: + return False + + def __lt__(self, other: OID | object) -> bool: + if isinstance(other, OID): + return self.cmp(other) < 0 + else: + return False + + def __le__(self, other: OID | object) -> bool: + if isinstance(other, OID): + return self.cmp(other) <= 0 + else: + return False + + def __gt__(self, other: OID | object) -> bool: + if isinstance(other, OID): + return self.cmp(other) > 0 + else: + return False + + def __ge__(self, other: OID | object) -> bool: + if isinstance(other, OID): + return self.cmp(other) >= 0 + else: + return False + + +class ECGroup: + def __init__(self, obj: c_void_p | None = None): + if not obj: + obj = c_void_p(0) + self.__obj = obj + + def handle_(self): + return self.__obj + + def __del__(self): + _DLL.botan_ec_group_destroy(self.__obj) + + @classmethod + def supports_application_specific_group(cls) -> bool: + """Returns true if in this build configuration it is possible + to register an application specific elliptic curve""" + r = c_int(0) + _DLL.botan_ec_group_supports_application_specific_group(byref(r)) + if r.value == 0: + return False + return True + + @classmethod + def supports_named_group(cls, name: str) -> bool: + """Returns true if in this build configuration `ECGroup.from_name(name)` will succeed""" + r = c_int(0) + _DLL.botan_ec_group_supports_named_group(_ctype_str(name), byref(r)) + if r.value == 0: + return False + return True + + @classmethod + def from_params(cls, oid: OID, p: MPI, a: MPI, b: MPI, base_x: MPI, base_y: MPI, order: MPI) -> ECGroup: + """Creates a new ECGroup from ec parameters""" + ec_group = ECGroup() + _DLL.botan_ec_group_from_params( + byref(ec_group.handle_()), + oid.handle_(), + p.handle_(), + a.handle_(), + b.handle_(), + base_x.handle_(), + base_y.handle_(), + order.handle_() + ) + return ec_group + + @classmethod + def from_ber(cls, ber: bytes) -> ECGroup: + """Creates a new ECGroup from a BER blob""" + ec_group = ECGroup() + _DLL.botan_ec_group_from_ber(byref(ec_group.handle_()), ber, len(ber)) + return ec_group + + @classmethod + def from_pem(cls, pem: str) -> ECGroup: + """Creates a new ECGroup from a PEM encoding""" + ec_group = ECGroup() + _DLL.botan_ec_group_from_pem(byref(ec_group.handle_()), _ctype_str(pem)) + return ec_group + + @classmethod + def from_oid(cls, oid: OID) -> ECGroup: + """Creates a new ECGroup from a group named by an OID""" + ec_group = ECGroup() + _DLL.botan_ec_group_from_oid(byref(ec_group.handle_()), oid.handle_()) + return ec_group + + @classmethod + def from_name(cls, name: str) -> ECGroup: + """Creates a new ECGroup from a common group name""" + ec_group = ECGroup() + _DLL.botan_ec_group_from_name(byref(ec_group.handle_()), _ctype_str(name)) + return ec_group + + @classmethod + def unregister(cls, oid: OID) -> bool: + """Unregister a previously registered group""" + rc = _DLL.botan_ec_group_unregister(oid.handle_()) + if rc == 1: + return True + return False + + def to_der(self) -> bytes: + """Export the group in DER encoding""" + return _call_fn_viewing_vec(lambda vc, vfn: _DLL.botan_ec_group_view_der(self.__obj, vc, vfn)) + + def to_pem(self) -> str: + """Export the group in PEM encoding""" + return _call_fn_viewing_str(lambda vc, vfn: _DLL.botan_ec_group_view_pem(self.__obj, vc, vfn)) + + def get_curve_oid(self) -> OID: + """Get the curve OID""" + oid = OID() + _DLL.botan_ec_group_get_curve_oid(byref(oid.handle_()), self.__obj) + return oid + + def get_p(self) -> MPI: + """Get the prime modulus of the field""" + p = c_void_p(0) + _DLL.botan_ec_group_get_p(byref(p), self.__obj) + return MPI(p) + + def get_a(self) -> MPI: + """Get the a parameter of the elliptic curve equation""" + a = c_void_p(0) + _DLL.botan_ec_group_get_a(byref(a), self.__obj) + return MPI(a) + + def get_b(self) -> MPI: + """Get the b parameter of the elliptic curve equation""" + b = c_void_p(0) + _DLL.botan_ec_group_get_b(byref(b), self.__obj) + return MPI(b) + + def get_g_x(self) -> MPI: + """Get the x coordinate of the base point""" + g_x = c_void_p(0) + _DLL.botan_ec_group_get_g_x(byref(g_x), self.__obj) + return MPI(g_x) + + def get_g_y(self) -> MPI: + """Get the y coordinate of the base point""" + g_y = c_void_p(0) + _DLL.botan_ec_group_get_g_y(byref(g_y), self.__obj) + return MPI(g_y) + + def get_order(self) -> MPI: + """Get the order of the base point""" + order = c_void_p(0) + _DLL.botan_ec_group_get_order(byref(order), self.__obj) + return MPI(order) + + def get_identity(self) -> ECPoint: + return ECPoint.identity(self) + + def get_generator(self) -> ECPoint: + return ECPoint.generator(self) + + def __eq__(self, other: ECGroup | object) -> bool: + if isinstance(other, ECGroup): + return _DLL.botan_ec_group_equal(self.__obj, other.handle_()) == 1 + else: + return False + + def __ne__(self, other: ECGroup | object) -> bool: + return not self == other + + +class ECScalar: + def __init__(self, obj: c_void_p | None = None): + if not obj: + obj = c_void_p(0) + self.__obj = obj + + def handle_(self): + return self.__obj + + def __del__(self): + _DLL.botan_ec_scalar_destroy(self.__obj) + + @classmethod + def random(cls, group: ECGroup, rng: RandomNumberGenerator) -> ECScalar: + """Create a new scalar with a random value""" + scalar = ECScalar() + _DLL.botan_ec_scalar_random(byref(scalar.handle_()), group.handle_(), rng.handle_()) + return scalar + + @classmethod + def from_mpi(cls, group: ECGroup, mpi: MPI) -> ECScalar: + """Convert from an MPI to a scalar. Raises an exception if the MPI is negative or too large.""" + scalar = ECScalar() + _DLL.botan_ec_scalar_from_mp(byref(scalar.handle_()), group.handle_(), mpi.handle_()) + return scalar + + def to_mpi(self) -> MPI: + """Convert from a scalar to an MPI.""" + obj = c_void_p(0) + _DLL.botan_ec_scalar_to_mp(self.__obj, byref(obj)) + return MPI(obj) + + +class ECPoint: + def __init__(self, obj: c_void_p | None = None): + if not obj: + obj = c_void_p(0) + self.__obj = obj + + def handle_(self): + return self.__obj + + def __del__(self): + _DLL.botan_ec_point_destroy(self.__obj) + + @classmethod + def identity(cls, group: ECGroup) -> ECPoint: + """Create a point set to the group identity""" + ec_point = ECPoint() + _DLL.botan_ec_point_identity(byref(ec_point.handle_()), group.handle_()) + return ec_point + + @classmethod + def generator(cls, group: ECGroup) -> ECPoint: + """Create a point set to the group generator""" + ec_point = ECPoint() + _DLL.botan_ec_point_generator(byref(ec_point.handle_()), group.handle_()) + return ec_point + + @classmethod + def from_xy(cls, group: ECGroup, x: MPI, y: MPI) -> ECPoint: + """Create a point from a set of (x,y) integers. + The integers must be within the field and must satisfy the curve equation.""" + ec_point = ECPoint() + _DLL.botan_ec_point_from_xy(byref(ec_point.handle_()), group.handle_(), x.handle_(), y.handle_()) + return ec_point + + @classmethod + def from_bytes(cls, group: ECGroup, buf: bytes) -> ECPoint: + """Create a point from a SEC1 compressed or uncompressed format.""" + ec_point = ECPoint() + _DLL.botan_ec_point_from_bytes(byref(ec_point.handle_()), group.handle_(), buf, len(buf)) + return ec_point + + def __eq__(self, other: ECPoint | object) -> bool: + if isinstance(other, ECPoint): + return _DLL.botan_ec_point_equal(self.__obj, other.handle_()) == 1 + else: + return False + + def __ne__(self, other: ECPoint | object) -> bool: + return not self == other + + def __add__(self, other: ECPoint): + r = ECPoint() + _DLL.botan_ec_point_add(byref(r.handle_()), self.__obj, other.handle_()) + return r + + def is_identity(self): + return _DLL.botan_ec_point_is_identity(self.__obj) == 1 + + def negate(self) -> ECPoint: + r = ECPoint() + _DLL.botan_ec_point_negate(byref(r.handle_()), self.__obj) + return r + + def mul(self, scalar: ECScalar, rng: RandomNumberGenerator) -> ECPoint: + r = ECPoint() + _DLL.botan_ec_point_mul(byref(r.handle_()), self.__obj, scalar.handle_(), rng.handle_()) + return r + + def to_x_bytes(self) -> bytes: + """Get the fixed length encoding of the affine x coordinate""" + return _call_fn_viewing_vec(lambda vc, vfn: _DLL.botan_ec_point_view_x_bytes(self.__obj, vc, vfn)) + + def to_y_bytes(self) -> bytes: + """Get the fixed length encoding of the affine y coordinate""" + return _call_fn_viewing_vec(lambda vc, vfn: _DLL.botan_ec_point_view_y_bytes(self.__obj, vc, vfn)) + + def to_xy_bytes(self) -> bytes: + """Get the fixed length encoding of the affine x and y coordinates""" + return _call_fn_viewing_vec(lambda vc, vfn: _DLL.botan_ec_point_view_xy_bytes(self.__obj, vc, vfn)) + + def to_uncompressed(self) -> bytes: + """Get the fixed length SEC1 uncompressed encoding""" + return _call_fn_viewing_vec(lambda vc, vfn: _DLL.botan_ec_point_view_uncompressed(self.__obj, vc, vfn)) + + def to_compressed(self) -> bytes: + """Get the fixed length SEC1 compressed encoding""" + return _call_fn_viewing_vec(lambda vc, vfn: _DLL.botan_ec_point_view_compressed(self.__obj, vc, vfn)) + + class FormatPreservingEncryptionFE1: - def __init__(self, modulus, key, rounds=5, compat_mode=False): + def __init__(self, modulus: MPI, key: bytes, rounds: int = 5, compat_mode: bool = False): + """Initialize an instance for format preserving encryption""" flags = c_uint32(1 if compat_mode else 0) self.__obj = c_void_p(0) _DLL.botan_fpe_fe1_init(byref(self.__obj), modulus.handle_(), key, len(key), c_size_t(rounds), flags) @@ -2122,32 +3181,45 @@ def __del__(self): _DLL.botan_fpe_destroy(self.__obj) - def encrypt(self, msg, tweak): + def encrypt(self, msg: MPILike, tweak: str | bytes) -> MPI: + """The msg should be a `botan3.MPI` or an object which can be converted to one""" r = MPI(msg) bits = _ctype_bits(tweak) _DLL.botan_fpe_encrypt(self.__obj, r.handle_(), bits, len(bits)) return r - def decrypt(self, msg, tweak): + def decrypt(self, msg: MPILike, tweak: str | bytes) -> MPI: + """The msg should be a `botan3.MPI` or an object which can be converted to one""" r = MPI(msg) bits = _ctype_bits(tweak) _DLL.botan_fpe_decrypt(self.__obj, r.handle_(), bits, len(bits)) return r class HOTP: - def __init__(self, key, digest="SHA-1", digits=6): + def __init__(self, key: bytes, digest: str = "SHA-1", digits: int = 6): self.__obj = c_void_p(0) _DLL.botan_hotp_init(byref(self.__obj), key, len(key), _ctype_str(digest), digits) def __del__(self): _DLL.botan_hotp_destroy(self.__obj) - def generate(self, counter): + def generate(self, counter: int) -> int: + """Generate an HOTP code for the provided counter""" code = c_uint32(0) _DLL.botan_hotp_generate(self.__obj, byref(code), counter) return code.value - def check(self, code, counter, resync_range=0): + def check(self, code: int, counter: int, resync_range: int = 0) -> tuple[bool, int]: + """Check if provided ``code`` is the correct code for ``counter``. + If ``resync_range`` is greater than zero, HOTP also checks + up to ``resync_range`` following counter values. + + Returns a tuple of (bool,int) where the boolean indicates if the + code was valid, and the int indicates the next counter value + that should be used. If the code did not verify, the next + counter value is always identical to the counter that was passed + in. If the code did verify and resync_range was zero, then the + next counter will always be counter+1.""" next_ctr = c_uint64(0) rc = _DLL.botan_hotp_check(self.__obj, byref(next_ctr), code, counter, resync_range) if rc == 0: @@ -2156,21 +3228,21 @@ return (False, counter) class TOTP: - def __init__(self, key, digest="SHA-1", digits=6, timestep=30): + def __init__(self, key: bytes, digest: str = "SHA-1", digits: int = 6, timestep: int = 30): self.__obj = c_void_p(0) _DLL.botan_totp_init(byref(self.__obj), key, len(key), _ctype_str(digest), digits, timestep) def __del__(self): _DLL.botan_totp_destroy(self.__obj) - def generate(self, timestamp=None): + def generate(self, timestamp: int | None = None) -> int: if timestamp is None: timestamp = int(system_time()) code = c_uint32(0) _DLL.botan_totp_generate(self.__obj, byref(code), timestamp) return code.value - def check(self, code, timestamp=None, acceptable_drift=0): + def check(self, code: int, timestamp: int | None = None, acceptable_drift: int = 0) -> bool: if timestamp is None: timestamp = int(system_time()) rc = _DLL.botan_totp_check(self.__obj, code, timestamp, acceptable_drift) @@ -2178,7 +3250,7 @@ return True return False -def nist_key_wrap(kek, key, cipher=None): +def nist_key_wrap(kek: bytes, key: bytes, cipher: str | None = None) -> bytes: cipher_algo = "AES-%d" % (8*len(kek)) if cipher is None else cipher padding = 0 output = create_string_buffer(len(key) + 8) @@ -2187,9 +3259,9 @@ key, len(key), kek, len(kek), output, byref(out_len)) - return output[0:int(out_len.value)] + return bytes(output[0:int(out_len.value)]) -def nist_key_unwrap(kek, wrapped, cipher=None): +def nist_key_unwrap(kek: bytes, wrapped: bytes, cipher: str | None = None) -> bytes: cipher_algo = "AES-%d" % (8*len(kek)) if cipher is None else cipher padding = 0 output = create_string_buffer(len(wrapped)) @@ -2198,21 +3270,43 @@ wrapped, len(wrapped), kek, len(kek), output, byref(out_len)) - return output[0:int(out_len.value)] + return bytes(output[0:int(out_len.value)]) + +def nist_key_wrap_padded(kek: bytes, key: bytes, cipher: str | None = None) -> bytes: + cipher_algo = "AES-%d" % (8*len(kek)) if cipher is None else cipher + padding = 1 + output = create_string_buffer(len(key) + 15) + out_len = c_size_t(len(output)) + _DLL.botan_nist_kw_enc(_ctype_str(cipher_algo), padding, + key, len(key), + kek, len(kek), + output, byref(out_len)) + return bytes(output[0:int(out_len.value)]) + +def nist_key_unwrap_padded(kek: bytes, wrapped: bytes, cipher: str | None = None) -> bytes: + cipher_algo = "AES-%d" % (8*len(kek)) if cipher is None else cipher + padding = 1 + output = create_string_buffer(len(wrapped)) + out_len = c_size_t(len(output)) + _DLL.botan_nist_kw_dec(_ctype_str(cipher_algo), padding, + wrapped, len(wrapped), + kek, len(kek), + output, byref(out_len)) + return bytes(output[0:int(out_len.value)]) class Srp6ServerSession: __obj = c_void_p(0) - def __init__(self, group): + def __init__(self, group: str): _DLL.botan_srp6_server_session_init(byref(self.__obj)) self.__group = group self.__group_size = _call_fn_returning_sz( - lambda l: _DLL.botan_srp6_group_size(_ctype_str(group), l)) + lambda len: _DLL.botan_srp6_group_size(_ctype_str(group), len)) def __del__(self): _DLL.botan_srp6_server_session_destroy(self.__obj) - def step1(self, verifier, hsh, rng): + def step1(self, verifier: bytes, hsh: str, rng: RandomNumberGenerator) -> bytes: return _call_fn_returning_vec(self.__group_size, lambda b, bl: _DLL.botan_srp6_server_session_step1(self.__obj, @@ -2222,14 +3316,14 @@ rng.handle_(), b, bl)) - def step2(self, a): + def step2(self, a: bytes) -> bytes: return _call_fn_returning_vec(self.__group_size, lambda k, kl: _DLL.botan_srp6_server_session_step2(self.__obj, a, len(a), k, kl)) -def srp6_generate_verifier(identifier, password, salt, group, hsh): - sz = _call_fn_returning_sz(lambda l: _DLL.botan_srp6_group_size(_ctype_str(group), l)) +def srp6_generate_verifier(identifier: str, password: str, salt: bytes, group: str, hsh: str) -> bytes: + sz = _call_fn_returning_sz(lambda len: _DLL.botan_srp6_group_size(_ctype_str(group), len)) return _call_fn_returning_vec(sz, lambda v, vl: _DLL.botan_srp6_generate_verifier(_ctype_str(identifier), @@ -2239,8 +3333,8 @@ _ctype_str(hsh), v, vl)) -def srp6_client_agree(username, password, group, hsh, salt, b, rng): - sz = _call_fn_returning_sz(lambda l: _DLL.botan_srp6_group_size(_ctype_str(group), l)) +def srp6_client_agree(username: str, password: str, group: str, hsh: str, salt: bytes, b: bytes, rng: RandomNumberGenerator) -> tuple[bytes, bytes]: + sz = _call_fn_returning_sz(lambda len: _DLL.botan_srp6_group_size(_ctype_str(group), len)) return _call_fn_returning_vec_pair(sz, sz, lambda a, al, k, kl: _DLL.botan_srp6_client_agree(_ctype_str(username), @@ -2253,13 +3347,13 @@ a, al, k, kl)) -def zfec_encode(k, n, input_bytes): +def zfec_encode(k: int, n: int, input_bytes: bytes) -> list[bytes]: """ ZFEC-encode an input message according to the given parameters - :param int k: the number of shares required to recover the original - :param int n: the total number of shares - :param bytes input_bytes: the input message, in bytes + :param k: the number of shares required to recover the original + :param n: the total number of shares + :param input_bytes: the input message, in bytes :returns: n arrays of bytes, each one containing a single share """ @@ -2286,18 +3380,16 @@ return [output.raw for output in outputs] -def zfec_decode(k, n, indexes, inputs): +def zfec_decode(k: int, n: int, indexes: list[int], inputs: list[bytes]) -> list[bytes]: """ ZFEC decode - :param int k: the number of shares required to recover the original - :param int n: the total number of shares - :param list[int] indexes: which of the shares are we giving the decoder - :param list[bytes] inputs: the input shares (e.g. from a previous - call to zfec_encode) which all must be the same length + :param k: the number of shares required to recover the original + :param n: the total number of shares + :param indexes: which of the shares are we giving the decoder + :param inputs: the input shares (e.g. from a previous call to zfec_encode) which all must be the same length - :returns: a list of bytes containing the original shares decoded - from the provided shares (in `inputs`) + :returns: a list of bytes containing the original shares decoded from the provided shares (in `inputs`) """ if len(inputs) < k: diff -Nru botan3-3.7.1+dfsg/src/scripts/Dockerfile.android botan3-3.12.0+dfsg/src/scripts/Dockerfile.android --- botan3-3.7.1+dfsg/src/scripts/Dockerfile.android 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/Dockerfile.android 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -FROM devnexen/android-ndk:r20 AS android-ndk -RUN apt-get update && apt-get install -y --no-install-recommends python -RUN mkdir -p /botan/android -WORKDIR /botan -COPY configure.py configure.py -COPY src src -COPY doc doc -COPY license.txt license.txt -COPY news.rst news.rst -ARG ANDROID_ARCH -ARG ANDROID_TOOLCHAIN_SUF -ARG ANDROID_ARCH_SUF -ARG ANDROID_SDK_VER -ENV PATH=$PATH:/opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/ -RUN ./configure.py --prefix=android/arm --os=android --cpu=${ANDROID_ARCH} --cc=clang --cc-bin=${ANDROID_ARCH}${ANDROID_ARCH_SUF}-linux-android${ANDROID_TOOLCHAIN_SUF}${ANDROID_SDK_VER}-clang++ --ar-command=${ANDROID_ARCH}${ANDROID_ARCH_SUF}-linux-android${ANDROID_TOOLCHAIN_SUF}-ar -RUN make -j`getconf _NPROCESSORS_ONLN` -RUN make install diff -Nru botan3-3.7.1+dfsg/src/scripts/acvp_tests.py botan3-3.12.0+dfsg/src/scripts/acvp_tests.py --- botan3-3.7.1+dfsg/src/scripts/acvp_tests.py 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/acvp_tests.py 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,2954 @@ +#!/usr/bin/env python3 + +""" +Validate against NIST ACVP test vectors + +Requires a checkout of https://github.com/usnistgov/ACVP-Server. Point +$ACVP_TESTDATA_DIR at the gen-val/json-files directory. + +(C) 2026 Jack Lloyd + +Botan is released under the Simplified BSD License (see license.txt) +""" + +import argparse +import binascii +import io +import json +import multiprocessing +import os +import subprocess +import sys +import traceback +from collections import Counter +from collections.abc import Callable +from dataclasses import dataclass, field +from pathlib import Path + +import botan3 as botan + + +# ---- Infra ---- + + +class TestSkip(Exception): + """Raised by a handler to skip a test vector.""" + + def __init__(self, reason: str): + self.reason = reason + super().__init__(reason) + + +class TestFailure(Exception): + """Raised by a handler when a test vector fails. + + Construct with either a dict of field_name -> value for .vec-style + output, or a plain string message. + """ + + def __init__(self, fields_or_message): + if isinstance(fields_or_message, dict): + self.fields = fields_or_message + msg = "; ".join(f"{k}={v}" for k, v in fields_or_message.items()) + else: + self.fields = None + msg = str(fields_or_message) + super().__init__(msg) + + +class FixedOutputRNG(botan.RandomNumberGenerator): + def __init__(self, entropy_pool: bytes = b""): + super().__init__( + "custom", get_callback=self._get, add_entropy_callback=self._add_entropy + ) + self._entropy_pool = entropy_pool + + def _get(self, length: int) -> bytes: + if length > len(self._entropy_pool): + raise ValueError("Not enough entropy in pool") + entropy = self._entropy_pool[:length] + self._entropy_pool = self._entropy_pool[length:] + return entropy + + def _add_entropy(self, data: bytes) -> None: + self._entropy_pool += data + + +class NullRNG(botan.RandomNumberGenerator): + """An RNG that raises an exception if it is used.""" + + def __init__(self): + super().__init__( + "custom", get_callback=self._get, add_entropy_callback=self._add_entropy + ) + + def _get(self, length: int) -> bytes: + raise botan.BotanException("Unexpected request to get entropy from RNG", rc=-23) + + def _add_entropy(self, data: bytes) -> None: + raise botan.BotanException("Unexpected request to add entropy to RNG", rc=-23) + + +class _Registry: + def __init__(self): + self._handlers: dict[str, Callable] = {} + self._ignored: set[str] = set() + + def register(self, algorithm: str, handler: Callable) -> None: + if algorithm in self._handlers: + raise RuntimeError( + f"Algorithm directory {algorithm!r} already registered by " + f"{self._handlers[algorithm].__name__}, cannot also register " + f"{handler.__name__}" + ) + self._handlers[algorithm] = handler + + def get(self, algorithm: str) -> Callable | None: + return self._handlers.get(algorithm) + + def ignore(self, *algorithms: str) -> None: + for algo in algorithms: + if algo in self._handlers: + raise RuntimeError(f"Algorithm {algo!r} is both registered and ignored") + self._ignored.add(algo) + + def is_ignored(self, algorithm: str) -> bool: + return algorithm in self._ignored + + +_registry = _Registry() + + +def register(*algorithm_dirs: str): + """Decorator to register a handler for one or more ACVP algorithm directories.""" + + def decorator(func): + for algo in algorithm_dirs: + _registry.register(algo, func) + return func + + return decorator + + +@dataclass +class _FileResult: + """Result of processing a single ACVP algorithm directory.""" + + algo_dir: str + category: str # "claimed", "unclaimed", "ignored", "missing", "malformed" + passed: int = 0 + failed: int = 0 + errors: int = 0 + skipped: int = 0 + skip_reasons: dict[str, int] = field(default_factory=dict) + output: str = "" + + +def _process_directory(args: tuple[str, str, int]) -> _FileResult: + algo_dir_str, data_dir_str, verbosity = args + algo_dir = Path(algo_dir_str) + data_dir = Path(data_dir_str) + algo_name = algo_dir.name + + if _registry.is_ignored(algo_name): + return _FileResult(algo_name, "ignored") + + handler = _registry.get(algo_name) + if handler is None: + return _FileResult(algo_name, "unclaimed") + + prompt_path = algo_dir / "prompt.json" + expected_path = algo_dir / "expectedResults.json" + + if not prompt_path.exists() or not expected_path.exists(): + return _FileResult(algo_name, "missing") + + try: + with open(prompt_path, encoding="utf-8") as f: + prompt = json.load(f) + with open(expected_path, encoding="utf-8") as f: + expected = json.load(f) + except (OSError, json.JSONDecodeError) as e: + result = _FileResult(algo_name, "malformed") + result.output = f"ERROR parsing {algo_dir.relative_to(data_dir)}: {e}\n" + return result + + header = {k: v for k, v in prompt.items() if k != "testGroups"} + + expected_by_tg: dict[int, dict[int, dict]] = {} + for eg in expected.get("testGroups", []): + expected_by_tg[eg["tgId"]] = {t["tcId"]: t for t in eg.get("tests", [])} + + result = _FileResult(algo_name, "claimed") + out = io.StringIO() + + if verbosity >= 2: + print(f"{algo_name}:", file=out) + + for group in prompt.get("testGroups", []): + tg_id = group["tgId"] + expected_tests = expected_by_tg.get(tg_id, {}) + + for test in group.get("tests", []): + tc_id = test.get("tcId", "?") + exp = expected_tests.get(tc_id, {}) + + try: + handler(header, group, test, exp) + result.passed += 1 + if verbosity >= 2: + print(f" PASS: tgId={tg_id} tcId={tc_id}", file=out) + except TestSkip as e: + result.skipped += 1 + reason = str(e) + result.skip_reasons[reason] = result.skip_reasons.get(reason, 0) + 1 + if verbosity >= 2: + print(f" SKIP: tgId={tg_id} tcId={tc_id}: {reason}", file=out) + except TestFailure as e: + result.failed += 1 + print( + f"\nFAIL: # ACVP tgId={tg_id} tcId={tc_id} in {algo_name}", + file=out, + ) + if e.fields: + for key, value in e.fields.items(): + print(f"{key} = {value}", file=out) + else: + print(f" {e}", file=out) + except Exception as e: + result.errors += 1 + print( + f"\nERROR: # ACVP tgId={tg_id} tcId={tc_id} in {algo_name}", + file=out, + ) + print(f" {type(e).__name__}: {e}", file=out) + print(traceback.format_exc(), file=out) + + result.output = out.getvalue() + return result + + +def _git_rev(directory: str) -> str | None: + try: + result = subprocess.run( + ["git", "rev-parse", "HEAD"], + cwd=directory, + capture_output=True, + text=True, + timeout=5, + check=True, + ) + return result.stdout.strip() or None + except ( + FileNotFoundError, + subprocess.TimeoutExpired, + subprocess.CalledProcessError, + ): + return None + + +def _discover_algo_dirs(data_dir: Path) -> list[Path]: + """Return ACVP algorithm directories: subdirs containing a prompt.json.""" + dirs = [] + for entry in sorted(data_dir.iterdir()): + if entry.is_dir() and (entry / "prompt.json").exists(): + dirs.append(entry) + return dirs + + +def run( + data_dir_str: str, + verbosity: int = 1, + jobs: int | None = None, + filters: list[str] | None = None, +) -> int: + data_dir = Path(data_dir_str) + if not data_dir.is_dir(): + print(f"ERROR: {data_dir} is not a directory") + return 1 + + algo_dirs = _discover_algo_dirs(data_dir) + + if filters: + filters_lower = [f.lower() for f in filters] + algo_dirs = [ + d for d in algo_dirs if any(f in d.name.lower() for f in filters_lower) + ] + + work = [(str(d), str(data_dir), verbosity) for d in algo_dirs] + + if jobs == 1: + file_results = [_process_directory(item) for item in work] + else: + with multiprocessing.Pool(jobs) as pool: + file_results = pool.map(_process_directory, work) + + passed = failed = errors = skipped = 0 + files_claimed = files_ignored = files_missing = 0 + unclaimed: list[str] = [] + malformed: list[str] = [] + skip_reasons: Counter[str] = Counter() + + for fr in file_results: + if fr.output: + sys.stdout.write(fr.output) + + if fr.category == "ignored": + files_ignored += 1 + elif fr.category == "unclaimed": + unclaimed.append(fr.algo_dir) + elif fr.category == "missing": + files_missing += 1 + elif fr.category == "malformed": + malformed.append(fr.algo_dir) + else: + files_claimed += 1 + passed += fr.passed + failed += fr.failed + errors += fr.errors + skipped += fr.skipped + for reason, count in fr.skip_reasons.items(): + skip_reasons[reason] += count + + total_dirs = ( + files_claimed + files_ignored + files_missing + len(unclaimed) + len(malformed) + ) + total_tests = passed + failed + errors + skipped + + acvp_rev = _git_rev(str(data_dir)) + + print("ACVP Results") + print(f"Botan version: {botan.version_string()}") + if acvp_rev: + print(f"ACVP-Server revision: {acvp_rev}") + print( + f"Total {total_tests} Passed {passed} Failed {failed} " + f"Errors {errors} Skipped {skipped}" + ) + print( + f"Directories: {total_dirs} total, {files_claimed} claimed, " + f"{files_ignored} ignored, {len(unclaimed)} unclaimed, " + f"{files_missing} without expectedResults" + ) + + if verbosity >= 1: + if skip_reasons: + print("\nSkipped tests (by reason):") + for reason, count in skip_reasons.most_common(): + print(f" {count}x: {reason}") + + if unclaimed: + print(f"\nUnclaimed directories ({len(unclaimed)}):") + for name in unclaimed: + print(f" {name}") + + if malformed: + print(f"\nMalformed directories ({len(malformed)}):") + for name in malformed: + print(f" {name}") + + return 0 if (failed == 0 and errors == 0) else 1 + + +# ---- Common utilities ---- + + +def _from_hex(value: str) -> bytes: + return binascii.unhexlify(value) + + +def _opt_hex(d: dict, key: str) -> bytes: + v = d.get(key) + return _from_hex(v) if v else b"" + + +_HASH_MAP = { + "SHA-1": "SHA-1", + "SHA2-224": "SHA-224", + "SHA2-256": "SHA-256", + "SHA2-384": "SHA-384", + "SHA2-512": "SHA-512", + "SHA2-512/224": None, # not implemented + "SHA2-512/256": "SHA-512-256", + "SHA3-224": "SHA-3(224)", + "SHA3-256": "SHA-3(256)", + "SHA3-384": "SHA-3(384)", + "SHA3-512": "SHA-3(512)", + "SHAKE-128": "SHAKE-128(256)", + "SHAKE-256": "SHAKE-256(512)", +} + +_HMAC_ALGO_MAP = { + f"HMAC-{k}": f"HMAC({v})" for k, v in _HASH_MAP.items() if v is not None +} +_PBKDF_HMAC_MAP = { + k: f"PBKDF2({v})" + for k, v in _HASH_MAP.items() + if v is not None and not k.startswith("SHAKE") +} +_HMAC_DRBG_MODE_MAP = {k: v for k, v in _HASH_MAP.items() if not k.startswith("SHAKE")} +_KDF_HASH_MAP = {k: v for k, v in _HASH_MAP.items() if not k.startswith("SHAKE")} + +_CURVE_MAP = { + "P-192": "secp192r1", + "P-224": "secp224r1", + "P-256": "secp256r1", + "P-384": "secp384r1", + "P-521": "secp521r1", +} + + +def _map_hash(acvp_hash: str) -> str: + h = _HASH_MAP.get(acvp_hash) + if h is None: + raise TestSkip(f"Hash {acvp_hash} not supported") + return h + + +def _require_aft(group: dict) -> None: + test_type = group.get("testType", "AFT") + if test_type != "AFT": + raise TestSkip(f"testType {test_type} not supported") + + +# Handlers can stash per-group state on the group dict under keys +# prefixed with "_b_" to avoid clashing with ACVP fields. +def _group_state(group: dict, key: str): + return group.get("_b_" + key) + + +def _set_group_state(group: dict, key: str, value) -> None: + group["_b_" + key] = value + + +# ---- Hash / XOF shared helpers ---- + + +def _hash_aft(algo: str, test: dict, exp: dict) -> None: + bit_len = test["len"] + if bit_len % 8 != 0: + raise TestSkip("Bit-oriented input not supported") + + h = botan.HashFunction(algo) + msg = _opt_hex(test, "msg")[: bit_len // 8] + h.update(msg) + computed = h.final().hex() + if computed != exp["md"].lower(): + raise TestFailure( + {"Algo": algo, "Msg": msg.hex(), "MD": exp["md"], "ComputedMD": computed} + ) + + +def _sha3_mct(algo: str, test: dict, exp: dict) -> None: + # SHA-3 MCT: single-chain iteration. When seed length differs from + # hash output length, ACVP uses the "alternate" variant that + # truncates/pads each input to the original seed length (see + # ACVP-Server AlternateSizeSha3Mct.cs). + seed = _from_hex(test["msg"]) + seed_len = len(seed) + hash_len = botan.HashFunction(algo).output_length() + alternate = seed_len != hash_len + + md = seed + for j, result in enumerate(exp.get("resultsArray", [])): + for _ in range(1000): + m = md + if alternate: + if len(m) >= seed_len: + m = m[:seed_len] + else: + m = m + bytes(seed_len - len(m)) + h = botan.HashFunction(algo) + h.update(m) + md = h.final() + if md.hex() != result["md"].lower(): + raise TestFailure( + { + "Algo": algo, + "MctOuter": str(j), + "MD": result["md"], + "ComputedMD": md.hex(), + } + ) + + +def _hash_ldt(algo: str, test: dict, exp: dict) -> None: + large_msg = test["largeMsg"] + content = _from_hex(large_msg["content"]) + full_length = large_msg["fullLength"] + if large_msg.get("expansionTechnique", "repeating") != "repeating": + raise TestSkip(f"expansionTechnique {large_msg.get('expansionTechnique')}") + + h = botan.HashFunction(algo) + content_len = len(content) + remaining = full_length + while remaining > 0: + chunk = min(content_len, remaining) + h.update(content[:chunk]) + remaining -= chunk + computed = h.final().hex() + if computed != exp["md"].lower(): + raise TestFailure({"Algo": algo, "MD": exp["md"], "ComputedMD": computed}) + + +def _xof_aft(algo: str, test: dict, exp: dict) -> None: + out_bits = test.get("outLen", test.get("outputLen", 256)) + if out_bits % 8 != 0: + raise TestSkip("Bit-oriented input not supported") + out_len = out_bits // 8 + + msg = _opt_hex(test, "msg") + bit_len = test.get("len", test.get("inLen", len(msg) * 8)) + if bit_len % 8 != 0: + raise TestSkip("Bit-oriented input not supported") + msg = msg[: bit_len // 8] + + xof = botan.XOF(algo) + xof.update(msg) + output = xof.output(out_len).hex() + expected = exp.get("md") or exp.get("output") or "" + if output != expected.lower(): + raise TestFailure( + {"Algo": algo, "OutLen": str(out_bits), "Expected": expected, "Got": output} + ) + + +# ---- SHA-2 MCT (Merkle-Damgård 3-seed) ---- + + +def _sha2_mct(algo: str, test: dict, exp: dict) -> None: + seed = _from_hex(test["msg"]) + seed_len = len(seed) + hash_len = botan.HashFunction(algo).output_length() + + # ACVP has two SHA-2 MCT variants (see ACVP-Server + # StandardSizeShaMct.cs / AlternateSizeShaMct.cs): + # Standard: seed length == hash output length. + # MSG = MD[i-3] || MD[i-2] || MD[i-1] (no truncation) + # Alternate: seed length != hash output length. + # MSG = (MD[i-3] || MD[i-2] || MD[i-1]), then truncated or + # zero-padded to the original seed length before hashing. + alternate = seed_len != hash_len + + md = [seed, seed, seed] + for j, result in enumerate(exp.get("resultsArray", [])): + for _ in range(1000): + m = md[0] + md[1] + md[2] + if alternate: + if len(m) >= seed_len: + m = m[:seed_len] + else: + m = m + bytes(seed_len - len(m)) + h = botan.HashFunction(algo) + h.update(m) + md = [md[1], md[2], h.final()] + if md[2].hex() != result["md"].lower(): + raise TestFailure( + { + "Algo": algo, + "MctOuter": str(j), + "MD": result["md"], + "ComputedMD": md[2].hex(), + } + ) + md = [md[2], md[2], md[2]] + + +# ---- SHA-1 / SHA-2 / SHA-3 ---- + + +@register( + "SHA-1-2.0", + "SHA2-224-1.0", + "SHA2-256-1.0", + "SHA2-384-1.0", + "SHA2-512-1.0", + "SHA2-512-224-1.0", + "SHA2-512-256-1.0", + "SHA3-224-2.0", + "SHA3-256-2.0", + "SHA3-384-2.0", + "SHA3-512-2.0", +) +def handle_hash(header: dict, group: dict, test: dict, exp: dict) -> None: + algo = _map_hash(header["algorithm"]) + test_type = group.get("testType", "AFT") + + if test_type == "AFT": + _hash_aft(algo, test, exp) + elif test_type == "MCT": + if header["algorithm"].startswith("SHA3-"): + _sha3_mct(algo, test, exp) + else: + _sha2_mct(algo, test, exp) + elif test_type == "LDT": + if os.environ.get("ACVP_RUN_SLOW_TESTS") != "1": + raise TestSkip("LDT disabled (set ACVP_RUN_SLOW_TESTS=1)") + _hash_ldt(algo, test, exp) + else: + raise TestSkip(f"testType {test_type} not supported") + + +# ---- SHAKE ---- + + +@register( + "SHAKE-128-1.0", + "SHAKE-256-1.0", + "SHAKE-128-FIPS202", + "SHAKE-256-FIPS202", +) +def handle_shake(header: dict, group: dict, test: dict, exp: dict) -> None: + algo_name = header["algorithm"] + if algo_name in ("SHAKE-128", "SHAKE128"): + algo = "SHAKE-128" + elif algo_name in ("SHAKE-256", "SHAKE256"): + algo = "SHAKE-256" + else: + raise TestSkip(f"Unsupported XOF: {algo_name}") + + test_type = group.get("testType", "AFT") + if test_type == "MCT": + raise TestSkip("SHAKE MCT not implemented") + if test_type not in ("AFT", "VOT"): + raise TestSkip(f"testType {test_type} not supported") + + _xof_aft(algo, test, exp) + + +# ---- cSHAKE ---- + + +@register("cSHAKE-128-1.0", "cSHAKE-256-1.0") +def handle_cshake(header: dict, group: dict, test: dict, exp: dict) -> None: + algo_name = header["algorithm"] + if algo_name == "cSHAKE-128": + algo = "cSHAKE-128" + elif algo_name == "cSHAKE-256": + algo = "cSHAKE-256" + else: + raise TestSkip(f"Unsupported: {algo_name}") + + test_type = group.get("testType", "AFT") + if test_type == "MCT": + raise TestSkip("cSHAKE MCT not implemented") + if test_type != "AFT": + raise TestSkip(f"testType {test_type} not supported") + + if test.get("functionName"): + raise TestSkip("cSHAKE function_name not exposed in Python XOF API") + if test.get("customization"): + raise TestSkip("cSHAKE customization not exposed in Python XOF API") + + out_len = test["outLen"] // 8 + msg = _opt_hex(test, "msg") + bit_len = test.get("len", len(msg) * 8) + msg = msg[: bit_len // 8] + + xof = botan.XOF(algo) + xof.update(msg) + output = xof.output(out_len).hex() + if output != exp["output"].lower(): + raise TestFailure({"Algo": algo, "Expected": exp["output"], "Got": output}) + + +# ---- Ascon ---- + + +@register("Ascon-AEAD128-SP800-232") +def handle_ascon_aead(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + if group.get("supportsNonceMasking"): + raise TestSkip("Ascon nonce masking not implemented") + if test["tagLen"] != 128: + raise TestSkip("Ascon truncated tag lengths not implemented") + if test["payloadLen"] % 8 != 0: + raise TestSkip("Bit-oriented input not supported") + if test["adLen"] % 8 != 0: + raise TestSkip("Bit-oriented input not supported") + + _aead_aft( + algo="Ascon-AEAD128", + direction=group["direction"], + key=_from_hex(test["key"]), + nonce=_from_hex(test["nonce"]), + aad=_opt_hex(test, "ad"), + test=test, + exp=exp, + tag_bytes=16, + combined_ct=False, + ) + + +@register("Ascon-Hash256-SP800-232") +def handle_ascon_hash(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + _hash_aft("Ascon-Hash256", test, exp) + + +@register("Ascon-XOF128-SP800-232") +def handle_ascon_xof(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + _xof_aft("Ascon-XOF128", test, exp) + + +# ---- AEAD helpers ---- + + +def _resolve_cipher_or_skip(algo: str, group: dict) -> None: + cached = _group_state(group, "cipher_ok") + if cached is True: + return + if cached is False: + raise TestSkip(f"Unsupported: {algo}") + try: + botan.SymmetricCipher(algo, True) + _set_group_state(group, "cipher_ok", True) + except botan.BotanException as e: + _set_group_state(group, "cipher_ok", False) + raise TestSkip(f"Unsupported: {algo}") from e + + +def _aead_aft( + algo: str, + direction: str, + key: bytes, + nonce: bytes, + aad: bytes, + test: dict, + exp: dict, + tag_bytes: int | None, + combined_ct: bool, +) -> None: + """Shared AEAD AFT handler. + + Parameters: + algo: Botan cipher name, already resolved. + direction: "encrypt" or "decrypt". + key, nonce, aad: prepared byte inputs. + test, exp: ACVP test/expected dicts (used for field lookup and messages). + tag_bytes: byte length of the tag. None if unknown (decryption only). + combined_ct: True when the ACVP ``ct`` field holds ``ct || tag`` (e.g. + CCM). False when ``ct`` and ``tag`` are separate top-level fields. + """ + if direction == "encrypt": + pt = _opt_hex(test, "pt") + enc = botan.SymmetricCipher(algo, True) + enc.set_key(key) + enc.set_assoc_data(aad) + enc.start(nonce) + ct_tag = enc.finish(pt) + + if combined_ct: + expected_ct_tag = exp["ct"] + computed_ct_tag = ct_tag.hex() + if computed_ct_tag != expected_ct_tag.lower(): + raise TestFailure( + { + "Algo": algo, + "Key": test["key"], + "IV": test.get("iv", test.get("nonce", "")), + "CT": expected_ct_tag, + "ComputedCT": computed_ct_tag, + } + ) + return + + assert tag_bytes is not None + computed_ct = ct_tag[:-tag_bytes].hex() + computed_tag = ct_tag[-tag_bytes:].hex() + expected_ct = exp.get("ct", "") + expected_tag = exp["tag"] + if computed_ct != expected_ct.lower() or computed_tag != expected_tag.lower(): + raise TestFailure( + { + "Algo": algo, + "Key": test["key"], + "IV": test.get("iv", test.get("nonce", "")), + "AAD": test.get("aad", test.get("ad", "")), + "PT": test.get("pt", ""), + "CT": expected_ct, + "Tag": expected_tag, + "ComputedCT": computed_ct, + "ComputedTag": computed_tag, + } + ) + return + + # decrypt + if combined_ct: + ct_tag = _opt_hex(test, "ct") + else: + ct_tag = _opt_hex(test, "ct") + _from_hex(test["tag"]) + + should_pass = exp.get("testPassed", True) + try: + dec = botan.SymmetricCipher(algo, False) + dec.set_key(key) + dec.set_assoc_data(aad) + dec.start(nonce) + pt = dec.finish(ct_tag) + except botan.BotanException as e: + if should_pass: + raise TestFailure( + { + "Algo": algo, + "Key": test["key"], + "Note": "Valid AEAD decryption failed", + } + ) from e + return + + if not should_pass: + raise TestFailure( + {"Algo": algo, "Note": "Invalid AEAD test decrypted successfully"} + ) + expected_pt = exp.get("pt", "") + if pt.hex() != expected_pt.lower(): + raise TestFailure({"Algo": algo, "PT": expected_pt, "ComputedPT": pt.hex()}) + + +# ---- AES-GCM ---- + + +@register("ACVP-AES-GCM-1.0") +def handle_aes_gcm(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + key_len = group["keyLen"] + tag_bytes = group["tagLen"] // 8 + algo = f"AES-{key_len}/GCM({tag_bytes})" + _resolve_cipher_or_skip(algo, group) + + _aead_aft( + algo=algo, + direction=group["direction"], + key=_from_hex(test["key"]), + nonce=_from_hex(test["iv"]), + aad=_opt_hex(test, "aad"), + test=test, + exp=exp, + tag_bytes=tag_bytes, + combined_ct=False, + ) + + +# ---- AES-CCM ---- + + +@register("ACVP-AES-CCM-1.0") +def handle_aes_ccm(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + key_len = group["keyLen"] + tag_bytes = group["tagLen"] // 8 + iv_len = group["ivLen"] // 8 + l_val = 15 - iv_len + algo = f"AES-{key_len}/CCM({tag_bytes},{l_val})" + _resolve_cipher_or_skip(algo, group) + + _aead_aft( + algo=algo, + direction=group["direction"], + key=_from_hex(test["key"]), + nonce=_from_hex(test["iv"]), + aad=_opt_hex(test, "aad"), + test=test, + exp=exp, + tag_bytes=tag_bytes, + combined_ct=True, + ) + + +# ---- Block cipher modes (AES / TDES) ---- + +_BLOCK_MODE_MAP = { + "ACVP-AES-CBC": ("AES-{keyLen}", "CBC/NoPadding"), + "ACVP-AES-ECB": ("AES-{keyLen}", "ECB"), + "ACVP-AES-OFB": ("AES-{keyLen}", "OFB"), + "ACVP-AES-CFB8": ("AES-{keyLen}", "CFB(8)"), + "ACVP-AES-CFB128": ("AES-{keyLen}", "CFB"), + "ACVP-TDES-ECB": ("TripleDES", "ECB"), + "ACVP-TDES-CBC": ("TripleDES", "CBC/NoPadding"), + "ACVP-TDES-OFB": ("TripleDES", "OFB"), + "ACVP-TDES-CFB64": ("TripleDES", "CFB"), + "ACVP-TDES-CFB8": ("TripleDES", "CFB(8)"), +} + + +def _block_mode_aft( + bc_name: str, + mode: str, + key: bytes, + test: dict, + exp: dict, + encrypt: bool, + group: dict, +) -> None: + if mode == "ECB": + bc = botan.BlockCipher(bc_name) + bc.set_key(key) + if encrypt: + got = bytes(bc.encrypt(_opt_hex(test, "pt"))).hex() + if got != exp["ct"].lower(): + raise TestFailure( + {"Algo": f"{bc_name}/ECB", "CT": exp["ct"], "ComputedCT": got} + ) + else: + got = bytes(bc.decrypt(_opt_hex(test, "ct"))).hex() + if got != exp["pt"].lower(): + raise TestFailure( + {"Algo": f"{bc_name}/ECB", "PT": exp["pt"], "ComputedPT": got} + ) + return + + algo = f"{bc_name}/{mode}" + _resolve_cipher_or_skip(algo, group) + + cipher = botan.SymmetricCipher(algo, encrypt) + cipher.set_key(key) + cipher.start(_opt_hex(test, "iv")) + + if encrypt: + got = cipher.finish(_opt_hex(test, "pt")).hex() + if got != exp["ct"].lower(): + raise TestFailure({"Algo": algo, "CT": exp["ct"], "ComputedCT": got}) + else: + got = cipher.finish(_opt_hex(test, "ct")).hex() + if got != exp["pt"].lower(): + raise TestFailure({"Algo": algo, "PT": exp["pt"], "ComputedPT": got}) + + +@register( + "ACVP-AES-CBC-1.0", + "ACVP-AES-CFB128-1.0", + "ACVP-AES-CFB8-1.0", + "ACVP-AES-ECB-1.0", + "ACVP-AES-OFB-1.0", + "ACVP-TDES-CBC-1.0", + "ACVP-TDES-CFB64-1.0", + "ACVP-TDES-CFB8-1.0", + "ACVP-TDES-ECB-1.0", + "ACVP-TDES-OFB-1.0", +) +def handle_block_modes(header: dict, group: dict, test: dict, exp: dict) -> None: + test_type = group.get("testType", "AFT") + if test_type == "MCT": + raise TestSkip("Block mode MCT not yet implemented") + if test_type != "AFT": + raise TestSkip(f"testType {test_type} not supported") + + entry = _BLOCK_MODE_MAP.get(header["algorithm"]) + if entry is None: + raise TestSkip(f"Unsupported algorithm: {header['algorithm']}") + bc_template, mode = entry + bc_name = bc_template.format(keyLen=group.get("keyLen", "")) + + encrypt = group["direction"] == "encrypt" + # TDES vectors split key into key1/key2/key3 + if "key1" in test: + key = ( + _from_hex(test["key1"]) + _from_hex(test["key2"]) + _from_hex(test["key3"]) + ) + else: + key = _from_hex(test["key"]) + + _block_mode_aft(bc_name, mode, key, test, exp, encrypt, group) + + +# ---- AES-XTS ---- + + +@register("ACVP-AES-XTS-1.0", "ACVP-AES-XTS-2.0") +def handle_aes_xts(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + tweak_mode = group.get("tweakMode", "hex") + + # XTS 1.0 puts payloadLen on the group; XTS 2.0 puts dataUnitLen/payloadLen on each test. + data_unit = test.get("dataUnitLen") or group.get("payloadLen") + payload_len = test.get("payloadLen") or group.get("payloadLen") or data_unit + if data_unit is not None and data_unit % 8 != 0: + raise TestSkip("Bit-oriented input not supported") + if payload_len is not None and payload_len % 8 != 0: + raise TestSkip("Bit-oriented input not supported") + if data_unit is not None and payload_len != data_unit: + raise TestSkip("XTS multi-data-unit not supported") + + algo = f"AES-{group['keyLen']}/XTS" + _resolve_cipher_or_skip(algo, group) + + encrypt = group["direction"] == "encrypt" + key = _from_hex(test["key"]) + if tweak_mode == "number": + # IEEE 1619 data unit sequence number: encode as 16-byte little-endian. + tweak = test["sequenceNumber"].to_bytes(16, "little") + else: + tweak = _from_hex(test["tweakValue"]) + + cipher = botan.SymmetricCipher(algo, encrypt) + cipher.set_key(key) + cipher.start(tweak) + + if encrypt: + pt = _opt_hex(test, "pt") + got = cipher.finish(pt).hex() + if got != exp["ct"].lower(): + raise TestFailure( + { + "Algo": algo, + "Key": test["key"], + "Tweak": test.get( + "tweakValue", str(test.get("sequenceNumber", "")) + ), + "PT": test.get("pt", ""), + "CT": exp["ct"], + "ComputedCT": got, + } + ) + else: + ct = _opt_hex(test, "ct") + got = cipher.finish(ct).hex() + if got != exp["pt"].lower(): + raise TestFailure( + { + "Algo": algo, + "Key": test["key"], + "Tweak": test.get( + "tweakValue", str(test.get("sequenceNumber", "")) + ), + "CT": test.get("ct", ""), + "PT": exp["pt"], + "ComputedPT": got, + } + ) + + +# ---- AES key wrap ---- + + +@register("ACVP-AES-KW-1.0", "ACVP-AES-KWP-1.0") +def handle_aes_key_wrap(header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + if group.get("kwCipher", "cipher") == "inverse": + raise TestSkip("SP800-38F inverse cipher key wrap (TKW-I) not implemented") + + is_kwp = "KWP" in header["algorithm"] + wrap_fn = botan.nist_key_wrap_padded if is_kwp else botan.nist_key_wrap + unwrap_fn = botan.nist_key_unwrap_padded if is_kwp else botan.nist_key_unwrap + name = header["algorithm"] + + direction = group["direction"] + key = _from_hex(test["key"]) + + if direction == "encrypt": + pt = _from_hex(test["pt"]) + got = wrap_fn(key, pt).hex() + if got != exp["ct"].lower(): + raise TestFailure( + { + "Algo": name, + "Key": test["key"], + "PT": test["pt"], + "CT": exp["ct"], + "ComputedCT": got, + } + ) + return + + ct = _from_hex(test["ct"]) + should_pass = exp.get("testPassed", True) + try: + pt = unwrap_fn(key, ct) + except botan.BotanException as e: + if should_pass: + raise TestFailure( + { + "Algo": name, + "Key": test["key"], + "CT": test["ct"], + "Note": "Valid unwrap failed", + } + ) from e + return + + if not should_pass: + raise TestFailure({"Algo": name, "Note": "Invalid test unwrapped successfully"}) + if pt.hex() != exp.get("pt", "").lower(): + raise TestFailure( + {"Algo": name, "PT": exp.get("pt", ""), "ComputedPT": pt.hex()} + ) + + +# ---- HMAC ---- + + +@register( + "HMAC-SHA-1-1.0", + "HMAC-SHA-1-2.0", + "HMAC-SHA2-224-1.0", + "HMAC-SHA2-224-2.0", + "HMAC-SHA2-256-1.0", + "HMAC-SHA2-256-2.0", + "HMAC-SHA2-384-1.0", + "HMAC-SHA2-384-2.0", + "HMAC-SHA2-512-1.0", + "HMAC-SHA2-512-2.0", + "HMAC-SHA2-512-224-1.0", + "HMAC-SHA2-512-224-2.0", + "HMAC-SHA2-512-256-1.0", + "HMAC-SHA2-512-256-2.0", + "HMAC-SHA3-224-1.0", + "HMAC-SHA3-224-2.0", + "HMAC-SHA3-256-1.0", + "HMAC-SHA3-256-2.0", + "HMAC-SHA3-384-1.0", + "HMAC-SHA3-384-2.0", + "HMAC-SHA3-512-1.0", + "HMAC-SHA3-512-2.0", +) +def handle_hmac(header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + algo = _HMAC_ALGO_MAP.get(header["algorithm"]) + if algo is None: + raise TestSkip(f"Unsupported HMAC: {header['algorithm']}") + + available = _group_state(group, "mac_ok") + if available is False: + raise TestSkip(f"Not available: {algo}") + if available is None: + try: + botan.MsgAuthCode(algo) + _set_group_state(group, "mac_ok", True) + except botan.BotanException as e: + _set_group_state(group, "mac_ok", False) + raise TestSkip(f"Not available: {algo}") from e + + key = _from_hex(test["key"]) + msg = _opt_hex(test, "msg") + mac_len_bits = test.get("macLen", group.get("macLen")) + if mac_len_bits is None: + raise TestSkip("No macLen in test or group") + mac_len = mac_len_bits // 8 + + mac = botan.MsgAuthCode(algo) + mac.set_key(key) + mac.update(msg) + got = mac.final()[:mac_len].hex() + if got != exp["mac"].lower(): + raise TestFailure( + { + "Algo": algo, + "Key": test["key"], + "Msg": test.get("msg", ""), + "Tag": exp["mac"], + "ComputedTag": got, + } + ) + + +# ---- CMAC ---- + + +@register("CMAC-AES-1.0", "CMAC-TDES-1.0") +def handle_cmac(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + direction = group.get("direction", "gen") + key = _from_hex(test["key"]) + msg = _opt_hex(test, "message") + + if "key1" in test: + algo = "CMAC(3DES)" + else: + algo = f"CMAC(AES-{len(key) * 8})" + + mac_len = test.get("macLen", group.get("macLen", 128)) // 8 + + mac = botan.MsgAuthCode(algo) + mac.set_key(key) + mac.update(msg) + computed = mac.final()[:mac_len] + + if direction == "gen": + if computed.hex() != exp["mac"].lower(): + raise TestFailure( + { + "Algo": algo, + "Key": test["key"], + "Msg": test.get("message", ""), + "Tag": exp["mac"], + "ComputedTag": computed.hex(), + } + ) + return + + expected_mac = _from_hex(test["mac"])[:mac_len] + should_pass = exp.get("testPassed", True) + if should_pass and computed != expected_mac: + raise TestFailure( + { + "Algo": algo, + "Key": test["key"], + "Note": "Valid CMAC did not verify", + "Tag": test["mac"], + "ComputedTag": computed.hex(), + } + ) + if not should_pass and computed == expected_mac: + raise TestFailure({"Algo": algo, "Note": "Invalid CMAC test matched"}) + + +# ---- KMAC ---- + + +@register("KMAC-128-1.0", "KMAC-256-1.0") +def handle_kmac(header: dict, group: dict, test: dict, exp: dict) -> None: + algo_name = header["algorithm"] + + if algo_name not in ["KMAC-128", "KMAC-256"]: + raise TestSkip(f"Unsupported: {algo_name}") + + test_type = group.get("testType", "AFT") + if test_type not in ("AFT", "MVT"): + raise TestSkip(f"testType {test_type} not supported") + + if group.get("xof"): + # KMAC XOF mode uses right_encode(0) instead of right_encode(L) + # in its domain separation. Botan's KMAC-128/256 always use the + # non-XOF variant so we can't match the expected output. + raise TestSkip("KMAC XOF mode not exposed via Python bindings") + if group.get("hexCustomization"): + raise TestSkip("KMAC hex customization not exposed via Python bindings") + + if test["macLen"] % 8 != 0: + raise TestSkip("Bit-oriented input not supported") + if test.get("msgLen", 0) % 8 != 0: + raise TestSkip("Bit-oriented input not supported") + if test.get("keyLen", 0) % 8 != 0: + raise TestSkip("Bit-oriented input not supported") + + key = _from_hex(test["key"]) + msg = _opt_hex(test, "msg") + mac_len = test["macLen"] // 8 + customization = test.get("customization", "") + + algo = f"{algo_name}({mac_len * 8})" + mac = botan.MsgAuthCode(algo) + try: + mac.set_key(key) + except botan.BotanException as e: + if "Invalid key length" in str(e): + raise TestSkip(f"Botan KMAC rejects key of length {len(key)}") from e + raise + if customization: + mac.set_nonce(customization.encode("utf-8")) + mac.update(msg) + computed = mac.final() + + if test_type == "AFT": + if computed.hex() != exp["mac"].lower(): + raise TestFailure( + { + "Algo": algo, + "Key": test["key"], + "Msg": test.get("msg", ""), + "Tag": exp["mac"], + "ComputedTag": computed.hex(), + } + ) + return + + expected_mac = _from_hex(test["mac"]) + should_pass = exp.get("testPassed", True) + if should_pass and computed != expected_mac: + raise TestFailure( + { + "Algo": algo, + "Note": "Valid KMAC did not verify", + "Tag": test["mac"], + "ComputedTag": computed.hex(), + } + ) + if not should_pass and computed == expected_mac: + raise TestFailure({"Algo": algo, "Note": "Invalid KMAC matched"}) + + +# ---- Finite-field DSA sigVer / sigGen ---- + + +_DSA_GROUP_MAP = { + (2048, 256): "dsa/botan/2048", + (3072, 256): "dsa/botan/3072", +} + + +@register("DSA-SigVer-1.0") +def handle_dsa_sigver(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + if group.get("conformance") == "SP800-106": + raise TestSkip("SP800-106 randomized hashing not supported") + + hash_algo = _map_hash(group["hashAlg"]) + + p = botan.MPI("0x" + group["p"]) + q = botan.MPI("0x" + group["q"]) + g = botan.MPI("0x" + group["g"]) + + expected_valid = exp.get("testPassed", True) + + try: + y = botan.MPI("0x" + test["y"]) + pub = botan.PublicKey.load_dsa(p, q, g, y) + except botan.BotanException as e: + if expected_valid: + raise TestFailure( + { + "L": group["l"], + "N": group["n"], + "Note": "DSA public key load failed on a valid test", + } + ) from e + return + + # Pad r and s to N/8 bytes; test vectors may omit leading zeros. + n_bytes = group["n"] // 8 + r = _from_hex(test["r"]).rjust(n_bytes, b"\x00") + s = _from_hex(test["s"]).rjust(n_bytes, b"\x00") + sig = r + s + msg = _from_hex(test["message"]) + + try: + verifier = botan.PKVerify(pub, hash_algo, der=False) + verifier.update(msg) + valid = verifier.check_signature(sig) + except botan.BotanException: + valid = False + + if valid != expected_valid: + raise TestFailure( + { + "L": group["l"], + "N": group["n"], + "Hash": group["hashAlg"], + "R": test["r"], + "S": test["s"], + "Expected": "valid" if expected_valid else "invalid", + "Got": "valid" if valid else "invalid", + } + ) + + +@register("DSA-SigGen-1.0") +def handle_dsa_siggen(_header: dict, group: dict, test: dict, _exp: dict) -> None: + _require_aft(group) + + group_l = group["l"] + group_n = group["n"] + group_name = _DSA_GROUP_MAP.get((group_l, group_n)) + if group_name is None: + raise TestSkip(f"No named DSA group for L={group_l}, N={group_n}") + + hash_algo = _map_hash(group["hashAlg"]) + + priv = _group_state(group, "priv") + if priv is None: + rng = botan.RandomNumberGenerator("system") + priv = botan.PrivateKey.create("DSA", group_name, rng) + _set_group_state(group, "priv", priv) + + msg = _from_hex(test["message"]) + rng = botan.RandomNumberGenerator("system") + + signer = botan.PKSign(priv, hash_algo, der=False) + signer.update(msg) + sig = signer.finish(rng) + + pub = priv.get_public_key() + verifier = botan.PKVerify(pub, hash_algo, der=False) + verifier.update(msg) + if not verifier.check_signature(sig): + raise TestFailure( + { + "L": group_l, + "N": group_n, + "Hash": group["hashAlg"], + "Sig": sig.hex(), + "Note": "Self-produced DSA signature failed to verify", + } + ) + + +# ---- ECDSA sig ver ---- + + +@register("ECDSA-SigVer-FIPS186-5", "ECDSA-SigVer-1.0") +def handle_ecdsa_sigver(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + if group.get("conformance") == "SP800-106": + raise TestSkip("SP800-106 randomized hashing not supported") + + curve = _CURVE_MAP.get(group["curve"]) + if curve is None: + raise TestSkip(f"Unsupported curve: {group['curve']}") + if not botan.ECGroup.supports_named_group(curve): + raise TestSkip(f"Curve {curve} not available in this build") + + hash_algo = _map_hash(group["hashAlg"]) + + expected_valid = exp.get("testPassed", True) + + try: + qx = botan.MPI("0x" + test["qx"]) + qy = botan.MPI("0x" + test["qy"]) + pub = botan.PublicKey.load_ecdsa(curve, qx, qy) + except botan.BotanException as e: + if expected_valid: + raise TestFailure( + { + "Curve": curve, + "Qx": test["qx"], + "Qy": test["qy"], + "Note": "Public key load failed on a valid test", + } + ) from e + return + + sig = _from_hex(test["r"]) + _from_hex(test["s"]) + msg = _from_hex(test["message"]) + + try: + verifier = botan.PKVerify(pub, hash_algo, der=False) + verifier.update(msg) + valid = verifier.check_signature(sig) + except botan.BotanException: + valid = False + + if valid != expected_valid: + raise TestFailure( + { + "Curve": curve, + "Hash": group["hashAlg"], + "R": test["r"], + "S": test["s"], + "Expected": "valid" if expected_valid else "invalid", + "Got": "valid" if valid else "invalid", + } + ) + + +# ---- ECDSA keyGen / keyVer / sigGen ---- + + +def _ecdsa_resolve_curve(group: dict) -> str: + curve = _CURVE_MAP.get(group["curve"]) + if curve is None: + raise TestSkip(f"Unsupported curve: {group['curve']}") + if not botan.ECGroup.supports_named_group(curve): + raise TestSkip(f"Curve {curve} not available in this build") + return curve + + +def _ecdsa_siggen_aft(group: dict, test: dict, *, deterministic: bool) -> None: + _require_aft(group) + + curve = _ecdsa_resolve_curve(group) + component = group.get("componentTest", False) + + # Component tests provide a pre-hashed message; use "Raw" padding. + # Normal tests provide the raw message; use the named hash. + if component: + padding = "Raw" + else: + padding = _map_hash(group["hashAlg"]) + + priv = _group_state(group, "priv") + if priv is None: + rng = botan.RandomNumberGenerator("system") + priv = botan.PrivateKey.create("ECDSA", curve, rng) + _set_group_state(group, "priv", priv) + + msg = _from_hex(test["message"]) + rng = botan.RandomNumberGenerator("system") + + signer = botan.PKSign(priv, padding, der=False) + signer.update(msg) + sig = signer.finish(rng) + + if deterministic: + signer2 = botan.PKSign(priv, padding, der=False) + signer2.update(msg) + sig2 = signer2.finish(rng) + if sig != sig2: + raise TestFailure( + { + "Curve": curve, + "Hash": group.get("hashAlg", "Raw"), + "Sig1": sig.hex(), + "Sig2": sig2.hex(), + "Note": "ECDSA is not deterministic under the same key/message", + } + ) + + pub = priv.get_public_key() + verifier = botan.PKVerify(pub, padding, der=False) + verifier.update(msg) + if not verifier.check_signature(sig): + raise TestFailure( + { + "Curve": curve, + "Hash": group.get("hashAlg", "Raw"), + "Sig": sig.hex(), + "Note": "Self-produced ECDSA signature failed to verify", + } + ) + + +@register("DetECDSA-SigGen-FIPS186-5") +def handle_detecdsa_siggen(_header: dict, group: dict, test: dict, _exp: dict) -> None: + _ecdsa_siggen_aft(group, test, deterministic=True) + + +@register("ECDSA-SigGen-FIPS186-5", "ECDSA-SigGen-1.0") +def handle_ecdsa_siggen(_header: dict, group: dict, test: dict, _exp: dict) -> None: + _ecdsa_siggen_aft(group, test, deterministic=False) + + +@register("ECDSA-KeyGen-FIPS186-5", "ECDSA-KeyGen-1.0") +def handle_ecdsa_keygen(_header: dict, group: dict, _test: dict, _exp: dict) -> None: + _require_aft(group) + curve = _ecdsa_resolve_curve(group) + + rng = botan.RandomNumberGenerator("system") + priv = botan.PrivateKey.create("ECDSA", curve, rng) + pub = priv.get_public_key() + + # Smoke check the generated key: sign a short message and verify. + signer = botan.PKSign(priv, "SHA-256", der=False) + signer.update(b"acvp keygen self-test") + sig = signer.finish(rng) + verifier = botan.PKVerify(pub, "SHA-256", der=False) + verifier.update(b"acvp keygen self-test") + if not verifier.check_signature(sig): + raise TestFailure( + {"Curve": curve, "Note": "Fresh ECDSA key sign/verify round-trip failed"} + ) + + +@register("ECDSA-KeyVer-FIPS186-5", "ECDSA-KeyVer-1.0") +def handle_ecdsa_keyver(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + curve = _ecdsa_resolve_curve(group) + + expected_valid = exp.get("testPassed", True) + try: + qx = botan.MPI("0x" + test["qx"]) + qy = botan.MPI("0x" + test["qy"]) + botan.PublicKey.load_ecdsa(curve, qx, qy) + valid = True + except botan.BotanException: + valid = False + + if valid != expected_valid: + raise TestFailure( + { + "Curve": curve, + "Qx": test["qx"], + "Qy": test["qy"], + "Expected": "valid" if expected_valid else "invalid", + "Got": "valid" if valid else "invalid", + } + ) + + +# ---- EdDSA keyGen / keyVer / sigGen ---- + +# Same IUT-generates-its-own-key pattern as ECDSA sigGen. ACVP supplies raw +# Ed25519/Ed448 public keys; wrap them in a SubjectPublicKeyInfo before +# handing to Botan's generic pubkey loader. +_ED25519_SPKI_PREFIX = bytes( + [ + 0x30, + 0x2A, + 0x30, + 0x05, + 0x06, + 0x03, + 0x2B, + 0x65, + 0x70, # OID 1.3.101.112 + 0x03, + 0x21, + 0x00, + ] +) +_ED448_SPKI_PREFIX = bytes( + [ + 0x30, + 0x43, + 0x30, + 0x05, + 0x06, + 0x03, + 0x2B, + 0x65, + 0x71, # OID 1.3.101.113 + 0x03, + 0x3A, + 0x00, + ] +) + +_EDDSA_CURVE_MAP = { + "ED-25519": ("Ed25519", _ED25519_SPKI_PREFIX), + "ED-448": ("Ed448", _ED448_SPKI_PREFIX), +} + + +def _eddsa_resolve(group: dict) -> tuple[str, bytes]: + info = _EDDSA_CURVE_MAP.get(group["curve"]) + if info is None: + raise TestSkip(f"Unsupported curve: {group['curve']}") + return info + + +@register("EDDSA-KeyGen-1.0") +def handle_eddsa_keygen(_header: dict, group: dict, _test: dict, _exp: dict) -> None: + _require_aft(group) + algo, _ = _eddsa_resolve(group) + + rng = botan.RandomNumberGenerator("system") + priv = botan.PrivateKey.create(algo, "", rng) + pub = priv.get_public_key() + + signer = botan.PKSign(priv, "") + signer.update(b"acvp eddsa keygen self-test") + sig = signer.finish(rng) + verifier = botan.PKVerify(pub, "") + verifier.update(b"acvp eddsa keygen self-test") + if not verifier.check_signature(sig): + raise TestFailure({"Algo": algo, "Note": "EdDSA key round-trip failed"}) + + +@register("EDDSA-KeyVer-1.0") +def handle_eddsa_keyver(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + _, spki_prefix = _eddsa_resolve(group) + + expected_valid = exp.get("testPassed", True) + if isinstance(expected_valid, str): + expected_valid = expected_valid.lower() == "true" + + try: + botan.PublicKey.load(spki_prefix + _from_hex(test["q"])) + valid = True + except botan.BotanException: + valid = False + + if valid and not expected_valid: + # Botan does not eagerly validate EdDSA point encodings on load; + # invalid keys are only rejected at verify time. + raise TestSkip("Botan does not eagerly reject invalid EdDSA keys") + + if valid != expected_valid: + raise TestFailure( + { + "Curve": group["curve"], + "Q": test["q"], + "Expected": "valid" if expected_valid else "invalid", + "Got": "valid" if valid else "invalid", + } + ) + + +@register("EDDSA-SigGen-1.0") +def handle_eddsa_siggen(_header: dict, group: dict, test: dict, _exp: dict) -> None: + _require_aft(group) + if group.get("preHash"): + raise TestSkip("EdDSA preHash not supported via Python bindings") + + algo, _ = _eddsa_resolve(group) + + priv = _group_state(group, "priv") + if priv is None: + rng = botan.RandomNumberGenerator("system") + priv = botan.PrivateKey.create(algo, "", rng) + _set_group_state(group, "priv", priv) + + msg = _from_hex(test["message"]) + rng = botan.RandomNumberGenerator("system") + + signer = botan.PKSign(priv, "") + signer.update(msg) + sig = signer.finish(rng) + + verifier = botan.PKVerify(priv.get_public_key(), "") + verifier.update(msg) + if not verifier.check_signature(sig): + raise TestFailure( + { + "Algo": algo, + "Sig": sig.hex(), + "Note": "Self-produced EdDSA signature failed to verify", + } + ) + + +# ---- RSA sigGen ---- + + +@register("RSA-SigGen-FIPS186-5", "RSA-SigGen-FIPS186-4") +def handle_rsa_siggen(_header: dict, group: dict, test: dict, _exp: dict) -> None: + # ACVP uses 'GDT' (generated data test) here. The IUT picks its own + # key (with the given modulo size), signs each message, and reports + # n/e/signatures. We check self-consistency (sign/verify round-trip). + test_type = group.get("testType", "GDT") + if test_type != "GDT": + raise TestSkip(f"testType {test_type} not supported") + + hash_algo = _map_hash(group["hashAlg"]) + mask_fn = group.get("maskFunction") + if mask_fn and mask_fn.startswith("shake"): + raise TestSkip(f"SHAKE-based MGF ({mask_fn}) not supported") + padding = _rsa_padding(group["sigType"], hash_algo, group.get("saltLen")) + + priv = _group_state(group, "priv") + if priv is None: + rng = botan.RandomNumberGenerator("system") + priv = botan.PrivateKey.create("RSA", str(group["modulo"]), rng) + _set_group_state(group, "priv", priv) + + msg = _from_hex(test["message"]) + rng = botan.RandomNumberGenerator("system") + + signer = botan.PKSign(priv, padding) + signer.update(msg) + sig = signer.finish(rng) + + verifier = botan.PKVerify(priv.get_public_key(), padding) + verifier.update(msg) + if not verifier.check_signature(sig): + raise TestFailure( + { + "Padding": padding, + "Modulo": str(group["modulo"]), + "Note": "Self-produced RSA signature failed to verify", + } + ) + + +# ---- RSA keyGen ---- + + +@register("RSA-KeyGen-FIPS186-5", "RSA-KeyGen-FIPS186-4") +def handle_rsa_keygen(_header: dict, group: dict, _test: dict, _exp: dict) -> None: + test_type = group.get("testType", "AFT") + if test_type not in ("GDT",): + # AFT/KAT require seed-based deterministic prime generation per + # FIPS 186-4 appendix B.3, which Botan's keygen does not expose. + raise TestSkip( + f"RSA KeyGen testType {test_type} requires seed-based generation" + ) + + modulo = group["modulo"] + rng = botan.RandomNumberGenerator("system") + priv = botan.PrivateKey.create("RSA", str(modulo), rng) + pub = priv.get_public_key() + + # Round-trip: sign and verify with the generated key. + signer = botan.PKSign(priv, "PKCS1v15(SHA-256)") + signer.update(b"acvp rsa keygen self-test") + sig = signer.finish(rng) + verifier = botan.PKVerify(pub, "PKCS1v15(SHA-256)") + verifier.update(b"acvp rsa keygen self-test") + if not verifier.check_signature(sig): + raise TestFailure( + { + "Modulo": str(modulo), + "Note": "Fresh RSA key sign/verify round-trip failed", + } + ) + + +# ---- RSA primitives (raw modexp) ---- + + +def _rsa_pad_to_modulus(data: bytes, n_hex: str) -> bytes: + mod_bytes = (len(n_hex) + 1) // 2 + if len(data) < mod_bytes: + return bytes(mod_bytes - len(data)) + data + return data + + +@register("RSA-SignaturePrimitive-2.0") +def handle_rsa_sig_primitive(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + expected_pass = exp.get("testPassed", True) + + try: + p = botan.MPI("0x" + test["p"]) + q = botan.MPI("0x" + test["q"]) + e = botan.MPI("0x" + test["e"]) + priv = botan.PrivateKey.load_rsa(p, q, e) + except botan.BotanException: + if not expected_pass: + return + raise + + msg_int = int(test["message"], 16) if test["message"] else 0 + n_int = int(test["n"], 16) + + # The signature primitive requires 0 < message < n. + if msg_int < 1 or msg_int >= n_int: + if expected_pass: + raise TestFailure( + {"Note": f"message out of range [1, n) but testPassed={expected_pass}"} + ) + return + + msg = _rsa_pad_to_modulus(_from_hex(test["message"]), test["n"]) + + try: + signer = botan.PKSign(priv, "Raw") + signer.update(msg) + rng = botan.RandomNumberGenerator("system") + sig = signer.finish(rng) + except botan.BotanException: + if not expected_pass: + return + raise + + if not expected_pass: + raise TestFailure( + {"Note": "Expected failure but RSA signature primitive succeeded"} + ) + + if sig.hex() != exp["signature"].lower(): + raise TestFailure({"Sig": exp["signature"], "ComputedSig": sig.hex()}) + + +@register("RSA-DecryptionPrimitive-Sp800-56Br2") +def handle_rsa_dec_primitive(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + expected_pass = exp.get("testPassed", True) + + try: + p = botan.MPI("0x" + test["p"]) + q = botan.MPI("0x" + test["q"]) + e = botan.MPI("0x" + test["e"]) + priv = botan.PrivateKey.load_rsa(p, q, e) + except botan.BotanException: + if not expected_pass: + return + raise + + # SP800-56Br2 §7.1.2 restricts the ciphertext to [2, n-2]. Botan's RSA + # implementation rejects ct == 0 and ct >= n, but values 1 and n-1 are + # only invalid under the SP800-56Br2 key transport primitive, not for + # general RSA decryption (OAEP, Raw). Skip those here. + ct_int = int(test["ct"], 16) if test["ct"] else 0 + n_int = int(test["n"], 16) + if ct_int in (1, n_int - 1): + if expected_pass: + raise TestFailure( + {"Note": f"ct == 1 or ct == n-1 but testPassed={expected_pass}"} + ) + return + + ct = _rsa_pad_to_modulus(_from_hex(test["ct"]), test["n"]) + + try: + dec = botan.PKDecrypt(priv, "Raw") + pt = dec.decrypt(ct) + except botan.BotanException: + if not expected_pass: + return + raise + + if not expected_pass: + raise TestFailure( + {"Note": "Expected failure but RSA decryption primitive succeeded"} + ) + + # Pad to modulus byte length (raw decryption may strip leading zeros). + mod_bytes = (len(test["n"]) + 1) // 2 + pt = bytes(mod_bytes - len(pt)) + pt if len(pt) < mod_bytes else pt + + if pt.hex() != exp["pt"].lower(): + raise TestFailure({"PT": exp["pt"], "ComputedPT": pt.hex()}) + + +# ---- AES-GMAC ---- + + +@register("ACVP-AES-GMAC-1.0") +def handle_aes_gmac(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + if group.get("ivGen", "external") != "external": + raise TestSkip(f"GMAC ivGen {group.get('ivGen')} not supported") + + algo = f"GMAC(AES-{group['keyLen']})" + key = _from_hex(test["key"]) + iv = _from_hex(test["iv"]) + aad = _opt_hex(test, "aad") + tag_bytes = group["tagLen"] // 8 + + mac = botan.MsgAuthCode(algo) + mac.set_key(key) + mac.set_nonce(iv) + mac.update(aad) + computed = mac.final()[:tag_bytes] + + if group["direction"] == "encrypt": + if computed.hex() != exp["tag"].lower(): + raise TestFailure( + { + "Algo": algo, + "Key": test["key"], + "IV": test["iv"], + "AAD": test.get("aad", ""), + "Tag": exp["tag"], + "ComputedTag": computed.hex(), + } + ) + return + + expected_mac = _from_hex(test["tag"])[:tag_bytes] + should_pass = exp.get("testPassed", True) + if should_pass and computed != expected_mac: + raise TestFailure( + { + "Algo": algo, + "Tag": test["tag"], + "ComputedTag": computed.hex(), + "Note": "Valid GMAC did not match", + } + ) + if not should_pass and computed == expected_mac: + raise TestFailure({"Algo": algo, "Note": "Invalid GMAC matched"}) + + +# ---- PBKDF2 ---- + + +@register("PBKDF-1.0") +def handle_pbkdf(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + algo = _PBKDF_HMAC_MAP.get(group["hmacAlg"]) + if algo is None: + raise TestSkip(f"Unsupported PBKDF hmacAlg: {group['hmacAlg']}") + + password = test["password"].encode("utf-8") + salt = _from_hex(test["salt"]) + iters = test["iterationCount"] + dk_len = test["keyLen"] // 8 + + _, _, dk = botan.pbkdf( + algo, password.decode("utf-8"), dk_len, iterations=iters, salt=salt + ) + if dk.hex() != exp["derivedKey"].lower(): + raise TestFailure( + { + "Algo": algo, + "Iters": str(iters), + "DkLen": str(dk_len), + "Expected": exp["derivedKey"], + "Got": dk.hex(), + } + ) + + +# ---- HKDF (SP800-56C) ---- + + +def _kda_party_info(party: dict) -> bytes: + # uPartyInfo / vPartyInfo: concatenation of the party's fields in the + # order they appear (partyId, ephemeralData). + out = _from_hex(party["partyId"]) + if "ephemeralData" in party and party["ephemeralData"]: + out += _from_hex(party["ephemeralData"]) + return out + + +def _kda_fixed_info(pattern: str, party_u: dict, party_v: dict, l_bits: int) -> bytes: + if pattern != "uPartyInfo||vPartyInfo||l": + raise TestSkip(f"fixedInfoPattern {pattern!r} not supported") + return ( + _kda_party_info(party_u) + _kda_party_info(party_v) + l_bits.to_bytes(4, "big") + ) + + +@register("HKDF-1.0") +def handle_hkdf_standalone(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + hash_name = _KDF_HASH_MAP.get(group["hmacAlg"]) + if hash_name is None: + raise TestSkip(f"Hash {group['hmacAlg']} not supported") + + algo = f"HKDF({hash_name})" + ikm = _from_hex(test["inputKeyingMaterial"]) + salt = _opt_hex(test, "salt") + info = _opt_hex(test, "otherInfo") + out_len = test["keyLength"] + + out = botan.kdf(algo, ikm, out_len, salt, info) + if out.hex() != exp["derivedKey"].lower(): + raise TestFailure( + {"Algo": algo, "DerivedKey": exp["derivedKey"], "ComputedKey": out.hex()} + ) + + +# ---- KDA shared helper (HKDF and OneStep) ---- + + +_KDA_ONESTEP_AUX_MAP = { + "SHA-1": "SP800-56A(SHA-1)", + "SHA2-224": "SP800-56A(SHA-224)", + "SHA2-256": "SP800-56A(SHA-256)", + "SHA2-384": "SP800-56A(SHA-384)", + "SHA2-512": "SP800-56A(SHA-512)", + "SHA3-224": "SP800-56A(SHA-3(224))", + "SHA3-256": "SP800-56A(SHA-3(256))", + "SHA3-384": "SP800-56A(SHA-3(384))", + "SHA3-512": "SP800-56A(SHA-3(512))", + "KMAC-128": "SP800-56A(KMAC-128)", + "KMAC-256": "SP800-56A(KMAC-256)", +} +# Add HMAC variants derived from the canonical hash map. +for _k, _v in _HASH_MAP.items(): + if _v is not None and not _k.startswith("SHAKE"): + _KDA_ONESTEP_AUX_MAP[f"HMAC-{_k}"] = f"SP800-56A(HMAC({_v}))" + + +def _kda_aft(algo: str, salt: bytes, group: dict, test: dict, exp: dict) -> None: + """Shared helper for KDA-HKDF and KDA-OneStep AFT/VAL tests.""" + test_type = group.get("testType", "AFT") + + kc = group["kdfConfiguration"] + kp = test["kdfParameter"] + + z = _from_hex(kp["z"]) + if group.get("usesHybridSharedSecret") and kp.get("t"): + z += _from_hex(kp["t"]) + l_bits = int(kp["l"]) + out_len = l_bits // 8 + + fixed_info = _kda_fixed_info( + kc["fixedInfoPattern"], test["fixedInfoPartyU"], test["fixedInfoPartyV"], l_bits + ) + + dkm = botan.kdf(algo, z, out_len, salt, fixed_info) + + if test_type == "AFT": + if dkm.hex() != exp["dkm"].lower(): + raise TestFailure( + {"Algo": algo, "DKM": exp["dkm"], "ComputedDKM": dkm.hex()} + ) + else: + expected_dkm = _from_hex(test["dkm"]) + passed = dkm == expected_dkm + if passed != exp.get("testPassed", True): + raise TestFailure( + { + "Algo": algo, + "Expected": str(exp.get("testPassed")), + "Got": str(passed), + } + ) + + +def _kda_resolve(group: dict, test: dict) -> tuple[str, bytes]: + """Parse kdfConfiguration and return (botan_algo_name, salt).""" + test_type = group.get("testType", "AFT") + if test_type not in ("AFT", "VAL"): + raise TestSkip(f"testType {test_type} not supported") + + kc = group.get("kdfConfiguration") + if kc is None: + raise TestSkip("Missing kdfConfiguration (multi-expansion not supported)") + if kc.get("fixedInfoEncoding", "concatenation") != "concatenation": + raise TestSkip(f"fixedInfoEncoding {kc.get('fixedInfoEncoding')} not supported") + + kdf_type = kc["kdfType"] + kp = test["kdfParameter"] + + if kdf_type == "hkdf": + hash_name = _KDF_HASH_MAP.get(kc["hmacAlg"]) + if hash_name is None: + raise TestSkip(f"Hash {kc['hmacAlg']} not supported") + return f"HKDF({hash_name})", _from_hex(kp.get("salt", "")) + + if kdf_type == "oneStep": + aux = kc.get("auxFunction") + algo = _KDA_ONESTEP_AUX_MAP.get(aux) + if algo is None: + raise TestSkip(f"Unsupported auxFunction: {aux}") + uses_salt = aux.startswith("HMAC-") or aux.startswith("KMAC-") + salt = _from_hex(kp.get("salt", "")) if uses_salt else b"" + return algo, salt + + raise TestSkip(f"Unsupported kdfType: {kdf_type}") + + +@register( + "KDA-HKDF-Sp800-56Cr1", + "KDA-HKDF-Sp800-56Cr2", + "KDA-OneStep-Sp800-56Cr1", + "KDA-OneStep-Sp800-56Cr2", +) +def handle_kda(_header: dict, group: dict, test: dict, exp: dict) -> None: + algo, salt = _kda_resolve(group, test) + _kda_aft(algo, salt, group, test, exp) + + +# ---- TLS-v1.2-KDF-RFC7627 (Extended Master Secret) ---- + + +@register("TLS-v1.2-KDF-RFC7627") +def handle_tls12_kdf_ems(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + hash_name = _KDF_HASH_MAP.get(group["hashAlg"]) + if hash_name is None: + raise TestSkip(f"Hash {group['hashAlg']} not supported") + algo = f"TLS-12-PRF({hash_name})" + + pms = _from_hex(test["preMasterSecret"]) + session_hash = _from_hex(test["sessionHash"]) + cr = _from_hex(test["clientRandom"]) + sr = _from_hex(test["serverRandom"]) + kb_len = group["keyBlockLength"] // 8 + + ms = botan.kdf(algo, pms, 48, session_hash, b"extended master secret") + if ms.hex() != exp["masterSecret"].lower(): + raise TestFailure( + {"Algo": algo, "MS": exp["masterSecret"], "ComputedMS": ms.hex()} + ) + + kb = botan.kdf(algo, ms, kb_len, sr + cr, b"key expansion") + if kb.hex() != exp["keyBlock"].lower(): + raise TestFailure({"Algo": algo, "KB": exp["keyBlock"], "ComputedKB": kb.hex()}) + + +# ---- ANSI X9.63 KDF (SEC 1) ---- + + +@register("kdf-components-ansix9.63-1.0") +def handle_kdf_ansix963(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + hash_name = _KDF_HASH_MAP.get(group["hashAlg"]) + if hash_name is None: + raise TestSkip(f"Hash {group['hashAlg']} not supported") + # ANSI X9.63 KDF == IEEE KDF2. + algo = f"KDF2({hash_name})" + + z = _from_hex(test["z"]) + shared = _opt_hex(test, "sharedInfo") + out_len = group["keyDataLength"] // 8 + + kd = botan.kdf(algo, z, out_len, shared, b"") + if kd.hex() != exp["keyData"].lower(): + raise TestFailure( + {"Algo": algo, "KeyData": exp["keyData"], "ComputedKeyData": kd.hex()} + ) + + +# ---- TLS PRF (kdf-components-tls) ---- + + +_TLS12_PRF_HASH_MAP = { + "SHA2-256": "TLS-12-PRF(SHA-256)", + "SHA2-384": "TLS-12-PRF(SHA-384)", + "SHA2-512": "TLS-12-PRF(SHA-512)", +} + + +@register("kdf-components-tls-1.0") +def handle_kdf_components_tls( + _header: dict, group: dict, test: dict, exp: dict +) -> None: + _require_aft(group) + + version = group["tlsVersion"] + if version != "v1.2": + raise TestSkip(f"TLS {version} PRF not implemented anymore") + + algo = _TLS12_PRF_HASH_MAP.get(group["hashAlg"]) + if algo is None: + raise TestSkip(f"TLS 1.2 PRF with hash {group['hashAlg']} not supported") + + pms = _from_hex(test["preMasterSecret"]) + chr_ = _from_hex(test["clientHelloRandom"]) + shr = _from_hex(test["serverHelloRandom"]) + cr = _from_hex(test["clientRandom"]) + sr = _from_hex(test["serverRandom"]) + kb_len = group["keyBlockLength"] // 8 + + ms = botan.kdf(algo, pms, 48, chr_ + shr, b"master secret") + if ms.hex() != exp["masterSecret"].lower(): + raise TestFailure( + {"Algo": algo, "MS": exp["masterSecret"], "ComputedMS": ms.hex()} + ) + + kb = botan.kdf(algo, ms, kb_len, sr + cr, b"key expansion") + if kb.hex() != exp["keyBlock"].lower(): + raise TestFailure({"Algo": algo, "KB": exp["keyBlock"], "ComputedKB": kb.hex()}) + + +# ---- RSA sig ver FIPS 186-2 ---- + + +@register("RSA-SigVer-FIPS186-2") +def handle_rsa_sigver_fips186_2( + header: dict, group: dict, test: dict, exp: dict +) -> None: + # Same handler as RSA-SigVer-FIPS186-5 — reuses the same padding logic + # and the same X9.31 skip. + handle_rsa_sigver(header, group, test, exp) + + +# ---- LMS keyGen ---- + +_LMS_HASH_FROM_MODE = { + "LMS_SHA256_M32": "SHA-256", + "LMS_SHA256_M24": "Truncated(SHA-256,192)", + "LMS_SHAKE_M32": "SHAKE-256(256)", + "LMS_SHAKE_M24": "SHAKE-256(192)", +} + +_LMOTS_W_FROM_SUFFIX = {"W1": 1, "W2": 2, "W4": 4, "W8": 8} + + +def _lms_botan_params(lms_mode: str, lmots_mode: str) -> str: + # lms_mode like 'LMS_SHA256_M24_H5' -> prefix 'LMS_SHA256_M24' and h=5. + # lmots_mode like 'LMOTS_SHA256_N24_W1' -> w=1. + prefix, _, h_tag = lms_mode.rpartition("_") + if not h_tag.startswith("H"): + raise ValueError(f"Unexpected LMS mode: {lms_mode}") + h = int(h_tag[1:]) + w = _LMOTS_W_FROM_SUFFIX[lmots_mode.rsplit("_", 1)[-1]] + hash_name = _LMS_HASH_FROM_MODE[prefix] + return f"{hash_name},HW({h},{w})" + + +@register("LMS-keyGen-1.0") +def handle_lms_keygen(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + # LMS keygen computes 2^h OTS leaves, which is too slow for the normal + # run even at the lowest tree heights in these vectors. Gate the whole + # directory behind ACVP_RUN_SLOW_TESTS. + if os.environ.get("ACVP_RUN_SLOW_TESTS") != "1": + raise TestSkip("LMS keygen disabled (set ACVP_RUN_SLOW_TESTS=1)") + + params = _lms_botan_params(group["lmsMode"], group["lmOtsMode"]) + seed = _from_hex(test["seed"]) + ident = _from_hex(test["i"]) + + rng = FixedOutputRNG(seed + ident) + priv = botan.PrivateKey.create("HSS-LMS", params, rng) + pub = priv.get_public_key() + + # Botan emits HSS-LMS format with a 4-byte level-count prefix; ACVP + # expects the underlying single-level LMS public key. Strip the prefix. + raw = pub.to_raw() + if raw[:4] != b"\x00\x00\x00\x01": + raise TestFailure({"Mode": params, "Note": "Expected HSS L=1 prefix in pubkey"}) + computed = raw[4:].hex() + if computed != exp["publicKey"].lower(): + raise TestFailure( + { + "LmsMode": group["lmsMode"], + "LmOtsMode": group["lmOtsMode"], + "PublicKey": exp["publicKey"], + "ComputedPublicKey": computed, + } + ) + + +# ---- LMS sigVer ---- + +# OID for id-alg-hss-lms-hashsig (1.2.840.113549.1.9.16.3.17), as used in +# RFC 8708's SPKI encoding for HSS/LMS public keys. +_HSS_LMS_OID_DER = bytes( + [ + 0x06, + 0x0B, + 0x2A, + 0x86, + 0x48, + 0x86, + 0xF7, + 0x0D, + 0x01, + 0x09, + 0x10, + 0x03, + 0x11, + ] +) + + +def _der_seq(body: bytes) -> bytes: + # Only used for short bodies (< 128 bytes); all LMS SPKI fit in that. + if len(body) >= 0x80: + raise ValueError("DER helper only handles short lengths") + return b"\x30" + bytes([len(body)]) + body + + +def _wrap_lms_spki(lms_pub: bytes) -> bytes: + """Wrap a raw LMS public key as an HSS-LMS SubjectPublicKeyInfo. + + ACVP's LMS vectors contain a standalone LMS public key; Botan loads + HSS-LMS, so we prepend the HSS L=1 level count and emit SPKI DER. + """ + hss_body = b"\x00\x00\x00\x01" + lms_pub + bit_string = b"\x03" + bytes([len(hss_body) + 1]) + b"\x00" + hss_body + alg_id = _der_seq(_HSS_LMS_OID_DER) + return _der_seq(alg_id + bit_string) + + +@register("LMS-sigVer-1.0") +def handle_lms_sigver(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + pub = _group_state(group, "pub") + if pub is None: + try: + pub = botan.PublicKey.load(_wrap_lms_spki(_from_hex(group["publicKey"]))) + except botan.BotanException as e: + raise TestSkip(f"HSS-LMS pubkey load failed: {e}") from e + _set_group_state(group, "pub", pub) + + msg = _from_hex(test["message"]) + # ACVP LMS signatures are plain LMS signatures; Botan's HSS-LMS verify + # wants an HSS wrapper with Nspk=0 (no signed keys below the root). + sig = b"\x00\x00\x00\x00" + _from_hex(test["signature"]) + + expected_valid = exp.get("testPassed", True) + try: + verifier = botan.PKVerify(pub, "") + verifier.update(msg) + valid = verifier.check_signature(sig) + except botan.BotanException: + valid = False + + if valid != expected_valid: + raise TestFailure( + { + "LmsMode": group.get("lmsMode"), + "LmOtsMode": group.get("lmOtsMode"), + "Msg": test["message"][:80] + "...", + "Expected": "valid" if expected_valid else "invalid", + "Got": "valid" if valid else "invalid", + } + ) + + +@register("EDDSA-SigVer-1.0") +def handle_eddsa_sigver(_header: dict, group: dict, test: dict, exp: dict) -> None: + test_type = group.get("testType", "AFT") + if test_type not in ("AFT", "BFT"): + raise TestSkip(f"testType {test_type} not supported") + + if group.get("preHash", False): + raise TestSkip("EdDSA preHash not supported via Python bindings") + + curve = group["curve"] + if curve == "ED-25519": + spki_prefix = _ED25519_SPKI_PREFIX + elif curve == "ED-448": + spki_prefix = _ED448_SPKI_PREFIX + else: + raise TestSkip(f"Unsupported curve: {curve}") + + expected_valid = exp.get("testPassed", True) + + try: + pub = botan.PublicKey.load(spki_prefix + _from_hex(test["q"])) + except botan.BotanException as e: + if expected_valid: + raise TestFailure( + {"Curve": curve, "Q": test["q"], "Note": "Pubkey load failed"} + ) from e + return + + msg = _from_hex(test["message"]) + sig = _from_hex(test["signature"]) + + try: + verifier = botan.PKVerify(pub, "") + verifier.update(msg) + valid = verifier.check_signature(sig) + except botan.BotanException: + valid = False + + if valid != expected_valid: + raise TestFailure( + { + "Curve": curve, + "Msg": test["message"], + "Sig": test["signature"], + "Expected": "valid" if expected_valid else "invalid", + "Got": "valid" if valid else "invalid", + } + ) + + +# ---- RSA sig ver ---- + + +def _rsa_padding(sig_type: str, hash_algo: str, salt_len: int | None = None) -> str: + if sig_type == "pkcs1v1.5": + return f"PKCS1v15({hash_algo})" + if sig_type == "pss": + if salt_len is not None: + return f"PSS({hash_algo},MGF1,{salt_len})" + return f"PSS({hash_algo})" + if sig_type == "ansx9.31": + # X9.31 allows signatures in both forms: s and (n - s). The ACVP + # vectors include signatures in the (n - s) form, which Botan's + # verification does not handle (it only checks s^e mod n, not + # (n - s)^e mod n). + raise TestSkip("X9.31 verification of (n - s) form not supported") + raise TestSkip(f"Unsupported RSA sig type: {sig_type}") + + +@register("RSA-SigVer-FIPS186-5", "RSA-SigVer-FIPS186-4") +def handle_rsa_sigver(_header: dict, group: dict, test: dict, exp: dict) -> None: + hash_algo = _map_hash(group["hashAlg"]) + + mask_fn = group.get("maskFunction") + if mask_fn and mask_fn.startswith("shake"): + raise TestSkip(f"SHAKE-based MGF ({mask_fn}) not supported") + + padding = _rsa_padding(group["sigType"], hash_algo, group.get("saltLen")) + + pub = _group_state(group, "rsa_pub") + if pub is None: + try: + n = botan.MPI("0x" + group["n"]) + e = botan.MPI("0x" + group["e"]) + pub = botan.PublicKey.load_rsa(n, e) + except botan.BotanException as e: + _set_group_state(group, "rsa_pub_failed", True) + raise TestFailure({"Note": "RSA pubkey load failed"}) from e + _set_group_state(group, "rsa_pub", pub) + + msg = _from_hex(test["message"]) + sig = _from_hex(test["signature"]) + expected_valid = exp.get("testPassed", True) + + try: + verifier = botan.PKVerify(pub, padding) + verifier.update(msg) + valid = verifier.check_signature(sig) + except botan.BotanException: + valid = False + + if valid != expected_valid: + raise TestFailure( + { + "Padding": padding, + "Msg": test["message"], + "Sig": test["signature"], + "Expected": "valid" if expected_valid else "invalid", + "Got": "valid" if valid else "invalid", + } + ) + + +# ---- ML-KEM ---- + + +@register("ML-KEM-keyGen-FIPS203") +def handle_mlkem_keygen(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + param_set = group["parameterSet"] + d = _from_hex(test["d"]) + z = _from_hex(test["z"]) + + priv = botan.PrivateKey.load_ml_kem(param_set, d + z) + pub = priv.get_public_key() + if pub.to_raw().hex() != exp["ek"].lower(): + raise TestFailure( + {"Mode": param_set, "EK": exp["ek"], "ComputedEK": pub.to_raw().hex()} + ) + # dk comparison skipped: priv.to_raw() returns seed form; ACVP + # expects the expanded dk. + + +@register("ML-KEM-encapDecap-FIPS203") +def handle_mlkem_encapdecap(_header: dict, group: dict, test: dict, exp: dict) -> None: + test_type = group.get("testType", "AFT") + param_set = group["parameterSet"] + function = group["function"] + + if function == "encapsulation" and test_type == "AFT": + ek = _from_hex(test["ek"]) + m = _from_hex(test["m"]) + pub = botan.PublicKey.load_ml_kem(param_set, ek) + expected_k = _from_hex(exp["k"]) + rng = FixedOutputRNG(m) + kem_e = botan.KemEncrypt(pub, "Raw") + k, c = kem_e.create_shared_key(rng, b"", len(expected_k)) + if c.hex() != exp["c"].lower() or k.hex() != exp["k"].lower(): + raise TestFailure( + { + "Mode": param_set, + "C": exp["c"], + "K": exp["k"], + "ComputedC": c.hex(), + "ComputedK": k.hex(), + } + ) + return + + if function == "decapsulation" and test_type == "VAL": + dk = _from_hex(test["dk"]) + c = _from_hex(test["c"]) + priv = botan.PrivateKey.load_ml_kem(param_set, dk) + kem_d = botan.KemDecrypt(priv, "Raw") + try: + k = kem_d.decrypt_shared_key(b"", 32, c) + except botan.BotanException: + return # Implicit rejection is acceptable + if k.hex() != exp["k"].lower(): + raise TestFailure({"Mode": param_set, "K": exp["k"], "ComputedK": k.hex()}) + return + + if ( + function in ("encapsulationKeyCheck", "decapsulationKeyCheck") + and test_type == "VAL" + ): + expected_pass = exp.get("testPassed", True) + try: + if function == "encapsulationKeyCheck": + botan.PublicKey.load_ml_kem(param_set, _from_hex(test["ek"])) + else: + botan.PrivateKey.load_ml_kem(param_set, _from_hex(test["dk"])) + passed = True + except botan.BotanException: + passed = False + if passed != expected_pass: + raise TestFailure( + { + "Mode": param_set, + "Function": function, + "Expected": str(expected_pass), + "Got": str(passed), + } + ) + return + + raise TestSkip(f"Unsupported function/type: {function}/{test_type}") + + +# ---- ML-DSA ---- + + +_MLDSA_MODE_MAP = { + "ML-DSA-44": "ML-DSA-4x4", + "ML-DSA-65": "ML-DSA-6x5", + "ML-DSA-87": "ML-DSA-8x7", +} + + +@register("ML-DSA-keyGen-FIPS204") +def handle_mldsa_keygen(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + param_set = group["parameterSet"] + mode = _MLDSA_MODE_MAP.get(param_set, param_set) + + priv = botan.PrivateKey.load_ml_dsa(mode, _from_hex(test["seed"])) + pub = priv.get_public_key() + if pub.to_raw().hex() != exp["pk"].lower(): + raise TestFailure( + {"Mode": mode, "PK": exp["pk"], "ComputedPK": pub.to_raw().hex()} + ) + # sk comparison skipped: priv.to_raw() is seed form. + + +@register("ML-DSA-sigGen-FIPS204") +def handle_mldsa_siggen(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + param_set = group["parameterSet"] + mode = _MLDSA_MODE_MAP.get(param_set, param_set) + + if group.get("signatureInterface") == "internal": + raise TestSkip("ML-DSA internal signature interface not exposed") + if group.get("externalMu"): + raise TestSkip("ML-DSA externalMu not exposed via Python bindings") + if group.get("preHash", "pure") != "pure": + raise TestSkip("ML-DSA preHash signing not yet supported") + if "hashAlg" in test: + raise TestSkip("ML-DSA preHash (per-test hashAlg) not supported") + if test.get("context", ""): + raise TestSkip("ML-DSA context not supported") + + sk = _from_hex(test["sk"]) + msg = _from_hex(test["message"]) + + try: + priv = botan.PrivateKey.load_ml_dsa(mode, sk) + except botan.BotanException as e: + # ACVP provides expanded sk (2560/4032/4896 bytes); Botan only + # accepts seed-form (32 bytes). + raise TestSkip("ML-DSA expanded private key loading not supported") from e + + deterministic = group.get("deterministic", True) + if deterministic: + signer = botan.PKSign(priv, "Deterministic") + signer.update(msg) + sig = signer.finish(NullRNG()).hex() + else: + rnd = _from_hex(test["rnd"]) + rng = FixedOutputRNG(rnd) + signer = botan.PKSign(priv, "") + signer.update(msg) + sig = signer.finish(rng).hex() + if sig != exp["signature"].lower(): + raise TestFailure( + { + "Mode": mode, + "Msg": test["message"], + "Sig": exp["signature"], + "ComputedSig": sig, + } + ) + + +@register("ML-DSA-sigVer-FIPS204") +def handle_mldsa_sigver(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + param_set = group["parameterSet"] + mode = _MLDSA_MODE_MAP.get(param_set, param_set) + + if group.get("signatureInterface") == "internal": + raise TestSkip("ML-DSA internal signature interface not exposed") + if group.get("externalMu"): + raise TestSkip("ML-DSA externalMu not exposed via Python bindings") + if group.get("preHash", "pure") != "pure": + raise TestSkip("ML-DSA preHash verification not yet supported") + if "hashAlg" in test: + raise TestSkip("ML-DSA preHash (per-test hashAlg) not supported") + if test.get("context", ""): + raise TestSkip("ML-DSA context not supported") + + expected_valid = exp.get("testPassed", True) + + try: + pub = botan.PublicKey.load_ml_dsa(mode, _from_hex(test["pk"])) + verifier = botan.PKVerify(pub, "") + verifier.update(_from_hex(test["message"])) + valid = verifier.check_signature(_from_hex(test["signature"])) + except botan.BotanException: + valid = False + + if valid != expected_valid: + raise TestFailure( + { + "Mode": mode, + "Msg": test["message"], + "Sig": test["signature"], + "Expected": "valid" if expected_valid else "invalid", + "Got": "valid" if valid else "invalid", + } + ) + + +# ---- SLH-DSA ---- + + +@register("SLH-DSA-keyGen-FIPS205") +def handle_slhdsa_keygen(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + param_set = group["parameterSet"] + seed = ( + _from_hex(test["skSeed"]) + _from_hex(test["skPrf"]) + _from_hex(test["pkSeed"]) + ) + + try: + priv = botan.PrivateKey.load_slh_dsa(param_set, seed) + except botan.BotanException as e: + raise TestSkip( + f"SLH-DSA key loading from seed not supported for {param_set}" + ) from e + + pub = priv.get_public_key() + if pub.to_raw().hex() != exp["pk"].lower(): + raise TestFailure( + {"Mode": param_set, "PK": exp["pk"], "ComputedPK": pub.to_raw().hex()} + ) + + +@register("SLH-DSA-sigVer-FIPS205") +def handle_slhdsa_sigver(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + param_set = group["parameterSet"] + if group.get("signatureInterface") == "internal": + raise TestSkip("SLH-DSA internal signature interface not exposed") + if group.get("preHash", "pure") != "pure": + raise TestSkip("SLH-DSA preHash verification not yet supported") + if test.get("context", ""): + raise TestSkip("SLH-DSA context not supported") + + expected_valid = exp.get("testPassed", True) + + try: + pub = botan.PublicKey.load_slh_dsa(param_set, _from_hex(test["pk"])) + verifier = botan.PKVerify(pub, "") + verifier.update(_from_hex(test["message"])) + valid = verifier.check_signature(_from_hex(test["signature"])) + except botan.BotanException: + valid = False + + if valid != expected_valid: + raise TestFailure( + { + "Mode": param_set, + "Msg": test["message"], + "Sig": test["signature"], + "Expected": "valid" if expected_valid else "invalid", + "Got": "valid" if valid else "invalid", + } + ) + + +@register("SLH-DSA-sigGen-FIPS205") +def handle_slhdsa_siggen(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + param_set = group["parameterSet"] + if group.get("signatureInterface") == "internal": + raise TestSkip("SLH-DSA internal signature interface not exposed") + if group.get("preHash", "pure") != "pure": + raise TestSkip("SLH-DSA preHash signing not yet supported") + if test.get("context", ""): + raise TestSkip("SLH-DSA context not supported") + + sk = _from_hex(test["sk"]) + msg = _from_hex(test["message"]) + + try: + priv = botan.PrivateKey.load_slh_dsa(param_set, sk) + except botan.BotanException as e: + raise TestSkip(f"SLH-DSA key loading not supported for {param_set}") from e + + deterministic = group.get("deterministic", True) + if deterministic: + signer = botan.PKSign(priv, "Deterministic") + signer.update(msg) + sig = signer.finish(NullRNG()).hex() + else: + rnd = _from_hex(test["additionalRandomness"]) + rng = FixedOutputRNG(rnd) + signer = botan.PKSign(priv, "") + signer.update(msg) + sig = signer.finish(rng).hex() + + if sig != exp["signature"].lower(): + raise TestFailure( + { + "Mode": param_set, + "Msg": test["message"][:40] + "...", + "Sig": exp["signature"][:40] + "...", + "ComputedSig": sig[:40] + "...", + } + ) + + +# ---- HMAC_DRBG ---- + + +@register("hmacDRBG-1.0") +def handle_hmac_drbg(_header: dict, group: dict, test: dict, exp: dict) -> None: + _require_aft(group) + + hash_name = _HMAC_DRBG_MODE_MAP.get(group["mode"]) + if hash_name is None: + raise TestSkip(f"Unsupported DRBG hash: {group['mode']}") + + entropy = _from_hex(test["entropyInput"]) + nonce = _from_hex(test["nonce"]) + perso = _from_hex(test["persoString"]) if test.get("persoString") else b"" + out_len = group["returnedBitsLen"] // 8 + pred_resistance = group.get("predResistance", False) + + drbg = botan.RandomNumberGenerator.drbg( + f"HMAC_DRBG({hash_name})", entropy + nonce + perso + ) + + out = b"" + for item in test["otherInput"]: + ai = _from_hex(item["additionalInput"]) if item.get("additionalInput") else b"" + ent = _from_hex(item["entropyInput"]) if item.get("entropyInput") else b"" + + if item["intendedUse"] == "reSeed": + drbg.add_entropy(ent + ai) + elif pred_resistance and ent: + # SP800-90A §9.3.1: with PR, reseed with (entropy || ai), + # then generate with empty additional input. + drbg.add_entropy(ent + ai) + out = drbg.generate_with_input(out_len, b"") + else: + out = drbg.generate_with_input(out_len, ai) + + if out.hex() != exp["returnedBits"].lower(): + raise TestFailure( + { + "Mode": group["mode"], + "ReturnedBits": exp["returnedBits"], + "ComputedBits": out.hex(), + } + ) + + +# ---- Ignored algorithms ---- + +_registry.ignore( + # Botan follows SP800-108s presentation of combining counter, label and + # context with domain separation and the length encoding. For whatever + # reason the ACVP tests completely skip this and just provide a block of data + # that should be fed to the raw PRF. Since we follow the spec (?!?) it's + # not possible to run these tests. + # + # NIST is not my favorite standards organization + "KDF-1.0", + "KDA-TwoStep-Sp800-56Cr1", + "KDA-TwoStep-Sp800-56Cr2", + # ACVP's CTR tests are strange and possibly impossible for us to implement + "ACVP-AES-CTR-1.0", + "ACVP-TDES-CTR-1.0", + # Unimplemented CBC ciphertext-stealing variants + "ACVP-AES-CBC-CS1-1.0", + "ACVP-AES-CBC-CS2-1.0", + "ACVP-AES-CBC-CS3-1.0", + # We don't support 1-bit CFB + "ACVP-AES-CFB1-1.0", + "ACVP-TDES-CFB1-1.0", + # GCM-SIV currently not implemented + "ACVP-AES-GCM-SIV-1.0", + # Unimplemented FPE schemes + "ACVP-AES-FF1-1.0", + "ACVP-AES-FF3-1-1.0", + # Weirdo modes + "ACVP-AES-XPN-1.0", + "ACVP-AES-CCM-ECMA-1.0", + "ACVP-TDES-CBCI-1.0", + "ACVP-TDES-CFBP1-1.0", + "ACVP-TDES-CFBP64-1.0", + "ACVP-TDES-CFBP8-1.0", + "ACVP-TDES-OFBI-1.0", + # Unimplemented, I didn't even know this was a thing + "ACVP-TDES-KW-1.0", + # Finite Field DSA + "DSA-KeyGen-1.0", + "DSA-PQGGen-1.0", + "DSA-PQGVer-1.0", + # Unimplemented + "Ascon-CXOF128-SP800-232", + "ParallelHash-128-1.0", + "ParallelHash-256-1.0", + "TupleHash-128-1.0", + "TupleHash-256-1.0", + # Doesn't seem relevant + "safePrimes-keyVer-1.0", + "safePrimes-keyGen-1.0", + # Unimplemented DRBGs and support fns + "ctrDRBG-1.0", + "hashDRBG-1.0", + "ConditioningComponent-AES-CBC-MAC-Sp800-90B", + "ConditioningComponent-BlockCipher_DF-Sp800-90B", + "ConditioningComponent-Hash_DF-Sp800-90B", + # Unimplemented KDFs + "kdf-components-IKEv1-1.0", + "kdf-components-ansix9.42-1.0", + "kdf-components-ikev2-1.0", + "kdf-components-snmp-1.0", + "kdf-components-srtp-1.0", + "kdf-components-ssh-1.0", + "kdf-components-tpm-1.0", + "KDF-KMAC-Sp800-108r1", + "KDA-OneStepNoCounter-Sp800-56Cr2", + # These are all some kind of multi-step protocol rather than + # just testing a primitive + "RSA-signaturePrimitive-1.0", + "RSA-decryptionPrimitive-1.0", + "KAS-ECC-1.0", + "KAS-ECC-CDH-Component-1.0", + "KAS-ECC-CDH-Component-Sp800-56Ar3", + "KAS-ECC-SSC-Sp800-56Ar3", + "KAS-ECC-Sp800-56Ar3", + "KAS-FFC-1.0", + "KAS-FFC-SSC-Sp800-56Ar3", + "KAS-FFC-Sp800-56Ar3", + "KAS-IFC-SSC-Sp800-56Br2", + "KAS-IFC-Sp800-56Br2", + "KAS-KC-Sp800-56", + "KTS-IFC-Sp800-56Br2", + "TLS-v1.3-KDF-RFC8446", +) + + +# ---- Entry point ---- + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Run NIST ACVP test vectors against Botan's Python bindings" + ) + parser.add_argument( + "data_dir", + nargs="?", + default=os.environ.get("ACVP_TESTDATA_DIR"), + help="path to ACVP-Server gen-val/json-files directory " + "(default: $ACVP_TESTDATA_DIR)", + ) + parser.add_argument("--verbose", "-v", action="store_true", help="be noisy") + parser.add_argument("--quiet", "-q", action="store_true", help="be quiet") + parser.add_argument( + "--jobs", "-j", type=int, default=None, help="number of workers" + ) + parser.add_argument( + "--filter", + "-f", + action="append", + default=[], + help="only run directories whose name matches FILTER (case-insensitive " + "substring, may be repeated)", + ) + args = parser.parse_args() + + if args.data_dir is None: + parser.error( + "data_dir argument or ACVP_TESTDATA_DIR environment variable required" + ) + + jobs = args.jobs + if jobs is not None and jobs <= 0: + parser.error("Invalid --jobs parameter") + + verbosity = 0 if args.quiet else (2 if args.verbose else 1) + return run(args.data_dir, verbosity, jobs, args.filter or None) + + +if __name__ == "__main__": + sys.exit(main()) diff -Nru botan3-3.7.1+dfsg/src/scripts/bench.py botan3-3.12.0+dfsg/src/scripts/bench.py --- botan3-3.7.1+dfsg/src/scripts/bench.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/bench.py 2026-05-07 01:38:28.000000000 +0000 @@ -120,18 +120,18 @@ result = {} - for l in output.splitlines(): - if ignored.match(l): + for line in output.splitlines(): + if ignored.match(line): continue if not result: - match = buf_header.match(l) + match = buf_header.match(line) if match is None: - logging.error("Unexpected output from OpenSSL %s", l) + logging.error("Unexpected output from OpenSSL %s", line) result = {'algo': algo, 'buf_size': int(match.group(3))} else: - match = res_header.match(l) + match = res_header.match(line) result['bytes'] = int(match.group(1)) * result['buf_size'] result['runtime'] = float(match.group(2)) @@ -145,18 +145,18 @@ result = {} - for l in output.splitlines(): - if ignored.match(l): + for line in output.splitlines(): + if ignored.match(line): continue - if match := signature_ops.match(l): + if match := signature_ops.match(line): results.append({ 'algo': algo, 'key_size': int(match.group(3)), 'op': 'sign', 'ops': int(match.group(2)), 'runtime': float(match.group(4))}) - elif match := verify_ops.match(l): + elif match := verify_ops.match(line): results.append({ 'algo': algo, 'key_size': int(match.group(3)), @@ -165,7 +165,7 @@ 'runtime': float(match.group(4)) }) else: - logging.error("Unexpected output from OpenSSL %s", l) + logging.error("Unexpected output from OpenSSL %s", line) elif algo in KEY_AGREEMENT_EVP_MAP: res_header = re.compile(r'\+(R7|R9|R12|R14):([0-9]+):([0-9]+):([0-9]+\.[0-9]+)$') @@ -173,18 +173,18 @@ result = {} - for l in output.splitlines(): - if ignored.match(l): + for line in output.splitlines(): + if ignored.match(line): continue - if match := res_header.match(l): + if match := res_header.match(line): results.append({ 'algo': algo, 'key_size': int(match.group(3)), 'ops': int(match.group(2)), 'runtime': float(match.group(4))}) else: - logging.error("Unexpected output from OpenSSL %s", l) + logging.error("Unexpected output from OpenSSL %s", line) return results @@ -236,13 +236,13 @@ output = json.loads(output) results = [] - for l in output: - if l['op'] == 'key agreements': + for res in output: + if res['op'] == 'key agreements': results.append({ 'algo': algo, - 'key_size': int(re.search(r'[A-Z]+-[a-z]*([0-9]+).*', l['algo']).group(1)), - 'ops': l['events'], - 'runtime': l['nanos'] / 1000 / 1000 / 1000, + 'key_size': int(re.search(r'[A-Z]+-[a-z]*([0-9]+).*', res['algo']).group(1)), + 'ops': res['events'], + 'runtime': res['nanos'] / 1000 / 1000 / 1000, }) return results diff -Nru botan3-3.7.1+dfsg/src/scripts/build_docs.py botan3-3.12.0+dfsg/src/scripts/build_docs.py --- botan3-3.7.1+dfsg/src/scripts/build_docs.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/build_docs.py 2026-05-07 01:38:28.000000000 +0000 @@ -155,6 +155,7 @@ with_docs = bool(cfg['with_documentation']) with_sphinx = bool(cfg['with_sphinx']) with_pdf = bool(cfg['with_pdf']) + with_texinfo = bool(cfg['with_texinfo']) with_rst2man = bool(cfg['with_rst2man']) with_doxygen = bool(cfg['with_doxygen']) @@ -182,6 +183,13 @@ cmds.append(sphinx_build + ['-b', 'latex', handbook_src, latex_output]) cmds.append(['make', '-C', latex_output]) cmds.append(['cp', os.path.join(latex_output, 'botan.pdf'), handbook_output]) + + if with_texinfo: + texinfo_output = tempfile.mkdtemp(prefix='botan_texinfo_') + cmds.append(sphinx_build + ['-b', 'texinfo', handbook_src, texinfo_output]) + cmds.append(['make', '-C', texinfo_output]) + cmds.append(['cp', os.path.join(texinfo_output, 'botan.texi'), handbook_output]) + else: # otherwise just copy it cmds.append(['cp', handbook_src, handbook_output]) diff -Nru botan3-3.7.1+dfsg/src/scripts/ci/ci_tlsanvil_check.py botan3-3.12.0+dfsg/src/scripts/ci/ci_tlsanvil_check.py --- botan3-3.7.1+dfsg/src/scripts/ci/ci_tlsanvil_check.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/ci/ci_tlsanvil_check.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,199 +0,0 @@ -# Parses a TLS-Anvil results directory. Returns 0 iff all results are expected. -# -# (C) 2023 Jack Lloyd -# (C) 2023 Fabian Albert, Rohde & Schwarz Cybersecurity -# -# Botan is released under the Simplified BSD License (see license.txt) -import sys -import argparse -import os -import json -import logging - - -result_level = { - "STRICTLY_SUCCEEDED": 0, - "CONCEPTUALLY_SUCCEEDED": 1, - "PARTIALLY_FAILED": 2, - "FULLY_FAILED": 3, -} - - -def expected_result_for(method_id: str): - """ Get the expected result for a given test id """ - allowed_to_conceptually_succeed = { - # Okay: RFC does not specifically define an alert. Bogo Test expects an DecodeError Alert - # while TLS-Anvil expects an IllegalParameter Alert. We use the DecodeError Alert. - "server.tls13.rfc8446.PreSharedKey.isLastButDuplicatedExtension" - } - - # TODO: Analyze partially failing tests and document if/why they are allowed to fail - allowed_to_partially_fail = { - "server.tls12.statemachine.StateMachine.earlyChangeCipherSpec", - "server.tls12.rfc7568.DoNotUseSSLVersion30.sendClientHelloVersion0300RecordVersion" - } - - # TODO: Analyze failing tests and document if/why they are allowed to fail - allowed_to_fully_fail = { - "both.tls13.rfc8446.KeyUpdate.respondsWithValidKeyUpdate", - "server.tls13.rfc8446.ClientHello.invalidLegacyVersion_ssl3", - "server.tls13.rfc8446.ClientHello.invalidLegacyVersion_ssl30", - "server.tls13.rfc8446.RecordLayer.zeroLengthRecord_Finished", - "server.tls13.rfc8446.KeyShare.abortsWhenSharedSecretIsZero", - "server.tls12.rfc8422.TLSExtensionForECC.rejectsInvalidCurvePoints", - "server.tls12.rfc5246.ClientHello.leaveOutExtensions", - "server.tls12.rfc5246.E1CompatibilityWithTLS10_11andSSL30.acceptAnyRecordVersionNumber", - "both.tls13.rfc8446.KeyUpdate.appDataUnderNewKeysSucceeds" - } - - if method_id in allowed_to_fully_fail: - return result_level["FULLY_FAILED"] - - if method_id in allowed_to_partially_fail: - return result_level["PARTIALLY_FAILED"] - - if method_id in allowed_to_conceptually_succeed: - return result_level["CONCEPTUALLY_SUCCEEDED"] - - return result_level["STRICTLY_SUCCEEDED"] - - -def test_result_valid(method_id: str, result: str): - """ - Return True iff the result is valid for the method. - """ - if result == "DISABLED": - return True - - expected_res = expected_result_for(method_id) - if result_level[result] < expected_res: - logging.warning("Warning: Test result better than expected for '%s'. Consider tighten the expectation.", method_id) - - return result_level[result] <= expected_result_for(method_id) - - -def failing_test_info(json_data, method_id) -> str: - """ Print debug information about a failing test """ - info_str = "" - try: - method_class, method_name = method_id.rsplit('.', 1) - info = [f"Error: {method_id} - Unexpected result '{json_data['Result']}'"] - info += [""] - info += [f"Class Name: 'de.rub.nds.tlstest.suite.tests.{method_class}'"] - info += [f"Method Name: '{method_name}'"] - info += [""] - if json_data['TestMethod']['RFC'] is not None: - info += [ f"RFC {json_data['TestMethod']['RFC']['number']}, Section {json_data['TestMethod']['RFC']['Section']}:"] - else: - info += ["Custom Test Case:"] - info += [f"{json_data['TestMethod']['Description']}"] - info += [""] - - info += [f"Result: {json_data['Result']} (expected {list(result_level.keys())[list(result_level.values()).index(expected_result_for(method_id))]})"] - if json_data['DisabledReason'] is not None: - info += [f"Disabled Reason: {json_data['DisabledReason']}"] - - - additional_res_info = list({state["AdditionalResultInformation"] for state in json_data['States'] if state["AdditionalResultInformation"] != ""}) - additional_test_info = list({state["AdditionalTestInformation"] for state in json_data['States'] if state["AdditionalTestInformation"] != ""}) - state_result = [{state["Result"] for state in json_data['States']}] - - if len(state_result) > 1 or len(additional_res_info) > 1 or len(additional_test_info) > 1: - info += ["Different results for different states. See test results artifact for more information."] - - if len(additional_res_info) == 1: - info += ["", f"Additional Result Info: {additional_res_info[0]}"] - - if len(additional_test_info) == 1: - info += ["", f"Additional Test Info: {additional_test_info[0]}"] - info += [""] - - info_str = "\n".join(info) - - # Color in red - info_str = "\n".join([f"\033[0;31m{line}\033[0m" for line in info_str.split("\n")]) - - # In GitHub Actions logging group - info_str = f"::group::{info_str}\n::endgroup::" - - except KeyError: - logging.warning("Cannot process test info.") - info_str = "" - - return info_str - - -def process_results_container(results_container_path: str): - """ - Given a path, process the respective results container .json file. - Returns True, iff the results of the container are expected. - """ - success = False - with open(results_container_path, "r", encoding="utf-8") as results_container_file: - try: - json_data = json.load(results_container_file) - method_id = ".".join( - [ - json_data["TestMethod"]["ClassName"], - json_data["TestMethod"]["MethodName"], - ] - ).removeprefix("de.rub.nds.tlstest.suite.tests.") - result = json_data["Result"] - is_valid = test_result_valid(method_id, result) - if is_valid: - logging.debug("%s: '%s' -> ok", method_id, result) - success = True - else: - # Print a GitHub logging group in red - logging.error(failing_test_info(json_data, method_id)) - - except KeyError: - logging.error("Json file '%s' has missing entries.", results_container_path) - - return success - - -def main(args=None): - """Parse args and check all result container files""" - if args is None: - args = sys.argv[1:] - - parser = argparse.ArgumentParser() - parser.add_argument("--verbose", action="store_true", default=False) - parser.add_argument("results-dir", help="directory of TLS-Anvil test results") - - args = vars(parser.parse_args(args)) - - logging.basicConfig( - level=(logging.DEBUG if args["verbose"] else logging.INFO), - format="%(message)s", - ) - - results_dir = args["results-dir"] - - if not os.access(results_dir, os.X_OK): - raise FileNotFoundError("Unable to read TLS-Anvil results dir") - - failed_methods_count = 0 - total_methods_count = 0 - for root, _, files in os.walk(results_dir): - for file in files: - if file == "_containerResult.json": - abs_path = os.path.abspath(os.path.join(root, file)) - total_methods_count += 1 - if not process_results_container(abs_path): - failed_methods_count += 1 - - logging.info( - "(%i/%i) test methods successful.", - total_methods_count - failed_methods_count, - total_methods_count, - ) - total_success = failed_methods_count == 0 - logging.info("Total result: %s", "Success." if total_success else "Failed.") - - return int(not total_success) - - -if __name__ == "__main__": - sys.exit(main()) diff -Nru botan3-3.7.1+dfsg/src/scripts/ci/ci_tlsanvil_test.py botan3-3.12.0+dfsg/src/scripts/ci/ci_tlsanvil_test.py --- botan3-3.7.1+dfsg/src/scripts/ci/ci_tlsanvil_test.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/ci/ci_tlsanvil_test.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,139 +0,0 @@ -# Script to run inside the CI container to test the botan -# TLS client/server with TLS-Anvil -# -# (C) 2023 Jack Lloyd -# (C) 2023 Fabian Albert, Rohde & Schwarz Cybersecurity -# -# Botan is released under the Simplified BSD License (see license.txt) -import sys -import argparse -import os -import subprocess - - -class Config: - """ Hardcoded configurations for this CI script """ - tls_anvil_docker_image = "ghcr.io/tls-attacker/tlsanvil" - tls_anvil_version_tag = "@sha256:e9abe034e6b1dac7fe204d524db338f379087a16aca71e94dc7b51ac835bb53f" # v1.2.2 + HelloRetry test fix - key_and_cert_storage_path = "/tmp/" - test_suite_results_dest = "." - test_suite_results_dir_name = "TestSuiteResults" - tmp_key_file_name = "tmp_rsa_key.pem" - tmp_cert_file_name = "tmp_rsa_cert.pem" - server_dest_ip = "127.0.0.1" - server_dest_port = 4433 - botan_server_log = "./logs/botan_server.log" - botan_config_args = ["--compiler-cache=ccache", "--build-targets=static,cli", - "--without-documentation", "--with-boost"] - -def group_output(group_title: str, func): - """ - Wraps a function to be called within a GitHub actions group, so that - the console output is expandable. - - Returns the wrapped function - """ - def wrapped_func(*args, **kwargs): - print(f"::group::{group_title}", flush=True) - ret = func(*args, **kwargs) - print("\n::endgroup::", flush=True) - return ret - return wrapped_func - - -def create_cert_and_key(botan_dir_path): - """ - Create a X.509 certificate and associated RSA key at Config.key_and_cert_storage_path - using Botan's CLI. - - Returns: (, ) - """ - - key_path = os.path.join(Config.key_and_cert_storage_path, Config.tmp_key_file_name) - cert_path = os.path.join(Config.key_and_cert_storage_path, Config.tmp_cert_file_name) - - with open(key_path, 'w', encoding='utf-8') as keyfile: - subprocess.run([botan_dir_path, "keygen", "--algo=RSA", "--params=2048"], stdout=keyfile, check=True) - - with open(cert_path, 'w', encoding='utf-8') as certfile: - subprocess.run([botan_dir_path, "gen_self_signed", key_path, "localhost"], stdout=certfile, check=True) - - return (cert_path, key_path) - - -def build_botan(botan_dir: str, parallel_jobs: int) -> str: - """ - Configure and build botan. - - Returns the botan executable path - """ - group_output("Configure Botan", subprocess.run)(["python3", "./configure.py"] + Config.botan_config_args, check=True, cwd=botan_dir) - group_output("Build Botan with Make", subprocess.run)(["make", f"-j{parallel_jobs}"], check=True, cwd=botan_dir) - - return os.path.join(botan_dir, "botan") - - -def server_test(botan_dir_path: str, parallel: int): - """ Test the Botan TLS server """ - cert_path, key_path = create_cert_and_key(botan_dir_path) - docker_img = f"{Config.tls_anvil_docker_image}{Config.tls_anvil_version_tag}" - - group_output("Pull TLS-Anvil image", subprocess.run)(["docker", "pull", docker_img], check=True) - - tls_anvil_cmd = [ - "docker", "run", - "--network", "host", - "-v", f"{Config.test_suite_results_dest}:/output", - docker_img, - "-strength", "1", - "-parallelHandshakes", str(parallel), - "-disableTcpDump", - "-outputFolder", os.path.join(Config.test_suite_results_dest, Config.test_suite_results_dir_name), - "-connectionTimeout", "5000", - "server", "-connect", f"{Config.server_dest_ip}:{Config.server_dest_port}" - ] - - botan_server_cmd = [ - botan_dir_path, "tls_http_server", cert_path, key_path, f"--port={Config.server_dest_port}" - ] - - os.makedirs(os.path.dirname(Config.botan_server_log), exist_ok=True) - - # Run Botan and test is with TLS-Anvil - with open(Config.botan_server_log, 'w', encoding='utf-8') as server_log_file: - botan_server_process = subprocess.Popen(botan_server_cmd, stdout=server_log_file, stderr=server_log_file) - subprocess.run(tls_anvil_cmd, check=True) - botan_server_process.kill() - - -def client_test(botan_dir_path: str, parallel: int): - """ Test the Botan TLS server """ - raise NotImplementedError("Client tests not yet implemented") - - -def main(args=None): - if args is None: - args = sys.argv[1:] - - parser = argparse.ArgumentParser() - parser.add_argument("--botan-dir", help="Botan base directory", required=True) - parser.add_argument("--test-target", help="The TLS side to test", choices=['client', 'server'], required=True) - parser.add_argument("--parallel", help="The number of parallel handshakes", type=int, default=1) - - args = vars(parser.parse_args(args)) - - if not os.path.isdir(args["botan_dir"]): - raise FileNotFoundError(f"Unable to find '{args['botan_dir']}'") - - botan_exe_path = build_botan(args["botan_dir"], args["parallel"]) - - if args["test_target"] == "server": - server_test(botan_exe_path, args["parallel"]) - elif args["test_target"] == "client": - client_test(botan_exe_path, args["parallel"]) - - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff -Nru botan3-3.7.1+dfsg/src/scripts/ci/cmake_tests/CMakeLists.txt botan3-3.12.0+dfsg/src/scripts/ci/cmake_tests/CMakeLists.txt --- botan3-3.7.1+dfsg/src/scripts/ci/cmake_tests/CMakeLists.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/ci/cmake_tests/CMakeLists.txt 2026-05-07 01:38:28.000000000 +0000 @@ -18,9 +18,9 @@ message(FATAL_ERROR "Test #1 failed: Failed to find Botan with any version") endif() -if(TARGET Botan::Botan) +if(TARGET botan::botan) add_executable(botan_version_test main.cpp) - target_link_libraries(botan_version_test Botan::Botan) + target_link_libraries(botan_version_test botan::botan) if(WIN32) add_custom_command(TARGET botan_version_test POST_BUILD @@ -37,9 +37,9 @@ # test #2: link statically find_package(Botan REQUIRED) -if(TARGET Botan::Botan-static) +if(TARGET botan::botan-static) add_executable(botan_version_test_static main.cpp) - target_link_libraries(botan_version_test_static Botan::Botan-static) + target_link_libraries(botan_version_test_static botan::botan-static) add_test(NAME "Using static library" COMMAND botan_version_test_static @@ -131,4 +131,15 @@ unset(Botan_FOUND) unset(botan_FOUND) +# test #11: use backward-compatibility ALIAS targets +# (Assuming that we use at least CMake 3.18, otherwise that might fail) +find_package(Botan REQUIRED) +if(Botan_FOUND) + if(NOT TARGET Botan::Botan AND NOT TARGET Botan::Botan-static) + message(FATAL_ERROR "Test #11 didn't find the expected backward-compatibility ALIAS target") + endif() +endif() + +unset(Botan_FOUND) + enable_testing() diff -Nru botan3-3.7.1+dfsg/src/scripts/ci/download_ci_dep.py botan3-3.12.0+dfsg/src/scripts/ci/download_ci_dep.py --- botan3-3.7.1+dfsg/src/scripts/ci/download_ci_dep.py 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/ci/download_ci_dep.py 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 + +""" +(C) 2026 Jack Lloyd + +Botan is released under the Simplified BSD License (see license.txt) +""" + +import argparse +import configparser +import hashlib +import os +import subprocess +import sys +import tempfile +import urllib.request + +def load_config(dep_name): + config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), + '..', '..', 'configs', 'ci_deps.conf') + config = configparser.ConfigParser() + config.read(config_path) + + if dep_name not in config: + print("Unknown dependency %s - available options are %s" % ( + dep_name, ','.join(config.sections()))) + sys.exit(1) + + section = config[dep_name] + url = section.get('url') + sha256 = section.get('sha256') + if not url or not sha256: + print("Bad config entry for %s" % (dep_name)) + sys.exit(1) + + return url, sha256 + +def download(url, fileobj, max_mb): + max_bytes = max_mb * 1024 * 1024 + req = urllib.request.Request(url) + hasher = hashlib.sha256() + total = 0 + with urllib.request.urlopen(req) as resp: + content_length = resp.headers.get('Content-Length') + content_length = int(content_length) if content_length is not None else None + + if (content_length is not None) and (content_length > max_bytes): + raise RuntimeError("Download of %s too large, server reports %d bytes" % (url, content_length)) + + while True: + chunk = resp.read(256 * 1024) + if not chunk: + break + total += len(chunk) + if (content_length is not None) and (total > content_length): + raise RuntimeError("Server sent too much data for %s, reported %d" % (url, content_length)) + if total > max_bytes: + raise RuntimeError("Server sent too much data for %s" % (url)) + hasher.update(chunk) + fileobj.write(chunk) + + return hasher.hexdigest(), total + +def main(): + parser = argparse.ArgumentParser(description='Download a CI dependency with integrity verification') + parser.add_argument('dep_name', help='Dependency name (section in ci_deps.conf)') + parser.add_argument('output_path', nargs='?', default=None, + help='Output file path (default: filename from URL in current directory)') + parser.add_argument('--max-download-mb', default=48, type=int, + help='Maximum download size in MB') + parser.add_argument('--extract', default=None, metavar='CMD', + help='Extract after download using CMD template (eg "tar -xf {file}")') + args = parser.parse_args() + + url, expected_sha256 = load_config(args.dep_name) + + if args.extract: + final_path = None + fd, tmp_path = tempfile.mkstemp(prefix='ci_dep_') + else: + if args.output_path: + final_path = args.output_path + else: + final_path = os.path.basename(urllib.request.url2pathname(url.split('?')[0])) + fd, tmp_path = tempfile.mkstemp(prefix='.ci_dep_', + dir=os.path.dirname(final_path) or '.') + + try: + with os.fdopen(fd, 'wb') as f: + computed_sha256, total = download(url, f, args.max_download_mb) + + if computed_sha256 != expected_sha256: + print("Checksum failure downloading %s - got %s (%d bytes)" % ( + url, computed_sha256, total)) + return 1 + + if args.extract: + cmd = args.extract.replace('{file}', tmp_path) + subprocess.run(cmd, shell=True, check=True) + else: + os.replace(tmp_path, final_path) + print(final_path) + + return 0 + except Exception as e: + print(str(e)) + return 1 + finally: + try: + os.unlink(tmp_path) + except FileNotFoundError: + pass + +if __name__ == '__main__': + sys.exit(main()) diff -Nru botan3-3.7.1+dfsg/src/scripts/ci/gh_clang_tidy_fixes_in_pr.py botan3-3.12.0+dfsg/src/scripts/ci/gh_clang_tidy_fixes_in_pr.py --- botan3-3.7.1+dfsg/src/scripts/ci/gh_clang_tidy_fixes_in_pr.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/ci/gh_clang_tidy_fixes_in_pr.py 2026-05-07 01:38:28.000000000 +0000 @@ -37,9 +37,9 @@ def __map_file_path(self, file_path, base_path): # pylint: disable=unused-argument if file_path.endswith(".h"): - raise RuntimeError(f"Header file {file_path} cannot be handled") - # TODO: try to map include files (residing in build/include) onto their - # origin path in src/lib etc. + # This only works for symlink builds, which is sufficient for CI reporting + return os.path.realpath(file_path) + return file_path @@ -48,11 +48,11 @@ with open(self.file, encoding="utf-8") as srcfile: readoffset = 0 lineoffset = 0 - for l in srcfile.readlines(): - readoffset += len(l) + for line in srcfile.readlines(): + readoffset += len(line) lineoffset += 1 if readoffset >= offset: - coloffset = offset - readoffset + len(l) + coloffset = offset - readoffset + len(line) return (lineoffset, coloffset) raise RuntimeError(f"FileOffset {offset} out of range for {self.file}") diff -Nru botan3-3.7.1+dfsg/src/scripts/ci/gh_get_changes_in_pr.py botan3-3.12.0+dfsg/src/scripts/ci/gh_get_changes_in_pr.py --- botan3-3.7.1+dfsg/src/scripts/ci/gh_get_changes_in_pr.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/ci/gh_get_changes_in_pr.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import gzip -import http.client -import json -import re -import sys - -def main(args = None): - if args is None: - args = sys.argv - - re_git_sha = re.compile('[0-9A-Fa-f]{20}') - - parser = argparse.ArgumentParser() - - parser.add_argument('--base-commit', default='master', metavar='BRANCH') - parser.add_argument('--api-host', default='api.github.com', metavar='HOST') - parser.add_argument('--api-token', default=None) - parser.add_argument('this_commit') - - args = vars(parser.parse_args()) - - gh_api = args['api_host'] - this_commit = args['this_commit'] - base_commit = args['base_commit'] - - if re_git_sha.match(this_commit) is None: - print("The argument '%s' does not look like a git commit id" % (this_commit)) - return 1 - - headers = { - "Accept": "application/vnd.github+json", - "Accept-Encoding": "gzip", - "X-GitHub-Api-Version": "2022-11-28", - "Host": gh_api, - "User-Agent": "Botan gh_get_changes_in_pr.py", - } - - if args.get('api_token'): - headers['Authorization'] = 'Bearer %s' % (args['api_token']) - - api_req = "/repos/randombit/botan/compare/%s...%s?per_page=0" % (base_commit, this_commit) - - gh = http.client.HTTPSConnection(gh_api) - gh.request('GET', api_req, headers=headers) - resp = gh.getresponse() - - if resp.status != 200: - print("GH API call returned unexpected status %d" % (resp.status)) - return 1 - - is_gzip = False - content_encoding = resp.getheader('Content-Encoding') - if content_encoding: - if content_encoding == 'gzip': - is_gzip = True - else: - print("Unexpected Content-Encoding %s" % (content_encoding)) - return 1 - - body = resp.read() - - if is_gzip: - body = gzip.decompress(body) - - j = json.loads(body) - - if 'files' not in j: - return 0 - - for f in j['files']: - print(f['filename']) - - return 0 - -if __name__ == '__main__': - sys.exit(main()) - diff -Nru botan3-3.7.1+dfsg/src/scripts/ci/gha_linux_packages.py botan3-3.12.0+dfsg/src/scripts/ci/gha_linux_packages.py --- botan3-3.7.1+dfsg/src/scripts/ci/gha_linux_packages.py 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/ci/gha_linux_packages.py 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 + +""" +(C) 2025 Jack Lloyd + +Botan is released under the Simplified BSD License (see license.txt) +""" + +import sys + +def gha_linux_packages(target, compiler): + packages = [ + 'ccache', + 'libbz2-dev', + 'liblzma-dev', + 'libsqlite3-dev', + ] + + if compiler in ['gcc-14']: + packages.append('gcc-14') + + if target.startswith('valgrind'): + packages.append('valgrind') + + if target in ['shared', 'coverage', 'amalgamation', 'sanitizer', 'tlsanvil', 'examples', 'clang-tidy', 'no_tls12', 'no_tls13']: + packages.append('libboost-dev') + + if target in ['clang']: + packages.append('clang') + + if target in ['cross-i386']: + packages.append('g++-multilib') + packages.append('linux-libc-dev') + packages.append('libc6-dev-i386') + + if target in ['cross-win64']: + packages.append('wine-development') + packages.append('g++-mingw-w64-x86-64') + + if target in ['cross-arm32']: + packages.append('qemu-user') + packages.append('g++-arm-linux-gnueabihf') + + if target in ['cross-arm64', 'cross-arm64-amalgamation']: + packages.append('qemu-user') + packages.append('g++-aarch64-linux-gnu') + + if target in ['cross-ppc32']: + packages.append('qemu-user') + packages.append('g++-powerpc-linux-gnu') + + if target in ['cross-ppc64']: + packages.append('qemu-user') + packages.append('g++-powerpc64le-linux-gnu') + + if target in ['cross-sh4']: + packages.append('qemu-user') + packages.append('g++-sh4-linux-gnu') + + if target in ['cross-sparc64']: + packages.append('qemu-user') + packages.append('g++-sparc64-linux-gnu') + + if target in ['cross-m68k']: + packages.append('qemu-user') + packages.append('g++-m68k-linux-gnu') + + if target in ['cross-riscv64']: + packages.append('qemu-user') + packages.append('g++-riscv64-linux-gnu') + + if target in ['cross-alpha']: + packages.append('qemu-user') + packages.append('g++-alpha-linux-gnu') + + if target in ['cross-arc']: + packages.append('qemu-user') + packages.append('g++-arc-linux-gnu') + + if target in ['cross-hppa64']: + packages.append('qemu-user') + packages.append('g++-hppa-linux-gnu') + + if target in ['cross-loongarch64']: + packages.append('qemu-user') + packages.append('g++-14-loongarch64-linux-gnu') + + if target in ['cross-mips']: + packages.append('qemu-user') + packages.append('g++-mips-linux-gnu') + + if target in ['cross-mips64']: + packages.append('qemu-user') + packages.append('g++-mips64-linux-gnuabi64') + + if target in ['cross-s390x']: + packages.append('qemu-user') + packages.append('g++-s390x-linux-gnu') + + if target in ['cross-arm32-baremetal']: + packages.append('gcc-arm-none-eabi') + packages.append('libstdc++-arm-none-eabi-newlib') + + if target in ['emscripten']: + packages.append('emscripten') + + if target in ['lint']: + packages.append('pylint') + packages.append('python3-matplotlib') + + if target in ['limbo']: + packages.append('python3-dateutil') + + if target in ['coverage']: + packages.append('lcov') + packages.append('python3-coverage') + + if target in ['strubbing']: + packages.append('gdb') + + if target in ['coverage', 'sanitizer', 'clang-tidy']: + packages.append('libtspi-dev') # TPM 1 development library [TODO(Botan4) remove this] + packages.append('libtss2-dev') # TPM 2 development library + + if target in ['coverage', 'sanitizer', 'pkcs11']: + packages.append('softhsm2') + + if target in ['coverage', 'sanitizer']: + # Following are only available on Ubuntu 24.04 + # If we wanted to test building of TPM2 on 22.04 we'd need to restrict these + + packages.append('tpm2-tools') # CLI tools to interact with the TPM + packages.append('swtpm') # TPM 2.0 simulator + packages.append('swtpm-tools') # CLI tools to set up the TPM simulator + packages.append('tpm2-abrmd') # user-space resource manager for TPM 2.0 + packages.append('libtss2-tcti-tabrmd0') # TCTI (TPM Command Transmission Interface) for the user-space resource manager + + if target in ['docs']: + packages.append('doxygen') + packages.append('python3-docutils') + packages.append('python3-sphinx') + + return packages + + +def main(args = None): + if args is None: + args = sys.argv + + if len(args) != 3: + print("Unexpected usage: %s " % (args[0])) + return 1 + + target = args[1] + compiler = args[2] + + print(" ".join(gha_linux_packages(target, compiler))) + + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff -Nru botan3-3.7.1+dfsg/src/scripts/ci/setup_gh_actions.ps1 botan3-3.12.0+dfsg/src/scripts/ci/setup_gh_actions.ps1 --- botan3-3.7.1+dfsg/src/scripts/ci/setup_gh_actions.ps1 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/ci/setup_gh_actions.ps1 2026-05-07 01:38:28.000000000 +0000 @@ -9,10 +9,26 @@ param( [Parameter()] [String]$TARGET, + [String]$COMPILER, [String]$ARCH ) -choco install -y sccache +# Create `sccache` in a CI temp directory +$ciTempDir = if ($env:RUNNER_TEMP) { $env:RUNNER_TEMP } else { $env:TEMP } +$sccacheDir = Join-Path -Path $ciTempDir -ChildPath "sccache" + +# Extract sccache tarball into sccache dir we just created +New-Item -ItemType Directory -Force -Path $sccacheDir | Out-Null +& python "$PSScriptRoot\download_ci_dep.py" sccache_windows --extract "tar -xzf {file} --strip-components=1 -C `"$sccacheDir`"" +if($LASTEXITCODE -ne 0) { + throw "Failed to download and extract sccache (exit code $LASTEXITCODE)" +} + +# Have to set path within this script for later invocations +$env:PATH = "$sccacheDir;$env:PATH" + +# Also store in GITHUB_PATH so it's found during the rest of the job +echo "$sccacheDir" >> $env:GITHUB_PATH # find the sccache cache location and store it in the build job's environment $raw_cl = (sccache --stats-format json --show-stats | ConvertFrom-Json).cache_location @@ -31,3 +47,8 @@ } else { echo "VSENV_ARCH=$ARCH" >> $env:GITHUB_ENV } + +# Remove standalone LLVM (and clang-cl) from PATH - we want to use the one shipped with VS. +# https://github.com/actions/runner-images/issues/10001#issuecomment-2150541007 +$no_llvm_path = ($env:PATH -split ';' | Where-Object { $_ -ne 'C:\Program Files\LLVM\bin' }) -join ';' +echo "PATH=$no_llvm_path" >> $env:GITHUB_ENV diff -Nru botan3-3.7.1+dfsg/src/scripts/ci/setup_gh_actions.sh botan3-3.12.0+dfsg/src/scripts/ci/setup_gh_actions.sh --- botan3-3.7.1+dfsg/src/scripts/ci/setup_gh_actions.sh 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/ci/setup_gh_actions.sh 2026-05-07 01:38:28.000000000 +0000 @@ -12,165 +12,76 @@ set -ex TARGET="$1" +COMPILER="$2" # shellcheck disable=SC2034 -ARCH="$2" +ARCH="$3" SCRIPT_LOCATION=$(cd "$(dirname "$0")"; pwd) +if [ "$GITHUB_ACTIONS" != "true" ]; then + echo "This script should only run in a Github Actions environment" >&2 + exit 1 +fi + if [ -z "$REPO_CONFIG_LOADED" ]; then echo "Repository configuration not loaded" >&2 exit 1 fi if type -p "apt-get"; then - # TPM2-TSS library (to build the library against) - tpm2_specific_packages=("libtss2-dev") - - # Our simulated TPM 2.0 setup depends on convenience features that are - # available only in Ubuntu 24.04. Technically, most of the TPM 2.0 support - # in Botan should work on 22.04 as well. - # - # TODO: Look into whether we can use the TPM 2.0 simulator on 22.04 to be - # able to run the tests on that version as well. - if [ "$(lsb_release -sr)" = "24.04" ]; then - # Additional TPM 2.0 related packages to set up a simulated - # TPM 2.0 environment for testing. - tpm2_specific_packages+=("tpm2-tools" # CLI tools to interact with the TPM - "swtpm" # TPM 2.0 simulator - "swtpm-tools" # CLI tools to set up the TPM simulator - "tpm2-abrmd" # user-space resource manager for TPM 2.0 - "libtss2-tcti-tabrmd0") # TCTI (TPM Command Transmission Interface) for the user-space resource manager) - ci_support_of_tpm2="test" - else - # If we are not on Ubuntu 24.04, we can't set up a TPM 2.0 simulator - # and potentially just build the library with TPM 2.0 support but don't - # run the tests. - ci_support_of_tpm2="build" - fi - if [ "$(lsb_release -sr)" = "22.04" ]; then - # Hack to deal with https://github.com/actions/runner-images/issues/8659 - sudo rm -f /etc/apt/sources.list.d/ubuntu-toolchain-r-ubuntu-test-jammy.list - sudo apt-get update - sudo apt-get install -y --allow-downgrades libc6=2.35-* libc6-dev=2.35-* libstdc++6=12.3.0-* libgcc-s1=12.3.0-* - fi + sudo rm -f /var/lib/man-db/auto-update - # Normal workflow follows sudo apt-get -qq update - sudo apt-get -qq install ccache libbz2-dev liblzma-dev libsqlite3-dev - - if [ "$TARGET" = "valgrind" ] || [ "$TARGET" = "valgrind-full" ] || [ "$TARGET" = "valgrind-ct-full" ] || [ "$TARGET" = "valgrind-ct" ]; then - # (l)ist mode (avoiding https://github.com/actions/runner-images/issues/9996) - sudo NEEDRESTART_MODE=l apt-get -qq install valgrind - - elif [ "$TARGET" = "static" ]; then - sudo apt-get -qq install "${tpm2_specific_packages[@]}" - echo "BOTAN_TPM2_ENABLED=${ci_support_of_tpm2}" >> "$GITHUB_ENV" - - elif [ "$TARGET" = "shared" ]; then - sudo apt-get -qq install libboost-dev "${tpm2_specific_packages[@]}" - echo "BOTAN_TPM2_ENABLED=${ci_support_of_tpm2}" >> "$GITHUB_ENV" - - elif [ "$TARGET" = "examples" ] || [ "$TARGET" = "amalgamation" ] || [ "$TARGET" = "tlsanvil" ] || [ "$TARGET" = "clang-tidy" ] ; then - sudo apt-get -qq install libboost-dev libtss2-dev - - elif [ "$TARGET" = "clang" ]; then - sudo apt-get -qq install clang - - elif [ "$TARGET" = "cross-i386" ]; then - sudo NEEDRESTART_MODE=l apt-get -qq install g++-multilib linux-libc-dev libc6-dev-i386 - - elif [ "$TARGET" = "cross-win64" ]; then - sudo apt-get -qq install wine-development g++-mingw-w64-x86-64 - - elif [ "$TARGET" = "cross-arm32" ]; then - sudo apt-get -qq install qemu-user g++-arm-linux-gnueabihf - - elif [ "$TARGET" = "cross-arm64" ] || [ "$TARGET" = "cross-arm64-amalgamation" ]; then - sudo apt-get -qq install qemu-user g++-aarch64-linux-gnu - - elif [ "$TARGET" = "cross-ppc32" ]; then - sudo apt-get -qq install qemu-user g++-powerpc-linux-gnu - - elif [ "$TARGET" = "cross-ppc64" ]; then - sudo apt-get -qq install qemu-user g++-powerpc64le-linux-gnu - - elif [ "$TARGET" = "cross-sh4" ]; then - sudo apt-get -qq install qemu-user g++-sh4-linux-gnu - - elif [ "$TARGET" = "cross-sparc64" ]; then - sudo apt-get -qq install qemu-user g++-sparc64-linux-gnu + # shellcheck disable=SC2046 + sudo apt-get -qq install $("${SCRIPT_LOCATION}"/gha_linux_packages.py "$TARGET" "$COMPILER") - elif [ "$TARGET" = "cross-m68k" ]; then - sudo apt-get -qq install qemu-user g++-m68k-linux-gnu - - elif [ "$TARGET" = "cross-riscv64" ]; then - sudo apt-get -qq install qemu-user g++-riscv64-linux-gnu - - elif [ "$TARGET" = "cross-alpha" ]; then - sudo apt-get -qq install qemu-user g++-alpha-linux-gnu - - elif [ "$TARGET" = "cross-arc" ]; then - sudo apt-get -qq install qemu-user g++-arc-linux-gnu - - elif [ "$TARGET" = "cross-hppa64" ]; then - sudo apt-get -qq install qemu-user g++-hppa-linux-gnu - - elif [ "$TARGET" = "cross-mips" ]; then - sudo apt-get -qq install qemu-user g++-mips-linux-gnu - - elif [ "$TARGET" = "cross-mips64" ]; then - sudo apt-get -qq install qemu-user g++-mips64-linux-gnuabi64 - - elif [ "$TARGET" = "cross-s390x" ]; then - sudo apt-get -qq install qemu-user g++-s390x-linux-gnu - - elif [ "$TARGET" = "sde" ]; then - wget "https://downloadmirror.intel.com/823664/${INTEL_SDE_VERSION}.tar.xz" - tar -xvf "${INTEL_SDE_VERSION}.tar.xz" + if [ "$TARGET" = "sde" ]; then + "${SCRIPT_LOCATION}"/download_ci_dep.py intel_sde --extract 'tar -xf {file}' echo "${INTEL_SDE_VERSION}" >> "$GITHUB_PATH" + echo "CXX=g++-14" >> "$GITHUB_ENV" + elif [ "$TARGET" = "cross-android-arm32" ] || [ "$TARGET" = "cross-android-arm64" ] || [ "$TARGET" = "cross-android-arm64-amalgamation" ]; then - wget -nv "https://dl.google.com/android/repository/${ANDROID_NDK}-linux.zip" - unzip -qq "$ANDROID_NDK"-linux.zip + "${SCRIPT_LOCATION}"/download_ci_dep.py --max-download-mb=800 android_ndk --extract 'unzip -qq {file}' elif [ "$TARGET" = "cross-arm32-baremetal" ]; then - sudo apt-get -qq install gcc-arm-none-eabi libstdc++-arm-none-eabi-newlib - echo 'extern "C" void __sync_synchronize() {}' >> "${SCRIPT_LOCATION}/../../tests/main.cpp" echo 'extern "C" void __sync_synchronize() {}' >> "${SCRIPT_LOCATION}/../../cli/main.cpp" - elif [ "$TARGET" = "emscripten" ]; then - sudo apt-get -qq install emscripten + elif [ "$TARGET" = "limbo" ]; then + "${SCRIPT_LOCATION}"/download_ci_dep.py limbo "${SCRIPT_LOCATION}/../../../limbo.json" elif [ "$TARGET" = "lint" ]; then - sudo apt-get -qq install pylint python3-matplotlib + pip install ruff - elif [ "$TARGET" = "limbo" ]; then - sudo apt-get -qq install python3-dateutil - wget -nv "https://raw.githubusercontent.com/C2SP/x509-limbo/${LIMBO_TEST_SUITE_REVISION}/limbo.json" -O "${SCRIPT_LOCATION}/../../../limbo.json" + elif [ "$TARGET" = "typos" ]; then + cargo install typos-cli - elif [ "$TARGET" = "coverage" ] || [ "$TARGET" = "sanitizer" ]; then - if [ "$TARGET" = "coverage" ]; then - sudo apt-get -qq install lcov python3-coverage - curl -L https://coveralls.io/coveralls-linux.tar.gz | tar -xz -C /usr/local/bin - fi + elif [ "$TARGET" = "coverage" ]; then + "${SCRIPT_LOCATION}"/download_ci_dep.py coveralls --extract 'tar -xz -C /usr/local/bin -f {file}' - sudo apt-get -qq install softhsm2 libtspi-dev libboost-dev "${tpm2_specific_packages[@]}" - echo "BOTAN_TPM2_ENABLED=${ci_support_of_tpm2}" >> "$GITHUB_ENV" + elif [ "$TARGET" = "wycheproof" ]; then + git clone --depth 1 "${WYCHEPROOF_GIT_URL}" wycheproof-git + echo "WYCHEPROOF_DIR=$(pwd)/wycheproof-git" >> "$GITHUB_ENV" - echo "$HOME/.local/bin" >> "$GITHUB_PATH" + elif [ "$TARGET" = "acvp" ]; then + git clone --depth 1 "${ACVP_SERVER_GIT_URL}" acvp-server-git + echo "ACVP_TESTDATA_DIR=$(pwd)/acvp-server-git/gen-val/json-files" >> "$GITHUB_ENV" + fi + if [ "$TARGET" = "coverage" ] || [ "$TARGET" = "sanitizer" ]; then + echo "BOTAN_TPM2_ENABLED=test" >> "$GITHUB_ENV" + fi + + # SoftHSM setup + if [ "$TARGET" = "coverage" ] || [ "$TARGET" = "sanitizer" ] || [ "$TARGET" = "pkcs11" ]; then sudo chgrp -R "$(id -g)" /var/lib/softhsm/ /etc/softhsm sudo chmod g+w /var/lib/softhsm/tokens softhsm2-util --init-token --free --label test --pin 123456 --so-pin 12345678 echo "PKCS11_LIB=/usr/lib/softhsm/libsofthsm2.so" >> "$GITHUB_ENV" - - elif [ "$TARGET" = "docs" ]; then - sudo apt-get -qq install doxygen python3-docutils python3-sphinx - fi else export HOMEBREW_NO_AUTO_UPDATE=1 @@ -190,7 +101,7 @@ if [ -d '/Applications/Xcode_16.1.app/Contents/Developer' ]; then sudo xcrun xcode-select --switch '/Applications/Xcode_16.1.app/Contents/Developer' - else + elif [ -d '/Applications/Xcode_15.2.app/Contents/Developer' ]; then sudo xcrun xcode-select --switch '/Applications/Xcode_15.2.app/Contents/Developer' fi fi @@ -200,3 +111,5 @@ cache_location="$( ccache --get-config cache_dir )" echo "COMPILER_CACHE_LOCATION=${cache_location}" >> "${GITHUB_ENV}" fi + +echo "BOTAN_CLANG_TIDY_CACHE=$HOME/botan_clang_tidy.db" >> "${GITHUB_ENV}" diff -Nru botan3-3.7.1+dfsg/src/scripts/ci/setup_gh_actions_after_ccache.sh botan3-3.12.0+dfsg/src/scripts/ci/setup_gh_actions_after_ccache.sh --- botan3-3.7.1+dfsg/src/scripts/ci/setup_gh_actions_after_ccache.sh 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/ci/setup_gh_actions_after_ccache.sh 2026-05-07 01:38:28.000000000 +0000 @@ -13,14 +13,19 @@ set -ex TARGET="$1" - SCRIPT_LOCATION=$(cd "$(dirname "$0")"; pwd) function build_and_install_jitterentropy() { - mkdir jitterentropy-library - curl -L "https://github.com/smuellerDD/jitterentropy-library/archive/refs/tags/v${JITTERENTROPY_VERSION}.tar.gz" | tar -xz -C . + "${SCRIPT_LOCATION}"/download_ci_dep.py jitterentropy --extract 'tar -xz -f {file}' jel_dir="$(realpath jitterentropy-library-*)" - cmake -B "${jel_dir}/build" -S "${jel_dir}" -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER_LAUNCHER=ccache + + # The -DCMAKE_POLICY_VERSION_MINIMUM=3.5 directive is a workaround because + # recent versions of CMake refused to configure this project as it still + # claims compatibility with 2.x releases of CMake which have now fallen out + # of support. + # + # See also: https://github.com/smuellerDD/jitterentropy-library/issues/147 + cmake -B "${jel_dir}/build" -S "${jel_dir}" -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_POLICY_VERSION_MINIMUM=3.5 cmake --build "${jel_dir}/build" sudo cmake --install "${jel_dir}/build" echo "BOTAN_BUILD_WITH_JITTERENTROPY=1" >> "$GITHUB_ENV" @@ -32,25 +37,18 @@ sudo apt-get -qq install libprotobuf-c-dev meson # download, build and install ESDM - curl -L "https://github.com/smuellerDD/esdm/archive/refs/tags/v${ESDM_VERSION}.tar.gz" | tar -xz -C . + "${SCRIPT_LOCATION}"/download_ci_dep.py esdm --extract 'tar -xz -f {file}' pushd "$(realpath esdm-*)" meson setup build -Dselinux=disabled -Dais2031=false -Dlinux-devfiles=disabled -Des_jent=disabled --prefix=/usr --libdir=lib meson compile -C build sudo meson install -C build popd + echo "BOTAN_BUILD_WITH_ESDM=1" >> "$GITHUB_ENV" } if type -p "apt-get"; then - if [ "$TARGET" = "valgrind" ] || [ "$TARGET" = "valgrind-full" ] || [ "$TARGET" = "valgrind-ct-full" ] || [ "$TARGET" = "valgrind-ct" ] || \ - [ "$TARGET" = "examples" ] || [ "$TARGET" = "amalgamation" ] || [ "$TARGET" = "tlsanvil" ] || [ "$TARGET" = "clang-tidy" ] ; then + if [ "$TARGET" = "coverage" ] || [ "$TARGET" = "clang-tidy" ] || [ "$TARGET" = "optional-rngs" ]; then build_and_install_jitterentropy - - elif [ "$TARGET" = "shared" ]; then build_and_install_esdm - - elif [ "$TARGET" = "coverage" ] || [ "$TARGET" = "sanitizer" ]; then - build_and_install_jitterentropy - build_and_install_esdm - fi fi diff -Nru botan3-3.7.1+dfsg/src/scripts/ci/setup_gh_actions_after_vcvars.ps1 botan3-3.12.0+dfsg/src/scripts/ci/setup_gh_actions_after_vcvars.ps1 --- botan3-3.7.1+dfsg/src/scripts/ci/setup_gh_actions_after_vcvars.ps1 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/ci/setup_gh_actions_after_vcvars.ps1 2026-05-07 01:38:28.000000000 +0000 @@ -6,7 +6,7 @@ # # Botan is released under the Simplified BSD License (see license.txt) -$targets_with_boost = @("shared", "amalgamation") +$targets_with_boost = @("amalgamation") if ($targets_with_boost -contains $args[0]) { nuget install -NonInteractive -OutputDirectory $env:DEPENDENCIES_LOCATION -Version 1.79.0 boost diff -Nru botan3-3.7.1+dfsg/src/scripts/ci/start_tpm2_simulator.sh botan3-3.12.0+dfsg/src/scripts/ci/start_tpm2_simulator.sh --- botan3-3.7.1+dfsg/src/scripts/ci/start_tpm2_simulator.sh 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/ci/start_tpm2_simulator.sh 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,4 @@ -#/bin/bash +#!/bin/bash # # Sets up a TPM2 simulator that is running behind a user-space TPM2 resource @@ -7,7 +7,7 @@ # # The simulator is populated with persistent keys for testing. # -# If you need the simulated TPM 2.0 setup in your deveolpment environment, you +# If you need the simulated TPM 2.0 setup in your development environment, you # can run this script manually. If something goes wrong, you can re-initialize # the TPM 2.0 simulator by running: # @@ -36,8 +36,8 @@ # "Endorsement Key" - baked into the TPM (signed by the manufacturer) # "Platform Key" - signed serial number of the EK (signed by the OEM; eg. the laptop manufacturer) # "Storage Root Key" - created by the user (signed by the EK) -rm -fR $tmp_dir && mkdir $tmp_dir -swtpm_setup --tpmstate $tmp_dir \ +rm -fR "$tmp_dir" && mkdir "$tmp_dir" +swtpm_setup --tpmstate "$tmp_dir" \ --create-ek-cert \ --create-platform-cert \ --create-spk \ @@ -45,7 +45,7 @@ --display echo "Starting TPM2 simulator..." -swtpm socket --tpmstate dir=$tmp_dir \ +swtpm socket --tpmstate dir="$tmp_dir" \ --ctrl type=tcp,port=2322 \ --server type=tcp,port=2321 \ --flags not-need-init \ @@ -73,42 +73,42 @@ --hierarchy e \ --hash-algorithm sha256 \ --key-algorithm rsa \ - --key-context $tmp_dir/primary.ctx + --key-context "$tmp_dir/primary.ctx" # Use default key template of tpm2_create for rsa. # This means that the key will NOT be "restricted". -tpm2_create --tcti="$tcti" \ - --parent-context $tmp_dir/primary.ctx \ - --key-algorithm rsa \ - --public $tmp_dir/rsa.pub \ - --private $tmp_dir/rsa.priv \ +tpm2_create --tcti="$tcti" \ + --parent-context "$tmp_dir/primary.ctx" \ + --key-algorithm rsa \ + --public "$tmp_dir/rsa.pub" \ + --private "$tmp_dir/rsa.priv" \ --key-auth $test_pwd -tpm2_load --tcti="$tcti" \ - --parent-context $tmp_dir/primary.ctx \ - --public $tmp_dir/rsa.pub \ - --private $tmp_dir/rsa.priv \ - --key-context $tmp_dir/rsa.ctx -tpm2_evictcontrol --tcti="$tcti" \ - --hierarchy o \ - --object-context $tmp_dir/rsa.ctx \ +tpm2_load --tcti="$tcti" \ + --parent-context "$tmp_dir/primary.ctx" \ + --public "$tmp_dir/rsa.pub" \ + --private "$tmp_dir/rsa.priv" \ + --key-context "$tmp_dir/rsa.ctx" +tpm2_evictcontrol --tcti="$tcti" \ + --hierarchy o \ + --object-context "$tmp_dir"/rsa.ctx \ $persistent_rsa_key_handle # Do the same for ecc -tpm2_create --tcti="$tcti" \ - --parent-context $tmp_dir/primary.ctx \ - --key-algorithm ecc \ - --public $tmp_dir/ecc.pub \ - --private $tmp_dir/ecc.priv \ +tpm2_create --tcti="$tcti" \ + --parent-context "$tmp_dir/primary.ctx" \ + --key-algorithm ecc \ + --public "$tmp_dir/ecc.pub" \ + --private "$tmp_dir/ecc.priv" \ --key-auth $test_pwd -tpm2_load --tcti="$tcti" \ - --parent-context $tmp_dir/primary.ctx \ - --public $tmp_dir/ecc.pub \ - --private $tmp_dir/ecc.priv \ - --key-context $tmp_dir/ecc.ctx -tpm2_evictcontrol --tcti="$tcti" \ - --hierarchy o \ - --object-context $tmp_dir/ecc.ctx \ +tpm2_load --tcti="$tcti" \ + --parent-context "$tmp_dir/primary.ctx" \ + --public "$tmp_dir/ecc.pub" \ + --private "$tmp_dir/ecc.priv" \ + --key-context "$tmp_dir/ecc.ctx" +tpm2_evictcontrol --tcti="$tcti" \ + --hierarchy o \ + --object-context "$tmp_dir/ecc.ctx" \ $persistent_ecc_key_handle echo "Effectively disable dictionary attack lockout..." @@ -122,9 +122,12 @@ # the test scripts that are going to run, if we're running on GitHub Actions. if [ -n "$GITHUB_ACTIONS" ]; then echo "Setting up GitHub Actions environment..." - echo "BOTAN_TPM2_TCTI_NAME=$tcti_name" >> $GITHUB_ENV - echo "BOTAN_TPM2_TCTI_CONF=$tcti_conf" >> $GITHUB_ENV - echo "BOTAN_TPM2_PERSISTENT_KEY_AUTH_VALUE=$test_pwd" >> $GITHUB_ENV - echo "BOTAN_TPM2_PERSISTENT_RSA_KEY_HANDLE=$persistent_rsa_key_handle" >> $GITHUB_ENV - echo "BOTAN_TPM2_PERSISTENT_ECC_KEY_HANDLE=$persistent_ecc_key_handle" >> $GITHUB_ENV + { + echo "BOTAN_TPM2_TCTI_NAME=$tcti_name" + echo "BOTAN_TPM2_TCTI_CONF=$tcti_conf" + echo "BOTAN_TPM2_PERSISTENT_KEY_AUTH_VALUE=$test_pwd" + echo "BOTAN_TPM2_PERSISTENT_RSA_KEY_HANDLE=$persistent_rsa_key_handle" + echo "BOTAN_TPM2_PERSISTENT_ECC_KEY_HANDLE=$persistent_ecc_key_handle" + } >> "$GITHUB_ENV" fi + diff -Nru botan3-3.7.1+dfsg/src/scripts/ci_build.py botan3-3.12.0+dfsg/src/scripts/ci_build.py --- botan3-3.7.1+dfsg/src/scripts/ci_build.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/ci_build.py 2026-05-07 01:38:28.000000000 +0000 @@ -30,7 +30,6 @@ def known_targets(): return [ 'amalgamation', - 'bsi', 'codeql', 'coverage', 'cross-alpha', @@ -44,6 +43,7 @@ 'cross-hppa64', 'cross-i386', 'cross-ios-arm64', + 'cross-loongarch64', 'cross-m68k', 'cross-mips', 'cross-mips64', @@ -65,14 +65,25 @@ 'minimized', 'nist', 'no_pcurves', + 'no_tls12', + 'optional-rngs', + 'no_tls13', + 'pkcs11', + 'policy-bsi', + 'policy-fips140', + 'policy-modern', 'sanitizer', 'sde', 'shared', 'static', + 'strubbing', + 'typos', 'valgrind', 'valgrind-full', 'valgrind-ct', 'valgrind-ct-full', + 'wycheproof', + 'acvp', ] def is_running_in_github_actions(): @@ -86,6 +97,7 @@ def __init__(self, group_title): self.group_title = group_title + self.start_time = time.time() def __enter__(self): if is_running_in_github_actions(): @@ -94,16 +106,20 @@ print("Running '%s' ..." % self.group_title) sys.stdout.flush() - return is_running_in_github_actions() def __exit__(self, exc_type, exc_value, exc_tb): + time_taken = int(time.time() - self.start_time) + if is_running_in_github_actions(): print("::endgroup::") + if time_taken > 10: + print("> Running '%s' took %d seconds" % (self.group_title, time_taken)) + def build_targets(target, target_os): - if target in ['shared', 'minimized', 'bsi', 'nist', 'examples']: + if target in ['shared', 'minimized', 'examples', 'limbo', 'optional-rngs', 'pkcs11', 'wycheproof', 'acvp'] or target.startswith('policy-'): yield 'shared' - elif target in ['static', 'fuzzers', 'cross-arm32-baremetal', 'emscripten']: + elif target in ['static', 'fuzzers', 'cross-arm32-baremetal', 'emscripten', 'strubbing']: yield 'static' elif target_os in ['windows']: yield 'shared' @@ -113,13 +129,13 @@ yield 'shared' yield 'static' - if target not in ['examples']: + if target not in ['examples', 'limbo', 'wycheproof', 'acvp']: yield 'cli' - if target not in ['examples', 'limbo']: + if target not in ['examples', 'limbo', 'hybrid-tls13-interop-test', 'strubbing', 'wycheproof', 'acvp']: yield 'tests' - if target in ['coverage']: + if target in ['coverage', 'no_tls12', 'no_tls13']: yield 'bogo_shim' if target in ['sanitizer'] and target_os not in ['windows']: yield 'bogo_shim' @@ -128,6 +144,16 @@ if target in ['valgrind', 'valgrind-full', 'valgrind-ct', 'valgrind-ct-full']: yield 'ct_selftest' +def make_targets(target, target_os): + # The result of build_targets() is for ./configure.py --build-targets='...'. + # make_targets() go into `make/ninja ...` and they are mostly equal. Except + # for 'libs' which is an umbrella target for 'static' and 'shared'. + tgts = [tgt if tgt not in ['static', 'shared'] else 'libs' for tgt in build_targets(target, target_os)] + if target in ['coverage', 'fuzzers']: + # These are special targets not found in ./configure.py --build-targets= + tgts += ['fuzzers', 'fuzzer_corpus_zip'] + return list(set(tgts)) + def determine_flags(target, target_os, target_cpu, target_cc, cc_bin, ccache, root_dir, build_dir, test_results_dir, pkcs11_lib, use_gdb, disable_werror, extra_cxxflags, custom_optimization_flags, disabled_tests): @@ -139,7 +165,7 @@ if target_os not in ['linux', 'osx', 'windows', 'freebsd']: print('Error unknown OS %s' % (target_os)) - return (None, None, None, None) + return (None, None, None) if is_cross_target: if target_os == 'osx': @@ -160,7 +186,6 @@ make_prefix = [] test_prefix = [] - pretest_cmd = [] test_cmd = [os.path.join(build_dir, 'botan-test'), '--data-dir=%s' % os.path.join(root_dir, 'src', 'tests', 'data'), '--run-memory-intensive-tests'] @@ -192,7 +217,7 @@ flags += ['--prefix=%s' % (install_prefix)] if ccache is not None: - flags += ['--no-store-vc-rev', '--compiler-cache=%s' % (ccache)] + flags += ['--compiler-cache=%s' % (ccache)] if not disable_werror: flags += ['--werror-mode'] @@ -212,26 +237,31 @@ # Workaround for https://github.com/actions/runner-images/issues/10004 flags += ['--extra-cxxflags=/D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR'] - if target_os == 'linux' and target in ['shared', 'coverage', 'sanitizer']: - flags += ['--with-esdm_rng'] - if target in ['minimized']: - flags += ['--minimized-build', '--enable-modules=system_rng,sha2_32,sha2_64,aes'] + flags += ['--minimized-build', '--enable-modules=system_rng,sha2*,aes'] if target in ['no_pcurves']: flags += ['--disable-modules=pcurves_impl'] + if target in ['no_tls12']: + flags += ['--disable-modules=tls12'] + + if target in ['no_tls13']: + flags += ['--disable-modules=tls13'] + if target in ['amalgamation', 'cross-arm64-amalgamation', 'cross-android-arm64-amalgamation']: flags += ['--amalgamation'] - if target in ['bsi', 'nist']: - # tls is optional for bsi/nist but add it so verify tests work with these minimized configs - flags += ['--module-policy=%s' % (target), '--enable-modules=tls12', '--disable-deprecated-features'] + if target.startswith('policy-'): + # ffi and tls are optional for bsi/fips140 - add to build to verify these work with the minimized config + flags += ['--module-policy=%s' % (target.replace('policy-', '')), '--enable-modules=ffi,tls12,tls13', '--disable-deprecated-features'] if target in ['docs']: flags += ['--with-doxygen', '--with-sphinx', '--with-rst2man'] + else: + flags += ['--without-doc'] - if target in ['docs', 'codeql', 'hybrid-tls13-interop-test', 'limbo']: + if target in ['docs', 'codeql', 'hybrid-tls13-interop-test', 'limbo', 'wycheproof', 'acvp']: test_cmd = None if target in ['codeql']: @@ -248,55 +278,25 @@ if target == 'coverage': flags += ['--with-coverage-info'] - if target in ['coverage']: + if target in ['coverage', 'valgrind', 'valgrind-full', 'valgrind-ct', 'valgrind-ct-full']: flags += ['--with-debug-info'] + if target in ['strubbing']: + # Stack scrubbing tests are based on scripted gdb runs and won't work on + # an optimized build unfortunately. + flags += ['--debug-mode'] + test_cmd = None + if target in ['coverage', 'sanitizer', 'fuzzers']: - flags += ['--unsafe-terminate-on-asserts'] + flags += ['--unsafe-terminate-on-asserts', '--enable-modules=tls_null'] if target in ['sde']: test_prefix = ['sde', '-future', '--'] if target in ['valgrind', 'valgrind-full', 'valgrind-ct', 'valgrind-ct-full']: flags += ['--with-valgrind'] - - test_prefix = ['valgrind', - '-v', - '--error-exitcode=9'] - - # For finding memory bugs, we're enabling more features that add runtime - # overhead which we don't need for the secret-dependent execution checks - # that 'valgrind-ct' and 'valgrind-ct-full' are aiming for. - if target not in ['valgrind-ct', 'valgrind-ct-full']: - test_prefix += ['--leak-check=full', - '--show-reachable=yes', - '--track-origins=yes'] - - build_config = os.path.join(build_dir, 'build', 'build_config.json') - pretest_cmd = ['python3', os.path.join(root_dir, 'src', 'ct_selftest', 'ct_selftest.py'), "--build-config-path=%s" % build_config, os.path.join(build_dir, 'botan_ct_selftest')] - - # valgrind is single threaded anyway - test_cmd += ['--test-threads=1'] - - if target not in ['valgrind-full', 'valgrind-ct-full']: - # valgrind is slow, so some tests only run in the nightly check - slow_tests = [ - 'argon2', 'bcrypt', 'bcrypt_pbkdf', 'compression_tests', 'cryptobox', - 'dh_invalid', 'dh_kat', 'dh_keygen', 'dl_group_gen', 'dlies', - 'dsa_kat_verify', 'dsa_param', 'ecc_basemul', 'ecdsa_verify_wycheproof', - 'ed25519_sign', 'elgamal_decrypt', 'elgamal_encrypt', 'elgamal_keygen', - 'ffi_dh', 'ffi_dsa', 'ffi_elgamal', 'frodo_kat_tests', 'hash_nist_mc', - 'hss_lms_keygen', 'hss_lms_sign', 'mce_keygen', 'passhash9', 'pbkdf', - 'pcurves_arith', 'pwdhash', 'rsa_encrypt', 'rsa_pss', 'rsa_pss_raw', 'scrypt', - 'sphincsplus', 'sphincsplus_fors', 'slh_dsa_keygen', 'slh_dsa', 'srp6_kat', - 'srp6_rt', 'unit_tls', 'x509_path_bsi', 'x509_path_rsa_pss', - 'xmss_keygen', 'xmss_keygen_reference', 'xmss_sign', 'xmss_unit_tests', - 'xmss_verify', 'xmss_verify_invalid', - ] - slow_tests += [f"dilithium_kat_{mode}_{rand}" for mode in ('6x5', '8x7', '6x5_AES', '8x7_AES') for rand in ('Deterministic', 'Randomized')] - slow_tests += [f"ml_dsa_kat_{mode}_{rand}" for mode in ('6x5', '8x7') for rand in ('Deterministic', 'Randomized')] - - disabled_tests += slow_tests + # valgrind is run via a script setup later + test_cmd = None if target == 'examples': flags += ['--with-boost'] @@ -320,9 +320,17 @@ flags += ['--disable-modules=locking_allocator'] if target == 'emscripten': - flags += ['--cpu=wasm'] - # need to find a way to run the wasm-compiled tests w/o a browser - test_cmd = None + # While it's possible to run the tests in a headless browser on CI, it's easier to just target Node.js instead, + # especially to gather the results. + flags += ['--cpu=wasm', '--program-suffix=.js', '--extra-cxxflags=-msimd128', '--ldflags=-sNODERAWFS=1'] + test_cmd = ['node', os.path.join(build_dir, 'botan-test.js')] + test_cmd[1:] + + if target in ['sanitizer', 'strubbing'] and target_cc in ['gcc']: + # Stack scrubbing is supported on GCC 14 and newer, only. This is newer + # than the current default compiler on GHA's Linux image (ubuntu 24.04). + # The CI setup has to ensure that we are configured to use a recent + # compiler for these targets, otherwise `./configure.py` will fail. + flags += ['--enable-stack-scrubbing'] if is_cross_target: if target_os == 'ios': @@ -375,8 +383,6 @@ flags += ['--cpu=armv7', '--extra-cxxflags=-D_FILE_OFFSET_BITS=64'] cc_bin = 'arm-linux-gnueabihf-g++' test_prefix = ['qemu-arm', '-L', '/usr/arm-linux-gnueabihf/'] - # disable a few tests that are exceptionally slow under arm32 qemu - disabled_tests += ['dh_invalid', 'dlies', 'frodo_kat_tests', 'xmss_sign'] elif target in ['cross-arm64', 'cross-arm64-amalgamation']: flags += ['--cpu=aarch64'] cc_bin = 'aarch64-linux-gnu-g++' @@ -408,7 +414,7 @@ test_prefix = ['qemu-ppc', '-L', '/usr/powerpc-linux-gnu/'] test_cmd = None # qemu crashes ... elif target == 'cross-ppc64': - flags += ['--cpu=ppc64', '--with-endian=little'] + flags += ['--cpu=ppc64'] cc_bin = 'powerpc64le-linux-gnu-g++' test_prefix = ['qemu-ppc64le', '-cpu', 'power10', '-L', '/usr/powerpc64le-linux-gnu/'] elif target == 'cross-riscv64': @@ -419,12 +425,16 @@ flags += ['--cpu=s390x'] cc_bin = 's390x-linux-gnu-g++' test_prefix = ['qemu-s390x', '-L', '/usr/s390x-linux-gnu/'] + elif target == 'cross-loongarch64': + flags += ['--cpu=loongarch64'] + cc_bin = 'loongarch64-linux-gnu-g++-14' + test_prefix = ['qemu-loongarch64', '-L', '/usr/loongarch64-linux-gnu/'] elif target == 'cross-mips': - flags += ['--cpu=mips32', '--with-endian=big'] + flags += ['--cpu=mips32'] cc_bin = 'mips-linux-gnu-g++' test_prefix = ['qemu-mips', '-L', '/usr/mips-linux-gnu/'] elif target == 'cross-mips64': - flags += ['--cpu=mips64', '--with-endian=big'] + flags += ['--cpu=mips64'] cc_bin = 'mips64-linux-gnuabi64-g++' test_prefix = ['qemu-mips64', '-L', '/usr/mips64-linux-gnuabi64/'] elif target in ['cross-arm32-baremetal']: @@ -443,12 +453,15 @@ flags += ['--with-commoncrypto'] def add_boost_support(target, target_os): - if target in ['coverage', 'shared', 'amalgamation']: + if target in ['coverage', 'amalgamation', 'no_tls12', 'no_tls13']: return True if target == 'sanitizer' and target_os == 'linux': return True + if target == 'shared' and target_os != 'windows': + return True + return False if add_boost_support(target, target_os): @@ -489,12 +502,14 @@ # only works for individual test names. test_cmd += ["--tpm2-tcti-name=disabled"] - if is_running_in_github_actions() and 'BOTAN_BUILD_WITH_JITTERENTROPY' in os.environ: - flags += ['--enable-modules=jitter_rng'] + if target in ['coverage', 'clang-tidy', 'optional-rngs']: + flags += ['--enable-modules=jitter_rng,esdm_rng'] if target in ['coverage']: flags += ['--with-tpm'] test_cmd += ['--run-online-tests'] + + if target in ['coverage', 'pkcs11']: if pkcs11_lib and os.access(pkcs11_lib, os.R_OK): test_cmd += ['--pkcs11-lib=%s' % (pkcs11_lib)] @@ -510,10 +525,8 @@ # slower tests to take as long as 5 minutes test_cmd.remove('--run-long-tests') - flags += ['--cc-bin=%s' % (cc_bin)] - - if not pretest_cmd: - pretest_cmd = None + if os.getenv('CXX') is None: + flags += ['--cc-bin=%s' % (cc_bin)] if test_cmd is None: run_test_command = None @@ -534,7 +547,7 @@ else: run_test_command = test_prefix + test_cmd - return flags, pretest_cmd, run_test_command, make_prefix + return flags, run_test_command, make_prefix def run_cmd(cmd, root_dir, build_dir): """ @@ -542,8 +555,6 @@ """ with LoggingGroup(' '.join(cmd)): - start = time.time() - cmd = [os.path.expandvars(elem) for elem in cmd] sub_env = os.environ.copy() sub_env['LD_LIBRARY_PATH'] = os.path.abspath(build_dir) @@ -569,11 +580,6 @@ proc = subprocess.Popen(cmd, cwd=cwd, close_fds=True, env=sub_env, stdout=redirect_stdout_fd) proc.communicate() - time_taken = int(time.time() - start) - - if time_taken > 10: - print("Ran for %d seconds" % (time_taken)) - if proc.returncode != 0: print("Command '%s' failed with error code %d" % (' '.join(cmd), proc.returncode)) @@ -619,6 +625,8 @@ help='Set directory to place build artifacts into (default %default)') parser.add_option('--boringssl-dir', metavar='D', default='boringssl', help='Set directory of BoringSSL checkout to use for BoGo tests') + parser.add_option('--ci-image', default=None, + help='Set the Github Actions CI image name') parser.add_option('--make-tool', metavar='TOOL', default=default_make_tool(), help='Specify tool to run to build source (default %default)') @@ -678,6 +686,25 @@ else: return make_tool, [] +# There is some unfortunate flakiness in the tls_proxy cli test that has +# yet to be debugged. Until this is resolved certain CI targets do not +# run this test. +def skip_tls_proxy_tests_for_this_target(ci_image): + # If we don't know what CI image we are on go ahead and run it + if ci_image is None: + return False + + # Occasionally fails - see GH #3845 #4178 #4181 + if ci_image == 'windows-2022': + return True + + # The tls_proxy test seems to consistently fail on certain macOS images + # See GH #5160 + if ci_image in ['macos-15-intel', 'macos-26']: + return True + + return False + def main(args=None): """ Parse options, do the things @@ -707,12 +734,17 @@ if options.cc_bin is None: if options.cc == 'gcc': options.cc_bin = 'g++' + elif options.cc == 'gcc-14': + options.cc = 'gcc' # Hack: 'gcc-14' is not a valid compiler identifier for ``./configure.py --cc`` + options.cc_bin = 'g++-14' elif options.cc == 'clang': options.cc_bin = 'clang++' elif options.cc == 'xcode': options.cc_bin = 'clang++' elif options.cc == 'msvc': options.cc_bin = 'cl' + elif options.cc == 'clangcl': + options.cc_bin = 'clang-cl' elif options.cc == "emcc": options.cc_bin = "em++" else: @@ -750,6 +782,7 @@ pylint_rc = '--rcfile=%s' % (os.path.join(root_dir, 'src/configs/pylint.rc')) pylint_flags = [pylint_rc, '--reports=no'] + pylint_flags += ['--ignored-modules=gdb'] # 'import gdb' is not available outside gdb... if is_running_in_github_actions(): pylint_flags += ["--msg-template='::warning file={path},line={line},endLine={end_line}::Pylint ({category}): {msg_id} {msg} ({symbol})'"] @@ -757,34 +790,52 @@ py_scripts = [ 'configure.py', 'src/python/botan3.py', + 'src/scripts/acvp_tests.py', 'src/scripts/ci_build.py', 'src/scripts/install.py', + 'src/scripts/ci_check_generated_files.py', 'src/scripts/ci_check_headers.py', 'src/scripts/ci_check_install.py', 'src/scripts/dist.py', 'src/scripts/cleanup.py', 'src/scripts/check.py', + 'src/scripts/compare_perf.py', 'src/scripts/build_docs.py', 'src/scripts/website.py', 'src/scripts/bench.py', 'src/scripts/test_python.py', + 'src/scripts/test_strubbed_symbols.py', 'src/scripts/test_fuzzers.py', 'src/scripts/test_cli.py', 'src/scripts/repo_config.py', + 'src/scripts/wycheproof.py', 'src/scripts/python_unittests.py', 'src/scripts/python_unittests_unix.py', 'src/scripts/dev_tools/run_clang_format.py', 'src/scripts/dev_tools/run_clang_tidy.py', + 'src/scripts/gdb/strubtest.py', 'src/editors/vscode/scripts/bogo.py', 'src/editors/vscode/scripts/common.py', 'src/editors/vscode/scripts/test.py', - 'src/ct_selftest/ct_selftest.py'] + 'src/ct_selftest/ct_selftest.py' + ] - # This has to run in the repository root to generate the correct + # These commands have to run in the repository root to generate the correct # relative paths in the output. Otherwise GitHub Actions will not # be able to annotate the correct files. cmds.append(["indir:%s" % root_dir, py_interp, '-m', 'pylint'] + pylint_flags + py_scripts) + ruff_flags = [] + if is_running_in_github_actions(): + ruff_flags += ["--output-format=github"] + + cmds.append(["indir:%s" % (root_dir), "ruff", "check"] + ruff_flags + ["."]) + + cmds.append(["indir:%s" % (root_dir), py_interp, + os.path.join(root_dir, 'src/scripts/ci_check_generated_files.py')]) + + elif target == 'typos': + cmds.append(['indir:%s' % (root_dir), 'typos', '-c', 'src/configs/typos.toml', '.']) elif target == 'format': cmds.append([py_interp, os.path.join(root_dir, 'src/scripts/dev_tools/run_clang_format.py'), @@ -795,7 +846,7 @@ if options.test_results_dir: os.makedirs(options.test_results_dir) - config_flags, pretest_cmd, run_test_command, make_prefix = determine_flags( + config_flags, run_test_command, make_prefix = determine_flags( target, options.os, options.cpu, options.cc, options.cc_bin, options.compiler_cache, root_dir, build_dir, options.test_results_dir, options.pkcs11_lib, options.use_gdb, options.disable_werror, @@ -819,42 +870,90 @@ if options.compiler_cache is not None: cmds.append([options.compiler_cache, '--show-stats']) - make_targets = ['libs', 'tests', 'cli'] - - if target in ['coverage', 'fuzzers']: - make_targets += ['fuzzer_corpus_zip', 'fuzzers'] - - if target in ['examples', 'amalgamation']: - make_targets += ['examples'] - - if target in ['valgrind', 'valgrind-full', 'valgrind-ct', 'valgrind-ct-full']: - make_targets += ['ct_selftest'] - - if target in ['coverage', 'sanitizer'] and options.os not in ['windows']: - make_targets += ['bogo_shim'] + cmds.append(make_prefix + make_cmd + make_targets(target, options.os)) - cmds.append(make_prefix + make_cmd + make_targets) + if target in ['examples'] and options.cc in ['clang', 'gcc']: + cmds.append([options.cc, '-Wall', '-Wextra', '-std=c89', + '-I%s' % (os.path.join(build_dir, 'build/include/public')), + os.path.join(root_dir, 'src/examples/ffi.c'), + '-L%s' % (build_dir), '-lbotan-3', '-o', + os.path.join(build_dir, 'build/examples/ffi')]) if options.compiler_cache is not None: cmds.append([options.compiler_cache, '--show-stats']) - if pretest_cmd is not None: - cmds.append(pretest_cmd) - if run_test_command is not None: cmds.append(run_test_command) - if target in ['coverage', 'sanitizer'] and options.os != 'windows': + if target in ['valgrind', 'valgrind-full', 'valgrind-ct', 'valgrind-ct-full']: + + build_config = os.path.join(build_dir, 'build', 'build_config.json') + cmds.append([os.path.join(root_dir, 'src', 'ct_selftest', 'ct_selftest.py'), + "--build-config-path=%s" % build_config, + os.path.join(build_dir, 'botan_ct_selftest')]) + + valgrind_script_options = ['--test-binary=%s' % (os.path.join(build_dir, 'botan-test')), + '--verbose', + '--bunch', + '--track-origins'] + + # For finding memory bugs, we're enabling more features that add runtime + # overhead which we don't need for the secret-dependent execution checks + # that 'valgrind-ct' and 'valgrind-ct-full' are aiming for. + if target not in ['valgrind-ct', 'valgrind-ct-full']: + valgrind_script_options.append('--with-leak-check') + + if target not in ['valgrind-full', 'valgrind-ct-full']: + # valgrind is slow, so some tests only run in the nightly check + slow_tests = [ + 'argon2', 'bcrypt', 'bcrypt_pbkdf', 'compression_tests', 'cryptobox', + 'dh_invalid', 'dh_kat', 'dh_keygen', 'dl_group_gen', 'dlies', + 'dsa_kat_verify', 'dsa_param', 'ecc_basemul', 'ecdsa_verify_wycheproof', + 'ed25519_sign', 'elgamal_decrypt', 'elgamal_encrypt', 'elgamal_keygen', + 'ffi_dh', 'ffi_dsa', 'ffi_elgamal', 'frodo_kat_tests', 'hash_nist_mc', + 'hss_lms_keygen', 'hss_lms_sign', 'mce_keygen', 'passhash9', 'pbkdf', + 'pcurves_arith', 'pwdhash', 'rsa_encrypt', 'rsa_pss', 'rsa_pss_raw', 'scrypt', + 'sphincsplus', 'sphincsplus_fors', 'slh_dsa_keygen', 'slh_dsa', 'srp6_kat', + 'srp6_rt', 'unit_tls', 'x509_path_bsi', 'x509_path_rsa_pss', + 'xmss_keygen', 'xmss_keygen_reference', 'xmss_sign', 'xmss_unit_tests', + 'xmss_verify', 'xmss_verify_invalid', + ] + slow_tests += [f"dilithium_kat_{mode}_{rand}" for mode in ('6x5', '8x7', '6x5_AES', '8x7_AES') for rand in ('Deterministic', 'Randomized')] + slow_tests += [f"ml_dsa_kat_{mode}_{rand}" for mode in ('6x5', '8x7') for rand in ('Deterministic', 'Randomized')] + + valgrind_script_options.append('--skip-tests=%s' % (','.join(slow_tests))) + elif target == 'valgrind-ct-full' and options.cc == 'clang' and '-Os' in options.custom_optimization_flags: + # Clang 18 (only) with -Os seems to have a problem with std::optional which flags certain + # uses as touching an uninitialized stack variable. This affects the x509_rpki tests + # TODO(26.04) We can remove this once we have a new version of Clang to use + valgrind_script_options.append('--skip-tests=x509_rpki') + + cmds.append(['indir:%s' % (root_dir), + 'src/scripts/run_tests_under_valgrind.py'] + + valgrind_script_options) + + if target in ['coverage', 'sanitizer', 'no_tls12', 'no_tls13'] and options.os != 'windows': if not options.boringssl_dir: - raise Exception('coverage build needs --boringssl-dir') + raise Exception('%s build needs --boringssl-dir' % (target)) runner_dir = os.path.abspath(os.path.join(options.boringssl_dir, 'ssl', 'test', 'runner')) + if target == 'no_tls12': + shim_config = os.path.abspath(os.path.join(root_dir, 'src', 'bogo_shim', 'config_no_tls12.json')) + extra_args = ['-skip-tls12', '-skip-dtls'] + elif target == 'no_tls13': + shim_config = os.path.abspath(os.path.join(root_dir, 'src', 'bogo_shim', 'config_no_tls13.json')) + extra_args = ['-skip-tls13'] + else: + shim_config = os.path.abspath(os.path.join(root_dir, 'src', 'bogo_shim', 'config.json')) + extra_args = [] + cmds.append(['indir:%s' % (runner_dir), 'go', 'test', '-pipe', + '-allow-unimplemented', '-num-workers', str(4*get_concurrency()), '-shim-path', os.path.abspath(os.path.join(build_dir, 'botan_bogo_shim')), - '-shim-config', os.path.abspath(os.path.join(root_dir, 'src', 'bogo_shim', 'config.json'))]) + '-shim-config', shim_config] + extra_args) if target in ['limbo']: cmds.append([py_interp, os.path.join(root_dir, 'src/scripts/run_limbo_tests.py'), @@ -869,15 +968,26 @@ botan_exe = os.path.join(build_dir, 'botan-cli.exe' if options.os == 'windows' else 'botan') args = ['--threads=%d' % (options.build_jobs)] - if target in ['coverage']: + if target in ['shared']: + # Ideally we'd run these in the coverage build but with coverage some + # of them run incredibly slowly, eg cli_xmss_sign_tests takes 10+ minutes args.append('--run-slow-tests') if root_dir != '.': args.append('--test-data-dir=%s' % root_dir) + test_scripts = ['test_cli.py', 'test_cli_crypt.py'] + for script in test_scripts: - test_data_arg = [] - cmds.append([py_interp, os.path.join(root_dir, 'src/scripts', script)] + - args + test_data_arg + [botan_exe]) + script_path = os.path.join(root_dir, 'src/scripts', script) + extra_args = [] + if script == 'test_cli.py' and skip_tls_proxy_tests_for_this_target(options.ci_image): + extra_args.append('--skip-tls-proxy-test') + + cmds.append([py_interp, script_path] + args + extra_args + [botan_exe]) + + if target in ['strubbing']: + cmds.append([py_interp, os.path.join(root_dir, 'src/scripts/test_strubbed_symbols.py'), + '--botan-cli', os.path.join(build_dir, 'botan')]) if target in ['hybrid-tls13-interop-test']: cmds.append([py_interp, os.path.join(root_dir, 'src/scripts/test_cli.py'), @@ -894,11 +1004,20 @@ if target in ['shared', 'coverage'] and not (options.os == 'windows' and options.cpu == 'x86'): cmds.append([py_interp, '-b'] + python_tests) + if target in ['wycheproof']: + wycheproof_test_script = os.path.join(root_dir, 'src/scripts/wycheproof.py') + cmds.append([py_interp, wycheproof_test_script]) + + if target in ['acvp']: + acvp_test_script = os.path.join(root_dir, 'src/scripts/acvp_tests.py') + cmds.append([py_interp, acvp_test_script]) + if target in ['shared', 'static']: cmds.append(make_cmd + ['install']) build_config = os.path.join(build_dir, 'build', 'build_config.json') cmds.append([py_interp, os.path.join(root_dir, 'src/scripts/ci_check_install.py'), build_config]) cmds.append([py_interp, os.path.join(root_dir, 'src/scripts/ci_check_headers.py'), build_config]) + cmds.append([py_interp, os.path.join(root_dir, 'src/scripts/ci_report_sizes.py'), build_config]) if target in ['coverage']: if have_prog('coverage'): @@ -917,7 +1036,7 @@ if have_prog('coveralls'): # If coveralls command exists, assume we are in CI and report to coveralls.io - cmds.append(['coveralls', '--format=lcov', '--file=%s' % (cov_file)]) + cmds.append(['coveralls', '--no-fail', '--format=lcov', '--file=%s' % (cov_file)]) else: # Otherwise generate a local HTML report cmds.append(['genhtml', cov_file, '--output-directory', os.path.join(build_dir, 'lcov-out')]) @@ -925,13 +1044,12 @@ cmds.append(make_cmd + ['clean']) cmds.append(make_cmd + ['distclean']) - # start ESDM in background, if on Linux - if target in ['shared', 'coverage', 'sanitizer'] and platform.system() == "Linux": + esdm_process = None + # start ESDM in background if needed + if target in ['coverage', 'optional-rngs']: print('Starting esdm-server for this target') esdm_process = subprocess.Popen('sudo /usr/bin/esdm-server -f', shell=True) assert esdm_process.poll() is None, f"esdm-server did not start for target {target}" - else: - print('Not starting esdm-server for this target') for cmd in cmds: if options.dry_run: @@ -939,7 +1057,7 @@ else: run_cmd(cmd, root_dir, build_dir) - if target in ['shared', 'coverage', 'sanitizer'] and platform.system() == "Linux": + if esdm_process: print('Stopping esdm-server') esdm_process.kill() diff -Nru botan3-3.7.1+dfsg/src/scripts/ci_check_generated_files.py botan3-3.12.0+dfsg/src/scripts/ci_check_generated_files.py --- botan3-3.7.1+dfsg/src/scripts/ci_check_generated_files.py 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/ci_check_generated_files.py 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +# coding=utf8 + +""" +(C) 2026 Jack Lloyd + +Botan is released under the Simplified BSD License (see license.txt) +""" + +import argparse +import os +import re +import subprocess +import sys + +# Stanza must appear near the start of the file, and must include a date. +GENERATED_RE = re.compile( + r'This file was automatically generated by\b.*?\bon (\d{4}-\d{2}-\d{2})', + re.DOTALL) + +# Only scan the head of the file; the stanza always lives in the opening +# comment block. +SCAN_BYTES = 512 + + +def run_git(*args): + return subprocess.check_output(['git'] + list(args), text=True) + + +def generation_date(content): + """Return the YYYY-MM-DD stamp from the stanza, or None if no stanza.""" + m = GENERATED_RE.search(content[:SCAN_BYTES]) + return m.group(1) if m else None + + +def file_at_ref(ref, path): + """Return the text contents of :, or None if missing.""" + try: + return subprocess.check_output( + ['git', 'show', f'{ref}:{path}'], + text=True, stderr=subprocess.DEVNULL, + errors='replace') + except subprocess.CalledProcessError: + return None + + +def file_in_working_tree(path): + try: + with open(path, encoding='utf8', errors='replace') as f: + return f.read(SCAN_BYTES) + except OSError: + return None + + +def changed_files(base): + """Files changed between base...HEAD plus any uncommitted changes.""" + out = set() + out.update(run_git('diff', '--name-only', f'{base}...HEAD').split()) + out.update(run_git('diff', '--name-only', 'HEAD').split()) + out.discard('') + return sorted(out) + + +def default_base(): + # On GitHub Actions during a pull_request event, the target branch name + # is in GITHUB_BASE_REF (e.g. "master"). The remote ref is "origin/". + github_base = os.environ.get('GITHUB_BASE_REF') + if github_base: + return f'origin/{github_base}' + return 'origin/master' + + +def main(argv=None): + parser = argparse.ArgumentParser(description="Check that generated files are not modified.") + parser.add_argument( + '--base', default=default_base(), + help='git ref to diff against (default: %(default)s)') + args = parser.parse_args(argv) + + try: + run_git('rev-parse', '--verify', args.base) + except subprocess.CalledProcessError: + # If the base isn't present (shallow clone, running on master + # itself, non-PR workflow, ...) there's nothing to compare against. + print(f'skipping: base ref {args.base!r} not available', file=sys.stderr) + return 0 + + flagged = [] + for path in changed_files(args.base): + print("Checking '%s'..." % (path)) + working = file_in_working_tree(path) + if working is None: + continue # deleted + + new_date = generation_date(working) + if new_date is None: + continue # not a generated file + + base_content = file_at_ref(args.base, path) + if base_content is None: + continue # newly added file; nothing to compare against + + old_date = generation_date(base_content) + if old_date is None: + continue + + if old_date == new_date: + flagged.append((path, old_date)) + + if flagged: + print('ERROR: generated file(s) were modified without updating the generation date.') + print('These files are regenerated by a gen_*.py script; edit the script and regenerate') + print('rather than editing the file directly.') + print() + for path, date in flagged: + print(f' {path} (date unchanged: {date})') + return 1 + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff -Nru botan3-3.7.1+dfsg/src/scripts/ci_check_install.py botan3-3.12.0+dfsg/src/scripts/ci_check_install.py --- botan3-3.7.1+dfsg/src/scripts/ci_check_install.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/ci_check_install.py 2026-05-07 01:38:28.000000000 +0000 @@ -27,7 +27,7 @@ major_version = int(build_config["version_major"]) - if build_config['compiler'] == 'msvc': + if build_config['compiler'] in ['msvc', 'clangcl']: expected_lib_format = r'^botan-%d\.(dll|lib)$' % (major_version) elif build_config['os'] == 'macos': expected_lib_format = r'^libbotan-%d\.(a|dylib)$' % (major_version) diff -Nru botan3-3.7.1+dfsg/src/scripts/ci_report_sizes.py botan3-3.12.0+dfsg/src/scripts/ci_report_sizes.py --- botan3-3.7.1+dfsg/src/scripts/ci_report_sizes.py 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/ci_report_sizes.py 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# coding=utf8 + +""" +This script reports the sizes of various binary artifacts in CI + +(C) 2025 Jack Lloyd + +Botan is released under the Simplified BSD License (see license.txt) +""" + +import os +import sys +import json + +def format_size(bytes): + kB = 1024 + + if bytes > kB: + return "%.02f kB" % (bytes / kB) + else: + return "%d bytes" % (bytes) + +def report_size(fsname): + if not os.access(fsname, os.R_OK): + print("ERROR: Could not find %s" % (fsname)) + return + bytes = os.stat(fsname).st_size + print("File '%s' is %s" % (fsname, format_size(bytes))) + +def main(args = None): + if args is None: + args = sys.argv + + if len(args) != 2: + print("Usage: %s " % (args[0])) + return 1 + + build_config = json.loads(open(args[1]).read()) + + for lib in build_config['library_targets'].split(' '): + report_size(lib) + + if 'text_exe' in build_config: + report_size(build_config['test_exe']) + + if 'cli_exe' in build_config: + report_size(build_config['cli_exe']) + +if __name__ == '__main__': + sys.exit(main()) diff -Nru botan3-3.7.1+dfsg/src/scripts/compare_perf.py botan3-3.12.0+dfsg/src/scripts/compare_perf.py --- botan3-3.7.1+dfsg/src/scripts/compare_perf.py 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/compare_perf.py 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 + +""" +(C) 2025 Jack Lloyd +Botan is released under the Simplified BSD License (see license.txt) + +Compare two JSON files output by `botan speed --format=json` and report +on noticeable improvements or regressions in performance. +""" + +import json +import optparse # pylint: disable=deprecated-module +import sys +import re + +def ops_per_second(events, nanos): + return (events * 1000000000) / nanos + +def format_pct(r): + #assert r > 1 + return "%.01f%%" % (abs(r - 1) * 100) + +def parse_perf_report(report): + if len(report) == 0: + print("No report data") + return None + + version = {'version': 'unknown', 'git': 'unknown'} + if 'version' in report[0]: + version = report[0] + + if 'git' in version and version['git'] != 'unknown': + version['git'] = version['git'][:12] + + report = report[1:] + + re_with_suffix = re.compile(r'(.*) \[[a-z0-9_]+\]$') + + results = [] + for t in report: + if 'algo' in t and 'op' in t and 'events' in t and 'nanos' in t: + + algo = t['algo'] + match = re_with_suffix.match(algo) + if match: + algo = match.group(1) + op = t['op'] + if 'buf_size' in t: + op += ' ' + str(t['buf_size']) + ' buffer' + + results.append(((algo, op), ops_per_second(t['events'], t['nanos']))) + else: + print("Unexpected record", t) + + results = sorted(results, key=lambda r: r[0]) + return (version, results) + +def read_src(src): + if src in ['stdin', '-']: + return sys.stdin.read() + else: + return open(src, encoding='utf8').read() + +def main(args = None): + if args is None: + args = sys.argv + + usage = "usage: %prog [options] base.json compare.json" + parser = optparse.OptionParser(usage=usage) + + parser.add_option('--limit', default=3, help="set reporting limit (as percent)") + parser.add_option('--filter', default='.*', help="filter results by regex") + + (options, args) = parser.parse_args(args) + + if len(args) != 3: + print("Usage: compare_perf.py orig.json new.json") + return 1 + + src1 = args[1] + src2 = args[2] + + if src1 == src2: + print("Doesn't make sense to compare the same inputs") + return 1 + + (ver0, rep0) = parse_perf_report(json.loads(read_src(src1))) + (ver1, rep1) = parse_perf_report(json.loads(read_src(src2))) + + reportable = float(options.limit) / 100 + filter_re = re.compile(options.filter) + + def diff(k, a, b): + return k in a and k in b and a[k] != b[k] and (a[k] != "unknown" or b[k] != "unknown") + + if ver0 and ver1: + s = "Diff between " + + diff_version = diff('version', ver0, ver1) + diff_git = diff('git', ver0, ver1) + + # TODO check/diff the compiler flags + + s += src1 + + if diff_version or diff_git: + s += " (" + if diff_version and diff_git: + s += ver0['version'] + " " + ver0['git'] + elif diff_version: + s += ver0['version'] + elif diff_git: + s += ver0['git'] + s += ")" + + s += " and " + src2 + + if diff_version or diff_git: + s += " (" + if diff_version and diff_git: + s += ver1['version'] + " " + ver1['git'] + elif diff_version: + s += ver1['version'] + elif diff_git: + s += ver1['git'] + s += ")" + + s += "\n" + + print(s) + + data_points = 0 + speedups = [] + slowdowns = [] + missing = 0 + + while rep0 != [] and rep1 != []: + while rep0 != [] and rep1 != [] and rep0[0][0] != rep1[0][0]: + missing += 1 + if rep0[0][0] < rep1[0][0]: + rep0 = rep0[1:] + else: + rep1 = rep1[1:] + + if rep0 != [] and rep1 != []: + assert rep0[0][0] == rep1[0][0] + algo = ' '.join(rep0[0][0]) + + if filter_re.search(algo) is not None: + orig = rep0[0][1] + new = rep1[0][1] + + ratio = new / orig + + data_points += 1 + + if ratio >= 1 + reportable: + speedups.append((ratio, algo)) + elif (orig / new) >= 1 + reportable: + slowdowns.append((orig / new, algo)) + + # go to next + rep0 = rep0[1:] + rep1 = rep1[1:] + + for (pct, algo) in sorted(speedups, key=lambda v: v[0], reverse=True): + print("+ %s improvement in %s" % (format_pct(pct), algo)) + + for (pct, algo) in sorted(slowdowns, key=lambda v: v[0]): + print("- %s regression in %s" % (format_pct(pct), algo)) + + if data_points > 0: + print("\nSummary: over %d tests saw %d speedups and %d slowdowns" % (data_points, len(speedups), len(slowdowns))) + else: + print("\nNo data points") + + if missing > 0: + print("NOTE: there were %d data points in one set but not the other" % (missing)) + +if __name__ == '__main__': + sys.exit(main()) diff -Nru botan3-3.7.1+dfsg/src/scripts/dev_tools/addchain.py botan3-3.12.0+dfsg/src/scripts/dev_tools/addchain.py --- botan3-3.7.1+dfsg/src/scripts/dev_tools/addchain.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/dev_tools/addchain.py 2026-05-07 01:38:28.000000000 +0000 @@ -30,46 +30,62 @@ return stdout.decode('utf8').split('\n') -if len(sys.argv) != 2: - print("Usage: addchain.py n") - sys.exit(1) - -n = int(sys.argv[1], 0) - -vars = set([]) - -for line in addchain_gen(n): - if line == '': - continue - c = line.strip().split() - - if c[0] == 'tmp': - continue - - decl = '' if c[1] in vars else 'auto ' - - if c[0] == 'double': - assert(len(c) == 3) - print(" %s%s = %s.square();" % (decl, c[1], c[2])) - vars.add(c[1]) - elif c[0] == 'add': - assert(len(c) == 4) - if c[1] == c[2]: - print(" %s *= %s;" % (c[1], c[3])) - elif c[1] == c[3]: - print(" %s *= %s;" % (c[1], c[2])) - else: - if c[2] < c[3]: - print(" %s%s = %s * %s;" % (decl, c[1], c[2], c[3])) +def addchain_code(n, indent=3): + vars = set([]) + + output = [] + + for line in addchain_gen(n): + if line == '': + continue + c = line.strip().split() + + if c[0] == 'tmp': + continue + + decl = '' if c[1] in vars else 'auto ' + + if c[0] == 'double': + assert(len(c) == 3) + output.append("%s%s = %s.square()" % (decl, c[1], c[2])) + vars.add(c[1]) + elif c[0] == 'add': + assert(len(c) == 4) + if c[1] == c[2]: + output.append("%s *= %s" % (c[1], c[3])) + elif c[1] == c[3]: + output.append("%s *= %s" % (c[1], c[2])) else: - print(" %s%s = %s * %s;" % (decl, c[1], c[3], c[2])) - vars.add(c[1]) - elif c[0] == 'shift': - - assert(len(c) == 4) - if c[1] != c[2]: - print(" %s%s = %s;" % (decl, c[1], c[2])) - print(" %s.square_n(%s);" % (c[1], c[3])) - vars.add(c[1]) - else: - print("UNKNOWN", c[0]) + if c[2] < c[3]: + output.append("%s%s = %s * %s" % (decl, c[1], c[2], c[3])) + else: + output.append("%s%s = %s * %s" % (decl, c[1], c[3], c[2])) + vars.add(c[1]) + elif c[0] == 'shift': + + assert(len(c) == 4) + if c[1] != c[2]: + output.append("%s%s = %s" % (decl, c[1], c[2])) + output.append("%s.square_n(%s)" % (c[1], c[3])) + vars.add(c[1]) + else: + raise Exception("Don't know what to do with %s" % (c[0])) + + output.append('return z') + + ws = " " * indent + return '\n'.join(['%s%s;' % (ws, line) for line in output]) + +def main(args = None): + if args is None: + args = sys.argv + if len(args) != 2: + print("Usage: %s " % (args[0])) + return 1 + + n = int(args[1], 0) + print(addchain_code(n)) + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff -Nru botan3-3.7.1+dfsg/src/scripts/dev_tools/analyze_timing_results.py botan3-3.12.0+dfsg/src/scripts/dev_tools/analyze_timing_results.py --- botan3-3.7.1+dfsg/src/scripts/dev_tools/analyze_timing_results.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/dev_tools/analyze_timing_results.py 2026-05-07 01:38:28.000000000 +0000 @@ -41,7 +41,7 @@ if match is None: print("Failed to match on '%s'" % (line)) - cnt = int(match.group(1)) + #cnt = int(match.group(1)) id = int(match.group(2)) time = int(match.group(3)) diff -Nru botan3-3.7.1+dfsg/src/scripts/dev_tools/file_size_check.py botan3-3.12.0+dfsg/src/scripts/dev_tools/file_size_check.py --- botan3-3.7.1+dfsg/src/scripts/dev_tools/file_size_check.py 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/dev_tools/file_size_check.py 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,62 @@ +#!/usr/bin/python + +import json +import subprocess +import re +import sys +from multiprocessing.pool import ThreadPool + +def lines_in(f): + lines = 0 + for line in f.decode('utf8').splitlines(): + if line == '': + continue + + if line.startswith('#'): + continue + lines += 1 + return lines + +def run_cc(cmd): + preproc = subprocess.run(cmd.split(' '), stdout=subprocess.PIPE) + + return lines_in(preproc.stdout) + +def main(): + search_for = None + + if len(sys.argv) == 2: + search_for = re.compile(sys.argv[1]) + + cc = json.loads(open('build/compile_commands.json').read()) + + src_file = re.compile('-E src/.*/([a-z0-9/_]+.cpp) ') + + pool = ThreadPool(8) + + total_lines = 0 + futures = [] + for c in cc: + cmd = c['command'].replace(' -c ', ' -E ').split(' -o')[0] + " -o -" + file_name = src_file.search(cmd) + if file_name is None: + continue + + file_name = file_name.group(1) + if search_for is not None and search_for.search(file_name) is None: + continue + + futures.append((file_name, pool.apply_async(run_cc, (cmd, )))) + + for (file_name, future) in futures: + lines = future.get() + total_lines += lines + print(lines, file_name) + sys.stdout.flush() + + print(total_lines, "total") + + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff -Nru botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_dilithium_kat.py botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_dilithium_kat.py --- botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_dilithium_kat.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_dilithium_kat.py 2026-05-07 01:38:28.000000000 +0000 @@ -4,7 +4,7 @@ # Strips the KAT harness produced by the Dilithium reference implementation down # to a less space consuming version. This script was used to generate # `src/tests/data/pubkey/dilithium_[...].vec` test data from the *.rsp files of -# the reference implemenation. +# the reference implementation. # # (C) 2022,2023 Jack Lloyd # (C) 2022 René Meusel, Rohde & Schwarz Cybersecurity @@ -41,7 +41,7 @@ while True: key, val = self.next_value() - if key == None: + if key is None: return # eof if key not in ['count', 'seed', 'mlen', 'msg', 'pk', 'sk', 'smlen', 'sm']: diff -Nru botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_ec_groups.py botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_ec_groups.py --- botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_ec_groups.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_ec_groups.py 2026-05-07 01:38:28.000000000 +0000 @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ -(C) 2021 Jack Lloyd +(C) 2021,2025 Jack Lloyd Botan is released under the Simplified BSD License (see license.txt) """ @@ -9,9 +9,29 @@ import sys import re import datetime +import os +import errno +from textwrap import dedent, indent +from jinja2 import Environment, FileSystemLoader +from addchain import addchain_code + +""" +NOTE: This script requires the Jinja templating library to be installed. + +This script generates the following files + +src/lib/pubkey/ec_group/ec_named.cpp +src/lib/math/pcurves/pcurves_instance.h +src/lib/math/pcurves/pcurves.cpp + +Additionally if a group is given in ec_groups.txt with an `Impl` that contains +"pcurves", and no pcurves implementation exists on disk, a default version will be +created. This step requires that addchain (https://github.com/mmcloughlin/addchain) +be installed. +""" def curve_info(src): - re_kv = re.compile('([A-Za-z]+) = ([0-9A-Za-z-_\. ]+)') + re_kv = re.compile('([A-Za-z]+) = ([0-9A-Za-z-_\\. ]+)') current = {} @@ -29,7 +49,7 @@ if key in ['Name']: current[key] = val - elif key in ['OID']: + elif key in ['OID', 'Impl']: current[key] = val.split(' ') elif key in ['A']: if val == '-3': @@ -40,73 +60,153 @@ current[key] = int(val, 16) if key == 'N': + current["N32"] = current["N"] & 0xFFFFFFFF + current["OIDExpr"] = ['OID{%s}' % (oid.replace('.', ', ')) for oid in current['OID']] yield current current = {} -def format_int(x): - if x.bit_length() <= 5: - return str(x) - return hex(x).upper().replace('0X', '0x') - -def print_curve(curve): - template_str = """ // %s - if(%s) { - return load_EC_group_info( - "%s", - "%s", - "%s", - "%s", - "%s", - "%s", - %s); - } -""" - - name = curve['Name'] - oids = ['OID{%s}' % (oid.replace('.', ', ')) for oid in curve['OID']] - p = format_int(curve['P']) - a = format_int(curve['A']) - b = format_int(curve['B']) - x = format_int(curve['X']) - y = format_int(curve['Y']) - n = format_int(curve['N']) - - oid_match = ' || '.join(['oid == %s' % oid for oid in oids]) - - pref_oid = 'oid' if len(oids) == 1 else oids[0] - - return template_str % (name, oid_match, p, a, b, x, y, n, pref_oid) - def format_names(names): - for nm in sorted(names): + # This would be quite complicated to render in the Jinja template language so + # we pre-render it as a string and insert it directly + legacy = [] + generic = [] + pcurves = [] + pcurves_no_generic = [] + + for (nm, impl) in names: + if 'pcurve' in impl: + pcurves.append(nm) + if 'generic' not in impl: + pcurves_no_generic.append(nm) + elif 'generic' in impl: + generic.append(nm) + else: + assert 'legacy' in impl + legacy.append(nm) + + legacy_macro = "defined(BOTAN_HAS_LEGACY_EC_POINT)" + generic_macro = "defined(BOTAN_HAS_PCURVES_GENERIC)" + for nm in sorted(pcurves): + nm_macro = "BOTAN_HAS_PCURVES_%s" % (nm.upper()) + + if nm in pcurves_no_generic: + yield "#if defined(%s) || %s" % (nm_macro, legacy_macro) + yield " // Not supported by pcurves_generic" + else: + yield "#if defined(%s) || %s || %s" % (nm_macro, legacy_macro, generic_macro) yield ' \"%s\",' % (nm) + yield "#endif\n" -def format_orders(orders): - template_str = """ if(low_bits == %s && order == BigInt("%s")) {\n return OID{%s};\n }\n"""; + yield "#if %s || %s" % (legacy_macro, generic_macro) + for nm in sorted(generic): + yield ' \"%s\",' % (nm) + yield "#endif\n" - orders_seen = set([]) + yield "#if %s" % (legacy_macro) + for nm in sorted(legacy): + yield ' \"%s\",' % (nm) + yield "#endif\n" - for (order,oid) in orders: - low_bits = hex(order & 0xFFFFFFFF).upper().replace('0X', '0x') - order = format_int(order) - if order in orders_seen: - raise Exception("Duplicate EC group order %s" % (order)) - orders_seen.add(order) - oid = oid[0].replace('.', ', ') - yield template_str % (low_bits, order, oid) +def datestamp(): + current_date = datetime.datetime.now() + return int(current_date.strftime("%Y%m%d")) + +class OmitFirstLine: + def __init__(self): + self.first_line = True + + def __call__(self, line): + r = not self.first_line + self.first_line = False + return r def main(): curves = [c for c in curve_info(open('./src/build-data/ec_groups.txt'))] - template_str = open('./src/build-data/ec_named.cpp.in').read() + pcurves = [] + for c in curves: + if 'pcurve' in c['Impl']: + pcurves.append(c) - names = "\n".join(format_names([c['Name'] for c in curves])) - orders = "\n".join(format_orders([(c['N'], c['OID']) for c in curves])) - curves = '\n'.join([print_curve(curve) for curve in curves]) this_script = sys.argv[0] - today = datetime.date.today().strftime("%Y-%m-%d") + date = datetime.date.today().strftime("%Y-%m-%d") + + env = Environment(loader=FileSystemLoader("src/build-data/templates")) + + # write ec_named.cpp + with open('./src/lib/pubkey/ec_group/ec_named.cpp', encoding='utf8', mode='w') as ec_named: + template = env.get_template("ec_named.cpp.in") + + named_groups = "\n".join(format_names([(c['Name'], c['Impl']) for c in curves])) + + ec_named.write(template.render(script=this_script, date=date, curves=curves, named_groups=named_groups.strip())) + ec_named.write("\n") + + # write pcurves_instance.h + with open('./src/lib/math/pcurves/pcurves_instance.h', encoding='utf8', mode='w') as pcurves_h: + template = env.get_template("pcurves_instance.h.in") + pcurves_h.write(template.render(script=this_script, date=date, pcurves=pcurves)) + pcurves_h.write("\n") + + # write pcurves.cpp + with open('./src/lib/math/pcurves/pcurves.cpp', encoding='utf8', mode='w') as pcurves_cpp: + template = env.get_template("pcurves.cpp.in") + pcurves_cpp.write(template.render(script=this_script, date=date, pcurves=pcurves)) + pcurves_cpp.write("\n") + + # Check if any pcurves modules need a new stub impl + for pcurve in pcurves: + curve = pcurve["Name"] + mod_dir = './src/lib/math/pcurves/pcurves_%s' % (curve) + info_path = os.path.join(mod_dir, 'info.txt') + impl_path = os.path.join(mod_dir, f'pcurves_{curve}.cpp') + + if os.access(impl_path, os.R_OK): + continue + + addchain_fe2 = addchain_code(pcurve['P'] - 3, 0) + addchain_fe_sqrt = addchain_code((pcurve['P'] + 1) // 4, 0) if pcurve['P'] % 4 == 3 else None + addchain_scalar = addchain_code(pcurve['N'] - 2, 0) + + try: + os.makedirs(mod_dir) + except OSError as ex: + if ex.errno != errno.EEXIST: + raise + + module_define = f"PCURVES_{curve.upper()}" + if not re.match('^[0-9A-Za-z_]{3,30}$', module_define): + raise ValueError(f"Invalid preprocessor define name ({module_define}) for new pcurve module") + + with open(info_path, 'w', encoding='utf8') as info_file: + info_file.write(dedent(f"""\ + + {module_define} -> {datestamp()} + + + + name -> "PCurve {curve}" + + + + pcurves_impl + + """)) + + with open(impl_path, 'w', encoding='utf8') as src_file: + crandall = (1 << pcurve["P"].bit_length()) - pcurve["P"] + if crandall > 2**32: + crandall = 0 + + template = env.get_template("pcurves_stub.cpp.in") + src_file.write(template.render(curve = pcurve, + crandall=crandall, + addchain_fe2=indent(addchain_fe2, 9 * ' ', OmitFirstLine()), + addchain_fe_sqrt=indent(addchain_fe_sqrt, 9 * ' ', OmitFirstLine()) if addchain_fe_sqrt else None, + addchain_scalar=indent(addchain_scalar, 9 * ' ', OmitFirstLine()))) + src_file.write("\n") + - print(template_str % (this_script, today, curves, orders, names), end='') return 0 if __name__ == '__main__': diff -Nru botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_frodo_kat.py botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_frodo_kat.py --- botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_frodo_kat.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_frodo_kat.py 2026-05-07 01:38:28.000000000 +0000 @@ -4,9 +4,9 @@ # Strips the KAT harness in the FrodoKEM reference implementation down # to a less space consuming version. This script was used to generate # `src/tests/data/pubkey/frodokem_kat.vec` test data from the *.rsp files in -# the reference implemenation repository. +# the reference implementation repository. # -# See here: https://github.com/microsoft/PQCrypto-LWEKE/tree/master/KAT +# See here: https://github.com/microsoft/PQCrypto-LWEKE/tree/master # # (C) 2023 Jack Lloyd # (C) 2023 René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity @@ -44,7 +44,7 @@ while True: key, val = self.next_value() - if key == None: + if key is None: return # eof if key not in ['count', 'seed', 'pk', 'sk', 'ct', 'ss']: @@ -66,8 +66,7 @@ return h.hexdigest(16) def compress_kat(kat): - first = kat['count'] == 0 - del kat['count'] + del kat['count'] # not needed # rename keys kat['Seed'] = kat.pop('seed') @@ -110,7 +109,7 @@ args = sys.argv with open('src/tests/data/pubkey/frodokem_kat.vec', 'w') as output: - print("# This file was auto-generated from the reference implemention's KATs", file=output) + print("# This file was auto-generated from the reference implementation's KATs", file=output) print("# See src/scripts/dev_tools/gen_frodo_kat.py\n", file=output) for file in args[1:]: diff -Nru botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_kyber_kat.py botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_kyber_kat.py --- botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_kyber_kat.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_kyber_kat.py 2026-05-07 01:38:28.000000000 +0000 @@ -57,7 +57,7 @@ while True: key, val = self.next_value() - if key == None: + if key is None: return # eof if key in ['msg']: @@ -88,8 +88,7 @@ return h.hexdigest()[:32] def compress_kat(kat, mode): - first = kat['count'] == 0 - del kat['count'] + del kat['count'] # Not needed hash_fn = sha256_16 if '90s' in mode else shake_256_16 @@ -200,7 +199,7 @@ if mode == "ML-KEM": print("# This file was auto-generated from github.com/post-quantum-cryptography/KAT", file=output) else: - print("# This file was auto-generated from the reference implemention's KATs", file=output) + print("# This file was auto-generated from the reference implementation's KATs", file=output) print("# See src/scripts/dev_tools/gen_kyber_kat.py\n", file=output) for file in args.files: diff -Nru botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_mlkem_acvp_kat.py botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_mlkem_acvp_kat.py --- botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_mlkem_acvp_kat.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_mlkem_acvp_kat.py 2026-05-07 01:38:28.000000000 +0000 @@ -80,7 +80,7 @@ qt['dk'] = qtg['dk'] decaps_kat += [qt] else: - print('ERROR: Unkonwn function:', func) + print('ERROR: Unknown function:', func) return (encaps_kat, decaps_kat) diff -Nru botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_mp_comba.py botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_mp_comba.py --- botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_mp_comba.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_mp_comba.py 2026-05-07 01:38:28.000000000 +0000 @@ -71,6 +71,7 @@ * Comba Multiplication and Squaring * * This file was automatically generated by %s on %s +* All manual changes will be lost. Edit the script instead. * * Botan is released under the Simplified BSD License (see license.txt) */ diff -Nru botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_mp_monty.py botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_mp_monty.py --- botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_mp_monty.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_mp_monty.py 2026-05-07 01:38:28.000000000 +0000 @@ -3,25 +3,40 @@ import sys import datetime -# (C) 2018,2023 Jack Lloyd +# (C) 2018,2023,2025 Jack Lloyd # Botan is released under the Simplified BSD License (see license.txt) # Used to generate src/lib/math/mp/mp_monty_n.cpp -def monty_redc_code(n): +def monty_redc_code(n, p_dash1=False): lines = [] + fn_name = "bigint_monty_redc_pdash1" if p_dash1 else "bigint_monty_redc" + + if p_dash1: + hdr = "void %s_%d(word r[%d], const word z[%d], const word p[%d], word ws[%d]) {\n" % (fn_name, n, n, 2*n, n, n) + else: + hdr = "void %s_%d(word r[%d], const word z[%d], const word p[%d], word p_dash, word ws[%d]) {\n" % (fn_name, n, n, 2*n, n, n) + + ftr = "\n}\n\n" + lines.append("word3 accum;") lines.append("accum.add(z[0]);") - lines.append("ws[0] = accum.monty_step(p[0], p_dash);") + if p_dash1: + lines.append("ws[0] = accum.monty_step_pdash1();") + else: + lines.append("ws[0] = accum.monty_step(p[0], p_dash);") for i in range(1, n): for j in range(0, i): lines.append("accum.mul(ws[%d], p[%d]);" % (j, i-j)) lines.append("accum.add(z[%d]);" % (i)) - lines.append("ws[%d] = accum.monty_step(p[0], p_dash);" % (i)) + if p_dash1: + lines.append("ws[%d] = accum.monty_step_pdash1();" % (i)) + else: + lines.append("ws[%d] = accum.monty_step(p[0], p_dash);" % (i)) for i in range(0, n - 1): for j in range(i + 1, n): @@ -30,28 +45,25 @@ lines.append("accum.add(z[%d]);" % (n+i)) lines.append("ws[%d] = accum.extract();" % (i)) - lines.append("accum.add(z[%d]);" % (2*n-1)); + lines.append("accum.add(z[%d]);" % (2*n-1)) lines.append("ws[%d] = accum.extract();" % (n - 1)) - lines.append("word w1 = accum.extract();") - - lines.append("bigint_monty_maybe_sub<%d>(z, w1, ws, p);" % (n)) + lines.append("const word w1 = accum.extract();") - lines.append("clear_mem(z + %d, %d);" % (n, n)) + lines.append("bigint_monty_maybe_sub<%d>(r, w1, ws, p);" % (n)) - for line in lines: - print(" %s" % (line)) + return hdr + "\n".join([" %s" % (line) for line in lines]) + ftr def main(args = None): if args is None: args = sys.argv if len(args) <= 1: - sizes = [4, 6, 8, 16, 24, 32] + sizes = [4, 6, 8, 12, 16, 24, 32] else: sizes = map(int, args[1:]) - print("""/* + header = """/* * This file was automatically generated by %s on %s * All manual changes will be lost. Edit the script instead. * @@ -60,19 +72,19 @@ #include -#include - namespace Botan { -""" % (sys.argv[0], datetime.date.today().strftime("%Y-%m-%d"))) - for n in sizes: - print("void bigint_monty_redc_%d(word z[%d], const word p[%d], word p_dash, word ws[]) {" % (n, 2*n, n)) +""" % (sys.argv[0], datetime.date.today().strftime("%Y-%m-%d")) + + footer = "} // namespace Botan\n" - monty_redc_code(n) + with open('./src/lib/math/mp/mp_monty_n.cpp', encoding='utf8', mode='w') as monty_n: + monty_n.write(header) - print("}\n") + for n in sizes: + monty_n.write(monty_redc_code(n)) - print("} // namespace Botan") + monty_n.write(footer) return 0 diff -Nru botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_oids.py botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_oids.py --- botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_oids.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_oids.py 2026-05-07 01:38:28.000000000 +0000 @@ -1,178 +1,91 @@ #!/usr/bin/env python3 """ -(C) 2016 Jack Lloyd +(C) 2016,2025 Jack Lloyd (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity Botan is released under the Simplified BSD License (see license.txt) + +NOTE: This script requires the Jinja templating library to be installed. """ import sys import datetime import re -from collections import defaultdict +from jinja2 import Environment, FileSystemLoader + +# This must match OID::hash_code +def hash_oid(oid): + word_size = 2 ** 64 + h = 0x621F302327D9A49A + + for part in map(int, oid.split('.')): + h = (h * 193) % word_size + h += part + + # This reduction step occurs in static_oids.cpp.in + return h % 858701 + +# This must match hash_oid_name in static_oids.cpp.in +def hash_oid_name(name): + word_size = 2 ** 64 + + h = 0x8188B31879A4879A + + for part in map(ord, name): + h = (h * 251) % word_size + h += part + + return h % 805289 def format_oid(oid): - #return '"' + oid + '"' - return "{" + oid.replace('.', ', ') + '}' + return '{' + oid.replace('.', ', ') + '}' -def format_map(m, for_oid = False): - s = '' - for k in sorted(m.keys()): - v = m[k] - - if len(s) > 0: - s += ' ' - - if for_oid: - s += '{"%s", OID(%s)},\n' % (k,format_oid(v)) - else: - s += '{OID(%s), "%s"},\n' % (format_oid(k),v) - - s = s[:-2] # chomp last two chars - - return s - - -def format_as_map(oid2str, str2oid): - return """/* -* OID maps -* -* This file was automatically generated by %s on %s -* -* All manual edits to this file will be lost. Edit the script -* then regenerate this source file. -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include - -namespace Botan { - -std::unordered_map OID_Map::load_oid2str_map() { - return std::unordered_map{ - - %s}; -} - -std::unordered_map OID_Map::load_str2oid_map() { - return std::unordered_map{ - - %s}; -} - -} // namespace Botan""" % ( - sys.argv[0], - datetime.date.today().strftime("%Y-%m-%d"), - format_map(oid2str), - format_map(str2oid, True)) - -def format_dn_ub_map(dn_ub, oid2str): - s = '' - for k in sorted(dn_ub.keys()): - v = dn_ub[k] - - expr = " {OID({%s}), %s}, " % (k.replace('.', ', '), v) - s += expr - s += ' '*(32 - len(expr)) - s += ' // %s\n' % (oid2str[k]) - - # delete last ',' and \n - idx = s.rfind(',') - if idx != -1: - s = s[:idx] + ' ' + s[idx+1:-1] - - return s - - -def format_dn_ub_as_map(dn_ub, oid2str): - return """/* -* DN_UB maps: Upper bounds on the length of DN strings -* -* This file was automatically generated by %s on %s -* -* All manual edits to this file will be lost. Edit the script -* then regenerate this source file. -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include - -namespace Botan { - -namespace { - -/** - * Upper bounds for the length of distinguished name fields as given in RFC 5280, Appendix A. - * Only OIDS recognized by botan are considered, so far. - * Maps OID string representations instead of human readable strings in order - * to avoid an additional lookup. - */ -const std::map DN_UB = { -%s -}; - -} // namespace - -//static -size_t X509_DN::lookup_ub(const OID& oid) { - auto ub_entry = DN_UB.find(oid); - if(ub_entry != DN_UB.end()) { - return ub_entry->second; - } else { - return 0; - } -} - -} // namespace Botan""" % (sys.argv[0], datetime.date.today().strftime("%Y-%m-%d"), - format_dn_ub_map(dn_ub,oid2str)) - - -def format_set_map(m): - s = '' - for k in sorted(m.keys()): - v = m[k] - - if len(s) > 0: - s += ' ' - - s += '{ "%s", {' % k - for pad in v: - s += '"%s", ' % pad - if len(v) != 0: - s = s[:-2] - s += '} },\n' - s = s[:-1] - return s +def render_static_oid(m): + res = [] + name_hashes = {} + oid_hashes = {} -def main(args = None): - """ Print header files (oids.cpp, dn_ub.cpp) depending on the first argument and on src/build-data/oids.txt + for (k, v) in m.items(): + + # Verify no collisions between any of the values + oid_hc = hash_oid(v) + if oid_hc in oid_hashes: + raise Exception("Hash collision between %s and %s" % (v, oid_hashes[oid_hc])) + oid_hashes[oid_hc] = v + + name_hc = hash_oid_name(k) + if name_hc in name_hashes: + raise Exception("Hash collision between %s and %s" % (k, name_hashes[name_hc])) + name_hashes[name_hc] = k + + res.append({ 'oid_hash': oid_hc, + 'name_hash': name_hc, + 'name': k, + 'oid': format_oid(v) }) - Choose 'oids' to print oids.cpp, needs to be written to src/lib/asn1/oids.cpp - Choose 'dn_ub' to print dn_ub.cpp, needs to be written to src/lib/x509/X509_dn_ub.cpp + return res + +def format_oid_with_name(m): + return [ {'name': kv[0], 'oid': format_oid(kv[1])} for kv in m ] + +def main(args = None): + """ + Regenerate src/lib/asn1/static_oids.cpp """ if args is None: args = sys.argv - if len(args) < 2: - print("Use either 'oids' or 'dn_ub' as first argument") - return 1 - oid_lines = open('./src/build-data/oids.txt').readlines() + oid_lines = open('./src/build-data/oids.txt', encoding='utf8').readlines() - oid_re = re.compile(r"^([0-9][0-9.]+) += +([A-Za-z0-9_\./\(\), -]+)(?: = )?([0-9]+)?$") + oid_re = re.compile(r"^([0-9][0-9.]+) += +([A-Za-z0-9_\./\(\), -]+)$") hdr_re = re.compile(r"^\[([a-z0-9_]+)\]$") oid2str = {} str2oid = {} - dn_ub = {} - cur_hdr = None + dup_oids = [] + aliases = [] for line in oid_lines: line = line.strip() @@ -184,7 +97,6 @@ match = hdr_re.match(line) if match is not None: - cur_hdr = match.group(1) continue match = oid_re.match(line) @@ -192,32 +104,29 @@ raise Exception(line) oid = match.group(1) - nam = match.group(2) + name = match.group(2) - if oid in str2oid: - print("Duplicated OID", oid, name, oid2str[oid]) - sys.exit() # hard error - else: - oid2str[oid] = nam - - # parse upper bounds for DNs - if cur_hdr == "dn": - if match.lastindex < 3: - raise Exception("Could not find an upper bound for DN " + match.group(1)) - dn_ub[oid] = match.group(3) - - if nam in str2oid: - #str2oid[nam] = oid - pass - else: - str2oid[nam] = oid - - if args[1] == "oids": - print(format_as_map(oid2str, str2oid)) - elif args[1] == "dn_ub": - print(format_dn_ub_as_map(dn_ub,oid2str)) - else: - print("Unknown command: try oids or dn_ub") + if name not in str2oid and oid not in oid2str: + str2oid[name] = oid + oid2str[oid] = name + elif name in str2oid: + dup_oids.append((name, oid)) + elif oid in oid2str: + aliases.append((name, oid)) + + this_script = sys.argv[0] + date = datetime.date.today().strftime("%Y-%m-%d") + + env = Environment(loader=FileSystemLoader("src/build-data/templates")) + + with open('./src/lib/asn1/static_oids.cpp', encoding='utf8', mode='w') as static_oids: + template = env.get_template("static_oids.cpp.in") + static_oids.write(template.render(script=this_script, + date=date, + static_oid_data=render_static_oid(str2oid), + dup_oids=format_oid_with_name(dup_oids), + aliases=format_oid_with_name(aliases))) + static_oids.write("\n") return 0 diff -Nru botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_os_features.py botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_os_features.py --- botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_os_features.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_os_features.py 2026-05-07 01:38:28.000000000 +0000 @@ -13,6 +13,7 @@ # global import argparse +import datetime import glob import os import sys @@ -22,7 +23,7 @@ # locale sys.path.append(botan_root) -from configure import OsInfo +from configure import OsInfo # noqa: E402 parser = argparse.ArgumentParser(description="") parser.add_argument('--verbose', dest='verbose', action='store_const', @@ -62,6 +63,11 @@ if args.verbose: print(featurelist) + today = datetime.date.today().strftime("%Y-%m-%d") + print(".. This file was automatically generated by src/scripts/dev_tools/%s on %s" + % (os.path.basename(sys.argv[0]), today), file=f1) + print(".. All manual changes will be lost. Edit the script instead.", file=f1) + print("", file=f1) print(TABLE_TITLE, file=f1) print("========================================", file=f1) print("", file=f1) @@ -86,10 +92,5 @@ line += 'X' if f in oss[o].target_features else ' ' line += '"' print(line, file=f1) - print("", file=f1) - print(".. note::", file=f1) - print(" This file is auto generated by ``src/scripts/%s``. Dont modify it manually." - % os.path.basename(sys.argv[0]), file=f1) - if __name__ == '__main__': update_os() diff -Nru botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_pqc_dsa_kats.py botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_pqc_dsa_kats.py --- botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_pqc_dsa_kats.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_pqc_dsa_kats.py 2026-05-07 01:38:28.000000000 +0000 @@ -112,7 +112,7 @@ hash_fn = sha3_256 def mldsa_sign_internal(m, sk, rnd): # For some reason the interfaces vary between FIPS 204 and FIPS 205... - if rnd == None: + if rnd is None: rnd = bytes([0]*32) return alg.sign_internal(sk, m, rnd) diff -Nru botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_sphincsplus_kat.py botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_sphincsplus_kat.py --- botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_sphincsplus_kat.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_sphincsplus_kat.py 2026-05-07 01:38:28.000000000 +0000 @@ -4,7 +4,7 @@ # Strips the KAT harness produced by the SPHINCS+ reference implementation down # to a less space consuming version. This script was used to generate # `src/tests/data/pubkey/sphincsplus.vec` test data from the *.rsp files of the -# reference implemenation. +# reference implementation. # # (C) 2023 Jack Lloyd # (C) 2023 René Meusel, Rohde & Schwarz Cybersecurity @@ -85,11 +85,11 @@ hash_fn = sha256 if "sha2" in param.lower() else sha3_256 - l = 0 + cnt = 0 for kat in reader.read_kats(): - if l >= limit: + if cnt >= limit: break - l += 1 + cnt += 1 # Remove the input message from the end of the 'sm' field signature = binascii.unhexlify(kat["sm"][:-kat["mlen"]*2]) diff -Nru botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_tls_suite_info.py botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_tls_suite_info.py --- botan3-3.7.1+dfsg/src/scripts/dev_tools/gen_tls_suite_info.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/dev_tools/gen_tls_suite_info.py 2026-05-07 01:38:28.000000000 +0000 @@ -14,6 +14,7 @@ import datetime import hashlib import optparse +from jinja2 import Environment, FileSystemLoader def to_ciphersuite_info(code, name): @@ -58,7 +59,7 @@ if mac_algo == '8' and cipher[-1] == 'CCM': cipher = cipher[:-1] mac_algo = 'CCM_8' - elif cipher[-2] == 'CCM' and cipher[-1] == '8': + elif len(cipher) >= 2 and cipher[-2] == 'CCM' and cipher[-1] == '8': cipher = cipher[:-1] mac_algo = 'CCM_8' @@ -78,6 +79,7 @@ 'AES': ('AES',None), 'SEED': ('SEED',16), 'ARIA': ('ARIA',None), + 'NULL': ('NULL', 0), } tls_to_botan_names = { @@ -137,6 +139,10 @@ if cipher_algo in ['AES', 'Camellia', 'ARIA']: cipher_algo += '-%d' % (cipher_keylen*8) + if cipher_algo == 'NULL': + # NULL cipher suite: HMAC-based MAC, no encryption + return (name, code, sig_algo, kex_algo, cipher_algo, cipher_keylen, mac_algo, mac_keylen[mac_algo], mac_algo, 'NULL_CIPHER') + mode = '' if cipher[0] == 'CHACHA20' and cipher[1] == 'POLY1305': @@ -168,7 +174,9 @@ except OSError: pass - import urllib.request, urllib.error, urllib.parse + import urllib.request + import urllib.error + import urllib.parse return urllib.request.urlopen(iana_url) else: return open(args[1]) @@ -207,7 +215,7 @@ static_dh = ['ECDH_ECDSA', 'ECDH_RSA', 'DH_DSS', 'DH_RSA'] # not supported removed_algos = ['SEED', 'CAMELLIA_128_CBC', 'CAMELLIA_256_CBC'] protocol_goop = ['SCSV', 'KRB5'] - maybe_someday = ['RSA_PSK', 'ECCPWD', 'AEGIS'] + maybe_someday = ['RSA_PSK', 'ECCPWD', 'AEGIS', 'ASCON'] macciphersuites = ['SHA256_SHA256', 'SHA384_SHA384'] shang_mi = ['SM4_GCM_SM3', 'SM4_CCM_SM3'] # RFC8998 not_supported = weak_crypto + static_dh + protocol_goop + maybe_someday + removed_algos + macciphersuites + shang_mi @@ -263,58 +271,128 @@ define_custom_ciphersuite('PSK_WITH_AES_256_OCB_SHA256', 'FFC7') define_custom_ciphersuite('ECDHE_PSK_WITH_AES_256_OCB_SHA256', 'FFCB') - suite_info = '' - - def header(): - return """/* -* TLS cipher suite information -* -* This file was automatically generated by %s on %s -* using the IANA assignments (tls-parameters.txt sha256 %s) -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -""" % (sys.argv[0], datetime.date.today().strftime("%Y-%m-%d"), contents_hash) - - suite_info += header() - - suite_info += """#include - -namespace Botan::TLS { - -//static -const std::vector& Ciphersuite::all_known_ciphersuites() { - // clang-format off + # NULL cipher suites (RFC9847). IANA lists more NULL suites, but the + # WITH_NULL substring above filters them all; we re-add the curated + # subset here so the generator stays in sync with the tls_null module. + define_custom_ciphersuite('PSK_WITH_NULL_SHA', '002C') + define_custom_ciphersuite('PSK_WITH_NULL_SHA256', '00B0') + define_custom_ciphersuite('PSK_WITH_NULL_SHA384', '00B1') + define_custom_ciphersuite('ECDHE_ECDSA_WITH_NULL_SHA', 'C006') + define_custom_ciphersuite('ECDHE_RSA_WITH_NULL_SHA', 'C010') + define_custom_ciphersuite('ECDHE_PSK_WITH_NULL_SHA', 'C039') + define_custom_ciphersuite('ECDHE_PSK_WITH_NULL_SHA256', 'C03A') + define_custom_ciphersuite('ECDHE_PSK_WITH_NULL_SHA384', 'C03B') + + def gates_for_suite(info): + """Return the list of BOTAN_HAS_* macros that must all be defined + for this suite to be considered usable. Order is preserved only + for readability of the emitted #if expression.""" + sig_algo = info[2] + kex_algo = info[3] + cipher_algo = info[4] + mac_algo = info[6] + prf_algo = info[8] + + feat = [] + + # Key exchange + if kex_algo in ('ECDH', 'ECDHE_PSK'): + feat.append('ECDH') + elif kex_algo == 'DH': + feat.append('DIFFIE_HELLMAN') + elif kex_algo == 'STATIC_RSA': + feat.append('RSA') + feat.append('PKCSV15_ENCRYPTION_PADDING') + + # Authentication + if sig_algo == 'ECDSA': + feat.append('ECDSA') + elif sig_algo == 'RSA': + feat.append('RSA') + + # Cipher + mode + if cipher_algo == 'NULL': + feat.append('TLS_NULL') + elif cipher_algo == 'ChaCha20Poly1305': + feat.append('AEAD_CHACHA20_POLY1305') + else: + if '/' in cipher_algo: + base, mode = cipher_algo.split('/', 1) + else: + base, mode = cipher_algo, 'CBC' + + if base.startswith('AES-'): + feat.append('AES') + elif base.startswith('ARIA-'): + feat.append('ARIA') + elif base.startswith('Camellia-'): + feat.append('CAMELLIA') + elif base == '3DES': + feat.append('DES') + + if mode == 'GCM': + feat.append('AEAD_GCM') + elif mode in ('CCM', 'CCM(8)'): + feat.append('AEAD_CCM') + elif mode.startswith('OCB'): + feat.append('AEAD_OCB') + elif mode == 'CBC': + feat.append('TLS_CBC') + + hash_macro = { + 'SHA-1': 'SHA1', + 'SHA-256': 'SHA2_32', + 'SHA-384': 'SHA2_64', + } + if mac_algo != 'AEAD': + feat.append(hash_macro[mac_algo]) + feat.append(hash_macro[prf_algo]) + + # Preserve order but drop duplicates + seen = set() + deduped = [] + for g in feat: + if g not in seen: + seen.add(g) + deduped.append('BOTAN_HAS_%s' % (g)) + return deduped - // Note that this list of ciphersuites is ordered by id! - static const std::vector g_ciphersuite_list = { -""" + rendered_suites = [] for code in sorted(suites.keys()): info = suites[code] assert len(info) == 10 - suite_expr = 'Ciphersuite(0x%s, "%s", Auth_Method::%s, Kex_Algo::%s, "%s", %d, "%s", %d, KDF_Algo::%s, Nonce_Format::%s)' % ( - code, info[0], info[2], info[3], info[4], info[5], info[6], info[7], info[8].replace('-','_'), info[9]) - - suite_info += " " + suite_expr + ",\n" - - suite_info += """ }; - - // clang-format on - - return g_ciphersuite_list; -} - -} // namespace Botan::TLS -""" + gates = gates_for_suite(info) + rendered_suites.append({ + 'code': code, + 'name': info[0], + 'sig_algo': info[2], + 'kex_algo': info[3], + 'cipher_algo': info[4], + 'cipher_keylen': int(info[5]), + 'mac_algo': info[6], + 'mac_keylen': info[7], + 'kdf_algo': info[8].replace('-', '_'), + 'nonce_format': info[9], + 'gates_expr': ' && '.join('defined(%s)' % g for g in gates), + }) + + env = Environment(loader=FileSystemLoader('src/build-data/templates')) + template = env.get_template('tls_suite_info.cpp.in') + suite_info = template.render( + script=sys.argv[0], + date=datetime.date.today().strftime("%Y-%m-%d"), + contents_hash=contents_hash, + suites=rendered_suites, + ) if options.output == '-': print(suite_info) else: out = open(options.output, 'w') out.write(suite_info) + out.write("\n") out.close() return 0 diff -Nru botan3-3.7.1+dfsg/src/scripts/dev_tools/mychain_creater.sh botan3-3.12.0+dfsg/src/scripts/dev_tools/mychain_creater.sh --- botan3-3.7.1+dfsg/src/scripts/dev_tools/mychain_creater.sh 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/dev_tools/mychain_creater.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,224 +0,0 @@ -#!/bin/sh - -# Helper script to generate a certificate chain -# alternative certificates might sign the OCSP responses. -# -# (C) 2022 Jack Lloyd -# (C) 2022 René Meusel (Rohde & Schwarz Cybersecurity) -# -# Botan is released under the Simplified BSD License (see license.txt) - -if [ $(date "+%y%m%d") != "220922" ]; then - echo "You should use a time machine to run this script..." - echo "Use libfaketime to set the system clock back to the 22nd of September 2022. This recreates the certificates with the same timestamps as used in the tests and saves you from re-setting the validation reference dates." - echo - echo "Like so (path is for Ubuntu, might vary):" - echo " LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1 FAKETIME=\"2022-09-22 12:00:00\" $0 $@" - exit 1 -fi - -set -ex - -PREFIX="mychain_" - -ROOTkey="root.key" -ROOTcsr="root.csr" -ROOTcert="${PREFIX}root.pem" -ROOTindex="root_index.txt" -ROOTconf="root.conf" - -INTkey="int.key" -INTcsr="int.csr" -INTcert="${PREFIX}int.pem" -INTindex="int_index.txt" -INTconf="int.conf" - -DELRESPkey="int_ocsp_delegate_responder.key" -DELRESPcsr="int_ocsp_delegate_responder.csr" -DELRESPcert="${PREFIX}int_ocsp_delegate_responder.pem" -DELRESPconf="int_ocsp_delegate_responder.conf" - -DELRESPnoOCSPcsr="int_ocsp_delegate_responder_no_ocsp_key_usage.csr" -DELRESPnoOCSPcert="${PREFIX}int_ocsp_delegate_responder_no_ocsp_key_usage.pem" -DELRESPnoOCSPconf="int_ocsp_delegate_responder_no_ocsp_key_usage.conf" - -EEkey="ee.key" -EEcsr="ee.csr" -EEcert="${PREFIX}ee.pem" -EEconf="ee.conf" - -# -# Create the Root CA -# -cat > $ROOTconf < $INTconf < $DELRESPconf < $DELRESPnoOCSPconf < $EEconf < $CAindex - elif [ "$subjectStatus" = "revoked" ]; then - formatted_currentdate=$(date "+%y%m%d%H%M%S") - echo "R\t${formatted_enddate}Z\t${formatted_currentdate}Z\t${serial}\tunknown\t${subject}" > $CAindex - else - echo "Don't understand OCSP response status: $subjectStatus" - exit 1 - fi - - if [ "$stapling" = "no_staple" ]; then - staple="-resp_no_certs" - else - staple="" - fi - - # generate an OCSP response using the just-created certificate - openssl ocsp -issuer $caCert -cert $subjectCert -reqout $ocspReq -text -no_nonce - openssl ocsp -reqin $ocspReq -rsigner $responderCert -rkey $responderKey -CA $caCert -index $CAindex -ndays 30 -respout $ocspResponse $staple -text -} - -# (Malformed) OCSP response for Intermediate signed by Intermediate itself -create_ocsp_response $INTcert $ROOTcert $INTcert $INTkey "valid" "${PREFIX}ocsp_for_int_self_signed.der" "no_staple" - -# (Malformed) OCSP response for End Entity signed by Root certificate -create_ocsp_response $EEcert $INTcert $ROOTcert $ROOTkey "valid" "${PREFIX}ocsp_for_ee_root_signed.der" "no_staple" - -# OCSP response for End Entity signed by Intermediate certificate -create_ocsp_response $EEcert $INTcert $INTcert $INTkey "valid" "${PREFIX}ocsp_for_ee.der" "no_staple" - -# OCSP response for End Entity signed by Delegate Responder of Intermediate certificate -create_ocsp_response $EEcert $INTcert $DELRESPcert $DELRESPkey "valid" "${PREFIX}ocsp_for_ee_delegate_signed.der" "staple" - -# OCSP response for End Entity signed by Delegate Responder of Intermediate certificate that does not have sufficient key usage flags -create_ocsp_response $EEcert $INTcert $DELRESPnoOCSPcert $DELRESPkey "valid" "${PREFIX}ocsp_for_ee_delegate_signed_malformed.der" "staple" diff -Nru botan3-3.7.1+dfsg/src/scripts/dev_tools/mychain_creator.sh botan3-3.12.0+dfsg/src/scripts/dev_tools/mychain_creator.sh --- botan3-3.7.1+dfsg/src/scripts/dev_tools/mychain_creator.sh 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/dev_tools/mychain_creator.sh 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,222 @@ +#!/bin/bash + +# Helper script to generate a certificate chain +# alternative certificates might sign the OCSP responses. +# +# (C) 2022 Jack Lloyd +# (C) 2022 René Meusel (Rohde & Schwarz Cybersecurity) +# +# Botan is released under the Simplified BSD License (see license.txt) + +if [ "$(date "+%y%m%d")" != "220922" ]; then + echo "You should use a time machine to run this script..." + echo "Use libfaketime to set the system clock back to the 22nd of September 2022. This recreates the certificates with the same timestamps as used in the tests and saves you from re-setting the validation reference dates." + echo + echo "Like so (path is for Ubuntu, might vary):" + echo " LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1 FAKETIME=\"2022-09-22 12:00:00\" $0 $*" + exit 1 +fi + +set -ex + +PREFIX="mychain_" + +ROOTkey="root.key" +ROOTcsr="root.csr" +ROOTcert="${PREFIX}root.pem" +ROOTconf="root.conf" + +INTkey="int.key" +INTcsr="int.csr" +INTcert="${PREFIX}int.pem" +INTconf="int.conf" + +DELRESPkey="int_ocsp_delegate_responder.key" +DELRESPcsr="int_ocsp_delegate_responder.csr" +DELRESPcert="${PREFIX}int_ocsp_delegate_responder.pem" +DELRESPconf="int_ocsp_delegate_responder.conf" + +DELRESPnoOCSPcsr="int_ocsp_delegate_responder_no_ocsp_key_usage.csr" +DELRESPnoOCSPcert="${PREFIX}int_ocsp_delegate_responder_no_ocsp_key_usage.pem" +DELRESPnoOCSPconf="int_ocsp_delegate_responder_no_ocsp_key_usage.conf" + +EEkey="ee.key" +EEcsr="ee.csr" +EEcert="${PREFIX}ee.pem" +EEconf="ee.conf" + +# +# Create the Root CA +# +cat > $ROOTconf < $INTconf < $DELRESPconf < $DELRESPnoOCSPconf < $EEconf < "$CAindex" + elif [ "$subjectStatus" = "revoked" ]; then + formatted_currentdate=$(date "+%y%m%d%H%M%S") + printf 'R\t%sZ\t%sZ\t%s\tunknown\t%s\n' "$formatted_enddate" "$formatted_currentdate" "$serial" "$subject" > "$CAindex" + else + echo "Don't understand OCSP response status: $subjectStatus" + exit 1 + fi + + if [ "$stapling" = "no_staple" ]; then + staple="-resp_no_certs" + else + staple="" + fi + + # generate an OCSP response using the just-created certificate + openssl ocsp -issuer "$caCert" -cert "$subjectCert" -reqout $ocspReq -text -no_nonce + openssl ocsp -reqin $ocspReq -rsigner "$responderCert" -rkey "$responderKey" -CA "$caCert" -index $CAindex -ndays 30 -respout "$ocspResponse" $staple -text +} + +# (Malformed) OCSP response for Intermediate signed by Intermediate itself +create_ocsp_response $INTcert $ROOTcert $INTcert $INTkey "valid" "${PREFIX}ocsp_for_int_self_signed.der" "no_staple" + +# (Malformed) OCSP response for End Entity signed by Root certificate +create_ocsp_response $EEcert $INTcert $ROOTcert $ROOTkey "valid" "${PREFIX}ocsp_for_ee_root_signed.der" "no_staple" + +# OCSP response for End Entity signed by Intermediate certificate +create_ocsp_response $EEcert $INTcert $INTcert $INTkey "valid" "${PREFIX}ocsp_for_ee.der" "no_staple" + +# OCSP response for End Entity signed by Delegate Responder of Intermediate certificate +create_ocsp_response $EEcert $INTcert $DELRESPcert $DELRESPkey "valid" "${PREFIX}ocsp_for_ee_delegate_signed.der" "staple" + +# OCSP response for End Entity signed by Delegate Responder of Intermediate certificate that does not have sufficient key usage flags +create_ocsp_response $EEcert $INTcert $DELRESPnoOCSPcert $DELRESPkey "valid" "${PREFIX}ocsp_for_ee_delegate_signed_malformed.der" "staple" diff -Nru botan3-3.7.1+dfsg/src/scripts/dev_tools/randombit_ocsp_forger.sh botan3-3.12.0+dfsg/src/scripts/dev_tools/randombit_ocsp_forger.sh --- botan3-3.7.1+dfsg/src/scripts/dev_tools/randombit_ocsp_forger.sh 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/dev_tools/randombit_ocsp_forger.sh 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Helper script to generate forged OCSP responses # that are signed by a random self-signed CA that @@ -15,11 +15,11 @@ exit 1 fi -if [ $(date "+%y%m%d") != "161118" ]; then +if [ "$(date "+%y%m%d")" != "161118" ]; then echo "You need a time machine to run this script..." echo "Use libfaketime to set the system clock back to the 18th of November 2016" echo "Like so (path is for Ubuntu, might vary):" - echo " LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1 FAKETIME=\"2016-11-18 12:00:00\" $0 $@" + echo " LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1 FAKETIME=\"2016-11-18 12:00:00\" $0 $*" exit 1 fi @@ -98,22 +98,22 @@ -sha256 -extfile $RPcertconf # mark victim's cert as "valid" or "revoked" -enddate=$(openssl x509 -in $Vcert -enddate -noout | sed 's/notAfter=//') +enddate=$(openssl x509 -in "$Vcert" -enddate -noout | sed 's/notAfter=//') formatted_enddate=$(date -d "$enddate" "+%y%m%d%H%M%S") -serial=$(openssl x509 -in $Vcert -serial -noout | sed 's/serial=//') -subject=$(openssl x509 -in $Vcert -subject -nameopt "oneline,RFC2253" -noout | sed 's/subject=//') +serial=$(openssl x509 -in "$Vcert" -serial -noout | sed 's/serial=//') +subject=$(openssl x509 -in "$Vcert" -subject -nameopt "oneline,RFC2253" -noout | sed 's/subject=//') if [ "$Vstatus" = "valid" ]; then - echo "V\t${formatted_enddate}Z\t\t${serial}\tunknown\t${subject}" > $RPindex + printf 'V\t%sZ\t\t%s\tunknown\t%s\n' "$formatted_enddate" "$serial" "$subject" > "$RPindex" elif [ "$Vstatus" = "revoked" ]; then formatted_currentdate=$(date "+%y%m%d%H%M%S") - echo "R\t${formatted_enddate}Z\t${formatted_currentdate}Z\t${serial}\tunknown\t${subject}" > $RPindex + printf 'R\t%sZ\t%sZ\t%s\tunknown\t%s\n' "$formatted_enddate" "$formatted_currentdate" "$serial" "$subject" > "$RPindex" else echo "Don't understand OCSP response status: $Vstatus" exit 1 fi # generate an OCSP response using the just-created certificate -openssl ocsp -issuer $Vissuer -cert $Vcert -reqout $RQ -text -no_nonce -openssl ocsp -reqin $RQ -rsigner $RPcert -rkey $RPkey -CA $Vissuer -index $RPindex -ndays 5 -respout $RP -text -openssl ocsp -reqin $RQ -rsigner $RPcert -rkey $RPkey -CA $Vissuer -index $RPindex -ndays 5 -respout $RPnocerts -resp_no_certs -text +openssl ocsp -issuer "$Vissuer" -cert "$Vcert" -reqout $RQ -text -no_nonce +openssl ocsp -reqin $RQ -rsigner $RPcert -rkey $RPkey -CA "$Vissuer" -index $RPindex -ndays 5 -respout $RP -text +openssl ocsp -reqin $RQ -rsigner $RPcert -rkey $RPkey -CA "$Vissuer" -index $RPindex -ndays 5 -respout $RPnocerts -resp_no_certs -text diff -Nru botan3-3.7.1+dfsg/src/scripts/dev_tools/run_clang_format.py botan3-3.12.0+dfsg/src/scripts/dev_tools/run_clang_format.py --- botan3-3.7.1+dfsg/src/scripts/dev_tools/run_clang_format.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/dev_tools/run_clang_format.py 2026-05-07 01:38:28.000000000 +0000 @@ -14,8 +14,14 @@ import time import os import re +import shutil from multiprocessing.pool import ThreadPool +def clang_format_style_path(): + script_location = os.path.dirname(os.path.abspath(__file__)) + relative_style_path = "../../configs/clang-format" + return os.path.realpath(os.path.join(script_location, relative_style_path)) + def run_command(cmdline): proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE, @@ -28,8 +34,8 @@ return (stdout, stderr) -def apply_clang_format(clang_format, source_file): - cmdline = [clang_format, '-i', source_file] +def apply_clang_format(clang_format, style_file, source_file): + cmdline = [clang_format, f'--style=file:{style_file}', '-i', source_file] (stdout, stderr) = run_command(cmdline) if stdout != '' or stderr != '': @@ -50,8 +56,8 @@ lineterm="", )) -def check_clang_format(clang_format, source_file): - cmdline = [clang_format, source_file] +def check_clang_format(clang_format, style_file, source_file): + cmdline = [clang_format, f'--style=file:{style_file}', source_file] (stdout, stderr) = run_command(cmdline) if stderr != '': @@ -66,7 +72,7 @@ return True def list_source_files_in(directory): - excluded = ['pkcs11t.h', 'pkcs11f.h', 'pkcs11.h'] + excluded = ['pkcs11.h'] for (dirpath, _, filenames) in os.walk(directory): for filename in filenames: @@ -89,6 +95,13 @@ return files_to_fmt +def find_clang_format_binary(req_version): + for binary_name in ['clang-format', f'clang-format-{req_version}']: + binary = shutil.which(binary_name) + if binary is not None: + return binary + return None + # Run clang-version -version and return the major version def clang_format_version(clang_format): clang_format_version_re = re.compile(r'^(.* )?clang-format version ([0-9]+)\.([0-9]+)\.([0-9]+)') @@ -116,12 +129,20 @@ parser.add_option('-j', '--jobs', action='store', type='int', default=0) parser.add_option('--src-dir', metavar='DIR', default='src') parser.add_option('--check', action='store_true', default=False) - parser.add_option('--clang-format-binary', metavar='PATH', default='clang-format') + parser.add_option('--clang-format-binary', metavar='PATH') + parser.add_option('--style-file', metavar='FILE', default=clang_format_style_path()) parser.add_option('--skip-version-check', action='store_true', default=False) (options, args) = parser.parse_args(args) - clang_format = options.clang_format_binary + # This check is probably stricter than we really need, and should + # be revised as we gain more experience with clang-format + req_version = 17 + + clang_format = options.clang_format_binary or find_clang_format_binary(req_version) + if clang_format is None: + print("Failed to find clang-format binary, exiting") + return 1 if not options.skip_version_check: version = clang_format_version(clang_format) @@ -130,15 +151,15 @@ print("Failed to get clang-format version, exiting") return 1 - # This check is probably stricter than we really need, and should - # be revised as we gain more experience with clang-format - req_version = 17 - if version != req_version: print("This script requires clang-format %d but current version is %d" % (req_version, version)) print("Use --skip-version-check to carry on, however formatting may be incorrect") return 1 + if not os.path.isfile(options.style_file): + print("Failed to find file containing clang-format configuration") + return 1 + jobs = options.jobs if jobs == 0: jobs = multiprocessing.cpu_count() + 1 @@ -170,9 +191,9 @@ results = [] for file in files_to_fmt: if options.check: - results.append(pool.apply_async(check_clang_format, (clang_format, file,))) + results.append(pool.apply_async(check_clang_format, (clang_format, options.style_file, file,))) else: - results.append(pool.apply_async(apply_clang_format, (clang_format, file,))) + results.append(pool.apply_async(apply_clang_format, (clang_format, options.style_file, file,))) fail_execution = False diff -Nru botan3-3.7.1+dfsg/src/scripts/dev_tools/run_clang_tidy.py botan3-3.12.0+dfsg/src/scripts/dev_tools/run_clang_tidy.py --- botan3-3.7.1+dfsg/src/scripts/dev_tools/run_clang_tidy.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/dev_tools/run_clang_tidy.py 2026-05-07 01:38:28.000000000 +0000 @@ -1,29 +1,26 @@ #!/usr/bin/env python3 """ -(C) 2022,2023 Jack Lloyd +(C) 2022,2023,2025 Jack Lloyd Botan is released under the Simplified BSD License (see license.txt) """ -import subprocess -import sys +import datetime +import hashlib import json +import multiprocessing +from multiprocessing.pool import ThreadPool import optparse # pylint: disable=deprecated-module import os +import random import re -import multiprocessing +import shlex +import sqlite3 +import subprocess +import sys import time import uuid -from multiprocessing.pool import ThreadPool - -quick_checks = [ - '-clang-analyzer*', # has to be explicitly disabled - 'modernize-use-nullptr', - 'readability-braces-around-statements', - 'performance-unnecessary-value-param', - '*-non-private-member-variables-in-classes', -] enabled_checks = [ 'bugprone-*', @@ -41,31 +38,20 @@ # these are ones that we might want to be clean for in the future, # but currently are not disabled_needs_work = [ - '*-named-parameter', - '*-member-init', # should definitely fix this one - 'bugprone-lambda-function-name', # should be an easy fix - 'bugprone-unchecked-optional-access', # clang-tidy seems buggy (many false positives) + 'cppcoreguidelines-use-default-member-init', + 'bugprone-unchecked-optional-access', 'bugprone-empty-catch', - 'cert-err58-cpp', # many false positives eg __m128i 'cppcoreguidelines-avoid-const-or-ref-data-members', - 'cppcoreguidelines-init-variables', - 'cppcoreguidelines-owning-memory', - 'cppcoreguidelines-prefer-member-initializer', - 'cppcoreguidelines-slicing', # private->public key slicing - 'hicpp-explicit-conversions', - 'misc-const-correctness', # pretty noisy - 'misc-include-cleaner', - 'misc-redundant-expression', # BigInt seems to confuse clang-tidy - 'misc-misplaced-const', - 'misc-confusable-identifiers', - 'modernize-avoid-bind', + 'cppcoreguidelines-pro-type-const-cast', + 'misc-include-cleaner', # warning: useful but quite buggy + 'misc-multiple-inheritance', # public key uses this but should be fixed + 'misc-override-with-different-visibility', # mostly fine but should be looked at 'modernize-pass-by-value', 'modernize-use-ranges', # limited by compiler support currently 'performance-avoid-endl', + 'readability-redundant-typename', # TODO(Botan4) when Clang min version increases this can be fixed 'readability-convert-member-functions-to-static', - 'readability-implicit-bool-conversion', 'readability-inconsistent-declaration-parameter-name', # should fix this, blocked by https://github.com/llvm/llvm-project/issues/60845 - 'readability-qualified-auto', 'readability-simplify-boolean-expr', # sometimes ok 'readability-static-accessed-through-instance', ] @@ -73,7 +59,7 @@ # these we are probably not interested in ever being clang-tidy clean for disabled_not_interested = [ '*-array-to-pointer-decay', - '*-avoid-c-arrays', + '*-avoid-c-arrays', # triggers also on foo(T x[], size_t len) decls '*-else-after-return', '*-function-size', '*-magic-numbers', # can't stop the magic @@ -82,45 +68,35 @@ '*-use-auto', # not universally a good idea '*-use-emplace', # often less clear '*-deprecated-headers', # wrong for system headers like stdlib.h - 'bugprone-argument-comment', - 'bugprone-branch-clone', # doesn't interact well with feature macros + 'cert-dcl21-cpp', # invalid, and removed already in clang-tidy 19 'bugprone-easily-swappable-parameters', - 'bugprone-implicit-widening-of-multiplication-result', - 'bugprone-suspicious-stringview-data-usage', # triggers on every use of string_view::data ?? - 'cppcoreguidelines-avoid-do-while', - 'cppcoreguidelines-non-private-member-variables-in-classes', # pk split keys + 'bugprone-implicit-widening-of-multiplication-result', # would be reasonable if it ignored constants (it doesn't) + 'cppcoreguidelines-pro-bounds-avoid-unchecked-container-access', 'cppcoreguidelines-pro-bounds-pointer-arithmetic', 'cppcoreguidelines-pro-bounds-constant-array-index', - 'cppcoreguidelines-pro-type-const-cast', # see above - 'cppcoreguidelines-pro-type-reinterpret-cast', # not possible thanks though - 'cppcoreguidelines-pro-type-vararg', # idiocy - 'hicpp-no-assembler', - 'hicpp-vararg', # idiocy - 'hicpp-signed-bitwise', # impossible to avoid in C/C++, int promotion rules :/ + 'cppcoreguidelines-pro-type-reinterpret-cast', + 'hicpp-signed-bitwise', # would be reasonable if it ignored constants (it doesn't) 'misc-no-recursion', - 'modernize-loop-convert', # sometimes very ugly - 'modernize-raw-string-literal', # usually less readable + 'modernize-avoid-c-style-cast', # the kind of cast clang-tidy is complaining about here is fine 'modernize-use-trailing-return-type', # fine, but we're not using it everywhere 'modernize-return-braced-init-list', # thanks I hate it 'modernize-use-default-member-init', 'modernize-use-designated-initializers', 'modernize-use-nodiscard', 'modernize-use-using', # fine not great - 'portability-simd-intrinsics', - 'readability-avoid-return-with-void-value', - 'readability-container-data-pointer', + 'readability-avoid-return-with-void-value', # Jack likes doing this 'readability-function-cognitive-complexity', 'readability-identifier-length', # lol, lmao - 'readability-isolate-declaration', 'readability-math-missing-parentheses', 'readability-non-const-parameter', + 'readability-use-concise-preprocessor-directives', # it's not more readable... + 'readability-redundant-parentheses', # often improves readability ... + 'readability-redundant-inline-specifier', # Jack likes doing this 'readability-redundant-access-specifiers', # reneme likes doing this - 'readability-suspicious-call-argument', - 'readability-use-std-min-max', 'readability-use-anyofallof', # not more readable ] -disabled_checks = disabled_needs_work + disabled_not_interested +disabled_checks = sorted(disabled_needs_work + disabled_not_interested) def create_check_option(enabled, disabled): return ','.join(enabled) + ',' + ','.join(['-' + d for d in disabled]) @@ -128,17 +104,16 @@ def render_clang_tidy_file(target_dir, enabled, disabled): filepath = os.path.join(target_dir, '.clang-tidy') print(f'regenerating {filepath}') + today = datetime.date.today().strftime("%Y-%m-%d") with open(filepath, "w", encoding="utf-8") as clang_tidy_file: clang_tidy_file.writelines([ '---\n', - f'# This file was automatically generated by {sys.argv[0]} --regenerate-inline-config-file\n', - '#\n', - '# All manual edits to this file will be lost. Edit the script\n', - '# then regenerate this configuration file.\n', + f'# This file was automatically generated by {sys.argv[0]} --regenerate-inline-config-file on {today}\n', + '# All manual changes will be lost. Edit the script instead.\n', '\n', 'Checks: >\n'] + - [ f' {check},\n' for check in enabled ] + - [ f' -{check},\n' for check in disabled] + + [ f' {check},\n' for check in sorted(enabled)] + + [ f' -{check},\n' for check in sorted(disabled)] + ['---\n']) def load_compile_commands(build_dir): @@ -166,7 +141,8 @@ cmdline = ['clang-tidy', '--quiet', '-checks=%s' % (check_config), - '-p', compile_commands_file] + '-p', compile_commands_file, + '-header-filter=.*'] if options.fixit: cmdline.append('-fix') @@ -180,10 +156,8 @@ stdout = run_command(cmdline) - if options.verbose: - print("Checked", source_file) - sys.stdout.flush() if stdout != "": + print("### Errors in", source_file) print(stdout) sys.stdout.flush() return False @@ -199,23 +173,124 @@ return True return False +def preproc_file(compile_commands): + cmd = shlex.split(compile_commands['command']) + + dash_o = cmd.index("-o") + if dash_o >= 0: + cmd.pop(dash_o + 1) # remove object file name + cmd.pop(dash_o) # remove -o + + if "-c" in cmd: + cmd.remove("-c") + cmd.append("-E") + + result = subprocess.run( + cmd, + check=True, + stdout=subprocess.PIPE, + universal_newlines=True) + + return hashlib.sha256(result.stdout.encode('utf-8')).hexdigest() + +def hash_args(**kwargs): + outer_hash = hashlib.sha256() + + for key in sorted(kwargs.keys()): + value = kwargs[key] + outer_hash.update(hashlib.sha256(key.encode('utf-8')).digest()) + outer_hash.update(hashlib.sha256(value.encode('utf-8')).digest()) + + return outer_hash.hexdigest() + +class CacheDatabase: + """ + Caches SUCCESSFUL clang-tidy runs on source files to speed up whole-tree + runs of clang-tidy. The cache key is calculated as the hash of the source file + and a set of meta information such as 'clang-tidy --version' and the check configuration. + """ + CACHE_DEBUG = False + + def __init__(self, db_path): + self.db = sqlite3.connect(db_path) + cur = self.db.cursor() + cur.execute("CREATE TABLE IF NOT EXISTS clang_tidy_clean(key UNIQUE)") + self.db.commit() + + def hit(self, **kwargs): + cache_key = hash_args(**kwargs) + + cur = self.db.cursor() + cur.execute("SELECT key from clang_tidy_clean where key = ?", (cache_key, )) + res = cur.fetchall() + + if len(res) > 0: + if self.CACHE_DEBUG: + print("Cache hit for", cache_key) + return True + else: + if self.CACHE_DEBUG: + print("Cache miss for", cache_key) + return False + + def record(self, **kwargs): + cache_key = hash_args(**kwargs) + + cur = self.db.cursor() + if self.CACHE_DEBUG: + print("Cache save for", cache_key) + cur.execute("INSERT OR IGNORE INTO clang_tidy_clean values(?)", (cache_key, )) + self.db.commit() + + def count(self): + cur = self.db.cursor() + cur.execute("SELECT count(*) FROM clang_tidy_clean") + entries = cur.fetchall() + return int(entries[0][0]) + + def cleanup(self, limit): + cur = self.db.cursor() + + cur.execute("SELECT key FROM clang_tidy_clean") + entries = cur.fetchall() + + if len(entries) <= limit: + return + + to_prune = len(entries) - limit + + # Randomly evict entries if cache has grown too large + random.shuffle(entries) + + for cache_key in entries[:to_prune]: + if self.CACHE_DEBUG: + print("Pruning", cache_key[0]) + cur.execute("DELETE FROM clang_tidy_clean WHERE key = ?", (cache_key[0], )) + self.db.commit() + def main(args = None): # pylint: disable=too-many-return-statements if args is None: args = sys.argv parser = optparse.OptionParser() - parser.add_option('-j', '--jobs', action='store', type='int', default=0) - parser.add_option('--verbose', action='store_true', default=False) + parser.add_option('-j', '--jobs', action='store', type='int', default=multiprocessing.cpu_count() + 1, + help='set number of jobs to run (default %default)') + parser.add_option('--quiet', action='store_true', default=False) parser.add_option('--fixit', action='store_true', default=False) parser.add_option('--build-dir', default='build') parser.add_option('--list-checks', action='store_true', default=False) parser.add_option('--regenerate-inline-config-file', action='store_true', default=False) - parser.add_option('--fast-checks-only', action='store_true', default=False) parser.add_option('--only-changed-files', action='store_true', default=False) parser.add_option('--only-matching', metavar='REGEX', default='.*') parser.add_option('--take-file-list-from-stdin', action='store_true', default=False) parser.add_option('--export-fixes-dir', default=None) + parser.add_option('--cache-db', metavar='DB', + help='Path to sqlite3 database file for caching', + default=os.getenv("BOTAN_CLANG_TIDY_CACHE")) + + # 100K entries with our current schema is about 15 Mb + max_db_entries = int(os.getenv("BOTAN_CLANG_TIDY_CACHE_MAX_SIZE") or 100_000) (options, args) = parser.parse_args(args) @@ -240,25 +315,25 @@ print("No C++ files were modified vs master, skipping clang-tidy checks") return 0 elif options.take_file_list_from_stdin: + scan_all = False + for line in sys.stdin: file = os.path.basename(line.strip()) if file.endswith('.cpp') or file.endswith('.h'): files_to_check.append(file) + elif file in ['run_clang_tidy.py']: + scan_all = True - if len(files_to_check) == 0: + if scan_all: + # clear this to revert to disable filtering causing all files to be scanned + files_to_check = [] + elif len(files_to_check) == 0: print("No C++ files were provided on stdin, skipping clang-tidy checks") return 0 - jobs = options.jobs - if jobs == 0: - jobs = multiprocessing.cpu_count() + 1 - (compile_commands_file, compile_commands) = load_compile_commands(options.build_dir) - if options.fast_checks_only: - check_config = ','.join(quick_checks) - else: - check_config = create_check_option(enabled_checks, disabled_checks) + check_config = create_check_option(enabled_checks, disabled_checks) if options.list_checks: print(run_command(['clang-tidy', '-list-checks', '-checks', check_config]), end='') @@ -268,13 +343,40 @@ render_clang_tidy_file('src', enabled_checks, disabled_checks) return 0 - pool = ThreadPool(jobs) + pool = ThreadPool(options.jobs) + + cache = CacheDatabase(options.cache_db) if options.cache_db else None start_time = time.time() files_checked = 0 file_matcher = re.compile(options.only_matching) + clang_tidy_version = None + + if cache is not None: + if not options.quiet: + print("Using cache at %s with %d entries" % (options.cache_db, cache.count())) + + results = [] + + clang_tidy_version = subprocess.run(['clang-tidy', '--version'], + check=True, + stdout=subprocess.PIPE).stdout.decode('utf-8') + + for info in compile_commands: + results.append(pool.apply_async(preproc_file, (info, ))) + + assert(len(results) == len(compile_commands)) + for (i, result) in enumerate(results): + compile_commands[i]['preproc'] = result.get() + + if not options.quiet: + print("Preprocessing/hashing %d files took %.02f sec" % (len(results), time.time() - start_time)) + sys.stdout.flush() + + start_time = time.time() + results = [] for info in compile_commands: file = info['file'] @@ -285,23 +387,46 @@ if file_matcher.search(file) is None: continue - files_checked += 1 - results.append(pool.apply_async( - run_clang_tidy, - (compile_commands_file, - check_config, - file, - options))) + cache_hit = False + if cache is not None: + if cache.hit(config=check_config, source_file=info['preproc'], clang_tidy=clang_tidy_version): + cache_hit = True + + if cache_hit and not options.quiet: + print("Checked", info["file"], " (cached)") + sys.stdout.flush() + + if not cache_hit: + files_checked += 1 + + results.append((info, pool.apply_async( + run_clang_tidy, + (compile_commands_file, + check_config, + file, + options)))) fail_cnt = 0 - for result in results: - if not result.get(): + for (info, result) in results: + success = result.get() + + if not options.quiet: + print("Checked", info['file']) + sys.stdout.flush() + + if success and cache is not None: + cache.record(config=check_config, source_file=info['preproc'], clang_tidy=clang_tidy_version) + + if not success: fail_cnt += 1 time_consumed = time.time() - start_time print("Checked %d files in %d seconds" % (files_checked, time_consumed)) + if cache is not None and max_db_entries > 0: + cache.cleanup(max_db_entries) + if fail_cnt == 0: return 0 else: diff -Nru botan3-3.7.1+dfsg/src/scripts/dev_tools/show_dependencies.py botan3-3.12.0+dfsg/src/scripts/dev_tools/show_dependencies.py --- botan3-3.7.1+dfsg/src/scripts/dev_tools/show_dependencies.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/dev_tools/show_dependencies.py 2026-05-07 01:38:28.000000000 +0000 @@ -25,7 +25,7 @@ # locale sys.path.append(botan_root) -from configure import ModuleInfo +from configure import ModuleInfo # noqa: E402 parser = argparse.ArgumentParser(description= 'Show Botan module dependencies. ' @@ -148,7 +148,7 @@ # Return true iff a depends on b, # i.e. b is in the dependencies of a def depends_on(a, b): - if not a in direct_dependencies: + if a not in direct_dependencies: return False else: return b in direct_dependencies[a] diff -Nru botan3-3.7.1+dfsg/src/scripts/dist.py botan3-3.12.0+dfsg/src/scripts/dist.py --- botan3-3.7.1+dfsg/src/scripts/dist.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/dist.py 2026-05-07 01:38:28.000000000 +0000 @@ -60,7 +60,7 @@ return check_subprocess_results(proc, 'git') def maybe_gpg(val): - val = val.decode('ascii') + val = val.decode('utf-8') if 'BEGIN PGP SIGNATURE' in val: return val.split('\n')[-2] else: diff -Nru botan3-3.7.1+dfsg/src/scripts/docker-android.sh botan3-3.12.0+dfsg/src/scripts/docker-android.sh --- botan3-3.7.1+dfsg/src/scripts/docker-android.sh 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/docker-android.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -VERSION=`./configure.py --version` -mkdir -p docker-builds -docker build -f src/scripts/Dockerfile.android --force-rm -t botan-android-${VERSION} \ - --build-arg ANDROID_ARCH=${ANDROID_ARCH} \ - --build-arg ANDROID_ARCH_SUF=${ANDROID_ARCH_SUF} \ - --build-arg ANDROID_SDK_VER=${ANDROID_SDK_VER} \ - --build-arg ANDROID_TOOLCHAIN_SUF=${ANDROID_TOOLCHAIN_SUF} \ - . -docker create --name botan-android-${VERSION} botan-android-${VERSION} -docker cp botan-android-${VERSION}:/botan/android docker-builds -docker rm -f botan-android-${VERSION} diff -Nru botan3-3.7.1+dfsg/src/scripts/gdb/strubtest.py botan3-3.12.0+dfsg/src/scripts/gdb/strubtest.py --- botan3-3.7.1+dfsg/src/scripts/gdb/strubtest.py 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/gdb/strubtest.py 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,179 @@ +# (C) 2025 Jack Lloyd +# 2025 René Meusel, Rohde & Schwarz Cybersecurity +# +# Botan is released under the Simplified BSD License (see license.txt) +# + +""" +User-defined GDB CLI command to help in testing GCC's stack scrubbing +annotations. See also src/scripts/test_strubbed_symbols.py for details. + +This can be used in interactive GDB sessions for debugging and development. + + $> gdb -x src/scripts/gdb/strubtest.py ./botan + (gdb) strubtest Botan::SHA_256::compress_digest + strubtest setup done + (gdb) run hash --algo=SHA-256 readme.rst + Success: stackframe of Botan::SHA_256::compress_digest contains 10176 zero bytes after invocation + ECD8814EDC180601831FDAD7DEDCF24D57F9F002295346E7D558C941AD318F8E readme.rst +""" + +import gdb + +def stack_pointer(frame): + return frame.read_register('sp') + +def frame_pointer(frame): + return frame.read_register('fp') + +def current_stackframe_memory_span(frame): + start, end = stack_pointer(frame), frame_pointer(frame) + if frame.architecture().name() == "aarch64" and end < start: + start, end = end, start # On aarch64, the stack grows downwards! + return (start, end - start) + +def report_error(error): + gdb.write(f"Error: {error}\n", gdb.STDERR) + +def report_status(status): + gdb.write(f"{status}\n", gdb.STDOUT) + +class PostStrubLocation(gdb.FinishBreakpoint): + """ + This (temporary) breakpoint is placed by the StrubTarget at the end of the + "virtual wrapper" GCC introduced to scrub the target's stackframe. When hit + it validates that the this stackframe indeed contains zero bytes only. + """ + + def __init__(self, caller_frame, stackframe, function_name): + super().__init__(caller_frame, internal=True) + self.function_name = function_name + self.payload_stack_memory = stackframe + + def stackframe_memory(self): + return gdb.selected_inferior().read_memory(*self.payload_stack_memory) + + def stackframe_size(self): + return self.payload_stack_memory[1] + + def is_stackframe_scrubbed(self): + return all(b'\x00' == b for b in self.stackframe_memory()) + + def stop(self): + if self.stackframe_size() == 0: + report_error(f"{self.function_name} has an empty stackframe, cannot validate") + elif not self.is_stackframe_scrubbed(): + report_error(f"{self.function_name} didn't get its stack frame scrubbed after usage") + else: + report_status(f"Success: stackframe of {self.function_name} contains {self.stackframe_size()} zero bytes after invcoation") + +class TargetReturnLocation(gdb.Breakpoint): + """ + This special breakpoint finds one or more return instructions in the target + frame and registers itself at those instruction addresses. When hit, it will + obtain the stackframe size just before the function returns and set a + PostStrubLocation breakpoint at the caller frame. + """ + + @staticmethod + def find_and_register_in(frame, function_name): + arch = frame.architecture() + assert arch.name() == "aarch64", "TargetReturnLocation is meant for aarch64" + disass = arch.disassemble(frame.block().start, frame.block().end) + addrs = [f"0x{instr['addr']:x}" for instr in disass if instr['asm'] == 'ret'] + if not addrs: + report_error(f"no ret instructions found in {function_name}") + else: + for ret_address in addrs: + TargetReturnLocation(ret_address, function_name) + + def __init__(self, address, function_name): + super().__init__(f"*{address}", internal=True, temporary=True) + self.function_name = function_name + + # Workaround: gdb.Breakpoint has a temporary= param in its constructor, + # that is meant to delete the breakpoint after it has been hit. Though, + # it didn't work for some reason. + self.hit = False + + def stop(self): + if not self.hit: + target_frame = gdb.newest_frame() + caller_frame = target_frame.older() + stackframe = current_stackframe_memory_span(target_frame) + PostStrubLocation(caller_frame, stackframe, self.function_name) + self.hit = True + +class StrubTarget(gdb.Breakpoint): + """ + This special breakpoint shall be set to a symbol that was marked with + GCC's __attribute__(strub("internal")). When hit, it will note the size of + its stackframe and set another temporary breakpoint at the end of the + "virtual wrapper" GCC introduced, see PostStrubLocation. There, we'll check + if the now-invalidated stackframe indeed contains only zero bytes. + """ + + def __init__(self, function_name): + super().__init__(function_name, internal=True) + self.function_name = function_name + + def stop(self): + target_frame = gdb.newest_frame() + caller_frame = gdb.newest_frame().older() + + # __attribute__( strub("internal") ) creates a "virtual wrapper" around + # the annotated function. This wrapper has the same symbol name as the + # actual target function. To tell them apart, we simply _assume_ that + # the wrapper is always at the lower address in the binary. This may + # change in the future or differ across compiler versions! + if len(self.locations) > 1: + wrapper_address = min(loc.address for loc in self.locations) + if wrapper_address == target_frame.pc(): + return False + + if target_frame.architecture().name() == "aarch64": + # On aarch64, the stackframe size is not available at the beginning + # of the function, so we set breakpoints at the return instructions + # of the current frame and obtain the stackframe size there. + TargetReturnLocation.find_and_register_in(target_frame, self.function_name) + else: + # On other platforms (e.g. x86_64), we can directly obtain the + # stackframe size at the beginning of the function and set the + # PostStrubLocation breakpoint immediately. + stackframe = current_stackframe_memory_span(target_frame) + PostStrubLocation(caller_frame, stackframe, self.function_name) + + # Don't stop for interactive inspection at this location + return False + +class StrubTest(gdb.Command): + """ + User-defined gdb command to set up a stack scrubbing (strub) test allowing + to check that the invocation of a given symbol gets its stack cleared after + returning. Stack scrubbing is a feature in GCC 14 and newer and is enabled + for relevant functions using `./configure.py --enable-stack-scrubbing`. + """ + + def __init__(self): + super().__init__("strubtest", gdb.COMMAND_USER) + + def invoke(self, argstring, _): + if not argstring: + report_error("a target symbol name is required") + return + + bp = StrubTarget(argstring) + if bp.pending: + bp.delete() + report_error(f"the provided symbol '{argstring}' does not appear to be available") + + report_status("strubtest setup done") + +# Register the custom command with GDB. It may be invoked from GDB's CLI: +# +# strubtest +# +# Any time a test program (compiled with --enable-stack-scrubbing --debug-mode) +# invokes this symbol GDB will either print "Success: ..." or "Error: ...", +# depending on the outcome of the test. +StrubTest() diff -Nru botan3-3.7.1+dfsg/src/scripts/install.py botan3-3.12.0+dfsg/src/scripts/install.py --- botan3-3.7.1+dfsg/src/scripts/install.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/install.py 2026-05-07 01:38:28.000000000 +0000 @@ -76,7 +76,7 @@ raise PrependDestdirError("--prefix must be an absolute path when DESTDIR is set.") path = os.path.normpath(path) - # Remove / or \ prefixes if existent to accomodate for os.path.join() + # Remove / or \ prefixes if existent to accommodate for os.path.join() path = path.lstrip(os.path.sep) path = os.path.join(destdir, path) @@ -123,7 +123,7 @@ def copy_file(src, dst): logging.debug('Copying %s to %s', src, dst) - shutil.copyfile(src, dst) + shutil.copy2(src, dst) def copy_executable(src, dst): copy_file(src, dst) @@ -146,7 +146,7 @@ lib_dir = cfg['libdir'] target_include_dir = cfg['installed_include_dir'] pkgconfig_dir = os.path.join(lib_dir, 'pkgconfig') - cmake_dir = os.path.join(lib_dir, 'cmake', 'Botan-%s' % cfg["version"]) + cmake_dir = cfg.get('cmake_install_dir') prefix = cfg['prefix'] diff -Nru botan3-3.7.1+dfsg/src/scripts/run_limbo_tests.py botan3-3.12.0+dfsg/src/scripts/run_limbo_tests.py --- botan3-3.7.1+dfsg/src/scripts/run_limbo_tests.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/run_limbo_tests.py 2026-05-07 01:38:28.000000000 +0000 @@ -7,7 +7,6 @@ from botan3 import X509Cert from dateutil import parser import json -import datetime import optparse # pylint: disable=deprecated-module import re import subprocess @@ -16,6 +15,11 @@ ignored_tests = {} tests_that_succeed_unexpectedly = { + 'crl::revoked-certificate-with-crl': 'Need CRL support in this script', + 'crl::crlnumber-missing': 'Need CRL support in this script', + 'crl::crlnumber-critical': 'Need CRL support in this script', + 'crl::issuer-missing-crlsign': 'Need CRL support in this script', + 'rfc5280::aki::critical-aki unexpected': 'Conflates CA and verifier requirements', 'rfc5280::aki::critical-aki': 'Conflates CA and verifier requirements', 'rfc5280::aki::intermediate-missing-aki': 'Conflates CA and verifier requirements', @@ -44,6 +48,16 @@ 'webpki::forbidden-dsa-leaf': 'Conflates CA and verifier requirements', 'webpki::forbidden-dsa-root': 'Conflates CA and verifier requirements', + 'webpki::cn::ipv4-hex-mismatch': 'CABF BR 7.1.4.3 applies to CAs not verifiers', + 'webpki::cn::ipv4-leading-zeros-mismatch': 'CABF BR 7.1.4.3 applies to CAs not verifiers', + 'webpki::cn::ipv6-uppercase-mismatch': 'CABF BR 7.1.4.3 applies to CAs not verifiers', + 'webpki::cn::ipv6-uncompressed-mismatch': 'CABF BR 7.1.4.3 applies to CAs not verifiers', + 'webpki::cn::ipv6-non-rfc5952-mismatch': 'CABF BR 7.1.4.3 applies to CAs not verifiers', + 'webpki::cn::punycode-not-in-san': 'CABF BR 7.1.4.3 applies to CAs not verifiers', + 'webpki::cn::utf8-vs-punycode-mismatch': 'CABF BR 7.1.4.3 applies to CAs not verifiers', + 'webpki::cn::not-in-san': 'CABF BR 7.1.4.3 applies to CAs not verifiers', + 'webpki::cn::case-mismatch': 'CABF BR 7.1.4.3 applies to CAs not verifiers', + 'webpki::forbidden-p192-leaf': 'We do not place restrictions on the leaf key', 'webpki::forbidden-weak-rsa-in-leaf': 'We do not place restrictions on the leaf key', @@ -55,11 +69,12 @@ 'rfc5280::nc::nc-forbids-othername': 'Othername is a NULL which we drop', 'webpki::san::wildcard-embedded-ulabel-san': 'Needs investigation', - 'webpki::malformed-aia': 'Needs investigation', - # A number of tests (736, 737, ...) seem to make the implicit assumption - # that if a name constraint applies to a certificate then we should not - # ever use the CN as the hostname, even if the ee cert does not have a SAN + # These tests are despite the nameconstraints prefix actually + # testing that no CN fallback exists - the end entity certs have a + # CN but no SAN, and are checking if we accept a DNS name that is + # set in the CN. Since currently we do consult the CN if (and only + # if) the SAN is completely absent, the tests fail 'bettertls::nameconstraints::tc736': 'See comment above', 'bettertls::nameconstraints::tc737': 'Same as 736', 'bettertls::nameconstraints::tc738': 'Same as 736', @@ -99,11 +114,8 @@ } tests_that_fail_unexpectedly = { - 'rfc5280::nc::permitted-ipv6-match': 'IPv6 name constraints not implemented', - 'cve::cve-2024-0567': 'Possible path building bug', 'rfc5280::root-and-intermediate-swapped': 'Possible path building bug', - 'rfc5280::nc::permitted-self-issued': 'Possible path building bug', } def report_success(test_id, modified_result, type): @@ -225,7 +237,7 @@ validation_time = int(parser.parse(test['validation_time']).timestamp()) hostname = None - if test['expected_peer_name'] != None: + if test['expected_peer_name'] is not None: if test['expected_peer_name']['kind'] in ['DNS', 'IP']: hostname = test['expected_peer_name']['value'] else: diff -Nru botan3-3.7.1+dfsg/src/scripts/run_tests_under_valgrind.py botan3-3.12.0+dfsg/src/scripts/run_tests_under_valgrind.py --- botan3-3.7.1+dfsg/src/scripts/run_tests_under_valgrind.py 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/run_tests_under_valgrind.py 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 + +""" +Run all tests under valgrind in a thread pool + +(C) 2025 Jack Lloyd + +Botan is released under the Simplified BSD License (see license.txt) +""" + +import multiprocessing +import optparse # pylint: disable=deprecated-module +import subprocess +import sys +import time + +from multiprocessing.pool import ThreadPool + +def get_concurrency(): + def_concurrency = 2 + max_concurrency = 16 + + try: + return min(max_concurrency, multiprocessing.cpu_count()) + except ImportError: + return def_concurrency + +def available_tests(botan_test): + cmd = [botan_test, '--list-tests'] + tests = subprocess.Popen(cmd, close_fds=True, stdout=subprocess.PIPE).communicate() + + return [str(s, encoding='utf8') for s in tests[0].split()] + +def run_valgrind(options, test): + valgrind_cmd = ['valgrind', '-v', '--error-exitcode=9'] + + if options.with_leak_check: + valgrind_cmd += ['--leak-check=full', '--show-reachable=yes'] + + if options.track_origins: + valgrind_cmd += ['--track-origins=yes'] + + botan_test_options = ['--test-threads=1', '--run-memory-intensive-tests'] + cmd = valgrind_cmd + [options.test_binary] + botan_test_options + test + + start = time.time() + proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + duration = time.time() - start + + if options.verbose: + print("Testing '%s' took %.02fs" % (test, duration)) + sys.stdout.flush() + + if proc.returncode == 0: + return True # success + + print("FAILED: valgrind testing %s failed with error code %d" % (test, proc.returncode)) + print(proc.stdout.decode('utf8')) + print(proc.stderr.decode('utf8')) + return False + +def filter_tests(available, cmdline, options): + skip_tests = sum([x.split(',') for x in options.skip_tests], []) + + to_run = [] + + for test in available: + if test in skip_tests: + continue + + if test.startswith('pkcs11'): + continue + + if cmdline == [] or test in cmdline: + to_run.append(test) + + return to_run + +def split_list(list, n): + return [list[x:x+n] for x in range(0, len(list), n)] + +def main(args = None): + if args is None: + args = sys.argv + + parser = optparse.OptionParser() + + parser.add_option('--verbose', action='store_true', default=False, help='be noisy') + + parser.add_option('--test-binary', metavar='PATH', default='./botan-test', + help='path to botan-test binary') + + parser.add_option('--jobs', metavar='J', default=get_concurrency(), + help='number of jobs to run in parallel (default %default)') + + parser.add_option('--with-leak-check', action='store_true', default=False, + help='enable full valgrind leak checks') + + parser.add_option('--track-origins', action='store_true', default=False, + help='enable origin tracking') + + parser.add_option('--skip-tests', metavar='TESTS', default=[], action='append', + help='skip the named tests') + + parser.add_option('--bunch', action='store_true', default=False, + help='run several test suites under each valgrind exec') + + (options, args) = parser.parse_args(args) + + tests = filter_tests(available_tests(options.test_binary), args[1:], options) + + jobs = int(options.jobs) + pool = ThreadPool(jobs) + + results = [] + + if options.bunch: + bunching = len(tests) // (jobs * 8) + + for test in split_list(tests, bunching): + results.append(pool.apply_async(run_valgrind, (options, test))) + else: + for test in tests: + results.append(pool.apply_async(run_valgrind, (options, [test]))) + + fail_cnt = 0 + for result in results: + if not result.get(): + fail_cnt += 1 + + if fail_cnt > 0: + print("%d tests failed" % (fail_cnt)) + return 1 + else: + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff -Nru botan3-3.7.1+dfsg/src/scripts/run_tls_attacker.py botan3-3.12.0+dfsg/src/scripts/run_tls_attacker.py --- botan3-3.7.1+dfsg/src/scripts/run_tls_attacker.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/run_tls_attacker.py 2026-05-07 01:38:28.000000000 +0000 @@ -54,11 +54,11 @@ print("Unknown --type %s" % (options.test_type)) return 1 - if os.access(cli_exe, os.X_OK) != True: + if not os.access(cli_exe, os.X_OK): print("Unable to find CLI tool at %s" % (cli_exe)) return 1 - if os.access(src_dir, os.X_OK) != True: + if not os.access(src_dir, os.X_OK): print("Unable to find src dir at %s" % (src_dir)) return 1 @@ -72,7 +72,7 @@ tls_attacker_testsuites = os.path.join(tls_attacker_dir, 'resources/testsuite') tls_fuzzer_workflows = os.path.join(tls_attacker_dir, 'resources/fuzzing/workflows') - if os.access(tls_attacker_jar, os.R_OK) != True: + if not os.access(tls_attacker_jar, os.R_OK): print("Unable to find TLS-Attacker jar at %s" % (tls_attacker_jar)) return 1 diff -Nru botan3-3.7.1+dfsg/src/scripts/run_tls_fuzzer.py botan3-3.12.0+dfsg/src/scripts/run_tls_fuzzer.py --- botan3-3.7.1+dfsg/src/scripts/run_tls_fuzzer.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/run_tls_fuzzer.py 2026-05-07 01:38:28.000000000 +0000 @@ -86,7 +86,7 @@ if script in results: continue - if proc.poll() != None: + if proc.poll() is not None: rv = proc.returncode results[script] = rv if rv == 0: diff -Nru botan3-3.7.1+dfsg/src/scripts/test_cli.py botan3-3.12.0+dfsg/src/scripts/test_cli.py --- botan3-3.7.1+dfsg/src/scripts/test_cli.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/test_cli.py 2026-05-07 01:38:28.000000000 +0000 @@ -262,6 +262,8 @@ if "\r\n" in stdout: stdout = stdout.replace("\r\n", "\n") + if "\r\n" in stderr: + stderr = stderr.replace("\r\n", "\n") if stderr: if expected_stderr is None: @@ -276,7 +278,8 @@ if expected_output is not None: if stdout != expected_output: logging.error("Got unexpected output running cmd %s %s", cmd, cmd_options, stack_info=True) - logging.info("Output lengths %d vs expected %d", len(stdout), len(expected_output)) + if len(stdout) != len(expected_output): + logging.info("Output lengths %d vs expected %d", len(stdout), len(expected_output)) logging.info("Got %s", stdout) logging.info("Exp %s", expected_output) @@ -305,7 +308,7 @@ logging.error("Bad prefix %s", prefix) if not ldflags.endswith(("-L%s/lib" % (prefix))): logging.error("Bad ldflags %s", ldflags) - if ("-I%s/include/botan-3" % (prefix)) not in cflags: + if ("-I%s/include/botan-3" % (prefix)) not in cflags and ("-I%s/include" % (prefix)) not in cflags: logging.error("Bad cflags %s", cflags) if "-lbotan-3" not in libs: logging.error("Bad libs %s", libs) @@ -322,12 +325,12 @@ version_re = re.compile(r'[0-9]\.[0-9]+\.[0-9](\-[a-z]+[0-9]+)?') if not version_re.match(output): - logging.error("Unexpected version output %s", output) + logging.error("Unexpected short version output %s", output) output = test_cli("version", ["--full"], None, None) - version_full_re = re.compile(r'Botan [0-9]\.[0-9]+\.[0-9](\-[a-z]+[0-9]+)? \(.* revision .*, distribution .*\)$') + version_full_re = re.compile(r'Botan [0-9]\.[0-9]+\.[0-9](\-[a-z]+[0-9]+)?( UNSAFE .* BUILD)? \(.*\)$') if not version_full_re.match(output): - logging.error("Unexpected version output %s", output) + logging.error("Unexpected long version output %s", output) def cli_is_prime_tests(_tmp_dir): test_cli("is_prime", "5", "5 is probably prime") @@ -337,6 +340,7 @@ def cli_gen_prime_tests(_tmp_dir): test_cli("gen_prime", "64", "15568813029901363163") test_cli("gen_prime", "128", "287193909494025008847286845478788766073") + test_cli("gen_prime", ["--hex", "64"], "0xD80F88F6ADBE67DB") def cli_cycle_counter(_tmp_dir): output = test_cli("cpu_clock", None, None) @@ -564,6 +568,11 @@ "Certificate did not validate - Certificate issuer not found") def cli_xmss_sign_tests(tmp_dir): + if os.linesep != '\n': + # This test is hashing the PEM encoding of the XMSS private key which + # will have a different value due to line endings + return + priv_key = os.path.join(tmp_dir, 'priv.pem') pub_key = os.path.join(tmp_dir, 'pub.pem') pub_key2 = os.path.join(tmp_dir, 'pub2.pem') @@ -577,41 +586,41 @@ test_cli("rng", ['--output=%s' % (msg)], "") test_cli("hash", ["--no-fsname", msg], "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855") - test_cli("keygen", ["--algo=XMSS", "--output=%s" % (priv_key)], "") - test_cli("hash", ["--no-fsname", priv_key], "1F040283F0D7D2156B06B7BE03FA5861035FF3BCC059671DB288162C04A94CED") + test_cli("keygen", ["--algo=XMSS", "--params=XMSS-SHA2_10_256", "--output=%s" % (priv_key)], "") + test_cli("hash", ["--no-fsname", priv_key], "737368AB8BFC6B0CDA0DF7FB6BD1DE48C2ABA81236F65E4E227920CECCC5F259") test_cli("pkcs8", "--pub-out --output=%s %s" % (pub_key, priv_key), "") test_cli("fingerprint", ['--no-fsname', pub_key], - "6F:C4:08:CB:C3:61:CC:49:8A:25:90:3B:2F:D4:4D:B8:7F:2F:27:06:8C:8F:01:E0:01:DB:42:1F:B4:09:09:D9") + "7B:9F:20:23:A8:FC:A7:BD:BA:F4:DE:58:3C:D8:68:52:D1:8E:16:C8:B4:33:B0:34:FE:42:37:16:AE:95:7B:83") # verify the key is updated after each signature: test_cli("sign", [priv_key, msg, "--output=%s" % (sig1)], "") test_cli("verify", [pub_key, msg, sig1], "Signature is valid") - test_cli("hash", ["--no-fsname", sig1], "9DEBA79CE9FDC4966D7BA7B05ABEC54E3C11BB1C2C2732F7658820F2CAE47646") - test_cli("hash", ["--no-fsname", priv_key], "A71507087530C85E9CF971CF3A305890B07B51519C405A2B3D0037C64D5802B1") + test_cli("hash", ["--no-fsname", sig1], "A37241040C0C7044DD502D92B69E8B931FF43FBC91CF4E3C869B23206EBFABF6") + test_cli("hash", ["--no-fsname", priv_key], "748E63766E8805A4CED94B7BC52F0A5EB2D6F8CECFD2CFC27C61DFA2C7DC1328") test_cli("sign", [priv_key, msg, "--output=%s" % (sig2)], "") test_cli("verify", [pub_key, msg, sig2], "Signature is valid") - test_cli("hash", ["--no-fsname", sig2], "803EC5D6BECDFB9DC676EE2EDFEFE3D71EE924343A2ED9D2D7BFF0A9D97D704E") - test_cli("hash", ["--no-fsname", priv_key], "D581F5BFDA65669A825165C7A9CF17D6D5C5DF349004BCB7416DCD1A5C0349A0") + test_cli("hash", ["--no-fsname", sig2], "D593C1D0ADED11422248AE16CA77828CD2161CA3FCE3D0118A78D188C637D883") + test_cli("hash", ["--no-fsname", priv_key], "745903AC3BB7F55A6003BB59B13FC3BB48F4ACCAC7FE5888DD38031C55785B70") # private key updates, public key is unchanged: test_cli("pkcs8", "--pub-out --output=%s %s" % (pub_key2, priv_key), "") test_cli("fingerprint", ['--no-fsname', pub_key2], - "6F:C4:08:CB:C3:61:CC:49:8A:25:90:3B:2F:D4:4D:B8:7F:2F:27:06:8C:8F:01:E0:01:DB:42:1F:B4:09:09:D9") + "7B:9F:20:23:A8:FC:A7:BD:BA:F4:DE:58:3C:D8:68:52:D1:8E:16:C8:B4:33:B0:34:FE:42:37:16:AE:95:7B:83") # verify that key is updated when creating a self-signed certificate test_cli("gen_self_signed", [priv_key, "Root", "--ca", "--path-limit=2", "--output="+root_crt], "") - test_cli("hash", ["--no-fsname", priv_key], "ACFD94CDF5D0674EE5489039CF70850A1FFF95480A94E8C6C6FD2BF006909D07") + test_cli("hash", ["--no-fsname", priv_key], "364744707707A05F9348848EF473CCCE600D337E9715EB5D899EE392E1B49FD7") # verify that key is updated after signing a certificate request test_cli("gen_pkcs10", "%s Intermediate --ca --output=%s" % (priv_key, int_csr)) - test_cli("hash", ["--no-fsname", priv_key], "BE6F8F868DB495D95F73B50A370A218225253048E2F1C7C3E286568FDE203700") + test_cli("hash", ["--no-fsname", priv_key], "C460DC6B4BAC18360C61F93F8E33762AECEDBC864E0D9EA7A2561A8B8CC04A89") # verify that key is updated after issuing a certificate test_cli("sign_cert", "%s %s %s --output=%s" % (root_crt, priv_key, int_csr, int_crt)) - test_cli("hash", ["--no-fsname", priv_key], "8D3B736D8A708C342F9263163E0E3BAFE4132F74AE53A8EDF78074422CF80496") + test_cli("hash", ["--no-fsname", priv_key], "DDFC14EADFC7EDCC569D038F6BCBBC359D035B1CBB7BB0814E134AA5BB8FB8C8") test_cli("cert_verify", "%s %s" % (int_crt, root_crt), "Certificate passes validation checks") @@ -707,7 +716,8 @@ rngs = ['system', 'auto', 'entropy'] # execute ESDM tests only on Linux - if platform.system() == "Linux": + + if 'BOTAN_BUILD_WITH_ESDM' in os.environ: rngs += ['esdm-full', 'esdm-pr'] for rng in rngs: @@ -921,8 +931,8 @@ if len(lines) != 2: logging.error('Unexpected output from dl_group_info') - for l in lines: - if not dl_output.match(l): + for line in lines: + if not dl_output.match(line): logging.error('Unexpected output from dl_group_info') @@ -936,11 +946,7 @@ G = 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296,0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5""" secp256r1_pem = """-----BEGIN EC PARAMETERS----- -MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP////////// -/////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12Ko6 -k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDydwN9 -gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1AiEA -/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE= +BggqhkjOPQMBBw== -----END EC PARAMETERS-----""" test_cli("ec_group_info", "secp256r1", secp256r1_info) @@ -1028,7 +1034,9 @@ for i in range(test_inputs): output_file = os.path.join(data_dir, "invalid%d" % i) - ctext = bytes([i] * 128) + ctext = bytearray(os.urandom(1024 // 8)) + # Clear high bit so ciphertext is smaller than the modulus + ctext[0] &= 0x7F with open(output_file, 'bw') as out: out.write(ctext) @@ -1118,6 +1126,7 @@ psk = "FEEDFACECAFEBEEF" psk_identity = "test-psk" + psk_prf = "SHA-384" class TestConfig: def __init__(self, name, protocol_version, policy, **kwargs): @@ -1128,6 +1137,7 @@ self.expect_error = kwargs.get("expect_error", False) self.psk = kwargs.get("psk") self.psk_identity = kwargs.get("psk_identity") + self.psk_prf = kwargs.get("psk_prf") configs = [ # Explicitly testing x448-based key exchange against ourselves, as Bogo test @@ -1156,8 +1166,9 @@ TestConfig("PSK TLS 1.2", "1.2", "allow_tls12=true\nallow_tls13=false\nkey_exchange_methods=ECDHE_PSK\n", psk=psk, psk_identity=psk_identity, stdout_regex=f'Handshake complete, TLS v1\\.2.*\nUtilized PSK identity: {psk_identity}.*'), + TestConfig("PSK TLS 1.3", "1.3", "allow_tls12=false\nallow_tls13=true\nkey_exchange_methods=ECDHE_PSK\n", - psk=psk, psk_identity=psk_identity, + psk=psk, psk_identity=psk_identity, psk_prf=psk_prf, stdout_regex=f'Handshake complete, TLS v1\\.3.*\nUtilized PSK identity: {psk_identity}.*'), TestConfig("Kyber KEM", "1.3", "allow_tls12=false\nallow_tls13=true\nkey_exchange_groups=ML-KEM-768"), @@ -1165,11 +1176,12 @@ ] class TestServer(AsyncTestProcess): - def __init__(self, tmp_dir, port, psk, psk_identity, clients=0): + def __init__(self, tmp_dir, port, psk, psk_identity, psk_prf, clients=0): super().__init__("Server") self.port = port self.psk = psk self.psk_identity = psk_identity + self.psk_prf = psk_prf self.clients = clients self.cert_suite = ServerCertificateSuite(tmp_dir, "secp256r1", "SHA-384") @@ -1187,6 +1199,7 @@ server_cmd = [CLI_PATH, "tls_server", f"--max-clients={self.clients}", f"--port={self.port}", f"--policy={self.policy}", f"--psk={self.psk}", f"--psk-identity={self.psk_identity}", + f"--psk-prf={self.psk_prf}", self.cert_suite.cert, self.cert_suite.private_key] await self._launch(server_cmd, b'Listening for new connections') @@ -1242,6 +1255,8 @@ f'--tls-version={self.config.protocol_version}', f'--policy={self.policy}'] if self.config.psk: client_cmd += [f'--psk={self.config.psk}', f'--psk-identity={self.config.psk_identity}'] + if self.config.psk_prf: + client_cmd += [f'--psk-prf={self.config.psk_prf}'] await self._launch(client_cmd, b'Handshake complete' if not self.config.expect_error else None) @@ -1251,7 +1266,7 @@ await self._finalize() async def run_async_test(): - async with TestServer(tmp_dir, port_for('tls_server'), psk, psk_identity, len(configs)) as server: + async with TestServer(tmp_dir, port_for('tls_server'), psk, psk_identity, psk_prf, len(configs)) as server: errors = 0 for tls_config in configs: logging.debug("Running test for %s in TLS %s mode", tls_config.name, tls_config.protocol_version) @@ -1339,18 +1354,18 @@ test_cfg += [ TestConfig("test.openquantumsafe.org", "x25519/ML-KEM-768", port=oqsp['X25519MLKEM768'], ca=oqs_test_ca), TestConfig("test.openquantumsafe.org", "secp256r1/ML-KEM-768", port=oqsp['SecP256r1MLKEM768'], ca=oqs_test_ca), - - # Currently oqs did not adopt the 0x0200, 0x0201 and 0x0202 code point defined in draft-connolly-tls-mlkem-key-agreement-05. - # Neither did they deploy a new server that re-assigns the respective Frodo code points that used those before. - # TODO: enable those tests once the code point are re-assigned by the OQS test server - # TestConfig("test.openquantumsafe.org", "ML-KEM-512", port=oqsp['mlkem512'], ca=oqs_test_ca), - # TestConfig("test.openquantumsafe.org", "ML-KEM-768", port=oqsp['mlkem768'], ca=oqs_test_ca), - # TestConfig("test.openquantumsafe.org", "ML-KEM-1024", port=oqsp['mlkem1024'], ca=oqs_test_ca), - # TestConfig("test.openquantumsafe.org", "eFrodoKEM-640-SHAKE", port=oqsp['frodo640shake'], ca=oqs_test_ca), + TestConfig("test.openquantumsafe.org", "ML-KEM-512", port=oqsp['mlkem512'], ca=oqs_test_ca), + TestConfig("test.openquantumsafe.org", "ML-KEM-768", port=oqsp['mlkem768'], ca=oqs_test_ca), + TestConfig("test.openquantumsafe.org", "ML-KEM-1024", port=oqsp['mlkem1024'], ca=oqs_test_ca), + + # We track OQS's code point allocations for FrodoKEM and hybrids thereof. + # All are defined in TLS's private code point section (0xFE00 - 0xFFFF) + # and may change in the future. + TestConfig("test.openquantumsafe.org", "eFrodoKEM-640-SHAKE", port=oqsp['frodo640shake'], ca=oqs_test_ca), TestConfig("test.openquantumsafe.org", "eFrodoKEM-976-SHAKE", port=oqsp['frodo976shake'], ca=oqs_test_ca), TestConfig("test.openquantumsafe.org", "eFrodoKEM-1344-SHAKE", port=oqsp['frodo1344shake'], ca=oqs_test_ca), - # TestConfig("test.openquantumsafe.org", "eFrodoKEM-640-AES", port=oqsp['frodo640aes'], ca=oqs_test_ca), - # TestConfig("test.openquantumsafe.org", "eFrodoKEM-976-AES", port=oqsp['frodo976aes'], ca=oqs_test_ca), + TestConfig("test.openquantumsafe.org", "eFrodoKEM-640-AES", port=oqsp['frodo640aes'], ca=oqs_test_ca), + TestConfig("test.openquantumsafe.org", "eFrodoKEM-976-AES", port=oqsp['frodo976aes'], ca=oqs_test_ca), TestConfig("test.openquantumsafe.org", "eFrodoKEM-1344-AES", port=oqsp['frodo1344aes'], ca=oqs_test_ca), TestConfig("test.openquantumsafe.org", "x25519/eFrodoKEM-640-SHAKE", port=oqsp['x25519_frodo640shake'], ca=oqs_test_ca), TestConfig("test.openquantumsafe.org", "x25519/eFrodoKEM-640-AES", port=oqsp['x25519_frodo640aes'], ca=oqs_test_ca), @@ -1429,10 +1444,7 @@ asyncio.run(run_async_test()) def cli_tls_proxy_tests(tmp_dir): - # This was disabled in GH #3845 due to flakyness, then thought possibly - # fixed and enabled again in Gh #4178. However the test still occasionally - # fails. Disable it again pending diagnosis... - if not run_socket_tests() or platform.system() == 'Windows' or not check_for_command("tls_proxy"): + if not run_socket_tests() or not check_for_command("tls_proxy"): return server_port = port_for('tls_proxy_backend') @@ -1615,12 +1627,25 @@ output = test_cli("tls_client_hello", ["--hex", "-"], None, chello) test_cli("hash", ["--no-fsname", "--algo=SHA-256", "-"], output_hash, output) -def cli_speed_pk_tests(_tmp_dir): +def cli_speed_pk_fast_tests(_tmp_dir): msec = 1 pk_algos = ["ECDSA", "ECDH", "SM2", "ECKCDSA", "ECGDSA", "GOST-34.10", - "DH", "DSA", "ElGamal", "Ed25519", "Ed448", "X25519", "X448", - "RSA", "RSA_keygen", "XMSS", "Kyber", "Dilithium", "SLH-DSA"] + "ML-KEM", "ML-DSA", "Ed25519", "Ed448", "X25519", "X448", + "DH", "DSA", "ElGamal"] + + output = test_cli("speed", ["--msec=%d" % (msec)] + pk_algos, None).split('\n') + + # ECDSA-secp256r1 106 keygen/sec; 9.35 ms/op 37489733 cycles/op (1 op in 9 ms) + format_re = re.compile(r'^.* [0-9]+ ([A-Za-z0-9 ]+)/sec; [0-9]+\.[0-9]+ ms/op .*\([0-9]+ (op|ops) in [0-9\.]+ ms\)') + for line in output: + if format_re.match(line) is None: + logging.error("Unexpected line %s", line) + +def cli_speed_pk_slow_tests(_tmp_dir): + msec = 1 + + pk_algos = ["RSA", "RSA_keygen", "XMSS", "SLH-DSA"] output = test_cli("speed", ["--msec=%d" % (msec)] + pk_algos, None).split('\n') @@ -1632,7 +1657,7 @@ def cli_speed_pbkdf_tests(_tmp_dir): msec = 1 - pbkdf_ops = ['bcrypt', 'passhash9', 'argon2'] + pbkdf_ops = ['bcrypt', 'passhash9', 'argon2', 'pbkdf2', 'scrypt'] format_re = re.compile(r'^.* [0-9]+ /sec; [0-9]+\.[0-9]+ ms/op .*\([0-9]+ (op|ops) in [0-9]+(\.[0-9]+)? ms\)') for op in pbkdf_ops: @@ -1644,7 +1669,7 @@ def cli_speed_table_tests(_tmp_dir): msec = 1 - version_re = re.compile(r'^Botan 3\.[0-9]+\.[0-9](\-.*[0-9]+)? \(.*, revision .*, distribution .*\)') + version_re = re.compile(r'Botan [0-9]\.[0-9]+\.[0-9](\-[a-z]+[0-9]+)?( UNSAFE .* BUILD)? \(.*\)$') cpuid_re = re.compile(r'^CPUID: [a-z_0-9 ]*$') format_re = re.compile(r'^.* buffer size [0-9]+ bytes: [0-9]+\.[0-9]+ MiB\/sec .*\([0-9]+\.[0-9]+ MiB in [0-9]+\.[0-9]+ ms\)') tbl_hdr_re = re.compile(r'^algo +operation +1024 bytes$') @@ -1652,6 +1677,12 @@ output = test_cli("speed", ["--format=table", "--msec=%d" % (msec), "AES-128"], None).split('\n') + # Mac is somewhat unusual in that there is (at least for now) a parallel provider + # that is distinct from but does not replace the usual algorithm implementations. + # Just zap out lines referencing this provider from the output so that the remainder + # has the same format as other platforms + output = [line for line in output if "commoncrypto" not in line] + if len(output) != 11: logging.error('Unexpected number of lines from table output') @@ -1701,7 +1732,8 @@ msec = 1 # these all have a common output format math_ops = ['mp_mul', 'mp_div', 'mp_div10', 'modexp', 'random_prime', 'inverse_mod', - 'rfc3394', 'fpe_fe1', 'ecdsa_recovery', 'ecc', 'bn_redc', 'primality_test'] + 'rfc3394', 'fpe_fe1', 'ecdsa_recovery', 'bn_redc', 'primality_test', + 'ecc_misc', 'ecc_mul', 'ecc_mul2', 'ecc_scalar', 'ecc_h2c'] format_re = re.compile(r'^.* [0-9]+ /sec; [0-9]+\.[0-9]+ ms/op .*\([0-9]+ (op|ops) in [0-9]+(\.[0-9]+)? ms\)') for op in math_ops: @@ -1767,7 +1799,10 @@ if len(json_blob) < 2: logging.error("Unexpected size for JSON output") - for b in json_blob: + if 'version' not in json_blob[0]: + logging.error("Didn't find version header in first JSON object") + + for b in json_blob[1:]: for field in ['algo', 'op', 'events', 'bps', 'buf_size', 'nanos']: if field not in b: logging.error('Missing field %s in JSON record %s', field, b) @@ -1797,6 +1832,7 @@ parser.add_option('--threads', action='store', type='int', default=0) parser.add_option('--run-slow-tests', action='store_true', default=False) parser.add_option('--run-online-tests', action='store_true', default=False) + parser.add_option('--skip-tls-proxy-test', action='store_true', default=False) parser.add_option('--test-data-dir', default='.') (options, args) = parser.parse_args(args) @@ -1830,12 +1866,8 @@ return 1 slow_test_fns = [ - cli_speed_tests, - cli_speed_pk_tests, - cli_speed_math_tests, + cli_speed_pk_slow_tests, cli_speed_pbkdf_tests, - cli_speed_table_tests, - cli_speed_invalid_option_tests, cli_xmss_sign_tests, ] @@ -1847,12 +1879,12 @@ cli_base64_tests, cli_bcrypt_tests, cli_cc_enc_tests, - cli_cycle_counter, - cli_cert_issuance_tests, cli_cert_issuance_alternative_algos_tests, + cli_cert_issuance_tests, cli_compress_tests, cli_config_tests, cli_cpuid_tests, + cli_cycle_counter, cli_dl_group_info_tests, cli_ec_group_info_tests, cli_entropy_tests, @@ -1874,13 +1906,17 @@ cli_rng_tests, cli_roughtime_check_tests, cli_roughtime_tests, + cli_speed_invalid_option_tests, + cli_speed_math_tests, + cli_speed_pk_fast_tests, + cli_speed_table_tests, + cli_speed_tests, cli_timing_test_tests, cli_tls_ciphersuite_tests, cli_tls_client_hello_tests, cli_tls_http_server_tests, - cli_tls_proxy_tests, - cli_tls_socket_tests, cli_tls_online_pqc_hybrid_tests, + cli_tls_socket_tests, cli_trust_root_tests, cli_tss_tests, cli_uuid_tests, @@ -1891,9 +1927,12 @@ test_fns = [] if options.run_slow_tests: - test_fns = slow_test_fns + fast_test_fns - else: - test_fns = fast_test_fns + test_fns += slow_test_fns + + test_fns += fast_test_fns + + if not options.skip_tls_proxy_test: + test_fns.append(cli_tls_proxy_tests) global ONLINE_TESTS ONLINE_TESTS = options.run_online_tests diff -Nru botan3-3.7.1+dfsg/src/scripts/test_cli_crypt.py botan3-3.12.0+dfsg/src/scripts/test_cli_crypt.py --- botan3-3.7.1+dfsg/src/scripts/test_cli_crypt.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/test_cli_crypt.py 2026-05-07 01:38:28.000000000 +0000 @@ -51,7 +51,7 @@ current_testcase[key] = value if current_testcase_number != last_testcase_number: - if not current_group_name in self.data: + if current_group_name not in self.data: self.data[current_group_name] = [] if len(current_testcase) != 0: self.data[current_group_name].append(current_testcase) diff -Nru botan3-3.7.1+dfsg/src/scripts/test_fuzzers.py botan3-3.12.0+dfsg/src/scripts/test_fuzzers.py --- botan3-3.7.1+dfsg/src/scripts/test_fuzzers.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/test_fuzzers.py 2026-05-07 01:38:28.000000000 +0000 @@ -120,10 +120,10 @@ for fuzzer in sorted(list(fuzzers_with_corpus)): fuzzer_bin = os.path.join(fuzzer_dir, fuzzer) corpus_subdir = os.path.join(corpus_dir, fuzzer) - corpus_files = [os.path.join(corpus_subdir, l) for l in sorted(list(os.listdir(corpus_subdir)))] + corpus_files = [os.path.join(corpus_subdir, fsname) for fsname in sorted(list(os.listdir(corpus_subdir)))] # We have to do this hack because multiprocessing's Pool.map doesn't support - # passing any initial arguments, just the single iteratable + # passing any initial arguments, just the single iterable map_args = [(fuzzer_bin, f) for f in corpus_files] start = time.time() @@ -170,7 +170,7 @@ else: corpus_subdir = random_corpus_dir - corpus_files = [os.path.join(corpus_subdir, l) for l in sorted(list(os.listdir(corpus_subdir)))] + corpus_files = [os.path.join(corpus_subdir, fsname) for fsname in sorted(list(os.listdir(corpus_subdir)))] if fuzzer in slow_fuzzers: corpus_files = corpus_files[:random_corpus_size_for_slow_fuzzers] diff -Nru botan3-3.7.1+dfsg/src/scripts/test_python.py botan3-3.12.0+dfsg/src/scripts/test_python.py --- botan3-3.7.1+dfsg/src/scripts/test_python.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/test_python.py 2026-05-07 01:38:28.000000000 +0000 @@ -12,6 +12,7 @@ import platform import argparse import sys +import time from itertools import permutations # Starting with Python 3.8 DLL search locations are more restricted on Windows. @@ -132,7 +133,7 @@ hmac = botan.MsgAuthCode('HMAC(SHA-256)') self.assertEqual(hmac.algo_name(), 'HMAC(SHA-256)') self.assertEqual(hmac.minimum_keylength(), 0) - self.assertEqual(hmac.maximum_keylength(), 4096) + self.assertEqual(hmac.maximum_keylength(), 8192) expected = hex_decode('A21B1F5D4CF4F73A4DD939750F7A066A7F98CC131CB16A6692759021CFAB8181') @@ -179,6 +180,107 @@ user_rng.add_entropy('seed material...') + def test_drbg(self): + seed = bytes.fromhex( + '000102030405060708090a0b0c0d0e0f' + '101112131415161718191a1b1c1d1e1f' + '2021222324252627') + + drbg = botan.RandomNumberGenerator.drbg('HMAC_DRBG(SHA-256)', seed) + out = drbg.get(32) + self.assertEqual(out.hex(), + '3f56de71d4c5155cea215d45b6a8a161e623ca06345ce68c29c8cb3326c64595') + + # generate_with_input mixes additional data before generating + drbg2 = botan.RandomNumberGenerator.drbg('HMAC_DRBG(SHA-256)', seed) + out2 = drbg2.generate_with_input(32, b'additional input') + self.assertEqual(out2.hex(), + 'e327411d65fd4004cd78ee6849177bc205a11a1ba1191c9d648890a7a3aa5dae') + + # add_entropy reseeds the DRBG state + drbg3 = botan.RandomNumberGenerator.drbg('HMAC_DRBG(SHA-256)', seed) + drbg3.add_entropy(bytes(32)) + out3 = drbg3.get(32) + self.assertEqual(out3.hex(), + '404b055a2d802320c5a288e0f46559c554425d70938326bf408efb361c7424bf') + + def test_custom_rng(self): + class CustomRngHandler: + def __init__(self, hardcoded=False): + self._entropy_pool = b'' + self._hardcoded = hardcoded + + @property + def entropy_pool(self): + return self._entropy_pool + + def get_entropy(self, length): + if self._hardcoded: + return b'x' * length + + if length > len(self._entropy_pool): + raise botan.BotanException("Not enough entropy in pool", -10) + + entropy = self._entropy_pool[:length] + self._entropy_pool = self._entropy_pool[length:] + return entropy + + def add_entropy(self, data): + self._entropy_pool += data + + # callback function validation + self.assertRaises(botan.BotanException, lambda: botan.RandomNumberGenerator("custom")) + self.assertRaises(botan.BotanException, lambda: botan.RandomNumberGenerator("custom", get_callback=None)) + self.assertRaises(botan.BotanException, lambda: botan.RandomNumberGenerator("custom", add_entropy_callback=lambda x: None)) + self.assertRaises(botan.BotanException, lambda: botan.RandomNumberGenerator("custom", get_callback=lambda x: b'x' * x, unexpected="unexpected")) + + my_custom_rng1 = CustomRngHandler() + my_custom_rng2 = CustomRngHandler() + my_custom_rng3 = CustomRngHandler(hardcoded=True) + custom_rng1 = botan.RandomNumberGenerator("custom", + get_callback=my_custom_rng1.get_entropy, + add_entropy_callback=my_custom_rng1.add_entropy) + custom_rng2 = botan.RandomNumberGenerator("custom", + get_callback=my_custom_rng2.get_entropy, + add_entropy_callback=my_custom_rng2.add_entropy) + + # omit the add_entropy_callback as it is optional + custom_rng3 = botan.RandomNumberGenerator("custom", get_callback=my_custom_rng3.get_entropy) + + self.assertRaises(botan.BotanException, lambda: custom_rng1.get(32)) + self.assertEqual(len(my_custom_rng1.entropy_pool), 0) + self.assertEqual(len(my_custom_rng2.entropy_pool), 0) + + custom_rng1.add_entropy(b'entropy') + self.assertEqual(len(my_custom_rng1.entropy_pool), 7) + self.assertEqual(len(my_custom_rng2.entropy_pool), 0) + self.assertEqual(len(my_custom_rng3.entropy_pool), 0) + + custom_rng2.add_entropy(b'entropie') + self.assertEqual(len(my_custom_rng1.entropy_pool), 7) + self.assertEqual(len(my_custom_rng2.entropy_pool), 8) + self.assertEqual(len(my_custom_rng3.entropy_pool), 0) + + custom_rng3.add_entropy(b'into the void') + self.assertEqual(len(my_custom_rng1.entropy_pool), 7) + self.assertEqual(len(my_custom_rng2.entropy_pool), 8) + self.assertEqual(len(my_custom_rng3.entropy_pool), 0) + + self.assertEqual(custom_rng1.get(7), b'entropy') + self.assertEqual(len(my_custom_rng1.entropy_pool), 0) + self.assertEqual(len(my_custom_rng2.entropy_pool), 8) + self.assertEqual(len(my_custom_rng3.entropy_pool), 0) + + self.assertEqual(custom_rng2.get(8), b'entropie') + self.assertEqual(len(my_custom_rng1.entropy_pool), 0) + self.assertEqual(len(my_custom_rng2.entropy_pool), 0) + self.assertEqual(len(my_custom_rng3.entropy_pool), 0) + + self.assertEqual(custom_rng3.get(13), b'x' * 13) + self.assertEqual(len(my_custom_rng1.entropy_pool), 0) + self.assertEqual(len(my_custom_rng2.entropy_pool), 0) + self.assertEqual(len(my_custom_rng3.entropy_pool), 0) + def test_esdm_rng(self): try: esdm_rng = botan.RandomNumberGenerator("esdm-full") @@ -293,6 +395,37 @@ self.assertEqual(hex_encode(sha256.final()), "08bfce15fd2406114825ee6f770a06b1b00c129cb48fcddc54ef58b5de48bdf5") + def test_xof(self): + try: + _h = botan.XOF('NoSuchXof') + except botan.BotanException as e: + self.assertEqual(str(e), "botan_xof_init failed: -40 (Not implemented)") + + shake128 = botan.XOF('SHAKE-128') + self.assertEqual(shake128.algo_name(), 'SHAKE-128') + self.assertEqual(shake128.block_size(), 168) + self.assertTrue(shake128.accepts_input()) + + shake128.update('ignore this please') + shake128.clear() + shake128.update(hex_decode("32a36452a646beba4bf611e0bf2cfcb6")) + + shake128_2 = shake128.copy_state() + self.assertTrue(shake128_2.accepts_input()) + + self.assertEqual(hex_encode(shake128.output(8)), "3df0ccef456072f3") + self.assertFalse(shake128.accepts_input()) + self.assertEqual(hex_encode(shake128.output(8)), "daa5642d4b02bd5f") + + self.assertEqual(hex_encode(shake128_2.output(4)), "3df0ccef") + self.assertFalse(shake128_2.accepts_input()) + shake128_3 = shake128_2.copy_state() + self.assertFalse(shake128_3.accepts_input()) + self.assertEqual(hex_encode(shake128_3.output(12)), "456072f3daa5642d4b02bd5f") + + with self.assertRaises(botan.BotanException): + shake128.update('no more input accepted') + def test_cipher(self): for mode in ['AES-128/CTR-BE', 'Serpent/GCM', 'ChaCha20Poly1305', 'AES-128/CBC/PKCS7']: try: @@ -400,6 +533,8 @@ rsapub = botan.PublicKey.load(rsa_pub_pem) self.assertEqual(rsapub.to_pem(), rsa_pub_pem) + with self.assertRaisesRegex(botan.BotanException, r".*Only ECC keys.*"): + rsapub.used_explicit_encoding() n = 0xB5AD8818DCA1F256FF8FAB0888D0667D95DF2098B0D201A4C75590D3EBDFA159DD91C64AFDA082609EF885B2D1F4DC055C8FF9FA371C2F3398E0B612C603151131C81DB322C8D15E53EB56B4DF7325F05046889CB25021DE4282E16B9B28F5CBB2B8DDECE0F8E4E8A77F674F26AE92B7220920A1FBE43F51039A9C79D1F1CB6B e = 0x10001 @@ -410,6 +545,20 @@ self.assertEqual(rsapub2.get_field("n"), n) self.assertEqual(rsapub2.get_field("e"), e) + def test_privkey_load_der(self): + # Verify that PrivateKey.load() works with both PEM and DER + rng = botan.RandomNumberGenerator() + priv = botan.PrivateKey.create('RSA', '2048', rng) + + pem = priv.export(True) + der = priv.export(False) + + priv_from_pem = botan.PrivateKey.load(pem) + self.assertEqual(priv_from_pem.to_pem(), pem) + + priv_from_der = botan.PrivateKey.load(der) + self.assertEqual(priv_from_der.to_pem(), pem) + def test_key_crypto(self): rng = botan.RandomNumberGenerator() priv = botan.PrivateKey.create('RSA', '1024', rng) @@ -430,9 +579,24 @@ dec3 = botan.PrivateKey.load(pem3, passphrase) self.assertEqual(dec3.export(is_pem), ref_val) + def test_stateful_operations(self): + rng = botan.RandomNumberGenerator() + priv1 = botan.PrivateKey.create('RSA', '1024', rng) + self.assertEqual(priv1.stateful_operation(), False) + + try: + remaining = priv1.remaining_operations() + except botan.BotanException as e: + self.assertEqual(str(e), "botan_privkey_remaining_operations failed: -3 (No value available)") + + priv2 = botan.PrivateKey.create('XMSS', 'XMSS-SHA2_10_256', rng) + self.assertEqual(priv2.stateful_operation(), True) + + remaining = priv2.remaining_operations() + self.assertEqual(remaining, 1024) + def test_check_key(self): - # valid (if rather small) RSA key - n = 273279220906618527352827457840955116141 + n = 0xc64bad5b1c8ec30ba72ed8b5374ef8989fefb771c7c9e68c3598d73170df75403f7571f5ebd2f69008741fcd04319a7f10a296b2fbd1038bbfc5e4d83ef17d6a495cfeef9d779b9910bfb48bd891860f85fa26ac9a420d2d3c6c2ec30636fea3fbdb5ef3a372e5f217b211deb59229b01d43ee71b1e87e5ffe185a9eb3ec696f e = 0x10001 rng = botan.RandomNumberGenerator() @@ -444,7 +608,61 @@ try: rsapub = botan.PublicKey.load_rsa(n - 1, e) except botan.BotanException as e: - self.assertEqual(str(e), "botan_pubkey_load_rsa failed: -1 (Invalid input): Invalid RSA public key parameters") + self.assertEqual(str(e), "botan_pubkey_load_rsa failed: -1 (Invalid input): Invalid RSA public key modulus") + + def _pksign_roundtrips(self, sk, pk, param_str): + def verify_positive_and_negative(verifier, sig): + self.assertTrue(verifier.check_signature(sig)) + invalid_sig = bytes(sig[0] ^ 0xFF) + sig[1:] + self.assertFalse(verifier.check_signature(invalid_sig)) + + rng = botan.RandomNumberGenerator() + msg = "test message" + raw_bytes = bytes.fromhex("8100112233445566778899AABBCCDDEEFF") # these bytes can't be decoded as UTF-8! + + # Check that update() takes UTF-8 data + signer = botan.PKSign(sk, param_str) + signer.update(msg[:3]) + signer.update(msg[3:]) + sig = signer.finish(rng) + + verify = botan.PKVerify(pk, param_str) + verify.update(msg[:5]) + verify.update(msg[5:]) + verify_positive_and_negative(verify, sig) + + # Check that update() takes raw bytes + signer = botan.PKSign(sk, param_str) + signer.update(raw_bytes[:7]) + signer.update(raw_bytes[7:]) + sig = signer.finish(rng) + + verify = botan.PKVerify(pk, param_str) + verify.update(raw_bytes) + verify_positive_and_negative(verify, sig) + + # Check that update() can take both UTF-8 and raw bytes + signer = botan.PKSign(sk, param_str) + signer.update(msg) + sig = signer.finish(rng) + + verify = botan.PKVerify(pk, param_str) + verify.update(msg.encode("utf-8")) + self.assertTrue(verify.check_signature(sig)) + + # Check with an empty message + signer = botan.PKSign(sk, param_str) + sig = signer.finish(rng) + + verify = botan.PKVerify(pk, param_str) + verify_positive_and_negative(verify, sig) + + @staticmethod + def _ecc_sec1_convert_to_compressed(uncompressed_sec1): + assert uncompressed_sec1[0] == 0x04 + is_odd = uncompressed_sec1[-1] & 0x01 != 0 + x = uncompressed_sec1[1:1 + (len(uncompressed_sec1) - 1) // 2] + return (b"\x03" if is_odd else b"\x02") + x def test_rsa(self): rng = botan.RandomNumberGenerator() @@ -477,24 +695,7 @@ self.assertEqual(ptext, symkey) - signer = botan.PKSign(rsapriv, 'EMSA4(SHA-384)') - - signer.update('messa') - signer.update('ge') - sig = signer.finish(botan.RandomNumberGenerator()) - - verify = botan.PKVerify(rsapub, 'EMSA4(SHA-384)') - - verify.update('mess') - verify.update('age') - self.assertTrue(verify.check_signature(sig)) - - verify.update('mess of things') - verify.update('age') - self.assertFalse(verify.check_signature(sig)) - - verify.update('message') - self.assertTrue(verify.check_signature(sig)) + self._pksign_roundtrips(rsapriv, rsapub, "PSS(SHA-384)") salt = b'saltyseawater' kem_e = botan.KemEncrypt(rsapub, 'KDF2(SHA-256)') @@ -525,15 +726,21 @@ def test_ecdsa(self): rng = botan.RandomNumberGenerator() - hash_fn = 'EMSA1(SHA-256)' + hash_fn = 'SHA-256' group = 'secp256r1' msg = 'test message' + if not botan.ECGroup.supports_named_group(group): + self.skipTest("No secp256r1 group support in this build") + return + priv = botan.PrivateKey.create('ECDSA', group, rng) pub = priv.get_public_key() self.assertEqual(pub.get_field('public_x'), priv.get_field('public_x')) self.assertEqual(pub.get_field('public_y'), priv.get_field('public_y')) + self._pksign_roundtrips(priv, pub, hash_fn) + signer = botan.PKSign(priv, hash_fn, True) signer.update(msg) signature = signer.finish(rng) @@ -547,55 +754,60 @@ verifier.update(msg) self.assertTrue(verifier.check_signature(signature)) + # Load public key from components pub_x = pub.get_field('public_x') pub_y = priv.get_field('public_y') pub2 = botan.PublicKey.load_ecdsa(group, pub_x, pub_y) - verifier = botan.PKVerify(pub2, hash_fn, True) - verifier.update(msg) - self.assertTrue(verifier.check_signature(signature)) + self._pksign_roundtrips(priv, pub2, hash_fn) + # Load private key from component priv2 = botan.PrivateKey.load_ecdsa(group, priv.get_field('x')) - signer = botan.PKSign(priv2, hash_fn, True) - # sign empty message - signature = signer.finish(rng) + self._pksign_roundtrips(priv2, pub, hash_fn) - # verify empty message - self.assertTrue(verifier.check_signature(signature)) + # Load public key from SEC.1 encoding + uncompressed_sec1 = pub.to_raw() + pub3 = botan.PublicKey.load_ecdsa_sec1(group, uncompressed_sec1) + self._pksign_roundtrips(priv, pub3, hash_fn) + + compressed_sec1 = BotanPythonTests._ecc_sec1_convert_to_compressed(uncompressed_sec1) + pub4 = botan.PublicKey.load_ecdsa_sec1(group, compressed_sec1) + self._pksign_roundtrips(priv, pub4, hash_fn) def test_sm2(self): rng = botan.RandomNumberGenerator() - hash_fn = 'EMSA1(SM3)' + hash_fn = 'SM3' group = 'sm2p256v1' - msg = 'test message' + + if not botan.ECGroup.supports_named_group(group): + self.skipTest("No sm2p256v1 group support in this build") + return priv = botan.PrivateKey.create('SM2', group, rng) pub = priv.get_public_key() self.assertEqual(pub.get_field('public_x'), priv.get_field('public_x')) self.assertEqual(pub.get_field('public_y'), priv.get_field('public_y')) - signer = botan.PKSign(priv, hash_fn) - signer.update(msg) - signature = signer.finish(rng) - - verifier = botan.PKVerify(pub, hash_fn) - verifier.update(msg) - self.assertTrue(verifier.check_signature(signature)) + self._pksign_roundtrips(priv, pub, hash_fn) + # Load public key from components pub_x = pub.get_field('public_x') pub_y = priv.get_field('public_y') pub2 = botan.PublicKey.load_sm2(group, pub_x, pub_y) - verifier = botan.PKVerify(pub2, hash_fn) - verifier.update(msg) - self.assertTrue(verifier.check_signature(signature)) + self._pksign_roundtrips(priv, pub2, hash_fn) + # Load private key from component priv2 = botan.PrivateKey.load_sm2(group, priv.get_field('x')) - signer = botan.PKSign(priv2, hash_fn) - # sign empty message - signature = signer.finish(rng) + self._pksign_roundtrips(priv2, pub, hash_fn) - # verify empty message - self.assertTrue(verifier.check_signature(signature)) + # Load public key from SEC.1 encoding + uncompressed_sec1 = pub.to_raw() + pub3 = botan.PublicKey.load_sm2_sec1(group, uncompressed_sec1) + self._pksign_roundtrips(priv, pub3, hash_fn) + + compressed_sec1 = BotanPythonTests._ecc_sec1_convert_to_compressed(uncompressed_sec1) + pub4 = botan.PublicKey.load_sm2_sec1(group, compressed_sec1) + self._pksign_roundtrips(priv, pub4, hash_fn) def test_ecdh(self): a_rng = botan.RandomNumberGenerator('user') @@ -603,7 +815,27 @@ kdf = 'KDF2(SHA-384)' + if botan.ECGroup.supports_application_specific_group(): + # This is a DER-encoded EC public key with an explicitly encoded group + # that is almost like secp256r1 but with a different prime modulus. + pub_almost_secp256r1 = bytes.fromhex("""308201333081ec06072a8648ce3d0201 + 3081e0020101302c06072a8648ce3d0101022100fd091059a6893635f900e9449d63 + f572b2aebc4cff7b4e5e33f1b200e8bbc1453044042002f6efa55976c9cb06ff16bb + 629c0a8d4d5143b40084b1a1cc0e4dff17443eb704205ac635d8aa3a93e7b3ebbd55 + 769886bc651d06b0cc53b0f63bce3c3e27d2604b0441040000000000000000000006 + 597fa94b1fd90000000000000000000000000000021b8c7dd77f9a95627922eceefe + a73f028f1ec95ba9b8fa95a3ad24bdf9fff414022100ffffffff00000000ffffffff + ffffffffbce6faada7179e84f3b9cac2fc6325510201010342000400000000000000 + 00000006597fa94b1fd90000000000000000000000000000021b8c7dd77f9a956279 + 22eceefea73f028f1ec95ba9b8fa95a3ad24bdf9fff414""") + + strange_pub = botan.PublicKey.load(pub_almost_secp256r1) + self.assertTrue(strange_pub.used_explicit_encoding()) + for grp in ['secp256r1', 'secp384r1', 'brainpool256r1']: + if not botan.ECGroup.supports_named_group(grp): + continue + a_priv = botan.PrivateKey.create('ECDH', grp, a_rng) b_priv = botan.PrivateKey.create('ECDH', grp, b_rng) @@ -620,6 +852,8 @@ self.assertEqual(a_op.public_value(), a_pub_pt) self.assertEqual(b_op.public_value(), b_pub_pt) self.assertEqual(a_pub_raw, a_pubv) + self.assertFalse(a_priv.get_public_key().used_explicit_encoding()) + self.assertFalse(b_priv.get_public_key().used_explicit_encoding()) salt = a_rng.get(8) + b_rng.get(8) @@ -639,6 +873,17 @@ a_raw = hex_encode(a_priv.to_raw()) self.assertEqual(int(a_raw, base=16), a_priv_x) + uncompressed_sec1 = a_priv.get_public_key().to_raw() + compressed_sec1 = BotanPythonTests._ecc_sec1_convert_to_compressed(uncompressed_sec1) + new_a_pub1 = botan.PublicKey.load_ecdh_sec1(grp, uncompressed_sec1) + new_a_pub2 = botan.PublicKey.load_ecdh_sec1(grp, compressed_sec1) + self.assertEqual(new_a_pub1.to_raw(), new_a_pub2.to_raw()) + self.assertEqual(new_a_pub1.to_raw(), a_priv.get_public_key().to_raw()) + b_op2 = botan.PKKeyAgreement(b_priv, kdf) + b_key2 = b_op2.agree(compressed_sec1, 32, salt) + self.assertEqual(b_key2, b_key) + + def test_rfc7748_kex(self): rng = botan.RandomNumberGenerator() @@ -662,25 +907,11 @@ def test_eddsa(self): rng = botan.RandomNumberGenerator() - msg = 'test message' for alg in ['Ed25519', 'Ed448']: priv = botan.PrivateKey.create(alg, '', rng) pub = priv.get_public_key() - - # Sign message - signer = botan.PKSign(priv, '') - signer.update(msg) - signature = signer.finish(rng) - - # Verify signature - verifier = botan.PKVerify(pub, '') - verifier.update(msg) - self.assertTrue(verifier.check_signature(signature)) - - # Verify invalid signature - verifier.update('not test message') - self.assertFalse(verifier.check_signature(signature)) + self._pksign_roundtrips(priv, pub, "") def test_certs(self): cert = botan.X509Cert(filename=test_data("src/tests/data/x509/ecc/isrg-root-x2.pem")) @@ -756,6 +987,40 @@ self.assertFalse(int04_1.is_revoked(rootcrl)) self.assertTrue(end21.is_revoked(int21crl)) + def test_crls(self): + rng = botan.RandomNumberGenerator() + now = int(time.time()) + + priv_pem = """ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgoVEKnWZw2Bfrf3MM +WLrfvRcAqq/sOf58jny37NLGQHShRANCAARageRLkKQEh1M86zvqeeesx2u9duLP +iWtHjIcunpiq6+IiB8IVu7Ncu6uPKoFS/mWzTvjgdNusmgNle9p3OAbE +-----END PRIVATE KEY-----""" + + ca_cert = botan.X509Cert(filename=test_data("src/tests/data/x509/crl/ca.crt")) + ca_key = botan.PrivateKey.load(priv_pem) + ca_pubkey = ca_key.get_public_key() + + sub1_cert = botan.X509Cert(filename=test_data("src/tests/data/x509/crl/sub1.crt")) + sub2_cert = botan.X509Cert(filename=test_data("src/tests/data/x509/crl/sub2.crt")) + + crl = botan.X509CRL.create(rng, ca_cert, ca_key, now, 600) + self.assertTrue(crl.verify(ca_pubkey)) + self.assertEqual(sub1_cert.verify(None, [ca_cert], crls=[crl]), 0) + self.assertEqual(sub2_cert.verify(None, [ca_cert], crls=[crl]), 0) + + to_revoke = botan.X509CRLEntry.create(sub2_cert, botan.X509CRLReason.KEY_COMPROMISE) + + crl = crl.revoke(rng, ca_cert, ca_key, now, 86400, [to_revoke]) + self.assertTrue(crl.verify(ca_pubkey)) + self.assertEqual(sub1_cert.verify(None, [ca_cert], crls=[crl]), 0) + self.assertEqual(sub2_cert.verify(None, [ca_cert], crls=[crl]), 5000) + self.assertEqual(len(crl.revoked()), 1) + revoked_entry = crl.revoked()[0] + self.assertEqual(revoked_entry.reason(), botan.X509CRLReason.KEY_COMPROMISE) + self.assertEqual(revoked_entry.serial_number(), botan.MPI("270431672985589325219914342203841486494")) + self.assertTrue(now - 20 <= revoked_entry.revocation_date() <= now + 20) def test_mpi(self): z = botan.MPI() @@ -773,6 +1038,9 @@ self.assertEqual(int(small), 0xDEADBEEF) self.assertEqual(int(radix), int(small)) + self.assertEqual(repr(small), "3735928559") + self.assertEqual(repr(big), "10578070104470344071876527419957") + self.assertEqual(int(small >> 16), 0xDEAD) small >>= 15 @@ -854,16 +1122,16 @@ def test_mpi_random(self): rng = botan.RandomNumberGenerator() - u = botan.MPI.random(rng, 512) - self.assertEqual(u.bit_count(), 512) + upper = botan.MPI.random(rng, 512) + self.assertEqual(upper.bit_count(), 512) - l = u >> 32 - self.assertEqual(l.bit_count(), 512-32) + lower = upper >> 32 + self.assertEqual(lower.bit_count(), 512-32) for _i in range(10): - x = botan.MPI.random_range(rng, l, u) - self.assertLess(x, u) - self.assertGreater(x, l) + x = botan.MPI.random_range(rng, lower, upper) + self.assertLess(x, upper) + self.assertGreater(x, lower) def test_fpe(self): @@ -1029,6 +1297,141 @@ self.assertEqual(sk_read.to_raw(), sk_bits) self.assertEqual(pk_read.to_raw(), pk_bits) + def test_oids(self): + oid = botan.OID.from_string("1.2.3.4.5") + self.assertEqual(oid.to_string(), "1.2.3.4.5") + + new_oid = botan.OID.from_string("1.2.3.4.5.6.7.8") + new_oid.register("random-name-that-definitely-has-no-oid") + new_oid_from_string = botan.OID.from_string("random-name-that-definitely-has-no-oid") + self.assertEqual(new_oid, new_oid_from_string) + self.assertEqual(new_oid.to_string(), new_oid_from_string.to_string()) + self.assertEqual(new_oid.to_name(), new_oid_from_string.to_name()) + + oid_rsa = botan.OID.from_string("RSA") + self.assertEqual(oid_rsa.to_string(), "1.2.840.113549.1.1.1") + self.assertEqual(oid_rsa.to_name(), "RSA") + + oid_a = botan.OID.from_string("1.2.3.4.5.6") + oid_b = botan.OID.from_string("1.2.3.4.5.6") + oid_c = botan.OID.from_string("1.2.3.4") + self.assertEqual(oid_a, oid_b) + self.assertNotEqual(oid_a, oid_c) + self.assertTrue(oid_a > oid_c) + self.assertTrue(oid_a >= oid_c) + self.assertTrue(oid_c < oid_a) + self.assertTrue(oid_c <= oid_a) + + rng = botan.RandomNumberGenerator() + priv = botan.PrivateKey.create("RSA", "1024", rng) + oid_rsa_priv = priv.object_identifier() + self.assertEqual(oid_rsa_priv, oid_rsa) + self.assertEqual(oid_rsa_priv.to_string(), oid_rsa.to_string()) + self.assertEqual(oid_rsa_priv.to_name(), oid_rsa.to_name()) + + pub = priv.get_public_key() + oid_rsa_pub = pub.object_identifier() + self.assertEqual(oid_rsa_pub, oid_rsa) + self.assertEqual(oid_rsa_pub.to_string(), oid_rsa.to_string()) + self.assertEqual(oid_rsa_pub.to_name(), oid_rsa_pub.to_name()) + + def test_ec_group(self): + if not botan.ECGroup.supports_named_group("secp256r1"): + self.skipTest("No secp256r1 group support in this build") + return + + secp256r1_oid = botan.OID.from_string("1.2.840.10045.3.1.7") + + group_from_name = botan.ECGroup.from_name("secp256r1") + group_from_oid = botan.ECGroup.from_oid(secp256r1_oid) + + self.assertEqual(secp256r1_oid, group_from_name.get_curve_oid()) + self.assertEqual(group_from_name, group_from_oid) + + rng = botan.RandomNumberGenerator() + priv = botan.PrivateKey.create_ec("ECDSA", group_from_name, rng) + self.assertEqual(priv.algo_name(), "ECDSA") + + self.assertEqual(group_from_name, botan.ECGroup.from_pem(group_from_name.to_pem())) + self.assertEqual(group_from_name, botan.ECGroup.from_ber(group_from_name.to_der())) + + if botan.ECGroup.supports_application_specific_group(): + secp256r1_new_oid = botan.OID.from_string("1.3.6.1.4.1.25258.100.0") + secp256r1_new_oid.register("secp256r1-but-manually-registered") + + p = botan.MPI("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", 16) + a = botan.MPI("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", 16) + b = botan.MPI("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", 16) + g_x = botan.MPI("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", 16) + g_y = botan.MPI("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", 16) + order = botan.MPI("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 16) + + group_from_parameters = botan.ECGroup.from_params(secp256r1_new_oid, p, a, b, g_x, g_y, order) + + self.assertEqual(group_from_name, group_from_parameters) + + self.assertEqual(p, group_from_name.get_p()) + self.assertEqual(a, group_from_name.get_a()) + self.assertEqual(b, group_from_name.get_b()) + self.assertEqual(g_x, group_from_name.get_g_x()) + self.assertEqual(g_y, group_from_name.get_g_y()) + self.assertEqual(order, group_from_name.get_order()) + + self.assertTrue(botan.ECGroup.unregister(secp256r1_new_oid)) + self.assertFalse(botan.ECGroup.unregister(secp256r1_new_oid)) + + self.assertTrue(botan.ECGroup.unregister(secp256r1_oid)) + self.assertFalse(botan.ECGroup.unregister(secp256r1_oid)) + + def test_ec_points(self): + if not botan.ECGroup.supports_named_group("secp256r1"): + self.skipTest("No secp256r1 group support in this build") + return + + group = botan.ECGroup.from_name("secp256r1") + rng = botan.RandomNumberGenerator() + + forty_two = botan.MPI(42) + scalar_forty_two = botan.ECScalar.from_mpi(group, forty_two) + self.assertEqual(forty_two, scalar_forty_two.to_mpi()) + + identity = group.get_identity() + generator = group.get_generator() + one = botan.ECScalar.from_mpi(group, botan.MPI(1)) + self.assertTrue(identity == identity + identity) + + order_minus_one = group.get_order() - botan.MPI(1) + order_minus_one_scalar = botan.ECScalar.from_mpi(group, order_minus_one) + self.assertTrue(generator.mul(order_minus_one_scalar, rng) + generator == identity) + self.assertTrue(generator == generator.mul(one, rng)) + self.assertTrue(identity == identity.mul(one, rng)) + self.assertTrue(generator.negate() == generator.mul(order_minus_one_scalar, rng)) + + pkey = botan.PrivateKey.create_ec("ECDSA", group, rng) + private_value = pkey.get_private_key() + public_value = botan.ECPoint.from_bytes(group, pkey.get_public_key().get_public_point()) + + self.assertEqual(pkey.get_group(), group) + self.assertEqual(pkey.get_public_key().get_group(), group) + + result = generator.mul(private_value, rng) + public_value + self.assertFalse(result.is_identity()) + + x_bytes = result.to_x_bytes().hex() + y_bytes = result.to_y_bytes().hex() + xy_bytes = result.to_xy_bytes().hex() + uncompressed_bytes = result.to_uncompressed().hex() + compressed_bytes = result.to_compressed().hex() + + self.assertTrue(xy_bytes == x_bytes + y_bytes) + self.assertTrue(uncompressed_bytes == "04" + xy_bytes) + self.assertTrue(compressed_bytes.startswith("02") or compressed_bytes.startswith("03")) + self.assertTrue(compressed_bytes[2::] == x_bytes) + + self.assertTrue(result == botan.ECPoint.from_xy(group, botan.MPI(x_bytes, 16), botan.MPI(y_bytes, 16))) + self.assertTrue(result == botan.ECPoint.from_bytes(group, result.to_uncompressed())) + self.assertTrue(result == botan.ECPoint.from_bytes(group, result.to_compressed())) + class BotanPythonZfecTests(unittest.TestCase): """ diff -Nru botan3-3.7.1+dfsg/src/scripts/test_strubbed_symbols.py botan3-3.12.0+dfsg/src/scripts/test_strubbed_symbols.py --- botan3-3.7.1+dfsg/src/scripts/test_strubbed_symbols.py 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/test_strubbed_symbols.py 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 + +# (C) 2025 Jack Lloyd +# 2025 René Meusel, Rohde & Schwarz Cybersecurity +# +# Botan is released under the Simplified BSD License (see license.txt) +# + +import subprocess +import os +import sys +import argparse +import tempfile +import platform + +from textwrap import indent +from enum import Enum + +SCRIPT_LOCATION = os.path.dirname(os.path.abspath(__file__)) +GDB_EXTENSION = os.path.join(SCRIPT_LOCATION, "gdb", "strubtest.py") + +class CPU(Enum): + ANY = "any" + X86_64 = "x86_64" + AARCH64 = "aarch64" + + @staticmethod + def current(): + cpu = platform.machine().lower() + if cpu in ['x86_64', 'amd64']: + return CPU.X86_64 + elif cpu in ['aarch64', 'arm64']: + return CPU.AARCH64 + else: + raise ValueError(f"Unsupported or unknown CPU architecture: {cpu}") + +class StrubTest: + def __init__(self, symbol, inferior_cmdline, masked_cpuid_bits = None, cpu = CPU.ANY, expect_fail = False): + self.symbol = symbol + self.inferior_cmdline = inferior_cmdline + self.masked_cpuid_bits = masked_cpuid_bits + self.cpu = cpu + self.expect_fail = expect_fail + + @property + def gdb_command(self): + return ["gdb", "-x", GDB_EXTENSION, + "-ex", f"strubtest {self.symbol}", + "-ex", "run", + "--batch", + "--args", *self.inferior_cmdline] + + @property + def rendered_gdb_command(self): + return " ".join([token if " " not in token else f"'{token}'" for token in self.gdb_command]) + + @property + def environment(self): + return {"BOTAN_CLEAR_CPUID": ",".join(self.masked_cpuid_bits)} if self.masked_cpuid_bits else {} + + @property + def rendered_environment(self): + full_env = os.environ.copy() + full_env.update(self.environment) + return full_env + + @property + def runnable(self): + return True if self.cpu == CPU.ANY else self.cpu == CPU.current() + + def run(self): + print(f"Checking {self.symbol}... ", end="") + if not self.runnable: + return self._skip("incompatible platform") + + try: + proc = subprocess.run(self.gdb_command, capture_output=True, check=False, env=self.rendered_environment) + except subprocess.SubprocessError as ex: + return self._fail("subprocess failure", exception=ex) + + if proc.returncode != 0: + return self._fail("nonzero error code", proc_result=proc) + + if "Error: " in proc.stderr.decode("utf-8"): + return self._fail("fail", proc_result=proc, may_be_expected=True) + + if "Success: " in proc.stdout.decode("utf-8"): + return self._succeed(proc_result=proc) + else: + return self._fail("never invoked", proc_result=proc) + + def _print_debug_report(self, proc_result = None, exception = None): + print(f" ran: {self.rendered_gdb_command}") + + if self.environment: + print(" Environment:") + print(indent("\n".join([f"{k}={v}" for (k,v) in self.environment.items()]), " " * 6)) + if exception: + print(" Exception:") + print(indent(str(exception), " " * 6)) + if proc_result: + if proc_result.stdout: + print(" stdout:") + print(indent(proc_result.stdout.decode("utf-8"), " " * 6)) + if proc_result.stderr: + print(" stderr:") + print(indent(proc_result.stderr.decode("utf-8"), " " * 6)) + + def _fail(self, errmsg, proc_result = None, exception = None, may_be_expected = False): + if may_be_expected and self.expect_fail: + print(f"{errmsg} (expected)") + return True + else: + print(errmsg) + self._print_debug_report(proc_result=proc_result, exception=exception) + return False + + def _succeed(self, proc_result = None): + if not self.expect_fail: + print("ok") + else: + print("ok (unexpected)") + self._print_debug_report(proc_result=proc_result) + return not self.expect_fail + + def _skip(self, reason): + print(f"skipped ({reason})") + return True + +def dummy_file(size = 1024): + with tempfile.NamedTemporaryFile(delete=False) as temp_file: + temp_file.write(os.urandom(size)) + return temp_file.name + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--botan-cli', required=True) + args = parser.parse_args() + + cli = args.botan_cli + myfile = dummy_file() + + tests = [ + # This is a self-test, it is expected to fail because the version + # information is naturally not annotated for stack scrubbing. + StrubTest("Botan::version_string", [cli, "version", "--full"], expect_fail=True), + + # Below is a list of all strub-annotated symbols. Each of these + # symbols is tested by running the Botan CLI with GDB and checking + # that the stack scrubbing was performed correctly. + + StrubTest("Botan::SHA_256::compress_digest", [cli, "hash", "--algo=SHA-256", myfile]), + StrubTest("Botan::SHA_256::compress_digest_x86", [cli, "hash", "--algo=SHA-256", myfile], cpu=CPU.X86_64), + StrubTest("Botan::SHA_256::compress_digest_armv8", [cli, "hash", "--algo=SHA-256", myfile], cpu=CPU.AARCH64), + StrubTest("Botan::SHA_256::compress_digest_x86_avx2", [cli, "hash", "--algo=SHA-256", myfile], cpu=CPU.X86_64, masked_cpuid_bits=["intel_sha"]), + StrubTest("Botan::SHA_256::compress_digest_x86_simd", [cli, "hash", "--algo=SHA-256", myfile], cpu=CPU.X86_64, masked_cpuid_bits=["intel_sha", "avx2"]), + ] + + results = [test.run() for test in tests] + os.remove(myfile) + + return 1 if any(not ok for ok in results) else 0 + +if __name__ == '__main__': + sys.exit(main()) diff -Nru botan3-3.7.1+dfsg/src/scripts/tls_anvil/analyze_tls_anvil_report.py botan3-3.12.0+dfsg/src/scripts/tls_anvil/analyze_tls_anvil_report.py --- botan3-3.7.1+dfsg/src/scripts/tls_anvil/analyze_tls_anvil_report.py 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/tls_anvil/analyze_tls_anvil_report.py 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,250 @@ +#!/usr/bin/env python3 + +# Parses a TLS-Anvil results directory. Returns 0 iff all results are expected. +# +# (C) 2023,2026 Jack Lloyd +# (C) 2023 Fabian Albert, Rohde & Schwarz Cybersecurity +# +# Botan is released under the Simplified BSD License (see license.txt) +import sys +import argparse +import os +import json +import logging + + +result_level = { + "STRICTLY_SUCCEEDED": 0, + "CONCEPTUALLY_SUCCEEDED": 1, + "PARTIALLY_FAILED": 2, + "FULLY_FAILED": 3, +} + + +def xfail_list(side): + """Return list of tests that are expected to fail""" + + conceptually_succeeded = { + # Okay: RFC does not specifically define an alert. Bogo Test expects an DecodeError Alert + # while TLS-Anvil expects an IllegalParameter Alert. We use the DecodeError Alert. + "server.tls13.rfc8446.PreSharedKey.isLastButDuplicatedExtension", + + # TLS-Anvil expects an alert which is incorrect in our context + # https://github.com/tls-attacker/TLS-Anvil/issues/61 + "client.tls13.rfc8446.EncryptedExtensions.sendPaddingExtensionInEE", + } + + partially_failed = { + # We accept the TLS 1.2 brainpool curve IDs in 1.3 + "server.tls13.rfc8446.KeyShare.serverAcceptsDeprecatedGroups", + } + + fully_failed = { + # If ClientHello has no extensions that includes supported signatures; TLS 1.2 then + # requires us to treat that as equivalent to supporting only SHA-1 -- which we do not support + "server.tls12.rfc5246.ClientHello.leaveOutExtensions", + + # TLS-Anvil expects us to tolerate any legacy_version even ones that RFC 8446 explicitly + # states we MUST reject - https://github.com/tls-attacker/TLS-Anvil/issues/60 + # 8446-bis has a change specifically mandating what we do right now + "client.tls13.rfc8446.SupportedVersions.invalidLegacyVersion", + + # TLS-Anvil seems to assume KeyUpdate response is immediate rather than opportunistic + # Possibly this can be fixed by using a dedicated util rather than tls_client + "both.tls13.rfc8446.KeyUpdate.respondsWithValidKeyUpdate", + "both.tls13.rfc8446.KeyUpdate.appDataUnderNewKeysSucceeds", + + # We accept the TLS 1.2 brainpool curve IDs in 1.3 + "server.tls13.rfc8446.KeyShare.serverAcceptsDeprecatedGroupsAllAtOnce", + } + + if side == 'client': + # TLS-Anvil's scanning phase seems to have a bug and decides we don't support handshake fragmentation + # Works for server side though + fully_failed.add("both.tls12.rfc5246.Fragmentation.recordFragmentationSupported") + + xfails = {} + for test in conceptually_succeeded: + if test.startswith('both.') or test.startswith(side): + xfails[test] = result_level["CONCEPTUALLY_SUCCEEDED"] + for test in partially_failed: + if test.startswith('both.') or test.startswith(side): + xfails[test] = result_level["PARTIALLY_FAILED"] + for test in fully_failed: + if test.startswith('both.') or test.startswith(side): + xfails[test] = result_level["FULLY_FAILED"] + return xfails + + +def extract_method_id(json_data): + """Extract the method_id from a test result JSON.""" + return (json_data["TestClass"] + "." + json_data["TestMethod"]).removeprefix("de.rub.nds.tlstest.suite.tests.") + + +def failing_test_info(json_data, method_id, expected_label) -> str: + """ Print debug information about a failing test """ + info_str = "" + try: + method_class, method_name = method_id.rsplit('.', 1) + info = [f"Error: {method_id} - Unexpected result '{json_data['Result']}' (expected {expected_label})"] + info += [""] + info += [f"Class Name: 'de.rub.nds.tlstest.suite.tests.{method_class}'"] + info += [f"Method Name: '{method_name}'"] + info += [""] + + metadata = json_data.get("MetaData") or {} + rfc = metadata.get("rfc") + if rfc is not None: + info += [f"RFC {rfc.get('number', '?')}, Section {rfc.get('section', '?')}:"] + else: + info += ["Custom Test Case:"] + + description = metadata.get("description", "") + if description: + info += [description] + info += [""] + + info += [f"Result: {json_data['Result']}"] + + if json_data.get('DisabledReason'): + info += [f"Disabled Reason: {json_data['DisabledReason']}"] + if json_data.get('FailedReason'): + info += [f"Failed Reason: {json_data['FailedReason']}"] + + info += [""] + info_str = "\n".join(info) + + # Color in red + info_str = "\n".join([f"\033[0;31m{line}\033[0m" for line in info_str.split("\n")]) + + # In GitHub Actions logging group + info_str = f"::group::{info_str}\n::endgroup::" + + except (KeyError, TypeError): + logging.warning("Cannot process test info for %s", method_id) + info_str = f"Error: {method_id} - Unexpected result '{json_data.get('Result', '?')}'" + + return info_str + + +def process_test_result(result_path: str, xfails: dict, seen_xfails: set): + """ + Given a path, process the respective test result .json file. + Returns True iff the results are expected. + """ + success = False + with open(result_path, "r", encoding="utf-8") as f: + try: + json_data = json.load(f) + method_id = extract_method_id(json_data) + result = json_data["Result"] + + if result == "DISABLED": + logging.debug("%s: 'DISABLED' -> ok", method_id) + return True + + if result not in result_level: + logging.error("Unknown result '%s' for test '%s'", result, method_id) + return False + + actual_level = result_level[result] + + if method_id in xfails: + expected_level = xfails[method_id] + seen_xfails.add(method_id) + expected_label = [k for k, v in result_level.items() if v == expected_level][0] + + if actual_level != expected_level: + # Test is doing other than expected -> error + logging.error( + "Error: %s has result '%s' but is xfail as '%s'. " + "Remove or update the xfail entry.", + method_id, result, expected_label) + success = False + else: + logging.debug("%s: '%s' -> ok (xfail as %s)", method_id, result, expected_label) + success = True + else: + # Not in xfail list: must strictly succeed + if actual_level > result_level["STRICTLY_SUCCEEDED"]: + logging.error(failing_test_info(json_data, method_id, "STRICTLY_SUCCEEDED")) + success = False + else: + logging.debug("%s: '%s' -> ok", method_id, result) + success = True + + except (KeyError, TypeError) as e: + logging.error("Json file '%s' has missing entries: %s", result_path, e) + + return success + + +RESULT_FILE_NAME = "_testRun.json" + + +def main(args=None): + """Parse args and check all result container files""" + if args is None: + args = sys.argv[1:] + + parser = argparse.ArgumentParser() + parser.add_argument("--verbose", action="store_true", default=False) + parser.add_argument("side", help="which side of the protocol was tested (client or server)") + parser.add_argument("results-dir", help="directory of TLS-Anvil test results") + + args = vars(parser.parse_args(args)) + + logging.basicConfig( + level=(logging.DEBUG if args["verbose"] else logging.INFO), + format="%(message)s", + ) + + side = args["side"] + + if side not in ["client", "server"]: + print("Unexpected side %s" % (side)) + return 1 + + results_dir = args["results-dir"] + + if not os.access(results_dir, os.X_OK): + raise FileNotFoundError("Unable to read TLS-Anvil results dir") + + xfails = xfail_list(side) + seen_xfails = set() + + failed_methods_count = 0 + total_methods_count = 0 + for root, _, files in os.walk(results_dir): + for file in files: + if file == RESULT_FILE_NAME: + abs_path = os.path.abspath(os.path.join(root, file)) + total_methods_count += 1 + if not process_test_result(abs_path, xfails, seen_xfails): + failed_methods_count += 1 + + if total_methods_count == 0: + logging.error("No test results found in '%s'", results_dir) + return 1 + + # Check that every xfail entry was actually seen in the results + missing_xfails = set(xfails.keys()) - seen_xfails + for method_id in sorted(missing_xfails): + expected_label = [k for k, v in result_level.items() if v == xfails[method_id]][0] + logging.warning( + "xfailed test '%s' (expected %s) was not found in results.", + method_id, expected_label) + + logging.info( + "(%i/%i) test methods successful.", + total_methods_count - failed_methods_count, + total_methods_count, + ) + total_success = failed_methods_count == 0 + logging.info("Total result: %s", "Success." if total_success else "Failed.") + + return int(not total_success) + + +if __name__ == "__main__": + sys.exit(main()) diff -Nru botan3-3.7.1+dfsg/src/scripts/tls_anvil/anvil_policy.txt botan3-3.12.0+dfsg/src/scripts/tls_anvil/anvil_policy.txt --- botan3-3.7.1+dfsg/src/scripts/tls_anvil/anvil_policy.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/tls_anvil/anvil_policy.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,13 @@ +allow_tls12 = true +allow_tls13 = true +ciphers = AES-128/GCM AES-256/GCM ChaCha20Poly1305 AES-256 AES-128 3DES + +# Anvil server uses small keys for some reason +minimum_rsa_bits = 1024 + +# RSA kex disabled pending apparent Anvil bug +key_exchange_methods = ECDH DH + +signature_methods = ECDSA RSA IMPLICIT + +require_extended_master_secret = false diff -Nru botan3-3.7.1+dfsg/src/scripts/tls_anvil/run_tls_anvil_tests.py botan3-3.12.0+dfsg/src/scripts/tls_anvil/run_tls_anvil_tests.py --- botan3-3.7.1+dfsg/src/scripts/tls_anvil/run_tls_anvil_tests.py 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/tls_anvil/run_tls_anvil_tests.py 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,172 @@ +#!/usr/bin/env python3 + +# Script to run inside the CI container to test the botan +# TLS client/server with TLS-Anvil +# +# (C) 2023,2026 Jack Lloyd +# (C) 2023 Fabian Albert, Rohde & Schwarz Cybersecurity +# +# Botan is released under the Simplified BSD License (see license.txt) +import sys +import argparse +import os +import subprocess + + +class Config: + """ Hardcoded configurations for this CI script """ + tls_anvil_docker_image = "ghcr.io/tls-attacker/tlsanvil" + tls_anvil_version_tag = "@sha256:fc25911df5a2dce9912e9db2037af88f3424fecf52368284bba5a1f50726ccc3" # 1.4.0 + key_and_cert_storage_path = "/tmp/" + test_suite_results_dest = "." + test_suite_results_dir_name = "TestSuiteResults" + tmp_key_file_name = "tmp_rsa_key.pem" + tmp_cert_file_name = "tmp_rsa_cert.pem" + server_dest_ip = "127.0.0.1" + server_dest_port = 4433 + client_test_port = 4433 + trigger_server_port = 8090 + botan_server_log = "./logs/botan_server.log" + botan_client_log_dir = "./logs/botan_client" + anvil_policy_file = "src/scripts/tls_anvil/anvil_policy.txt" + +def group_output(group_title: str, func): + """ + Wraps a function to be called within a GitHub actions group, so that + the console output is expandable. + + Returns the wrapped function + """ + def wrapped_func(*args, **kwargs): + print(f"::group::{group_title}", flush=True) + ret = func(*args, **kwargs) + print("\n::endgroup::", flush=True) + return ret + return wrapped_func + + +def create_cert_and_key(botan_cli): + """ + Create a X.509 certificate and associated RSA key at Config.key_and_cert_storage_path + using Botan's CLI. + + Returns: (, ) + """ + + key_path = os.path.join(Config.key_and_cert_storage_path, Config.tmp_key_file_name) + cert_path = os.path.join(Config.key_and_cert_storage_path, Config.tmp_cert_file_name) + + with open(key_path, 'w', encoding='utf-8') as keyfile: + subprocess.run([botan_cli, "keygen", "--algo=RSA", "--params=2048"], stdout=keyfile, check=True) + + with open(cert_path, 'w', encoding='utf-8') as certfile: + subprocess.run([botan_cli, "gen_self_signed", key_path, "localhost"], stdout=certfile, check=True) + + return (cert_path, key_path) + + +def server_test(botan_cli: str, parallel: int): + """ Test the Botan TLS server """ + cert_path, key_path = create_cert_and_key(botan_cli) + docker_img = f"{Config.tls_anvil_docker_image}{Config.tls_anvil_version_tag}" + + group_output("Pull TLS-Anvil image", subprocess.run)(["docker", "pull", docker_img], check=True) + + tls_anvil_cmd = [ + "docker", "run", + "--network", "host", + "-v", f"{Config.test_suite_results_dest}:/output", + docker_img, + "-strength", "1", + "-parallelHandshakes", str(parallel), + "-disableTcpDump", + "-outputFolder", os.path.join(Config.test_suite_results_dest, Config.test_suite_results_dir_name), + "-connectionTimeout", "5000", + "server", "-connect", f"{Config.server_dest_ip}:{Config.server_dest_port}" + ] + + botan_server_cmd = [ + botan_cli, "tls_http_server", cert_path, key_path, + f"--port={Config.server_dest_port}", + f"--policy={Config.anvil_policy_file}", + ] + + os.makedirs(os.path.dirname(Config.botan_server_log), exist_ok=True) + + # Run Botan and test is with TLS-Anvil + with open(Config.botan_server_log, 'w', encoding='utf-8') as server_log_file: + botan_server_process = subprocess.Popen(botan_server_cmd, stdout=server_log_file, stderr=server_log_file) + subprocess.run(tls_anvil_cmd, check=True) + botan_server_process.kill() + + +def client_test(botan_cli: str, parallel: int): + """ Test the Botan TLS client """ + docker_img = f"{Config.tls_anvil_docker_image}{Config.tls_anvil_version_tag}" + + group_output("Pull TLS-Anvil image", subprocess.run)(["docker", "pull", docker_img], check=True) + + trigger_server_script = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "tls_anvil_trigger_server.py") + + trigger_server_cmd = [ + "python3", trigger_server_script, + "--botan-cli", botan_cli, + "--tls-anvil-policy", Config.anvil_policy_file, + "--tls-anvil-host", Config.server_dest_ip, + "--tls-anvil-port", str(Config.client_test_port), + "--listen-port", str(Config.trigger_server_port), + "--log-dir", Config.botan_client_log_dir, + ] + + tls_anvil_cmd = [ + "docker", "run", + "--network", "host", + "-v", f"{Config.test_suite_results_dest}:/output", + docker_img, + "-strength", "1", + "-parallelHandshakes", str(parallel), + "-disableTcpDump", + "-outputFolder", os.path.join(Config.test_suite_results_dest, Config.test_suite_results_dir_name), + "-connectionTimeout", "5000", + "client", + "-port", str(Config.client_test_port), + "-triggerScript", "curl", "--connect-timeout", "2", + f"http://{Config.server_dest_ip}:{Config.trigger_server_port}/trigger" + ] + + os.makedirs(Config.botan_client_log_dir, exist_ok=True) + + trigger_server_process = subprocess.Popen(trigger_server_cmd) + try: + subprocess.run(tls_anvil_cmd, check=True) + finally: + trigger_server_process.terminate() + trigger_server_process.wait() + + +def main(args=None): + if args is None: + args = sys.argv[1:] + + parser = argparse.ArgumentParser() + parser.add_argument("--botan-cli", help="Path to botan CLI executable", required=True) + parser.add_argument("--test-target", help="The TLS side to test", choices=['client', 'server'], required=True) + parser.add_argument("--parallel", help="The number of parallel handshakes", type=int, default=16) + + args = vars(parser.parse_args(args)) + + botan_cli = args["botan_cli"] + if not os.access(botan_cli, os.X_OK): + raise FileNotFoundError(f"Botan CLI not found or not executable: '{botan_cli}'") + + if args["test_target"] == "server": + server_test(botan_cli, args["parallel"]) + elif args["test_target"] == "client": + client_test(botan_cli, args["parallel"]) + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff -Nru botan3-3.7.1+dfsg/src/scripts/tls_anvil/tls_anvil_trigger_server.py botan3-3.12.0+dfsg/src/scripts/tls_anvil/tls_anvil_trigger_server.py --- botan3-3.7.1+dfsg/src/scripts/tls_anvil/tls_anvil_trigger_server.py 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/tls_anvil/tls_anvil_trigger_server.py 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,184 @@ +# TLS-Anvil calls a trigger script before each test handshake to make the +# client-under-test initiate a new TLS connection. This server listens for +# HTTP GET /trigger requests and spawns a fresh Botan tls_client process +# for each one. +# +# (C) 2026 Jack Lloyd +# +# Botan is released under the Simplified BSD License (see license.txt) + +import sys +import os +import signal +import subprocess +import argparse +import threading +from http.server import HTTPServer, BaseHTTPRequestHandler + + +# Populated from command-line arguments in main() +botan_cli = None +tls_anvil_policy = None +tls_anvil_host = None +tls_anvil_port = None +client_log_dir = None +client_timeout = 30 + +# Track spawned client processes so we can clean them up +client_processes = [] +trigger_count = 0 + + +def reap_client(proc): + """Wait for the timeout, then kill the process if still running. + + While waiting, periodically write to stdin so that tls_client calls + send() and flushes any deferred KeyUpdate response.""" + import time + deadline = time.monotonic() + client_timeout + while time.monotonic() < deadline: + if proc.poll() is not None: + break + if proc.stdin: + try: + proc.stdin.write(b".\n") + proc.stdin.flush() + except OSError: + break + try: + proc.wait(timeout=1) + break + except subprocess.TimeoutExpired: + pass + if proc.poll() is None: + proc.kill() + proc.wait() + if proc.stdin: + try: + proc.stdin.close() + except OSError: + pass + + +def cleanup_finished_clients(): + """Remove completed client processes from the tracking list.""" + still_running = [] + for proc in client_processes: + if proc.poll() is None: + still_running.append(proc) + client_processes[:] = still_running + + +def kill_all_clients(): + """Terminate all tracked client processes.""" + for proc in client_processes: + try: + proc.kill() + proc.wait() + if proc.stdin: + proc.stdin.close() + except OSError: + pass + client_processes.clear() + + +class TriggerHandler(BaseHTTPRequestHandler): + def do_GET(self): + if self.path == "/trigger": + global trigger_count + cleanup_finished_clients() + + log_file = None + if client_log_dir: + log_path = os.path.join(client_log_dir, f"client_{trigger_count}.log") + log_file = open(log_path, 'w', encoding='utf-8') + + trigger_count += 1 + + proc = subprocess.Popen( + [botan_cli, "tls_client", tls_anvil_host, + f"--port={tls_anvil_port}", + f"--policy={tls_anvil_policy}", + "--ignore-cert-error"], + stdin=subprocess.PIPE, + stdout=log_file or subprocess.DEVNULL, + stderr=log_file or subprocess.DEVNULL, + ) + client_processes.append(proc) + + # Close the log file in this process; the child has its own fd + if log_file: + log_file.close() + + # Start a reaper thread that kills the process after the timeout + threading.Thread(target=reap_client, args=(proc,), daemon=True).start() + + self.send_response(200) + self.end_headers() + self.wfile.write(b"OK\n") + + elif self.path == "/shutdown": + self.send_response(200) + self.end_headers() + self.wfile.write(b"OK\n") + + def shutdown(): + self.server.shutdown() + threading.Thread(target=shutdown, daemon=True).start() + + else: + self.send_response(404) + self.end_headers() + + def log_message(self, format, *args): + # Suppress request logging noise + pass + + +def main(args=None): + global botan_cli, tls_anvil_policy, tls_anvil_host, tls_anvil_port, client_log_dir, client_timeout + + if args is None: + args = sys.argv[1:] + + parser = argparse.ArgumentParser(description="TLS-Anvil trigger server for Botan client testing") + parser.add_argument("--botan-cli", required=True, help="Path to botan executable") + parser.add_argument("--tls-anvil-policy", default="default", help="Policy to use for TLS-Anvil testing") + parser.add_argument("--tls-anvil-host", default="127.0.0.1", help="Host where TLS-Anvil listens") + parser.add_argument("--tls-anvil-port", type=int, required=True, help="Port where TLS-Anvil listens") + parser.add_argument("--listen-port", type=int, default=8090, help="Port for this trigger server") + parser.add_argument("--log-dir", default=None, help="Directory for client log files") + parser.add_argument("--timeout", type=int, default=10, + help="Kill client processes after this many seconds (default: %(default)s)") + + parsed = parser.parse_args(args) + + botan_cli = parsed.botan_cli + tls_anvil_policy = parsed.tls_anvil_policy + tls_anvil_host = parsed.tls_anvil_host + tls_anvil_port = parsed.tls_anvil_port + client_log_dir = parsed.log_dir + client_timeout = parsed.timeout + + if client_log_dir: + os.makedirs(client_log_dir, exist_ok=True) + + server = HTTPServer(("127.0.0.1", parsed.listen_port), TriggerHandler) + print(f"Trigger server listening on 127.0.0.1:{parsed.listen_port}", flush=True) + + def handle_signal(signum, frame): + kill_all_clients() + sys.exit(0) + + signal.signal(signal.SIGTERM, handle_signal) + signal.signal(signal.SIGINT, handle_signal) + + try: + server.serve_forever() + finally: + kill_all_clients() + server.server_close() + + +if __name__ == "__main__": + main() diff -Nru botan3-3.7.1+dfsg/src/scripts/tls_scanner/tls_scanner.py botan3-3.12.0+dfsg/src/scripts/tls_scanner/tls_scanner.py --- botan3-3.7.1+dfsg/src/scripts/tls_scanner/tls_scanner.py 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/tls_scanner/tls_scanner.py 2026-05-07 01:38:28.000000000 +0000 @@ -45,12 +45,12 @@ for i in range(timeout): scanners[url].poll() - if scanners[url].returncode != None: + if scanners[url].returncode is not None: break #print("Waiting %d more seconds for %s" % (timeout-i, url)) time.sleep(1) - if scanners[url].returncode != None: + if scanners[url].returncode is not None: output = scanners[url].stdout.read() + scanners[url].stderr.read() report[url] = format_report(output.decode("utf-8")) diff -Nru botan3-3.7.1+dfsg/src/scripts/wycheproof.py botan3-3.12.0+dfsg/src/scripts/wycheproof.py --- botan3-3.7.1+dfsg/src/scripts/wycheproof.py 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/scripts/wycheproof.py 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,1831 @@ +#!/usr/bin/env python3 + +""" +Run the Wycheproof tests + +This script is run against a git checkout of Wycheproof + +(C) 2026 Jack Lloyd +(C) 2026 Rene Meusel, Rohde & Schwarz Cybersecurity + +Botan is released under the Simplified BSD License (see license.txt) +""" + +import argparse +import base64 +import binascii +import ctypes +import io +import json +import multiprocessing +import os +import subprocess +import sys +import traceback +from collections import Counter +from collections.abc import Callable +from dataclasses import dataclass, field +from pathlib import Path + +import botan3 as botan + +# ---- Framework ---- + + +class TestSkip(Exception): + """Raised by a handler to skip a test vector.""" + + def __init__(self, reason: str): + self.reason = reason + super().__init__(reason) + + +class TestFailure(Exception): + """Raised by a handler when a test vector fails. + + Can be constructed with either: + - A dict of field_name -> value for .vec-style output + - A plain string message + """ + + def __init__(self, fields_or_message): + if isinstance(fields_or_message, dict): + self.fields = fields_or_message + msg = "; ".join(f"{k}={v}" for k, v in fields_or_message.items()) + else: + self.fields = None + msg = str(fields_or_message) + super().__init__(msg) + + +class FixedOutputRNG(botan.RandomNumberGenerator): + def __init__(self, entropy_pool: bytes = b""): + super().__init__( + "custom", get_callback=self._get, add_entropy_callback=self._add_entropy + ) + self._entropy_pool = entropy_pool + + def _get(self, length: int) -> bytes: + if length > len(self._entropy_pool): + raise ValueError("Not enough entropy in pool") + entropy = self._entropy_pool[:length] + self._entropy_pool = self._entropy_pool[length:] + return entropy + + def _add_entropy(self, data: bytes) -> None: + self._entropy_pool += data + + +class NullRNG(botan.RandomNumberGenerator): + """An RNG that raises an exception if it is used.""" + + def __init__(self): + super().__init__( + "custom", get_callback=self._get, add_entropy_callback=self._add_entropy + ) + + def _get(self, length: int) -> bytes: + raise botan.BotanException("Unexpected request to get entropy from RNG", rc=-23) + + def _add_entropy(self, data: bytes) -> None: + raise botan.BotanException("Unexpected request to add entropy to RNG", rc=-23) + + +class _Registry: + def __init__(self): + self._handlers: dict[str, Callable] = {} + self._ignored: set[str] = set() + + def register(self, algorithm: str, handler: Callable) -> None: + if algorithm in self._handlers: + raise RuntimeError( + f"Algorithm {algorithm!r} already registered by " + f"{self._handlers[algorithm].__name__}, " + f"cannot also register {handler.__name__}" + ) + self._handlers[algorithm] = handler + + def get(self, algorithm: str) -> Callable | None: + return self._handlers.get(algorithm) + + def ignore(self, *algorithms: str) -> None: + for algo in algorithms: + if algo in self._handlers: + raise RuntimeError(f"Algorithm {algo!r} is both registered and ignored") + self._ignored.add(algo) + + def is_ignored(self, algorithm: str) -> bool: + return algorithm in self._ignored + + +_registry = _Registry() + + +def register(*algorithms: str): + """Decorator to register a handler for one or more Wycheproof algorithm values.""" + + def decorator(func): + for algo in algorithms: + _registry.register(algo, func) + return func + + return decorator + + +@dataclass +class _FileResult: + """Result of processing a single JSON file.""" + + filename: str + category: str # "claimed", "unclaimed", "ignored", "no_algorithm" + algorithm: str | None = None + passed: int = 0 + failed: int = 0 + errors: int = 0 + skipped: int = 0 + skip_reasons: dict[str, int] = field(default_factory=dict) + output: str = "" + + +def _process_file(args: tuple[str, int]) -> _FileResult: + json_path_str, verbosity = args + json_path = Path(json_path_str) + filename = json_path.name + + data = json.loads(json_path.read_bytes()) + algorithm = data.get("algorithm") + + if algorithm is None: + return _FileResult(filename, "no_algorithm") + if _registry.is_ignored(algorithm): + return _FileResult(filename, "ignored", algorithm=algorithm) + handler = _registry.get(algorithm) + if handler is None: + return _FileResult(filename, "unclaimed", algorithm=algorithm) + + result = _FileResult(filename, "claimed", algorithm=algorithm) + out = io.StringIO() + + if verbosity >= 2: + print(f"{filename} ({algorithm}):", file=out) + + for group in data["testGroups"]: + for test in group["tests"]: + tc_id = test.get("tcId", "?") + try: + handler(data, group, test) + result.passed += 1 + if verbosity >= 2: + print(f" PASS: test {tc_id}", file=out) + except TestSkip as e: + result.skipped += 1 + reason = str(e) + result.skip_reasons[reason] = result.skip_reasons.get(reason, 0) + 1 + if verbosity >= 2: + print(f" SKIP: test {tc_id}: {reason}", file=out) + except TestFailure as e: + result.failed += 1 + print(f"\nFAIL: # Wycheproof test {tc_id} from {filename}", file=out) + if e.fields: + for key, value in e.fields.items(): + print(f"{key} = {value}", file=out) + else: + print(f" {e}", file=out) + except Exception as e: + result.errors += 1 + print(f"\nERROR: # Wycheproof test {tc_id} from {filename}", file=out) + print(f" {type(e).__name__}: {e}", file=out) + print(traceback.format_exc(), file=out) + + result.output = out.getvalue() + return result + + +def _git_rev(directory: str) -> str | None: + """Return the git revision of a directory, or None if not a git repo.""" + try: + result = subprocess.run( + ["git", "rev-parse", "HEAD"], + cwd=directory, + capture_output=True, + text=True, + timeout=5, + check=True, + ) + if result.returncode == 0: + return result.stdout.strip() + except (FileNotFoundError, subprocess.TimeoutExpired, subprocess.CalledProcessError): + pass + return None + + +def run( + wycheproof_dir: str, + verbosity: int = 1, + jobs: int | None = None, + filters: list[str] | None = None, +) -> int: + """Main entry point. Returns 0 on success, 1 on failure.""" + tv_dir = Path(wycheproof_dir) / "testvectors_v1" + if not tv_dir.is_dir(): + print(f"ERROR: {tv_dir} is not a directory") + return 1 + + wycheproof_rev = _git_rev(wycheproof_dir) + + json_paths = sorted(tv_dir.glob("*.json")) + + if filters: + filters_lower = [f.lower() for f in filters] + filtered = [] + for p in json_paths: + if any(f in p.name.lower() for f in filters_lower): + filtered.append(p) + continue + algo = json.loads(p.read_bytes()).get("algorithm", "") + if any(f in algo.lower() for f in filters_lower): + filtered.append(p) + json_paths = filtered + + work = [(str(p), verbosity) for p in json_paths] + + if jobs == 1: + file_results = [_process_file(item) for item in work] + else: + with multiprocessing.Pool(jobs) as pool: + file_results = pool.map(_process_file, work) + + # Aggregate results and print buffered output + passed = 0 + failed = 0 + errors = 0 + skipped = 0 + files_claimed = 0 + files_ignored = 0 + unclaimed: list[tuple[str, str]] = [] + no_algorithm: list[str] = [] + skip_reasons: Counter[str] = Counter() + + for fr in file_results: + if fr.output: + sys.stderr.write(fr.output) + + if fr.category == "no_algorithm": + no_algorithm.append(fr.filename) + elif fr.category == "ignored": + files_ignored += 1 + elif fr.category == "unclaimed": + unclaimed.append((fr.filename, fr.algorithm)) + else: + files_claimed += 1 + passed += fr.passed + failed += fr.failed + errors += fr.errors + skipped += fr.skipped + for reason, count in fr.skip_reasons.items(): + skip_reasons[reason] += count + + # Print summary + total_files = files_claimed + files_ignored + len(unclaimed) + len(no_algorithm) + total_tests = passed + failed + errors + skipped + + print("Wycheproof Results") + print("Botan version: %s" % (botan.version_string())) + print("Wycheproof revision: %s" % (wycheproof_rev)) + + print("Total %d Passed %d Failed %d Errors %d Skipped %d" % (total_tests, passed, failed, errors, skipped)) + + print( + f"Files: {total_files} total, {files_claimed} claimed, " + f"{files_ignored} ignored, {len(unclaimed)} unclaimed", + ) + + if verbosity >= 1: + if skip_reasons: + print("\nSkipped tests (by reason):") + for reason, count in skip_reasons.most_common(): + print(f" {count}x: {reason}") + + if unclaimed: + print(f"\nUnclaimed files ({len(unclaimed)}):") + for filename, algorithm in unclaimed: + print(f" {filename} ({algorithm})") + + if no_algorithm: + print(f"\nFiles without algorithm field ({len(no_algorithm)}):") + for filename in no_algorithm: + print(f" {filename}") + + return 0 if (failed == 0 and errors == 0) else 1 + + +# ---- Common utilities ---- + + +def _from_hex(value: str) -> bytes: + return binascii.unhexlify(value) + + +def _b64url_decode(s: str) -> bytes: + return base64.urlsafe_b64decode(s + "=" * (4 - len(s) % 4)) + + +_CURVE_NAME_MAP = { + "brainpoolP224r1": "brainpool224r1", + "brainpoolP256r1": "brainpool256r1", + "brainpoolP320r1": "brainpool320r1", + "brainpoolP384r1": "brainpool384r1", + "brainpoolP512r1": "brainpool512r1", + "P-256": "secp256r1", + "P-384": "secp384r1", + "P-521": "secp521r1", +} + +_HASH_NAME_MAP = { + "SHA-1": "SHA-1", + "SHA-224": "SHA-224", + "SHA-256": "SHA-256", + "SHA-384": "SHA-384", + "SHA-512": "SHA-512", + "SHA-512/256": "SHA-512-256", + "SHA3-224": "SHA-3(224)", + "SHA3-256": "SHA-3(256)", + "SHA3-384": "SHA-3(384)", + "SHA3-512": "SHA-3(512)", + "SHAKE128": "SHAKE-128(256)", + "SHAKE256": "SHAKE-256(512)", +} + + +# ---- AEAD handler ---- + +_AEAD_CIPHER_ALIASES = { + "AES-GCM": "AES", + "AES-CCM": "AES", + "AEAD-AES-SIV-CMAC": "AES-SIV-CMAC", + "ARIA-GCM": "ARIA", + "ARIA-CCM": "ARIA", + "CAMELLIA-CCM": "Camellia", + "SEED-GCM": "SEED", + "SEED-CCM": "SEED", + "SM4-GCM": "SM4", + "SM4-CCM": "SM4", +} + + +def _aead_algorithm( + algorithm: str, key_size_bits: int, tag_size_bits: int | None, nonce_len: int +) -> str: + tag_len_bytes = tag_size_bits // 8 if tag_size_bits is not None else None + + if algorithm in ("AEAD-AES-SIV-CMAC", "AES-SIV-CMAC"): + return f"AES-{key_size_bits // 2}/SIV" + if algorithm == "AES-EAX": + return f"AES-{key_size_bits}/EAX" + if algorithm in ("CHACHA20-POLY1305", "XCHACHA20-POLY1305"): + return "ChaCha20Poly1305" + + cipher = _AEAD_CIPHER_ALIASES.get(algorithm) + if cipher is None: + raise ValueError(f"Unsupported AEAD algorithm: {algorithm}") + + if cipher in ("AES", "ARIA", "Camellia"): + cipher = f"{cipher}-{key_size_bits}" + + if algorithm.endswith("GCM"): + suffix = f"/GCM({tag_len_bytes})" if tag_len_bytes is not None else "/GCM" + return f"{cipher}{suffix}" + + if algorithm.endswith("CCM"): + l_val = 15 - nonce_len + if tag_len_bytes is None: + return f"{cipher}/CCM({l_val})" + return f"{cipher}/CCM({tag_len_bytes},{l_val})" + + raise ValueError(f"Unhandled AEAD algorithm: {algorithm}") + + +def _aead_process( + aead: botan.SymmetricCipher, iv: bytes, aad: bytes, data: bytes +) -> bytes: + aead.set_assoc_data(aad) + aead.start(iv) + return aead.finish(data) + + +@register( + "AES-GCM", + "AES-CCM", + "AES-EAX", + "AEAD-AES-SIV-CMAC", + "AES-SIV-CMAC", + "ARIA-GCM", + "ARIA-CCM", + "CAMELLIA-CCM", + "SEED-GCM", + "SEED-CCM", + "SM4-GCM", + "SM4-CCM", + "CHACHA20-POLY1305", + "XCHACHA20-POLY1305", +) +def handle_aead(data: dict, group: dict, test: dict) -> None: + algorithm = data["algorithm"] + is_siv = algorithm in ("AEAD-AES-SIV-CMAC", "AES-SIV-CMAC") + + tag_size_bits = group.get("tagSize") + key_size_bits = group.get("keySize") + + key = _from_hex(test["key"]) + iv = _from_hex(test.get("iv", "")) + aad = _from_hex(test.get("aad", "")) + msg = _from_hex(test["msg"]) + ct = _from_hex(test["ct"]) + tag = _from_hex(test["tag"]) if "tag" in test else b"" + expected_ct_tag = (tag + ct) if is_siv else (ct + tag) + + algo = _aead_algorithm(algorithm, key_size_bits, tag_size_bits, len(iv)) + + def _fields(**extra): + fields = {"Key": test["key"], "Msg": test["msg"]} + if "iv" in test: + fields["Nonce"] = test["iv"] + if "aad" in test: + fields["AD"] = test["aad"] + fields["CT"] = test["ct"] + if "tag" in test: + fields["Tag"] = test["tag"] + fields.update(extra) + return fields + + if test["result"] == "valid": + enc = botan.SymmetricCipher(algo, True) + enc.set_key(key) + enc_out = _aead_process(enc, iv, aad, msg) + if enc_out != expected_ct_tag: + raise TestFailure(_fields(ComputedCT=enc_out.hex())) + + dec = botan.SymmetricCipher(algo, False) + dec.set_key(key) + dec_out = _aead_process(dec, iv, aad, expected_ct_tag) + if dec_out != msg: + raise TestFailure(_fields(DecryptedMsg=dec_out.hex())) + elif test["result"] in ("invalid", "acceptable"): + try: + dec = botan.SymmetricCipher(algo, False) + dec.set_key(key) + dec_out = _aead_process(dec, iv, aad, expected_ct_tag) + if test["result"] == "invalid": + raise TestFailure( + _fields( + DecryptedMsg=dec_out.hex(), + Note="Invalid AEAD test decrypted without authentication failure", + ) + ) + if test["result"] == "acceptable" and dec_out != msg: + raise TestFailure( + _fields( + DecryptedMsg=dec_out.hex(), + Note="Acceptable test decrypted to wrong plaintext", + ) + ) + except botan.BotanException: + pass + else: + raise TestFailure(f"Unknown test result: {test['result']}") + + +# ---- ECDH handler ---- + + +@register("ECDH") +def handle_ecdh(_data: dict, group: dict, test: dict) -> None: + group_type = group.get("type", "") + if group_type not in ( + "EcdhTest", + "EcdhEcpointTest", + "EcdhPemTest", + "EcdhWebcryptoTest", + ): + raise TestSkip(f"ECDH group type {group_type!r} not supported") + + curve = _CURVE_NAME_MAP.get(group["curve"], group["curve"]) + if not botan.ECGroup.supports_named_group(curve): + raise TestSkip(f"Curve {curve} not supported in this build") + + try: + if group_type == "EcdhPemTest": + # PEM EC keys load as ECDSA by default; re-create as ECDH + pem_key = botan.PrivateKey.load(test["private"].encode()) + priv_key = botan.PrivateKey.load_ecdh(curve, pem_key.get_field("x")) + elif group_type == "EcdhWebcryptoTest": + priv_d = int.from_bytes(_b64url_decode(test["private"]["d"]), "big") + priv_key = botan.PrivateKey.load_ecdh(curve, botan.MPI(priv_d)) + else: + priv_key = botan.PrivateKey.load_ecdh( + curve, botan.MPI(test["private"], radix=16) + ) + except botan.BotanException: + if test["result"] != "valid": + return + raise + + if group_type == "EcdhEcpointTest": + pub_raw = _from_hex(test["public"]) + elif group_type == "EcdhWebcryptoTest": + if "InvalidPublic" in test.get("flags", []): + raise TestSkip("JWK structural validation test") + pub_x = _b64url_decode(test["public"]["x"]) + pub_y = _b64url_decode(test["public"]["y"]) + pub_raw = b"\x04" + pub_x + pub_y + elif group_type == "EcdhPemTest": + try: + pub_key = botan.PublicKey.load(test["public"].encode()) + except botan.BotanException: + if test["result"] != "valid": + return + raise + if pub_key.used_explicit_encoding(): + if test["result"] not in ("invalid", "acceptable"): + raise TestFailure( + { + "Curve": curve, + "Note": "Explicit curve encoding on valid test", + } + ) + return + pub_raw = pub_key.to_raw() + else: + try: + pub_key = botan.PublicKey.load(_from_hex(test["public"])) + except botan.BotanException: + if test["result"] != "valid": + return + raise + + if pub_key.used_explicit_encoding(): + if test["result"] not in ("invalid", "acceptable"): + raise TestFailure( + { + "Curve": curve, + "Private": test["private"], + "Public": test["public"], + "Note": "Explicit curve encoding on valid test", + } + ) + return + pub_raw = pub_key.to_raw() + + try: + ka = botan.PKKeyAgreement(priv_key, "Raw") + shared_secret = ka.agree(pub_raw, 0, b"") + except botan.BotanException: + if test["result"] != "valid": + return + raise + + if test["result"] not in ("valid", "acceptable"): + raise TestFailure( + { + "Curve": curve, + "Private": test["private"], + "Public": test["public"], + "Shared": shared_secret.hex(), + "Note": "Invalid test case produced a shared secret", + } + ) + + expected = _from_hex(test["shared"]) + if shared_secret != expected: + raise TestFailure( + { + "Curve": curve, + "Private": test["private"], + "Public": test["public"], + "Shared": test["shared"], + "ComputedShared": shared_secret.hex(), + } + ) + + +# ---- ECDSA handler ---- + + +@register("ECDSA") +def handle_ecdsa(_data: dict, group: dict, test: dict) -> None: + botan_hash = _map_hash(group["sha"]) + curve = _CURVE_NAME_MAP.get( + group["publicKey"]["curve"], group["publicKey"]["curve"] + ) + if not botan.ECGroup.supports_named_group(curve): + raise TestSkip(f"Curve {curve} not supported in this build") + + try: + pub_key = botan.PublicKey.load(_from_hex(group["publicKeyDer"])) + except botan.BotanException: + if test["result"] != "valid": + return + raise + + group_type = group["type"] + if group_type == "EcdsaBitcoinVerify": + raise TestSkip("Bitcoin variant of ECDSA is not supported") + + if group_type in ("EcdsaVerify", "EcdsaBitcoinVerify"): + use_der = True + elif group_type == "EcdsaP1363Verify": + use_der = False + else: + raise TestFailure(f"Unknown test group type: {group_type}") + + try: + verifier = botan.PKVerify(pub_key, botan_hash, der=use_der) + verifier.update(_from_hex(test["msg"])) + valid = verifier.check_signature(_from_hex(test["sig"])) + except botan.BotanException: + if test["result"] != "valid": + return + raise + + expected_valid = test["result"] == "valid" + if valid != expected_valid: + raise TestFailure( + { + "Curve": curve, + "Hash": group["sha"], + "Msg": test["msg"], + "Sig": test["sig"], + "Expected": "valid" if expected_valid else "invalid", + "Got": "valid" if valid else "invalid", + } + ) + + +# ---- HKDF handler ---- + +_HKDF_ALIASES = { + "HKDF-SHA-1": "HKDF(SHA-1)", + "HKDF-SHA-256": "HKDF(SHA-256)", + "HKDF-SHA-384": "HKDF(SHA-384)", + "HKDF-SHA-512": "HKDF(SHA-512)", +} + + +@register("HKDF-SHA-1", "HKDF-SHA-256", "HKDF-SHA-384", "HKDF-SHA-512") +def handle_hkdf(data: dict, _group: dict, test: dict) -> None: + algo = _HKDF_ALIASES.get(data["algorithm"]) + if algo is None: + raise ValueError(f"Unsupported HKDF algorithm: {data['algorithm']}") + + ikm = _from_hex(test["ikm"]) + salt = _from_hex(test["salt"]) + info = _from_hex(test["info"]) + size = test["size"] + expected = _from_hex(test["okm"]) + + try: + actual = botan.kdf(algo, ikm, size, salt, info) + except botan.BotanException: + if test["result"] in ("invalid", "acceptable"): + return + raise + + if test["result"] == "valid": + if actual != expected: + raise TestFailure( + { + "IKM": test["ikm"], + "Salt": test["salt"], + "Info": test["info"], + "Size": str(size), + "OKM": test["okm"], + "ComputedOKM": actual.hex(), + } + ) + elif test["result"] == "invalid": + if actual == expected: + raise TestFailure( + { + "IKM": test["ikm"], + "Salt": test["salt"], + "Info": test["info"], + "Size": str(size), + "OKM": test["okm"], + "Note": "Invalid test produced matching output", + } + ) + elif test["result"] == "acceptable": + pass + else: + raise TestFailure(f"Unknown test result: {test['result']}") + + +# ---- PBKDF2 handler ---- + +_PBKDF2_ALIASES = { + "PBKDF2-HMACSHA1": "PBKDF2(SHA-1)", + "PBKDF2-HMACSHA224": "PBKDF2(SHA-224)", + "PBKDF2-HMACSHA256": "PBKDF2(SHA-256)", + "PBKDF2-HMACSHA384": "PBKDF2(SHA-384)", + "PBKDF2-HMACSHA512": "PBKDF2(SHA-512)", +} + + +@register( + "PBKDF2-HMACSHA1", + "PBKDF2-HMACSHA224", + "PBKDF2-HMACSHA256", + "PBKDF2-HMACSHA384", + "PBKDF2-HMACSHA512", +) +def handle_pbkdf2(data: dict, _group: dict, test: dict) -> None: + algo = _PBKDF2_ALIASES[data["algorithm"]] + password = _from_hex(test["password"]) + salt = _from_hex(test["salt"]) + iterations = test["iterationCount"] + dk_len = test["dkLen"] + expected = _from_hex(test["dk"]) + + try: + out_buf = ctypes.create_string_buffer(dk_len) + # pylint: disable=protected-access + raw_algo = botan._ctype_str(algo) + # pylint: disable=protected-access + botan._DLL.botan_pwdhash( + raw_algo, + iterations, + 0, + 0, + out_buf, + dk_len, + password, + len(password), + salt, + len(salt), + ) + actual = out_buf.raw + except botan.BotanException: + if test["result"] in ("invalid", "acceptable"): + return + raise + + if test["result"] == "valid": + if actual != expected: + raise TestFailure( + { + "Password": test["password"], + "Salt": test["salt"], + "Iterations": str(iterations), + "DkLen": str(dk_len), + "DK": test["dk"], + "ComputedDK": actual.hex(), + } + ) + elif test["result"] == "invalid": + if actual == expected: + raise TestFailure( + { + "Password": test["password"], + "Salt": test["salt"], + "Iterations": str(iterations), + "DkLen": str(dk_len), + "DK": test["dk"], + "Note": "Invalid test produced matching output", + } + ) + elif test["result"] == "acceptable": + pass + else: + raise TestFailure(f"Unknown test result: {test['result']}") + + +# ---- MAC handler ---- + +_MAC_ALGORITHMS = { + "HMACSHA1": "HMAC(SHA-1)", + "HMACSHA224": "HMAC(SHA-224)", + "HMACSHA256": "HMAC(SHA-256)", + "HMACSHA384": "HMAC(SHA-384)", + "HMACSHA512": "HMAC(SHA-512)", + "HMACSHA3-224": "HMAC(SHA-3(224))", + "HMACSHA3-256": "HMAC(SHA-3(256))", + "HMACSHA3-384": "HMAC(SHA-3(384))", + "HMACSHA3-512": "HMAC(SHA-3(512))", + "HMACSM3": "HMAC(SM3)", + "HMACSHA512/256": "HMAC(SHA-512-256)", + "SipHash-1-3": "SipHash(1,3)", + "SipHash-2-4": "SipHash(2,4)", + "SipHash-4-8": "SipHash(4,8)", + "KMAC128": "KMAC-128", + "KMAC256": "KMAC-256", +} + +_CMAC_CIPHERS = { + "AES-CMAC": "AES", + "ARIA-CMAC": "ARIA", + "CAMELLIA-CMAC": "Camellia", +} + + +def _mac_algorithm( + algorithm: str, key_size_bits: int | None, tag_size_bits: int | None +) -> str: + if algorithm == "AES-GMAC": + if key_size_bits is None: + raise ValueError("AES-GMAC requires a key size") + return f"GMAC(AES-{key_size_bits})" + if algorithm.startswith("KMAC"): + if tag_size_bits is None: + raise ValueError("KMAC requires a tag size") + return f"{_MAC_ALGORITHMS[algorithm]}({tag_size_bits})" + if algorithm in _CMAC_CIPHERS: + if key_size_bits is None: + raise ValueError(f"{algorithm} requires a key size") + return f"CMAC({_CMAC_CIPHERS[algorithm]}-{key_size_bits})" + return _MAC_ALGORITHMS[algorithm] + + +@register( + "HMACSHA1", + "HMACSHA224", + "HMACSHA256", + "HMACSHA384", + "HMACSHA512", + "HMACSHA3-224", + "HMACSHA3-256", + "HMACSHA3-384", + "HMACSHA3-512", + "HMACSM3", + "HMACSHA512/256", + "SipHash-1-3", + "SipHash-2-4", + "SipHash-4-8", + "AES-GMAC", + "KMAC128", + "KMAC256", + "AES-CMAC", + "ARIA-CMAC", + "CAMELLIA-CMAC", +) +def handle_mac(data: dict, group: dict, test: dict) -> None: + key_size_bits = group.get("keySize") + iv_size_bits = group.get("ivSize") + tag_size_bits = group.get("tagSize") + + algo = _mac_algorithm(data["algorithm"], key_size_bits, tag_size_bits) + + try: + mac = botan.MsgAuthCode(algo) + mac.set_key(_from_hex(test["key"])) + except botan.BotanException: + if test["result"] != "valid": + return + raise + + if iv_size_bits is not None: + mac.set_nonce(_from_hex(test["iv"])) + mac.update(_from_hex(test["msg"])) + + final_mac = mac.final() + actual_mac = ( + final_mac[: tag_size_bits // 8] if tag_size_bits is not None else final_mac + ) + expected = _from_hex(test["tag"]) + + if test["result"] == "valid": + if actual_mac != expected: + raise TestFailure( + { + "Key": test["key"], + "Msg": test["msg"], + "Tag": test["tag"], + "ComputedTag": actual_mac.hex(), + } + ) + elif test["result"] == "invalid": + if actual_mac == expected: + raise TestFailure( + { + "Key": test["key"], + "Msg": test["msg"], + "Tag": test["tag"], + "Note": "Invalid test produced matching MAC", + } + ) + elif test["result"] == "acceptable": + pass + else: + raise TestFailure(f"Unknown test result: {test['result']}") + + +# ---- ML-DSA handler ---- + +_MLDSA_MODE_MAP = { + "ML-DSA-44": "ML-DSA-4x4", + "ML-DSA-65": "ML-DSA-6x5", + "ML-DSA-87": "ML-DSA-8x7", +} + + +def _mldsa_sign_test(mldsa_mode: str, group: dict, test: dict) -> None: + priv = None + try: + if "privateSeed" in group: + priv = botan.PrivateKey.load_ml_dsa( + mldsa_mode, _from_hex(group["privateSeed"]) + ) + # TODO: implement loading from expanded private key + except botan.BotanException: + if test["result"] == "invalid": + return + raise + + if priv is None: + raise TestSkip("ML-DSA noseed (expanded private key) not yet supported") + + pub = priv.get_public_key() + if "publicKey" in group and group["publicKey"] is not None: + expected_pk = _from_hex(group["publicKey"]) + if pub.to_raw() != expected_pk: + raise TestFailure( + { + "Mode": mldsa_mode, + "PrivateSeed": group.get("privateSeed", ""), + "PublicKey": group["publicKey"], + "ComputedPublicKey": pub.to_raw().hex(), + "Note": "Deserialized public key does not match expected", + } + ) + + try: + signer = botan.PKSign(priv, "Deterministic") + signer.update(_from_hex(test["msg"])) + actual_sig = signer.finish(NullRNG()) + except botan.BotanException: + if test["result"] == "invalid": + return + raise + + expected_sig = _from_hex(test["sig"]) + if actual_sig != expected_sig: + raise TestFailure( + { + "Mode": mldsa_mode, + "Msg": test["msg"], + "Sig": test["sig"], + "ComputedSig": actual_sig.hex(), + } + ) + + +def _mldsa_verify_test(mldsa_mode: str, group: dict, test: dict) -> None: + try: + pub = botan.PublicKey.load_ml_dsa(mldsa_mode, _from_hex(group["publicKey"])) + except botan.BotanException: + if test["result"] == "invalid": + return + raise + + verifier = botan.PKVerify(pub, "") + verifier.update(_from_hex(test["msg"])) + valid = verifier.check_signature(_from_hex(test["sig"])) + + expected_valid = test["result"] == "valid" + if valid != expected_valid: + raise TestFailure( + { + "Mode": mldsa_mode, + "Msg": test["msg"], + "Sig": test["sig"], + "Expected": "valid" if expected_valid else "invalid", + "Got": "valid" if valid else "invalid", + } + ) + + +@register("ML-DSA-44", "ML-DSA-65", "ML-DSA-87") +def handle_mldsa(data: dict, group: dict, test: dict) -> None: + ctx = test.get("ctx") + if ctx is not None and ctx != "": + raise TestSkip("ML-DSA ctx not supported") + + if "msg" not in test or ("flags" in test and "Internal" in test["flags"]): + raise TestSkip("ML-DSA's Sign_internal interface is not exposed") + + mldsa_mode = _MLDSA_MODE_MAP.get( + data.get("algorithm", ""), data.get("algorithm", "") + ) + group_type = group.get("type") + + if group_type == "MlDsaSign": + _mldsa_sign_test(mldsa_mode, group, test) + elif group_type == "MlDsaVerify": + _mldsa_verify_test(mldsa_mode, group, test) + else: + raise TestFailure(f"Unknown test group type: {group_type}") + + +# ---- ML-KEM handler ---- + + +@register("ML-KEM") +def handle_mlkem(_data: dict, group: dict, test: dict) -> None: + mlkem_mode = group["parameterSet"] + + group_type = group.get("type", "") + if group_type == "MLKEMDecapsValidationTest": + raise TestSkip("ML-KEM semi-expanded decapsulation not yet supported") + + priv = None + pub = None + + if "seed" in test: + try: + priv = botan.PrivateKey.load_ml_kem(mlkem_mode, _from_hex(test["seed"])) + except botan.BotanException: + if test["result"] == "invalid": + return + raise + pub = priv.get_public_key() + + if "ek" in test: + expected_ek = _from_hex(test["ek"]) + if pub is None: + try: + pub = botan.PublicKey.load_ml_kem(mlkem_mode, expected_ek) + except botan.BotanException: + if test["result"] == "invalid": + return + raise + + if pub.to_raw() != expected_ek: + raise TestFailure( + { + "Mode": mlkem_mode, + "Seed": test.get("seed", ""), + "EK": test["ek"], + "ComputedEK": pub.to_raw().hex(), + } + ) + + if not pub: + raise ValueError("No public key available in this test vector") + + if "dk" in test: + try: + expected_dk = _from_hex(test["dk"]) + priv2 = botan.PrivateKey.load_ml_kem(mlkem_mode, expected_dk) + # TODO: currently we cannot export the expanded private key via the python API + if priv2.to_raw() != expected_dk: + raise TestFailure( + { + "Mode": mlkem_mode, + "DK": test["dk"], + "ComputedDK": priv2.to_raw().hex(), + } + ) + if priv2.get_public_key().to_raw() != pub.to_raw(): + raise TestFailure( + { + "Mode": mlkem_mode, + "DK": test["dk"], + "Note": "Private key's public key does not match expected", + } + ) + except botan.BotanException: + if test["result"] in ("invalid", "acceptable"): + return + raise + + if "c" in test and "K" in test: + expected_k = _from_hex(test["K"]) + expected_c = _from_hex(test["c"]) + + if "m" in test: + rng = FixedOutputRNG(_from_hex(test["m"])) + kem_e = botan.KemEncrypt(pub, "Raw") + actual_k, actual_c = kem_e.create_shared_key(rng, b"", len(expected_k)) + if actual_k != expected_k: + raise TestFailure( + { + "Mode": mlkem_mode, + "K": test["K"], + "ComputedK": actual_k.hex(), + } + ) + if actual_c != expected_c: + raise TestFailure( + { + "Mode": mlkem_mode, + "C": test["c"], + "ComputedC": actual_c.hex(), + } + ) + + if priv is not None: + kem_d = botan.KemDecrypt(priv, "Raw") + try: + actual_k = kem_d.decrypt_shared_key(b"", len(expected_k), expected_c) + except botan.BotanException: + if test["result"] in ("invalid", "acceptable"): + return + raise + + if test["result"] == "valid": + if actual_k != expected_k: + raise TestFailure( + { + "Mode": mlkem_mode, + "K": test["K"], + "C": test["c"], + "ComputedK": actual_k.hex(), + } + ) + elif test["result"] == "invalid": + if actual_k == expected_k: + raise TestFailure( + { + "Mode": mlkem_mode, + "K": test["K"], + "C": test["c"], + "Note": "Invalid test produced matching shared key", + } + ) + elif test["result"] == "acceptable": + pass + else: + raise TestFailure(f"Unknown test result: {test['result']}") + + +# ---- Symmetric cipher handlers (non-AEAD) ---- + +_BLOCK_CIPHER_MAP = { + "AES-CBC-PKCS5": ("AES", "/CBC/PKCS7", 1), + "ARIA-CBC-PKCS5": ("ARIA", "/CBC/PKCS7", 1), + "CAMELLIA-CBC-PKCS5": ("Camellia", "/CBC/PKCS7", 1), + "AES-XTS": ("AES", "/XTS", 2), +} + + +@register("AES-CBC-PKCS5", "ARIA-CBC-PKCS5", "CAMELLIA-CBC-PKCS5", "AES-XTS") +def handle_block_cipher(data: dict, group: dict, test: dict) -> None: + cipher_base, mode, key_divisor = _BLOCK_CIPHER_MAP[data["algorithm"]] + key_size = group["keySize"] // key_divisor + algo = f"{cipher_base}-{key_size}{mode}" + + key = _from_hex(test["key"]) + iv = _from_hex(test["iv"]) + + msg = _from_hex(test["msg"]) + ct = _from_hex(test["ct"]) + + def _fields(**extra): + fields = { + "Key": test["key"], + "IV": test["iv"], + "Msg": test["msg"], + "CT": test["ct"], + } + fields.update(extra) + return fields + + if test["result"] == "valid": + try: + enc = botan.SymmetricCipher(algo, True) + enc.set_key(key) + enc.start(iv) + enc_out = enc.finish(msg) + except botan.BotanException: + # pylint: disable=raise-missing-from + raise TestFailure(_fields(Note="Encryption failed")) + if enc_out != ct: + raise TestFailure(_fields(ComputedCT=enc_out.hex())) + + try: + dec = botan.SymmetricCipher(algo, False) + dec.set_key(key) + dec.start(iv) + dec_out = dec.finish(ct) + except botan.BotanException: + # pylint: disable=raise-missing-from + raise TestFailure(_fields(Note="Decryption failed")) + if dec_out != msg: + raise TestFailure(_fields(DecryptedMsg=dec_out.hex())) + + elif test["result"] in ("invalid", "acceptable"): + try: + dec = botan.SymmetricCipher(algo, False) + dec.set_key(key) + dec.start(iv) + dec_out = dec.finish(ct) + if test["result"] == "invalid" and dec_out == msg: + raise TestFailure(_fields(Note="Invalid test decrypted successfully")) + except botan.BotanException: + pass + else: + raise TestFailure(f"Unknown test result: {test['result']}") + + +# ---- Key wrap handlers ---- + +_KEYWRAP_CIPHERS = { + "AES-WRAP": "AES-{keySize}", + "AES-KWP": "AES-{keySize}", + "ARIA-WRAP": "ARIA-{keySize}", + "ARIA-KWP": "ARIA-{keySize}", + "CAMELLIA-WRAP": "Camellia-{keySize}", + "SEED-WRAP": "SEED", +} + + +def _keywrap_cipher(algorithm: str, key_size: int) -> str: + template = _KEYWRAP_CIPHERS[algorithm] + return template.format(keySize=key_size) + + +@register("AES-WRAP", "ARIA-WRAP", "CAMELLIA-WRAP", "SEED-WRAP") +def handle_keywrap(data: dict, group: dict, test: dict) -> None: + cipher = _keywrap_cipher(data["algorithm"], group["keySize"]) + key = _from_hex(test["key"]) + msg = _from_hex(test["msg"]) + ct = _from_hex(test["ct"]) + + if test["result"] == "valid": + wrapped = botan.nist_key_wrap(key, msg, cipher) + if wrapped != ct: + raise TestFailure( + { + "Key": test["key"], + "Msg": test["msg"], + "CT": test["ct"], + "ComputedCT": wrapped.hex(), + } + ) + try: + unwrapped = botan.nist_key_unwrap(key, ct, cipher) + except botan.BotanException: + # pylint: disable=raise-missing-from + raise TestFailure( + { + "Key": test["key"], + "CT": test["ct"], + "Note": "Valid wrap unwrap failed", + } + ) + if unwrapped != msg: + raise TestFailure( + { + "Key": test["key"], + "CT": test["ct"], + "Msg": test["msg"], + "ComputedMsg": unwrapped.hex(), + } + ) + elif test["result"] == "invalid": + try: + unwrapped = botan.nist_key_unwrap(key, ct, cipher) + if unwrapped == msg: + raise TestFailure( + { + "Key": test["key"], + "CT": test["ct"], + "Note": "Invalid wrap unwrapped successfully", + } + ) + except botan.BotanException: + pass + elif test["result"] == "acceptable": + pass + else: + raise TestFailure(f"Unknown test result: {test['result']}") + + +@register("AES-KWP", "ARIA-KWP") +def handle_keywrap_padded(data: dict, group: dict, test: dict) -> None: + cipher = _keywrap_cipher(data["algorithm"], group["keySize"]) + key = _from_hex(test["key"]) + msg = _from_hex(test["msg"]) + ct = _from_hex(test["ct"]) + + if test["result"] == "valid": + wrapped = botan.nist_key_wrap_padded(key, msg, cipher) + if wrapped != ct: + raise TestFailure( + { + "Key": test["key"], + "Msg": test["msg"], + "CT": test["ct"], + "ComputedCT": wrapped.hex(), + } + ) + try: + unwrapped = botan.nist_key_unwrap_padded(key, ct, cipher) + except botan.BotanException: + # pylint: disable=raise-missing-from + raise TestFailure( + { + "Key": test["key"], + "CT": test["ct"], + "Note": "Valid padded wrap unwrap failed", + } + ) + if unwrapped != msg: + raise TestFailure( + { + "Key": test["key"], + "CT": test["ct"], + "Msg": test["msg"], + "ComputedMsg": unwrapped.hex(), + } + ) + elif test["result"] == "invalid": + try: + unwrapped = botan.nist_key_unwrap_padded(key, ct, cipher) + if unwrapped == msg: + raise TestFailure( + { + "Key": test["key"], + "CT": test["ct"], + "Note": "Invalid padded wrap unwrapped successfully", + } + ) + except botan.BotanException: + pass + elif test["result"] == "acceptable": + pass + else: + raise TestFailure(f"Unknown test result: {test['result']}") + + +# ---- DSA handler ---- + + +@register("DSA") +def handle_dsa(_data: dict, group: dict, test: dict) -> None: + group_type = group["type"] + if group_type == "DsaVerify": + use_der = True + elif group_type == "DsaP1363Verify": + use_der = False + else: + raise TestFailure(f"Unknown DSA group type: {group_type}") + + try: + pub_key = botan.PublicKey.load(_from_hex(group["publicKeyDer"])) + except botan.BotanException: + if test["result"] != "valid": + return + raise + + botan_hash = _map_hash(group["sha"]) + + try: + verifier = botan.PKVerify(pub_key, botan_hash, der=use_der) + verifier.update(_from_hex(test["msg"])) + valid = verifier.check_signature(_from_hex(test["sig"])) + except botan.BotanException: + if test["result"] != "valid": + return + raise + + expected_valid = test["result"] == "valid" + if valid != expected_valid: + raise TestFailure( + { + "Hash": group["sha"], + "Msg": test["msg"], + "Sig": test["sig"], + "Expected": "valid" if expected_valid else "invalid", + "Got": "valid" if valid else "invalid", + } + ) + + +# ---- EdDSA handler ---- + + +@register("EDDSA") +def handle_eddsa(_data: dict, group: dict, test: dict) -> None: + try: + pub_key = botan.PublicKey.load(_from_hex(group["publicKeyDer"])) + except botan.BotanException: + if test["result"] != "valid": + return + raise + + try: + verifier = botan.PKVerify(pub_key, "Pure") + verifier.update(_from_hex(test["msg"])) + valid = verifier.check_signature(_from_hex(test["sig"])) + except botan.BotanException: + if test["result"] != "valid": + return + raise + + expected_valid = test["result"] == "valid" + if valid != expected_valid: + raise TestFailure( + { + "Curve": group["publicKey"]["curve"], + "Msg": test["msg"], + "Sig": test["sig"], + "Expected": "valid" if expected_valid else "invalid", + "Got": "valid" if valid else "invalid", + } + ) + + +# ---- RSA signature handlers ---- + + +def _map_hash(name: str) -> str: + h = _HASH_NAME_MAP.get(name) + if h is None: + raise TestSkip(f"Hash {name} not supported") + return h + + +def _rsa_pss_padding(group: dict) -> str: + if group.get("mgf") != "MGF1": + raise TestSkip(f"PSS with {group.get('mgf')} MGF not supported") + sha = _map_hash(group["sha"]) + mgf_sha = _map_hash(group["mgfSha"]) + if sha != mgf_sha: + raise TestSkip("PSS with different MGF hash not supported") + s_len = group["sLen"] + return f"PSSR({sha},MGF1,{s_len})" + + +def _rsa_pkcs1_padding(group: dict) -> str: + return f"EMSA3({_map_hash(group['sha'])})" + + +def _rsa_verify(pub_key, padding: str, test: dict) -> None: + try: + verifier = botan.PKVerify(pub_key, padding) + verifier.update(_from_hex(test["msg"])) + valid = verifier.check_signature(_from_hex(test["sig"])) + except botan.BotanException: + if test["result"] != "valid": + return + raise + + expected_valid = test["result"] == "valid" + if valid != expected_valid: + raise TestFailure( + { + "Msg": test["msg"], + "Sig": test["sig"], + "Padding": padding, + "Expected": "valid" if expected_valid else "invalid", + "Got": "valid" if valid else "invalid", + } + ) + + +@register("RSASSA-PKCS1-v1_5") +def handle_rsa_pkcs1_sig(_data: dict, group: dict, test: dict) -> None: + group_type = group["type"] + + if group_type == "RsassaPkcs1Verify": + try: + pub_key = botan.PublicKey.load(_from_hex(group["publicKeyDer"])) + except botan.BotanException: + if test["result"] != "valid": + return + raise + _rsa_verify(pub_key, _rsa_pkcs1_padding(group), test) + + elif group_type == "RsassaPkcs1Generate": + try: + priv_key = botan.PrivateKey.load(_from_hex(group["privateKeyPkcs8"])) + except botan.BotanException: + if test["result"] != "valid": + return + raise + + padding = _rsa_pkcs1_padding(group) + try: + signer = botan.PKSign(priv_key, padding) + signer.update(_from_hex(test["msg"])) + actual_sig = signer.finish(botan.RandomNumberGenerator("system")) + except botan.BotanException: + if test["result"] != "valid": + return + raise + + expected_sig = _from_hex(test["sig"]) + if actual_sig != expected_sig: + raise TestFailure( + { + "Msg": test["msg"], + "Padding": padding, + "Sig": test["sig"], + "ComputedSig": actual_sig.hex(), + } + ) + else: + raise TestFailure(f"Unknown RSA PKCS1 sig group type: {group_type}") + + +@register("RSASSA-PSS") +def handle_rsa_pss_sig(_data: dict, group: dict, test: dict) -> None: + group_type = group["type"] + + if group_type not in ("RsassaPssVerify", "RsassaPssWithParametersVerify"): + raise TestFailure(f"Unknown RSA PSS group type: {group_type}") + + try: + pub_key = botan.PublicKey.load(_from_hex(group["publicKeyDer"])) + except botan.BotanException: + if test["result"] != "valid": + return + raise + + _rsa_verify(pub_key, _rsa_pss_padding(group), test) + + +# ---- RSA encryption handlers ---- + + +def _rsa_oaep_padding(group: dict) -> str: + sha = _map_hash(group["sha"]) + mgf_sha = _map_hash(group["mgfSha"]) + return f"OAEP({sha},MGF1({mgf_sha}))" + + +@register("RSAES-OAEP") +def handle_rsa_oaep(_data: dict, group: dict, test: dict) -> None: + if "otherPrimeInfos" in group.get("privateKey", {}): + raise TestSkip("Multi-prime RSA not supported") + + # Botan's OAEP label parameter only accepts string labels, not arbitrary binary + if test.get("label", "") != "": + raise TestSkip("OAEP with binary label not supported in padding string parser") + + try: + priv_key = botan.PrivateKey.load(_from_hex(group["privateKeyPkcs8"])) + except botan.BotanException: + if test["result"] != "valid": + return + raise + + padding = _rsa_oaep_padding(group) + + try: + decryptor = botan.PKDecrypt(priv_key, padding) + plaintext = decryptor.decrypt(_from_hex(test["ct"])) + except botan.BotanException: + if test["result"] != "valid": + return + raise + + expected = _from_hex(test["msg"]) + if test["result"] == "valid": + if plaintext != expected: + raise TestFailure( + { + "Ctext": test["ct"], + "Ptext": test["msg"], + "Padding": padding, + "ComputedMsg": plaintext.hex(), + } + ) + elif test["result"] == "invalid": + if plaintext == expected: + raise TestFailure( + { + "Ctext": test["ct"], + "Ptext": test["msg"], + "Padding": padding, + "Note": "Invalid test decrypted successfully", + } + ) + + +@register("RSAES-PKCS1-v1_5") +def handle_rsa_pkcs1_enc(_data: dict, group: dict, test: dict) -> None: + try: + priv_key = botan.PrivateKey.load(_from_hex(group["privateKeyPkcs8"])) + except botan.BotanException: + if test["result"] != "valid": + return + raise + + try: + decryptor = botan.PKDecrypt(priv_key, "PKCS1v15") + plaintext = decryptor.decrypt(_from_hex(test["ct"])) + except botan.BotanException: + if test["result"] != "valid": + return + raise + + expected = _from_hex(test["msg"]) + if test["result"] == "valid": + if plaintext != expected: + raise TestFailure( + { + "Ctext": test["ct"], + "Ptext": test["msg"], + "ComputedMsg": plaintext.hex(), + } + ) + elif test["result"] == "invalid": + if plaintext == expected: + raise TestFailure( + { + "Ctext": test["ct"], + "Ptext": test["msg"], + "Note": "Invalid test decrypted successfully", + } + ) + + +# ---- XDH handler (X25519, X448) ---- + +_XDH_LOAD_RAW = { + "curve25519": botan.PrivateKey.load_x25519, + "curve448": botan.PrivateKey.load_x448, +} + + +@register("XDH") +def handle_xdh(_data: dict, group: dict, test: dict) -> None: + group_type = group.get("type", "") + curve = group.get("curve", "") + + expected = _from_hex(test["shared"]) + + # Load private key and public key bytes based on encoding format + try: + if group_type == "XdhComp": + load_fn = _XDH_LOAD_RAW.get(curve) + if load_fn is None: + raise TestSkip(f"XDH curve {curve} not supported") + priv_key = load_fn(_from_hex(test["private"])) + pub_bytes = _from_hex(test["public"]) + elif group_type in ("XdhAsnComp", "XdhPemComp"): + if group_type == "XdhAsnComp": + priv_key = botan.PrivateKey.load(_from_hex(test["private"])) + pub_key = botan.PublicKey.load(_from_hex(test["public"])) + else: + priv_key = botan.PrivateKey.load(test["private"].encode()) + pub_key = botan.PublicKey.load(test["public"].encode()) + pub_bytes = pub_key.to_raw() + elif group_type == "XdhJwkComp": + load_fn = _XDH_LOAD_RAW.get(curve) + if load_fn is None: + raise TestSkip(f"XDH curve {curve} not supported") + # We extract raw bytes from the JWK, bypassing JWK structural + # validation (kty, crv, missing fields). Skip InvalidPublic + # tests since those test JWK parsing, not the crypto. + if "InvalidPublic" in test.get("flags", []): + raise TestSkip("JWK structural validation test") + priv_key = load_fn(_b64url_decode(test["private"]["d"])) + pub_bytes = _b64url_decode(test["public"]["x"]) + else: + raise TestSkip(f"XDH group type {group_type!r} not supported") + except (botan.BotanException, KeyError, ValueError): + if test["result"] != "valid": + return + raise + + try: + ka = botan.PKKeyAgreement(priv_key, "Raw") + shared = ka.agree(pub_bytes, 0, b"") + except botan.BotanException: + if test["result"] != "valid": + return + raise + + if test["result"] not in ("valid", "acceptable"): + raise TestFailure( + { + "Curve": curve, + "Shared": shared.hex(), + "Note": "Invalid test case produced a shared secret", + } + ) + + if shared != expected: + raise TestFailure( + { + "Curve": curve, + "Shared": test["shared"], + "ComputedShared": shared.hex(), + } + ) + + +# ---- Primality test handler ---- + + +@register("PrimalityTest") +def handle_primality(_data: dict, _group: dict, test: dict) -> None: + value = test["value"] + if value in ["", "00"]: + return + + # This test encodes integers as signed twos complement for unclear reasons + value_bytes = _from_hex(value) + if value_bytes[0] & 0x80: + is_prime = False + else: + n = botan.MPI(value, radix=16) + rng = botan.RandomNumberGenerator("system") + is_prime = n.is_prime(rng) + + expected_prime = test["result"] == "valid" + + if is_prime != expected_prime: + raise TestFailure( + { + "Value": value, + "Expected": "prime" if expected_prime else "composite", + "Got": "prime" if is_prime else "composite", + } + ) + + +# ---- Ignored algorithms ---- + +_registry.ignore( + "AES-FF1", # Not implemented + "AES-GCM-SIV", # Not implemented + "A128CBC-HS256", # Not implemented + "A192CBC-HS384", # Not implemented + "A256CBC-HS512", # Not implemented + "AEGIS128", # Not implemented + "AEGIS128L", # Not implemented + "AEGIS256", # Not implemented + "ASCON128", # Pre-NIST Ascon not implemented + "ASCON128A", # Pre-NIST Ascon not implemented + "ASCON80PQ", # Pre-NIST Ascon not implemented + "BLS", # Not implemented + "PbeWithHmacSha1AndAes_128", # PBES2 not directly exposed in API + "PbeWithHmacSha1AndAes_192", + "PbeWithHmacSha1AndAes_256", + "PbeWithHmacSha224AndAes_128", + "PbeWithHmacSha224AndAes_192", + "PbeWithHmacSha224AndAes_256", + "PbeWithHmacSha256AndAes_128", + "PbeWithHmacSha256AndAes_192", + "PbeWithHmacSha256AndAes_256", + "PbeWithHmacSha384AndAes_128", + "PbeWithHmacSha384AndAes_192", + "PbeWithHmacSha384AndAes_256", + "PbeWithHmacSha512AndAes_128", + "PbeWithHmacSha512AndAes_192", + "PbeWithHmacSha512AndAes_256", + "EcCurveTest", # Not even clear what this is for + "MORUS640", # Not implemented + "MORUS1280", # Not implemented + "HMACSHA512/224", # Not implemented + "SipHashX-2-4", # 128-bit SipHash variant, not implemented + "SipHashX-4-8", # 128-bit SipHash variant, not implemented + "VMAC-AES", # Not implemented +) + + +# ---- Entry point ---- + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Run Wycheproof test vectors against Botan's Python bindings" + ) + parser.add_argument( + "wycheproof_dir", + nargs="?", + default=os.environ.get("WYCHEPROOF_DIR"), + help="path to Wycheproof git checkout (default: $WYCHEPROOF_DIR)", + ) + parser.add_argument("--verbose", "-v", action="store_true", help="be noisy") + parser.add_argument("--quiet", "-q", action="store_true", help="be quiet") + parser.add_argument( + "--jobs", "-j", type=int, default=None, help="number of workers" + ) + parser.add_argument( + "--filter", + "-f", + action="append", + default=[], + help="only run files matching FILTER (case-insensitive substring of filename or algorithm, may be repeated)", + ) + args = parser.parse_args() + + if args.wycheproof_dir is None: + parser.error( + "wycheproof_dir argument or WYCHEPROOF_DIR environment variable required" + ) + + jobs = args.jobs + if jobs is not None and jobs <= 0: + parser.error("Invalid --jobs parameter") + + verbosity = 0 if args.quiet else (2 if args.verbose else 1) + return run(args.wycheproof_dir, verbosity, jobs, args.filter or None) + + +if __name__ == "__main__": + sys.exit(main()) diff -Nru botan3-3.7.1+dfsg/src/tests/data/aead/ascon_aead128.vec botan3-3.12.0+dfsg/src/tests/data/aead/ascon_aead128.vec --- botan3-3.7.1+dfsg/src/tests/data/aead/ascon_aead128.vec 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/aead/ascon_aead128.vec 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,730 @@ +[Ascon-AEAD128] + +# From the Ascon reference implementation +# Only some 120 test vectors (with a plaintext and AD stride width of 3 bytes) + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = +AD = +Out = 4F9C278211BEC9316BF68F46EE8B2EC6 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = +AD = 303132 +Out = D127CF7D2CD4DA8930616C70B3619F42 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = +AD = 303132333435 +Out = 51CCBC46D56E93B89B1A3BFDAD0AA4D5 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = +AD = 303132333435363738 +Out = 24F13284A0F90F906B18C7E4061C0896 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = +AD = 303132333435363738393A3B +Out = 4B03B405717243D04CE7D1713728975E + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = +AD = 303132333435363738393A3B3C3D3E +Out = 759102A6953861627AAE1836D003A294 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = +AD = 303132333435363738393A3B3C3D3E3F4041 +Out = 118F0CCED10E0BB559A85F7EDE580836 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = +AD = 303132333435363738393A3B3C3D3E3F4041424344 +Out = 7B59AFD062513B22047EAF7F764CED9B + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = +AD = 303132333435363738393A3B3C3D3E3F4041424344454647 +Out = FAAF823952248AFD0E3FE69834C15D65 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = +AD = 303132333435363738393A3B3C3D3E3F404142434445464748494A +Out = A35C52EC6E7C78C051B23D03F691916F + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = +AD = 303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D +Out = 82E22C860881C0485EC5F5E8CEA42CEA + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122 +AD = +Out = E8C3DEAF8E12816B8EDF39AD1571A9492B7CA2 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122 +AD = 303132 +Out = 66D0D5790A715044A3E616D441C1790951404A + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122 +AD = 303132333435 +Out = 9310C6D50D94E52A31553833E8F30BE814BE02 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122 +AD = 303132333435363738 +Out = BF79D7F4CFC0E8D70C3F570F880557BC1D8CA0 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122 +AD = 303132333435363738393A3B +Out = B83A1C906083E5AEE1B974DDCD54B3409BD9C1 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122 +AD = 303132333435363738393A3B3C3D3E +Out = 20FD19FB947B6928C4F5F4930B4BD8F1ED14B3 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122 +AD = 303132333435363738393A3B3C3D3E3F4041 +Out = B3B05FD419A8BC5FC53006E86111C525F16A09 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122 +AD = 303132333435363738393A3B3C3D3E3F4041424344 +Out = CC261D266BA8C885C6FE76F85BDDB2E4CDF019 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122 +AD = 303132333435363738393A3B3C3D3E3F4041424344454647 +Out = 9D29F9D5D1876DE04C364766107C0B0AC38B59 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122 +AD = 303132333435363738393A3B3C3D3E3F404142434445464748494A +Out = E4C1BBCBF02957AE28C2B18EA1E308E13FA568 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122 +AD = 303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D +Out = 565968B49AEF3EDC9AC55BF3CA13738BB70925 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425 +AD = +Out = E8C3DEEE246C05D75242AFC7928D1157C10AB4AEE0F2 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425 +AD = 303132 +Out = 66D0D52BF4010F6A46461573F744B038D904E8E1DA2A + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425 +AD = 303132333435 +Out = 9310C6DD8E9C376C1C60A0772518BA981B0DEBE8C1AE + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425 +AD = 303132333435363738 +Out = BF79D7476D6F11611DA1356DFCD346C1A1148EF39C98 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425 +AD = 303132333435363738393A3B +Out = B83A1C62AD05ECD5E1FAFA666730152C615EF7656CDF + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425 +AD = 303132333435363738393A3B3C3D3E +Out = 20FD19DABC1AC897A91DA0799E355D110C043C313EE3 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425 +AD = 303132333435363738393A3B3C3D3E3F4041 +Out = B3B05F0A08C5E00130E3529B3A179E633F858D86688F + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425 +AD = 303132333435363738393A3B3C3D3E3F4041424344 +Out = CC261D9F8FB862D584F26DEB1043A65F8562A85FA2D8 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425 +AD = 303132333435363738393A3B3C3D3E3F4041424344454647 +Out = 9D29F9D52ADF240286529CEED53EFEE495645FC7E701 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425 +AD = 303132333435363738393A3B3C3D3E3F404142434445464748494A +Out = E4C1BB6B1B262A5403095C37BFA0E5C98D44050FD852 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425 +AD = 303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D +Out = 565968B0563D07664992086E8C0C46DB49E2C08F9841 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728 +AD = +Out = E8C3DEEE246CC5EAE3329BC950AE101B9247F3605EBEDCBF6C + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728 +AD = 303132 +Out = 66D0D52BF401C6DC1CD53C2882E73404CAACF83950F538B9CE + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728 +AD = 303132333435 +Out = 9310C6DD8E9CBC3E40D90B8789DAFBFFD15EDF4621B406DC39 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728 +AD = 303132333435363738 +Out = BF79D7476D6FB05B8819CCA281C41C56EE49AF66737E2985D1 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728 +AD = 303132333435363738393A3B +Out = B83A1C62AD05C670D6B8CA7ABEDD607BAB410117733246E064 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728 +AD = 303132333435363738393A3B3C3D3E +Out = 20FD19DABC1A5CC449CF7089E8C1C27A8BBD286393716751CB + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728 +AD = 303132333435363738393A3B3C3D3E3F4041 +Out = B3B05F0A08C5769749B51634A3A91D3437AB565186E92660F6 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728 +AD = 303132333435363738393A3B3C3D3E3F4041424344 +Out = CC261D9F8FB8842E8265A7212A2D39FCEF082C7E83EFC82D43 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728 +AD = 303132333435363738393A3B3C3D3E3F4041424344454647 +Out = 9D29F9D52ADF9470AFC07F414F69653505D497F0B8299F714A + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728 +AD = 303132333435363738393A3B3C3D3E3F404142434445464748494A +Out = E4C1BB6B1B264F12EE82C37B1EEC0E908114688368D8FE8186 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728 +AD = 303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D +Out = 565968B0563DF95EA928A99AA90D961BB17AFFE431E0721E29 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B +AD = +Out = E8C3DEEE246CC5EAE3E872313681E50093528EA0705C07B5D3DC6486 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B +AD = 303132 +Out = 66D0D52BF401C6DC1C0AD4DA5D29DF34FD308C61C71F5CED12F170BE + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B +AD = 303132333435 +Out = 9310C6DD8E9CBC3E406C0EBF0C1AA3849275BF49196BC7064C2B9964 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B +AD = 303132333435363738 +Out = BF79D7476D6FB05B8891A079821ACCDE77B9401B62C58638757D62BD + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B +AD = 303132333435363738393A3B +Out = B83A1C62AD05C670D6E9220E10128E8D21684B829D3988F82CA51E68 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B +AD = 303132333435363738393A3B3C3D3E +Out = 20FD19DABC1A5CC449A621D36115D19E3062AD7797985F16A0A18155 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B +AD = 303132333435363738393A3B3C3D3E3F4041 +Out = B3B05F0A08C5769749A3B56571982F096057F86BCE93935BE1DA5E2F + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B +AD = 303132333435363738393A3B3C3D3E3F4041424344 +Out = CC261D9F8FB8842E82E22B10A923ED56F9DCA3D85D6362A381BFE4E1 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B +AD = 303132333435363738393A3B3C3D3E3F4041424344454647 +Out = 9D29F9D52ADF9470AF4CBCE0A8FD904178509873F8B4852CD27DF443 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B +AD = 303132333435363738393A3B3C3D3E3F404142434445464748494A +Out = E4C1BB6B1B264F12EEC2ABB5391ED0DC1FFD4478DE6548C6C67504D2 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B +AD = 303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D +Out = 565968B0563DF95EA9124E5FF6D1D9F3418F33B2E1F058EFF57D4369 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E +AD = +Out = E8C3DEEE246CC5EAE3E872313897A283AECC1DA0834A52940EC4BFCDDB6404 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E +AD = 303132 +Out = 66D0D52BF401C6DC1C0AD4DACD1D946A05A976C212C3C1534838EC99A13FCE + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E +AD = 303132333435 +Out = 9310C6DD8E9CBC3E406C0EBFBEA31282DF8B4C4A7E3C3FE9B6339251CCB933 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E +AD = 303132333435363738 +Out = BF79D7476D6FB05B8891A079BE8A191CC15730F75A187CAF77A9504AF37DF7 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E +AD = 303132333435363738393A3B +Out = B83A1C62AD05C670D6E9220E0DD9BEB178731459A4013C05763C6A552E1122 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E +AD = 303132333435363738393A3B3C3D3E +Out = 20FD19DABC1A5CC449A621D34DAC60D7F316F7F9AEE44F263C8D7B7094C199 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E +AD = 303132333435363738393A3B3C3D3E3F4041 +Out = B3B05F0A08C5769749A3B5658BCB1D9C4C2970AA154B8547C7D0FD2AD455A0 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E +AD = 303132333435363738393A3B3C3D3E3F4041424344 +Out = CC261D9F8FB8842E82E22B106796CDE3BC50B6EE4FBDDD987CC83549A5C2E5 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E +AD = 303132333435363738393A3B3C3D3E3F4041424344454647 +Out = 9D29F9D52ADF9470AF4CBCE0A4481AC660005A81628FA166594398AB6CE999 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E +AD = 303132333435363738393A3B3C3D3E3F404142434445464748494A +Out = E4C1BB6B1B264F12EEC2ABB542761AF6D92AAC07A55F97F5FDDDE66B3DD76B + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E +AD = 303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D +Out = 565968B0563DF95EA9124E5F4506BC59125003191C2B341DFE83368ADE535D + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031 +AD = +Out = E8C3DEEE246CC5EAE3E872313897A2BB608922F7517D289D000B4418443947AD52DA + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031 +AD = 303132 +Out = 66D0D52BF401C6DC1C0AD4DACD1D94C4F3F17915AB4188784520AACC107FF4A82EC5 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031 +AD = 303132333435 +Out = 9310C6DD8E9CBC3E406C0EBFBEA312435F2C6975FAF3B6B2B17EF1EA2503C3D31EF5 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031 +AD = 303132333435363738 +Out = BF79D7476D6FB05B8891A079BE8A1992B63F5554125721C60DBE94011141B4EB18F9 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031 +AD = 303132333435363738393A3B +Out = B83A1C62AD05C670D6E9220E0DD9BE46E43D74914E4CB82465BE860A650404D48E7E + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031 +AD = 303132333435363738393A3B3C3D3E +Out = 20FD19DABC1A5CC449A621D34DAC6013EF43199A73799070D6D17D320761F4BA4BC0 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031 +AD = 303132333435363738393A3B3C3D3E3F4041 +Out = B3B05F0A08C5769749A3B5658BCB1D409C753E4537125E43A411D2BA637F443219AB + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031 +AD = 303132333435363738393A3B3C3D3E3F4041424344 +Out = CC261D9F8FB8842E82E22B106796CD708B23CB3AC39EE776EB899A3EBF51FE04E4D7 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031 +AD = 303132333435363738393A3B3C3D3E3F4041424344454647 +Out = 9D29F9D52ADF9470AF4CBCE0A4481AC7FCB16062C006200D33E84FA192734495D928 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031 +AD = 303132333435363738393A3B3C3D3E3F404142434445464748494A +Out = E4C1BB6B1B264F12EEC2ABB542761ABBC0D8672EB837FBFB85B363C146B07A5D25C6 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031 +AD = 303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D +Out = 565968B0563DF95EA9124E5F4506BC64F5778A3CACB2F8D24D0E02B32E74883CCE6B + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031323334 +AD = +Out = E8C3DEEE246CC5EAE3E872313897A2BB6089AA3E1514B0B35B5C13FB37BA8EA9F73A767092 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031323334 +AD = 303132 +Out = 66D0D52BF401C6DC1C0AD4DACD1D94C4F3F13056B07463B5634AA9F2AAE94C330BEA112864 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031323334 +AD = 303132333435 +Out = 9310C6DD8E9CBC3E406C0EBFBEA312435F2C894A6EAA62AB6C68CA7F8F8D5A00FF8BFC1165 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031323334 +AD = 303132333435363738 +Out = BF79D7476D6FB05B8891A079BE8A1992B63FFE310624B1ED7F9517E94247EF707EDB9D606D + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031323334 +AD = 303132333435363738393A3B +Out = B83A1C62AD05C670D6E9220E0DD9BE46E43D8D6C068332A5B3BCB9D4D6F999AA1B4F665EA9 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031323334 +AD = 303132333435363738393A3B3C3D3E +Out = 20FD19DABC1A5CC449A621D34DAC6013EF43F597DF5F4D6C3B7E98A651D7A629B75F3E0F71 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031323334 +AD = 303132333435363738393A3B3C3D3E3F4041 +Out = B3B05F0A08C5769749A3B5658BCB1D409C75AAD1E8F985F2396D90E24F3AC6A5FF1A367206 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031323334 +AD = 303132333435363738393A3B3C3D3E3F4041424344 +Out = CC261D9F8FB8842E82E22B106796CD708B23422BE0548806C7600E630EF01E5A17C3B00042 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031323334 +AD = 303132333435363738393A3B3C3D3E3F4041424344454647 +Out = 9D29F9D52ADF9470AF4CBCE0A4481AC7FCB1B32976FC741903A262E7172D45C50EFEEAD4B2 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031323334 +AD = 303132333435363738393A3B3C3D3E3F404142434445464748494A +Out = E4C1BB6B1B264F12EEC2ABB542761ABBC0D8286FB9875E7339F1DDF0B85B8FC7B242CFC562 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031323334 +AD = 303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D +Out = 565968B0563DF95EA9124E5F4506BC64F57779D678A440ED6972000320403705FF8526A36B + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031323334353637 +AD = +Out = E8C3DEEE246CC5EAE3E872313897A2BB6089AA3E15E80307F2D00B0754CB343B0EA742A196423647 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031323334353637 +AD = 303132 +Out = 66D0D52BF401C6DC1C0AD4DACD1D94C4F3F13056B09B17976FFF67A83597604FCB05C21132E35A2F + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031323334353637 +AD = 303132333435 +Out = 9310C6DD8E9CBC3E406C0EBFBEA312435F2C894A6E978A90211212693A848BB79A669E735342EC63 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031323334353637 +AD = 303132333435363738 +Out = BF79D7476D6FB05B8891A079BE8A1992B63FFE31062DC7AA1329BC31CEA3264A444B6C11CF607F88 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031323334353637 +AD = 303132333435363738393A3B +Out = B83A1C62AD05C670D6E9220E0DD9BE46E43D8D6C06DA586C7B0FD7D968000E17BD5B57BD7BC79994 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031323334353637 +AD = 303132333435363738393A3B3C3D3E +Out = 20FD19DABC1A5CC449A621D34DAC6013EF43F597DF00A4FC844B28F3AFDC5ACD07D31F44CF499CA9 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031323334353637 +AD = 303132333435363738393A3B3C3D3E3F4041 +Out = B3B05F0A08C5769749A3B5658BCB1D409C75AAD1E8564974259FFF441D58A09329BAA5CFD2EA5DD8 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031323334353637 +AD = 303132333435363738393A3B3C3D3E3F4041424344 +Out = CC261D9F8FB8842E82E22B106796CD708B23422BE018E5536F42038000D00E70522D029B26A1CCD2 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031323334353637 +AD = 303132333435363738393A3B3C3D3E3F4041424344454647 +Out = 9D29F9D52ADF9470AF4CBCE0A4481AC7FCB1B32976469892DFEBAF445205EC9B019D022C7042AE59 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031323334353637 +AD = 303132333435363738393A3B3C3D3E3F404142434445464748494A +Out = E4C1BB6B1B264F12EEC2ABB542761ABBC0D8286FB990CB04BD063B9C21575F6E9D934AFBEACA0262 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F3031323334353637 +AD = 303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D +Out = 565968B0563DF95EA9124E5F4506BC64F57779D678DB879EAA3BEC0FEE9B463CAE31BFB9754A7ED3 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F303132333435363738393A +AD = +Out = E8C3DEEE246CC5EAE3E872313897A2BB6089AA3E15E80307970F2D8FA96A7A8F18A2163D45CC8061DDBE9E + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F303132333435363738393A +AD = 303132 +Out = 66D0D52BF401C6DC1C0AD4DACD1D94C4F3F13056B09B1797A127EE5570130144C1F1B1B2CAEC959370BD24 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F303132333435363738393A +AD = 303132333435 +Out = 9310C6DD8E9CBC3E406C0EBFBEA312435F2C894A6E978A90FDB1BC77B268A5653068EBB83BF38B1F741C7D + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F303132333435363738393A +AD = 303132333435363738 +Out = BF79D7476D6FB05B8891A079BE8A1992B63FFE31062DC7AAE2545181866427935FC63F66D69208A0B04DC7 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F303132333435363738393A +AD = 303132333435363738393A3B +Out = B83A1C62AD05C670D6E9220E0DD9BE46E43D8D6C06DA586CEED818A19D0A8967051D18CCEFC1986751B15D + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F303132333435363738393A +AD = 303132333435363738393A3B3C3D3E +Out = 20FD19DABC1A5CC449A621D34DAC6013EF43F597DF00A4FCC9570FABEE4CF13C4565A781565F3F69C4DA29 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F303132333435363738393A +AD = 303132333435363738393A3B3C3D3E3F4041 +Out = B3B05F0A08C5769749A3B5658BCB1D409C75AAD1E8564974C66CFC36BCCE20293FA20F6C8D742C8D05E286 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F303132333435363738393A +AD = 303132333435363738393A3B3C3D3E3F4041424344 +Out = CC261D9F8FB8842E82E22B106796CD708B23422BE018E5533C5EB9FB31BBFF94C3EA7282FDB640674055E4 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F303132333435363738393A +AD = 303132333435363738393A3B3C3D3E3F4041424344454647 +Out = 9D29F9D52ADF9470AF4CBCE0A4481AC7FCB1B32976469892D5BC91FD3CE3F4A8BC3659AC09DE2E027D885D + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F303132333435363738393A +AD = 303132333435363738393A3B3C3D3E3F404142434445464748494A +Out = E4C1BB6B1B264F12EEC2ABB542761ABBC0D8286FB990CB040DC107ADD34CB2B4CEFB49B2EAAE625701A6DF + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F303132333435363738393A +AD = 303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D +Out = 565968B0563DF95EA9124E5F4506BC64F57779D678DB879EFF69D9B45E5A34F710BD01DB9990064094F410 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D +AD = +Out = E8C3DEEE246CC5EAE3E872313897A2BB6089AA3E15E80307970F2D1F0066FA1EA2B92A5DD1B9D1C9DEB4CBEAABA0 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D +AD = 303132 +Out = 66D0D52BF401C6DC1C0AD4DACD1D94C4F3F13056B09B1797A127EE7EBD04EFA1AB5E9FDE810D007E11185A97C772 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D +AD = 303132333435 +Out = 9310C6DD8E9CBC3E406C0EBFBEA312435F2C894A6E978A90FDB1BCC6802001162EFFADEE650799A92E7F34AF5AFB + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D +AD = 303132333435363738 +Out = BF79D7476D6FB05B8891A079BE8A1992B63FFE31062DC7AAE254516F0860A895946CDD7001544DBC0F430A3DF552 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D +AD = 303132333435363738393A3B +Out = B83A1C62AD05C670D6E9220E0DD9BE46E43D8D6C06DA586CEED818F6797D401A603567B721DE38E57CC26014E881 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D +AD = 303132333435363738393A3B3C3D3E +Out = 20FD19DABC1A5CC449A621D34DAC6013EF43F597DF00A4FCC9570F4C64C4A21BF61016B8E605B58C2C9C065E7BBE + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D +AD = 303132333435363738393A3B3C3D3E3F4041 +Out = B3B05F0A08C5769749A3B5658BCB1D409C75AAD1E8564974C66CFCE4D9FBAED202A3ED692B4C5AEF6AA172E36594 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D +AD = 303132333435363738393A3B3C3D3E3F4041424344 +Out = CC261D9F8FB8842E82E22B106796CD708B23422BE018E5533C5EB98E2EB7B9C1D5C0A3F3F255397B2A02D32DA03A + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D +AD = 303132333435363738393A3B3C3D3E3F4041424344454647 +Out = 9D29F9D52ADF9470AF4CBCE0A4481AC7FCB1B32976469892D5BC918CAC7997E800B5C7A91CC043BD8E175AD080BA + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D +AD = 303132333435363738393A3B3C3D3E3F404142434445464748494A +Out = E4C1BB6B1B264F12EEC2ABB542761ABBC0D8286FB990CB040DC107595C16F966098856B32B97C868331F6CEF1B27 + +Key = 000102030405060708090A0B0C0D0E0F +Nonce = 101112131415161718191A1B1C1D1E1F +In = 202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D +AD = 303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D +Out = 565968B0563DF95EA9124E5F4506BC64F57779D678DB879EFF69D9D73F1848FC8179F2A09D4E82DA925CFC92FC67 diff -Nru botan3-3.7.1+dfsg/src/tests/data/aead/chacha20poly1305.vec botan3-3.12.0+dfsg/src/tests/data/aead/chacha20poly1305.vec --- botan3-3.7.1+dfsg/src/tests/data/aead/chacha20poly1305.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/aead/chacha20poly1305.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,3 +1,6 @@ + +#test cpuid avx512 avx2 + [ChaCha20Poly1305] # From draft-agl-tls-chacha20poly1305-04 @@ -9461,3 +9464,948 @@ Nonce = 000102030405060708090a0b0c0d0e0f1011121314151617 In = f82bb3540d3e2478acf2b085eb3c08184ae68d5b6b7bfd21180a14392ed613ae63d1c8914e7f998c6aee6d244540 Out = 45fc5106fd0f45d98ef099e07330cbfb997d9d000f58271e033b2170b16a12c25d56587802bd26dd5da4cc65df3dca6941eaa39982b29f98edc37f7d8e58 + +# Test case 122 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = 00000000000000000000000000000000 +In = 65b63bf074b7283992e24b1ac0df0d22b555dbe2254d94a43f1de748d3cc6f0d +Out = 000000000000000000000000000000000000000000000000000000000000000039f4fce3026d83789ffd1ee6f2cd7c4f + +# Test case 123 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = 00000000000000000000000000000000 +In = 65b63bf074b7283992e24b1ac0df0d22b555dbe2254d94a43f1de748d3cc6f0d20c142fe898fbbe668d4324394434c1b18b58ead710aed9c31db1f2a8a1f1bb2 +Out = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f5eaa804605c3a4785f9d7f13b6f67d6 + +# Test case 124 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = 00000000000000000000000000000000 +In = 65b63bf074b7283992e24b1ac0df0d22b555dbe2254d94a43f1de748d3cc6f0d20c142fe898fbbe668d4324394434c1b18b58ead710aed9c31db1f2a8a1f1bb24405c183af94ee1ad630cd931158a6213d48c8fff10d0a1f9ef760188e658802aad55e41a1d99069a18db55c56af7c10a6f21ecc8af9b7ce0a7ea0b67426e925 +Out = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009b5c43a78d954e8a3c659eebc13d5d55 + +# Test case 125 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = ffffffffffffffffffffffffffffffff +In = 9a49c40f8b48d7c66d1db4e53f20f2dd4aaa241ddab26b5bc0e218b72c3390f2 +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff37e3399d9ca696799f08f4f72bc0cdd8 + +# Test case 126 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = ffffffffffffffffffffffffffffffff +In = 9a49c40f8b48d7c66d1db4e53f20f2dd4aaa241ddab26b5bc0e218b72c3390f2df3ebd0176704419972bcdbc6bbcb3e4e74a71528ef51263ce24e0d575e0e44d +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3d52710bec86d4ea9fea2ff269549191 + +# Test case 127 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = ffffffffffffffffffffffffffffffff +In = 9a49c40f8b48d7c66d1db4e53f20f2dd4aaa241ddab26b5bc0e218b72c3390f2df3ebd0176704419972bcdbc6bbcb3e4e74a71528ef51263ce24e0d575e0e44dbbfa3e7c506b11e529cf326ceea759dec2b737000ef2f5e061089fe7719a77fd552aa1be5e266f965e724aa3a95083ef590de13375064831f5815f498bd916da +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff51356329e280b12d55d3d98f0a580cbe + +# Test case 128 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = 00000080000000800000008000000080 +In = 65b63b7074b728b992e24b9ac0df0da2b555db62254d94243f1de7c8d3cc6f8d +Out = 0000008000000080000000800000008000000080000000800000008000000080c152a4b90c548c71dc479edeaf9211bf + +# Test case 129 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = 00000080000000800000008000000080 +In = 65b63b7074b728b992e24b9ac0df0da2b555db62254d94243f1de7c8d3cc6f8d20c1427e898fbb6668d432c394434c9b18b58e2d710aed1c31db1faa8a1f1b32 +Out = 0000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008040ef6383052d91c2e4b4611b0e32c5ff + +# Test case 130 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = 00000080000000800000008000000080 +In = 65b63b7074b728b992e24b9ac0df0da2b555db62254d94243f1de7c8d3cc6f8d20c1427e898fbb6668d432c394434c9b18b58e2d710aed1c31db1faa8a1f1b324405c103af94ee9ad630cd131158a6a13d48c87ff10d0a9f9ef760988e658882aad55ec1a1d990e9a18db5dc56af7c90a6f21e4c8af9b74e0a7ea0367426e9a5 +Out = 0000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080ae9b542541e84fc74542eed6be638fee + +# Test case 131 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = 80000000800000008000000080000000 +In = e5b63bf0f4b7283912e24b1a40df0d223555dbe2a54d94a4bf1de74853cc6f0d +Out = 800000008000000080000000800000008000000080000000800000008000000010fee3ecfba9cdf797bae37a626ec83b + +# Test case 132 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = 80000000800000008000000080000000 +In = e5b63bf0f4b7283912e24b1a40df0d223555dbe2a54d94a4bf1de74853cc6f0da0c142fe098fbbe6e8d4324314434c1b98b58eadf10aed9cb1db1f2a0a1f1bb2 +Out = 800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000007490795bdbbbf5d0aecb9a4f65aa379f + +# Test case 133 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = 80000000800000008000000080000000 +In = e5b63bf0f4b7283912e24b1a40df0d223555dbe2a54d94a4bf1de74853cc6f0da0c142fe098fbbe6e8d4324314434c1b98b58eadf10aed9cb1db1f2a0a1f1bb2c405c1832f94ee1a5630cd939158a621bd48c8ff710d0a1f1ef760180e6588022ad55e4121d99069218db55cd6af7c1026f21ecc0af9b7ce8a7ea0b6f426e925 +Out = 80000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000001d1096a8ca9e2bda2762c41d5b16f62f + +# Test case 134 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = ffffff7fffffff7fffffff7fffffff7f +In = 9a49c48f8b48d7466d1db4653f20f25d4aaa249ddab26bdbc0e218372c339072 +Out = ffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7faf8492c792bf8d8062be74ff6efb3869 + +# Test case 135 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = ffffff7fffffff7fffffff7fffffff7f +In = 9a49c48f8b48d7466d1db4653f20f25d4aaa249ddab26bdbc0e218372c339072df3ebd8176704499972bcd3c6bbcb364e74a71d28ef512e3ce24e05575e0e4cd +Out = ffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7ff24db68c46b67d6f402fa6c897913368 + +# Test case 136 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = ffffff7fffffff7fffffff7fffffff7f +In = 9a49c48f8b48d7466d1db4653f20f25d4aaa249ddab26bdbc0e218372c339072df3ebd8176704499972bcd3c6bbcb364e74a71d28ef512e3ce24e05575e0e4cdbbfa3efc506b116529cf32eceea7595ec2b737800ef2f56061089f67719a777d552aa13e5e266f165e724a23a950836f590de1b3750648b1f5815fc98bd9165a +Out = ffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7f43f651ab2e2eb0f04bf689a40d32da24 + +# Test case 137 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = 7fffffff7fffffff7fffffff7fffffff +In = 1a49c40f0b48d7c6ed1db4e5bf20f2ddcaaa241d5ab26b5b40e218b7ac3390f2 +Out = 7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff60d95294a3694cfaa64b2f63bc1f82ec + +# Test case 138 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = 7fffffff7fffffff7fffffff7fffffff +In = 1a49c40f0b48d7c6ed1db4e5bf20f2ddcaaa241d5ab26b5b40e218b7ac3390f25f3ebd01f6704419172bcdbcebbcb3e4674a71520ef512634e24e0d5f5e0e44d +Out = 7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffffbeaca0b47027196176186d944019c1c8 + +# Test case 139 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = 7fffffff7fffffff7fffffff7fffffff +In = 1a49c40f0b48d7c6ed1db4e5bf20f2ddcaaa241d5ab26b5b40e218b7ac3390f25f3ebd01f6704419172bcdbcebbcb3e4674a71520ef512634e24e0d5f5e0e44d3bfa3e7cd06b11e5a9cf326c6ea759de42b737008ef2f5e0e1089fe7f19a77fdd52aa1bede266f96de724aa3295083efd90de133f506483175815f490bd916da +Out = 7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffffd4811028a577d4dd69d6b35d717f73e3 + +# Test case 140 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = 00000000ffffffff00000000ffffffff +In = 65b63bf08b48d7c692e24b1a3f20f2ddb555dbe2dab26b5b3f1de7482c3390f2 +Out = 00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff10fb61272b555bee104f5a71818716d6 + +# Test case 141 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = 00000000ffffffff00000000ffffffff +In = 65b63bf08b48d7c692e24b1a3f20f2ddb555dbe2dab26b5b3f1de7482c3390f220c142fe7670441968d432436bbcb3e418b58ead8ef5126331db1f2a75e0e44d +Out = 00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff4756764e59583504182877d8c33120f0 + +# Test case 142 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = 00000000ffffffff00000000ffffffff +In = 65b63bf08b48d7c692e24b1a3f20f2ddb555dbe2dab26b5b3f1de7482c3390f220c142fe7670441968d432436bbcb3e418b58ead8ef5126331db1f2a75e0e44d4405c183506b11e5d630cd93eea759de3d48c8ff0ef2f5e09ef76018719a77fdaad55e415e266f96a18db55ca95083efa6f21ecc750648310a7ea0b68bd916da +Out = 00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff95a2b12a4a280089d4bd4f904253e754 + +# Test case 143 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = ffffffff00000000ffffffff00000000 +In = 9a49c40f74b728396d1db4e5c0df0d224aaa241d254d94a4c0e218b7d3cc6f0d +Out = ffffffff00000000ffffffff00000000ffffffff00000000ffffffff0000000060dcd45974bebe032eb7b86c9d063452 + +# Test case 144 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = ffffffff00000000ffffffff00000000 +In = 9a49c40f74b728396d1db4e5c0df0d224aaa241d254d94a4c0e218b7d3cc6f0ddf3ebd01898fbbe6972bcdbc94434c1be74a7152710aed9cce24e0d58a1f1bb2 +Out = ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000f0e6a3c1f28ad92d0dbc900be291d877 + +# Test case 145 from Wycheproof, Poly1305 edge case +Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f +Nonce = 000102030405060708090a0b +AD = ffffffff00000000ffffffff00000000 +In = 9a49c40f74b728396d1db4e5c0df0d224aaa241d254d94a4c0e218b7d3cc6f0ddf3ebd01898fbbe6972bcdbc94434c1be74a7152710aed9cce24e0d58a1f1bb2bbfa3e7caf94ee1a29cf326c1158a621c2b73700f10d0a1f61089fe78e658802552aa1bea1d990695e724aa356af7c10590de1338af9b7cef5815f497426e925 +Out = ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff0000000057eff4a525eeff2ebd7a28eb894282be + +# Test case 206 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +In = dc8ce708bf26aab862d97e1b42f31ef38c382cf07174142ea564920612997b1c2e38aca2438b588d5459493e97e7fa330ff9bc3b9458297ba0967d86ed090b435103478f2869b93ee29c837e95fb6b9903f3b735b7345428eb93b3db1d9b5187cebb889aa177d83e4f63fc9a5c0596eed939883d06aacdfdea44fdecdf5cb7fc +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc296436246c3a7c4b3ba09ab2a6a0889 + +# Test case 207 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 0001020304050607051e9373 +AD = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +In = 931227274a89d0b3aade7fac62c96262c1e77b8dafd248f10ad37c6ccb69cb7131b041593c8bb8c3db38f39dd8a124c424fce4389dede1d3cb9d46cf95970aea9856b6e313d756197baf4fcb58df275bca8a2188f9e8a1ad04354ede542ddc30e8b735b2f5905f5811799282be94ae842ec126c55d2e667235e9acf1d48798f0 +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff99a3b0fff6fdcbcce9dc5820f2a64861 + +# Test case 208 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 0001020304050607048c3c5f +AD = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +In = 0df91f31230e8941e700a752fef08c897c511ed618fdf8a378a1f439013b40a48d4634c27d9ada7c0bb6f3fa92e341425903d7ecd0c49bee4c77e84b11f1c721922308642885b813fae364da32eaf120d6a43a74fb1632443667bfea6eef1be73eb1c3c0b5a57cee8dc4feed4a1fb9ae02f7b1695588c3c878451cb6ee0cb3dc +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeaff8f47ef9268fd0d94e8a9c4b78d24 + +# Test case 209 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +In = 1fde9b9ec8b247d42bbee2016d6715ba428a85431430eada56a2c5dc944b6aa6cef0b056a2eecc51d30838e640615e1458e0943e30f91ba41b4362fa9ed6037b21d14da7b4f76f9f68fa8903138d563ce2590af1201c7cfec2290cfce98a822ebb8d1ed9dc4e20d241755aff91cdfd10fdb69efa0d5c8082692601cbfbb955c7 +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86ed21fda080a7d13981078d86b3e3cd + +# Test case 210 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +In = 66115e67ecd3d4178c4c60e713ab4e5e66f8d1f971da17437a2b5e04fbca1671e847139a5f4e3f8e92d7a3b71eb4ff0e50354c0c1580af3662d5f8151e3f7e8264a0085c32ddfcbeb01a8be4c34d53319800ac4ef9d4e4014524bc7cd3387242e774f4d1a7a0521e42ec44844d0bd8b9d73fec959212fd7e8eacf4d984996d9b +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff34f9e0faa515eee0e784e6ef2678befa + +# Test case 211 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060726c6961b +AD = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +In = e97244259af5a379238da0cad2a5f493655ec0e5024fd553bbb3deb66a94036d106c3d513407b2dd1cc5936c4c9c1e4f4b37b54dec261c601dc99e90680e23e2dc5c9a8d503d8bea49a8cdca3706bfd2a3daa0afb19a70fd3d355fc37c13f3f9e5c8d0864a5f80a780b36d4698ec2ce9ccc27b97ecbe672e41628ebd773acb81 +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3c94b9fe60bdb35c6b7b73b765083492 + +# Test case 212 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 0001020304050607013da060 +AD = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +In = 9453aa159c3d87f17e21e88adabc37e553b904d00eefc66b8e0905e23576fbdc9c7bea9777f3b8368481932534b3344d309e6307cddfe7b3549300dd9cda7efe9d43c8a115912a392904079ee92bcd33099f7022ea94c1e7353b89bfc54de3ceb56f529a1a608bb5a970e1359609d1f56806b37f8605f4c27451da6066fc557a +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2b11cf9f8db8490d409fc62afd7379f3 + +# Test case 213 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060707db33de +AD = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +In = 2e1836640d810c2709fb83ccf1aef3a971085d1bbfb58a425abf75ccec70b3abde0e80539e83a82546e7372a19481547053308dd7842675e9c4f61302426da0d71c1da3102031030ed928152be009b15b52f71b5911991d39f68a8658d99729df2bbef31c8989f9604558df9f2aba4b3766c58aaef3548de545ec1f080225a88 +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc9c8366920f88381407712cec61e6607 + +# Test case 214 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060702a11942 +AD = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +In = 0ecb4d85c956b5268c9b35a8c63b4e9d3e5cb72b64ef98773841b947bd7d59ef7d0eb0e1c050d49a5424ce7deb527d76087e4746674c958965df32d9e5fb03b46501706128d481217aaeae2f78f9259273358a2954cac0bc2fbfe77447d1d387b9314c6541b69f1270b3438b1042b2b4663e62ba4d49c07ac6f163034afa80af +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2373cfa2ab24446ad5a236167b8027fe + +# Test case 215 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506073c0df637 +AD = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +In = 2e8e45e903bfab32f2f0d49d9a3e449bef6f4093e2722cdab2cf935c1822b830fb5a4056516d560dfc8638c9a57d2927200a56f0b67153271d498e8f08dc888c61ef634f7ae40f4608f96f92fea5a1e5bd45131120098dc5de0378e58f2ddb46fa4aa5adb38fe006bb19b69146382f77a79e06214def547cfb5ce37a7008b9b6 +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5f93946478d8081e7247f414ad39a515 + +# Test case 216 from Wycheproof, Poly1305 edge case +Key = 9de836aa579585081f330a7c4036e20e38ef15eff3945184d231867f505fffdf +Nonce = 00000000101112130bc672c3 +AD = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +In = 3619cb470af86dceceb6940f2d9abb34c9a9131476053387445ffebbe240d4f9818377855652f46a8219c7f71c3554f8acef8258de4b7d17c0f3d353ac981cc6a13287be1e6b41dc6d133df4ababebdf43d665ce7a4a5c982a0b139cb8202eebc74173e3224a440e4c37d2b595f384290e939ba016df0d49b36cdb4bd91c39 +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff133fe62391744d11ce44594b96c53baf + +# Test case 217 from Wycheproof, Poly1305 edge case +Key = 9de836aa579585081f330a7c4036e20e38ef15eff3945184d231867f505fffdf +Nonce = 000000001011121303e9b9a4 +AD = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +In = af205bda819f7451be0f28667d4b01b59ff2daa8173cab52046c3c9e0d989889c5e021ef7afd06e9ce6cc30e3a6ebab509134ba10d10e570c55587c13eee53e73be54804c8539ffbf23b35922b1ca37b9e9bc24ee204837ca5a294ce05d12600c7eff6aee32270db2feff47dc5a04176169e15850628e6035f78994f9f5603 +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe3451adb9d23a7710a1aafba26f56387 + +# Test case 218 from Wycheproof, Poly1305 edge case +Key = 9de836aa579585081f330a7c4036e20e38ef15eff3945184d231867f505fffdf +Nonce = 00000000101112130700b982 +AD = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +In = 68c67272036fb652a0182eeb4781358e4704a4a702fd731bf3b3ea994717989e7d9104e0ae81732a8c7e9a82b3d31d541761a366b67c3396f1a6c67e293ddb65a59e42541dda144dc6c78388cfca982e23350958ac5b3d54a1722fd64733577862e1879c9e9445ebdec5315d1706db7ebbedd4c779935e72057e5b0ecde081 +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0bb8a55ff5f52a5043c6e7795847557 + +# Test case 219 from Wycheproof, Poly1305 edge case +Key = 9de836aa579585081f330a7c4036e20e38ef15eff3945184d231867f505fffdf +Nonce = 0000000010111213019836bb +AD = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +In = c483b7334ebe2e879b0c3f9db4fcd9f5219062360d6ce44cdae0f94e04c8345ea7e3ae33855118741dcafe0de4ae98c4e43af7b12b04ee8ab175625823ac040e5abac4403f1d45238adcb8c0cf44bd56917f9f5d93974c82b56951986a9c0450bd9047b5a616e814526ad0580e3ecd8189c9fef2cdb979a22ad3a01930fbd1 +Out = fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4fc25f4c5543a9afee9819e2904fb68 + +# Test case 220 from Wycheproof, Poly1305 edge case +Key = 9de836aa579585081f330a7c4036e20e38ef15eff3945184d231867f505fffdf +Nonce = 00000000101112131d59f288 +AD = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +In = bc7f4f15fd1e4c1399740836670abe39a05707be19956ce169b32321759e0f213ae19ad34aa612b3a29f02c4bbac9f785a55a3adfe419ab891bbe0acee9921322ea21002c9dd3dcdd13a7f8554dddc10f9b529ce94be7050937dab76557b7eb17c685aad8f0797e39d62553988989aab1d9764fe431cc1d4c595062ce93ce9 +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5e67a7b8733e0e4b01ac2178a205ae7e + +# Test case 221 from Wycheproof, Poly1305 edge case +Key = 9de836aa579585081f330a7c4036e20e38ef15eff3945184d231867f505fffdf +Nonce = 00000000101112130552a411 +AD = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +In = eaccaa778935ef249e0900149dd889462d2a061486ba102b8caebe465f3959fb3119ebb5689676ffdd6d851a26739e772b54a2f5f473ea9c7e58ccbc4cfc953e8c420b2175d9dd519265630bb79bd87a601b113231a8b16ce54c331347ec04c2b1c9160f38207aa46e96feb06dee883eb422fa14908df300bb1a1ef758c408 +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff177a77fce114a4349c4f8d5ec825d06f + +# Test case 222 from Wycheproof, Poly1305 edge case +Key = 9de836aa579585081f330a7c4036e20e38ef15eff3945184d231867f505fffdf +Nonce = 00000000101112130c807a72 +AD = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +In = a76c330e015060a17e64cb7b6d753f201f75be8759fd7539fb92b22aef54c9d3029dba0c15cbf7c95135888319c6b2e6276da21e0c351fd522b29aabb5883a3291d6f427de773b124390ef6fd96621ffbc42dfbf7a34da272cbc9ccb1a498d078033d1ac3bf7e92715948b06d69d5c5039e9164ba9c3a02219ec5908206b3b +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff623c7d4424f5497aedfd1339cf8cecce + +# Test case 223 from Wycheproof, Poly1305 edge case +Key = 9de836aa579585081f330a7c4036e20e38ef15eff3945184d231867f505fffdf +Nonce = 00000000101112130397a143 +AD = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +In = 228a7e15bcce13051de9145f77f7f4ff7921828b4f99efc4ff55ee0d9344955b69ec2d4798b0517f0273c4456ae5ffc5929cbe74ddb0da51d4f2b4df7578a31240c88ae922c3c5eca7b97d72d497062050a587447c562b343d5c71921944872f9fd06b8f34b3eb5d4341f5ff8a907dd7c2e1676b81252726ba54814da51eab +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1c18b69354b189731a1a83fe8f0d57c9 + +# Test case 224 from Wycheproof, Poly1305 edge case +Key = 9de836aa579585081f330a7c4036e20e38ef15eff3945184d231867f505fffdf +Nonce = 000000001011121308cb0f3f +AD = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +In = c7d843188ab193dfef5c4daf583f952cd4b195f240fa2e704d021723023c123371a41e87dfc6e6c3874a42f331cf035988a38c72ba2da854b1208f98bf8cc29948169481ab3a402d5fcc7ff78f9e31925576dc3938074b8c5b27960e3afc750ad686563688b7441787288d5256c1301d563b7744843bd1ab4eff5be6f1653d +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2045815b8211b9a2995effe0b8ed9868 + +# Test case 225 from Wycheproof, Poly1305 edge case +Key = 9de836aa579585081f330a7c4036e20e38ef15eff3945184d231867f505fffdf +Nonce = 00000000101112130d8fcf4e +AD = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +In = cfc3db8631c81c69023a3c8a9ad66c35053685144c4fa2a9510add72e211dad9ca5b982e4c194591fdb74116280311d1299ad81227258cb52f079bbcb12aff161d278dec33a326d71276b3de01a8327ee7f45f94179dff18a3fe643e56c30cfd03871c8110ab00f6612b9e17a4647360d7847bb63a3122613c2e7cdddd08ae +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1ae2ed84ea9774d78d782bf8d972a8b8 + +# Test case 226 from Wycheproof, Poly1305 edge case +Key = 404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f +Nonce = 000102030405060708090a0b +AD = ffffffffffffffffffffffffffffffff415771fda4fbcc55c377f73203e60226 +In = e48caf8a76183327c9561a4651c07c822ccd1642c06607d0d4bc0afb4de15915dbfa3b0b422e77e15c64bf6247031f15fdb643117809821870000adf83834da5 +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000102030405060708090a0b0c0d0e0f + +# Test case 227 from Wycheproof, Poly1305 edge case +Key = 404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f +Nonce = 000102030405060708090a0b +AD = f1ffffffffffffffffffffffffffffff615af39eddb5fcd2519190d5507d3b06 +In = e48caf8a76183327c9561a4651c07c822ccd1642c06607d0d4bc0afb4de15915dbfa3b0b422e77e15c64bf6247031f15fdb643117809821870000adf83834da5 +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000 + +# Test case 228 from Wycheproof, Poly1305 edge case +Key = 404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f +Nonce = 000102030405060708090a0b +AD = b5ffffffffffffffffffffffffffffff764e5d82ce7da0d44148484fd96a6107 +In = e48caf8a76183327c9561a4651c07c822ccd1642c06607d0d4bc0afb4de15915dbfa3b0b422e77e15c64bf6247031f15fdb643117809821870000adf83834da5 +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + +# Test case 229 from Wycheproof, Poly1305 edge case +Key = 404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f +Nonce = 000102030405060708090a0b +AD = fdffffffffffffffffffffffffffffff2bdbf16d8ea4d39dab8dcb3d4bc4e104 +In = e48caf8a76183327c9561a4651c07c822ccd1642c06607d0d4bc0afb4de15915dbfa3b0b422e77e15c64bf6247031f15fdb643117809821870000adf83834da5 +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000080000000800000008000000080 + +# Test case 230 from Wycheproof, Poly1305 edge case +Key = 404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f +Nonce = 000102030405060708090a0b +AD = a9ffffffffffffffffffffffffffffffaccd5eb31d8fc909e84b0de7de23bb08 +In = e48caf8a76183327c9561a4651c07c822ccd1642c06607d0d4bc0afb4de15915dbfa3b0b422e77e15c64bf6247031f15fdb643117809821870000adf83834da5 +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffff7fffffff7fffffff7f + +# Test case 231 from Wycheproof, Poly1305 edge case +Key = 404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f +Nonce = 000102030405060708090a0b +AD = d2ffffffffffffffffffffffffffffffdd4b933e7b1a7ed93cc7c050db71dc03 +In = e48caf8a76183327c9561a4651c07c822ccd1642c06607d0d4bc0afb4de15915dbfa3b0b422e77e15c64bf6247031f15fdb643117809821870000adf83834da5 +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01000000010000000100000001000000 + +# Test case 232 from Wycheproof, Poly1305 edge case +Key = 404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f +Nonce = 000102030405060708090a0b +AD = ffffffffffffffffffffffffffffffffa08164425d7642e9e90fc8d5c32d2cf6 +In = e48caf8a76183327c9561a4651c07c822ccd1642c06607d0d4bc0afb4de15915dbfa3b0b422e77e15c64bf6247031f15fdb643117809821870000adf83834da5 +Out = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000 + +# Test case 233 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = c68ce708bf26aab862d97e1b42f31ef37bb66f8090c149e452ec7f20327eb2ea2e38aca2438b588d5459493e97e7fa330ff9bc23c897df6b00af86931d6c81555103478f2869b93ee29c837e95fb6b9903f3b72debfba2384baa48ceedfedb91 +Out = e5ffffffffffffffffffffffffffffff0871bc8f1e4aa235087712d9df183609ffffffffffffffffffffffffffffffffffffffe7a33009ef5fc604ea0f9a75e9ffffffffffffffffffffffffffffffffffffffe7a33009ef5fc604ea0f9a75e93572162777262c518eef573b720e8e64 + +# Test case 234 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 40115e67ecd3d4178c4c60e713ab4e5e390ef93aeb61aa307f141323c38e0685fa47139a5f4e3f8e92d7a3b71eb4ff0e259445f4ffc31bce540190edd6ad207876a0085c32ddfcbeb01a8be4c34d5331eda1a5b6139750f973f0d4841baa2cb8 +Out = d9ffffffffffffffffffffffffffffffa009d73c6544428cfac0b2d8c7bbef0bedffffffffffffffffffffffffffffff8a5ef60715bc4b07c92b9707376da105edffffffffffffffffffffffffffffff8a5ef60715bc4b07c92b9707376da10519532d9fa0b5fbd582aaeda830602f1d + +# Test case 235 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 19de9b9ec8b247d42bbee2016d6715babc286fd979807951b183a188930ad15edcf0b056a2eecc51d30838e640615e14890e659fd3028c904e65018fdfd6038333d14da7b4f76f9f68fa8903138d563c33b7fb50c3e7ebca970f6f89a88a82d6 +Out = f9ffffffffffffffffffffffffffffff015d1565924f6c7418de9babf8be4407edffffffffffffffffffffffffffffff2e110e5e1c0468cbaad99c8abeffff07edffffffffffffffffffffffffffffff2e110e5e1c0468cbaad99c8abeffff0747e5d4294239db73b836c04070ff5b2d + +# Test case 236 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = c78ce708bf26aab862d97e1b42f31ef376209eef141691fba5d10eaf581affe62e38aca2438b588d5459493e97e7fa330e73d2dc3bbd954989cb8433b7d6597b5103478f2869b93ee29c837e95fb6b990279d9d218d1e81ac2ce4a6e474403bf +Out = e4ffffffffffffffffffffffffffffff05e74de09a9d7a2aff4a6356b57c7b05fffffffffffffffffffffffffffffffffe759118501a43cdd6a2064aa520adc7fffffffffffffffffffffffffffffffffe759118501a43cdd6a2064aa520adc7347216375f5b7b5c4e6bff4912fd9473 + +# Test case 237 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 49115e67ecd3d4178c4c60e713ab4e5ee02b87aeae8c3da8895f8cb0f6b9cc80f447139a5f4e3f8e92d7a3b71eb4ff0ecc4b7b803a5f8f4647df169080fe567a78a0085c32ddfcbeb01a8be4c34d5331047e9bc2d60bc471602e52f94df95aba +Out = d0ffffffffffffffffffffffffffffff792ca9a820a9d5140c8b2d4bf28c250ee3ffffffffffffffffffffffffffffff6381c873d020df8fdaf5117a613ed707e3ffffffffffffffffffffffffffffff6381c873d020df8fdaf5117a613ed707adbd2cafc8c8f0e51250e7b81c9d0a2d + +# Test case 238 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 1fde9b9ec8b247d42bbee2016d6715ba839f811ad0310c77052f45320b0d9560c4f0b056a2eecc51d30838e640615e1470d6b14fd209fedf261fd1d250d3478d2bd14da7b4f76f9f68fa8903138d563cca6f2f80c2ec9985ff75bfd4278fc6d8 +Out = ffffffffffffffffffffffffffffffff3eeafba63bfe1952ac727f1160b90039f5ffffffffffffffffffffffffffffffd7c9da8e1d0f1a84c2a34cd731fabb09f5ffffffffffffffffffffffffffffffd7c9da8e1d0f1a84c2a34cd731fabb09232c882f7a1a2f808ccf26496cff5b3d + +# Test case 239 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = fc8ce708bf26aab862d97e1b42f31ef38b79403dfaabc0d8c18d23a3469c13e62e38aca2438b588d5459493e97e7fa330a4b941e6b66fcc2ed7d8cb3e8cc7ffc5103478f2869b93ee29c837e95fb6b9906419f10480a8191a67842ee185e2538 +Out = dffffffffffffffffffffffffffffffff8be933274202b099b164e5aabfa9705fffffffffffffffffffffffffffffffffa4dd7da00c12a46b2140ecafa3a8b40fffffffffffffffffffffffffffffffffa4dd7da00c12a46b2140ecafa3a8b4030721677ff2eb8894e5a9d8492b7b0af + +# Test case 240 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = fa8ce708bf26aab862d97e1b42f31ef39bcbb8da477d580d772de4229bba7de22938aca2438b588d5459493e97e7fa331e9dedf9dd64a0681bac2969549425bc5603478f2869b93ee29c837e95fb6b991297e6f7fe08dd3b50a9e734a4067f78 +Out = d9ffffffffffffffffffffffffffffffe80c6bd5c9f6b3dc2db689db76dcf901f8ffffffffffffffffffffffffffffffee9bae3db6c376ec44c5ab104662d100f8ffffffffffffffffffffffffffffffee9bae3db6c376ec44c5ab104662d1002b7216c7873744c20ec5e2cdb260d3fa + +# Test case 241 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 66115e67ecd3d4178c4c60e713ab4e5e891b797521ba925b24090aaf6c4482bae847139a5f4e3f8e92d7a3b71eb4ff0e6d50c32d05a946cb8cea57c9f1442cb164a0085c32ddfcbeb01a8be4c34d5331a565236fe9fd0dfcab1b13a03c432071 +Out = ffffffffffffffffffffffffffffffff101c5773af9f7ae7a1ddab5468716b34ffffffffffffffffffffffffffffffffc29a70deefd6160211c050231084adccffffffffffffffffffffffffffffffffc29a70deefd6160211c050231084adcce17c273f31758e752322ae4869c1bfbb + +# Test case 242 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = ee8ce708bf26aab862d97e1b42f31ef3b9f55bd56e0fd74b46063a96354cfbee3238aca2438b588d5459493e97e7fa3320c78886a6f6292d6cc5fbddb546a2b04d03478f2869b93ee29c837e95fb6b992ccd8388859a547e27c0358045d4f874 +Out = cdffffffffffffffffffffffffffffffca3288dae0843c9a1c9d576fd82a7f0de3ffffffffffffffffffffffffffffffd0c1cb42cd51ffa933ac79a4a7b0560ce3ffffffffffffffffffffffffffffffd0c1cb42cd51ffa933ac79a4a7b0560c22721657b0130d28cf1ec65153c41182 + +# Test case 243 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = ef8ce708bf26aab862d97e1b42f31ef3b46fca24d353ff5e49eac51540e840ea3038aca2438b588d5459493e97e7fa333d311e572202011a75e948586fe268b44f03478f2869b93ee29c837e95fb6b99313b1559016e7c493eec86059f703270 +Out = ccffffffffffffffffffffffffffffffc7a8192b5dd8148f1371a8ecad8ec409e1ffffffffffffffffffffffffffffffcd375d9349a5d79e2a80ca217d149c08e1ffffffffffffffffffffffffffffffcd375d9349a5d79e2a80ca217d149c082172166798485c338f9a6d60f3b21891 + +# Test case 244 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = f59d56151de28bef83505f6d89c0b0f7f75b2fa8e6dce386075db283ec85ee62555baffad423af25f66069bb69fb6f4d +Out = d6ee4ee25d3bdea81e76de8934cc51fb849cfca7685708575dc6df7a01e36a81849cfca7685708575dc6df7a01e36a81831312cbb0f165dc3e8ff52125f48640 + +# Test case 245 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = f717f8d5b28032d5c8e8061cd44d71e4f2d55de772fe7a91ce85e410db3e2d8d50d5ddb5400136323fb83f285e40aca2 +Out = d464e022f259679255ce87f8694190e881128ee8fc759140941e89e93658a96e81128ee8fc759140941e89e93658a96e821312db9826b5e7fe0a9d30c5e28d4f + +# Test case 246 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = f28ce708bf26aab862d97e1b42f31ef3e68a922c9219d30f07554d7d99f2bde92c38aca2438b588d5459493e97e7fa33e24c07dd98f9b253ab0c318d9b14f6b15303478f2869b93ee29c837e95fb6b99ee460cd3bb95cf00e009ffd06b86ac75 +Out = d1ffffffffffffffffffffffffffffff954d41231c9238de5dce20847494390afdffffffffffffffffffffffffffffff124a4419f35e64d7f465b3f489e2020dfdffffffffffffffffffffffffffffff124a4419f35e64d7f465b3f489e2020dc1045769d487d545cef3f0d34b7a8733 + +# Test case 247 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 1fde9b9ec8b247d42bbee2016d6715badf0599194b0ce890cc1d8eb383b57f38dcf0b056a2eecc51d30838e640615e1435df81077d068077ce805ea592f6f88833d14da7b4f76f9f68fa8903138d563c8f661fc86de3e72d17ea30a3e5aa79dd +Out = ffffffffffffffffffffffffffffffff6270e3a5a0c3fdb56540b490e801ea61edffffffffffffffffffffffffffffff92c0eac6b200642c2a3cc3a0f3df040cedffffffffffffffffffffffffffffff92c0eac6b200642c2a3cc3a0f3df040c6cf2f9230af8679e7ecb19421362fce3 + +# Test case 248 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = dc8ce708bf26aab862d97e1b42f31ef32e6784d857df07543d0dc72f179935fbede8c8baf01ee2044b162cbb343b355acc29d82327cd93f2bfd918034ed5c42a +Out = ffffffffffffffffffffffffffffffff5da057d7d954ec856796aad6faffb1183c2f9be74c6a4576e0b09a7a5c2330963c2f9be74c6a4576e0b09a7a5c23309664e7efd24516a83e2c87e06a76e2dea3 + +# Test case 249 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = f78ce708bf26aab862d97e1b42f31ef34c6ead26f84a0225d557745d32fc72e72c38aca2438b588d5459493e97e7fa3364db334b69bee579383e61ae742c71bb5303478f2869b93ee29c837e95fb6b9968d138454ad2982a733baff384be2b7f +Out = d4ffffffffffffffffffffffffffffff3fa97e2976c1e9f48fcc19a4df9af604fdffffffffffffffffffffffffffffff94dd708f021933fd6757e3d766da8507fdffffffffffffffffffffffffffffff94dd708f021933fd6757e3d766da8507e6cc6729d79ba558cd73b03cba54d660 + +# Test case 250 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 12de9b9ec8b247d42bbee2016d6715ba327f3a1befb4287c17450391ed0eb854d6f0b056a2eecc51d30838e640615e141460d3545c29ddc790711b8e7533698539d14da7b4f76f9f68fa8903138d563caed94d9b4cccba9d491b7588026fe8d0 +Out = f2ffffffffffffffffffffffffffffff8f0a40a7047b3d59be1839b286ba2d0de7ffffffffffffffffffffffffffffffb37fb895932f399c74cd868b141a9501e7ffffffffffffffffffffffffffffffb37fb895932f399c74cd868b141a950174dda12e0558877bc0e40c3eace0af29 + +# Test case 251 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = f08ce708bf26aab862d97e1b42f31ef34fd8c3757c9f2938dc3b07d85898bfe22a38aca2438b588d5459493e97e7fa336155412415cbdd760142b62c2ec83fbf5503478f2869b93ee29c837e95fb6b996d5f4a2a36a7a0254a477871de5a657b +Out = d3ffffffffffffffffffffffffffffff3c1f107af214c2e986a06a21b5fe3b01fbffffffffffffffffffffffffffffff915302e07e6c0bf25e2b34553c3ecb03fbffffffffffffffffffffffffffffff915302e07e6c0bf25e2b34553c3ecb03e5cc6739bfd0f4638def574b5a43dd6f + +# Test case 252 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 1bde9b9ec8b247d42bbee2016d6715ba85b67664ee49fa347fbfd2dd92007c57def0b056a2eecc51d30838e640615e14fb27ee075b3c0f0f682babdde63dad8731d14da7b4f76f9f68fa8903138d563c419e70c84bd96855b141c5db91612cd2 +Out = fbffffffffffffffffffffffffffffff38c30cd80586ef11d6e2e8fef9b4e90eefffffffffffffffffffffffffffffff5c3885c6943aeb548c9736d887145103efffffffffffffffffffffffffffffff5c3885c6943aeb548c9736d887145103502455343d39db87947d7346a8e0af39 + +# Test case 253 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = f28ce708bf26aab862d97e1b42f31ef3df03ca84082f7f70ad8e4004cabd2ce42b38aca2438b588d5459493e97e7fa3328fd413caab1d02bf1c65753aa2ad3b95403478f2869b93ee29c837e95fb6b9924f74a3289ddad78bac3990e5ab8897d +Out = d1ffffffffffffffffffffffffffffffacc4198b86a494a1f7152dfd27dba807faffffffffffffffffffffffffffffffd8fb02f8c11606afaeafd52ab8dc2705faffffffffffffffffffffffffffffffd8fb02f8c11606afaeafd52ab8dc27050fca702228817d53ee64d142b192e665 + +# Test case 254 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = f38ce708bf26aab862d97e1b42f31ef31ffc31ae69399394b8c338674c3dfde92938aca2438b588d5459493e97e7fa33477ec8cf3ea3d4d5d76d85ad2b7f0bb85603478f2869b93ee29c837e95fb6b994b74c3c11dcfa9869c684bf0dbed517c +Out = d0ffffffffffffffffffffffffffffff6c3be2a1e7b27845e258559ea15b790af8ffffffffffffffffffffffffffffffb7788b0b55040251880407d43989ff04f8ffffffffffffffffffffffffffffffb7788b0b55040251880407d43989ff04efc3b035ded6b460bfce6f494955e677 + +# Test case 255 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 4f115e67ecd3d4178c4c60e713ab4e5e4156269fe3da101eeb0abf8dda20fe8fff47139a5f4e3f8e92d7a3b71eb4ff0e6aece983e64f97e43ff5295bc884fa7773a0085c32ddfcbeb01a8be4c34d5331a2d909c10a1bdcd318046d320583f6b7 +Out = d6ffffffffffffffffffffffffffffffd85108996dfff8a26ede1e76de151701e8ffffffffffffffffffffffffffffffc5265a700c30c72da2df2eb129447b0ae8ffffffffffffffffffffffffffffffc5265a700c30c72da2df2eb129447b0a3ea8f9b2012321e63d5fb5bc2c5d332d + +# Test case 256 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 1fde9b9ec8b247d42bbee2016d6715baf999461058f6d7733e5cd0d1639d9025cbf0b056a2eecc51d30838e640615e14520a0da50439db00e289e1791342068e24d14da7b4f76f9f68fa8903138d563ce8b3936a14dcbc5a3be38f7f641e87db +Out = ffffffffffffffffffffffffffffffff44ec3cacb339c2569701eaf20829057cfafffffffffffffffffffffffffffffff5156664cb3f3f5b06357c7c726bfa0afafffffffffffffffffffffffffffffff5156664cb3f3f5b06357c7c726bfa0abf7fbd422cbf0e700fd1605be8fd212f + +# Test case 257 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = 2bfd0d56ece98771756d60d9d9106cd0c6fc106936c7ef347c078fd71c54228164fc903b0438a3978d3a54ef992aa3ae +Out = 088e15a1ac30d236e84be13d641c8ddcb53bc366b84c04e5269ce22ef132a662b53bc366b84c04e5269ce22ef132a662345fc9fe573c136c1be83730500ce662 + +# Test case 258 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = f68ce708bf26aab862d97e1b42f31ef37cc2255decdf8e0fe1373591da0e28e42838aca2438b588d5459493e97e7fa33e291fb4838019c51dfb7141515bb53b15703478f2869b93ee29c837e95fb6b99ee9bf0461b6de10294b2da48e5290975 +Out = d5ffffffffffffffffffffffffffffff0f05f652625465debbac58683768ac07f9ffffffffffffffffffffffffffffff1297b88c53a64ad580de966c074da70df9ffffffffffffffffffffffffffffff1297b88c53a64ad580de966c074da70d336f97a5faa995a2a03781b591588da8 + +# Test case 259 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = c68ce708bf26aab862d97e1b42f31ef37ab66f8090c149e452ec7f20327eb2ea0438aca2438b588d5459493e97e7fa338d2613ea0ef8b656b247373ecec015bc7b03478f2869b93ee29c837e95fb6b99812c18e42d94cb05f942f9633e524f78 +Out = e5ffffffffffffffffffffffffffffff0971bc8f1e4aa235087712d9df183609d5ffffffffffffffffffffffffffffff7d20502e655f60d2ed2eb547dc36e100d5ffffffffffffffffffffffffffffff7d20502e655f60d2ed2eb547dc36e1009351c680c8a5d34882d42145e89745c4 + +# Test case 260 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = c68ce708bf26aab862d97e1b42f31ef374b66f8090c149e452ec7f20327eb2ea2e38aca2438b588d5459493e97e7fa33acd9ec859e0866620cc24c8a97d5d9f55103478f2869b93ee29c837e95fb6b99a0d3e78bbd641b3147c782d767478331 +Out = e5ffffffffffffffffffffffffffffff0771bc8f1e4aa235087712d9df183609ffffffffffffffffffffffffffffffff5cdfaf41f5afb0e653abcef385232d49ffffffffffffffffffffffffffffffff5cdfaf41f5afb0e653abcef385232d49d79266cd25a784599a0a8e31fc84d604 + +# Test case 261 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = f78ce708bf26aab862d97e1b42f31ef34251cd29b0aaa960557c9ea2828334e4e4e231db0a27fac9ec9e744886eb0133c5232142ddf48b3f185140f0fc05f043 +Out = d4ffffffffffffffffffffffffffffff31961e263e2142b10fe7f35b6fe5b00735256286b6535dbb4738c289eef304ff35256286b6535dbb4738c289eef304ff9d671d407d7660459d5d582d83915efe + +# Test case 262 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = f58ce708bf26aab862d97e1b42f31ef373bd9f01bf3331b12e31dd14cf11feee1d38aca2438b588d5459493e97e7fa33625c6965f61a1c36118c747076d5b7b76203478f2869b93ee29c837e95fb6b996e56626bd57661655a89ba2d8647ed73 +Out = d6ffffffffffffffffffffffffffffff007a4c0e31b8da6074aab0ed22777a0dccffffffffffffffffffffffffffffff925a2aa19dbdcab24ee5f6096423430bccffffffffffffffffffffffffffffff925a2aa19dbdcab24ee5f6096423430b7b207c2c3278c64f0d6b913fe371fe63 + +# Test case 263 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 1fde9b9ec8b247d42bbee2016d6715bad64add2aa3c5a30a31d9e65e90f93ad1cbf0b056a2eecc51d30838e640615e14de9aeab86144d5464811b2373ba4cc8324d14da7b4f76f9f68fa8903138d563c6423747771a1b21c917bdc314cf84dd6 +Out = ffffffffffffffffffffffffffffffff6b3fa796480ab62f9884dc7dfb4daf88faffffffffffffffffffffffffffffff79858179ae42311dacad2f325a8d3007faffffffffffffffffffffffffffffff79858179ae42311dacad2f325a8d300762630c18de8c10876adb9f30f300963f + +# Test case 264 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = dc8ce708bf26aab862d97e1b42f31ef3ec0933f0bfb91218cea0d74e061f559e2d38aca2438b588d5459493e97e7fa338d5b67e0acee534ce2d9791487b1ecb25203478f2869b93ee29c837e95fb6b9981516cee8f822e1fa9dcb7497723b676 +Out = ffffffffffffffffffffffffffffffff9fcee0ff3132f9c9943bbab7eb79d17dfcffffffffffffffffffffffffffffff7d5d2424c74985c8bdb0fb6d9547180efcffffffffffffffffffffffffffffff7d5d2424c74985c8bdb0fb6d9547180e3672162bb1f3ff537ece013f1aca4f68 + +# Test case 265 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 1fde9b9ec8b247d42bbee2016d6715bacc3492272b8a4b112a4e7d7ccf092692cef0b056a2eecc51d30838e640615e1430ce678e9375b2af0b82c2d2fbd7928c21d14da7b4f76f9f68fa8903138d563c8a77f9418390d5f5d2e8acd48c8b13d9 +Out = ffffffffffffffffffffffffffffffff7141e89bc0455e348313475fa4bdb3cbffffffffffffffffffffffffffffffff97d10c4f5c7356f4ef3e5fd79afe6e08ffffffffffffffffffffffffffffffff97d10c4f5c7356f4ef3e5fd79afe6e08feb6412b9031f076eddcd9426fff5b31 + +# Test case 266 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = dc8ce708bf26aab862d97e1b42f31ef3ee83a14f48db696291080edfcc898b882b38aca2438b588d5459493e97e7fa338ad5f6b0283a8b39ebedce92785da9b65403478f2869b93ee29c837e95fb6b9986dffdbe0b56f66aa0e800cf88cff372 +Out = ffffffffffffffffffffffffffffffff9d447240c65082b3cb93632621ef0f6bfaffffffffffffffffffffffffffffff7ad3b574439d5dbdb4844ceb6aab5d0afaffffffffffffffffffffffffffffff7ad3b574439d5dbdb4844ceb6aab5d0a3572163b99284f5f3e4aa94dbab85677 + +# Test case 267 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 34de9b9ec8b247d42bbee2016d6715ba722b6549c9df0f4b04b5f7432203fa54cef0b056a2eecc51d30838e640615e1487de186cd28e43544c73de628fd1d60e21d14da7b4f76f9f68fa8903138d563c3d6786a3c26b240e9519b064f88d575b +Out = d4ffffffffffffffffffffffffffffffcf5e1ff522101a6eade8cd6049b76f0dffffffffffffffffffffffffffffffff20c173ad1d88a70fa8cf4367eef82a8affffffffffffffffffffffffffffffff20c173ad1d88a70fa8cf4367eef82a8adafdf430c8124483c175404b6bff5b41 + +# Test case 268 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = dc8ce708bf26aab862d97e1b42f31ef3e87dd08ed4e4e04c5877616cbb02cabb2938aca2438b588d5459493e97e7fa33874f0401d457e336f4311f1152f957ba5603478f2869b93ee29c837e95fb6b998b450f0ff73b9e65bf34d14ca26b0d7e +Out = ffffffffffffffffffffffffffffffff9bba03815a6f0b9d02ec0c9556644e58f8ffffffffffffffffffffffffffffff774947c5bff035b2ab589d68400fa306f8ffffffffffffffffffffffffffffff774947c5bff035b2ab589d68400fa3063472164b815d9e6afec5505c5aa75d86 + +# Test case 269 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 4c115e67ecd3d4178c4c60e713ab4e5ebb5357ed314ad740b9910fad6f01d781f047139a5f4e3f8e92d7a3b71eb4ff0ec8042b414fdd1bba3a6c936b7ed678797ca0085c32ddfcbeb01a8be4c34d53310031cb03a389508d1d9dd702b3d174b9 +Out = d5ffffffffffffffffffffffffffffff225479ebbf6f3ffc3c45ae566b343e0fe7ffffffffffffffffffffffffffffff67ce98b2a5a24b73a74694819f16f904e7ffffffffffffffffffffffffffffff67ce98b2a5a24b73a74694819f16f904e6022cc3ba20e3f9065fdfcc43a9dc40 + +# Test case 270 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = c88ce708bf26aab862d97e1b42f31ef36be436e346f8f2b32f4cbbaef95150ef0438aca2438b588d5459493e97e7fa332fb76b5132e930f6d0acf70875e977b57b03478f2869b93ee29c837e95fb6b9923bd605f11854da59ba93955857b2d71 +Out = ebffffffffffffffffffffffffffffff1823e5ecc873196275d7d6571437d40cd5ffffffffffffffffffffffffffffffdfb12895594ee6728fc57571671f8309d5ffffffffffffffffffffffffffffffdfb12895594ee6728fc57571671f83093a7216d7ee1da018ce8412f251656b19 + +# Test case 271 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 66115e67ecd3d4178c4c60e713ab4e5ef64296975af7fced168181f76c6508e1c947139a5f4e3f8e92d7a3b71eb4ff0e4975060f7ddef4a098699333b30fbf7c45a0085c32ddfcbeb01a8be4c34d53318140e64d918abf97bf98d75a7e08b3bc +Out = ffffffffffffffffffffffffffffffff6f45b891d4d214519355200c6850e16fdeffffffffffffffffffffffffffffffe6bfb5fc97a1a469054394d952cf3e01deffffffffffffffffffffffffffffffe6bfb5fc97a1a469054394d952cf3e01353e304fd8553286b26e0d59942fe7cd + +# Test case 272 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = c58ce708bf26aab862d97e1b42f31ef3783cf9302c7d22914b38aca2e7d374ef1d38aca2438b588d5459493e97e7fa33228f2d23597640d574f8e20c4f6b6bb56203478f2869b93ee29c837e95fb6b992e85262d7a1a3d863ffd2c51bff93171 +Out = e6ffffffffffffffffffffffffffffff0bfb2a3fa2f6c94011a3c15b0ab5f00cccffffffffffffffffffffffffffffffd2896ee732d196512b9160755d9d9f09ccffffffffffffffffffffffffffffffd2896ee732d196512b9160755d9d9f09367216178ff1dc45ce73b02cd21f8755 + +# Test case 273 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = dc8ce708bf26aab862d97e1b42f31ef35db72f89d1402b1a0373ff0a9c5cd44b6d67af40798f5455501792953248ec234ca6bfd9ae5c25a3a4d8a62d48a61d53 +Out = ffffffffffffffffffffffffffffffff2e70fc865fcbc0cb59e892f3713a50a8bca0fc1dc5fbf327fbb124545a50e9efbca0fc1dc5fbf327fbb124545a50e9ef0b4961c9525ea2f2cdad6273e1c7824c + +# Test case 274 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = dc8ce708bf26aab862d97e1b42f31ef35f215ec87d62a264cadb519b4ac90a7668d1dd03e56eda6399ac7803e7dd22114910cd9a32bdab956d634cbb9d33d361 +Out = ffffffffffffffffffffffffffffffff2ce68dc7f3e949b590403c62a7af8e95b9168e5e591a7d11320acec28fc527ddb9168e5e591a7d11320acec28fc527dd0a4961d93a93f1fd8d290a8281b6895b + +# Test case 275 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060710abb165 +AD = ffffffff +In = dc8ce708bf26aab862d97e1b42f31ef3d15ad590dd0f40ba18acd168f6ac777a0f38aca2438b588d5459493e97e7fa33932a097f1d39a04ad30f1b6c650260bf7003478f2869b93ee29c837e95fb6b999f2002713e55dd19980ad53195903a7b +Out = ffffffffffffffffffffffffffffffffa29d069f5384ab6b4237bc911bcaf399deffffffffffffffffffffffffffffff632c4abb769e76ce8c66991577f49403deffffffffffffffffffffffffffffff632c4abb769e76ce8c66991577f494033572161355240943de9406292a64c551 + +# Test case 276 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 4d115e67ecd3d4178c4c60e713ab4e5e6ee628fc4b5830184cd293364a213e84fe47139a5f4e3f8e92d7a3b71eb4ff0e29db953ad5458fea61f013ea1854fe7572a0085c32ddfcbeb01a8be4c34d5331e1ee75783911c4dd46015783d553f2b5 +Out = d4fffffffffffffffffffffffffffffff7e106fac57dd8a4c90632cd4e14d70ae9ffffffffffffffffffffffffffffff861126c93f3adf23fcda1400f9947f08e9ffffffffffffffffffffffffffffff861126c93f3adf23fcda1400f9947f08e00d2e8bae5d09c28e9bf59409545d09 + +# Test case 277 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 3ede9b9ec8b247d42bbee2016d6715ba8567a7fde812a3aa2f552a33c1718c58e2f0b056a2eecc51d30838e640615e14bb8729fd148f23b2a916b7f40f2f29810dd14da7b4f76f9f68fa8903138d563c013eb732046a44e8707cd9f27873a8d4 +Out = deffffffffffffffffffffffffffffff3812dd4103ddb68f86081010aac51901d3ffffffffffffffffffffffffffffff1c98423cdb89c7e94daa2af16e06d505d3ffffffffffffffffffffffffffffff1c98423cdb89c7e94daa2af16e06d505b4ccb422bc5f7264aff73f3675ff5b19 + +# Test case 278 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 43eadae036f733ea9b5b7eb22aee395db6f51a4d10bc2460810c229651556acf384ad82e3e280cad69f0df25b42b83b0 +Out = da047b7825db1802e8e8e1aac6ba88fc2ff2344b9e99ccdc04d8836d556083412ff2344b9e99ccdc04d8836d55608341973e270a7afcab75348e14dbe19c5156 + +# Test case 279 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 6a115e67ecd3d4178c4c60e713ab4e5e519cccebf72573dbee8c12f74255d18c0add1035861ffc0b7f40079b969f8c63b2af4fa3ccd16cb38f425c3996140def +Out = f3ffffffffffffffffffffffffffffffc89be2ed79009b676b58b30c466038021d65fc5026ae3c7a12685bd377d48c921d65fc5026ae3c7a12685bd377d48c92a22390224c5db0f01696743d870725c5 + +# Test case 280 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = e235b8c21384557085c3f2eb2a8fa36058cffd2af743dacf96b4ae4d51b4e488d6703f49d9d7f2027e4853feb4ca0df7 +Out = 7bdb195a00a87e98f6706df3c6db12c1c1c8d32c7966327313600fb655810d06c1c8d32c7966327313600fb655810d06437d1efad21b0865a541b5cab62e2a44 + +# Test case 281 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 66115e67ecd3d4178c4c60e713ab4e5e8fab58574a322bac6f394474e4ce7eaec347139a5f4e3f8e92d7a3b71eb4ff0e71532dfb0e9141b00983394722829e7c4fa0085c32ddfcbeb01a8be4c34d5331b966cdb9e2c50a872e727d2eef8592bc +Out = ffffffffffffffffffffffffffffffff16ac7651c417c310eaede58fe0fb9720d4ffffffffffffffffffffffffffffffde999e08e4ee117994a93eadc3421f01d4ffffffffffffffffffffffffffffffde999e08e4ee117994a93eadc3421f01acf4ffa20c0d06d61a18e9a8d4c84d1d + +# Test case 282 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 61115e67ecd3d4178c4c60e713ab4e5e5efe679ba17384c55eb8cc193666fe8d04608c3503d217aa3f90a9b0e1b3b313bc12d3a3491c8712cf92f212e138329f +Out = f8ffffffffffffffffffffffffffffffc7f9499d2f566c79db6c6de23253170313d86050a363d7db52b8f5f800f8b3e213d86050a363d7db52b8f5f800f8b3e2cd466d06e75b7fd18d5fe21d9227d9a7 + +# Test case 283 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 9064b88a282052a1ee44df05ad213da679f8d1f971da17437a2b5e04fbca167151b2650ec945fec70588bc65a616a5f24f354c0c1580af3662d5f8151e3f7e82dd557ec8a4d63df7274594367bef09cd +Out = 098a19123b0c79499df7401d41758c07e0ffffffffffffffffffffffffffffff460a896b69f43eb668a0e02d475da503e0ffffffffffffffffffffffffffffff460a896b69f43eb668a0e02d475da503ce8a3d4d887d95613d829b538ed01196 + +# Test case 284 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 43115e67ecd3d4178c4c60e713ab4e5eeef67bd4795b74015a3493905d544a86e847139a5f4e3f8e92d7a3b71eb4ff0e3197be28eff843592bd8fc8d578421d664a0085c32ddfcbeb01a8be4c34d5331f9a25e6a03ac086e0c29b8e49a832d16 +Out = daffffffffffffffffffffffffffffff77f155d2f77e9cbddfe0326b5961a308ffffffffffffffffffffffffffffffff9e5d0ddb05871390b6f2fb67b644a0abffffffffffffffffffffffffffffffff9e5d0ddb05871390b6f2fb67b644a0ab08289f5199df476fe90475cb95225566 + +# Test case 285 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 39de9b9ec8b247d42bbee2016d6715ba4092e1f9a22c8b18184d805c128ade57c7f0b056a2eecc51d30838e640615e1464fe8b9bdd215a620973affefe93398528d14da7b4f76f9f68fa8903138d563cde471554cdc43d38d019c1f889cfb8d0 +Out = d9fffffffffffffffffffffffffffffffde79b4549e39e3db110ba7f793e4b0ef6ffffffffffffffffffffffffffffffc3e1e05a1227be39edcf32fb9fbac501f6ffffffffffffffffffffffffffffffc3e1e05a1227be39edcf32fb9fbac5016d46d2230a9848d518f9d94bb2c49caa + +# Test case 286 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 6b115e67ecd3d4178c4c60e713ab4e5e1e34412ab0a056e809d5d4b92be1128a4b2a651a62aeab26cf437fb195407574f3583a8c28603b9e3f41241395cbf4f8 +Out = f2ffffffffffffffffffffffffffffff87336f2c3e85be548c0175422fd4fb045c92897fc21f6b57a26b23f9740b75855c92897fc21f6b57a26b23f9740b758506df93f651ea5cc56911f30d3e58f997 + +# Test case 287 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 3fe606108f35869df4c7aa0128464a1265f8d1f971da17437a2b5e04fbca1671fdbe843a0ad9be25055992ab6dcbc9f153354c0c1580af3662d5f8151e3f7e8271599ffc674a7d152794baf8b03265ce +Out = a608a7889c19ad7587743519c412fbb3fcffffffffffffffffffffffffffffffea06685faa687e546871cee38c80c900fcffffffffffffffffffffffffffffffea06685faa687e546871cee38c80c9009264fc0f47febb30661254daf9a06189 + +# Test case 288 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 6e8eb98cf7fffe4cd683568cf892991564f8d1f971da17437a2b5e04fbca1671c70f5d8b30c64bf2e6d1d613f40e0bf052354c0c1580af3662d5f8151e3f7e824be8464d5d5588c2c41cfe4029f7a7cf +Out = f7601814e4d3d5a4a530c99414c628b4fdffffffffffffffffffffffffffffffd0b7b1ee90778b838bf98a5b15450b01fdffffffffffffffffffffffffffffffd0b7b1ee90778b838bf98a5b15450b0169a124fc7f96e220d1a031ced5527279 + +# Test case 289 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 66115e67ecd3d4178c4c60e713ab4e5e18f125ef374c1454b680e23427e7dc69e447139a5f4e3f8e92d7a3b71eb4ff0e858b08eb1d581570a7cd1e48593b757568a0085c32ddfcbeb01a8be4c34d53314dbee8a9f10c5e47803c5a21943c79b5 +Out = ffffffffffffffffffffffffffffffff81f60be9b969fce8335443cf23d235e7f3ffffffffffffffffffffffffffffff2a41bb18f72745b93ae719a2b8fbf408f3ffffffffffffffffffffffffffffff2a41bb18f72745b93ae719a2b8fbf408dfaf8a3a15d45e7f4c3430048d8589f0 + +# Test case 290 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 15de9b9ec8b247d42bbee2016d6715bacc1629a40cd11eafdf04138b45afe458eff0b056a2eecc51d30838e640615e14340ac9b45a5896a418a8cee8032e078f00d14da7b4f76f9f68fa8903138d563c8eb3577b4abdf1fec1c2a0ee747286da +Out = f5ffffffffffffffffffffffffffffff71635318e71e0b8a765929a82e1b7101deffffffffffffffffffffffffffffff9315a275955e72fffc1453ed6207fb0bdeffffffffffffffffffffffffffffff9315a275955e72fffc1453ed6207fb0bc6f23204865b0adde0070037d6538dd3 + +# Test case 291 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = b02ab747a310d6a3bbdb97018a3be8b341f8d1f971da17437a2b5e04fbca1671b7a338bc3423895f0fd96cdb27a787f277354c0c1580af3662d5f8151e3f7e823b44237a59b04a6f2d144488fa5e2bcd +Out = 29c416dfb03cfd4bc8680819666f5912d8ffffffffffffffffffffffffffffffa01bd4d99492492e62f13093c6ec8703d8ffffffffffffffffffffffffffffffa01bd4d99492492e62f13093c6ec87033408eb2b13a9b76befcedf699422d61f + +# Test case 292 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 40115e67ecd3d4178c4c60e713ab4e5e380ef93aeb61aa307f141323c38e0685f647139a5f4e3f8e92d7a3b71eb4ff0e3f769a30e8951ff2fb365fa780fdde7e7aa0085c32ddfcbeb01a8be4c34d5331f7437a7204c154c5dcc71bce4dfad2be +Out = d9ffffffffffffffffffffffffffffffa109d73c6544428cfac0b2d8c7bbef0be1ffffffffffffffffffffffffffffff90bc29c302ea4f3b661c584d613d5f03e1ffffffffffffffffffffffffffffff90bc29c302ea4f3b661c584d613d5f0309f4f2a3936d7461a67ce022176bb8dd + +# Test case 293 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 40115e67ecd3d4178c4c60e713ab4e5e060ef93aeb61aa307f141323c38e0685ee47139a5f4e3f8e92d7a3b71eb4ff0e2bca70bfcdf1171ab611d12bed5d627a62a0085c32ddfcbeb01a8be4c34d5331e3ff90fd21a55c2d91e09542205a6eba +Out = d9ffffffffffffffffffffffffffffff9f09d73c6544428cfac0b2d8c7bbef0bf9ffffffffffffffffffffffffffffff8400c34c278e47d32b3bd6c10c9de307f9ffffffffffffffffffffffffffffff8400c34c278e47d32b3bd6c10c9de3072eb2679aadfd824a5fd8fa2e4a55a65c + +# Test case 294 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 56115e67ecd3d4178c4c60e713ab4e5e6c7e1312c6774fae7d1e5d0cc609028ff547139a5f4e3f8e92d7a3b71eb4ff0e81c9e61cbeeed5546b1ce5d8fef21a7a79a0085c32ddfcbeb01a8be4c34d533149fc065e52ba9e634ceda1b133f516ba +Out = cffffffffffffffffffffffffffffffff5793d144852a712f8cafcf7c23ceb01e2ffffffffffffffffffffffffffffff2e0355ef5491859df636e2321f329b07e2ffffffffffffffffffffffffffffff2e0355ef5491859df636e2321f329b075e89349f6b011cd6e24ee6ac2f590c21 + +# Test case 295 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 32de9b9ec8b247d42bbee2016d6715ba258d5d3e441683f546beba2e23755f5ccef0b056a2eecc51d30838e640615e149d13fdf8fa899836fa5c410d4ccd25ea21d14da7b4f76f9f68fa8903138d563c27aa6337ea6cff6c23362f0b3b91a4bf +Out = d2ffffffffffffffffffffffffffffff98f82782afd996d0efe3800d48c1ca05ffffffffffffffffffffffffffffffff3a0c9639358f7c6d1ee0dc082de4d96effffffffffffffffffffffffffffffff3a0c9639358f7c6d1ee0dc082de4d96ed1be7426cd12446fe52e8d45331e0835 + +# Test case 296 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 2ea8410b4dca8c9d5369a033d8db61e46cf8d1f971da17437a2b5e04fbca1671f0f58e8bba6cf1a52146273d8fe0c4fc5a354c0c1580af3662d5f8151e3f7e827c12954dd7ff3295038b0f6e521968c3 +Out = b746e0935ee6a77520da3f2b348fd045f5ffffffffffffffffffffffffffffffe74d62ee1add31d44c6e7b756eabc40df5ffffffffffffffffffffffffffffffe74d62ee1add31d44c6e7b756eabc40db24537fcb0dcb6200b0285cafc9c3a7d + +# Test case 297 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 17059a7c8883a28b90bd94ae44d1543662f8d1f971da17437a2b5e04fbca1671a23018bf8e68e413e99ac2d4ab3f8df154354c0c1580af3662d5f8151e3f7e822ed70379e3fb2723cb57ea8776c621ce +Out = 8eeb3be49baf8963e30e0bb6a885e597fbffffffffffffffffffffffffffffffb588f4da2ed9246284b29e9c4a748d00fbffffffffffffffffffffffffffffffb588f4da2ed9246284b29e9c4a748d0043300400ea36e720361153ce0c5d637d + +# Test case 298 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = aaa1b258fd4b54b497b520806a66d7aa68f8d1f971da17437a2b5e04fbca167199132a234a8c789bf8544547940ec3f35e354c0c1580af3662d5f8151e3f7e8215f431e5271fbbabda996d1449f76fcc +Out = 334f13c0ee677f5ce406bf988632660bf1ffffffffffffffffffffffffffffff8eabc646ea3db8ea957c190f7545c302f1ffffffffffffffffffffffffffffff8eabc646ea3db8ea957c190f7545c302d79a0310124adc30c6b64cdef8993e8d + +# Test case 299 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 9841cfc927a57dc491ab35427ff935e66ef8d1f971da17437a2b5e04fbca1671a683c8f9f9e6780fda4940ddedd76bf258354c0c1580af3662d5f8151e3f7e822a64d33f9475bb3ff884688e302ec7cd +Out = 01af6e513489562ce218aa5a93ad8447f7ffffffffffffffffffffffffffffffb13b249c5957b87eb7611c950c9c6b03f7ffffffffffffffffffffffffffffffb13b249c5957b87eb7611c950c9c6b030aeb04ecf7def40c42025bbae5509169 + +# Test case 300 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = f4ebbe3fca96bc4885b35582c43e0eb3588a85431430eada56a2c5dc944b6aa6b4570e8446e886bcbff82a24f49be5ed42e0943e30f91ba41b4362fa9ed6037b5b76f37550f12572040a9bc1a777edc5 +Out = 14cada5efddb046351f2487c56a6e4f6e5ffffffffffffffffffffffffffffff8558412d1bf9b512930fed3d4b054406e5ffffffffffffffffffffffffffffff8558412d1bf9b512930fed3d4b054406af7293eb09957d9de7432dd41316f0e4 + +# Test case 301 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 42115e67ecd3d4178c4c60e713ab4e5e0b61bf9b7caf83cc34da625593514289e847139a5f4e3f8e92d7a3b71eb4ff0e696a5c7fb9da9cd4a39c8591086db42d64a0085c32ddfcbeb01a8be4c34d5331a15fbc3d558ed7e3846dc1f8c56ab8ed +Out = dbffffffffffffffffffffffffffffff9266919df28a6b70b10ec3ae9764ab07ffffffffffffffffffffffffffffffffc6a0ef8c53a5cc1d3eb6827be9ad3550ffffffffffffffffffffffffffffffffc6a0ef8c53a5cc1d3eb6827be9ad35508fc4f77a6ee052a4c314780b8df9a2d0 + +# Test case 302 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 1ade9b9ec8b247d42bbee2016d6715ba571a3fca3cda7def4c93d4a382ca3a57eaf0b056a2eecc51d30838e640615e1476cddbee2f185776174f6df3bbe5b38105d14da7b4f76f9f68fa8903138d563ccc7445213ffd302cce2503f5ccb932d4 +Out = faffffffffffffffffffffffffffffffea6f4576d71568cae5ceee80e97eaf0edbffffffffffffffffffffffffffffffd1d2b02fe01eb32df3f3f0f6dacc4f05dbffffffffffffffffffffffffffffffd1d2b02fe01eb32df3f3f0f6dacc4f05e178b0d5eb9bc551fa645c49f9f17667 + +# Test case 303 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 00010203040506072dd4cd40 +AD = ffffffff +In = 4b115e67ecd3d4178c4c60e713ab4e5ef28e4d0f20ca1644470c9cdac6000887ed47139a5f4e3f8e92d7a3b71eb4ff0e1464775bacd5c69fe26e1a74968ea27e61a0085c32ddfcbeb01a8be4c34d5331dc51971940818da8c59f5e1d5b89aebe +Out = d2ffffffffffffffffffffffffffffff6b896309aeeffef8c2d83d21c235e109faffffffffffffffffffffffffffffffbbaec4a846aa96567f441d9e774e2303faffffffffffffffffffffffffffffffbbaec4a846aa96567f441d9e774e2303232ff78a96f347b453ba711b79367ee0 + +# Test case 304 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 1fde9b9ec8b247d42bbee2016d6715babe31a501536a7c91e4a102cc27cdfe09d2f0b056a2eecc51d30838e640615e14dd9416a12e2f81bdee023d462feef7833dd14da7b4f76f9f68fa8903138d563c672d886e3ecae6e73768534058b276d6 +Out = ffffffffffffffffffffffffffffffff0344dfbdb8a569b44dfc38ef4c796b50e3ffffffffffffffffffffffffffffff7a8b7d60e12965e60abea0434ec70b07e3ffffffffffffffffffffffffffffff7a8b7d60e12965e60abea0434ec70b07bdbf63db237d195ecefdc251f5f17677 + +# Test case 305 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 97311cd6e2d25a7b4eaa16f0a61ca6246b8a85431430eada56a2c5dc944b6aa695136310b6b6b5c17c9f8c02ba7d0aeb71e0943e30f91ba41b4362fa9ed6037b7a329ee1a0af160fc76d3de7e99102c3 +Out = 771078b7d59fe2509aeb0b0e34844c61d6ffffffffffffffffffffffffffffffa41c2cb9eba7866f50684b1b05e3ab00d6ffffffffffffffffffffffffffffffa41c2cb9eba7866f50684b1b05e3ab00d71bc70d5adc74e7dfd89406fc15f044 + +# Test case 306 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 34de9b9ec8b247d42bbee2016d6715ba74cf7e9d82b7e8ed9ec965f6ea310951dc104940e08a4222556828eba459f65a4a006d28729d95d79d2372f77aeeab35 +Out = d4ffffffffffffffffffffffffffffffc9ba04216978fdc837945fd581859c08ed1f06e9bd9b718c799feff21bc757b1ed1f06e9bd9b718c799feff21bc757b121e63987d494673f3040ae9de2bc0da0 + +# Test case 307 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = e72b83514e5e50509070359c1cac7e1c428a85431430eada56a2c5dc944b6aa6dad35950d8a9b55a472f9bb8860a526358e0943e30f91ba41b4362fa9ed6037b35f2a4a1ceb01694fcdd2a5dd5e65a4b +Out = 070ae7307913e87b443128628e349459ffffffffffffffffffffffffffffffffebdc16f985b886f46bd85ca13994f388ffffffffffffffffffffffffffffffffebdc16f985b886f46bd85ca13994f388e4fb945d6a2d0b947834317cc415f024 + +# Test case 308 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 8c6165f445443588041b6e044fb6baae728a85431430eada56a2c5dc944b6aa6881a54c09516a1f1cae7b9dd71130ee168e0943e30f91ba41b4362fa9ed6037b673ba931830f023f7115083822ff06c9 +Out = 6c40019572098da3d05a73fadd2e50ebcfffffffffffffffffffffffffffffffb9151b69c807925fe6107ec4ce8daf0acfffffffffffffffffffffffffffffffb9151b69c807925fe6107ec4ce8daf0ac0424863a20e5fa04ccd9784c015f034 + +# Test case 309 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 18e36174545fa7ec9ea9f05d7057c5ca638a85431430eada56a2c5dc944b6aa6434e1c5e71005b690ca5cb8d580b89ed79e0943e30f91ba41b4362fa9ed6037bac6fe1af6719f8a7b7577a680be781c5 +Out = f8c2051563121fc74ae8eda3e2cf2f8fdeffffffffffffffffffffffffffffff724153f72c1168c720520c94e7952806deffffffffffffffffffffffffffffff724153f72c1168c720520c94e7952806aa7293ffe5db30a31f2581e0e7ae56ed + +# Test case 310 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 12de9b9ec8b247d42bbee2016d6715ba54305dff6b61c40b775c352d025c1a56d7f0b056a2eecc51d30838e640615e14bce574e9e11afedbdca021e53bb9188338d14da7b4f76f9f68fa8903138d563c065cea26f1ff998105ca4fe34ce599d6 +Out = f2ffffffffffffffffffffffffffffffe945274380aed12ede010f0e69e88f0fe6ffffffffffffffffffffffffffffff1bfa1f282e1c1a80381cbce05a90e407e6ffffffffffffffffffffffffffffff1bfa1f282e1c1a80381cbce05a90e40742e5d43d1e808e79f017144d4498c235 + +# Test case 311 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 36de9b9ec8b247d42bbee2016d6715ba1132811b2f18321ba99b12432c7f865aa3352cd2d7ac70b4c6f5419767926e20352508ba45bba7410ebe1b8bb925334f +Out = d6ffffffffffffffffffffffffffffffac47fba7c4d7273e00c6286047cb1303923a637b8abd431aea02868ed80ccfcb923a637b8abd431aea02868ed80ccfcb14fba149d1c0edc8aa665851126b5afd + +# Test case 312 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 31de9b9ec8b247d42bbee2016d6715baff746ef53ec3357cbc3c3ce4ab1d2d51ed9eb456dc9d9b59f656a5d2d974d26a7b8e903e4e8a4cac3e1dffce07c38f05 +Out = d1ffffffffffffffffffffffffffffff42011449d50c2059156106c7c0a9b808dc91fbff818ca8f7daa162cb66ea7381dc91fbff818ca8f7daa162cb66ea73818cff61b7b3919ed6bde72b36e0d31326 + +# Test case 313 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 19de9b9ec8b247d42bbee2016d6715babf286fd979807951b183a188930ad15ecef0b056a2eecc51d30838e640615e1464413d71939b9cb0a4d32ef115da9e1021d14da7b4f76f9f68fa8903138d563cdef8a3be837efbea7db940f762861f45 +Out = f9ffffffffffffffffffffffffffffff025d1565924f6c7418de9babf8be4407ffffffffffffffffffffffffffffffffc35e56b05c9d78eb406fb3f474f36294ffffffffffffffffffffffffffffffffc35e56b05c9d78eb406fb3f474f36294369cf17011cae47539e2723f010cf980 + +# Test case 314 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 19de9b9ec8b247d42bbee2016d6715babd286fd979807951b183a188930ad15ee3f0b056a2eecc51d30838e640615e14f25e78fe1b53ae416d1fbc698522618f0cd14da7b4f76f9f68fa8903138d563c48e7e6310bb6c91bb475d26ff27ee0da +Out = f9ffffffffffffffffffffffffffffff005d1565924f6c7418de9babf8be4407d2ffffffffffffffffffffffffffffff5541133fd4554a1a89a3216ce40b9d0bd2ffffffffffffffffffffffffffffff5541133fd4554a1a89a3216ce40b9d0b532eb8e272a8d171378b0d42dff2bed9 + +# Test case 315 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 3dde9b9ec8b247d42bbee2016d6715bac5629699cfd4d9036cef478ed705be5650f575882c3800f757ea6e0f8c6d47acc6e551e0be2fd7029fa1341352da1ac3 +Out = ddffffffffffffffffffffffffffffff7817ec25241bcc26c5b27dadbcb12b0f61fa3a21712933597b1da91633f3e64761fa3a21712933597b1da91633f3e647f8800c5b6283dddfc41f935c01bd0d24 + +# Test case 316 from Wycheproof, Poly1305 edge case +Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +Nonce = 000102030405060703e76f6f +AD = ffffffff +In = 1fde9b9ec8b247d42bbee2016d6715ba66d624f288f52941ca24865ce96f0d9736ff33a27c23f4976fc74f1fcd82f5cca0ef17caee342362a78c15031335a8a3 +Out = ffffffffffffffffffffffffffffffffdba35e4e633a3c646379bc7f82db98ce07f07c0b2132c73943308806721c542707f07c0b2132c73943308806721c542738bfb8318c627d86c34bab1f1ebd0db0 diff -Nru botan3-3.7.1+dfsg/src/tests/data/aead/gcm.vec botan3-3.12.0+dfsg/src/tests/data/aead/gcm.vec --- botan3-3.7.1+dfsg/src/tests/data/aead/gcm.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/aead/gcm.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,5 @@ -#test cpuid aesni clmul pmull ssse3 +#test cpuid aesni clmul pmull ssse3 simd128 [AES-128/GCM] # Nist | Test Case 1 @@ -62,6 +62,13 @@ In = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 Out = A833DCC2EC04BEF5ED9E7E7FB08D61244CD795C21FEE194E7AAF7D9BD66B324F39FFC46DB5A7B9E1D6703F95290027DE6AC274CAE11C632F16261B71CE97EEA6CBE8A82B4F55F5A8B5CE1B268A7B35D8 +# Random test cases generated by OpenSSL + +Nonce = 3c448dc2a2a2d83052adab78 +Key = e59f190cd7467b1555b1e71f7770f97f +AD = 642b9ab7507f90dbde359441364a8f1e3d16cbd9b05b5626df991f4e00b6f600 +In = ef3a91cba6e7da6d22fb673f781db723fe34f2879c68c9e6013678550c5b6321cd541732aaa75e0d78c8a1935e1d0b798786fc93c5b14cd2611eba3c6b0c020451f4a4d76215726b9d96080739a80786b0fdaf285e3c36860da35956150fd8a6c6b12d5ba37064299e834293798c55c05f0eb632e8137015ba0f49c268bb75e7e83c94d9f3322a1316ac2c87cc545c89d92a4d166c3a5dca7b03699866b41283129124ca4527e59dffabbb9f79158c820967730ace38738d8273452f3af42a212bb0fdeabc2abf32236fa73f0b353fd80789f13b621f3ba7e9ba2a7f5b6a80222a7a2694a381e11e678ce1ee3420e5ebfb118d9e7a2f548de8d49a8e8c5affe1656427883d1f026c22108758ec2d6c58a312a44363029f26d78874d02bf71d55e912542e0ccad6f0ec8c471c3928d7e59e15109dccb274243d3fe1dc08922d8ecb8eda2897823b15864e33ae65469c843cb4163c0155f57653e92fdfc65aaaf1f0ca75d6714fdc8b5305f59cdde8b269cdc346583a3151aca2738f2236ef58c7de5604d078d899305f981028edbd917a527e64fef24b6aa67c5bcf9c1615482368a751a5121a28d3a579f2b6d9dfc77c558ff6206db1a3aa28f4dc9668d3b70b8b4ad09ac21b398157446cb02e6a8eb368ba462ff1078122d343a24785d3dbd02c57e91484becb7617b7e509ce87b6e56d03ef1b955f11cedaf40bac99c183f4a9481cde0e552c5d3a877c65065c2897a27e1bc20df6c1892877a881c4099cbdf7a664f183e55a57d2349fc54a9735f92be9e4bc33 +Out = 55d4ddd8d0df6b24adc0ddacd371eed45485d4553e2de648ad0e7ed417ffbd7afb0c4c3b887fe7665465131725091957186a4b7cf2576a2516d03cebfea5957abfc847841ddf3417d71a8e202f576ba51f0c1e3a85f5d567eceb4002fde5d0f51fd8f2ab796d3089264265061e13fd64428766842866bda58779ab8570cea4e2c0b49178773ae82e149ab8b36057cce70168982f14fb8f96f4742f3ff1d2f428bc48170633945e92bea5c4dc93d5415f1ad01b5340f5ce8629fa7f65a6c7c0acc37303470f387ebe461683717420509fdf7589f069dcf00b273d704b496a62e42286214a9b50e9d820ba0938673a7a8d9fed41a12a370b44f638101d38f0c0f4d1d1bed24b15e8325e4219663e320b49dc22117dbc7bfb8a20683a6e2ddafb53f541f7b47e3ed4fd774c591936fd2244a24a87925484be76879771aba24aa0846a20bcb1cf0508888695e188f5742c65ebc99595ac9e4198178aebf4a514769d0c3ee87cff31138c344b8bec835e17036590ddc294761896695c23102f9a440edc6148c7f0b861576eae02e00e4f740e740dac12b27e930d8c85dbb175b55062c1699a6354ab65fe29997c87f253ff2eb147c92006898c965b5432ecb7d484acdad528242740076fe72517622d4aada89c358791a7d7014d9d3d28729689ae6607a7d8eb5f811bee8792e33d4f2d607cd3d08da18f329cc1aff8d6bcb923093b8cce6e96c4551dc7024e4ba48ab37c785b68af68af59366161fd61020d1a7093ce041c9671a99833eb73c3a0b42c19d88115077b5810b8877b01bc076ff64a23f01fc75639 [AES-128/GCM(12)] # Nist | Test Case 5 with 12 byte tag @@ -120,6 +127,14 @@ Out = d27e88681ce3243c4830165a8fdcf9ff1de9a1d8e6b447ef6ef7b79828666e4581e79012af34ddd9e2f037589b292db3e67c036745fa22e7e9b7373bdcf566ff291c25bbb8568fc3d376a6d9 +# Generated by OpenSSL 3.6 +Key = 7B30EEF21CCF941AB489D109E7CFC0CC7CA496AF6E844CB5 +Nonce = 737092FF666E3F9DBF058170 +AD = BEB6DABC6B2655A0E2D947878536413CAD42D835944056F6AB17100E631EE7EF +In = DADE5BC3D8FD77C453B14862825629F420CD87938DA0865A950930B2AD84B7BDB44B2B41434FC200F5407629F2084C7185E7F69F795D84718FCB1F0182342A604392766DB85F78E8DDADE14B2B8E5B9D924FF1246D9354A447C97C1E4DF205F6CE4A26A7F2CFF32828E191CBA6BAA334432356D8009128429925EDBA408A56B4DBE2035EA095F911170BBADEE5A930BDFAD7AE565674BB79D462D52AD807DA22545A215BD6596B3598D2D4AEC7E88FFC33757FA3E3558BBAC06E015663F668998B593B297030B2E25E10BB82E824C9FDE0B878642981C6C532B70C713DCFEB42C742E243985BF603B0D4E1EAFF64F489B1B84A55C278F62AAA3E4670BB266F3A279D26AE5A251E6B3C7198694358DE4C9DF3489FC556B3C358ADDC6B7FF477D05173E04DBC579C1E91077EF1BE9F4DFB83D8A6C9D19B24FE188BCF79915690AC320BEA52F464A4D0D884955B3465A835E66246DFAB46B032798C188AD04428E20DF59C23AD20BE6B2EFEA6E408F3EF6DCE5C1B470A477A5CAF02C5E22B6FA2DD9F13FF97D1B60D1FF23F9961C22EB20B79EC05D3C0FFCBAFD14BC3A0BF18747A690F09157270FAE38DFF1419D9F8EAA9DDE58CCF6AD46E2583677730D00A9B9C98CFBE5FD8043B76D0A4D8E6BF95498037C8379590440764116BA44A79D995A9ACFCADC74E04469A425DD6FBF8B2753CAC61A6A003D3296EA07C5C4972EF4DABC317CCE621DD749574439A11CF957F4A069E252F97D06285F08E31FA3D38995B53378646619211810DD16AF95B8DED9D1A3B1ADCCD4C4B663CB3303399EB9A53840315178EFBA808B22E545F303C51D4E4E93D99AEDAB660B474E077860415D55293E1DD2F9889A4FA33E96F0A82297F54F10B2D0C7066AEE91AF827203B058E6BD36CE9093E89EC25ECD48D3222137EDCF34DF7A0B85E5591ED194CD674F49AC3CB27E58EAD0EBEDAC0F7D72359386BF22E8C4EECEB2DC0D16646EB75A059AE0A28A2734A79FFD45A385449672872766D4ADC997185B2326B737203AB119BFB81ECD85449870C8137BE56C3E4DD6802844A8A12224EE29E5442F06DE887E07F22422235A677E5FBA0C428097AEAA969D84E8584340EB94BD0ABBE8B403B09FF6A66A7C0EE4CD06F463BEE55E1B37A8F1C4494A10BD5D86A048049FB40755CB8A0906BED90D53300335BB4F3FCC5751D4EB96B31F69461B0691C7D1C435253337D234FAB8431D14C49D6D5A037BDA9FC9BF3D4E88DAB915D824A9BF5661F37B7C500AB5C1994E8CA451CE8F5485EB8F8B4EB46890DC33E8247F680B16396D19B03329BB08505FFE08ED118630B6FE810A3F36F21B6309B209964B9F6C9386F046A7504B3B052CD6109084E86816D969C51ADD36CC95C457D95C1FEA79BF136E8B3173F8073443A5AAB7641574243E7575ED45A50FC730A69E07A4F339AD5A105 +Out = 2AF7707FF643656B22526CA04335133786ED3CC63EBB4CC6CB9338D1E0F47A0F1B64529F4F3418A2DBBF0346C3B8BE6D7CF0650D2A4331B5B9AED47ADA52F4422A11CF6B69E48490204F75A1170F65F404FC4E758520FB61F32A7E78E8DDCCAB3555BF373281B012291CE0A01564F202F6E6596D1E7666C7EB13F3CB0598C6AFA91EC59B07B4EDB1D3FC3029C028AF14758B53D94C12664640AF32BC53271B7C8C901DC9861D5522C87BB201C0B1423DB3FA7FE97001F5E7BF0E22DEA430BDC66ED229E9236907DD912C1609185697592382A400AA9EBB205802C61307829E0E18762C64006C30A8BDE1D8E9A228609F92BBA1C840053B7CE871828CC3878CC87A494462BBEBC0A6317494B7B4AD4DCCD08BC5D2C371614F71E7D6F65DD733D638D21F589DDFCC1408290F5D7756A25FAF7EAB4BAAF3DE0F1B56939E8391634FF5F2E29937A241A77F44F7241278A825FCF1759E4A180A5603DF96E9D9B6042CE23482E6076EF384FC4A2DCECF44F802580B7D1F076581794CCE6C70B94A0AE01A065C38961E3EA69547DA3D6410B6E1EAB6501C1DDF6F448FF2B09DD2AE0CD7D8178A50EE89FD383F3A79C7401A564A9D38A512990479BFC0F4621AF9D3543A698834CD3350699517ADEB77F6589F88F48AE724B399EC84FEB6455E2D11C16B16F92EF995173A8D9503DA568B78B777D44D17F229481DB32AF84DD3887CE2AB6AE63E2DE10AB6023B3FE4B70FACE2B62509BE803763AE6E9AD175C2CE7B6A253AC0304D07D98696977F21E595C71A8D590D29A95ABC765C5D9EA91A16927CF75E44C0EB9F1EA811499E30A7834C3B46CB2464AE35ACE7FE766D2E17F7B82DD739DA96AD4D97E1AF784D31BB5DC5103FAECE521483CB1DD7B62F786B0D47FB9C8E9668A62FE9EAAA6468F2C7D1A7295D5464429D3F74E955BBFDEC5EB956E0562186BF12E5B01124D2AC9BCD4BC1B4D6E9F8DE2AAE124B580BD161910F1DA271B156A366989D501E4901FD355E1933F36722DADE966C570F606DF09EB1E01D7C7724D40B6ED72CD622FC4362FF22D2AF85D1F0249911547124545C8B96A624CF88653009E2C3E9EAB34A6A3E9E6C36B495798DA74177945482A5E23687EB6F1CBFB1D03FBC2AAF0B97572CC3B4C5035BDD6AF58BF55E9B238F0D0A946218D1AC112DA5A24880841242D969E37CB49171D15E49AA97FFFFD3BEFAC0F03AC7A7A93B23315AD246F43C5805EEA13B194A815AEFCFD57677593FF99CEA0C2037834E73E84A4BB6635AA10FA29658A35E9E055AAAD4C4348DB7823786F852D865CDE73C261BBE476A867E6382A03416A2DCC1552E53350D03964F35DFF7923149FF28AE821AC9EAAA86D3F58B928887118F0BB3FC1993E68C5E874540208BE0EB202A8605CD96F15E3B4E9509AC6666E06997D870C874BEA4BE2132B428D66917EFAB83DC242F456291AF753603BDF98C7A73 + + [AES-192/GCM(12)] # Nist | Test Case 10 with 12 byte tag Key = feffe9928665731c6d6a8f9467308308feffe9928665731c @@ -177,6 +192,14 @@ Out = 5a8def2f0c9e53f1f75d7853659e2a20eeb2b22aafde6419a058ab4f6f746bf40fc0c3b780f244452da3ebf1c5d82cdea2418997200ef82e44ae7e3fa44a8266ee1c8eb0c8b5d4cf5ae9f19a +# Generated by OpenSSL 3.6 +Key = 19A99E2FFF05CB61BBAE87026950ACAD55CED1F91683F5BFF9F6FB70CEDD456E +Nonce = 09F94CD91331E3A96D8BD9D9 +AD = 99A5B053C5BA18F0C4F639C08AB0CEBD49892BD53ED39EA985BE9C430D49C848 +In = A6376826791CAEEC7D67BE46F84176E38D7969899D6AF642B9F2D1951C817E7F971E1DD911D414388725F2D82E209CF563225D65E2BF0A1B836EA47D6A80630AF55323F9F086DC7D235C98DD0D691871E650CD9BE6D57FFD2E9F67F847D25E45DF76C83F1FE4840D846A485F85E496547D0278B91E917B044B8C57719A02B4222660A293B99DE8AEF070C655F3E9DB3A60CD57A1BEEF28844C6F29FD141996F0A5AFEE71EC2BEC8956C51F514D8756C0FD945FDAF4FEFBF692B67404A5065C44CAE5107C5213472760ECB87602A16321353355DB669F44D29FBEC51CEFDCA6DE77AEB2FC115B33F022287CB52C031BFA9C55926FC4DA7EB30C14F515A79751C9B6FDE0C30E61F416B63247F707212BDAC8A331019CB13E740806FB477F22820E87400FB52024668480EB7347A9102C802B2DD4CA9B609945D505BE3AB7F85EA6A8213BE103E05AD7BE5B0DD973E6BDFD8B3E71B9E63B422E15890E628F86A14DD0A4C9ED7093CC355F1CFAD4AE441B925C7C5A14B6D38B8FBE9DE125348A2403EB80C4E3A1B2A8F90290DF79F58C5AD38698E79315F533AFA45AF5B880214A8EC03A9F1293C2D770493CC0B1080B5A92A9F9FD19C8ADBDF8973C2B3EF596599C8EECAC049C3AF0B536DD225C63714A1D4A6CDB4CA811706E90218D3F405ABA76C038976CE01A465F038262CF5A9DE3E50BA1DB3F4A0F54ECAB8D8DE8EE2C5B0E4AD6E18CB429FB9BC9463AF875E6FAB30B2A4BA63864DBA5ABABDADC8FB57B74CB8BC0E7CA2C130023D81C16C9E290260F7841AF7F01979524F8A9544D1E424AA1965EE1348D1C54586E74112271D040E682794C89304C64E6CBF3B6CA9CCA7BBC01100C6D2BE06E25D5A8A57D268A80783C8C70D5801917A5080E4090BEDFFB0D068002BC1DE2A32E65D24C434C179A0F50755887E05336DE7B3949B4A3F28D4175A639B9E84B2BF8775CE7C89215439A0A23475CC970959E341109B0070DA6A08EA4FCB5504AEB4C2E7E30447E1C0A4F1610B0AFC0F38A92FD91D5EE5BAF85AE5ACA43463F0D02119E58FD346EA8AFE3928430F21D2039E884657D63B94B5E463462F92BD292326DD1DCB1F3C4F7D1061BE972F0529AE916276A83449A3824F094D546B8ED51902CC8955A4F3E53ADC658C8477EA9C08B62A45C651442A4868114D35A4012CE02F6285BAB3EFE0F11346F3E8124173047920489105A3A789866318667843FCE4C1045AEEFA1A01F883A5854CE5B86AE6AA0FE78BE3E925C6A72842532FDE6D6CDBB285BEEDC4F0214BC44D2501149A3A8ED3FA4B65EB4FBC3403D4F49E947039ABCB7405712026962B621495C99B476F896CF50223A2B5F13FC96C1241CDBA67D0B26C95C58CB5288CBAEEA48981BDD24AC33BDB04616AD64CF71D99123F08F0CE25AA9A211BCEB745A57E6713BD6EF861F04DEFFD4F6CCFE +Out = FEFDFE105DBC0B168A4787000130EB0146F155594C095E19BE98831F9FCF30D5A6FAC8329A8D2F022D679E532AF727200459A39515BBF34397D0B0358258579BC12D04CD6A0A25366CB14893F26DFD0DBD4D6A23073FD86C3E253DCFE5F9AAD611536CD317749D9A0AF7B7A65B30589EAFA8278EB6F1DF53DE863B7AE782D517D145EA7E5A2184FAD34061E776CA4867582444E9EE42DD89B0001B0E6897537FFE147C34646B3DABEC83D85A2C7E1E86D0AEC1A4C710B253A157B83B1F82BA476BCDC0B745561799A651EBD64B9AB1ECCE37151CFB2BF7275ED966400BA5999D2EDE7BD56D8F65FE3D3091FD47F675AA5628E1F7D232ABC27E8A4F470C0C7E443EB71EC07B22951CB43F5B9CEC88EF210DC17D10BE422E5F7ED1CC05B0D4B3A5A30B25F384B51D525E43F202FE28173D69B6639BEDB2F4745EC544F25716AC150E9CB6C2ED595EF5E163D55646021BC67799BEA578EDAF4A843ACDCEEDE544B052BA76FD8AB1811DD422E35B569007EE198673431CB00BF664D55900BDA808C3C24DE4C7AA87169B35B400EACD35CD97866A457BC2D2C36F4F51B76C5F6D72547EEC24A19601CE9C66DE9402A54896109D681B767301EE03A919AC1317BB0C027DC3EEB4015583C1CBB29F42ABC475ECF2A33D768379B99396CD2B5DBCEADABAC27F46EB03029A5F75BF7663685A5274B7866709275879DB848D1FFD3A4EF1945E192E61DA4348AD3BDB624D378E8C1CB7805E0201F64D5D68C765DC76D33C1E64F70C0A2CEE78F3775E3E4EBBD8E4B4628712AF6CCC13FB9FE8D3120FBECCD8629929123B49036B579A584F450835852997F4EBBABF08F2CFCB4B63A4CA1549DB17F4F1B4179E5FDB1D6D0F59200AC954C9FC9E580ADE776B994D1EDB09F9889F24FB98149A33174294CF1DBB3779964E47F6440986F9D7960F75143667F0A62123818CC95296F187FA1A9E05736F4CE7CAF300DE638E0E0FCDF4703EBBEE52189F4CF788867D5F96A578DC57447043A1C2126423035324E6393B5C5ED669768E9D111887B8BA9743A2A5CE64010A028D8AD0A5C8E38411137665F3B0E5BC310286E6F6625C87D92B510C713B365EFD310576E8208BE22ED4B4795AAB858CBAAF557F571A8021956EFD98248F35CD229FE4A599B1FA257FE4D2A89E9A41C4EC1986E95785AA0A64D5B5D23AE7DA3AEBF917B5A3D0F9DDDB7744E3497351AAF108F437E24C880FE0D83BFE289FE75B86756578CEBC2D9F7A50F0074D94483ACD9389B7F60BB7450EF1AD607B81EECF37D2A17EED44579392DE18E60F53A4E375DBDA4EDE6A627E791CD21DF24BC393D0173B75A999F56806B203AB22A5315A45D8F122D0A5AD763658A32D5E912BB8C5C92F66EF2BEB72D9BDB330EEBF30726A42415F0D43CC26CC42BD868D24F43CC186E7B4F2A4A321AA506B5C7B097F588F7352D7EFCA595E4DFD74900233BF61F9 + + [AES-256/GCM(12)] # Nist | Test Case 16 with 12 byte tag Key = feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308 @@ -306,6 +329,14 @@ In = 00000000000000000000000000000000000000000000000000000000000000000000000000000000 Out = 0cf6ae47156b14dce03c8a07a2e172b1127af9b39ecdfc57bb11a2847c7c2d3d8f938f40f877e0c4f145c2dcaf339eede427be934357eac0 +# Generated by OpenSSL 3.6 +Key = 0A5A12D88AC469295D2C48230910E3CB +Nonce = D80C3E1F795505C8EAB1C314 +AD = 8BE9E1D446EC80E17282F11B4EF4D5ACE38B6FC1D3E522CC4DCDDB250C32DC64 +In = 82CF34F17A2FC86B31DDC3C10AA02EA4FA51FC0D5DD01B7DECF8A7EDBB4C790E6756BBD2652A595CD29A3B11052C3F2BC01FF84ECE541FCFA766546EA7CCDA01053D7F1C0678296791CB1207730FCCE2F81859EE8F870AB5C468388DC90E664E186C471EB5677E77592775F1BCFEE7D9D26448E3F6392D377C65DB5EC4298B0EB646769895626D445AC86789DD6E94314FBF0DD74F45C830B290629BFB362958924F388B66C30775C96099F30A7F221D7B1D20E1700E3F81C0FC80B4D74032C956762C723DFD35375BE66257281872BEC4D5B919E46CCF74ABA4C4D10ACF851D9121F30A5F95FE038E086E1B0B12EE65E2A54213E0A43C2F5C103DB4DE620E28CDD03084EA1C3DED40192E5F54AC49BC7F7737CB4BCFD228664F6620E082911EC3850B074442B93C4B7A49E56F3D4CCAA7D407A4E4D563D0D94781FB5CEF9FB801433CAC2C134F4054C8478EF4314768B49409B35BBAE7BCA1B6D45E6D8FA485891E5F9B91748BA02F08564F2CD72A442E67A2533DCB6E32A5219618C2ADCCF3E61FBB5FC45C3E3563A5319961AC63B94FE3273EFBCD04AE71371A27D0D86A81ED00270E9B9062DE8B01F52ED797BEC7A398C50FCD217008B4875AACBA15F3BF47C8542FE5C1B494D046A73142BB59F0B868FA776E33031B5EB1387339BD12DB33026CBB2E538A2DDC792CEE4D3E1A3C0E95CA4EF5618C084ACCDD1A19455D608ACDFD9398267D8D289FDAE9148D5E7BD7EA7DD334E2A9588EE996B9D7BE01901DE2C24F791016B380F833226DA5E927553A0A437C99B2B5982F77B011978F25031D7F5425E6E46AAB522C61560960653FC0263CF6D806FE4A06599928FD508B36F6ACD288DFEC6F202172219C8668C46E7E547210D0494E5C931A2D2683E1A10A2D00F4CFBB506B562E4883AFE165D93E4D34AFD611B9A67CDF6F83C86A059FA4C8FECF0C2F0C0B079EBC67C4CEA61B063216CD4E1B9A456112D4B8AC456592852524D3888BF6EEA3E105A4121C065DFF1D11209479F395C3D6B21C304DBBE76CC4DEEB586195BEA6E704FF2BB46322F74739997D20A26852DBB95244794C03A093CF247043A8D8A5ED29B87CEED080FB9EF8E64035471B2C08A1FE0FB2D4106C2717BBA4AC8525BA15CD4576E95C812F3E63B0432AAF6BB70D791F303DB83FBEEFE63D21CC09F10C50F2ADAA662B9637DACEF4F612E3ADF087D02A0D564C7FEB064E62BA56349ACE78BD9073D34EC1B5A6C8C5D34629E999702794AF86A32D2B88A04D2ECA5F184A9208B596025C7BCEB7F75EAE051B1308B96182923FC7FF5F0120840A70E7D2C67FCA17978AB7B78B17FFFF7279D411BCDECACDBFB4AE451F64D5D90FDDB49EFB3618A522CD53FDE7A04BA1C082233E46AB02369137A133502D686BD7E4E3E99B695B39CE0548448DE7B464BBFCB459C6326A51640AD1BD +Out = C126B718C010E4B31EF357828074CC0F5928F71AF84F93E7672324247D3488D84E4756FB524084F8CA5840D21507B67971E4A3E041798D3B2FB3FF9D0B5AF7035D96799616B4EAF082F5E6B3D18858AE9BBDB356A0AF1AB40AF4F730CA3BA62BDA7CCDA53C02F07DC4767A56FD55298DE2FA0FB778F424B4E622A6DEE4BF332FAB0C56D67CFE233BBEAB6C2D9B48876A7CA906592EB1680131C4579AADC4D6DEAA7863E36AF82D52EFBD2FDA9D7FDE1B3E71E8DB99EDA8ABC47D0968EF54C8873EAE1711D61646A5516E5F4679D6F1109F7A80244C91AA8D7D082240C0A55CA29AA1F2217CECA68B7B017F1527F01458FFB1E8788F00DAD04F4A09D3748C065D49193FEB6DA85ABA5743EF472B6726BE4C30DB9A90B8EFB5FCBAA68F9B1330D07088A4BA2CB42C789D2AA0F6A969B0455480197169EDD88366CA8725F2A4BB176420ADD2D8DBBCC2AE8DD158571CD79BD1749590C40679429F2F9F8605FF25908009A3376CFD043449B603A95DFA1659241D9816202FA59F45E47F04FD2DC95C633095AC873C4210C3383EC3D4DD8AF79C986F9CCC877ED31C30549B1A6E29FFA44618E254103C3CBF12A33E8735EB711C32B6840774D931AD8B799B0CD79B59FCBF251FCD1967C7B1B5FE7709604F7B603CF987878624737DF7A6EAA3B51E6A7990410D4C53A424F2E74F2E20588BEBF85034FA73DF5AA0B125B7D69C92D460F7725FD9E0CA86B6E108F505349613F3D5CC43A0AD5B72E209DACDDC8E6016C8719354737A6E55A35FBC71918349D6FBC39197D9F39D2410803E66273658918A96D89D7BB92732B89A4193461ACDD3F89E8D70CD447AB55FFA19F02C60960D063816FA8189B7904184A08B9CE69ACE85C7154683F2CF24CF400045D6CFB142267FD7B77D5D6BE26441244AD8FDDE455334181A59D400FD5C859B2C8BA4E7599A8CA36CFECD9187EA2360A37E4BFEB523C7C1FB49C9E2B46265B03411D649EFB3B2885C18DBF68DCB25C17A8C808EC321AC64C0F940000CFF82508F2E55E1B90E6BC391CF8C83B7A9792A72FCC4C125FC364B94D6071C71A62479DF7AA4CF4D676F46FBEC9C68AF129D01D3EF36463AE75964170FAD9AC4CB1DE0DE93E3325F7DECBC46A001D0E67C46295E1383E0DD1BBC2D9FC8A8E0B99B48FCA47D7D290ABFE066009C05F18C87EA0258EA8B2C935B0AEE95AFA859CBE830F00853101A48C53F96F6B319F128648578EB5C872DAB9765F1284987078BAE8BEC005B0E070053F92404B8F364B883EF09D5A12D41AD8D62EF64D4E434DB61C6F3B0646278D83844F8E7520C11255EBCF68D73391292F7C3D13451B9EF1EEFD1B5391440721220BDD2367C505D97E7ED8E94B63DE5DCEBB9D7A8A01AD9EAC760C6685AE30BF94F79C9DBB230D61CFE4D6A17762419C497DD6D00E61918E776B61FFE440EF7E66A108313907D3F552A837167B0B0FA37A2 + + [ARIA-128/GCM] # from draft-ietf-avtcore-aria-srtp-10 @@ -315,6 +346,21 @@ In = f57af5fd4ae19562976ec57a5a7ad55a5af5c5e5c5fdf5c55ad57a4a7272d57262e9729566ed66e97ac54a4a5a7ad5e15ae5fdd5fd5ac5d56ae56ad5c572d54ae54ac55a956afd6aed5a4ac562957a9516991691d572fd14e97ae962ed7a9f4a955af572e162f57a956666e17ae1f54a95f566d54a66e16e4afd6a9f7ae1c5c55ae5d56afde916c5e94a6ec56695e14afde1148416e94ad57ac5146ed59d1cc5 Out = 4d8a9a0675550c704b17d8c9ddc81a5cd6f7da34f2fe1b3db7cb3dfb9697102ea0f3c1fc2dbc873d44bceeae8e4442974ba21ff6789d3272613fb9631a7cf3f14bacbeb421633a90ffbe58c2fa6bdca534f10d0de0502ce1d531b6336e58878278531e5c22bc6c85bbd784d78d9e680aa19031aaf89101d669d7a3965c1f7e16229d7463e0535f4e253f5d18187d40b8ae0f564bd970b5e7e2adfb211e89a9535abace3f37f5a736f4be984bbffbedc1 +# Generated by OpenSSL 3.6 +Key = 03476061769DB0F96932B51624E7E4F9 +Nonce = 1DB5D4047156623EE98018ED +AD = 16639F776A45E78A7A1BDC732B0161AC91A4565CACA5ACC7F16D414C9BA04303 +In = 883BC20E3BB2F4FCD566EFA1487055444AE0696D1C48E73B0A1004EDE96E7301325BA49A111006BA0D16E96E19555AA0E4EF14C079E3FD75DC3C9251C389A5A1A3F9377ACE9595317C906DCD6248E2BF91478B546E4BEBCF5C94B687D3022B89C31B3844151A99FC779B1DF6C6D3360F5DE5586EC34F8593E3B23133D0A3F18E700E4F3B6AB6B79280F0ADF5E53DAFA2B63F67F647059A5897BBB8C6A5567176D4910518D0B43349D2048726E74D545493C664CDEFE1559BAB3E6F2FFCDC14304C43487C35874B0F87FCBA0273004ED1A668F09E1700CD64F197D7EC9C12FCAAC8C30DEB04CBBD4F99CD62A9D9E189DC54F6ABFBC25492097BB315DC77E3697ACFC32188ED7555A2D0F5082ED58D56D905DD94FDB74982A1149C15927B84936B3673A6BB94D729715E889427109F5B1340FE465A1C1D988E46DBA31737F680457066EE2380CB76CC05A270ACF26D6B2F800524608FD6A7E4DFAD6C3EE728D674E976D1E5DA628574D7099B795301AF4FED05D5ABEE75B50D98EE01CD957C0B81AD7F713C39481B248BBCC10F3FA38D3FF9E952391798CC79569C9D67B4CDD0D78D147816DECA25FCF6C8A9A0D24B4E54EE38C3F5346549D10F199E23969071016433B491AD352527C968BF98E602DE461618C3316E36A8AF9730C0A36E0521DC8C9D335B5B51EAEC71CFFDBCA921FA80A63D9A4EB83556730FC38779563A355389A7353895931A8C03E6EAB3AA2E86EBC4658C924C0710708A438AB9AEF3FD9CC332E0EE5F8ABB223CA87A0B9013D9B4D1CD891BCD0C9D6D8A5568F162AC145FB76E7F4C76AE609D2776AE7180BB312F3729C004492AF18E4B1CC47B628944CE16799C0718BEF95AD2AD6D343A71BA7F8B3A0B4AC84450CA55F4615CEF0396126CCAA1D75D095128B6A356FCB87A1F017DC793A2BB69B9A9FFE09A3C9D536DC9770E767C5F574847A47523297594EB85757E743A67BB663DCF22F6527795F92D149FE067CD95628129C79B0DEC93F293A4B611B22AC207C136547304B52D9DD40461BB10B45F4417FE697466C5253A03162C95983642E16C8C83943B2EB79C1A716DD70945298B6B1EC83BC52ABC60D813E32D47C76150C65D282B1E38720DA6E7E17ACCB1268B040BCEF07A2ACA333B7F19D0306E50E40CD6ABF3FF6B094F71CAFA26EFE37251B1AC59CA127CEB501FBDC745C482EE6FA17FBC8AC225D016D21C7C51C18EADDF6DD7080AE0CAEFCF39B2B2A06B0CB940AAC0890C816498E4A7D5BB9A18D1C23834A9A48515B07E7882B96813EA6C315514512F76C0EE218CF865FA55808347D6676E3E8C51865EFC1F0CFBCB804EBD5BC0A35C73037A3F9DD1CF8A75F4418E18D6E99F9981CCEC7DE982D22AA4A4588994D48C9FD218A37E4941E749697C1B97DE66190B348DB94FC0466D483FF28729B4834B79E2A5227A7C +Out = 13B159446AB2C6ECE6A63102767B1FC6A0836513D7A91213D84D3FDBC3FA1738BAF72CEB8FEEC0DEBADF1ED8D624420904632609B7F9FDDAFB384033D849F0F538910E7B0210D9DCCE321D9D2081B8AABB3E9F4FF75893EB527BBC104660623C1424FBFB140F0ADF2EE0E9A649094EA5DE050F622F9478BF5A65A22EB0512F03606EEBE2837155A8B740BE5723DB0FBFF342604961319C5C2D0F4762439EECCDA78D72FA4FEEBED9AD1627D73E0D9B4EFE8915BF56D361E38BDD92B4D6110F3900D6DC6EE5A31CC3548350DD5BA29DB15B857846BF5D25D722EF34FFA6BCD27952E1FBBAA9A64E6FCB0F3E84765CD29B25140A5D8B178A0D27CE95E49382B945FBEF22D5F269B45B6D912B59AC1D4D853F28F01293912FF5C3FAE0A1F12EC5AAAC422D17F06C6DA28EB0B923BF18E3BAC13C33E64CB7D55E2A269DBD025826A4ACAAE6B4AB6DFDE680AB7DF807DCB3F05C1F2AC8BC6BDBC07AD459F085C434AB7C480743BCCD62927DFC3A327B380806D365C3115B165F917A69C72423B2944B08676D06FDDA790525AB1DAA028918C6E67941E2E8113FAAE173F3E689790383600CCBAAFF28D270580778C77ED33F56912220809B2B1D037392B67B65D1EFFEFECABF0813266778BC6C42C374B5B72A08B8965CD324B75201996B0F1FEBBAB0D0DB65225FBB3FDDE619CB0264AE5477A98112A0C9D271E04D19C8004351AEE1326EB51E7F4C3149322AA8CF4EBE9449FCFA444DCBBEF96A4CE7448999E64015B129F5C339224D6B13FDF5B2C21219F4D453B76CD7FDE9EE72F865FD8EF55AB5D61E412508E974273BCD6046381798E03AD51B91FF49776D998A12DCDFF944CB14BB5FF2F05293C690BAF8E73CDDAE229B663913F458E78989F019AD260A8A81445300384F73A6F891514CEFBBB64302284F014D3991A67BB8A0A88B15FAC2035149332009E73CBAE7154D22F8CA6BFDCA1F04D22860BCAF01532AF3FAEB30518317D9EFA2C1400DFA6E0A22816A8AF8B8C7EA3FBB263515864E9733AEF3419CD47C46F4A3ED959E5E2C85044334CE212D0CFDDB8FCA680B481C83166E825D530BAA789F2F50B0010A2443FF01EFDB3E52ABC284CD3407B281E981F247E12F5F3BD24C8BF9664F0200C7DD7AE6CFD6005D513EA24ED5DC8FC9717DB9B4EA5035F2E674F8080F128E08551622EE9597C16BE0012638EB4EFB50C19FC4B58CCFFFDB8743F5157D0938975DD1E796BBFFB098742ED8FA74CF43F0818C9A7943EBC438F0842CAD104124720D6DDCED32C101B4E0A695E43FA466AABFD41E170C4A289093DF24A3DABAB50D352613A1C6058C461F6E98DA461C3EFC887E7AC9D0E2B7E704E094C5182C78573C80CB98EE6A3A00B6A17053EED658548E89330023337F3E1351FE0830104503E93C6750042F2C54864BFF987697589D148501A65884E12F4075D2A7FE60A867F1EF6882A592E2 + +[ARIA-192/GCM] +# Generated by OpenSSL 3.6 +Key = 5F75E4C37A3A72E359152EAE62E897A64BB5274E7F199CCE +Nonce = 96D7A06E98CEEC1FF0DC3AA9 +AD = 66E3724A2DAE6547BD09956FDC47A26C02C3EEDE8F432788204CADFCD6DE317E +In = 5DE8AB4175BD45AD3A7752615095B53245122CEDA22D355B6BD9A34793DD23F7DE21B9DEBDCBDD315847BF9166CF4435105A8A21EA0D51F901F3A525A4A27A284F5B2C32FA014643EA91B2FED64A0E8A25B606D5C070B467385A5D3AB3BDABF06F014D4C6D736F7A7D27997F5B7118F408D4FE8ADBCD667F30EACD867EA7415D2CB979742872839BB2FF57D5D2FB1A56C6F7964D2847D8C0C5FD24ED4DB60F5FB38D37E7328E662380F2DC726BEF928C099CBAAF5B4E7B28BA8F2F8CFFF3FC218B9764B77F3048A20B8FA9B66E089C3A2829CE3E844DE41C7F61DB15EA60D22F0BBA44B021D4B04C1B474CCC17BECD2D2D43DB3F0CFB2F0A66CFD6C65DD5EB2DF015DFCAF9B6D201489D6B912F9BDCADC60D3F7DAA804B72DD218DA0EE84ED4A539957CD90920FBD5486DA76A62415AF40507235FE6EDD8AF50E566231C291E34F61B0014C6A521815A218F8D78E45FD30927807E289BD71075DF894847E6F7799171B94A126C9459CD9EA3E3104BCDEC75AD46F0801FE5AE9664B0BF25C504C07BFCCC7B3507670F5F78B58132B0B237DDDE51A8C1C9B20748AABA25DC1D2415D7952AF360E51A6E4F1AF5C2DA64ECBA7336AF398574967298AEE2F298FB71D4714648C1AD0FFC50CAB645C3BD52079AAAD1A830A6EFCF911801105FC461303A7A34B110B73AE8689BBCA6B4EF0EFF0519D7137D8569B30C2FEC291E0DF24999E86C50240C4C3AD92B61810D34FE7C173E8475D63BCE7AD256E3D8A81653221D66C2B915A87ED3238A50EA0039B378B02473F5F9DC7DFA30FDEAD3A2B3F716381B9C26A6BDCB5B474A9B16894572B58008EB434DB983F9FDB8D214C264E5572BD57E5F22D8B2448DF62DD17FBD24F4216007F1835E13CBD0A5A15D2C7ADE1067E12DD37C05A8072BCD2188791807725CA1CBE4C0E7EFCADB31FBF9CCB223FCF161B515CC47E48388D13B7615E792DA315FD1DCDB0DD9BDDF2618DF6B89395B48509E3E1777EE5D9B29EA7D5BD6A33EF1C61973C1F3C44C7BCBC5D068857A9E66AD75801DBEC0C391D724F6567620E32E2AD20F4D63FE0D69E1710EABE8A063C5B150E931F6346E4909BA73F673A08D360D082417B4C261C74FFF3F346E74FC1074E60F1A817F69309BF2889FD75ADD961AADE35AB817FBBF8206287D60AC148D9B3F076A6B1997CA408B42448F715B20B91227DD363CC85B51DF648B728A69E793E62CD04DB5A0C8F4D9ECACC8F6C8ABD0775E81642062D79AAD1DEC951668D83CD670169C37E2DEDC767DFD5A4D3EA0501AC7EA89E050A593052446E39B924BBD79EE50BBC814BADBC292550EB7512A9CE39E65EE69844BFE75DF857B63C96000412A7EFF21BD00CC2D77FE21BCF5E28872CA11A957DCC407EA7B30A0F6D10DEDE4DBEA5D994C6004CA54D83466F57CBEE200B44CA68C4E39EBE453106566E +Out = 71CAEEA2518AD25F9201F0D40282CDEEBAEF2258BDFBB52427551F2755B6DD3DE3E17F27A0C15B09EFCA3DBF10D2F08A9FB94AA2BBF7C5BF7CDE42ED6439D31AEF03636ADB0C70241E5EFA727CA2807DD7F3BD05A955575E59683B4C50292063353356A807B1F2268C7C68D86C5A76ECF420E2326B427435E45A565692293B23E50C8CE5AD2F8DBA45D3A12B7D9C2666BFD43C9A5CAEF57E8EE71EB18F52BDE8F4DD00997D8E3C35B5BC8239F2AD44CA34559817A3406EBA0B65F33272B74ABF147C4FFE1490F7190FDE41A337DADCA8CC1B430879CAB1AB2E6F0EBACB5D2F54AF08FD1B4C8EE4E2569817EBA82F8963C288712BB93008744B9A9797692F567C39C7156126744C796417020CFDA9A795D95F182A05F5B89ECB00768B5F349E498113E0771C1D44E46A6E22A29290C2E36540C7F81B39EBB3067FBCF8BD65D09B42B643343380850E23CC6D13E051A4B97DE7DFFD7C557D5993FCEC2D62FEC6E78F22980896F627256212D804ECACBC69614ADF5DC03641EA3B85621D5536A5DC6949B4171F28E47F8EA0B92AD4CC8D52D21353EFC93D284803779AB5EA8DCA6E0CF6478A4274930ADC756EFF73079564DA8C7535423DA8053CAF32F24C9AD0F0C8C83A3C4E0F276074AC8A5797BDF71F0B3D87D60C991A9D34080F81EBD41357F777BBB8F6D4039D13E315030C4E723A0E0FA0BE464EDBFAA6E8C58A229B6B65B7405820B86C9AC72749F94665C1F60C631F17914D4401030AB86612753947A529DD80C0734B2B7C64F34FC150BBE650621951DF7AF52941C959CF213E41D0E7E13FA2B294F435DC905A3D9CBFF281B67D211F3E5FF1CEEA934A3475FADB2ED6BFB1DCED70F2B43C3474BFC21C25F5F1A1810FA64A280AB4C289EF4A592447CCAE98CE69C8510BD9CC53B04EB83E978FE60542B3B00740E20CE065EEE169C3D57D947709713772B75A84D712F1FF5F6B96EC89B6CAD15586E0CE66CC265EF89DABC1B902F78E4FB9E6241097EC3A8241A3EC6D07D7801A6C6C8CEAFC4CC71778BBAA03D2CCAFFB93A6BF516C410052ABD32405ADCD8B06AD8CB410B2FC738203E9EE94A8110481AB658567779E5C31C886D461B28DE3F4BB430BCA1671A0D79451FCA56230D52D3D3002BB98D25F501E330082756845884911F160EEB823A847151C4A298326827770859A263AE25425415EFE81726FF99D67A31296BB3593200E0627046DFE0CC3AE7F6A3190526D445028B0FC4ADF207F1ABE2943ABC42A10C293168E61FDF1CC56E8A4D3E6F362169365A7A50024F4085A696C48F18838711EBF53A7BAA67BBF9FBF5C2BA0E39126779D2E7C711433D66C872272FD7E40F5A1DF2B345E4FA6E4CCE83A86FD49134E2D5C78EC21394C1D7102BC16CC08BF25FE06A725F8618A8BA2B98AE2151B25A6EFC82D26F23BAA0B2FB84B471F0DEBECF32F44F13AF4781A001A26583587BFB8 + [ARIA-256/GCM] Key = 0c5ffd37a11edc42c325287fc0604f2e3e8cd5671a00fe3216aa5eb105783b54 Nonce = 000020e8f5eb00000000315e @@ -322,6 +368,14 @@ In = f57af5fd4ae19562976ec57a5a7ad55a5af5c5e5c5fdf5c55ad57a4a7272d57262e9729566ed66e97ac54a4a5a7ad5e15ae5fdd5fd5ac5d56ae56ad5c572d54ae54ac55a956afd6aed5a4ac562957a9516991691d572fd14e97ae962ed7a9f4a955af572e162f57a956666e17ae1f54a95f566d54a66e16e4afd6a9f7ae1c5c55ae5d56afde916c5e94a6ec56695e14afde1148416e94ad57ac5146ed59d1cc5 Out = 6f9e4bcbc8c85fc0128fb1e4a0a20cb9932ff74581f54fc013dd054b19f99371425b352d97d3f337b90b63d1b082adeeea9d2d7391897d591b985e55fb50cb5350cf7d38dc27dda127c078a149c8eb98083d66363a46e3726af217d3a00275ad5bf772c7610ea4c23006878f0ee69a8397703169a419303f40b72e4573714d19e2697df61e7c7252e5abc6bade876ac4961bfac4d5e867afca351a48aed52822e210d6ced2cf430ff841472915e7ef48 +# Generated by OpenSSL 3.6 +Key = 4881D3F0445E7544F00C2ED125B071554B789D3A427FEC3EE9CC706288F33F0B +Nonce = 13439629165FC03738587AA1 +AD = 67AAF9C6199BA8BCD294D06F8CC0DD5D5143A91657117011931C72D6C7911BD8 +In = 1F3109F6A929346FAA12761B1B2805551F61E9505A6D60000B5A4A3E4866B4CA288D9C17B74B463EB0F941FB818717617A48B2A601BE72F2F58692B5482E9E56597E25B663BE937E7F39CBD33754302BB8FF95319D15B55A459BB6101E55E76DF1112D51F7B7D68762E446D8D89F4CB1D134D1B66CEAA49649BDFA63BE9A5B474CE531B3B901A52293FA359E82585B3D0682DEFC93754E05315BCB9F8EBF41491266191EFCF2BD6B70AA0DD5F3387A2378F4B3BAEDAC43CD7D7CA131F7209113056BF974772F9683F2CCE5FD12DA016D877DD035ECD35C0B7BD0D5789D086F152017708BCB4BEAC05551E0FBBEF24151A53157164165C033880E1F2E0AA7D6BCDFB0889C11A9590A13FDEF62886953DB454468BF9E1D4E5E681D344859215ABB6CC95087EC0EDA57DC755BEF441D041882A5FAB5A80C2DC84B58C05BF9265DC8F187A7C319917181CBBB299B662180DD6839E8B85DC1886FC2F6878DF7C0B9E1350AF8B079268CF70B7FD0C2FA1734963D6187B549BC52167416B8C4FAC2CA5484E7D29A1C416F25D3510D5ED21D7CF6B1767282E7B51A6730A8DDFCDAFC308DA24C44F988951592BC9DFC5014C41E127B0F765C8E8EFEA99362ED8A5BD4A910F68097AB59B99CAC2E1B1CF8FFB2D2D57C003A6760D35087E12356D26253F66F850610C2F7638BA8A9C84E73867CEB50734CBFDD77D0E3E90E9EEFD466F4AAEA9C6AF6208913FA4DE7D06BE6CFDAB4267BF7549CAC4415ECF442F2496865AE25BB82EAC4952E8E21B3FEEC7E0A5040E23962DA8C7815306EEF33BE37B069E55E0B2DA254C73AA9D5989B4FCCEFF6F5BF29D06F4690D1FE116BE1C61B7C6ED647EB7D6E29D64AF2423F6EB0C686F3CE7945B0EF46FCFD25EBD4A67D2D8C3F31295E61C84957589CB1ED34FFC67C0BEF5C672880FF6196FFD5C60EEC2ED710470A99B59C114F1AAAC4F61084C204B37A88CF7214F72144B837A22543F577C8AA5E9903DD610E47BF17EDA9DE4E2EDB1F364260588531266CABE86E783B64341A638C223E6A6F0687A7F9FC9C10810F08C048E14C7D6351D26824ED789FFAE3A655AAFB04649744726744A4B446E2ED650779C2618F80B3046ECFC593212E1380D88703CFC7AF806507BB652467EAF639B9EB1E81F66AB005D3CAC2DFB254D69498A858F0EA5A3D11521FA48AE877434CE1DFCFB869DE5E643EE85335DAC16450947B95470A2AF1DE5BE80B13A30A02D4276356D3EA0AB335198665045545A5EB02FA2D6950BFFFD49DE0DCB485BB1FC94E346C4AD31B368461A7DCCAF9153708F50F7CFB3A696ACF038BC92F73330E3D5E89833CFDDF1A444391FE1B5B938897F9B2E1DF2C96B0332C907C1367EC26A06935F83DE3DDECBE5B9303FADCBF93436252F8E25FEC1D1D5407FB7AFD7F9A4791C0C0F2AD3D7DAE8420D01567F0E42753 +Out = 611C85ECCB080B211DC60ED471827B8E8FF10E3CF1AA171584BCDAB2CEAD5490728039A960D02A99C13896BE9A44C7219D64D2EC46053EA33A07491D70F8624CEF54211435B47B4819336C590C285E3C47993B8CBD6B40FE2691F9E26CB5D4B4509E90A7E087F7CAA12C38F9218441249D74988217684E6FD5808FFF620F4799158D144DB33FDA93FC0C21F11AB544E465D563B05CAA19878FB11ECD0C5F32343309198BA93CFE31DD22900E0E061377985E5FDCF00248354C98D15C78563951EA677DEC8C14F0F20786D5202795AFC2D64085D64A77D4B3B1181C85B9E6A416C5AE5F885F1A6E86573E8C8CFA2441F761AA4B7540E4B823FCF90D217D90246991ACD7D81D401B8C8E3020761458B26E210059366A292FF4BD128EFC1EBED6B5E5A9379664F8AA6360D1AB49CDFE08024EDEE902194C0158CC9C6E2A9387373C820488BA1C4E91C4CE504EA00F272F6767D74A21F739FFA295661461417D629C473D6017D208F79DDAECB8F9F4B0E586A034C02CB1AF03F7628A8FF478DD3AE3CA35AB79BE794BB20DC1788C95D66B76DAC68B41077F2C1DE826BDBFAB042887D34900318F5A29C4E32B7F9DE8DA0CF191FFCCACC8E233EABDFDBD7D16B3E69F943A588BE7880328C7A01247AE8F5880923AA99F99E1EDD19B5F279E6F298E28FA777D19B406333668787AE202027D2C057B3D11B722A2B533A7F7D28D62DB108AD1EF01D17B503882A36B85732745FCC29017675EEEBDAA3C80B26B6B412EED09E6FD66E8D3D1D98B5AF01352C34D1C351FBA91B530E162460D8452DC640085CAD3875D9D63287D668B0026D9A9898CFBD5B3DB9CE1979F03F20DC5222DA70A2C92D67D50E5AC354826C71FF8D03A4F5D51D8A3F01F3B61B08FAE315491E3FCD434BE17CE0A150B8C2E0FF87B0D46750B7584E7222DFBF9D9FA400FEED729F37BE76984FBDD554E97414B3C15337C1FF98727B2B77212ECA756FD231414521DC2FFAFE120CE82E61E0E7FCEB4E6BC217EBF255CD25AE8FB2AB085601766FBF3BAC9005F018F1D81910B1D736C1A1A49129BF7B1516CC53F12C066B8A8207359377D5B9D0A04B0C38D5E7104B2A40EFCAD922905A5E65DD2907CCAD037B06797397404A5AAF7137C718F6976540596C58C991FA8E794AE79F2FB54EB08CD47DD2177A8D82F73FCAEB5B5AA0DA4160B3A9E39A26067382F1DEE44657DD419814F54F76AB4BF1BD448C1FF94601829C2FDF1ADDDD1E27228845CC581EC5B35B4AC92A6E45E8CB64948CB1138B6E256059138F916D1175127A8497D3D4B50286E123D04B847D9799368BDACAAA3406BE0376ACB3202388CBBF3131CA261723BFB7C5AE156E6A3FF632BCF015CA12B31233583F95241CD7EC06960F854D9470DA0AB4C4587CF0D8201767AC1EE13849CA1253ED0CF818D7E5A8ACFC8177CC45859513038CD61FCFE35A1DC8CAD12D6F129E6 + + [SM4/GCM] # From RFC 8998 @@ -331,3 +385,18 @@ AD = FEEDFACEDEADBEEFFEEDFACEDEADBEEFABADDAD2 In = AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAA Out = 17F399F08C67D5EE19D0DC9969C4BB7D5FD46FD3756489069157B282BB200735D82710CA5C22F0CCFA7CBF93D496AC15A56834CBCF98C397B4024A2691233B8D83DE3541E4C2B58177E065A9BF7B62EC + +# Random test case generated by OpenSSL + +Nonce = 11f527d9efd098de496c72b2 +Key = 31d24bae5280a8500101c0234d20a6f0 +AD = f2ff0e943becf55d73b9ac5a91914a14921fae1364d622853adf0149a73232bc +In = 23216854d7215abfd296d909c33af08567a00ac19a2fc06e823c0f6fd4d19a80f26bd235e1b5351972b41a43c0d95bb04bb8f9d6aa98f7b0148d874b105156e07ed99229e088412a98becf5f6f73d9d8b7913f7c9a692151d8e3d09b613364616dc10b1ad13442075550591c5d22b3325224bc7726ee0f14ddfff66d2c0571e1486b12e20414bca95993200da5e9e46d70d8f068ec81ae5da8bd88702572fb2446b7aeb832fd2714778c8d734de8f6d9bd38bdeeeb38680db563881fee4e5f7e80e34e9ea33db3324a2249292b8f08cefddffd9acd28cf827fc64c005770b7298ee5bc36ab2daa92a7c4d0cf1210e6321d71b3d604b64f88ebcc01b8ed24f2b19b29a1b8da293868f74dae7fb9256b1d0624edfa9c70177f470e9bc8d747005b7cdb09e302859dae0e0ae6b0b3823a78dedad01579ccf58b380d3ea89788ceaeaad7e7b5973b6b66739242278b3697700fd6079e03ba041afe8ae3cf316864fa8fbbe80b470959c36b408ed2ab441e653feb8b9f42f104cc9c535fb2fc063efd23d01466efd32cb4ade7593704ed1a19e263a76c0b7c32b70cde3e865ddc3c55553d156e38825d82a17ad12f599a7df3de541fe6adbfc7510436a93cace99545861d4747c00e848e915e79c22dc5ae4ec1c45cbed85f920187cd26a75b4cd41dc9ef3a8ea4797db2191926d7aefd02cc5084b526264497c7a0d830e828cedb51dca8132fd6ae292a5b3599edf69edeb016 +Out = 7abac91c362d457124b810af1f53f2b611587860df22797f8ed71b55bea3bbd9b55247cf957aed766f463e73da9bd7dff1b898d207efc0720cefcd9ae8910fb86111fa118925610b97137ba95e9b8523f6f6404329d8372c799336f54d8036db36631a55e4760836f80bce6b6d0312e56001168c076c5e1024b456df46a1d4caeda7063ce4a7153e543c591e9361dbcfd24264d1efd6ac822bc35db7c453bae22b8de85d31e2b7f3b86d1d18642f6928f32c0c92a36d9ea15489c5bf32fa63a92f5231f249df627fd4823fe9185fbd2538239147111fa788f799724a2c30f23c2795ef8f63ffa5b5c16e348cad3bcd2ffbbc0a46790182b3a5cd5df51e4a7cb149ae230eed1228e27ad8761eed363680c559ff13c2f7927f4fe9e992d83161860dd4cd52009fb42d1555d0cfd9a521e64bae0dff4a07ab2e2feda0f7fdcbd49eb446e875466e3184ef39514cdb76a2bf6e1de7f788153e2b5dcec97f43fc7386c1ab7074f70481e3b860729cb6ddc2939e8e52053238e3ffb1355903257d2971cb8477659c4d8b0b373686948de1dc922affab43899ed865189307ce651ded2fdbdc09e4c2040567af7dca773c8196dc077dbcbf823e16bf19d3c3cbea142f1e6bc2fc013fced0a59e5720afbcd267ffc73d839e3b1c0c7c3c56f9099ae3e06c2dcc42a3aa685dc7fc38a7a4e5a4e6f986e12726529643cf5d08793135a16b7d62f83fb96dde6d233706323041043dfc3d2f84b1697313077c639b0436753a2eb5 + +# Generated by OpenSSL 3.6 +Key = 64687FFC1ECBF7C9C5121224274814E4 +Nonce = 656552115989F21FBB9BF73E +AD = B9A0A2324564E39F5BAB0EE47E3A1B2B89C68DE7F249077F845A35224DE2E909 +In = BBC7CBD7E690FF5DB65D727EA9FAA294394CE8D7CC9F6AACC669533E353B05E087FCF5BAD9A1422073D20B0C4CF21DC47146444D32F11A9419E3F8CFF8D063B2EFF9FA07D594BA0D805F7D983B51A3EB8E826B814BB87FCBEFE27F8866A1CE0F17B43D490B10734C25863D30F59FD453EC88967D329C3CB3437E5B35705DD91E5D32E8923C2D5FEDE0C43A81FF17A91FDA5E2A780CF3355144B149961EF55D113B0EAFBFC7C1F80F1D8D0475DD480DC1DA6106A6527FA0E01F6C3C12804406F7B656ECB49215924CDC2C5BD57F2F7B16473E0E357E4B88C29A1B4B8D257A109285F66D11D6EAA1FC6051D44E0AF6343E6AFA7E10677AF255AB0DC5FC054B5C82C83B10FD9E373C29F6CE20D1C827823FCBADB5ED04BDAD4BE7E04BAD59BE5BD05D96A23189BAB6C0C35A34B105E3A2E2AE41B0A21AFB501ABF2228268573C04040D38402EEF692D3920ED37E6BA8EF962160C04C8EAC95099585EC5C01CC692AFDD14E61DF9D54D0EE300188DD17CDAF1EDA38FD4D42F428366CE011C244DBE7C0D648F99A1910A20D34AAD7D229F3DD14F59B37525BBA8F1048E1BBD6C08945AD975A6BC7C907E69747949898B8C9406017E26E9DAE69DF60E734D92E3DBEC82D8D9850A777FE9115DA4302941281A50FAE0A9A29AE648A4F1DE80175AF4E717E003BFE0FF98A6831B828A2C705DED39F6ECA901C72C9BF5F2F660BA8147E6B36837E6DE4D529EA9BFB08CBB75F8A038219A1B96BCCDE66F488DC42101980A3AB2EAFC01A1DBC0D43E72819DC0AB86259DBE9871B5F8E40EFC25FB1F0F7F469A33C23EA4389D3F6F118874281A7C93C47DA4A503A9D84861D9CDA345E57C33385556991A104FDD99014A4E4059571954E8B17C51F1AFE1F19EA6D5A44D0D57BAD4FAD79AB96CCD94140CEAE46909C162180C645249764EB8C598A899A14D01BB83EF372A927507976469AADD9D531A042BA15F94CA527E75FA746369ADD2D33CBDAA7B6547F06A12BA2BCD3F79D6622E254A387C05743B2E4729443764C8FBA209FCE365A050AAF6D6AFF5FF758CFCB8B7CEB5C2FF64BD830C6417FD8DF589CDCB9644DD59B8489FA489B2A4E94FED62D488B67240C6C71B8BBAC3A66A40302EB5775F56664A16750C28935D2F281EA311E243FB41DC7251BD9C2B5363A2E185ADB3A9C0A3AD4EF1C3B66E6A0E13978AD60A9513D568673AAC2A769D135925DC6F3F75731C5D1CBC1D3EB0EC9BE088561DF0C361BF18D60FF485B9A9DFDEFAA35DFCB64F47E35D6F4779AE717D21D649F3F8A99D2C59328800A25CD838EFA075E336CCFAA17EB87F321651186F50B9172B7316E79C74EEADB284BC33F4FF6909DBB2FBF81FF96C56B4B2584752D63ACEB508AED674811EE866FC509A764FC43091EE0A5DA8ECF2D22EBDD8B3E9FA2D89596AAAB1FFC9F0EABE3F7577B3404C1 +Out = 4DAB6EFC58CD7E34972834F8DEAAFD76E82EAFC47502C7EEFE596FB2E366665471C4361E11265D3C0F230730674A51CF91574548DD69E07942C717EF600B74508E6612D12ADAA25FB1504F0673A063522EC51FFAB7F43D4D30CE398EC6A73D3ABD1268C22F18F1E794B83B940F63303E84605167A8599528FF548F7BC92B9EB36EFAC814DF4932754097531DBD2FB37D463D567289974FCA1B952598C742C874441C474C147460A76581EE45E9F9648EBFF11D0B9DB7BEE2428BC8E2827E1D968E1CAB1B3E71F8CA1231CD58C7E1C18ACB3465A7CE65E8DBCEA2C3A240C65EBB653002C4C3E21E50C7BEBD08D2B06EE92B0774E87837037CF1806AE23F4918E064FB70F39A53CC0C9C30DD74D94F0AD8CF9011A8340DC45C9683D1DB9558788B102A993A526D32458B7025FDB70929BCBE189849A9576D7F71985D4229780C8FC75301A568593F6DFEB9838550EFF62EC1DC0886E27D2D7254E9ED45B09E30B7671008C31979FD2D1CDBCEEA74557954B5F8F4052A55ED6B585A21CD15A73E2FF21E3F58D96C4A579E6CC58E6EF1953E990F969833729727BEFDD8FC0C6AD734913EE47DDCCE7D76D49284FD3A650DF8BA630A61B8B1D92EEA22245008FAFAC570DD37F032C75AECFFF13AB8D3D6E26FC95008C7FE1CEEBB9BA1AA2B9AD325EA533A44C19D44C7858B31286EFD2EB364E88139D64D1E7AB5EBC160574742227578E661CAA58AA0D0CD7C63353743D3E8A29FBF60CD844896B8397607100C66D9387FDEEABD988D32FCA83D3B98B04295CD9C319199E9BE280CD261DE3BCF53075A2485D1CEF9006027022B3C8F06934B43C998E612EB144A688A7261E4BAB90CE3938DED031ED3F1346598EFBE411EDB8F9E6325197A930024615825591F4C0BEDA5591BE77D69DB285830E17CB5C60EE3431279AAF6905D99CE22F203EB4FFE9311DF1A2A777BFECF7E80DCBFBBA3CD838737610C364695B77B7FC71197AF32C5104A4FF3150CF79F3B5EBA3FB5C9712344F1C69CB86B49001EDE64D5FF025FE5A079CCA6CE745341AE092177202DABB167BE83A5C84DD27D39471C80E68B149E5161E483B0CEE9907950661B2BD771F45318696ED2D7C8EBC6D8123016C7153E7B87FC0BB1FC36E0817E036238348451783589BF6C423DD3480D332B2DFBDD93C55C5DB51327D0FA5EEDC2CE216E02490EF343668583AAEC5D03771C2BE6AF95AA7741FFF9C9675F3D3D7CD2917393492250AF611D33A827C4E8E4789C1DFAAB7C05D270AF03E2A2F72961AC2D01E8EFB19A0A3263CA8C34DEDEC16226307EBBED6673EB26EA1659DEC742858154F6407F3BE6673E164C5DD58DCB8538F016E0D23E24DB336AAE1DB3E78A9E8FB4DE5D51EC8EF6613F1442983CF8A8C5A89CE7ECE27E5EC071E197D5F35CF56E2120A6E853217471DA697D9979C80A3C1BC3AB5E89FD97C7A9021C04A7F1179C35DE diff -Nru botan3-3.7.1+dfsg/src/tests/data/argon2.vec botan3-3.12.0+dfsg/src/tests/data/argon2.vec --- botan3-3.7.1+dfsg/src/tests/data/argon2.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/argon2.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,4 @@ -#test cpuid avx2 ssse3 +#test cpuid avx2 ssse3 simd128 # First three are the official test vectors diff -Nru botan3-3.7.1+dfsg/src/tests/data/asn1_decoding.vec botan3-3.12.0+dfsg/src/tests/data/asn1_decoding.vec --- botan3-3.7.1+dfsg/src/tests/data/asn1_decoding.vec 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/asn1_decoding.vec 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,252 @@ + +# DER-encoded BOOLEAN TRUE +Input = 0101FF +ResultBER = OK + +# DER-encoded BOOLEAN FALSE +Input = 010100 +ResultBER = OK + +# BER-encoded BOOLEAN TRUE with non-canonical value 0x01 +Input = 010101 +ResultDER = Detected non-canonical boolean encoding in DER structure +ResultBER = OK + +# BER-encoded BOOLEAN TRUE with non-canonical value 0x7F +Input = 01017F +ResultDER = Detected non-canonical boolean encoding in DER structure +ResultBER = OK + +# DER-encoded INTEGER 0 +Input = 020100 +ResultBER = OK + +# DER-encoded INTEGER 1 +Input = 020101 +ResultBER = OK + +# DER-encoded INTEGER -1 +Input = 0201FF +ResultBER = OK + +# DER-encoded INTEGER 128 +Input = 02020080 +ResultBER = OK + +# DER-encoded INTEGER -128 +Input = 020180 +ResultBER = OK + +# DER-encoded INTEGER -129 +Input = 0202FF7F +ResultBER = OK + +# Non-minimal INTEGER encoding (leading zero not needed for value 1) +Input = 02020001 +ResultDER = Detected non-minimal INTEGER encoding in DER structure +ResultBER = OK + +# Non-minimal INTEGER encoding (two leading zeros) +Input = 0203000001 +ResultDER = Detected non-minimal INTEGER encoding in DER structure +ResultBER = OK + +# Non-minimal negative INTEGER encoding (leading FF not needed) +Input = 0202FF80 +ResultDER = Detected non-minimal INTEGER encoding in DER structure +ResultBER = OK + +# Non-minimal negative INTEGER (leading FF, next byte has high bit set) +Input = 0202FFFF +ResultDER = Detected non-minimal INTEGER encoding in DER structure +ResultBER = OK + +# Empty INTEGER encoding +Input = 0200 +ResultDER = Detected empty INTEGER encoding in DER structure +ResultBER = OK + +# DER-encoded NULL +Input = 0500 +ResultBER = OK + +# DER-encoded SEQUENCE containing an INTEGER +Input = 3003020101 +ResultBER = OK + +# DER-encoded SEQUENCE containing two INTEGERs +Input = 3006020101020102 +ResultBER = OK + +# DER-encoded BIT STRING (no unused bits) +Input = 030200FF +ResultBER = OK + +# DER-encoded BIT STRING (zero-length content, just unused-bits byte) +Input = 030100 +ResultBER = OK + +# BIT STRING with non-zero padding bits (3 unused, last byte 0xFF) +Input = 030203FF +ResultDER = Detected non-zero padding bits in BIT STRING in DER structure +ResultBER = OK + +# BIT STRING with non-zero padding (1 unused, last byte 0x01) +Input = 030201FF +ResultDER = Detected non-zero padding bits in BIT STRING in DER structure +ResultBER = OK + +# BIT STRING with 1 unused bit and clean padding +Input = 030201FE +ResultBER = OK + +# BIT STRING with 7 unused bits and clean padding +Input = 03020780 +ResultBER = OK + +# DER-encoded OCTET STRING +Input = 040568656C6C6F +ResultBER = OK + +# Empty input +Input = +ResultBER = OK + +# Truncated value +Input = 0402FF +ResultBER = Value truncated + +# Length field not found (tag with no length) +Input = 02 +ResultBER = Length field not found + +# EOC marker (00 00) followed by a valid INTEGER +Input = 000002010A +ResultDER = Detected EOC marker in DER structure +ResultBER = OK + +# Valid OID: 1.2.840.113549.1.1.11 (sha256WithRSAEncryption) +Input = 06092A864886F70D01010B +ResultBER = OK + +# Valid OID: 2.5.4.3 (commonName) +Input = 0603550403 +ResultBER = OK + +# Valid OID: 0.0 +Input = 060100 +ResultBER = OK + +# Valid OID: 2.999 (largest second arc for root 2 that fits in one byte) +Input = 06028837 +ResultBER = OK + +# Empty OID content +Input = 0600 +ResultBER = OID encoding is too short + +# Leading zero byte in multibyte OID component encoding (X.690 8.19.2) +# 2.0.1.128 but the 128 is encoded as 8001 instead of the minimal 8100 +Input = 0603500180 +ResultBER = Leading zero byte in multibyte OID encoding + +# Truncated multibyte OID component (high bit set on last byte) +Input = 060350018101 +ResultBER = Truncated OID value + +# All continuation bytes, never terminated +Input = 060450018181 +ResultBER = Truncated OID value + +# OID component overflow: 2.1.4294967296 (exceeds 32-bit) +Input = 0606519080808000 +ResultBER = OID component overflow + +# OID component overflow: 2.4294967216 (exceeds 32-bit second arc) +Input = 06059080808000 +ResultBER = OID component overflow + +# Tag 06 with zero-length content (duplicate of empty OID, different TLV) +Input = 060000 +ResultBER = OID encoding is too short + +# Tag 06 with length 0xFF (invalid long-form length) +Input = 06FF00 +ResultBER = Length field is too large + +# Leading zero in multibyte encoding with many continuation bytes +# 0.1 followed by a component starting with 0x80 (leading zero) +Input = 06070180808080807F +ResultBER = Leading zero byte in multibyte OID encoding + +# Component encoded as 80 01 - leading zero, should be just 01 +Input = 06028001 +ResultBER = Leading zero byte in multibyte OID encoding + +# Component encoded as 80 7F - leading zero, should be just 7F +Input = 0602807F +ResultBER = Leading zero byte in multibyte OID encoding + +# Single continuation byte with no terminator +Input = 060181 +ResultBER = Truncated OID value + +# Multibyte component that overflows 32 bits +Input = 0606FFFFFFFFFF7F +ResultBER = OID component overflow + +# Long-form tag with 0x80 as first subsequent octet (continuation, no data) +# X.690 8.1.2.4.2(c) forbids bits 7-1 of the first subsequent octet being all zero +Input = 1F8001 +ResultBER = Long form tag with leading zero + +# Long-form tag with 0x00 as first subsequent octet (encoding of tag value 0) +# Without this check the 2-byte tag decodes as (Eoc, Universal) and collides +# with the EOC marker, enabling BER malleability inside indefinite-length TLVs +Input = 1F0000 +ResultBER = Long form tag with leading zero + +# Indefinite-length SEQUENCE whose content is a single fake-EOC TLV 1F 00 00 +# encoded with a long-form tag. Before the 0x00 check was added, find_eoc +# stopped at the fake EOC and decode_length under-reported content_length +# by one byte, silently accepting the input with truncated content. +Input = 30801F0000000000 +ResultDER = Detected indefinite-length encoding in DER structure +ResultBER = Long form tag with leading zero + +# Same malleability embedded after a valid INTEGER: find_eoc would accept the +# 3-byte fake EOC and the trailing real 00 00 passed the EOC validator. +Input = 30800201421F00000000 +ResultDER = Detected indefinite-length encoding in DER structure +ResultBER = Long form tag with leading zero + +# Indefinite-length SEQUENCE whose first inner TLV is a malformed EOC +# 00 02 00 00 (tag 0, length 2, content 00 00). Per X.690 8.1.5 EOC must be +# exactly 00 00; find_eoc previously terminated on any (Eoc, Universal) tag +# regardless of length and assigned bytes to the outer content. +Input = 308000020000 +ResultDER = Detected indefinite-length encoding in DER structure +ResultBER = EOC marker with non-zero length + +# Same bug with long-form (non-canonical) length on the EOC tag: 00 81 00 is +# still tag 0 length 0, but the length field is 2 bytes not 1. +Input = 3080008100 +ResultDER = Detected indefinite-length encoding in DER structure +ResultBER = EOC marker with non-zero length + +# Malformed EOC at top level (outside any indefinite-length context). The +# get_next_object loop used to read "content" bytes and continue silently; +# the tag+length must be rejected before any content is consumed. +Input = 00020000 +ResultBER = EOC marker with non-zero length + +# Long-form tag encoding of tag 1 (Boolean). Per X.690 8.1.2.2 tag values +# 0-30 must use short form; long form is reserved for tag >= 31. Accepting +# this produces a parser differential with strict BER parsers since the +# bytes 1F 01 01 FF decode to the same value as short-form 01 01 FF. +Input = 1F0101FF +ResultBER = Long-form tag encoding used for small tag value + +# Long-form tag encoding of tag 30 (BMPString) - boundary case. +Input = 1F1E00 +ResultBER = Long-form tag encoding used for small tag value diff -Nru botan3-3.7.1+dfsg/src/tests/data/asn1_oid_invalid.vec botan3-3.12.0+dfsg/src/tests/data/asn1_oid_invalid.vec --- botan3-3.7.1+dfsg/src/tests/data/asn1_oid_invalid.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/asn1_oid_invalid.vec 2026-05-07 01:38:28.000000000 +0000 @@ -30,5 +30,5 @@ DER = 06028001 DER = 0602807F -# BUG: we accept constructed encoding of an OID, but should not -#DER = 06800000 +# Indefinite-length encoding of primitive OID type (X.690 8.1.3.2) +DER = 06800000 diff -Nru botan3-3.7.1+dfsg/src/tests/data/asn1_print/output7.txt botan3-3.12.0+dfsg/src/tests/data/asn1_print/output7.txt --- botan3-3.7.1+dfsg/src/tests/data/asn1_print/output7.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/asn1_print/output7.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,15 +1,15 @@ - d= 0, l=5304: SEQUENCE + d= 0, l=5302: SEQUENCE d= 1, l= 9: OBJECT 1.2.840.113549.1.7.2 - d= 1, l=5289: cons [0] context - d= 2, l=5285: SEQUENCE + d= 1, l=5287: cons [0] context + d= 2, l=5283: SEQUENCE d= 3, l= 1: INTEGER 1 d= 3, l= 15: SET d= 4, l= 13: SEQUENCE d= 5, l= 9: OBJECT SHA-256 [2.16.840.1.101.3.4.2.1] d= 5, l= 0: NULL - d= 3, l= 13: SEQUENCE - d= 4, l= 9: OBJECT 1.2.840.113549.1.7.1 - d= 3, l=4679: cons [0] context + d= 3, l= 11: SEQUENCE + d= 4, l= 9: OBJECT PKCS7.Data [1.2.840.113549.1.7.1] + d= 3, l=4677: cons [0] context d= 4, l=1799: SEQUENCE d= 5, l=1391: SEQUENCE d= 6, l= 3: cons [0] context @@ -411,7 +411,7 @@ d= 6, l= 24: SEQUENCE d= 7, l= 9: OBJECT PKCS9.ContentType [1.2.840.113549.1.9.3] d= 7, l= 11: SET - d= 8, l= 9: OBJECT 1.2.840.113549.1.7.1 + d= 8, l= 9: OBJECT PKCS7.Data [1.2.840.113549.1.7.1] d= 6, l= 28: SEQUENCE d= 7, l= 9: OBJECT 1.2.840.113549.1.9.5 d= 7, l= 15: SET diff -Nru botan3-3.7.1+dfsg/src/tests/data/asn1_string_validation.vec botan3-3.12.0+dfsg/src/tests/data/asn1_string_validation.vec --- botan3-3.7.1+dfsg/src/tests/data/asn1_string_validation.vec 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/asn1_string_validation.vec 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,56 @@ + +# Empty string is accepted by all supported string types +Input = +ValidNumeric = true +ValidPrintable = true +ValidIa5 = true +ValidVisible = true +ValidUtf8 = true + +# Digits are accepted by all subset string types +Input = 9999 +ValidNumeric = true +ValidPrintable = true +ValidIa5 = true +ValidVisible = true +ValidUtf8 = true + +# Lower-case ASCII is not NumericString +Input = notnumeric +ValidNumeric = false +ValidPrintable = true +ValidIa5 = true +ValidVisible = true +ValidUtf8 = true + +# Apostrophe is allowed in PrintableString +Input = O'Paddigons +ValidNumeric = false +ValidPrintable = true +ValidIa5 = true +ValidVisible = true +ValidUtf8 = true + +# Asterisk is visible IA5 but not PrintableString +Input = Wildcard*Time +ValidNumeric = false +ValidPrintable = false +ValidIa5 = true +ValidVisible = true +ValidUtf8 = true + +# Contains a literal tab between 'a' and 'b' +Input = a b +ValidNumeric = false +ValidPrintable = false +ValidIa5 = true +ValidVisible = false +ValidUtf8 = true + +# Valid UTF-8, not an ASCII subset +Input = Fancé +ValidNumeric = false +ValidPrintable = false +ValidIa5 = false +ValidVisible = false +ValidUtf8 = true diff -Nru botan3-3.7.1+dfsg/src/tests/data/block/aes.vec botan3-3.12.0+dfsg/src/tests/data/block/aes.vec --- botan3-3.7.1+dfsg/src/tests/data/block/aes.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/block/aes.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,7 +1,7 @@ # Test vectors from NIST CAVP AESAVS # http://csrc.nist.gov/groups/STM/cavp/documents/aes/AESAVS.pdf -#test cpuid aesni avx2_vaes armv8aes power_crypto ssse3 neon altivec +#test cpuid avx2_vaes aesni armv8aes power_crypto ssse3 neon altivec lsx simd128 [AES-128] Key = 000102030405060708090A0B0C0D0E0F @@ -1536,6 +1536,16 @@ In = 0000000000000000000000000000000400000000000000000000000000000002000000000000000000000000000000018000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000 Out = 200211214E7394DA2089B6ACD093ABE00388DACE60B6A392F328C2B971B2FE7858E2FCCEFA7E3061367F1D57A4E7455A3AD78E726C1EC02B7EBFE92B23D9EC3445BC707D29E8204D88DFBA2F0B0CAD9B161556838018F52805CDBD6202002E3FF5569B3AB6A6D11EFDE1BF0A64C6854A64E82B50E501FBD7DD4116921159B83E +# Generated by OpenSSL +Key = 9FCFB037623F4B9A3F7FF3DD87CE6E41 +In = C15BC160670CBDF94167AE26F17EAD140C85107A1DCD15E534D6C000A65372805D0CEBE902D6B699BB5F0996F8503E50E4AD6E3AC3E119A0072522C555437C0A8C0221259AA0A675C1D85F1536D30B10F4E07D9EB8276A6388BEB0FE352A624D6D0C7DA627D894432081FD02296F49C3740AED5FEA3DDB265D46724C76433ECC1F76D6C5EF92A4FE66327793810740D9 +Out = 09F0329649975583F8B55763246988A9BF0DD3FAAF60EC074E85626BAE7EC57585C51DDAE5F0D66F131FE560E7640BE4C36E7317F6A08B79086AC2AED8E3BE050D60917ABBAE62AE37B256D02D2E73A7B95491B8561CB2FCB3CF8309EBEF0483588A724465FBF04B5476C2287AFFE877C2DD293E29B28981B6A9D90F9403C9FE78B72E50A4EB90DE4C66A75F160FC707 + +# Generated by OpenSSL +Key = 00112233445566778899aabbccddeeff +In = 000100020003000400050006000700080009000a000b000c000d000e000f0010001100120013001400150016001700180019001a001b001c001d001e001f0020002100220023002400250026002700280029002a002b002c002d002e002f0030003100320033003400350036003700380039003a003b003c003d003e003f0040004100420043004400450046004700480049004a004b004c004d004e004f0050005100520053005400550056005700580059005a005b005c005d005e005f0060006100620063006400650066006700680069006a006b006c006d006e006f0070007100720073007400750076007700780079007a007b007c007d007e007f0080008100820083008400850086008700880089008a008b008c008d008e008f0090009100920093009400950096009700980099009a009b009c009d009e009f00a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b000b100b200b300b400b500b600b700b800b900ba00bb00bc00bd00be00bf00c000c100c200c300c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d300d400d500d600d700d800d900da00db00dc00dd00de00df00e000e100e200e300e400e500e600e700e800e900ea00eb00ec00ed00ee00ef00f000f100f200f300f400f500f600f700f800f900fa00fb00fc00fd00fe00ff0000010101020103010401050106010701080109010a010b010c010d010e010f0110011101120113011401150116011701180119011a011b011c011d011e011f0120012101220123012401250126012701280129012a012b012c012d012e012f0130013101320133013401350136013701380139013a013b013c013d013e013f0140014101420143014401450146014701480149014a014b014c014d014e014f0150015101520153015401550156015701580159015a015b015c015d015e015f0160016101620163016401650166016701680169016a016b016c016d016e016f0170017101720173017401750176017701780179017a017b017c017d017e017f0180018101820183018401850186018701880189018a018b018c018d018e018f0190019101920193019401950196019701980199019a019b019c019d019e019f01a001a101a201a301a401a501a601a701a801a901aa01ab01ac01ad01ae01af01b001b101b201b301b401b501b601b701b801b901ba01bb01bc01bd01be01bf01c001c101c201c301c401c501c601c701c801c901ca01cb01cc01cd01ce01cf01d001d101d201d301d401d501d601d701d801d901da01db01dc01dd01de01df01e001e101e201e301e401e501e601e701e801e901ea01eb01ec01ed01ee01ef01f001f101f201f301f401f501f601f701f801f901fa01fb01fc01fd01fe01ff0100020102020203020402050206020702080209020a020b020c020d020e020f021002110212021302140215021602170218 +Out = b0e1e4b69f7a344211d24e017715e5dc3f9da1a4af0c2a9470975ebf48d354a5942c04169bb67bcc7388c0e77323bbb7ba349a9cdc9554eb5f261d30741d98beb3e1e3512777f44458940b68252b7637c0fdc988f534eb63476ba224630646b442cd514d582aa7096b345490c47f41f3586100db6b5da00075c54d70d2f00cae36d1d11fbe37d3c60a970c9bda2ea496cfaefb1ea041434ac8ebfe91427f9dc9abe1a240db2eefdb6d2e6552303f5e99f61e9da5fd2b62618d000acb40743a1162963882df76589960aec48d298565922ecda1df9261d18234adebc9a3ba7c566081af9aaaad467397186b2978c265e1dac5dfa9208f7618371104df4e1c95aa00092bee89c9ee03c1d7fee310f76895db12a285dde89001accbf9e7a8ca44d9d1688c7b58d5ff78c427b8cb98b99d3b911e9e19fa875b0e89189edc76204c1819483fffe89681a0659d7e5f206e9d642abd41b528a3c538f36a5c97f89158a0b6e4b63d4004a83e9dfeae51ff4241fbd2022da28a16a7dd0fe1f7a6c7dbcaf5ef36ecc567c1384aee70f9b32a46874f9b0d82fb07658c6ce2593048b474e0eeb86c68357a3c1ca5ade89e0bd6bccedea218fa3acb5afd29d61ac9bf2ea4cdb2ed85e676dac531c959f60a51187b8a3527a420abfa28b2a09319546d555aae7894b22b0ef6aaa2ea56374a2b0202ac183d5b61ddd4e985dd4af8ee0a0d215a32d8c8393e7f1eaf32ef75cd01e8e7e0a4f1eadfb1d26f8e379ef13b68b80b3cedee7692f5a65b1b408d26e15d1a23f36e65d646bb85e1763b1606c9d350c77845e3ba7f13f55fa74a79c4ec5f452f24193fc8c47843d7914d67e9ea783487ee389443832060d46805ad3e485b2669b44d48ee0fab0f6391215d67faa2e1d32b899e430554ca461ac64cc0f6b9372e7684c770370b58ffa91c2f201ab4bcf1af13c033f31ee7a8e319d6a4e76de7c7f2898cb27c8ca741a6ccbe836d452852da88d1bd6be32a654dd46f2b7666bba02f01c8282d14c88edd25f977a4f49687ba83aee905d879329fc6a30c1856f669f39894ae70a0eec359e1825bddeae8c8287fb5513b0837acbf80f4e87f823c3d41496820654569f9b9691fc22cf1306689f215bcac9f20f5dbca05668dba3e9191d06ac15ecef4b2826ff10433220e862ecb2624ebd700c56a562d60e8e52a9d4ff99c077fc0a1adc5c24db2f68cc1edec2fe71fe3a3d23c8ab4422c5ea4da3bccb0e8d7d33eb4e9a8ecb518c2e2672bd80b19ef4834d10bcbca4d0544b20fa8df7d271f90a4c213f8d9388ad72ee7168192780110d136e9b52d2538dc7a5bf2ab9e5bc89548a1e299d2e9b0cff23acf74367c15dc5c5ed21a8fbeb1b494e1f5384631632eb56793b4d197d4401657fc6cc26d6e9bb57834c4e1b85c0d1f76126f8c9ae5003c418f78777be36ab34915d5168a162ca0b1e9c94cfed56fc5fdaf0a4b3b7e5b0a605f06fde6d2102c71a5e02b7d30139f440f24f978ff7b5df7b5eb32 + [AES-192] Key = 000102030405060708090A0B0C0D0E0F1011121314151617 In = 00112233445566778899AABBCCDDEEFF @@ -3337,6 +3347,16 @@ In = 00000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001 Out = 53995DE0009CA18BECAFB8307C54C14C2006BF99F4C58B6CC2627856593FAEEA2DA697D2737CB30B744A4644FA1CBC6E47A22ACDB60C3A986A8F76ECD0EA3433FDAA17C2CDE20268FE36E164EA53215198E7247C07F0FE411C267E4384B0F600CD33B28AC773F74BA00ED1F312572435 +# Generated by OpenSSL +Key = 2B53B89B85F59E23717637BA896D96112824B0610FFD00B4 +In = C15BC160670CBDF94167AE26F17EAD140C85107A1DCD15E534D6C000A65372805D0CEBE902D6B699BB5F0996F8503E50E4AD6E3AC3E119A0072522C555437C0A8C0221259AA0A675C1D85F1536D30B10F4E07D9EB8276A6388BEB0FE352A624D6D0C7DA627D894432081FD02296F49C3740AED5FEA3DDB265D46724C76433ECC1F76D6C5EF92A4FE66327793810740D9 +Out = 7A187211A68CE315907AE4948A12ADE3FCBA6E27367D59A36452D265D1208B8B67A71B23BE1A8DAB5B0E50170B7BC78C880937C2DFDA1DDF1DB02703C77401B8FA86BEC3725DDF70F79DA8BC3703867D9905D637539B439976AC63AB35DB97A38B614150D5D2D94952A3DFABF7487FB879A09132521ABDE989443ED39BC9FAA0C512B1C0DA324A7FC01808A5E06F1A68 + +# Generated by OpenSSL +Key = 00112233445566778899aabbccddeeff1021324354657687 +In = 000100020003000400050006000700080009000a000b000c000d000e000f0010001100120013001400150016001700180019001a001b001c001d001e001f0020002100220023002400250026002700280029002a002b002c002d002e002f0030003100320033003400350036003700380039003a003b003c003d003e003f0040004100420043004400450046004700480049004a004b004c004d004e004f0050005100520053005400550056005700580059005a005b005c005d005e005f0060006100620063006400650066006700680069006a006b006c006d006e006f0070007100720073007400750076007700780079007a007b007c007d007e007f0080008100820083008400850086008700880089008a008b008c008d008e008f0090009100920093009400950096009700980099009a009b009c009d009e009f00a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b000b100b200b300b400b500b600b700b800b900ba00bb00bc00bd00be00bf00c000c100c200c300c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d300d400d500d600d700d800d900da00db00dc00dd00de00df00e000e100e200e300e400e500e600e700e800e900ea00eb00ec00ed00ee00ef00f000f100f200f300f400f500f600f700f800f900fa00fb00fc00fd00fe00ff0000010101020103010401050106010701080109010a010b010c010d010e010f0110011101120113011401150116011701180119011a011b011c011d011e011f0120012101220123012401250126012701280129012a012b012c012d012e012f0130013101320133013401350136013701380139013a013b013c013d013e013f0140014101420143014401450146014701480149014a014b014c014d014e014f0150015101520153015401550156015701580159015a015b015c015d015e015f0160016101620163016401650166016701680169016a016b016c016d016e016f0170017101720173017401750176017701780179017a017b017c017d017e017f0180018101820183018401850186018701880189018a018b018c018d018e018f0190019101920193019401950196019701980199019a019b019c019d019e019f01a001a101a201a301a401a501a601a701a801a901aa01ab01ac01ad01ae01af01b001b101b201b301b401b501b601b701b801b901ba01bb01bc01bd01be01bf01c001c101c201c301c401c501c601c701c801c901ca01cb01cc01cd01ce01cf01d001d101d201d301d401d501d601d701d801d901da01db01dc01dd01de01df01e001e101e201e301e401e501e601e701e801e901ea01eb01ec01ed01ee01ef01f001f101f201f301f401f501f601f701f801f901fa01fb01fc01fd01fe01ff0100020102020203020402050206020702080209020a020b020c020d020e020f021002110212021302140215021602170218 +Out = 6db73f8357a59178bab90fadd41b000fb3e6c0e65bbf94d24075fa783fa4c46dd65e682c5a52eaaf080fdf1c844f645d9b7dbbb1935ab9bf2e0497340cb7fe52900489da9d9a8eb9c11b614bab3b6596075e9902bc37bbc632f0ab72ccc3e5ca7ba5d44ac4fb53cb7956c518d4a5da1780204b7bc879ae9e9dc02be6d253f55c73e098d765d50faeb2b42c775d77c24c8d1cf89f795a1e1552254ac2f654318a2e4474479cbcc6d256ee08c3444601b601f652ba8efaa4326b5bd9675beb435525d6f2e7f4e28166bade3759e59aa5e7c6e906a5219fab6c1aec079a157c5f929af32d12ad95b5aaf1061199e425b6531f7459bb527df84fcb1c90405458d93e3e8c4c8cb8898ec98184d1d91b20961fce993222d8e6e0c8c0fb27392a4313b9d76a9d38343a5d97ffdcc615e666498d55f7edc95bd6c94b0b2815aaad7343e42e94c4f3d0eb90e7d83ea0f1218c1c99a205c60d0cb6e4f2e17c07f4b2390c27d5b23bb3c93a85dcdaeefd05ce8ce310a61a7d3936a311fcaff2835d1b5b0d72974051de77c9a6840dbc1956873a5dd9981fa04d55d60fbd1e1673a1d2b29d4674ca44084b6197313580fcc743037ceef54c21b316fc67c526a4fca09350ce4d8ad9fbcf94f72bd023784ce9b2e0f2cd376169c81aa0f699f06d98d84c0cda834ec09b5a8c42377a111a28f68b16ace0ca71e9dd306fe30b57c35185e906b54ab74f52a683a248c59aed0e9f57dab7b973fe3f26b60a2bbc780bfb823f0d91c3de95791cd4d972bac4a48514d4c21374ac436d4bf5ab2a9658305016a71643c648d644c28982451dbff08dd8c83ba537102bc080738d132f17e812245aef28489920ed2a34d0ce1e71fb36d97462d9d7fbd86fe34839c432205d39efe4a67eeb1d309d9236d8099aba7ff2fef03d1c3f74af2eff4e2bc6070ccc3dafca3b1e7402533db07b6db47746582ffd1da0291f59da3e728c26cfc92078bb5a720b0e2d95a86d5a79ec5168331a0807dd37ebeb54160fb1c3f86fa8c5f546a71d6b754fde944b4bfca9ddbc3d2cf1dc9e153d76d6d280ff89a09abfa9a1f47ba57981f8f2f030f153318cad874f86dfe9d99a763e5a60dc9f195a7ead5f28f32ac5ccb8c05575e84225b55413bfeb353afa1fae1949b87850cc51cd7e292979e86aecaca5a9cec159f18b94e732ac217f70b6d9f69be2dd89649fafb2effed5d92ed238b4e95d2c3a8fd92bfc5d35f28cba340b070900f2ff448ed0fa30836d9973ff108303119015621c7434df9009e98ab869fedad0531c979cbe5a91047c612f0606b7972784efc52bfdc8c193f9cc63ee61ba8e38588bac654589e0003f4eee06e0e26d2d8619f1a40438553a7ada6cc4df5003ba92baaf787a4947a38537a8b197a3600d22824c80a6744ac36ab949996d0626d3c72c9cd5b1be0c975e6ecf9c9ea35df39743fd5cc08a06cf1fedf1f89141ca8662691a0a7cf5e86d6b53141d73ff7529f2e1772e0e855d254777a33616 + [AES-256] Key = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F In = 00112233445566778899AABBCCDDEEFF @@ -5393,3 +5413,13 @@ Key = 0000000000000000000000000000000000000000000000000000000000000000 In = 0000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001 Out = 1490A05A7CEE43BDE98B56E309DC0126ABFA77CD6E85DA245FB0BDC5E52CFC29DD4AB1284D4AE17B41E85924470C36F7CEA7403D4D606B6E074EC5D3BAF39D18530F8AFBC74536B9A963B4F1C4CB738B + +# Generated by OpenSSL +Key = 7085D794ED7B9FE8D43E112FC5C73F9D1F66BB1EB9D918FEB3F25F362A9BC2BB +In = C15BC160670CBDF94167AE26F17EAD140C85107A1DCD15E534D6C000A65372805D0CEBE902D6B699BB5F0996F8503E50E4AD6E3AC3E119A0072522C555437C0A8C0221259AA0A675C1D85F1536D30B10F4E07D9EB8276A6388BEB0FE352A624D6D0C7DA627D894432081FD02296F49C3740AED5FEA3DDB265D46724C76433ECC1F76D6C5EF92A4FE66327793810740D9 +Out = 293C4745E7116F58F29C04F169BC34796FF1E585ED61339170D310DD5A9FB173A694C8B6A5B6278A563AEB61AEAE34C84D8FF0299430BE61ABDFB055D6C0B0497C9A15EC1EA557DC18231105D97A6EC34EF4D6BD1DC05441E7B6DCCCE96BC1B7E123143A8B8356237B48F0C9CBF7E28754441F6300B08AE82CAAC7959D04CAA7E0A73452F330BB4FEE2DD32CFB4CE3AD + +# Generated by OpenSSL +Key = 00112233445566778899aabbccddeeff102132435465768798a9bacbdcedfe0f +In = 000100020003000400050006000700080009000a000b000c000d000e000f0010001100120013001400150016001700180019001a001b001c001d001e001f0020002100220023002400250026002700280029002a002b002c002d002e002f0030003100320033003400350036003700380039003a003b003c003d003e003f0040004100420043004400450046004700480049004a004b004c004d004e004f0050005100520053005400550056005700580059005a005b005c005d005e005f0060006100620063006400650066006700680069006a006b006c006d006e006f0070007100720073007400750076007700780079007a007b007c007d007e007f0080008100820083008400850086008700880089008a008b008c008d008e008f0090009100920093009400950096009700980099009a009b009c009d009e009f00a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b000b100b200b300b400b500b600b700b800b900ba00bb00bc00bd00be00bf00c000c100c200c300c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d300d400d500d600d700d800d900da00db00dc00dd00de00df00e000e100e200e300e400e500e600e700e800e900ea00eb00ec00ed00ee00ef00f000f100f200f300f400f500f600f700f800f900fa00fb00fc00fd00fe00ff0000010101020103010401050106010701080109010a010b010c010d010e010f0110011101120113011401150116011701180119011a011b011c011d011e011f0120012101220123012401250126012701280129012a012b012c012d012e012f0130013101320133013401350136013701380139013a013b013c013d013e013f0140014101420143014401450146014701480149014a014b014c014d014e014f0150015101520153015401550156015701580159015a015b015c015d015e015f0160016101620163016401650166016701680169016a016b016c016d016e016f0170017101720173017401750176017701780179017a017b017c017d017e017f0180018101820183018401850186018701880189018a018b018c018d018e018f0190019101920193019401950196019701980199019a019b019c019d019e019f01a001a101a201a301a401a501a601a701a801a901aa01ab01ac01ad01ae01af01b001b101b201b301b401b501b601b701b801b901ba01bb01bc01bd01be01bf01c001c101c201c301c401c501c601c701c801c901ca01cb01cc01cd01ce01cf01d001d101d201d301d401d501d601d701d801d901da01db01dc01dd01de01df01e001e101e201e301e401e501e601e701e801e901ea01eb01ec01ed01ee01ef01f001f101f201f301f401f501f601f701f801f901fa01fb01fc01fd01fe01ff0100020102020203020402050206020702080209020a020b020c020d020e020f021002110212021302140215021602170218 +Out = 7a7f0f46b61f69e2c4c368c2f275f814432357ab2874d1d650dbd44da45df148cfca10c29836fab25e6105d60a147dbb6e7b65ded7167bec01c09800b9620a94c0728257fcc401e37a0a3c054fb077d723e3431d3ea99e09bd7861aecac8c56846c69b96634f3c87665a5dbb431c5d0758a763307f637f7cb1699b843a00fe6456c3fcfe9e645f3f6d56f157c1b20f7190594ba4f4b757aa0fe89e0f870c9a5224d246bbaf50751cd5ba752fcf4b41ecf880b9757627ec5ced52131b6891f87c73b1c3b3869b85d89e9f7f3e5c60222726773eebedb3c7a3c9093481ac17a3aa858d51abce9c66116f1de25a659d53cf9bf3b55ebb21892c238dac1f49397751d5d05b550a2e73937302358cc68f1b9cf824118e51a5b0da0e856daf2b270410db4c6e25f709a88db56636659bb8186a863eb73136b1014e22300cd28441a02a4b53e49ce16f419e2f70ae2fc6797f2b150290575dac950b1cb4f9f0d91cc9914739a1d4fd4575508a11f5f94db56b162478743b36f5d519173934f33ade001866f9980f2a75151f1edee73099432639a33898a373f8d4d3724ef7330ff00c91b6f941fb94be72dbfbd4ed5da78d74fe8113e9a21b7c2c7ca04b5dac3d42e0a04f16d7a2fd18f2e07b0c7112246250be92b744b370ea6ba8cf1ffa17144531a6e671e568e9bc47998f120f6f3488f19671017f3f2c1070ede058a3808e133632bcf86a4a0cff47e9f5b4ae518d859d4fca56b74cb370e7b6f0848751354b9fcae02ed5bdf5d924aaa89775e5a73f0419bc3ea2527577b6bb629f3d9f077643fab48dbef93fa2f959146dd69398d721b783ebe11041eadf59ca57f6968b78559136cbc65d4433882056704aa84c37c32ec4ed379972c235ba43ba42a5057b07ad0f533423b7dab881b13fddcdf71cd1240fc54de0ae241b76aa69d696b827af8bc357f28add6812a77bd5b55a6faac0dc4a5ad3da890227134009a2f7adda01a3b773611713e9941549f4b009a815fed3ac1d786d9637774dcac84c02c08b8d2828233cb163903ff61979e54fe56a80929c089eef242769d87a1142f5eaba8296fe252d24d39318edf39a79bae3b319aef206538efc81f95aae52118db415b73a3b249775e077783997182fcc9b372fdd9311fcf7c379a12c7473a9e74ed61415bbdc144193e358e0b4b87db7e17ba06faec98f5eefc122559a7ad450e448cddf3fe54afd0dba4f353505173e0dafa248934de09fd76c79e5c3852eb7a1da7a58ad109968e1d14021bbac3e4edbe90850719dabb903a92527d3aa4d333593418c3635f437e4853a18d0974d6b46f60ba88a3d26e0873bcc1fa273d2ed0143c5afc12ccaeb7a76144cd2082ef384935ce08a967e053f5a6a823b3f6c067c6c3957bf7a571de0ed54d7d333491db5db318088c97f343c989054ee0fa1b7f79cdd528bdb3705b38d71ca99185bd424557db0f91418610a670e1842b335aefde16c2380bea8f96f45e233509ec24241493cf4 diff -Nru botan3-3.7.1+dfsg/src/tests/data/block/aria.vec botan3-3.12.0+dfsg/src/tests/data/block/aria.vec --- botan3-3.7.1+dfsg/src/tests/data/block/aria.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/block/aria.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,3 +1,6 @@ + +#test cpuid avx512 aesni armv8aes + [ARIA-128] # ARIA Test Vector PDF @@ -10,6 +13,11 @@ In = 00112233445566778899aabbccddeeff Out = d718fbd6ab644c739da95f3be6451778 +# Generated by OpenSSL +Key = 0123456789abcdeffedcba9876543210 +In = 000100020003000400050006000700080009000a000b000c000d000e000f0010001100120013001400150016001700180019001a001b001c001d001e001f0020002100220023002400250026002700280029002a002b002c002d002e002f0030003100320033003400350036003700380039003a003b003c003d003e003f0040004100420043004400450046004700480049004a004b004c004d004e004f0050005100520053005400550056005700580059005a005b005c005d005e005f0060006100620063006400650066006700680069006a006b006c006d006e006f0070007100720073007400750076007700780079007a007b007c007d007e007f0080008100820083008400850086008700880089008a008b008c008d008e008f0090009100920093009400950096009700980099009a009b009c009d009e009f00a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b000b100b200b300b400b500b600b700b800b900ba00bb00bc00bd00be00bf00c000c100c200c300c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d300d400d500d600d700d800d900da00db00dc00dd00de00df00e000e100e200e300e400e500e600e700e800e900ea00eb00ec00ed00ee00ef00f000f100f200f300f400f500f600f700f800f900fa00fb00fc00fd00fe00ff0000010101020103010401050106010701080109010a010b010c010d010e010f0110011101120113011401150116011701180119011a011b011c011d011e011f0120012101220123012401250126012701280129012a012b012c012d012e012f0130013101320133013401350136013701380139013a013b013c013d013e013f0140014101420143014401450146014701480149014a014b014c014d014e014f0150015101520153015401550156015701580159015a015b015c015d015e015f0160016101620163016401650166016701680169016a016b016c016d016e016f0170017101720173017401750176017701780179017a017b017c017d017e017f0180018101820183018401850186018701880189018a018b018c018d018e018f0190019101920193019401950196019701980199019a019b019c019d019e019f01a001a101a201a301a401a501a601a701a801a901aa01ab01ac01ad01ae01af01b001b101b201b301b401b501b601b701b801b901ba01bb01bc01bd01be01bf01c001c101c201c301c401c501c601c701c801c901ca01cb01cc01cd01ce01cf01d001d101d201d301d401d501d601d701d801d901da01db01dc01dd01de01df01e001e101e201e301e401e501e601e701e801e901ea01eb01ec01ed01ee01ef01f001f101f201f301f401f501f601f701f801f901fa01fb01fc01fd01fe01ff0100020102020203020402050206020702080209020a020b020c020d020e020f021002110212021302140215021602170218 +Out = 29d4664fcfdcc7ebb1d20d00604ae8058972e9711553522e4e0789c9a8959ad7bace6dce85666fb84ced9dd487861d63ba07221ef705ee7165b624687f4cb78b03aacbf775df2be0e6ba947e4e6db4e9c75170ff9e017d52504136b0775368d6cb28783b381f32c2e0fe0d20b8f23d33b043daa416d32682e2ef83529e4b9c9989755d3ec2b1b8da65b1f94729f3d7a38b061aef62d140b9c31308710bc1bfe5ac533b205d63bc63061d4c79fd6410759ea50002014279f273a075efd45409d42f92bc5731f7157e1e0a564d4e9b1c1585a7de85e786e6426c8cacfe81a57f4af1379d80b98676a6db6dd6cdf1203f62f175e19cf365c5aa1ee01417bbece4104e246c14a68d6b8a94b4ea4dd596d597c7251bab894e7bda7f418bced648d0133287882baf3e05dc825c7fa5b89a6be95ab81d615fa57d36b608ef2658de1968f271d6a5587858bcbd58307957ae12d987ff915b79db1cadfb57847cee8b9b00f6326380a04768ebb7eb2c88f769f9e47644fd555a9ef281ce0c6102013ec5506c952d5bb6ec1bda12e2ff9194b174e5e18ac9f365d42192cc9fd6accba16e5366f1ff49536040817c897cfebf662d281c8f0cdaf09217b7888e07048a884a9879c7d1df5a1c5cbe7339acb0da543076cb192a5d16c2ee37499d88084d2db1bf075674df921e184eded8c593ca1b1aa5accab0a8cfb340c06df9374f85da8647ed1ff0b14f9de846f71e1fe8ee7c0c7d0cf9e3e45a1d46b53239dcd848b8800307a7db681e1130468de2bc7d8c13547b878057ff489489fd9b9ce164a2e621bc1f3f915e603a0c2d5fb7fd6b8410c83727ec4968d0f02e4fffcdf94568aeb4decb73274550917f04b7ddac7d343d147de2ddbab7d4e0438008a1857651036bbd7ff1598449a6e6c40d4d548763f0b0383f9cd659d25a7b81a83449482598878429528055f31bcf09a03f9720f6f7241d625809043b704952196faf59afafdb8685da530185c09cacc1a86505b82c9dfa18c75fe623c16ad1f9a3c2f96a99d1f86d0554cf0ba2c73e8f6e5a5b93791320f15130196d9c12e2a9f62ceb949961b8e0778f90dad93602f8e4833fed853524733a5a47d862d5021012781723266b4f947cc81e5da3377f38a150c296ccc2382dbdee38ea7932863f2cc3e58e74ec308a5212c40b6588b08381c31ac05a1869aecf19bf1b097f4c9eea5fbb1443ec110e20128fa7c410b5ef75beb1e5d2b75c4280516851526987d1ac3ca063a106a0d3b00d9147b981c2a7d6c547d8255e964efa5cdfc8f2d0d6f85ed43f65003ca394d7fdcd19ee1b258867d58b39d62edf695f6eaa1b5b188087dd5e7c2da8d32da9de0cf76ae7ba59dfbb0c328fcaf790fc613ce92d35aa7568a84525e9fb6c8d5330589fa0ee208ce8b4dfcc8ab89381243ac6d42631c0cfea207d0e7add39df31fc5f499a25c11b529d993854385962d77bf8f1bde19d1a3ddecaef453b132c1a26ea86f63088ac662f7cdc5564ac73 + [ARIA-192] # ARIA Test Vector PDF @@ -22,6 +30,11 @@ In = 00112233445566778899aabbccddeeff Out = 26449c1805dbe7aa25a468ce263a9e79 +# Generated by OpenSSL +Key = 0123456789abcdeffedcba9876543210f0f1f2f3f4f5f6f7 +In = 000100020003000400050006000700080009000a000b000c000d000e000f0010001100120013001400150016001700180019001a001b001c001d001e001f0020002100220023002400250026002700280029002a002b002c002d002e002f0030003100320033003400350036003700380039003a003b003c003d003e003f0040004100420043004400450046004700480049004a004b004c004d004e004f0050005100520053005400550056005700580059005a005b005c005d005e005f0060006100620063006400650066006700680069006a006b006c006d006e006f0070007100720073007400750076007700780079007a007b007c007d007e007f0080008100820083008400850086008700880089008a008b008c008d008e008f0090009100920093009400950096009700980099009a009b009c009d009e009f00a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b000b100b200b300b400b500b600b700b800b900ba00bb00bc00bd00be00bf00c000c100c200c300c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d300d400d500d600d700d800d900da00db00dc00dd00de00df00e000e100e200e300e400e500e600e700e800e900ea00eb00ec00ed00ee00ef00f000f100f200f300f400f500f600f700f800f900fa00fb00fc00fd00fe00ff0000010101020103010401050106010701080109010a010b010c010d010e010f0110011101120113011401150116011701180119011a011b011c011d011e011f0120012101220123012401250126012701280129012a012b012c012d012e012f0130013101320133013401350136013701380139013a013b013c013d013e013f0140014101420143014401450146014701480149014a014b014c014d014e014f0150015101520153015401550156015701580159015a015b015c015d015e015f0160016101620163016401650166016701680169016a016b016c016d016e016f0170017101720173017401750176017701780179017a017b017c017d017e017f0180018101820183018401850186018701880189018a018b018c018d018e018f0190019101920193019401950196019701980199019a019b019c019d019e019f01a001a101a201a301a401a501a601a701a801a901aa01ab01ac01ad01ae01af01b001b101b201b301b401b501b601b701b801b901ba01bb01bc01bd01be01bf01c001c101c201c301c401c501c601c701c801c901ca01cb01cc01cd01ce01cf01d001d101d201d301d401d501d601d701d801d901da01db01dc01dd01de01df01e001e101e201e301e401e501e601e701e801e901ea01eb01ec01ed01ee01ef01f001f101f201f301f401f501f601f701f801f901fa01fb01fc01fd01fe01ff0100020102020203020402050206020702080209020a020b020c020d020e020f021002110212021302140215021602170218 +Out = c2bdb18394f4ffcc53ddb23fd9bcd163f83140132989e9f496307470018a677aa185268f8a8586a6a2e05535c88a6c97023ff7cfeb1f618e56686646aa84539ebfaf9b842c7b210ed4476213c6f58e1ac63aadee5c6fb734862ea308d9107a2171658c285540f762a6abd1f4ae5995a7bc82bcc0caf7812223bc9f69c92ac13f68f6e532d79c702cfe226484f98d78dedb4bccf65a9e6273eb45cd5777efa2b01a94202cf2d1be63d75dfab657309c208b7067f790d3645c282e613717deedb312b15868c1d9f0478bf5c60e0288f0eb850beea3b3eb371bc8a0be4e2e0e8cba7e749c5a0ca0bdb49eafb91e2fa2fc292fd899ef15eab35311758ca31d0b25ed82a479f4440543b318a0cab81baecef7a4ec36e0e6930653d81eec6b315dcc599dc5477190956bb8c12be17ce6ffac64f17711ff99fbb665035e64ff7e69513b324f00a8b783884e2ba5a3d1336db7c413efb2bc556152bcf47b98437dce58daf5c854e8ad83f1b078e0e4123d03f4ebd289a974ed7c7469f5ee12c58d2380e94a39de17d12fcba965ea5bf9dc2225522dc185081cd23d1e09053b8ef64323e15f931b234d96323ca120535ae5451151f76ada066967b8ec5152c2ae428fff1edbcaa9323182d8c44202ef9da9acc0e26c823d9232cdf81eb0a1bc9ba1ef134c83cf1e0dcf84d53bcd1aa06095d3b1344c5fe6ed571d4775669b08fea484ce9c805f670aad3ddc6149037679a545bdb1a9de74253861aef7074df5adb11d926fc774a281245462669915d93cfc0eddada102121132d880202ba0a5410f2b1bbed04c1864103bda726973513bef520be6b239f35c627fbcaa9c746413cd7c0be2d5e2c84c5547d78bdcb18e1487875bd6af303e3053ba15b4041bb4d1a28818dcaee0bf90bafdc3f5ff06d839e2646c333b82eed6a0222bbccb61e9a3898e0d643afca54e076a31b1273a9b5f15f9f14f6c0a1d385863b0773498e082db93fd495df5d27e0bc1b7145f9a06ab69a53bb1c61ae2be00cddb361631f1fb46b946297d78468827a3ab693c7d3b8710c6c502b433522d3e16efe463e964a5549923a1b62d0c7439f3c72f81417fb3aa5c9cf7caef59785b3ef62dc94f32441a11be7804c0f36adb09f845076f17508bbbc40bb1f32b7d739e4bd8cc1cab276438b0d2f2b18e91edbf5cc8795000e937b7edbf6575917fd61c39d92b0f299bcf620a6137e0c361a62277fb1e4b74749d0c6c3cce8d196563243059c67a2491ec7bb00b972561b40842cf590eb2e974a711f764b8909cdeab045da699a3bf7d63280008b071ffe57367a1ecb7c26095919bcfef676725ccde1bedb71047aa51efdccd4c10412b2feb78c2296077bdcc41571952c16e0bb5d3d9668beff5699a3a91106360cfe601de76f8513b0ae85b30ed561f5cb59c545a18695493b76284d0ccd9a1013222db8e881f929e15b1b1cca49c0bf5691e36b416408365e7d25198654ea3f00e14dc2f1e447b84cd17e93d70efba + [ARIA-256] # ARIA Test Vector PDF @@ -33,3 +46,8 @@ Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f In = 00112233445566778899aabbccddeeff Out = f92bd7c79fb72e2f2b8f80c1972d24fc + +# Generated by OpenSSL +Key = 0123456789abcdeffedcba9876543210f0f1f2f3f4f5f6f70001020304050607 +In = 000100020003000400050006000700080009000a000b000c000d000e000f0010001100120013001400150016001700180019001a001b001c001d001e001f0020002100220023002400250026002700280029002a002b002c002d002e002f0030003100320033003400350036003700380039003a003b003c003d003e003f0040004100420043004400450046004700480049004a004b004c004d004e004f0050005100520053005400550056005700580059005a005b005c005d005e005f0060006100620063006400650066006700680069006a006b006c006d006e006f0070007100720073007400750076007700780079007a007b007c007d007e007f0080008100820083008400850086008700880089008a008b008c008d008e008f0090009100920093009400950096009700980099009a009b009c009d009e009f00a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b000b100b200b300b400b500b600b700b800b900ba00bb00bc00bd00be00bf00c000c100c200c300c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d300d400d500d600d700d800d900da00db00dc00dd00de00df00e000e100e200e300e400e500e600e700e800e900ea00eb00ec00ed00ee00ef00f000f100f200f300f400f500f600f700f800f900fa00fb00fc00fd00fe00ff0000010101020103010401050106010701080109010a010b010c010d010e010f0110011101120113011401150116011701180119011a011b011c011d011e011f0120012101220123012401250126012701280129012a012b012c012d012e012f0130013101320133013401350136013701380139013a013b013c013d013e013f0140014101420143014401450146014701480149014a014b014c014d014e014f0150015101520153015401550156015701580159015a015b015c015d015e015f0160016101620163016401650166016701680169016a016b016c016d016e016f0170017101720173017401750176017701780179017a017b017c017d017e017f0180018101820183018401850186018701880189018a018b018c018d018e018f0190019101920193019401950196019701980199019a019b019c019d019e019f01a001a101a201a301a401a501a601a701a801a901aa01ab01ac01ad01ae01af01b001b101b201b301b401b501b601b701b801b901ba01bb01bc01bd01be01bf01c001c101c201c301c401c501c601c701c801c901ca01cb01cc01cd01ce01cf01d001d101d201d301d401d501d601d701d801d901da01db01dc01dd01de01df01e001e101e201e301e401e501e601e701e801e901ea01eb01ec01ed01ee01ef01f001f101f201f301f401f501f601f701f801f901fa01fb01fc01fd01fe01ff0100020102020203020402050206020702080209020a020b020c020d020e020f021002110212021302140215021602170218 +Out = 61df80fe8ca729460c041652b3a7bb49f19c83cb590e9a641c05fe3aced9ba65293cb889e52dc97421eea0e5b9c287940ef0e421e48a9a2dd7b26b55f4886ba70c7cb7161828d160165893e479569a76df3ab618dc3e0c2525b903355bbc6bd97e02ac5770bdbbb4f1b5537b80f2b954bf88fca6ca1512b4db4a2b78e7f854976a81476b4cf689687203f9885dfff122b0a4f86d95e5ba9329dfb3009276cd46b3f0cfa2b6208032419b7b829f960ad8b403bac9f94503656c5a408f998dced8c695b71fd5d17068c95b78aa7dfeeee75447de9caf77867ab4cfb78d46b6b5a6fdf76cf661aa7afa6f594747be6cd0f8ee9d81f7960477960ffdb6fe28eee7b8e2df290aec8041438431c556bdbb1424ee04d1c9b1145f0b87813889d55a93edf7091630cd71c0c16433e79bc41511301cfc47bc37d13009e02f034a7f9ca6d4b1d11d14b403e6c67b329945ace42b370ed678706fde42ae20872e19c4323e916d4495ffab39bd9827f6dfb0fa96dad0e71c668e5ef589a53b8eb3dff98c72c3450444a8f2ac6d4ac1b581bce2a2379f582aa753173b0690e48e043da2760e66a429766a3fb6e3536a4f6c851d3aca9c8b6dbff3bbddd31f9ffd66b864ae9a6cb41f13cb570253d40fe5ee9f800db6ccb627ad6877b1d2df503f45618f8c955c29cc09e2df3fd12beb41633f017132ff10ba0bf0a3e2f36ec1439ae2e1af3b2ff29f5a1983f6fbf029a0b08d8d06babbab7863c8106b488c04f629350296645d317f930a6b735f52913badccbbbecf8467afd19171f73c93417bc12b2664893c21cf70334de00e97c31b39598512fa60796b3a131bae32d57beb579d9b4fcf1b8acdc68d738ab4a73b6de0bc2ae2dcb289c6fdc74212c110be6521728e14b6708114b9ab9b1f25d275ca97010abeb44fdd64a2255169452a2500721d1940b50f84725eb298177a7fb87d9e6322e5c548bd4a524824dd2dc46a1e01ce9ebd9dae4890ba3b76ae462e28a5f1c9b213805555326ce2637a430cba0f93a48f631fc01f38adeab3ff02fce667f545ba318fab991abb19329d5bdea65000f2600c874afb9491cfe59b5cb94d74c84b72a74a359a75d79fc0aa71d4c1cf77fa1e8c4eb1176adef80fc5fdefd9eda2a652148845a38eaca5bb23a29eb8e8509662884a38bc3499371785f8346e5ad4f623b7ebe284dc13da0727c7de897c6fdc28058f4a0c390b242a264cc0b59bc36fcd17f194871b9f20caf51d443de28033c5922ba2da8fb3c76a1c4bfb04b2ed2b39c8e17dea63973b9ebd6ab6ed2b8dae984473ed8d3585c2d9f4fb0ccd53277aec98fbad7b6df19e9f906409cbe7dff0f093dd07cd50f0be55282a9c48b0c2d2e5e65889c59f76a7884e8f84589f6886a13f2187877413fa8f17a31a8c40ef3a6fad230684f7e96c2304503522d817ee52b5c5c8a297198237c3e3c726bc4263be0ba1a66146d559a439b8ba3a17b9ced9801052a20f2dd80b6c1458a96d9175f011a7d2 diff -Nru botan3-3.7.1+dfsg/src/tests/data/block/blowfish.vec botan3-3.12.0+dfsg/src/tests/data/block/blowfish.vec --- botan3-3.7.1+dfsg/src/tests/data/block/blowfish.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/block/blowfish.vec 2026-05-07 01:38:28.000000000 +0000 @@ -247,3 +247,8 @@ Key = 00112233445566778899AABBCCDDEEFF In = E22D6ED893F299EB7BB5DA56FECEBFFBC0AECC3CC59AC717D360C6B3B0141ED8B226DC10E8EE963BB83B76E95454BC8A41640635BF793CDB37357F8DDCBEAE2D Out = 499DB1464EA293B246A429CD198666EDA5EC02B9E04480B1D7AA64A5B8DECADD0DF49DA89D2B83348179EE0340279C78D027E6D8BB6BE5EC29F2DBBFA7B0B8A7 + +# Generated by OpenSSL +Key = 00112233445566778899aabbccddeeff +In = 000100020003000400050006000700080009000a000b000c000d000e000f0010001100120013001400150016001700180019001a001b001c001d001e001f0020002100220023002400250026002700280029002a002b002c002d002e002f0030003100320033003400350036003700380039003a003b003c003d003e003f0040004100420043004400450046004700480049004a004b004c004d004e004f0050005100520053005400550056005700580059005a005b005c005d005e005f0060006100620063006400650066006700680069006a006b006c006d006e006f0070007100720073007400750076007700780079007a007b007c007d007e007f0080008100820083008400850086008700880089008a008b008c008d008e008f0090009100920093009400950096009700980099009a009b009c009d009e009f00a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b000b100b200b300b400b500b600b700b800b900ba00bb00bc00bd00be00bf00c000c100c200c300c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d300d400d500d600d700d800d900da00db00dc00dd00de00df00e000e100e200e300e400e500e600e700e800e900ea00eb00ec00ed00ee00ef00f000f100f200f300f400f500f600f700f800f900fa00fb00fc00fd00fe00ff0000010101020103010401050106010701080109010a010b010c +Out = 91288b2fa6b357598f37c31559f11f3a7976c3d70f6ead9c66db21148f1ddc22857b299be03a377d406321e0fe7d899a63248287027c1ecd22ccd811b1aaf6de6548fd32f00b1bbc183bba1b4333e61145b9c786d63753e3eb45f77420850cdc3983d04ed511db0e5efbb54129c9a430b4cfe9871cf0c3c69902ec375916563e458b84d3afba5f391060fc4fc57ea7567d37311f7b54334ce4516c707245f8d37bcf893612a8c3840163ea41e804d2b15d1b43166d68b3691eb5b331fc3c82f0c3e854f16fca2e8ceb06e4da4ecce50719e40bf99aa29bb435488ed1b6b5551b4fe323f3c7ac0cbd1e46a5a465df5068dfd17d5fc2e4f0832ae59deea43ed853b822efd55d8a9b0e916f682066fba9ccc1b37624bd88fcbb9ca4f36d4ace41340fa7ed05f7f5dc70597b16cace005760a306f36958b7fe49efeddbd21500b5248508526c32e5df6056f6b25fa0bdda070dab37032bd25356bfa61c9720deb7bf7f6dcdbd6e0ed4e942431ccbe76323ceca207b19c36c561708ef75c0ef35aa5d93441aa029086b31d24c6e1ec8d9a10400fdce154fabed4c07c2e3ca7999c2a888ebaae8b1a36dddc478a8910a813230523471fa41b868adabe4a9bcdbee22ee331b08a7b29f41e2dcb21c4e7bacddfa5e741f0604b0b791485f0f2cfbd834449d73fd7fd3c6a97fbc6d00bcdad46d199ad5be64bca5f016323f0b0e2af863da74355b0f4079e4f66532831ea866df0a8e7c5501862a2343 diff -Nru botan3-3.7.1+dfsg/src/tests/data/block/camellia.vec botan3-3.12.0+dfsg/src/tests/data/block/camellia.vec --- botan3-3.7.1+dfsg/src/tests/data/block/camellia.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/block/camellia.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,3 +1,6 @@ + +#test cpuid avx512 gfni aesni armv8aes + [Camellia-128] Key = 0123456789ABCDEFFEDCBA9876543210 In = 0123456789ABCDEFFEDCBA9876543210 @@ -23,6 +26,11 @@ In = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF Out = 25DD9EB9DD67FBC6E8431F56F4FBE651 +# Generated by OpenSSL +Key = 0123456789abcdeffedcba9876543210 +In = 000100020003000400050006000700080009000a000b000c000d000e000f0010001100120013001400150016001700180019001a001b001c001d001e001f0020002100220023002400250026002700280029002a002b002c002d002e002f0030003100320033003400350036003700380039003a003b003c003d003e003f0040004100420043004400450046004700480049004a004b004c004d004e004f0050005100520053005400550056005700580059005a005b005c005d005e005f0060006100620063006400650066006700680069006a006b006c006d006e006f0070007100720073007400750076007700780079007a007b007c007d007e007f0080008100820083008400850086008700880089008a008b008c008d008e008f0090009100920093009400950096009700980099009a009b009c009d009e009f00a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b000b100b200b300b400b500b600b700b800b900ba00bb00bc00bd00be00bf00c000c100c200c300c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d300d400d500d600d700d800d900da00db00dc00dd00de00df00e000e100e200e300e400e500e600e700e800e900ea00eb00ec00ed00ee00ef00f000f100f200f300f400f500f600f700f800f900fa00fb00fc00fd00fe00ff0000010101020103010401050106010701080109010a010b010c010d010e010f0110011101120113011401150116011701180119011a011b011c011d011e011f0120012101220123012401250126012701280129012a012b012c012d012e012f0130013101320133013401350136013701380139013a013b013c013d013e013f0140014101420143014401450146014701480149014a014b014c014d014e014f0150015101520153015401550156015701580159015a015b015c015d015e015f0160016101620163016401650166016701680169016a016b016c016d016e016f0170017101720173017401750176017701780179017a017b017c017d017e017f0180018101820183018401850186018701880189018a018b018c018d018e018f0190019101920193019401950196019701980199019a019b019c019d019e019f01a001a101a201a301a401a501a601a701a801a901aa01ab01ac01ad01ae01af01b001b101b201b301b401b501b601b701b801b901ba01bb01bc01bd01be01bf01c001c101c201c301c401c501c601c701c801c901ca01cb01cc01cd01ce01cf01d001d101d201d301d401d501d601d701d801d901da01db01dc01dd01de01df01e001e101e201e301e401e501e601e701e801e901ea01eb01ec01ed01ee01ef01f001f101f201f301f401f501f601f701f801f901fa01fb01fc01fd01fe01ff0100020102020203020402050206020702080209020a020b020c020d020e020f021002110212021302140215021602170218 +Out = 6895729d9a6c8247297887d1a7803de4a1224c8e78c5fde10613cdc2b9bcfcdab0a9043b243920442c435b1b33da6133923ff880b76c3ec346f2242ec65f55471f77def783b891d881444d5dbc5795c7ba922efcc41d48922322937b93e3f460213bc417f84c5f3574f5ce68da35b53f9be57feb14294778f3dc9b871ea04f84b2261df83711dc226d3be8c159a41879effa68ef65b43f2699aeff562ad45b903ebf09bedbdf949f443ce59250dca3dc40419c826f7beeecd7fbf666547b1023361462156c6ce369336ae56a941dc1567e533e692c34825fb9bc8181cf4e4399e58759a5bf34d9dcb7c76bd2fd58f839d13750fc32f1dae89a622a1cf0b05de5e05922f590157eec70319c542c6cc2b78a1a6b6181ca3dd8a60e61d806db0c34588909435b094b58ffc599ef47b8f153cc4085e3ed1d3a45dfa3a35d610e61e68007397619a3db392d457360e8d257fc0d73f4af05929fd612c45891c5a6a01c7158a1a025612c4ff8903f85069dc3c7878e73a90a460f976358bd3153ba8adc639d9f81430676ce03b66c6a9985af1398e13b3c0020582a49771679ea8c54014649a01393c4858db7626c1f0ac1e9baed94d23f6d7c2caadb7758d64aa7d96b266b482006a5d77df8047d5c85ba4e938fd3e2c4afde57f5e32a6d201371524544d456cc93fa0ccd9830c9f28f36e977e7250a519ddf97522d353c2582e7f1f36f85889c3b07e77bda56e0b635770c4f651898df254ea20f31341685532b538cf75272826da368062b2636dbf3f351060722c5ce798384103cf56fee787552f8cbe2aab6c87f3dae35303fc705ed5c35cfd5637a33de04c115058441b57ef1d504c071e9b5b180ad658e3e661c58013abe072a9e8baa9906be8e1aa9d041d894e4813cd9fe4b57917f3090cd3f57b37941c2b1dd18f1e6fbb0c05a1c788b9ed26f9ee50cfb7602aac21d8d2d5d30bf6b49e940ae8fd3be13f35a13f791325c64b507ca21b9493b5b23cfd83fee611b6c75f8c8868e241f04ee34b61cf9ebf8358dcc7c60f56803c6eabea27fcbea0f60ab9b0e89b6597c6ec67876f70225a7d7990b538a3573d043e510d1ff873d11751b1037b13fed79205466a4adb98742bd8fa31dae75ad3a8c57f811e9dbb8e34ce7aa63c35b278451f3867f655f6e9783a999fd56dd45c72b90edc3867df9be34a5ec747d45e991d54486f1d00762c4937615ba43f3c386fc0d2678b01dc511a327509eb5c2cd1bcaba8a726fd284f5affac6c0fdb37805a38f14a06879f97119fa0632d86820dfef2471e642cea2faa181bd0ff117314b89e9e90620769f0975a6c204ae38f836d775832208f5242f0568e6f10a8b112c4dd73b35baa840c21d2e45b7bfc6d18ef3e44f0f162a52c754b06c861330561ece0786788053a4530f4075153d512d27a0f27a5fe81927c573a1fcefca73f060c7aa61fd6dd4c7ee596dec2b9a50621ef21b9c6128dd4d7edca68770909c003edf6f35397bc21544e6 + [Camellia-192] Key = 0123456789ABCDEFFEDCBA98765432100011223344556677 In = 0123456789ABCDEFFEDCBA9876543210 @@ -36,6 +44,11 @@ In = FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFE Out = A2F5A98929658AF4A9700B9923DAF014 +# Generated by OpenSSL +Key = 0123456789abcdeffedcba9876543210f0f1f2f3f4f5f6f7 +In = 000100020003000400050006000700080009000a000b000c000d000e000f0010001100120013001400150016001700180019001a001b001c001d001e001f0020002100220023002400250026002700280029002a002b002c002d002e002f0030003100320033003400350036003700380039003a003b003c003d003e003f0040004100420043004400450046004700480049004a004b004c004d004e004f0050005100520053005400550056005700580059005a005b005c005d005e005f0060006100620063006400650066006700680069006a006b006c006d006e006f0070007100720073007400750076007700780079007a007b007c007d007e007f0080008100820083008400850086008700880089008a008b008c008d008e008f0090009100920093009400950096009700980099009a009b009c009d009e009f00a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b000b100b200b300b400b500b600b700b800b900ba00bb00bc00bd00be00bf00c000c100c200c300c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d300d400d500d600d700d800d900da00db00dc00dd00de00df00e000e100e200e300e400e500e600e700e800e900ea00eb00ec00ed00ee00ef00f000f100f200f300f400f500f600f700f800f900fa00fb00fc00fd00fe00ff0000010101020103010401050106010701080109010a010b010c010d010e010f0110011101120113011401150116011701180119011a011b011c011d011e011f0120012101220123012401250126012701280129012a012b012c012d012e012f0130013101320133013401350136013701380139013a013b013c013d013e013f0140014101420143014401450146014701480149014a014b014c014d014e014f0150015101520153015401550156015701580159015a015b015c015d015e015f0160016101620163016401650166016701680169016a016b016c016d016e016f0170017101720173017401750176017701780179017a017b017c017d017e017f0180018101820183018401850186018701880189018a018b018c018d018e018f0190019101920193019401950196019701980199019a019b019c019d019e019f01a001a101a201a301a401a501a601a701a801a901aa01ab01ac01ad01ae01af01b001b101b201b301b401b501b601b701b801b901ba01bb01bc01bd01be01bf01c001c101c201c301c401c501c601c701c801c901ca01cb01cc01cd01ce01cf01d001d101d201d301d401d501d601d701d801d901da01db01dc01dd01de01df01e001e101e201e301e401e501e601e701e801e901ea01eb01ec01ed01ee01ef01f001f101f201f301f401f501f601f701f801f901fa01fb01fc01fd01fe01ff0100020102020203020402050206020702080209020a020b020c020d020e020f021002110212021302140215021602170218 +Out = eb6b8d41ac6f151c5928c7c41f0d3f8814b945da6bbf8c6c1cfc5ef423de070d4b58dc410612652a9fa08206945fb76ba8338ab2c24b54a46c931ba2a0d27277db84cfce802ea8df6c63b96fdbfa84d21a3fe518b1b845c92e8f468a175515ab4658538cd227d323fc60bf14648066d841e05d6bd6b9fb6f859f0045cccd6128acdeebf455932c2ec1a7643f0a321c48bd7f76991b0b02f1c3bf5539a18d7fadbd07f0249a5818cdc408d6144653f919bb7872ed1381c3470fda6ccc9a8c13b22fdb2739ff12c0fc62e9ae3a137bc180045c328b3ad6e913fcb86a5620d16adafbea6a2cb65c722788c27aa235a694c08ff5ac498c9ec838c9f73b3715eb192ad265f1d5775f4581b71149d871376869aff6b52648fd88049b98945ea4052b6bb8f18338ac3e3ffb21c1a3695ac5eb835440bea51a1b439047a1c5649b7bec8339407f11e53c9dd2bc3b89d3fd27a555549247c7d7916f6de0cd60535ae7ffbb62ec9a0898a77080f95d2a9099f9fc7538defd3dfcbd5a2eb2951e46a715b78ab1c20ca044f05a1f0cd886708b93b004d80bb35e8fd5fc6c572aa1cc62a92195e0c4b89f4dfbe964f018ba02fe46308099fbdbac8d17bb7630100ae859e0845c34fdc99f1a0c16d8efe442ae515c266de530daa59aef287d616d500e76144da2f257feb84b5c6311ecf303dbc4e22c06e01779b3821cc04cca011d701feeee0131fe23c86002e27f819ae62d4f5685c9c11568f9a48bf22f0bacb2094d04ca0d40089f92e7e9580d475f22b41115acf180b3b9a0cea958a2a4d84220a680bcc244695850745a33f43db7a4f7951f3c63de98875d0fb08060dd38825841995d5b0356d71fa428d70fee8cc7aff203a1e2743b0abecec67692ef6e14200c7a2247fb0c607b054584a18e657f56a9767113aad16348f14a03d653db96d38deab440dbeccae033637dc57172566bcf081b6ec58f8471cdcf91eedd843928f7860ae9c304ba61c4ae306fc72b112b78b476a986de88f0320ba05e94cabcf21483b98cfe3256c6e440e0ee9dcf3d2cfd903e411dffe2d76ae3d19a959ec0b259f6e7a08f21a4fc694fe2e80bf487e07eb737b2f7992afb79d56d3734fd62472599f3bdd3028f9e3a733dc19b2d311c8e3f152f6edb5f6a3e4485aed92f0b2a6a82e3645b31f19d984554e768e9eb17014a16bf368aabf04e6ccb98abafc65e95bdd62af7c017df9ae3539b216ea1dfaddc8eebfc8f1f41797f4b8f00365875a8f8401ac874dbfc472c55562d5a1790380016040f577b25ba8c993f1831c8c8d5dd0503c589edc026b555857129547eaf548c7a1ae5515c9bfbafd7813410432d0427d9bd01380f03dfdf1eebaeaddd07edc93f89d09a4026e5bc846ec7124cb054caa448ad916d6d5b2c3f30c596fc144aa6852e677b2f3ce168aa6267ba686ebc585f62373308afdbac5fa4690593d0ed692b20c4820024ebff44e3ae36b4a217a1a823aa1b305648ba247f3e042b4a2d0796 + [Camellia-256] Key = 0123456789ABCDEFFEDCBA987654321000112233445566778899AABBCCDDEEFF In = 0123456789ABCDEFFEDCBA9876543210 @@ -57,3 +70,7 @@ In = 0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000020000000000000000000000000000000400000000000000000000000000000008000000000000000000000000000000100000000000000000000000000000002000000000000000000000000000000040 Out = 396154111ADEFC500CF6E5C99038BC179CDB269B5D293BC5DB9C55B057D9B5913D4B2CDE666761BA5DFB305178E667FB6A3F25AAB7E92D9CF378E5D9C040F26B7C92854D801A1648F65CA81813DDBF83BA664AC39855518DFDEE10D1B3111FAE7A6985778D3A66E97F23E01F0D0E45E78B1F247802E47C91BEE2AA34ECFD7A01 +# Generated by OpenSSL +Key = 0123456789abcdeffedcba9876543210f0f1f2f3f4f5f6f70001020304050607 +In = 000100020003000400050006000700080009000a000b000c000d000e000f0010001100120013001400150016001700180019001a001b001c001d001e001f0020002100220023002400250026002700280029002a002b002c002d002e002f0030003100320033003400350036003700380039003a003b003c003d003e003f0040004100420043004400450046004700480049004a004b004c004d004e004f0050005100520053005400550056005700580059005a005b005c005d005e005f0060006100620063006400650066006700680069006a006b006c006d006e006f0070007100720073007400750076007700780079007a007b007c007d007e007f0080008100820083008400850086008700880089008a008b008c008d008e008f0090009100920093009400950096009700980099009a009b009c009d009e009f00a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b000b100b200b300b400b500b600b700b800b900ba00bb00bc00bd00be00bf00c000c100c200c300c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d300d400d500d600d700d800d900da00db00dc00dd00de00df00e000e100e200e300e400e500e600e700e800e900ea00eb00ec00ed00ee00ef00f000f100f200f300f400f500f600f700f800f900fa00fb00fc00fd00fe00ff0000010101020103010401050106010701080109010a010b010c010d010e010f0110011101120113011401150116011701180119011a011b011c011d011e011f0120012101220123012401250126012701280129012a012b012c012d012e012f0130013101320133013401350136013701380139013a013b013c013d013e013f0140014101420143014401450146014701480149014a014b014c014d014e014f0150015101520153015401550156015701580159015a015b015c015d015e015f0160016101620163016401650166016701680169016a016b016c016d016e016f0170017101720173017401750176017701780179017a017b017c017d017e017f0180018101820183018401850186018701880189018a018b018c018d018e018f0190019101920193019401950196019701980199019a019b019c019d019e019f01a001a101a201a301a401a501a601a701a801a901aa01ab01ac01ad01ae01af01b001b101b201b301b401b501b601b701b801b901ba01bb01bc01bd01be01bf01c001c101c201c301c401c501c601c701c801c901ca01cb01cc01cd01ce01cf01d001d101d201d301d401d501d601d701d801d901da01db01dc01dd01de01df01e001e101e201e301e401e501e601e701e801e901ea01eb01ec01ed01ee01ef01f001f101f201f301f401f501f601f701f801f901fa01fb01fc01fd01fe01ff0100020102020203020402050206020702080209020a020b020c020d020e020f021002110212021302140215021602170218 +Out = 0c6e38c794f93c9981526c8ccf777f52bed447212a4c2f62c52c25baa20883ab9fe24ccc7a602bf69052f6f5d7c80372f3810a553293a8a20763a8ba4d4015ee036552a7c3d631a0652eccf3a2a4d54708d9e2a44a72b07f9c7df47d178883451370078380b6902e7b6e361b0ec20596816317f160b2fe0c9fc6fc5b786dcab6c75eb518297021f050701e76d843d9618ed86302ae407f3e1532214fb10bc02abd91892f1700c029a6270f191ee043051da779b3cbec22747f8ff1a7c303d204db38198e09afee5f24eb4d425b72bf7f998c2daf88cafe0f160b7178f8f1b643d0656b9afb3a4dc9e698ec1980cd7439ad4e41d7da4f754387b92a63ae444dc7c2e8ce62883c25e9ccaa3ff96790671c64305b8e530dfd96ce91b79c1b3e5d5bac6f93041084d8ea3c513db3e408168820fb8f8ac6ee1d862c58af0a75ca12354a772c62fdcf81055dfa3c4f8ed3922d58879cdb9f52b10364165949d5e86bbdb8e6d970b11dca435674042cb5cf058e96db150471617bd6ee1cbd7e0176d9dc8d33de11ff2300e8d5bcb66077e83c54eabbc9505f312fc2a568f764dd6cfb617c6fe10e7fd6383102a17aa7839ae12e2e4567b1fa13507e1688f95408ea31d80fd97ca437b0f2c0cd7cfb594b813f0251639c9a57d00682fcd0e6405593b5838ca330361b27b2ba14a747be06e67c27faea64064890ed16e21d3791f105af1c8c54a3ee207e4170f624c8562b01d421628516b29f0709a61eb37a84c31e825f9b3edeff9326996428180c13ac87d234d08b2b6426ce3f2aa212728ddd82973d4001f42fa160fa2c4fac80e20affcf023789a6901a149809e10dbdb34900d0ef0d5103ff488d0b8d0d284e2803e4d7dd9d8f2eaa6054f36aa83cc01fafb3ae068c29741da7f253d86eca5394111e378ca85482246c09cc14e2644c29ba27559eaeabe0dd2b81305ccfab15247423ea34c9faba0264c4785eb1cc721aaa3d86c16d3fb30d621e6c368a87bc2835b595a342aecb364adaf6795189fcdf7b661f58f4d7e93e44c2a1b346c89e3a46fb088447c6129d2217c82b7fc7c0b9064f27dda40f5ae256711f7ed3c65312d126db596df27bb69998efb6b450508be21197d2dcbd04ad8b133f4c1175aab449a27f3c3f34deef56a98ce7df6950f66de535c768da9964ee60f38f9c689a341cd45d5d4730985cde9f3183644cae02944284d5a99a3f36dd7b7585c00bd40a31b706b0aeafd54f184846f2c44c3631dcedc8e1005bec96dbd45972842aec84ca4d2f165450ade3a4b4fbee0b44af1d860125fc6164e6ba8bed891f1a418eb7347c8a214aa41e58987e32b01cf1e37e49f4f7af841d9b2714b32f14b9d8482745a2b59869f1af1b7ce77e3c076040ce38f749ae107ceece2b032f7a473e3c1c7068b1b82483726ebe98618e8994792837c4d3cd4c91f1259fd47d7c703e2138f9408ed9685ebab0c332bd53990de4c78a05b549098ea056345a1ff09d413564be428b47 diff -Nru botan3-3.7.1+dfsg/src/tests/data/block/des.vec botan3-3.12.0+dfsg/src/tests/data/block/des.vec --- botan3-3.7.1+dfsg/src/tests/data/block/des.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/block/des.vec 2026-05-07 01:38:28.000000000 +0000 @@ -199,269 +199,13 @@ In = 0100000000000000 Out = 6F353E3388ABE2EF -Key = 0101010101010101 -In = 95F8A5E5DD31D900 -Out = 8000000000000000 - Key = 0000000000000000 In = 95F8A5E5DD31D900 Out = 8000000000000000 Key = 0101010101010101 -In = DD7F121CA5015619 -Out = 4000000000000000 - -Key = 0101010101010101 -In = 2E8653104F3834EA -Out = 2000000000000000 - -Key = 0101010101010101 -In = 4BD388FF6CD81D4F -Out = 1000000000000000 - -Key = 0101010101010101 -In = 20B9E767B2FB1456 -Out = 0800000000000000 - -Key = 0001010101010100 -In = 20B9E767B2FB1456 -Out = 0800000000000000 - -Key = 0101010101010101 -In = 55579380D77138EF -Out = 0400000000000000 - -Key = 0101010101010101 -In = 6CC5DEFAAF04512F -Out = 0200000000000000 - -Key = 0101010101010101 -In = 0D9F279BA5D87260 -Out = 0100000000000000 - -Key = 0101010101010101 -In = D9031B0271BD5A0A -Out = 0080000000000000 - -Key = 0101010101010101 -In = 424250B37C3DD951 -Out = 0040000000000000 - -Key = 0101010101010101 -In = B8061B7ECD9A21E5 -Out = 0020000000000000 - -Key = 0101010101010101 -In = F15D0F286B65BD28 -Out = 0010000000000000 - -Key = 0101010101010101 -In = ADD0CC8D6E5DEBA1 -Out = 0008000000000000 - -Key = 0101010101010101 -In = E6D5F82752AD63D1 -Out = 0004000000000000 - -Key = 0101010101010101 -In = ECBFE3BD3F591A5E -Out = 0002000000000000 - -Key = 0101010101010101 -In = F356834379D165CD -Out = 0001000000000000 - -Key = 0101010101010101 -In = 2B9F982F20037FA9 -Out = 0000800000000000 - -Key = 0101010101010101 -In = 889DE068A16F0BE6 -Out = 0000400000000000 - -Key = 0101010101010101 -In = E19E275D846A1298 -Out = 0000200000000000 - -Key = 0101010101010101 -In = 329A8ED523D71AEC -Out = 0000100000000000 - -Key = 0101010101010101 -In = E7FCE22557D23C97 -Out = 0000080000000000 - -Key = 0101010101010101 -In = 12A9F5817FF2D65D -Out = 0000040000000000 - -Key = 0101010101010101 -In = A484C3AD38DC9C19 -Out = 0000020000000000 - -Key = 0101010101010101 -In = FBE00A8A1EF8AD72 -Out = 0000010000000000 - -Key = 0101010101010101 -In = 750D079407521363 -Out = 0000008000000000 - -Key = 0101010101010101 -In = 64FEED9C724C2FAF -Out = 0000004000000000 - -Key = 0101010101010101 -In = F02B263B328E2B60 -Out = 0000002000000000 - -Key = 0101010101010101 -In = 9D64555A9A10B852 -Out = 0000001000000000 - -Key = 0101010101010101 -In = D106FF0BED5255D7 -Out = 0000000800000000 - -Key = 0101010101010101 -In = E1652C6B138C64A5 -Out = 0000000400000000 - -Key = 0101010101010101 -In = E428581186EC8F46 -Out = 0000000200000000 - -Key = 0101010101010101 -In = AEB5F5EDE22D1A36 -Out = 0000000100000000 - -Key = 0101010101010101 -In = E943D7568AEC0C5C -Out = 0000000080000000 - -Key = 0101010101010101 -In = DF98C8276F54B04B -Out = 0000000040000000 - -Key = 0101010101010101 -In = B160E4680F6C696F -Out = 0000000020000000 - -Key = 0101010101010101 -In = FA0752B07D9C4AB8 -Out = 0000000010000000 - -Key = 0101010101010101 -In = CA3A2B036DBC8502 -Out = 0000000008000000 - -Key = 0101010101010101 -In = 5E0905517BB59BCF -Out = 0000000004000000 - -Key = 0101010101010101 -In = 814EEB3B91D90726 -Out = 0000000002000000 - -Key = 0101010101010101 -In = 4D49DB1532919C9F -Out = 0000000001000000 - -Key = 0101010101010101 -In = 25EB5FC3F8CF0621 -Out = 0000000000800000 - -Key = 0101010101010101 -In = AB6A20C0620D1C6F -Out = 0000000000400000 - -Key = 0101010101010101 -In = 79E90DBC98F92CCA -Out = 0000000000200000 - -Key = 0101010101010101 -In = 866ECEDD8072BB0E -Out = 0000000000100000 - -Key = 0101010101010101 -In = 8B54536F2F3E64A8 -Out = 0000000000080000 - -Key = 0101010101010101 -In = EA51D3975595B86B -Out = 0000000000040000 - -Key = 0101010101010101 -In = CAFFC6AC4542DE31 -Out = 0000000000020000 - -Key = 0101010101010101 -In = 8DD45A2DDF90796C -Out = 0000000000010000 - -Key = 0101010101010101 -In = 1029D55E880EC2D0 -Out = 0000000000008000 - -Key = 0101010101010101 -In = 5D86CB23639DBEA9 -Out = 0000000000004000 - -Key = 0101010101010101 -In = 1D1CA853AE7C0C5F -Out = 0000000000002000 - -Key = 0101010101010101 -In = CE332329248F3228 -Out = 0000000000001000 - -Key = 0101010101010101 -In = 8405D1ABE24FB942 -Out = 0000000000000800 - -Key = 0101010101010101 -In = E643D78090CA4207 -Out = 0000000000000400 - -Key = 0101010101010101 -In = 48221B9937748A23 -Out = 0000000000000200 - -Key = 0101010101010101 -In = DD7C0BBD61FAFD54 -Out = 0000000000000100 - -Key = 0101010101010101 -In = 2FBC291A570DB5C4 -Out = 0000000000000080 - -Key = 0101010101010101 -In = E07C30D7E4E26E12 -Out = 0000000000000040 - -Key = 0101010101010101 -In = 0953E2258E8E90A1 -Out = 0000000000000020 - -Key = 0101010101010101 -In = 5B711BC4CEEBF2EE -Out = 0000000000000010 - -Key = 0101010101010101 -In = CC083F1E6D9E85F6 -Out = 0000000000000008 - -Key = 0101010101010101 -In = D2FD8867D50D2DFE -Out = 0000000000000004 - -Key = 0101010101010101 -In = 06E7EA22CE92708F -Out = 0000000000000002 - -Key = 0101010101010101 -In = 166B40B44ABA4BD6 -Out = 0000000000000001 +In = 95F8A5E5DD31D900DD7F121CA50156192E8653104F3834EA4BD388FF6CD81D4F20B9E767B2FB145620B9E767B2FB145655579380D77138EF6CC5DEFAAF04512F0D9F279BA5D87260D9031B0271BD5A0A424250B37C3DD951B8061B7ECD9A21E5F15D0F286B65BD28ADD0CC8D6E5DEBA1E6D5F82752AD63D1ECBFE3BD3F591A5EF356834379D165CD2B9F982F20037FA9889DE068A16F0BE6E19E275D846A1298329A8ED523D71AECE7FCE22557D23C9712A9F5817FF2D65DA484C3AD38DC9C19FBE00A8A1EF8AD72750D07940752136364FEED9C724C2FAFF02B263B328E2B609D64555A9A10B852D106FF0BED5255D7E1652C6B138C64A5E428581186EC8F46AEB5F5EDE22D1A36E943D7568AEC0C5CDF98C8276F54B04BB160E4680F6C696FFA0752B07D9C4AB8CA3A2B036DBC85025E0905517BB59BCF814EEB3B91D907264D49DB1532919C9F25EB5FC3F8CF0621AB6A20C0620D1C6F79E90DBC98F92CCA866ECEDD8072BB0E8B54536F2F3E64A8EA51D3975595B86BCAFFC6AC4542DE318DD45A2DDF90796C1029D55E880EC2D05D86CB23639DBEA91D1CA853AE7C0C5FCE332329248F32288405D1ABE24FB942E643D78090CA420748221B9937748A23DD7C0BBD61FAFD542FBC291A570DB5C4E07C30D7E4E26E120953E2258E8E90A15B711BC4CEEBF2EECC083F1E6D9E85F6D2FD8867D50D2DFE06E7EA22CE92708F166B40B44ABA4BD6 +Out = 80000000000000004000000000000000200000000000000010000000000000000800000000000000080000000000000004000000000000000200000000000000010000000000000000800000000000000040000000000000002000000000000000100000000000000008000000000000000400000000000000020000000000000001000000000000000080000000000000004000000000000000200000000000000010000000000000000800000000000000040000000000000002000000000000000100000000000000008000000000000000400000000000000020000000000000001000000000000000080000000000000004000000000000000200000000000000010000000000000000800000000000000040000000000000002000000000000000100000000000000008000000000000000400000000000000020000000000000001000000000000000080000000000000004000000000000000200000000000000010000000000000000800000000000000040000000000000002000000000000000100000000000000008000000000000000400000000000000020000000000000001000000000000000080000000000000004000000000000000200000000000000010000000000000000800000000000000040000000000000002000000000000000100000000000000008000000000000000400000000000000020000000000000001 Key = 8001010101010101 In = 0000000000000000 @@ -1283,6 +1027,11 @@ In = 1B1A2DDB4C642438 Out = 95EC2578C2C433F0 +# Generated by OpenSSL +Key = 0123456789abcdef +In = 000100020003000400050006000700080009000a000b000c000d000e000f0010001100120013001400150016001700180019001a001b001c001d001e001f0020002100220023002400250026002700280029002a002b002c002d002e002f0030003100320033003400350036003700380039003a003b003c003d003e003f0040004100420043004400450046004700480049004a004b004c004d004e004f0050005100520053005400550056005700580059005a005b005c005d005e005f0060006100620063006400650066006700680069006a006b006c006d006e006f0070007100720073007400750076007700780079007a007b007c007d007e007f0080008100820083008400850086008700880089008a008b008c008d008e008f0090009100920093009400950096009700980099009a009b009c009d009e009f00a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b000b100b200b300b400b500b600b700b800b900ba00bb00bc00bd00be00bf00c000c100c200c300c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d300d400d500d600d700d800d900da00db00dc00dd00de00df00e000e100e200e300e400e500e600e700e800e900ea00eb00ec00ed00ee00ef00f000f100f200f300f400f500f600f700f800f900fa00fb00fc00fd00fe00ff0000010101020103010401050106010701080109010a010b010c +Out = 96f51a28024594c8a7d2a0743658ce2d5ad3e22f0317ff608bacbe0eef1d110fd1c9fd2f760360f7797a6a505791d3175d8f4c1d381afdfe4410a10e54a2cb97b40ecc6521145f45cf833f6d7df8c3b972eba294f046a595774589f268d466ab772c435e7563fd99a3ea513729558793c07d06213362e229212bbea6118e2f41d0549477d7f89c000cacaab64f7ee20e01dce4784e2d466878f14c84ca3ae1a97add095e833f3d1860375099c29ebaf9f540df7a7468237b1cdb379846d91799578427573e5922bd5df010f35b58f436d897ab630a6dadd2000bcb24505bc2ed79b145e40ef000f66b5039a1385c6b881d7e0486c3e38ab31ff4c7e84ade93a0bb5242fce8b6b554a1b304bd6a3a4b70c6ddb6ca64e986971f9d1196a1e683a509b9b7c5306229d8d6802dc47f7fbf407a5fd5e2a51b37aa059cbf38b89da4c78da5d62d78a4e4863eca8f287c42094d33aeb2b89ad63e92abf5bbc6e853f3cf2ebd64ac887a1280f707809d501c5304137e9fffffbe4490d798ff7eae70a7e7e80b14cfb38c67218b6c105267d16aed885f392805edb9c8ab6f97d36cd5c65757aa2791d4f03b4052f9e4c75b4c3f2b885147944c11293218cac1848783178fe47c3fa8a06875602ef4b31d164f96192c36e14ec3edd59fde3da1871654fa77e1dc2519413aa9e09ae3cba63faf18cdcad534616d5f20ecb216a78b418deff0d1d7acef2a46fcab525aadcca60352b0322fa3fe380c04bf + [TripleDES] Key = 0123456789ABCDEFFEDCBA9876543210 In = 0123456789ABCDE7 @@ -1508,3 +1257,7 @@ In = DA0EDCDC26C81F71 Out = 5E54344C7E8CF8A0 +# Generated by OpenSSL +Key = 0123456789abcdeffedcba9876543210f0f1f2f3f4f5f6f7 +In = 000100020003000400050006000700080009000a000b000c000d000e000f0010001100120013001400150016001700180019001a001b001c001d001e001f0020002100220023002400250026002700280029002a002b002c002d002e002f0030003100320033003400350036003700380039003a003b003c003d003e003f0040004100420043004400450046004700480049004a004b004c004d004e004f0050005100520053005400550056005700580059005a005b005c005d005e005f0060006100620063006400650066006700680069006a006b006c006d006e006f0070007100720073007400750076007700780079007a007b007c007d007e007f0080008100820083008400850086008700880089008a008b008c008d008e008f0090009100920093009400950096009700980099009a009b009c009d009e009f00a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b000b100b200b300b400b500b600b700b800b900ba00bb00bc00bd00be00bf00c000c100c200c300c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d300d400d500d600d700d800d900da00db00dc00dd00de00df00e000e100e200e300e400e500e600e700e800e900ea00eb00ec00ed00ee00ef00f000f100f200f300f400f500f600f700f800f900fa00fb00fc00fd00fe00ff0000010101020103010401050106010701080109010a010b010c +Out = 20d7e800b2e3ddd7cb9e9406b374081a57acc1be51a9b8c98585c8124b0df334d1d8d887eb3e235dc4b13aafd0667d9f0dbe15ba073713cd774b240024e764440a6d06e5577f39c6b00ba8cec2b1b1a45ea5d4c43ce2cb79e26fa726a531009cd751fe9ae2ca6eb10b3d3edf31dba428d170032b1691a1d90a446ca343ecaed942e32cf820c61160e55e9c24e6c17296f011eb22983f24e3d04058537c5d26780a032c3104eee6e5047d4137aa0da1f0715680f67bd823dfec6451ec8eda23116315b42e631717d3861f9c5f5e9f34743f8fe4927527011c7d1ce5c12a7b7487ef5defd290810475d32465691ecab361e7b433937aa134226a48d23dc4e8a6d628382fc8812bad0a0f4bb82d30c6a54a8fe7ad3385db66935542da74bd2d1c193996d3119ffe389e9afdc96da06ae9c8416d5da54a0ba3d88689e721fd09d64b15171a3233e8d7b3afe0e6d81483cd537c1394a390326f0811ad930122f8c7feff0e79e06ad3f7ca138857308694c2462ad55f0931a04341a74933ddb59f26120cff0c44631b296df6b9e7d178898795e191423deecc6e03e841f31cb1f9a46b40866457a09fa91d97cfe044738e977f9a8fdcc4f0b907f15a19448acf2e3cccb4684341e4251c7aad4c3b265e4bfa38a1359505c3b2df193289d84493227080a3188ee1595dc88c9f20292477f6dd8cce110a714cccb6000074d7a9f3ee2b6b4a30f8155409e221d350d6404b7df16a02cad651a35a102c diff -Nru botan3-3.7.1+dfsg/src/tests/data/block/noekeon.vec botan3-3.12.0+dfsg/src/tests/data/block/noekeon.vec --- botan3-3.7.1+dfsg/src/tests/data/block/noekeon.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/block/noekeon.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,4 @@ -#test cpuid simd32 +#test cpuid sse2 neon altivec lsx simd128 [Noekeon] diff -Nru botan3-3.7.1+dfsg/src/tests/data/block/seed.vec botan3-3.12.0+dfsg/src/tests/data/block/seed.vec --- botan3-3.7.1+dfsg/src/tests/data/block/seed.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/block/seed.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,3 +1,6 @@ + +#test cpuid gfni aesni armv8aes + [SEED] Key = 00000000000000000000000000000000 In = 000102030405060708090A0B0C0D0E0F @@ -15,3 +18,7 @@ In = B41E6BE2EBA84A148E2EED84593C5EC7 Out = 9B9B7BFCD1813CB95D0B3618F40F5122 +# Generated by OpenSSL +Key = 0123456789abcdeffedcba9876543210 +In = 000100020003000400050006000700080009000a000b000c000d000e000f0010001100120013001400150016001700180019001a001b001c001d001e001f0020002100220023002400250026002700280029002a002b002c002d002e002f0030003100320033003400350036003700380039003a003b003c003d003e003f0040004100420043004400450046004700480049004a004b004c004d004e004f0050005100520053005400550056005700580059005a005b005c005d005e005f0060006100620063006400650066006700680069006a006b006c006d006e006f0070007100720073007400750076007700780079007a007b007c007d007e007f0080008100820083008400850086008700880089008a008b008c008d008e008f0090009100920093009400950096009700980099009a009b009c009d009e009f00a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b000b100b200b300b400b500b600b700b800b900ba00bb00bc00bd00be00bf00c000c100c200c300c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d300d400d500d600d700d800d900da00db00dc00dd00de00df00e000e100e200e300e400e500e600e700e800e900ea00eb00ec00ed00ee00ef00f000f100f200f300f400f500f600f700f800f900fa00fb00fc00fd00fe00ff0000010101020103010401050106010701080109010a010b010c010d010e010f0110011101120113011401150116011701180119011a011b011c011d011e011f0120012101220123012401250126012701280129012a012b012c012d012e012f0130013101320133013401350136013701380139013a013b013c013d013e013f0140014101420143014401450146014701480149014a014b014c014d014e014f0150015101520153015401550156015701580159015a015b015c015d015e015f0160016101620163016401650166016701680169016a016b016c016d016e016f0170017101720173017401750176017701780179017a017b017c017d017e017f0180018101820183018401850186018701880189018a018b018c018d018e018f0190019101920193019401950196019701980199019a019b019c019d019e019f01a001a101a201a301a401a501a601a701a801a901aa01ab01ac01ad01ae01af01b001b101b201b301b401b501b601b701b801b901ba01bb01bc01bd01be01bf01c001c101c201c301c401c501c601c701c801c901ca01cb01cc01cd01ce01cf01d001d101d201d301d401d501d601d701d801d901da01db01dc01dd01de01df01e001e101e201e301e401e501e601e701e801e901ea01eb01ec01ed01ee01ef01f001f101f201f301f401f501f601f701f801f901fa01fb01fc01fd01fe01ff0100020102020203020402050206020702080209020a020b020c020d020e020f021002110212021302140215021602170218 +Out = 082e96e1a6434256b295dd8b51804b58f1234d5b70749ff038ec8378f6c95a59f195158a17fb160469cfae2e519c93564556b912a05cd29c04bdc49ef0f26e8ff68c44c37bf8b23151daf78be61d6903228d837ed2db1a2592580f95c5b5bad3c442a2be00b6bdcc519dafdd8c90b7f48061428d4443dad8f4cf1b7710420cc4d2f09020f64cc050240470a76012e309fd61ed415dfaa31e480d15ae4c9fb059c4ba98ecbd3566513f4972bc29acbc4d87eac68c526b85fa84b847b6fbf7f9c838cac3aa99047285a3ced5f95bf6ab55a5c38202448b85b8cf2c43c89c02e8db6399aa2ffa1236dab1a3d08c8929448a30f47d1b683dfbe1aee2a926eb25c696604ccce0da16cd044dd46198c9db57c71c7a304616c0c1576adb488aadcc65df83ea2ca3b8e801c93ec0e25f6e6959fabc5e967abdd7698aedbdc6091f266cb2858a8618737cb7c3878af1744efac061b2f33dc5b5c3292caf7b99e41d274d7948f6372cc1d5432a6bc38a326d6d5910ad7839ded628520259108732290b34647c8369a5152b711262820dbbd6af65e3966a82a2a89d2c19f3ae0bcabd2ecc0e2f039d094db2281e64d0340e63d55aed935af638a70c4c1561301b970a017addf6feee4b3502112e078cb30f0185a2b9b6ac7e96864b1228a664efdec2723fe8b93bd66ec80507d3f06393bfbb256e240fe9012ff0106fa0fea7dfe3963fc4d2253ccc5646c96307d878e67683783974538d97329cfcf564c4615909e74afa01df0ae596d94103b8acf3f7dd64271b2cead4f38510791898f0125889a874cf538a26f216c59994b8c4047a5f64b440957b61fb220173e425d353f29131a1458bf8f067cb8f9d290482dcf046395562ea444ef7bb140f6d21293130c14de711b34cbc59f7567fe34dadc7249f4f0a725b98179dd29b644475218aba1d7240da1a04dfdee82301b36222b30413c4c1b0dffd723e25dfd1d5237b759db3423f796983a5eca37e9fcadb318f6f4959dc3579b910a60663261b68f69e9fdce3052b0b23a9b6df4616d001418038e977565da48932876bf61c3e176a7f5f9633c811fe09cf2b6d1fc8b2fca4ffbb25b69fd05f101fe406343dc3d095bcc1cb025973459715fb7422405204d534ce6b760fe34f58152d015db8182d5506e571d30e942fc3d121d9cba1d1a6846384f340397c7ba97884d92678f90eebbee567d755ecce25bbdeff0c75cba752133034c91742511fa730c112ccca4f30f25bcd5a80a2301d2bee792b94850543a5610dc8285a8d868a0983505b4b2f67b530c57c0acb9d04ecfc747b43a562700db19730591e583011feb2f49a471c9dd0a4ba67f90e1f31c610449d42af6d40bb587fe49e1b9d2321208af8c84119c3e0f7bb238d17c8792d69c9c2d3b0f97ef335f93b17c9abc0607ec7d9568dfbc0a54bd70b88efbf70fd110b87ea57b629f71ff43f8a35e2c810b75fe79333ae00e7686bf309541ba733271279dea13384d4fe1d89f37e41 diff -Nru botan3-3.7.1+dfsg/src/tests/data/block/serpent.vec botan3-3.12.0+dfsg/src/tests/data/block/serpent.vec --- botan3-3.7.1+dfsg/src/tests/data/block/serpent.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/block/serpent.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,5 @@ -#test cpuid avx512 avx2 simd +#test cpuid avx512 avx2 sse2 neon altivec lsx simd128 [Serpent] Key = 00000000000000000000000000000000 diff -Nru botan3-3.7.1+dfsg/src/tests/data/block/shacal2.vec botan3-3.12.0+dfsg/src/tests/data/block/shacal2.vec --- botan3-3.7.1+dfsg/src/tests/data/block/shacal2.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/block/shacal2.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,4 @@ -#test cpuid intel_sha avx2 sse2 +#test cpuid avx512 intel_sha avx2 sse2 neon altivec lsx simd128 [SHACAL2] diff -Nru botan3-3.7.1+dfsg/src/tests/data/block/sm4.vec botan3-3.12.0+dfsg/src/tests/data/block/sm4.vec --- botan3-3.7.1+dfsg/src/tests/data/block/sm4.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/block/sm4.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,16 +1,10 @@ -#test cpuid gfni armv8sm4 +#test cpuid intel_sm4 avx512 gfni armv8sm4 aesni armv8aes [SM4] Key = 0123456789abcdeffedcba9876543210 In = 0123456789abcdeffedcba9876543210 Out = 681edf34d206965e86b3e94f536e4246 -Iterations = 1000000 -Key = 0123456789abcdeffedcba9876543210 -In = 0123456789abcdeffedcba9876543210 -Out = 595298c7c6fd271f0402f804c33d3f66 - - # Random tests generated by GmSSL Key = 681EDF34D206965E86B3E94F536E4246 In = F42131B002425B6F5CF52A810682A09D07BCAE6A8388E14651FED84B3749D386F4762615B32C000A165E1D722D708052BA3C19D8926356ED1491C6E4E528782F @@ -84,3 +78,15 @@ Key = fb6d97987e9fd25c658fbb570751c174 In = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff Out = 8017c5440e56ee8da14ea4962e2303ab17982c9927c1fab9b96115900476b9654477db66b40bd3b30012d4b13cebec3667c5547c4374127be8d5c25d5c1cd838d4acf258cff24329e404ebdf94a5c6add8934ad73432f8cf55e775f28d8009dafd1c877aa98e30063f92e17545b92f735f7819d4974a645d9de07ab30535be135632c5140e2e78392c97b1bd9a757caf71090eb46febc05561eb0281fcb792b194feebaa342aa3368163a16ea73c647bb5c75b2b0fdc97cf81ec38b22607b267b383f828521180c6bb49c1b5fb8e38cf5598cd0cd399693722235aed362d20ff4ee8e3fa5843fff8acbdc7cf1860f02841bf274d06e57fdc3adbda0acdff8305 + +Key = fb6d97987e9fd25c658fbb570751c174 +In = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff +Out = 8017c5440e56ee8da14ea4962e2303ab17982c9927c1fab9b96115900476b9654477db66b40bd3b30012d4b13cebec3667c5547c4374127be8d5c25d5c1cd838d4acf258cff24329e404ebdf94a5c6add8934ad73432f8cf55e775f28d8009dafd1c877aa98e30063f92e17545b92f735f7819d4974a645d9de07ab30535be135632c5140e2e78392c97b1bd9a757caf71090eb46febc05561eb0281fcb792b194feebaa342aa3368163a16ea73c647bb5c75b2b0fdc97cf81ec38b22607b267b383f828521180c6bb49c1b5fb8e38cf5598cd0cd399693722235aed362d20ff4ee8e3fa5843fff8acbdc7cf1860f02841bf274d06e57fdc3adbda0acdff8305 + +Key = 0123456789abcdeffedcba9876543210 +In = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a79787776757473727170 +Out = fb1d77b140b9588a295ca20db6658d08cbd4fd4d96987a8cb2fcb00f5f134fc1d0eb369db5e52379f0138df87c4d4c3fb00a18057e472290acc65de23581fc7e204f8b5255a505f1a85c27ad091d1780914374a0c491545ca695c945facd9dd91b71723b913c0484fa9a413e31ccb53bd043a279fc54396296161feb8d70f03523e39d759622583c2b06f485334f3120edd01e115554dc4984742f010941132c9b877858209d8db6aaddb0ddc554da2beaee4e0d1089de870eac280284cc28dc5d9e1203a1718bb4ff02bf9d814310846cd1243ea0234a9a56a9d6c4d9988013fdef01b7ca1fb980884608ecd1de657228805167c0e42b3c5c8de46992ad640ffb1d77b140b9588a295ca20db6658d08cbd4fd4d96987a8cb2fcb00f5f134fc1d0eb369db5e52379f0138df87c4d4c3fb00a18057e472290acc65de23581fc7e204f8b5255a505f1a85c27ad091d1780914374a0c491545ca695c945facd9dd91b71723b913c0484fa9a413e31ccb53bd043a279fc54396296161feb8d70f03523e39d759622583c2b06f485334f3120edd01e115554dc4984742f010941132c9b877858209d8db6aaddb0ddc554da2beaee4e0d1089de870eac280284cc28dc5d9e1203a1718bb4ff02bf9d814310846cd1243ea0234a9a56a9d6c4d9988013fdef01b7ca1fb980884608ecd1de657228805167c0e42b3c5c8de46992ad640ffb1d77b140b9588a295ca20db6658d08cbd4fd4d96987a8cb2fcb00f5f134fc1d0eb369db5e52379f0138df87c4d4c3fb00a18057e472290acc65de23581fc7e204f8b5255a505f1a85c27ad091d1780914374a0c491545ca695c945facd9dd91b71723b913c0484fa9a413e31ccb53bd043a279fc54396296161feb8d70f03523e39d759622583c2b06f485334f3120edd01e115554dc4984742f010941132c9b877858209d8db6aaddb0ddc554da2beaee4e0d1089de870eac280284cc28dc5d9e1203a1718bb4ff02bf9d814310846cd1243ea0234a9a56a9d6c4d9988013fdef01b7ca1fb980884608ecd1de657228805167c0e42b3c5c8de46992ad640ffb1d77b140b9588a295ca20db6658d08cbd4fd4d96987a8cb2fcb00f5f134fc1d0eb369db5e52379f0138df87c4d4c3fb00a18057e472290acc65de23581fc7e204f8b5255a505f1a85c27ad091d1780914374a0c491545ca695c945facd9dd91b71723b913c0484fa9a413e31ccb53bd043a279fc54396296161feb8d70f03523e39d759622583c2b06f485334f3120 + +Key = 0123456789abcdeffedcba9876543210 +In = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff +Out = 06989c613da668ad2a8df782e1a8f96a4b910651754b5553f10cfa0c8a09e9e5f42952cf94ac83688437c9b671d6c7fad55bfd68e7901219f41fab48427ab58d718e2043bac7ec8bfd57a907118650150c2c0fab0b4e22ee8de3028b161a7c371cd7855afd87ef606bcfedad65c86b5b596849dc7c7a6d63ca3a767538d15ef6274ce8f040d663da6e6dab9c5638b0f03ae7764fd21a7e3fd8a0a01b5ea5ee7d7b394a3279148396bc321ece57ad1c7d0834b5b1b574d0eca777704293b3d7066c6db629d0dbb08939d0feaa80a0b0376625d79c1f4dfb4ecaef50a90afd3d466b72b123f62c39c6ab8271bb1d2be38b5e22e6c782c5b0bdf160158ac77978b706989c613da668ad2a8df782e1a8f96a4b910651754b5553f10cfa0c8a09e9e5f42952cf94ac83688437c9b671d6c7fad55bfd68e7901219f41fab48427ab58d718e2043bac7ec8bfd57a907118650150c2c0fab0b4e22ee8de3028b161a7c371cd7855afd87ef606bcfedad65c86b5b596849dc7c7a6d63ca3a767538d15ef6274ce8f040d663da6e6dab9c5638b0f03ae7764fd21a7e3fd8a0a01b5ea5ee7d7b394a3279148396bc321ece57ad1c7d0834b5b1b574d0eca777704293b3d7066c6db629d0dbb08939d0feaa80a0b0376625d79c1f4dfb4ecaef50a90afd3d466b72b123f62c39c6ab8271bb1d2be38b5e22e6c782c5b0bdf160158ac77978b706989c613da668ad2a8df782e1a8f96a4b910651754b5553f10cfa0c8a09e9e5f42952cf94ac83688437c9b671d6c7fad55bfd68e7901219f41fab48427ab58d718e2043bac7ec8bfd57a907118650150c2c0fab0b4e22ee8de3028b161a7c371cd7855afd87ef606bcfedad65c86b5b596849dc7c7a6d63ca3a767538d15ef6274ce8f040d663da6e6dab9c5638b0f03ae7764fd21a7e3fd8a0a01b5ea5ee7d7b394a3279148396bc321ece57ad1c7d0834b5b1b574d0eca777704293b3d7066c6db629d0dbb08939d0feaa80a0b0376625d79c1f4dfb4ecaef50a90afd3d466b72b123f62c39c6ab8271bb1d2be38b5e22e6c782c5b0bdf160158ac77978b706989c613da668ad2a8df782e1a8f96a4b910651754b5553f10cfa0c8a09e9e5f42952cf94ac83688437c9b671d6c7fad55bfd68e7901219f41fab48427ab58d718e2043bac7ec8bfd57a907118650150c2c0fab0b4e22ee8de3028b161a7c371cd7855afd87ef606bcfedad65c86b5b596849dc7c7a6d63ca3a767538d15ef6274ce8f040d663da6e6dab9c5638b0f03ae7764fd21a7e3fd8a0a01b5ea5ee7d7b394a3279148396bc321ece57ad1c7d0834b5b1b574d0eca777704293b3d7066c6db629d0dbb08939d0feaa80a0b0376625d79c1f4dfb4ecaef50a90afd3d466b72b123f62c39c6ab8271bb1d2be38b5e22e6c782c5b0bdf160158ac77978b7 diff -Nru botan3-3.7.1+dfsg/src/tests/data/block/twofish.vec botan3-3.12.0+dfsg/src/tests/data/block/twofish.vec --- botan3-3.7.1+dfsg/src/tests/data/block/twofish.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/block/twofish.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,11 +1,10 @@ -[Twofish] -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000000000 -Out = 9F589F5CF6122C32B6BFEC2F2AE8C35A +#test cpuid avx512 + +[Twofish] Key = 00000000000000000000000000000000 -In = 9F589F5CF6122C32B6BFEC2F2AE8C35A -Out = D491DB16E7B1C39E86CB086B789F5419 +In = 000000000000000000000000000000009F589F5CF6122C32B6BFEC2F2AE8C35A +Out = 9F589F5CF6122C32B6BFEC2F2AE8C35AD491DB16E7B1C39E86CB086B789F5419 Key = 9F589F5CF6122C32B6BFEC2F2AE8C35A In = D491DB16E7B1C39E86CB086B789F5419 @@ -196,12 +195,8 @@ Out = 5D9D4EEFFA9151575524F115815A12E0 Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000000 -Out = EFA71F788965BD4453F860178FC19101 - -Key = 000000000000000000000000000000000000000000000000 -In = EFA71F788965BD4453F860178FC19101 -Out = 88B2B2706B105E36B446BB6D731A1E88 +In = 00000000000000000000000000000000EFA71F788965BD4453F860178FC19101 +Out = EFA71F788965BD4453F860178FC1910188B2B2706B105E36B446BB6D731A1E88 Key = EFA71F788965BD4453F860178FC191010000000000000000 In = 88B2B2706B105E36B446BB6D731A1E88 @@ -392,12 +387,8 @@ Out = E75449212BEEF9F4A390BD860A640941 Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000000 -Out = 57FF739D4DC92C1BD7FC01700CC8216F - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 57FF739D4DC92C1BD7FC01700CC8216F -Out = D43BB7556EA32E46F2A282B7D45B4E0D +In = 0000000000000000000000000000000057FF739D4DC92C1BD7FC01700CC8216F +Out = 57FF739D4DC92C1BD7FC01700CC8216FD43BB7556EA32E46F2A282B7D45B4E0D Key = 57FF739D4DC92C1BD7FC01700CC8216F00000000000000000000000000000000 In = D43BB7556EA32E46F2A282B7D45B4E0D @@ -2892,1522 +2883,14 @@ Out = 85F345366155D13F8F257734D2CBD6D9 Key = 00000000000000000000000000000000 -In = 80000000000000000000000000000000 -Out = 73B9FF14CF2589901FF52A0D6F4B7EDE - -Key = 00000000000000000000000000000000 -In = 40000000000000000000000000000000 -Out = F5A9150BAB6D6AEBD6B4F97D9E93B28B - -Key = 00000000000000000000000000000000 -In = 20000000000000000000000000000000 -Out = C30F8B221FD6D3996F973CDCDC6E305C - -Key = 00000000000000000000000000000000 -In = 10000000000000000000000000000000 -Out = D6A531FE826CB0454F2D567A20018CB7 - -Key = 00000000000000000000000000000000 -In = 08000000000000000000000000000000 -Out = B62324BE427332A6089C7BE40D40292E - -Key = 00000000000000000000000000000000 -In = 04000000000000000000000000000000 -Out = 929B4789E9D6940C9A158880CA21C0E2 - -Key = 00000000000000000000000000000000 -In = 02000000000000000000000000000000 -Out = C14830DB50BA7221B27DC033B0D8D331 - -Key = 00000000000000000000000000000000 -In = 01000000000000000000000000000000 -Out = 743342B02EBE647AE47092D435FA60F6 - -Key = 00000000000000000000000000000000 -In = 00800000000000000000000000000000 -Out = 4F02AF45C09373D879CD01506A4E7D14 - -Key = 00000000000000000000000000000000 -In = 00400000000000000000000000000000 -Out = 92BC9085AB0BA8FFEC2EA6D360864817 - -Key = 00000000000000000000000000000000 -In = 00200000000000000000000000000000 -Out = 670A4ED16EA1BDE23E16CB52DBD31CB0 - -Key = 00000000000000000000000000000000 -In = 00100000000000000000000000000000 -Out = A52335AA9F42886084E21400DE48B62F - -Key = 00000000000000000000000000000000 -In = 00080000000000000000000000000000 -Out = A5A240EBFED79F38F31497EA4C9CFCDA - -Key = 00000000000000000000000000000000 -In = 00040000000000000000000000000000 -Out = 46A64A07123E1212FE9E2F30EDFD80FF - -Key = 00000000000000000000000000000000 -In = 00020000000000000000000000000000 -Out = 20C9F20A8045AEDEE9D6E1CDA948339A - -Key = 00000000000000000000000000000000 -In = 00010000000000000000000000000000 -Out = DF1606EEF4FEE3F4FC9EC26E2AB388AB - -Key = 00000000000000000000000000000000 -In = 00008000000000000000000000000000 -Out = 6758972B3171F0EA46304542776337FC - -Key = 00000000000000000000000000000000 -In = 00004000000000000000000000000000 -Out = EC9B591DB8476C26C3CFDA618C1DBBD8 - -Key = 00000000000000000000000000000000 -In = 00002000000000000000000000000000 -Out = 651551E741359E0A10BB4EE6A1C07C02 - -Key = 00000000000000000000000000000000 -In = 00001000000000000000000000000000 -Out = 88BC2BF1F8A55562B95F8547C9A19E56 - -Key = 00000000000000000000000000000000 -In = 00000800000000000000000000000000 -Out = 1A6CCCBD8D40AA14810ED615A6A6E24D - -Key = 00000000000000000000000000000000 -In = 00000400000000000000000000000000 -Out = 7B68DA568ABA5AE69D93C915E37DEE91 - -Key = 00000000000000000000000000000000 -In = 00000200000000000000000000000000 -Out = EC3922A728DA9E4C212D910E5C4AE632 - -Key = 00000000000000000000000000000000 -In = 00000100000000000000000000000000 -Out = 5BAC94C97A4069400875A5ABC07BCB17 - -Key = 00000000000000000000000000000000 -In = 00000080000000000000000000000000 -Out = F3B4662918864BA94C1CF79C73B1F259 - -Key = 00000000000000000000000000000000 -In = 00000040000000000000000000000000 -Out = 6FBF5A9A93EFA6640AFB80D9A2D22CF7 - -Key = 00000000000000000000000000000000 -In = 00000020000000000000000000000000 -Out = 74139BD645DED7690F606490CCA44DD2 - -Key = 00000000000000000000000000000000 -In = 00000010000000000000000000000000 -Out = B20F456519D353AF91C012793576F9B8 - -Key = 00000000000000000000000000000000 -In = 00000008000000000000000000000000 -Out = C0AD52D4B4F67A9333A5E4B1B1176EEC - -Key = 00000000000000000000000000000000 -In = 00000004000000000000000000000000 -Out = 797224710FD09F9830B0F160AE9051E8 - -Key = 00000000000000000000000000000000 -In = 00000002000000000000000000000000 -Out = 73669B64C292F4461FAA3A3D091D08DA - -Key = 00000000000000000000000000000000 -In = 00000001000000000000000000000000 -Out = EFE0E893CE04008935CB7D43A7DC9ADD - -Key = 00000000000000000000000000000000 -In = 00000000800000000000000000000000 -Out = 3B0A2D3B236324221F81BFCAE45217D8 - -Key = 00000000000000000000000000000000 -In = 00000000400000000000000000000000 -Out = CE6F569FC89127B1AE19466FA36DD6E4 - -Key = 00000000000000000000000000000000 -In = 00000000200000000000000000000000 -Out = 6037FE38896C05745C58C28CDF7FF386 - -Key = 00000000000000000000000000000000 -In = 00000000100000000000000000000000 -Out = 92F5817D0BE37241F9292F6FF918A8E5 - -Key = 00000000000000000000000000000000 -In = 00000000080000000000000000000000 -Out = 20C9A2A684563495C255A5751C1AC01E - -Key = 00000000000000000000000000000000 -In = 00000000040000000000000000000000 -Out = AC6B6DB6D069B6895F2283435D33BD43 - -Key = 00000000000000000000000000000000 -In = 00000000020000000000000000000000 -Out = F9354B12C2366F1CE10F9A0550281267 - -Key = 00000000000000000000000000000000 -In = 00000000010000000000000000000000 -Out = 684FDA9FCF3B3B5648A452CDA07CF002 - -Key = 00000000000000000000000000000000 -In = 00000000008000000000000000000000 -Out = 9BE294C97C2A963006A2BD4541DC7DB5 - -Key = 00000000000000000000000000000000 -In = 00000000004000000000000000000000 -Out = A984F6F70E93FE65C8798C01D4E5D30C - -Key = 00000000000000000000000000000000 -In = 00000000002000000000000000000000 -Out = E06A6CE2D74DB3D78E8F5D991C322B87 - -Key = 00000000000000000000000000000000 -In = 00000000001000000000000000000000 -Out = 646771D16BAEDAC3F8E9D00C212518A2 - -Key = 00000000000000000000000000000000 -In = 00000000000800000000000000000000 -Out = 9D2D410DC6F3BEC913D64BDBDEF3285E - -Key = 00000000000000000000000000000000 -In = 00000000000400000000000000000000 -Out = 6850AFECD8064E77F4F6944BDF5B324D - -Key = 00000000000000000000000000000000 -In = 00000000000200000000000000000000 -Out = 2E341142550F73F4C8E9DCCC5931A158 - -Key = 00000000000000000000000000000000 -In = 00000000000100000000000000000000 -Out = 0CA58E149C2120A8EBF9A7885A89ACBC - -Key = 00000000000000000000000000000000 -In = 00000000000080000000000000000000 -Out = 41EACB7F6B5F9E3E3D299CA416EA2C59 - -Key = 00000000000000000000000000000000 -In = 00000000000040000000000000000000 -Out = C4D45503484DBC83CB52D3DB4AD0A7CC - -Key = 00000000000000000000000000000000 -In = 00000000000020000000000000000000 -Out = CC52B159C2BCF87EE5F4926C6E7B7744 - -Key = 00000000000000000000000000000000 -In = 00000000000010000000000000000000 -Out = 7E8A4023B8890A2DBF0D54E330FDF2A2 - -Key = 00000000000000000000000000000000 -In = 00000000000008000000000000000000 -Out = B05E771660493DCE3A275B0252D343A7 - -Key = 00000000000000000000000000000000 -In = 00000000000004000000000000000000 -Out = 83D0034D231E179207F6A97FB1457FEB - -Key = 00000000000000000000000000000000 -In = 00000000000002000000000000000000 -Out = 7132BF130E8732C41F68107F49153FF2 - -Key = 00000000000000000000000000000000 -In = 00000000000001000000000000000000 -Out = B93021593B9EA2588F16E87D3C5DE0EC - -Key = 00000000000000000000000000000000 -In = 00000000000000800000000000000000 -Out = 26031449FBD6C84201B0BFB53B2C23CA - -Key = 00000000000000000000000000000000 -In = 00000000000000400000000000000000 -Out = AFC8E9D2B9BFED9CE0B898F28607DF4C - -Key = 00000000000000000000000000000000 -In = 00000000000000200000000000000000 -Out = C5094DE7E36CAEBE1B76EC3AC2C875F5 - -Key = 00000000000000000000000000000000 -In = 00000000000000100000000000000000 -Out = 7DF8910A2D256FFB5D56FD1358F131FE - -Key = 00000000000000000000000000000000 -In = 00000000000000080000000000000000 -Out = D93F84C1519D6627465E984675AA800B - -Key = 00000000000000000000000000000000 -In = 00000000000000040000000000000000 -Out = 5F1861F1523CDA0C95644B0C4F2EE6D1 - -Key = 00000000000000000000000000000000 -In = 00000000000000020000000000000000 -Out = FC5C4893AD148E4134EAEB3B1B190E29 - -Key = 00000000000000000000000000000000 -In = 00000000000000010000000000000000 -Out = 80D1463F9E9416A143B2FF69DE629510 - -Key = 00000000000000000000000000000000 -In = 00000000000000008000000000000000 -Out = F16305404AE6266C619DC8ACA2D492E1 - -Key = 00000000000000000000000000000000 -In = 00000000000000004000000000000000 -Out = 3B2554E422F9CEBB8271D7A48C94E03F - -Key = 00000000000000000000000000000000 -In = 00000000000000002000000000000000 -Out = 18B039EECB68A05CBF8C65EE85BDC4BC - -Key = 00000000000000000000000000000000 -In = 00000000000000001000000000000000 -Out = BB26ABB17AD5482B1DCC4018E7DB0950 - -Key = 00000000000000000000000000000000 -In = 00000000000000000800000000000000 -Out = 7E7AC0FD5B98157CEAD4BBAB643BE4CA - -Key = 00000000000000000000000000000000 -In = 00000000000000000400000000000000 -Out = BD8A3B64849E54CC2D8379DCA9E42FDD - -Key = 00000000000000000000000000000000 -In = 00000000000000000200000000000000 -Out = 58C388DFB41FF3E14394C73FD8AAC56A - -Key = 00000000000000000000000000000000 -In = 00000000000000000100000000000000 -Out = 944B295E23C5B2542DED57A155D33EF8 - -Key = 00000000000000000000000000000000 -In = 00000000000000000080000000000000 -Out = 067B4DD07DCA1292CFF0D80D75BDACA5 - -Key = 00000000000000000000000000000000 -In = 00000000000000000040000000000000 -Out = 7D7344373196C5B30676F270BFC90B07 - -Key = 00000000000000000000000000000000 -In = 00000000000000000020000000000000 -Out = 988C5164A82254B29326C98812A716CE - -Key = 00000000000000000000000000000000 -In = 00000000000000000010000000000000 -Out = 8FFD48787C28542E0450FAD4CBAD34D0 - -Key = 00000000000000000000000000000000 -In = 00000000000000000008000000000000 -Out = C50E7CD771628964E708425160FFB02C - -Key = 00000000000000000000000000000000 -In = 00000000000000000004000000000000 -Out = B293B07F92D68C18FEC1466996B78020 - -Key = 00000000000000000000000000000000 -In = 00000000000000000002000000000000 -Out = DE21B2A6C8D7B90A7714DB3EF5209A6B - -Key = 00000000000000000000000000000000 -In = 00000000000000000001000000000000 -Out = 4700E22C08FE953CABAC7E78A3F747A7 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000800000000000 -Out = 504C1D7FAE3AB9A62323F21BF9A80A67 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000400000000000 -Out = 064BA0E8ADDD8E9DF4496E6931AD25F4 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000200000000000 -Out = 51D0B15C08FF32F1DCE7B28320875566 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000100000000000 -Out = 1A20EA3DB071121460244EDE27DA7A39 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000080000000000 -Out = 0035A5F7557B1B009327109D0C62F25C - -Key = 00000000000000000000000000000000 -In = 00000000000000000000040000000000 -Out = 803939C10EE11BB254A7768FAD053DA4 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000020000000000 -Out = 35D493255A870959C12F26170E6A1B64 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000010000000000 -Out = DE8381198215D45B1BE787E4E8438500 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000008000000000 -Out = B22E9707E738F723CD9B99386CE0162E - -Key = 00000000000000000000000000000000 -In = 00000000000000000000004000000000 -Out = 24C65ADDE5C3CD24B75C343782E87F6E - -Key = 00000000000000000000000000000000 -In = 00000000000000000000002000000000 -Out = 0050FF2C1A3C2AA68207D333F9956A72 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000001000000000 -Out = 162F7B8D35C1A98305BA0FE2A91FF27A - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000800000000 -Out = 223D6117FE4864C2B3C513EAC2A5266A - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000400000000 -Out = 3DBF645715ED7AD1964E2DCDE2F8806A - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000200000000 -Out = 3DBB85509557BB00FE0F2013A90A5753 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000100000000 -Out = F2D75E45A62D1758C7A542BC805AC482 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000080000000 -Out = 77628153A62DFD455B1C0E5B6CE9688C - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000040000000 -Out = 8AE9DF2D94F6E85C86459132130E1BF5 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000020000000 -Out = BB610990F42303F4ECCD795E16780A13 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000010000000 -Out = A38330C5C0B464FFD6983972CF9541CC - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000008000000 -Out = EFD11CA98FEBB6F1ECBEBBCB8BD5E35B - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000004000000 -Out = 3BC6740BF141DD33D65FDEAA10BF1655 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000002000000 -Out = FCF7BE4B89B54547C0BEF84EB85734F6 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000001000000 -Out = CBF5EF38DDEBCA39F2F6BEB2F3042D96 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000800000 -Out = 1119C20F08D4EA77A13C331678D4D71F - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000400000 -Out = 5C8241DA3FB0DC7328271B9FF72C91BA - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000200000 -Out = 337FCEE0AD0BDCA24AF5411B69D39B37 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000100000 -Out = F6C56A841A31D58A90F5693F87380A3F - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000080000 -Out = DD8C11B46F768B7CB2EAEE7E3448DD37 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000040000 -Out = B8EE7182E563888E4F99335CF0372598 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000020000 -Out = FCA9BAD58DD9C77B0BC0E616E7DE7F2D - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000010000 -Out = 798DA99BEFFBF99B23A3C15A31F60CB1 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000008000 -Out = 015CD86F000C87948BF3591C3DE4391F - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000004000 -Out = B40B9945EEF7BC52E0B244ED71FAD3D3 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000002000 -Out = D9BBB27B7D8AB20241E60F04108F1E12 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000001000 -Out = 6EC3F259B3FA960505CDE9D20F9EB905 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000000800 -Out = 01C1772AD104A988B2978447B91199F0 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000000400 -Out = F8D5E997A8DD1B5BBE79C9F36B94C73C - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000000200 -Out = 4040058B08B27B6A585F18BFDBAE3E29 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000000100 -Out = 55547D09ACAF0C915B24E15ABAB0C827 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000000080 -Out = FD7A0B33D397DA035D146DD56C869960 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000000040 -Out = 80C38900313E9350219EAE9AA7DA5E1B - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000000020 -Out = F50D8495C3DCBFF4DFED0736F92475BB - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000000010 -Out = 58A06DC5AD2D7C0550771D6E9D59D58B - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000000008 -Out = EEE324733E6409500FC9F9D6DCA185E0 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000000004 -Out = F97C415886D05C12598F2C95F6B3EB16 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000000002 -Out = F0FC7D86D814589A09D8EC136F95A124 - -Key = 00000000000000000000000000000000 -In = 00000000000000000000000000000001 -Out = CA737FF1FD0FE5B8E41E90358A5F2CB1 - -Key = 000000000000000000000000000000000000000000000000 -In = 80000000000000000000000000000000 -Out = 62EF193EDB7D399ACA50EC1CBE5398D8 - -Key = 000000000000000000000000000000000000000000000000 -In = 40000000000000000000000000000000 -Out = E7A58D547688BA8B69DA949E38AA6FAD - -Key = 000000000000000000000000000000000000000000000000 -In = 20000000000000000000000000000000 -Out = 71579F70A8EDB2BA5C00C513E2D7DEEB - -Key = 000000000000000000000000000000000000000000000000 -In = 10000000000000000000000000000000 -Out = C6171EF892F8224DC5FAE230AF629F52 - -Key = 000000000000000000000000000000000000000000000000 -In = 08000000000000000000000000000000 -Out = C6A61053C48D7ECD7DDD12DB0F316AD7 - -Key = 000000000000000000000000000000000000000000000000 -In = 04000000000000000000000000000000 -Out = EA5833714F1324DAB7F53CACC63F784F - -Key = 000000000000000000000000000000000000000000000000 -In = 02000000000000000000000000000000 -Out = 450BCB0C7351CB1CDAC5D02E80D13C64 - -Key = 000000000000000000000000000000000000000000000000 -In = 01000000000000000000000000000000 -Out = C267D3634F84215FB7B4635AFA385E52 - -Key = 000000000000000000000000000000000000000000000000 -In = 00800000000000000000000000000000 -Out = 6DB5B1B156DA1C36B9AB5AB59B063C29 - -Key = 000000000000000000000000000000000000000000000000 -In = 00400000000000000000000000000000 -Out = EDF2D9B19FF75561E8FA6F411C4A0431 - -Key = 000000000000000000000000000000000000000000000000 -In = 00200000000000000000000000000000 -Out = A11AE84E6D2C56DED2B9497FEC7504A7 - -Key = 000000000000000000000000000000000000000000000000 -In = 00100000000000000000000000000000 -Out = B3A1A1E271BF94DA3A5ECFF1D4293A56 - -Key = 000000000000000000000000000000000000000000000000 -In = 00080000000000000000000000000000 -Out = 5577374ADCF2F58EEEFFC432C42AEB76 - -Key = 000000000000000000000000000000000000000000000000 -In = 00040000000000000000000000000000 -Out = CD9D355C3574343BC7FBE645CE7EA721 - -Key = 000000000000000000000000000000000000000000000000 -In = 00020000000000000000000000000000 -Out = 76EB4C046F052AE4ED41060BF60067FB - -Key = 000000000000000000000000000000000000000000000000 -In = 00010000000000000000000000000000 -Out = 1273A4BC420BCC9E29619567B1E61762 - -Key = 000000000000000000000000000000000000000000000000 -In = 00008000000000000000000000000000 -Out = 1F80E1CE3F12C96F2E647BACB6DA78D8 - -Key = 000000000000000000000000000000000000000000000000 -In = 00004000000000000000000000000000 -Out = 12D34A7875E716B37A7E250D37AFFDEA - -Key = 000000000000000000000000000000000000000000000000 -In = 00002000000000000000000000000000 -Out = 5A54D764EEFFC4B64143A58B071514B4 - -Key = 000000000000000000000000000000000000000000000000 -In = 00001000000000000000000000000000 -Out = F4F1567BDC5B1F938D5A214419090FE0 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000800000000000000000000000000 -Out = 6CB91B935A0FBF49636CDF64A12955B7 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000400000000000000000000000000 -Out = C082154A07AF64FF6ECA811B0E3302E6 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000200000000000000000000000000 -Out = 49A746CC225C1B6009A93649CDD9EDCD - -Key = 000000000000000000000000000000000000000000000000 -In = 00000100000000000000000000000000 -Out = 1CAD0512E3CC51210B411EE452DF62E4 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000080000000000000000000000000 -Out = 4FE9A0CF34BBCFFF906D8450197CC9D1 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000040000000000000000000000000 -Out = 38DB8EF6C8993F17BB4D1614B9DE15D4 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000020000000000000000000000000 -Out = 08DB3F476F551D19D9643A9E139E0553 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000010000000000000000000000000 -Out = F0F518F73795AB51BFB6E0AA99A0DAC7 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000008000000000000000000000000 -Out = 91544D597F679E7DFE6D16D475809851 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000004000000000000000000000000 -Out = B1F92601B6D8C6B81176A46EE7341D28 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000002000000000000000000000000 -Out = 796C3A8CBC6450E51FA6F8765ACD0F10 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000001000000000000000000000000 -Out = 5F7BFE71BD2E81599DDA3411BC1CA579 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000800000000000000000000000 -Out = 2DF7D576EC6296101CAB16012092C12C - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000400000000000000000000000 -Out = 20769DE071FBE22AE49E7B3F5D646418 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000200000000000000000000000 -Out = B6F5C012E4BA15DC86536F328B137FC0 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000100000000000000000000000 -Out = 1C3031DD05EAA4C278B55EF0E7E4C1F9 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000080000000000000000000000 -Out = 3D72A53BD537D04AC59E0AF36D105ED9 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000040000000000000000000000 -Out = A6FFE499A2050C38F4E89DBF12B27430 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000020000000000000000000000 -Out = BFBB2A537B2C1339D6230F35A256F289 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000010000000000000000000000 -Out = 70E79718C97DDB187411436AC072B148 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000008000000000000000000000 -Out = 13BB56699085842B81DBE8FAA8B26269 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000004000000000000000000000 -Out = 3E4DB71B9C737C7F8AEF632A5ADEF61D - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000002000000000000000000000 -Out = D7A254B7341C7677C72F9DE729A3BB78 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000001000000000000000000000 -Out = 527D14E58B74224622DF7F3FD65932F5 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000800000000000000000000 -Out = 708703993AD1DDE5C7F8714686F3AF32 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000400000000000000000000 -Out = C57D3ECB71A7DA4708DE6F338BC13E09 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000200000000000000000000 -Out = B31FF60BE0FDC17001CAF87FC7FC0B2D - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000100000000000000000000 -Out = 01877AC646A283472DA74182FDC1E2B5 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000080000000000000000000 -Out = 3C678BADA3FB1B872C018DC035AE16E8 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000040000000000000000000 -Out = A2CB209DA6AA40E043E6FB9DD2476100 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000020000000000000000000 -Out = 4F9402FF56D04C8FB37DBE1A3109D2DE - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000010000000000000000000 -Out = 342DAAD90F0F1699048D5CD16FDA2EC7 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000008000000000000000000 -Out = E68CC86CA4D952BC3890AEC0A7AAA4B5 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000004000000000000000000 -Out = 02A88364BF94A677124F670A566E3F0D - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000002000000000000000000 -Out = C961EF325E2A32A4359CC63BE1EE2C77 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000001000000000000000000 -Out = 03BF43C9083E4D9919D12FE5C0315E67 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000800000000000000000 -Out = B27063442470BE07DCC2256EC71A4F1A - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000400000000000000000 -Out = 222CC5A29084A4BDB05D4C2FD95648DF - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000200000000000000000 -Out = E08686BFAA936E1890AEEC834E3B474A +In = 8000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001 +Out = 73B9FF14CF2589901FF52A0D6F4B7EDEF5A9150BAB6D6AEBD6B4F97D9E93B28BC30F8B221FD6D3996F973CDCDC6E305CD6A531FE826CB0454F2D567A20018CB7B62324BE427332A6089C7BE40D40292E929B4789E9D6940C9A158880CA21C0E2C14830DB50BA7221B27DC033B0D8D331743342B02EBE647AE47092D435FA60F64F02AF45C09373D879CD01506A4E7D1492BC9085AB0BA8FFEC2EA6D360864817670A4ED16EA1BDE23E16CB52DBD31CB0A52335AA9F42886084E21400DE48B62FA5A240EBFED79F38F31497EA4C9CFCDA46A64A07123E1212FE9E2F30EDFD80FF20C9F20A8045AEDEE9D6E1CDA948339ADF1606EEF4FEE3F4FC9EC26E2AB388AB6758972B3171F0EA46304542776337FCEC9B591DB8476C26C3CFDA618C1DBBD8651551E741359E0A10BB4EE6A1C07C0288BC2BF1F8A55562B95F8547C9A19E561A6CCCBD8D40AA14810ED615A6A6E24D7B68DA568ABA5AE69D93C915E37DEE91EC3922A728DA9E4C212D910E5C4AE6325BAC94C97A4069400875A5ABC07BCB17F3B4662918864BA94C1CF79C73B1F2596FBF5A9A93EFA6640AFB80D9A2D22CF774139BD645DED7690F606490CCA44DD2B20F456519D353AF91C012793576F9B8C0AD52D4B4F67A9333A5E4B1B1176EEC797224710FD09F9830B0F160AE9051E873669B64C292F4461FAA3A3D091D08DAEFE0E893CE04008935CB7D43A7DC9ADD3B0A2D3B236324221F81BFCAE45217D8CE6F569FC89127B1AE19466FA36DD6E46037FE38896C05745C58C28CDF7FF38692F5817D0BE37241F9292F6FF918A8E520C9A2A684563495C255A5751C1AC01EAC6B6DB6D069B6895F2283435D33BD43F9354B12C2366F1CE10F9A0550281267684FDA9FCF3B3B5648A452CDA07CF0029BE294C97C2A963006A2BD4541DC7DB5A984F6F70E93FE65C8798C01D4E5D30CE06A6CE2D74DB3D78E8F5D991C322B87646771D16BAEDAC3F8E9D00C212518A29D2D410DC6F3BEC913D64BDBDEF3285E6850AFECD8064E77F4F6944BDF5B324D2E341142550F73F4C8E9DCCC5931A1580CA58E149C2120A8EBF9A7885A89ACBC41EACB7F6B5F9E3E3D299CA416EA2C59C4D45503484DBC83CB52D3DB4AD0A7CCCC52B159C2BCF87EE5F4926C6E7B77447E8A4023B8890A2DBF0D54E330FDF2A2B05E771660493DCE3A275B0252D343A783D0034D231E179207F6A97FB1457FEB7132BF130E8732C41F68107F49153FF2B93021593B9EA2588F16E87D3C5DE0EC26031449FBD6C84201B0BFB53B2C23CAAFC8E9D2B9BFED9CE0B898F28607DF4CC5094DE7E36CAEBE1B76EC3AC2C875F57DF8910A2D256FFB5D56FD1358F131FED93F84C1519D6627465E984675AA800B5F1861F1523CDA0C95644B0C4F2EE6D1FC5C4893AD148E4134EAEB3B1B190E2980D1463F9E9416A143B2FF69DE629510F16305404AE6266C619DC8ACA2D492E13B2554E422F9CEBB8271D7A48C94E03F18B039EECB68A05CBF8C65EE85BDC4BCBB26ABB17AD5482B1DCC4018E7DB09507E7AC0FD5B98157CEAD4BBAB643BE4CABD8A3B64849E54CC2D8379DCA9E42FDD58C388DFB41FF3E14394C73FD8AAC56A944B295E23C5B2542DED57A155D33EF8067B4DD07DCA1292CFF0D80D75BDACA57D7344373196C5B30676F270BFC90B07988C5164A82254B29326C98812A716CE8FFD48787C28542E0450FAD4CBAD34D0C50E7CD771628964E708425160FFB02CB293B07F92D68C18FEC1466996B78020DE21B2A6C8D7B90A7714DB3EF5209A6B4700E22C08FE953CABAC7E78A3F747A7504C1D7FAE3AB9A62323F21BF9A80A67064BA0E8ADDD8E9DF4496E6931AD25F451D0B15C08FF32F1DCE7B283208755661A20EA3DB071121460244EDE27DA7A390035A5F7557B1B009327109D0C62F25C803939C10EE11BB254A7768FAD053DA435D493255A870959C12F26170E6A1B64DE8381198215D45B1BE787E4E8438500B22E9707E738F723CD9B99386CE0162E24C65ADDE5C3CD24B75C343782E87F6E0050FF2C1A3C2AA68207D333F9956A72162F7B8D35C1A98305BA0FE2A91FF27A223D6117FE4864C2B3C513EAC2A5266A3DBF645715ED7AD1964E2DCDE2F8806A3DBB85509557BB00FE0F2013A90A5753F2D75E45A62D1758C7A542BC805AC48277628153A62DFD455B1C0E5B6CE9688C8AE9DF2D94F6E85C86459132130E1BF5BB610990F42303F4ECCD795E16780A13A38330C5C0B464FFD6983972CF9541CCEFD11CA98FEBB6F1ECBEBBCB8BD5E35B3BC6740BF141DD33D65FDEAA10BF1655FCF7BE4B89B54547C0BEF84EB85734F6CBF5EF38DDEBCA39F2F6BEB2F3042D961119C20F08D4EA77A13C331678D4D71F5C8241DA3FB0DC7328271B9FF72C91BA337FCEE0AD0BDCA24AF5411B69D39B37F6C56A841A31D58A90F5693F87380A3FDD8C11B46F768B7CB2EAEE7E3448DD37B8EE7182E563888E4F99335CF0372598FCA9BAD58DD9C77B0BC0E616E7DE7F2D798DA99BEFFBF99B23A3C15A31F60CB1015CD86F000C87948BF3591C3DE4391FB40B9945EEF7BC52E0B244ED71FAD3D3D9BBB27B7D8AB20241E60F04108F1E126EC3F259B3FA960505CDE9D20F9EB90501C1772AD104A988B2978447B91199F0F8D5E997A8DD1B5BBE79C9F36B94C73C4040058B08B27B6A585F18BFDBAE3E2955547D09ACAF0C915B24E15ABAB0C827FD7A0B33D397DA035D146DD56C86996080C38900313E9350219EAE9AA7DA5E1BF50D8495C3DCBFF4DFED0736F92475BB58A06DC5AD2D7C0550771D6E9D59D58BEEE324733E6409500FC9F9D6DCA185E0F97C415886D05C12598F2C95F6B3EB16F0FC7D86D814589A09D8EC136F95A124CA737FF1FD0FE5B8E41E90358A5F2CB1 Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000100000000000000000 -Out = 10DC4FFCAC5F5A1FB668277E8E75BEAB - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000080000000000000000 -Out = 03A9199978F9D652A4C528FF86C39CE9 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000040000000000000000 -Out = DDF718A1BEB37CC1B0905520DB7C1611 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000020000000000000000 -Out = 2072191C277EE40FD557FF5F67A2A546 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000010000000000000000 -Out = 96B3608C06112F619B156105EB082BBE - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000008000000000000000 -Out = B5D919DFD9828C4FF4427E72ABFC77B2 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000004000000000000000 -Out = 9570E7A96D4674C800AF8DCD01DEFF64 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000002000000000000000 -Out = 59723DB66EDB29A9C81175668903777F - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000001000000000000000 -Out = 1779BDFAC6DB722BCAF4D3A8D9D2E725 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000800000000000000 -Out = 5107B50A150998C6EA01C14697FC0E53 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000400000000000000 -Out = 36A6CAA08BD5D2B332E9323F7C2E76CA - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000200000000000000 -Out = D1CF37451667EDB8D2E9934D39A27A92 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000100000000000000 -Out = 4E96227B0A018755FBE8AD5BF0E421B0 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000080000000000000 -Out = 3221B3D3A24745B483BBF99509B330A0 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000040000000000000 -Out = 98A4AB39B84D21D36A5DDC2660BD68C5 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000020000000000000 -Out = E5C351E1783465EDBA5CE0592BC77E8E - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000010000000000000 -Out = 35489488EE33D0344C1BDB6263D28286 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000008000000000000 -Out = 97EA899F53C60536DAFEB18123FD6C5B - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000004000000000000 -Out = 8511B11C7D2D8FBF63702A7E3AACA08A - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000002000000000000 -Out = FE0C90596E4F47FD8A9927D83F4DCA52 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000001000000000000 -Out = 186FE000683CC19F621BC6C2DA300B71 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000800000000000 -Out = 8291F94EDF578E8A70CD0CF8F3FB3558 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000400000000000 -Out = 2891C81846949C917E757EBAE20D34EE - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000200000000000 -Out = 762D85A32DAF0C9F3CFB3388E808FEC2 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000100000000000 -Out = CC6807D209B728C559C32336FD8FB71D - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000080000000000 -Out = FCD07057EF4820154075A0DDCBFC0BBD - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000040000000000 -Out = FDBFE2C3FFC82792D338388A1FC6D22E - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000020000000000 -Out = 04D5F7CD68FC2352BDDBC82CFE35DA80 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000010000000000 -Out = B1784A7BF395FB525471EE3DC8972FED - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000008000000000 -Out = CB5D8A62D16220123EF0005876E35B19 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000004000000000 -Out = 311292EDE1F30E9F22F1EEF8FD19BD80 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000002000000000 -Out = 382BC583EC9B6E16E3DF2188CF0BF1E8 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000001000000000 -Out = 632C9AD674BD1B8A15827A789BD133A7 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000800000000 -Out = 0EE175DF45B889CED6974E9C2B8F8A78 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000400000000 -Out = 6131A1A18F00CFDA5B4AF4FBE5487445 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000200000000 -Out = 0C8746747460A540E2304B55C12EA672 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000100000000 -Out = DD6D4D2AA3EB702C597E4E2DE59BF4C9 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000080000000 -Out = 774EDF219459A4744AA1CCDE7D969A60 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000040000000 -Out = 1D7B340C4CE68AC97369FFF0FF9980C8 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000020000000 -Out = D18104617258AB02AAE02ECCF552A891 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000010000000 -Out = 8DC66F1D7648EB62F1DCBEB3CD237985 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000008000000 -Out = ABF090054ABE052ECE0B07BE6B6CC6DB - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000004000000 -Out = 89239AAFE9BA86E5EC794397E0180111 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000002000000 -Out = 6E1CDC049333211B4D7533E21504D200 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000001000000 -Out = EFE662AF24D9997FAE45CAD4F92F3091 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000800000 -Out = 16F53F3A6CC3B4F86DDA1B8792244901 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000400000 -Out = 047CBACDC5EA84771A61FE1204813D46 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000200000 -Out = B442FF3318822EA7F60E2A8A082A043A - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000100000 -Out = 971D07AEEDBCA5B7BDCD033F708C97DB - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000080000 -Out = D307263273E250C0B9E08FF23003B0D1 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000040000 -Out = 75F7005CA9C6EE5A1F9A4897FA67C661 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000020000 -Out = 9B421C68873D49F07E3B9025AD609787 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000010000 -Out = 5B182593B47DC674D8B1942CB97224B2 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000008000 -Out = A896871A3157CCD1F5A788E253A6B0FD - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000004000 -Out = 789A3D2B2A70181EFCCE5529F200DC44 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000002000 -Out = 7F9462D23DB6E99AD0A54EA84DBD94E2 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000001000 -Out = 23EAFE1F7CCE96B6BFE2484E1DFC4AB9 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000800 -Out = A76C323805F9C252C8B86C83294B9987 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000400 -Out = D8CA1075B7A3F2CF2DAC0980B1B66CC1 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000200 -Out = 8206D89F2A07BA6403647FB85A2F4D7D - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000100 -Out = 3DE7573CD46D85488364472B038869F9 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000080 -Out = 31C62401D6E9E4995C0913747BFE7C7E - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000040 -Out = C464728025916B4E8D56E4A9F98C1A25 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000020 -Out = 0D4643EE09ABD6D6062187789AE9A77D - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000010 -Out = 0C06EAC3043ABE6554C2DA42F21E7B05 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000008 -Out = E26C6B2CBE2130729A5BF96E7CD29912 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000004 -Out = 4DE2B8FFF3588A5D4E62CADB720E5BCC - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000002 -Out = B96DD46C6A286BFF721693A98491F529 - -Key = 000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000001 -Out = 64F1DBD3C79EE69AC9E0ED5F554F4AB6 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 8000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000 -Out = 23A385F617F313DAC05BCB7EABD6180735BE2B4738602A1DA3DE5C9E7E87192303E8BB7A568E95BA792DCE77D5523C2BD3ACBE92C482D2E806FD837E41DBB288DC3B1C37C69B4059EAADF03FCD016EB4 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 04000000000000000000000000000000 -Out = 3C9D9BD904E0E6916089A4BAC35E5368 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 02000000000000000000000000000000 -Out = C47DA045701B93A388E76FCBCD349F22 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 01000000000000000000000000000000 -Out = 52F264B196925A345CA5ADC57C234B96 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00800000000000000000000000000000 -Out = C923754C5AD2E3F842D01705A716BE8A - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00400000000000000000000000000000 -Out = 630075C7563CDBACDFEADB781CC9467C - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00200000000000000000000000000000 -Out = CB4F69BCC76A2499C6FCFBBE4CEB8CFB - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00100000000000000000000000000000 -Out = 0442F15EA2BD6D9EB773F9B99804DF56 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00080000000000000000000000000000 -Out = 9CE6896C15C3CC00E2AA1944D7117B98 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00040000000000000000000000000000 -Out = E934066740023616B349F45582442647 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00020000000000000000000000000000 -Out = D08EF37A59D94ED645B1D1B160E3E816 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00010000000000000000000000000000 -Out = E91891CAC17FE493C7167C6CB59DCB69 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00008000000000000000000000000000 -Out = B27A42D2C870DC96BC6C551218C44CC4 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00004000000000000000000000000000 -Out = CE8D23E64E6BC18208CEEB282E387326 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00002000000000000000000000000000 -Out = 36F76678A27F2F5A436073D5ADA4AB3B - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00001000000000000000000000000000 -Out = D0EAA36F9A648905B277F0BD24B1A339 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000800000000000000000000000000 -Out = 82BE0E3673E7872BF79BBE2A45F4BD93 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000400000000000000000000000000 -Out = 013CAE3986083F8D321273D68CA9784D - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000200000000000000000000000000 -Out = 0A7FCF71DAC023718153FFB761BAEBEF - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000100000000000000000000000000 -Out = C784C5EA8CE3897F153336047D2FE3E0 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000080000000000000000000000000 -Out = FE776B4476A4F029E5EA9293E3C1BCA1 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000040000000000000000000000000 -Out = 302A5F9A73B07D83699EB9DE1D86DB7B - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000020000000000000000000000000 -Out = E6C43F6F62F1EEE0BDB3484F325053B6 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000010000000000000000000000000 -Out = C5579556F710EACFAD9319AA85B89F6F - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000008000000000000000000000000 -Out = 96C88E46C1C2BA0B583F30FE0248A794 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000004000000000000000000000000 -Out = 287D27FB1CA40821294B1AFC868F3A6F - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000002000000000000000000000000 -Out = F4602DF76A24010DE5A1353043CF178E - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000001000000000000000000000000 -Out = 47F98AA9DF5E7314D3D5571EF6B95284 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000800000000000000000000000 -Out = 77CBF7DCA60F913FD9C82AC65212EBB2 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000400000000000000000000000 -Out = BEB1C2BC4B5C363FCF5A0466883079A9 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000200000000000000000000000 -Out = 2AB8B9254ADBDCE17F0A719815DEFF7E - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000100000000000000000000000 -Out = 75A30CEAA03AF66E44A85DC66DEB20C9 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000080000000000000000000000 -Out = C37E64CE86B615573C4C42BBE71DACD8 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000040000000000000000000000 -Out = 284CB50259A96CCED1C1C64D8B603024 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000020000000000000000000000 -Out = BE346B07869425CBCA54F93D7A1F4035 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000010000000000000000000000 -Out = 60EE2023B03033A972E28E4A21C7005F - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000008000000000000000000000 -Out = 4E1FD4BC99AEA3BCC6B9066EC6329D43 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000004000000000000000000000 -Out = 079BDF2DAD2CE6FB7D21BBD76A7ABF48 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000002000000000000000000000 -Out = 6B933D9914169C2A704E52EC6D7E4E1D - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000001000000000000000000000 -Out = FD854A50372E5D301367D8E98CC88028 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000800000000000000000000 -Out = 8CE94A2C43B01825CE5F271135481BB2 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000400000000000000000000 -Out = 73A43C713898BA7D7D2B6BC8673A7AAA - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000200000000000000000000 -Out = 13873CFBDED482C0B7B435025A9F1CF4 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000100000000000000000000 -Out = 3F168CD782896F22C56A92A09EA7E162 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000080000000000000000000 -Out = BCDF1C8686E68810FE90B16ECAB46147 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000040000000000000000000 -Out = 95C18EBA59E3CB6359DE7CCE9E8751F9 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000020000000000000000000 -Out = 03CFEA7D36D56552CDEF806215EA7596 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000010000000000000000000 -Out = CC189A2E8F529EB139DCA2033109F40B - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000008000000000000000000 -Out = 453912532144CED54B7D4049BC8B8CF2 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000004000000000000000000 -Out = C6DBBF405A056A80CA788267538FE8F0 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000002000000000000000000 -Out = 2EB5E272874CE244A328BA6410480B4C - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000001000000000000000000 -Out = 263BFB611CBD9D9C7FF6B1A9E3276696 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000800000000000000000 -Out = 9CC3EB61A9907F5F22251239A9EB38ED - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000400000000000000000 -Out = 726A3EE922EDFE52206C2191E1F045F4 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000200000000000000000 -Out = 1344C1E04A9D97668A240D82396AC021 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000100000000000000000 -Out = 9067BAA44C264E9A2AEC292390A6F492 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000080000000000000000 -Out = 1176621BD24D35670B08D6A065806B02 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000040000000000000000 -Out = DF0D31D14D81FD086E8E32479919FDBC - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000020000000000000000 -Out = 44BAD80B0BA01E971ADC4139D6DE0C36 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000010000000000000000 -Out = 76C59131EFFAE14058D99E22698B602D - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000008000000000000000 -Out = 7604D9F3110F8440917ABCEA49710ADA - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000004000000000000000 -Out = 4584FCBB487171176C4318082EFEFDF7 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000002000000000000000 -Out = 019D3B42FA31A9F9175759E6C3193A07 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000001000000000000000 -Out = C324FF5F71A974F13F5D83226441E3BD - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000800000000000000 -Out = D9DF41408DFF80DE7C9571706B39038F - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000400000000000000 -Out = FBB99A524AC23D74047D814EC0AEDBE2 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000200000000000000 -Out = 877C855E25345F6C7DB4237ECF64C874 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000100000000000000 -Out = 206500F822C1305F9D61F49FC57AFBF2 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000080000000000000 -Out = C7538D97A78844C3C00740865E26755B - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000040000000000000 -Out = FADBC4A6E4564041ADA094C603CABAA4 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000020000000000000 -Out = 04C482E0707DE6DC1917727D00C4FA6B - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000010000000000000 -Out = EE36B8996AFF98BEA6E2115B9D173321 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000008000000000000 -Out = DBA4A5F38B104985D796ECDDD812B605 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000004000000000000 -Out = 1DC0F910CD5AF1E5734169459E170192 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000002000000000000 -Out = C3B7D6914052503D377B01DB4E3A630D - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000001000000000000 -Out = 54D807506602ECF7D6B8C4D923317738 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000800000000000 -Out = ACE4949143D4D1441AA854331E7F511B - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000400000000000 -Out = F7BA140AED4756B26789498A17EBF62D - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000200000000000 -Out = F23E08B81ACB75FE2326A94ECC5968AC - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000100000000000 -Out = 4CF26F088604368B17DDC09FF9D0146D - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000080000000000 -Out = E93AEB7AD76A6AD0AF4092F363421F1B - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000040000000000 -Out = 3D234C0F78ECBEFCCDCE1EA6EC98C145 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000020000000000 -Out = BA300B0234F0C96125D33123CDD7D6A4 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000010000000000 -Out = 1CF1A160FAD7E744F08BA1454A999211 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000008000000000 -Out = 59A9E8C14ACBCEC235529425CF86998E - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000004000000000 -Out = 22D29CFEF3A6DC0EC67A9EA8523D6158 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000002000000000 -Out = 513971F979FC906FADD982D7F08E4F05 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000001000000000 -Out = 6A95C07D7FACF2CD36DF362116A2DD5F - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000800000000 -Out = CD62AD57393A38607436FAF0985C2D50 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000400000000 -Out = 5209ADE137B93BA0963528E3E1A40F2A - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000200000000 -Out = 545BDE9D1C11239ED70D93060F24E397 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000100000000 -Out = 720622F5194578B3C24B5DDDC7E30327 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000080000000 -Out = 9E12338BF484106249754EAA6C441192 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000040000000 -Out = F13303DC759CA65097EC87F8D854163C - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000020000000 -Out = 4A2A6EB6845723C1C790D693B596CDE3 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000010000000 -Out = EF3D7C67417CA1FEEF03EF71441BDDFE - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000008000000 -Out = 214FB38A7511A87CF160F59CCA2B8E33 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000004000000 -Out = DCFFDB5E44574D0D593A70ADA4C79474 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000002000000 -Out = AC9D55D4A4FBB80C9B79C9077BA381B1 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000001000000 -Out = 05C6D61B75312924E0BCEDCB4B8D55DD - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000800000 -Out = E650CB445AF48A77E8DB6E2EFBCE6FA7 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000400000 -Out = 4C9902E89253D7A172BABFA87DB94816 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000200000 -Out = 5AF4F5E8491EE7F87EB809D82AEC12DB - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000100000 -Out = 1C11730D62BCA6F847B1457B5287BD12 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000080000 -Out = BEF65A32FF7383CFDB5A90C2F3B93837 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000040000 -Out = 2F3AA68FFE3B99DC92621782F3F9ED67 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000020000 -Out = 18BD598BFA2C77E21DBD594EE0E5CDCB - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000010000 -Out = 248CF533016A6AB1F84F85B2C5CD41A7 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000008000 -Out = A3B12F578353514CCC500ADD6C495A6C - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000004000 -Out = EE2DC099B37D200B4D3930A6DE07208E - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000002000 -Out = 412C9F198D58A0F01F66DF07CF211636 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000001000 -Out = 5B3532BA46716B02E761339DBAFBAAB0 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000800 -Out = 0148187CA72EC46B522E4FE7E1261522 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000400 -Out = 84C529CC4E23683AB90A24A6690662ED - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000200 -Out = 0D1EF0481593A3D95F0361C776D9A4D2 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000100 -Out = D622171C73726DB6620FFDA6540D510E - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000080 -Out = F218D92AED363C6829F7FA3BA346E0FB - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000040 -Out = 40CD83A5F0BFD0E1D7FE14299CECFB7C - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000020 -Out = 512F022157AEF0015E93F3737911A35E - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000010 -Out = C7A5A88356152E95F36739AB5EF9F63F - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000008 -Out = 8FD0F15E2504A8F4FD751CA7799FFB1D - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000004 -Out = 0137C87257A8CBD18C218A867B3AB5F8 - -Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000002 -Out = 3DB0F1674F187DF1CB036DB33A05A0D7 +In = 8000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001 +Out = 62EF193EDB7D399ACA50EC1CBE5398D8E7A58D547688BA8B69DA949E38AA6FAD71579F70A8EDB2BA5C00C513E2D7DEEBC6171EF892F8224DC5FAE230AF629F52C6A61053C48D7ECD7DDD12DB0F316AD7EA5833714F1324DAB7F53CACC63F784F450BCB0C7351CB1CDAC5D02E80D13C64C267D3634F84215FB7B4635AFA385E526DB5B1B156DA1C36B9AB5AB59B063C29EDF2D9B19FF75561E8FA6F411C4A0431A11AE84E6D2C56DED2B9497FEC7504A7B3A1A1E271BF94DA3A5ECFF1D4293A565577374ADCF2F58EEEFFC432C42AEB76CD9D355C3574343BC7FBE645CE7EA72176EB4C046F052AE4ED41060BF60067FB1273A4BC420BCC9E29619567B1E617621F80E1CE3F12C96F2E647BACB6DA78D812D34A7875E716B37A7E250D37AFFDEA5A54D764EEFFC4B64143A58B071514B4F4F1567BDC5B1F938D5A214419090FE06CB91B935A0FBF49636CDF64A12955B7C082154A07AF64FF6ECA811B0E3302E649A746CC225C1B6009A93649CDD9EDCD1CAD0512E3CC51210B411EE452DF62E44FE9A0CF34BBCFFF906D8450197CC9D138DB8EF6C8993F17BB4D1614B9DE15D408DB3F476F551D19D9643A9E139E0553F0F518F73795AB51BFB6E0AA99A0DAC791544D597F679E7DFE6D16D475809851B1F92601B6D8C6B81176A46EE7341D28796C3A8CBC6450E51FA6F8765ACD0F105F7BFE71BD2E81599DDA3411BC1CA5792DF7D576EC6296101CAB16012092C12C20769DE071FBE22AE49E7B3F5D646418B6F5C012E4BA15DC86536F328B137FC01C3031DD05EAA4C278B55EF0E7E4C1F93D72A53BD537D04AC59E0AF36D105ED9A6FFE499A2050C38F4E89DBF12B27430BFBB2A537B2C1339D6230F35A256F28970E79718C97DDB187411436AC072B14813BB56699085842B81DBE8FAA8B262693E4DB71B9C737C7F8AEF632A5ADEF61DD7A254B7341C7677C72F9DE729A3BB78527D14E58B74224622DF7F3FD65932F5708703993AD1DDE5C7F8714686F3AF32C57D3ECB71A7DA4708DE6F338BC13E09B31FF60BE0FDC17001CAF87FC7FC0B2D01877AC646A283472DA74182FDC1E2B53C678BADA3FB1B872C018DC035AE16E8A2CB209DA6AA40E043E6FB9DD24761004F9402FF56D04C8FB37DBE1A3109D2DE342DAAD90F0F1699048D5CD16FDA2EC7E68CC86CA4D952BC3890AEC0A7AAA4B502A88364BF94A677124F670A566E3F0DC961EF325E2A32A4359CC63BE1EE2C7703BF43C9083E4D9919D12FE5C0315E67B27063442470BE07DCC2256EC71A4F1A222CC5A29084A4BDB05D4C2FD95648DFE08686BFAA936E1890AEEC834E3B474A10DC4FFCAC5F5A1FB668277E8E75BEAB03A9199978F9D652A4C528FF86C39CE9DDF718A1BEB37CC1B0905520DB7C16112072191C277EE40FD557FF5F67A2A54696B3608C06112F619B156105EB082BBEB5D919DFD9828C4FF4427E72ABFC77B29570E7A96D4674C800AF8DCD01DEFF6459723DB66EDB29A9C81175668903777F1779BDFAC6DB722BCAF4D3A8D9D2E7255107B50A150998C6EA01C14697FC0E5336A6CAA08BD5D2B332E9323F7C2E76CAD1CF37451667EDB8D2E9934D39A27A924E96227B0A018755FBE8AD5BF0E421B03221B3D3A24745B483BBF99509B330A098A4AB39B84D21D36A5DDC2660BD68C5E5C351E1783465EDBA5CE0592BC77E8E35489488EE33D0344C1BDB6263D2828697EA899F53C60536DAFEB18123FD6C5B8511B11C7D2D8FBF63702A7E3AACA08AFE0C90596E4F47FD8A9927D83F4DCA52186FE000683CC19F621BC6C2DA300B718291F94EDF578E8A70CD0CF8F3FB35582891C81846949C917E757EBAE20D34EE762D85A32DAF0C9F3CFB3388E808FEC2CC6807D209B728C559C32336FD8FB71DFCD07057EF4820154075A0DDCBFC0BBDFDBFE2C3FFC82792D338388A1FC6D22E04D5F7CD68FC2352BDDBC82CFE35DA80B1784A7BF395FB525471EE3DC8972FEDCB5D8A62D16220123EF0005876E35B19311292EDE1F30E9F22F1EEF8FD19BD80382BC583EC9B6E16E3DF2188CF0BF1E8632C9AD674BD1B8A15827A789BD133A70EE175DF45B889CED6974E9C2B8F8A786131A1A18F00CFDA5B4AF4FBE54874450C8746747460A540E2304B55C12EA672DD6D4D2AA3EB702C597E4E2DE59BF4C9774EDF219459A4744AA1CCDE7D969A601D7B340C4CE68AC97369FFF0FF9980C8D18104617258AB02AAE02ECCF552A8918DC66F1D7648EB62F1DCBEB3CD237985ABF090054ABE052ECE0B07BE6B6CC6DB89239AAFE9BA86E5EC794397E01801116E1CDC049333211B4D7533E21504D200EFE662AF24D9997FAE45CAD4F92F309116F53F3A6CC3B4F86DDA1B8792244901047CBACDC5EA84771A61FE1204813D46B442FF3318822EA7F60E2A8A082A043A971D07AEEDBCA5B7BDCD033F708C97DBD307263273E250C0B9E08FF23003B0D175F7005CA9C6EE5A1F9A4897FA67C6619B421C68873D49F07E3B9025AD6097875B182593B47DC674D8B1942CB97224B2A896871A3157CCD1F5A788E253A6B0FD789A3D2B2A70181EFCCE5529F200DC447F9462D23DB6E99AD0A54EA84DBD94E223EAFE1F7CCE96B6BFE2484E1DFC4AB9A76C323805F9C252C8B86C83294B9987D8CA1075B7A3F2CF2DAC0980B1B66CC18206D89F2A07BA6403647FB85A2F4D7D3DE7573CD46D85488364472B038869F931C62401D6E9E4995C0913747BFE7C7EC464728025916B4E8D56E4A9F98C1A250D4643EE09ABD6D6062187789AE9A77D0C06EAC3043ABE6554C2DA42F21E7B05E26C6B2CBE2130729A5BF96E7CD299124DE2B8FFF3588A5D4E62CADB720E5BCCB96DD46C6A286BFF721693A98491F52964F1DBD3C79EE69AC9E0ED5F554F4AB6 Key = 0000000000000000000000000000000000000000000000000000000000000000 -In = 00000000000000000000000000000001 -Out = 23D1247EFF4CA8CBB378DF118369821E +In = 8000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001000000000000000000000000000000008000000000000000000000000000000040000000000000000000000000000000200000000000000000000000000000001 +Out = 23A385F617F313DAC05BCB7EABD6180735BE2B4738602A1DA3DE5C9E7E87192303E8BB7A568E95BA792DCE77D5523C2BD3ACBE92C482D2E806FD837E41DBB288DC3B1C37C69B4059EAADF03FCD016EB43C9D9BD904E0E6916089A4BAC35E5368C47DA045701B93A388E76FCBCD349F2252F264B196925A345CA5ADC57C234B96C923754C5AD2E3F842D01705A716BE8A630075C7563CDBACDFEADB781CC9467CCB4F69BCC76A2499C6FCFBBE4CEB8CFB0442F15EA2BD6D9EB773F9B99804DF569CE6896C15C3CC00E2AA1944D7117B98E934066740023616B349F45582442647D08EF37A59D94ED645B1D1B160E3E816E91891CAC17FE493C7167C6CB59DCB69B27A42D2C870DC96BC6C551218C44CC4CE8D23E64E6BC18208CEEB282E38732636F76678A27F2F5A436073D5ADA4AB3BD0EAA36F9A648905B277F0BD24B1A33982BE0E3673E7872BF79BBE2A45F4BD93013CAE3986083F8D321273D68CA9784D0A7FCF71DAC023718153FFB761BAEBEFC784C5EA8CE3897F153336047D2FE3E0FE776B4476A4F029E5EA9293E3C1BCA1302A5F9A73B07D83699EB9DE1D86DB7BE6C43F6F62F1EEE0BDB3484F325053B6C5579556F710EACFAD9319AA85B89F6F96C88E46C1C2BA0B583F30FE0248A794287D27FB1CA40821294B1AFC868F3A6FF4602DF76A24010DE5A1353043CF178E47F98AA9DF5E7314D3D5571EF6B9528477CBF7DCA60F913FD9C82AC65212EBB2BEB1C2BC4B5C363FCF5A0466883079A92AB8B9254ADBDCE17F0A719815DEFF7E75A30CEAA03AF66E44A85DC66DEB20C9C37E64CE86B615573C4C42BBE71DACD8284CB50259A96CCED1C1C64D8B603024BE346B07869425CBCA54F93D7A1F403560EE2023B03033A972E28E4A21C7005F4E1FD4BC99AEA3BCC6B9066EC6329D43079BDF2DAD2CE6FB7D21BBD76A7ABF486B933D9914169C2A704E52EC6D7E4E1DFD854A50372E5D301367D8E98CC880288CE94A2C43B01825CE5F271135481BB273A43C713898BA7D7D2B6BC8673A7AAA13873CFBDED482C0B7B435025A9F1CF43F168CD782896F22C56A92A09EA7E162BCDF1C8686E68810FE90B16ECAB4614795C18EBA59E3CB6359DE7CCE9E8751F903CFEA7D36D56552CDEF806215EA7596CC189A2E8F529EB139DCA2033109F40B453912532144CED54B7D4049BC8B8CF2C6DBBF405A056A80CA788267538FE8F02EB5E272874CE244A328BA6410480B4C263BFB611CBD9D9C7FF6B1A9E32766969CC3EB61A9907F5F22251239A9EB38ED726A3EE922EDFE52206C2191E1F045F41344C1E04A9D97668A240D82396AC0219067BAA44C264E9A2AEC292390A6F4921176621BD24D35670B08D6A065806B02DF0D31D14D81FD086E8E32479919FDBC44BAD80B0BA01E971ADC4139D6DE0C3676C59131EFFAE14058D99E22698B602D7604D9F3110F8440917ABCEA49710ADA4584FCBB487171176C4318082EFEFDF7019D3B42FA31A9F9175759E6C3193A07C324FF5F71A974F13F5D83226441E3BDD9DF41408DFF80DE7C9571706B39038FFBB99A524AC23D74047D814EC0AEDBE2877C855E25345F6C7DB4237ECF64C874206500F822C1305F9D61F49FC57AFBF2C7538D97A78844C3C00740865E26755BFADBC4A6E4564041ADA094C603CABAA404C482E0707DE6DC1917727D00C4FA6BEE36B8996AFF98BEA6E2115B9D173321DBA4A5F38B104985D796ECDDD812B6051DC0F910CD5AF1E5734169459E170192C3B7D6914052503D377B01DB4E3A630D54D807506602ECF7D6B8C4D923317738ACE4949143D4D1441AA854331E7F511BF7BA140AED4756B26789498A17EBF62DF23E08B81ACB75FE2326A94ECC5968AC4CF26F088604368B17DDC09FF9D0146DE93AEB7AD76A6AD0AF4092F363421F1B3D234C0F78ECBEFCCDCE1EA6EC98C145BA300B0234F0C96125D33123CDD7D6A41CF1A160FAD7E744F08BA1454A99921159A9E8C14ACBCEC235529425CF86998E22D29CFEF3A6DC0EC67A9EA8523D6158513971F979FC906FADD982D7F08E4F056A95C07D7FACF2CD36DF362116A2DD5FCD62AD57393A38607436FAF0985C2D505209ADE137B93BA0963528E3E1A40F2A545BDE9D1C11239ED70D93060F24E397720622F5194578B3C24B5DDDC7E303279E12338BF484106249754EAA6C441192F13303DC759CA65097EC87F8D854163C4A2A6EB6845723C1C790D693B596CDE3EF3D7C67417CA1FEEF03EF71441BDDFE214FB38A7511A87CF160F59CCA2B8E33DCFFDB5E44574D0D593A70ADA4C79474AC9D55D4A4FBB80C9B79C9077BA381B105C6D61B75312924E0BCEDCB4B8D55DDE650CB445AF48A77E8DB6E2EFBCE6FA74C9902E89253D7A172BABFA87DB948165AF4F5E8491EE7F87EB809D82AEC12DB1C11730D62BCA6F847B1457B5287BD12BEF65A32FF7383CFDB5A90C2F3B938372F3AA68FFE3B99DC92621782F3F9ED6718BD598BFA2C77E21DBD594EE0E5CDCB248CF533016A6AB1F84F85B2C5CD41A7A3B12F578353514CCC500ADD6C495A6CEE2DC099B37D200B4D3930A6DE07208E412C9F198D58A0F01F66DF07CF2116365B3532BA46716B02E761339DBAFBAAB00148187CA72EC46B522E4FE7E126152284C529CC4E23683AB90A24A6690662ED0D1EF0481593A3D95F0361C776D9A4D2D622171C73726DB6620FFDA6540D510EF218D92AED363C6829F7FA3BA346E0FB40CD83A5F0BFD0E1D7FE14299CECFB7C512F022157AEF0015E93F3737911A35EC7A5A88356152E95F36739AB5EF9F63F8FD0F15E2504A8F4FD751CA7799FFB1D0137C87257A8CBD18C218A867B3AB5F83DB0F1674F187DF1CB036DB33A05A0D723D1247EFF4CA8CBB378DF118369821E diff -Nru botan3-3.7.1+dfsg/src/tests/data/bn/divide.vec botan3-3.12.0+dfsg/src/tests/data/bn/divide.vec --- botan3-3.7.1+dfsg/src/tests/data/bn/divide.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/bn/divide.vec 2026-05-07 01:38:28.000000000 +0000 @@ -3,6 +3,18 @@ In2 = 5 Output = 0 +In1 = 129 +In2 = -16 +Output = -8 + +In1 = -129 +In2 = 2 +Output = -65 + +In1 = 129 +In2 = -2 +Output = -64 + In1 = 0x1234567 In2 = 0x103 Output = 73701 diff -Nru botan3-3.7.1+dfsg/src/tests/data/bn/from_radix.vec botan3-3.12.0+dfsg/src/tests/data/bn/from_radix.vec --- botan3-3.7.1+dfsg/src/tests/data/bn/from_radix.vec 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/bn/from_radix.vec 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,233 @@ +[Valid] + +# Decimal inputs for 0 to 40 characters + +Radix = 10 + +Input = +Output = + +Input = 2 +Output = 02 + +Input = 14 +Output = 0E + +Input = 432 +Output = 01B0 + +Input = 2819 +Output = 0B03 + +Input = 70013 +Output = 01117D + +Input = 489083 +Output = 07767B + +Input = 9637940 +Output = 931034 + +Input = 36542351 +Output = 022D978F + +Input = 261559407 +Output = 0F97146F + +Input = 9161849593 +Output = 022216BAF9 + +Input = 20341316475 +Output = 04BC6FDB7B + +Input = 355341928327 +Output = 52BC078787 + +Input = 7483503056413 +Output = 06CE638A961D + +Input = 63767242388496 +Output = 39FEF7D4B410 + +Input = 753287101226916 +Output = 02AD1C52151FA4 + +Input = 7978480184514627 +Output = 1C5862AD3ED843 + +Input = 14828148932528809 +Output = 34AE1F75CCCAA9 + +Input = 670154303911718227 +Output = 094CDDC9ED5B6D53 + +Input = 9248963834657871331 +Output = 805AEB9AC13795E3 + +Input = 60983930103105183473 +Output = 034E52715272D5DEF1 + +Input = 929973763116566701065 +Output = 3269FA8257A439E409 + +Input = 2333872624731781080132 +Output = 7E84FD32CE725C2444 + +Input = 77736026064746872343098 +Output = 1076141322C5001C8A3A + +Input = 150097882081219136193990 +Output = 1FC8D28ED434E04C05C6 + +Input = 2699854353462475107991183 +Output = 023BB765D450B21EB5FE8F + +Input = 94251354278498084124118244 +Output = 4DF6802746B1380C004CE4 + +Input = 453487401640052427868011280 +Output = 01771DB14D7A4F9C5BB6FB10 + +Input = 6982620450533158692322602563 +Output = 168FE3651B0144F9F014EE43 + +Input = 52160733754330365414586850142 +Output = A88A5902BE807F2D532E1B5E + +Input = 501965569816934060883561595148 +Output = 0655F030016C157FD008A6C10C + +Input = 5656482366299468044369957773872 +Output = 4765148C746439C1A797006230 + +Input = 24895134332003791769367632016328 +Output = 013A38847761D952560A8969F3C8 + +Input = 808317278895798687277434873471434 +Output = 27DA65FDE4EBD0E9FECAC0DE61CA + +Input = 6581223623166587603669096705466889 +Output = 01447AB86BF59ADA36C84FAC0EA209 + +Input = 47346706562729806990162720465375564 +Output = 091E5F73E8ED635915A4EB43A4514C + +Input = 741708053100330923271937452991241904 +Output = 8ED907C7630B9978F3F1B3A276DEB0 + +Input = 7631931491905865185067165726284987769 +Output = 05BDDB462A256F99A0A533245B303D79 + +Input = 55314737996507527354549480831367837770 +Output = 299D3B249B9954CEC55612552338F84A + +Input = 243634957885685574443135182337498941343 +Output = B74A62D6BD2C7E68A6D690359054079F + +Input = 6240824008427109477752047116719022941318 +Output = 125712FBC9CE29F7429AA5A21A0F382486 + +# Decimal with leading zeros +Input = 000123 +Output = 7B + +Input = 00000 +Output = + +Input = 340282366920938463463374607431768211455 +Output = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + +Input = 0000000340282366920938463463374607431768211456 +Output = 0100000000000000000000000000000000 + +# Hexadecimal inputs + +Radix = 16 + +Input = 0 +Output = + +Input = 00 +Output = + +Input = 000 +Output = + +Input = F +Output = 0F + +Input = FF +Output = FF + +Input = ff +Output = FF + +Input = ABC +Output = 0ABC + +Input = 0ABC +Output = 0ABC + +Input = abcdef +Output = ABCDEF + +Input = FEDCBA9876543210 +Output = FEDCBA9876543210 + +# Odd-length hex +Input = 1 +Output = 01 + +Input = 123 +Output = 0123 + +Input = 12345 +Output = 012345 + +# Hex with leading zeros +Input = 00FF +Output = FF + +Input = 000001 +Output = 01 + +[Invalid] + +# Unsupported radix +Input = 101 +Radix = 2 +Output = BigInt::from_radix_digits unknown radix + +Input = 777 +Radix = 8 +Output = BigInt::from_radix_digits unknown radix + +Input = 0 +Radix = 0 +Output = BigInt::from_radix_digits unknown radix + +Input = hello +Radix = 36 +Output = BigInt::from_radix_digits unknown radix + +# Invalid decimal characters +Input = 123abc +Radix = 10 +Output = Invalid decimal character + +Input = -42 +Radix = 10 +Output = Invalid decimal character + +Input = 1 2 +Radix = 10 +Output = Invalid decimal character + +Input = 12.34 +Radix = 10 +Output = Invalid decimal character + +# Invalid hex characters +Input = ABCXYZ +Radix = 16 +Output = hex_decode: invalid character diff -Nru botan3-3.7.1+dfsg/src/tests/data/bn/lshift.vec botan3-3.12.0+dfsg/src/tests/data/bn/lshift.vec --- botan3-3.7.1+dfsg/src/tests/data/bn/lshift.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/bn/lshift.vec 2026-05-07 01:38:28.000000000 +0000 @@ -199,3 +199,8 @@ Shift = 0xA5 Output = 0x4528D49E48D6339D281BC2CA1BFA239B45CC623B5A445C7FA42D02A22D23FAA27C8F60C5759452BD802567BC1F8B279577800000000000000000000000000000000000000000 +# Regression Test for GH #5128 reported by @hgarrereyn +# Shifting an "empty" (aka zero) big int caused a segfault +Value = 0x0 +Shift = 0x0 +Output = 0x0 diff -Nru botan3-3.7.1+dfsg/src/tests/data/bn/mod.vec botan3-3.12.0+dfsg/src/tests/data/bn/mod.vec --- botan3-3.7.1+dfsg/src/tests/data/bn/mod.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/bn/mod.vec 2026-05-07 01:38:28.000000000 +0000 @@ -3,6 +3,10 @@ In2 = 1 Output = 0 +In1 = -129 +In2 = 31 +Output = 26 + In1 = 5 In2 = 1 Output = 0 diff -Nru botan3-3.7.1+dfsg/src/tests/data/bn/rshift.vec botan3-3.12.0+dfsg/src/tests/data/bn/rshift.vec --- botan3-3.7.1+dfsg/src/tests/data/bn/rshift.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/bn/rshift.vec 2026-05-07 01:38:28.000000000 +0000 @@ -211,3 +211,8 @@ Shift = 0x28 Output = -0x2C74CC8EB77FB260B99B22CF68FA5DE561B0F1D3D6248FD4FC9A32 +# Left-shifting an "empty" (aka zero) big int caused a segfault. This test is a +# right shift and was added to mirror the regression test in lshift.vec. +Value = 0x0 +Shift = 0x0 +Output = 0x0 diff -Nru botan3-3.7.1+dfsg/src/tests/data/charset.vec botan3-3.12.0+dfsg/src/tests/data/charset.vec --- botan3-3.7.1+dfsg/src/tests/data/charset.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/charset.vec 2026-05-07 01:38:28.000000000 +0000 @@ -12,10 +12,194 @@ In = 0A0000000000000000000000000030000000000000001D1D1D1D01000000000000001D1D1D1D00000000000000000000 Out = E0A880000000000000E38080000000E1B49DE1B49DC480000000E1B49DE1B49D0000000000 +[UTF8-UCS2] + +# Empty string +In = +Out = + +# U+0080: minimum 2-byte sequence +In = C280 +Out = 0080 + +# U+07FF: maximum 2-byte sequence +In = DFBF +Out = 07FF + +# U+0800: minimum 3-byte sequence +In = E0A080 +Out = 0800 + +# U+D7FF: maximum codepoint before surrogate range +In = ED9FBF +Out = D7FF + +# U+E000: minimum codepoint after surrogate range +In = EE8080 +Out = E000 + +# U+FFFF: maximum 3-byte sequence (BMP) +In = EFBFBF +Out = FFFF + +In = 426F74616E +Out = 0042006F00740061006E + +# Nonsense, converted with iconv +In = CEB4D296D596E0A5A7E181B5E1A0A7EFBFB0 +Out = 03B404960556096710751827FFF0 + +In = EB8AB2EB8AB2EB8AB2EB8AB2EB8AB2EB8AB2EB8AB2EB8AB2EB8AB2EB8AB2EB8AB2EB8AB2EB8AB2EB8AB2EB8986EB8AB2EB8AB2EB8AB2EB8AB2EB8AB2EB8AB2EB8AB2EB8AB2EB8AB2EB8AB2EB8884 +Out = B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B246B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B204 + +In = E0A880000000000000E38080000000E1B49DE1B49DC480000000E1B49DE1B49D0000000000 +Out = 0A0000000000000000000000000030000000000000001D1D1D1D01000000000000001D1D1D1D00000000000000000000 + [UCS4-UTF8] In = 0000004800000065000000690000007A000000F60000006C00000072000000FC000000630000006B00000073000000740000006F000000DF000000610000006200000064000000E40000006D0000007000000066000000750000006E00000067 Out = 4865697AC3B66C72C3BC636B73746FC39F616264C3A46D7066756E67 +[UTF8-UCS4] + +# Empty string +In = +Out = + +# U+0080: minimum 2-byte sequence +In = C280 +Out = 00000080 + +# U+07FF: maximum 2-byte sequence +In = DFBF +Out = 000007FF + +# U+0800: minimum 3-byte sequence +In = E0A080 +Out = 00000800 + +# U+D7FF: maximum codepoint before surrogate range +In = ED9FBF +Out = 0000D7FF + +# U+E000: minimum codepoint after surrogate range +In = EE8080 +Out = 0000E000 + +# U+FFFF: maximum 3-byte sequence (BMP) +In = EFBFBF +Out = 0000FFFF + +# U+10000: minimum 4-byte sequence +In = F0908080 +Out = 00010000 + +# U+10FFFF: maximum valid Unicode codepoint +In = F48FBFBF +Out = 0010FFFF + +In = 4865697AC3B66C72C3BC636B73746FC39F616264C3A46D7066756E67 +Out = 0000004800000065000000690000007A000000F60000006C00000072000000FC000000630000006B00000073000000740000006F000000DF000000610000006200000064000000E40000006D0000007000000066000000750000006E00000067 + +# Non-BMP code point U+1F600 (😀) - valid in UCS-4 but not in UCS-2 +In = F09F9880 +Out = 0001F600 + +[UTF8-UCS2-INVALID] + +# Truncated 2-byte sequence +In = C3 +Out = + +# Truncated 3-byte sequence +In = E282 +Out = + +# Bad continuation byte (0x00 instead of 0x80..0xBF) +In = C300 +Out = + +# Overlong 2-byte NUL (0xC0 0x80) +In = C080 +Out = + +# Overlong 3-byte sequence (0xE0 0x80 0x80) +In = E08080 +Out = + +# Surrogate U+D800 (encoded as ED A0 80) +In = EDA080 +Out = + +# Surrogate U+DFFF (encoded as ED BF BF) +In = EDBFBF +Out = + +# Invalid leading byte 0xFF +In = FF +Out = + +# Code point > U+FFFF (U+1F600, encoded as F0 9F 98 80) - valid Unicode but not encodable in UCS-2 +In = F09F9880 +Out = + +# Invalid continuation byte: C2 followed by 0x79 (< 0x80) +In = C279 +Out = + +# Overlong 3-byte: U+07FF encoded as 3 bytes (second byte 0x9F < 0xA0 required for E0 lead) +In = E09FBF +Out = + +# Overlong 4-byte: U+FFFF encoded as 4 bytes (second byte 0x8F < 0x90 required for F0 lead) +In = F08FBFBF +Out = + +# U+110000: one above maximum Unicode codepoint (second byte 0x90 > 0x8F allowed for F4 lead) +In = F4908080 +Out = + +[UTF8-UCS4-INVALID] + +# Truncated 4-byte sequence +In = F09F98 +Out = + +# Bad continuation byte in 3-byte sequence (0x00 instead of 0x80..0xBF) +In = E28200 +Out = + +# Overlong 4-byte sequence (0xF0 0x80 0x80 0x80) +In = F0808080 +Out = + +# Surrogate U+D800 (encoded as ED A0 80) +In = EDA080 +Out = + +# Invalid leading byte 0xFE +In = FE808080 +Out = + +# Code point > U+10FFFF via 0xF5 lead byte (decodes to U+140000) +In = F5808080 +Out = + +# Invalid continuation byte: C2 followed by 0x79 (< 0x80) +In = C279 +Out = + +# Overlong 3-byte: U+07FF encoded as 3 bytes (second byte 0x9F < 0xA0 required for E0 lead) +In = E09FBF +Out = + +# Overlong 4-byte: U+FFFF encoded as 4 bytes (second byte 0x8F < 0x90 required for F0 lead) +In = F08FBFBF +Out = + +# U+110000: one above maximum Unicode codepoint (second byte 0x90 > 0x8F allowed for F4 lead) +In = F4908080 +Out = + [LATIN1-UTF8] # Botan diff -Nru botan3-3.7.1+dfsg/src/tests/data/codec/base58.vec botan3-3.12.0+dfsg/src/tests/data/codec/base58.vec --- botan3-3.7.1+dfsg/src/tests/data/codec/base58.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/codec/base58.vec 2026-05-07 01:38:28.000000000 +0000 @@ -51,6 +51,69 @@ Binary = 00000000616263 Base58 = 1111ZiCa +# Single zero byte +Binary = 00 +Base58 = 1 + +# All zeros +Binary = 0000 +Base58 = 11 + +# Single byte at each character range boundary +Binary = 01 +Base58 = 2 + +Binary = 08 +Base58 = 9 + +Binary = 09 +Base58 = A + +Binary = 10 +Base58 = H + +Binary = 11 +Base58 = J + +Binary = 15 +Base58 = N + +Binary = 16 +Base58 = P + +Binary = 21 +Base58 = a + +Binary = 2B +Base58 = k + +Binary = 2C +Base58 = m + +Binary = 0D +Base58 = E + +Binary = 0E +Base58 = F + +Binary = 0F +Base58 = G + +Binary = 1C +Base58 = V + +Binary = 25 +Base58 = e + +Binary = 28 +Base58 = h + +Binary = 2F +Base58 = p + +Binary = FF +Base58 = 5Q + [invalid] Base58 = 0 @@ -65,4 +128,8 @@ Base58 = t$@mX<* Base58 = AreYouEvenLookingAtThese? +Base58 = abc:def +Base58 = abc[def +Base58 = `abcdef + diff -Nru botan3-3.7.1+dfsg/src/tests/data/hash/ascon_hash256.vec botan3-3.12.0+dfsg/src/tests/data/hash/ascon_hash256.vec --- botan3-3.7.1+dfsg/src/tests/data/hash/ascon_hash256.vec 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/hash/ascon_hash256.vec 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,325 @@ +[Ascon-Hash256] + +# From NIST's ACVP: https://github.com/usnistgov/ACVP-Server/blob/master/gen-val/json-files/Ascon-Hash256-SP800-232/internalProjection.json +# Only test vectors that have a byte-aligned input length + +In = +Out = 0B3BE5850F2F6B98CAF29F8FDEA89B64A1FA70AA249B8F839BD53BAA304D92B2 + +In = 50 +Out = B96DA347D720272533A87F5A94A356155F49CDF7C0C10A3E6F346D8A2293E480 + +In = 8214 +Out = A2387C22040DB2DACDCB763A64D4D33B19EB33840CF3F396529BCC985BB71CC4 + +In = 011BF0DE +Out = 4A3DDFF9256208CE57BB26913417CEFAE50843DD08C8F790E12B071D46544B53 + +In = C215227BFC213A3A +Out = FFEDC2EA8BBD9DB05ACE229B6403612E10AA5F3AE3B5C66D244A3384CCDAFAF3 + +In = 7FDAE5E338E663624D6550FC246EE8B1 +Out = 772EC06D8EE009B3B1FF7E9202DD9EFB1E92F35E93EE1409BDA21E30B62BFCE7 + +In = 2714E23621B9C5A8D7F2F560F45EFB3D +Out = CE75A081DFBDF588A6FBD07C1B09D7F10FCEC45F524343F5BCA56EA3EBDCEC5B + +# From the Ascon reference implementation +# Only the first 100 test vectors + +In = 00 +Out = 0728621035AF3ED2BCA03BF6FDE900F9456F5330E4B5EE23E7F6A1E70291BC80 + +In = 0001 +Out = 6115E7C9C4081C2797FC8FE1BC57A836AFA1C5381E556DD583860CA2DFB48DD2 + +In = 000102 +Out = 265AB89A609F5A05DCA57E83FBBA700F9A2D2C4211BA4CC9F0A1A369E17B915C + +In = 00010203 +Out = D7E4C7ED9B8A325CD08B9EF259F8877054ECD8304FE1B2D7FD847137DF6727EE + +In = 0001020304 +Out = C7B28962D4F5C2211F466F83D3C57AE1504387E2A326949747A8376447A6BB51 + +In = 000102030405 +Out = DC0C6748AF8FFE63E1084AA3E5786A194685C88C21348B29E184FB50409703BC + +In = 00010203040506 +Out = 3E4D273BA69B3B9C53216107E88B75CDBEEDBCBF8FAF0219C3928AB62B116577 + +In = 0001020304050607 +Out = B88E497AE8E6FB641B87EF622EB8F2FCA0ED95383F7FFEBE167ACF1099BA764F + +In = 000102030405060708 +Out = 94269C30E0296E1EC86655041841823EFA1927F520FD58C8E9BCE6197878C1A6 + +In = 00010203040506070809 +Out = 894F5C5BC78A0A97ABC0D63123D09A335FB0D92430ADF9D11FD2643854179968 + +In = 000102030405060708090A +Out = 688466B9EC5070476CEB939FE368C2A32C0F1A795AF761942D5518909A1081DA + +In = 000102030405060708090A0B +Out = 8CAFA656807FAFA3DE9FEB2FB566D6239BEBA74AE48C4E9A4CED5BF1B13A75C9 + +In = 000102030405060708090A0B0C +Out = 94883D2A44E8F13964F926DE553583DB7B1A82B0AACC58EF2D4846A5D6E8B4AC + +In = 000102030405060708090A0B0C0D +Out = BEB6353F24E8DEE9F2B99279F29F425F7CCE3098A3665B7AF3AF86F759CC985D + +In = 000102030405060708090A0B0C0D0E +Out = 6421330DF99C05EB715415EE17B455F2674F862AE3CC5BADFFE43A4A3ED273E1 + +In = 000102030405060708090A0B0C0D0E0F +Out = 3158C1940A2FBADBD68AB661777859B94A689E4EFC375911467ADDD641835C38 + +In = 000102030405060708090A0B0C0D0E0F10 +Out = F149E99DD0F429599BB89B8079BF3F4DCA3F298EFEFCF9B1EA16FE84F9B8B6E2 + +In = 000102030405060708090A0B0C0D0E0F1011 +Out = D070F7BA0E9BCDB6B34E8357343E9041943146F334FDAF6E2009275F6F25CBED + +In = 000102030405060708090A0B0C0D0E0F101112 +Out = 759401BA2D3373EB5A0152BDE6FCC726EC65DEE795A574F1D715F3CAC21F0381 + +In = 000102030405060708090A0B0C0D0E0F10111213 +Out = 98941E484FBAE87452DDAF6030088B798A56C36EB6D4D3E94B5CFF78B20E0481 + +In = 000102030405060708090A0B0C0D0E0F1011121314 +Out = 41C8F733B9D823BE30B64EE717C322C576D36781FFC5F7D6C730ECA549789725 + +In = 000102030405060708090A0B0C0D0E0F101112131415 +Out = 04183F603CF56EA25E5C299D3FBDB17228D6411F15E9F77C7789CE89EC9B8B0E + +In = 000102030405060708090A0B0C0D0E0F10111213141516 +Out = B4F88D121EDDF6D1FEA9AEF15F68A0F3A16D3D2CDD9817225809C20452B04C61 + +In = 000102030405060708090A0B0C0D0E0F1011121314151617 +Out = 7E6A31FA6559536A7AD61622F6150FA3B2A29EBBF39AD8011B7902CC612571E6 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718 +Out = ACF39FB49CBAA0B4BC04C548224543B75019BA639CE4D0A58CAEDAF17E0F8D9F + +In = 000102030405060708090A0B0C0D0E0F10111213141516171819 +Out = B1BB948EDA56009F5D66ED7BDC5894E5E772D4341BB70405C365518976EB9573 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A +Out = B16EE200DD3E0C85CCCD7FFC3D6BD71EBCB76B2F4C033AB0602F686FCADFC3FA + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B +Out = 0986ECA8E631D184CF4A15F4A58D4A17625E66FF0EDE486BA119E5D4155A1545 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C +Out = 41DF85F3647236939ADBB88ABD30A5E052723E25DB09AB23F28BBA554056F5EB + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D +Out = 84B68FDEB9F5DFBA8FBE0314C4C79A1E3EAE97C9B018FCAC88FD9BD407086E70 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E +Out = B900CD3F06F1618B68C16665807206DBE273DF40135361F449847D573903FABD + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F +Out = BD9D3D60A66B53868EAB2A5C74539A518A1F60F01EB176C60E43DEE81680B33E + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20 +Out = A58665A2CB9530C502096A7957A76E428AF4AD044B4DA5C471F9DA6F7B3E5868 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021 +Out = 233B38DC792B0BF3B4345A4C12AE6EF258907F9F23BB8A48FB3D2C53C171C476 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122 +Out = D10C48FE22F41CEF300A095CAE25C3568FA2E48FF6A0AFF0E6457C7A40013588 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223 +Out = 6C735583F60D13574DA79B62BA7D3E87F5FD0935D22697ECAFC17F395825AE78 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324 +Out = F24919E8FEA2718CBF9A8BF098381E0BE66D1B9654AEC04BEF0012AD9D0B382D + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425 +Out = D92A873036D0328EB835766697726CC81A6AA22ADA1A52248BE8044C65C3EBBF + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223242526 +Out = BA2AAD77830B4F5CFFDA771FEFEF9E8C1E947C97A107725F0D37E69AF5B2FDC1 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627 +Out = 30B780A62AF595CA6D21A67BCF88AE12DAA3E336DBBA52F4112D3716BC0E633C + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728 +Out = D3C061333D563AFEDAE0907AED1B398A0D9BBA6A7BF38111C198D3734985AA30 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223242526272829 +Out = 0D1A50491718B30024652B02F6BBC8FD648996289B80BC689B8863F92AF3F02A + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A +Out = B4CB3655CFD0FF0CD9590F6F24C5F34CF9B2C6FAC877CD3E0394254D61ACA92C + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B +Out = CC140BC46B2F71E22BDC553F7939EA69EB6941DACECB1DD7E36416C63C8ECD1F + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C +Out = E0B68F415B408260D18E70CF9C9D0C0E73A87344814F6DE5B4769B0AC388BDDF + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D +Out = 09A37F0B5D1C582A39B076A429085DB48FED6DD4A19619CA66CF29396B34911D + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E +Out = EF8D8652ABD1F6C1AD298F3750D4D459F1E0A823D8415C224CB50EDCF3475199 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F +Out = B1F1672071C9F75A5FB22BAFC96722CD9705DB76D34D00170E07D077B2224863 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30 +Out = C1548DC2089F067407D0EE602AC14F2B3C51D959BB880B7FAF61AC11D740A6FD + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F3031 +Out = 56C4DF3D75039DB96B97512908BAA1C76321F20480B8EAE55BD97F48B3B4CBFB + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132 +Out = A18B3B8A87B0A3BA2066734E20D1F2646552AE116A3531AC0C483987C3FE5426 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233 +Out = A09D3448158D824657E24963B0DD7181BD11EEC4AD1815A0CB7C0A45AF4F1E0A + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F3031323334 +Out = D2D10DC1B32B54A6ED866E9AE5EBD0D30E175DC0211B945F0B1D97A42F65860E + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435 +Out = EC5B70B8B9CB7703734915E4A73DD22684CC796781C8AF298B76C9C220FC2CCD + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233343536 +Out = 7E1AC099003D8DBB94B6174E1D9AC94806FEA0505DECBB2F44EDF7A7F4C55D9F + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F3031323334353637 +Out = F5D3134B3C0894738024A96CBB56FC2CDAB72D99FD9E97965ADCB6B8A243DE09 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738 +Out = 7A736E2E8ED2CFFFC3AC4BE0184D7FBFE465343A2F867A8350BE8E3D0821EA76 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233343536373839 +Out = 36BB5CCAFDBDEAD84933CDF84AE51523289BA2BF5D662FDAE67353E5C31BE81F + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A +Out = 09CC7BCCBF908D951FB0D926298E0F546BD6A7A97724AEFD26D6F7E59D208B46 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B +Out = 4A99FA5104DCF3B3DB1ABF9989F605DB41F47E996498E89508393BD498010D14 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C +Out = 55606349F739943D5058607CEAC1969F855D28B9A1A9B3ACD6FEDD4B396AC50F + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D +Out = 1033934CD0B88FF3753D64CE194472C40BAF644CD8B0FE6AE8E5D1270AED4828 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E +Out = 5072896862F6B9CFE8EF76D80559E156254782A40AC5F64CBF7934AD1F624B30 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F +Out = A6F241BEA5D16405812C06019D9F72D60132BD7C089C60549B2E56BB01C64F48 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40 +Out = BFF4FA006FE6FEABB5CE9B219492D0D230F4D05F2BAC42DB7189F441B1E83B53 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F4041 +Out = 75990E9627BBB8C3F7C9E1F38CB41B51E511058E619011F0D567E1C03160151F + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142 +Out = F1FE88E3EA13AE2FBA90427D2F2DDD29002A34FF19891A2EA5975E1679BE51D0 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40414243 +Out = D2FF3D2E180C7B440185036CBFADEF95A7DE897EB1EDD565CB112845AD632FB2 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F4041424344 +Out = 9C6B596A7B55E06A857665C01536EB98621D5EFAA186926BAC4C4BC72680F5A5 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445 +Out = 4AE57CDDE706247B6A0F3FEB5E2E9691358A34BC127FE629E5F0885E751C6847 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40414243444546 +Out = F4D25E0CED015965CA64F0FA2A0243EA4B6A2A288691D331923F8915B157C8E5 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F4041424344454647 +Out = DCB21F4833A5060616E1E6F0AB190833CCF65CE6A96E93336582AE732EDB55CA + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748 +Out = 921019C19A287F9A269986EAE4F1C27AF9AC6DA28487ECF9AE7057EABC6E156E + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40414243444546474849 +Out = 6221D9D6AD7839127F69601B1DAFBD5C2DEAA7D7F65BE0F10D1C766F187DB3A1 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A +Out = 18891B924C3786466E2948A164DFFDFCCC39D90E02FEB223EE59C684603330EE + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B +Out = F0F6D114D5A9D7258931B0C230CAB42D5FDD885E1040B164D67C5E0D819DF0F5 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C +Out = B8FB1C67457EDA6435FC12D66A1D9C8FCB84AF39E366E12D919C4ACEABC89A5F + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D +Out = 09E52E20E9276CD93DE580CD62247C290667869B8DD0594EE206FA6C803E66B3 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E +Out = F670CE042542F094170AAE96529D9A668DE5007CED226D0CC33D8935C589ECFE + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F +Out = 1256EAF42DA269CDF7B4060FBFAC5C0C9E4F157D75A4280D5AE392B47EA73A8D + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50 +Out = 7ECC08FC32C0D08DCBB1A7D9D6593D613255CAE7A0660D8458C89BD15F146132 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F5051 +Out = 63E69CB1ED972EBDEFB61DFA172D4E7D26BE9A96940718B3E3EA6E245C34D23D + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152 +Out = C4E86ECB29D9A5BC01637D116283DF12C364A6E47D6E5A175EBA866ED8875A4E + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50515253 +Out = 626F7954419A3621A51A4E5B8E5122FCA4642CF4786A7805927E40C37723C007 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F5051525354 +Out = 07D14CB0768FB8B662C9615A55F36C6FE5B04C109A28A62BF6A38221B7B33C29 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455 +Out = 5629F63E46BE5470DD6682EA8E0A3D942DE273BCAEDD8084F2EE9212B29053E2 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50515253545556 +Out = 4146445DDCD693057DEC80457BC46FD6BB65C26EDFB78D42568BEC94D99E1DF8 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F5051525354555657 +Out = C0F262A58F3E664964375EDC2975FCCE1F4ECAF18C0258E72DB4D25C9E08CC69 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758 +Out = 08FE27A3F820D8C52D624DD938AED161536E1A33E5CE4CC6239DF05F0A59A751 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50515253545556575859 +Out = 9C13F4CE1E975A801F374F2F6470F18FDE9F0DE8A8DF6B4E36758BB026764542 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A +Out = B5E00574C2BA7A65EA9DF2FC0F706782C7BDA27EBF5B24E19A20ABB86E3B6F04 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B +Out = D7CE4559E0A45E4B0BE31270449FB1D47C57E179E3A4431AA4723F320AF92A7D + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C +Out = 00FF7C40F1DEC062D921C241A6D5FD20AB4801EB8DC98EF20AA0A4E973169F23 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D +Out = 1DBD867B3D4071CDEDEFCE2979079A0D148FFFB90189E24F3E494352D55B32B8 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E +Out = 58DBF708687DF397483FA2BE310647FC11F24DB0CC0723C1B079EC2DD433FA26 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F +Out = EFA6AF2D55DADDD7C47960671F4522F346DB32F509F1390B9149FDA55E88B705 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60 +Out = E005A4129CA6C441D92C592F422C7C80936E78F6A627C3CB17327FAF0E5F7604 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6061 +Out = 86CE35146F77129EA1A2D3A242EA6BA638C26994EF7870747D2CA7C591F6FFE5 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162 +Out = 31B3C6BB3EF3FEE1C820F2BDA31A52387AAAE816003DDB992AC03D2DD9C6E005 diff -Nru botan3-3.7.1+dfsg/src/tests/data/hash/blake2b.vec botan3-3.12.0+dfsg/src/tests/data/hash/blake2b.vec --- botan3-3.7.1+dfsg/src/tests/data/hash/blake2b.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/hash/blake2b.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,3114 +1,2325 @@ -[BLAKE2b(224)] -In = -Out = 836cc68931c2e4e3e838602eca1902591d216837bafddfe6f0c8cb07 -In = CC -Out = 1a926bc943aed563be0f1b2c7e3a64d962e054509018a422d2bdcbbc - -In = 41FB -Out = 0f13ab23fcea3b2ea5d55452af7266fc40c3c9cf5dd19d26fcefbee7 - -In = 1F877C -Out = 5517b00622a841ed3c3e1579cb9a298f49f293a9f6c97b2f6bf06ec8 - -In = C1ECFDFC -Out = da3dbd8c051292cef4492561cf9e13f2633d4a53a042ca6fdb7460c5 - -In = 21F134AC57 -Out = d34caff9fe2a54f665d9f0422d0141acf5585c7a8f222f3fb8d3696c - -In = C6F50BB74E29 -Out = 746a72a362ac79c5f077820b180fb5fcc5d1aff00d9b71e627c74036 - -In = 119713CC83EEEF -Out = 075194e4614bf009bfa810759489994200190c1f2712daf5766dfd24 - -In = 4A4F202484512526 -Out = 9989709d0c5be9353b902acac4617dc028d4195e0a83fcbf3fefa4e0 - -In = 1F66AB4185ED9B6375 -Out = adcac140e02efe54d91be9c3997cec2fd9983e12a83570dfc7d332d6 - -In = EED7422227613B6F53C9 -Out = ce11dbad8b796f09b88a6eda279e22e18b8da147013a9e39bc898d77 - -In = EAEED5CDFFD89DECE455F1 -Out = b9a77e554a3c22c6a1c8a7636fb369a77a9e612f8994fa49b5ab56e6 - -In = 5BE43C90F22902E4FE8ED2D3 -Out = 37d1addd0dcc99c57268772002531a6c3fd24c26119f1ca1e6b76247 - -In = A746273228122F381C3B46E4F1 -Out = 6f932b21814f9c82eb1d7646addad7a6445f45afc6d3d8fafb501ee7 - -In = 3C5871CD619C69A63B540EB5A625 -Out = 1a5de095c488219be1db8c34f78d0f7c052b4be2a2845b14fe342ddc - -In = FA22874BCC068879E8EF11A69F0722 -Out = 6f1d50881e662a1b73e13305335763066f91d0a7917f8bd26ada916c - -In = 52A608AB21CCDD8A4457A57EDE782176 -Out = 6f08fdf677c07c2e13b508e68c38adcb708d0c08911c24cf35c15231 - -In = 82E192E4043DDCD12ECF52969D0F807EED -Out = cf679ef6dc5fa29c269d7298971aa77768fe702e9017a0ea1f203590 - -In = 75683DCB556140C522543BB6E9098B21A21E -Out = f4391b3678b07490e49a7890eee47da35cd3f697d5dcd8872a824d12 - -In = 06E4EFE45035E61FAAF4287B4D8D1F12CA97E5 -Out = b0edeb4dd2ca86a9d7e8dd553b8a2e1b6b3820e67ca3b042239ed13a - -In = E26193989D06568FE688E75540AEA06747D9F851 -Out = 1a873bf701c5b0727be7828de917d9a0c80f50e49ad75dc9c5f736b6 - -In = D8DC8FDEFBDCE9D44E4CBAFE78447BAE3B5436102A -Out = 3782e0f4fe2239cb4d01e1024ecd0309a163d97763b7dc9cdcdaf5f4 - -In = 57085FD7E14216AB102D8317B0CB338A786D5FC32D8F -Out = b33c243fe508dd1445f885472e3c9b3cc8768c285805e47ce62a45ec - -In = A05404DF5DBB57697E2C16FA29DEFAC8AB3560D6126FA0 -Out = 597a22938743e1c5eeee9a7051fbceb01cf8d19c4eb894b5e3b7cdab - -In = AECBB02759F7433D6FCB06963C74061CD83B5B3FFA6F13C6 -Out = 2ea1c5e7eab2c7cc9c698b0c9297610d2e8f25a9ceaee6f76368a232 - -In = AAFDC9243D3D4A096558A360CC27C8D862F0BE73DB5E88AA55 -Out = 294960eeb981139a6afc38f682c3cc7db486735a44da057992418e3a - -In = 7BC84867F6F9E9FDC3E1046CAE3A52C77ED485860EE260E30B15 -Out = 0797b09e6c66696f445087f126685a76e8e671f4be069438b2a9944c - -In = FAC523575A99EC48279A7A459E98FF901918A475034327EFB55843 -Out = e4a6172ed88ce97309e1e44c6281cbd51fed1cb729250b89cce91e98 - -In = 0F8B2D8FCFD9D68CFFC17CCFB117709B53D26462A3F346FB7C79B85E -Out = b8ff1920d46a6e5ee08f9b4d97efd1f32f8184fa5f14ef4579ad651d - -In = A963C3E895FF5A0BE4824400518D81412F875FA50521E26E85EAC90C04 -Out = 86ff91b6ad8a766ec67299e3a4fd492287d53a4bb47b971e00d5e40e - -In = 03A18688B10CC0EDF83ADF0A84808A9718383C4070C6C4F295098699AC2C -Out = f05931f0003226b005d7e0ae2a0353918785dcd6341934faf80693bc - -In = 84FB51B517DF6C5ACCB5D022F8F28DA09B10232D42320FFC32DBECC3835B29 -Out = 1b6143808c646484d4d1eea8fd0e01d829fa7ba58136a240b9baba2e - -In = 9F2FCC7C90DE090D6B87CD7E9718C1EA6CB21118FC2D5DE9F97E5DB6AC1E9C10 -Out = c8993fa6e6b5ee380157d2fe1aa7623dba31644b5bbf17073507553d - -In = DE8F1B3FAA4B7040ED4563C3B8E598253178E87E4D0DF75E4FF2F2DEDD5A0BE046 -Out = dbdc64e81245b2e02881f7a4c40f4f658d416ee6371ccd208b6e3f6b - -In = 62F154EC394D0BC757D045C798C8B87A00E0655D0481A7D2D9FB58D93AEDC676B5A0 -Out = fd86785b490be79b2ba14aa6e3a1e31b2102785e00ba76a82007428c - -In = B2DCFE9FF19E2B23CE7DA2A4207D3E5EC7C6112A8A22AEC9675A886378E14E5BFBAD4E -Out = 76b84ef62281b420cc7f502c6456d632ac6154aa523508e416f4b435 - -In = 47F5697AC8C31409C0868827347A613A3562041C633CF1F1F86865A576E02835ED2C2492 -Out = 394a5bc22413238584f25df59d8e51e3cbe57c6a78f8152d4aaafd04 - -In = 512A6D292E67ECB2FE486BFE92660953A75484FF4C4F2ECA2B0AF0EDCDD4339C6B2EE4E542 -Out = c9a154216dfaaaf986fa29640cfff12ec5d6ed7cb9d75f96d12b10cd - -In = 973CF2B4DCF0BFA872B41194CB05BB4E16760A1840D8343301802576197EC19E2A1493D8F4FB -Out = a7cc240c0a92de66c15ffcbafb2671590774b74b42ac4e9d8ced35fd - -In = 80BEEBCD2E3F8A9451D4499961C9731AE667CDC24EA020CE3B9AA4BBC0A7F79E30A934467DA4B0 -Out = 560cc57b1062ecd6cab5b9d695125a9eb1ddd3b2e902b466980e7f3c - -In = 7ABAA12EC2A7347674E444140AE0FB659D08E1C66DECD8D6EAE925FA451D65F3C0308E29446B8ED3 -Out = 6618ee0f49fb5187645b5606f2522d824a9f760eabfe7cefd649e78e - -In = C88DEE9927679B8AF422ABCBACF283B904FF31E1CAC58C7819809F65D5807D46723B20F67BA610C2B7 -Out = d4f392cc7ff19662012746dd693099a419012f0d311009a2a9c7234d - -In = 01E43FE350FCEC450EC9B102053E6B5D56E09896E0DDD9074FE138E6038210270C834CE6EADC2BB86BF6 -Out = 1039de3317a6203a2b48012b8d2ebb35eac9a73ac0e95a7fcb5cb80a - -In = 337023370A48B62EE43546F17C4EF2BF8D7ECD1D49F90BAB604B839C2E6E5BD21540D29BA27AB8E309A4B7 -Out = 23c59b050922bb833687d0d047f328a081c552fe1f0ced62faf035ba - -In = 6892540F964C8C74BD2DB02C0AD884510CB38AFD4438AF31FC912756F3EFEC6B32B58EBC38FC2A6B913596A8 -Out = 5850e3b51d1f5babfad30bb68cf1f3b6a34f0333cb1c9dc0335a7052 - -In = F5961DFD2B1FFFFDA4FFBF30560C165BFEDAB8CE0BE525845DEB8DC61004B7DB38467205F5DCFB34A2ACFE96C0 -Out = 77b15c630b6b01b806a545c6c190b805e96c487dab8ff9669650b490 - -In = CA061A2EB6CEED8881CE2057172D869D73A1951E63D57261384B80CEB5451E77B06CF0F5A0EA15CA907EE1C27EBA -Out = 3b7788e60b07e0155fac3b5b69d42671161d21016a92da40e97a560e - -In = 1743A77251D69242750C4F1140532CD3C33F9B5CCDF7514E8584D4A5F9FBD730BCF84D0D4726364B9BF95AB251D9BB -Out = 8e757c2abcdb1004dba65ae9b1f78fd118704cae119f401c6c629eb4 - -In = D8FABA1F5194C4DB5F176FABFFF856924EF627A37CD08CF55608BBA8F1E324D7C7F157298EABC4DCE7D89CE5162499F9 -Out = 43794731166168f1ca3239e0bae95fbca248e732959c99b363b23a4c - -In = BE9684BE70340860373C9C482BA517E899FC81BAAA12E5C6D7727975D1D41BA8BEF788CDB5CF4606C9C1C7F61AED59F97D -Out = 22a7a42d55cdaffe4a48ccbb0a4ef92267e96f7251a63ed0ed436514 - -In = 7E15D2B9EA74CA60F66C8DFAB377D9198B7B16DEB6A1BA0EA3C7EE2042F89D3786E779CF053C77785AA9E692F821F14A7F51 -Out = df0243a92e210853cc1c5af2419db141ce4a5adaa991dfdcae5f02a0 - -In = 9A219BE43713BD578015E9FDA66C0F2D83CAC563B776AB9F38F3E4F7EF229CB443304FBA401EFB2BDBD7ECE939102298651C86 -Out = 5399dba7eac60b19c5e5917bbc85d1d642dcd275e12a429ddf4c4d2b - -In = C8F2B693BD0D75EF99CAEBDC22ADF4088A95A3542F637203E283BBC3268780E787D68D28CC3897452F6A22AA8573CCEBF245972A -Out = aea9933eb9ff3decf76508fc6d29e7d6fc1a56edeb71ccf8a3ba3cbf - -In = EC0F99711016C6A2A07AD80D16427506CE6F441059FD269442BAAA28C6CA037B22EEAC49D5D894C0BF66219F2C08E9D0E8AB21DE52 -Out = 58cee899e8d4ebc55bdffd729117f16f3cb871bcaa830a0299d78c81 - -In = 0DC45181337CA32A8222FE7A3BF42FC9F89744259CFF653504D6051FE84B1A7FFD20CB47D4696CE212A686BB9BE9A8AB1C697B6D6A33 -Out = 6024a8913ad88f475be1e5f4c691e593636162e4198bb81e4757b33e - -In = DE286BA4206E8B005714F80FB1CDFAEBDE91D29F84603E4A3EBC04686F99A46C9E880B96C574825582E8812A26E5A857FFC6579F63742F -Out = d9ccfd4ef9aca3c8a7b85473baa78bdc28d88a7691e30a33f2ac5e40 - -In = EEBCC18057252CBF3F9C070F1A73213356D5D4BC19AC2A411EC8CDEEE7A571E2E20EAF61FD0C33A0FFEB297DDB77A97F0A415347DB66BCAF -Out = 29977abf2112409e9d1aa141712f006f9d48b84883e10a6a0b95bce0 - -In = 416B5CDC9FE951BD361BD7ABFC120A5054758EBA88FDD68FD84E39D3B09AC25497D36B43CBE7B85A6A3CEBDA8DB4E5549C3EE51BB6FCB6AC1E -Out = 10c6feb7aa83b1ffebfbf7b53a5b39cde5d3b192a488e19ab0654cab - -In = 5C5FAF66F32E0F8311C32E8DA8284A4ED60891A5A7E50FB2956B3CBAA79FC66CA376460E100415401FC2B8518C64502F187EA14BFC9503759705 -Out = dde5042150fbbf1c5ce5972e3b98c01b41d5b896488680b114e4817d - -In = 7167E1E02BE1A7CA69D788666F823AE4EEF39271F3C26A5CF7CEE05BCA83161066DC2E217B330DF821103799DF6D74810EED363ADC4AB99F36046A -Out = 055bd81a0adcb8512263ff6702f272134f3e6542eb122439847f7ef1 - -In = 2FDA311DBBA27321C5329510FAE6948F03210B76D43E7448D1689A063877B6D14C4F6D0EAA96C150051371F7DD8A4119F7DA5C483CC3E6723C01FB7D -Out = 996d28d06a9b43cfa9ac004c312f9e7145ba3504b801954b1510d158 - -In = 95D1474A5AAB5D2422ACA6E481187833A6212BD2D0F91451A67DD786DFC91DFED51B35F47E1DEB8A8AB4B9CB67B70179CC26F553AE7B569969CE151B8D -Out = 009b27c2db55ed146bf15dcdbe807ac7d4bed610f39eb3521e6f9644 - -In = C71BD7941F41DF044A2927A8FF55B4B467C33D089F0988AA253D294ADDBDB32530C0D4208B10D9959823F0C0F0734684006DF79F7099870F6BF53211A88D -Out = fd97f126b8704c6e6224e7191c5ea2e05398054109a02a8885be7a41 - -In = F57C64006D9EA761892E145C99DF1B24640883DA79D9ED5262859DCDA8C3C32E05B03D984F1AB4A230242AB6B78D368DC5AAA1E6D3498D53371E84B0C1D4BA -Out = bbf756f38f7a7433230a3a3c89f757ca34ec1ca11996983c07c785c0 - -In = E926AE8B0AF6E53176DBFFCC2A6B88C6BD765F939D3D178A9BDE9EF3AA131C61E31C1E42CDFAF4B4DCDE579A37E150EFBEF5555B4C1CB40439D835A724E2FAE7 -Out = d810a1c32442a954386d3c15478c154779c2bd5dcaa8dacb0ee1e471 - -In = 16E8B3D8F988E9BB04DE9C96F2627811C973CE4A5296B4772CA3EEFEB80A652BDF21F50DF79F32DB23F9F73D393B2D57D9A0297F7A2F2E79CFDA39FA393DF1AC00 -Out = df9454a06490db91a1c8ffdb246c8f3971c4c6e7d205dd19e83fda22 - -In = FC424EEB27C18A11C01F39C555D8B78A805B88DBA1DC2A42ED5E2C0EC737FF68B2456D80EB85E11714FA3F8EABFB906D3C17964CB4F5E76B29C1765DB03D91BE37FC -Out = 9e02d86135e194325245807e28227cf387f88917209866371929f6db - -In = ABE3472B54E72734BDBA7D9158736464251C4F21B33FBBC92D7FAC9A35C4E3322FF01D2380CBAA4EF8FB07D21A2128B7B9F5B6D9F34E13F39C7FFC2E72E47888599BA5 -Out = 33c470201c40d4b5be4a3bc55d479c1073e337a4caa78e5fcdda44ff - -In = 36F9F0A65F2CA498D739B944D6EFF3DA5EBBA57E7D9C41598A2B0E4380F3CF4B479EC2348D015FFE6256273511154AFCF3B4B4BF09D6C4744FDD0F62D75079D440706B05 -Out = 18dede59aa676d1c8ed2942d93c3c4d3bfaab34d7ff64138adb10392 - -In = ABC87763CAE1CA98BD8C5B82CABA54AC83286F87E9610128AE4DE68AC95DF5E329C360717BD349F26B872528492CA7C94C2C1E1EF56B74DBB65C2AC351981FDB31D06C77A4 -Out = f201b78a8c46168d8df97bcce91feb0505ea74bdaabeb930a4255b48 - -In = 94F7CA8E1A54234C6D53CC734BB3D3150C8BA8C5F880EAB8D25FED13793A9701EBE320509286FD8E422E931D99C98DA4DF7E70AE447BAB8CFFD92382D8A77760A259FC4FBD72 -Out = 7e641b66490231e94fb06a030d13ecc069c938a66a8b500f6281eb33 - -In = 13BD2811F6ED2B6F04FF3895ACEED7BEF8DCD45EB121791BC194A0F806206BFFC3B9281C2B308B1A729CE008119DD3066E9378ACDCC50A98A82E20738800B6CDDBE5FE9694AD6D -Out = 762dd5bf53a54b62374a3a953e0b2f8f5c72610613d583468d7a7602 - -In = 1EED9CBA179A009EC2EC5508773DD305477CA117E6D569E66B5F64C6BC64801CE25A8424CE4A26D575B8A6FB10EAD3FD1992EDDDEEC2EBE7150DC98F63ADC3237EF57B91397AA8A7 -Out = b3f1cf829ec165c09f8b8cc15070582ff54c1d2618d467de93b49811 - -In = BA5B67B5EC3A3FFAE2C19DD8176A2EF75C0CD903725D45C9CB7009A900C0B0CA7A2967A95AE68269A6DBF8466C7B6844A1D608AC661F7EFF00538E323DB5F2C644B78B2D48DE1A08AA -Out = 0e4444ad5694fffda4ec7261ac079dffadcbb277f71bbfda9fc253a0 - -In = 0EFA26AC5673167DCACAB860932ED612F65FF49B80FA9AE65465E5542CB62075DF1C5AE54FBA4DB807BE25B070033EFA223BDD5B1D3C94C6E1909C02B620D4B1B3A6C9FED24D70749604 -Out = 5e1fd1bc075b4998ef9ef8077dfed9ec0fa5c6c3ec35cfedd8444365 - -In = BBFD933D1FD7BF594AC7F435277DC17D8D5A5B8E4D13D96D2F64E771ABBD51A5A8AEA741BECCBDDB177BCEA05243EBD003CFDEAE877CCA4DA94605B67691919D8B033F77D384CA01593C1B -Out = 1f6a8b599362afd0cbedcc4d96f7ad9c60c47db77152fba700e762af - -In = 90078999FD3C35B8AFBF4066CBDE335891365F0FC75C1286CDD88FA51FAB94F9B8DEF7C9AC582A5DBCD95817AFB7D1B48F63704E19C2BAA4DF347F48D4A6D603013C23F1E9611D595EBAC37C -Out = b785eefbff713b35bf5a4b8d021f34cb69b9a8ed284725110a1c08cc - -In = 64105ECA863515C20E7CFBAA0A0B8809046164F374D691CDBD6508AAABC1819F9AC84B52BAFC1B0FE7CDDBC554B608C01C8904C669D8DB316A0953A4C68ECE324EC5A49FFDB59A1BD6A292AA0E -Out = 01a3a1c117b944f17eccfe26760c434e3bfaf12f9e26d8bf77bc6d7f - -In = D4654BE288B9F3B711C2D02015978A8CC57471D5680A092AA534F7372C71CEAAB725A383C4FCF4D8DEAA57FCA3CE056F312961ECCF9B86F14981BA5BED6AB5B4498E1F6C82C6CAE6FC14845B3C8A -Out = 7eeab67b9cfb95697804771df14f1b20900e0fd3f7785b2b31bfddb2 - -In = 12D9394888305AC96E65F2BF0E1B18C29C90FE9D714DD59F651F52B88B3008C588435548066EA2FC4C101118C91F32556224A540DE6EFDDBCA296EF1FB00341F5B01FECFC146BDB251B3BDAD556CD2 -Out = 8b2605ef61de11387022ae193f45fc56b38a5dc9a430c2a2e0d151f9 - -In = 871A0D7A5F36C3DA1DFCE57ACD8AB8487C274FAD336BC137EBD6FF4658B547C1DCFAB65F037AA58F35EF16AFF4ABE77BA61F65826F7BE681B5B6D5A1EA8085E2AE9CD5CF0991878A311B549A6D6AF230 -Out = 0a65959672a176828d5fc820187b1a60ec7ec5ba8898a6ba58bd1a18 - -In = E90B4FFEF4D457BC7711FF4AA72231CA25AF6B2E206F8BF859D8758B89A7CD36105DB2538D06DA83BAD5F663BA11A5F6F61F236FD5F8D53C5E89F183A3CEC615B50C7C681E773D109FF7491B5CC22296C5 -Out = 15236b06eb92ff99f8349af48804118f6ac1d6c9ddb60ddf6fd9b42e - -In = E728DE62D75856500C4C77A428612CD804F30C3F10D36FB219C5CA0AA30726AB190E5F3F279E0733D77E7267C17BE27D21650A9A4D1E32F649627638DBADA9702C7CA303269ED14014B2F3CF8B894EAC8554 -Out = ddcb16043af13661205fd35dcf248ca7d92f6aa04693688c0e7d9908 - -In = 6348F229E7B1DF3B770C77544E5166E081850FA1C6C88169DB74C76E42EB983FACB276AD6A0D1FA7B50D3E3B6FCD799EC97470920A7ABED47D288FF883E24CA21C7F8016B93BB9B9E078BDB9703D2B781B616E -Out = 0d02ba5c73dbbf28dcbbec5fe2a9ee90f1f2aab6daae10b7f89bdd02 - -In = 4B127FDE5DE733A1680C2790363627E63AC8A3F1B4707D982CAEA258655D9BF18F89AFE54127482BA01E08845594B671306A025C9A5C5B6F93B0A39522DC877437BE5C2436CBF300CE7AB6747934FCFC30AEAAF6 -Out = 7dcd63efd7a9eff1dedc1a420eae7d5c52d633ad17683417c85a507e - -In = 08461F006CFF4CC64B752C957287E5A0FAABC05C9BFF89D23FD902D324C79903B48FCB8F8F4B01F3E4DDB483593D25F000386698F5ADE7FAADE9615FDC50D32785EA51D49894E45BAA3DC707E224688C6408B68B11 -Out = 9c57386eaa0d94f4287eb4cd59b85f80c2b55b598a16a909b345241a - -In = 68C8F8849B120E6E0C9969A5866AF591A829B92F33CD9A4A3196957A148C49138E1E2F5C7619A6D5EDEBE995ACD81EC8BB9C7B9CFCA678D081EA9E25A75D39DB04E18D475920CE828B94E72241F24DB72546B352A0E4 -Out = a294502fd797dab011b1063af73a50cbc8c5d7302fdc0b30a7b58a05 - -In = B8D56472954E31FB54E28FCA743F84D8DC34891CB564C64B08F7B71636DEBD64CA1EDBDBA7FC5C3E40049CE982BBA8C7E0703034E331384695E9DE76B5104F2FBC4535ECBEEBC33BC27F29F18F6F27E8023B0FBB6F563C -Out = 1ec342a2b721764c04754182841543614e2066e1f4420123b74d5398 - -In = 0D58AC665FA84342E60CEFEE31B1A4EACDB092F122DFC68309077AED1F3E528F578859EE9E4CEFB4A728E946324927B675CD4F4AC84F64DB3DACFE850C1DD18744C74CECCD9FE4DC214085108F404EAB6D8F452B5442A47D -Out = 61e0b41492c37fefecb767f14bc0b50f5d81e0a09f41f59beb11de10 - -In = 1755E2D2E5D1C1B0156456B539753FF416651D44698E87002DCF61DCFA2B4E72F264D9AD591DF1FDEE7B41B2EB00283C5AEBB3411323B672EAA145C5125185104F20F335804B02325B6DEA65603F349F4D5D8B782DD3469CCD -Out = af6e05feaa32d06392e5197d41cf8b86c68caca794e8332f3a066151 - -In = B180DE1A611111EE7584BA2C4B020598CD574AC77E404E853D15A101C6F5A2E5C801D7D85DC95286A1804C870BB9F00FD4DCB03AA8328275158819DCAD7253F3E3D237AEAA7979268A5DB1C6CE08A9EC7C2579783C8AFC1F91A7 -Out = d11777b8fe2c62025db282c7ab894e6185dd7a859af970812f66d36c - -In = CF3583CBDFD4CBC17063B1E7D90B02F0E6E2EE05F99D77E24E560392535E47E05077157F96813544A17046914F9EFB64762A23CF7A49FE52A0A4C01C630CFE8727B81FB99A89FF7CC11DCA5173057E0417B8FE7A9EFBA6D95C555F -Out = 5b6a196d9a4cb8e1d47eff4f9e8a4bf72693b400b272141514e85937 - -In = 072FC02340EF99115BAD72F92C01E4C093B9599F6CFC45CB380EE686CB5EB019E806AB9BD55E634AB10AA62A9510CC0672CD3EDDB589C7DF2B67FCD3329F61B1A4441ECA87A33C8F55DA4FBBAD5CF2B2527B8E983BB31A2FADEC7523 -Out = 47c7fdd3920c0315ad285b9b73b8e15553f826780e3741a35d6d44f5 - -In = 76EECF956A52649F877528146DE33DF249CD800E21830F65E90F0F25CA9D6540FDE40603230ECA6760F1139C7F268DEBA2060631EEA92B1FFF05F93FD5572FBE29579ECD48BC3A8D6C2EB4A6B26E38D6C5FBF2C08044AEEA470A8F2F26 -Out = 5b2887b88f4cc8c94d617f4a779de0c6718d2e65ef8da96518640c76 - -In = 7ADC0B6693E61C269F278E6944A5A2D8300981E40022F839AC644387BFAC9086650085C2CDC585FEA47B9D2E52D65A2B29A7DC370401EF5D60DD0D21F9E2B90FAE919319B14B8C5565B0423CEFB827D5F1203302A9D01523498A4DB10374 -Out = 322807a8cf1b52945e0f943f452d6f97cda5ae76e724228dc95806a0 - -In = E1FFFA9826CCE8B86BCCEFB8794E48C46CDF372013F782ECED1E378269B7BE2B7BF51374092261AE120E822BE685F2E7A83664BCFBE38FE8633F24E633FFE1988E1BC5ACF59A587079A57A910BDA60060E85B5F5B6F776F0529639D9CCE4BD -Out = a0c872265d1737a58a004f9176552177f78a8548e15137bdbe6526f7 - -In = 69F9ABBA65592EE01DB4DCE52DBAB90B08FC04193602792EE4DAA263033D59081587B09BBE49D0B49C9825D22840B2FF5D9C5155F975F8F2C2E7A90C75D2E4A8040FE39F63BBAFB403D9E28CC3B86E04E394A9C9E8065BD3C85FA9F0C7891600 -Out = b8c057ce96753f987372cf5f4c912814b2a96b8d99a7cd390e24601a - -In = 38A10A352CA5AEDFA8E19C64787D8E9C3A75DBF3B8674BFAB29B5DBFC15A63D10FAE66CD1A6E6D2452D557967EAAD89A4C98449787B0B3164CA5B717A93F24EB0B506CEB70CBBCB8D72B2A72993F909AAD92F044E0B5A2C9AC9CB16A0CA2F81F49 -Out = a48a671d6bb5c1606350281b8a5da8bd4f095f1af9c0fd5570176e29 - -In = 6D8C6E449BC13634F115749C248C17CD148B72157A2C37BF8969EA83B4D6BA8C0EE2711C28EE11495F43049596520CE436004B026B6C1F7292B9C436B055CBB72D530D860D1276A1502A5140E3C3F54A93663E4D20EDEC32D284E25564F624955B52 -Out = aad0bb1a3dc786b16a7a4d824228ad1e8d217ee91e2dac74d24e9c3b - -In = 6EFCBCAF451C129DBE00B9CEF0C3749D3EE9D41C7BD500ADE40CDC65DEDBBBADB885A5B14B32A0C0D087825201E303288A733842FA7E599C0C514E078F05C821C7A4498B01C40032E9F1872A1C925FA17CE253E8935E4C3C71282242CB716B2089CCC1 -Out = dff80d2ae82c0881e9633b22d53bc031be939e576af2d686551a2756 - -In = 433C5303131624C0021D868A30825475E8D0BD3052A022180398F4CA4423B98214B6BEAAC21C8807A2C33F8C93BD42B092CC1B06CEDF3224D5ED1EC29784444F22E08A55AA58542B524B02CD3D5D5F6907AFE71C5D7462224A3F9D9E53E7E0846DCBB4CE -Out = 89d97c0cccba10e1e0cfd801ad93bb1274a29cd1ab0266689506ac3d - -In = A873E0C67CA639026B6683008F7AA6324D4979550E9BCE064CA1E1FB97A30B147A24F3F666C0A72D71348EDE701CF2D17E2253C34D1EC3B647DBCEF2F879F4EB881C4830B791378C901EB725EA5C172316C6D606E0AF7DF4DF7F76E490CD30B2BADF45685F -Out = 199ba9673347f8c61605d9cbb611885364f6f8f108f80523e1f729f4 - -In = 006917B64F9DCDF1D2D87C8A6173B64F6587168E80FAA80F82D84F60301E561E312D9FBCE62F39A6FB476E01E925F26BCC91DE621449BE6504C504830AAE394096C8FC7694651051365D4EE9070101EC9B68086F2EA8F8AB7B811EA8AD934D5C9B62C60A4771 -Out = f6936912a7d330a28eeda8d43f9b14bdde4b0ce5d2b971d7abf9a1af - -In = F13C972C52CB3CC4A4DF28C97F2DF11CE089B815466BE88863243EB318C2ADB1A417CB1041308598541720197B9B1CB5BA2318BD5574D1DF2174AF14884149BA9B2F446D609DF240CE335599957B8EC80876D9A085AE084907BC5961B20BF5F6CA58D5DAB38ADB -Out = 6575454f9456efb484e9860d59068ce986cdbc5220267ac4cfd43305 - -In = E35780EB9799AD4C77535D4DDB683CF33EF367715327CF4C4A58ED9CBDCDD486F669F80189D549A9364FA82A51A52654EC721BB3AAB95DCEB4A86A6AFA93826DB923517E928F33E3FBA850D45660EF83B9876ACCAFA2A9987A254B137C6E140A21691E1069413848 -Out = 6388fb19fe381055d52ea3858e67ffce44bddca1ed2b13eb7ec2110f - -In = 64EC021C9585E01FFE6D31BB50D44C79B6993D72678163DB474947A053674619D158016ADB243F5C8D50AA92F50AB36E579FF2DABB780A2B529370DAA299207CFBCDD3A9A25006D19C4F1FE33E4B1EAEC315D8C6EE1E730623FD1941875B924EB57D6D0C2EDC4E78D6 -Out = 9729e34e75fa3992a71cecf277b6af6dac4bebae6b7ab0deafb81cbe - -In = 5954BAB512CF327D66B5D9F296180080402624AD7628506B555EEA8382562324CF452FBA4A2130DE3E165D11831A270D9CB97CE8C2D32A96F50D71600BB4CA268CF98E90D6496B0A6619A5A8C63DB6D8A0634DFC6C7EC8EA9C006B6C456F1B20CD19E781AF20454AC880 -Out = 4c93486fdec96ca5fcb9e758b3c9481407be5d9289998e8be5d4c5ac - -In = 03D9F92B2C565709A568724A0AFF90F8F347F43B02338F94A03ED32E6F33666FF5802DA4C81BDCE0D0E86C04AFD4EDC2FC8B4141C2975B6F07639B1994C973D9A9AFCE3D9D365862003498513BFA166D2629E314D97441667B007414E739D7FEBF0FE3C32C17AA188A8683 -Out = 6d370ae522db82091d7b50edf0b9da75cdd80c3860dad31d3a3cca8c - -In = F31E8B4F9E0621D531D22A380BE5D9ABD56FAEC53CBD39B1FAB230EA67184440E5B1D15457BD25F56204FA917FA48E669016CB48C1FFC1E1E45274B3B47379E00A43843CF8601A5551411EC12503E5AAC43D8676A1B2297EC7A0800DBFEE04292E937F21C005F17411473041 -Out = 89699ed6306d2198c1d6b2af6f20eaca549e1074ad2c360b38189a93 - -In = 758EA3FEA738973DB0B8BE7E599BBEF4519373D6E6DCD7195EA885FC991D896762992759C2A09002912FB08E0CB5B76F49162AEB8CF87B172CF3AD190253DF612F77B1F0C532E3B5FC99C2D31F8F65011695A087A35EE4EEE5E334C369D8EE5D29F695815D866DA99DF3F79403 -Out = fa4beb5faeea2ce35a19c31830642ae0004290350a3d08c98f92742a - -In = 47C6E0C2B74948465921868804F0F7BD50DD323583DC784F998A93CD1CA4C6EF84D41DC81C2C40F34B5BEE6A93867B3BDBA0052C5F59E6F3657918C382E771D33109122CC8BB0E1E53C4E3D13B43CE44970F5E0C079D2AD7D7A3549CD75760C21BB15B447589E86E8D76B1E9CED2 -Out = 37f557f4774c62ad409878dde6bed9cbb8fe71d655ad16dcbc3e0b18 - -In = F690A132AB46B28EDFA6479283D6444E371C6459108AFD9C35DBD235E0B6B6FF4C4EA58E7554BD002460433B2164CA51E868F7947D7D7A0D792E4ABF0BE5F450853CC40D85485B2B8857EA31B5EA6E4CCFA2F3A7EF3380066D7D8979FDAC618AAD3D7E886DEA4F005AE4AD05E5065F -Out = e0100987bf06977be4b249f3dc91ca3579a1b1af26589e8cced72f4a - -In = 58D6A99BC6458824B256916770A8417040721CCCFD4B79EACD8B65A3767CE5BA7E74104C985AC56B8CC9AEBD16FEBD4CDA5ADB130B0FF2329CC8D611EB14DAC268A2F9E633C99DE33997FEA41C52A7C5E1317D5B5DAED35EBA7D5A60E45D1FA7EAABC35F5C2B0A0F2379231953322C4E -Out = cd0ae7d09aa39e3ee86b609940a174a6bb569de2ed02afc567a39982 - -In = BEFAB574396D7F8B6705E2D5B58B2C1C820BB24E3F4BAE3E8FBCD36DBF734EE14E5D6AB972AEDD3540235466E825850EE4C512EA9795ABFD33F330D9FD7F79E62BBB63A6EA85DE15BEAEEA6F8D204A28956059E2632D11861DFB0E65BC07AC8A159388D5C3277E227286F65FF5E5B5AEC1 -Out = 3d6c866ebaa149e0c6ad8ba5e9a685e1ad56d81a00fb99d9020f11c0 - -In = 8E58144FA9179D686478622CE450C748260C95D1BA43B8F9B59ABECA8D93488DA73463EF40198B4D16FB0B0707201347E0506FF19D01BEA0F42B8AF9E71A1F1BD168781069D4D338FDEF00BF419FBB003031DF671F4A37979564F69282DE9C65407847DD0DA505AB1641C02DEA4F0D834986 -Out = ae8a3fec270593fd694f24d7dff0e58b35a6117197a912f02f37bd07 - -In = B55C10EAE0EC684C16D13463F29291BF26C82E2FA0422A99C71DB4AF14DD9C7F33EDA52FD73D017CC0F2DBE734D831F0D820D06D5F89DACC485739144F8CFD4799223B1AFF9031A105CB6A029BA71E6E5867D85A554991C38DF3C9EF8C1E1E9A7630BE61CAABCA69280C399C1FB7A12D12AEFC -Out = e1234ff6c2cf27eaf4f1b61d65af1ee5e999a00273df6b4ed9ee0209 - -In = 2EEEA693F585F4ED6F6F8865BBAE47A6908AECD7C429E4BEC4F0DE1D0CA0183FA201A0CB14A529B7D7AC0E6FF6607A3243EE9FB11BCF3E2304FE75FFCDDD6C5C2E2A4CD45F63C962D010645058D36571404A6D2B4F44755434D76998E83409C3205AA1615DB44057DB991231D2CB42624574F545 -Out = f732477086288d447e6be84cff07848f976c6fb331cf0c41fc5a38e8 - -In = DAB11DC0B047DB0420A585F56C42D93175562852428499F66A0DB811FCDDDAB2F7CDFFED1543E5FB72110B64686BC7B6887A538AD44C050F1E42631BC4EC8A9F2A047163D822A38989EE4AAB01B4C1F161B062D873B1CFA388FD301514F62224157B9BEF423C7783B7AAC8D30D65CD1BBA8D689C2D -Out = 685e022a2a4bc57eee9d451a5c033a81ebcf4375d7f82f992a03cda7 - -In = 42E99A2F80AEE0E001279A2434F731E01D34A44B1A8101726921C0590C30F3120EB83059F325E894A5AC959DCA71CE2214799916424E859D27D789437B9D27240BF8C35ADBAFCECC322B48AA205B293962D858652ABACBD588BCF6CBC388D0993BD622F96ED54614C25B6A9AA527589EAAFFCF17DDF7 -Out = a8e50d0549972db96dede853569c0a4453ebb50ac754abc596412918 - -In = 3C9B46450C0F2CAE8E3823F8BDB4277F31B744CE2EB17054BDDC6DFF36AF7F49FB8A2320CC3BDF8E0A2EA29AD3A55DE1165D219ADEDDB5175253E2D1489E9B6FDD02E2C3D3A4B54D60E3A47334C37913C5695378A669E9B72DEC32AF5434F93F46176EBF044C4784467C700470D0C0B40C8A088C815816 -Out = c5804ec4ede0701857d69ca283aa8d96403ae92595a269b800217007 - -In = D1E654B77CB155F5C77971A64DF9E5D34C26A3CAD6C7F6B300D39DEB1910094691ADAA095BE4BA5D86690A976428635D5526F3E946F7DC3BD4DBC78999E653441187A81F9ADCD5A3C5F254BC8256B0158F54673DCC1232F6E918EBFC6C51CE67EAEB042D9F57EEC4BFE910E169AF78B3DE48D137DF4F2840 -Out = 5a99bfb07143bc45f141220e3a5d3e712ad5379d19f07ec9968ab2a5 - -In = 626F68C18A69A6590159A9C46BE03D5965698F2DAC3DE779B878B3D9C421E0F21B955A16C715C1EC1E22CE3EB645B8B4F263F60660EA3028981EEBD6C8C3A367285B691C8EE56944A7CD1217997E1D9C21620B536BDBD5DE8925FF71DEC6FBC06624AB6B21E329813DE90D1E572DFB89A18120C3F606355D25 -Out = 0e59962e136392445608b297e9b1bca21fdbafa379c9a87f3d5ca2a9 - -In = 651A6FB3C4B80C7C68C6011675E6094EB56ABF5FC3057324EBC6477825061F9F27E7A94633ABD1FA598A746E4A577CAF524C52EC1788471F92B8C37F23795CA19D559D446CAB16CBCDCE90B79FA1026CEE77BF4AB1B503C5B94C2256AD75B3EAC6FD5DCB96ACA4B03A834BFB4E9AF988CECBF2AE597CB9097940 -Out = f130a956ca3e557e2d446355abcd0ce0e8c594ea669bc0f177c9d4af - -In = 8AAF072FCE8A2D96BC10B3C91C809EE93072FB205CA7F10ABD82ECD82CF040B1BC49EA13D1857815C0E99781DE3ADBB5443CE1C897E55188CEAF221AA9681638DE05AE1B322938F46BCE51543B57ECDB4C266272259D1798DE13BE90E10EFEC2D07484D9B21A3870E2AA9E06C21AA2D0C9CF420080A80A91DEE16F -Out = 73959160f546098eed7e8c45b997f10a09061245033e814ca5aeeb5e - -In = 53F918FD00B1701BD504F8CDEA803ACCA21AC18C564AB90C2A17DA592C7D69688F6580575395551E8CD33E0FEF08CA6ED4588D4D140B3E44C032355DF1C531564D7F4835753344345A6781E11CD5E095B73DF5F82C8AE3AD00877936896671E947CC52E2B29DCD463D90A0C9929128DA222B5A211450BBC0E02448E2 -Out = 481fedfae1d80a9f609b9baea3ef1987f8cd8e97bd6fb9b838659a1a - -In = A64599B8A61B5CCEC9E67AED69447459C8DA3D1EC6C7C7C82A7428B9B584FA67E90F68E2C00FBBED4613666E5168DA4A16F395F7A3C3832B3B134BFC9CBAA95D2A0FE252F44AC6681EB6D40AB91C1D0282FED6701C57463D3C5F2BB8C6A7301FB4576AA3B5F15510DB8956FF77478C26A7C09BEA7B398CFC83503F538E -Out = 7eb5d5050caebdc2d1c875cb07d6d2518de07931473e3ed5df2393f1 - -In = 0E3AB0E054739B00CDB6A87BD12CAE024B54CB5E550E6C425360C2E87E59401F5EC24EF0314855F0F56C47695D56A7FB1417693AF2A1ED5291F2FEE95F75EED54A1B1C2E81226FBFF6F63ADE584911C71967A8EB70933BC3F5D15BC91B5C2644D9516D3C3A8C154EE48E118BD1442C043C7A0DBA5AC5B1D5360AAE5B9065 -Out = add9b57e76f144334625fb6a7b9eb06c2ab32921eb9068201e9c947c - -In = A62FC595B4096E6336E53FCDFC8D1CC175D71DAC9D750A6133D23199EAAC288207944CEA6B16D27631915B4619F743DA2E30A0C00BBDB1BBB35AB852EF3B9AEC6B0A8DCC6E9E1ABAA3AD62AC0A6C5DE765DE2C3711B769E3FDE44A74016FFF82AC46FA8F1797D3B2A726B696E3DEA5530439ACEE3A45C2A51BC32DD055650B -Out = 6054990815003279c9b4575c71496fc3a8e298f3081718550b2d79ef - -In = 2B6DB7CED8665EBE9DEB080295218426BDAA7C6DA9ADD2088932CDFFBAA1C14129BCCDD70F369EFB149285858D2B1D155D14DE2FDB680A8B027284055182A0CAE275234CC9C92863C1B4AB66F304CF0621CD54565F5BFF461D3B461BD40DF28198E3732501B4860EADD503D26D6E69338F4E0456E9E9BAF3D827AE685FB1D817 -Out = 84f92e76c0de6c7928bbd75c84c47b353549627eae09081cc232024d - -In = 10DB509B2CDCABA6C062AE33BE48116A29EB18E390E1BBADA5CA0A2718AFBCD23431440106594893043CC7F2625281BF7DE2655880966A23705F0C5155C2F5CCA9F2C2142E96D0A2E763B70686CD421B5DB812DACED0C6D65035FDE558E94F26B3E6DDE5BD13980CC80292B723013BD033284584BFF27657871B0CF07A849F4AE2 -Out = 1cd418dbdf948fba46a74524312df9132412071765243dfefda9cfa8 - -In = 9334DE60C997BDA6086101A6314F64E4458F5FF9450C509DF006E8C547983C651CA97879175AABA0C539E82D05C1E02C480975CBB30118121061B1EBAC4F8D9A3781E2DB6B18042E01ECF9017A64A0E57447EC7FCBE6A7F82585F7403EE2223D52D37B4BF426428613D6B4257980972A0ACAB508A7620C1CB28EB4E9D30FC41361EC -Out = 353f2f4c635f43a5ab24020317dd0fc3b87dfb7489307342c959eb9d - -In = E88AB086891693AA535CEB20E64C7AB97C7DD3548F3786339897A5F0C39031549CA870166E477743CCFBE016B4428D89738E426F5FFE81626137F17AECFF61B72DBEE2DC20961880CFE281DFAB5EE38B1921881450E16032DE5E4D55AD8D4FCA609721B0692BAC79BE5A06E177FE8C80C0C83519FB3347DE9F43D5561CB8107B9B5EDC -Out = ce84931fb96a0b10a96a68bbf27c6aa3ee6f8032a4e21485050295d7 - -In = FD19E01A83EB6EC810B94582CB8FBFA2FCB992B53684FB748D2264F020D3B960CB1D6B8C348C2B54A9FCEA72330C2AAA9A24ECDB00C436ABC702361A82BB8828B85369B8C72ECE0082FE06557163899C2A0EFA466C33C04343A839417057399A63A3929BE1EE4805D6CE3E5D0D0967FE9004696A5663F4CAC9179006A2CEB75542D75D68 -Out = 5245eaf1a9bc404147b74d5c8e2ad990adc5c5c52ec8de312f71f7d1 - -In = 59AE20B6F7E0B3C7A989AFB28324A40FCA25D8651CF1F46AE383EF6D8441587AA1C04C3E3BF88E8131CE6145CFB8973D961E8432B202FA5AF3E09D625FAAD825BC19DA9B5C6C20D02ABDA2FCC58B5BD3FE507BF201263F30543819510C12BC23E2DDB4F711D087A86EDB1B355313363A2DE996B891025E147036087401CCF3CA7815BF3C49 -Out = b746f02fffc759ee82557ee41ed2361113695a8eefcae3ef806dbc7f - -In = 77EE804B9F3295AB2362798B72B0A1B2D3291DCEB8139896355830F34B3B328561531F8079B79A6E9980705150866402FDC176C05897E359A6CB1A7AB067383EB497182A7E5AEF7038E4C96D133B2782917417E391535B5E1B51F47D8ED7E4D4025FE98DC87B9C1622614BFF3D1029E68E372DE719803857CA52067CDDAAD958951CB2068CC6 -Out = 03b80280e6ac994431cf47d74c5ca44344ecbb13311c4ba77ef86fe3 - -In = B771D5CEF5D1A41A93D15643D7181D2A2EF0A8E84D91812F20ED21F147BEF732BF3A60EF4067C3734B85BC8CD471780F10DC9E8291B58339A677B960218F71E793F2797AEA349406512829065D37BB55EA796FA4F56FD8896B49B2CD19B43215AD967C712B24E5032D065232E02C127409D2ED4146B9D75D763D52DB98D949D3B0FED6A8052FBB -Out = c8e2ef36db40ad08cdeed1d0d1ff5ae27384f2033a08d8e87a089032 - -In = B32D95B0B9AAD2A8816DE6D06D1F86008505BD8C14124F6E9A163B5A2ADE55F835D0EC3880EF50700D3B25E42CC0AF050CCD1BE5E555B23087E04D7BF9813622780C7313A1954F8740B6EE2D3F71F768DD417F520482BD3A08D4F222B4EE9DBD015447B33507DD50F3AB4247C5DE9A8ABD62A8DECEA01E3B87C8B927F5B08BEB37674C6F8E380C04 -Out = f90c3ffaa84dfb0edb08621a7eb29048888971f98caba02e86286b36 - -In = 04410E31082A47584B406F051398A6ABE74E4DA59BB6F85E6B49E8A1F7F2CA00DFBA5462C2CD2BFDE8B64FB21D70C083F11318B56A52D03B81CAC5EEC29EB31BD0078B6156786DA3D6D8C33098C5C47BB67AC64DB14165AF65B44544D806DDE5F487D5373C7F9792C299E9686B7E5821E7C8E2458315B996B5677D926DAC57B3F22DA873C601016A0D -Out = 2a0739e583c1729003665fde843a4dd527a0ba67c95f52239d429e71 - -In = 8B81E9BADDE026F14D95C019977024C9E13DB7A5CD21F9E9FC491D716164BBACDC7060D882615D411438AEA056C340CDF977788F6E17D118DE55026855F93270472D1FD18B9E7E812BAE107E0DFDE7063301B71F6CFE4E225CAB3B232905A56E994F08EE2891BA922D49C3DAFEB75F7C69750CB67D822C96176C46BD8A29F1701373FB09A1A6E3C7158F -Out = 5e6b416891ea14355f3df18944a9264e8373adad06917b61c7eeb8f1 - -In = FA6EED24DA6666A22208146B19A532C2EC9BA94F09F1DEF1E7FC13C399A48E41ACC2A589D099276296348F396253B57CB0E40291BD282773656B6E0D8BEA1CDA084A3738816A840485FCF3FB307F777FA5FEAC48695C2AF4769720258C77943FB4556C362D9CBA8BF103AEB9034BAA8EA8BFB9C4F8E6742CE0D52C49EA8E974F339612E830E9E7A9C29065 -Out = a4b4e5738a17f8bd44b7dc01d028b41c33662a6aa6dc8efa7c2c2187 - -In = 9BB4AF1B4F09C071CE3CAFA92E4EB73CE8A6F5D82A85733440368DEE4EB1CBC7B55AC150773B6FE47DBE036C45582ED67E23F4C74585DAB509DF1B83610564545642B2B1EC463E18048FC23477C6B2AA035594ECD33791AF6AF4CBC2A1166ABA8D628C57E707F0B0E8707CAF91CD44BDB915E0296E0190D56D33D8DDE10B5B60377838973C1D943C22ED335E -Out = 637e5d1e2f936e5e4ca009b876df0e41f906f6670c06543fc49dc7bf - -In = 2167F02118CC62043E9091A647CADBED95611A521FE0D64E8518F16C808AB297725598AE296880A773607A798F7C3CFCE80D251EBEC6885015F9ABF7EAABAE46798F82CB5926DE5C23F44A3F9F9534B3C6F405B5364C2F8A8BDC5CA49C749BED8CE4BA48897062AE8424CA6DDE5F55C0E42A95D1E292CA54FB46A84FBC9CD87F2D0C9E7448DE3043AE22FDD229 -Out = 2e58db86af9a1a61c11641d3520face4420574ccc4ae13b83c391ba2 - -In = 94B7FA0BC1C44E949B1D7617D31B4720CBE7CA57C6FA4F4094D4761567E389ECC64F6968E4064DF70DF836A47D0C713336B5028B35930D29EB7A7F9A5AF9AD5CF441745BAEC9BB014CEEFF5A41BA5C1CE085FEB980BAB9CF79F2158E03EF7E63E29C38D7816A84D4F71E0F548B7FC316085AE38A060FF9B8DEC36F91AD9EBC0A5B6C338CBB8F6659D342A24368CF -Out = e2f27e6cc4829659d8274eeb837945acae44ddf762d55f19bf3f0d52 - -In = EA40E83CB18B3A242C1ECC6CCD0B7853A439DAB2C569CFC6DC38A19F5C90ACBF76AEF9EA3742FF3B54EF7D36EB7CE4FF1C9AB3BC119CFF6BE93C03E208783335C0AB8137BE5B10CDC66FF3F89A1BDDC6A1EED74F504CBE7290690BB295A872B9E3FE2CEE9E6C67C41DB8EFD7D863CF10F840FE618E7936DA3DCA5CA6DF933F24F6954BA0801A1294CD8D7E66DFAFEC -Out = 8a96e3a5d124d0e3f737481d74c0947b8964cc86745fefcb0c43f682 - -In = 157D5B7E4507F66D9A267476D33831E7BB768D4D04CC3438DA12F9010263EA5FCAFBDE2579DB2F6B58F911D593D5F79FB05FE3596E3FA80FF2F761D1B0E57080055C118C53E53CDB63055261D7C9B2B39BD90ACC32520CBBDBDA2C4FD8856DBCEE173132A2679198DAF83007A9B5C51511AE49766C792A29520388444EBEFE28256FB33D4260439CBA73A9479EE00C63 -Out = 19f2f6d97bd6d25b8e9f01da837c422ee2b8ee0d58bb3a0ff856cfc4 - -In = 836B34B515476F613FE447A4E0C3F3B8F20910AC89A3977055C960D2D5D2B72BD8ACC715A9035321B86703A411DDE0466D58A59769672AA60AD587B8481DE4BBA552A1645779789501EC53D540B904821F32B0BD1855B04E4848F9F8CFE9EBD8911BE95781A759D7AD9724A7102DBE576776B7C632BC39B9B5E19057E226552A5994C1DBB3B5C7871A11F5537011044C53 -Out = 43903dceb49f9734d52a7cc7e272204db0d51b7e78dbe4e0603ad818 - -In = CC7784A4912A7AB5AD3620AAB29BA87077CD3CB83636ADC9F3DC94F51EDF521B2161EF108F21A0A298557981C0E53CE6CED45BDF782C1EF200D29BAB81DD6460586964EDAB7CEBDBBEC75FD7925060F7DA2B853B2B089588FA0F8C16EC6498B14C55DCEE335CB3A91D698E4D393AB8E8EAC0825F8ADEBEEE196DF41205C011674E53426CAA453F8DE1CBB57932B0B741D4C6 -Out = 4a16b6e76511f374130f806388549705819856baaf33c2db572d0796 - -In = 7639B461FFF270B2455AC1D1AFCE782944AEA5E9087EB4A39EB96BB5C3BAAF0E868C8526D3404F9405E79E77BFAC5FFB89BF1957B523E17D341D7323C302EA7083872DD5E8705694ACDDA36D5A1B895AAA16ECA6104C82688532C8BFE1790B5DC9F4EC5FE95BAED37E1D287BE710431F1E5E8EE105BC42ED37D74B1E55984BF1C09FE6A1FA13EF3B96FAEAED6A2A1950A12153 -Out = 1213c45e8802c5724f9b38fd6bc55bbe5166f5e8e750e18e97b13a07 - -In = EB6513FC61B30CFBA58D4D7E80F94D14589090CF1D80B1DF2E68088DC6104959BA0D583D585E9578AB0AEC0CF36C48435EB52ED9AB4BBCE7A5ABE679C97AE2DBE35E8CC1D45B06DDA3CF418665C57CBEE4BBB47FA4CAF78F4EE656FEC237FE4EEBBAFA206E1EF2BD0EE4AE71BD0E9B2F54F91DAADF1FEBFD7032381D636B733DCB3BF76FB14E23AFF1F68ED3DBCF75C9B99C6F26 -Out = 9662eec9857994ba2e9a65337d68657505dd96f7f34803496558cb9c - -In = 1594D74BF5DDE444265D4C04DAD9721FF3E34CBF622DAF341FE16B96431F6C4DF1F760D34F296EB97D98D560AD5286FEC4DCE1724F20B54FD7DF51D4BF137ADD656C80546FB1BF516D62EE82BAA992910EF4CC18B70F3F8698276FCFB44E0EC546C2C39CFD8EE91034FF9303058B4252462F86C823EB15BF481E6B79CC3A02218595B3658E8B37382BD5048EAED5FD02C37944E73B -Out = 95853c4c524afdd85b765835e4fa4dc691ed5f7ad9d7c103038ac5b2 - -In = 4CFA1278903026F66FEDD41374558BE1B585D03C5C55DAC94361DF286D4BD39C7CB8037ED3B267B07C346626449D0CC5B0DD2CF221F7E4C3449A4BE99985D2D5E67BFF2923357DDEAB5ABCB4619F3A3A57B2CF928A022EB27676C6CF805689004FCA4D41EA6C2D0A4789C7605F7BB838DD883B3AD3E6027E775BCF262881428099C7FFF95B14C095EA130E0B9938A5E22FC52650F591 -Out = d091c10ab17dc3b54a0fa183970956616c9e351e1979be912aa59062 - -In = D3E65CB92CFA79662F6AF493D696A07CCF32AAADCCEFF06E73E8D9F6F909209E66715D6E978788C49EFB9087B170ECF3AA86D2D4D1A065AE0EFC8924F365D676B3CB9E2BEC918FD96D0B43DEE83727C9A93BF56CA2B2E59ADBA85696546A815067FC7A78039629D4948D157E7B0D826D1BF8E81237BAB7321312FDAA4D521744F988DB6FDF04549D0FDCA393D639C729AF716E9C8BBA48 -Out = 64b0bb1d1943212afaeeaa95dafbc8649ddfa5e0a9d0c87a265914a4 - -In = 842CC583504539622D7F71E7E31863A2B885C56A0BA62DB4C2A3F2FD12E79660DC7205CA29A0DC0A87DB4DC62EE47A41DB36B9DDB3293B9AC4BAAE7DF5C6E7201E17F717AB56E12CAD476BE49608AD2D50309E7D48D2D8DE4FA58AC3CFEAFEEE48C0A9EEC88498E3EFC51F54D300D828DDDCCB9D0B06DD021A29CF5CB5B2506915BEB8A11998B8B886E0F9B7A80E97D91A7D01270F9A7717 -Out = 20a6a4ce9f4bb907eb4aee92b468588ae4ae28b62c86e5fcb864fe13 - -In = 6C4B0A0719573E57248661E98FEBE326571F9A1CA813D3638531AE28B4860F23C3A3A8AC1C250034A660E2D71E16D3ACC4BF9CE215C6F15B1C0FC7E77D3D27157E66DA9CEEC9258F8F2BF9E02B4AC93793DD6E29E307EDE3695A0DF63CBDC0FC66FB770813EB149CA2A916911BEE4902C47C7802E69E405FE3C04CEB5522792A5503FA829F707272226621F7C488A7698C0D69AA561BE9F378 -Out = 07a4a321d80ce8b51909216c29043e22a64801df275ae064ec41d96f - -In = 51B7DBB7CE2FFEB427A91CCFE5218FD40F9E0B7E24756D4C47CD55606008BDC27D16400933906FD9F30EFFDD4880022D081155342AF3FB6CD53672AB7FB5B3A3BCBE47BE1FD3A2278CAE8A5FD61C1433F7D350675DD21803746CADCA574130F01200024C6340AB0CC2CF74F2234669F34E9009EF2EB94823D62B31407F4BA46F1A1EEC41641E84D77727B59E746B8A671BEF936F05BE820759FA -Out = 3e30ef42379a09417f0b24d839e12cfbedb8b6ddb211953502fa5b2b - -In = 83599D93F5561E821BD01A472386BC2FF4EFBD4AED60D5821E84AAE74D8071029810F5E286F8F17651CD27DA07B1EB4382F754CD1C95268783AD09220F5502840370D494BEB17124220F6AFCE91EC8A0F55231F9652433E5CE3489B727716CF4AEBA7DCDA20CD29AA9A859201253F948DD94395ABA9E3852BD1D60DDA7AE5DC045B283DA006E1CBAD83CC13292A315DB5553305C628DD091146597 -Out = f00be8135126fff6a46c44248b9b792660c29fd076c53f4ed046b104 - -In = 2BE9BF526C9D5A75D565DD11EF63B979D068659C7F026C08BEA4AF161D85A462D80E45040E91F4165C074C43AC661380311A8CBED59CC8E4C4518E80CD2C78AB1CABF66BFF83EAB3A80148550307310950D034A6286C93A1ECE8929E6385C5E3BB6EA8A7C0FB6D6332E320E71CC4EB462A2A62E2BFE08F0CCAD93E61BEDB5DD0B786A728AB666F07E0576D189C92BF9FB20DCA49AC2D3956D47385E2 -Out = c81e4f41dd960abd066339f24b744f453bfe642bf5a5dfea63246cc7 - -In = CA76D3A12595A817682617006848675547D3E8F50C2210F9AF906C0E7CE50B4460186FE70457A9E879E79FD4D1A688C70A347361C847BA0DD6AA52936EAF8E58A1BE2F5C1C704E20146D366AEB3853BED9DE9BEFE9569AC8AAEA37A9FB7139A1A1A7D5C748605A8DEFB297869EBEDD71D615A5DA23496D11E11ABBB126B206FA0A7797EE7DE117986012D0362DCEF775C2FE145ADA6BDA1CCB326BF644 -Out = 112f7ecc2afe7a429687804eccd7df473a2c58e6e21aa0ee759310ba - -In = F76B85DC67421025D64E93096D1D712B7BAF7FB001716F02D33B2160C2C882C310EF13A576B1C2D30EF8F78EF8D2F465007109AAD93F74CB9E7D7BEF7C9590E8AF3B267C89C15DB238138C45833C98CC4A471A7802723EF4C744A853CF80A0C2568DD4ED58A2C9644806F42104CEE53628E5BDF7B63B0B338E931E31B87C24B146C6D040605567CEEF5960DF9E022CB469D4C787F4CBA3C544A1AC91F95F -Out = 87e063509cc4b67a1d25858034b53646738d3c906c48492e9a977425 - -In = 25B8C9C032EA6BCD733FFC8718FBB2A503A4EA8F71DEA1176189F694304F0FF68E862A8197B839957549EF243A5279FC2646BD4C009B6D1EDEBF24738197ABB4C992F6B1DC9BA891F570879ACCD5A6B18691A93C7D0A8D38F95B639C1DAEB48C4C2F15CCF5B9D508F8333C32DE78781B41850F261B855C4BEBCC125A380C54D501C5D3BD07E6B52102116088E53D76583B0161E2A58D0778F091206AABD5A1 -Out = 692810807c7602365c8be4b2bd2348cf87f691db4730bba80193ea9b - -In = 21CFDC2A7CCB7F331B3D2EEFFF37E48AD9FA9C788C3F3C200E0173D99963E1CBCA93623B264E920394AE48BB4C3A5BB96FFBC8F0E53F30E22956ADABC2765F57FB761E147ECBF8567533DB6E50C8A1F894310A94EDF806DD8CA6A0E141C0FA7C9FAE6C6AE65F18C93A8529E6E5B553BF55F25BE2E80A9882BD37F145FECBEB3D447A3C4E46C21524CC55CDD62F521AB92A8BA72B897996C49BB273198B7B1C9E -Out = 463bc05c5e7648e16b1cf4a37697139ed20625ca611360de3b20839b - -In = 4E452BA42127DCC956EF4F8F35DD68CB225FB73B5BC7E1EC5A898BBA2931563E74FAFF3B67314F241EC49F4A7061E3BD0213AE826BAB380F1F14FAAB8B0EFDDD5FD1BB49373853A08F30553D5A55CCBBB8153DE4704F29CA2BDEEF0419468E05DD51557CCC80C0A96190BBCC4D77ECFF21C66BDF486459D427F986410F883A80A5BCC32C20F0478BB9A97A126FC5F95451E40F292A4614930D054C851ACD019CCF -Out = 28ae8245484af07503c8da5c8aff313741ef19e1e28ee12d4931a6be - -In = FA85671DF7DADF99A6FFEE97A3AB9991671F5629195049880497487867A6C446B60087FAC9A0F2FCC8E3B24E97E42345B93B5F7D3691829D3F8CCD4BB36411B85FC2328EB0C51CB3151F70860AD3246CE0623A8DC8B3C49F958F8690F8E3860E71EB2B1479A5CEA0B3F8BEFD87ACAF5362435EAECCB52F38617BC6C5C2C6E269EAD1FBD69E941D4AD2012DA2C5B21BCFBF98E4A77AB2AF1F3FDA3233F046D38F1DC8 -Out = 02781b3e406ce42756bd7572ebf273bf5373936a031c4d51c3c77c51 - -In = E90847AE6797FBC0B6B36D6E588C0A743D725788CA50B6D792352EA8294F5BA654A15366B8E1B288D84F5178240827975A763BC45C7B0430E8A559DF4488505E009C63DA994F1403F407958203CEBB6E37D89C94A5EACF6039A327F6C4DBBC7A2A307D976AA39E41AF6537243FC218DFA6AB4DD817B6A397DF5CA69107A9198799ED248641B63B42CB4C29BFDD7975AC96EDFC274AC562D0474C60347A078CE4C25E88 -Out = 2dbb8bddf19da20c10337f9df558ba448c67989e900ad1562af88677 - -In = F6D5C2B6C93954FC627602C00C4CA9A7D3ED12B27173F0B2C9B0E4A5939398A665E67E69D0B12FB7E4CEB253E8083D1CEB724AC07F009F094E42F2D6F2129489E846EAFF0700A8D4453EF453A3EDDC18F408C77A83275617FABC4EA3A2833AA73406C0E966276079D38E8E38539A70E194CC5513AAA457C699383FD1900B1E72BDFB835D1FD321B37BA80549B078A49EA08152869A918CA57F5B54ED71E4FD3AC5C06729 -Out = 13999b2a55c892740e2fba894add14e4c1d155db375a2c13d004e796 - -In = CF8562B1BED89892D67DDAAF3DEEB28246456E972326DBCDB5CF3FB289ACA01E68DA5D59896E3A6165358B071B304D6AB3D018944BE5049D5E0E2BB819ACF67A6006111089E6767132D72DD85BEDDCBB2D64496DB0CC92955AB4C6234F1EEA24F2D51483F2E209E4589BF9519FAC51B4D061E801125E605F8093BB6997BC163D551596FE4AB7CFAE8FB9A90F6980480CE0C229FD1675409BD788354DAF316240CFE0AF93EB -Out = e77f719583964b841e84a1fd8513397e694cd50675136bc5049bd657 - -In = 2ACE31ABB0A2E3267944D2F75E1559985DB7354C6E605F18DC8470423FCA30B7331D9B33C4A4326783D1CAAE1B4F07060EFF978E4746BF0C7E30CD61040BD5EC2746B29863EB7F103EBDA614C4291A805B6A4C8214230564A0557BC7102E0BD3ED23719252F7435D64D210EE2AAFC585BE903FA41E1968C50FD5D5367926DF7A05E3A42CF07E656FF92DE73B036CF8B19898C0CB34557C0C12C2D8B84E91181AF467BC75A9D1 -Out = fc01efe802e733d81b9643955c3c1f6511cf111e2f5d174eacbf8d27 - -In = 0D8D09AED19F1013969CE5E7EB92F83A209AE76BE31C754844EA9116CEB39A22EBB6003017BBCF26555FA6624185187DB8F0CB3564B8B1C06BF685D47F3286EDA20B83358F599D2044BBF0583FAB8D78F854FE0A596183230C5EF8E54426750EAF2CC4E29D3BDD037E734D863C2BD9789B4C243096138F7672C232314EFFDFC6513427E2DA76916B5248933BE312EB5DDE4CF70804FB258AC5FB82D58D08177AC6F4756017FFF5 -Out = a7123385b484cd2da667fd049b99467d5cb1c2890b59f7bc003fc685 - -In = C3236B73DEB7662BF3F3DAA58F137B358BA610560EF7455785A9BEFDB035A066E90704F929BD9689CEF0CE3BDA5ACF4480BCEB8D09D10B098AD8500D9B6071DFC3A14AF6C77511D81E3AA8844986C3BEA6F469F9E02194C92868CD5F51646256798FF0424954C1434BDFED9FACB390B07D342E992936E0F88BFD0E884A0DDB679D0547CCDEC6384285A45429D115AC7D235A717242021D1DC35641F5F0A48E8445DBA58E6CB2C8EA -Out = b1dc062dfe088856dc5abcf99c3712f778bbec4bf5b64cf3c57490e0 - -In = B39FEB8283EADC63E8184B51DF5AE3FD41AAC8A963BB0BE1CD08AA5867D8D910C669221E73243360646F6553D1CA05A84E8DC0DE05B6419EC349CA994480193D01C92525F3FB3DCEFB08AFC6D26947BDBBFD85193F53B50609C6140905C53A6686B58E53A319A57B962331EDE98149AF3DE3118A819DA4D76706A0424B4E1D2910B0ED26AF61D150EBCB46595D4266A0BD7F651BA47D0C7F179CA28545007D92E8419D48FDFBD744CE -Out = 4084988104259252fed4dff3ecfc889e1e184ecbb7be67e07dd06758 - -In = A983D54F503803E8C7999F4EDBBE82E9084F422143A932DDDDC47A17B0B7564A7F37A99D0786E99476428D29E29D3C197A72BFAB1342C12A0FC4787FD7017D7A6174049EA43B5779169EF7472BDBBD941DCB82FC73AAC45A8A94C9F2BD3477F61FD3B796F02A1B8264A214C6FEA74B7051B226C722099EC7883A462B83B6AFDD4009248B8A237F605FE5A08FE7D8B45321421EBBA67BD70A0B00DDBF94BAAB7F359D5D1EEA105F28DCFB -Out = c8d88ecb1f35b7172de504ffaac4e2a08aa756d92501e6ee4383b815 - -In = E4D1C1897A0A866CE564635B74222F9696BF2C7F640DD78D7E2ACA66E1B61C642BB03EA7536AAE597811E9BF4A7B453EDE31F97B46A5F0EF51A071A2B3918DF16B152519AE3776F9F1EDAB4C2A377C3292E96408359D3613844D5EB393000283D5AD3401A318B12FD1474B8612F2BB50FB6A8B9E023A54D7DDE28C43D6D8854C8D9D1155935C199811DBFC87E9E0072E90EB88681CC7529714F8FB8A2C9D88567ADFB974EE205A9BF7B848 -Out = 939d6d46f204cb8526676b1bcf309b85187f542e07825718fe44e412 - -In = B10C59723E3DCADD6D75DF87D0A1580E73133A9B7D00CB95EC19F5547027323BE75158B11F80B6E142C6A78531886D9047B08E551E75E6261E79785366D7024BD7CD9CF322D9BE7D57FB661069F2481C7BB759CD71B4B36CA2BC2DF6D3A328FAEBDB995A9794A8D72155ED551A1F87C80BF6059B43FC764900B18A1C2441F7487743CF84E565F61F8DD2ECE6B6CCC9444049197AAAF53E926FBEE3BFCA8BE588EC77F29D211BE89DE18B15F6 -Out = 471ff6f1eb3955c23fdb4c0246d59296260245283eefdf6ac271bfb1 - -In = DB11F609BABA7B0CA634926B1DD539C8CBADA24967D7ADD4D9876F77C2D80C0F4DCEFBD7121548373582705CCA2495BD2A43716FE64ED26D059CFB566B3364BD49EE0717BDD9810DD14D8FAD80DBBDC4CAFB37CC60FB0FE2A80FB4541B8CA9D59DCE457738A9D3D8F641AF8C3FD6DA162DC16FC01AAC527A4A0255B4D231C0BE50F44F0DB0B713AF03D968FE7F0F61ED0824C55C4B5265548FEBD6AAD5C5EEDF63EFE793489C39B8FD29D104CE -Out = 36595a20f5c9a01ed25f8d52b3bf3026fb200970f0e73c2184aed9e4 - -In = BEBD4F1A84FC8B15E4452A54BD02D69E304B7F32616AADD90537937106AE4E28DE9D8AAB02D19BC3E2FDE1D651559E296453E4DBA94370A14DBBB2D1D4E2022302EE90E208321EFCD8528AD89E46DC839EA9DF618EA8394A6BFF308E7726BAE0C19BCD4BE52DA6258E2EF4E96AA21244429F49EF5CB486D7FF35CAC1BACB7E95711944BCCB2AB34700D42D1EB38B5D536B947348A458EDE3DC6BD6EC547B1B0CAE5B257BE36A7124E1060C170FFA -Out = 4a34b435d182661100c14b1dc95a47ce67b9f36abe3a1eeef2eb5c73 - -In = 5ACA56A03A13784BDC3289D9364F79E2A85C12276B49B92DB0ADAA4F206D5028F213F678C3510E111F9DC4C1C1F8B6ACB17A6413AA227607C515C62A733817BA5E762CC6748E7E0D6872C984D723C9BB3B117EB8963185300A80BFA65CDE495D70A46C44858605FCCBED086C2B45CEF963D33294DBE9706B13AF22F1B7C4CD5A001CFEC251FBA18E722C6E1C4B1166918B4F6F48A98B64B3C07FC86A6B17A6D0480AB79D4E6415B520F1C484D675B1 -Out = addbdad9c5cf13c6abe94e34426a406a75163e72532d203436027121 - -In = A5AAD0E4646A32C85CFCAC73F02FC5300F1982FABB2F2179E28303E447854094CDFC854310E5C0F60993CEFF54D84D6B46323D930ADB07C17599B35B505F09E784BCA5985E0172257797FB53649E2E9723EFD16865C31B5C3D5113B58BB0BFC8920FABDDA086D7537E66D709D050BD14D0C960873F156FAD5B3D3840CDFCDC9BE6AF519DB262A27F40896AB25CC39F96984D650611C0D5A3080D5B3A1BF186ABD42956588B3B58CD948970D298776060 -Out = ccad25625f5456d361c7c1485d016e43eb52f0d6520846680a913b02 - -In = 06CBBE67E94A978203EAD6C057A1A5B098478B4B4CBEF5A97E93C8E42F5572713575FC2A884531D7622F8F879387A859A80F10EF02708CD8F7413AB385AFC357678B9578C0EBF641EF076A1A30F1F75379E9DCB2A885BDD295905EE80C0168A62A9597D10CF12DD2D8CEE46645C7E5A141F6E0E23AA482ABE5661C16E69EF1E28371E2E236C359BA4E92C25626A7B7FF13F6EA4AE906E1CFE163E91719B1F750A96CBDE5FBC953D9E576CD216AFC90323A -Out = 534b4bdb1ea69b150278457420d6ccf23e4ecc32b234da39cf3f2dff - -In = F1C528CF7739874707D4D8AD5B98F7C77169DE0B57188DF233B2DC8A5B31EDA5DB4291DD9F68E6BAD37B8D7F6C9C0044B3BF74BBC3D7D1798E138709B0D75E7C593D3CCCDC1B20C7174B4E692ADD820ACE262D45CCFAE2077E878796347168060A162ECCA8C38C1A88350BD63BB539134F700FD4ADDD5959E255337DAA06BC86358FABCBEFDFB5BC889783D843C08AADC6C4F6C36F65F156E851C9A0F917E4A367B5AD93D874812A1DE6A7B93CD53AD97232 -Out = db2ef9a783a2efd08d3c90b82446720c8935ffb8c28f67c26c96b20d - -In = 9D9F3A7ECD51B41F6572FD0D0881E30390DFB780991DAE7DB3B47619134718E6F987810E542619DFAA7B505C76B7350C6432D8BF1CFEBDF1069B90A35F0D04CBDF130B0DFC7875F4A4E62CDB8E525AADD7CE842520A482AC18F09442D78305FE85A74E39E760A4837482ED2F437DD13B2EC1042AFCF9DECDC3E877E50FF4106AD10A525230D11920324A81094DA31DEAB6476AA42F20C84843CFC1C58545EE80352BDD3740DD6A16792AE2D86F11641BB717C2 -Out = 229ffa8ad5900504a7f6b7253926a5452add85203e5296f524f77f35 - -In = 5179888724819FBAD3AFA927D3577796660E6A81C52D98E9303261D5A4A83232F6F758934D50AA83FF9E20A5926DFEBAAC49529D006EB923C5AE5048ED544EC471ED7191EDF46363383824F915769B3E688094C682B02151E5EE01E510B431C8865AFF8B6B6F2F59CB6D129DA79E97C6D2B8FA6C6DA3F603199D2D1BCAB547682A81CD6CF65F6551121391D78BCC23B5BD0E922EC6D8BF97C952E84DD28AEF909ABA31EDB903B28FBFC33B7703CD996215A11238 -Out = 174e4ad0a551f460d4fb4b7adf708a1f0aea0288259c1446fd4c2d3e - -In = 576EF3520D30B7A4899B8C0D5E359E45C5189ADD100E43BE429A02FB3DE5FF4F8FD0E79D9663ACCA72CD29C94582B19292A557C5B1315297D168FBB54E9E2ECD13809C2B5FCE998EDC6570545E1499DBE7FB74D47CD7F35823B212B05BF3F5A79CAA34224FDD670D335FCB106F5D92C3946F44D3AFCBAE2E41AC554D8E6759F332B76BE89A0324AA12C5482D1EA3EE89DED4936F3E3C080436F539FA137E74C6D3389BDF5A45074C47BC7B20B0948407A66D855E2F -Out = 0d8d2d56cdd6e19376cff11a2afbddfa919dc20ac7b7108486b172ea - -In = 0DF2152FA4F4357C8741529DD77E783925D3D76E95BAFA2B542A2C33F3D1D117D159CF473F82310356FEE4C90A9E505E70F8F24859656368BA09381FA245EB6C3D763F3093F0C89B972E66B53D59406D9F01AEA07F8B3B615CAC4EE4D05F542E7D0DAB45D67CCCCD3A606CCBEB31EA1FA7005BA07176E60DAB7D78F6810EF086F42F08E595F0EC217372B98970CC6321576D92CE38F7C397A403BADA1548D205C343AC09DECA86325373C3B76D9F32028FEA8EB32515 -Out = b45689b7690b46dd097e6bd75fd7d95b0a115d78e758c0f53341c963 - -In = 3E15350D87D6EBB5C8AD99D42515CFE17980933C7A8F6B8BBBF0A63728CEFAAD2052623C0BD5931839112A48633FB3C2004E0749C87A41B26A8B48945539D1FF41A4B269462FD199BFECD45374756F55A9116E92093AC99451AEFB2AF9FD32D6D7F5FBC7F7A540D5097C096EBC3B3A721541DE073A1CC02F7FB0FB1B9327FB0B1218CA49C9487AB5396622A13AE546C97ABDEF6B56380DDA7012A8384091B6656D0AB272D363CEA78163FF765CDD13AB1738B940D16CAE -Out = 53d76ca8defcfaecf1bb14618a3f0c713f2c3c3422cf11d241eb4c10 - -In = C38D6B0B757CB552BE40940ECE0009EF3B0B59307C1451686F1A22702922800D58BCE7A636C1727EE547C01B214779E898FC0E560F8AE7F61BEF4D75EAA696B921FD6B735D171535E9EDD267C192B99880C87997711002009095D8A7A437E258104A41A505E5EF71E5613DDD2008195F0C574E6BA3FE40099CFA116E5F1A2FA8A6DA04BADCB4E2D5D0DE31FDC4800891C45781A0AAC7C907B56D631FCA5CE8B2CDE620D11D1777ED9FA603541DE794DDC5758FCD5FAD78C0 -Out = 3c9f950687fbcc49f2d85cf0ce22ba183fab2c63e4453e072ffa5fbc - -In = 8D2DE3F0B37A6385C90739805B170057F091CD0C7A0BC951540F26A5A75B3E694631BB64C7635EED316F51318E9D8DE13C70A2ABA04A14836855F35E480528B776D0A1E8A23B547C8B8D6A0D09B241D3BE9377160CCA4E6793D00A515DC2992CB7FC741DACA171431DA99CCE6F7789F129E2AC5CF65B40D703035CD2185BB936C82002DAF8CBC27A7A9E554B06196630446A6F0A14BA155ED26D95BD627B7205C072D02B60DB0FD7E49EA058C2E0BA202DAFF0DE91E845CF79 -Out = f9c3f4b2421d0ae667f4df71261062a85aca92cf922af91b2ef00aa4 - -In = C464BBDAD275C50DCD983B65AD1019B9FF85A1E71C807F3204BB2C921DC31FBCD8C5FC45868AE9EF85B6C9B83BBA2A5A822201ED68586EC5EC27FB2857A5D1A2D09D09115F22DCC39FE61F5E1BA0FF6E8B4ACB4C6DA748BE7F3F0839739394FF7FA8E39F7F7E84A33C3866875C01BCB1263C9405D91908E9E0B50E7459FABB63D8C6BBB73D8E3483C099B55BC30FF092FF68B6ADEDFD477D63570C9F5515847F36E24BA0B705557130CEC57EBAD1D0B31A378E91894EE26E3A04 -Out = 65d58a6ba1e5372aa8a9bb7f7777075c3774398dbf0e8d68e4ae92ab - -In = 8B8D68BB8A75732FE272815A68A1C9C5AA31B41DEDC8493E76525D1D013D33CEBD9E21A5BB95DB2616976A8C07FCF411F5F6BC6F7E0B57ACA78CC2790A6F9B898858AC9C79B165FF24E66677531E39F572BE5D81EB3264524181115F32780257BFB9AEEC6AF12AF28E587CAC068A1A2953B59AD680F4C245B2E3EC36F59940D37E1D3DB38E13EDB29B5C0F404F6FF87F80FC8BE7A225FF22FBB9C8B6B1D7330C57840D24BC75B06B80D30DAD6806544D510AF6C4785E823AC3E0B8 -Out = 8b0e038bee7afc95cd40a341e667aae46413d8df91ece90e9da96ec1 - -In = 6B018710446F368E7421F1BC0CCF562D9C1843846BC8D98D1C9BF7D9D6FCB48BFC3BF83B36D44C4FA93430AF75CD190BDE36A7F92F867F58A803900DF8018150384D85D82132F123006AC2AEBA58E02A037FE6AFBD65ECA7C44977DD3DC74F48B6E7A1BFD5CC4DCF24E4D52E92BD4455848E4928B0EAC8B7476FE3CC03E862AA4DFF4470DBFED6DE48E410F25096487ECFC32A27277F3F5023B2725ADE461B1355889554A8836C9CF53BD767F5737D55184EEA1AB3F53EDD0976C485 -Out = 1d513a663c14801900cf786e4f1fb97224b83b0978d7964558871286 - -In = C9534A24714BD4BE37C88A3DA1082EDA7CABD154C309D7BD670DCCD95AA535594463058A29F79031D6ECAA9F675D1211E9359BE82669A79C855EA8D89DD38C2C761DDD0EC0CE9E97597432E9A1BEAE062CDD71EDFDFD464119BE9E69D18A7A7FD7CE0E2106F0C8B0ABF4715E2CA48EF9F454DC203C96656653B727083513F8EFB86E49C513BB758B3B052FE21F1C05BB33C37129D6CC81F1AEF6ADC45B0E8827A830FE545CF57D0955802C117D23CCB55EA28F95C0D8C2F9C5A242B33F -Out = eb88c491a852a31f38e78a8d0b430b9566917aba73b337e17c877e6a - -In = 07906C87297B867ABF4576E9F3CC7F82F22B154AFCBF293B9319F1B0584DA6A40C27B32E0B1B7F412C4F1B82480E70A9235B12EC27090A5A33175A2BB28D8ADC475CEFE33F7803F8CE27967217381F02E67A3B4F84A71F1C5228E0C2AD971373F6F672624FCEA8D1A9F85170FAD30FA0BBD25035C3B41A6175D467998BD1215F6F3866F53847F9CF68EF3E2FBB54BC994DE2302B829C5EEA68EC441FCBAFD7D16AE4FE9FFF98BF00E5BC2AD54DD91FF9FDA4DD77B6C754A91955D1FBAAD0 -Out = d5848e6ec1f79c10c9c8c085d965eed9e3fb59747f5c574b01b4b7c5 - -In = 588E94B9054ABC2189DF69B8BA34341B77CDD528E7860E5DEFCAA79B0C9A452AD4B82AA306BE84536EB7CEDCBE058D7B84A6AEF826B028B8A0271B69AC3605A9635EA9F5EA0AA700F3EB7835BC54611B922964300C953EFE7491E3677C2CEBE0822E956CD16433B02C68C4A23252C3F9E151A416B4963257B783E038F6B4D5C9F110F871652C7A649A7BCEDCBCCC6F2D0725BB903CC196BA76C76AA9F10A190B1D1168993BAA9FFC96A1655216773458BEC72B0E39C9F2C121378FEAB4E76A -Out = d678f01d6da99ae8cba1651ae1b3a11fbe84bd73a76c58b6bf9607f1 - -In = 08959A7E4BAAE874928813364071194E2939772F20DB7C3157078987C557C2A6D5ABE68D520EEF3DC491692E1E21BCD880ADEBF63BB4213B50897FA005256ED41B5690F78F52855C8D9168A4B666FCE2DA2B456D7A7E7C17AB5F2FB1EE90B79E698712E963715983FD07641AE4B4E9DC73203FAC1AE11FA1F8C7941FCC82EAB247ADDB56E2638447E9D609E610B60CE086656AAEBF1DA3C8A231D7D94E2FD0AFE46B391FF14A72EAEB3F44AD4DF85866DEF43D4781A0B3578BC996C87970B132 -Out = 98843bb1f4bd663849885699c9b944a738e0006d795dcbab2965dccc - -In = CB2A234F45E2ECD5863895A451D389A369AAB99CFEF0D5C9FFCA1E6E63F763B5C14FB9B478313C8E8C0EFEB3AC9500CF5FD93791B789E67EAC12FD038E2547CC8E0FC9DB591F33A1E4907C64A922DDA23EC9827310B306098554A4A78F050262DB5B545B159E1FF1DCA6EB734B872343B842C57EAFCFDA8405EEDBB48EF32E99696D135979235C3A05364E371C2D76F1902F1D83146DF9495C0A6C57D7BF9EE77E80F9787AEE27BE1FE126CDC9EF893A4A7DCBBC367E40FE4E1EE90B42EA25AF01 -Out = def31ca9f3873379cd2b4fbc2814d0cc67ceafe91521afa84a4261d8 - -In = D16BEADF02AB1D4DC6F88B8C4554C51E866DF830B89C06E786A5F8757E8909310AF51C840EFE8D20B35331F4355D80F73295974653DDD620CDDE4730FB6C8D0D2DCB2B45D92D4FBDB567C0A3E86BD1A8A795AF26FBF29FC6C65941CDDB090FF7CD230AC5268AB4606FCCBA9EDED0A2B5D014EE0C34F0B2881AC036E24E151BE89EEB6CD9A7A790AFCCFF234D7CB11B99EBF58CD0C589F20BDAC4F9F0E28F75E3E04E5B3DEBCE607A496D848D67FA7B49132C71B878FD5557E082A18ECA1FBDA94D4B -Out = 681d3c081207618a1b3321393498c8c58ee7b5ddff597ab2cc4bfde9 - -In = 8F65F6BC59A85705016E2BAE7FE57980DE3127E5AB275F573D334F73F8603106EC3553016608EF2DD6E69B24BE0B7113BF6A760BA6E9CE1C48F9E186012CF96A1D4849D75DF5BB8315387FD78E9E153E76F8BA7EC6C8849810F59FB4BB9B004318210B37F1299526866F44059E017E22E96CBE418699D014C6EA01C9F0038B10299884DBEC3199BB05ADC94E955A1533219C1115FED0E5F21228B071F40DD57C4240D98D37B73E412FE0FA4703120D7C0C67972ED233E5DEB300A22605472FA3A3BA86 -Out = 4fd27570c2c4b665e4ac9a97b6201dc85d274524c11c0c5e2da3c412 - -In = 84891E52E0D451813210C3FD635B39A03A6B7A7317B221A7ABC270DFA946C42669AACBBBDF801E1584F330E28C729847EA14152BD637B3D0F2B38B4BD5BF9C791C58806281103A3EABBAEDE5E711E539E6A8B2CF297CF351C078B4FA8F7F35CF61BEBF8814BF248A01D41E86C5715EA40C63F7375379A7EB1D78F27622FB468AB784AAABA4E534A6DFD1DF6FA15511341E725ED2E87F98737CCB7B6A6DFAE416477472B046BF1811187D151BFA9F7B2BF9ACDB23A3BE507CDF14CFDF517D2CB5FB9E4AB6 -Out = 509d74c9639ab044b635110334003f59d46c440cf48aa57b15d68184 - -In = FDD7A9433A3B4AFABD7A3A5E3457E56DEBF78E84B7A0B0CA0E8C6D53BD0C2DAE31B2700C6128334F43981BE3B213B1D7A118D59C7E6B6493A86F866A1635C12859CFB9AD17460A77B4522A5C1883C3D6ACC86E6162667EC414E9A104AA892053A2B1D72165A855BACD8FAF8034A5DD9B716F47A0818C09BB6BAF22AA503C06B4CA261F557761989D2AFBD88B6A678AD128AF68672107D0F1FC73C5CA740459297B3292B281E93BCEB761BDE7221C3A55708E5EC84472CDDCAA84ECF23723CC0991355C6280 -Out = d52ad0bbaac22a144f9e21cc99a585edd87878fea7c561fa76935c12 - -In = 70A40BFBEF92277A1AAD72F6B79D0177197C4EBD432668CFEC05D099ACCB651062B5DFF156C0B27336687A94B26679CFDD9DAF7AD204338DD9C4D14114033A5C225BD11F217B5F4732DA167EE3F939262D4043FC9CBA92303B7B5E96AEA12ADDA64859DF4B86E9EE0B58E39091E6B188B408AC94E1294A8911245EE361E60E601EFF58D1D37639F3753BEC80EBB4EFDE25817436076623FC65415FE51D1B0280366D12C554D86743F3C3B6572E400361A60726131441BA493A83FBE9AFDA90F7AF1AE717238D -Out = 17c24deccce765600437c1433d2f2f69552eceae7c8346e647b26e1d - -In = 74356E449F4BF8644F77B14F4D67CB6BD9C1F5AE357621D5B8147E562B65C66585CAF2E491B48529A01A34D226D436959153815380D5689E30B35357CDAC6E08D3F2B0E88E200600D62BD9F5EAF488DF86A4470EA227006182E44809009868C4C280C43D7D64A5268FA719074960087B3A6ABC837882F882C837834535929389A12B2C78187E2EA07EF8B8EEF27DC85002C3AE35F1A50BEE6A1C48BA7E175F3316670B27983472AA6A61EED0A683A39EE323080620EA44A9F74411AE5CE99030528F9AB49C79F2 -Out = b28f9ef601bdee03f2c4bbb2d358dd5e232ebbe3c8f24be10f217e37 - -In = 8C3798E51BC68482D7337D3ABB75DC9FFE860714A9AD73551E120059860DDE24AB87327222B64CF774415A70F724CDF270DE3FE47DDA07B61C9EF2A3551F45A5584860248FABDE676E1CD75F6355AA3EAEABE3B51DC813D9FB2EAA4F0F1D9F834D7CAD9C7C695AE84B329385BC0BEF895B9F1EDF44A03D4B410CC23A79A6B62E4F346A5E8DD851C2857995DDBF5B2D717AEB847310E1F6A46AC3D26A7F9B44985AF656D2B7C9406E8A9E8F47DCB4EF6B83CAACF9AEFB6118BFCFF7E44BEF6937EBDDC89186839B77 -Out = 1c035c2668feca7620c592212864883e4f68808c4b6ba4a574b7086f - -In = FA56BF730C4F8395875189C10C4FB251605757A8FECC31F9737E3C2503B02608E6731E85D7A38393C67DE516B85304824BFB135E33BF22B3A23B913BF6ACD2B7AB85198B8187B2BCD454D5E3318CACB32FD6261C31AE7F6C54EF6A7A2A4C9F3ECB81CE3555D4F0AD466DD4C108A90399D70041997C3B25345A9653F3C9A6711AB1B91D6A9D2216442DA2C973CBD685EE7643BFD77327A2F7AE9CB283620A08716DFB462E5C1D65432CA9D56A90E811443CD1ECB8F0DE179C9CB48BA4F6FEC360C66F252F6E64EDC96B -Out = 856f030764bbfbdd5d1384ea9da5bbf7f0a06e0b57445bcf50e10c77 - -In = B6134F9C3E91DD8000740D009DD806240811D51AB1546A974BCB18D344642BAA5CD5903AF84D58EC5BA17301D5EC0F10CCD0509CBB3FD3FFF9172D193AF0F782252FD1338C7244D40E0E42362275B22D01C4C3389F19DD69BDF958EBE28E31A4FFE2B5F18A87831CFB7095F58A87C9FA21DB72BA269379B2DC2384B3DA953C7925761FED324620ACEA435E52B424A7723F6A2357374157A34CD8252351C25A1B232826CEFE1BD3E70FFC15A31E7C0598219D7F00436294D11891B82497BC78AA5363892A2495DF8C1EEF -Out = 844cc3a727675ca2ace1c78e6fc2e15a0de1bfed22017bda97ae8af9 - -In = C941CDB9C28AB0A791F2E5C8E8BB52850626AA89205BEC3A7E22682313D198B1FA33FC7295381354858758AE6C8EC6FAC3245C6E454D16FA2F51C4166FAB51DF272858F2D603770C40987F64442D487AF49CD5C3991CE858EA2A60DAB6A65A34414965933973AC2457089E359160B7CDEDC42F29E10A91921785F6B7224EE0B349393CDCFF6151B50B377D609559923D0984CDA6000829B916AB6896693EF6A2199B3C22F7DC5500A15B8258420E314C222BC000BC4E5413E6DD82C993F8330F5C6D1BE4BC79F08A1A0A46 -Out = c6e18316befa5793bfbace6f44655b18080083be196a9148ccf3f264 - -In = 4499EFFFAC4BCEA52747EFD1E4F20B73E48758BE915C88A1FFE5299B0B005837A46B2F20A9CB3C6E64A9E3C564A27C0F1C6AD1960373036EC5BFE1A8FC6A435C2185ED0F114C50E8B3E4C7ED96B06A036819C9463E864A58D6286F785E32A804443A56AF0B4DF6ABC57ED5C2B185DDEE8489EA080DEEEE66AA33C2E6DAB36251C402682B6824821F998C32163164298E1FAFD31BABBCFFB594C91888C6219079D907FDB438ED89529D6D96212FD55ABE20399DBEFD342248507436931CDEAD496EB6E4A80358ACC78647D043 -Out = df45297feb54539d81283aba4a482947b0df1fe818fcb1a2c716803a - -In = EECBB8FDFA4DA62170FD06727F697D81F83F601FF61E478105D3CB7502F2C89BF3E8F56EDD469D049807A38882A7EEFBC85FC9A950952E9FA84B8AFEBD3CE782D4DA598002827B1EB98882EA1F0A8F7AA9CE013A6E9BC462FB66C8D4A18DA21401E1B93356EB12F3725B6DB1684F2300A98B9A119E5D27FF704AFFB618E12708E77E6E5F34139A5A41131FD1D6336C272A8FC37080F041C71341BEE6AB550CB4A20A6DDB6A8E0299F2B14BC730C54B8B1C1C487B494BDCCFD3A53535AB2F231590BF2C4062FD2AD58F906A2D0D -Out = 19b9bd4d5349788ae6d12eeac0407b84de297b8d8e23339150303a71 - -In = E64F3E4ACE5C8418D65FEC2BC5D2A303DD458034736E3B0DF719098BE7A206DEAF52D6BA82316CAF330EF852375188CDE2B39CC94AA449578A7E2A8E3F5A9D68E816B8D16889FBC0EBF0939D04F63033AE9AE2BDAB73B88C26D6BD25EE460EE1EF58FB0AFA92CC539F8C76D3D097E7A6A63EBB9B5887EDF3CF076028C5BBD5B9DB3211371AD3FE121D4E9BF44229F4E1ECF5A0F9F0EBA4D5CEB72878AB22C3F0EB5A625323AC66F7061F4A81FAC834471E0C59553F108475FE290D43E6A055AE3EE46FB67422F814A68C4BE3E8C9 -Out = 4f5f0377d3335014cfb8ae31f1bf33732dda8c6e9105ea72f6f238a3 - -In = D2CB2D733033F9E91395312808383CC4F0CA974E87EC68400D52E96B3FA6984AC58D9AD0938DDE5A973008D818C49607D9DE2284E7618F1B8AED8372FBD52ED54557AF4220FAC09DFA8443011699B97D743F8F2B1AEF3537EBB45DCC9E13DFB438428EE190A4EFDB3CAEB7F3933117BF63ABDC7E57BEB4171C7E1AD260AB0587806C4D137B6316B50ABC9CCE0DFF3ACADA47BBB86BE777E617BBE578FF4519844DB360E0A96C6701290E76BB95D26F0F804C8A4F2717EAC4E7DE9F2CFF3BBC55A17E776C0D02856032A6CD10AD2838 -Out = 8cd7f88d47b4135b45c742870d199d60eaf4e1bb4234e8e17a192bb9 - -In = F2998955613DD414CC111DF5CE30A995BB792E260B0E37A5B1D942FE90171A4AC2F66D4928D7AD377F4D0554CBF4C523D21F6E5F379D6F4B028CDCB9B1758D3B39663242FF3CB6EDE6A36A6F05DB3BC41E0D861B384B6DEC58BB096D0A422FD542DF175E1BE1571FB52AE66F2D86A2F6824A8CFAACBAC4A7492AD0433EEB15454AF8F312B3B2A577750E3EFBD370E8A8CAC1582581971FBA3BA4BD0D76E718DACF8433D33A59D287F8CC92234E7A271041B526E389EFB0E40B6A18B3AAF658E82ED1C78631FD23B4C3EB27C3FAEC8685 -Out = d9a1b4b1e5c6e0b568a1671b93f7808d9b16c31a7f8ac37df8c8413d - -In = 447797E2899B72A356BA55BF4DF3ACCA6CDB1041EB477BD1834A9F9ACBC340A294D729F2F97DF3A610BE0FF15EDB9C6D5DB41644B9874360140FC64F52AA03F0286C8A640670067A84E017926A70438DB1BB361DEFEE7317021425F8821DEF26D1EFD77FC853B818545D055ADC9284796E583C76E6FE74C9AC2587AA46AA8F8804F2FEB5836CC4B3ABABAB8429A5783E17D5999F32242EB59EF30CD7ADABC16D72DBDB097623047C98989F88D14EAF02A7212BE16EC2D07981AAA99949DDF89ECD90333A77BC4E1988A82ABF7C7CAF3291 -Out = 91065d29e0e1755d896335ac503d526039d1a6561b70cdc98adf2c07 - -In = 9F2C18ADE9B380C784E170FB763E9AA205F64303067EB1BCEA93DF5DAC4BF5A2E00B78195F808DF24FC76E26CB7BE31DC35F0844CDED1567BBA29858CFFC97FB29010331B01D6A3FB3159CC1B973D255DA9843E34A0A4061CABDB9ED37F241BFABB3C20D32743F4026B59A4CCC385A2301F83C0B0A190B0F2D01ACB8F0D41111E10F2F4E149379275599A52DC089B35FDD5234B0CFB7B6D8AEBD563CA1FA653C5C021DFD6F5920E6F18BFAFDBECBF0AB00281333ED50B9A999549C1C8F8C63D7626C48322E9791D5FF72294049BDE91E73F8 -Out = 769175a927c65ad802211900e31c664d81a8373faf6d964567e56232 - -In = AE159F3FA33619002AE6BCCE8CBBDD7D28E5ED9D61534595C4C9F43C402A9BB31F3B301CBFD4A43CE4C24CD5C9849CC6259ECA90E2A79E01FFBAC07BA0E147FA42676A1D668570E0396387B5BCD599E8E66AAED1B8A191C5A47547F61373021FA6DEADCB55363D233C24440F2C73DBB519F7C9FA5A8962EFD5F6252C0407F190DFEFAD707F3C7007D69FF36B8489A5B6B7C557E79DD4F50C06511F599F56C896B35C917B63BA35C6FF8092BAF7D1658E77FC95D8A6A43EEB4C01F33F03877F92774BE89C1114DD531C011E53A34DC248A2F0E6 -Out = 448039a19c2020dc2ad9062dc87b504793238fcc555387b7c80a9a46 - -In = 3B8E97C5FFC2D6A40FA7DE7FCEFC90F3B12C940E7AB415321E29EE692DFAC799B009C99DCDDB708FCE5A178C5C35EE2B8617143EDC4C40B4D313661F49ABDD93CEA79D117518805496FE6ACF292C4C2A1F76B403A97D7C399DAF85B46AD84E16246C67D6836757BDE336C290D5D401E6C1386AB32797AF6BB251E9B2D8FE754C47482B72E0B394EAB76916126FD68EA7D65EB93D59F5B4C5AC40F7C3B37E7F3694F29424C24AF8C8F0EF59CD9DBF1D28E0E10F799A6F78CAD1D45B9DB3D7DEE4A7059ABE99182714983B9C9D44D7F5643596D4F3 -Out = 48cb9b942cc1c9f3064bfad7b99f419a3a447027f774967ad012ee65 - -In = 3434EC31B10FAFDBFEEC0DD6BD94E80F7BA9DCA19EF075F7EB017512AF66D6A4BCF7D16BA0819A1892A6372F9B35BCC7CA8155EE19E8428BC22D214856ED5FA9374C3C09BDE169602CC219679F65A1566FC7316F4CC3B631A18FB4449FA6AFA16A3DB2BC4212EFF539C67CF184680826535589C7111D73BFFCE431B4C40492E763D9279560AAA38EB2DC14A212D723F994A1FE656FF4DD14551CE4E7C621B2AA5604A10001B2878A897A28A08095C325E10A26D2FB1A75BFD64C250309BB55A44F23BBAC0D5516A1C687D3B41EF2FBBF9CC56D4739 -Out = 300788847e84260eed7f81e9347b0a28705f6dec2ce1d41e7d748a2b - -In = 7C7953D81C8D208FD1C97681D48F49DD003456DE60475B84070EF4847C333B74575B1FC8D2A186964485A3B8634FEAA3595AAA1A2F4595A7D6B6153563DEE31BBAC443C8A33EED6D5D956A980A68366C2527B550EE950250DFB691EACBD5D56AE14B970668BE174C89DF2FEA43AE52F13142639C884FD62A3683C0C3792F0F24AB1318BCB27E21F4737FAB62C77EA38BC8FD1CF41F7DAB64C13FEBE7152BF5BB7AB5A78F5346D43CC741CB6F72B7B8980F268B68BF62ABDFB1577A52438FE14B591498CC95F071228460C7C5D5CEB4A7BDE588E7F21C -Out = 6afd5e6fcae451417ca6968dcd9b8bfb5cd2defd835fcef62e8799bc - -In = 7A6A4F4FDC59A1D223381AE5AF498D74B7252ECF59E389E49130C7EAEE626E7BD9897EFFD92017F4CCDE66B0440462CDEDFD352D8153E6A4C8D7A0812F701CC737B5178C2556F07111200EB627DBC299CAA792DFA58F35935299FA3A3519E9B03166DFFA159103FFA35E8577F7C0A86C6B46FE13DB8E2CDD9DCFBA85BDDDCCE0A7A8E155F81F712D8E9FE646153D3D22C811BD39F830433B2213DD46301941B59293FD0A33E2B63ADBD95239BC01315C46FDB678875B3C81E053A40F581CFBEC24A1404B1671A1B88A6D06120229518FB13A74CA0AC5AE -Out = ae504b123cf7600f8792b90cce79eac630a42ebfcb10fdd2e0dd63dc - -In = D9FAA14CEBE9B7DE551B6C0765409A33938562013B5E8E0E1E0A6418DF7399D0A6A771FB81C3CA9BD3BB8E2951B0BC792525A294EBD1083688806FE5E7F1E17FD4E3A41D00C89E8FCF4A363CAEDB1ACB558E3D562F1302B3D83BB886ED27B76033798131DAB05B4217381EAAA7BA15EC820BB5C13B516DD640EAEC5A27D05FDFCA0F35B3A5312146806B4C0275BCD0AAA3B2017F346975DB566F9B4D137F4EE10644C2A2DA66DEECA5342E236495C3C6280528BFD32E90AF4CD9BB908F34012B52B4BC56D48CC8A6B59BAB014988EABD12E1A0A1C2E170E7 -Out = eea3834e7e253f5204db74719ebcc390ddf7a7677613be337cb88df5 - -In = 2D8427433D0C61F2D96CFE80CF1E932265A191365C3B61AAA3D6DCC039F6BA2AD52A6A8CC30FC10F705E6B7705105977FA496C1C708A277A124304F1FC40911E7441D1B5E77B951AAD7B01FD5DB1B377D165B05BBF898042E39660CAF8B279FE5229D1A8DB86C0999ED65E53D01CCBC4B43173CCF992B3A14586F6BA42F5FE30AFA8AE40C5DF29966F9346DA5F8B35F16A1DE3AB6DE0F477D8D8660918060E88B9B9E9CA6A4207033B87A812DBF5544D39E4882010F82B6CE005F8E8FF6FE3C3806BC2B73C2B83AFB704345629304F9F86358712E9FAE3CA3E -Out = e5049c18e846e89a7ee19e63c9a2fe3d2a9c21c877ce18d789968d7c - -In = 5E19D97887FCAAC0387E22C6F803C34A3DACD2604172433F7A8A7A526CA4A2A1271ECFC5D5D7BE5AC0D85D921095350DFC65997D443C21C8094E0A3FEFD2961BCB94AED03291AE310CCDA75D8ACE4BC7D89E7D3E5D1650BDA5D668B8B50BFC8E608E184F4D3A9A2BADC4FF5F07E0C0BC8A9F2E0B2A26FD6D8C550008FAAAB75FD71AF2A424BEC9A7CD9D83FAD4C8E9319115656A8717D3B523A68FF8004258B9990ED362308461804BA3E3A7E92D8F2FFAE5C2FBA55BA5A3C27C0A2F71BD711D2FE1799C2ADB31B200035481E9EE5C4ADF2AB9C0FA50B23975CF -Out = 37b3ba05184555eeb84a052fea719374d04f5f97ae047ef04ae142a8 - -In = C8E976AB4638909387CE3B8D4E510C3230E5690E02C45093B1D297910ABC481E56EEA0F296F98379DFC9080AF69E73B2399D1C143BEE80AE1328162CE1BA7F6A8374679B20AACD380EB4E61382C99998704D62701AFA914F9A2705CDB065885F50D086C3EB5753700C387118BB142F3E6DA1E988DFB31AC75D7368931E45D1391A274B22F83CEB072F9BCABC0B216685BFD789F5023971024B1878A205442522F9EA7D8797A4102A3DF41703768251FD5E017C85D1200A464118AA35654E7CA39F3C375B8EF8CBE7534DBC64BC20BEFB417CF60EC92F63D9EE7397 -Out = a6dbc685dc366bfce1408346a09e9f82eb51c29d4109131d15d13743 - -In = 7145FA124B7429A1FC2231237A949BA7201BCC1822D3272DE005B682398196C25F7E5CC2F289FBF44415F699CB7FE6757791B1443410234AE061EDF623359E2B4E32C19BF88450432DD01CAA5EB16A1DC378F391CA5E3C4E5F356728BDDD4975DB7C890DA8BBC84CC73FF244394D0D48954978765E4A00B593F70F2CA082673A261ED88DBCEF1127728D8CD89BC2C597E9102CED6010F65FA75A14EBE467FA57CE3BD4948B6867D74A9DF5C0EC6F530CBF2EE61CE6F06BC8F2864DFF5583776B31DF8C7FFCB61428A56BF7BD37188B4A5123BBF338393AF46EDA85E6 -Out = 48c711b6161f5303dbbb0c44d301044abb57a48504d5efda76bf6777 - -In = 7FDFADCC9D29BAD23AE038C6C65CDA1AEF757221B8872ED3D75FF8DF7DA0627D266E224E812C39F7983E4558BFD0A1F2BEF3FEB56BA09120EF762917B9C093867948547AEE98600D10D87B20106878A8D22C64378BF634F7F75900C03986B077B0BF8B740A82447B61B99FEE5376C5EB6680EC9E3088F0BDD0C56883413D60C1357D3C811950E5890E7600103C916341B80C743C6A852B7B4FB60C3BA21F3BC15B8382437A68454779CF3CD7F9F90CCC8EF28D0B706535B1E4108EB5627BB45D719CB046839AEE311CA1ABDC8319E050D67972CB35A6B1601B25DBF487 -Out = b4218bdab6a5a63d80cdfcf8fdf65264ae58948a560c08207ad2fd99 - -In = 988638219FD3095421F826F56E4F09E356296B628C3CE6930C9F2E758FD1A80C8273F2F61E4DAAE65C4F110D3E7CA0965AC7D24E34C0DC4BA2D6FF0BF5BBE93B3585F354D7543CB542A1AA54674D375077F2D360A8F4D42F3DB131C3B7AB7306267BA107659864A90C8C909460A73621D1F5D9D3FD95BEB19B23DB1CB6C0D0FBA91D36891529B8BD8263CAA1BAB56A4AFFAED44962DF096D8D5B1EB845EF31188B3E10F1AF811A13F156BEB7A288AAE593EBD1471B624AA1A7C6ADF01E2200B3D72D88A3AED3100C88231E41EFC376906F0B580DC895F080FDA5741DB1CB -Out = d0731a432132aae12f7acc306e0d7868bc8782e60bb7b5c8804d42d2 - -In = 5AAB62756D307A669D146ABA988D9074C5A159B3DE85151A819B117CA1FF6597F6156E80FDD28C9C3176835164D37DA7DA11D94E09ADD770B68A6E081CD22CA0C004BFE7CD283BF43A588DA91F509B27A6584C474A4A2F3EE0F1F56447379240A5AB1FB77FDCA49B305F07BA86B62756FB9EFB4FC225C86845F026EA542076B91A0BC2CDD136E122C659BE259D98E5841DF4C2F60330D4D8CDEE7BF1A0A244524EECC68FF2AEF5BF0069C9E87A11C6E519DE1A4062A10C83837388F7EF58598A3846F49D499682B683C4A062B421594FAFBC1383C943BA83BDEF515EFCF10D -Out = a8b8dc55bbead446d58e87aa1c07fe58c87f099670eb05d4fb3f96d2 - -In = 47B8216AA0FBB5D67966F2E82C17C07AA2D6327E96FCD83E3DE7333689F3EE79994A1BF45082C4D725ED8D41205CB5BCDF5C341F77FACB1DA46A5B9B2CBC49EADF786BCD881F371A95FA17DF73F606519AEA0FF79D5A11427B98EE7F13A5C00637E2854134691059839121FEA9ABE2CD1BCBBBF27C74CAF3678E05BFB1C949897EA01F56FFA4DAFBE8644611685C617A3206C7A7036E4AC816799F693DAFE7F19F303CE4EBA09D21E03610201BFC665B72400A547A1E00FA9B7AD8D84F84B34AEF118515E74DEF11B9188BD1E1F97D9A12C30132EC2806339BDADACDA2FD8B78 -Out = 442ca919770a679ac8c2caa7c31e47eb0442dbe89d987e84d5d2c6ef - -In = 8CFF1F67FE53C098896D9136389BD8881816CCAB34862BB67A656E3D98896F3CE6FFD4DA73975809FCDF9666760D6E561C55238B205D8049C1CEDEEF374D1735DAA533147BFA960B2CCE4A4F254176BB4D1BD1E89654432B8DBE1A135C42115B394B024856A2A83DC85D6782BE4B444239567CCEC4B184D4548EAE3FF6A192F343292BA2E32A0F267F31CC26719EB85245D415FB897AC2DA433EE91A99424C9D7F1766A44171D1651001C38FC79294ACCC68CEB5665D36218454D3BA169AE058A831338C17743603F81EE173BFC0927464F9BD728DEE94C6AEAB7AAE6EE3A627E8 -Out = d0fce0b2439abe400d461653bc154105cae56c0f53b80cc2647d888c - -In = EACD07971CFF9B9939903F8C1D8CBB5D4DB1B548A85D04E037514A583604E787F32992BF2111B97AC5E8A938233552731321522AB5E8583561260B7D13EBEEF785B23A41FD8576A6DA764A8ED6D822D4957A545D5244756C18AA80E1AAD4D1F9C20D259DEE1711E2CC8FD013169FB7CC4CE38B362F8E0936AE9198B7E838DCEA4F7A5B9429BB3F6BBCF2DC92565E3676C1C5E6EB3DD2A0F86AA23EDD3D0891F197447692794B3DFA269611AD97F72B795602B4FDB198F3FD3EB41B415064256E345E8D8C51C555DC8A21904A9B0F1AD0EFFAB7786AAC2DA3B196507E9F33CA356427 -Out = 9d7ef88e849281dcea90adf1da822253b6ac07849f110e8a7a0f352c - -In = 23AC4E9A42C6EF45C3336CE6DFC2FF7DE8884CD23DC912FEF0F7756C09D335C189F3AD3A23697ABDA851A81881A0C8CCAFC980AB2C702564C2BE15FE4C4B9F10DFB2248D0D0CB2E2887FD4598A1D4ACDA897944A2FFC580FF92719C95CF2AA42DC584674CB5A9BC5765B9D6DDF5789791D15F8DD925AA12BFFAFBCE60827B490BB7DF3DDA6F2A143C8BF96ABC903D83D59A791E2D62814A89B8080A28060568CF24A80AE61179FE84E0FFAD00388178CB6A617D37EFD54CC01970A4A41D1A8D3DDCE46EDBBA4AB7C90AD565398D376F431189CE8C1C33E132FEAE6A8CD17A61C630012 -Out = 408d2b7a956b6d39d5dd67ad7db4b840937d134aeee8be0eb3cf31a4 - -In = 0172DF732282C9D488669C358E3492260CBE91C95CFBC1E3FEA6C4B0EC129B45F242ACE09F152FC6234E1BEE8AAB8CD56E8B486E1DCBA9C05407C2F95DA8D8F1C0AF78EE2ED82A3A79EC0CB0709396EE62AADB84F8A4EE8A7CCCA3C1EE84E302A09EA802204AFECF04097E67D0F8E8A9D2651126C0A598A37081E42D168B0AE8A71951C524259E4E2054E535B779679BDADE566FE55700858618E626B4A0FAF895BCCE9011504A49E05FD56127EAE3D1F8917AFB548ECADABDA1020111FEC9314C413498A360B08640549A22CB23C731ACE743252A8227A0D2689D4C6001606678DFB921 -Out = d56c0952990841c3e2e62a3fe0ff91fd0359ad70cdd84b8a538188cf - -In = 3875B9240CF3E0A8B59C658540F26A701CF188496E2C2174788B126FD29402D6A75453BA0635284D08835F40051A2A9683DC92AFB9383719191231170379BA6F4ADC816FECBB0F9C446B785BF520796841E58878B73C58D3EBB097CE4761FDEABE15DE2F319DFBAF1742CDEB389559C788131A6793E193856661376C81CE9568DA19AA6925B47FFD77A43C7A0E758C37D69254909FF0FBD415EF8EB937BCD49F91468B49974C07DC819ABD67395DB0E05874FF83DDDAB895344ABD0E7111B2DF9E58D76D85AD98106B36295826BE04D435615595605E4B4BB824B33C4AFEB5E7BB0D19F909 -Out = 305164be2141732b4f286464070da6108870c3c36a9e783aa63783c5 - -In = 747CC1A59FEFBA94A9C75BA866C30DC5C1CB0C0F8E9361D98484956DD5D1A40F6184AFBE3DAC9F76028D1CAECCFBF69199C6CE2B4C092A3F4D2A56FE5A33A00757F4D7DEE5DFB0524311A97AE0668A47971B95766E2F6DD48C3F57841F91F04A00AD5EA70F2D479A2620DC5CD78EAAB3A3B011719B7E78D19DDF70D9423798AF77517EBC55392FCD01FC600D8D466B9E7A7A85BF33F9CC5419E9BD874DDFD60981150DDAF8D7FEBAA4374F0872A5628D318000311E2F5655365AD4D407C20E5C04DF17A222E7DEEC79C5AB1116D8572F91CD06E1CCC7CED53736FC867FD49ECEBE6BF8082E8A -Out = c152bc289a4c5c358642d6fc12e7b4f635928b366e76cb134f6c66a2 - -In = 57AF971FCCAEC97435DC2EC9EF0429BCEDC6B647729EA168858A6E49AC1071E706F4A5A645CA14E8C7746D65511620682C906C8B86EC901F3DDED4167B3F00B06CBFAC6AEE3728051B3E5FF10B4F9ED8BD0B8DA94303C833755B3CA3AEDDF0B54BC8D6632138B5D25BAB03D17B3458A9D782108006F5BB7DE75B5C0BA854B423D8BB801E701E99DC4FEAAD59BC1C7112453B04D33EA3635639FB802C73C2B71D58A56BBD671B18FE34ED2E3DCA38827D63FDB1D4FB3285405004B2B3E26081A8FF08CD6D2B08F8E7B7E90A2AB1ED7A41B1D0128522C2F8BFF56A7FE67969422CE839A9D4608F03 -Out = 6f9a3f00739cbef2e04611dce0bf577995a8e0c31012cd3614a15a21 - -In = 04E16DEDC1227902BAAF332D3D08923601BDD64F573FAA1BB7201918CFE16B1E10151DAE875DA0C0D63C59C3DD050C4C6A874011B018421AFC4623AB0381831B2DA2A8BA42C96E4F70864AC44E106F94311051E74C77C1291BF5DB9539E69567BF6A11CF6932BBBAD33F8946BF5814C066D851633D1A513510039B349939BFD42B858C21827C8FF05F1D09B1B0765DC78A135B5CA4DFBA0801BCADDFA175623C8B647EACFB4444B85A44F73890607D06D507A4F8393658788669F6EF4DEB58D08C50CA0756D5E2F49D1A7AD73E0F0B3D3B5F090ACF622B1878C59133E4A848E05153592EA81C6FBF -Out = 92f385222c33fa5b1180c0618e1b1e004b89afa4560859f30b6aed3b - -In = 7C815C384EEE0F288ECE27CCED52A01603127B079C007378BC5D1E6C5E9E6D1C735723ACBBD5801AC49854B2B569D4472D33F40BBB8882956245C366DC3582D71696A97A4E19557E41E54DEE482A14229005F93AFD2C4A7D8614D10A97A9DFA07F7CD946FA45263063DDD29DB8F9E34DB60DAA32684F0072EA2A9426ECEBFA5239FB67F29C18CBAA2AF6ED4BF4283936823AC1790164FEC5457A9CBA7C767CA59392D94CAB7448F50EB34E9A93A80027471CE59736F099C886DEA1AB4CBA4D89F5FC7AE2F21CCD27F611ECA4626B2D08DC22382E92C1EFB2F6AFDC8FDC3D2172604F5035C46B8197D3 -Out = ad155539b87633fbfd23936e9f2be1b90edf5ebb4f2c0ad823b1ed86 - -In = E29D505158DBDD937D9E3D2145658EE6F5992A2FC790F4F608D9CDB44A091D5B94B88E81FAC4FDF5C49442F13B911C55886469629551189EAFF62488F1A479B7DB11A1560E198DDCCCCF50159093425FF7F1CB8D1D1246D0978764087D6BAC257026B090EFAE8CEC5F22B6F21C59ACE1AC7386F5B8837CA6A12B6FBF5534DD0560EF05CA78104D3B943DDB220FEAEC89AA5E692A00F822A2AB9A2FE60350D75E7BE16FF2526DC643872502D01F42F188ABED0A6E9A6F5FD0D1CE7D5755C9FFA66B0AF0B20BD806F08E06156690D81AC811778CA3DAC2C249B96002017FCE93E507E3B953ACF99964B847 -Out = 3564713f91511219542d3501728b440dd539aa7fbac38e499521725f - -In = D85588696F576E65ECA0155F395F0CFACD83F36A99111ED5768DF2D116D2121E32357BA4F54EDE927F189F297D3A97FAD4E9A0F5B41D8D89DD7FE20156799C2B7B6BF9C957BA0D6763F5C3BC5129747BBB53652B49290CFF1C87E2CDF2C4B95D8AAEE09BC8FBFA6883E62D237885810491BFC101F1D8C636E3D0EDE838AD05C207A3DF4FAD76452979EB99F29AFAECEDD1C63B8D36CF378454A1BB67A741C77AC6B6B3F95F4F02B64DABC15438613EA49750DF42EE90101F115AA9ABB9FF64324DDE9DABBB01054E1BD6B4BCDC7930A44C2300D87CA78C06924D0323AD7887E46C90E8C4D100ACD9EED21E -Out = 419c532e640a31a848dc8c201cd5c29a5aed25f4db50be2527b6cbd2 - -In = 3A12F8508B40C32C74492B66323375DCFE49184C78F73179F3314B79E63376B8AC683F5A51F1534BD729B02B04D002F55CBD8E8FC9B5EC1EA6BBE6A0D0E7431518E6BA45D124035F9D3DCE0A8BB7BF1430A9F657E0B4EA9F20EB20C786A58181A1E20A96F1628F8728A13BDF7A4B4B32FC8AA7054CC4881AE7FA19AFA65C6C3EE1B3ADE3192AF42054A8A911B8EC1826865D46D93F1E7C5E2B7813C92A506E53886F3D4701BB93D2A681AD109C845904BB861AF8AF0646B6E399B38B614051D34F6842563A0F37EC00CB3D865FC5D746C4987DE2A65071100883A2A9C7A2BFE1E2DD603D9EA24DC7C5FD06BE -Out = 11bbf19863cf76d242be3c7ec3e0c648f7e385438549508364893409 - -In = 1861EDCE46FA5AD17E1FF1DEAE084DEC580F97D0A67885DFE834B9DFAC1AE076742CE9E267512CA51F6DF5A455AF0C5FD6ABF94ACEA103A3370C354485A7846FB84F3AC7C2904B5B2FBF227002CE512133BB7E1C4E50057BFD1E44DB33C7CDB969A99E284B184F50A14B068A1FC5009D9B298DBE92239572A7627AAC02ABE8F3E3B473417F36D4D2505D16B7577F4526C9D94A270A2DFE450D06DA8F6FA956879A0A55CFE99E742EA555EA477BA3E9B44CCD508C375423611AF92E55345DC215779B2D5119EBA49C71D49B9FE3F1569FA24E5CA3E332D042422A8B8158D3EC66A80012976F31FFDF305F0C9C5E -Out = 074d75def58e309b17d29b2346ba6510400f124edac09b2b3c0bec5f - -In = 08D0FFDE3A6E4EF65608EA672E4830C12943D7187CCFF08F4941CFC13E545F3B9C7AD5EEBBE2B01642B486CAF855C2C73F58C1E4E3391DA8E2D63D96E15FD84953AE5C231911B00AD6050CD7AAFDAAC9B0F663AE6AAB45519D0F5391A541707D479034E73A6AD805AE3598096AF078F1393301493D663DD71F83869CA27BA508B7E91E81E128C1716DC3ACFE3084B2201E04CF8006617EECF1B640474A5D45CFDE9F4D3EF92D6D055B909892194D8A8218DB6D8203A84261D200D71473D7488F3427416B6896C137D455F231071CACBC86E0415AB88AEC841D96B7B8AF41E05BB461A40645BF176601F1E760DE5F -Out = e74992099989a4ce8118935029c87365f2b40a940712474f3ede51d1 - -In = D782ABB72A5BE3392757BE02D3E45BE6E2099D6F000D042C8A543F50ED6EBC055A7F133B0DD8E9BC348536EDCAAE2E12EC18E8837DF7A1B3C87EC46D50C241DEE820FD586197552DC20BEEA50F445A07A38F1768A39E2B2FF05DDDEDF751F1DEF612D2E4D810DAA3A0CC904516F9A43AF660315385178A529E51F8AAE141808C8BC5D7B60CAC26BB984AC1890D0436EF780426C547E94A7B08F01ACBFC4A3825EAE04F520A9016F2FB8BF5165ED12736FC71E36A49A73614739EAA3EC834069B1B40F1350C2B3AB885C02C640B9F7686ED5F99527E41CFCD796FE4C256C9173186C226169FF257954EBDA81C0E5F99 -Out = 6c8845c560e769be5f9e9ebd968669b20db12156ee285dce0ee5a4cc - -In = 5FCE8109A358570E40983E1184E541833BB9091E280F258CFB144387B05D190E431CB19BAA67273BA0C58ABE91308E1844DCD0B3678BAA42F335F2FA05267A0240B3C718A5942B3B3E3BFA98A55C25A1466E8D7A603722CB2BBF03AFA54CD769A99F310735EE5A05DAE2C22D397BD95635F58C48A67F90E1B73AAFCD3F82117F0166657838691005B18DA6F341D6E90FC1CDB352B30FAE45D348294E501B63252DE14740F2B85AE5299DDEC3172DE8B6D0BA219A20A23BB5E10FF434D39DB3F583305E9F5C039D98569E377B75A70AB837D1DF269B8A4B566F40BB91B577455FD3C356C914FA06B9A7CE24C7317A172D -Out = 528c3097cd389ee2fba0c772a5b5e9b36184c53298e236d54ec735b8 - -In = 6172F1971A6E1E4E6170AFBAD95D5FEC99BF69B24B674BC17DD78011615E502DE6F56B86B1A71D3F4348087218AC7B7D09302993BE272E4A591968AEF18A1262D665610D1070EE91CC8DA36E1F841A69A7A682C580E836941D21D909A3AFC1F0B963E1CA5AB193E124A1A53DF1C587470E5881FB54DAE1B0D840F0C8F9D1B04C645BA1041C7D8DBF22030A623AA15638B3D99A2C400FF76F3252079AF88D2B37F35EE66C1AD7801A28D3D388AC450B97D5F0F79E4541755356B3B1A5696B023F39AB7AB5F28DF4202936BC97393B93BC915CB159EA1BD7A0A414CB4B7A1AC3AF68F50D79F0C9C7314E750F7D02FAA58BFA -Out = 467d328b49dab2b3405add621b839bc7d2ef21584cf7629e413c2384 - -In = 5668ECD99DFBE215C4118398AC9C9EAF1A1433FAB4CCDD3968064752B625EA944731F75D48A27D047D67547F14DD0FFAA55FA5E29F7AF0D161D85EAFC4F2029B717C918EAB9D304543290BDBA7158B68020C0BA4E079BC95B5BC0FC044A992B94B4CCD3BD66D0EABB5DBBAB904D62E00752C4E3B0091D773BCF4C14B4377DA3EFFF824B1CB2FA01B32D1E46C909E626ED2DAE920F4C7DBEB635BC754FACBD8D49BEBA3F23C1C41CCBFCD0EE0C114E69737F5597C0BF1D859F0C767E18002AE8E39C26261FFDE2920D3D0BAF0E906138696CFE5B7E32B600F45DF3AAA39932F3A7DF95B60FA8712A2271FCAF3911CE7B511B1 -Out = 94182ca40024f6835f333f281fa8c6ab8fc619444c4ae14a184e813b - -In = 03D625488354DF30E3F875A68EDFCF340E8366A8E1AB67F9D5C5486A96829DFAC0578289082B2A62117E1CF418B43B90E0ADC881FC6AE8105C888E9ECD21AEA1C9AE1A4038DFD17378FED71D02AE492087D7CDCD98F746855227967CB1AB4714261EE3BEAD3F4DB118329D3EBEF4BC48A875C19BA763966DA0EBEA800E01B2F50B00E9DD4CACA6DCB314D00184EF71EA2391D760C950710DB4A70F9212FFC54861F9DC752CE18867B8AD0C48DF8466EF7231E7AC567F0EB55099E622EBB86CB237520190A61C66AD34F1F4E289CB3282AE3EAAC6152ED24D2C92BAE5A7658252A53C49B7B02DFE54FDB2E90074B6CF310AC661 -Out = 79b5c75db50855926373d021af5735b404bb59afd88301bf2b908d3c - -In = 2EDC282FFB90B97118DD03AAA03B145F363905E3CBD2D50ECD692B37BF000185C651D3E9726C690D3773EC1E48510E42B17742B0B0377E7DE6B8F55E00A8A4DB4740CEE6DB0830529DD19617501DC1E9359AA3BCF147E0A76B3AB70C4984C13E339E6806BB35E683AF8527093670859F3D8A0FC7D493BCBA6BB12B5F65E71E705CA5D6C948D66ED3D730B26DB395B3447737C26FAD089AA0AD0E306CB28BF0ACF106F89AF3745F0EC72D534968CCA543CD2CA50C94B1456743254E358C1317C07A07BF2B0ECA438A709367FAFC89A57239028FC5FECFD53B8EF958EF10EE0608B7F5CB9923AD97058EC067700CC746C127A61EE3 -Out = 738c8dba6c9d31d016e8f794b641a80003c457ecb35849cc79d5d8b7 - -In = 90B28A6AA1FE533915BCB8E81ED6CACDC10962B7FF82474F845EEB86977600CF70B07BA8E3796141EE340E3FCE842A38A50AFBE90301A3BDCC591F2E7D9DE53E495525560B908C892439990A2CA2679C5539FFDF636777AD9C1CDEF809CDA9E8DCDB451ABB9E9C17EFA4379ABD24B182BD981CAFC792640A183B61694301D04C5B3EAAD694A6BD4CC06EF5DA8FA23B4FA2A64559C5A68397930079D250C51BCF00E2B16A6C49171433B0AADFD80231276560B80458DD77089B7A1BBCC9E7E4B9F881EACD6C92C4318348A13F4914EB27115A1CFC5D16D7FD94954C3532EFACA2CAB025103B2D02C6FD71DA3A77F417D7932685888A -Out = 8a8723659435a7ed51e71c65def9fa7c510613d7a9bc163ab739a08c - -In = 2969447D175490F2AA9BB055014DBEF2E6854C95F8D60950BFE8C0BE8DE254C26B2D31B9E4DE9C68C9ADF49E4EE9B1C2850967F29F5D08738483B417BB96B2A56F0C8ACA632B552059C59AAC3F61F7B45C966B75F1D9931FF4E596406378CEE91AAA726A3A84C33F37E9CDBE626B5745A0B06064A8A8D56E53AAF102D23DD9DF0A3FDF7A638509A6761A33FA42FA8DDBD8E16159C93008B53765019C3F0E9F10B144CE2AC57F5D7297F9C9949E4FF68B70D339F87501CE8550B772F32C6DA8AD2CE2100A895D8B08FA1EEAD7C376B407709703C510B50F87E73E43F8E7348F87C3832A547EF2BBE5799ABEDCF5E1F372EA809233F006 -Out = 517a8427524efb9a6be662bf533888319a5ff6219f7f71d8d7057b78 - -In = 721645633A44A2C78B19024EAECF58575AB23C27190833C26875DC0F0D50B46AEA9C343D82EA7D5B3E50EC700545C615DAEAEA64726A0F05607576DCD396D812B03FB6551C641087856D050B10E6A4D5577B82A98AFB89CEE8594C9DC19E79FEFF0382FCFD127F1B803A4B9946F4AC9A4378E1E6E041B1389A53E3450CD32D9D2941B0CBABDB50DA8EA2513145164C3AB6BCBD251C448D2D4B087AC57A59C2285D564F16DA4ED5E607ED979592146FFB0EF3F3DB308FB342DF5EB5924A48256FC763141A278814C82D6D6348577545870AE3A83C7230AC02A1540FE1798F7EF09E335A865A2AE0949B21E4F748FB8A51F44750E213A8FB -Out = 13c91e9edb90adba9217a7c570d3e48d1bf7e850652d26b4b99402cb - -In = 6B860D39725A14B498BB714574B4D37CA787404768F64C648B1751B353AC92BAC2C3A28EA909FDF0423336401A02E63EC24325300D823B6864BB701F9D7C7A1F8EC9D0AE3584AA6DD62EA1997CD831B4BABD9A4DA50932D4EFDA745C61E4130890E156AEE6113716DAF95764222A91187DB2EFFEA49D5D0596102D619BD26A616BBFDA8335505FBB0D90B4C180D1A2335B91538E1668F9F9642790B4E55F9CAB0FE2BDD2935D001EE6419ABAB5457880D0DBFF20ED8758F4C20FE759EFB33141CF0E892587FE8187E5FBC57786B7E8B089612C936DFC03D27EFBBE7C8673F1606BD51D5FF386F4A7AB68EDF59F385EB1291F117BFE717399 -Out = 620d083af32d1e75a4d470ec7a5f5683c0e17a1f20e687009d4457bf - -In = 6A01830AF3889A25183244DECB508BD01253D5B508AB490D3124AFBF42626B2E70894E9B562B288D0A2450CFACF14A0DDAE5C04716E5A0082C33981F6037D23D5E045EE1EF2283FB8B6378A914C5D9441627A722C282FF452E25A7EA608D69CEE4393A0725D17963D0342684F255496D8A18C2961145315130549311FC07F0312FB78E6077334F87EAA873BEE8AA95698996EB21375EB2B4EF53C14401207DEB4568398E5DD9A7CF97E8C9663E23334B46912F8344C19EFCF8C2BA6F04325F1A27E062B62A58D0766FC6DB4D2C6A1928604B0175D872D16B7908EBC041761187CC785526C2A3873FEAC3A642BB39F5351550AF9770C328AF7B -Out = fb444ffb4c9a249866507a12942fd580547852274aa6fb1ad18ae562 - -In = B3C5E74B69933C2533106C563B4CA20238F2B6E675E8681E34A389894785BDADE59652D4A73D80A5C85BD454FD1E9FFDAD1C3815F5038E9EF432AAC5C3C4FE840CC370CF86580A6011778BBEDAF511A51B56D1A2EB68394AA299E26DA9ADA6A2F39B9FAFF7FBA457689B9C1A577B2A1E505FDF75C7A0A64B1DF81B3A356001BF0DF4E02A1FC59F651C9D585EC6224BB279C6BEBA2966E8882D68376081B987468E7AED1EF90EBD090AE825795CDCA1B4F09A979C8DFC21A48D8A53CDBB26C4DB547FC06EFE2F9850EDD2685A4661CB4911F165D4B63EF25B87D0A96D3DFF6AB0758999AAD214D07BD4F133A6734FDE445FE474711B69A98F7E2B -Out = 527ab8e54375758a3715b718401a73d861938cac3b41b3287366eb8e - -In = 83AF34279CCB5430FEBEC07A81950D30F4B66F484826AFEE7456F0071A51E1BBC55570B5CC7EC6F9309C17BF5BEFDD7C6BA6E968CF218A2B34BD5CF927AB846E38A40BBD81759E9E33381016A755F699DF35D660007B5EADF292FEEFB735207EBF70B5BD17834F7BFA0E16CB219AD4AF524AB1EA37334AA66435E5D397FC0A065C411EBBCE32C240B90476D307CE802EC82C1C49BC1BEC48C0675EC2A6C6F3ED3E5B741D13437095707C565E10D8A20B8C20468FF9514FCF31B4249CD82DCEE58C0A2AF538B291A87E3390D737191A07484A5D3F3FB8C8F15CE056E5E5F8FEBE5E1FB59D6740980AA06CA8A0C20F5712B4CDE5D032E92AB89F0AE1 -Out = cbe66be6a01ad4ab6a082108e7dd445b79ee6afeb8affb3bf2d9e4ca - -In = A7ED84749CCC56BB1DFBA57119D279D412B8A986886D810F067AF349E8749E9EA746A60B03742636C464FC1EE233ACC52C1983914692B64309EDFDF29F1AB912EC3E8DA074D3F1D231511F5756F0B6EEAD3E89A6A88FE330A10FACE267BFFBFC3E3090C7FD9A850561F363AD75EA881E7244F80FF55802D5EF7A1A4E7B89FCFA80F16DF54D1B056EE637E6964B9E0FFD15B6196BDD7DB270C56B47251485348E49813B4EB9ED122A01B3EA45AD5E1A929DF61D5C0F3E77E1FDC356B63883A60E9CBB9FC3E00C2F32DBD469659883F690C6772E335F617BC33F161D6F6984252EE12E62B6000AC5231E0C9BC65BE223D8DFD94C5004A101AF9FD6C0FB -Out = 0363bca9541020d4045b7c88f57a10a1e9c0d9a1a66eea81f66ae0f9 - -In = A6FE30DCFCDA1A329E82AB50E32B5F50EB25C873C5D2305860A835AECEE6264AA36A47429922C4B8B3AFD00DA16035830EDB897831C4E7B00F2C23FC0B15FDC30D85FB70C30C431C638E1A25B51CAF1D7E8B050B7F89BFB30F59F0F20FECFF3D639ABC4255B3868FC45DD81E47EB12AB40F2AAC735DF5D1DC1AD997CEFC4D836B854CEE9AC02900036F3867FE0D84AFFF37BDE3308C2206C62C4743375094108877C73B87B2546FE05EA137BEDFC06A2796274099A0D554DA8F7D7223A48CBF31B7DECAA1EBC8B145763E3673168C1B1B715C1CD99ECD3DDB238B06049885ECAD9347C2436DFF32C771F34A38587A44A82C5D3D137A03CAA27E66C8FF6 -Out = 28ee87cf342e5a7320762c905d4b60e78fd6aa526f9f52d739bd43e4 - -In = 83167FF53704C3AA19E9FB3303539759C46DD4091A52DDAE9AD86408B69335989E61414BC20AB4D01220E35241EFF5C9522B079FBA597674C8D716FE441E566110B6211531CECCF8FD06BC8E511D00785E57788ED9A1C5C73524F01830D2E1148C92D0EDC97113E3B7B5CD3049627ABDB8B39DD4D6890E0EE91993F92B03354A88F52251C546E64434D9C3D74544F23FB93E5A2D2F1FB15545B4E1367C97335B0291944C8B730AD3D4789273FA44FB98D78A36C3C3764ABEEAC7C569C1E43A352E5B770C3504F87090DEE075A1C4C85C0C39CF421BDCC615F9EFF6CB4FE6468004AECE5F30E1ECC6DB22AD9939BB2B0CCC96521DFBF4AE008B5B46BC006E -Out = a7040164f5423605b981c97e95c347bc38553bab95d33eb33c063f10 - -In = 3A3A819C48EFDE2AD914FBF00E18AB6BC4F14513AB27D0C178A188B61431E7F5623CB66B23346775D386B50E982C493ADBBFC54B9A3CD383382336A1A0B2150A15358F336D03AE18F666C7573D55C4FD181C29E6CCFDE63EA35F0ADF5885CFC0A3D84A2B2E4DD24496DB789E663170CEF74798AA1BBCD4574EA0BBA40489D764B2F83AADC66B148B4A0CD95246C127D5871C4F11418690A5DDF01246A0C80A43C70088B6183639DCFDA4125BD113A8F49EE23ED306FAAC576C3FB0C1E256671D817FC2534A52F5B439F72E424DE376F4C565CCA82307DD9EF76DA5B7C4EB7E085172E328807C02D011FFBF33785378D79DC266F6A5BE6BB0E4A92ECEEBAEB1 -Out = e50badcf93cc87bacece66b1887ff3c6b58377ffe28e6455667c0f76 +# The official BLAKE2 test vectors all uses a key; these are tested +# in mac/blake2bmac.vec +# +# These test vectors were generated by libsodium [BLAKE2b(256)] -In = -Out = 0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8 - -In = CC -Out = e3f0d5af2a41c8fd042a6ec5fef8acfd591903cbb97a3ab666f08e7c39c8a948 - -In = 41FB -Out = a464a6ab07b3205c7d44e07dbd83431c2bcbb67739a611748687820e1473b418 -In = 1F877C -Out = 5868d8802e659afd11b06134af3bde8af8771904d981c670eb5afff39801723e +In = +Out = 0E5751C026E543B2E8AB2EB06099DAA1D1E5DF47778F7787FAAB45CDF12FE3A8 -In = C1ECFDFC -Out = c86d7b42e003fdf7cf2edecaf72ad31dfcd2e760c2ef3c5271f518b50ebf0948 +In = 00 +Out = 03170A2E7597B7B7E3D84C05391D139A62B157E78786D8C082F29DCF4C111314 -In = 21F134AC57 -Out = 07c408f561f4e787c372e2bed3c5e0712974b3779da8bff92c352781620ab9bd +In = 0001 +Out = 01CF79DA4945C370C68B265EF70641AAA65EAA8F5953E3900D97724C2C5AA095 -In = C6F50BB74E29 -Out = 5d506ac9eb30d9e0aff48367c826337d6c580ef0baa65606ead12e12fee007f6 +In = 000102 +Out = 3D8C3D594928271F44AAD7A04B177154806867BCF918E1549C0BC16F9DA2B09B -In = 119713CC83EEEF -Out = 1dc248af1b1ab128599207862440a45d1ddcca8684b04f9318561142f1c3db76 +In = 00010203 +Out = E1EAE5A8ADAE652EC9AF9677346A9D60ECED61E3A0A69BFACF518DB31F86E36B -In = 4A4F202484512526 -Out = 508b1db1b81a82b89f1884847f5c32a85414a4c93e49e976adaa41e4678ec3e8 +In = 0001020304 +Out = 663694AC6520BDCE7CAAB1CF3929FFE78CB2FEA67A3DFC8559753A9F512A0C85 -In = 1F66AB4185ED9B6375 -Out = d1b8b12a6d5c44515b8ce2836a18330072a7cd0b990a86737a44c3303a7dac50 +In = 000102030405 +Out = 274327D0E2A207844988FAC0B39E071422E3F621913D69A5CFEF23B38601A56F -In = EED7422227613B6F53C9 -Out = d47185943d9efb4866a4497a28526a2f95f8d2143bc449d1d76fa7c3d962be98 +In = 00010203040506 +Out = 9DF14B7248764A869197C35E392D2A6D6FDC5B79D597297920FD3F1491B442D2 -In = EAEED5CDFFD89DECE455F1 -Out = 2cd25fe400acfca6f2c4f819904102086ec9b30045b99f067f121dfd40f9b889 +In = 0001020304050607 +Out = 77065D25B622A8251094D869EDF6B4E9BA0708A8DB1F239CB68E4EEB45851621 -In = 5BE43C90F22902E4FE8ED2D3 -Out = a132982e52ce28d1cbc1edb9a49ed360507280b6a6eb3bfe30651254e4e5bce7 +In = 000102030405060708 +Out = 8660231B62CE1D61FC8BE93BD6ACDB43FF61A7AB4CC9494F0CC803362360B07B -In = A746273228122F381C3B46E4F1 -Out = a3f3607784917cffa25842cffadb76a32003886f025c12e326bb570f8cb4c638 +In = 00010203040506070809 +Out = 8B57A796A5D07CB04CC1614DFC2ACB3F73EDC712D7F433619CA3BBE66BB15F49 -In = 3C5871CD619C69A63B540EB5A625 -Out = bc4584fd19429b6c0076cba7241f4e24cc303bada37dc1fecf81d8f794d9a254 +In = 000102030405060708090A +Out = CC932BEE351BE391849C87925F2E00A83051419DC310B288D4304D4ADEA3D0E0 -In = FA22874BCC068879E8EF11A69F0722 -Out = aa95009e7516191a163adfc34d51acd6196cbd5d9fbd25c8130267428d94b066 +In = 000102030405060708090A0B +Out = 99BD72C73BEA193F7040AC279BD656CDEC7FD35E097A657B6C03B4FA967223ED -In = 52A608AB21CCDD8A4457A57EDE782176 -Out = aafcf0ba5f5a4393f484522edf2b3c38d0102afd3de10ea0e8e512c7c7e4052e +In = 000102030405060708090A0B0C +Out = 695E93B723E0A08E8DD8DD4656389363519564DAF4CDE5FE95A6A0CA71D3705E -In = 82E192E4043DDCD12ECF52969D0F807EED -Out = 34292aa01d199af294cb06771fb8d136d983b5fd122eb2d7c838aeaa3204398d +In = 000102030405060708090A0B0C0D +Out = 4CCE7128E4F659BA41EE163C45280D468163ADC8C76C4937A0BBFA0CF3BDEAE7 -In = 75683DCB556140C522543BB6E9098B21A21E -Out = b1b8510092925c1c8eaa87cf43f905585ceac0bd9cb0d242025a3ed70456db16 +In = 000102030405060708090A0B0C0D0E +Out = 929CEC40E9E746E771C6AD05CFCF37641254EF5E802FA71A02F8982F525F2B00 -In = 06E4EFE45035E61FAAF4287B4D8D1F12CA97E5 -Out = a05b0d118ede2b221ea3b403a0dd5b70ca2487def109ff3dca25474cef832271 +In = 000102030405060708090A0B0C0D0E0F +Out = C7CB5D1A1A214F1D833A21FE6C7B2420E417C2F220784CBE90072975131BC367 -In = E26193989D06568FE688E75540AEA06747D9F851 -Out = 2d38b779252c92dac4df25a15cbc1923e46e84235a2da0854e4b8adec1e61202 +In = 000102030405060708090A0B0C0D0E0F10 +Out = CED0CD609F3C8FF85B9CEC93BBA556DBE3CAA996AC5BEB629D4512473D6B31AE -In = D8DC8FDEFBDCE9D44E4CBAFE78447BAE3B5436102A -Out = 1b724c6fb639211014641b87777e5544c20f215a79cca1d1cb43fa24f68f753a +In = 000102030405060708090A0B0C0D0E0F1011 +Out = 584C9CB4DA3BE635D86E803C9EEBCCFD27FBED4AAE27B0207CE3C934A0043AA4 -In = 57085FD7E14216AB102D8317B0CB338A786D5FC32D8F -Out = 5111345d92ca78c6634cee4936d0fa6cdc7647359fb520c4d48d71f1f46ac2c4 +In = 000102030405060708090A0B0C0D0E0F101112 +Out = 9B508680D1F75D5F1E5306FBFAF7E88621CEBF39F7F5CBF9E2DBB7EBB88504D4 -In = A05404DF5DBB57697E2C16FA29DEFAC8AB3560D6126FA0 -Out = 71d45f5d62e020dbdee78e87b2e44f58322c1687c7308302f89c2f46481eeee3 +In = 000102030405060708090A0B0C0D0E0F10111213 +Out = 5D597F201EAD11DAA0687185C579EFD702E288D5BD72B6B21238A4ECB52D288A -In = AECBB02759F7433D6FCB06963C74061CD83B5B3FFA6F13C6 -Out = d0e391fd70bf80f2e90843f841b4bcdad7d26f09be6aee8c789841cc14947dd0 +In = 000102030405060708090A0B0C0D0E0F1011121314 +Out = 85B8AFD95165D04681AB948F2F0545C47A8D11D9D1866CB7ECC88BC31A634891 -In = AAFDC9243D3D4A096558A360CC27C8D862F0BE73DB5E88AA55 -Out = d520bd9d8400e032806bc303392edd20099f9f9fbba345f15b8be8a597d87d39 +In = 000102030405060708090A0B0C0D0E0F101112131415 +Out = 93C541C87FB52D506B1B262E49ED71689A15B745E8F3E003893C8C59CFC669FE -In = 7BC84867F6F9E9FDC3E1046CAE3A52C77ED485860EE260E30B15 -Out = a383833ef172bda3b17cabd684f472df0b27e8bc33c34f075bad0e59219102f1 +In = 000102030405060708090A0B0C0D0E0F10111213141516 +Out = 395D6A5E3B41B6151411B9B22F07FCBAE6C7C30DF59C10CA2DFCFE333AC8E3FB -In = FAC523575A99EC48279A7A459E98FF901918A475034327EFB55843 -Out = 535a39c7f85889353f30bca0ef2dab4c38aa29a4e7bfaa9302bc897887700a63 +In = 000102030405060708090A0B0C0D0E0F1011121314151617 +Out = 8D71AEB3137041D31ED42466EA5FDCA2EC7A35C7701D142CCB813F8C614CECA2 -In = 0F8B2D8FCFD9D68CFFC17CCFB117709B53D26462A3F346FB7C79B85E -Out = ca40772e2225942216b2cc12f52cb6a21ebaf0e184deb624367f076d0ec1c0e2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718 +Out = 3B0B9B4027203DAEB62F4FF868AC6CDD78A5CBBF7664725421A613794702F4F4 -In = A963C3E895FF5A0BE4824400518D81412F875FA50521E26E85EAC90C04 -Out = 803932ffa31dc035a403bbd85ed038139fcb4a9e140195c9177dea6ec22f14c1 +In = 000102030405060708090A0B0C0D0E0F10111213141516171819 +Out = 7384C8812F6803D8649BED21A3ACBBF36239BBD17274D249369DD65E6329FD84 -In = 03A18688B10CC0EDF83ADF0A84808A9718383C4070C6C4F295098699AC2C -Out = c454c20b0686a6fdd4f4dcbbd836f8292045ff4ae95c75d58b54f057bc094835 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A +Out = 3890962F7D604FE0FCADE7D8FC03C7E6285DA2035BAC5A9362C1D68A353D5350 -In = 84FB51B517DF6C5ACCB5D022F8F28DA09B10232D42320FFC32DBECC3835B29 -Out = c30405a54d19d17ad6f4a8b012dcb6a4bb8600fe726a7eb629602e4fe8822071 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B +Out = 9DC0B41D6A6A6C194D04336EB383AC7F4EA537700D5926346DFB1379E9453460 -In = 9F2FCC7C90DE090D6B87CD7E9718C1EA6CB21118FC2D5DE9F97E5DB6AC1E9C10 -Out = 93237359cbd20f0cbca1f67ac89dc68ebd529a7275e6bbdd8549fb576e8b8685 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C +Out = BD78E1C368D70B968E194DCA2C6FBDA605A67D5E52824289E058C93EEE073EF5 -In = DE8F1B3FAA4B7040ED4563C3B8E598253178E87E4D0DF75E4FF2F2DEDD5A0BE046 -Out = 8f57999020ebcf50fc8b6fb381e69a8f7571a593e36beaf7331850ca70df00a2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D +Out = 4CC3E428D63DC132471D3135D406F8D6E30D2480D571BFFBD64957BBB090F582 -In = 62F154EC394D0BC757D045C798C8B87A00E0655D0481A7D2D9FB58D93AEDC676B5A0 -Out = be17d12bd3ee5d011c8ecd5fd482653878f02eb89146d8457c63ecf81462a9c8 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E +Out = 9ADF65B53153B1CAEC84CD717E00E01C2000D0569704CE38D065180ADEE5D964 -In = B2DCFE9FF19E2B23CE7DA2A4207D3E5EC7C6112A8A22AEC9675A886378E14E5BFBAD4E -Out = b5102cb0626c6735e6397135af5b6c9aa582ef9ce97a10c95ddc0d9a7ce27995 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F +Out = CB2F5160FC1F7E05A55EF49D340B48DA2E5A78099D53393351CD579DD42503D6 -In = 47F5697AC8C31409C0868827347A613A3562041C633CF1F1F86865A576E02835ED2C2492 -Out = 59bcec212effb5f69fce4d63fafabba58628f46958c10d39e30fe0122ee52e4d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20 +Out = B7634FE13C7ACA3914EE896E22CFABC9DA5B4F13E72A2CCBECB6D44BBDA95BCC -In = 512A6D292E67ECB2FE486BFE92660953A75484FF4C4F2ECA2B0AF0EDCDD4339C6B2EE4E542 -Out = ea29fe84ba0ccb890af0b2c728ff70ca1467c491503e533c9013f8f20db5280f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021 +Out = 9BADDDEBF24552CB1F66D32990476594E5249A729254F7B5C840728A42749A45 -In = 973CF2B4DCF0BFA872B41194CB05BB4E16760A1840D8343301802576197EC19E2A1493D8F4FB -Out = 6786f4f2898c8a7d36f1eb2d6ed934cb95009d45f23e2a3b5062e379d0ab4856 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122 +Out = 13891B823D3A2CFE0D1A5E60FE89D8C091524F994CDC3241C4DA19C4BB3C2C6B -In = 80BEEBCD2E3F8A9451D4499961C9731AE667CDC24EA020CE3B9AA4BBC0A7F79E30A934467DA4B0 -Out = 3d97e793b18c54a5f78606675fabf0b663f44bcf2d494448d49a0905785ebe8e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223 +Out = C1B7EB8D130D705C5FA9EE8061076A3151F2E36E42D9C9289D85065B9AB343DD -In = 7ABAA12EC2A7347674E444140AE0FB659D08E1C66DECD8D6EAE925FA451D65F3C0308E29446B8ED3 -Out = e18bfbbaa743258a6a83c851bec08491f3ee24b6e484c9337fd17621f3971806 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324 +Out = EE6A288C4FCAE34572FC6DBA3E0B7D698BEF65DCC63BF28EBD74207A2065718A -In = C88DEE9927679B8AF422ABCBACF283B904FF31E1CAC58C7819809F65D5807D46723B20F67BA610C2B7 -Out = f2faef23673cf74fdeda22d334278fe45d5d99c0bce1df0093a36473a8748d53 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425 +Out = 1B5AD5F31E4061F423EE11E3DE88FEF05DFED9393C268FD360D05FE4465FE40A -In = 01E43FE350FCEC450EC9B102053E6B5D56E09896E0DDD9074FE138E6038210270C834CE6EADC2BB86BF6 -Out = 0294142dd3792018031a5ff62d0f5606a9e2711eb9be870803630fa39f583530 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223242526 +Out = 87C40636D0EE94687FDF020E3D165F4E45F21D62FA04AA2B9103A8187DA6E64A -In = 337023370A48B62EE43546F17C4EF2BF8D7ECD1D49F90BAB604B839C2E6E5BD21540D29BA27AB8E309A4B7 -Out = 9e4faa7f496642397d73c76b6e9c93c04111377c37a5b4a40386d1f71ab3f314 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627 +Out = 70A3082DFC7582B9D252939A474338DB1F94A6DCC7724709377797D17FF51AC5 -In = 6892540F964C8C74BD2DB02C0AD884510CB38AFD4438AF31FC912756F3EFEC6B32B58EBC38FC2A6B913596A8 -Out = b9f4b742f917e7e57e62bf9ee84c15e7690eff3ac2a12b43834c6ec7dc34f08f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728 +Out = 109036D1DECE657AC6471F7F7ED33846986FDCB9DAE8A73EA0881607E5E45F13 -In = F5961DFD2B1FFFFDA4FFBF30560C165BFEDAB8CE0BE525845DEB8DC61004B7DB38467205F5DCFB34A2ACFE96C0 -Out = 4187a0c461636c02d363369835b71f7f252f45df3cfd72c27a3c7b65faff0e01 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223242526272829 +Out = DEDA4F3E98E58392ADAEE3C2E7B91D172551C50945A8AE9E9E1AD10C2AE510A8 -In = CA061A2EB6CEED8881CE2057172D869D73A1951E63D57261384B80CEB5451E77B06CF0F5A0EA15CA907EE1C27EBA -Out = 7bcca696598549abdac0e99436e8d1ac5052401af6554fb81db3be81aab9df68 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A +Out = 2A82CABBBB09956D212D182CFAF7CDE2F55FA33F96E3A1AB19FCCFDB668CE2F2 -In = 1743A77251D69242750C4F1140532CD3C33F9B5CCDF7514E8584D4A5F9FBD730BCF84D0D4726364B9BF95AB251D9BB -Out = 439ad6b1dc79f3de8f545aba29c20ef9e0029a633bb225930baac6a033b8d945 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B +Out = 4A21B38C69F755C016EBB4A66EB76B4F9D4087A02FC8C3C257C1183EFEBDA824 -In = D8FABA1F5194C4DB5F176FABFFF856924EF627A37CD08CF55608BBA8F1E324D7C7F157298EABC4DCE7D89CE5162499F9 -Out = e4406b4df4a4569a35bd528c7a976c6a45a7acd86fe639c8c6c7831c89385dbd +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C +Out = D3ECAA4853A092755C3692CAC3FEA5D9CCFACA2D32B59CCAE151705333359E79 -In = BE9684BE70340860373C9C482BA517E899FC81BAAA12E5C6D7727975D1D41BA8BEF788CDB5CF4606C9C1C7F61AED59F97D -Out = cf2c3bc29ea4e541dac3f8be27c1d588b38addbc35fc47efb13d222854162e41 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D +Out = 0AB5250248686AFE6ECA3F3ED89E061C0B3AE2A13454B907BBDB643A72B25A66 -In = 7E15D2B9EA74CA60F66C8DFAB377D9198B7B16DEB6A1BA0EA3C7EE2042F89D3786E779CF053C77785AA9E692F821F14A7F51 -Out = 1dcff872fd9666de9ba2cda1e130785b97522ccc756f3d46a90979938abfed23 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E +Out = 2FB57AB5FC0927E8301B6933325530E90FA7A94EAEA95BAD7C3F2F1052032900 -In = 9A219BE43713BD578015E9FDA66C0F2D83CAC563B776AB9F38F3E4F7EF229CB443304FBA401EFB2BDBD7ECE939102298651C86 -Out = 122fb026c20bcbd250f98092e660e85e5c600cb9eb31d09a2c69932cfe5ef8ac +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F +Out = 48DE881E6C1DC35253D9C8D56E773743640F097BB7274B80EC090F1B33D1DC2E -In = C8F2B693BD0D75EF99CAEBDC22ADF4088A95A3542F637203E283BBC3268780E787D68D28CC3897452F6A22AA8573CCEBF245972A -Out = bc079c1cd80b2a2a7fb2f36d2d3e81401f7c441bcfdc2dcecfc9b2b30a9395d9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30 +Out = 71745158A70425E25A8446122DDA82FCFEB6BFCB593B25D79C539C6B989C526D -In = EC0F99711016C6A2A07AD80D16427506CE6F441059FD269442BAAA28C6CA037B22EEAC49D5D894C0BF66219F2C08E9D0E8AB21DE52 -Out = 7c67e9eb9281e58517728e8e955ef640ef7ef7bc51dc86e6d9dd9b9662bba869 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F3031 +Out = 45D3D95F04F304BE5A61EF38357BD01E61F9BB7F8C9979458D846D9899436167 -In = 0DC45181337CA32A8222FE7A3BF42FC9F89744259CFF653504D6051FE84B1A7FFD20CB47D4696CE212A686BB9BE9A8AB1C697B6D6A33 -Out = 3b8b0b5c83711e3f31568d03b74b0aaab7ffd7c0bacc9cea66ac06ef188604e4 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132 +Out = D5AFEBAD633B7D595E6C8482D8C9A429091F58ACBD84725ADBAC12E8BE80ADA9 -In = DE286BA4206E8B005714F80FB1CDFAEBDE91D29F84603E4A3EBC04686F99A46C9E880B96C574825582E8812A26E5A857FFC6579F63742F -Out = b5f519e714ae8ee83c429fb7d5b4cadccfcb9b2226018fda618ffdb67f3dcc2a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233 +Out = 06BBB3B17EA95E7E00AC21B8632F84615F11456FABCDA9D99CBF079E3134CFE5 -In = EEBCC18057252CBF3F9C070F1A73213356D5D4BC19AC2A411EC8CDEEE7A571E2E20EAF61FD0C33A0FFEB297DDB77A97F0A415347DB66BCAF -Out = 767380e3669d709c576004ba71002adc5826f3e3510bcd0ad0e935e6c8597dfb +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F3031323334 +Out = 02B8E881B5F78B451995F07116AF3549066CBCE498497F546A9772981779D908 -In = 416B5CDC9FE951BD361BD7ABFC120A5054758EBA88FDD68FD84E39D3B09AC25497D36B43CBE7B85A6A3CEBDA8DB4E5549C3EE51BB6FCB6AC1E -Out = 4cdb9071af6cfde4a5705af0df6ac371de4f48afce80fbe3fae692b41b8b37f6 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435 +Out = 370C5EFE99822C30460A123467E8F151F012065BA3639BC0407ED3B3609E5D56 -In = 5C5FAF66F32E0F8311C32E8DA8284A4ED60891A5A7E50FB2956B3CBAA79FC66CA376460E100415401FC2B8518C64502F187EA14BFC9503759705 -Out = 45a4283c0aac0b8ef6aeea077a949a5db452e7f2dd1c24017015f4fcc9b8601e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233343536 +Out = ACE7DB9E8F298E823B7B265BBCD004577E0029256C48684B2D7A7CDFCBFF5E47 -In = 7167E1E02BE1A7CA69D788666F823AE4EEF39271F3C26A5CF7CEE05BCA83161066DC2E217B330DF821103799DF6D74810EED363ADC4AB99F36046A -Out = 07179dfb3c9bf77bb204995255379b41bdded4a81edc5acbab07b6bd0f4145bb +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F3031323334353637 +Out = A6C1D35F74218E57B4DBD4ED467B41981208666738FFA0D9DC53D3DE96BE702A -In = 2FDA311DBBA27321C5329510FAE6948F03210B76D43E7448D1689A063877B6D14C4F6D0EAA96C150051371F7DD8A4119F7DA5C483CC3E6723C01FB7D -Out = 73e1b3b61296c03de773c64f5da7d603f6a93ba266b0fa7a227dbc0e255f1064 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738 +Out = 25E44457F063AE4EA799502C38D66EEEFB46F520B4FC9A298DF9826C9D62EEA9 -In = 95D1474A5AAB5D2422ACA6E481187833A6212BD2D0F91451A67DD786DFC91DFED51B35F47E1DEB8A8AB4B9CB67B70179CC26F553AE7B569969CE151B8D -Out = 310ee5215c969b054949535e1f17c45ccdde886ed7c0f6ab63c589a0a2222d08 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233343536373839 +Out = 1B35B64CF659D6D7D0CA933C9A52FB0E67FBA76A304FADB7C47DD8FF6B6FF0FA -In = C71BD7941F41DF044A2927A8FF55B4B467C33D089F0988AA253D294ADDBDB32530C0D4208B10D9959823F0C0F0734684006DF79F7099870F6BF53211A88D -Out = 52ed4708782f773201a7c5bb56422884bef29fdcb0e878295aceecd11a1c7306 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A +Out = A72DE68FF63BBE9CCFACCFA6859AF660BB413F9E5D0200106100919C10301EF8 -In = F57C64006D9EA761892E145C99DF1B24640883DA79D9ED5262859DCDA8C3C32E05B03D984F1AB4A230242AB6B78D368DC5AAA1E6D3498D53371E84B0C1D4BA -Out = 1b916fee456761126bd218ce7eef47c02e85bc8df9d239c1902f3b62573107bd +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B +Out = CFE37AB9C02BF84AFA7A734A10317150479B791A27EFC374DD669F4EF67A801D -In = E926AE8B0AF6E53176DBFFCC2A6B88C6BD765F939D3D178A9BDE9EF3AA131C61E31C1E42CDFAF4B4DCDE579A37E150EFBEF5555B4C1CB40439D835A724E2FAE7 -Out = 7ef5cfe1a98d19888586d942a1a4a15797f56b1846b29d06222174fec757e132 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C +Out = A55A2C06F3DB74C95B33D1E962DA18772C3601EB13FE0B86A78667B2B9F9DF86 -In = 16E8B3D8F988E9BB04DE9C96F2627811C973CE4A5296B4772CA3EEFEB80A652BDF21F50DF79F32DB23F9F73D393B2D57D9A0297F7A2F2E79CFDA39FA393DF1AC00 -Out = 58c767cd6c1ff8f37468ba77487a3a286d055cf598a9246955c07c7166a17e25 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D +Out = 1B8A9195724AC01AD7DA4A76C2545D2F3DC223A0242537050D7395F588748079 -In = FC424EEB27C18A11C01F39C555D8B78A805B88DBA1DC2A42ED5E2C0EC737FF68B2456D80EB85E11714FA3F8EABFB906D3C17964CB4F5E76B29C1765DB03D91BE37FC -Out = e0d0a13753afbcf39958c080ee42985d11c7546dca9a21e33a64de4ac63ccdba +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E +Out = 29E41A64FBDD2FD27612228623C0702222BF367451E7324287F181CB3DCF7237 -In = ABE3472B54E72734BDBA7D9158736464251C4F21B33FBBC92D7FAC9A35C4E3322FF01D2380CBAA4EF8FB07D21A2128B7B9F5B6D9F34E13F39C7FFC2E72E47888599BA5 -Out = 71ce286655dc4b00b2840748ae4c3c5493cb7ead3d4c1ad72e564a310c65f420 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F +Out = 10D8E6D534B00939843FE9DCC4DAE48CDF008F6B8B2B82B156F5404D874887F5 -In = 36F9F0A65F2CA498D739B944D6EFF3DA5EBBA57E7D9C41598A2B0E4380F3CF4B479EC2348D015FFE6256273511154AFCF3B4B4BF09D6C4744FDD0F62D75079D440706B05 -Out = 1a3f414efbada323578d0d16d09419fd1bd47e7637526aab9bbdab77c825bfa9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40 +Out = 84C04AB082C8AE24206561F77397704B627892089A05887A2A1996472BCFE15D -In = ABC87763CAE1CA98BD8C5B82CABA54AC83286F87E9610128AE4DE68AC95DF5E329C360717BD349F26B872528492CA7C94C2C1E1EF56B74DBB65C2AC351981FDB31D06C77A4 -Out = b5e7c3433208c457c6ed4cd4dfaceb33ee81b99de57036f0e82fbf095409ec4f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F4041 +Out = 2B684BABA4A7245E38C3263D85B5524A51BAA8CA18444AF1B5958596BC30D424 -In = 94F7CA8E1A54234C6D53CC734BB3D3150C8BA8C5F880EAB8D25FED13793A9701EBE320509286FD8E422E931D99C98DA4DF7E70AE447BAB8CFFD92382D8A77760A259FC4FBD72 -Out = 0fb8a6e1a1acd7db33fe05353fe11d5a58cb710cb529f4ddf51b37df2ae50f8b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142 +Out = 1D59FA9501213440975A0B27E4B52EEA7274F8794EC35B8EE4824F7B00897ED7 -In = 13BD2811F6ED2B6F04FF3895ACEED7BEF8DCD45EB121791BC194A0F806206BFFC3B9281C2B308B1A729CE008119DD3066E9378ACDCC50A98A82E20738800B6CDDBE5FE9694AD6D -Out = c2c92829f2d80d3cb807a533d6c418026034d2b4c14f9afc9d2340b1db4d7da9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40414243 +Out = 60669608711B9DF3715B631B474D5206179A81D054EF5494612899728B55E103 -In = 1EED9CBA179A009EC2EC5508773DD305477CA117E6D569E66B5F64C6BC64801CE25A8424CE4A26D575B8A6FB10EAD3FD1992EDDDEEC2EBE7150DC98F63ADC3237EF57B91397AA8A7 -Out = a62d20b74e48226a398771c4e530f2d6c281eadbe6baca476e999264eb4c132f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F4041424344 +Out = 46060CB9DC6BE177BAFE5425C4F84F9143B71CFC75DD958E15FF7A26CC92859B -In = BA5B67B5EC3A3FFAE2C19DD8176A2EF75C0CD903725D45C9CB7009A900C0B0CA7A2967A95AE68269A6DBF8466C7B6844A1D608AC661F7EFF00538E323DB5F2C644B78B2D48DE1A08AA -Out = c4c577488c7bd1d698ecec0d60dae8745b4a098e337442deed68949cd02e7e3e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445 +Out = E7F909C8E018BC36B59A8E7848D16C05D6BDB0EBB91FB9BAF54CC328192CC362 -In = 0EFA26AC5673167DCACAB860932ED612F65FF49B80FA9AE65465E5542CB62075DF1C5AE54FBA4DB807BE25B070033EFA223BDD5B1D3C94C6E1909C02B620D4B1B3A6C9FED24D70749604 -Out = e8926b27f61a8589be7fc3547fde36380932af213eb97bf3cefaae759e04a0d2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40414243444546 +Out = 7A9F75439C68869A6595EB63C61DB2BC213D3EF067EC0CB3F25E02E554FCE2A9 -In = BBFD933D1FD7BF594AC7F435277DC17D8D5A5B8E4D13D96D2F64E771ABBD51A5A8AEA741BECCBDDB177BCEA05243EBD003CFDEAE877CCA4DA94605B67691919D8B033F77D384CA01593C1B -Out = 0acac5a021d54b0f45ec644113f20744c7cd49945cd031192378732f15736a7e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F4041424344454647 +Out = ADB39B3E53A791DFD32ECA83191EB82FDEF4321AC504539B257C3376DA240ACA -In = 90078999FD3C35B8AFBF4066CBDE335891365F0FC75C1286CDD88FA51FAB94F9B8DEF7C9AC582A5DBCD95817AFB7D1B48F63704E19C2BAA4DF347F48D4A6D603013C23F1E9611D595EBAC37C -Out = c81d70e306be8d231443a7b076c3d8d6925c8e8d855d600d8c65528ddbbd1d2d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748 +Out = 240CA19B3671DCC5EF174331FB0DD24B845DFCD01B0FBE52FA72A29BCAEF1373 -In = 64105ECA863515C20E7CFBAA0A0B8809046164F374D691CDBD6508AAABC1819F9AC84B52BAFC1B0FE7CDDBC554B608C01C8904C669D8DB316A0953A4C68ECE324EC5A49FFDB59A1BD6A292AA0E -Out = c176e6a4388514cce135abab6d92d5e56a38599f0f363f7288a952dd45d910ee +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40414243444546474849 +Out = EBAE0805FD52D3E9F5F29AEB33B6BE8CAB0F28E668990D3CB95444D9EF90B932 -In = D4654BE288B9F3B711C2D02015978A8CC57471D5680A092AA534F7372C71CEAAB725A383C4FCF4D8DEAA57FCA3CE056F312961ECCF9B86F14981BA5BED6AB5B4498E1F6C82C6CAE6FC14845B3C8A -Out = b29bc33b06c5b2b9d9847d788ce32b088b6bd5fe70f360c8635db660e5444800 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A +Out = EA7C3C052928F0FA3B8D86B19C87DEE905E8A4B26A0A23B3C8E8DC7255EA82D6 -In = 12D9394888305AC96E65F2BF0E1B18C29C90FE9D714DD59F651F52B88B3008C588435548066EA2FC4C101118C91F32556224A540DE6EFDDBCA296EF1FB00341F5B01FECFC146BDB251B3BDAD556CD2 -Out = af2eabefc30dd72e687f199f3e944ea7e2dcccd9686288601ad6f6b4d6fb8713 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B +Out = 4AD32C1F2D18C8B7DC29D1526D7C751B89B86882FB12AA3CC9C6EDDB7991D266 -In = 871A0D7A5F36C3DA1DFCE57ACD8AB8487C274FAD336BC137EBD6FF4658B547C1DCFAB65F037AA58F35EF16AFF4ABE77BA61F65826F7BE681B5B6D5A1EA8085E2AE9CD5CF0991878A311B549A6D6AF230 -Out = d315482850cb31ea68152970899dacdd827d2341c9cce2ba983486e8f067e496 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C +Out = C61F81C3E6B899D0501B05DF1DE32099A7B0E878689DC2A3FD5583DE90A74164 -In = E90B4FFEF4D457BC7711FF4AA72231CA25AF6B2E206F8BF859D8758B89A7CD36105DB2538D06DA83BAD5F663BA11A5F6F61F236FD5F8D53C5E89F183A3CEC615B50C7C681E773D109FF7491B5CC22296C5 -Out = 4cfb36b4bc783ba8c7b82cc3829a704cfa36427a8c8347261ff3573018b7f4ab +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D +Out = 068A8A0B96076A2773A8E00E00E57EEF4FB26886B521285A6C747130850792D7 -In = E728DE62D75856500C4C77A428612CD804F30C3F10D36FB219C5CA0AA30726AB190E5F3F279E0733D77E7267C17BE27D21650A9A4D1E32F649627638DBADA9702C7CA303269ED14014B2F3CF8B894EAC8554 -Out = ee488a95d5650dc01ca7d60377610b76dd95fbb33fb4efd788e96f9cdbcb3ae0 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E +Out = EFAB0151162523B18147CBA89679BDAB2D9B670F5AE222A34C360C094AF1D441 -In = 6348F229E7B1DF3B770C77544E5166E081850FA1C6C88169DB74C76E42EB983FACB276AD6A0D1FA7B50D3E3B6FCD799EC97470920A7ABED47D288FF883E24CA21C7F8016B93BB9B9E078BDB9703D2B781B616E -Out = 970daaee43de3f19f05ca2d875455ea77a35ec897db60d9d3a44f743e84fbb29 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F +Out = 066DE1009DACA2B8390A9DC734BCE547AC4E3CC4531645BB8B9CBC0070941D88 -In = 4B127FDE5DE733A1680C2790363627E63AC8A3F1B4707D982CAEA258655D9BF18F89AFE54127482BA01E08845594B671306A025C9A5C5B6F93B0A39522DC877437BE5C2436CBF300CE7AB6747934FCFC30AEAAF6 -Out = f07fde60e8144c48812c4c23e0e8c2b4f50ddda401674031c05247f5389137a2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50 +Out = 9FBB33B95E79C5C1683AB09A6ABFF6612FFFB4458543DC8BBC7723A6DC2BF2ED -In = 08461F006CFF4CC64B752C957287E5A0FAABC05C9BFF89D23FD902D324C79903B48FCB8F8F4B01F3E4DDB483593D25F000386698F5ADE7FAADE9615FDC50D32785EA51D49894E45BAA3DC707E224688C6408B68B11 -Out = f221c50c415dca6944c9ef2f6929afc6378d850b94446628f694a9caa6be0e4c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F5051 +Out = 88FEA89237618CFC0270CBCD08E7FFDAA8933607C0DBCCB8DD075B84FBA83B11 -In = 68C8F8849B120E6E0C9969A5866AF591A829B92F33CD9A4A3196957A148C49138E1E2F5C7619A6D5EDEBE995ACD81EC8BB9C7B9CFCA678D081EA9E25A75D39DB04E18D475920CE828B94E72241F24DB72546B352A0E4 -Out = 42834303ceb44f62e994927206770ffe805de02561375f10d95d88dec7474b4d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152 +Out = B1960BEAA4FED01453679C7D6CF78D25442BDF92AE51BE479DAB18E1B2B922D3 -In = B8D56472954E31FB54E28FCA743F84D8DC34891CB564C64B08F7B71636DEBD64CA1EDBDBA7FC5C3E40049CE982BBA8C7E0703034E331384695E9DE76B5104F2FBC4535ECBEEBC33BC27F29F18F6F27E8023B0FBB6F563C -Out = 475f44d41c2917fd4ca4d507be217799877bf7aeb4aea3dd0311eec58f173bd1 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50515253 +Out = A7D6821A97BC57CF6B5E25953C689439BEDA79364C903A3E64B6671DC9ECEB2F -In = 0D58AC665FA84342E60CEFEE31B1A4EACDB092F122DFC68309077AED1F3E528F578859EE9E4CEFB4A728E946324927B675CD4F4AC84F64DB3DACFE850C1DD18744C74CECCD9FE4DC214085108F404EAB6D8F452B5442A47D -Out = 68c56f207afdf31f33c38457be41060fee65e68dcc38ba6b3494ba747f583cef +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F5051525354 +Out = 90CA93ECD3E0E7C839E4FC0F2E4748954A89B2C7338E55FC18A53CCD08ABED2E -In = 1755E2D2E5D1C1B0156456B539753FF416651D44698E87002DCF61DCFA2B4E72F264D9AD591DF1FDEE7B41B2EB00283C5AEBB3411323B672EAA145C5125185104F20F335804B02325B6DEA65603F349F4D5D8B782DD3469CCD -Out = 5632b5a25f4cbebf21124dd5295332e211b07c57e46fae098585fa003ab66985 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455 +Out = 58FE5F45B9915A5B177F4256B51CE5781662352A2DD620A946B8755213FBD61F -In = B180DE1A611111EE7584BA2C4B020598CD574AC77E404E853D15A101C6F5A2E5C801D7D85DC95286A1804C870BB9F00FD4DCB03AA8328275158819DCAD7253F3E3D237AEAA7979268A5DB1C6CE08A9EC7C2579783C8AFC1F91A7 -Out = 56566c8dd254f1095518d0f2cb1ffc3e3e9d75c7fc95443cb21624d49b895b51 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50515253545556 +Out = D109C8EC77E2253B5E7C761F59B184815E46B06CC5132A92511A93DEAD6315C2 -In = CF3583CBDFD4CBC17063B1E7D90B02F0E6E2EE05F99D77E24E560392535E47E05077157F96813544A17046914F9EFB64762A23CF7A49FE52A0A4C01C630CFE8727B81FB99A89FF7CC11DCA5173057E0417B8FE7A9EFBA6D95C555F -Out = 8d28231fd63ea9968868baec5687b376e2e1ad607bd3c6b69fe880898392fa38 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F5051525354555657 +Out = FD9664ECB814785C8474188706E6AB0952925F9B9D8E351665CED12E84D92FAD -In = 072FC02340EF99115BAD72F92C01E4C093B9599F6CFC45CB380EE686CB5EB019E806AB9BD55E634AB10AA62A9510CC0672CD3EDDB589C7DF2B67FCD3329F61B1A4441ECA87A33C8F55DA4FBBAD5CF2B2527B8E983BB31A2FADEC7523 -Out = 2150245ddaba054a9643edc9e4767c2d07645f7e2007ccea16c8e91611b94bd8 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758 +Out = 81342700C48F41EC5B54180D560E73E5AD6782717B2D3320B42280BE11F0873E -In = 76EECF956A52649F877528146DE33DF249CD800E21830F65E90F0F25CA9D6540FDE40603230ECA6760F1139C7F268DEBA2060631EEA92B1FFF05F93FD5572FBE29579ECD48BC3A8D6C2EB4A6B26E38D6C5FBF2C08044AEEA470A8F2F26 -Out = f5b7ca757e953c51316c507176a6bf03d9341066f3f413e8045b0cf36fd50817 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50515253545556575859 +Out = 523CF9A356076FE8C3538BDD752BC43712C7D28F29499AAAE7812F1E4167840E -In = 7ADC0B6693E61C269F278E6944A5A2D8300981E40022F839AC644387BFAC9086650085C2CDC585FEA47B9D2E52D65A2B29A7DC370401EF5D60DD0D21F9E2B90FAE919319B14B8C5565B0423CEFB827D5F1203302A9D01523498A4DB10374 -Out = 9631af35c804c5fc09160b7d49eaffb826ebcfddd8c210e1901ada23cdac03a7 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A +Out = 9706D70E28A9E99DB15974B1C01635868A62C6567EB40903ADAE58D417B882F2 -In = E1FFFA9826CCE8B86BCCEFB8794E48C46CDF372013F782ECED1E378269B7BE2B7BF51374092261AE120E822BE685F2E7A83664BCFBE38FE8633F24E633FFE1988E1BC5ACF59A587079A57A910BDA60060E85B5F5B6F776F0529639D9CCE4BD -Out = 4fb2d2033f21f8bf2be0039fadf078b95e42dcccc9d60efa184b85733ce0b233 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B +Out = 2216F01255FF24422E18D906C64506924940451E09D4EC17E4DBC8EA6D14EF59 -In = 69F9ABBA65592EE01DB4DCE52DBAB90B08FC04193602792EE4DAA263033D59081587B09BBE49D0B49C9825D22840B2FF5D9C5155F975F8F2C2E7A90C75D2E4A8040FE39F63BBAFB403D9E28CC3B86E04E394A9C9E8065BD3C85FA9F0C7891600 -Out = 2401fc50eea0b566ee020559f756484fea01fb89729634e6b1b8c6fed323836f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C +Out = 64AD22E77FC9F8A03AD6E06A067ADD9F0E0360D4533014FA286CCDF40DCEB231 -In = 38A10A352CA5AEDFA8E19C64787D8E9C3A75DBF3B8674BFAB29B5DBFC15A63D10FAE66CD1A6E6D2452D557967EAAD89A4C98449787B0B3164CA5B717A93F24EB0B506CEB70CBBCB8D72B2A72993F909AAD92F044E0B5A2C9AC9CB16A0CA2F81F49 -Out = 88da26ab49a0630a18131139cd108edfe8982908a38b29dd4d48ebf67a3bf3ff +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D +Out = 9908418A2BB564AB9607D2F863B841DD4FE47EE370DDE05A9368E400F7F9904B -In = 6D8C6E449BC13634F115749C248C17CD148B72157A2C37BF8969EA83B4D6BA8C0EE2711C28EE11495F43049596520CE436004B026B6C1F7292B9C436B055CBB72D530D860D1276A1502A5140E3C3F54A93663E4D20EDEC32D284E25564F624955B52 -Out = 50dc52601eda2fa399f0714024ffe5c7e8421e79bd1e558e7ee0fd8cdfe90bf0 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E +Out = 981A7989084E74F6D5FD8870321E860991729EED88E60D85AB5FDAA769BE6FD2 -In = 6EFCBCAF451C129DBE00B9CEF0C3749D3EE9D41C7BD500ADE40CDC65DEDBBBADB885A5B14B32A0C0D087825201E303288A733842FA7E599C0C514E078F05C821C7A4498B01C40032E9F1872A1C925FA17CE253E8935E4C3C71282242CB716B2089CCC1 -Out = fcd265721e75338e35672231d1aa569e6eebf43b07e73cd4a125e3991722cf5a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F +Out = 6528EA458EFD23391E968E0DD3A40202AC94E3854D1A4642CBBE0D13A15CB849 -In = 433C5303131624C0021D868A30825475E8D0BD3052A022180398F4CA4423B98214B6BEAAC21C8807A2C33F8C93BD42B092CC1B06CEDF3224D5ED1EC29784444F22E08A55AA58542B524B02CD3D5D5F6907AFE71C5D7462224A3F9D9E53E7E0846DCBB4CE -Out = 73a5c82c4cdb988f670836c2986c59749d93d231d22a61489acf3be5d41031af +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60 +Out = 767FDCD43CF16CBF980FD560DFC55F67BE20510F9A0C8FCD5C749DA8EF23FD7B -In = A873E0C67CA639026B6683008F7AA6324D4979550E9BCE064CA1E1FB97A30B147A24F3F666C0A72D71348EDE701CF2D17E2253C34D1EC3B647DBCEF2F879F4EB881C4830B791378C901EB725EA5C172316C6D606E0AF7DF4DF7F76E490CD30B2BADF45685F -Out = 5b77cc2446775cbadec1400fdda0c53b4093dc9e683c1d1248462fda74a3a835 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6061 +Out = 90AFC4E366BCEE748591D93BA9F7C2E05A1FDA261B58D094F8F0450C8A31FFB8 -In = 006917B64F9DCDF1D2D87C8A6173B64F6587168E80FAA80F82D84F60301E561E312D9FBCE62F39A6FB476E01E925F26BCC91DE621449BE6504C504830AAE394096C8FC7694651051365D4EE9070101EC9B68086F2EA8F8AB7B811EA8AD934D5C9B62C60A4771 -Out = e8d22d20a8f3d147f5bcf11670aa8be226292a6d880e01a3e4b4d765a9c63d21 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162 +Out = 660F9F5D430BF89F5946FD4649AD41F806937641C808A80DA70660F53179A941 -In = F13C972C52CB3CC4A4DF28C97F2DF11CE089B815466BE88863243EB318C2ADB1A417CB1041308598541720197B9B1CB5BA2318BD5574D1DF2174AF14884149BA9B2F446D609DF240CE335599957B8EC80876D9A085AE084907BC5961B20BF5F6CA58D5DAB38ADB -Out = 45211376760c5e2646d1bf3aac7bd03c95e577e95a05659d2cbda004b6f2a9c6 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60616263 +Out = 5AC86383DEC1DB602FDBC2C978C3FE1BF4328FEA1E1B495B68BE2C3B67BA033B -In = E35780EB9799AD4C77535D4DDB683CF33EF367715327CF4C4A58ED9CBDCDD486F669F80189D549A9364FA82A51A52654EC721BB3AAB95DCEB4A86A6AFA93826DB923517E928F33E3FBA850D45660EF83B9876ACCAFA2A9987A254B137C6E140A21691E1069413848 -Out = 92627e5a262be427b123dd488a06571b4d067934f3b5a4d9adc43d1624e9556c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6061626364 +Out = 7C15CDF3705457EB6B8EDD79FB75BF568692CCB778F85FA2E1D462F48EE55C4F -In = 64EC021C9585E01FFE6D31BB50D44C79B6993D72678163DB474947A053674619D158016ADB243F5C8D50AA92F50AB36E579FF2DABB780A2B529370DAA299207CFBCDD3A9A25006D19C4F1FE33E4B1EAEC315D8C6EE1E730623FD1941875B924EB57D6D0C2EDC4E78D6 -Out = 965dcf3c955d4608a2556b43ac1bdd2061ceadd5b32a24b56d0f341ef6c0743a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465 +Out = A8DF989766FC245EDBB4B7268AFB7380E4EA3F7123802B72AE2ADB938211C9F9 -In = 5954BAB512CF327D66B5D9F296180080402624AD7628506B555EEA8382562324CF452FBA4A2130DE3E165D11831A270D9CB97CE8C2D32A96F50D71600BB4CA268CF98E90D6496B0A6619A5A8C63DB6D8A0634DFC6C7EC8EA9C006B6C456F1B20CD19E781AF20454AC880 -Out = 291780e51d780aa9487dde0d3962594fba7edd264ecdf3150ef4c339bee941ff +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60616263646566 +Out = 707AA875C6162027AC2829894C0BE750F5EE8FE8A64465080025F708DC200F0E -In = 03D9F92B2C565709A568724A0AFF90F8F347F43B02338F94A03ED32E6F33666FF5802DA4C81BDCE0D0E86C04AFD4EDC2FC8B4141C2975B6F07639B1994C973D9A9AFCE3D9D365862003498513BFA166D2629E314D97441667B007414E739D7FEBF0FE3C32C17AA188A8683 -Out = 54a38b82637fca03a45625a52f2cb545fc500c706d273e8b42f1d6370a19ed00 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6061626364656667 +Out = 6EFD41538FEC50459B6C2583ADE5754C86617580EEF3071D38723AAF743F93C7 -In = F31E8B4F9E0621D531D22A380BE5D9ABD56FAEC53CBD39B1FAB230EA67184440E5B1D15457BD25F56204FA917FA48E669016CB48C1FFC1E1E45274B3B47379E00A43843CF8601A5551411EC12503E5AAC43D8676A1B2297EC7A0800DBFEE04292E937F21C005F17411473041 -Out = d9bf619c5bf263c5d1a0dba92584c9bafa2223349997cb6d4dca316373a0c10d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768 +Out = 5C22D6A56A4FBDB85AA70994B4E118224BB6DB2AFD017A98C2B992CE26EA8925 -In = 758EA3FEA738973DB0B8BE7E599BBEF4519373D6E6DCD7195EA885FC991D896762992759C2A09002912FB08E0CB5B76F49162AEB8CF87B172CF3AD190253DF612F77B1F0C532E3B5FC99C2D31F8F65011695A087A35EE4EEE5E334C369D8EE5D29F695815D866DA99DF3F79403 -Out = 6532f91edbe1cb613b5d66e2e8a913df356f535ccd02f771cc095bfe1aafde8b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60616263646566676869 +Out = A4A9739A1C83135BAB3334990B470164ED81F633D7FE79722B3AC6E65DADD38E -In = 47C6E0C2B74948465921868804F0F7BD50DD323583DC784F998A93CD1CA4C6EF84D41DC81C2C40F34B5BEE6A93867B3BDBA0052C5F59E6F3657918C382E771D33109122CC8BB0E1E53C4E3D13B43CE44970F5E0C079D2AD7D7A3549CD75760C21BB15B447589E86E8D76B1E9CED2 -Out = 72b91168003912d77cfa8be4bd81a96318bb6a4e692d64021dd9eaad95b8840d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A +Out = F1AC1AD3348EA6C949FDE09CBEE706EA0AECF3A93FC51A8A7E2BDB8CD7400B01 -In = F690A132AB46B28EDFA6479283D6444E371C6459108AFD9C35DBD235E0B6B6FF4C4EA58E7554BD002460433B2164CA51E868F7947D7D7A0D792E4ABF0BE5F450853CC40D85485B2B8857EA31B5EA6E4CCFA2F3A7EF3380066D7D8979FDAC618AAD3D7E886DEA4F005AE4AD05E5065F -Out = 1295ce0ff1f581911cfc517bf6eb5257828d805b7cdef76be1dee45e73faeccd +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B +Out = 492D9EAE92F27ECCE118D3FD8DF63158CF709ED5069502263B8BE1C105196EA3 -In = 58D6A99BC6458824B256916770A8417040721CCCFD4B79EACD8B65A3767CE5BA7E74104C985AC56B8CC9AEBD16FEBD4CDA5ADB130B0FF2329CC8D611EB14DAC268A2F9E633C99DE33997FEA41C52A7C5E1317D5B5DAED35EBA7D5A60E45D1FA7EAABC35F5C2B0A0F2379231953322C4E -Out = 9b55d2232cde7157e6a82ec7bc71b6127fd91b32b86168f0384b87511b06d06e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C +Out = 8691C32810F3220C0DF76816AF0A73B3FADE2594CBCC711B855E2CD81DBDEC95 -In = BEFAB574396D7F8B6705E2D5B58B2C1C820BB24E3F4BAE3E8FBCD36DBF734EE14E5D6AB972AEDD3540235466E825850EE4C512EA9795ABFD33F330D9FD7F79E62BBB63A6EA85DE15BEAEEA6F8D204A28956059E2632D11861DFB0E65BC07AC8A159388D5C3277E227286F65FF5E5B5AEC1 -Out = 5b593d68dde30a2a12e155d709f6f0e22a2027ee48b395c30daa5ba9a437516c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D +Out = CAB341F7059D974CBC620BC423B02F58B5E1A899F416B0858AE1A736D4681162 -In = 8E58144FA9179D686478622CE450C748260C95D1BA43B8F9B59ABECA8D93488DA73463EF40198B4D16FB0B0707201347E0506FF19D01BEA0F42B8AF9E71A1F1BD168781069D4D338FDEF00BF419FBB003031DF671F4A37979564F69282DE9C65407847DD0DA505AB1641C02DEA4F0D834986 -Out = 0487dbaacd52f5a7071057af915eed49e4f9d62e208910bab55be369b947c1ef +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E +Out = E08B3D16F0A3B663F319DA999DB897488D1677048A38FA7B4F1F783E4F1974E6 -In = B55C10EAE0EC684C16D13463F29291BF26C82E2FA0422A99C71DB4AF14DD9C7F33EDA52FD73D017CC0F2DBE734D831F0D820D06D5F89DACC485739144F8CFD4799223B1AFF9031A105CB6A029BA71E6E5867D85A554991C38DF3C9EF8C1E1E9A7630BE61CAABCA69280C399C1FB7A12D12AEFC -Out = b10c588da5a0d784c530f0da2f8080fac6b88e0ae0b5f6b41ee72b328bbef39e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F +Out = D5587E0B33166A320DD69417ADB01CB1129AFF5506A2F655574CF2E264636BDB -In = 2EEEA693F585F4ED6F6F8865BBAE47A6908AECD7C429E4BEC4F0DE1D0CA0183FA201A0CB14A529B7D7AC0E6FF6607A3243EE9FB11BCF3E2304FE75FFCDDD6C5C2E2A4CD45F63C962D010645058D36571404A6D2B4F44755434D76998E83409C3205AA1615DB44057DB991231D2CB42624574F545 -Out = 219780cc657d6c702af614da677cec8725b5c2c9e6dd37264bdaa532d8a8af30 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70 +Out = 5A69194C22AF2B7040A8488738F96C901AE215965D611A572932464261539FC8 -In = DAB11DC0B047DB0420A585F56C42D93175562852428499F66A0DB811FCDDDAB2F7CDFFED1543E5FB72110B64686BC7B6887A538AD44C050F1E42631BC4EC8A9F2A047163D822A38989EE4AAB01B4C1F161B062D873B1CFA388FD301514F62224157B9BEF423C7783B7AAC8D30D65CD1BBA8D689C2D -Out = 791fabca399168cedeebe404ae232c75db5f1be23b45792df975a8ea3ad28a0d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F7071 +Out = D8E7D58CD37ACAD505940843A8BB046971C43E4D0593C6BB946B07926644F78F -In = 42E99A2F80AEE0E001279A2434F731E01D34A44B1A8101726921C0590C30F3120EB83059F325E894A5AC959DCA71CE2214799916424E859D27D789437B9D27240BF8C35ADBAFCECC322B48AA205B293962D858652ABACBD588BCF6CBC388D0993BD622F96ED54614C25B6A9AA527589EAAFFCF17DDF7 -Out = f5255001f18bffbed199daf1eaddf3207d74f708e643405f5af175565052a29a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172 +Out = 46B07CC026B633E6245661813D5ACA2BCC295D0B8AB01F27F517B4F2823D0B3E -In = 3C9B46450C0F2CAE8E3823F8BDB4277F31B744CE2EB17054BDDC6DFF36AF7F49FB8A2320CC3BDF8E0A2EA29AD3A55DE1165D219ADEDDB5175253E2D1489E9B6FDD02E2C3D3A4B54D60E3A47334C37913C5695378A669E9B72DEC32AF5434F93F46176EBF044C4784467C700470D0C0B40C8A088C815816 -Out = 011671ac40735bb022b9e4b419c4cc8fa0eb668ec5cc1ef3cb019d9f4f66988c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273 +Out = FC169B3E5480E72057D828702F9DA4F08D141A178EB8ADEF03CA0708C1A10DF7 -In = D1E654B77CB155F5C77971A64DF9E5D34C26A3CAD6C7F6B300D39DEB1910094691ADAA095BE4BA5D86690A976428635D5526F3E946F7DC3BD4DBC78999E653441187A81F9ADCD5A3C5F254BC8256B0158F54673DCC1232F6E918EBFC6C51CE67EAEB042D9F57EEC4BFE910E169AF78B3DE48D137DF4F2840 -Out = c8a95e18e0e4f7a933e8566d2494a6da915a0d98cf8349e92b4cb93747e77b01 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F7071727374 +Out = AE3E241C1C394190FBAC7FCCB3DF0D0BC317C6E1A15993330E9B648C80264066 -In = 626F68C18A69A6590159A9C46BE03D5965698F2DAC3DE779B878B3D9C421E0F21B955A16C715C1EC1E22CE3EB645B8B4F263F60660EA3028981EEBD6C8C3A367285B691C8EE56944A7CD1217997E1D9C21620B536BDBD5DE8925FF71DEC6FBC06624AB6B21E329813DE90D1E572DFB89A18120C3F606355D25 -Out = 2abd4fdc77775e5c047ae9df4730d4b8a77dbb1352ab78a812ab8118ccd08c9b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475 +Out = F430AEEF864823D8C8B448F2D25452EA3ED7ABE2966ADC8C42636B69D0A1BAD9 -In = 651A6FB3C4B80C7C68C6011675E6094EB56ABF5FC3057324EBC6477825061F9F27E7A94633ABD1FA598A746E4A577CAF524C52EC1788471F92B8C37F23795CA19D559D446CAB16CBCDCE90B79FA1026CEE77BF4AB1B503C5B94C2256AD75B3EAC6FD5DCB96ACA4B03A834BFB4E9AF988CECBF2AE597CB9097940 -Out = 2d177f43de5f062131303586770b6cdd57d343be690435649119f596764e21e6 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576 +Out = B0D6518AFA4E3590746AA5FD1E58F7EC06A347981177737AD6631B22C53B6617 -In = 8AAF072FCE8A2D96BC10B3C91C809EE93072FB205CA7F10ABD82ECD82CF040B1BC49EA13D1857815C0E99781DE3ADBB5443CE1C897E55188CEAF221AA9681638DE05AE1B322938F46BCE51543B57ECDB4C266272259D1798DE13BE90E10EFEC2D07484D9B21A3870E2AA9E06C21AA2D0C9CF420080A80A91DEE16F -Out = f84b134f32926b71ad100a2d896d1ceff0b158645c7a2e78bb0f33f869a94487 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F7071727374757677 +Out = 16684DDD272DFC18DD40C16A7F57CECCA70DF0A96C4A066B97646953E7C7691A -In = 53F918FD00B1701BD504F8CDEA803ACCA21AC18C564AB90C2A17DA592C7D69688F6580575395551E8CD33E0FEF08CA6ED4588D4D140B3E44C032355DF1C531564D7F4835753344345A6781E11CD5E095B73DF5F82C8AE3AD00877936896671E947CC52E2B29DCD463D90A0C9929128DA222B5A211450BBC0E02448E2 -Out = 6ae134e6de806b000e8d349137a76947746457e9be368eeb9e9f85fab9938d4e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778 +Out = E8208DE0982F3D8E9AC258EB26EB3F130CED7331797B625D6B65BA4BA2064C9C -In = A64599B8A61B5CCEC9E67AED69447459C8DA3D1EC6C7C7C82A7428B9B584FA67E90F68E2C00FBBED4613666E5168DA4A16F395F7A3C3832B3B134BFC9CBAA95D2A0FE252F44AC6681EB6D40AB91C1D0282FED6701C57463D3C5F2BB8C6A7301FB4576AA3B5F15510DB8956FF77478C26A7C09BEA7B398CFC83503F538E -Out = eccc9b83615e804711884ef987559e449b0e2e28fda5309d6e3cc4ed97d5da80 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576777879 +Out = 8ECD4BDC226FB29DD486CD77B566723E44C77194BA7A000734736DBB76F61C7C -In = 0E3AB0E054739B00CDB6A87BD12CAE024B54CB5E550E6C425360C2E87E59401F5EC24EF0314855F0F56C47695D56A7FB1417693AF2A1ED5291F2FEE95F75EED54A1B1C2E81226FBFF6F63ADE584911C71967A8EB70933BC3F5D15BC91B5C2644D9516D3C3A8C154EE48E118BD1442C043C7A0DBA5AC5B1D5360AAE5B9065 -Out = d60ff5c4001a945421595a6b7dda94b3f4c35bfd640f279e068a40b76806c9c0 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A +Out = 153D5C1D5EDB08F51F6353B6B7BDD48A9BBA14068923B8991BE59346A4F932F7 -In = A62FC595B4096E6336E53FCDFC8D1CC175D71DAC9D750A6133D23199EAAC288207944CEA6B16D27631915B4619F743DA2E30A0C00BBDB1BBB35AB852EF3B9AEC6B0A8DCC6E9E1ABAA3AD62AC0A6C5DE765DE2C3711B769E3FDE44A74016FFF82AC46FA8F1797D3B2A726B696E3DEA5530439ACEE3A45C2A51BC32DD055650B -Out = 48518c3fba4900895aa353087ecccc60b2ecfcd6626cba0c56015701dfe94ab4 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B +Out = 60E555A694396B48273D2D778709C208C0757CD15697A43CF0C2115599C80E68 -In = 2B6DB7CED8665EBE9DEB080295218426BDAA7C6DA9ADD2088932CDFFBAA1C14129BCCDD70F369EFB149285858D2B1D155D14DE2FDB680A8B027284055182A0CAE275234CC9C92863C1B4AB66F304CF0621CD54565F5BFF461D3B461BD40DF28198E3732501B4860EADD503D26D6E69338F4E0456E9E9BAF3D827AE685FB1D817 -Out = 81ed71d5dddf3871ff0e521cadcfcb7adea4ce8e9acd766ecd8ce36661f58a75 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C +Out = 7A4A15B47029005972A6B814D5E7F2C86C616BD98E6A06BD95CBED2F47400C67 -In = 10DB509B2CDCABA6C062AE33BE48116A29EB18E390E1BBADA5CA0A2718AFBCD23431440106594893043CC7F2625281BF7DE2655880966A23705F0C5155C2F5CCA9F2C2142E96D0A2E763B70686CD421B5DB812DACED0C6D65035FDE558E94F26B3E6DDE5BD13980CC80292B723013BD033284584BFF27657871B0CF07A849F4AE2 -Out = c2b0dc6e97ef828c47ebf283c94c3eae4c169bba48430061a165f4d86d7f035c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D +Out = 3DE5ADB8E3BF8DFABB41AFE53C58FAD23427A148755148011B5C12B88ABA3DC0 -In = 9334DE60C997BDA6086101A6314F64E4458F5FF9450C509DF006E8C547983C651CA97879175AABA0C539E82D05C1E02C480975CBB30118121061B1EBAC4F8D9A3781E2DB6B18042E01ECF9017A64A0E57447EC7FCBE6A7F82585F7403EE2223D52D37B4BF426428613D6B4257980972A0ACAB508A7620C1CB28EB4E9D30FC41361EC -Out = 0b69eb2a0dd618c0e7443c040d82c5204732f5b6496e1ce47a6d9fc58d5ddbd2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E +Out = F2FE67FF342E21B8F45E8F2E0BCD1D9243245D50EE6C78042E9C491388791C72 -In = E88AB086891693AA535CEB20E64C7AB97C7DD3548F3786339897A5F0C39031549CA870166E477743CCFBE016B4428D89738E426F5FFE81626137F17AECFF61B72DBEE2DC20961880CFE281DFAB5EE38B1921881450E16032DE5E4D55AD8D4FCA609721B0692BAC79BE5A06E177FE8C80C0C83519FB3347DE9F43D5561CB8107B9B5EDC -Out = af20adbea664f2674cac4a77731d92b9003f1bcc6113842ef22abd85c92b044e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F +Out = C3582F71EBB2BE66FA5DD750F80BAAE97554F3B015663C8BE377CFCB2488C1D1 -In = FD19E01A83EB6EC810B94582CB8FBFA2FCB992B53684FB748D2264F020D3B960CB1D6B8C348C2B54A9FCEA72330C2AAA9A24ECDB00C436ABC702361A82BB8828B85369B8C72ECE0082FE06557163899C2A0EFA466C33C04343A839417057399A63A3929BE1EE4805D6CE3E5D0D0967FE9004696A5663F4CAC9179006A2CEB75542D75D68 -Out = 2dbefd6b8b145ff5f4dce845d35cb713f959a2129b09c89f099741e04b22c33e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F80 +Out = F7F3C46BA2564FF4C4C162DA1F5B605F9F1C4AA6A20652A9F9A337C1A2F5B9C9 -In = 59AE20B6F7E0B3C7A989AFB28324A40FCA25D8651CF1F46AE383EF6D8441587AA1C04C3E3BF88E8131CE6145CFB8973D961E8432B202FA5AF3E09D625FAAD825BC19DA9B5C6C20D02ABDA2FCC58B5BD3FE507BF201263F30543819510C12BC23E2DDB4F711D087A86EDB1B355313363A2DE996B891025E147036087401CCF3CA7815BF3C49 -Out = 89dc08fc3d97de1f548af1a1441e86d5476ac97dcc2aae370fefb10ba9e8c817 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F8081 +Out = D3B11B4CBE513EC7B6F4BB5FF0C411DE405A8641CF221493447A35927CA3AEA7 -In = 77EE804B9F3295AB2362798B72B0A1B2D3291DCEB8139896355830F34B3B328561531F8079B79A6E9980705150866402FDC176C05897E359A6CB1A7AB067383EB497182A7E5AEF7038E4C96D133B2782917417E391535B5E1B51F47D8ED7E4D4025FE98DC87B9C1622614BFF3D1029E68E372DE719803857CA52067CDDAAD958951CB2068CC6 -Out = f145265b5b92c46f38045ec71a73f8856ec8be41de234e29e1400c7b4da2f347 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182 +Out = 62323574C7CDAACA448FCBC7091AE4AF3E04DECB0D3C7C55636BAA0A99C88699 -In = B771D5CEF5D1A41A93D15643D7181D2A2EF0A8E84D91812F20ED21F147BEF732BF3A60EF4067C3734B85BC8CD471780F10DC9E8291B58339A677B960218F71E793F2797AEA349406512829065D37BB55EA796FA4F56FD8896B49B2CD19B43215AD967C712B24E5032D065232E02C127409D2ED4146B9D75D763D52DB98D949D3B0FED6A8052FBB -Out = 32b47fd29f2b30122cab117dae1b4afe90ec72e8f88b3bfd4540c501a5d416a2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F80818283 +Out = 89B7D0203387E0332622CA84A879BDDBCFF155A213997560FC05428E79E06891 -In = B32D95B0B9AAD2A8816DE6D06D1F86008505BD8C14124F6E9A163B5A2ADE55F835D0EC3880EF50700D3B25E42CC0AF050CCD1BE5E555B23087E04D7BF9813622780C7313A1954F8740B6EE2D3F71F768DD417F520482BD3A08D4F222B4EE9DBD015447B33507DD50F3AB4247C5DE9A8ABD62A8DECEA01E3B87C8B927F5B08BEB37674C6F8E380C04 -Out = a10a59244c886d66068945a6b90bd6f19cf8355dd6005c967d6287e84d0dce96 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F8081828384 +Out = BE5CCE3E365FBBB139F68A3DA3367E42AA8BD79B15F5252F7C08C3C8F0DCAB27 -In = 04410E31082A47584B406F051398A6ABE74E4DA59BB6F85E6B49E8A1F7F2CA00DFBA5462C2CD2BFDE8B64FB21D70C083F11318B56A52D03B81CAC5EEC29EB31BD0078B6156786DA3D6D8C33098C5C47BB67AC64DB14165AF65B44544D806DDE5F487D5373C7F9792C299E9686B7E5821E7C8E2458315B996B5677D926DAC57B3F22DA873C601016A0D -Out = da9dfeff4f1fed9bfa8fdfb7fdbe9f60aef5038018a48997c8540ce01582badf +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485 +Out = 7AE7702BC025692F2EA5AB0C2EB6552C7975A57C5ACB93FF115D303F341A579C -In = 8B81E9BADDE026F14D95C019977024C9E13DB7A5CD21F9E9FC491D716164BBACDC7060D882615D411438AEA056C340CDF977788F6E17D118DE55026855F93270472D1FD18B9E7E812BAE107E0DFDE7063301B71F6CFE4E225CAB3B232905A56E994F08EE2891BA922D49C3DAFEB75F7C69750CB67D822C96176C46BD8A29F1701373FB09A1A6E3C7158F -Out = 3ec2295d0616fdfe887dcd93cb3cc57bd381e30ee9c1dc14566a050c58742444 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F80818283848586 +Out = F7C4EFACC0A4CB5836F170EA0BF5DC5CE36FE2D88E76A9F259EAAB71AEF0FF13 -In = FA6EED24DA6666A22208146B19A532C2EC9BA94F09F1DEF1E7FC13C399A48E41ACC2A589D099276296348F396253B57CB0E40291BD282773656B6E0D8BEA1CDA084A3738816A840485FCF3FB307F777FA5FEAC48695C2AF4769720258C77943FB4556C362D9CBA8BF103AEB9034BAA8EA8BFB9C4F8E6742CE0D52C49EA8E974F339612E830E9E7A9C29065 -Out = 329777c1378d000aa39806e27f6d92d22e375f8bf5cd950d885745e3635c9a7b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F8081828384858687 +Out = 6A35D3DADC62DFE7819519F92181B2F8D38F5E0ED3D51A22CF8A133AB628D6F4 -In = 9BB4AF1B4F09C071CE3CAFA92E4EB73CE8A6F5D82A85733440368DEE4EB1CBC7B55AC150773B6FE47DBE036C45582ED67E23F4C74585DAB509DF1B83610564545642B2B1EC463E18048FC23477C6B2AA035594ECD33791AF6AF4CBC2A1166ABA8D628C57E707F0B0E8707CAF91CD44BDB915E0296E0190D56D33D8DDE10B5B60377838973C1D943C22ED335E -Out = a68036022089316c48273d4bbd681bd6c2a4c0eafd1a28889619eef67ff93446 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788 +Out = BACECC2948C41BEB73C815CA7CEE6C7DBF2E4219190936EDAC5E4680500DD4D2 -In = 2167F02118CC62043E9091A647CADBED95611A521FE0D64E8518F16C808AB297725598AE296880A773607A798F7C3CFCE80D251EBEC6885015F9ABF7EAABAE46798F82CB5926DE5C23F44A3F9F9534B3C6F405B5364C2F8A8BDC5CA49C749BED8CE4BA48897062AE8424CA6DDE5F55C0E42A95D1E292CA54FB46A84FBC9CD87F2D0C9E7448DE3043AE22FDD229 -Out = 88b94e751911e3686fdb8c7e7aafcd8462dbd71200d763da1ed626432bfefbe5 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F80818283848586878889 +Out = 2DCE89F76F4F37472AE6374B0825A9CD61619A995C7F49733F62B606A7CE905A -In = 94B7FA0BC1C44E949B1D7617D31B4720CBE7CA57C6FA4F4094D4761567E389ECC64F6968E4064DF70DF836A47D0C713336B5028B35930D29EB7A7F9A5AF9AD5CF441745BAEC9BB014CEEFF5A41BA5C1CE085FEB980BAB9CF79F2158E03EF7E63E29C38D7816A84D4F71E0F548B7FC316085AE38A060FF9B8DEC36F91AD9EBC0A5B6C338CBB8F6659D342A24368CF -Out = 90d764ee1cfa59b9b66ba321b3ede26f27392951dd53096466391e24402a8f55 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A +Out = 7B65E63A0D17D68C798B5D5631F17C35FAFF70DBE90006589CA89277DBEEAFC0 -In = EA40E83CB18B3A242C1ECC6CCD0B7853A439DAB2C569CFC6DC38A19F5C90ACBF76AEF9EA3742FF3B54EF7D36EB7CE4FF1C9AB3BC119CFF6BE93C03E208783335C0AB8137BE5B10CDC66FF3F89A1BDDC6A1EED74F504CBE7290690BB295A872B9E3FE2CEE9E6C67C41DB8EFD7D863CF10F840FE618E7936DA3DCA5CA6DF933F24F6954BA0801A1294CD8D7E66DFAFEC -Out = 20b9dab34809d1eaa844ff22c14b207230a8dc660173aa0bbc744bcb9386c8ea +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B +Out = 9477E374453099D2F8679E1D9B167B5F1C4E3CC66F32BDD9A3748A10876A27B3 -In = 157D5B7E4507F66D9A267476D33831E7BB768D4D04CC3438DA12F9010263EA5FCAFBDE2579DB2F6B58F911D593D5F79FB05FE3596E3FA80FF2F761D1B0E57080055C118C53E53CDB63055261D7C9B2B39BD90ACC32520CBBDBDA2C4FD8856DBCEE173132A2679198DAF83007A9B5C51511AE49766C792A29520388444EBEFE28256FB33D4260439CBA73A9479EE00C63 -Out = b27d35c0dbdb739a3f45a7f08c1310bf6f31ade1bec0c033b0a5f127c5be58a0 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C +Out = 448437ADAD41878E6529FCC2FAD9BBDB13697B6CBB2669FC8150D3AA7E0418B8 -In = 836B34B515476F613FE447A4E0C3F3B8F20910AC89A3977055C960D2D5D2B72BD8ACC715A9035321B86703A411DDE0466D58A59769672AA60AD587B8481DE4BBA552A1645779789501EC53D540B904821F32B0BD1855B04E4848F9F8CFE9EBD8911BE95781A759D7AD9724A7102DBE576776B7C632BC39B9B5E19057E226552A5994C1DBB3B5C7871A11F5537011044C53 -Out = 14127b73faffbe7e95f5e2beae8176668eee182eb38759e54591eaba4f3442e2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D +Out = CE4936743020825F508CA72C8355C88224A52C348A21567E65526FF8F82632FD -In = CC7784A4912A7AB5AD3620AAB29BA87077CD3CB83636ADC9F3DC94F51EDF521B2161EF108F21A0A298557981C0E53CE6CED45BDF782C1EF200D29BAB81DD6460586964EDAB7CEBDBBEC75FD7925060F7DA2B853B2B089588FA0F8C16EC6498B14C55DCEE335CB3A91D698E4D393AB8E8EAC0825F8ADEBEEE196DF41205C011674E53426CAA453F8DE1CBB57932B0B741D4C6 -Out = fc915bfed73d808f74159fbfb03d0824689f19cca24c112cf87cfff164c84160 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E +Out = 556A195BF23CBA5C3193FBBE472F1CD5478EA4CAD2DC6D6A193102A2ABD0FAC4 -In = 7639B461FFF270B2455AC1D1AFCE782944AEA5E9087EB4A39EB96BB5C3BAAF0E868C8526D3404F9405E79E77BFAC5FFB89BF1957B523E17D341D7323C302EA7083872DD5E8705694ACDDA36D5A1B895AAA16ECA6104C82688532C8BFE1790B5DC9F4EC5FE95BAED37E1D287BE710431F1E5E8EE105BC42ED37D74B1E55984BF1C09FE6A1FA13EF3B96FAEAED6A2A1950A12153 -Out = beccb4aed629aaa32dc028764755215477e7b128ef583dbeda45a659076fcfbf +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F +Out = E15DC6238E2E58E9EA212B0D7ABFD700DA3AE5120D4D601341CE9E424A7C5828 -In = EB6513FC61B30CFBA58D4D7E80F94D14589090CF1D80B1DF2E68088DC6104959BA0D583D585E9578AB0AEC0CF36C48435EB52ED9AB4BBCE7A5ABE679C97AE2DBE35E8CC1D45B06DDA3CF418665C57CBEE4BBB47FA4CAF78F4EE656FEC237FE4EEBBAFA206E1EF2BD0EE4AE71BD0E9B2F54F91DAADF1FEBFD7032381D636B733DCB3BF76FB14E23AFF1F68ED3DBCF75C9B99C6F26 -Out = f5051155cae4a30d87a5634ece4a85f6235151379e17243d9344c1b668217b82 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F90 +Out = A22022450276C5018D51CD321E0E195A0ADD003E33ECAA97028D6974B5712187 -In = 1594D74BF5DDE444265D4C04DAD9721FF3E34CBF622DAF341FE16B96431F6C4DF1F760D34F296EB97D98D560AD5286FEC4DCE1724F20B54FD7DF51D4BF137ADD656C80546FB1BF516D62EE82BAA992910EF4CC18B70F3F8698276FCFB44E0EC546C2C39CFD8EE91034FF9303058B4252462F86C823EB15BF481E6B79CC3A02218595B3658E8B37382BD5048EAED5FD02C37944E73B -Out = 5c6ec5810a859a9575c1ee331ab38779bb708b0b92a9e41bc28532ee90ab7024 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F9091 +Out = 044CABB79E57DA22B772249C332BFB43C5D1C9B683D1B2D2B75F4C5E6773A216 -In = 4CFA1278903026F66FEDD41374558BE1B585D03C5C55DAC94361DF286D4BD39C7CB8037ED3B267B07C346626449D0CC5B0DD2CF221F7E4C3449A4BE99985D2D5E67BFF2923357DDEAB5ABCB4619F3A3A57B2CF928A022EB27676C6CF805689004FCA4D41EA6C2D0A4789C7605F7BB838DD883B3AD3E6027E775BCF262881428099C7FFF95B14C095EA130E0B9938A5E22FC52650F591 -Out = 5a25393d15c2a154c0078a2b956d2051416ffab2bed60aa994db26db3af701c7 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192 +Out = 766E1167593896BDC8793FB7CCDB1D35DC430AAEAFE1E7A96ABA870416587E7E -In = D3E65CB92CFA79662F6AF493D696A07CCF32AAADCCEFF06E73E8D9F6F909209E66715D6E978788C49EFB9087B170ECF3AA86D2D4D1A065AE0EFC8924F365D676B3CB9E2BEC918FD96D0B43DEE83727C9A93BF56CA2B2E59ADBA85696546A815067FC7A78039629D4948D157E7B0D826D1BF8E81237BAB7321312FDAA4D521744F988DB6FDF04549D0FDCA393D639C729AF716E9C8BBA48 -Out = 4fb09d4818be81be1fca8d115f6db9aea5d8883a39f4175f7e96b37a6a378c73 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F90919293 +Out = E19D72CA8438477DB71E1BFA48924C4E75EE4F84C7AA9B0911521C60A2BA6440 -In = 842CC583504539622D7F71E7E31863A2B885C56A0BA62DB4C2A3F2FD12E79660DC7205CA29A0DC0A87DB4DC62EE47A41DB36B9DDB3293B9AC4BAAE7DF5C6E7201E17F717AB56E12CAD476BE49608AD2D50309E7D48D2D8DE4FA58AC3CFEAFEEE48C0A9EEC88498E3EFC51F54D300D828DDDCCB9D0B06DD021A29CF5CB5B2506915BEB8A11998B8B886E0F9B7A80E97D91A7D01270F9A7717 -Out = 9006f59611f10214ba326945b04f9db075cd1fefc9af6349d368f4773c470d1e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F9091929394 +Out = 512EE7AA89497A761F0798C29A1DD37D1D86F1C0870519A0AAB69D265DF118C0 -In = 6C4B0A0719573E57248661E98FEBE326571F9A1CA813D3638531AE28B4860F23C3A3A8AC1C250034A660E2D71E16D3ACC4BF9CE215C6F15B1C0FC7E77D3D27157E66DA9CEEC9258F8F2BF9E02B4AC93793DD6E29E307EDE3695A0DF63CBDC0FC66FB770813EB149CA2A916911BEE4902C47C7802E69E405FE3C04CEB5522792A5503FA829F707272226621F7C488A7698C0D69AA561BE9F378 -Out = 673f1d05f6e2251894b027d68799d1baf37306822af0cf6793e5c0b5930b0576 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495 +Out = 80C199310A2AB7AF6A808F6D68843136C30E9228A766618632D4E1210EDF365C -In = 51B7DBB7CE2FFEB427A91CCFE5218FD40F9E0B7E24756D4C47CD55606008BDC27D16400933906FD9F30EFFDD4880022D081155342AF3FB6CD53672AB7FB5B3A3BCBE47BE1FD3A2278CAE8A5FD61C1433F7D350675DD21803746CADCA574130F01200024C6340AB0CC2CF74F2234669F34E9009EF2EB94823D62B31407F4BA46F1A1EEC41641E84D77727B59E746B8A671BEF936F05BE820759FA -Out = 6587ac7b68f5c2f03b623d7dbadc6bfd589bfb3d6888bf7f5082884d53346280 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F90919293949596 +Out = 43FDFFD19C692B6EF87C103CAD9B80FB86919E6ECDCB73364D260DA29A5F28D3 -In = 83599D93F5561E821BD01A472386BC2FF4EFBD4AED60D5821E84AAE74D8071029810F5E286F8F17651CD27DA07B1EB4382F754CD1C95268783AD09220F5502840370D494BEB17124220F6AFCE91EC8A0F55231F9652433E5CE3489B727716CF4AEBA7DCDA20CD29AA9A859201253F948DD94395ABA9E3852BD1D60DDA7AE5DC045B283DA006E1CBAD83CC13292A315DB5553305C628DD091146597 -Out = 08b1909024480eb77ea582fbe7810db5c6c83657507a4437f7176b71a8efb1e9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F9091929394959697 +Out = 1D1DC1909A6A8E552A0F8964601102D0EDC89F5A02D3ACCED71826BBC5CA37AF -In = 2BE9BF526C9D5A75D565DD11EF63B979D068659C7F026C08BEA4AF161D85A462D80E45040E91F4165C074C43AC661380311A8CBED59CC8E4C4518E80CD2C78AB1CABF66BFF83EAB3A80148550307310950D034A6286C93A1ECE8929E6385C5E3BB6EA8A7C0FB6D6332E320E71CC4EB462A2A62E2BFE08F0CCAD93E61BEDB5DD0B786A728AB666F07E0576D189C92BF9FB20DCA49AC2D3956D47385E2 -Out = 3286cc13f6de5dd70e5bdf48c9152a40a239ec2e1c8b5ab5f01b183a4f251cbc +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798 +Out = FA77DAC317E8C531246E14265ED42A87ADE6FC3CE351652E6AD9290F8A157617 -In = CA76D3A12595A817682617006848675547D3E8F50C2210F9AF906C0E7CE50B4460186FE70457A9E879E79FD4D1A688C70A347361C847BA0DD6AA52936EAF8E58A1BE2F5C1C704E20146D366AEB3853BED9DE9BEFE9569AC8AAEA37A9FB7139A1A1A7D5C748605A8DEFB297869EBEDD71D615A5DA23496D11E11ABBB126B206FA0A7797EE7DE117986012D0362DCEF775C2FE145ADA6BDA1CCB326BF644 -Out = 84ee4d4f569edbfa4271a6aa5082da7f36a71eceb5acf185b8755ef4e0e43081 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F90919293949596979899 +Out = 5BBC0F8B1A52732ED548E600865AE53360F0642A5674ECC7C6185F2CDEEB6601 -In = F76B85DC67421025D64E93096D1D712B7BAF7FB001716F02D33B2160C2C882C310EF13A576B1C2D30EF8F78EF8D2F465007109AAD93F74CB9E7D7BEF7C9590E8AF3B267C89C15DB238138C45833C98CC4A471A7802723EF4C744A853CF80A0C2568DD4ED58A2C9644806F42104CEE53628E5BDF7B63B0B338E931E31B87C24B146C6D040605567CEEF5960DF9E022CB469D4C787F4CBA3C544A1AC91F95F -Out = abaaca3251b25d0043a450613b555d644e798b5d51b86acc08ead65b4fbda716 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A +Out = 89C5FCBAF3A61D6FA4FA33C3ECA8761EBAB3C3467ABA7D255394A0E70811EF3F -In = 25B8C9C032EA6BCD733FFC8718FBB2A503A4EA8F71DEA1176189F694304F0FF68E862A8197B839957549EF243A5279FC2646BD4C009B6D1EDEBF24738197ABB4C992F6B1DC9BA891F570879ACCD5A6B18691A93C7D0A8D38F95B639C1DAEB48C4C2F15CCF5B9D508F8333C32DE78781B41850F261B855C4BEBCC125A380C54D501C5D3BD07E6B52102116088E53D76583B0161E2A58D0778F091206AABD5A1 -Out = 987e45a007a4424167ac7506da8f539c4387304e053a534983ecc5b0621e8f3f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B +Out = 9B3ECEC85FA5BA5E0F1542085FFF46BB2262163979879C9565294B2C56842A28 -In = 21CFDC2A7CCB7F331B3D2EEFFF37E48AD9FA9C788C3F3C200E0173D99963E1CBCA93623B264E920394AE48BB4C3A5BB96FFBC8F0E53F30E22956ADABC2765F57FB761E147ECBF8567533DB6E50C8A1F894310A94EDF806DD8CA6A0E141C0FA7C9FAE6C6AE65F18C93A8529E6E5B553BF55F25BE2E80A9882BD37F145FECBEB3D447A3C4E46C21524CC55CDD62F521AB92A8BA72B897996C49BB273198B7B1C9E -Out = 5fe299c37f7cfb6adf20b406617324ba3ae49fd4635964929ab7e8007c66f573 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C +Out = 4E301B852D473B5D12271209BDC350B4EC615C99CB07BDC2B379F86679B465BB -In = 4E452BA42127DCC956EF4F8F35DD68CB225FB73B5BC7E1EC5A898BBA2931563E74FAFF3B67314F241EC49F4A7061E3BD0213AE826BAB380F1F14FAAB8B0EFDDD5FD1BB49373853A08F30553D5A55CCBBB8153DE4704F29CA2BDEEF0419468E05DD51557CCC80C0A96190BBCC4D77ECFF21C66BDF486459D427F986410F883A80A5BCC32C20F0478BB9A97A126FC5F95451E40F292A4614930D054C851ACD019CCF -Out = e3f2c62d17e9a19ff760e4166f98abbdcb4a157e6bfb46129b05425c0a37385c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D +Out = B306FBBB65EEDBD07AEA67A6490DB9158A768D8223772D9414B124FE184098BA -In = FA85671DF7DADF99A6FFEE97A3AB9991671F5629195049880497487867A6C446B60087FAC9A0F2FCC8E3B24E97E42345B93B5F7D3691829D3F8CCD4BB36411B85FC2328EB0C51CB3151F70860AD3246CE0623A8DC8B3C49F958F8690F8E3860E71EB2B1479A5CEA0B3F8BEFD87ACAF5362435EAECCB52F38617BC6C5C2C6E269EAD1FBD69E941D4AD2012DA2C5B21BCFBF98E4A77AB2AF1F3FDA3233F046D38F1DC8 -Out = a0c97ae36867d5b066e46a710f3f5fa68a2e67b9580aea0d04a20fa0f03ffd73 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E +Out = DE9FC91112BA62A5B732D4B708D6CC27A4B77D88E8F9C50DB361C6F27295B46C -In = E90847AE6797FBC0B6B36D6E588C0A743D725788CA50B6D792352EA8294F5BA654A15366B8E1B288D84F5178240827975A763BC45C7B0430E8A559DF4488505E009C63DA994F1403F407958203CEBB6E37D89C94A5EACF6039A327F6C4DBBC7A2A307D976AA39E41AF6537243FC218DFA6AB4DD817B6A397DF5CA69107A9198799ED248641B63B42CB4C29BFDD7975AC96EDFC274AC562D0474C60347A078CE4C25E88 -Out = 11224522bbdc452e1ac313a60d8ecf582bc4e28499130e884ac76bdf0b57bd8d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F +Out = F3AAC26DC5197EA8DC068D6BFC9E3EE2217D908DF5115FD236BA31828637AB7A -In = F6D5C2B6C93954FC627602C00C4CA9A7D3ED12B27173F0B2C9B0E4A5939398A665E67E69D0B12FB7E4CEB253E8083D1CEB724AC07F009F094E42F2D6F2129489E846EAFF0700A8D4453EF453A3EDDC18F408C77A83275617FABC4EA3A2833AA73406C0E966276079D38E8E38539A70E194CC5513AAA457C699383FD1900B1E72BDFB835D1FD321B37BA80549B078A49EA08152869A918CA57F5B54ED71E4FD3AC5C06729 -Out = 104f0210cbe3ea227fa7017bc96ee6350ab3fe4299f78d0b6afde0c97cf94c96 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0 +Out = 3A0126EC4DB7BD8C0C44E5197A84465C9C97C45F9D1FB8AB256EB5681DC25000 -In = CF8562B1BED89892D67DDAAF3DEEB28246456E972326DBCDB5CF3FB289ACA01E68DA5D59896E3A6165358B071B304D6AB3D018944BE5049D5E0E2BB819ACF67A6006111089E6767132D72DD85BEDDCBB2D64496DB0CC92955AB4C6234F1EEA24F2D51483F2E209E4589BF9519FAC51B4D061E801125E605F8093BB6997BC163D551596FE4AB7CFAE8FB9A90F6980480CE0C229FD1675409BD788354DAF316240CFE0AF93EB -Out = eef772347e060c4dc0a1eb5854926c3bf7197f4937f226881a038e3bb0458bfe +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1 +Out = 7B6B55E2623A8EB6D70FA0913012E3DB03CEA4A6DDC0F5B875673A225F95D86E -In = 2ACE31ABB0A2E3267944D2F75E1559985DB7354C6E605F18DC8470423FCA30B7331D9B33C4A4326783D1CAAE1B4F07060EFF978E4746BF0C7E30CD61040BD5EC2746B29863EB7F103EBDA614C4291A805B6A4C8214230564A0557BC7102E0BD3ED23719252F7435D64D210EE2AAFC585BE903FA41E1968C50FD5D5367926DF7A05E3A42CF07E656FF92DE73B036CF8B19898C0CB34557C0C12C2D8B84E91181AF467BC75A9D1 -Out = 1aefa0c484231061d4f6201be7bfbbb91276ac1f69380e6eb399ca4efe3d80b9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2 +Out = BE3ED738F221B53726D8A54AB7171189D77700BCA86A2C614F5FEFBCA37EB5B0 -In = 0D8D09AED19F1013969CE5E7EB92F83A209AE76BE31C754844EA9116CEB39A22EBB6003017BBCF26555FA6624185187DB8F0CB3564B8B1C06BF685D47F3286EDA20B83358F599D2044BBF0583FAB8D78F854FE0A596183230C5EF8E54426750EAF2CC4E29D3BDD037E734D863C2BD9789B4C243096138F7672C232314EFFDFC6513427E2DA76916B5248933BE312EB5DDE4CF70804FB258AC5FB82D58D08177AC6F4756017FFF5 -Out = 75ffecb3e1ecf60058f243fd0300bc5d634598d82b54b863e64fd9d2fb113955 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3 +Out = C90BCB90AEA0AB6A2331ECC17F4E68507B544910F3561D20A41916D8ABB421DB -In = C3236B73DEB7662BF3F3DAA58F137B358BA610560EF7455785A9BEFDB035A066E90704F929BD9689CEF0CE3BDA5ACF4480BCEB8D09D10B098AD8500D9B6071DFC3A14AF6C77511D81E3AA8844986C3BEA6F469F9E02194C92868CD5F51646256798FF0424954C1434BDFED9FACB390B07D342E992936E0F88BFD0E884A0DDB679D0547CCDEC6384285A45429D115AC7D235A717242021D1DC35641F5F0A48E8445DBA58E6CB2C8EA -Out = cf3b62269f8bd93b5e17a0c45d9690c2638248a9f6f871380e60fe9a03280223 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4 +Out = 72F18A50776C9EFBBB1D360342235BF28AB259648DBC08480390333314E02672 -In = B39FEB8283EADC63E8184B51DF5AE3FD41AAC8A963BB0BE1CD08AA5867D8D910C669221E73243360646F6553D1CA05A84E8DC0DE05B6419EC349CA994480193D01C92525F3FB3DCEFB08AFC6D26947BDBBFD85193F53B50609C6140905C53A6686B58E53A319A57B962331EDE98149AF3DE3118A819DA4D76706A0424B4E1D2910B0ED26AF61D150EBCB46595D4266A0BD7F651BA47D0C7F179CA28545007D92E8419D48FDFBD744CE -Out = 1162f0313bca0a04fd770a2124172fbc2aa5b7914d5b89aeb2f35ce3c9198d0a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5 +Out = 2F1C635728444B5189540CF99D92A77057AED5A8C6FD7DEADBBF9D86815CF5F9 -In = A983D54F503803E8C7999F4EDBBE82E9084F422143A932DDDDC47A17B0B7564A7F37A99D0786E99476428D29E29D3C197A72BFAB1342C12A0FC4787FD7017D7A6174049EA43B5779169EF7472BDBBD941DCB82FC73AAC45A8A94C9F2BD3477F61FD3B796F02A1B8264A214C6FEA74B7051B226C722099EC7883A462B83B6AFDD4009248B8A237F605FE5A08FE7D8B45321421EBBA67BD70A0B00DDBF94BAAB7F359D5D1EEA105F28DCFB -Out = fba3051cdaaf5ab29ea6344cf7f3783d945f5dec34970810210879900382008d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6 +Out = C372E87D3540FB30E7316AF85B65378384DE4664A04E51B30ADA778E3A226D85 -In = E4D1C1897A0A866CE564635B74222F9696BF2C7F640DD78D7E2ACA66E1B61C642BB03EA7536AAE597811E9BF4A7B453EDE31F97B46A5F0EF51A071A2B3918DF16B152519AE3776F9F1EDAB4C2A377C3292E96408359D3613844D5EB393000283D5AD3401A318B12FD1474B8612F2BB50FB6A8B9E023A54D7DDE28C43D6D8854C8D9D1155935C199811DBFC87E9E0072E90EB88681CC7529714F8FB8A2C9D88567ADFB974EE205A9BF7B848 -Out = b291da99ea66457b22844ec35f23718138976058fa8dbaa580da90f57492fab9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7 +Out = D244F4CE129EF1C84EB40D30DA099A2D65682E025E132B94F630FA3FDBDA05AF -In = B10C59723E3DCADD6D75DF87D0A1580E73133A9B7D00CB95EC19F5547027323BE75158B11F80B6E142C6A78531886D9047B08E551E75E6261E79785366D7024BD7CD9CF322D9BE7D57FB661069F2481C7BB759CD71B4B36CA2BC2DF6D3A328FAEBDB995A9794A8D72155ED551A1F87C80BF6059B43FC764900B18A1C2441F7487743CF84E565F61F8DD2ECE6B6CCC9444049197AAAF53E926FBEE3BFCA8BE588EC77F29D211BE89DE18B15F6 -Out = 6510c0ca9697a03f800e483f78389491f18e9e102bb82939e3c366efab52d17e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8 +Out = 930A3E1F69EF7BCF3B9A81C1FEB9758F60F9086B331FC170E6FA20B2A5738540 -In = DB11F609BABA7B0CA634926B1DD539C8CBADA24967D7ADD4D9876F77C2D80C0F4DCEFBD7121548373582705CCA2495BD2A43716FE64ED26D059CFB566B3364BD49EE0717BDD9810DD14D8FAD80DBBDC4CAFB37CC60FB0FE2A80FB4541B8CA9D59DCE457738A9D3D8F641AF8C3FD6DA162DC16FC01AAC527A4A0255B4D231C0BE50F44F0DB0B713AF03D968FE7F0F61ED0824C55C4B5265548FEBD6AAD5C5EEDF63EFE793489C39B8FD29D104CE -Out = c5f0176242bfcd3812aadfe6708db65f8d1ada7a63a6a1dfe395ff59db1b474a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9 +Out = 84C3E0415A9D4DA972A4FD9EE82D63B00099A2620889BC274ECB8606912CF92F -In = BEBD4F1A84FC8B15E4452A54BD02D69E304B7F32616AADD90537937106AE4E28DE9D8AAB02D19BC3E2FDE1D651559E296453E4DBA94370A14DBBB2D1D4E2022302EE90E208321EFCD8528AD89E46DC839EA9DF618EA8394A6BFF308E7726BAE0C19BCD4BE52DA6258E2EF4E96AA21244429F49EF5CB486D7FF35CAC1BACB7E95711944BCCB2AB34700D42D1EB38B5D536B947348A458EDE3DC6BD6EC547B1B0CAE5B257BE36A7124E1060C170FFA -Out = 7e578105d756ccee051cfc3e3192e9a0a521fe2481026ca2b4768f90de1b8322 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AA +Out = 6D0D4CDE9886218E9240A0C956836EA3939B558B8DA0B309CA686F3C631F4942 -In = 5ACA56A03A13784BDC3289D9364F79E2A85C12276B49B92DB0ADAA4F206D5028F213F678C3510E111F9DC4C1C1F8B6ACB17A6413AA227607C515C62A733817BA5E762CC6748E7E0D6872C984D723C9BB3B117EB8963185300A80BFA65CDE495D70A46C44858605FCCBED086C2B45CEF963D33294DBE9706B13AF22F1B7C4CD5A001CFEC251FBA18E722C6E1C4B1166918B4F6F48A98B64B3C07FC86A6B17A6D0480AB79D4E6415B520F1C484D675B1 -Out = 4368d5475c3ac6a5894f91bf1f84c1d5e1162f80e6e358797294cc142f4d7390 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAAB +Out = 86E476F2F8BACABC996360A0D6D57F0E045C9D3085FFE06D2305601C30D2340C -In = A5AAD0E4646A32C85CFCAC73F02FC5300F1982FABB2F2179E28303E447854094CDFC854310E5C0F60993CEFF54D84D6B46323D930ADB07C17599B35B505F09E784BCA5985E0172257797FB53649E2E9723EFD16865C31B5C3D5113B58BB0BFC8920FABDDA086D7537E66D709D050BD14D0C960873F156FAD5B3D3840CDFCDC9BE6AF519DB262A27F40896AB25CC39F96984D650611C0D5A3080D5B3A1BF186ABD42956588B3B58CD948970D298776060 -Out = b130064096a916e2ab1bb60d673acb5ba08a140d74435854dc74b5c9f6a1ad62 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABAC +Out = 6CA5FCF07D2D4B1449924C8C84AD2C2E432A1F923DD365D81D9FDE3C4985724F -In = 06CBBE67E94A978203EAD6C057A1A5B098478B4B4CBEF5A97E93C8E42F5572713575FC2A884531D7622F8F879387A859A80F10EF02708CD8F7413AB385AFC357678B9578C0EBF641EF076A1A30F1F75379E9DCB2A885BDD295905EE80C0168A62A9597D10CF12DD2D8CEE46645C7E5A141F6E0E23AA482ABE5661C16E69EF1E28371E2E236C359BA4E92C25626A7B7FF13F6EA4AE906E1CFE163E91719B1F750A96CBDE5FBC953D9E576CD216AFC90323A -Out = 19a433c573fd994772370b68f15bd3072385636787d202146289375b734f82ac +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACAD +Out = 9A7E1A3B1E01F24FCE4D115EB98E2BB9EEF349CF0D40493B8AB33727C9A1CCC8 -In = F1C528CF7739874707D4D8AD5B98F7C77169DE0B57188DF233B2DC8A5B31EDA5DB4291DD9F68E6BAD37B8D7F6C9C0044B3BF74BBC3D7D1798E138709B0D75E7C593D3CCCDC1B20C7174B4E692ADD820ACE262D45CCFAE2077E878796347168060A162ECCA8C38C1A88350BD63BB539134F700FD4ADDD5959E255337DAA06BC86358FABCBEFDFB5BC889783D843C08AADC6C4F6C36F65F156E851C9A0F917E4A367B5AD93D874812A1DE6A7B93CD53AD97232 -Out = 3bb032072a6bb19ba9cf822f583cd174b55077e120089c82fba770e277b35d43 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE +Out = 165509739C09EC3486143B0C7C009D5B588736AFF2813A2DBBFD733D5840EF98 -In = 9D9F3A7ECD51B41F6572FD0D0881E30390DFB780991DAE7DB3B47619134718E6F987810E542619DFAA7B505C76B7350C6432D8BF1CFEBDF1069B90A35F0D04CBDF130B0DFC7875F4A4E62CDB8E525AADD7CE842520A482AC18F09442D78305FE85A74E39E760A4837482ED2F437DD13B2EC1042AFCF9DECDC3E877E50FF4106AD10A525230D11920324A81094DA31DEAB6476AA42F20C84843CFC1C58545EE80352BDD3740DD6A16792AE2D86F11641BB717C2 -Out = 1fa2be5ed1a5ea507dca6c8f05079b036a928b1eaf2991756535c2bec0025bc3 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAF +Out = AD191A623337F01563824B493BD735BEE26833E9224D971A67B453D6E113D96A -In = 5179888724819FBAD3AFA927D3577796660E6A81C52D98E9303261D5A4A83232F6F758934D50AA83FF9E20A5926DFEBAAC49529D006EB923C5AE5048ED544EC471ED7191EDF46363383824F915769B3E688094C682B02151E5EE01E510B431C8865AFF8B6B6F2F59CB6D129DA79E97C6D2B8FA6C6DA3F603199D2D1BCAB547682A81CD6CF65F6551121391D78BCC23B5BD0E922EC6D8BF97C952E84DD28AEF909ABA31EDB903B28FBFC33B7703CD996215A11238 -Out = ade26df33218b924fb606eca06962d0be8e0c012c55fd81a0e7b8dd4d101e762 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0 +Out = F99E6D680BCBC6BB384290A5F966B18D4DB38951ABE695C6444C319058BB8904 -In = 576EF3520D30B7A4899B8C0D5E359E45C5189ADD100E43BE429A02FB3DE5FF4F8FD0E79D9663ACCA72CD29C94582B19292A557C5B1315297D168FBB54E9E2ECD13809C2B5FCE998EDC6570545E1499DBE7FB74D47CD7F35823B212B05BF3F5A79CAA34224FDD670D335FCB106F5D92C3946F44D3AFCBAE2E41AC554D8E6759F332B76BE89A0324AA12C5482D1EA3EE89DED4936F3E3C080436F539FA137E74C6D3389BDF5A45074C47BC7B20B0948407A66D855E2F -Out = 1aa87867857c28ca2e14503a29f98a7faa57c0eafdeb7d5e60b903262bc79512 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1 +Out = 20E7E81E12C9AD28B7D65D3917051C327031F334A2F82B1F746CBCD7DC41F87D -In = 0DF2152FA4F4357C8741529DD77E783925D3D76E95BAFA2B542A2C33F3D1D117D159CF473F82310356FEE4C90A9E505E70F8F24859656368BA09381FA245EB6C3D763F3093F0C89B972E66B53D59406D9F01AEA07F8B3B615CAC4EE4D05F542E7D0DAB45D67CCCCD3A606CCBEB31EA1FA7005BA07176E60DAB7D78F6810EF086F42F08E595F0EC217372B98970CC6321576D92CE38F7C397A403BADA1548D205C343AC09DECA86325373C3B76D9F32028FEA8EB32515 -Out = 664ef4854e1a2aed15b2d6b8463d9f30d7c3f1a4b74acec296a2cab24afd810b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2 +Out = 14675C8FE5CBE514491F7093AAD58E9DCF8DD33AB22F3A8BCA1CA5130D5E5BEA -In = 3E15350D87D6EBB5C8AD99D42515CFE17980933C7A8F6B8BBBF0A63728CEFAAD2052623C0BD5931839112A48633FB3C2004E0749C87A41B26A8B48945539D1FF41A4B269462FD199BFECD45374756F55A9116E92093AC99451AEFB2AF9FD32D6D7F5FBC7F7A540D5097C096EBC3B3A721541DE073A1CC02F7FB0FB1B9327FB0B1218CA49C9487AB5396622A13AE546C97ABDEF6B56380DDA7012A8384091B6656D0AB272D363CEA78163FF765CDD13AB1738B940D16CAE -Out = 2235c5bb92492211d2f008e637c38786638bc2f24252a7480532cd37072db5ae +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3 +Out = E105BE1CAF056CF599B91423DF3CC1FB2808E0C6A50DA68FC9DB5693D1E0A9DC -In = C38D6B0B757CB552BE40940ECE0009EF3B0B59307C1451686F1A22702922800D58BCE7A636C1727EE547C01B214779E898FC0E560F8AE7F61BEF4D75EAA696B921FD6B735D171535E9EDD267C192B99880C87997711002009095D8A7A437E258104A41A505E5EF71E5613DDD2008195F0C574E6BA3FE40099CFA116E5F1A2FA8A6DA04BADCB4E2D5D0DE31FDC4800891C45781A0AAC7C907B56D631FCA5CE8B2CDE620D11D1777ED9FA603541DE794DDC5758FCD5FAD78C0 -Out = 21ce9f83868fbe56f0ca9c65a927a6fac5168e57b542b96df378633c5c2616bd +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4 +Out = 8FCADE674361487F88D58330ACB439A4B3E717341615BA691370BECF4905E057 -In = 8D2DE3F0B37A6385C90739805B170057F091CD0C7A0BC951540F26A5A75B3E694631BB64C7635EED316F51318E9D8DE13C70A2ABA04A14836855F35E480528B776D0A1E8A23B547C8B8D6A0D09B241D3BE9377160CCA4E6793D00A515DC2992CB7FC741DACA171431DA99CCE6F7789F129E2AC5CF65B40D703035CD2185BB936C82002DAF8CBC27A7A9E554B06196630446A6F0A14BA155ED26D95BD627B7205C072D02B60DB0FD7E49EA058C2E0BA202DAFF0DE91E845CF79 -Out = 1ea29abe6b1a8c319b4f53ef8c6edd575e9de1551957d05fbfb8ae0217f77055 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5 +Out = 70251DCDF7C899BDDD4BC745597B998506C34BEBC5DE115D4FE85AF2AA420171 -In = C464BBDAD275C50DCD983B65AD1019B9FF85A1E71C807F3204BB2C921DC31FBCD8C5FC45868AE9EF85B6C9B83BBA2A5A822201ED68586EC5EC27FB2857A5D1A2D09D09115F22DCC39FE61F5E1BA0FF6E8B4ACB4C6DA748BE7F3F0839739394FF7FA8E39F7F7E84A33C3866875C01BCB1263C9405D91908E9E0B50E7459FABB63D8C6BBB73D8E3483C099B55BC30FF092FF68B6ADEDFD477D63570C9F5515847F36E24BA0B705557130CEC57EBAD1D0B31A378E91894EE26E3A04 -Out = 4a6cf6c2a09bcdef0ca2353eb847c2b99cbee2e9a5f0c07e579fe493c813312b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6 +Out = D987826931A47B9FF871A0F2A8F10BBB659598DA57D8ABF81714B48A475A1356 -In = 8B8D68BB8A75732FE272815A68A1C9C5AA31B41DEDC8493E76525D1D013D33CEBD9E21A5BB95DB2616976A8C07FCF411F5F6BC6F7E0B57ACA78CC2790A6F9B898858AC9C79B165FF24E66677531E39F572BE5D81EB3264524181115F32780257BFB9AEEC6AF12AF28E587CAC068A1A2953B59AD680F4C245B2E3EC36F59940D37E1D3DB38E13EDB29B5C0F404F6FF87F80FC8BE7A225FF22FBB9C8B6B1D7330C57840D24BC75B06B80D30DAD6806544D510AF6C4785E823AC3E0B8 -Out = 551c87a7b77342982e5118f85909910c677e00a986bc0bcd6920097191409d46 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7 +Out = FEA31B338415F503DCB3708D68F377C4267E96345158E7B88D24EC8C232EF2B7 -In = 6B018710446F368E7421F1BC0CCF562D9C1843846BC8D98D1C9BF7D9D6FCB48BFC3BF83B36D44C4FA93430AF75CD190BDE36A7F92F867F58A803900DF8018150384D85D82132F123006AC2AEBA58E02A037FE6AFBD65ECA7C44977DD3DC74F48B6E7A1BFD5CC4DCF24E4D52E92BD4455848E4928B0EAC8B7476FE3CC03E862AA4DFF4470DBFED6DE48E410F25096487ECFC32A27277F3F5023B2725ADE461B1355889554A8836C9CF53BD767F5737D55184EEA1AB3F53EDD0976C485 -Out = 98a3c2f0816b96835f8bae291e14636d0e764e0e41839e1d5de7e56095e63402 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8 +Out = 3F2084DEBFEAFA75D0C078BED7EDA160459A3792A440C3718FB5C14C118E3263 -In = C9534A24714BD4BE37C88A3DA1082EDA7CABD154C309D7BD670DCCD95AA535594463058A29F79031D6ECAA9F675D1211E9359BE82669A79C855EA8D89DD38C2C761DDD0EC0CE9E97597432E9A1BEAE062CDD71EDFDFD464119BE9E69D18A7A7FD7CE0E2106F0C8B0ABF4715E2CA48EF9F454DC203C96656653B727083513F8EFB86E49C513BB758B3B052FE21F1C05BB33C37129D6CC81F1AEF6ADC45B0E8827A830FE545CF57D0955802C117D23CCB55EA28F95C0D8C2F9C5A242B33F -Out = 27be7cb7417099b827558edaf0dcd75e952f06553f6b444ce5fc3701b4fbbbdd +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9 +Out = E55583BD588FB053F040C541DB36603E4959A7421D6281FCA88454720DA34C17 -In = 07906C87297B867ABF4576E9F3CC7F82F22B154AFCBF293B9319F1B0584DA6A40C27B32E0B1B7F412C4F1B82480E70A9235B12EC27090A5A33175A2BB28D8ADC475CEFE33F7803F8CE27967217381F02E67A3B4F84A71F1C5228E0C2AD971373F6F672624FCEA8D1A9F85170FAD30FA0BBD25035C3B41A6175D467998BD1215F6F3866F53847F9CF68EF3E2FBB54BC994DE2302B829C5EEA68EC441FCBAFD7D16AE4FE9FFF98BF00E5BC2AD54DD91FF9FDA4DD77B6C754A91955D1FBAAD0 -Out = f19d1d7c358c79d85de2dd5471ebf4f23115281830078cfc7452991194b6466b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BA +Out = 0F433DE1BBCA62440BE5D1CEAEC8138A2154B374921D17E2D6493F85529A5EE2 -In = 588E94B9054ABC2189DF69B8BA34341B77CDD528E7860E5DEFCAA79B0C9A452AD4B82AA306BE84536EB7CEDCBE058D7B84A6AEF826B028B8A0271B69AC3605A9635EA9F5EA0AA700F3EB7835BC54611B922964300C953EFE7491E3677C2CEBE0822E956CD16433B02C68C4A23252C3F9E151A416B4963257B783E038F6B4D5C9F110F871652C7A649A7BCEDCBCCC6F2D0725BB903CC196BA76C76AA9F10A190B1D1168993BAA9FFC96A1655216773458BEC72B0E39C9F2C121378FEAB4E76A -Out = e03f498a70743c4bb9532d27919d60eb872bb13ed76d8c764dc124595d03da19 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABB +Out = 39CC27D72466D2D63F70F28C59950A0665005B4BC8CFAAA662AEEAF34A19601F -In = 08959A7E4BAAE874928813364071194E2939772F20DB7C3157078987C557C2A6D5ABE68D520EEF3DC491692E1E21BCD880ADEBF63BB4213B50897FA005256ED41B5690F78F52855C8D9168A4B666FCE2DA2B456D7A7E7C17AB5F2FB1EE90B79E698712E963715983FD07641AE4B4E9DC73203FAC1AE11FA1F8C7941FCC82EAB247ADDB56E2638447E9D609E610B60CE086656AAEBF1DA3C8A231D7D94E2FD0AFE46B391FF14A72EAEB3F44AD4DF85866DEF43D4781A0B3578BC996C87970B132 -Out = 61edb23976b4c414f841a75ecb8980b281ccfaab94de2a1e3fe7e6677a64be06 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBC +Out = E7A556EFDAC11394B6058496B06DAD0EB7315CAE4CBD509E77E496C85F202AA4 -In = CB2A234F45E2ECD5863895A451D389A369AAB99CFEF0D5C9FFCA1E6E63F763B5C14FB9B478313C8E8C0EFEB3AC9500CF5FD93791B789E67EAC12FD038E2547CC8E0FC9DB591F33A1E4907C64A922DDA23EC9827310B306098554A4A78F050262DB5B545B159E1FF1DCA6EB734B872343B842C57EAFCFDA8405EEDBB48EF32E99696D135979235C3A05364E371C2D76F1902F1D83146DF9495C0A6C57D7BF9EE77E80F9787AEE27BE1FE126CDC9EF893A4A7DCBBC367E40FE4E1EE90B42EA25AF01 -Out = cb45b6d9d6d58bfbc76b81cac6f8bf1f7f97616839f0943ad3e56f84640c1392 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBD +Out = B90CF1FD5DBEE8A9C18764962BFF431DC560E113073828D839E11929D7D602FF -In = D16BEADF02AB1D4DC6F88B8C4554C51E866DF830B89C06E786A5F8757E8909310AF51C840EFE8D20B35331F4355D80F73295974653DDD620CDDE4730FB6C8D0D2DCB2B45D92D4FBDB567C0A3E86BD1A8A795AF26FBF29FC6C65941CDDB090FF7CD230AC5268AB4606FCCBA9EDED0A2B5D014EE0C34F0B2881AC036E24E151BE89EEB6CD9A7A790AFCCFF234D7CB11B99EBF58CD0C589F20BDAC4F9F0E28F75E3E04E5B3DEBCE607A496D848D67FA7B49132C71B878FD5557E082A18ECA1FBDA94D4B -Out = e955c0302b945acfed294258ad26cea5524331c6be309dc93fe1083f38455f96 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBE +Out = 62B227EFFE8222299E757A065F1B64AB73FE6D2AAC5D762303DB956BC82B78CE -In = 8F65F6BC59A85705016E2BAE7FE57980DE3127E5AB275F573D334F73F8603106EC3553016608EF2DD6E69B24BE0B7113BF6A760BA6E9CE1C48F9E186012CF96A1D4849D75DF5BB8315387FD78E9E153E76F8BA7EC6C8849810F59FB4BB9B004318210B37F1299526866F44059E017E22E96CBE418699D014C6EA01C9F0038B10299884DBEC3199BB05ADC94E955A1533219C1115FED0E5F21228B071F40DD57C4240D98D37B73E412FE0FA4703120D7C0C67972ED233E5DEB300A22605472FA3A3BA86 -Out = 3c351ca113e30ab9e9e99b313ec32a8e0f37b7d1fd098ff69c96b4d3f2dba7e9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF +Out = 7B8F54247422C43A6D36977260E195D06E1DBBA44C392B3FE76DCF4A96C433D5 -In = 84891E52E0D451813210C3FD635B39A03A6B7A7317B221A7ABC270DFA946C42669AACBBBDF801E1584F330E28C729847EA14152BD637B3D0F2B38B4BD5BF9C791C58806281103A3EABBAEDE5E711E539E6A8B2CF297CF351C078B4FA8F7F35CF61BEBF8814BF248A01D41E86C5715EA40C63F7375379A7EB1D78F27622FB468AB784AAABA4E534A6DFD1DF6FA15511341E725ED2E87F98737CCB7B6A6DFAE416477472B046BF1811187D151BFA9F7B2BF9ACDB23A3BE507CDF14CFDF517D2CB5FB9E4AB6 -Out = a16083c61ae30575ff618b9fc560ddd1d1cc835cadd1cad4c6d48fb36447b384 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0 +Out = D1400C9E8C7758B19E29C38E356EBC7ABE8C0887741B845426821C7F74EBD35E -In = FDD7A9433A3B4AFABD7A3A5E3457E56DEBF78E84B7A0B0CA0E8C6D53BD0C2DAE31B2700C6128334F43981BE3B213B1D7A118D59C7E6B6493A86F866A1635C12859CFB9AD17460A77B4522A5C1883C3D6ACC86E6162667EC414E9A104AA892053A2B1D72165A855BACD8FAF8034A5DD9B716F47A0818C09BB6BAF22AA503C06B4CA261F557761989D2AFBD88B6A678AD128AF68672107D0F1FC73C5CA740459297B3292B281E93BCEB761BDE7221C3A55708E5EC84472CDDCAA84ECF23723CC0991355C6280 -Out = a9cccc8124d2cc619c00d130b950a1dce3197026e12c3143717d2ab77f605c4a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1 +Out = 9C557316EBD1E9CDE622271E48F654553067C08D58986190BD3108D8BF54F130 -In = 70A40BFBEF92277A1AAD72F6B79D0177197C4EBD432668CFEC05D099ACCB651062B5DFF156C0B27336687A94B26679CFDD9DAF7AD204338DD9C4D14114033A5C225BD11F217B5F4732DA167EE3F939262D4043FC9CBA92303B7B5E96AEA12ADDA64859DF4B86E9EE0B58E39091E6B188B408AC94E1294A8911245EE361E60E601EFF58D1D37639F3753BEC80EBB4EFDE25817436076623FC65415FE51D1B0280366D12C554D86743F3C3B6572E400361A60726131441BA493A83FBE9AFDA90F7AF1AE717238D -Out = 53bf43d1d384804d88340ab050271e66de532c1618fc929dc2004b100385d64b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2 +Out = F005478FB35B7A389377B35FB6193AAB0E9F3C4138127D0905E488A3E5ED1BD6 -In = 74356E449F4BF8644F77B14F4D67CB6BD9C1F5AE357621D5B8147E562B65C66585CAF2E491B48529A01A34D226D436959153815380D5689E30B35357CDAC6E08D3F2B0E88E200600D62BD9F5EAF488DF86A4470EA227006182E44809009868C4C280C43D7D64A5268FA719074960087B3A6ABC837882F882C837834535929389A12B2C78187E2EA07EF8B8EEF27DC85002C3AE35F1A50BEE6A1C48BA7E175F3316670B27983472AA6A61EED0A683A39EE323080620EA44A9F74411AE5CE99030528F9AB49C79F2 -Out = 2b813d01b0e6e9ccd63ac9c513e837cd33fed46b8fa0be7c91a299f8b1106a8d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3 +Out = F62114D69C3BFA8493061F44DF01566E3A932E83B050B94ECA7A1F7F189D6471 -In = 8C3798E51BC68482D7337D3ABB75DC9FFE860714A9AD73551E120059860DDE24AB87327222B64CF774415A70F724CDF270DE3FE47DDA07B61C9EF2A3551F45A5584860248FABDE676E1CD75F6355AA3EAEABE3B51DC813D9FB2EAA4F0F1D9F834D7CAD9C7C695AE84B329385BC0BEF895B9F1EDF44A03D4B410CC23A79A6B62E4F346A5E8DD851C2857995DDBF5B2D717AEB847310E1F6A46AC3D26A7F9B44985AF656D2B7C9406E8A9E8F47DCB4EF6B83CAACF9AEFB6118BFCFF7E44BEF6937EBDDC89186839B77 -Out = 9a90d20687176af65aa965c3e9a3acba89fd3b5d4ad3b48fa954cef043c49663 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4 +Out = FD915AE6A50C06BA3917BB6D001A4B84C2FF8A906813BA78E80B043A91E7D1D6 -In = FA56BF730C4F8395875189C10C4FB251605757A8FECC31F9737E3C2503B02608E6731E85D7A38393C67DE516B85304824BFB135E33BF22B3A23B913BF6ACD2B7AB85198B8187B2BCD454D5E3318CACB32FD6261C31AE7F6C54EF6A7A2A4C9F3ECB81CE3555D4F0AD466DD4C108A90399D70041997C3B25345A9653F3C9A6711AB1B91D6A9D2216442DA2C973CBD685EE7643BFD77327A2F7AE9CB283620A08716DFB462E5C1D65432CA9D56A90E811443CD1ECB8F0DE179C9CB48BA4F6FEC360C66F252F6E64EDC96B -Out = 4985ebafaecb5fccf715e9fbfb987c10fdabfd92ffb0be5e19e7160d7118cc4f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5 +Out = 2176A39525E6A57CEED2F28EDA5179172EC4F5A15BE41B6CECE8AB140FF1194C -In = B6134F9C3E91DD8000740D009DD806240811D51AB1546A974BCB18D344642BAA5CD5903AF84D58EC5BA17301D5EC0F10CCD0509CBB3FD3FFF9172D193AF0F782252FD1338C7244D40E0E42362275B22D01C4C3389F19DD69BDF958EBE28E31A4FFE2B5F18A87831CFB7095F58A87C9FA21DB72BA269379B2DC2384B3DA953C7925761FED324620ACEA435E52B424A7723F6A2357374157A34CD8252351C25A1B232826CEFE1BD3E70FFC15A31E7C0598219D7F00436294D11891B82497BC78AA5363892A2495DF8C1EEF -Out = 52a0d20fd2a6059fe921ac866f56cae0e3a873b58853421035453cd1d88fa2e1 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6 +Out = BB831D2653AF40B4E8A1048309C1F058D21334AA20C78DC63B8EB74A56FBDE3C -In = C941CDB9C28AB0A791F2E5C8E8BB52850626AA89205BEC3A7E22682313D198B1FA33FC7295381354858758AE6C8EC6FAC3245C6E454D16FA2F51C4166FAB51DF272858F2D603770C40987F64442D487AF49CD5C3991CE858EA2A60DAB6A65A34414965933973AC2457089E359160B7CDEDC42F29E10A91921785F6B7224EE0B349393CDCFF6151B50B377D609559923D0984CDA6000829B916AB6896693EF6A2199B3C22F7DC5500A15B8258420E314C222BC000BC4E5413E6DD82C993F8330F5C6D1BE4BC79F08A1A0A46 -Out = ed03a43c09fc34ebb21fe0988c6c37bd7f750435e8fe7ee3e3e90e5a99c0fab7 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7 +Out = 63C3D97A9F8894D5E043A707B0FEE7F7EC4C049A23BBF1079DF20B4165F9E22D -In = 4499EFFFAC4BCEA52747EFD1E4F20B73E48758BE915C88A1FFE5299B0B005837A46B2F20A9CB3C6E64A9E3C564A27C0F1C6AD1960373036EC5BFE1A8FC6A435C2185ED0F114C50E8B3E4C7ED96B06A036819C9463E864A58D6286F785E32A804443A56AF0B4DF6ABC57ED5C2B185DDEE8489EA080DEEEE66AA33C2E6DAB36251C402682B6824821F998C32163164298E1FAFD31BABBCFFB594C91888C6219079D907FDB438ED89529D6D96212FD55ABE20399DBEFD342248507436931CDEAD496EB6E4A80358ACC78647D043 -Out = 68b3c731f2ca85fef1b9bdfb527a58f49d0393bf6208bbfc6411013790070b9f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8 +Out = 3E53214E700694863284E7DC8DEC3B98C1EAF97ADD0C1431E3BD321D6742A586 -In = EECBB8FDFA4DA62170FD06727F697D81F83F601FF61E478105D3CB7502F2C89BF3E8F56EDD469D049807A38882A7EEFBC85FC9A950952E9FA84B8AFEBD3CE782D4DA598002827B1EB98882EA1F0A8F7AA9CE013A6E9BC462FB66C8D4A18DA21401E1B93356EB12F3725B6DB1684F2300A98B9A119E5D27FF704AFFB618E12708E77E6E5F34139A5A41131FD1D6336C272A8FC37080F041C71341BEE6AB550CB4A20A6DDB6A8E0299F2B14BC730C54B8B1C1C487B494BDCCFD3A53535AB2F231590BF2C4062FD2AD58F906A2D0D -Out = 95d5b88da031bf444f1dd839e69a9e5708a611f0f2266ddc0e98192b116b7f48 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9 +Out = CB4E69B8ADACDCB20DD2D79655117DE489030ECC86C210A268B9985126CA9DF3 -In = E64F3E4ACE5C8418D65FEC2BC5D2A303DD458034736E3B0DF719098BE7A206DEAF52D6BA82316CAF330EF852375188CDE2B39CC94AA449578A7E2A8E3F5A9D68E816B8D16889FBC0EBF0939D04F63033AE9AE2BDAB73B88C26D6BD25EE460EE1EF58FB0AFA92CC539F8C76D3D097E7A6A63EBB9B5887EDF3CF076028C5BBD5B9DB3211371AD3FE121D4E9BF44229F4E1ECF5A0F9F0EBA4D5CEB72878AB22C3F0EB5A625323AC66F7061F4A81FAC834471E0C59553F108475FE290D43E6A055AE3EE46FB67422F814A68C4BE3E8C9 -Out = 04f23b04763288bd67ae2f590cb099da0a96ae836b2240784a6174f0683a0dbe +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CA +Out = 33EB4A42F46198EB7B52F8FD556FEE08430872D3AD16FC3FCA466A2D63CC4932 -In = D2CB2D733033F9E91395312808383CC4F0CA974E87EC68400D52E96B3FA6984AC58D9AD0938DDE5A973008D818C49607D9DE2284E7618F1B8AED8372FBD52ED54557AF4220FAC09DFA8443011699B97D743F8F2B1AEF3537EBB45DCC9E13DFB438428EE190A4EFDB3CAEB7F3933117BF63ABDC7E57BEB4171C7E1AD260AB0587806C4D137B6316B50ABC9CCE0DFF3ACADA47BBB86BE777E617BBE578FF4519844DB360E0A96C6701290E76BB95D26F0F804C8A4F2717EAC4E7DE9F2CFF3BBC55A17E776C0D02856032A6CD10AD2838 -Out = f3f61dc26ec5fdf0fcec7d6a2d13c97fa6ab77b8438906106c00164a6e17144a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACB +Out = 4FB1C07CD6E6ED8DBFAC72210F3FF9707293DAD1F8A4AA19F0AC8FF6C80F3CE9 -In = F2998955613DD414CC111DF5CE30A995BB792E260B0E37A5B1D942FE90171A4AC2F66D4928D7AD377F4D0554CBF4C523D21F6E5F379D6F4B028CDCB9B1758D3B39663242FF3CB6EDE6A36A6F05DB3BC41E0D861B384B6DEC58BB096D0A422FD542DF175E1BE1571FB52AE66F2D86A2F6824A8CFAACBAC4A7492AD0433EEB15454AF8F312B3B2A577750E3EFBD370E8A8CAC1582581971FBA3BA4BD0D76E718DACF8433D33A59D287F8CC92234E7A271041B526E389EFB0E40B6A18B3AAF658E82ED1C78631FD23B4C3EB27C3FAEC8685 -Out = 3d3b2f132643f8a34d5e0a2419243d2ef06bc12256d36e8a9f6b058a2db90076 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCC +Out = 22349AA35ACBCFFA6C84AB0E0231199D8353ADDE248CA1FF91010234886C94DE -In = 447797E2899B72A356BA55BF4DF3ACCA6CDB1041EB477BD1834A9F9ACBC340A294D729F2F97DF3A610BE0FF15EDB9C6D5DB41644B9874360140FC64F52AA03F0286C8A640670067A84E017926A70438DB1BB361DEFEE7317021425F8821DEF26D1EFD77FC853B818545D055ADC9284796E583C76E6FE74C9AC2587AA46AA8F8804F2FEB5836CC4B3ABABAB8429A5783E17D5999F32242EB59EF30CD7ADABC16D72DBDB097623047C98989F88D14EAF02A7212BE16EC2D07981AAA99949DDF89ECD90333A77BC4E1988A82ABF7C7CAF3291 -Out = bcde85279ceecd8f4fb4cce66653c848d01e8d997c7b0a20013078b78ac0e7ba +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCD +Out = 4DDF8CB3D4E80C9971A9AB171F9BC29FA4FD23ECEED01E3BC297892DE389D6F7 -In = 9F2C18ADE9B380C784E170FB763E9AA205F64303067EB1BCEA93DF5DAC4BF5A2E00B78195F808DF24FC76E26CB7BE31DC35F0844CDED1567BBA29858CFFC97FB29010331B01D6A3FB3159CC1B973D255DA9843E34A0A4061CABDB9ED37F241BFABB3C20D32743F4026B59A4CCC385A2301F83C0B0A190B0F2D01ACB8F0D41111E10F2F4E149379275599A52DC089B35FDD5234B0CFB7B6D8AEBD563CA1FA653C5C021DFD6F5920E6F18BFAFDBECBF0AB00281333ED50B9A999549C1C8F8C63D7626C48322E9791D5FF72294049BDE91E73F8 -Out = 8cc2ca7f2193c1899796734cc70e77ec6f6ab342846cd7dbddf7f92d1973e8fb +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCE +Out = 69BE23136E4AF5F52BB333DEB3F32F76610B9FD9DAC9EDE75B2EF0491BF218AB -In = AE159F3FA33619002AE6BCCE8CBBDD7D28E5ED9D61534595C4C9F43C402A9BB31F3B301CBFD4A43CE4C24CD5C9849CC6259ECA90E2A79E01FFBAC07BA0E147FA42676A1D668570E0396387B5BCD599E8E66AAED1B8A191C5A47547F61373021FA6DEADCB55363D233C24440F2C73DBB519F7C9FA5A8962EFD5F6252C0407F190DFEFAD707F3C7007D69FF36B8489A5B6B7C557E79DD4F50C06511F599F56C896B35C917B63BA35C6FF8092BAF7D1658E77FC95D8A6A43EEB4C01F33F03877F92774BE89C1114DD531C011E53A34DC248A2F0E6 -Out = c492d44558167ea86477e78866cf7f98a609ae1369e8d09974f8d7cae0ae8b5e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECF +Out = 0B5CE2869EBBBC91BCC4D2E9560BCC21F4DA20FFFC96CD4EEC422B795641C808 -In = 3B8E97C5FFC2D6A40FA7DE7FCEFC90F3B12C940E7AB415321E29EE692DFAC799B009C99DCDDB708FCE5A178C5C35EE2B8617143EDC4C40B4D313661F49ABDD93CEA79D117518805496FE6ACF292C4C2A1F76B403A97D7C399DAF85B46AD84E16246C67D6836757BDE336C290D5D401E6C1386AB32797AF6BB251E9B2D8FE754C47482B72E0B394EAB76916126FD68EA7D65EB93D59F5B4C5AC40F7C3B37E7F3694F29424C24AF8C8F0EF59CD9DBF1D28E0E10F799A6F78CAD1D45B9DB3D7DEE4A7059ABE99182714983B9C9D44D7F5643596D4F3 -Out = e9e8a971df9f735f2f0a0bf406f7c3147920947d496b0a79132b2c93ac5376c9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0 +Out = 82C17CE6066F792DF2DCE06EACD03AB4D202185A0A531E4AF9A1A2D7B2D43DEF -In = 3434EC31B10FAFDBFEEC0DD6BD94E80F7BA9DCA19EF075F7EB017512AF66D6A4BCF7D16BA0819A1892A6372F9B35BCC7CA8155EE19E8428BC22D214856ED5FA9374C3C09BDE169602CC219679F65A1566FC7316F4CC3B631A18FB4449FA6AFA16A3DB2BC4212EFF539C67CF184680826535589C7111D73BFFCE431B4C40492E763D9279560AAA38EB2DC14A212D723F994A1FE656FF4DD14551CE4E7C621B2AA5604A10001B2878A897A28A08095C325E10A26D2FB1A75BFD64C250309BB55A44F23BBAC0D5516A1C687D3B41EF2FBBF9CC56D4739 -Out = 5112ebf6d8411a2f536fb616a138385f61ff281e217b637312c3b29126ccb7d1 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1 +Out = 97B1B2E7E7AA6A9D1821EE2AD6C8062AF397072EB9A8547C75817D0F0AD1659E -In = 7C7953D81C8D208FD1C97681D48F49DD003456DE60475B84070EF4847C333B74575B1FC8D2A186964485A3B8634FEAA3595AAA1A2F4595A7D6B6153563DEE31BBAC443C8A33EED6D5D956A980A68366C2527B550EE950250DFB691EACBD5D56AE14B970668BE174C89DF2FEA43AE52F13142639C884FD62A3683C0C3792F0F24AB1318BCB27E21F4737FAB62C77EA38BC8FD1CF41F7DAB64C13FEBE7152BF5BB7AB5A78F5346D43CC741CB6F72B7B8980F268B68BF62ABDFB1577A52438FE14B591498CC95F071228460C7C5D5CEB4A7BDE588E7F21C -Out = 104fc7dac280e6c0d4ffdb4c092d3023f6b4da6ced49ac5b7dd7afa04627b734 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2 +Out = 5AA4D29AF9903050D5D329D4D7F3A657CCD038543DA764ED931560F799690A50 -In = 7A6A4F4FDC59A1D223381AE5AF498D74B7252ECF59E389E49130C7EAEE626E7BD9897EFFD92017F4CCDE66B0440462CDEDFD352D8153E6A4C8D7A0812F701CC737B5178C2556F07111200EB627DBC299CAA792DFA58F35935299FA3A3519E9B03166DFFA159103FFA35E8577F7C0A86C6B46FE13DB8E2CDD9DCFBA85BDDDCCE0A7A8E155F81F712D8E9FE646153D3D22C811BD39F830433B2213DD46301941B59293FD0A33E2B63ADBD95239BC01315C46FDB678875B3C81E053A40F581CFBEC24A1404B1671A1B88A6D06120229518FB13A74CA0AC5AE -Out = 2731c99917b5cba4e7a2acf1fb3d82f6b22515b13ab1a73b0f78148b3fe0e40f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3 +Out = C78B17FF5EA603A809668BC06DBD99B78561B37FF615F6F5E5B86165A442EC2C -In = D9FAA14CEBE9B7DE551B6C0765409A33938562013B5E8E0E1E0A6418DF7399D0A6A771FB81C3CA9BD3BB8E2951B0BC792525A294EBD1083688806FE5E7F1E17FD4E3A41D00C89E8FCF4A363CAEDB1ACB558E3D562F1302B3D83BB886ED27B76033798131DAB05B4217381EAAA7BA15EC820BB5C13B516DD640EAEC5A27D05FDFCA0F35B3A5312146806B4C0275BCD0AAA3B2017F346975DB566F9B4D137F4EE10644C2A2DA66DEECA5342E236495C3C6280528BFD32E90AF4CD9BB908F34012B52B4BC56D48CC8A6B59BAB014988EABD12E1A0A1C2E170E7 -Out = aee65c0f1adbc1ed5cd80d97370e5e104f8f5b33daebefa65b2f96cd53a0a61a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4 +Out = 2D1F5FB13901A205B158C5DC01BF54A5BFA9914C6B19AB66F501DA64975E3A4D -In = 2D8427433D0C61F2D96CFE80CF1E932265A191365C3B61AAA3D6DCC039F6BA2AD52A6A8CC30FC10F705E6B7705105977FA496C1C708A277A124304F1FC40911E7441D1B5E77B951AAD7B01FD5DB1B377D165B05BBF898042E39660CAF8B279FE5229D1A8DB86C0999ED65E53D01CCBC4B43173CCF992B3A14586F6BA42F5FE30AFA8AE40C5DF29966F9346DA5F8B35F16A1DE3AB6DE0F477D8D8660918060E88B9B9E9CA6A4207033B87A812DBF5544D39E4882010F82B6CE005F8E8FF6FE3C3806BC2B73C2B83AFB704345629304F9F86358712E9FAE3CA3E -Out = 6c9cd8d65c3295c941279dffff07f396faf6958219d38485812c56ddfd8aef32 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5 +Out = EF7D3F61C537CAC2A217CC214CD9D3E80F4ADAAC8431768F9DB88A6571D3A57D -In = 5E19D97887FCAAC0387E22C6F803C34A3DACD2604172433F7A8A7A526CA4A2A1271ECFC5D5D7BE5AC0D85D921095350DFC65997D443C21C8094E0A3FEFD2961BCB94AED03291AE310CCDA75D8ACE4BC7D89E7D3E5D1650BDA5D668B8B50BFC8E608E184F4D3A9A2BADC4FF5F07E0C0BC8A9F2E0B2A26FD6D8C550008FAAAB75FD71AF2A424BEC9A7CD9D83FAD4C8E9319115656A8717D3B523A68FF8004258B9990ED362308461804BA3E3A7E92D8F2FFAE5C2FBA55BA5A3C27C0A2F71BD711D2FE1799C2ADB31B200035481E9EE5C4ADF2AB9C0FA50B23975CF -Out = 1ddc9fc60c6b89abb22d5358c887def45a0bcb998530ee2ffc4495b90f8b2fec +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6 +Out = B23A6EB1184E297B5E9EC2E3AEEAEC3C8DE411DC614F2979DE285CE4D3802E18 -In = C8E976AB4638909387CE3B8D4E510C3230E5690E02C45093B1D297910ABC481E56EEA0F296F98379DFC9080AF69E73B2399D1C143BEE80AE1328162CE1BA7F6A8374679B20AACD380EB4E61382C99998704D62701AFA914F9A2705CDB065885F50D086C3EB5753700C387118BB142F3E6DA1E988DFB31AC75D7368931E45D1391A274B22F83CEB072F9BCABC0B216685BFD789F5023971024B1878A205442522F9EA7D8797A4102A3DF41703768251FD5E017C85D1200A464118AA35654E7CA39F3C375B8EF8CBE7534DBC64BC20BEFB417CF60EC92F63D9EE7397 -Out = 70e41012213367c48c58bb10975540b0f93245d9a8e73b38b17d215c15ba396e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7 +Out = 6598F5924005CD92E779A5525636FB061A1CFE4E7AFE97D468EB3106817D6C7A -In = 7145FA124B7429A1FC2231237A949BA7201BCC1822D3272DE005B682398196C25F7E5CC2F289FBF44415F699CB7FE6757791B1443410234AE061EDF623359E2B4E32C19BF88450432DD01CAA5EB16A1DC378F391CA5E3C4E5F356728BDDD4975DB7C890DA8BBC84CC73FF244394D0D48954978765E4A00B593F70F2CA082673A261ED88DBCEF1127728D8CD89BC2C597E9102CED6010F65FA75A14EBE467FA57CE3BD4948B6867D74A9DF5C0EC6F530CBF2EE61CE6F06BC8F2864DFF5583776B31DF8C7FFCB61428A56BF7BD37188B4A5123BBF338393AF46EDA85E6 -Out = d8387e89301b745bd3b50792bfa2ec0f7b2c053faff02e8320375acb50029ae9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8 +Out = F0AEC1C2872DFF14F3592C5F7E83C0DC4D299F94A7CFB247D0A4B95B9B0FC077 -In = 7FDFADCC9D29BAD23AE038C6C65CDA1AEF757221B8872ED3D75FF8DF7DA0627D266E224E812C39F7983E4558BFD0A1F2BEF3FEB56BA09120EF762917B9C093867948547AEE98600D10D87B20106878A8D22C64378BF634F7F75900C03986B077B0BF8B740A82447B61B99FEE5376C5EB6680EC9E3088F0BDD0C56883413D60C1357D3C811950E5890E7600103C916341B80C743C6A852B7B4FB60C3BA21F3BC15B8382437A68454779CF3CD7F9F90CCC8EF28D0B706535B1E4108EB5627BB45D719CB046839AEE311CA1ABDC8319E050D67972CB35A6B1601B25DBF487 -Out = c752394672423c6588ac01fc3d5a89d3eec2f56d99be6cc74530e403cc624885 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9 +Out = 709DA24C1CA042C055A3CFF57280D72F2B50094BBF029D9AED1DCCA3288022C8 -In = 988638219FD3095421F826F56E4F09E356296B628C3CE6930C9F2E758FD1A80C8273F2F61E4DAAE65C4F110D3E7CA0965AC7D24E34C0DC4BA2D6FF0BF5BBE93B3585F354D7543CB542A1AA54674D375077F2D360A8F4D42F3DB131C3B7AB7306267BA107659864A90C8C909460A73621D1F5D9D3FD95BEB19B23DB1CB6C0D0FBA91D36891529B8BD8263CAA1BAB56A4AFFAED44962DF096D8D5B1EB845EF31188B3E10F1AF811A13F156BEB7A288AAE593EBD1471B624AA1A7C6ADF01E2200B3D72D88A3AED3100C88231E41EFC376906F0B580DC895F080FDA5741DB1CB -Out = 71f3e4c26a706e0c3c30f5ad6eb16573f9850aba942d1ff3729634b6bccb1e48 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DA +Out = AFB0ABB463999CC9AB124C95993D62E839BF7409D91D1C377912A895523C3125 -In = 5AAB62756D307A669D146ABA988D9074C5A159B3DE85151A819B117CA1FF6597F6156E80FDD28C9C3176835164D37DA7DA11D94E09ADD770B68A6E081CD22CA0C004BFE7CD283BF43A588DA91F509B27A6584C474A4A2F3EE0F1F56447379240A5AB1FB77FDCA49B305F07BA86B62756FB9EFB4FC225C86845F026EA542076B91A0BC2CDD136E122C659BE259D98E5841DF4C2F60330D4D8CDEE7BF1A0A244524EECC68FF2AEF5BF0069C9E87A11C6E519DE1A4062A10C83837388F7EF58598A3846F49D499682B683C4A062B421594FAFBC1383C943BA83BDEF515EFCF10D -Out = 54e6b4e7a918b5b0a73a458f7a6e3c426366e2e6bc110ce16cdddf96356d2164 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADB +Out = 049EB53D4BC939E817C9572A5F0DEF95E4E38B4614969D866E738E3AE6E24936 -In = 47B8216AA0FBB5D67966F2E82C17C07AA2D6327E96FCD83E3DE7333689F3EE79994A1BF45082C4D725ED8D41205CB5BCDF5C341F77FACB1DA46A5B9B2CBC49EADF786BCD881F371A95FA17DF73F606519AEA0FF79D5A11427B98EE7F13A5C00637E2854134691059839121FEA9ABE2CD1BCBBBF27C74CAF3678E05BFB1C949897EA01F56FFA4DAFBE8644611685C617A3206C7A7036E4AC816799F693DAFE7F19F303CE4EBA09D21E03610201BFC665B72400A547A1E00FA9B7AD8D84F84B34AEF118515E74DEF11B9188BD1E1F97D9A12C30132EC2806339BDADACDA2FD8B78 -Out = 64daf074aae0ee8c06e8bd87067d8ca82d600d388e1514fe0718aaf4c92a5fae +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDC +Out = 1F9DF26FB5219482437297326737C0558EB557DA4EB6374805DD9A30F842AAE4 -In = 8CFF1F67FE53C098896D9136389BD8881816CCAB34862BB67A656E3D98896F3CE6FFD4DA73975809FCDF9666760D6E561C55238B205D8049C1CEDEEF374D1735DAA533147BFA960B2CCE4A4F254176BB4D1BD1E89654432B8DBE1A135C42115B394B024856A2A83DC85D6782BE4B444239567CCEC4B184D4548EAE3FF6A192F343292BA2E32A0F267F31CC26719EB85245D415FB897AC2DA433EE91A99424C9D7F1766A44171D1651001C38FC79294ACCC68CEB5665D36218454D3BA169AE058A831338C17743603F81EE173BFC0927464F9BD728DEE94C6AEAB7AAE6EE3A627E8 -Out = 47695052d3e232e74d2a516baa5eb7c0e5d291088989ee9bd3ee75c0dbb749c5 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDD +Out = 11151481E2199BE550F9AE696CBD9DDC9BE9686DBC77B619C005D0FB5AEF9B89 -In = EACD07971CFF9B9939903F8C1D8CBB5D4DB1B548A85D04E037514A583604E787F32992BF2111B97AC5E8A938233552731321522AB5E8583561260B7D13EBEEF785B23A41FD8576A6DA764A8ED6D822D4957A545D5244756C18AA80E1AAD4D1F9C20D259DEE1711E2CC8FD013169FB7CC4CE38B362F8E0936AE9198B7E838DCEA4F7A5B9429BB3F6BBCF2DC92565E3676C1C5E6EB3DD2A0F86AA23EDD3D0891F197447692794B3DFA269611AD97F72B795602B4FDB198F3FD3EB41B415064256E345E8D8C51C555DC8A21904A9B0F1AD0EFFAB7786AAC2DA3B196507E9F33CA356427 -Out = cccc14222df23463559456d77a37484def4b1a9ea644dce0529c7945f402e021 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDE +Out = 0767C2DE1353F58B416E3FA492173C9C39792DE46A34C6153D5878BA01E5F07D -In = 23AC4E9A42C6EF45C3336CE6DFC2FF7DE8884CD23DC912FEF0F7756C09D335C189F3AD3A23697ABDA851A81881A0C8CCAFC980AB2C702564C2BE15FE4C4B9F10DFB2248D0D0CB2E2887FD4598A1D4ACDA897944A2FFC580FF92719C95CF2AA42DC584674CB5A9BC5765B9D6DDF5789791D15F8DD925AA12BFFAFBCE60827B490BB7DF3DDA6F2A143C8BF96ABC903D83D59A791E2D62814A89B8080A28060568CF24A80AE61179FE84E0FFAD00388178CB6A617D37EFD54CC01970A4A41D1A8D3DDCE46EDBBA4AB7C90AD565398D376F431189CE8C1C33E132FEAE6A8CD17A61C630012 -Out = 916f3fb79807853beb01d10f44b2ca108a4cdc3992e4f7efd4e2b5d21d8ff043 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF +Out = 46D743927EC8AD5E403C9E20405200481961E7335E90C4EA9C5AD35B77F3681C -In = 0172DF732282C9D488669C358E3492260CBE91C95CFBC1E3FEA6C4B0EC129B45F242ACE09F152FC6234E1BEE8AAB8CD56E8B486E1DCBA9C05407C2F95DA8D8F1C0AF78EE2ED82A3A79EC0CB0709396EE62AADB84F8A4EE8A7CCCA3C1EE84E302A09EA802204AFECF04097E67D0F8E8A9D2651126C0A598A37081E42D168B0AE8A71951C524259E4E2054E535B779679BDADE566FE55700858618E626B4A0FAF895BCCE9011504A49E05FD56127EAE3D1F8917AFB548ECADABDA1020111FEC9314C413498A360B08640549A22CB23C731ACE743252A8227A0D2689D4C6001606678DFB921 -Out = 7e80f30d3966cc30e93b155bd74b64c5292eade7faa76d80393668ed442d57c5 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0 +Out = D5FFD30325257614674DE0B40F7B2DED3A287F48AE229B02C91DD2BE28404171 -In = 3875B9240CF3E0A8B59C658540F26A701CF188496E2C2174788B126FD29402D6A75453BA0635284D08835F40051A2A9683DC92AFB9383719191231170379BA6F4ADC816FECBB0F9C446B785BF520796841E58878B73C58D3EBB097CE4761FDEABE15DE2F319DFBAF1742CDEB389559C788131A6793E193856661376C81CE9568DA19AA6925B47FFD77A43C7A0E758C37D69254909FF0FBD415EF8EB937BCD49F91468B49974C07DC819ABD67395DB0E05874FF83DDDAB895344ABD0E7111B2DF9E58D76D85AD98106B36295826BE04D435615595605E4B4BB824B33C4AFEB5E7BB0D19F909 -Out = 127413e6927e6a4fc79cbfc0b295b3b861d11943e02a67b2514651d475418e26 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1 +Out = C36F10F2463B839F4BBE7130C8F3ABF29A56608BDF767199FACCADECC8245631 -In = 747CC1A59FEFBA94A9C75BA866C30DC5C1CB0C0F8E9361D98484956DD5D1A40F6184AFBE3DAC9F76028D1CAECCFBF69199C6CE2B4C092A3F4D2A56FE5A33A00757F4D7DEE5DFB0524311A97AE0668A47971B95766E2F6DD48C3F57841F91F04A00AD5EA70F2D479A2620DC5CD78EAAB3A3B011719B7E78D19DDF70D9423798AF77517EBC55392FCD01FC600D8D466B9E7A7A85BF33F9CC5419E9BD874DDFD60981150DDAF8D7FEBAA4374F0872A5628D318000311E2F5655365AD4D407C20E5C04DF17A222E7DEEC79C5AB1116D8572F91CD06E1CCC7CED53736FC867FD49ECEBE6BF8082E8A -Out = 0be9498d42c42e4b34340e8c5c3291e79c43823cb32e0aa1487c6fa0a2c0f3d0 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2 +Out = 4265605E9D5B02FC438C15823BDD01CBCEC073D57AD7A699D0126312FDBE4322 -In = 57AF971FCCAEC97435DC2EC9EF0429BCEDC6B647729EA168858A6E49AC1071E706F4A5A645CA14E8C7746D65511620682C906C8B86EC901F3DDED4167B3F00B06CBFAC6AEE3728051B3E5FF10B4F9ED8BD0B8DA94303C833755B3CA3AEDDF0B54BC8D6632138B5D25BAB03D17B3458A9D782108006F5BB7DE75B5C0BA854B423D8BB801E701E99DC4FEAAD59BC1C7112453B04D33EA3635639FB802C73C2B71D58A56BBD671B18FE34ED2E3DCA38827D63FDB1D4FB3285405004B2B3E26081A8FF08CD6D2B08F8E7B7E90A2AB1ED7A41B1D0128522C2F8BFF56A7FE67969422CE839A9D4608F03 -Out = 1be9a7d7bd198a1283c838073aa4994c5fe3084a7e474d061988c5c425bbcb92 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3 +Out = 829F989B205831D3C5C002EBF8CEA1B5FBA3DAF966539E3B421B6C2C768F7554 -In = 04E16DEDC1227902BAAF332D3D08923601BDD64F573FAA1BB7201918CFE16B1E10151DAE875DA0C0D63C59C3DD050C4C6A874011B018421AFC4623AB0381831B2DA2A8BA42C96E4F70864AC44E106F94311051E74C77C1291BF5DB9539E69567BF6A11CF6932BBBAD33F8946BF5814C066D851633D1A513510039B349939BFD42B858C21827C8FF05F1D09B1B0765DC78A135B5CA4DFBA0801BCADDFA175623C8B647EACFB4444B85A44F73890607D06D507A4F8393658788669F6EF4DEB58D08C50CA0756D5E2F49D1A7AD73E0F0B3D3B5F090ACF622B1878C59133E4A848E05153592EA81C6FBF -Out = 8c3ffd7b284ccb88dc8226854dc29bd779a60a5cb65dbc73c5a5b5c2279c77f2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4 +Out = 406A227D2D1767E0CF417D6BF7CA58A262C79460F421B9C155513714187D10D2 -In = 7C815C384EEE0F288ECE27CCED52A01603127B079C007378BC5D1E6C5E9E6D1C735723ACBBD5801AC49854B2B569D4472D33F40BBB8882956245C366DC3582D71696A97A4E19557E41E54DEE482A14229005F93AFD2C4A7D8614D10A97A9DFA07F7CD946FA45263063DDD29DB8F9E34DB60DAA32684F0072EA2A9426ECEBFA5239FB67F29C18CBAA2AF6ED4BF4283936823AC1790164FEC5457A9CBA7C767CA59392D94CAB7448F50EB34E9A93A80027471CE59736F099C886DEA1AB4CBA4D89F5FC7AE2F21CCD27F611ECA4626B2D08DC22382E92C1EFB2F6AFDC8FDC3D2172604F5035C46B8197D3 -Out = c43199215a8f796edd9388c8377bbdfee8367fec31bc4135e2ba2a1778441f87 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5 +Out = ADF7D0028E41121E47AEF77DCA9DE82FEF7FE4F4C82F2D6DF253ADB4E756F2EC -In = E29D505158DBDD937D9E3D2145658EE6F5992A2FC790F4F608D9CDB44A091D5B94B88E81FAC4FDF5C49442F13B911C55886469629551189EAFF62488F1A479B7DB11A1560E198DDCCCCF50159093425FF7F1CB8D1D1246D0978764087D6BAC257026B090EFAE8CEC5F22B6F21C59ACE1AC7386F5B8837CA6A12B6FBF5534DD0560EF05CA78104D3B943DDB220FEAEC89AA5E692A00F822A2AB9A2FE60350D75E7BE16FF2526DC643872502D01F42F188ABED0A6E9A6F5FD0D1CE7D5755C9FFA66B0AF0B20BD806F08E06156690D81AC811778CA3DAC2C249B96002017FCE93E507E3B953ACF99964B847 -Out = 1fdb0067ee0563b16a0eeb231320ad723a1e4d28d28811b45bac842490d2d7e9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6 +Out = B9A11305F92002DE551314DE53739B1C7E31C257149AEE21F3A5BA4AF068215D -In = D85588696F576E65ECA0155F395F0CFACD83F36A99111ED5768DF2D116D2121E32357BA4F54EDE927F189F297D3A97FAD4E9A0F5B41D8D89DD7FE20156799C2B7B6BF9C957BA0D6763F5C3BC5129747BBB53652B49290CFF1C87E2CDF2C4B95D8AAEE09BC8FBFA6883E62D237885810491BFC101F1D8C636E3D0EDE838AD05C207A3DF4FAD76452979EB99F29AFAECEDD1C63B8D36CF378454A1BB67A741C77AC6B6B3F95F4F02B64DABC15438613EA49750DF42EE90101F115AA9ABB9FF64324DDE9DABBB01054E1BD6B4BCDC7930A44C2300D87CA78C06924D0323AD7887E46C90E8C4D100ACD9EED21E -Out = 71ffdfa05038d4862ebfaeedd1538750f12db7bcb810cbb341d656284cfbdc6c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7 +Out = 49F9ABBE9007E85091827B49256730F552E4A2170A7B6F9CC461483BD8AE0D52 -In = 3A12F8508B40C32C74492B66323375DCFE49184C78F73179F3314B79E63376B8AC683F5A51F1534BD729B02B04D002F55CBD8E8FC9B5EC1EA6BBE6A0D0E7431518E6BA45D124035F9D3DCE0A8BB7BF1430A9F657E0B4EA9F20EB20C786A58181A1E20A96F1628F8728A13BDF7A4B4B32FC8AA7054CC4881AE7FA19AFA65C6C3EE1B3ADE3192AF42054A8A911B8EC1826865D46D93F1E7C5E2B7813C92A506E53886F3D4701BB93D2A681AD109C845904BB861AF8AF0646B6E399B38B614051D34F6842563A0F37EC00CB3D865FC5D746C4987DE2A65071100883A2A9C7A2BFE1E2DD603D9EA24DC7C5FD06BE -Out = 6dbdf5a5f5950280281636c16d798bb18fc68c3830a5340ada428706b9ebd0cc +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8 +Out = 98EBCE2D0B548D9FB99FBD63D842C119F2CE671317CD080EE036FA69457FCCDA -In = 1861EDCE46FA5AD17E1FF1DEAE084DEC580F97D0A67885DFE834B9DFAC1AE076742CE9E267512CA51F6DF5A455AF0C5FD6ABF94ACEA103A3370C354485A7846FB84F3AC7C2904B5B2FBF227002CE512133BB7E1C4E50057BFD1E44DB33C7CDB969A99E284B184F50A14B068A1FC5009D9B298DBE92239572A7627AAC02ABE8F3E3B473417F36D4D2505D16B7577F4526C9D94A270A2DFE450D06DA8F6FA956879A0A55CFE99E742EA555EA477BA3E9B44CCD508C375423611AF92E55345DC215779B2D5119EBA49C71D49B9FE3F1569FA24E5CA3E332D042422A8B8158D3EC66A80012976F31FFDF305F0C9C5E -Out = aac3581d6fb39d26950cac99d149bc1751233dd4614e896e66dddad5676c7f12 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9 +Out = 779042D9184C626289868DC73662E71F413C1DF4FECD2F08B0EDF40BD8D520F4 -In = 08D0FFDE3A6E4EF65608EA672E4830C12943D7187CCFF08F4941CFC13E545F3B9C7AD5EEBBE2B01642B486CAF855C2C73F58C1E4E3391DA8E2D63D96E15FD84953AE5C231911B00AD6050CD7AAFDAAC9B0F663AE6AAB45519D0F5391A541707D479034E73A6AD805AE3598096AF078F1393301493D663DD71F83869CA27BA508B7E91E81E128C1716DC3ACFE3084B2201E04CF8006617EECF1B640474A5D45CFDE9F4D3EF92D6D055B909892194D8A8218DB6D8203A84261D200D71473D7488F3427416B6896C137D455F231071CACBC86E0415AB88AEC841D96B7B8AF41E05BB461A40645BF176601F1E760DE5F -Out = bd6109c513075c279b8130701d92cdb823f6403471ec1fd464c2fdfe6059f7a3 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EA +Out = F8AD01320E4BBAB09282A97511598384089A447F9A6A8FA298B65A82F1731806 -In = D782ABB72A5BE3392757BE02D3E45BE6E2099D6F000D042C8A543F50ED6EBC055A7F133B0DD8E9BC348536EDCAAE2E12EC18E8837DF7A1B3C87EC46D50C241DEE820FD586197552DC20BEEA50F445A07A38F1768A39E2B2FF05DDDEDF751F1DEF612D2E4D810DAA3A0CC904516F9A43AF660315385178A529E51F8AAE141808C8BC5D7B60CAC26BB984AC1890D0436EF780426C547E94A7B08F01ACBFC4A3825EAE04F520A9016F2FB8BF5165ED12736FC71E36A49A73614739EAA3EC834069B1B40F1350C2B3AB885C02C640B9F7686ED5F99527E41CFCD796FE4C256C9173186C226169FF257954EBDA81C0E5F99 -Out = 437d315083ea9a70b5cb7048a43f4de2c0387bc2a288706c4530b22faae3244e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEB +Out = 1D799E024FD1627CC0395C68BEC456631153BFFAEB625CDA58411B9CBE137B34 -In = 5FCE8109A358570E40983E1184E541833BB9091E280F258CFB144387B05D190E431CB19BAA67273BA0C58ABE91308E1844DCD0B3678BAA42F335F2FA05267A0240B3C718A5942B3B3E3BFA98A55C25A1466E8D7A603722CB2BBF03AFA54CD769A99F310735EE5A05DAE2C22D397BD95635F58C48A67F90E1B73AAFCD3F82117F0166657838691005B18DA6F341D6E90FC1CDB352B30FAE45D348294E501B63252DE14740F2B85AE5299DDEC3172DE8B6D0BA219A20A23BB5E10FF434D39DB3F583305E9F5C039D98569E377B75A70AB837D1DF269B8A4B566F40BB91B577455FD3C356C914FA06B9A7CE24C7317A172D -Out = b2e980bf1c4efe1689e5946f38cff8197003e6e0850fb9c6120dea1c68c3d937 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBEC +Out = A9BA7072400DAC24052EF744CA60C8273D743AF357C851A7016CAFD599225672 -In = 6172F1971A6E1E4E6170AFBAD95D5FEC99BF69B24B674BC17DD78011615E502DE6F56B86B1A71D3F4348087218AC7B7D09302993BE272E4A591968AEF18A1262D665610D1070EE91CC8DA36E1F841A69A7A682C580E836941D21D909A3AFC1F0B963E1CA5AB193E124A1A53DF1C587470E5881FB54DAE1B0D840F0C8F9D1B04C645BA1041C7D8DBF22030A623AA15638B3D99A2C400FF76F3252079AF88D2B37F35EE66C1AD7801A28D3D388AC450B97D5F0F79E4541755356B3B1A5696B023F39AB7AB5F28DF4202936BC97393B93BC915CB159EA1BD7A0A414CB4B7A1AC3AF68F50D79F0C9C7314E750F7D02FAA58BFA -Out = dc54e21ced98c3986142d785a5b5ec64de3098adeac0a73f362dbbe9e810483f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECED +Out = B02147BF83730CC51219F660FE93C63A7463C765395B6721BB842E3EF230E6F5 -In = 5668ECD99DFBE215C4118398AC9C9EAF1A1433FAB4CCDD3968064752B625EA944731F75D48A27D047D67547F14DD0FFAA55FA5E29F7AF0D161D85EAFC4F2029B717C918EAB9D304543290BDBA7158B68020C0BA4E079BC95B5BC0FC044A992B94B4CCD3BD66D0EABB5DBBAB904D62E00752C4E3B0091D773BCF4C14B4377DA3EFFF824B1CB2FA01B32D1E46C909E626ED2DAE920F4C7DBEB635BC754FACBD8D49BEBA3F23C1C41CCBFCD0EE0C114E69737F5597C0BF1D859F0C767E18002AE8E39C26261FFDE2920D3D0BAF0E906138696CFE5B7E32B600F45DF3AAA39932F3A7DF95B60FA8712A2271FCAF3911CE7B511B1 -Out = ab9daee11b314d25bd6e63dbc6f64b95a3459d8e9135026857b2530d65cab990 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEE +Out = B642374D57AFA89053DCB7F6E1E72680FB96E3F28ADAAF5AED89AB4CFC78214A -In = 03D625488354DF30E3F875A68EDFCF340E8366A8E1AB67F9D5C5486A96829DFAC0578289082B2A62117E1CF418B43B90E0ADC881FC6AE8105C888E9ECD21AEA1C9AE1A4038DFD17378FED71D02AE492087D7CDCD98F746855227967CB1AB4714261EE3BEAD3F4DB118329D3EBEF4BC48A875C19BA763966DA0EBEA800E01B2F50B00E9DD4CACA6DCB314D00184EF71EA2391D760C950710DB4A70F9212FFC54861F9DC752CE18867B8AD0C48DF8466EF7231E7AC567F0EB55099E622EBB86CB237520190A61C66AD34F1F4E289CB3282AE3EAAC6152ED24D2C92BAE5A7658252A53C49B7B02DFE54FDB2E90074B6CF310AC661 -Out = bb125f2ada003b4252f7bf1354dced94a887d12d9f7d7ff869549df8cb232c90 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF +Out = 546B4BE370BA614A1761FE8CEF8C023D6A7F968981E23A1E8E1168B25CCE2EFA -In = 2EDC282FFB90B97118DD03AAA03B145F363905E3CBD2D50ECD692B37BF000185C651D3E9726C690D3773EC1E48510E42B17742B0B0377E7DE6B8F55E00A8A4DB4740CEE6DB0830529DD19617501DC1E9359AA3BCF147E0A76B3AB70C4984C13E339E6806BB35E683AF8527093670859F3D8A0FC7D493BCBA6BB12B5F65E71E705CA5D6C948D66ED3D730B26DB395B3447737C26FAD089AA0AD0E306CB28BF0ACF106F89AF3745F0EC72D534968CCA543CD2CA50C94B1456743254E358C1317C07A07BF2B0ECA438A709367FAFC89A57239028FC5FECFD53B8EF958EF10EE0608B7F5CB9923AD97058EC067700CC746C127A61EE3 -Out = 6c9fc1b76ebcadccb5dfa3349f90e3904f2f716991257f14f7b2cef43f259337 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0 +Out = 72DAB9724F6E174D48E6B8CAF489747995D649828A1B1D7A6E8527D9A01523A4 -In = 90B28A6AA1FE533915BCB8E81ED6CACDC10962B7FF82474F845EEB86977600CF70B07BA8E3796141EE340E3FCE842A38A50AFBE90301A3BDCC591F2E7D9DE53E495525560B908C892439990A2CA2679C5539FFDF636777AD9C1CDEF809CDA9E8DCDB451ABB9E9C17EFA4379ABD24B182BD981CAFC792640A183B61694301D04C5B3EAAD694A6BD4CC06EF5DA8FA23B4FA2A64559C5A68397930079D250C51BCF00E2B16A6C49171433B0AADFD80231276560B80458DD77089B7A1BBCC9E7E4B9F881EACD6C92C4318348A13F4914EB27115A1CFC5D16D7FD94954C3532EFACA2CAB025103B2D02C6FD71DA3A77F417D7932685888A -Out = 3efb688c2f015265ffd8bc4e728baf1b8afdce02d5a3f45d5febefa6db5cd38d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1 +Out = 30B611734127B847BED5C68A867BCDED79B2B9DA0A358DBE15C4DD578F81E6C0 -In = 2969447D175490F2AA9BB055014DBEF2E6854C95F8D60950BFE8C0BE8DE254C26B2D31B9E4DE9C68C9ADF49E4EE9B1C2850967F29F5D08738483B417BB96B2A56F0C8ACA632B552059C59AAC3F61F7B45C966B75F1D9931FF4E596406378CEE91AAA726A3A84C33F37E9CDBE626B5745A0B06064A8A8D56E53AAF102D23DD9DF0A3FDF7A638509A6761A33FA42FA8DDBD8E16159C93008B53765019C3F0E9F10B144CE2AC57F5D7297F9C9949E4FF68B70D339F87501CE8550B772F32C6DA8AD2CE2100A895D8B08FA1EEAD7C376B407709703C510B50F87E73E43F8E7348F87C3832A547EF2BBE5799ABEDCF5E1F372EA809233F006 -Out = 9bc2a1c137b07ee7b341e696a19f25582d7341ebcb9eeee1ab434e6375809841 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2 +Out = 986317B1D1AFD4692DD533A712718196D412F46A6F3166F417EF01306AB695DD -In = 721645633A44A2C78B19024EAECF58575AB23C27190833C26875DC0F0D50B46AEA9C343D82EA7D5B3E50EC700545C615DAEAEA64726A0F05607576DCD396D812B03FB6551C641087856D050B10E6A4D5577B82A98AFB89CEE8594C9DC19E79FEFF0382FCFD127F1B803A4B9946F4AC9A4378E1E6E041B1389A53E3450CD32D9D2941B0CBABDB50DA8EA2513145164C3AB6BCBD251C448D2D4B087AC57A59C2285D564F16DA4ED5E607ED979592146FFB0EF3F3DB308FB342DF5EB5924A48256FC763141A278814C82D6D6348577545870AE3A83C7230AC02A1540FE1798F7EF09E335A865A2AE0949B21E4F748FB8A51F44750E213A8FB -Out = f0aa77a6116e4cd26fc40d3c390c072642d6328f8816c2288ef8f81cbe1c0b19 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3 +Out = DB6329F783ADF7CF5A10E47369FE03C95BCF523558F3AEDF18B51A355A252517 -In = 6B860D39725A14B498BB714574B4D37CA787404768F64C648B1751B353AC92BAC2C3A28EA909FDF0423336401A02E63EC24325300D823B6864BB701F9D7C7A1F8EC9D0AE3584AA6DD62EA1997CD831B4BABD9A4DA50932D4EFDA745C61E4130890E156AEE6113716DAF95764222A91187DB2EFFEA49D5D0596102D619BD26A616BBFDA8335505FBB0D90B4C180D1A2335B91538E1668F9F9642790B4E55F9CAB0FE2BDD2935D001EE6419ABAB5457880D0DBFF20ED8758F4C20FE759EFB33141CF0E892587FE8187E5FBC57786B7E8B089612C936DFC03D27EFBBE7C8673F1606BD51D5FF386F4A7AB68EDF59F385EB1291F117BFE717399 -Out = c99db06a3bed5b8f26cb898cd1c9eedfba59b1a223a98f1f7c91031fd8126948 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4 +Out = 54BB60A43C1E045644420916BAC42CC72F86649FE8EFEEC866CD8E8128A88DEF -In = 6A01830AF3889A25183244DECB508BD01253D5B508AB490D3124AFBF42626B2E70894E9B562B288D0A2450CFACF14A0DDAE5C04716E5A0082C33981F6037D23D5E045EE1EF2283FB8B6378A914C5D9441627A722C282FF452E25A7EA608D69CEE4393A0725D17963D0342684F255496D8A18C2961145315130549311FC07F0312FB78E6077334F87EAA873BEE8AA95698996EB21375EB2B4EF53C14401207DEB4568398E5DD9A7CF97E8C9663E23334B46912F8344C19EFCF8C2BA6F04325F1A27E062B62A58D0766FC6DB4D2C6A1928604B0175D872D16B7908EBC041761187CC785526C2A3873FEAC3A642BB39F5351550AF9770C328AF7B -Out = 6639c5984d3e535ca509d017c2c142cd05836bce0f29db5b30ff14f299546026 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5 +Out = 802875948F79EEFB707094E323A0FAA2D6D88949A08C044ED3757174492BD2EB -In = B3C5E74B69933C2533106C563B4CA20238F2B6E675E8681E34A389894785BDADE59652D4A73D80A5C85BD454FD1E9FFDAD1C3815F5038E9EF432AAC5C3C4FE840CC370CF86580A6011778BBEDAF511A51B56D1A2EB68394AA299E26DA9ADA6A2F39B9FAFF7FBA457689B9C1A577B2A1E505FDF75C7A0A64B1DF81B3A356001BF0DF4E02A1FC59F651C9D585EC6224BB279C6BEBA2966E8882D68376081B987468E7AED1EF90EBD090AE825795CDCA1B4F09A979C8DFC21A48D8A53CDBB26C4DB547FC06EFE2F9850EDD2685A4661CB4911F165D4B63EF25B87D0A96D3DFF6AB0758999AAD214D07BD4F133A6734FDE445FE474711B69A98F7E2B -Out = 827871105662dca2416b0114a00edfbe42a8bf9802b23d43fd7039c6f3e0291b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6 +Out = FFC9E261842E1AFDAED9F364F125301F20AC8FB0EEA556DB975211C7DB281DAA -In = 83AF34279CCB5430FEBEC07A81950D30F4B66F484826AFEE7456F0071A51E1BBC55570B5CC7EC6F9309C17BF5BEFDD7C6BA6E968CF218A2B34BD5CF927AB846E38A40BBD81759E9E33381016A755F699DF35D660007B5EADF292FEEFB735207EBF70B5BD17834F7BFA0E16CB219AD4AF524AB1EA37334AA66435E5D397FC0A065C411EBBCE32C240B90476D307CE802EC82C1C49BC1BEC48C0675EC2A6C6F3ED3E5B741D13437095707C565E10D8A20B8C20468FF9514FCF31B4249CD82DCEE58C0A2AF538B291A87E3390D737191A07484A5D3F3FB8C8F15CE056E5E5F8FEBE5E1FB59D6740980AA06CA8A0C20F5712B4CDE5D032E92AB89F0AE1 -Out = faa28f28a188bc7327a02b4e3d5a2988b9331af492262adb9f3ee659508026f0 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7 +Out = EDFE0AA71067C62E894A516A72DD4BEF8C3917E46EB22D39626EA826B90804E9 -In = A7ED84749CCC56BB1DFBA57119D279D412B8A986886D810F067AF349E8749E9EA746A60B03742636C464FC1EE233ACC52C1983914692B64309EDFDF29F1AB912EC3E8DA074D3F1D231511F5756F0B6EEAD3E89A6A88FE330A10FACE267BFFBFC3E3090C7FD9A850561F363AD75EA881E7244F80FF55802D5EF7A1A4E7B89FCFA80F16DF54D1B056EE637E6964B9E0FFD15B6196BDD7DB270C56B47251485348E49813B4EB9ED122A01B3EA45AD5E1A929DF61D5C0F3E77E1FDC356B63883A60E9CBB9FC3E00C2F32DBD469659883F690C6772E335F617BC33F161D6F6984252EE12E62B6000AC5231E0C9BC65BE223D8DFD94C5004A101AF9FD6C0FB -Out = feb2814c962211bdb47877ef42d7720c52ebdb0c520e48ab97da22878ef721e3 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8 +Out = 98832102B93BAB4F82EE872D1DC96D2651CCC9E908C3CF25A56B59CCE20319DF -In = A6FE30DCFCDA1A329E82AB50E32B5F50EB25C873C5D2305860A835AECEE6264AA36A47429922C4B8B3AFD00DA16035830EDB897831C4E7B00F2C23FC0B15FDC30D85FB70C30C431C638E1A25B51CAF1D7E8B050B7F89BFB30F59F0F20FECFF3D639ABC4255B3868FC45DD81E47EB12AB40F2AAC735DF5D1DC1AD997CEFC4D836B854CEE9AC02900036F3867FE0D84AFFF37BDE3308C2206C62C4743375094108877C73B87B2546FE05EA137BEDFC06A2796274099A0D554DA8F7D7223A48CBF31B7DECAA1EBC8B145763E3673168C1B1B715C1CD99ECD3DDB238B06049885ECAD9347C2436DFF32C771F34A38587A44A82C5D3D137A03CAA27E66C8FF6 -Out = 10bc0f48980f5335a65b7025382edd0d44d538623406f7fe4fe762bf903060a9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9 +Out = 1248D2D1736F1C125C6928BF893F581EA25BE6E6E3D3C46081C557DE591D6C6C -In = 83167FF53704C3AA19E9FB3303539759C46DD4091A52DDAE9AD86408B69335989E61414BC20AB4D01220E35241EFF5C9522B079FBA597674C8D716FE441E566110B6211531CECCF8FD06BC8E511D00785E57788ED9A1C5C73524F01830D2E1148C92D0EDC97113E3B7B5CD3049627ABDB8B39DD4D6890E0EE91993F92B03354A88F52251C546E64434D9C3D74544F23FB93E5A2D2F1FB15545B4E1367C97335B0291944C8B730AD3D4789273FA44FB98D78A36C3C3764ABEEAC7C569C1E43A352E5B770C3504F87090DEE075A1C4C85C0C39CF421BDCC615F9EFF6CB4FE6468004AECE5F30E1ECC6DB22AD9939BB2B0CCC96521DFBF4AE008B5B46BC006E -Out = 4c8c8ca9b8a4a909429cfc5e32d3a85f3857ed7813deb272cb56e7cead64bf39 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FA +Out = 7A01651D8FFA44F6695270C73066CA9D61733AE3C181E3477D11E7C9563594A3 -In = 3A3A819C48EFDE2AD914FBF00E18AB6BC4F14513AB27D0C178A188B61431E7F5623CB66B23346775D386B50E982C493ADBBFC54B9A3CD383382336A1A0B2150A15358F336D03AE18F666C7573D55C4FD181C29E6CCFDE63EA35F0ADF5885CFC0A3D84A2B2E4DD24496DB789E663170CEF74798AA1BBCD4574EA0BBA40489D764B2F83AADC66B148B4A0CD95246C127D5871C4F11418690A5DDF01246A0C80A43C70088B6183639DCFDA4125BD113A8F49EE23ED306FAAC576C3FB0C1E256671D817FC2534A52F5B439F72E424DE376F4C565CCA82307DD9EF76DA5B7C4EB7E085172E328807C02D011FFBF33785378D79DC266F6A5BE6BB0E4A92ECEEBAEB1 -Out = f1312ac440f089b1eaef3e3a8e7c15c734d5fbb2e2dab1caed3824ff13827e2e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFB +Out = CED595397CFD128AADBF451002457FF5B0FC2AC3993CABD47F0FC3DDBC6D0F32 -[BLAKE2b(384)] -In = -Out = b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100 - -In = CC -Out = 9f520c539e7b9c056895f27718a97990ff374677d08b5c7f307f2f0d6d84d96ce65ee615d19452c7237feb11907d9bb6 - -In = 41FB -Out = c4131fe792ad9c30f714558c44a37906fe1a959e8f80e590d592b878bc77e52af9870af93240420d2a40fe9dc208ad7e - -In = 1F877C -Out = 3c93148a342ef7dca597826edb97466148fa217d8761175e5f6dc52461a4e270007e5a0663fbf97cdd894e78140e2663 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFC +Out = 8797DB8DD8DDD1ECBEE1F1D71AED5D41ADCA8345277A7BC0B12DF75BB4536722 -In = C1ECFDFC -Out = 5f77d0d10c723078fa42127c7bf89e8869503075cd82cf6bb3ebf3a502fea269a2a6f7f216ee159088c781ec47ee0d70 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFD +Out = 369B09E04BE27275DF75C180D7283C083162F35456152F0B16AE680DDA5195BC -In = 21F134AC57 -Out = ab828876b8b36b77b3df9ea8ecd123c248e31faba8372298dba31158ceef8fb6d68d54fa43f784cbefc9a188d1d29f3a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFE +Out = 1D0850EE9BCA0ABC9601E9DEABE1418FEDEC2FB6AC4150BD5302D2430F9BE943 -In = C6F50BB74E29 -Out = 77c55a5efdac2e9db8c49797c17b1d68942437740648126169dfaf7917af3eddc0184aa0c8cbc185d0df1012393c1780 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF +Out = 39A7EB9FEDC19AABC83425C6755DD90E6F9D0C804964A1F4AAEEA3B9FB599835 -In = 119713CC83EEEF -Out = a1a60084d6c9a4a9d92653815fbb3e4587b35177112014dad9011338a2d4617cd96071a32344f7547891d23385f0139a - -In = 4A4F202484512526 -Out = 141e5ead3e6f54be88c6069a4c64cc0b591b718778bb3256e85d2707b22211a9b9db86be06d15e3d755f15c45f31e22c +[BLAKE2b(384)] -In = 1F66AB4185ED9B6375 -Out = cdf2fc66549d6acfbdabc2ea9b5d6d895f4bebd2d1a83fe10eee819ed10a26c6cb0fd7810a74e45cef71964585a87321 +In = +Out = B32811423377F52D7862286EE1A72EE540524380FDA1724A6F25D7978C6FD3244A6CAF0498812673C5E05EF583825100 -In = EED7422227613B6F53C9 -Out = 8543472ca5e3258f9924cdefa09adf9122271324130737c671014a75b3be5f42b31cd5515b8d48c6844e4d1146479ca0 +In = 00 +Out = CC01088536F784F0BB769E41C4957B6D0CDE1FCC8CF1D91FC477D4DD6E3FBFCD43D1698D146F348B2C36A339682BEC3F -In = EAEED5CDFFD89DECE455F1 -Out = 7ae9f858489a6dd4d8f58c7b886142b65b6b62f4160db42d332c432b2d1d676cbce8789517ed97a9ae96ad71a70f639c +In = 0001 +Out = 7AA1310D573ABED36C22FF582DD50F1DCBECE6617A402BACCBAA2B71D6FE1379C317FF80C15216D6DA64186ACDF89EB3 -In = 5BE43C90F22902E4FE8ED2D3 -Out = 3d268230fbd42202a2fbc80635365be2d03e6aa64f9bb67b19f283ae2f33992f56f544f3346f14bdbebf9d0c22827b63 +In = 000102 +Out = DACC5FDE6F28330BE86BCB13BE11032485C6FD2EB8DBE9EB9FA217F4583FF9A564BB354DD768A672BDF46A2E1465D515 -In = A746273228122F381C3B46E4F1 -Out = 5a39ac570b92e86da1316b17e5ca2adbe19dcc9fc4a286b5aae953e7b92bad17b1fa69af0e06b2d6fe9bc26b651a59ef +In = 00010203 +Out = AD4955474387FBD0FBD0384A8569FDC80DC55FD99B5AB37718C38D471DE31798DF9D287473CF2BF7965E025C97743EC3 -In = 3C5871CD619C69A63B540EB5A625 -Out = 237b8f576c048667e11c4ea32901a8ebcd63c7c7b40bc324824f301b007978043902db961741566b3af6769cf4ac8376 +In = 0001020304 +Out = 3B1DB9196F040256579DAB7044E829FF12D49C0062F65A99307286A03A43E08962CC65B431EDEAFDECE0FAC3F637081D -In = FA22874BCC068879E8EF11A69F0722 -Out = dd837f5133c09f1d5d7169f20e8fcdfaa4ad5b2ca9c93bb42f8353768ffedaf1499614baf869ffa7683d3beef18b927b +In = 000102030405 +Out = 5446F0F8400A98B847C7C99D303A948220E62272C33F25A2C55DFB50C8FF04586F9834E1F2E7CBFC928473711088E725 -In = 52A608AB21CCDD8A4457A57EDE782176 -Out = 3866acdc3ad454c52edf7ef3326f1f1734ff2bf7b2955f948abbf20e96467f901c6cfca3fe8eb4a7e88f8ebfe0472e67 +In = 00010203040506 +Out = 7917C2E9694CA3A8527B3848FAB9D93F54FE77EC5246398F49FB970ECD2FA3E7639B9D0DC1E93CF9A2A31752F26F734B -In = 82E192E4043DDCD12ECF52969D0F807EED -Out = eb5df0af95f3952322087266b495be3cb69322d1b8baa0fb2df924d98ef22222b876b5be6077ed8df9b6603a930dbb61 +In = 0001020304050607 +Out = 8EDA71FCD7AFA16D333D5897026E2628E6C272E78ACAF021A3FAB244AEE81F8A6DBE68ADF9B649C90536D621067A01BC -In = 75683DCB556140C522543BB6E9098B21A21E -Out = 28e56bf77c856b206b27feb5c65adbcff905ed72f15274ee3493bb0c0f6ea94d8548e7f7a6e58f8b6f050398f66f768d +In = 000102030405060708 +Out = 68D136924ADEA6DE85BD5C010277B0B62EFB7D1351EE20273946156ED9D5EDCAAA284C1FD25CE1B0BAC547F126454336 -In = 06E4EFE45035E61FAAF4287B4D8D1F12CA97E5 -Out = 4a2a982161af4d4ce12ef10e02b198727643b85d63146cd78c29759607b727eb086bf5a1417c5de9626bd3da79975207 +In = 00010203040506070809 +Out = 57EBE1BEA284B150F9E15F8D58CB2606FA37AEA4BF96668304B896A7CC9CB3F5EB02DAB3058E1A26E94E59799C227984 -In = E26193989D06568FE688E75540AEA06747D9F851 -Out = 8c6a7b5eb771c09d45adccddfda6d2e266ce02b1c02ee98ce70b78726181ab9a7202fac620152475a82f5231d861218e +In = 000102030405060708090A +Out = 516BBE48CF3F244CE0A2EAFF66D09A7162A5E338AD3151A527940E27E66A159838A99962AB85A9164C2A035794B57D1D -In = D8DC8FDEFBDCE9D44E4CBAFE78447BAE3B5436102A -Out = 6b27923e5a298cfc27c65daaedb95ad14eb60921f32ec921d75304cdcb70a2f03c4b679b648b95bb3de654f99cc18a40 +In = 000102030405060708090A0B +Out = C301D5B04393D60AE2DFA5E5985EC36C37B1E58AED5753246DD8E7FC36212E0183558CCB66D3CB97DC6E6FEB9BC5CFB7 -In = 57085FD7E14216AB102D8317B0CB338A786D5FC32D8F -Out = 092b927f5eef3f4bbb86a568b44ebf742ee55a194b339e98800e81523864d7211dfe7521a6954a0719f81f20a79c623b +In = 000102030405060708090A0B0C +Out = 1521E9AE85DD32E9A903A72DCF1C969173648DEB84C991E3C4914649801286BDCE85CF59E7BF15D53D84E832B0E9A1FD -In = A05404DF5DBB57697E2C16FA29DEFAC8AB3560D6126FA0 -Out = 9cfe6cd164a63098e2e1d4f4947c3cad945ecc38fa69c704d2d9975c48ad44548f2013c6856817f4f614bde0ca6a86b4 +In = 000102030405060708090A0B0C0D +Out = D52238289F07E2864591953DD6AA4657C60335BE4AFDD3D9D65A3C5BED85165EAF59EF577196770B27533B3569BD2BB4 -In = AECBB02759F7433D6FCB06963C74061CD83B5B3FFA6F13C6 -Out = 915f6308a931d761f3764589de11d7ba65bfb6bcbc8460ddf0d671ad129ff4105b9d82739d36a777353e304cd46db67a +In = 000102030405060708090A0B0C0D0E +Out = 0558EE75B48030065F6F53D1770521BAB938CEDF8EDEA3DA43D8670836E674DF97B0F01A65EB2151D1BF7341CE1E1018 -In = AAFDC9243D3D4A096558A360CC27C8D862F0BE73DB5E88AA55 -Out = 78b7dd2f938bd4933b673ea42202db2d59d8073d58639903cc3ecb40eba9c1836689dbf709282525a8c9b4c90b6b6b93 +In = 000102030405060708090A0B0C0D0E0F +Out = 7DBFE0CF53262764CA067721002A7A16A10D6CB22F6DC554591EE8E008E1BD26CFCD125DE7BE3EF5A80FE5E5A7C9EA34 -In = 7BC84867F6F9E9FDC3E1046CAE3A52C77ED485860EE260E30B15 -Out = d623ef431778b4f28e28e632d5c892c0a969ed3b1f98b79753f8b7b3a6d1a4c8e218a15b0535e00154ebe43670657fdd +In = 000102030405060708090A0B0C0D0E0F10 +Out = 971E429E3E7EC82B2DFAB160EE2607CC57F6839412DDE8E23DBB744A126B1EAE250021E800B9F40812B7EF44D82A8FC5 -In = FAC523575A99EC48279A7A459E98FF901918A475034327EFB55843 -Out = 9c62fa3e75ffb7c80f216bc5d17d1de4ec4e45bc971b07704c27db9a67085420c4e4cdb83601367d415b7ec1d91ff410 +In = 000102030405060708090A0B0C0D0E0F1011 +Out = A11C06EFB06D8A01153B06E9827A7F209A6202864AE18AB984E89E98E77FF32C3CCF5F5D40FA1CC09336E40303D05EFF -In = 0F8B2D8FCFD9D68CFFC17CCFB117709B53D26462A3F346FB7C79B85E -Out = 8245798dceb37527ffb69985d01932f9743ac934328dbf38d29a69f7709ac93968bcd077d03fa34c8c0630b8e872e747 +In = 000102030405060708090A0B0C0D0E0F101112 +Out = BF3BF737BDDB8FA7D4055177D8F06AD9228C6BD7AA2C8D0C2B3DA435F3C9D485486712EAC07CD456C16B0A03785A2610 -In = A963C3E895FF5A0BE4824400518D81412F875FA50521E26E85EAC90C04 -Out = 53a448530dddb72c05eb575d320bb7c9b28a77ffbacaaa4d697c98e9da3b10413ca782a308c3140bb1a5713b38c9ec69 +In = 000102030405060708090A0B0C0D0E0F10111213 +Out = 094B7F217646F1637A26076C749C40BD1DD57106501342C8DF8ECB30EFC44A6F8A6155FB77CEE476DC126E88BF1BA0EA -In = 03A18688B10CC0EDF83ADF0A84808A9718383C4070C6C4F295098699AC2C -Out = acf14905179789a271f8c8f08a79023d26edaccc68094a94af24864eb46ed80f334a3639530768ce23857612dc9ed2dc +In = 000102030405060708090A0B0C0D0E0F1011121314 +Out = 195AED3C477C98901175BFABAAB4834D559C3E93B6849B6C7006F2E44C73B30985C56BAB7415B4AB7F872913605C4993 -In = 84FB51B517DF6C5ACCB5D022F8F28DA09B10232D42320FFC32DBECC3835B29 -Out = fe6bf735216bbfcf893478b24d0bae7331e46012ac3f11065ab94ccdcd4ef44e3cbaa994d80cbab84ba543af8269c6db +In = 000102030405060708090A0B0C0D0E0F101112131415 +Out = 2029007DB07004086EC1DE9A61EB9597B8AD2A5273D28AB7B46A0E2337CFF1C66672F76E7330E3FBEF1A26C2A4541E65 -In = 9F2FCC7C90DE090D6B87CD7E9718C1EA6CB21118FC2D5DE9F97E5DB6AC1E9C10 -Out = b985eea9a06293ae8483f27348d788aa773e63a4272ab1407943047de2d9dc5bb67746ad8e97b43ac754437b5131ebc6 +In = 000102030405060708090A0B0C0D0E0F10111213141516 +Out = E01514C4D1B44F784FF6FB4051F38197C2223AC510A80E01D13F701F6A0486780C72430FBD7C8DD4F8FC19C0D04D94AC -In = DE8F1B3FAA4B7040ED4563C3B8E598253178E87E4D0DF75E4FF2F2DEDD5A0BE046 -Out = f411663d2953492f3b4ee570a84784901c0759b5b75a782c11547afcd5d7407d4748945a48d85e85a13a82fb5082819a +In = 000102030405060708090A0B0C0D0E0F1011121314151617 +Out = E991FBFC45C40FFAA3AAB7C9884BAF823C03EA63A4A4CCC4FD5D6A6805BFEEF007E0460D221B65D28314010A75D0D930 -In = 62F154EC394D0BC757D045C798C8B87A00E0655D0481A7D2D9FB58D93AEDC676B5A0 -Out = 78c37c14335d8b9f3931beac79141481a9858fe4835290628cecd8118cb30180506590c53f1557611940a7d3b355f1ae +In = 000102030405060708090A0B0C0D0E0F101112131415161718 +Out = 8B331E2E7EDA24D8DEA1CB4C1AB41FF75FD0C6506413DBA04E300EDA8324D56B8ABDAAF5F3D1E05515FB51145FE7F6F3 -In = B2DCFE9FF19E2B23CE7DA2A4207D3E5EC7C6112A8A22AEC9675A886378E14E5BFBAD4E -Out = 4ed8ebf0e21813354db6d49838eb6bd98bb2db9206b6753496c1cf67a34e2e5e26745a704ca75dc1dcde1a03877ce0c0 +In = 000102030405060708090A0B0C0D0E0F10111213141516171819 +Out = B7AECB3B1DD4E1EADADC97510A604FBF64C60C4068C44D7B33E74D065145DA6929EFDAD333A92EB98CB7DA20FCD39D38 -In = 47F5697AC8C31409C0868827347A613A3562041C633CF1F1F86865A576E02835ED2C2492 -Out = aabdc93bd423e5204724c19991291c8ef0bc0ef2658e01f2744c0eba577ff6d605b90252a8790a1fdde6d0fb495be147 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A +Out = ECA0856AA7F204653BE5EA42AAFC91306B3019C42E8581F52E76EFCF4D023BDCF57F73A8E5DC15A497B65FE1B139D5C0 -In = 512A6D292E67ECB2FE486BFE92660953A75484FF4C4F2ECA2B0AF0EDCDD4339C6B2EE4E542 -Out = bfb1dc5ed4b8f37a27fbad30a9528c50fb29a4a4babc02719f4c41755b09837fc62898bbed05a83978fa46cfe47cc598 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B +Out = 215C2D6D58DB2FA8FDFB1C2EA3692E9ACD68C6929F15DD751B1FE056E0F3792D46D0427BCD3AF72779E8EF304254C778 -In = 973CF2B4DCF0BFA872B41194CB05BB4E16760A1840D8343301802576197EC19E2A1493D8F4FB -Out = fc6537cea70fd68f9bc55177512a424397a1b5b285d3c453be0554d02392da2940dd37f45d07a72436441340b5fe8cdc +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C +Out = C22E73ADE37B480AB20F7EC31F1844E36E5C3064F4B60B4FF5715D8D84292348B5BF482CBBA3D09032B83A1377962374 -In = 80BEEBCD2E3F8A9451D4499961C9731AE667CDC24EA020CE3B9AA4BBC0A7F79E30A934467DA4B0 -Out = 0955a0cdfe35668017d2822105d9f6a60b3e4b4707f834620afa7e06b15a94386d3293c6c3e374833be90561ecb8da9f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D +Out = 9E661F0789728B4A7118715C19FBD272C2C5C0BBB8F4543E81133888CC38B17554687753D193131B35319F1743CCEB9B -In = 7ABAA12EC2A7347674E444140AE0FB659D08E1C66DECD8D6EAE925FA451D65F3C0308E29446B8ED3 -Out = 16ecabded51e6d3d89d7ec9eb58945bd25dfa28ddf6c45245621349a4d35450979ce4d8ad309ec5f804ad5a112991cd6 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E +Out = E440945BCE0807DC2FE07018E086ED53751B9BC3C0052CD039CB48EE1A80A0FD72F38C90D9A7280C9082B9F52C8A039B -In = C88DEE9927679B8AF422ABCBACF283B904FF31E1CAC58C7819809F65D5807D46723B20F67BA610C2B7 -Out = 3de757110c9d68a38c561596f22d919291f4bb5946cba4251a63f303e091489a2f69ee5ad2c88466cf3f9c4385047d81 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F +Out = 7DF0B7BE6C29A965D6C3A8056CC72BF36DD8849EB73FC1F23A3AA1902B869E0C8EE99663887EA76893E239C9E45988F7 -In = 01E43FE350FCEC450EC9B102053E6B5D56E09896E0DDD9074FE138E6038210270C834CE6EADC2BB86BF6 -Out = f6a107fa14449b7a5eabd06d7f6870117b73964979ea688a9156351b696c11accca1b3827a6aa5d476c7af926cbe4218 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20 +Out = 8116547B404D20AA0459B97AF4E75D44703EE8E41084F10080F68FC6FA76C38C1047B5E17CA388DED34B5A83C87043C0 -In = 337023370A48B62EE43546F17C4EF2BF8D7ECD1D49F90BAB604B839C2E6E5BD21540D29BA27AB8E309A4B7 -Out = d9f8eaf05c65c5ffde2e8cffa235d0d10427e14547cdd6a8b350677847021084e7ea7b4d0a89d5092880e9c285be28a5 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021 +Out = F7D5B22F0A2409ABC271202B06CE4FCC70FA9C1D725A24CEABAFE241E9F6789668AAA797F414D560F479B9AF3156A265 -In = 6892540F964C8C74BD2DB02C0AD884510CB38AFD4438AF31FC912756F3EFEC6B32B58EBC38FC2A6B913596A8 -Out = 829bd205a8f1a8121d0499d2d3a4bd73f563174e8dc59703e1747824385edd173e6f973bfa4329c6e097262cbda16b7e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122 +Out = D2C5E302EB59E8B2F532B37435557C6BAB3E87B3B768C22EC6EE867235D3AED81335C6A8DEAF009DCE362718EB4905B3 -In = F5961DFD2B1FFFFDA4FFBF30560C165BFEDAB8CE0BE525845DEB8DC61004B7DB38467205F5DCFB34A2ACFE96C0 -Out = b282b5f8c7d63cdeabe9c726ccf16b892d2809aac99aba85286344de336caaed572f13cd4df9dc477a2dd0a4d50a66be +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223 +Out = C07401041428648F80F11E0F5BC7572A0E25198A2AFA1F9F97B575E58ECB4CB6022A643665EF5E1820801D8C1B54A7C8 -In = CA061A2EB6CEED8881CE2057172D869D73A1951E63D57261384B80CEB5451E77B06CF0F5A0EA15CA907EE1C27EBA -Out = 75ab78c51609121ad69ec25f347d48783456d460832a129a31956ca519f4afdf6b8f6511ebcf6d0b40b97c1bf37bc780 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324 +Out = 5CA967220BCD51FE2243E456725856B1F3112EC7CDFDA93DA7CB1B1F6C672CAFEE4E17AF5B57790261D1385AE9783B6E -In = 1743A77251D69242750C4F1140532CD3C33F9B5CCDF7514E8584D4A5F9FBD730BCF84D0D4726364B9BF95AB251D9BB -Out = d34352cffc4a5683bd187c276bfa1dfd73fefc9009352fd3d519fbe4d48ac058e83fcc3120f57c459efbf454047c8ab6 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425 +Out = 82378F12C4AC4EE4CF23F13E6DFFE15817183D4AA354C9A14FA352CAF7DDC114663E8185A25B76E21C8FE6B9E3486E3F -In = D8FABA1F5194C4DB5F176FABFFF856924EF627A37CD08CF55608BBA8F1E324D7C7F157298EABC4DCE7D89CE5162499F9 -Out = 5e9cdd7dcdcaa8d08affa3829bae7475bcffd44aea6cc2707bce55991106e748051b760ef700dc5b0d3c12fb1a89182c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223242526 +Out = AFFEE2FFE4287228EED3D9332FFDBD3CD807FB1BFAC1DDE4999E0A249D7D063B492D48ADD72CF4B906510BEA724458E9 -In = BE9684BE70340860373C9C482BA517E899FC81BAAA12E5C6D7727975D1D41BA8BEF788CDB5CF4606C9C1C7F61AED59F97D -Out = 01b17c63286fc3cc35e407a2ae91b64a788d5871a8f2833c890f9f0a79945d04ab1e07fa7ca9da40d8cade5c741463d6 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627 +Out = 2C455A23E456EF7209A73DB89902BAF67DAC313147C337409FF5085423A571DBBC79AAD3C09BDDCF30CEBD4E2E975EE7 -In = 7E15D2B9EA74CA60F66C8DFAB377D9198B7B16DEB6A1BA0EA3C7EE2042F89D3786E779CF053C77785AA9E692F821F14A7F51 -Out = c109be681cfb94d3c539d388596590ace0195fe259dbf18b61c29414b1cc328edb3ed378e17cf8005fe35e3504bd6a9b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728 +Out = CAA00F63A96D68CA39B213ECAE9204861256CC0CF19ECB1048DA9213F3ACC56202C5D0C95544E8B70B655F610EE45721 -In = 9A219BE43713BD578015E9FDA66C0F2D83CAC563B776AB9F38F3E4F7EF229CB443304FBA401EFB2BDBD7ECE939102298651C86 -Out = 6f0afb88c3ca9529d9c430a5b571f19e86860dc2abae894ed452c38cad5cc6a7bfc6d58fa0cf95129f2f357b930d2ee4 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223242526272829 +Out = C2B4602F2BBB0F9A35DA3C4510E430B9385B8198239E45F5E2FC3BD39C7B6D9AB8AEF8E7F30615C93CB811D3E0FAE6C6 -In = C8F2B693BD0D75EF99CAEBDC22ADF4088A95A3542F637203E283BBC3268780E787D68D28CC3897452F6A22AA8573CCEBF245972A -Out = 247285c03828a23df11b743f48d7d5be419318ff383aadc9ab94518b2ce8d1dfb89427ffca8c38696061a3f16f698aa6 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A +Out = 86C274D20B1D5E2CD36100711FDCAA8835185A899FB0A34CFF37279466329299A57EBB346A55009844B88B1CF788FEF6 -In = EC0F99711016C6A2A07AD80D16427506CE6F441059FD269442BAAA28C6CA037B22EEAC49D5D894C0BF66219F2C08E9D0E8AB21DE52 -Out = 6d678615839e017f80b7f2adc79677be4a0c51fdade2579461e1994cb639d3284b4fcfdbbaac31799ca728d51a01d615 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B +Out = 0C5F57B0D24D37F3CEB925156B11C6784BAAABE196774033010033D7E2495B157F9F238F774DF3FEC6F86117E2F3712F -In = 0DC45181337CA32A8222FE7A3BF42FC9F89744259CFF653504D6051FE84B1A7FFD20CB47D4696CE212A686BB9BE9A8AB1C697B6D6A33 -Out = f667bb758b54549184920259ad80d01f16d1e6a70ea9095947d3ee915f3a404bb66c06cf85204513f1d78abb28371aad +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C +Out = 49048770429D040EC90B9DBC11CCB2EDE463A5B263C12C3DC529B1E28B01398992051DE4AD9FE598CF8EC5F1589FA2E5 -In = DE286BA4206E8B005714F80FB1CDFAEBDE91D29F84603E4A3EBC04686F99A46C9E880B96C574825582E8812A26E5A857FFC6579F63742F -Out = c2d5c24a37c1631845a21e952350313175c3a6daa39d186deeb01cec5a3b326442e1b32cdedcf19f1c01e030ec3191a6 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D +Out = 2BBA965E13AD32B3832A2AD0DC5DADE3EB21DCB6A919E0CF18E08E6EC7CB5BDF1B87A0347AB8D4930FCA5E612003D439 -In = EEBCC18057252CBF3F9C070F1A73213356D5D4BC19AC2A411EC8CDEEE7A571E2E20EAF61FD0C33A0FFEB297DDB77A97F0A415347DB66BCAF -Out = 1a6624b4c537365fdf1f2ee17993111feaad2ca2e43a3effa47c101a3ce0650d4ae89d97833344fd5cf72133c697ee57 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E +Out = A01DE48A297DA53FB1BBC9C43EFACB7F4FA47D95265A8211451F6D18B97D56CC19962ABED9D275034C0DA48ADDA002C7 -In = 416B5CDC9FE951BD361BD7ABFC120A5054758EBA88FDD68FD84E39D3B09AC25497D36B43CBE7B85A6A3CEBDA8DB4E5549C3EE51BB6FCB6AC1E -Out = 528c89564249d054b7a1b16a2fee6ae558755ff5eb773112d9c9f1e69e7b4b3e86d3a0db0997ad20a445e1f5820a4e29 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F +Out = AA8339E709CE63EC6597401E71F130D615B830D3ACD50A8F61ED68DF50CEAC4621DDAA12DD68267878CD4AB7A2BA6975 -In = 5C5FAF66F32E0F8311C32E8DA8284A4ED60891A5A7E50FB2956B3CBAA79FC66CA376460E100415401FC2B8518C64502F187EA14BFC9503759705 -Out = 948e6e49c8c4a84ea5c6dbe9f49c89df5c85e03e96052669650cf887383ffdc6d4b725a3a0bc8395fdc3945b767ef8dd +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30 +Out = 5544A73DCDB9E814896E78793D6E149C407BD334CD802193FA8DC3459A4A776713F45C8DF3AD59B8F5005930AAACCB7B -In = 7167E1E02BE1A7CA69D788666F823AE4EEF39271F3C26A5CF7CEE05BCA83161066DC2E217B330DF821103799DF6D74810EED363ADC4AB99F36046A -Out = fea3951b37647b55a940533bc441d18f6ab1429d49e15db6bad2a13a1d50597c00363999980ce8eedff7065b41c53307 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F3031 +Out = DB71BCF0B95791F47A586BE936FC88B538124904E39FA99E04BBD0E7A1456B614975ACFC05EFD5684D43ABC464390F5C -In = 2FDA311DBBA27321C5329510FAE6948F03210B76D43E7448D1689A063877B6D14C4F6D0EAA96C150051371F7DD8A4119F7DA5C483CC3E6723C01FB7D -Out = c281e1eb33761ea2dac216d2f366ee5990bee48f53cdd824c01011486d2f8cd57b01ab51229e07b6687c179f288399c9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132 +Out = 87AF4B918713FC85E73C10AB6304D5A4AC64CC96FA1B62A404A4AFDC56694038631794083117FCEF571175DBA4DDA404 -In = 95D1474A5AAB5D2422ACA6E481187833A6212BD2D0F91451A67DD786DFC91DFED51B35F47E1DEB8A8AB4B9CB67B70179CC26F553AE7B569969CE151B8D -Out = 72fe1fbe6df8b7e297a56aeb94ae7008bfd431d950c1892e82d4a2d328fd9817d5328feb84edf3548ca9dd3d91494d69 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233 +Out = B0B4DFE92CB69C0CB1264907B0843D588803B1CD9F024FEF2BA61AAA8AF0E60F584C754143FDE0E551B739DAD526BD2C -In = C71BD7941F41DF044A2927A8FF55B4B467C33D089F0988AA253D294ADDBDB32530C0D4208B10D9959823F0C0F0734684006DF79F7099870F6BF53211A88D -Out = 319624bc5976161defe71338dd6ef42a1b160e1b3ac6162ce298b552915a2b62ab62e92ecde38be5f40eb5b360a54051 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F3031323334 +Out = 3F0E22A4790A955DBBE8C557B2299BFDD9D1A472DC9FA0E16AE573AE27C14D6BEDF6C3B368D8A3E99E394FBF1E0B09DE -In = F57C64006D9EA761892E145C99DF1B24640883DA79D9ED5262859DCDA8C3C32E05B03D984F1AB4A230242AB6B78D368DC5AAA1E6D3498D53371E84B0C1D4BA -Out = e4154b4a5d9542af7130120d321d5b0991341d2ff8610266b7883abaeb927d1d5567e4280e25dd04431428a0fe52ee20 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435 +Out = 3B08FF3536EC029B03D7664479F33B01DE1831623834A925433F002BDB071129B89109790E1E0DA2FE6D2F5D8C5FB749 -In = E926AE8B0AF6E53176DBFFCC2A6B88C6BD765F939D3D178A9BDE9EF3AA131C61E31C1E42CDFAF4B4DCDE579A37E150EFBEF5555B4C1CB40439D835A724E2FAE7 -Out = 02ecf14a6e42d2650874d928a5940c8f045687185e83e5257aa0ced7f7fc1ce28ba32f9b6466135d68318d7876d0be52 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233343536 +Out = 1A27A6C9A0C6F493D66E567061980FB0EB5819461C4976D5D30CC91B3C5DE1118E89CD2E00176322C42BFEBAC3508CA3 -In = 16E8B3D8F988E9BB04DE9C96F2627811C973CE4A5296B4772CA3EEFEB80A652BDF21F50DF79F32DB23F9F73D393B2D57D9A0297F7A2F2E79CFDA39FA393DF1AC00 -Out = e070f7dd0ad5ff98ec0142a2fca07ee228e5d483ada57fe0208ba6e0dd987a7b742bd02c48ee489e5f074ddfeddaa438 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F3031323334353637 +Out = 40FE3F1E4F60AE955D7EBCE290DC3C867557650620F512EC42689D999F7FE6CF51459A361D688225D43903BF67197483 -In = FC424EEB27C18A11C01F39C555D8B78A805B88DBA1DC2A42ED5E2C0EC737FF68B2456D80EB85E11714FA3F8EABFB906D3C17964CB4F5E76B29C1765DB03D91BE37FC -Out = e877529070fe4579c184d78444b1c97ff223e144951b7a7e32f1d2770f61a53538c58e73a8ff9623db27b58a1357a15d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738 +Out = D7C98F147ACA01767A630CAE9F73A03C0E12870E79A715229E23EA6E98B8ED666D0B79B318E5F63E7869F07F52329242 -In = ABE3472B54E72734BDBA7D9158736464251C4F21B33FBBC92D7FAC9A35C4E3322FF01D2380CBAA4EF8FB07D21A2128B7B9F5B6D9F34E13F39C7FFC2E72E47888599BA5 -Out = 091564553555f5a7e8c34b99d78045aae4ee45b9264aa459a64f7ef6fb50f412d701b7e16098dfac9ce5344b63cea813 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233343536373839 +Out = 5D48009FA2A03966BC9AB3604B68E86F097F6E7B54B33446A52C11A9ABC447AB60781E8EB2F7706FBC1F9D75C18FF50F -In = 36F9F0A65F2CA498D739B944D6EFF3DA5EBBA57E7D9C41598A2B0E4380F3CF4B479EC2348D015FFE6256273511154AFCF3B4B4BF09D6C4744FDD0F62D75079D440706B05 -Out = 140684e982eb089cda2fac32abb79b35647fae68055ab533252b3c2a10207aa2240c70cbf3d7d81bca42c03b725b9b6b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A +Out = 2F6BD98F71F935EEB12216F11964171DE85A9BA01EF4770BBB1B9C1C27C51A50354FECB5F1C37C7E207D940355A1F099 -In = ABC87763CAE1CA98BD8C5B82CABA54AC83286F87E9610128AE4DE68AC95DF5E329C360717BD349F26B872528492CA7C94C2C1E1EF56B74DBB65C2AC351981FDB31D06C77A4 -Out = 5fb98a996b6be084fdc472038841b66b0cb34a811d307fefb1e9b1239e43422c30f8bb8f4fb9c4f1fb98eaa17b4785fb +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B +Out = 99DE86DE7A2973FE64726E216891F5EBBC64F2F6931F0E15082BF3EAC044C9BCEB2FABD3DC1CCCEDBC7F80313BE5B5F6 -In = 94F7CA8E1A54234C6D53CC734BB3D3150C8BA8C5F880EAB8D25FED13793A9701EBE320509286FD8E422E931D99C98DA4DF7E70AE447BAB8CFFD92382D8A77760A259FC4FBD72 -Out = dd218a6dc9368dcf92a39e786a331fcefda5fc43037d08370341029eb97476c4c0bbb54fe257bc350083a1cae44bcfd7 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C +Out = C148F35C0FB796AC801F5FBD4ACE22C52E86013E123A65F2946EBCB0BE7B020019AE4E3EDE83AF60628E076CBA4BC4FF -In = 13BD2811F6ED2B6F04FF3895ACEED7BEF8DCD45EB121791BC194A0F806206BFFC3B9281C2B308B1A729CE008119DD3066E9378ACDCC50A98A82E20738800B6CDDBE5FE9694AD6D -Out = cf4e11aa284f233e4135bdea3f8fa908e316876263ffa352de264cfb476b8164544dadd93116949302596be3ee9ee170 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D +Out = EB059F7B0CE4C6A2BABDF8C8B0CD9E1E17AF300AA403CD35A6A98A5A73CCEEC80DFC80B6F76BECA4D634A4E0B996E1A8 -In = 1EED9CBA179A009EC2EC5508773DD305477CA117E6D569E66B5F64C6BC64801CE25A8424CE4A26D575B8A6FB10EAD3FD1992EDDDEEC2EBE7150DC98F63ADC3237EF57B91397AA8A7 -Out = eba5f7f07f25d2cdc1f49b93d1228039c95d4ae6341660ac99519006ba6c86ecb6c38f9725b89ff4249fe344dae9ec32 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E +Out = 16BB371B2BCCA20F406442146AB47467ED37A24D1E51115C2ED5C10B31435BB9FB5CD4025156E428B5A57701EC5DBF3C -In = BA5B67B5EC3A3FFAE2C19DD8176A2EF75C0CD903725D45C9CB7009A900C0B0CA7A2967A95AE68269A6DBF8466C7B6844A1D608AC661F7EFF00538E323DB5F2C644B78B2D48DE1A08AA -Out = 18e5c463197099f4186238f6aaad32a04d4096a594db0563e1e7c5cb7f12b598dba4bc7cd491852f83273e14f7f8f42d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F +Out = 11C8E1A6AD99F75BD0B8DF1530549C6BF2E72D64E6703535AD06512417B0F335DFE07E63CCB8C5CF99D76EE1F653F609 -In = 0EFA26AC5673167DCACAB860932ED612F65FF49B80FA9AE65465E5542CB62075DF1C5AE54FBA4DB807BE25B070033EFA223BDD5B1D3C94C6E1909C02B620D4B1B3A6C9FED24D70749604 -Out = e23b48aae58896cc35fe65321c526a44191cc8ef78c1759785e85da854f953a5cea15a946fbb019f4db46ad1c496b2fa +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40 +Out = 31466A2F0FF943F08D69924B1049181670949CECC738075F410A6EF41A8E25BD2A8DF14829701637ABA4C97199EFF213 -In = BBFD933D1FD7BF594AC7F435277DC17D8D5A5B8E4D13D96D2F64E771ABBD51A5A8AEA741BECCBDDB177BCEA05243EBD003CFDEAE877CCA4DA94605B67691919D8B033F77D384CA01593C1B -Out = e11b87bd2c1a84e1b8a3b295f884b2160981ae9632a64648c6be70d899e97f7ba3ff7cc71bf57c95295e6ce97b8a3ea4 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F4041 +Out = A1390F24E088B0EFDCFB6B4E92F5086EDC10B6C217D629FE81A1C0A52481BF018341B183A48E1ABFD710FA1F61887E57 -In = 90078999FD3C35B8AFBF4066CBDE335891365F0FC75C1286CDD88FA51FAB94F9B8DEF7C9AC582A5DBCD95817AFB7D1B48F63704E19C2BAA4DF347F48D4A6D603013C23F1E9611D595EBAC37C -Out = 7dfac9bb5fe6529594e6c651b570985e8e64360c8a9c7846ab89b58c4db63bef3cd947e946055aef427524aa451d1e05 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142 +Out = D00AFAECF9006508F47E6AD3D9E575EF5DFC358B0D2576AFA98C965943376910DC8BDC23CEB47D485B933BEF350AD788 -In = 64105ECA863515C20E7CFBAA0A0B8809046164F374D691CDBD6508AAABC1819F9AC84B52BAFC1B0FE7CDDBC554B608C01C8904C669D8DB316A0953A4C68ECE324EC5A49FFDB59A1BD6A292AA0E -Out = 35175bae7d827ef11bb897e4a4122a97dbd6fdfcd33a19dbedeb6eee35ebb5e0ae5a7e50758bce0cd56ee0263ed2909c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40414243 +Out = D2AD61644DD08EBB11133FD86F973DD55DE4E67EA347B53E5A8A9091D7095F5F63C332673F291FA2CC0F9A924B60CB58 -In = D4654BE288B9F3B711C2D02015978A8CC57471D5680A092AA534F7372C71CEAAB725A383C4FCF4D8DEAA57FCA3CE056F312961ECCF9B86F14981BA5BED6AB5B4498E1F6C82C6CAE6FC14845B3C8A -Out = 7cd1b977981a9eefc9fe86a2b708b69a18f0902a7994564447b77875a80f5a003441850659a750a9c45ab74d728df571 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F4041424344 +Out = 6CEA68549CAD3A2729AF0F3D2E468FD95BAEFFBDB1BEDFFE5116A97938E49A6F1224B6F5090CC8288EDC176A8D925ABD -In = 12D9394888305AC96E65F2BF0E1B18C29C90FE9D714DD59F651F52B88B3008C588435548066EA2FC4C101118C91F32556224A540DE6EFDDBCA296EF1FB00341F5B01FECFC146BDB251B3BDAD556CD2 -Out = abd67da7fa1faac0855e7fd9515a08816ae3c7087c28e49ad763ce7c14b2ada4f9950412e35e90cb9fd8adb6e34cb0ff +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445 +Out = 7A261025C8CD21B6FF423139C14DA4F880B56991F11C39BB9B21FDDE2072C4D572534ED9AE3F1E3FE4959E977E78BCB4 -In = 871A0D7A5F36C3DA1DFCE57ACD8AB8487C274FAD336BC137EBD6FF4658B547C1DCFAB65F037AA58F35EF16AFF4ABE77BA61F65826F7BE681B5B6D5A1EA8085E2AE9CD5CF0991878A311B549A6D6AF230 -Out = 37a079ee9d2f50f6706288d0cdf2cc9e2592aa0b27da61f7282376b6cb1e504ff034fb5e7df6fbfeb4f8de1d967bc45f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40414243444546 +Out = B3C46D3661B30BD2B66494B2DF909BD10D0E9FE4147DF4287EACDF3C2D88074E15E4284C9F9DEB326CA22E6AB35458E6 -In = E90B4FFEF4D457BC7711FF4AA72231CA25AF6B2E206F8BF859D8758B89A7CD36105DB2538D06DA83BAD5F663BA11A5F6F61F236FD5F8D53C5E89F183A3CEC615B50C7C681E773D109FF7491B5CC22296C5 -Out = 1fa6e82b15990eba4a04c730920edd6b249dbb50983b0edef742f37818caadafdbb38cb4ae37bf8d6c64bfe9f387be05 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F4041424344454647 +Out = 09DCA1ADAE2FCF98AB4BCA7AFD2ABC68300E23FFA14345FE4057EE75E362E602EDB5A3F08055410ED5E93AD8B944DE38 -In = E728DE62D75856500C4C77A428612CD804F30C3F10D36FB219C5CA0AA30726AB190E5F3F279E0733D77E7267C17BE27D21650A9A4D1E32F649627638DBADA9702C7CA303269ED14014B2F3CF8B894EAC8554 -Out = 9f4bd47aed0c824e9adba7317a2bc4493455ce7511ce5140ab6251b1e06549937b7b7e5b4b8a18d7f834b243c2e06f95 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748 +Out = 1FAEF87632ECCBA883117E6C1480B5042324071F509755CF6DA179987DB08E47F41C37B1097627750970EC6C72F7A02D -In = 6348F229E7B1DF3B770C77544E5166E081850FA1C6C88169DB74C76E42EB983FACB276AD6A0D1FA7B50D3E3B6FCD799EC97470920A7ABED47D288FF883E24CA21C7F8016B93BB9B9E078BDB9703D2B781B616E -Out = e9c11a55019a9f3e47b23a33b2255e625ca9ff6076dfabb433710cc1b7ba4ffca3c810ec030ff5efc28de1705f6aea10 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40414243444546474849 +Out = D3135CCCE3CD1B90288DAB286F143E05BD02703DBDC4AF12335F23258B6399B80A8E12650A933D4262B419F973F4B72E -In = 4B127FDE5DE733A1680C2790363627E63AC8A3F1B4707D982CAEA258655D9BF18F89AFE54127482BA01E08845594B671306A025C9A5C5B6F93B0A39522DC877437BE5C2436CBF300CE7AB6747934FCFC30AEAAF6 -Out = c936e5c153d411ff269ee9087a4b73e7235ef0696e423ed2fe883ac11e393d566b334502108c0d2fe79bb4666440f344 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A +Out = 6B9709CF76D2C68D0EC2F8DD34CDC700356143A61A3BD0646F9BE30EFF9102F59A5D0ABC5B5306ED6701115C39FDB96E -In = 08461F006CFF4CC64B752C957287E5A0FAABC05C9BFF89D23FD902D324C79903B48FCB8F8F4B01F3E4DDB483593D25F000386698F5ADE7FAADE9615FDC50D32785EA51D49894E45BAA3DC707E224688C6408B68B11 -Out = 56440839424c5e384f8ea87d15fd1de3db7c278cba6ded61cd425ad442f8209d902b93542afb05ad1fe69adbb9376c58 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B +Out = BA648F2BCD568F5D498D9505D2A0CD86B9A073C99AB85D9976E4844BDFE801FAD3AA22515A9FF08EB72FE16FAF4DC81D -In = 68C8F8849B120E6E0C9969A5866AF591A829B92F33CD9A4A3196957A148C49138E1E2F5C7619A6D5EDEBE995ACD81EC8BB9C7B9CFCA678D081EA9E25A75D39DB04E18D475920CE828B94E72241F24DB72546B352A0E4 -Out = 167eb3c251979cb742006be5a5d092a9d5e6adcf8a744fdac305479fe87c54dd67a50ee05c2750f7fd22d3f8c035e15e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C +Out = 433B7557735704CC1E47AAAF1C7C53141E5E2FF6420E3570181524724A09CE73BFE11EA2A7B059C7E18BEE7E5D928125 -In = B8D56472954E31FB54E28FCA743F84D8DC34891CB564C64B08F7B71636DEBD64CA1EDBDBA7FC5C3E40049CE982BBA8C7E0703034E331384695E9DE76B5104F2FBC4535ECBEEBC33BC27F29F18F6F27E8023B0FBB6F563C -Out = 27b313d61d98cd8c26d2613f67c134c5aa7f36c790ea72b7e71431adf74848ed7f6c372f320c839035bf6dd28e897c4a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D +Out = A3DE980310B14F269FEC07D5AD8D0F9CEAB17EDE99F97849F976877027FAA37DD0B492198467C0DE17649167FC80C9CC -In = 0D58AC665FA84342E60CEFEE31B1A4EACDB092F122DFC68309077AED1F3E528F578859EE9E4CEFB4A728E946324927B675CD4F4AC84F64DB3DACFE850C1DD18744C74CECCD9FE4DC214085108F404EAB6D8F452B5442A47D -Out = 28112f192cf39087783f85c8fcf8c59a27dc847563491b31ac0fc49f524ef91649aed06bebca73e36a29ba3a9bf962d6 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E +Out = 363900E86CF859C978F1A7E860AB872BD9AFDBA445EDF044364313BC29BCCFF9C912D065E55288976B5804D0861D1708 -In = 1755E2D2E5D1C1B0156456B539753FF416651D44698E87002DCF61DCFA2B4E72F264D9AD591DF1FDEE7B41B2EB00283C5AEBB3411323B672EAA145C5125185104F20F335804B02325B6DEA65603F349F4D5D8B782DD3469CCD -Out = 1ce9e8fc9a7354464e35a23777c08da27c51270df91c482411c7bc343c64d35134579b6a8947b881f3e43e08d0b45354 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F +Out = 5DF4164BE6F6706D8A3A2FC157725565CABBD520B0B198CC25D3CFBCF2C5FC14D98EC46A226A61C72AA213284FE880FF -In = B180DE1A611111EE7584BA2C4B020598CD574AC77E404E853D15A101C6F5A2E5C801D7D85DC95286A1804C870BB9F00FD4DCB03AA8328275158819DCAD7253F3E3D237AEAA7979268A5DB1C6CE08A9EC7C2579783C8AFC1F91A7 -Out = 74b6586a542126238eb1d85d95cd6d7e65ab9c77320a5a7b247c06d833760a62b74b0d8c181d450ad9c69bffcb0ced4b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50 +Out = DAF3B3602A2B6A0E918C78D72885FCD9B24377879A2E8CBA72DCE8BE3C36FF5DE39D01ADEB4D7DCA8773A3438EA415B3 -In = CF3583CBDFD4CBC17063B1E7D90B02F0E6E2EE05F99D77E24E560392535E47E05077157F96813544A17046914F9EFB64762A23CF7A49FE52A0A4C01C630CFE8727B81FB99A89FF7CC11DCA5173057E0417B8FE7A9EFBA6D95C555F -Out = aff33f2d163c9b76ab432be4bcf97068937979eafb63ebdc1f01b0c0b522d75db35374d9b5df486f22f522449d232a9c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F5051 +Out = 8F9751EA2DA27AA11FBCCB55960A5A5D648D34C81A36EDFC9123255A4529A018AB9230FD07551236422E1B53B06A8BE6 -In = 072FC02340EF99115BAD72F92C01E4C093B9599F6CFC45CB380EE686CB5EB019E806AB9BD55E634AB10AA62A9510CC0672CD3EDDB589C7DF2B67FCD3329F61B1A4441ECA87A33C8F55DA4FBBAD5CF2B2527B8E983BB31A2FADEC7523 -Out = a42b704d2954844f583c5ffbf60a9216e62f25d27ead79910e084c0334376752ab694d99021fc6212a81497770f4f340 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152 +Out = DA5E38532DB4AFBE5C38BDC272889FD8B18B249C9658C6806F8BE25743E89AFA06860C117E1DF6067CDE4E2AA5A359D2 -In = 76EECF956A52649F877528146DE33DF249CD800E21830F65E90F0F25CA9D6540FDE40603230ECA6760F1139C7F268DEBA2060631EEA92B1FFF05F93FD5572FBE29579ECD48BC3A8D6C2EB4A6B26E38D6C5FBF2C08044AEEA470A8F2F26 -Out = 7c55504ff134c929167c45c2c5cbf998295911c93f9a3a612444dfa6b79ab139774b846fa3af601d08f36cbb8d8e0416 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50515253 +Out = E1B0A43B0F121293EBB439C828ABDCADBDAFED717796CD0A8107237158D1F55E77422212B7450CF230CDC49B78F55A09 -In = 7ADC0B6693E61C269F278E6944A5A2D8300981E40022F839AC644387BFAC9086650085C2CDC585FEA47B9D2E52D65A2B29A7DC370401EF5D60DD0D21F9E2B90FAE919319B14B8C5565B0423CEFB827D5F1203302A9D01523498A4DB10374 -Out = 476d2b5fafa7ff08625a9eab779093969bc53dddcfdd8d5b56737aa16b709d498fc214ce592de2c246db42aa00b989ee +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F5051525354 +Out = E0415562EE8DBF78FA3A543D0BCF8823BF3240419ADB0339DBA53AEB565D861448879740051D2831DFB0791D5F537CB6 -In = E1FFFA9826CCE8B86BCCEFB8794E48C46CDF372013F782ECED1E378269B7BE2B7BF51374092261AE120E822BE685F2E7A83664BCFBE38FE8633F24E633FFE1988E1BC5ACF59A587079A57A910BDA60060E85B5F5B6F776F0529639D9CCE4BD -Out = 658e527d34bd40e90d9f2422852ea03ab12f728642e898fd9e0aa46b5687e82e123bb1ac3b7dcb16c62101bb5d03cec9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455 +Out = 2061E08844332CBEE5C5C81C8DB9FCFCBAA9D1B4FDC14FEA69BCB2D4B7CBA02D4D179EF0BAF4D12530584F9AA83EDBC6 -In = 69F9ABBA65592EE01DB4DCE52DBAB90B08FC04193602792EE4DAA263033D59081587B09BBE49D0B49C9825D22840B2FF5D9C5155F975F8F2C2E7A90C75D2E4A8040FE39F63BBAFB403D9E28CC3B86E04E394A9C9E8065BD3C85FA9F0C7891600 -Out = c4bd7c55a4cd6dc5e85ad2dacdfe57b7fdf826d9eb4e85e1a850e014e1e62ace5642a6fefd47865a281e0a95c41272ac +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50515253545556 +Out = 8EB1C337D49DB5ABF181CA2D1EB0D5A3B530BEA1720504C7ECBCF46FE1B0DFE5365EFD119C0BDAE6D92A1F3BFE62F4D8 -In = 38A10A352CA5AEDFA8E19C64787D8E9C3A75DBF3B8674BFAB29B5DBFC15A63D10FAE66CD1A6E6D2452D557967EAAD89A4C98449787B0B3164CA5B717A93F24EB0B506CEB70CBBCB8D72B2A72993F909AAD92F044E0B5A2C9AC9CB16A0CA2F81F49 -Out = 2cfbefa3ecd702b5c557b5031ef0a63273b4d1948476f914a2d8b871ad33eed72079160370d23002474bf1ee010c83d0 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F5051525354555657 +Out = 5389909A11359D68263875199B2D53BC2C473D44BD5ECCC562634DDAED80C6075A35BDA179AA3129EAEC1A11B87AF6A8 -In = 6D8C6E449BC13634F115749C248C17CD148B72157A2C37BF8969EA83B4D6BA8C0EE2711C28EE11495F43049596520CE436004B026B6C1F7292B9C436B055CBB72D530D860D1276A1502A5140E3C3F54A93663E4D20EDEC32D284E25564F624955B52 -Out = 06c9a2326a988d2466a61a1b3805005a335ec87c9801564dd14338b403af209e1d22d514cce4bd4c266ea27f607f01d9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758 +Out = E4991FF9FEA81EA2C32583CB642941BDD7677B1680B63825F2873515C632BAAF313447BC291F03F5F493FDD66DBBC462 -In = 6EFCBCAF451C129DBE00B9CEF0C3749D3EE9D41C7BD500ADE40CDC65DEDBBBADB885A5B14B32A0C0D087825201E303288A733842FA7E599C0C514E078F05C821C7A4498B01C40032E9F1872A1C925FA17CE253E8935E4C3C71282242CB716B2089CCC1 -Out = 79668721ca4ed5e831b3301c6d77aba90f313f79db48dda9af909c0f884007770d17536baf9216f92ad05f9301098c3c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50515253545556575859 +Out = 8CAC77097F7412CE765BC20B900A8DA83BDD561E6F0BCAD987C677A428A42D1D289E0D8B2861135F33FD6AB3F593F4BF -In = 433C5303131624C0021D868A30825475E8D0BD3052A022180398F4CA4423B98214B6BEAAC21C8807A2C33F8C93BD42B092CC1B06CEDF3224D5ED1EC29784444F22E08A55AA58542B524B02CD3D5D5F6907AFE71C5D7462224A3F9D9E53E7E0846DCBB4CE -Out = bf3cbf54e89e717ea4ce44772f2fc784e50b73af277483307051bf33e61dc7f5e24927ac8d853c2ee6ced0e018b5b208 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A +Out = 38C9510779D5AE1F114D0C3C3015C9869923FFC39E38660048735E7ADC4BFC073FD90505754692C3FCCACF40BD5E61E2 -In = A873E0C67CA639026B6683008F7AA6324D4979550E9BCE064CA1E1FB97A30B147A24F3F666C0A72D71348EDE701CF2D17E2253C34D1EC3B647DBCEF2F879F4EB881C4830B791378C901EB725EA5C172316C6D606E0AF7DF4DF7F76E490CD30B2BADF45685F -Out = 7247a15203bf1d4e5a3f73d74bf8dec2a336b93207a6ea32f632a925b25f80fa1fc4ac939beb15b829142a5d0433d472 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B +Out = 4B1D3D0763BB619F0E898E0DD6F06020BE5D1828A798277553D607F273C74498CE505F160F2961B995A1D4959C3D0A69 -In = 006917B64F9DCDF1D2D87C8A6173B64F6587168E80FAA80F82D84F60301E561E312D9FBCE62F39A6FB476E01E925F26BCC91DE621449BE6504C504830AAE394096C8FC7694651051365D4EE9070101EC9B68086F2EA8F8AB7B811EA8AD934D5C9B62C60A4771 -Out = 28878ced06af49e54878ea1fe85160f661757fb578bfedc440cb3f350d5bafd4fdc6ed8c37e6c579e7e4c3817a9876ed +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C +Out = 8820CA3444593CA1896328D90A836B768A271410BAA29D1588D22EF68F2D0F647BD2637279F2C4469FE7DF2625EE1DA3 -In = F13C972C52CB3CC4A4DF28C97F2DF11CE089B815466BE88863243EB318C2ADB1A417CB1041308598541720197B9B1CB5BA2318BD5574D1DF2174AF14884149BA9B2F446D609DF240CE335599957B8EC80876D9A085AE084907BC5961B20BF5F6CA58D5DAB38ADB -Out = 2f6fb3faf7385acdbe889f31b53d64bc223c34ae7a37079cff3cf0fb26534cabf067a86447a08f2113cf174dfaec0126 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D +Out = F5BE62D7CF77289C4D3D656BC1328ED6D32A43B2DC2A9F2615D7C8F97FF3EE28ABED0CA6EE02E9966AE16BD0337E25E6 -In = E35780EB9799AD4C77535D4DDB683CF33EF367715327CF4C4A58ED9CBDCDD486F669F80189D549A9364FA82A51A52654EC721BB3AAB95DCEB4A86A6AFA93826DB923517E928F33E3FBA850D45660EF83B9876ACCAFA2A9987A254B137C6E140A21691E1069413848 -Out = 47e0608ca8f3719763575914859f57d9e4e8cb74409747e1f2f34a10731d96dcb5c35283835cd2d32eb8ef5d151f934d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E +Out = CCD1AE814F18CE64E176D8895364C8B600CE901573079DBC7EEBAD25CA1FCC23B48E7E352BDDB625DF03B2168F097F20 -In = 64EC021C9585E01FFE6D31BB50D44C79B6993D72678163DB474947A053674619D158016ADB243F5C8D50AA92F50AB36E579FF2DABB780A2B529370DAA299207CFBCDD3A9A25006D19C4F1FE33E4B1EAEC315D8C6EE1E730623FD1941875B924EB57D6D0C2EDC4E78D6 -Out = 308cebb832faa2ef1d63ff68c514c9847f2babbeeb90ae724bbc66be69338858cc88005988225b05c57aa32a45af3683 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F +Out = 447502E039A85D13DAC5EC5D009B3D9607D6F0E37BB15A0AF7D1513D63D2DDDDDF4386F642D6D7B2B47DAFC8ACBB4297 -In = 5954BAB512CF327D66B5D9F296180080402624AD7628506B555EEA8382562324CF452FBA4A2130DE3E165D11831A270D9CB97CE8C2D32A96F50D71600BB4CA268CF98E90D6496B0A6619A5A8C63DB6D8A0634DFC6C7EC8EA9C006B6C456F1B20CD19E781AF20454AC880 -Out = 05b695195f956ae904ccace385e84b16efd8672b8c86c03579eb968d6c5332c7df66153699f20b992c3de7ac8678f05c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60 +Out = EF3C7F9D484601A79908B61B8A58049F63A0FE6B33AF31706332C5C4D5003A93D2C4DE7BDEC5EDE6E9A9CF4362BD1CA8 -In = 03D9F92B2C565709A568724A0AFF90F8F347F43B02338F94A03ED32E6F33666FF5802DA4C81BDCE0D0E86C04AFD4EDC2FC8B4141C2975B6F07639B1994C973D9A9AFCE3D9D365862003498513BFA166D2629E314D97441667B007414E739D7FEBF0FE3C32C17AA188A8683 -Out = e2ae6eb4a7d72404fa3d025980e6a1253a1d2cb6998af3f645ec0b7a24bb7a70c48937ca2d47179c6f29c5b41ebdc38c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6061 +Out = BB7E3FD37861E3210AAAE39E2F2F3985B6143BB872BAD077D71E2545D2E9F95A898930BF6EA2C48F2411437D08AF93B1 -In = F31E8B4F9E0621D531D22A380BE5D9ABD56FAEC53CBD39B1FAB230EA67184440E5B1D15457BD25F56204FA917FA48E669016CB48C1FFC1E1E45274B3B47379E00A43843CF8601A5551411EC12503E5AAC43D8676A1B2297EC7A0800DBFEE04292E937F21C005F17411473041 -Out = 596e7214c029c8f839567496b989b1ccb4c79bb835f63099e89a7b0e7fa1b6cb2d52587cd8d33ab025077f8c6c979080 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162 +Out = ED83072604C544B7B8BA03E61D6F9EDE575FB35DB4261B7783773F3DB5B1944D6D9AD484582FC78A0F7505988B5F1A07 -In = 758EA3FEA738973DB0B8BE7E599BBEF4519373D6E6DCD7195EA885FC991D896762992759C2A09002912FB08E0CB5B76F49162AEB8CF87B172CF3AD190253DF612F77B1F0C532E3B5FC99C2D31F8F65011695A087A35EE4EEE5E334C369D8EE5D29F695815D866DA99DF3F79403 -Out = d2608eb2b9a3cf31c9a35d21d4a6100494fa4d11360b11a6e9def5af783a111a52beac700b894c8d46458b0d80d283ff +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60616263 +Out = 313B0350C45772FF6787F1E2831CAFC6C8D77162BE100819E5142F1F398BB0FB3A3123FBFF2C94927F7196386952067C -In = 47C6E0C2B74948465921868804F0F7BD50DD323583DC784F998A93CD1CA4C6EF84D41DC81C2C40F34B5BEE6A93867B3BDBA0052C5F59E6F3657918C382E771D33109122CC8BB0E1E53C4E3D13B43CE44970F5E0C079D2AD7D7A3549CD75760C21BB15B447589E86E8D76B1E9CED2 -Out = bfb2ef53dfde08379153dc7bf747fc2fe14dd1374dc24ece818c8e342419bcf829619714f5203ba14d5f6005f37cc346 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6061626364 +Out = C746055CCDC870C9FD92D249E19641C35C4908EAF0158284562897B21255A84D9051D71E9F3C0C6B5B107D7F080A765B -In = F690A132AB46B28EDFA6479283D6444E371C6459108AFD9C35DBD235E0B6B6FF4C4EA58E7554BD002460433B2164CA51E868F7947D7D7A0D792E4ABF0BE5F450853CC40D85485B2B8857EA31B5EA6E4CCFA2F3A7EF3380066D7D8979FDAC618AAD3D7E886DEA4F005AE4AD05E5065F -Out = c1382f1a23960a36e337519593385567a5283a407ca51b24b946cc56cb03e6d3e82c4ad7a2511311ec7cf15297fbd92e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465 +Out = 39D7DEF4679233C60A2F2AD6C5AB9EFB8741E330A533D15F912BAC27C61C6B26936D6B70FB8723B916FAA61CD0949306 -In = 58D6A99BC6458824B256916770A8417040721CCCFD4B79EACD8B65A3767CE5BA7E74104C985AC56B8CC9AEBD16FEBD4CDA5ADB130B0FF2329CC8D611EB14DAC268A2F9E633C99DE33997FEA41C52A7C5E1317D5B5DAED35EBA7D5A60E45D1FA7EAABC35F5C2B0A0F2379231953322C4E -Out = 3197b70cc0d94d3d0e21642705e67c074ffc4653f8f908917f659c2466dd10909dd41ea27910dcf4ad4e9440238d5bd0 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60616263646566 +Out = 32DA7CC15BE7214D99C928E817C13B87C23837D0F4A0949E8CCCDCF66AD9D66E7329ADEBF4F8322E4737A765E4E5B69A -In = BEFAB574396D7F8B6705E2D5B58B2C1C820BB24E3F4BAE3E8FBCD36DBF734EE14E5D6AB972AEDD3540235466E825850EE4C512EA9795ABFD33F330D9FD7F79E62BBB63A6EA85DE15BEAEEA6F8D204A28956059E2632D11861DFB0E65BC07AC8A159388D5C3277E227286F65FF5E5B5AEC1 -Out = b0516d916263efd61097b58c575252c4dd9196d667a1416cbda066780fd89d4b1b8afd98f64da1034e48d6c947d63c4d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6061626364656667 +Out = D847BBB38306F215A96B2D991AE3ED8165BD9E25561DDC000582D968D54318D82F0FE0E331E933CF2BEB9D31DD3EA4A9 -In = 8E58144FA9179D686478622CE450C748260C95D1BA43B8F9B59ABECA8D93488DA73463EF40198B4D16FB0B0707201347E0506FF19D01BEA0F42B8AF9E71A1F1BD168781069D4D338FDEF00BF419FBB003031DF671F4A37979564F69282DE9C65407847DD0DA505AB1641C02DEA4F0D834986 -Out = aa7ee456bf6f53ec9d8c665cc3242e403d2bbaf56c85d79c513ee0c57d4954c95c08d6fc268ec9b46dfff62a7d149dce +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768 +Out = CB186B1873DF28A7341697806DD13B3A79032BDC9DE6CFD5F8DABB380AA7C7CAE453BC6CC53FE0ADBB2A13861F9915AD -In = B55C10EAE0EC684C16D13463F29291BF26C82E2FA0422A99C71DB4AF14DD9C7F33EDA52FD73D017CC0F2DBE734D831F0D820D06D5F89DACC485739144F8CFD4799223B1AFF9031A105CB6A029BA71E6E5867D85A554991C38DF3C9EF8C1E1E9A7630BE61CAABCA69280C399C1FB7A12D12AEFC -Out = 5f37e678531d5ba4f936e9f67a21aa73bf2496ec2dbec80c47355b0656af45678603ff8c32b7b7459c96f96f28acfc27 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60616263646566676869 +Out = 03C82565471F18CE9EA9BC5C945179D1B05AC9D1B7D42213E4C6D331E98019F5429CD4C68449E20E17112628E35B449C -In = 2EEEA693F585F4ED6F6F8865BBAE47A6908AECD7C429E4BEC4F0DE1D0CA0183FA201A0CB14A529B7D7AC0E6FF6607A3243EE9FB11BCF3E2304FE75FFCDDD6C5C2E2A4CD45F63C962D010645058D36571404A6D2B4F44755434D76998E83409C3205AA1615DB44057DB991231D2CB42624574F545 -Out = 0b2d57740bbef251edbc4d02f0415ca7a7eaa7b94d3f74fe3bd4d4d225b11d36f8a6296a4ab94b381c182cd29f14bc9e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A +Out = BEDBBB184452D605455FF4C247C349B589CE3BD2D9548CF7D70C33347BC3EC90656E3A4D8C9AFEFC42356240A4B396EF -In = DAB11DC0B047DB0420A585F56C42D93175562852428499F66A0DB811FCDDDAB2F7CDFFED1543E5FB72110B64686BC7B6887A538AD44C050F1E42631BC4EC8A9F2A047163D822A38989EE4AAB01B4C1F161B062D873B1CFA388FD301514F62224157B9BEF423C7783B7AAC8D30D65CD1BBA8D689C2D -Out = 19cb41cdd2b9ae126512d3007bc5a38e7fc70eed6f777231c9487bf21ebe2631fe471221124a8a26e5e8a7a8f77cb15b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B +Out = B9227B25C189C98256B061A0EB3A9B95CB65A1EADA1CF7F99EFFEA0A3D13153CDD0FA6C5187C6EA3BC6358BEE389DA76 -In = 42E99A2F80AEE0E001279A2434F731E01D34A44B1A8101726921C0590C30F3120EB83059F325E894A5AC959DCA71CE2214799916424E859D27D789437B9D27240BF8C35ADBAFCECC322B48AA205B293962D858652ABACBD588BCF6CBC388D0993BD622F96ED54614C25B6A9AA527589EAAFFCF17DDF7 -Out = b70eb35115783e621b8464de1c2b16bfe053b628faef100a663f1fde11fd87b06690f1cf7cefab9f94febd80234c4590 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C +Out = F66BEA2EDF972A29C888BB2E213E30C3583F07D2FC4231A4FF39CA1B8AFE14C4F7702D6F24CB69CC119A5709AA32DCBD -In = 3C9B46450C0F2CAE8E3823F8BDB4277F31B744CE2EB17054BDDC6DFF36AF7F49FB8A2320CC3BDF8E0A2EA29AD3A55DE1165D219ADEDDB5175253E2D1489E9B6FDD02E2C3D3A4B54D60E3A47334C37913C5695378A669E9B72DEC32AF5434F93F46176EBF044C4784467C700470D0C0B40C8A088C815816 -Out = 6949030b1c9f7c4e06348c6f09e966a38aa7b05afe026649521ed7f1ec60bee218c78ca2bf51b388ee8b54a2cf645d61 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D +Out = F1D96469C28B5B74EB4451F775813DF3512C212B9B9B07A3215FCD3C98A392EDF752FF95D0FA8D991E82B5EB3C62219E -In = D1E654B77CB155F5C77971A64DF9E5D34C26A3CAD6C7F6B300D39DEB1910094691ADAA095BE4BA5D86690A976428635D5526F3E946F7DC3BD4DBC78999E653441187A81F9ADCD5A3C5F254BC8256B0158F54673DCC1232F6E918EBFC6C51CE67EAEB042D9F57EEC4BFE910E169AF78B3DE48D137DF4F2840 -Out = 333dd84e1d4330e3ee1415cad6376b0bc2d15dc020541d195bd6fdab54e49c1ae979bd7e12095d4ba2f9134aa4dd7d3f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E +Out = D443F41B6461562048765AE37FEB6C63683800C4C7AD7DC4C8D7D6B67A689F8435FBB2940506D6722A09C0F3E46D6C64 -In = 626F68C18A69A6590159A9C46BE03D5965698F2DAC3DE779B878B3D9C421E0F21B955A16C715C1EC1E22CE3EB645B8B4F263F60660EA3028981EEBD6C8C3A367285B691C8EE56944A7CD1217997E1D9C21620B536BDBD5DE8925FF71DEC6FBC06624AB6B21E329813DE90D1E572DFB89A18120C3F606355D25 -Out = 863bd57c046ef94200b6b2962311a68534409127be5d6edcbce9d72ca35a49ca0978d10f4ad43da1791d5bea3cf9aa21 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F +Out = 40790C95C391EB482BB315E6153838AB6622BB651EBFE979B09B7456EF021C811FB216027D063732C5B83F0AD639EBC3 -In = 651A6FB3C4B80C7C68C6011675E6094EB56ABF5FC3057324EBC6477825061F9F27E7A94633ABD1FA598A746E4A577CAF524C52EC1788471F92B8C37F23795CA19D559D446CAB16CBCDCE90B79FA1026CEE77BF4AB1B503C5B94C2256AD75B3EAC6FD5DCB96ACA4B03A834BFB4E9AF988CECBF2AE597CB9097940 -Out = 2bf1b8e98338347f3c6a8646d8a591eb32d5582c7b5518545f511295cc0aacffe9581ce513cb78e0992133fcd975d757 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70 +Out = 55985DE48CFD269E07E5D2610D64FE61F14E874098E60B681C95393B7F2576A0842F086DA49CB392195CC1333166EB2A -In = 8AAF072FCE8A2D96BC10B3C91C809EE93072FB205CA7F10ABD82ECD82CF040B1BC49EA13D1857815C0E99781DE3ADBB5443CE1C897E55188CEAF221AA9681638DE05AE1B322938F46BCE51543B57ECDB4C266272259D1798DE13BE90E10EFEC2D07484D9B21A3870E2AA9E06C21AA2D0C9CF420080A80A91DEE16F -Out = 67248a52a271f44c7c252ca17f932a443f389a9d80d010986bd94ec7a2e78ed523ec20515446089885f708d41d442d16 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F7071 +Out = AFBD441595F4AF79546D0F56B17CA57A3991ECDAC87A029E3A51FF4150856F19DC7D97DF9AF3E92A3E75ED5993D1F259 -In = 53F918FD00B1701BD504F8CDEA803ACCA21AC18C564AB90C2A17DA592C7D69688F6580575395551E8CD33E0FEF08CA6ED4588D4D140B3E44C032355DF1C531564D7F4835753344345A6781E11CD5E095B73DF5F82C8AE3AD00877936896671E947CC52E2B29DCD463D90A0C9929128DA222B5A211450BBC0E02448E2 -Out = 00ea27f40d5addcda90a18162bb57c263567c51ffa97918c052014a8ee9e5c93f821f2c8d207d8ba183434d08f5ccc89 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172 +Out = B3D06CC9DFF506E46701E6E66B69CF0B47167E46407AA348522CD913CABC0DBF1E0CDE6AA1FD0FD986C65393272CCBCD -In = A64599B8A61B5CCEC9E67AED69447459C8DA3D1EC6C7C7C82A7428B9B584FA67E90F68E2C00FBBED4613666E5168DA4A16F395F7A3C3832B3B134BFC9CBAA95D2A0FE252F44AC6681EB6D40AB91C1D0282FED6701C57463D3C5F2BB8C6A7301FB4576AA3B5F15510DB8956FF77478C26A7C09BEA7B398CFC83503F538E -Out = e07dd123cfe81a3154facc2df52db9ef3fcc91eafc8b8ba2bc6673942df4ed15018c6ccdb0c1926f71eed24fd8781366 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273 +Out = 0FFD81EAC822E16262F242DEFEAF1FBC864F7BAEB4C75DF2A6D6B88F72C8CF7314F8C57463D27B85D123C1F5E0ECBDB0 -In = 0E3AB0E054739B00CDB6A87BD12CAE024B54CB5E550E6C425360C2E87E59401F5EC24EF0314855F0F56C47695D56A7FB1417693AF2A1ED5291F2FEE95F75EED54A1B1C2E81226FBFF6F63ADE584911C71967A8EB70933BC3F5D15BC91B5C2644D9516D3C3A8C154EE48E118BD1442C043C7A0DBA5AC5B1D5360AAE5B9065 -Out = 2bb324dbb4a953d856dc6d7b481969e2ce0bac5b8226e35ed63012dbba5d2ba280fc4adc017f09d6604ff0ea8ee140cb +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F7071727374 +Out = 4F2717491FF76CEBD2F5F2387B16DAE119F584C8EF4C503CC4CA980B0C780F7EE8D74200EA147F0E2443D194293CD0B6 -In = A62FC595B4096E6336E53FCDFC8D1CC175D71DAC9D750A6133D23199EAAC288207944CEA6B16D27631915B4619F743DA2E30A0C00BBDB1BBB35AB852EF3B9AEC6B0A8DCC6E9E1ABAA3AD62AC0A6C5DE765DE2C3711B769E3FDE44A74016FFF82AC46FA8F1797D3B2A726B696E3DEA5530439ACEE3A45C2A51BC32DD055650B -Out = 5fd63c99f250a407683169c79e5ff49b2471e681ff0b7dc603ba09ba216b2429dcb1cfc34540efc9cf58a211bd026f30 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475 +Out = 926BE35284BB3E56620FAB3224B5213E06FBAEC1D3C8642E8ED86066AE531E6B0547320CD8266CCB14E8595AC2AAE69B -In = 2B6DB7CED8665EBE9DEB080295218426BDAA7C6DA9ADD2088932CDFFBAA1C14129BCCDD70F369EFB149285858D2B1D155D14DE2FDB680A8B027284055182A0CAE275234CC9C92863C1B4AB66F304CF0621CD54565F5BFF461D3B461BD40DF28198E3732501B4860EADD503D26D6E69338F4E0456E9E9BAF3D827AE685FB1D817 -Out = 81ff81eb6aa70ea32ba23c0a453c8ae3877ec3a9a92a30bc884d22b4413a833019f78b2c4b626e0f275d577c8273a41c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576 +Out = EDDE7C594252AC8FF98A48E682D6290B0381C53835373F4F9F2BB5923DC020878D83F2FDF3B9412424D5C70EA1BF183F -In = 10DB509B2CDCABA6C062AE33BE48116A29EB18E390E1BBADA5CA0A2718AFBCD23431440106594893043CC7F2625281BF7DE2655880966A23705F0C5155C2F5CCA9F2C2142E96D0A2E763B70686CD421B5DB812DACED0C6D65035FDE558E94F26B3E6DDE5BD13980CC80292B723013BD033284584BFF27657871B0CF07A849F4AE2 -Out = 77c517179319728d7379b1f36f50d8550c3c8f2e6edeb34310fb497156bbbb5adc13090d3f3cc7d145d49dd5f1ba75f2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F7071727374757677 +Out = 2460963A3BA372E93A397D0BAB1CF3E4141785818AAA7BF92AC3658E45B6B55D1C9D1705D46ABC69E0F2EBCEA48A0464 -In = 9334DE60C997BDA6086101A6314F64E4458F5FF9450C509DF006E8C547983C651CA97879175AABA0C539E82D05C1E02C480975CBB30118121061B1EBAC4F8D9A3781E2DB6B18042E01ECF9017A64A0E57447EC7FCBE6A7F82585F7403EE2223D52D37B4BF426428613D6B4257980972A0ACAB508A7620C1CB28EB4E9D30FC41361EC -Out = e779c1eeddae094b9906a7c29659592be49dbd85c13c65e9b142796779c121f6364fd67c370af0b1301fc9f5558bc2a3 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778 +Out = F699225B801D19D3A7BB057B657A8BFD2D7E5AA8CB858EF05551319D4BE76D25865928CDC4C53464AA6D12785B870EA0 -In = E88AB086891693AA535CEB20E64C7AB97C7DD3548F3786339897A5F0C39031549CA870166E477743CCFBE016B4428D89738E426F5FFE81626137F17AECFF61B72DBEE2DC20961880CFE281DFAB5EE38B1921881450E16032DE5E4D55AD8D4FCA609721B0692BAC79BE5A06E177FE8C80C0C83519FB3347DE9F43D5561CB8107B9B5EDC -Out = 5b3a9b041191e54cfed76930387205f619577c28f043e778f3d6812b40e48a6a23c73ce1e2d4fa883dbbe334f3597fe1 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576777879 +Out = E4CA222F516E44719D874BEF8622C600DDBC475F8C80D664329D79372AA070900D84912C885F1EAC370D13C28D12FB04 -In = FD19E01A83EB6EC810B94582CB8FBFA2FCB992B53684FB748D2264F020D3B960CB1D6B8C348C2B54A9FCEA72330C2AAA9A24ECDB00C436ABC702361A82BB8828B85369B8C72ECE0082FE06557163899C2A0EFA466C33C04343A839417057399A63A3929BE1EE4805D6CE3E5D0D0967FE9004696A5663F4CAC9179006A2CEB75542D75D68 -Out = 28859002b9f0a4859080eac9369cdc70f49ab61bd43e25d9ee56404e93023e4456a7f1e6d5ca4704f3ea3dcb8bd6bfdf +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A +Out = BD05EF18A7914C1C7747568B9E5FD61F7B0384E0B43FE3DFDE6C01E38A503AC98F9F4D8150B2EB2DA889A3F4B10B256D -In = 59AE20B6F7E0B3C7A989AFB28324A40FCA25D8651CF1F46AE383EF6D8441587AA1C04C3E3BF88E8131CE6145CFB8973D961E8432B202FA5AF3E09D625FAAD825BC19DA9B5C6C20D02ABDA2FCC58B5BD3FE507BF201263F30543819510C12BC23E2DDB4F711D087A86EDB1B355313363A2DE996B891025E147036087401CCF3CA7815BF3C49 -Out = 6e2497f9f88ea45bd8d142fb2f09b3db66e994fb4b3169cd329ffab6ea658939bd54337d361c2d87b139ea8659c4d91f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B +Out = 6C701A9BE9FEA1EEA3B702A3183C35BCE6B890BC623E8FAF3CD086DFE563AA7879B4ABF639FED909307F03AE4E7D65A8 -In = 77EE804B9F3295AB2362798B72B0A1B2D3291DCEB8139896355830F34B3B328561531F8079B79A6E9980705150866402FDC176C05897E359A6CB1A7AB067383EB497182A7E5AEF7038E4C96D133B2782917417E391535B5E1B51F47D8ED7E4D4025FE98DC87B9C1622614BFF3D1029E68E372DE719803857CA52067CDDAAD958951CB2068CC6 -Out = 0e51f4461bfe88ba37b85b434b1e62e9284e6cc9865ab9abbffe5a0fe10dc11910d2e60674f808fb8b8c4fa02bd4da08 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C +Out = E87693FE710A95FBE1B6DFF46B54AFC430D290B2C7FDBC0BF551C1E8F4F40B44B68CEED6A18B0D7C4A48648ED97FCA99 -In = B771D5CEF5D1A41A93D15643D7181D2A2EF0A8E84D91812F20ED21F147BEF732BF3A60EF4067C3734B85BC8CD471780F10DC9E8291B58339A677B960218F71E793F2797AEA349406512829065D37BB55EA796FA4F56FD8896B49B2CD19B43215AD967C712B24E5032D065232E02C127409D2ED4146B9D75D763D52DB98D949D3B0FED6A8052FBB -Out = c1f47c367e8894b5a1ceb6dde9dd12d86550e2e438de4b2a1285d2f0f2c61d7a30f131286172708038505d9817892bef +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D +Out = B371595A7AF71BF1E8FD22BD950DDAB0FD642F5D3FED006A6E66D1E71E47867CC74A7018BE3FCA6829053454A52DF1F6 -In = B32D95B0B9AAD2A8816DE6D06D1F86008505BD8C14124F6E9A163B5A2ADE55F835D0EC3880EF50700D3B25E42CC0AF050CCD1BE5E555B23087E04D7BF9813622780C7313A1954F8740B6EE2D3F71F768DD417F520482BD3A08D4F222B4EE9DBD015447B33507DD50F3AB4247C5DE9A8ABD62A8DECEA01E3B87C8B927F5B08BEB37674C6F8E380C04 -Out = 14c149e65a8fa2d4198a9ddb4a6919ed5e2e4b547b509f41e71bfa5312d25f04f4468b73d9a7625f3880789ea9d5ed6b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E +Out = 0C046DCE7C3ED50A4BE7ECA79FDEB9D821EBE28F5D82ACADAC3D7449E6892789313679018034A2CE6B42F006C02F19EE -In = 04410E31082A47584B406F051398A6ABE74E4DA59BB6F85E6B49E8A1F7F2CA00DFBA5462C2CD2BFDE8B64FB21D70C083F11318B56A52D03B81CAC5EEC29EB31BD0078B6156786DA3D6D8C33098C5C47BB67AC64DB14165AF65B44544D806DDE5F487D5373C7F9792C299E9686B7E5821E7C8E2458315B996B5677D926DAC57B3F22DA873C601016A0D -Out = 458cf8ad1f1bd64e6303a0441f9199f678ba96481ccd2a4e6ea3f6c04698e09dc8483c2a50268f474b601c4fdf8a5f4f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F +Out = A2C2ACF7CE4079C02B7F38E2EF33BFF531A31A7C7EFFE712C5348B4D616C0CBA9B152679317984EC632D0C70EB11EECE -In = 8B81E9BADDE026F14D95C019977024C9E13DB7A5CD21F9E9FC491D716164BBACDC7060D882615D411438AEA056C340CDF977788F6E17D118DE55026855F93270472D1FD18B9E7E812BAE107E0DFDE7063301B71F6CFE4E225CAB3B232905A56E994F08EE2891BA922D49C3DAFEB75F7C69750CB67D822C96176C46BD8A29F1701373FB09A1A6E3C7158F -Out = 9feaa15675450b9d0e01363941b7c6866f7cd3869835c9992f1240fb5796e8f5871e916270c5ddc8cac9ae2243c014b2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F80 +Out = A95DB6E5CCD191793AD20179BFD63E8C7AEDF0CC1084549F73127E3FCCC738B405AC2A93D692E76214320089121073E5 -In = FA6EED24DA6666A22208146B19A532C2EC9BA94F09F1DEF1E7FC13C399A48E41ACC2A589D099276296348F396253B57CB0E40291BD282773656B6E0D8BEA1CDA084A3738816A840485FCF3FB307F777FA5FEAC48695C2AF4769720258C77943FB4556C362D9CBA8BF103AEB9034BAA8EA8BFB9C4F8E6742CE0D52C49EA8E974F339612E830E9E7A9C29065 -Out = 64324fa938bf221081ea4b91479d8b9b33024fb11f84f9bf0d63330655b1f67517ae87492e4ec2a851e17fdff06f61c8 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F8081 +Out = 6ED4BDD216425D5E91458C254152ACBFE8F4751BE0045401C1CBE1E2979ACFAB8384E66816D1CDE56418A9FECBE76B3F -In = 9BB4AF1B4F09C071CE3CAFA92E4EB73CE8A6F5D82A85733440368DEE4EB1CBC7B55AC150773B6FE47DBE036C45582ED67E23F4C74585DAB509DF1B83610564545642B2B1EC463E18048FC23477C6B2AA035594ECD33791AF6AF4CBC2A1166ABA8D628C57E707F0B0E8707CAF91CD44BDB915E0296E0190D56D33D8DDE10B5B60377838973C1D943C22ED335E -Out = b188654a91b0856dd4afc68270fab9511b11d78bdc2847b04a376f3746afe7351cd1b27fde0d655dc6b5451525f4731a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182 +Out = 18391E55459C665CC076D3152EE0CFAA5C498595298C4D82E2E160C7ACEA15D7FD749E1D5566A104E7F24344E0BC142E -In = 2167F02118CC62043E9091A647CADBED95611A521FE0D64E8518F16C808AB297725598AE296880A773607A798F7C3CFCE80D251EBEC6885015F9ABF7EAABAE46798F82CB5926DE5C23F44A3F9F9534B3C6F405B5364C2F8A8BDC5CA49C749BED8CE4BA48897062AE8424CA6DDE5F55C0E42A95D1E292CA54FB46A84FBC9CD87F2D0C9E7448DE3043AE22FDD229 -Out = 305c8ea9a4ae2ad5b86155dd07b0c92d5504fd29dad87f74fb4fee93dbb09c5f2ec053e87caaef71eb6bcde048af2d9e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F80818283 +Out = 0C841025777221D8F7E1271E03687A380250B269236CF5535D5CBD77A480FCE6872E4885F62DDA17136C9AE05F07FAA4 -In = 94B7FA0BC1C44E949B1D7617D31B4720CBE7CA57C6FA4F4094D4761567E389ECC64F6968E4064DF70DF836A47D0C713336B5028B35930D29EB7A7F9A5AF9AD5CF441745BAEC9BB014CEEFF5A41BA5C1CE085FEB980BAB9CF79F2158E03EF7E63E29C38D7816A84D4F71E0F548B7FC316085AE38A060FF9B8DEC36F91AD9EBC0A5B6C338CBB8F6659D342A24368CF -Out = db68c56feca532840c9464b4a4f5bb571231c0af976bb13c0d2d4a76e853db4a91252bf6bd2a8a3acd6fee6d8b017f53 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F8081828384 +Out = 0E6A87DDCA034B11F6D221F17F53052D85B22968ED7A2E2DFCD13DC1DE64C27639A1DDF29ABEBFDF02DFAAF687E3108F -In = EA40E83CB18B3A242C1ECC6CCD0B7853A439DAB2C569CFC6DC38A19F5C90ACBF76AEF9EA3742FF3B54EF7D36EB7CE4FF1C9AB3BC119CFF6BE93C03E208783335C0AB8137BE5B10CDC66FF3F89A1BDDC6A1EED74F504CBE7290690BB295A872B9E3FE2CEE9E6C67C41DB8EFD7D863CF10F840FE618E7936DA3DCA5CA6DF933F24F6954BA0801A1294CD8D7E66DFAFEC -Out = d82141569ad9941c0e027c8476e2b807caf6de06cf3a46db2b14346a5b74f219a650375efb820e577af749b9e086b9c1 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485 +Out = 8CAB0302A4402B1E9CE08E4D87F6B9D82F17424C5DDA59A2670FC29DD8F504CC7B15474BA179DB20DCF3D3580EBAAFCB -In = 157D5B7E4507F66D9A267476D33831E7BB768D4D04CC3438DA12F9010263EA5FCAFBDE2579DB2F6B58F911D593D5F79FB05FE3596E3FA80FF2F761D1B0E57080055C118C53E53CDB63055261D7C9B2B39BD90ACC32520CBBDBDA2C4FD8856DBCEE173132A2679198DAF83007A9B5C51511AE49766C792A29520388444EBEFE28256FB33D4260439CBA73A9479EE00C63 -Out = ff47d48754e79dbb40bb3c73e0030ebfb0001d22c8a2d764853c0ac1e745caa84eb4b189aca330032ca3b1ffafe99d30 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F80818283848586 +Out = 17BFC685EE89A404752DDCCD0BED6B9B0696C0F968A2D96749A2BEDE10BBF8E57F4B1AEDC9E8DE723FFCC22E3F309C81 -In = 836B34B515476F613FE447A4E0C3F3B8F20910AC89A3977055C960D2D5D2B72BD8ACC715A9035321B86703A411DDE0466D58A59769672AA60AD587B8481DE4BBA552A1645779789501EC53D540B904821F32B0BD1855B04E4848F9F8CFE9EBD8911BE95781A759D7AD9724A7102DBE576776B7C632BC39B9B5E19057E226552A5994C1DBB3B5C7871A11F5537011044C53 -Out = c575cfa29a77dba2ad7a49244151363c1dc0427644916ce322fa91b80304ee90e5c16811315004efaab003a32f636bea +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F8081828384858687 +Out = ADF0CC27BB9D359090D277C3A47A3C96E1F0C6598E9ABF460ED128BD39565AE464B39CD6A75DBCA75298181AF1356C94 -In = CC7784A4912A7AB5AD3620AAB29BA87077CD3CB83636ADC9F3DC94F51EDF521B2161EF108F21A0A298557981C0E53CE6CED45BDF782C1EF200D29BAB81DD6460586964EDAB7CEBDBBEC75FD7925060F7DA2B853B2B089588FA0F8C16EC6498B14C55DCEE335CB3A91D698E4D393AB8E8EAC0825F8ADEBEEE196DF41205C011674E53426CAA453F8DE1CBB57932B0B741D4C6 -Out = aa92f1067b9851724b56947bdab893f1ee77cc4eb05eecc8323fa2c3f419fc79061022c61ba11b7c3114b8a3461cc3f5 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788 +Out = 02AF7C886E86140E74F6635431732ED6CF9FA4EA8D27174FDBAE9A4D92EB03383F0419B26508C556D6A0A1E0777EF38F -In = 7639B461FFF270B2455AC1D1AFCE782944AEA5E9087EB4A39EB96BB5C3BAAF0E868C8526D3404F9405E79E77BFAC5FFB89BF1957B523E17D341D7323C302EA7083872DD5E8705694ACDDA36D5A1B895AAA16ECA6104C82688532C8BFE1790B5DC9F4EC5FE95BAED37E1D287BE710431F1E5E8EE105BC42ED37D74B1E55984BF1C09FE6A1FA13EF3B96FAEAED6A2A1950A12153 -Out = b3730a58519c31a4c78ae95f5625e52c0537604ab0f63b3dffe4455224949da3db312ec17a0d9492bbe37de27608c8b4 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F80818283848586878889 +Out = 806150B2F09CAAC8311C4E1BDBE6A1FDE29693AFE7DD49C9929A85E593F2184CC27C895633FAB658C7D20C6EE545B827 -In = EB6513FC61B30CFBA58D4D7E80F94D14589090CF1D80B1DF2E68088DC6104959BA0D583D585E9578AB0AEC0CF36C48435EB52ED9AB4BBCE7A5ABE679C97AE2DBE35E8CC1D45B06DDA3CF418665C57CBEE4BBB47FA4CAF78F4EE656FEC237FE4EEBBAFA206E1EF2BD0EE4AE71BD0E9B2F54F91DAADF1FEBFD7032381D636B733DCB3BF76FB14E23AFF1F68ED3DBCF75C9B99C6F26 -Out = acc1af0d1bf9d27b1f3b221fae0c826c56aa151501e3e767d45bd793c2ffc71ebc7fbec93bcb33a52baa5265eed6cf94 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A +Out = D0A7B0CA39327EE0BD4AE02B870903A1400B7A8D28016834B4F0F4C49A5F62581D4D4A8FE658111B80E6A6C1F2C9B691 -In = 1594D74BF5DDE444265D4C04DAD9721FF3E34CBF622DAF341FE16B96431F6C4DF1F760D34F296EB97D98D560AD5286FEC4DCE1724F20B54FD7DF51D4BF137ADD656C80546FB1BF516D62EE82BAA992910EF4CC18B70F3F8698276FCFB44E0EC546C2C39CFD8EE91034FF9303058B4252462F86C823EB15BF481E6B79CC3A02218595B3658E8B37382BD5048EAED5FD02C37944E73B -Out = f45709b474091dd6e28d1df60bbab3fdcd74532620b516b6968aa2365c1a165f6e02cbc7051a3e31a7077aea89aa6149 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B +Out = DC19DAEC1D86ECD2D9FAE6A1BD4BFE3A7C4EDD941DEFD83A92AB50C2C48DF2B9911ABF2B91CFFA11BB839692BD80B2DA -In = 4CFA1278903026F66FEDD41374558BE1B585D03C5C55DAC94361DF286D4BD39C7CB8037ED3B267B07C346626449D0CC5B0DD2CF221F7E4C3449A4BE99985D2D5E67BFF2923357DDEAB5ABCB4619F3A3A57B2CF928A022EB27676C6CF805689004FCA4D41EA6C2D0A4789C7605F7BB838DD883B3AD3E6027E775BCF262881428099C7FFF95B14C095EA130E0B9938A5E22FC52650F591 -Out = 2b28baf7e6e0c24315ddfac88c1fde6779bc3b9982199e8deeeec60b5c21aba84ed06b3552a49f0fabe0d139284769bf +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C +Out = ABB814006D0908D4BA2CFE11B589BDADD4A17CE1E799B55E44600A7B2A0225E6BF6AFAC61D8C7958060D1F28579BF129 -In = D3E65CB92CFA79662F6AF493D696A07CCF32AAADCCEFF06E73E8D9F6F909209E66715D6E978788C49EFB9087B170ECF3AA86D2D4D1A065AE0EFC8924F365D676B3CB9E2BEC918FD96D0B43DEE83727C9A93BF56CA2B2E59ADBA85696546A815067FC7A78039629D4948D157E7B0D826D1BF8E81237BAB7321312FDAA4D521744F988DB6FDF04549D0FDCA393D639C729AF716E9C8BBA48 -Out = 533496735f25d1b4ea445a9c0fee5146d6083552c8f16ececefb1986cd112ec362aa0351f7d1cdab3c236eabc3c52f69 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D +Out = 63F83790ED5882A85BEA456CA446A2401644E3C9BFE3D4F8A54210DBD6BB807F60F9641AEEB994D4CC8A6090F8D58871 -In = 842CC583504539622D7F71E7E31863A2B885C56A0BA62DB4C2A3F2FD12E79660DC7205CA29A0DC0A87DB4DC62EE47A41DB36B9DDB3293B9AC4BAAE7DF5C6E7201E17F717AB56E12CAD476BE49608AD2D50309E7D48D2D8DE4FA58AC3CFEAFEEE48C0A9EEC88498E3EFC51F54D300D828DDDCCB9D0B06DD021A29CF5CB5B2506915BEB8A11998B8B886E0F9B7A80E97D91A7D01270F9A7717 -Out = dd0d1b5a5d8220ed17c97623b090e2f22b900eadc0bbe02bcb98a7d869211b6ffa4f04cde244dbec3c9a2053511b62c2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E +Out = 185FE5D0E6764C660BFDF8D581CDCE8F118232BA58CDC4BEE4C021FBA072C6D04250C8CE3DA4DE58FE43C8D0172B612C -In = 6C4B0A0719573E57248661E98FEBE326571F9A1CA813D3638531AE28B4860F23C3A3A8AC1C250034A660E2D71E16D3ACC4BF9CE215C6F15B1C0FC7E77D3D27157E66DA9CEEC9258F8F2BF9E02B4AC93793DD6E29E307EDE3695A0DF63CBDC0FC66FB770813EB149CA2A916911BEE4902C47C7802E69E405FE3C04CEB5522792A5503FA829F707272226621F7C488A7698C0D69AA561BE9F378 -Out = 7c49b4cbea2298aa3130085de990234285024bffbf3444ec1017becbfcaad9e9b46bfd00fc610d4ddb99384d071b15a8 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F +Out = 57D66CB0A11F02127FD13D435B6CB6195E30EA2562E92C88B9F5DAAC5EC3E86F738F05C9C949D66F04D4665F942EC991 -In = 51B7DBB7CE2FFEB427A91CCFE5218FD40F9E0B7E24756D4C47CD55606008BDC27D16400933906FD9F30EFFDD4880022D081155342AF3FB6CD53672AB7FB5B3A3BCBE47BE1FD3A2278CAE8A5FD61C1433F7D350675DD21803746CADCA574130F01200024C6340AB0CC2CF74F2234669F34E9009EF2EB94823D62B31407F4BA46F1A1EEC41641E84D77727B59E746B8A671BEF936F05BE820759FA -Out = f882e3e0b6af563b4b491bf3adf73816381ebcb7c3254153a6b8550c1058d2c9ee1aa41d899d96e1ffee86133a30c796 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F90 +Out = EAF6A0121B3B4BECB5EC1163FFCC4ACCE76330D4236427646767487E0B5F1DD938204B3F3848082313C2170B3FD7F06C -In = 83599D93F5561E821BD01A472386BC2FF4EFBD4AED60D5821E84AAE74D8071029810F5E286F8F17651CD27DA07B1EB4382F754CD1C95268783AD09220F5502840370D494BEB17124220F6AFCE91EC8A0F55231F9652433E5CE3489B727716CF4AEBA7DCDA20CD29AA9A859201253F948DD94395ABA9E3852BD1D60DDA7AE5DC045B283DA006E1CBAD83CC13292A315DB5553305C628DD091146597 -Out = 6617a6befcfbb629eeb99fdafbebb560b2fe8bec7bb6315cecc9657ad69e74af7a95b1854c6a78bd30eb21babc1a1b41 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F9091 +Out = E6793995C6B7F03BDFEDB7C41BF75FA223030E33E47AE9FB75CDAAB46AB9E5A580E864301E94A10C87EFA08AF4CE0ACE -In = 2BE9BF526C9D5A75D565DD11EF63B979D068659C7F026C08BEA4AF161D85A462D80E45040E91F4165C074C43AC661380311A8CBED59CC8E4C4518E80CD2C78AB1CABF66BFF83EAB3A80148550307310950D034A6286C93A1ECE8929E6385C5E3BB6EA8A7C0FB6D6332E320E71CC4EB462A2A62E2BFE08F0CCAD93E61BEDB5DD0B786A728AB666F07E0576D189C92BF9FB20DCA49AC2D3956D47385E2 -Out = e9993b8b57a33be5d6cf1b42641fdde187e700e86372ad7c5aef0342a8273c568de95caceeb2d3995eb0fef9d81c4cdb +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192 +Out = 616846974DD72FD90BC27DA07485D4E55BADFAAFDDF09E0CB48B7494280DC0CC20A524BD3BC60E28723F939F915B838B -In = CA76D3A12595A817682617006848675547D3E8F50C2210F9AF906C0E7CE50B4460186FE70457A9E879E79FD4D1A688C70A347361C847BA0DD6AA52936EAF8E58A1BE2F5C1C704E20146D366AEB3853BED9DE9BEFE9569AC8AAEA37A9FB7139A1A1A7D5C748605A8DEFB297869EBEDD71D615A5DA23496D11E11ABBB126B206FA0A7797EE7DE117986012D0362DCEF775C2FE145ADA6BDA1CCB326BF644 -Out = c6ad29409ef84b4d1304bcaaa55b069649de29aedc7230541a4765875a41e47abe29663019855b28cf3e17209c16422f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F90919293 +Out = ACEFB24F02371015638A8D2D1F8D0CEE4CC71959F328D7B443F61BE497E1AE5D934BC47B7C3611418862820F6F2CA27B -In = F76B85DC67421025D64E93096D1D712B7BAF7FB001716F02D33B2160C2C882C310EF13A576B1C2D30EF8F78EF8D2F465007109AAD93F74CB9E7D7BEF7C9590E8AF3B267C89C15DB238138C45833C98CC4A471A7802723EF4C744A853CF80A0C2568DD4ED58A2C9644806F42104CEE53628E5BDF7B63B0B338E931E31B87C24B146C6D040605567CEEF5960DF9E022CB469D4C787F4CBA3C544A1AC91F95F -Out = c962f1e1739c8793309057fbd7f1228b9e4d78d2e5cc58193369565a2c73fb729bad66b8ae03c1e8f1309a306c12c4ce +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F9091929394 +Out = 2F3F1757EAFA15BCA9E5A463D68AA56EAF59BAD93542B89FE7C733F3991640595298CF0447A35A207388F0160BE3FE21 -In = 25B8C9C032EA6BCD733FFC8718FBB2A503A4EA8F71DEA1176189F694304F0FF68E862A8197B839957549EF243A5279FC2646BD4C009B6D1EDEBF24738197ABB4C992F6B1DC9BA891F570879ACCD5A6B18691A93C7D0A8D38F95B639C1DAEB48C4C2F15CCF5B9D508F8333C32DE78781B41850F261B855C4BEBCC125A380C54D501C5D3BD07E6B52102116088E53D76583B0161E2A58D0778F091206AABD5A1 -Out = ce274ec62ad13390ad701ddea3213639a8c22cf6a128e50a8440791d30994523376c8b3633a93dfb2284040a980bdb49 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495 +Out = 2604AEC3CC0CC8A5F3C51D4B7977341751D35EBC4157A4CC3A5915B65370DAC58E0F7C8A5E3517A12EAFF69FAE323CD0 -In = 21CFDC2A7CCB7F331B3D2EEFFF37E48AD9FA9C788C3F3C200E0173D99963E1CBCA93623B264E920394AE48BB4C3A5BB96FFBC8F0E53F30E22956ADABC2765F57FB761E147ECBF8567533DB6E50C8A1F894310A94EDF806DD8CA6A0E141C0FA7C9FAE6C6AE65F18C93A8529E6E5B553BF55F25BE2E80A9882BD37F145FECBEB3D447A3C4E46C21524CC55CDD62F521AB92A8BA72B897996C49BB273198B7B1C9E -Out = 9719827aa5f969dcf746be754a73952f81e71819dda6b4d2c107cbd83d2a2b9f29d1b61578c36ca63f07eb701122d359 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F90919293949596 +Out = CCD7214BE7EEBF6B1B203675A6C2A77646469794DB7841511B88A6BB95570773B1F30D494CA56320A6591F55FE011F7E -In = 4E452BA42127DCC956EF4F8F35DD68CB225FB73B5BC7E1EC5A898BBA2931563E74FAFF3B67314F241EC49F4A7061E3BD0213AE826BAB380F1F14FAAB8B0EFDDD5FD1BB49373853A08F30553D5A55CCBBB8153DE4704F29CA2BDEEF0419468E05DD51557CCC80C0A96190BBCC4D77ECFF21C66BDF486459D427F986410F883A80A5BCC32C20F0478BB9A97A126FC5F95451E40F292A4614930D054C851ACD019CCF -Out = d0c4f64bfedee16a79f467056fafe67e61d7669616c4d5b2893a2d22af2eb087ecf4733204243298eed9e24da25c01b0 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F9091929394959697 +Out = FEAB9CBB082877BFA8D1215C098BD9131E9C22B8F4ED666D1571F467771EC03FE0BDE78D7E24EDC5C9F14B9488D24966 -In = FA85671DF7DADF99A6FFEE97A3AB9991671F5629195049880497487867A6C446B60087FAC9A0F2FCC8E3B24E97E42345B93B5F7D3691829D3F8CCD4BB36411B85FC2328EB0C51CB3151F70860AD3246CE0623A8DC8B3C49F958F8690F8E3860E71EB2B1479A5CEA0B3F8BEFD87ACAF5362435EAECCB52F38617BC6C5C2C6E269EAD1FBD69E941D4AD2012DA2C5B21BCFBF98E4A77AB2AF1F3FDA3233F046D38F1DC8 -Out = 82136c02e56609103dcba87a1f6f8c8ef1e40d50742f9489c0b691560debf9cd2ce80e93a8d920f53baf8f96d2205ff5 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798 +Out = 90948F9AE3B1C195005D81BCAC2F3F32629B1D512F922E4376456782BCB5D916AAC5BAC7AFBF6DF03974AEF73C48CA5B -In = E90847AE6797FBC0B6B36D6E588C0A743D725788CA50B6D792352EA8294F5BA654A15366B8E1B288D84F5178240827975A763BC45C7B0430E8A559DF4488505E009C63DA994F1403F407958203CEBB6E37D89C94A5EACF6039A327F6C4DBBC7A2A307D976AA39E41AF6537243FC218DFA6AB4DD817B6A397DF5CA69107A9198799ED248641B63B42CB4C29BFDD7975AC96EDFC274AC562D0474C60347A078CE4C25E88 -Out = 9af7cb722d4c5f5a212f5d47155d3b3052fe7e7ac434d10566881dab5400088a1f51e6a02acf69c41c56cf6690d29c03 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F90919293949596979899 +Out = 2FDE1A345A9874CDDD1C966413F518D3DB91AFCBF253EC5F6F90228C904977F65B24C1039055E6AC94A7111CE00C0EF3 -In = F6D5C2B6C93954FC627602C00C4CA9A7D3ED12B27173F0B2C9B0E4A5939398A665E67E69D0B12FB7E4CEB253E8083D1CEB724AC07F009F094E42F2D6F2129489E846EAFF0700A8D4453EF453A3EDDC18F408C77A83275617FABC4EA3A2833AA73406C0E966276079D38E8E38539A70E194CC5513AAA457C699383FD1900B1E72BDFB835D1FD321B37BA80549B078A49EA08152869A918CA57F5B54ED71E4FD3AC5C06729 -Out = 8dba987b0370444a6452e4fb0957398cfcf33a971b29d340b60282c0bd8a2e7a188d8a0eb740acbcd680a20c4de5df1a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A +Out = 285F4FE6AAE37FB6DE5318AC8F07E1CFD678C8EB42D408323989589CE4D50AB96684BF399FD5EE775B4EA583C6DC4BCF -In = CF8562B1BED89892D67DDAAF3DEEB28246456E972326DBCDB5CF3FB289ACA01E68DA5D59896E3A6165358B071B304D6AB3D018944BE5049D5E0E2BB819ACF67A6006111089E6767132D72DD85BEDDCBB2D64496DB0CC92955AB4C6234F1EEA24F2D51483F2E209E4589BF9519FAC51B4D061E801125E605F8093BB6997BC163D551596FE4AB7CFAE8FB9A90F6980480CE0C229FD1675409BD788354DAF316240CFE0AF93EB -Out = a646293fd1f3af1f2f372c06e7a8ee9103d1e3ee91bb020409e48bc20aceeda13901524cbb7601816135781611d7a384 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B +Out = AC29B1F850E3FBF0CBF88EB04A23DF73E966C2D9F10259AC87240B740B94814F42C1D9EC460C5353D4518ABF4FF72A3C -In = 2ACE31ABB0A2E3267944D2F75E1559985DB7354C6E605F18DC8470423FCA30B7331D9B33C4A4326783D1CAAE1B4F07060EFF978E4746BF0C7E30CD61040BD5EC2746B29863EB7F103EBDA614C4291A805B6A4C8214230564A0557BC7102E0BD3ED23719252F7435D64D210EE2AAFC585BE903FA41E1968C50FD5D5367926DF7A05E3A42CF07E656FF92DE73B036CF8B19898C0CB34557C0C12C2D8B84E91181AF467BC75A9D1 -Out = 553c17a289d3666d44ee7595547270ae6e7939c18e7ddbbd91585d352c08b40d77289a68a1cec3c37ef174003e2cb6c3 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C +Out = E56BDFE4DE330B91B58DD9AAD2CD203A74FA71CC2B7973F5A055390D99A498A7C71844EE711D2989C29A481A526482E0 -In = 0D8D09AED19F1013969CE5E7EB92F83A209AE76BE31C754844EA9116CEB39A22EBB6003017BBCF26555FA6624185187DB8F0CB3564B8B1C06BF685D47F3286EDA20B83358F599D2044BBF0583FAB8D78F854FE0A596183230C5EF8E54426750EAF2CC4E29D3BDD037E734D863C2BD9789B4C243096138F7672C232314EFFDFC6513427E2DA76916B5248933BE312EB5DDE4CF70804FB258AC5FB82D58D08177AC6F4756017FFF5 -Out = e54a96244d04d63fdceab516522a09c8b7a4baad6517916708044103a66294553c27e98aab6d8efa114122e632f6e1f4 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D +Out = EF6A9215377AEC65A6382C6775F7A703E0C56665B3E71F877DF5B00BAAE6E0C5E963B2C29D7E57241A6EDDB338683CA8 -In = C3236B73DEB7662BF3F3DAA58F137B358BA610560EF7455785A9BEFDB035A066E90704F929BD9689CEF0CE3BDA5ACF4480BCEB8D09D10B098AD8500D9B6071DFC3A14AF6C77511D81E3AA8844986C3BEA6F469F9E02194C92868CD5F51646256798FF0424954C1434BDFED9FACB390B07D342E992936E0F88BFD0E884A0DDB679D0547CCDEC6384285A45429D115AC7D235A717242021D1DC35641F5F0A48E8445DBA58E6CB2C8EA -Out = 459525a7de3a8a986f03bc38d9797bd58a97fa72ae02b5e6865ac5338ef730330264e2525fc8c1b75d437fd7434a25cd +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E +Out = 2AADD60F700F29CF5F1739ACF5888CC137AC0D57FC113549D1153881287C0E598728D88E19CC37C4BE1BD9E1AA2B510F -In = B39FEB8283EADC63E8184B51DF5AE3FD41AAC8A963BB0BE1CD08AA5867D8D910C669221E73243360646F6553D1CA05A84E8DC0DE05B6419EC349CA994480193D01C92525F3FB3DCEFB08AFC6D26947BDBBFD85193F53B50609C6140905C53A6686B58E53A319A57B962331EDE98149AF3DE3118A819DA4D76706A0424B4E1D2910B0ED26AF61D150EBCB46595D4266A0BD7F651BA47D0C7F179CA28545007D92E8419D48FDFBD744CE -Out = 7e97d0ba568c3bfae07b4bd4e8cd6aa14186907c7a2c18e613c6ac124c517c9b8ca62df0835f43130a5a6fe499301da8 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F +Out = 1407E4D4E7E572DB091E06E73D4F0D59D67A97B861716318CEBF80AED598F26CF1BBE0EC52918D3A78A434094939E2E7 -In = A983D54F503803E8C7999F4EDBBE82E9084F422143A932DDDDC47A17B0B7564A7F37A99D0786E99476428D29E29D3C197A72BFAB1342C12A0FC4787FD7017D7A6174049EA43B5779169EF7472BDBBD941DCB82FC73AAC45A8A94C9F2BD3477F61FD3B796F02A1B8264A214C6FEA74B7051B226C722099EC7883A462B83B6AFDD4009248B8A237F605FE5A08FE7D8B45321421EBBA67BD70A0B00DDBF94BAAB7F359D5D1EEA105F28DCFB -Out = 8d1c83d98a63adec8062e665b81abf3a3864811902f4f1b989dab1161918b7511b176d284c3d42525e457fdbf8b844d3 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0 +Out = EA5733A41647AB8A1A717550B41FC53AB1CA49CC28DF772B229D94727E5CA98FB18663EB0EDA37F81FBA387A8A4F4BCB -In = E4D1C1897A0A866CE564635B74222F9696BF2C7F640DD78D7E2ACA66E1B61C642BB03EA7536AAE597811E9BF4A7B453EDE31F97B46A5F0EF51A071A2B3918DF16B152519AE3776F9F1EDAB4C2A377C3292E96408359D3613844D5EB393000283D5AD3401A318B12FD1474B8612F2BB50FB6A8B9E023A54D7DDE28C43D6D8854C8D9D1155935C199811DBFC87E9E0072E90EB88681CC7529714F8FB8A2C9D88567ADFB974EE205A9BF7B848 -Out = 6fdf52321d3ed730ab6c9758f46a6ed9ae9ee22271ff56f99d0bd2e2577295a644db668d54611ff8a063d1a35a9802f9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1 +Out = 9915D517A86107E27B1F78AF6E7B0896E3D2E90C7A917CF16C9F68AD4F17DCC736F0F2D8CE5C7B634BF297B197D235C9 -In = B10C59723E3DCADD6D75DF87D0A1580E73133A9B7D00CB95EC19F5547027323BE75158B11F80B6E142C6A78531886D9047B08E551E75E6261E79785366D7024BD7CD9CF322D9BE7D57FB661069F2481C7BB759CD71B4B36CA2BC2DF6D3A328FAEBDB995A9794A8D72155ED551A1F87C80BF6059B43FC764900B18A1C2441F7487743CF84E565F61F8DD2ECE6B6CCC9444049197AAAF53E926FBEE3BFCA8BE588EC77F29D211BE89DE18B15F6 -Out = e5906c73ff1af5e41ea96cbe2df7c6d895752f723ebe63785a834b67c5f6181ec92b820697ecd3422c9757d8b4800974 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2 +Out = 5D9E3D02EC4088C2F3D514FDFBE27A9DB53B0037CC3FFF615B4AFB3EEF279DD7A0B8882F0DF5E125F06302BE9A30C1C7 -In = DB11F609BABA7B0CA634926B1DD539C8CBADA24967D7ADD4D9876F77C2D80C0F4DCEFBD7121548373582705CCA2495BD2A43716FE64ED26D059CFB566B3364BD49EE0717BDD9810DD14D8FAD80DBBDC4CAFB37CC60FB0FE2A80FB4541B8CA9D59DCE457738A9D3D8F641AF8C3FD6DA162DC16FC01AAC527A4A0255B4D231C0BE50F44F0DB0B713AF03D968FE7F0F61ED0824C55C4B5265548FEBD6AAD5C5EEDF63EFE793489C39B8FD29D104CE -Out = 190abadd04b5b35a805931717bbbdd8b15e77876c40beb51503a3cc63901dc89544854ace5ff8637432a3983c2d81255 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3 +Out = 0627AA95EB148A7682118D502CAD7ABB8BD83E13008364F5E8B8732EC3E648C0D0B78B7B3B0E2E5A58103DBB7F56E4AF -In = BEBD4F1A84FC8B15E4452A54BD02D69E304B7F32616AADD90537937106AE4E28DE9D8AAB02D19BC3E2FDE1D651559E296453E4DBA94370A14DBBB2D1D4E2022302EE90E208321EFCD8528AD89E46DC839EA9DF618EA8394A6BFF308E7726BAE0C19BCD4BE52DA6258E2EF4E96AA21244429F49EF5CB486D7FF35CAC1BACB7E95711944BCCB2AB34700D42D1EB38B5D536B947348A458EDE3DC6BD6EC547B1B0CAE5B257BE36A7124E1060C170FFA -Out = 50d604dd54c2960ef578e73fee29015e4cbb14efa1385d1700153c9896fab7f9161f13629ef42039445592c8fcb540cc +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4 +Out = 51BAFAFA927821102EE620D2DABFD4F9B18C365707569E161C9A7A33B3C433E16783C87DC2A949732683E951E538C3E4 -In = 5ACA56A03A13784BDC3289D9364F79E2A85C12276B49B92DB0ADAA4F206D5028F213F678C3510E111F9DC4C1C1F8B6ACB17A6413AA227607C515C62A733817BA5E762CC6748E7E0D6872C984D723C9BB3B117EB8963185300A80BFA65CDE495D70A46C44858605FCCBED086C2B45CEF963D33294DBE9706B13AF22F1B7C4CD5A001CFEC251FBA18E722C6E1C4B1166918B4F6F48A98B64B3C07FC86A6B17A6D0480AB79D4E6415B520F1C484D675B1 -Out = e7bced7b40fdde8556db8412971b9d2da0b13cc57bdd72d7b18d949998ac2caa6bd8aaaaa08beec8df5db4c7664b2231 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5 +Out = 08D9894DCD5C8FF0B4A8339A75FA95E9E88C68427CD8DBAD9297ABF02598BB6ED3FAF8F7B51470514B3BB9A6C74A0137 -In = A5AAD0E4646A32C85CFCAC73F02FC5300F1982FABB2F2179E28303E447854094CDFC854310E5C0F60993CEFF54D84D6B46323D930ADB07C17599B35B505F09E784BCA5985E0172257797FB53649E2E9723EFD16865C31B5C3D5113B58BB0BFC8920FABDDA086D7537E66D709D050BD14D0C960873F156FAD5B3D3840CDFCDC9BE6AF519DB262A27F40896AB25CC39F96984D650611C0D5A3080D5B3A1BF186ABD42956588B3B58CD948970D298776060 -Out = d26e62659f9d8224413bfefa2bb84a41a661d31d5cea3593d653892bd0e48d8b580d56ce9c999418906a46876275626b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6 +Out = 594B4CF5BE4B482A8F23F610F7E57493C708B269207B246E628516CA66D91C7965F9D830E1A245BA191113BB2E2D19BF -In = 06CBBE67E94A978203EAD6C057A1A5B098478B4B4CBEF5A97E93C8E42F5572713575FC2A884531D7622F8F879387A859A80F10EF02708CD8F7413AB385AFC357678B9578C0EBF641EF076A1A30F1F75379E9DCB2A885BDD295905EE80C0168A62A9597D10CF12DD2D8CEE46645C7E5A141F6E0E23AA482ABE5661C16E69EF1E28371E2E236C359BA4E92C25626A7B7FF13F6EA4AE906E1CFE163E91719B1F750A96CBDE5FBC953D9E576CD216AFC90323A -Out = c2e78b6be635882a9cbda6ef1422ba7042c4e351a33d7ea8ca610cd633a7f6f569b401005b4517bc9cc5229b3a410559 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7 +Out = 02FDDB82B56E4550B8DE39C7013BA1B3B8E1EF0186A1EE899FEFEABB5B83BEF41C76CD276657E69FFBF131D78FEAD875 -In = F1C528CF7739874707D4D8AD5B98F7C77169DE0B57188DF233B2DC8A5B31EDA5DB4291DD9F68E6BAD37B8D7F6C9C0044B3BF74BBC3D7D1798E138709B0D75E7C593D3CCCDC1B20C7174B4E692ADD820ACE262D45CCFAE2077E878796347168060A162ECCA8C38C1A88350BD63BB539134F700FD4ADDD5959E255337DAA06BC86358FABCBEFDFB5BC889783D843C08AADC6C4F6C36F65F156E851C9A0F917E4A367B5AD93D874812A1DE6A7B93CD53AD97232 -Out = efa81e9bb4c0181c8465b803d8d352c75acb692430d832c6edcb276435ee119de6e87fa5b5153520dbca62ff6c8c49be +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8 +Out = E1420C71511116DE6BD3225DFDCF54D6242DE8E84DB032E698A5548F5311D306B6B3AE57417193EDFBBB9BAF516AD804 -In = 9D9F3A7ECD51B41F6572FD0D0881E30390DFB780991DAE7DB3B47619134718E6F987810E542619DFAA7B505C76B7350C6432D8BF1CFEBDF1069B90A35F0D04CBDF130B0DFC7875F4A4E62CDB8E525AADD7CE842520A482AC18F09442D78305FE85A74E39E760A4837482ED2F437DD13B2EC1042AFCF9DECDC3E877E50FF4106AD10A525230D11920324A81094DA31DEAB6476AA42F20C84843CFC1C58545EE80352BDD3740DD6A16792AE2D86F11641BB717C2 -Out = d9446b90f46ab68c2558211a4f64c3adf44af25d35b2da8232e84e546e3ab26b42cf1afe1d78b13637541bfbad82f350 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9 +Out = 9616D9830ADC19D61EFAF62B3B0BDDCA2182A3F9A340BAD5E347A780716A73D2195C00C5BA4783C41006D2B43FAE98A3 -In = 5179888724819FBAD3AFA927D3577796660E6A81C52D98E9303261D5A4A83232F6F758934D50AA83FF9E20A5926DFEBAAC49529D006EB923C5AE5048ED544EC471ED7191EDF46363383824F915769B3E688094C682B02151E5EE01E510B431C8865AFF8B6B6F2F59CB6D129DA79E97C6D2B8FA6C6DA3F603199D2D1BCAB547682A81CD6CF65F6551121391D78BCC23B5BD0E922EC6D8BF97C952E84DD28AEF909ABA31EDB903B28FBFC33B7703CD996215A11238 -Out = af9c582652e70ce44f2e3f2ca560fda17777295836e7fe6ed0e1d3f04678d144eb7289a4fa6c62f6b12e235453524e30 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AA +Out = 1A5816F33CEB8F566AD383C47850387535E020A8D0B24DE45135ABF217599664E6E78A492ABD367A7B04A44CD1B8912C -In = 576EF3520D30B7A4899B8C0D5E359E45C5189ADD100E43BE429A02FB3DE5FF4F8FD0E79D9663ACCA72CD29C94582B19292A557C5B1315297D168FBB54E9E2ECD13809C2B5FCE998EDC6570545E1499DBE7FB74D47CD7F35823B212B05BF3F5A79CAA34224FDD670D335FCB106F5D92C3946F44D3AFCBAE2E41AC554D8E6759F332B76BE89A0324AA12C5482D1EA3EE89DED4936F3E3C080436F539FA137E74C6D3389BDF5A45074C47BC7B20B0948407A66D855E2F -Out = f78373e2d9e513cce980e76c88cf6309b6aa6d3665bf28be01d9280d12de091397fdd0e44db1f71acc695c295682cf6d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAAB +Out = 5547E5190F50DCAAFC3CED495AD238852EDAB167F092FDA5648EBE8A602F073849F925B3F76D4E5506FD671885B236FA -In = 0DF2152FA4F4357C8741529DD77E783925D3D76E95BAFA2B542A2C33F3D1D117D159CF473F82310356FEE4C90A9E505E70F8F24859656368BA09381FA245EB6C3D763F3093F0C89B972E66B53D59406D9F01AEA07F8B3B615CAC4EE4D05F542E7D0DAB45D67CCCCD3A606CCBEB31EA1FA7005BA07176E60DAB7D78F6810EF086F42F08E595F0EC217372B98970CC6321576D92CE38F7C397A403BADA1548D205C343AC09DECA86325373C3B76D9F32028FEA8EB32515 -Out = 13411f6fdb552cc8014ce092d3f0193defce75c5b62afa29540ea50356739286542ee69d8cecb2712beae859951477a4 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABAC +Out = 5D6A6082AF10CDB8323202F11A79FAC522AD32BE9620F77C24C809970F1F80D7E5468BB11FDD63788C8FA46F553F9B23 -In = 3E15350D87D6EBB5C8AD99D42515CFE17980933C7A8F6B8BBBF0A63728CEFAAD2052623C0BD5931839112A48633FB3C2004E0749C87A41B26A8B48945539D1FF41A4B269462FD199BFECD45374756F55A9116E92093AC99451AEFB2AF9FD32D6D7F5FBC7F7A540D5097C096EBC3B3A721541DE073A1CC02F7FB0FB1B9327FB0B1218CA49C9487AB5396622A13AE546C97ABDEF6B56380DDA7012A8384091B6656D0AB272D363CEA78163FF765CDD13AB1738B940D16CAE -Out = 9ad0cd56ecd303155c7b0886a4542908ad7f63b2ad60367e6c60c053020b5cf64aa4c0262a2c03104431c267b9196674 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACAD +Out = F9AF7AD6AA566D2E80478B50B043DFCDA40256A2724351BEF6CE819039F541D8BD88DEF486CA1E2256B18417BA0F5472 -In = C38D6B0B757CB552BE40940ECE0009EF3B0B59307C1451686F1A22702922800D58BCE7A636C1727EE547C01B214779E898FC0E560F8AE7F61BEF4D75EAA696B921FD6B735D171535E9EDD267C192B99880C87997711002009095D8A7A437E258104A41A505E5EF71E5613DDD2008195F0C574E6BA3FE40099CFA116E5F1A2FA8A6DA04BADCB4E2D5D0DE31FDC4800891C45781A0AAC7C907B56D631FCA5CE8B2CDE620D11D1777ED9FA603541DE794DDC5758FCD5FAD78C0 -Out = 50377eafda8ab6d16b1253f0813bcb2793cedf40826163565a07cab8f9238c00e8c6840bb205983c6cc769f5d6dcec0b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE +Out = C3106AAB2E9F2A4C772274FE1A36BA98F3A9AA94BAD93B54ADDCFA72CFB084640BC3A6881BE9A2783AE191BE13A7E85C -In = 8D2DE3F0B37A6385C90739805B170057F091CD0C7A0BC951540F26A5A75B3E694631BB64C7635EED316F51318E9D8DE13C70A2ABA04A14836855F35E480528B776D0A1E8A23B547C8B8D6A0D09B241D3BE9377160CCA4E6793D00A515DC2992CB7FC741DACA171431DA99CCE6F7789F129E2AC5CF65B40D703035CD2185BB936C82002DAF8CBC27A7A9E554B06196630446A6F0A14BA155ED26D95BD627B7205C072D02B60DB0FD7E49EA058C2E0BA202DAFF0DE91E845CF79 -Out = 3c2d91de42731365e5d89021e8d9fb2b0be54e25369473d721719bcac11274d53ba42a3a1ea4a327e48cc2502a6efc1e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAF +Out = 713B25A0A73DE00F72FB93E440DE6828D6FC1B4FE631F27538A4B37A1DD6E1F4C7928A96912590F750B04BBAE0132B96 -In = C464BBDAD275C50DCD983B65AD1019B9FF85A1E71C807F3204BB2C921DC31FBCD8C5FC45868AE9EF85B6C9B83BBA2A5A822201ED68586EC5EC27FB2857A5D1A2D09D09115F22DCC39FE61F5E1BA0FF6E8B4ACB4C6DA748BE7F3F0839739394FF7FA8E39F7F7E84A33C3866875C01BCB1263C9405D91908E9E0B50E7459FABB63D8C6BBB73D8E3483C099B55BC30FF092FF68B6ADEDFD477D63570C9F5515847F36E24BA0B705557130CEC57EBAD1D0B31A378E91894EE26E3A04 -Out = 35dcbceab923c709f846015df43f8a9bf1d53b5d91f4c836bc38a13867aae8b555fa3460f219c9fb691fb9fd5f9edfa1 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0 +Out = EF3AD97AFF769E1450656A161B0BD0D6B251ABE681F0EB0EC856D125E2DCB5B0D04D787232FC9D941271AA62E7CC28A7 -In = 8B8D68BB8A75732FE272815A68A1C9C5AA31B41DEDC8493E76525D1D013D33CEBD9E21A5BB95DB2616976A8C07FCF411F5F6BC6F7E0B57ACA78CC2790A6F9B898858AC9C79B165FF24E66677531E39F572BE5D81EB3264524181115F32780257BFB9AEEC6AF12AF28E587CAC068A1A2953B59AD680F4C245B2E3EC36F59940D37E1D3DB38E13EDB29B5C0F404F6FF87F80FC8BE7A225FF22FBB9C8B6B1D7330C57840D24BC75B06B80D30DAD6806544D510AF6C4785E823AC3E0B8 -Out = 8f9fed3ed19274d3530486cdc5f82c909c713b59d630d065c4d668386c1ce0352b012823d88e8eac18df18ddccabe1aa +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1 +Out = 6B3052F9304DDBD0EAE8ABE5FAF982641A5FE98D89F531373C74CC6E068C1D9A7E57994C2856EC490F607A1C421DEE9F -In = 6B018710446F368E7421F1BC0CCF562D9C1843846BC8D98D1C9BF7D9D6FCB48BFC3BF83B36D44C4FA93430AF75CD190BDE36A7F92F867F58A803900DF8018150384D85D82132F123006AC2AEBA58E02A037FE6AFBD65ECA7C44977DD3DC74F48B6E7A1BFD5CC4DCF24E4D52E92BD4455848E4928B0EAC8B7476FE3CC03E862AA4DFF4470DBFED6DE48E410F25096487ECFC32A27277F3F5023B2725ADE461B1355889554A8836C9CF53BD767F5737D55184EEA1AB3F53EDD0976C485 -Out = 892e2a0d09a3a1279d9b62f86903f3fb70bc5118f72c8464a04628ce946bbd2a1372f65a5e042ba11be6664b5ffb997f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2 +Out = D28F0C437E0F812CFD4068F7FED2068E2C16FA89D3FED5E43FF06CB6A9EABDE3C81F585EF0CCB9CB4EB1D3BFDE7AADA8 -In = C9534A24714BD4BE37C88A3DA1082EDA7CABD154C309D7BD670DCCD95AA535594463058A29F79031D6ECAA9F675D1211E9359BE82669A79C855EA8D89DD38C2C761DDD0EC0CE9E97597432E9A1BEAE062CDD71EDFDFD464119BE9E69D18A7A7FD7CE0E2106F0C8B0ABF4715E2CA48EF9F454DC203C96656653B727083513F8EFB86E49C513BB758B3B052FE21F1C05BB33C37129D6CC81F1AEF6ADC45B0E8827A830FE545CF57D0955802C117D23CCB55EA28F95C0D8C2F9C5A242B33F -Out = 032fea2a692bd8c3ea2377e345607257eca97c604cafb14085b80ae967cd815e0df8d94656ece1271a45d1b8e0bf6f50 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3 +Out = 4A7EDFD099BAE536F39D27E28EEE09683B4381A237ED49B152490108FBCB5B008B452509184F24E2C15BFF448C4222AE -In = 07906C87297B867ABF4576E9F3CC7F82F22B154AFCBF293B9319F1B0584DA6A40C27B32E0B1B7F412C4F1B82480E70A9235B12EC27090A5A33175A2BB28D8ADC475CEFE33F7803F8CE27967217381F02E67A3B4F84A71F1C5228E0C2AD971373F6F672624FCEA8D1A9F85170FAD30FA0BBD25035C3B41A6175D467998BD1215F6F3866F53847F9CF68EF3E2FBB54BC994DE2302B829C5EEA68EC441FCBAFD7D16AE4FE9FFF98BF00E5BC2AD54DD91FF9FDA4DD77B6C754A91955D1FBAAD0 -Out = c275246a4841977f5fc54918ba57afc67b4e9a7c5fa320b544ef98937111333623feb6a80c8edc3ffd8e18887b9ea370 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4 +Out = 389B32D6503D04B43A0F608D171F79DDED2CC8F82919FED673084DAC52315A72177FEE73A489B0344EA3FA4DA2F7B82C -In = 588E94B9054ABC2189DF69B8BA34341B77CDD528E7860E5DEFCAA79B0C9A452AD4B82AA306BE84536EB7CEDCBE058D7B84A6AEF826B028B8A0271B69AC3605A9635EA9F5EA0AA700F3EB7835BC54611B922964300C953EFE7491E3677C2CEBE0822E956CD16433B02C68C4A23252C3F9E151A416B4963257B783E038F6B4D5C9F110F871652C7A649A7BCEDCBCCC6F2D0725BB903CC196BA76C76AA9F10A190B1D1168993BAA9FFC96A1655216773458BEC72B0E39C9F2C121378FEAB4E76A -Out = 91a542751ac85394cc73b8933debfb5bdfee1d18601565a9e947e295a95e2966cabc03fdf1724b30f295ae82d6f3ca2d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5 +Out = 335F0D7D2EBEA7236E1A15D892923FC92F184E112FCF749E173C3109FA4C33FE9E4B0353DBEDC0C9974A51EBF3A9620E -In = 08959A7E4BAAE874928813364071194E2939772F20DB7C3157078987C557C2A6D5ABE68D520EEF3DC491692E1E21BCD880ADEBF63BB4213B50897FA005256ED41B5690F78F52855C8D9168A4B666FCE2DA2B456D7A7E7C17AB5F2FB1EE90B79E698712E963715983FD07641AE4B4E9DC73203FAC1AE11FA1F8C7941FCC82EAB247ADDB56E2638447E9D609E610B60CE086656AAEBF1DA3C8A231D7D94E2FD0AFE46B391FF14A72EAEB3F44AD4DF85866DEF43D4781A0B3578BC996C87970B132 -Out = 8af3096381876c1af19fdf33125f09204d21f8f489f21f689d4d0b602c9df7fc5f5e7bcba12c7cf7488d4ed383a6ec4b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6 +Out = 7A2F48F490C5649A4875ADCC8B37A06A3D76D08ADD0613119E7D752D3E39E3C4D9F28E55230B1D0F0FD411685671C9E5 -In = CB2A234F45E2ECD5863895A451D389A369AAB99CFEF0D5C9FFCA1E6E63F763B5C14FB9B478313C8E8C0EFEB3AC9500CF5FD93791B789E67EAC12FD038E2547CC8E0FC9DB591F33A1E4907C64A922DDA23EC9827310B306098554A4A78F050262DB5B545B159E1FF1DCA6EB734B872343B842C57EAFCFDA8405EEDBB48EF32E99696D135979235C3A05364E371C2D76F1902F1D83146DF9495C0A6C57D7BF9EE77E80F9787AEE27BE1FE126CDC9EF893A4A7DCBBC367E40FE4E1EE90B42EA25AF01 -Out = 89fa7456831ab6fd269b74e07afaa2ae23cc91db568f32c685d6e4703a66fc98869881e5ec021cd7dd714eb152e36625 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7 +Out = 064EFC6A149C8BD56B889BB0D0C427C47349F72A57655B97897BFF17B3C997C3217E03F63F90E6FCDD94847FF243B6F4 -In = D16BEADF02AB1D4DC6F88B8C4554C51E866DF830B89C06E786A5F8757E8909310AF51C840EFE8D20B35331F4355D80F73295974653DDD620CDDE4730FB6C8D0D2DCB2B45D92D4FBDB567C0A3E86BD1A8A795AF26FBF29FC6C65941CDDB090FF7CD230AC5268AB4606FCCBA9EDED0A2B5D014EE0C34F0B2881AC036E24E151BE89EEB6CD9A7A790AFCCFF234D7CB11B99EBF58CD0C589F20BDAC4F9F0E28F75E3E04E5B3DEBCE607A496D848D67FA7B49132C71B878FD5557E082A18ECA1FBDA94D4B -Out = 8ad02911a7732af0a4ee93ab89b23fdb46e1207a028cc780c8eb606890dbaf90b049db2e610f57fd526bec3ec85de861 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8 +Out = 1AC5D8B018C1981AF1CF0FF849C5819C42B0015B83A0111C313F3425F9B6389D7309DB7E5A7B17BB50DE72F4B4F7CE03 -In = 8F65F6BC59A85705016E2BAE7FE57980DE3127E5AB275F573D334F73F8603106EC3553016608EF2DD6E69B24BE0B7113BF6A760BA6E9CE1C48F9E186012CF96A1D4849D75DF5BB8315387FD78E9E153E76F8BA7EC6C8849810F59FB4BB9B004318210B37F1299526866F44059E017E22E96CBE418699D014C6EA01C9F0038B10299884DBEC3199BB05ADC94E955A1533219C1115FED0E5F21228B071F40DD57C4240D98D37B73E412FE0FA4703120D7C0C67972ED233E5DEB300A22605472FA3A3BA86 -Out = 6dd54f8ee63d3e26ed509f9299934f56eb308ca3564fe9fbc50bebbcea203e5a7e580f1b104719275c94942a187edc7b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9 +Out = A8D496F68517A880B7C27BB2A7DF5EECD65A79355F15645385EAF830F95B14F72C57C60E030610B6263E7A9430602222 -In = 84891E52E0D451813210C3FD635B39A03A6B7A7317B221A7ABC270DFA946C42669AACBBBDF801E1584F330E28C729847EA14152BD637B3D0F2B38B4BD5BF9C791C58806281103A3EABBAEDE5E711E539E6A8B2CF297CF351C078B4FA8F7F35CF61BEBF8814BF248A01D41E86C5715EA40C63F7375379A7EB1D78F27622FB468AB784AAABA4E534A6DFD1DF6FA15511341E725ED2E87F98737CCB7B6A6DFAE416477472B046BF1811187D151BFA9F7B2BF9ACDB23A3BE507CDF14CFDF517D2CB5FB9E4AB6 -Out = 5559adba63ced58eedb6fb2191a33398cc727beeaca3473e53af6fc1968947f0c2748475aa3638d48b9a84aa1204cb5f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BA +Out = 551345C9934FD4ECFE647A7EFB9E6F094FEB70F3935A6C81A33325CB8B15A4816D3FD1AD3FFC88DD0F47CB143E8B5EAB -In = FDD7A9433A3B4AFABD7A3A5E3457E56DEBF78E84B7A0B0CA0E8C6D53BD0C2DAE31B2700C6128334F43981BE3B213B1D7A118D59C7E6B6493A86F866A1635C12859CFB9AD17460A77B4522A5C1883C3D6ACC86E6162667EC414E9A104AA892053A2B1D72165A855BACD8FAF8034A5DD9B716F47A0818C09BB6BAF22AA503C06B4CA261F557761989D2AFBD88B6A678AD128AF68672107D0F1FC73C5CA740459297B3292B281E93BCEB761BDE7221C3A55708E5EC84472CDDCAA84ECF23723CC0991355C6280 -Out = 03a090e383d6e4cb5e7d49d4a2fce10c9342626b9e0030aa055a63623b55210921b02be66e62d368582de2386cc75056 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABB +Out = 11EE22A28D7F0B7BF6E8110A12E3413B58FB6A0BEF872744F579CBC44990AD83BDFB13FF5C1177839590312DAC646AD0 -In = 70A40BFBEF92277A1AAD72F6B79D0177197C4EBD432668CFEC05D099ACCB651062B5DFF156C0B27336687A94B26679CFDD9DAF7AD204338DD9C4D14114033A5C225BD11F217B5F4732DA167EE3F939262D4043FC9CBA92303B7B5E96AEA12ADDA64859DF4B86E9EE0B58E39091E6B188B408AC94E1294A8911245EE361E60E601EFF58D1D37639F3753BEC80EBB4EFDE25817436076623FC65415FE51D1B0280366D12C554D86743F3C3B6572E400361A60726131441BA493A83FBE9AFDA90F7AF1AE717238D -Out = 025430e6adbe0bf0f78a1b478a94e9575b84f2e25dee611f31f9258a2e8bdb51853d18609122374eef41d4062ad884b8 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBC +Out = A9BE399F1C9D3DD12EB61E69E0BA0AD878FF86C9A81DB6F36DA6F29B53B6FEBD998889E3DA5D1E65B35276056D019307 -In = 74356E449F4BF8644F77B14F4D67CB6BD9C1F5AE357621D5B8147E562B65C66585CAF2E491B48529A01A34D226D436959153815380D5689E30B35357CDAC6E08D3F2B0E88E200600D62BD9F5EAF488DF86A4470EA227006182E44809009868C4C280C43D7D64A5268FA719074960087B3A6ABC837882F882C837834535929389A12B2C78187E2EA07EF8B8EEF27DC85002C3AE35F1A50BEE6A1C48BA7E175F3316670B27983472AA6A61EED0A683A39EE323080620EA44A9F74411AE5CE99030528F9AB49C79F2 -Out = 19c179db7f2c884c85921e2953deeab1fc6252db73b7422d8f09cbb577abb121bc7d28376d2c95c3e176f53e169323c2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBD +Out = CA0299A194825AC60197E2017618A656D8CE4973C6B2FF434DCCC202A3692373BC05BE34CC5F4E7BBC133D32D3A7D17C -In = 8C3798E51BC68482D7337D3ABB75DC9FFE860714A9AD73551E120059860DDE24AB87327222B64CF774415A70F724CDF270DE3FE47DDA07B61C9EF2A3551F45A5584860248FABDE676E1CD75F6355AA3EAEABE3B51DC813D9FB2EAA4F0F1D9F834D7CAD9C7C695AE84B329385BC0BEF895B9F1EDF44A03D4B410CC23A79A6B62E4F346A5E8DD851C2857995DDBF5B2D717AEB847310E1F6A46AC3D26A7F9B44985AF656D2B7C9406E8A9E8F47DCB4EF6B83CAACF9AEFB6118BFCFF7E44BEF6937EBDDC89186839B77 -Out = 65bb6e8b834040c1abb67d6cf1a0040e615f1d6e893ec18c2705fcf7872ae5545264e92ee6623f8dd7d960ac02713105 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBE +Out = FA0B8D238239F40622207F0CB6B08ED33DBA359ACB9FE7FF26EA0E1893F909D695AE7BFB87ED74199DC23C9E68CBBDA4 -In = FA56BF730C4F8395875189C10C4FB251605757A8FECC31F9737E3C2503B02608E6731E85D7A38393C67DE516B85304824BFB135E33BF22B3A23B913BF6ACD2B7AB85198B8187B2BCD454D5E3318CACB32FD6261C31AE7F6C54EF6A7A2A4C9F3ECB81CE3555D4F0AD466DD4C108A90399D70041997C3B25345A9653F3C9A6711AB1B91D6A9D2216442DA2C973CBD685EE7643BFD77327A2F7AE9CB283620A08716DFB462E5C1D65432CA9D56A90E811443CD1ECB8F0DE179C9CB48BA4F6FEC360C66F252F6E64EDC96B -Out = c0db6f13224bbd06f1c3d788fccade03fae995e4bf31d22b624af44ed8c31f9ec666a5619be5eba4207cd1bbe3c41c39 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF +Out = FD7ACCB685C639F49D4D21AE57E2D3971A902D1892737220BFEDDA0FD89B33A97D8B96B18773910220D268A61CCDD070 -In = B6134F9C3E91DD8000740D009DD806240811D51AB1546A974BCB18D344642BAA5CD5903AF84D58EC5BA17301D5EC0F10CCD0509CBB3FD3FFF9172D193AF0F782252FD1338C7244D40E0E42362275B22D01C4C3389F19DD69BDF958EBE28E31A4FFE2B5F18A87831CFB7095F58A87C9FA21DB72BA269379B2DC2384B3DA953C7925761FED324620ACEA435E52B424A7723F6A2357374157A34CD8252351C25A1B232826CEFE1BD3E70FFC15A31E7C0598219D7F00436294D11891B82497BC78AA5363892A2495DF8C1EEF -Out = 1318f393e1da8eec72389731ae116196b10f4c319c06cad9bbb2f98fa936953890754ef3dd7cf8b30a727e3e09a55abc +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0 +Out = C62FE53E61FED07C1718A5BBB3FC65141F4F294BFF1CAB291EF62DB856953EFFD0FAB5F3BD20F8E4AAF48DDC71F3EFDB -In = C941CDB9C28AB0A791F2E5C8E8BB52850626AA89205BEC3A7E22682313D198B1FA33FC7295381354858758AE6C8EC6FAC3245C6E454D16FA2F51C4166FAB51DF272858F2D603770C40987F64442D487AF49CD5C3991CE858EA2A60DAB6A65A34414965933973AC2457089E359160B7CDEDC42F29E10A91921785F6B7224EE0B349393CDCFF6151B50B377D609559923D0984CDA6000829B916AB6896693EF6A2199B3C22F7DC5500A15B8258420E314C222BC000BC4E5413E6DD82C993F8330F5C6D1BE4BC79F08A1A0A46 -Out = 6c3dc89984841a71747ffaf533118584ce4aa6a5bfb2aff2887505a2f8a3bb13468f97e22ebb46857cbd60bdcbe37bb9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1 +Out = D0882A9FB46B0C8D2C70DE5EFC5569027B516450835E3AD9C5FFB7A86962B55436B692B4E930B66D0CDCE2FE3BCE25C5 -In = 4499EFFFAC4BCEA52747EFD1E4F20B73E48758BE915C88A1FFE5299B0B005837A46B2F20A9CB3C6E64A9E3C564A27C0F1C6AD1960373036EC5BFE1A8FC6A435C2185ED0F114C50E8B3E4C7ED96B06A036819C9463E864A58D6286F785E32A804443A56AF0B4DF6ABC57ED5C2B185DDEE8489EA080DEEEE66AA33C2E6DAB36251C402682B6824821F998C32163164298E1FAFD31BABBCFFB594C91888C6219079D907FDB438ED89529D6D96212FD55ABE20399DBEFD342248507436931CDEAD496EB6E4A80358ACC78647D043 -Out = 0ed3256bf5be71f9e1c168b719ac8125a583204e656bf4f6b25f3e2f7902b4297cb3f54ed5ef56f4447496ec21b2598b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2 +Out = 8DF5F9265406DEB1DCA57AB46E423AE7E9D5EAA1E650EF9B399DE74C175C694324BAEC0386AEA2AF0C75F2AAA1F4D421 -In = EECBB8FDFA4DA62170FD06727F697D81F83F601FF61E478105D3CB7502F2C89BF3E8F56EDD469D049807A38882A7EEFBC85FC9A950952E9FA84B8AFEBD3CE782D4DA598002827B1EB98882EA1F0A8F7AA9CE013A6E9BC462FB66C8D4A18DA21401E1B93356EB12F3725B6DB1684F2300A98B9A119E5D27FF704AFFB618E12708E77E6E5F34139A5A41131FD1D6336C272A8FC37080F041C71341BEE6AB550CB4A20A6DDB6A8E0299F2B14BC730C54B8B1C1C487B494BDCCFD3A53535AB2F231590BF2C4062FD2AD58F906A2D0D -Out = 5d90e16b9610b03a12956155092560e5dd17f6c4fa2b07ce8d2bd885344be0c1a9189b76a521c37e704d79841a9d764a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3 +Out = 3887E609D8841E57CE7415CCF815C34D7A9A31C2A1A29B9007E4D826BDA8EFF5EB8D8DBB6F9496FD2565B83ADD400DD7 -In = E64F3E4ACE5C8418D65FEC2BC5D2A303DD458034736E3B0DF719098BE7A206DEAF52D6BA82316CAF330EF852375188CDE2B39CC94AA449578A7E2A8E3F5A9D68E816B8D16889FBC0EBF0939D04F63033AE9AE2BDAB73B88C26D6BD25EE460EE1EF58FB0AFA92CC539F8C76D3D097E7A6A63EBB9B5887EDF3CF076028C5BBD5B9DB3211371AD3FE121D4E9BF44229F4E1ECF5A0F9F0EBA4D5CEB72878AB22C3F0EB5A625323AC66F7061F4A81FAC834471E0C59553F108475FE290D43E6A055AE3EE46FB67422F814A68C4BE3E8C9 -Out = f8c565996e3cf46f18622ef3a962253ab57b3ba8b4f7c8a7bcffe4db2015c7fcf093c76f7deaa0e4cbfd94d2e21bb7c2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4 +Out = 0694FF8371961712D8A35970009BE8E3953E09E54F119EDFE4B947B11CFAF078ED961AE1F335BB17FC389A9EA35F7EE1 -In = D2CB2D733033F9E91395312808383CC4F0CA974E87EC68400D52E96B3FA6984AC58D9AD0938DDE5A973008D818C49607D9DE2284E7618F1B8AED8372FBD52ED54557AF4220FAC09DFA8443011699B97D743F8F2B1AEF3537EBB45DCC9E13DFB438428EE190A4EFDB3CAEB7F3933117BF63ABDC7E57BEB4171C7E1AD260AB0587806C4D137B6316B50ABC9CCE0DFF3ACADA47BBB86BE777E617BBE578FF4519844DB360E0A96C6701290E76BB95D26F0F804C8A4F2717EAC4E7DE9F2CFF3BBC55A17E776C0D02856032A6CD10AD2838 -Out = 7ce9d2e764611ecaf6f468ac6771842bf6d9a557c98288db48c9c597efe6df716bf20eb25c73035ac69e2b8885dbe5a2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5 +Out = 99A8EC7449A5E51B5FB4F6E086714960ED74ADF8FEFE89C1F49054CD34C3A86E673CE92154BC7DBB1DD4F780969EEC3D -In = F2998955613DD414CC111DF5CE30A995BB792E260B0E37A5B1D942FE90171A4AC2F66D4928D7AD377F4D0554CBF4C523D21F6E5F379D6F4B028CDCB9B1758D3B39663242FF3CB6EDE6A36A6F05DB3BC41E0D861B384B6DEC58BB096D0A422FD542DF175E1BE1571FB52AE66F2D86A2F6824A8CFAACBAC4A7492AD0433EEB15454AF8F312B3B2A577750E3EFBD370E8A8CAC1582581971FBA3BA4BD0D76E718DACF8433D33A59D287F8CC92234E7A271041B526E389EFB0E40B6A18B3AAF658E82ED1C78631FD23B4C3EB27C3FAEC8685 -Out = 13a57b2acb6b97058b9b15d520f28b05ccc2bd2ae8b49826cf48ce37bd10aa1c887cab2e5715d2d04c161ddf386a6d52 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6 +Out = B138AAC5B451C7558D02C518636FC80BF49FAAA6AA972D68981A684F500C43E9DDA171AE6168BA8EC2B52D0428FAB4F5 -In = 447797E2899B72A356BA55BF4DF3ACCA6CDB1041EB477BD1834A9F9ACBC340A294D729F2F97DF3A610BE0FF15EDB9C6D5DB41644B9874360140FC64F52AA03F0286C8A640670067A84E017926A70438DB1BB361DEFEE7317021425F8821DEF26D1EFD77FC853B818545D055ADC9284796E583C76E6FE74C9AC2587AA46AA8F8804F2FEB5836CC4B3ABABAB8429A5783E17D5999F32242EB59EF30CD7ADABC16D72DBDB097623047C98989F88D14EAF02A7212BE16EC2D07981AAA99949DDF89ECD90333A77BC4E1988A82ABF7C7CAF3291 -Out = 6703c6f174e5aa6f00dc3a1c778018c00c979f46f7edb9cb467cdabd47653366a14cee1ff9f52be334698bbd0990cae5 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7 +Out = C3FB89D604F306FC6EE2AAFEBEFBF69D26B21DBBDC055166858D527A4501FF479894B533398334379C182AD6747BD1AF -In = 9F2C18ADE9B380C784E170FB763E9AA205F64303067EB1BCEA93DF5DAC4BF5A2E00B78195F808DF24FC76E26CB7BE31DC35F0844CDED1567BBA29858CFFC97FB29010331B01D6A3FB3159CC1B973D255DA9843E34A0A4061CABDB9ED37F241BFABB3C20D32743F4026B59A4CCC385A2301F83C0B0A190B0F2D01ACB8F0D41111E10F2F4E149379275599A52DC089B35FDD5234B0CFB7B6D8AEBD563CA1FA653C5C021DFD6F5920E6F18BFAFDBECBF0AB00281333ED50B9A999549C1C8F8C63D7626C48322E9791D5FF72294049BDE91E73F8 -Out = 6631a04e7dab4fdcbc80ed02dc96358dbcb82b2d24bbe5e895c88acd54429b559d4a5bca36783b8835655c64991c5c34 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8 +Out = 5AF5504C1F18EDF72C918A40EBCDC51B898FA45C42CBB23A64B2BCE44F74716E7BA9465EE8E347E33C3A6DD6C1ED78D1 -In = AE159F3FA33619002AE6BCCE8CBBDD7D28E5ED9D61534595C4C9F43C402A9BB31F3B301CBFD4A43CE4C24CD5C9849CC6259ECA90E2A79E01FFBAC07BA0E147FA42676A1D668570E0396387B5BCD599E8E66AAED1B8A191C5A47547F61373021FA6DEADCB55363D233C24440F2C73DBB519F7C9FA5A8962EFD5F6252C0407F190DFEFAD707F3C7007D69FF36B8489A5B6B7C557E79DD4F50C06511F599F56C896B35C917B63BA35C6FF8092BAF7D1658E77FC95D8A6A43EEB4C01F33F03877F92774BE89C1114DD531C011E53A34DC248A2F0E6 -Out = cc742ed53828b4c0893c3a623ba4658bb0de2c2224577d95aa091604a6c2651c511b5db595d56b8175e79ef7ab8c871d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9 +Out = BAABBDC830ECD27BE8B089AF00D4CC0C9225533721AEF1D24070CAFA20789407FB3CDF27D2AF1FE0058D0BFC85183E09 -In = 3B8E97C5FFC2D6A40FA7DE7FCEFC90F3B12C940E7AB415321E29EE692DFAC799B009C99DCDDB708FCE5A178C5C35EE2B8617143EDC4C40B4D313661F49ABDD93CEA79D117518805496FE6ACF292C4C2A1F76B403A97D7C399DAF85B46AD84E16246C67D6836757BDE336C290D5D401E6C1386AB32797AF6BB251E9B2D8FE754C47482B72E0B394EAB76916126FD68EA7D65EB93D59F5B4C5AC40F7C3B37E7F3694F29424C24AF8C8F0EF59CD9DBF1D28E0E10F799A6F78CAD1D45B9DB3D7DEE4A7059ABE99182714983B9C9D44D7F5643596D4F3 -Out = 81410886bdf351983963e24ceb8c5304ce16ce320c86528064d7d45bf51d330386afbd61f792a8ec52af4f107f5ae74c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CA +Out = 917496B4E3D0D4780A6480FCB04B998A035047BEE524ECB437FB2558A093DB8E1A62E62B0C2B82E73463F7E630DCB197 -In = 3434EC31B10FAFDBFEEC0DD6BD94E80F7BA9DCA19EF075F7EB017512AF66D6A4BCF7D16BA0819A1892A6372F9B35BCC7CA8155EE19E8428BC22D214856ED5FA9374C3C09BDE169602CC219679F65A1566FC7316F4CC3B631A18FB4449FA6AFA16A3DB2BC4212EFF539C67CF184680826535589C7111D73BFFCE431B4C40492E763D9279560AAA38EB2DC14A212D723F994A1FE656FF4DD14551CE4E7C621B2AA5604A10001B2878A897A28A08095C325E10A26D2FB1A75BFD64C250309BB55A44F23BBAC0D5516A1C687D3B41EF2FBBF9CC56D4739 -Out = 237a920f145006fe54302a0d4cb0591b4b0fcade3a55d9cd1c41a509df1b83e89788b28d3f625785f7c59f6c951826fe +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACB +Out = 24CE0EB4A3ED509348442C252E7B173108A20AE8C5042DAB97431CBBB07C5A473B14E44CC5BCFD2101C8EA4DD88E336C -In = 7C7953D81C8D208FD1C97681D48F49DD003456DE60475B84070EF4847C333B74575B1FC8D2A186964485A3B8634FEAA3595AAA1A2F4595A7D6B6153563DEE31BBAC443C8A33EED6D5D956A980A68366C2527B550EE950250DFB691EACBD5D56AE14B970668BE174C89DF2FEA43AE52F13142639C884FD62A3683C0C3792F0F24AB1318BCB27E21F4737FAB62C77EA38BC8FD1CF41F7DAB64C13FEBE7152BF5BB7AB5A78F5346D43CC741CB6F72B7B8980F268B68BF62ABDFB1577A52438FE14B591498CC95F071228460C7C5D5CEB4A7BDE588E7F21C -Out = 0e23eba25fcab77fce3a887d1f9db94cb99142126d2d310d470e71a7e4c6a7df0fa2126eb50d5c977c6c1459b3317873 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCC +Out = 82E9F843BEB8669C9E9A05B4F8F51B59EC6454659E5753CB6327749E8D3940DDD8593ECDBD608A6564B1D638F581D2A0 -In = 7A6A4F4FDC59A1D223381AE5AF498D74B7252ECF59E389E49130C7EAEE626E7BD9897EFFD92017F4CCDE66B0440462CDEDFD352D8153E6A4C8D7A0812F701CC737B5178C2556F07111200EB627DBC299CAA792DFA58F35935299FA3A3519E9B03166DFFA159103FFA35E8577F7C0A86C6B46FE13DB8E2CDD9DCFBA85BDDDCCE0A7A8E155F81F712D8E9FE646153D3D22C811BD39F830433B2213DD46301941B59293FD0A33E2B63ADBD95239BC01315C46FDB678875B3C81E053A40F581CFBEC24A1404B1671A1B88A6D06120229518FB13A74CA0AC5AE -Out = caf6a36a964a994ebbe5c5df0e593316feb66909d729a25273e5bd949ca344dd0dd87ace49fa7d8e2bcc349005bd4ffc +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCD +Out = 9ACDFCF2BCE50A32B083A69418B2814B10571AE455FD5179480AA02A488962B28CED5D7F54DF3569DB34E11B6276E984 -In = D9FAA14CEBE9B7DE551B6C0765409A33938562013B5E8E0E1E0A6418DF7399D0A6A771FB81C3CA9BD3BB8E2951B0BC792525A294EBD1083688806FE5E7F1E17FD4E3A41D00C89E8FCF4A363CAEDB1ACB558E3D562F1302B3D83BB886ED27B76033798131DAB05B4217381EAAA7BA15EC820BB5C13B516DD640EAEC5A27D05FDFCA0F35B3A5312146806B4C0275BCD0AAA3B2017F346975DB566F9B4D137F4EE10644C2A2DA66DEECA5342E236495C3C6280528BFD32E90AF4CD9BB908F34012B52B4BC56D48CC8A6B59BAB014988EABD12E1A0A1C2E170E7 -Out = 19cc31e2e24966c2af3a6004de188b9aff367d8ed6db06e84ac5fa2f72ee0e13f410812ac2d263981a598e2ff9c0563c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCE +Out = E83BF11E8469A7F17EE92CA39BBCC34BE04316C71DC698C008B85B5D578BCE3E3528A4001C4A39DBE0BB24B97973AEC7 -In = 2D8427433D0C61F2D96CFE80CF1E932265A191365C3B61AAA3D6DCC039F6BA2AD52A6A8CC30FC10F705E6B7705105977FA496C1C708A277A124304F1FC40911E7441D1B5E77B951AAD7B01FD5DB1B377D165B05BBF898042E39660CAF8B279FE5229D1A8DB86C0999ED65E53D01CCBC4B43173CCF992B3A14586F6BA42F5FE30AFA8AE40C5DF29966F9346DA5F8B35F16A1DE3AB6DE0F477D8D8660918060E88B9B9E9CA6A4207033B87A812DBF5544D39E4882010F82B6CE005F8E8FF6FE3C3806BC2B73C2B83AFB704345629304F9F86358712E9FAE3CA3E -Out = a614be4171d5df5b2b3f6ece56a2a83f7ee89a8d8c7659f0315451c8636e4186448a3b458a03d0a4cb4c1177b409457a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECF +Out = 8A1A5287F1875B39342B60DEA85ACB93BBB3A4DA75C13D15A88253BA0591C7308646264150B19A2B698C288419813759 -In = 5E19D97887FCAAC0387E22C6F803C34A3DACD2604172433F7A8A7A526CA4A2A1271ECFC5D5D7BE5AC0D85D921095350DFC65997D443C21C8094E0A3FEFD2961BCB94AED03291AE310CCDA75D8ACE4BC7D89E7D3E5D1650BDA5D668B8B50BFC8E608E184F4D3A9A2BADC4FF5F07E0C0BC8A9F2E0B2A26FD6D8C550008FAAAB75FD71AF2A424BEC9A7CD9D83FAD4C8E9319115656A8717D3B523A68FF8004258B9990ED362308461804BA3E3A7E92D8F2FFAE5C2FBA55BA5A3C27C0A2F71BD711D2FE1799C2ADB31B200035481E9EE5C4ADF2AB9C0FA50B23975CF -Out = 2d448485075cd13048e9ca9ba6d9ef76b06ba367020f419947731a566096d9f3794a5d25f5d173face53038bb19212f7 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0 +Out = BFB72AF495427F4DA2663D7A71F2CFB08AAA195E9F27064B4360256AC4CF243ED69BBB5DE791F058BB2BC80149B71295 -In = C8E976AB4638909387CE3B8D4E510C3230E5690E02C45093B1D297910ABC481E56EEA0F296F98379DFC9080AF69E73B2399D1C143BEE80AE1328162CE1BA7F6A8374679B20AACD380EB4E61382C99998704D62701AFA914F9A2705CDB065885F50D086C3EB5753700C387118BB142F3E6DA1E988DFB31AC75D7368931E45D1391A274B22F83CEB072F9BCABC0B216685BFD789F5023971024B1878A205442522F9EA7D8797A4102A3DF41703768251FD5E017C85D1200A464118AA35654E7CA39F3C375B8EF8CBE7534DBC64BC20BEFB417CF60EC92F63D9EE7397 -Out = 84f60d2b8fa0c64eaf672d9b2dcd4500c63dfcecc610d830afd75332e7bc3fe3b38713dca0ef4c2c296df670a1d2f900 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1 +Out = 47BE392C89A6A1E9FE451B5E869BFBDA0A9A23453C66D81FF3E716DA65F6E4CCDCB6CFC1315D627B7A4D0938E04FBB89 -In = 7145FA124B7429A1FC2231237A949BA7201BCC1822D3272DE005B682398196C25F7E5CC2F289FBF44415F699CB7FE6757791B1443410234AE061EDF623359E2B4E32C19BF88450432DD01CAA5EB16A1DC378F391CA5E3C4E5F356728BDDD4975DB7C890DA8BBC84CC73FF244394D0D48954978765E4A00B593F70F2CA082673A261ED88DBCEF1127728D8CD89BC2C597E9102CED6010F65FA75A14EBE467FA57CE3BD4948B6867D74A9DF5C0EC6F530CBF2EE61CE6F06BC8F2864DFF5583776B31DF8C7FFCB61428A56BF7BD37188B4A5123BBF338393AF46EDA85E6 -Out = 33145feca0d4f671faa806b771f7fbf346ac274f276bceec27acdbaba25d8a79fe9f815702426fa1324d3f56221ba394 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2 +Out = FF110E7ABE253D27C0109FD1A4AF2A89290828BBF06F3233C1E0CD979C1372F1235216B66D0BAF3F9FE1E503C6983746 -In = 7FDFADCC9D29BAD23AE038C6C65CDA1AEF757221B8872ED3D75FF8DF7DA0627D266E224E812C39F7983E4558BFD0A1F2BEF3FEB56BA09120EF762917B9C093867948547AEE98600D10D87B20106878A8D22C64378BF634F7F75900C03986B077B0BF8B740A82447B61B99FEE5376C5EB6680EC9E3088F0BDD0C56883413D60C1357D3C811950E5890E7600103C916341B80C743C6A852B7B4FB60C3BA21F3BC15B8382437A68454779CF3CD7F9F90CCC8EF28D0B706535B1E4108EB5627BB45D719CB046839AEE311CA1ABDC8319E050D67972CB35A6B1601B25DBF487 -Out = 8c1d659811dff725eb1fb72630bdc250718f6365a6c5d508fda703bf0e1bf112cd462b57a6c2d9eac55d489cdc55208e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3 +Out = BAD05016EC91AB339F8F5790A86938AAB9F9A96C317F61344BE0E49FC17F49B47F11E35DFDE96D1AFC74D73F42E73712 -In = 988638219FD3095421F826F56E4F09E356296B628C3CE6930C9F2E758FD1A80C8273F2F61E4DAAE65C4F110D3E7CA0965AC7D24E34C0DC4BA2D6FF0BF5BBE93B3585F354D7543CB542A1AA54674D375077F2D360A8F4D42F3DB131C3B7AB7306267BA107659864A90C8C909460A73621D1F5D9D3FD95BEB19B23DB1CB6C0D0FBA91D36891529B8BD8263CAA1BAB56A4AFFAED44962DF096D8D5B1EB845EF31188B3E10F1AF811A13F156BEB7A288AAE593EBD1471B624AA1A7C6ADF01E2200B3D72D88A3AED3100C88231E41EFC376906F0B580DC895F080FDA5741DB1CB -Out = 635faadd379c9d2fdfb90fb44ea9cb40433045fdddaf3c2780cff6a22135de133337063397bed3831c2553a8b0de7e08 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4 +Out = 5DC02FB03284E604C66F38F61936A1557AB265423C36AC4C66CC5AF4BD282BFFC2EE33EC924FCD47C8B771695494B487 -In = 5AAB62756D307A669D146ABA988D9074C5A159B3DE85151A819B117CA1FF6597F6156E80FDD28C9C3176835164D37DA7DA11D94E09ADD770B68A6E081CD22CA0C004BFE7CD283BF43A588DA91F509B27A6584C474A4A2F3EE0F1F56447379240A5AB1FB77FDCA49B305F07BA86B62756FB9EFB4FC225C86845F026EA542076B91A0BC2CDD136E122C659BE259D98E5841DF4C2F60330D4D8CDEE7BF1A0A244524EECC68FF2AEF5BF0069C9E87A11C6E519DE1A4062A10C83837388F7EF58598A3846F49D499682B683C4A062B421594FAFBC1383C943BA83BDEF515EFCF10D -Out = 378727f32dac6cbab01b65d5f6c3ad39c33428ad7c0914e39901a07ef32b0499f246f1d7ab3cb1516bd250b2767317c2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5 +Out = BE13DF098FA3CE6C0BC1EE8551D28FAB56989C41BC273F1E5289204A172266C294F12E631841AE8ABF90DDF15C9C9993 -In = 47B8216AA0FBB5D67966F2E82C17C07AA2D6327E96FCD83E3DE7333689F3EE79994A1BF45082C4D725ED8D41205CB5BCDF5C341F77FACB1DA46A5B9B2CBC49EADF786BCD881F371A95FA17DF73F606519AEA0FF79D5A11427B98EE7F13A5C00637E2854134691059839121FEA9ABE2CD1BCBBBF27C74CAF3678E05BFB1C949897EA01F56FFA4DAFBE8644611685C617A3206C7A7036E4AC816799F693DAFE7F19F303CE4EBA09D21E03610201BFC665B72400A547A1E00FA9B7AD8D84F84B34AEF118515E74DEF11B9188BD1E1F97D9A12C30132EC2806339BDADACDA2FD8B78 -Out = dbea71a183f4a5bc8c8900d63bc13c02164994d09ddece4f6d49c161322e06409f32e6f07efc08e6f3d69819c8d228b0 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6 +Out = 3EE39B0127853E2B42ABEAA2C3CAAB17A8474945CC140EE09146F2B2A9844D8490AEA003FDB7329525F4E3E49D222F8B -In = 8CFF1F67FE53C098896D9136389BD8881816CCAB34862BB67A656E3D98896F3CE6FFD4DA73975809FCDF9666760D6E561C55238B205D8049C1CEDEEF374D1735DAA533147BFA960B2CCE4A4F254176BB4D1BD1E89654432B8DBE1A135C42115B394B024856A2A83DC85D6782BE4B444239567CCEC4B184D4548EAE3FF6A192F343292BA2E32A0F267F31CC26719EB85245D415FB897AC2DA433EE91A99424C9D7F1766A44171D1651001C38FC79294ACCC68CEB5665D36218454D3BA169AE058A831338C17743603F81EE173BFC0927464F9BD728DEE94C6AEAB7AAE6EE3A627E8 -Out = b35ad699186095463013e6eef839e9559eaae0fdfe47aa194b4e52a0daa2b57434e53c4d9e7af148b2abd0368c6e3038 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7 +Out = 313B503A0480DDD1387A8CB1DA3FBA0773C465964AA49DB3893B973FE2345402BFA062532B3D90FC5A679C95FE97C1FD -In = EACD07971CFF9B9939903F8C1D8CBB5D4DB1B548A85D04E037514A583604E787F32992BF2111B97AC5E8A938233552731321522AB5E8583561260B7D13EBEEF785B23A41FD8576A6DA764A8ED6D822D4957A545D5244756C18AA80E1AAD4D1F9C20D259DEE1711E2CC8FD013169FB7CC4CE38B362F8E0936AE9198B7E838DCEA4F7A5B9429BB3F6BBCF2DC92565E3676C1C5E6EB3DD2A0F86AA23EDD3D0891F197447692794B3DFA269611AD97F72B795602B4FDB198F3FD3EB41B415064256E345E8D8C51C555DC8A21904A9B0F1AD0EFFAB7786AAC2DA3B196507E9F33CA356427 -Out = 4fc7e15dbbe3f65851712aa62f0f131eaaf94e9d1c52c41730b8cff271586b7927dc7908a25c445bf4c6686289fbb464 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8 +Out = 3A59FA6049CF57C5F0459B4DE0AA3EA727A223E63B9098177D9ECB6BBDB59DDEDE8E0DFD798DCFFE9B641BEA32629745 -In = 23AC4E9A42C6EF45C3336CE6DFC2FF7DE8884CD23DC912FEF0F7756C09D335C189F3AD3A23697ABDA851A81881A0C8CCAFC980AB2C702564C2BE15FE4C4B9F10DFB2248D0D0CB2E2887FD4598A1D4ACDA897944A2FFC580FF92719C95CF2AA42DC584674CB5A9BC5765B9D6DDF5789791D15F8DD925AA12BFFAFBCE60827B490BB7DF3DDA6F2A143C8BF96ABC903D83D59A791E2D62814A89B8080A28060568CF24A80AE61179FE84E0FFAD00388178CB6A617D37EFD54CC01970A4A41D1A8D3DDCE46EDBBA4AB7C90AD565398D376F431189CE8C1C33E132FEAE6A8CD17A61C630012 -Out = 32ee08e3bef3c8047b351b71a20fd46690207cf7b725a8d14fef260c3950e3e6340e97a1b41d8972ebbe346bc9e498c8 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9 +Out = F5F5695B468E89E150B8D0769D5866FAE28D2F4B74EB01DF91744F06D88088681EED0CDBAAEEAFCEF069680FDAD53CC6 -In = 0172DF732282C9D488669C358E3492260CBE91C95CFBC1E3FEA6C4B0EC129B45F242ACE09F152FC6234E1BEE8AAB8CD56E8B486E1DCBA9C05407C2F95DA8D8F1C0AF78EE2ED82A3A79EC0CB0709396EE62AADB84F8A4EE8A7CCCA3C1EE84E302A09EA802204AFECF04097E67D0F8E8A9D2651126C0A598A37081E42D168B0AE8A71951C524259E4E2054E535B779679BDADE566FE55700858618E626B4A0FAF895BCCE9011504A49E05FD56127EAE3D1F8917AFB548ECADABDA1020111FEC9314C413498A360B08640549A22CB23C731ACE743252A8227A0D2689D4C6001606678DFB921 -Out = 922662629b8567737aff249b99a3ee04bbbe0eec2e5a4749ce027e5ac1e0af99d7d1980c125be7f00c364e7e4d9205a6 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DA +Out = 9F3217D32F0399547A5F5476FDCDC45D60ADB4EC3ACA0C9AFB12E3B914F5A3E6A42E673A3156F842C482C9AED6F452F7 -In = 3875B9240CF3E0A8B59C658540F26A701CF188496E2C2174788B126FD29402D6A75453BA0635284D08835F40051A2A9683DC92AFB9383719191231170379BA6F4ADC816FECBB0F9C446B785BF520796841E58878B73C58D3EBB097CE4761FDEABE15DE2F319DFBAF1742CDEB389559C788131A6793E193856661376C81CE9568DA19AA6925B47FFD77A43C7A0E758C37D69254909FF0FBD415EF8EB937BCD49F91468B49974C07DC819ABD67395DB0E05874FF83DDDAB895344ABD0E7111B2DF9E58D76D85AD98106B36295826BE04D435615595605E4B4BB824B33C4AFEB5E7BB0D19F909 -Out = 3ecd165631b7c251f0d849d42e819340d0118d74d0aa42f5eaf4971dcb8608e3869ebaad35bcb71e27bbf9ae2de45b04 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADB +Out = DC10EB95F4A95C62A62361277E3B7B4EBD26A0CFB4D40BD9B60124A718E64138F634797C4AF404E38CDCA2AF1F891E8A -In = 747CC1A59FEFBA94A9C75BA866C30DC5C1CB0C0F8E9361D98484956DD5D1A40F6184AFBE3DAC9F76028D1CAECCFBF69199C6CE2B4C092A3F4D2A56FE5A33A00757F4D7DEE5DFB0524311A97AE0668A47971B95766E2F6DD48C3F57841F91F04A00AD5EA70F2D479A2620DC5CD78EAAB3A3B011719B7E78D19DDF70D9423798AF77517EBC55392FCD01FC600D8D466B9E7A7A85BF33F9CC5419E9BD874DDFD60981150DDAF8D7FEBAA4374F0872A5628D318000311E2F5655365AD4D407C20E5C04DF17A222E7DEEC79C5AB1116D8572F91CD06E1CCC7CED53736FC867FD49ECEBE6BF8082E8A -Out = 2f73ebe57fdca33764fcebb88ab6e3196c3b92c6c05d4834d20cb226e3fb9959f0a1e904aff8ef452bd75bd04e553936 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDC +Out = 58B9D8007536A0ADE76CD798928830E8B2454565BFBB9B9CB4F212C423CC01B2707F23FC1B98B153E453AFF75228BFF6 -In = 57AF971FCCAEC97435DC2EC9EF0429BCEDC6B647729EA168858A6E49AC1071E706F4A5A645CA14E8C7746D65511620682C906C8B86EC901F3DDED4167B3F00B06CBFAC6AEE3728051B3E5FF10B4F9ED8BD0B8DA94303C833755B3CA3AEDDF0B54BC8D6632138B5D25BAB03D17B3458A9D782108006F5BB7DE75B5C0BA854B423D8BB801E701E99DC4FEAAD59BC1C7112453B04D33EA3635639FB802C73C2B71D58A56BBD671B18FE34ED2E3DCA38827D63FDB1D4FB3285405004B2B3E26081A8FF08CD6D2B08F8E7B7E90A2AB1ED7A41B1D0128522C2F8BFF56A7FE67969422CE839A9D4608F03 -Out = 9d8cca44d87a09c02a4e2c960109fb5753958b9dfbe2fb19106505039a52ed8c3dc5430a18bc61c8e99b8d79ef656199 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDD +Out = 96588A4D0EAFA475F6C5EF4A541057F2B42CB39A80D9304CC42FF36E52F4ED01E59477468D1C4A4E76689EE63CF3F2F6 -In = 04E16DEDC1227902BAAF332D3D08923601BDD64F573FAA1BB7201918CFE16B1E10151DAE875DA0C0D63C59C3DD050C4C6A874011B018421AFC4623AB0381831B2DA2A8BA42C96E4F70864AC44E106F94311051E74C77C1291BF5DB9539E69567BF6A11CF6932BBBAD33F8946BF5814C066D851633D1A513510039B349939BFD42B858C21827C8FF05F1D09B1B0765DC78A135B5CA4DFBA0801BCADDFA175623C8B647EACFB4444B85A44F73890607D06D507A4F8393658788669F6EF4DEB58D08C50CA0756D5E2F49D1A7AD73E0F0B3D3B5F090ACF622B1878C59133E4A848E05153592EA81C6FBF -Out = d4a4a038dc4e8ebf7f89ade2ec890a6414b1738276531c3f4b62849b6f0571d0561e59cc048f67fc07bd5422ea1d6f4b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDE +Out = 75F4E8ABE2665CAC88D4AF0B5965E553667D44F735BB6FBF50C200EF72F842BEEF8247561898258CE1B91B03FA181BEB -In = 7C815C384EEE0F288ECE27CCED52A01603127B079C007378BC5D1E6C5E9E6D1C735723ACBBD5801AC49854B2B569D4472D33F40BBB8882956245C366DC3582D71696A97A4E19557E41E54DEE482A14229005F93AFD2C4A7D8614D10A97A9DFA07F7CD946FA45263063DDD29DB8F9E34DB60DAA32684F0072EA2A9426ECEBFA5239FB67F29C18CBAA2AF6ED4BF4283936823AC1790164FEC5457A9CBA7C767CA59392D94CAB7448F50EB34E9A93A80027471CE59736F099C886DEA1AB4CBA4D89F5FC7AE2F21CCD27F611ECA4626B2D08DC22382E92C1EFB2F6AFDC8FDC3D2172604F5035C46B8197D3 -Out = 2139e0f51574d76cbdc150fa01a65d3e6aea258b86d4f8e9215dbdbff17be4a6ef903a0ab60492d745eb45ef31cf72d4 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF +Out = 01F15A3254772AF6633FF9071244E79F2A5A5245B01DE8EA96BB83D4EEA7358BF2A5D605D9444E8FBB603A93E3FA3533 -In = E29D505158DBDD937D9E3D2145658EE6F5992A2FC790F4F608D9CDB44A091D5B94B88E81FAC4FDF5C49442F13B911C55886469629551189EAFF62488F1A479B7DB11A1560E198DDCCCCF50159093425FF7F1CB8D1D1246D0978764087D6BAC257026B090EFAE8CEC5F22B6F21C59ACE1AC7386F5B8837CA6A12B6FBF5534DD0560EF05CA78104D3B943DDB220FEAEC89AA5E692A00F822A2AB9A2FE60350D75E7BE16FF2526DC643872502D01F42F188ABED0A6E9A6F5FD0D1CE7D5755C9FFA66B0AF0B20BD806F08E06156690D81AC811778CA3DAC2C249B96002017FCE93E507E3B953ACF99964B847 -Out = 1f2ff8112dbaee699034bffdb41a8b9134dd988f88b07490b55f63456acb803aef7e3beb172c72888b679336ebbad7dc +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0 +Out = 23CB6BF2EDE9199F57324CB43E9DB7A1E8FADF8997295DAB095297108DE429A6594DD9B80EB8BEA86E5D5D3C16A3E8FA -In = D85588696F576E65ECA0155F395F0CFACD83F36A99111ED5768DF2D116D2121E32357BA4F54EDE927F189F297D3A97FAD4E9A0F5B41D8D89DD7FE20156799C2B7B6BF9C957BA0D6763F5C3BC5129747BBB53652B49290CFF1C87E2CDF2C4B95D8AAEE09BC8FBFA6883E62D237885810491BFC101F1D8C636E3D0EDE838AD05C207A3DF4FAD76452979EB99F29AFAECEDD1C63B8D36CF378454A1BB67A741C77AC6B6B3F95F4F02B64DABC15438613EA49750DF42EE90101F115AA9ABB9FF64324DDE9DABBB01054E1BD6B4BCDC7930A44C2300D87CA78C06924D0323AD7887E46C90E8C4D100ACD9EED21E -Out = 72108b77eaa6c8e9343fb76c22c8e8880595d508317f5cbfe186145bf8c0ae55fc572c2630eae05ccb5af80e2da1d74e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1 +Out = BAAA44617DEB6EC6F686D609337BFA50278CFD4E7DDC18F674A604151EB26D1D2904BEF0F928CB45E263321B0EAD96B6 -In = 3A12F8508B40C32C74492B66323375DCFE49184C78F73179F3314B79E63376B8AC683F5A51F1534BD729B02B04D002F55CBD8E8FC9B5EC1EA6BBE6A0D0E7431518E6BA45D124035F9D3DCE0A8BB7BF1430A9F657E0B4EA9F20EB20C786A58181A1E20A96F1628F8728A13BDF7A4B4B32FC8AA7054CC4881AE7FA19AFA65C6C3EE1B3ADE3192AF42054A8A911B8EC1826865D46D93F1E7C5E2B7813C92A506E53886F3D4701BB93D2A681AD109C845904BB861AF8AF0646B6E399B38B614051D34F6842563A0F37EC00CB3D865FC5D746C4987DE2A65071100883A2A9C7A2BFE1E2DD603D9EA24DC7C5FD06BE -Out = f7c53a713b12d71a6c1d9d1807830b9741543d015d3d8a9c9dcc00a259f242fef75da89db2e88955b96d5cb4e82d61ea +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2 +Out = 2869DE18E00085B176BCBD60DB172EC57B6DF786F0E6AD675773F5923516B35F87868579DFCB7C640CB350ACF4BA661E -In = 1861EDCE46FA5AD17E1FF1DEAE084DEC580F97D0A67885DFE834B9DFAC1AE076742CE9E267512CA51F6DF5A455AF0C5FD6ABF94ACEA103A3370C354485A7846FB84F3AC7C2904B5B2FBF227002CE512133BB7E1C4E50057BFD1E44DB33C7CDB969A99E284B184F50A14B068A1FC5009D9B298DBE92239572A7627AAC02ABE8F3E3B473417F36D4D2505D16B7577F4526C9D94A270A2DFE450D06DA8F6FA956879A0A55CFE99E742EA555EA477BA3E9B44CCD508C375423611AF92E55345DC215779B2D5119EBA49C71D49B9FE3F1569FA24E5CA3E332D042422A8B8158D3EC66A80012976F31FFDF305F0C9C5E -Out = efcf4a1c0f5c537fb5f48654dd6f120f48c572a8edc8f7cc3b5a9fa80c2ff5c98490ea3faa3947582aa8e0ee4331211c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3 +Out = C71EC380475733D52C6D05F37A5356DD4B695C83B7893EE1F46DCE48AFB039112C5A23A11A8898AC5C7F9149C2681608 -In = 08D0FFDE3A6E4EF65608EA672E4830C12943D7187CCFF08F4941CFC13E545F3B9C7AD5EEBBE2B01642B486CAF855C2C73F58C1E4E3391DA8E2D63D96E15FD84953AE5C231911B00AD6050CD7AAFDAAC9B0F663AE6AAB45519D0F5391A541707D479034E73A6AD805AE3598096AF078F1393301493D663DD71F83869CA27BA508B7E91E81E128C1716DC3ACFE3084B2201E04CF8006617EECF1B640474A5D45CFDE9F4D3EF92D6D055B909892194D8A8218DB6D8203A84261D200D71473D7488F3427416B6896C137D455F231071CACBC86E0415AB88AEC841D96B7B8AF41E05BB461A40645BF176601F1E760DE5F -Out = aa8e8181b8c003e0ade20560d96bb1ca8d135b89e7eac7257e5c83afebce6691412a35cde5417570bbf05e155903d35e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4 +Out = 4BA5FCBB593DF2612912E0910DD1B42CA8566F643B7F80BB92B79079C42F787936E0CD4CBD12C2315B1D4264B4D40C20 -In = D782ABB72A5BE3392757BE02D3E45BE6E2099D6F000D042C8A543F50ED6EBC055A7F133B0DD8E9BC348536EDCAAE2E12EC18E8837DF7A1B3C87EC46D50C241DEE820FD586197552DC20BEEA50F445A07A38F1768A39E2B2FF05DDDEDF751F1DEF612D2E4D810DAA3A0CC904516F9A43AF660315385178A529E51F8AAE141808C8BC5D7B60CAC26BB984AC1890D0436EF780426C547E94A7B08F01ACBFC4A3825EAE04F520A9016F2FB8BF5165ED12736FC71E36A49A73614739EAA3EC834069B1B40F1350C2B3AB885C02C640B9F7686ED5F99527E41CFCD796FE4C256C9173186C226169FF257954EBDA81C0E5F99 -Out = ca6d1fce2c77aa5166d1921d1ed47267279a01d76edd9f23b99384f2b6d75edfb37d76e703bbf9c1a1c1f3255200089c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5 +Out = 3252104DB29CC76745BA21B7F77F994E68780C7A0C3679C291C7F02BD8A9719A0F7ADEC42930B6EC3DBEA0F1723016A5 -In = 5FCE8109A358570E40983E1184E541833BB9091E280F258CFB144387B05D190E431CB19BAA67273BA0C58ABE91308E1844DCD0B3678BAA42F335F2FA05267A0240B3C718A5942B3B3E3BFA98A55C25A1466E8D7A603722CB2BBF03AFA54CD769A99F310735EE5A05DAE2C22D397BD95635F58C48A67F90E1B73AAFCD3F82117F0166657838691005B18DA6F341D6E90FC1CDB352B30FAE45D348294E501B63252DE14740F2B85AE5299DDEC3172DE8B6D0BA219A20A23BB5E10FF434D39DB3F583305E9F5C039D98569E377B75A70AB837D1DF269B8A4B566F40BB91B577455FD3C356C914FA06B9A7CE24C7317A172D -Out = 471d5dd2ee36fef634630fd1c1ee05a8525e9659e7ea728ba58c377d26b3a6ffd0bdb3ce6d9a0cb41f24b617633bae88 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6 +Out = DA9B50C254272DE5EF502DB79D01D7685097C469B523910842213034458E9051A74C3E040778AFCD75D8598E636EA184 -In = 6172F1971A6E1E4E6170AFBAD95D5FEC99BF69B24B674BC17DD78011615E502DE6F56B86B1A71D3F4348087218AC7B7D09302993BE272E4A591968AEF18A1262D665610D1070EE91CC8DA36E1F841A69A7A682C580E836941D21D909A3AFC1F0B963E1CA5AB193E124A1A53DF1C587470E5881FB54DAE1B0D840F0C8F9D1B04C645BA1041C7D8DBF22030A623AA15638B3D99A2C400FF76F3252079AF88D2B37F35EE66C1AD7801A28D3D388AC450B97D5F0F79E4541755356B3B1A5696B023F39AB7AB5F28DF4202936BC97393B93BC915CB159EA1BD7A0A414CB4B7A1AC3AF68F50D79F0C9C7314E750F7D02FAA58BFA -Out = e96d1daaca88a54db215d70081b7cc1e6a76d958630e7f9b25ad40f81144d3867e00031acdd20ed5da8df4974a48cbbe +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7 +Out = 5D075E4C4B7A0CFDC184A2EAFD1B3E9223E21020DD04DF20F5964A8233C5A7FFC5C623AAC2AB5A1F02EA8D1A5031F593 -In = 5668ECD99DFBE215C4118398AC9C9EAF1A1433FAB4CCDD3968064752B625EA944731F75D48A27D047D67547F14DD0FFAA55FA5E29F7AF0D161D85EAFC4F2029B717C918EAB9D304543290BDBA7158B68020C0BA4E079BC95B5BC0FC044A992B94B4CCD3BD66D0EABB5DBBAB904D62E00752C4E3B0091D773BCF4C14B4377DA3EFFF824B1CB2FA01B32D1E46C909E626ED2DAE920F4C7DBEB635BC754FACBD8D49BEBA3F23C1C41CCBFCD0EE0C114E69737F5597C0BF1D859F0C767E18002AE8E39C26261FFDE2920D3D0BAF0E906138696CFE5B7E32B600F45DF3AAA39932F3A7DF95B60FA8712A2271FCAF3911CE7B511B1 -Out = 3af51bf205bc6dac06826c868419ed5b572a3f9272c2ed0f81fbed6326fa5f508949527b53bfec31033b755dc2b8534c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8 +Out = 16129C669BD55B939C1FB73215E79D43D47052A1EF756D439814B4DCC6C7FEE6E8A8925758CC9EFA3AEEE49989590554 -In = 03D625488354DF30E3F875A68EDFCF340E8366A8E1AB67F9D5C5486A96829DFAC0578289082B2A62117E1CF418B43B90E0ADC881FC6AE8105C888E9ECD21AEA1C9AE1A4038DFD17378FED71D02AE492087D7CDCD98F746855227967CB1AB4714261EE3BEAD3F4DB118329D3EBEF4BC48A875C19BA763966DA0EBEA800E01B2F50B00E9DD4CACA6DCB314D00184EF71EA2391D760C950710DB4A70F9212FFC54861F9DC752CE18867B8AD0C48DF8466EF7231E7AC567F0EB55099E622EBB86CB237520190A61C66AD34F1F4E289CB3282AE3EAAC6152ED24D2C92BAE5A7658252A53C49B7B02DFE54FDB2E90074B6CF310AC661 -Out = ce7834551d72513af5687bc70ae9a5d2fb1301104674be4fc09d59c62d7a97c37d40c955f3b94f27861ad88e5409084a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9 +Out = 06173DEE9A8BE76CDE992894F06D26A70A5284F120AB05BD887F6024B518A6137DE60D0158D7C34F4E1F85A6DDC20AB7 -In = 2EDC282FFB90B97118DD03AAA03B145F363905E3CBD2D50ECD692B37BF000185C651D3E9726C690D3773EC1E48510E42B17742B0B0377E7DE6B8F55E00A8A4DB4740CEE6DB0830529DD19617501DC1E9359AA3BCF147E0A76B3AB70C4984C13E339E6806BB35E683AF8527093670859F3D8A0FC7D493BCBA6BB12B5F65E71E705CA5D6C948D66ED3D730B26DB395B3447737C26FAD089AA0AD0E306CB28BF0ACF106F89AF3745F0EC72D534968CCA543CD2CA50C94B1456743254E358C1317C07A07BF2B0ECA438A709367FAFC89A57239028FC5FECFD53B8EF958EF10EE0608B7F5CB9923AD97058EC067700CC746C127A61EE3 -Out = 2ef5bccf6cbbe34bd442536855a5badabd70d2b943e79c8833aeb4e70956dee63bfa56acbd797bd3be95026ff10b8b32 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EA +Out = 2E5468C9269C522E7A246039CE96F0491F97643776F41A2A70C8958E139D2FD106B952E8A5D766783FDC7A740F0958FA -In = 90B28A6AA1FE533915BCB8E81ED6CACDC10962B7FF82474F845EEB86977600CF70B07BA8E3796141EE340E3FCE842A38A50AFBE90301A3BDCC591F2E7D9DE53E495525560B908C892439990A2CA2679C5539FFDF636777AD9C1CDEF809CDA9E8DCDB451ABB9E9C17EFA4379ABD24B182BD981CAFC792640A183B61694301D04C5B3EAAD694A6BD4CC06EF5DA8FA23B4FA2A64559C5A68397930079D250C51BCF00E2B16A6C49171433B0AADFD80231276560B80458DD77089B7A1BBCC9E7E4B9F881EACD6C92C4318348A13F4914EB27115A1CFC5D16D7FD94954C3532EFACA2CAB025103B2D02C6FD71DA3A77F417D7932685888A -Out = 9ffee5bbe29e5920da7386d4e07c623d98d437b887e40588b48ce90fcfd5eb7d8ce83e61a0b112386e2a5b85fbf89a9d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEB +Out = 893C17BE1F39231F4C98321B0473B04E2CB421E5F6A0D9A22FF1D6E0B46884A52CB8721014280B39C5DA17E5F6CC5EB6 -In = 2969447D175490F2AA9BB055014DBEF2E6854C95F8D60950BFE8C0BE8DE254C26B2D31B9E4DE9C68C9ADF49E4EE9B1C2850967F29F5D08738483B417BB96B2A56F0C8ACA632B552059C59AAC3F61F7B45C966B75F1D9931FF4E596406378CEE91AAA726A3A84C33F37E9CDBE626B5745A0B06064A8A8D56E53AAF102D23DD9DF0A3FDF7A638509A6761A33FA42FA8DDBD8E16159C93008B53765019C3F0E9F10B144CE2AC57F5D7297F9C9949E4FF68B70D339F87501CE8550B772F32C6DA8AD2CE2100A895D8B08FA1EEAD7C376B407709703C510B50F87E73E43F8E7348F87C3832A547EF2BBE5799ABEDCF5E1F372EA809233F006 -Out = 1b9d6a10fdeff6ddf18f3bb03ef3ea321f5fc07e217d706d30e1d01f248c890789722f4d519940d4294fe975cd2ebafa +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBEC +Out = FE61D7BAC3A3159E2F76D090871529ED846584D5710048912D75BB99674E595FD3D483B665287A86AAC5A58D6B1138D5 -In = 721645633A44A2C78B19024EAECF58575AB23C27190833C26875DC0F0D50B46AEA9C343D82EA7D5B3E50EC700545C615DAEAEA64726A0F05607576DCD396D812B03FB6551C641087856D050B10E6A4D5577B82A98AFB89CEE8594C9DC19E79FEFF0382FCFD127F1B803A4B9946F4AC9A4378E1E6E041B1389A53E3450CD32D9D2941B0CBABDB50DA8EA2513145164C3AB6BCBD251C448D2D4B087AC57A59C2285D564F16DA4ED5E607ED979592146FFB0EF3F3DB308FB342DF5EB5924A48256FC763141A278814C82D6D6348577545870AE3A83C7230AC02A1540FE1798F7EF09E335A865A2AE0949B21E4F748FB8A51F44750E213A8FB -Out = 34db131dcf0551187f0f91065ff7e088af1accc3a5e9f2ca1eb3db53d24f014c63e2d751928a253087bba7014a46dd1c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECED +Out = 2ABC3C3D615EDCCC0235D36815DB403378E957BFF9AB44E4D2C94B9D5890BEB3F6C58C4BC6161D302E70AFE6B8736482 -In = 6B860D39725A14B498BB714574B4D37CA787404768F64C648B1751B353AC92BAC2C3A28EA909FDF0423336401A02E63EC24325300D823B6864BB701F9D7C7A1F8EC9D0AE3584AA6DD62EA1997CD831B4BABD9A4DA50932D4EFDA745C61E4130890E156AEE6113716DAF95764222A91187DB2EFFEA49D5D0596102D619BD26A616BBFDA8335505FBB0D90B4C180D1A2335B91538E1668F9F9642790B4E55F9CAB0FE2BDD2935D001EE6419ABAB5457880D0DBFF20ED8758F4C20FE759EFB33141CF0E892587FE8187E5FBC57786B7E8B089612C936DFC03D27EFBBE7C8673F1606BD51D5FF386F4A7AB68EDF59F385EB1291F117BFE717399 -Out = e691fefa41ed8303b4047147e73d2786dfaa3ca665920559cf70fff722bd4c580165c30c600b50099f94b1362f2a395a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEE +Out = 4C592636F932BCCEE6B161ABF1A565543A89CA6C7CABD2E3A3547267D1AA0732C76FE1677AE9888DD919B00A14C3F5FC -In = 6A01830AF3889A25183244DECB508BD01253D5B508AB490D3124AFBF42626B2E70894E9B562B288D0A2450CFACF14A0DDAE5C04716E5A0082C33981F6037D23D5E045EE1EF2283FB8B6378A914C5D9441627A722C282FF452E25A7EA608D69CEE4393A0725D17963D0342684F255496D8A18C2961145315130549311FC07F0312FB78E6077334F87EAA873BEE8AA95698996EB21375EB2B4EF53C14401207DEB4568398E5DD9A7CF97E8C9663E23334B46912F8344C19EFCF8C2BA6F04325F1A27E062B62A58D0766FC6DB4D2C6A1928604B0175D872D16B7908EBC041761187CC785526C2A3873FEAC3A642BB39F5351550AF9770C328AF7B -Out = ad83836b7821c9d25cb18ae8b5746104c46e41e66cbfc4d9474a509730e401b6a606fdf3c5c07980bec8faa0a108fdc2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF +Out = 1B06997AA47379409CA92F969366FF92AE888201B22ECADFB644EF6B3F8417AC220FA338F4CD6C9B08996F0CBFDEE947 -In = B3C5E74B69933C2533106C563B4CA20238F2B6E675E8681E34A389894785BDADE59652D4A73D80A5C85BD454FD1E9FFDAD1C3815F5038E9EF432AAC5C3C4FE840CC370CF86580A6011778BBEDAF511A51B56D1A2EB68394AA299E26DA9ADA6A2F39B9FAFF7FBA457689B9C1A577B2A1E505FDF75C7A0A64B1DF81B3A356001BF0DF4E02A1FC59F651C9D585EC6224BB279C6BEBA2966E8882D68376081B987468E7AED1EF90EBD090AE825795CDCA1B4F09A979C8DFC21A48D8A53CDBB26C4DB547FC06EFE2F9850EDD2685A4661CB4911F165D4B63EF25B87D0A96D3DFF6AB0758999AAD214D07BD4F133A6734FDE445FE474711B69A98F7E2B -Out = 17281ee206a052e79a4b037134d6afc0eb256d0ff11d1eeb08eb895e10e57755da73f80398a1496b85a3ee5e6391b8d2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0 +Out = 8917996EFCA60051306B4D1992911CDFF4926746BFB41768A5EB407A2F2A492869F1C404EFF07B3FFBA03C928195EA3A -In = 83AF34279CCB5430FEBEC07A81950D30F4B66F484826AFEE7456F0071A51E1BBC55570B5CC7EC6F9309C17BF5BEFDD7C6BA6E968CF218A2B34BD5CF927AB846E38A40BBD81759E9E33381016A755F699DF35D660007B5EADF292FEEFB735207EBF70B5BD17834F7BFA0E16CB219AD4AF524AB1EA37334AA66435E5D397FC0A065C411EBBCE32C240B90476D307CE802EC82C1C49BC1BEC48C0675EC2A6C6F3ED3E5B741D13437095707C565E10D8A20B8C20468FF9514FCF31B4249CD82DCEE58C0A2AF538B291A87E3390D737191A07484A5D3F3FB8C8F15CE056E5E5F8FEBE5E1FB59D6740980AA06CA8A0C20F5712B4CDE5D032E92AB89F0AE1 -Out = c3bb34cbd9a8ca8cb34331d06ec1ccdfca00c33d6fc36a0f919472617b02cf23fc9ed85cd85f5d248ae92f4ba60df38f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1 +Out = 5FC13C39D40A4528EEBEC469AE57F32F1BC8C4E36555CE49618CFD3E74ADE57CB8D023A07B8B599076ADAB673073390A -In = A7ED84749CCC56BB1DFBA57119D279D412B8A986886D810F067AF349E8749E9EA746A60B03742636C464FC1EE233ACC52C1983914692B64309EDFDF29F1AB912EC3E8DA074D3F1D231511F5756F0B6EEAD3E89A6A88FE330A10FACE267BFFBFC3E3090C7FD9A850561F363AD75EA881E7244F80FF55802D5EF7A1A4E7B89FCFA80F16DF54D1B056EE637E6964B9E0FFD15B6196BDD7DB270C56B47251485348E49813B4EB9ED122A01B3EA45AD5E1A929DF61D5C0F3E77E1FDC356B63883A60E9CBB9FC3E00C2F32DBD469659883F690C6772E335F617BC33F161D6F6984252EE12E62B6000AC5231E0C9BC65BE223D8DFD94C5004A101AF9FD6C0FB -Out = 668c747e71597c8d48912d0fa7f09e48b4e9db16b431fbfa6be694c840b044d703a99d89f06f2b0da1f464514c5ab2a8 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2 +Out = F66A41D3FDC709E33E6511E329799164BB18FAA44930F47BD6DF4472C5BABB1A41A514BA76F89825265C43DE407EAD03 -In = A6FE30DCFCDA1A329E82AB50E32B5F50EB25C873C5D2305860A835AECEE6264AA36A47429922C4B8B3AFD00DA16035830EDB897831C4E7B00F2C23FC0B15FDC30D85FB70C30C431C638E1A25B51CAF1D7E8B050B7F89BFB30F59F0F20FECFF3D639ABC4255B3868FC45DD81E47EB12AB40F2AAC735DF5D1DC1AD997CEFC4D836B854CEE9AC02900036F3867FE0D84AFFF37BDE3308C2206C62C4743375094108877C73B87B2546FE05EA137BEDFC06A2796274099A0D554DA8F7D7223A48CBF31B7DECAA1EBC8B145763E3673168C1B1B715C1CD99ECD3DDB238B06049885ECAD9347C2436DFF32C771F34A38587A44A82C5D3D137A03CAA27E66C8FF6 -Out = 67b34e61cd24fe2b8c3a5b2112011450dbd099dc4b0a6114cbbb7ce6ee57e060612977e3fb6f51e1626adc52e0080e1a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3 +Out = 7B23F639B5704B57AB3459FFBAE5434D6E1D913494C64081A51613B147C94E6585C8DD0C2E887835BD147DD59BBF72DB -In = 83167FF53704C3AA19E9FB3303539759C46DD4091A52DDAE9AD86408B69335989E61414BC20AB4D01220E35241EFF5C9522B079FBA597674C8D716FE441E566110B6211531CECCF8FD06BC8E511D00785E57788ED9A1C5C73524F01830D2E1148C92D0EDC97113E3B7B5CD3049627ABDB8B39DD4D6890E0EE91993F92B03354A88F52251C546E64434D9C3D74544F23FB93E5A2D2F1FB15545B4E1367C97335B0291944C8B730AD3D4789273FA44FB98D78A36C3C3764ABEEAC7C569C1E43A352E5B770C3504F87090DEE075A1C4C85C0C39CF421BDCC615F9EFF6CB4FE6468004AECE5F30E1ECC6DB22AD9939BB2B0CCC96521DFBF4AE008B5B46BC006E -Out = 65a2ef5d325e731352b279503d004405b6593cff9466189b5c10004fd62eb1dc25681568b6da669ade962a2b253db162 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4 +Out = 10E3C81532095BD18D7252708CD19FA796A43EE417721CFFD047FF54598C38A69389B357E14AFE69703A5863336660D7 -In = 3A3A819C48EFDE2AD914FBF00E18AB6BC4F14513AB27D0C178A188B61431E7F5623CB66B23346775D386B50E982C493ADBBFC54B9A3CD383382336A1A0B2150A15358F336D03AE18F666C7573D55C4FD181C29E6CCFDE63EA35F0ADF5885CFC0A3D84A2B2E4DD24496DB789E663170CEF74798AA1BBCD4574EA0BBA40489D764B2F83AADC66B148B4A0CD95246C127D5871C4F11418690A5DDF01246A0C80A43C70088B6183639DCFDA4125BD113A8F49EE23ED306FAAC576C3FB0C1E256671D817FC2534A52F5B439F72E424DE376F4C565CCA82307DD9EF76DA5B7C4EB7E085172E328807C02D011FFBF33785378D79DC266F6A5BE6BB0E4A92ECEEBAEB1 -Out = c06cba6b82271d5ed9f60c191b531925b8853f2952334509acc60d13cf9d7d4a0a0585ae87030cc4e011eb93579af861 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5 +Out = 6C015B143565592127A5D9E9C90766946CBF83388817B0C26E605C03A4637BCA7F3975BD34712177B4B7A5313BE4ED5A -[BLAKE2b(512)] -In = 616263 -Out = BA80A53F981C4D0D6A2797B69F12F6E94C212F14685AC4B74B12BB6FDBFFA2D17D87C5392AAB792DC252D5DE4533CC9518D38AA8DBF1925AB92386EDD4009923 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6 +Out = C8F8F0A269FACC4D325F1388CA71998FF730415D6E34B76E3ED046B6CA3066B26F0C355417CD261BEF4898E0567C05D2 -In = -Out = 786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7 +Out = DF7F3CD66B7DCDCC6089E2D51C27561AC5FDD261B61ECE80ED067C35B146610998BCF693FE3466DAD5C92BCE663D18EC -In = CC -Out = e9bfc0d3aac9639604e9ec53ee0f41282f2df5ea7a0d6e88a620edf208694b96e8b0a8d21f0908bc6af54a05c16b8379a21016843e535d41b7deca17089e4926 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8 +Out = 30EBA9B4F06B52C4A2C4791972588AA2F2DA6D606D6445FBFFC00477BD69D5106070F5049A0B5CEDAA93506AD7E0B84D -In = 41FB -Out = 37ce1fe37bcfdf1da0018341e405286ba20ee9de3d674e0768861913864121113d3357a2ba48dcd2e3ac11d69ed4f83664bf1da91e47576c45a8ea06d054a01f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9 +Out = 82A241F49733D9909BC3B7881E7337AD00CE85FD8BF7D8B8F531767E48EEA606A04FA75E038692E98B89BD8D673CFA82 -In = 1F877C -Out = f9a58b6f9f3e7355ca9d031e3f5b460f49f7ff9d9ca310d4c6330fa0c869ed15848a9196130430f5f9a1a5dfb7347cb5c444d05ac6234905bdda3d291f046f4b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FA +Out = E9B1E02B6F1E2EF5902BFDE2A3327F960AA97B5D585CCF621F1A509FA02C280EC6BFCC13B293975AE9727A818A7658F9 -In = C1ECFDFC -Out = c524e0f5fc9a535135e9eb6120365e22f0979ff910b24face1468b0e54416dab422425020c0c45cac1763b64e2b7bbdbbfa3314453c7924f6a6ec543eb08f425 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFB +Out = D931609260468BF52988D10632B2844FCADBB1410536FE332301107E0512DBDF48159C6928A7E05D4018D2B474EBF927 -In = 21F134AC57 -Out = 6d132924c50de1017a11e6bb5ec736b2f1617fb7ecf12963cc43f569ea33fdf2c3a17232b30d3054fd7cd8a4b848ece7eabddaf893e9d36499b8e60fe943d723 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFC +Out = E401AF666582970D7EEFEF6CDC74101859CF6A14371F4E650D1CA5C0E50E5D960B6D5835F170DA9274C3E55BA6BDF6A4 -In = C6F50BB74E29 -Out = aa6a1929b2288692f7fb56da27d1c69ecee2c0a2ed21848201b37fa7860066d0a569c95a8088f7ae04a077c0593520c4a7aa6fb0b0bc79751af076985c998129 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFD +Out = 98154F1CF4569B9C64DBDC42C629637185CFA820502B4262B1EA6C0F782F9A1187D23D300F7B9DA8AEA07FE985490F81 -In = 119713CC83EEEF -Out = 6dde70cb29cf808df56472dbef85fae90f22a947b35f7108a67b1c57ef74339a37beaeec0afc2c1f3e45a582b97de3e140b0a70d234b0a1bb21bf8177868fb1d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFE +Out = CBC5A8273028CE5CF0EBE5651C5910ECF16B6F18E4DD28749A72C0DCF2110DCA0A7BEA48512A9EE96277AB0F8A12A3CB -In = 4A4F202484512526 -Out = fee4f7fe1bb9aee79551f365c1dff2051a40c9953c6e7cf166e63e82abb4e9f19032da2c79a441dc483f6d13619db0a497b59171d5d8bd5cced8ffbde18b2868 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF +Out = 9BD2B1BF7A89613FDCC76A3E02DABE81772A97BD5E6274FD9FE72E219BFFE88C5E6F681A31481485DCB85DFA34BDC657 -In = 1F66AB4185ED9B6375 -Out = 6587e21d2f9e6ff2fd6d85998c98f9479f3c9a2942f4894422237df6337e7c7c6d7115113ba9779dc7ab9af610602016ef6509d866bda1dc670adb673377596a +[BLAKE2b(512)] -In = EED7422227613B6F53C9 -Out = 4b54f9536bce7c9dc0978783f78888d6ca10312a6f44d2c7f840f8ba13f48efc27bc9873875a831ef307956928058575fec8c98062617a7a54aa68eba8464618 +In = +Out = 786A02F742015903C6C6FD852552D272912F4740E15847618A86E217F71F5419D25E1031AFEE585313896444934EB04B903A685B1448B755D56F701AFE9BE2CE -In = EAEED5CDFFD89DECE455F1 -Out = 892d8dc7842b5fdb08af06a22046269ca92ca858b2c090899006bd674c11d8c744eed680a20eec97d8d842821d10cc3770672247b1a6f443a02e2492b8c28b9d +In = 00 +Out = 2FA3F686DF876995167E7C2E5D74C4C7B6E48F8068FE0E44208344D480F7904C36963E44115FE3EB2A3AC8694C28BCB4F5A0F3276F2E79487D8219057A506E4B -In = 5BE43C90F22902E4FE8ED2D3 -Out = 3ecce01033e85d14fa2f150cfcb5f85c10a392b516c3dbd72f7f295b83aa300ff6b361482daf69dd4d7b36d580339000dc06e7f58975b8e5d6890f11e0ae7325 +In = 0001 +Out = 1C08798DC641ABA9DEE435E22519A4729A09B2BFE0FF00EF2DCD8ED6F8A07D15EAF4AEE52BBF18AB5608A6190F70B90486C8A7D4873710B1115D3DEBBB4327B5 -In = A746273228122F381C3B46E4F1 -Out = ba01a2d4d3a5309ffe3f09e50bb557b8e114be70f1643acbfba02d1e58d882d8ce98f96b6b8b667532213a5d0563fc6d6b33532472510d6837322f12ff00e6a1 +In = 000102 +Out = 40A374727302D9A4769C17B5F409FF32F58AA24FF122D7603E4FDA1509E919D4107A52C57570A6D94E50967AEA573B11F86F473F537565C66F7039830A85D186 -In = 3C5871CD619C69A63B540EB5A625 -Out = ecbdd9306d5b2e633eca3d17a5a9113c64770d0518c1c4232ba5d45c83855be59678923ff9b86cfa99d503363fdcecbd81fffd3781a2d2d372559baefa9e8cd1 +In = 00010203 +Out = 77DDF4B14425EB3D053C1E84E3469D92C4CD910ED20F92035E0C99D8A7A86CECAF69F9663C20A7AA230BC82F60D22FB4A00B09D3EB8FC65EF547FE63C8D3DDCE -In = FA22874BCC068879E8EF11A69F0722 -Out = 9c82d5926485b2764c5a005732198951b5b253464ffae1d45c1225de16693978a287c99000f70c0a137d5e05fa113efe99bd1564071afd18ddefad343cde35cd +In = 0001020304 +Out = CBAA0BA7D482B1F301109AE41051991A3289BC1198005AF226C5E4F103B66579F461361044C8BA3439FF12C515FB29C52161B7EB9C2837B76A5DC33F7CB2E2E8 -In = 52A608AB21CCDD8A4457A57EDE782176 -Out = e213e496199fe6579b50b45569efd030ce67a76b045a19c9f1c214da41459b4c1e25c6754171d3d174c90d5d45d133bcbed7fdfaf78293bbe7bffd1947989532 +In = 000102030405 +Out = F95D45CF69AF5C2023BDB505821E62E85D7CAEDF7BEDA12C0248775B0C88205EEB35AF3A90816F6608CE7DD44EC28DB1140614E1DDEBF3AA9CD1843E0FAD2C36 -In = 82E192E4043DDCD12ECF52969D0F807EED -Out = 293ab40bf700f8f0d59c0ff823860a2e0a29ea7382ce063f41356f46ddb682392ae2f256a73012330eed8dfe68397f4de671d6d8220d869adc3a3f5bc6174606 +In = 00010203040506 +Out = 8F945BA700F2530E5C2A7DF7D5DCE0F83F9EFC78C073FE71AE1F88204A4FD1CF70A073F5D1F942ED623AA16E90A871246C90C45B621B3401A5DDBD9DF6264165 -In = 75683DCB556140C522543BB6E9098B21A21E -Out = 75ac7e512c3d5b36a05e37f653828d7000019022c74fadc7f46a017eaba1940ad3f055f1edb7243d55420514b82fc917a1664959b0caee3d6e82c08adbb854bf +In = 0001020304050607 +Out = E998E0DC03EC30EB99BB6BFAAF6618ACC620320D7220B3AF2B23D112D8E9CB1262F3C0D60D183B1EE7F096D12DAE42C958418600214D04F5ED6F5E718BE35566 -In = 06E4EFE45035E61FAAF4287B4D8D1F12CA97E5 -Out = 9cd9d8ad54be08ab872a564ca19eb08e054d10764c55f20149d44c784eb016396c497097396dcf6dcfc20632611df7438a2e9ffd4ce9bb221a5d151e4d1599b8 +In = 000102030405060708 +Out = 6A9A090C61B3410AEDE7EC9138146CEB2C69662F460C3DA53C6515C1EB31F41CA3D280E567882F95CF664A94147D78F42CFC714A40D22EF19470E053493508A2 -In = E26193989D06568FE688E75540AEA06747D9F851 -Out = 46e0bf724ebf8a3c66185ba56d6332980aaa6a918d2b6e17da603e647c1dcd886a2b0569cec63c463f53faf2f3aa8d66eab805f3c70c5b6b047a148fee6a1fdf +In = 00010203040506070809 +Out = 29102511D749DB3CC9B4E335FA1F5E8FACA8421D558F6A3F3321D50D044A248BA595CFC3EFD3D2ADC97334DA732413F5CBF4751C362BA1D53862AC1E8DABEEE8 -In = D8DC8FDEFBDCE9D44E4CBAFE78447BAE3B5436102A -Out = 12c71c14214cd0dc9a3627a0d8608c9ed0c54cd95217c08393ed9f0c6b2a5dc68f0dc104832ebea798d1f6c66566235809c6a47123219eb4046f489da9efc7bf +In = 000102030405060708090A +Out = C97A4779D47E6F77729B5917D0138ABB35980AB641BD73A8859EB1AC98C05362ED7D608F2E9587D6BA9E271D343125D40D933A8ED04EC1FE75EC407C7A53C34E -In = 57085FD7E14216AB102D8317B0CB338A786D5FC32D8F -Out = 4b019a0cbf45bfc5b8a3bbc52987b0c13bcc338e22f77d46711abed86abc7038f99309852124da87e98691a373fea22538c5a701463809c6d48c3e27a2acc5a9 +In = 000102030405060708090A0B +Out = 10F0DC91B9F845FB95FAD6860E6CE1ADFA002C7FC327116D44D047CD7D5870D772BB12B5FAC00E02B08AC2A0174D0446C36AB35F14CA31894CD61C78C849B48A -In = A05404DF5DBB57697E2C16FA29DEFAC8AB3560D6126FA0 -Out = b36bd83040e1fb360a18593ede85abf858413fb8bad1f6e1f12f5b0f17cc4b484815b578f2ad25b095365e5fcc8145f714f496d3ba6235c28e9c2784b2b8ad8a +In = 000102030405060708090A0B0C +Out = DEA9101CAC62B8F6A3C650F90EEA5BFAE2653A4EAFD63A6D1F0F132DB9E4F2B1B662432EC85B17BCAC41E775637881F6AAB38DD66DCBD080F0990A7A6E9854FE -In = AECBB02759F7433D6FCB06963C74061CD83B5B3FFA6F13C6 -Out = f256b1ff2a679500d8480139f2a1033894d2f1939f18b5636083ef0b9f1faac8bec9665b15999f9f5f3284dc911df86af82c5fb230b9800fea17aecdd80b4254 +In = 000102030405060708090A0B0C0D +Out = 441FFAA08CD79DFF4AFC9B9E5B5620EEC086730C25F661B1D6FBFBD1CEC3148DD72258C65641F2FCA5EB155FADBCABB13C6E21DC11FAF72C2A281B7D56145F19 -In = AAFDC9243D3D4A096558A360CC27C8D862F0BE73DB5E88AA55 -Out = 44e92b277adb2bc74095d96663f2e42989772ef2d33e3772676875ec0db4121662c523e9ee6a490be142f45de5914a5aab9a550e8e530731adb982ad9969d493 +In = 000102030405060708090A0B0C0D0E +Out = 444B240FE3ED86D0E2EF4CE7D851EDDE22155582AA0914797B726CD058B6F45932E0E129516876527B1DD88FC66D7119F4AB3BED93A61A0E2D2D2AEAC336D958 -In = 7BC84867F6F9E9FDC3E1046CAE3A52C77ED485860EE260E30B15 -Out = eb494064d42ea61bdef59d83fa38adaadbd45f7eb3bf330d7041dd539aeb20940a708fd14f25d95cac1856d4d532a58fd385622ebf4c47604a8e7218d6b4d353 +In = 000102030405060708090A0B0C0D0E0F +Out = BFBABBEF45554CCFA0DC83752A19CC35D5920956B301D558D772282BC867009168E9E98606BB5BA73A385DE5749228C925A85019B71F72FE29B3CD37CA52EFE6 -In = FAC523575A99EC48279A7A459E98FF901918A475034327EFB55843 -Out = 7cd4dadfea22d24e40239724a03533ec5fadea1a41b70f8e70ff404b9295ac9e3d9c18942476201c82e0d381a44a658d14918718a4bf4a0102fbce49449c470d +In = 000102030405060708090A0B0C0D0E0F10 +Out = 9C4D0C3E1CDBBF485BEC86F41CEC7C98373F0E09F392849AAA229EBFBF397B22085529CB7EF39F9C7C2222A514182B1EFFAA178CC3687B1B2B6CBCB6FDEB96F8 -In = 0F8B2D8FCFD9D68CFFC17CCFB117709B53D26462A3F346FB7C79B85E -Out = 71e63cb25f35b9cbf0f0e1f48c60afacbc4bb7a95f61515c928e0ddd074b00210c693f03aebafbe0ed83caadb378753a2c5bbba9d78f49b319c7f07fb2ef70ea +In = 000102030405060708090A0B0C0D0E0F1011 +Out = 477176B3BFCBADD7657C23C24625E4D0D674D1868F006006398AF97AA41877C8E70D3D14C3BBC9BBCDCEA801BD0E1599AF1F3EEC67405170F4E26C964A57A8B7 -In = A963C3E895FF5A0BE4824400518D81412F875FA50521E26E85EAC90C04 -Out = 6db2a68c97fc451b016a5c3f2b360a8c830f6072d010043b2d22ab6d05c4640e6ec80b8e1395d3951b4374098049634a74470d568ebcdd5ebf597f9d9de94550 +In = 000102030405060708090A0B0C0D0E0F101112 +Out = A78C490EDA3173BB3F10DEE52F110FB1C08E0302230B85DDD7C11257D92DE148785EF00C039C0BB8EB9808A35B2D8C080F572859714C9D4069C5BCAF090E898E -In = 03A18688B10CC0EDF83ADF0A84808A9718383C4070C6C4F295098699AC2C -Out = f9f9f80e7e810a99d55fad81ec6fcaa36efcbf247b6c04a09f9817c204ab3e46668c35a32b476f2660dce84632a5f6c52445b2b343244b10949a2db9446af43c +In = 000102030405060708090A0B0C0D0E0F10111213 +Out = 58D023397BEB5B4145CB2255B07D74290B36D9FD1E594AFBD8EEA47C205B2EFBFE6F46190FAF95AF504AB072E36F6C85D767A321BFD7F22687A4ABBF494A689C -In = 84FB51B517DF6C5ACCB5D022F8F28DA09B10232D42320FFC32DBECC3835B29 -Out = 25385e59f2b4405d9d18186db1e308362e1efc5e642df385d8646b4e1459f18b3f22546e5b18e91a8a90d82e19424c95babd1039a78b1050ddb7f70b81ec4e77 +In = 000102030405060708090A0B0C0D0E0F1011121314 +Out = 4001EC74D5A46FD29C2C3CDBE5D1B9F20E51A941BE98D2A4E1E2FBF866A672121DB6F81A514CFD10E7358D571BDBA48E4CE708B9D124894BC0B5ED554935F73A -In = 9F2FCC7C90DE090D6B87CD7E9718C1EA6CB21118FC2D5DE9F97E5DB6AC1E9C10 -Out = abc8df2f99b3e802dce1db5666611149a9bff2202bc242e91985bfd9a69f7a71700e06c8eb7e35148c027926ed73f5c18cbd658381d34b6415acb109e51ca6ca +In = 000102030405060708090A0B0C0D0E0F101112131415 +Out = CCD1B22DAB6511225D2401EA2D8625D206A12473CC732B615E5640CEFFF0A4ADF971B0E827A619E0A80F5DB9CCD0962329010D07E34A2064E731C520817B2183 -In = DE8F1B3FAA4B7040ED4563C3B8E598253178E87E4D0DF75E4FF2F2DEDD5A0BE046 -Out = 0b1a4ea9058954afff67ecd4dc2d6338a09887ed1c499d7e33da3fa56ddd53cfaa773cae565048786ff49175636e5a06f7a45e5ef28e287ddc58104d9741dbbc +In = 000102030405060708090A0B0C0D0E0F10111213141516 +Out = B4A0A9E3574EDB9E1E72AA31E39CC5F30DBF943F8CABC408449654A39131E66D718A18819143E3EA96B4A1895988A1C0056CF2B6E04F9AC19D657383C2910C44 -In = 62F154EC394D0BC757D045C798C8B87A00E0655D0481A7D2D9FB58D93AEDC676B5A0 -Out = 9f823b85c7ee25f609909c6c37fad9efe886b6f50bb0404c6f99980ed621a3b5aeb0f9f128e0cb2da9dde4af3b0e8eebe349f236fbd124cb4b64ad5b6105b4c4 +In = 000102030405060708090A0B0C0D0E0F1011121314151617 +Out = 447BECAB16630608D39F4F058B16F7AF95B85A76AA0FA7CEA2B80755FB76E9C804F2CA78F02643C915FBF2FCE5E19DE86000DE03B18861815A83126071F8A37B -In = B2DCFE9FF19E2B23CE7DA2A4207D3E5EC7C6112A8A22AEC9675A886378E14E5BFBAD4E -Out = b1897ebc572d0b25f3f856910861e9f4bb84965ef0fc154473565b38829290772eb72c1bb92723eb46c03d9110728850ba8ddf17875be85376b8d04372a131e4 +In = 000102030405060708090A0B0C0D0E0F101112131415161718 +Out = 54E6DAB9977380A5665822DB93374EDA528D9BEB626F9B94027071CB26675E112B4A7FEC941EE60A81E4D2EA3FF7BC52CFC45DFBFE735A1C646B2CF6D6A49B62 -In = 47F5697AC8C31409C0868827347A613A3562041C633CF1F1F86865A576E02835ED2C2492 -Out = 1b8de009cfd655e62c9b0fdcc4c48e6b6609a5980db7d993af881a2a347486cb9ab2d9102dab7837a0af26269593a52a0a71b1aaf4a5989682d36b5d6a925b9c +In = 000102030405060708090A0B0C0D0E0F10111213141516171819 +Out = 3EA62625949E3646704D7E3C906F82F6C028F540F5F72A794B0C57BF97B7649BFEB90B01D3CA3E829DE21B3826E6F87014D3C77350CB5A15FF5D468A81BEC160 -In = 512A6D292E67ECB2FE486BFE92660953A75484FF4C4F2ECA2B0AF0EDCDD4339C6B2EE4E542 -Out = 43a94cd295acfff0d8b151e600266f89cb2b9603c2e801d9e53d96031fa0764be76644c9a4ebddc0de872be6c97486e541edba1ef9d7a7a188dd021f5450ccf9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A +Out = 213CFE145C54A33691569980E5938C8883A46D84D149C8FF1A67CD287B4D49C6DA69D3A035443DB085983D0EFE63706BD5B6F15A7DA459E8D50A19093DB55E80 -In = 973CF2B4DCF0BFA872B41194CB05BB4E16760A1840D8343301802576197EC19E2A1493D8F4FB -Out = 803ef2cff9f64dfdba23360e6388097894c078bcd7d40362b5710b2c36272e4966c2dab6f131caf666a471d7d2c43b0af4fb6f946dccc1a6e640f05a215b419a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B +Out = 5716C4A38F38DB104E494A0A27CBE89A26A6BB6F499EC01C8C01AA7CB88497E75148CD6EEE12A7168B6F78AB74E4BE749251A1A74C38C86D6129177E2889E0B6 -In = 80BEEBCD2E3F8A9451D4499961C9731AE667CDC24EA020CE3B9AA4BBC0A7F79E30A934467DA4B0 -Out = 8996e2a459ca1b19de295a303b3d6e91f465b7da5932d075ffe62aedbdd7453c3379599257980305c209d225b1549bcd48080a064030f38e98a8c7c2ac4ce358 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C +Out = 030460A98BDF9FF17CD96404F28FC304F2B7C04EAADE53677FD28F788CA22186B8BC80DD21D17F8549C711AFF0E514E19D4E15F5990252A03E082F28DC2052F6 -In = 7ABAA12EC2A7347674E444140AE0FB659D08E1C66DECD8D6EAE925FA451D65F3C0308E29446B8ED3 -Out = c4f3e195746d272cb103805b4d1dee6925ae2dfc2a41757b342bf2eb9260644420cd2d75297c978ebbede14f8a9c5caba54a26c3831c1c284860737f65d3a96b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D +Out = 19E7F1CCEE88A10672333E390CF22013A8C734C6CB9EAB41F17C3C8032A2E4ACA0569EA36F0860C7A1AF28FA476840D66011168859334A9E4EF9CC2E61A0E29E -In = C88DEE9927679B8AF422ABCBACF283B904FF31E1CAC58C7819809F65D5807D46723B20F67BA610C2B7 -Out = 8b0f9eb869510611ae3d9ea2cb40fe5afcd764fadb1e08907f5b1665da3f2696545107e8248f1dafa37878f9080d2b1d0287bcf5b8584ba28cdf8b91a7a64e59 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E +Out = 29F8B8C78C80F2FCB4BDF7825ED90A70D625FF785D262677E250C04F3720C888D03F8045E4EDF3F5285BD39D928A10A7D0A5DF00B8484AC2868142A1E8BEA351 -In = 01E43FE350FCEC450EC9B102053E6B5D56E09896E0DDD9074FE138E6038210270C834CE6EADC2BB86BF6 -Out = cc8ec6e40d5f229b6741e76cef7285137e514a690342dc3aa4f4986bd50b042f6600db6cacab6da0c534db2527c32e17a526c72b4dc0f87626082b53bccf82e8 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F +Out = 5C52920A7263E39D57920CA0CB752AC6D79A04FEF8A7A216A1ECB7115CE06D89FD7D735BD6F4272555DBA22C2D1C96E6352322C62C5630FDE0F4777A76C3DE2C -In = 337023370A48B62EE43546F17C4EF2BF8D7ECD1D49F90BAB604B839C2E6E5BD21540D29BA27AB8E309A4B7 -Out = ac163b27ab4d2991e83642d049a28a16cf3a6b4d6d7bfd731b61e76a9d86d7bbe309828d28d17967bdc9d1719e071d12ad7d52e78da870fa611b0ff7aafbc70f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20 +Out = 83B098F262251BF660064A9D3511CE7687A09E6DFBB878299C30E93DFB43A9314DB9A600337DB26EBEEDAF2256A96DABE9B29E7573AD11C3523D874DDE5BE7ED -In = 6892540F964C8C74BD2DB02C0AD884510CB38AFD4438AF31FC912756F3EFEC6B32B58EBC38FC2A6B913596A8 -Out = a441f774b4673fbee0437229a6f40954d8435a8e579982eb1db09e7371223a8f869fa7a399617a3c53635031ba979c73450035701a1dc6f2f2f16a0e496a9ca3 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021 +Out = 9447D98AA5C9331352F43D3E56D0A9A9F9581865998E2885CC56DD0A0BD5A7B50595BD10F7529BCD31F37DC16A1465D594079667DA2A3FCB70401498837CEDEB -In = F5961DFD2B1FFFFDA4FFBF30560C165BFEDAB8CE0BE525845DEB8DC61004B7DB38467205F5DCFB34A2ACFE96C0 -Out = 063684f451ea6904870bf0fe2d1fedcb28769a1171e14d4096d6cefbc5f4197138c823739660977e06b15eb1a286040576a4fbdc9c273ee82b9919a3eb5e8681 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122 +Out = 867732F2FEEB23893097561AC710A4BFF453BE9CFBEDBA8BA324F9D312A82D732E1B83B829FDCD177B882CA0C1BF544B223BE529924A246A63CF059BFDC50A1B -In = CA061A2EB6CEED8881CE2057172D869D73A1951E63D57261384B80CEB5451E77B06CF0F5A0EA15CA907EE1C27EBA -Out = 2f765bc67dc543d6d42bb9393c637898e4e5c41f4d3cc8c359665c5884409d224d3c116aa047dcfe662f24b5ba927d4843d31d1214c06e28ee870c259b8e8fe9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223 +Out = F15AB26D4CDFCF56E196BB6BA170A8FCCC414DE9285AFD98A3D3CF2FB88FCBC0F19832AC433A5B2CC2392A4CE34332987D8D2C2BEF6C3466138DB0C6E42FA47B -In = 1743A77251D69242750C4F1140532CD3C33F9B5CCDF7514E8584D4A5F9FBD730BCF84D0D4726364B9BF95AB251D9BB -Out = 70db18b9c53d55091eb5f2526231e5d6605d0c963c2bd442ca004ffe73cef29faf5fbfdd63ce492c811462f1f9c5d152c1d0e1aef7c93461db5aaf754f573aca +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324 +Out = 2813516D68ED4A08B39D648AA6AACD81E9D655ECD5F0C13556C60FDF0D333EA38464B36C02BACCD746E9575E96C63014F074AE34A0A25B320F0FBEDD6ACF7665 -In = D8FABA1F5194C4DB5F176FABFFF856924EF627A37CD08CF55608BBA8F1E324D7C7F157298EABC4DCE7D89CE5162499F9 -Out = 3d9d5b2c5ca7ace5ca54e19f8c65174e0b5221d1ae533cdf2c513bfc3fe47a952f3ae3103b49121606ca8866de0c52b0639b86f10d94a225292fbc9c72f40765 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425 +Out = D3259AFCA8A48962FA892E145ACF547F26923AE8D4924C8A531581526B04B44C7AF83C643EF5A0BC282D36F3FB04C84E28B351F40C74B69DC7840BC717B6F15F -In = BE9684BE70340860373C9C482BA517E899FC81BAAA12E5C6D7727975D1D41BA8BEF788CDB5CF4606C9C1C7F61AED59F97D -Out = b0979dd7f8766b9d1c71c73c0cd08656c3c0c03307e70d132bcc05ed0119238a6d62bace7dd9b4f3bab66e8f3bb8ea507d02b1b81ef56cadbcf191e80f4799dd +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223242526 +Out = F14B061AE359FA31B989E30332BFE8DE8CC8CDB568E14BE214A2223B84CAAB7419549ECFCC96CE2ACEC119485D87D157D3A8734FC426597D64F36570CEAF224D -In = 7E15D2B9EA74CA60F66C8DFAB377D9198B7B16DEB6A1BA0EA3C7EE2042F89D3786E779CF053C77785AA9E692F821F14A7F51 -Out = 20af18db3aceca3146f321a55b3bcd59cb84023cc7d4f719fd01af6e4d083209d971b5ba85a4ad2fbebb0f8ea3067db7b238d6eb0ef1e6e232147f7237281ec4 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627 +Out = 55E70B01D1FBF8B23B57FB62E26C2CE54F13F8FA2464E6EB98D16A6117026D8B90819012496D4071EBE2E59557ECE3519A7AA45802F9615374877332B73490B3 -In = 9A219BE43713BD578015E9FDA66C0F2D83CAC563B776AB9F38F3E4F7EF229CB443304FBA401EFB2BDBD7ECE939102298651C86 -Out = 53aebac7d4237a8d65afe54097ec6111cb1a661d6b64105c72a7348243cfe84a7c667e77b5ddcd855ee6a1ba2e1db0f32e5936fcf523fb848d4d0d43f89fce4f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728 +Out = 25261EB296971D6E4A71B2928E64839C67D422872BF9F3C31993615222DE9F8F0B2C4BE8548559B4B354E736416E3218D4E8A1E219A4A6D43E1A9A521D0E75FC -In = C8F2B693BD0D75EF99CAEBDC22ADF4088A95A3542F637203E283BBC3268780E787D68D28CC3897452F6A22AA8573CCEBF245972A -Out = 5c52703fade0c7733366151fd7256fbcb11e5148c7dd8f64d6be5c7c44a6e7aa68f23c93c2f71b79b2fdd363a4eeb8b06a8c9facf4ff5e9d17b205096c8de89a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223242526272829 +Out = 08307F347C41294E34BB54CB42B1522D22F824F7B6E5DB50FDA096798E181A8F026FA27B4AE45D52A62CAF9D5198E24A4913C6671775B2D723C1239BFBF016D7 -In = EC0F99711016C6A2A07AD80D16427506CE6F441059FD269442BAAA28C6CA037B22EEAC49D5D894C0BF66219F2C08E9D0E8AB21DE52 -Out = 91120cbf5cab81f8433dec6475d4658f50760f78514c87cfd13bd0688f0ca719429419352d0794cfdb785e6cb51fad7d1d8f74dcf3db661a497db2bc679b3e04 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A +Out = 1E5C62E7E9BFA1B118747A2DE08B3CA10112AF96A46E4B22C3FC06F9BFEE4EB5C49E057A4A4886234324572576BB9B5ECFDE0D99B0DE4F98EC16E4D1B85FA947 -In = 0DC45181337CA32A8222FE7A3BF42FC9F89744259CFF653504D6051FE84B1A7FFD20CB47D4696CE212A686BB9BE9A8AB1C697B6D6A33 -Out = e83247665b1416f6a6fb108604d50640f0fc1a54c3d1bee400bed043acde117d53ebf234d0abec671667a27a4aa01a2d18ddae907f27b41829b99bcdcfcd7af9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B +Out = C74A77395FB8BC126447454838E561E962853DC7EB49A1E3CB67C3D0851F3E39517BE8C350AC910903D49CD2BFDF545C99316D0346170B739F0ADD5D533C2CFC -In = DE286BA4206E8B005714F80FB1CDFAEBDE91D29F84603E4A3EBC04686F99A46C9E880B96C574825582E8812A26E5A857FFC6579F63742F -Out = 4f26127dfc5dbf72a721683c911dfe160a2ec85d5618153247aa233dab551f0987f87acc4039a914caf8bfa2fc852f695d146d1bdf3cbdc4de8db84e5fcdeba7 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C +Out = 0DD57B423CC01EB2861391EB886A0D17079B933FC76EB3FC08A19F8A74952CB68F6BCDC644F77370966E4D13E80560BCF082EF0479D48FBBAB4DF03B53A4E178 -In = EEBCC18057252CBF3F9C070F1A73213356D5D4BC19AC2A411EC8CDEEE7A571E2E20EAF61FD0C33A0FFEB297DDB77A97F0A415347DB66BCAF -Out = 56633a7b792142826cabd249ec31a12d5b0dc8f92cbbb510c0cf498bb8454f29b129efa1216a69a82ae5e95d6573c9311548f474722ac097a07dbee1dac925b7 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D +Out = 4D8DC3923EDCCDFCE70072398B8A3DA5C31FCB3EE3B645C85F717CBAEB4B673A19394425A585BFB464D92F1597D0B754D163F97CED343B25DB5A70EF48EBB34F -In = 416B5CDC9FE951BD361BD7ABFC120A5054758EBA88FDD68FD84E39D3B09AC25497D36B43CBE7B85A6A3CEBDA8DB4E5549C3EE51BB6FCB6AC1E -Out = 1329bc4493a75f74762c061e303fc3180ba517d92f526480761cf2387ebfec2acd80c1a9b6711bb209479752610e99d7cfad2c5370e4c9565d8312441733a7ad +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E +Out = F0A50553E4DFB0C4E3E3D3BA82034857E3B1E50918F5B8A7D698E10D242B0FB544AF6C92D0C3AAF9932220416117B4E78ECB8A8F430E13B82A5915290A5819C5 -In = 5C5FAF66F32E0F8311C32E8DA8284A4ED60891A5A7E50FB2956B3CBAA79FC66CA376460E100415401FC2B8518C64502F187EA14BFC9503759705 -Out = 9348024df82f48fa41b8e0d0486603f3187751710a2a5270c66bef8b175b3af7ef5e6b1b0b750365cb5061bb458023995c8de4523657743f4de22e2db8a371c3 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F +Out = B15543F3F736086627CC5365E7E8988C2EF155C0FD4F428961B00D1526F04D6D6A658B4B8ED32C5D8621E7F4F8E8A933D9ECC9DD1B8333CBE28CFC37D9719E1C -In = 7167E1E02BE1A7CA69D788666F823AE4EEF39271F3C26A5CF7CEE05BCA83161066DC2E217B330DF821103799DF6D74810EED363ADC4AB99F36046A -Out = 2844fa546040cd5bb3287cb4a2656580a651f6ac4e6d98544c2073d16a3c5961af39c57f9af42d0941aa083dc1ae9a7711ff196c69a513545727e68b85a71098 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30 +Out = 7B4FA158E415FEF023247264CBBE15D16D91A44424A8DB707EB1E2033C30E9E1E7C8C0864595D2CB8C580EB47E9D16ABBD7E44E824F7CEDB7DEF57130E52CFE9 -In = 2FDA311DBBA27321C5329510FAE6948F03210B76D43E7448D1689A063877B6D14C4F6D0EAA96C150051371F7DD8A4119F7DA5C483CC3E6723C01FB7D -Out = 4e0cf0975e3f37b8880d262b771ca9b3dec567a1da3b0d069508977aea904a5f414ff1ac45d13f68768b041a474754fe48d06139e55a16af781c80eafb96fa9a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F3031 +Out = 60424FF23234C34DC9687AD502869372CC31A59380186BC2361C835D972F49666EB1AC69629DE646F03F9B4DB9E2ACE093FBFDF8F20AB5F98541978BE8EF549F -In = 95D1474A5AAB5D2422ACA6E481187833A6212BD2D0F91451A67DD786DFC91DFED51B35F47E1DEB8A8AB4B9CB67B70179CC26F553AE7B569969CE151B8D -Out = 810e53733df59eb89a73a5e40deb666aa286023a259a4bdf072022ab14ce3fb4238fa84328276bb29dc2fec4270318f55cd633ab1c10a593bed5973e28061ebb +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132 +Out = 7406018CE704D84F5EB9C79FEA97DA345699468A350EE0B2D0F3A4BF2070304EA862D72A51C57D3064947286F531E0EAF7563702262E6C724ABF5ED8C8398D17 -In = C71BD7941F41DF044A2927A8FF55B4B467C33D089F0988AA253D294ADDBDB32530C0D4208B10D9959823F0C0F0734684006DF79F7099870F6BF53211A88D -Out = 467be3e23d528464dbf9c9f8e205fd2dc6daa546ae7d4813f703d01ee7445ae90833850e9f11329b604229aedb6c83d640dc5d6f4d9b102c0dff1059393e19d1 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233 +Out = 14EF5C6D647B3BD1E6E32006C231199810DE5C4DC88E70240273B0EA18E651A3EB4F5CA3114B8A56716969C7CDA27E0C8DB832AD5E89A2DC6CB0ADBE7D93ABD1 -In = F57C64006D9EA761892E145C99DF1B24640883DA79D9ED5262859DCDA8C3C32E05B03D984F1AB4A230242AB6B78D368DC5AAA1E6D3498D53371E84B0C1D4BA -Out = 2c45fdcedb2bba9b13648a018e87c983ae780f90c7763a602eed1929802b0aaab7ce11c672aac2246d34ad1695d2250dc52a02dd4e8975ede59f900627ccdf56 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F3031323334 +Out = 38CF6C24E3E08BCF1F6CF3D1B1F65B905239A3118033249E448113EC632EA6DC346FEEB2571C38BD9A7398B2221280328002B23E1A45ADAFFE66D93F6564EAA2 -In = E926AE8B0AF6E53176DBFFCC2A6B88C6BD765F939D3D178A9BDE9EF3AA131C61E31C1E42CDFAF4B4DCDE579A37E150EFBEF5555B4C1CB40439D835A724E2FAE7 -Out = 8560f21fece0a9598b99c34596fc8796f4f0d323a77a6dcc6d72cc1a02e8b96d9d45472cec1f237a435aaf790c75b722df9a8a03522435226531ef9d0af79b86 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435 +Out = 6CD7208A4BC7E7E56201BBBA02A0F489CD384ABE40AFD4222F158B3D986EE72A54C50FB64FD4ED2530EDA2C8AF2928A0DA6D4F830AE1C9DB469DFD970F12A56F -In = 16E8B3D8F988E9BB04DE9C96F2627811C973CE4A5296B4772CA3EEFEB80A652BDF21F50DF79F32DB23F9F73D393B2D57D9A0297F7A2F2E79CFDA39FA393DF1AC00 -Out = fe36d2d0d90278280b233c219d4cf34dc8db0d6db4c25e63874d9779e93efa7254175cf02291473977b3ebb96ab11999f70659a6570483fd5f6e129a5216314a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233343536 +Out = 659858F0B5C9EDAB5B94FD732F6E6B17C51CC096104F09BEB3AFC3AA467C2ECF885C4C6541EFFA9023D3B5738AE5A14D867E15DB06FE1F9D1127B77E1AABB516 -In = FC424EEB27C18A11C01F39C555D8B78A805B88DBA1DC2A42ED5E2C0EC737FF68B2456D80EB85E11714FA3F8EABFB906D3C17964CB4F5E76B29C1765DB03D91BE37FC -Out = 316782797c22c3f22ff3287032ed438e2b3486b6aaf0479476aebf1395cb6ec0e5f31cdd2f5c5c0ff3ac939beb7259f4073a8de3014ac24656fce94ed81d51dc +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F3031323334353637 +Out = 26CCA0126F5D1A813C62E5C71001C046F9C92095704550BE5873A495A999AD010A4F79491F24F286500ADCE1A137BC2084E4949F5B7294CEFE51ECAFF8E95CBA -In = ABE3472B54E72734BDBA7D9158736464251C4F21B33FBBC92D7FAC9A35C4E3322FF01D2380CBAA4EF8FB07D21A2128B7B9F5B6D9F34E13F39C7FFC2E72E47888599BA5 -Out = 08d059e04307d56fa4ef8ae385ad586c461d90a554f862c160426f6217e25a291ae74c73e597e70644ac7d31e4d1e79cb76980ac51120a59c50c577966a948e8 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738 +Out = 4147C1F55172788C5567C561FEEF876F621FFF1CE87786B8467637E70DFBCD0DBDB6415CB600954AB9C04C0E457E625B407222C0FE1AE21B2143688ADA94DC58 -In = 36F9F0A65F2CA498D739B944D6EFF3DA5EBBA57E7D9C41598A2B0E4380F3CF4B479EC2348D015FFE6256273511154AFCF3B4B4BF09D6C4744FDD0F62D75079D440706B05 -Out = 97cf6ebca0b710f07cddbc253e3ab73037c49387898b1d572b0cdf530d15f2e170bbc2fbbc7983f8d177edb6037365b6dd6731e2f3746eacc0bd1512d09b9f0e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233343536373839 +Out = 5B1BF154C62A8AF6E93D35F18F7F90ABB16A6EF0E8D1AECD118BF70167BAB2AF08935C6FDC0663CE74482D17A8E54B546D1C296631C65F3B522A515839D43D71 -In = ABC87763CAE1CA98BD8C5B82CABA54AC83286F87E9610128AE4DE68AC95DF5E329C360717BD349F26B872528492CA7C94C2C1E1EF56B74DBB65C2AC351981FDB31D06C77A4 -Out = 8ab442ed7d06f81866c2eb871108080ec4aa19369dfacd161c1bc48b6c93872ac0014a1eac1d512efbe40ac40dfb72484d5f64b62f40d4ca6015f380e4f9fd21 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A +Out = 9F600419A4E8F4FB834C24B0F7FC13BF4E279D98E8A3C765EE934917403E3A66097182EA21453CB63EBBE8B73A9C2167596446438C57627F330BADD4F569F7D6 -In = 94F7CA8E1A54234C6D53CC734BB3D3150C8BA8C5F880EAB8D25FED13793A9701EBE320509286FD8E422E931D99C98DA4DF7E70AE447BAB8CFFD92382D8A77760A259FC4FBD72 -Out = dcb9d75366d5d12e33312c07394592d4adfd7502cb5019e01e0d2fc0f3ced25975e6060f3df8ba6b60b08a05e1f7c858ef618ca84dfbad65b638e54dfff6f487 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B +Out = 457EF6466A8924FD8011A34471A5A1AC8CCD9BD0D07A97414AC943021CE4B9E4B9C8DB0A28F016ED43B1542481990022147B313E194671131E708DD43A3ED7DC -In = 13BD2811F6ED2B6F04FF3895ACEED7BEF8DCD45EB121791BC194A0F806206BFFC3B9281C2B308B1A729CE008119DD3066E9378ACDCC50A98A82E20738800B6CDDBE5FE9694AD6D -Out = d96d344a97aad94f719358d2205a404bc1519687610e15702ce383fc7c2985bf44579c606129a41eb18aeca840c3f1b6c5745cfdcbe14a5f021361e6ebf0b821 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C +Out = 9997B2194D9AF6DFCB9143F41C0ED83D3A3F4388361103D38C2A49B280A581212715FD908D41C651F5C715CA38C0CE2830A37E00E508CED1BCDC320E5E4D1E2E -In = 1EED9CBA179A009EC2EC5508773DD305477CA117E6D569E66B5F64C6BC64801CE25A8424CE4A26D575B8A6FB10EAD3FD1992EDDDEEC2EBE7150DC98F63ADC3237EF57B91397AA8A7 -Out = 5129926418f12f3d18023e1db38ce20862f89ba303076f794ea34f3f398e60b4ff6fea7e975b8d78589314da1de3b546fe9dfbca0e0bfa93ec2e0cdf840ee7b9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D +Out = 5C6BBF16BAA180F986BD40A1287ED4C549770E7284858FC47BC21AB95EBBF3374B4EE3FD9F2AF60F3395221B2ACC76F2D34C132954049F8A3A996F1E32EC84E5 -In = BA5B67B5EC3A3FFAE2C19DD8176A2EF75C0CD903725D45C9CB7009A900C0B0CA7A2967A95AE68269A6DBF8466C7B6844A1D608AC661F7EFF00538E323DB5F2C644B78B2D48DE1A08AA -Out = a0400331e4cb8630699131344eaa0a3354e4f862c10c9d75655a071c90b959f5e20055058e3e06d42c6d51ec205156a33b8d6f5b4a6d1716141685632abe5665 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E +Out = D10BF9A15B1C9FC8D41F89BB140BF0BE08D2F3666176D13BAAC4D381358AD074C9D4748C300520EB026DAEAEA7C5B158892FDE4E8EC17DC998DCD507DF26EB63 -In = 0EFA26AC5673167DCACAB860932ED612F65FF49B80FA9AE65465E5542CB62075DF1C5AE54FBA4DB807BE25B070033EFA223BDD5B1D3C94C6E1909C02B620D4B1B3A6C9FED24D70749604 -Out = f589d702b13a22c12f32ea9b9b44224fa6d85d42fbcfaca0464b057d7877b29d5a76de8a20087db6feb1a1e3e8d0d17eb97adc2e824a2409a21c5cdfca67f076 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F +Out = 2FC6E69FA26A89A5ED269092CB9B2A449A4409A7A44011EECAD13D7C4B0456602D402FA5844F1A7A758136CE3D5D8D0E8B86921FFFF4F692DD95BDC8E5FF0052 -In = BBFD933D1FD7BF594AC7F435277DC17D8D5A5B8E4D13D96D2F64E771ABBD51A5A8AEA741BECCBDDB177BCEA05243EBD003CFDEAE877CCA4DA94605B67691919D8B033F77D384CA01593C1B -Out = 92d12ba06c6e4202dcf43ddffa89a8b73f7126e6e6107eb52b7e47f3d35ba782184bff47730619ee44734d8d595a6000e7df4d671cc8c325845ec22722d85808 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40 +Out = FCBE8BE7DCB49A32DBDF239459E26308B84DFF1EA480DF8D104EEFF34B46FAE98627B450C2267D48C0946A697C5B59531452AC0484F1C84E3A33D0C339BB2E28 -In = 90078999FD3C35B8AFBF4066CBDE335891365F0FC75C1286CDD88FA51FAB94F9B8DEF7C9AC582A5DBCD95817AFB7D1B48F63704E19C2BAA4DF347F48D4A6D603013C23F1E9611D595EBAC37C -Out = 94d5200a002a73944c63589bf5a1f86172a22874720fd022c54b4a92f85122b1fb113ce005e24131cc3b532f553cda600540185a43ba195a8f992d317ab2cbfe +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F4041 +Out = A19093A6E3BCF5952F850F2030F69B9606F147F90B8BAEE3362DA71D9F35B44EF9D8F0A7712BA1877FDDCD2D8EA8F1E5A773D0B745D4725605983A2DE901F803 -In = 64105ECA863515C20E7CFBAA0A0B8809046164F374D691CDBD6508AAABC1819F9AC84B52BAFC1B0FE7CDDBC554B608C01C8904C669D8DB316A0953A4C68ECE324EC5A49FFDB59A1BD6A292AA0E -Out = 0b4b60615124647a2cb1fda6482bff150a47feabbe014297d542b3520b9cc382b1c8bf9b1aab16f4742d30241130ebda9c1a1b9ca33098f80773b8112d894cc2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142 +Out = 3C2006423F73E268FA59D2920377EB29A4F9A8B462BE15983EE3B85AE8A78E992633581A9099893B63DB30241C34F643027DC878279AF5850D7E2D4A2653073A -In = D4654BE288B9F3B711C2D02015978A8CC57471D5680A092AA534F7372C71CEAAB725A383C4FCF4D8DEAA57FCA3CE056F312961ECCF9B86F14981BA5BED6AB5B4498E1F6C82C6CAE6FC14845B3C8A -Out = e9f7f7f0f37135413abc285c1c628cb6621a383fe3b5a10649907476b6a4561aa92903a37a86975d089b0874e9221486f96a1e55f86094c6e1100875568e8b17 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40414243 +Out = D0F2F2E3787653F77CCE2FA24835785BBD0C433FC779465A115149905A9DD1CB827A628506D457FCF124A0C2AEF9CE2D2A0A0F63545570D8667FF9E2EBA07334 -In = 12D9394888305AC96E65F2BF0E1B18C29C90FE9D714DD59F651F52B88B3008C588435548066EA2FC4C101118C91F32556224A540DE6EFDDBCA296EF1FB00341F5B01FECFC146BDB251B3BDAD556CD2 -Out = 0a238f7e5556d921004bb4941cdd0dd6febeb98120d258186be7b3c21345c62b1c7c2947bb182e03446121deee3c4b5451eab932a38bbe0da4da73ebe3bb0e5b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F4041424344 +Out = 78A9FC048E25C6DCB5DE45667DE8FFDD3A93711141D594E9FA62A959475DA6075EA8F0916E84E45AD911B75467077EE52D2C9AEBF4D58F20CE4A3A00458B05D4 -In = 871A0D7A5F36C3DA1DFCE57ACD8AB8487C274FAD336BC137EBD6FF4658B547C1DCFAB65F037AA58F35EF16AFF4ABE77BA61F65826F7BE681B5B6D5A1EA8085E2AE9CD5CF0991878A311B549A6D6AF230 -Out = 1e37024564b51dc3ea04adaf5b883674549450729cd6f631b3c809e38d7450e9f939f12cb923221d1d918eb9f4d954a4666eb909c583ba5714cad51f27a0de3f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445 +Out = 45813F441769AB6ED37D349FF6E72267D76AE6BB3E3C612EC05C6E02A12AF5A37C918B52BF74267C3F6A3F183A8064FF84C07B193D08066789A01ACCDB6F9340 -In = E90B4FFEF4D457BC7711FF4AA72231CA25AF6B2E206F8BF859D8758B89A7CD36105DB2538D06DA83BAD5F663BA11A5F6F61F236FD5F8D53C5E89F183A3CEC615B50C7C681E773D109FF7491B5CC22296C5 -Out = 7e8fa1902cb1b317b6195bef840fdd4ed6776ab15dc4cbbd4b17d3cc53f10ecab8734c35ec98da2227d4dfe31ebb368af7b055df88ed2f36cb8e3f564f3b7038 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40414243444546 +Out = 956DA1C68D83A7B881E01B9A966C3C0BF27F68606A8B71D457BD016D4C41DD8A380C709A296CB4C6544792920FD788835771A07D4A16FB52ED48050331DC4C8B -In = E728DE62D75856500C4C77A428612CD804F30C3F10D36FB219C5CA0AA30726AB190E5F3F279E0733D77E7267C17BE27D21650A9A4D1E32F649627638DBADA9702C7CA303269ED14014B2F3CF8B894EAC8554 -Out = ebc14bf37fee002b132eac0f15716a233188cbb133e630dcf9c370a6c87466557c292dd699218ed891c0a70607e77bb761d03744b5091d3dbd7a519273ded9cf +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F4041424344454647 +Out = DF186C2DC09CAA48E14E942F75DE5AC1B7A21E4F9F072A5B371E09E07345B0740C76177B01278808FEC025EDED9822C122AFD1C63E6F0CE2E32631041063145C -In = 6348F229E7B1DF3B770C77544E5166E081850FA1C6C88169DB74C76E42EB983FACB276AD6A0D1FA7B50D3E3B6FCD799EC97470920A7ABED47D288FF883E24CA21C7F8016B93BB9B9E078BDB9703D2B781B616E -Out = 772782a2f0cf9c0bad2e91a64b9c6b5f8c53c9ad3d55607ffb46be7f37828806320e5e82ba8fd62aa443eb226677df628c14276791ea4062c8da10b6c3786130 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748 +Out = 87475640966A9FDCD6D3A3B5A2CCA5C08F0D882B10243C0EC1BF3C6B1C37F2CD3212F19A057864477D5EAF8FAED73F2937C768A0AF415E84BBCE6BD7DE23B660 -In = 4B127FDE5DE733A1680C2790363627E63AC8A3F1B4707D982CAEA258655D9BF18F89AFE54127482BA01E08845594B671306A025C9A5C5B6F93B0A39522DC877437BE5C2436CBF300CE7AB6747934FCFC30AEAAF6 -Out = 61ddf7035a24a93eac20e48735bfd1ee901b2a5e2eab74d6aa3b9a22c3dd41af3f6c6f8540812145efff0a4947828c22d4f369fa08293aa908d738883bf94c2c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40414243444546474849 +Out = C3B573BBE10949A0FBD4FF884C446F2229B76902F9DFDBB8A0353DA5C83CA14E8151BBAAC82FD1576A009ADC6F1935CF26EDD4F1FB8DA483E6C5CD9D8923ADC3 -In = 08461F006CFF4CC64B752C957287E5A0FAABC05C9BFF89D23FD902D324C79903B48FCB8F8F4B01F3E4DDB483593D25F000386698F5ADE7FAADE9615FDC50D32785EA51D49894E45BAA3DC707E224688C6408B68B11 -Out = 30af5f1ce400715ec3e76eea3bd51eb8fc252d825d8b3ac2430fad259cc72d1110024293ca3615e41f1445ecdb13169aaf2ca0f23a920d0754b1556ae4fab29c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A +Out = B09D8D0BBA8A7286E43568F7907550E42036D674E3C8FC34D8CA46F771D6466B70FB605875F6A863C877D12F07063FDC2E90CCD459B1910DCD52D8F10B2B0A15 -In = 68C8F8849B120E6E0C9969A5866AF591A829B92F33CD9A4A3196957A148C49138E1E2F5C7619A6D5EDEBE995ACD81EC8BB9C7B9CFCA678D081EA9E25A75D39DB04E18D475920CE828B94E72241F24DB72546B352A0E4 -Out = f38dd5440610a5b56c10bb4a84b393520d25cd5665ae8acae8a2041d22c50428776fcca9471a92bf32efdea5a8321d051e78f396535f4bf0ae4f3c7ae878c046 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B +Out = AF3A22BF75B21ABFB0ACD54422BA1B7300A952EFF02EBEB65B5C234471A98DF32F4F9643CE1904108A168767924280BD76C83F8C82D9A79D9259B195362A2A04 -In = B8D56472954E31FB54E28FCA743F84D8DC34891CB564C64B08F7B71636DEBD64CA1EDBDBA7FC5C3E40049CE982BBA8C7E0703034E331384695E9DE76B5104F2FBC4535ECBEEBC33BC27F29F18F6F27E8023B0FBB6F563C -Out = 3d6904ce2d4d926212ee7f55399815766d6a6199ff05849976cb54916d68def0ce712aeb8106dad0b891f80363276058c5428f90c9c026d4d349383465d80ecf +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C +Out = BF4FF2221B7E6957A724CD964AA3D5D0D9941F540413752F4699D8101B3E537508BF09F8508B317736FFD265F2847AA7D84BD2D97569C49D632AED9945E5FA5E -In = 0D58AC665FA84342E60CEFEE31B1A4EACDB092F122DFC68309077AED1F3E528F578859EE9E4CEFB4A728E946324927B675CD4F4AC84F64DB3DACFE850C1DD18744C74CECCD9FE4DC214085108F404EAB6D8F452B5442A47D -Out = f3ec578b2a40e894e34d1b37888fb831677635b315c423d25e477f440abc1dfb10cdd62891f65544ca0404a513e194bf3905b297774b647ffe523197bb4ca889 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D +Out = 9C6B6B78199B1BDACB4300E31479FA622A6B5BC80D4678A6078F88A8268CD7206A2799E8D4621A464EF6B43DD8ADFFE97CAF221B22B6B8778B149A822AEFBB09 -In = 1755E2D2E5D1C1B0156456B539753FF416651D44698E87002DCF61DCFA2B4E72F264D9AD591DF1FDEE7B41B2EB00283C5AEBB3411323B672EAA145C5125185104F20F335804B02325B6DEA65603F349F4D5D8B782DD3469CCD -Out = a5114ffa5542c0bee2b0a80a2b3d9344c200495f96b9002c1da1c4a05da31a5d848941c078578d76b88a91012f72ad0ad4adcaacfbd7f1cd11d938e0e350231a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E +Out = 890656F09C99D280B5ECB381F56427B813751BC652C7828078B23A4AF83B4E3A61FDBAC61F89BEE84EA6BEE760C047F25C6B0A201C69A38FD6FD971AF18588BB -In = B180DE1A611111EE7584BA2C4B020598CD574AC77E404E853D15A101C6F5A2E5C801D7D85DC95286A1804C870BB9F00FD4DCB03AA8328275158819DCAD7253F3E3D237AEAA7979268A5DB1C6CE08A9EC7C2579783C8AFC1F91A7 -Out = 5d61e4a3a9defaf1faf4dc9cfefaa126b0243accac611ddb9394390f7cd4d182e663e28e87ee9bb30c844d24373ae9c5005786a612c1ba32b583b9183df8cfbc +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F +Out = 31A046F7882FFE6F83CE472E9A0701832EC7B3F76FBCFD1DF60FE3EA48FDE1651254247C3FD95E100F9172731E17FD5297C11F4BB328363CA361624A81AF797C -In = CF3583CBDFD4CBC17063B1E7D90B02F0E6E2EE05F99D77E24E560392535E47E05077157F96813544A17046914F9EFB64762A23CF7A49FE52A0A4C01C630CFE8727B81FB99A89FF7CC11DCA5173057E0417B8FE7A9EFBA6D95C555F -Out = e2e10d6f6d41f2f3ee16b72a37d5192c41d60da003e32711ed8a793725ee8e80120e7ecdf89306c62aa8bc31261179026cb64c528484359c20701068ca9a07ab +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50 +Out = 27A60B2D00E7A671D47D0AEC2A686A0AC04B52F40AB6629028EB7D13F4BAA99AC0FE46EE6C814944F2F4B4D20E9378E4847EA44C13178091E277B87EA7A55711 -In = 072FC02340EF99115BAD72F92C01E4C093B9599F6CFC45CB380EE686CB5EB019E806AB9BD55E634AB10AA62A9510CC0672CD3EDDB589C7DF2B67FCD3329F61B1A4441ECA87A33C8F55DA4FBBAD5CF2B2527B8E983BB31A2FADEC7523 -Out = c8805c26b92b9fc68ed2755d0f93a815f7202a18a824b00547d79ff78f13ba3978c353d31ee4173d428ec8c60f4356ab13854ff35151b41c76740e19f426d002 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F5051 +Out = 8B5CCEF194162C1F19D68F91E0B0928F289EC5283720840C2F73D253111238DCFE94AF2B59C2C1CA2591901A7BC060E7459B6C47DF0F71701A35CC0AA831B5B6 -In = 76EECF956A52649F877528146DE33DF249CD800E21830F65E90F0F25CA9D6540FDE40603230ECA6760F1139C7F268DEBA2060631EEA92B1FFF05F93FD5572FBE29579ECD48BC3A8D6C2EB4A6B26E38D6C5FBF2C08044AEEA470A8F2F26 -Out = 45936978ee10e57bdda2c7dc9c59902b30c242cd5f7d046aa8b3665a95fef8730af3d3b26c4602f15c77f53804f6b5440010e1230c8228f0248451a97b56dd4d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152 +Out = 57AB6C4B2229AEB3B70476D803CD63812F107CE6DA17FED9B17875E8F86C724F49E024CBF3A1B8B119C50357652B81879D2ADE2D588B9E4F7CEDBA0E4644C9EE -In = 7ADC0B6693E61C269F278E6944A5A2D8300981E40022F839AC644387BFAC9086650085C2CDC585FEA47B9D2E52D65A2B29A7DC370401EF5D60DD0D21F9E2B90FAE919319B14B8C5565B0423CEFB827D5F1203302A9D01523498A4DB10374 -Out = 61e9ebfc3eca0048b66469d3fd4467c59ce826cfd826f875608dd6253eb79973ea7865d0eb6f118bf20dd063be7a44d12e58eb4140423d025780da33256dec96 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50515253 +Out = 0190A8DAC320A739F322E15731AA140DDAF5BED294D5C82E54FEF29F214E18AAFAA84F8BE99AF62950266B8F901F15DD4C5D35516FC35B4CAB2E96E4695BBE1C -In = E1FFFA9826CCE8B86BCCEFB8794E48C46CDF372013F782ECED1E378269B7BE2B7BF51374092261AE120E822BE685F2E7A83664BCFBE38FE8633F24E633FFE1988E1BC5ACF59A587079A57A910BDA60060E85B5F5B6F776F0529639D9CCE4BD -Out = 2e7d6aaca1b5c9579cb1027147c0d2837148d6dce77427e6ecf099f7363e21c363f895c265c6d7693d40619247e7ad3f2a250f9bd044051e4420661499e3abf3 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F5051525354 +Out = D14D7C4C415EEB0E10B159224BEA127EBD84F9591C702A330F5BB7BB7AA44EA39DE6ED01F18DA7ADF40CFB97C5D152C27528824B21E239526AF8F36B214E0CFB -In = 69F9ABBA65592EE01DB4DCE52DBAB90B08FC04193602792EE4DAA263033D59081587B09BBE49D0B49C9825D22840B2FF5D9C5155F975F8F2C2E7A90C75D2E4A8040FE39F63BBAFB403D9E28CC3B86E04E394A9C9E8065BD3C85FA9F0C7891600 -Out = 697763f1910368ff4ce75e907a5fcd2454b388aac955638ff7d3e2b7c6b526263079612143a214316f94bec80dbd974638bab37a79b9938b2b642cec33f6900a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455 +Out = BE28C4BE706970488FAC7D29C3BD5C4E986085C4C3332F1F3FD30973DB614164BA2F31A78875FFDC150325C88327A9443ED04FDFE5BE93876D1628560C764A80 -In = 38A10A352CA5AEDFA8E19C64787D8E9C3A75DBF3B8674BFAB29B5DBFC15A63D10FAE66CD1A6E6D2452D557967EAAD89A4C98449787B0B3164CA5B717A93F24EB0B506CEB70CBBCB8D72B2A72993F909AAD92F044E0B5A2C9AC9CB16A0CA2F81F49 -Out = 9794754a10e3fe43228fbcf088ade821d3823e31779c707f51c044ea6c87b4ca93695685865238e8f0a4c77a9014a540e76850eb076569250fe3515940eecf52 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50515253545556 +Out = 031DA1069E3A2E9C3382E436FFD79DF74B1CA6A8ADB2DEABE676AB45994CBC054F037D2F0EACE858D32C14E2D1C8B46077308E3BDC2C1B53172ECF7A8C14E349 -In = 6D8C6E449BC13634F115749C248C17CD148B72157A2C37BF8969EA83B4D6BA8C0EE2711C28EE11495F43049596520CE436004B026B6C1F7292B9C436B055CBB72D530D860D1276A1502A5140E3C3F54A93663E4D20EDEC32D284E25564F624955B52 -Out = 66f0c30b81a8682c4b6da37296e2f1a267ff041f8588e155067b6ba1114a69958700ea3c3c76cfa3dd2d4a46611e19cc3b157510d55e4d7f675790cf264b4c8d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F5051525354555657 +Out = 4665CEF8BA4DB4D0ACB118F2987F0BB09F8F86AA445AA3D5FC9A8B346864787489E8FCECC125D17E9B56E12988EAC5ECC7286883DB0661B8FF05DA2AFFF30FE4 -In = 6EFCBCAF451C129DBE00B9CEF0C3749D3EE9D41C7BD500ADE40CDC65DEDBBBADB885A5B14B32A0C0D087825201E303288A733842FA7E599C0C514E078F05C821C7A4498B01C40032E9F1872A1C925FA17CE253E8935E4C3C71282242CB716B2089CCC1 -Out = ccdcc5dc3b458a1bd9d598405594c9dfe8a1ed4ad31fbe0c9df3e86688f10cc2a44f49a86d1c619e1bdce05510a443423dd28f9b6dbc1d67aac28ab6dd46ae62 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758 +Out = 63B7032E5F930CC9939517F9E986816CFBEC2BE59B9568B13F2EAD05BAE7777CAB620C6659404F7409E4199A3BE5F7865AA7CBDF8C4253F7E8219B1BD5F46FEA -In = 433C5303131624C0021D868A30825475E8D0BD3052A022180398F4CA4423B98214B6BEAAC21C8807A2C33F8C93BD42B092CC1B06CEDF3224D5ED1EC29784444F22E08A55AA58542B524B02CD3D5D5F6907AFE71C5D7462224A3F9D9E53E7E0846DCBB4CE -Out = 483e1c04aa498d310258fd77cc55a601f734cf12719e6547f16cd9b92cf45053e5be9aa80479ac4d4595916932d171fb1f0ebe1aa50806dc7dbf6776232a1139 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50515253545556575859 +Out = 9F09BF093A2B0FF8C2634B49E37F1B2135B447AA9144C9787DBFD92129316C99E88AAB8A21FDEF2372D1189AEC500F95775F1F92BFB45545E4259FB9B7B02D14 -In = A873E0C67CA639026B6683008F7AA6324D4979550E9BCE064CA1E1FB97A30B147A24F3F666C0A72D71348EDE701CF2D17E2253C34D1EC3B647DBCEF2F879F4EB881C4830B791378C901EB725EA5C172316C6D606E0AF7DF4DF7F76E490CD30B2BADF45685F -Out = c492c716b387aa60b25192618195279a9703e94f21b59f1ac42ec2818e4c51006c6588c48e1efaeae580c41ea324ea128a8d9574c9566e7aeb6f16ca653dcba7 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A +Out = F9F8493C68088807DF7F6A2693D64EA59F03E9E05A223E68524CA32195A4734B654FCEA4D2734C866CF95C889FB10C49159BE2F5043DC98BB55E02EF7BDCB082 -In = 006917B64F9DCDF1D2D87C8A6173B64F6587168E80FAA80F82D84F60301E561E312D9FBCE62F39A6FB476E01E925F26BCC91DE621449BE6504C504830AAE394096C8FC7694651051365D4EE9070101EC9B68086F2EA8F8AB7B811EA8AD934D5C9B62C60A4771 -Out = 57055d19864eacae49454ff937807bd66fe4d4ef2c46a71f0c67cc1378c6982875b0ed01d838a4a72ca39950edcee0a66036e036fcf26d730e4f75c2eefa20d0 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B +Out = 3C9A7359AB4FEBCE07B20AC447B06A240B7FE1DAE5439C49B60B5819F7812E4C172406C1AAC316713CF0DDED1038077258E2EFF5B33913D9D95CAEB4E6C6B970 -In = F13C972C52CB3CC4A4DF28C97F2DF11CE089B815466BE88863243EB318C2ADB1A417CB1041308598541720197B9B1CB5BA2318BD5574D1DF2174AF14884149BA9B2F446D609DF240CE335599957B8EC80876D9A085AE084907BC5961B20BF5F6CA58D5DAB38ADB -Out = fd16d5eafac106ddd134fcf0263209bba499f88cc0465ce9a6c35bbead3c0fa0a3193b6c52bde6b54f9e8db7d6972b27ddfcd0c6f71c12ad90600b0936eb2a4e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C +Out = AD6AAB8084510E822CFCE8625D62CF4DE655F4763884C71E80BAB9AC9D5318DBA4A6033ED29084E65216C031606CA17615DCFE3BA11D26851AE0999CA6E232CF -In = E35780EB9799AD4C77535D4DDB683CF33EF367715327CF4C4A58ED9CBDCDD486F669F80189D549A9364FA82A51A52654EC721BB3AAB95DCEB4A86A6AFA93826DB923517E928F33E3FBA850D45660EF83B9876ACCAFA2A9987A254B137C6E140A21691E1069413848 -Out = 3eb279ee85ea26405223233073d8a0d8ed7188fac306937020ec9a6492ed2449bfde6e1fc5dc805c9bce5bedbe00b649c54af24bf7a41728ee8cdb8dbb7c0470 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D +Out = 156E9E6261374C9DC884F36E70F0FE1AB9297997B836FA7D170A9C9EBF575B881E7BCEA44D6C0248D35597907154828955BE19135852F9228815ECA024A8ADFB -In = 64EC021C9585E01FFE6D31BB50D44C79B6993D72678163DB474947A053674619D158016ADB243F5C8D50AA92F50AB36E579FF2DABB780A2B529370DAA299207CFBCDD3A9A25006D19C4F1FE33E4B1EAEC315D8C6EE1E730623FD1941875B924EB57D6D0C2EDC4E78D6 -Out = 7b000245b287b8527f90798a712f793b22b961793b9778e4c907a784061553ab9b713a29e21fa2602538b1ea14cf58cc9b9121c9c652ccc4a6577f4f3f59c023 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E +Out = 4215407633F4CCA9B6788BE93E6AA3D963C7D6CE4B147247099F46A3ACB500A30038CB3E788C3D29F132AD844E80E9E99251F6DB96ACD8A091CFC770AF53847B -In = 5954BAB512CF327D66B5D9F296180080402624AD7628506B555EEA8382562324CF452FBA4A2130DE3E165D11831A270D9CB97CE8C2D32A96F50D71600BB4CA268CF98E90D6496B0A6619A5A8C63DB6D8A0634DFC6C7EC8EA9C006B6C456F1B20CD19E781AF20454AC880 -Out = e7e68bc85f25b3871709c5adb8420923d142351e9eb8057419bb50c2aa936157d589f8d60c902871b6a954f02ade64aa1b0f72ec9873644d4eb0d1ae873225e2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F +Out = 1C077E279DE6548523502B6DF800FFDAB5E2C3E9442EB838F58C295F3B147CEF9D701C41C321283F00C71AFFA0619310399126295B78DD4D1A74572EF9ED5135 -In = 03D9F92B2C565709A568724A0AFF90F8F347F43B02338F94A03ED32E6F33666FF5802DA4C81BDCE0D0E86C04AFD4EDC2FC8B4141C2975B6F07639B1994C973D9A9AFCE3D9D365862003498513BFA166D2629E314D97441667B007414E739D7FEBF0FE3C32C17AA188A8683 -Out = 498696271b2009f9aef1b570257c9aebaf399504d15ff8f1d647f92f00f6f962fb07a3c884ff854fc064bca38ed5e7fa78bdb5985841455e85c9f96a5e0152af +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60 +Out = F07A555F49FE481CF4CD0A87B71B82E4A95064D06677FDD90A0EB598877BA1C83D4677B393C3A3B6661C421F5B12CB99D20376BA7275C2F3A8F5A9B7821720DA -In = F31E8B4F9E0621D531D22A380BE5D9ABD56FAEC53CBD39B1FAB230EA67184440E5B1D15457BD25F56204FA917FA48E669016CB48C1FFC1E1E45274B3B47379E00A43843CF8601A5551411EC12503E5AAC43D8676A1B2297EC7A0800DBFEE04292E937F21C005F17411473041 -Out = 2d95ad464465805ef681b1442b327de134cbb9b677dfc25b253a7b70ff11f011ff1b743f79854b6577645bc7f25757d61a729f4a2feb6125d08d6a1a55acabad +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6061 +Out = B5911B380D20C7B04323E4026B38E200F534259233B581E02C1E3E2D8438D6C66D5A4EB201D5A8B75072C4EC29106334DA70BC79521B0CED2CFD533F5FF84F95 -In = 758EA3FEA738973DB0B8BE7E599BBEF4519373D6E6DCD7195EA885FC991D896762992759C2A09002912FB08E0CB5B76F49162AEB8CF87B172CF3AD190253DF612F77B1F0C532E3B5FC99C2D31F8F65011695A087A35EE4EEE5E334C369D8EE5D29F695815D866DA99DF3F79403 -Out = ab6a84eec8c58e3a49efc4200dfd5bcb2702a5eaf94881a63b1d9222e306b9d6261aa02441a38a9040800707b992107f26020a041e3aeab2aab0a68749d11e74 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162 +Out = 01F070A09BAE911296361F91AA0E8E0D09A7725478536D9D48C5FE1E5E7C3C5B9B9D6EB07796F6DA57AE562A7D70E882E37ADFDE83F0C433C2CD363536BB22C8 -In = 47C6E0C2B74948465921868804F0F7BD50DD323583DC784F998A93CD1CA4C6EF84D41DC81C2C40F34B5BEE6A93867B3BDBA0052C5F59E6F3657918C382E771D33109122CC8BB0E1E53C4E3D13B43CE44970F5E0C079D2AD7D7A3549CD75760C21BB15B447589E86E8D76B1E9CED2 -Out = 33757eee7a4973a83216ef0bca5ab2713e76bfee9d9e9f5efa46faba1888a0486cb9ec50e53c0d976db08d84de254f7bcff16a45f6c739c620c8a2616b57e7a2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60616263 +Out = 6F793EB4374A48B0775ACAF9ADCF8E45E54270C9475F004AD8D5973E2ACA52747FF4ED04AE967275B9F9EB0E1FF75FB4F794FA8BE9ADD7A41304868D103FAB10 -In = F690A132AB46B28EDFA6479283D6444E371C6459108AFD9C35DBD235E0B6B6FF4C4EA58E7554BD002460433B2164CA51E868F7947D7D7A0D792E4ABF0BE5F450853CC40D85485B2B8857EA31B5EA6E4CCFA2F3A7EF3380066D7D8979FDAC618AAD3D7E886DEA4F005AE4AD05E5065F -Out = 998284e7687d92cce337b69bf850d389279d9d8f1276c63d4f736b8e6c3876fad215b098b0e42b5b20bdb2d8a9ef9ab67d109b95c24e7b030b371457ff43e0d9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6061626364 +Out = 965F20F139765FCC4CE4BA3794675863CAC24DB472CD2B799D035BCE3DBEA502DA7B524865F6B811D8C5828D3A889646FE64A380DA1AA7C7044E9F245DCED128 -In = 58D6A99BC6458824B256916770A8417040721CCCFD4B79EACD8B65A3767CE5BA7E74104C985AC56B8CC9AEBD16FEBD4CDA5ADB130B0FF2329CC8D611EB14DAC268A2F9E633C99DE33997FEA41C52A7C5E1317D5B5DAED35EBA7D5A60E45D1FA7EAABC35F5C2B0A0F2379231953322C4E -Out = 258bc25d618dc460087bb02e2ba2e2fb9ce05a83c79cc442e1f461023e8fa5fc57f6b5465e29350175f3fa7154585e3df7ae016049fafb07e8a94683e47712b8 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465 +Out = EC295B5783601244C30E4641E3B45BE222C4DCE77A58700F53BC8EC52A941690B4D0B087FB6FCB3F39832B9DE8F75EC20BD43079811749CDC907EDB94157D180 -In = BEFAB574396D7F8B6705E2D5B58B2C1C820BB24E3F4BAE3E8FBCD36DBF734EE14E5D6AB972AEDD3540235466E825850EE4C512EA9795ABFD33F330D9FD7F79E62BBB63A6EA85DE15BEAEEA6F8D204A28956059E2632D11861DFB0E65BC07AC8A159388D5C3277E227286F65FF5E5B5AEC1 -Out = abd00774f93f2a75ccf7af7d9c8e8f6e0e3cfb90cadcaebbe92722f46dd207e5cbdead01827d2cbc4ec152ad0d1e5a92ac4b1dd1117bf7b252f56ae110cac3f1 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60616263646566 +Out = 61C72F8CCC91DBB54CA6750BC489672DE09FAEDB8FDD4F94FF2320909A303F5D5A98481C0BC1A625419FB4DEBFBF7F8A53BB07EC3D985E8EA11E72D559940780 -In = 8E58144FA9179D686478622CE450C748260C95D1BA43B8F9B59ABECA8D93488DA73463EF40198B4D16FB0B0707201347E0506FF19D01BEA0F42B8AF9E71A1F1BD168781069D4D338FDEF00BF419FBB003031DF671F4A37979564F69282DE9C65407847DD0DA505AB1641C02DEA4F0D834986 -Out = 29c1daa5106facea688dd0300eab219dd67554b761443cafb5204b75b84c8bb05c5f3fd2a0eed0b39c1e70fe8b115089faf2c04316bee15500ab40861660916f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6061626364656667 +Out = AFD8145B259EEFC8D12620C3C5B03E1ED8FD2CCEFE0365078C80FD42C1770E28B44948F27E65A1886690110DB814397B68E43D80D1BA16DFA358E739C898CFA3 -In = B55C10EAE0EC684C16D13463F29291BF26C82E2FA0422A99C71DB4AF14DD9C7F33EDA52FD73D017CC0F2DBE734D831F0D820D06D5F89DACC485739144F8CFD4799223B1AFF9031A105CB6A029BA71E6E5867D85A554991C38DF3C9EF8C1E1E9A7630BE61CAABCA69280C399C1FB7A12D12AEFC -Out = 1cf96cad34bd8efef3453b1e64231fdb79ac15055f96b7fc935895b37aad6e1a9c25188a4cde7198f2762ce2acc32e97fef46a161323e185be90611171ed37f9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768 +Out = 552FC7893CF1CE933ADA35C0DA98844E41545E244C3157A1428D7B4C21F9CD7E4071AED77B7CA9F1C38FBA32237412EF21A342742EC8324378F21E507FAFDD88 -In = 2EEEA693F585F4ED6F6F8865BBAE47A6908AECD7C429E4BEC4F0DE1D0CA0183FA201A0CB14A529B7D7AC0E6FF6607A3243EE9FB11BCF3E2304FE75FFCDDD6C5C2E2A4CD45F63C962D010645058D36571404A6D2B4F44755434D76998E83409C3205AA1615DB44057DB991231D2CB42624574F545 -Out = c10b4ac1aff6c2f5ea841a60f9c9f9c73793c7059f8becb086d7d6b9d2f11a4d3c12eaa191f5b8bca1369eeb2096554bb8b0367c04c608f6c3fecdcd2d69d762 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60616263646566676869 +Out = 467A33FBADF5EBC52596EF86AAAEFC6FABA8EE651B1CE04DE368A03A5A9040EF2835E00ADB09ABB3FBD2BCE818A2413D0B0253B5BDA4FC5B2F6F85F3FD5B55F2 -In = DAB11DC0B047DB0420A585F56C42D93175562852428499F66A0DB811FCDDDAB2F7CDFFED1543E5FB72110B64686BC7B6887A538AD44C050F1E42631BC4EC8A9F2A047163D822A38989EE4AAB01B4C1F161B062D873B1CFA388FD301514F62224157B9BEF423C7783B7AAC8D30D65CD1BBA8D689C2D -Out = a5ef0500b3994db5c05fe7b63d1a9f206d61494a4566440ae12e09a4eb55efb93aed128da8e42a56d51822915dc1fa5a4f49e5f6b47328fc4431cf8352f91af4 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A +Out = 22EFF8E6DD5236F5F57D94EDE874D6C9428E8F5D566F17CD6D1848CD752FE13C655CB10FBAAFF76872F2BF2DA99E15DC624075E1EC2F58A3F64072121838569E -In = 42E99A2F80AEE0E001279A2434F731E01D34A44B1A8101726921C0590C30F3120EB83059F325E894A5AC959DCA71CE2214799916424E859D27D789437B9D27240BF8C35ADBAFCECC322B48AA205B293962D858652ABACBD588BCF6CBC388D0993BD622F96ED54614C25B6A9AA527589EAAFFCF17DDF7 -Out = b66fdcbf35be506d81f580988a909c771306250afc627baf57f3395781598fe42ff6a42ce45ed895fbec7c23942a760e20374a61968a38462a369ab57c480aac +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B +Out = 9CEC6BBF62C4BCE4138ABAE1CBEC8DAD31950444E90321B1347196834C114B864AF3F3CC3508F83751FFB4EDA7C84D140734BB4263C3625C00F04F4C8068981B -In = 3C9B46450C0F2CAE8E3823F8BDB4277F31B744CE2EB17054BDDC6DFF36AF7F49FB8A2320CC3BDF8E0A2EA29AD3A55DE1165D219ADEDDB5175253E2D1489E9B6FDD02E2C3D3A4B54D60E3A47334C37913C5695378A669E9B72DEC32AF5434F93F46176EBF044C4784467C700470D0C0B40C8A088C815816 -Out = 54b7035cb097ab5c0894dd3719b7534f5bfc4afdadc19bd5f5c5cebdc4c8bce8ef5bcda21dc2273cb2d412aabdbdcf470a3248b79a0db00b3a2fa0795d4b044b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C +Out = A8B60FA4FC2442F6F1514AD7402626920CC7C2C9F72124B8CBA8EE2CB7C4586F658A4410CFFCC0AB88343955E094C6AF0D20D0C714FB0A988F543F300F58D389 -In = D1E654B77CB155F5C77971A64DF9E5D34C26A3CAD6C7F6B300D39DEB1910094691ADAA095BE4BA5D86690A976428635D5526F3E946F7DC3BD4DBC78999E653441187A81F9ADCD5A3C5F254BC8256B0158F54673DCC1232F6E918EBFC6C51CE67EAEB042D9F57EEC4BFE910E169AF78B3DE48D137DF4F2840 -Out = 26b86742fc3a6bbd6a7a5284cd0501e23686ff183912d23d70d2547bacbde06938d81e6b05bbda2ec447665cda49cd6841a293c52459b17dcc47cd557dfabf4c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D +Out = 8271CC45DFA5E4170E847E8630B952CF9C2AA777D06F26A7585B8381F188DACC7337391CFCC94B053DC4EC29CC17F077870428F1AC23FDDDA165EF5A3F155F39 -In = 626F68C18A69A6590159A9C46BE03D5965698F2DAC3DE779B878B3D9C421E0F21B955A16C715C1EC1E22CE3EB645B8B4F263F60660EA3028981EEBD6C8C3A367285B691C8EE56944A7CD1217997E1D9C21620B536BDBD5DE8925FF71DEC6FBC06624AB6B21E329813DE90D1E572DFB89A18120C3F606355D25 -Out = 7983c025e5e5f709b5ebad8e460bd79b1d75870e5b03ddbd519e7643f5015cb78ebc8caf35570d7cf5f1c6985e0fbdd62a7180717f82ba8d8fa60e2900bdf630 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E +Out = BF23C0C25C8060E4F6995F1623A3BEBECAA96E308680000A8AA3CD56BB1A6DA099E10D9231B37F4519B2EFD2C24DE72F31A5F19535241B4A59FA3C03CEB790E7 -In = 651A6FB3C4B80C7C68C6011675E6094EB56ABF5FC3057324EBC6477825061F9F27E7A94633ABD1FA598A746E4A577CAF524C52EC1788471F92B8C37F23795CA19D559D446CAB16CBCDCE90B79FA1026CEE77BF4AB1B503C5B94C2256AD75B3EAC6FD5DCB96ACA4B03A834BFB4E9AF988CECBF2AE597CB9097940 -Out = ca2e683ac12ab45e5aea1f8ad2d38acdedeb8f1ef56c3b549a8b3ab77be27fe9c4f5eb33ce0ca1cef12b167d3a2da03bfdb013053d288d99b1390300c2314e4c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F +Out = 877FD652C05281009C0A5250E7A3A671F8B18C108817FE4A874DE22DA8E45DB11958A600C5F62E67D36CBF84474CF244A9C2B03A9FB9DC711CD1A2CAB6F3FAE0 -In = 8AAF072FCE8A2D96BC10B3C91C809EE93072FB205CA7F10ABD82ECD82CF040B1BC49EA13D1857815C0E99781DE3ADBB5443CE1C897E55188CEAF221AA9681638DE05AE1B322938F46BCE51543B57ECDB4C266272259D1798DE13BE90E10EFEC2D07484D9B21A3870E2AA9E06C21AA2D0C9CF420080A80A91DEE16F -Out = 7ad59b633faf7945264e5a9338e2c1d6dd17f36fc58b117129e8a23b5b91b5f59129e104058f468d19b05f04d8ecc56df60c26dfecc28a142e60753b6fad184b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70 +Out = 29DF4D87EA444BAF5BCDF5F4E41579E28A67DE84149F06C03F110EA84F572A9F676ADDD04C4878F49C5C00ACCDA441B1A387CACEB2E993BB7A10CD8C2D6717E1 -In = 53F918FD00B1701BD504F8CDEA803ACCA21AC18C564AB90C2A17DA592C7D69688F6580575395551E8CD33E0FEF08CA6ED4588D4D140B3E44C032355DF1C531564D7F4835753344345A6781E11CD5E095B73DF5F82C8AE3AD00877936896671E947CC52E2B29DCD463D90A0C9929128DA222B5A211450BBC0E02448E2 -Out = fdd4decc23bcff136f54c1df4496ecff0be04d44e47af44664064cb8304bf0599c3069d1ccb9a642746c8ce0ec40512c524bfcd67d35f01211faa14d805c5631 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F7071 +Out = 710DACB166844639CD7B637C274209424E2449DC35D790BBFA4F76177054A36B3B76FAC0CA6E61DF1E687000678AC0746DF75D0A3954897681FD393A155A1BB4 -In = A64599B8A61B5CCEC9E67AED69447459C8DA3D1EC6C7C7C82A7428B9B584FA67E90F68E2C00FBBED4613666E5168DA4A16F395F7A3C3832B3B134BFC9CBAA95D2A0FE252F44AC6681EB6D40AB91C1D0282FED6701C57463D3C5F2BB8C6A7301FB4576AA3B5F15510DB8956FF77478C26A7C09BEA7B398CFC83503F538E -Out = 22f166360c2012b0492f0ded75d242649cf64a378d76469db5428ee9f70daa639aaf00ea5452376ec9a7856b86005bd260358dbf57422a9681ec88a188b19fce +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172 +Out = C1D5F93B8DEA1F2571BABCCBC01764541A0CDA87E444D673C50966CA559C33354B3ACB26E5D5781FFB28847A4B4754D77008C62A835835F500DEA7C3B58BDAE2 -In = 0E3AB0E054739B00CDB6A87BD12CAE024B54CB5E550E6C425360C2E87E59401F5EC24EF0314855F0F56C47695D56A7FB1417693AF2A1ED5291F2FEE95F75EED54A1B1C2E81226FBFF6F63ADE584911C71967A8EB70933BC3F5D15BC91B5C2644D9516D3C3A8C154EE48E118BD1442C043C7A0DBA5AC5B1D5360AAE5B9065 -Out = 7d48a380a623b044c48c8ac2cb3215b44581c5bf8f976990e53e201a7ce07cca1fd14ce6a9e8afd3b4a56d7284512d7b1b32c010989fb2b31c386a56f79708ec +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273 +Out = A41E41271CDAB8AF4D72B104BFB2AD041AC4DF14677DA671D85640C4B187F50C2B66513C4619FBD5D5DC4FE65DD37B9042E9848DDA556A504CAA2B1C6AFE4730 -In = A62FC595B4096E6336E53FCDFC8D1CC175D71DAC9D750A6133D23199EAAC288207944CEA6B16D27631915B4619F743DA2E30A0C00BBDB1BBB35AB852EF3B9AEC6B0A8DCC6E9E1ABAA3AD62AC0A6C5DE765DE2C3711B769E3FDE44A74016FFF82AC46FA8F1797D3B2A726B696E3DEA5530439ACEE3A45C2A51BC32DD055650B -Out = 4a3c9089148f5be86e123b4114ba5c12d2cbb73937a9b5375dc6670598789248f8a7b43d2d612617048763f3eff8ccca3ffad24e12801911a0cd2821990693ee +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F7071727374 +Out = E7BCBACDC379C43D81EBADCB37781552FC1D753E8CF310D968392D06C91F1D64CC9E90CE1D22C32D277FC6CDA433A4D442C762E9EACF2C259F32D64CF9DA3A22 -In = 2B6DB7CED8665EBE9DEB080295218426BDAA7C6DA9ADD2088932CDFFBAA1C14129BCCDD70F369EFB149285858D2B1D155D14DE2FDB680A8B027284055182A0CAE275234CC9C92863C1B4AB66F304CF0621CD54565F5BFF461D3B461BD40DF28198E3732501B4860EADD503D26D6E69338F4E0456E9E9BAF3D827AE685FB1D817 -Out = 16d2f600f38cf0eb7c5c1e347817ad3c98b085f720007e98aa82dcb68de46ea939f768809ea60acc7c92d6d9712809b1f91690e7192d04ccb34b61c00e01becc +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475 +Out = 51755B4AC5456B13218A19C5B9242F57C4A981E4D4ECDCE09A3193362B808A579345D4881C2607A56534DD7F21956AFF72C2F4173A6E7B6CC2212BA0E3DAEE1F -In = 10DB509B2CDCABA6C062AE33BE48116A29EB18E390E1BBADA5CA0A2718AFBCD23431440106594893043CC7F2625281BF7DE2655880966A23705F0C5155C2F5CCA9F2C2142E96D0A2E763B70686CD421B5DB812DACED0C6D65035FDE558E94F26B3E6DDE5BD13980CC80292B723013BD033284584BFF27657871B0CF07A849F4AE2 -Out = ca0a8af149192df8b7f99fbae435fa0a897228998ca225346a4f9754300a9bd63b0712a92ea9c1ddb43208c9365def3bb9f639e7217cf6b2e5cbc35746383343 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576 +Out = DCC2C4BEB9C1F2607B786C20C631972347034C1CC02FCC7D02FF01099CFE1C6989840AC213923629113AA8BAD713CCF0FE4CE13264FB32B8B0FE372DA382544A -In = 9334DE60C997BDA6086101A6314F64E4458F5FF9450C509DF006E8C547983C651CA97879175AABA0C539E82D05C1E02C480975CBB30118121061B1EBAC4F8D9A3781E2DB6B18042E01ECF9017A64A0E57447EC7FCBE6A7F82585F7403EE2223D52D37B4BF426428613D6B4257980972A0ACAB508A7620C1CB28EB4E9D30FC41361EC -Out = 4020f947b7ab7194b53e45ae11aa1c21c96db65e7ec9cfa3ed87918be7fffa258e5d824f1e58d9250273db639ca6f8984652a01e3364feb53bb46bb2311e3a7a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F7071727374757677 +Out = 3D55176ACEA4A7E3A65FFA9FB10A7A1767199CF077CEE9F71532D67CD7C73C9F93CFC37CCDCC1FDEF50AAD46A504A650D298D597A3A9FA95C6C40CB71FA5E725 -In = E88AB086891693AA535CEB20E64C7AB97C7DD3548F3786339897A5F0C39031549CA870166E477743CCFBE016B4428D89738E426F5FFE81626137F17AECFF61B72DBEE2DC20961880CFE281DFAB5EE38B1921881450E16032DE5E4D55AD8D4FCA609721B0692BAC79BE5A06E177FE8C80C0C83519FB3347DE9F43D5561CB8107B9B5EDC -Out = 3398c1564c33aa4f800bf8e778e9ee941edf0c485fc9530cca45999328eaf867b9cd2045430428c14e6e64412dbeb842e932c97a3e032597e0f63a438034344b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778 +Out = D07713C005DE96DD21D2EB8BBECA66746EA51A31AE922A3E74864889540A48DB27D7E4C90311638B224BF0201B501891754848113C266108D0ADB13DB71909C7 -In = FD19E01A83EB6EC810B94582CB8FBFA2FCB992B53684FB748D2264F020D3B960CB1D6B8C348C2B54A9FCEA72330C2AAA9A24ECDB00C436ABC702361A82BB8828B85369B8C72ECE0082FE06557163899C2A0EFA466C33C04343A839417057399A63A3929BE1EE4805D6CE3E5D0D0967FE9004696A5663F4CAC9179006A2CEB75542D75D68 -Out = 802f77fbe223b8ea6e25a33d46ece9392770b72f894696a7266e0a386bd81cf407a304328cc8f5d46458ef54fb3cec57af735a1917aac1b9aa6f77f7c5828782 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576777879 +Out = 58983C21433D950CAA23E4BC18543B8E601C204318532152DAF5E159A0CD1480183D29285C05F129CB0CC3164687928086FFE380158DF1D394C6AC0D4288BCA8 -In = 59AE20B6F7E0B3C7A989AFB28324A40FCA25D8651CF1F46AE383EF6D8441587AA1C04C3E3BF88E8131CE6145CFB8973D961E8432B202FA5AF3E09D625FAAD825BC19DA9B5C6C20D02ABDA2FCC58B5BD3FE507BF201263F30543819510C12BC23E2DDB4F711D087A86EDB1B355313363A2DE996B891025E147036087401CCF3CA7815BF3C49 -Out = 12d3554d958a81df7cef444f54f8d98b346fa83f0626c9270917aa1530d2f54aa4364e194ed85373163f02e2684bd557aa571c125fa33c228e3e5a5a85f5514c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A +Out = 8100A8DC528D2B682AB4250801BA33F02A3E94C54DAC0AE1482AA21F51EF3A82F3807E6FACB0AEB05947BF7AA2ADCB034356F90FA4560EDE02201A37E411EC1A -In = 77EE804B9F3295AB2362798B72B0A1B2D3291DCEB8139896355830F34B3B328561531F8079B79A6E9980705150866402FDC176C05897E359A6CB1A7AB067383EB497182A7E5AEF7038E4C96D133B2782917417E391535B5E1B51F47D8ED7E4D4025FE98DC87B9C1622614BFF3D1029E68E372DE719803857CA52067CDDAAD958951CB2068CC6 -Out = 444f0f0862884742e65fa7271de03785f8d7f6226a3d402c31a25a8371b7f91e23507556ebee1303b10c77132404df09ccca3dcc2d70fd54678502bc1b79a8ed +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B +Out = 07025F1BB6C784F3FE49DE5C14B936A5ACACACAAB33F6AC4D0E00AB6A12483D6BEC00B4FE67C7CA5CC508C2A53EFB5BFA5398769D843FF0D9E8B14D36A01A77F -In = B771D5CEF5D1A41A93D15643D7181D2A2EF0A8E84D91812F20ED21F147BEF732BF3A60EF4067C3734B85BC8CD471780F10DC9E8291B58339A677B960218F71E793F2797AEA349406512829065D37BB55EA796FA4F56FD8896B49B2CD19B43215AD967C712B24E5032D065232E02C127409D2ED4146B9D75D763D52DB98D949D3B0FED6A8052FBB -Out = 9df2cd7dd4a7a7bd940086814e5ea4f7186be43193d3edfac14f1304b32f6b9cbf31fe86049cb7eca2e2bdc4cf00a62db34462223707afe3a5e2b9207d1964bd +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C +Out = BA6AEFD972B6186E027A76273A4A723321A3F580CFA894DA5A9CE8E721C828552C64DACEE3A7FD2D743B5C35AD0C8EFA71F8CE99BF96334710E2C2346E8F3C52 -In = B32D95B0B9AAD2A8816DE6D06D1F86008505BD8C14124F6E9A163B5A2ADE55F835D0EC3880EF50700D3B25E42CC0AF050CCD1BE5E555B23087E04D7BF9813622780C7313A1954F8740B6EE2D3F71F768DD417F520482BD3A08D4F222B4EE9DBD015447B33507DD50F3AB4247C5DE9A8ABD62A8DECEA01E3B87C8B927F5B08BEB37674C6F8E380C04 -Out = 3d6ed5a0b54a883baa3163fbe50accff279269579c2be16b16b149a545c13386da89fac372b2d119990b6d94ee930e50a947636627a508f1def392aefd209856 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D +Out = E0721E02517AEDFA4E7E9BA503E025FD46E714566DC889A84CBFE56A55DFBE2FC4938AC4120588335DEAC8EF3FA229ADC9647F54AD2E3472234F9B34EFC46543 -In = 04410E31082A47584B406F051398A6ABE74E4DA59BB6F85E6B49E8A1F7F2CA00DFBA5462C2CD2BFDE8B64FB21D70C083F11318B56A52D03B81CAC5EEC29EB31BD0078B6156786DA3D6D8C33098C5C47BB67AC64DB14165AF65B44544D806DDE5F487D5373C7F9792C299E9686B7E5821E7C8E2458315B996B5677D926DAC57B3F22DA873C601016A0D -Out = 8384ce6b3815a95b4a0c1938293286a74a9b689ad14118e431e925e30ae11426859b2c21020acd4dbd40943a0c149676e32062200f3f1dcfc76d4cb5c6946ec4 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E +Out = B6292669CCD38D5F01CAAE96BA272C76A879A45743AFA0725D83B9EBB26665B731F1848C52F11972B6644F554C064FA90780DBBBF3A89D4FC31F67DF3E5857EF -In = 8B81E9BADDE026F14D95C019977024C9E13DB7A5CD21F9E9FC491D716164BBACDC7060D882615D411438AEA056C340CDF977788F6E17D118DE55026855F93270472D1FD18B9E7E812BAE107E0DFDE7063301B71F6CFE4E225CAB3B232905A56E994F08EE2891BA922D49C3DAFEB75F7C69750CB67D822C96176C46BD8A29F1701373FB09A1A6E3C7158F -Out = b5f4876fff10cd88c17a38adefcb89073c46fa89c15ee1a858734e38d0c9c0c1b31803980bc7c3073f78661a6296120ca61e2c54f5e2f35b34c2b04da4627f58 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F +Out = 2319E3789C47E2DAA5FE807F61BEC2A1A6537FA03F19FF32E87EECBFD64B7E0E8CCFF439AC333B040F19B0C4DDD11A61E24AC1FE0F10A039806C5DCC0DA3D115 -In = FA6EED24DA6666A22208146B19A532C2EC9BA94F09F1DEF1E7FC13C399A48E41ACC2A589D099276296348F396253B57CB0E40291BD282773656B6E0D8BEA1CDA084A3738816A840485FCF3FB307F777FA5FEAC48695C2AF4769720258C77943FB4556C362D9CBA8BF103AEB9034BAA8EA8BFB9C4F8E6742CE0D52C49EA8E974F339612E830E9E7A9C29065 -Out = 6a931091dc886b54f112eaef7d40d7f121e79e88e2db66074a2bebabaeac5bb78e14c48ecce0b70d2fd4e030c8877c01bcdddc59a6c4525353bf46a452817a67 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F80 +Out = F59711D44A031D5F97A9413C065D1E614C417EDE998590325F49BAD2FD444D3E4418BE19AEC4E11449AC1A57207898BC57D76A1BCF3566292C20C683A5C4648F -In = 9BB4AF1B4F09C071CE3CAFA92E4EB73CE8A6F5D82A85733440368DEE4EB1CBC7B55AC150773B6FE47DBE036C45582ED67E23F4C74585DAB509DF1B83610564545642B2B1EC463E18048FC23477C6B2AA035594ECD33791AF6AF4CBC2A1166ABA8D628C57E707F0B0E8707CAF91CD44BDB915E0296E0190D56D33D8DDE10B5B60377838973C1D943C22ED335E -Out = 0b3a8cc2b3a81cec764cc0f00e367ba5d9824163f07b0d5b982bce034b130d47eecbc60bd6df29605128ee1b14b8d372ad530893178f7a682959a45ab4a6eff4 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F8081 +Out = DF0A9D0C212843A6A934E3902B2DD30D17FBA5F969D2030B12A546D8A6A45E80CF5635F071F0452E9C919275DA99BED51EB1173C1AF0518726B75B0EC3BAE2B5 -In = 2167F02118CC62043E9091A647CADBED95611A521FE0D64E8518F16C808AB297725598AE296880A773607A798F7C3CFCE80D251EBEC6885015F9ABF7EAABAE46798F82CB5926DE5C23F44A3F9F9534B3C6F405B5364C2F8A8BDC5CA49C749BED8CE4BA48897062AE8424CA6DDE5F55C0E42A95D1E292CA54FB46A84FBC9CD87F2D0C9E7448DE3043AE22FDD229 -Out = b24a228de29fb0f1ece8187b2d508d651771854f3b877f8a81aa26ec0b00f1fa2bb4f115f25e583ea0ee85cead412c1e01ff919571094335f80ed4734d04cb9a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182 +Out = A3EB6E6C7BF2FB8B28BFE8B15E15BB500F781ECC86F778C3A4E655FC5869BF2846A245D4E33B7B14436A17E63BE79B36655C226A50FFBC7124207B0202342DB5 -In = 94B7FA0BC1C44E949B1D7617D31B4720CBE7CA57C6FA4F4094D4761567E389ECC64F6968E4064DF70DF836A47D0C713336B5028B35930D29EB7A7F9A5AF9AD5CF441745BAEC9BB014CEEFF5A41BA5C1CE085FEB980BAB9CF79F2158E03EF7E63E29C38D7816A84D4F71E0F548B7FC316085AE38A060FF9B8DEC36F91AD9EBC0A5B6C338CBB8F6659D342A24368CF -Out = 40066735133f7a878a65f79b6571ab0b264765bc8cab19050c8bd9da5a7894b1aaf9ba80678151cb42fe3537c9374e272c03ebd410f6f0bf09a6bb4239204a01 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F80818283 +Out = 56D4CBCD070563426A017069425C2CD2AE540668287A5FB9DAC432EB8AB1A353A30F2FE1F40D83333AFE696A267795408A92FE7DA07A0C1814CF77F36E105EE8 -In = EA40E83CB18B3A242C1ECC6CCD0B7853A439DAB2C569CFC6DC38A19F5C90ACBF76AEF9EA3742FF3B54EF7D36EB7CE4FF1C9AB3BC119CFF6BE93C03E208783335C0AB8137BE5B10CDC66FF3F89A1BDDC6A1EED74F504CBE7290690BB295A872B9E3FE2CEE9E6C67C41DB8EFD7D863CF10F840FE618E7936DA3DCA5CA6DF933F24F6954BA0801A1294CD8D7E66DFAFEC -Out = 10973a1e03372fb6c881c301f596b29ab06a13cd51593e6ac0f01292188447e5209a3551cdadfe332e96a723420f8f87b442eb2bca3457782af30695a1025c96 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F8081828384 +Out = E59B9987D428B3EDA37D80ABDB16CD2B0AEF674C2B1DDA4432EA91EE6C935C684B48B4428A8CC740E579A30DEFF35A803013820DD23F14AE1D8413B5C8672AEC -In = 157D5B7E4507F66D9A267476D33831E7BB768D4D04CC3438DA12F9010263EA5FCAFBDE2579DB2F6B58F911D593D5F79FB05FE3596E3FA80FF2F761D1B0E57080055C118C53E53CDB63055261D7C9B2B39BD90ACC32520CBBDBDA2C4FD8856DBCEE173132A2679198DAF83007A9B5C51511AE49766C792A29520388444EBEFE28256FB33D4260439CBA73A9479EE00C63 -Out = ba80e9c38acb1c54a8b17e7f950da3c52a7a45045dd49c88a6df602e773badb2d9c01b34a72375f39bd89050b2a08b354e063919a4a9246e15765ba21796238d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485 +Out = CD9FCC99F99D4CC16D031900B2A736E1508DB4B586814E6345857F354A70CCECB1DF3B50A19ADAF43C278EFA423FF4BB6C523EC7FD7859B97B168A7EBFF8467C -In = 836B34B515476F613FE447A4E0C3F3B8F20910AC89A3977055C960D2D5D2B72BD8ACC715A9035321B86703A411DDE0466D58A59769672AA60AD587B8481DE4BBA552A1645779789501EC53D540B904821F32B0BD1855B04E4848F9F8CFE9EBD8911BE95781A759D7AD9724A7102DBE576776B7C632BC39B9B5E19057E226552A5994C1DBB3B5C7871A11F5537011044C53 -Out = e665414d7ed3753c22a27aafb13082c04e4389493f037a2c06dde2542fae979b8272f89e0aa54dae0ebf6cea2b33a0c2c7a7cf3b847b18def08fbd79973fc055 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F80818283848586 +Out = 0602185D8C3A78738B99164B8BC6FFB21C7DEBEBBF806372E0DA44D121545597B9C662A255DC31542CF995ECBE6A50FB5E6E0EE4EF240FE557EDED1188087E86 -In = CC7784A4912A7AB5AD3620AAB29BA87077CD3CB83636ADC9F3DC94F51EDF521B2161EF108F21A0A298557981C0E53CE6CED45BDF782C1EF200D29BAB81DD6460586964EDAB7CEBDBBEC75FD7925060F7DA2B853B2B089588FA0F8C16EC6498B14C55DCEE335CB3A91D698E4D393AB8E8EAC0825F8ADEBEEE196DF41205C011674E53426CAA453F8DE1CBB57932B0B741D4C6 -Out = 04e7f56171ed9b4759e30f8c614a0e125a0adf14b37ce8b2c7f2376632b7b615a704c01556f1bba7bdf141916f581792920bb66c33dbcd3650573a0ff7fc50ec +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F8081828384858687 +Out = C08AFA5B927BF08097AFC5FFF9CA4E7800125C1F52F2AF3553FA2B89E1E3015C4F87D5E0A48956AD31450B083DAD147FFB5EC03434A26830CF37D103AB50C5DA -In = 7639B461FFF270B2455AC1D1AFCE782944AEA5E9087EB4A39EB96BB5C3BAAF0E868C8526D3404F9405E79E77BFAC5FFB89BF1957B523E17D341D7323C302EA7083872DD5E8705694ACDDA36D5A1B895AAA16ECA6104C82688532C8BFE1790B5DC9F4EC5FE95BAED37E1D287BE710431F1E5E8EE105BC42ED37D74B1E55984BF1C09FE6A1FA13EF3B96FAEAED6A2A1950A12153 -Out = 341d8dff8a01677a86de518b8df9f82ef37316f6be7926f1cfed5bfe354e675a6bb6e269433fd41a735cf320db9c140dd61991db74fc30911592ab0bdcd61aa4 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788 +Out = 36F1E1C11D6EF6BC3B536D505D544A871522C5C2A253067EC9933B6EC25464DAF985525F5B9560A16D890259AC1BB5CC67C0C469CDE133DEF000EA1D686F4F5D -In = EB6513FC61B30CFBA58D4D7E80F94D14589090CF1D80B1DF2E68088DC6104959BA0D583D585E9578AB0AEC0CF36C48435EB52ED9AB4BBCE7A5ABE679C97AE2DBE35E8CC1D45B06DDA3CF418665C57CBEE4BBB47FA4CAF78F4EE656FEC237FE4EEBBAFA206E1EF2BD0EE4AE71BD0E9B2F54F91DAADF1FEBFD7032381D636B733DCB3BF76FB14E23AFF1F68ED3DBCF75C9B99C6F26 -Out = 2c99ba8948b6f25430bbd5bee47d09b9c8db23664b47a5fdccf0d7eb07334963fed07d8d596253a12371523344670c57b01ca431352a1736841aa0c1c078f990 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F80818283848586878889 +Out = BF2AB2E2470F5438C3B689E66E7686FFFA0CB1E1798AD3A86FF99075BF6138E33D9C0CE59AFB24AC67A02AF34428191A9A0A6041C07471B7C3B1A752D6FC0B8B -In = 1594D74BF5DDE444265D4C04DAD9721FF3E34CBF622DAF341FE16B96431F6C4DF1F760D34F296EB97D98D560AD5286FEC4DCE1724F20B54FD7DF51D4BF137ADD656C80546FB1BF516D62EE82BAA992910EF4CC18B70F3F8698276FCFB44E0EC546C2C39CFD8EE91034FF9303058B4252462F86C823EB15BF481E6B79CC3A02218595B3658E8B37382BD5048EAED5FD02C37944E73B -Out = 22d5d68e844ea3c92be236cc7a645a1dbfdfbab4b30324e01925b8a0f2ab32de2ad504d6e8fe90a5a0ab5525c9922b30f5025a7227ac484b6a94667ac2de6763 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A +Out = D400601F9728CCC4C92342D9787D8D28AB323AF375CA5624B4BB91D17271FBAE862E413BE73F1F68E615B8C5C391BE0DBD9144746EB339AD541547BA9C468A17 -In = 4CFA1278903026F66FEDD41374558BE1B585D03C5C55DAC94361DF286D4BD39C7CB8037ED3B267B07C346626449D0CC5B0DD2CF221F7E4C3449A4BE99985D2D5E67BFF2923357DDEAB5ABCB4619F3A3A57B2CF928A022EB27676C6CF805689004FCA4D41EA6C2D0A4789C7605F7BB838DD883B3AD3E6027E775BCF262881428099C7FFF95B14C095EA130E0B9938A5E22FC52650F591 -Out = 380f84511cda436cc951f43f3e2866516a5b4033888a2a9935b5766241aa797223a150423097b6ab4f830dbb595ad9640768559c2c63036acf31fe2d8600eb7b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B +Out = 79FE2FE157EB85A038ABB8EBBC647731D2C83F51B0AC6EE14AA284CB6A3549A4DCCEB300740A825F52F5FB30B03B8C4D8B0F4AA67A63F4A94E3303C4EDA4C02B -In = D3E65CB92CFA79662F6AF493D696A07CCF32AAADCCEFF06E73E8D9F6F909209E66715D6E978788C49EFB9087B170ECF3AA86D2D4D1A065AE0EFC8924F365D676B3CB9E2BEC918FD96D0B43DEE83727C9A93BF56CA2B2E59ADBA85696546A815067FC7A78039629D4948D157E7B0D826D1BF8E81237BAB7321312FDAA4D521744F988DB6FDF04549D0FDCA393D639C729AF716E9C8BBA48 -Out = 5a8c59a3774ab1b0c27db7325ee45c659f821caa8da4a1fcb0f145827c83b27e630473d5bcff87106b4d0f9d17b5826c6ed15a07d8ca7fc8114334aa9030b8ff +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C +Out = 75351313B52A8529298D8C186B1768666DCCA8595317D7A4816EB88C062020C0C8EFC554BB341B64688DB5CCAFC35F3C3CD09D6564B36D7B04A248E146980D4B -In = 842CC583504539622D7F71E7E31863A2B885C56A0BA62DB4C2A3F2FD12E79660DC7205CA29A0DC0A87DB4DC62EE47A41DB36B9DDB3293B9AC4BAAE7DF5C6E7201E17F717AB56E12CAD476BE49608AD2D50309E7D48D2D8DE4FA58AC3CFEAFEEE48C0A9EEC88498E3EFC51F54D300D828DDDCCB9D0B06DD021A29CF5CB5B2506915BEB8A11998B8B886E0F9B7A80E97D91A7D01270F9A7717 -Out = f57e7b8a9a00a8051ddce2ee84c5f52fc78db5f66a6df5389b520a91315c8b9cc5e6af092491dbd6d4efeab7356134a03c0602dc70b7fbeeff777dbed74bc5c9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D +Out = E3128B1D311D02179D7F25F97A5A8BEE2CC8C86303644FCD664E157D1FEF00F23E46F9A5E8E5C890CE565BB6ABD4302CE06469D52A5BD53E1C5A54D04649DC03 -In = 6C4B0A0719573E57248661E98FEBE326571F9A1CA813D3638531AE28B4860F23C3A3A8AC1C250034A660E2D71E16D3ACC4BF9CE215C6F15B1C0FC7E77D3D27157E66DA9CEEC9258F8F2BF9E02B4AC93793DD6E29E307EDE3695A0DF63CBDC0FC66FB770813EB149CA2A916911BEE4902C47C7802E69E405FE3C04CEB5522792A5503FA829F707272226621F7C488A7698C0D69AA561BE9F378 -Out = a90b61d18791179a62fbdcbede4933779cdf366610a9c963ebb0a65e9f39612c7fd2806c82e15f148cc6db49d716f1c811b25efbc791a03f08305d8a01e5ce07 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E +Out = C2382A72D2D3ACE9D5933D00B60827ED380CDA08D0BA5F6DD41E29EE6DBE8ECB9235F06BE95D83B6816A2FB7A5AD47035E8A4B69A4884B99E4BECE58CAB25D44 -In = 51B7DBB7CE2FFEB427A91CCFE5218FD40F9E0B7E24756D4C47CD55606008BDC27D16400933906FD9F30EFFDD4880022D081155342AF3FB6CD53672AB7FB5B3A3BCBE47BE1FD3A2278CAE8A5FD61C1433F7D350675DD21803746CADCA574130F01200024C6340AB0CC2CF74F2234669F34E9009EF2EB94823D62B31407F4BA46F1A1EEC41641E84D77727B59E746B8A671BEF936F05BE820759FA -Out = 5097ddf361b067e64f04cad31d1f291fcfa48fffec29cea580c35e7ce60cb7cb1743a54f95e1924e21632e2f42b2ea87f979b46e1d5f488731a7fb0b437acdbd +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F +Out = 6B1C69460BBD50AC2ED6F32E6E887CFED407D47DCF0AAA60387FE320D780BD03EAB6D7BAEB2A07D10CD552A300341354EA9A5F03183A623F92A2D4D9F00926AF -In = 83599D93F5561E821BD01A472386BC2FF4EFBD4AED60D5821E84AAE74D8071029810F5E286F8F17651CD27DA07B1EB4382F754CD1C95268783AD09220F5502840370D494BEB17124220F6AFCE91EC8A0F55231F9652433E5CE3489B727716CF4AEBA7DCDA20CD29AA9A859201253F948DD94395ABA9E3852BD1D60DDA7AE5DC045B283DA006E1CBAD83CC13292A315DB5553305C628DD091146597 -Out = 1df93508dd4e63ae1f12a9b1816dc6f68799e93450733cdc499152059836a4236425bbd816988e718c4fbd1280d4f6956fb07fce76387935fb5cdd43c84e1c77 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F90 +Out = 6CDA206C80CDC9C44BA990E0328C314F819B142D00630404C48C05DC76D1B00CE4D72FC6A48E1469DDEF609412C364820854214B4869AF090F00D3C1BA443E1B -In = 2BE9BF526C9D5A75D565DD11EF63B979D068659C7F026C08BEA4AF161D85A462D80E45040E91F4165C074C43AC661380311A8CBED59CC8E4C4518E80CD2C78AB1CABF66BFF83EAB3A80148550307310950D034A6286C93A1ECE8929E6385C5E3BB6EA8A7C0FB6D6332E320E71CC4EB462A2A62E2BFE08F0CCAD93E61BEDB5DD0B786A728AB666F07E0576D189C92BF9FB20DCA49AC2D3956D47385E2 -Out = ad2f0a8d29bc566abe21be38ac89c668220d58756ebd499df1acb1a2ef752618d17b1f0a8953d8721be9f3b5df62754bcd516dc9f2c7af055f1e6ebca3fdac15 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F9091 +Out = 7FFC8C26FBD6A0F7A609E6E1939F6A9EDF1B0B066641FB76C4F9602ED748D11602496B35355B1AA255850A509D2F8EE18C8F3E1D7DCBC37A136598F56A59ED17 -In = CA76D3A12595A817682617006848675547D3E8F50C2210F9AF906C0E7CE50B4460186FE70457A9E879E79FD4D1A688C70A347361C847BA0DD6AA52936EAF8E58A1BE2F5C1C704E20146D366AEB3853BED9DE9BEFE9569AC8AAEA37A9FB7139A1A1A7D5C748605A8DEFB297869EBEDD71D615A5DA23496D11E11ABBB126B206FA0A7797EE7DE117986012D0362DCEF775C2FE145ADA6BDA1CCB326BF644 -Out = a6425f3c2192e1e17681327ab1276c2bb34974763d2015808b849cb65ec92d6cd9fcce56c7657488c72ee5076463fe5cf6460ef05e7349d3d4f4f525bc18778c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192 +Out = 70DE1F08DD4E09D5FC151F17FC991A23ABFC05104290D50468882EFAF582B6EC2F14F577C0D68C3AD06626916E3C86E6DAAB6C53E5163E82B6BD0CE49FC0D8DF -In = F76B85DC67421025D64E93096D1D712B7BAF7FB001716F02D33B2160C2C882C310EF13A576B1C2D30EF8F78EF8D2F465007109AAD93F74CB9E7D7BEF7C9590E8AF3B267C89C15DB238138C45833C98CC4A471A7802723EF4C744A853CF80A0C2568DD4ED58A2C9644806F42104CEE53628E5BDF7B63B0B338E931E31B87C24B146C6D040605567CEEF5960DF9E022CB469D4C787F4CBA3C544A1AC91F95F -Out = 7597b9715900c996f69009f0a3d7ad036d370504213350da721a54cdaced01667d742b3d8dd0c1787af449a005336f75f855d1c3ed0a430c80a9670d4c51825a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F90919293 +Out = 4F81935756ED35EE2058EE0C6A6110D6FAC5CB6A4F46AA9411603F99965823B6DA4838276C5C06BC7880E376D92758369EE7305BCEC8D3CFD28CCABB7B4F0579 -In = 25B8C9C032EA6BCD733FFC8718FBB2A503A4EA8F71DEA1176189F694304F0FF68E862A8197B839957549EF243A5279FC2646BD4C009B6D1EDEBF24738197ABB4C992F6B1DC9BA891F570879ACCD5A6B18691A93C7D0A8D38F95B639C1DAEB48C4C2F15CCF5B9D508F8333C32DE78781B41850F261B855C4BEBCC125A380C54D501C5D3BD07E6B52102116088E53D76583B0161E2A58D0778F091206AABD5A1 -Out = 8996be65f722a5439fe070cd74a855b7d06263109d94245a44097b5463376e408750e22243aeede93248aacc078f7e9eee56efceb7c88d7d69cb13470b477585 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F9091929394 +Out = ABCB61CB3683D18F27AD527908ED2D32A0426CB7BB4BF18061903A7DC42E7E76F982382304D18AF8C80D91DD58DD47AF76F8E2C36E28AF2476B4BCCF82E89FDF -In = 21CFDC2A7CCB7F331B3D2EEFFF37E48AD9FA9C788C3F3C200E0173D99963E1CBCA93623B264E920394AE48BB4C3A5BB96FFBC8F0E53F30E22956ADABC2765F57FB761E147ECBF8567533DB6E50C8A1F894310A94EDF806DD8CA6A0E141C0FA7C9FAE6C6AE65F18C93A8529E6E5B553BF55F25BE2E80A9882BD37F145FECBEB3D447A3C4E46C21524CC55CDD62F521AB92A8BA72B897996C49BB273198B7B1C9E -Out = 3f02f63361b647fe629c76d35c656fd950aa2d065ad5e5df7dc73b896fe5692a723c7b24b0efb2ee356b8786de9c5b72aec47a0322765583a635932936a4eb95 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495 +Out = 02D261AD56A526331B643DD2186DE9A82E72A58223CD1E723686C53D869B83B94632B7B647AB2AFC0D522E29DA3A5615B741D82852E0DF41B66007DBCBA90543 -In = 4E452BA42127DCC956EF4F8F35DD68CB225FB73B5BC7E1EC5A898BBA2931563E74FAFF3B67314F241EC49F4A7061E3BD0213AE826BAB380F1F14FAAB8B0EFDDD5FD1BB49373853A08F30553D5A55CCBBB8153DE4704F29CA2BDEEF0419468E05DD51557CCC80C0A96190BBCC4D77ECFF21C66BDF486459D427F986410F883A80A5BCC32C20F0478BB9A97A126FC5F95451E40F292A4614930D054C851ACD019CCF -Out = d394c299b14b605334c28220e41cae3ddbc48c3c0b47790e49a95d89bd6270aa84c047592f9918c9b09d0a84978f8eaab289ca6e2c2b72e3cadb944be65e6a9d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F90919293949596 +Out = C5832741FA30C5436823015383D297FF4C4A5D7276C3F902122066E04BE5431B1A85FAF73B918434F9300963D1DEA9E8AC3924EF490226EDEEA5F743E410669F -In = FA85671DF7DADF99A6FFEE97A3AB9991671F5629195049880497487867A6C446B60087FAC9A0F2FCC8E3B24E97E42345B93B5F7D3691829D3F8CCD4BB36411B85FC2328EB0C51CB3151F70860AD3246CE0623A8DC8B3C49F958F8690F8E3860E71EB2B1479A5CEA0B3F8BEFD87ACAF5362435EAECCB52F38617BC6C5C2C6E269EAD1FBD69E941D4AD2012DA2C5B21BCFBF98E4A77AB2AF1F3FDA3233F046D38F1DC8 -Out = 91eb9d68328f1eb3356f88f676b7a5d979a7fac960e50f054d5e47f97f6b8cc7400c156a18931729b24ada19870d01a7d5c6aa173c55f4d95872f4013c8604c8 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F9091929394959697 +Out = CFAEAB268CD075A5A6AED515023A032D54F2F2FF733CE0CBC78DB51DB4504D675923F82746D6594606AD5D67734B11A67CC6A468C2032E43CA1A94C6273A985E -In = E90847AE6797FBC0B6B36D6E588C0A743D725788CA50B6D792352EA8294F5BA654A15366B8E1B288D84F5178240827975A763BC45C7B0430E8A559DF4488505E009C63DA994F1403F407958203CEBB6E37D89C94A5EACF6039A327F6C4DBBC7A2A307D976AA39E41AF6537243FC218DFA6AB4DD817B6A397DF5CA69107A9198799ED248641B63B42CB4C29BFDD7975AC96EDFC274AC562D0474C60347A078CE4C25E88 -Out = 32723cc296f37f04aa7605775c7d87b2ef624eb4b4086498de728895871b5eefa5cbd65e7e99ba2575dfeb2956d5dcef9fb8f2e51c6385ffed83c13fc2cf9e44 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798 +Out = 860850F92EB268272B67D133609BD64E34F61BF03F4C1738645C17FEC818465D7ECD2BE2907641130025FDA79470AB731646E7F69440E8367EA76AC4CEE8A1DF -In = F6D5C2B6C93954FC627602C00C4CA9A7D3ED12B27173F0B2C9B0E4A5939398A665E67E69D0B12FB7E4CEB253E8083D1CEB724AC07F009F094E42F2D6F2129489E846EAFF0700A8D4453EF453A3EDDC18F408C77A83275617FABC4EA3A2833AA73406C0E966276079D38E8E38539A70E194CC5513AAA457C699383FD1900B1E72BDFB835D1FD321B37BA80549B078A49EA08152869A918CA57F5B54ED71E4FD3AC5C06729 -Out = 882204d91182faf1ef31913eec0b4bdcc6d945d9f1ac8fc9258b664a2abc515b73145d555bbeccaf231cc8eb9dc1d1a9caa00e194f195e4c5cf5fd0167488b23 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F90919293949596979899 +Out = 84B154ED29BBEDEFA648286839046F4B5AA34430E2D67F7496E4C39F2C7EA78995F69E1292200016F16AC3B37700E6C7E7861AFC396B64A59A1DBF47A55C4BBC -In = CF8562B1BED89892D67DDAAF3DEEB28246456E972326DBCDB5CF3FB289ACA01E68DA5D59896E3A6165358B071B304D6AB3D018944BE5049D5E0E2BB819ACF67A6006111089E6767132D72DD85BEDDCBB2D64496DB0CC92955AB4C6234F1EEA24F2D51483F2E209E4589BF9519FAC51B4D061E801125E605F8093BB6997BC163D551596FE4AB7CFAE8FB9A90F6980480CE0C229FD1675409BD788354DAF316240CFE0AF93EB -Out = 6f9468be77f86685e8da9f38f8b66ef97ce3dc577d6948f64004ce508708abaa362dfd4ad96def78ad077a6a9854a97e64184485eff5f5ed02b23a8bd4875541 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A +Out = AEEEC260A5D8EFF5CCAB8B95DA435A63ED7A21EA7FC7559413FD617E33609F8C290E64BBACC528F6C080262288B0F0A3219BE223C991BEE92E72349593E67638 -In = 2ACE31ABB0A2E3267944D2F75E1559985DB7354C6E605F18DC8470423FCA30B7331D9B33C4A4326783D1CAAE1B4F07060EFF978E4746BF0C7E30CD61040BD5EC2746B29863EB7F103EBDA614C4291A805B6A4C8214230564A0557BC7102E0BD3ED23719252F7435D64D210EE2AAFC585BE903FA41E1968C50FD5D5367926DF7A05E3A42CF07E656FF92DE73B036CF8B19898C0CB34557C0C12C2D8B84E91181AF467BC75A9D1 -Out = 9ece7d544363b33dba4f8411ca98013568aecc739b4329d564cd73a085c2faf9191602905e3fcf9970dbfaf3645dfeb75ddc338a9272c6f40fa29b42213c87d9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B +Out = 8AD78A9F26601D127E8D2F2F976E63D19A054A17DCF59E0F013AB54A6887BBDFFDE7AAAE117E0FBF3271016595B9D9C712C01B2C53E9655A382BC4522E616645 -In = 0D8D09AED19F1013969CE5E7EB92F83A209AE76BE31C754844EA9116CEB39A22EBB6003017BBCF26555FA6624185187DB8F0CB3564B8B1C06BF685D47F3286EDA20B83358F599D2044BBF0583FAB8D78F854FE0A596183230C5EF8E54426750EAF2CC4E29D3BDD037E734D863C2BD9789B4C243096138F7672C232314EFFDFC6513427E2DA76916B5248933BE312EB5DDE4CF70804FB258AC5FB82D58D08177AC6F4756017FFF5 -Out = 329a5d4b30d16a061e4ecb01bc2bf587f847021770225e4dcb0588a33056ffdbc81a707d3514b6fa40ede1785201fb4677132e9ae86b1da05112802832440efc +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C +Out = 8934159DADE1AC74147DFA282C75954FCEF443EF25F80DFE9FB6EA633B8545111D08B34EF43FFF17026C7964F5DEAC6D2B3C29DACF2747F022DF5967DFDC1A0A -In = C3236B73DEB7662BF3F3DAA58F137B358BA610560EF7455785A9BEFDB035A066E90704F929BD9689CEF0CE3BDA5ACF4480BCEB8D09D10B098AD8500D9B6071DFC3A14AF6C77511D81E3AA8844986C3BEA6F469F9E02194C92868CD5F51646256798FF0424954C1434BDFED9FACB390B07D342E992936E0F88BFD0E884A0DDB679D0547CCDEC6384285A45429D115AC7D235A717242021D1DC35641F5F0A48E8445DBA58E6CB2C8EA -Out = 0543180b8f1ae2fb5187948c6c981260c1a8aa0691872cc1656209c9d38661d35e666226b4f75e253a183fc457d87d7a05f6edae7d13605ef5ed1ed7b9eb983c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D +Out = CD36DD0B240614CF2FA2B9E959679DCDD72EC0CD58A43DA3790A92F6CDEB9E1E795E478A0A47D371100D340C5CEDCDBBC9E68B3F460818E5BDFF7B4CDA4C2744 -In = B39FEB8283EADC63E8184B51DF5AE3FD41AAC8A963BB0BE1CD08AA5867D8D910C669221E73243360646F6553D1CA05A84E8DC0DE05B6419EC349CA994480193D01C92525F3FB3DCEFB08AFC6D26947BDBBFD85193F53B50609C6140905C53A6686B58E53A319A57B962331EDE98149AF3DE3118A819DA4D76706A0424B4E1D2910B0ED26AF61D150EBCB46595D4266A0BD7F651BA47D0C7F179CA28545007D92E8419D48FDFBD744CE -Out = d49f9d32a787d2335797af169953232574fb0ca3477df8088eaa79c5b720b81ff5e9bd2194b29d6dd7b0dd626d58ca850674e963169259cb9161dc5e50371609 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E +Out = 00DF4E099B807137A85990F49D3A94315E5A5F7F7A6076B303E96B056FB93800111F479628E2F8DB59AEB6AC70C3B61F51F9B46E80FFDEAE25EBDDB4AF6CB4EE -In = A983D54F503803E8C7999F4EDBBE82E9084F422143A932DDDDC47A17B0B7564A7F37A99D0786E99476428D29E29D3C197A72BFAB1342C12A0FC4787FD7017D7A6174049EA43B5779169EF7472BDBBD941DCB82FC73AAC45A8A94C9F2BD3477F61FD3B796F02A1B8264A214C6FEA74B7051B226C722099EC7883A462B83B6AFDD4009248B8A237F605FE5A08FE7D8B45321421EBBA67BD70A0B00DDBF94BAAB7F359D5D1EEA105F28DCFB -Out = ce4e1d9a08ae9f9db885daea741ffc56887d1bc8ddb98d0fc0b3695822b1ef75325ddb7a52236389dfc7e4857ecd409d9052f420b211a2578a251171c9c2ade1 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F +Out = 2B9C955E6CAED4B7C9E246B86F9A1726E810C59D126CEE66ED71BF015B83558A4B6D84D18DC3FF4620C2FFB722359FDEF85BA0D4E2D22ECBE0ED784F99AFE587 -In = E4D1C1897A0A866CE564635B74222F9696BF2C7F640DD78D7E2ACA66E1B61C642BB03EA7536AAE597811E9BF4A7B453EDE31F97B46A5F0EF51A071A2B3918DF16B152519AE3776F9F1EDAB4C2A377C3292E96408359D3613844D5EB393000283D5AD3401A318B12FD1474B8612F2BB50FB6A8B9E023A54D7DDE28C43D6D8854C8D9D1155935C199811DBFC87E9E0072E90EB88681CC7529714F8FB8A2C9D88567ADFB974EE205A9BF7B848 -Out = 0689f8ac424663aa21040c523afc9d1ea5d22f99efc166fbac64a19fe15acca2d45baa7a42347f1716d3703b4c97b055292a82fcf798d3d65e334bd0d70ec92d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0 +Out = 181DF0A261A2F7D29EA5A15772715105D450A4B6C236F699F462D60CA76487FEEDFC9F5EB92DF838E8FB5DC3694E84C5E0F4A10B761F506762BE052C745A6EE8 -In = B10C59723E3DCADD6D75DF87D0A1580E73133A9B7D00CB95EC19F5547027323BE75158B11F80B6E142C6A78531886D9047B08E551E75E6261E79785366D7024BD7CD9CF322D9BE7D57FB661069F2481C7BB759CD71B4B36CA2BC2DF6D3A328FAEBDB995A9794A8D72155ED551A1F87C80BF6059B43FC764900B18A1C2441F7487743CF84E565F61F8DD2ECE6B6CCC9444049197AAAF53E926FBEE3BFCA8BE588EC77F29D211BE89DE18B15F6 -Out = 30f8de9721ef84528998adc80280918fc40baa105e8f8b8dd07fdf0a6f801f4a071c990bf9d9f2a380b0ec2de7c4139e5a6c8394890c5ceb2430f098e4bc22c5 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1 +Out = 21FB203458BF3A7E9A80439F9A902899CD5DE0139DFD56F7110C9DEC8437B26BDA63DE2F565926D85EDB1D6C6825669743DD9992653D13979544D5DC8228BFAA -In = DB11F609BABA7B0CA634926B1DD539C8CBADA24967D7ADD4D9876F77C2D80C0F4DCEFBD7121548373582705CCA2495BD2A43716FE64ED26D059CFB566B3364BD49EE0717BDD9810DD14D8FAD80DBBDC4CAFB37CC60FB0FE2A80FB4541B8CA9D59DCE457738A9D3D8F641AF8C3FD6DA162DC16FC01AAC527A4A0255B4D231C0BE50F44F0DB0B713AF03D968FE7F0F61ED0824C55C4B5265548FEBD6AAD5C5EEDF63EFE793489C39B8FD29D104CE -Out = a092a14c6f5291c4711e47c32f576ed6074fbe52f7c1eed8692fc673569370b47ea4c67569421ce2bace72b5837628f4f0a1982cb784200a80659a54903ee63c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2 +Out = EF021F29C5FFB830E64B9AA9058DD660FD2FCB81C497A7E698BCFBF59DE5AD4A86FF93C10A4B9D1AE5774725F9072DCDE9E1F199BAB91F8BFF921864AA502EEE -In = BEBD4F1A84FC8B15E4452A54BD02D69E304B7F32616AADD90537937106AE4E28DE9D8AAB02D19BC3E2FDE1D651559E296453E4DBA94370A14DBBB2D1D4E2022302EE90E208321EFCD8528AD89E46DC839EA9DF618EA8394A6BFF308E7726BAE0C19BCD4BE52DA6258E2EF4E96AA21244429F49EF5CB486D7FF35CAC1BACB7E95711944BCCB2AB34700D42D1EB38B5D536B947348A458EDE3DC6BD6EC547B1B0CAE5B257BE36A7124E1060C170FFA -Out = b8d200a7129ab8659fab9e786489e7946fa70030ddfc41aea209fdb32172cd060014cd8c069c7eec83c44184221fd519acd11e35bc38797a2fb01b616433a789 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3 +Out = B3CFDA40526B7F1D37569BDFCDF911E5A6EFE6B2EC90A0454C47B2C046BF130FC3B352B34DF4813D48D33AB8E269B69B075676CB6D00A8DCF9E1F967EC191B2C -In = 5ACA56A03A13784BDC3289D9364F79E2A85C12276B49B92DB0ADAA4F206D5028F213F678C3510E111F9DC4C1C1F8B6ACB17A6413AA227607C515C62A733817BA5E762CC6748E7E0D6872C984D723C9BB3B117EB8963185300A80BFA65CDE495D70A46C44858605FCCBED086C2B45CEF963D33294DBE9706B13AF22F1B7C4CD5A001CFEC251FBA18E722C6E1C4B1166918B4F6F48A98B64B3C07FC86A6B17A6D0480AB79D4E6415B520F1C484D675B1 -Out = b2471140773a97d9b6f7d205ac703f1f6aab45ffd1c9e7fbb16e246027476eee2e789686ffd9456e905029b00d21510e86bda845364af1f7ab04e7a714e7f121 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4 +Out = B4C6C3B267071EEFB9C8C72E0E2B941293641F8673CB70C1CC26AD1E73CF141755860AD19B34C2F34ED35BB52EC4507CC1FE59047743A5F0C6FEBDE625E26091 -In = A5AAD0E4646A32C85CFCAC73F02FC5300F1982FABB2F2179E28303E447854094CDFC854310E5C0F60993CEFF54D84D6B46323D930ADB07C17599B35B505F09E784BCA5985E0172257797FB53649E2E9723EFD16865C31B5C3D5113B58BB0BFC8920FABDDA086D7537E66D709D050BD14D0C960873F156FAD5B3D3840CDFCDC9BE6AF519DB262A27F40896AB25CC39F96984D650611C0D5A3080D5B3A1BF186ABD42956588B3B58CD948970D298776060 -Out = 2cc78153d1d7c42eaa1e846ab73463ac2c36aff5b6e8b56b82060d46792014a0625ac81064124d6d67f70c106ea557764ab2e798e85530d88b4fa4e996c446fb +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5 +Out = 57A34F2BCCA60D4B85103B830C9D7952A416BE5263AE429C9E5E53FE8590A8F78EC65A51109EA85DCDF7B6223F9F2B340539FAD81923DBF8EDABF95129E4DFF6 -In = 06CBBE67E94A978203EAD6C057A1A5B098478B4B4CBEF5A97E93C8E42F5572713575FC2A884531D7622F8F879387A859A80F10EF02708CD8F7413AB385AFC357678B9578C0EBF641EF076A1A30F1F75379E9DCB2A885BDD295905EE80C0168A62A9597D10CF12DD2D8CEE46645C7E5A141F6E0E23AA482ABE5661C16E69EF1E28371E2E236C359BA4E92C25626A7B7FF13F6EA4AE906E1CFE163E91719B1F750A96CBDE5FBC953D9E576CD216AFC90323A -Out = 9ff76839321b6c006fb039c72d28592f25d83180588a3191d9321f5ac1e3f0799e9a77023b0ecaff944e0100518a12c9a99ab5f2d9132a81d0cbc7fa83f7b87c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6 +Out = 9CF46662FCD61A232277B685663B8B5DA832DFD9A3B8CCFEEC993EC6AC415AD07E048ADFE414DF272770DBA867DA5C1224C6FD0AA0C2187D426AC647E9887361 -In = F1C528CF7739874707D4D8AD5B98F7C77169DE0B57188DF233B2DC8A5B31EDA5DB4291DD9F68E6BAD37B8D7F6C9C0044B3BF74BBC3D7D1798E138709B0D75E7C593D3CCCDC1B20C7174B4E692ADD820ACE262D45CCFAE2077E878796347168060A162ECCA8C38C1A88350BD63BB539134F700FD4ADDD5959E255337DAA06BC86358FABCBEFDFB5BC889783D843C08AADC6C4F6C36F65F156E851C9A0F917E4A367B5AD93D874812A1DE6A7B93CD53AD97232 -Out = fa3fe5b61e8954aa79305a926aa4a4ab88ada3a4e889b922e2415eef742dc1d2d463ee438cf9f9f5e80f0513245804177499bd8e98eae749765e5217caba9ad6 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7 +Out = 5CE1042AB4D542C2F9EE9D17262AF8164098935BEF173D0E18489B04841746CD2F2DF866BD7DA6E5EF9024C648023EC723AB9C62FD80285739D84F15D2AB515A -In = 9D9F3A7ECD51B41F6572FD0D0881E30390DFB780991DAE7DB3B47619134718E6F987810E542619DFAA7B505C76B7350C6432D8BF1CFEBDF1069B90A35F0D04CBDF130B0DFC7875F4A4E62CDB8E525AADD7CE842520A482AC18F09442D78305FE85A74E39E760A4837482ED2F437DD13B2EC1042AFCF9DECDC3E877E50FF4106AD10A525230D11920324A81094DA31DEAB6476AA42F20C84843CFC1C58545EE80352BDD3740DD6A16792AE2D86F11641BB717C2 -Out = d206aaf6ceb8608420c8eb28adaffec428402550cf7730dd2dadbb233ca4da6c34191ebeeb1c38e07fb18dc4eca8586464671fcad2c78c71e68b72f65e711721 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8 +Out = 8488396BD4A8729B7A473178F232DADF3F0F8E22678BA5A43E041E72DA1E2CF82194C307207A54CB8156293339EAEC693FF66BFCD5EFC65E95E4ECAF54530ABD -In = 5179888724819FBAD3AFA927D3577796660E6A81C52D98E9303261D5A4A83232F6F758934D50AA83FF9E20A5926DFEBAAC49529D006EB923C5AE5048ED544EC471ED7191EDF46363383824F915769B3E688094C682B02151E5EE01E510B431C8865AFF8B6B6F2F59CB6D129DA79E97C6D2B8FA6C6DA3F603199D2D1BCAB547682A81CD6CF65F6551121391D78BCC23B5BD0E922EC6D8BF97C952E84DD28AEF909ABA31EDB903B28FBFC33B7703CD996215A11238 -Out = da68e6936d6a159cfd72c8e639f976b1838ded757adc2454c7f5a2ed6eb17dca07dd5bc3f91fd227d1e68bf4f90ac223debac5a1c4838f15731d58965ad05bf6 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9 +Out = F598DA901C3835BCA560779037DFDE9F0C51DC61C0B760FC1522D7B470EE63F5BDC6498476E86049AD86E4E21AF2854A984CC905427D2F17F66B1F41C3DA6F61 -In = 576EF3520D30B7A4899B8C0D5E359E45C5189ADD100E43BE429A02FB3DE5FF4F8FD0E79D9663ACCA72CD29C94582B19292A557C5B1315297D168FBB54E9E2ECD13809C2B5FCE998EDC6570545E1499DBE7FB74D47CD7F35823B212B05BF3F5A79CAA34224FDD670D335FCB106F5D92C3946F44D3AFCBAE2E41AC554D8E6759F332B76BE89A0324AA12C5482D1EA3EE89DED4936F3E3C080436F539FA137E74C6D3389BDF5A45074C47BC7B20B0948407A66D855E2F -Out = ee4a75df2fc3884053d0227eb2aeb9ad9fbd09755b7747b718a6d2de3d07a4e7f60afe565e8fb00693d406e0094839ba8fc04ee4cb9e9f5e1c385569ee06dd15 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AA +Out = 5F93269798CF02132107337660A8D7A177354C0212EB93E555E7C37A08AEF3D8DCE01217011CD965C04DD2C105F2E2B6CAE5E4E6BCAF09DFBEE3E0A6A6357C37 -In = 0DF2152FA4F4357C8741529DD77E783925D3D76E95BAFA2B542A2C33F3D1D117D159CF473F82310356FEE4C90A9E505E70F8F24859656368BA09381FA245EB6C3D763F3093F0C89B972E66B53D59406D9F01AEA07F8B3B615CAC4EE4D05F542E7D0DAB45D67CCCCD3A606CCBEB31EA1FA7005BA07176E60DAB7D78F6810EF086F42F08E595F0EC217372B98970CC6321576D92CE38F7C397A403BADA1548D205C343AC09DECA86325373C3B76D9F32028FEA8EB32515 -Out = 28a733099375f0e6792933ad42dd3f8f70f925f501531072469418eb846227b48a4d293b52efb92913982f1af5a5df4e003a5c65dd0b2ed9d0b16926502da70e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAAB +Out = 0ECF581D47BAC9230986FAABD70C2F5B80E91066F0EC55A842937882286D2CA007BB4E973B0B091D52167FF7C4009C7AB4AD38FFF1DCEACDB7BE81EF4A452952 -In = 3E15350D87D6EBB5C8AD99D42515CFE17980933C7A8F6B8BBBF0A63728CEFAAD2052623C0BD5931839112A48633FB3C2004E0749C87A41B26A8B48945539D1FF41A4B269462FD199BFECD45374756F55A9116E92093AC99451AEFB2AF9FD32D6D7F5FBC7F7A540D5097C096EBC3B3A721541DE073A1CC02F7FB0FB1B9327FB0B1218CA49C9487AB5396622A13AE546C97ABDEF6B56380DDA7012A8384091B6656D0AB272D363CEA78163FF765CDD13AB1738B940D16CAE -Out = b21a3d49db6505650b123499679abe41b9812e81e18ccda59fca58c83bfa57dbc94fa7f5cb8a8f77993d43422c47efea12ccd394709a632a4f6ae31dd022eec2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABAC +Out = 5AECA8ABE1528582B2A307B4009585498A3D467CA6101CB0C5126F9976056E9FFC123CC20C302B2A737F492C75D21F01512C90CA0541DFA56E950A321DCB28D8 -In = C38D6B0B757CB552BE40940ECE0009EF3B0B59307C1451686F1A22702922800D58BCE7A636C1727EE547C01B214779E898FC0E560F8AE7F61BEF4D75EAA696B921FD6B735D171535E9EDD267C192B99880C87997711002009095D8A7A437E258104A41A505E5EF71E5613DDD2008195F0C574E6BA3FE40099CFA116E5F1A2FA8A6DA04BADCB4E2D5D0DE31FDC4800891C45781A0AAC7C907B56D631FCA5CE8B2CDE620D11D1777ED9FA603541DE794DDC5758FCD5FAD78C0 -Out = f6f20ccdadd50a694015ea045488a73d4965b93309aa14a6dbd24feee4255b03dc88909b277886a073317e99e404f28d8b86f65e2447b81d8c3b8f38ecbf41e0 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACAD +Out = 732FBF8F1CB2B8329263EDE27858FE46F8D3354D376BCDA0548E7CE1FA9DD11F85EB661FE950B543AA635CA4D3F04EDE5B32D6B656E5CE1C44D35C4A6C56CFF8 -In = 8D2DE3F0B37A6385C90739805B170057F091CD0C7A0BC951540F26A5A75B3E694631BB64C7635EED316F51318E9D8DE13C70A2ABA04A14836855F35E480528B776D0A1E8A23B547C8B8D6A0D09B241D3BE9377160CCA4E6793D00A515DC2992CB7FC741DACA171431DA99CCE6F7789F129E2AC5CF65B40D703035CD2185BB936C82002DAF8CBC27A7A9E554B06196630446A6F0A14BA155ED26D95BD627B7205C072D02B60DB0FD7E49EA058C2E0BA202DAFF0DE91E845CF79 -Out = aa7f13cd4751f9bbb7ee58a19ffdaebef8b64821ba610f211efe2e79adb27adb0523cb4c8acda0c81ed010280253f6db511414cb2dbfc460b1632e9821a2f8ae +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE +Out = D5E938735D63788C80100AEFD18648D18CF272F69F20FF24CFE2895C088AD08B0104DA1672A4EB26FC52545CC7D7A01B266CF546C403C45BD129EB41BDD9200B -In = C464BBDAD275C50DCD983B65AD1019B9FF85A1E71C807F3204BB2C921DC31FBCD8C5FC45868AE9EF85B6C9B83BBA2A5A822201ED68586EC5EC27FB2857A5D1A2D09D09115F22DCC39FE61F5E1BA0FF6E8B4ACB4C6DA748BE7F3F0839739394FF7FA8E39F7F7E84A33C3866875C01BCB1263C9405D91908E9E0B50E7459FABB63D8C6BBB73D8E3483C099B55BC30FF092FF68B6ADEDFD477D63570C9F5515847F36E24BA0B705557130CEC57EBAD1D0B31A378E91894EE26E3A04 -Out = 60d5d5fa5fe6ca648d8b2908fa3ab578f6b2c230b7351a5ab675ef12b2f3a02f6b6e7de8b674196acbeeec79a19334e99750a00064156710db4524ff42ae29e3 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAF +Out = 65A245B49352EE297D91AF8C8BE00528AC6E046DD83AC7BD465A98816DD68F3E00E1AE8F895327A7E9A8C9326598379A29C9FC91EC0C6EEF08F3E2B216C11008 -In = 8B8D68BB8A75732FE272815A68A1C9C5AA31B41DEDC8493E76525D1D013D33CEBD9E21A5BB95DB2616976A8C07FCF411F5F6BC6F7E0B57ACA78CC2790A6F9B898858AC9C79B165FF24E66677531E39F572BE5D81EB3264524181115F32780257BFB9AEEC6AF12AF28E587CAC068A1A2953B59AD680F4C245B2E3EC36F59940D37E1D3DB38E13EDB29B5C0F404F6FF87F80FC8BE7A225FF22FBB9C8B6B1D7330C57840D24BC75B06B80D30DAD6806544D510AF6C4785E823AC3E0B8 -Out = 589095b8ca7edcfa527698990e121b3a1b8ae0d4d44003282e9bf4632a54919cf27dfb625885236eab08b26be320fb4a51204057b565fc8d9cbffd1d320044dd +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0 +Out = C95654B63019130AB45DD0FB4941B98AEB3AF2A123913ECA2CE99B3E97410A7BF8661CC7FBAA2BC1CF2B13113B1ED40A0118B88E5FFFC3542759EA007ED4C58D -In = 6B018710446F368E7421F1BC0CCF562D9C1843846BC8D98D1C9BF7D9D6FCB48BFC3BF83B36D44C4FA93430AF75CD190BDE36A7F92F867F58A803900DF8018150384D85D82132F123006AC2AEBA58E02A037FE6AFBD65ECA7C44977DD3DC74F48B6E7A1BFD5CC4DCF24E4D52E92BD4455848E4928B0EAC8B7476FE3CC03E862AA4DFF4470DBFED6DE48E410F25096487ECFC32A27277F3F5023B2725ADE461B1355889554A8836C9CF53BD767F5737D55184EEA1AB3F53EDD0976C485 -Out = f5099f5a3f18e8c27057aa1e1eea86074f4ed07ccb550819a724c9c0e4b968dcdfde232ac65062061529f4be185e14fc1544e0209beb7d06757a4d3ea63f65f4 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1 +Out = 1EB262F38FA494431F017DAD44C0DFB69324AC032F04B657FC91A88647BB74760F24E7C956514F0CF002990B182C1642B9B2426E96A61187E4E012F00E217D84 -In = C9534A24714BD4BE37C88A3DA1082EDA7CABD154C309D7BD670DCCD95AA535594463058A29F79031D6ECAA9F675D1211E9359BE82669A79C855EA8D89DD38C2C761DDD0EC0CE9E97597432E9A1BEAE062CDD71EDFDFD464119BE9E69D18A7A7FD7CE0E2106F0C8B0ABF4715E2CA48EF9F454DC203C96656653B727083513F8EFB86E49C513BB758B3B052FE21F1C05BB33C37129D6CC81F1AEF6ADC45B0E8827A830FE545CF57D0955802C117D23CCB55EA28F95C0D8C2F9C5A242B33F -Out = 9759012c49caed45d31e60db814e32b38330d2d354527079fa04a34a7c7499ee4d8358efe190ba53650ac132b63404df3afa3b557abdf64687959a7ac83bf260 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2 +Out = 3B955AEEBFA5151AC1AB8E3F5CC1E3767084C842A575D36269836E97353D41622B731DDDCD5F269550A3A5B87BE1E90326340B6E0E62555815D9600597AC6EF9 -In = 07906C87297B867ABF4576E9F3CC7F82F22B154AFCBF293B9319F1B0584DA6A40C27B32E0B1B7F412C4F1B82480E70A9235B12EC27090A5A33175A2BB28D8ADC475CEFE33F7803F8CE27967217381F02E67A3B4F84A71F1C5228E0C2AD971373F6F672624FCEA8D1A9F85170FAD30FA0BBD25035C3B41A6175D467998BD1215F6F3866F53847F9CF68EF3E2FBB54BC994DE2302B829C5EEA68EC441FCBAFD7D16AE4FE9FFF98BF00E5BC2AD54DD91FF9FDA4DD77B6C754A91955D1FBAAD0 -Out = cdb18a741a36b22d478bfe58320d114f31759b47b230ec49269352c5c4a9b7df6e75bc500b525bda50f9c971e6073b835c202b9e7888a61555b6d6073aff51b1 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3 +Out = 68289F6605473BA0E4F241BAF7477A9885426A858F19EF2A18B0D40EF8E41282ED5526B519799E270F13881327918278755711071D8511FE963E3B5606AA3716 -In = 588E94B9054ABC2189DF69B8BA34341B77CDD528E7860E5DEFCAA79B0C9A452AD4B82AA306BE84536EB7CEDCBE058D7B84A6AEF826B028B8A0271B69AC3605A9635EA9F5EA0AA700F3EB7835BC54611B922964300C953EFE7491E3677C2CEBE0822E956CD16433B02C68C4A23252C3F9E151A416B4963257B783E038F6B4D5C9F110F871652C7A649A7BCEDCBCCC6F2D0725BB903CC196BA76C76AA9F10A190B1D1168993BAA9FFC96A1655216773458BEC72B0E39C9F2C121378FEAB4E76A -Out = 4964da1a5bd8ec90620a08109a0705f36ac854084e19f0603c56f5631012e7a468ccb4c75943c886852224f7eaf78d488b4ba1081fbc9710f76f883171f39409 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4 +Out = 80A33787542612C38F6BCD7CD86CAB460227509B1CBAD5EC408A91413D51155A0476DADBF3A2518E4A6E77CC346622E347A469BF8BAA5F04EB2D98705355D063 -In = 08959A7E4BAAE874928813364071194E2939772F20DB7C3157078987C557C2A6D5ABE68D520EEF3DC491692E1E21BCD880ADEBF63BB4213B50897FA005256ED41B5690F78F52855C8D9168A4B666FCE2DA2B456D7A7E7C17AB5F2FB1EE90B79E698712E963715983FD07641AE4B4E9DC73203FAC1AE11FA1F8C7941FCC82EAB247ADDB56E2638447E9D609E610B60CE086656AAEBF1DA3C8A231D7D94E2FD0AFE46B391FF14A72EAEB3F44AD4DF85866DEF43D4781A0B3578BC996C87970B132 -Out = e81b4e894a4accf2c5cfd9fa1e826d5554c7dd68073e97f2408592d697289ee7c3504960108ce44d3ae067eb4f710b6e01fea7688ea8592aabf127982414624f +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5 +Out = 34629BC6D831391C4CDF8AF1B4B7B6B8E8EE17CF98C70E5DD586CD99F14B11DF945166236A9571E6D591BB83EE4D164D46F6B9D8EF86FF865A81BFB91B00424B -In = CB2A234F45E2ECD5863895A451D389A369AAB99CFEF0D5C9FFCA1E6E63F763B5C14FB9B478313C8E8C0EFEB3AC9500CF5FD93791B789E67EAC12FD038E2547CC8E0FC9DB591F33A1E4907C64A922DDA23EC9827310B306098554A4A78F050262DB5B545B159E1FF1DCA6EB734B872343B842C57EAFCFDA8405EEDBB48EF32E99696D135979235C3A05364E371C2D76F1902F1D83146DF9495C0A6C57D7BF9EE77E80F9787AEE27BE1FE126CDC9EF893A4A7DCBBC367E40FE4E1EE90B42EA25AF01 -Out = 229529601c476f05eae23c0560fa94c7a16e530137437f4167c8de6d8ee71f73d6714ca3920ed1e4f797e7c0e73a07e8f9c077e5cf22e55cc2dae7049db237c4 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6 +Out = 8B7CC339163863BB4383E542B0EF0E7CF36B84AD932CDF5A80419EC9AD692E7A7E784D2C7CB3796A18B8F800035F3AA06C824100611120A7BDEB35618CCB81B7 -In = D16BEADF02AB1D4DC6F88B8C4554C51E866DF830B89C06E786A5F8757E8909310AF51C840EFE8D20B35331F4355D80F73295974653DDD620CDDE4730FB6C8D0D2DCB2B45D92D4FBDB567C0A3E86BD1A8A795AF26FBF29FC6C65941CDDB090FF7CD230AC5268AB4606FCCBA9EDED0A2B5D014EE0C34F0B2881AC036E24E151BE89EEB6CD9A7A790AFCCFF234D7CB11B99EBF58CD0C589F20BDAC4F9F0E28F75E3E04E5B3DEBCE607A496D848D67FA7B49132C71B878FD5557E082A18ECA1FBDA94D4B -Out = 435031f471e8d76fc7da2500dce358f11e94ad6574d4cfadae8d4a84ae09d3ab81bcc5f8ebf69102e174d827ecac0e3303851a5828ea6881e3e7977121a06f2c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7 +Out = 4F084E4939DD5A7F5A658FAD58A18A15C25C32EC1C7FD5C5C6C3E892B3971AEAAC308304EF17B1C47239EA4BB398B3FD6D4528D8DE8E768AE0F1A5A5C6B5C297 -In = 8F65F6BC59A85705016E2BAE7FE57980DE3127E5AB275F573D334F73F8603106EC3553016608EF2DD6E69B24BE0B7113BF6A760BA6E9CE1C48F9E186012CF96A1D4849D75DF5BB8315387FD78E9E153E76F8BA7EC6C8849810F59FB4BB9B004318210B37F1299526866F44059E017E22E96CBE418699D014C6EA01C9F0038B10299884DBEC3199BB05ADC94E955A1533219C1115FED0E5F21228B071F40DD57C4240D98D37B73E412FE0FA4703120D7C0C67972ED233E5DEB300A22605472FA3A3BA86 -Out = 569fabe8d56b700d46cea867bdf5514c50201442b8005ed9be3b1603431a33ccd2f8a79aac4ecc6990ec4acf8c2522c8533295aa8270dc53834bb34948926944 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8 +Out = 48F407A1AF5B8009B2051742E8CF5CD5656669E7D722EE8E7BD202060849442168D8FACC117C012BFB7BF449D99BEFFF6A34AEA203F1D8D352722BE5014EC818 -In = 84891E52E0D451813210C3FD635B39A03A6B7A7317B221A7ABC270DFA946C42669AACBBBDF801E1584F330E28C729847EA14152BD637B3D0F2B38B4BD5BF9C791C58806281103A3EABBAEDE5E711E539E6A8B2CF297CF351C078B4FA8F7F35CF61BEBF8814BF248A01D41E86C5715EA40C63F7375379A7EB1D78F27622FB468AB784AAABA4E534A6DFD1DF6FA15511341E725ED2E87F98737CCB7B6A6DFAE416477472B046BF1811187D151BFA9F7B2BF9ACDB23A3BE507CDF14CFDF517D2CB5FB9E4AB6 -Out = 60e7f4ede39a61b77187b29ed22247a9f121ac7893fa0e16bbf39354898f2eff333de43720819046fb958853da2df6d94ef6b11cf94d8d103886d1c74c3ff4c4 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9 +Out = A6AA82CD1E426F9A73BFA39A29037876114655B8C22D6D3FF8B638AE7DEA6B17843E09E52EB66FA1E475E4A8A3DE429B7D0F4A776FCB8BDC9B9FEDE7D52E815F -In = FDD7A9433A3B4AFABD7A3A5E3457E56DEBF78E84B7A0B0CA0E8C6D53BD0C2DAE31B2700C6128334F43981BE3B213B1D7A118D59C7E6B6493A86F866A1635C12859CFB9AD17460A77B4522A5C1883C3D6ACC86E6162667EC414E9A104AA892053A2B1D72165A855BACD8FAF8034A5DD9B716F47A0818C09BB6BAF22AA503C06B4CA261F557761989D2AFBD88B6A678AD128AF68672107D0F1FC73C5CA740459297B3292B281E93BCEB761BDE7221C3A55708E5EC84472CDDCAA84ECF23723CC0991355C6280 -Out = b730bb918328578ede7baaeca9a965436bddc3839d9947223420cf77bd08a1b33b4753efa591e1fbbc5061df84424f7449adfd55766fd43d1f4ad3af05e506c7 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BA +Out = 5817027D6BDD00C5DD10AC593CD560372270775A18526D7E6F13872A2E20EAB664625BE7168AC4BD7C9E0CE7FC4099E0F48442E2C767191C6E1284E9B2CCEA8C -In = 70A40BFBEF92277A1AAD72F6B79D0177197C4EBD432668CFEC05D099ACCB651062B5DFF156C0B27336687A94B26679CFDD9DAF7AD204338DD9C4D14114033A5C225BD11F217B5F4732DA167EE3F939262D4043FC9CBA92303B7B5E96AEA12ADDA64859DF4B86E9EE0B58E39091E6B188B408AC94E1294A8911245EE361E60E601EFF58D1D37639F3753BEC80EBB4EFDE25817436076623FC65415FE51D1B0280366D12C554D86743F3C3B6572E400361A60726131441BA493A83FBE9AFDA90F7AF1AE717238D -Out = 0c6013aa66c6e04a7bf5c122c197efbb1449fcfe0f37d251b0c994f7b06e80f4b7e4c3587d8457f227c898aa9e4986bf6ff405194bd7b8ddd9dc7ee4af2ede63 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABB +Out = 08E41028340A45C74E4052B3A8D6389E22E043A1ADAB5E28D97619450D723469B620CAA519B81C14523854F619FD3027E3847BD03276E60604A80DDB4DE876D6 -In = 74356E449F4BF8644F77B14F4D67CB6BD9C1F5AE357621D5B8147E562B65C66585CAF2E491B48529A01A34D226D436959153815380D5689E30B35357CDAC6E08D3F2B0E88E200600D62BD9F5EAF488DF86A4470EA227006182E44809009868C4C280C43D7D64A5268FA719074960087B3A6ABC837882F882C837834535929389A12B2C78187E2EA07EF8B8EEF27DC85002C3AE35F1A50BEE6A1C48BA7E175F3316670B27983472AA6A61EED0A683A39EE323080620EA44A9F74411AE5CE99030528F9AB49C79F2 -Out = e7a8f1356afd6e6c19d62794e9e33765f152ae9ff36e7f1f67ecfa100fd285837475c78b1c2a7b2e6e400bb6442be0e669e177718a474aba87b8378c683fe01d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBC +Out = 130B8420537EB07D72ABDA07C85ACBD8B9A44F16321DD0422145F809673D30F2B5321326E2BFF317EF3FEF983C51C4F8AB24A325D298E34AFCE569A82555774C -In = 8C3798E51BC68482D7337D3ABB75DC9FFE860714A9AD73551E120059860DDE24AB87327222B64CF774415A70F724CDF270DE3FE47DDA07B61C9EF2A3551F45A5584860248FABDE676E1CD75F6355AA3EAEABE3B51DC813D9FB2EAA4F0F1D9F834D7CAD9C7C695AE84B329385BC0BEF895B9F1EDF44A03D4B410CC23A79A6B62E4F346A5E8DD851C2857995DDBF5B2D717AEB847310E1F6A46AC3D26A7F9B44985AF656D2B7C9406E8A9E8F47DCB4EF6B83CAACF9AEFB6118BFCFF7E44BEF6937EBDDC89186839B77 -Out = 4ee1a6af6cd61378b0d96c8f6b7cf6ea30fb075a30356d8ae0638eb968b78e21a5b55c74176eb33d7ba10548a1c338c0a831827b4e4fcf900010df71f1fce426 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBD +Out = AC49B844AFAA012E31C474CA263648844FD2F6307992C2F752ACA02C3828965175794DEEE2D2EE95C61CD284F6B5A2D75E2EF2B29EE8149E77FB81447B2FD04B -In = FA56BF730C4F8395875189C10C4FB251605757A8FECC31F9737E3C2503B02608E6731E85D7A38393C67DE516B85304824BFB135E33BF22B3A23B913BF6ACD2B7AB85198B8187B2BCD454D5E3318CACB32FD6261C31AE7F6C54EF6A7A2A4C9F3ECB81CE3555D4F0AD466DD4C108A90399D70041997C3B25345A9653F3C9A6711AB1B91D6A9D2216442DA2C973CBD685EE7643BFD77327A2F7AE9CB283620A08716DFB462E5C1D65432CA9D56A90E811443CD1ECB8F0DE179C9CB48BA4F6FEC360C66F252F6E64EDC96B -Out = d533b61769ac37b2ba659fefb80b07da679fb7bb9ba7c007e02e5879ec4622fb4363a71388460e7e302c19da0632fcfa83d63fcb53a52cc77470f360a1efdb5b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBE +Out = B9D7CA81CC60BB9578E44024E5A0A0BE80F27336A6A9F4E53DF3999CB191280B090E2AC2D29C5BAAD9D71415BDC129E69AA2667AF6A7FD5E189FCCDCEE817340 -In = B6134F9C3E91DD8000740D009DD806240811D51AB1546A974BCB18D344642BAA5CD5903AF84D58EC5BA17301D5EC0F10CCD0509CBB3FD3FFF9172D193AF0F782252FD1338C7244D40E0E42362275B22D01C4C3389F19DD69BDF958EBE28E31A4FFE2B5F18A87831CFB7095F58A87C9FA21DB72BA269379B2DC2384B3DA953C7925761FED324620ACEA435E52B424A7723F6A2357374157A34CD8252351C25A1B232826CEFE1BD3E70FFC15A31E7C0598219D7F00436294D11891B82497BC78AA5363892A2495DF8C1EEF -Out = 3ab1a381e0b14b91c3590fa485ff2648e80255cabe6b3e390352b8be55df4efb50d4cc06cff8e813581266cb85b6897925c7065507a09a9c1cf666876d632c4a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF +Out = A755E113386572C75CED61D719706070B9146048E42A9F8CD35667A088B42F08808ABDF77E618ABD959AFC757379CA2C00BCC1A48390FA2BFF618B1E0078A613 -In = C941CDB9C28AB0A791F2E5C8E8BB52850626AA89205BEC3A7E22682313D198B1FA33FC7295381354858758AE6C8EC6FAC3245C6E454D16FA2F51C4166FAB51DF272858F2D603770C40987F64442D487AF49CD5C3991CE858EA2A60DAB6A65A34414965933973AC2457089E359160B7CDEDC42F29E10A91921785F6B7224EE0B349393CDCFF6151B50B377D609559923D0984CDA6000829B916AB6896693EF6A2199B3C22F7DC5500A15B8258420E314C222BC000BC4E5413E6DD82C993F8330F5C6D1BE4BC79F08A1A0A46 -Out = 81dd6e8869be675d380ed8ebf89a23ebd27858479c338e60647c586940d21ff5df374eee431be798e3c5cbafc5b3547e9e377b5c1b0bf2a5c6f3164160373657 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0 +Out = A73C7DEBED326F1C0DB0795EE7D6E3946894B826B1F8101C56C823BA17168312E7F53FC7DBE52C3E11E69852C40485E2EF182477862EA6A34EC136E2DFEEA6F4 -In = 4499EFFFAC4BCEA52747EFD1E4F20B73E48758BE915C88A1FFE5299B0B005837A46B2F20A9CB3C6E64A9E3C564A27C0F1C6AD1960373036EC5BFE1A8FC6A435C2185ED0F114C50E8B3E4C7ED96B06A036819C9463E864A58D6286F785E32A804443A56AF0B4DF6ABC57ED5C2B185DDEE8489EA080DEEEE66AA33C2E6DAB36251C402682B6824821F998C32163164298E1FAFD31BABBCFFB594C91888C6219079D907FDB438ED89529D6D96212FD55ABE20399DBEFD342248507436931CDEAD496EB6E4A80358ACC78647D043 -Out = 771325a02405d96d5222beff7e88b4e5afdc8b1c067df4926c976cd9ff3253727bc4e78125c81dd162588975fa9506411283cd25cc459dddddf59de8111da3af +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1 +Out = 6CB8F9D52C56D82CAC28F39EA1593E8BB2506293AC0D68376A1709B62A46DF14A4AE64B2D8FAB76733A1CED2D548E3F3C6FCB49D40C3D5808E449CD83D1C2AA2 -In = EECBB8FDFA4DA62170FD06727F697D81F83F601FF61E478105D3CB7502F2C89BF3E8F56EDD469D049807A38882A7EEFBC85FC9A950952E9FA84B8AFEBD3CE782D4DA598002827B1EB98882EA1F0A8F7AA9CE013A6E9BC462FB66C8D4A18DA21401E1B93356EB12F3725B6DB1684F2300A98B9A119E5D27FF704AFFB618E12708E77E6E5F34139A5A41131FD1D6336C272A8FC37080F041C71341BEE6AB550CB4A20A6DDB6A8E0299F2B14BC730C54B8B1C1C487B494BDCCFD3A53535AB2F231590BF2C4062FD2AD58F906A2D0D -Out = d5afc9a9095a023369a3714f20ad920c372111c69ec6350ab32207114aafd38a83ae61217d5256626127d03a21694398d96ea8f387d0f9a0a3c8f749e061dd18 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2 +Out = 683FA2B2369A10162C1C1C7B24BC970EE67DA220564F32203F625696C0352A0B9AD96624362D952D84463C1106A2DBA7A092599884B35A0B89C8F1B6A9B5A61E -In = E64F3E4ACE5C8418D65FEC2BC5D2A303DD458034736E3B0DF719098BE7A206DEAF52D6BA82316CAF330EF852375188CDE2B39CC94AA449578A7E2A8E3F5A9D68E816B8D16889FBC0EBF0939D04F63033AE9AE2BDAB73B88C26D6BD25EE460EE1EF58FB0AFA92CC539F8C76D3D097E7A6A63EBB9B5887EDF3CF076028C5BBD5B9DB3211371AD3FE121D4E9BF44229F4E1ECF5A0F9F0EBA4D5CEB72878AB22C3F0EB5A625323AC66F7061F4A81FAC834471E0C59553F108475FE290D43E6A055AE3EE46FB67422F814A68C4BE3E8C9 -Out = b4f9601a5eb75123eeef7d03e41dca075c17a1770eb9b7c19ea357dad55c8edd56de2b0205362861eddf52d528b7feccfe6fedd6ccf0b0ed8fb2f9ca133e40a3 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3 +Out = AAD9AD44610118B77D508AEB1BBCD1C1B7D0171397FB510A401BBC0EC34623670D86A2DC3C8F3AB5A2044DF730256727545F0860CE21A1EAC717DFC48F5D228E -In = D2CB2D733033F9E91395312808383CC4F0CA974E87EC68400D52E96B3FA6984AC58D9AD0938DDE5A973008D818C49607D9DE2284E7618F1B8AED8372FBD52ED54557AF4220FAC09DFA8443011699B97D743F8F2B1AEF3537EBB45DCC9E13DFB438428EE190A4EFDB3CAEB7F3933117BF63ABDC7E57BEB4171C7E1AD260AB0587806C4D137B6316B50ABC9CCE0DFF3ACADA47BBB86BE777E617BBE578FF4519844DB360E0A96C6701290E76BB95D26F0F804C8A4F2717EAC4E7DE9F2CFF3BBC55A17E776C0D02856032A6CD10AD2838 -Out = 64fe31a2ed2f070918b20d233796cb4671dc79f3adb43fdcac52c548c9654767725f60d92d36cfb902f1d8bd49fdd0e3c2d79588ae8d93babc42ee2dae7ecab2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4 +Out = C42578DE23B4C987D5E1AC4D689ED5DE4B0417F9704BC6BCE969FA13471585D62C2CB1212A944F397FC9CA2C3747C3BEB694EC4C5BE68828DDA53EF43FAEC6C0 -In = F2998955613DD414CC111DF5CE30A995BB792E260B0E37A5B1D942FE90171A4AC2F66D4928D7AD377F4D0554CBF4C523D21F6E5F379D6F4B028CDCB9B1758D3B39663242FF3CB6EDE6A36A6F05DB3BC41E0D861B384B6DEC58BB096D0A422FD542DF175E1BE1571FB52AE66F2D86A2F6824A8CFAACBAC4A7492AD0433EEB15454AF8F312B3B2A577750E3EFBD370E8A8CAC1582581971FBA3BA4BD0D76E718DACF8433D33A59D287F8CC92234E7A271041B526E389EFB0E40B6A18B3AAF658E82ED1C78631FD23B4C3EB27C3FAEC8685 -Out = d040e47163677d093993baf5712c9bb543352a84edf3695ae739c4938cc94e223cc61f3eeca372e8eba41336be33a163bc51ff07fc0c2819757497af28036bcb +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5 +Out = 470F00841EE8244E63ED2C7EA30E2E419897C197462ECCCECF713B42A5065FFF5914BC9B79AFFE8F6B657875E789AE213BD914CD35BD174D46E9D18BD843773D -In = 447797E2899B72A356BA55BF4DF3ACCA6CDB1041EB477BD1834A9F9ACBC340A294D729F2F97DF3A610BE0FF15EDB9C6D5DB41644B9874360140FC64F52AA03F0286C8A640670067A84E017926A70438DB1BB361DEFEE7317021425F8821DEF26D1EFD77FC853B818545D055ADC9284796E583C76E6FE74C9AC2587AA46AA8F8804F2FEB5836CC4B3ABABAB8429A5783E17D5999F32242EB59EF30CD7ADABC16D72DBDB097623047C98989F88D14EAF02A7212BE16EC2D07981AAA99949DDF89ECD90333A77BC4E1988A82ABF7C7CAF3291 -Out = 4fe50604c47cbac76923ca1acf9cbc1a112d74f4b1cd6f4fc257c734316ae66e9206598d60c8cc88dfc2f303f27a52d4f3c73bcf1e3980bccd6988ad3d95d2cc +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6 +Out = 34FC4213730F47A5E9A3580F643E12945CFCB31BF206F6AD450CE528DA3FA432E005D6B0ECCE10DCA7C5995F6AACC5150E1B009E19751E8309F8859531844374 -In = 9F2C18ADE9B380C784E170FB763E9AA205F64303067EB1BCEA93DF5DAC4BF5A2E00B78195F808DF24FC76E26CB7BE31DC35F0844CDED1567BBA29858CFFC97FB29010331B01D6A3FB3159CC1B973D255DA9843E34A0A4061CABDB9ED37F241BFABB3C20D32743F4026B59A4CCC385A2301F83C0B0A190B0F2D01ACB8F0D41111E10F2F4E149379275599A52DC089B35FDD5234B0CFB7B6D8AEBD563CA1FA653C5C021DFD6F5920E6F18BFAFDBECBF0AB00281333ED50B9A999549C1C8F8C63D7626C48322E9791D5FF72294049BDE91E73F8 -Out = faed5e6286e55888d55e4ffa86ca3bf7fd9bb286c8a2ba8e5b7c33f8f20edb1b801632b3bbe81a58e64763ae4b7469b4a814fbc774c23850bab931afb71a8608 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7 +Out = FB3C1F0F56A56F8E316FDF5D853C8C872C39635D083634C3904FC3AC07D1B578E85FF0E480E92D44ADE33B62E893EE32343E79DDF6EF292E89B582D312502314 -In = AE159F3FA33619002AE6BCCE8CBBDD7D28E5ED9D61534595C4C9F43C402A9BB31F3B301CBFD4A43CE4C24CD5C9849CC6259ECA90E2A79E01FFBAC07BA0E147FA42676A1D668570E0396387B5BCD599E8E66AAED1B8A191C5A47547F61373021FA6DEADCB55363D233C24440F2C73DBB519F7C9FA5A8962EFD5F6252C0407F190DFEFAD707F3C7007D69FF36B8489A5B6B7C557E79DD4F50C06511F599F56C896B35C917B63BA35C6FF8092BAF7D1658E77FC95D8A6A43EEB4C01F33F03877F92774BE89C1114DD531C011E53A34DC248A2F0E6 -Out = 840a933c8a117537611b8a68291c0039a0ca592b6b184d037a7d0333046dc46ff7a7cd0a9dd5c66c949ca72dfbcde9d416df9e893a79e348bf9ee0a1d72fb9dd +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8 +Out = C7C97FC65DD2B9E3D3D607D31598D3F84261E9919251E9C8E57BB5F829377D5F73EABBED55C6C381180F29AD02E5BE797FFEC7E57BDECBC50AD3D062F0993AB0 -In = 3B8E97C5FFC2D6A40FA7DE7FCEFC90F3B12C940E7AB415321E29EE692DFAC799B009C99DCDDB708FCE5A178C5C35EE2B8617143EDC4C40B4D313661F49ABDD93CEA79D117518805496FE6ACF292C4C2A1F76B403A97D7C399DAF85B46AD84E16246C67D6836757BDE336C290D5D401E6C1386AB32797AF6BB251E9B2D8FE754C47482B72E0B394EAB76916126FD68EA7D65EB93D59F5B4C5AC40F7C3B37E7F3694F29424C24AF8C8F0EF59CD9DBF1D28E0E10F799A6F78CAD1D45B9DB3D7DEE4A7059ABE99182714983B9C9D44D7F5643596D4F3 -Out = 482a3dc10a575fce17888f69c99e0de17c730ffbf56f01b7f54447bb251e9b2018f35561d3ca7c12ccf9b3731f419b22ffe412777fa45666b79a223748233891 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9 +Out = A57A49CDBE67AE7D9F797BB5CC7EFC2DF07F4E1B15955F85DAE74B76E2ECB85AFB6CD9EEED8888D5CA3EC5AB65D27A7B19E578475760A045AC3C92E13A938E77 -In = 3434EC31B10FAFDBFEEC0DD6BD94E80F7BA9DCA19EF075F7EB017512AF66D6A4BCF7D16BA0819A1892A6372F9B35BCC7CA8155EE19E8428BC22D214856ED5FA9374C3C09BDE169602CC219679F65A1566FC7316F4CC3B631A18FB4449FA6AFA16A3DB2BC4212EFF539C67CF184680826535589C7111D73BFFCE431B4C40492E763D9279560AAA38EB2DC14A212D723F994A1FE656FF4DD14551CE4E7C621B2AA5604A10001B2878A897A28A08095C325E10A26D2FB1A75BFD64C250309BB55A44F23BBAC0D5516A1C687D3B41EF2FBBF9CC56D4739 -Out = a844b8b905e059254419dd0ccad361413fcbee76d6ab82c8f4d708ace62ed3138073bbe49a366b84c95f4777fefe92ab213955fa63f7c438359fa038134c612a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CA +Out = C7143FCE9614A17FD653AEB140726DC9C3DBB1DE6CC581B2726897EC24B7A50359AD492243BE66D9EDD8C933B5B80E0B91BB61EA98056006516976FAE8D99A35 -In = 7C7953D81C8D208FD1C97681D48F49DD003456DE60475B84070EF4847C333B74575B1FC8D2A186964485A3B8634FEAA3595AAA1A2F4595A7D6B6153563DEE31BBAC443C8A33EED6D5D956A980A68366C2527B550EE950250DFB691EACBD5D56AE14B970668BE174C89DF2FEA43AE52F13142639C884FD62A3683C0C3792F0F24AB1318BCB27E21F4737FAB62C77EA38BC8FD1CF41F7DAB64C13FEBE7152BF5BB7AB5A78F5346D43CC741CB6F72B7B8980F268B68BF62ABDFB1577A52438FE14B591498CC95F071228460C7C5D5CEB4A7BDE588E7F21C -Out = 7241ee8ec880717f94a700e9e915aa474cab2a1068a7a56d7cf1cdcc0400323535151fa9c8759877cadbe39d357f9fff6950abdfeacf2d1c3d54835ed7fed953 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACB +Out = 65BB58D07F937E2D3C7E65385F9C54730B704105CCDB691F6E146D4EE8F6C086F49511035110A9AD6031FDCEB943E0F9613BCB276DD40F0624EF0F924F809783 -In = 7A6A4F4FDC59A1D223381AE5AF498D74B7252ECF59E389E49130C7EAEE626E7BD9897EFFD92017F4CCDE66B0440462CDEDFD352D8153E6A4C8D7A0812F701CC737B5178C2556F07111200EB627DBC299CAA792DFA58F35935299FA3A3519E9B03166DFFA159103FFA35E8577F7C0A86C6B46FE13DB8E2CDD9DCFBA85BDDDCCE0A7A8E155F81F712D8E9FE646153D3D22C811BD39F830433B2213DD46301941B59293FD0A33E2B63ADBD95239BC01315C46FDB678875B3C81E053A40F581CFBEC24A1404B1671A1B88A6D06120229518FB13A74CA0AC5AE -Out = 8542062229938ce461c47e348bcadc6085118e0a7800227477abf5f5981237894c2ce131799d455142db0c877cb46a35be1d2c0cb386a200c9d07b868a5bdb40 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCC +Out = E540277F683B1186DD3B5B3F61433396581A35FEB12002BE8C6A6231FC40FFA70F08081BC58B2D94F7649543614A435FAA2D62110E13DABC7B86629B63AF9C24 -In = D9FAA14CEBE9B7DE551B6C0765409A33938562013B5E8E0E1E0A6418DF7399D0A6A771FB81C3CA9BD3BB8E2951B0BC792525A294EBD1083688806FE5E7F1E17FD4E3A41D00C89E8FCF4A363CAEDB1ACB558E3D562F1302B3D83BB886ED27B76033798131DAB05B4217381EAAA7BA15EC820BB5C13B516DD640EAEC5A27D05FDFCA0F35B3A5312146806B4C0275BCD0AAA3B2017F346975DB566F9B4D137F4EE10644C2A2DA66DEECA5342E236495C3C6280528BFD32E90AF4CD9BB908F34012B52B4BC56D48CC8A6B59BAB014988EABD12E1A0A1C2E170E7 -Out = 9b3d69e33e0c6f51ddef4457af78657cfc07bf35d90ffdee5cec43d6c81b5c987c62019eb2cb42a24bb99cb261166258f4241c53a5f8d34468541ee7bc6bf1fb +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCD +Out = 418500878C5FBCB584C432F4285E05E49F2E3E075399A0DBFCF874EBF8C03D02BF16BC6989D161C77CA0786B05053C6C709433712319192128835CF0B660595B -In = 2D8427433D0C61F2D96CFE80CF1E932265A191365C3B61AAA3D6DCC039F6BA2AD52A6A8CC30FC10F705E6B7705105977FA496C1C708A277A124304F1FC40911E7441D1B5E77B951AAD7B01FD5DB1B377D165B05BBF898042E39660CAF8B279FE5229D1A8DB86C0999ED65E53D01CCBC4B43173CCF992B3A14586F6BA42F5FE30AFA8AE40C5DF29966F9346DA5F8B35F16A1DE3AB6DE0F477D8D8660918060E88B9B9E9CA6A4207033B87A812DBF5544D39E4882010F82B6CE005F8E8FF6FE3C3806BC2B73C2B83AFB704345629304F9F86358712E9FAE3CA3E -Out = 405ccfe3a43b542778e5d8cadaa73555ab3095a31123f852cdcb76561e8a45b295a47e54e05a38a93a765fcef0ff7ce198592a27da0661605213a002cecc4020 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCE +Out = 889090DBB1944BDC9433EE5EF1010C7A4A24A8E71ECEA8E12A31318CE49DCAB0ACA5C3802334AAB2CC84B14C6B9321FE586BF3F876F19CD406EB1127FB944801 -In = 5E19D97887FCAAC0387E22C6F803C34A3DACD2604172433F7A8A7A526CA4A2A1271ECFC5D5D7BE5AC0D85D921095350DFC65997D443C21C8094E0A3FEFD2961BCB94AED03291AE310CCDA75D8ACE4BC7D89E7D3E5D1650BDA5D668B8B50BFC8E608E184F4D3A9A2BADC4FF5F07E0C0BC8A9F2E0B2A26FD6D8C550008FAAAB75FD71AF2A424BEC9A7CD9D83FAD4C8E9319115656A8717D3B523A68FF8004258B9990ED362308461804BA3E3A7E92D8F2FFAE5C2FBA55BA5A3C27C0A2F71BD711D2FE1799C2ADB31B200035481E9EE5C4ADF2AB9C0FA50B23975CF -Out = 91e5fcd46cece02f3405db279da87eaa6b77c926945a77a75c346cb137a3f95b753497abfddd99094764ea0a318aab2e89502a0a22d151f0b5bf2c46300be2eb +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECF +Out = 53B6A28910AA92E27E536FB549CF9B9918791060898E0B9FE183577FF43B5E9C7689C745B32E412269837C31B89E6CC12BF76E13CAD366B74ECE48BB85FD09E9 -In = C8E976AB4638909387CE3B8D4E510C3230E5690E02C45093B1D297910ABC481E56EEA0F296F98379DFC9080AF69E73B2399D1C143BEE80AE1328162CE1BA7F6A8374679B20AACD380EB4E61382C99998704D62701AFA914F9A2705CDB065885F50D086C3EB5753700C387118BB142F3E6DA1E988DFB31AC75D7368931E45D1391A274B22F83CEB072F9BCABC0B216685BFD789F5023971024B1878A205442522F9EA7D8797A4102A3DF41703768251FD5E017C85D1200A464118AA35654E7CA39F3C375B8EF8CBE7534DBC64BC20BEFB417CF60EC92F63D9EE7397 -Out = 4747177a5b44e517fccd84e022259675e925c967447a759c2f20277bf2123b40507f51f12b7c6301eb0b22ff7bd3c4fbebc14ca47483b5038d48576d6bb647df +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0 +Out = 7C092080C6A80D672409D081D3D177106BCD63567785140719490950AE07AE8FCAABBAAAB330CFBCF7374482C220AF2EADEEB73DCBB35ED823344E144E7D4899 -In = 7145FA124B7429A1FC2231237A949BA7201BCC1822D3272DE005B682398196C25F7E5CC2F289FBF44415F699CB7FE6757791B1443410234AE061EDF623359E2B4E32C19BF88450432DD01CAA5EB16A1DC378F391CA5E3C4E5F356728BDDD4975DB7C890DA8BBC84CC73FF244394D0D48954978765E4A00B593F70F2CA082673A261ED88DBCEF1127728D8CD89BC2C597E9102CED6010F65FA75A14EBE467FA57CE3BD4948B6867D74A9DF5C0EC6F530CBF2EE61CE6F06BC8F2864DFF5583776B31DF8C7FFCB61428A56BF7BD37188B4A5123BBF338393AF46EDA85E6 -Out = af235c0efd38e3326880503f5e0d67ca3a950f8783801b9002bf054bbd3e30db2c19d97719cdfb56517acd8c1b1fd67ca3c143b44763f1edbbefa3a150486e24 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1 +Out = 9CCDE566D2400509181111F32DDE4CD63209FE59A30C114546AD2776D889A41BAD8FA1BB468CB2F9D42CA9928A7770FEF8E8BA4D0C812D9A1E75C3D8D2CCD75A -In = 7FDFADCC9D29BAD23AE038C6C65CDA1AEF757221B8872ED3D75FF8DF7DA0627D266E224E812C39F7983E4558BFD0A1F2BEF3FEB56BA09120EF762917B9C093867948547AEE98600D10D87B20106878A8D22C64378BF634F7F75900C03986B077B0BF8B740A82447B61B99FEE5376C5EB6680EC9E3088F0BDD0C56883413D60C1357D3C811950E5890E7600103C916341B80C743C6A852B7B4FB60C3BA21F3BC15B8382437A68454779CF3CD7F9F90CCC8EF28D0B706535B1E4108EB5627BB45D719CB046839AEE311CA1ABDC8319E050D67972CB35A6B1601B25DBF487 -Out = 95c8f1c229a87040a8704c9a1903a728784dfd0a095d0a70fa04adaad80b7814a9b236f5870b301bdb720b5ac9f65e18144ec8505e673e4799e716982728e92e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2 +Out = 6E293BF5D03FE43977CFE3F57CCDB3AE282A85455DCA33F37F4B74F8398CC612433D755CBEC412F8F82A3BD3BC4A278F7ECD0DFA9BBDC40BE7A787C8F159B2DF -In = 988638219FD3095421F826F56E4F09E356296B628C3CE6930C9F2E758FD1A80C8273F2F61E4DAAE65C4F110D3E7CA0965AC7D24E34C0DC4BA2D6FF0BF5BBE93B3585F354D7543CB542A1AA54674D375077F2D360A8F4D42F3DB131C3B7AB7306267BA107659864A90C8C909460A73621D1F5D9D3FD95BEB19B23DB1CB6C0D0FBA91D36891529B8BD8263CAA1BAB56A4AFFAED44962DF096D8D5B1EB845EF31188B3E10F1AF811A13F156BEB7A288AAE593EBD1471B624AA1A7C6ADF01E2200B3D72D88A3AED3100C88231E41EFC376906F0B580DC895F080FDA5741DB1CB -Out = 8f5371eddc8a5ff9d9fb8a045d56d61f18002a28128dfab9cf1fae13e0ff82aff6863875d173023875b4fc2ea06b2cce0edc42e947961b6de11f4be3027fcf35 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3 +Out = C56546FB2178456F336164C18B90DEFFC83AE2B5A3ACA77B6884D36D2C1DB39501B3E65E36C758C66E3188451FDB3515EE162C001F06C3E8CB573ADF30F7A101 -In = 5AAB62756D307A669D146ABA988D9074C5A159B3DE85151A819B117CA1FF6597F6156E80FDD28C9C3176835164D37DA7DA11D94E09ADD770B68A6E081CD22CA0C004BFE7CD283BF43A588DA91F509B27A6584C474A4A2F3EE0F1F56447379240A5AB1FB77FDCA49B305F07BA86B62756FB9EFB4FC225C86845F026EA542076B91A0BC2CDD136E122C659BE259D98E5841DF4C2F60330D4D8CDEE7BF1A0A244524EECC68FF2AEF5BF0069C9E87A11C6E519DE1A4062A10C83837388F7EF58598A3846F49D499682B683C4A062B421594FAFBC1383C943BA83BDEF515EFCF10D -Out = 89d67fd2b2fb4167fe5fa19e3578c846856e95dc9da8e28c7be4ed60f08f11e636d09d3dcea514c0de3c369f1ce2ec392353a683a27b2c611eaa916d7f76f68d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4 +Out = 6F82F89F299EBCA2FE014B59BFFE1AA84E88B1915FE256AFB646FD8448AF2B8891A7FAB37A4EA6F9A50E6C317039D8CF878F4C8E1A0DD464F0B4D6FF1C7EA853 -In = 47B8216AA0FBB5D67966F2E82C17C07AA2D6327E96FCD83E3DE7333689F3EE79994A1BF45082C4D725ED8D41205CB5BCDF5C341F77FACB1DA46A5B9B2CBC49EADF786BCD881F371A95FA17DF73F606519AEA0FF79D5A11427B98EE7F13A5C00637E2854134691059839121FEA9ABE2CD1BCBBBF27C74CAF3678E05BFB1C949897EA01F56FFA4DAFBE8644611685C617A3206C7A7036E4AC816799F693DAFE7F19F303CE4EBA09D21E03610201BFC665B72400A547A1E00FA9B7AD8D84F84B34AEF118515E74DEF11B9188BD1E1F97D9A12C30132EC2806339BDADACDA2FD8B78 -Out = 785c212ddd4313d925b62a7348ab602e30a146363bbee39b7a0b6b790d35db6a6af2e811aebde82427d76d0742c0e764c33e823ed6070b0695413c811bc1cfa0 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5 +Out = 2B8599FF9C3D6198637AD51E57D1998B0D75313FE2DD61A533C964A6DD9607C6F723E9452CE46E014B1C1D6DE77BA5B88C914D1C597BF1EAE13474B4290E89B2 -In = 8CFF1F67FE53C098896D9136389BD8881816CCAB34862BB67A656E3D98896F3CE6FFD4DA73975809FCDF9666760D6E561C55238B205D8049C1CEDEEF374D1735DAA533147BFA960B2CCE4A4F254176BB4D1BD1E89654432B8DBE1A135C42115B394B024856A2A83DC85D6782BE4B444239567CCEC4B184D4548EAE3FF6A192F343292BA2E32A0F267F31CC26719EB85245D415FB897AC2DA433EE91A99424C9D7F1766A44171D1651001C38FC79294ACCC68CEB5665D36218454D3BA169AE058A831338C17743603F81EE173BFC0927464F9BD728DEE94C6AEAB7AAE6EE3A627E8 -Out = 7e1546f4f0555006a087ad79088ccc29755acfca43e28ea979d68ef1f53a9aad6f2152eb68d308c95e1511d60d17b490b34bd0b467d790971e1516a1ee7bc71c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6 +Out = 08BF346D38E1DF06C8260EDB1DA75579275948D5C0A0AA9ED2886F8856DE5417A156998758F5B17E52F101CA957A71137473DFD18D7D209C4C10D9233C93691D -In = EACD07971CFF9B9939903F8C1D8CBB5D4DB1B548A85D04E037514A583604E787F32992BF2111B97AC5E8A938233552731321522AB5E8583561260B7D13EBEEF785B23A41FD8576A6DA764A8ED6D822D4957A545D5244756C18AA80E1AAD4D1F9C20D259DEE1711E2CC8FD013169FB7CC4CE38B362F8E0936AE9198B7E838DCEA4F7A5B9429BB3F6BBCF2DC92565E3676C1C5E6EB3DD2A0F86AA23EDD3D0891F197447692794B3DFA269611AD97F72B795602B4FDB198F3FD3EB41B415064256E345E8D8C51C555DC8A21904A9B0F1AD0EFFAB7786AAC2DA3B196507E9F33CA356427 -Out = 48f474be0fea796aface17f6d984d57aed1d5166010af7d3b6d4dec47e4a1dbdc63f136bc3c07a82d53821e78282588a688dd1e36648a3b65c91469b4de3ed6c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7 +Out = 6DF2156D773114D310B63DB9EE5350D77E6BCF25B05FCD910F9B31BC42BB13FE8225EBCB2A23A62280777B6BF74E2CD0917C7640B43DEFE468CD1E18C943C66A -In = 23AC4E9A42C6EF45C3336CE6DFC2FF7DE8884CD23DC912FEF0F7756C09D335C189F3AD3A23697ABDA851A81881A0C8CCAFC980AB2C702564C2BE15FE4C4B9F10DFB2248D0D0CB2E2887FD4598A1D4ACDA897944A2FFC580FF92719C95CF2AA42DC584674CB5A9BC5765B9D6DDF5789791D15F8DD925AA12BFFAFBCE60827B490BB7DF3DDA6F2A143C8BF96ABC903D83D59A791E2D62814A89B8080A28060568CF24A80AE61179FE84E0FFAD00388178CB6A617D37EFD54CC01970A4A41D1A8D3DDCE46EDBBA4AB7C90AD565398D376F431189CE8C1C33E132FEAE6A8CD17A61C630012 -Out = bc388ded65483faa5dd20ff2d77950829f311e35cda8664466983c2412f72278ef29ad9bbc9b2929b579ebc40f23656b849a21bfd10a70f22e7e91f6a37e4b1b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8 +Out = 7C7038BC13A91151828A5BA82B4A96040F258A4DFB1B1373F0D359168AFB0517A20B28A12D3644046BE66B8D08D8AE7F6A923EA1C00187C6D11DC502BAC71305 -In = 0172DF732282C9D488669C358E3492260CBE91C95CFBC1E3FEA6C4B0EC129B45F242ACE09F152FC6234E1BEE8AAB8CD56E8B486E1DCBA9C05407C2F95DA8D8F1C0AF78EE2ED82A3A79EC0CB0709396EE62AADB84F8A4EE8A7CCCA3C1EE84E302A09EA802204AFECF04097E67D0F8E8A9D2651126C0A598A37081E42D168B0AE8A71951C524259E4E2054E535B779679BDADE566FE55700858618E626B4A0FAF895BCCE9011504A49E05FD56127EAE3D1F8917AFB548ECADABDA1020111FEC9314C413498A360B08640549A22CB23C731ACE743252A8227A0D2689D4C6001606678DFB921 -Out = d59756115277a35a80f5d0d5b1a975fa0e74d6a56da90855480454252303eda30c884fc3cf39a82baab9a85c32b1fc870515498b77f178e1a439ced65abe8b3d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9 +Out = BCD1B30D808FB739B987CBF154BEA00DA9D40380B861D4C1D6377122DADD61C0E59018B71941CFB62E00DCD70AEB9ABF0473E80F0A7ECA6B6DEA246AB229DD2B -In = 3875B9240CF3E0A8B59C658540F26A701CF188496E2C2174788B126FD29402D6A75453BA0635284D08835F40051A2A9683DC92AFB9383719191231170379BA6F4ADC816FECBB0F9C446B785BF520796841E58878B73C58D3EBB097CE4761FDEABE15DE2F319DFBAF1742CDEB389559C788131A6793E193856661376C81CE9568DA19AA6925B47FFD77A43C7A0E758C37D69254909FF0FBD415EF8EB937BCD49F91468B49974C07DC819ABD67395DB0E05874FF83DDDAB895344ABD0E7111B2DF9E58D76D85AD98106B36295826BE04D435615595605E4B4BB824B33C4AFEB5E7BB0D19F909 -Out = 8ac3c8d5254237948d95a7093e5a69b74f13f03b836edd7892f7e0fb4b8756710efa9ca0d89a6f33326d4e5695ef248296340d911222a487c9d59472ab5dcf02 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DA +Out = 7ED4468D968530FE7AB2C33540B26D8C3BD3ED44B34FBE8C2A9D7F805B5ADA0EA252EEADE4FCE97F89728AD85BC8BB2430B1BEF2CDDD32C8446E59B8E8BA3C67 -In = 747CC1A59FEFBA94A9C75BA866C30DC5C1CB0C0F8E9361D98484956DD5D1A40F6184AFBE3DAC9F76028D1CAECCFBF69199C6CE2B4C092A3F4D2A56FE5A33A00757F4D7DEE5DFB0524311A97AE0668A47971B95766E2F6DD48C3F57841F91F04A00AD5EA70F2D479A2620DC5CD78EAAB3A3B011719B7E78D19DDF70D9423798AF77517EBC55392FCD01FC600D8D466B9E7A7A85BF33F9CC5419E9BD874DDFD60981150DDAF8D7FEBAA4374F0872A5628D318000311E2F5655365AD4D407C20E5C04DF17A222E7DEEC79C5AB1116D8572F91CD06E1CCC7CED53736FC867FD49ECEBE6BF8082E8A -Out = 51af24e79da7ad92b1b2dd1229aa5fcc0c493e074805cf9cff99cece8943f19fb8b78b89cdc23794f5feb38a81abb078a984f65464dfc35b94721df7a8fa87cf +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADB +Out = 6D30B7C6CE8A3236C0CA2F8D728B1088CA06983A8043E621D5DCF0C537D13B08791EDEB01A3CF0943EC1C890AB6E29B146A236CD46BCB9D93BF516FB67C63FE5 -In = 57AF971FCCAEC97435DC2EC9EF0429BCEDC6B647729EA168858A6E49AC1071E706F4A5A645CA14E8C7746D65511620682C906C8B86EC901F3DDED4167B3F00B06CBFAC6AEE3728051B3E5FF10B4F9ED8BD0B8DA94303C833755B3CA3AEDDF0B54BC8D6632138B5D25BAB03D17B3458A9D782108006F5BB7DE75B5C0BA854B423D8BB801E701E99DC4FEAAD59BC1C7112453B04D33EA3635639FB802C73C2B71D58A56BBD671B18FE34ED2E3DCA38827D63FDB1D4FB3285405004B2B3E26081A8FF08CD6D2B08F8E7B7E90A2AB1ED7A41B1D0128522C2F8BFF56A7FE67969422CE839A9D4608F03 -Out = 061491ac2501a065b29ec16fc474d5e7121bc81f1aa820dd1df7423ee04c3700e815cc8435b24ad0cb3a8d7377bfda5af8115379640d1ce7cb4b7795e9b8fb18 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDC +Out = 97FE03CEF31438508911BDED975980A66029305DC5E3FA8AD1B4FB22FCDF5A19A733320327D8F71CCF496CB3A44A77AF56E3DDE73D3A5F176896CC57C9A5AD99 -In = 04E16DEDC1227902BAAF332D3D08923601BDD64F573FAA1BB7201918CFE16B1E10151DAE875DA0C0D63C59C3DD050C4C6A874011B018421AFC4623AB0381831B2DA2A8BA42C96E4F70864AC44E106F94311051E74C77C1291BF5DB9539E69567BF6A11CF6932BBBAD33F8946BF5814C066D851633D1A513510039B349939BFD42B858C21827C8FF05F1D09B1B0765DC78A135B5CA4DFBA0801BCADDFA175623C8B647EACFB4444B85A44F73890607D06D507A4F8393658788669F6EF4DEB58D08C50CA0756D5E2F49D1A7AD73E0F0B3D3B5F090ACF622B1878C59133E4A848E05153592EA81C6FBF -Out = e3f5146c41d7dc3fb691df35c37923e63c0790d4f5e47b077cafa986500dfbe3942b49054417309bb3986d15e2c7048630d69f646f672deddaec33d807272e38 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDD +Out = 785A9D0FBD21136DBCE8FA7EAFD63C9DAD220052978416B31D9753EAA149097847ED9B30A65C70507EFF01879149ED5CF0471D37798EDC05ABD56AD4A2CCCB1D -In = 7C815C384EEE0F288ECE27CCED52A01603127B079C007378BC5D1E6C5E9E6D1C735723ACBBD5801AC49854B2B569D4472D33F40BBB8882956245C366DC3582D71696A97A4E19557E41E54DEE482A14229005F93AFD2C4A7D8614D10A97A9DFA07F7CD946FA45263063DDD29DB8F9E34DB60DAA32684F0072EA2A9426ECEBFA5239FB67F29C18CBAA2AF6ED4BF4283936823AC1790164FEC5457A9CBA7C767CA59392D94CAB7448F50EB34E9A93A80027471CE59736F099C886DEA1AB4CBA4D89F5FC7AE2F21CCD27F611ECA4626B2D08DC22382E92C1EFB2F6AFDC8FDC3D2172604F5035C46B8197D3 -Out = 716e69c4ab2793d751351636de09066e17bb959a4a0cfe526d64b36a73aff29f1cd248016b256a8ea7859232ccf8a6ef8ad71632eedafd4f2b86317c1059e77d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDE +Out = AD408D2ABDDFD37B3BF34794C1A3371D928ED7FC8D966225333584C5665817832A37C07F0DC7CB5AA874CD7D20FE8FAB8EABCB9B33D2E0841F6E200960899D95 -In = E29D505158DBDD937D9E3D2145658EE6F5992A2FC790F4F608D9CDB44A091D5B94B88E81FAC4FDF5C49442F13B911C55886469629551189EAFF62488F1A479B7DB11A1560E198DDCCCCF50159093425FF7F1CB8D1D1246D0978764087D6BAC257026B090EFAE8CEC5F22B6F21C59ACE1AC7386F5B8837CA6A12B6FBF5534DD0560EF05CA78104D3B943DDB220FEAEC89AA5E692A00F822A2AB9A2FE60350D75E7BE16FF2526DC643872502D01F42F188ABED0A6E9A6F5FD0D1CE7D5755C9FFA66B0AF0B20BD806F08E06156690D81AC811778CA3DAC2C249B96002017FCE93E507E3B953ACF99964B847 -Out = 15b7f5030f1b7221386f386e0c7c6fa51dc066270a695e1883cfe1937eb1ff17651a7c5740a29559bdefa7877276af2a427b6ecfce2cf6b86ecb5a76f691ccb2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF +Out = 97668F745B6032FC815D9579322769DCCD9501A5080029B8AE826BEFB6742331BD9F76EFEB3E2B8E81A9786B282F5068A3A2424697A77C41876B7E753F4C7767 -In = D85588696F576E65ECA0155F395F0CFACD83F36A99111ED5768DF2D116D2121E32357BA4F54EDE927F189F297D3A97FAD4E9A0F5B41D8D89DD7FE20156799C2B7B6BF9C957BA0D6763F5C3BC5129747BBB53652B49290CFF1C87E2CDF2C4B95D8AAEE09BC8FBFA6883E62D237885810491BFC101F1D8C636E3D0EDE838AD05C207A3DF4FAD76452979EB99F29AFAECEDD1C63B8D36CF378454A1BB67A741C77AC6B6B3F95F4F02B64DABC15438613EA49750DF42EE90101F115AA9ABB9FF64324DDE9DABBB01054E1BD6B4BCDC7930A44C2300D87CA78C06924D0323AD7887E46C90E8C4D100ACD9EED21E -Out = 5706430335324092a58f970627bfbc5547c8ec14d4c20a504b33f8dbc0b2584add8e42444f1a356a253738e00e3e2c732a7d0e5065bcfddb2973ef5b2a3eacd6 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0 +Out = 26BB985F47E7FEE0CFD252D4EF96BED42B9C370C1C6A3E8C9EB04EF7F7818B833A0D1F043EBAFB911DC779E02740A02A44D3A1EA45ED4AD55E686C927CAFE97E -In = 3A12F8508B40C32C74492B66323375DCFE49184C78F73179F3314B79E63376B8AC683F5A51F1534BD729B02B04D002F55CBD8E8FC9B5EC1EA6BBE6A0D0E7431518E6BA45D124035F9D3DCE0A8BB7BF1430A9F657E0B4EA9F20EB20C786A58181A1E20A96F1628F8728A13BDF7A4B4B32FC8AA7054CC4881AE7FA19AFA65C6C3EE1B3ADE3192AF42054A8A911B8EC1826865D46D93F1E7C5E2B7813C92A506E53886F3D4701BB93D2A681AD109C845904BB861AF8AF0646B6E399B38B614051D34F6842563A0F37EC00CB3D865FC5D746C4987DE2A65071100883A2A9C7A2BFE1E2DD603D9EA24DC7C5FD06BE -Out = 1f2013ffb10abb079354a4aeca9b6284630a4d869f2c005899bc8a51e8e3c78f3e91131e0525e089782e769806d766f0ecbdba20e3e88b49138fb2c6289978de +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1 +Out = 5BFE2B1DCF7FE9B95088ACEDB575C19016C743B2E763BF5851AC407C9EDA43715EDFA48B4825492C5179593FFF21351B76E8B7E034E4C53C79F61F29C479BD08 -In = 1861EDCE46FA5AD17E1FF1DEAE084DEC580F97D0A67885DFE834B9DFAC1AE076742CE9E267512CA51F6DF5A455AF0C5FD6ABF94ACEA103A3370C354485A7846FB84F3AC7C2904B5B2FBF227002CE512133BB7E1C4E50057BFD1E44DB33C7CDB969A99E284B184F50A14B068A1FC5009D9B298DBE92239572A7627AAC02ABE8F3E3B473417F36D4D2505D16B7577F4526C9D94A270A2DFE450D06DA8F6FA956879A0A55CFE99E742EA555EA477BA3E9B44CCD508C375423611AF92E55345DC215779B2D5119EBA49C71D49B9FE3F1569FA24E5CA3E332D042422A8B8158D3EC66A80012976F31FFDF305F0C9C5E -Out = dcc6ecfff93b04ae6b0d2f5699ded5b7a9bf933ff4a37f60a65964af2d55c5afe6e0e4dbf56426b4293f945325230b70ee92e50ebe09942b05578bdfcb553a35 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2 +Out = C76509EF72F4A6F9C9C40618ED52B2084F83502232E0AC8BDAF3264368E4D0180F6854C4ABF4F6509C79CAAFC44CF3194AFC57BD077BD7B3C9BDA3D4B8775816 -In = 08D0FFDE3A6E4EF65608EA672E4830C12943D7187CCFF08F4941CFC13E545F3B9C7AD5EEBBE2B01642B486CAF855C2C73F58C1E4E3391DA8E2D63D96E15FD84953AE5C231911B00AD6050CD7AAFDAAC9B0F663AE6AAB45519D0F5391A541707D479034E73A6AD805AE3598096AF078F1393301493D663DD71F83869CA27BA508B7E91E81E128C1716DC3ACFE3084B2201E04CF8006617EECF1B640474A5D45CFDE9F4D3EF92D6D055B909892194D8A8218DB6D8203A84261D200D71473D7488F3427416B6896C137D455F231071CACBC86E0415AB88AEC841D96B7B8AF41E05BB461A40645BF176601F1E760DE5F -Out = 17af20d2d44775531a946de82fa2aae7d87e9b2a109039f2ed555d277210d270b80ee25eeb52157e88a88b50a68878ce084fb92d46a626dae42214acc98ff009 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3 +Out = D66F2BEAB990E354CCB910E4E9C7AC618C7B63EF292A96B552341DE78DC46D3EC8CFABC699B50AF41FDA39CF1B0173660923510AD67FAEDEF5207CFFE8641D20 -In = D782ABB72A5BE3392757BE02D3E45BE6E2099D6F000D042C8A543F50ED6EBC055A7F133B0DD8E9BC348536EDCAAE2E12EC18E8837DF7A1B3C87EC46D50C241DEE820FD586197552DC20BEEA50F445A07A38F1768A39E2B2FF05DDDEDF751F1DEF612D2E4D810DAA3A0CC904516F9A43AF660315385178A529E51F8AAE141808C8BC5D7B60CAC26BB984AC1890D0436EF780426C547E94A7B08F01ACBFC4A3825EAE04F520A9016F2FB8BF5165ED12736FC71E36A49A73614739EAA3EC834069B1B40F1350C2B3AB885C02C640B9F7686ED5F99527E41CFCD796FE4C256C9173186C226169FF257954EBDA81C0E5F99 -Out = b3c85df7606362b8282d9b7a2741880e0755da229d53c902c15e2c8925ec3ca42f79e5f6bbf3e69512d4b160a785eca8da05a81caffcbcf4f85ad760cf461d76 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4 +Out = 7D8F0672992B79BE3A364D8E5904F4AB713BBC8AB01B4F309AD8CCF223CE1034A860DCB0B00550612CC2FA17F2969E18F22E1427D254B4A82B3A03A3EB394ADF -In = 5FCE8109A358570E40983E1184E541833BB9091E280F258CFB144387B05D190E431CB19BAA67273BA0C58ABE91308E1844DCD0B3678BAA42F335F2FA05267A0240B3C718A5942B3B3E3BFA98A55C25A1466E8D7A603722CB2BBF03AFA54CD769A99F310735EE5A05DAE2C22D397BD95635F58C48A67F90E1B73AAFCD3F82117F0166657838691005B18DA6F341D6E90FC1CDB352B30FAE45D348294E501B63252DE14740F2B85AE5299DDEC3172DE8B6D0BA219A20A23BB5E10FF434D39DB3F583305E9F5C039D98569E377B75A70AB837D1DF269B8A4B566F40BB91B577455FD3C356C914FA06B9A7CE24C7317A172D -Out = f0f00afe3ab5f31fcc11ea7d70b7c4e61d13f600eeea442d09a90ddee9d17e574d553dda158bf63c3f357f89436c0990e5fdaa054647af4de0f2981653a9d813 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5 +Out = A56D6725BFB3DE47C1414ADF25FC8F0FC9846F6987722BC06366D5CA4E89722925EBBC881418844075397A0CA89842C7B9E9E07E1D9D183EBEB39E120B483BF7 -In = 6172F1971A6E1E4E6170AFBAD95D5FEC99BF69B24B674BC17DD78011615E502DE6F56B86B1A71D3F4348087218AC7B7D09302993BE272E4A591968AEF18A1262D665610D1070EE91CC8DA36E1F841A69A7A682C580E836941D21D909A3AFC1F0B963E1CA5AB193E124A1A53DF1C587470E5881FB54DAE1B0D840F0C8F9D1B04C645BA1041C7D8DBF22030A623AA15638B3D99A2C400FF76F3252079AF88D2B37F35EE66C1AD7801A28D3D388AC450B97D5F0F79E4541755356B3B1A5696B023F39AB7AB5F28DF4202936BC97393B93BC915CB159EA1BD7A0A414CB4B7A1AC3AF68F50D79F0C9C7314E750F7D02FAA58BFA -Out = c397f4991aa13a7038bacb194f178244835c90f4654f1a41680e881c06bdc0000752c630e18903967ca03229a0f26db70dc5297c357c169f1abfc9ef90363cc4 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6 +Out = AF5E03D7FE60C67E10313344434E79485A03A758D6DCE985574745763C1C5C77D4FB3E6FB12230368370993BF90FEED0C5D1607524562D7C09C0C210ED393D7C -In = 5668ECD99DFBE215C4118398AC9C9EAF1A1433FAB4CCDD3968064752B625EA944731F75D48A27D047D67547F14DD0FFAA55FA5E29F7AF0D161D85EAFC4F2029B717C918EAB9D304543290BDBA7158B68020C0BA4E079BC95B5BC0FC044A992B94B4CCD3BD66D0EABB5DBBAB904D62E00752C4E3B0091D773BCF4C14B4377DA3EFFF824B1CB2FA01B32D1E46C909E626ED2DAE920F4C7DBEB635BC754FACBD8D49BEBA3F23C1C41CCBFCD0EE0C114E69737F5597C0BF1D859F0C767E18002AE8E39C26261FFDE2920D3D0BAF0E906138696CFE5B7E32B600F45DF3AAA39932F3A7DF95B60FA8712A2271FCAF3911CE7B511B1 -Out = dfa614e840f355830273713bf1ab44d30755a084311b228cc3a417af171fdf618f5480b7cbea7f97f04f84dd169aedb9685965ff7aeb3fbdd4fbfb5752a2e00d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7 +Out = 7A20540CC07BF72B582421FC342E82F52134B69841EC28ED189E2EA6A29DD2F82A640352D222B52F2911DC72A7DAB31CAADD80C6118F13C56B2A1E4373BE0EA3 -In = 03D625488354DF30E3F875A68EDFCF340E8366A8E1AB67F9D5C5486A96829DFAC0578289082B2A62117E1CF418B43B90E0ADC881FC6AE8105C888E9ECD21AEA1C9AE1A4038DFD17378FED71D02AE492087D7CDCD98F746855227967CB1AB4714261EE3BEAD3F4DB118329D3EBEF4BC48A875C19BA763966DA0EBEA800E01B2F50B00E9DD4CACA6DCB314D00184EF71EA2391D760C950710DB4A70F9212FFC54861F9DC752CE18867B8AD0C48DF8466EF7231E7AC567F0EB55099E622EBB86CB237520190A61C66AD34F1F4E289CB3282AE3EAAC6152ED24D2C92BAE5A7658252A53C49B7B02DFE54FDB2E90074B6CF310AC661 -Out = afeab3b6af4c84fc5b8aab8e33c346c0d3d8f246b3bb7ff38b542dee084f2f6486b52665b51097b35ff62bf63f3a8c690dd23081dc8b2533089b0af79ca788b6 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8 +Out = 486F02C63E5467EA1FDDE7E82BFACC2C1BA5D636D9F3D08B210DA3F372F706EC218CC17FF60AEF703BBE0C15C38AE55D286A684F864C78211CCAB4178C92ADBA -In = 2EDC282FFB90B97118DD03AAA03B145F363905E3CBD2D50ECD692B37BF000185C651D3E9726C690D3773EC1E48510E42B17742B0B0377E7DE6B8F55E00A8A4DB4740CEE6DB0830529DD19617501DC1E9359AA3BCF147E0A76B3AB70C4984C13E339E6806BB35E683AF8527093670859F3D8A0FC7D493BCBA6BB12B5F65E71E705CA5D6C948D66ED3D730B26DB395B3447737C26FAD089AA0AD0E306CB28BF0ACF106F89AF3745F0EC72D534968CCA543CD2CA50C94B1456743254E358C1317C07A07BF2B0ECA438A709367FAFC89A57239028FC5FECFD53B8EF958EF10EE0608B7F5CB9923AD97058EC067700CC746C127A61EE3 -Out = a87fa390fe8f87a6597b111af53926d2b9743b65c0b91b01ed4fa41a1ae55c109b0e9491f29063343a290f58e8f5217573ad9733c01ae70dac6334e1ad42a201 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9 +Out = 1C7A5C1DEDCD04A921788F7EB23361CA1953B04B9C7AEC35D65EA3E4996DB26F281278EA4AE666AD81027D98AF57262CDBFA4C085F4210568C7E15EEC7805114 -In = 90B28A6AA1FE533915BCB8E81ED6CACDC10962B7FF82474F845EEB86977600CF70B07BA8E3796141EE340E3FCE842A38A50AFBE90301A3BDCC591F2E7D9DE53E495525560B908C892439990A2CA2679C5539FFDF636777AD9C1CDEF809CDA9E8DCDB451ABB9E9C17EFA4379ABD24B182BD981CAFC792640A183B61694301D04C5B3EAAD694A6BD4CC06EF5DA8FA23B4FA2A64559C5A68397930079D250C51BCF00E2B16A6C49171433B0AADFD80231276560B80458DD77089B7A1BBCC9E7E4B9F881EACD6C92C4318348A13F4914EB27115A1CFC5D16D7FD94954C3532EFACA2CAB025103B2D02C6FD71DA3A77F417D7932685888A -Out = eb4b329d7e77347a5594da0f5069b796da506ea44a0fc2b705244207f0e33e7d95989897089be8d157a58f21e3cd17c359cea4533735fa67e758979f0ba6f1fe +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EA +Out = 9CE3FA9A860BDBD5378FD6D7B8B671C6CB7692910CE8F9B6CB4122CBCBE6AC06CA0422CEF1225935053B7D193A81B9E972EB85A1D3074F14CBB5EC9F0573892D -In = 2969447D175490F2AA9BB055014DBEF2E6854C95F8D60950BFE8C0BE8DE254C26B2D31B9E4DE9C68C9ADF49E4EE9B1C2850967F29F5D08738483B417BB96B2A56F0C8ACA632B552059C59AAC3F61F7B45C966B75F1D9931FF4E596406378CEE91AAA726A3A84C33F37E9CDBE626B5745A0B06064A8A8D56E53AAF102D23DD9DF0A3FDF7A638509A6761A33FA42FA8DDBD8E16159C93008B53765019C3F0E9F10B144CE2AC57F5D7297F9C9949E4FF68B70D339F87501CE8550B772F32C6DA8AD2CE2100A895D8B08FA1EEAD7C376B407709703C510B50F87E73E43F8E7348F87C3832A547EF2BBE5799ABEDCF5E1F372EA809233F006 -Out = acecc573d277ea7b1f81212b7e9c9347c590f788f8dab30806ec8fadc9decf213bd909ebde0cd81e16e77cd8985fa73aca60ffd27e8dcbf179f94b2c6df80883 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEB +Out = A91187BE5C371C4265C174FD4653B8AB708551F83D1FEE1CC1479581BC006D6FB78FCC9A5DEE1DB3666F508F9780A37593EBCCCF5FBED39667DC6361E921F779 -In = 721645633A44A2C78B19024EAECF58575AB23C27190833C26875DC0F0D50B46AEA9C343D82EA7D5B3E50EC700545C615DAEAEA64726A0F05607576DCD396D812B03FB6551C641087856D050B10E6A4D5577B82A98AFB89CEE8594C9DC19E79FEFF0382FCFD127F1B803A4B9946F4AC9A4378E1E6E041B1389A53E3450CD32D9D2941B0CBABDB50DA8EA2513145164C3AB6BCBD251C448D2D4B087AC57A59C2285D564F16DA4ED5E607ED979592146FFB0EF3F3DB308FB342DF5EB5924A48256FC763141A278814C82D6D6348577545870AE3A83C7230AC02A1540FE1798F7EF09E335A865A2AE0949B21E4F748FB8A51F44750E213A8FB -Out = 135f8392c8598e8edfe202eeb5f93710a322c6593f93c0ee42b4e9d0478e1c3546e9511c0f6d9e6cfaf4e575f15e120f6aff84e56a58f29bcaf0a34d8b7ac04c +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBEC +Out = 4625767D7B1D3D3ED2FBC674AF14E0244152F2A4021FCF3311505D89BD81E2F9F9A500C3B199914DB49500B3C98D03EA93286751A686A3B875DAAB0CCD63B44F -In = 6B860D39725A14B498BB714574B4D37CA787404768F64C648B1751B353AC92BAC2C3A28EA909FDF0423336401A02E63EC24325300D823B6864BB701F9D7C7A1F8EC9D0AE3584AA6DD62EA1997CD831B4BABD9A4DA50932D4EFDA745C61E4130890E156AEE6113716DAF95764222A91187DB2EFFEA49D5D0596102D619BD26A616BBFDA8335505FBB0D90B4C180D1A2335B91538E1668F9F9642790B4E55F9CAB0FE2BDD2935D001EE6419ABAB5457880D0DBFF20ED8758F4C20FE759EFB33141CF0E892587FE8187E5FBC57786B7E8B089612C936DFC03D27EFBBE7C8673F1606BD51D5FF386F4A7AB68EDF59F385EB1291F117BFE717399 -Out = 4ffc1394beaa8989316ce1fe42e6f854aa4bee3c9011f75860a967f304b287b3c686d46b870cd09cd230db2740b5d123c8eadec00beff68a9a8f4179af8026d3 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECED +Out = 43DFDFE1B014FED3A2ACABB7F3E9A182F2AA18019D27E3E6CDCF31A15B428E91E7B08CF5E5C376FCE2D8A28FF85AB0A0A1656EDB4A0A91532620096D9A5A652D -In = 6A01830AF3889A25183244DECB508BD01253D5B508AB490D3124AFBF42626B2E70894E9B562B288D0A2450CFACF14A0DDAE5C04716E5A0082C33981F6037D23D5E045EE1EF2283FB8B6378A914C5D9441627A722C282FF452E25A7EA608D69CEE4393A0725D17963D0342684F255496D8A18C2961145315130549311FC07F0312FB78E6077334F87EAA873BEE8AA95698996EB21375EB2B4EF53C14401207DEB4568398E5DD9A7CF97E8C9663E23334B46912F8344C19EFCF8C2BA6F04325F1A27E062B62A58D0766FC6DB4D2C6A1928604B0175D872D16B7908EBC041761187CC785526C2A3873FEAC3A642BB39F5351550AF9770C328AF7B -Out = 0b922c3e2805006f792d6e8df0feb70166769c8e06ee1ea40f8f5e2755146308ded4875295a6dafc2b1840fc5bd081c8125e1546946c8c473e904c6205ccaa1b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEE +Out = 279E3202BE3989BA3112772585177487E4FE3EE3EAB49C2F7FA7FE87CFE7B80D3E0355EDFF6D031E6C96C795DB1C6F041880EC3824DEFACF9263820A8E7327DE -In = B3C5E74B69933C2533106C563B4CA20238F2B6E675E8681E34A389894785BDADE59652D4A73D80A5C85BD454FD1E9FFDAD1C3815F5038E9EF432AAC5C3C4FE840CC370CF86580A6011778BBEDAF511A51B56D1A2EB68394AA299E26DA9ADA6A2F39B9FAFF7FBA457689B9C1A577B2A1E505FDF75C7A0A64B1DF81B3A356001BF0DF4E02A1FC59F651C9D585EC6224BB279C6BEBA2966E8882D68376081B987468E7AED1EF90EBD090AE825795CDCA1B4F09A979C8DFC21A48D8A53CDBB26C4DB547FC06EFE2F9850EDD2685A4661CB4911F165D4B63EF25B87D0A96D3DFF6AB0758999AAD214D07BD4F133A6734FDE445FE474711B69A98F7E2B -Out = 5db5736f8140f21478bb0a8aa8e0e6792f76a203dd2fc9ad7121786c824085da31d906137256ca1290310a2ddd5780168c5e3acec6a5a5b7455fe78012217540 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF +Out = EA2D066AC229D4D4B616A8BEDEC734325224E4B4E58F1AE6DAD7E40C2DA29196C3B1EA9571DACC81E87328CAA0211E09027B0524AA3F4A849917B3586747EBBB -In = 83AF34279CCB5430FEBEC07A81950D30F4B66F484826AFEE7456F0071A51E1BBC55570B5CC7EC6F9309C17BF5BEFDD7C6BA6E968CF218A2B34BD5CF927AB846E38A40BBD81759E9E33381016A755F699DF35D660007B5EADF292FEEFB735207EBF70B5BD17834F7BFA0E16CB219AD4AF524AB1EA37334AA66435E5D397FC0A065C411EBBCE32C240B90476D307CE802EC82C1C49BC1BEC48C0675EC2A6C6F3ED3E5B741D13437095707C565E10D8A20B8C20468FF9514FCF31B4249CD82DCEE58C0A2AF538B291A87E3390D737191A07484A5D3F3FB8C8F15CE056E5E5F8FEBE5E1FB59D6740980AA06CA8A0C20F5712B4CDE5D032E92AB89F0AE1 -Out = 75ae631ce948c713c408b39b8acd88248938173fc17bf6c161ac3bcfabca2c134d61ca2e87a31468420840835c957180e60c2c3e21405638203cd2f58424f9f7 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0 +Out = 49F014F5C61822C899AB5CAE51BE4044A4495E777DEB7DA9B6D8490EFBB87530ADF293DAF079F94C33B7044EF62E2E5BB3EB11E17304F8453EE6CE24F033DDB0 -In = A7ED84749CCC56BB1DFBA57119D279D412B8A986886D810F067AF349E8749E9EA746A60B03742636C464FC1EE233ACC52C1983914692B64309EDFDF29F1AB912EC3E8DA074D3F1D231511F5756F0B6EEAD3E89A6A88FE330A10FACE267BFFBFC3E3090C7FD9A850561F363AD75EA881E7244F80FF55802D5EF7A1A4E7B89FCFA80F16DF54D1B056EE637E6964B9E0FFD15B6196BDD7DB270C56B47251485348E49813B4EB9ED122A01B3EA45AD5E1A929DF61D5C0F3E77E1FDC356B63883A60E9CBB9FC3E00C2F32DBD469659883F690C6772E335F617BC33F161D6F6984252EE12E62B6000AC5231E0C9BC65BE223D8DFD94C5004A101AF9FD6C0FB -Out = b9a9ef514060dedb1a47ec5d1149cf1593a0b3379fd28de8f7da13750320b870675f9ffbc2df1ff8bcee74458b01780c0683e85e164baa4c530e655845fe218a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1 +Out = 9233490344E5B0DC5912671B7AE54CEE7730DBE1F4C7D92A4D3E3AAB50571708DB51DCF9C2944591DB651DB32D22935B86944969BE77D5B5FEAE6C3840A8DB26 -In = A6FE30DCFCDA1A329E82AB50E32B5F50EB25C873C5D2305860A835AECEE6264AA36A47429922C4B8B3AFD00DA16035830EDB897831C4E7B00F2C23FC0B15FDC30D85FB70C30C431C638E1A25B51CAF1D7E8B050B7F89BFB30F59F0F20FECFF3D639ABC4255B3868FC45DD81E47EB12AB40F2AAC735DF5D1DC1AD997CEFC4D836B854CEE9AC02900036F3867FE0D84AFFF37BDE3308C2206C62C4743375094108877C73B87B2546FE05EA137BEDFC06A2796274099A0D554DA8F7D7223A48CBF31B7DECAA1EBC8B145763E3673168C1B1B715C1CD99ECD3DDB238B06049885ECAD9347C2436DFF32C771F34A38587A44A82C5D3D137A03CAA27E66C8FF6 -Out = 2c246116303a48a5476760353e5c1c1fa3e7c0d3a20a1fd4a30e3665fdc4a8c70bf71ed1dbfe61f58b76d17cdd3aa816bd51be5017d9c7a1406598799a5c4df2 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2 +Out = B6E75E6F4C7F453B7465D25B5AC8C7196902EAA953875228C8634E16E2AE1F38BC3275304335F5989ECCC1E34167D4E68D7719968FBA8E2FE67947C35C48E806 -In = 83167FF53704C3AA19E9FB3303539759C46DD4091A52DDAE9AD86408B69335989E61414BC20AB4D01220E35241EFF5C9522B079FBA597674C8D716FE441E566110B6211531CECCF8FD06BC8E511D00785E57788ED9A1C5C73524F01830D2E1148C92D0EDC97113E3B7B5CD3049627ABDB8B39DD4D6890E0EE91993F92B03354A88F52251C546E64434D9C3D74544F23FB93E5A2D2F1FB15545B4E1367C97335B0291944C8B730AD3D4789273FA44FB98D78A36C3C3764ABEEAC7C569C1E43A352E5B770C3504F87090DEE075A1C4C85C0C39CF421BDCC615F9EFF6CB4FE6468004AECE5F30E1ECC6DB22AD9939BB2B0CCC96521DFBF4AE008B5B46BC006E -Out = 3d7ca411ac45d5f1e8980e4729c66a86f4d5d5570412437c896b9e226c7af57443d419d456dd97a561d81f89d53595fdb45dca968e4f0c155fbfdfa344cd500e +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3 +Out = CC14CA665AF1483EFBC3AF80080E650D5046A3932F4F51F3FE90A0705EC25104ADF07839265DC51D43401411246E474F0D5E5637AF94767283D53E0617E981F4 -In = 3A3A819C48EFDE2AD914FBF00E18AB6BC4F14513AB27D0C178A188B61431E7F5623CB66B23346775D386B50E982C493ADBBFC54B9A3CD383382336A1A0B2150A15358F336D03AE18F666C7573D55C4FD181C29E6CCFDE63EA35F0ADF5885CFC0A3D84A2B2E4DD24496DB789E663170CEF74798AA1BBCD4574EA0BBA40489D764B2F83AADC66B148B4A0CD95246C127D5871C4F11418690A5DDF01246A0C80A43C70088B6183639DCFDA4125BD113A8F49EE23ED306FAAC576C3FB0C1E256671D817FC2534A52F5B439F72E424DE376F4C565CCA82307DD9EF76DA5B7C4EB7E085172E328807C02D011FFBF33785378D79DC266F6A5BE6BB0E4A92ECEEBAEB1 -Out = 3c2bda5f81efc825b7ad7df07eed5d55b02b1a81165c95830dbc6281c4f8edade4158779e1afda7ccc0bdf0f9c09b02ed979b9291d67b92c58d8c443e981a471 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4 +Out = 230A1C857CB2E7852E41B647E90E4585D2D881E1734DC38955356E8DD7BFF39053092C6B38E236E1899525647073DDDF6895D64206325E7647F275567B255909 -In = 724627916C50338643E6996F07877EAFD96BDF01DA7E991D4155B9BE1295EA7D21C9391F4C4A41C75F77E5D27389253393725F1427F57914B273AB862B9E31DABCE506E558720520D33352D119F699E784F9E548FF91BC35CA147042128709820D69A8287EA3257857615EB0321270E94B84F446942765CE882B191FAEE7E1C87E0F0BD4E0CD8A927703524B559B769CA4ECE1F6DBF313FDCF67C572EC4185C1A88E86EC11B6454B371980020F19633B6B95BD280E4FBCB0161E1A82470320CEC6ECFA25AC73D09F1536F286D3F9DACAFB2CD1D0CE72D64D197F5C7520B3CCB2FD74EB72664BA93853EF41EABF52F015DD591500D018DD162815CC993595B195 -Out = ae7676911f5f0dd933707ad7657ac3830a85a5b6f13365fc57b4b8f18e0828943b4988a8955f7677e00ae7b697235943347a7a45f4ae7a159624575e42deb0cf +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5 +Out = CBB65321AC436E2FFDAB2936359CE49023F7DEE7614EF28D173C3D27C5D1BFFA51553D433F8EE3C9E49C05A2B883CCE954C9A8093B80612A0CDD4732E041F995 -In = 3139840B8AD4BCD39092916FD9D01798FF5AA1E48F34702C72DFE74B12E98A114E318CDD2D47A9C320FFF908A8DBC2A5B1D87267C8E983829861A567558B37B292D4575E200DE9F1DE45755FAFF9EFAE34964E4336C259F1E66599A7C904EC02539F1A8EAB8706E0B4F48F72FEC2794909EE4A7B092D6061C74481C9E21B9332DC7C6E482D7F9CC3210B38A6F88F7918C2D8C55E64A428CE2B68FD07AB572A8B0A2388664F99489F04EB54DF1376271810E0E7BCE396F52807710E0DEA94EB49F4B367271260C3456B9818FC7A72234E6BF2205FF6A36546205015EBD7D8C2527AA430F58E0E8AC97A7B6B793CD403D517D66295F37A34D0B7D2FA7BC345AC04CA1E266480DEEC39F5C88641C9DC0BD1358158FDECDD96685BBBB5C1FE5EA89D2CB4A9D5D12BB8C893281FF38E87D6B4841F0650092D447E013F20EA934E18 -Out = af3aeb7968ddd9d60a60851a430d7970297e32e11a9df17ad6a302b0805d1995b1d81cef0367297f61efa82093888f281265b997709ececb5f3fc6de6a54b6bf +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6 +Out = 3E7E570074337275EFB51315588034C3CF0DDDCA20B4612E0BD5B881E7E5476D319CE4FE9F19186E4C0826F44F131EB048E65BE242B1172C63BADB123AB0CBE8 -In = 023D91AC532601C7CA3942D62827566D9268BB4276FCAA1AE927693A6961652676DBA09219A01B3D5ADFA12547A946E78F3C5C62DD880B02D2EEEB4B96636529C6B01120B23EFC49CCFB36B8497CD19767B53710A636683BC5E0E5C9534CFC004691E87D1BEE39B86B953572927BD668620EAB87836D9F3F8F28ACE41150776C0BC6657178EBF297FE1F7214EDD9F215FFB491B681B06AC2032D35E6FDF832A8B06056DA70D77F1E9B4D26AE712D8523C86F79250718405F91B0A87C725F2D3F52088965F887D8CF87206DFDE422386E58EDDA34DDE2783B3049B86917B4628027A05D4D1F429D2B49C4B1C898DDDCB82F343E145596DE11A54182F39F4718ECAE8F506BD9739F5CD5D5686D7FEFC834514CD1B2C91C33B381B45E2E5335D7A8720A8F17AFC8C2CB2BD88B14AA2DCA099B00AA575D0A0CCF099CDEC4870FB710D2680E60C48BFC291FF0CEF2EEBF9B36902E9FBA8C889BF6B4B9F5CE53A19B0D9399CD19D61BD08C0C2EC25E099959848E6A550CA7137B63F43138D7B651 -Out = c15611edcf5bf5fef25c36edb456dab9a1bb3d958e55a90cac8261ac1934a72adf086198fcc9461e8cf386e4449b1bf664d2b916db8287da45f15244008c0823 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7 +Out = D32E9EC02D38D4E1B8249DF8DCB00C5B9C68EB8922672E3505393B6A210BA56F9496E5EE0490EF387C3CDEC061F06BC0382D9304CAFBB8E0CD33D57029E62DF2 -In = 20FF454369A5D05B81A78F3DB05819FEA9B08C2384F75CB0AB6AA115DD690DA3131874A1CA8F708AD1519EA952C1E249CB540D196392C79E87755424FEE7C890808C562722359EEA52E8A12FBBB969DD7961D2BA52037493755A5FA04F0D50A1AA26C9B44148C0D3B94D1C4A59A31ACA15AE8BD44ACB7833D8E91C4B86FA3135A423387B8151B4133ED23F6D7187B50EC2204AD901AD74D396E44274E0ECAFAAE17B3B9085E22260B35CA53B15CC52ABBA758AF6798FBD04ECEECED648F3AF4FDB3DED7557A9A5CFB7382612A8A8F3F45947D1A29CE29072928EC193CA25D51071BD5E1984ECF402F306EA762F0F25282F5296D997658BE3F983696FFA6D095C6369B4DAF79E9A5D3136229128F8EB63C12B9E9FA78AFF7A3E9E19A62022493CD136DEFBB5BB7BA1B938F367FD2F63EB5CA76C0B0FF21B9E36C3F07230CF3C3074E5DA587040A76975D7E39F4494ACE5486FCBF380AB7558C4FE89656335B82E4DB8659509EAB46A19613126E594042732DD4C411F41AA8CDEAC71C0FB40A94E6DA558C05E77B6182806F26D9AFDF3DA00C69419222C8186A6EFAD600B410E6CE2F2A797E49DC1F135319801FA6F396B06F975E2A190A023E474B618E7 -Out = 499fdf1f42f1a5bb7b4641309038ca0533902c4bcec8f1d6282d20a471b428e05256964502b67c71cc2d816526959e26c805c30ff858eebc29ee984bf7210ea9 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8 +Out = 8C1512466089F05B3775C262B62D22B83854A83218130B4EC91B3CCBD293D2A54302CECAAB9B100C68D1E6DDC8F07CDDBDFE6FDAAAF099CC09D6B725879C6369 -In = 4FBDC596508D24A2A0010E140980B809FB9C6D55EC75125891DD985D37665BD80F9BEB6A50207588ABF3CEEE8C77CD8A5AD48A9E0AA074ED388738362496D2FB2C87543BB3349EA64997CE3E7B424EA92D122F57DBB0855A803058437FE08AFB0C8B5E7179B9044BBF4D81A7163B3139E30888B536B0F957EFF99A7162F4CA5AA756A4A982DFADBF31EF255083C4B5C6C1B99A107D7D3AFFFDB89147C2CC4C9A2643F478E5E2D393AEA37B4C7CB4B5E97DADCF16B6B50AAE0F3B549ECE47746DB6CE6F67DD4406CD4E75595D5103D13F9DFA79372924D328F8DD1FCBEB5A8E2E8BF4C76DE08E3FC46AA021F989C49329C7ACAC5A688556D7BCBCB2A5D4BE69D3284E9C40EC4838EE8592120CE20A0B635ECADAA84FD5690509F54F77E35A417C584648BC9839B974E07BFAB0038E90295D0B13902530A830D1C2BDD53F1F9C9FAED43CA4EED0A8DD761BC7EDBDDA28A287C60CD42AF5F9C758E5C7250231C09A582563689AFC65E2B79A7A2B68200667752E9101746F03184E2399E4ED8835CB8E9AE90E296AF220AE234259FE0BD0BCC60F7A4A5FF3F70C5ED4DE9C8C519A10E962F673C82C5E9351786A8A3BFD570031857BD4C87F4FCA31ED4D50E14F2107DA02CB5058700B74EA241A8B41D78461658F1B2B90BFD84A4C2C9D6543861AB3C56451757DCFB9BA60333488DBDD02D601B41AAE317CA7474EB6E6DD -Out = 024b16b48409d87c7867c1beee9ac489c20699f6da86d3a1fc95743130b436998aea4e0ea9a52bd7d0574d572e6346db3c8f5b5d29c9220e720d3f692981657b +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9 +Out = 91A7F61C97C2911E4C812EF71D780AD8FA788794561D08303FD1C1CB608A46A12563086EC5B39D471AED94FB0F6C678A43B8792932F9028D772A22768EA23A9B -In = D1890B4704E169C28E44DDF62A1091450404910539FC2DAEB26E8ACF4533B024E5215C2D02820DD8FB2CFC1743955CBACFF0F8F35DFBB5E3F942F36247F68211D518F3F601AAE12A1CDC000BAB43D4C973F287E80741DD1FCF6C34F2E6B4B6C313D01C4FF3CBF9166F26946F18EF2D58271BA9233F09A6B77BFD4F48B36EB3D73D1133C4F842A7DC3907F680B0B773242C11E3DD973A44327EA7CEA9C0F8E07D682B6651E506B587559FE01ED721000BAF570A16FBDD9EA29FA3DEF4BE912058321A8B720C5C102E48A6E7ED6F8838D400DD57D06EEDBCD15323F86D855C94B21E41B14EC9E1BBC8019211FD88138C91F9ABBD9BB3914D26C1DDC21673D2D51263B39D66E741D924CF2B192C5D2C1A140126A3D64A2C77BE6C2C6EBE8599978AE90BD36CBB9AF64D078910C4094AB3BF399C34F2AB8EF843E9FE1BF88BF443BA21E4377E5F49C07FD9653B526E14562237F02D11B904BCA6AC31AE721A43E3C4910A24AF6F4D80C031C109FC0FE49F15274BCA92BDA04C3B4196C192F6CE489C63A806ACFC895AB52CAD657C1783B528E12D0ED856E1F8FC91F2AAFDFA0A92498D68530772EE73B359FCF1418D1096C46B34DCF90E5B468BBB2970BECBD70089CFB039D64CC50FFF5EEF26384D34F24515A6558B06A1FDD88F1050C5BD78CC6ED83D4C2B0E882AEBCF84AFB0430D0BF09F2FB42B8B4589158093A7709AAE75A790910E211EE1333FFB6FD80778DA3BF73858978E9DD647978841B18001DBAAEA43CA0C0A03DBB9BCF30CE76A6F4B2CF2A9B6531B3E4051E7E05090CD421BC66C4731E7122AD129FC42DEDC83BB460E3F889992FBD3CA072686E56B72C720FBC98D723EF7F247286F77CCDDC728738E941B1A74D4F16671C21FDD5643A115DDBCB88EE7EC67EA66FD2BCE718DF6E085D4B5FC71A72696636A8F7B3A68AFA51A896771FAAA7F1F827430AC5E8089DBC0D4175E1B22A057BC5F1724EADC1A41E78FA3ACAA8B97E5F2E19EF9D59AE12B04E7F0E8A621E098A66910E2A5ED2102B824CD3EA044A854F1CD0B33E61E7F737414B2953549F25DD34D19AA1981DE7CD5649FF6C6364A4F25312EF62395A747AB88AAD722C05AEC40DEEA8EEE5E779EF458A68840BC6BD5D29AD40F98B3AE010B6213372ABB7BB8B8 -Out = 870d4fb4417e01b05dc6cac65eed06b70f8cc12ee199dd96f6b2cd860a14380b87165620b7d24b77e5a1634e2a4bda67c3927d0513633a5b1a7abf37c83a0d2d +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FA +Out = 4F6BB222A395E8B18F6BA155477AED3F0729AC9E83E16D31A2A8BC655422B837C891C6199E6F0D75799E3B691525C581953517F252C4B9E3A27A28FBAF49644C -In = 4FA3DF1DEA75AD4B9C379206A95FED930000482E5B683FD2B17DC8E7D5C4BC1B73186CCC13C9FF2DD09FC1D4F68034D120E84CA73A00B71A3B46D1EFC6FF88CF2EDA65810B098CC5E651D9CF064E87076D5A871849F3B405D3D58EF5B1F10520A9FB4FC84A81A87B13DBFBF9D8674943E28C257E46D8AD7BE1785F1DC7C9B1BD574AD1DDA48F0255C853D2490BD3D63DA22A8369CFD02594999A2EF443308FB8298266A11EFA177102C75DC674E89FC9DCC1A0D3C863BC26141102175D2678EB6E13D90BBD9A5EB89AE8C0CB47D7F340D3D32042A2762BC9BF2B40EB40E87FB42610FE7E357051F01494704FBFF73321B47301A0799B7EE3FE5E62200F397A61ED4509A62F7106ED0EFB0ABD6AE9E4A1FE9B02C092DCDC75015CF602F3B9A8988B609E6C0D1C5C3E219FF57875C2EF01615F89447EA602DFC94EEC17A398C014BD346691FE209A002771DC8164422CD166AFB457A8B3071282178A3EBD201D9B07B27E711E7EE7D33AA5210ED4E4E92486775D14A6CED092E34A7AC82670939948FEC149F9C018FCAAD3FC597D315713F44FC5E1725F448ECAED40E8D841BD02F1E81C019B08F99412E360C0BD378391C67D964B47F50C26F0A483ED664023616B0FC9AFE43620DBE9CCFE070EF295C049EAC754C2123130C6B2C0232F6403AA7F0DC35A5999BF95D34AD612234C6289277ADB60E4F72EC2DF570F05395B3BE8A0A3C78B732821AA08927C524E15D65F66A3DB8C1C96FB70BC0686AAC310051F469FC5EF880C0F66947C1C328F97684EA24CBE63BAED8D114F40507C2901034E6AB3893F366D53F1CFCA309309218CABCECA4722FA9CCBC7249B87C12FF8397F40487EB00082E7F551D27E301C3BC7B5389F7042534BF7E692DFEA4DA24F7C34B8D2FF145F54B517FC97134EC5AC2CB925C508D7A6BD01FE7B764648274972BF08560D30802E0EB7EDCC57AF4797BBF92E8688268606B0F1BC901FCC22136281665EC16393FA9601C4FBDB18CD1D1EE382BC07973903E91FFA87399D1141D49F4F0C064ACF3AC9897891DF10BCA0116F2C3FEF180FE6A8E937C478F2EF293AE9186DCB1F76B6E48101DF64E57EA7C64C5C0025E221C8F5CBA5CC92D9CEC628140996B26D17F439B780F59A999301122F82D0495F8AB5AE1EA5790F45E992DFE00D5F82A7FF1354AEFDCEFC0D2D1731D22FA2B75AFD4FDA25AB194055FA9628381055247C8C7587D22E73C60136C4282452D47AE03AA035FEBC26FCCD42A1CB79CF866DB6418A49FD8261E877DDBB839CC39514DDB87A8A40D795532626FEA4A4C35D13E028F9ED1BC09B06BE999B8DDD2258AA0596BCBBF72AF67E10BEDD58D599B8D577A583D676BF5561F80CE5E9528729A92DF578FE75DBC70474B75747A8D55DE70E57BDD62D4344DC2115ED4DD62F1FC98BFA1E7421FC0700025C46D0ED1BEF35C3B778563211B9FA9E8BA4BBCBF01C2FB626AB7EF325CE9F468DF2CACDB178D36557CD85D542C067C289E926C1EA2F20ABD329E984168BB6DEF1DDCCF214DCB6A53AFD462F0E7E7A19E8C88F049244125A6D7DD41E58BC9B2FF7FA2478DF76AF73090CB1AB59E388BA20E2C297C967737A1AF61793B68ECD7439444C48E28E2D09C48FADA5E0D1D15E5B340A52F8B3B854CCA479F0A598445E14F53B3BA36891050C79673DF3E2B5825C955A29E5C9A22F3991D0AA785718CFEA1D2385F8E47E4A75ACBC7988D0558D541D71C4E6C5F1CB15B60CEA0C34A67BBCE105D7A896025E0254DE7D7AF724C9027D44B8642192A08AB8E1EF3046DDA6014DF7F4C9E63C635E48AB2E70B640D480998EC9357E665F99D76FE5529EF23C1BDFE017C3A66CD4EB2DDB42EF85EA0CD65534 -Out = 23fc1456e95c5c0b805fb288e5f0f3d026ced63c8ede11f40f0ba5fb217822e7b3f6eefe7015941259a83819adb9a554e6512340665a4f2543118a51c080f10a +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFB +Out = 5D06C07E7A646C413A501C3F4BB2FC38127DE7509B7077C4D9B5613201C1AA02FD5F79D2745915DD57FBCB4CE08695F6EFC0CB3D2D330E19B4B0E6004EA6471E -In = 523DE8B1F4CBB65E81FF0B6CCD6EB8EF0A0F0A691ACAF4A77F25ACD2D66AD4B3EFD25BE70308853C094412A518A32020E3020A9F6AB32F0CD60EC0D7A194917D6C457B168A54A4B46F7B0D0C71BD61CD202F4C718776A701E0770B0EFA05418770F98E4E79CD066366FB3300E8BE359A98B82B764BC2FBBF59C7E8F94A157B01C6C7577B1428138CD422BC47330F8ED99F4C0AAB0D984287445539839389EE08D6345108AF26ADED0EC1D7BE774CFB8C5205DFE07CF6CAF8C1AFE37C7A2E4FE6013B93EB2463DE4E0971C7178D6A76B16A0E8960C984CE8BBE71B3B466EDF0445B835F09414D01F14C7B6167FF78FF118127BBD5F812C27FACD57B3B120E2BCFE87315C7A92B82EF5D50CA14A7174D1BEA7E056523E055A6AE42EA3765094E5544E5ED003C989C2F98F38A17E3DDA74DBAF9C669A319638A2698B0E4A611480D8AD3CF016792ECD1034925F42B9811A7214D623D047ABCA31997DDEB03275F80DD21F40DDC80616E7AD3D481E8EBC0A1A6A398E16A78369215541ED10B75671ADEB1AAE6E11142A1CF665FC1B7332DFBB0E10C21A2B48F78E57319AC9C58DFA8B1C2548E2979EF1ACCFEB215AFCD6C2C1B46FE97DD491758378330EFFC7283661D2CB84FA05281E9E517408508D24D042E7B9BCD34DB87CE972E4CBCDB98615FB93093369DFEDC782F44BCD03E81CF93051318B2401FF29F753A264BDA65AF199E3FCBB8B5D39C838A67D6C7A3DB046DC56C323DDBB5340CBC229E47CFF8C9D29B7A49AC0EC8C1440AE498C7D150EF91C29BEA7DF3EFCC2871A13A1D72D139CB4603D9FFFE85F6DDD544850EF63C3944FB35DBC00D4308CEAA6394B6E23F650D323F8F7EF50DDB68F1486EABF989BF44451F620EC9485C0B52D1415D3C909A2CFBE9D77DB19D069D33BAEE4D77292E63FCBF65C1EBA24BFFDDEFE95211EF0AAF8ABFDA9F94445E582976F986F5382CB669506AF2B4A5A0C43000A3C72C5CA4AACDC9D3D39FC5C492A393B6C341B86DACBBF6BA8B465100CC683EDB2D9B9F83EDF9C6A32645F51CC79ADC22A52A007BAACA618BE35E356D1FD1CFBDA73F1ED09253039DEF609450FD2D5943B9CD49CBD52A318EE3510D7CF3FD8FB388AC6CB9C6EEFEF3D3CAD8501B91CC04A888D33E16D6A4C9666F5F5F3B257193F2B46DEDDE11842909D8C48ADE57775B0B272E2DC9CEF1A083EB2CE58F4D1F211922FD6ADED1B82FE6F5B11251CD396E5A3666ED9626036E4E356231C146BBA0A91AFD3648EB7BFE0B9C14F15AF2F92309826F468945CAD0AC422DE3D6A773B76178422107CE0270E7F580B5CCEBA82CA0184AAFA8341141E65E39859885768FBC5CE63B965A0604B659E71D9DA2C7A43646088D8071D76926163AAFC69E25355BB0A222B7B2DA9F0A20C021ADC462E905A9C3BF31C16D87FBEC3F014F3957A720F1432E1741553092052FB58A198640479ABCAA51B104CC93E2636E1460643EA812BD44E819C2166EB6B349BA5BDEBAD59078910B5C22A56F004B8D9E4B1224D8D204B48ABE7355548A402736C5CB110F3A1476ED631FF168F4F3EFD89B38DE4751536548647523D334FAD7CC2D142973F2DB3C1FE08FC5CF83F9F2BD2DAA524B37864816AF29EE05951FA09D1C51D9D14EE4F72FD7BBF18B1A724FF5A0958A063947C430142AD2356E4400AAECA442E163372A8F1CD36E2DB988E7781165E5D4E7074ACE40858E8370E883694AF09977704347FB735C8717C42BC4EEEB2AAA50DFE637C640909CE379BFB9E2608F88751377038D1669F248178AD580A908D7A1B8DCC7E53E01801F1E485B5893F103F03E0F53B2B1440BE95644D85AA7F6EB7EDFBB46652196695EA23C08573397B111FF909025E20C5201293B4D223BF7AA01DE7CB28B94714370434B9588097E2401B62C7A0DEF1FBF89809E810749FD3CE9EC3C07CE4BF4C43DC966429B2BEB4D711FC6C448A12097B36F1E6817EAF4937A983F85D9CF3E62CC1B2AC6AE1EC9EAA8CD8EE2C3322239CFE5DB3D4E8786282E630A7D259C2FEFECA03031C960A66A71E436A3ED6F2F3CFAB4BD77C660D14205ABF606FE561A346F7D849B69475AC9F6822D80B9A2E56D5D495E4B309B0EA963C9FC5C7EF94B217EE5337989AFBC7107D233A8B362AC27C4F69DF9E191CD65AE97D6EB9E5484EB6F10349575E4CAE51452380151F902415AC9CF42C824EB23C9541D2DA1C26DB85F53CDAFB06A12B8393CD580A8E494EDB6710C720DCAE30832967E33E6303A92B1DF0841D7724284FFD2E00B95C6D623B168D21AC1BD3C675EDA33182A2C22370998DE1E5EB905372CC6EF32D5B765F5C94870DF4842D011603BE4CDB1C227E41EB2F2E8542CD325884FEDC9C5C7BB07A92D20D64B836215C59F162A3DA8BB67D6FC13FEF97CAB6ECB8A29E431A6519A6261C4521CCB90E6E609869E6FE398404AE047F64EC4263566DEFEE66329DD40AC985EB8A08D26529A544891B6F57CC235C63C09057AB6B6ED720EF41A3C9AE65768B43F6DCF4962A103DD93C213171DC2C9194E43265C689B49331450281A3FEBC618D1AA4D65A135137051FD46B568CE294C89 -Out = 55579a82d97586845c32a2cbc776b39d93a7ec26dbc4bc7540acf2c0921df71989aca1125b4fd6d8f18de53cfe33432bfc1a5e412db46a4dff0256551299c264 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFC +Out = B96756E57909968F14B796A5D30F4C9D671472CF82C8CFB2CACA7AC7A44CA0A14C9842D00C82E337502C94D5960ACA4C492EA7B0DF919DDF1AADA2A275BB10D4 -In = F5080D4C59E804BF8F34B334CABBCC7D32011BDE3677F4B9069416AC204114CD9DA7A0ED0F4B4D8344416336EEC15553EF526B6DEC267B1242657DD0B508AF81FECF9CFF9C82A6A7A9539814DD7E097615EF15373836B5D2F765CC8D5F82E90449F13AA741D5EE2FE63898E55ACD85116846807606FE1E2E29F98F9940B067D0D1DF01F080211B2EE4B0A30803782A7BC2EAFDC5EBDBA91EB05F7D7DC8E34BF6D44FEC05824F53418F235FB64E899EE147BCB403C8855E94AF378D182D79C3EAF977CB4E9D4A16D990A6C388CEB567B97785E6F2BC6745102B99AE765E960B6B32BAF01E2379CD6ECB74D3E1A56552F5976DFE5C742BC92BE596CA742FFC3D0FA032AC29F9F7C1A5C43BCCA62DF7D9DE35D0C7C179DB2E1AA255CEDCCA55064C2049FEE1AF2CE5EF696ED4BC46B7C55BDD51F2D44C8713FB2475C0B85246AC0103CC3863B7EB026AE076A600313F6FB40A4DF62A2AF81B7E917951EA870ECB31B3401928B5046D9A1E62D14B30FDEBAF262868517318FE17EC3C0D52524F44120ED8ED3BA70C643300CD0BC70DA72C964A88F52C3A91EC20BFEB5CAEFCD4D9C7685D8407476B5F34676C5EBD1E88A6CFF1C625322F8CD59B9ED60CEFB21F9491B95E72791F7AC7EAA3C16159FE9DF7A989ADD6C2282C47585E11397EDA9F47DF2B40166E03BCDD6186B46C6835118268DDBEF19A28BBADE1BDE0228FFD7E8B3C3C598D89E24B8CDEE79C940254DE26CC6814BA2722E42F7571600B7325E1FF300251D52A895B8CCBD049B2953B8D231445F68F7C26EC25A4B8695C8AC116F736BE939EDD762C9B4743E463C9B9B2F88E0BC0CE78781CDDC3BCA825ACD463C7CAC2AA6C430BBE820EA94AF9A40B1B5C006E9641A2FFA6E427379E1AD49C81B98320B3431FF0030DC683D61026438BC6A6D34B2C73704D9F62EAEB13ABB3E4B0562B4E0482CD6B2D7AEBC0367EA29A88F4A76F3D76FA1197E1DCA92C8216C84C1AF9B8C78C9E3A7799A4A79A783033B0F5547E8E75E69CF3615AB04EF989FE1A463B1672C571D50AB56972896E8A50C242F22C7F6E27CA4CA793F627E79608680F5421B28BDD2589F05E65430DF774EE873FCD1234064F7A33CF5A1FA4E368137FF9C1597F1FA0FA36493F20538077669EADFD3B06F788C912C715FB5D334DB6BED133A8FDC40F5496E66AD63881F0BA3727416715865253DC5290327B515BF68DA188DD5B4B0EAC7CA712CAFA8FCAE0C5503FE58A219182F1C30DA6D0C19CFEE897B7D837C97996A35F4CA8CF0537A01D17E7DE0CC9C129E4DA0ADAF1FDA85030DF9127BE628263B0624F372C47C3AC87EB945A57F5C732BEEE81A7403001798992F3DC944114FF3D54C4666AC5AC8C98D0D5596CBDEB420665F5EDAAE747D54CF7EDD37B162E372249D135938CF17D174D12D88279CB4C32BD6F018C766DA6983D4EA51D6BD8FF0A9B34E9A93BBDA70CF1B4B867D60A74811FD98D52FAA559B52C755CB70A76C94BD19654CAE7017CCD70222BF08C5D7AD1F5E4E6344FDB3ABE703452C29A696F39F9826ED8BC510A4A148E5BF8A5DBE6B82D7220164F08011C05AC5159D52CE9D45D758B645BBB248C2D341DBEFA1F8602C5D458A64F38F3B04DB39089807B6A10E1BB52770B92CE72E2D3BB0C2241CDED35054B84558D1CC099EF7B2296951951D5B6A22F93BF962AC5EF8FB55EC6CC2B316428EDF12078ED1B66D525D022819CBD489E1BEDB02FFBD507D55F9B5D4E22F6396EA233453754688D20151A09C70044B8A5A9AC033C3C3B847AD833D5C05B33407666EE82F9581DF9034EE15A9CA67D52F1D9B634B84C1B8BA9E515F1F060A5AC5CBAE2DE75F94E112F7198E239DF08D3103F065627438995026DF511C6E5BFDEEE5667D511D4181850C7C5D179107C1B86D24D5532A88A4149A2810DCAE73731B0E1247281A6FD31613DF6891B4C17B7A6A9AD9B77468254B93F85958AA0F01CEFC10B25169DC46E035D3F24557B4BF0E7D60174219108D916FFDC55E25BFFD9809EFD058E12C14F39C69D8FB73D3EC6458F47F2F8DB901BA76C86550B11B54D0641D4DB3EB000057DD00F2E511FB7A47E959A4402A3AC5462234B40B184020FCF7A0396C4D00A987C8741A4537BC17102A5C42AFEAB9F71EA66ED4CBC7B5EE682FF04F56F4BA1EA0BB326C4089930F9E3F3FFA3E06637CCE32113881A06CC3A13837448145C2BD01307A580FDBC385D8F46FB92FFEDBC8918D269DD1871164D4B3E2023441EC8B99C82A5F09821CDDF6B38C9ACC3BF3A38D5628016159588C33EAA29D9463A537C000A16AD8C177DC4CF716E625F46FC4CA8C19FBD8EF320F1D680639195C8B195B0A02738E0665F4190D6287E589CD6DD45B9E8CC23B08E1681BFC6F66B88DE6B091E825EA4BBFBD697E10BC407570AE4F2A3EBE569554639C2B8E051656CC30C837F5A92260EAD1D552B45801B6D28134166796C87F900225CFDC3CC49D72DFBC18D8D95B1E160ED3CAFD5C3467D48AFF87402CBCB1E1420E3FCB588AA19C8F42753B59DB6FB6A9FDBA127CA806DBA7DD97F2488FC2E438EEF57A4CC85B88DCFDE76AE1FF61225A1CA8BF4A14F729950322EA681B16D6492902506702DC8F348E4D3AE7FB55FAC1231FDE82091B34F1791B6AE37587B10325F6FF5E23B855845B86EAE90785B9D10D90A16644D01BB626F343B908A9591F4069B21822CA4ECF985C1E710475F33DF9AF4764CFB0FFE649063775338F15BEA7CFF29F164678160960A80ED148C9B7FAA58E9139911D3DD9536F69646F718F083DC9029D6294FC4C607688AA75AF350AC2C0B001A157D023D73D86ED8133809FCB9592D12089CBD7A1BB6BBA882FE227C09A53FF088907CB4BC2FB4B7F62D41D3D397C4FE0AD12BB3964370E21712951C679814D506E738C0201E42181D231136A435AE0397B61CCBC5E8BBEBF8EA77C8BC48BD6211F29248F9D498D818E2B544D28A5E60BA727F32EF4BA2707962230C900076FB764D0ED5CE078C9DB14DE894BBB836C6DE9E83202AE89F9A8D8CB0341E1C81B5FA8B16731B8E231E969C0F1EF95336D4E73EAD6DA23DE3AD1EB608ACCE4D4D93996DD76EC1F5F2C576F6B3B76E07BD8A810FF5D88B00FFE48C42700B61CC499336E7FB57AD72FF44FC631C7222C9A3D1ABF6E77B5ED7FE2F7228FED6C849BF7142C4103989A80F7C15642AE61650CDCA7E854EB25E9E72F4C3E3768E6CCC8BFD556B56D3507EDDE9E5C331DDEA75568B07813D20E8F4C9547838ED28448F2E67158ACF0C00B131473847816C5E2DC215 -Out = 8199a6466cca59ee6f770cc24531397acd3f827a6dea1cb8882fceaed9145a515731429476fa343b528d2a60137d4717ddb58be19afd5cf69e380e316914ffa6 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFD +Out = FF0A015E98DB9C99F03977710AAC3E658C0D896F6D71D618BA79DC6CF72AC75B7C038EB6862DEDE4543E145413A6368D69F5722C827BA3EF25B6AE6440D39276 -In = 08944CB473B828B118A31986DB67FC757F238182E790553404B792AA4F0095A6A83291E287CDD16521A3AE8C48F56FBC909DFCCFAA7BCC570C2159F26592DCD6B15BC4DD55CC05595AC634B2C3DE15360B0F07A03B5957BC9333CC5097919399DD9973ACE15E55940178C4C96BB5E0A0A10BAE175769548EBCE11E0D7D9DB29647F197D4B87F7039F5D4E59E016531DBEBF55A797AC9A6835032CDF34240A7EE7423E89C09124829CAFC5F89431C8AFC54FD979E50D48A82B47A53523C84B6004DAA323EFB708203E5388A6A5110C6CE2E341048A65FDEADEB3837A03420F9FADDC3F02A544F1E46D96B07C90C7971A7040A179E8198E90AA019268E00367120D5F3D98A5CCE82C885E77144B1AAD66EE682847776B04F01F501DCBEFE3903080A8058B3B8F1D823D917ECF31FC2D5B0795BF95A55C7093ECA7C801DD0BD0DBDBEDE7D56513128B29FC0B4D25A6240B24C99E017BDFF7ACAFC8F8DE9FAF5A2944384AECE82BEA04DCCC6D51FC6E6F27AA38F131B7959B13681A09B311D242E6222A1CE5687DE5C080508B1DB16B6F8290D33A3CC0D0138AC61FD9093825E9D3752889E9F20DB9F80F92750EAC88B38AC81C0016D40371EAB4A87E845E91446B0A07081B84F559CDB95340CB020AF22AEA1BFF2FDA12F7A42973FF163A1C6F33DB8B8214AE27ABDF1C54F5B03E29310FA210125E1296E8AF93A2996DBAEFBADD4C51C2C3B8A3E2BC9FE060C42BA32768F6992A99599206CD2291CCC5BBD50856F7F8D2D0AE1EFB5892C15A799B77482DE4553736B162ABB06631F1688F6746E7D7A37EE7EF24E6CC901175F04960C01990178F81E957E941DEAAC8846B3704E24204F43DDB0765C433F3F7D4D201459CD65682B7DDF3D47E95CDB31B96A4CB22907F08BA6E92A4A07703B2DCF150F922C4B7CF181380303FB72547847305999C3C8F9AC877D05D9DC4159DEB8A13D36AD1D533A56950E20F906D29D51DDC45BD15C1773991707480E37B827044BDC6473181B760A9036E0D3FA491C2F08C55130D8CDD5AC8E97D0813164AF3D28A585F0C2EC7004D498F95C6B62231A632A56C2D0C48FC3A6992D4051957B9ED6D9A86DBCCD962A8883CF82CAF01DA2F51A203D56B6089BC8FD0B1BD414C8063031ED469555E22EF872689C130B1C101034D572FD8CD0EDDABEC9EF1503D7F728B0941EFE2B9512438C7DDB176BE2EC2D9FFCD56495A4511428DF02819CDDA18D1ED5D3B16C6F42AA0AC681A9FAB51E8A1A856C15C51A3EC1031427142EA12543014DD4ACAC640B8A7729E63AB7DF1051112CDEFD4B988A2258334FA9A7F5B3A87A02074B9F69DD81B83FC74089A91D76AA4041259E80FA255F2084902AEB9E996AC2288AB464BDEC47AAB26A28A2A8194989755D48FC9A5C9279285F2F1DBB8B8018F3E4E13115D78A879792E45A8F4F24ED4A317440BA63E6929056EFC1D2529B75A709D6C0097DC2D97F646F334EBE6195EC5630132FDE58E25DBC17DAD822D9FA0938A2A2C926B105D108403DC29CF371C3504FF73BCE9C7ACF9A74C4954CE6A32DA96B21CF3211B3E49953DAB78C49C3E532A349003C59C62F7D40261CBA63A9EA21C89A38AA63CE431C43AE261C4D9999B1CAF491FAB8E7BE6E8C3454F1BE8793B2D27141FC107DA599A4694C41353D7785C05B5E31440458D17C6DB66FEB8A9C5C073FB946A67AC0312BB669D9B12FABAA5272CA6631379EF4ED420A4424A5CD08526384C047C33A84D5D7DC0C2153663B54C73DD799A3568C01B818992CDF8143F1DADD6B50CAE6EAE13AC66F31FFA2B362CC4D2880592B7FEE4B9E4CD6AA5E5DE27AAB9B5DAD9F7D39407AE927530CAB2B61CD7394A21EF47BFB813B5EA6091458D239664923280ED0D5CCA8285BB2281A2F9FB3FFECC8E9147E1E8FAC957D90C9E5F513738745A47C2AD0C31FD8986EF3B6388C6E821F166513811D547AB4336B5E04643497FC9F8D6E380EF6478B82B6E2F5F65DD98A63C68C32B94610E1D3B9538F13A7688FBB1EC3448BE9BD77BB93A34546172AE8D614F85228988E7FEB18C9A0C9827699E8B3CBC69750BDFECDA8268F694F4C509BEFC1A1166F85C829725299D173F867A300987A2D36D1BBBE37BE3208FB8EFE9152A41A5F0E931B6382FF7F9B18937958FB180E61F2A8C28F36C3C80C3722935AACB81C24AA17FB3E7A1026F7031A7449818ED62BA7705CA27C2D3268F90B6322921683DFF800A306CFC186CF2A61B37F35837B217E3B2CECB0843D84EAC67431E3D689F01522D4A4C73618B7C2965C9DABB15C0BE637D10CEEF72271CF39A7B803B41767BC34433C3E6FF449A439AE13DA1EAFA038CB9F2E1C84F1CE39C05DF56FE3D7B82386C4E628B6E27CBC5D575C66ADA3510C246BD04DB48F4AFC2D7352966DA2266C2BC9831532F53655D8BE42B421AC0D70D8AD1D3587257886DBF93668E907E861BA64F45999BADB0F766EADCE5238B5ED397F265935194812C03C5769137BAC97140525303CF48D65F39004A3F59B1FAB09895CEE05335D15B9B12265892F4ABB92AB1DD2002ED00CF3562CB67DFE1055968E4AB3306BB34BB87D0F64B26848812A2F7B50424A21FF94081A7F70F7B684AB0F092B2B085DCF84CA38414CF7290F607BF79C37EA84253ABCA8D4184D2DBE2E900200B81479E1CE8B71DCF2BD6E3C557A8E431D627BA669C2EA03068E0F7EA62C29777B22142D7A1D451BD541EF8EBDDBBA4E3BD8FFCD340E935BE7C66EFC14A13EA48134F655B0DE3180101F09D204C379743A357E6DF1268B55A9F7524398ECF3A59849A27B142239059998083E8FA91785E91C4D220B2FB17E3389EBAA384A49D89B5D78136DD2454F06CDE9837F096B744D53221127869904AC227CDF30BFEA78CC5545583F999B9C42A1184E2FB9FF3EC095B9DA0D138205C4EAC4C8C480C43153608849F63E161135C79D8B6C9CFE9B8DFD8AFAB559D8B595DDD43835033B4BBD391E028BB2A60832D9B697EE61408F149744DCE71AA11BB2B0436C1E2626AC3A27CDA293366B90B9CDE2D927855130758D3946B867192DCF3FCE9A3B9A5276E8C37B8CB136FC90A6DC22650F95E796A9886EFD3F424BE63A66DBB1041CB3D4A06F4E7EEE89F0B6D15C36F9EA010C66B332011C8888E8E4AB2B3AB5223191E1388613A0FD0F07C1B26D7CC7CDF1AC62A226454D6291B431CC3EF2DB2B2442B37DEFB942117FA247096BEAE598611B8104F37BEBEDD8BB8B949A89B5BF8E228ECA1D8F16BFEC75A02FFBB4EEE3A6D4A6087C43634D675311E72A9F3253BB5DD364E07EB4B9C84F586BA267BAFFAEFEC79E03B83B18595FE06D7E063EE604FF287004D141C1A43AF0CA7C5651D98F633FA875B4743353FB07BDE59B6567AE25F7095F1D9EDF30570E2F7D7EC194216898D910F9E295A41DFEE072CB56F914BB78CC9854129250F9874B63BB3EBE9A1CDC6EBCB0916E1C440354DED6AA818F2811DA913912A21D3961AC94A39F0827D3A419616905DC45842C8E69A43004B8AE922C8DE1E8CD0668674A7760153213835BC63FAE4F8D65614AFD74A34D42ABAD5025B884B34639340B45D49CCED423771916E18AA077291923017CA50795F3B7A3F349A3D29923833CE57801C631576E23B838A7767CA1BDA92B82AC502DB3688FFC83C09A4E40CAC31D20D9D32FA6724A80BE7091CDE9C7A6560CFB326B467CADDB9E9B7A491EDA283EFB0B61B4A1116DD859D5C0897EAA2A3FB2CD82FFB33770BF9E08091363B6B81D23E61C2A647D2BE440C5C79EA89690656D9F10B1F07942834E1CB6E2D2DF106EB6D6A21FA23819E65028515E88BD279F9317BEAFFD394EA51F8639371C3A89F11305A4CA35FB0711F5E2C7C3DD1659C790245812113204B4ED8AAE9FF09D43C6DDB13F5070D98831B2C7639FB6B9B01C288812DDFA8861DB32DC8268C07D30CF969953042B3DAD530D9D744C06AABE7A886C0FE57B09B7F42D193FB3E9C06329818251A2F7E6474462C95DED -Out = 578041cb3c013b2ebaf5648b2a53882e3c93eedba0d8f37588723123c9a52ccd95fa60b47cca11132d6c2eadb22aaa45d29355260a51d25165f12bb22eb84ad7 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFE +Out = 5B21C5FD8868367612474FA2E70E9CFA2201FFEEE8FAFAB5797AD58FEFA17C9B5B107DA4A3DB6320BAAF2C8617D5A51DF914AE88DA3867C2D41F0CC14FA67928 -In = 1A3DEAFCE70AF6F3F55D66AD9CE78D5F4D5C5F2638A810AFCD07D67E9F9A1380D6B34BE482EF030C22F1E978F544609CCE35A74C5109EE7038495B6210CDBCA8DC82C6E9E7B0D593FAD9665382B3C401AB8941DF71307DD77EBAF140AA66A1F76316478850E58886A9610631E9C722F459FA00C0B53124FB4F12778BBBA3760826D3DBA67CD030A96B654AF93F8E395F5F439549489F8161683F124BC980E6939C83A6085E4B6CAAF8BCD89A0E01ED70DB487166CC29735D9235A9CDC57B80C9C2E591DF6322F5BEDD32937073F781A30389552AE83FBE147D1B3D3461A3DF96C15CD96900C56718EAAE838417057579115936862679F5F2A45DADF65D14108AF1641DF987B57986384FA1433789F5DFBE87E90BD4E9D8D4D0741FCDA7348322B967B566B18612DBB8FE64F151947C3F7E361EE868676BCCD0CB3A1AFE046BE70057A05ADD3E65AF31E3FF414A627C0183E8FF583B41B75B203650420216E6DFCAB289665F054CFE3EA0943647528518573BBB1D0F27E1449E98739EAF0D009432DF0C1EDC1625264B94A71DB762659FF5A3A7A867F182D1F1FD34B341A4A181221870DC4A494013091A7E3B2B07E0160C438F1EE1E8A2B989C4FFEC36B5083EA427606767C29672F44779A8505B422B25A56907F565B27690D011426A62DF0036D57D967CD1D14E915BBC2691E7AF818C769D9E1F9EDD40894BE88FA1D7A5952AFD898E837716ACD73953BF2D1D448123FD1A0276D2C2EBDC760A4B787416CFAE5E963FCBDC8B551CB70F91DA0ED4A8090FEF178E879F2C34F3A269DFFFF09772D44A13D7A17468B5834C460957D5243C325F63F05F89D4ED98D361E7F0AB8A83948A64D0CFF8514841AA21C7F337920A9983E59BE4A0F1339E1E462F92DC1FC070126206012458A499A8111FAE078E00B0CA3BC1D6C7087CD318D5603C1C7E0425E6F729CEECA5F35B82F8A42E0E9B950EFB0904C5FB5C06D91D239913665ED1F1EE4B82185A01BA86CA2D3EA94E5A8842231A94C05280183B7ACA289984103F122203EC2FBA4A382E6F5236D6F68DA05E3BB0C558421F0EFAB91DCEEF6D1ECDC60F9B88F8BEFE31CDC3C2F024A1AF2C7336AA5D151E8CDA814A5FE898BADEB9DD680E337E682EBC22BFAE445417E37D2D89A338659A280AB1206DB74DD42C6F25639C1803BFDF2156DF613B0F5924D209F7F9003CE8794F989F4F27B82121210F4F65EC5A1F7723305CEE438C41F793EE04496BBE337BBD2FD3023830B1C8889C6F4D0C1192E364EDBE1CD987BA5D66224EE9C9405E1DFCEC0EEFFC5C73D3123F6731C6295D1E6B854B884FD22B6A3BBBE5395312585CD138BCA67532C6AB71BEBC6657C50DA87D2AC6068FA3970202C5E15EB7B4B3D2676C0134BCF1EAC2B26BA46930B5E660B16060894884C88BFACD6779276B86F685AB6F17C6D53F621275FAD66D021D26D1D480AFAB4B5EC75E0E763FFC45F599EA02504DA5D91EB5EFC3E4AE196F219E45E7CB05594958C876FF474A020EF73C1F09B1F7F7457E816D3AF51D86663D4D461754CD5E907456691E02446D6CACFD33516206A31870543D574592087773653D4086C2BDCBAB3C9B65CA11AD0D4E58DDDA8B440309989857103929549B7300CED42651D4086661694092C42875CB62858E6D1BE5F7274B4BCD83AA4DA05CACA186A30902830790F9FFA24418E1F9DB00FA40477E83B05C2D11AD7D81DDDB1E31F94A9DD5E9E13391C22479B570976E3AFC1BE41086D3BE6689D87CA4326A7CDE8E5B396A678D3CDB2C80FECFBA2BC799AE8B1528E96D880CD098DDE910D097EAAE660AD4D7EA51C18F18AA1B39614299A172512521DFD231B9840909839EB69C892EE23F1BCEEC1FADBA75786C7DED93BC9983F74CEAB397EB8BA84F7E4130B34258D628594A6F9E2348FD91BA2594E07B8057E8A2AE3ADFEA0EF919555385977041C5B6DC4F3880569171F7217AAA9A85F2F5BBDFE3FFDF79248F2A35FD4DEC34980C67290339B1C0A5A6AB8838157AE2F5140B4A24924A6688AE5CE72A48103EE9029CE8A0F15B1FBB19A12FAAB80A7CD9C0E389FC2775833E3190F1CF735ECDFE7F6B6C326506AA82613CBEDA8DD3691B81F4C1E3B0FC32D7E6719CBFC12F4A26E0FC29D6417953ABC9568DB4ED9A294B9FD5F2A666DDA546ABA301B1C60985033953EFD6F4538333B5C7DD3148814A3FD7927C366F40B3D7ABBDEB2332DDB586AF80959097663CFAB2FEECAD6D368AE10EFF9663D5F8BAB95935D25F45776F7F04B46817D05165A9DD4770509ABB92F8B9E7373CA780703569981754A51D6D376D65C57F55CD70E2DF5FDF5A6B829AE30CE3BF942815C8B4BE858DB58151D02A68AAB9FD373E047EFA51BD1A0CD1B61744D9E97CEBA3334B3BAAFEA3BC9E43AE097CF2C3D713EECC247FF43EC74D54907D8BF45E45B2E0E11D82B126A8179D3F66C055E11F69EA67AACC5FEE8AF01FAA379E51998F5070F9EE0FD30A2EB22A925586FB1B39024EB5EB1E127C76A149E7F02AF1B73C16E9E5A5DBE378E08A9FADF1194C625132AB3FDEFE8FE9A89BB8E0035A1A3AC5278F5D3D0ADE0E41C81C6853A41C4AC45BE3F68180FE23F27F18BE2E339DE1D559D75DE63ADF7A32BAE42B037AEAA3E123A5314891BCD35CA48D57DF4C17540E97202A8EA1328DA25B1FD6BE2B56AEC1E5DEB209F3B7A13ADB1CBE53EB645956E577A7621D74E42376D70BC5C4AACD239A852FBB7B3F62CF59FE10438C1DC8E1E46566325DA0CA43AAA63FB7E0B450A2DB3E3A2204704D894DB24B72B3078106E096CD543DCF027650CB4965E38AC36A8AD588C5962B4E26548AB88F0BC20E10ACC1C3FC00EF415B3C32499264552B14E2C0E789A3B8A8BFF9620FD939D0B34E806177EC696A4B3B1CA4B32BA979B2690CFB3A6B17BCEE6877FFCE757E4116DA01099FFE82ADD5A0C593E73449A96DB9CC2B9E846D166B095174F2CAF8B35DD878C836D9BB6EEEAF8E1BC5D0E149C739828CC480D731DC16B35B80D4AD82ED7D29BD05018239EFECF8DEAE180C6A459DBCBFE4AAB9A5E2C1E1BC31418CF2EEEB31FDF8BA02C9A91525E9163F672BAE2EDEC38C1BDB84EA237B4EF86BF5C0F0FFE178E3761E82D94F66E5EA40BA8170BF768409E1B4177AAFD9937BCE3FBFF590320D7C445372463FBBFB34F57447F42C16E026F179CBF82F617C86D1E8D42F6C908F9C6B77E38D25D51303DBD781FFAB569B4CF31FD0B947C45E1768A2E9DFE8369F520DC38D77937B69B821DB4FFEA8F50EBC404F0587B5598189F54B5A5B98966FD16801C87DE2C3C7813DD70DC600824D426D88C55E89D47214D59206A7A65A65DA7CA2E42FA62ED17E7AA5B3ED446BCC71F17FEC8593BE96D2037BD07F9476D4D732B32BC5DF8C921316B45699004716FC89F8D45BAE402C26DBCDF1A340847B932FF882DBEAFBEDD252E126C89A1E1FDD8908A1F67D15D8E432DAD8E08E950A3BC46B96CB89CC5BDAC703B3FA3E986EF1C6E7E6606E6845BA1EB2FBDCFEE744B5E45206F4A419E1CB103C8490EB293EE9AEC1F0A0D294F9D3847737413D30873F3C94740E8FD072817815EBBCE3F09EDEC9D1211A9E99547D620B2EC56C89E9CB8144AE9E46636324BD13C6CCA3AB9CD9FD8F7F937ABABC598232384427A2D4CE0CBF9765F7225E208C3CE128602B0AD08A1BAAB77EDB3111F0C6CA7BA0EAC9D89D5B4378EB82C17F6EA08308A79A53D150D3F85EFAB77294F02EE0E2885EE2AB2793392B87DB11FA77992F5B4FD75EF2F1A822E87407A4878894215AB89B6CC4A120F5A78B3C31AB80FFCC9ACEF53FC6F7F85685EB9D56D30D87C21ABBF1652EEF8F32C7C567BD1F08623B09C29F33561D42727A5649A3850071AA6C11735AE63C4FD31559CE560B27A362786A83353FE460B37074664A9421D3B2F6A864D5ACA087187B27E2B82F31CB3DF5E985CEA271C609B94B4E58356D40C7D5C7FF2E5990FB39588154843EA5FCA92F120075D4C4D006661A0FA1B0585454BEA725473EEF7D58117D5840C8348999003736C5EEB7858FFD273A1C3EB2812F5697C59110275B08F6BEFBE84C92497D5F73B7B6F794A849713B23AC5F29D5C7112FB2E7A6E89EB54DDFA3122E6C79624C1BF25EBFB9FE5CE6DAA779F3ECB2984DA42F8C6ADC77B21DD291E684FCA50E46070962A2D4F00813D8DE1B8ED33FED9715180C7EA8E2BB74FA65D9C7F6E142F3C81CDC59172E1020F62F65CA5A12CF2BED9DEA04A4D8CABC2948F7BE823A3E792625275B3925A6C8D8E2B428C75A5DB0F7120278CD7D6CAB768755C7FE2FBF89FDED1FB38AC7F76A2F8798CA36ED42CB7C07F006271205F546A4812C20077F050D4CDC79459FA686E97F0704B7A9FF7DE16318E862C53D361BC635A55A264BE15016545DBFCE3C6D6849576ADEFB6884EDD768214E0B438B0231B4F2692C2C0B5C177674F8A0DE236EACD9E0CEC7C8647E4E9A5861B957EC834A2F8572F01304C3FD6A06019E5F1499B62BAA8670B652467FA9A4F10F053263BFE9743CC7D933F86136AAE3A6FB56754D7D238397A0030CEBEA87CB255AF36138C373DBBAC41DD4A697032E4796C552AD9C9B3FA713C3A4E09E0EC5581E94BE7F31065157662F9E9C678B1EF1B8B8A847C51789C22B1841BCFC855820AF3258AF9E08231090B45D10046A00178E89BD515616B8A44E77BF57795DABAF40687B2CDA7A5014168F -Out = 9d80cc24759de69e41ae45aa3d67ade2262179a57b12d2be4ac9ee81736de3a3f5836240ebdea3910b8aaf7af0679e5ee38ff728437f78cbdd3a0f07a4200c09 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF +Out = 1ECC896F34D3F9CAC484C73F75F6A5FB58EE6784BE41B35F46067B9C65C63A6794D3D744112C653F73DD7DEB6666204C5A9BFA5B46081FC10FDBE7884FA5CBF8 -In = 1633256AB03B20CE079196B708A1C02D1B6072219070712C8589EE21341D50752ACB6CFDA17E982D828BBD6CDF54BC7232FD418A323D64939928597B9B52F07CF488250C5E42BFD3AB48012D709F8D747225839296386FCE5FC5AECC4BA7A1076D089DEA8ECEFAA0CF66FCA8602395719C12A04F929321784D7AB8239FCE2FF3BDAE046A266132B5C2AD9F7261F3014E87B389A6695978693D9371D0B1FF9C405F338C2FDE4687359603950A54CF4B9CDD9B24480B239ACC5405C14C886BBB0378391CEF0662A38882BDD09E3866AB9A66CFBD28EB5EE4F8009BDEFC4AEB16700EBA7DC557B489190A71FDA75E85F7EF841697F70FFD4FEA185E7A67C81C5B8F273BFB97B2CEF695C1C74446C4B425BE6B2E66DC0AAACB247E4467B7C7D84EC33B6B5AB8FA1979F503008BDCFF948CDBF1226B1B066CBCF34797298F3BA8C60FA01E0AC8B803223C656112FB91435D75453BAE4707B63330467DD13E0A4B992E6F7E46995899A2D95D23F4AC3D0802B2A6E7D024DEA19CA408C4BBE053F14C9CE264F129724A18BCB18F385B1CA091A11434EA96D98C8D0602E98EDC8DFA14141AF93ED0BA66E885E9FA108591AE59E109AE34D6B9F5586E4B4D75E7DF7C32958A65E88A9BAF41082A0A3F11539DC4EA2CBD9E1C6C3C439B622F1DE574FA75470C8C939B51D2D1C2A7204B859881D43086BFD8FB90346218D099C5AB36846F3B98A7C847318BDFA01E09717943FCD864C5A8A17B6CEB89D98E872D388F20ADC2BE5E2006846904F41682FB1283214F3D20DBC9FC9E0FF571844A1282E88590D7C085B2C568EC5ACC4462B389FEAA5757F7033187E2DE31955FCE55FEDC909255048B327CCAB2E582BBC9D8054BF5CB45145C7D3A3AF9CD5CF6ECBA490C634ECF00E646BF95E8642C43A4978EF08A574EF1F78F6CE57C3B34B5A123D123617FC8EC9B2AC0F9B70A7F6062D38DD7B8E9FB4ECCEF13DED5C0477483ADDAE4F1CC0CFCA274B1307ED0DE72FBCB819154CDA897D7575213042615F1741A8CB646A39F8D134FDF9E60E000EB8220F65CC30F5FA52C431B9E3B6101B96E25B8D0440B96E572A18A01747C02AFCD7513542F7AACE194632099D16274F31EBABB60DDD94FE43DACCE900EC0902EB5E686D48ED8D09AE63DA0E15C736809903A0297A92DE84E0260F11F446E1FC448E0EBF59FAEA3C726F97925C57CBDF85B1F77078D36257C85D56CBBEDCE180FE12B687ADA2DC9912FAC60334166BD2CEF06B089ED5C9563844D71D8FEAD2F3A93F3C07C52537336A8A70BF5B596B9007B9FDF2D082000F20E6B70D2A7E6C7ED27C4146895A6D85A246F623C1B9258A2F891F823ADE4CEFFD59D4FFAD077351E2F506E9A5BDD3900F0204B9E8969AFE72F5DCCB9CDF986D197AE4C4DB53014041AE6221B750E5290E307AD292C8DE6B899235212EF8CE954785537DC9435AF11E0F3427A9C7B22EFA752EA0B7EADE5F6EB4093BACB78676E506698139E4F774423B8942166F9A7D22480D814FC0AE19CF4960FBF6E01FFA65C8DA5BED4F1AE2B9ECEC5BE7B3C38DD4045B0C93EE6CC77A7E61E85D331B23C0D164B104518B3405497054445A353E9B48F2AC5E8E96298D6655614336CFFE6D8C9C915E387391519AD2632366AA3BC935030FD12927EFCA17505ED74C94650C778539004854DF6C24269AAB9C273A493D3E5B0B1D687C33C2FACE46B4BB3742D6DF743D09164D2E0EE7F6BA128BD5FBA2E3B33C199AE80FA9DEE3AD811D02BAA3D42A6362B2AD47BBA8A2C5CD00B46CF22CFE367281488A4852EB8B7FACE79F0CA6F8E78D32578DFEE01711C4DCF3C26D0BA13F3075478E708C5C5315AFDC2E4C0062D16458213BEC506A9E991A61825FF78DA9BA1BAABBEFA56B4A8C9E2E7B60EC4B7B541C8E0F79C86BB5F03F736761A37169B2AAB8884EC6EA217B02C59035F5BB327243D126B78D4AAB430212439B5A75B80618DAFEB66AA3AFF866C4DAEE47D374B512E74ADA933EF24A841BA271C6F02C870E8AB950FE06E93C91DF0E99165DC01BCB190E411ECCD85358FD4A88127A22E4CF4266A90845124BF97B25D7B1C46D3A0D68A684F84E2A638C692A52CB6E8C651A3AC492B0460004073D5349E35552359CA37660F77B2770D6B2B3F7B1922424AC4A8598B4C61A6DB507608A72A6A7D573CC055206276E14005A28A0EC41F28D7E260611D40F089FFE5E529375691412F4E9E12E62C3BE2C563C26D2444EA9C69E6C935FEB4DC4E802E5FE3906F8ACEF4798D940C3CD574BB5E74506C3E0B70CB62454A25F589EADB6B0709FE3B50417CD1D98F08E08B7CF68A04CCCF8D6588F9FC2F31E533CDA6159BAA4297FA446450D71C16EA2324EC09773E7C8817ECF680ED12F64A04863EFE3D9D8760F34DE5B0860B3991FF0EE5EDBA22C4D69120DE19D5429E4AAE91C9E7CF05CC807159A58F13B480872AC1609D87E7009DEDB71C09CEAAB640A2B6135855CEAE4AC2954933A0255B425D9FDCD9C246F82AEB7C3BB78C6E73E03DB7AEC4245A28693FBD36EF4938D59CCE19EAFC00671A0851612406A075713C5D1154D8E13B59B7C5B0902239D4BACFA386AC817AC5EE02A181A9A47C622B3ECF287E14843D452AF347110498A620B34AB4E116308D976062C9EE9CD35DB6CB79805B93AC9A15AFBCB52F1ED4309879D1924A4BA190B0B86E60A516E77D34B4E0A49D4EF2CEF3CC2F410FD8EC901363FC9EBD75EB460D4D8910BDF27CE26A8B4AEB94F9F76242401DC35D0644842B99FB6C439B82D82ECFE1AF0D01F9BECB15BEC83F13B260F7F714AA381032923FDE8F8018F3518547451435C9A5207294D08A907C73696F6CB000745E072E25B73B3EE11595433D27A1F11468686F08094F1D31F5ADA81F11F0677A29D72EBB2E1C4792CCC607CB938647E1F153F9EEF03D982595C631E49B6B7C1FA003A6EB8D59CB8892CD0888B05240F12701753F89007C859515A2FEF944BC60B36003A26702AC6FE04D2E942978FC31A97EB29871D6752399D3521720729007B6A7215A4282B2A4EFC2C56BD129E74C9B00847692B96FCC71CF7A7F19F3FD6B45C519FD73B4860880A2DD74E5727B31A93F0A87F0078155344AE9F7BDBF00D83393B634B5DCA88A398E42C320EB95C4A826ACEA90B65E4767B2EBA748F97C247568393E2FD3A66075CC12935B6D7EB5C2FF5282185CB62C73972A37B3CA508004B4F796BDF82B83B5BDF90D6BFD32B5089B0CA2683DC7FB2337DE42E650ED911DBEE1EF98257F9BA5AF54B1A54B04C0087A5A64BA779D86461BA15337C2E7D4955FDD777A025DE226306A17C384F1C52CDB5946FB0B46DD5C13BD7A55FE2E27E4C6D40D61D6FFC024468F8EDFC7C7992DF5DC5D05063FE723199224F53678E48F25250EA28BDF1089718EB8B730D1C06735C2F871164E2EB5E885A8DFD2A083BE97EDC94159CE9BF75D2433F1D782762F771903CBF9A1C9D13F710BA0E151B079DC0A8262BCEB1DBCBBC0F35DF6EECF7BAA7105B9808745853C96B4372E95E482035916B726DAC7BE95A72B19DAD48DB1B19E6EB2EDAB5AC1B3013839E7806625ABC129F41813E6D71EE4AB2040D81E42E6ED73ABBA64FF2EB433B910EA7D4F5ED3D8D27D39BB454EC019DF6114F544D7B155549D0C56D14551FAF353994A80F30F3C97E863A4F2AF316468A568038EB4D799350A6FACAFF90ECD44E0F44EFB6DC42EE4B0DC2C59EA9C1827326DF08C0A6E55CF4F9C3EA0E78CFF3635F5D08E44F1400D20F638D56BA84B4832090454DE57EF04B6C8805A36F63E5CCC6E830C87FFC164647CED20E4C486D09DE7A5F9E4B68D5456CDB22B0DDED2B95B3BCAE529215C2D25D6823C7D66A4FAE0A1E9F022BA5663204F2314DFA51A1F10E11D6D62A8BA6C28B6AE7DA1DEB5B57F2B65D7456059AD9F03DC5A524054DA39DD100D74EB657DE219795E3C45A0E4C762BA22F9DA9D8159E425A1EE783B4B22C250D8894CBEC706CE16D5CA393404FF478F141BE7CC69E45B077BA1955F1F49EFBE4847C795347F703300F672334F490ABF8B644A34B56DA00EC45A350314B9ADF27CAF7C51CB7DBA0C5477E7D37662F4F23247BCB8F7DD5F3E9CB8BDA40FA97568832AF0ADC68F71422E412254A6BFC8943BB465B01FCC8DE0B957677C78BC1F7566953E9D2446239F602C682A521C14F741FEA98C7E27AABEC339B6F5B94C78287A894AFDAE971F8DA7C7E4A4C92C8DA47BE82DC2532EC2DA9BACEDD2BE6DB2B2FB34DCCDCB34116507376578CBCA105E5E443BEC0F2EF23BE34CDF862EDAB34F0FF21335E3ACD92F59688B419F824EA61EEA82BC80E3463452192377131BA51FB0795E089FC077D0ECA8012E58B0637AD7022206887FE9EC00EE5DF7AD2E26FE819EE35C7A179C579098AA3DF645D9064CD557DA90BDD21F871CEB048CA56DF9653A10ED60F5E9F0ED7F8D89BCF5C22D1143CF44718FF2DFD8E10CEF8AABB67D2305F18177C1426BD4CD03F2625E459CE905067826A214E08E56D8F9455593E6B324E72DEDCC429D3BEFE2AE0599E360DF95E80D453A3A849E48389FA745635BEDE30E7932DE6A3816E31A2217F98D5E40238963D0A36C159FD4EC32D8A5CF59D433DEF3378634AF6887FDB3F3EDB96FC8840FE1B538C329674AE810E8C8B2B46DB208716D38E9D1AEAB097068AD83ADD7DD2647839B3A7388B0615BDE26F8692E9C07D8ADECC2A875203C3D3A9C6CB1D7D06307E9E1D9C3BC536DD8EB271E9A2159C904E61E8C9357FE759F36366AEF5A3D14CEE82913CD2708AA6069369CED763C8E830D70924E82E9015C2998E86EFC1DCE6AC2EBCB49455542A6D7DAB265AD6D7381FFEEE1AA40F8FAC0659B6FB56BB03CD8CAFAACD48D13672F7D524EB9684CFED4DBB7476E99149C28EC08F33BA6AFF839AA178F86B8EEAF1739C829177BA78547AD394136AA3FAD451A11E9642506568B39668B2436610E06EA45FA11D04D3759B033B5382645F15B3C39270B81B80487643913A24F2F1C1A1ED57C85CCDDC8CD6D59B62FA67CC80572968C8FD01894F0153634C88792A7C4A407A4A4CE46CEC5FE5D2569F95A27DE242444EA0C715B357518CAEA23E767E8545983F0D3A4DF66111B4AA1D399CCAFD796D7A80E592D5A51D2B3F60B5B04F8D9C009CA56CBD4DD84127A29B72ADB7645FB7279C9818B2B43963BD605F45B6575A5E2E369E0B401F5EC10EC703F1179B0AB9D4A89D6F096573952E513827364A84D38922734137E969D8167D6959B70F42F2BDA37E4C989ABAA8024C1A84ED6BEB74780927F78B32EA736B9B2B4A795C355C0319811729D9CC399D23519730338D62E16E5035FC52A817090703FE776D65EF9FEF5BA5F4FFEC3CC8E9EB2E312C50A479BDD4E6AB0A56C18C2DF69ED408417BEE28BB41DD13F8366FF6EDA4B34090FC9BC045271 -Out = ae9b21d46be8420bd282f83cf326846f1f6fbdc11ec98b19c612c53896da4cac0a6d591282c8d7c6bc21500966468688a27b48485f4e41ca31f5711cca7176cc diff -Nru botan3-3.7.1+dfsg/src/tests/data/hash/sha1.vec botan3-3.12.0+dfsg/src/tests/data/hash/sha1.vec --- botan3-3.7.1+dfsg/src/tests/data/hash/sha1.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/hash/sha1.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,4 @@ -#test cpuid intel_sha armv8sha1 sse2 +#test cpuid intel_sha armv8sha1 avx2 ssse3 simd128 [SHA-1] In = @@ -229,6 +229,12 @@ In = F0B38F0505089A926595ECA3080BFFEE28929EF769577D91A8E5E2867B01EA4C58A4CE62101ED2DA38D608C266346DFB5FC14800D618E958A8572B35B23393A11613C85CD443D2B4DE78D5A4D641E70A23467D7CB4BBBE362AFF2AB35D1A075CA33EB619DDBC9AFC65586F077A3ADAE4B1150E0FE9274280FB011F0420155E5848920D9E25D1BE66DCC670D4395FE2847848E04645C13B334E4BCB588E069510BA6D014115579706670D2D93E3080466CE6BBD07CDDB9670ACF68E4E36FA17E03EC3F88910FBCE247128486B0475775AA5642812B44F444A14DC547E7D5D98527302261A13FEB8BDB4A7A0FB221E1D08388B4353AE2DC7E3464969DCF12B17ED0A5E549B8FF8A8956FE5FB69C1AB428BBA8CAE7E267F52CAFFFD2DCAE8F9B5D448AF548C7BE88443649254089A281A1B7537896D7D4C97399F0513E3976981843E20C15EDF14378DC4425CAC30ABF7CB034B6D4AE213360CCDD1C956ACC0D437C177C983EAEB9FB5F3A5DEB19CE3319C9368C2A7AF4B077A6B1BD417F17E2CA36BBDA826718515FDEBBBEB5268801073457F8AC83ABD1EED888BBF218DCC4554F67EFCE107A216B0540116691DAE98F34FB218969728E3165425C7EF1BB033329DDE4CD98A7317727F4E6E24AFBDF39F79D71CF3C6B93E75E007500874BDD94F7484C15EC5F5A509FA4AB14828B2895B0280C554600DC9834D23F824E1EC4662CC62CECDA8B9F9F31A6B4D589E5F95401644800FFD2132F60CB45A91940CD420ADAA5DB3577F41E099EB79708F7A0C61D37923B503DE8810C55F80AF43B4D87060070BBA4A948A41DE58E7A7DBDA9020D77F290A62F1DE6E47521C495B6EFCA40FC97FED9DE7389836F82BA05FEE66975121C38A6C7926EAED407E37CAB7E7005A891A93D88F68DDE4DE9FEAE55D6FE86FF34CAF9CCD7263085C2CFA67A77FD28659FACC7AFB9734159D35C6C96A755FCACB2048ACB7756696F4E481328A5A93A9D0BF8AFE0C31A98E1E6A4BED0C6CA661EB7218F9C5792334121604587A86423B652FBFA1638C96CA1715164C7D074117ADB267923F06ACAE45B493438BFAF384CDF7697AB0E0B75C6D21C6FF1139752143FF84F4652BAC47D44689E7A73A87E4491C0D9B8DC60D546E4350BC66BCF814ED004F202AAA9CE6E4638ADEA6B485CCAD2D55FF7A597DE0662C746AEB34B36C7D46FD47A67DB75363D314B40672A6289B60F1D444090B606F73A9CFE3BA46BBD3D5E629AD508D6F6592DA126E9400377D7DA41295DFA37E5654FA841A5616E528C65952846DB40FDF29DB7317B8FEEE5B0F670EA0198520E58FABEBFB68BAD63709C5941BA9F8DBB064EDC425B263DF4573EB6E3CADD080D9EF7BCCA048F60E163448DD6531B6B9F8CDB66CA368FE6AD9B651C51284E51253A3B64FFEE4806A0A88840BD61CA888345648D7743A060A36105C88 Out = 8395ABC1B17BA7BF947145EB1DF1A2050C8E78C3 +In = 4142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe000102030405060708090a0b0c0d0e0f1011121314 +Out = 37b8028971b947f73330f2e06b9a8d71da2f79b7 + +In = 4142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455 +Out = ec39d968e7c137cb52e0b001ffd5cbbe057c3bfa + # Collision (from https://shattered.io/static/shattered.pdf) In = 255044462D312E330A25E2E3CFD30A0A0A312030206F626A0A3C3C2F57696474682032203020522F4865696768742033203020522F547970652034203020522F537562747970652035203020522F46696C7465722036203020522F436F6C6F7253706163652037203020522F4C656E6774682038203020522F42697473506572436F6D706F6E656E7420383E3E0A73747265616D0AFFD8FFFE00245348412D3120697320646561642121212121852FEC092339759C39B1A1C63C4C97E1FFFE017F46DC93A6B67E013B029AAA1DB2560B45CA67D688C7F84B8C4C791FE02B3DF614F86DB1690901C56B45C1530AFEDFB76038E972722FE7AD728F0E4904E046C230570FE9D41398ABE12EF5BC942BE33542A4802D98B5D70F2A332EC37FAC3514E74DDC0F2CC1A874CD0C78305A21566461309789606BD0BF3F98CDA8044629A10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFE00FE0000000000000000FFE000104A46494600010101004800480000FFFE00134372656174656420776974682047494D50FFDB00430001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101FFDB00430101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101FFC20011080008000803011100021101031101FFC40014000100000000000000000000000000000008FFC40014010100000000000000000000000000000009FFFE0006FFFE002FFFDA000C03010002100310000001539DC51FFFC4001510010100000000000000000000000000001626FFFE0006FFFE0033FFDA0008010100010502A953FFC4001F1100000309000000000000000000000000141517001316274563658695FFFE0006FFFE0041FFDA0008010301013F019A8AA56D533BB238739E612166B90E605BFFC4001E11000004070000000000000000000000000014151713274563658594FFFE0006FFFE0040FFDA0008010201013F01984E1555C155B66CDC3E04A21A444C40FFC4001E10000101090000000000000000000000001413001215164462648594FFFE0006FFFE0033FFDA0008010100063F02AD9A4DB175DCE6086D743B05BFFFC40014100100000000000000000000000000000000FFFE0006FFFE0012FFDA0008010100013F216001FFFE0006FFFE002BFFDA000C030100020003000000101FFFC40014110100000000000000000000000000000000FFFE0006FFFE0028FFDA0008010301013F106980FFC40014110100000000000000000000000000000000FFFE0006FFFE0028FFDA0008010201013F106BC7FFC40014100100000000000000000000000000000000FFFE0006FFFE0014FFDA0008010100013F10153FFFD9414E4745FFE000104A46494600010101004800480000FFFE00134372656174656420776974682047494D50FFDB00430001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101FFDB00430101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101FFC20011080008000803011100021101031101FFC40014000100000000000000000000000000000009FFC4001501010100000000000000000000000000000607FFDA000C03010002100310000001524A5FFF00FFC40014100100000000000000000000000000000000FFDA00080101000105027FFFC40018110002030000000000000000000000000000F037A7B7FFDA0008010301013F01DFDBFD9FCFFFC40018110002030000000000000000000000000000F036A6B6FFDA0008010201013F01CA3546287FFFC40018100002030000000000000000000000000000F03767A7FFDA0008010100063F02A99C99898FFFC40014100100000000000000000000000000000000FFDA0008010100013F217FFFDA000C030100020003000000101FFFC40014110100000000000000000000000000000000FFDA0008010301013F100FFFC40014110100000000000000000000000000000000FFDA0008010201013F100FFFC40014100100000000000000000000000000000000FFDA0008010100013F100FFFD90A656E6473747265616D0A656E646F626A0A0A322030206F626A0A380A656E646F626A0A0A332030206F626A0A380A656E646F626A0A0A342030206F626A0A2F584F626A6563740A656E646F626A0A0A352030206F626A0A2F496D6167650A656E646F626A0A0A362030206F626A0A2F4443544465636F64650A656E646F626A0A0A372030206F626A0A2F4465766963655247420A656E646F626A0A0A382030206F626A0A313639330A656E646F626A0A0A392030206F626A0A3C3C0A20202F54797065202F436174616C6F670A20202F5061676573203130203020520A3E3E0A656E646F626A0A0A0A31302030206F626A0A3C3C0A20202F54797065202F50616765730A20202F436F756E7420310A20202F4B696473205B3131203020525D0A3E3E0A656E646F626A0A0A31312030206F626A0A3C3C0A20202F54797065202F506167650A20202F506172656E74203130203020520A20202F4D65646961426F78205B302030203820385D0A20202F43726F70426F78205B302030203820385D0A20202F436F6E74656E7473203132203020520A20202F5265736F75726365730A20203C3C0A202020202F584F626A656374203C3C2F496D302031203020523E3E0A20203E3E0A3E3E0A656E646F626A0A0A31322030206F626A0A3C3C2F4C656E6774682033303E3E0A73747265616D0A710A2020382030203020382030203020636D0A20202F496D3020446F0A510A656E6473747265616D0A656E646F626A0A0A0A0A787265660A30203133200A303030303030303030302036353533352066200A30303030303030303137203030303030206E200A30303030303031383631203030303030206E200A30303030303031383739203030303030206E200A30303030303031383937203030303030206E200A30303030303031393232203030303030206E200A30303030303031393435203030303030206E200A30303030303031393732203030303030206E200A30303030303031393939203030303030206E200A30303030303032303230203030303030206E200A30303030303032303736203030303030206E200A30303030303032313432203030303030206E200A30303030303032333039203030303030206E200A0A747261696C6572203C3C202F526F6F74203920302052202F53697A652031333E3E0A0A7374617274787265660A323339310A2525454F460A Out = d00bbe65d80f6d53d5c15da7c6b4f0a655c5a86a diff -Nru botan3-3.7.1+dfsg/src/tests/data/hash/sha2_32.vec botan3-3.12.0+dfsg/src/tests/data/hash/sha2_32.vec --- botan3-3.7.1+dfsg/src/tests/data/hash/sha2_32.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/hash/sha2_32.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,4 @@ -#test cpuid intel_sha armv8sha2 bmi2 +#test cpuid intel_sha armv8sha2 avx2 sse2 neon simd128 [SHA-224] In = @@ -797,6 +797,9 @@ In = 0000000000000000000000000000000000000000000000000000000000000001 Out = EC4916DD28FC4C10D78E287CA5D9CC51EE1AE73CBFDE08C6B37324CBFAAC8BC5 +# From NIST +In = 08BE55573177D70C9ECA518C96B457677CE07E31A126CC295C536C175D28A67B3EA50FE35B87FD9DE40F3E8E30050A6254FD35E6F5D9A9B15A8F140AC52CDE0604195EF1439D4DEF24A47BE312BFC090D26B36FF5A96A520E75F3FB34A1E8E6982A4AA4790C4AF4C87126E53E3EA633B1BB4E8447A67A7BC2A4C55DC92059EEDE2CD5BAEEB010BFA35E081A64B8FDAA95A5FB27FFA5398CF4CDDBE4B45E9F5D7491CD9EEFC5E494255961BA3F4B40D22B5F5FE7685625E9F749BE3C90CD27D72E11A8DCF6AE2526C0FBCA3148364E4F054FD33F2C19DE275CB0C2A1D8FC91D05D24EDD19DE950CC08DDB83BFA3A4475A60FFB8BB560B0C9879BAFC1175D5BDD744413293FF806086F47A226CFA7E1EA70184F799EDB5C552A52DC26B66FF45315E79F50776AA36056F22E8E530F951205E1357542EA1F3E977EBE2F40C4E9E5B48808C3BEA1C7786235A3DF1EE1DC80DA03440B3C0D97FA6187EC6740CCAAE9D2BDE61F704DC09513BAA8957DCF36CB6EE6F1A804C6552D1B06ED4B3117B5E3F2F19DA056CF4D6AEDD9A34E0A1822362714D4E81794B53B266417678C16A97887BBB612CC96BC5E532B3A654E5D3D65A5155427FF09569906381138CC49E3FC2384C5D33C34ABD3D617C487B52EC6EE7B5105F41584B7EB5CFB512B8C31F3F338D5236E30398A8FF927E801C8ED7D14FC5040D915A737967D166DDC266F68023A3575304315D6D74EF3FB701419CE9DAABBBB5359E1741EF911BDB72542AE9DCA1E21E5EF5A2F4E19D4956F014419CD28CBDBDF6CB3EC095385C749236C361A5B07CFEB8F56E2591C724C3B2AED0D47D93908F9C89F1DDA0EEB210E8B3CAD2C5F8AB5BDBDCC9E8CB9356680FB9507825E5BE91EF8237805ADAA3173E74462385A0FA9E9050BB25D62969ACCEDACA7010ADCF2DB75B18910925B9F15A203F3C2DD1EE2D9DF94DC4FBB2E5F6B9BB45A4861149CABBBF9CF9F6F67C070460BD0505B21171CA8186AD825650B09604C9FCD139B6CFC454CC9E697673BF06DCC966546CEAD2E18D6FC8B33C34412E5D5F60384E9DA69AC2AF69A9CD2682273B6A47642601B9A8C80EFED58D1811496C0DB8027887B605B24D4200221DB92E26A9907B09DF8CE9D76D3532708588AFDCDDEC78DEFB67CDCCAF12B49DE1CAE4448C29E23D0BB46A659456100E020E2753D7E4E2A98121B9B7DCBF0E68F91F113E1EFAE1E90D9AF418894AB88F170B7A7902888800A14C921CDC3356A8ED1E7DBB64EBBF356EA54E9856F7721A4CE770F866C1B10CE45020A2B854D4884631EF6468E5C64AB53C428E034786D72A0AD1750B75A6F5D1962BF2770CD02E8BBF30E131CE3C506AC996A296213BEF38DC659BFA8DB0E2F1847BB3214291C2443009D9C7906A6E16B3137B196CDA8AFEC6F40F3AF215FBCA83D78EBFDE606DF9EB3FF4331198CD406281C29312ABCF052E457CD38A1D6AE6FC092B58C78556335A9DDB7C3B0E95703EA81E0DFAF6E7D47D2188CE3F1254CD55D731F4E748A779E4EA36AAF413ED2EAF4F388E0C03FAF41C50DB32F83AE405F80A499B25F08554F1EDACC626F0FD031D40B71E30192FEB719A1E079DDF3F184B6A1A5669C71A4BE96FE6D6B8EEEE76F04144C54E82AE43E6C7F9551547908EB8BE4D2C9B138EE635388CE59253E810901B878C4E84A083E29E13254ABAFDEDA5D08926A41D09AD3B1045F89C6712576596277A18682D34CDE8157E2382AEBF7C66774B6A2DB22964BFB919BE7495D5D879CD9895767CDD0E6DA66988E6CC8BA449BEA3D68BB1E1180C914FE0C099124F8E20EDABF5B6060FF56FED612D7FDA85410736D07E2F5DC0C175A3EA944595339BC981432F02383748181296A0EE338715FA0D414F5426C2A40C1BC438C1E6ED696F5C466A89F9EE2D48E01087421E128F3E2F0F4747C3887EFFC256F4368DE3C0BB879AEF72D49C6EA760B52AA2238E757CA424AC31BC7816F59D8910CC127F6755092906E64EA9D1DE996ED037CB63905B7566C6399AFC3697B643D3AAFE2828E4EAD9F60DF17E1F959324C2299DF9A884A9BFDF88E47BA48D146D87F9D945C9103294090E44811F7357BD499F22B9859E48163D45225D297EBFF072E029401383EA418512CF1B9B21EC23ABC1E009EE525B522999DD098D13C0B2DC887880CEE21AC8B3401BB459475A3C30B86152AC48E85DEBAC9BE998F31E9D0F184D6DC2D8C811DBC1AFB5238CAAF222AC2DFD8827FA1ECBA5E76412C6E19852B9316FB60BFE0345BC1384BF98E32BC7C4CA704798967D11AC46E2C236128AEC93ABEE7423C388385AD08BF5140DD16929C215B442C66204EB42CB71E9B75D26CD4352C2C92087FEC36D318FA831CB4E039D5396CE91B5ED876288E787DBF84890810369A51BE9DD1E72DD53FB5601BE79E191B4E3005E14AB2E7FECB983439A03EE6A315EBF941E9368F90BB6845B03B31839D72A1946C17D2F194827B926634F11EDE19C1171084CD6EC7D80C3C164DAC9B2C74AE6533C25ECBEC2788489ED9B72F543091B68E56E441E72021C1B28167D644CEC6F6ECCCCEED72454ED547E109AEB1D4BE462FB243AF7B1E49651988EBBB72EB8BB80BB9ABBCA3465FA6F5FD61035380A7445949441DF04CD3A4BFC6B0B133455D26F4FB6DD01BB50B5C2D8145C3D5CEFD4FBF6E6E03E31650FF29CD4F5E0286CBC25D149DCE280C0263630F2076950A10B6A6943A07C01551D2CBFF20286D0A48188887A3ED74D5B54B1B1999823DEC9217C37D308013C456CE2AEEDB96DF4A62A82BE728D47E8C9471CA13197D2CC0F1F17A6BDA035AADC05FDB2ECA3FD5E1ABFFAB958509A1AB3076049E18DDDE31A0C25002AF08112C3EF631117469FD5A646D7C3551037ABC19D63F1D201AEEB93F08D8B41814B3EA232FE13972CB920F5C90322915488639A100FE12C7BACAC21D337902BDAFCF420D7B40294DDB508AD4F651E33A4F40AE7684B6016833FEE6DBE0B560F83FDFDE5E46F2435E0F95577E1E5016488E3674FADFEB7B8A2CF781FB8F1271605B5219A6416C3A3BCEFCFFA866B114B89BD437BE8122F3B5FC413DECAD14967E78B27E75C912506FCFD3DDF46DF98112EEB6612216E0EC743878752FB93052CB1E9D973D6C89285258D69CD2929DCDD00D398BD5EFA9C83D57E9C24531FA38AEC36FDE7D9A35448C106196D383226D886DEA124A99E23989D1219FCDA5D6BAB1FD95CEA6E0AB27857D016677AE0BD29487448EE0942E92E23ABC8819936A5B7D23CBE259CE5F33D70532862F81CEBFACEFE56C487CE376BF0B26F5C979098D58DC6EB3F6B1C60BE93F61606B8CFF670A1E2944D292A557A8B8DD735DC558D2ED9DFAAE1E39741244824AAFCC4DF27B5488AC732F93F8B817CA6C8B2716BCCEA3DEFC4B30D3EDE961842AAEC2436C6F14B5CBA1AFFF321A94C640A7E5DBC9D30425E025CF0FA7E3D89B9DF7DDEBA1B4B33C234AE422F5E19822E643FA82E48286E952A8594B16A4125F11158FC556DEC8623FC96CADC8CFAA66E9F9E5BAB14FC4CFA04D5024CEBC97452082585EE06FCCFE7DB799FE0F173408B83427D1A4BD161F65AF541B447FDFD458B8B826C2CA2937599FFCA25D5ADD9EDD8D4166233D237F2F28C59CFC60648306432FAB928065C37FED1529182CFF8FC66FB2F6D1424555495435387B20CDD7C59C3F5BC4251B194D1973F0E3F022620F560CE2238F243850BC236 +Out = C525EEF8B2CA56547565C947BB7E964E2ECAE7C9C82C29228B6C932D2ACE181C # Randomly generated inputs length 1 ... 129 generated by OpenSSL @@ -1184,6 +1187,5 @@ In = 075D68D52224EE85A0F029E116C1894B0C673DED797F803EA298163D316C6B59C9584A0203D08F5F79F36891FABF8430CF9212C02FB2A287DEC3DDC772003167909D68E912DE0192817C085A6FB729ACCADB2ACAB6D5E91ABEB92F4CE68123DCD4FAD9F6ED80515142EF1081981A6C2D62B1630EEF02690DCC71F120E661DCD1 Out = C00F6356A020B716EF4BB1C89C82BF7CE6E5DD167BFBF2C81AC5DF42217DC3FD -# From NIST -In = 08BE55573177D70C9ECA518C96B457677CE07E31A126CC295C536C175D28A67B3EA50FE35B87FD9DE40F3E8E30050A6254FD35E6F5D9A9B15A8F140AC52CDE0604195EF1439D4DEF24A47BE312BFC090D26B36FF5A96A520E75F3FB34A1E8E6982A4AA4790C4AF4C87126E53E3EA633B1BB4E8447A67A7BC2A4C55DC92059EEDE2CD5BAEEB010BFA35E081A64B8FDAA95A5FB27FFA5398CF4CDDBE4B45E9F5D7491CD9EEFC5E494255961BA3F4B40D22B5F5FE7685625E9F749BE3C90CD27D72E11A8DCF6AE2526C0FBCA3148364E4F054FD33F2C19DE275CB0C2A1D8FC91D05D24EDD19DE950CC08DDB83BFA3A4475A60FFB8BB560B0C9879BAFC1175D5BDD744413293FF806086F47A226CFA7E1EA70184F799EDB5C552A52DC26B66FF45315E79F50776AA36056F22E8E530F951205E1357542EA1F3E977EBE2F40C4E9E5B48808C3BEA1C7786235A3DF1EE1DC80DA03440B3C0D97FA6187EC6740CCAAE9D2BDE61F704DC09513BAA8957DCF36CB6EE6F1A804C6552D1B06ED4B3117B5E3F2F19DA056CF4D6AEDD9A34E0A1822362714D4E81794B53B266417678C16A97887BBB612CC96BC5E532B3A654E5D3D65A5155427FF09569906381138CC49E3FC2384C5D33C34ABD3D617C487B52EC6EE7B5105F41584B7EB5CFB512B8C31F3F338D5236E30398A8FF927E801C8ED7D14FC5040D915A737967D166DDC266F68023A3575304315D6D74EF3FB701419CE9DAABBBB5359E1741EF911BDB72542AE9DCA1E21E5EF5A2F4E19D4956F014419CD28CBDBDF6CB3EC095385C749236C361A5B07CFEB8F56E2591C724C3B2AED0D47D93908F9C89F1DDA0EEB210E8B3CAD2C5F8AB5BDBDCC9E8CB9356680FB9507825E5BE91EF8237805ADAA3173E74462385A0FA9E9050BB25D62969ACCEDACA7010ADCF2DB75B18910925B9F15A203F3C2DD1EE2D9DF94DC4FBB2E5F6B9BB45A4861149CABBBF9CF9F6F67C070460BD0505B21171CA8186AD825650B09604C9FCD139B6CFC454CC9E697673BF06DCC966546CEAD2E18D6FC8B33C34412E5D5F60384E9DA69AC2AF69A9CD2682273B6A47642601B9A8C80EFED58D1811496C0DB8027887B605B24D4200221DB92E26A9907B09DF8CE9D76D3532708588AFDCDDEC78DEFB67CDCCAF12B49DE1CAE4448C29E23D0BB46A659456100E020E2753D7E4E2A98121B9B7DCBF0E68F91F113E1EFAE1E90D9AF418894AB88F170B7A7902888800A14C921CDC3356A8ED1E7DBB64EBBF356EA54E9856F7721A4CE770F866C1B10CE45020A2B854D4884631EF6468E5C64AB53C428E034786D72A0AD1750B75A6F5D1962BF2770CD02E8BBF30E131CE3C506AC996A296213BEF38DC659BFA8DB0E2F1847BB3214291C2443009D9C7906A6E16B3137B196CDA8AFEC6F40F3AF215FBCA83D78EBFDE606DF9EB3FF4331198CD406281C29312ABCF052E457CD38A1D6AE6FC092B58C78556335A9DDB7C3B0E95703EA81E0DFAF6E7D47D2188CE3F1254CD55D731F4E748A779E4EA36AAF413ED2EAF4F388E0C03FAF41C50DB32F83AE405F80A499B25F08554F1EDACC626F0FD031D40B71E30192FEB719A1E079DDF3F184B6A1A5669C71A4BE96FE6D6B8EEEE76F04144C54E82AE43E6C7F9551547908EB8BE4D2C9B138EE635388CE59253E810901B878C4E84A083E29E13254ABAFDEDA5D08926A41D09AD3B1045F89C6712576596277A18682D34CDE8157E2382AEBF7C66774B6A2DB22964BFB919BE7495D5D879CD9895767CDD0E6DA66988E6CC8BA449BEA3D68BB1E1180C914FE0C099124F8E20EDABF5B6060FF56FED612D7FDA85410736D07E2F5DC0C175A3EA944595339BC981432F02383748181296A0EE338715FA0D414F5426C2A40C1BC438C1E6ED696F5C466A89F9EE2D48E01087421E128F3E2F0F4747C3887EFFC256F4368DE3C0BB879AEF72D49C6EA760B52AA2238E757CA424AC31BC7816F59D8910CC127F6755092906E64EA9D1DE996ED037CB63905B7566C6399AFC3697B643D3AAFE2828E4EAD9F60DF17E1F959324C2299DF9A884A9BFDF88E47BA48D146D87F9D945C9103294090E44811F7357BD499F22B9859E48163D45225D297EBFF072E029401383EA418512CF1B9B21EC23ABC1E009EE525B522999DD098D13C0B2DC887880CEE21AC8B3401BB459475A3C30B86152AC48E85DEBAC9BE998F31E9D0F184D6DC2D8C811DBC1AFB5238CAAF222AC2DFD8827FA1ECBA5E76412C6E19852B9316FB60BFE0345BC1384BF98E32BC7C4CA704798967D11AC46E2C236128AEC93ABEE7423C388385AD08BF5140DD16929C215B442C66204EB42CB71E9B75D26CD4352C2C92087FEC36D318FA831CB4E039D5396CE91B5ED876288E787DBF84890810369A51BE9DD1E72DD53FB5601BE79E191B4E3005E14AB2E7FECB983439A03EE6A315EBF941E9368F90BB6845B03B31839D72A1946C17D2F194827B926634F11EDE19C1171084CD6EC7D80C3C164DAC9B2C74AE6533C25ECBEC2788489ED9B72F543091B68E56E441E72021C1B28167D644CEC6F6ECCCCEED72454ED547E109AEB1D4BE462FB243AF7B1E49651988EBBB72EB8BB80BB9ABBCA3465FA6F5FD61035380A7445949441DF04CD3A4BFC6B0B133455D26F4FB6DD01BB50B5C2D8145C3D5CEFD4FBF6E6E03E31650FF29CD4F5E0286CBC25D149DCE280C0263630F2076950A10B6A6943A07C01551D2CBFF20286D0A48188887A3ED74D5B54B1B1999823DEC9217C37D308013C456CE2AEEDB96DF4A62A82BE728D47E8C9471CA13197D2CC0F1F17A6BDA035AADC05FDB2ECA3FD5E1ABFFAB958509A1AB3076049E18DDDE31A0C25002AF08112C3EF631117469FD5A646D7C3551037ABC19D63F1D201AEEB93F08D8B41814B3EA232FE13972CB920F5C90322915488639A100FE12C7BACAC21D337902BDAFCF420D7B40294DDB508AD4F651E33A4F40AE7684B6016833FEE6DBE0B560F83FDFDE5E46F2435E0F95577E1E5016488E3674FADFEB7B8A2CF781FB8F1271605B5219A6416C3A3BCEFCFFA866B114B89BD437BE8122F3B5FC413DECAD14967E78B27E75C912506FCFD3DDF46DF98112EEB6612216E0EC743878752FB93052CB1E9D973D6C89285258D69CD2929DCDD00D398BD5EFA9C83D57E9C24531FA38AEC36FDE7D9A35448C106196D383226D886DEA124A99E23989D1219FCDA5D6BAB1FD95CEA6E0AB27857D016677AE0BD29487448EE0942E92E23ABC8819936A5B7D23CBE259CE5F33D70532862F81CEBFACEFE56C487CE376BF0B26F5C979098D58DC6EB3F6B1C60BE93F61606B8CFF670A1E2944D292A557A8B8DD735DC558D2ED9DFAAE1E39741244824AAFCC4DF27B5488AC732F93F8B817CA6C8B2716BCCEA3DEFC4B30D3EDE961842AAEC2436C6F14B5CBA1AFFF321A94C640A7E5DBC9D30425E025CF0FA7E3D89B9DF7DDEBA1B4B33C234AE422F5E19822E643FA82E48286E952A8594B16A4125F11158FC556DEC8623FC96CADC8CFAA66E9F9E5BAB14FC4CFA04D5024CEBC97452082585EE06FCCFE7DB799FE0F173408B83427D1A4BD161F65AF541B447FDFD458B8B826C2CA2937599FFCA25D5ADD9EDD8D4166233D237F2F28C59CFC60648306432FAB928065C37FED1529182CFF8FC66FB2F6D1424555495435387B20CDD7C59C3F5BC4251B194D1973F0E3F022620F560CE2238F243850BC236 -Out = C525EEF8B2CA56547565C947BB7E964E2ECAE7C9C82C29228B6C932D2ACE181C +In = DEADBEEFDFAEBFF0E0AFC0F1E1B0C1F2E2B1C2F3E3B2C3F4E4B3C4F5E5B4C5F6E6B5C6F7E7B6C7F8E8B7C8F9E9B8C9FAEAB9CAFBEBBACBFCECBBCCFDEDBCCDFEEEBDCEFFEFBED000F0BFD101F1C0D202F2C1D303F3C2D404F4C3D505F5C4D606F6C5D707F7C6D808F8C7D909F9C8DA0AFAC9DB0BFBCADC0CFCCBDD0DFDCCDE0EFECDDF0FFFCEE01000CFE11101D0E21202D1E31303D2E41404D3E51505D4E61606D5E71707D6E81808D7E91909D8EA1A0AD9EB1B0BDAEC1C0CDBED1D0DDCEE1E0EDDEF1F0FDEF02010DFF12111E0F22212E1F32313E2F42414E3F52515E4F62616E5F72717E6F82818E7F92919E8FA2A1AE9FB2B1BEAFC2C1CEBFD2D1DECFE2E1EEDFF2F1FEF003020F0013121F1023222F2033323F3043424F4053525F5063626F6073727F7083828F8093929F90A3A2AFA0B3B2BFB0C3C2CFC0D3D2DFD0E3E2EFE0F3F2FFF10403100114132011242330213433403144435041545360516463706174738071848390819493A091A4A3B0A1B4B3C0B1C4C3D0C1D4D3E0D1E4E3F0E1F4F400F20504110215142112252431223534413245445142555461526564716275748172858491829594A192A5A4B1A2B5B4C1B2C5C4D1C2D5D4E1D2E5E4F1E2F5F501F30605120316152213262532233635423346455243565562536665726376758273868592839695A293A6A5B2A3B6B5C2B3C6C5D2C3D6D5E2D3E +Out = 67164ECE34A972047522004FC6DF409C39C4276679E1535C90E8945CB99ACC60 diff -Nru botan3-3.7.1+dfsg/src/tests/data/hash/sha2_64.vec botan3-3.12.0+dfsg/src/tests/data/hash/sha2_64.vec --- botan3-3.7.1+dfsg/src/tests/data/hash/sha2_64.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/hash/sha2_64.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,4 @@ -#test cpuid armv8sha2_512 bmi2 +#test cpuid intel_sha512 armv8sha2_512 avx512 avx2 [SHA-384] In = diff -Nru botan3-3.7.1+dfsg/src/tests/data/hash/sha3.vec botan3-3.12.0+dfsg/src/tests/data/hash/sha3.vec --- botan3-3.7.1+dfsg/src/tests/data/hash/sha3.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/hash/sha3.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,4 @@ -#test cpuid bmi2 +#test cpuid avx512 bmi2 [SHA-3(224)] In = diff -Nru botan3-3.7.1+dfsg/src/tests/data/hash/shake.vec botan3-3.12.0+dfsg/src/tests/data/hash/shake.vec --- botan3-3.7.1+dfsg/src/tests/data/hash/shake.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/hash/shake.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,3 +1,5 @@ +#test cpuid avx512 bmi2 + # Selected values from the NIST CAVS file for SHAKE [SHAKE-128(128)] diff -Nru botan3-3.7.1+dfsg/src/tests/data/hash/sm3.vec botan3-3.12.0+dfsg/src/tests/data/hash/sm3.vec --- botan3-3.7.1+dfsg/src/tests/data/hash/sm3.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/hash/sm3.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,3 +1,5 @@ +#test cpuid intel_sm3 avx2 + [SM3] # From ZA computation in https://tools.ietf.org/html/draft-shen-sm2-ecdsa-02 diff -Nru botan3-3.7.1+dfsg/src/tests/data/hash/whirlpool.vec botan3-3.12.0+dfsg/src/tests/data/hash/whirlpool.vec --- botan3-3.7.1+dfsg/src/tests/data/hash/whirlpool.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/hash/whirlpool.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,3 +1,6 @@ + +#test cpuid avx512 avx2 + [Whirlpool] In = Out = 19FA61D75522A4669B44E39C1D2E1726C530232130D407F89AFEE0964997F7A73E83BE698B288FEBCF88E3E03C4F0757EA8964E59B63D93708B138CC42A66EB3 @@ -23,3 +26,14 @@ In = 6162636462636465636465666465666765666768666768696768696A68696A6B Out = 2A987EA40F917061F5D6F0A0E4644F488A7A5A52DEEE656207C562F988E95C6916BDC8031BC5BE1B7B947639FE050B56939BAAA0ADFF9AE6745B7B181C3BE3FD +# Following generated by OpenSSL + +In = 6162636465666768696A6B6C6D6E6F707172737475767778797A6162636465666768696A6B6C6D6E6F707172737475767778797A6162636465666768696A6B6C6D6E6F707172737475767778797A6162636465666768696A6B6C6D6E6F707172737475767778797A +Out = 9FE4AFFE44E3D16D4E6109A252AEAF3FBD46F8A402A5CDC10EEE48B6F64BE2DEEE2B9EE62EA5C037236CEA0B71CB1909A421672CA23662558CD7D98CCBD820EC + +In = 6162636465666768696A6B6C6D6E6F707172737475767778797A303132333435363738396162636465666768696A6B6C6D6E6F707172737475767778797A303132333435363738396162636465666768696A6B6C6D6E6F707172737475767778797A303132333435363738396162636465666768696A6B6C6D6E6F707172737475767778797A30313233343536373839 +Out = CAA3E64B61ADAEDFBB32B955F756F61D2B8DFABF04BFD4458438632431ABF7FD8F164836B8903AE02355D89D136321689E571472C169B89A380FC5727FA6808F + +In = FF3435363738393A3B3C3D3E3F4041424344458A4748494A4B4C4D4E4F505152535455565758955A5B5C5D5E5F606162636465666768696A6BA06D6E6F707172737475767778797A7B7C7D7EB3808182838485868788898A8B8C8D8E8F90915E939495969798999A9B9C9D9E9FA0A1A2A3A469A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B774B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CA07CCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDD12DFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF03DF2F3F4F5F6F7F8F9FAFBFCFDFEFF00010203C805060708090A0B0C0D0E0F10111213141516DB18191A1B1C1D1E1F20212223242526272829E62B2C2D2E2F303132333435363738393A3B3CF13E3F404142434445464748494A4B4C4D4E4F9C5152535455565758595A5B5C5D5E5F606162AF6465666768696A6B6C6D6E6F707172737475BA7778797A7B7C7D7E7F808182838485868788458A8B8C8D8E8F909192939495969798999A9B509D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE63B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C10EC3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D419D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E724E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FA37FCFDFEFF000102030405060708090A0B0C0DC20F101112131415161718191A1B1C1D1E1F20ED22232425262728292A2B2C2D2E2F30313233F835363738393A3B3C3D3E3F404142434445468B48494A4B4C4D4E4F50515253545556575859965B5C5D5E5F606162636465666768696A6B6CA16E6F707172737475767778797A7B7C7D7E7F4C8182838485868788898A8B8C8D8E8F9091925F9495969798999A9B9C9D9E9FA0A1A2A3A4A56AA7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B875BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACB00CDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDE13E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F13EF3F4F5F6F7F8F9FAFBFCFDFEFF0001020304C9060708090A0B0C0D0E0F1011121314151617D4191A1B1C1D1E1F202122232425262728292AE72C2D2E2F303132333435363738393A3B3C3DF23F404142434445464748494A4B4C4D4E4F509D52535455565758595A5B5C5D5E5F60616263A865666768696A6B6C6D6E6F70717273747576BB78797A7B7C7D7E7F80818283848586878889468B8C8D8E8F909192939495969798999A9B9C519E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAF7CB1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C20FC4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D51AD7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E825EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFB30FDFEFF000102030405060708090A0B0C0D0EC3101112131415161718191A1B1C1D1E1F2021EE232425262728292A2B2C2D2E2F3031323334F9363738393A3B3C3D3E3F404142434445464784494A4B4C4D4E4F505152535455565758595A975C5D5E5F606162636465666768696A6B6C6DA26F707172737475767778797A7B7C7D7E7F804D82838485868788898A8B8C8D8E8F909192935895969798999A9B9C9D9E9FA0A1A2A3A4A5A66BA8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B976BBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCC01CECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF2CE1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F23FF4F5F6F7F8F9FAFBFCFDFEFF000102030405CA0708090A0B0C0D0E0F101112131415161718D51A1B1C1D1E1F202122232425262728292A2BE02D2E2F303132333435363738393A3B3C3D3EF3404142434445464748494A4B4C4D4E4F50519E535455565758595A5B5C5D5E5F6061626364A9666768696A6B6C6D6E6F7071727374757677B4797A7B7C7D7E7F808182838485868788898A478C8D8E8F909192939495969798999A9B9C9D529FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB07DB2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C308C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D61BD8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E926EBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFC31FEFF000102030405060708090A0B0C0D0E0FDC1112131415161718191A1B1C1D1E1F202122EF2425262728292A2B2C2D2E2F303132333435FA3738393A3B3C3D3E3F404142434445464748854A4B4C4D4E4F505152535455565758595A5B905D5E5F606162636465666768696A6B6C6D6EA3707172737475767778797A7B7C7D7E7F80814E838485868788898A8B8C8D8E8F909192939459969798999A9B9C9D9E9FA0A1A2A3A4A5A6A764A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BA77BCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCD02CFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE02DE2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F338F5F6F7F8F9FAFBFCFDFEFF00010203040506CB08090A0B0C0D0E0F10111213141516171819D61B1C1D1E1F202122232425262728292A2B2CE12E2F303132333435363738393A3B3C3D3E3F8C4142434445464748494A4B4C4D4E4F5051529F5455565758595A5B5C5D5E5F606162636465AA6768696A6B6C6D6E6F707172737475767778B57A7B7C7D7E7F808182838485868788898A8B408D8E8F909192939495969798999A9B9C9D9E53A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B17EB3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C409C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D714D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EA27ECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFD32FF000102030405060708090A0B0C0D0E0F10DD12131415161718191A1B1C1D1E1F20212223E825262728292A2B2C2D2E2F30313233343536FB38393A3B3C3D3E3F40414243444546474849864B4C4D4E4F505152535455565758595A5B5C915E5F606162636465666768696A6B6C6D6E6FBC7172737475767778797A7B7C7D7E7F8081824F8485868788898A8B8C8D8E8F9091929394955A9798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A865AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABB70BDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCE03D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E12EE3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F439F6F7F8F9FAFBFCFDFEFF0001020304050607C4090A0B0C0D0E0F101112131415161718191AD71C1D1E1F202122232425262728292A2B2C2DE22F303132333435363738393A3B3C3D3E3F408D42434445464748494A4B4C4D4E4F505152539855565758595A5B5C5D5E5F60616263646566AB68696A6B6C6D6E6F70717273747576777879B67B7C7D7E7F808182838485868788898A8B8C418E8F909192939495969798999A9B9C9D9E9F6CA1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B27FB4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C50AC7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D815DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEB20EDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFE33000102030405060708090A0B0C0D0E0F1011DE131415161718191A1B1C1D1E1F2021222324E9262728292A2B2C2D2E2F3031323334353637F4393A3B3C3D3E3F404142434445464748494A874C4D4E4F505152535455565758595A5B5C5D925F606162636465666768696A6B6C6D6E6F70BD72737475767778797A7B7C7D7E7F808182834885868788898A8B8C8D8E8F909192939495965B98999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A966ABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBC71BEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECF1CD1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E22FE4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F53AF7F8F9FAFBFCFDFEFF000102030405060708C50A0B0C0D0E0F101112131415161718191A1BD01D1E1F202122232425262728292A2B2C2D2EE3303132333435363738393A3B3C3D3E3F40418E434445464748494A4B4C4D4E4F505152535499565758595A5B5C5D5E5F6061626364656667A4696A6B6C6D6E6F707172737475767778797AB77C7D7E7F808182838485868788898A8B8C8D428F909192939495969798999A9B9C9D9E9FA06DA2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B378B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C60BC8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D916DBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBEC21EEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFFCC0102030405060708090A0B0C0D0E0F101112DF1415161718191A1B1C1D1E1F202122232425EA2728292A2B2C2D2E2F303132333435363738F53A3B3C3D3E3F404142434445464748494A4B804D4E4F505152535455565758595A5B5C5D5E93606162636465666768696A6B6C6D6E6F7071BE737475767778797A7B7C7D7E7F808182838449868788898A8B8C8D8E8F909192939495969754999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AA67ACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBD72BFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD01DD2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E328E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F63BF8F9FAFBFCFDFEFF00010203040506070809C60B0C0D0E0F101112131415161718191A1B1CD11E1F202122232425262728292A2B2C2D2E2FFC3132333435363738393A3B3C3D3E3F4041428F4445464748494A4B4C4D4E4F5051525354559A5758595A5B5C5D5E5F606162636465666768A56A6B6C6D6E6F707172737475767778797A7BB07D7E7F808182838485868788898A8B8C8D8E43909192939495969798999A9B9C9D9E9FA0A16EA3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B479B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C704C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DA17DCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECED22EFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF00CD02030405060708090A0B0C0D0E0F10111213D815161718191A1B1C1D1E1F20212223242526EB28292A2B2C2D2E2F30313233343536373839F63B3C3D3E3F404142434445464748494A4B4C814E4F505152535455565758595A5B5C5D5E5FAC6162636465666768696A6B6C6D6E6F707172BF7475767778797A7B7C7D7E7F8081828384854A8788898A8B8C8D8E8F909192939495969798559A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAAB60ADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBE73C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D11ED3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E429E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F734F9FAFBFCFDFEFF000102030405060708090AC70C0D0E0F101112131415161718191A1B1C1DD21F202122232425262728292A2B2C2D2E2F30FD32333435363738393A3B3C3D3E3F404142438845464748494A4B4C4D4E4F505152535455569B58595A5B5C5D5E5F60616263646566676869A66B6C6D6E6F707172737475767778797A7B7CB17E7F808182838485868788898A8B8C8D8E8F5C9192939495969798999A9B9C9D9E9FA0A1A26FA4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B57AB7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C805CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADB10DDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEE23F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF0001CE030405060708090A0B0C0D0E0F1011121314D9161718191A1B1C1D1E1F2021222324252627E4292A2B2C2D2E2F303132 +Out = CAA5094695E7C17FB14ED2263D5B59CFDA8E270F417CB3FF3EB1E025796815AA44F5DA4682E0C142967E66C56E97B8A8131FDC5684539969EB18BBAC767CC94F + diff -Nru botan3-3.7.1+dfsg/src/tests/data/hostnames.vec botan3-3.12.0+dfsg/src/tests/data/hostnames.vec --- botan3-3.7.1+dfsg/src/tests/data/hostnames.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/hostnames.vec 2026-05-07 01:38:28.000000000 +0000 @@ -55,6 +55,18 @@ Issued = b*z.example.net Hostname = bz.example.net +Issued = 1example.com +Hostname = 1EXAMPLE.com + +Issued = my-host.example.com +Hostname = my-HOST.example.com + +Issued = *.example.com +Hostname = averylongsubdomainname.example.com + +Issued = www*.example.com +Hostname = www.example.com + [Invalid] Issued = Hostname = empty.com @@ -134,3 +146,9 @@ Issued = *.*.example.com Hostname = foo.bar.example.com + +Issued = O.example.com +Hostname = 0.example.com + +Issued = @.example.com +Hostname = `.example.com \ No newline at end of file diff -Nru botan3-3.7.1+dfsg/src/tests/data/kdf/hkdf.vec botan3-3.12.0+dfsg/src/tests/data/kdf/hkdf.vec --- botan3-3.7.1+dfsg/src/tests/data/kdf/hkdf.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/kdf/hkdf.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,4 @@ # SHA-1 and SHA-256 data from RFC 5869 -# SHA-512 data from https://www.kullo.net/blog/hkdf-sha-512-test-vectors/ [HKDF(HMAC(SHA-1))] Salt = 000102030405060708090A0B0C diff -Nru botan3-3.7.1+dfsg/src/tests/data/mac/cmac.vec botan3-3.12.0+dfsg/src/tests/data/mac/cmac.vec --- botan3-3.7.1+dfsg/src/tests/data/mac/cmac.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/mac/cmac.vec 2026-05-07 01:38:28.000000000 +0000 @@ -154,3 +154,9 @@ Key = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 In = 0000000000000000000000000000000000000000000000000000000000000000 Out = C07C71A284C7A63023146376F895E83543EA3547A9268861CD00B7AC405AFC34A9EC86A30622D38C7E765521B148AAB5DEDD3AE80496ED56BCCB17B2E3D18009 + +[CMAC(ARIA-128)] +# Wycheproof +Key = e34f15c7bd819930fe9d66e0c166e61c +In = +Out = 9572121a969370034390cd00d6a89130 diff -Nru botan3-3.7.1+dfsg/src/tests/data/mac/gmac.vec botan3-3.12.0+dfsg/src/tests/data/mac/gmac.vec --- botan3-3.7.1+dfsg/src/tests/data/mac/gmac.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/mac/gmac.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,4 @@ -#test cpuid aesni clmul pmull ssse3 +#test cpuid avx512_clmul aesni clmul pmull ssse3 # Testvectors in this file have been generated using the Bouncy Castle Crypto # API version 1.54 (https://www.bouncycastle.org) @@ -80,3 +80,646 @@ Key = 0994C9E2A62E30A727BC69AE2DACC8823B00DD2888ECE29C2CB764A38FD30FBB In = 4E1F2940DA4E4F5616304E7E398070FD106B32B68A0A47977CD008760F0972B5B519FD91C4AEDE49AC869D0766AF8C1A8309 Out = FFF8F5311D7A16F78930F319EC3E9F8A + +[GMAC(AES-128)] +# Generated by OpenSSL + +IV = d0442700e2bf1bc92bb27b96 +Key = 1aa0239e0e3ab4882f7e2aa691c55c5a +In = 3bad +Out = 473f2d493b464daecb2bd72536259648 + +IV = 442700e2bf1bc92bb27b963a +Key = a0239e0e3ab4882f7e2aa691c55c5adf +In = ad24142520db7c3edb76 +Out = 8507f9e22c72a8670ac14ea9b407b4a1 + +IV = 2700e2bf1bc92bb27b963a6b +Key = 239e0e3ab4882f7e2aa691c55c5adff7 +In = 24142520db7c3edb766470 +Out = 35251fdf906c8b041179fd0fa7b5e969 + +IV = 00e2bf1bc92bb27b963a6b58 +Key = 9e0e3ab4882f7e2aa691c55c5adff755 +In = 142520db7c3edb766470247555db54487172aa +Out = 7ceca49bdac6e02a2c087a761be940e9 + +IV = e2bf1bc92bb27b963a6b58f2 +Key = 0e3ab4882f7e2aa691c55c5adff755d5 +In = 2520db7c3edb766470247555db54487172aae2722a7d3c4b54 +Out = e259294282bb0a27c10f0efa0bdb9fb5 + +IV = bf1bc92bb27b963a6b58f2e9 +Key = 3ab4882f7e2aa691c55c5adff755d5dc +In = 20db7c3edb766470247555db54487172aae2722a7d3c4b54aafb9ed08158602626 +Out = 4d03da8ab44812dd4dc7345af04ec7ba + +IV = 1bc92bb27b963a6b58f2e917 +Key = b4882f7e2aa691c55c5adff755d5dcd5 +In = db7c3edb766470247555db54487172aae2722a7d3c4b54aafb9ed08158602626f9ef77 +Out = 96cf3b403838482b1a9703d539b13266 + +IV = c92bb27b963a6b58f2e917d3 +Key = 882f7e2aa691c55c5adff755d5dcd5a0 +In = 7c3edb766470247555db54487172aae2722a7d3c4b54aafb9ed08158602626f9ef77d704da786c +Out = 71523c7092f20fd8b685e380ce12ee0c + +IV = 2bb27b963a6b58f2e917d39d +Key = 2f7e2aa691c55c5adff755d5dcd5a055 +In = 3edb766470247555db54487172aae2722a7d3c4b54aafb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6 +Out = ff34dee2f18ac573ffa84d246150b04e + +IV = b27b963a6b58f2e917d39dd0 +Key = 7e2aa691c55c5adff755d5dcd5a05500 +In = db766470247555db54487172aae2722a7d3c4b54aafb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc +Out = 31eb8601219c32013b732b59fd59bb8a + +IV = 7b963a6b58f2e917d39dd08b +Key = 2aa691c55c5adff755d5dcd5a055003b +In = 766470247555db54487172aae2722a7d3c4b54aafb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a1 +Out = df8016889ec93b541c638d93c183c376 + +IV = 963a6b58f2e917d39dd08b26 +Key = a691c55c5adff755d5dcd5a055003b52 +In = 6470247555db54487172aae2722a7d3c4b54aafb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e2 +Out = 49cf68cfebb69c17b9e18a53331ce228 + +IV = 3a6b58f2e917d39dd08b26fe +Key = 91c55c5adff755d5dcd5a055003b52f7 +In = 70247555db54487172aae2722a7d3c4b54aafb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e +Out = a57a440cbf483b57c1592fbcc2d15b3e + +IV = 6b58f2e917d39dd08b26fe1e +Key = c55c5adff755d5dcd5a055003b52f779 +In = 247555db54487172aae2722a7d3c4b54aafb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747 +Out = 2c8f10dcf194d4abf9bda32a100a401a + +IV = 58f2e917d39dd08b26fe1e7e +Key = 5c5adff755d5dcd5a055003b52f77980 +In = 7555db54487172aae2722a7d3c4b54aafb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d +Out = 15eee20ebd71f15426e22df4d4ce7b90 + +IV = f2e917d39dd08b26fe1e7e9c +Key = 5adff755d5dcd5a055003b52f7798096 +In = 55db54487172aae2722a7d3c4b54aafb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983 +Out = 4d7dc98ab0b3c23a4c8191357d938b08 + +IV = e917d39dd08b26fe1e7e9ce2 +Key = dff755d5dcd5a055003b52f7798096d7 +In = db54487172aae2722a7d3c4b54aafb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c +Out = 4736663e49754a01e24bfe0b815a975f + +IV = 17d39dd08b26fe1e7e9ce245 +Key = f755d5dcd5a055003b52f7798096d7f3 +In = 54487172aae2722a7d3c4b54aafb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bf +Out = cc6d12b7f642193057e9c04a9a38e736 + +IV = d39dd08b26fe1e7e9ce2458d +Key = 55d5dcd5a055003b52f7798096d7f38f +In = 487172aae2722a7d3c4b54aafb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b8 +Out = a9165395121b9af8058e79633e84ac83 + +IV = 9dd08b26fe1e7e9ce2458dc6 +Key = d5dcd5a055003b52f7798096d7f38fdb +In = 7172aae2722a7d3c4b54aafb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b +Out = 74c45301468456e1bd03ac41346a7f41 + +IV = d08b26fe1e7e9ce2458dc65b +Key = dcd5a055003b52f7798096d7f38fdb07 +In = 72aae2722a7d3c4b54aafb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190 +Out = 5c88c076148dd3b861384b0bfd17bf2b + +IV = 8b26fe1e7e9ce2458dc65b71 +Key = d5a055003b52f7798096d7f38fdb0779 +In = aae2722a7d3c4b54aafb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e2 +Out = 85aa5bad0cba4e2cc0a4f23a0ec8b488 + +IV = 26fe1e7e9ce2458dc65b7104 +Key = a055003b52f7798096d7f38fdb07797d +In = e2722a7d3c4b54aafb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f +Out = ebe3c65b4d60822cc6e6ce3c87e18279 + +IV = fe1e7e9ce2458dc65b7104b3 +Key = 55003b52f7798096d7f38fdb07797d1c +In = 722a7d3c4b54aafb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331d +Out = ac308595f3303535e8f685bdab088ae4 + +IV = 1e7e9ce2458dc65b7104b369 +Key = 003b52f7798096d7f38fdb07797d1c9b +In = 2a7d3c4b54aafb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0def +Out = af62e9193acc85d32a8ec616b0d5ae6f + +IV = 7e9ce2458dc65b7104b36953 +Key = 3b52f7798096d7f38fdb07797d1c9b56 +In = 7d3c4b54aafb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc +Out = 5a3d4ad648d65de9c4e30ee2bf38cc19 + +IV = 9ce2458dc65b7104b369537c +Key = 52f7798096d7f38fdb07797d1c9b5657 +In = 3c4b54aafb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be +Out = d3f9b35366af5559fde26821abeb5510 + +IV = e2458dc65b7104b369537c94 +Key = f7798096d7f38fdb07797d1c9b56573d +In = 4b54aafb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a948 +Out = fc8ae633540617704fd8e18e63f42009 + +IV = 458dc65b7104b369537c94b2 +Key = 798096d7f38fdb07797d1c9b56573db2 +In = 54aafb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8 +Out = f3d3886b5f1228f6d28c638576650021 + +IV = 8dc65b7104b369537c94b2a3 +Key = 8096d7f38fdb07797d1c9b56573db284 +In = aafb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b +Out = fbbb4f6dd28ce3d934f66e39ceb22d6b + +IV = c65b7104b369537c94b2a309 +Key = 96d7f38fdb07797d1c9b56573db2847d +In = fb9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa4 +Out = d37e86a43300b28bcbc632917acb5054 + +IV = 5b7104b369537c94b2a30911 +Key = d7f38fdb07797d1c9b56573db2847d30 +In = 9ed08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dc +Out = bb780b647aecfd1944feb23ae5e0c62d + +IV = 7104b369537c94b2a309115c +Key = f38fdb07797d1c9b56573db2847d303d +In = d08158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3 +Out = 6816028ce59cabdad1db9d87a2f9ce7a + +IV = 04b369537c94b2a309115cee +Key = 8fdb07797d1c9b56573db2847d303de1 +In = 8158602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6a +Out = 7200bf65fbfe64cf6f92deedb674d41e + +IV = b369537c94b2a309115ceeee +Key = db07797d1c9b56573db2847d303de134 +In = 58602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdc +Out = a4834aa3ab714f7d88da00b5c4dcccbc + +IV = 69537c94b2a309115ceeee84 +Key = 07797d1c9b56573db2847d303de13482 +In = 602626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b +Out = 4b5276abb3caae6e212c5aca24341093 + +IV = 537c94b2a309115ceeee8452 +Key = 797d1c9b56573db2847d303de13482d8 +In = 2626f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818 +Out = e34e2263f9e30210d6b977cc923f188c + +IV = 7c94b2a309115ceeee845279 +Key = 7d1c9b56573db2847d303de13482d89b +In = 26f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56 +Out = 6301162eb4f635342a9fc893892c7e82 + +IV = 94b2a309115ceeee84527918 +Key = 1c9b56573db2847d303de13482d89b6c +In = f9ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce +Out = d342eeb9f59e25ea3394b45796b0d794 + +IV = b2a309115ceeee8452791890 +Key = 9b56573db2847d303de13482d89b6c13 +In = ef77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259 +Out = fccdbc9ed06cdc041d8a957941f4ae50 + +IV = a309115ceeee845279189014 +Key = 56573db2847d303de13482d89b6c139d +In = 77d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1a +Out = 36faf1d4ac3d90e8e3d6578aab778c8d + +IV = 09115ceeee84527918901428 +Key = 573db2847d303de13482d89b6c139da0 +In = d704da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac996 +Out = e3d5779a9862f92d127cadc69833769f + +IV = 115ceeee8452791890142897 +Key = 3db2847d303de13482d89b6c139da008 +In = 04da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2 +Out = d190c1b29675c3b9e45f1ae78fe92aae + +IV = 5ceeee845279189014289740 +Key = b2847d303de13482d89b6c139da008a1 +In = da786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2 +Out = 933d85878482f41379819b08284609ca + +IV = eeee8452791890142897408d +Key = 847d303de13482d89b6c139da008a167 +In = 786c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa +Out = 448eaf2dc9f482cdf8c5d749d602e47c + +IV = ee8452791890142897408d0c +Key = 7d303de13482d89b6c139da008a167e8 +In = 6c5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb +Out = 92652712c20c88487bb6f54840a2632a + +IV = 8452791890142897408d0caa +Key = 303de13482d89b6c139da008a167e8a0 +In = 5abdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5 +Out = 013494ffd66bd5d28541f60bb77ddcba + +IV = 52791890142897408d0caa75 +Key = 3de13482d89b6c139da008a167e8a06d +In = bdf6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c8 +Out = 069439f8e1826e2ab6ec435665598846 + +IV = 791890142897408d0caa758d +Key = e13482d89b6c139da008a167e8a06d93 +In = f6b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47f +Out = e67baf019680f950a648e8f0954cf286 + +IV = 1890142897408d0caa758d82 +Key = 3482d89b6c139da008a167e8a06d9365 +In = b09902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0 +Out = 2b47d2d8428b1cb8ee924cf64cf00c37 + +IV = 90142897408d0caa758d82c6 +Key = 82d89b6c139da008a167e8a06d936503 +In = 9902e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98f +Out = e1d03ba85e879f93d170953336ba1bf6 + +IV = 142897408d0caa758d82c608 +Key = d89b6c139da008a167e8a06d93650398 +In = 02e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908 +Out = d999e00e7e89c377b7f0f36680c44af3 + +IV = 2897408d0caa758d82c6082b +Key = 9b6c139da008a167e8a06d93650398c4 +In = e6d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4d +Out = 6ee02c1fe1435538f185badb6ac7d1b3 + +IV = 97408d0caa758d82c6082b29 +Key = 6c139da008a167e8a06d93650398c422 +In = d6472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9 +Out = 6e97bced9fc122faecb7eae415d3e15b + +IV = 408d0caa758d82c6082b29c1 +Key = 139da008a167e8a06d93650398c42261 +In = 472ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3 +Out = 53b9f06f228b1791c67b02a80b0e77ee + +IV = 8d0caa758d82c6082b29c1af +Key = 9da008a167e8a06d93650398c4226181 +In = 2ddc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147 +Out = 300f887ea435598a78505630aa6dda14 + +IV = 0caa758d82c6082b29c1af35 +Key = a008a167e8a06d93650398c422618121 +In = dc771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b9 +Out = 497c2c422fd7f215a67a322922fd7825 + +IV = aa758d82c6082b29c1af3597 +Key = 08a167e8a06d93650398c42261812175 +In = 771690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c +Out = 1cf555f7f8cc15ccdb6b73ed714b1c77 + +IV = 758d82c6082b29c1af3597d5 +Key = a167e8a06d93650398c422618121757e +In = 1690ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55 +Out = 8fe58d1ea9249ecc26641ad16795f912 + +IV = 8d82c6082b29c1af3597d5b9 +Key = 67e8a06d93650398c422618121757e0f +In = 90ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f +Out = 8f16fa337444d69a45815cb3b402d9de + +IV = 82c6082b29c1af3597d5b9ee +Key = e8a06d93650398c422618121757e0fd7 +In = ace5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c +Out = 450d37f688e294bc9307b2bb5bb65e67 + +IV = c6082b29c1af3597d5b9eed5 +Key = a06d93650398c422618121757e0fd740 +In = e5f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7 +Out = b1f735909aae8fcca13a70441c226bac + +IV = 082b29c1af3597d5b9eed523 +Key = 6d93650398c422618121757e0fd74072 +In = f5a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19 +Out = 374765da436b36c2e3567d0fbb45d532 + +IV = 2b29c1af3597d5b9eed523b1 +Key = 93650398c422618121757e0fd7407215 +In = a138b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22a +Out = 8229ec8b9fc83c68fecf0453b3677a34 + +IV = 29c1af3597d5b9eed523b151 +Key = 650398c422618121757e0fd7407215a0 +In = 38b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8 +Out = c2d4828f31487bbcae5a3fe9e6713141 + +IV = c1af3597d5b9eed523b151ab +Key = 0398c422618121757e0fd7407215a056 +In = b09a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e +Out = 5d2bfe59307525fa1d3b4b4485e3d47a + +IV = af3597d5b9eed523b151abec +Key = 98c422618121757e0fd7407215a0569f +In = 9a7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a +Out = ca69826308621f5aebc681943dbf0c8b + +IV = 3597d5b9eed523b151abec09 +Key = c422618121757e0fd7407215a0569fe8 +In = 7578e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f1402004 +Out = e2d76f9fe4283a1c90fb4d61c1bf1b9a + +IV = 97d5b9eed523b151abec098f +Key = 22618121757e0fd7407215a0569fe8e8 +In = 78e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c +Out = 9054b5a9365e59c7fe1bc7d131b6cc38 + +IV = d5b9eed523b151abec098fcd +Key = 618121757e0fd7407215a0569fe8e8a7 +In = e20ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4 +Out = e4ce4838f614055a4f078d95ae3e49ed + +IV = b9eed523b151abec098fcd67 +Key = 8121757e0fd7407215a0569fe8e8a7a3 +In = 0ac9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44 +Out = 9b9d4076ac20b6e29d2a9607ede10680 + +IV = eed523b151abec098fcd6765 +Key = 21757e0fd7407215a0569fe8e8a7a30f +In = c9b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747 +Out = 8844a31e4ff0e1021e98a22952b6fe05 + +IV = d523b151abec098fcd67659b +Key = 757e0fd7407215a0569fe8e8a7a30f8f +In = b13212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c +Out = 223c23f31c389779a0c999843165e7fb + +IV = 23b151abec098fcd67659b21 +Key = 7e0fd7407215a0569fe8e8a7a30f8fb5 +In = 3212be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c0 +Out = ac89ddce4f0a5a74b587e5b796debbc2 + +IV = b151abec098fcd67659b2127 +Key = 0fd7407215a0569fe8e8a7a30f8fb505 +In = 12be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a58 +Out = c862941e312a708a1cfbff45af94fec2 + +IV = 51abec098fcd67659b21272c +Key = d7407215a0569fe8e8a7a30f8fb50504 +In = be0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49 +Out = da6d7db3876a2cd476ed38a73207c1be + +IV = abec098fcd67659b21272ccf +Key = 407215a0569fe8e8a7a30f8fb505041d +In = 0e0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb523110 +Out = 50059e84677030cfa3194b543ca3bd69 + +IV = ec098fcd67659b21272ccfcd +Key = 7215a0569fe8e8a7a30f8fb505041d98 +In = 0db53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf +Out = 87ecdb644f1a6ca26962a2d0dff74558 + +IV = 098fcd67659b21272ccfcd6b +Key = 15a0569fe8e8a7a30f8fb505041d989e +In = b53ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583 +Out = a90f22d12f6b4c30420f084447b4735c + +IV = 8fcd67659b21272ccfcd6bac +Key = a0569fe8e8a7a30f8fb505041d989ea8 +In = 3ad0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9 +Out = e259f0ec8743d74853e9e5de2514131d + +IV = cd67659b21272ccfcd6bacfa +Key = 569fe8e8a7a30f8fb505041d989ea8ba +In = d0eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f76051348480 +Out = a2cace096eac9ef3a7c678d5226d10f5 + +IV = 67659b21272ccfcd6bacfafd +Key = 9fe8e8a7a30f8fb505041d989ea8baf9 +In = eca7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12 +Out = af82027d71f56d02ca478c6ca0596c36 + +IV = 659b21272ccfcd6bacfafdd2 +Key = e8e8a7a30f8fb505041d989ea8baf97c +In = a7588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae +Out = 236c135c0f3bd80e0f76806eddeb5b5d + +IV = 9b21272ccfcd6bacfafdd2a7 +Key = e8a7a30f8fb505041d989ea8baf97ce4 +In = 588747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75 +Out = 9feeb513fab372bea541c96484c3329f + +IV = 21272ccfcd6bacfafdd2a727 +Key = a7a30f8fb505041d989ea8baf97ce4c4 +In = 8747b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e45946 +Out = 72ea0faa27257e4b504c19c9a7b7f268 + +IV = 272ccfcd6bacfafdd2a7270c +Key = a30f8fb505041d989ea8baf97ce4c439 +In = 47b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f +Out = 7e54797cf1fcbbd3f0234394bb1e8c19 + +IV = 2ccfcd6bacfafdd2a7270c2d +Key = 0f8fb505041d989ea8baf97ce4c43933 +In = b1008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebf +Out = 002943f027a99c829cc349f2516a14b6 + +IV = cfcd6bacfafdd2a7270c2d1a +Key = 8fb505041d989ea8baf97ce4c43933cb +In = 008708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610 +Out = fb58d5f075a0f649557f849d98e97006 + +IV = cd6bacfafdd2a7270c2d1a6c +Key = b505041d989ea8baf97ce4c43933cbcf +In = 8708e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd38 +Out = e9e18b3a461e711434653a3b26cb19ba + +IV = 6bacfafdd2a7270c2d1a6cf4 +Key = 05041d989ea8baf97ce4c43933cbcf35 +In = 08e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f3 +Out = 4398e995bdb85e400604c418af2cc879 + +IV = acfafdd2a7270c2d1a6cf495 +Key = 041d989ea8baf97ce4c43933cbcf359d +In = e3ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07 +Out = e594dc6ec847d0c3e33326b1705dcaf1 + +IV = fafdd2a7270c2d1a6cf495bf +Key = 1d989ea8baf97ce4c43933cbcf359d67 +In = ff6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb +Out = 72f4e9bf311ada3985fdef8d46e3129d + +IV = fdd2a7270c2d1a6cf495bfce +Key = 989ea8baf97ce4c43933cbcf359d672e +In = 6d258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77 +Out = 5b22dc1838783db773c27c4762a2b651 + +IV = d2a7270c2d1a6cf495bfcec8 +Key = 9ea8baf97ce4c43933cbcf359d672e71 +In = 258800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c8 +Out = bb637fe6d9c932e6d8510075acb60607 + +IV = a7270c2d1a6cf495bfcec834 +Key = a8baf97ce4c43933cbcf359d672e71a7 +In = 8800a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd +Out = a3fe2f114b291fa239c3311ffdf7ab92 + +IV = 270c2d1a6cf495bfcec83491 +Key = baf97ce4c43933cbcf359d672e71a753 +In = 00a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409 +Out = 849eb6fafd039b796a35a71683e8b017 + +IV = 0c2d1a6cf495bfcec834916a +Key = f97ce4c43933cbcf359d672e71a75346 +In = a3c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba3235 +Out = 85ade2121d8a68c38dda1ad4ddf1053b + +IV = 2d1a6cf495bfcec834916a39 +Key = 7ce4c43933cbcf359d672e71a753464b +In = c983fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c03 +Out = 70d74b15031b10c381a46112c51e382b + +IV = 1a6cf495bfcec834916a393c +Key = e4c43933cbcf359d672e71a753464bdb +In = 83fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691 +Out = de276dd617fa367a94ff74ebc7d1247c + +IV = 6cf495bfcec834916a393ce8 +Key = c43933cbcf359d672e71a753464bdb01 +In = fd8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce +Out = ea69cff4393adc6b29c7347f85996cf6 + +IV = f495bfcec834916a393ce826 +Key = 3933cbcf359d672e71a753464bdb01f4 +In = 8a36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54 +Out = 23a8512433032053461188312ca7823a + +IV = 95bfcec834916a393ce82696 +Key = 33cbcf359d672e71a753464bdb01f4b4 +In = 36e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a +Out = 3424272332f6568c6eff0f20ea15a982 + +IV = bfcec834916a393ce8269690 +Key = cbcf359d672e71a753464bdb01f4b45a +In = e795562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a4 +Out = e4851a5e4626575f2226ce93a2e239f4 + +IV = cec834916a393ce8269690f6 +Key = cf359d672e71a753464bdb01f4b45a1c +In = 95562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a894 +Out = dbeb14b75e6a72877cf3849521460476 + +IV = c834916a393ce8269690f608 +Key = 359d672e71a753464bdb01f4b45a1c40 +In = 562c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0d +Out = b0440755ce0f4268c8aca59b1e0676be + +IV = 34916a393ce8269690f60872 +Key = 9d672e71a753464bdb01f4b45a1c4051 +In = 2c1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443 +Out = 0f5a39e2bc6713c612e7604e7d64ade0 + +IV = 916a393ce8269690f60872dc +Key = 672e71a753464bdb01f4b45a1c4051b6 +In = 1c34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443d8b67b4c33c346 +Out = c88fe55c7a47091fbfff99f4488de24e + +IV = 6a393ce8269690f60872dc9c +Key = 2e71a753464bdb01f4b45a1c4051b64c +In = 34bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443d8b67b4c33c346938ed37c +Out = 66af2b5f0142a6e1b41203398d7f3c6f + +IV = 393ce8269690f60872dc9cd9 +Key = 71a753464bdb01f4b45a1c4051b64c66 +In = bff06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443d8b67b4c33c346938ed37ce18031b1dc2a66cf +Out = 2cf5fd616a7e8c887bfa5d19dd80eae5 + +IV = 3ce8269690f60872dc9cd949 +Key = a753464bdb01f4b45a1c4051b64c663f +In = f06a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443d8b67b4c33c346938ed37ce18031b1dc2a66cf22dd +Out = e2ce090287f13631bf84ca3b143ae79b + +IV = e8269690f60872dc9cd949d4 +Key = 53464bdb01f4b45a1c4051b64c663f00 +In = 6a9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443d8b67b4c33c346938ed37ce18031b1dc2a66cf22ddff328f7985f0887d48 +Out = fb4ff34829167414ba7f99ea1d427c85 + +IV = 269690f60872dc9cd949d42a +Key = 464bdb01f4b45a1c4051b64c663f003a +In = 9bd14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443d8b67b4c33c346938ed37ce18031b1dc2a66cf22ddff328f7985f0887d48c54853a9 +Out = 964132912280a20e45c55a21e2a49d3a + +IV = 9690f60872dc9cd949d42a7c +Key = 4bdb01f4b45a1c4051b64c663f003a44 +In = d14e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443d8b67b4c33c346938ed37ce18031b1dc2a66cf22ddff328f7985f0887d48c54853a99395a98588cbdff1c1 +Out = a1a37c8b1dd8ba2543b49456614d0cd7 + +IV = 90f60872dc9cd949d42a7c7b +Key = db01f4b45a1c4051b64c663f003a44df +In = 4e7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443d8b67b4c33c346938ed37ce18031b1dc2a66cf22ddff328f7985f0887d48c54853a99395a98588cbdff1c18370c145 +Out = 06e6e98f2c1b43f5e9707c0484ace864 + +IV = f60872dc9cd949d42a7c7b35 +Key = 01f4b45a1c4051b64c663f003a44dfff +In = 7b7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443d8b67b4c33c346938ed37ce18031b1dc2a66cf22ddff328f7985f0887d48c54853a99395a98588cbdff1c18370c145dea845a6acd58d8113c2 +Out = 9ddb6106c0d8437b9b06d275b5c4956c + +IV = 0872dc9cd949d42a7c7b35ad +Key = f4b45a1c4051b64c663f003a44dfff3a +In = 7e18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443d8b67b4c33c346938ed37ce18031b1dc2a66cf22ddff328f7985f0887d48c54853a99395a98588cbdff1c18370c145dea845a6acd58d8113c219a193 +Out = 6e7574dc29d194a9b988cb853af262e2 + +IV = 72dc9cd949d42a7c7b35ad48 +Key = b45a1c4051b64c663f003a44dfff3a2f +In = 18b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443d8b67b4c33c346938ed37ce18031b1dc2a66cf22ddff328f7985f0887d48c54853a99395a98588cbdff1c18370c145dea845a6acd58d8113c219a1932eb9786ae7dd58b7 +Out = 0a423f3aed4133202e66b60fe435f8fe + +IV = dc9cd949d42a7c7b35ad482c +Key = 5a1c4051b64c663f003a44dfff3a2f0d +In = b83e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443d8b67b4c33c346938ed37ce18031b1dc2a66cf22ddff328f7985f0887d48c54853a99395a98588cbdff1c18370c145dea845a6acd58d8113c219a1932eb9786ae7dd58b74cdf0462261de4 +Out = 1e7358c1145a66119447f69a78715abf + +IV = 9cd949d42a7c7b35ad482cc1 +Key = 1c4051b64c663f003a44dfff3a2f0db0 +In = 3e616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443d8b67b4c33c346938ed37ce18031b1dc2a66cf22ddff328f7985f0887d48c54853a99395a98588cbdff1c18370c145dea845a6acd58d8113c219a1932eb9786ae7dd58b74cdf0462261de4cd9501 +Out = f66d8209ebfd9cd96b3e0538f09c1a8d + +IV = d949d42a7c7b35ad482cc156 +Key = 4051b64c663f003a44dfff3a2f0db0e5 +In = 616b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443d8b67b4c33c346938ed37ce18031b1dc2a66cf22ddff328f7985f0887d48c54853a99395a98588cbdff1c18370c145dea845a6acd58d8113c219a1932eb9786ae7dd58b74cdf0462261de4cd9501aa8013578f52db +Out = b20999a57bf6d7a5999998fb8d916a59 + +IV = 49d42a7c7b35ad482cc1563d +Key = 51b64c663f003a44dfff3a2f0db0e5ff +In = 6b359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443d8b67b4c33c346938ed37ce18031b1dc2a66cf22ddff328f7985f0887d48c54853a99395a98588cbdff1c18370c145dea845a6acd58d8113c219a1932eb9786ae7dd58b74cdf0462261de4cd9501aa8013578f52db1bcf56e9d658 +Out = a16f1d94ef5454e47947360b3eecf58d + +IV = d42a7c7b35ad482cc1563dd7 +Key = b64c663f003a44dfff3a2f0db0e5ff8b +In = 359da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443d8b67b4c33c346938ed37ce18031b1dc2a66cf22ddff328f7985f0887d48c54853a99395a98588cbdff1c18370c145dea845a6acd58d8113c219a1932eb9786ae7dd58b74cdf0462261de4cd9501aa8013578f52db1bcf56e9d65888a1e7e5714b0283 +Out = 6443ba00d9548c2b295afc1ac576ae93 + +IV = 2a7c7b35ad482cc1563dd703 +Key = 4c663f003a44dfff3a2f0db0e5ff8b64 +In = 9da9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443d8b67b4c33c346938ed37ce18031b1dc2a66cf22ddff328f7985f0887d48c54853a99395a98588cbdff1c18370c145dea845a6acd58d8113c219a1932eb9786ae7dd58b74cdf0462261de4cd9501aa8013578f52db1bcf56e9d65888a1e7e5714b02833634 +Out = b8312aae3e22f5de3443de4f0594d73b + +IV = 7c7b35ad482cc1563dd703a2 +Key = 663f003a44dfff3a2f0db0e5ff8b64a4 +In = a9ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443d8b67b4c33c346938ed37ce18031b1dc2a66cf22ddff328f7985f0887d48c54853a99395a98588cbdff1c18370c145dea845a6acd58d8113c219a1932eb9786ae7dd58b74cdf0462261de4cd9501aa8013578f52db1bcf56e9d65888a1e7e5714b02833634d19a4d90d744 +Out = 9c798c3297a6f637e3e23934311b7c41 + +IV = 7b35ad482cc1563dd703a2ef +Key = 3f003a44dfff3a2f0db0e5ff8b64a418 +In = ed37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443d8b67b4c33c346938ed37ce18031b1dc2a66cf22ddff328f7985f0887d48c54853a99395a98588cbdff1c18370c145dea845a6acd58d8113c219a1932eb9786ae7dd58b74cdf0462261de4cd9501aa8013578f52db1bcf56e9d65888a1e7e5714b02833634d19a4d90d7448f54a0304116cce3 +Out = fea5fc6ee12b768ae58d18ee6724e4d7 + +IV = 35ad482cc1563dd703a2ef4c +Key = 003a44dfff3a2f0db0e5ff8b64a4180f +In = 37567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443d8b67b4c33c346938ed37ce18031b1dc2a66cf22ddff328f7985f0887d48c54853a99395a98588cbdff1c18370c145dea845a6acd58d8113c219a1932eb9786ae7dd58b74cdf0462261de4cd9501aa8013578f52db1bcf56e9d65888a1e7e5714b02833634d19a4d90d7448f54a0304116cce3dcb5d729 +Out = 0b92b5388fb1f60ea5fce011f59e31a3 + +IV = ad482cc1563dd703a2ef4c8a +Key = 3a44dfff3a2f0db0e5ff8b64a4180f0d +In = 567da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443d8b67b4c33c346938ed37ce18031b1dc2a66cf22ddff328f7985f0887d48c54853a99395a98588cbdff1c18370c145dea845a6acd58d8113c219a1932eb9786ae7dd58b74cdf0462261de4cd9501aa8013578f52db1bcf56e9d65888a1e7e5714b02833634d19a4d90d7448f54a0304116cce3dcb5d7295d31590200 +Out = 9b0546c68ae38b65cff277a9951ab219 + +IV = 482cc1563dd703a2ef4c8aaf +Key = 44dfff3a2f0db0e5ff8b64a4180f0d32 +In = 7da96190f08d02faff81fd6968e28a06373ad902011f8230b375de09331de41f28a66d9c3b0a0defc359f2346171e2b3f4dc27e0d1493db9b92600be446ec933a9488c30f8581d4b61dbcfde98daa437dcfda3c740edeb6abfe5777f5cdbdcb03ac1c62b2cb818ade4624720215e4d56134a233d24ce2f572196abc2a85259cbee8051ed897e1ac99619c2f79ecdd2d3110c9ddbf7aa4e91b2566ebb76b5bc9164d8b5233343c88835b47fe9a9804cd0c300c98fb74908625e4dbe6ed5f6bdebb61441a9ac864351317bb7c68be3b147c66016bbf9c6b99fc26238bb586c42f924de5e55772fce9f505f9e60ec3cfcc13c3c75e7713d359506ff19f36a0d99bbdbc22ae6239ca8ca9ddfffefafd4f5838e328cd8378b7517601a73f140200472e61c1aa08c09f174a4fa44a747002c25c020969a582cbf7460664e8f49cb52311078dd6ee1d56dcf56d5820f03b58583c96b7f7132e0a9023f88f760513484808aaf9a0022356f12b64dc6cdc71d4aae46b4eb6e7595d15c2d75d0cd48a5e4594679c9ad4a0f48d22d7802040ebfddff5610f6f4ed00d08d8a4bbd384282f39a400fd749f50d07bdef9e916c7ae8fb8e725765d125dc480a77d2c82e7acd19b409df31f98ef637ba32359c038a2578d691a64afbce3c54f3062a0fcde3a474a8949d4ea4b4dc83f3086e0db62f0f7443d8b67b4c33c346938ed37ce18031b1dc2a66cf22ddff328f7985f0887d48c54853a99395a98588cbdff1c18370c145dea845a6acd58d8113c219a1932eb9786ae7dd58b74cdf0462261de4cd9501aa8013578f52db1bcf56e9d65888a1e7e5714b02833634d19a4d90d7448f54a0304116cce3dcb5d7295d31590200c5d56861 +Out = 5ba4a2c81a7ab4b9793e01a4c86cde41 diff -Nru botan3-3.7.1+dfsg/src/tests/data/mac/poly1305.vec botan3-3.12.0+dfsg/src/tests/data/mac/poly1305.vec --- botan3-3.7.1+dfsg/src/tests/data/mac/poly1305.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/mac/poly1305.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,42 @@ +#test cpuid avx512 avx2 + [Poly1305] +# RFC 7539 section A.3 + +Key = 0000000000000000000000000000000000000000000000000000000000000000 +In = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +Out = 00000000000000000000000000000000 + +Key = 0200000000000000000000000000000000000000000000000000000000000000 +In = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +Out = 03000000000000000000000000000000 + +Key = 02000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +In = 02000000000000000000000000000000 +Out = 03000000000000000000000000000000 + +Key = 0100000000000000000000000000000000000000000000000000000000000000 +In = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11000000000000000000000000000000 +Out = 05000000000000000000000000000000 + +Key = 0100000000000000000000000000000000000000000000000000000000000000 +In = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFEFEFEFEFEFEFEFEFEFEFEFEFEFEFE01010101010101010101010101010101 +Out = 00000000000000000000000000000000 + +Key = 0200000000000000000000000000000000000000000000000000000000000000 +In = FDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +Out = FAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + +Key = 0100000000000000040000000000000000000000000000000000000000000000 +In = E33594D7505E43B900000000000000003394D7505E4379CD01000000000000000000000000000000000000000000000001000000000000000000000000000000 +Out = 14000000000000005500000000000000 + +Key = 0100000000000000040000000000000000000000000000000000000000000000 +In = E33594D7505E43B900000000000000003394D7505E4379CD010000000000000000000000000000000000000000000000 +Out = 13000000000000000000000000000000 + # self test included in poly1305-donna Key = DDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFC In = 797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1 diff -Nru botan3-3.7.1+dfsg/src/tests/data/modes/xts.vec botan3-3.12.0+dfsg/src/tests/data/modes/xts.vec --- botan3-3.7.1+dfsg/src/tests/data/modes/xts.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/modes/xts.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1151,6 +1151,13 @@ In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E Out = 581EA1FEE5516AD432DDEBE75FD27C6FC30CA8F2ED57307EDC87E544867AC8 + +Key = FFFEFDFCFBFAF9F8F7F6F5F4F3F2F1F0EFEEEDECEBEAE9E8E7E6E5E4E3E2E1E0BFBEBDBCBBBAB9B8B7B6B5B4B3B2B1B0AFAEADACABAAA9A8A7A6A5A4A3A2A1A0 +Nonce = 9A785634120000000000000000000000 +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E +Out = C30CA8F2ED57307EDC87E544867AC888F6E6E2339A1F5719BFB7D5A0326D7B4A6D6F63203D985082A0C2EEFA47BE1B91B6C3C3BD0A11FA4A33DFF2101BE712D2A9DCAC38E8DF4F14F233EFF4901DA84E12DFA5D20B8EAB0842DFB285246168E611A918741227EF8A50C939B079887BD12AD89692A75D8373EA192AF3747A3A53BB1FD1E7058A6FEF84DBAA70D7A42E03FC93BC29F759D70258F8F77CCF43E769E1B9A62080A24A66C70EAA96C194CB3DD65BA98A91FD3BADF6516CCB3CBDDCED369D30518D8B4687BD20B97BF61F818F85C7A2642BC3F4E26CFD8F6AA7D8E93E8510B76A861E5F60444720A8EAF86938CB1F548E0D621CEE32F9449808025A8E3511A3BA0A578422D5B4D169C9F7C21C81A35D374E1AE36ECF7456CEC47A58DC6BCCDC7BA828EBCB28F6C04818DCB06AA068217B1E84CF69BD59C098395A6788501683C8354EBB5A4FAFC3E534A11D14ACF19341B3F0FC0E3FABE1DE02394434B3E3D650C93B2CA01296B772DA5D0C7958D5F6A7C5AC106FAEA916938E93FEE2CDA2D29B2E2C99F1B43E283DAD6F3F17438730D07882B4A979AD9624365CE2CA44F093A2862F355E2E0EC0B34830790DFBAEA21EACA459F0358AA2F5D8D8E935DD024F0F2186D2BB51585E59CF73990085EB409E7EC8FAB07960C186B336CEEC88B7CAD3F695C5A40CE9C8D8B68CE27CFC2EFAD0566E0EB36A16D35CF67AC203305FAE487BE7DC0FF3DD6E5CA182B17C08EBC7A0C06FCE407F7756D93286B48FEAD6B787311627281937176F4423BB03CBFA99613ABEA9EF8494A08BA5C7CB73287314A342FDC561B6136F9B03F56CD116624659B44D4E611F47227F9811EDD6F2EBF88ADD5074A3143E9AA620480C7DDB12DFF9D60BFBAF57AD3B95771EFD28E06FA0902CF5B811561A1CF3304BF5C2CA252910454A28355BEAF73D4300898F7A7E74499A651B88F40986E977AD18595BA85712BA083E1F5AAE1D8797F395AFFC5A22457C01EDC08A8E1232D0E420FBBD45AAF180893233CC4B4147282B4DA2CD13694B2FFFBE2B0DF792EE613CCB72853337A50F99BB8FD3EC0E252551872A34EA624D073CDDA8A6FC2416D9746E11E10F40C27EA319671B328A9C686A86FD4056CC8BA623A0EE880CA87F312090419D062222ED7AD0A9F0683B696E83B4B66470D3BB6DFB9E7CFF5454AFD439C3F3CE6D9F695376D6215641F06A3963D2D9F6A2958EA6CA3BFD515D39CD3F00D9D6237B7E9D54179EA744217E3F58EC93ED5BCF412A3C401BD3FBB7F50072CACE1682FE3474FDBF24B9E930C72467CE7D949DC5F79AA96D7FC04DFB96467F581F4E4EC583279235A3FBC43D50CDC4CC2A275F9203919F82EC396A02CAF353BF19D62FECB6EED19FF587690D4D031C1A6E21092F790FC10C0E48F435F3E3F796870007FF3BC706B644CBE27340AB4BF1300B58A170E0D130144EDC5A64AAC3D18C7DCC81F4B959A2986A5D0DD7F90D84E2D06CD83659C8806CEF81A02D49592AD002367F2E81A63AE748C64774875F9E76F7662AE1A8EA78F5C77A1ABED6B882E78A5694AC770FA51AB98DCF9774EB05F354F2EE01E6ED41D55894C22267C427547F3AF0EF4F3D73B46781BA1160ADC84DB18D352ED1019B903638B935E8A5FFB5AA265E593241EE6610E3F786D7A8915AC4C3D0E815F2EA689BE81E1F2A489F9ED96203F7C6F06324B349CF496AB67384C7137DD8DB7F95E7BD9C4881C3A0BB3E495756770582259000F73B2BC6240163665D55F99BBECEED57EFA73A3D370C09DDBD247213A5E036CF49C18828CE150E28CAA471C75B02C9EC9724ECE0FE137C8B786F7791B3AE9E1A22F1133D49410AE6CD2322BEBBB5524C983319FA2CA0B12F9391A0288EFDDFDA30ADB657900DFDE11DBA5D0DDD625E5A27994B365A87BA170A4BC70BB0D68672CBBB06881B511F1261B12CE1442C16C5BD5FE953C96C77AF43AB646695E35942F432DC0B56B35BDF8BCF2693B6896C401B15B84A89A647AD54B5C7094EF2E9638E14423CD9E908725E58AC0C710A186F9E38A0B1C71AEBEA67 + + [Twofish/XTS] Key = 879AB1F37890ACE3AA1FF21EB403C2029E225E4B82CC00CD6377EBFDF6A313ED6152830450ABA4E5EED0055968E66C81BB6DF06BFA92910550819F0BCD3D484B Nonce = A5F270CDCB5D92FE5F19805AA93B7D13 diff -Nru botan3-3.7.1+dfsg/src/tests/data/pubkey/ec_h2s.vec botan3-3.12.0+dfsg/src/tests/data/pubkey/ec_h2s.vec --- botan3-3.7.1+dfsg/src/tests/data/pubkey/ec_h2s.vec 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/pubkey/ec_h2s.vec 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,98 @@ +[secp224r1] + +Hash = SHA-224 +Domain = dst + +Input = msg +Output = 8D685CC9277CD703A51EEF7EA594566D367E97671AFD2E8AEA5B0FC6 + +Input = abcdef +Output = 86D08A2B05EE06280320F0EDF298463876D918B7C30B81E087CA1B3A + +[secp256r1] + +Hash = SHA-256 +Domain = dst + +Input = msg +Output = CA7A61BC7BFB88A10A18BE9D08DC7B1326FEDFB1542F1739AF18EB90B760D7C1 + +Input = abcdef +Output = 77ADD5C57ACBD27A3432D60C58175D6F8FA8273861A3CC4C1AD946738F7F9264 + +[secp384r1] + +Hash = SHA-384 +Domain = dst + +Input = msg +Output = edbb973f32b89b169da260b2887fbd7886e5c403fbdf3e8cb2e9264fdbf96e5cdacdf5051e335b6dad8d32eabb533b84 + +Input = abcdef +Output = 49f43f7ec7f40069a4a960332d358f85568533ed752532f9b6b874e68c865757e89c301cbf27c9dc161f50822db6a3d4 + +[secp521r1] + +Hash = SHA-512 +Domain = dst + +Input = msg +Output = 0151f2a39c59559009011efc17dbe3748bee5f3df2c1d7ce6f721da6cc5418debeb84cab8636eb88434890cf1d6d073d740d1bc0cc84d226b81a50bdf0e036afd714 + +Input = abcdef +Output = 01dbdef5084ee3421558c843f0e49bee7b91130aa593d77c2e2c3b8b45751ef945a7cb0397834f7bea067974851c130f190b4cfaba84dba3c6e302344d3a2f27ac51 + +[secp256k1] + +Hash = SHA-256 +Domain = dst + +Input = msg +Output = 582F8E0D434FBF70C7ACD72C62C13170F9F2190597C1547671A90C551ABD572F + +Input = abcdef +Output = BA941C7F0F4991412D4B7D223246E52F790E8A0194812599A35173FC2D9BF480 + +[brainpool256r1] + +Hash = SHA-256 +Domain = dst + +Input = msg +Output = 8D4D68024FB8A8A3D8DE4B650998B1B7A1440949E70D8E6682D3456330E3B233 + +Input = abcdef +Output = 4D49BE526EDFE2DB9D78DD77CF4C9F7E019BBC919593F0D9B504293D85B4408C + +[brainpool384r1] + +Hash = SHA-384 +Domain = dst + +Input = msg +Output = 34AADF54EA9B93286CF4CB2B545CE7A9684459D248869511CB6AD62B74528D1736E368B9C7A532FF78238E89F456D388 + +Input = abcdef +Output = 6DE919B77DB1E7C0F45ABF3A8E7B9D9524642752F43BC369A6784FDC8DA02D6D193A8DE36218607468D631CF52974261 + +[brainpool512r1] + +Hash = SHA-512 +Domain = dst + +Input = msg +Output = 414ECFB3196E6A91013245B867A45F928973B4E2EDFA8B0745ABE31589178D63899E53E759087DAC4A9F527795BBDFF8975186C7808211A8A1CDD1F00C476D75 + +Input = abcdef +Output = 9AB32CD59F214BFFA01E702718922AD1E3F959E2D21923FBFCD61CA5DC5A70F8BF92547FD5D1E4850463CA75E43FE3A4F9D4AC0A2C9A9927864489FD1221ADE8 + +[numsp512d1] + +Hash = SHA-512 +Domain = dst + +Input = msg +Output = 05F0538B71FAE35AEA23594FE6C2FF17CA212506E7EA642BC4A3D25071CEFBA5E8C634AEDA489C83919BE479E14BE87F4F26310F6ABF02FC61E2C4628AA64F35 + +Input = abcdef +Output = C0FD0E393F9B58142AF5E1F2C23982E8B8F441E9772275BB85665536667D92F063CA4F8726DC8E0B42ACE098DD1880D9C5E19E2C632393CCFEE2DA248AEF07B9 diff -Nru botan3-3.7.1+dfsg/src/tests/data/pubkey/ecc_explicit_curve.vec botan3-3.12.0+dfsg/src/tests/data/pubkey/ecc_explicit_curve.vec --- botan3-3.7.1+dfsg/src/tests/data/pubkey/ecc_explicit_curve.vec 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/pubkey/ecc_explicit_curve.vec 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,63 @@ + +# secp256r1 +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF30440420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B0441046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63255102010103420004CE00D94744EB2E486E86BB1A97B775BB002BBD93FF47879299A0774D5737905B11F120A5A959F5DF3E6DFA5E9EDD7D827A3D6D369548245F1AB9D37F8BC5C73E +Result = OK + +# numsp384d1 +Pubkey = 308201B53082014D06072A8648CE3D020130820140020101303C06072A8648CE3D0101023100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC330640430FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC00430FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF77BB0461040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023C9F82CB4B87B4DC71E763E0663E5DBD8034ED422F04F82673330DC58D15FFA2B4A3D0BAD5D30F865BCBBF503EA66F43023100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD61EAF1EEB5D6881BEDA9D3D4C37E27A604D81F67B0E61B902010103620004C23D4E83CD9988055C1DC816F7881C14B1C915C756FE189885F67B82CF38CE69756B30FD35FB651BEC58DD8FF66018F7B5A7B269877B43FB2D5991C48059055BC6E4012C928E6D4A0761638F6A28C7157F562BF173DB9F365276291F1D5DBA78 +Result = OK + +# brainpool256t1 +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537730440420A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E53740420662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04044104A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE022100A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7020101034200043398E38269601DBADCC70F2B3EE6D77ACC16A31EA0B4CF80A073587551A1D08729C849FF3E6D6C67435D67F9495BFB27EB172C495D61002369BBE8D0A59D4726 +Result = OK + +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537930440420A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E53740420662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04044104A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE022100A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7020101034200043DE15A95F34445551D72A71D079EC862E5B6C6DDF6735E89C1AAA4FE41E6086B29E7BBA60FE13B86189FA92BA16BEEE1B6C9910A3861F67AAAA1046FFEC0C2DB +Result = p parameter is not a prime + + +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537730440420A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E53790420662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04044104A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE022100A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7020101034200046D64F7DC28A25C8A7FCDC0ADB17209600D5F51CA4704ED65542BE4C14CAA04905F7EEC921312B97DC92397292BFF38453BA33C276DC10DE1B3F376F97C48EDF9 +Result = Invalid ECC a parameter + +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537730440420A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537404200000000000000000000000000000000000000000000000000000000000000000044104A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE022100A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A70201010342000477DDB835AC3BD17447F43916052A237AFA751C731E4E1931774BA941F9D6A4DB2665EDD6F67A722D21DBA21967AD949239C8AFDA0BB74163C8FC760E6CB4E572 +Result = Invalid ECC b parameter + +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537730440420A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E53740420A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377044104A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE022100A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A702010103420004436AC9833C202619691DE801179A32ED82C8CE6FC7B65E3BF4B1E7D93002E18E91205D73CE90B25031EBB187DCFE6616780754DEA6AE10AD4A3B90E457187A96 +Result = Invalid ECC b parameter + +Pubkey = 308201343081ED06072A8648CE3D02013081E1020101302C06072A8648CE3D0101022100A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537730440420A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E53740420662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04044104A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE022100A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A70202232803420004A4E91B6C4A84A40973ABC033BAE33F0BE201035B09547EE99854C63BA9DB50F58ABA23E502205D260E8A214C145829E3583C234060D78D3F0D6A8DBD64624C18 +Result = Invalid ECC cofactor + +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537730440420A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E53740420662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04044104A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE022100A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A702011103420004892A7459F99695DFDB613608AA529A9E385179BE8C5DD5E7A027CDC2148578D733C3CA51C255D927D83A3CA3C2F136CAACBD69EAC3EDD97B5B1C50B8047022C2 +Result = Invalid ECC cofactor + +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537730440420A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E53740420662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04044104A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE022100A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A9020101034200047E7B3DF984A44D388749CDE71DC649659488DC688C30F08DF6CB675B7908DF41A27872F84E0FD4ED90039A247DDA73B4B9F6B40A1EFF426C43B7D3742DB621E6 +Result = Invalid ECC order + +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537730440420A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E53740420662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04044104A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE022102A7ED5F6E87BAA6F0F9982A42760E35C630E5EA8ED5869BDE40783A0A5D215A9C0201010342000423FA1983DB88D73BCF3D4C04515B66A89D3E0AB6F17DF185C983128CE2BB4ADA8F98D9C4F000F8806DC2BD0523F6D623BE19C10E8E4AC2F62FD9995EACE2EAA1 +Result = Invalid ECC group order + +# secp256r1 except p + 2 +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100FFFFFFFF0000000100000000000000000000000100000000000000000000000130440420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B0441046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC6325510201010342000443C639578961AA7D33A9BF3B01C5F78D60F100212038BAD943DCBAF1B97D4768BCB0E52F1CCAD87CA131506B2973C0F6A256331A14D57CF0E1BF735A58746C9D +Result = ECC p parameter is not a prime + +# secp256r1 except order too large +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF30440420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B0441046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5022101FFFFFFFE00000001FFFFFFFFFFFFFFFFBCE6FAAEA7179E84F3B9CAC2FC632550020101034200040FD6C6B52EF3D6C94871551A59A4A79DF50B8570CF99060537D9871FB212582B190B1C90695C18467EF9B8A0E661DE51FB9858E92FB3809DB6440E87094EA0EA +Result = Invalid ECC order + +# secp256r1 except order is negative +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF30440420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B0441046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F50221FF00000000FFFFFFFF00000000000000004319055258E8617B0C46353D039CDAAF02010103420004896F4BB3988F7924C5FBB7E1D74A911E3C8238288CC4386AB1A486079FA983BE4B58ED1FB49FCBD9F4EBBFC13DB017FC7BE16370209722746B93E3A8D22CF6D4 +Result = Invalid ECC group order + +# secp256r1 except cofactor is set to 17 +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF30440420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B0441046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC6325510201110342000438378C6590DAF376BBD6E307521EF2CF38209E180EE01B64F0384AD0F2139692DDBF761F3BC66AF3B983E30400BE1B6076FD8453E8470588E86D828F1837787D +Result = Invalid ECC cofactor + +# secp256r1 except cofactor is zero +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF30440420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B0441046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63255102010003420004CE92F24CA68EFD9DBF9E85D15EF6C718C75C5FBB3E51D2A31A538982A25898B44E199C37D94F371EEF4123C61C02C680D9AB48ECE6C4C5C7F51A52300A5199C2 +Result = Invalid ECC cofactor + +# secp256r1 except invalid group generator +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF30440420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B0441046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2974FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63255102010103420004F25EDB253BF46F789C1180223B5F882C11F6481F11C2AD9EEA81E88446B5A5D0208D81AA408165899F2408AA0888D894BF6A67E958B692653C9ECFA932B89DBC +Result = Invalid ECC base point + + diff -Nru botan3-3.7.1+dfsg/src/tests/data/pubkey/ecdsa_explicit.vec botan3-3.12.0+dfsg/src/tests/data/pubkey/ecdsa_explicit.vec --- botan3-3.7.1+dfsg/src/tests/data/pubkey/ecdsa_explicit.vec 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/pubkey/ecdsa_explicit.vec 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,38 @@ +[brainpool224r1] +Key = 3082013D0201003081D406072A8648CE3D02013081C8020101302806072A8648CE3D0101021D00D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF303C041C68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43041C2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B0439040D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD021D00D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F0201010461305F020101041C42F37CB94F83641A3168DFA03A2A1F0A06A53FEDE53C64F8148BBB38A13C033A000405837BC8C74ADA34A3A315217D0C3DAF8C4B4BA1397ADF6E148572B5B31F5E5864A9F400344D281D37EF47DC0E8A36CC8900D0881986B065 + +[brainpool256r1] +Key = 308201610201003081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377304404207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9042026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B60441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997022100A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7020101046D306B02010104201436B81E1B789AB024116EC27A673EA298230A0776BB794DAD4F1E6F7932769BA1440342000433CBDA98F66297E7BEEF02AC9266F057F1C6E90B7C457ECE575FD622735CF776933C7CA60F94B9FA22B92F61B9F795582ABCF54A7483FAE23077CF34591EE366 + +[brainpool320r1] +Key = 308201AD0201003082011D06072A8648CE3D020130820110020101303406072A8648CE3D0101022900D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E27305404283EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9F492F375A97D860EB40428520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD884539816F5EB4AC8FB1F1A604510443BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599C710AF8D0D39E2061114FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6AC7D35245D1692E8EE1022900D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D482EC7EE8658E98691555B44C593110201010481863081830201010428165208D1EC0E25B2C582ECCE75742E3A3532E184B8B4302929F9042BE2E532646643A95D5204E414A154035200045BB93613755CC04C8FA183177970C048763A0B97184A2BFAEAD0842B9CBE84598E540AE6CEB446B0A0CC84EACDA6D0944A1D69D847F4BC1B13E6C149C6F7CF45F1446BADE549F8D7B2B0B45E2721F2C6 + +[brainpool384r1] +Key = 308201F50201003082014D06072A8648CE3D020130820140020101303C06072A8648CE3D01010231008CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53306404307BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503AD4EB04A8C7DD22CE2826043004A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DBC9943AB78696FA504C110461041D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C53150231008CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E904656502010104819E30819B0201010430622AC98AE6F29E8ACBC7B77B0C3185D8AFE095D6307059F48FF0D6A005DB684EBC88BD4AF0F1B357DD09BE50159CAC7AA16403620004093D8BAEE6A1692CC4D706DD1AA5C5B741209F73E635DB9759AF088750E8996D3190C1E0D3BA42E24CF3467F2C57BA221C8101769961339ACABED1FCA81DC5040B4F131BE2B35EC4955939185A631B52DC49360E59895ED0A54DAB43D836C6E5 + +[brainpool512r1] +Key = 30820289020100308201AF06072A8648CE3D0201308201A2020101304C06072A8648CE3D0101024100AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F330818404407830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA04403DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F7230481810481AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F8227DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892024100AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA900690201010481D03081CD02010104406C835E4CC4FADE6DA8566080B4CE6CCEED318178F4F87E227978D7C210147230144FC0E5739C8004F4CBA214A7F57B5BD001CB103D4AB74618027727914B36BFA18185038182000471C6D97C30D7814524DC32A2841B5A1C33371670D4F7C959532C75F4A9658266F55744EA367AE6E63D874ECC175753D31A8966F1C2E147B6671761E248D2F756A72A8BFADC2FA8BBC75130911FF2E4C525E3CDB1C7977A1B14659EEECE742C68DC7CB465298C513B38AA563C92F32BAF4EBB6259A309C926EF9E9D04E4A0B7AB + +[frp256v1] +Key = 308201610201003081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100F1FD178C0B3AD58F10126DE8CE42435B3961ADBCABC8CA6DE8FCF353D86E9C0330440420F1FD178C0B3AD58F10126DE8CE42435B3961ADBCABC8CA6DE8FCF353D86E9C000420EE353FCA5428A9300D4ABA754A44C00FDFEC0C9AE4B1A1803075ED967B7BB73F044104B6B3D4C356C139EB31183D4749D423958C27D2DCAF98B70164C97A2DD98F5CFF6142E0F7C8B204911F9271F0F3ECEF8C2701C307E8E4C9E183115A1554062CFB022100F1FD178C0B3AD58F10126DE8CE42435B53DC67E140D2BF941FFDD459C6D655E1020101046D306B020101042063F7E7DC391F5342B3F85794798290672C614901A3B854531037306CF11D5258A14403420004C2391B7C156EA9A6FC267CE65F0B59CFE3859650765791660B2EF8F11D1C1E4DD2CE2F6A62EFA5C74CE688ECB627C57AF9A86B4410F8501FD182931811DB99BD + +[secp192r1] +Key = 308201190201003081BC06072A8648CE3D02013081B0020101302406072A8648CE3D0101021900FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF30340418FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC041864210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1043104188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF101207192B95FFC8DA78631011ED6B24CDD573F977A11E794811021900FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D228310201010455305302010104188C3688EF482B212AE0400A2EDE8240F7A8CCDBF186749B9DA134033200044E0C98ED36C4F7CA525BE86D55E3D13ECC244E49001CB7B5FB246627B7B9C4D4A479F8EED79FA897A44B629B9533C1AC + +[secp224r1] +Key = 3082013D0201003081D406072A8648CE3D02013081C8020101302806072A8648CE3D0101021D00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001303C041CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE041CB4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4043904B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34021D00FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D0201010461305F020101041CF27599DC7AD58D8B6F57706B445C7E67CC4BECA68211EAA8B65D0EB9A13C033A0004F8DA1C810BAA889799BF82519638080F3EE82078C3D8C45D794AFC940EEC277727C37BE247D2C256C5FC107C2FFBC0FFBFFC3C74068E7D01 + +[secp256k1] +Key = 308201610201003081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F3044042000000000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000000704410479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8022100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141020101046D306B020101042028C5A2F48CA3FDA92EF7264DB8A3B4132BA1F2441542B436B73C0D1F6EEA1075A144034200045A9CF7D3A1AAC6F62F943DCC1CB9D1146BC365F6BF59D4148D0463063B2BE1C9D4BAEE8336FD644700142CB68E7E128D8ED89F64C298B991CEECE0F8547290C7 + +[secp256r1] +Key = 308201610201003081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF30440420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B0441046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551020101046D306B02010104208F4DEA35631071546F0DA81D3023828F7CD3632CE4BB35CA9F150728AD8F8C04A1440342000415383AABD6640EA981B8E34FF460B0EACF82726CCA54E3AC5500A84021DE25D723B079C23FF589320E142AEFA47037BBD287E3A1412463F4D1398FB5BEABE469 + +[secp384r1] +Key = 308201F50201003082014D06072A8648CE3D020130820140020101303C06072A8648CE3D0101023100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF30640430FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC0430B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF046104AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB73617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F023100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC5297302010104819E30819B02010104309F9447BC5EED548FE1AA89D58617C661BE24DCA8E2AEA50DB0252C091FD82D9661C3C079295F699E1043CCE70106FF2AA164036200042A1493D1EF7CE46F65D73B5E2E02708E470AFDE36565B83285F72808538E559BB9F3BFCA8F199E21AF279408C15ADAD1D057BDDF842E26A2ABDA81344E27A037911730F02A8189E3A24CD81DADAE344F934EE2F268AE6EDF1008E396D3596E3F + +[secp521r1] +Key = 30820299020100308201B906072A8648CE3D0201308201AC020101304D06072A8648CE3D0101024201FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF308188044201FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC04420051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F000481850400C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650024201FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E913864090201010481D63081D3020101044201ED44E47ED512012EAF470FB227F9A4BD0863B076891A4A74606F45FD5630D0982998838852AB442ED66994D6289EC30B45331E4D874900BC63ACEEE44E7BA78000A1818903818600040054435FE4D76AB0DAFCFF209EFCE25F75B6499EF400623C8E555D1E3F37603D274633243711F7FCD7BE6987C1DFB32CDFE2384CE1D7B9857BCAFAC24D078F1A56CB012C1BF7FC4B7A00F53FAB19867AB59377F24C3FD60127A0F0436EDFFF49525703BCFEF8B6EF3F852886EF8ECFAC2ACB4AC5A9447D0DBCC0AD7A93B910C0DBF597D9 + +[sm2p256v1] +Key = 308201610201003081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF30440420FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC042028E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E9304410432C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0022100FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123020101046D306B0201010420DADF81FCB018DE863116FB9B7D5585855AA9E96C1DB3CBACE802919589C09C1FA1440342000436CA23BBEC51A911EFE26350680BB356C9B2FD9DFC5848A1EDC1DEE1CDC104ADD5FAD7AA76512A869638F56E0D562D93766572EFDF121FB0D372019BA7573818 diff -Nru botan3-3.7.1+dfsg/src/tests/data/pubkey/ecdsa_keygen.vec botan3-3.12.0+dfsg/src/tests/data/pubkey/ecdsa_keygen.vec --- botan3-3.7.1+dfsg/src/tests/data/pubkey/ecdsa_keygen.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/pubkey/ecdsa_keygen.vec 2026-05-07 01:38:28.000000000 +0000 @@ -8,3 +8,9 @@ Rng = Fixed RngSeed = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBA Key = 3081EE020100301006072A8648CE3D020106052B810400230481D63081D3020101044201BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAA18189038186000400E58D192E4524748FCE49AD980063E8A66206C361200A39A0DD14F0E1110E46D8329FF588E3584D6832061754021C2BD4283534EE03978521F5C4914E84A17BB5E10083F4E1794511A5112AE1478A483BE4D53D991F80C0EF8D68E1BFF137F87CE57AF6EC64B21977D27C5C3C55F23FF7E7D753DB14C5DA7BA82468E213BF68F576FAF3 + +KeyParams = secp521r1 +Rng = HMAC_DRBG +RngParams = SHA-256 +RngSeed = FF71E81F219CB9B55D03849EECDD41425B1949E7E1AA9AB52A0B745ABFEE796D +Key = 3081EE020100301006072A8648CE3D020106052B810400230481D63081D30201010442009BAA89F1179CB19F97F99D229E18665D942E67664A4289527E3311F5FAACECE39781A0E284BCEEC771CB511940265A00FBE4FD47DACF67AD310D0D7BDD43C31814A1818903818600040088E03A3E4F96285D1DFAC05C28565824A83132B0A1CA8ED550BB35C0335C13B3DD11E3CE7B1D07FEDA0BE44C488A219E34409375651F616038014BEE2C90BAEC4B01B53C7FD11F0A5976BE78C628911E662F76D5EE775ABD4FBDA55F3702A596D3E4762371EB6B61E0C9B264E16A77CA66F7F4D1782F36369D35D0A352AA3CAB18DE00 diff -Nru botan3-3.7.1+dfsg/src/tests/data/pubkey/ecdsa_verify.vec botan3-3.12.0+dfsg/src/tests/data/pubkey/ecdsa_verify.vec --- botan3-3.7.1+dfsg/src/tests/data/pubkey/ecdsa_verify.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/pubkey/ecdsa_verify.vec 2026-05-07 01:38:28.000000000 +0000 @@ -99,3 +99,120 @@ Py = 0x6f7f1dedfaf8ebaf438143d5721cd47cde7ddddc6c72f880d35b7a7cad1a Msg = E8DD36C65CA8E9B0A5D44C7BD91B6B2EF9D558DA25B4A897516CDF253145464E Signature = 662ebdcc2e8925c91fd15451ab83728063a048c203fc242bdcdc33264f060c833f36dc5799adc73b61fc007ee569d555ace73d31de4d8d4d8ec99946 + +# Reported in GH #5211 +Group = secp160k1 +Valid = 0 +Msg = 8b3ee52dc99beae3c8329e7f5380abce5d557cb0 +Px = 0xa3dffaeb7710d664c4df0f333aa26d1dbd79d4c4 +Py = 0x20d221ce0377d4d98ea09944ed9b2913688a368 +Signature = 0100000000000000000001b5d96f650a6238165653000000000000000000000000000000000000000001 + +# Reported in GH #5211 +Group = secp224k1 +Valid = 0 +Msg = b750b49f96f1f7a708560d988a306aace7c9e26a4ff42f37d106b520 +Px = 0xaf06fb109bbb988acbafd8bcbd037d51423306ba7359b4c72da876 +Py = 0x54b6424aff6f8a1f0ca719a5f85a480873cc4a96898480a07883c769 +Signature = 010000000000000000000000000001dce8d2ec6184b4f11b68d0a6671200dd4c63dbffffffffffffc95ba5968afb7b80265aa7a7efef60618484 + +# GH #5211 extended to other affected curves: +Group = secp160r1 +Valid = 0 +Msg = 36302A7635159968156610D8F00A643C97DF7C38F0 +Px = 0x2103DBEE14EB60C358DE1C98FD9738DBD82AF770 +Py = 0x8DC37901937E17D7BB95B3229E5ECA51EE718F52 +Signature = 00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000003000000000000000000000000000000000000000001 + +Group = secp160r2 +Valid = 0 +Msg = FC2E9E56ED2821DA004CE20113E60E8DE80BECCD30 +Px = 0x9783B65D6E7E97F5493CF8C2F415EFADF437ED25 +Py = 0x32656B89546B5BE4E84845FA04259CEF5730404D +Signature = 00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC76000000000000000000000000000000000000000001 + +Group = x962_p239v2 +Valid = 0 +Msg = 012F7C72516FBEF3EB8720EF27BF12A9F5F06DCA824664793557051A8613 +Px = 0x71BFA93E36B38FF848497BFFA3EEA48CD1E9A8E72775DC63FAA9C78C932B +Py = 0x15426226924DAF7963A23835ECDEB9E3C18BBFD17A859456411C051DC06A +Signature = 7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF800000000000800000000001000000000000000000000000000000000000000000000000000000000001 + +Group = frp256v1 +Valid = 0 +Msg = C53512D1756A69A51202264BAA7145EBCF4C71C9F850646FA76F2FF13790F76A +Px = 0xC228CE9CA0B372BC309FB97F9609B670F6DA74AA03EC6E8680CF701E1FC69A87 +Py = 0x568B23C31B5F546741E325F0EE0BE24455EED100B966488C7F80D34FD9CB1FE6 +Signature = F1FD178C0B3AD58F10126DE8CE42435B3961ADBCABC8CA6DE8FCF353D86E9C050000000000000000000000000000000000000000000000000000000000000001 + +# Variations on #5211 but with reduced v, verified by OpenSSL + +Group = secp224r1 +Valid = 1 +Msg = 710658814EF63EE7C5238BF0D0724D6FAB98AA125323E7E1034BFECF +Px = 0x8560C3AE7BDF0CF6CA6226E4AD86F591612793AE8EECADC563321700 +Py = 0x56FEC7B2409FFB7BE6E0E2DB4A969496A7B4C04BC0AEF0E567370E96 +Signature = 0000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000001 + +Group = secp256r1 +Valid = 1 +Msg = 18F93726DA7AA39B97AE40A3319EEDD3559655B4AC3BD0CEDC03A32A55BF73FF +Px = 0x77C1542E735A55CA290D62A537BE4A0204FAD7AED68C894D763815E4AE28444F +Py = 0x0E2E2EDCD6BC0A2E5BF00CFF764447480D977FED7CE1F388D2A795D3BB2CBC5A +Signature = 00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000001 + +Group = secp256k1 +Valid = 1 +Msg = E30D1A48047EE3078512BA210DA2FBA9F1A5BBEC397C2C1BDFD23A51C6D25D23 +Px = 0x80C0D9F2283FDCF570D68D2EF95144EDA1CA93D508515A4B4964D62E1715D97F +Py = 0x4E900DDAAD3F9BDA7CD63738445D8F1C7E4903C6ACDC8BD30D6D53F27F333A2B +Signature = 00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001 + +Group = secp384r1 +Valid = 1 +Msg = 63AC1806DEA50FF8CC3AD52C437D6135964812CEA1E7B7DA1EC77C0B83CB7B19BF02881D7F25C7797857E7ED31F6D8C8 +Px = 0xE66AB27E6724158F6D4598910A264401E95C334EDCF2E9F2911D4CDECF383EEC3817D65427ED480FA6D57FB32BC1E8AF +Py = 0xD0438CAD348424BF81B40C0C00A0E9C39202AF9F97A073374EB34D4AE4A8E16FBFE5851BC87B66A9A47D949F3697FB54 +Signature = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 + +Group = secp521r1 +Valid = 1 +Msg = 211D5FD75572ABC64254DE59A69A0AE5E6B699084DBC202DC03EE2F586089C9AF99D77867BB7A2C5C8DF38BCDB55F3487D53FED666CF591D86F8892DDA77C886140A +Px = 0x015C25481B2A86C95B4D3450298C8262E0F4A561DD528A6C002B53D299FD14B6151157D8925D78580CEEF0AA73D38E448BA1C9213D28DF1A6B6F2A9B3DECDA66BE70 +Py = 0x00B175B52DBE6DB982D25CD22B6DC5581865EE9366C9FE99622C52A38574EDA0905F4B89197F6C71CC44EC393DC7A64B51420386DD9AC020831CCAAE72EFAEABF4BD +Signature = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 + +Group = secp224r1 +Valid = 1 +Msg = 5768617420746865206675636B2064696420796F75206A7573742066 +Px = 0x0776E28D7563F7C3099839AF5B0DB91C6934F856081106EE9EC7B06C +Py = 0x6BF2555E59F979BB8D8837343A692BA1D6664F4D98E43E515ECC9742 +Signature = 75636B696E67207361792061626F7574206D652C20796F75206C6974746C652062697463683F2049276C6C206861766520796F75206B6E6F + +Group = secp256r1 +Valid = 1 +Msg = 5768617420746865206675636B2064696420796F75206A757374206675636B69 +Px = 0xE0686CDD3A3A1BA7AAD90C24B7D57354D76D020A6BEB7449E414B36502FC5CFF +Py = 0x265D92F9924F614960766177822F374C6524941B466E533B972A2AC0ADCB2B4C +Signature = 6E67207361792061626F7574206D652C20796F75206C6974746C652062697463683F2049276C6C206861766520796F75206B6E6F772049206772616475617465 + +Group = secp256k1 +Valid = 1 +Msg = 5768617420746865206675636B2064696420796F75206A757374206675636B69 +Px = 0xDAA0743ED537F4030591373C48712A25B2EBB3A3FA91A12B16D07372FD56BD2B +Py = 0xF4523A7C4F4C22D587A9F8C36538A66D25993D256821B4B04E0D121AA0D0D48E +Signature = 6E67207361792061626F7574206D652C20796F75206C6974746C652062697463683F2049276C6C206861766520796F75206B6E6F772049206772616475617465 + +Group = secp384r1 +Valid = 1 +Msg = 5768617420746865206675636B2064696420796F75206A757374206675636B696E67207361792061626F7574206D652C +Px = 0x0AB45150D149F9B142573DC88586F6675176147ADCA9686B5C1481B18F880EC214F9D7C02178B32BE84054C4FC845D01 +Py = 0xA9555351A851F181EEEE55500E4639591D87360D619CE68AABF9331110CE7AD2F1A305D5B34152C2118D68B9814560D1 +Signature = 20796F75206C6974746C652062697463683F2049276C6C206861766520796F75206B6E6F7720492067726164756174666420746F70206F66206D7920636C61737320696E20746865204E617679205365616C732C20616E642049277665206265 + +Group = secp521r1 +Valid = 1 +Msg = 5768617420746865206675636B2064696420796F75206A757374206675636B696E67207361792061626F7574206D652C20796F75206C6974746C652062697463683F +Px = 0x00AC793D0BD1E4FD0C0F695DC21E825458DB116EC94551CD21192F406AA52DB196861D09DD2F1ED2ADFBA150A4F890C639B764C79B34FD5C3C6C6B8A7EAFAC86A871 +Py = 0x0089410A961F70A13F7A9AE4DFFC2F4DE2E6C59AB00D78296653C223B3E51DA6BB7F81F6A30395F577AD97A15BA2DE3DE6162BC8D65F4FB893DDE1BC0979E617FE58 +Signature = 0049276C6C206861766520796F75206B6E6F7720492067726164756174656420746F70206F66206D7920636C61737320696E20746865204E617679205365616C732D00616E642049277665206265656E20696E766F6C76656420696E206E756D65726F757320736563726574207261696473206F6E20416C2D5175616564612C20616E64 diff -Nru botan3-3.7.1+dfsg/src/tests/data/pubkey/ecies.vec botan3-3.12.0+dfsg/src/tests/data/pubkey/ecies.vec --- botan3-3.7.1+dfsg/src/tests/data/pubkey/ecies.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/pubkey/ecies.vec 2026-05-07 01:38:28.000000000 +0000 @@ -159,7 +159,7 @@ Ciphertext = 0201519EAA0489FF9D51E98E4C22349463E2001CD06F8CE47D81D4007A79ACF98E92C814686477CEA666EFC277DC84E15FC95E38AFF8E16D478A44CD5C5F1517F8B1F3B4D4D7BF8B86834928A86567A7C5AF80538D7F5EFF49F3A14947503EB8ACCC90D916CDC07C0AC00A9D558857F2C2EC3DC5142713F4A4AE0334987BCC3DCE9ABB4403A674F3821124D29D92F184568BA31FC60F1C0C58B4CBBCD6BD588462FC50 # use secp112r2 - curve with cofactor != 1 -Curve = -----BEGIN EC PARAMETERS-----MHMCAQEwGgYHKoZIzj0BAQIPANt8Kr9i415mgHa+rSCLMCAEDmEnwkwF84oKqvZcDvAsBA5R3vGBXbXtdPzDTIXXCQQdBEujCrXokrThZJ3QkoZDrc1G9YguN0fe826VbpcCDjbfCq/YuNdZfKEFINBLAgEE-----END EC PARAMETERS----- +Curve = secp112r2 PrivateKey = 656008468895526658474428975817604 OtherPrivateKey = 563449446384594847151017584539074 Kdf = KDF2(SHA-1) @@ -178,7 +178,7 @@ Ciphertext = 048c40bda0986dadeb651178b4a8e64b7735fb02f43e621151849ea761a0f79fbb500b76e4eb9cd65281b804406536d04059b60689ed286490afcbf8f7f32dfefff8d37d29d335cb11aef3cc5d65f87571e3c8799974038f9d377a2683 # use secp112r2 - curve with cofactor != 1 -Curve = -----BEGIN EC PARAMETERS-----MHMCAQEwGgYHKoZIzj0BAQIPANt8Kr9i415mgHa+rSCLMCAEDmEnwkwF84oKqvZcDvAsBA5R3vGBXbXtdPzDTIXXCQQdBEujCrXokrThZJ3QkoZDrc1G9YguN0fe826VbpcCDjbfCq/YuNdZfKEFINBLAgEE-----END EC PARAMETERS----- +Curve = secp112r2 PrivateKey = 656008468895526658474428975817604 OtherPrivateKey = 563449446384594847151017584539074 Kdf = KDF2(SHA-1) @@ -196,7 +196,7 @@ Plaintext = 000102030405060708090A0B0C0D0E0F Ciphertext = 048c40bda0986dadeb651178b4a8e64b7735fb02f43e621151849ea761230f2bddf1ffa3262673bcb3f468dd8b92c31a32e23935cfd27dfcc123928a18bbc82bdcada733be6d42119d3fb968ac4b77fff9a47d336fa025bfad3ee54286 -Curve = -----BEGIN EC PARAMETERS-----MHMCAQEwGgYHKoZIzj0BAQIPANt8Kr9i415mgHa+rSCLMCAEDmEnwkwF84oKqvZcDvAsBA5R3vGBXbXtdPzDTIXXCQQdBEujCrXokrThZJ3QkoZDrc1G9YguN0fe826VbpcCDjbfCq/YuNdZfKEFINBLAgEE-----END EC PARAMETERS----- +Curve = secp112r2 PrivateKey = 656008468895526658474428975817604 OtherPrivateKey = 563449446384594847151017584539074 Kdf = KDF1-18033(SHA-1) @@ -214,7 +214,7 @@ Plaintext = 000102030405060708090A0B0C0D0E0F Ciphertext = 048C40BDA0986DADEB651178B4A8E64B7735FB02F43E621151849EA76156865605D031B2DE966E35FE7A8201139C30B19DF8E3CE86657032AE1A1397FD00B223AFC1123550A8ABB3983A9F62C5CC1D9A34B8BD938921D67AE08E07211E -Curve = -----BEGIN EC PARAMETERS-----MHMCAQEwGgYHKoZIzj0BAQIPANt8Kr9i415mgHa+rSCLMCAEDmEnwkwF84oKqvZcDvAsBA5R3vGBXbXtdPzDTIXXCQQdBEujCrXokrThZJ3QkoZDrc1G9YguN0fe826VbpcCDjbfCq/YuNdZfKEFINBLAgEE-----END EC PARAMETERS----- +Curve = secp112r2 PrivateKey = 656008468895526658474428975817604 OtherPrivateKey = 563449446384594847151017584539074 Kdf = KDF2(SHA-512) diff -Nru botan3-3.7.1+dfsg/src/tests/data/pubkey/eckcdsa.vec botan3-3.12.0+dfsg/src/tests/data/pubkey/eckcdsa.vec --- botan3-3.7.1+dfsg/src/tests/data/pubkey/eckcdsa.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/pubkey/eckcdsa.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,6 +1,6 @@ # Taken from Korean TTA Standard TTAK.KO-12.0015/R2 -# "Digital Signature Mechanism with Appendix - Part 3: Korean Certificate-based Digitial Signature Algorithm using Elliptic Curves (EC-KCDSA)" +# "Digital Signature Mechanism with Appendix - Part 3: Korean Certificate-based Digital Signature Algorithm using Elliptic Curves (EC-KCDSA)" # http://www.tta.or.kr/include/Download.jsp?filename=stnfile/TTAK.KO-12.0015_R2.pdf # Same as ISO/IEC 14888-3:2018 - F.7.1 Example 1: Field Fp, 224-bit Prime p, SHA-224 diff -Nru botan3-3.7.1+dfsg/src/tests/data/pubkey/frodokem_kat.vec botan3-3.12.0+dfsg/src/tests/data/pubkey/frodokem_kat.vec --- botan3-3.7.1+dfsg/src/tests/data/pubkey/frodokem_kat.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/pubkey/frodokem_kat.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,4 @@ -# This file was auto-generated from the reference implemention's KATs +# This file was auto-generated from the reference implementation's KATs # See src/scripts/dev_tools/gen_frodo_kat.py [eFrodoKEM-640-AES] diff -Nru botan3-3.7.1+dfsg/src/tests/data/pubkey/hss_lms_sig.vec botan3-3.12.0+dfsg/src/tests/data/pubkey/hss_lms_sig.vec --- botan3-3.7.1+dfsg/src/tests/data/pubkey/hss_lms_sig.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/pubkey/hss_lms_sig.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,12 +1,9 @@ # Test cases created using the reference implementation https://github.com/cisco/hash-sigs # with seed derivation logic Secret Method 2. - -# HSS with 2 levels: -# Root Level: LMS_SHA256_N32_H10 with LMOTS_SHA256_N32_W4 -# 2. Level: LMS_SHA256_N32_H5 with LMOTS_SHA256_N32_W8 -PrivateKey = 0000000200000000000000830000000600000003000000050000000467c6697351ff4aec29cdbaabf2fbe3467cc254f81be8e78d765a2e63339fc99a66320db73158a35a255d051758e95ed4 -Msg = deadbeef -Signature = 0000000100000004000000033b4c17fbfa9a6ae2c471d7f26992849c4d956a81a65ee52e757f1df90b8c031d56a47a2d953108c6958b9e26da66abf4f52b8856bc93e3305f61512a04d2c7a77239318432f1bb858fd2b8af19f2ffa1713eabe3d5c7bd4eec1ae075041e87a8dec5ec762772dcda6e1a19be5ae4745ddcd8f954d8570c866a62a0ef1d5fc449c93b1dd3151fc75596b1be6b98ead724d45f9879525483101e11105aa8d94d2c42d24921f148095cd71be805bb40f319b1632a2b0a2d2f5b3cb5911531eddc36540b622d9bbf232cba12fd1508b59e47cce1d6756ff5c0a76f8f11aa09f0e156514ddde065481c527b3cd20b644c450fca3fadf589ed57c1a893cc134eea4b4c2a0668b741b707040ddc495ce60046b76547d0a2f48952126701060acf931c8bfd4edb60f0e90af071e0aa5bd54a26cd10964f4b978600e586b012546c17e2d3955219229c279c2ba05bd61be9f117dbfd512ba9ede726c0b2216fb5d972e440a75e5e9a3f72e42e986ff4ce03c5182bef6bd8b7aac80e91d9e72a1c9ef857de713cce2916f7e723954e177f93425df98a79e461d1c5c890138fd05f5721c63663877dc5cf72cb3ac1209a9282e843d1097e58bd577b5b611606f054f8c3238e315c40aef191de389c7978ce56d21f01336f346ddc6203076dfd11f2b9abe06823bd4b9331123a7774a1a8b29a89f9122891b9f3ce13fcfddf5758c77bff267f7f22215831061e5209c46149e541f108960c918fc8a9bdff2d78987cf782d5dc4a8de064e921d433c3a8f4362d340e19156564072d3ac925fe884c67434b7cc3760c203cfb2f34c21e881b4ddb26214b711ad96bfcaadab534799af504762bcfd367e34efe693a7da6e828e68979585aeab758378bd779ca8e379dcd2e2dc38c0d7f7211f58bad8bb0a72461af81712c35c9825f8b23e26625b7aa46ea71847ab67c5770e732cfc9a3948ec0772f67382bb2b9a863166693329a50f80c2a87b8547b6c11a5de36f9b10f28ed67813770fd1ae160c4b33e3a96b0dbec09a24f5a1852383b1fb066b778acb0a86302297fe67f99a488d984c2875ffa2bae331d961bcb726b818662402c3e5c9ce3ce8b0e7ae87e221e18ae35072d3dbf313f58b5d399c7e783d5be4234234391b5b9676ad6e7417aab4716ce1a7aa24db9254f325c6e4632e62ee8bbabd5885ebd19f703c6aa0199469519ec68fbfca30c7549911afa846735dde5c69865475f13ddc09b240a6d3bc50ea0940e89778e369cece235de4b1ed23433d305d7da612eec60b066e519724ad75ec00b2da20551b08723d9779015f2df6b30d1529ef52eb40383f37190c72be0c9d045b3d382eb5e56ddd3eb2028a074cb4a4b6105dfcd14ab226cc1240c681d3805e6f54260ab8aee8b8acafb483d37f7c87a4674b8043c40dad09baf64f2aacf915bea02fe01b6581b1a4d1e37d76ae7e4014cce79f7c817007f1d4dbb30057450efe38e2a843f44cc6a99e190e3199d8a1f65e120bddb0ec1df8475339b9d0fe3653a927bd915f6447ac495a0f76fc9336a77ba34e646d20fb0b63535683da47bd53d8bc3c846e4c27e199e7be6a6c2821c1a0311fa55699e88c100a0e1520cb775bf4d998915ba93f654bac743640e8c01ea15389840c49cec190420fa99080aac1d82cd917c15f454a1d3869387ccf169a5b015e3c918edb251130d773354485cda5666d8500907dc315c93467044413829526b89f567ac882fb0b868fa29687950b3c1a8a1eaafeb164df0ae598fbb204540e4cfe2ad121e28c305e8d8eb50822a345b24efa536ad999fb824a11a1574c3c80676120eab3f68552ba940df7bab21534ffd17491d7d804103671aee935c925d57c14519cfe3afa4be8870d5abaa0c26da9586136ddcbcc3818d8e9f611f6c8fde36fbe81b0b21095a9906abece667c7c15f9efbbdd30329b963d2a32b4d3230f56b762cf86bbc84c1fd50e4a65b2eb4c3ecd273b4815dc6bfb10972fa1b3bc13c7c1902448cd4873895fca5c452d2fbffd0313bb280bef50583a322f1266e86bc26debcb53ca98935a170c0f21a729f4e3996f2318b9d13d6b1099689c8c520b7cde5b1938f54172b21e8cc2703433c454a26b26f6ea33744e3991e99377b2eb3e67c7a3e83f73206293685e16b406b5117111c56113a1f703cc3bd7309e23057278354074cb127c12a918f1fa22444c972b66d04e0065740a64b9c0e190653e4845ab0ce568a18edd204e4b673093f33eb0b45be36d2240312b2e9f5dee6255a689e86167c8d059fd06a2e15e2d437b542e06a915c0858db75034a61de2ae85689c34f6a4398ea1c8d64e87ddfaafca41454548afe6c50b0645a0317b83e14f6bca49cf9fa9a0152887cdd7311faf728c2ce0f9bfd48d35ba75bdacbd77180ad0f7d0ba9e8422fa8349296a3dc6dec74cb76515211fb31d1ed51279ea004f7c529151f576da91047080f6c86dfa9d75e22b71a66331610c75af8bcb865f0d17b89fb3e2ff9c262423d00843924547b9f8d9ee7bf7cc49519028785a8b912813fb4a2524a23d211d75e2421d09f7d77d1a8cb00d637225a3509571ae50e7496de26d04ecebfc07c8c2add9d34f73d6ac593e5fe03707aadcef5f362e422ec285bebad8abec92a77b56df920a68aff30433bb2a2beca8662eec7bb053def251d42f59e97fabe4ee304f952b073fc64b04661d401f19d61c61a1594716d986bdd1749fe6244701e33916561e9d7be473c866bb4ee616d985f10d07405f82e0bdbf91499b07ab3866a21a52b8d89522acb9b410c0784ce4033acbfceed097cc7c11e0216934eef324c272374f3a947a5ba501e8db1416d54881bb0efaa2794f0d0e2592a2ed0526fb56e286e2beaf2f22a32028ba75c2a4e3690d0b7ce6e4fbeb944756b9e68db4b8cc873313484063dcbcc3cb842507e09ae5cbacb5761a9eaff5fdf8a271dea35192d4e4086760fd3d363d6a1bd7254e1f330cbd69589e61ce0fe9debe70f39cf1989979d82f4563a55aa8e609126d8d11a90db5ff0d39975781fd81adfe6b628b2ff1537eaeef692f6748068d6b300000006f0b054a497da7ad540d28c4cabc6fb47d0b99287073378d2ef81da4c970839f0676cfc7c443e335f06259e4342497e824b9a2630ac28124582e9fa8cd0dcf0ddf81b07478dd5ec5c84c7fb43158202814767556027f40fefa69ee48dcb865422346afe98f627c0b1e6c7ad2de6de92594a4acbfea6dcaad85c5f066f57cc010de83cba33ea6cd9975e1f7b32ea8184e69f1f4bc82c0c96f59e194932c133a1be2d24f1c945046e32b2e0480c70de41152603f8cc322b67cff334546d9083be11de638e7448d32cccfefd5bad504bfd59764c797f91c61400f3eed0b8c92c20fbfce05ae61377a499c7a2d364dfa4d3d5fc54f1070364e08364d71cbf3bfd1b6561553169277d221d4fe50867d1e0223a22169b78927a131b7ee42f55fe9e3eff55fb241e0006d38ff42fb4bc929f32378b17ad5de3dea117dda0eaff570466b8000000050000000470fa96c7207fd718fbbc14f206be924232934612284f800c38fe9ea986386a321704fbaecfa1996db792a8d14123e3440000000300000004d1324626d19a4f048634ec2b98dc95ce0ce192986cbed9a7faf605a7a9da476688c28e49e9e676ef0640709d23015073dab879ba93d64f71837b1b6628a43c80a3f06032c4318e03b18c252e16797afde4cc8db79abab009272475fb7e909a78742d467bfe3820f37ba4f0575abfff0a32970a246d54d794e5757ee82185ec993e3aafb598eb0d634fd0070467d60d4619a8fb5c49c82fc5df0d39ff40811ff6ec9c3ad0c87a95254566a1065099c0174317943d21a2dc9eb8c81d8c9363a686406c46caf8683f16b9f865f5063627bed345b7514e06abd2e28543b182a0cc36a09a1399788b04575d79b478dd86ce2870d8202bda4ca79d9afdaefe32398a2521e605f4adb6ed8612a1430a25f6ebe264ad37d4f401284d16924fff9d68630323c4338f2252f5780f44be76b9a97d5010e2abf846c17e811114a245424796c3c97b5992cbdb8014928723088a5f421ba067482b19c2f4890cdb0c7c15ec6eccbd8e133dc2a2c10e544ba855b2b07a2ccd982ce8c6ddf191c09a0705e73a9a492099cec2731623dc551a7425255d95e17eefe2ec160bd96c488e9f3c9985454241f4429b15f4193680dfbd30c7ccfd6920b0f5f7f1ae0a563b0fc93690807d5c6d784b8ddf24b1f6c9bce3692061de54e057e897c10323657c624294fdce227fee7c50ff9be5b9bd56022adc6515b7fb44964e9c07969c2c325b2083977f07342bb503448621a51f9f9e48b2e5bd9756371f900e1a1aca92331c1f0173e25a211c9a7083dc2135324420b4472919c6e9c6d2b18e60ed9fda9f9de23869cc428547d1030b0f27fcc0814d4a91967fb94d2b0c04873486e50a5875e21508c25f4679e04879484ffbf0a7c65c40937a246a4eceb959d8e0cf6cc6c29e95019c3d69558640a51ce187c6df658947fea5b358680ecc568902442fc159c1479bc43393683019ad7b8ceaf094ea165ea4fab3ebcdb57337e5e28c5f23e3bf7de2fc7fa9ac3a19a7eba424bfccf4d5c1614869c6085d3cbf1f77af36e7a6bbdf60dd8e3792974c7712f33107dc553dbf3f1cf4a7423b7cb1624aa40d01f4450e245530bf20056adf34b7ae3e8b17c059c9073931e46eece7b8b6a725c3aa84ef43dfc25bd23e441953aa40d8689cd34e9ec0ce7a9dfb37454b8d152877f45592eda49df265c4a6586e8de427fcbc01cadc240df08fa3fb3a36572ef8758185ee748ee063e08c6b47c031e75b799861e877019265515267f65497e3307c50d0680043fd36c9e13a83bd7b3e0892dbe41211ea50f795ab2408dac8b5e1eb7c23673b72e4c076faf93a8c71d3ad9f8570091d16667f1fde53eb5380804a5c5c78c6bea53acfff3fe673599351d9988acf0435a8c692e8d46de87af8d72389deadf9a0cca2312ab35684d06c0bd1cb362077fa53805c4d7823914656904094ffc6a1e155e52974b07ff2055fa4c14454f2dd46fa16fda77a11e131df27ad93f5c007d2354d344c1fc79a67eef198083480d62be9c8294d7f01763089c0580f74ad18471848d758e4b225d8972e20261ff3753e60630199bd43f6d41693bffbfd7921719dd7b60000000598a22fbbeb0bfc68202e2e9d480f6f1df0e4e6b75e3566ca6cd61efa6347d12bcb3448adc2c04c4de6528270236158998fd4569299325dbbc648a99c3f0d511c23006ebb7a865a20de81dd118b374aeee6494fa9169dead8d7b0d7c7b07416f259846e45169c5a83dfdcdfe6b145eb0626baf570eff8b8e6f995d2498ac74c331faf60b72418234316df461327e4ac14619b565ebcfd7e5a4b388930198b9d0d +# +# NOTE: Some of test vectors use the same seed and identifier. They must be +# ordered by index, as Stateful_Key_Index_Registry enforces that the index for +# any key only moves forward. # HSS with 8 levels: # Root Level: LMS_SHA256_N32_H5 with LMOTS_SHA256_N32_W8 @@ -20,3 +17,10 @@ PrivateKey = 00000008000000000000002a0000000500000004000000050000000400000005000000040000000500000004000000050000000400000005000000040000000500000004000000050000000467c6697351ff4aec29cdbaabf2fbe3467cc254f81be8e78d765a2e63339fc99a66320db73158a35a255d051758e95ed4 Msg = 1234f006ba89f00111213baf0016171819f0 Signature = 00000007000000000000000405d6540e1e239a4c12025ce32634d2fa91bdcb3610e756bfacf24100206cda393968e22f252ce3ebbd698ada87c64722729b47ea30b8ea2280b0a343a3291c226463d3a2bdc4f3fd3943cd41188ae66e124413cc44be8b7be7f0be1e7e2410b09d956331d2adfe21f5c9edb29ea288a51d051a57fb5063a25c9d7b5d7da96bda2e6b92e0a18322e4840ee07ac17d0cfe2423dfc8f618f09b18be76d65eb9acbad0d9b97a1c8e56f17c3394a39859f10868e7e7ab0e12ba21c08c17aabb8a06df6b81b9d88dc0b7c434f9155f770cabe3d207c4d69334c5c8964b09a58ef812f2cef94c1a4edcb24089849302527465e2d49192378a4eeb3b5b9b7bd397cd4a7afddd6e7a8678bb6184ee9262f1b3de632b4824d8ef84d4c1c84096ea1e19a5f8c335fef770df4eff1b71380bc756b365a181f8e6b2283370f0ebd3c67dc7ddd93a827344cf29713362e583d83f686b212c61444a2a7f68ae52d2ed26773a2300af3e8994f9c6be9da89df58d3e74a7d7e5a043030818dcdb1e1a7f4d99bedc296d40898f3bbaf738db43bf48029a63149f241a9b0a4f819aa90103347809ec39bec35bbae153d8534a019e218c07e3af40a05f5a434566c829181e5a34af65030960a60f6e44d18a0968cec07462f97e17b0e22cc40790dd55b157c0fc2afe50ba462cd5498065f1956c3590c7645f1fc57d4b084bfc0fbb37815b5579f7da4f1cdf37b5917e28ff9c2bd36a014581db1f37209be1765aca712edfcddd5aa73f35018817d84406203f52ac2a9a934adc5504f9b390c24b3bee7fbf8e3e14d2a69bfeb64f11fe3efa576a2f75a21652805a0c5427c4e38753f0cf249d65557e595c638175d1fa9646ccf9e337532122456a1062c6eea4a5e96a8c5ebf52712829d322b057135a77f2b271c049bdee1d468caf7b67f3dcf6d8f86a6b8e4e52a2216d887a96cc5e8108ed8b76eee7689acdd582cebe2932dfa334f0b22ffd409dbb051b69c113b6816cfe01945305712e68f898636cafdd7a5beac8ea4312e676e4d6ec08c653e497453711d68fa5222e9e6901338554fa0bb30a095cd0c81702d04bf180c1a438897bedb5a253298863074d0c44ec53d36366f9d7f8eb499e7139bf521e0ea7283d727f29a4de2f8b4f8d88990bca0b0228ef00e0fcc75554c78b2b36f84120228b13cad748b6dced7be47c3315b8841cded1039899a800b9f67443c0d541c8a7b08ed9a33edc837aa7aaca9c777c3b2e1d84041e1e5e42d40a02a43203c0be6197f44fb79c5ea1b5d30e597d5a5f2786b19ccf65f10aab43ce7e2e560a3eda27df1e621792a4c3a98f0dd7ec190d0d45cfcf54459b0add4695f8d2c2d8f4d6430c71e4c2743ead7174ccbb64ce78b98479cdaa5e579b4441273b2bd3e9e692e17356660f063e0e231911a45bcdcaeb21ae5cc1ab4fea49aaa75095f1c23804603c490a6ebfd3b831698736a3d3ee6db206548cb3ea23c7ab6503ac9964c73d4b92f4733df66b64fb6d2c6994324d14f198f2a135dcf9b58105cf3464c3bc96dbb2a563b2c37f37a9b9c046fb3887ca9a7b670325fc92d12a35b0000000054a970a2543c843b3714d001881ee29f8a2b57f2771cd395b65ef564256c1a699e43b8805dc7fd899b7cbad019c8f763dccd3199b1604fd1fd615e0b17bdcbcafeaeddf3ff41a6287db6d578914fd4f490442b5224a213e39a447e303fe773447b75b2a348a22add5309b677ff3cf401cd02feb39bd590b3848b1842dd0b40d5a4619ac8dd06c1ddaffa01c25a31cfe37b7b0e1de2631bfd93670784a7894abc20000000500000004b9e64d2247edcfeb98973062bf0fbc69b15e5cb290f5ef1ca11e972cfba495c9c7e7e48ce4fb18d69f36bbc7e2d6ef370000000000000004f1d08604e954027b6754e4983743ca61f9c2428df683144570de246e3539564a7be6b8f6b0c1b97f125cce5961ab203004cfcd6d92be8cc1b94f3adbb8e5bb4391f5a7336b69445623f8fcda6f0fbd2e671669f24d5928d466e63633b5c526fe0b5cac1d62341d2595d0294e44abc3a38470a590a258b18aafca160f688df4e98cd8c88e94086ec4115397f90554f0551d97c256a071b57ae571cd569b1ac8248ef174c29e8e97c058795bc45e7e93e4dfc40cf8c0388d59c0bfa1519e142134782663a14d40144c43444a80cd479f50f706ff7bb884236f97696931aad382f07d130cc1bb597de8c49041a44d65ce78c61dd70701d08a014a0e8f2939912f736f84016d3bf486fa3fd36ef35623cead9a2fbe2ef58ede86eb631ae2be4dc087225169bc4f04fc4b6cfbbccfe2655849e1b00ef2eed5be1136e32ac2542355dc750b6c8e6ca5082a0226139ec020c23ebde8be59971fceaa569a992520b5ff92501a72ded50694288341fb98de445f0f193e31380a66ce1d12846097f207fe30adee435a48b1c0dac3174419394719d43be334f5533f7e92ea2a1dd4541848c8e30c25bc727ddfde6eb4f32c78298bd778e7d11bb6d4c26c09519ce51421df0074d7c6cf7461b38eea57ede4584cd2d5e433bcecd70491a2323c0c5d7993eabea1de12eb6b47246506559b26c14deb84347b6986f70ae6550c7a57bacaf09ba0740df66aed4700c1a016512ce10fc4370b7d0d8c90d90188569f502f27f4499e78a2e924e3b8b2aabf3db342fb0403b7878dab41576e0539a41e94c40751ed8cb6afb35704ea8584e533bf5f4f002821dc243884b4c82e3511f57cfcb9bea544ae969b37d6c81186bf2a778c6ec4b70666ae1b38ca9d9892d5ef04880f0c749e60dc8f7a05b3db1ac8e8714090b14e5d7405265f9631b65596e8a754d4a860bde4b71350d66952410a4a2cfa5cae79fe35b9e8372076a651796f605c3427164d74567a996be2d313d4f4f22d921a3e38dbb25d8fe89cbc9488d989e31561920b552f26895bb7dbe42a2ba7ccaaad08f1463808749723fcc87cd6fbb93ed0088fd4ffd2ad775e64d748e574090ba69c861eecaf778ef591d806444327183b6948d931bf15029adde57f59340acf24023fe6ef667e239f048e198782895057f9d7517f348f60b314c2237b4259a6c7320c03c71d4d29fb0e13857f68f77dee5d1c536b44febd242fc7e2928f9e449a44b742825ec3ac3505456f2ee47696cf16dd5f4424935d9dfdcfb36b0b251e7367288cea74af922c7bd586fa5abb2ecf1a2398fc0d7810f0089bacbd32f1eb30eaaa14e90481043dfbef4c8872f4bb292cabed42e6c0895988db3a5bac8fa86e079400ab358e152d3f2b5505a980d569defd48e3d14279397e6889b3db2d6dcfe073787264019370c3d8fb5fecac4316288197cf6bf45e007a2c3241c7e043a6c78aafa3f7808b4fffca82de752807e6dfc18981d39bb1e45515090db04afde329273d7e71be0c90bff437ec907610d9315a44a8e4feeb788eca167fe51950e011da211d523332e4033506b128fa1db6c64a000000050b180d4bee3f157a6ffe348e507d39ea68b8a5744b86213cc2ef01f4d5c300611aff4114afb1c7f4aa622251da30a10b326a6855c7de57c09bfa553ce5be8e40a54a477bb21dd23634b9232f5215381fce7b8c2c611ea64e5dc2fc004746adc1e801f2a8819ad0dce9d846998a49f0df5da5a491b2c79502d66b898bdcea3bf2cd798a3fa16144abfd217798d156dd483e4dfd4ab0ba8cc27bbb5deb269de1b20000000500000004fd49b7ac89c4b8839802e7972e13167ef167a7a86042696dca85eef5ea7faeb96a7fdefd23df5ffe4f389e5ce7e7a9b30000000000000004ce8c0974ae894c9f1540a8115afac5a90505e579cf2200c1786fa4e418da82f1ef4877dbc21a69a6e7cb255ffd947851cd24072d8953c0c465de07a2b1d9efd47aba87ef02104bfefc5958b9818280732a56d6fabb8df486128d71c72733d4cc615876f3eadf450ffd5ce85439838ba01215addb18e801c75a6456c09c5224ea627e2c41f63206439d62c547faa2c2364060db8f4d20bedef5c2b048320b133057154150ef0f9bcc07f10419fc49628f4a29c856f8cae268811e52ff7a6eeb8e01960ee054d5c3f46f07faf99d62c8faf653a0bf01b3b3203cc9228d3647fea12f88528c2414d0b1c11f4b14ebf696242e74646df2317aeea1f3564a32805c3056e156071a3ba25a675dda45f99438c3a954f9e8aa87106db304f5d311bb3606e47110a50403866d1ad450e6527a13df5a4e0722173b7d6b331a9cc85683b3346ee3837030ca546ededb8a96fe7f92a5615e0503147374ef1400f483776a2de8c4e03649b5f04c426e8ea436b1d528dc916d6e6bc7fcfdaaf0d3d74f7385faf41d0b9ae6a2fbedc26f4a4b36235b18abfe287a4f194cc314c370a07eb55ad4158fd500320f4b97cfa51d8ce83e435b34de937333b81c7af2f53be8792fa0116a3242dcbd9db53ea8513bd288331dd507aea922eba376387cc52e219fc0358c0b219112fc3c8f7346d09a062f62219af3ffcc142b78cd635f6d39b96a4f81bae6750248b4dc1bb34787f71f9a8056b5111fcb90e17345a4ecb0a9c4962293c37efdb9252a2039e8211a521b3e28bd733a392477dc11f99d40e00512563e60ed0497ab99f5a08e2e0f140a279f30d799aa3eab3557df4236a4df2d8e61541f80713254564252c2e03d22ae7cfac3ee330d76afd815bb03ba103cbeacecf42575601fa11d97ef6e5f17ff7eae2b2a67bae7418bb1d9adbe8f61fcc2f9fd6af7db4e68f688599aabcfcdad97ae3214a78844302b3d3418bf5caac744dc6a7db9256d45627ff33cff2854fe5e05d82466efa389d503b07c3c99f569ae4f3a3eef8426340d404d32f89f5c8bed9edda2bc824bec18eeeaf5b065f09536acbf483b409998ed316d321ac0140baaa394bbd5c6e0ed9302dc1dcc5cb1fc1738dd220b03749cae079b4850237f4250debc2b76e37922bc0d259d8c73e92b3738b59d1b6a8d59db7d85de95c255803dadc7b60e5e799c414443c32a07aa1f6392ae819c9c7f1d59aab1370064a3c3aed346f273b4e6e690769e3087fc75a840e89d4dd939d617fa99a641c96efb1735835290e366e52a7a2f5366c6483aa59cd87b379c8b25fba3128418157fd2dc8dab35fae8855139c7b3ccb08e022fe78362b94dac9b44ad3a73d52d1833b25fbed9d2713a7e7162164b293d95c1ff2b1ac17165380d648c8d925d43bc5594ec8e3ac323245dce749ce2eff8ea586ac13ee91de2adba6cc5c3ef270373247cad05e18afed6ec0f69b3f688a6f27c04e0e3fe5fb1795bf1dfdbcb75d6bc71ecf95f4df54dd710fe5076c5636bfe9e942c05f2feea6361f1bdf7e97d45c5710064989bd3292bf745f69f823616c4a9335eb92252afec5ca7000000052fe436bb96be4b74c39a2519f21461bc40db9266d532adce3e82aec6b964d061051678af6bd0063f03f937fc382e8a0ea472b30cbbe02840ead6d3c0a1a140f6b937d519689cafd82c67ac5c294e8d82628440892d74f18c30b8bf7737573b0d812a72cae4635218766fd73578f1b8ae6201935e9d7fb121dbe8e15163e76363dbac7bf1d06204a7da2d3c09e74a648c128cf83a6f3a453e457d157624f6954a000000050000000403a98d91f5fed62e9b9c3d907c8df63fe29d053f9476167d814d36ba31bba86bbe4fb8addb172512540157ddd36c1d190000000000000004288ca511323d353ce1ec31144503e475b648e934c3f15fe9051cebbfab186a81391722b32c471fbda6f6c0b0f9c8e063f0d343bc174ed2f292730ad353cb2d9966221b6e24e8c36f287dfa65c3237111dde4897a455b950831dd38e7d7d3b2458eb528160adfb8789569008399353c1d5d63b17aee102eeb22e516d749710bb28aaaab242bb579e7ecfd5097c44b8be71edf2b5c59bed6a86a27d88605d6b7c1bf25590d3a84b6e6437fba8c7479a84252221c2e0fc6ed26a8e94a75838958e52f5aea0442eefaa67d1b183fab268c3ee26c51704962b85ee2476f46906af3a8feb5e44a07bd8f11160ba9c4b5b42c1fa02bcb94b51e15f3591784fba19ab3f8741eb1ae8c1156a21e642779dd2ebadc37cdb247131537012c2bcb45a3828ced39834c4beee1b74e884c64453e0925dc22fde064ac74d4a3283577c0327993fd862d9a1ad1adbdde2c74fb4e2d3955cb6a8044248ebd2f31fe88536d005d3ba900d017088f5b40ad8dfd64b90587ca561e4ced1dc13f4f77b253fd7db71eead5f4d9d91a31dab44c3664790554ca731898dd06a1f6b14ec1735077afd3ca12ff620c84dbb1cb27c39228d0a993dc92b7420516e0bfc410a92a182783bc6fa41c4005d75696c507c8447ca35d81e9b75d11d231bf476fe37106afcb43b347bd2086829f9569bba03aa6aa58c8e5c2176f8da4d69c9cfbe90cdb01cf961ab1b1424d53ed6f5245c147a21e9805a9709311f3b8dd8131c842133ef9afc8fbfcc909d71a3fac020830d9b92aee2de144ab52b6a833c5b1b3d2aabf021414ab3d1f1bff4453e62aacc4a3ec86b351b3ebdb06b1d8aebeb19be8398a6f8fe674886a41eef72dd72a6a99078d0b2912555f791631fd555839ebd91402094b8ee268d91dc5468d0fd9cd1ea5260cdfd08a20bd14b672e3bce567a425e503722f76c3ca0a080a6cf6307c609111b72002a60b293a3d9367b35f6faabbfee547d7337bdd75c4d90848da596d01c0cce633fbe45736d520640e8a4bf8eb91f64126f3b6e0dfd56d104101ffbe195c7602d7b1e63f9c77945cd53b5c1d6d19c4e59ddd8334643f15fa8b358857f6088152c3c2f1d9e21a56706c0bcc30f87d3900470216819a46c12bc16f3ac917ee74e651f20d8bf1e856d1ddb729ac5f7fda4488f50cdb32c2f58988fd09e3be17d058b3c09f4e61eeed0b52618f4b23d090931c8632d7d2aef40768135fd963edb736acf89679c7be29aaef385ae919a8836d5b60ab2f85334b600a9b49c27a12b5f3e09717653263ec258517672bf28bc58f3251e876dd0890ddd837361f13d176776d7db28a44992b1eefd69b02184b9b6c12cc73af90835522a76c46bcbf4aef0051bba88f94c730b071b095af2ae689a2a3afd4a2b1ad94ee7ab509724d49a9f8626455253aac13d9067b7876cb6727b97bebd2d479555b67351ee59efccb31ca217883ac230943b55a7a94a89cc579379986befaf478faa998a50eb1deb548723da7f018aa9642c5e996b20dc572e1b35de2104d757ad02f08392fc7a0f96e0a75671a001d80eddf08a4d2c998dd51e3ac1ae92a7200000005071200db237436cd7c542ff1ad5e8973f60a2f9547f24140bce52b22dba7559437b84bd023661fcad2f8aa6bb5c52340a04c6a1ad72ba2327b621a1f64833bdd3ff778b8deb63d933599a9d9659d097ecce2d0ac76d36c7f636edab0a0d57097d7ba39878dc356fe9f0736d4ac01de9f0691fb35055a7881c39728bd332febef41da6c758ed1dc545ca858b1b0fe8c46267224aa45ef5a7a019bf98aa35ff99e00000005000000044a85c34f1d5e3d5f6186b5fa093ca9819fe4743ca2db8ae3d316157de54f1c5f72babaed7497624ff7fea4f24cfd7fe30000000000000004e73fc7b3b38ce2198321c0f05704e8ca9a07dd2fd008bf32a417fe8fdefcc6f8b118b31fc93533076e29159660e4e5bab2d943a3bd29fc5589aec445c2d44eab4670cab7b78f2b7e813f8172d329d573af0868d56a3081bd22f7fa575b366b2729e3af9da748c4efbe4db8866d435674202537c82097433e4a6333dd9acf7a85844f6e36859d547d0c3f505a7952ce5aebce5d20e0a34d20a6741bdee394f119fe21405683309dd2086828d9fc86d370ced71fac710c73b5e7abaa930a82303eb8616d6fa572a0734e9bbe4a3630a61fca4ac9ebef438f66a4978050b189d65cb966d2ae978fd9dae85960dfbe589c8fe97e79fbe2cd0eda320e7507db44a5abc20511a12de217f37b6be38bde75cd8fbd6e3c7ceb95fecc58be7c41ebed985d5b9c83b67fe7dff9b62318bc8a1e6e2149727e972ca47bc63f3d446b57a6d7295dfbc19eb440ab3ec888027b0f54a585d87e1f3461be46808869382e0d3cb647eca7b29f42cdc5446006fbd58153580c42437b6d20bdd8fbe5ce02154e618df750f368d31b23f839396ea8fa2842fdbbe7aaf8dfa8df00d22d13fd1f2eff2daa7e0a193fc683b86985e0a33da498c5151d613f28d33cbbf20a0d2f8dd668f9ca667be11bf2ec4aa1848e1a9f66b3069e63d89d0c3c421bc566dd71e13157e72e4b28a489bf35c6464ca6b98ba6dc0a5e236fd1d6afeffaebcf8bb443c7a50d6cf38035a55d1f18f7b1378fcb510bf41800117085c5f276a78f853ca79f8e9121f23e2b3e4a514a78839caaae42204431068655a9e74c33caa82b5e6a677db7f71943e3321eeca123f4bd90e4e17a4abbe025684afe7fc49057fa86a64393d5ee109e6aade800c64414c4b6024ba9ea1431cfd23414f708ef15811e0d14b57fa9f06a6092996472cb55752d8f900984bf9bf9fd816ca5953491c91ad174d817d7ced49a0c2380eecc5039822617afad7b5244a451f21e76d8bd8d449e720904587e76b4e8fc771f29a4038e5bb112ef9eae477677333f1718c12472dde0a18204efe10adb117165825fd5f76fd161f027850979b4304ef64b906b8c5dbd51df63c0027d0b61820c89dd3b8d747d285e918aac6daea72c68929cb63edbb4181af2b6f50a73404415d11baee572af2b1d600f5fe63ef2a0e021e0c41a5a23333461a074a81ef92abc97b46320086a341dec3bb47ca477a01c64e7f749c6c19fae0fede2f480310916f273f2bdb2755a34a8f2fb367a6c2af642789cc17d52b29b1d8fe5b9921f06f9a0d74f871c8cca49829ca65c61cdce17b46a4dc927f95be281bc1ff72b9d4391e2fc8626c1953f284a3a310e09bdab525faa78b0b7e26859aaec194e88aaf4fe1f792924bcf8b51622e852d34d847c3d099e74145fbec1f6d8b77756c863835d7f7b82b3aef190f57233e97807ef8a4d20858e121f40c845edf12e5cb481d83d9ce50ac4be81acf5c57deece23f4c138cad107797d5756fe40ddd7bda76b4d9e5f744cf649b76d5724b97f8e6ea2c349393bf8e314b334c63f578d47ffea7dc3faec8d2e657faf8566367d66b1602975f36a82181f837e545d00000005c89ca9bb2d7db697f367fdddfd5d8bfdccbbc5b1d9f5f33c0f0d938f9aa4028760240001d43c7991dd66f9ea20183f1f292934c064d5f8df527568b995f206a86580f098fc3f9fd33d082e77232fcdba14b5d0813710ec4a5a60c16b9e2ac82df3446ade7c76f4ff4a46beff4ff30f960826a2fec9af85a842ad6076e02e259687d977d1d63506b00fde5afd9addd100b15db34b151f41a05a154296a7f1a1e3000000050000000490bfe15d1f05d95c8d253f6912ac96f14cb60f4e64fc90998ae6d74bb0b4bce0a6f3f285618e2d55db46424561b9e84f000000000000000416214e38e67788bb4cf4a76d7b93355c0538b5b1662ff60e7585952e3112f21aa9286a7706b621b63e8b88524600e101f4e56c5fea5980c2a1ddca9db1b551852f65956e852ea422835ae5c788714b5fc8296c0c4c06fdbf199251fc1cb17ee4b4937abb22e35465f88020ba330e5f7411633e6204a30709e40414b755bfb0d84986cc2bb58b8c80d630ae379a8c4adc6df29f85a52fa70ead5073fde470c9666137ebd63f7f13637da99fb978b1f16dd50df2c213a22d9a828ec060c4c90437ba3593ee61439fbcd1920dcf74d6433177881ef1e2bea125fdd625584dab5960129210d01be44d4cc5d80f0930a58f6f210db866e68b63794c1ea602a7ad5520d3888aae9f652fe6445aeeb6c9c2cad8806e3e1e3b073448753447215cff453317d58f3003068af281356a5f448dde54f7d352cd62d5da058ecb792f56e9da395acec4d0236e3c80e3b449f31f892bbf171365236289b491e0dcd67cd86302d0583a4c54ad5e15366dfc02ec74b1cd197ddd07176d588e2c9e16d159518a9e6306a2eb7a4f353759be460b1e1aeaad7880ed82010820b34f1ac46b5a05a4dcc146458b3eef5e0ec19ec9c087604271692d2697f8c04b6529063dec7b53dde54ebe42a435ce2f3f3b94a7f78aa6f0aeaee0356b5a622bc540fd81df8a5a0f9cc7baedcf6231f50f707fd39ce1e0101fb4cd79d576104f735c647f555dcc756eb868b793f961c5689107d9d8a6c628ab52cf6d552c1962e4decfb224fa1b32afe6e837ce2c584e848724d5165f4cbbe94e16482435498b787a7b16136e9bfab14275c30decb873821b7ead9cdd3304383f9c9027eed62d871d127e8fc99094430639a87cda5097bed99d6613115d9a4415dfb618889bbd9e44c34dbebd06da3e167993d388a6f106b0948d4cee011830970ee8e5719bb3cfeab6ce5a9f6415b89770b39615e98aa6348e2a0526112dffd31efc22bfe8e6f233e45ff70ed9039a0624fa290211dbe9bf965993a57f24b658879a4464164c245c6ae11476ba05fe6c7821bc5e42375556dda06f1bb3e8728b88f77b8a0a13325269eaf4c176bc822abf8b5159dcb8a4675e4eddc51827a34547e754cef9addbd2116cbae7b70c3324dd17dee271390ba6a09014930297b3abdfea220bc5dc17974e88c2992dee1ee2c1ae1e4802fed5b94120db3ce063c3acb7e625c4b4de2546aab6441b9bc84d63d43fa33557ed1e28de5076e2e3c2440b502406d2cd019a55c2f0ca457510e36cf32d09b5c92f6e600c7246567122dc2f2cb6a5bbcf6aed8ef5686523ba11a7379830d3ab78081e239cbabaa9ee320b0d3074c0306dcaa35ec63962dba95b9d861ab19ea2f5682f505e964f47ff9fcbee437dbf8689ca01b580adff8a2e35f8c4bd3004889e74b3e7ec1c7a5bc381d577fad6be1bf34ea8dbaa761b2e96ad618cdd0153ad7e6e3a5f4f855e0889aa1d321e244b2f1a5f603a456e69cc321abcc9353413f5f6a2ffeefdebe156c3b6739dcd93acb95113e5abe10435a561e25a7e016d03a18ccaf00f9c774fc1c0497c4a478be37e9abf60f221c206e47257a76d000000052c887acb0b01fec3d954155fb6c05c1cfd49a7d08f47b922ff0a037dca04512d09ebb3f25c134b43782c2bb22d4635564d7d443b84848ed576a9c6c0d41f529788e76a899c34b361795c891b2ef03cac5dd5f85df25f2d4705226e60617619a5aa86e9474a0b5fa6544dafb998af97ce7f99082fbd4296920af3dd3879b1887b058004d091cee10d0796ea20bc0683d7230169fef922d6903e9f946410b7e54700000005000000040e0d0f3221d2ac730300088e7360b2b9c57764c964e440d7e2f291c8d5d9e2ac225f58adaec5332a98b9c596e3e4e6650000000100000004a755f4c5ba2c97b47448ef2baa8adc9b4e47070af99a3d9abf25ef3b062e7b3fa3fbc54d239babb16ae961ae8e440b6df5d2440cce3fa87e19b516ffa6a022a528ab89921dc9ea6c4955d0d0b4a5068272dba7ea21e6d3629d8af2c5a902ece63f5599d9b7bc4a431dfbe553d949e571a99b5a4603038d3c03a9a6556da85c1d898b0190fd4577b3aa90478693bda15b42d07de0f90f98ee8fbd2a86ef768aa393b5bbfc7984654239b9e4f40a8bcddf34b422933566eeff3d21f88ea8dd2a3c865b9dcffb856f36016378ef08d3b53dffff17b9a1a04b1bd6b29d74d3ee0def2c231176d0d9acf93c7f61c83e569ad935266cf0d2641e0b90fbf91b04cdc635a2ea378fabd9ae6f03ca8e68c36dad3cd69293fe7c871c4fdef014ceed7483018cc2da6d29f6317f504b0490529a4776ee96048f2ba88973c6f21a6e5786be1927b53c881f79e9bfb62f0dbdd90e62ebd16bf18dbc8b062b046cb04f50efecc7d1712cf169ac2987f7eafa1d33ca3b653275b3cf8f5d5a6248e02245ec87854dc2b8da097726a26179c5960d7bd7bf62b59f89bcfba31211d04f18874470ceb8c8cff42237b15e38587342cf33fc9b54a47988e5f7897722d20f3713e80e8d75806ae7eb82b459072f28420ab2a6cc1e603f39dd0aec59a1be963aa72bb174e4330ee61ed79e1337515f6c38d4738c8c17e499aa4613469e4cc0aacf345bc634a3a3c5057bd802b252a6bfd626275039993e11889c2fd6c1e5b454aa34333cc4bfb4b24aef6dad7843781c9e81bd5d9ab528f880f25370a9f9da186b0acabec8ecaf405cab5f75f850958eacc21ae5138c80ead4c580e3a0f8ca4701ad6dcde39a9963544744de529fd1d6d67ab6674accbab6636042e90e24cb3d2eb2e017def1ad7be905d29d51d1376ded655da81cb4d04d59082fcd70fe43430a959fb01f02722fa6ed39c6c612f1bd5d4f2ce3f462efa2d07bd4b3bb20b1418e36088369d289b5e6a62ab5c71a831f635edcf4d3a112bb44ad216d8bc48062e50925ce1f260d1ae4c709c16f3c531f0e7b6da1dc9d362291f9c58b56bc6b9b16ecb4988755d29fe9f2fbffbee9f0ee0d5b431426db9c72e633b10bc6b9b51117877323e00e72b0b60c78f53fcee4851c2a89bbc3ce62704415b0ee1a78b7fb981eaf8490b284f47e488075f05d32a7630d7b6bf12adb495c853e245ae856922a764ebcb6ec194710d8b3e1145c1b573548cd76a9a567413cd5b3527797ce6feeecee8de885240a7913ac5c121ebbde05691124dce512d9c19df63f908b1d23536a93ded8316802da8348e1bf46cf80f3a58954a6acbd56efe44ee6a7e977a14fd4889b9ec57f6ba99d47eb75f197a1d234f8ed8861dc34a7376d5b8a66d3562fe92df1482979f4d8cb7f01ea4978d5071a6f2513efc6dc65ed4bbe9382b59a2bf5f86004244193e483d200d4c413f28247ff74951c9a6ffdafd71b6c6e581fa2255da48fd837b0757603f316bf95e95a1752ea0d3fb559ba090a7f306ec3ebdc55365aedb352050936298442a88e7ab27bf91a86a5a2b1e2692c469d3edacaf1d361976700000005ac67c55d81b59a4f49a3373c41941394f2df2a29bd154718cb7bf01b59f6c0d3293dfc6653da2687a2acd7b1209201d8eb54ec86e0999f9cd9911346d6bb4b5038729269c3f09560792d44602af220bba33f78757e0b1a625783d83e080a312d36b274205192b04c4945e5e541514933d1eb52724d39a2c72dfcb3d2e665250a22f411c4f9b224d38e73d63a1ac6ff2dd225846a51a691215663b0cbab31c28e000000050000000475769664d9c2a018a311b3181afeb6dc5644e3c96f758148a862eb0b9acbd9454daaba156a0dc07880367649a62f058d0000000a000000042e71818137e7a0de0913d3dbe669d8ed052eb11463e0e27944d8321827eb9ca10f6333c8f46cf967ed981ba096943ba9bb200c349c3c861e44d69e4892da1cff00c1f3f761384c17500be09af903705f6fdd58f5b6191744361565e453a0ad4db84fd6d9b05b29eecf53d42c279b449436ef411d9a3a7644093871dcf4191350611289215624c0bbfae167bd2ba5fea16b5bd267a16ce73b42ed7b7cdbc9ec90e9eda87a264346877871b136a0e15f284e953a01ce47243f4baa84b209da7097a29449c0e1dd5de4909676071e822b2cdf4020d048090d62f139efbe257cf22f5f6e260ca398e822b78ab6f369fda984e0eac0e1c406a10c2500d816e189da26c79da4f20d8d1c70b9d2a2b7cd1b35531b83f5109aea65dc33c52950190c5e91ed06e7ce01fe22d3ea8277df3b18b973e1bb29aa9385e9623c7d75d0d137257e13207b63660e88a145acc65d1c14faf9459836b1343d55b7d5a2d0e953e5a0d2f6e54321cbe661b0360372941eecd5b2d5dcc5a9fb7cc846f277260b0ddb92eb15086d7e44d43eed12e05a84c5de08bb924d9695687c63958a186bc0a4f7b284b0cf40540a3749f019565ec9facb1d5602d8485f3ff5b9e6dfa0bb96428b3f9abdec3d830b6893c682a767eff95afd5e5486bb924373ae42824d8bd3914bbe37fa2f39f5c4569e0962163af6a9621dec44d9c2c21f02d9126439caa62299d569426648ede23af52fe8f3ef62962778e12dcf6f50c82a77fc7b5b601f8304a800ad3cd17f57ce757baf8db0f12994831c204dc55b47f3aa650b096a506118aa213721d4a5ec26951d5b489d623d2341382359aaa447895b7b8b7ccffe5473f3bd4e4a4feee427307c155c34b857fc7ab7c7b4dac15d0b1bf0201e5c32702b8bcab101f5c3bb7d357f1ba37ef329210bbd95ab073b6028f3e0f5410d07cb7860d24f9431cb32ef567eefd542831a8539b321fa23f8c8c2e5c96699cac8931299672d3451d67882c91df6cbd2dcd18b8eb6d0d42d9601ea60c0469774aaa8001e36e2512bb8488315350d02650b2a7b8d614d18140d32958be9f0ff626005a0362632e9797770506e29794d78a441220d7af4f2393cd48dc3dd60dbb4ede627939e5db71aacb5a7bd66355ec4ef7afef1b6b10e84f4d46f3951f136194e2ed02e6a501f74b3933e14ffee106a814e45fde1112dbdffd75925724b48d35cbf8ca958535910bf7dd22008c2f1c929602a55e975cd394e8faabdc5b1a7d1f423d46e3544f38be84c30b786e4aa3ae3a258c76b76e439a59fc014be73d155bb616fa6743d95d6fb10ce7b6e47c825b050a61661e1e121749edca4a788957a2591dd9a4227341ae9a15ac21aa160a7d317c7a6e5473087f74c8af6559f94bef1b2ef386bff40a7dfc6db067970d161abc22068a6d16989ab890c3c71492addd96b9fff4a5d05633432f975574cc41624745fc78b732f1b0111d5c69b17847d22d3731ff262028db786829c282f8b050c5250491e87f13ad387a2523fa5255ad0af22ce6a63df5d51237b48323f5de1b7ebe8f1fb244a2e1f1a447a8f68e0225bc694b203000000053e73cf2115d7d605601febff7a660097aaccf4705112b7fa133990a734fdc6de8436508c95c94243184cb4a272e1748c983dbb0e9d6e8023ccfa07e4b1b5b0109d72ca5b6b83fb49c7b76fa9c3e88beaaba6e46364c5ed122df80ef06838ee05480ef5d99c10a2c704ae827e2c7529b1c4c7053449698987d74d0704de2d94c5c75bef86b09aa5e3633315c7c7522f98efdb912779018acd75d59392185ac4fa + +# HSS with 2 levels: +# Root Level: LMS_SHA256_N32_H10 with LMOTS_SHA256_N32_W4 +# 2. Level: LMS_SHA256_N32_H5 with LMOTS_SHA256_N32_W8 +PrivateKey = 0000000200000000000000830000000600000003000000050000000467c6697351ff4aec29cdbaabf2fbe3467cc254f81be8e78d765a2e63339fc99a66320db73158a35a255d051758e95ed4 +Msg = deadbeef +Signature = 0000000100000004000000033b4c17fbfa9a6ae2c471d7f26992849c4d956a81a65ee52e757f1df90b8c031d56a47a2d953108c6958b9e26da66abf4f52b8856bc93e3305f61512a04d2c7a77239318432f1bb858fd2b8af19f2ffa1713eabe3d5c7bd4eec1ae075041e87a8dec5ec762772dcda6e1a19be5ae4745ddcd8f954d8570c866a62a0ef1d5fc449c93b1dd3151fc75596b1be6b98ead724d45f9879525483101e11105aa8d94d2c42d24921f148095cd71be805bb40f319b1632a2b0a2d2f5b3cb5911531eddc36540b622d9bbf232cba12fd1508b59e47cce1d6756ff5c0a76f8f11aa09f0e156514ddde065481c527b3cd20b644c450fca3fadf589ed57c1a893cc134eea4b4c2a0668b741b707040ddc495ce60046b76547d0a2f48952126701060acf931c8bfd4edb60f0e90af071e0aa5bd54a26cd10964f4b978600e586b012546c17e2d3955219229c279c2ba05bd61be9f117dbfd512ba9ede726c0b2216fb5d972e440a75e5e9a3f72e42e986ff4ce03c5182bef6bd8b7aac80e91d9e72a1c9ef857de713cce2916f7e723954e177f93425df98a79e461d1c5c890138fd05f5721c63663877dc5cf72cb3ac1209a9282e843d1097e58bd577b5b611606f054f8c3238e315c40aef191de389c7978ce56d21f01336f346ddc6203076dfd11f2b9abe06823bd4b9331123a7774a1a8b29a89f9122891b9f3ce13fcfddf5758c77bff267f7f22215831061e5209c46149e541f108960c918fc8a9bdff2d78987cf782d5dc4a8de064e921d433c3a8f4362d340e19156564072d3ac925fe884c67434b7cc3760c203cfb2f34c21e881b4ddb26214b711ad96bfcaadab534799af504762bcfd367e34efe693a7da6e828e68979585aeab758378bd779ca8e379dcd2e2dc38c0d7f7211f58bad8bb0a72461af81712c35c9825f8b23e26625b7aa46ea71847ab67c5770e732cfc9a3948ec0772f67382bb2b9a863166693329a50f80c2a87b8547b6c11a5de36f9b10f28ed67813770fd1ae160c4b33e3a96b0dbec09a24f5a1852383b1fb066b778acb0a86302297fe67f99a488d984c2875ffa2bae331d961bcb726b818662402c3e5c9ce3ce8b0e7ae87e221e18ae35072d3dbf313f58b5d399c7e783d5be4234234391b5b9676ad6e7417aab4716ce1a7aa24db9254f325c6e4632e62ee8bbabd5885ebd19f703c6aa0199469519ec68fbfca30c7549911afa846735dde5c69865475f13ddc09b240a6d3bc50ea0940e89778e369cece235de4b1ed23433d305d7da612eec60b066e519724ad75ec00b2da20551b08723d9779015f2df6b30d1529ef52eb40383f37190c72be0c9d045b3d382eb5e56ddd3eb2028a074cb4a4b6105dfcd14ab226cc1240c681d3805e6f54260ab8aee8b8acafb483d37f7c87a4674b8043c40dad09baf64f2aacf915bea02fe01b6581b1a4d1e37d76ae7e4014cce79f7c817007f1d4dbb30057450efe38e2a843f44cc6a99e190e3199d8a1f65e120bddb0ec1df8475339b9d0fe3653a927bd915f6447ac495a0f76fc9336a77ba34e646d20fb0b63535683da47bd53d8bc3c846e4c27e199e7be6a6c2821c1a0311fa55699e88c100a0e1520cb775bf4d998915ba93f654bac743640e8c01ea15389840c49cec190420fa99080aac1d82cd917c15f454a1d3869387ccf169a5b015e3c918edb251130d773354485cda5666d8500907dc315c93467044413829526b89f567ac882fb0b868fa29687950b3c1a8a1eaafeb164df0ae598fbb204540e4cfe2ad121e28c305e8d8eb50822a345b24efa536ad999fb824a11a1574c3c80676120eab3f68552ba940df7bab21534ffd17491d7d804103671aee935c925d57c14519cfe3afa4be8870d5abaa0c26da9586136ddcbcc3818d8e9f611f6c8fde36fbe81b0b21095a9906abece667c7c15f9efbbdd30329b963d2a32b4d3230f56b762cf86bbc84c1fd50e4a65b2eb4c3ecd273b4815dc6bfb10972fa1b3bc13c7c1902448cd4873895fca5c452d2fbffd0313bb280bef50583a322f1266e86bc26debcb53ca98935a170c0f21a729f4e3996f2318b9d13d6b1099689c8c520b7cde5b1938f54172b21e8cc2703433c454a26b26f6ea33744e3991e99377b2eb3e67c7a3e83f73206293685e16b406b5117111c56113a1f703cc3bd7309e23057278354074cb127c12a918f1fa22444c972b66d04e0065740a64b9c0e190653e4845ab0ce568a18edd204e4b673093f33eb0b45be36d2240312b2e9f5dee6255a689e86167c8d059fd06a2e15e2d437b542e06a915c0858db75034a61de2ae85689c34f6a4398ea1c8d64e87ddfaafca41454548afe6c50b0645a0317b83e14f6bca49cf9fa9a0152887cdd7311faf728c2ce0f9bfd48d35ba75bdacbd77180ad0f7d0ba9e8422fa8349296a3dc6dec74cb76515211fb31d1ed51279ea004f7c529151f576da91047080f6c86dfa9d75e22b71a66331610c75af8bcb865f0d17b89fb3e2ff9c262423d00843924547b9f8d9ee7bf7cc49519028785a8b912813fb4a2524a23d211d75e2421d09f7d77d1a8cb00d637225a3509571ae50e7496de26d04ecebfc07c8c2add9d34f73d6ac593e5fe03707aadcef5f362e422ec285bebad8abec92a77b56df920a68aff30433bb2a2beca8662eec7bb053def251d42f59e97fabe4ee304f952b073fc64b04661d401f19d61c61a1594716d986bdd1749fe6244701e33916561e9d7be473c866bb4ee616d985f10d07405f82e0bdbf91499b07ab3866a21a52b8d89522acb9b410c0784ce4033acbfceed097cc7c11e0216934eef324c272374f3a947a5ba501e8db1416d54881bb0efaa2794f0d0e2592a2ed0526fb56e286e2beaf2f22a32028ba75c2a4e3690d0b7ce6e4fbeb944756b9e68db4b8cc873313484063dcbcc3cb842507e09ae5cbacb5761a9eaff5fdf8a271dea35192d4e4086760fd3d363d6a1bd7254e1f330cbd69589e61ce0fe9debe70f39cf1989979d82f4563a55aa8e609126d8d11a90db5ff0d39975781fd81adfe6b628b2ff1537eaeef692f6748068d6b300000006f0b054a497da7ad540d28c4cabc6fb47d0b99287073378d2ef81da4c970839f0676cfc7c443e335f06259e4342497e824b9a2630ac28124582e9fa8cd0dcf0ddf81b07478dd5ec5c84c7fb43158202814767556027f40fefa69ee48dcb865422346afe98f627c0b1e6c7ad2de6de92594a4acbfea6dcaad85c5f066f57cc010de83cba33ea6cd9975e1f7b32ea8184e69f1f4bc82c0c96f59e194932c133a1be2d24f1c945046e32b2e0480c70de41152603f8cc322b67cff334546d9083be11de638e7448d32cccfefd5bad504bfd59764c797f91c61400f3eed0b8c92c20fbfce05ae61377a499c7a2d364dfa4d3d5fc54f1070364e08364d71cbf3bfd1b6561553169277d221d4fe50867d1e0223a22169b78927a131b7ee42f55fe9e3eff55fb241e0006d38ff42fb4bc929f32378b17ad5de3dea117dda0eaff570466b8000000050000000470fa96c7207fd718fbbc14f206be924232934612284f800c38fe9ea986386a321704fbaecfa1996db792a8d14123e3440000000300000004d1324626d19a4f048634ec2b98dc95ce0ce192986cbed9a7faf605a7a9da476688c28e49e9e676ef0640709d23015073dab879ba93d64f71837b1b6628a43c80a3f06032c4318e03b18c252e16797afde4cc8db79abab009272475fb7e909a78742d467bfe3820f37ba4f0575abfff0a32970a246d54d794e5757ee82185ec993e3aafb598eb0d634fd0070467d60d4619a8fb5c49c82fc5df0d39ff40811ff6ec9c3ad0c87a95254566a1065099c0174317943d21a2dc9eb8c81d8c9363a686406c46caf8683f16b9f865f5063627bed345b7514e06abd2e28543b182a0cc36a09a1399788b04575d79b478dd86ce2870d8202bda4ca79d9afdaefe32398a2521e605f4adb6ed8612a1430a25f6ebe264ad37d4f401284d16924fff9d68630323c4338f2252f5780f44be76b9a97d5010e2abf846c17e811114a245424796c3c97b5992cbdb8014928723088a5f421ba067482b19c2f4890cdb0c7c15ec6eccbd8e133dc2a2c10e544ba855b2b07a2ccd982ce8c6ddf191c09a0705e73a9a492099cec2731623dc551a7425255d95e17eefe2ec160bd96c488e9f3c9985454241f4429b15f4193680dfbd30c7ccfd6920b0f5f7f1ae0a563b0fc93690807d5c6d784b8ddf24b1f6c9bce3692061de54e057e897c10323657c624294fdce227fee7c50ff9be5b9bd56022adc6515b7fb44964e9c07969c2c325b2083977f07342bb503448621a51f9f9e48b2e5bd9756371f900e1a1aca92331c1f0173e25a211c9a7083dc2135324420b4472919c6e9c6d2b18e60ed9fda9f9de23869cc428547d1030b0f27fcc0814d4a91967fb94d2b0c04873486e50a5875e21508c25f4679e04879484ffbf0a7c65c40937a246a4eceb959d8e0cf6cc6c29e95019c3d69558640a51ce187c6df658947fea5b358680ecc568902442fc159c1479bc43393683019ad7b8ceaf094ea165ea4fab3ebcdb57337e5e28c5f23e3bf7de2fc7fa9ac3a19a7eba424bfccf4d5c1614869c6085d3cbf1f77af36e7a6bbdf60dd8e3792974c7712f33107dc553dbf3f1cf4a7423b7cb1624aa40d01f4450e245530bf20056adf34b7ae3e8b17c059c9073931e46eece7b8b6a725c3aa84ef43dfc25bd23e441953aa40d8689cd34e9ec0ce7a9dfb37454b8d152877f45592eda49df265c4a6586e8de427fcbc01cadc240df08fa3fb3a36572ef8758185ee748ee063e08c6b47c031e75b799861e877019265515267f65497e3307c50d0680043fd36c9e13a83bd7b3e0892dbe41211ea50f795ab2408dac8b5e1eb7c23673b72e4c076faf93a8c71d3ad9f8570091d16667f1fde53eb5380804a5c5c78c6bea53acfff3fe673599351d9988acf0435a8c692e8d46de87af8d72389deadf9a0cca2312ab35684d06c0bd1cb362077fa53805c4d7823914656904094ffc6a1e155e52974b07ff2055fa4c14454f2dd46fa16fda77a11e131df27ad93f5c007d2354d344c1fc79a67eef198083480d62be9c8294d7f01763089c0580f74ad18471848d758e4b225d8972e20261ff3753e60630199bd43f6d41693bffbfd7921719dd7b60000000598a22fbbeb0bfc68202e2e9d480f6f1df0e4e6b75e3566ca6cd61efa6347d12bcb3448adc2c04c4de6528270236158998fd4569299325dbbc648a99c3f0d511c23006ebb7a865a20de81dd118b374aeee6494fa9169dead8d7b0d7c7b07416f259846e45169c5a83dfdcdfe6b145eb0626baf570eff8b8e6f995d2498ac74c331faf60b72418234316df461327e4ac14619b565ebcfd7e5a4b388930198b9d0d diff -Nru botan3-3.7.1+dfsg/src/tests/data/pubkey/kyber_encodings.vec botan3-3.12.0+dfsg/src/tests/data/pubkey/kyber_encodings.vec --- botan3-3.7.1+dfsg/src/tests/data/pubkey/kyber_encodings.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/pubkey/kyber_encodings.vec 2026-05-07 01:38:28.000000000 +0000 @@ -4,6 +4,10 @@ PrivateRaw = BAADF00D PublicRaw = +Error = Kyber round 3 private keys do not support the seed format +PrivateRaw = 3305F0CF680FEAD1BCC50ABB0B04F288BD3266AE12188FFF50D316FC00681A81D72D4E46CB5A96ABBBD507E8D7BB4C523080550B142D1594B9A75113D6D2D70C +PublicRaw = + Error = Public key does not have the correct byte count PrivateRaw = PublicRaw = BAADF00DE52CC940EC939B216BB5C77F5BAA8B2672950C72B722D19DF9671C264061A4D424E21A450B8CAC882508AFD23565E86664C4CD56094C1F72542E1C25424B0DCD4842DF48C8F79893D73148DCB99FB6490FF1218822D6017FD17FC8D6CC5514A5B3B3A1A169CCE766A7C74B24B4E8468A5527BC205919569E8619B87197BE1C5C34DBDC3756112875858FE986BA6E3A7A7F64CF37215E15AC522BDBAAB1D73A8738B90AF35A6F83C8839765AEA1322FF923237271E7E1BD9A56092A9591E8B54245A046E6E8268EA8306C0488F616BC65985ABE804F695A9EBB385DA97789F8D381DBE2156E70B09B1201F80143E55265B4868A7B67A924D0C6D932B3DFD18B58D3A6A5002D41080B03A9796CB3734B3627F4044929151ADC0C8348137B2F549FDD70B622C72F3AE772B77B638B6379162A64B0110FB0532162068919C351276618623A7B96867C3479CE6B3101658BAC7F05938F462C2F88918823A8444019DFE0381FA70A7C11BD85A394A1D11FCB6B8D7DD94E012881E6AC768A7486BC668665A83BFBF261B0C280D7878747EC340D0C57A35810D1FC768D128796EAB1F4C72BD7341ECCF030508392E8095985E3A63BA4CDACB96A15E506F22C5031D8CB6C4C2B227C6EA8EC76244BCC7F425FB5F28E1F9702C0F3AFE7A946A94BAD108375D512C43B52607ECC91889408A1156EC61593FF8156780C5EA2B462B733A72256424442CABA87CC1D101983095E0142420D295F2C064AC96738B021BBC75291194C10A978CB20FC15A1C4A91E1B2462149B473540FE677645D5C8F473C2862633461B4BDFB561C0D5256A239F49483A926C2B6CBAA08A4381FA1AC4E26688ECE103E23C3D85B19D8600CEE57C301D3C2C13731673F599F3338CB7B644DE19A32297C0CE553FDDD737266630BB3416CF6C1477163B520276D6B63DB789A0284A65838696869CAEE20A6B828A476384C6C1AA6C48D9B4ABF816912C425AEB5B90093B253C936D3359E86041C9637B92E76261181F4DB98E4DC9415E5075815773FD07B2CA36C5821123D40A55CC57C8FB648C18642CF2A19F25D9678AE0001A1981BF9979FE2826ECF1A53B8C07A9346FED6BCF664CFD63FA8DB1327FCAF87FA71A @@ -36,8 +40,8 @@ [ML-KEM-512] Error = Private key does not have the correct byte count -PrivateRaw = C6C9B2CA96CE5B715A6262CAA2989B72EB6730B54BC89B65BC021FAAC7AB88E5833B9A7454C94E49CC55EBE30C6B7702EBAB0A40728E1F108E8FF14AF217556E85551810C2716B4248B9B10EA7BD5D05CF12425CA648A653B26B1D645595D95323F153CB1ACAB9D29ED955817592C017C4465DB30066FB68AE3429E5453FB0199C6E9967608939057632674B3DA8BB587174BAF362433C4B9B75BA304F995AA6238BDE7398BD460619E00F61CB4A2040BF09C5120F51BA7808AE171B1C9C733F4025B872904F45AA8AFDA6B1B8B86407A1C619065634F78296D6915156C44735634CB2AB498574282754E020473F02201219327A4B748CAC50617A01D101C34FB20EE04BA70BE541794440F0E815778444A310295B39164C7633A8448408D16B47E68E80096DED5373840B830E84CBE1F25100788E53E3CD35AA365674A334DA913EA7A6EDE3C002987ED94A6D5AEB8B3272B3EB8097B6F723C7541782F90F3126BF8ABAC201F51D009B258C877CDA9304FD73242050693E380380D3898749CC7FAABAE09477319228B6D9606D876469D622B15A6A4845CDAF1A6603F25AADF5A271C225C4213FD9422743705B33FBC2FA9BB55B585A8F858DE578BC44C4B3AA919660045733E222D5409E5C4B789A109F458961AEF8AC798342C2E854E2701013729921574E340B9AEE44BF9A2971FCD80B6AF994F7385FB505359346417963AF61986076ABC54ED6B4130C5042EB04EB6C885E2C4888A63A52BA49B9C6BFA41A91CE650EF234A4ABB8B1122A15E167ADF4A78115881B6C2B26C9CC32E2A5552DB35AF280A0919A09047562CD33AC8332B74E6B962E3643B14019C7479CA1A29BC420038DF52FB6A2A7DF10C7798ACD4AC315423361C8334D38B0099D4A5136AB32D599A0CD5983C4864567509E9672C3BF6BAC914B0ED5D04AFCB1A8DA16776E0391E4F5655E7147F955C5078C798FD362C93A6C7E9914454A591CD93972107C61057A9AE417B776A1BF6683505A2D85D53A6AE0BC4740278B69BD0A608CB0484B04122FB3D46F6C06A3CC5523EC054030C9A8A585A5DE82CA41B39082052D970BCFE55389AA956BB9E2C62AC5CE80FA64A15B4DE4FB71DA7100E04B85596849F3E0326570227B48589E45B387E612C36C8BABF09EFA22A021184BB6197B011842DCE1C9682492836B2E96C059C92C15BD8825A8C0265B795234F8756E505C1EAA29E482BFE4FC0CE945B75E84817E5310245CB57A3A51E2F91CCCDC850CF59027497D124BB36449B28C62BB90181F01372B0379C03B994343C7476B3146C87C6E25B677F30CA7BDE3369F1177B8A90BB2561B1C637D7E9CAB2C8BC4E0717669871B4451313DA55879F07FD01637ED052A82A5C7B3D1427611CC5D9B17827B4F3A28610ECA998903B4CBB2187C849E7D862867391812A94139DB76328C4EECB6AD0590C31FB627E1A3C319DA5D3DE44FEF088027108A6F9762D1B6075C0C3835A83A44F230E7B0ACD77241AD0CBFE4822BF189C868161A6B330DCE8C1A39A3042D81BA5AB86603205747995E6C6AAAF33149ABF8B2061137A773074F6846277C6A67C6789814AD32B088CD688924948528648238050EC9C8955EC7897125CA5F708112735687017F51788FDB9C68526AB14E76C25C490E7794315BF686596CAB483624754C834A7AAA4268BB98131A739A617907456D139805830945C8BDD0104ECB41A96AF92B86C04B72D963E47AA9168A0BD72210672AA7D15642B1220541643C2CFC0636A6AD344022C01CA6B3B787B659A06DC50F85B59C5806AF9833876EDB66C558A3EA71585DB88F62E520CFF0C3FC8208B390CBA833BCCEB5B72576CAD3F564DEFB9B5C7589CC7A4B5F5B0E857A3E90E7B87797379E47A27774172A0AAE0417CDECA89E3114837F1C3B4B614AD9E774802B4E4226C6C7A56F603C931878422600CE5EE3A44C2015287AB3665544C470AEE0AC2A31F8345B459BFA2B7900257B0A1B0DF9086352660956A54598251A347860518B8E507CCA39ABB73215A9C347499CA94C2DC24B0FD66EB9AC5DAC214B46B65694D114B3893EBA6663EE3A6AE1846B7A2398569BB4B06A1EF34584AF9CC3F4E415FAA9096B4B96B0915BF2D5808AB71CD65C890226C4BEE74DD97A91B1B6363849870DA23EF08A1CFD487C194D66FF16C96F3B02FB97D3D087B1FD1D6E91878E8380318F7B4843FBFC07C4BC1D13EDCC88E413D23BD8E73176A0B05B6D2381822474393D22585AF90351770587A6FCDEA8254D8EEC0CEF8668E30B3D24525B36AB3C75063B8A03D4 -PublicRaw = F9951AFAD2C96730335CF42103F9267A11C16E209D366603B04789378684987B655CD57B2DCBCC195B137227B9ECD3BAB0C0183B5A0667E407D7C86402D7719869CD6570B30B8C227ACB8B478320D64353CCB94809052558B1AC94C0CF498219442B988FD64F7CF6423C372F46F26791D3680817B3EBB24BFA98CA2EB22FDF48919D1595F06115C70CA050C04B17D6A91E2926677120EF24AEAF499B43542EC0C91DB4A3B7AD6A5DB646C164D70F496225A7800DD81C634C004796A79148E47A4E312F3BF56BE80C2E9D01C431767FF50496ADA127C1D286A2FC9FB98677BBF95203F63296881D5C536B39F22222C570AD921C82565E1C7C069D69BB3CD431A4CCB5F0C790C6578C1EB0185F84C706137A7052680BD8AAAC941D34A3756EA8B006F732B995612E330616BC22F9EACF4423A1DFE18B0E63BF18B24DB8E1B2D1097CB2E305A80372E08492161871D8480D55DA41ED80AEF73682873A3065472A98816846550C80608A8FA8A9342BA87FAA6105B5C8C194C7268C694D38A08AA83102239073E02581A6A604B06B0673B43CECC07A0586B65CA8EA79BE8A140CF329223434745B576BF9CB2A70709D2621359621A057A3BFFEE465E421CA1D48096E06ACD6309F40A64126B0491D03BF7272ABB1475B72A54E9A2675262AC831B00BE163235280B76D5B88101524077A76A97C7A4F2A5AA8A547F14203F8BA26D1368B23E832987696E965177BB648CC3325766A4E8935074D9C7344E031917AB0DE452092E2720D121A0E32CC9E5B2325500BD1B76E38360A36E972CEFC56E93482E47605AF9A69A6F42D89C6C21E2B0CD5E14534A75012CC905E3042BB0043CF0280185A90D245B86C7097F2C1694F2BAE0CC5C995E580343A96A2656A108769F1DC0B7717B0C4A1A635DA3DF4D248B3D29116A8B939FCC063250198982EEF436E3564881A2A2C0F94AEAD47690A9C194C56CF47868026BA8CDEBB7631E643E6B20A9F24061B1C496518B89F1A45A6C5518B742990413D9B0012F895872E4B258371123839BD209C1A004B4575567C391A6158A464B9428761D0191F4B6CE2630A6F77590B25FA894D654EB1905481FF4CC75C2A435630DB1CEE9254090FA8D11E +PrivateRaw = ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB +PublicRaw = Error = Decoded polynomial coefficients out of range PrivateRaw = 2B44523FC0A955F93A28A8A08DA078E6AD11122E26852647D8E0C6795E0577E65D8BD5D2F82C871554442FEBF6E132EC08A596619CF00A63C1F1B35B758CC37A @@ -59,3 +63,17 @@ PrivateRaw = 0A2D58EA457CF62BB3E49A2D4BED33B1F7EA92E555A987E3478C3B09A006AA240AEC427FD829DF9C628DA3750E257B15B233088B491A69F805BA8BAE5D70690C PublicRaw = 29C4868E6397D076A75E9B4D4A3645E400C41081AA6E3521DB28C3BE7B8B0E2A4EEF017944F1A82E8347E1553D78C5656C4C1DE0D6A4F18848D2EB8E1181C4289B1F24A165BEF0597295988A182CB605C1E1C2344088CD4FAB38F456524FDB931B29175671514A641C01B641D6E1940488C0893A9617EC9C3F1B6F44F051C0EB3129607230A84DBB235BB253204D3C977179AAD4196EE2B0A9C6D49B78F10563D0717DCB230942B5B2F40D34C09ECDE7325FA257E65150C0C240CB04A13EE4710D6514D81A059D2556F0295250348D937C7862342CDF265F3ABA2836F364407673EF534E97C59491F7A611D669E6458CE5E639DB35C61A81A409A352A7E411B5A73800C28B2EE55431F594D3210369B4698E570B4EAC81B0776C80C2689395429B274730E1AB91794BCEE34DF506B9EC58191B2B03853C8C2D3B61A195322909CE1B297A318445DFCCB27761021B075E20E66E3376046C516E2517764DE4B807AC2E4503471421C004C8029711A70D962B73EA69F955743FF79D0FFABBDCA4C9A24C00B84C448949981068340A984FBDDA93863519C1D6A941DC82B9E931CB3BAD96C2B5F7E5B6BC5CCD1E7159D2BB73D87668F4305AAF1775A856BDFDFAB140799995050452219B7773B1D01157E8A02CEB4BAAB24223A611542997530BD70E9193354324BA9DC91B54FBCB6C7AA68EA3C8799324F7567E23C43A35D05299E5568A8C92F43876A6B6582CB1A023A628763B85E1A714BE43B8988615EB712EA5583E1AF15144E92C71D97BAC68735575C8BC9A6131552D7CB08B8B887F163B5CA5BCB210240252BC469160183BDA0D9D8796504AC4BF31BE4F7665D0EA880F1C6F0A301036157BB6491CAC6332F5103B49293B2F5C053398721059276927BDF218A60829969844602124C6B522524B374968F0476394715EA704E6CA24D99434F245237DF5841CE571E6D5C029750572C3529E6936D167631AB71FB1225A7143AFC3C8193B8060E4F1792A533866167A51649487F79EA69A382055303B3B64E02A6C200A6AFDFC88A178BA8EF0BD8B23AE14E96B794A52BD273137828E62D94A417C26202425A3FB91D7BA4CCC20AEFD179BF3069C90109D5D8A836E8B545A612CA6E8448567A93840BAA6F80F2C4CA51CBAB81E005F3B62C57533717F58BA671999571558C3F4906808A11D18468FCA256D0C050CD9C9B7144C4F3389785B8855DB1B5C965DA83C59C30294A57933B6F08ADEFB3506958D467A4356A41A744180CED0A15DFB4A6A131D79D91D6A934CB8618191A5250D951F12A8CC60197C1D10B3E0D18C7D405F55FC126C80C391B1197FEA54C015C33DB708A3B530F79749FFE32D1543654045C44BD52FF12BBED1B2BA19326C8F9B0A1E100967263244A717768283C02CA8E6A66680E01D8149B6DD89821531B9D43A515AA324E66804EF8C1522722426E34B5169AE06C32C930A49778573FC7C62D06620F83111735028FD7A133DAC75D8708D55F7BD35194A71B1B076D73CBA438A45A5C7E7F276CA9449C588A000EC6B08813C611084A59359DC52A2A3EA360D3965AD82832C645D3A2C488C91723314AF7F3410DA9735DFA1791026481B3A4A5E921A3A0CC4701BC2BFE54B72481372722E6BAB092BF255CBC02B1C19658DF01610BBA8AAB76658727286C75090CB8EF5120196546BEE1CA9E8D80C539B86AA32C18585B461DA56FA419D7C6AC251146A16A7367D239CDDF38A7D790E51976789B029221418670739FE221FBAB5148B445BB150293E0BB9EC6835AB94757D41C5E367CA8DFBBDEF6554DC8455E2E340E48BA63578C1789C6E9AF17FCD477CD7637F99D475E8728CAF3A105374BDB3C2811D6318DFFC0FC9F760B8DA7F8474B476C25A22CA4C3A0C0D5FC0A2EAD8510820C800D668718AA65727B706B5BF30F31C8762BB269A66C1274EB988B7F4656DC9CA6321F5CF3938036C901DC9204FDE337F7D518078D457CCD866BA16B5678987C9B194CE740DE8976133CC36AF418E16C1239266383CA8219CBB39580C5165CA7C9E449A044C87E05B931FB51F9D3858EF98A64A6A2215D6616E465452993E81972F7824C0AEE61E66B358A0B7815F6B5E32671B1891879D1C00A302C719CA88EE59C6D77AB4481636E75C4361E98EE74B194D7482F7B4AF6050DCAF6412DD7107ECEEB3E17A8985EE90BC9CE7C81ADD003DC123C06CF14D + +# Expanded private keys (KATs from https://github.com/usnistgov/ACVP-Server/blob/85f8742965b2691862/gen-val/json-files/ML-KEM-keyGen-FIPS203/internalProjection.json) + +[ML-KEM-512] +PrivateRaw = EA35075D429C8E81ADA6C4BB97D78624C602FB173DFFC78E5C744FF2FAC345B55E04A3B7325A63F58B43EEF168E259910C35A0952A7ADCF43634889C98918A1CE7B62671C1968A02688160C09B7DE9978B77A557D265A40F7C79F3124247FA0C14F8A9F2C2B939470CAD5ACA5E664ADD7B5444643B559C4BF84C524A063DA7E552C9107BB0887BC22C73F76B63ABA60955A06D1CF34491275859D46FEDEA738FEC14D9920458A7C31E657549A6160480784D5C229AD88932B384A0B0A16DD6C8FCEA56B61A50E67442FB9928B4676D92A6717A564D8E939BB7957D750BB70D2B20D8445A8912BC7940489AE01584DC7D7952A686F245D09C547EF40B74C6748B318059F66F2B76C72ECBB3A64167CAE08C27368902B76DFF684A14482AFAE5837CB6838E685987FA4E3FF7CC4A36631CBA1F77915E7C580853E3C6C84859ADC8C2A15CB131E78305F4BCB4A8100AAB206EC97D14862CF5DA4D3AE2066D4C41BBA9187BECBE0809CE6AAC1FE20BCAE87714BE1542BA9053F1802B65C82909594984A186841B2BFCB3AF38100C5E685E7B9B85515C469CA50B1F799229E024B68A4A412A185677444491689957A576F5029C742DB64C3F63614B43AA23C433A1B37811CDC1184E11BB7D9C20E587A862B364EF59651259B26F8375E4510CBB12A475816A364BA72C07566D50A2B4E4503188B7B465080DB88EE663928C894367492E1617CCBF36CF844B9D17072A3178809629B4B9729CF82C9935AA9B1205AEA6631C8EE23CEE832C46E0583FAC43C5BC5131B934527740AB12877D295C72089073E6A2567B655CCB2965FAA3DECA8315815E7B6514F05BACE8A7000B548993734DE3964F2709542496122BE58A7E536CC827839693492AE88E75EA13154AB109E6BD16F4486EE1C3EA61B8FA259283B3BC2BFB589D3206A3C77521AA08C253B4E4CC8B5F87467EEA18EBD48D92604382DB0C0087A3B501066CB55EC9602D2696380A6A5DF33C05C9B01700B61D0FC169DEBC28B3108B096B24D93B8FFE67066286B0E1461265896E33A8089D8B0B81A9781700A983B28022125A4934575220325B318622F73E6FC6CBA5571D0537894AA890426B835640489AA218972180BB2534BCC477C62CC839135934F3B14CD0808A11557D331103B30F9A8C0CB0FA8F0A2A152E802E48E408087510D5114D4D2399A51530616C7E310528308176D0042710BC8344EC3D4CA810A92978BFABB516D81CAB0753CDF325AC2377A1F96EFC73C15F5AA367A1582A13651B0337C7943C1D54637669686BEBBD392511FFFC9E3A68CBEEC0CE2CF59A8D51C4DE288EB4641DF6610C82D09CDDA418ACD83F0DCA2859B27117E87981AAA8EBA47515812DA2C27ADF9C682E373D5AF294BE3104474B8D14173788965ECCD80322B6CA04240E7D150F2CD4B04066C1924039B9E4A9E06C2B55DBA2FDDABB4065CFE7EBC5AE01CD45C76374683CB1820C34A841836391B9D8C2AA22B29E7436CFCAB789B3CE8AE2700351C1165B7B4F72CC53E913E5668AE75170352A0DE68A5E3819443DB4113161A2019C4930C97011F31540B833E9A890503A7EC3F38C0D94BE3C7501C6161F39099E3CAC0139ACC7271B70D1664A36A89FA4D22857C6C15AD4C52D5C26E23B81DCDA9FD7A49980C5818888AB2538AD91F54E691B7558C63FAE433A7FAB51485989F4335E6187B65041401238AA0A5A932356207796AF2C70363034546F4615499245E1228BFF2C76674634A60C9A04E00FB276C6C00A114BF1B2C8961E740A082940CEEAAB464370BBBB3919C7421BC81C732415A711AA935A4C2C02CB5D0BCBB99CE830EDDBAE4C228E4F095E29FBC27EA2B881697A1D309D28C480C3E9691FB63480BC5C6239B6CCAA41CD52A6209038C2C887BC71C1BD514A0FAA21721A2A5B30ACB168227833A8260422C1F4815EC2ADB207389FB1B817D78FC96063434B6728E18469475DB5D712BC403D8231CF9C8926D0A94B6830881FA5678AD04499F40D5CA83479BA85A70B1196C32A68A6B7FFB40EA6FC3FF020768B91B27F653746546C5E256B14069E827C1616FC7647F8B70F8A32DB551CF715BBB315B7B9BC20FF76847CFC4AEAC23DDC1302EC928CFE40447C761143194DA1415D3D8389F61BAB41EB605729123A320BB54B3B3FBCBC787C46F354C7D7D60F8DFE3729375AEF1891C08A79DE237E39E860061D2B87926182B602639ABB65FEBAF116F6A2FCCC167A51A2E2E6F4494C58336A2E1A394111163803FE2E8519C335A6867556338EADAFA22B5FC557430560CCD693 +PublicRaw = 5B318622F73E6FC6CBA5571D0537894AA890426B835640489AA218972180BB2534BCC477C62CC839135934F3B14CD0808A11557D331103B30F9A8C0CB0FA8F0A2A152E802E48E408087510D5114D4D2399A51530616C7E310528308176D0042710BC8344EC3D4CA810A92978BFABB516D81CAB0753CDF325AC2377A1F96EFC73C15F5AA367A1582A13651B0337C7943C1D54637669686BEBBD392511FFFC9E3A68CBEEC0CE2CF59A8D51C4DE288EB4641DF6610C82D09CDDA418ACD83F0DCA2859B27117E87981AAA8EBA47515812DA2C27ADF9C682E373D5AF294BE3104474B8D14173788965ECCD80322B6CA04240E7D150F2CD4B04066C1924039B9E4A9E06C2B55DBA2FDDABB4065CFE7EBC5AE01CD45C76374683CB1820C34A841836391B9D8C2AA22B29E7436CFCAB789B3CE8AE2700351C1165B7B4F72CC53E913E5668AE75170352A0DE68A5E3819443DB4113161A2019C4930C97011F31540B833E9A890503A7EC3F38C0D94BE3C7501C6161F39099E3CAC0139ACC7271B70D1664A36A89FA4D22857C6C15AD4C52D5C26E23B81DCDA9FD7A49980C5818888AB2538AD91F54E691B7558C63FAE433A7FAB51485989F4335E6187B65041401238AA0A5A932356207796AF2C70363034546F4615499245E1228BFF2C76674634A60C9A04E00FB276C6C00A114BF1B2C8961E740A082940CEEAAB464370BBBB3919C7421BC81C732415A711AA935A4C2C02CB5D0BCBB99CE830EDDBAE4C228E4F095E29FBC27EA2B881697A1D309D28C480C3E9691FB63480BC5C6239B6CCAA41CD52A6209038C2C887BC71C1BD514A0FAA21721A2A5B30ACB168227833A8260422C1F4815EC2ADB207389FB1B817D78FC96063434B6728E18469475DB5D712BC403D8231CF9C8926D0A94B6830881FA5678AD04499F40D5CA83479BA85A70B1196C32A68A6B7FFB40EA6FC3FF020768B91B27F653746546C5E256B14069E827C1616FC7647F8B70F8A32DB551CF715BBB315B7B9BC20FF76847CFC4AEAC23DDC1302EC928CFE40447C761143194DA1415D3D8389F61BAB41EB605729123A320BB54B3B3FBCBC787C46F354C7D7D60F8DFE3729375AEF1891C08A79DE237E39E860061D + +[ML-KEM-768] +PrivateRaw = F9C23E5887010816840BC7BE9715B0C3232B94EB64B5C983A67248EAE2623893CDE2F6B4EB421F97D43F71F51804533705D0893B029ABBF5218551B4E1D98CBF3A5A27902F92A66CB4A982CDE622C4F2700BF623FD32A1251B1B067A87BBAA2EBBF03597689D6FA40063BA20FED9C841188D8754641F501B0EA78EC6B5C22B669E45F1C842F070734B409940512003C765F0626BC3764D0BB7DF0B06BE48441C609E53E6B0F8A679560A22307027844949A80B1768E884B69CCD51C3C8EA5C995FA24444103812A918F8A5A861AB974011AB7183060D5B522873A474C28F1E39C9ED434FE6237A0455BEB09C9ADDF16FF537B38EA8923BCAA64C8334D16A3C7050437B350C014561DC3B34057196915B615CEB936E7C8A1AB19F0EA342FC682F48E87C08630D1BCB668F68B35657647546B245721F8FC1AC748A70997BC4AC7B3141A518024299074A4D9198301B2A866E19BE9FA5251493481566BBBC313010C9980FC89BAA464B8CCA92871359ED925A1F6584DA3B7B0BE208A912BF6133B43205C722C585ECCC98AF8224D2286F25F1952EE63C90E41065202AB3C9752CB242FF6A26F071572DF49763C357606127A4048E06E1BBCE1504AEBA1692E62312993B51785820A33E0D62411D187E3738761B601CF5C0C710A6C6FC028036189C011B0EDA97BBD21C9F92744D0D89674003C3BB563F93D7B027C79C0B6718CFB14B80A8934111C644A9A63C30B7CA11A2279B478C8B84470CB11F74A3DDD54D8D395FBE2A54298671BCB08DCA21502B694591B56AE87453B7450DAB8648ABF7AC94A646A9DBAEB82623B92133C94C7A36235B9A874112BB02F1F1B80F02BE5AE6A0370165E854A1ACC74FCAE75CB117255CE08BCF24CB64467E4704A6715A4C5ADBC3BA876CB5B6A34BC3CFE43718F43A04A90C9BAB88CD46EC75708950CC1C1D31FA8DF9BA034DC14FA64A2DEBA28E039622DB241159F05A4815AD8FB774CB807B0667B1BBC9B8CBD8CEB8518C5FD87E16659D26963CB5074AC77046C5053CB4182DAE65037E0C601AD37E25E22DF4A4814EC49CDB21148975B9AADBC290FC0546D2A2F3FB461FB130A457BE4276166622748FDB30E43AB426418E135660BF4A2588653AC2365E63445670CA9CE5E7AFE3378555C29F43DA3C0326012FFC372F356812BC79F77877E415662D788EDAE10E34708FD42808D8BAC0D5B5148A27A41039496CB47C81C5B17A727BAE2057AA33080DD0A71D6718354570CC363AD33381E16B8AC645747ED92B3A16105148069339240EA8118C2250F62691E4D62A3DBC3335019F1BD22AB8F608A8F2AC26DCB483732AFB4190BEB41AB30A3A987251FFC449C90BCB0EC32D2AEC764FE63C19A39A5EF8A4ADA206304267C0A80663A184374A811F903DD08C29AA74C9901A6D0C35506E26B8A8859535DC230DC87236C6A8BAA6CF68A24ABC6565E2A3CE31295916AA55B2C5A15F34A191407430771B8E9318A1688FEC495F6742658646748E8A356CEC16D1A48C1A23747F758EA74BBC382A3A98864AB71055CFB60448A941A374AAC44756047AB40644CDD3987580476253B98033648554E336DCB954D831781B892058DB9E66FA796464412954C0C4308CB02B4C7A380D1B43CC2B343B01C6655DE002A38CBCB0229DB481BC50C9B6D1DC3A86742AEFAA867AB16122F83726A71446E7B2ABB2CA04FA2FF07A4910B16A73A28B66487483861A1A0A4DC86330F5051E3831631A334EB58B42295C7E4292CB43E8A53A435039A44D74C15598707F46947D45002B80197AD2E28C4FBC1F5CB53E71D808D9D38F52EA7C07A5625959A64B5A81574907337BB43BC283463C557031AF36F08EC20B69AB286258564809EB072AEB0DC69028FFE1407706314BF8538B02778F5921ED40C87102467F5A4580D13B02E508B4140764A3C3265461A7B90A959C028048C723B673F1286B50E70C8AF8730A674EB8F95029D181C8F478B843AA89FA55180B9AC6D3AE6FD75AB356028FB923ED329D38065662D836964811AECA796B1A862905681A39274EBA7AFEFBBA2561A8241847CA38B309003A13433102868882520F17A0049000817B9148DF851D67E09078B6A1C1EA91CE5B7F1D2B2B1C07884DCB07DB46691F9869E8B58323D4695B9073CDE5717FF8C2DC38CEEFBC24FE2630E4790583EC671AD59F6C049AF994659BD075752A4EE7A63D7CDA02E65C6507FA2CEC73B0B6A346A67C9FF5D345169A5FF5390FE2B5C264E9619C24916A271D7838227004C57BF84ED22A3C442895DAD975A96CC5FC949647484EB0872BF55A625353C09A658E1E806B26A51720C4AF3D5CCF739C51A6D744BB103BA67B48AA716CEEA77784C7093646716BF26B1BFB54D2FA114594B7174C567A0C48ADD1236EE8888C551533C7862A3B24D15568E2061C0B41232319476753AE9ECC883D7BB44BD3776ADC95C587C8D5A24D76B787C92CA8A2FB64241442EC751995494A91114D0003212B9C747C1B6D18278318CA979029B637D7A0C2F2345B3BC0BD1B29DD457FE9B8629880B519CC47431066C6AC1687C60B8F961E1547C6DAE75B43104B35801DC0B032FAD18D6B901255E32F09981458F43BC2C63B8C262FFB947304E13B1A05BC883A5106A32E99CB6E651447D1934724F54C0EB37CC019C37228A4537B96259A825A741DE4882737184022E529E803457013123BD925921943706C8BA2C2CF09D68E34753CB6358DF1ACA9A6CC322755333E50050FA207C1EBB1E1142B009C73004AC7BBF20A362A8DF73677767388B3DB57BD278999D99D69DBA055E44051DAC2DD5138255833BE91C4C1054FB3B0994F58A7F6DC3AF92CB9627943237419A8045828E7A62CBC4B15511AE0E2A31E1BAF171A84B7F4BA19F22609B528D3271587A673A7931E0E0678CB017E026426B6770C8AE903C49B79F9219A83B02595D3A7A40B1D7DAA33FCB8760A72BD02B96B2A20513C4A5F9AC772C64A03FE86C80E601D2A1BBDD707B10F71259FC025FAA6B129E81FE5604386269F64E5CC8840A4C3A721DCC819FDE555B8654992EC055131BC69B2CDD19A9343B63E8D098473FC39074C97D899728904A4F539C235E4BBED4024771056E1772FD14503149048D638537F2AA5DD339C4DA15F8E3A05FE0533CCB0167CB74AF29B88EC0C961B164B81626ECAF6ABD876B8B8253BEFEC3163B039088365112422EFB4B3A7E17695AB36CECC06F19AA31104817BC3181BE78DD30C4AD5F7684D142354AA5EC322CEF3AB3A8E906088D2939A35349164DF25688C015D345F067D4A9B077FBF18F7561CC71D409D21A3038F1EADD46F165E47B736ED73D7F7FE2AA083A10A9C267D3163D0FB888C9D2D7614482095EF1817E1E9E4AC88 +PublicRaw = C4308CB02B4C7A380D1B43CC2B343B01C6655DE002A38CBCB0229DB481BC50C9B6D1DC3A86742AEFAA867AB16122F83726A71446E7B2ABB2CA04FA2FF07A4910B16A73A28B66487483861A1A0A4DC86330F5051E3831631A334EB58B42295C7E4292CB43E8A53A435039A44D74C15598707F46947D45002B80197AD2E28C4FBC1F5CB53E71D808D9D38F52EA7C07A5625959A64B5A81574907337BB43BC283463C557031AF36F08EC20B69AB286258564809EB072AEB0DC69028FFE1407706314BF8538B02778F5921ED40C87102467F5A4580D13B02E508B4140764A3C3265461A7B90A959C028048C723B673F1286B50E70C8AF8730A674EB8F95029D181C8F478B843AA89FA55180B9AC6D3AE6FD75AB356028FB923ED329D38065662D836964811AECA796B1A862905681A39274EBA7AFEFBBA2561A8241847CA38B309003A13433102868882520F17A0049000817B9148DF851D67E09078B6A1C1EA91CE5B7F1D2B2B1C07884DCB07DB46691F9869E8B58323D4695B9073CDE5717FF8C2DC38CEEFBC24FE2630E4790583EC671AD59F6C049AF994659BD075752A4EE7A63D7CDA02E65C6507FA2CEC73B0B6A346A67C9FF5D345169A5FF5390FE2B5C264E9619C24916A271D7838227004C57BF84ED22A3C442895DAD975A96CC5FC949647484EB0872BF55A625353C09A658E1E806B26A51720C4AF3D5CCF739C51A6D744BB103BA67B48AA716CEEA77784C7093646716BF26B1BFB54D2FA114594B7174C567A0C48ADD1236EE8888C551533C7862A3B24D15568E2061C0B41232319476753AE9ECC883D7BB44BD3776ADC95C587C8D5A24D76B787C92CA8A2FB64241442EC751995494A91114D0003212B9C747C1B6D18278318CA979029B637D7A0C2F2345B3BC0BD1B29DD457FE9B8629880B519CC47431066C6AC1687C60B8F961E1547C6DAE75B43104B35801DC0B032FAD18D6B901255E32F09981458F43BC2C63B8C262FFB947304E13B1A05BC883A5106A32E99CB6E651447D1934724F54C0EB37CC019C37228A4537B96259A825A741DE4882737184022E529E803457013123BD925921943706C8BA2C2CF09D68E34753CB6358DF1ACA9A6CC322755333E50050FA207C1EBB1E1142B009C73004AC7BBF20A362A8DF73677767388B3DB57BD278999D99D69DBA055E44051DAC2DD5138255833BE91C4C1054FB3B0994F58A7F6DC3AF92CB9627943237419A8045828E7A62CBC4B15511AE0E2A31E1BAF171A84B7F4BA19F22609B528D3271587A673A7931E0E0678CB017E026426B6770C8AE903C49B79F9219A83B02595D3A7A40B1D7DAA33FCB8760A72BD02B96B2A20513C4A5F9AC772C64A03FE86C80E601D2A1BBDD707B10F71259FC025FAA6B129E81FE5604386269F64E5CC8840A4C3A721DCC819FDE555B8654992EC055131BC69B2CDD19A9343B63E8D098473FC39074C97D899728904A4F539C235E4BBED4024771056E1772FD14503149048D638537F2AA5DD339C4DA15F8E3A05FE0533CCB0167CB74AF29B88EC0C961B164B81626ECAF6ABD876B8B8253BEFEC3163B039088365112422EFB4B3A7E17695AB36CECC06F19AA31104817BC3181BE78DD30C4AD5F7684D142354AA5EC322CEF3AB3A8E906088D2939A35349164DF25688C015D + +[ML-KEM-1024] +PrivateRaw = CE109720FA07BE01AC762187DD00BA90418D9F567832300D98A27C7A294FAFA267822AAAFED4463200662BFABA61EBBBA77B4D4FB628BC15AB1C458707787A9A7CAE5984A3E141B2A9106F2C101278379E81F218CD89A8E7A62D6886281B5925F5F0C39642A54DD391F2E0357369202CD9839FF0A6DB547E832A0EAE4B32468C109CFA543F8C7A61D69D8D170EACBA1439628B6093A1B5051471B3C8B229A8D6AA4A5D51255726035F8B1518A0A647CB1F1BB88E9D522ABD02BFF375552084039F3A0CE4740A690917636A2CFE32BBC4F339529ACDD1B28E1987B764E7A4B1C3C782225C1C30CE91551642657DF378A63CB56A1C471D955B33C23203CC2B95644787E313B87162B8716BB3D2053E70969A50B5175D010DF1482F0628BD58320C62502AFDE3200F01169288B3A57925932BC7DD31CC26480B98BCB438CA319D038939038A6E69CA1193C56843367B13396E859FE08169EF8925740754F8226D1310ABE54400B9B24127FA46208AC875C3C1685B9CA5496672966A3E681260E30CC529041C38C9E5E07610BC237EA8AB5C9873714C22E77A092AEC4860FB4543DB394F161AE456B3BEC89622532DAFAC16CF059E66D38C3BD24568277E83782CCDBB2067C15418B8AAB5F41F581CB7EFA7BA114317AF346959411DA2F44D65C264AFE028285B7865C879ACF9280F6C60A3B71752B142E39C9BDA400E7A46170789CAA8F296EC3594A0B3C50644764BE6A18B4057B4B2764735CE41DC13C551AD9D421205633A7D3A916CFABA3FF5390A55998ED3C2D0C9376B09A7C66B2AECC52EFB551263710F590524E9977B1D8749C659CF4CAA195A31B7309401CB209C15C00A47CC8AC8997DCD47C6CDF13E156814650568C276CDF3C4CF6F3417307B420F6C694BA2021BD82D242A7325842F1AF4991BE4333853A45DF31B379604584B45CE5A4E127BCDB01977A301808BD83408DB8E69E1B7B875BB5EF86EDA606870A81E71362686791A954603122B736FD1AC6A247B1D966194A955C444B92A2BB4C1CB7B5AE3C79DF81EB031B7CDBC0359F04782E08FA1E654091AABB8C89F7F8043B28C4BE0681754A7B255A0CFC7342B431A0CC6AA24A88A43B0EC5860651D6E252E3D314BEAD1807A0A137234B8FC0A52E7FA870D936B42B23EF0B76224EA461A661ACCF5B615545B56AC23CE6A579E5841B8CB69E7798F9E7A4CC51CC688692B2710C6E1751AB8628DFAAA211D4B1D002BAD7E241E1259588DF7457B8293230C0C61EA1812B7375D23A6E4A80B872624EA0C7FB2841E4B572EED9BA182EB8B4617666C172E504109E317B23A0B839C4B6E530A05F8846A6A914DFFDA14A2C555D23C1EA45379C270B2023785C927132C8BBA7AA05A1361B34AF1CDEF186F5D3B5FC022A2DC2B3845A0B972E52636267E212C884957B885D6C9B50903200863B45716863924F3C5CC382A9FD7410E2C27BD80549DB8E439F81966EE418A55E5C2DC1A90215507382A976C649CF9061612A43D14B227F918ABD10AB0D06C285F1574E6157F95598FD86453A12320CB8CA9B7B408A4A83B872C9180C9BD5F786F4F94B2F806AE5A8B55A8470469F7A21EEA9972143D872897F86C50998B2AB83B9AA6554B2764CFA2F04C9BB2100B7092C56CBC293BC78DA66598EC4FEF95C31FD69AE74407E9A655CC012F3007A422A0BAD01C564D8355207128E1FCA59C9AB7F0298EAD2301633AC590F0545162B744B7C01C98AB2E05B92289044FDB42619099A6E373F9E871C4665526DA69F067C643F88F779B2D6D30BEE00470E695A4EEE221E0622E11A30E4FDACEFBB9120CABC298EB2E4B3A93E2D09EB88444F50458245BA2CFA247C77A451DB9842397442D34B6D6AC3D4E40BD04E910E0338FE4586F0779CAB721C713D9B19AB696A224AE3645AF8BC5BE819848DB80B133418F79617F0D994689F847BD8B768E94999FCA867621402CDB575C25A3D9F647BCF4614CD7ABFBAA1CF3B9C732D30603D8B101644E620C18A08711151A6AEB69BD0EAC7961F42A4B649257912835F9BBC4079EC7C7C5CBB2B0895C3F075A969F84C179396470B18EA32397F04C63E4F89F6F14CA23F503D207AE91E891FDE8ADEA7418C5B70B2DA2B226D06E8CCC7C090CB2D7B8A1F16885F0CB3694972D0C14839DBBCE8859395F916E37E747BC9A5798764BD7C7A6B2071A7D5797A33816CC941EEB77A62049CA1E3A20A6012198D0903CD5CA9D813A994C639EE1952AA70CCBF78AE1FB104232102C987069E7583C73C427C8812F467F0C7A27F4799343471285BCAA939A38243007BB1C62ACDB42A8E85F7F63CA74F31ACBC6BDCF949FAFFC6C3DF4A9B6603CF95563A26921D056B8CA0A8CF8A649143728A9E3AF24AA3787663480EC2FD7A485F90300B672B9718B8A881AB606456893A105CE518C61202069E4924A5A6CE0508B54A37E591915A0544B67BBC4E0F0225D3A7F3BA48F92CC91D7802485E3BC0A408A3C123A069518C8D304D47B96D9AB80FEB50322F2887AC733097A06F1327F727399ECF17F0E2B76BF852E4B2464FBB8424D4629F8644B5F787A16FA5D24D295E0CA707AF6417EB4782CD8337215322C82C645002E12EB57008054F3528F1906260F692D5985ADCFE45BB59CC2143CC986D5975773150718C7CDEA20F4D8BB70584597B0C67E6C0A7A221DBB4C98A99296E9A458800022350C3086949E50784E8CAC90AB901583B45253472B752213892888DD8C6937C856B829C5EB904FA51BACAD81B2273062CEF29EF3FA76E1B56E1BD9142E27C360384C46C687A340BA78750230423393EC245DF58A3077247E6383E3D3AAEE14090A8BAF3E6912699954FA50AAD5E158809355A8E9C7AFE49497E7ABA193275ED1C0B1A5A31C28B5EEC20C38681F4EE049778BAEADF6586863B5EFE35E1349CE6199A58EBA7EB333163F441006A05E39D3BDEB8568F6797F6E724D9358004DE76A843CADB3C939F4961A3EF9A13530B85A7A9C7D23372615BDF827C2D9E57A2AB867E2CB77F7C5023D64BA3DA96C2A1AB55B891835C10B6A848FAB88B311E434BF28766973BFC4B00702F07CD8D35985EB0ADBBA21F0D79C6070361F9212EC5A5EDC0C606E17CB82F1B36F8025C682752E3307EA60347E1644AAA2C0D6F79015516EE729AA44CA64209078DD3A05B8D24760A265CE630E7875178127268053912B2405B7E63F5082A10832A922D2973C9AB0BFF1323B443C66F8338296C77715883FDAC2C373043A878722CA01A701215A7A8ED0BBBF67E95299F70E4E7B897AE45FDA1543373C83CAF08CFF8C4D9E1B2601D773AE3956333454806446C7D63ABD32B9347719848C873DA9989D845884A9A7562326D2109AE06496EB790B2139C6337C8667CBBAF3040AD73C4F2A7A2DEF3B1260174BFD4106092B4C1215A4D52425633880B396721FD0ADA23A991BE1A3E1B7ACF6E3A6A0B23A5FA5C2C40A5D9B252E0E3C8E923063EF2643BA655A95C32518F3BACDB612764889C7E71C006A389E5B80C55A1C93771D009177A6F5BC1D304F8095A672276D64B78D2816AA49080988568593F337A13B5128ABAFF0D70A6330631B96864005459F8B2824FC5DF7408E4B8A2B09628E4059CBAEC95CC984CD5DB8BAF584BD7695487BD220A49307B05A87D06713254900D26B6E384B455A8C2A41A9ADED866E5235960990B19DF6AF36458B95575341B782C0DCB3DCA940A207189AB672BED83720B65ED6B47DD335898F3C55C7CCB6CA03A556932AC6AA15F0A40CBBA14C66948390F8054D33970FEC82F8F370193A2F15EA8CBB9ABD97A31F10085AE1AC5E80712F277A5D6B7127B04798CEC91FE2B0B1858A0E904236007951D184503AB4665FE72B835216E13C8DB81A68818583D293B1CEE09F80705A1FE29C4BA89563E01B313450598409927779626A874E7103F34C758D204F609CC8C85A1FF94187D8A6783B2B206AA9821A1197F223AE770572157069F0E221C7365F72300CCD798746544FD2D989BF10C71647106386B88F999E26727ADBC52E92323F724552E810C1ADFB8CDFA132DD79507CA017D4B0B81118C758CA49F20871C54161E49C7E1E8A482EE53FAB584DB1E3258744A8B4664B7C06AF752B949D681B37B6C57F9C32B0F49A839476F4122989A761D9E3236EB3B63C0CA395A9391B5B1A8BEA76CFD7A267E7574AD938C918364330C1D7F45CAE5A8327C892C075B2D0546C8C1B391B55A4EE013E0F600BA5043C242B1735EB5449494F9E9BBB2EE7C741DACC97C08AD4ACBC2A6B40C36847547A4FF29854248484CB9CC17D98941F544441F0B674898B21873785FC204829B3A8074BE8113160D0CB0AEEEE9B7795B6659669BD3311A722DDA728AC095933A8B130FE1F45233005F73159381C4C8B10F8536837D1931B6D40C9FA53D4118391600FDD18126AC926D3A745172186BEB557CE6F0D7911A5C9EE387689C799E912 +PublicRaw = 8859395F916E37E747BC9A5798764BD7C7A6B2071A7D5797A33816CC941EEB77A62049CA1E3A20A6012198D0903CD5CA9D813A994C639EE1952AA70CCBF78AE1FB104232102C987069E7583C73C427C8812F467F0C7A27F4799343471285BCAA939A38243007BB1C62ACDB42A8E85F7F63CA74F31ACBC6BDCF949FAFFC6C3DF4A9B6603CF95563A26921D056B8CA0A8CF8A649143728A9E3AF24AA3787663480EC2FD7A485F90300B672B9718B8A881AB606456893A105CE518C61202069E4924A5A6CE0508B54A37E591915A0544B67BBC4E0F0225D3A7F3BA48F92CC91D7802485E3BC0A408A3C123A069518C8D304D47B96D9AB80FEB50322F2887AC733097A06F1327F727399ECF17F0E2B76BF852E4B2464FBB8424D4629F8644B5F787A16FA5D24D295E0CA707AF6417EB4782CD8337215322C82C645002E12EB57008054F3528F1906260F692D5985ADCFE45BB59CC2143CC986D5975773150718C7CDEA20F4D8BB70584597B0C67E6C0A7A221DBB4C98A99296E9A458800022350C3086949E50784E8CAC90AB901583B45253472B752213892888DD8C6937C856B829C5EB904FA51BACAD81B2273062CEF29EF3FA76E1B56E1BD9142E27C360384C46C687A340BA78750230423393EC245DF58A3077247E6383E3D3AAEE14090A8BAF3E6912699954FA50AAD5E158809355A8E9C7AFE49497E7ABA193275ED1C0B1A5A31C28B5EEC20C38681F4EE049778BAEADF6586863B5EFE35E1349CE6199A58EBA7EB333163F441006A05E39D3BDEB8568F6797F6E724D9358004DE76A843CADB3C939F4961A3EF9A13530B85A7A9C7D23372615BDF827C2D9E57A2AB867E2CB77F7C5023D64BA3DA96C2A1AB55B891835C10B6A848FAB88B311E434BF28766973BFC4B00702F07CD8D35985EB0ADBBA21F0D79C6070361F9212EC5A5EDC0C606E17CB82F1B36F8025C682752E3307EA60347E1644AAA2C0D6F79015516EE729AA44CA64209078DD3A05B8D24760A265CE630E7875178127268053912B2405B7E63F5082A10832A922D2973C9AB0BFF1323B443C66F8338296C77715883FDAC2C373043A878722CA01A701215A7A8ED0BBBF67E95299F70E4E7B897AE45FDA1543373C83CAF08CFF8C4D9E1B2601D773AE3956333454806446C7D63ABD32B9347719848C873DA9989D845884A9A7562326D2109AE06496EB790B2139C6337C8667CBBAF3040AD73C4F2A7A2DEF3B1260174BFD4106092B4C1215A4D52425633880B396721FD0ADA23A991BE1A3E1B7ACF6E3A6A0B23A5FA5C2C40A5D9B252E0E3C8E923063EF2643BA655A95C32518F3BACDB612764889C7E71C006A389E5B80C55A1C93771D009177A6F5BC1D304F8095A672276D64B78D2816AA49080988568593F337A13B5128ABAFF0D70A6330631B96864005459F8B2824FC5DF7408E4B8A2B09628E4059CBAEC95CC984CD5DB8BAF584BD7695487BD220A49307B05A87D06713254900D26B6E384B455A8C2A41A9ADED866E5235960990B19DF6AF36458B95575341B782C0DCB3DCA940A207189AB672BED83720B65ED6B47DD335898F3C55C7CCB6CA03A556932AC6AA15F0A40CBBA14C66948390F8054D33970FEC82F8F370193A2F15EA8CBB9ABD97A31F10085AE1AC5E80712F277A5D6B7127B04798CEC91FE2B0B1858A0E904236007951D184503AB4665FE72B835216E13C8DB81A68818583D293B1CEE09F80705A1FE29C4BA89563E01B313450598409927779626A874E7103F34C758D204F609CC8C85A1FF94187D8A6783B2B206AA9821A1197F223AE770572157069F0E221C7365F72300CCD798746544FD2D989BF10C71647106386B88F999E26727ADBC52E92323F724552E810C1ADFB8CDFA132DD79507CA017D4B0B81118C758CA49F20871C54161E49C7E1E8A482EE53FAB584DB1E3258744A8B4664B7C06AF752B949D681B37B6C57F9C32B0F49A839476F4122989A761D9E3236EB3B63C0CA395A9391B5B1A8BEA76CFD7A267E7574AD938C918364330C1D7F45CAE5A8327C892C075B2D0546C8C1B391B55A4EE013E0F600BA5043C242B1735EB5449494F9E9BBB2EE7C741DACC97C08AD4ACBC2A6B40C36847547A4FF29854248484CB9CC17D98941F544441F0B674898B21873785FC204829B3A8074BE8113160D0CB0AEEEE9B7795B6659669BD3311A722DDA728AC095933A8 diff -Nru botan3-3.7.1+dfsg/src/tests/data/pubkey/kyber_kat.vec botan3-3.12.0+dfsg/src/tests/data/pubkey/kyber_kat.vec --- botan3-3.7.1+dfsg/src/tests/data/pubkey/kyber_kat.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/pubkey/kyber_kat.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,4 +1,4 @@ -# This file was auto-generated from the reference implemention's KATs +# This file was auto-generated from the reference implementation's KATs # See src/scripts/dev_tools/gen_kyber_kat.py [Kyber-512-r3] diff -Nru botan3-3.7.1+dfsg/src/tests/data/pubkey/rsa_verify.vec botan3-3.12.0+dfsg/src/tests/data/pubkey/rsa_verify.vec --- botan3-3.7.1+dfsg/src/tests/data/pubkey/rsa_verify.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/pubkey/rsa_verify.vec 2026-05-07 01:38:28.000000000 +0000 @@ -569,12 +569,24 @@ Msg = 6162636462636465636465666465666765666768666768696768696A68696A6B696A6B6C6A6B6C6D6B6C6D6E6C6D6E6F6D6E6F706E6F70716F70717270717273 Signature = 2486dd61e560e661511db92f41045a87cbbb78ce577d28da533bc15fdf9cbc2748311a5faa6501270b46414ba3549de34160c1ef18eff339eeeae2c53f7ed4a5fddc19c5b3f5c391e8efb5548555d478f0698ec351f6a4974c9c74f0a0eba9fc03db9253f41f02ffc5f03cb9d1973946993aa3f831aa1d9e73a783e67bf7695d +# From ISO 9697-2 Appendix D.1.2.3 +E = 3 +N = 0xFAA8ED34EEF1CE38D29814B6EEAA154DC060BB37EB1A51E8AB0398DDADDFD334CB9BE20C087B1DDF1F78A39762B5F20A7A73008630913CD2EE60183DE249DD169CA4EB3AE0420E5113D730504A73A926BEFBFF32C89858DE5E5B3899FEC5252104933163625F29635AB8FAA7AA14C4F3C0DD2470DEFCEB392429110A0149A771 +Msg = +Signature = F9DD9F72FAB4AFFCED3B0538C5848B27756AC50CB2890F4CBC268D96C5E91EE88E3B058F2EF6585FEF5323CA4E2C308CC6140CF5F53579605B3BF0CC621082EB77F4A42D3567355EAA151FB4652BAFFE58A4B3107A064669FD4177C8D79F5DE5EEC562FFA2D0F5D9C409AEA0D5B9F8DF493AF2F18F91D828CE32C4CC35C13113 + [ISO_9796_DS2(RIPEMD-160,exp)] E = 17 N = 125242242467304226980818040029626771449089399969616333381049941622953718673240322529328207020354780888067722576207206966012991943446137640922660671107037754599453565985942582513009492907982173446675216454634592761000191710251638590123948630732326307922952494464857505415177402322499891218582307842351942219477 Msg = FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA98 Signature = 3d853e02ccea35ac803227458aaf5c964387e20390a476419e853ed415b3ad2ad750d19b4e4667597c1863ea2b0aa35fdbb4de589c4663583674e2c8d15d07daa54ff389ae96d78cceb2b5a50b649362357042b2c40d780361b7f6f089c7e27e92d21db1b3e3d368582e3dfdcf0312f727743c09c5c2cb3c0552b78db71be278 +# From ISO 9697-2 Appendix D.1.2.2 +E = 3 +N = 0xFAA8ED34EEF1CE38D29814B6EEAA154DC060BB37EB1A51E8AB0398DDADDFD334CB9BE20C087B1DDF1F78A39762B5F20A7A73008630913CD2EE60183DE249DD169CA4EB3AE0420E5113D730504A73A926BEFBFF32C89858DE5E5B3899FEC5252104933163625F29635AB8FAA7AA14C4F3C0DD2470DEFCEB392429110A0149A771 +Msg = FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210 +Signature = A4958BADDA6AB0F5E7F544BB1313DB93BB7336053678459A31386D3A9F0A477F37B853DF6BBBA87BECAC7CD2B19FFACD98B40E820B638D5F7DDAAE56FF198EF6AB1002C376C1FFDE03041201FF8E6AF94AFDF05606E10E32F3F6909134864AEBD983AAA2BD725FCCA288DECE27810D34807956DC78F3CFC4EA45A8DFADA4226C + [ISO_9796_DS3(RIPEMD-160,imp)] E = 17 N = 125242242467304226980818040029626771449089399969616333381049941622953718673240322529328207020354780888067722576207206966012991943446137640922660671107037754599453565985942582513009492907982173446675216454634592761000191710251638590123948630732326307922952494464857505415177402322499891218582307842351942219477 diff -Nru botan3-3.7.1+dfsg/src/tests/data/pubkey/sm2_invalid.vec botan3-3.12.0+dfsg/src/tests/data/pubkey/sm2_invalid.vec --- botan3-3.7.1+dfsg/src/tests/data/pubkey/sm2_invalid.vec 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/pubkey/sm2_invalid.vec 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,60 @@ + +Key = 8C84F7F069CD09D59543ED980CFEB77E68C7D39B9B73D359EA67C0CDB2A86B6F + + +# Empty +Ctext = + +# Just the SEQUENCE marker +Ctext = 30 + +# Truncated +Ctext = 3072022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF04202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E51 + +# C3 MAC too short +Ctext = 3071022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF041F2E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F36826493860409CA311F43DB0E5E516F + +# C3 MAC too long +Ctext = 3073022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF04212E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F368264938696000409CA311F43DB0E5E516F + +# C3 and C2 fields swapped +Ctext = 3072022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF0409CA311F43DB0E5E516F04202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F368264938696 + +# C3 MAC last bit flipped +Ctext = 3072022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF04202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386970409CA311F43DB0E5E516F + +# C3 MAC first byte inverted +Ctext = 3072022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF0420D11B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E516F + +# C1 y off by one +Ctext = 3072022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27D004202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E516F + +# C1 x/y serialization boundary shift +Ctext = 3073022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E500200022100C055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E2704202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E516F + +# C1 is generator point +Ctext = 3072022032C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7022100BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A004202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E516F + +# C1 is origin (0,0) +Ctext = 303302010002010004202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E516F + +# C1 x equals field prime +Ctext = 3072022100FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF04202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E516F + +# C2 last bit flipped +Ctext = 3072022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF04202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E516E + +# C2 replaced with empty +Ctext = 3069022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF04202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960400 + +# Trailing byte after SEQUENCE +Ctext = 3072022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF04202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E516F00 + +# SEQUENCE with non-minimal long-form length +Ctext = 308172022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF04202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E516F + +# INTEGER x1 with non-minimal leading zero +Ctext = 307302220000A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF04202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E516F + +# OCTET STRING C3 with non-minimal long-form length +Ctext = 3073022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF0481202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E516F diff -Nru botan3-3.7.1+dfsg/src/tests/data/pubkey/workfactor.vec botan3-3.12.0+dfsg/src/tests/data/pubkey/workfactor.vec --- botan3-3.7.1+dfsg/src/tests/data/pubkey/workfactor.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/pubkey/workfactor.vec 2026-05-07 01:38:28.000000000 +0000 @@ -37,13 +37,28 @@ Workfactor = 224 ParamSize = 2048 -Workfactor = 256 +Workfactor = 224 ParamSize = 3072 -Workfactor = 384 +Workfactor = 256 + +ParamSize = 4090 +Workfactor = 304 ParamSize = 4096 -Workfactor = 384 +Workfactor = 304 + +ParamSize = 4097 +Workfactor = 352 + +ParamSize = 6144 +Workfactor = 352 + +ParamSize = 6145 +Workfactor = 400 ParamSize = 8192 +Workfactor = 400 + +ParamSize = 8193 Workfactor = 512 diff -Nru botan3-3.7.1+dfsg/src/tests/data/roughtime/roughtime_response.vec botan3-3.12.0+dfsg/src/tests/data/roughtime/roughtime_response.vec --- botan3-3.7.1+dfsg/src/tests/data/roughtime/roughtime_response.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/roughtime/roughtime_response.vec 2026-05-07 01:38:28.000000000 +0000 @@ -52,14 +52,14 @@ RadiusMicroSeconds = 1000000 Response = 050000004000000040000000A40000003C01000053494700504154485352455043455254494E445889AD80EC2EF7E507FD68E4B88F6DB48020807749BCAE886FE8221D31B5EC070FEB25E401FD746D9C2C995B354FFED67F8FC05B56F4844502F632EAD44FD7140E03000000040000000C000000524144494D494450524F4F5440420F0038596D5D678205008DC2277E99668AF765D3D7372D915B904AD6CEB0BAA4262E194C894C0634936DA2CCD92ADBA30FF286ADF5EBF68A5E7BE43559A6226BD3500DDA16083C11C2A202000000400000005349470044454C45A82675D99316586079BDDF8965030CEA112DAAC1D9EADE089CFB7B7C9ABF7D9F87FFDFBB958FCBAC5BE082FBFC110E8B75E11ECEA3DD6D91348AFADCC045260A0300000020000000280000005055424B4D494E544D41585468B3FAF25B844DE2860ECC833283DEBE12A2852E195758B03AA8E39B5247F35E30DFFF4B5E820500303FD7697282050000000000 -# Merkle tree 2 leafs +# Merkle tree 2 leaves Nonce = 8FEBCFDCB149EA09E96405547F5360A2C243169F3243B8BA16B962CC94EF62E5E1A619EA25A18B8F324A63B85B615285A17065BE94592D8C1FDF3FAFF279A6E8 Pubkey = 6f79ced1b4d650a7bb23325b68a9866da2cf33ffd89f073933a5a6107d55aca1 MidpointMicroSeconds = 1550830411384409 RadiusMicroSeconds = 1000000 Response = 050000004000000080000000E40000007C01000053494700504154485352455043455254494E4458357CD0583B97ADFFB7AB5EFD932661366EEFD6AB5B204FAB2CBD9F1CFBE2314FD953CCC66E280307BB30C004A838202EE12BBAC5CA6BE0D179C13441072A8102FD2915F34A58CB40516E2731B2884051482D829F7A996EC3D1BF7367CE40AB8EF28C8BD6A04063F0BE45FBBBF3C79332CCE35E82E783800B6B1E814ECE7C815903000000040000000C000000524144494D494450524F4F5440420F005906C7D778820500FB6AF62DD67A7EFA90E64E26DE4B93FB6A3455DA1C73ABE30CFA6FA0BCAD1E56F4C133BAE1FE959EECFF6F674A8FFC95148E723C8D08510C2394326A3C595D9202000000400000005349470044454C457865CA05DFFED083CF78A50A24A7D00E7AC2AC27449C9A8CFDA496DBE8ACC74C2C7B766449CBD085AE88C40627EAE8D0F9D07EC30F4D8F37ABB17842ABDC380A0300000020000000280000005055424B4D494E544D415854A9600D58ECE49A48410ABB4F20FDA858ADF5BE3CCCFF09B4ED028DC76A5956560000000000000000FFFFFFFFFFFFFFFF01000000 -# Merkle tree 4 leafs +# Merkle tree 4 leaves Nonce = 92DD76EC9302DA565A14D4B7C71AF005DBB51DFE50A931E4BF25925BB9667066E4E593EB8332A57F4CFA14074210AAC5D43A9E4CC13FE5889E9DE0C428AA9D5D Pubkey = 6f79ced1b4d650a7bb23325b68a9866da2cf33ffd89f073933a5a6107d55aca1 MidpointMicroSeconds = 1550830502590635 diff -Nru botan3-3.7.1+dfsg/src/tests/data/stream/chacha.vec botan3-3.12.0+dfsg/src/tests/data/stream/chacha.vec --- botan3-3.7.1+dfsg/src/tests/data/stream/chacha.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/stream/chacha.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,5 @@ -#test cpuid avx512 avx2 sse2 +#test cpuid avx512 avx2 sse2 neon altivec lsx simd128 [ChaCha(8)] diff -Nru botan3-3.7.1+dfsg/src/tests/data/stream/ctr.vec botan3-3.12.0+dfsg/src/tests/data/stream/ctr.vec --- botan3-3.7.1+dfsg/src/tests/data/stream/ctr.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/stream/ctr.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,3 +1,6 @@ + +#test cpuid avx2 + [CTR-BE(DES)] Key = 0123456789ABCDEF Nonce = 1234567890ABCDEF @@ -285,6 +288,10 @@ Nonce = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF Out = 8AF2860142F786F409307C1A3F7EAAAC597D5761063D8BAD232CB0136888AABB90B8CF63F44412CEEE802A522AB6566313C5E10652749056AD2F02CE3BBF5BEC280D53A3DEB427CBD72262F24668144D91A7FDA59E051F2863443EF463514587B7BE1DBF1D6AA496AD0BDA0B040158A22CABBA7E1C4765DDDE9309E840112D643A6B34DFF3B75112C461F6C2C43C2DB4674750EFFFB2061881C4D6C59B88354D441603C178AAEE8C6E96245201F5F92C36B61565E0AA3C544A51BC5EB792FD15A3468FA2D9493063B3A09F27079EFDD25F04F945A715A9C5FC380EAF1059F7085959EBCE24D5D5FADB38A2D0F1BA26511518214A3A9D5E66031C18AF60DE74458BFF3B11648C195BF3531847B8E21BCD6EAF0D01C8841BDC9DED894A6D2A7AE2313E8FDBFCAE1820D0F89880179947A1A8BB697B44DE07061C1B4BD1851F1D02F6AC7EEDF089349CC9E0277CB599FC68AE1EFFDCCD7DA712423A9C871B6F5514C7AAAC01FCEA368DE3C4D42CB2F32714A0EEDCD1AC197C567AEFE974A74366FEFD2039D7734B4C295038B25FD6BE7CBAB6E3000A4E28ADF21C71EBB40FC0D3CD27BF0BA5302A0274EAD05A8AAE5FECCDBEA92666B9590874E24FA1EEA3AD3AA4EC89AF728D26B476D0BA8517D57ADBCFF5CDEF825EE3981198D538C80ED2529693AF0D2EA0A567AA21FEADD06E10A90169A06388F5EB7619730ACA4A724FFA67 +Key = 2B7E151628AED2A6ABF7158809CF4F3C +Nonce = ABCDEF0123456789FAEBDC01FFFFFFAB +Out = 0CCBE9C637C732BDA84FB5EF0BE98A8AE5B1D65082DF1C6420D563224D41A01BA5A8C810F9B08F516249A442EC6A649D20E509425EB7FED0FCFCA85E630473D1697A92AA95C2070433E7902932A6A0A0CE06338276DAA255A4053C189715788B1E72F9D27451269D9A27F9F3EBDA82C458C5A5B909FEBE606E593D5B44426C2DBD8E3534DCC4B42645AB4D6473F3E8733D60D509D6807FAA6FA61C9B65E0C9FE7B684C9FFDFB2B8E5B800782B351F4AA54991B5062C217E2D9430DF53A019CA5815569A0B704963707A99AF6003441497AC38D2C176557E5343CE1E745943662A7B61BA790BE95638C294928F2AA932C05CBAEBE51A98AEF3C1E9E96D922B3DFEDC2F0084124C3CCF08E58D48859A82030895A33EAF2A4315F9CEB06FE28FBAE2B5FF0714B97E11C7F0C208A34398FE503E07FEE53D8536D39A011D14E7E5F1B41FC0F78C4889E0BE3028AB26E19160C8648A1B24BDA5C2036777C9242ED5BDD9EF6A529B0C3C4F1E2038D2B511CDA8A058A7D609D05FB35652AC34DEF823BE330FEE0F6C65D46B03BD460911AF2974A51D7179E33E02580FDA55A316AA4BDE5085DEA08F0083904D13188A01CFAABC90F4D575347B382B20DA6381F89B80CF643C7554E86531ACCB605660228048B09C466523D96877ED4C4C2387975C7E880ED50180C13C92CE8388F22A1235F53878ABAE494ECD3C5178EB4027FE7D8E717083D7F55AF044E280CCA8AC99A08A907C31D66095410F1BB21A8FB5CF663FC68109F9998C3207BB6CE23F95760DA1BE6D43DDDDAD5E4C9DBD2438BDB69790B9DE4F3CABDC3D8F783F4B2BF58C58D7F652C8247E72528B68F8D1E6A088D931319A5DD1525B38402A500F8C5C0E62226B58577FCBF4272ECC4B1800A60AB802190383D28D4ACFA249E6C53FED9034DD132033ADAA2C1014AF6A327BF6D9D5978E33AE7D55D39FBCF504A954F2C97AEEEA4401A0C4373460AE04024A80BE7F5C3D529748FD33A1A927060C9E670E5E75705130EED64E859AD7038476A7556ECAEDA4D56DF055BCB2F6674E711F1D9230D37F2D6A049EA6D9722B3411611AAB6835956E40B35BBF3FF7E54A8BA923400DBDFC3161B34100537539AF889D92ADE33982F7ECFE327F34DE0CB4612CF3815508E62F377EFDC0E9E5BAE0D95613378812F497643A0501FACB2A4A1B2EC2DC82AFEF07BC01EFABDB43C1178D22CB088A81BA28164EFAC2A9AE8B96A5266857880D49809D483970524B28DD49E64D106195939ED640E82D2BE7F90D8C93959AD74973048172A109A25E1EE971CCCA474E4F9600091C56CA18404BD520A1F1B223EBC4802F31EC5F406050F61B6277C51AEB0FC32906ED126839B71F941043C8E8428EF321A8E64F672770B1F2B19289FF4E7C62F346CE0373252A006E52F4876CC6DAE720017C339C910DBCB735AA0E51AA2 + [CTR-BE(AES-128,5)] Key = 2B7E151628AED2A6ABF7158809CF4F3C Nonce = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF diff -Nru botan3-3.7.1+dfsg/src/tests/data/tls-policy/compat.txt botan3-3.12.0+dfsg/src/tests/data/tls-policy/compat.txt --- botan3-3.7.1+dfsg/src/tests/data/tls-policy/compat.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/tls-policy/compat.txt 2026-05-07 01:38:28.000000000 +0000 @@ -11,7 +11,7 @@ allow_tls13 = false allow_dtls10 = false allow_dtls12 = false -ciphers = ChaCha20Poly1305 AES-256/GCM AES-128/GCM AES-256 AES-128 3DES +ciphers = AES-256/GCM AES-128/GCM ChaCha20Poly1305 AES-256 AES-128 3DES macs = AEAD SHA-256 SHA-384 SHA-1 signature_hashes = SHA-512 SHA-384 SHA-256 SHA-1 signature_methods = ECDSA RSA IMPLICIT diff -Nru botan3-3.7.1+dfsg/src/tests/data/tls-policy/datagram.txt botan3-3.12.0+dfsg/src/tests/data/tls-policy/datagram.txt --- botan3-3.7.1+dfsg/src/tests/data/tls-policy/datagram.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/tls-policy/datagram.txt 2026-05-07 01:38:28.000000000 +0000 @@ -2,12 +2,12 @@ allow_tls13 = false allow_dtls12 = true allow_ssl_key_log_file = false -ciphers = ChaCha20Poly1305 AES-256/GCM AES-128/GCM +ciphers = AES-256/GCM AES-128/GCM ChaCha20Poly1305 macs = AEAD signature_hashes = SHA-512 SHA-384 SHA-256 signature_methods = ECDSA RSA key_exchange_methods = ECDH DH -key_exchange_groups = x25519 secp256r1 x25519/ML-KEM-768 x448 secp384r1 secp521r1 brainpool256r1 brainpool384r1 brainpool512r1 ffdhe/ietf/2048 ffdhe/ietf/3072 +key_exchange_groups = x25519 secp256r1 x25519/ML-KEM-768 secp256r1/ML-KEM-768 secp384r1/ML-KEM-1024 x448 secp384r1 secp521r1 brainpool256r1 brainpool384r1 brainpool512r1 ffdhe/ietf/2048 ffdhe/ietf/3072 allow_insecure_renegotiation = false include_time_in_hello_random = true allow_server_initiated_renegotiation = false diff -Nru botan3-3.7.1+dfsg/src/tests/data/tls-policy/default.txt botan3-3.12.0+dfsg/src/tests/data/tls-policy/default.txt --- botan3-3.7.1+dfsg/src/tests/data/tls-policy/default.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/tls-policy/default.txt 2026-05-07 01:38:28.000000000 +0000 @@ -1,12 +1,12 @@ allow_tls12 = true allow_tls13 = false allow_dtls12 = true -ciphers = ChaCha20Poly1305 AES-256/GCM AES-128/GCM +ciphers = AES-256/GCM AES-128/GCM ChaCha20Poly1305 macs = AEAD SHA-256 SHA-384 SHA-1 signature_hashes = SHA-512 SHA-384 SHA-256 signature_methods = ECDSA RSA key_exchange_methods = ECDH DH -key_exchange_groups = x25519 secp256r1 x25519/ML-KEM-768 x448 secp384r1 secp521r1 brainpool256r1 brainpool384r1 brainpool512r1 ffdhe/ietf/2048 ffdhe/ietf/3072 +key_exchange_groups = x25519 secp256r1 x25519/ML-KEM-768 secp256r1/ML-KEM-768 secp384r1/ML-KEM-1024 x448 secp384r1 secp521r1 brainpool256r1 brainpool384r1 brainpool512r1 ffdhe/ietf/2048 ffdhe/ietf/3072 allow_insecure_renegotiation = false include_time_in_hello_random = true allow_server_initiated_renegotiation = false diff -Nru botan3-3.7.1+dfsg/src/tests/data/tls-policy/default_tls13.txt botan3-3.12.0+dfsg/src/tests/data/tls-policy/default_tls13.txt --- botan3-3.7.1+dfsg/src/tests/data/tls-policy/default_tls13.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/tls-policy/default_tls13.txt 2026-05-07 01:38:28.000000000 +0000 @@ -2,12 +2,12 @@ allow_tls13 = true allow_dtls12 = true allow_ssl_key_log_file = false -ciphers = ChaCha20Poly1305 AES-256/GCM AES-128/GCM +ciphers = AES-256/GCM AES-128/GCM ChaCha20Poly1305 macs = AEAD SHA-256 SHA-384 SHA-1 signature_hashes = SHA-512 SHA-384 SHA-256 signature_methods = ECDSA RSA key_exchange_methods = ECDH DH -key_exchange_groups = x25519 secp256r1 x25519/ML-KEM-768 x448 secp384r1 secp521r1 brainpool256r1 brainpool384r1 brainpool512r1 ffdhe/ietf/2048 ffdhe/ietf/3072 +key_exchange_groups = x25519 secp256r1 x25519/ML-KEM-768 secp256r1/ML-KEM-768 secp384r1/ML-KEM-1024 x448 secp384r1 secp521r1 brainpool256r1 brainpool384r1 brainpool512r1 ffdhe/ietf/2048 ffdhe/ietf/3072 allow_insecure_renegotiation = false include_time_in_hello_random = true allow_server_initiated_renegotiation = false diff -Nru botan3-3.7.1+dfsg/src/tests/data/tls-policy/strict.txt botan3-3.12.0+dfsg/src/tests/data/tls-policy/strict.txt --- botan3-3.7.1+dfsg/src/tests/data/tls-policy/strict.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/tls-policy/strict.txt 2026-05-07 01:38:28.000000000 +0000 @@ -2,7 +2,7 @@ allow_tls13 = false allow_dtls12 = true allow_ssl_key_log_file = false -ciphers = ChaCha20Poly1305 AES-256/GCM AES-128/GCM +ciphers = AES-256/GCM AES-128/GCM ChaCha20Poly1305 macs = AEAD signature_hashes = SHA-512 SHA-384 signature_methods = ECDSA RSA diff -Nru botan3-3.7.1+dfsg/src/tests/data/tls-policy/strict_tls13.txt botan3-3.12.0+dfsg/src/tests/data/tls-policy/strict_tls13.txt --- botan3-3.7.1+dfsg/src/tests/data/tls-policy/strict_tls13.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/tls-policy/strict_tls13.txt 2026-05-07 01:38:28.000000000 +0000 @@ -2,12 +2,12 @@ allow_tls13 = true allow_dtls12 = true allow_ssl_key_log_file = false -ciphers = ChaCha20Poly1305 AES-256/GCM AES-128/GCM +ciphers = AES-256/GCM AES-128/GCM ChaCha20Poly1305 macs = AEAD signature_hashes = SHA-512 SHA-384 signature_methods = ECDSA RSA key_exchange_methods = ECDH -key_exchange_groups = x25519 secp256r1 x25519/ML-KEM-768 x448 secp384r1 secp521r1 brainpool256r1 brainpool384r1 brainpool512r1 ffdhe/ietf/2048 ffdhe/ietf/3072 +key_exchange_groups = x25519 secp256r1 x25519/ML-KEM-768 secp256r1/ML-KEM-768 secp384r1/ML-KEM-1024 x448 secp384r1 secp521r1 brainpool256r1 brainpool384r1 brainpool512r1 ffdhe/ietf/2048 ffdhe/ietf/3072 allow_insecure_renegotiation = false include_time_in_hello_random = true allow_server_initiated_renegotiation = false diff -Nru botan3-3.7.1+dfsg/src/tests/data/tls_13/client_hello.vec botan3-3.12.0+dfsg/src/tests/data/tls_13/client_hello.vec --- botan3-3.7.1+dfsg/src/tests/data/tls_13/client_hello.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/tls_13/client_hello.vec 2026-05-07 01:38:28.000000000 +0000 @@ -5,26 +5,26 @@ # - Extensions [client_hello] -# no extension (empty renegotiation generated) +# no extension Buffer = 030320f3dc33f90be6509e6133a1819f2b80fe6ccc6268d9195ca4ead7504ffe7e2a0000aac030c02cc028c024c014c00a00a500a300a1009f006b006a0069006800390038003700360088008700860085c032c02ec02ac026c00fc005009d003d00350084c02fc02bc027c023c013c00900a400a200a0009e00670040003f003e0033003200310030009a0099009800970045004400430042c031c02dc029c025c00ec004009c003c002f00960041c011c007c00cc00200050004c012c008001600130010000dc00dc003000a00ff01000000 Protocol = 0303 -AdditionalData = FF01 +AdditionalData = Ciphersuite = C030C02CC028C024C014C00A00A500A300A1009F006B006A0069006800390038003700360088008700860085C032C02EC02AC026C00FC005009D003D00350084C02FC02BC027C023C013C00900A400A200A0009E00670040003F003E0033003200310030009A0099009800970045004400430042C031C02DC029C025C00EC004009C003C002F00960041C011C007C00CC00200050004C012C008001600130010000DC00DC003000A00FF Message_Type = client_hello_12 Exception = -# with extensions: point formats, ec curves, session ticket, signature algorithms, heartbeat (point formats and heartbeat not supported, empty renegotiation generated) +# with extensions: point formats, ec curves, session ticket, signature algorithms, heartbeat (point formats and heartbeat not supported) Buffer = 0303871e18983024eaee1be8ae6607d5ecad941d33fd7fc1d8554a9e1fbfda8d30880000aac030c02cc028c024c014c00a00a500a300a1009f006b006a0069006800390038003700360088008700860085c032c02ec02ac026c00fc005009d003d00350084c02fc02bc027c023c013c00900a400a200a0009e00670040003f003e0033003200310030009a0099009800970045004400430042c031c02dc029c025c00ec004009c003c002f00960041c011c007c00cc00200050004c012c008001600130010000dc00dc003000a00ff01000055000b000403000102000a001c001a00170019001c001b0018001a0016000e000d000b000c0009000a00230000000d0020001e060106020603050105020503040104020403030103020303020102020203000f000101 Protocol = 0303 -AdditionalData = 000A000B000D000F0023FF01 +AdditionalData = 000A000B000D000F0023 Ciphersuite = C030C02CC028C024C014C00A00A500A300A1009F006B006A0069006800390038003700360088008700860085C032C02EC02AC026C00FC005009D003D00350084C02FC02BC027C023C013C00900A400A200A0009E00670040003F003E0033003200310030009A0099009800970045004400430042C031C02DC029C025C00EC004009C003C002F00960041C011C007C00CC00200050004C012C008001600130010000DC00DC003000A00FF Message_Type = client_hello_12 Exception = -# with extensions: point formats, ec curves, session ticket, signature algorithms, heartbeat, Encrypt-then-MAC, Extended Master Secret (point formats and heartbeat not supported, empty renegotiation generated) +# with extensions: point formats, ec curves, session ticket, signature algorithms, heartbeat, Encrypt-then-MAC, Extended Master Secret (point formats and heartbeat not supported) Buffer = 0303e00da23523058b5dc9c445d97b2bb6315b019e97838ac4f16c23b2cb031b6a490000e2c0afc0adc030c02cc028c024c014c00ac0a3c09f00a500a300a1009f006b006a006900680039003800370036cca9cca8c077c073ccaa00c400c300c200c10088008700860085c032c02ec02ac026c00fc005c079c075c0a1c09d009d003d003500c00084c0aec0acc02fc02bc027c023c013c009c0a2c09e00a400a200a0009e00670040003f003e0033003200310030c076c07200be00bd00bc00bb009a0099009800970045004400430042c031c02dc029c025c00ec004c078c074c0a0c09c009c003c002f00ba009600410007c012c008001600130010000dc00dc003000a00ff0100005f000b000403000102000a001c001a00170019001c001b0018001a0016000e000d000b000c0009000a00230000000d00220020060106020603050105020503040104020403030103020303020102020203eded000f0001010016000000170000 Protocol = 0303 -AdditionalData = 000A000B000D000F001600170023FF01 +AdditionalData = 000A000B000D000F001600170023 Ciphersuite = C0AFC0ADC030C02CC028C024C014C00AC0A3C09F00A500A300A1009F006B006A006900680039003800370036CCA9CCA8C077C073CCAA00C400C300C200C10088008700860085C032C02EC02AC026C00FC005C079C075C0A1C09D009D003D003500C00084C0AEC0ACC02FC02BC027C023C013C009C0A2C09E00A400A200A0009E00670040003F003E0033003200310030C076C07200BE00BD00BC00BB009A0099009800970045004400430042C031C02DC029C025C00EC004C078C074C0A0C09C009C003C002F00BA009600410007C012C008001600130010000DC00DC003000A00FF Message_Type = client_hello_12 Exception = diff -Nru botan3-3.7.1+dfsg/src/tests/data/tls_13_psk_import.vec botan3-3.12.0+dfsg/src/tests/data/tls_13_psk_import.vec --- botan3-3.7.1+dfsg/src/tests/data/tls_13_psk_import.vec 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/tls_13_psk_import.vec 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,47 @@ +# Test vectors for RFC 9258 PSK Import + +[SHA-256] + +# SHA-256 key, SHA-256 target +Key = 4142434441424344414243444142434441424344414243444142434441424344 +Identity = 70736b31 +Context = 636f6e7465787431 +TargetHash = SHA-256 +Output = 77cfa040c1e1ca63f0cd989dbefb28f5129625b08ce4f258bb825a9b0a4642a0 + +# SHA-256 key, SHA-384 target +Key = 4142434441424344414243444142434441424344414243444142434441424344 +Identity = 70736b31 +Context = 636f6e7465787431 +TargetHash = SHA-384 +Output = 3cb0eb3c10863c51a674df9f7b5900b47aa9353ba35c475b7ff6ad53f868ea97ee896b7b54460853340f699b91949d8d + +# SHA-256 key, empty context +Key = 4142434441424344414243444142434441424344414243444142434441424344 +Identity = 70736b31 +Context = +TargetHash = SHA-256 +Output = f38b065c368f79497cd9b2615d047e3415b62ce43b7eb6e1d0474cb5636fedb0 + +# SHA-256 key #3, SHA-256 target +Key = 494a4b4c494a4b4c494a4b4c494a4b4c494a4b4c494a4b4c494a4b4c494a4b4c +Identity = 70736b33 +Context = 636f6e7465787433 +TargetHash = SHA-256 +Output = 64d6ffd17c8eb3a64558008681b2f7e5a338963a25c7d1c75f4f72196159db02 + +[SHA-384] + +# SHA-384 key, SHA-384 target +Key = 454647484546474845464748454647484546474845464748454647484546474845464748454647484546474845464748 +Identity = 70736b32 +Context = 636f6e7465787432 +TargetHash = SHA-384 +Output = fbc3670da40a25dfbe8132b2840a412a7fb3a93846c3111ee45609fe68b70d5c9dfabdfdb3268a52a604de3c33b65f1d + +# SHA-384 key, SHA-256 target +Key = 454647484546474845464748454647484546474845464748454647484546474845464748454647484546474845464748 +Identity = 70736b32 +Context = 636f6e7465787432 +TargetHash = SHA-256 +Output = 02b3e806e32d41ebbd4ea0dc49c4a448c186df3e161e1f68bec1889e84fc4fcd diff -Nru botan3-3.7.1+dfsg/src/tests/data/tls_13_rfc8448/transcripts.vec botan3-3.12.0+dfsg/src/tests/data/tls_13_rfc8448/transcripts.vec --- botan3-3.7.1+dfsg/src/tests/data/tls_13_rfc8448/transcripts.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/tls_13_rfc8448/transcripts.vec 2026-05-07 01:38:28.000000000 +0000 @@ -34,8 +34,8 @@ Record_Client_EarlyAppData = 1703030017ab1df420e75c457a7cc5d2844f76d5aee4b4edbf049be0 Record_ServerHello = 16030300600200005c03033ccfd2dec890222763472ae8136777c9d7358777bb66e91ea5122495f559ea2d00130100003400290002000000330024001d0020121761ee42c333e1b9e77b60dd57c2053cd94512ab47f115e86eff50942cea31002b00020304 Message_ServerHello = 0200005c03033ccfd2dec890222763472ae8136777c9d7358777bb66e91ea5122495f559ea2d00130100003400290002000000330024001d0020121761ee42c333e1b9e77b60dd57c2053cd94512ab47f115e86eff50942cea31002b00020304 -Record_ServerHandshakeMessages = 1703030061dc48237b4b879f50d0d4d262ea8b4716eb40ddc1eb957e11126e8a7149c2d012d37a7115957e64ce30008b9e0323f2c05a9c1c77b4f37849a695ab255060a33fee770ca95cb8486bfd0843b87024865ca35cc41c4e515c64dcb1369f98635bc7a5 -Message_EncryptedExtensions = 080000280026000a00140012001d00170018001901000101010201030104001c0002400100000000002a0000 +Record_ServerHandshakeMessages = 170303005ddc4823774b839f50d0d4d262ea8b4716eb40ddc1eb957e11126e8a7149c2d012d37a7115955464ce242a8bbec05967ce9127b806c07218590d06b66ff0aa95b56562f404c410b702dda9a4a4bb3b9cc44fe20e2675e63020d66b936120 +Message_EncryptedExtensions = 080000240022000a00140012001d00170018001901000101010201030104001c00024001002a0000 Message_Server_Finished = 1400002048d3e0e1b3d907c6acff145e16090388c77b05c050b634ab1a88bbd0dd1a34b2 # ClientFinished contains two records: # * end of early data diff -Nru botan3-3.7.1+dfsg/src/tests/data/tls_cbc_kat.vec botan3-3.12.0+dfsg/src/tests/data/tls_cbc_kat.vec --- botan3-3.7.1+dfsg/src/tests/data/tls_cbc_kat.vec 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/tls_cbc_kat.vec 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,123 @@ +# This test vector is taken from transcripts generated by our unit_tls test + +[TLS_CBC(AES-128,HMAC(SHA-256))] +BlockCipher = AES-128 +MAC = HMAC(SHA-256) +KeylenCipher = 16 +KeylenMAC = 32 +EncryptThenMAC = false +Protocol = TLS +Key = 228F2C6EAACE13EF6EB9986A9E6C333695661D292B22F1491EBA0E35EBDA6370FF2609E26D2E07EDEBE9A2C97BF76B4F +AssociatedData = 00000000000000001603030010 +Nonce = 40B6B772C42FBB0DDAF9D0F8DB3F4B45 +Plaintext = 1400000C08C742ABD8C48C573CBD3FC7 +Ciphertext = 6BD213365CBA73C0F2697C5E772942BEAC666F997CA6A0AFCD43291BA69A9F38C0BF3459551D6405AE6775626F9B4D1E6105B0C758A180699E4B2F46DC128EA0 + +BlockCipher = AES-128 +MAC = HMAC(SHA-256) +KeylenCipher = 16 +KeylenMAC = 32 +EncryptThenMAC = false +Protocol = TLS +Key = 228F2C6EAACE13EF6EB9986A9E6C333695661D292B22F1491EBA0E35EBDA6370FF2609E26D2E07EDEBE9A2C97BF76B4F +AssociatedData = 00000000000000031503030002 +Nonce = 3B43563DC6F6AF075EF5CBCEE8FC3A95 +Plaintext = 0164 +Ciphertext = A32AEF79E640787FA96DD93191C63B03254469C78FE9590D1599E97D80397638D3B3A75A9BBA5485013BC3198B9D27AD + +BlockCipher = AES-128 +MAC = HMAC(SHA-256) +KeylenCipher = 16 +KeylenMAC = 32 +EncryptThenMAC = false +Protocol = TLS +Key = 42039FD3E3AF88B9D41A0D9FF3A73B70264038353DB262DE275C175766D3F2902BB403149B8BC8E3A58FA9D25ADB6747 +AssociatedData = 00000000000000021703030287 +Nonce = 4BFFF731AE424166B87DB996BD6BF18D +Plaintext = 2AEAA19A2E82D0B08E6E00E87F90EE5508654E779B2D976836FF94A386293EED927452D063C8303D389F80C3B9E215E2AE999901B9FB78739E5898287E60496DA549014F54475E1410EDE35CFAE1FF4CA373DA65E98361735A56180C94B7314231FB64D8AF5F11A6E546209173CC269CBB3D4F9FDFDF900E3FA29566FF22F8AE0E8D3D9D31E10FD2950940AF644F1350D8B343176098525D36519EB90A04B1642971623EA60E2EE610055B751E856153EFFE11A741A506E734E9D4FC0D327E897C89B6CFED9653A35477980F03FCB9FF02B92798686F1AA34261E69471F191B013292DCFF2987538720E301A84B0D72123EE01A4CACF0CF9761F9657AFF52CDC0812CC31B1A79A4587E96AA5220D84F3771829F46B0C6AC0F8FBB2895162A1808777A55539C2D8D8C297A02D6EF0992230213D2261E0D140FE391BE1EFCD5280CBFBDE0EA65955726414389F09A502C89163E836D073F130D092C184323BB12F1EAFAB9D264D4702BBD1AC58A4E9B772EDAAE7ABEF5D87B7C42AA407D5204052DC174FB2F4EFF4E626AB8226FFE8C41AA82F066A01A7616D4199D4709E60911A70241F715FFFB3F014F15447ED3E432C349E20CC5CC85E5AC0E571346950472D553A7E69C4ADE95D0561C9674FF85C83150F219B65AB6AF4C685AB391DB4129C162AE19D8F990CDE862A99A514934B6CDD0E071091A68623EB5EC3D3B4C0B7EC4E37CC7D3DD5A29238EA8C8E3FFA59A03096DCD46583BD40D5C808C937190510A814D3EC5CE14208CAAF7B1FE08BE14DC010BD0276792F103D89DB50BFD4E16CE0E29A3B89AC9240F9F94DC519114C0C5158D7226A320ACCE9D7AA0E75753BD2BF35D42B719847AA97B4FB5F1ACA15EBB6B7642EF5C58B1AAF58F81791F01587220F47EDD07528 +Ciphertext = 569FE160953DCE220807DF2F5D1D6768A2000CA7BEBD7444ACC744D9C3FD3ACDD3F301CAC4331061170C01F7A2CC60479CE3640467076FC9B2AE4EA8ACE2CF62F183F230D5F13E78A6270A50B2D419122764EF2735AC1785A20843FBE1E42851894B628F069419133CFCA68DE92BD47F06FCD813E40CBEEF222FB60D0AC5B413FCD64553540B736050C80C5441BADFCE31DA6136C9664CA110CD5D0318BCA908285634675935A947FEE485144E14488D94610399F81D518A55535447318C4265D5B5B314AD4716E1222C551FF1AD6417163A36D5FAEB91D1986045FCFCE3D16BC5C4FB67E5A95D756A2E8D304593FC948BC53317F99D0765AA91A97302F06F2A45E2FBEEB1C4BE50B6722F381C85BBE083B0B5CB397BFDB86997B48ABE7807E7A6981D19B2372B761BB9A4ED3F4D58EC45F889735CE8D56CA4D79DC3814CC40917DAD9DD1E1AC6EF3F97EF82E433EDB2CE2A50C8CF080C9353AAD68AC7B7E42026CB91BF942C7D1C2083CCBD0CF64D147C5094C7FB2C32155CD295F1C477A4CB02B8C7B9164626E5B77226F7406C2AD7838F1BA00BCCB3BA03B1EF939E5D06CE07C27A0C07531415CB5CE00486FE2BDB20762019D3809C29182A33B95AE92620EC01F07CCEE2715DF724B926FF0A08C35628968C8A479CF650123FAD14EC1853CABC5BEA0570081CBEF470A94568CF31C42EF8D9BD4956CCE05EB9B5470364FC25189A0FA8DF27C89C7E8480C37CE638AA9014A8677A53716C1C994793250DE1ECF2ABE2FAF8EFCFAB7B3B5F9217F83D0BDA1E24B6BCF59247803CEA767AEBC5AE34668D5C74B1B77F4246EE8A071CCFC3E0CA488E9346D061FDE6992FD6016B498C682E3ED3266058059DE2B2232D0A6480267A0BE6AC77EDD59056A9C1869B9BA31700630C4111D4B010727CCD16E8222EDB542DC5A143018181096EFD13E3DF31D2C6FD5FA3899CEA6BED79C09AB7 + +BlockCipher = AES-128 +MAC = HMAC(SHA-256) +KeylenCipher = 16 +KeylenMAC = 32 +EncryptThenMAC = true +Protocol = TLS +Key = A787FC7C9A946A60B67F959CF660B9CBE6B04C1642329E76638A0CED9C870C35AEF9C9078CAE44553890C34CAF054CD9 +AssociatedData = 00000000000000001603030010 +Nonce = 5FA3BCD753100C20398437771F35C3B1 +Plaintext = 1400000C89147D7ACC915D8B5CD3F6F0 +Ciphertext = D039E4C32939CFE4122F3FFEAA7EA0E90B415E8817CCBBC1503B489FC1FD7EBF26849BF090B40751227526A9D4127705BFB9932019F80C96B3DE89EAE4609311 + +BlockCipher = AES-128 +MAC = HMAC(SHA-256) +KeylenCipher = 16 +KeylenMAC = 32 +EncryptThenMAC = true +Protocol = DTLS +Key = 2D843E00CB8BD321A6E429E86B21919A815C47AFD49988BEB6A61FE457D276F79A1CD2318628AFA306F106EA5664F3D0 +AssociatedData = 000100000000000016FEFD0018 +Nonce = 345D1FEF92A1DE247A389571E6EE4668 +Plaintext = 1400000C000300000000000C351C7B841073AD34F90E2935 +Ciphertext = 5A77B87FC3F3FFF8FCC063DBB62092D687FAFA255FFFAA3B338FC1BA378E077E88D9892925ECAAC601435130FED2A295EF029707A78D12C9672BB8C2945EF376 + +BlockCipher = AES-128 +MAC = HMAC(SHA-256) +KeylenCipher = 16 +KeylenMAC = 32 +EncryptThenMAC = true +Protocol = DTLS +Key = 2D843E00CB8BD321A6E429E86B21919A815C47AFD49988BEB6A61FE457D276F79A1CD2318628AFA306F106EA5664F3D0 +AssociatedData = 000100000000000117FEFD0910 +Nonce = 0E01769ED85B098867B39E3E3ED8DA2B +Plaintext = 03807FD82EBDA118B57B3158D13556CD48A01FC3A64259D7CB24E60831281A267D739B20CD7746A6531794AEAB6609557A892697D52C5666DE186EA037612558AABA13089F56BB12CFF20A27C172ED0386A45A093D906639D7F7DE07A359E5821A2C0F67739F89837B7FC1F629BC0FD781FFEBA636EB192C8F6C6A2710A8705C6911C5832B074A8BB9A3F2BE06138D41911B186F282A128BF092545A2A58EB0F471F7D13B5B4A72FFBB0E9908CBA9223EBE630D08BAA0012F4F6EF6CAEE38B33707F8D3D133B58E5C36901F000625DCCD7C192A8E73AA6EFA5939E96673194D1B4C85D9753A2268FA403A4CFB62D3BFDAB7AAD46D416D4BD1DD3D583329D5D61F0026329975EEC843E214F9111AC87E6CB510069FAEB6B898793164EFBEF49CB13A327680E55918646D68C0786E0B128AFF61B3E12D85CCF1C1EF480BD62CF691993403BF8DB0FCC7BA5F574993C34D3DC889C65E36AA97C262F1415869E7202132A55F8A6B86EF8B183378BDE9F9D68E99633EB459E62EC00F1277670BDC9CE1D2F1D67761FC91FCAD20C6DF95C8AD77B209D4F21E2A9ED1401F07EA849787765DA5FBCDBB648C7B7653FAE9D35A8804995AC7F6E12AEB9DB6A44786A3A34152AD1F19F539323E37A82AC50905AB43419D53DDA347EB2FDE0A7041E02FBC230BA2DBC266E3CA5D827DA3DC4A56D7B34C02F3F2F8DE107D7BCA3249ACC65F8C17275B6D8CDA52679DE91EDEFBF80D92F2563B3BB9F690ED21BBAA88835C1BB2FC1A1E7AA20340F0DD23BC821D514BD473E9FA62EA2B438EBB5B7A1F0B8C9FF542616640427BFDA4745DAE81EE81A220C118438A5E0D0068F55D6334EE1A6C9782DAF56BCB18B0F4B89E479180EF4177DB42197AFAF390899D5C3928D4CF1305475B0F3199F4D47AE003AB6B26A73B80C4DF6044B78DEE1562098FF06A6B5570FACD383D0E22B2C741C31E9FD31D9329A12F1CDE7B41B40832EE1CF84AA697342903E5D0878BA76135F8C6F7DA6D7C37549725161E9BDE84C0A9A654125F9CBF6F089E7577200EF6E5C7EB1231D8FB7604949FF08B004A84DCF2E32E9F3BDB3A4A9BC98C5F17270D9B3AA2B52FC916B8A76B5569AB29B6394A678BC95FF7F8F32A94DF9C723F6E119182568DDB5E04D934865E546A6A10A9CCAC493CF4678D6FBEE259F444AE13D644D710306CDEDDA8D457A4CAA54A29C5184CE5D93D4510BC6879A3293B6F88B5C2383A77FD89A9EF8028139D4949C2C112EC0CB4BC525C5CC92746979C571E5167CBC106C793737C3257C6C444FFEDAA83336A0D0447CA8B53CEB0B2DEAF174184BF2075D6686515066D9B3E77DA3D8510D3CB16F8F506A9AC4A6EF57A28F71559267695762B9AB6F8877ED1C25D967BA454DDFE0F10ACE0377BCFC0B80CE253162CF21CB4171106164A70BB15F5FD70076551E4FC684ABE9B4B52AD121A5E982DE4D299BE7BF5ED5DE270AE555618AAD4AAE72557A07E6B4E9887E9E387A278C38758C1A482682EDED28FA663D7DF1C07B25ECFE894D733C949D11DA83205702B24A660B68967A389747F9535FC08CA4D2F5B0C3807F58CA43CC095BD4DF020A9E3FE1A05C7051B7F296383915A8ED352BC8F290FA37A917945A9E68096BC2395E383A7B478726DB2593F419C622B6C16FBCF3BFA37E8C4A350116B613EA3F916489BAAF5E1C2484642E7E5FE98B9603975B081938CD36FDE4E8C96CF2F1338832F4BCBFE4E08B02F6C633EB08B14F29AFA094DA850EEC376FA51D21B68666035BAEAD9C2EF4A517322A840AB9D2B3CED1F708AC659A0372B4418ADB7F1FB2557F5A07A99FEBD1F17D9BEF7CA69BA2FE253CE9A9C53BDF95A23296107E9F0044520A54B9ADC57C324DC7036E87290D38E7B9CA5CA02AF7F83661DAA7F25E49039644310DDAF322EB29CE6080220AF566602B4EBF0411CF667F24FBF72B2095371BF9485ABB59519BA63FB4BF513B6CE5EBBD273D0B08FC159A43B1C5891CFFF5C68E53C0F345A5346A1BBEAAFB2A9282F20918B0E29B23AEB6F6C4487DF5AEC087D520D2387BCC142DCF4E3019B39979EE1DF663C6C1F94B10E4092887C2DD8E601387944D2C830EB33C42AEB226C2776972A20F01E54472F72568793609D3C365FB757606F99761D2B28AF4DF3E5D97FFF510E0F0DFD81DC93FD9C32BBEEB32B6B0E45C47F3E5BE72EF2540C829CB685801437000FA9BB4F42A62FC15478D2286C6E098049C5A458722223FA4867FE42F614FC9ACEE8110F54F1A8AB57A3246173F84ED41E6F38719ED9F12BC60F8784E51556680E590785C832844DB9C64C2BA90FA8D1332BD843C5DCC456837C9018F7D8A311979DDA6C60FC4706762BB2BD39D7D1A9E50D5AD17F3E9D810F691CBDDFF2F8522B38CC1E0AA34C549F2E185D3B75AA7148469E2E19D43392CFC02933063992C570CD3636A7BD06A81E18E453CA4EAB6BC7DB473DEBC3A464416DD8593A42B60856FF794311BFFF41F358B50A195993AE85F0621661E3E4EF081F43D212F56CC88334DCB139037694363B3F9A230E295FCB9B91CDE03CC10D8EC26C903DE9F8A4B2339F2FE5100421E51ED06F38650996D8D3C05BD1B75B9B47266A373FD9926CB793261F147F163EE5433AA551C7F8ABF4E0CEC8784D8E84DA3B5BABC47E79814DDBCE1FAC9B02405318E8A9BE6181986DCB552D5D0A2AB7A7A226937E83B4D42696CA9369DF64AC21F17BBA745D7696688D6274BFB94822466D17C4F7B581D81A6E863D3FC880D96C1F6C0690F880D131519CC9F787D5A4245F0317E0C13540D8FE326108D3F12012E668FE686050651B43E1098243B92DD62C2334D164DAC328C247C3B68007191ECAF8B2E5D39C7F40510350C4FBEF90C7D989F5784FA4F4C767F5CB113C4AEE9EF2A8A64B92033E01B6EEBE1B805CCF8C2D30146B21DD8C798DA30E28791C2B79D41D0BA2DC59C097743520C901EBAD8CFE357D36CCA521FAB2ACE4A2A7F13BCCC6B1072BB42C5740B8EFA957529E2F6B14B0CCAEC2236E1D7738178D5B376C8C13370DED8C2DF34375BE5907755D2A8E69AFC03DF5A6FA8B8CA000ACE6732BB3132846067818E6DCEC7822515E189575B72756A5FB35821555674AFCDDFFF8542105169BCCAE45410FFB3883F1C747B6D8432F7F982BC07294A7523FA7401258A864A7D99F8632CAF40C6015465739DEB9160B6A72AD3271CEC0B36EB8DD0AD0D5F542E3376FD4ACDD87EE5242BD453116A9CC0D51C4A5C8991B0C5A9A1723C4171C51C2CBE1612FB230A +Ciphertext = 198C77684CCD281B861ED4E73CAF7951F567ED9D7E2B6E23902C1870FA3AF227BFE07D49078580B96E990E6EE0BA1CDE11B52B054C40C0C151CB2A102A8C5C019325EF87E6761B615E30EA18DB1E167DDB0ADB9AA4C6EC214CBD7C4F1D91158AB3E487601228278C5D570EFCBB6456963F524070310DE39388632D887D7D87AFA93301518CB8DA109703FB533ECBFA987245AD9B70E545439F46042979E842D58CB7F49611E2584F67C4D75A424BD91781AC2362F61CE8C9016E68778E7DC89D35AD7C215A772D748F0F13A2A2B0D54590ACF215B583EECB49596B2F49121AF84DD4B4877DABA7EA46BEE8A05B4BC4B2CD0F46418BFFF14C5B0211F00A1174D976F123EDB8002B51FF4A315E9923E039C87558F771E411970F85378B4392EA0553FD7AD0482F47C55346BF000283F5679E7E82D5EF65D7FFCB23F50094A371BB02B163FA220F6A95C1A0F982C67876A6E7AA52272C6D28B9755C77744F4D84ED6D23BC5743306E8DD6EF6D8E788A305C4EA25C7A01BD9D3A554BB23F62608A74E1117B38F260297AE6D98D483277A2E3B32D3AB6295F02237C4A73F97EAE25E943CC47132F6FAE99B6B9A79A2E52266EA839468558A5CAEE5F9C93D845B867233A7289999598597D32A6FE4AC63C70E991B1C02E7DA26E1775A9A485DF11DC7DF5E5079EBCBBD841FFAF202BB9006A39A497AE059AC5D00810681BC2850B2E40103C8DE1FC2764957ACDD4A6B4A32BC396BEACB1C060AC90709165AED95B1A5A4DC09F799A5EFD52428699D7FD2883066EA38947790432A1791E15FD12FC1B305FAB7B8B9ED8C560F45F9C476177965717D27D804427104CD8B94C7E09A6AB096CC3D911587DF5A32C0B364318D45A5A58E7042644845442E283E474A062628369189DA4BD9994BA64EA8A9C94F39D96952847DEEBE91B6E3E4E18020EDC6297992673C16AB9C84FDC19813903903A1D3A382196459CA3DA366E79353C80ABEFF6646C2A1DA40DD60A6E4277AA71B954261C271E7C9903220AE34B32BD7348262C1604B4829C8C13DBECE08B256774E7D21CBD29F200958DBB59F3321FAAAB9E0728FA646439191EFAA91AF79E3CAECC202819AFC71E2819F62C6D50786025C0775C5631F5288A9B9415103CB6EA4504655FEAFA601A4F750D7C6D9B99CAEDB4945586A7745F339C07F114EF5EC148C5911F59456D3E6CED90F5255B09BE523CE33F4745B8A2B3095BD60CA299FA9DBEA5CC351BC1D613DBA34F24076AEECDB13003ABE1D6BED849737AFAAA6AE964214088DEBBAAC7043275F9B2316A29B7E61DEAB143D9EE45840F0C20DCC7FB345E8F3D2F685BBA6267F03D90AEBA83F110C200662FEEC7C6125A1466E5D7673E35961E5F6F0AC089258887AE1D45AC7E6F5D9DD9E3D5161FDE0B932AF1E7582039EB9C26B55BB1AF5FBC221BD8F66D0ADF782DF418E3A99654E7E97AA1EB816A0FF71DC2D15C3ADCF410AFA571B78D8D0AC6838B34D2E264271B447A42FC42A7E5C96699BF54A46B91C72343E7BB3B4FB6031DD7FE5AD6E06F38B3D287C84F4B5E561F779482DA94AF6E11D67BB92E9ABC8EF74922709E5C31E0BA307C4ACC67DB4D232F84FDF685C5E54891C5443FF15F20110AFDEB9DE8016E3D42A4B501C6EB52B9802B314DB4B7EB6588885C2C0DF58E8951BE2E53EEF1EEE467D6CEBB8A821A895EE948F59B4DEFD89661DCBF31E4AB12F532F42B8A8268CD42660ED521FFB884D8DC72B76C7046FF590EB4328DE63C9F13839657DF5D2635C3E644861604F5C23DF86FDB371A5E41356822E8A71BF3DF417405924196DAAE6230CF530D13719E2F73ABD2FFA85CCC332C2264FDD77850E16BA6481BC121F92CDC31E05D888CB11BEFBB4C6EDF8695DA983517C611C62F4AAD0F7FBF77F1AF59DED4797C1A904714B2A24A2BF97C2E8CB68D74EA5160F51FA33269C29047A5692A2CC5F74C524017802E8FE7225AB56C6FB4C902D5BB8BA4B0D968EF16B141E6D38CA7C6CBFEF8FB39B0134B0898555141A3A83A86B77A62FCE15B6A20EC553B6D57E3B37B5A520BA057D88429B399CD5E53F1DB065B7BB1C05691B958DBFEF976C12C9174EEB17FB1295AA6E31FF99EC7A1D291D8AD1BA726F13EBA954A24E4E21641158F508DA06AD87DBB0E30EBBB610D4A4E493F7CCCEF3868E73B94515BBCDC5FC0E1792FE3D8C5E8C812536B7F48ED41D5FF09FAC7FA02CD48FE72F2D7366827AFB8FA0C70709701D0F07A798EFC37A892C0F6BEBB22948BAAF700437F069D0788C793143C5A08AF4A6367EAAB3A5DA028AC0433580B2EE6B115C3003625CC6F43ECDF46085D9524082DF21B711595EAFA2948FCB83F248D481B7590557F56F62FF9DE558A3C6D290D5CC5E8374616490A5ACD3BDE1C3A1037BA623D17389348A97771F0054DD0BC49D48F9CE2E4439EEA25D8721B52F256D7C4AFA57D4ED26BAAA18152B80B1D0CE7006E8B3C262F00C5761A781D2CD045AB796EDAEB203E4CE89A01AA0DEEAD44810352EA989C9C5A63D9898CCFD94FB09F610C599CE9B46FAF6996C1CCBA1D38AC8826E5622C5E61370D058FBCF6A1A4B13DE473728F49B5E531D5012784BF788CC6AE55142334158322561F910E3FCE86D6EFA3E41F036B57B85822180D266DDB21CB7E9F1E7863DD97F3E11B9E299DE559C2F1E27438AD65DF45479F880BD6B04D3949534B495AA7D120FA7E84A056C03C9B4E0369C1C7F279013751852D05B5DE62794B0EB3CB49B2F13DC3316F485E4782204E0C2A826F704201CCE01C9DA5DE781BEF90CC4D3AAAB8D8265CEF6678BB9FB18CB02750441472CA1AFACAF154ADA8E7F5B3E6713C6B8C59D437F6E21E6D76BFD94E5DADA65B38588DA3BDF30C830205CFC0D980A4451CE16BBC4AD2B0EA6B81761BF1DFFF9B8F5E4FECDA4A19C9D39BF6A73D6A8B438F52E50814590C3A9065AF69C9F534B3025F2C4DDE38388CA90D695828D3DE67B3A86C49BEFF4B86ABC57D912968FE53DC8014DDC0AB0F1D8BBE4F612AEEEA3B6E0507381534AEBB9A7411CDA088E3BA3CE79F170D2FBAA3A1255959A17FC14BA2B0D4783EEC7A75A42B4B5B2A5A93F0C3AEEC012575A7C76094F4C3AF24199A4C53548F656CADEC55FA3B7B4319EEBDFCC4B33950B2A41B223FCD5BB2047AE8675EEC6A239EEA861DCD2B3FF842951BBA0D6B5FA9FDFD69D90159E18C16015F847BECD33C0C673181BBB5F6566E9E33973B8C96BF27E3263A3E78B8739D298507E90FE0568C09609125660A81D6970EC46E464C305526FB4BAA5A4C0F21B1890EFEB3DF6E66244BC2333DD3BA347C93BB930AD30A2E252C1E11582B0ACE61CA + +[TLS_CBC(TripleDES,HMAC(SHA-1))] +BlockCipher = TripleDES +MAC = HMAC(SHA-1) +KeylenCipher = 24 +KeylenMAC = 20 +EncryptThenMAC = false +Protocol = DTLS +Key = 55A76A58308635556CF6108BD5A8D8B2336647DC2BC7FF28F3D29D4E30E19CE193B29011353DDEDF72F5101A +AssociatedData = 000100000000000016FEFD0018 +Nonce = 5DA3831D96978F95 +Plaintext = 1400000C000300000000000C408F29D8624461606326521D +Ciphertext = DA2D09EBCDEB417954458E27CF1CEE30C0BAE669B5B9EBF4559F1A0E6D4DE98C28CA71D760EAC71ED42F7E3FA780B0EF + +BlockCipher = TripleDES +MAC = HMAC(SHA-1) +KeylenCipher = 24 +KeylenMAC = 20 +EncryptThenMAC = false +Protocol = DTLS +Key = 55A76A58308635556CF6108BD5A8D8B2336647DC2BC7FF28F3D29D4E30E19CE193B29011353DDEDF72F5101A +AssociatedData = 000100000000000117FEFD0A2A +Nonce = 0332ABB1C8635D1F +Plaintext = F8C3E4C336015239651085202ECA737D6C9D558F17E8BF34B83486DCD8D0E59EFC3D67EF9F041567B35A6A2E252E8681E7062AD3F641F706F32F6C4BAF8D330F49D2239247E397913699778E50AB9639A272C66C3C043B4ADB33A9FAA3DFAD77532DF7058317B6BE64D1CDB7869545471A438DEC4763F13FF1FDE39595D17EB19C69CD12B7896166C57A9B8DB0AF46C0D94CF25483FE8F94C4BED43B77E1DF09B70FA2F35A919570F0792166C92F5A267ACE7A166EE59C69F112437948FB1A3A481B824FF0F761348C26AF07D8BA536DA87DB8149499AE4E290A094C1C7B8A7002F689420EF4E27A5047A6A7F76214F91E7B529F93096A412A220D21112D9846A77BE35559304677031275EA50AE8F9DA85AFB77189787B3C44947D75A4FC0C80CF5CB8087C4CCD57B2E9DE51A91C59C201D77CFE014CA83D6DFC0BA388C8971121E8F2E5C39C2AAA1B1AD1FA070C9A971379949B7BE0AFF4EB19088FC02902CAD208A37AD86867E6B21468D391FBCE9968947F57C472DE82CFDDE6E063B7D56E09628E660138548E176179350E2D2EF9B6675551AD0286D7E73E30AC8360BB8BE8BE5F36ABB3F701814E2085D6F4BBD9B90255A90E8032E6530E76AC25E05906A784E89CFC440CD39D47531EAE97BC9C03A6D66E991D33A0EC4410A879043881748FE40A4E828A77BFBB1C48FE6C2F44607704A443ABD0FB92C5BD8B717B0BB0856A222104EA3B5244086E5F568949278096348CDC4F99FB5D7AC3202B246B6916CF5A84690701E8BC8F42BD6E67168B1C28911C27FCC4862A3BBE52B8B107314C5C3BC8CB55E7A182C0C9AFB42EDA75B2537C76F2D8CD92DDF222E0240275E050AE9B837374AD04270ECA93CD3A9F5F293D0FB31FC9F92974988BB68DDB552E6575364ACFD23998F0CC53C835B586302E1CAAE768F7C232E10A5A94EDFF49C4A35FDFB6060E5BA97E6D8A9C911BB76244FA752BAF5A8AB91D34287B6312FF5D5A0F225D828A18C015474B61797A4210591FBC98BAEBABA709E3550B030BF8B3A024FFCA98F72D7841DFA978603F6C85EC96C6386AB574E89F269735EA90FF73B34410A783B88D0E777D9F33ED8A33EFC89ADE2574D36D7ACBCD3CDF0D99947AD830247FB472121020992DF7A0CACC5BAD48278BD641D35B75B7EACA76AE6F471A7DF1EF6398BE0BAE9B4E082032413811CBFC78330E1B79A9E80CCD67B91EB7BCB35683F0B2396079DDFEBAF912C4A4E4448DE8762691D53C2015CDC974486CE8B6E6EBA255938F11DC5676AFAF8FD2C9D1141B61BAB96F3763BF92ABCB8927DF008E95C60A92F8DCE24282DF4C83135EC1EE00DEAACC197D974B04354B94AAB748F042B03A2520586CD7480A2EF589561841D99D084AE6F7905FEB63D20598B039D573CC8E2E98D8DA000FC99D05686A067C9783E58DDBA5556D226C3D6DB61F6E0EDB4D717AB6E998DF34BDEDB7E55BAF926D6135E3CC7DADE8942A3D75B7E18143DC7C8FFBFFFD094E125E494955B3381E5F0A1DC2BF5E827EFCD092CD547A40433BBA468BEA6C8C5CF54918CCC04FD512713A9287AB3CDC9439792EFB32F448D27E5E231ADDB894033B13965C8CA79CD7F9D59A96980476A3181494BD2B8129AE2AA6C4706258835F4249FD19D3A7B6BEDFB53D4A5EC84C3D87CAC797D7599289F7F9BA9AE1AACE565A8F993CDEE46ADE3A9A82F5095249DC589A7C36F0AD34307E93563FF634B9F4C44EB7C4C918A284234D751F82B34CFF5BA9684D42A2B07327E7596BEB52CC3E0128A6F7DDE6C7F5C3BD1E09C6C3442D530452547987BA0ABE1026E24D885C2C53B5A5D35A17D68F269F691AE2711ED441BF221D3260E4A6BC9F429D8A4FD1B10F84F4AB6B4C0A6367C182A0A3C29D88EBC4802F5B89D51DBFAD517F1FEAA5F4892CD45B302A70DC9407683029A4C1D7165646197415411D54A9C94F8DF132E6263AA7351861481BC94C180553FB8E21B77D3E915C4CDF6A29DB20FE24C2D37C3D22A04224D5BF692641918313B1B5F286390543B54127CFCE9F005185F603B405A003C2B6F3D713AAD0E5137AD93DEA6A9096C43149E300C329E06723C820A1F84536BDE1AED05187CA24176C4B0B514DC1A0CDA375BAD6C8E74AF361CD98415E7B5F41F870ED6FCEE450E6DBE6F4D5D9B0F1C1A01A3A024219BAE1C451C99492E2B4A4101B1192830D6EE0CEC3F1EE099A4978FC442C4AF22ABC14B0F71484F122BED08E55A124C80A727B2ACFFB16A95078735E371E6F6D4ADB65CE16EA32C5C9771A79CFA148C2A78102E6DFC9C3862D30777D1DBE1528D196B55311BC957E4C677C3A3F483778C790EC1AEF689F8E0154030129BA28DB3D7CFAD87EDEAB4947AB16D831CD510124B9719FF1A2EC800496CAD393A25E0B941DF4D7ACB479964E9E22AD8B28CC739FE3157FD075634C35CD6A7E8B475F5251EA98CB00B2FCC5FB7DBED4E693D7F8F1373085ACCD63A3BAAD74BD372B0E58DA64D30E27402819CF97F199F658C89B179032ED2E798488800DA35BC4F00399483F672D53D305F4633BF8396362DA64C9396363646933BDC9A586F7796A2EBF1CDE4F9620B3A7B94599E6C0EE1E3081D2FF3026549D0617725DC75E701E364690C65DD6FF5B05AD1CF2E8AF3D7844FF08FF0DF4DBC6777ABBD0032FFBA1D7FCF39D7A4F28232F1546B91FE3FFAF48FA362728EEFDF7FAAD9EE6E03C25CBE830BBBA847F4A76FA78871F85DFD9E254724CA703E5761519772B5E0B243F2412B0F6C552C8CCC2750E694A6AB49841D696E56F18FA75F254DF67E1F7AA5F533A1E3946E2CE0C72C32F5FAEE5A4EDFEF589008098F0D685249F72607081C502E7C9AEC8F8D27DE5D035137304E4751BFE3A64FDFBFC77B427817FA8077E95EE0C85A9B6807A8C8A9E7A050DFDA7EEDC24EDD0CA90E26066B3705B4865261E905FE573AB5C2BBAB12759DB87EC24F363C487290AEDA87E7575583832294E62C6806D7D24654C71EA6B111559D39251D80F4B39B42F55B6D9D0E63036EA9ECD0E63A30DBF8C643278712D57AE0E37B05F2E4FC5CCD9C2056E50F843F946DF5F16B64C53EBA8FB53CE47CD1EDF9D4DCC10817BB76C58065D1E777D9B5CF2B9419F2BE0C528896B544FE45FFE1A88DE39344016CF8AFC0CA7C6BD36350689E6D4288F08BE27333EA95233368FBC1043B2D3B819D6221EBFD654A20316EBAF133D64914D523962C0BF6B57B2550269B9FB391EBEAA6C201BD0C6BCC67DB2EC1A8B161B391C122BBDE9E24C25E63C827DAB5FAFF02234772DA5EC7B333D23F18A11C0CC773D253853C32EF1D4E2AAAE9C8383AEEA0AA771C32B96524D9027673FFD296BA41DF77A30BC9A6133D08DE9753AC255B927B284FB6EE5EDDA8856FBCB6FE5B65CFE1EE9B42EAEA9984D3865277F69F56B5473FCA6238D90901E8D24FC939C811023F2466E88B6B148DCFD3AF3E669D60F62FB4BE4849104757295AF74B8F5777D0D4986B1512E959CFAF08B1C4C0216D52C28BC9853C49A529B3D1F05F7A96C23EE6D880EF5633AED59977CC333641DCD601DCFB4AA2DD9F096211E638E3208A602E82EFAD9D2BC9382282E3C76A217FA05DCBD4D4783423988404F31A27A2D95CD6456D979EBFCEA04C9C49CB6FF499DBB68F42732EC68F87D4A4A3560ED7286674DD23047F +Ciphertext = AEBD01969C20E3F262B680F2368C271E2C520FD9BC27AA269653B838B728CAEF0967A548E5D4512B7ECDB464BCB213A7FD3A6E259B17DEF2EAF3D9B7F7CA7BBD6ADCFC5A0634EC64F000762EF878637626EB66D83791AB2FB9D9775EB3A5587466EC552528A2828BF0C3FF855F5667400BDD62A0C02BF23235BF152BDE3675034529F27F863736148E766BD62E0C9E1CD47EC00BB182FBF63B1F17CD4346D21789B8365A08D95A01A820F2EFA22B856EFA352A7CC2BB36FE1600DA24B617C4DDB1A886499CF01F1F50DC05A90E7EA49804BB8D3C4A075A01226EE0DB867883A07435A167801AAAE74E4BB0D7DE1D8565E46AAE28090DBA415C23F3CEECB2B26BF6C2FA66FABA1F751A8320944DEE0FB4F56F87310A3660405A520AA5E13DD612EB0702B5EBEB0AAA80CBAC34CCAE50A7F56C82609B19FFB0479FA7BA687ADCD77B267251C90547FC506A114ADC4C10D4B8DD388EF5D33922B04787788842840E57B6E856944B4FCB9674DD24BF96C429AA6275DDC4F2B6DECD72B0561AD50206E81806CC40B7A0BD5A88203C5D79AE776ADFF9367B8C4FDC74B94BCE43989EB8C0BBBA6B5FDB900EF71CA069FE9E738918478E31239840C14CB66FFE2B7ABC6FBD1BE6F33FC1A5989494A1C9F22C8678F7156CEDDC778163DF9259CD4289A0B60963B764F6D71CA9F5EA778529B5EF4A834377D67F509F6588C639F3D14660A03F0D337D831EEADCFA437BFD897376F634C1F7E664182913FEEDB6221EB4D9299131EBA5B4823FAA3026D905A8395B04AE7315CEA731CE8BCF760F8B87A1F0A5E753F86562991ECEE66F79576581F3B979CA49A914BC39FE5D3440B4C1C2F3CF0C63F695DE9233BD3EE0DED36EE3F9B955EC48755C522ACC654E60EE5A1D8D9E34AAD1ABE4C7367616404B557DCA681B423043DD62DC2B35822A81F4187BDDB86EF214CC5106119D5E71BA2AFCE792E6F3788A1540B98C8A43E1E81CF85EE789F9ECCE58FB4EA7C2604CD4BB42D965187BBB33990ABEC8C89274984DC7525361BCA070AEE6A1924056845046FAE10DF50F5C18B00B42F7D1B2A8334D758CD93BA270FF267AA2D1E2847BAF3E21604D4010DE528E78D47374F3528634047989D70712BD3479F7E81433A6DC4D5705EBC4AD754126D89DF8348C473D512266D23527F577838F887FD1E6769FADE2A2BC486F83ED90ED4D73B08FC7877084DC2D8FD22EF459D76D482B70DEF4A636FD454BA7D63F0022E364CD74CA8BA16356C14889EA202DFAFEC57C2876A38CF804E706F1A0F32634CBDD96B6CC9CE8188F9024F92534CCE18998A858BB95DFD3F8A419B5E6F389F02FB453F2418ED38D0F0AD1745A7DA2DCB90256B3DD11036FB77E602D5D33243D6440405BBC4F54ACA8496F72AB6BA9D25F82E1B0010728C22B26326B10BC43587710B897FEE30635B12D5D0B504BEE1991C0EE1FE12D563D2BB2EA860B6760D2F28394AB95698E62428EE0D00F6AEB6503D7AF6C82F18A2BD998189D8CE5D0C0B289F01A2048B6B589DA1120439E7339540BE332A7AA831C0FF77EB5FCC89C3ED92C707DB359DE440CF620945BB749DDB476AB616D890E809CC56D31EA7776480F23912D8CA0F9A00139AE70569D0BA3AE28A139DFBAFE8D9CA8F5946BD982768DEBD3FC5CA30C7FF139C0D2606F6AF163323271719F323060F8E43A8FCE514CE2D921AC402658176C51B0D4A2996F70F87DE69D8894871B15AEBE37516B5C8549C7A96C38A501811A5E741B1979233978BBFEC0CE22FCC7DF39BD8CC8BD062015DCE4C27485069200346BF0A81E51642884D371A1600AD942AFDFFF3E645E790ABCB57613ED64AEF9B15CF365080DA95CF8C3DD2F23EB8DA0938DA5B59F28D7ACF72D1E16CB9A2BDD5749DB62BE3C44FF4A8018432F6A7F55E363E95B112BF0E6D50872B7B2EA62829FDB478EBF6CCFE7416C3639B5310628644ECE3FDB4603804987ACF2679DA94637BBF3A96012CAA577C8D7CD4BBB1BFBD312633A7B743DD18D7BF6E809F6D1B05D55F31F0A332F44D69E09F96FB73F47C2BDB0F5A616AC5A7862A7777221ECCDB2204BF43C77B93AD78B18DE7A65B6FAE513156611BFC591BB420628CE2F63529F21F4A89AAEA018A58897800936DD91C7029F6C9BCD8C0E4BD80F8E7741B2C99E199A7C8F583B83372C93E8A76B5FED6B05E95AC5F749C52B672ED21DB462A5B59AE6CB8FAC194A3F7C0C3EC130D1A1EC495414C883D9FDB6CDCED80BD7A9741CC2EC8CD9A2F9510072A54818A32B5A833FA2ABDE48C42286C80A64A19075FE2D15213E3BD6E6AA836B7AEFCF130ED2B37C499E3F5ACE439FFEBEFF2DCE11297CE75CBA1A0A7AA8F07371D554A0422D5473EF95C453CC3091B3A804B8BC01091BA0FB195DF75BD5BC085079E93338219A291C81AE0034EBF02C6EC1ECA6F498C73368005BE21B7418D9B6EF83F4DAB21AE30D104D2A2546D4B978FB0A677CD85901E688138BDE53E66935F279096638852142E29F143A97A9981478E000295AC0B865E728C016DE991F1095BA1CB1D93C5023B034D9AD25CABEB7786C61BA1CD1F2479920D8FDFAB7D8E775A4A1987B81EB46470512AB3536B54FE841728963CEC43B4EB868E9F3B0F71013FC1371F4E5022E90D529750CAD2C9F2907E59EEA96ABF9AC50AB6E9F57EAE61BD6F7ECB7A52827307FAB67CD6866E86911DA6C896FF750A220CFC32F3FB6F761524DF29F4E5667B5BE9C0E2505CE3269043DDA75703C11624DDCD0565DCBFF7DD27C7CFB3E12B10BBAEF56852DC705E2EB24297CD430500AD902F504F8FE93449CAC8EDFDB280F8256ECFB5AEDF34A156A41FD6157D8DF780B49D18E8EC5E18211C6EA86B0029F2EBBBF8860EEB3EB2CBCA7E92A204C21F6059735B1B1DE0989616039D65AB3B896EC14091343745ABA56F48175A1937F3ADEFBC45EC590816DE74A95DC96B18454AC83A6F059C336C6D3CF1E114605BC29FFFC3FA36AF6878B79FF3A18C97F2544E69A6C1433832E0755BCECF49398FD677B4FBC438A8D20BAC1E245C411694033FF4D6A92A778E122D2C38ED1AC612DEEA10EFC4E66383135F702E0B4407F3671A064812D91B9AF007FFADE12724CAF56303A2975FBAB875F533C71AFCD2E56D439FBC9D458475C3E63256F0D31315155D8B591898D5499AFE9DB1E1834C93A00787EB4F53F22E570E8A5D25832FEB28F7DD2E08AD15733D30EDA0F1FC6F21832E1F274164A3A4FA22DED4C09155B7FD80555AFAE317666A0E1D1D5932B7DD20313369D0D285AF0069BF8276042B1D80BE207582D91E205472EE59670762E39A2C88F3A11184D53F5941E31F73F397E540473CE27FDBA203FAA931834BF8A113F743EEAAE8A399A38E8AE9D0C4801EFA37984DC981B6C9CC14ED3310F1A7D4CC59C56AD39B8C09CD54DF2518089BD412F654C83F0D1DEC57BA401653F6EC6A13EA15E94869DE8A2AA25241550E34B2D2ECB1D96E5DBE8930181AECB3946C4DD19898387BAFF9A28CFD52BD39CDCA4CF65397BD812B656C34F5D705C87FE9DA1664785E778DBB27EA18E249D510F38B4E2FB57370D05EE73FEEEB01CD095D58E53D0476BDFAE9FBDD4BDFF338EC9CD523F398B054BDD902A961CF0140FAE1717945B0DA1D44EA8813EE3AF3AAD5546A1BE8F20DCCD4F531397509F10FFF65DE4B7BD4B2C29A627FF6E688150877156 + +BlockCipher = TripleDES +MAC = HMAC(SHA-1) +KeylenCipher = 24 +KeylenMAC = 20 +EncryptThenMAC = false +Protocol = TLS +Key = B46D04E9814E389BB971B4142687AC75915A4219E28E7E53C488C99D0D08520D4819B8A08730DA08FF2B5EBC +AssociatedData = 00000000000000011703030580 +Nonce = 49970915ACAA4D05 +Plaintext = 014677E1C42182BD4B62A9099C25629896A514F39FDEA56EEA30617DDE86BCCC158472A07C357FF909C478F490ADC1FB35FE142FF916F39CE26A6CAE41CABD6C91B151F676139BC65430513907F078D52E3BDADB9EDC4A96686451F5FE91AEDFF5BC34D7E0C7865DCB162F92CC0193A13A523E74F548101C5297C4921AF5522734A0AC4A5B735E0679F980C81B038CEC84478987D7801DFEE48DEAD80722DA5AAF6CB966F645B119DD6820B79F294C51A43174B48BBBB51CD5DE562AAA52EA9C373BCC4F307B7DFDE4065A4774B7307CA43227A7C84191654A340EF854D293200E3DC43CF966312CEC82ED7426FF0029FE823A20B568D5DBD84786C5CBFB592AE5ACF374B064AB2DC29E0347AE64F8269B65B5ECEA98198D85E1A22441392E10DED7194C25E33B97A32C3ADA7A5AC24DD42F12E96E47639BC6DBB7B659077433891A662A97649D143D0C9E586463788A72463805B9FD2A35811D892E28F0FD0AE9E37C84B576015CAE2FABF9B812A6DBB020803FB352539B0AA14C4C308F0C176FAE69E1A0B6063582964E0A320B444A3640B2A5B2EA361E2870A6E5658F54EFFC09AFD6E5D5B979B853E3E2FC01BEF41F3D08537E7F991D11A3AAD82BABF736E2913E0A8692990FBC8637EEB2B7EE05F3BB287A4FD7B2086863DD1A55AF87A0E2F27633F1BB94F06C6186A660FF1EB9AB6F2D56CBC9276044E933AB267407F12EEA2916052F092315247D9481BD0A5CB31E9D360C3C10B52A7E119E52E8E9FD6746958A13DEC7C17621375400E4DB4AE29E72779727F2A6107B4C14FB0310A99FE36D74D9C70BF2BBB9428E3A772CF082D415886491C4E45C492640B7D3CDE756AFD0CC87F985ED815C9AFDF88907C84DB45CE8DB92ED2FE16257658870E5BD7FA54E96BD9252FBD78DAB6B783DE7606C439223D3504256E74E00D3E108883E7AD3E9EA89C301743ADB52B264C7B65478986ED894030B3B22A7B8EDA6D45A8F1A5611EC6CC991C097E8DCBCD768CF4E7CD619B5D4F1FDCCB81581262B206CE3A05CA5D455F570574B6503835E75FC0DF0322B79BD71400B3E51D2FE3246427EBD21F7E6A3A47CC12513F611F350775BBEF3ACF0E4EB6906BA268E09F0B3CDB692F3C67A2546049660C45181026DEC143F6C16FA51D67FDFA16B0AE807E071ECB12D44F51C5BC67EAC581FFC664E73253D0450847DB9F9C5D9090A4D8C59FF971C3F11CE3671F13225BFDDBC6C87998AF12EB48D4D2ABDF8B6FBC2FB01BABA3944A43D8A9328237959FD790CCEBA564F047109221BD120C80048D8C35AAD55670AE949C0C22D6A2B45204E45B79B169091618761AC65EB96EA0A5F88FBECF2C6BFAD2415C3424630564B28D2C4ECB47820A3D87939AD53D11A6BDC3BB6442208269B313F0533A4826BAF442CFF817945ABEC14A86980FFFAA6A243DFD090EAF270703E0669E2E326D08C50DEE43E20419C02C23B53C605A114F9F986FCBABB583EFB8C3E3C3CD136433466825F14D3CACBB9DD927F74EB6558CAD2515DBD771EA118CDCE3F40ADDAF20813C2CB072D4B83F6CB19E493A8F7D87D137386A472391BB41FABA0FF254A79785557F32A39407CB0664FEA39A3166A8B702F7C8C6DAC9ECB14DD019658CDE606960C13A07036E0E9F6C1698DB193527D0ED5B19E9B8B8A6CAD786F35D3BDB743B5BCD69CBEC849B9330FA7C71A4F47ED80C809159FFBB2B359A46A1B9780DAD1FC4F58627CA2C04346E65C2D93386E84CC7AD83D8A2F56CEF996E397108FAB5F2AC124447581BEBDC704AF17444F5CFA666EE071E06B29F8DE9437081E73F8AF691B36A5E4312D6BBF3838EBC2A517183234561C92054B80C7F437DE0EC2FE925777F8F8728D67A4A65EFF21155814B98470DCB9035F4EC3E61C2296A0266BEAFFFF47C13B8134CC65667B40B19A39C422D386D306ED332DDA83A8C101555DA6DFEE6E93C90BCE57534D92DE7FAADB324020D805DE35 +Ciphertext = 4FF227E1C933B6BA3B502A1142D60B961C2881E27B06706910B98EE06C9CC7DE536466767031605FA2AA0E21A0119E20798AA5AF34FA566F223593ACFF26291129D3D2DE84BAEFE85C22E0D489C45E88496C9ECCDEEB4071FE950B98E29D865A594932859344439D8546E98BE83E17C6BC20C4ED2FDC8040CF31E458A2830BD4D0E2941837907AC632524A5DE6229BEFD385EB250CC65C79E16CE0AAB9BF7A04B3009679FF74FDA4B7B494955A106874C4B082BDFD73E66F8CFFDE9D31C370190DC0EC21A83A4B5E237EE53BF41E0A7E9E08ACBE72074166F8E7611E7F06D54BBA56F78C1791586A6F2F56A00DE1B26E6E52C6000DD9CFF8BCCFB629892207027B67F129F11444DF52A9A13BA6BF7F6EA822189F23903573F7263BE9AF7E9030C59F45AF78D8639870EEF18EAC4CE0BDD1DF83D5AC6BDB76363BCAEC7368A09DDEE654EE845C13E76934A80019A51A1D72C2E32D8A839D2616CB402B2BE30280EC3EF22459BD7BAB4134644F00339A7F082C78144C80000893208F997BB084A78686EB8CF7ABEC0C76EDBA3DAE73E4300FE2C7649E3179F57E691F5E16B4EC7448B8D3F15B2AD8FCCE26775155789C1ED35CEADAA3CF306F2582E84702A5E0EF01C9CB3521DCF840633EBC37933A66D8F585B03851946B84D8B2F479FA9344B1B1679FCC1CC77284B1C2828CBB644B595E016C256217CAFBDB740606CE6091A171FED293AC4F0A43CAED2B6E54F72A6C9F349009EA000B35D2B96D29FCD0502A51142C5A5E3642CB52AB5AA18F7B66D971996ACC8F8D3FCAF17E3D63DE776FFC0BC83D05ADA26FAF2719524253BD1E5855A9E34EC544553CAEA5493A7D7A7A161D8AA9667F3CABFD4ED58B3F525E4ACEEFF4F1983AEBF3E0B3DEED2B66C1F1373CDF3A97008BA9F1A6F5B8465778375C3263EF4BE4E1D943A478C2BD7DC63A13EFCF2CB3AEE65E0422750A905C75C8E80388F084675CD7FD16548EEDBD36B23A4B6A59299595D572516DA3BC14A999DD3CE9F78D25EB021F2948CECD975236B4D32C616B90FC4639388F92EE34E6CEDD8FED75C675E048C3A4AAF2822A8674B39AB542003B33ABB174E0F078C7D13F75D722B9E5EE8B5EC96BA36F32C451F3E4152E69E499E3C6210F5088407F20BB33E53F9D2DF301FA539AF8A6163D929B81B3395DBE31EAED388EDE5CF73D4D14D73EF9F5E9B17AF770CA9E6F639E28C9F994571DF5920CE016348A56D5B78BC9AC1665B4AE3A51AA3A6A4A2C0312285A4543009D3E18806EB3C0208A4A0B5343D62DA7985B258FC70469321C874695E22DEBCD3FFE3D26C9EF37567F26EA68173E4FF095AB607319E18205917C75BA80424360E9A71ACDC578692FA88B08CA8DC51250F6564190C846133F4D7B12F8899350221E986BD7F3E6CDB44FFF4B4D90BB967AB587DAC8A0B136F95A86D79AF907883AB03672B072C9B799EEE630A83CBA8C8639E0B440C1BF82B80CE6D774D9F81FF31487B0F5D062D0E7953EEEBE7FA230BF439DAE95225B5BDEFAF3B433AD72228F7066E03E02CA451F2DB63F3C9E2C6FC0DD9C20A811A38280A725FC57A9549794FB463C37AC91A29ACCCFB9FB35A98AF232987917750A1842616E0F9DDAAD393728F58525907BD3EDD10B67BC2E79AF4C152172BEBFA38A606AA8042091F32C58BB9332CC36D1230ADCF4691A029FE622F1C8DC47E04708A021773B4B830902B5D41655D7CA23AB96C5DA5D14558237D4633294DC9B0FC6C7A79312F297D390D6604CCABFEF0C144E205AA5C727951901D0B636EDAC9D5A8B239B2E2B032D48D95CA74F6CB731F704B0557CA0E5F689B2603BBA898A7FDEFDCE7AEB232A26B9671606C4D739AE357B2D2E6D949C3AD7DCD9881D004994CB9429EF0FE721B2566EB78AD4D18531DF2A6CA577C761E047B72163D392D03E0C8666AB7EEF1E3DD3CB5B4D1223F0CD76ECDF5382DD8FAF4CBA89DAF8F7E1BDF583ED06380D71BEE0CD90E2EAF69F5A8FEE89E50E57AB28371D99CCDD52C401 + +BlockCipher = TripleDES +MAC = HMAC(SHA-1) +KeylenCipher = 24 +KeylenMAC = 20 +EncryptThenMAC = true +Protocol = DTLS +Key = 01457590EDEC8AC9F78BE81CE807FEBB47D8E7B89437C0C8B6C4C7CAC8C707CC0F6BEB738731A27A5072AABE +AssociatedData = 000100000000000016FEFD0018 +Nonce = 0F3B6A3E59D24BF3 +Plaintext = 1400000C000300000000000C4D298448BFAD0DA49345D771 +Ciphertext = C80945B53DAF2A9474BAD0CBBF48FEBBD88ABED7EBA5609E7DBE53F8D87866B3EB24BD957EE8EE7A46D7BE100CFBEAAE74B5EC63 diff -Nru botan3-3.7.1+dfsg/src/tests/data/tls_extensions/generation/key_share_CH_offers.vec botan3-3.12.0+dfsg/src/tests/data/tls_extensions/generation/key_share_CH_offers.vec --- botan3-3.7.1+dfsg/src/tests/data/tls_extensions/generation/key_share_CH_offers.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/tls_extensions/generation/key_share_CH_offers.vec 2026-05-07 01:38:28.000000000 +0000 @@ -27,16 +27,16 @@ Expected_Content = 0045001700410486E8631CECD233F133F6FC99156D8BB504DB91DEC753C31AEA8AEC3C874221653C986F7B1D00FD4EBFD3F48BCC2CDE3E9C94442B4F53BF2F906B3ECEE6EA12F0 Groups = ffdhe/ietf/2048 -Rng_Data = 49af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea5005 -Expected_Content = 010401000100534C002FD3A1C9B25B664DC8CCAEB34857CABDA5BDF1EB5B99EEB8FF689EC6761746B54AA35B3AEECDA7708E0C4B046EBE6E275B5C4E1C02351DA5F432AEEF93DF3E3727CEE4868041A1CF5E35DF73750AA62D9B91F4785A2F7DC4D5304BFFB339B1193BDE6D0EE6F7698BD4C2871192A209ED34594B2A46925F064FA25CC56B858A05C205171DD7C7119FB8D27AAEC0CFE301F2E7F3AC7B4EDA614164F05E5AF88DAE6F07DA0455EFF704A83E496E86625CBADBA8DA9AC22EE9337AC891AC2F9F46A73BB3CDFF21DC9C2F3B120ED792E9C12BFC08E27854FD5F657B8E9EFC65549F82FF5F64C718A6829026F1D027F24F7296BD22038230EBB2F629B6885267 +Rng_Data = 49af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540 +Expected_Content = 0104010001004854C74CDEB35C2DD056CC4F2CBF21C2E5A2E09803B7C7B9E040621CF1A9DA9D2EE334B0A8A6EC52B534C223EB14A14E41EE4BC2FB75C95292035EA869D9D5D1C25C648E55F5D6E0885257F971C981ED14B4FFF0C54D1C703AEFE72FB2B2721D52649F21D5E7F956EADE5BE93530AA8FA47D4970ACF6DCDD56F010D59549C510C9C3B8EF4E9F4D96B23AB450B32762EC7729CD95328F3F53B841A6555AFAEF8E5F1C7533AEBE75BE62AF07ECE1F1EE80D72835E654EA9BECE03BAFD25BE9F1EE3C027F55947EFF34AC0BC9C43E32944D44FCEEE462E91680FF7648B95BE4FF7CAB60A9AB66775BBD8494CFFF1ED6F7338124C9E03C9A972247B36FF6AC72C078 -# this test data has four \0 bytes at the start of the 'public value' +# this test data has two leading zero bytes in 'public value' # RFC 8446 Ch. 4.2.8.1: # ... encoded as a big-endian integer and padded to # the left with zeros to the size of p in bytes Groups = ffdhe/ietf/2048 -Rng_Data = 317FEC44E299183D1A17F3F699E036620852EE1FA2C3B3E549900779B9CDC204 -Expected_Content = 010401000100000046016B7B5EB1A64DE87235279C07D3C47686454A9D6089D460FB6C0DD2F3DB7D2EF252A00DD6F1D432B4BB63FD757C2A9DBBE6C497C78A7C765C3F49B711D9E1A58199AB5DE61C963AA522DB8313DD39115BCF207485EADB816CCC08070CB8B200C25D8ECC0BD36ADBE0AAF278A0CCFE48A9BA7098B53AF2C3EC55147CE7114FAF1084A21743A3DB29E05B5874CAE917D7E5449478A37066B0D2A9F00E29777962CC7C3C3CE15F8C3DB118F69628FF71565E242B476407F55DD0B0DD9003134992E24ED52B45DEC5C4AD98300B4C0767A78C4612C1B3D1430060E56280942FA77407B282CC21349030DFD654EAFB76B0B63FAAA1814D0C20248AE13B0D1E +Rng_Data = DF6FCF9D290232720DA33E6AA1AF27F1C2839F744A8A20DE6EC48DDA +Expected_Content = 010401000100000004D506A10F44DBC242F62F2952322E4CB697D3B9782D874DE2FC75C36B9476776ACFCCE977D5D15F6762C4326A81EF86281536B17ECCDF80B4052B568DCC4DD5CED6D7D360DF80EDFB8667B64A2E063889E1A4D06E47370DFABB265A675389B7A8E3A7F60AD552ADD106E4C292413CEDFD46F7CCBAF66F072C63E7060060518273B931BE3C979C8EB5233C45B6360071AE27EB23CC54D86746B2A93803B12AFE463738656E7CFF92CA4E823D3C76DA981C207C006777CE7EAC7B85B61C53F56DA98E9E39C6A3752C0E9525E131899B58BEFF04456DAA82AD6ED8AB804B64D47E1AD4C998DED59F0265776453647CFFAD5C67522CE170BD24811185255F5A # OFFER LESS GROUPS THAN SUPPORTED @@ -57,13 +57,13 @@ Expected_Content = 0069001d002099381de560e4bd43d23d8e435a7dbafeb3c06e51c13cae4d5413691e529aaf2c001700410486E8631CECD233F133F6FC99156D8BB504DB91DEC753C31AEA8AEC3C874221653C986F7B1D00FD4EBFD3F48BCC2CDE3E9C94442B4F53BF2F906B3ECEE6EA12F0 Groups = x25519 ffdhe/ietf/2048 secp256r1 -Rng_Data = 49af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea500549af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea500549af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea5005 -Expected_Content = 016d001d002099381de560e4bd43d23d8e435a7dbafeb3c06e51c13cae4d5413691e529aaf2c01000100534C002FD3A1C9B25B664DC8CCAEB34857CABDA5BDF1EB5B99EEB8FF689EC6761746B54AA35B3AEECDA7708E0C4B046EBE6E275B5C4E1C02351DA5F432AEEF93DF3E3727CEE4868041A1CF5E35DF73750AA62D9B91F4785A2F7DC4D5304BFFB339B1193BDE6D0EE6F7698BD4C2871192A209ED34594B2A46925F064FA25CC56B858A05C205171DD7C7119FB8D27AAEC0CFE301F2E7F3AC7B4EDA614164F05E5AF88DAE6F07DA0455EFF704A83E496E86625CBADBA8DA9AC22EE9337AC891AC2F9F46A73BB3CDFF21DC9C2F3B120ED792E9C12BFC08E27854FD5F657B8E9EFC65549F82FF5F64C718A6829026F1D027F24F7296BD22038230EBB2F629B6885267001700410486E8631CECD233F133F6FC99156D8BB504DB91DEC753C31AEA8AEC3C874221653C986F7B1D00FD4EBFD3F48BCC2CDE3E9C94442B4F53BF2F906B3ECEE6EA12F0 +Rng_Data = 49af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea500549af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea500549af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540 +Expected_Content = 016D001D002099381DE560E4BD43D23D8E435A7DBAFEB3C06E51C13CAE4D5413691E529AAF2C010001004854C74CDEB35C2DD056CC4F2CBF21C2E5A2E09803B7C7B9E040621CF1A9DA9D2EE334B0A8A6EC52B534C223EB14A14E41EE4BC2FB75C95292035EA869D9D5D1C25C648E55F5D6E0885257F971C981ED14B4FFF0C54D1C703AEFE72FB2B2721D52649F21D5E7F956EADE5BE93530AA8FA47D4970ACF6DCDD56F010D59549C510C9C3B8EF4E9F4D96B23AB450B32762EC7729CD95328F3F53B841A6555AFAEF8E5F1C7533AEBE75BE62AF07ECE1F1EE80D72835E654EA9BECE03BAFD25BE9F1EE3C027F55947EFF34AC0BC9C43E32944D44FCEEE462E91680FF7648B95BE4FF7CAB60A9AB66775BBD8494CFFF1ED6F7338124C9E03C9A972247B36FF6AC72C07800170041044DA674B121DF2EBD2A5A3174706E80F7A9B2D16ADC1F19FF47D9299DE5649895E182AE8CCA8C97E50F90C316DC2CFEC54D230D004492AC834E984B321190C8DA Groups = x25519 ffdhe/ietf/2048 secp256r1 Offered_Groups = x25519 secp256r1 ffdhe/ietf/2048 -Rng_Data = 49af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea500549af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea500549af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea5005 -Expected_Content = 016d001d002099381de560e4bd43d23d8e435a7dbafeb3c06e51c13cae4d5413691e529aaf2c01000100534C002FD3A1C9B25B664DC8CCAEB34857CABDA5BDF1EB5B99EEB8FF689EC6761746B54AA35B3AEECDA7708E0C4B046EBE6E275B5C4E1C02351DA5F432AEEF93DF3E3727CEE4868041A1CF5E35DF73750AA62D9B91F4785A2F7DC4D5304BFFB339B1193BDE6D0EE6F7698BD4C2871192A209ED34594B2A46925F064FA25CC56B858A05C205171DD7C7119FB8D27AAEC0CFE301F2E7F3AC7B4EDA614164F05E5AF88DAE6F07DA0455EFF704A83E496E86625CBADBA8DA9AC22EE9337AC891AC2F9F46A73BB3CDFF21DC9C2F3B120ED792E9C12BFC08E27854FD5F657B8E9EFC65549F82FF5F64C718A6829026F1D027F24F7296BD22038230EBB2F629B6885267001700410486E8631CECD233F133F6FC99156D8BB504DB91DEC753C31AEA8AEC3C874221653C986F7B1D00FD4EBFD3F48BCC2CDE3E9C94442B4F53BF2F906B3ECEE6EA12F0 +Rng_Data = 49af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea500549af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea500549af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540 +Expected_Content = 016D001D002099381DE560E4BD43D23D8E435A7DBAFEB3C06E51C13CAE4D5413691E529AAF2C010001004854C74CDEB35C2DD056CC4F2CBF21C2E5A2E09803B7C7B9E040621CF1A9DA9D2EE334B0A8A6EC52B534C223EB14A14E41EE4BC2FB75C95292035EA869D9D5D1C25C648E55F5D6E0885257F971C981ED14B4FFF0C54D1C703AEFE72FB2B2721D52649F21D5E7F956EADE5BE93530AA8FA47D4970ACF6DCDD56F010D59549C510C9C3B8EF4E9F4D96B23AB450B32762EC7729CD95328F3F53B841A6555AFAEF8E5F1C7533AEBE75BE62AF07ECE1F1EE80D72835E654EA9BECE03BAFD25BE9F1EE3C027F55947EFF34AC0BC9C43E32944D44FCEEE462E91680FF7648B95BE4FF7CAB60A9AB66775BBD8494CFFF1ED6F7338124C9E03C9A972247B36FF6AC72C07800170041044DA674B121DF2EBD2A5A3174706E80F7A9B2D16ADC1F19FF47D9299DE5649895E182AE8CCA8C97E50F90C316DC2CFEC54D230D004492AC834E984B321190C8DA # OFFER GROUPS THAT ARE NOT SUPPORTED # expected: unsupported groups are silently ignored diff -Nru botan3-3.7.1+dfsg/src/tests/data/tls_extensions/parsing/alpn.vec botan3-3.12.0+dfsg/src/tests/data/tls_extensions/parsing/alpn.vec --- botan3-3.7.1+dfsg/src/tests/data/tls_extensions/parsing/alpn.vec 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/tls_extensions/parsing/alpn.vec 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,32 @@ +# Application_Layer_Protocol_Notification (ALPN, RFC 7301) wire format: +# - protocol_name_list length (2 bytes) +# - sequence of: +# - ProtocolName length (1 byte) +# - ProtocolName bytes +# +# RFC 7301 3.1 specifies ProtocolName protocol_name_list<2..2^16-1> +# (i.e. at least one non-empty protocol name). + +[alpn] +Buffer = 0003026832 +Expected_Content = h2 +Exception = + +Buffer = 000c02683208687474702f312e31 +Expected_Content = h2,http/1.1 +Exception = + +Buffer = +Exception = ALPN extension cannot be empty + +# protocol_name_list has an inconsistent length +Buffer = 0004026832 +Exception = Bad encoding of ALPN extension, bad length field + +# empty protocol_name_list +Buffer = 0000 +Exception = Empty ALPN protocol_name_list not allowed + +# valid list, empty protocol name +Buffer = 000100 +Exception = Empty ALPN protocol not allowed diff -Nru botan3-3.7.1+dfsg/src/tests/data/tls_extensions/parsing/cookie.vec botan3-3.12.0+dfsg/src/tests/data/tls_extensions/parsing/cookie.vec --- botan3-3.7.1+dfsg/src/tests/data/tls_extensions/parsing/cookie.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/tls_extensions/parsing/cookie.vec 2026-05-07 01:38:28.000000000 +0000 @@ -11,8 +11,27 @@ Expected_Content = 01020304 Exception = +# Empty extension data (no length field, no cookie value). +# RFC 8446 4.2.2 requires opaque cookie<1..2^16-1> so this is illegal. +Buffer = +Exception = Empty cookie extension is illegal + +# Truncated length field (only 1 byte) is also below the minimum. +Buffer = 00 +Exception = Empty cookie extension is illegal + +# Length field present but no cookie data following it. +Buffer = 0000 +Exception = Empty cookie extension is illegal + +# Length field claims zero, with trailing bytes. Buffer = 00000304 -Exception = Cookie length must be bigger than 0 +Exception = Inconsistent length in cookie extension + +# Length field claims fewer bytes than are present (trailing data). +Buffer = 000103040506 +Exception = Inconsistent length in cookie extension +# Length field claims more bytes than are present. Buffer = 000203 -Exception = Not enough bytes in the buffer to decode Cookie \ No newline at end of file +Exception = Inconsistent length in cookie extension diff -Nru botan3-3.7.1+dfsg/src/tests/data/tls_extensions/parsing/key_share_CH.vec botan3-3.12.0+dfsg/src/tests/data/tls_extensions/parsing/key_share_CH.vec --- botan3-3.7.1+dfsg/src/tests/data/tls_extensions/parsing/key_share_CH.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/tls_extensions/parsing/key_share_CH.vec 2026-05-07 01:38:28.000000000 +0000 @@ -18,4 +18,8 @@ # not enough bytes in the buffer to decode the extension Buffer = 05ed413804929e -Exception = Not enough bytes in the buffer to decode KeyShare (ClientHello) extension +Exception = Inconsistent length in client KeyShare extension + +# key share with inconsistent length fields +Buffer = 0004001700045b5b5b5b00 +Exception = Inconsistent length in client KeyShare extension diff -Nru botan3-3.7.1+dfsg/src/tests/data/tls_extensions/parsing/key_share_SH.vec botan3-3.12.0+dfsg/src/tests/data/tls_extensions/parsing/key_share_SH.vec --- botan3-3.7.1+dfsg/src/tests/data/tls_extensions/parsing/key_share_SH.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/tls_extensions/parsing/key_share_SH.vec 2026-05-07 01:38:28.000000000 +0000 @@ -14,4 +14,4 @@ # not enough bytes in the buffer to decode extension Buffer = 001d0020f0dc1c73b9de09ca9a65dc7565b06e698c0a2ac27f5240026e56c5b2f8d88d Expected_Content = -Exception = Not enough bytes in the buffer to decode KeyShare (ServerHello) extension +Exception = Invalid ServerHello: Expected 32 bytes remaining, only 31 left diff -Nru botan3-3.7.1+dfsg/src/tests/data/tls_extensions/parsing/signature_algorithms_cert.vec botan3-3.12.0+dfsg/src/tests/data/tls_extensions/parsing/signature_algorithms_cert.vec --- botan3-3.7.1+dfsg/src/tests/data/tls_extensions/parsing/signature_algorithms_cert.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/tls_extensions/parsing/signature_algorithms_cert.vec 2026-05-07 01:38:28.000000000 +0000 @@ -19,9 +19,9 @@ # incorrect extension content size 0 Buffer = 00000304 -Exception = signature_algorithms_cert length must be bigger than 0 +Exception = Bad encoding on signature algorithms extension # incorrect extension content size 256 Buffer = 0100040104030804 -Exception = Too many signature schemes +Exception = Bad encoding on signature algorithms extension diff -Nru botan3-3.7.1+dfsg/src/tests/data/tls_extensions/parsing/supported_groups.vec botan3-3.12.0+dfsg/src/tests/data/tls_extensions/parsing/supported_groups.vec --- botan3-3.7.1+dfsg/src/tests/data/tls_extensions/parsing/supported_groups.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/tls_extensions/parsing/supported_groups.vec 2026-05-07 01:38:28.000000000 +0000 @@ -24,3 +24,7 @@ # incorrect extension odd bytes number Buffer = 0003001700 Exception = Supported groups list of strange size + +# empty list (RFC 8446 4.2.7 requires named_group_list<2..2^16-1>) +Buffer = 0000 +Exception = Empty supported groups list diff -Nru botan3-3.7.1+dfsg/src/tests/data/tls_null.vec botan3-3.12.0+dfsg/src/tests/data/tls_null.vec --- botan3-3.7.1+dfsg/src/tests/data/tls_null.vec 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/tls_null.vec 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,32 @@ +# See section 6.2.3.1 of RFC 5246 on how to derive fragment +Hash = SHA-1 +Key = 168235432ABFBC5630F12EF123730dE983CDAB37 +AssociatedData = 0123456789ABCDEF0123456789 +Message = 9876543210 +Fragment = 9876543210335007a1e2fb2ed53a698c44be7a392f2fb0b177 + +Hash = SHA-256 +Key = 168235432ABFBC5630F12EF123730dE983CDAB37234988DBA238EFAB83F6301A +AssociatedData = 0123456789ABCDEF0123456789 +Message = 9876543210 +Fragment = 9876543210f4e82b56999505eed8139f2df51a9ff13dbbd2c0fc042e20cca8027df69fb513 + +Hash = SHA-384 +Key = 168235432ABFBC5630F12EF123730dE983CDAB3773bCA03DEF93645F73945789034BCADEF23409579245620352525567 +AssociatedData = 0123456789ABCDEF0123456789 +Message = 9876543210 +Fragment = 9876543210976bc77f2a53309b2cad42fbf83919f381b4ec0c17fd02b81fabe6edad9c543beccdce1d6727cacabb5cedc3c530db45 + +[InvalidMAC] +Hash = SHA-1 +Key = 168235432ABFBC5630F12EF123730dE983CDAB37 +AssociatedData = 0123456789ABCDEF0123456789 +Message = 9876543210 +Fragment = 98765432100000000000000000000000000000000000000000 + +[InvalidAssociatedDataLength] +Hash = SHA-1 +Key = 168235432ABFBC5630F12EF123730dE983CDAB37 +AssociatedData = 0123456789ABCDEF01234567 +Message = 9876543210 +Fragment = 98765432100000000000000000000000000000000000000000 diff -Nru botan3-3.7.1+dfsg/src/tests/data/utils/dns.vec botan3-3.12.0+dfsg/src/tests/data/utils/dns.vec --- botan3-3.7.1+dfsg/src/tests/data/utils/dns.vec 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/utils/dns.vec 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,64 @@ +# Test for check_and_canonicalize_dns_name + +[Valid] +DNS = localhost +DNS = localhost.localdomain +DNS = a.com +DNS = a.b.com +DNS = example.org +DNS = a.longer.example.org +DNS = sub.domain.net +DNS = best.domain-ever123.io +DNS = test.co.uk +DNS = foo.bar.baz +DNS = 0.com +DNS = 123abc.com +DNS = a1b2c3.d4e5f6 +DNS = a-1-b-2-c-3.com +DNS = A.B.C +DNS = SUB-DOMAIN.EXAMPLE.NET +DNS = a1-B2.c3-D4 +DNS = x--x.com +DNS = xn--test8t.example +DNS = MixedCase123-Test-123.COM +DNS = 123.456.789 +DNS = a-b-c.d.e.f +DNS = * +DNS = *.example.com +DNS = this-is-a-valid-sixty-three-character-long-label-test-is--magic.com + +[Invalid] +DNS = +DNS = . +DNS = -bad.com +DNS = bad-.com +DNS = down.-bad.com +DNS = down.bad-.com +DNS = really.bad- +DNS = really.-bad +DNS = .startingdot.com +DNS = endingdot.com. +DNS = inv@lid.com +DNS = surprise!.party.com +DNS = sequential..period.com +DNS = sequentialperiod..com +DNS = too...many.dots +DNS = ..thats-a-lot-of-dots.com +DNS = spaces not allowed.com +DNS = whitespace\tcharacter.com +DNS = invalid@character +DNS = invalid#character +DNS = invalid$character +DNS = invalid%character +DNS = invalid&character +DNS = invalid+character +DNS = invalid=character +DNS = invalid?character +DNS = invalid!character +DNS = invalid_character +DNS = exteñded.ascii.com +DNS = (parentheses).com +DNS = [brackets].com +DNS = {braces}.com +DNS = this-is-a-very-long-label-that-exceeds-sixty-for-characters-test +DNS = toolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtool diff -Nru botan3-3.7.1+dfsg/src/tests/data/utils/ipv6.vec botan3-3.12.0+dfsg/src/tests/data/utils/ipv6.vec --- botan3-3.7.1+dfsg/src/tests/data/utils/ipv6.vec 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/utils/ipv6.vec 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,60 @@ + +[Valid] +IPv6 = 0:0:0:0:0:0:0:0 +IPv6 = 0:0:0:0:0:0:0:1 +IPv6 = 1:0:0:0:0:0:0:0 +IPv6 = 2001:db8:0:0:0:0:0:1 +IPv6 = fe80:0:0:0:0:0:0:1 +IPv6 = ff02:0:0:0:0:0:0:1 +IPv6 = 2001:db8:85a3:0:0:8a2e:370:7334 +IPv6 = ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff +IPv6 = abcd:ef01:2345:6789:fedc:ba98:7654:3210 +IPv6 = a:b:c:d:e:f:1:2 + +[Invalid] + +# empty / pure separator +IPv6 = +IPv6 = : +# "::" is valid and tested in ipv6_nc.vec +IPv6 = ::: +IPv6 = :::: + +# wrong number of groups without "::" +IPv6 = 1 +IPv6 = 1:2 +IPv6 = 1:2:3:4:5:6 +IPv6 = 1:2:3:4:5:6:7 +IPv6 = 1:2:3:4:5:6:7:8:9 +IPv6 = 1:2:3:4:5:6:7:8:9:10 + +# two "::" not allowed +IPv6 = 1::2::3 +IPv6 = ::1:: + +# trailing or leading single ':' +IPv6 = :1:2:3:4:5:6:7:8 +IPv6 = 1:2:3:4:5:6:7:8: +IPv6 = 1:2:3:4:5:6:7:8: +IPv6 = ::1: +IPv6 = 1::2: + +# empty inner groups without "::" +IPv6 = 1::2:3::4 +IPv6 = 1:2::3::4:5 + +# hex group too long +IPv6 = 12345:: +IPv6 = ::12345 +IPv6 = 2001:db8:abcde::1 + +# non-hex characters +IPv6 = xyz:: +IPv6 = ::g +IPv6 = 2001:db!::1 +IPv6 = 2001:db8::1z + +# garbage +IPv6 = 1.2.3.4 +IPv6 = not an ip +IPv6 = 2001:db8::ffff:192.0.2.1 diff -Nru botan3-3.7.1+dfsg/src/tests/data/utils/ipv6_nc.vec botan3-3.12.0+dfsg/src/tests/data/utils/ipv6_nc.vec --- botan3-3.7.1+dfsg/src/tests/data/utils/ipv6_nc.vec 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/utils/ipv6_nc.vec 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,46 @@ +Input = :: +Canonical = 0:0:0:0:0:0:0:0 + +Input = ::1 +Canonical = 0:0:0:0:0:0:0:1 + +Input = 1:: +Canonical = 1:0:0:0:0:0:0:0 + +Input = 2001:db8::1 +Canonical = 2001:db8:0:0:0:0:0:1 + +Input = 2001:db8:: +Canonical = 2001:db8:0:0:0:0:0:0 + +Input = ::ffff +Canonical = 0:0:0:0:0:0:0:ffff + +Input = 1::2:3 +Canonical = 1:0:0:0:0:0:2:3 + +Input = fe80::1:2:3:4 +Canonical = fe80:0:0:0:1:2:3:4 + +# uppercase hex +Input = 2001:DB8::1 +Canonical = 2001:db8:0:0:0:0:0:1 + +Input = FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF +Canonical = ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff + +Input = ABCD:EF01:2345:6789:FEDC:BA98:7654:3210 +Canonical = abcd:ef01:2345:6789:fedc:ba98:7654:3210 + + +# leading zeros within a group +Input = 2001:0db8::0001 +Canonical = 2001:db8:0:0:0:0:0:1 + +Input = 0000:0000:0000:0000:0000:0000:0000:0001 +Canonical = 0:0:0:0:0:0:0:1 + + +Input = 2001:0DB8::0001 +Canonical = 2001:db8:0:0:0:0:0:1 + diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/bsi/expected.txt botan3-3.12.0+dfsg/src/tests/data/x509/bsi/expected.txt --- botan3-3.7.1+dfsg/src/tests/data/x509/bsi/expected.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/bsi/expected.txt 2026-05-07 01:38:28.000000000 +0000 @@ -3,7 +3,7 @@ algo_strength_03$Signature error common_01$Verified common_02$Cannot establish trust -common_03$CERTIFICATE decoding failed with X.509 Certificate had differing algorithm identifers in inner and outer ID fields +common_03$CERTIFICATE decoding failed with X.509 Certificate had differing algorithm identifiers in inner and outer ID fields common_04$Certificate signed with unknown/unavailable algorithm common_05$CERTIFICATE decoding failed with BER: Value truncated common_06$Warning: Certificate serial number is negative @@ -37,12 +37,12 @@ ext_02$Encountered extension in certificate with version that does not allow it ext_03$Verified ext_04$Unknown critical extension encountered -ext_05$Duplicate certificate extension encountered +ext_05$CERTIFICATE decoding failed with Duplicate certificate extension encountered ext_06$CA certificate not allowed to issue certs ext_07$CA certificate not allowed to issue certs ext_08$Certificate chain too long ext_09$Verified -ext_10$Unknown critical extension encountered +ext_10$Certificate extension encoding error ext_11$CA certificate not allowed to issue certs ext_12$Certificate contains duplicate policy ext_13$Unknown critical extension encountered diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/crl/ca.crt botan3-3.12.0+dfsg/src/tests/data/x509/crl/ca.crt --- botan3-3.7.1+dfsg/src/tests/data/x509/crl/ca.crt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/crl/ca.crt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBxTCCAWugAwIBAgIRANaPVuZZ0F1TP1Cyepqi9UcwCgYIKoZIzj0EAwIwKzEp +MCcGA1UEAxMgVGVzdCBDQS9VUy9Cb3RhbiBQcm9qZWN0L1Rlc3RpbmcwIBcNMjUx +MTIxMTUzOTQ2WhgPMjEyNTEwMjgxNTM5NDZaMCsxKTAnBgNVBAMTIFRlc3QgQ0Ev +VVMvQm90YW4gUHJvamVjdC9UZXN0aW5nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD +QgAEWoHkS5CkBIdTPOs76nnnrMdrvXbiz4lrR4yHLp6YquviIgfCFbuzXLurjyqB +Uv5ls0744HTbrJoDZXvadzgGxKNuMGwwIQYDVR0OBBoEGM6/Sxg0CYRYRGfOWslf +ejh8frTqyJmK9jAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBATAj +BgNVHSMEHDAagBjOv0sYNAmEWERnzlrJX3o4fH606siZivYwCgYIKoZIzj0EAwID +SAAwRQIgB1pUBb+jznOrFBTDz9r60f6Q548KdqQX5IEALLD+Gl0CIQCCh6ZvmIGk +Poyp/IDllrZcbKGbMPvMHE81r33DwgShBg== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/crl/sub1.crt botan3-3.12.0+dfsg/src/tests/data/x509/crl/sub1.crt --- botan3-3.7.1+dfsg/src/tests/data/x509/crl/sub1.crt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/crl/sub1.crt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBtjCCAVygAwIBAgIRANYzqdzaS1B3OIKXqOo2NK0wCgYIKoZIzj0EAwIwKzEp +MCcGA1UEAxMgVGVzdCBDQS9VUy9Cb3RhbiBQcm9qZWN0L1Rlc3RpbmcwIBcNMjUx +MTIxMTU0MjE0WhgPMjEyNTEwMjgxNTQyMTRaMDIxMDAuBgNVBAMTJ1Rlc3QgQ2Vy +dCBTdWIxL1VTL0JvdGFuIFByb2plY3QvVGVzdGluZzBZMBMGByqGSM49AgEGCCqG +SM49AwEHA0IABPuKRaa3tieYoeaIq0EwKgEaWQQqArGEJI4voYKoyz4Yr4PoAowX +Aw6iay0vSDcu2L3q6RsNlS0kR1COb+qfh22jWDBWMCEGA1UdDgQaBBg8yOoSIn4A +jnsn42OMNUCjcQ/skT6rbQ4wDAYDVR0TAQH/BAIwADAjBgNVHSMEHDAagBjOv0sY +NAmEWERnzlrJX3o4fH606siZivYwCgYIKoZIzj0EAwIDSAAwRQIhAJ3K4x2Jlgvu +n6p9N+7O4X+auqbkeTBrvDWymJBOcoCuAiA6KO5WedzThA4c+seatfc3Lr8WLM9f +CxcfHT040ib+lQ== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/crl/sub2.crt botan3-3.12.0+dfsg/src/tests/data/x509/crl/sub2.crt --- botan3-3.7.1+dfsg/src/tests/data/x509/crl/sub2.crt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/crl/sub2.crt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBtjCCAVygAwIBAgIRAMtzPuWhJ9gzszvF+b/Knp4wCgYIKoZIzj0EAwIwKzEp +MCcGA1UEAxMgVGVzdCBDQS9VUy9Cb3RhbiBQcm9qZWN0L1Rlc3RpbmcwIBcNMjUx +MTIxMTU0MjE4WhgPMjEyNTEwMjgxNTQyMThaMDIxMDAuBgNVBAMTJ1Rlc3QgQ2Vy +dCBTdWIyL1VTL0JvdGFuIFByb2plY3QvVGVzdGluZzBZMBMGByqGSM49AgEGCCqG +SM49AwEHA0IABJOWUmHvX7EEIcpOeYOd4olY5/FhK8R81zrEAj7ZFZzdUPkSWQo7 +Tw4wEKhEh5yWnpf5/GYPeLeJVILNl6V3CC2jWDBWMCEGA1UdDgQaBBjVnj0PMoah +Z75YsOESDdjAOKRH2X00f1wwDAYDVR0TAQH/BAIwADAjBgNVHSMEHDAagBjOv0sY +NAmEWERnzlrJX3o4fH606siZivYwCgYIKoZIzj0EAwIDSAAwRQIgbsswTSNN5oiY +RjfYKoLqBqiSXUwMobCm/a/AmYWCm1gCIQCT5AS0BZYHqcJaMFnvqvQoiP5pG4UA +rWSdD+Aor4KcEQ== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/cve_2026_35580/end_entity.pem botan3-3.12.0+dfsg/src/tests/data/x509/cve_2026_35580/end_entity.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/cve_2026_35580/end_entity.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/cve_2026_35580/end_entity.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDRjCCAjCgAwIBAgIRAPANmin5rYHctR1/ho66Z2swCwYJKoZIhvcNAQELME8x +GzAZBgNVBAMTEkxlZ2l0aW1hdGUgUm9vdCBDQTELMAkGA1UEBhMCVVMxFTATBgNV +BAoTDFRydXN0ZWQgQ29ycDEMMAoGA1UECxMDUEtJMB4XDTIwMDEwMTAwMDAwMFoX +DTM1MDEwMTAwMDAwMFowTzEbMBkGA1UEAxMSTGVnaXRpbWF0ZSBSb290IENBMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMVHJ1c3RlZCBDb3JwMQwwCgYDVQQLEwNQS0kw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDlVY+A5MhG1Y6zmOPHV2Ye +IyNtyJKluTLe8ts1ETUDCEw6NWwNBA3Blyund6lEhFIBqrAYdD1k5SCx3QP/GSVA +s4yZI2RHdtiNI8sI6GUTxohBulLPjbPc1mc/cGKLotsX6uZqmz3aF9a/6e+xFqRx +5e/Iv55AWoOrHLzCrxkROGIncpHvN2cQDL7GEwWTleOjd5cXrSOdr41uqpzrCg5C +pt00+wmMBSMQAHs1lqpg6dajnvPgqwndC2UzcbAQbcHOI1JqL4e3XfckK3CiTJS9 +tW3DXV/8FooDaOSfS6SySMm5AVYYIlSysNC177V38QC14LgrWQ1BjaYLxRhCc32V +AgMBAAGjITAfMB0GA1UdEQQWMBSCEnZpY3RpbS5leGFtcGxlLmNvbTALBgkqhkiG +9w0BAQsDggEBANYeHDakpm9ExaPORnGAHx9z2KlJLpOgy6E0wQjHNhPFVi3TfjXw +MuYNEhxrCuaWHXmzQqxzzNWVC8kxHJjWrKhvPIgzeFK+YGVasCHBgCvtwOMj5zkW +toeYrzSRW6uVAVCtlmFDLo8rR18rppnc4cVTxd2GKvspF2pa4MH8I31x+UJC3Jh4 +I6PbAzYuscyq7hRRsPRp6yNreVjR3dDkRDoW8pTGs4LMcXbeIsxWkLzJkDPOKzqV +WJ6C/gPZnbzutTtqUnadkE3883O1BWdxmwr4hTk6rh2x6QGbgX+K/FjJoV0hC6ir +PBBHKAg7vmadbnnAli0gE2wOJHj27U60c50= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/cve_2026_35580/root.pem botan3-3.12.0+dfsg/src/tests/data/x509/cve_2026_35580/root.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/cve_2026_35580/root.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/cve_2026_35580/root.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDkzCCAn2gAwIBAgIRAMJdl10ZoP/ygQOL3iqFPdQwCwYJKoZIhvcNAQELME8x +GzAZBgNVBAMTEkxlZ2l0aW1hdGUgUm9vdCBDQTELMAkGA1UEBhMCVVMxFTATBgNV +BAoTDFRydXN0ZWQgQ29ycDEMMAoGA1UECxMDUEtJMB4XDTIwMDEwMTAwMDAwMFoX +DTM1MDEwMTAwMDAwMFowTzEbMBkGA1UEAxMSTGVnaXRpbWF0ZSBSb290IENBMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMVHJ1c3RlZCBDb3JwMQwwCgYDVQQLEwNQS0kw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDH7iAPHuxG6KahQ3kvcINe +x250cq/Ya5oiosithy02/vUhn3BPRfl4DZfDRp6FD/XaYCLm8R/XHm69Gm02O6aT +qQz91t/oE58rGRyJdhxsaw5quPUIZt+xcz+GdfpqTKnm1FgEoO3uKzUlQ2R2v9Oz +v1aXwlEkp7PhkyPjA3enXGDFJzwbbukgikalmUcJtFGZc/XD9dkCv8iHFdw0hXdd +Me7aeS4n29Yrjbo/bnUJdSm8PoDQYhm6nf3pBwCJtvAf8svp0HGsv+PH0zYC/h4u +3uYTQdh8ivoRQgvoxoq9yMzv/cL5oQAzAmi0T7aWwBm1fWXTzRbYFELr/bBsnW+5 +AgMBAAGjbjBsMCEGA1UdDgQaBBixkU0cq4Tgd7gKxz24NAhqHdpNXu81U/cwDgYD +VR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQEwIwYDVR0jBBwwGoAYsZFN +HKuE4He4Csc9uDQIah3aTV7vNVP3MAsGCSqGSIb3DQEBCwOCAQEArh7FjMTyFEh7 +EiVnohpK7pg3tfRdjee/JCcj3hSNwpsvEzKF0UQLgxjzWAGcWdrSUJwaYzM5cgue +ZvuLrjADFSyB0uUFnBitcDQUUd5yZSqGsRfUDFMCK4D+0HFSoADeDRRgaoRJxSjL +HR1BEstaiQUzLWHNtA/nqZ8Vn4keieMiCXKPKVWWH4V7GEYmMMe7S5NDfewl+/Yp +zOH2fbq783sl+nxteqX3EPjQrijKIX/a4voW8NC6pca3ArQthup+8uponMDXmM58 +158nJyDazOKeFYfUzEPH03G2Gn0sBTR67+lvP8MnJGJnsRPJCc6BrO9YtO69rTQu +2zrUY6qfRw== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/ecc/nodompar_private.pkcs8.pem botan3-3.12.0+dfsg/src/tests/data/x509/ecc/nodompar_private.pkcs8.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/ecc/nodompar_private.pkcs8.pem 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/ecc/nodompar_private.pkcs8.pem 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIB0QIBADCCAYcGByqGSM49AgEwggF6AgEBMEUGByqGSM49AQECOgthcsnViAAA -AAAAAAAAAAAAAAAAAAAAAAAAAEdshQ7mkmMLkJZUVU4Ol915g3uKHPNUo9AwD+x4 -7PkweAQ6Cjd97ea1IzM9NseOmw6qO/SM6TBB9tT8NAFNCPaDOAdJje7dQpAQHFhm -6N+1iUhdEzV7nnjC1/vp/gQ6CprPjIumF3d+JIUJvLRxfU2zRiAr+eNSzVYzcx3Z -KlG3Kk3Ds9F8gj/Mj72k2gjyXeqJBGCHNCWVpwR1BAgVI9A9TxLNAoed6kv2pPOn -3ybtiI8QxbIjWhJ0w4ai8hgwDe5u0heEEWRTO83JA/B6CW+fv07pW6wJihEfKW9Y -MP5cNbPjRNXfOiJWmF9k++bQ7cxMYdGL72gd05nfPQGUxaQxXgEuAkXs6lY2W6qe -i+H3AjoLYXLJ1YgAAAAAAAAAAAAAAAAAAAAAAAAAAABHbIeQSOXYXqco7S6hwduS -xOT5ZSNk/c26d1X6bDYvAgEBBEEwPwIBAQQ6CQZ+cOjNmFKHK7JoXGdAPByb7XfI -kTIoUEiQxOiDRUH9pUqf0lpcCHhjtTb9hQZ1RGYrjqsccdL6ng== ------END PRIVATE KEY----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/ecc/withdompar_private.pkcs8.pem botan3-3.12.0+dfsg/src/tests/data/x509/ecc/withdompar_private.pkcs8.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/ecc/withdompar_private.pkcs8.pem 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/ecc/withdompar_private.pkcs8.pem 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY----- -MGACAQAwGAYHKoZIzj0CAQYNKwYBBAHAbQMBAgkAIQRBMD8CAQEEOgG97/hDkXbJ -tgF36JmM7NliJIlDFzTm69KYouwhjPOsh6hKo5NPTtsmHafplOqpUf0TyAhB1Q88 -3xA= ------END PRIVATE KEY----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/general_name_ip.vec botan3-3.12.0+dfsg/src/tests/data/x509/general_name_ip.vec --- botan3-3.7.1+dfsg/src/tests/data/x509/general_name_ip.vec 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/general_name_ip.vec 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,64 @@ +[Valid] + +# IPv4 + +Address = 00000000 +Netmask = 00000000 + +Address = 0A000000 +Netmask = FF000000 + +Address = 0A020000 +Netmask = FFFE0000 + +Address = 0A022000 +Netmask = FFFFF000 + +Address = 0A000000 +Netmask = FF800000 + +Address = C0A8012A +Netmask = FFFFFFFF + +# IPv6 + +Address = 00000000000000000000000000000000 +Netmask = 00000000000000000000000000000000 + +Address = 86753090000000000000000000000000 +Netmask = 00000000000000000000000000000000 + +Address = 20010DB8000000000000000000000000 +Netmask = FFFFFFFF000000000000000000000000 + +Address = 20010DB8000000000000000000000000 +Netmask = FFFFFFFF800000000000000000000000 + +Address = 20010DB8CAFE00000000000000000000 +Netmask = FFFFFFFFFFFFFFFF0000000000000000 + +Address = 20010db8000000000000000000000001 +Netmask = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + +[Invalid] + +Address = 0A000000 +Netmask = FF00FF00 + +Address = 00000000 +Netmask = 00FF0000 + +Address = 00000000 +Netmask = 7F000000 + +Address = 0A000000 +Netmask = FFFEFFFF + +Address = 0A000000 +Netmask = FF030000 + +Address = 00000000000000000000000000000000 +Netmask = FF00FF00FF00FF00FF00FF00FF00FF00 + +Address = 00000000000000000000000000000000 +Netmask = FFFFFEFF000000000000000000000000 diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/misc/contains_any_extended_key_usage.pem botan3-3.12.0+dfsg/src/tests/data/x509/misc/contains_any_extended_key_usage.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/misc/contains_any_extended_key_usage.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/misc/contains_any_extended_key_usage.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICmzCCAkKgAwIBAgIUQVJ25K/BQU4Tby6TTgieMvfdS6owCgYIKoZIzj0EAwIw +gZIxCzAJBgNVBAYTAkNBMSkwJwYDVQQKDCBOZXVzdGFyIEluZm9ybWF0aW9uIFNl +cnZpY2VzIEluYzEcMBoGA1UECwwTY21zLWNhLmNjaWQubmV1c3RhcjE6MDgGA1UE +AwwxTmV1c3RhciBDYW5hZGEgQ2VydGlmaWVkIENhbGxlciBJRCBTSEFLRU4gUm9v +dCBDQTAeFw0yMDEwMDgxNDQ5NDlaFw00MDEwMDgxNDQ5NDlaMIGSMQswCQYDVQQG +EwJDQTEpMCcGA1UECgwgTmV1c3RhciBJbmZvcm1hdGlvbiBTZXJ2aWNlcyBJbmMx +HDAaBgNVBAsME2Ntcy1jYS5jY2lkLm5ldXN0YXIxOjA4BgNVBAMMMU5ldXN0YXIg +Q2FuYWRhIENlcnRpZmllZCBDYWxsZXIgSUQgU0hBS0VOIFJvb3QgQ0EwWTATBgcq +hkjOPQIBBggqhkjOPQMBBwNCAASjwCO8A6fzPwY1F5MuaXLpJ3idemAo1q8CdAyQ +XS6Ln4yVkNhghSM+EhKjc7RlBX340zfExlIaIzpyHTZVNvpUo3QwcjAPBgNVHRMB +Af8EBTADAQH/MB8GA1UdIwQYMBaAFEgDf+8c7YwuP4vEBHIWEAP62tkuMA8GA1Ud +JQQIMAYGBFUdJQAwHQYDVR0OBBYEFEgDf+8c7YwuP4vEBHIWEAP62tkuMA4GA1Ud +DwEB/wQEAwIBhjAKBggqhkjOPQQDAgNHADBEAiARILOdPc23+PI4QwMZXudEjoNv +usfK+5RMKwDnQu5LMAIgf5PAGmqtyAdFlRVuJ00qWw7G2+Hw5Oq3NuHYjFZfzKE= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/misc/contains_multiple_ocsp_responders.pem botan3-3.12.0+dfsg/src/tests/data/x509/misc/contains_multiple_ocsp_responders.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/misc/contains_multiple_ocsp_responders.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/misc/contains_multiple_ocsp_responders.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,55 @@ +-----BEGIN CERTIFICATE----- +MIIFvzCCA6egAwIBAgIUbNlFYpSi5yeivRmg+5P61FgbKucwDQYJKoZIhvcNAQEL +BQAwLjEsMCoGA1UEAwwjVGVzdCBDZXJ0aWZpY2F0ZSB3aXRoIE11bHRpcGxlIE9D +U1AwHhcNMjYwMTEyMTMxNTE0WhcNMzYwMTEwMTMxNTE0WjAuMSwwKgYDVQQDDCNU +ZXN0IENlcnRpZmljYXRlIHdpdGggTXVsdGlwbGUgT0NTUDCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAJ1P8yscKZ5lKggYIxMjRrID52eXuo1K1i5MyllF +qljU5cKrsCHXQRNSS1qIc2k3mYwTUerGT7a4uLG/Z6WHA5xgt6Q4ivSmKvcm56Ta +fiSEgHBxG5BrgemUciCbjNSJTX2N+8urrCbLYkuS6+DLfUdTPy7RGEijBJZ4+/rc +7FNiU6WA0O4X5vm6lUBhCKnqE4Yq15bi40fTWW65e7O1mrtyjT7tcfGm4Dla5UCi +oTQ2xedubjP2PPLgjw1yHYNX5y+TRN/A7pZDRv0/irK7AaTjIc69JY3u3kDmVeB1 +fjqSveaCrhjc6ZdEDYSnE3YxSoqrbwU513ArwH9ujtuaiDtj1eIcYOGLoh076j7S +/1iugUhTsDeveHU1fG9idiiktNyRUWRCzMvL97u0og+mQRus4BrsZhukj6VzjOmG +xXLjq/S6mHMG7N2Rk0NyOVomzV+DqfhxT2u53beww8ISJxAFZZOEx1ys9FRJNoc5 +BewbpAi3nRtxE2BPIzL89kDmjDsXuJTipVatN0LlI1KOwXLfjt1fAKIl88cDx0xW +Y5sqiI5vX/vh5doveRzwwyLrCyiPnVzcFL0v9sIFZnFsc0F8FSfnf5yXOX0j2omC +Ymll9FJUeU7tzuiiDNjdz5iOOcLXAYlchcDPGiuOTwJTXLJ5QecGOfXfSJRE5vnk +jgCbAgMBAAGjgdQwgdEwHQYDVR0OBBYEFOI4tY7OLK/QNpmDBjE7lnO6oNG5MB8G +A1UdIwQYMBaAFOI4tY7OLK/QNpmDBjE7lnO6oNG5MAwGA1UdEwQFMAMBAf8wgYAG +CCsGAQUFBwEBBHQwcjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AxLmV4YW1wbGUu +Y29tMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcDIuZXhhbXBsZS5jb20wJAYIKwYB +BQUHMAGGGGh0dHA6Ly9vY3NwMy5leGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOC +AgEARhrXE1DcGt7UJtH1j0zxMEgfKzgbnolLVgaWMzZ//1sOH6Qk8P2mfOyjm+l3 +4pH6bz/h8+D8TglOKDlcdniwOHXsuP2w4CG9LfQS3YprKH4ltGaQCQ5cL1OMWWq4 +MlQ80tHe43kSbJ6eQF+EZr5WGxEwA8hcAaQ08F6QEiPN1h0zOpTfIa4zU0ExUYGm +dCs1ST0RNQuCkEogKnyl6iFzd4AzshHkmzeqdSjvewsbO+UDwSsy/R5pYfzX2IJE +zg8J77q5fy43WJrcubT4uu2fsUKjHMxjpgdYrkIlLtNByviBj6px/4JK2wrczPJ0 ++hPOa5boE7P+qpimklmBHnOXDo5BTpRCZXwvHQHKB1JHPmLAGLibF4ljzJ32H44h +DUIV/z1P2Jx2duxh6CIgfOJAZBSln2blpHSpuSemt2j1orM+tT+DWuIz+H3uzKm6 +O2TmYGi5R0+wvV8a1ux4HsUMYU9yW+g88LVYRzWWw89g8aRbkVQ6OAAvYgMtJj7d +VDnoCzt9VOU6269w4N2jmQ1IYa3sP60JbdpW+pvfeN35WARmvwcPEBrQYiLl92Fu +e/P6h6L4nMkVS952DJCCDdBceDIh2CPD8t0/XHZqdLRYCaeiOVe+zNYTe6TY1gcx +Soy/P3vJc7UfvNtl0PZx3Fs5UCb5O0RhJXdOn58l8lScCWo= +-----END CERTIFICATE----- + + +openssl req -new -x509 -days 3650 -nodes \ + -newkey rsa:4096 \ + -keyout multi_ocsp_key.pem \ + -out multi_ocsp_cert.pem \ + -config multi_ocsp.cnf + +[ req ] +default_bits = 4096 +distinguished_name = req_distinguished_name +x509_extensions = v3_ca +prompt = no + +[ req_distinguished_name ] +CN = Test Certificate with Multiple OCSP + +[ v3_ca ] +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +basicConstraints = CA:TRUE +authorityInfoAccess = OCSP;URI:http://ocsp1.example.com,OCSP;URI:http://ocsp2.example.com,OCSP;URI:http://ocsp3.example.com diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/01.pem botan3-3.12.0+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/01.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/01.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/01.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICVjCCAbugAwIBAgIBATAKBggqhkjOPQQDBDA6MQ0wCwYDVQQDDARSb290MQsw +CQYDVQQGEwJERTEPMA0GA1UECwwGZm9vYmFyMQswCQYDVQQuEwJDYTAeFw0yNTAz +MDIxMjAwMDBaFw0yNjAyMjQxNDM3MzlaMDAxEDAOBgNVBAMMB1Jldm9rZWQxCzAJ +BgNVBAYTAkRFMQ8wDQYDVQQLDAZmb29iYXIwgZswFAYHKoZIzj0CAQYJKyQDAwII +AQENA4GCAAQ/Xipz6whAb5ALRNlPgfPtCTUIuADksXXeM12YujeQ8tv2cC1RMnAZ +T1oCLpLBJ3DBfYw0q1pWbpGG92pq3jGFG+4M35+iGRBouGQqW7x8Lu/s7hZz451g +Z8xUYbtlKixhLSiPQBgfPv7j30w5zkMNUqNgD4mRMtM8zfocepxfXaN5MHcwCQYD +VR0TBAIwADAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYEFD/sOCEwX9ssm/sV9xBk +I+wFhoxXMDsGA1UdIwQ0MDKAMAHDgpSV8Y6U42fjA03JZSkEez7iBSXN5qH/cAaA +xPFcAt0zTnGtDNPbRSu22dj+jjAKBggqhkjOPQQDBAOBiAAwgYQCQBOCB0THsWC0 +B2xNJbO6ENnog6Wv1AUrMIpSk6n7zH/Yy4Z0RN2GmF5sTtHTEFGKzd5aeCwG65gV +mYr//HqnEuECQAT6lbN7J/s2W9qOvBLEx0h2CphkrOeOYSjuaQY6j8+XGxUjbxQY +b86hvmWFAl3WXBZTmLeUHbq9gM1PueLaZHw= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/42.pem botan3-3.12.0+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/42.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/42.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/42.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICVDCCAbmgAwIBAgIBKjAKBggqhkjOPQQDBDA6MQ0wCwYDVQQDDARSb290MQsw +CQYDVQQGEwJERTEPMA0GA1UECwwGZm9vYmFyMQswCQYDVQQuEwJDYTAeFw0yNTAz +MDIxMjAwMDBaFw0yNjAyMjQxNDM3MzlaMC4xDjAMBgNVBAMMBVZhbGlkMQswCQYD +VQQGEwJERTEPMA0GA1UECwwGZm9vYmFyMIGbMBQGByqGSM49AgEGCSskAwMCCAEB +DQOBggAEOGCjS6M99uoGdVfR1nLqcUogCRK1IYllHG7GtsCQrZ9i7v8zzBSU4STq +Oygefl6MrFz0kbCeiUqfm4DmDU7CAl374l6/eu9EPzD245YGQzTVoljn2ahg6eVI +cfSNxOc9t101FvEUpcCMiWW9Zre+VBnNE2DGxA755s0sDa/v0LujeTB3MAkGA1Ud +EwQCMAAwDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBTKZxAUPc9hmU1gyAilSRzw +wLihFTA7BgNVHSMENDAygDABw4KUlfGOlONn4wNNyWUpBHs+4gUlzeah/3AGgMTx +XALdM05xrQzT20UrttnY/o4wCgYIKoZIzj0EAwQDgYgAMIGEAkAY6Vnn+hDkNPH7 +A4dhDDyYYA3wDJNYeXMkm4Wf2TuYhhF2Qu27ffqb/Am6G0ePxw6PZxwEWGrOA5g3 +Tw6VF0NgAkAlUwfCICur0CFJXsHeHPtewjLBPW3NEp9xtWTXo/N8AeDlACKEwL5n +uTzrx+n3O8X+BGjO/+IANHOilLvmmELx +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/README.md botan3-3.12.0+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/README.md --- botan3-3.7.1+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/README.md 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/README.md 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,21 @@ +# CRL without a nextUpdate field + +This is allowed by the X.509 standard but RFC 5280 requires all CAs to set it. +The CRL in this test body does not comply with that, nevertheless parsing and +path validation should still work as expected. + +## Find Contained + + * valid_forever.crl - a CRL that does not define nextUpdate + revoking serial numbers "1" and "10" + * ca.pem - a CA root certificate issuing the CRL + * 01.pem - a certificate with serial number "1", issued by the CA + and revoked by the CRL + * 42.pem - a certificate with serial number "42", issued by the CA + and _not_ revoked by the CRL + +## Recreation of this test data + +The CRL originates from a downstream application and cannot be easily recreated. +Scripts and private key to regenerate the root CA and leaf certificates can be +found in GH #4732. diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/ca.pem botan3-3.12.0+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/ca.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/ca.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/ca.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICRTCCAaigAwIBAgICEAAwCgYIKoZIzj0EAwQwOjENMAsGA1UEAwwEUm9vdDEL +MAkGA1UEBhMCREUxDzANBgNVBAsMBmZvb2JhcjELMAkGA1UELhMCQ2EwHhcNMjUw +MzAxMTIwMDAwWhcNMjYwMjI0MTQzNzM5WjA6MQ0wCwYDVQQDDARSb290MQswCQYD +VQQGEwJERTEPMA0GA1UECwwGZm9vYmFyMQswCQYDVQQuEwJDYTCBmzAUBgcqhkjO +PQIBBgkrJAMDAggBAQ0DgYIABBgecauApzs2MaCus+PQvmU5Uq/WPVpW8EaMSDqE +t9qfl623DujjOoj1bBAA/fAwTCcQZ3xxxC2RCZlao0p34CZdaMOZSZc1VmuPpVXe +k3q7lRRdRKBGKsME74bK6XpfcVo0CgKV9yuwm7vkKDI/OR0hfxzDV2kd459kQaNR +BQDTo1swWTAMBgNVHRMEBTADAQH/MA4GA1UdDwEB/wQEAwIBhjA5BgNVHQ4EMgQw +AcOClJXxjpTjZ+MDTcllKQR7PuIFJc3mof9wBoDE8VwC3TNOca0M09tFK7bZ2P6O +MAoGCCqGSM49BAMEA4GKADCBhgJBAJfWqYqA/UhKrT+PMSmArB0DIN58Kos7W7ls +VHX2MWdop5FaLOMoB7IH5L5RcjuWr1Uncu6FFgVhi2ExiQUQuzMCQQCAPe9GuR76 +lNIcFPjj/xf43/qR+OpIP+G5+Vo4IiznbPvVp2EY/VUTwti8554bXYdQeLP+mIle +WFN/7rsit34E +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/valid_forever.crl botan3-3.12.0+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/valid_forever.crl --- botan3-3.7.1+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/valid_forever.crl 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/misc/crl_without_nextupdate/valid_forever.crl 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN X509 CRL----- +MIIB4jCCAUcCAQEwCgYIKoZIzj0EAwQwOjENMAsGA1UEAwwEUm9vdDEPMA0GA1UECwwGZ +m9vYmFyMQswCQYDVQQGEwJERTELMAkGA1UELhMCQ2EYDzIwMjUwMzA1MDkwOTI5WjBgMC +4CAQEXDTI1MDMwNTEwMDkyOVowGjAYBgNVHRgEERgPMjAyNTAzMDUxMDA5MjlaMC4CAQo +XDTI1MDMwNTEwMDkyOVowGjAYBgNVHRgEERgPMjAyNTAzMDUxMDA5MjlaoIGGMIGDMAoG +A1UdFAQDAgEAMDsGA1UdIwQ0MDKAMAHDgpSV8Y6U42fjA03JZSkEez7iBSXN5qH/cAaAx +PFcAt0zTnGtDNPbRSu22dj+jjA4BgNVHRIEMTAvoBoGCysGAQQBizpzeQEaoAsWCXBraW +Zvb2JhcoYRdXJuOmZvb2Jhcjpmb29iYXIwCgYIKoZIzj0EAwQDgYgAMIGEAkA8nye3Zfy +OJdxYJYyx7ELEuU18xWF0GZXO8HKHVEMPdWTpne99Kdvkq5d32uBWzDPsO73EAkFORmOT +WnkyovETAkAjl96lUy6B0RokLH1JsbdY14SjAz0wSygX56dLyX3Sap8qLi/GKbPIcht8r +ggARYv0N+tv36zZQNXU/5lajeg0 +-----END X509 CRL----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/misc/multiple_alternative_names.pem botan3-3.12.0+dfsg/src/tests/data/x509/misc/multiple_alternative_names.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/misc/multiple_alternative_names.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/misc/multiple_alternative_names.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,65 @@ +-----BEGIN CERTIFICATE----- +MIIEMzCCA9mgAwIBAgIBKjAKBggqhkjOPQQDAjBJMRAwDgYDVQQDEwdUZXN0IENB +MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNQm90YW4gUHJvamVjdDEQMA4GA1UECxMH +VGVzdGluZzAeFw0yNDAxMDEwMDAwMDBaFw0yNzAxMDEwMDAwMDBaMGMxJDAiBgNV +BAMTG011bHRpLU5hbWUgVGVzdCBDZXJ0aWZpY2F0ZTELMAkGA1UEBhMCREUxEjAQ +BgNVBAoTCVRlc3QgQ29ycDEaMBgGA1UECxMRWC41MDkgVGVzdGluZyBMYWIwWTAT +BgcqhkjOPQIBBggqhkjOPQMBBwNCAAT4iUt3sNUng53QKFAvzlMgJLlZxxtyndX6 +IucTWTNV13JU0MOeOdS1REj1JbatMLmreqIIFOGud+t57ou7YCdho4ICljCCApIw +IQYDVR0OBBoEGF9fFjTjQB6f1+v1UKk/6Xus1OhKDpjqgjAOBgNVHQ8BAf8EBAMC +B4AwggEmBgNVHREEggEdMIIBGYESaW5mb0B4NTA5LWxhYnMuY29tgRV0ZXN0aW5n +QHg1MDktbGFicy5jb22CEnRlc3QueDUwOS1sYWJzLmNvbYITdHJhaWwueDUwOS1s +YWJzLmNvbYIVdmVyc3VjaC54NTA5LWxhYnMuY29tpCcwJTETMBEGA1UEAxMKRmly +c3QgTmFtZTEOMAwGA1UECxMFTGFiIDGkJjAkMRIwEAYDVQQDEwlMYXN0IE5hbWUx +DjAMBgNVBAsTBUxhYiAzpCgwJjEUMBIGA1UEAxMLTWlkZGxlIE5hbWUxDjAMBgNV +BAsTBUxhYiAyhhRodHRwOi8veDUwOS1sYWJzLmNvbYYVaHR0cHM6Ly94NTA5LWxh +YnMuY29thwR/AAABMIH/BgNVHRIEgfcwgfSCFXRlc3QueDUwOS1sYWJzLWNhLmNv +bYIWdHJhaWwueDUwOS1sYWJzLWNhLmNvbYIYdmVyc3VjaC54NTA5LWxhYnMtY2Eu +Y29tpCQwIjERMA8GA1UEAxMIRmlyc3QgQ0ExDTALBgNVBAsTBENBIDGkIzAhMRAw +DgYDVQQDEwdMYXN0IENBMQ0wCwYDVQQLEwRDQSAzpCUwIzESMBAGA1UEAxMJTWlk +ZGxlIENBMQ0wCwYDVQQLEwRDQSAyhhdodHRwOi8veDUwOS1sYWJzLWNhLmNvbYYY +aHR0cHM6Ly94NTA5LWxhYnMtY2EuY29thwTAqAEBMAwGA1UdEwEB/wQCMAAwIwYD +VR0jBBwwGoAY9DficXI3Yj7ff1cCpqhwpiFOXDo716+IMAoGCCqGSM49BAMCA0gA +MEUCIASrUtXryIn1kOTy6A9Z79IXft7mrBim/32QUHQoET+oAiEAtTJlv9rjKu0i +RJIp66gNT3MRf/g5HNFgvMJOH2HRDhA= +-----END CERTIFICATE----- + +auto dn = Botan::X509_DN({{"X520.CommonName", "Multi-Name Test Certificate"}, + {"X520.Country", "DE"}, + {"X520.Organization", "Test Corp"}, + {"X520.OrganizationalUnit", "X.509 Testing Lab"}}); + +auto exts = Botan::Extensions(); +exts.add(std::make_unique(false, 0)); +exts.add(std::make_unique(Botan::Key_Constraints::DigitalSignature)); + +auto san = Botan::AlternativeName(); +san.add_email("testing@x509-labs.com"); +san.add_email("info@x509-labs.com"); +san.add_dns("test.x509-labs.com"); +san.add_dns("versuch.x509-labs.com"); +san.add_dns("trail.x509-labs.com"); +san.add_uri("https://x509-labs.com"); +san.add_uri("http://x509-labs.com"); +san.add_dn(Botan::X509_DN({{"X520.CommonName", "First Name"}, {"X520.OrganizationalUnit", "Lab 1"}})); +san.add_dn(Botan::X509_DN({{"X520.CommonName", "Middle Name"}, {"X520.OrganizationalUnit", "Lab 2"}})); +san.add_dn(Botan::X509_DN({{"X520.CommonName", "Last Name"}, {"X520.OrganizationalUnit", "Lab 3"}})); +san.add_ipv4_address(Botan::string_to_ipv4("127.0.0.1").value()); +exts.add(std::make_unique(san)); + +auto ian = Botan::AlternativeName(); +// email deliberately not set +ian.add_dns("test.x509-labs-ca.com"); +ian.add_dns("versuch.x509-labs-ca.com"); +ian.add_dns("trail.x509-labs-ca.com"); +ian.add_uri("https://x509-labs-ca.com"); +ian.add_uri("http://x509-labs-ca.com"); +ian.add_dn(Botan::X509_DN({{"X520.CommonName", "First CA"}, {"X520.OrganizationalUnit", "CA 1"}})); +ian.add_dn(Botan::X509_DN({{"X520.CommonName", "Middle CA"}, {"X520.OrganizationalUnit", "CA 2"}})); +ian.add_dn(Botan::X509_DN({{"X520.CommonName", "Last CA"}, {"X520.OrganizationalUnit", "CA 3"}})); +ian.add_ipv4_address(Botan::string_to_ipv4("192.168.1.1").value()); +exts.add(std::make_unique(ian)); + +auto req = Botan::PKCS10_Request::create(*somekey, dn, exts, some_hash_fn, somerng); +auto crt = someca.sign_request(req, somerng, Botan::BigInt(42), from_date(-1, 01, 01), from_date(2, 01, 01)); +std::cout << crt.PEM_encode() << '\n'; diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/misc/no_alternative_names.pem botan3-3.12.0+dfsg/src/tests/data/x509/misc/no_alternative_names.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/misc/no_alternative_names.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/misc/no_alternative_names.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIICBDCCAamgAwIBAgIBKjAKBggqhkjOPQQDAjBJMRAwDgYDVQQDEwdUZXN0IENB +MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNQm90YW4gUHJvamVjdDEQMA4GA1UECxMH +VGVzdGluZzAeFw0yNDAxMDEwMDAwMDBaFw0yNzAxMDEwMDAwMDBaMGMxJDAiBgNV +BAMTG011bHRpLU5hbWUgVGVzdCBDZXJ0aWZpY2F0ZTELMAkGA1UEBhMCREUxEjAQ +BgNVBAoTCVRlc3QgQ29ycDEaMBgGA1UECxMRWC41MDkgVGVzdGluZyBMYWIwWTAT +BgcqhkjOPQIBBggqhkjOPQMBBwNCAASOA9Xe+ot4ZTkK/NEeu77GWW4wkIg2BXGY +9Zr8LOt/3+ULreIEqmCZdYGRZk1Qhi5bLHrJwyaKkMibC5b6kOVco2gwZjAhBgNV +HQ4EGgQYhz94eb52ghUcGHOr1PR1I0j/Px671qQhMA4GA1UdDwEB/wQEAwIHgDAM +BgNVHRMBAf8EAjAAMCMGA1UdIwQcMBqAGE640d7YwFbBdhFvCbwVupi01tvLUhMX +rzAKBggqhkjOPQQDAgNJADBGAiEAhHEV9F4tc83u3j99/qS5P5cbVqL9tzc0u52Y +wdeJOqoCIQDT1W2FbafrYYiHXZtT5qVNBMqt6Ggl38xEi2zUup/fXw== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/misc/self-signed-end-entity.pem botan3-3.12.0+dfsg/src/tests/data/x509/misc/self-signed-end-entity.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/misc/self-signed-end-entity.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/misc/self-signed-end-entity.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBnDCCAUGgAwIBAgIRAIrbFNqSsr6LwxUJfgp/ClswCgYIKoZIzj0EAwIwITEf +MB0GA1UEAxMWc2VsZi1zaWduZWQtZW5kLWVudGl0eTAgFw0wMDAxMDEwMDAwMDBa +GA8yMTAwMDEwMTAwMDAwMFowITEfMB0GA1UEAxMWc2VsZi1zaWduZWQtZW5kLWVu +dGl0eTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABBzbojsoXkl9Os+rOlMP53yL +H320KN0sWdfMum8kqB41HftdWbZzaNo7Okp7Dgxt/vp4RMvuFZdVDxJpc84NN2yj +WDBWMCEGA1UdDgQaBBjfqw0AfGlgD/cG1aLKsQN15p5FileGbHcwDAYDVR0TAQH/ +BAIwADAjBgNVHSMEHDAagBjfqw0AfGlgD/cG1aLKsQN15p5FileGbHcwCgYIKoZI +zj0EAwIDSQAwRgIhAIjtkRK7qUP1mgS6AtQrVdi+0QgUpdGaHrkDlG+ixZJzAiEA +qpyygh5CumB1cD11kURGTA3rk9SPEyWNNH4gcCNDT3I= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint/Invalid_DNS_Excluded_Mixed_Case_CN.crt botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint/Invalid_DNS_Excluded_Mixed_Case_CN.crt --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint/Invalid_DNS_Excluded_Mixed_Case_CN.crt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint/Invalid_DNS_Excluded_Mixed_Case_CN.crt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDCTCCAfGgAwIBAgIUAbjRT6B+WHVf1Wo9JdFHlHegF1cwDQYJKoZIhvcNAQEL +BQAwFzEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTI2MDMxNTE0NTM1MloXDTI3 +MDMxNTE0NTM1MlowFzEVMBMGA1UEAwwMU3ViLkVWSUwuQ09NMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsn1rve+kuZJd2udT4QEnjdws+YRT+lJI+rG/ +4OpljAcxQywCc6oHDY4ouaqOKdhT9+luvqRVSEnH3JfczT/D+wZD+gBOlgUqsmXp +QasD8HSyHWdjyaLrkZuBGICoUZTh9a7ZOPuIQ2sblxOvBm3klndpSpXJC6sJsWAk +tyU+5WdNz+ncTC82sFSY0YQLC2QvxckEP6Jxi4I+h9vANtWXCk6CRz/kT4dY3cD4 +VBFtAy+vUVYUZnLi48fEYlMt4rP64OerY7hjw8gE+66rmUQPml4+DAek+xJtL4Sl +Q0SGytWE9tJb1zzICv4TNvj/+IKmbK4BCu7fVjOd2C64zNFTywIDAQABo00wSzAJ +BgNVHRMEAjAAMB0GA1UdDgQWBBS6hC1DrVlXAq/GWDYl/UQbP4TmLTAfBgNVHSME +GDAWgBQPZ2Gp3izLTRAUzQKCKRLbM5OA2DANBgkqhkiG9w0BAQsFAAOCAQEAcWBy +f9+tJDDj7T7z4QziZnxIfKP7x7uP9wAlYGRpKcbbW6SHAg6DA1V5GBWHendkg1hb +Nywy7pUFgJ0xlJ7KYr76Ylfa1BTUPT+gExJWvSmsg8WSQ4q3bOMVB1qRxWDv/8KY +ZOT03KEzLyGL6ia9r/UFw1jibxS26Ff6qCE9EhDLA/4z0D/+9QzdLATuzSn/SLSR +wtarLaWhCfOSpfRrekYhaSndG45BCKpUd99iWt1HA4LyjZe8/8cxsRkD8klaCalG +8pRp+PolijzmYsrEXuG22Bq2idgxHTRvw8l4rBQrSdMWuWqqTdIpFIUrXNft0a5f +d8RmhI6PZlPL43OCxw== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint/Root_DNS_Excluded_Mixed_Case_CN.crt botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint/Root_DNS_Excluded_Mixed_Case_CN.crt --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint/Root_DNS_Excluded_Mixed_Case_CN.crt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint/Root_DNS_Excluded_Mixed_Case_CN.crt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDGjCCAgKgAwIBAgIUJj5ZhECZYOzt5qWnoN8DMcZzlzgwDQYJKoZIhvcNAQEL +BQAwFzEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTI2MDMxNTE0NTM1MloXDTM2 +MDMxMjE0NTM1MlowFzEVMBMGA1UEAwwMVGVzdCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtEqBgRHixpRodBHeqXrcttdKD3tpGG+S67tM +WT1h+572k6EFIZ9RVfYokH32sNFNz8LNI5FwKKFTf/WaVhMdsqxaMrf/+06UzKdw +7dKz+Q5uLGrygv6opSVqhojkjLZH78zmEER0Gba4Ac4FGq5pjafA2jtoIMDyQ2SV +IpOy932WCajQL6IM20yHPQAsr0c0hoFP4RdbJPuiEVPfZv95VjEwwV0zMBFmiICk +gItEDfiexBHEIvXAGNBModMHaBhUDzHZp0X7gNW/MD2ieiQGoLTyG/t7sKv0J3zV +LnUpXVIrmHSIkGVfuc5EFOlq6OXZ/J29VjPEnVVeZARDhgyGRQIDAQABo14wXDAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQPZ2Gp3izLTRAUzQKCKRLbM5OA2DAO +BgNVHQ8BAf8EBAMCAQYwGgYDVR0eAQH/BBAwDqEMMAqCCGV2aWwuY29tMA0GCSqG +SIb3DQEBCwUAA4IBAQA5RLYaZWVxFIKpZRM5ZBk4fDDlAvRl7NVhMeQO3DMyyIzw +Bp7IIEgH0I6PlL4/02SzSaqUuVVXYyO1LhbgBlFnE1h818zz+jN06i0lUemuXGAo +wnHBwLW+V/r0JBm4BbnbneCYHUFS07sfR9IjQqV9Futp2WILwieZ7Ib96xGuX96L +f6pxYEd82rYSXa/K14HvMKXkzC3qe72V+/E1GOPZqzlbZFriYfRUHUOY6P7LjcWl +3Z+aUQcG8vEHbJjTd+ZZzP2PICjLKgaX1acpBOUR6pelHgcUmuR3z86V/DD8TWsH +BHDLLBxTje/8fL0PR4qo4r4F+2Cj+hXnY/TFVQ+W +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_empty_subject/intermediate.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_empty_subject/intermediate.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_empty_subject/intermediate.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_empty_subject/intermediate.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDZTCCAk2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADAkMRUwEwYDVQQKDAxBY21l +IE5DIFJvb3QxCzAJBgNVBAYTAlVTMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAw +MDAwMFowLDEdMBsGA1UECgwUQWNtZSBOQyBJbnRlcm1lZGlhdGUxCzAJBgNVBAYT +AlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArDRlRarpgauf82Wm +KXvdhOo0SCOI19PqRlrtY/4IdGu7f4k+pBN+lgPE31Q2lqi1V+GWeHLnzsY1PYq6 +PHODXRP7LlYvc6+UtCz8zxJED2BvNcUdjzS8SW6MD+DKzy9noxmLcOudB/jfJgb3 +K9cibm3iyrobUjNVopUbtbEHxsPknWtNT/ZAMjukdgw9WmFZR3qiQ/e3VaiqPfwi ++l+Kch+R5zT4nl5gg/XMMckAB8I6JjFbJEp65f7sE2qeKZLQuCrKMEjxBfKEXsOD +JIuIHkUVA+xQ1LixAMXuWpQnz6AFXbkL0MjHm4zmtZmQkKL15AHR0SMiLF1Esx3f +uoCsSQIDAQABo4GZMIGWMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQD +AgEGMDAGA1UdHgEB/wQmMCSgIjAgpB4wHDELMAkGA1UEBhMCVVMxDTALBgNVBAoM +BEFjbWUwHQYDVR0OBBYEFE8+XBmT4zaUEZfHG2mBmbVJcABzMB8GA1UdIwQYMBaA +FMHDrsrJyAC+UWeWd/cIPhEkKYWQMA0GCSqGSIb3DQEBCwUAA4IBAQCPzSfn71Fy +VjliKysH/PHDkAbFpOxkPzDoYbeA0q75qc+weexl63kqb/UtlsqTCwmpDt8przuM +07wO8S+965bWDgIccZOBPXPwlqC1o4tR1LzJDcQBBsRR/CoUxR2JrLWSyvzws3dQ +gn2xI4hhqkOFJFQ5v2xCQACpxmfNB/vAF16519nuyKGaFvzVAQPleRa5OYgtXINl +uyjnDIkgpeAUs4aA18s4MvuKLeOdsqY2QGG0XVEa2byvwxu4GbYVAvNg/OjGPTjE +o+3r2Bdk1uYdaP2y1verjBMxao2l/sQFAh3UO0GHP1ZXVGxusbIGlBSgr5D5Usy/ +vyuXuk9g8LO7 +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_empty_subject/leaf.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_empty_subject/leaf.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_empty_subject/leaf.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_empty_subject/leaf.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDSDCCAjCgAwIBAgIBAzANBgkqhkiG9w0BAQsFADAsMR0wGwYDVQQKDBRBY21l +IE5DIEludGVybWVkaWF0ZTELMAkGA1UEBhMCVVMwHhcNMjYwMTAxMDAwMDAwWhcN +MzYwMTAxMDAwMDAwWjAAMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +04/Uo/QFD/Rm3kFe9y0eS37e1Yl+/DBYkGYhDymf1CEBrr5FstyOp0J8fbU4/tLw +cqqOG8VtlJQneQkAlTvKms2FeFWOdSo5UphUfXfB6ixUQBto5sdGdBVPC9368+8K +BWAnAdB489uJr4NKrrmH147AplKYIwzp3uK3dXMSzQ0QPoVwzqZwcpqAAHyY6eBP +x9cot+fzCOGci1xPHk7fadaQS4LmIgbp27DPNGu3J/w9lPxtoJJg6sxUMYtI3Q/d +UIqazX2Cz5B2udVFcMracaM9HKrE0XoKQw21ttkF2HpCMplxjK6bh/AncHfEPy7P +VY9AyzNMQekGDoYiEU7bEwIDAQABo4GgMIGdMAwGA1UdEwEB/wQCMAAwDgYDVR0P +AQH/BAQDAgWgMD0GA1UdEQEB/wQzMDGkLzAtMQswCQYDVQQGEwJVUzENMAsGA1UE +CgwEQWNtZTEPMA0GA1UEAwwGc2VydmVyMB0GA1UdDgQWBBQHLLRL3ca54ID1GBc5 +RxkhN/nVmzAfBgNVHSMEGDAWgBRPPlwZk+M2lBGXxxtpgZm1SXAAczANBgkqhkiG +9w0BAQsFAAOCAQEAgEm4N0rP/5+k+0ubBZ/nsLKJQ/42YbRCzr7x9DRT/YEUHAIp +6a/XxtcWdZJPZpu9Bh+EJBAO7knFJ5Zuei0qqR+QKel2r2GeGsJ54sKqI1pM+Cae +p78W5xL00rs32fwZq2mI+qClS2RcgPLWd/XpJqr5yxWq8uswfdSmKVYxYGST9f1J +dpFR/WoEILuR2yVT2mMdRLnO1e3HnSPhtx8JdGppMgJrWd3Hqi9BBbqnIjIja494 +TZEncMgz6YeX47z/Qeaq2SDXUBceWdtwrgdUoem11XceDkHUI4WDj3OTJTo/wb4B +lGCBaXvF2PRVkD8jJEsOwjifC/p34L2CSHd7KQ== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_empty_subject/root.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_empty_subject/root.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_empty_subject/root.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_empty_subject/root.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBTCCAe2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAkMRUwEwYDVQQKDAxBY21l +IE5DIFJvb3QxCzAJBgNVBAYTAlVTMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAw +MDAwMFowJDEVMBMGA1UECgwMQWNtZSBOQyBSb290MQswCQYDVQQGEwJVUzCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKv8u2YbWwg5n5+3OqM7/T0r2v4J +GTZKPsMSPFJeS9r82pSPdGPWNLxGfk8X2ienVdIdnM3/sTJ7QQvIcVDwXHgf5iFV +EnC5EQhB09s4oyeIEiPZgWqNy1coQxo5PbyXNiqea/0bRnkq9KCq+5PCvhR0RHeK +M3v1y15a8+wnimOkwWBJXQdRy10D1ugLcseEW+xRTHZsAJ+UtG57DGwrlkcKuKKk +Ikq2ZnsFbboVkltzy2NySS1BJKGJhV2TDoyEqsbYjkmPktURc1K/fHkcGlAZzSFB +VTNLXj3XeuptkANjEbdHn/kBTEhn+dpaZoh9wKVz6/KBKtzGPZTpGdxBa90CAwEA +AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FMHDrsrJyAC+UWeWd/cIPhEkKYWQMA0GCSqGSIb3DQEBCwUAA4IBAQCM+mpRD1hy +dNy0ozRtR0YUn8+rR4xpYEWFDMnVUQXBIT6+MHzqCTAVtYRxWtElf0nUuXJAnY2X +sDJdI5eoum97v9Hyu0R9pu1JCRnv8FXT3DvPFjrCIRGjM8SKhZqSs63lf49+VRZS +MipaI+eLRcXBdDb6ptjGu/5a31/i9bPfMryIunqfUwF4/XtnYKYVYdO6ZZ/pHhWN +h6SyF2qC40Vr5S/lMZtFJxNpdi2XqEGh/NDKaZgJ8ygQXrwgPgUDM8IPldMnAH0G +trjXsyfy7HJiKWcsz3v89Q1R+LqdPKzQd4j5qF4RAY8Q6tTcOFBlDu1alijspUBa +PV7Bz5BkoTV6 +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v4only/leaf_invalid.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v4only/leaf_invalid.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v4only/leaf_invalid.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v4only/leaf_invalid.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIBCjANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDDCNCb3Rh +biBUZXN0IElQdjQtb25seSBQZXJtaXR0ZWQgUm9vdDAeFw0yNjAxMDEwMDAwMDBa +Fw0zNjAxMDEwMDAwMDBaMC4xLDAqBgNVBAMMI0JvdGFuIElQdjYgU0FOIFVuZGVy +IElQdjQtb25seSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +3HH1fRhFKxSkGP9e/1S9dH/pCxF+k9CL7xiczccqYjWj/ffuYDuQQyJUTnIYRQOF +mYdc9/a69BuDUw0ZuxNJ8fDI12Udz0AeF69aWP7Xtc+rJyZKgSzSAS6aTDMoRMet +2Z61bx6vtemDdBYm9sSWKKCvqJJ9njSq+Nh2Ej7Ur86nKvqNtDonraOcpSF5cagQ +9gr0J9ZvbOld5Dxv0nLQ2+sobzA40xiL1ogRIhqKa13Aj+ZRXadkp6+RN4epAqrj +A21a7BZwhxVn5MR3ZDZLLBKKIGjyd3HhPrhIy9sBfeRnUO6gFMA7F9uElphSWaOu +lty3VyHXmnM60pFuwggmmwIDAQABo30wezAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB +/wQEAwIFoDAdBgNVHQ4EFgQUz0DhFHX1AkDkCxl2A9u4k9AxwgkwHwYDVR0jBBgw +FoAUbj6H8KROGjpW1QQstNRr57o9x+gwGwYDVR0RBBQwEocQIAENuAAAAAAAAAAA +AAAAATANBgkqhkiG9w0BAQsFAAOCAQEABmbyXdPZPfjlkekXH2oC9aA8Ep8eQQlO +hggCHkGcYDtRShH0SVKTMg5x7dgJQtXf69+zHN1o9IYU/z8nJ2J6XDW7qfm3mFbh +2Hd5FpV7CzpaSO8UVbwN8AXkm5llrjMB0NZoresVJkrY++rzCSTwqqwRcT+1u9dA +im49fVRdDvBZ/QiMizdIFhZTOd5qpr6mKjxAv7IzNVZKasL1En8m9BCTbL5DVyTt +4NDENS0u/Y2Hi/G3X2oYgquE8JyVVkM5oGi0Eh7mvFaqmpqV0f2sVxMzuI9PKOue +Qun3E1tA1F+LrTVxfV5eDiSjlEU2V+GtApnANFtidjYYYu7kmcOl/w== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v4only/leaf_valid.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v4only/leaf_valid.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v4only/leaf_valid.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v4only/leaf_valid.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDSDCCAjCgAwIBAgIBCzANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDDCNCb3Rh +biBUZXN0IElQdjQtb25seSBQZXJtaXR0ZWQgUm9vdDAeFw0yNjAxMDEwMDAwMDBa +Fw0zNjAxMDEwMDAwMDBaMC4xLDAqBgNVBAMMI0JvdGFuIElQdjQgU0FOIFVuZGVy +IElQdjQtb25seSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +357Zfd0jjYCU+LF2bbJOFpqkQK97mGng1xUROm6B5ETD0p51ADhiRpy4S7GPKFrt +glxo43JKE8NYDTBxILhFPLYBQzg0KOLVe/6XLpgXf0qGwlua6krkcBp4Ss2Apvp5 +iHzL/cKxXVf98U5XWIjhaHVmaDOhzm8qOzo+AKazn4RqWPMrBHAnlHD8uMnUQngd +di+7Y1OlZVa0io82T+SF81QZWONVspd11KLiw1st8likx4SrT2hYHAq/f/0fQKL+ +FVMtPnTazd97vNOFDXs7xiU2sXmnE8qNEUxfMF+ty5O4Mqppx1t6tWB2fCYiAzMT +vxLAMLWOBKFwPxhp31V+bwIDAQABo3EwbzAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB +/wQEAwIFoDAdBgNVHQ4EFgQUAWZas/1mVhhLSlPIkN8+P53ermIwHwYDVR0jBBgw +FoAUbj6H8KROGjpW1QQstNRr57o9x+gwDwYDVR0RBAgwBocECgECAzANBgkqhkiG +9w0BAQsFAAOCAQEAeuhSWVVKdyJquK0wxXve1GmQ0wnWqaL65bw71mCP6qPscDyM +9b77AcJXXrWmb6A0XIvOnoPZ8qTdgsJ1WT83hJwbAcsaTRdIDQQZv+fNDKsKVaVd +Mbsff92qEh18od95AKd4ZR3s5OhckiXOGLKDfMJXQRZmHARjDJBtNAL8TvRSR1ei +YuolTflgjqVXBSjtJskUxprdXTbLkeoocaP6f9/ozHunzmpz62Hl920a1dr+9uid +2hdgsKg267PTwK+aE2lAKmYYi3r4EHVhF9Tu5Tvcj5B2ehpVR2X9NTVEL1xpF8p6 +Yn/Lg1uFcdlCv59x748bGSEjX7WFlcelpQ9zUg== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v4only/root.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v4only/root.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v4only/root.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v4only/root.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDNTCCAh2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDDCNCb3Rh +biBUZXN0IElQdjQtb25seSBQZXJtaXR0ZWQgUm9vdDAeFw0yNjAxMDEwMDAwMDBa +Fw0zNjAxMDEwMDAwMDBaMC4xLDAqBgNVBAMMI0JvdGFuIFRlc3QgSVB2NC1vbmx5 +IFBlcm1pdHRlZCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +ntp95gtElvJVSdlgWSHyLCqMVlxJp+faI2dP+skKgCUowd9bkiXx88pYOSwUKezO +zEuEBbY9NXccKZHgVN3ALBLxxWXQowctTwM7UMix5G6pxeoM6xfNLavq+SX13Xr1 +AgW8AqxypmiIH0eJQohJ2yvlJ8n/RG2vXAaSKp1L04uOpY/MU04+TXtqKa4LmNUz +Pm/aH2eO2qzDqRiRT5+K6IIzHaK9HfgW715ciTCFxe5RpgayTe9OkHP3JjlpH5FW +Fy5X7LGEtYCXkZd+Yik81QOHeOSjDR5LXSKSPgdHfqdN82YW4RHQ/ynHC5x3Oop+ +AELkq/t8GpKWnat5Vly3ewIDAQABo14wXDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUbj6H8KROGjpW1QQstNRr57o9x+gwGgYDVR0e +AQH/BBAwDqAMMAqHCAoAAAD/AAAAMA0GCSqGSIb3DQEBCwUAA4IBAQAVPAr+xZ3A +9Fp3Ee+0rZbna3bRqhuIk2kee2bIm11Y/x0MfRUa37vBk7rBj6mvrTVjQ1vhfy4m +42a7JcIIQy7nslAAWqk0QcXMi494BsSXk378XzSUGH7YQk38NPHYTep9MseppEHH +6qX5lGumggY1jQ2g+Y6uiNLQtDosDy3/utWChNEzYXFBFmzWnpXspgs+cLxLP7T1 +FlqvM+ELWsr5/LgVa48JpzB9OiINUM+1ikw974xkUHobpdXqKuOdTCRL/YFKe68N +Jmb5JERw/UWzg24OV52pDrGqBuQNROhIGeL1peWjL0YgHpo7yjsbPpSFv2UexVtB +mlXIugocOp93 +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v6only/leaf_invalid.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v6only/leaf_invalid.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v6only/leaf_invalid.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v6only/leaf_invalid.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDSDCCAjCgAwIBAgIBCjANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDDCNCb3Rh +biBUZXN0IElQdjYtb25seSBQZXJtaXR0ZWQgUm9vdDAeFw0yNjAxMDEwMDAwMDBa +Fw0zNjAxMDEwMDAwMDBaMC4xLDAqBgNVBAMMI0JvdGFuIElQdjQgU0FOIFVuZGVy +IElQdjYtb25seSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +2uwq2GtUXoCDpKl742ID/gqeAw/ewhSHAn4BJ3TpkGxrU8QI7SvvXNXifnu1U0Yd +yKJ152MqcpMRNMEQUO5WXom6UNeFTpZVZOdt0MfnvVX+0DIvhjxfRP8PLyOT7oS9 +1BX7ZG+SAOzV0mKFrUMjabk94JAxoj+T1VsKrGVwLFZz5w5z7qRO5+Ikqj8JKFOf +ImL68muvXOfwgpULunw2Jh8+tuWhNSoE2hwAfh+USANTrJn4XfMGJJW2QNYzAqVm +qFeM0NYTibM5ijocr37UkxEIAOT8AzwE002NWH0fBA5z92DTutx08lIboWaF229b +JppSFEuCwd3nwHJ9JYI85wIDAQABo3EwbzAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB +/wQEAwIFoDAdBgNVHQ4EFgQUwIxvOWlGRyC8vRtNKNqcLVZe1+owHwYDVR0jBBgw +FoAUV9IxavtgQdLVOe8IHsRFM23k/xowDwYDVR0RBAgwBocECgECAzANBgkqhkiG +9w0BAQsFAAOCAQEAA5/Gq5JiHotcVjE0RZUCNAf8gVzM49/aTm/WC3txl8Awb4ts +dnY4a6gzMIT/pctLYLTkSWEesZ+pr5CCjbNSM/tjAriDoA5FSoQjLBGK8rl185Ii +p/wCKEnLTZ8AqzLnDrskdRgJfCMRAFptgSwb33iwp3Bvc124ev4ZI1r6AeMRiskH +scgbo35AoWF7A4nmEyvyMFAD8VCNXUQkVD5+W+Rh7jLrmeO+r1HtLS6O7K+6P6CL +rH8TwjrBWuO4rmJ1tXjkX3mad3APyes2GqPg1AyN4aUj7EIR732FRfgCg3DcWSbc +KPDr2mEOzHPKiTEGu5NJR2DqaRYhcJRlJ7gaCg== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v6only/leaf_valid.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v6only/leaf_valid.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v6only/leaf_valid.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v6only/leaf_valid.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIBCzANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDDCNCb3Rh +biBUZXN0IElQdjYtb25seSBQZXJtaXR0ZWQgUm9vdDAeFw0yNjAxMDEwMDAwMDBa +Fw0zNjAxMDEwMDAwMDBaMC4xLDAqBgNVBAMMI0JvdGFuIElQdjYgU0FOIFVuZGVy +IElQdjYtb25seSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +shtWVw7Mki9pLMfU/t0BAumh10ZQi0WdEBPhL9nTuzc6kfpkBOEnmnWrylnbCgDT +WyYdEbZch4EkpxK4oeXwGVmZTuXP8A+w0UKoUuKrjonvRxCEHsgORxmKabKzSEIU +FSnaVrAc2s5Fa4p50MCzc8UGVySmAwhs4rb4dy6hRcBMk+TulE3wBJU0CWS5AGn+ +Vhpk6x98nqosYQCeO+L2ixuBViAMbtyAXRBn1/MK1uKIHbPsdBWiLvai4xuj8HOA +Mz7L9Nw0z2lkBcgG468J+pBUsoVA5UFTN/oRhzJulZnRcypN5WOsBTSiOpfQojFw +qVZECZsoegkRNerWETw6rwIDAQABo30wezAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB +/wQEAwIFoDAdBgNVHQ4EFgQUFxzO1w8thwTXivN8yo1XB9e57rUwHwYDVR0jBBgw +FoAUV9IxavtgQdLVOe8IHsRFM23k/xowGwYDVR0RBBQwEocQIAENuAAAAAAAAAAA +AAAAQjANBgkqhkiG9w0BAQsFAAOCAQEAuShpdr6y0YWVxjo3UCJEi77hDzXZoHCK +RsjMtwWLlNQJiv6vwutlZweIvJ2qbhtYHt2doqZ20w4CDkIIaMS8tGDRco2I4zfV +8oI9RSqgq/6qlABp5c+fJoyjMfRDkCmqiIo1EVfLNPXAOjUAEC77n9oyX8SB8fYy +uR7WWCp8xLsOvqLl8uiIT1R9V5oMqk9qwfkaqkGy2BjkKL7HzDf3bBwBSI/A8VTZ +NdIFuA0yOIqekbqL+BTONc0nKt1AJqJuwey8hrNEnwohfO2JfIl0e0CIvcCu0Nu4 +hfmB1D6BjwROdd/johTCqKNI6Cl0XIhMp4OI+HkJ7ffEsuElL1n95Q== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v6only/root.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v6only/root.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v6only/root.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/cross_v6only/root.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDTTCCAjWgAwIBAgIBATANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDDCNCb3Rh +biBUZXN0IElQdjYtb25seSBQZXJtaXR0ZWQgUm9vdDAeFw0yNjAxMDEwMDAwMDBa +Fw0zNjAxMDEwMDAwMDBaMC4xLDAqBgNVBAMMI0JvdGFuIFRlc3QgSVB2Ni1vbmx5 +IFBlcm1pdHRlZCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +zkQYm/oyEW6CzSj4Xhtea6GrO3kmoWXjOzbWSskpWTGIiKoG4pqfiAF2sKamEtwz +fIz+CZ0oE0zxp8GqKdiVAgLB+89hIaweKibNkA1s5xOwnqy70xNItARHwR3I03Kk +V6uTXBWRblD9jG7sQf53R19DCw2F4EHI/qiY/UxasRSf+NfVbroPLAyUvXn2WUNm +7/FVwWfdV0npbs8wdNohFRpoyOeU3ZJLzLov+X1ORzOchTJaJl4xbbo4hmeWXaBL +x0WFJnWUH+CEGYmAOuG9HbR26MAHzSYtgkgulOvx5H1M1LmyliAPbxA6it6nYflc +ZUa4yEH+aug9ZF7rXjdksQIDAQABo3YwdDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUV9IxavtgQdLVOe8IHsRFM23k/xowMgYDVR0e +AQH/BCgwJqAkMCKHICABDbgAAAAAAAAAAAAAAAD/////AAAAAAAAAAAAAAAAMA0G +CSqGSIb3DQEBCwUAA4IBAQBytfKxNtcuReHI+pNb71mHbXKqhfqIYWfvLs6CJnm/ +I+K86o86v1Pb9ocZh+7ZjPEh+nL15SLe+64ki9QYcAxA2OgCpTVm/T6x0gmVud7h +o/WwZTE3Ez367dy+OG+VsIs00AL9MTD3lPzHQrlPAJXCSZ6DeOPLteQGymYQJU24 +RqNpHqwb/vGEHWSxVxOBTVECBLZNgJmeMT2BJNSjNuO9SlDykHjAbpqVK0fUZHuL +nXY9dOkBlzFIAMQO9fGl7+BXEs8bocPO80DUHMWlZRFPiaYl/0uPCX3qLIEumMJg +8gh4vidpmhiieZG4rt8hMcpR64zv+8Ten0CLIM7DMIaX +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/excluded/leaf_invalid.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/excluded/leaf_invalid.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/excluded/leaf_invalid.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/excluded/leaf_invalid.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIBCzANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQDDB1Cb3Rh +biBUZXN0IElQdjYgRXhjbHVkZWQgUm9vdDAeFw0yNjAxMDEwMDAwMDBaFw0zNjAx +MDEwMDAwMDBaMCwxKjAoBgNVBAMMIUJvdGFuIElQdjYgRXhjbHVkZWQgTGVhZiAo +aW5zaWRlKTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALg2/p3pFgFb +4oHwLfko4Q3t7Qy0IO8c3HBsvtpThynm2xwl4ZXC84CvkMuVmTFm0GFyldubmxsm +hhoNA9TfSB+mRv/naAPI+KlJgEV0ofbQwAtAp8OPpsafm6d9bb8Iyt8R/rRTD4CC +oEo1LWHO9V+dgPVVQwdwMaKiUcnuA08p5HVIbeq57x3wRuTsxEtbokm4MT4kBWsK +QxdTPpwVfASq3MXq2fOZaU93wwhsfvaDfbfnU27ef2v+2DWujDoW1y7q7tVx1Jjs +0kCchZtvtZ5uGwtfZ6291Dya6CIYTk69XH5j0uGnLfVp2LjAF6ZjpHaEWf9v5MJy +lwcYW1sdDTUCAwEAAaN9MHswDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBaAw +HQYDVR0OBBYEFAlv0ZvyFKZuiNidkJcNY/op3ctlMB8GA1UdIwQYMBaAFFG7UwVa +qmF7lCB39HhwEdUl6OPyMBsGA1UdEQQUMBKHECABDbgAAAAAAAAAAAAAAAEwDQYJ +KoZIhvcNAQELBQADggEBAB3OFDoiIweaHSOpK97hFltW01tVG/hVe7VcJQaCZMj1 +pwmfUo9MYETsWVtd3seSyHFTRPRt5O2fmkNMW3dur0TQ2+Qw4LfeCfC/1pQs8Tt3 +aQlOPEP6LR5P97iCHmJaOcYMlFhHmMNMyRrFnWWKoC8aYM2EXNK/lishbjK2Q3ds +nejD59gM0TW9rChQ1gDDr7a6IQWQi3oNoZpv8Q3We2MHvAdHetRv1jFmHXW/J9w5 +hWx/U5DK/pQUKmv1UJcu1x5SBBuiZJeqDCPnAM0by2NW1jGe3uVWGry775DiKcBY +8xwoWI0zR9VQO5ypeX7Jegk+XymySjvMuZcOhoFJUIA= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/excluded/leaf_valid.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/excluded/leaf_valid.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/excluded/leaf_valid.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/excluded/leaf_valid.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDTTCCAjWgAwIBAgIBCjANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQDDB1Cb3Rh +biBUZXN0IElQdjYgRXhjbHVkZWQgUm9vdDAeFw0yNjAxMDEwMDAwMDBaFw0zNjAx +MDEwMDAwMDBaMC0xKzApBgNVBAMMIkJvdGFuIElQdjYgRXhjbHVkZWQgTGVhZiAo +b3V0c2lkZSkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCih+BijNTS +l2QQGObZAqQqIG847pWdtZko5GwyEJOmrC9URMjn4E7DCADQSyrstfN9MZ4Tep0S +VagizWn/yhD/4xv94+f/lwAEpAlqCFIU34QZ0+UMH7P6XOGQQj/i9ZVIxrrZ+4YE +BS5Q4J2MQXYcjMNH8oH7QTXeLe9YFQIbje/HAqLZrOvYwbysl4OVAlmx9467VAXv +hK/EL9LOAxVuz4NNUgSHQcfpa9fWG3X8UaVaM5vYZiw71saqz0iZYuOfQ2XcIVde +2Y463DNvFk3fCdP6qZabKpIWewQ3eIFyZWeoP7jlxnq2VXy4pjDyNRQw4qdQhqRV +1lshB4OLFp0bAgMBAAGjfTB7MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgWg +MB0GA1UdDgQWBBT2/t+xgP4v4Isqyqjzq+pnfssP/zAfBgNVHSMEGDAWgBRRu1MF +Wqphe5Qgd/R4cBHVJejj8jAbBgNVHREEFDAShxAmBkcAAAAAAAAAAAAAAAABMA0G +CSqGSIb3DQEBCwUAA4IBAQCcqfwnS4h73UfI3jd1OjoTy32nvGFEP6SbzhBbMNFB +otP9mUXZvaXmSVyTZPoUBGmlH/ovZMrUuVmNSnQILoNDnHRi3RZRzo60LfTM83eY ++9d5+OHJ0LzMyJ/NiEam739nXQQpC1o4vS8SxiDdei69+dofTxDKO7WBEQZ79xam +uakn8g2fWFydpf/FCJBf2HShYdyOJufcAJoUpqdtitIFr1hXwbfG94QNhmk+j570 +DTCMgwRkNIrzE6n4wofMskP4cRsCRCZaYuRVoDH8OmmbX8ZNT/UWd3kH0HugElku +LCIrQJeurSCyMqAjPHikGiCgFOXUrlQcB4xVRU0OC6l5 +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/excluded/root.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/excluded/root.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/excluded/root.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/excluded/root.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgIBATANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQDDB1Cb3Rh +biBUZXN0IElQdjYgRXhjbHVkZWQgUm9vdDAeFw0yNjAxMDEwMDAwMDBaFw0zNjAx +MDEwMDAwMDBaMCgxJjAkBgNVBAMMHUJvdGFuIFRlc3QgSVB2NiBFeGNsdWRlZCBS +b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzQXjcEco3AcMSGhL +dByzTghgtevSwl4EG4TkyWIAeLB5WZMkscdBajNAotj0WD5umvWuF3Mhd37tYHIQ +4GVfmzEYlY3kUhjZ7L+K6LS/PYog6gzxodlTqOOgRxBjLZhRLYc8vewUM7EJO489 +Okwe2y7axLbIBqCjRnZHWVZYQtJhq3fsiR2UHlFtWtF4xqzyooRV5tXjtaG36Q/u +jD2pyqcTP1tIwTfVVPaVCS/dsgx9aQHaF7/pYpX6irYHJhe4H8DMZfuRcHDUpsf5 +u9vbTWEuVhQtny2uMc5zl/309PdkkrymjjHFkTc0fD59PmpzfiJ1HjRNpBT7vCKI +3156OQIDAQABo3YwdDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd +BgNVHQ4EFgQUUbtTBVqqYXuUIHf0eHAR1SXo4/IwMgYDVR0eAQH/BCgwJqEkMCKH +ICABDbgAAAAAAAAAAAAAAAD/////AAAAAAAAAAAAAAAAMA0GCSqGSIb3DQEBCwUA +A4IBAQCZBJzw7zZrBxj9zQXBYa6Qgg0RKtbNPe0O1iWLdnYfDva6Zf+7wjVepX13 ++CiNeaKlLl4chntAFHEtJPvpB9fMSUpVp97CN4mlRE2ECOsuUU0xlFyuwYJFPuLu +fZGuq2JmovItj1aHKUpUJc0/fFBepA5P2dAPfwNY4XaVRClzK21JgQFlMb5l+UKD +9DCguupK4ZiTG6fj42Kw/zG/pX78ZQKi+GuX4dCUrmXXXNOLCbVhu1HDpwCLUjVV +GxHU7U/egwb3mVHk2bHg2/zkKCnviyU0Z1T3KAU9es3xXzj+Jz5rYFQZPfvzDIKw +EC3VRzOOLXSKAWmJERD3Y7DnlhGt +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/int.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/int.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/int.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/int.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIBAjANBgkqhkiG9w0BAQsFADAeMRwwGgYDVQQDDBNCb3Rh +biBNaXhlZC1JUCBSb290MB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFow +JjEkMCIGA1UEAwwbQm90YW4gTWl4ZWQtSVAgSW50ZXJtZWRpYXRlMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnC1HaY0V64wh4g8RVEi3/yJJk5sPg5zK +KK8OujI7nFtdvZvZpMq5ui1+bMdwiHhAWNgUF0g73EauNTZzheo8xrGyz5hc7HuA +vz/2F5D6H4CceJiMa0PZay2iTQHhm3+WMSMn5oeITryk3bicfRnMhdl8UqGZBkVY +Ypq5xnSPyqrhBLf9uEhHCSJNvBt9l0Hnpad6dsYONi6ejahMYnMfoQYvGRzhuPP5 +MLXSUAa3ggpcXmVRCQt+GbLFfoEYcFw1UMH5D8yMLAJ1jBjHR3XwZO6rH/k+uFAL +zz5rHME00IPfYuafHfUw/Mkfv8GZEV9b0raA6fqhKN3cAKeaRanPbQIDAQABo4HW +MIHTMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSh +Gd3JSpjwcaOe5TL2mkbuYZi99TAfBgNVHSMEGDAWgBRq8/w2yBXVwb/rYGbkMbti +W/W+oTBwBgNVHR4BAf8EZjBkoDAwCocICgEAAP//AAAwIocgIAENuMr+AAAAAAAA +AAAAAP///////wAAAAAAAAAAAAChMDAKhwgKAWMA////ADAihyAgAQ24yv4LrQAA +AAAAAAAA//////////8AAAAAAAAAADANBgkqhkiG9w0BAQsFAAOCAQEAtYeDCmo5 +RE1VCvrvZU1p58lTntsoxgeVy5xNXFMnrv5Nxo9x2Mm6h8dRPkUHD+Z3F3/M2an6 +fRz1ga0tXSMvwZl/lJq+YJ4uJgLZSvh/U6bWMkiOoO5TRg7+ZI1lZ6j4b40bAlML +osZgKE69gHxSdAYHHi30RJ5wKluK3mDAwtQSjBIxYIjN3gVYl1Ezsaj+RFa9LC9j +I1eOUH72z2zLOcR9Y05GOePLKWyavplBF3BMdKH1rUt6retNDSgK3xhyWhQzJ23p +ZziLLjHlsBZFYVEb8geqakt4sPOA0GhE88+I45uUkh8LZcQ4XWyy9KOJIxCedV7O +gtgNna2GlyAUFQ== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_excl_v4.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_excl_v4.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_excl_v4.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_excl_v4.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDTzCCAjegAwIBAgIBDTANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtCb3Rh +biBNaXhlZC1JUCBJbnRlcm1lZGlhdGUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAx +MDAwMDAwWjApMScwJQYDVQQDDB5NaXhlZCBMZWFmIHY0IGhpdHMgaW50IGV4Y2x1 +ZGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDi1lp52TmE5O2NURiz +BbQ2qNnEL765IivJ/DUgvwMiBz3D8NUZxLEC6oULPecMlhaP+YzIgC/AtBbE63FA +L2S3m9hLnRjwxMMoMdXM+XndzIj1HH1uVVo1rjmTUk4+INchTMAMGKpPfnp8+3su +KexZy5oBjfz/RNd2aVspuIPAWrkXqyXNh9cdAp9PkIH1OGH3HNxIBUbo2ABc/JWN +qU113GXhqKOz/r4Mp2HUBX2lE1KSrOCa5nMWMhmkslOO2s6j6Tzir6FRXGv/WmCT +yVltsh1rIndCA2v7Ek/KnU271L5eKPEWcL+tz4h/v5ygr4U4oo88YHrjMP5bOuSK +sIDnAgMBAAGjgYQwgYEwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBaAwHQYD +VR0OBBYEFI/jrNsLsSElwpK2kXMxI3uoMl7fMB8GA1UdIwQYMBaAFKEZ3clKmPBx +o57lMvaaRu5hmL31MCEGA1UdEQQaMBiHBAoBYweHECABDbjK/gAAAAAAAAAAAAEw +DQYJKoZIhvcNAQELBQADggEBAFP7Fquabo5gwlGVWe56mXJRoO01PDHAGlazLwOi +0SSyTyYZUsgFOd2eJyozO88cBGWVk3habqiPQV33/BKoo06WREHVI0YckCdHK1TF +ERxkbMBci33p9+ZVXojxThXgyg//ro/Yl40kgIFVJkWRX0uf4AjzMdHzg9ujK51a +7mFhQ7D1K+4WPmSbW3oOVKxAh/X+I/UfjYo4vunGH2eaLxCc73xaMJ2URHgv0R+/ +i07XRfea4X6caBCHhwj6rCd/3d0esoXqgEbSdJXmbA6ymJVslW9vaKN0Eu1G+5cF +nQBn+ffjd5cMPEjHbpeRmRndvG5wl4iZejsM3V6v+LPGVNw= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_excl_v6.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_excl_v6.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_excl_v6.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_excl_v6.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDTzCCAjegAwIBAgIBDjANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtCb3Rh +biBNaXhlZC1JUCBJbnRlcm1lZGlhdGUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAx +MDAwMDAwWjApMScwJQYDVQQDDB5NaXhlZCBMZWFmIHY2IGhpdHMgaW50IGV4Y2x1 +ZGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDE358THvNP+vzAx5XJ +vCaqIR922lvxGwtrLLmH6FxzljXn0K2m1yC1doMUpNNf/FdBAbjaBSz9DRLySMcR +WXG0Slz0DfFLOyhuv5U4C8zTRqqq4LNlnsJBNKFGio7MIIiJie4D8ytVF5ofLg5J +9CRIryhjoheQlECpRjO5hu+v6B3+Hr9EAXvAF8Wgp5AbmBY4yqKY1h4fsgkF9hY8 +1JRA6gAwtCXGg8+nLmXCka4TaWfpmVgQDEFv9GtU+ShvxcyM+uZ7EBMh6fJDf6Dm +L7DQsLiOSbdVihI6WotNzwIKBhjfggUgVNFlcnZt3P8gz1BCYz9ZEefC7/xwonF3 +72vrAgMBAAGjgYQwgYEwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBaAwHQYD +VR0OBBYEFNp/vf5rl837wASzDo9wSJ3m2xSZMB8GA1UdIwQYMBaAFKEZ3clKmPBx +o57lMvaaRu5hmL31MCEGA1UdEQQaMBiHBAoBAgOHECABDbjK/gutAAAAAAAAAAEw +DQYJKoZIhvcNAQELBQADggEBAI91ra3rMfsd0T3OKtNMPxAgjREXyaNb1lVOfBN+ +GRia2liVt7oBlMS3Akjnm5Kmvjh7HWTGmIZ3p8rsCacdWygW2w4ktzdL2FlRGeep +04fT4FHbGL68DVWb+zsZGYQIxL2vOh57W17PcDMuznutK6DEUiuOm0uUMh88m64/ +pwLaCnooz7dUTmlaAk6fow0gHoMXVwjjkujc8C+lOyvuxEIDnPWjJ42t1xhEPxX0 +ml2Gbaz7KK2vcdMWAgAlQ/e6gRFJH/DbZDPW/5WDiizwh9G7/DzEDGm3tTRgzkN1 +NtQL9+TVFIL/BQi2zsv8swN9bmxP7WywG5qZcmHd7QCl720= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_int_v4.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_int_v4.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_int_v4.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_int_v4.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDUTCCAjmgAwIBAgIBCzANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtCb3Rh +biBNaXhlZC1JUCBJbnRlcm1lZGlhdGUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAx +MDAwMDAwWjArMSkwJwYDVQQDDCBNaXhlZCBMZWFmIHY0IG91dHNpZGUgaW50IHBl +cm1pdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMEOwo2KuoY3NKNm +fum883uL58PW0yNmKNnIvEl2eipG1Awqp6/ZZJF4ZNwuTWdy2cdDcYWLB+YWwfo3 +DV52H8nhAGY8RCcE7fg+b4T9FZYocK8LJkCsErU4FLWwXMXC5SMzqHDj2ueNPRnx +xuKofDuimFeGq3kVC4ou5EV0YYvM8EAXZf2M0qBGD8uSJ+06LT14NyFHji4kBR5X +VOiuKhQ3qrutpWpAmNRS526fpw+LexhLdDWeafnJD6SN+duBcax7XFsn44cwyAo+ +UKFh29/QGsPhs5lfFhGlEEa6B2v/YJQDp03LU7c2riB9KDM6G1i9lzCXqPNSrTQR +6yFMIKkCAwEAAaOBhDCBgTAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIFoDAd +BgNVHQ4EFgQUaxe5lXkun+OherTCtMHzEvXcWmIwHwYDVR0jBBgwFoAUoRndyUqY +8HGjnuUy9ppG7mGYvfUwIQYDVR0RBBowGIcECgIDBIcQIAENuMr+AAAAAAAAAAAA +ATANBgkqhkiG9w0BAQsFAAOCAQEADauouZhEWA68aFdew613yvb3FRUiGZvJB6w9 +2gr1YGO/CPoXiUW7vWBZJ7KmX6KTVszZdt0OqYwyOl39D3UyoHJoOpsioXKel2Ch +4z8ZP1WCOXQIEn1TnUYkkbtD+r+cwSr3rYO+sTYtWLIn5DLsd4ukuE+hSLqxHMbl +KOtNgB9jKmwOdLFb5AgrO6nNLPyh8hufFmpVCZUeo+MfRG466O7acci8KB/jxc/G +HKm5DOrcGmJghr1uYMdX5QH0ym0fLdb8q3UtnZ11xgDYNBEkbZm8oZIG+Fjex6eh +BXCA5jAuGzYm0f6PjZH5G2PUdJwbUpqSNrKBCcx2eSeiBqZXPw== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_int_v6.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_int_v6.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_int_v6.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_int_v6.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDUTCCAjmgAwIBAgIBDDANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtCb3Rh +biBNaXhlZC1JUCBJbnRlcm1lZGlhdGUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAx +MDAwMDAwWjArMSkwJwYDVQQDDCBNaXhlZCBMZWFmIHY2IG91dHNpZGUgaW50IHBl +cm1pdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJtIU9PvuwHcO/md +tf6sm1Na/2xI8jqG8UdkyrPCae77aOdJtZ/fCPwXJTbGTCtOJMwKKUS7LKYI/3TW +cDFE+iwBuvl1FfCKijRmDO2RJ3mAKxfKb+J0pQeoKW+WCQYdFtwvv2/A4kdKoLyv +bak+XdVrIni3MvwS1vSle46BUGrWbx0Kx5Pdfcr0avlqPScxZIswqeGjpJIxP3qh +g97AqQZOfObfTkgFBhJE1clHNK66qnW8woJfJXo13wPFeWwcr7tKXNhkGZ6IZNdZ +AkZ/siW55U7ecZsWK0/JNhhoaL7AJR6MFtO+obnPZgv67qk8c2jIzVM/dconrXNb +lH+5jekCAwEAAaOBhDCBgTAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIFoDAd +BgNVHQ4EFgQUJY3HlRVRsyCSQDvbwcKCoQlHtNwwHwYDVR0jBBgwFoAUoRndyUqY +8HGjnuUy9ppG7mGYvfUwIQYDVR0RBBowGIcECgECA4cQIAENuN6tAAAAAAAAAAAA +ATANBgkqhkiG9w0BAQsFAAOCAQEAJzs7zd6hiOg8yn9fcImPGMitpWAS9JUCZ7HF +wpyoLpUzD/W1OlDHms+jXspv5Mqk+GUjPpLvil5/MgypdkTiAG4opkVylCt5sLrN +lJWVo+1XsDkCPnPoGWyw8cQ28zUM6VBETuK59+wyrIUsvGoOFuA08Twm939Kz2+v +vTzhQLl3yo4Km86lrD+/Lhpy/o0QdOVnO92ZCABbr9wAXgc2KKxHKOWxtXuDOqBw +OgEe6ysUMlUOwFyFXbcH8rXNh5pi0QCuCGbNYJKJv76CPfsB5J/1hZx262wx0SPd +QKKmqXqArSyOgvojkRBu39+b6B0AjXwJUZ9+kDzyE15p5vYiBQ== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_root_v4.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_root_v4.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_root_v4.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_root_v4.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDUjCCAjqgAwIBAgIBDzANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtCb3Rh +biBNaXhlZC1JUCBJbnRlcm1lZGlhdGUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAx +MDAwMDAwWjAsMSowKAYDVQQDDCFNaXhlZCBMZWFmIHY0IG91dHNpZGUgcm9vdCBw +ZXJtaXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5y3gCO+r6c/ts +bA7DIb/uBrBU2oh7KpbvZ3tAqONEDuISWKHt/YZ77nFvN7iLIskPCAFIhcqV6GVf +AmhLmYpXmAHIwcUeHEY7QDoNeExbIh3+iKEE45zQXXgAam2IF5eY+BCn3EYW+9BZ +ddHWBQsr3nfhjbHAKJ3E9gv3YUmVpheHDgFYlMBZZMHaoAkY5ezjev4DqctHHoVj +poxw9xjk93QeC0kMr0shfwjxuBzmzKzlsigjUxVKOoJ/UTPCjpz3voLbbxIrcAtu +gy9XVjbo/Wi2Rw/XVjp3YpX+fbf5JknrsrC+RD+hypqQWtTvjbRqj75RJMXq4xJQ +ZbuvEsupAgMBAAGjgYQwgYEwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBaAw +HQYDVR0OBBYEFNSp+o7PMa0Nuzv/tna9LHwT5prgMB8GA1UdIwQYMBaAFKEZ3clK +mPBxo57lMvaaRu5hmL31MCEGA1UdEQQaMBiHBKwQAAGHECABDbjK/gAAAAAAAAAA +AAEwDQYJKoZIhvcNAQELBQADggEBACu8ej3TEohSm9rqYYSYB4Dr4/8/DfZwshK8 +sPbsKn6vELWS16ydUyww5TYcft0bLmns5Q7vve4T72EdCzMvR5wIgFnhsAZiQ8dQ +zePDqk2isTU7OjUnxHPuNe+mOZKpcUGZpd09f4xZww2S97cn60sdhJ+ifp8UPA5b +TLIiwkA7QEoCDYXSvhFdJDpQr6o6Lx3C4lFlwt2I1kc2V9BJs9Fp1aFc3tvhtz7h +1G2pIz2L5Q/o5dqwgGc/5qa9aLJhdPD2yBMPldijInVgcags9Ouvm7VXGcOVozNc +39QuFz1mpIysTYeHs5lsiY3t/LD56Fm9RsSyCYMnE0tsAUoRpzY= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_root_v6.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_root_v6.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_root_v6.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_invalid_root_v6.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDUjCCAjqgAwIBAgIBEDANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtCb3Rh +biBNaXhlZC1JUCBJbnRlcm1lZGlhdGUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAx +MDAwMDAwWjAsMSowKAYDVQQDDCFNaXhlZCBMZWFmIHY2IG91dHNpZGUgcm9vdCBw +ZXJtaXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCmy0nEx0A9ELmG +hsdgyf+W8Wb9j3xe0UI/++R8W7IJ8NyNU6TEcoT3oFoUCjQOEY4NR2hPvoEzkt1/ +jtS6LF+s4molDBRcAE2Pnsh2B7W1uGYktwgT6opuXnPCXHZcp0qmEc4jNeUhRyov +hPzTaGtwhF3eL28RCkaRs7D3IbrP5Si7td3+4ANSAQCyS7PS4Pjeq1skOwL6CX/C +mJM+jv8ScEx9CH/aHb7J0Yx73yChHRDsIaAqTz9YWcMC4f4Jpf9JCzY9WLPmV+7Y +vq5n7R5Wryf5V/blQ/DtfccHolUy39BAJxlXl27deawzsSqIdRt6+ZvJ9BgFt1Eg +1xmsPi+xAgMBAAGjgYQwgYEwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBaAw +HQYDVR0OBBYEFLC5gfBnjtSSW7ThdOssZMxwyXCOMB8GA1UdIwQYMBaAFKEZ3clK +mPBxo57lMvaaRu5hmL31MCEGA1UdEQQaMBiHBAoBAgOHECABDbkAAAAAAAAAAAAA +AAEwDQYJKoZIhvcNAQELBQADggEBAGRWVxyqNIQ5HNMcrIHb9lPaL1Ttu14m2Vmf +VqroVDLT2U1HQLiKxW4APX0HxaQrv+xn2JTEjWk/QxiYZO6Ty5fjw52WeS3ZpgSo +EwkCjqS63SJrwWV2la2VtdHfBau5azg8Dpot/QuKD/uPRKvwbQSCWH+cEzp97zEo +BBQwXMrWqrGjkyuwcKGrvD6jcb3NOEUaMmSR0OQm97J+/joqOww+rfyOj76JV0ac +WmFv/bq52JDEB5LBoy7+ElxNjWx4kxBr4p+OcJix8hgWGlIUoZPFieWAMiE2EIxU +86xKaEd8IVTJ3ykguzlaZpcWEaOkREdtrB30b8QP/gkb7pEuauw= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_valid.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_valid.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_valid.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/leaf_valid.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgIBCjANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtCb3Rh +biBNaXhlZC1JUCBJbnRlcm1lZGlhdGUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAx +MDAwMDAwWjAbMRkwFwYDVQQDDBBNaXhlZCBWYWxpZCBMZWFmMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmaadhiyxvzTIjowLE4L92qptK0Hf7l15BuT2 +BcnKzoMIegGBr5V+F005m16Ihnw5eDMzLLwJ/Il1u3RUiCRUXgATZbcEnC9LSowx +YwnThhSGaESTcu80iwVoxp59QBvn8MT8EE/Zfv9AbH/ItcWYhI8yGPpNSS/ct6Kb +bixWQi5NGw2M7dUeCEVlfNTuk1h1NFYosk/sy5bh1P+2+KiIQKYk4NPqVeMq3D/X +SF0Hjx7CTTCHQzB3WmZ8bKiA/3Y3atCC8ezB9rCyrBe5Vm/nifloamPLdBv8sFER +CSK3defcHqEKF/yeGp+3Vgc6r33xnTOGlPkcMUqFvxKr/6dSQQIDAQABo4GEMIGB +MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgWgMB0GA1UdDgQWBBRyYrMTHGh6 +P+dapktgrYFD9POPrjAfBgNVHSMEGDAWgBShGd3JSpjwcaOe5TL2mkbuYZi99TAh +BgNVHREEGjAYhwQKAQIDhxAgAQ24yv4AAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUA +A4IBAQCGrP7V0ZudtnjSxEmCRLg5Igw2+hRvXMIUZSP1lsva6ZtLeUhO6SzoZ5WY +eFFsbXiSgGfI0wzEO+m6DSdhUUXxPu7IPe1jt0QCK2UG1YMzPipqk+Ptz9ZaqwYT +KyK8rp3NzACnMkdZe/BQAfkGHFbSIkWdAdHa6vNlg4YW4rrsvWiTEcfLV/kbezgr +lq7pYbM8A5F8Yf03REXbtCMcR4K0enlyUz8t9xX3rVp0ZN2+GrocRMzOEregJmV5 +QTuNUdHH9yTlTB72DP1E15bTJgtnOxdbs98023GR3GW7x5e9y8bl9eRxJ69GCsgM +JYZmFJdxp5L/m7T5fioeKxFOcs61 +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/root.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/root.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/root.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/mixed_multi/root.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDOzCCAiOgAwIBAgIBATANBgkqhkiG9w0BAQsFADAeMRwwGgYDVQQDDBNCb3Rh +biBNaXhlZC1JUCBSb290MB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFow +HjEcMBoGA1UEAwwTQm90YW4gTWl4ZWQtSVAgUm9vdDCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANyqdhZYcTpwU/uULwbNJsteoRtOxos7+Hnr5HdW3Rf+ +ysxxzslleHeFCScEAR7gvZ/RnjOYUyvfxJlVwtamPUa2nz4Ytt1j13cagFQdAL2u +RO6+TPrtMYA3cZJvsKwqIvjPwzn5WMjdEMbETRFiL9gfTkvokAl84aUgdk6aVBE5 +T97GxZigvC+YVef/fWu21nj81Ubs3dgBxjXb49lc7jw1V9YYfu+0n4xM7uLgh2u7 +CPF3lSeo8YDFYgV9PZZKeLIMoj8Ut4RMl3ORGWAN25aV8GCKdguobDQwk338CD8y +b2nNmpDhRyGdICG7KP3zv5LqqBWAjrKXvNlJdb245r0CAwEAAaOBgzCBgDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUavP8NsgV1cG/ +62Bm5DG7Ylv1vqEwPgYDVR0eAQH/BDQwMqAwMAqHCAoAAAD/AAAAMCKHICABDbgA +AAAAAAAAAAAAAAD/////AAAAAAAAAAAAAAAAMA0GCSqGSIb3DQEBCwUAA4IBAQDW +p5+t12+lZKNAwMIphYf1Hlf3RTwrl2EhG9OEeJISSVgB0TtKP7B4cvdeoH9zCLZx +e2ZSxbYSDbAp3Rup8E7fO0IvubJT0JMoHwlmARhG3ly5nVB1D1Ej6Dy3DpNN3Z8C +p55RsNdeMQ+DzYMiyyviks4nj7rqO+/vzUavxsO69Gr5ddpSc9y+N2ggpQwIRg5h +p9DpN4L4IcLZy34cwp4a/tO1+5jSFymkDI2/tzupirpuKWrsRDugL/HC78AcCejf +cg36wvUTntVu5rUJEiXN26ABbTRfSMgNsfczplYQWt+g24A5ct67QLHO3Sy2dSr8 +KqUWk48JJ7G7CTBrojmM +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/permitted/leaf_invalid.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/permitted/leaf_invalid.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/permitted/leaf_invalid.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/permitted/leaf_invalid.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDTzCCAjegAwIBAgIBCzANBgkqhkiG9w0BAQsFADApMScwJQYDVQQDDB5Cb3Rh +biBUZXN0IElQdjYgUGVybWl0dGVkIFJvb3QwHhcNMjYwMTAxMDAwMDAwWhcNMzYw +MTAxMDAwMDAwWjAuMSwwKgYDVQQDDCNCb3RhbiBJUHY2IFBlcm1pdHRlZCBMZWFm +IChvdXRzaWRlKTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALzARM63 +ABusErwcHKn6ezraCSSwQO0jJixlXqeLXLwsFmV/P9ku6lhKLmIlI36piU3EEKC6 +UIWdcY4bwhGR1/0bjSbydncvr83BOdwZDql47AfhRjTOBEYXU9p2lEsW60JehDvd +AvvIftIgXFxFTmkUu1Wl62cR+HwWWaxH7JxFAMS+TjWqvHaps8gsl9c0BOo3buGe +Q7ZJX/LGzteVywrfxq1Q5Fay8i40ZUS8qxR/QEz6vQScdFHcOlsHJT/jjJK2fkfU +11aZxRL/GKDIY+meCRpZCCvayaFI3Fe/jSPjvP+EDFW+keEblcWZhMQtJtso8QwL +p8M0CluU0YSlW9UCAwEAAaN9MHswDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMC +BaAwHQYDVR0OBBYEFFXAwkCGHJZIeAmoPlMmE7rsPimLMB8GA1UdIwQYMBaAFPBN +mKndAmL2gTCCWAccm9nk+bWcMBsGA1UdEQQUMBKHECYGRwAAAAAAAAAAAAAAAAEw +DQYJKoZIhvcNAQELBQADggEBAFh/GNelFBw549iUiXwR/MhiDh0ZaNuSWUVBfAkP +3j44ENMvVKEP6ICHEpJZAQ3AaQP6dBrPY0AEtCyybG37dBHACQqSRqKaxzf+0nih +bALO+k/UvFyBzp/St2W9R4eu0xab+p1Ju2jeNJBQMGTGKV65+JUerA6tjLTPthC7 +itcFduYvNGihYNdqK/g9yzoBZblGxd1UNQlhuV5Mbmhtm0Z0SuBnbZQzArJvYrXy +2P1w1YyIwB/jbtOY85WodrSPDAJhNg+dIPUg5BSxCD8QZdZBgdL67mPaa3rGT7FH +x5u43Z+PzLLun2Nb189KI/b/hOCyZA1RKFc6oOD2AuUGiK8= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/permitted/leaf_valid.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/permitted/leaf_valid.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/permitted/leaf_valid.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/permitted/leaf_valid.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDTjCCAjagAwIBAgIBCjANBgkqhkiG9w0BAQsFADApMScwJQYDVQQDDB5Cb3Rh +biBUZXN0IElQdjYgUGVybWl0dGVkIFJvb3QwHhcNMjYwMTAxMDAwMDAwWhcNMzYw +MTAxMDAwMDAwWjAtMSswKQYDVQQDDCJCb3RhbiBJUHY2IFBlcm1pdHRlZCBMZWFm +IChpbnNpZGUpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArjQfUt2F +NAez4OS1js4lGo+QBnktlgBqVq2nFInmDsXyrBnCFwG8k5ETZ0WXlJPGjpJ9OtX2 +muVaDNCMbngHVjXUFKf8LwEl0QZO1jK5pEWehct971xCjX+zJOq8ToazvAj9ZfQu +l7G1oVohKA/r96RIjl6soPd9IR8lh5qiIBMnhio+Ic3Y3j6F4kYKmUAG+QE3Z3Tk +TGbwSd3qQN+xPjYXS5OJpBL3TCETsZCcF5sCQ7fQbUJOa5bBHu7ce2eBXXbWhyEd +diF8DSN3FKLLx5NCCC0pyWUKiADkB6otjNWWm8zZy9Wov1LYhiW1Dv706nGi8YF0 +HQM96dKtipIWOQIDAQABo30wezAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIF +oDAdBgNVHQ4EFgQU36vERVP3clB8BD5zidLhOLcd6jMwHwYDVR0jBBgwFoAU8E2Y +qd0CYvaBMIJYBxyb2eT5tZwwGwYDVR0RBBQwEocQIAENuAAAAAAAAAAAAAAAQjAN +BgkqhkiG9w0BAQsFAAOCAQEAGaAhHx8ojKJG/hquS60vyWFYPzhMf/qZ/RBoFCMG +6eSW9Al9oemLaPWTcSj1+l8VDZlsbSDQcevQsdu7WT8UBdLRV0ZKfCdTgpMYaH0S +EYILxEk/H9nt9NhXIhcon50f1/OILMhjXbVyIGqQ3r38fJgj0jutSkPLaK7hoXXF +bZevQ8CJ1BeWSlRODhPPnw77Llk/qrZhPJ+oByDAbFJ/U+Or0zJAK45DaG93bSQA +upeMrZlkPfk9dnyms/tRwM/OG3AQoItKk8JknUZoFaubpbuiJK4vIpTssZHs4des +Nv5lTdIO/swAtX5Y/br9sNrCWim3HGD4LASqpana+3Svtg== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/permitted/root.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/permitted/root.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/permitted/root.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/permitted/root.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDQzCCAiugAwIBAgIBATANBgkqhkiG9w0BAQsFADApMScwJQYDVQQDDB5Cb3Rh +biBUZXN0IElQdjYgUGVybWl0dGVkIFJvb3QwHhcNMjYwMTAxMDAwMDAwWhcNMzYw +MTAxMDAwMDAwWjApMScwJQYDVQQDDB5Cb3RhbiBUZXN0IElQdjYgUGVybWl0dGVk +IFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDD4932dJsQY81z +vwL8mhXmOV5pXTbJNi4QyHGXd50SRHhTkFy2Tqpe4tfO0Rjm7T7ZAfRPLpN15+Cu +nV5T8f441SXc6Wb3BFgyxmG/u2wYDPeD4p/6J7MFrQcHs95dekyMe5KI5VG2XZi5 +aXGofyTtFVOfni+bSmubI/VvxXbraTtAkjhDapy7uS9fgjmI0iXzVbSj9A/7YwF7 +Hooyjsc4lSeH5QOypplghj0NYFI2shBXdVfzf6BEFZSAreNeRZuwlP5oUYmMmgDr +fVI2JyGfYrcMmpc9xbcGrMoKZuAak+kcWvR/zZ9tLycuFy44QhPZYSE7E95aqjRo +Tfb7J40FAgMBAAGjdjB0MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG +MB0GA1UdDgQWBBTwTZip3QJi9oEwglgHHJvZ5Pm1nDAyBgNVHR4BAf8EKDAmoCQw +IocgIAENuAAAAAAAAAAAAAAAAP////8AAAAAAAAAAAAAAAAwDQYJKoZIhvcNAQEL +BQADggEBAGu6QFMjqYvoGY+/JGlZx2+61ZGvXU6unKF8XD9xVEqL0jDEGloqVbJi +McpITt6MFYqXpkgLvUgxM1dSd+ycXbHXIydvI3zTmNyacsBfPzzWLOBITbE/kvP6 +2r9djG7NwaYy+Pg4ROlYgm6O8dZYxSGfFZsCPoP0015As/3kivfrGi++lgyN0MJO +PFUQrGD9aY0lMLy1mfIpTIw0t3W8dy7R44Cjy/EWSlQX2eW3U1e6YXQPql86fZ0p +V9KOpXtnD0wYlZSOg0lf7WPsvOhesx2s+Lpck9C/aP94RIqSJfDmRFtZ6rOb2llc +qCknE9dHBsFqaD80ups3ih9DOtScgDQ= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_exclude_mapped/leaf_invalid.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_exclude_mapped/leaf_invalid.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_exclude_mapped/leaf_invalid.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_exclude_mapped/leaf_invalid.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgIBAzANBgkqhkiG9w0BAQsFADAsMSowKAYDVQQDDCFCb3Rh +biBUZXN0IFY0IEV4Y2x1ZGUgTWFwcGVkIFJvb3QwHhcNMjYwMTAxMDAwMDAwWhcN +MzYwMTAxMDAwMDAwWjAdMRswGQYDVQQDDBJCb3RhbiBMZWFmIEludmFsaWQwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDQoFCC+FI5AQ8h9flRna5Od9LH +5JAc9mP4hvlCudQm+Bz9G+saK8Ys3e8l0Ksm3x/9VT1g0EJpJs3R9zMXXWO7lY6o +CnnQhQQzZ828CC+1uwICOg0cXddE97+DLAMVVf48sl22Q7rrOUuXwWmmbkViTbKo +WDbXwUpt9GdDPw/1NC2QgnL6nv0oceKm7Wz4fnKbp/MnxCOi5U/D/CkpJln2l9I5 +pIX/9zAvVv396cyu4hTmxd96D5oJ5oBOIYVMqDVO1QozX45bmehivO/zGymPxUOc +0DTbqj4lNoGI0iyqzqQz6qkoO8iXgXzdkq5LsZ7bhNauytWOLc93JBHNh1JjAgMB +AAGjfTB7MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgWgMBsGA1UdEQQUMBKH +EAAAAAAAAAAAAAD//woAAAEwHQYDVR0OBBYEFOCuW9XLLtrWgR8IZS4nj+V7GQzr +MB8GA1UdIwQYMBaAFF4vQ31gKMseGPVOVuD4fplYO8OjMA0GCSqGSIb3DQEBCwUA +A4IBAQBkpF1kiY/wc9jYPEHRt9zp42nXXy0pWkTLqe2O0xKG6+vT9OvVeamQsjd0 +NfZxtSGf0evMp6pPDEZcYDiSL8x0EvUX0WjkLxQEzf19b8Hhb373gysW8NyRZDpy +cl2aa4MuF5rUmukTrWqDWDxGScdHhrVikmAS8+e8wwtfu4DuIN3onU2AMKpAyqE8 +04mbNZQdChxKiGT3SHz6+ERit5BkcNFJ+luuTThWgd2T4YFMlygs8pFaiDSl1cvG +SacBCSzpzl4RSCAEuB9KIpLht9QUBiOWw3TTrBGpRlY19a0aakKtgGZf/su7LpNR +yclZgdytRmp7xKIHH2g7e3G1Mom6 +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_exclude_mapped/leaf_valid.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_exclude_mapped/leaf_valid.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_exclude_mapped/leaf_valid.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_exclude_mapped/leaf_valid.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDPzCCAiegAwIBAgIBAjANBgkqhkiG9w0BAQsFADAsMSowKAYDVQQDDCFCb3Rh +biBUZXN0IFY0IEV4Y2x1ZGUgTWFwcGVkIFJvb3QwHhcNMjYwMTAxMDAwMDAwWhcN +MzYwMTAxMDAwMDAwWjAbMRkwFwYDVQQDDBBCb3RhbiBMZWFmIFZhbGlkMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjmsVG8vM2oEFYzqKFUHp8HSCoCLJ +Gymt1eN29AuBMHuVU7+X8eqtAio3M/r2bxKngKrr61H9nJ6zjo+Y4wMtLRBaADGA +49xuzBXYzO0e0f5Bm8JpkKxnpZLLGCUg0rhw1z8jypCza/0WbSwaeJNQSDJE4UE1 +YkDrzZxPOGQHB6PV1+8Hqx04UDOWCZ6rqUzgbcoQXJqF/LCUPuwwkLFVZ2clr8WK ++b7//8Xb26x3J4bBaZ4GNK0ycuT9yV09t4hrSLoJEbH7fiun/CwRv33sNbRL8Ll9 +1kqqYLWkDCguKifaSC9x7fVXWCeaTghYwchUboxkCHVfqFtLBbHRVaXBqQIDAQAB +o30wezAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIFoDAbBgNVHREEFDAShxAA +AAAAAAAAAAAA///AqAABMB0GA1UdDgQWBBRsS5EYpV22QIROTwViF/5q54pzbTAf +BgNVHSMEGDAWgBReL0N9YCjLHhj1Tlbg+H6ZWDvDozANBgkqhkiG9w0BAQsFAAOC +AQEAoFcdasTQkqK/MYZQPutzvgMbVajBsHLxu88XdOV3L85EDt4tW537k6lXw/iy +TwQRX8/EGM5BFFi6tTlg6Qr1aqD76Bc48DaC/N7KGvH2hiXwyxFjvDZchbpZ8bJB ++eD7OVkHChzy8qlJg+vnB+EvfHDjk5b7NJ5l0TuJo4L3sCV+lCHBHF7d4b0A337d +GXRShdKWbg77AhSVySKIYzX8YRXdYg+5R/0O+J8+7BGl82Y435aCw2eYbOoU2ihm +1VPAHtTE5w6pAceZXEk1UTAoXxFiRQR+ZUHZSxtclYmocVuyrDNO+LdBCiZ6x/cF +U+pKJhKdiSu7kViEj8pUNck3LA== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_exclude_mapped/root.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_exclude_mapped/root.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_exclude_mapped/root.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_exclude_mapped/root.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMTCCAhmgAwIBAgIBATANBgkqhkiG9w0BAQsFADAsMSowKAYDVQQDDCFCb3Rh +biBUZXN0IFY0IEV4Y2x1ZGUgTWFwcGVkIFJvb3QwHhcNMjYwMTAxMDAwMDAwWhcN +MzYwMTAxMDAwMDAwWjAsMSowKAYDVQQDDCFCb3RhbiBUZXN0IFY0IEV4Y2x1ZGUg +TWFwcGVkIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6yqnv +GwPGvrh20GHC4J6oHKCPfee5utuqPSAAxvNaiMEhYM4zcXmigbe4j4s5Y6k0/+Zn +PwWOfF6grciaCYzsQCoiVbZ/r97CWoe7FFOGw2Ajd41b+NX72HhuEdtg06GS5Fzo +rJrs0LrqjWO/mW3swluZq9t9SRGOm7JCoKEtQ6ulWUlyCa8OpR3Vu+/sVOb/ZSrZ +WaNZhdPxkH0oOQX477S9gzLWgLqImHjT/REWs3D6ciWit9BxHx8XZSUZc5lagFUW +cJf3q65MuDLeC5fDKcKRXo1sRPjp+5GdjWcm4ph+heW90X3LXAxMX5EFSdSvEtA3 +Y00uIIDRzn1EcMF/AgMBAAGjXjBcMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgEGMB0GA1UdDgQWBBReL0N9YCjLHhj1Tlbg+H6ZWDvDozAaBgNVHR4BAf8E +EDAOoQwwCocICgAAAP8AAAAwDQYJKoZIhvcNAQELBQADggEBAFOHbrR6Y0ZqUMwm +F3ZNihGq11KW5jAiWOktdADFCyo6XPty59Sj9YZlSEUs0H5Wi/RRhLBftxpDoPkg +oaHL0CzObTQcQ0GdOeB3VdaEwFcfwby7CWDbGbAoTovpT1kuorsBUzJiq0Xbs51V +Vs6mNieCtc02EQDvQNFKmkkDzFFA2xS9mKculkAqK0TMaw1X+56sIEG1IU6sGSeY +yJFgVoJwx7rb9iiwiz/3feutwa6ChVS4XhVGvvwwRpK2rJjMQuIn0qeKKJZC/84H +6wT+lgoMBmbkuICalxsK6OW39lWrzqFYw3fgcKiq524TxOmIMAL/CEGgKxjY6jW7 +hQhsE/k= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_permit_mapped/leaf_invalid.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_permit_mapped/leaf_invalid.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_permit_mapped/leaf_invalid.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_permit_mapped/leaf_invalid.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDQDCCAiigAwIBAgIBAzANBgkqhkiG9w0BAQsFADArMSkwJwYDVQQDDCBCb3Rh +biBUZXN0IFY0IFBlcm1pdCBNYXBwZWQgUm9vdDAeFw0yNjAxMDEwMDAwMDBaFw0z +NjAxMDEwMDAwMDBaMB0xGzAZBgNVBAMMEkJvdGFuIExlYWYgSW52YWxpZDCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKh1rMsJLtf8UxP7oatmbLEDHsKc ++Yg0O9EDietA7cVm9QAeg0BCTtOYnw+Z9Xq5txFBsjra5qkcJ0GmhOElWbHF7Jlr +tOza7Hbx1ZwURI+S9EJ7Lwg6gPXU6n3sKUFJ4Rczu1yuDBvnUii1v0Lm8wjgtL95 +iEg91exBt4MnTys4gcTjLJza6kVWoF8y1V59x90PKMz/bO8e4aT5Gp7pJtytHnTW +cok1jSs9w+7KK9uy/hzB0Q7ClHUrDwpmMEY0Sels0XgGdN73llMUEbIOvX7sVlKh +ME50I7QxXqveO/h20FKk+vgXNU+gdmW6P52i5hL3Xb3oCS7gquvxE1c6yr8CAwEA +AaN9MHswDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBaAwGwYDVR0RBBQwEocQ +AAAAAAAAAAAAAP//wKgAATAdBgNVHQ4EFgQUwEHyaZ2ocH0d+kWmLEk/lqxa0s8w +HwYDVR0jBBgwFoAUbYyfDaoykd8U4WhgHWV6tQ5i9QAwDQYJKoZIhvcNAQELBQAD +ggEBABAo5ccqs8JjOfoePcrZpuAOm0XhsEO8In/wnmXYrfsO2psIpRiSMrPAJWuU +GDOGawcKSJML2Vb6IQ6aY9A3qArEZ+qGcmHVrTbZgsiTExI08cCwj5B753ibrXKk +YeDTWk3cSwbMs+7tXBmg323LYaBBsXAhWQnZCLr9RcO8ievlpL9YnI3QNCDdaFQF +HwTmQtwi5kUI58r3/v31h9YdNly9EvNdgzRXVABMF/YtT/cUXAWIRr7AWDFC7Gqe +IT9SEmzprTgKLxBFFBGDHxwdx0NatWcOXpk8OXmp5kY65L3Ondyi2pmy3SWio3KU +Nar1Lpn24/sMTU2CEQwcG89kBkQ= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_permit_mapped/leaf_valid.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_permit_mapped/leaf_valid.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_permit_mapped/leaf_valid.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_permit_mapped/leaf_valid.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDPjCCAiagAwIBAgIBAjANBgkqhkiG9w0BAQsFADArMSkwJwYDVQQDDCBCb3Rh +biBUZXN0IFY0IFBlcm1pdCBNYXBwZWQgUm9vdDAeFw0yNjAxMDEwMDAwMDBaFw0z +NjAxMDEwMDAwMDBaMBsxGTAXBgNVBAMMEEJvdGFuIExlYWYgVmFsaWQwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDFn+KUzk8UlL/htlh+0miKxfq6IeVO +BuJFoQXAe9RM3ZZPSpZm2jjuZPBywypgV+a3PpIdnPqAQCOIeHgZkjHQtETKcsBP +z7tVg2lBTbPhXt7lF01BDbhoFoqCD4ocY5M0vzAxbufOq58Sjcuvd5gajgZ77ZcY +Jd+kaJzcRx9AA9+8cphj8NiVfq9JweuGWz1vJWIbRk5zXNM9PhOVr2PPyA0kP7lP +PROnzJDAZnafmNBHpXY5iGxaEHoIydKHIbzrcdB2M2AI8HrCWJfDcotEKnkZPHQU +n5EM2U+6HQvhplqWhWRHQHR7fpWGzwuBjzK+n0/YXlzUr6kpRxXZjJTrAgMBAAGj +fTB7MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgWgMBsGA1UdEQQUMBKHEAAA +AAAAAAAAAAD//woBAgMwHQYDVR0OBBYEFNRIR/mLpKlalncwHXu9LdZGRinqMB8G +A1UdIwQYMBaAFG2Mnw2qMpHfFOFoYB1lerUOYvUAMA0GCSqGSIb3DQEBCwUAA4IB +AQCFriTSDX8qDXJVkFNs/0hgB2ZJDyS8eJ8+f+E0S4ueFPGgMSsyLxNyI+bdjZK4 +rzaCCKk4d+gBaVZOqV8ICqWthA+QmJq1lreFwtd/SXkN++tYX05s2u3XReQZhfAB +MkEgN1czWwSsQ4J1girFvKlObWl0hpmfSQZClppYXzcpMxROQyC/O8w0f0OpoXBS +AyilTsp+S30MW0kYtW871EKK9gz2nU7xjDsXvOLtcaSvhHcYoqOU9jDc1iFJu4g8 +fGZJIwKBeE8aWx14wO1lDA5VqHcw3ntvba7At53L5BHJwS7tEnca7LoUz7WdGS4m +qNFsTImNhMYdEJc3OEnq6mL4 +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_permit_mapped/root.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_permit_mapped/root.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_permit_mapped/root.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_ipv6/v4_permit_mapped/root.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDLzCCAhegAwIBAgIBATANBgkqhkiG9w0BAQsFADArMSkwJwYDVQQDDCBCb3Rh +biBUZXN0IFY0IFBlcm1pdCBNYXBwZWQgUm9vdDAeFw0yNjAxMDEwMDAwMDBaFw0z +NjAxMDEwMDAwMDBaMCsxKTAnBgNVBAMMIEJvdGFuIFRlc3QgVjQgUGVybWl0IE1h +cHBlZCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx5matayN +6lidsxFzKMhOyVweQudmAq5axENaMFqUeGYeHjcYKYZZNr/qFRuSp+mCqfRXDSMg +Y82LLph5+g3YuQhcGuuMrRpN3ksY2Lcxif1M9oP/Sg57jwv4Wj2+ELr5Z9XHa5cu +nw2iic0lJ6fbr6YC7erisQr3A+CnUpjLezNcDoQLmF6jn9F2a5ZisqgRxPLTdvsA +iGVFuOMllJmFqMNnNo8UBqQiAgsZch3H5AQsvzN2BQsa3pO/kXWvg74wdctqmMha +WvnhCQsoc4b7wrq5gnoWkIAmfbfXy8XrOdYiHQnPzuTb5QI8q4DapnRONN9Pt6U0 +4L+44Cv4ghDmzQIDAQABo14wXDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIBBjAdBgNVHQ4EFgQUbYyfDaoykd8U4WhgHWV6tQ5i9QAwGgYDVR0eAQH/BBAw +DqAMMAqHCAoAAAD/AAAAMA0GCSqGSIb3DQEBCwUAA4IBAQC/+dxKAUTCajrrm2yY +zW/Y/oyCfOdTetaXN1Ttb2h2JFpzeS9e5yPjguWNUivvTsYZgJe7UV/Rh/WMx2nR +rcJVW+b6cS5KplSRJgfumlY6/A9aV8wYPmCS2OfkXJB+lV4p1Z1Vlu5rCnccTwtO +tWk0p0714CV/o4pMe04p4T14n11b4e+FakJtOA9TrQzpVarntrMlGRHIs+qIV5pJ +Nj4AxejNdTouYVIT0ACvg6JFcY+jsVODIjiC0uoXKfbXDSO+aBnY4r52HrZXqowL +n9dghkmzJu6TgtLInXzWbkC5FrLqn4byLxe21GMRlb8GwvrwTqdVqoIYunTSdmQY +exK6 +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_ca_accepted.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_ca_accepted.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_ca_accepted.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_ca_accepted.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICSDCCAbGgAwIBAgIBADANBgkqhkiG9w0BAQsFADAvMQswCQYDVQQGEwJDSDET +MBEGA1UEChMKc3Ryb25nU3dhbjELMAkGA1UEAxMCQ0EwHhcNMjUxMjI5MDg1NjA5 +WhcNMjYxMjI5MDg1NjA5WjAvMQswCQYDVQQGEwJDSDETMBEGA1UEChMKc3Ryb25n +U3dhbjELMAkGA1UEAxMCQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALGb +1FEk/FYdPfui6jcCcHKHhC87LW4i7z83BLJvt+fYWAXeNL+Z5kB6Vqdz9ZjLsDeQ +XtE/9HNQf1OO8QQltHciToqdJ49vr1m9sA/wqhGUZhYQWK13oaxYtNANvBHgwOkp +3EJjASNPKEFtNJ4MSshig7VxcQtRwEw31GgZUpqLAgMBAAGjdDByMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTibR7fg46iH8MA3URv +ik1wDALjHzAwBgNVHR4EKTAnoSUwI6QhMB8xCzAJBgNVBAYTAkNIMRAwDgYDVQQK +Ewdhbm90aGVyMA0GCSqGSIb3DQEBCwUAA4GBAFcZl34NFR2fiR+zCXYdTz0aovMC +XN/2jfsotj0J8hgytVrIanI5PiYZLJPUH6sbC+DYkuc8nfLR4V1AifAOMeOH/9Ho +96vG620tJx/2nmf2gd0MAFGj9j5TOk+hOb1KCuk4g26YLfQqVGTfI3oiquIWNv3N +i5oSoNUy9s8rN9oY +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_ca_not_accepted.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_ca_not_accepted.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_ca_not_accepted.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_ca_not_accepted.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICSDCCAbGgAwIBAgIBADANBgkqhkiG9w0BAQsFADAvMQswCQYDVQQGEwJDSDET +MBEGA1UEChMKc3Ryb25nU3dhbjELMAkGA1UEAxMCQ0EwHhcNMjUxMjI5MDg0MzM5 +WhcNMjYxMjI5MDg0MzM5WjAvMQswCQYDVQQGEwJDSDETMBEGA1UEChMKc3Ryb25n +U3dhbjELMAkGA1UEAxMCQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALGb +1FEk/FYdPfui6jcCcHKHhC87LW4i7z83BLJvt+fYWAXeNL+Z5kB6Vqdz9ZjLsDeQ +XtE/9HNQf1OO8QQltHciToqdJ49vr1m9sA/wqhGUZhYQWK13oaxYtNANvBHgwOkp +3EJjASNPKEFtNJ4MSshig7VxcQtRwEw31GgZUpqLAgMBAAGjdDByMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTibR7fg46iH8MA3URv +ik1wDALjHzAwBgNVHR4EKTAnoSUwI6QhMB8xCzAJBgNVBAYTAkNIMRAwDgYDVQQK +Ewdhbm90aGVyMA0GCSqGSIb3DQEBCwUAA4GBAGj+hTfKlRVaWPfGVcQKq+QWZvs0 +xZY2hP5lZxOwMl79jHd9Mwuieanwyvp/OKTkOZoq1gjlO2vld62UlF26mD1lQSP9 +czzVujoJsa8rjkRXMXNKaWwG5YtAjfA9uULjORa0CalpCQ2LAF5+2iUw8btEr1iM +GgS/nF5DMZHR9MP5 +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_im_accepted.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_im_accepted.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_im_accepted.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_im_accepted.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICazCCAdSgAwIBAgIBADANBgkqhkiG9w0BAQsFADAvMQswCQYDVQQGEwJDSDET +MBEGA1UEChMKc3Ryb25nU3dhbjELMAkGA1UEAxMCQ0EwHhcNMjUxMjI5MDg1NjA5 +WhcNMjYxMjI5MDg1NjA5WjAvMQswCQYDVQQGEwJDSDETMBEGA1UEChMKc3Ryb25n +U3dhbjELMAkGA1UEAxMCSU0wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALGb +1FEk/FYdPfui6jcCcHKHhC87LW4i7z83BLJvt+fYWAXeNL+Z5kB6Vqdz9ZjLsDeQ +XtE/9HNQf1OO8QQltHciToqdJ49vr1m9sA/wqhGUZhYQWK13oaxYtNANvBHgwOkp +3EJjASNPKEFtNJ4MSshig7VxcQtRwEw31GgZUpqLAgMBAAGjgZYwgZMwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFOJtHt+DjqIfwwDd +RG+KTXAMAuMfMB8GA1UdIwQYMBaAFOJtHt+DjqIfwwDdRG+KTXAMAuMfMDAGA1Ud +HgQpMCehJTAjpCEwHzELMAkGA1UEBhMCQ0gxEDAOBgNVBAoTB2Fub3RoZXIwDQYJ +KoZIhvcNAQELBQADgYEAI3KwAGbWYFfaSddrFIsaJw7pG1p8ZGwpLHqOLcOZ9DWh +TLUqg2BPxdso3+V9qCltyzvxc0Hl50nr0BD7GFnJH1odP1V+1A59mvOVFAroALLa +mLsks2v/sm+L3lshS2a26ddwsJv5O/cpu7x30OEh2Y+6y4Iy8N2MQisLec+WYeU= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_im_not_accepted.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_im_not_accepted.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_im_not_accepted.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_im_not_accepted.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICazCCAdSgAwIBAgIBADANBgkqhkiG9w0BAQsFADAvMQswCQYDVQQGEwJDSDET +MBEGA1UEChMKc3Ryb25nU3dhbjELMAkGA1UEAxMCQ0EwHhcNMjUxMjI5MDg0MzM5 +WhcNMjYxMjI5MDg0MzM5WjAvMQswCQYDVQQGEwJDSDETMBEGA1UEChMKc3Ryb25n +U3dhbjELMAkGA1UEAxMCSU0wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALGb +1FEk/FYdPfui6jcCcHKHhC87LW4i7z83BLJvt+fYWAXeNL+Z5kB6Vqdz9ZjLsDeQ +XtE/9HNQf1OO8QQltHciToqdJ49vr1m9sA/wqhGUZhYQWK13oaxYtNANvBHgwOkp +3EJjASNPKEFtNJ4MSshig7VxcQtRwEw31GgZUpqLAgMBAAGjgZYwgZMwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFOJtHt+DjqIfwwDd +RG+KTXAMAuMfMB8GA1UdIwQYMBaAFOJtHt+DjqIfwwDdRG+KTXAMAuMfMDAGA1Ud +HgQpMCehJTAjpCEwHzELMAkGA1UEBhMCQ0gxEDAOBgNVBAoTB2Fub3RoZXIwDQYJ +KoZIhvcNAQELBQADgYEAasfeoCZ9NtjpVYn00WbgNPnYMG5yWVqN4Flm6mmtdEE8 +NhUfv7o7d1rs4GBt3vvUlopCBfULRoFOY5YN2LZ8jnt/Gbyo2FCKzj/2lvFnOtWF +4QJY+IqIMBb2ogqdDoz+Lk59640MjA7mbVBTBlBG1MDQ1MF9CMqAA9+v5zYJkRs= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_subject_accepted.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_subject_accepted.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_subject_accepted.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_subject_accepted.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB+DCCAWGgAwIBAgIBADANBgkqhkiG9w0BAQsFADAvMQswCQYDVQQGEwJDSDET +MBEGA1UEChMKc3Ryb25nU3dhbjELMAkGA1UEAxMCSU0wHhcNMjUxMjI5MDg1NjA5 +WhcNMjYxMjI5MDg1NjA5WjAwMQswCQYDVQQGEwJDSDEPMA0GA1UEAxMGdGVzdGVy +MRAwDgYDVQQKEwdhbm90aGVyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCx +m9RRJPxWHT37ouo3AnByh4QvOy1uIu8/NwSyb7fn2FgF3jS/meZAelanc/WYy7A3 +kF7RP/RzUH9TjvEEJbR3Ik6KnSePb69ZvbAP8KoRlGYWEFitd6GsWLTQDbwR4MDp +KdxCYwEjTyhBbTSeDErIYoO1cXELUcBMN9RoGVKaiwIDAQABoyMwITAfBgNVHSME +GDAWgBTibR7fg46iH8MA3URvik1wDALjHzANBgkqhkiG9w0BAQsFAAOBgQBN3w8q +YnD/mB3TVUw4+j+5OYtwrkCMX4zbwF68wC5pRA0ws0xQnRDJ/b5kCLSbdgfZIP1N +MuwLhUIfBjqKs+h7mGXc/N9MTNuResaG6U8lv2I5pikOYb+CEtfucRAvXgyQglOe +lqSGJ+IEPAxwW+K4u+H+5bZlerOOfnYKTWZBHw== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_subject_not_accepted.pem botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_subject_not_accepted.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_subject_not_accepted.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/name_constraint_prefix/nc_prefix_strongswan_subject_not_accepted.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB+DCCAWGgAwIBAgIBADANBgkqhkiG9w0BAQsFADAvMQswCQYDVQQGEwJDSDET +MBEGA1UEChMKc3Ryb25nU3dhbjELMAkGA1UEAxMCSU0wHhcNMjUxMjI5MDg0MzM5 +WhcNMjYxMjI5MDg0MzM5WjAwMQswCQYDVQQGEwJDSDEQMA4GA1UEChMHYW5vdGhl +cjEPMA0GA1UEAxMGdGVzdGVyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCx +m9RRJPxWHT37ouo3AnByh4QvOy1uIu8/NwSyb7fn2FgF3jS/meZAelanc/WYy7A3 +kF7RP/RzUH9TjvEEJbR3Ik6KnSePb69ZvbAP8KoRlGYWEFitd6GsWLTQDbwR4MDp +KdxCYwEjTyhBbTSeDErIYoO1cXELUcBMN9RoGVKaiwIDAQABoyMwITAfBgNVHSME +GDAWgBTibR7fg46iH8MA3URvik1wDALjHzANBgkqhkiG9w0BAQsFAAOBgQCp47WL +ZBVfFgJnNNJIXaeTsGL4hqTTZYLQ3gy/nOz9edyHWY1kSWqUpfc0FUAioWiPf50g +HNzoMWY4ZzB/yQrO+HuazSvfuYPrDsfDXxz+e2GGz7okOCK4zuN0Xa7MVvXbA/5P +j7zVxjD6vKNZ2dyjIYipAwCQlyjfGHU9m+Axpw== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/nist/expected.txt botan3-3.12.0+dfsg/src/tests/data/x509/nist/expected.txt --- botan3-3.7.1+dfsg/src/tests/data/x509/nist/expected.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/nist/expected.txt 2026-05-07 01:38:28.000000000 +0000 @@ -80,5 +80,7 @@ # Right answer for wrong reason (delta CRL with no base CRL) test75:Certificate is revoked -# CRL contains IDP extension saying CRL is for CA certs only -test76:Verified +# CRL contains IDP extension saying CRL is for CA certs only. +# The IDP parser does not handle onlyContainsCACerts, so the critical +# extension is treated as unprocessable and certificates are assumed revoked. +test76:Certificate is revoked diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/ocsp/byKey_responder.pem botan3-3.12.0+dfsg/src/tests/data/x509/ocsp/byKey_responder.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/ocsp/byKey_responder.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/ocsp/byKey_responder.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBTCCAe2gAwIBAgIUR1HewbUb/oEsBgi45ukFDHQCnUswDQYJKoZIhvcNAQEL +BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAeFw0yNjA1MDUxNDA1NTdaFw0zNjA1MDIx +NDA1NTdaMBIxEDAOBgNVBAMMB1Rlc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQC7tse65ni8c3xHUMdaymlsSQl1rOZv9iofVKp0YlkhTgD4YnA6 +UhqYrn0U7u375tRh4udlsabwkbXRk2jL048utyim/tlCY2ZtkfY9av5ELEwD6uZp +3HgkW/7tv1Gpg/5lRD8rGyjehfayFOQZN4C0i3JJG5OtKFp8PToOP6x+6Vc1/+xP +gzY2cJdepn+IxyjEZDYg3UBAEcVd80YUfQgtwkHJ4MvyWAtzsUioSJ0/6kd+WS8s +Vl25q8uRnj/+bGXcZwwv2QhZGHVkkGlOHSojflu6+KqjfcHQG/YAgVhT2aIWt4of +Xmet+qBuxGE2UiHentNu4ywe/Id9Ji983vQZAgMBAAGjUzBRMB0GA1UdDgQWBBSF +ChJL1KahXKyHTSAphvLh0VYk0TAfBgNVHSMEGDAWgBSFChJL1KahXKyHTSAphvLh +0VYk0TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBJ+Yk5Y7Wh +N8vjHXhxj+1RMG4RoYQesWbH89hOkAycJSPtVxNJY8XJujl5TsWuRArWhDxHlHVC +o6w1mXQiE8zUUx5MfpowfNK+oPTs18K1kfuAGBBeS2dop79MPDEAMdrhElTuPSMo +A3g8nSHPvowH+NF26CYtKHPfIV2/POF0ttpB1N4JX2c4UW67av1Ic8NM6uJr6V4S +MiKqo0JZPFv3+bWemSr48HKmre9CCPXCUUgTgvhDY+d1/twUZIm6mDr9Sg/2JqvA +HPhREMwHdWHJy9dQkr0BTT7nGcDzHHwe2hMpJSQDREndPrEkA5Lf5LDjY3ouQ5VK +eJe4zVGAQVUK +-----END CERTIFICATE----- Binary files /srv/release.debian.org/tmp/eenrhLkv4k/botan3-3.7.1+dfsg/src/tests/data/x509/ocsp/byKey_responderID.der and /srv/release.debian.org/tmp/oKBJC3rrE3/botan3-3.12.0+dfsg/src/tests/data/x509/ocsp/byKey_responderID.der differ diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/end01.pem botan3-3.12.0+dfsg/src/tests/data/x509/path_building/end01.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/end01.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/end01.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBuTCCAV+gAwIBAgIUHxEBkPDWmzoN0kCaWd2da4oVbTYwCgYIKoZIzj0EAwIw +QDEfMB0GA1UECgwWUGF0aCBCdWlsZGluZyBERlMgVGVzdDEdMBsGA1UEAwwUSW50 +ZXJtZWRpYXRlIExldmVsIDEwHhcNMjYwMTAxMDAwMDAwWhcNMzUxMjMwMDAwMDAw +WjA4MR8wHQYDVQQKDBZQYXRoIEJ1aWxkaW5nIERGUyBUZXN0MRUwEwYDVQQDDAxF +bmQgRW50aXR5IDAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATRD37aSH8OjTAp +6BQQBozXD3/IEn38tpcM7zEAHIRUUJdWi3OmHNONAPN+jqC0hWraA1Y4VmlpBsVz +/YTS403uoz8wPTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBR1aN54oQTHWr6igcWN +wr4MzdWtLTAOBgNVHQ8BAf8EBAMCB4AwCgYIKoZIzj0EAwIDSAAwRQIgBVbeHMNu +bHEcJjID1/mRu1mQeKJr4s+D8JWWtWNWTUkCIQDbE/+a4msKZaBQX9XCo2oACLxP +IPdt0UJKSWZVB5D2Sw== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/end02.pem botan3-3.12.0+dfsg/src/tests/data/x509/path_building/end02.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/end02.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/end02.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBujCCAV+gAwIBAgIUIO7luuHduSgt/KlORojaVgzSbVwwCgYIKoZIzj0EAwIw +QDEfMB0GA1UECgwWUGF0aCBCdWlsZGluZyBERlMgVGVzdDEdMBsGA1UEAwwUSW50 +ZXJtZWRpYXRlIExldmVsIDEwHhcNMjYwMTAxMDAwMDAwWhcNMzUxMjMwMDAwMDAw +WjA4MR8wHQYDVQQKDBZQYXRoIEJ1aWxkaW5nIERGUyBUZXN0MRUwEwYDVQQDDAxF +bmQgRW50aXR5IDEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS+p+d0evbuzn7W +qWrQqbgmeC5TwsSJCeN3sHZNxW2igTn0REvfVb9eEVoFg+lZZOe+Pv8AfxFKLGM+ +FBZI4B92oz8wPTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSU4bXG01EY9UocjZaf +XqQlJ4oz2jAOBgNVHQ8BAf8EBAMCB4AwCgYIKoZIzj0EAwIDSQAwRgIhAKuYNQ2L +4WTITq178HrXqbLPhkUJ9eL2lBHYPPcQbsmiAiEA9W/kophycoaexvCcYklchD4k +MmZ7dv08J29sr423RZw= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/end03.pem botan3-3.12.0+dfsg/src/tests/data/x509/path_building/end03.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/end03.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/end03.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBuTCCAV+gAwIBAgIUGKQlr3BXjwNHrvBXR2brKhCgcIQwCgYIKoZIzj0EAwIw +QDEfMB0GA1UECgwWUGF0aCBCdWlsZGluZyBERlMgVGVzdDEdMBsGA1UEAwwUSW50 +ZXJtZWRpYXRlIExldmVsIDEwHhcNMjYwMTAxMDAwMDAwWhcNMzUxMjMwMDAwMDAw +WjA4MR8wHQYDVQQKDBZQYXRoIEJ1aWxkaW5nIERGUyBUZXN0MRUwEwYDVQQDDAxF +bmQgRW50aXR5IDIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAStJ4m2szpsI4NV +UM14WCvTPlpQGJS+x2HRKsLjN77kLTY2fcmou/bLw6ZqEF6nwpzF7Dzsr50DFBun +cuQh+BgZoz8wPTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQTsO41CsbqumG8jqgB +6N2JinI/vDAOBgNVHQ8BAf8EBAMCB4AwCgYIKoZIzj0EAwIDSAAwRQIgQjGCx/iC +qAta7DqkibeHtoPaYfSQA6ps9p7ehqgDKl4CIQDTFwY/9mqPF9qFEacF8fPL2W0D +X2KkmCItSApk4bsgLQ== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/end04.pem botan3-3.12.0+dfsg/src/tests/data/x509/path_building/end04.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/end04.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/end04.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBuTCCAV+gAwIBAgIUFkiikr/uQzf0R9P6+Di42cudeWQwCgYIKoZIzj0EAwIw +QDEfMB0GA1UECgwWUGF0aCBCdWlsZGluZyBERlMgVGVzdDEdMBsGA1UEAwwUSW50 +ZXJtZWRpYXRlIExldmVsIDEwHhcNMjYwMTAxMDAwMDAwWhcNMzUxMjMwMDAwMDAw +WjA4MR8wHQYDVQQKDBZQYXRoIEJ1aWxkaW5nIERGUyBUZXN0MRUwEwYDVQQDDAxF +bmQgRW50aXR5IDMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARf2aoYXCTODPal +pFZRn9qGk9sA6+UqjuZBd6H3RqG1sjl3C/wrg4wDkyDqh6KCLOnT+PbkBPjFbMhg +LvuRLOvooz8wPTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQsdpZQQyXptUCUfvqq +ji4fP74taTAOBgNVHQ8BAf8EBAMCB4AwCgYIKoZIzj0EAwIDSAAwRQIhANSteVVf +eCD9cB5ooMUP9x2Bfx3VcLpUtEJn+K2ZgzG5AiAcIHMM6GOQ+pdNN2qSCYsUhq2U +FWHPw+bGVx2p7J5ITQ== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/end05.pem botan3-3.12.0+dfsg/src/tests/data/x509/path_building/end05.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/end05.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/end05.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBuDCCAV+gAwIBAgIUV6AdcCz7/K/w8kI838qJ6VbpUfcwCgYIKoZIzj0EAwIw +QDEfMB0GA1UECgwWUGF0aCBCdWlsZGluZyBERlMgVGVzdDEdMBsGA1UEAwwUSW50 +ZXJtZWRpYXRlIExldmVsIDEwHhcNMjYwMTAxMDAwMDAwWhcNMzUxMjMwMDAwMDAw +WjA4MR8wHQYDVQQKDBZQYXRoIEJ1aWxkaW5nIERGUyBUZXN0MRUwEwYDVQQDDAxF +bmQgRW50aXR5IDQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASEvT1fCHmVzv+s +pafL1bUScvkkWf8VyE7njigoTuCsuUVLZSxb1+yj06ZDOvwxWGKpx804/0DLTKOs +WV+4hutGoz8wPTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSBKVEXltMRcnKNSz46 +SMqB0NmenzAOBgNVHQ8BAf8EBAMCB4AwCgYIKoZIzj0EAwIDRwAwRAIgdT4GhRGC +uoJ8M5mZ4DEubh64c79D+MXm6Tq/1BTjzzQCIGKkbbNosA5AE1nbpBmqF1FmgFY/ +BRTKwItWBOHnFU1g +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/end06.pem botan3-3.12.0+dfsg/src/tests/data/x509/path_building/end06.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/end06.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/end06.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBuDCCAV+gAwIBAgIUQS6epXN54ZI+aJaObok7V9p8/0IwCgYIKoZIzj0EAwIw +QDEfMB0GA1UECgwWUGF0aCBCdWlsZGluZyBERlMgVGVzdDEdMBsGA1UEAwwUSW50 +ZXJtZWRpYXRlIExldmVsIDEwHhcNMjYwMTAxMDAwMDAwWhcNMzUxMjMwMDAwMDAw +WjA4MR8wHQYDVQQKDBZQYXRoIEJ1aWxkaW5nIERGUyBUZXN0MRUwEwYDVQQDDAxF +bmQgRW50aXR5IDUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS1r+Dh9Hex68io +7itC7JF9F+LR+NOmStgQ9HXmNuRcoQLHJXwAbel3Ef6jQR9Oiv03x7qjHydHdhsP +2DivU9mxoz8wPTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRFMUWBzu9GKUBtyhnr +8WjWFTIUXTAOBgNVHQ8BAf8EBAMCB4AwCgYIKoZIzj0EAwIDRwAwRAIgCEV1Ox1B +3c9WiC2lgrJ6DRavLMdHjLIZr2/GGilsoEYCIBOaPQrGVZxSd2lAT+rDSMj3Kmq8 +sOeRULlgKkmb8VVl +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/end07.pem botan3-3.12.0+dfsg/src/tests/data/x509/path_building/end07.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/end07.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/end07.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBujCCAV+gAwIBAgIUKT+4rIiFpbumTl2GvVBg3/4WydEwCgYIKoZIzj0EAwIw +QDEfMB0GA1UECgwWUGF0aCBCdWlsZGluZyBERlMgVGVzdDEdMBsGA1UEAwwUSW50 +ZXJtZWRpYXRlIExldmVsIDEwHhcNMjYwMTAxMDAwMDAwWhcNMzUxMjMwMDAwMDAw +WjA4MR8wHQYDVQQKDBZQYXRoIEJ1aWxkaW5nIERGUyBUZXN0MRUwEwYDVQQDDAxF +bmQgRW50aXR5IDYwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARm9vI4z0bUc6GD +tjfU+B7YUm2h9drPI3nk07q5l+fi7rLx+udewaXvEv9dHNSZwESGhMhRpjx1wY5u +EE4op4v+oz8wPTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQ2rqlAq/53USzoUHXb +8Phkd2/ETjAOBgNVHQ8BAf8EBAMCB4AwCgYIKoZIzj0EAwIDSQAwRgIhALZ2n0hk +xieZO+V4dvqVR5SFWBFUD2/qmSJdCAymg/qsAiEA6W/nJL3Qfje5qzOsLbS19q66 +g+nqZG76Jfcb8npJ+SE= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/expected.txt botan3-3.12.0+dfsg/src/tests/data/x509/path_building/expected.txt --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/expected.txt 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/expected.txt 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,7 @@ +end01.pem:Verified +end02.pem:Verified +end03.pem:Verified +end04.pem:Verified +end05.pem:Verified +end06.pem:Verified +end07.pem:Verified diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level1_0.pem botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level1_0.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level1_0.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level1_0.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBwzCCAWqgAwIBAgIULPY1bQ9+kRlVWHT2CsoNq9yw0H0wCgYIKoZIzj0EAwIw +QDEfMB0GA1UECgwWUGF0aCBCdWlsZGluZyBERlMgVGVzdDEdMBsGA1UEAwwUSW50 +ZXJtZWRpYXRlIExldmVsIDIwHhcNMjYwMTAxMDAwMDAwWhcNMzUxMjMwMDAwMDAw +WjBAMR8wHQYDVQQKDBZQYXRoIEJ1aWxkaW5nIERGUyBUZXN0MR0wGwYDVQQDDBRJ +bnRlcm1lZGlhdGUgTGV2ZWwgMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHgi +KPpSl1VypZ3FycPbiKVbXPV1v7asfsOwBwoOTzGI6KR1f8YC0RQjJQ2hkDsN+2/H +jWRPuCZQx9har+LpLU+jQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFCvU +SkPvWYYs/a6vBA3DldWAt1SBMA4GA1UdDwEB/wQEAwIBhjAKBggqhkjOPQQDAgNH +ADBEAiASruedDTuEPbt4rwcEcfirBuCFPGlNhsYxkpSvlA1WGQIgT8g2043xME5g +bzjHLaOgR7gs4wFphlcbTqu3VMSsjgw= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level1_1.pem botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level1_1.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level1_1.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level1_1.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBxDCCAWqgAwIBAgIUM3GjC0wK0pdT3mQRKMd+IHIUy9QwCgYIKoZIzj0EAwIw +QDEfMB0GA1UECgwWUGF0aCBCdWlsZGluZyBERlMgVGVzdDEdMBsGA1UEAwwUSW50 +ZXJtZWRpYXRlIExldmVsIDIwHhcNMjYwMTAxMDAwMDAwWhcNMzUxMjMwMDAwMDAw +WjBAMR8wHQYDVQQKDBZQYXRoIEJ1aWxkaW5nIERGUyBUZXN0MR0wGwYDVQQDDBRJ +bnRlcm1lZGlhdGUgTGV2ZWwgMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABO8d +On6fT52TpczY84uWOXnRqSowkeMsCsS6GjozRJEg3QDk5LZP7UyZZ4TQatvB6Qwi +AapZpMLmJTWorpatABijQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFPzw +T7I+YElGPYT24KU4Z8YOYQzHMA4GA1UdDwEB/wQEAwIBhjAKBggqhkjOPQQDAgNI +ADBFAiEA/EuWKvczT9zNdyGmJLTgrQtEOvi0hS2/RS6I64229UMCIGAZJ/WO6gPN +vnf6dEaUIcHRMrnknDjxPsl5osuSv0+s +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level1_2.pem botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level1_2.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level1_2.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level1_2.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBwzCCAWqgAwIBAgIUE7MPqpKyda+pJ5gMMG4i7xARD2owCgYIKoZIzj0EAwIw +QDEfMB0GA1UECgwWUGF0aCBCdWlsZGluZyBERlMgVGVzdDEdMBsGA1UEAwwUSW50 +ZXJtZWRpYXRlIExldmVsIDIwHhcNMjYwMTAxMDAwMDAwWhcNMzUxMjMwMDAwMDAw +WjBAMR8wHQYDVQQKDBZQYXRoIEJ1aWxkaW5nIERGUyBUZXN0MR0wGwYDVQQDDBRJ +bnRlcm1lZGlhdGUgTGV2ZWwgMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABB8/ +uFS40bnO5+tEtnamWiDamaV5sBTqjq7stKRZ+CQ/rweOoINkZFReg/vgorIylyY8 +6/EDFPy7H/T8nFq07N+jQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNvv +NeOpzxq0IFS2BfCQla4JDwT3MA4GA1UdDwEB/wQEAwIBhjAKBggqhkjOPQQDAgNH +ADBEAiAz/ztNfvUA8pqA/seKA9N01zv7VDldAGcOy/GZBxOnHgIgSK4BrS15VKkM +blc5W+96VptBLd5rKO3XHpi4iYBJq20= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level1_3.pem botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level1_3.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level1_3.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level1_3.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBxDCCAWqgAwIBAgIUWYUUIXbB/T2ezKt/+RO0EGtm9AswCgYIKoZIzj0EAwIw +QDEfMB0GA1UECgwWUGF0aCBCdWlsZGluZyBERlMgVGVzdDEdMBsGA1UEAwwUSW50 +ZXJtZWRpYXRlIExldmVsIDIwHhcNMjYwMTAxMDAwMDAwWhcNMzUxMjMwMDAwMDAw +WjBAMR8wHQYDVQQKDBZQYXRoIEJ1aWxkaW5nIERGUyBUZXN0MR0wGwYDVQQDDBRJ +bnRlcm1lZGlhdGUgTGV2ZWwgMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABEoH +mqRq3bOemM2u7i4aB1urBvUNM+fzAM1B+TnV2ztd8c9fbCPmR0d6YrXkqjj7DsGh +CrOZq+9HHZCtwxjbYOGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFuh +9C6ydM4F/CyPAdPKvyY6A299MA4GA1UdDwEB/wQEAwIBhjAKBggqhkjOPQQDAgNI +ADBFAiEA3ohH545vXw35sweQ3kzA3K1WI4cBMu2rjQR5tmg8rawCIAe+1xOtj5EY +XCx6FmECZQ12+Tdbojor2KHAVNCeW2pI +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level1_4.pem botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level1_4.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level1_4.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level1_4.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBxDCCAWqgAwIBAgIUU2cTcCE9lPTZ5A9dchzau3xYKi4wCgYIKoZIzj0EAwIw +QDEfMB0GA1UECgwWUGF0aCBCdWlsZGluZyBERlMgVGVzdDEdMBsGA1UEAwwUSW50 +ZXJtZWRpYXRlIExldmVsIDIwHhcNMjYwMTAxMDAwMDAwWhcNMzUxMjMwMDAwMDAw +WjBAMR8wHQYDVQQKDBZQYXRoIEJ1aWxkaW5nIERGUyBUZXN0MR0wGwYDVQQDDBRJ +bnRlcm1lZGlhdGUgTGV2ZWwgMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD8r +1+0dVZ7csfMU+zqSUFVieNvjZUynZUxtYAKS0KO5L+UxtqRpzpqtoUHAfVxIDYBN +F7TFdpsC21sYT5bJTPajQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOJ5 +Xq33nS+Wu6I1HWG9FEtBTv/CMA4GA1UdDwEB/wQEAwIBhjAKBggqhkjOPQQDAgNI +ADBFAiBM10agOgH7kVBryb4wgJnlw7cBLFBtU5wiZFKx/PvejgIhAModer/1LJde +Izbzak6ES4gJ4kHDVxutTG3OyDUqrPF9 +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level1_5.pem botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level1_5.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level1_5.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level1_5.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBxTCCAWqgAwIBAgIUHdu8aJ2lIvmqG6GHJz+S7durnYAwCgYIKoZIzj0EAwIw +QDEfMB0GA1UECgwWUGF0aCBCdWlsZGluZyBERlMgVGVzdDEdMBsGA1UEAwwUSW50 +ZXJtZWRpYXRlIExldmVsIDIwHhcNMjYwMTAxMDAwMDAwWhcNMzUxMjMwMDAwMDAw +WjBAMR8wHQYDVQQKDBZQYXRoIEJ1aWxkaW5nIERGUyBUZXN0MR0wGwYDVQQDDBRJ +bnRlcm1lZGlhdGUgTGV2ZWwgMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNEb +aprx23CpN0cNTLhNfD2tETpL1r4TAOcYpW1z/oRSCX87xBvw+XEGXkR1XGrANsi2 +i30h0Jd15joPrVwOXqejQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNhr +29Kg2ZRQ3BzluiEJ82FW2yVFMA4GA1UdDwEB/wQEAwIBhjAKBggqhkjOPQQDAgNJ +ADBGAiEAudbrY8W5wYs9SPtPAv4snoNI2JFqGRd4okGKv3p3zOcCIQC/tamVMo/0 +wr3OTg6DIGsmaTbtPNF4C7/NnwzSjucrzg== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level1_6.pem botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level1_6.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level1_6.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level1_6.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBxDCCAWqgAwIBAgIUSE9nKMYYSYhgHUXWt9uIHZb8g5gwCgYIKoZIzj0EAwIw +QDEfMB0GA1UECgwWUGF0aCBCdWlsZGluZyBERlMgVGVzdDEdMBsGA1UEAwwUSW50 +ZXJtZWRpYXRlIExldmVsIDIwHhcNMjYwMTAxMDAwMDAwWhcNMzUxMjMwMDAwMDAw +WjBAMR8wHQYDVQQKDBZQYXRoIEJ1aWxkaW5nIERGUyBUZXN0MR0wGwYDVQQDDBRJ +bnRlcm1lZGlhdGUgTGV2ZWwgMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABN+5 +HG5/LSJ1DjTlS9Aq31+dfcIJaA90IaLBbKqTAwg/rtPKNR0Kd+56bKG7AmTGF2wg +oP46qSEIEOU5c5Z7dXKjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIAO +hBM1nS9paYv7MV0SrasbEl+XMA4GA1UdDwEB/wQEAwIBhjAKBggqhkjOPQQDAgNI +ADBFAiBOPQ5Izh5nrRp4JVk4skPFD946WxaNN86dmRTioCkQ+wIhAKPICl6tGN44 +zlbriGs20NnTvlyrsawqS+25DhZDsXRY +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level2_0.pem botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level2_0.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level2_0.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level2_0.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB2DCCAX6gAwIBAgIURsjrCbkE5tP0YsLQWhL18X5m3mYwCgYIKoZIzj0EAwIw +MzEfMB0GA1UECgwWUGF0aCBCdWlsZGluZyBERlMgVGVzdDEQMA4GA1UEAwwHUm9v +dCBDQTAeFw0yNjAxMDEwMDAwMDBaFw0zNTEyMzAwMDAwMDBaMEAxHzAdBgNVBAoM +FlBhdGggQnVpbGRpbmcgREZTIFRlc3QxHTAbBgNVBAMMFEludGVybWVkaWF0ZSBM +ZXZlbCAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwDpf0z7P4hYxX/FxtSEO +ikDbAy3B4jFoMsNUufqHfEwqSMfJxjxBcCaB0lvS5dIXqNs26RsyAf3RmEVJG7hN +F6NjMGEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA2MrVnPHhSrxQpPaFl/g +PbxCfgQwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFBTiTHCkz1XhA5wgZqHF +jn17KF3nMAoGCCqGSM49BAMCA0gAMEUCIQC0ZzfGFmUepN5Cj0Aa9Lr7PLzqOLXR +r9Ny7rQa/WXoUgIgIF/ckDTjO31uWcpzFk9swzI9Nf1zQn6hxIKAXpvnlVU= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level2_1.pem botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level2_1.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level2_1.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level2_1.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB1zCCAX6gAwIBAgIUWiyAg7TsygYhU0cUPux6Vd6MvpkwCgYIKoZIzj0EAwIw +MzEfMB0GA1UECgwWUGF0aCBCdWlsZGluZyBERlMgVGVzdDEQMA4GA1UEAwwHUm9v +dCBDQTAeFw0yNjAxMDEwMDAwMDBaFw0zNTEyMzAwMDAwMDBaMEAxHzAdBgNVBAoM +FlBhdGggQnVpbGRpbmcgREZTIFRlc3QxHTAbBgNVBAMMFEludGVybWVkaWF0ZSBM +ZXZlbCAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9myiZAjfquozYdFqy48V +K8rD7WnqNECoRnsyrJUq7ZccCPHr0Yh5av2vOvrKJIK2LYeO9H8ZudHYjdjuUyyb +oKNjMGEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUnsCzONCikayyr6lTWYVH +Z7sKTJ4wDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFBTiTHCkz1XhA5wgZqHF +jn17KF3nMAoGCCqGSM49BAMCA0cAMEQCIFkB2+0yiVYVfNWH47uQf9+ysX1/2nAD +Oc6wi5W0dTB3AiAk98z5Cl0gD1Ay5L8DUfm9pjj/x6L0UXNc7zX5gxsf3Q== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level2_2.pem botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level2_2.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level2_2.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level2_2.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB2DCCAX6gAwIBAgIUF4WnEFMAvcOlZfO3wgj4ObqcrZgwCgYIKoZIzj0EAwIw +MzEfMB0GA1UECgwWUGF0aCBCdWlsZGluZyBERlMgVGVzdDEQMA4GA1UEAwwHUm9v +dCBDQTAeFw0yNjAxMDEwMDAwMDBaFw0zNTEyMzAwMDAwMDBaMEAxHzAdBgNVBAoM +FlBhdGggQnVpbGRpbmcgREZTIFRlc3QxHTAbBgNVBAMMFEludGVybWVkaWF0ZSBM +ZXZlbCAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEVkmRqTLeehcUfG+FijGJ +sihuq02jui6hzoHqaZMmozqEE2ZzOIEFKfxMLSivtTB/yTAksnHcYqS1wL1HXwbs +yqNjMGEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUzPo/G3JqMSrixt5FElRI +13FaBRAwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFBTiTHCkz1XhA5wgZqHF +jn17KF3nMAoGCCqGSM49BAMCA0gAMEUCIQClXZh3GkkGizkMx0vARiu1lfk8S6w2 +IoXlUioBu9vj4gIgTS2lP5GlmOaTAX8HzMlugin3kbjejTen9GJ3SRJAIB4= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level2_3.pem botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level2_3.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level2_3.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level2_3.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB2DCCAX6gAwIBAgIUdeOC4yEQXXAWj9pQYwcZB2c4qLYwCgYIKoZIzj0EAwIw +MzEfMB0GA1UECgwWUGF0aCBCdWlsZGluZyBERlMgVGVzdDEQMA4GA1UEAwwHUm9v +dCBDQTAeFw0yNjAxMDEwMDAwMDBaFw0zNTEyMzAwMDAwMDBaMEAxHzAdBgNVBAoM +FlBhdGggQnVpbGRpbmcgREZTIFRlc3QxHTAbBgNVBAMMFEludGVybWVkaWF0ZSBM +ZXZlbCAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/7gbCjNwZH4AI/m27DkR ++IQuSg7y4rSxN49DMAkjVFHFJ+LCbuvkO+z6FFcg/WFgQWpxGlFE6EvnsiSQkr+J ++6NjMGEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUnCSCobAw+1LwPDVqSbW6 +nVeatwYwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFBTiTHCkz1XhA5wgZqHF +jn17KF3nMAoGCCqGSM49BAMCA0gAMEUCIQClHgNvmyZUm5wdoxD43Dgg2FnLzDu2 +qp+m8wb2vzYV3AIgScO62812I/XObBRzn7RRhtZfI3V/fi43VUYLFecWamw= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level2_4.pem botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level2_4.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level2_4.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level2_4.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB2TCCAX6gAwIBAgIUamh7NsIidHb5QwVVCqM3Tl8dhhcwCgYIKoZIzj0EAwIw +MzEfMB0GA1UECgwWUGF0aCBCdWlsZGluZyBERlMgVGVzdDEQMA4GA1UEAwwHUm9v +dCBDQTAeFw0yNjAxMDEwMDAwMDBaFw0zNTEyMzAwMDAwMDBaMEAxHzAdBgNVBAoM +FlBhdGggQnVpbGRpbmcgREZTIFRlc3QxHTAbBgNVBAMMFEludGVybWVkaWF0ZSBM +ZXZlbCAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZvgPSiB5+o6h5/X/YYzz +aWO05TtW6QDJMTmbIZfqMddLeBfwv1glitQKb+NRaazE+VXQfkzUCwqGnGqBu9l7 +1KNjMGEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUykujINCeRdXHLK6yiwbX +CSuGa44wDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFBTiTHCkz1XhA5wgZqHF +jn17KF3nMAoGCCqGSM49BAMCA0kAMEYCIQC/4rErDW2RbCW+WhsXdA1CnvrIAfJ/ +CHaCXdFWuOmwewIhAKcMW5psNFRvYs/iozQ3e0RCqCkv2PEJhaWJoYcAG0xM +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level2_5.pem botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level2_5.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level2_5.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level2_5.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB2TCCAX6gAwIBAgIULShyMrEEwLx1FFStFMb5Xv98JgkwCgYIKoZIzj0EAwIw +MzEfMB0GA1UECgwWUGF0aCBCdWlsZGluZyBERlMgVGVzdDEQMA4GA1UEAwwHUm9v +dCBDQTAeFw0yNjAxMDEwMDAwMDBaFw0zNTEyMzAwMDAwMDBaMEAxHzAdBgNVBAoM +FlBhdGggQnVpbGRpbmcgREZTIFRlc3QxHTAbBgNVBAMMFEludGVybWVkaWF0ZSBM +ZXZlbCAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEebgiP98ejXbZkeEX08hg +kvvdNprJAmij9pQ5khDGkOFDaoc7s0inYa4q1IBZ/CZy0ZzWP6PXqbIy0AWsEbab +DKNjMGEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUC96DWvZ/23AtJMNg/3Ih +Tk3RDdkwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFBTiTHCkz1XhA5wgZqHF +jn17KF3nMAoGCCqGSM49BAMCA0kAMEYCIQClO/wYOsexbxkT3riR7WcbD0QgS1g0 +X7sK8WUeU9mzIAIhAKpZNYhVV5POKMHZ5XQraS4WsAF7dKR51dWDLRcJL3SU +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level2_6.pem botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level2_6.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/level2_6.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/level2_6.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB2DCCAX6gAwIBAgIUGr1TaDUGKzQgGkf4y7+K/yXXGiswCgYIKoZIzj0EAwIw +MzEfMB0GA1UECgwWUGF0aCBCdWlsZGluZyBERlMgVGVzdDEQMA4GA1UEAwwHUm9v +dCBDQTAeFw0yNjAxMDEwMDAwMDBaFw0zNTEyMzAwMDAwMDBaMEAxHzAdBgNVBAoM +FlBhdGggQnVpbGRpbmcgREZTIFRlc3QxHTAbBgNVBAMMFEludGVybWVkaWF0ZSBM +ZXZlbCAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEebDhXDN0mFNYd568lV2s +HF6WxyMvyTElRdWEHWgzqxrprbVm0Z3ALKdSCe0eA2w1fiUBhGt9jcne8yCjsjaf ++KNjMGEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU2NzZTUwlHGrEHgYDs0YP +BCgJQkgwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFBTiTHCkz1XhA5wgZqHF +jn17KF3nMAoGCCqGSM49BAMCA0gAMEUCIDMWwiTeE3BBGRdRH2IklDv1Iu4zJNn1 +viiRHEdof1B/AiEArMdKww2wegR38SvJ/Pr2XON6jU6tbJjlzgQ3OAIpa4k= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/path_building/root.pem botan3-3.12.0+dfsg/src/tests/data/x509/path_building/root.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/path_building/root.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/path_building/root.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBqzCCAVCgAwIBAgIUVKcI18YLYGWXzaX9kw3k4vFfmPQwCgYIKoZIzj0EAwIw +MzEfMB0GA1UECgwWUGF0aCBCdWlsZGluZyBERlMgVGVzdDEQMA4GA1UEAwwHUm9v +dCBDQTAeFw0yNjAxMDEwMDAwMDBaFw0zNTEyMzAwMDAwMDBaMDMxHzAdBgNVBAoM +FlBhdGggQnVpbGRpbmcgREZTIFRlc3QxEDAOBgNVBAMMB1Jvb3QgQ0EwWTATBgcq +hkjOPQIBBggqhkjOPQMBBwNCAATwk+usmdWxXyllDQgKVU3h2csKF8i/xHTl0qkV +Y+FSK+swYBIsBnhLJlmmHFHuCC9q8t+6P2lTN7ddCbBvoV9zo0IwQDAPBgNVHRMB +Af8EBTADAQH/MB0GA1UdDgQWBBQU4kxwpM9V4QOcIGahxY59eyhd5zAOBgNVHQ8B +Af8EBAMCAYYwCgYIKoZIzj0EAwIDSQAwRgIhAKCabckbAhiB0QTqiTDWPekkQciM +NG9QsItJA2JiF3hhAiEAu7N7FXgwuS1DcLEI551//9d/QcJ0DPBEB+Inc2SfaCU= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/pss_certs/expected.txt botan3-3.12.0+dfsg/src/tests/data/x509/pss_certs/expected.txt --- botan3-3.7.1+dfsg/src/tests/data/x509/pss_certs/expected.txt 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/pss_certs/expected.txt 2026-05-07 01:38:28.000000000 +0000 @@ -79,21 +79,21 @@ 79:Verified 80:Verified 81:Verified -82:1 -83:1 -84:1 -85:1 -86:1 -87:1 -88:1 -89:1 -90:1 -91:1 -92:1 -93:1 -94:1 -95:1 -96:1 +82:Verified +83:Verified +84:Verified +85:Verified +86:Verified +87:Verified +88:Verified +89:Verified +90:Verified +91:Verified +92:Verified +93:Verified +94:Verified +95:Verified +96:Verified 97:Certificate signature has invalid parameters 98:Signature error 99:Signature error @@ -104,15 +104,15 @@ 104:Verified 105:Verified 106:Verified -107:1 +107:Verified 108:Verified 109:CRL bad signature 110:Verified 111:Verified 112:Verified 113:Verified -114:1 -115:1 -116:1 -117:1 -118:1 +114:Verified +115:Verified +116:Verified +117:Verified +118:Verified diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/x509test/ASNumberCert.pem botan3-3.12.0+dfsg/src/tests/data/x509/x509test/ASNumberCert.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/x509test/ASNumberCert.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/x509test/ASNumberCert.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBqDCCAU6gAwIBAgIRALPImt78hhfZi1Y9pXp9uPkwCgYIKoZIzj0EAwIwADAe +Fw0yNDEwMjExNDQ1MTBaFw0yNTEwMjExNDQ1MTBaMAAwWTATBgcqhkjOPQIBBggq +hkjOPQMBBwNCAAQYLa6kWGm3hE1ug3BVUaui+Ui013pu/ZTeCKYU++tQEjGydJyO +UCzFDjuMZgu76+iaGWfa0PlN2pPFoIQoJduAo4GoMIGlME0GCCsGAQUFBwEIAQH/ +BD4wPKAbMBkwBwIBAAICA+cCAhOyMAoCAQACBQD/////oR0wGzAIAgIE0gICFi4C +AwCAADAKAgEAAgUA/////zAhBgNVHQ4EGgQYGz6Wu2X5h8+j64aiGIj9ts4i+J6R +lBMHMAwGA1UdEwEB/wQCMAAwIwYDVR0jBBwwGoAYGz6Wu2X5h8+j64aiGIj9ts4i ++J6RlBMHMAoGCCqGSM49BAMCA0gAMEUCIG+x6GaNAKDT2Gs9Jh7rTtAd8KAP/MCC +orUYhAug4kzQAiEApwyX0MvUoZV9fUg0AyN79OCbt0XPneyjdwYPSk3nmyI= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/x509test/ASNumberInherit.pem botan3-3.12.0+dfsg/src/tests/data/x509/x509test/ASNumberInherit.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/x509test/ASNumberInherit.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/x509test/ASNumberInherit.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBiTCCATCgAwIBAgIRAIxkvUFe24qH+RH0D814mEswCgYIKoZIzj0EAwIwADAe +Fw0yNDEwMjIxMDQwMTNaFw0yNTEwMjIxMDQwMTNaMAAwWTATBgcqhkjOPQIBBggq +hkjOPQMBBwNCAAS8OgRLt85kZt8M5MGKcwXyOkUXoylpsp3gKVnQukeEVUPzhYUT +t/nAC9s6tlqQx06aLo4NMpC/ZiLjfqRoh7/Co4GKMIGHMC8GCCsGAQUFBwEIAQH/ +BCAwHqACBQChGDAWMAgCAgTSAgIWLjAKAgEAAgUA/////zAhBgNVHQ4EGgQYEekx +OowtPJb0QL2dSh4YuqEhfAEZh6g6MAwGA1UdEwEB/wQCMAAwIwYDVR0jBBwwGoAY +EekxOowtPJb0QL2dSh4YuqEhfAEZh6g6MAoGCCqGSM49BAMCA0cAMEQCIG5s6rM9 +fpV76Ydij83G5dfNw8xq/PKohCQAsRc5BFP1AiANm2/BiqB6yzNO3t+1PFdjgpFu +8zYpwnxA4Q4yEvKDxg== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/x509test/ASNumberOnly.pem botan3-3.12.0+dfsg/src/tests/data/x509/x509test/ASNumberOnly.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/x509test/ASNumberOnly.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/x509test/ASNumberOnly.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBhjCCASugAwIBAgIRAPeuZ8n5f7uJoOnwr0vpOsMwCgYIKoZIzj0EAwIwADAe +Fw0yNDEwMjIxMDM4MDFaFw0yNTEwMjIxMDM4MDFaMAAwWTATBgcqhkjOPQIBBggq +hkjOPQMBBwNCAAQs/GF1Owcxm3dS3BJtIrAPZkI2WSXet77azlDkRx9LTqjmDi53 +xRbBGTIeOQ3wOtXWH2QLFUWnBvLcGrb12OFCo4GFMIGCMCoGCCsGAQUFBwEIAQH/ +BBswGaAXMBUwBwIBAAICA+cwCgIBAAIFAP////8wIQYDVR0OBBoEGPCRR4/g4jN6 +euAgVpPhG1+vr+YBL+OmwjAMBgNVHRMBAf8EAjAAMCMGA1UdIwQcMBqAGPCRR4/g +4jN6euAgVpPhG1+vr+YBL+OmwjAKBggqhkjOPQQDAgNJADBGAiEAsucYpXQTeSoC ++DGPyrVFRS9XaimDHcDIG+VDKbRhmp8CIQDcoCqItPWoGZjF/PzW6L8CdhBzNaAP +s424AISGuExA7A== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/x509test/ASRdiOnly.pem botan3-3.12.0+dfsg/src/tests/data/x509/x509test/ASRdiOnly.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/x509test/ASRdiOnly.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/x509test/ASRdiOnly.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBhjCCASygAwIBAgIRANz2VZ/ccV2i/GgObA+fDU4wCgYIKoZIzj0EAwIwADAe +Fw0yNDEwMjIxMDM5MjZaFw0yNTEwMjIxMDM5MjZaMAAwWTATBgcqhkjOPQIBBggq +hkjOPQMBBwNCAATpsmi/80tOyt9nDOqJzNTVox3wOGZSEwGXeMNTR0cbytK9h+t8 +Ea3+dl6LeXvo423FZd0TNPxRrjaLYFpFjX4Ko4GGMIGDMCsGCCsGAQUFBwEIAQH/ +BBwwGqEYMBYwCAICBNICAhYuMAoCAQACBQD/////MCEGA1UdDgQaBBhMamGZIJk1 +k//8v1K14lioRkSxGj2ryhIwDAYDVR0TAQH/BAIwADAjBgNVHSMEHDAagBhMamGZ +IJk1k//8v1K14lioRkSxGj2ryhIwCgYIKoZIzj0EAwIDSAAwRQIhAMHro3vcKus9 +Id+1hnMeffZL/CWFOSTgtKjX7OMbQOK8AiBijOvIranrc1X0OwtvM2A4bJi035G9 +e8BeRHCpaJGitg== +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/x509test/IPAddrBlocksAll.pem botan3-3.12.0+dfsg/src/tests/data/x509/x509test/IPAddrBlocksAll.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/x509test/IPAddrBlocksAll.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/x509test/IPAddrBlocksAll.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID2TCCAsGgAwIBAgIIDCV8W/5Tqq8wDQYJKoZIhvcNAQELBQAwYTENMAsGA1UE +AxMEWk9SQjELMAkGA1UEBhMCREUxDTALBgNVBAgTBFRodXIxEzARBgNVBAoTClRV +IElsbWVuYXUxHzAdBgNVBAsTFlRlbGVtYXRpay9SZWNobmVybmV0emUwHhcNMjQx +MDAyMTIxMDM4WhcNMjcxMDAyMTIxMDM4WjBhMQ0wCwYDVQQDEwRaT1JCMQswCQYD +VQQGEwJERTENMAsGA1UECBMEVGh1cjETMBEGA1UEChMKVFUgSWxtZW5hdTEfMB0G +A1UECxMWVGVsZW1hdGlrL1JlY2huZXJuZXR6ZTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAKS8eqobCYN9/Gj41lEVvYxkBBj0tWTVKCavNRPtAPpATsbO +hGEDO0Cvt2WZZMBTjXdiCjJkdy8aHfsg4SDOK9GUlxCAR7jL1XfFeHE2Q2CWBM8J +NDk+Kx7Nxj1TBY//rTf2gPiu/CDMQPpihTH0kGUw+dR2zybjj0d3h1nQAiVWaauf +A+QZ1qcgpXpSp9r0Jds+GzCW9119oglPVMgbQGR8ExO9/gU3VS15MowZ+lonGCa7 +KSd8rO+rUbDvdZ3Gu3C00yR8Dsft4/1YqSYtdeKD87AdGu3wkx62Ia4lwarjWYwE +mmbOzEXddy/rM7eFEgCIPecxk/8eMb3MxB1Y2tsCAwEAAaOBlDCBkTCBjgYIKwYB +BQUHAQcEgYEwfzAmBAIAATAgAwQHwKgAAwMBwagwDAMDA8KoAwUAw68BAgMFAMSo +AAEwVQQCAAIwTwMKB/qAAAAAAAAAAAMGA/4gAAAAAxEAIAMAAGgpNDUEIBDFAAAA +xDAmAxEAqwEAAAAAAAAAAAAAAAAAAQMRAM0CAAAAAAAAAAAAAAAAAAIwDQYJKoZI +hvcNAQELBQADggEBAA1vysHUycl6/ij2b6pXlvei4Qni1laHGJT/8b2YW2Q3U0uc +V4WMy+nKR9/IDpFc03kZW9ihe7zbbJcoINaKq3UTfEeMcLbzDSzFFaKUANv/C2vx +sUihUo1ojle4EmmVYyYXeiZiu+46aUzuUJuWddTs4kJdNUxFkTKMmhdiGSosGKvz +wRqXj5pG1iEQZmZYDWrricVFkGuwcbAbWTtQqh+cSTt+1sKi4FwL6kkCH9kG5D+1 +/zugVwhXRgguSmqMixpNowMmiDJggzIruGGeJc3ubKuvAnRJmW4VZCXXbVDNAPXi +HDOjomC4OO17uOrWnLht/oiJ+VUhjkFtorO2RLY= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/x509test/IPAddrBlocksUnsorted.pem botan3-3.12.0+dfsg/src/tests/data/x509/x509test/IPAddrBlocksUnsorted.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/x509test/IPAddrBlocksUnsorted.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/x509test/IPAddrBlocksUnsorted.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICqzCCAmKgAwIBAgIRANPort9DlhqMt2QI6bFLA+IwCgYIKoZIzj0EAwIwSTEQ +MA4GA1UEAxMHVGVzdCBDQTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUJvdGFuIFBy +b2plY3QxEDAOBgNVBAsTB1Rlc3RpbmcwHhcNMjUwNjE0MTkxNjEzWhcNMjYwNjE0 +MTkxNjEzWjBJMRAwDgYDVQQDEwdUZXN0IENBMQswCQYDVQQGEwJVUzEWMBQGA1UE +ChMNQm90YW4gUHJvamVjdDEQMA4GA1UECxMHVGVzdGluZzBJMBMGByqGSM49AgEG +CCqGSM49AwEBAzIABN0stcHCSpEww/+tZrO2Uv36ZJmjLel058Rdr5tdShPCNEmy +MeXB+cGQ1kWVMh+sp6OCATkwggE1MHMGCCsGAQUFBwEHBGcwZTAHBAMAAgEFADAZ +BAIAAjATAxEA/////////////////////zAHBAMAAQIFADAVBAMAAQEwDjAMAwMD +wKgDBQHAqAIAMBcEAwABATAQMA4DBQHAqAICAwUAyAAAADAGBAIAAQUAMCEGA1Ud +DgQaBBgub8YveBEYQ3Q3XbeiHtrh38tnkuzOOtQwDgYDVR0PAQH/BAQDAgGGMFIG +A1UdEQRLMEmBFXRlc3RpbmdAcmFuZG9tYml0Lm5ldIITYm90YW4ucmFuZG9tYml0 +Lm5ldIYbaHR0cHM6Ly9ib3Rhbi5yYW5kb21iaXQubmV0MBIGA1UdEwEB/wQIMAYB +Af8CAQEwIwYDVR0jBBwwGoAYLm/GL3gRGEN0N123oh7a4d/LZ5LszjrUMAoGCCqG +SM49BAMCAzcAMDQCGF6Idq8d0ibVHxOTBA7xzFrquTz7crUfBAIYMNxljBJPw+CX +VaIdhfLji2fOE9P8vx9O +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509/x509test/InvalidIPAddrBlocks.pem botan3-3.12.0+dfsg/src/tests/data/x509/x509test/InvalidIPAddrBlocks.pem --- botan3-3.7.1+dfsg/src/tests/data/x509/x509test/InvalidIPAddrBlocks.pem 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509/x509test/InvalidIPAddrBlocks.pem 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICUzCCAgmgAwIBAgIRAPphGYQXHPWC9Y9mBr3lVkQwCgYIKoZIzj0EAwIwSTEQ +MA4GA1UEAxMHVGVzdCBDQTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUJvdGFuIFBy +b2plY3QxEDAOBgNVBAsTB1Rlc3RpbmcwHhcNMjUwNjE0MTkyMTIwWhcNMjYwNjE0 +MTkyMTIwWjBJMRAwDgYDVQQDEwdUZXN0IENBMQswCQYDVQQGEwJVUzEWMBQGA1UE +ChMNQm90YW4gUHJvamVjdDEQMA4GA1UECxMHVGVzdGluZzBJMBMGByqGSM49AgEG +CCqGSM49AwEBAzIABLT1a9v+orDaWRmzckbwZge9S94Sc4rUhTTD/neIxwtoRYIh +cY49G1rv+KnGRXoeOKOB4TCB3jAcBggrBgEFBQcBBwQQMA4wDAQCAAEwBgMECQoA +IDAhBgNVHQ4EGgQYGgRoConyOCjqg7Gs2mK1rDUrR4Sjr8v0MA4GA1UdDwEB/wQE +AwIBhjBSBgNVHREESzBJgRV0ZXN0aW5nQHJhbmRvbWJpdC5uZXSCE2JvdGFuLnJh +bmRvbWJpdC5uZXSGG2h0dHBzOi8vYm90YW4ucmFuZG9tYml0Lm5ldDASBgNVHRMB +Af8ECDAGAQH/AgEBMCMGA1UdIwQcMBqAGBoEaAqJ8jgo6oOxrNpitaw1K0eEo6/L +9DAKBggqhkjOPQQDAgM4ADA1AhkAhMM9sBVJYDp1/6eZw+WX0tHH/OuPJeGxAhgM +w4b5TgHFvyYquWJ4pZq1SwmZnDotYXU= +-----END CERTIFICATE----- diff -Nru botan3-3.7.1+dfsg/src/tests/data/x509_dn.vec botan3-3.12.0+dfsg/src/tests/data/x509_dn.vec --- botan3-3.7.1+dfsg/src/tests/data/x509_dn.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/x509_dn.vec 2026-05-07 01:38:28.000000000 +0000 @@ -16,17 +16,17 @@ DN1 = 301C310B3009060355040613025654310D300B0603550403130454455354 DN2 = 301C310D300B0603550403130474457354310B3009060355040613027674 -# Empty, different encodings +# Empty, different encodings (indefinite length with EOC) DN1 = 3000 -DN2 = 3080 +DN2 = 30800000 # Empty, one using EOC encoding DN1 = 0000308100 DN2 = 3000 [Unequal] -DN1 = 301C310B3009060355040613025654310D300B0603550403130454450054 -DN2 = 301C310B3009060355040613025600310D300B0603550403130454455354 +DN1 = 301C310B3009060355040613025654310D300B0603550403130454455354 +DN2 = 301C310B3009060355040613025655310D300B0603550403130454455354 DN1 = 3019311730150603550403140E4141200141414141414141414141 DN2 = 3019311730150603550403130E4141202020202020202020202020 diff -Nru botan3-3.7.1+dfsg/src/tests/data/xof/ascon_xof128.vec botan3-3.12.0+dfsg/src/tests/data/xof/ascon_xof128.vec --- botan3-3.7.1+dfsg/src/tests/data/xof/ascon_xof128.vec 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/xof/ascon_xof128.vec 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,326 @@ +[Ascon-XOF128] + +# From NIST's ACVP: https://github.com/usnistgov/ACVP-Server/tree/master/gen-val/json-files/Ascon-XOF128-SP800-232 +# Only test vectors that have a byte-aligned input length and output lengths +# were truncated significantly. + +In = +Out = 473D5E6164F58B39 + +In = DD +Out = 21F0EA9D11B85DB0350DB0B4 + +In = 565D +Out = 156FC33F4DC39E1AE67C2F4321E8DA82 + +In = 23CBD8D5 +Out = FC + +In = C5E69408B6390150 +Out = F28B43 + +In = B97478CE249B899F010195E709636901 +Out = C6CD1BF440B71F124DA6DAE310E15E2EAD208798604A6371DFDA5C4A34548C64 + +# From the Ascon reference implementation +# Only the first 100 test vectors + +In = +Out = 473D5E6164F58B39DFD84AACDB8AE42EC2D91FED33388EE0D960D9B3993295C6AD77855A5D3B13FE6AD9E6098988373AF7D0956D05A8F1665D2C67D1A3AD10FF + +In = 00 +Out = 51430E0438ECDF642B393630D977625F5F337656BA58AB1E960784AC32A16E0D446405551F5469384F8EA283CF12E64FA72C426BFEBAEA3AA1529E2C4AB23A2F + +In = 0001 +Out = A05383077AF971D3830BD37E7B981497A773D441DB077C6494CC73125953846EB6427FBA4CD308FF90A11385D51101341BF5379249217BFDACE9CCA1148CC966 + +In = 000102 +Out = 9C96F31C3E7BDFDC5EF6BA836F760A0D6548D94DD0A512033022C9242E8BA916C30C3961D37D7DD7282E2191494D60DC5058588B276C60C90BE2AAA7E7013D96 + +In = 00010203 +Out = 21F7FD74588E244AF45F9016B8DB19B857EC5E6208978CFC1B4611ED91FB38F87E8F82A6409FB2B77ACFBBA8862AA22A7B0C98C1C01D5A4FDF64827B450FA1EB + +In = 0001020304 +Out = D647CC91AAFFF06A486F00A33FDFE9222F08B94DA3B17804DA9AAAE167B4285DD6395E2A61FDED3CF73C99774AFF7066F74F7698F4824BA538602087D7C267FA + +In = 000102030405 +Out = 4793FBE6AA7688E52CD3A97A2685C68B218E0CA8754307956509974AB107D8BA19070424D5DFD336C3FC1250A273B9146F9F26D7658B9E213C37AEBBE74ABC6E + +In = 00010203040506 +Out = 7AE562DB37212A9ACD2673ECFD5B4F1C5CB2E6F64EBF00AA7F6EF8DC82C448D5FE11CD91F4368C37690D79E5DE0CA8AD419E1918CE8DAB2D42363E9476638A7B + +In = 0001020304050607 +Out = 8D1886F5D3EC4AF8D15B44BC62B74DA6EA91BC28FB82F9C34079B5ED6E38B6C951803D7DFB3C5E512A0EF5E4060062A6FD067F9C73EF9BEE527411BDA67FC896 + +In = 000102030405060708 +Out = DB3013BFBBD132DC1D3152FD955ED48F7CBB675E9AD2A2FECF92B74C957592E0C89959E81C16FD07EAD9EEB8E40359C497AA20258B43D87EC69AD0BB0993FD38 + +In = 00010203040506070809 +Out = 816FA0F1ECF91988BAD311B02A6B009A44DBC9A70430093D7C3FC47D9C72879BAF008653E573C52A280C8FFCC84B995CBD3BB81BBB902A8DCCA7E7F6804B3499 + +In = 000102030405060708090A +Out = E8B79D96B0025ACA303233C6EF693A204E58E0418686293D0E25EC4E5BC44BCA92C9A3A95E95E0A8D768E0DE962A401AF5E3610A819EBC5EA79DBF674B3D288A + +In = 000102030405060708090A0B +Out = 9865B2D1D980FEDAE9EECBE58A4F88E4F59F3BA0428CCA6CA78D76EFF1B8B8949517432F2A4D61CA1B2071A4C654ED0C0EBCEDB1DB6F4CC5A07D07A58165E9E7 + +In = 000102030405060708090A0B0C +Out = 008D52F47112BC66D8701237DE11898C481ACCC77E7C9B54C1B5540D6F82B34C5DFFE7311F44AC776713DDDC67628FCB42AB27E6AFC90FE14C6A04D48E47FDFE + +In = 000102030405060708090A0B0C0D +Out = 2EEDA00422F003F00B82F0E26E106F0B53C5BFFAF67435804B6280E126DB81E4611729F987D499E81069A9EF04525B7CA8F5644020664F2A3F915C4279F952BC + +In = 000102030405060708090A0B0C0D0E +Out = 7517D9B0383DC7742E9E1335D97D3F1C5A971416CA4E72BF504E962F80286862733AD8F5E60ADCC1C5B21E8BE99D32BC80D70277B81E709DC56579C37BEBC080 + +In = 000102030405060708090A0B0C0D0E0F +Out = 10BFEDC5F6442D3E1D8C324878CE1DDF73B01CAFC365589283AC4CBB98E48DE3CEDA8A41BB0983D539E4D90F6458C5C781724FAD641ED3CDB4779931097440B3 + +In = 000102030405060708090A0B0C0D0E0F10 +Out = 233AF64F97CA9BD97BAE06270571E57215C5CB5BA4038536C5C128DA1D3A379AE13DA3E54546A1499014CA03F2EEE10B7AA930FAA58A3994FD4BCC71F6CB1927 + +In = 000102030405060708090A0B0C0D0E0F1011 +Out = 864197F3ABCEBB2272195C53E08D51C1625312883FBF17237531FD7FED40E303681BBC049FEA71850E0F628FF5EEC9A5B591652EC770BAE4A4E683BEEE517F16 + +In = 000102030405060708090A0B0C0D0E0F101112 +Out = F4B87B886CE28D50BDA038F31593BC6408F177CD10897AE6401A091782D806FE0711A38663E20D14483E41ED0B29E689F77161A5ECEB88B472B3C68209A59F43 + +In = 000102030405060708090A0B0C0D0E0F10111213 +Out = 38A193882A795D3F55F3DCB08746BA7B2852FCFFFE7C6606159784ACB311C7FC4FF5547F7498DA80E0F2F236986D0DAD407D18A51B6672D79FC5C6F8CEECE490 + +In = 000102030405060708090A0B0C0D0E0F1011121314 +Out = 1F1653E06ECC97DC8F32484A5A46151AFD303C2483A61F0CEAB5D8499B46DF9B28953499D28AB2F0CC3AE35D6495CA845E46A3AC393629133FD2F25C903124D8 + +In = 000102030405060708090A0B0C0D0E0F101112131415 +Out = D3626497E9D42FD612A720B6683B5FA74E25054D320018860F9DAA3ECEEB216B457A45F2C0F643E860698B99820A608277DAE3BBE3B5AD1977A3D521F28778E7 + +In = 000102030405060708090A0B0C0D0E0F10111213141516 +Out = 5AE21E68EF4FDC6FEFBF604B0BD8672406F6F23F0BDF2F28E5460B081D9068B8ECA5F2F654A29BE0EA1669E8FA8A6D03CD404C5FFB1DF8D6E13D3E0F3DB9FE95 + +In = 000102030405060708090A0B0C0D0E0F1011121314151617 +Out = 25AD36B2F1712F42A285E13F2FC2CE5A7938E399F02B8B4468106854E6FEB94CFB465DA80BF523C18126D337021473D0C7E553282346F323657DB53EEBAB7BEA + +In = 000102030405060708090A0B0C0D0E0F101112131415161718 +Out = 01AF3FCB017C949B5E2ABADA540A901862C6DED81B0756C8FEE0FC5E6BEE01484A9652A994D9FE629076DA09ED0D458EA38820AF8F9803914849F07A37A94154 + +In = 000102030405060708090A0B0C0D0E0F10111213141516171819 +Out = 299968405045CA071735639E61E2F50E641F0C14AA753E94F167216664CD6174D7F0E8C0686AC076F738AF190717A5D61D627EEB5B44972E52FFB7A79DEF5201 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A +Out = 0F8316BBAEB9D75A684A1E1795BBA210F7452DA4C210FCF24CA9226F838436648D41E140D31382756F7B90C8373FCDEA69069E27110390EDE469E1474B38D71C + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B +Out = 0DEBFD2058BA5B8DBF4D44D9A36A81B2311C2385DF9913F61A471897F4464535660CA8C7422912C996E72991753D8CEF89168F1BEF285DA8151BAC3B9491AC78 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C +Out = 259D670887F177CE377D40FDE81304BEA72B3246CC38DB7464BC20408B450CFB8F500987DAB1A08B6B5D2713D789F83803CEDF421E7F7D4584FA35CDCCC6E335 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D +Out = 90C9BCA9AC226137A2CBFB092352E7F67206FF90D0561C1D90127A4D51FD3EEAE3FE5A469A6285DCF0FE0C0AF761D3CA2C98038F6AF65C632D2B5DA370FE2248 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E +Out = 0517BA0498A2BCB8198492CD6022B91283DBDB4464EE3B2859AAC793C948BED6B7C9FA1D1B55D1D6DCB4AE9511F4171F12F6B2DF734FA5BC95A7D94B086D0AAF + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F +Out = 2E5F3403F4171471CC7934B51982CECE8D6628435DB70E89880F3BE4E0B7B05232DFE63C44A836D771337C9C5A2688D1B71ECABE0D5C2006FEF36EF3186138AD + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20 +Out = FEF74B7EBD183BA1D87BF414000B29258D6A2233A2A03ED519C646B351BC008464CB725C2922E77A5E2B71F2D48E8D1AB34B45C3DA91F5D46C9C3D9AE9057E02 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021 +Out = 9FBBF9D0F796379DAF504C2679F3C1B58EFE25D3731E481FD513F89BCA30822EEF3F039680FEFC7A5E8A62351E882B598DC27C64F8E671D9C68A83AEA403BB38 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122 +Out = 6125B03C2C691C04744CBF6ADD658B4E59176EA5101504E561B324C55032C372510F4D4E7874441FC2D1E197B7EC034E61EF2BE66651B60597A1D913E638202C + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223 +Out = 436B080C43C5C8B9891638857AD36594631442984E377C38B25448A0E35FFF01976DEACF1DC5BF75F36A2461D97ED7469D8553976B30D821581F26FE18AF93E9 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324 +Out = A1348CF2680EC27D92FFA9C6D9B7359A89A4BC340415A609D28A226BCAFED459B0D3BFF80F01B817A266524F0467BC5D1E045F95CB3D1524A40297A516F2C816 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425 +Out = 4622E8A9AE4344D1EEE665B6A69D9206C638BC317A7539D896C09B35BC82C72BD599561414B8ACCE9EA6FAC5C545C6761EB71C3CF2016202097B3CCB23AB53F6 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223242526 +Out = 7BBC623737BC74DEB2FF3FBD878C49FE056B7C4B1C763E05D53BE641401855FA870B909E6E1BE957971DC2FC98E0CF2B4739B6BEC4F21367170EB9E733E4ED55 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627 +Out = A632894EAB39F9D48FA4C2E39C248996118FA8DE6F217E6D94543950D797C1096E19E045E40014538F346DA4EEBD4AF6B11A42679823267E8250BB520AA47CDF + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728 +Out = D17E46FB431F781710ECEB1E2044EEDA1F6FE16584BD77C621985B275184A4A15A78A072538E41491E8EC898901D518952F12BBA38A089C140116FAC2995FE0F + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223242526272829 +Out = 71FFE1554117AECF8E93F1523FE4548D2FC0A44B96215689778468BD417C87CBBD414D6C4D924FE781D65C0029C424F3B3BBD35F1BFB3F5A891F5BE1215321AE + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A +Out = E5BC28DB31C27397E81BFE5F49A64A768B2838E4C88FFF8C36FAC897BE22C29579C8A1AB81873C97954CEECC3F9A72F4BD7CCD1F22C99E474C67E1B7AC6289B4 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B +Out = 960390CAA9ACDCE1F6D3EF8BEDB8A690C5827B8D10EC2D80DEEB678187B8ECE3FDAA4077E48212000D4736F9E3FA3434779D480DC331920114232C167D00D3FB + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C +Out = 8DB9A811FCACB5271804BAAD1ED057E1D945781256AF9AA31C9C8427155E43B1F994F724275FC08E86B7A9F9C2CDA0E2BACA9C6B787B2298D9F993A069D1E012 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D +Out = D0A18B15F2163206AC40E61EF35B8DDC06D865904A8D02439BEEEC68F93BA4760B97296D4B5A7AE293E7B6A75E46E1AD9CE5E2124250620A467BDF4034F3AA79 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E +Out = 1A093454920E32367821ECD29E8C2D97A8C1381AE0C66EDE9FF675D5764807FB154BD3EB5E293576CAA324889DCFCF829A2EDB7AEF2ED168F7EA018ECAE928FD + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F +Out = 695355FD17E4E96EFA7A8E08B2D4D436091C3BC4021B2C301C9BD97394A0B61FE4FEB624742E08DFD9DEEB2735204FBFD728D0C874AAA91E7DC4BE4E69469FC9 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30 +Out = F9BCB37BBF5F9CDE2F24C9D653984E06EE8970E84C18281B811C29D2405F5FF040C4929B699850248A48C3FA5260CDD6929A05B9BCD88E5913D7FD557A9CB541 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F3031 +Out = 215A32769AB15AFA75FBD7B30DE55DDF77D1E78BE66442D4FA184DF860DD498533D03F4C3FBF507B77D2F800A0E02831F38D805C75CB11965E76FECA223CDA62 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132 +Out = B9EA57CE0A96FA91E97E046BFD1140BD81913A14741E1497890A0A7AA48BD1CDB00951A43A721A0F75DBFE5044954A48F32E019333AC3E88CC325D88B8D5356D + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233 +Out = C87D6FA9293B918E44C2538DE53C4053D76C9BA7885ED538FE6326A6214D98E52863EC86254E0275586FBC797D9A02BEA3B0545B28F1EE560B2ADFEAF82651FA + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F3031323334 +Out = 7F169B56A19D644FC488CFF44CBC7287EA0C68E6DADCFC85A4F88889DB783EB183DD3B55999E6A0EFE4AB1E06AD49E12A28EBD89DA15C06C70932B2C622619FA + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435 +Out = E5B26E04FDB98EF1E82E222A81015C87016FEBADD8885481C00B2A409F88C5860C5A4B521DB06F2B284A7F282C194FCF7DE9872904F8F256AFA8831872E389E8 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233343536 +Out = D48476B7C37710A79882006A7574E6278A8F5C2C75DC80A70C809404FED7B657AF1E1BA04721305BA0AF0517D5B26451F58D402E9032F39F28351735F22A2247 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F3031323334353637 +Out = 9615C31A9D014A6DC4980C8C07AB0158E4DEDB98F949B672F2B7773DCFA326CC29D0AA7F364D29ED0B9B26ADA2E71B98126549716BCA0CDA687B058408D77031 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738 +Out = 69FEB7881600AC726C89DA1E85EEE68FB5D92387E3BAA4D4221B11B892E53FED474DB000BD42F77E13C18C882610B142639C51EF1C902FE2A3ECAE8DF085201E + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233343536373839 +Out = 4A214029ACE757B01A5DA43142AC3FE3D630AF76470BDF90CC4F553EB50C7F063985574F84C8F212806D5F074020101E678319D2F97157A717B55396BCC63D96 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A +Out = C8394D0297D5CD35807B22F68096391C58DF1AD624110E10B8C21D42B4B5DEE21FF30E1BAF751008D3F5FA23743A57F5CFF3077D9ED93C831DC9B953BCAF2BAB + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B +Out = 5369F945A2B7505480E4A69FE7A3C3F65FA580F6A142ADE523953ACF0A9A70C3E23431722261BBD22261EADD3DBBE224F55840B7E9310E8EC7DAEB6D4FE5BE54 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C +Out = E4BC8640DABE20B716403633417513E6EE94622E3D2E47B0CDA43852B70A2B47477C5172013137D43FE0821CE0B08EF38BB1EEC30E010E8ABDFFAFA267621F87 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D +Out = EFD4D1B05E4D04780F1A42FA566CD32F60A51031CB6C3403EDFEDF7AB55D9276EB38464B4016433A4CF92DED1F928409E9963F83F9FA059BE48005D3A40800AD + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E +Out = 2A4849B4D960678780A24F58D51D3C8155E5DC006021024BA3AC463F242499DEE2476355B24DD8324407D8615CFC34DE8F770C2CFDFA8E725BA0049A0C45CEAA + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F +Out = 0865C2FA92C71058E79E5C4214F3A1505540411586920536CCEE85FBF2940B9F0131385FFE92F15F35BD35373F14D8BF11F078D9850096016F857D27575DA423 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40 +Out = 439F3BCEB9C9313CF5584DFE9ABC803BC5D54DF8FA95DEF24041E8A666A0EACE60448F5D4AEB0295546363F7F2C319B9E64795D80E0C4C72780F6E2B1C6EF60A + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F4041 +Out = 53D62F34D43D2F00A35BAEF651D1D1B45E28DE8767908C28AB7C206D8CA9AD3B6525C572777A04CD6DA98581DCA01AC5F4A45E7007FB6153824271C6E83CFD8E + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142 +Out = 4EA59632185CEB6B614A7928F561916A07305D43CEB7606B9A8684B8B3060718625CEC296BF5D3CE033894B8204AC48A6438207740229588F114ABF33D0914A4 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40414243 +Out = 8A1FF815FC8BA0E8FDE6783FD5A4B796222441517F3EF79EFD5A275DEEEED1830A9D919E4BF100882A8B0FFC61A4EB6A496E5305E08BB280ED51D3E7883FE0D2 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F4041424344 +Out = B0D67746977BC569E99CD06391A69DCA5ED3AB42F6830025AE999FC0734B51C73D8733EC99697913912EAA903E8A7C7E204DA3BEFBF5AD3A714FCE9382E50D87 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445 +Out = 2438373E66536FD750039AA7AAA680C0BF637D20C84BB6E29AECDBD0D99CF37FF4778D7C3CC5E879EDB2D60C7FC38084B54C4D3D85993705F32EBC2AABF094AA + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40414243444546 +Out = AA85944A2429C008AFA4CF15D63D8FE73C4797C888FFC85320E564A0720331929C4276FBBFDAFE27A5B8E62293AF409EBF17A0936EB6B15D74A5EFA125A83A1E + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F4041424344454647 +Out = 4A311A1277B7ADB08E6FBBCF43524DB789090BD36EAF537FC5E3537B4002D6A059A51AEB90A7DA4F9B4D2BA226C107273DB1A80CFE2819794DCC63E7E21EB345 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748 +Out = 4A64651DA9B14DC6116E9910BB497F1DE9CD0D33A4F1D622BC4A3C4FE1F0833315683B94659CEDF4BC0C70526D7D65ADA8C304BAF70525D64402FAA857311232 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40414243444546474849 +Out = 00E3F23EAD1D0C56B91CE4194D28A067D58EF0F634C6CECE4622F5A5F469D78D0B2BEC8E1996DE66A36C9DCFA86EA126242E1A859F69B9D94C6C1D66AC6ECB43 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A +Out = A60FA23406B351CA9B99B56C028A6C09E6C6A4B4271050E980ABCA2E216A6EF1990AD195341599B114D747004AE630400CCC8239D74D34EEB790C7A00C6009B6 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B +Out = 98A88559A1701D6E885165EA0BD8A903E6DB1BE4D155AEBD159EC8B1FA6037E49AD3539A86ED8E2EBC1BCBF24BBBF0127F1CDA653570D158B03A4B65DAAD28AE + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C +Out = 6A1BBD9075C149615BD7CEED593A08B6ECDC498A51360694698CEE2C145549F0F1F0F883178BAA75468353C7C9FED31BFFCAE54E50A5FD345E178836F5818060 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D +Out = 74CC52A19FD313A5A28744CAD791A81DAD51776F5A35EAA902DD5C9F3EE245F1EACBFDBA59EEF394910C80FB2E551CF41C0895FDC8AACCD7EA5EF6568B90E5EA + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E +Out = C31C3A4D755ED295071F6CBECAADFF4D2F6A24448CB106B09FBC9408334B9D28197BAD4F6261CD723482E8D4020183C953D2364A2C223F4B29F358770BB0F18D + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F +Out = F67FB0946B279080F79B7E47659993A2B42E618CE5D6D6C437B874BC82BA32854F5BE66F102D4B1E342D2A036DB10A9E55F2274274102827F9F736666548B77F + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50 +Out = C393648AF48664960227B8DE53EB535712FF689291283B5D01AB8231E5724C86577659A4FA05CFFBB8C5E60391E11E43BDCE50DE720A5B14EF3D5649595A2BD6 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F5051 +Out = A7280F2E3E1E7CD85A0856DBB5FC324C43BD01DAC212D9C31E087573519230A152A7C5847C622748BBFD10D58B377F13A7E85EEDAD739E13277641486AB34711 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152 +Out = 767608FB988172D1F01A4C4FA42AAA238E1ED4D0F38F562ADD0C49A3216AFFEC6E7F57D47FEBC8D7CBF8BF40D936A470ABD17069915B32A64C78430AF4754810 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50515253 +Out = 08FDC8395A5E61E7D25696C25082F41925406D3006F78D05DF74B587770C8314C1AD80D966E9E244A263FD5300D39BDEFBF0BA6C3F55974AEEF84F4B84982DEB + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F5051525354 +Out = 45D647E3AB1B2043B669104BC60B00973C3046281C133166791D658273039871EBE3197473AD5A1D39D8A1DA9C938C720CABF68CB56236CC69725FDE8347E808 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455 +Out = 32EB26AB2483D2F3BE978A9103695AC261938041392B3EC42F0004A3486E4C25E8EDE76650D18307AA584D70A263E775F09EA2264F2364EA22C64A0C8B46FF69 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50515253545556 +Out = BF565228D8540557327D5D960A6DFAF189F99E1425E6644B6B8B560DB5E64FDF333ED9720FB06BBD9C68DACAF69E14AD63F399A21125ABC7C0F8062321A0AB79 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F5051525354555657 +Out = 3CE9E5A6B0EFC45B9DACD5B974AD74F4CCE9692E4F1B7C9BCC0AA3B4B8B59E2CD24F5F780C1C6ABA39E638AD49671D3E911D22B761588263F300FC47658A9C01 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758 +Out = 08C1CA315395305BD178BAE29B7B8D64BB4FA0A48E5FC5DE2CE0418A78FBC081055C71297F24CF9A3F0656FD783471FD3E9E1D9AE1471E6536ACE14A9D270AC8 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50515253545556575859 +Out = B5670E46F8FEFF91C79DF47287120159227319EBC6FFA70F87E50798C216EC026D80E49C4324D33DED1D47824E10A171C6A91E4E5D7116818C24E83E43B509F8 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A +Out = F9CD011CC3A013D37ECAB7A879B3D8FF5003D7F4B2E0A34EF88DD6564B1725D7BB623DDBC04C38D1E3328DE6348F00BAA8C305ACEF23DA5FC6E72C6A302E24B0 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B +Out = 0B8246399B00650FC4EAD6B4C23C5FC02E35C26942EA63ACBB76C07223FDAC8714C1DF919F657B3BE28C8D41AC49BE847EDE2080BC359E097160398F2E96371F + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C +Out = 8BADB719B3A09BB15513339E237EC5B1726D9EE4840D8A7668F48D0CFB488D144C54B8C13CFE9EE2C1B05FBDA9B7917CBDAD7097FE43AE86AD1B8420BA3DD039 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D +Out = 0D64A96D59310A382FC6F2AC970AA0CA255B6284383BEA86931C223770BE10704BD8B0FE29CB346D97440878E91C2E6D68549A2F8D5AA3D361084AC9CB60F9DF + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E +Out = 12456EA63299D78B9E85DE0EEC8315FB4790245ACCB0945965773AC984476DB40EFE681B9F2E09477EFA19041DD6D628A2459E0C7BBEB201BB484652A735205C + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F +Out = 92335F74E5394ED204328AE830DD451D5D15B8821F0DFB68EACF18B2444382D8FF23F1F68B0147E874A9EFD2610C863F43C30C9FC079226ED7A7DC2AFA03CC0A + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60 +Out = 76A4B5A449D12A94DB75C2319527601E48E453260154E812897F3F17C50DEE7EBA9A7E09531174E18D3B93D26762C2745B6E9A14BE6AA652275FE0C36A068846 + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6061 +Out = B8AD8A26AF9D031303CE4456075632D58D1C291CBD4FB83C48547E107B0F41CDA9905AD5B25A77BDAC19B6C51B91E727B8272BCD0D4EB761E1D553937DC569BC + +In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162 +Out = 00CABF63946E5A608B16769D8FCED9E1706CDDA0C33EA180953A9C81CEC4A4C91D0FD215B0242C2EBCF6A62C5E53C65D9607A10BB8D4C9E04E4F99751042D576 diff -Nru botan3-3.7.1+dfsg/src/tests/data/zfec.vec botan3-3.12.0+dfsg/src/tests/data/zfec.vec --- botan3-3.7.1+dfsg/src/tests/data/zfec.vec 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/data/zfec.vec 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,5 @@ # Generated by zfec (https://github.com/tahoe-lafs/zfec) -#test cpuid ssse3 sse2 +#test cpuid ssse3 sse2 neon lsx simd128 K = 1 N = 2 diff -Nru botan3-3.7.1+dfsg/src/tests/main.cpp botan3-3.12.0+dfsg/src/tests/main.cpp --- botan3-3.7.1+dfsg/src/tests/main.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/main.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -13,10 +13,11 @@ #include #include +#include namespace { -void print_item_list(std::ostringstream& err, const std::set& list) { +void print_item_list(std::ostringstream& err, const std::vector& list) { size_t line_len = 0; for(const auto& item : list) { @@ -60,12 +61,12 @@ try { const std::string arg_spec = "botan-test --verbose --help --data-dir= --pkcs11-lib= --provider= " - "--tpm2-tcti-name= --tpm2-tcti-conf= --tpm2-persistent-rsa-handle=0x81000008 " + "--tpm2-tcti-name=disabled --tpm2-tcti-conf= --tpm2-persistent-rsa-handle=0x81000008 " "--tpm2-persistent-ecc-handle=0x81000010 --tpm2-persistent-auth-value=password " "--log-success --abort-on-first-fail --no-stdout --no-avoid-undefined " "--skip-tests= --test-threads=0 --test-results-dir= --run-long-tests " "--run-memory-intensive-tests --run-online-tests --test-runs=1 " - "--drbg-seed= --report-properties= *suites"; + "--drbg-seed= --report-properties= --list-tests *suites"; Botan_CLI::Argument_Parser parser(arg_spec); @@ -76,6 +77,13 @@ return 0; } + if(parser.flag_set("list-tests")) { + for(const auto& test_name : Botan_Tests::Test::registered_tests()) { + std::cout << test_name << "\n"; + } + return 0; + } + #if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_HAS_THREAD_UTILS) /* The mlock pool becomes a major contention point when many threads are running, @@ -113,9 +121,9 @@ return tests.run(opts) ? 0 : 1; } catch(std::exception& e) { - std::cerr << "Exiting with error: " << e.what() << std::endl; + std::cerr << "Exiting with error: " << e.what() << "\n"; } catch(...) { - std::cerr << "Exiting with unknown exception" << std::endl; + std::cerr << "Exiting with unknown exception\n"; } return 2; } diff -Nru botan3-3.7.1+dfsg/src/tests/runner/test_reporter.cpp botan3-3.12.0+dfsg/src/tests/runner/test_reporter.cpp --- botan3-3.7.1+dfsg/src/tests/runner/test_reporter.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/runner/test_reporter.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,7 @@ #include "test_reporter.h" +#include #include namespace Botan_Tests { @@ -25,13 +26,14 @@ } // namespace TestSummary::TestSummary(const Test::Result& result) : - name(result.who()), - code_location(result.code_location()), - assertions(result.tests_run()), - notes(result.notes()), - failures(result.failures()), - timestamp(result.timestamp()), - elapsed_time(result.elapsed_time()) {} + m_name(result.who()), + m_code_location(result.code_location()), + m_assertions(result.tests_run()), + m_notes(result.notes()), + m_failures(result.failures()), + m_timestamp( + std::chrono::duration_cast(std::chrono::nanoseconds(result.timestamp()))), + m_elapsed_time(result.elapsed_time()) {} Testsuite::Testsuite(std::string name) : m_name(std::move(name)) {} @@ -53,7 +55,7 @@ m_results.end(), std::chrono::system_clock::time_point::max(), [](const auto& a, const auto& b) { return std::min(a, b); }, - [](const auto& result) { return result.timestamp; }); + [](const auto& result) { return result.timestamp(); }); } std::optional Testsuite::elapsed_time() const { @@ -62,7 +64,7 @@ m_results.end(), std::make_optional(std::chrono::nanoseconds::zero()), [](const auto& a, const auto& b) { return a + b; }, - [](const auto& result) { return result.elapsed_time; }); + [](const auto& result) { return result.elapsed_time(); }); } Reporter::Reporter(const Test_Options& opts) : m_total_test_runs(opts.test_runs()), m_current_test_run(0) {} @@ -84,6 +86,10 @@ suite.record(result); } +void Reporter::waiting_for_next_results(const std::string& test_name) { + next_testsuite(test_name); +} + void Reporter::record(const std::string& testsuite_name, const std::vector& results) { std::map combined; for(const auto& result : results) { @@ -97,7 +103,6 @@ i->second.merge(result); } - next_testsuite(testsuite_name); for(const auto& result : combined) { record(testsuite_name, result.second); } diff -Nru botan3-3.7.1+dfsg/src/tests/runner/test_reporter.h botan3-3.12.0+dfsg/src/tests/runner/test_reporter.h --- botan3-3.7.1+dfsg/src/tests/runner/test_reporter.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/runner/test_reporter.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,10 @@ #include "../tests.h" +#include +#include +#include + namespace Botan_Tests { /** @@ -17,22 +21,36 @@ */ class TestSummary final { public: - TestSummary(const Test::Result& result); + explicit TestSummary(const Test::Result& result); - bool passed() const { return failures.empty(); } + bool passed() const { return m_failures.empty(); } - bool failed() const { return !failures.empty(); } + bool failed() const { return !m_failures.empty(); } - public: - const std::string name; - const std::optional code_location; + const std::string& name() const { return m_name; } + + const std::optional& code_location() const { return m_code_location; } + + size_t assertions() const { return m_assertions; } + + const std::vector& notes() const { return m_notes; } + + const std::vector& failures() const { return m_failures; } + + const std::chrono::system_clock::time_point& timestamp() const { return m_timestamp; } - const size_t assertions; - const std::vector notes; - const std::vector failures; + const std::optional& elapsed_time() const { return m_elapsed_time; } - const std::chrono::system_clock::time_point timestamp; - const std::optional elapsed_time; + private: + const std::string m_name; + const std::optional m_code_location; + + const size_t m_assertions; + const std::vector m_notes; + const std::vector m_failures; + + const std::chrono::system_clock::time_point m_timestamp; + const std::optional m_elapsed_time; }; /** @@ -40,7 +58,7 @@ */ class Testsuite final { public: - Testsuite(std::string name); + explicit Testsuite(std::string name); void record(const Test::Result& result); @@ -109,6 +127,16 @@ void next_test_run(); /** + * @brief Announce waiting for a new set of test results + * + * This should be followed up by calls to record with the results + * of this test. + * + * This is used by the stdout printer + */ + void waiting_for_next_results(const std::string& test_name); + + /** * @brief Reports a single test result * * The default implementation records the result as `Testsuite` and @@ -130,7 +158,7 @@ * * Note that this merges test results with the same name */ - void record(const std::string& test_name, const std::vector& results); + void record(const std::string& testsuite_name, const std::vector& results); /** * Called once all test results have been reported for a single run. diff -Nru botan3-3.7.1+dfsg/src/tests/runner/test_runner.cpp botan3-3.12.0+dfsg/src/tests/runner/test_runner.cpp --- botan3-3.7.1+dfsg/src/tests/runner/test_runner.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/runner/test_runner.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,10 +11,14 @@ #include "test_stdout_reporter.h" #include "test_xml_reporter.h" +#include #include -#include #include +#if defined(BOTAN_HAS_CPUID) + #include +#endif + #if defined(BOTAN_HAS_THREAD_UTILS) #include #include @@ -28,22 +32,22 @@ Test_Runner::~Test_Runner() = default; -bool Test_Runner::run(const Test_Options& opts) { - if(!opts.no_stdout()) { - m_reporters.emplace_back(std::make_unique(opts, output())); +bool Test_Runner::run(const Test_Options& options) { + if(!options.no_stdout()) { + m_reporters.emplace_back(std::make_unique(options, output())); } - if(!opts.xml_results_dir().empty()) { + if(!options.xml_results_dir().empty()) { #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) - m_reporters.emplace_back(std::make_unique(opts, opts.xml_results_dir())); + m_reporters.emplace_back(std::make_unique(options, options.xml_results_dir())); #else output() << "Generating test report files is not supported on this platform\n"; #endif } - auto req = Botan_Tests::Test::filter_registered_tests(opts.requested_tests(), opts.skip_tests()); + auto req = Botan_Tests::Test::filter_registered_tests(options.requested_tests(), options.skip_tests()); // TODO: Test runner should not be aware of certain test's environmental requirements. - if(opts.pkcs11_lib().empty()) { + if(options.pkcs11_lib().empty()) { // do not run pkcs11 tests by default unless pkcs11-lib set for(auto iter = req.begin(); iter != req.end();) { if((*iter).find("pkcs11") != std::string::npos) { @@ -58,7 +62,7 @@ throw Test_Error("No tests to run"); } - std::vector seed = Botan::hex_decode(opts.drbg_seed()); + std::vector seed = Botan::hex_decode(options.drbg_seed()); if(seed.empty()) { const uint64_t ts = Botan_Tests::Test::timestamp(); seed.resize(8); @@ -66,25 +70,27 @@ } for(auto& reporter : m_reporters) { +#if defined(BOTAN_HAS_CPUID) const std::string cpuid = Botan::CPUID::to_string(); if(!cpuid.empty()) { reporter->set_property("CPU flags", cpuid); } +#endif - if(!opts.pkcs11_lib().empty()) { - reporter->set_property("pkcs11 library", opts.pkcs11_lib()); + if(!options.pkcs11_lib().empty()) { + reporter->set_property("pkcs11 library", options.pkcs11_lib()); } - if(!opts.provider().empty()) { - reporter->set_property("provider", opts.provider()); + if(!options.provider().empty()) { + reporter->set_property("provider", options.provider()); } reporter->set_property("drbg_seed", Botan::hex_encode(seed)); } - Botan_Tests::Test::set_test_options(opts); + Botan_Tests::Test::set_test_options(options); - for(size_t i = 0; i != opts.test_runs(); ++i) { + for(size_t i = 0; i != options.test_runs(); ++i) { Botan_Tests::Test::set_test_rng_seed(seed, i); for(const auto& reporter : m_reporters) { @@ -92,7 +98,7 @@ } const bool passed = - (opts.test_threads() == 1) ? run_tests(req) : run_tests_multithreaded(req, opts.test_threads()); + (options.test_threads() == 1) ? run_tests(req) : run_tests_multithreaded(req, options.test_threads()); for(const auto& reporter : m_reporters) { reporter->render(); @@ -112,9 +118,7 @@ std::vector results; try { - if(test_name == "simd_32" && Botan::CPUID::has_simd_32() == false) { - results.push_back(Test::Result::Note(test_name, "SIMD not available on this platform")); - } else if(std::unique_ptr test = Test::get_test(test_name)) { + if(std::unique_ptr test = Test::get_test(test_name)) { std::vector test_results = test->run(); for(auto& result : test_results) { if(!result.code_location() && test->registration_location()) { @@ -154,29 +158,32 @@ Botan::Thread_Pool pool(test_threads); Botan::RWLock rwlock; - std::vector>> m_fut_results; + std::vector>> fut_results; auto run_test_exclusive = [&](const std::string& test_name) { - std::unique_lock lk(rwlock); + const std::unique_lock lk(rwlock); return run_a_test(test_name); }; auto run_test_shared = [&](const std::string& test_name) { - std::shared_lock lk(rwlock); + const std::shared_lock lk(rwlock); return run_a_test(test_name); }; for(const auto& test_name : tests_to_run) { if(Test::test_needs_serialization(test_name)) { - m_fut_results.push_back(pool.run(run_test_exclusive, test_name)); + fut_results.push_back(pool.run(run_test_exclusive, test_name)); } else { - m_fut_results.push_back(pool.run(run_test_shared, test_name)); + fut_results.push_back(pool.run(run_test_shared, test_name)); } } bool passed = true; - for(size_t i = 0; i != m_fut_results.size(); ++i) { - const auto results = m_fut_results[i].get(); + for(size_t i = 0; i != fut_results.size(); ++i) { + for(auto& reporter : m_reporters) { + reporter->waiting_for_next_results(tests_to_run[i]); + } + const auto results = fut_results[i].get(); for(auto& reporter : m_reporters) { reporter->record(tests_to_run[i], results); } @@ -192,6 +199,9 @@ bool Test_Runner::run_tests(const std::vector& tests_to_run) { bool passed = true; for(const auto& test_name : tests_to_run) { + for(auto& reporter : m_reporters) { + reporter->waiting_for_next_results(test_name); + } const auto results = run_a_test(test_name); for(auto& reporter : m_reporters) { diff -Nru botan3-3.7.1+dfsg/src/tests/runner/test_runner.h botan3-3.12.0+dfsg/src/tests/runner/test_runner.h --- botan3-3.7.1+dfsg/src/tests/runner/test_runner.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/runner/test_runner.h 2026-05-07 01:38:28.000000000 +0000 @@ -10,7 +10,6 @@ #include #include -#include #include #include @@ -19,9 +18,9 @@ class Test_Options; class Reporter; -class Test_Runner final { +class Test_Runner final /* NOLINT(*-special-member-functions) */ { public: - Test_Runner(std::ostream& out); + explicit Test_Runner(std::ostream& out); ~Test_Runner(); /// @return true iff all tests have passed diff -Nru botan3-3.7.1+dfsg/src/tests/runner/test_stdout_reporter.cpp botan3-3.12.0+dfsg/src/tests/runner/test_stdout_reporter.cpp --- botan3-3.7.1+dfsg/src/tests/runner/test_stdout_reporter.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/runner/test_stdout_reporter.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,7 +12,7 @@ namespace Botan_Tests { StdoutReporter::StdoutReporter(const Test_Options& opts, std::ostream& output_stream) : - Reporter(opts), m_verbose(opts.verbose()), m_out(output_stream), m_tests_failed(0), m_tests_run(0) {} + Reporter(opts), m_verbose(opts.verbose()), m_out(output_stream) {} void StdoutReporter::next_run() { if(current_test_run() == 1) { @@ -68,7 +68,7 @@ m_out << "Test run " << current_test_run() << "/" << total_test_runs(); } - m_out << " complete ran " << m_tests_run << " tests in " << Botan_Tests::Test::format_time(total_ns) << " "; + m_out << " complete ran " << m_tests_run << " tests in " << Botan_Tests::Test::format_time(total_ns.count()) << " "; if(m_tests_failed > 0) { m_out << m_tests_failed << " tests failed (in "; diff -Nru botan3-3.7.1+dfsg/src/tests/runner/test_stdout_reporter.h botan3-3.12.0+dfsg/src/tests/runner/test_stdout_reporter.h --- botan3-3.7.1+dfsg/src/tests/runner/test_stdout_reporter.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/runner/test_stdout_reporter.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,8 @@ #define BOTAN_TEST_STDOUT_REPORTER_H_ #include "test_reporter.h" +#include +#include namespace Botan_Tests { @@ -33,8 +35,8 @@ std::ostream& m_out; std::set m_tests_failed_names; - size_t m_tests_failed; - size_t m_tests_run; + size_t m_tests_failed = 0; + size_t m_tests_run = 0; }; } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/runner/test_xml_reporter.cpp botan3-3.12.0+dfsg/src/tests/runner/test_xml_reporter.cpp --- botan3-3.7.1+dfsg/src/tests/runner/test_xml_reporter.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/runner/test_xml_reporter.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,15 +10,16 @@ #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) #include + #include + #include #include #include + #include #if defined(BOTAN_HAS_OS_UTILS) #include #endif - #include - #include #include namespace Botan_Tests { @@ -41,7 +42,7 @@ std::ostringstream oss; oss << std::setfill('0') << std::setw(2) << major << "." << std::setw(2) << minor << "." << std::setw(5) << patch - << "." << std::setw(2) << build << std::endl; + << "." << std::setw(2) << build << "\n"; return oss.str(); #else @@ -54,6 +55,8 @@ return "xcode"; #elif defined(BOTAN_BUILD_COMPILER_IS_CLANG) return "clang"; + #elif defined(BOTAN_BUILD_COMPILER_IS_CLANGCL) + return "clangcl"; #elif defined(BOTAN_BUILD_COMPILER_IS_GCC) return "gcc"; #elif defined(BOTAN_BUILD_COMPILER_IS_MSVC) @@ -74,7 +77,7 @@ } std::string format(const std::chrono::nanoseconds& dur) { - const float secs = static_cast(dur.count()) / 1000000000; + const double secs = static_cast(dur.count()) / 1000000000.0; std::ostringstream out; out.precision(3); @@ -82,6 +85,22 @@ return out.str(); } +std::map parse_report_properties(const std::vector& report_properties) { + std::map result; + + for(const auto& prop : report_properties) { + const auto colon = prop.find(':'); + // props without a colon separator or without a name are not allowed + if(colon == std::string::npos || colon == 0) { + throw Test_Error("--report-properties should be of the form :,:,..."); + } + + result.insert_or_assign(prop.substr(0, colon), prop.substr(colon + 1, prop.size() - colon - 1)); + } + + return result; +} + } // namespace XmlReporter::XmlReporter(const Test_Options& opts, std::string output_dir) : @@ -90,7 +109,7 @@ set_property("compiler", full_compiler_name_string()); set_property("compiler_version", full_compiler_version_string()); set_property("timestamp", format(std::chrono::system_clock::now())); - auto custom_props = opts.report_properties(); + auto custom_props = parse_report_properties(opts.report_properties()); for(const auto& prop : custom_props) { set_property(prop.first, prop.second); } @@ -242,20 +261,20 @@ void XmlReporter::render_testcase(std::ostream& out, const TestSummary& test) const { out << "path) << "\"" - << " line=\"" << test.code_location->line << "\""; + if(test.code_location().has_value()) { + out << " file=\"" << escape(test.code_location()->path) << "\"" + << " line=\"" << test.code_location()->line << "\""; } - if(test.failures.empty() && test.notes.empty()) { + if(test.passed() && test.notes().empty()) { out << " />\n"; } else { out << ">\n"; @@ -265,7 +284,7 @@ } void XmlReporter::render_failures_and_stdout(std::ostream& out, const TestSummary& test) const { - for(const auto& failure : test.failures) { + for(const auto& failure : test.failures()) { out << "\n" << format_cdata(failure) << "\n" << "\n"; @@ -273,9 +292,9 @@ // xUnit format does not have a special tag for test notes, hence we // render it into the freetext 'system-out' - if(!test.notes.empty()) { + if(!test.notes().empty()) { out << "\n"; - for(const auto& note : test.notes) { + for(const auto& note : test.notes()) { out << format_cdata(note) << '\n'; } out << "\n"; diff -Nru botan3-3.7.1+dfsg/src/tests/test_aead.cpp botan3-3.12.0+dfsg/src/tests/test_aead.cpp --- botan3-3.7.1+dfsg/src/tests/test_aead.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_aead.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,7 +9,8 @@ #if defined(BOTAN_HAS_AEAD_MODES) #include - #include + #include + #include #endif namespace Botan_Tests { @@ -35,42 +36,57 @@ auto enc = Botan::AEAD_Mode::create(algo, Botan::Cipher_Dir::Encryption); - result.test_eq("AEAD encrypt output_length is correct", enc->output_length(input.size()), expected.size()); + result.test_sz_eq("AEAD encrypt output_length is correct", enc->output_length(input.size()), expected.size()); - 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())); + result.test_is_true("AEAD name is not empty", !enc->name().empty()); + result.test_is_true("AEAD default nonce size is accepted", + enc->valid_nonce_length(enc->default_nonce_length())); - Botan::secure_vector garbage = rng.random_vec(enc->update_granularity()); + auto get_garbage = [&] { return rng.random_vec(enc->update_granularity()); }; - if(is_siv == false) { - result.test_throws("Unkeyed object throws for encrypt", [&]() { enc->update(garbage); }); + if(!is_siv) { + result.test_throws("Unkeyed object throws for encrypt", [&]() { + auto garbage = get_garbage(); + enc->update(garbage); + }); } - result.test_throws("Unkeyed object throws for encrypt", [&]() { enc->finish(garbage); }); + result.test_throws("Unkeyed object throws for encrypt", [&]() { + auto garbage = get_garbage(); + enc->finish(garbage); + }); if(enc->associated_data_requires_key()) { - result.test_throws("Unkeyed object throws for set AD", - [&]() { enc->set_associated_data(ad.data(), ad.size()); }); + result.test_throws("Unkeyed object throws for set AD", + [&]() { enc->set_associated_data(ad.data(), ad.size()); }); } - result.test_eq("key is not set", enc->has_keying_material(), false); + result.test_is_false("key is not set", enc->has_keying_material()); // Ensure that test resets AD and message state - result.test_eq("key is not set", enc->has_keying_material(), false); + result.test_is_false("key is not set", enc->has_keying_material()); enc->set_key(key); - result.test_eq("key is set", enc->has_keying_material(), true); + result.test_is_true("key is set", enc->has_keying_material()); - if(is_siv == false) { - result.test_throws("Cannot process data until nonce is set (enc)", [&]() { enc->update(garbage); }); - result.test_throws("Cannot process data until nonce is set (enc)", [&]() { enc->finish(garbage); }); + if(!is_siv) { + result.test_throws("Cannot process data until nonce is set (enc)", [&]() { + auto garbage = get_garbage(); + enc->update(garbage); + }); + result.test_throws("Cannot process data until nonce is set (enc)", [&]() { + auto garbage = get_garbage(); + enc->finish(garbage); + }); } enc->set_associated_data(mutate_vec(ad, rng)); enc->start(mutate_vec(nonce, rng)); + + auto garbage = get_garbage(); enc->update(garbage); // reset message specific state - enc->reset(); + enc->reset(); // NOLINT(*-ambiguous-smartptr-reset-call) /* Now try to set the AD *after* setting the nonce @@ -82,7 +98,7 @@ enc->set_associated_data(ad); } catch(Botan::Invalid_State&) { // ad after setting nonce rejected, in this case we need to reset - enc->reset(); + enc->reset(); // NOLINT(*-ambiguous-smartptr-reset-call) enc->set_associated_data(ad); enc->start(nonce); } @@ -92,17 +108,17 @@ // have to check here first if input is empty if not we can test update() and eventually process() if(buf.empty()) { enc->finish(buf); - result.test_eq("encrypt with empty input", buf, expected); + result.test_bin_eq("encrypt with empty input", buf, expected); } else { // test finish() with full input enc->finish(buf); - result.test_eq("encrypt full", buf, expected); + result.test_bin_eq("encrypt full", buf, expected); // additionally test update() if possible const size_t update_granularity = enc->update_granularity(); if(input.size() > update_granularity) { // reset state first - enc->reset(); + enc->reset(); // NOLINT(*-ambiguous-smartptr-reset-call) enc->set_associated_data(ad); enc->start(nonce); @@ -128,14 +144,14 @@ enc->finish(block); ciphertext.insert(ciphertext.end(), block.begin(), block.end()); - result.test_eq("encrypt update", ciphertext, expected); + result.test_bin_eq("encrypt update", ciphertext, expected); } // additionally test process() if possible - size_t min_final_bytes = enc->minimum_final_size(); + const size_t min_final_bytes = enc->minimum_final_size(); if(input.size() > (update_granularity + min_final_bytes)) { // again reset state first - enc->reset(); + enc->reset(); // NOLINT(*-ambiguous-smartptr-reset-call) enc->set_associated_data(ad); enc->start(nonce); @@ -148,33 +164,36 @@ const size_t bytes_written = enc->process(buf.data(), bytes_to_process); - result.confirm("Process returns data unless requires_entire_message", - enc->requires_entire_message(), - bytes_written == 0); + if(enc->requires_entire_message()) { + result.test_sz_eq("If requires_entire_message then no output is produced", bytes_written, 0); + } else { + result.test_sz_gt("If !requires_entire_message then some output is produced", bytes_written, 0); + } if(bytes_written == 0) { // SIV case buf.erase(buf.begin(), buf.begin() + bytes_to_process); enc->finish(buf); } else { - result.test_eq("correct number of bytes processed", bytes_written, bytes_to_process); + result.test_sz_eq("correct number of bytes processed", bytes_written, bytes_to_process); enc->finish(buf, bytes_written); } - result.test_eq("encrypt process", buf, expected); + result.test_bin_eq("encrypt process", buf, expected); } } // Make sure we can set the AD after processing a message enc->set_associated_data(ad); enc->clear(); - result.test_eq("key is not set", enc->has_keying_material(), false); + result.test_is_false("key is not set", enc->has_keying_material()); - result.test_throws("Unkeyed object throws for encrypt after clear", [&]() { enc->finish(buf); }); + result.test_throws("Unkeyed object throws for encrypt after clear", + [&]() { enc->finish(buf); }); if(enc->associated_data_requires_key()) { - result.test_throws("Unkeyed object throws for set AD after clear", - [&]() { enc->set_associated_data(ad.data(), ad.size()); }); + result.test_throws("Unkeyed object throws for set AD after clear", + [&]() { enc->set_associated_data(ad.data(), ad.size()); }); } return result; @@ -193,39 +212,52 @@ auto dec = Botan::AEAD_Mode::create(algo, Botan::Cipher_Dir::Decryption); - result.test_eq("AEAD decrypt output_length is correct", dec->output_length(input.size()), expected.size()); - - Botan::secure_vector garbage = rng.random_vec(dec->update_granularity()); + result.test_sz_eq("AEAD decrypt output_length is correct", dec->output_length(input.size()), expected.size()); - if(is_siv == false) { - result.test_throws("Unkeyed object throws for decrypt", [&]() { dec->update(garbage); }); - } + auto get_garbage = [&] { return rng.random_vec(dec->update_granularity()); }; + auto get_ultimate_garbage = [&] { return rng.random_vec(dec->minimum_final_size()); }; - result.test_throws("Unkeyed object throws for decrypt", [&]() { dec->finish(garbage); }); + if(!is_siv) { + result.test_throws("Unkeyed object throws for decrypt", [&]() { + auto garbage = get_garbage(); + dec->update(garbage); + }); + } + + result.test_throws("Unkeyed object throws for decrypt", [&]() { + auto garbage = get_ultimate_garbage(); + dec->finish(garbage); + }); if(dec->associated_data_requires_key()) { - result.test_throws("Unkeyed object throws for set AD", - [&]() { dec->set_associated_data(ad.data(), ad.size()); }); + result.test_throws("Unkeyed object throws for set AD", + [&]() { dec->set_associated_data(ad.data(), ad.size()); }); } // First some tests for reset() to make sure it resets what we need it to // set garbage values - result.test_eq("key is not set", dec->has_keying_material(), false); + result.test_is_false("key is not set", dec->has_keying_material()); dec->set_key(key); - result.test_eq("key is set", dec->has_keying_material(), true); + result.test_is_true("key is set", dec->has_keying_material()); 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); }); + if(!is_siv) { + result.test_throws("Cannot process data until nonce is set (dec)", [&]() { + auto garbage = get_garbage(); + dec->update(garbage); + }); + result.test_throws("Cannot process data until nonce is set (dec)", [&]() { + auto garbage = get_ultimate_garbage(); + dec->finish(garbage); + }); } dec->start(mutate_vec(nonce, rng)); - + auto garbage = get_garbage(); dec->update(garbage); // reset message specific state - dec->reset(); + dec->reset(); // NOLINT(*-ambiguous-smartptr-reset-call) Botan::secure_vector buf(input.begin(), input.end()); try { @@ -236,20 +268,20 @@ dec->set_associated_data(ad); } catch(Botan::Invalid_State&) { // ad after setting nonce rejected, in this case we need to reset - dec->reset(); + dec->reset(); // NOLINT(*-ambiguous-smartptr-reset-call) dec->set_associated_data(ad); dec->start(nonce); } // test finish() with full input dec->finish(buf); - result.test_eq("decrypt full", buf, expected); + result.test_bin_eq("decrypt full", buf, expected); // additionally test update() if possible const size_t update_granularity = dec->update_granularity(); if(input.size() > update_granularity) { // reset state first - dec->reset(); + dec->reset(); // NOLINT(*-ambiguous-smartptr-reset-call) dec->set_associated_data(ad); dec->start(nonce); @@ -274,14 +306,14 @@ dec->finish(block); plaintext.insert(plaintext.end(), block.begin(), block.end()); - result.test_eq("decrypt update", plaintext, expected); + result.test_bin_eq("decrypt update", plaintext, expected); } // additionally test process() if possible const size_t min_final_size = dec->minimum_final_size(); if(input.size() > (update_granularity + min_final_size)) { // again reset state first - dec->reset(); + dec->reset(); // NOLINT(*-ambiguous-smartptr-reset-call) dec->set_associated_data(ad); dec->start(nonce); @@ -294,20 +326,22 @@ const size_t bytes_written = dec->process(buf.data(), bytes_to_process); - result.confirm("Process returns data unless requires_entire_message", - dec->requires_entire_message(), - bytes_written == 0); + if(dec->requires_entire_message()) { + result.test_sz_eq("If requires_entire_message then no output is produced", bytes_written, 0); + } else { + result.test_sz_gt("If !requires_entire_message then some output is produced", bytes_written, 0); + } if(bytes_written == 0) { // SIV case buf.erase(buf.begin(), buf.begin() + bytes_to_process); dec->finish(buf); } else { - result.test_eq("correct number of bytes processed", bytes_written, bytes_to_process); + result.test_sz_eq("correct number of bytes processed", bytes_written, bytes_to_process); dec->finish(buf, bytes_to_process); } - result.test_eq("decrypt process", buf, expected); + result.test_bin_eq("decrypt process", buf, expected); } } catch(Botan::Exception& e) { @@ -318,7 +352,7 @@ const std::vector mutated_input = mutate_vec(input, rng, true); buf.assign(mutated_input.begin(), mutated_input.end()); - dec->reset(); + dec->reset(); // NOLINT(*-ambiguous-smartptr-reset-call) dec->set_associated_data(ad); dec->start(nonce); @@ -337,7 +371,7 @@ buf.assign(input.begin(), input.end()); std::vector bad_nonce = mutate_vec(nonce, rng); - dec->reset(); + dec->reset(); // NOLINT(*-ambiguous-smartptr-reset-call) dec->set_associated_data(ad); dec->start(bad_nonce); @@ -354,7 +388,7 @@ // test decryption with modified associated_data const std::vector bad_ad = mutate_vec(ad, rng, true); - dec->reset(); + dec->reset(); // NOLINT(*-ambiguous-smartptr-reset-call) dec->set_associated_data(bad_ad); dec->start(nonce); @@ -372,13 +406,13 @@ // Make sure we can set the AD after processing a message dec->set_associated_data(ad); dec->clear(); - result.test_eq("key is not set", dec->has_keying_material(), false); + result.test_is_false("key is not set", dec->has_keying_material()); - result.test_throws("Unkeyed object throws for decrypt", [&]() { dec->finish(buf); }); + result.test_throws("Unkeyed object throws for decrypt", [&]() { dec->finish(buf); }); if(dec->associated_data_requires_key()) { - result.test_throws("Unkeyed object throws for set AD", - [&]() { dec->set_associated_data(ad.data(), ad.size()); }); + result.test_throws("Unkeyed object throws for set AD", + [&]() { dec->set_associated_data(ad.data(), ad.size()); }); } return result; @@ -402,35 +436,36 @@ } // must be authenticated - result.test_eq("Encryption algo is an authenticated mode", enc->authenticated(), true); - result.test_eq("Decryption algo is an authenticated mode", dec->authenticated(), true); + result.test_is_true("Encryption algo is an authenticated mode", enc->authenticated()); + result.test_is_true("Decryption algo is an authenticated mode", dec->authenticated()); const std::string enc_provider = enc->provider(); - result.test_is_nonempty("enc provider", enc_provider); + result.test_str_not_empty("enc provider", enc_provider); const std::string dec_provider = enc->provider(); - result.test_is_nonempty("dec provider", dec_provider); + result.test_str_not_empty("dec provider", dec_provider); - result.test_eq("same provider", enc_provider, dec_provider); + result.test_str_eq("same provider", enc_provider, dec_provider); // FFI currently requires this, so assure it is true for all modes - result.test_gt("enc buffer sizes ok", enc->ideal_granularity(), enc->minimum_final_size()); - result.test_gt("dec buffer sizes ok", dec->ideal_granularity(), dec->minimum_final_size()); + result.test_sz_gt("enc buffer sizes ok", enc->ideal_granularity(), enc->minimum_final_size()); + result.test_sz_gt("dec buffer sizes ok", dec->ideal_granularity(), dec->minimum_final_size()); - result.test_gt("update granularity is non-zero", enc->update_granularity(), 0); + result.test_sz_gt("update granularity is non-zero", enc->update_granularity(), 0); - result.test_eq( + result.test_sz_eq( "enc and dec ideal granularity is the same", enc->ideal_granularity(), dec->ideal_granularity()); - result.test_gt( + result.test_sz_gt( "ideal granularity is at least update granularity", enc->ideal_granularity(), enc->update_granularity()); - result.confirm("ideal granularity is a multiple of update granularity", - enc->ideal_granularity() % enc->update_granularity() == 0); + result.test_is_true("ideal granularity is a multiple of update granularity", + enc->ideal_granularity() % enc->update_granularity() == 0); // test enc result.merge(test_enc(key, nonce, input, expected, ad, algo, this->rng())); // test dec + // NOLINTNEXTLINE(*-suspicious-call-argument) Yes we are swapping ptext and ctext arguments here result.merge(test_dec(key, nonce, expected, input, ad, algo, this->rng())); return result; diff -Nru botan3-3.7.1+dfsg/src/tests/test_alt_name.cpp botan3-3.12.0+dfsg/src/tests/test_alt_name.cpp --- botan3-3.7.1+dfsg/src/tests/test_alt_name.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_alt_name.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -14,6 +14,8 @@ namespace Botan_Tests { +namespace { + #if defined(BOTAN_HAS_X509_CERTIFICATES) class X509_Alt_Name_Tests final : public Test { public: @@ -39,6 +41,11 @@ 0xC0A80102, }; + const std::vector ipv6_names = { + Botan::IPv6Address({0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}), + Botan::IPv6Address({0x26, 0x06, 0x47, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}), + }; + Botan::AlternativeName alt_name; for(const auto& uri : uri_names) { alt_name.add_uri(uri); @@ -49,6 +56,9 @@ for(const auto ipv4 : ipv4_names) { alt_name.add_ipv4_address(ipv4); } + for(const auto& ipv6 : ipv6_names) { + alt_name.add_ipv6_address(ipv6); + } for(const auto& email : email_names) { alt_name.add_email(email); } @@ -72,23 +82,33 @@ Botan::BER_Decoder dec(der); dec.decode(recoded); - result.test_eq("Expected number of domains", recoded.dns().size(), dns_names.size()); + result.test_sz_eq("Expected number of domains", recoded.dns().size(), dns_names.size()); for(const auto& name : dns_names) { - result.confirm("Has expected DNS name", recoded.dns().contains(name)); + result.test_is_true("Has expected DNS name", recoded.dns().contains(name)); } - result.test_eq("Expected number of URIs", recoded.uris().size(), uri_names.size()); + result.test_sz_eq("Expected number of URIs", recoded.uris().size(), uri_names.size()); for(const auto& name : uri_names) { - result.confirm("Has expected URI name", recoded.uris().contains(name)); + result.test_is_true("Has expected URI name", recoded.uris().contains(name)); } - result.test_eq("Expected number of email", recoded.email().size(), email_names.size()); + result.test_sz_eq("Expected number of email", recoded.email().size(), email_names.size()); for(const auto& name : email_names) { - result.confirm("Has expected email name", recoded.email().contains(name)); + result.test_is_true("Has expected email name", recoded.email().contains(name)); + } + + result.test_sz_eq("Expected number of IPv4", recoded.ipv4_address().size(), ipv4_names.size()); + for(const auto ipv4 : ipv4_names) { + result.test_is_true("Has expected IPv4 name", recoded.ipv4_address().contains(ipv4)); } - result.test_eq("Expected number of DNs", recoded.directory_names().size(), 2); - result.test_eq("Expected number of Othernames", recoded.other_names().size(), 2); + result.test_sz_eq("Expected number of IPv6", recoded.ipv6_address().size(), ipv6_names.size()); + for(const auto& ipv6 : ipv6_names) { + result.test_is_true("Has expected IPv6 name", recoded.ipv6_address().contains(ipv6)); + } + + result.test_sz_eq("Expected number of DNs", recoded.directory_names().size(), 2); + result.test_sz_eq("Expected number of Othernames", recoded.other_names().size(), 2); return {result}; } @@ -98,4 +118,6 @@ #endif +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_arb_eq.h botan3-3.12.0+dfsg/src/tests/test_arb_eq.h --- botan3-3.7.1+dfsg/src/tests/test_arb_eq.h 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_arb_eq.h 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,128 @@ +/* +* (C) 2022 René Meusel +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TEST_ARB_EQ_H_ +#define BOTAN_TEST_ARB_EQ_H_ + +#include "tests.h" + +#include +#include +#include +#include + +namespace Botan_Tests { + +namespace detail { + +/* Does T have a member to_string() returning std::string? */ +template +constexpr bool has_member_to_string = false; + +template +constexpr bool has_member_to_string().to_string())>> = + std::is_convertible_v().to_string()), std::string>; + +/* Is std::to_string(T) defined? */ +template +constexpr bool has_std_to_string = false; + +template +constexpr bool has_std_to_string()))>> = true; + +/* Is ostream<<(T) defined? */ +template +constexpr bool has_ostream_operator = false; + +template +constexpr bool + has_ostream_operator(), std::declval()))>> = + true; + +/* Is T a std::optional? */ +template +struct is_optional : std::false_type {}; + +template +struct is_optional> : std::true_type {}; + +template +constexpr bool is_optional_v = is_optional::value; + +/* Is T a std::vector? */ +template +struct is_vector : std::false_type {}; + +template +struct is_vector> : std::true_type {}; + +template +constexpr bool is_vector_v = is_vector::value; + +/* Is T a std::array? */ +template +struct is_std_array : std::false_type {}; + +template +struct is_std_array> : std::true_type {}; + +template +constexpr bool is_std_array_v = is_std_array::value; + +template +std::string to_string(const T& v) { + if constexpr(detail::is_optional_v) { + return (v.has_value()) ? to_string(v.value()) : std::string("std::nullopt"); + } else if constexpr(detail::is_vector_v || detail::is_std_array_v) { + std::ostringstream oss; + oss << "{"; + for(size_t i = 0; i != v.size(); ++i) { + if(i > 0) { + oss << ", "; + } + oss << to_string(v[i]); + } + oss << "}"; + return oss.str(); + } else if constexpr(detail::has_member_to_string) { + return v.to_string(); + } else if constexpr(detail::has_ostream_operator) { + std::ostringstream oss; + oss << v; + return oss.str(); + } else if constexpr(detail::has_std_to_string) { + return std::to_string(v); + } else { + static_assert(!sizeof(T), "This type is not printable"); + return ""; + } +} + +} // namespace detail + +template +bool test_arb_eq(Test::Result& result, std::string_view what, const T& produced, const T& expected) { + static_assert(!std::convertible_to>, "Use test_bin_eq"); + static_assert(!std::convertible_to, "Use test_str_eq"); + static_assert(!std::is_integral_v, "Use test_{sz,u8,u16,u32,u64}_eq"); + static_assert(!std::is_enum_v, "Use test_enum_eq"); + + if(produced == expected) { + return result.test_success(what); + } else { + std::ostringstream out; + + out << result.who() << " " << what << " produced unexpected result '" << detail::to_string(produced) + << "' expected '" << detail::to_string(expected) << "'"; + + return result.test_failure(out.str()); + } +} + +} // namespace Botan_Tests + +#endif diff -Nru botan3-3.7.1+dfsg/src/tests/test_asn1.cpp botan3-3.12.0+dfsg/src/tests/test_asn1.cpp --- botan3-3.7.1+dfsg/src/tests/test_asn1.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_asn1.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,18 +7,22 @@ #include "tests.h" #if defined(BOTAN_HAS_ASN1) + #include #include + #include #include + #include + #include #include #include #endif namespace Botan_Tests { -#if defined(BOTAN_HAS_ASN1) - namespace { +#if defined(BOTAN_HAS_ASN1) + Test::Result test_ber_stack_recursion() { Test::Result result("BER stack recursion"); @@ -45,7 +49,7 @@ // OSS-Fuzz #4353 - Botan::ASN1_Pretty_Printer printer; + const Botan::ASN1_Pretty_Printer printer; size_t max_eoc_allowed = 0; @@ -70,7 +74,7 @@ } } - result.test_eq("EOC limited to prevent stack exhaustion", max_eoc_allowed, 16); + result.test_sz_eq("EOC limited to prevent stack exhaustion", max_eoc_allowed, 16); return result; } @@ -90,7 +94,7 @@ Botan::ASN1_String str; str.decode_from(dec); - result.test_eq("value()", str.value(), moscow_plain); + result.test_str_eq("value()", str.value(), moscow_plain); } catch(const Botan::Decoding_Error& ex) { result.test_failure(ex.what()); } @@ -113,7 +117,7 @@ Botan::ASN1_String str; str.decode_from(dec); - result.test_eq("value()", str.value(), moscow_plain); + result.test_str_eq("value()", str.value(), moscow_plain); } catch(const Botan::Decoding_Error& ex) { result.test_failure(ex.what()); } @@ -137,7 +141,7 @@ Botan::ASN1_String str; str.decode_from(dec); - result.test_eq("value()", str.value(), moscow_plain); + result.test_str_eq("value()", str.value(), moscow_plain); } catch(const Botan::Decoding_Error& ex) { result.test_failure(ex.what()); } @@ -161,7 +165,7 @@ Botan::ASN1_String str; str.decode_from(dec); - result.test_eq("value()", str.value(), moscow_plain); + result.test_str_eq("value()", str.value(), moscow_plain); } catch(const Botan::Decoding_Error& ex) { result.test_failure(ex.what()); } @@ -169,13 +173,62 @@ return result; } +Test::Result test_asn1_ucs_invalid_codepoint_rejection() { + Test::Result result("ASN.1 UCS-2/UCS-4 invalid codepoint rejection"); + + auto expect_decode_throws = [&](const char* what, const std::vector& wire) { + result.test_throws(what, [&]() { + Botan::DataSource_Memory input(wire.data(), wire.size()); + Botan::BER_Decoder dec(input); + Botan::ASN1_String str; + str.decode_from(dec); + }); + }; + + auto expect_decode_ok = [&](const char* what, const std::vector& wire) { + try { + Botan::DataSource_Memory input(wire.data(), wire.size()); + Botan::BER_Decoder dec(input); + Botan::ASN1_String str; + str.decode_from(dec); + result.test_success(what); + } catch(const std::exception& ex) { + result.test_failure(Botan::fmt("{}: unexpected throw: {}", what, ex.what())); + } + }; + + // UniversalString (tag 0x1C) with codepoint 0x00110000 - one past Unicode max + expect_decode_throws("UniversalString rejects codepoint > 0x10FFFF", {0x1C, 0x04, 0x00, 0x11, 0x00, 0x00}); + + // UniversalString with codepoint 0xFFFFFFFF (clearly out of range) + expect_decode_throws("UniversalString rejects codepoint 0xFFFFFFFF", {0x1C, 0x04, 0xFF, 0xFF, 0xFF, 0xFF}); + + // UniversalString with high surrogate 0xD800 + expect_decode_throws("UniversalString rejects surrogate codepoint", {0x1C, 0x04, 0x00, 0x00, 0xD8, 0x00}); + + // UniversalString boundary case: 0x10FFFF is the highest valid codepoint + expect_decode_ok("UniversalString accepts codepoint 0x10FFFF", {0x1C, 0x04, 0x00, 0x10, 0xFF, 0xFF}); + + // BmpString (tag 0x1E) with high surrogate + expect_decode_throws("BmpString rejects surrogate codepoint", {0x1E, 0x02, 0xD8, 0x00}); + + // BmpString with odd length is malformed + expect_decode_throws("BmpString rejects odd-length payload", {0x1E, 0x03, 0x00, 0x41, 0x00}); + + // UniversalString with non-multiple-of-4 length is malformed + expect_decode_throws("UniversalString rejects non-multiple-of-4 payload", + {0x1C, 0x05, 0x00, 0x00, 0x00, 0x41, 0x00}); + + return result; +} + Test::Result test_asn1_ascii_encoding() { Test::Result result("ASN.1 ASCII encoding"); try { // UTF-8 encoded (ASCII chars only) word 'Moscow' - const std::string moscow = "\x4D\x6F\x73\x63\x6F\x77"; - Botan::ASN1_String str(moscow); + const std::string moscow = "Moscow"; + const Botan::ASN1_String str(moscow); Botan::DER_Encoder enc; @@ -184,8 +237,7 @@ // \x13 - ASN1 tag for 'printable string' // \x06 - 6 characters of payload - const auto moscowEncoded = Botan::hex_decode("13064D6F73636F77"); - result.test_eq("encoding result", encodingResult, moscowEncoded); + result.test_bin_eq("encoding result", encodingResult, "13064D6F73636F77"); result.test_success("No crash"); } catch(const std::exception& ex) { @@ -201,7 +253,7 @@ try { // UTF-8 encoded russian word for Moscow in cyrillic script const std::string moscow = "\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0"; - Botan::ASN1_String str(moscow); + const Botan::ASN1_String str(moscow); Botan::DER_Encoder enc; @@ -210,8 +262,7 @@ // \x0C - ASN1 tag for 'UTF8 string' // \x0C - 12 characters of payload - const auto moscowEncoded = Botan::hex_decode("0C0CD09CD0BED181D0BAD0B2D0B0"); - result.test_eq("encoding result", encodingResult, moscowEncoded); + result.test_bin_eq("encoding result", encodingResult, "0C0CD09CD0BED181D0BAD0B2D0B0"); result.test_success("No crash"); } catch(const std::exception& ex) { @@ -239,7 +290,161 @@ return result; } -} // namespace +Test::Result test_asn1_negative_int_encoding() { + Test::Result result("DER encode/decode of negative integers"); + + BigInt n(32); + + for(size_t i = 0; i != 2048; ++i) { + n--; + + const auto enc = Botan::DER_Encoder().encode(n).get_contents_unlocked(); + + BigInt n_dec; + Botan::BER_Decoder(enc, Botan::BER_Decoder::Limits::DER()).decode(n_dec); + + result.test_bn_eq("DER encoding round trips negative integers", n_dec, n); + } + + return result; +} + +Test::Result test_der_constructed_tag_17_not_sorted() { + Test::Result result("DER constructed [17] is not SET-sorted"); + + // Two INTEGERs in descending order. A universal SET would lex-sort and put + // 0x01 before 0x02; a non-universal constructed [17] must preserve order. + const std::vector first = {0x02, 0x01, 0x02}; // INTEGER 2 + const std::vector second = {0x02, 0x01, 0x01}; // INTEGER 1 + + auto encode_with = [&](auto starter) { + Botan::DER_Encoder enc; + starter(enc).raw_bytes(first).raw_bytes(second).end_cons(); + return enc.get_contents_unlocked(); + }; + + // Reference: a universal SET of the same children gets sorted + const auto set_enc = encode_with([](Botan::DER_Encoder& e) -> Botan::DER_Encoder& { return e.start_set(); }); + const std::vector set_expected = {0x31, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02}; + result.test_bin_eq("universal SET is lex-sorted", set_enc, set_expected); + + // start_context_specific(17): tag byte = ContextSpecific | Constructed | 17 = 0xB1 + const auto ctx_enc = + encode_with([](Botan::DER_Encoder& e) -> Botan::DER_Encoder& { return e.start_context_specific(17); }); + const std::vector ctx_expected = {0xB1, 0x06, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01}; + result.test_bin_eq("context-specific [17] preserves order", ctx_enc, ctx_expected); + + // start_explicit_context_specific(17): same tag byte 0xB1 + const auto explicit_ctx_enc = + encode_with([](Botan::DER_Encoder& e) -> Botan::DER_Encoder& { return e.start_explicit_context_specific(17); }); + result.test_bin_eq("explicit context-specific [17] preserves order", explicit_ctx_enc, ctx_expected); + + // start_explicit(17): used to throw Internal_Error; must now produce [17] in order + const auto explicit_enc = + encode_with([](Botan::DER_Encoder& e) -> Botan::DER_Encoder& { return e.start_explicit(17); }); + result.test_bin_eq("start_explicit(17) preserves order", explicit_enc, ctx_expected); + + return result; +} + +Test::Result test_ber_indefinite_length_trailing_data() { + Test::Result result("BER indefinite length trailing data"); + + // Case 1: verify_end after consuming indef SEQUENCE + try { + const std::vector enc = {0x30, 0x80, 0x02, 0x01, 0x42, 0x00, 0x00}; + Botan::BER_Decoder dec(enc); + Botan::BigInt x; + dec.start_sequence().decode(x).end_cons(); + dec.verify_end(); + result.test_bn_eq("verify_end decoded x", x, Botan::BigInt(0x42)); + } catch(Botan::Exception& e) { + result.test_failure("verify_end after indef SEQUENCE", e.what()); + } + + // Case 2: two back-to-back indef SEQUENCES at top level + try { + const std::vector enc = { + 0x30, 0x80, 0x02, 0x01, 0x42, 0x00, 0x00, 0x30, 0x80, 0x02, 0x01, 0x43, 0x00, 0x00}; + Botan::BER_Decoder dec(enc); + Botan::BigInt x; + Botan::BigInt y; + dec.start_sequence().decode(x).end_cons(); + dec.start_sequence().decode(y).end_cons(); + dec.verify_end(); + result.test_bn_eq("back-to-back x", x, Botan::BigInt(0x42)); + result.test_bn_eq("back-to-back y", y, Botan::BigInt(0x43)); + } catch(Botan::Exception& e) { + result.test_failure("two back-to-back indef SEQUENCES", e.what()); + } + + // Case 3: nested indef SEQUENCES + try { + const std::vector enc = {0x30, 0x80, 0x30, 0x80, 0x02, 0x01, 0x42, 0x00, 0x00, 0x00, 0x00}; + Botan::BER_Decoder dec(enc); + Botan::BigInt x; + auto outer = dec.start_sequence(); + outer.start_sequence().decode(x).end_cons(); + outer.end_cons(); + dec.verify_end(); + result.test_bn_eq("nested x", x, Botan::BigInt(0x42)); + } catch(Botan::Exception& e) { + result.test_failure("nested indef SEQUENCE", e.what()); + } + + // Case 4: while(more_items()) loop over an indef SEQUENCE + try { + const std::vector enc = {0x30, 0x80, 0x02, 0x01, 0x42, 0x02, 0x01, 0x43, 0x00, 0x00}; + Botan::BER_Decoder dec(enc); + auto seq = dec.start_sequence(); + std::vector xs; + while(seq.more_items()) { + Botan::BigInt x; + seq.decode(x); + xs.push_back(x); + } + seq.end_cons(); + dec.verify_end(); + result.test_sz_eq("more_items count", xs.size(), 2); + if(xs.size() == 2) { + result.test_bn_eq("more_items xs[0]", xs[0], Botan::BigInt(0x42)); + result.test_bn_eq("more_items xs[1]", xs[1], Botan::BigInt(0x43)); + } + } catch(Botan::Exception& e) { + result.test_failure("more_items loop over indef SEQUENCE", e.what()); + } + + return result; +} + +Test::Result test_ber_find_eoc() { + Test::Result result("BER indefinite length EOC matching"); + + const size_t num_siblings = 4096; + + std::vector ber; + ber.push_back(0x30); // outer SEQUENCE | CONSTRUCTED + ber.push_back(0x80); // indefinite length + for(size_t i = 0; i != num_siblings; ++i) { + ber.push_back(0x30); // inner SEQUENCE | CONSTRUCTED + ber.push_back(0x80); // indefinite length + ber.push_back(0x00); // EOC tag + ber.push_back(0x00); // EOC length + } + ber.push_back(0x00); // outer EOC tag + ber.push_back(0x00); // outer EOC length + + try { + Botan::BER_Decoder dec(ber); + const Botan::BER_Object obj = dec.get_next_object(); + + result.test_sz_eq("object body includes children", obj.length(), num_siblings * 4); + } catch(Botan::Exception& e) { + result.test_failure("decode failed", e.what()); + } + + return result; +} class ASN1_Tests final : public Test { public: @@ -248,13 +453,18 @@ results.push_back(test_ber_stack_recursion()); results.push_back(test_ber_eoc_decoding_limits()); + results.push_back(test_ber_indefinite_length_trailing_data()); + results.push_back(test_ber_find_eoc()); results.push_back(test_asn1_utf8_ascii_parsing()); results.push_back(test_asn1_utf8_parsing()); results.push_back(test_asn1_ucs2_parsing()); results.push_back(test_asn1_ucs4_parsing()); + results.push_back(test_asn1_ucs_invalid_codepoint_rejection()); results.push_back(test_asn1_ascii_encoding()); results.push_back(test_asn1_utf8_encoding()); results.push_back(test_asn1_tag_underlying_type()); + results.push_back(test_asn1_negative_int_encoding()); + results.push_back(test_der_constructed_tag_17_not_sorted()); return results; } @@ -283,10 +493,10 @@ const bool valid = tag_str.find(".invalid") == std::string::npos; if(valid) { - Botan::ASN1_Time time(tspec, tag); + const Botan::ASN1_Time time(tspec, tag); result.test_success("Accepted valid time"); } else { - result.test_throws("Invalid time rejected", [=]() { Botan::ASN1_Time time(tspec, tag); }); + result.test_throws("Invalid time rejected", [=]() { const Botan::ASN1_Time time(tspec, tag); }); } return result; @@ -295,23 +505,103 @@ BOTAN_REGISTER_TEST("asn1", "asn1_time", ASN1_Time_Parsing_Tests); +class ASN1_String_Validation_Tests final : public Text_Based_Test { + public: + ASN1_String_Validation_Tests() : + Text_Based_Test("asn1_string_validation.vec", + "Input,ValidNumeric,ValidPrintable,ValidIa5,ValidVisible,ValidUtf8") {} + + Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override { + Test::Result result("ASN.1 string validation"); + + const auto input = vars.get_req_str("Input"); + const bool valid_numeric = vars.get_req_bool("ValidNumeric"); + const bool valid_printable = vars.get_req_bool("ValidPrintable"); + const bool valid_ia5 = vars.get_req_bool("ValidIa5"); + const bool valid_visible = vars.get_req_bool("ValidVisible"); + const bool valid_utf8 = vars.get_req_bool("ValidUtf8"); + + test_string_type(result, input, "NumericString", Botan::ASN1_Type::NumericString, valid_numeric); + test_string_type(result, input, "PrintableString", Botan::ASN1_Type::PrintableString, valid_printable); + test_string_type(result, input, "Ia5String", Botan::ASN1_Type::Ia5String, valid_ia5); + test_string_type(result, input, "VisibleString", Botan::ASN1_Type::VisibleString, valid_visible); + test_string_type(result, input, "Utf8String", Botan::ASN1_Type::Utf8String, valid_utf8); + + if(valid_utf8) { + try { + const Botan::ASN1_String str(input); + const auto expected_tag = + valid_printable ? Botan::ASN1_Type::PrintableString : Botan::ASN1_Type::Utf8String; + result.test_u32_eq("String tagging categorization", + static_cast(str.tagging()), + static_cast(expected_tag)); + } catch(const std::exception& ex) { + result.test_failure(Botan::fmt("default constructor unexpectedly rejected '{}': {}", input, ex.what())); + } + } + + return result; + } + + private: + void test_string_type(Test::Result& result, + std::string_view input, + std::string_view type, + Botan::ASN1_Type tag, + bool expected_valid) { + if(expected_valid) { + try { + const Botan::ASN1_String str(input, tag); + result.test_str_eq(Botan::fmt("{} constructor value", type), str.value(), input); + + const auto enc = raw_encode_string(input, tag); + Botan::BER_Decoder dec(enc); + Botan::ASN1_String decoded; + decoded.decode_from(dec); + result.test_str_eq(Botan::fmt("{} decode value", type), decoded.value(), input); + } catch(const std::exception& e) { + result.test_failure(Botan::fmt("{} unexpectedly rejected '{}': {}", type, input, e.what())); + } + } else { + result.test_throws(Botan::fmt("{} constructor rejects", type), + [&]() { const Botan::ASN1_String str(input, tag); }); + + result.test_throws(Botan::fmt("{} decode rejects", type), [&]() { + const auto enc = raw_encode_string(input, tag); + Botan::BER_Decoder dec(enc); + Botan::ASN1_String decoded; + decoded.decode_from(dec); + }); + } + } + + static std::vector raw_encode_string(std::string_view input, Botan::ASN1_Type tag) { + std::vector encoding; + Botan::DER_Encoder der(encoding); + der.add_object(tag, Botan::ASN1_Class::Universal, input); + return encoding; + } +}; + +BOTAN_REGISTER_TEST("asn1", "asn1_string_validation", ASN1_String_Validation_Tests); + class ASN1_Printer_Tests final : public Test { public: std::vector run() override { Test::Result result("ASN1_Pretty_Printer"); - Botan::ASN1_Pretty_Printer printer; + const Botan::ASN1_Pretty_Printer printer; const size_t num_tests = 7; for(size_t i = 1; i <= num_tests; ++i) { - std::string i_str = std::to_string(i); + const std::string i_str = std::to_string(i); const std::vector input_data = Test::read_binary_data_file("asn1_print/input" + i_str + ".der"); const std::string expected_output = Test::read_data_file("asn1_print/output" + i_str + ".txt"); try { const std::string output = printer.print(input_data); - result.test_eq("Test " + i_str, output, expected_output); + result.test_str_eq("Test " + i_str, output, expected_output); } catch(Botan::Exception& e) { result.test_failure(Botan::fmt("Printing test {} failed with an exception: '{}'", i, e.what())); } @@ -323,6 +613,59 @@ BOTAN_REGISTER_TEST("asn1", "asn1_printer", ASN1_Printer_Tests); +class ASN1_Decoding_Tests final : public Text_Based_Test { + public: + ASN1_Decoding_Tests() : Text_Based_Test("asn1_decoding.vec", "Input,ResultBER", "ResultDER") {} + + Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override { + const auto input = vars.get_req_bin("Input"); + const std::string expected_ber = vars.get_req_str("ResultBER"); + const std::string expected_der = vars.get_opt_str("ResultDER", expected_ber); + + Test::Result result("ASN1 decoding"); + + decoding_test(result, input, expected_ber, false); + decoding_test(result, input, expected_der, true); + + return result; + } + + private: + static void decoding_test(Test::Result& result, + std::span input, + std::string_view expected, + bool require_der) { + const Botan::ASN1_Pretty_Printer printer(4096, 2048, true, 0, 60, 64, require_der); + const std::string mode = require_der ? "DER" : "BER"; + std::ostringstream sink; + + try { + printer.print_to_stream(sink, input.data(), input.size()); + + if(expected == "OK") { + result.test_success(); + } else { + result.test_failure(Botan::fmt("Accepted invalid {} input, expected error {}", mode, expected)); + } + } catch(const std::exception& e) { + if(expected == "OK") { + result.test_failure(Botan::fmt("Rejected valid {} input with {}", mode, e.what())); + } else { + // BER_Decoding_Error prepends "BER: " to the message + std::string msg = e.what(); + if(msg.starts_with("BER: ")) { + msg = msg.substr(5); + } + result.test_str_eq("error message", msg, expected); + } + } + } +}; + +BOTAN_REGISTER_TEST("asn1", "asn1_decoding", ASN1_Decoding_Tests); + #endif +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_bigint.cpp botan3-3.12.0+dfsg/src/tests/test_bigint.cpp --- botan3-3.7.1+dfsg/src/tests/test_bigint.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_bigint.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,16 +10,17 @@ #if defined(BOTAN_HAS_NUMBERTHEORY) #include "test_rng.h" #include + #include #include - #include + #include + #include #include #include #include #include - #include #include #include - #include + #include #endif namespace Botan_Tests { @@ -48,7 +49,7 @@ static Test::Result test_bigint_sizes() { Test::Result result("BigInt size functions"); - for(size_t bit : {1, 8, 16, 31, 32, 64, 97, 128, 179, 192, 512, 521}) { + for(const size_t bit : {1, 8, 16, 31, 32, 64, 97, 128, 179, 192, 512, 521}) { BigInt a; a.set_bit(bit); @@ -56,14 +57,14 @@ // Test 2^n and 2^n-1 for(size_t i = 0; i != 2; ++i) { const size_t exp_bits = bit + 1 - i; - result.test_eq("BigInt::bits", a.bits(), exp_bits); - result.test_eq( + result.test_sz_eq("BigInt::bits", a.bits(), exp_bits); + result.test_sz_eq( "BigInt::bytes", a.bytes(), (exp_bits % 8 == 0) ? (exp_bits / 8) : (exp_bits + 8 - exp_bits % 8) / 8); if(bit == 1 && i == 1) { - result.test_is_eq("BigInt::to_u32bit zero", a.to_u32bit(), static_cast(1)); + result.test_u32_eq("BigInt::to_u32bit zero", a.to_u32bit(), static_cast(1)); } else if(bit <= 31 || (bit == 32 && i == 1)) { - result.test_is_eq( + result.test_u32_eq( "BigInt::to_u32bit", a.to_u32bit(), static_cast((uint64_t(1) << bit) - i)); } else { try { @@ -95,25 +96,25 @@ }); BigInt p = Botan::random_prime(*rng, 2); - result.confirm("Only two 2-bit primes", p == 2 || p == 3); + result.test_is_true("Only two 2-bit primes", p == 2 || p == 3); p = Botan::random_prime(*rng, 3); - result.confirm("Only two 3-bit primes", p == 5 || p == 7); + result.test_is_true("Only two 3-bit primes", p == 5 || p == 7); p = Botan::random_prime(*rng, 4); - result.confirm("Only two 4-bit primes", p == 11 || p == 13); + result.test_is_true("Only two 4-bit primes", p == 11 || p == 13); for(size_t bits = 5; bits <= 32; ++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, *rng), true); + result.test_sz_eq("Expected bit size", p.bits(), bits); + result.test_is_true("P is prime", Botan::is_prime(p, *rng)); } const size_t safe_prime_bits = 65; 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, *rng)); - result.confirm("(P-1)/2 is prime", Botan::is_prime((safe_prime - 1) / 2, *rng)); + result.test_sz_eq("Safe prime size", safe_prime.bits(), safe_prime_bits); + result.test_is_true("P is prime", Botan::is_prime(safe_prime, *rng)); + result.test_is_true("(P-1)/2 is prime", Botan::is_prime((safe_prime - 1) / 2, *rng)); return result; } @@ -129,7 +130,7 @@ const auto expected = Botan::concat(encoded_n1, encoded_n2); const auto encoded_n1_n2 = BigInt::encode_fixed_length_int_pair(n1, n2, 256); - result.test_eq("encode_fixed_length_int_pair", encoded_n1_n2, expected); + result.test_bin_eq("encode_fixed_length_int_pair", encoded_n1_n2, expected); for(size_t i = 0; i < 256 - n1.bytes(); ++i) { if(encoded_n1[i] != 0) { @@ -158,7 +159,7 @@ const uint32_t cmp = t.to_u32bit(); - result.test_eq("Same value", size_t(val), size_t(cmp)); + result.test_sz_eq("Same value", size_t(val), size_t(cmp)); } } @@ -180,7 +181,7 @@ iss.str(vec.first); iss >> n; - result.test_eq("input '" + vec.first + "'", n, vec.second); + result.test_bn_eq("input '" + vec.first + "'", n, vec.second); } auto check_bigint_formatting = [&](const Botan::BigInt& n, @@ -190,19 +191,19 @@ const std::string& neg_hex) { std::ostringstream oss; oss << n; - result.test_eq("output decimal", oss.str(), dec); + result.test_str_eq("output decimal", oss.str(), dec); oss.str(""); oss << (-n); - result.test_eq("output negative decimal", oss.str(), neg_dec); + result.test_str_eq("output negative decimal", oss.str(), neg_dec); oss.str(""); oss << std::hex << n; - result.test_eq("output hex", oss.str(), hex); + result.test_str_eq("output hex", oss.str(), hex); oss.str(""); oss << std::hex << (-n); - result.test_eq("output negative hex", oss.str(), neg_hex); + result.test_str_eq("output negative hex", oss.str(), neg_hex); }; check_bigint_formatting(Botan::BigInt(33), "33", "0x21", "-33", "-0x21"); @@ -212,7 +213,7 @@ check_bigint_formatting(Botan::BigInt(5), "5", "0x05", "-5", "-0x05"); result.test_throws("octal output not supported", [&]() { - Botan::BigInt n(5); + const Botan::BigInt n(5); std::ostringstream oss; oss << std::oct << n; }); @@ -235,22 +236,22 @@ const bool expected = vars.get_req_bool("R"); if(op == "EQ") { - result.confirm("Values equal", x == y, expected); + result.test_bool_eq("Values equal", x == y, expected); } else if(op == "LT") { - result.confirm("Values LT", x < y, expected); + result.test_bool_eq("Values LT", x < y, expected); if(expected) { - result.confirm("If LT then reverse is GT", y >= x); + result.test_is_true("If LT then reverse is GT", y >= x); } else { - result.confirm("If not LT then GTE", x >= y); + result.test_is_true("If not LT then GTE", x >= y); } } else if(op == "LTE") { - result.confirm("Values LTE", x <= y, expected); + result.test_bool_eq("Values LTE", x <= y, expected); if(expected) { - result.confirm("If LTE then either LT or EQ", x < y || x == y); + result.test_is_true("If LTE then either LT or EQ", x < y || x == y); } else { - result.confirm("If not LTE then GT", x > y); + result.test_is_true("If not LTE then GT", x > y); } } else { throw Test_Error("Unknown BigInt comparison type " + op); @@ -275,16 +276,16 @@ const BigInt b = vars.get_req_bn("In2"); const BigInt c = vars.get_req_bn("Output"); - result.test_eq("a + b", a + b, c); - result.test_eq("b + a", b + a, c); + result.test_bn_eq("a + b", a + b, c); + result.test_bn_eq("b + a", b + a, c); BigInt e = a; e += b; - result.test_eq("a += b", e, c); + result.test_bn_eq("a += b", e, c); e = b; e += a; - result.test_eq("b += a", e, c); + result.test_bn_eq("b += a", e, c); return result; } @@ -303,11 +304,11 @@ const BigInt b = vars.get_req_bn("In2"); const BigInt c = vars.get_req_bn("Output"); - result.test_eq("a - b", a - b, c); + result.test_bn_eq("a - b", a - b, c); BigInt e = a; e -= b; - result.test_eq("a -= b", e, c); + result.test_bn_eq("a -= b", e, c); return result; } @@ -326,16 +327,16 @@ const BigInt b = vars.get_req_bn("In2"); const BigInt c = vars.get_req_bn("Output"); - result.test_eq("a * b", a * b, c); - result.test_eq("b * a", b * a, c); + result.test_bn_eq("a * b", a * b, c); + result.test_bn_eq("b * a", b * a, c); BigInt e = a; e *= b; - result.test_eq("a *= b", e, c); + result.test_bn_eq("a *= b", e, c); e = b; e *= a; - result.test_eq("b *= a", e, c); + result.test_bn_eq("b *= a", e, c); return result; } @@ -353,8 +354,8 @@ const BigInt input = vars.get_req_bn("Input"); const BigInt output = vars.get_req_bn("Output"); - result.test_eq("a * a", input * input, output); - result.test_eq("sqr(a)", square(input), output); + result.test_bn_eq("a * a", input * input, output); + result.test_bn_eq("sqr(a)", square(input), output); return result; } @@ -373,27 +374,28 @@ const BigInt b = vars.get_req_bn("In2"); const BigInt c = vars.get_req_bn("Output"); - result.test_eq("a / b", a / b, c); + result.test_bn_eq("a / b", a / b, c); BigInt e = a; e /= b; - result.test_eq("a /= b", e, c); + result.test_bn_eq("a /= b", e, c); - if(b.sig_words() == 1) { + if(b.sig_words() == 1 && b.signum() >= 0) { const Botan::word bw = b.word_at(0); - result.test_eq("bw ok", Botan::BigInt::from_word(bw), b); + result.test_bn_eq("Low word correct", Botan::BigInt::from_word(bw), b); Botan::BigInt ct_q; - Botan::word ct_r; + Botan::word ct_r = 0; Botan::ct_divide_word(a, bw, ct_q, ct_r); - result.test_eq("ct_divide_word q", ct_q, c); - result.test_eq("ct_divide_word r", ct_q * b + ct_r, a); + result.test_bn_eq("ct_divide_word q", ct_q, c); + result.test_bn_eq("ct_divide_word r", ct_q * b + ct_r, a); } - Botan::BigInt ct_q, ct_r; + Botan::BigInt ct_q; + Botan::BigInt ct_r; Botan::ct_divide(a, b, ct_q, ct_r); - result.test_eq("ct_divide q", ct_q, c); - result.test_eq("ct_divide r", ct_q * b + ct_r, a); + result.test_bn_eq("ct_divide q", ct_q, c); + result.test_bn_eq("ct_divide r", ct_q * b + ct_r, a); return result; } @@ -404,17 +406,12 @@ class BigInt_DivPow2k_Test final : public Test { public: std::vector run() override { - Test::Result result("BigInt ct_divide_pow2k"); + Test::Result result("BigInt divide pow2k"); for(size_t k = 2; k != 128; ++k) { - auto div1 = Botan::ct_divide_pow2k(k, 1); - result.test_eq("ct_divide_pow2k div 1", div1, Botan::BigInt::power_of_2(k)); - - auto div2 = Botan::ct_divide_pow2k(k, 2); - result.test_eq("ct_divide_pow2k div 2", div2, Botan::BigInt::power_of_2(k - 1)); - - auto div4 = Botan::ct_divide_pow2k(k, 4); - result.test_eq("ct_divide_pow2k div 4", div4, Botan::BigInt::power_of_2(k - 2)); + testcase(k, 1, result); + testcase(k, 2, result); + testcase(k, 4, result); } for(size_t k = 4; k != 512; ++k) { @@ -425,15 +422,23 @@ if(y.is_zero()) { continue; } - const BigInt ct_pow2k = ct_divide_pow2k(k, y); - const BigInt ref = BigInt::power_of_2(k) / y; - result.test_eq("ct_divide_pow2k matches Knuth division", ct_pow2k, ref); + testcase(k, y, result); } } return {result}; } + + private: + static void testcase(size_t k, const BigInt& y, Test::Result& result) { + const BigInt ct_pow2k = ct_divide_pow2k(k, y); + const BigInt vt_pow2k = vartime_divide_pow2k(k, y); + const BigInt ref = BigInt::power_of_2(k) / y; + + result.test_bn_eq("ct_divide_pow2k matches Knuth division", ct_pow2k, ref); + result.test_bn_eq("vartime_divide_pow2k matches Knuth division", vt_pow2k, ref); + } }; BOTAN_REGISTER_TEST("math", "bn_div_pow2k", BigInt_DivPow2k_Test); @@ -449,17 +454,19 @@ const BigInt b = vars.get_req_bn("In2"); const BigInt expected = vars.get_req_bn("Output"); - result.test_eq("a % b", a % b, expected); + result.test_bn_eq("a % b", a % b, expected); BigInt e = a; e %= b; - result.test_eq("a %= b", e, expected); + result.test_bn_eq("a %= b", e, expected); - auto mod_b_pub = Botan::Modular_Reducer::for_public_modulus(b); - result.test_eq("Barrett public", mod_b_pub.reduce(a), expected); + if(a.signum() >= 0 && a < (b * b)) { + auto mod_b_pub = Botan::Barrett_Reduction::for_public_modulus(b); + result.test_bn_eq("Barrett public", mod_b_pub.reduce(a), expected); - auto mod_b_sec = Botan::Modular_Reducer::for_secret_modulus(b); - result.test_eq("Barrett secret", mod_b_sec.reduce(a), expected); + auto mod_b_sec = Botan::Barrett_Reduction::for_secret_modulus(b); + result.test_bn_eq("Barrett secret", mod_b_sec.reduce(a), expected); + } // if b fits into a Botan::word test %= operator for words if(b.sig_words() == 1) { @@ -467,19 +474,20 @@ e = a; e %= b_word; - result.test_eq("a %= b (as word)", e, expected); + result.test_bn_eq("a %= b (as word)", e, expected); - result.test_eq("a % b (as word)", a % b_word, expected); + result.test_bn_eq("a % b (as word)", a % b_word, expected); Botan::BigInt ct_q; - Botan::word ct_r; + Botan::word ct_r = 0; Botan::ct_divide_word(a, b.word_at(0), ct_q, ct_r); - result.test_eq("ct_divide_u8 r", ct_r, expected); + result.test_bn_eq("ct_divide_u8 r", ct_r, expected); } - Botan::BigInt ct_q, ct_r; + Botan::BigInt ct_q; + Botan::BigInt ct_r; Botan::ct_divide(a, b, ct_q, ct_r); - result.test_eq("ct_divide r", ct_r, expected); + result.test_bn_eq("ct_divide r", ct_r, expected); return result; } @@ -487,6 +495,55 @@ BOTAN_REGISTER_TEST("math", "bn_mod", BigInt_Mod_Test); +class Barrett_Redc_Test final : public Test { + public: + std::vector run() override { + Test::Result result("Barrett reduction"); + + for(size_t t = 0; t != 10000; ++t) { + const auto mod = [&]() { + if(t <= 16) { + return BigInt::from_u64(t) + 1; + } else { + const size_t bits = (t / 4) + 2; + + if(t % 4 == 0) { + return BigInt::power_of_2(bits); + } else if(t % 4 == 1) { + return BigInt::power_of_2(bits) - 1; + } else if(t % 4 == 2) { + return BigInt::power_of_2(bits) + 1; + } else { + Botan::BigInt b; + b.randomize(rng(), bits, true); + return b; + } + } + }(); + + const size_t mod_bits = mod.bits(); + auto barrett = Botan::Barrett_Reduction::for_public_modulus(mod); + + for(size_t i = 0; i != 10; ++i) { + const auto input = [&]() { + Botan::BigInt b; + b.randomize(rng(), 2 * mod_bits, false); + return b; + }(); + + const auto reduced_ref = input % mod; + const auto reduced_barrett = barrett.reduce(input); + + result.test_bn_eq("Barrett reduction matches variable time division", reduced_barrett, reduced_ref); + } + } + + return {result}; + } +}; + +BOTAN_REGISTER_TEST("math", "barrett_redc", Barrett_Redc_Test); + class BigInt_GCD_Test final : public Text_Based_Test { public: BigInt_GCD_Test() : Text_Based_Test("bn/gcd.vec", "X,Y,GCD") {} @@ -499,10 +556,10 @@ const BigInt expected = vars.get_req_bn("GCD"); const BigInt g1 = Botan::gcd(x, y); - result.test_eq("gcd", g1, expected); + result.test_bn_eq("gcd", g1, expected); const BigInt g2 = Botan::gcd(y, x); - result.test_eq("gcd", g2, expected); + result.test_bn_eq("gcd", g2, expected); return result; } @@ -524,11 +581,11 @@ const int32_t j = Botan::jacobi(a, n); if(j == 0) { - result.test_eq("jacobi", expected, "0"); + result.test_str_eq("jacobi", expected, "0"); } else if(j == -1) { - result.test_eq("jacobi", expected, "-1"); + result.test_str_eq("jacobi", expected, "-1"); } else { - result.test_eq("jacobi", expected, "1"); + result.test_str_eq("jacobi", expected, "1"); } return result; @@ -548,11 +605,11 @@ const size_t shift = vars.get_req_bn("Shift").to_u32bit(); const BigInt output = vars.get_req_bn("Output"); - result.test_eq("a << s", value << shift, output); + result.test_bn_eq("a << s", value << shift, output); BigInt e = value; e <<= shift; - result.test_eq("a <<= s", e, output); + result.test_bn_eq("a <<= s", e, output); return result; } @@ -571,11 +628,11 @@ const size_t shift = vars.get_req_bn("Shift").to_u32bit(); const BigInt output = vars.get_req_bn("Output"); - result.test_eq("a >> s", value >> shift, output); + result.test_bn_eq("a >> s", value >> shift, output); BigInt e = value; e >>= shift; - result.test_eq("a >>= s", e, output); + result.test_bn_eq("a >>= s", e, output); return result; } @@ -593,7 +650,7 @@ Botan::BigInt a = Botan::BigInt::with_capacity(bits / sizeof(Botan::word)); for(size_t i = 0; i < bits; ++i) { - if(rng->next_byte() & 1) { + if(rng->next_byte() % 2 == 1) { a.set_bit(i); } } @@ -605,7 +662,7 @@ ct.ct_shift_left(i); Botan::CT::unpoison(ct); chk <<= i; - result.test_eq(Botan::fmt("ct << {}", i), ct, chk); + result.test_bn_eq(Botan::fmt("ct << {}", i), ct, chk); } result.end_timer(); @@ -625,7 +682,7 @@ const BigInt modulus = vars.get_req_bn("Modulus"); const BigInt expected = vars.get_req_bn("Output"); - result.test_eq("power_mod", Botan::power_mod(base, exponent, modulus), expected); + result.test_bn_eq("power_mod", Botan::power_mod(base, exponent, modulus), expected); return result; } }; @@ -645,7 +702,7 @@ const bool is_prime = (header == "Prime"); Test::Result result("BigInt Test " + header); - result.test_eq("is_prime", Botan::is_prime(value, this->rng()), is_prime); + result.test_bool_eq("is_prime", Botan::is_prime(value, this->rng()), is_prime); return result; } @@ -663,7 +720,7 @@ const BigInt computed = Botan::is_perfect_square(value); Test::Result result("BigInt IsSquare"); - result.test_eq("is_perfect_square", computed, expected); + result.test_bn_eq("is_perfect_square", computed, expected); return result; } }; @@ -683,11 +740,11 @@ const Botan::BigInt a_sqrt = Botan::sqrt_modulo_prime(a, p); - result.test_eq("sqrt_modulo_prime", a_sqrt, exp); + result.test_bn_eq("sqrt_modulo_prime", a_sqrt, exp); if(a_sqrt > 1) { const Botan::BigInt a_sqrt2 = (a_sqrt * a_sqrt) % p; - result.test_eq("square correct", a_sqrt2, a); + result.test_bn_eq("square correct", a_sqrt2, a); } return result; @@ -707,21 +764,21 @@ const Botan::BigInt mod = vars.get_req_bn("Modulus"); const Botan::BigInt expected = vars.get_req_bn("Output"); - result.test_eq("inverse_mod", Botan::inverse_mod(a, mod), expected); + result.test_bn_eq("inverse_mod", Botan::inverse_mod(a, mod), expected); if(a < mod && a > 0 && a < mod) { auto g = Botan::inverse_mod_general(a, mod); if(g.has_value()) { - result.test_eq("inverse_mod_general", g.value(), expected); - result.test_eq("inverse works", ((g.value() * a) % mod), BigInt::one()); + result.test_bn_eq("inverse_mod_general", g.value(), expected); + result.test_bn_eq("inverse works", ((g.value() * a) % mod), BigInt::one()); } else { - result.confirm("inverse_mod_general", expected.is_zero()); + result.test_is_true("inverse_mod_general", expected.is_zero()); } if(Botan::is_prime(mod, rng()) && mod != 2) { BOTAN_ASSERT_NOMSG(expected > 0); - result.test_eq("inverse_mod_secret_prime", Botan::inverse_mod_secret_prime(a, mod), expected); - result.test_eq("inverse_mod_public_prime", Botan::inverse_mod_public_prime(a, mod), expected); + result.test_bn_eq("inverse_mod_secret_prime", Botan::inverse_mod_secret_prime(a, mod), expected); + result.test_bn_eq("inverse_mod_public_prime", Botan::inverse_mod_public_prime(a, mod), expected); } } @@ -744,9 +801,9 @@ const Botan::BigInt expected = vars.get_req_bn("Output"); Fixed_Output_RNG rng(seed); - Botan::BigInt generated = BigInt::random_integer(rng, min, max); + const Botan::BigInt generated = BigInt::random_integer(rng, min, max); - result.test_eq("random_integer KAT", generated, expected); + result.test_bn_eq("random_integer KAT", generated, expected); return result; } @@ -757,10 +814,10 @@ class Lucas_Primality_Test final : public Test { public: std::vector run() override { - const uint32_t lucas_max = (Test::run_long_tests() ? 100000 : 6000); + const uint32_t lucas_max = (Test::run_long_tests() ? 100000 : 10000) + 1; // OEIS A217120 - std::set lucas_pp{ + const std::set lucas_pp{ 323, 377, 1159, 1829, 3827, 5459, 5777, 9071, 9179, 10877, 11419, 11663, 13919, 14839, 16109, 16211, 18407, 18971, 19043, 22499, 23407, 24569, 25199, 25877, 26069, 27323, 32759, 34943, 35207, 39059, 39203, 39689, 40309, 44099, 46979, 47879, 50183, 51983, 53663, 56279, 58519, 60377, 63881, 69509, 72389, @@ -770,16 +827,16 @@ Test::Result result("Lucas primality test"); for(uint32_t i = 3; i <= lucas_max; i += 2) { - auto mod_i = Botan::Modular_Reducer::for_public_modulus(i); + auto mod_i = Botan::Barrett_Reduction::for_public_modulus(i); const bool passes_lucas = Botan::is_lucas_probable_prime(i, mod_i); const bool is_prime = Botan::is_prime(i, this->rng()); const bool is_lucas_pp = (is_prime == false && passes_lucas == true); if(is_lucas_pp) { - result.confirm("Lucas pseudoprime is in list", lucas_pp.count(i) == 1); + result.test_is_true("Lucas pseudoprime is in list", lucas_pp.contains(i)); } else { - result.confirm("Lucas non-pseudoprime is not in list", !lucas_pp.contains(i)); + result.test_is_true("Lucas non-pseudoprime is not in list", !lucas_pp.contains(i)); } } @@ -806,7 +863,7 @@ const auto random_primes = [&]() { std::vector rp; for(size_t i = 0; i != iter / 10; ++i) { - size_t bits = (128 + (i % 1024)) % 4096; + const size_t bits = (128 + (i % 1024)) % 4096; auto p = Botan::random_prime(rng(), bits); if(gcd(p - 1, e) == 1) { rp.push_back(p); @@ -830,9 +887,9 @@ auto d = Botan::compute_rsa_secret_exponent(e, phi_n, p, q); - auto one = (e * d) % phi_n; + auto ed_mod_phi_n = (e * d) % phi_n; - result.test_eq("compute_rsa_secret_exponent returned inverse", (e * d) % phi_n, Botan::BigInt::one()); + result.test_bn_eq("compute_rsa_secret_exponent returned inverse", ed_mod_phi_n, Botan::BigInt::one()); } return {result}; @@ -864,10 +921,11 @@ Test::Result result("DSA Parameter Generation"); try { - Botan::BigInt gen_P, gen_Q; + Botan::BigInt gen_P; + Botan::BigInt gen_Q; 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); + result.test_bn_eq("P", gen_P, exp_P); + result.test_bn_eq("Q", gen_Q, exp_Q); } else { result.test_failure("Seed did not generate a DSA parameter"); } @@ -887,50 +945,84 @@ [](Test::Result& res) { Botan::BigInt a(0x1234567890ABCDEF); auto enc = a.serialize(); - res.test_eq("BigInt::serialize", enc, Botan::hex_decode("1234567890ABCDEF")); + res.test_bin_eq("BigInt::serialize", enc, "1234567890ABCDEF"); auto enc10 = a.serialize(10); - res.test_eq("BigInt::serialize", enc10, Botan::hex_decode("00001234567890ABCDEF")); + res.test_bin_eq("BigInt::serialize", enc10, "00001234567890ABCDEF"); res.test_throws("BigInt::serialize rejects short output", [&]() { a.serialize(7); }); }), CHECK("BigInt truncated/padded binary serialization", [&](Test::Result& res) { - Botan::BigInt a(0xFEDCBA9876543210); + const 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")); + res.test_bin_eq("BigInt::binary_encode", enc1, "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")); + res.test_bin_eq("BigInt::binary_encode", enc2, "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")); + res.test_bin_eq("BigInt::binary_encode", enc3, "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")); + res.test_bin_eq("BigInt::binary_encode", enc4, "000000FEDCBA9876543210"); - Botan::BigInt b(Botan::hex_decode("FEDCBA9876543210BAADC0FFEE")); + const Botan::BigInt b("0xFEDCBA9876543210BAADC0FFEE"); 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")); + res.test_bin_eq("BigInt::binary_encode", enc5, "000000000000000000000000FEDCBA9876543210BAADC0FFEE"); }), }; } BOTAN_REGISTER_TEST_FN("math", "bignum_auxiliary", test_const_time_left_shift, test_bigint_serialization); +class BigInt_FromRadix_Tests final : public Text_Based_Test { + public: + BigInt_FromRadix_Tests() : Text_Based_Test("bn/from_radix.vec", "Input,Radix,Output") {} + + bool clear_between_callbacks() const override { return false; } + + Test::Result run_one_test(const std::string& header, const VarMap& vars) override { + Test::Result result("BigInt from_radix_digits"); + + const std::string input = vars.get_req_str("Input"); + const size_t radix = vars.get_req_sz("Radix"); + const std::string expected_output = vars.get_req_str("Output"); + + if(header == "Valid") { + const Botan::BigInt n = Botan::BigInt::from_radix_digits(input, radix); + const auto serialized = n.serialize(); + result.test_bin_eq("from_radix_digits", serialized, expected_output); + } else if(header == "Invalid") { + try { + Botan::BigInt::from_radix_digits(input, radix); + result.test_failure("from_radix_digits should have thrown"); + } catch(const std::exception& e) { + const std::string msg = e.what(); + result.test_is_true("exception message contains '" + expected_output + "'", + msg.find(expected_output) != std::string::npos); + } + } else { + result.test_failure("Unknown header " + header); + } + + return result; + } +}; + +BOTAN_REGISTER_TEST("math", "bn_from_radix", BigInt_FromRadix_Tests); + #endif } // namespace diff -Nru botan3-3.7.1+dfsg/src/tests/test_block.cpp botan3-3.12.0+dfsg/src/tests/test_block.cpp --- botan3-3.7.1+dfsg/src/tests/test_block.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_block.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,16 +8,21 @@ #if defined(BOTAN_HAS_BLOCK_CIPHER) #include + #include + #include + #include #include #endif namespace Botan_Tests { +namespace { + #if defined(BOTAN_HAS_BLOCK_CIPHER) class Block_Cipher_Tests final : public Text_Based_Test { public: - Block_Cipher_Tests() : Text_Based_Test("block", "Key,In,Out", "Tweak,Iterations") {} + Block_Cipher_Tests() : Text_Based_Test("block", "Key,In,Out", "Tweak") {} std::vector possible_providers(const std::string& algo) override { return provider_filter(Botan::BlockCipher::providers(algo)); @@ -28,14 +33,9 @@ const std::vector input = vars.get_req_bin("In"); const std::vector expected = vars.get_req_bin("Out"); const std::vector tweak = vars.get_opt_bin("Tweak"); - const size_t iterations = vars.get_opt_sz("Iterations", 1); Test::Result result(algo); - if(iterations > 1 && run_long_tests() == false) { - return result; - } - const std::vector providers = possible_providers(algo); if(providers.empty()) { @@ -52,13 +52,13 @@ } const std::string provider(cipher->provider()); - result.test_is_nonempty("provider", provider); - result.test_eq(provider, cipher->name(), algo); - result.test_gte(provider, cipher->parallelism(), 1); - result.test_gte(provider, cipher->block_size(), 8); - result.test_gte(provider, cipher->parallel_bytes(), cipher->block_size() * cipher->parallelism()); + result.test_str_not_empty("provider", provider); + result.test_str_eq(provider, cipher->name(), algo); + result.test_sz_gte(provider, cipher->parallelism(), 1); + result.test_sz_gte(provider, cipher->block_size(), 8); + result.test_sz_gte(provider, cipher->parallel_bytes(), cipher->block_size() * cipher->parallelism()); - result.test_eq("no key set", cipher->has_keying_material(), false); + result.test_is_false("no key set", cipher->has_keying_material()); // Test that trying to encrypt or decrypt with no key set throws Botan::Invalid_State try { @@ -94,7 +94,7 @@ } cipher->set_key(key); - result.test_eq("key set", cipher->has_keying_material(), true); + result.test_is_true("key set", cipher->has_keying_material()); if(!tweak.empty()) { Botan::Tweakable_Block_Cipher* tbc = dynamic_cast(cipher.get()); @@ -107,57 +107,43 @@ // Test that clone works and does not affect parent object 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()); + result.test_is_true("Clone has different pointer", cipher.get() != clone.get()); + result.test_str_eq("Clone has same name", cipher->name(), clone->name()); clone->set_key(this->rng().random_vec(cipher->maximum_keylength())); // have called set_key on clone: process input values std::vector buf = input; - for(size_t i = 0; i != iterations; ++i) { - cipher->encrypt(buf); - } + cipher->encrypt(buf); - result.test_eq(provider, "encrypt", buf, expected); + result.test_bin_eq(provider + " encrypt", buf, expected); // always decrypt expected ciphertext vs what we produced above buf = expected; - for(size_t i = 0; i != iterations; ++i) { - cipher->decrypt(buf); - } + cipher->decrypt(buf); - result.test_eq(provider, "decrypt", buf, input); + result.test_bin_eq(provider + " decrypt", buf, input); // Now test misaligned buffers const size_t blocks = input.size() / cipher->block_size(); buf.resize(input.size() + 1); Botan::copy_mem(buf.data() + 1, input.data(), input.size()); - for(size_t i = 0; i != iterations; ++i) { - cipher->encrypt_n(buf.data() + 1, buf.data() + 1, blocks); - } + cipher->encrypt_n(buf.data() + 1, buf.data() + 1, blocks); - result.test_eq(provider.c_str(), - "encrypt misaligned", - buf.data() + 1, - buf.size() - 1, - expected.data(), - expected.size()); + result.test_bin_eq(provider + " encrypt misaligned", {buf.data() + 1, buf.size() - 1}, expected); // always decrypt expected ciphertext vs what we produced above Botan::copy_mem(buf.data() + 1, expected.data(), expected.size()); - for(size_t i = 0; i != iterations; ++i) { - cipher->decrypt_n(buf.data() + 1, buf.data() + 1, blocks); - } + cipher->decrypt_n(buf.data() + 1, buf.data() + 1, blocks); - result.test_eq( - provider.c_str(), "decrypt misaligned", buf.data() + 1, buf.size() - 1, input.data(), input.size()); + result.test_bin_eq(provider + " decrypt misaligned", std::span{buf.data() + 1, buf.size() - 1}, input); - result.test_eq("key set", cipher->has_keying_material(), true); + result.test_is_true("key set", cipher->has_keying_material()); cipher->clear(); - result.test_eq("key set", cipher->has_keying_material(), false); + result.test_is_false("key set", cipher->has_keying_material()); try { std::vector block(cipher->block_size()); @@ -182,6 +168,95 @@ BOTAN_REGISTER_SERIALIZED_SMOKE_TEST("block", "block_ciphers", Block_Cipher_Tests); +class BlockCipher_ParallelOp_Test final : public Test { + public: + std::vector run() override { + /* + * This is somewhat intentionally not a list of all ciphers + * but rather those that are or are likely in the future to be + * implemented using some kind of bitslicing or SIMD technique. + */ + const std::vector ciphers = {"AES-128", + "AES-192", + "AES-256", + "ARIA-128", + "ARIA-256", + "Camellia-128", + "Camellia-192", + "Camellia-256", + "DES", + "TripleDES", + "IDEA", + "Noekeon", + "SEED", + "Serpent", + "SHACAL2", + "SM4"}; + + std::vector results; + results.reserve(ciphers.size()); + for(const auto& cipher : ciphers) { + results.push_back(test_parallel_op(cipher)); + } + return results; + } + + private: + Test::Result test_parallel_op(const std::string& cipher_name) const { + Test::Result result(cipher_name + " parallel operation"); + + auto cipher = Botan::BlockCipher::create(cipher_name); + if(cipher == nullptr) { + result.note_missing(cipher_name); + return result; + } + + result.test_sz_gte("Has non-zero parallelism", cipher->parallelism(), 1); + + const size_t block_size = cipher->block_size(); + + // Chosen to maximize coverage of handling of tail blocks + constexpr size_t test_blocks = 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1; + + std::vector input(block_size * test_blocks); + rng().randomize(input); + + cipher->set_key(rng().random_vec(cipher->maximum_keylength())); + + // Encrypt the message one block at a time + std::vector enc_1by1(input); + + for(size_t i = 0; i != test_blocks; ++i) { + cipher->encrypt(&enc_1by1[i * block_size], &enc_1by1[i * block_size]); + } + + // Encrypt the message with all blocks potentially in parallel + std::vector enc_all(input); + + cipher->encrypt(enc_all); + + result.test_bin_eq("Same output no matter how encrypted", enc_all, enc_1by1); + + // Decrypt the message one block at a time + for(size_t i = 0; i != test_blocks; ++i) { + cipher->decrypt(&enc_1by1[i * block_size], &enc_1by1[i * block_size]); + } + + // Decrypt the message with all blocks potentially in parallel + cipher->decrypt(enc_all); + + result.test_bin_eq("Same output no matter how decrypted", enc_all, enc_1by1); + result.test_bin_eq("Original input recovered in 1-by-1", enc_1by1, input); + result.test_bin_eq("Original input recovered in parallel processing", enc_all, input); + + return result; + } +}; + +BOTAN_REGISTER_TEST("block", "bc_parop", BlockCipher_ParallelOp_Test); + #endif +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_blowfish.cpp botan3-3.12.0+dfsg/src/tests/test_blowfish.cpp --- botan3-3.7.1+dfsg/src/tests/test_blowfish.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_blowfish.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,6 +12,8 @@ namespace Botan_Tests { +namespace { + class Blowfish_Salted_Tests final : public Text_Based_Test { public: Blowfish_Salted_Tests() : Text_Based_Test("salted_blowfish.vec", "Key,Salt,Out") {} @@ -30,7 +32,7 @@ std::vector block(8); blowfish.encrypt(block); - result.test_eq("Expected output", block, expected); + result.test_bin_eq("Expected output", block, expected); return result; } @@ -38,6 +40,8 @@ BOTAN_REGISTER_TEST("block", "blowfish_salted", Blowfish_Salted_Tests); +} // namespace + } // namespace Botan_Tests #endif diff -Nru botan3-3.7.1+dfsg/src/tests/test_bufcomp.cpp botan3-3.12.0+dfsg/src/tests/test_bufcomp.cpp --- botan3-3.7.1+dfsg/src/tests/test_bufcomp.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_bufcomp.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,11 +9,11 @@ #include "tests.h" #include -#include #include #include #include +#include #include #include @@ -23,17 +23,17 @@ class Test_Buf_Comp final : public Botan::Buffered_Computation { public: - Test_Buf_Comp(Test::Result& res) : m_result(res), m_counter(0) {} + explicit Test_Buf_Comp(Test::Result& res) : m_result(res), m_counter(0) {} size_t output_length() const override { return sizeof(m_counter); } void add_data(std::span input) override { - if(m_result.test_eq("input length as expected", input.size(), size_t(5))) { - m_result.confirm("input[0] == 'A'", input[0] == 'A'); - m_result.confirm("input[0] == 'B'", input[1] == 'B'); - m_result.confirm("input[0] == 'C'", input[2] == 'C'); - m_result.confirm("input[0] == 'D'", input[3] == 'D'); - m_result.confirm("input[0] == 'E'", input[4] == 'E'); + if(m_result.test_sz_eq("input length as expected", input.size(), size_t(5))) { + m_result.test_is_true("input[0] == 'A'", input[0] == 'A'); + m_result.test_is_true("input[0] == 'B'", input[1] == 'B'); + m_result.test_is_true("input[0] == 'C'", input[2] == 'C'); + m_result.test_is_true("input[0] == 'D'", input[3] == 'D'); + m_result.test_is_true("input[0] == 'E'", input[4] == 'E'); } ++m_counter; @@ -52,16 +52,16 @@ }; void check(Test::Result& result, std::span produced, size_t expected) { - uint8_t expected_bytes[sizeof(size_t)]; - std::memcpy(expected_bytes, &expected, sizeof(expected)); - result.test_eq("", "result is correct", produced.data(), produced.size(), expected_bytes, sizeof(expected_bytes)); + std::array expected_bytes{}; + std::memcpy(&expected_bytes[0], &expected, sizeof(expected)); // NOLINT + result.test_bin_eq("expected result", produced, expected_bytes); } using TestStdVector = Botan::Strong, struct TestStdVector_>; using TestSecureVector = Botan::Strong, struct TestSecureVector_>; Test::Result test_buffered_computation_convenience_api() { - // This is mainly to test compilability of the various container + // This is mainly to test compatibility of the various container // types as in and out parameters. Hence, we refrain from checking // the 'final' output everywhere. Test::Result result("Convenience API of Buffered_Computation"); @@ -75,7 +75,7 @@ Botan::secure_vector out_sv; std::vector out_vec; - std::array out_arr; + std::array out_arr{}; TestSecureVector out_strong_type; // update with basic string-ish types @@ -105,7 +105,7 @@ t.final(out_vec); out_vec.resize(t.output_length() * 2); t.final(out_vec); - result.test_int_eq("out vector is resized", out_vec.size(), t.output_length()); + result.test_sz_eq("out vector is resized", out_vec.size(), t.output_length()); check(result, out_vec, 6); diff -Nru botan3-3.7.1+dfsg/src/tests/test_certstor.cpp botan3-3.12.0+dfsg/src/tests/test_certstor.cpp --- botan3-3.7.1+dfsg/src/tests/test_certstor.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_certstor.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -15,6 +15,7 @@ #if defined(BOTAN_HAS_CERTSTOR_SQLITE3) #include + #include #include #endif #endif @@ -83,10 +84,10 @@ return result; } - result.test_eq("Got wrong certificate", cert.fingerprint(), w_keyid->fingerprint()); + result.test_str_eq("Got wrong certificate", cert.fingerprint(), w_keyid->fingerprint()); if(priv) { - result.test_eq("Got wrong private key", key.private_key_bits(), priv->private_key_bits()); + result.test_bin_eq("Got wrong private key", key.private_key_bits(), priv->private_key_bits()); const auto rev_certs = store.find_certs_for_key(*priv); @@ -98,22 +99,22 @@ return c.fingerprint() == cert.fingerprint(); }); - result.test_eq("Got wrong/no certificate", found, true); + result.test_is_true("Got wrong/no certificate", found); } } if(certsandkeys[4] != certandkey && certsandkeys[5] != certandkey) { - result.test_eq("Got wrong certificate", cert.fingerprint(), wo_keyid->fingerprint()); + result.test_str_eq("Got wrong certificate", cert.fingerprint(), wo_keyid->fingerprint()); } - result.test_eq("Can't remove certificate", store.remove_cert(cert), true); - result.test_eq("Can't remove certificate", !store.find_cert(cert.subject_dn(), cert.subject_key_id()), true); + result.test_is_true("Can't remove certificate", store.remove_cert(cert)); + result.test_is_true("Can't remove certificate", !store.find_cert(cert.subject_dn(), cert.subject_key_id())); if(priv) { store.remove_key(key); } - result.test_eq("Can't remove key", !store.find_key(cert), true); + result.test_is_true("Can't remove key", !store.find_key(cert)); } return result; @@ -142,15 +143,13 @@ { const auto crls = store.generate_crls(); - result.test_eq("Can't revoke certificate", crls.size(), 2); - result.test_eq( + result.test_sz_eq("Can't revoke certificate", crls.size(), 2); + result.test_is_true( "Can't revoke certificate", - crls[0].is_revoked(certsandkeys[0].certificate()) ^ crls[1].is_revoked(certsandkeys[0].certificate()), - true); - result.test_eq( + crls[0].is_revoked(certsandkeys[0].certificate()) ^ crls[1].is_revoked(certsandkeys[0].certificate())); + result.test_is_true( "Can't revoke certificate", - crls[0].is_revoked(certsandkeys[3].certificate()) ^ crls[1].is_revoked(certsandkeys[3].certificate()), - true); + crls[0].is_revoked(certsandkeys[3].certificate()) ^ crls[1].is_revoked(certsandkeys[3].certificate())); } store.affirm_cert(certsandkeys[3].certificate()); @@ -158,21 +157,21 @@ { const auto crls = store.generate_crls(); - result.test_eq("Can't revoke certificate, wrong crl size", crls.size(), 1); - result.test_eq( - "Can't revoke certificate, cert 0 not revoked", crls[0].is_revoked(certsandkeys[0].certificate()), true); + result.test_sz_eq("Can't revoke certificate, wrong crl size", crls.size(), 1); + result.test_is_true("Can't revoke certificate, cert 0 not revoked", + crls[0].is_revoked(certsandkeys[0].certificate())); } const auto cert0_crl = store.find_crl_for(certsandkeys[0].certificate()); - result.test_eq("Can't revoke certificate, crl for cert 0", !cert0_crl, false); - result.test_eq("Can't revoke certificate, crl for cert 0 size check", cert0_crl->get_revoked().size(), 1); - result.test_eq( - "Can't revoke certificate, no crl for cert 0", cert0_crl->is_revoked(certsandkeys[0].certificate()), true); + result.test_is_false("Can't revoke certificate, crl for cert 0", !cert0_crl); + result.test_sz_eq("Can't revoke certificate, crl for cert 0 size check", cert0_crl->get_revoked().size(), 1); + result.test_is_true("Can't revoke certificate, no crl for cert 0", + cert0_crl->is_revoked(certsandkeys[0].certificate())); const auto cert3_crl = store.find_crl_for(certsandkeys[3].certificate()); - result.test_eq("Can't revoke certificate, crl for cert 3", !cert3_crl, true); + result.test_is_true("Can't revoke certificate, crl for cert 3", !cert3_crl); return result; } catch(std::exception& e) { @@ -195,16 +194,15 @@ const auto subjects = store.all_subjects(); - result.test_eq("Check subject list length", subjects.size(), 6); + result.test_sz_eq("Check subject list length", subjects.size(), 6); for(const auto& sub : subjects) { const std::string ss = sub.to_string(); - result.test_eq("Check subject " + ss, - certsandkeys[0].subject_dn() == sub || certsandkeys[1].subject_dn() == sub || - certsandkeys[2].subject_dn() == sub || certsandkeys[3].subject_dn() == sub || - certsandkeys[4].subject_dn() == sub || certsandkeys[5].subject_dn() == sub, - true); + result.test_is_true("Check subject " + ss, + certsandkeys[0].subject_dn() == sub || certsandkeys[1].subject_dn() == sub || + certsandkeys[2].subject_dn() == sub || certsandkeys[3].subject_dn() == sub || + certsandkeys[4].subject_dn() == sub || certsandkeys[5].subject_dn() == sub); } return result; } catch(std::exception& e) { @@ -233,13 +231,13 @@ } else { const std::string a_str = a.subject_dn().to_string(); const std::string res_str = res_vec.at(0).subject_dn().to_string(); - result.test_eq("Check subject " + a_str, a_str, res_str); + result.test_str_eq("Check subject " + a_str, a_str, res_str); } } - Botan::X509_Certificate same_dn_1 = + const Botan::X509_Certificate same_dn_1 = Botan::X509_Certificate(Test::data_file("x509/bsi/common_14/common_14_sub_ca.ca.pem.crt")); - Botan::X509_Certificate same_dn_2 = + const Botan::X509_Certificate same_dn_2 = Botan::X509_Certificate(Test::data_file("x509/bsi/common_14/common_14_wrong_sub_ca.ca.pem.crt")); store.insert_cert(same_dn_1); @@ -253,10 +251,10 @@ const std::string cert_dn = same_dn_1.subject_dn().to_string(); const std::string res0_dn = res_vec.at(0).subject_dn().to_string(); - result.test_eq("Check subject " + cert_dn, cert_dn, res0_dn); + result.test_str_eq("Check subject " + cert_dn, cert_dn, res0_dn); const std::string res1_dn = res_vec.at(1).subject_dn().to_string(); - result.test_eq("Check subject " + cert_dn, cert_dn, res1_dn); + result.test_str_eq("Check subject " + cert_dn, cert_dn, res1_dn); } } catch(const std::exception& e) { result.test_failure(e.what()); @@ -267,8 +265,8 @@ #endif -Test::Result test_certstor_find_hash_subject(const std::vector& certsandkeys) { - Test::Result result("Certificate Store - Find by subject hash"); +Test::Result test_certstor_all_finders(const std::vector& certsandkeys) { + Test::Result result("Certificate Store - Test all finders"); try { Botan::Certificate_Store_In_Memory store; @@ -279,15 +277,33 @@ for(const auto& certandkey : certsandkeys) { const auto& cert = certandkey.certificate(); - const auto hash = cert.raw_subject_dn_sha256(); - const auto found = store.find_cert_by_raw_subject_dn_sha256(hash); - if(!found) { - result.test_failure("Can't retrieve certificate " + cert.fingerprint("SHA-1")); - return result; + // find by subject hash + { + const auto& hash = cert.raw_subject_dn_sha256(); + + const auto found = store.find_cert_by_raw_subject_dn_sha256(hash); + if(!found) { + result.test_failure("Can't retrieve certificate " + cert.fingerprint("SHA-1")); + return result; + } + + result.test_bin_eq("Got wrong certificate", hash, found->raw_subject_dn_sha256()); } - result.test_eq("Got wrong certificate", hash, found->raw_subject_dn_sha256()); + // find by issuer dn and serial number + { + const auto& issuer_dn = cert.issuer_dn(); + const auto& serial_number = cert.serial_number(); + + const auto found = store.find_cert_by_issuer_dn_and_serial_number(issuer_dn, serial_number); + if(!found) { + result.test_failure("Can't retrieve certificate " + cert.fingerprint("SHA-1")); + return result; + } + + result.test_bin_eq("Got wrong certificate", serial_number, found->serial_number()); + } } const auto found = store.find_cert_by_raw_subject_dn_sha256(std::vector(32, 0)); @@ -312,14 +328,14 @@ try { result.test_note("load certs from dir: " + test_dir_bundled); // Certificate_Store_In_Memory constructor loads every cert of every files of the dir. - Botan::Certificate_Store_In_Memory store(test_dir_bundled); + const Botan::Certificate_Store_In_Memory store(test_dir_bundled); // X509_Certificate constructor loads only the first certificate found in the file. - Botan::X509_Certificate root_cert(Test::data_file("x509/x509test/root.pem")); - Botan::X509_Certificate valid_cert(Test::data_file("x509/x509test/ValidCert.pem")); - std::vector key_id; - result.confirm("Root cert found", store.find_cert(root_cert.subject_dn(), key_id) != std::nullopt); - result.confirm("ValidCert found", store.find_cert(valid_cert.subject_dn(), key_id) != std::nullopt); + const Botan::X509_Certificate root_cert(Test::data_file("x509/x509test/root.pem")); + const Botan::X509_Certificate valid_cert(Test::data_file("x509/x509test/ValidCert.pem")); + const std::vector key_id; + result.test_is_true("Root cert found", store.find_cert(root_cert.subject_dn(), key_id) != std::nullopt); + result.test_is_true("ValidCert found", store.find_cert(valid_cert.subject_dn(), key_id) != std::nullopt); return result; } catch(std::exception& e) { result.test_failure(e.what()); @@ -350,7 +366,7 @@ const auto test_key = Test::data_file("x509/certstor/" + keypath); Botan::DataSource_Stream key_stream(test_key); - std::shared_ptr private_key = Botan::PKCS8::load_key(key_stream); + const std::shared_ptr private_key = Botan::PKCS8::load_key(key_stream); if(!private_key) { Test::Result result("Certificate Store"); @@ -363,7 +379,7 @@ std::vector results; - results.push_back(test_certstor_find_hash_subject(certsandkeys)); + results.push_back(test_certstor_all_finders(certsandkeys)); results.push_back(test_certstor_load_allcert()); #if defined(BOTAN_HAS_CERTSTOR_SQLITE3) results.push_back(test_certstor_sqlite3_insert_find_remove_test(certsandkeys)); diff -Nru botan3-3.7.1+dfsg/src/tests/test_certstor_flatfile.cpp botan3-3.12.0+dfsg/src/tests/test_certstor_flatfile.cpp --- botan3-3.7.1+dfsg/src/tests/test_certstor_flatfile.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_certstor_flatfile.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,10 +10,8 @@ #if defined(BOTAN_HAS_CERTSTOR_FLATFILE) #include "test_certstor_utils.h" - #include #include - #include - #include + #include namespace Botan_Tests { @@ -32,9 +30,9 @@ try { result.start_timer(); - Botan::Flatfile_Certificate_Store unused(get_valid_ca_bundle_path()); + const Botan::Flatfile_Certificate_Store unused(get_valid_ca_bundle_path()); result.end_timer(); - result.test_gt("found some certificates", unused.all_subjects().size(), 0); + result.test_sz_gt("found some certificates", unused.all_subjects().size(), 0); } catch(std::exception& e) { result.test_failure(e.what()); } @@ -49,21 +47,21 @@ try { result.start_timer(); - Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path()); + const Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path()); auto cert = certstore.find_cert_by_pubkey_sha1(get_key_id()); result.end_timer(); - if(result.test_not_nullopt("found certificate", cert)) { + if(result.test_opt_not_null("found certificate", cert)) { auto cns = cert->subject_dn().get_attribute("CN"); - result.test_int_eq("exactly one CN", cns.size(), 1); - result.test_eq("CN", cns.front(), get_subject_cn()); + result.test_sz_eq("exactly one CN", cns.size(), 1); + result.test_str_eq("CN", cns.front(), get_subject_cn()); } } catch(std::exception& e) { result.test_failure(e.what()); } result.test_throws("on invalid SHA1 hash data", [&] { - Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path()); + const Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path()); certstore.find_cert_by_pubkey_sha1({}); }); @@ -77,14 +75,14 @@ auto dn = get_dn(); result.start_timer(); - Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path()); + const Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path()); auto cert = certstore.find_cert(dn, std::vector()); result.end_timer(); - if(result.test_not_nullopt("found certificate", cert)) { + if(result.test_opt_not_null("found certificate", cert)) { auto cns = cert->subject_dn().get_attribute("CN"); - result.test_int_eq("exactly one CN", cns.size(), 1); - result.test_eq("CN", cns.front(), get_subject_cn()); + result.test_sz_eq("exactly one CN", cns.size(), 1); + result.test_str_eq("CN", cns.front(), get_subject_cn()); } } catch(std::exception& e) { result.test_failure(e.what()); @@ -100,15 +98,15 @@ auto dn = get_utf8_dn(); result.start_timer(); - Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path()); + const Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path()); auto cert = certstore.find_cert(dn, std::vector()); result.end_timer(); - if(result.test_not_nullopt("found certificate", cert)) { + if(result.test_opt_not_null("found certificate", cert)) { auto cns = cert->subject_dn().get_attribute("CN"); - result.test_is_eq("exactly one CN", cns.size(), size_t(1)); - result.test_eq("CN", cns.front(), "D-TRUST Root Class 3 CA 2 EV 2009"); + result.test_sz_eq("exactly one CN", cns.size(), 1); + result.test_str_eq("CN", cns.front(), "D-TRUST Root Class 3 CA 2 EV 2009"); } } catch(std::exception& e) { result.test_failure(e.what()); @@ -124,14 +122,14 @@ auto dn = get_dn(); result.start_timer(); - Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path()); + const Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path()); auto cert = certstore.find_cert(dn, get_key_id()); result.end_timer(); - if(result.test_not_nullopt("found certificate", cert)) { + if(result.test_opt_not_null("found certificate", cert)) { auto cns = cert->subject_dn().get_attribute("CN"); - result.test_int_eq("exactly one CN", cns.size(), 1); - result.test_eq("CN", cns.front(), get_subject_cn()); + result.test_sz_eq("exactly one CN", cns.size(), 1); + result.test_str_eq("CN", cns.front(), get_subject_cn()); } } catch(std::exception& e) { result.test_failure(e.what()); @@ -147,15 +145,15 @@ auto dn = get_dn(); result.start_timer(); - Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path()); + const Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path()); auto certs = certstore.find_all_certs(dn, get_key_id()); result.end_timer(); - if(result.confirm("result not empty", !certs.empty()) && - result.test_eq("exactly one certificate", certs.size(), 1)) { + if(result.test_is_true("result not empty", !certs.empty()) && + result.test_sz_eq("exactly one certificate", certs.size(), 1)) { auto cns = certs.front().subject_dn().get_attribute("CN"); - result.test_int_eq("exactly one CN", cns.size(), 1); - result.test_eq("CN", cns.front(), get_subject_cn()); + result.test_sz_eq("exactly one CN", cns.size(), 1); + result.test_str_eq("CN", cns.front(), get_subject_cn()); } } catch(std::exception& e) { result.test_failure(e.what()); @@ -169,17 +167,17 @@ try { result.start_timer(); - Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path()); + const Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path()); auto subjects = certstore.all_subjects(); result.end_timer(); - if(result.confirm("result not empty", !subjects.empty())) { + if(result.test_is_true("result not empty", !subjects.empty())) { auto dn = get_dn(); auto needle = std::find_if( subjects.cbegin(), subjects.cend(), [=](const Botan::X509_DN& subject) { return subject == dn; }); - if(result.confirm("found expected certificate", needle != subjects.end())) { - result.confirm("expected certificate", *needle == dn); + if(result.test_is_true("found expected certificate", needle != subjects.end())) { + result.test_is_true("expected certificate", *needle == dn); } } } catch(std::exception& e) { @@ -197,16 +195,16 @@ auto kid = get_unknown_key_id(); result.start_timer(); - Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path()); + const Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path()); auto certs = certstore.find_all_certs(dn, kid); auto cert = certstore.find_cert(dn, kid); auto pubk_cert = certstore.find_cert_by_pubkey_sha1(kid); result.end_timer(); - result.confirm("find_all_certs did not find the dummy", certs.empty()); - result.confirm("find_cert did not find the dummy", !cert); - result.confirm("find_cert_by_pubkey_sha1 did not find the dummy", !pubk_cert); + result.test_is_true("find_all_certs did not find the dummy", certs.empty()); + result.test_is_true("find_cert did not find the dummy", !cert); + result.test_is_true("find_cert_by_pubkey_sha1 did not find the dummy", !pubk_cert); } catch(std::exception& e) { result.test_failure(e.what()); } @@ -219,7 +217,7 @@ try { result.start_timer(); - Botan::Flatfile_Certificate_Store certstore(get_ca_bundle_containing_user_cert()); + const Botan::Flatfile_Certificate_Store certstore(get_ca_bundle_containing_user_cert()); result.test_failure("CA bundle with non-CA certs should be rejected"); } catch(Botan::Invalid_Argument&) { result.test_success(); @@ -228,6 +226,28 @@ return result; } +Test::Result find_cert_by_issuer_dn_and_serial_number() { + Test::Result result("Flatfile Certificate Store - Find Certificate by issuer DN and serial number"); + + try { + result.start_timer(); + const Botan::Flatfile_Certificate_Store certstore(get_valid_ca_bundle_path()); + auto cert = certstore.find_cert_by_issuer_dn_and_serial_number(get_dn(), get_serial_number()); + result.end_timer(); + + if(result.test_opt_not_null("found certificate", cert)) { + auto cns = cert->subject_dn().get_attribute("CN"); + result.test_sz_eq("exactly one CN", cns.size(), 1); + result.test_str_eq("CN", cns.front(), get_subject_cn()); + result.test_bin_eq("serial number", cert->serial_number(), get_serial_number()); + } + } catch(std::exception& e) { + result.test_failure(e.what()); + } + + return result; +} + class Certstor_Flatfile_Tests final : public Test { public: std::vector run() override { @@ -242,6 +262,7 @@ results.push_back(find_all_subjects()); results.push_back(no_certificate_matches()); results.push_back(certstore_contains_user_certificate()); + results.push_back(find_cert_by_issuer_dn_and_serial_number()); return results; } diff -Nru botan3-3.7.1+dfsg/src/tests/test_certstor_system.cpp botan3-3.12.0+dfsg/src/tests/test_certstor_system.cpp --- botan3-3.7.1+dfsg/src/tests/test_certstor_system.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_certstor_system.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,10 +10,8 @@ #if defined(BOTAN_HAS_CERTSTOR_SYSTEM) #include "test_certstor_utils.h" - #include #include - #include - #include + #include #include namespace Botan_Tests { @@ -28,10 +26,10 @@ auto cert = certstore.find_cert_by_pubkey_sha1(get_key_id()); result.end_timer(); - if(result.test_not_nullopt("found certificate", cert)) { + if(result.test_opt_not_null("found certificate", cert)) { auto cns = cert->subject_dn().get_attribute("CN"); - result.test_is_eq("exactly one CN", cns.size(), size_t(1)); - result.test_eq("CN", cns.front(), get_subject_cn()); + result.test_sz_eq("exactly one CN", cns.size(), 1); + result.test_str_eq("CN", cns.front(), get_subject_cn()); } } catch(std::exception& e) { result.test_failure(e.what()); @@ -55,10 +53,10 @@ auto cert = certstore.find_cert_by_pubkey_sha1(get_pubkey_sha1_of_cert_with_different_key_id()); result.end_timer(); - if(result.test_not_nullopt("found certificate", cert)) { + if(result.test_opt_not_null("found certificate", cert)) { auto cns = cert->subject_dn().get_attribute("CN"); - result.test_is_eq("exactly one CN", cns.size(), size_t(1)); - result.test_eq("CN", cns.front(), "SecureTrust CA"); + result.test_sz_eq("exactly one CN", cns.size(), 1); + result.test_str_eq("CN", cns.front(), "SecureTrust CA"); } } catch(std::exception& e) { result.test_failure(e.what()); @@ -77,10 +75,10 @@ auto cert = certstore.find_cert(dn, std::vector()); result.end_timer(); - if(result.test_not_nullopt("found certificate", cert)) { + if(result.test_opt_not_null("found certificate", cert)) { auto cns = cert->subject_dn().get_attribute("CN"); - result.test_is_eq("exactly one CN", cns.size(), size_t(1)); - result.test_eq("CN", cns.front(), get_subject_cn()); + result.test_sz_eq("exactly one CN", cns.size(), 1); + result.test_str_eq("CN", cns.front(), get_subject_cn()); } } catch(std::exception& e) { result.test_failure(e.what()); @@ -101,8 +99,8 @@ for(const auto& [cn, dn] : DNs) { if(auto cert = certstore.find_cert(dn, {})) { auto cns = cert->subject_dn().get_attribute("CN"); - result.test_is_eq("exactly one CN", cns.size(), size_t(1)); - result.test_eq("CN", cns.front(), cn); + result.test_sz_eq("exactly one CN", cns.size(), 1); + result.test_str_eq("CN", cns.front(), cn); ++found; } @@ -119,7 +117,7 @@ result.test_failure("Did not find any certificate via an UTF-8 encoded DN"); } - result.test_gte("found at least one certificate", found, 1); + result.test_sz_gte("found at least one certificate", found, 1); } catch(std::exception& e) { result.test_failure(e.what()); } @@ -137,10 +135,10 @@ auto cert = certstore.find_cert(dn, get_key_id()); result.end_timer(); - if(result.test_not_nullopt("found certificate", cert)) { + if(result.test_opt_not_null("found certificate", cert)) { auto cns = cert->subject_dn().get_attribute("CN"); - result.test_is_eq("exactly one CN", cns.size(), size_t(1)); - result.test_eq("CN", cns.front(), get_subject_cn()); + result.test_sz_eq("exactly one CN", cns.size(), 1); + result.test_str_eq("CN", cns.front(), get_subject_cn()); } } catch(std::exception& e) { result.test_failure(e.what()); @@ -159,11 +157,12 @@ auto certs = certstore.find_all_certs(dn, get_key_id()); result.end_timer(); - if(result.confirm("result not empty", !certs.empty()) && - result.test_eq("exactly one certificate", certs.size(), 1)) { + if(result.test_is_true("result not empty", !certs.empty()) && + result.test_sz_eq("exactly one certificate", certs.size(), 1)) { auto cns = certs.front().subject_dn().get_attribute("CN"); - result.test_is_eq("exactly one CN", cns.size(), size_t(1)); - result.test_eq("CN", cns.front(), get_subject_cn()); + result.test_sz_eq("exactly one CN", cns.size(), 1); + result.test_str_eq("CN", cns.front(), get_subject_cn()); + result.test_is_true("returned cert is considered contained", certstore.contains(certs.front())); } } catch(std::exception& e) { result.test_failure(e.what()); @@ -190,10 +189,15 @@ } } - if(result.confirm("result not empty", !certs.empty())) { + // check all returned certs are considered contained + for(const auto& cert : certs) { + result.test_is_true("contains returns true", certstore.contains(cert)); + } + + if(result.test_is_true("result not empty", !certs.empty())) { auto cns = certs.front().subject_dn().get_attribute("CN"); - result.test_gte("at least one CN", cns.size(), size_t(1)); - result.test_eq("CN", cns.front(), get_subject_cn()); + result.test_sz_gte("at least one CN", cns.size(), size_t(1)); + result.test_str_eq("CN", cns.front(), get_subject_cn()); } } catch(std::exception& e) { result.test_failure(e.what()); @@ -210,13 +214,13 @@ auto subjects = certstore.all_subjects(); result.end_timer(); - if(result.confirm("result not empty", !subjects.empty())) { + if(result.test_is_true("result not empty", !subjects.empty())) { auto dn = get_dn(); auto needle = std::find_if( subjects.cbegin(), subjects.cend(), [=](const Botan::X509_DN& subject) { return subject == dn; }); - if(result.confirm("found expected certificate", needle != subjects.end())) { - result.confirm("expected certificate", *needle == dn); + if(result.test_is_true("found expected certificate", needle != subjects.end())) { + result.test_is_true("expected certificate", *needle == dn); } } } catch(std::exception& e) { @@ -226,6 +230,28 @@ return result; } +Test::Result find_cert_by_issuer_dn_and_serial_number(Botan::Certificate_Store& certstore) { + Test::Result result("System Certificate Store - Find Certificate by issuer DN and serial number"); + + try { + result.start_timer(); + auto cert = certstore.find_cert_by_issuer_dn_and_serial_number(get_dn(), get_serial_number()); + result.end_timer(); + + if(result.test_opt_not_null("found certificate", cert)) { + auto cns = cert->subject_dn().get_attribute("CN"); + result.test_sz_eq("exactly one CN", cns.size(), 1); + result.test_str_eq("CN", cns.front(), get_subject_cn()); + result.test_bin_eq("serial number", cert->serial_number(), get_serial_number()); + result.test_is_true("returned cert is considered contained", certstore.contains(cert.value())); + } + } catch(std::exception& e) { + result.test_failure(e.what()); + } + + return result; +} + Test::Result no_certificate_matches(Botan::Certificate_Store& certstore) { Test::Result result("System Certificate Store - can deal with no matches (regression test)"); @@ -239,9 +265,9 @@ auto pubk_cert = certstore.find_cert_by_pubkey_sha1(kid); result.end_timer(); - result.confirm("find_all_certs did not find the dummy", certs.empty()); - result.confirm("find_cert did not find the dummy", !cert); - result.confirm("find_cert_by_pubkey_sha1 did not find the dummy", !pubk_cert); + result.test_is_true("find_all_certs did not find the dummy", certs.empty()); + result.test_is_true("find_cert did not find the dummy", !cert); + result.test_is_true("find_cert_by_pubkey_sha1 did not find the dummy", !pubk_cert); } catch(std::exception& e) { result.test_failure(e.what()); } @@ -262,11 +288,16 @@ auto cert = certstore.find_cert(dn, std::vector()); result.end_timer(); - if(result.confirm("find_all_certs did find the skewed DN", !certs.empty()) && - result.confirm("find_cert did find the skewed DN", cert.has_value())) { - result.test_eq( + if(result.test_is_true("find_all_certs did find the skewed DN", !certs.empty()) && + result.test_is_true("find_cert did find the skewed DN", cert.has_value())) { + result.test_str_eq( "it is the correct cert", certs.front().subject_dn().get_first_attribute("CN"), get_subject_cn()); - result.test_eq("it is the correct cert", cert->subject_dn().get_first_attribute("CN"), get_subject_cn()); + result.test_str_eq("it is the correct cert", cert->subject_dn().get_first_attribute("CN"), get_subject_cn()); + } + + // check all returned certs are considered contained + for(const auto& ret : certs) { + result.test_is_true("contains returns true", certstore.contains(ret)); } } catch(std::exception& e) { result.test_failure(e.what()); @@ -288,8 +319,7 @@ open_result.start_timer(); system = std::make_unique(); open_result.end_timer(); - } catch(Botan::Not_Implemented& e) { - BOTAN_UNUSED(e); + } catch(Botan::Not_Implemented&) { open_result.test_note("Skipping due to not available in current build"); return {open_result}; } catch(std::exception& e) { @@ -311,6 +341,7 @@ results.push_back(find_all_subjects(*system)); results.push_back(no_certificate_matches(*system)); results.push_back(find_cert_by_utf8_subject_dn(*system)); + results.push_back(find_cert_by_issuer_dn_and_serial_number(*system)); #if defined(BOTAN_HAS_CERTSTOR_MACOS) results.push_back(certificate_matching_with_dn_normalization(*system)); #endif diff -Nru botan3-3.7.1+dfsg/src/tests/test_certstor_utils.cpp botan3-3.12.0+dfsg/src/tests/test_certstor_utils.cpp --- botan3-3.7.1+dfsg/src/tests/test_certstor_utils.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_certstor_utils.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,8 +9,10 @@ #if defined(BOTAN_HAS_X509_CERTIFICATES) + #include #include #include + #include namespace Botan_Tests { @@ -86,6 +88,11 @@ return "ISRG Root X1"; } +std::vector get_serial_number() { + // serial number of "ISRG Root X1" + return Botan::hex_decode("8210CFB0D240E3594463E0BB63828B00"); +} + std::vector get_pubkey_sha1_of_cert_with_different_key_id() { // see https://github.com/randombit/botan/issues/2779 for details // diff -Nru botan3-3.7.1+dfsg/src/tests/test_certstor_utils.h botan3-3.12.0+dfsg/src/tests/test_certstor_utils.h --- botan3-3.7.1+dfsg/src/tests/test_certstor_utils.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_certstor_utils.h 2026-05-07 01:38:28.000000000 +0000 @@ -28,6 +28,7 @@ std::vector get_key_id(); std::string get_subject_cn(); +std::vector get_serial_number(); std::vector get_pubkey_sha1_of_cert_with_different_key_id(); Botan::X509_DN get_dn_of_cert_with_different_key_id(); diff -Nru botan3-3.7.1+dfsg/src/tests/test_cmce.cpp botan3-3.12.0+dfsg/src/tests/test_cmce.cpp --- botan3-3.7.1+dfsg/src/tests/test_cmce.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_cmce.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,24 +7,23 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include "test_pubkey.h" -#include "test_pubkey_pqc.h" -#include "test_rng.h" #include "tests.h" #if defined(BOTAN_HAS_CLASSICMCELIECE) + #include "test_arb_eq.h" + #include "test_pubkey.h" + #include "test_pubkey_pqc.h" + #include "test_rng.h" #include #include + #include #include #include - #include - #include - #include #include - #include #include #include + #include namespace Botan_Tests { @@ -37,9 +36,10 @@ Botan::load_le(coef.data(), bytes.data(), ring.degree()); std::vector coeff_vec_gf; - std::transform(coef.begin(), coef.end(), std::back_inserter(coeff_vec_gf), [&](auto& coeff) { - return Botan::Classic_McEliece_GF(Botan::CmceGfElem(coeff), ring.poly_f()); - }); + coeff_vec_gf.reserve(coef.size()); + for(const auto& coeff : coef) { + coeff_vec_gf.push_back(Botan::Classic_McEliece_GF(Botan::CmceGfElem(coeff), ring.poly_f())); + } return Botan::Classic_McEliece_Polynomial(coeff_vec_gf); } @@ -86,7 +86,6 @@ auto to_test = instances_to_test(); return std::find(to_test.begin(), to_test.end(), params.parameter_set()) == to_test.end(); } -} // namespace class CMCE_Utility_Tests final : public Test { public: @@ -103,13 +102,13 @@ "543e2791fd98dbc1" // first 8 bytes "d332a7c40776ca01"); // last 8 bytes - size_t byte_length = + const size_t byte_length = (params.n() + params.sigma2() * params.q() + params.sigma1() * params.t() + params.ell()) / 8; auto rand = params.prg(seed)->output_stdvec(byte_length); rand.erase(rand.begin() + 8, rand.end() - 8); - result.test_is_eq("Seed expansion", rand, exp_first_and_last_bytes); + result.test_bin_eq("Seed expansion", rand, exp_first_and_last_bytes); return result; } @@ -132,8 +131,8 @@ params.poly_f()); auto g = params.poly_ring().compute_minimal_polynomial(random_bits); - result.confirm("Minimize polynomial successful", g.has_value()); - result.test_is_eq("Minimize polynomial", g.value().coef(), exp_g.coef()); + result.test_is_true("Minimize polynomial successful", g.has_value()); + result.test_is_true("Minimize polynomial", g.value().coef() == exp_g.coef()); return result; } @@ -146,7 +145,7 @@ auto v = params.gf(Botan::CmceGfElem(42)); auto v_inv = v.inv(); - result.test_is_eq("Control bits creation", (v * v_inv).elem(), Botan::CmceGfElem(1)); + test_arb_eq(result, "Control bits creation", (v * v_inv).elem(), Botan::CmceGfElem(1)); return result; } @@ -178,7 +177,7 @@ field); auto mul = field.multiply(val1, val2); // val1 * val2; - result.test_is_eq("GF multiplication", mul.coef(), exp_mul.coef()); + result.test_is_true("GF multiplication", mul.coef() == exp_mul.coef()); return result; } @@ -233,9 +232,9 @@ auto params = Botan::Classic_McEliece_Parameters::create(params_str); - const auto kat_seed = Botan::lock(vars.get_req_bin("seed")); + const auto kat_seed = vars.get_req_bin("seed"); const auto ct_invalid = vars.get_req_bin("ct_invalid"); - const auto ref_ss_invalid = Botan::lock(vars.get_req_bin("ss_invalid")); + const auto ref_ss_invalid = vars.get_req_bin("ss_invalid"); const auto test_rng = std::make_unique(kat_seed); @@ -245,17 +244,17 @@ auto dec = Botan::PK_KEM_Decryptor(*private_key, *test_rng, "Raw"); auto decaps_ct_invalid = dec.decrypt(ct_invalid); - result.test_is_eq("Decaps an invalid encapsulated key", decaps_ct_invalid, ref_ss_invalid); + result.test_bin_eq("Decaps an invalid encapsulated key", decaps_ct_invalid, ref_ss_invalid); if(params.is_pc()) { // For pc variants, additionally check the plaintext confirmation (pc) logic by // flipping a bit in the second part of the ciphertext (C_1 in pc). In this case // C_0 is decoded correctly, but pc will change the shared secret, since C_1' != C_1. const auto ct_invalid_c1 = vars.get_opt_bin("ct_invalid_c1"); - const auto ref_ss_invalid_c1 = Botan::lock(vars.get_opt_bin("ss_invalid_c1")); + const auto ref_ss_invalid_c1 = vars.get_opt_bin("ss_invalid_c1"); auto decaps_ct_invalid_c1 = dec.decrypt(ct_invalid_c1); - result.test_is_eq("Decaps with invalid C_1 in pc", decaps_ct_invalid_c1, ref_ss_invalid_c1); + result.test_bin_eq("Decaps with invalid C_1 in pc", decaps_ct_invalid_c1, ref_ss_invalid_c1); } return result; @@ -266,13 +265,10 @@ class CMCE_Generic_Keygen_Tests final : public PK_Key_Generation_Test { public: std::vector keygen_params() const override { - auto to_test = get_test_instances_min(); - std::vector res; - std::transform(to_test.begin(), to_test.end(), std::back_inserter(res), [](auto& param_set) { - return param_set.to_string(); - }); - + for(const auto& param_set : get_test_instances_min()) { + res.push_back(param_set.to_string()); + } return res; } @@ -297,7 +293,9 @@ bool is_available(const std::string& alg_name) const final { return !skip_cmce_test(alg_name); } - std::vector map_value(const std::string&, std::span value, VarType var_type) const final { + std::vector map_value(const std::string& /*params*/, + std::span value, + VarType var_type) const final { if(var_type == VarType::Ciphertext || var_type == VarType::SharedSecret) { return {value.begin(), value.end()}; } @@ -305,14 +303,14 @@ return hash->process>(value); } - Fixed_Output_RNG rng_for_keygen(const std::string&, Botan::RandomNumberGenerator& rng) const final { + Fixed_Output_RNG rng_for_keygen(const std::string& /*params*/, Botan::RandomNumberGenerator& rng) const final { const auto seed = rng.random_vec(Botan::Classic_McEliece_Parameters::seed_len()); return Fixed_Output_RNG(seed); } Fixed_Output_RNG rng_for_encapsulation(const std::string& alg_name, Botan::RandomNumberGenerator& rng) const final { - // There is no way to tell exacly how much randomness is + // There is no way to tell exactly how much randomness is // needed for encapsulation (rejection sampling) // For testing we use a number that fits for all test cases auto params = get_params(alg_name); @@ -328,7 +326,9 @@ return Fixed_Output_RNG(rand_buffer); } - void inspect_rng_after_encaps(const std::string&, const Fixed_Output_RNG&, Test::Result&) const final { + void inspect_rng_after_encaps(const std::string& /*params*/, + const Fixed_Output_RNG& /*rng*/, + Test::Result& /*result*/) const final { // Encaps uses any number of random bytes, so we cannot check the RNG } }; @@ -340,6 +340,8 @@ BOTAN_REGISTER_TEST("cmce", "cmce_invalid", CMCE_Invalid_Test); #endif +} // namespace + } // namespace Botan_Tests #endif // BOTAN_HAS_CLASSICMCELIECE diff -Nru botan3-3.7.1+dfsg/src/tests/test_codec.cpp botan3-3.12.0+dfsg/src/tests/test_codec.cpp --- botan3-3.7.1+dfsg/src/tests/test_codec.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_codec.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,6 +6,8 @@ #include "tests.h" +#include + #if defined(BOTAN_HAS_BASE64_CODEC) #include #endif @@ -20,6 +22,8 @@ namespace Botan_Tests { +namespace { + #if defined(BOTAN_HAS_BASE32_CODEC) class Base32_Tests final : public Text_Based_Test { @@ -35,8 +39,8 @@ try { if(is_valid) { const std::vector binary = vars.get_req_bin("Binary"); - result.test_eq("base32 decoding", Botan::base32_decode(base32), binary); - result.test_eq("base32 encoding", Botan::base32_encode(binary), base32); + result.test_bin_eq("base32 decoding", Botan::base32_decode(base32), binary); + result.test_str_eq("base32 encoding", Botan::base32_encode(binary), base32); } else { auto res = Botan::base32_decode(base32); result.test_failure("decoded invalid base32 to " + Botan::hex_encode(res)); @@ -56,7 +60,7 @@ Test::Result result("Base32"); const std::string valid_b32 = "MY======"; - for(char ws_char : {' ', '\t', '\r', '\n'}) { + for(const char ws_char : {' ', '\t', '\r', '\n'}) { for(size_t i = 0; i <= valid_b32.size(); ++i) { std::string b32_ws = valid_b32; b32_ws.insert(i, 1, ws_char); @@ -66,7 +70,7 @@ } catch(std::exception&) {} try { - result.test_eq("base32 decoding with whitespace", Botan::base32_decode(b32_ws, true), "66"); + result.test_bin_eq("base32 decoding with whitespace", Botan::base32_decode(b32_ws, true), "66"); } catch(std::exception& e) { result.test_failure(b32_ws, e.what()); } @@ -96,8 +100,8 @@ try { if(is_valid) { const std::vector binary = vars.get_req_bin("Binary"); - result.test_eq("base58 decoding", Botan::base58_decode(base58), binary); - result.test_eq("base58 encoding", Botan::base58_encode(binary), base58); + result.test_bin_eq("base58 decoding", Botan::base58_decode(base58), binary); + result.test_str_eq("base58 encoding", Botan::base58_encode(binary), base58); } else { auto res = Botan::base58_decode(base58); result.test_failure("decoded invalid base58 to " + Botan::hex_encode(res)); @@ -129,8 +133,8 @@ try { if(is_valid) { const std::vector binary = vars.get_req_bin("Binary"); - result.test_eq("base58 decoding", Botan::base58_check_decode(base58), binary); - result.test_eq("base58 encoding", Botan::base58_check_encode(binary), base58); + result.test_bin_eq("base58 decoding", Botan::base58_check_decode(base58), binary); + result.test_str_eq("base58 encoding", Botan::base58_check_encode(binary), base58); } else { auto res = Botan::base58_check_decode(base58); result.test_failure("decoded invalid base58c to " + Botan::hex_encode(res)); @@ -166,8 +170,8 @@ try { if(is_valid) { const std::vector binary = vars.get_req_bin("Binary"); - result.test_eq("base64 decoding", Botan::base64_decode(base64), binary); - result.test_eq("base64 encoding", Botan::base64_encode(binary), base64); + result.test_bin_eq("base64 decoding", Botan::base64_decode(base64), binary); + result.test_str_eq("base64 encoding", Botan::base64_encode(binary), base64); } else { auto res = Botan::base64_decode(base64); result.test_failure("decoded invalid base64 to " + Botan::hex_encode(res)); @@ -187,7 +191,7 @@ Test::Result result("Base64"); const std::string valid_b64 = "Zg=="; - for(char ws_char : {' ', '\t', '\r', '\n'}) { + for(const char ws_char : {' ', '\t', '\r', '\n'}) { for(size_t i = 0; i <= valid_b64.size(); ++i) { std::string b64_ws = valid_b64; b64_ws.insert(i, 1, ws_char); @@ -197,7 +201,7 @@ } catch(std::exception&) {} try { - result.test_eq("base64 decoding with whitespace", Botan::base64_decode(b64_ws, true), "66"); + result.test_bin_eq("base64 decoding with whitespace", Botan::base64_decode(b64_ws, true), "66"); } catch(std::exception& e) { result.test_failure(b64_ws, e.what()); } @@ -212,4 +216,6 @@ #endif +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_compression.cpp botan3-3.12.0+dfsg/src/tests/test_compression.cpp --- botan3-3.7.1+dfsg/src/tests/test_compression.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_compression.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,8 @@ #if defined(BOTAN_HAS_COMPRESSION) #include + #include + #include #endif namespace Botan_Tests { @@ -58,7 +60,7 @@ std::vector results; const size_t text_len = std::strlen(COMPRESSION_TEST_TEXT); - for(std::string algo : {"zlib", "deflate", "gzip", "bz2", "lzma"}) { + for(const std::string algo : {"zlib", "deflate", "gzip", "bz2", "lzma"}) { try { Test::Result result(algo + " compression"); @@ -72,7 +74,7 @@ continue; } - result.test_ne("Not the same name", c->name(), d->name()); + result.test_str_ne("Not the same name", c->name(), d->name()); const Botan::secure_vector empty; const Botan::secure_vector all_zeros(text_len, 0); @@ -93,20 +95,21 @@ const size_t c1_s = run_compression(result, 1, *c, *d, short_text); const size_t c9_s = run_compression(result, 9, *c, *d, short_text); - 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_sz_gte("Empty input L1 compresses to non-empty output", c1_e, 1); + result.test_sz_gte("Empty input L9 compresses to non-empty output", c9_e, 1); // 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_sz_gte("Level 9 compresses empty at least as well as level 1", c1_e + (c1_e / 10), c9_e); + result.test_sz_gte("Level 9 compresses zeros at least as well as level 1", c1_z + (c1_z / 10), c9_z); + result.test_sz_gte("Level 9 compresses random at least as well as level 1", c1_r + (c1_r / 10), c9_r); + result.test_sz_gte("Level 9 compresses text at least as well as level 1", c1_t + (c1_t / 10), c9_t); + result.test_sz_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); + result.test_sz_lt("Zeros compresses much better than text", c1_z / 8, c1_t); + result.test_sz_lt("Text compresses much better than random", c1_t / 2, c1_r); result.end_timer(); @@ -128,7 +131,7 @@ const Botan::secure_vector& msg) { Botan::secure_vector compressed(2 * msg.size()); - for(bool with_flush : {true, false}) { + for(const bool with_flush : {true, false}) { try { compressed = msg; @@ -154,7 +157,7 @@ decompressed += final_outputs; - result.test_eq("compression round tripped", msg, decompressed); + result.test_bin_eq("compression round tripped", msg, decompressed); } catch(Botan::Exception& e) { result.test_failure(e.what()); } @@ -166,12 +169,88 @@ BOTAN_REGISTER_TEST("compression", "compression_tests", Compression_Tests); +class Concatenated_Compression_Tests final : public Test { + public: + std::vector run() override { + std::vector results; + + for(const std::string algo : {"zlib", "deflate", "gzip", "bz2", "lzma"}) { + try { + Test::Result result(algo + " concatenated decompression"); + + auto c = Botan::Compression_Algorithm::create(algo); + auto d = Botan::Decompression_Algorithm::create(algo); + + if(!c || !d) { + result.note_missing(algo); + continue; + } + + const Botan::secure_vector msg1 = {'H', 'e', 'l', 'l', 'o'}; + const Botan::secure_vector msg2 = {'W', 'o', 'r', 'l', 'd'}; + + // Compress two messages independently + auto compress = [&](const Botan::secure_vector& msg) { + Botan::secure_vector buf = msg; + c->start(6); + c->update(buf, 0, false); + Botan::secure_vector final_bits; + c->finish(final_bits); + buf += final_bits; + return buf; + }; + + const auto c1 = compress(msg1); + const auto c2 = compress(msg2); + + // Concatenate the two compressed streams + Botan::secure_vector concatenated = c1; + concatenated += c2; + + // Decompress in a single update() call + Botan::secure_vector decompressed = concatenated; + d->start(); + d->update(decompressed); + Botan::secure_vector final_outputs; + d->finish(final_outputs); + decompressed += final_outputs; + + Botan::secure_vector expected = msg1; + expected += msg2; + result.test_bin_eq("concatenated streams decompressed correctly", expected, decompressed); + + // Decompress feeding one byte at a time + decompressed.clear(); + d->start(); + for(const auto byte : concatenated) { + Botan::secure_vector buf = {byte}; + d->update(buf); + decompressed += buf; + } + final_outputs.clear(); + d->finish(final_outputs); + decompressed += final_outputs; + + result.test_bin_eq("byte-at-a-time concatenated streams", expected, decompressed); + + results.emplace_back(result); + } catch(std::exception& e) { + results.emplace_back(Test::Result::Failure("testing " + algo, e.what())); + } + } + + return results; + } +}; + +BOTAN_REGISTER_TEST("compression", "concat_compression_tests", Concatenated_Compression_Tests); + class CompressionCreate_Tests final : public Test { public: std::vector run() override { std::vector results; - for(std::string algo : {"zlib", "deflate", "gzip", "bz2", "lzma"}) { + for(const std::string algo : {"zlib", "deflate", "gzip", "bz2", "lzma"}) { try { Test::Result result(algo + " create compression"); @@ -182,7 +261,7 @@ result.note_missing(algo); continue; } - result.test_ne("Not the same name after create", c1->name(), d1->name()); + result.test_str_ne("Not the same name after create", c1->name(), d1->name()); auto c2 = Botan::Compression_Algorithm::create_or_throw(algo); auto d2 = Botan::Decompression_Algorithm::create_or_throw(algo); @@ -191,7 +270,7 @@ result.note_missing(algo); continue; } - result.test_ne("Not the same name after create_or_throw", c2->name(), d2->name()); + result.test_str_ne("Not the same name after create_or_throw", c2->name(), d2->name()); results.emplace_back(result); } catch(std::exception& e) { diff -Nru botan3-3.7.1+dfsg/src/tests/test_concurrent_pk.cpp botan3-3.12.0+dfsg/src/tests/test_concurrent_pk.cpp --- botan3-3.7.1+dfsg/src/tests/test_concurrent_pk.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_concurrent_pk.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,516 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "tests.h" + +#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) && defined(BOTAN_TARGET_OS_HAS_THREADS) + #include + #include + #include + #include + #include + #include +#endif + +namespace Botan_Tests { + +#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) && defined(BOTAN_TARGET_OS_HAS_THREADS) + +/* +* Test that public key operations (signing, verification, encryption, decryption, KEM, key +* agreement) with a shared key from multiple threads produce correct results without racing. +* +* TODO: Add concurrent test for ECIES handling +*/ + +namespace { + +constexpr size_t ConcurrentThreads = 10; // arbitrary + +class ConcurrentPkTestCase { + public: + ConcurrentPkTestCase(std::string_view pk_algo, std::string_view keygen_params, std::string_view op_params = "") : + m_pk_algo(pk_algo), m_keygen_params(keygen_params), m_op_params(op_params) {} + + const std::string& algo_name() const { return m_pk_algo; } + + const std::string& op_params() const { return m_op_params; } + + Test::Result result(std::string_view operation) const { + std::ostringstream name; + name << "Concurrent " << m_pk_algo; + if(!m_keygen_params.empty()) { + name << " " << m_keygen_params; + } + if(!m_op_params.empty()) { + name << " " << m_op_params; + } + name << " " << operation; + + return Test::Result(name.str()); + } + + Test::Result skip_missing(std::string_view operation) const { + auto result = this->result(operation); + result.test_note("Skipping due to missing algorithm", this->algo_name()); + return result; + } + + std::unique_ptr try_create_key(Botan::RandomNumberGenerator& rng) const { + try { + return Botan::create_private_key(m_pk_algo, rng, m_keygen_params); + } catch(Botan::Lookup_Error&) { + return nullptr; + } catch(Botan::Not_Implemented&) { + return nullptr; + } + } + + private: + std::string m_pk_algo; + std::string m_keygen_params; + std::string m_op_params; +}; + +Test::Result test_concurrent_signing(const ConcurrentPkTestCase& tc, + const Botan::Private_Key& privkey, + const Botan::Public_Key& pubkey) { + auto result = tc.result("signing"); + auto rng = Test::new_rng(result.who()); + const auto test_message = rng->random_vec(32); + + const auto operations_remaining_at_start = privkey.remaining_operations(); + + std::vector>> futures; + futures.reserve(ConcurrentThreads); + + for(size_t i = 0; i != ConcurrentThreads; ++i) { + futures.push_back(std::async(std::launch::async, [&, i]() -> std::vector { + auto thread_rng = Test::new_rng(Botan::fmt("{} thread {}", result.who(), i)); + Botan::PK_Signer signer(privkey, *thread_rng, tc.op_params()); + return signer.sign_message(test_message, *thread_rng); + })); + } + + Botan::PK_Verifier verifier(pubkey, tc.op_params()); + + for(size_t i = 0; i != ConcurrentThreads; ++i) { + try { + const auto signature = futures[i].get(); + + if(signature.empty()) { + result.test_failure(Botan::fmt("Thread {} produced empty signature", i)); + } else { + const bool valid = verifier.verify_message(test_message, signature); + result.test_is_true(Botan::fmt("Thread {} signature is valid", i), valid); + } + } catch(std::exception& e) { + result.test_failure(Botan::fmt("Thread {} failed: {}", i, e.what())); + } + } + + if(operations_remaining_at_start.has_value()) { + result.test_is_true("Private key should be stateful", privkey.stateful_operation()); + const auto left_at_end = privkey.remaining_operations(); + + if(left_at_end.has_value()) { + result.test_u64_lt( + "Number of operations went down", left_at_end.value(), operations_remaining_at_start.value()); + + const uint64_t consumed = operations_remaining_at_start.value() - left_at_end.value(); + + result.test_u64_eq( + "Private key should have consumed exactly ConcurrentThreads many operations", consumed, ConcurrentThreads); + } else { + result.test_failure("Private key remaining_operations should return something both times"); + } + } else { + result.test_is_false("Private key should not be stateful", privkey.stateful_operation()); + } + + return result; +} + +Test::Result test_concurrent_verification(const ConcurrentPkTestCase& tc, + const Botan::Private_Key& privkey, + const Botan::Public_Key& pubkey) { + auto result = tc.result("verification"); + auto rng = Test::new_rng(result.who()); + const auto test_message = rng->random_vec(32); + + Botan::PK_Signer signer(privkey, *rng, tc.op_params()); + const auto signature = signer.sign_message(test_message, *rng); + + std::vector> futures; + futures.reserve(ConcurrentThreads); + + for(size_t i = 0; i != ConcurrentThreads; ++i) { + futures.push_back(std::async(std::launch::async, [&]() -> bool { + Botan::PK_Verifier verifier(pubkey, tc.op_params()); + return verifier.verify_message(test_message, signature); + })); + } + + for(size_t i = 0; i != ConcurrentThreads; ++i) { + try { + const bool valid = futures[i].get(); + result.test_is_true(Botan::fmt("Thread {} verification succeeded", i), valid); + } catch(std::exception& e) { + result.test_failure(Botan::fmt("Thread {} threw: {}", i, e.what())); + } + } + + return result; +} + +Test::Result test_concurrent_encryption(const ConcurrentPkTestCase& tc, + const Botan::Private_Key& privkey, + const Botan::Public_Key& pubkey) { + auto result = tc.result("encryption"); + auto rng = Test::new_rng(result.who()); + const auto test_message = rng->random_vec(32); + + std::vector>> futures; + futures.reserve(ConcurrentThreads); + + for(size_t i = 0; i != ConcurrentThreads; ++i) { + futures.push_back(std::async(std::launch::async, [&, i]() -> std::vector { + auto thread_rng = Test::new_rng(Botan::fmt("{} thread {}", result.who(), i)); + const Botan::PK_Encryptor_EME encryptor(pubkey, *thread_rng, tc.op_params()); + return encryptor.encrypt(test_message, *thread_rng); + })); + } + + const Botan::PK_Decryptor_EME decryptor(privkey, *rng, tc.op_params()); + + for(size_t i = 0; i != ConcurrentThreads; ++i) { + try { + const auto ciphertext = futures[i].get(); + const auto plaintext = decryptor.decrypt(ciphertext); + result.test_bin_eq(Botan::fmt("Thread {} decrypts correctly", i), plaintext, test_message); + } catch(std::exception& e) { + result.test_failure(Botan::fmt("Thread {} encrypt threw: {}", i, e.what())); + } + } + + return result; +} + +Test::Result test_concurrent_decryption(const ConcurrentPkTestCase& tc, + const Botan::Private_Key& privkey, + const Botan::Public_Key& pubkey) { + auto result = tc.result("decryption"); + auto rng = Test::new_rng(result.who()); + const auto test_message = rng->random_vec(32); + + const Botan::PK_Encryptor_EME encryptor(pubkey, *rng, tc.op_params()); + const auto ciphertext = encryptor.encrypt(test_message, *rng); + + std::vector>> futures; + futures.reserve(ConcurrentThreads); + + for(size_t i = 0; i != ConcurrentThreads; ++i) { + futures.push_back(std::async(std::launch::async, [&, i]() -> Botan::secure_vector { + auto thread_rng = Test::new_rng(Botan::fmt("{} thread {}", result.who(), i)); + const Botan::PK_Decryptor_EME decryptor(privkey, *thread_rng, tc.op_params()); + return decryptor.decrypt(ciphertext); + })); + } + + for(size_t i = 0; i != ConcurrentThreads; ++i) { + try { + const auto plaintext = futures[i].get(); + result.test_bin_eq(Botan::fmt("Thread {} decrypts correctly", i), plaintext, test_message); + } catch(std::exception& e) { + result.test_failure(Botan::fmt("Thread {} decrypt threw: {}", i, e.what())); + } + } + + return result; +} + +Test::Result test_concurrent_kem_encap(const ConcurrentPkTestCase& tc, + const Botan::Private_Key& privkey, + const Botan::Public_Key& pubkey) { + auto result = tc.result("KEM encapsulate"); + auto rng = Test::new_rng(result.who()); + + std::vector> futures; + futures.reserve(ConcurrentThreads); + + for(size_t i = 0; i != ConcurrentThreads; ++i) { + futures.push_back(std::async(std::launch::async, [&, i]() -> Botan::KEM_Encapsulation { + auto thread_rng = Test::new_rng(Botan::fmt("{} thread {}", result.who(), i)); + Botan::PK_KEM_Encryptor encryptor(pubkey, tc.op_params()); + return encryptor.encrypt(*thread_rng); + })); + } + + Botan::PK_KEM_Decryptor decryptor(privkey, *rng, tc.op_params()); + + for(size_t i = 0; i != ConcurrentThreads; ++i) { + try { + const auto kr = futures[i].get(); + const auto shared_key = decryptor.decrypt(kr.encapsulated_shared_key(), 32); + result.test_bin_eq(Botan::fmt("Thread {} shared key matches", i), shared_key, kr.shared_key()); + } catch(std::exception& e) { + result.test_failure(Botan::fmt("Thread {} encapsulate threw: {}", i, e.what())); + } + } + + return result; +} + +Test::Result test_concurrent_kem_decap(const ConcurrentPkTestCase& tc, + const Botan::Private_Key& privkey, + const Botan::Public_Key& pubkey) { + auto result = tc.result("KEM decapsulate"); + auto rng = Test::new_rng(result.who()); + + Botan::PK_KEM_Encryptor encryptor(pubkey, tc.op_params()); + auto kem_enc = encryptor.encrypt(*rng); + + std::vector>> futures; + futures.reserve(ConcurrentThreads); + + for(size_t i = 0; i != ConcurrentThreads; ++i) { + futures.push_back(std::async(std::launch::async, [&, i]() -> Botan::secure_vector { + auto thread_rng = Test::new_rng(Botan::fmt("{} thread {}", result.who(), i)); + Botan::PK_KEM_Decryptor decryptor(privkey, *thread_rng, tc.op_params()); + return decryptor.decrypt(kem_enc.encapsulated_shared_key(), 0); + })); + } + + for(size_t i = 0; i != ConcurrentThreads; ++i) { + try { + const auto shared_key = futures[i].get(); + result.test_bin_eq(Botan::fmt("Thread {} shared key matches", i), shared_key, kem_enc.shared_key()); + } catch(std::exception& e) { + result.test_failure(Botan::fmt("Thread {} decapsulate threw: {}", i, e.what())); + } + } + + return result; +} + +Test::Result test_concurrent_key_agreement(const ConcurrentPkTestCase& tc) { + auto result = tc.result("key agreement"); + + auto rng = Test::new_rng(result.who()); + auto our_key = tc.try_create_key(*rng); + if(!our_key) { + result.test_note("Skipping due to missing algorithm"); + return result; + } + + auto peer_key = tc.try_create_key(*rng); + + const auto* our_ka_key = dynamic_cast(our_key.get()); + const auto* peer_ka_key = dynamic_cast(peer_key.get()); + if(our_ka_key == nullptr || peer_ka_key == nullptr) { + result.test_failure("Key does not support key agreement"); + return result; + } + + const auto peer_public = peer_ka_key->public_value(); + + // Compute reference shared secret single-threaded + const Botan::PK_Key_Agreement ref_ka(*our_key, *rng, tc.op_params()); + const auto reference_secret = ref_ka.derive_key(32, peer_public).bits_of(); + + std::vector>> futures; + futures.reserve(ConcurrentThreads); + + for(size_t i = 0; i != ConcurrentThreads; ++i) { + futures.push_back(std::async(std::launch::async, [&, i]() -> std::vector { + auto thread_rng = Test::new_rng(Botan::fmt("{} thread {}", result.who(), i)); + const Botan::PK_Key_Agreement ka(*our_key, *thread_rng, tc.op_params()); + return Botan::unlock(ka.derive_key(32, peer_public).bits_of()); + })); + } + + for(size_t i = 0; i != ConcurrentThreads; ++i) { + try { + const auto shared_secret = futures[i].get(); + result.test_bin_eq(Botan::fmt("Thread {} shared secret matches", i), shared_secret, reference_secret); + } catch(std::exception& e) { + result.test_failure(Botan::fmt("Thread {} threw: {}", i, e.what())); + } + } + + return result; +} + +Test::Result test_concurrent_key_generation(const ConcurrentPkTestCase& tc) { + auto result = tc.result("key generation"); + + auto rng = Test::new_rng(result.who()); + + if(tc.try_create_key(*rng) == nullptr) { + result.test_note("Keygen not available"); + return result; + } + + std::vector>> futures; + futures.reserve(ConcurrentThreads); + + for(size_t i = 0; i != ConcurrentThreads; ++i) { + futures.push_back(std::async(std::launch::async, [&, i]() -> std::unique_ptr { + auto thread_rng = Test::new_rng(Botan::fmt("{} thread {}", result.who(), i)); + return tc.try_create_key(*thread_rng); + })); + } + + for(size_t i = 0; i != ConcurrentThreads; ++i) { + try { + const auto sk = futures[i].get(); + result.test_not_null(Botan::fmt("Thread {} generated a key", i), sk.get()); + + if(sk) { + result.test_is_true(Botan::fmt("Thread {} generated key seems valid", i), sk->check_key(*rng, true)); + } + } catch(std::exception& e) { + result.test_failure(Botan::fmt("Thread {} threw: {}", i, e.what())); + } + } + + return result; +} + +class Concurrent_Public_Key_Operations_Test : public Test { + public: + std::vector run() override { + std::vector results; + + concurrent_signing_and_verification_tests(results); + concurrent_encryption_tests(results); + concurrent_kem_tests(results); + concurrent_key_agreement_tests(results); + concurrent_key_generation_tests(results); + + return results; + } + + private: + void concurrent_signing_and_verification_tests(std::vector& results) { + const std::vector test_cases = { + ConcurrentPkTestCase("RSA", "1536", "PKCS1v15(SHA-256)"), + ConcurrentPkTestCase("ECDSA", "secp256r1", "SHA-256"), + ConcurrentPkTestCase("ECKCDSA", "secp256r1", "SHA-256"), + ConcurrentPkTestCase("ECGDSA", "secp256r1", "SHA-256"), + ConcurrentPkTestCase("DSA", "dsa/jce/1024", "SHA-256"), + ConcurrentPkTestCase("SM2", "sm2p256v1", "SM3"), + ConcurrentPkTestCase("Ed25519", "", "Pure"), + ConcurrentPkTestCase("Ed448", "", "Pure"), + ConcurrentPkTestCase("ML-DSA", "ML-DSA-4x4"), + ConcurrentPkTestCase("Dilithium", "Dilithium-4x4-r3"), + ConcurrentPkTestCase("Dilithium", "Dilithium-4x4-AES-r3"), + ConcurrentPkTestCase("SLH-DSA", "SLH-DSA-SHA2-128f"), + ConcurrentPkTestCase("HSS-LMS", "SHA-256,HW(5,8)"), + ConcurrentPkTestCase("XMSS", "XMSS-SHA2_10_256"), + }; + + for(const auto& tc : test_cases) { + auto rng = Test::new_rng(tc.algo_name()); + + if(auto privkey = tc.try_create_key(*rng)) { + auto pubkey = privkey->public_key(); + results.push_back(test_concurrent_signing(tc, *privkey, *pubkey)); + results.push_back(test_concurrent_verification(tc, *privkey, *pubkey)); + } else { + results.push_back(tc.skip_missing("signing")); + } + } + } + + void concurrent_encryption_tests(std::vector& results) { + const std::vector test_cases = { + ConcurrentPkTestCase("RSA", "1536", "OAEP(SHA-256)"), + ConcurrentPkTestCase("ElGamal", "modp/ietf/1536", "PKCS1v15"), + }; + + for(const auto& tc : test_cases) { + auto rng = Test::new_rng(tc.algo_name()); + + if(auto privkey = tc.try_create_key(*rng)) { + auto pubkey = privkey->public_key(); + results.push_back(test_concurrent_encryption(tc, *privkey, *pubkey)); + results.push_back(test_concurrent_decryption(tc, *privkey, *pubkey)); + } else { + results.push_back(tc.skip_missing("encryption")); + } + } + } + + void concurrent_kem_tests(std::vector& results) { + const std::vector test_cases = { + ConcurrentPkTestCase("RSA", "1536", "Raw"), + ConcurrentPkTestCase("ClassicMcEliece", "348864f", "Raw"), + ConcurrentPkTestCase("McEliece", "1632,33", "Raw"), + ConcurrentPkTestCase("FrodoKEM", "FrodoKEM-640-SHAKE", "Raw"), + ConcurrentPkTestCase("FrodoKEM", "FrodoKEM-640-AES", "Raw"), + ConcurrentPkTestCase("ML-KEM", "ML-KEM-512", "Raw"), + ConcurrentPkTestCase("Kyber", "Kyber-512-90s-r3", "Raw"), + ConcurrentPkTestCase("Kyber", "Kyber-512-r3", "Raw"), + }; + + for(const auto& tc : test_cases) { + auto rng = Test::new_rng(tc.algo_name()); + if(auto privkey = tc.try_create_key(*rng)) { + auto pubkey = privkey->public_key(); + results.push_back(test_concurrent_kem_encap(tc, *privkey, *pubkey)); + results.push_back(test_concurrent_kem_decap(tc, *privkey, *pubkey)); + } else { + results.push_back(tc.skip_missing("KEM encapsulate")); + } + } + } + + void concurrent_key_agreement_tests(std::vector& results) { + const std::vector test_cases = { + ConcurrentPkTestCase("DH", "modp/ietf/1536", "Raw"), + ConcurrentPkTestCase("ECDH", "secp256r1", "Raw"), + ConcurrentPkTestCase("X25519", "", "Raw"), + ConcurrentPkTestCase("X448", "", "Raw"), + }; + + for(const auto& tc : test_cases) { + results.push_back(test_concurrent_key_agreement(tc)); + } + } + + void concurrent_key_generation_tests(std::vector& results) { + const std::vector test_cases = { + ConcurrentPkTestCase("ClassicMcEliece", "348864f"), + ConcurrentPkTestCase("DH", "modp/ietf/1536"), + ConcurrentPkTestCase("DSA", "dsa/jce/1024"), + ConcurrentPkTestCase("ECDH", "secp256r1"), + ConcurrentPkTestCase("ECDSA", "secp256r1"), + ConcurrentPkTestCase("ECGDSA", "secp256r1"), + ConcurrentPkTestCase("ECKCDSA", "secp256r1"), + ConcurrentPkTestCase("Ed25519", ""), + ConcurrentPkTestCase("Ed448", ""), + ConcurrentPkTestCase("HSS-LMS", "SHA-256,HW(5,8)"), + ConcurrentPkTestCase("RSA", "1536"), + ConcurrentPkTestCase("SLH-DSA", "SLH-DSA-SHA2-128f"), + ConcurrentPkTestCase("SM2", "sm2p256v1"), + ConcurrentPkTestCase("X25519", ""), + ConcurrentPkTestCase("X448", ""), + }; + + for(const auto& tc : test_cases) { + results.push_back(test_concurrent_key_generation(tc)); + } + } +}; + +BOTAN_REGISTER_SERIALIZED_TEST("pubkey", "pk_concurrent_ops", Concurrent_Public_Key_Operations_Test); + +} // namespace + +#endif + +} // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_cryptobox.cpp botan3-3.12.0+dfsg/src/tests/test_cryptobox.cpp --- botan3-3.7.1+dfsg/src/tests/test_cryptobox.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_cryptobox.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -4,13 +4,12 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#define BOTAN_NO_DEPRECATED_WARNINGS - -#include "test_rng.h" #include "tests.h" #if defined(BOTAN_HAS_CRYPTO_BOX) + #include "test_rng.h" #include + #include #include #endif @@ -41,9 +40,9 @@ const std::string ciphertext = Botan::CryptoBox::encrypt(input.data(), input.size(), password, salt_rng); - result.test_eq("encryption is expected value", ciphertext, expected_pem); + result.test_str_eq("encryption is expected value", ciphertext, expected_pem); - result.test_eq("decryption works", Botan::CryptoBox::decrypt_bin(ciphertext, password), input); + result.test_bin_eq("decryption works", Botan::CryptoBox::decrypt_bin(ciphertext, password), input); // Now corrupt a bit and ensure it fails try { diff -Nru botan3-3.7.1+dfsg/src/tests/test_crystals.cpp botan3-3.12.0+dfsg/src/tests/test_crystals.cpp --- botan3-3.7.1+dfsg/src/tests/test_crystals.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_crystals.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,12 +10,11 @@ #if defined(BOTAN_HAS_PQCRYSTALS) #include + #include - #include #include #include #include - #include namespace Botan_Tests { @@ -42,27 +41,27 @@ // The wrapper template functions gcd<>(), v<>() and u<>() are workarounds // for an assumed bug in MSVC 19.38.33134 that does not accept the invocation // of the consteval function `extended_euclidean_algorithm` as a parameter to - // `test_is_eq()`. + // `test_.._eq()`. // // The resulting error is: // error C7595: 'Botan::extended_euclidean_algorithm': call to immediate function is not a constant expression // // What we'd actually want to write here: - // res.test_is_eq("gcd(350, 294)", Botan::extended_euclidean_algorithm(350, 294).gcd, 14); - res.test_is_eq("gcd(1337, 1337)", gcd(1337, 1337), 1337); - res.test_is_eq("gcd(350, 294)", gcd(350, 294), 14); - res.test_is_eq("gcd(294, 350)", gcd(294, 350), 14); - - res.test_is_eq("gcd(1337, 1337)", gcd(1337, 1337), 1337); - res.test_is_eq("gcd(350, 294)", gcd(350, 294), 14); - res.test_is_eq("gcd(294, 350)", gcd(294, 350), 14); - - res.test_is_eq("u(1337, 1337)", u(1337, 1337), 0); - res.test_is_eq("v(1337, 1337)", v(1337, 1337), 1); - res.test_is_eq("u(294, 350)", u(294, 350), 6); + // res.test_u32_eq("gcd(350, 294)", Botan::extended_euclidean_algorithm(350, 294).gcd, 14); + res.test_u32_eq("gcd(1337, 1337)", gcd(1337, 1337), 1337); + res.test_u32_eq("gcd(350, 294)", gcd(350, 294), 14); + res.test_u32_eq("gcd(294, 350)", gcd(294, 350), 14); + + res.test_u16_eq("gcd(1337, 1337)", gcd(1337, 1337), 1337); + res.test_u16_eq("gcd(350, 294)", gcd(350, 294), 14); + res.test_u16_eq("gcd(294, 350)", gcd(294, 350), 14); + + res.test_u16_eq("u(1337, 1337)", u(1337, 1337), 0); + res.test_u16_eq("v(1337, 1337)", v(1337, 1337), 1); + res.test_u16_eq("u(294, 350)", u(294, 350), 6); - res.test_is_eq("q^-1(3329) - Kyber::Q", Botan::modular_inverse(3329), -3327); - res.test_is_eq("q^-1(8380417) - Dilithium::Q", Botan::modular_inverse(8380417), 58728449); + res.test_i16_eq("q^-1(3329) - Kyber::Q", Botan::modular_inverse(3329), -3327); + res.test_i32_eq("q^-1(8380417) - Dilithium::Q", Botan::modular_inverse(8380417), 58728449); return res; } @@ -87,24 +86,30 @@ static constexpr size_t NTT_Degree = 256; }; -template -class Mock_Trait final : public Botan::CRYSTALS::Trait_Base> { +template +class Mock_Trait final : public Botan::CRYSTALS::Trait_Base> { public: - using T = typename Botan::CRYSTALS::Trait_Base>::T; - using T2 = typename Botan::CRYSTALS::Trait_Base>::T2; - constexpr static auto N = Botan::CRYSTALS::Trait_Base>::N; + using T = typename Botan::CRYSTALS::Trait_Base>::T; + using T2 = typename Botan::CRYSTALS::Trait_Base>::T2; + constexpr static auto N = Botan::CRYSTALS::Trait_Base>::N; - static T montgomery_reduce_coefficient(T2) { + static T montgomery_reduce_coefficient(T2 /*unused*/) { throw Botan_Tests::Test_Error("montgomery reduction not implemented"); } - static T barrett_reduce_coefficient(T) { throw Botan_Tests::Test_Error("barrett reduction not implemented"); } + static T barrett_reduce_coefficient(T /*unused*/) { + throw Botan_Tests::Test_Error("barrett reduction not implemented"); + } - static void ntt(std::span) { throw Botan_Tests::Test_Error("NTT not implemented"); } + static void ntt(std::span /*unused*/) { throw Botan_Tests::Test_Error("NTT not implemented"); } - static void inverse_ntt(std::span) { throw Botan_Tests::Test_Error("inverse NTT not implemented"); } + static void inverse_ntt(std::span /*unused*/) { + throw Botan_Tests::Test_Error("inverse NTT not implemented"); + } - static void poly_pointwise_montgomery(std::span, std::span, std::span) { + static void poly_pointwise_montgomery(std::span /*unused*/, + std::span /*unused*/, + std::span /*unused*/) { throw Botan_Tests::Test_Error("pointwise multiplication not implemented"); } }; @@ -123,157 +128,157 @@ return { CHECK("polynomial owning storage", [](Test::Result& res) { - Kyberish_Poly p; - res.confirm("default constructed poly owns memory", p.owns_storage()); + const Kyberish_Poly p; + res.test_is_true("default constructed poly owns memory", p.owns_storage()); for(auto coeff : p) { - res.test_is_eq("default constructed poly has 0 coefficients", coeff, 0); + res.test_i16_eq("default constructed poly has 0 coefficients", coeff, 0); } - Kyberish_Poly p_ntt; - res.confirm("default constructed poly owns memory (NTT)", p_ntt.owns_storage()); + const Kyberish_Poly p_ntt; + res.test_is_true("default constructed poly owns memory (NTT)", p_ntt.owns_storage()); for(auto coeff : p) { - res.test_is_eq("default constructed poly (NTT) has 0 coefficients", coeff, 0); + res.test_i16_eq("default constructed poly (NTT) has 0 coefficients", coeff, 0); } }), CHECK("polynomial vector managing storage", [](Test::Result& res) { - Kyberish_PolyVec polys(4); - res.test_is_eq("requested size", polys.size(), 4); + const Kyberish_PolyVec polys(4); + res.test_sz_eq("requested size", polys.size(), 4); for(const auto& poly : polys) { - res.confirm("poly embedded in vector does not own memory", !poly.owns_storage()); + res.test_is_true("poly embedded in vector does not own memory", !poly.owns_storage()); } - Kyberish_PolyVec polys_ntt(4); - res.test_is_eq("requested size (NTT)", polys.size(), 4); + const Kyberish_PolyVec polys_ntt(4); + res.test_sz_eq("requested size (NTT)", polys.size(), 4); for(const auto& poly : polys_ntt) { - res.confirm("poly (NTT) embedded in vector does not own memory", !poly.owns_storage()); + res.test_is_true("poly (NTT) embedded in vector does not own memory", !poly.owns_storage()); } }), - CHECK("cloned polynomials always manage their storge", + CHECK("cloned polynomials always manage their storage", [](Test::Result& res) { - Kyberish_Poly p; + const Kyberish_Poly p; auto p2 = p.clone(); - res.confirm("cloned poly owns memory", p2.owns_storage()); + res.test_is_true("cloned poly owns memory", p2.owns_storage()); - Kyberish_PolyVec pv(3); - for(auto& poly : pv) { + const Kyberish_PolyVec pv(3); + for(const auto& poly : pv) { res.require("poly in vector does not own memory", !poly.owns_storage()); auto pv2 = poly.clone(); - res.confirm("cloned poly in vector owns memory", pv2.owns_storage()); + res.test_is_true("cloned poly in vector owns memory", pv2.owns_storage()); } auto pv2 = pv.clone(); for(const auto& poly : pv2) { - res.confirm("cloned vector polynomial don't own memory", !poly.owns_storage()); + res.test_is_true("cloned vector polynomial don't own memory", !poly.owns_storage()); } - Kyberish_Poly p_ntt; + const Kyberish_Poly p_ntt; auto p2_ntt = p_ntt.clone(); - res.confirm("cloned poly (NTT) owns memory", p2_ntt.owns_storage()); + res.test_is_true("cloned poly (NTT) owns memory", p2_ntt.owns_storage()); - Kyberish_PolyVec pv_ntt(3); - for(auto& poly : pv_ntt) { + const Kyberish_PolyVec pv_ntt(3); + for(const auto& poly : pv_ntt) { res.require("poly (NTT) in vector does not own memory", !poly.owns_storage()); auto pv2_ntt = poly.clone(); - res.confirm("cloned poly (NTT) in vector owns memory", pv2_ntt.owns_storage()); + res.test_is_true("cloned poly (NTT) in vector owns memory", pv2_ntt.owns_storage()); } auto pv2_ntt = pv_ntt.clone(); for(const auto& poly : pv2_ntt) { - res.confirm("cloned vector polynomial (NTT) don't own memory", !poly.owns_storage()); + res.test_is_true("cloned vector polynomial (NTT) don't own memory", !poly.owns_storage()); } }), CHECK("hamming weight of polynomials", [](Test::Result& res) { Kyberish_Poly p; - res.test_is_eq("hamming weight of 0", p.hamming_weight(), 0); + res.test_sz_eq("hamming weight of 0", p.hamming_weight(), 0); p[0] = 1337; - res.test_is_eq("hamming weight of 1", p.hamming_weight(), 1); + res.test_sz_eq("hamming weight of 1", p.hamming_weight(), 1); p[1] = 42; - res.test_is_eq("hamming weight of 2", p.hamming_weight(), 2); + res.test_sz_eq("hamming weight of 2", p.hamming_weight(), 2); p[2] = 11; - res.test_is_eq("hamming weight of 3", p.hamming_weight(), 3); + res.test_sz_eq("hamming weight of 3", p.hamming_weight(), 3); p[3] = 4; - res.test_is_eq("hamming weight of 4", p.hamming_weight(), 4); + res.test_sz_eq("hamming weight of 4", p.hamming_weight(), 4); p[3] = 0; - res.test_is_eq("hamming weight of 3", p.hamming_weight(), 3); + res.test_sz_eq("hamming weight of 3", p.hamming_weight(), 3); p[2] = 0; - res.test_is_eq("hamming weight of 2", p.hamming_weight(), 2); + res.test_sz_eq("hamming weight of 2", p.hamming_weight(), 2); p[1] = 0; - res.test_is_eq("hamming weight of 1", p.hamming_weight(), 1); + res.test_sz_eq("hamming weight of 1", p.hamming_weight(), 1); p[0] = 0; - res.test_is_eq("hamming weight of 0", p.hamming_weight(), 0); + res.test_sz_eq("hamming weight of 0", p.hamming_weight(), 0); }), CHECK("hamming weight of polynomial vectors", [](Test::Result& res) { Kyberish_PolyVec pv(3); - res.test_is_eq("hamming weight of 0", pv.hamming_weight(), 0); + res.test_sz_eq("hamming weight of 0", pv.hamming_weight(), 0); pv[0][0] = 1337; - res.test_is_eq("hamming weight of 1", pv.hamming_weight(), 1); + res.test_sz_eq("hamming weight of 1", pv.hamming_weight(), 1); pv[1][1] = 42; - res.test_is_eq("hamming weight of 2", pv.hamming_weight(), 2); + res.test_sz_eq("hamming weight of 2", pv.hamming_weight(), 2); pv[2][2] = 11; - res.test_is_eq("hamming weight of 3", pv.hamming_weight(), 3); + res.test_sz_eq("hamming weight of 3", pv.hamming_weight(), 3); pv[2][2] = 0; - res.test_is_eq("hamming weight of 2", pv.hamming_weight(), 2); + res.test_sz_eq("hamming weight of 2", pv.hamming_weight(), 2); pv[1][1] = 0; - res.test_is_eq("hamming weight of 1", pv.hamming_weight(), 1); + res.test_sz_eq("hamming weight of 1", pv.hamming_weight(), 1); pv[0][0] = 0; - res.test_is_eq("hamming weight of 0", pv.hamming_weight(), 0); + res.test_sz_eq("hamming weight of 0", pv.hamming_weight(), 0); }), CHECK("value range validation", [](Test::Result& res) { Kyberish_Poly p; - res.confirm("value range validation (all zero)", p.ct_validate_value_range(0, 1)); + res.test_is_true("value range validation (all zero)", p.ct_validate_value_range(0, 1)); p[0] = 1; p[32] = 1; p[172] = 1; - res.confirm("value range validation", p.ct_validate_value_range(0, 1)); + res.test_is_true("value range validation", p.ct_validate_value_range(0, 1)); p[11] = 2; - res.confirm("value range validation", !p.ct_validate_value_range(0, 1)); + res.test_is_true("value range validation", !p.ct_validate_value_range(0, 1)); p[11] = -1; - res.confirm("value range validation", !p.ct_validate_value_range(0, 1)); + res.test_is_true("value range validation", !p.ct_validate_value_range(0, 1)); }), CHECK("value range validation for polynomial vectors", [](Test::Result& res) { Kyberish_PolyVec pv(3); - res.confirm("value range validation (all zero)", pv.ct_validate_value_range(0, 1)); + res.test_is_true("value range validation (all zero)", pv.ct_validate_value_range(0, 1)); pv[0][0] = 1; pv[1][32] = 1; pv[2][172] = 1; - res.confirm("value range validation", pv.ct_validate_value_range(0, 1)); + res.test_is_true("value range validation", pv.ct_validate_value_range(0, 1)); pv[0][11] = 2; - res.confirm("value range validation", !pv.ct_validate_value_range(0, 1)); + res.test_is_true("value range validation", !pv.ct_validate_value_range(0, 1)); pv[0][11] = -1; - res.confirm("value range validation", !pv.ct_validate_value_range(0, 1)); + res.test_is_true("value range validation", !pv.ct_validate_value_range(0, 1)); }), }; } @@ -284,7 +289,7 @@ class DeterministicXOF : public Botan::XOF { public: - DeterministicXOF(std::span data) : m_data(data) {} + explicit DeterministicXOF(std::span data) : m_data(data) {} std::string name() const override { return "DeterministicXOF"; } @@ -296,11 +301,13 @@ size_t block_size() const override { return 1; } - void start_msg(std::span, std::span) override { + void start_msg(std::span /*unused*/, std::span /*unused*/) override { throw Botan_Tests::Test_Error("start_msg not implemented"); } - void add_data(std::span) override { throw Botan_Tests::Test_Error("add_data not implemented"); } + void add_data(std::span /*unused*/) override { + throw Botan_Tests::Test_Error("add_data not implemented"); + } void generate_bytes(std::span output) override { m_data.copy_into(output); } @@ -319,7 +326,7 @@ auto random_poly = [&rng]() -> Poly { Poly p; - std::array buf; + std::array buf{}; for(auto& coeff : p) { rng.randomize(buf); coeff = static_cast((Botan::load_be(buf) % (range + 1))); @@ -331,15 +338,15 @@ std::vector buffer((p.size() * expected_encoding_bits + 7) / 8); Botan::BufferStuffer stuffer(buffer); Botan::CRYSTALS::pack(p, stuffer); - res.confirm("encoded polynomial fills buffer", stuffer.full()); + res.test_is_true("encoded polynomial fills buffer", stuffer.full()); Botan::BufferSlicer slicer(buffer); Poly p_unpacked; Botan::CRYSTALS::unpack(p_unpacked, slicer); - res.confirm("decoded polynomial reads all bytes", slicer.empty()); + res.test_is_true("decoded polynomial reads all bytes", slicer.empty()); p_unpacked -= p; - res.test_eq("p = unpack(pack(p))", p_unpacked.hamming_weight(), 0); + res.test_sz_eq("p = unpack(pack(p))", p_unpacked.hamming_weight(), 0); } } // namespace @@ -383,7 +390,7 @@ std::vector buffer1(96); Botan::BufferStuffer stuffer1(buffer1); Botan::CRYSTALS::pack<6>(p1, stuffer1); - res.test_eq("3 bit encoding", buffer1, threebitencoding); + res.test_bin_eq("3 bit encoding", buffer1, threebitencoding); // value range is exactly one byte Kyberish_Poly p2; @@ -394,13 +401,13 @@ std::vector buffer2(256); Botan::BufferStuffer stuffer2(buffer2); Botan::CRYSTALS::pack<255>(p2, stuffer2); - res.test_eq("8 bit encoding", buffer2, eightbitencoding); + res.test_bin_eq("8 bit encoding", buffer2, eightbitencoding); // value range for 10 bits, with mapping function std::vector buffer3(p2.size() / 8 * 10 /* bits */); Botan::BufferStuffer stuffer3(buffer3); Botan::CRYSTALS::pack<512>(p2, stuffer3, [](int16_t x) -> uint16_t { return x * 2; }); - res.test_eq("10 bit encoding", buffer3, tenbitencoding); + res.test_bin_eq("10 bit encoding", buffer3, tenbitencoding); }), CHECK("decode polynomial coefficients from buffer", @@ -408,25 +415,25 @@ Kyberish_Poly p1; Botan::BufferSlicer slicer1(threebitencoding); Botan::CRYSTALS::unpack<6>(p1, slicer1); - res.require("read all bytes from 3-bit encoding", slicer1.empty()); + res.test_is_true("read all bytes from 3-bit encoding", slicer1.empty()); for(size_t i = 0; i < p1.size(); ++i) { - res.test_is_eq("decoded 3-bit coefficient", p1[i], i % 7); + res.test_i16_eq("decoded 3-bit coefficient", p1[i], i % 7); } Kyberish_Poly p2; Botan::BufferSlicer slicer2(eightbitencoding); Botan::CRYSTALS::unpack<255>(p2, slicer2); - res.require("read all bytes from 8-bit encoding", slicer2.empty()); + res.test_is_true("read all bytes from 8-bit encoding", slicer2.empty()); for(size_t i = 0; i < p2.size(); ++i) { - res.test_is_eq("decoded 8-bit coefficient", p2[i], i); + res.test_sz_eq("decoded 8-bit coefficient", p2[i], i); } Kyberish_Poly p3; Botan::BufferSlicer slicer3(tenbitencoding); Botan::CRYSTALS::unpack<512>(p3, slicer3, [](uint16_t x) -> int16_t { return x / 2; }); - res.require("read all bytes from 10-bit encoding", slicer3.empty()); + res.test_is_true("read all bytes from 10-bit encoding", slicer3.empty()); for(size_t i = 0; i < p3.size(); ++i) { - res.test_is_eq("decoded 10-bit coefficient with mapping", p3[i], i); + res.test_sz_eq("decoded 10-bit coefficient with mapping", p3[i], i); } }), @@ -437,22 +444,24 @@ DeterministicXOF xof1(threebitencoding); Botan::CRYSTALS::unpack<6>(p1, xof1); for(size_t i = 0; i < p1.size(); ++i) { - res.test_is_eq("decoded 3-bit coefficient", p1[i], i % 7); + res.test_i16_eq("decoded 3-bit coefficient", p1[i], i % 7); } Kyberish_Poly p2; DeterministicXOF xof2(eightbitencoding); Botan::CRYSTALS::unpack<255>(p2, xof2); for(size_t i = 0; i < p2.size(); ++i) { - res.test_is_eq("decoded 8-bit coefficient", p2[i], i); + res.test_sz_eq("decoded 8-bit coefficient", p2[i], i); } Kyberish_Poly p3; DeterministicXOF xof3(tenbitencoding); Botan::CRYSTALS::unpack<512>(p3, xof3, [](int16_t x) -> int16_t { return x / 2; }); for(size_t i = 0; i < p3.size(); ++i) { - res.test_is_eq("decoded 10-bit coefficient with mapping", p3[i], i); + res.test_sz_eq("decoded 10-bit coefficient with mapping", p3[i], i); } + #else + BOTAN_UNUSED(res); #endif }), @@ -511,7 +520,7 @@ template auto output() { - std::array result; + std::array result{}; for(uint8_t& byte : result) { byte = static_cast(m_counter++); } @@ -536,9 +545,9 @@ CHECK("bounded XOF with small bound", [](Test::Result& result) { Mocked_Bounded_XOF<3> xof; - result.test_is_eq("next_byte() returns 0", xof.next_byte(), uint8_t(0)); - result.test_is_eq("next_byte() returns 1", xof.next_byte(), uint8_t(1)); - result.test_is_eq("next_byte() returns 2", xof.next_byte(), uint8_t(2)); + result.test_u8_eq("next_byte() returns 0", xof.next_byte(), uint8_t(0)); + result.test_u8_eq("next_byte() returns 1", xof.next_byte(), uint8_t(1)); + result.test_u8_eq("next_byte() returns 2", xof.next_byte(), uint8_t(2)); result.test_throws("next_byte() throws", [&xof]() { xof.next_byte(); }); }), @@ -550,8 +559,8 @@ }; Mocked_Bounded_XOF<5> xof; - result.test_is_eq("next_byte() returns 1", xof.next_byte(filter), uint8_t(1)); - result.test_is_eq("next_byte() returns 3", xof.next_byte(filter), uint8_t(3)); + result.test_u8_eq("next_byte() returns 1", xof.next_byte(filter), uint8_t(1)); + result.test_u8_eq("next_byte() returns 3", xof.next_byte(filter), uint8_t(3)); result.test_throws("next_byte() throws", [&]() { xof.next_byte(filter); }); }), @@ -560,10 +569,10 @@ auto map = [](auto bytes) { return Botan::load_be(bytes); }; Mocked_Bounded_XOF<17> xof; - result.test_is_eq("next returns 0x00010203", xof.next<4>(map), uint32_t(0x00010203)); - result.test_is_eq("next returns 0x04050607", xof.next<4>(map), uint32_t(0x04050607)); - result.test_is_eq("next returns 0x08090A0B", xof.next<4>(map), uint32_t(0x08090A0B)); - result.test_is_eq("next returns 0x0C0D0E0F", xof.next<4>(map), uint32_t(0x0C0D0E0F)); + result.test_u32_eq("next returns 0x00010203", xof.next<4>(map), uint32_t(0x00010203)); + result.test_u32_eq("next returns 0x04050607", xof.next<4>(map), uint32_t(0x04050607)); + result.test_u32_eq("next returns 0x08090A0B", xof.next<4>(map), uint32_t(0x08090A0B)); + result.test_u32_eq("next returns 0x0C0D0E0F", xof.next<4>(map), uint32_t(0x0C0D0E0F)); result.test_throws("next() throws", [&]() { xof.next<4>(map); }); }), @@ -573,11 +582,11 @@ auto filter = [](uint32_t number) { return number < 50; }; Mocked_Bounded_XOF<17> xof; - result.test_is_eq("next returns 3", xof.next<3>(map, filter), uint32_t(3)); - result.test_is_eq("next returns 12", xof.next<3>(map, filter), uint32_t(12)); - result.test_is_eq("next returns 21", xof.next<3>(map, filter), uint32_t(21)); - result.test_is_eq("next returns 30", xof.next<3>(map, filter), uint32_t(30)); - result.test_is_eq("next returns 39", xof.next<3>(map, filter), uint32_t(39)); + result.test_u32_eq("next returns 3", xof.next<3>(map, filter), uint32_t(3)); + result.test_u32_eq("next returns 12", xof.next<3>(map, filter), uint32_t(12)); + result.test_u32_eq("next returns 21", xof.next<3>(map, filter), uint32_t(21)); + result.test_u32_eq("next returns 30", xof.next<3>(map, filter), uint32_t(30)); + result.test_u32_eq("next returns 39", xof.next<3>(map, filter), uint32_t(39)); result.test_throws("next() throws", [&]() { xof.next<3>(map, filter); }); }), }; diff -Nru botan3-3.7.1+dfsg/src/tests/test_ct_utils.cpp botan3-3.12.0+dfsg/src/tests/test_ct_utils.cpp --- botan3-3.7.1+dfsg/src/tests/test_ct_utils.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_ct_utils.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -5,41 +5,46 @@ */ #include "tests.h" +#include #include #include +#include +#include namespace Botan_Tests { +namespace { + class CT_Mask_Tests final : public Test { public: std::vector run() override { Test::Result result("CT::Mask"); - result.test_eq_sz("CT::is_zero8", Botan::CT::Mask::is_zero(0).value(), 0xFF); - result.test_eq_sz("CT::is_zero8", Botan::CT::Mask::is_zero(1).value(), 0x00); - result.test_eq_sz("CT::is_zero8", Botan::CT::Mask::is_zero(0xFF).value(), 0x00); - - result.test_eq_sz("CT::is_zero16", Botan::CT::Mask::is_zero(0).value(), 0xFFFF); - result.test_eq_sz("CT::is_zero16", Botan::CT::Mask::is_zero(1).value(), 0x0000); - result.test_eq_sz("CT::is_zero16", Botan::CT::Mask::is_zero(0xFF).value(), 0x0000); - - result.test_eq_sz("CT::is_zero32", Botan::CT::Mask::is_zero(0).value(), 0xFFFFFFFF); - result.test_eq_sz("CT::is_zero32", Botan::CT::Mask::is_zero(1).value(), 0x00000000); - result.test_eq_sz("CT::is_zero32", Botan::CT::Mask::is_zero(0xFF).value(), 0x00000000); - - result.test_eq_sz("CT::is_less8", Botan::CT::Mask::is_lt(0, 1).value(), 0xFF); - result.test_eq_sz("CT::is_less8", Botan::CT::Mask::is_lt(1, 0).value(), 0x00); - result.test_eq_sz("CT::is_less8", Botan::CT::Mask::is_lt(0xFF, 5).value(), 0x00); - - result.test_eq_sz("CT::is_less16", Botan::CT::Mask::is_lt(0, 1).value(), 0xFFFF); - result.test_eq_sz("CT::is_less16", Botan::CT::Mask::is_lt(1, 0).value(), 0x0000); - result.test_eq_sz("CT::is_less16", Botan::CT::Mask::is_lt(0xFFFF, 5).value(), 0x0000); - - result.test_eq_sz("CT::is_less32", Botan::CT::Mask::is_lt(0, 1).value(), 0xFFFFFFFF); - result.test_eq_sz("CT::is_less32", Botan::CT::Mask::is_lt(1, 0).value(), 0x00000000); - result.test_eq_sz("CT::is_less32", Botan::CT::Mask::is_lt(0xFFFF5, 5).value(), 0x00000000); - result.test_eq_sz("CT::is_less32", Botan::CT::Mask::is_lt(0xFFFFFFFF, 5).value(), 0x00000000); - result.test_eq_sz("CT::is_less32", Botan::CT::Mask::is_lt(5, 0xFFFFFFFF).value(), 0xFFFFFFFF); + result.test_u8_eq("CT::is_zero8", Botan::CT::Mask::is_zero(0).value(), 0xFF); + result.test_u8_eq("CT::is_zero8", Botan::CT::Mask::is_zero(1).value(), 0x00); + result.test_u8_eq("CT::is_zero8", Botan::CT::Mask::is_zero(0xFF).value(), 0x00); + + result.test_u16_eq("CT::is_zero16", Botan::CT::Mask::is_zero(0).value(), 0xFFFF); + result.test_u16_eq("CT::is_zero16", Botan::CT::Mask::is_zero(1).value(), 0x0000); + result.test_u16_eq("CT::is_zero16", Botan::CT::Mask::is_zero(0xFF).value(), 0x0000); + + result.test_u32_eq("CT::is_zero32", Botan::CT::Mask::is_zero(0).value(), 0xFFFFFFFF); + result.test_u32_eq("CT::is_zero32", Botan::CT::Mask::is_zero(1).value(), 0x00000000); + result.test_u32_eq("CT::is_zero32", Botan::CT::Mask::is_zero(0xFF).value(), 0x00000000); + + result.test_u8_eq("CT::is_less8", Botan::CT::Mask::is_lt(0, 1).value(), 0xFF); + result.test_u8_eq("CT::is_less8", Botan::CT::Mask::is_lt(1, 0).value(), 0x00); + result.test_u8_eq("CT::is_less8", Botan::CT::Mask::is_lt(0xFF, 5).value(), 0x00); + + result.test_u16_eq("CT::is_less16", Botan::CT::Mask::is_lt(0, 1).value(), 0xFFFF); + result.test_u16_eq("CT::is_less16", Botan::CT::Mask::is_lt(1, 0).value(), 0x0000); + result.test_u16_eq("CT::is_less16", Botan::CT::Mask::is_lt(0xFFFF, 5).value(), 0x0000); + + result.test_u32_eq("CT::is_less32", Botan::CT::Mask::is_lt(0, 1).value(), 0xFFFFFFFF); + result.test_u32_eq("CT::is_less32", Botan::CT::Mask::is_lt(1, 0).value(), 0x00000000); + result.test_u32_eq("CT::is_less32", Botan::CT::Mask::is_lt(0xFFFF5, 5).value(), 0x00000000); + result.test_u32_eq("CT::is_less32", Botan::CT::Mask::is_lt(0xFFFFFFFF, 5).value(), 0x00000000); + result.test_u32_eq("CT::is_less32", Botan::CT::Mask::is_lt(5, 0xFFFFFFFF).value(), 0xFFFFFFFF); for(auto bad_input : {0, 1}) { for(size_t input_length = 0; input_length != 64; ++input_length) { @@ -53,17 +58,17 @@ auto written = Botan::CT::copy_output(accept, output, input, offset); - if(bad_input) { - result.confirm("If bad input, no output", !written.has_value().as_bool()); + if(bad_input > 0) { + result.test_is_true("If bad input, no output", !written.has_value().as_bool()); } else { if(offset > input_length) { - result.confirm("If offset is too large, no output", !written.has_value().as_bool()); + result.test_is_true("If offset is too large, no output", !written.has_value().as_bool()); } else { const size_t bytes = written.value(); - result.test_eq_sz("CT::copy_output length", bytes, input.size() - offset); + result.test_sz_eq("CT::copy_output length", bytes, input.size() - offset); for(size_t i = 0; i != bytes; ++i) { - result.test_eq_sz("CT::copy_output offset", output[i], input[i + offset]); + result.test_u8_eq("CT::copy_output offset", output[i], input[i + offset]); } } } @@ -82,8 +87,8 @@ std::vector run() override { Test::Result result("CT::Choice"); - result.test_eq("CT::Choice::yes", Botan::CT::Choice::yes().as_bool(), true); - result.test_eq("CT::Choice::no", Botan::CT::Choice::no().as_bool(), false); + result.test_is_true("CT::Choice::yes", Botan::CT::Choice::yes().as_bool()); + result.test_is_false("CT::Choice::no", Botan::CT::Choice::no().as_bool()); test_choice_from_int("uint8_t", result); test_choice_from_int("uint16_t", result); @@ -99,10 +104,10 @@ const auto tname = Botan::fmt("CT::Choice::from_int<{}>", type_name); constexpr size_t tbits = sizeof(T) * 8; - result.test_eq(tname, Botan::CT::Choice::from_int(0).as_bool(), false); + result.test_is_false(tname, Botan::CT::Choice::from_int(0).as_bool()); for(size_t b = 0; b != tbits; ++b) { const auto choice = Botan::CT::Choice::from_int(static_cast(1) << b); - result.test_eq(tname, choice.as_bool(), true); + result.test_is_true(tname, choice.as_bool()); } } }; @@ -118,7 +123,7 @@ public: Val() : m_val() {} - Val(uint8_t x) : m_val{x, x, x, x} {} + explicit Val(uint8_t x) : m_val{x, x, x, x} {} void conditional_assign(Botan::CT::Choice choice, const Val& other) { Botan::CT::conditional_assign_mem(choice, m_val, other.m_val, 4); @@ -149,12 +154,12 @@ template void test_ct_option(Test::Result& result, const T& value, const T& value2) { auto unset = Botan::CT::Option(); - result.test_eq("Unset does not have value", unset.has_value().as_bool(), false); + result.test_is_false("Unset does not have value", unset.has_value().as_bool()); result.test_throws("Unset Option throws if value is called", [&]() { unset.value(); }); - result.confirm("Unset Option returns alternative with value_or", unset.value_or(value) == value); - result.confirm("Unset Option returns alternative with value_or", unset.value_or(value2) == value2); - result.confirm( - "Unset Option returns nullopt for as_optional_vartime", unset.as_optional_vartime().has_value(), false); + result.test_is_true("Unset Option returns alternative with value_or", unset.value_or(value) == value); + result.test_is_true("Unset Option returns alternative with value_or", unset.value_or(value2) == value2); + result.test_is_false("Unset Option returns nullopt for as_optional_vartime", + unset.as_optional_vartime().has_value()); auto next = [](const T& v) -> T { T n = v; @@ -162,26 +167,26 @@ return n; }; - result.test_eq("Unset Option transform returns unset", unset.transform(next).has_value().as_bool(), false); + result.test_is_false("Unset Option transform returns unset", unset.transform(next).has_value().as_bool()); auto set = Botan::CT::Option(value); - result.test_eq("Set does have value", set.has_value().as_bool(), true); - result.confirm("Set Option has the expected value", set.value() == value); - result.confirm("Set Option returns original with value_or", set.value_or(value2) == value); - result.confirm("Set Option returns something for as_optional_vartime", - set.as_optional_vartime().value() == value); + result.test_is_true("Set does have value", set.has_value().as_bool()); + result.test_is_true("Set Option has the expected value", set.value() == value); + result.test_is_true("Set Option returns original with value_or", set.value_or(value2) == value); + + auto as_opt = set.as_optional_vartime(); + result.test_is_true("Set Option returns something for as_optional_vartime", + as_opt.has_value() && as_opt.value() == value); - result.confirm("Set Option transform returns set", set.transform(next).value() == next(value)); + result.test_is_true("Set Option transform returns set", set.transform(next).value() == next(value)); } }; BOTAN_REGISTER_TEST("ct_utils", "ct_option", CT_Option_Tests); -namespace { - template struct Poisonable { - mutable bool poisoned = false; // NOLINT(misc-non-private-member-variables-in-classes) + mutable bool poisoned = false; // NOLINT(*non-private-member-variable*) void _const_time_poison() const { poisoned = true; } @@ -192,45 +197,45 @@ return { CHECK("custom poisonable object", [](Test::Result& result) { - Poisonable p; - result.confirm("not poisoned", p.poisoned == false); + const Poisonable p; + result.test_is_true("not poisoned", p.poisoned == false); Botan::CT::poison(p); - result.confirm("poisoned", p.poisoned == true); + result.test_is_true("poisoned", p.poisoned == true); Botan::CT::unpoison(p); - result.confirm("unpoisoned", p.poisoned == false); + result.test_is_true("unpoisoned", p.poisoned == false); }), CHECK("poison multiple objects", [](Test::Result& result) { // template is useless, but p1, p2, and p3 are different types and we // want to make sure that poison_all/unpoison_all can deal with that. - Poisonable p1; - Poisonable p2; - Poisonable p3; + const Poisonable p1; + const Poisonable p2; + const Poisonable p3; - result.confirm("all not poisoned", !p1.poisoned && !p2.poisoned && !p3.poisoned); + result.test_is_true("all not poisoned", !p1.poisoned && !p2.poisoned && !p3.poisoned); Botan::CT::poison_all(p1, p2, p3); - result.confirm("all poisoned", p1.poisoned && p2.poisoned && p3.poisoned); + result.test_is_true("all poisoned", p1.poisoned && p2.poisoned && p3.poisoned); Botan::CT::unpoison_all(p1, p2, p3); - result.confirm("all unpoisoned", !p1.poisoned && !p2.poisoned && !p3.poisoned); + result.test_is_true("all unpoisoned", !p1.poisoned && !p2.poisoned && !p3.poisoned); }), CHECK("scoped poison", [](Test::Result& result) { // template is useless, but p1, p2, and p3 are different types and we // want to make sure that poison_all/unpoison_all can deal with that. - Poisonable p1; - Poisonable p2; - Poisonable p3; + const Poisonable p1; + const Poisonable p2; + const Poisonable p3; - result.confirm("not poisoned", !p1.poisoned && !p2.poisoned, !p3.poisoned); + result.test_bool_eq("not poisoned", !p1.poisoned && !p2.poisoned, !p3.poisoned); { auto scope = Botan::CT::scoped_poison(p1, p2, p3); - result.confirm("poisoned", p1.poisoned && p2.poisoned && p3.poisoned); + result.test_is_true("poisoned", p1.poisoned && p2.poisoned && p3.poisoned); } - result.confirm("unpoisoned", !p1.poisoned && !p2.poisoned && !p3.poisoned); + result.test_is_true("unpoisoned", !p1.poisoned && !p2.poisoned && !p3.poisoned); }), CHECK("poison a range of poisonable objects", @@ -238,31 +243,31 @@ auto is_poisoned = [](const auto& p) { return p.poisoned; }; std::vector> v(10); - result.confirm("none poisoned", std::none_of(v.begin(), v.end(), is_poisoned)); + result.test_is_true("none poisoned", std::none_of(v.begin(), v.end(), is_poisoned)); Botan::CT::poison_range(v); - result.confirm("all poisoned", std::all_of(v.begin(), v.end(), is_poisoned)); + result.test_is_true("all poisoned", std::all_of(v.begin(), v.end(), is_poisoned)); Botan::CT::unpoison_range(v); - result.confirm("all unpoisoned", std::none_of(v.begin(), v.end(), is_poisoned)); + result.test_is_true("all unpoisoned", std::none_of(v.begin(), v.end(), is_poisoned)); }), CHECK("poison a poisonable objects with driveby_poison", [](Test::Result& result) { Poisonable p; - result.confirm("not poisoned", p.poisoned == false); + result.test_is_true("not poisoned", p.poisoned == false); Poisonable p_poisoned = Botan::CT::driveby_poison(std::move(p)); // NOLINT(hicpp-move-const-arg,performance-move-const-arg) - result.confirm("poisoned", p_poisoned.poisoned == true); - Poisonable p_unpoisoned = Botan::CT::driveby_unpoison( + result.test_is_true("poisoned", p_poisoned.poisoned == true); + const Poisonable p_unpoisoned = Botan::CT::driveby_unpoison( std::move(p_poisoned)); // NOLINT(hicpp-move-const-arg,performance-move-const-arg) - result.confirm("unpoisoned", p_unpoisoned.poisoned == false); + result.test_is_true("unpoisoned", p_unpoisoned.poisoned == false); }), }; } -} // namespace - BOTAN_REGISTER_TEST_FN("ct_utils", "ct_poison", test_higher_level_ct_poison); +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_dh.cpp botan3-3.12.0+dfsg/src/tests/test_dh.cpp --- botan3-3.7.1+dfsg/src/tests/test_dh.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_dh.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -28,7 +28,7 @@ std::unique_ptr load_our_key(const std::string& /*header*/, const VarMap& vars) override { const Botan::BigInt p = vars.get_req_bn("P"); - const Botan::BigInt q = vars.get_opt_bn("Q", 0); + const Botan::BigInt q = vars.get_opt_bn("Q", Botan::BigInt::zero()); const Botan::BigInt g = vars.get_req_bn("G"); const Botan::BigInt x = vars.get_req_bn("X"); @@ -45,7 +45,7 @@ std::vector load_their_key(const std::string& /*header*/, const VarMap& vars) override { const Botan::BigInt p = vars.get_req_bn("P"); - const Botan::BigInt q = vars.get_opt_bn("Q", 0); + const Botan::BigInt q = vars.get_opt_bn("Q", Botan::BigInt::zero()); const Botan::BigInt g = vars.get_req_bn("G"); const Botan::BigInt y = vars.get_req_bn("Y"); @@ -57,7 +57,7 @@ } }(); - Botan::DH_PublicKey key(group, y); + const Botan::DH_PublicKey key(group, y); return key.public_value(); } @@ -75,12 +75,12 @@ result.test_throws("agreement input too big", "DH agreement - invalid key provided", [&kas]() { const BigInt too_big("584580020955360946586837552585233629614212007514394561597561641914945762794672"); - kas->derive_key(16, BigInt::encode(too_big)); + kas->derive_key(16, too_big.serialize()); }); result.test_throws("agreement input too small", "DH agreement - invalid key provided", [&kas]() { const BigInt too_small("1"); - kas->derive_key(16, BigInt::encode(too_small)); + kas->derive_key(16, too_small.serialize()); }); return {result}; @@ -101,10 +101,10 @@ const Botan::BigInt g = vars.get_req_bn("G"); const Botan::BigInt pubkey = vars.get_req_bn("InvalidKey"); - Botan::DL_Group group(p, q, g); + const Botan::DL_Group group(p, q, g); auto key = std::make_unique(group, pubkey); - result.test_eq("public key fails check", key->check_key(this->rng(), false), false); + result.test_is_false("public key fails check", key->check_key(this->rng(), false)); return result; } }; diff -Nru botan3-3.7.1+dfsg/src/tests/test_dilithium.cpp botan3-3.12.0+dfsg/src/tests/test_dilithium.cpp --- botan3-3.7.1+dfsg/src/tests/test_dilithium.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_dilithium.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,7 +9,6 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include "test_rng.h" #include "tests.h" #if defined(BOTAN_HAS_DILITHIUM_COMMON) @@ -17,18 +16,21 @@ #include #include #include - #include #include "test_pubkey.h" + #include "test_rng.h" #endif namespace Botan_Tests { +namespace { + #if defined(BOTAN_HAS_DILITHIUM_COMMON) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_SHA3) template class Dilithium_KAT_Tests : public Text_Based_Test { public: + // NOLINTNEXTLINE(*crtp-constructor-accessibility) Dilithium_KAT_Tests() : Text_Based_Test(DerivedT::test_vector, "Seed,Msg,HashPk,HashSk,HashSig", "Sig") {} Test::Result run_one_test(const std::string& name, const VarMap& vars) override { @@ -46,34 +48,34 @@ auto dilithium_test_rng = std::make_unique(ref_seed); - Botan::Dilithium_PrivateKey priv_key(*dilithium_test_rng, DerivedT::mode); + const Botan::Dilithium_PrivateKey priv_key(*dilithium_test_rng, DerivedT::mode); - result.test_eq( + result.test_bin_eq( "generated expected private key hash", sha3_256->process(priv_key.private_key_bits()), ref_sk_hash); - result.test_eq( + result.test_bin_eq( "generated expected public key hash", sha3_256->process(priv_key.public_key_bits()), ref_pk_hash); auto signer = Botan::PK_Signer(priv_key, *dilithium_test_rng, DerivedT::sign_param); auto signature = signer.sign_message(ref_msg.data(), ref_msg.size(), *dilithium_test_rng); - result.test_eq("generated expected signature hash", sha3_256->process(signature), ref_sig_hash); + result.test_bin_eq("generated expected signature hash", sha3_256->process(signature), ref_sig_hash); if(!ref_sig.empty()) { - result.test_eq("generated expected signature", signature, ref_sig); + result.test_bin_eq("generated expected signature", signature, ref_sig); } - Botan::Dilithium_PublicKey pub_key(priv_key.public_key_bits(), DerivedT::mode); + const Botan::Dilithium_PublicKey pub_key(priv_key.public_key_bits(), DerivedT::mode); auto verifier = Botan::PK_Verifier(pub_key, ""); verifier.update(ref_msg.data(), ref_msg.size()); - result.confirm("signature verifies", verifier.check_signature(signature.data(), signature.size())); + result.test_is_true("signature verifies", verifier.check_signature(signature.data(), signature.size())); // test validating incorrect wrong signature auto mutated_signature = Test::mutate_vec(signature, this->rng()); - result.confirm("invalid signature rejected", - !verifier.check_signature(mutated_signature.data(), mutated_signature.size())); + result.test_is_true("invalid signature rejected", + !verifier.check_signature(mutated_signature.data(), mutated_signature.size())); verifier.update(ref_msg.data(), ref_msg.size()); - result.confirm("signature verifies", verifier.check_signature(signature.data(), signature.size())); + result.test_is_true("signature verifies", verifier.check_signature(signature.data(), signature.size())); return result; } @@ -153,41 +155,44 @@ 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(*rng, mode); + const Botan::Dilithium_PrivateKey priv_key(*rng, mode); const Botan::Dilithium_PublicKey& pub_key = priv_key; - result.test_eq("key strength", priv_key.estimated_strength(), strength); - result.test_eq("key length", priv_key.key_length(), psid); - result.test_eq("key strength", pub_key.estimated_strength(), strength); - result.test_eq("key length", pub_key.key_length(), psid); + result.test_sz_eq("key strength", priv_key.estimated_strength(), strength); + result.test_sz_eq("key length", priv_key.key_length(), psid); + result.test_sz_eq("key strength", pub_key.estimated_strength(), strength); + result.test_sz_eq("key length", pub_key.key_length(), psid); const auto sig_before_codec = sign(priv_key, msgvec); const auto priv_key_encoded = priv_key.private_key_bits(); const auto pub_key_encoded = priv_key.public_key_bits(); - Botan::Dilithium_PrivateKey priv_key_decoded(priv_key_encoded, mode); - Botan::Dilithium_PublicKey pub_key_decoded(pub_key_encoded, mode); + const Botan::Dilithium_PrivateKey priv_key_decoded(priv_key_encoded, mode); + const Botan::Dilithium_PublicKey pub_key_decoded(pub_key_encoded, mode); const auto sig_after_codec = sign(priv_key_decoded, msgvec); - result.confirm("Pubkey: before, Sig: before", verify(pub_key, msgvec, sig_before_codec)); - result.confirm("Pubkey: before, Sig: after", verify(pub_key, msgvec, sig_after_codec)); - result.confirm("Pubkey: after, Sig: after", verify(pub_key_decoded, msgvec, sig_after_codec)); - result.confirm("Pubkey: after, Sig: before", verify(pub_key_decoded, msgvec, sig_before_codec)); - result.confirm("Pubkey: recalc'ed Sig: before", verify(priv_key_decoded, msgvec, sig_before_codec)); - result.confirm("Pubkey: recalc'ed Sig: after", verify(priv_key_decoded, msgvec, sig_after_codec)); + result.test_is_true("Pubkey: before, Sig: before", verify(pub_key, msgvec, sig_before_codec)); + result.test_is_true("Pubkey: before, Sig: after", verify(pub_key, msgvec, sig_after_codec)); + result.test_is_true("Pubkey: after, Sig: after", verify(pub_key_decoded, msgvec, sig_after_codec)); + result.test_is_true("Pubkey: after, Sig: before", verify(pub_key_decoded, msgvec, sig_before_codec)); + result.test_is_true("Pubkey: recalc'ed Sig: before", verify(priv_key_decoded, msgvec, sig_before_codec)); + result.test_is_true("Pubkey: recalc'ed Sig: after", verify(priv_key_decoded, msgvec, sig_after_codec)); auto tampered_msgvec = msgvec; tampered_msgvec.front() = 'X'; - result.confirm("Pubkey: before, Broken Sig: before", !verify(pub_key, tampered_msgvec, sig_before_codec)); - result.confirm("Pubkey: before, Broken Sig: after", !verify(pub_key, tampered_msgvec, sig_after_codec)); - result.confirm("Pubkey: after, Broken Sig: after", - !verify(pub_key_decoded, tampered_msgvec, sig_after_codec)); - result.confirm("Pubkey: after, Broken Sig: before", - !verify(pub_key_decoded, tampered_msgvec, sig_before_codec)); - result.confirm("Pubkey: recalc'ed Sig: before", !verify(priv_key_decoded, tampered_msgvec, sig_before_codec)); - result.confirm("Pubkey: recalc'ed Sig: after", !verify(priv_key_decoded, tampered_msgvec, sig_after_codec)); + result.test_is_true("Pubkey: before, Broken Sig: before", + !verify(pub_key, tampered_msgvec, sig_before_codec)); + result.test_is_true("Pubkey: before, Broken Sig: after", !verify(pub_key, tampered_msgvec, sig_after_codec)); + result.test_is_true("Pubkey: after, Broken Sig: after", + !verify(pub_key_decoded, tampered_msgvec, sig_after_codec)); + result.test_is_true("Pubkey: after, Broken Sig: before", + !verify(pub_key_decoded, tampered_msgvec, sig_before_codec)); + result.test_is_true("Pubkey: recalc'ed Sig: before", + !verify(priv_key_decoded, tampered_msgvec, sig_before_codec)); + result.test_is_true("Pubkey: recalc'ed Sig: after", + !verify(priv_key_decoded, tampered_msgvec, sig_after_codec)); // decoding via generic pk_algs.h const auto generic_pubkey_decoded = Botan::load_public_key(pub_key.algorithm_identifier(), pub_key_encoded); @@ -199,12 +204,12 @@ const auto sig_after_generic_codec = sign(*generic_privkey_decoded, msgvec); - result.confirm("verification with generic public key", - verify(*generic_pubkey_decoded, msgvec, sig_before_codec)); - result.confirm("verification of signature with generic private key", - verify(*generic_pubkey_decoded, msgvec, sig_after_generic_codec)); - result.confirm("verification with generic private key", - verify(*generic_privkey_decoded, msgvec, sig_before_codec)); + result.test_is_true("verification with generic public key", + verify(*generic_pubkey_decoded, msgvec, sig_before_codec)); + result.test_is_true("verification of signature with generic private key", + verify(*generic_pubkey_decoded, msgvec, sig_after_generic_codec)); + result.test_is_true("verification with generic private key", + verify(*generic_privkey_decoded, msgvec, sig_before_codec)); return result; } @@ -238,7 +243,7 @@ class Dilithium_Keygen_Tests final : public PK_Key_Generation_Test { public: std::vector keygen_params() const override { - std::vector all_instances = { + const std::vector all_instances = { "Dilithium-4x4-AES-r3", "Dilithium-6x5-AES-r3", "Dilithium-8x7-AES-r3", @@ -249,11 +254,14 @@ "ML-DSA-6x5", "ML-DSA-8x7", }; + std::vector available_instances; - std::copy_if(all_instances.begin(), - all_instances.end(), - std::back_inserter(available_instances), - [](const std::string& instance) { return Botan::DilithiumMode(instance).is_available(); }); + + for(const auto& mode : all_instances) { + if(Botan::DilithiumMode(mode).is_available()) { + available_instances.push_back(mode); + } + } return available_instances; } @@ -278,4 +286,6 @@ #endif +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_dl_group.cpp botan3-3.12.0+dfsg/src/tests/test_dl_group.cpp --- botan3-3.7.1+dfsg/src/tests/test_dl_group.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_dl_group.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #if defined(BOTAN_HAS_DL_GROUP) #include + #include #include #endif @@ -32,19 +33,10 @@ static Test::Result test_dl_errors() { Test::Result result("DL_Group errors"); result.test_throws("Uninitialized", "DL_Group uninitialized", []() { - Botan::DL_Group dl; + const Botan::DL_Group dl; dl.get_p(); }); - #if !defined(BOTAN_HAS_SANITIZER_UNDEFINED) - result.test_throws("Bad generator param", "DL_Group unknown PrimeType", []() { - // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) - auto invalid_type = static_cast(9); - Botan::Null_RNG null_rng; - Botan::DL_Group dl(null_rng, invalid_type, 1024); - }); - #endif - return result; } @@ -59,21 +51,21 @@ const auto group1 = Botan::DL_Group::from_PEM(pem1); - result.test_eq("Same p in X9.42 decoding", group1.get_p(), orig.get_p()); - result.test_eq("Same q in X9.42 decoding", group1.get_q(), orig.get_q()); - result.test_eq("Same g in X9.42 decoding", group1.get_g(), orig.get_g()); + result.test_bn_eq("Same p in X9.42 decoding", group1.get_p(), orig.get_p()); + result.test_bn_eq("Same q in X9.42 decoding", group1.get_q(), orig.get_q()); + result.test_bn_eq("Same g in X9.42 decoding", group1.get_g(), orig.get_g()); const auto group2 = Botan::DL_Group::from_PEM(pem2); - result.test_eq("Same p in X9.57 decoding", group2.get_p(), orig.get_p()); - result.test_eq("Same q in X9.57 decoding", group2.get_q(), orig.get_q()); - result.test_eq("Same g in X9.57 decoding", group2.get_g(), orig.get_g()); + result.test_bn_eq("Same p in X9.57 decoding", group2.get_p(), orig.get_p()); + result.test_bn_eq("Same q in X9.57 decoding", group2.get_q(), orig.get_q()); + result.test_bn_eq("Same g in X9.57 decoding", group2.get_g(), orig.get_g()); const auto group3 = Botan::DL_Group::from_PEM(pem3); - result.test_eq("Same p in X9.57 decoding", group3.get_p(), orig.get_p()); + result.test_bn_eq("Same p in X9.57 decoding", group3.get_p(), orig.get_p()); // no q in PKCS #3 format - result.test_eq("Same g in X9.57 decoding", group3.get_g(), orig.get_g()); + result.test_bn_eq("Same g in X9.57 decoding", group3.get_g(), orig.get_g()); return result; } @@ -90,61 +82,64 @@ 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); - result.test_eq("DH q size", dh1050.get_q().bits(), 175); - result.test_lte("DH g size", dh1050.get_g().bits(), 1050); - result.test_eq("DH group verifies", dh1050.verify_group(rng, false), true); - - Botan::DL_Group dh_implicit_q(rng, Botan::DL_Group::Prime_Subgroup, 1040); - result.test_eq("DH p size", dh_implicit_q.get_p().bits(), 1040); - result.test_eq("DH q size", dh_implicit_q.get_q().bits(), Botan::dl_exponent_size(1040)); - result.test_lte("DH g size", dh_implicit_q.get_g().bits(), 1040); - result.test_eq("DH group verifies", dh_implicit_q.verify_group(rng, false), true); + const Botan::DL_Group dh1050(rng, Botan::DL_Group::Prime_Subgroup, 1050, 175); + result.test_sz_eq("DH p size", dh1050.get_p().bits(), 1050); + result.test_sz_eq("DH q size", dh1050.get_q().bits(), 175); + result.test_sz_lte("DH g size", dh1050.get_g().bits(), 1050); + result.test_is_true("DH group verifies", dh1050.verify_group(rng, false)); + + const Botan::DL_Group dh_implicit_q(rng, Botan::DL_Group::Prime_Subgroup, 1040); + result.test_sz_eq("DH p size", dh_implicit_q.get_p().bits(), 1040); + result.test_sz_eq("DH q size", dh_implicit_q.get_q().bits(), Botan::dl_exponent_size(1040)); + result.test_sz_lte("DH g size", dh_implicit_q.get_g().bits(), 1040); + result.test_is_true("DH group verifies", dh_implicit_q.verify_group(rng, false)); if(Test::run_long_tests()) { - Botan::DL_Group dh_strong(rng, Botan::DL_Group::Strong, 1025); - result.test_eq("DH p size", dh_strong.get_p().bits(), 1025); - result.test_eq("DH q size", dh_strong.get_q().bits(), 1024); - result.test_eq("DH group verifies", dh_strong.verify_group(rng, false), true); + const Botan::DL_Group dh_strong(rng, Botan::DL_Group::Strong, 1025); + result.test_sz_eq("DH p size", dh_strong.get_p().bits(), 1025); + result.test_sz_eq("DH q size", dh_strong.get_q().bits(), 1024); + result.test_is_true("DH group verifies", dh_strong.verify_group(rng, false)); } #if defined(BOTAN_HAS_SHA1) - Botan::DL_Group dsa1024(rng, Botan::DL_Group::DSA_Kosherizer, 1024); - result.test_eq("DSA p size", dsa1024.get_p().bits(), 1024); - result.test_eq("DSA q size", dsa1024.get_q().bits(), 160); - result.test_lte("DSA g size", dsa1024.get_g().bits(), 1024); - result.test_eq("DSA group verifies", dsa1024.verify_group(rng, false), true); + const Botan::DL_Group dsa1024(rng, Botan::DL_Group::DSA_Kosherizer, 1024); + result.test_sz_eq("DSA p size", dsa1024.get_p().bits(), 1024); + result.test_sz_eq("DSA q size", dsa1024.get_q().bits(), 160); + result.test_sz_lte("DSA g size", dsa1024.get_g().bits(), 1024); + result.test_is_true("DSA group verifies", dsa1024.verify_group(rng, false)); - const std::vector short_seed(16); const std::vector invalid_seed(20); - const std::vector working_seed = Botan::hex_decode("0000000000000000000000000000000000000021"); - result.test_throws("DSA seed does not generate group", "DL_Group: The seed given does not generate a DSA group", - [&rng, &invalid_seed]() { Botan::DL_Group dsa(rng, invalid_seed, 1024, 160); }); + [&rng, &invalid_seed]() { const Botan::DL_Group dsa(rng, invalid_seed, 1024, 160); }); + const std::vector short_seed(16); result.test_throws( "DSA seed is too short", "Generating a DSA parameter set with a 160 bit long q requires a seed at least as many bits long", - [&rng, &short_seed]() { Botan::DL_Group dsa(rng, short_seed, 1024, 160); }); + [&rng, &short_seed]() { const Botan::DL_Group dsa(rng, short_seed, 1024, 160); }); + + const std::vector working_seed = Botan::hex_decode("0000000000000000000000000000000000000021"); + const Botan::DL_Group dsa(rng, working_seed, 1024, 160); + result.test_is_true("DSA group from working seed verifies", dsa.verify_group(rng, false)); // From FIPS 186-3 test data const std::vector seed = Botan::hex_decode("1F5DA0AF598EEADEE6E6665BF880E63D8B609BA2"); - result.test_throws("invalid params", [&]() { Botan::DL_Group invalid(rng, seed, 1024, 224); }); - result.test_throws("invalid params", [&]() { Botan::DL_Group invalid(rng, seed, 3072, 224); }); - result.test_throws("invalid params", [&]() { Botan::DL_Group invalid(rng, seed, 2048, 256); }); + result.test_throws("invalid params", [&]() { const Botan::DL_Group invalid(rng, seed, 1024, 224); }); + result.test_throws("invalid params", [&]() { const Botan::DL_Group invalid(rng, seed, 3072, 224); }); + result.test_throws("invalid params", [&]() { const Botan::DL_Group invalid(rng, seed, 2048, 256); }); - Botan::DL_Group dsa_from_seed(rng, seed, 1024, 160); + const Botan::DL_Group dsa_from_seed(rng, seed, 1024, 160); - result.test_eq( + result.test_bn_eq( "DSA q from seed", dsa_from_seed.get_q(), Botan::BigInt("0xAB1A788BCE3C557A965A5BFA6908FAA665FDEB7D")); // Modulo just to avoid embedding entire 1024-bit P in src file - result.test_eq("DSA p from seed", static_cast(dsa_from_seed.get_p() % 4294967291), size_t(2513712339)); + result.test_sz_eq( + "DSA p from seed", static_cast(dsa_from_seed.get_p() % 4294967291), size_t(2513712339)); - result.test_eq("DSA group from seed verifies", dsa_from_seed.verify_group(rng, false), true); + result.test_is_true("DSA group from seed verifies", dsa_from_seed.verify_group(rng, false)); #endif result.end_timer(); @@ -177,24 +172,24 @@ // Confirm we can load every group we expect auto group = Botan::DL_Group::from_name(name); - result.test_ne("DL_Group p is set", group.get_p(), 0); - result.test_ne("DL_Group g is set", group.get_g(), 0); + result.test_bn_ne("DL_Group p is set", group.get_p(), 0); + result.test_bn_ne("DL_Group g is set", group.get_g(), 0); const size_t strength = group.estimated_strength(); // 8192 bit ~~ 2**202 strength - result.confirm("Plausible strength", strength >= 80 && strength < 210); + result.test_is_true("Plausible strength", strength >= 80 && strength < 210); - result.confirm("Expected source", group.source() == Botan::DL_Group_Source::Builtin); + result.test_enum_eq("Expected source", group.source(), Botan::DL_Group_Source::Builtin); if(name.find("modp/srp/") == std::string::npos) { - result.test_ne("DL_Group q is set", group.get_q(), 0); + result.test_bn_ne("DL_Group q is set", group.get_q(), 0); } else { - result.test_eq("DL_Group q is not set for SRP groups", group.get_q(), 0); + result.test_bn_eq("DL_Group q is not set for SRP groups", group.get_q(), 0); } if(group.p_bits() <= 1536 || Test::run_long_tests()) { - result.test_eq(name + " verifies", group.verify_group(this->rng()), true); + result.test_is_true(name + " verifies", group.verify_group(this->rng())); } } result.end_timer(); diff -Nru botan3-3.7.1+dfsg/src/tests/test_dlies.cpp botan3-3.12.0+dfsg/src/tests/test_dlies.cpp --- botan3-3.7.1+dfsg/src/tests/test_dlies.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_dlies.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,6 +12,7 @@ #include #include #include + #include #endif namespace Botan_Tests { @@ -42,13 +43,13 @@ auto kdf = Botan::KDF::create(kdf_algo); if(!kdf) { - result.test_note("Skipping due to missing KDF: " + kdf_algo); + result.test_note("Skipping due to missing KDF", kdf_algo); return result; } auto mac = Botan::MAC::create(mac_algo); if(!mac) { - result.test_note("Skipping due to missing MAC: " + mac_algo); + result.test_note("Skipping due to missing MAC", mac_algo); return result; } @@ -61,7 +62,7 @@ dec = Botan::Cipher_Mode::create(cipher_algo, Botan::Cipher_Dir::Decryption); if(!enc || !dec) { - result.test_note("Skipping due to missing cipher: " + mac_algo); + result.test_note("Skipping due to missing cipher", cipher_algo); return result; } @@ -70,8 +71,8 @@ auto group = Botan::DL_Group::from_name(group_name); - Botan::DH_PrivateKey from(group, x1); - Botan::DH_PrivateKey to(group, x2); + const Botan::DH_PrivateKey from(group, x1); + const Botan::DH_PrivateKey to(group, x2); Botan::DLIES_Encryptor encryptor( from, this->rng(), kdf->new_object(), std::move(enc), cipher_key_len, mac->new_object(), mac_key_len); @@ -85,8 +86,8 @@ encryptor.set_other_key(to.public_value()); - result.test_eq("encryption", encryptor.encrypt(input, this->rng()), expected); - result.test_eq("decryption", decryptor.decrypt(expected), input); + result.test_bin_eq("encryption", encryptor.encrypt(input, this->rng()), expected); + result.test_bin_eq("decryption", decryptor.decrypt(expected), input); check_invalid_ciphertexts(result, decryptor, input, expected, this->rng()); @@ -99,8 +100,8 @@ Test::Result test_xor() { Test::Result result("DLIES XOR"); - std::vector kdfs = {"KDF2(SHA-512)", "KDF1-18033(SHA-512)"}; - std::vector macs = {"HMAC(SHA-512)", "CMAC(AES-128)"}; + const std::vector kdfs = {"KDF2(SHA-512)", "KDF1-18033(SHA-512)"}; + const std::vector macs = {"HMAC(SHA-512)", "CMAC(AES-128)"}; const size_t mac_key_len = 16; @@ -111,14 +112,14 @@ auto group = Botan::DL_Group::from_name("modp/ietf/2048"); - Botan::DH_PrivateKey alice(*rng, group); - Botan::DH_PrivateKey bob(*rng, group); + const Botan::DH_PrivateKey alice(*rng, group); + const Botan::DH_PrivateKey bob(*rng, group); for(const auto& kfunc : kdfs) { kdf = Botan::KDF::create(kfunc); if(!kdf) { - result.test_note("Skipping due to missing KDF: " + kfunc); + result.test_note("Skipping due to missing KDF", kfunc); continue; } @@ -126,7 +127,7 @@ mac = Botan::MAC::create(mfunc); if(!mac) { - result.test_note("Skipping due to missing MAC: " + mfunc); + result.test_note("Skipping due to missing MAC", mfunc); continue; } @@ -146,7 +147,7 @@ // 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); + result.test_bin_eq("decryption", decryptor.decrypt(ciphertext), plaintext); check_invalid_ciphertexts(result, decryptor, unlock(plaintext), ciphertext, *rng); } diff -Nru botan3-3.7.1+dfsg/src/tests/test_ec_group.cpp botan3-3.12.0+dfsg/src/tests/test_ec_group.cpp --- botan3-3.7.1+dfsg/src/tests/test_ec_group.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_ec_group.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -15,9 +15,13 @@ #include #include #include - #include + #include #include + #include #include + #if defined(BOTAN_HAS_ECDSA) + #include + #endif #endif namespace Botan_Tests { @@ -80,7 +84,7 @@ Botan::EC_Point create_random_point(Botan::RandomNumberGenerator& rng, const Botan::EC_Group& group) { const Botan::BigInt& p = group.get_p(); - auto mod_p = Botan::Modular_Reducer::for_public_modulus(p); + auto mod_p = Botan::Barrett_Reduction::for_public_modulus(p); for(;;) { const Botan::BigInt x = Botan::BigInt::random_integer(rng, 1, p); @@ -112,9 +116,8 @@ const Botan::EC_Point pt = create_random_point(this->rng(), group); - std::vector blind_ws; - try { + std::vector blind_ws; const size_t trials = (Test::run_long_tests() ? 10 : 3); for(size_t i = 0; i < trials; ++i) { const Botan::BigInt a = test_integer(rng(), group.get_order_bits(), group.get_order()); @@ -132,28 +135,28 @@ Botan::EC_Point A1 = P + Q; Botan::EC_Point A2 = Q + P; - result.test_eq("p + q", A1, R); - result.test_eq("q + p", A2, R); + result.test_bin_eq("p + q", A1.xy_bytes(), R.xy_bytes()); + result.test_bin_eq("q + p", A2.xy_bytes(), R.xy_bytes()); A1.force_affine(); A2.force_affine(); - result.test_eq("p + q", A1, R); - result.test_eq("q + p", A2, R); + result.test_bin_eq("p + q", A1.xy_bytes(), R.xy_bytes()); + result.test_bin_eq("q + p", A2.xy_bytes(), R.xy_bytes()); - result.test_eq("p on the curve", P.on_the_curve(), true); - result.test_eq("q on the curve", Q.on_the_curve(), true); - result.test_eq("r on the curve", R.on_the_curve(), true); - - result.test_eq("P1", P1, P); - result.test_eq("Q1", Q1, Q); - result.test_eq("R1", R1, R); + result.test_is_true("p on the curve", P.on_the_curve()); + result.test_is_true("q on the curve", Q.on_the_curve()); + result.test_is_true("r on the curve", R.on_the_curve()); + + result.test_bin_eq("P1", P1.xy_bytes(), P.xy_bytes()); + result.test_bin_eq("Q1", Q1.xy_bytes(), Q.xy_bytes()); + result.test_bin_eq("R1", R1.xy_bytes(), R.xy_bytes()); P1.force_affine(); Q1.force_affine(); R1.force_affine(); - result.test_eq("P1", P1, P); - result.test_eq("Q1", Q1, Q); - result.test_eq("R1", R1, R); + result.test_bin_eq("P1", P1.xy_bytes(), P.xy_bytes()); + result.test_bin_eq("Q1", Q1.xy_bytes(), Q.xy_bytes()); + result.test_bin_eq("R1", R1.xy_bytes(), R.xy_bytes()); } } catch(std::exception& e) { result.test_failure(group_name, e.what()); @@ -181,21 +184,21 @@ const auto group = Botan::EC_Group::from_name(group_name); - result.confirm("EC_Group is known", group.get_curve_oid().has_value()); - 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_is_true("EC_Group is known", group.get_curve_oid().has_value()); + result.test_is_true("EC_Group is considered valid", group.verify_group(this->rng(), true)); + result.test_is_true("EC_Group is not considered explicit encoding", !group.used_explicit_encoding()); - result.test_eq("EC_Group has correct bit size", group.get_p().bits(), group.get_p_bits()); - result.test_eq("EC_Group has byte size", group.get_p().bytes(), group.get_p_bytes()); + result.test_sz_eq("EC_Group has correct bit size", group.get_p().bits(), group.get_p_bits()); + result.test_sz_eq("EC_Group has byte size", group.get_p().bytes(), group.get_p_bytes()); - result.test_eq("EC_Group has cofactor == 1", group.get_cofactor(), 1); + result.test_bn_eq("EC_Group has cofactor == 1", group.get_cofactor(), 1); const Botan::OID from_order = Botan::EC_Group::EC_group_identity_from_order(group.get_order()); - result.test_eq( + result.test_str_eq( "EC_group_identity_from_order works", from_order.to_string(), group.get_curve_oid().to_string()); - result.confirm("Same group is same", group == Botan::EC_Group::from_name(group_name)); + result.test_is_true("Same group is same", group == Botan::EC_Group::from_name(group_name)); try { const Botan::EC_Group copy(group.get_curve_oid(), @@ -206,34 +209,34 @@ group.get_g_y(), group.get_order()); - result.confirm("Same group is same even with copy", group == copy); + result.test_is_true("Same group is same even with copy", group == copy); } catch(Botan::Invalid_Argument&) {} const auto group_der_oid = group.DER_encode(); const Botan::EC_Group group_via_oid(group_der_oid); - result.confirm("EC_Group via OID is not considered explict encoding", - !group_via_oid.used_explicit_encoding()); + result.test_is_true("EC_Group via OID is not considered explicit encoding", + !group_via_oid.used_explicit_encoding()); const auto group_der_explicit = group.DER_encode(Botan::EC_Group_Encoding::Explicit); const Botan::EC_Group group_via_explicit(group_der_explicit); - result.confirm("EC_Group via explicit DER is considered explict encoding", - group_via_explicit.used_explicit_encoding()); + result.test_is_true("EC_Group via explicit DER is considered explicit encoding", + group_via_explicit.used_explicit_encoding()); if(group.a_is_minus_3()) { - result.test_eq("Group A equals -3", group.get_a(), group.get_p() - 3); + result.test_bn_eq("Group A equals -3", group.get_a(), group.get_p() - 3); } else { - result.test_ne("Group " + group_name + " A does not equal -3", group.get_a(), group.get_p() - 3); + result.test_bn_ne("Group " + group_name + " A does not equal -3", group.get_a(), group.get_p() - 3); } if(group.a_is_zero()) { - result.test_eq("Group A is zero", group.get_a(), BigInt(0)); + result.test_bn_eq("Group A is zero", group.get_a(), BigInt(0)); } else { - result.test_ne("Group " + group_name + " A does not equal zero", group.get_a(), BigInt(0)); + result.test_bn_ne("Group " + group_name + " A does not equal zero", group.get_a(), BigInt(0)); } #if defined(BOTAN_HAS_LEGACY_EC_POINT) const auto pt_mult_by_order = group.get_base_point() * group.get_order(); - result.confirm("Multiplying point by the order results in zero point", pt_mult_by_order.is_zero()); + result.test_is_true("Multiplying point by the order results in zero point", pt_mult_by_order.is_zero()); // get a valid point Botan::EC_Point p = group.get_base_point() * this->rng().next_nonzero_byte(); @@ -244,13 +247,13 @@ 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()); + result.test_bn_eq("affine x after copy", p.get_affine_x(), q.get_affine_x()); + result.test_bn_eq("affine y after copy", p.get_affine_y(), q.get_affine_y()); q.force_affine(); - 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()); + result.test_bn_eq("affine x after copy", p.get_affine_x(), q.get_affine_x()); + result.test_bn_eq("affine y after copy", p.get_affine_y(), q.get_affine_y()); test_ser_der(result, group); test_basic_math(result, group); @@ -277,34 +280,46 @@ for(auto scheme : {Botan::EC_Point_Format::Uncompressed, Botan::EC_Point_Format::Compressed, Botan::EC_Point_Format::Hybrid}) { - result.test_eq("encoded/decode rt works", group.OS2ECP(pt.encode(scheme)), pt); - result.test_eq("encoded/decode rt works", group.OS2ECP(zero.encode(scheme)), zero); + try { + result.test_bin_eq("encoded/decode rt works", group.OS2ECP(pt.encode(scheme)).xy_bytes(), pt.xy_bytes()); + } catch(Botan::Exception& e) { + result.test_failure("Failed to round trip encode a random point", e.what()); + } + + try { + result.test_is_true("encoded/decode rt works", group.OS2ECP(zero.encode(scheme)).is_zero()); + } catch(Botan::Exception& e) { + result.test_failure("Failed to round trip encode the identity element", e.what()); + } } } static void test_basic_math(Test::Result& result, const Botan::EC_Group& group) { const Botan::EC_Point& G = group.get_base_point(); - Botan::EC_Point p1 = G * 2; + const auto G2 = G * 2; + const auto G3 = G * 3; + + Botan::EC_Point p1 = G2; p1 += G; - result.test_eq("point addition", p1, G * 3); + result.test_bin_eq("point addition", p1.xy_bytes(), G3.xy_bytes()); - p1 -= G * 2; + p1 -= G2; - result.test_eq("point subtraction", p1, G); + result.test_bin_eq("point subtraction", p1.xy_bytes(), G.xy_bytes()); // The scalar multiplication algorithm relies on this being true: try { - Botan::EC_Point zero_coords = group.point(0, 0); - result.confirm("point (0,0) is not on the curve", !zero_coords.on_the_curve()); + const Botan::EC_Point zero_coords = group.point(0, 0); + result.test_is_true("point (0,0) is not on the curve", !zero_coords.on_the_curve()); } catch(Botan::Exception&) { result.test_success("point (0,0) is rejected"); } } void test_point_swap(Test::Result& result, const Botan::EC_Group& group) { - Botan::EC_Point a(create_random_point(this->rng(), group)); + const 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); @@ -312,8 +327,8 @@ Botan::EC_Point d(b); d.swap(c); - result.test_eq("swap correct", a, d); - result.test_eq("swap correct", b, c); + result.test_bin_eq("swap correct", a.xy_bytes(), d.xy_bytes()); + result.test_bin_eq("swap correct", b.xy_bytes(), c.xy_bytes()); } static void test_zeropoint(Test::Result& result, const Botan::EC_Group& group) { @@ -324,35 +339,35 @@ const Botan::EC_Point p1 = group.get_base_point() * 2; - result.confirm("point is on the curve", p1.on_the_curve()); - result.confirm("point is not zero", !p1.is_zero()); + result.test_is_true("point is on the curve", p1.on_the_curve()); + result.test_is_true("point is not zero", !p1.is_zero()); Botan::EC_Point p2 = p1; p2 -= p1; - result.confirm("p - q with q = p results in zero", p2.is_zero()); + result.test_is_true("p - q with q = p results in zero", p2.is_zero()); const Botan::EC_Point minus_p1 = -p1; - result.confirm("point is on the curve", minus_p1.on_the_curve()); + result.test_is_true("point is on the curve", minus_p1.on_the_curve()); const Botan::EC_Point shouldBeZero = p1 + minus_p1; - result.confirm("point is on the curve", shouldBeZero.on_the_curve()); - result.confirm("point is zero", shouldBeZero.is_zero()); + result.test_is_true("point is on the curve", shouldBeZero.on_the_curve()); + result.test_is_true("point is zero", shouldBeZero.is_zero()); - result.test_eq("minus point x", minus_p1.get_affine_x(), p1.get_affine_x()); - result.test_eq("minus point y", minus_p1.get_affine_y(), group.get_p() - p1.get_affine_y()); + result.test_bn_eq("minus point x", minus_p1.get_affine_x(), p1.get_affine_x()); + result.test_bn_eq("minus point y", minus_p1.get_affine_y(), group.get_p() - p1.get_affine_y()); - result.confirm("zero point is zero", zero.is_zero()); - result.confirm("zero point is on the curve", zero.on_the_curve()); - result.test_eq("addition of zero does nothing", p1, p1 + zero); - result.test_eq("addition of zero does nothing", p1, zero + p1); - result.test_eq("addition of zero does nothing", p1, p1 - zero); - result.confirm("zero times anything is the zero point", (zero * 39193).is_zero()); + result.test_is_true("zero point is zero", zero.is_zero()); + result.test_is_true("zero point is on the curve", zero.on_the_curve()); + result.test_bin_eq("addition of zero does nothing", p1.xy_bytes(), (p1 + zero).xy_bytes()); + result.test_bin_eq("addition of zero does nothing", p1.xy_bytes(), (zero + p1).xy_bytes()); + result.test_bin_eq("addition of zero does nothing", p1.xy_bytes(), (p1 - zero).xy_bytes()); + result.test_is_true("zero times anything is the zero point", (zero * 39193).is_zero()); for(auto scheme : {Botan::EC_Point_Format::Uncompressed, Botan::EC_Point_Format::Compressed, Botan::EC_Point_Format::Hybrid}) { const std::vector v = zero.encode(scheme); - result.test_eq("encoded/decode rt works", group.OS2ECP(v), zero); + result.test_is_true("encoded/decode rt works", group.OS2ECP(v).is_zero()); } } #endif @@ -368,8 +383,8 @@ const auto secp384r1 = Botan::EC_Group::from_name("secp384r1"); const auto secp384r1_with_seed = Botan::EC_Group::from_PEM(Test::read_data_file("x509/ecc/secp384r1_seed.pem")); - result.confirm("decoding worked", secp384r1_with_seed.initialized()); - result.test_eq("P-384 prime", secp384r1_with_seed.get_p(), secp384r1.get_p()); + result.test_is_true("decoding worked", secp384r1_with_seed.initialized()); + result.test_bn_eq("P-384 prime", secp384r1_with_seed.get_p(), secp384r1.get_p()); } } catch(Botan::Exception& e) { result.test_failure(e.what()); @@ -389,7 +404,7 @@ const Botan::EC_Point& G256 = secp256r1.get_base_point(); const Botan::EC_Point& G384 = secp384r1.get_base_point(); - result.test_throws("Mixing points from different groups", [&] { Botan::EC_Point p = G256 + G384; }); + result.test_throws("Mixing points from different groups", [&] { const Botan::EC_Point p = G256 + G384; }); #endif const auto p1 = Botan::EC_AffinePoint::generator(secp256r1); @@ -400,184 +415,303 @@ return result; } -Test::Result test_ecc_registration() { - Test::Result result("ECC registration"); +class ECC_Unit_Tests final : public Test { + public: + std::vector run() override { + std::vector results; - // numsp256d1 - const Botan::BigInt p("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43"); - const Botan::BigInt a("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40"); - const Botan::BigInt b("0x25581"); - const Botan::BigInt order("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE43C8275EA265C6020AB20294751A825"); + results.push_back(test_decoding_with_seed()); + results.push_back(test_mixed_points()); - const Botan::BigInt g_x("0x01"); - const Botan::BigInt g_y("0x696F1853C1E466D7FC82C96CCEEEDD6BD02C2F9375894EC10BF46306C2B56C77"); + return results; + } +}; - const Botan::OID oid("1.3.6.1.4.1.25258.4.1"); +BOTAN_REGISTER_TEST("pubkey", "ecc_unit", ECC_Unit_Tests); - // Creating this object implicitly registers the curve for future use ... - Botan::EC_Group reg_group(oid, p, a, b, g_x, g_y, order); +class EC_Group_Registration_Tests final : public Test { + public: + std::vector run() override { + std::vector results; - auto group = Botan::EC_Group::from_OID(oid); + if(Botan::EC_Group::supports_application_specific_group()) { + results.push_back(test_ecc_registration()); + results.push_back(test_ec_group_from_params()); + results.push_back(test_ec_group_bad_registration()); + results.push_back(test_ec_group_duplicate_orders()); + results.push_back(test_ec_group_registration_with_custom_oid()); + results.push_back(test_ec_group_unregistration()); + results.push_back(test_supports_named_group_with_registration()); + } - result.test_eq("Group registration worked", group.get_p(), p); + return results; + } - // TODO(Botan4) this could change to == Generic - result.confirm("Group is not pcurve", group.engine() != Botan::EC_Group_Engine::Optimized); + private: + Test::Result test_ecc_registration() { + Test::Result result("ECC registration"); - return result; -} + // numsp256d1 + const Botan::BigInt p("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43"); + const Botan::BigInt a("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40"); + const Botan::BigInt b("0x25581"); + const Botan::BigInt order("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE43C8275EA265C6020AB20294751A825"); -Test::Result test_ec_group_from_params() { - Test::Result result("EC_Group from params"); + const Botan::BigInt g_x("0x01"); + const Botan::BigInt g_y("0x696F1853C1E466D7FC82C96CCEEEDD6BD02C2F9375894EC10BF46306C2B56C77"); - Botan::EC_Group::clear_registered_curve_data(); + const Botan::OID oid("1.3.6.1.4.1.25258.4.1"); - // secp256r1 - const Botan::BigInt p("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF"); - const Botan::BigInt a("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC"); - const Botan::BigInt b("0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B"); - - const Botan::BigInt g_x("0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"); - const Botan::BigInt g_y("0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"); - const Botan::BigInt order("0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"); - - const Botan::OID oid("1.2.840.10045.3.1.7"); - - // This uses the deprecated constructor to verify we dedup even without an OID - // This whole test can be removed once explicit curve support is removed - Botan::EC_Group reg_group(p, a, b, g_x, g_y, order, 1); - result.confirm("Group has correct OID", reg_group.get_curve_oid() == oid); + // Creating this object implicitly registers the curve for future use ... + const Botan::EC_Group reg_group(oid, p, a, b, g_x, g_y, order); - return result; -} + auto group = Botan::EC_Group::from_OID(oid); -Test::Result test_ec_group_bad_registration() { - Test::Result result("EC_Group registering non-match"); + result.test_bn_eq("Group registration worked", group.get_p(), p); - Botan::EC_Group::clear_registered_curve_data(); + // TODO(Botan4) this could change to == Generic + result.test_is_true("Group is not pcurve", group.engine() != Botan::EC_Group_Engine::Optimized); - // secp256r1 params except with a bad B param - const Botan::BigInt p("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF"); - const Botan::BigInt a("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC"); - const Botan::BigInt b("0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604C"); + return result; + } - const Botan::BigInt g_x("0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"); - const Botan::BigInt g_y("0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"); - const Botan::BigInt order("0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"); + Test::Result test_ec_group_from_params() { + Test::Result result("EC_Group from params"); - const Botan::OID oid("1.2.840.10045.3.1.7"); + Botan::EC_Group::clear_registered_curve_data(); - try { - Botan::EC_Group reg_group(oid, p, a, b, g_x, g_y, order); - result.test_failure("Should have failed"); - } catch(Botan::Invalid_Argument&) { - result.test_success("Got expected exception"); - } + // secp256r1 + const Botan::BigInt p("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF"); + const Botan::BigInt a("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC"); + const Botan::BigInt b("0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B"); - return result; -} + const Botan::BigInt g_x("0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"); + const Botan::BigInt g_y("0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"); + const Botan::BigInt order("0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"); -Test::Result test_ec_group_duplicate_orders() { - Test::Result result("EC_Group with duplicate group order"); + const Botan::OID oid("1.2.840.10045.3.1.7"); - Botan::EC_Group::clear_registered_curve_data(); + // This uses the deprecated constructor to verify we dedup even without an OID + // This whole test can be removed once explicit curve support is removed + const Botan::EC_Group reg_group(p, a, b, g_x, g_y, order, 1); + result.test_is_true("Group has correct OID", reg_group.get_curve_oid() == oid); - // secp256r1 - const Botan::BigInt p("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF"); - const Botan::BigInt a("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC"); - const Botan::BigInt b("0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B"); - - const Botan::BigInt g_x("0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"); - const Botan::BigInt g_y("0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"); - const Botan::BigInt order("0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"); - - const Botan::OID oid("1.3.6.1.4.1.25258.100.0"); // some other random OID - - Botan::EC_Group reg_group(oid, p, a, b, g_x, g_y, order); - result.test_success("Registration success"); - result.confirm("Group has correct OID", reg_group.get_curve_oid() == oid); - - // We can now get it by OID: - const auto hc_group = Botan::EC_Group::from_OID(oid); - result.confirm("Group has correct OID", hc_group.get_curve_oid() == oid); - - // Existing secp256r1 unmodified: - const Botan::OID secp160r1("1.2.840.10045.3.1.7"); - const auto other_group = Botan::EC_Group::from_OID(secp160r1); - result.confirm("Group has correct OID", other_group.get_curve_oid() == secp160r1); + return result; + } - return result; -} + Test::Result test_ec_group_bad_registration() { + Test::Result result("EC_Group registering non-match"); + + Botan::EC_Group::clear_registered_curve_data(); + + // secp256r1 params except with a bad B param + const Botan::BigInt p("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF"); + const Botan::BigInt a("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC"); + const Botan::BigInt b("0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604C"); + + const Botan::BigInt g_x("0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"); + const Botan::BigInt g_y("0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"); + const Botan::BigInt order("0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"); + + const Botan::OID oid("1.2.840.10045.3.1.7"); + + try { + const Botan::EC_Group reg_group(oid, p, a, b, g_x, g_y, order); + result.test_failure("Should have failed"); + } catch(Botan::Invalid_Argument&) { + result.test_success("Got expected exception"); + } + + return result; + } + + Test::Result test_ec_group_duplicate_orders() { + Test::Result result("EC_Group with duplicate group order"); + + Botan::EC_Group::clear_registered_curve_data(); + + // secp256r1 + const Botan::BigInt p("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF"); + const Botan::BigInt a("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC"); + const Botan::BigInt b("0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B"); -Test::Result test_ec_group_registration_with_custom_oid() { - Test::Result result("EC_Group registration of standard group with custom OID"); + const Botan::BigInt g_x("0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"); + const Botan::BigInt g_y("0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"); + const Botan::BigInt order("0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"); - Botan::EC_Group::clear_registered_curve_data(); + const Botan::OID oid("1.3.6.1.4.1.25258.100.0"); // some other random OID - const Botan::OID secp256r1_oid("1.2.840.10045.3.1.7"); - const auto secp256r1 = Botan::EC_Group::from_OID(secp256r1_oid); - result.confirm("Group has correct OID", secp256r1.get_curve_oid() == secp256r1_oid); + const Botan::EC_Group reg_group(oid, p, a, b, g_x, g_y, order); + result.test_success("Registration success"); + result.test_is_true("Group has correct OID", reg_group.get_curve_oid() == oid); - const Botan::OID custom_oid("1.3.6.1.4.1.25258.100.99"); // some other random OID + // We can now get it by OID: + const auto hc_group = Botan::EC_Group::from_OID(oid); + result.test_is_true("Group has correct OID", hc_group.get_curve_oid() == oid); - Botan::OID::register_oid(custom_oid, "secp256r1"); + // Existing secp256r1 unmodified: + const Botan::OID secp160r1("1.2.840.10045.3.1.7"); + const auto other_group = Botan::EC_Group::from_OID(secp160r1); + result.test_is_true("Group has correct OID", other_group.get_curve_oid() == secp160r1); - Botan::EC_Group reg_group(custom_oid, - secp256r1.get_p(), - secp256r1.get_a(), - secp256r1.get_b(), - secp256r1.get_g_x(), - secp256r1.get_g_y(), - secp256r1.get_order()); + return result; + } + + Test::Result test_ec_group_registration_with_custom_oid() { + Test::Result result("EC_Group registration of standard group with custom OID"); + + Botan::EC_Group::clear_registered_curve_data(); + + const Botan::OID secp256r1_oid("1.2.840.10045.3.1.7"); + const auto secp256r1 = Botan::EC_Group::from_OID(secp256r1_oid); + result.test_is_true("Group has correct OID", secp256r1.get_curve_oid() == secp256r1_oid); + + const Botan::OID custom_oid("1.3.6.1.4.1.25258.100.99"); // some other random OID + + Botan::OID::register_oid(custom_oid, "secp256r1"); + + const Botan::EC_Group reg_group(custom_oid, + secp256r1.get_p(), + secp256r1.get_a(), + secp256r1.get_b(), + secp256r1.get_g_x(), + secp256r1.get_g_y(), + secp256r1.get_order()); - result.test_success("Registration success"); - result.confirm("Group has correct OID", reg_group.get_curve_oid() == custom_oid); + result.test_success("Registration success"); + result.test_is_true("Group has correct OID", reg_group.get_curve_oid() == custom_oid); - // We can now get it by OID: - result.confirm("Group has correct OID", Botan::EC_Group::from_OID(custom_oid).get_curve_oid() == custom_oid); + // We can now get it by OID: + result.test_is_true("Group has correct OID", + Botan::EC_Group::from_OID(custom_oid).get_curve_oid() == custom_oid); - // In the current data model of EC_Group there is a 1:1 OID:group, so these - // have distinct underlying data - result.confirm("Groups have different inner data pointers", reg_group._data() != secp256r1._data()); + // In the current data model of EC_Group there is a 1:1 OID:group, so these + // have distinct underlying data + result.test_is_true("Groups have different inner data pointers", reg_group._data() != secp256r1._data()); #if defined(BOTAN_HAS_PCURVES_SECP256R1) - // However we should have gotten a pcurves out of the deal *and* it - // should be the exact same shared_ptr as the official curve + // However we should have gotten a pcurves out of the deal *and* it + // should be the exact same shared_ptr as the official curve - result.confirm("Group is pcurves based", reg_group.engine() == Botan::EC_Group_Engine::Optimized); + result.test_enum_eq("Group is pcurves based", reg_group.engine(), Botan::EC_Group_Engine::Optimized); - try { - const auto& pcurve = reg_group._data()->pcurve(); - result.confirm("Group with custom OID got the same pcurve pointer", &pcurve == &secp256r1._data()->pcurve()); - } catch(...) { - result.test_failure("Group with custom OID did not get a pcurve pointer"); - } + try { + const auto& pcurve = reg_group._data()->pcurve(); + result.test_is_true("Group with custom OID got the same pcurve pointer", + &pcurve == &secp256r1._data()->pcurve()); + } catch(...) { + result.test_failure("Group with custom OID did not get a pcurve pointer"); + } #endif - return result; -} + return result; + } -class ECC_Unit_Tests final : public Test { - public: - std::vector run() override { - std::vector results; + Test::Result test_supports_named_group_with_registration() { + Test::Result result("EC_Group::supports_named_group with custom registration"); - results.push_back(test_decoding_with_seed()); - results.push_back(test_mixed_points()); + Botan::EC_Group::clear_registered_curve_data(); - if(Botan::EC_Group::supports_application_specific_group()) { - results.push_back(test_ecc_registration()); - results.push_back(test_ec_group_from_params()); - results.push_back(test_ec_group_bad_registration()); - results.push_back(test_ec_group_duplicate_orders()); - results.push_back(test_ec_group_registration_with_custom_oid()); + result.test_is_false("Unknown name is not supported", + Botan::EC_Group::supports_named_group("not_a_real_curve_name_xyz")); + + const Botan::OID custom_oid("1.3.6.1.4.1.25258.4.9000"); + const std::string custom_name = "goku-curve"; // very strong + + Botan::OID::register_oid(custom_oid, custom_name); + + result.test_is_false("Mapped OID without registered EC_Group is not supported", + Botan::EC_Group::supports_named_group(custom_name)); + + const Botan::BigInt p("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43"); + const Botan::BigInt a("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40"); + const Botan::BigInt b("0x25581"); + const Botan::BigInt order("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE43C8275EA265C6020AB20294751A825"); + const Botan::BigInt g_x("0x01"); + const Botan::BigInt g_y("0x696F1853C1E466D7FC82C96CCEEEDD6BD02C2F9375894EC10BF46306C2B56C77"); + + const Botan::EC_Group reg_group(custom_oid, p, a, b, g_x, g_y, order); + + result.test_is_true("After registration the custom name is supported", + Botan::EC_Group::supports_named_group(custom_name)); + + const auto resolved = Botan::EC_Group::from_name(custom_name); + result.test_is_true("from_name resolves to the registered OID", resolved.get_curve_oid() == custom_oid); + + result.test_is_false("known_named_groups still does not include custom name", + Botan::EC_Group::known_named_groups().contains(custom_name)); + + result.test_is_true("Unregistering removes support for the custom name", + Botan::EC_Group::unregister(custom_oid)); + result.test_is_false("After unregister the custom name is not supported", + Botan::EC_Group::supports_named_group(custom_name)); + + return result; + } + + Test::Result test_ec_group_unregistration() { + Test::Result result("EC_Group unregistration of group"); + + Botan::EC_Group::clear_registered_curve_data(); + + const Botan::OID secp256r1_oid("1.2.840.10045.3.1.7"); + const auto secp256r1 = Botan::EC_Group::from_OID(secp256r1_oid); + const Botan::OID custom_oid("1.3.6.1.4.1.25258.100.99"); + Botan::OID::register_oid(custom_oid, "secp256r1"); + + const Botan::EC_Group reg_group(custom_oid, + secp256r1.get_p(), + secp256r1.get_a(), + secp256r1.get_b(), + secp256r1.get_g_x(), + secp256r1.get_g_y(), + secp256r1.get_order()); + + const Botan::EC_Group group_from_oid = Botan::EC_Group::from_OID(custom_oid); + + result.test_is_true("Internal group is unregistered", Botan::EC_Group::unregister(secp256r1_oid)); + result.test_is_false("Unregistering internal group again does nothing", + Botan::EC_Group::unregister(secp256r1_oid)); + result.test_is_true("User defined group is unregistered", Botan::EC_Group::unregister(custom_oid)); + result.test_is_false("Unregistering user defined group again does nothing", + Botan::EC_Group::unregister(custom_oid)); + + try { + Botan::EC_Group::from_OID(custom_oid); + result.test_failure("Should have failed"); + } catch(Botan::Invalid_Argument&) { + result.test_success("Got expected exception"); } - return results; + result.test_is_true("Group can still be accessed", reg_group.get_curve_oid() == custom_oid); + result.test_is_true("Group has correct p parameter", reg_group.get_p() == secp256r1.get_p()); + result.test_is_true("Group has correct a parameter", reg_group.get_a() == secp256r1.get_a()); + result.test_is_true("Group has correct b parameter", reg_group.get_b() == secp256r1.get_b()); + result.test_is_true("Group has correct g_x parameter", reg_group.get_g_x() == secp256r1.get_g_x()); + result.test_is_true("Group has correct g_y parameter", reg_group.get_g_y() == secp256r1.get_g_y()); + result.test_is_true("Group has correct order parameter", reg_group.get_order() == secp256r1.get_order()); + + #if defined(BOTAN_HAS_ECDSA) + std::unique_ptr rng = Test::new_rng("test_ec_group_unregistration"); + std::unique_ptr key = Botan::create_ec_private_key("ECDSA", group_from_oid, *rng); + result.test_success("Can still use group to create a key"); + result.test_is_true("Key was created correctly", key->check_key(*rng, true)); + #endif + + // TODO(Botan4) remove this when OIDs lose internal nullability + try { + const Botan::OID empty_oid(""); + Botan::EC_Group::unregister(empty_oid); + result.test_failure("Should have failed"); + } catch(Botan::Invalid_Argument&) { + result.test_success("Got expected exception"); + } + + return result; } }; -BOTAN_REGISTER_SERIALIZED_TEST("pubkey", "ecc_unit", ECC_Unit_Tests); +BOTAN_REGISTER_SERIALIZED_TEST("pubkey", "ec_group_registration", EC_Group_Registration_Tests); class EC_PointEnc_Tests final : public Test { public: @@ -593,33 +727,31 @@ result.start_timer(); - std::vector ws; - for(size_t trial = 0; trial != 100; ++trial) { const auto scalar = Botan::EC_Scalar::random(group, rng); - const auto pt = Botan::EC_AffinePoint::g_mul(scalar, rng, ws); + const auto pt = Botan::EC_AffinePoint::g_mul(scalar, rng); const auto pt_u = pt.serialize_uncompressed(); - result.test_eq("Expected uncompressed header", static_cast(pt_u[0]), 0x04); + result.test_u8_eq("Expected uncompressed header", pt_u[0], 0x04); const size_t fe_bytes = (pt_u.size() - 1) / 2; const auto pt_c = pt.serialize_compressed(); - result.test_eq("Expected compressed size", pt_c.size(), 1 + fe_bytes); + result.test_sz_eq("Expected compressed size", pt_c.size(), 1 + fe_bytes); const uint8_t expected_c_header = (pt_u[pt_u.size() - 1] % 2 == 0) ? 0x02 : 0x03; - result.confirm("Expected compressed header", pt_c[0] == expected_c_header); + result.test_u8_eq("Expected compressed header", pt_c[0], expected_c_header); - result.test_eq( + result.test_bin_eq( "Expected compressed x", std::span{pt_c}.subspan(1), std::span{pt_u}.subspan(1, fe_bytes)); if(auto d_pt_u = Botan::EC_AffinePoint::deserialize(group, pt_u)) { - result.test_eq( + result.test_bin_eq( "Deserializing uncompressed returned correct point", d_pt_u->serialize_uncompressed(), pt_u); } else { result.test_failure("Failed to deserialize uncompressed point"); } if(auto d_pt_c = Botan::EC_AffinePoint::deserialize(group, pt_c)) { - result.test_eq( + result.test_bin_eq( "Deserializing compressed returned correct point", d_pt_c->serialize_uncompressed(), pt_u); } else { result.test_failure("Failed to deserialize compressed point"); @@ -632,9 +764,9 @@ }(); if(auto d_neg_pt_c = Botan::EC_AffinePoint::deserialize(group, neg_pt_c)) { - result.test_eq("Deserializing compressed with inverted header returned negated point", - d_neg_pt_c->serialize_uncompressed(), - pt.negate().serialize_uncompressed()); + result.test_bin_eq("Deserializing compressed with inverted header returned negated point", + d_neg_pt_c->serialize_uncompressed(), + pt.negate().serialize_uncompressed()); } else { result.test_failure("Failed to deserialize compressed point"); } @@ -658,8 +790,6 @@ auto& rng = Test::rng(); - std::vector ws; - for(const auto& group_id : Botan::EC_Group::known_named_groups()) { const auto group = Botan::EC_Group::from_name(group_id); @@ -668,69 +798,69 @@ result.start_timer(); const auto one = Botan::EC_Scalar::one(group); - const auto zero = one - one; + const auto zero = one - one; // NOLINT(*-redundant-expression) const auto g = Botan::EC_AffinePoint::generator(group); const auto g_bytes = g.serialize_uncompressed(); - const auto id = Botan::EC_AffinePoint::g_mul(zero, rng, ws); - result.confirm("g*zero is point at identity", id.is_identity()); + const auto id = Botan::EC_AffinePoint::g_mul(zero, rng); + result.test_is_true("g*zero is point at identity", id.is_identity()); const auto id2 = id.add(id); - result.confirm("identity plus itself is identity", id2.is_identity()); + result.test_is_true("identity plus itself is identity", id2.is_identity()); - const auto g_one = Botan::EC_AffinePoint::g_mul(one, rng, ws); - result.test_eq("g*one == generator", g_one.serialize_uncompressed(), g_bytes); + const auto g_one = Botan::EC_AffinePoint::g_mul(one, rng); + result.test_bin_eq("g*one == generator", g_one.serialize_uncompressed(), g_bytes); const auto g_plus_id = g_one.add(id); - result.test_eq("g + id == g", g_plus_id.serialize_uncompressed(), g_bytes); + result.test_bin_eq("g + id == g", g_plus_id.serialize_uncompressed(), g_bytes); const auto id_plus_g = id.add(g_one); - result.test_eq("id + g == g", id_plus_g.serialize_uncompressed(), g_bytes); + result.test_bin_eq("id + g == g", id_plus_g.serialize_uncompressed(), g_bytes); - const auto g_neg_one = Botan::EC_AffinePoint::g_mul(one.negate(), rng, ws); + const auto g_neg_one = Botan::EC_AffinePoint::g_mul(one.negate(), rng); const auto id_from_g = g_one.add(g_neg_one); - result.confirm("g - g is identity", id_from_g.is_identity()); + result.test_is_true("g - g is identity", id_from_g.is_identity()); - const auto g_two = Botan::EC_AffinePoint::g_mul(one + one, rng, ws); + const auto g_two = Botan::EC_AffinePoint::g_mul(one + one, rng); const auto g_plus_g = g_one.add(g_one); - result.test_eq("2*g == g+g", g_two.serialize_uncompressed(), g_plus_g.serialize_uncompressed()); + result.test_bin_eq("2*g == g+g", g_two.serialize_uncompressed(), g_plus_g.serialize_uncompressed()); - result.confirm("Scalar::zero is zero", zero.is_zero()); - result.confirm("(zero+zero) is zero", (zero + zero).is_zero()); - result.confirm("(zero*zero) is zero", (zero * zero).is_zero()); - result.confirm("(zero-zero) is zero", (zero - zero).is_zero()); + result.test_is_true("Scalar::zero is zero", zero.is_zero()); + result.test_is_true("(zero+zero) is zero", (zero + zero).is_zero()); + result.test_is_true("(zero*zero) is zero", (zero * zero).is_zero()); + result.test_is_true("(zero-zero) is zero", (zero - zero).is_zero()); // NOLINT(*-redundant-expression) const auto neg_zero = zero.negate(); - result.confirm("zero.negate() is zero", neg_zero.is_zero()); + result.test_is_true("zero.negate() is zero", neg_zero.is_zero()); - result.confirm("(zero+nz) is zero", (zero + neg_zero).is_zero()); - result.confirm("(nz+nz) is zero", (neg_zero + neg_zero).is_zero()); - result.confirm("(nz+zero) is zero", (neg_zero + zero).is_zero()); - - result.confirm("Scalar::one is not zero", !one.is_zero()); - result.confirm("(one-one) is zero", (one - one).is_zero()); - result.confirm("(one+one.negate()) is zero", (one + one.negate()).is_zero()); - result.confirm("(one.negate()+one) is zero", (one.negate() + one).is_zero()); + result.test_is_true("(zero+nz) is zero", (zero + neg_zero).is_zero()); + result.test_is_true("(nz+nz) is zero", (neg_zero + neg_zero).is_zero()); + result.test_is_true("(nz+zero) is zero", (neg_zero + zero).is_zero()); + + result.test_is_true("Scalar::one is not zero", !one.is_zero()); + result.test_is_true("(one-one) is zero", (one - one).is_zero()); // NOLINT(*-redundant-expression) + result.test_is_true("(one+one.negate()) is zero", (one + one.negate()).is_zero()); + result.test_is_true("(one.negate()+one) is zero", (one.negate() + one).is_zero()); for(size_t i = 0; i != 16; ++i) { - const auto pt = Botan::EC_AffinePoint::g_mul(Botan::EC_Scalar::random(group, rng), rng, ws); + const auto pt = Botan::EC_AffinePoint::g_mul(Botan::EC_Scalar::random(group, rng), rng); const auto a = Botan::EC_Scalar::random(group, rng); const auto b = Botan::EC_Scalar::random(group, rng); const auto c = a + b; - const auto Pa = pt.mul(a, rng, ws); - const auto Pb = pt.mul(b, rng, ws); - const auto Pc = pt.mul(c, rng, ws); + const auto Pa = pt.mul(a, rng); + const auto Pb = pt.mul(b, rng); + const auto Pc = pt.mul(c, rng); const auto Pc_bytes = Pc.serialize_uncompressed(); const auto Pab = Pa.add(Pb); - result.test_eq("Pa + Pb == Pc", Pab.serialize_uncompressed(), Pc_bytes); + result.test_bin_eq("Pa + Pb == Pc", Pab.serialize_uncompressed(), Pc_bytes); const auto Pba = Pb.add(Pa); - result.test_eq("Pb + Pa == Pc", Pba.serialize_uncompressed(), Pc_bytes); + result.test_bin_eq("Pb + Pa == Pc", Pba.serialize_uncompressed(), Pc_bytes); } for(size_t i = 0; i != 64; ++i) { @@ -753,7 +883,7 @@ return Botan::EC_Scalar::random(group, rng); } }(); - auto x = Botan::EC_AffinePoint::g_mul(s, rng, ws); + auto x = Botan::EC_AffinePoint::g_mul(s, rng); return x; }(); @@ -762,12 +892,12 @@ const Botan::EC_Group::Mul2Table mul2_table(h); - const auto ref = Botan::EC_AffinePoint::g_mul(s1, rng, ws).add(h.mul(s2, rng, ws)); + const auto ref = Botan::EC_AffinePoint::g_mul(s1, rng).add(h.mul(s2, rng)); if(auto mul2pt = mul2_table.mul2_vartime(s1, s2)) { - result.test_eq("ref == mul2t", ref.serialize_uncompressed(), mul2pt->serialize_uncompressed()); + result.test_bin_eq("ref == mul2t", ref.serialize_uncompressed(), mul2pt->serialize_uncompressed()); } else { - result.confirm("ref is identity", ref.is_identity()); + result.test_is_true("ref is identity", ref.is_identity()); } } @@ -798,7 +928,7 @@ try { auto key = Botan::X509::load_key(key_data); - result.test_eq("public key fails check", key->check_key(this->rng(), false), false); + result.test_is_false("public key fails check", key->check_key(this->rng(), false)); } catch(Botan::Decoding_Error&) { result.test_success("Decoding invalid ECC key results in decoding error exception"); } diff -Nru botan3-3.7.1+dfsg/src/tests/test_ecc_explicit_params.cpp botan3-3.12.0+dfsg/src/tests/test_ecc_explicit_params.cpp --- botan3-3.7.1+dfsg/src/tests/test_ecc_explicit_params.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_ecc_explicit_params.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,80 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "tests.h" + +#if defined(BOTAN_HAS_ECDSA) + #include + #include + #include +#endif + +namespace Botan_Tests { + +namespace { + +#if defined(BOTAN_HAS_ECDSA) + +class ECC_Explicit_Curve_Tests final : public Text_Based_Test { + public: + ECC_Explicit_Curve_Tests() : Text_Based_Test("pubkey/ecc_explicit_curve.vec", "Pubkey,Result") {} + + Test::Result run_one_test(const std::string& /*unused*/, const VarMap& vars) override { + Test::Result result("ECC explicit curve validation"); + + const auto pubkey = vars.get_req_bin("Pubkey"); + const auto expected_result = vars.get_req_str("Result"); + + try { + auto pk = Botan::X509::load_key(pubkey); + + const auto* ecdsa = dynamic_cast(pk.get()); + if(ecdsa != nullptr) { + result.test_success("Returned key was ECDSA"); + + auto used_explicit = ecdsa->domain().used_explicit_encoding(); + + result.test_is_true("Loaded ECC key marked as an explicit encoding", used_explicit); + } else { + result.test_failure("Returned key was some other type"); + } + + if(expected_result == "OK") { + result.test_success("Accepted valid explicit curve parameters"); + } else { + result.test_failure("Accepted invalid explicit curve parameters"); + } + } catch(Botan::Not_Implemented& e) { + // Can happen if pcurves_generic is not in the build + const std::string err(e.what()); + result.test_is_true("Expected error", + err.find("is not supported in this build config") != std::string::npos); + } catch(Botan::Exception& e) { + const std::string err(e.what()); + if(expected_result == "OK") { + result.test_failure("Rejected valid explicit curve parameters", err); + } else { + result.test_success("Rejected invalid explicit curve parameters"); + + if(err.find(expected_result) != std::string::npos) { + result.test_success("Rejection error matches expected"); + } else { + result.test_failure("Rejection failure other than what was expected", err); + } + } + } + + return result; + } +}; + +BOTAN_REGISTER_TEST("pubkey", "ecc_explicit_curve", ECC_Explicit_Curve_Tests); + +#endif + +} // namespace + +} // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_ecc_h2c.cpp botan3-3.12.0+dfsg/src/tests/test_ecc_h2c.cpp --- botan3-3.7.1+dfsg/src/tests/test_ecc_h2c.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_ecc_h2c.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,11 +6,12 @@ #include "tests.h" -#if defined(BOTAN_HAS_EC_HASH_TO_CURVE) +#if defined(BOTAN_HAS_ECC_GROUP) #include #endif #if defined(BOTAN_HAS_XMD) + #include #include #endif @@ -34,9 +35,9 @@ const std::vector expected = vars.get_req_bin("Output"); std::vector output(expected.size()); - Botan::expand_message_xmd(hash, output, input, domain); + Botan::expand_message_xmd(hash, output, Botan::as_span_of_bytes(input), Botan::as_span_of_bytes(domain)); - result.test_eq("XMD output", output, expected); + result.test_bin_eq("XMD output", output, expected); return result; } }; @@ -45,6 +46,46 @@ #endif +#if defined(BOTAN_HAS_XMD) && defined(BOTAN_HAS_ECC_GROUP) + +class ECC_H2S_Tests final : public Text_Based_Test { + public: + ECC_H2S_Tests() : Text_Based_Test("pubkey/ec_h2s.vec", "Hash,Domain,Input,Output") {} + + bool clear_between_callbacks() const override { return false; } + + bool skip_this_test(const std::string& group_id, const VarMap& /*vars*/) override { + return !Botan::EC_Group::supports_named_group(group_id); + } + + Test::Result run_one_test(const std::string& group_id, const VarMap& vars) override { + Test::Result result("ECC hash to scalar " + group_id); + + const std::string hash_fn = vars.get_req_str("Hash"); + const std::string domain_str = vars.get_req_str("Domain"); + const std::string input_str = vars.get_req_str("Input"); + const std::vector expected_value = vars.get_req_bin("Output"); + + auto input = std::span{reinterpret_cast(input_str.data()), input_str.size()}; + auto domain = std::span{reinterpret_cast(domain_str.data()), domain_str.size()}; + + const auto group = Botan::EC_Group::from_name(group_id); + + try { + auto scalar = Botan::EC_Scalar::hash(group, hash_fn, input, domain).serialize(); + result.test_bin_eq("output", scalar, expected_value); + } catch(Botan::Not_Implemented&) { + result.test_note("Skipping due to not implemented"); + } + + return result; + } +}; + +BOTAN_REGISTER_TEST("ec_h2c", "ec_h2s_kat", ECC_H2S_Tests); + +#endif + #if defined(BOTAN_HAS_EC_HASH_TO_CURVE) class ECC_H2C_Tests final : public Text_Based_Test { @@ -53,7 +94,7 @@ bool clear_between_callbacks() const override { return false; } - bool skip_this_test(const std::string&, const VarMap& vars) override { + bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override { return !Botan::EC_Group::supports_named_group(vars.get_req_str("Group")); } @@ -80,7 +121,7 @@ pt = Botan::EC_AffinePoint::hash_to_curve_nu(group, hash_fn, input, domain).serialize_uncompressed(); } - result.test_eq("Generated point serialization", pt, expected_point); + result.test_bin_eq("Generated point serialization", pt, expected_point); } catch(Botan::Not_Implemented&) { result.test_note("Skipping due to not implemented"); } diff -Nru botan3-3.7.1+dfsg/src/tests/test_ecc_pointmul.cpp botan3-3.12.0+dfsg/src/tests/test_ecc_pointmul.cpp --- botan3-3.7.1+dfsg/src/tests/test_ecc_pointmul.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_ecc_pointmul.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,8 +7,10 @@ #include "tests.h" #if defined(BOTAN_HAS_ECC_GROUP) + #include #include #include + #include #include #endif @@ -22,7 +24,7 @@ public: ECC_Basepoint_Mul_Tests() : Text_Based_Test("pubkey/ecc_base_point_mul.vec", "k,P") {} - bool skip_this_test(const std::string& group_id, const VarMap&) override { + bool skip_this_test(const std::string& group_id, const VarMap& /*vars*/) override { return !Botan::EC_Group::supports_named_group(group_id); } @@ -35,21 +37,20 @@ const auto group = Botan::EC_Group::from_name(group_id); const Botan::BigInt k(k_bytes); - std::vector ws; #if defined(BOTAN_HAS_LEGACY_EC_POINT) const auto pt = group.OS2ECP(P_bytes); const Botan::EC_Point p1 = group.get_base_point() * k; - result.test_eq("EC_Point Montgomery ladder", p1.encode(Botan::EC_Point_Format::Uncompressed), P_bytes); + result.test_bin_eq("EC_Point Montgomery ladder", p1.encode(Botan::EC_Point_Format::Uncompressed), P_bytes); #endif const auto scalar = Botan::EC_Scalar::from_bigint(group, k); - const auto apg = Botan::EC_AffinePoint::g_mul(scalar, this->rng(), ws); - result.test_eq("AffinePoint::g_mul", apg.serialize_uncompressed(), P_bytes); + const auto apg = Botan::EC_AffinePoint::g_mul(scalar, this->rng()); + result.test_bin_eq("AffinePoint::g_mul", apg.serialize_uncompressed(), P_bytes); const auto ag = Botan::EC_AffinePoint::generator(group); - const auto ap = ag.mul(scalar, this->rng(), ws); - result.test_eq("AffinePoint::mul", ap.serialize_uncompressed(), P_bytes); + const auto ap = ag.mul(scalar, this->rng()); + result.test_bin_eq("AffinePoint::mul", ap.serialize_uncompressed(), P_bytes); return result; } @@ -61,7 +62,7 @@ public: ECC_Varpoint_Mul_Tests() : Text_Based_Test("pubkey/ecc_var_point_mul.vec", "P,k,Z") {} - bool skip_this_test(const std::string& group_id, const VarMap&) override { + bool skip_this_test(const std::string& group_id, const VarMap& /*vars*/) override { return !Botan::EC_Group::supports_named_group(group_id); } @@ -74,24 +75,21 @@ const auto group = Botan::EC_Group::from_name(group_id); - std::vector ws; - #if defined(BOTAN_HAS_LEGACY_EC_POINT) const Botan::EC_Point p1 = group.OS2ECP(p) * k; - result.test_eq("EC_Point Montgomery ladder", p1.encode(Botan::EC_Point::Compressed), z); - result.confirm("Output point is on the curve", p1.on_the_curve()); + result.test_bin_eq("EC_Point Montgomery ladder", p1.encode(Botan::EC_Point::Compressed), z); #endif const auto s_k = Botan::EC_Scalar::from_bigint(group, k); const auto apt = Botan::EC_AffinePoint::deserialize(group, p).value(); - const auto apt_k = apt.mul(s_k, this->rng(), ws); - result.test_eq("p * k (AffinePoint)", apt_k.serialize_compressed(), z); + const auto apt_k = apt.mul(s_k, this->rng()); + result.test_bin_eq("p * k (AffinePoint)", apt_k.serialize_compressed(), z); - const auto apt_k_neg = apt.negate().mul(s_k.negate(), this->rng(), ws); - result.test_eq("-p * -k (AffinePoint)", apt_k_neg.serialize_compressed(), z); + const auto apt_k_neg = apt.negate().mul(s_k.negate(), this->rng()); + result.test_bin_eq("-p * -k (AffinePoint)", apt_k_neg.serialize_compressed(), z); - const auto neg_apt_neg_k = apt.mul(s_k.negate(), this->rng(), ws).negate(); - result.test_eq("-(p * -k) (AffinePoint)", neg_apt_neg_k.serialize_compressed(), z); + const auto neg_apt_neg_k = apt.mul(s_k.negate(), this->rng()).negate(); + result.test_bin_eq("-(p * -k) (AffinePoint)", neg_apt_neg_k.serialize_compressed(), z); return result; } @@ -103,7 +101,7 @@ public: ECC_Mul2_Tests() : Text_Based_Test("pubkey/ecc_var_point_mul2.vec", "P,x,Q,y,Z") {} - bool skip_this_test(const std::string& group_id, const VarMap&) override { + bool skip_this_test(const std::string& group_id, const VarMap& /*vars*/) override { return !Botan::EC_Group::supports_named_group(group_id); } @@ -120,21 +118,20 @@ bool with_final_negation = false) { if(const auto z = Botan::EC_AffinePoint::mul_px_qy(p, x, q, y, rng())) { if(with_final_negation) { - result.test_eq(what, z->negate().serialize_compressed(), Z_bytes); + result.test_bin_eq(what, z->negate().serialize_compressed(), Z_bytes); } else { - result.test_eq(what, z->serialize_compressed(), Z_bytes); + result.test_bin_eq(what, z->serialize_compressed(), Z_bytes); } } else { result.test_failure("EC_AffinePoint::mul_px_qy failed to produce a result"); } // Now check the same using naive multiply and add: - std::vector ws; - auto z = p.mul(x, rng(), ws).add(q.mul(y, rng(), ws)); + auto z = p.mul(x, rng()).add(q.mul(y, rng())); if(with_final_negation) { z = z.negate(); } - result.test_eq("p*x + q*y naive", z.serialize_compressed(), Z_bytes); + result.test_bin_eq("p*x + q*y naive", z.serialize_compressed(), Z_bytes); }; const auto group = Botan::EC_Group::from_name(group_id); @@ -185,15 +182,14 @@ const auto g = Botan::EC_AffinePoint::generator(group); // Choose some other random point z - std::vector ws; - const auto z = g.mul(Botan::EC_Scalar::random(group, rng()), rng(), ws); + const auto z = g.mul(Botan::EC_Scalar::random(group, rng()), rng()); const auto r = Botan::EC_Scalar::random(group, rng()); const auto neg_r = r.negate(); const auto neg_r2 = neg_r + neg_r; - const auto zero = r - r; - result.confirm("Computed EC_Scalar is zero", zero.is_zero()); + const auto zero = r - r; // NOLINT(*-redundant-expression) + result.test_is_true("Computed EC_Scalar is zero", zero.is_zero()); const auto g2 = g.add(g); @@ -207,6 +203,12 @@ check_px_qy("r*g + r*-g", g, r, g.negate(), r); check_px_qy("r*g2 + -r2*g", g2, r, g, neg_r2); + // Test 'zeroization' (explicit erasure of the scalar content) + auto r2 = Botan::EC_Scalar::random(group, rng()); + result.test_is_true("random value is not zero", !r2.is_zero()); + r2.zeroize(); + result.test_is_true("value is zero", r2.is_zero()); + results.push_back(result); } @@ -227,20 +229,19 @@ const auto group = Botan::EC_Group::from_name(group_id); const auto g = Botan::EC_AffinePoint::generator(group); - result.test_eq("g is not the identity element", g.is_identity(), false); + result.test_is_false("g is not the identity element", g.is_identity()); // Choose some other random point z - std::vector ws; - const auto z = g.mul(Botan::EC_Scalar::random(group, rng()), rng(), ws); - result.test_eq("z is not the identity element", z.is_identity(), false); + const auto z = g.mul(Botan::EC_Scalar::random(group, rng()), rng()); + result.test_is_false("z is not the identity element", z.is_identity()); const auto id = Botan::EC_AffinePoint::identity(group); - result.test_eq("id is the identity element", id.is_identity(), true); + result.test_is_true("id is the identity element", id.is_identity()); const auto g_bytes = g.serialize_uncompressed(); auto check_expr_is_g = [&](const char* msg, const Botan::EC_AffinePoint& pt) { - result.test_eq(Botan::fmt("{} is g", msg), pt.serialize_uncompressed(), g_bytes); + result.test_bin_eq(Botan::fmt("{} is g", msg), pt.serialize_uncompressed(), g_bytes); }; const auto nz = z.negate(); @@ -287,7 +288,7 @@ const Botan::EC_Group& group, Botan::RandomNumberGenerator& rng) const { const auto one = Botan::EC_Scalar::one(group); - const auto zero = one - one; + const auto zero = one - one; // NOLINT(*-redundant-expression) const auto two = one + one; const size_t order_bytes = group.get_order_bytes(); @@ -301,16 +302,22 @@ return b; }(); - result.test_eq("Serialization of zero is expected value", zero.serialize(), ser_zero); - result.test_eq("Serialization of one is expected value", one.serialize(), ser_one); + result.test_bin_eq("Serialization of zero is expected value", zero.serialize(), ser_zero); + result.test_bin_eq("Serialization of one is expected value", one.serialize(), ser_one); - result.test_eq("Zero is zero", zero.is_zero(), true); - result.test_eq("Negation of zero is zero", zero.negate().is_zero(), true); - result.test_eq("One is not zero", one.is_zero(), false); + result.test_is_false("EC_Scalar::deserialize rejects zero", + Botan::EC_Scalar::deserialize(group, ser_zero).has_value()); + + result.test_is_true("Zero is zero", zero.is_zero()); + result.test_is_true("Negation of zero is zero", zero.negate().is_zero()); + result.test_is_false("One is not zero", one.is_zero()); // Zero inverse is not mathematically correct, but works out for our purposes - result.test_eq("Inverse of zero is zero", zero.invert().serialize(), ser_zero); - result.test_eq("Inverse of one is one", one.invert().serialize(), ser_one); + result.test_bin_eq("Inverse of zero is zero", zero.invert().serialize(), ser_zero); + result.test_bin_eq("Inverse of one is one", one.invert().serialize(), ser_one); + + result.test_bin_eq("Inverse (vt) of zero is zero", zero.invert_vartime().serialize(), ser_zero); + result.test_bin_eq("Inverse (vt) of one is one", one.invert_vartime().serialize(), ser_one); constexpr size_t test_iter = 128; @@ -318,21 +325,22 @@ const auto r = Botan::EC_Scalar::random(group, rng); // Negation and addition are inverses - result.test_eq("r + -r == 0", (r + r.negate()).serialize(), ser_zero); + result.test_bin_eq("r + -r == 0", (r + r.negate()).serialize(), ser_zero); // Serialization and deserialization are inverses const auto r_bytes = r.serialize(); - result.test_eq("Deserialization of r round trips", - Botan::EC_Scalar::deserialize(group, r_bytes).value().serialize(), - r_bytes); + result.test_bin_eq("Deserialization of r round trips", + Botan::EC_Scalar::deserialize(group, r_bytes).value().serialize(), + r_bytes); // Multiplication and inversion are inverses const auto r2 = r * r; const auto r_inv = r.invert(); - result.test_eq("r * r^-1 = 1", (r * r_inv).serialize(), ser_one); + result.test_bin_eq("r * r^-1 = 1", (r * r_inv).serialize(), ser_one); const auto r_inv_vt = r.invert_vartime(); - result.confirm("CT and variable time inversions produced same result", r_inv == r_inv_vt); + result.test_bin_eq( + "CT and variable time inversions produced same result", r_inv.serialize(), r_inv_vt.serialize()); } for(size_t i = 0; i != test_iter; ++i) { @@ -343,14 +351,14 @@ const auto a_inv = a.invert(); const auto b_inv = b.invert(); - result.test_eq("a * b / b = a", (ab * b_inv).serialize(), a.serialize()); - result.test_eq("a * b / a = b", (ab * a_inv).serialize(), b.serialize()); + result.test_bin_eq("a * b / b = a", (ab * b_inv).serialize(), a.serialize()); + result.test_bin_eq("a * b / a = b", (ab * a_inv).serialize(), b.serialize()); auto a_plus_b = a + b; - result.test_eq("(a + b) - b == a", (a_plus_b - b).serialize(), a.serialize()); - result.test_eq("(a + b) - a == b", (a_plus_b - a).serialize(), b.serialize()); - result.test_eq("b - (a + b) == -a", (b - a_plus_b).serialize(), a.negate().serialize()); - result.test_eq("a - (a + b) == -b", (a - a_plus_b).serialize(), b.negate().serialize()); + result.test_bin_eq("(a + b) - b == a", (a_plus_b - b).serialize(), a.serialize()); + result.test_bin_eq("(a + b) - a == b", (a_plus_b - a).serialize(), b.serialize()); + result.test_bin_eq("b - (a + b) == -a", (b - a_plus_b).serialize(), a.negate().serialize()); + result.test_bin_eq("a - (a + b) == -b", (a - a_plus_b).serialize(), b.negate().serialize()); } for(size_t i = 0; i != test_iter; ++i) { @@ -361,7 +369,7 @@ const auto ab_c = (a + b) * c; const auto ac_bc = a * c + b * c; - result.test_eq("(a + b)*c == a * c + b * c", ab_c.serialize(), ac_bc.serialize()); + result.test_bin_eq("(a + b)*c == a * c + b * c", ab_c.serialize(), ac_bc.serialize()); } for(size_t i = 0; i != test_iter; ++i) { @@ -370,14 +378,14 @@ const auto c = a * b; const auto c_bn = (a.to_bigint() * b.to_bigint()); - result.test_eq("matches BigInt", c.serialize(), (c_bn % group.get_order()).serialize(order_bytes)); + result.test_bin_eq("matches BigInt", c.serialize(), (c_bn % group.get_order()).serialize(order_bytes)); const auto c_wide_bytes = c_bn.serialize(); - result.test_lte("Expected size", c_wide_bytes.size(), 2 * order_bytes); + result.test_sz_lte("Expected size", c_wide_bytes.size(), 2 * order_bytes); const auto z = Botan::EC_Scalar::from_bytes_mod_order(group, c_wide_bytes); - result.test_eq("from_bytes_mod_order", c.serialize(), z.serialize()); + result.test_bin_eq("from_bytes_mod_order", c.serialize(), z.serialize()); } for(size_t i = 0; i != test_iter; ++i) { @@ -389,7 +397,8 @@ const auto scalar = Botan::EC_Scalar::from_bytes_mod_order(group, r); - result.test_eq("from_bytes_mod_order (random)", scalar.serialize(), ref.serialize(group.get_order_bytes())); + result.test_bin_eq( + "from_bytes_mod_order (random)", scalar.serialize(), ref.serialize(group.get_order_bytes())); } result.end_timer(); diff -Nru botan3-3.7.1+dfsg/src/tests/test_ecdh.cpp botan3-3.12.0+dfsg/src/tests/test_ecdh.cpp --- botan3-3.7.1+dfsg/src/tests/test_ecdh.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_ecdh.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,7 +8,9 @@ #if defined(BOTAN_HAS_ECDH) #include "test_pubkey.h" + #include #include + #include #endif namespace Botan_Tests { @@ -23,7 +25,7 @@ std::string default_kdf(const VarMap& /*unused*/) const override { return "Raw"; } - bool skip_this_test(const std::string& group_id, const VarMap&) override { + bool skip_this_test(const std::string& group_id, const VarMap& /*vars*/) override { return !Botan::EC_Group::supports_named_group(group_id); } @@ -71,6 +73,39 @@ try { const auto group = Botan::EC_Group::from_name(group_name); + // Regression test: prohibit loading an all-zero private key + result.test_throws("all-zero private key is unacceptable", [&] { + const auto one = Botan::EC_Scalar::one(group); + const auto zero = one - one; // NOLINT(*-redundant-expression) + Botan::ECDH_PrivateKey(group, zero); + }); + + // Regression test: prohibit loading a public point that is the identity (point at infinity) + result.test_throws("point at infinity isn't a valid public key", [&] { + const auto infinity = Botan::EC_AffinePoint::identity(group); + Botan::ECDH_PublicKey(group, infinity); + }); + + // Regression test: prohibit ECDH-agreement with all-zero public value + result.test_throws("ECDH public value is point-at-infinity", [&] { + const auto sk = Botan::ECDH_PrivateKey(rng(), group); + const Botan::PK_Key_Agreement ka(sk, rng(), kdf); + std::vector sec1_infinity(1, 0x00); + const auto a_ss = ka.derive_key(0, sec1_infinity); + }); + + // Regression test: prohibit loading a point not on the curve + result.test_throws("point is not on curve", [&] { + const auto& base_point = Botan::EC_AffinePoint::generator(group); + auto encoded = base_point.serialize_uncompressed(); + encoded[3] -= 1; + + const Botan::ECDH_PrivateKey a_priv(rng(), group); + const auto a_pub = a_priv.public_value(); + const Botan::PK_Key_Agreement a_ka(a_priv, rng(), kdf); + const auto a_ss = a_ka.derive_key(0, encoded); + }); + for(size_t i = 0; i != 100; ++i) { const Botan::ECDH_PrivateKey a_priv(rng(), group); const auto a_pub = a_priv.public_value(); @@ -78,13 +113,13 @@ const Botan::ECDH_PrivateKey b_priv(rng(), group); const auto b_pub = b_priv.public_value(); - Botan::PK_Key_Agreement a_ka(a_priv, rng(), kdf); + const Botan::PK_Key_Agreement a_ka(a_priv, rng(), kdf); const auto a_ss = a_ka.derive_key(0, b_pub); - Botan::PK_Key_Agreement b_ka(b_priv, rng(), kdf); + const Botan::PK_Key_Agreement b_ka(b_priv, rng(), kdf); const auto b_ss = b_ka.derive_key(0, a_pub); - result.test_eq("Same shared secret", a_ss.bits_of(), b_ss.bits_of()); + result.test_bin_eq("Same shared secret", a_ss.bits_of(), b_ss.bits_of()); } } catch(std::exception& e) { result.test_failure("Exception", e.what()); diff -Nru botan3-3.7.1+dfsg/src/tests/test_ecdsa.cpp botan3-3.12.0+dfsg/src/tests/test_ecdsa.cpp --- botan3-3.7.1+dfsg/src/tests/test_ecdsa.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_ecdsa.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,13 +7,17 @@ #include "tests.h" -#include "test_rng.h" - #if defined(BOTAN_HAS_ECDSA) #include "test_pubkey.h" + #include "test_rng.h" + #include #include #include #include + #include + #include + #include + #include #endif namespace Botan_Tests { @@ -29,7 +33,7 @@ bool clear_between_callbacks() const override { return false; } - bool skip_this_test(const std::string&, const VarMap& vars) override { + bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override { return !Botan::EC_Group::supports_named_group(vars.get_req_str("Group")); } @@ -59,7 +63,7 @@ bool test_random_invalid_sigs() const override { return false; } - bool skip_this_test(const std::string&, const VarMap& vars) override { + bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override { return !Botan::EC_Group::supports_named_group(vars.get_req_str("Group")); } @@ -93,7 +97,7 @@ bool clear_between_callbacks() const override { return false; } - bool skip_this_test(const std::string&, const VarMap& vars) override { + bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override { return !Botan::EC_Group::supports_named_group(vars.get_req_str("Group")); } @@ -132,7 +136,7 @@ bool clear_between_callbacks() const override { return false; } - bool skip_this_test(const std::string&, const VarMap& vars) override { + bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override { return !Botan::EC_Group::supports_named_group(vars.get_req_str("Group")); } @@ -141,7 +145,7 @@ const BigInt x = vars.get_req_bn("X"); const auto group = Botan::EC_Group::from_name(group_id); - Botan::ECDSA_PrivateKey priv_key(this->rng(), group, x); + const Botan::ECDSA_PrivateKey priv_key(this->rng(), group, x); return priv_key.public_key(); } @@ -161,7 +165,7 @@ class ECDSA_Keygen_Tests final : public PK_Key_Generation_Test { public: std::vector keygen_params() const override { - auto grp = Botan::EC_Group::known_named_groups(); + const auto& grp = Botan::EC_Group::known_named_groups(); return std::vector(grp.begin(), grp.end()); } @@ -187,7 +191,7 @@ public: ECDSA_Key_Recovery_Tests() : Text_Based_Test("pubkey/ecdsa_key_recovery.vec", "Group,Msg,R,S,V,Pubkey") {} - bool skip_this_test(const std::string&, const VarMap& vars) override { + bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override { return !Botan::EC_Group::supports_named_group(vars.get_req_str("Group")); } @@ -204,17 +208,21 @@ const auto expected_pubkey = vars.get_req_bin("Pubkey"); try { - Botan::ECDSA_PublicKey pubkey(group, msg, R, S, V); - result.test_eq("Pubkey X coordinate", pubkey.public_key_bits(), expected_pubkey); + const Botan::ECDSA_PublicKey pubkey(group, msg, R, S, V); + result.test_bin_eq("Pubkey X coordinate", pubkey.public_key_bits(), expected_pubkey); const uint8_t computed_V = pubkey.recovery_param(msg, R, S); - result.test_eq("Recovery param is correct", static_cast(computed_V), static_cast(V)); + result.test_u8_eq("Recovery param is correct", computed_V, V); Botan::PK_Verifier verifier(pubkey, "Raw"); - auto sig = Botan::BigInt::encode_fixed_length_int_pair(R, S, group.get_order_bytes()); + const size_t n_bytes = group.get_order_bytes(); + std::vector sig(2 * n_bytes); + Botan::BufferStuffer stuffer(sig); + R.serialize_to(stuffer.next(n_bytes)); + S.serialize_to(stuffer.next(n_bytes)); - result.confirm("Signature verifies", verifier.verify_message(msg, sig)); + result.test_is_true("Signature verifies", verifier.verify_message(msg, sig)); } catch(Botan::Exception& e) { result.test_failure("Failed to recover ECDSA public key", e.what()); } @@ -233,7 +241,7 @@ bool clear_between_callbacks() const override { return false; } - bool skip_this_test(const std::string&, const VarMap& vars) override { + bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override { return !Botan::EC_Group::supports_named_group(vars.get_req_str("Group")); } @@ -283,17 +291,18 @@ Botan::PK_Verifier verifier(*pub, hash); for(size_t i = 0; i != 16; ++i) { - auto message = rng().random_vec(rng().next_byte()); + auto message = Botan::unlock(rng().random_vec(rng().next_byte())); auto sig = signer.sign_message(message, rng()); - result.test_eq("Expected signature size", sig.size(), 2 * group.get_order_bytes()); + result.test_sz_eq("Expected signature size", sig.size(), 2 * group.get_order_bytes()); - result.confirm("Signature accepted", verifier.verify_message(message, sig)); + result.test_is_true("Signature accepted", verifier.verify_message(message, sig)); const auto corrupted_message = mutate_vec(message, rng(), true); - result.confirm("Modified message rejected", !verifier.verify_message(corrupted_message, sig)); + result.test_is_true("Modified message rejected", !verifier.verify_message(corrupted_message, sig)); const auto corrupted_sig = mutate_vec(sig, rng(), true); - result.confirm("Modified signature rejected", !verifier.verify_message(message, corrupted_sig)); + result.test_is_true("Modified signature rejected", + !verifier.verify_message(message, corrupted_sig)); } } catch(std::exception& e) { result.test_failure("Exception", e.what()); @@ -308,6 +317,43 @@ } }; +class ECDSA_ExplicitCurveKey_Test : public Text_Based_Test { + public: + ECDSA_ExplicitCurveKey_Test() : Text_Based_Test("pubkey/ecdsa_explicit.vec", "Key") {} + + bool clear_between_callbacks() const override { return false; } + + bool skip_this_test(const std::string& group, const VarMap& /*vars*/) override { + return !Botan::EC_Group::supports_named_group(group); + } + + Test::Result run_one_test(const std::string& group_name, const VarMap& vars) override { + Test::Result result("ECDSA explicit key " + group_name); + + const auto key_bytes = vars.get_req_bin("Key"); + + try { + const auto expected_oid = Botan::OID::from_name(group_name).value(); + + auto key = Botan::PKCS8::load_key(key_bytes); + const auto* ecdsa = dynamic_cast(key.get()); + if(ecdsa != nullptr) { + result.test_success("Returned key was ECDSA"); + + const auto& group = ecdsa->domain(); + result.test_is_true("Key is marked as explicit encoding", group.used_explicit_encoding()); + result.test_is_true("Group has expected OID", group.get_curve_oid() == expected_oid); + } else { + result.test_failure("Returned key was some other type"); + } + } catch(Botan::Exception& e) { + result.test_failure("Failed to parse key", e.what()); + } + + return result; + } +}; + BOTAN_REGISTER_TEST("pubkey", "ecdsa_verify", ECDSA_Verification_Tests); BOTAN_REGISTER_TEST("pubkey", "ecdsa_verify_wycheproof", ECDSA_Wycheproof_Verification_Tests); BOTAN_REGISTER_TEST("pubkey", "ecdsa_sign", ECDSA_Signature_KAT_Tests); @@ -317,6 +363,7 @@ BOTAN_REGISTER_TEST("pubkey", "ecdsa_keygen_stability", ECDSA_Keygen_Stability_Tests); BOTAN_REGISTER_TEST("pubkey", "ecdsa_invalid", ECDSA_Invalid_Key_Tests); BOTAN_REGISTER_TEST("pubkey", "ecdsa_all_groups", ECDSA_AllGroups_Test); +BOTAN_REGISTER_TEST("pubkey", "ecdsa_explicit_curve_key", ECDSA_ExplicitCurveKey_Test); #endif diff -Nru botan3-3.7.1+dfsg/src/tests/test_ecgdsa.cpp botan3-3.12.0+dfsg/src/tests/test_ecgdsa.cpp --- botan3-3.7.1+dfsg/src/tests/test_ecgdsa.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_ecgdsa.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,7 @@ #if defined(BOTAN_HAS_ECGDSA) #include "test_pubkey.h" + #include #include #endif @@ -26,7 +27,7 @@ bool clear_between_callbacks() const override { return false; } - bool skip_this_test(const std::string&, const VarMap& vars) override { + bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override { return !Botan::EC_Group::supports_named_group(vars.get_req_str("Group")); } diff -Nru botan3-3.7.1+dfsg/src/tests/test_ecies.cpp botan3-3.12.0+dfsg/src/tests/test_ecies.cpp --- botan3-3.7.1+dfsg/src/tests/test_ecies.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_ecies.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,8 @@ #if defined(BOTAN_HAS_ECIES) #include #include + #include + #include #endif namespace Botan_Tests { @@ -62,10 +64,10 @@ const std::vector encrypted = ecies_enc.encrypt(plaintext, rng); if(!ciphertext.empty()) { - result.test_eq("encrypted data", encrypted, ciphertext); + result.test_bin_eq("encrypted data", encrypted, ciphertext); } const Botan::secure_vector decrypted = ecies_dec.decrypt(encrypted); - result.test_eq("decrypted data equals plaintext", decrypted, plaintext); + result.test_bin_eq("decrypted data equals plaintext", decrypted, plaintext); std::vector invalid_encrypted = encrypted; uint8_t& last_byte = invalid_encrypted[invalid_encrypted.size() - 1]; @@ -73,16 +75,16 @@ result.test_throws("throw on invalid ciphertext", [&ecies_dec, &invalid_encrypted] { ecies_dec.decrypt(invalid_encrypted); }); } catch(Botan::Lookup_Error& e) { - result.test_note(std::string("Test not executed: ") + e.what()); + result.test_note("Not available", e.what()); } } -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, - Botan::RandomNumberGenerator& rng) { +[[maybe_unused]] 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, + Botan::RandomNumberGenerator& rng) { const std::vector plaintext{1, 2, 3}; check_encrypt_decrypt(result, private_key, @@ -103,7 +105,7 @@ bool clear_between_callbacks() const override { return false; } - bool skip_this_test(const std::string&, const VarMap&) override { + bool skip_this_test(const std::string& /*header*/, const VarMap& /*vars*/) override { return !Botan::EC_Group::supports_application_specific_group(); } @@ -136,25 +138,27 @@ // (ephemeral) keys of alice const Botan::ECDH_PrivateKey eph_private_key(this->rng(), domain, r); const auto eph_public_key_bin = eph_private_key.public_value(compression_type); - result.test_eq("encoded (ephemeral) public key", eph_public_key_bin, c0); + result.test_bin_eq("encoded (ephemeral) public key", eph_public_key_bin, c0); // test secret derivation: ISO 18033 test vectors use KDF1 from ISO 18033 // no cofactor-/oldcofactor-/singlehash-/check-mode and 128 byte secret length - Botan::ECIES_KA_Params ka_params( + const 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, 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); + result.test_bin_eq("derived secret key", secret_key.bits_of(), k); // test encryption / decryption + // TODO(Botan4) clean this up after removing cofactor support + for(auto comp_type : {Botan::EC_Point_Format::Uncompressed, Botan::EC_Point_Format::Compressed, Botan::EC_Point_Format::Hybrid}) { - for(bool cofactor_mode : {true, false}) { - for(bool single_hash_mode : {true, false}) { - for(bool old_cofactor_mode : {true, false}) { - for(bool check_mode : {true, false}) { + for(const bool cofactor_mode : {true, false}) { + for(const bool single_hash_mode : {true, false}) { + for(const bool old_cofactor_mode : {true, false}) { + for(const bool check_mode : {true, false}) { Flags flags = ecies_flags(cofactor_mode, old_cofactor_mode, check_mode, single_hash_mode); if(size_t(cofactor_mode) + size_t(check_mode) + size_t(old_cofactor_mode) > 1) { @@ -172,14 +176,14 @@ continue; } - Botan::ECIES_System_Params ecies_params(eph_private_key.domain(), - "KDF2(SHA-1)", - "AES-256/CBC", - 32, - "HMAC(SHA-1)", - 20, - comp_type, - flags); + const Botan::ECIES_System_Params ecies_params(eph_private_key.domain(), + "KDF2(SHA-1)", + "AES-256/CBC", + 32, + "HMAC(SHA-1)", + 20, + comp_type, + flags); check_encrypt_decrypt( result, eph_private_key, other_private_key, ecies_params, 16, this->rng()); } @@ -202,13 +206,28 @@ Text_Based_Test("pubkey/ecies.vec", "Curve,PrivateKey,OtherPrivateKey,Kdf,Dem,DemKeyLen,Mac,MacKeyLen,Format," "CofactorMode,OldCofactorMode,CheckMode,SingleHashMode,Label,Plaintext,Ciphertext", - "Iv") {} + "Iv") { + // In order to test cofactor handling flags some of the tests use secp112r2 which has a cofactor of 4 + // TODO(Botan4) kill it with fire + if(Botan::EC_Group::supports_application_specific_group_with_cofactor()) { + auto p = Botan::BigInt::from_string("0xDB7C2ABF62E35E668076BEAD208B"); + auto a = Botan::BigInt::from_string("0x6127C24C05F38A0AAAF65C0EF02C"); + auto b = Botan::BigInt::from_string("0x51DEF1815DB5ED74FCC34C85D709"); + + auto g_x = Botan::BigInt::from_string("0x4BA30AB5E892B4E1649DD0928643"); + auto g_y = Botan::BigInt::from_string("0xADCD46F5882E3747DEF36E956E97"); + auto order = Botan::BigInt::from_string("0x36DF0AAFD8B8D7597CA10520D04B"); + auto cofactor = Botan::BigInt::from_u64(4); + m_secp112r2 = std::make_unique(p, a, b, g_x, g_y, order, cofactor); + } + } - bool skip_this_test(const std::string&, const VarMap& vars) override { + bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override { const auto curve = vars.get_req_str("Curve"); - if(curve.starts_with("-----BEGIN EC PARAMETERS")) { - return !Botan::EC_Group::supports_application_specific_group(); + // TODO(Botan4) remove this since cofactors no longer supported + if(curve == "secp112r2") { + return !Botan::EC_Group::supports_application_specific_group_with_cofactor(); } else { return !Botan::EC_Group::supports_named_group(curve); } @@ -237,10 +256,16 @@ const Flags flags = ecies_flags(cofactor_mode, old_cofactor_mode, check_mode, single_hash_mode); - // This test uses a mix of named curves plus PEM, so we use the deprecated constructor atm - const Botan::EC_Group domain(curve); - 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 auto group = [&]() { + if(curve == "secp112r2") { + return *m_secp112r2; + } else { + return Botan::EC_Group::from_name(curve); + } + }(); + + const Botan::ECDH_PrivateKey private_key(this->rng(), group, private_key_value); + const Botan::ECDH_PrivateKey other_private_key(this->rng(), group, 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); @@ -249,6 +274,9 @@ return result; } + + private: + std::unique_ptr m_secp112r2; }; BOTAN_REGISTER_TEST("pubkey", "ecies", ECIES_Tests); @@ -310,7 +338,7 @@ flags); result.test_throws("kdf not found", [&]() { - Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, *rng); + const Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, *rng); ecies_enc.encrypt(std::vector(8), *rng); }); @@ -341,7 +369,7 @@ flags); result.test_throws("mac not found", [&]() { - Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, *rng); + const Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, *rng); ecies_enc.encrypt(std::vector(8), *rng); }); @@ -372,7 +400,7 @@ flags); result.test_throws("cipher not found", [&]() { - Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, *rng); + const Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, *rng); ecies_enc.encrypt(std::vector(8), *rng); }); diff -Nru botan3-3.7.1+dfsg/src/tests/test_eckcdsa.cpp botan3-3.12.0+dfsg/src/tests/test_eckcdsa.cpp --- botan3-3.7.1+dfsg/src/tests/test_eckcdsa.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_eckcdsa.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,6 +10,7 @@ #if defined(BOTAN_HAS_ECKCDSA) #include "test_pubkey.h" + #include #include #endif @@ -24,7 +25,7 @@ ECKCDSA_Signature_KAT_Tests() : PK_Signature_Generation_Test("ECKCDSA", "pubkey/eckcdsa.vec", "Group,X,Hash,Msg,Nonce,Signature") {} - bool skip_this_test(const std::string&, const VarMap& vars) override { + bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override { return !Botan::EC_Group::supports_named_group(vars.get_req_str("Group")); } diff -Nru botan3-3.7.1+dfsg/src/tests/test_ed25519.cpp botan3-3.12.0+dfsg/src/tests/test_ed25519.cpp --- botan3-3.7.1+dfsg/src/tests/test_ed25519.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_ed25519.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,6 +11,7 @@ #include #include #include + #include #include #endif @@ -54,11 +55,9 @@ const std::vector privkey = vars.get_req_bin("Privkey"); const std::vector pubkey = vars.get_req_bin("Pubkey"); - Botan::secure_vector seed(privkey.begin(), privkey.end()); + auto key = std::make_unique(Botan::Ed25519_PrivateKey::from_seed(privkey)); - auto key = std::make_unique(seed); - - if(key->get_public_key() != pubkey) { + if(key->raw_public_key_bits() != pubkey) { throw Test_Error("Invalid Ed25519 key in test data"); } @@ -84,11 +83,11 @@ Botan::DataSource_Memory priv_data(priv_key_str); auto priv_key = Botan::PKCS8::load_key(priv_data); - result.confirm("Private key loaded", priv_key != nullptr); + result.test_is_true("Private key loaded", priv_key != nullptr); Botan::DataSource_Memory pub_data(pub_key_str); auto pub_key = Botan::X509::load_key(pub_data); - result.confirm("Public key loaded", pub_key != nullptr); + result.test_is_true("Public key loaded", pub_key != nullptr); Botan::PK_Signer signer(*priv_key, this->rng(), "Pure"); signer.update("message"); @@ -96,7 +95,7 @@ Botan::PK_Verifier verifier(*pub_key, "Pure"); verifier.update("message"); - result.confirm("Signature valid", verifier.check_signature(sig)); + result.test_is_true("Signature valid", verifier.check_signature(sig)); return std::vector{result}; } diff -Nru botan3-3.7.1+dfsg/src/tests/test_ed448.cpp botan3-3.12.0+dfsg/src/tests/test_ed448.cpp --- botan3-3.7.1+dfsg/src/tests/test_ed448.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_ed448.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -14,6 +14,7 @@ #include #include #include + #include namespace Botan_Tests { namespace { @@ -57,7 +58,9 @@ return sk; } - bool skip_this_test(const std::string&, const VarMap& vars) override { return vars.get_req_sz("Valid") != 1; } + bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override { + return vars.get_req_sz("Valid") != 1; + } }; class Ed448_Verification_Tests : public PK_Signature_Verification_Test { @@ -76,7 +79,7 @@ template std::array to_array(std::span sp) { BOTAN_ASSERT_NOMSG(sp.size() == S); - std::array arr; + std::array arr{}; Botan::copy_mem(arr.data(), sp.data(), S); return arr; } @@ -84,7 +87,7 @@ public: Ed448_General_Test() : Text_Based_Test("pubkey/ed448.vec", "Msg,PrivateKey,PublicKey,Valid,Signature") {} - Test::Result run_one_test(const std::string&, const VarMap& vars) final { + Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) final { Test::Result result("Ed448 general tests"); const auto pub_key_ref = to_array<57>(vars.get_req_bin("PublicKey")); @@ -94,17 +97,19 @@ const auto p = Botan::Ed448Point::decode(pub_key_ref); const auto reencoded_point_data = p.encode(); - result.test_is_eq("Enc- and decoding roundtrip", reencoded_point_data, pub_key_ref); + result.test_bin_eq("Enc- and decoding roundtrip", reencoded_point_data, pub_key_ref); // Test public key creation const auto pub_key = Botan::create_pk_from_sk(sk); - result.test_is_eq("Public key from secret key", pub_key, pub_key_ref); + result.test_bin_eq("Public key from secret key", pub_key, pub_key_ref); return result; } - bool skip_this_test(const std::string&, const VarMap& vars) override { return vars.get_req_sz("Valid") != 1; } + bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override { + return vars.get_req_sz("Valid") != 1; + } }; class Ed448_Utils_Test final : public Test { @@ -129,11 +134,11 @@ const std::vector> test_vectors = { full, std::array{0x42}, std::array{0}}; - for(auto& t : test_vectors) { + for(const auto& t : test_vectors) { const auto ref = reduce_mod_L_ref(t); - std::array res; + std::array res{}; result.test_no_throw("Reduce mod L does not throw", [&] { res = Botan::Scalar448(t).to_bytes<56>(); }); - result.test_is_eq("Reduce mod L result", res, ref); + result.test_bin_eq("Reduce mod L result", res, ref); } return result; diff -Nru botan3-3.7.1+dfsg/src/tests/test_entropy.cpp botan3-3.12.0+dfsg/src/tests/test_entropy.cpp --- botan3-3.7.1+dfsg/src/tests/test_entropy.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_entropy.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -26,7 +26,7 @@ std::vector run() override { Botan::Entropy_Sources& srcs = Botan::Entropy_Sources::global_sources(); - std::vector src_names = srcs.enabled_sources(); + const std::vector src_names = srcs.enabled_sources(); std::vector results; @@ -38,13 +38,13 @@ try { SeedCapturing_RNG rng; - size_t bits = srcs.poll_just(rng, src_name); + const size_t bits = srcs.poll_just(rng, src_name); - result.test_gte("Entropy estimate", rng.seed_material().size() * 8, bits); + result.test_sz_gte("Entropy estimate", rng.seed_material().size() * 8, bits); if(rng.samples() > 0) { - result.test_gte("Seed material bytes", rng.seed_material().size(), 1); - result.test_gte("Samples", rng.samples(), 1); + result.test_sz_gte("Seed material bytes", rng.seed_material().size(), 1); + result.test_sz_gte("Samples", rng.samples(), 1); } result.test_note("poll result", rng.seed_material()); @@ -69,7 +69,7 @@ comp1_size = compressed.size(); - result.test_gte( + result.test_sz_gte( comp_algo + " compressed entropy better than advertised", compressed.size() * 8, bits); } catch(std::exception& e) { result.test_failure(comp_algo + " exception while compressing", e.what()); @@ -77,7 +77,7 @@ SeedCapturing_RNG rng2; - size_t bits2 = srcs.poll_just(rng2, src_name); + const size_t bits2 = srcs.poll_just(rng2, src_name); result.test_note("poll 2 result", rng2.seed_material()); @@ -92,13 +92,13 @@ comp->start(); comp->finish(compressed); - size_t comp2_size = compressed.size(); + const size_t comp2_size = compressed.size(); - result.test_lt("Two blocks of entropy are larger than one", comp1_size, comp2_size); + result.test_sz_lt("Two blocks of entropy are larger than one", comp1_size, comp2_size); - size_t comp_diff = comp2_size - comp1_size; + const size_t comp_diff = comp2_size - comp1_size; - result.test_gte( + result.test_sz_gte( comp_algo + " diff compressed entropy better than advertised", comp_diff * 8, bits2); } catch(std::exception& e) { result.test_failure(comp_algo + " exception while compressing", e.what()); diff -Nru botan3-3.7.1+dfsg/src/tests/test_ffi.cpp botan3-3.12.0+dfsg/src/tests/test_ffi.cpp --- botan3-3.7.1+dfsg/src/tests/test_ffi.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_ffi.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,20 +7,29 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#define BOTAN_NO_DEPRECATED_WARNINGS - #include "tests.h" #include #if defined(BOTAN_HAS_FFI) + #include + #include #include #include + #include + #include + #include #include #include #include + #include #include #endif +#if defined(BOTAN_HAS_X509) + #include + #include +#endif + #if defined(BOTAN_HAS_TPM2) #include #include @@ -32,48 +41,121 @@ #if defined(BOTAN_HAS_FFI) - // NOLINTNEXTLINE(*-macro-usage) +// NOLINTBEGIN(*-macro-usage) + #define _TEST_FFI_STR_HELPER(x) #x - // NOLINTNEXTLINE(*-macro-usage) + #define _TEST_FFI_STR(x) _TEST_FFI_STR_HELPER(x) - // NOLINTNEXTLINE(*-macro-usage) + #define _TEST_FFI_SOURCE_LOCATION(func, file, line) (func " invoked at " file ":" _TEST_FFI_STR(line)) - // NOLINTNEXTLINE(*-macro-usage) #define TEST_FFI_OK(func, args) result.test_rc_ok(_TEST_FFI_SOURCE_LOCATION(#func, __FILE__, __LINE__), func args) - // NOLINTNEXTLINE(*-macro-usage) + #define TEST_FFI_INIT(func, args) \ result.test_rc_init(_TEST_FFI_SOURCE_LOCATION(#func, __FILE__, __LINE__), func args) - // NOLINTNEXTLINE(*-macro-usage) + #define TEST_FFI_FAIL(msg, func, args) \ result.test_rc_fail(_TEST_FFI_SOURCE_LOCATION(#func, __FILE__, __LINE__), msg, func args) - // NOLINTNEXTLINE(*-macro-usage) + #define TEST_FFI_RC(rc, func, args) \ - result.test_rc(_TEST_FFI_SOURCE_LOCATION(#func, __FILE__, __LINE__), rc, func args) + result.test_rc(_TEST_FFI_SOURCE_LOCATION(#func, __FILE__, __LINE__), func args, rc) - // NOLINTNEXTLINE(*-macro-usage) #define REQUIRE_FFI_OK(func, args) \ if(!TEST_FFI_OK(func, args)) { \ result.test_note("Exiting test early due to failure"); \ return; \ } +// NOLINTEND(*-macro-usage) + +/** + * Helper class for testing "view"-style API functions that take a callback + * that gets passed a variable-length buffer of bytes. + * + * Example: + * botan_privkey_t priv; + * ViewBytesSink sink; + * botan_privkey_view_raw(priv, sink.delegate(), sink.callback()); + * std::cout << hex_encode(sink.get()) << std::endl; + */ +class ViewBytesSink final { + public: + void* delegate() { return this; } + + botan_view_bin_fn callback() { return &write_fn; } + + std::span get() const { return m_buf; } + + const uint8_t* data() const { return m_buf.data(); } + + size_t size() const { return m_buf.size(); } + + private: + static int write_fn(void* ctx, const uint8_t buf[], size_t len) { + if(ctx == nullptr || buf == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + auto* sink = static_cast(ctx); + sink->m_buf.assign(buf, buf + len); + + return BOTAN_FFI_SUCCESS; + } + + private: + std::vector m_buf; +}; + +/** + * See ViewBytesSink for how to use this. Works for `botan_view_str_fn` instead. +*/ +class ViewStringSink final { + public: + void* delegate() { return this; } + + botan_view_str_fn callback() { return &write_fn; } + + const std::string& get() { return m_str; } + + private: + static int write_fn(void* ctx, const char* str, size_t len) { + if(ctx == nullptr || str == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + auto* sink = static_cast(ctx); + // discard the null terminator + sink->m_str = std::string(str, len - 1); + + return BOTAN_FFI_SUCCESS; + } + + private: + std::string m_str; +}; + +// NOLINTBEGIN(*-init-variables) + class FFI_Test : public Test { public: std::vector run() override { Test::Result result(this->name()); - botan_rng_t rng; - if(botan_rng_init(&rng, "system") != 0) { - result.test_failure("Failed to init RNG"); - return {result}; - } + if(!skip_this_test()) { + botan_rng_t rng; + if(botan_rng_init(&rng, "system") != 0) { + result.test_failure("Failed to init RNG"); + return {result}; + } - result.start_timer(); - ffi_test(result, rng); - result.end_timer(); + result.start_timer(); + ffi_test(result, rng); + result.end_timer(); - botan_rng_destroy(rng); + botan_rng_destroy(rng); + } else { + result.test_note("FFI test asked to be skipped"); + } return {result}; } @@ -81,6 +163,8 @@ private: virtual std::string name() const = 0; virtual void ffi_test(Test::Result& result, botan_rng_t rng) = 0; + + virtual bool skip_this_test() const { return false; } }; void ffi_test_pubkey_export(Test::Result& result, botan_pubkey_t pub, botan_privkey_t priv, botan_rng_t rng) { @@ -123,7 +207,7 @@ privkey.resize(privkey_len); - result.test_gte("Reasonable size", privkey.size(), 32); + result.test_sz_gte("Reasonable size", privkey.size(), 32); // reimport exported private key botan_privkey_t copy; @@ -203,10 +287,10 @@ 0)); if(pbe_hash == "Scrypt") { - result.test_eq("Scrypt iters set to zero in this API", pbkdf_iters_out, 0); + result.test_sz_eq("Scrypt iters set to zero in this API", pbkdf_iters_out, 0); } else { // PBKDF2 currently always rounds to multiple of 2000 - result.test_eq("Expected PBKDF2 iters", pbkdf_iters_out % 2000, 0); + result.test_sz_eq("Expected PBKDF2 iters", pbkdf_iters_out % 2000, 0); } privkey.resize(privkey_len); @@ -218,7 +302,7 @@ // calculate fingerprint size_t strength = 0; TEST_FFI_OK(botan_pubkey_estimated_strength, (pub, &strength)); - result.test_gte("estimated strength", strength, 1); + result.test_sz_gte("estimated strength", strength, 1); size_t fingerprint_len = 0; TEST_FFI_RC( @@ -233,24 +317,25 @@ std::string name() const override { return "FFI Utils"; } void ffi_test(Test::Result& result, botan_rng_t /*unused*/) override { - result.test_is_eq("FFI API version", botan_ffi_api_version(), uint32_t(BOTAN_HAS_FFI)); - result.test_is_eq("Major version", botan_version_major(), Botan::version_major()); - result.test_is_eq("Minor version", botan_version_minor(), Botan::version_minor()); - result.test_is_eq("Patch version", botan_version_patch(), Botan::version_patch()); - result.test_is_eq("Botan version", botan_version_string(), Botan::version_cstr()); - result.test_is_eq("Botan version datestamp", botan_version_datestamp(), Botan::version_datestamp()); - result.test_is_eq("FFI supports its own version", botan_ffi_supports_api(botan_ffi_api_version()), 0); - - result.test_is_eq("FFI compile time time var matches botan_ffi_api_version", - botan_ffi_api_version(), - uint32_t(BOTAN_FFI_API_VERSION)); - - result.test_is_eq("FFI supports 2.0 version", botan_ffi_supports_api(20150515), 0); - result.test_is_eq("FFI supports 2.1 version", botan_ffi_supports_api(20170327), 0); - result.test_is_eq("FFI supports 2.3 version", botan_ffi_supports_api(20170815), 0); - result.test_is_eq("FFI supports 2.8 version", botan_ffi_supports_api(20180713), 0); + result.test_u32_eq("FFI API version macro", uint32_t(BOTAN_FFI_API_VERSION), uint32_t(BOTAN_HAS_FFI)); + result.test_u32_eq("FFI API version function", botan_ffi_api_version(), uint32_t(BOTAN_HAS_FFI)); + result.test_u32_eq("Major version", botan_version_major(), Botan::version_major()); + result.test_u32_eq("Minor version", botan_version_minor(), Botan::version_minor()); + result.test_u32_eq("Patch version", botan_version_patch(), Botan::version_patch()); + result.test_str_eq("Botan version", botan_version_string(), Botan::version_cstr()); + result.test_u32_eq("Botan version datestamp", botan_version_datestamp(), Botan::version_datestamp()); + result.test_rc_ok("FFI supports its own version", botan_ffi_supports_api(botan_ffi_api_version())); + + result.test_u32_eq("FFI compile time time var matches botan_ffi_api_version", + botan_ffi_api_version(), + uint32_t(BOTAN_FFI_API_VERSION)); + + result.test_rc_ok("FFI supports 2.0 version", botan_ffi_supports_api(20150515)); + result.test_rc_ok("FFI supports 2.1 version", botan_ffi_supports_api(20170327)); + result.test_rc_ok("FFI supports 2.3 version", botan_ffi_supports_api(20170815)); + result.test_rc_ok("FFI supports 2.8 version", botan_ffi_supports_api(20180713)); - result.test_is_eq("FFI doesn't support bogus version", botan_ffi_supports_api(20160229), -1); + result.test_rc("FFI doesn't support bogus version", botan_ffi_supports_api(20160229), -1); const std::vector mem1 = {0xFF, 0xAA, 0xFF}; const std::vector mem2 = {0xFF, 0xA9, 0xFF}; @@ -260,19 +345,17 @@ std::vector to_zero = {0xFF, 0xA0}; TEST_FFI_OK(botan_scrub_mem, (to_zero.data(), to_zero.size())); - result.confirm("scrub_memory zeros", to_zero[0] == 0 && to_zero[1] == 0); + result.test_is_true("scrub_memory zeros", to_zero[0] == 0 && to_zero[1] == 0); const std::vector bin = {0xAA, 0xDE, 0x01}; std::string outstr; - std::vector outbuf; - outstr.resize(2 * bin.size()); - TEST_FFI_OK(botan_hex_encode, (bin.data(), bin.size(), &outstr[0], 0)); - result.test_eq("uppercase hex", outstr, "AADE01"); + TEST_FFI_OK(botan_hex_encode, (bin.data(), bin.size(), outstr.data(), 0)); + result.test_str_eq("uppercase hex", outstr, "AADE01"); - TEST_FFI_OK(botan_hex_encode, (bin.data(), bin.size(), &outstr[0], BOTAN_FFI_HEX_LOWER_CASE)); - result.test_eq("lowercase hex", outstr, "aade01"); + TEST_FFI_OK(botan_hex_encode, (bin.data(), bin.size(), outstr.data(), BOTAN_FFI_HEX_LOWER_CASE)); + result.test_str_eq("lowercase hex", outstr, "aade01"); } }; @@ -298,12 +381,12 @@ REQUIRE_FFI_OK(botan_rng_init, (&null_rng, "null")); int rc = botan_rng_init(&hwrng_rng, "hwrng"); - result.confirm("Either success or not implemented", rc == 0 || rc == BOTAN_FFI_ERROR_NOT_IMPLEMENTED); + result.test_is_true("Either success or not implemented", rc == 0 || rc == BOTAN_FFI_ERROR_NOT_IMPLEMENTED); std::vector outbuf(512); rc = botan_rng_init(&rng, "user-threadsafe"); - result.confirm("Either success or not implemented", rc == 0 || rc == BOTAN_FFI_ERROR_NOT_IMPLEMENTED); + result.test_is_true("Either success or not implemented", rc == 0 || rc == BOTAN_FFI_ERROR_NOT_IMPLEMENTED); if(rc != 0) { REQUIRE_FFI_OK(botan_rng_init, (&rng, "user")); @@ -315,7 +398,7 @@ TEST_FFI_OK(botan_rng_reseed, (rng, 256)); TEST_FFI_RC(BOTAN_FFI_ERROR_INVALID_OBJECT_STATE, botan_rng_reseed_from_rng, (rng, null_rng, 256)); - if(hwrng_rng) { + if(hwrng_rng != nullptr) { TEST_FFI_OK(botan_rng_reseed_from_rng, (rng, hwrng_rng, 256)); } TEST_FFI_RC(BOTAN_FFI_ERROR_INVALID_OBJECT_STATE, botan_rng_get, (null_rng, outbuf.data(), outbuf.size())); @@ -359,22 +442,22 @@ (&custom_rng, "custom rng", &cb_counter, custom_get_cb, custom_add_entropy_cb, custom_destroy_cb))) { Botan::clear_mem(outbuf.data(), outbuf.size()); TEST_FFI_OK(botan_rng_get, (custom_rng, outbuf.data(), outbuf.size())); - result.test_eq("custom_get_cb called", cb_counter, 1); + result.test_sz_eq("custom_get_cb called", cb_counter, 1); std::vector pattern(outbuf.size(), 0x12); - result.test_eq("custom_get_cb returned bytes", pattern, outbuf); + result.test_bin_eq("custom_get_cb returned bytes", pattern, outbuf); TEST_FFI_OK(botan_rng_reseed, (custom_rng, 256)); - result.test_eq("custom_add_entropy_cb called", cb_counter, 2); + result.test_sz_eq("custom_add_entropy_cb called", cb_counter, 2); TEST_FFI_OK(botan_rng_reseed_from_rng, (custom_rng, system_rng, 256)); - result.test_eq("custom_add_entropy_cb called", cb_counter, 3); + result.test_sz_eq("custom_add_entropy_cb called", cb_counter, 3); uint8_t not_really_entropy[32] = {0}; TEST_FFI_OK(botan_rng_add_entropy, (custom_rng, not_really_entropy, 32)); - result.test_eq("custom_add_entropy_cb called", cb_counter, 4); + result.test_sz_eq("custom_add_entropy_cb called", cb_counter, 4); TEST_FFI_OK(botan_rng_destroy, (custom_rng)); - result.test_eq("custom_destroy_cb called", cb_counter, 5); + result.test_sz_eq("custom_destroy_cb called", cb_counter, 5); } #ifdef BOTAN_HAS_JITTER_RNG @@ -595,7 +678,7 @@ public: std::string name() const override { return "FFI CRL"; } - void ffi_test(Test::Result& result, botan_rng_t /*unused*/) override { + void ffi_test(Test::Result& result, botan_rng_t rng) override { const char* crl_string = "-----BEGIN X509 CRL-----\n" "MIICoTCCAQkCAQEwDQYJKoZIhvcNAQELBQAwgZQxLTArBgNVBAMTJFVzYWJsZSBj\n" @@ -620,6 +703,40 @@ return; } + botan_x509_crl_t crl_without_next_update; + if(!TEST_FFI_INIT(botan_x509_crl_load_file, + (&crl_without_next_update, + Test::data_file("x509/misc/crl_without_nextupdate/valid_forever.crl").c_str()))) { + return; + } + + uint64_t this_update; + uint64_t next_update; + TEST_FFI_RC(BOTAN_FFI_ERROR_NO_VALUE, botan_x509_crl_next_update, (crl_without_next_update, &next_update)); + TEST_FFI_RC(BOTAN_FFI_ERROR_NO_VALUE, botan_x509_crl_next_update, (crl_without_next_update, nullptr)); + TEST_FFI_OK(botan_x509_crl_this_update, (bytecrl, &this_update)); + TEST_FFI_OK(botan_x509_crl_next_update, (bytecrl, &next_update)); + result.test_u64_eq( + "this update", this_update, Botan::calendar_point(2050, 2, 25, 15, 21, 42).seconds_since_epoch()); + result.test_u64_eq( + "next update", next_update, Botan::calendar_point(2050, 2, 25, 15, 24, 41).seconds_since_epoch()); + + TEST_FFI_RC(BOTAN_FFI_ERROR_NULL_POINTER, botan_x509_crl_this_update, (bytecrl, nullptr)); + TEST_FFI_RC(BOTAN_FFI_ERROR_NULL_POINTER, botan_x509_crl_next_update, (bytecrl, nullptr)); + + ViewBytesSink akid; + TEST_FFI_OK(botan_x509_crl_view_binary_values, + (bytecrl, BOTAN_X509_AUTHORITY_KEY_IDENTIFIER, 0, akid.delegate(), akid.callback())); + result.test_bin_eq("authority key ID", akid.get(), "4ACF102F238FAB555A3F2732E2811CE7444C81F9"); + + TEST_FFI_RC(BOTAN_FFI_ERROR_NO_VALUE, + botan_x509_crl_view_binary_values, + (bytecrl, BOTAN_X509_SUBJECT_KEY_IDENTIFIER, 0, akid.delegate(), akid.callback())); + size_t akid_count; + TEST_FFI_OK(botan_x509_crl_view_binary_values_count, + (bytecrl, BOTAN_X509_SUBJECT_KEY_IDENTIFIER, &akid_count)); + result.test_sz_eq("no subject key ID entries", akid_count, 0); + botan_x509_crl_t crl; REQUIRE_FFI_OK(botan_x509_crl_load_file, (&crl, Test::data_file("x509/nist/root.crl").c_str())); @@ -629,13 +746,175 @@ TEST_FFI_OK(botan_x509_cert_destroy, (cert1)); botan_x509_cert_t cert2; + std::vector cert2_serial; + botan_mp_t cert2_serial_bn; + size_t cert2_serial_len = 0; REQUIRE_FFI_OK(botan_x509_cert_load_file, (&cert2, Test::data_file("x509/nist/test20/int.crt").c_str())); TEST_FFI_RC(0, botan_x509_is_revoked, (crl, cert2)); TEST_FFI_RC(-1, botan_x509_is_revoked, (bytecrl, cert2)); + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, + botan_x509_cert_get_serial_number, + (cert2, nullptr, &cert2_serial_len)); + cert2_serial.resize(cert2_serial_len); + TEST_FFI_OK(botan_x509_cert_get_serial_number, (cert2, cert2_serial.data(), &cert2_serial_len)); + TEST_FFI_OK(botan_x509_cert_serial_number, (cert2, &cert2_serial_bn)); TEST_FFI_OK(botan_x509_cert_destroy, (cert2)); + size_t entries; + TEST_FFI_OK(botan_x509_crl_entries_count, (crl, &entries)); + result.test_sz_eq("one revoked cert", entries, 1); + TEST_FFI_OK(botan_x509_crl_entries_count, (bytecrl, &entries)); + result.test_sz_eq("no revoked cert", entries, 0); + + ViewBytesSink serial; + TEST_FFI_OK(botan_mp_view_bin, (cert2_serial_bn, serial.delegate(), serial.callback())); + result.test_bin_eq("serial == serial_bn", serial.get(), cert2_serial); + TEST_FFI_OK(botan_mp_destroy, (cert2_serial_bn)); + + botan_x509_crl_entry_t entry; + TEST_FFI_OK(botan_x509_crl_entries, (crl, 0, &entry)); + + uint64_t ts; + int reason; + botan_mp_t entry_serial_bn; + + TEST_FFI_OK(botan_x509_crl_entry_view_serial_number, (entry, serial.delegate(), serial.callback())); + TEST_FFI_OK(botan_x509_crl_entry_serial_number, (entry, &entry_serial_bn)); + TEST_FFI_OK(botan_x509_crl_entry_revocation_date, (entry, &ts)); + TEST_FFI_OK(botan_x509_crl_entry_reason, (entry, &reason)); + TEST_FFI_OK(botan_x509_crl_entry_destroy, (entry)); + + #if defined(BOTAN_HAS_X509) + result.test_u8_eq( + "Reason", static_cast(reason), Botan::to_underlying(Botan::CRL_Code::KeyCompromise)); + #endif + result.test_u64_eq("Revocation time", ts, Botan::calendar_point(1999, 1, 1, 12, 0, 0).seconds_since_epoch()); + result.test_bin_eq("Revoked cert serial", serial.get(), cert2_serial); + + TEST_FFI_OK(botan_mp_view_bin, (entry_serial_bn, serial.delegate(), serial.callback())); + result.test_bin_eq("Revoked cert serial_bn", serial.get(), cert2_serial); + TEST_FFI_OK(botan_mp_destroy, (entry_serial_bn)); + + TEST_FFI_RC(BOTAN_FFI_ERROR_OUT_OF_RANGE, botan_x509_crl_entries, (crl, 1, &entry)); + TEST_FFI_RC(BOTAN_FFI_ERROR_OUT_OF_RANGE, botan_x509_crl_entries, (bytecrl, 0, &entry)); + + ViewStringSink crl_pem; + TEST_FFI_OK(botan_x509_crl_view_string_values, + (bytecrl, BOTAN_X509_PEM_ENCODING, 0, crl_pem.delegate(), crl_pem.callback())); + size_t pem_count; + TEST_FFI_OK(botan_x509_crl_view_string_values_count, (bytecrl, BOTAN_X509_PEM_ENCODING, &pem_count)); + result.test_sz_eq("one PEM encoding", pem_count, 1); + + auto remove_newlines = [](std::string_view str) { + auto out = std::string(str); + std::erase(out, '\n'); + std::erase(out, '\r'); + return out; + }; + + result.test_str_eq("CRL PEM", remove_newlines(crl_pem.get()), remove_newlines(crl_string)); + TEST_FFI_OK(botan_x509_crl_destroy, (crl)); TEST_FFI_OK(botan_x509_crl_destroy, (bytecrl)); + TEST_FFI_OK(botan_x509_crl_destroy, (crl_without_next_update)); + + const uint64_t now = + std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()) + .count(); + + const char* priv_string = + "-----BEGIN PRIVATE KEY-----\n" + "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgoVEKnWZw2Bfrf3MM\n" + "WLrfvRcAqq/sOf58jny37NLGQHShRANCAARageRLkKQEh1M86zvqeeesx2u9duLP\n" + "iWtHjIcunpiq6+IiB8IVu7Ncu6uPKoFS/mWzTvjgdNusmgNle9p3OAbE\n" + "-----END PRIVATE KEY-----"; + + botan_privkey_t ca_key; + botan_x509_cert_t ca_cert; + botan_x509_cert_t sub1_cert; + botan_x509_cert_t sub2_cert; + + REQUIRE_FFI_OK(botan_privkey_load, + (&ca_key, nullptr, reinterpret_cast(priv_string), 240, nullptr)); + REQUIRE_FFI_OK(botan_x509_cert_load_file, (&ca_cert, Test::data_file("x509/crl/ca.crt").c_str())); + REQUIRE_FFI_OK(botan_x509_cert_load_file, (&sub1_cert, Test::data_file("x509/crl/sub1.crt").c_str())); + REQUIRE_FFI_OK(botan_x509_cert_load_file, (&sub2_cert, Test::data_file("x509/crl/sub2.crt").c_str())); + + botan_pubkey_t ca_pubkey; + REQUIRE_FFI_OK(botan_privkey_export_pubkey, (&ca_pubkey, ca_key)); + + botan_x509_crl_t empty_crl; + TEST_FFI_OK(botan_x509_crl_create, (&empty_crl, rng, ca_cert, ca_key, now, 86400, nullptr, nullptr)); + + int rc; + // both validate, because the crl is empty + TEST_FFI_RC(0, + botan_x509_cert_verify_with_crl, + (&rc, sub1_cert, nullptr, 0, &ca_cert, 1, &empty_crl, 1, nullptr, 0, nullptr, 0)); + TEST_FFI_RC(0, + botan_x509_cert_verify_with_crl, + (&rc, sub2_cert, nullptr, 0, &ca_cert, 1, &empty_crl, 1, nullptr, 0, nullptr, 0)); + + botan_x509_crl_entry_t crl_entry; + TEST_FFI_RC(BOTAN_FFI_ERROR_OUT_OF_RANGE, botan_x509_crl_entries, (empty_crl, 0, &crl_entry)); + TEST_FFI_OK(botan_x509_crl_entry_create, (&crl_entry, sub2_cert, BOTAN_CRL_ENTRY_KEY_COMPROMISE)); + + botan_x509_crl_t new_crl; + const botan_x509_crl_entry_t crl_entries[1] = {crl_entry}; + + TEST_FFI_RC(BOTAN_FFI_ERROR_NULL_POINTER, + botan_x509_crl_update, + (&new_crl, empty_crl, rng, ca_cert, ca_key, now, 86400, nullptr, 1, nullptr, nullptr)); + TEST_FFI_OK(botan_x509_crl_update, + (&new_crl, empty_crl, rng, ca_cert, ca_key, now, 86400, crl_entries, 1, nullptr, nullptr)); + // sub 1 still validates + TEST_FFI_RC(0, + botan_x509_cert_verify_with_crl, + (&rc, sub1_cert, nullptr, 0, &ca_cert, 1, &new_crl, 1, nullptr, 0, nullptr, 0)); + // but sub 2 is revoked + TEST_FFI_RC(1, + botan_x509_cert_verify_with_crl, + (&rc, sub2_cert, nullptr, 0, &ca_cert, 1, &new_crl, 1, nullptr, 0, nullptr, 0)); + + botan_x509_crl_entry_t crl_entry_2; + TEST_FFI_RC(BOTAN_FFI_ERROR_OUT_OF_RANGE, botan_x509_crl_entries, (new_crl, 1, &crl_entry_2)); + TEST_FFI_OK(botan_x509_crl_entries, (new_crl, 0, &crl_entry_2)); + + botan_mp_t serial_from_str; + TEST_FFI_OK(botan_mp_init, (&serial_from_str)); + TEST_FFI_OK(botan_mp_set_from_str, (serial_from_str, "270431672985589325219914342203841486494")); + + uint64_t expire_time; + TEST_FFI_OK(botan_x509_crl_entry_revocation_date, (crl_entry_2, &expire_time)); + TEST_FFI_OK(botan_x509_crl_entry_reason, (crl_entry_2, &reason)); + + botan_mp_t serial_from_crl; + TEST_FFI_OK(botan_x509_crl_entry_serial_number, (crl_entry_2, &serial_from_crl)); + TEST_FFI_RC(1, botan_mp_equal, (serial_from_str, serial_from_crl)); + result.test_is_true("expire time is correct", now - 20 <= expire_time && expire_time <= now + 20); + result.test_is_true("reason is correct", reason == BOTAN_CRL_ENTRY_KEY_COMPROMISE); + + TEST_FFI_RC(1, botan_x509_crl_verify_signature, (new_crl, ca_pubkey)); + + botan_x509_crl_t even_newer_crl; + TEST_FFI_OK(botan_x509_crl_update, + (&even_newer_crl, new_crl, rng, ca_cert, ca_key, now, 456, nullptr, 0, nullptr, nullptr)); + + TEST_FFI_OK(botan_x509_crl_next_update, (even_newer_crl, &expire_time)); + result.test_is_true("expire time is correct", expire_time == now + 456); + + TEST_FFI_OK(botan_x509_crl_entry_destroy, (crl_entry)); + TEST_FFI_OK(botan_x509_crl_entry_destroy, (crl_entry_2)); + TEST_FFI_OK(botan_mp_destroy, (serial_from_str)); + TEST_FFI_OK(botan_mp_destroy, (serial_from_crl)); + TEST_FFI_OK(botan_x509_crl_destroy, (empty_crl)); + TEST_FFI_OK(botan_x509_crl_destroy, (new_crl)); + TEST_FFI_OK(botan_x509_crl_destroy, (even_newer_crl)); + TEST_FFI_OK(botan_x509_cert_destroy, (ca_cert)); + TEST_FFI_OK(botan_x509_cert_destroy, (sub1_cert)); + TEST_FFI_OK(botan_x509_cert_destroy, (sub2_cert)); + TEST_FFI_OK(botan_pubkey_destroy, (ca_pubkey)); + TEST_FFI_OK(botan_privkey_destroy, (ca_key)); } }; @@ -643,6 +922,75 @@ public: std::string name() const override { return "FFI Cert Validation"; } + bool skip_this_test() const override { + #if !defined(BOTAN_HAS_PKCSV15_SIGNATURE_PADDING) + return true; + #else + return false; + #endif + } + + void verify_bare_pkcs1_rsa_signature(Test::Result& result, botan_x509_cert_t ee, botan_x509_cert_t ca) { + ViewBytesSink tbs_data; + ViewBytesSink sig_scheme; + ViewBytesSink signature; + ViewBytesSink public_key; + + TEST_FFI_OK(botan_x509_cert_view_binary_values, + (ee, BOTAN_X509_TBS_DATA_BITS, 0, tbs_data.delegate(), tbs_data.callback())); + TEST_FFI_OK(botan_x509_cert_view_binary_values, + (ee, BOTAN_X509_SIGNATURE_SCHEME_BITS, 0, sig_scheme.delegate(), sig_scheme.callback())); + TEST_FFI_OK(botan_x509_cert_view_binary_values, + (ee, BOTAN_X509_SIGNATURE_BITS, 0, signature.delegate(), signature.callback())); + TEST_FFI_OK(botan_x509_cert_view_binary_values, + (ca, BOTAN_X509_PUBLIC_KEY_PKCS8_BITS, 0, public_key.delegate(), public_key.callback())); + + // These values exist exactly once in a certificate + TEST_FFI_RC(BOTAN_FFI_ERROR_OUT_OF_RANGE, + botan_x509_cert_view_binary_values, + (ee, BOTAN_X509_TBS_DATA_BITS, 1, tbs_data.delegate(), tbs_data.callback())); + TEST_FFI_RC(BOTAN_FFI_ERROR_OUT_OF_RANGE, + botan_x509_cert_view_binary_values, + (ee, BOTAN_X509_SIGNATURE_SCHEME_BITS, 1, sig_scheme.delegate(), sig_scheme.callback())); + TEST_FFI_RC(BOTAN_FFI_ERROR_OUT_OF_RANGE, + botan_x509_cert_view_binary_values, + (ee, BOTAN_X509_SIGNATURE_BITS, 1, signature.delegate(), signature.callback())); + TEST_FFI_RC(BOTAN_FFI_ERROR_OUT_OF_RANGE, + botan_x509_cert_view_binary_values, + (ca, BOTAN_X509_PUBLIC_KEY_PKCS8_BITS, 1, public_key.delegate(), public_key.callback())); + + size_t count; + TEST_FFI_OK(botan_x509_cert_view_binary_values_count, (ee, BOTAN_X509_TBS_DATA_BITS, &count)); + result.test_sz_eq("TBS data count", count, 1); + TEST_FFI_OK(botan_x509_cert_view_binary_values_count, (ee, BOTAN_X509_SIGNATURE_SCHEME_BITS, &count)); + result.test_sz_eq("Signature scheme count", count, 1); + TEST_FFI_OK(botan_x509_cert_view_binary_values_count, (ee, BOTAN_X509_SIGNATURE_BITS, &count)); + result.test_sz_eq("Signature count", count, 1); + TEST_FFI_OK(botan_x509_cert_view_binary_values_count, (ca, BOTAN_X509_PUBLIC_KEY_PKCS8_BITS, &count)); + result.test_sz_eq("Public key count", count, 1); + + // At the moment there's no way to directly instantiate a signature + // verifier object with an encoded signature algorithm scheme. Hence, + // we just check that the hard-coded expectation is fulfilled. + // + // TODO: improve this if we ever have a pk_op_verify_t constructor that + // takes an encoded AlgorithmIdentifier. + const auto expected_sig_scheme = + Botan::AlgorithmIdentifier("RSA/PKCS1v15(SHA-1)", Botan::AlgorithmIdentifier::USE_NULL_PARAM).BER_encode(); + result.test_bin_eq("AlgorithmIdentifier", sig_scheme.get(), expected_sig_scheme); + + botan_pubkey_t pubkey; + TEST_FFI_INIT(botan_pubkey_load, (&pubkey, public_key.data(), public_key.size())); + + botan_pk_op_verify_t verifier; + TEST_FFI_INIT(botan_pk_op_verify_create, (&verifier, pubkey, "PKCS1v15(SHA-1)", 0)); + TEST_FFI_OK(botan_pk_op_verify_update, (verifier, tbs_data.data(), tbs_data.size())); + TEST_FFI_OK(botan_pk_op_verify_finish, (verifier, signature.data(), signature.size())); + + TEST_FFI_OK(botan_pk_op_verify_destroy, (verifier)); + TEST_FFI_OK(botan_pubkey_destroy, (pubkey)); + } + void ffi_test(Test::Result& result, botan_rng_t /*unused*/) override { botan_x509_cert_t root; int rc; @@ -650,19 +998,31 @@ if(!TEST_FFI_INIT(botan_x509_cert_load_file, (&root, Test::data_file("x509/nist/root.crt").c_str()))) { return; } + TEST_FFI_RC(1, botan_x509_cert_is_ca, (root)); botan_x509_cert_t end2; botan_x509_cert_t sub2; REQUIRE_FFI_OK(botan_x509_cert_load_file, (&end2, Test::data_file("x509/nist/test02/end.crt").c_str())); REQUIRE_FFI_OK(botan_x509_cert_load_file, (&sub2, Test::data_file("x509/nist/test02/int.crt").c_str())); + TEST_FFI_RC(0, botan_x509_cert_is_ca, (end2)); + + size_t path_limit; + TEST_FFI_RC(BOTAN_FFI_ERROR_NO_VALUE, botan_x509_cert_get_path_length_constraint, (root, &path_limit)); + + botan_x509_cert_t root_with_pathlen; + REQUIRE_FFI_OK(botan_x509_cert_load_file, + (&root_with_pathlen, Test::data_file("x509/extended/02/root.crt").c_str())); + TEST_FFI_OK(botan_x509_cert_get_path_length_constraint, (root_with_pathlen, &path_limit)); + result.test_sz_eq("Path length constraint", path_limit, 1); TEST_FFI_RC(1, botan_x509_cert_verify, (&rc, end2, &sub2, 1, &root, 1, nullptr, 0, nullptr, 0)); - result.confirm("Validation test02 failed", rc == 5002); - result.test_eq("Validation test02 status string", botan_x509_cert_validation_status(rc), "Signature error"); + result.test_is_true("Validation test02 failed", rc == 5002); + result.test_str_eq( + "Validation test02 status string", botan_x509_cert_validation_status(rc), "Signature error"); TEST_FFI_RC(1, botan_x509_cert_verify, (&rc, end2, nullptr, 0, &root, 1, nullptr, 0, nullptr, 0)); - result.confirm("Validation test02 failed (missing int)", rc == 3000); - result.test_eq( + result.test_is_true("Validation test02 failed (missing int)", rc == 3000); + result.test_str_eq( "Validation test02 status string", botan_x509_cert_validation_status(rc), "Certificate issuer not found"); botan_x509_cert_t end7; @@ -670,22 +1030,24 @@ REQUIRE_FFI_OK(botan_x509_cert_load_file, (&end7, Test::data_file("x509/nist/test07/end.crt").c_str())); REQUIRE_FFI_OK(botan_x509_cert_load_file, (&sub7, Test::data_file("x509/nist/test07/int.crt").c_str())); - botan_x509_cert_t subs[2] = {sub2, sub7}; + const botan_x509_cert_t subs[2] = {sub2, sub7}; TEST_FFI_RC(1, botan_x509_cert_verify, (&rc, end7, subs, 2, &root, 1, nullptr, 0, nullptr, 0)); - result.confirm("Validation test07 failed with expected error", rc == 1001); - result.test_eq("Validation test07 status string", - botan_x509_cert_validation_status(rc), - "Hash function used is considered too weak for security"); + result.test_is_true("Validation test07 failed with expected error", rc == 1001); + result.test_str_eq("Validation test07 status string", + botan_x509_cert_validation_status(rc), + "Hash function used is considered too weak for security"); TEST_FFI_RC(0, botan_x509_cert_verify, (&rc, end7, subs, 2, &root, 1, nullptr, 80, nullptr, 0)); - result.confirm("Validation test07 passed", rc == 0); - result.test_eq("Validation test07 status string", botan_x509_cert_validation_status(rc), "Verified"); + result.test_is_true("Validation test07 passed", rc == 0); + result.test_str_eq("Validation test07 status string", botan_x509_cert_validation_status(rc), "Verified"); + + verify_bare_pkcs1_rsa_signature(result, end7, sub7); TEST_FFI_RC(1, botan_x509_cert_verify_with_crl, (&rc, end7, subs, 2, nullptr, 0, nullptr, 0, "x509/farce", 0, nullptr, 0)); - result.confirm("Validation test07 failed with expected error", rc == 3000); - result.test_eq( + result.test_is_true("Validation test07 failed with expected error", rc == 3000); + result.test_str_eq( "Validation test07 status string", botan_x509_cert_validation_status(rc), "Certificate issuer not found"); botan_x509_crl_t rootcrl; @@ -693,8 +1055,9 @@ REQUIRE_FFI_OK(botan_x509_crl_load_file, (&rootcrl, Test::data_file("x509/nist/root.crl").c_str())); TEST_FFI_RC( 0, botan_x509_cert_verify_with_crl, (&rc, end7, subs, 2, &root, 1, &rootcrl, 1, nullptr, 80, nullptr, 0)); - result.confirm("Validation test07 with CRL passed", rc == 0); - result.test_eq("Validation test07 with CRL status string", botan_x509_cert_validation_status(rc), "Verified"); + result.test_is_true("Validation test07 with CRL passed", rc == 0); + result.test_str_eq( + "Validation test07 with CRL status string", botan_x509_cert_validation_status(rc), "Verified"); botan_x509_cert_t end20; botan_x509_cert_t sub20; @@ -702,13 +1065,14 @@ REQUIRE_FFI_OK(botan_x509_cert_load_file, (&end20, Test::data_file("x509/nist/test20/end.crt").c_str())); REQUIRE_FFI_OK(botan_x509_cert_load_file, (&sub20, Test::data_file("x509/nist/test20/int.crt").c_str())); REQUIRE_FFI_OK(botan_x509_crl_load_file, (&sub20crl, Test::data_file("x509/nist/test20/int.crl").c_str())); - botan_x509_crl_t crls[2] = {sub20crl, rootcrl}; + const botan_x509_crl_t crls[2] = {sub20crl, rootcrl}; TEST_FFI_RC( 1, botan_x509_cert_verify_with_crl, (&rc, end20, &sub20, 1, &root, 1, crls, 2, nullptr, 80, nullptr, 0)); - result.confirm("Validation test20 failed with expected error", rc == 5000); - result.test_eq( + result.test_is_true("Validation test20 failed with expected error", rc == 5000); + result.test_str_eq( "Validation test20 status string", botan_x509_cert_validation_status(rc), "Certificate is revoked"); + TEST_FFI_OK(botan_x509_cert_destroy, (root_with_pathlen)); TEST_FFI_OK(botan_x509_cert_destroy, (end2)); TEST_FFI_OK(botan_x509_cert_destroy, (sub2)); TEST_FFI_OK(botan_x509_cert_destroy, (end7)); @@ -737,24 +1101,24 @@ BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_time_starts, (cert, nullptr, &date_len)); std::string date(date_len - 1, '0'); - TEST_FFI_OK(botan_x509_cert_get_time_starts, (cert, &date[0], &date_len)); - result.test_eq("cert valid from", date, "200904000000Z"); + TEST_FFI_OK(botan_x509_cert_get_time_starts, (cert, date.data(), &date_len)); + result.test_str_eq("cert valid from", date, "200904000000Z"); date_len = 0; TEST_FFI_RC( BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_time_expires, (cert, nullptr, &date_len)); date.resize(date_len - 1); - TEST_FFI_OK(botan_x509_cert_get_time_expires, (cert, &date[0], &date_len)); - result.test_eq("cert valid until", date, "400917160000Z"); + TEST_FFI_OK(botan_x509_cert_get_time_expires, (cert, date.data(), &date_len)); + result.test_str_eq("cert valid until", date, "400917160000Z"); uint64_t not_before = 0; TEST_FFI_OK(botan_x509_cert_not_before, (cert, ¬_before)); - result.confirm("cert not before", not_before == 1599177600); + result.test_is_true("cert not before", not_before == 1599177600); uint64_t not_after = 0; TEST_FFI_OK(botan_x509_cert_not_after, (cert, ¬_after)); - result.confirm("cert not after", not_after == 2231510400); + result.test_is_true("cert not after", not_after == 2231510400); size_t serial_len = 0; TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, @@ -763,8 +1127,13 @@ std::vector serial(serial_len); TEST_FFI_OK(botan_x509_cert_get_serial_number, (cert, serial.data(), &serial_len)); - result.test_eq("cert serial length", serial.size(), 16); - result.test_eq("cert serial", Botan::hex_encode(serial), "41D29DD172EAEEA780C12C6CE92F8752"); + result.test_sz_eq("cert serial length", serial.size(), 16); + result.test_bin_eq("cert serial", serial, "41D29DD172EAEEA780C12C6CE92F8752"); + + ViewBytesSink serial_sink; + TEST_FFI_OK(botan_x509_cert_view_binary_values, + (cert, BOTAN_X509_SERIAL_NUMBER, 0, serial_sink.delegate(), serial_sink.callback())); + result.test_bin_eq("cert serial (2)", serial_sink.get(), "41D29DD172EAEEA780C12C6CE92F8752"); size_t fingerprint_len = 0; TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, @@ -773,7 +1142,7 @@ std::vector fingerprint(fingerprint_len); TEST_FFI_OK(botan_x509_cert_get_fingerprint, (cert, "SHA-256", fingerprint.data(), &fingerprint_len)); - result.test_eq( + result.test_str_eq( "cert fingerprint", reinterpret_cast(fingerprint.data()), "69:72:9B:8E:15:A8:6E:FC:17:7A:57:AF:B7:17:1D:FC:64:AD:D2:8C:2F:CA:8C:F1:50:7E:34:45:3C:CB:14:70"); @@ -783,7 +1152,13 @@ botan_x509_cert_get_authority_key_id, (cert, nullptr, &key_id_len)); - result.test_eq("No AKID", key_id_len, 0); + result.test_sz_eq("No AKID", key_id_len, 0); + + // "No AKID" is explicitly communicated with an error code + ViewBytesSink key_id_sink; + TEST_FFI_RC(BOTAN_FFI_ERROR_NO_VALUE, + botan_x509_cert_view_binary_values, + (cert, BOTAN_X509_AUTHORITY_KEY_IDENTIFIER, 0, key_id_sink.delegate(), key_id_sink.callback())); key_id_len = 0; TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, @@ -792,9 +1167,13 @@ std::vector key_id(key_id_len); TEST_FFI_OK(botan_x509_cert_get_subject_key_id, (cert, key_id.data(), &key_id_len)); - result.test_eq("cert subject key id", - Botan::hex_encode(key_id.data(), key_id.size(), true), - "7C4296AEDE4B483BFA92F89E8CCF6D8BA9723795"); + result.test_str_eq("cert subject key id", + Botan::hex_encode(key_id.data(), key_id.size(), true), + "7C4296AEDE4B483BFA92F89E8CCF6D8BA9723795"); + + TEST_FFI_OK(botan_x509_cert_view_binary_values, + (cert, BOTAN_X509_SUBJECT_KEY_IDENTIFIER, 0, key_id_sink.delegate(), key_id_sink.callback())); + result.test_bin_eq("cert subject key id", key_id_sink.get(), "7C4296AEDE4B483BFA92F89E8CCF6D8BA9723795"); size_t pubkey_len = 0; TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, @@ -812,6 +1191,12 @@ } #endif + size_t rdn_count; + TEST_FFI_OK(botan_x509_cert_get_issuer_dn_count, (cert, "Name", &rdn_count)); + result.test_sz_eq("issuer DN 'name' count", rdn_count, 1); + TEST_FFI_OK(botan_x509_cert_get_issuer_dn_count, (cert, "Organizational Unit", &rdn_count)); + result.test_sz_eq("issuer DN 'organizational unit' count", rdn_count, 0); + size_t dn_len = 0; TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_get_issuer_dn, @@ -819,7 +1204,12 @@ std::vector dn(dn_len); TEST_FFI_OK(botan_x509_cert_get_issuer_dn, (cert, "Name", 0, dn.data(), &dn_len)); - result.test_eq("issuer dn", reinterpret_cast(dn.data()), "ISRG Root X2"); + result.test_str_eq("issuer dn", reinterpret_cast(dn.data()), "ISRG Root X2"); + + TEST_FFI_OK(botan_x509_cert_get_subject_dn_count, (cert, "Name", &rdn_count)); + result.test_sz_eq("subject DN 'name' count", rdn_count, 1); + TEST_FFI_OK(botan_x509_cert_get_subject_dn_count, (cert, "Organizational Unit", &rdn_count)); + result.test_sz_eq("subject DN 'organizational unit' count", rdn_count, 0); dn_len = 0; TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, @@ -828,14 +1218,29 @@ dn.resize(dn_len); TEST_FFI_OK(botan_x509_cert_get_subject_dn, (cert, "Name", 0, dn.data(), &dn_len)); - result.test_eq("subject dn", reinterpret_cast(dn.data()), "ISRG Root X2"); + result.test_str_eq("subject dn", reinterpret_cast(dn.data()), "ISRG Root X2"); size_t printable_len = 0; TEST_FFI_RC( BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_x509_cert_to_string, (cert, nullptr, &printable_len)); std::string printable(printable_len - 1, '0'); - TEST_FFI_OK(botan_x509_cert_to_string, (cert, &printable[0], &printable_len)); + TEST_FFI_OK(botan_x509_cert_to_string, (cert, printable.data(), &printable_len)); + + size_t count; + TEST_FFI_OK(botan_x509_cert_view_string_values_count, (cert, BOTAN_X509_PEM_ENCODING, &count)); + result.test_sz_eq("one PEM encoding", count, 1); + TEST_FFI_OK(botan_x509_cert_view_binary_values_count, (cert, BOTAN_X509_DER_ENCODING, &count)); + result.test_sz_eq("one DER encoding", count, 1); + + ViewBytesSink der; + ViewStringSink pem; + TEST_FFI_OK(botan_x509_cert_view_binary_values, + (cert, BOTAN_X509_DER_ENCODING, 0, der.delegate(), der.callback())); + result.test_is_true("DER encoding produced something", !der.get().empty()); + TEST_FFI_OK(botan_x509_cert_view_string_values, + (cert, BOTAN_X509_PEM_ENCODING, 0, pem.delegate(), pem.callback())); + result.test_is_true("PEM encoding produced something", !pem.get().empty()); TEST_FFI_RC(0, botan_x509_cert_allowed_usage, (cert, KEY_CERT_SIGN)); TEST_FFI_RC(0, botan_x509_cert_allowed_usage, (cert, CRL_SIGN)); @@ -846,6 +1251,470 @@ } }; +class FFI_Cert_ExtKeyUsages_Test final : public FFI_Test { + public: + std::string name() const override { return "FFI X509 Extended Key Usage"; } + + void ffi_test(Test::Result& result, botan_rng_t /*unused*/) override { + botan_x509_cert_t cert_with_eku; + if(!TEST_FFI_INIT(botan_x509_cert_load_file, + (&cert_with_eku, Test::data_file("x509/pss_certs/03/end.crt").c_str()))) { + return; + } + + // Prepare some OID objects for OID-based EKU queries + botan_asn1_oid_t oid_srv_auth1; + botan_asn1_oid_t oid_srv_auth2; + botan_asn1_oid_t oid_ocsp_signing; + TEST_FFI_OK(botan_oid_from_string, (&oid_srv_auth1, "1.3.6.1.5.5.7.3.1")); + TEST_FFI_OK(botan_oid_from_string, (&oid_srv_auth2, "PKIX.ServerAuth")); + TEST_FFI_OK(botan_oid_from_string, (&oid_ocsp_signing, "PKIX.OCSPSigning")); + + // Make sure the OID object is checked for nullptr + TEST_FFI_RC( + BOTAN_FFI_ERROR_NULL_POINTER, botan_x509_cert_allowed_extended_usage_oid, (cert_with_eku, nullptr)); + + // Should have serverAuth (TLS Web Server Authentication) + TEST_FFI_RC(1, botan_x509_cert_allowed_extended_usage_str, (cert_with_eku, "1.3.6.1.5.5.7.3.1")); + TEST_FFI_RC(1, botan_x509_cert_allowed_extended_usage_str, (cert_with_eku, "PKIX.ServerAuth")); + TEST_FFI_RC(1, botan_x509_cert_allowed_extended_usage_oid, (cert_with_eku, oid_srv_auth1)); + TEST_FFI_RC(1, botan_x509_cert_allowed_extended_usage_oid, (cert_with_eku, oid_srv_auth2)); + + // Should have clientAuth (TLS Web Client Authentication) + TEST_FFI_RC(1, botan_x509_cert_allowed_extended_usage_str, (cert_with_eku, "1.3.6.1.5.5.7.3.2")); + TEST_FFI_RC(1, botan_x509_cert_allowed_extended_usage_str, (cert_with_eku, "PKIX.ClientAuth")); + + // Should NOT have OCSPSigning + TEST_FFI_RC(0, botan_x509_cert_allowed_extended_usage_str, (cert_with_eku, "1.3.6.1.5.5.7.3.9")); + TEST_FFI_RC(0, botan_x509_cert_allowed_extended_usage_str, (cert_with_eku, "PKIX.OCSPSigning")); + + // Should NOT have codeSigning + TEST_FFI_RC(0, botan_x509_cert_allowed_extended_usage_str, (cert_with_eku, "1.3.6.1.5.5.7.3.3")); + TEST_FFI_RC(0, botan_x509_cert_allowed_extended_usage_str, (cert_with_eku, "PKIX.CodeSigning")); + TEST_FFI_RC(0, botan_x509_cert_allowed_extended_usage_oid, (cert_with_eku, oid_ocsp_signing)); + + TEST_FFI_OK(botan_x509_cert_destroy, (cert_with_eku)); + + botan_x509_cert_t cert_without_eku; + if(!TEST_FFI_INIT(botan_x509_cert_load_file, + (&cert_without_eku, Test::data_file("x509/nist/root.crt").c_str()))) { + return; + } + + // Should return zero for any EKU query (no EKU extension present) + TEST_FFI_RC(0, botan_x509_cert_allowed_extended_usage_str, (cert_without_eku, "1.3.6.1.5.5.7.3.1")); + TEST_FFI_RC(0, botan_x509_cert_allowed_extended_usage_str, (cert_without_eku, "1.3.6.1.5.5.7.3.2")); + TEST_FFI_RC(0, botan_x509_cert_allowed_extended_usage_str, (cert_without_eku, "PKIX.OCSPSigning")); + TEST_FFI_RC(0, botan_x509_cert_allowed_extended_usage_str, (cert_without_eku, "PKIX.CodeSigning")); + + TEST_FFI_OK(botan_oid_destroy, (oid_srv_auth1)); + TEST_FFI_OK(botan_oid_destroy, (oid_srv_auth2)); + TEST_FFI_OK(botan_oid_destroy, (oid_ocsp_signing)); + TEST_FFI_OK(botan_x509_cert_destroy, (cert_without_eku)); + } +}; + + #if defined(BOTAN_HAS_X509) + +auto read_distinguished_name(std::span bytes) { + auto dec = Botan::BER_Decoder(bytes, Botan::BER_Decoder::Limits::DER()); + Botan::X509_DN dn; + dn.decode_from(dec); + return dn; +} + +class FFI_Cert_AlternativeNames_Test final : public FFI_Test { + private: + template EnumeratorT, + std::invocable CountFnT, + std::invocable VisitorT> + static void visit_general_names(Test::Result& result, + botan_x509_cert_t cert, + EnumeratorT enumerator_fn, + CountFnT count_fn, + VisitorT visitor_fn) { + int rc = BOTAN_FFI_SUCCESS; + for(size_t i = 0; rc == BOTAN_FFI_SUCCESS; ++i) { + botan_x509_general_name_t gn; + rc = enumerator_fn(cert, i, &gn); + if(rc == BOTAN_FFI_SUCCESS) { + visitor_fn(gn); + TEST_FFI_OK(botan_x509_general_name_destroy, (gn)); + } else if(rc == BOTAN_FFI_ERROR_OUT_OF_RANGE) { + // Now check we are at the expected index + size_t count; + TEST_FFI_OK(count_fn, (cert, &count)); + result.test_sz_eq("enumerator reached end at expected index", i, count); + } else { + result.test_note( + Botan::fmt("enumerator produced unexpected return code: {}", botan_error_description(rc))); + } + } + } + + template + static auto read_string_alternative_names(Test::Result& result, + botan_x509_cert_t cert, + EnumeratorT enumerator_fn, + CountFnT count_fn, + botan_x509_general_name_types type) { + std::vector out; + + visit_general_names(result, cert, enumerator_fn, count_fn, [&](botan_x509_general_name_t gn) { + unsigned int gn_type; + TEST_FFI_OK(botan_x509_general_name_get_type, (gn, &gn_type)); + if(static_cast(gn_type) == type) { + ViewStringSink str; + TEST_FFI_OK(botan_x509_general_name_view_string_value, (gn, str.delegate(), str.callback())); + out.push_back(str.get()); + } + }); + + return out; + } + + template + static auto read_binary_alternative_names(Test::Result& result, + botan_x509_cert_t cert, + EnumeratorT enumerator_fn, + CountFnT count_fn, + botan_x509_general_name_types type) { + std::vector> out; + + visit_general_names(result, cert, enumerator_fn, count_fn, [&](botan_x509_general_name_t gn) { + unsigned int gn_type; + TEST_FFI_OK(botan_x509_general_name_get_type, (gn, &gn_type)); + if(static_cast(gn_type) == type) { + ViewBytesSink data; + TEST_FFI_OK(botan_x509_general_name_view_binary_value, (gn, data.delegate(), data.callback())); + out.emplace_back(data.get().begin(), data.get().end()); + } + }); + + return out; + } + + static auto read_common_names(std::span> bytes) { + std::vector result; + for(const auto& dn_bytes : bytes) { + const auto dn = read_distinguished_name(dn_bytes); + result.push_back(dn.get_first_attribute("X520.CommonName")); + } + return result; + } + + public: + std::string name() const override { return "FFI X509 Alternative Names"; } + + void ffi_test(Test::Result& result, botan_rng_t /*unused*/) override { + botan_x509_cert_t cert_none; + if(!TEST_FFI_INIT(botan_x509_cert_load_file, + (&cert_none, Test::data_file("x509/misc/no_alternative_names.pem").c_str()))) { + return; + } + + botan_x509_general_name_t nil = nullptr; + TEST_FFI_RC(BOTAN_FFI_ERROR_NO_VALUE, botan_x509_cert_subject_alternative_names, (cert_none, 0, &nil)); + TEST_FFI_RC(BOTAN_FFI_ERROR_NO_VALUE, botan_x509_cert_issuer_alternative_names, (cert_none, 0, &nil)); + result.test_is_true("no general name created", nil == nullptr); + + botan_x509_cert_t cert; + if(!TEST_FFI_INIT(botan_x509_cert_load_file, + (&cert, Test::data_file("x509/misc/multiple_alternative_names.pem").c_str()))) { + return; + } + + const auto get_san = botan_x509_cert_subject_alternative_names; + const auto count_san = botan_x509_cert_subject_alternative_names_count; + + const auto san_email = + read_string_alternative_names(result, cert, get_san, count_san, BOTAN_X509_EMAIL_ADDRESS); + result.test_sz_eq("expected number of emails in SAN", san_email.size(), 2); + result.test_is_true("testing@x509-labs.com", Botan::value_exists(san_email, "testing@x509-labs.com")); + result.test_is_true("info@x509-labs.com", Botan::value_exists(san_email, "info@x509-labs.com")); + + const auto san_dns = read_string_alternative_names(result, cert, get_san, count_san, BOTAN_X509_DNS_NAME); + result.test_sz_eq("expected number of hostnames in SAN", san_dns.size(), 3); + result.test_is_true("test.x509-labs.com", Botan::value_exists(san_dns, "test.x509-labs.com")); + result.test_is_true("versuch.x509-labs.com", Botan::value_exists(san_dns, "versuch.x509-labs.com")); + result.test_is_true("trail.x509-labs.com", Botan::value_exists(san_dns, "trail.x509-labs.com")); + + const auto san_uri = read_string_alternative_names(result, cert, get_san, count_san, BOTAN_X509_URI); + result.test_sz_eq("expected number of URIs in SAN", san_uri.size(), 2); + result.test_is_true("https://x509-labs.com", Botan::value_exists(san_uri, "https://x509-labs.com")); + result.test_is_true("http://x509-labs.com", Botan::value_exists(san_uri, "http://x509-labs.com")); + + const auto san_ip4 = read_string_alternative_names(result, cert, get_san, count_san, BOTAN_X509_IP_ADDRESS); + result.test_sz_eq("expected number of IPv4 addresses", san_ip4.size(), 1); + result.test_is_true("127.0.0.1", Botan::value_exists(san_ip4, "127.0.0.1")); + const auto san_ip4_bin = + read_binary_alternative_names(result, cert, get_san, count_san, BOTAN_X509_IP_ADDRESS); + result.test_sz_eq("expected number of IPv4 addresses (bin)", san_ip4_bin.size(), 1); + result.test_bin_eq("127.0.0.1 (bin)", san_ip4_bin.front(), Botan::store_be(uint32_t(0x7F000001))); + + const auto san_dn_bytes = + read_binary_alternative_names(result, cert, get_san, count_san, BOTAN_X509_DIRECTORY_NAME); + result.test_sz_eq("expected number of DNs in SAN", san_dn_bytes.size(), 3); + const auto san_dn_cns = read_common_names(san_dn_bytes); + result.test_is_true("First Name", Botan::value_exists(san_dn_cns, "First Name")); + result.test_is_true("Middle Name", Botan::value_exists(san_dn_cns, "Middle Name")); + result.test_is_true("Last Name", Botan::value_exists(san_dn_cns, "Last Name")); + + auto get_ian = botan_x509_cert_issuer_alternative_names; + auto count_ian = botan_x509_cert_issuer_alternative_names_count; + + const auto ian_email = + read_string_alternative_names(result, cert, get_ian, count_ian, BOTAN_X509_EMAIL_ADDRESS); + result.test_sz_eq("expected number of emails in IAN", ian_email.size(), 0); + + const auto ian_dns = read_string_alternative_names(result, cert, get_ian, count_ian, BOTAN_X509_DNS_NAME); + result.test_sz_eq("expected number of hostnames in IAN", ian_dns.size(), 3); + result.test_is_true("test.x509-labs-ca.com", Botan::value_exists(ian_dns, "test.x509-labs-ca.com")); + result.test_is_true("versuch.x509-labs-ca.com", Botan::value_exists(ian_dns, "versuch.x509-labs-ca.com")); + result.test_is_true("trail.x509-labs-ca.com", Botan::value_exists(ian_dns, "trail.x509-labs-ca.com")); + + const auto ian_uri = read_string_alternative_names(result, cert, get_ian, count_ian, BOTAN_X509_URI); + result.test_sz_eq("expected number of URIs in IAN", ian_uri.size(), 2); + result.test_is_true("https://x509-labs-ca.com", Botan::value_exists(ian_uri, "https://x509-labs-ca.com")); + result.test_is_true("http://x509-labs-ca.com", Botan::value_exists(ian_uri, "http://x509-labs-ca.com")); + + const auto ian_ip4 = read_string_alternative_names(result, cert, get_ian, count_ian, BOTAN_X509_IP_ADDRESS); + result.test_sz_eq("expected number of IPv4 addresses", ian_ip4.size(), 1); + result.test_is_true("192.168.1.1", Botan::value_exists(ian_ip4, "192.168.1.1")); + const auto ian_ip4_bin = + read_binary_alternative_names(result, cert, get_ian, count_ian, BOTAN_X509_IP_ADDRESS); + result.test_sz_eq("expected number of IPv4 addresses (bin)", ian_ip4_bin.size(), 1); + result.test_bin_eq("192.168.1.1 (bin)", ian_ip4_bin.front(), Botan::store_be(uint32_t(0xC0A80101))); + + const auto ian_dn_bytes = + read_binary_alternative_names(result, cert, get_ian, count_ian, BOTAN_X509_DIRECTORY_NAME); + result.test_sz_eq("expected number of DNs in IAN", ian_dn_bytes.size(), 3); + const auto ian_dn_cns = read_common_names(ian_dn_bytes); + result.test_is_true("First CA", Botan::value_exists(ian_dn_cns, "First CA")); + result.test_is_true("Middle CA", Botan::value_exists(ian_dn_cns, "Middle CA")); + result.test_is_true("Last CA", Botan::value_exists(ian_dn_cns, "Last CA")); + + TEST_FFI_OK(botan_x509_cert_destroy, (cert)); + TEST_FFI_OK(botan_x509_cert_destroy, (cert_none)); + } +}; + +class FFI_Cert_NameConstraints_Test final : public FFI_Test { + private: + static auto read_constraints(Test::Result& result, botan_x509_cert_t cert, bool permitted) { + std::vector> out; + + int rc = BOTAN_FFI_SUCCESS; + for(size_t i = 0; rc == BOTAN_FFI_SUCCESS; ++i) { + botan_x509_general_name_t constraint; + if(permitted) { + rc = botan_x509_cert_permitted_name_constraints(cert, i, &constraint); + } else { + rc = botan_x509_cert_excluded_name_constraints(cert, i, &constraint); + } + + if(rc == BOTAN_FFI_SUCCESS) { + ViewBytesSink bytes; + ViewStringSink string; + + unsigned int type; + const auto rc2 = botan_x509_general_name_get_type(constraint, &type); + if(rc2 == BOTAN_FFI_SUCCESS) { + const auto gn_type = static_cast(type); + switch(gn_type) { + case BOTAN_X509_EMAIL_ADDRESS: + case BOTAN_X509_DNS_NAME: + case BOTAN_X509_URI: + case BOTAN_X509_IP_ADDRESS: + TEST_FFI_OK(botan_x509_general_name_view_string_value, + (constraint, string.delegate(), string.callback())); + out.emplace_back(gn_type, string.get()); + break; + case BOTAN_X509_DIRECTORY_NAME: + TEST_FFI_OK(botan_x509_general_name_view_binary_value, + (constraint, bytes.delegate(), bytes.callback())); + out.emplace_back(gn_type, read_distinguished_name(bytes.get()).to_string()); + break; + case BOTAN_X509_OTHER_NAME: + out.emplace_back(gn_type, ""); + break; + } + } else { + result.test_note( + Botan::fmt("botan_x509_general_name_get_type returned {}", botan_error_description(rc2))); + } + + TEST_FFI_OK(botan_x509_general_name_destroy, (constraint)); + } else if(rc == BOTAN_FFI_ERROR_OUT_OF_RANGE) { + // Now check that we are at the expected index + size_t count; + if(permitted) { + TEST_FFI_OK(botan_x509_cert_permitted_name_constraints_count, (cert, &count)); + } else { + TEST_FFI_OK(botan_x509_cert_excluded_name_constraints_count, (cert, &count)); + } + result.test_sz_eq("expected length of name constraint list", i, count); + } else { + result.test_failure(Botan::fmt("unexpected error code: {}", botan_error_description(rc))); + } + } + + return out; + } + + public: + std::string name() const override { return "FFI X509 Name Constraints"; } + + void ffi_test(Test::Result& result, botan_rng_t /*unused*/) override { + botan_x509_cert_t cert; + if(!TEST_FFI_INIT(botan_x509_cert_load_file, + (&cert, Test::data_file("x509/misc/name_constraint_ci/int.pem").c_str()))) { + return; + } + + const auto permitted = read_constraints(result, cert, true); + const auto excluded = read_constraints(result, cert, false); + + result.test_sz_eq("permissions", permitted.size(), 72); + result.test_sz_eq("exclusions", excluded.size(), 2); + + using V = decltype(permitted)::value_type; + result.test_is_true("email", Botan::value_exists(permitted, V{BOTAN_X509_EMAIL_ADDRESS, "pec.aruba.it"})); + result.test_is_true("DNS", Botan::value_exists(permitted, V{BOTAN_X509_DNS_NAME, "gov.it"})); + result.test_is_true( + "DN", + Botan::value_exists( + permitted, + V{BOTAN_X509_DIRECTORY_NAME, R"(C="IT",X520.State="Roma",X520.Locality="Roma",O="Sogei S.p.A.")"})); + result.test_is_true("IPv4", Botan::value_exists(excluded, V{BOTAN_X509_IP_ADDRESS, "0.0.0.0/0"})); + result.test_is_true("IPv6", Botan::value_exists(excluded, V{BOTAN_X509_IP_ADDRESS, "0:0:0:0:0:0:0:0/0"})); + + // below are more generic general_name_t tests + + botan_x509_general_name_t email; + botan_x509_general_name_t dns; + botan_x509_general_name_t dn; + botan_x509_general_name_t ip; + TEST_FFI_OK(botan_x509_cert_permitted_name_constraints, (cert, 0, &email)); + TEST_FFI_OK(botan_x509_cert_permitted_name_constraints, (cert, 33, &dns)); + TEST_FFI_OK(botan_x509_cert_permitted_name_constraints, (cert, 47, &dn)); + TEST_FFI_OK(botan_x509_cert_excluded_name_constraints, (cert, 0, &ip)); + + unsigned int type; + TEST_FFI_OK(botan_x509_general_name_get_type, (email, &type)); + result.test_enum_eq("email", static_cast(type), BOTAN_X509_EMAIL_ADDRESS); + TEST_FFI_OK(botan_x509_general_name_get_type, (dns, &type)); + result.test_enum_eq("dns", static_cast(type), BOTAN_X509_DNS_NAME); + TEST_FFI_OK(botan_x509_general_name_get_type, (dn, &type)); + result.test_enum_eq("dn", static_cast(type), BOTAN_X509_DIRECTORY_NAME); + TEST_FFI_OK(botan_x509_general_name_get_type, (ip, &type)); + result.test_enum_eq("ip", static_cast(type), BOTAN_X509_IP_ADDRESS); + + ViewBytesSink bin; + ViewStringSink str; + + TEST_FFI_OK(botan_x509_general_name_view_string_value, (email, str.delegate(), str.callback())); + result.test_str_eq("email as expected", str.get(), "agid.gov.it"); + TEST_FFI_RC(BOTAN_FFI_ERROR_INVALID_OBJECT_STATE, + botan_x509_general_name_view_binary_value, + (email, bin.delegate(), bin.callback())); + + TEST_FFI_OK(botan_x509_general_name_view_string_value, (dns, str.delegate(), str.callback())); + result.test_str_eq("dns as expected", str.get(), "agendadigitale.it"); + TEST_FFI_RC(BOTAN_FFI_ERROR_INVALID_OBJECT_STATE, + botan_x509_general_name_view_binary_value, + (dns, bin.delegate(), bin.callback())); + + TEST_FFI_OK(botan_x509_general_name_view_binary_value, (dn, bin.delegate(), bin.callback())); + const auto organization = read_distinguished_name(bin.get()).get_first_attribute("O"); + result.test_str_eq("dn as expected", organization, "ACI Informatica S.p.A."); + TEST_FFI_RC(BOTAN_FFI_ERROR_INVALID_OBJECT_STATE, + botan_x509_general_name_view_string_value, + (dn, str.delegate(), str.callback())); + + TEST_FFI_OK(botan_x509_general_name_view_binary_value, (ip, bin.delegate(), bin.callback())); + result.test_sz_eq("ip has correct length", bin.get().size(), 8); + TEST_FFI_OK(botan_x509_general_name_view_string_value, (ip, str.delegate(), str.callback())); + result.test_str_eq("ip has correct length", str.get(), "0.0.0.0/0"); + + TEST_FFI_OK(botan_x509_general_name_destroy, (email)); + TEST_FFI_OK(botan_x509_general_name_destroy, (dns)); + TEST_FFI_OK(botan_x509_general_name_destroy, (dn)); + TEST_FFI_OK(botan_x509_general_name_destroy, (ip)); + + TEST_FFI_OK(botan_x509_cert_destroy, (cert)); + } +}; + +class FFI_Cert_AuthorityInformationAccess_Test final : public FFI_Test { + private: + static auto read_aia_string_list(Test::Result& result, botan_x509_cert_t cert, botan_x509_value_type value_type) { + std::vector out; + + size_t count = 0; + TEST_FFI_OK(botan_x509_cert_view_string_values_count, (cert, value_type, &count)); + for(size_t i = 0; i < count; ++i) { + TEST_FFI_OK(botan_x509_cert_view_string_values, + (cert, value_type, i, &out, [](botan_view_ctx ctx, const char* str, size_t) -> int { + static_cast*>(ctx)->emplace_back(str); + return BOTAN_FFI_SUCCESS; + })); + } + + return out; + } + + public: + std::string name() const override { return "FFI X509 Authority Information Access"; } + + void ffi_test(Test::Result& result, botan_rng_t /*unused*/) override { + botan_x509_cert_t cert_with_aia; + botan_x509_cert_t cert_without_crl_dps; + if(!TEST_FFI_INIT( + botan_x509_cert_load_file, + (&cert_with_aia, + Test::data_file("x509/misc/contains_authority_info_access_with_two_ca_issuers.pem").c_str()))) { + return; + } + + if(!TEST_FFI_INIT(botan_x509_cert_load_file, + (&cert_without_crl_dps, Test::data_file("x509/misc/no_alternative_names.pem").c_str()))) { + return; + } + + TEST_FFI_RC( + BOTAN_FFI_ERROR_OUT_OF_RANGE, + botan_x509_cert_view_string_values, + (cert_without_crl_dps, BOTAN_X509_CRL_DISTRIBUTION_URLS, 0, nullptr, [](auto, auto, auto) { return 0; })); + + const auto crl_dps = read_aia_string_list(result, cert_with_aia, BOTAN_X509_CRL_DISTRIBUTION_URLS); + result.test_sz_eq("has two CRL URI distribution points", crl_dps.size(), 2); + result.test_is_true("has expected CRL URI distribution point", + Botan::value_exists(crl_dps, "http://crl.d-trust.net/crl/bdrive_test_ca_1-2_2017.crl")); + + const auto dummy_callback = [](auto, auto, auto) -> int { return BOTAN_FFI_SUCCESS; }; + + TEST_FFI_RC(BOTAN_FFI_ERROR_OUT_OF_RANGE, + botan_x509_cert_view_string_values, + (cert_without_crl_dps, BOTAN_X509_OCSP_RESPONDER_URLS, 0, nullptr, dummy_callback)); + TEST_FFI_RC(BOTAN_FFI_ERROR_OUT_OF_RANGE, + botan_x509_cert_view_string_values, + (cert_without_crl_dps, BOTAN_X509_CA_ISSUERS_URLS, 0, nullptr, dummy_callback)); + + const auto ocsps = read_aia_string_list(result, cert_with_aia, BOTAN_X509_OCSP_RESPONDER_URLS); + result.test_is_true("OCSP responder found", Botan::value_exists(ocsps, "http://staging.ocsp.d-trust.net")); + + const auto cas = read_aia_string_list(result, cert_with_aia, BOTAN_X509_CA_ISSUERS_URLS); + result.test_is_true("CA issuer found", + Botan::value_exists(cas, "http://www.d-trust.net/cgi-bin/Bdrive_Test_CA_1-2_2017.crt")); + + TEST_FFI_OK(botan_x509_cert_destroy, (cert_with_aia)); + TEST_FFI_OK(botan_x509_cert_destroy, (cert_without_crl_dps)); + } +}; + + #endif + class FFI_PKCS_Hashid_Test final : public FFI_Test { public: std::string name() const override { return "FFI PKCS hash id"; } @@ -855,10 +1724,10 @@ size_t hash_id_len = hash_id.size(); if(TEST_FFI_INIT(botan_pkcs_hash_id, ("SHA-256", hash_id.data(), &hash_id_len))) { - result.test_eq("Expected SHA-256 PKCS hash id len", hash_id_len, 19); + result.test_sz_eq("Expected SHA-256 PKCS hash id len", hash_id_len, 19); hash_id.resize(hash_id_len); - result.test_eq("Expected SHA_256 PKCS hash id", hash_id, "3031300D060960864801650304020105000420"); + result.test_bin_eq("Expected SHA_256 PKCS hash id", hash_id, "3031300D060960864801650304020105000420"); hash_id_len = 3; // too short TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, @@ -873,14 +1742,15 @@ std::string name() const override { return "FFI CBC cipher"; } void ffi_test(Test::Result& result, botan_rng_t /*unused*/) override { - botan_cipher_t cipher_encrypt, cipher_decrypt; + botan_cipher_t cipher_encrypt; + botan_cipher_t cipher_decrypt; if(TEST_FFI_INIT(botan_cipher_init, (&cipher_encrypt, "AES-128/CBC/PKCS7", BOTAN_CIPHER_INIT_FLAG_ENCRYPT))) { size_t min_keylen = 0; size_t max_keylen = 0; TEST_FFI_OK(botan_cipher_query_keylen, (cipher_encrypt, &min_keylen, &max_keylen)); - result.test_int_eq(min_keylen, 16, "Min key length"); - result.test_int_eq(max_keylen, 16, "Max key length"); + result.test_sz_eq("Min key length", min_keylen, 16); + result.test_sz_eq("Max key length", max_keylen, 16); // from https://github.com/geertj/bluepass/blob/master/tests/vectors/aes-cbc-pkcs7.txt const std::vector plaintext = @@ -897,7 +1767,7 @@ for(size_t r = 0; r != 2; ++r) { size_t ctext_len; TEST_FFI_OK(botan_cipher_output_length, (cipher_encrypt, plaintext.size(), &ctext_len)); - result.test_eq("Expected size of padded message", ctext_len, plaintext.size() + 15); + result.test_sz_eq("Expected size of padded message", ctext_len, plaintext.size() + 15); std::vector ciphertext(ctext_len); size_t update_granularity = 0; @@ -908,9 +1778,9 @@ TEST_FFI_OK(botan_cipher_get_ideal_update_granularity, (cipher_encrypt, &ideal_granularity)); TEST_FFI_OK(botan_cipher_get_tag_length, (cipher_encrypt, &taglen)); - result.test_eq( + result.test_sz_eq( "ideal granularity is a multiple of update granularity", ideal_granularity % update_granularity, 0); - result.test_eq("not an AEAD, hence no tag", taglen, 0); + result.test_sz_eq("not an AEAD, hence no tag", taglen, 0); TEST_FFI_OK(botan_cipher_set_key, (cipher_encrypt, symkey.data(), symkey.size())); TEST_FFI_OK(botan_cipher_start, (cipher_encrypt, nonce.data(), nonce.size())); @@ -938,7 +1808,7 @@ &input_consumed)); ciphertext.resize(output_written); - result.test_eq("AES/CBC ciphertext", ciphertext, exp_ciphertext); + result.test_bin_eq("AES/CBC ciphertext", ciphertext, exp_ciphertext); if(TEST_FFI_OK(botan_cipher_init, (&cipher_decrypt, "AES-128/CBC", BOTAN_CIPHER_INIT_FLAG_DECRYPT))) { size_t ptext_len; @@ -951,10 +1821,10 @@ TEST_FFI_OK(botan_cipher_get_ideal_update_granularity, (cipher_decrypt, &ideal_granularity)); TEST_FFI_OK(botan_cipher_get_tag_length, (cipher_decrypt, &taglen)); - result.test_eq("ideal granularity is a multiple of update granularity (decrypt)", - ideal_granularity % update_granularity, - 0); - result.test_eq("not an AEAD, hence no tag (decrypt)", taglen, 0); + result.test_sz_eq("ideal granularity is a multiple of update granularity (decrypt)", + ideal_granularity % update_granularity, + 0); + result.test_sz_eq("not an AEAD, hence no tag (decrypt)", taglen, 0); 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())); @@ -970,7 +1840,7 @@ decrypted.resize(output_written); - result.test_eq("AES/CBC plaintext", decrypted, plaintext); + result.test_bin_eq("AES/CBC plaintext", decrypted, plaintext); TEST_FFI_OK(botan_cipher_destroy, (cipher_decrypt)); } @@ -986,18 +1856,19 @@ std::string name() const override { return "FFI GCM"; } void ffi_test(Test::Result& result, botan_rng_t /*unused*/) override { - botan_cipher_t cipher_encrypt, cipher_decrypt; + botan_cipher_t cipher_encrypt; + botan_cipher_t cipher_decrypt; if(TEST_FFI_INIT(botan_cipher_init, (&cipher_encrypt, "AES-128/GCM", BOTAN_CIPHER_INIT_FLAG_ENCRYPT))) { - char namebuf[18]; + std::array namebuf{}; size_t name_len = 15; - TEST_FFI_FAIL("output buffer too short", botan_cipher_name, (cipher_encrypt, namebuf, &name_len)); - result.test_eq("name len", name_len, 16); + TEST_FFI_FAIL("output buffer too short", botan_cipher_name, (cipher_encrypt, namebuf.data(), &name_len)); + result.test_sz_eq("name len", name_len, 16); - name_len = sizeof(namebuf); - if(TEST_FFI_OK(botan_cipher_name, (cipher_encrypt, namebuf, &name_len))) { - result.test_eq("name len", name_len, 16); - result.test_eq("name", std::string(namebuf), "AES-128/GCM(16)"); + name_len = namebuf.size(); + if(TEST_FFI_OK(botan_cipher_name, (cipher_encrypt, namebuf.data(), &name_len))) { + result.test_sz_eq("name len", name_len, 16); + result.test_str_eq("name", namebuf.data(), "AES-128/GCM(16)"); } size_t min_keylen = 0; @@ -1010,18 +1881,18 @@ TEST_FFI_OK(botan_cipher_get_update_granularity, (cipher_encrypt, &update_granularity)); TEST_FFI_OK(botan_cipher_get_ideal_update_granularity, (cipher_encrypt, &ideal_granularity)); - result.test_eq( + result.test_sz_eq( "ideal granularity is a multiple of update granularity", ideal_granularity % update_granularity, 0); TEST_FFI_OK(botan_cipher_query_keylen, (cipher_encrypt, &min_keylen, &max_keylen)); - result.test_int_eq(min_keylen, 16, "Min key length"); - result.test_int_eq(max_keylen, 16, "Max key length"); + result.test_sz_eq("Min key length", min_keylen, 16); + result.test_sz_eq("Max key length", max_keylen, 16); TEST_FFI_OK(botan_cipher_get_default_nonce_length, (cipher_encrypt, &nonce_len)); - result.test_int_eq(nonce_len, 12, "Expected default GCM nonce length"); + result.test_sz_eq("Expected default GCM nonce length", nonce_len, 12); TEST_FFI_OK(botan_cipher_get_tag_length, (cipher_encrypt, &tag_len)); - result.test_int_eq(tag_len, 16, "Expected GCM tag length"); + result.test_sz_eq("Expected GCM tag length", tag_len, 16); TEST_FFI_RC(1, botan_cipher_is_authenticated, (cipher_encrypt)); @@ -1080,7 +1951,7 @@ &input_consumed)); ciphertext.resize(output_written); - result.test_eq("AES/GCM ciphertext", ciphertext, exp_ciphertext); + result.test_bin_eq("AES/GCM ciphertext", ciphertext, exp_ciphertext); if(TEST_FFI_OK(botan_cipher_init, (&cipher_decrypt, "AES-128/GCM", BOTAN_CIPHER_INIT_FLAG_DECRYPT))) { std::vector decrypted(plaintext.size()); @@ -1088,9 +1959,9 @@ TEST_FFI_OK(botan_cipher_get_update_granularity, (cipher_decrypt, &update_granularity)); TEST_FFI_OK(botan_cipher_get_ideal_update_granularity, (cipher_decrypt, &ideal_granularity)); - result.test_eq("ideal granularity is a multiple of update granularity (decrypt)", - ideal_granularity % update_granularity, - 0); + result.test_sz_eq("ideal granularity is a multiple of update granularity (decrypt)", + ideal_granularity % update_granularity, + 0); TEST_FFI_OK(botan_cipher_set_key, (cipher_decrypt, symkey.data(), symkey.size())); TEST_FFI_OK(botan_cipher_set_associated_data, (cipher_decrypt, aad.data(), aad.size())); @@ -1105,9 +1976,9 @@ ciphertext.size(), &input_consumed)); - result.test_int_eq(input_consumed, ciphertext.size(), "All input consumed"); - result.test_int_eq(output_written, decrypted.size(), "Expected output size produced"); - result.test_eq("AES/GCM plaintext", decrypted, plaintext); + result.test_sz_eq("All input consumed", input_consumed, ciphertext.size()); + result.test_sz_eq("Expected output size produced", output_written, decrypted.size()); + result.test_bin_eq("AES/GCM plaintext", decrypted, plaintext); TEST_FFI_OK(botan_cipher_destroy, (cipher_decrypt)); } @@ -1123,18 +1994,19 @@ std::string name() const override { return "FFI ChaCha20Poly1305"; } void ffi_test(Test::Result& result, botan_rng_t /*unused*/) override { - botan_cipher_t cipher_encrypt, cipher_decrypt; + botan_cipher_t cipher_encrypt; + botan_cipher_t cipher_decrypt; if(TEST_FFI_INIT(botan_cipher_init, (&cipher_encrypt, "ChaCha20Poly1305", BOTAN_CIPHER_INIT_FLAG_ENCRYPT))) { - std::array namebuf; + std::array namebuf{}; size_t name_len = 15; TEST_FFI_FAIL("output buffer too short", botan_cipher_name, (cipher_encrypt, namebuf.data(), &name_len)); - result.test_eq("name len", name_len, 17); + result.test_sz_eq("name len", name_len, 17); name_len = namebuf.size(); if(TEST_FFI_OK(botan_cipher_name, (cipher_encrypt, namebuf.data(), &name_len))) { - result.test_eq("name len", name_len, 17); - result.test_eq("name", std::string(namebuf.data()), "ChaCha20Poly1305"); + result.test_sz_eq("name len", name_len, 17); + result.test_str_eq("name", std::string(namebuf.data()), "ChaCha20Poly1305"); } size_t min_keylen = 0; @@ -1147,18 +2019,18 @@ TEST_FFI_OK(botan_cipher_get_update_granularity, (cipher_encrypt, &update_granularity)); TEST_FFI_OK(botan_cipher_get_ideal_update_granularity, (cipher_encrypt, &ideal_granularity)); - result.test_eq( + result.test_sz_eq( "ideal granularity is a multiple of update granularity", ideal_granularity % update_granularity, 0); TEST_FFI_OK(botan_cipher_query_keylen, (cipher_encrypt, &min_keylen, &max_keylen)); - result.test_int_eq(min_keylen, 32, "Min key length"); - result.test_int_eq(max_keylen, 32, "Max key length"); + result.test_sz_eq("Min key length", min_keylen, 32); + result.test_sz_eq("Max key length", max_keylen, 32); TEST_FFI_OK(botan_cipher_get_default_nonce_length, (cipher_encrypt, &nonce_len)); - result.test_int_eq(nonce_len, 12, "Expected default ChaCha20Poly1305 nonce length"); + result.test_sz_eq("Expected default ChaCha20Poly1305 nonce length", nonce_len, 12); TEST_FFI_OK(botan_cipher_get_tag_length, (cipher_encrypt, &tag_len)); - result.test_int_eq(tag_len, 16, "Expected Chacha20Poly1305 tag length"); + result.test_sz_eq("Expected Chacha20Poly1305 tag length", tag_len, 16); TEST_FFI_RC(1, botan_cipher_is_authenticated, (cipher_encrypt)); @@ -1211,7 +2083,7 @@ &input_consumed)); ciphertext.resize(output_written); - result.test_eq("AES/GCM ciphertext", ciphertext, exp_ciphertext); + result.test_bin_eq("AES/GCM ciphertext", ciphertext, exp_ciphertext); if(TEST_FFI_OK(botan_cipher_init, (&cipher_decrypt, "ChaCha20Poly1305", BOTAN_CIPHER_INIT_FLAG_DECRYPT))) { @@ -1220,9 +2092,9 @@ TEST_FFI_OK(botan_cipher_get_update_granularity, (cipher_decrypt, &update_granularity)); TEST_FFI_OK(botan_cipher_get_ideal_update_granularity, (cipher_decrypt, &ideal_granularity)); - result.test_eq("ideal granularity is a multiple of update granularity (decrypt)", - ideal_granularity % update_granularity, - 0); + result.test_sz_eq("ideal granularity is a multiple of update granularity (decrypt)", + ideal_granularity % update_granularity, + 0); TEST_FFI_OK(botan_cipher_set_key, (cipher_decrypt, symkey.data(), symkey.size())); TEST_FFI_OK(botan_cipher_set_associated_data, (cipher_decrypt, aad.data(), aad.size())); @@ -1237,9 +2109,9 @@ ciphertext.size(), &input_consumed)); - result.test_int_eq(input_consumed, ciphertext.size(), "All input consumed"); - result.test_int_eq(output_written, decrypted.size(), "Expected output size produced"); - result.test_eq("AES/GCM plaintext", decrypted, plaintext); + result.test_sz_eq("All input consumed", input_consumed, ciphertext.size()); + result.test_sz_eq("Expected output size produced", output_written, decrypted.size()); + result.test_bin_eq("AES/GCM plaintext", decrypted, plaintext); TEST_FFI_OK(botan_cipher_destroy, (cipher_decrypt)); } @@ -1255,7 +2127,8 @@ std::string name() const override { return "FFI EAX"; } void ffi_test(Test::Result& result, botan_rng_t /*unused*/) override { - botan_cipher_t cipher_encrypt, cipher_decrypt; + botan_cipher_t cipher_encrypt; + botan_cipher_t cipher_decrypt; if(TEST_FFI_INIT(botan_cipher_init, (&cipher_encrypt, "AES-128/EAX", BOTAN_CIPHER_INIT_FLAG_ENCRYPT))) { size_t min_keylen = 0; @@ -1269,23 +2142,23 @@ TEST_FFI_OK(botan_cipher_get_update_granularity, (cipher_encrypt, &update_granularity)); TEST_FFI_OK(botan_cipher_get_ideal_update_granularity, (cipher_encrypt, &ideal_granularity)); - result.test_eq( + result.test_sz_eq( "ideal granularity is a multiple of update granularity", ideal_granularity % update_granularity, 0); TEST_FFI_OK(botan_cipher_query_keylen, (cipher_encrypt, &min_keylen, &max_keylen)); - result.test_int_eq(min_keylen, 16, "Min key length"); - result.test_int_eq(max_keylen, 16, "Max key length"); + result.test_sz_eq("Min key length", min_keylen, 16); + result.test_sz_eq("Max key length", max_keylen, 16); TEST_FFI_OK(botan_cipher_get_keyspec, (cipher_encrypt, &min_keylen, &max_keylen, &mod_keylen)); - result.test_int_eq(min_keylen, 16, "Min key length"); - result.test_int_eq(max_keylen, 16, "Max key length"); - result.test_int_eq(mod_keylen, 1, "Mod key length"); + result.test_sz_eq("Min key length", min_keylen, 16); + result.test_sz_eq("Max key length", max_keylen, 16); + result.test_sz_eq("Mod key length", mod_keylen, 1); TEST_FFI_OK(botan_cipher_get_default_nonce_length, (cipher_encrypt, &nonce_len)); - result.test_int_eq(nonce_len, 12, "Expected default EAX nonce length"); + result.test_sz_eq("Expected default EAX nonce length", nonce_len, 12); TEST_FFI_OK(botan_cipher_get_tag_length, (cipher_encrypt, &tag_len)); - result.test_int_eq(tag_len, 16, "Expected EAX tag length"); + result.test_sz_eq("Expected EAX tag length", tag_len, 16); TEST_FFI_RC(1, botan_cipher_is_authenticated, (cipher_encrypt)); @@ -1333,7 +2206,7 @@ &input_consumed)); ciphertext.resize(output_written); - result.test_eq("AES/EAX ciphertext", ciphertext, exp_ciphertext); + result.test_bin_eq("AES/EAX ciphertext", ciphertext, exp_ciphertext); if(TEST_FFI_OK(botan_cipher_init, (&cipher_decrypt, "AES-128/EAX", BOTAN_CIPHER_INIT_FLAG_DECRYPT))) { std::vector decrypted(plaintext.size()); @@ -1341,9 +2214,9 @@ TEST_FFI_OK(botan_cipher_get_update_granularity, (cipher_decrypt, &update_granularity)); TEST_FFI_OK(botan_cipher_get_ideal_update_granularity, (cipher_decrypt, &ideal_granularity)); - result.test_eq("ideal granularity is a multiple of update granularity (decrypt)", - ideal_granularity % update_granularity, - 0); + result.test_sz_eq("ideal granularity is a multiple of update granularity (decrypt)", + ideal_granularity % update_granularity, + 0); 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())); @@ -1357,9 +2230,9 @@ ciphertext.size(), &input_consumed)); - result.test_int_eq(input_consumed, ciphertext.size(), "All input consumed"); - result.test_int_eq(output_written, decrypted.size(), "Expected output size produced"); - result.test_eq("AES/EAX plaintext", decrypted, plaintext); + result.test_sz_eq("All input consumed", input_consumed, ciphertext.size()); + result.test_sz_eq("Expected output size produced", output_written, decrypted.size()); + result.test_bin_eq("AES/EAX plaintext", decrypted, plaintext); TEST_FFI_OK(botan_cipher_destroy, (cipher_decrypt)); } @@ -1375,9 +2248,10 @@ std::string name() const override { return "FFI AEAD"; } void ffi_test(Test::Result& merged_result, botan_rng_t rng) override { - botan_cipher_t cipher_encrypt, cipher_decrypt; + botan_cipher_t cipher_encrypt; + botan_cipher_t cipher_decrypt; - std::array aeads = { + const std::array aeads = { "AES-128/GCM", "ChaCha20Poly1305", "AES-128/EAX", "AES-256/SIV", "AES-128/CCM"}; for(const std::string& aead : aeads) { @@ -1387,7 +2261,7 @@ continue; } - if(!botan_cipher_is_authenticated(cipher_encrypt)) { + if(botan_cipher_is_authenticated(cipher_encrypt) == 0) { result.test_failure("Cipher " + aead + " claims is not authenticated"); botan_cipher_destroy(cipher_encrypt); continue; @@ -1406,7 +2280,7 @@ TEST_FFI_OK(botan_cipher_get_default_nonce_length, (cipher_encrypt, &noncelen)); TEST_FFI_OK(botan_cipher_get_tag_length, (cipher_encrypt, &taglen)); - result.test_eq( + result.test_sz_eq( "ideal granularity is a multiple of update granularity", ideal_granularity % update_granularity, 0); std::vector key(max_keylen); @@ -1425,8 +2299,8 @@ TEST_FFI_OK(botan_rng_get, (rng, dummy_buffer.data(), dummy_buffer.size())); std::vector dummy_buffer_reference = dummy_buffer; - const bool requires_entire_message = botan_cipher_requires_entire_message(cipher_encrypt); - result.test_eq( + const bool requires_entire_message = botan_cipher_requires_entire_message(cipher_encrypt) == 1; + result.test_bool_eq( "requires entire message", requires_entire_message, (aead == "AES-256/SIV" || aead == "AES-128/CCM")); std::span pt_slicer(plaintext); @@ -1459,14 +2333,14 @@ pt_chunk.size(), &input_consumed)); - result.test_gt("some input consumed", input_consumed, 0); - result.test_lte("at most, all input consumed", input_consumed, pt_chunk.size()); + result.test_sz_gt("some input consumed", input_consumed, 0); + result.test_sz_lte("at most, all input consumed", input_consumed, pt_chunk.size()); pt_slicer = pt_slicer.subspan(input_consumed); if(requires_entire_message) { - result.test_eq("no output produced", output_written, 0); + result.test_sz_eq("no output produced", output_written, 0); } else { - result.test_eq("all bytes produced", output_written, input_consumed); + result.test_sz_eq("all bytes produced", output_written, input_consumed); ct_stuffer = ct_stuffer.subspan(output_written); } } @@ -1489,8 +2363,9 @@ const size_t expected_final_size = requires_entire_message ? ciphertext.size() : taglen + pt_slicer.size(); - result.test_eq("remaining bytes consumed in bogus final", final_input_consumed, pt_slicer.size()); - result.test_eq("required buffer size is written in bogus final", final_output_written, expected_final_size); + result.test_sz_eq("remaining bytes consumed in bogus final", final_input_consumed, pt_slicer.size()); + result.test_sz_eq( + "required buffer size is written in bogus final", final_output_written, expected_final_size); auto final_ct_chunk = ct_stuffer.first(expected_final_size); @@ -1504,9 +2379,9 @@ 0, &final_input_consumed)); - result.test_eq("no bytes consumed in final", final_input_consumed, 0); - result.test_eq("final bytes written", final_output_written, expected_final_size); - result.test_eq("dummy buffer unchanged", dummy_buffer, dummy_buffer_reference); + result.test_sz_eq("no bytes consumed in final", final_input_consumed, 0); + result.test_sz_eq("final bytes written", final_output_written, expected_final_size); + result.test_bin_eq("dummy buffer unchanged", dummy_buffer, dummy_buffer_reference); TEST_FFI_OK(botan_cipher_destroy, (cipher_encrypt)); @@ -1517,9 +2392,9 @@ TEST_FFI_OK(botan_cipher_get_update_granularity, (cipher_decrypt, &update_granularity)); TEST_FFI_OK(botan_cipher_get_ideal_update_granularity, (cipher_decrypt, &ideal_granularity)); - result.test_eq("ideal granularity is a multiple of update granularity (decrypt)", - ideal_granularity % update_granularity, - 0); + result.test_sz_eq("ideal granularity is a multiple of update granularity (decrypt)", + ideal_granularity % update_granularity, + 0); TEST_FFI_OK(botan_cipher_set_key, (cipher_decrypt, key.data(), key.size())); TEST_FFI_OK(botan_cipher_start, (cipher_decrypt, nonce.data(), nonce.size())); @@ -1555,14 +2430,14 @@ ct_chunk.size(), &input_consumed)); - result.test_gt("some input consumed", input_consumed, 0); - result.test_lte("at most, all input consumed", input_consumed, ct_chunk.size()); + result.test_sz_gt("some input consumed", input_consumed, 0); + result.test_sz_lte("at most, all input consumed", input_consumed, ct_chunk.size()); ct_slicer = ct_slicer.subspan(input_consumed); if(requires_entire_message) { - result.test_eq("no output produced", output_written, 0); + result.test_sz_eq("no output produced", output_written, 0); } else { - result.test_eq("all bytes produced", output_written, input_consumed); + result.test_sz_eq("all bytes produced", output_written, input_consumed); pt_stuffer = pt_stuffer.subspan(output_written); } } @@ -1583,11 +2458,12 @@ ct_slicer.size(), &final_input_consumed_dec)); - result.test_eq("remaining bytes consumed in final (decrypt)", final_input_consumed_dec, ct_slicer.size()); - result.test_eq("bytes written in final (decrypt)", final_output_written_dec, expected_final_size_dec); - result.test_eq("dummy buffer unchanged", dummy_buffer, dummy_buffer_reference); + result.test_sz_eq( + "remaining bytes consumed in final (decrypt)", final_input_consumed_dec, ct_slicer.size()); + result.test_sz_eq("bytes written in final (decrypt)", final_output_written_dec, expected_final_size_dec); + result.test_bin_eq("dummy buffer unchanged", dummy_buffer, dummy_buffer_reference); - result.test_eq("decrypted plaintext", decrypted, plaintext); + result.test_bin_eq("decrypted plaintext", decrypted, plaintext); TEST_FFI_OK(botan_cipher_destroy, (cipher_decrypt)); @@ -1619,7 +2495,7 @@ TEST_FFI_OK(botan_cipher_get_update_granularity, (ctr, &update_granularity)); TEST_FFI_OK(botan_cipher_get_ideal_update_granularity, (ctr, &ideal_granularity)); - result.test_eq( + result.test_sz_eq( "ideal granularity is a multiple of update granularity", ideal_granularity % update_granularity, 0); TEST_FFI_RC(0, botan_cipher_is_authenticated, (ctr)); @@ -1634,21 +2510,89 @@ TEST_FFI_OK(botan_cipher_update, (ctr, 0, ct.data(), ct.size(), &output_written, pt.data(), 5, &input_consumed)); - result.test_int_eq(output_written, 5, "Expected output written"); - result.test_int_eq(input_consumed, 5, "Expected input consumed"); + result.test_sz_eq("Expected output written", output_written, 5); + result.test_sz_eq("Expected input consumed", input_consumed, 5); TEST_FFI_OK(botan_cipher_update, (ctr, 0, &ct[5], ct.size() - 5, &output_written, &pt[5], pt.size() - 5, &input_consumed)); - result.test_int_eq(output_written, ct.size() - 5, "Expected output written"); - result.test_int_eq(input_consumed, pt.size() - 5, "Expected input consumed"); - result.test_eq("AES-128/CTR ciphertext", ct, exp_ct); + result.test_sz_eq("Expected output written", output_written, ct.size() - 5); + result.test_sz_eq("Expected input consumed", input_consumed, pt.size() - 5); + result.test_bin_eq("AES-128/CTR ciphertext", ct, exp_ct); TEST_FFI_OK(botan_cipher_destroy, (ctr)); } } }; +class FFI_XOF_Test final : public FFI_Test { + std::string name() const override { return "FFI XOF"; } + + void ffi_test(Test::Result& result, botan_rng_t /*unused*/) override { + const char* input1 = "XOF input"; + const char* input2 = "more XOF input"; + const char* input3 = "additional XOF input"; + const char* xof_name = "SHAKE-128"; + + botan_xof_t xof1; + TEST_FFI_FAIL("unknown XOF", botan_xof_init, (&xof1, "SCHUETTEL-128", 0)); + TEST_FFI_FAIL("invalid flags", botan_xof_init, (&xof1, "SHAKE-128", 42)); + + if(!TEST_FFI_INIT(botan_xof_init, (&xof1, xof_name, 0))) { + return; + } + + std::array out_name{}; + size_t out_name_len = 5; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_xof_name, (xof1, nullptr, &out_name_len)); + result.test_sz_eq("valid XOF name length", out_name_len, out_name.size()); + TEST_FFI_OK(botan_xof_name, (xof1, out_name.data(), &out_name_len)); + + size_t out_block_size; + TEST_FFI_OK(botan_xof_block_size, (xof1, &out_block_size)); + result.test_sz_eq("valid XOF block size", out_block_size, 168); + + result.test_rc("ready for input", botan_xof_accepts_input(xof1), 1); + TEST_FFI_OK(botan_xof_update, (xof1, reinterpret_cast(input1), strlen(input1))); + result.test_rc("still ready for input", botan_xof_accepts_input(xof1), 1); + TEST_FFI_OK(botan_xof_update, (xof1, reinterpret_cast(input2), strlen(input2))); + + botan_xof_t xof2; + TEST_FFI_OK(botan_xof_copy_state, (&xof2, xof1)); + result.test_rc("copy still ready for input", botan_xof_accepts_input(xof2), 1); + + std::array out_bytes{}; + TEST_FFI_OK(botan_xof_output, (xof1, nullptr, 0)); + TEST_FFI_RC(BOTAN_FFI_ERROR_NULL_POINTER, botan_xof_output, (xof1, nullptr, 1)); + TEST_FFI_OK(botan_xof_output, (xof1, out_bytes.data(), out_bytes.size())); + + result.test_bin_eq("expected first output", out_bytes, "2E870A5FE35999A7B15F9F0BB5AC1689"); + result.test_sz_ne("no more input", botan_xof_accepts_input(xof1), 1); + TEST_FFI_RC(BOTAN_FFI_ERROR_INVALID_OBJECT_STATE, + botan_xof_update, + (xof1, reinterpret_cast(input1), strlen(input1))); + + TEST_FFI_OK(botan_xof_output, (xof1, out_bytes.data(), out_bytes.size())); + result.test_bin_eq("expected second output", out_bytes, "E266E213DA0F2763AE29601AB8F9DEDC"); + + TEST_FFI_OK(botan_xof_update, (xof2, reinterpret_cast(input3), strlen(input3))); + TEST_FFI_OK(botan_xof_output, (xof2, out_bytes.data(), out_bytes.size())); + result.test_bin_eq( + "expected first output after additional input", out_bytes, "D9D5416188659DDC5C26FCF52E49A157"); + + TEST_FFI_OK(botan_xof_clear, (xof1)); + result.test_rc("again ready for input", botan_xof_accepts_input(xof1), 1); + TEST_FFI_OK(botan_xof_update, (xof1, reinterpret_cast(input1), strlen(input1))); + TEST_FFI_OK(botan_xof_update, (xof1, reinterpret_cast(input2), strlen(input2))); + TEST_FFI_OK(botan_xof_update, (xof1, reinterpret_cast(input3), strlen(input3))); + TEST_FFI_OK(botan_xof_output, (xof1, out_bytes.data(), out_bytes.size())); + result.test_bin_eq("expected first output with full input", out_bytes, "D9D5416188659DDC5C26FCF52E49A157"); + + TEST_FFI_OK(botan_xof_destroy, (xof1)); + TEST_FFI_OK(botan_xof_destroy, (xof2)); + } +}; + class FFI_HashFunction_Test final : public FFI_Test { public: std::string name() const override { return "FFI hash"; } @@ -1661,25 +2605,25 @@ TEST_FFI_FAIL("invalid flags", botan_hash_init, (&hash, "SHA-256", 1)); if(TEST_FFI_INIT(botan_hash_init, (&hash, "SHA-256", 0))) { - char namebuf[10]; + std::array namebuf{}; size_t name_len = 7; - TEST_FFI_FAIL("output buffer too short", botan_hash_name, (hash, namebuf, &name_len)); - result.test_eq("name len", name_len, 8); + TEST_FFI_FAIL("output buffer too short", botan_hash_name, (hash, namebuf.data(), &name_len)); + result.test_sz_eq("name len", name_len, 8); - name_len = sizeof(namebuf); - if(TEST_FFI_OK(botan_hash_name, (hash, namebuf, &name_len))) { - result.test_eq("name len", name_len, 8); - result.test_eq("name", std::string(namebuf), "SHA-256"); + name_len = namebuf.size(); + if(TEST_FFI_OK(botan_hash_name, (hash, namebuf.data(), &name_len))) { + result.test_sz_eq("name len", name_len, 8); + result.test_str_eq("name", namebuf.data(), "SHA-256"); } size_t block_size; if(TEST_FFI_OK(botan_hash_block_size, (hash, &block_size))) { - result.test_eq("hash block size", block_size, 64); + result.test_sz_eq("hash block size", block_size, 64); } size_t output_len; if(TEST_FFI_OK(botan_hash_output_length, (hash, &output_len))) { - result.test_eq("hash output length", output_len, 32); + result.test_sz_eq("hash output length", output_len, 32); std::vector outbuf(output_len); @@ -1692,7 +2636,7 @@ (hash, reinterpret_cast(input_str), std::strlen(input_str))); TEST_FFI_OK(botan_hash_final, (hash, outbuf.data())); - result.test_eq( + result.test_bin_eq( "SHA-256 output", outbuf, "B5D4045C3F466FA91FE2CC6ABE79232A1A57CDF104F7A26E716E0A1E2789DF78"); } @@ -1709,12 +2653,12 @@ TEST_FFI_OK(botan_hash_update, (hash, reinterpret_cast(&msg[1]), std::strlen(msg) - 1)); TEST_FFI_OK(botan_hash_final, (hash, outbuf.data())); - result.test_eq("hashing split", outbuf, expected); + result.test_bin_eq("hashing split", outbuf, expected); TEST_FFI_OK(botan_hash_update, (fork, reinterpret_cast(&msg[std::strlen(msg) - 1]), 1)); TEST_FFI_OK(botan_hash_final, (fork, outbuf.data())); - result.test_eq("hashing split", outbuf, expected); + result.test_bin_eq("hashing split", outbuf, expected); TEST_FFI_OK(botan_hash_destroy, (fork)); } @@ -1738,30 +2682,32 @@ TEST_FFI_FAIL("bad name", botan_mac_init, (&mac, "HMAC(SHA-259)", 0)); if(TEST_FFI_INIT(botan_mac_init, (&mac, "HMAC(SHA-256)", 0))) { - char namebuf[16]; + std::array namebuf{}; size_t name_len = 13; - TEST_FFI_FAIL("output buffer too short", botan_mac_name, (mac, namebuf, &name_len)); - result.test_eq("name len", name_len, 14); + TEST_FFI_FAIL("output buffer too short", botan_mac_name, (mac, namebuf.data(), &name_len)); + result.test_sz_eq("name len", name_len, 14); - name_len = sizeof(namebuf); - if(TEST_FFI_OK(botan_mac_name, (mac, namebuf, &name_len))) { - result.test_eq("name len", name_len, 14); - result.test_eq("name", std::string(namebuf), "HMAC(SHA-256)"); + name_len = namebuf.size(); + if(TEST_FFI_OK(botan_mac_name, (mac, namebuf.data(), &name_len))) { + result.test_sz_eq("name len", name_len, 14); + result.test_str_eq("name", namebuf.data(), "HMAC(SHA-256)"); } - size_t min_keylen = 0, max_keylen = 0, mod_keylen = 0; + size_t min_keylen = 0; + size_t max_keylen = 0; + size_t mod_keylen = 0; TEST_FFI_RC(0, botan_mac_get_keyspec, (mac, nullptr, nullptr, nullptr)); TEST_FFI_RC(0, botan_mac_get_keyspec, (mac, &min_keylen, nullptr, nullptr)); TEST_FFI_RC(0, botan_mac_get_keyspec, (mac, nullptr, &max_keylen, nullptr)); TEST_FFI_RC(0, botan_mac_get_keyspec, (mac, nullptr, nullptr, &mod_keylen)); - result.test_eq("Expected min keylen", min_keylen, 0); - result.test_eq("Expected max keylen", max_keylen, 4096); - result.test_eq("Expected mod keylen", mod_keylen, 1); + result.test_sz_eq("Expected min keylen", min_keylen, 0); + result.test_sz_eq("Expected max keylen", max_keylen, 8192); + result.test_sz_eq("Expected mod keylen", mod_keylen, 1); size_t output_len; if(TEST_FFI_OK(botan_mac_output_length, (mac, &output_len))) { - result.test_eq("MAC output length", output_len, 32); + result.test_sz_eq("MAC output length", output_len, 32); const uint8_t mac_key[] = {0xAA, 0xBB, 0xCC, 0xDD}; std::vector outbuf(output_len); @@ -1778,7 +2724,7 @@ (mac, reinterpret_cast(input_str), std::strlen(input_str))); TEST_FFI_OK(botan_mac_final, (mac, outbuf.data())); - result.test_eq( + result.test_bin_eq( "HMAC output", outbuf, "1A82EEA984BC4A7285617CC0D05F1FE1D6C96675924A81BC965EE8FF7B0697A7"); } } @@ -1798,16 +2744,18 @@ const char* pass = "password"; if(TEST_FFI_INIT(botan_scrypt, (output.data(), output.size(), pass, salt, sizeof(salt), 8, 1, 1))) { - result.test_eq("scrypt output", output, "4B9B888D695288E002CC4F9D90808A4D296A45CE4471AFBB"); + result.test_bin_eq("scrypt output", output, "4B9B888D695288E002CC4F9D90808A4D296A45CE4471AFBB"); - size_t N, r, p; + size_t N; + size_t r; + size_t p; TEST_FFI_OK(botan_pwdhash_timed, ("Scrypt", 50, &r, &p, &N, output.data(), output.size(), "bunny", 5, salt, sizeof(salt))); std::vector cmp(output.size()); TEST_FFI_OK(botan_pwdhash, ("Scrypt", N, r, p, cmp.data(), cmp.size(), "bunny", 5, salt, sizeof(salt))); - result.test_eq("recomputed scrypt", cmp, output); + result.test_bin_eq("recomputed scrypt", cmp, output); } } }; @@ -1835,9 +2783,10 @@ pbkdf_salt.data(), pbkdf_salt.size(), pbkdf_iterations))) { - result.test_eq("PBKDF output", outbuf, "027AFADD48F4BE8DCC4F"); + result.test_bin_eq("PBKDF output", outbuf, "027AFADD48F4BE8DCC4F"); - size_t iters_10ms, iters_100ms; + size_t iters_10ms; + size_t iters_100ms; TEST_FFI_OK(botan_pbkdf_timed, ("PBKDF2(SHA-1)", @@ -1877,18 +2826,18 @@ kdf_salt.size(), nullptr, 0))) { - result.test_eq("KDF output", outbuf, "3A5DC9AA1C872B4744515AC2702D6396FC2A"); + result.test_bin_eq("KDF output", outbuf, "3A5DC9AA1C872B4744515AC2702D6396FC2A"); } size_t out_len = 64; std::string outstr; outstr.resize(out_len); - int rc = - botan_bcrypt_generate(reinterpret_cast(&outstr[0]), &out_len, passphrase.c_str(), rng, 4, 0); + const int rc = + botan_bcrypt_generate(reinterpret_cast(outstr.data()), &out_len, passphrase.c_str(), rng, 4, 0); if(rc == 0) { - result.test_eq("bcrypt output size", out_len, 61); + result.test_sz_eq("bcrypt output size", out_len, 61); TEST_FFI_OK(botan_bcrypt_is_valid, (passphrase.c_str(), outstr.data())); TEST_FFI_FAIL("bad password", botan_bcrypt_is_valid, ("nope", outstr.data())); @@ -1904,15 +2853,15 @@ botan_block_cipher_t cipher; if(TEST_FFI_INIT(botan_block_cipher_init, (&cipher, "AES-128"))) { - char namebuf[10]; + std::array namebuf{}; size_t name_len = 7; - TEST_FFI_FAIL("output buffer too short", botan_block_cipher_name, (cipher, namebuf, &name_len)); - result.test_eq("name len", name_len, 8); + TEST_FFI_FAIL("output buffer too short", botan_block_cipher_name, (cipher, namebuf.data(), &name_len)); + result.test_sz_eq("name len", name_len, 8); - name_len = sizeof(namebuf); - if(TEST_FFI_OK(botan_block_cipher_name, (cipher, namebuf, &name_len))) { - result.test_eq("name len", name_len, 8); - result.test_eq("name", std::string(namebuf), "AES-128"); + name_len = namebuf.size(); + if(TEST_FFI_OK(botan_block_cipher_name, (cipher, namebuf.data(), &name_len))) { + result.test_sz_eq("name len", name_len, 8); + result.test_str_eq("name", namebuf.data(), "AES-128"); } const std::vector zero16(16, 0); @@ -1930,29 +2879,31 @@ TEST_FFI_RC(16, botan_block_cipher_block_size, (cipher)); - size_t min_keylen = 0, max_keylen = 0, mod_keylen = 0; + size_t min_keylen = 0; + size_t max_keylen = 0; + size_t mod_keylen = 0; TEST_FFI_RC(0, botan_block_cipher_get_keyspec, (cipher, nullptr, nullptr, nullptr)); TEST_FFI_RC(0, botan_block_cipher_get_keyspec, (cipher, &min_keylen, nullptr, nullptr)); TEST_FFI_RC(0, botan_block_cipher_get_keyspec, (cipher, nullptr, &max_keylen, nullptr)); TEST_FFI_RC(0, botan_block_cipher_get_keyspec, (cipher, nullptr, nullptr, &mod_keylen)); - result.test_eq("Expected min keylen", min_keylen, 16); - result.test_eq("Expected max keylen", max_keylen, 16); - result.test_eq("Expected mod keylen", mod_keylen, 1); + result.test_sz_eq("Expected min keylen", min_keylen, 16); + result.test_sz_eq("Expected max keylen", max_keylen, 16); + result.test_sz_eq("Expected mod keylen", mod_keylen, 1); TEST_FFI_OK(botan_block_cipher_set_key, (cipher, zero16.data(), zero16.size())); TEST_FFI_OK(botan_block_cipher_encrypt_blocks, (cipher, block.data(), block.data(), 1)); - result.test_eq("AES-128 encryption works", block, "66E94BD4EF8A2C3B884CFA59CA342B2E"); + result.test_bin_eq("AES-128 encryption works", block, "66E94BD4EF8A2C3B884CFA59CA342B2E"); TEST_FFI_OK(botan_block_cipher_encrypt_blocks, (cipher, block.data(), block.data(), 1)); - result.test_eq("AES-128 encryption works", block, "F795BD4A52E29ED713D313FA20E98DBC"); + result.test_bin_eq("AES-128 encryption works", block, "F795BD4A52E29ED713D313FA20E98DBC"); TEST_FFI_OK(botan_block_cipher_decrypt_blocks, (cipher, block.data(), block.data(), 1)); - result.test_eq("AES-128 decryption works", block, "66E94BD4EF8A2C3B884CFA59CA342B2E"); + result.test_bin_eq("AES-128 decryption works", block, "66E94BD4EF8A2C3B884CFA59CA342B2E"); TEST_FFI_OK(botan_block_cipher_decrypt_blocks, (cipher, block.data(), block.data(), 1)); - result.test_eq("AES-128 decryption works", block, "00000000000000000000000000000000"); + result.test_bin_eq("AES-128 decryption works", block, "00000000000000000000000000000000"); TEST_FFI_OK(botan_block_cipher_clear, (cipher)); botan_block_cipher_destroy(cipher); @@ -1979,13 +2930,13 @@ std::set errors; for(int i = -100; i != 50; ++i) { const char* err = botan_error_description(i); - result.confirm("Never a null pointer", err != nullptr); + result.test_is_true("Never a null pointer", err != nullptr); - if(err) { - std::string s(err); + if(err != nullptr) { + const std::string s(err); if(s != "Unknown error") { - result.confirm("No duplicate messages", !errors.contains(s)); + result.test_is_true("No duplicate messages", !errors.contains(s)); errors.insert(s); } } @@ -2004,7 +2955,7 @@ size_t out_len = sizeof(out_buf); TEST_FFI_OK(botan_base64_encode, (bin, sizeof(bin), out_buf, &out_len)); - result.test_eq("encoded string", out_buf, "FoofBunny900"); + result.test_str_eq("encoded string", out_buf, "FoofBunny900"); out_len -= 1; TEST_FFI_RC( @@ -2018,12 +2969,12 @@ botan_base64_decode, (base64, strlen(base64), out_bin, &out_len)); - result.test_eq("output length", out_len, 18); + result.test_sz_eq("output length", out_len, 18); out_len = sizeof(out_bin); TEST_FFI_OK(botan_base64_decode, (base64, strlen(base64), out_bin, &out_len)); - result.test_eq( + result.test_str_eq( "decoded string", std::string(reinterpret_cast(out_bin), out_len), "Such base64 wow!"); } }; @@ -2038,7 +2989,7 @@ TEST_FFI_OK(botan_hex_encode, (bin, sizeof(bin), hex_buf, 0)); - result.test_eq("encoded string", hex_buf, "DEADBEEF"); + result.test_str_eq("encoded string", hex_buf, "DEADBEEF"); const char* hex = "67657420796572206A756D626F20736872696D70"; uint8_t out_bin[1024] = {0}; @@ -2050,7 +3001,7 @@ out_len = sizeof(out_bin); TEST_FFI_OK(botan_hex_decode, (hex, strlen(hex), out_bin, &out_len)); - result.test_eq( + result.test_str_eq( "decoded string", std::string(reinterpret_cast(out_bin), out_len), "get yer jumbo shrimp"); } }; @@ -2075,29 +3026,29 @@ botan_mp_init(&x); size_t bn_bytes = 0; TEST_FFI_OK(botan_mp_num_bytes, (x, &bn_bytes)); - result.test_eq("Expected size for MP 0", bn_bytes, 0); + result.test_sz_eq("Expected size for MP 0", bn_bytes, 0); botan_mp_set_from_int(x, 5); TEST_FFI_OK(botan_mp_num_bytes, (x, &bn_bytes)); - result.test_eq("Expected size for MP 5", bn_bytes, 1); + result.test_sz_eq("Expected size for MP 5", bn_bytes, 1); botan_mp_add_u32(x, x, 75); TEST_FFI_OK(botan_mp_num_bytes, (x, &bn_bytes)); - result.test_eq("Expected size for MP 80", bn_bytes, 1); + result.test_sz_eq("Expected size for MP 80", bn_bytes, 1); str_len = sizeof(str_buf); TEST_FFI_OK(botan_mp_to_str, (x, 10, str_buf, &str_len)); - result.test_eq("botan_mp_add", std::string(str_buf), "80"); + result.test_str_eq("botan_mp_add", std::string(str_buf), "80"); botan_mp_sub_u32(x, x, 80); TEST_FFI_RC(1, botan_mp_is_zero, (x)); botan_mp_add_u32(x, x, 259); TEST_FFI_OK(botan_mp_num_bytes, (x, &bn_bytes)); - result.test_eq("Expected size for MP 259", bn_bytes, 2); + result.test_sz_eq("Expected size for MP 259", bn_bytes, 2); str_len = sizeof(str_buf); TEST_FFI_OK(botan_mp_to_str, (x, 10, str_buf, &str_len)); - result.test_eq("botan_mp_add", std::string(str_buf), "259"); + result.test_str_eq("botan_mp_add", std::string(str_buf), "259"); TEST_FFI_RC(1, botan_mp_is_odd, (x)); TEST_FFI_RC(0, botan_mp_is_even, (x)); @@ -2110,10 +3061,10 @@ botan_mp_init(&zero); int cmp; TEST_FFI_OK(botan_mp_cmp, (&cmp, x, zero)); - result.confirm("bigint_mp_cmp(+, 0)", cmp == 1); + result.test_is_true("bigint_mp_cmp(+, 0)", cmp == 1); TEST_FFI_OK(botan_mp_cmp, (&cmp, zero, x)); - result.confirm("bigint_mp_cmp(0, +)", cmp == -1); + result.test_is_true("bigint_mp_cmp(0, +)", cmp == -1); TEST_FFI_RC(0, botan_mp_is_negative, (x)); TEST_FFI_RC(1, botan_mp_is_positive, (x)); @@ -2129,43 +3080,62 @@ TEST_FFI_RC(1, botan_mp_is_positive, (zero)); TEST_FFI_OK(botan_mp_cmp, (&cmp, x, zero)); - result.confirm("bigint_mp_cmp(-, 0)", cmp == -1); + result.test_is_true("bigint_mp_cmp(-, 0)", cmp == -1); TEST_FFI_OK(botan_mp_cmp, (&cmp, zero, x)); - result.confirm("bigint_mp_cmp(0, -)", cmp == 1); + result.test_is_true("bigint_mp_cmp(0, -)", cmp == 1); TEST_FFI_OK(botan_mp_cmp, (&cmp, zero, zero)); - result.confirm("bigint_mp_cmp(0, 0)", cmp == 0); + result.test_is_true("bigint_mp_cmp(0, 0)", cmp == 0); TEST_FFI_OK(botan_mp_cmp, (&cmp, x, x)); - result.confirm("bigint_mp_cmp(x, x)", cmp == 0); + result.test_is_true("bigint_mp_cmp(x, x)", cmp == 0); TEST_FFI_OK(botan_mp_flip_sign, (x)); + // Regression test for bug reported by @hgarrereyn + // See: GH #5128 + botan_mp_t out_shift; + TEST_FFI_OK(botan_mp_init, (&out_shift)); + TEST_FFI_OK(botan_mp_lshift, (out_shift, zero, 0)); + botan_mp_destroy(zero); + botan_mp_destroy(out_shift); } size_t x_bits = 0; TEST_FFI_OK(botan_mp_num_bits, (x, &x_bits)); - result.test_eq("botan_mp_num_bits", x_bits, 9); + result.test_sz_eq("botan_mp_num_bits", x_bits, 9); TEST_FFI_OK(botan_mp_to_hex, (x, str_buf)); - result.test_eq("botan_mp_to_hex", std::string(str_buf), "0x0103"); + result.test_str_eq("botan_mp_to_hex", std::string(str_buf), "0x0103"); + + ViewStringSink hex_sink; + TEST_FFI_OK(botan_mp_view_hex, (x, hex_sink.delegate(), hex_sink.callback())); + result.test_str_eq("botan_mp_view_hex", hex_sink.get(), "0x0103"); + + ViewStringSink str_sink; + TEST_FFI_OK(botan_mp_view_str, (x, 10, str_sink.delegate(), str_sink.callback())); + result.test_str_eq("botan_mp_view_str", str_sink.get(), "259"); + + ViewBytesSink bin_sink; + TEST_FFI_OK(botan_mp_view_bin, (x, bin_sink.delegate(), bin_sink.callback())); + result.test_bin_eq("botan_mp_view_str", bin_sink.get(), "0103"); uint32_t x_32; TEST_FFI_OK(botan_mp_to_uint32, (x, &x_32)); - result.test_eq("botan_mp_to_uint32", size_t(x_32), size_t(0x103)); + result.test_sz_eq("botan_mp_to_uint32", size_t(x_32), size_t(0x103)); TEST_FFI_RC(1, botan_mp_get_bit, (x, 1)); TEST_FFI_RC(0, botan_mp_get_bit, (x, 87)); TEST_FFI_OK(botan_mp_set_bit, (x, 87)); TEST_FFI_RC(1, botan_mp_get_bit, (x, 87)); TEST_FFI_OK(botan_mp_to_hex, (x, str_buf)); - result.test_eq("botan_mp_set_bit", std::string(str_buf), "0x8000000000000000000103"); + result.test_str_eq("botan_mp_set_bit", std::string(str_buf), "0x8000000000000000000103"); TEST_FFI_OK(botan_mp_clear_bit, (x, 87)); TEST_FFI_OK(botan_mp_to_hex, (x, str_buf)); - result.test_eq("botan_mp_set_bit", std::string(str_buf), "0x0103"); + result.test_str_eq("botan_mp_set_bit", std::string(str_buf), "0x0103"); botan_mp_t y; TEST_FFI_OK(botan_mp_init, (&y)); @@ -2177,12 +3147,12 @@ TEST_FFI_OK(botan_mp_add, (r, x, y)); str_len = sizeof(str_buf); TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len)); - result.test_eq("botan_mp_add", std::string(str_buf), "19089002"); + result.test_str_eq("botan_mp_add", std::string(str_buf), "19089002"); TEST_FFI_OK(botan_mp_mul, (r, x, y)); str_len = sizeof(str_buf); TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len)); - result.test_eq("botan_mp_mul", std::string(str_buf), "4943984437"); + result.test_str_eq("botan_mp_mul", std::string(str_buf), "4943984437"); TEST_FFI_RC(0, botan_mp_is_negative, (r)); botan_mp_t q; @@ -2191,33 +3161,33 @@ str_len = sizeof(str_buf); TEST_FFI_OK(botan_mp_to_str, (q, 10, str_buf, &str_len)); - result.test_eq("botan_mp_div_q", std::string(str_buf), "73701"); + result.test_str_eq("botan_mp_div_q", std::string(str_buf), "73701"); str_len = sizeof(str_buf); TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len)); - result.test_eq("botan_mp_div_r", std::string(str_buf), "184"); + result.test_str_eq("botan_mp_div_r", std::string(str_buf), "184"); TEST_FFI_OK(botan_mp_set_from_str, (y, "4943984437")); TEST_FFI_OK(botan_mp_sub, (r, x, y)); str_len = sizeof(str_buf); TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len)); - result.test_eq("botan_mp_sub", std::string(str_buf), "-4943984178"); + result.test_str_eq("botan_mp_sub", std::string(str_buf), "-4943984178"); TEST_FFI_RC(1, botan_mp_is_negative, (r)); TEST_FFI_OK(botan_mp_lshift, (r, x, 39)); str_len = sizeof(str_buf); TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len)); - result.test_eq("botan_mp_lshift", std::string(str_buf), "142386755796992"); + result.test_str_eq("botan_mp_lshift", std::string(str_buf), "142386755796992"); TEST_FFI_OK(botan_mp_rshift, (r, r, 3)); str_len = sizeof(str_buf); TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len)); - result.test_eq("botan_mp_rshift", std::string(str_buf), "17798344474624"); + result.test_str_eq("botan_mp_rshift", std::string(str_buf), "17798344474624"); TEST_FFI_OK(botan_mp_gcd, (r, x, y)); str_len = sizeof(str_buf); TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len)); - result.test_eq("botan_mp_gcd", std::string(str_buf), "259"); + result.test_str_eq("botan_mp_gcd", std::string(str_buf), "259"); botan_mp_t p; botan_mp_init(&p); @@ -2228,31 +3198,31 @@ size_t p_bits = 0; TEST_FFI_OK(botan_mp_num_bits, (p, &p_bits)); - result.test_eq("botan_mp_num_bits", p_bits, 127); + result.test_sz_eq("botan_mp_num_bits", p_bits, 127); TEST_FFI_OK(botan_mp_mod_inverse, (r, x, p)); str_len = sizeof(str_buf); TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len)); - result.test_eq("botan_mp_mod_inverse", std::string(str_buf), "40728777507911553541948312086427855425"); + result.test_str_eq("botan_mp_mod_inverse", std::string(str_buf), "40728777507911553541948312086427855425"); TEST_FFI_OK(botan_mp_powmod, (r, x, r, p)); str_len = sizeof(str_buf); TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len)); - result.test_eq("botan_mp_powmod", std::string(str_buf), "40550417419160441638948180641668117560"); + result.test_str_eq("botan_mp_powmod", std::string(str_buf), "40550417419160441638948180641668117560"); TEST_FFI_OK(botan_mp_num_bytes, (r, &bn_bytes)); - result.test_eq("botan_mp_num_bytes", bn_bytes, 16); + result.test_sz_eq("botan_mp_num_bytes", bn_bytes, 16); std::vector bn_buf; bn_buf.resize(bn_bytes); botan_mp_to_bin(r, bn_buf.data()); - result.test_eq("botan_mp_to_bin", bn_buf, "1E81B9EFE0BE1902F6D03F9F5E5FB438"); + result.test_bin_eq("botan_mp_to_bin", bn_buf, "1E81B9EFE0BE1902F6D03F9F5E5FB438"); TEST_FFI_OK(botan_mp_set_from_mp, (y, r)); TEST_FFI_OK(botan_mp_mod_mul, (r, x, y, p)); str_len = sizeof(str_buf); TEST_FFI_OK(botan_mp_to_str, (r, 10, str_buf, &str_len)); - result.test_eq("botan_mp_mod_mul", std::string(str_buf), "123945920473931248854653259523111998693"); + result.test_str_eq("botan_mp_mod_mul", std::string(str_buf), "123945920473931248854653259523111998693"); str_len = 0; TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_mp_to_str, (r, 10, str_buf, &str_len)); @@ -2260,15 +3230,15 @@ size_t x_bytes; botan_mp_rand_bits(x, rng, 512); TEST_FFI_OK(botan_mp_num_bytes, (x, &x_bytes)); - result.test_lte("botan_mp_num_bytes", x_bytes, 512 / 8); + result.test_sz_lte("botan_mp_num_bytes", x_bytes, 512 / 8); TEST_FFI_OK(botan_mp_set_from_radix_str, (x, "909A", 16)); TEST_FFI_OK(botan_mp_to_uint32, (x, &x_32)); - result.test_eq("botan_mp_set_from_radix_str(16)", x_32, static_cast(0x909A)); + result.test_u32_eq("botan_mp_set_from_radix_str(16)", x_32, 0x909A); TEST_FFI_OK(botan_mp_set_from_radix_str, (x, "9098135", 10)); TEST_FFI_OK(botan_mp_to_uint32, (x, &x_32)); - result.test_eq("botan_mp_set_from_radix_str(10)", x_32, static_cast(9098135)); + result.test_u32_eq("botan_mp_set_from_radix_str(10)", x_32, 9098135); botan_mp_destroy(p); botan_mp_destroy(x); @@ -2303,14 +3273,14 @@ uint32_t xval = 0; TEST_FFI_OK(botan_mp_to_uint32, (x, &xval)); - result.test_eq("Expected FPE ciphertext", xval, size_t(605648666)); + result.test_sz_eq("Expected FPE ciphertext", xval, size_t(605648666)); TEST_FFI_OK(botan_fpe_encrypt, (fpe, x, nullptr, 0)); TEST_FFI_OK(botan_fpe_decrypt, (fpe, x, nullptr, 0)); TEST_FFI_OK(botan_fpe_decrypt, (fpe, x, nullptr, 0)); TEST_FFI_OK(botan_mp_to_uint32, (x, &xval)); - result.test_eq("FPE round trip", xval, size_t(178051120)); + result.test_sz_eq("FPE round trip", xval, size_t(178051120)); TEST_FFI_OK(botan_fpe_destroy, (fpe)); TEST_FFI_OK(botan_mp_destroy, (x)); @@ -2334,10 +3304,10 @@ uint32_t code; TEST_FFI_OK(botan_totp_generate, (totp, &code, 59)); - result.confirm("TOTP code", code == 94287082); + result.test_u32_eq("TOTP code", code, 94287082); TEST_FFI_OK(botan_totp_generate, (totp, &code, 1111111109)); - result.confirm("TOTP code 2", code == 7081804); + result.test_u32_eq("TOTP code 2", code, 7081804); TEST_FFI_OK(botan_totp_check, (totp, 94287082, 59 + 60, 60)); TEST_FFI_RC(1, botan_totp_check, (totp, 94287082, 59 + 31, 1)); @@ -2363,22 +3333,22 @@ } TEST_FFI_OK(botan_hotp_generate, (hotp, &hotp_val, 0)); - result.confirm("Valid value for counter 0", hotp_val == 755224); + result.test_u32_eq("Valid value for counter 0", hotp_val, 755224); TEST_FFI_OK(botan_hotp_generate, (hotp, &hotp_val, 1)); - result.confirm("Valid value for counter 0", hotp_val == 287082); + result.test_u32_eq("Valid value for counter 0", hotp_val, 287082); TEST_FFI_OK(botan_hotp_generate, (hotp, &hotp_val, 2)); - result.confirm("Valid value for counter 0", hotp_val == 359152); + result.test_u32_eq("Valid value for counter 0", hotp_val, 359152); TEST_FFI_OK(botan_hotp_generate, (hotp, &hotp_val, 0)); - result.confirm("Valid value for counter 0", hotp_val == 755224); + result.test_u32_eq("Valid value for counter 0", hotp_val, 755224); uint64_t next_ctr = 0; TEST_FFI_OK(botan_hotp_check, (hotp, &next_ctr, 755224, 0, 0)); - result.confirm("HOTP resync", next_ctr == 1); + result.test_u64_eq("HOTP resync", next_ctr, 1); TEST_FFI_OK(botan_hotp_check, (hotp, nullptr, 359152, 2, 0)); TEST_FFI_RC(1, botan_hotp_check, (hotp, nullptr, 359152, 1, 0)); TEST_FFI_OK(botan_hotp_check, (hotp, &next_ctr, 359152, 0, 2)); - result.confirm("HOTP resync", next_ctr == 3); + result.test_u64_eq("HOTP resync", next_ctr, 3); TEST_FFI_OK(botan_hotp_destroy, (hotp)); } @@ -2400,16 +3370,41 @@ 0x81, 0xCA, 0x4F, 0x59, 0x74, 0x4D, 0xED, 0x29, 0x1F, 0x3F, 0xE5, 0x24, 0x00, 0x1B, 0x93, 0x20}; - result.test_eq("Expected wrapped keylen size", wrapped_keylen, 16 + 8); + result.test_sz_eq("Expected wrapped keylen size", wrapped_keylen, 16 + 8); - result.test_eq( - nullptr, "Wrapped key", wrapped, wrapped_keylen, expected_wrapped_key, sizeof(expected_wrapped_key)); + result.test_bin_eq( + "Wrapped key", {wrapped, wrapped_keylen}, {expected_wrapped_key, sizeof(expected_wrapped_key)}); uint8_t dec_key[16] = {0}; size_t dec_keylen = sizeof(dec_key); TEST_FFI_OK(botan_key_unwrap3394, (wrapped, sizeof(wrapped), kek, sizeof(kek), dec_key, &dec_keylen)); - result.test_eq(nullptr, "Unwrapped key", dec_key, dec_keylen, key, sizeof(key)); + result.test_bin_eq("Unwrapped key", {dec_key, dec_keylen}, {key, sizeof(key)}); + } + } +}; + +class FFI_XMSS_Test final : public FFI_Test { + public: + std::string name() const override { return "FFI XMSS"; } + + void ffi_test(Test::Result& result, botan_rng_t rng) override { + botan_privkey_t priv; + if(TEST_FFI_INIT(botan_privkey_create, (&priv, "XMSS", "XMSS-SHA2_10_256", rng))) { + TEST_FFI_OK(botan_privkey_check_key, (priv, rng, 0)); + + TEST_FFI_RC(BOTAN_FFI_ERROR_NULL_POINTER, botan_privkey_stateful_operation, (priv, nullptr)); + TEST_FFI_RC(BOTAN_FFI_ERROR_NULL_POINTER, botan_privkey_remaining_operations, (priv, nullptr)); + + int stateful; + TEST_FFI_OK(botan_privkey_stateful_operation, (priv, &stateful)); + result.test_is_true("key is stateful", stateful == 1); + + uint64_t remaining; + TEST_FFI_OK(botan_privkey_remaining_operations, (priv, &remaining)); + result.test_u64_eq("key has remaining operations", remaining, 1024); + + TEST_FFI_OK(botan_privkey_destroy, (priv)); } } }; @@ -2424,13 +3419,24 @@ if(TEST_FFI_INIT(botan_privkey_create_rsa, (&priv, rng, 1024))) { TEST_FFI_OK(botan_privkey_check_key, (priv, rng, 0)); + int stateful; + TEST_FFI_OK(botan_privkey_stateful_operation, (priv, &stateful)); + result.test_is_true("key is not stateful", stateful == 0); + + uint64_t remaining; + TEST_FFI_FAIL("key is not stateful", botan_privkey_remaining_operations, (priv, &remaining)); + botan_pubkey_t pub; TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv)); TEST_FFI_OK(botan_pubkey_check_key, (pub, rng, 0)); ffi_test_pubkey_export(result, pub, priv, rng); - botan_mp_t p, q, d, n, e; + botan_mp_t p; + botan_mp_t q; + botan_mp_t d; + botan_mp_t n; + botan_mp_t e; botan_mp_init(&p); botan_mp_init(&q); botan_mp_init(&d); @@ -2448,7 +3454,8 @@ // Confirm same (e,n) values in public key { - botan_mp_t pub_e, pub_n; + botan_mp_t pub_e; + botan_mp_t pub_n; botan_mp_init(&pub_e); botan_mp_init(&pub_n); TEST_FFI_OK(botan_pubkey_rsa_get_e, (pub_e, pub)); @@ -2475,10 +3482,8 @@ botan_mp_destroy(x); botan_privkey_t loaded_privkey; - // First try loading a bogus key and verify check_key fails - TEST_FFI_OK(botan_privkey_load_rsa, (&loaded_privkey, n, d, q)); - TEST_FFI_RC(-1, botan_privkey_check_key, (loaded_privkey, rng, 0)); - botan_privkey_destroy(loaded_privkey); + // First try loading a bogus key and verify it is rejected + TEST_FFI_RC(-1, botan_privkey_load_rsa, (&loaded_privkey, n, d, q)); TEST_FFI_OK(botan_privkey_load_rsa, (&loaded_privkey, p, q, e)); TEST_FFI_OK(botan_privkey_check_key, (loaded_privkey, rng, 0)); @@ -2514,15 +3519,15 @@ TEST_FFI_OK(botan_privkey_rsa_get_privkey, (loaded_privkey, pkcs1.data(), &pkcs1_len, BOTAN_PRIVKEY_EXPORT_FLAG_PEM)); - char namebuf[32] = {0}; - size_t name_len = sizeof(namebuf); - if(TEST_FFI_OK(botan_pubkey_algo_name, (loaded_pubkey, namebuf, &name_len))) { - result.test_eq("algo name", std::string(namebuf), "RSA"); + std::array namebuf{}; + size_t name_len = namebuf.size(); + if(TEST_FFI_OK(botan_pubkey_algo_name, (loaded_pubkey, namebuf.data(), &name_len))) { + result.test_str_eq("algo name", namebuf.data(), "RSA"); } - name_len = sizeof(namebuf); - if(TEST_FFI_OK(botan_privkey_algo_name, (loaded_privkey, namebuf, &name_len))) { - result.test_eq("algo name", std::string(namebuf), "RSA"); + name_len = namebuf.size(); + if(TEST_FFI_OK(botan_privkey_algo_name, (loaded_privkey, namebuf.data(), &name_len))) { + result.test_str_eq("algo name", namebuf.data(), "RSA"); } botan_pk_op_encrypt_t encrypt; @@ -2547,7 +3552,7 @@ (decrypt, decrypted.data(), &decrypted_len, ciphertext.data(), ciphertext.size())); decrypted.resize(decrypted_len); - result.test_eq("RSA plaintext", decrypted, plaintext); + result.test_bin_eq("RSA plaintext", decrypted, plaintext); } TEST_FFI_OK(botan_pk_op_decrypt_destroy, (decrypt)); @@ -2590,7 +3595,11 @@ ffi_test_pubkey_export(result, pub, priv, rng); - botan_mp_t p, q, g, x, y; + botan_mp_t p; + botan_mp_t q; + botan_mp_t g; + botan_mp_t x; + botan_mp_t y; botan_mp_init(&p); botan_mp_init(&q); botan_mp_init(&g); @@ -2646,7 +3655,7 @@ size_t output_sig_len = sig_len; TEST_FFI_OK(botan_pk_op_sign_finish, (signer, rng, signature.data(), &output_sig_len)); - result.test_lte("Output length is upper bound", output_sig_len, sig_len); + result.test_sz_lte("Output length is upper bound", output_sig_len, sig_len); signature.resize(output_sig_len); TEST_FFI_OK(botan_pk_op_sign_destroy, (signer)); @@ -2705,7 +3714,10 @@ ffi_test_pubkey_export(result, pub, priv, rng); // Check key load functions - botan_mp_t private_scalar, public_x, public_y; + botan_mp_t private_scalar; + botan_mp_t public_x; + botan_mp_t public_y; + ViewBytesSink sec1; botan_mp_init(&private_scalar); botan_mp_init(&public_x); botan_mp_init(&public_y); @@ -2716,21 +3728,26 @@ TEST_FFI_OK(botan_privkey_get_field, (private_scalar, priv, "x")); TEST_FFI_OK(botan_pubkey_get_field, (public_x, pub, "public_x")); TEST_FFI_OK(botan_pubkey_get_field, (public_y, pub, "public_y")); + TEST_FFI_OK(botan_pubkey_view_raw, (pub, sec1.delegate(), sec1.callback())); botan_privkey_t loaded_privkey; - botan_pubkey_t loaded_pubkey; + botan_pubkey_t loaded_pubkey1; + botan_pubkey_t loaded_pubkey2; TEST_FFI_OK(botan_privkey_load_ecdsa, (&loaded_privkey, private_scalar, kCurve)); - TEST_FFI_OK(botan_pubkey_load_ecdsa, (&loaded_pubkey, public_x, public_y, kCurve)); + TEST_FFI_OK(botan_pubkey_load_ecdsa, (&loaded_pubkey1, public_x, public_y, kCurve)); + TEST_FFI_OK(botan_pubkey_load_ecdsa_sec1, (&loaded_pubkey2, sec1.data(), sec1.size(), kCurve)); TEST_FFI_OK(botan_privkey_check_key, (loaded_privkey, rng, 0)); - TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey, rng, 0)); + TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey1, rng, 0)); + TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey2, rng, 0)); - char namebuf[32] = {0}; - size_t name_len = sizeof(namebuf); + std::array namebuf{}; + size_t name_len = namebuf.size(); - TEST_FFI_OK(botan_pubkey_algo_name, (pub, &namebuf[0], &name_len)); - result.test_eq(namebuf, namebuf, "ECDSA"); + TEST_FFI_OK(botan_pubkey_algo_name, (pub, namebuf.data(), &name_len)); + result.test_str_eq("Algo name is expected", namebuf.data(), "ECDSA"); - std::vector message(1280), signature; + std::vector message(1280); + std::vector signature; TEST_FFI_OK(botan_rng_get, (rng, message.data(), message.size())); for(uint32_t flags = 0; flags <= 1; ++flags) { @@ -2790,7 +3807,8 @@ TEST_FFI_OK(botan_pubkey_destroy, (pub)); TEST_FFI_OK(botan_privkey_destroy, (priv)); TEST_FFI_OK(botan_privkey_destroy, (loaded_privkey)); - TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey)); + TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey1)); + TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey2)); } }; @@ -2804,7 +3822,8 @@ botan_privkey_t priv; botan_pubkey_t pub; botan_privkey_t loaded_privkey; - botan_pubkey_t loaded_pubkey; + botan_pubkey_t loaded_pubkey1; + botan_pubkey_t loaded_pubkey2; if(!TEST_FFI_INIT(botan_privkey_create, (&priv, "SM2_Sig", kCurve, rng))) { return; @@ -2818,7 +3837,10 @@ TEST_FFI_OK(botan_pubkey_sm2_compute_za, (za, &sizeof_za, "Ident", "SM3", pub)); // Check key load functions - botan_mp_t private_scalar, public_x, public_y; + botan_mp_t private_scalar; + botan_mp_t public_x; + botan_mp_t public_y; + ViewBytesSink sec1; botan_mp_init(&private_scalar); botan_mp_init(&public_x); botan_mp_init(&public_y); @@ -2826,18 +3848,22 @@ TEST_FFI_OK(botan_privkey_get_field, (private_scalar, priv, "x")); TEST_FFI_OK(botan_pubkey_get_field, (public_x, pub, "public_x")); TEST_FFI_OK(botan_pubkey_get_field, (public_y, pub, "public_y")); + TEST_FFI_OK(botan_pubkey_view_raw, (pub, sec1.delegate(), sec1.callback())); REQUIRE_FFI_OK(botan_privkey_load_sm2, (&loaded_privkey, private_scalar, kCurve)); - REQUIRE_FFI_OK(botan_pubkey_load_sm2, (&loaded_pubkey, public_x, public_y, kCurve)); + REQUIRE_FFI_OK(botan_pubkey_load_sm2, (&loaded_pubkey1, public_x, public_y, kCurve)); + REQUIRE_FFI_OK(botan_pubkey_load_sm2_sec1, (&loaded_pubkey2, sec1.data(), sec1.size(), kCurve)); TEST_FFI_OK(botan_privkey_check_key, (loaded_privkey, rng, 0)); - TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey, rng, 0)); + TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey1, rng, 0)); + TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey2, rng, 0)); - char namebuf[32] = {0}; - size_t name_len = sizeof(namebuf); + std::array namebuf{}; + size_t name_len = namebuf.size(); - TEST_FFI_OK(botan_pubkey_algo_name, (pub, &namebuf[0], &name_len)); - result.test_eq(namebuf, namebuf, "SM2"); + TEST_FFI_OK(botan_pubkey_algo_name, (pub, namebuf.data(), &name_len)); + result.test_str_eq("Algo name is expected", namebuf.data(), "SM2"); - std::vector message(1280), signature; + std::vector message(1280); + std::vector signature; TEST_FFI_OK(botan_rng_get, (rng, message.data(), message.size())); botan_pk_op_sign_t signer; if(TEST_FFI_OK(botan_pk_op_sign_create, (&signer, loaded_privkey, sm2_ident.c_str(), 0))) { @@ -2890,7 +3916,8 @@ TEST_FFI_OK(botan_pubkey_destroy, (pub)); TEST_FFI_OK(botan_privkey_destroy, (priv)); TEST_FFI_OK(botan_privkey_destroy, (loaded_privkey)); - TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey)); + TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey1)); + TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey2)); } }; @@ -2903,7 +3930,8 @@ botan_privkey_t priv; botan_pubkey_t pub; botan_privkey_t loaded_privkey; - botan_pubkey_t loaded_pubkey; + botan_pubkey_t loaded_pubkey1; + botan_pubkey_t loaded_pubkey2; if(!TEST_FFI_INIT(botan_privkey_create, (&priv, "SM2_Enc", kCurve, rng))) { return; @@ -2917,7 +3945,10 @@ TEST_FFI_OK(botan_pubkey_sm2_compute_za, (za, &sizeof_za, "Ident", "SM3", pub)); // Check key load functions - botan_mp_t private_scalar, public_x, public_y; + botan_mp_t private_scalar; + botan_mp_t public_x; + botan_mp_t public_y; + ViewBytesSink sec1; botan_mp_init(&private_scalar); botan_mp_init(&public_x); botan_mp_init(&public_y); @@ -2925,16 +3956,19 @@ TEST_FFI_OK(botan_privkey_get_field, (private_scalar, priv, "x")); TEST_FFI_OK(botan_pubkey_get_field, (public_x, pub, "public_x")); TEST_FFI_OK(botan_pubkey_get_field, (public_y, pub, "public_y")); + TEST_FFI_OK(botan_pubkey_view_raw, (pub, sec1.delegate(), sec1.callback())); REQUIRE_FFI_OK(botan_privkey_load_sm2_enc, (&loaded_privkey, private_scalar, kCurve)); - REQUIRE_FFI_OK(botan_pubkey_load_sm2_enc, (&loaded_pubkey, public_x, public_y, kCurve)); + REQUIRE_FFI_OK(botan_pubkey_load_sm2_enc, (&loaded_pubkey1, public_x, public_y, kCurve)); + REQUIRE_FFI_OK(botan_pubkey_load_sm2_sec1, (&loaded_pubkey2, sec1.data(), sec1.size(), kCurve)); TEST_FFI_OK(botan_privkey_check_key, (loaded_privkey, rng, 0)); - TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey, rng, 0)); + TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey1, rng, 0)); + TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey2, rng, 0)); - char namebuf[32] = {0}; - size_t name_len = sizeof(namebuf); + std::array namebuf{}; + size_t name_len = namebuf.size(); - TEST_FFI_OK(botan_pubkey_algo_name, (pub, &namebuf[0], &name_len)); - result.test_eq(namebuf, namebuf, "SM2"); + TEST_FFI_OK(botan_pubkey_algo_name, (pub, namebuf.data(), &name_len)); + result.test_str_eq("Algo name is expected", namebuf.data(), "SM2"); std::vector message(32); @@ -2942,7 +3976,7 @@ TEST_FFI_OK(botan_rng_get, (rng, message.data(), message.size())); botan_pk_op_encrypt_t enc; - if(TEST_FFI_OK(botan_pk_op_encrypt_create, (&enc, loaded_pubkey, "", 0))) { + if(TEST_FFI_OK(botan_pk_op_encrypt_create, (&enc, loaded_pubkey1, "", 0))) { size_t ctext_len; TEST_FFI_OK(botan_pk_op_encrypt_output_length, (enc, message.size(), &ctext_len)); @@ -2969,7 +4003,8 @@ TEST_FFI_OK(botan_pubkey_destroy, (pub)); TEST_FFI_OK(botan_privkey_destroy, (priv)); TEST_FFI_OK(botan_privkey_destroy, (loaded_privkey)); - TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey)); + TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey1)); + TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey2)); } }; @@ -2993,7 +4028,10 @@ REQUIRE_FFI_OK(botan_privkey_export_pubkey, (&pub2, priv2)); /* Reload key-pair1 in order to test functions for key loading */ - botan_mp_t private_scalar, public_x, public_y; + botan_mp_t private_scalar; + botan_mp_t public_x; + botan_mp_t public_y; + ViewBytesSink sec1; botan_mp_init(&private_scalar); botan_mp_init(&public_x); botan_mp_init(&public_y); @@ -3001,21 +4039,35 @@ TEST_FFI_OK(botan_privkey_get_field, (private_scalar, priv1, "x")); TEST_FFI_OK(botan_pubkey_get_field, (public_x, pub1, "public_x")); TEST_FFI_OK(botan_pubkey_get_field, (public_y, pub1, "public_y")); + TEST_FFI_OK(botan_pubkey_view_raw, (pub1, sec1.delegate(), sec1.callback())); botan_privkey_t loaded_privkey1; botan_pubkey_t loaded_pubkey1; + botan_pubkey_t loaded_pubkey2; REQUIRE_FFI_OK(botan_privkey_load_ecdh, (&loaded_privkey1, private_scalar, "secp256r1")); REQUIRE_FFI_OK(botan_pubkey_load_ecdh, (&loaded_pubkey1, public_x, public_y, "secp256r1")); + REQUIRE_FFI_OK(botan_pubkey_load_ecdh_sec1, (&loaded_pubkey2, sec1.data(), sec1.size(), "secp256r1")); TEST_FFI_OK(botan_privkey_check_key, (loaded_privkey1, rng, 0)); TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey1, rng, 0)); + TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey2, rng, 0)); ffi_test_pubkey_export(result, loaded_pubkey1, priv1, rng); + ffi_test_pubkey_export(result, loaded_pubkey2, priv1, rng); ffi_test_pubkey_export(result, pub2, priv2, rng); + #if defined(BOTAN_HAS_KDF2) && defined(BOTAN_HAS_SHA_256) + constexpr bool has_kdf2_sha256 = true; + #else + constexpr bool has_kdf2_sha256 = false; + #endif + + const char* kdf = has_kdf2_sha256 ? "KDF2(SHA-256)" : "Raw"; + constexpr size_t salt_len = has_kdf2_sha256 ? 32 : 0; + botan_pk_op_ka_t ka1; - REQUIRE_FFI_OK(botan_pk_op_key_agreement_create, (&ka1, loaded_privkey1, "KDF2(SHA-256)", 0)); + REQUIRE_FFI_OK(botan_pk_op_key_agreement_create, (&ka1, loaded_privkey1, kdf, 0)); botan_pk_op_ka_t ka2; - REQUIRE_FFI_OK(botan_pk_op_key_agreement_create, (&ka2, priv2, "KDF2(SHA-256)", 0)); + REQUIRE_FFI_OK(botan_pk_op_key_agreement_create, (&ka2, priv2, kdf, 0)); size_t pubkey1_len = 0; TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, @@ -3030,10 +4082,10 @@ std::vector pubkey2(pubkey2_len); REQUIRE_FFI_OK(botan_pk_op_key_agreement_export_public, (priv2, pubkey2.data(), &pubkey2_len)); - std::vector salt(32); + std::vector salt(salt_len); TEST_FFI_OK(botan_rng_get, (rng, salt.data(), salt.size())); - const size_t shared_key_len = 64; + const size_t shared_key_len = 32; std::vector key1(shared_key_len); size_t key1_len = key1.size(); @@ -3045,7 +4097,7 @@ TEST_FFI_OK(botan_pk_op_key_agreement, (ka2, key2.data(), &key2_len, pubkey1.data(), pubkey1.size(), salt.data(), salt.size())); - result.test_eq("shared ECDH key", key1, key2); + result.test_bin_eq("shared ECDH key", key1, key2); TEST_FFI_OK(botan_mp_destroy, (private_scalar)); TEST_FFI_OK(botan_mp_destroy, (public_x)); @@ -3058,6 +4110,7 @@ TEST_FFI_OK(botan_pubkey_destroy, (pub2)); TEST_FFI_OK(botan_privkey_destroy, (loaded_privkey1)); TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey1)); + TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey2)); } }; @@ -3073,10 +4126,10 @@ ffi_test_pubkey_export(result, pub, priv, rng); - char namebuf[32] = {0}; - size_t name_len = sizeof(namebuf); - if(TEST_FFI_OK(botan_pubkey_algo_name, (pub, namebuf, &name_len))) { - result.test_eq("algo name", std::string(namebuf), "McEliece"); + std::array namebuf{}; + size_t name_len = namebuf.size(); + if(TEST_FFI_OK(botan_pubkey_algo_name, (pub, namebuf.data(), &name_len))) { + result.test_str_eq("algo name", namebuf.data(), "McEliece"); } // TODO test KEM @@ -3112,13 +4165,13 @@ uint8_t retr_privkey[64]; TEST_FFI_OK(botan_privkey_ed25519_get_privkey, (priv, retr_privkey)); - result.test_eq(nullptr, "Public key matches", retr_privkey + 32, 32, pubkey.data(), pubkey.size()); + result.test_bin_eq("Public key matches", {retr_privkey + 32, 32}, pubkey); TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv)); uint8_t retr_pubkey[32]; TEST_FFI_OK(botan_pubkey_ed25519_get_pubkey, (pub, retr_pubkey)); - result.test_eq(nullptr, "Public key matches", retr_pubkey, 32, pubkey.data(), pubkey.size()); + result.test_bin_eq("Public key matches", {retr_pubkey, 32}, pubkey); TEST_FFI_OK(botan_pubkey_destroy, (pub)); TEST_FFI_OK(botan_pubkey_load_ed25519, (&pub, pubkey.data())); @@ -3140,7 +4193,7 @@ TEST_FFI_OK(botan_pk_op_sign_destroy, (signer)); } - result.test_eq("Expected signature", signature, exp_sig); + result.test_bin_eq("Expected signature", signature, exp_sig); botan_pk_op_verify_t verifier; @@ -3179,13 +4232,13 @@ std::vector retr_privkey(57); TEST_FFI_OK(botan_privkey_ed448_get_privkey, (priv, retr_privkey.data())); - result.test_is_eq("Private key matches", retr_privkey, sk); + result.test_bin_eq("Private key matches", retr_privkey, sk); TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv)); std::vector retr_pubkey(57); TEST_FFI_OK(botan_pubkey_ed448_get_pubkey, (pub, retr_pubkey.data())); - result.test_is_eq("Public key matches", retr_pubkey, pk_ref); + result.test_bin_eq("Public key matches", retr_pubkey, pk_ref); TEST_FFI_OK(botan_pubkey_destroy, (pub)); TEST_FFI_OK(botan_pubkey_load_ed448, (&pub, pk_ref.data())); @@ -3207,7 +4260,7 @@ TEST_FFI_OK(botan_pk_op_sign_destroy, (signer)); } - result.test_eq("Expected signature", signature, sig_ref); + result.test_bin_eq("Expected signature", signature, sig_ref); botan_pk_op_verify_t verifier; @@ -3246,19 +4299,19 @@ std::vector privkey_read(32); TEST_FFI_OK(botan_privkey_x25519_get_privkey, (b_priv, privkey_read.data())); - result.test_eq("X25519 private key", privkey_read, b_priv_bits); + result.test_bin_eq("X25519 private key", privkey_read, b_priv_bits); std::vector pubkey_read(32); botan_pubkey_t b_pub; TEST_FFI_OK(botan_privkey_export_pubkey, (&b_pub, b_priv)); TEST_FFI_OK(botan_pubkey_x25519_get_pubkey, (b_pub, pubkey_read.data())); - result.test_eq("X25519 public key b", pubkey_read, b_pub_bits); + result.test_bin_eq("X25519 public key b", pubkey_read, b_pub_bits); botan_pubkey_t a_pub; TEST_FFI_OK(botan_pubkey_load_x25519, (&a_pub, a_pub_bits.data())); TEST_FFI_OK(botan_pubkey_x25519_get_pubkey, (a_pub, pubkey_read.data())); - result.test_eq("X25519 public key a", pubkey_read, a_pub_bits); + result.test_bin_eq("X25519 public key a", pubkey_read, a_pub_bits); botan_pk_op_ka_t ka; REQUIRE_FFI_OK(botan_pk_op_key_agreement_create, (&ka, b_priv, "Raw", 0)); @@ -3268,7 +4321,7 @@ TEST_FFI_OK(botan_pk_op_key_agreement, (ka, shared_output.data(), &shared_len, a_pub_bits.data(), a_pub_bits.size(), nullptr, 0)); - result.test_eq("Shared secret matches expected", shared_secret_bits, shared_output); + result.test_bin_eq("Shared secret matches expected", shared_secret_bits, shared_output); TEST_FFI_OK(botan_pubkey_destroy, (a_pub)); TEST_FFI_OK(botan_pubkey_destroy, (b_pub)); @@ -3299,19 +4352,19 @@ std::vector privkey_read(56); TEST_FFI_OK(botan_privkey_x448_get_privkey, (b_priv, privkey_read.data())); - result.test_eq("X448 private key", privkey_read, b_priv_ref); + result.test_bin_eq("X448 private key", privkey_read, b_priv_ref); std::vector pubkey_read(56); botan_pubkey_t b_pub; TEST_FFI_OK(botan_privkey_export_pubkey, (&b_pub, b_priv)); TEST_FFI_OK(botan_pubkey_x448_get_pubkey, (b_pub, pubkey_read.data())); - result.test_eq("X448 public key b", pubkey_read, b_pub_ref); + result.test_bin_eq("X448 public key b", pubkey_read, b_pub_ref); botan_pubkey_t a_pub; TEST_FFI_OK(botan_pubkey_load_x448, (&a_pub, a_pub_ref.data())); TEST_FFI_OK(botan_pubkey_x448_get_pubkey, (a_pub, pubkey_read.data())); - result.test_eq("X448 public key a", pubkey_read, a_pub_ref); + result.test_bin_eq("X448 public key a", pubkey_read, a_pub_ref); botan_pk_op_ka_t ka; REQUIRE_FFI_OK(botan_pk_op_key_agreement_create, (&ka, b_priv, "Raw", 0)); @@ -3321,7 +4374,7 @@ TEST_FFI_OK(botan_pk_op_key_agreement, (ka, shared_output.data(), &shared_len, a_pub_ref.data(), a_pub_ref.size(), nullptr, 0)); - result.test_eq("Shared secret matches expected", shared_secret_ref, shared_output); + result.test_bin_eq("Shared secret matches expected", shared_secret_ref, shared_output); TEST_FFI_OK(botan_pubkey_destroy, (a_pub)); TEST_FFI_OK(botan_pubkey_destroy, (b_pub)); @@ -3331,40 +4384,6 @@ }; /** - * Helper class for testing "view"-style API functions that take a callback - * that gets passed a variable-length buffer of bytes. - * - * Example: - * botan_privkey_t priv; - * ViewBytesSink sink; - * botan_privkey_view_raw(priv, sink.delegate(), sink.callback()); - * std::cout << hex_encode(sink.get()) << std::endl; - */ -class ViewBytesSink final { - public: - void* delegate() { return this; } - - botan_view_bin_fn callback() { return &write_fn; } - - const std::vector& get() { return m_buf; } - - private: - static int write_fn(void* ctx, const uint8_t buf[], size_t len) { - if(!ctx || !buf) { - return BOTAN_FFI_ERROR_NULL_POINTER; - } - - auto* sink = static_cast(ctx); - sink->m_buf.assign(buf, buf + len); - - return 0; - } - - private: - std::vector m_buf; -}; - -/** * Base class for roundtrip tests of FFI bindings for Key Encapsulation Mechanisms. */ class FFI_KEM_Roundtrip_Test : public FFI_Test { @@ -3380,7 +4399,7 @@ public: void ffi_test(Test::Result& result, botan_rng_t rng) override { - for(auto mode : modes()) { + for(const auto* mode : modes()) { // generate a key pair botan_privkey_t priv; botan_pubkey_t pub; @@ -3408,8 +4427,8 @@ ViewBytesSink pub_bytes2; TEST_FFI_OK(botan_privkey_view_raw, (priv_loaded, priv_bytes2.delegate(), priv_bytes2.callback())); TEST_FFI_OK(botan_pubkey_view_raw, (pub_loaded, pub_bytes2.delegate(), pub_bytes2.callback())); - result.test_eq("private key encoding", priv_bytes.get(), priv_bytes2.get()); - result.test_eq("public key encoding", pub_bytes.get(), pub_bytes2.get()); + result.test_bin_eq("private key encoding", priv_bytes.get(), priv_bytes2.get()); + result.test_bin_eq("public key encoding", pub_bytes.get(), pub_bytes2.get()); // KEM encryption (using the loaded public key) botan_pk_op_kem_encrypt_t kem_enc; @@ -3437,8 +4456,9 @@ &ciphertext_length_out)); // TODO: should this report both lengths for usage convenience? - result.confirm("at least one buffer length is reported", - shared_key_length_out == shared_key_length || ciphertext_length_out == ciphertext_length); + result.test_is_true( + "at least one buffer length is reported", + shared_key_length_out == shared_key_length || ciphertext_length_out == ciphertext_length); // allocate buffers (with additional space) and perform the actual encryption shared_key_length_out = shared_key_length * 2; @@ -3455,8 +4475,8 @@ &shared_key_length_out, ciphertext.data(), &ciphertext_length_out)); - result.test_eq("shared key length", shared_key_length, shared_key_length_out); - result.test_eq("ciphertext length", ciphertext_length, ciphertext_length_out); + result.test_sz_eq("shared key length", shared_key_length, shared_key_length_out); + result.test_sz_eq("ciphertext length", ciphertext_length, ciphertext_length_out); shared_key.resize(shared_key_length_out); ciphertext.resize(ciphertext_length_out); TEST_FFI_OK(botan_pk_op_kem_encrypt_destroy, (kem_enc)); @@ -3466,7 +4486,7 @@ TEST_FFI_OK(botan_pk_op_kem_decrypt_create, (&kem_dec, priv, "Raw")); size_t shared_key_length2 = 0; TEST_FFI_OK(botan_pk_op_kem_decrypt_shared_key_length, (kem_dec, shared_key_length, &shared_key_length2)); - result.test_eq("shared key lengths are consistent", shared_key_length, shared_key_length2); + result.test_sz_eq("shared key lengths are consistent", shared_key_length, shared_key_length2); // check that insufficient buffer space is handled correctly shared_key_length_out = 0; @@ -3480,7 +4500,7 @@ 0 /* default length */, nullptr, &shared_key_length_out)); - result.test_eq("reported buffer length requirement", shared_key_length, shared_key_length_out); + result.test_sz_eq("reported buffer length requirement", shared_key_length, shared_key_length_out); // allocate buffer (double the size) and perform the actual decryption shared_key_length_out = shared_key_length * 2; @@ -3494,12 +4514,12 @@ 0 /* default length */, shared_key2.data(), &shared_key_length_out)); - result.test_eq("shared key output length", shared_key_length, shared_key_length_out); + result.test_sz_eq("shared key output length", shared_key_length, shared_key_length_out); shared_key2.resize(shared_key_length_out); TEST_FFI_OK(botan_pk_op_kem_decrypt_destroy, (kem_dec)); // final check and clean up - result.test_eq("shared keys match", shared_key, shared_key2); + result.test_bin_eq("shared keys match", shared_key, shared_key2); TEST_FFI_OK(botan_pubkey_destroy, (pub)); TEST_FFI_OK(botan_pubkey_destroy, (pub_loaded)); @@ -3529,7 +4549,7 @@ const std::vector message1 = {'H', 'e', 'l', 'l', 'o', ' '}; const std::vector message2 = {'W', 'o', 'r', 'l', 'd', '!'}; - for(auto mode : modes()) { + for(const auto* mode : modes()) { // generate a key pair botan_privkey_t priv; botan_pubkey_t pub; @@ -3557,8 +4577,8 @@ ViewBytesSink pub_bytes2; TEST_FFI_OK(botan_privkey_view_raw, (priv_loaded, priv_bytes2.delegate(), priv_bytes2.callback())); TEST_FFI_OK(botan_pubkey_view_raw, (pub_loaded, pub_bytes2.delegate(), pub_bytes2.callback())); - result.test_eq("private key encoding", priv_bytes.get(), priv_bytes2.get()); - result.test_eq("public key encoding", pub_bytes.get(), pub_bytes2.get()); + result.test_bin_eq("private key encoding", priv_bytes.get(), priv_bytes2.get()); + result.test_bin_eq("public key encoding", pub_bytes.get(), pub_bytes2.get()); // Signature Creation (using the loaded private key) botan_pk_op_sign_t signer; @@ -3577,7 +4597,7 @@ TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_pk_op_sign_finish, (signer, rng, nullptr, &sig_output_length_out)); - result.test_eq("reported sig lengths are equal", sig_output_length, sig_output_length_out); + result.test_sz_eq("reported sig lengths are equal", sig_output_length, sig_output_length_out); // Recreate signer and try again TEST_FFI_OK(botan_pk_op_sign_destroy, (signer)); @@ -3589,7 +4609,7 @@ sig_output_length_out = sig_output_length * 2; Botan::secure_vector signature(sig_output_length_out); TEST_FFI_OK(botan_pk_op_sign_finish, (signer, rng, signature.data(), &sig_output_length_out)); - result.test_eq("signature length", sig_output_length, sig_output_length_out); + result.test_sz_eq("signature length", sig_output_length, sig_output_length_out); signature.resize(sig_output_length_out); TEST_FFI_OK(botan_pk_op_sign_destroy, (signer)); @@ -3640,8 +4660,8 @@ ViewBytesSink privkey_read_raw; TEST_FFI_OK(botan_privkey_view_kyber_raw_key, (b_priv, privkey_read.delegate(), privkey_read.callback())); TEST_FFI_OK(botan_privkey_view_raw, (b_priv, privkey_read_raw.delegate(), privkey_read_raw.callback())); - result.test_eq("kyber512 private key", privkey_read.get(), b_priv_bits); - result.test_eq("kyber512 private key raw", privkey_read_raw.get(), b_priv_bits); + result.test_bin_eq("kyber512 private key", privkey_read.get(), b_priv_bits); + result.test_bin_eq("kyber512 private key raw", privkey_read_raw.get(), b_priv_bits); ViewBytesSink pubkey_read; ViewBytesSink pubkey_read_raw; @@ -3650,13 +4670,13 @@ TEST_FFI_OK(botan_privkey_export_pubkey, (&b_pub, b_priv)); TEST_FFI_OK(botan_pubkey_view_kyber_raw_key, (b_pub, pubkey_read.delegate(), pubkey_read.callback())); TEST_FFI_OK(botan_pubkey_view_raw, (b_pub, pubkey_read_raw.delegate(), pubkey_read_raw.callback())); - result.test_eq("kyber512 public key b", pubkey_read.get(), b_pub_bits); - result.test_eq("kyber512 raw public key b", pubkey_read_raw.get(), b_pub_bits); + result.test_bin_eq("kyber512 public key b", pubkey_read.get(), b_pub_bits); + result.test_bin_eq("kyber512 raw public key b", pubkey_read_raw.get(), b_pub_bits); botan_pubkey_t a_pub; TEST_FFI_OK(botan_pubkey_load_kyber, (&a_pub, a_pub_bits.data(), 800)); TEST_FFI_OK(botan_pubkey_view_kyber_raw_key, (a_pub, pubkey_read.delegate(), pubkey_read.callback())); - result.test_eq("kyber512 public key a", pubkey_read.get(), a_pub_bits); + result.test_bin_eq("kyber512 public key a", pubkey_read.get(), a_pub_bits); TEST_FFI_OK(botan_pubkey_destroy, (a_pub)); TEST_FFI_OK(botan_pubkey_destroy, (b_pub)); @@ -3685,8 +4705,8 @@ ViewBytesSink privkey_read_raw; TEST_FFI_OK(botan_privkey_view_kyber_raw_key, (b_priv, privkey_read.delegate(), privkey_read.callback())); TEST_FFI_OK(botan_privkey_view_raw, (b_priv, privkey_read_raw.delegate(), privkey_read_raw.callback())); - result.test_eq("kyber768 private key", privkey_read.get(), b_priv_bits); - result.test_eq("kyber768 private key raw", privkey_read_raw.get(), b_priv_bits); + result.test_bin_eq("kyber768 private key", privkey_read.get(), b_priv_bits); + result.test_bin_eq("kyber768 private key raw", privkey_read_raw.get(), b_priv_bits); ViewBytesSink pubkey_read; ViewBytesSink pubkey_read_raw; @@ -3695,13 +4715,13 @@ TEST_FFI_OK(botan_privkey_export_pubkey, (&b_pub, b_priv)); TEST_FFI_OK(botan_pubkey_view_kyber_raw_key, (b_pub, pubkey_read.delegate(), pubkey_read.callback())); TEST_FFI_OK(botan_pubkey_view_raw, (b_pub, pubkey_read_raw.delegate(), pubkey_read_raw.callback())); - result.test_eq("kyber768 public key b", pubkey_read.get(), b_pub_bits); - result.test_eq("kyber768 public key raw b", pubkey_read_raw.get(), b_pub_bits); + result.test_bin_eq("kyber768 public key b", pubkey_read.get(), b_pub_bits); + result.test_bin_eq("kyber768 public key raw b", pubkey_read_raw.get(), b_pub_bits); botan_pubkey_t a_pub; TEST_FFI_OK(botan_pubkey_load_kyber, (&a_pub, a_pub_bits.data(), 1184)); TEST_FFI_OK(botan_pubkey_view_kyber_raw_key, (a_pub, pubkey_read.delegate(), pubkey_read.callback())); - result.test_eq("kyber768 public key a", pubkey_read.get(), a_pub_bits); + result.test_bin_eq("kyber768 public key a", pubkey_read.get(), a_pub_bits); TEST_FFI_OK(botan_pubkey_destroy, (a_pub)); TEST_FFI_OK(botan_pubkey_destroy, (b_pub)); @@ -3730,8 +4750,8 @@ ViewBytesSink privkey_read_raw; TEST_FFI_OK(botan_privkey_view_kyber_raw_key, (b_priv, privkey_read.delegate(), privkey_read.callback())); TEST_FFI_OK(botan_privkey_view_raw, (b_priv, privkey_read_raw.delegate(), privkey_read_raw.callback())); - result.test_eq("kyber1024 private key", privkey_read.get(), b_priv_bits); - result.test_eq("kyber1024 private key raw", privkey_read_raw.get(), b_priv_bits); + result.test_bin_eq("kyber1024 private key", privkey_read.get(), b_priv_bits); + result.test_bin_eq("kyber1024 private key raw", privkey_read_raw.get(), b_priv_bits); ViewBytesSink pubkey_read; ViewBytesSink pubkey_read_raw; @@ -3740,13 +4760,13 @@ TEST_FFI_OK(botan_privkey_export_pubkey, (&b_pub, b_priv)); TEST_FFI_OK(botan_pubkey_view_kyber_raw_key, (b_pub, pubkey_read.delegate(), pubkey_read.callback())); TEST_FFI_OK(botan_pubkey_view_raw, (b_pub, pubkey_read_raw.delegate(), pubkey_read_raw.callback())); - result.test_eq("kyber1024 public key b", pubkey_read.get(), b_pub_bits); - result.test_eq("kyber1024 public key raw b", pubkey_read_raw.get(), b_pub_bits); + result.test_bin_eq("kyber1024 public key b", pubkey_read.get(), b_pub_bits); + result.test_bin_eq("kyber1024 public key raw b", pubkey_read_raw.get(), b_pub_bits); botan_pubkey_t a_pub; TEST_FFI_OK(botan_pubkey_load_kyber, (&a_pub, a_pub_bits.data(), 1568)); TEST_FFI_OK(botan_pubkey_view_kyber_raw_key, (a_pub, pubkey_read.delegate(), pubkey_read.callback())); - result.test_eq("kyber1024 public key a", pubkey_read.get(), a_pub_bits); + result.test_bin_eq("kyber1024 public key a", pubkey_read.get(), a_pub_bits); TEST_FFI_OK(botan_pubkey_destroy, (a_pub)); TEST_FFI_OK(botan_pubkey_destroy, (b_pub)); @@ -3917,12 +4937,15 @@ static void do_elgamal_test(botan_privkey_t priv, botan_rng_t rng, Test::Result& result) { TEST_FFI_OK(botan_privkey_check_key, (priv, rng, 0)); - botan_pubkey_t pub; + botan_pubkey_t pub = nullptr; TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv)); TEST_FFI_OK(botan_pubkey_check_key, (pub, rng, 0)); ffi_test_pubkey_export(result, pub, priv, rng); - botan_mp_t p, g, x, y; + botan_mp_t p = nullptr; + botan_mp_t g = nullptr; + botan_mp_t x = nullptr; + botan_mp_t y = nullptr; botan_mp_init(&p); botan_mp_init(&g); botan_mp_init(&x); @@ -3981,7 +5004,7 @@ TEST_FFI_OK(botan_pk_op_decrypt_destroy, (op_dec)); } - result.test_eq("decryption worked", decryption, plaintext); + result.test_bin_eq("decryption worked", decryption, plaintext); TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey)); TEST_FFI_OK(botan_pubkey_destroy, (pub)); @@ -4010,7 +5033,10 @@ REQUIRE_FFI_OK(botan_privkey_export_pubkey, (&pub2, priv2)); // Reload key-pair1 in order to test functions for key loading - botan_mp_t private_x, public_g, public_p, public_y; + botan_mp_t private_x; + botan_mp_t public_g; + botan_mp_t public_p; + botan_mp_t public_y; botan_mp_init(&private_x); botan_mp_init(&public_g); @@ -4030,7 +5056,9 @@ TEST_FFI_OK(botan_privkey_check_key, (loaded_privkey1, rng, 0)); TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey1, rng, 0)); - botan_mp_t loaded_public_g, loaded_public_p, loaded_public_y; + botan_mp_t loaded_public_g; + botan_mp_t loaded_public_p; + botan_mp_t loaded_public_y; botan_mp_init(&loaded_public_g); botan_mp_init(&loaded_public_p); botan_mp_init(&loaded_public_y); @@ -4042,13 +5070,13 @@ int cmp; TEST_FFI_OK(botan_mp_cmp, (&cmp, loaded_public_g, public_g)); - result.confirm("bigint_mp_cmp(g, g)", cmp == 0); + result.test_is_true("bigint_mp_cmp(g, g)", cmp == 0); TEST_FFI_OK(botan_mp_cmp, (&cmp, loaded_public_p, public_p)); - result.confirm("bigint_mp_cmp(p, p)", cmp == 0); + result.test_is_true("bigint_mp_cmp(p, p)", cmp == 0); TEST_FFI_OK(botan_mp_cmp, (&cmp, loaded_public_y, public_y)); - result.confirm("bigint_mp_cmp(y, y)", cmp == 0); + result.test_is_true("bigint_mp_cmp(y, y)", cmp == 0); botan_pk_op_ka_t ka1; REQUIRE_FFI_OK(botan_pk_op_key_agreement_create, (&ka1, loaded_privkey1, "Raw", 0)); @@ -4082,7 +5110,7 @@ TEST_FFI_OK(botan_pk_op_key_agreement, (ka2, key2.data(), &key2_len, pubkey1.data(), pubkey1.size(), nullptr, 0)); - result.test_eq("shared DH key", key1, key2); + result.test_bin_eq("shared DH key", key1, key2); TEST_FFI_OK(botan_mp_destroy, (private_x)); TEST_FFI_OK(botan_mp_destroy, (public_p)); @@ -4104,6 +5132,583 @@ } }; +class FFI_OID_Test final : public FFI_Test { + public: + std::string name() const override { return "FFI OID"; } + + void ffi_test(Test::Result& result, botan_rng_t rng) override { + botan_asn1_oid_t oid; + botan_asn1_oid_t new_oid; + botan_asn1_oid_t new_oid_from_string; + botan_asn1_oid_t oid_a; + botan_asn1_oid_t oid_b; + botan_asn1_oid_t oid_c; + + TEST_FFI_FAIL("empty oid", botan_oid_from_string, (&oid, "")); + TEST_FFI_OK(botan_oid_from_string, (&oid, "1.2.3.4.5")); + + TEST_FFI_RC(BOTAN_FFI_ERROR_BAD_PARAMETER, botan_oid_from_string, (&new_oid, "a.a.a")); + TEST_FFI_RC(BOTAN_FFI_ERROR_BAD_PARAMETER, botan_oid_from_string, (&new_oid, "0.40")); + TEST_FFI_RC( + BOTAN_FFI_ERROR_BAD_PARAMETER, botan_oid_from_string, (&new_oid, "random-name-that-definitely-has-no-oid")); + + TEST_FFI_OK(botan_oid_from_string, (&new_oid, "1.2.3.4.5.6.7.8")); + TEST_FFI_OK(botan_oid_register, (new_oid, "random-name-that-definitely-has-no-oid")); + + TEST_FFI_OK(botan_oid_from_string, (&new_oid_from_string, "random-name-that-definitely-has-no-oid")); + TEST_FFI_RC(1, botan_oid_equal, (new_oid, new_oid_from_string)); + + TEST_FFI_OK(botan_oid_from_string, (&oid_a, "1.2.3.4.5.6")); + TEST_FFI_OK(botan_oid_from_string, (&oid_b, "1.2.3.4.5.6")); + TEST_FFI_OK(botan_oid_from_string, (&oid_c, "1.2.3.4.4")); + + TEST_FFI_RC(1, botan_oid_equal, (oid_a, oid_b)); + TEST_FFI_RC(0, botan_oid_equal, (oid_a, oid_c)); + + int res; + + TEST_FFI_OK(botan_oid_cmp, (&res, oid_a, oid_b)); + result.test_is_true("oid_a and oid_b are equal", res == 0); + + TEST_FFI_OK(botan_oid_cmp, (&res, oid_a, oid_c)); + result.test_is_true("oid_a is bigger", res == 1); + + TEST_FFI_OK(botan_oid_cmp, (&res, oid_c, oid_a)); + result.test_is_true("oid_c is smaller", res == -1); + + TEST_FFI_OK(botan_oid_destroy, (oid)); + TEST_FFI_OK(botan_oid_destroy, (new_oid)); + TEST_FFI_OK(botan_oid_destroy, (new_oid_from_string)); + TEST_FFI_OK(botan_oid_destroy, (oid_a)); + TEST_FFI_OK(botan_oid_destroy, (oid_b)); + TEST_FFI_OK(botan_oid_destroy, (oid_c)); + + botan_privkey_t priv; + if(TEST_FFI_INIT(botan_privkey_create_rsa, (&priv, rng, 1024))) { + TEST_FFI_OK(botan_privkey_check_key, (priv, rng, 0)); + + const std::string oid_rsa_expected = "1.2.840.113549.1.1.1"; + + botan_asn1_oid_t rsa_oid_priv; + botan_asn1_oid_t rsa_oid_pub; + botan_asn1_oid_t rsa_oid_expected; + botan_asn1_oid_t rsa_oid_from_name; + + TEST_FFI_RC(BOTAN_FFI_ERROR_NULL_POINTER, botan_oid_from_string, (&rsa_oid_expected, nullptr)); + TEST_FFI_RC(BOTAN_FFI_ERROR_NULL_POINTER, botan_oid_from_string, (nullptr, "1.2.3.4.5")); + TEST_FFI_OK(botan_oid_from_string, (&rsa_oid_expected, oid_rsa_expected.c_str())); + TEST_FFI_OK(botan_privkey_oid, (&rsa_oid_priv, priv)); + + TEST_FFI_RC(1, botan_oid_equal, (rsa_oid_priv, rsa_oid_expected)); + + botan_pubkey_t pub; + TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv)); + + TEST_FFI_OK(botan_pubkey_oid, (&rsa_oid_pub, pub)); + TEST_FFI_RC(1, botan_oid_equal, (rsa_oid_pub, rsa_oid_expected)); + + ViewStringSink oid_string; + TEST_FFI_OK(botan_oid_view_string, (rsa_oid_expected, oid_string.delegate(), oid_string.callback())); + const std::string oid_actual = {oid_string.get().begin(), oid_string.get().end()}; + + result.test_str_eq("oid to string", oid_actual, oid_rsa_expected); + + TEST_FFI_OK(botan_oid_from_string, (&rsa_oid_from_name, "RSA")); + TEST_FFI_RC(1, botan_oid_equal, (rsa_oid_expected, rsa_oid_from_name)); + + ViewStringSink rsa_name; + TEST_FFI_OK(botan_oid_view_name, (rsa_oid_from_name, rsa_name.delegate(), rsa_name.callback())); + const std::string rsa_name_string = {rsa_name.get().begin(), rsa_name.get().end()}; + result.test_str_eq("oid to name", rsa_name_string, "RSA"); + + TEST_FFI_OK(botan_oid_destroy, (rsa_oid_priv)); + TEST_FFI_OK(botan_oid_destroy, (rsa_oid_pub)); + TEST_FFI_OK(botan_oid_destroy, (rsa_oid_expected)); + TEST_FFI_OK(botan_oid_destroy, (rsa_oid_from_name)); + + TEST_FFI_OK(botan_pubkey_destroy, (pub)); + TEST_FFI_OK(botan_privkey_destroy, (priv)); + } + } +}; + +class FFI_EC_Group_Test final : public FFI_Test { + public: + std::string name() const override { return "FFI EC Group"; } + + void ffi_test(Test::Result& result, botan_rng_t rng) override { + int appl_spec_groups; + int named_group; + TEST_FFI_OK(botan_ec_group_supports_application_specific_group, (&appl_spec_groups)); + TEST_FFI_OK(botan_ec_group_supports_named_group, ("secp256r1", &named_group)); + result.test_bool_eq("application specific groups support matches build", + appl_spec_groups == 1, + Botan::EC_Group::supports_application_specific_group()); + result.test_bool_eq( + "named group support matches build", named_group == 1, Botan::EC_Group::supports_named_group("secp256r1")); + + if(named_group == 1) { + botan_ec_group_t group_from_name; + botan_asn1_oid_t oid_from_name; + botan_mp_t p_from_name; + botan_mp_t a_from_name; + botan_mp_t b_from_name; + botan_mp_t g_x_from_name; + botan_mp_t g_y_from_name; + botan_mp_t order_from_name; + + TEST_FFI_RC(BOTAN_FFI_ERROR_BAD_PARAMETER, botan_ec_group_from_name, (&group_from_name, "")); + + TEST_FFI_OK(botan_ec_group_from_name, (&group_from_name, "secp256r1")); + + get_group_parameters(group_from_name, + &oid_from_name, + &p_from_name, + &a_from_name, + &b_from_name, + &g_x_from_name, + &g_y_from_name, + &order_from_name, + result); + + botan_asn1_oid_t group_oid; + botan_ec_group_t group_from_oid; + botan_asn1_oid_t oid_from_oid; + botan_mp_t p_from_oid; + botan_mp_t a_from_oid; + botan_mp_t b_from_oid; + botan_mp_t g_x_from_oid; + botan_mp_t g_y_from_oid; + botan_mp_t order_from_oid; + + TEST_FFI_OK(botan_oid_from_string, (&group_oid, "1.2.840.10045.3.1.7")); + + TEST_FFI_OK(botan_ec_group_from_oid, (&group_from_oid, group_oid)); + + get_group_parameters(group_from_oid, + &oid_from_oid, + &p_from_oid, + &a_from_oid, + &b_from_oid, + &g_x_from_oid, + &g_y_from_oid, + &order_from_oid, + result); + + TEST_FFI_RC(1, botan_oid_equal, (group_oid, oid_from_oid)); + TEST_FFI_RC(1, botan_oid_equal, (oid_from_name, oid_from_oid)); + + if(appl_spec_groups == 1) { + botan_asn1_oid_t group_parameter_oid; + botan_mp_t p_parameter; + botan_mp_t a_parameter; + botan_mp_t b_parameter; + botan_mp_t g_x_parameter; + botan_mp_t g_y_parameter; + botan_mp_t order_parameter; + + botan_ec_group_t group_from_parameters; + botan_asn1_oid_t oid_from_parameters; + botan_mp_t p_from_parameters; + botan_mp_t a_from_parameters; + botan_mp_t b_from_parameters; + botan_mp_t g_x_from_parameters; + botan_mp_t g_y_from_parameters; + botan_mp_t order_from_parameters; + + TEST_FFI_OK(botan_oid_from_string, (&group_parameter_oid, "1.3.6.1.4.1.25258.100.0")); + botan_oid_register(group_parameter_oid, "secp256r1-but-manually-registered"); + botan_mp_init(&p_parameter); + botan_mp_init(&a_parameter); + botan_mp_init(&b_parameter); + botan_mp_init(&g_x_parameter); + botan_mp_init(&g_y_parameter); + botan_mp_init(&order_parameter); + + botan_mp_set_from_str(p_parameter, "0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF"); + botan_mp_set_from_str(a_parameter, "0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC"); + botan_mp_set_from_str(b_parameter, "0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B"); + botan_mp_set_from_str(g_x_parameter, + "0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"); + botan_mp_set_from_str(g_y_parameter, + "0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"); + botan_mp_set_from_str(order_parameter, + "0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"); + + TEST_FFI_OK(botan_ec_group_from_params, + (&group_from_parameters, + group_parameter_oid, + p_parameter, + a_parameter, + b_parameter, + g_x_parameter, + g_y_parameter, + order_parameter)); + + get_group_parameters(group_from_parameters, + &oid_from_parameters, + &p_from_parameters, + &a_from_parameters, + &b_from_parameters, + &g_x_from_parameters, + &g_y_from_parameters, + &order_from_parameters, + result); + + botan_ec_group_t group_from_registered_oid; + + TEST_FFI_OK(botan_ec_group_from_name, (&group_from_registered_oid, "secp256r1-but-manually-registered")); + + // we registered this group under a different oid + TEST_FFI_RC(0, botan_oid_equal, (oid_from_oid, oid_from_parameters)); + + TEST_FFI_RC(1, botan_ec_group_equal, (group_from_name, group_from_parameters)); + TEST_FFI_RC(1, botan_ec_group_equal, (group_from_parameters, group_from_registered_oid)); + + const std::vector> parameters_inner = { + {p_from_name, p_from_parameters}, + {a_from_name, a_from_parameters}, + {b_from_name, b_from_parameters}, + {g_x_from_name, g_x_from_parameters}, + {g_y_from_name, g_y_from_parameters}, + {order_from_name, order_from_parameters}}; + + for(auto [x, y] : parameters_inner) { + TEST_FFI_RC(1, botan_mp_equal, (x, y)); + botan_mp_destroy(y); + } + + botan_mp_destroy(p_parameter); + botan_mp_destroy(a_parameter); + botan_mp_destroy(b_parameter); + botan_mp_destroy(g_x_parameter); + botan_mp_destroy(g_y_parameter); + botan_mp_destroy(order_parameter); + + TEST_FFI_RC(1, botan_ec_group_unregister, (group_parameter_oid)); + TEST_FFI_RC(0, botan_ec_group_unregister, (group_parameter_oid)); + TEST_FFI_RC(1, botan_ec_group_unregister, (oid_from_name)); + TEST_FFI_RC(0, botan_ec_group_unregister, (oid_from_name)); + + botan_ec_group_t unregistered_group; + TEST_FFI_RC( + BOTAN_FFI_ERROR_BAD_PARAMETER, botan_ec_group_from_oid, (&unregistered_group, group_parameter_oid)); + + botan_oid_destroy(group_parameter_oid); + botan_oid_destroy(oid_from_parameters); + + TEST_FFI_OK(botan_ec_group_destroy, (group_from_parameters)); + TEST_FFI_OK(botan_ec_group_destroy, (group_from_registered_oid)); + } + + botan_oid_destroy(oid_from_name); + botan_oid_destroy(group_oid); + botan_oid_destroy(oid_from_oid); + + const std::vector> parameters = {{p_from_name, p_from_oid}, + {a_from_name, a_from_oid}, + {b_from_name, b_from_oid}, + {g_x_from_name, g_x_from_oid}, + {g_y_from_name, g_y_from_oid}, + {order_from_name, order_from_oid}}; + + for(auto [x, y] : parameters) { + TEST_FFI_RC(1, botan_mp_equal, (x, y)); + botan_mp_destroy(x); + botan_mp_destroy(y); + } + + botan_ec_group_t secp384r1; + botan_ec_group_t secp384r1_with_seed; + + TEST_FFI_OK(botan_ec_group_from_name, (&secp384r1, "secp384r1")); + TEST_FFI_OK(botan_ec_group_from_pem, + (&secp384r1_with_seed, Test::read_data_file("x509/ecc/secp384r1_seed.pem").c_str())); + + botan_mp_t p; + botan_mp_t p_with_seed; + TEST_FFI_OK(botan_ec_group_get_p, (&p, secp384r1)); + TEST_FFI_OK(botan_ec_group_get_p, (&p_with_seed, secp384r1_with_seed)); + TEST_FFI_RC(1, botan_mp_equal, (p, p_with_seed)); + botan_mp_destroy(p); + botan_mp_destroy(p_with_seed); + + TEST_FFI_RC(0, botan_ec_group_equal, (group_from_name, secp384r1)); + TEST_FFI_RC(1, botan_ec_group_equal, (group_from_name, group_from_oid)); + + ViewBytesSink der_bytes; + TEST_FFI_OK(botan_ec_group_view_der, (group_from_name, der_bytes.delegate(), der_bytes.callback())); + botan_ec_group_t group_from_ber; + TEST_FFI_OK( + botan_ec_group_from_ber, + (&group_from_ber, reinterpret_cast(der_bytes.get().data()), der_bytes.get().size())); + + ViewStringSink pem_string; + TEST_FFI_OK(botan_ec_group_view_pem, (group_from_name, pem_string.delegate(), pem_string.callback())); + const std::string pem_actual = {pem_string.get().begin(), pem_string.get().end()}; + + botan_ec_group_t group_from_pem; + TEST_FFI_OK(botan_ec_group_from_pem, (&group_from_pem, pem_actual.c_str())); + + TEST_FFI_RC(1, botan_ec_group_equal, (group_from_name, group_from_ber)); + TEST_FFI_RC(1, botan_ec_group_equal, (group_from_name, group_from_pem)); + + botan_privkey_t priv; + TEST_FFI_OK(botan_ec_privkey_create, (&priv, "ECDSA", secp384r1, rng)); + std::array namebuf{}; + size_t name_len = namebuf.size(); + + TEST_FFI_OK(botan_privkey_algo_name, (priv, namebuf.data(), &name_len)); + result.test_str_eq("Key name is expected value", namebuf.data(), "ECDSA"); + + botan_ec_group_t group_from_key; + TEST_FFI_OK(botan_ec_privkey_get_group, (priv, &group_from_key)); + TEST_FFI_RC(1, botan_ec_group_equal, (group_from_key, secp384r1)); + + botan_ec_scalar_t private_value; + TEST_FFI_OK(botan_ec_privkey_get_private_key, (priv, &private_value)); + + botan_ec_scalar_destroy(private_value); + botan_privkey_destroy(priv); + + TEST_FFI_OK(botan_ec_group_destroy, (group_from_name)); + TEST_FFI_OK(botan_ec_group_destroy, (group_from_oid)); + TEST_FFI_OK(botan_ec_group_destroy, (secp384r1)); + TEST_FFI_OK(botan_ec_group_destroy, (secp384r1_with_seed)); + TEST_FFI_OK(botan_ec_group_destroy, (group_from_ber)); + TEST_FFI_OK(botan_ec_group_destroy, (group_from_pem)); + TEST_FFI_OK(botan_ec_group_destroy, (group_from_key)); + } + } + + private: + static void get_group_parameters(botan_ec_group_t ec_group, + botan_asn1_oid_t* oid, + botan_mp_t* p, + botan_mp_t* a, + botan_mp_t* b, + botan_mp_t* g_x, + botan_mp_t* g_y, + botan_mp_t* order, + Test::Result& result) { + TEST_FFI_OK(botan_ec_group_get_curve_oid, (oid, ec_group)); + TEST_FFI_OK(botan_ec_group_get_p, (p, ec_group)); + TEST_FFI_OK(botan_ec_group_get_a, (a, ec_group)); + TEST_FFI_OK(botan_ec_group_get_b, (b, ec_group)); + TEST_FFI_OK(botan_ec_group_get_g_x, (g_x, ec_group)); + TEST_FFI_OK(botan_ec_group_get_g_y, (g_y, ec_group)); + TEST_FFI_OK(botan_ec_group_get_order, (order, ec_group)); + } +}; + +class FFI_EC_Point_Test final : public FFI_Test { + public: + std::string name() const override { return "FFI Points and Scalars"; } + + void ffi_test(Test::Result& result, botan_rng_t rng) override { + botan_ec_group_t group; + + botan_ec_scalar_t random; + botan_mp_t to_scalar; + botan_ec_scalar_t from_mp; + botan_mp_t from_scalar; + + if(!Botan::EC_Group::supports_named_group("secp256r1")) { + result.test_note("Group needed for test not supported by this build configuration."); + return; + } + + TEST_FFI_OK(botan_mp_init, (&to_scalar)); + TEST_FFI_OK(botan_mp_set_from_str, (to_scalar, "12345")); + + TEST_FFI_OK(botan_ec_group_from_name, (&group, "secp256r1")); + TEST_FFI_OK(botan_ec_scalar_random, (&random, group, rng)); + TEST_FFI_OK(botan_ec_scalar_from_mp, (&from_mp, group, to_scalar)); + TEST_FFI_OK(botan_ec_scalar_to_mp, (from_mp, &from_scalar)); + TEST_FFI_RC(1, botan_mp_equal, (to_scalar, from_scalar)); + + TEST_FFI_OK(botan_ec_scalar_destroy, (random)); + TEST_FFI_OK(botan_ec_scalar_destroy, (from_mp)); + TEST_FFI_OK(botan_mp_destroy, (to_scalar)); + TEST_FFI_OK(botan_mp_destroy, (from_scalar)); + + botan_ec_point_t identity; + botan_ec_point_t generator; + botan_ec_point_t generator_neg; + botan_ec_point_t out_add_ident; + botan_ec_point_t out_add_inverse; + + TEST_FFI_OK(botan_ec_point_identity, (&identity, group)); + TEST_FFI_OK(botan_ec_point_generator, (&generator, group)); + TEST_FFI_OK(botan_ec_point_negate, (&generator_neg, generator)); + + TEST_FFI_OK(botan_ec_point_add, (&out_add_ident, generator, identity)); + TEST_FFI_OK(botan_ec_point_add, (&out_add_inverse, generator, generator_neg)); + + ViewBytesSink generator_bytes; + TEST_FFI_OK(botan_ec_point_view_xy_bytes, (generator, generator_bytes.delegate(), generator_bytes.callback())); + + ViewBytesSink gen_plus_ident_bytes; + TEST_FFI_OK(botan_ec_point_view_xy_bytes, + (out_add_ident, gen_plus_ident_bytes.delegate(), gen_plus_ident_bytes.callback())); + + result.test_bin_eq("generator == out_add_ident", generator_bytes.get(), gen_plus_ident_bytes.get()); + + TEST_FFI_RC(1, botan_ec_point_equal, (generator, out_add_ident)); + TEST_FFI_RC(1, botan_ec_point_equal, (identity, out_add_inverse)); + TEST_FFI_RC(1, botan_ec_point_is_identity, (out_add_inverse)); + + ViewBytesSink identity_bytes; + TEST_FFI_RC(BOTAN_FFI_ERROR_INVALID_OBJECT_STATE, + botan_ec_point_view_xy_bytes, + (identity, identity_bytes.delegate(), identity_bytes.callback())); + + botan_mp_t group_order; + TEST_FFI_OK(botan_ec_group_get_order, (&group_order, group)); + botan_mp_t group_order_minus_1; + TEST_FFI_OK(botan_mp_init, (&group_order_minus_1)); + + TEST_FFI_OK(botan_mp_sub_u32, (group_order_minus_1, group_order, 1)); + botan_ec_scalar_t order; + TEST_FFI_OK(botan_ec_scalar_from_mp, (&order, group, group_order_minus_1)); + + botan_ec_point_t out_mul_order_minus_one; + TEST_FFI_OK(botan_ec_point_mul, (&out_mul_order_minus_one, generator, order, rng)); + + botan_ec_point_t out_add_gen_to_order; + TEST_FFI_OK(botan_ec_point_add, (&out_add_gen_to_order, out_mul_order_minus_one, generator)); + + TEST_FFI_RC(1, botan_ec_point_is_identity, (out_add_gen_to_order)); + + TEST_FFI_OK(botan_mp_destroy, (group_order)); + TEST_FFI_OK(botan_mp_destroy, (group_order_minus_1)); + TEST_FFI_OK(botan_ec_scalar_destroy, (order)); + + TEST_FFI_OK(botan_ec_point_destroy, (identity)); + TEST_FFI_OK(botan_ec_point_destroy, (generator)); + TEST_FFI_OK(botan_ec_point_destroy, (generator_neg)); + TEST_FFI_OK(botan_ec_point_destroy, (out_add_ident)); + TEST_FFI_OK(botan_ec_point_destroy, (out_add_inverse)); + TEST_FFI_OK(botan_ec_point_destroy, (out_mul_order_minus_one)); + TEST_FFI_OK(botan_ec_point_destroy, (out_add_gen_to_order)); + TEST_FFI_OK(botan_ec_group_destroy, (group)); + } +}; + +class FFI_SRP6_Test final : public FFI_Test { + public: + std::string name() const override { return "FFI SRP6"; } + + bool skip_this_test() const override { + #if !defined(BOTAN_HAS_SRP6) + return true; + #else + return false; + #endif + } + + void ffi_test(Test::Result& result, botan_rng_t rng) override { + constexpr size_t group_bytes = 128; + const char* username = "alice"; + const char* password = "secret"; + const char* srp_group = "modp/srp/1024"; + const char* srp_hash = "SHA-256"; + const auto salt = Botan::hex_decode("beb25379d1a8581eb5a727673a2441ee"); + + std::array output{}; + + size_t group_size = 0; + TEST_FFI_OK(botan_srp6_group_size, (srp_group, &group_size)); + result.test_sz_eq("reported group size", group_size, group_bytes); + + size_t short_output_len = output.size() / 2; + TEST_FFI_RC( + BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, + botan_srp6_generate_verifier, + (username, password, salt.data(), salt.size(), srp_group, srp_hash, output.data(), &short_output_len)); + result.test_sz_eq("requested verifier length", short_output_len, group_bytes); + + size_t verifier_output_len = short_output_len; + TEST_FFI_OK( + botan_srp6_generate_verifier, + (username, password, salt.data(), salt.size(), srp_group, srp_hash, output.data(), &verifier_output_len)); + const auto verifier = std::vector(output.data(), output.data() + verifier_output_len); + + botan_srp6_server_session_t srp_server; + TEST_FFI_OK(botan_srp6_server_session_init, (&srp_server)); + + short_output_len = output.size() / 2; + TEST_FFI_RC( + BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, + botan_srp6_server_session_step1, + (srp_server, verifier.data(), verifier.size(), srp_group, srp_hash, rng, output.data(), &short_output_len)); + result.test_sz_eq("requested B_pub length", short_output_len, group_bytes); + + size_t pub_B_output_len = short_output_len; + TEST_FFI_OK( + botan_srp6_server_session_step1, + (srp_server, verifier.data(), verifier.size(), srp_group, srp_hash, rng, output.data(), &pub_B_output_len)); + const auto pub_B = std::vector(output.data(), output.data() + pub_B_output_len); + + std::array output2{}; + size_t short_output_len2 = output2.size() / 2; + short_output_len = output.size() / 2; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, + botan_srp6_client_agree, + (username, + password, + srp_group, + srp_hash, + salt.data(), + salt.size(), + pub_B.data(), + pub_B.size(), + rng, + output.data(), + &short_output_len, + output2.data(), + &short_output_len2)); + result.test_sz_eq("requested pub_A length", short_output_len, group_bytes); + result.test_sz_eq("requested K1 length", short_output_len2, group_bytes); + + size_t pub_A_output_len = short_output_len; + size_t K1_output_len = short_output_len2; + TEST_FFI_OK(botan_srp6_client_agree, + (username, + password, + srp_group, + srp_hash, + salt.data(), + salt.size(), + pub_B.data(), + pub_B.size(), + rng, + output.data(), + &pub_A_output_len, + output2.data(), + &K1_output_len)); + const auto pub_A = std::vector(output.data(), output.data() + pub_A_output_len); + const auto K1 = std::vector(output2.data(), output2.data() + K1_output_len); + + short_output_len = output.size() / 2; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, + botan_srp6_server_session_step2, + (srp_server, pub_A.data(), pub_A.size(), output.data(), &short_output_len)); + result.test_sz_eq("requested K2 length", short_output_len, group_bytes); + + size_t K2_output_len = short_output_len; + TEST_FFI_OK(botan_srp6_server_session_step2, + (srp_server, pub_A.data(), pub_A.size(), output.data(), &K2_output_len)); + const auto K2 = std::vector(output.data(), output.data() + K2_output_len); + + result.test_bin_eq("K1 == K2", K1, K2); + + TEST_FFI_OK(botan_srp6_server_session_destroy, (srp_server)); + } +}; + +// NOLINTEND(*-init-variables) + BOTAN_REGISTER_TEST("ffi", "ffi_utils", FFI_Utils_Test); BOTAN_REGISTER_TEST("ffi", "ffi_rng", FFI_RNG_Test); BOTAN_REGISTER_TEST("ffi", "ffi_rsa_cert", FFI_RSA_Cert_Test); @@ -4111,6 +5716,7 @@ BOTAN_REGISTER_TEST("ffi", "ffi_crl", FFI_CRL_Test); BOTAN_REGISTER_TEST("ffi", "ffi_cert_validation", FFI_Cert_Validation_Test); BOTAN_REGISTER_TEST("ffi", "ffi_ecdsa_certificate", FFI_ECDSA_Certificate_Test); +BOTAN_REGISTER_TEST("ffi", "ffi_cert_ext_keyusage", FFI_Cert_ExtKeyUsages_Test); BOTAN_REGISTER_TEST("ffi", "ffi_pkcs_hashid", FFI_PKCS_Hashid_Test); BOTAN_REGISTER_TEST("ffi", "ffi_cbc_cipher", FFI_CBC_Cipher_Test); BOTAN_REGISTER_TEST("ffi", "ffi_gcm", FFI_GCM_Test); @@ -4118,6 +5724,7 @@ BOTAN_REGISTER_TEST("ffi", "ffi_eax", FFI_EAX_Test); BOTAN_REGISTER_TEST("ffi", "ffi_aead", FFI_AEAD_Test); BOTAN_REGISTER_TEST("ffi", "ffi_streamcipher", FFI_StreamCipher_Test); +BOTAN_REGISTER_TEST("ffi", "ffi_xof", FFI_XOF_Test); BOTAN_REGISTER_TEST("ffi", "ffi_hashfunction", FFI_HashFunction_Test); BOTAN_REGISTER_TEST("ffi", "ffi_mac", FFI_MAC_Test); BOTAN_REGISTER_TEST("ffi", "ffi_scrypt", FFI_Scrypt_Test); @@ -4131,6 +5738,7 @@ BOTAN_REGISTER_TEST("ffi", "ffi_totp", FFI_TOTP_Test); BOTAN_REGISTER_TEST("ffi", "ffi_hotp", FFI_HOTP_Test); BOTAN_REGISTER_TEST("ffi", "ffi_keywrap", FFI_Keywrap_Test); +BOTAN_REGISTER_TEST("ffi", "ffi_xmss", FFI_XMSS_Test); BOTAN_REGISTER_TEST("ffi", "ffi_rsa", FFI_RSA_Test); BOTAN_REGISTER_TEST("ffi", "ffi_dsa", FFI_DSA_Test); BOTAN_REGISTER_TEST("ffi", "ffi_ecdsa", FFI_ECDSA_Test); @@ -4152,6 +5760,16 @@ BOTAN_REGISTER_TEST("ffi", "ffi_cmce", FFI_Classic_McEliece_Test); BOTAN_REGISTER_TEST("ffi", "ffi_elgamal", FFI_ElGamal_Test); BOTAN_REGISTER_TEST("ffi", "ffi_dh", FFI_DH_Test); +BOTAN_REGISTER_TEST("ffi", "ffi_oid", FFI_OID_Test); +BOTAN_REGISTER_TEST("ffi", "ffi_ec_group", FFI_EC_Group_Test); +BOTAN_REGISTER_TEST("ffi", "ffi_ec_points", FFI_EC_Point_Test); +BOTAN_REGISTER_TEST("ffi", "ffi_srp6", FFI_SRP6_Test); + + #if defined(BOTAN_HAS_X509) +BOTAN_REGISTER_TEST("ffi", "ffi_cert_alt_names", FFI_Cert_AlternativeNames_Test); +BOTAN_REGISTER_TEST("ffi", "ffi_cert_name_constraints", FFI_Cert_NameConstraints_Test); +BOTAN_REGISTER_TEST("ffi", "ffi_cert_aia", FFI_Cert_AuthorityInformationAccess_Test); + #endif #endif diff -Nru botan3-3.7.1+dfsg/src/tests/test_filters.cpp botan3-3.12.0+dfsg/src/tests/test_filters.cpp --- botan3-3.7.1+dfsg/src/tests/test_filters.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_filters.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,15 +7,16 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#define BOTAN_NO_DEPRECATED_WARNINGS - #include "tests.h" #if defined(BOTAN_HAS_FILTERS) #include #include + #include #include #include + #include + #include #endif #if defined(BOTAN_HAS_PIPE_UNIXFD_IO) @@ -29,8 +30,12 @@ namespace Botan_Tests { +namespace { + #if defined(BOTAN_HAS_FILTERS) +// NOLINTBEGIN(*-owning-memory) + class Filter_Tests final : public Test { public: std::vector run() override { @@ -63,29 +68,29 @@ try { Botan::SecureQueue queue_a; - result.test_eq("queue not attachable", queue_a.attachable(), false); + result.test_is_false("queue not attachable", queue_a.attachable()); std::vector test_data = {0x24, 0xB2, 0xBF, 0xC2, 0xE6, 0xD4, 0x7E, 0x04, 0x67, 0xB3}; queue_a.write(test_data.data(), test_data.size()); - result.test_eq("size of SecureQueue is correct", queue_a.size(), test_data.size()); - result.test_eq("0 bytes read so far from SecureQueue", queue_a.get_bytes_read(), 0); + result.test_sz_eq("size of SecureQueue is correct", queue_a.size(), test_data.size()); + result.test_sz_eq("0 bytes read so far from SecureQueue", queue_a.get_bytes_read(), 0); - uint8_t b; - result.test_eq("check_available", queue_a.check_available(1), true); - result.test_eq("check_available", queue_a.check_available(50), false); - size_t bytes_read = queue_a.read_byte(b); - result.test_eq("1 byte read", bytes_read, 1); + result.test_is_true("check_available", queue_a.check_available(1)); + result.test_is_false("check_available", queue_a.check_available(50)); + uint8_t b = 0; + const size_t bytes_read = queue_a.read_byte(b); + result.test_sz_eq("1 byte read", bytes_read, 1); Botan::secure_vector produced(b); Botan::secure_vector expected(test_data.at(0)); - result.test_eq("byte read is correct", produced, expected); + result.test_bin_eq("byte read is correct", produced, expected); - result.test_eq("1 bytes read so far from SecureQueue", queue_a.get_bytes_read(), 1); + result.test_sz_eq("1 bytes read so far from SecureQueue", queue_a.get_bytes_read(), 1); - Botan::SecureQueue queue_b; + const Botan::SecureQueue queue_b; queue_a = queue_b; - result.test_eq("bytes_read is set correctly", queue_a.get_bytes_read(), 0); + result.test_sz_eq("bytes_read is set correctly", queue_a.get_bytes_read(), 0); } catch(std::exception& e) { result.test_failure("SecureQueue", e.what()); } @@ -104,18 +109,18 @@ Botan::DataSource_Memory input_mem("65666768"); pipe.process_msg(input_mem); - result.test_eq("output string", oss.str(), "efgh"); + result.test_str_eq("output string", oss.str(), "efgh"); Botan::DataSource_Memory input_mem2("41414141"); pipe.process_msg(input_mem2); - result.test_eq("output string", oss.str(), "efghAAAA"); + result.test_str_eq("output string", oss.str(), "efghAAAA"); std::istringstream iss("4343"); Botan::DataSource_Stream input_strm(iss); pipe.process_msg(input_strm); - result.test_eq("output string", oss.str(), "efghAAAACC"); + result.test_str_eq("output string", oss.str(), "efghAAAACC"); #endif return result; } @@ -142,7 +147,7 @@ std::stringstream ss; ss << outfile_read.rdbuf(); - result.test_eq("output string", ss.str(), "efgh"); + result.test_str_eq("output string", ss.str(), "efgh"); // ensure files are closed outfile.close(); @@ -165,7 +170,7 @@ std::ostringstream oss; oss << pipe; - result.test_eq("output string", oss.str(), "41424344"); + result.test_str_eq("output string", oss.str(), "41424344"); std::istringstream iss("AAAA"); pipe.start_msg(); @@ -174,7 +179,7 @@ pipe.set_default_msg(1); oss << pipe; - result.test_eq("output string2", oss.str(), "4142434441414141"); + result.test_str_eq("output string2", oss.str(), "4142434441414141"); #endif return result; @@ -239,9 +244,8 @@ }); result.test_throws("pipe error", "Pipe::read: Invalid message number 100", [&]() { - uint8_t b; - size_t got = pipe.read(&b, 1, 100); - BOTAN_UNUSED(got); + uint8_t b = 0; + [[maybe_unused]] const size_t got = pipe.read(&b, 1, 100); }); pipe.append(nullptr); // ignored @@ -271,9 +275,9 @@ pipe.process_msg("Bye"); pipe.process_msg("Hi"); - result.test_eq("MAC 1", pipe.read_all_as_string(0), "e7NoVbtudgU0QiCZ"); - result.test_eq("MAC 2", pipe.read_all_as_string(1), "LhPnfEG+0rk+Ej6y"); - result.test_eq("MAC 3", pipe.read_all_as_string(2), "e7NoVbtudgU0QiCZ"); + result.test_str_eq("MAC 1", pipe.read_all_as_string(0), "e7NoVbtudgU0QiCZ"); + result.test_str_eq("MAC 2", pipe.read_all_as_string(1), "LhPnfEG+0rk+Ej6y"); + result.test_str_eq("MAC 3", pipe.read_all_as_string(2), "e7NoVbtudgU0QiCZ"); #endif return result; } @@ -288,36 +292,37 @@ pipe.append(new Botan::Hash_Filter("SHA-256")); - result.test_eq("Message count", pipe.message_count(), 0); + result.test_sz_eq("Message count", pipe.message_count(), 0); pipe.start_msg(); - uint8_t inb = 0x41; + const uint8_t inb = 0x41; pipe.write(&inb, 1); pipe.write(std::vector(6, 0x41)); pipe.write(inb); pipe.end_msg(); - result.test_eq("Message count", pipe.message_count(), 1); - result.test_eq("Message size", pipe.remaining(), 32); + result.test_sz_eq("Message count", pipe.message_count(), 1); + result.test_sz_eq("Message size", pipe.remaining(), 32); - std::vector out(32), last16(16); + std::vector out(32); + std::vector last16(16); - result.test_eq("Bytes read", pipe.get_bytes_read(0), 0); - result.test_eq("More to read", pipe.end_of_data(), false); - result.test_eq("Expected read count", pipe.read(&out[0], 5), 5); - result.test_eq("Bytes read", pipe.get_bytes_read(0), 5); - result.test_eq("Peek read", pipe.peek(last16.data(), 18, 11), 16); - result.test_eq("Expected read count", pipe.read(&out[5], 17), 17); - result.test_eq("Bytes read", pipe.get_bytes_read(0), 22); - result.test_eq("Remaining", pipe.remaining(), 10); - result.test_eq("Remaining", pipe.remaining(), 10); - result.test_eq("Expected read count", pipe.read(&out[22], 12), 10); - result.test_eq("Expected read count", pipe.read(&out[0], 1), 0); // no more output - result.test_eq("Bytes read", pipe.get_bytes_read(0), 32); - result.test_eq("No more to read", pipe.end_of_data(), true); + result.test_sz_eq("Bytes read", pipe.get_bytes_read(0), 0); + result.test_is_false("More to read", pipe.end_of_data()); + result.test_sz_eq("Expected read count", pipe.read(out.data(), 5), 5); + result.test_sz_eq("Bytes read", pipe.get_bytes_read(0), 5); + result.test_sz_eq("Peek read", pipe.peek(last16.data(), 18, 11), 16); + result.test_sz_eq("Expected read count", pipe.read(&out[5], 17), 17); + result.test_sz_eq("Bytes read", pipe.get_bytes_read(0), 22); + result.test_sz_eq("Remaining", pipe.remaining(), 10); + result.test_sz_eq("Remaining", pipe.remaining(), 10); + result.test_sz_eq("Expected read count", pipe.read(&out[22], 12), 10); + result.test_sz_eq("Expected read count", pipe.read(out.data(), 1), 0); // no more output + result.test_sz_eq("Bytes read", pipe.get_bytes_read(0), 32); + result.test_is_true("No more to read", pipe.end_of_data()); - result.test_eq("Expected output", out, "C34AB6ABB7B2BB595BC25C3B388C872FD1D575819A8F55CC689510285E212385"); - result.test_eq("Expected last16", last16, "D1D575819A8F55CC689510285E212385"); + result.test_bin_eq("Expected output", out, "C34AB6ABB7B2BB595BC25C3B388C872FD1D575819A8F55CC689510285E212385"); + result.test_bin_eq("Expected last16", last16, "D1D575819A8F55CC689510285E212385"); pipe.reset(); @@ -325,7 +330,7 @@ pipe.prepend(new Botan::Hash_Filter("CRC32")); pipe.append(new Botan::Hash_Filter("CRC32")); pipe.process_msg(std::vector(1024, 0)); - result.test_eq("Expected CRC32d", pipe.read_all(1), "99841F60"); + result.test_bin_eq("Expected CRC32d", pipe.read_all(1), "99841F60"); #endif #endif return result; @@ -376,15 +381,15 @@ dec_pipe.process_msg(cfb_expected[i - 1]); } - result.test_eq("enc pipe msg count", enc_pipe.message_count(), sizeof(msg_bits) - 1); - result.test_eq("dec pipe msg count", dec_pipe.message_count(), sizeof(msg_bits) - 1); + result.test_sz_eq("enc pipe msg count", enc_pipe.message_count(), sizeof(msg_bits) - 1); + result.test_sz_eq("dec pipe msg count", dec_pipe.message_count(), sizeof(msg_bits) - 1); for(size_t i = 0; i != enc_pipe.message_count(); ++i) { - result.test_eq("encrypt", enc_pipe.read_all_as_string(i), cfb_expected[i]); + result.test_str_eq("encrypt", enc_pipe.read_all_as_string(i), cfb_expected[i]); } for(size_t i = 0; i != dec_pipe.message_count(); ++i) { - result.test_eq("decrypt", dec_pipe.read_all_as_string(i), Botan::hex_encode(msg_bits, i + 1)); + result.test_str_eq("decrypt", dec_pipe.read_all_as_string(i), Botan::hex_encode(msg_bits, i + 1)); } #endif @@ -398,13 +403,13 @@ Botan::Cipher_Mode_Filter* cipher = new Botan::Cipher_Mode_Filter( Botan::Cipher_Mode::create("AES-128/CBC/PKCS7", Botan::Cipher_Dir::Encryption)); - result.test_eq("Cipher filter name", cipher->name(), "AES-128/CBC/PKCS7"); + result.test_str_eq("Cipher filter name", cipher->name(), "AES-128/CBC/PKCS7"); - result.test_eq("Cipher filter nonce size", cipher->valid_iv_length(16), true); - result.test_eq("Cipher filter nonce size", cipher->valid_iv_length(17), false); + result.test_is_true("Cipher filter nonce size", cipher->valid_iv_length(16)); + result.test_is_false("Cipher filter nonce size", cipher->valid_iv_length(17)); - result.test_eq("Cipher key length max", cipher->key_spec().maximum_keylength(), 16); - result.test_eq("Cipher key length min", cipher->key_spec().minimum_keylength(), 16); + result.test_sz_eq("Cipher key length max", cipher->key_spec().maximum_keylength(), 16); + result.test_sz_eq("Cipher key length min", cipher->key_spec().minimum_keylength(), 16); // takes ownership of cipher Botan::Pipe pipe(cipher); @@ -414,12 +419,13 @@ pipe.process_msg("Don't use plain CBC mode"); - result.test_eq("Message count", pipe.message_count(), 1); - result.test_eq("Bytes read", pipe.get_bytes_read(), 0); + result.test_sz_eq("Message count", pipe.message_count(), 1); + result.test_sz_eq("Bytes read", pipe.get_bytes_read(), 0); auto ciphertext = pipe.read_all(); - result.test_eq("Bytes read after", pipe.get_bytes_read(), ciphertext.size()); + result.test_sz_eq("Bytes read after", pipe.get_bytes_read(), ciphertext.size()); - result.test_eq("Ciphertext", ciphertext, "9BDD7300E0CB61CA71FFF957A71605DB 6836159C36781246A1ADF50982757F4B"); + result.test_bin_eq( + "Ciphertext", ciphertext, "9BDD7300E0CB61CA71FFF957A71605DB 6836159C36781246A1ADF50982757F4B"); pipe.process_msg("IV carryover"); auto ciphertext2 = pipe.read_all(1); @@ -427,8 +433,8 @@ auto ciphertext3 = pipe.read_all(2); // These values tested against PyCrypto - result.test_eq("Ciphertext2", ciphertext2, "AA8D682958A4A044735DAC502B274DB2"); - result.test_eq("Ciphertext3", ciphertext3, "1241B9976F73051BCF809525D6E86C25"); + result.test_bin_eq("Ciphertext2", ciphertext2, "AA8D682958A4A044735DAC502B274DB2"); + result.test_bin_eq("Ciphertext3", ciphertext3, "1241B9976F73051BCF809525D6E86C25"); Botan::Cipher_Mode_Filter* dec_cipher = new Botan::Cipher_Mode_Filter( Botan::Cipher_Mode::create("AES-128/CBC/PKCS7", Botan::Cipher_Dir::Decryption)); @@ -446,11 +452,11 @@ pipe.end_msg(); pipe.set_default_msg(3); - result.test_eq("Bytes read", pipe.get_bytes_read(), 0); + result.test_sz_eq("Bytes read", pipe.get_bytes_read(), 0); Botan::secure_vector zeros_out = pipe.read_all(); - result.test_eq("Bytes read", pipe.get_bytes_read(), zeros_out.size()); + result.test_sz_eq("Bytes read", pipe.get_bytes_read(), zeros_out.size()); - result.test_eq("Cipher roundtrip", zeros_in, zeros_out); + result.test_bin_eq("Cipher roundtrip", zeros_in, zeros_out); #endif return result; } @@ -462,7 +468,7 @@ auto comp_f = std::make_unique("zlib", 9); - result.test_eq("Compressor filter name", comp_f->name(), "Zlib_Compression"); + result.test_str_eq("Compressor filter name", comp_f->name(), "Zlib_Compression"); Botan::Pipe pipe(comp_f.release()); const std::string input_str = "Hello there HELLO there I said is this thing on?"; @@ -473,17 +479,17 @@ auto compr = pipe.read_all(0); // Can't do equality check on compression because output may differ - result.test_lt("Compressed is shorter", compr.size(), input_str.size()); + result.test_sz_lt("Compressed is shorter", compr.size(), input_str.size()); auto decomp_f = std::make_unique("zlib"); - result.test_eq("Decompressor name", decomp_f->name(), "Zlib_Decompression"); + result.test_str_eq("Decompressor name", decomp_f->name(), "Zlib_Decompression"); pipe.append(decomp_f.release()); pipe.pop(); // remove compressor pipe.process_msg(compr); - std::string decomp = pipe.read_all_as_string(1); - result.test_eq("Decompressed ok", decomp, input_str); + const std::string decomp = pipe.read_all_as_string(1); + result.test_str_eq("Decompressed ok", decomp, input_str); #endif return result; @@ -496,7 +502,7 @@ auto comp_f = std::make_unique("bzip2", 9); - result.test_eq("Compressor filter name", comp_f->name(), "Bzip2_Compression"); + result.test_str_eq("Compressor filter name", comp_f->name(), "Bzip2_Compression"); Botan::Pipe pipe(comp_f.release()); const std::string input_str = "foo\n"; @@ -509,14 +515,14 @@ // Here the output is actually longer than the input as input is so short auto decomp_f = std::make_unique("bzip2"); - result.test_eq("Decompressor name", decomp_f->name(), "Bzip2_Decompression"); + result.test_str_eq("Decompressor name", decomp_f->name(), "Bzip2_Decompression"); pipe.append(decomp_f.release()); pipe.pop(); // remove compressor pipe.process_msg(compr); - std::string decomp = pipe.read_all_as_string(1); - result.test_eq("Decompressed ok", decomp, input_str); + const std::string decomp = pipe.read_all_as_string(1); + result.test_str_eq("Decompressed ok", decomp, input_str); #endif return result; @@ -528,21 +534,21 @@ #if defined(BOTAN_HAS_CODEC_FILTERS) Botan::Pipe pipe(new Botan::Base64_Encoder); - result.test_eq("Message count", pipe.message_count(), 0); + result.test_sz_eq("Message count", pipe.message_count(), 0); pipe.process_msg("ABCDX"); - result.test_eq("Message count", pipe.message_count(), 1); - result.test_eq("Message size", pipe.remaining(), 8); + result.test_sz_eq("Message count", pipe.message_count(), 1); + result.test_sz_eq("Message size", pipe.remaining(), 8); - std::string output = pipe.read_all_as_string(0); - result.test_eq("Message size", pipe.remaining(0), 0); - result.test_eq("Output round tripped", output, "QUJDRFg="); + const std::string output = pipe.read_all_as_string(0); + result.test_sz_eq("Message size", pipe.remaining(0), 0); + result.test_str_eq("Output round tripped", output, "QUJDRFg="); pipe.append(new Botan::Base64_Decoder); pipe.process_msg("FOOBAZ"); - result.test_eq("base64 roundtrip", pipe.read_all_as_string(1), "FOOBAZ"); + result.test_str_eq("base64 roundtrip", pipe.read_all_as_string(1), "FOOBAZ"); pipe.pop(); pipe.pop(); @@ -551,49 +557,49 @@ pipe.process_msg("surprise plaintext"); pipe.set_default_msg(2); - result.test_eq("Message 2", pipe.read_all_as_string(), "surprise plaintext"); + result.test_str_eq("Message 2", pipe.read_all_as_string(), "surprise plaintext"); pipe.append(new Botan::Hex_Decoder); pipe.process_msg("F331F00D"); Botan::secure_vector bin = pipe.read_all(3); - result.test_eq("hex decoded", bin, "F331F00D"); + result.test_bin_eq("hex decoded", bin, "F331F00D"); pipe.append(new Botan::Hex_Encoder); pipe.process_msg("F331F00D"); - result.test_eq("hex roundtrip", pipe.read_all_as_string(4), "F331F00D"); + result.test_str_eq("hex roundtrip", pipe.read_all_as_string(4), "F331F00D"); // Now tests with line wrapping enabled pipe.reset(); pipe.append(new Botan::Hex_Decoder); - pipe.append(new Botan::Base64_Encoder(/*break_lines=*/true, + pipe.append(new Botan::Base64_Encoder(/*line_breaks=*/true, /*line_length=*/4, /*trailing_newline=*/true)); pipe.process_msg("6dab1eeb8a2eb69bad"); - result.test_eq( + result.test_str_eq( "base64 with linebreaks and trailing newline", pipe.read_all_as_string(5), "base\n64ou\ntput\n\n"); pipe.reset(); pipe.append(new Botan::Hex_Decoder); pipe.append(new Botan::Base64_Encoder(true, 5, false)); pipe.process_msg("6dab1eeb8a2eb69bad"); - result.test_eq("base64 with linebreaks", pipe.read_all_as_string(6), "base6\n4outp\nut\n"); + result.test_str_eq("base64 with linebreaks", pipe.read_all_as_string(6), "base6\n4outp\nut\n"); pipe.reset(); pipe.append(new Botan::Hex_Encoder(true, 13, Botan::Hex_Encoder::Uppercase)); pipe.process_msg("hex encoding this string"); - result.test_eq("hex uppercase with linebreaks", - pipe.read_all_as_string(7), - "68657820656E6\n36F64696E6720\n7468697320737\n472696E67\n"); + result.test_str_eq("hex uppercase with linebreaks", + pipe.read_all_as_string(7), + "68657820656E6\n36F64696E6720\n7468697320737\n472696E67\n"); pipe.reset(); pipe.append(new Botan::Hex_Encoder(true, 16, Botan::Hex_Encoder::Lowercase)); pipe.process_msg("hex encoding this string"); - result.test_eq("hex lowercase with linebreaks", - pipe.read_all_as_string(8), - "68657820656e636f\n64696e6720746869\n7320737472696e67\n"); + result.test_str_eq("hex lowercase with linebreaks", + pipe.read_all_as_string(8), + "68657820656e636f\n64696e6720746869\n7320737472696e67\n"); #endif return result; @@ -614,11 +620,11 @@ pipe.process_msg("ABCDEF"); - result.test_eq("Message count", pipe.message_count(), 1); - result.test_eq("Ciphertext", pipe.read_all(), "FDFD6238F7C6"); + result.test_sz_eq("Message count", pipe.message_count(), 1); + result.test_bin_eq("Ciphertext", pipe.read_all(), "FDFD6238F7C6"); pipe.process_msg("ABCDEF"); - result.test_eq("Ciphertext", pipe.read_all(1), "8E72F1153514"); + result.test_bin_eq("Ciphertext", pipe.read_all(1), "8E72F1153514"); #endif return result; } @@ -629,13 +635,15 @@ #if defined(BOTAN_HAS_SHA2_32) && defined(BOTAN_HAS_SHA2_64) Botan::Pipe pipe(new Botan::Fork(new Botan::Hash_Filter("SHA-256"), new Botan::Hash_Filter("SHA-512-256"))); - result.test_eq("Message count", pipe.message_count(), 0); + result.test_sz_eq("Message count", pipe.message_count(), 0); pipe.process_msg("OMG"); - result.test_eq("Message count", pipe.message_count(), 2); + result.test_sz_eq("Message count", pipe.message_count(), 2); // Test reading out of order - result.test_eq("Hash 2", pipe.read_all(1), "610480FFA82F24F6926544B976FE387878E3D973C03DFD591C2E9896EFB903E0"); - result.test_eq("Hash 1", pipe.read_all(0), "C00862D1C6C1CF7C1B49388306E7B3C1BB79D8D6EC978B41035B556DBB3797DF"); + result.test_bin_eq( + "Hash 2", pipe.read_all(1), "610480FFA82F24F6926544B976FE387878E3D973C03DFD591C2E9896EFB903E0"); + result.test_bin_eq( + "Hash 1", pipe.read_all(0), "C00862D1C6C1CF7C1B49388306E7B3C1BB79D8D6EC978B41035B556DBB3797DF"); #endif return result; } @@ -645,25 +653,26 @@ #if defined(BOTAN_HAS_CODEC_FILTERS) && defined(BOTAN_HAS_SHA2_32) && defined(BOTAN_HAS_SHA2_64) + // NOLINTNEXTLINE(*-const-correctness) bug in clang-tidy Botan::Filter* filters[2] = {new Botan::Hash_Filter("SHA-256"), new Botan::Hex_Encoder}; auto chain = std::make_unique(filters, 2); - result.test_eq("Chain has a name", chain->name(), "Chain"); + result.test_str_eq("Chain has a name", chain->name(), "Chain"); auto fork = std::make_unique( chain.release(), new Botan::Chain(new Botan::Hash_Filter("SHA-512-256", 19), new Botan::Hex_Encoder)); - result.test_eq("Fork has a name", fork->name(), "Fork"); + result.test_str_eq("Fork has a name", fork->name(), "Fork"); Botan::Pipe pipe(fork.release()); - result.test_eq("Message count", pipe.message_count(), 0); + result.test_sz_eq("Message count", pipe.message_count(), 0); pipe.process_msg("OMG"); - result.test_eq("Message count", pipe.message_count(), 2); + result.test_sz_eq("Message count", pipe.message_count(), 2); - result.test_eq( + result.test_str_eq( "Hash 1", pipe.read_all_as_string(0), "C00862D1C6C1CF7C1B49388306E7B3C1BB79D8D6EC978B41035B556DBB3797DF"); - result.test_eq("Hash 2", pipe.read_all_as_string(1), "610480FFA82F24F6926544B976FE387878E3D9"); + result.test_str_eq("Hash 2", pipe.read_all_as_string(1), "610480FFA82F24F6926544B976FE387878E3D9"); #endif return result; @@ -672,7 +681,7 @@ static Test::Result test_pipe_fd_io() { Test::Result result("Pipe file descriptor IO"); - #if defined(BOTAN_HAS_PIPE_UNIXFD_IO) && defined(BOTAN_HAS_CODEC_FILTERS) + #if defined(BOTAN_HAS_PIPE_UNIXFD_IO) && defined(BOTAN_HAS_CODEC_FILTERS) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN) int fd[2]; if(::pipe(fd) != 0) { return result; // pipe unavailable? @@ -690,9 +699,9 @@ hex_dec.end_msg(); ::close(fd[0]); - std::string dec = hex_dec.read_all_as_string(); + const std::string dec = hex_dec.read_all_as_string(); - result.test_eq("IO through Unix pipe works", dec, "hi chappy"); + result.test_str_eq("IO through Unix pipe works", dec, "hi chappy"); #endif return result; @@ -704,36 +713,36 @@ #if defined(BOTAN_HAS_THREAD_UTILS) && defined(BOTAN_HAS_CODEC_FILTERS) && defined(BOTAN_HAS_SHA2_32) Botan::Pipe pipe(new Botan::Threaded_Fork(new Botan::Hex_Encoder, new Botan::Base64_Encoder)); - result.test_eq("Message count", pipe.message_count(), 0); + result.test_sz_eq("Message count", pipe.message_count(), 0); pipe.process_msg("woo"); - result.test_eq("Message count", pipe.message_count(), 2); + result.test_sz_eq("Message count", pipe.message_count(), 2); // Test reading out of order - result.test_eq("Hash 2", pipe.read_all_as_string(1), "d29v"); - result.test_eq("Hash 1", pipe.read_all_as_string(0), "776F6F"); + result.test_str_eq("Hash 2", pipe.read_all_as_string(1), "d29v"); + result.test_str_eq("Hash 1", pipe.read_all_as_string(0), "776F6F"); pipe.reset(); const size_t filter_count = 5; Botan::Filter* filters[filter_count]; - for(size_t i = 0; i != filter_count; ++i) { - filters[i] = new Botan::Hash_Filter("SHA-256"); + for(auto& filter : filters) { + filter = new Botan::Hash_Filter("SHA-256"); } pipe.append(new Botan::Threaded_Fork(filters, filter_count)); - result.test_eq("Message count before start_msg", pipe.message_count(), 2); + result.test_sz_eq("Message count before start_msg", pipe.message_count(), 2); pipe.start_msg(); for(size_t i = 0; i != 919; ++i) { - std::vector input(i + 5, static_cast(i)); + const std::vector input(i + 5, static_cast(i)); pipe.write(input); } pipe.end_msg(); - result.test_eq("Message count after end_msg", pipe.message_count(), 2 + filter_count); + result.test_sz_eq("Message count after end_msg", pipe.message_count(), 2 + filter_count); for(size_t i = 0; i != filter_count; ++i) { - result.test_eq( + result.test_bin_eq( "Output", pipe.read_all(2 + i), "327AD8055223F5926693D8BEA40F7B35BDEEB535647DFB93F464E40EA01939A9"); } #endif @@ -741,8 +750,12 @@ } }; +// NOLINTEND(*-owning-memory) + BOTAN_REGISTER_TEST("filters", "filter", Filter_Tests); #endif +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_fpe.cpp botan3-3.12.0+dfsg/src/tests/test_fpe.cpp --- botan3-3.7.1+dfsg/src/tests/test_fpe.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_fpe.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,6 +12,8 @@ namespace Botan_Tests { +namespace { + #if defined(BOTAN_HAS_FPE_FE1) class FPE_FE1_Tests final : public Text_Based_Test { @@ -29,11 +31,11 @@ const Botan::BigInt got = Botan::FPE::fe1_encrypt(modulus, input, key, tweak); - result.test_eq("ciphertext", got, expected); + result.test_bn_eq("ciphertext", got, expected); const Botan::BigInt decry = Botan::FPE::fe1_decrypt(modulus, got, key, tweak); - result.test_eq("decrypted", decry, input); + result.test_bn_eq("decrypted", decry, input); return result; } @@ -43,4 +45,6 @@ #endif +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_frodokem.cpp botan3-3.12.0+dfsg/src/tests/test_frodokem.cpp --- botan3-3.7.1+dfsg/src/tests/test_frodokem.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_frodokem.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,7 +1,7 @@ /* * Tests for FrodoKEM ("You SHALL Pass") * - KAT tests using the KAT vectors from - * https://github.com/microsoft/PQCrypto-LWEKE/tree/master/KAT + * https://github.com/microsoft/PQCrypto-LWEKE/tree/master * * (C) 2023 Jack Lloyd * (C) 2023 René Meusel and Amos Treiber, Rohde & Schwarz Cybersecurity @@ -9,20 +9,18 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include "test_pubkey_pqc.h" -#include "test_rng.h" #include "tests.h" -#include #include #if defined(BOTAN_HAS_FRODOKEM) #include "test_pubkey.h" + #include "test_pubkey_pqc.h" + #include "test_rng.h" #include #include #include - #include #include #endif @@ -40,7 +38,9 @@ bool is_available(const std::string& mode) const final { return get_mode(mode).is_available(); } - std::vector map_value(const std::string&, std::span value, VarType var_type) const final { + std::vector map_value(const std::string& /*params*/, + std::span value, + VarType var_type) const final { if(var_type == VarType::SharedSecret) { return {value.begin(), value.end()}; } @@ -50,13 +50,13 @@ } Fixed_Output_RNG rng_for_keygen(const std::string& mode, Botan::RandomNumberGenerator& rng) const final { - Botan::FrodoKEMConstants consts(get_mode(mode)); - return Fixed_Output_RNG(rng, consts.len_sec_bytes() + consts.len_se_bytes() + consts.len_a_bytes()); + const Botan::FrodoKEMConstants constants(get_mode(mode)); + return Fixed_Output_RNG(rng, constants.len_sec_bytes() + constants.len_se_bytes() + constants.len_a_bytes()); } Fixed_Output_RNG rng_for_encapsulation(const std::string& mode, Botan::RandomNumberGenerator& rng) const final { - Botan::FrodoKEMConstants consts(get_mode(mode)); - return Fixed_Output_RNG(rng, consts.len_sec_bytes() + consts.len_salt_bytes()); + const Botan::FrodoKEMConstants constants(get_mode(mode)); + return Fixed_Output_RNG(rng, constants.len_sec_bytes() + constants.len_salt_bytes()); } }; @@ -76,58 +76,59 @@ Botan::FrodoKEMMode::FrodoKEM976_AES, Botan::FrodoKEMMode::FrodoKEM640_AES}; - auto get_decryption_error_value = [](Botan::FrodoKEMConstants& consts, + auto get_decryption_error_value = [](const Botan::FrodoKEMConstants& constants, std::span encaps_value, const Botan::FrodoKEM_PrivateKey& sk) { // Extracts the `S` value from the encoded private key - auto& shake = consts.SHAKE_XOF(); + auto shake = constants.create_xof(); const auto sk_bytes = sk.raw_private_key_bits(); - auto sk_s = std::span(sk_bytes.data(), consts.len_sec_bytes()); - shake.update(encaps_value); - shake.update(sk_s); - return shake.output(consts.len_sec_bytes()); + auto sk_s = std::span(sk_bytes.data(), constants.len_sec_bytes()); + shake->update(encaps_value); + shake->update(sk_s); + return shake->output(constants.len_sec_bytes()); }; std::vector results; for(auto mode : modes) { - Botan::FrodoKEMMode m(mode); + const Botan::FrodoKEMMode m(mode); if(!m.is_available()) { continue; } - Botan::FrodoKEMConstants consts(mode); + const Botan::FrodoKEMConstants constants(mode); Test::Result& result = results.emplace_back("FrodoKEM roundtrip: " + m.to_string()); - Botan::FrodoKEM_PrivateKey sk1(*rng, mode); - Botan::FrodoKEM_PublicKey pk1(sk1.public_key_bits(), mode); + const Botan::FrodoKEM_PrivateKey sk1(*rng, mode); + const 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 */); - 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()); + result.test_sz_eq("length of shared secret", enc_res.shared_key().size(), enc1.shared_key_length(0)); + result.test_sz_eq( + "length of ciphertext", enc_res.encapsulated_shared_key().size(), enc1.encapsulated_key_length()); 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)); + result.test_bin_eq("shared secrets match", ss, enc_res.shared_key()); + result.test_sz_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); + const Botan::FrodoKEM_PrivateKey sk2(*rng, mode); // Decryption failure: mismatching private key 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)); + result.test_bin_eq("decryption failure sk", + ss_mismatch, + get_decryption_error_value(constants, 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(), *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)); + result.test_bin_eq( + "decryption failure bitflip", ss_mismatch, get_decryption_error_value(constants, mutated_encaps_value, sk2)); // Decryption failure: malformed encapsulation value result.test_throws( diff -Nru botan3-3.7.1+dfsg/src/tests/test_gf2m.cpp botan3-3.12.0+dfsg/src/tests/test_gf2m.cpp --- botan3-3.7.1+dfsg/src/tests/test_gf2m.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_gf2m.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -35,14 +35,12 @@ * occurred. It should be extended to test more of the interface. */ for(size_t degree = 2; degree <= 15; ++degree) { - Botan::GF2m_Field field(degree); + const Botan::GF2m_Field field(degree); for(size_t i = 0; i <= field.gf_ord(); ++i) { - Botan::gf2m a = static_cast(i); + const Botan::gf2m a = static_cast(i); - result.test_eq("square vs multiply", - static_cast(field.gf_square(a)), - static_cast(field.gf_mul(a, a))); + result.test_u16_eq("square vs multiply", field.gf_square(a), field.gf_mul(a, a)); /* * This sequence is from the start of gf2m_decomp_rootfind_state::calc_Fxj_j_neq_0 @@ -51,9 +49,9 @@ const Botan::gf2m jl_gray = field.gf_l_from_n(a); const Botan::gf2m xl_j_tt_5 = field.gf_square_rr(jl_gray); const Botan::gf2m xl_gray_tt_3 = field.gf_mul_rrr(xl_j_tt_5, jl_gray); - Botan::gf2m s = field.gf_mul_nrr(xl_gray_tt_3, field.gf_ord()); + const Botan::gf2m s = field.gf_mul_nrr(xl_gray_tt_3, field.gf_ord()); - result.test_gte("Value less than order", field.gf_ord(), s); + result.test_sz_gte("Value less than order", field.gf_ord(), s); } } } diff -Nru botan3-3.7.1+dfsg/src/tests/test_gost_3410.cpp botan3-3.12.0+dfsg/src/tests/test_gost_3410.cpp --- botan3-3.7.1+dfsg/src/tests/test_gost_3410.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_gost_3410.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,8 @@ #if defined(BOTAN_HAS_GOST_34_10_2001) #include "test_pubkey.h" + #include "test_rng.h" + #include #include #endif @@ -23,7 +25,7 @@ PK_Signature_Verification_Test( "GOST 34.10-2001", "pubkey/gost_3410_verify.vec", "P,A,B,Gx,Gy,Oid,Order,Px,Py,Hash,Msg,Signature") {} - bool skip_this_test(const std::string&, const VarMap&) override { + bool skip_this_test(const std::string& /*header*/, const VarMap& /*vars*/) override { return !Botan::EC_Group::supports_application_specific_group(); } @@ -36,14 +38,16 @@ const BigInt order = vars.get_req_bn("Order"); const Botan::OID oid(vars.get_req_str("Oid")); - Botan::EC_Group group(p, a, b, Gx, Gy, order, BigInt::one(), oid); + const Botan::EC_Group group(p, a, b, Gx, Gy, order, BigInt::one(), oid); const BigInt Px = vars.get_req_bn("Px"); const BigInt Py = vars.get_req_bn("Py"); - const auto public_point = Botan::EC_AffinePoint::from_bigint_xy(group, Px, Py).value(); - - return std::make_unique(group, public_point); + if(const auto public_point = Botan::EC_AffinePoint::from_bigint_xy(group, Px, Py)) { + return std::make_unique(group, *public_point); + } else { + throw Test_Error("Failed to load GOST 34.10 public key, invalid x/y coordinates"); + } } std::string default_padding(const VarMap& vars) const override { return vars.get_req_str("Hash"); } @@ -55,7 +59,7 @@ PK_Signature_Generation_Test( "GOST 34.10-2001", "pubkey/gost_3410_sign.vec", "P,A,B,Gx,Gy,Oid,Order,X,Hash,Nonce,Msg,Signature") {} - bool skip_this_test(const std::string&, const VarMap&) override { + bool skip_this_test(const std::string& /*header*/, const VarMap& /*vars*/) override { return !Botan::EC_Group::supports_application_specific_group(); } @@ -68,7 +72,7 @@ const BigInt order = vars.get_req_bn("Order"); const Botan::OID oid(vars.get_req_str("Oid")); - Botan::EC_Group group(p, a, b, Gx, Gy, order, BigInt::one(), oid); + const Botan::EC_Group group(p, a, b, Gx, Gy, order, BigInt::one(), oid); const BigInt x = vars.get_req_bn("X"); diff -Nru botan3-3.7.1+dfsg/src/tests/test_hash.cpp botan3-3.12.0+dfsg/src/tests/test_hash.cpp --- botan3-3.7.1+dfsg/src/tests/test_hash.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_hash.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,7 +7,9 @@ #include "tests.h" #if defined(BOTAN_HAS_HASH) + #include #include + #include #include #endif @@ -39,11 +41,11 @@ } catch(Botan::Invalid_Argument& e) { const std::string msg = e.what(); const std::string full_msg = "" + expected_msg; - result.test_eq("expected error message", msg, full_msg); + result.test_str_eq("expected error message", msg, full_msg); } catch(Botan::Lookup_Error& e) { const std::string algo_not_found_msg = "Unavailable Hash " + name; const std::string msg = e.what(); - result.test_eq("expected error message", msg, algo_not_found_msg); + result.test_str_eq("expected error message", msg, algo_not_found_msg); } catch(std::exception& e) { result.test_failure("some unknown exception", e.what()); } catch(...) { @@ -86,17 +88,17 @@ auto clone = hash->new_object(); const std::string provider(hash->provider()); - result.test_is_nonempty("provider", provider); - result.test_eq(provider, hash->name(), algo); - result.test_eq(provider, hash->name(), clone->name()); + result.test_str_not_empty("provider", provider); + result.test_str_eq(provider, hash->name(), algo); + result.test_str_eq(provider, hash->name(), clone->name()); for(size_t i = 0; i != 3; ++i) { hash->update(input); - result.test_eq(provider, "hashing", hash->final(), expected); + result.test_bin_eq(provider + " hashing", hash->final(), expected); } clone->update(input); - result.test_eq(provider, "hashing (clone)", clone->final(), expected); + result.test_bin_eq(provider + " hashing (clone)", clone->final(), expected); // Test to make sure clear() resets what we need it to hash->update("some discarded input"); @@ -104,7 +106,7 @@ hash->update(nullptr, 0); // this should be effectively ignored hash->update(input); - result.test_eq(provider, "hashing after clear", hash->final(), expected); + result.test_bin_eq(provider + " hashing after clear", hash->final(), expected); // Test that misaligned inputs work @@ -119,7 +121,7 @@ } hash->update(&misaligned[bytes_to_misalign], input.size()); - result.test_eq(provider, "hashing misaligned data", hash->final(), expected); + result.test_bin_eq(provider + " hashing misaligned data", hash->final(), expected); } if(input.size() > 5) { @@ -140,15 +142,15 @@ hash->update(&input[so_far], take); so_far += take; } - result.test_eq(provider, "hashing split", hash->final(), expected); + result.test_bin_eq(provider + " hashing split", hash->final(), expected); fork->update(&input[input.size() - 1], 1); - result.test_eq(provider, "hashing split", fork->final(), expected); + result.test_bin_eq(provider + " hashing split", fork->final(), expected); } if(hash->hash_block_size() > 0) { // GOST-34.11 uses 32 byte block - result.test_gte("If hash_block_size is set, it is large", hash->hash_block_size(), 32); + result.test_sz_gte("If hash_block_size is set, it is large", hash->hash_block_size(), 32); } } @@ -193,8 +195,6 @@ input.push_back(seed); input.push_back(seed); - std::vector buf(hash->output_length()); - for(size_t j = 0; j <= count; ++j) { for(size_t i = 3; i != 1003; ++i) { hash->update(input[0]); @@ -212,7 +212,7 @@ } } - result.test_eq("Output is expected", input[2], expected); + result.test_bin_eq("Output is expected", input[2], expected); } return result; @@ -280,7 +280,7 @@ std::vector output(hash->output_length()); hash->final(output.data()); - result.test_eq("Output is expected", output, expected); + result.test_bin_eq("Output is expected", output, expected); } return result; @@ -299,7 +299,7 @@ result.test_throws("cannot output more bits than the underlying hash", [] { Botan::HashFunction::create("Truncated(SHA-256,257)"); }); auto unobtainable = Botan::HashFunction::create("Truncated(NonExistentHash-256,128)"); - result.confirm("non-existent hashes are not created", unobtainable == nullptr); + result.test_is_true("non-existent hashes are not created", unobtainable == nullptr); return result; } diff -Nru botan3-3.7.1+dfsg/src/tests/test_hash_id.cpp botan3-3.12.0+dfsg/src/tests/test_hash_id.cpp --- botan3-3.7.1+dfsg/src/tests/test_hash_id.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_hash_id.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -14,6 +14,8 @@ namespace Botan_Tests { +namespace { + #if defined(BOTAN_HAS_HASH_ID) && defined(BOTAN_HAS_ASN1) class PKCS_HashID_Test final : public Test { @@ -54,7 +56,7 @@ Botan::DER_Encoder der(bits); der.start_sequence().encode(alg).encode(dummy_hash, Botan::ASN1_Type::OctetString).end_cons(); - result.test_eq("Dummy hash is expected size", bits.size() - pkcs_id.size(), dummy_hash.size()); + result.test_sz_eq("Dummy hash is expected size", bits.size() - pkcs_id.size(), dummy_hash.size()); for(size_t i = pkcs_id.size(); i != bits.size(); ++i) { if(bits[i] != 0) { @@ -65,7 +67,7 @@ std::vector encoded_id(bits.begin(), bits.begin() + pkcs_id.size()); - result.test_eq("Encoded ID matches hardcoded", encoded_id, pkcs_id); + result.test_bin_eq("Encoded ID matches hardcoded", encoded_id, pkcs_id); } catch(Botan::Exception& e) { result.test_failure(e.what()); @@ -81,4 +83,6 @@ BOTAN_REGISTER_TEST("pubkey", "pkcs_hash_id", PKCS_HashID_Test); #endif +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_hss_lms.cpp botan3-3.12.0+dfsg/src/tests/test_hss_lms.cpp --- botan3-3.7.1+dfsg/src/tests/test_hss_lms.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_hss_lms.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -5,13 +5,14 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include "test_pubkey.h" #include "tests.h" #if defined(BOTAN_HAS_HSS_LMS) - + #include "test_arb_eq.h" + #include "test_pubkey.h" #include #include + #include #include #include #include @@ -28,25 +29,25 @@ CHECK("HSS Parameter Parsing", [&](Test::Result& result) { result.test_no_throw("no throw", [&] { - Botan::HSS_LMS_Params hss_params("SHA-256,HW(5,1),HW(25,8)"); + const Botan::HSS_LMS_Params hss_params("SHA-256,HW(5,1),HW(25,8)"); - result.test_is_eq("hss levels", hss_params.L(), Botan::HSS_Level(2)); - auto& top_lms_params = hss_params.params_at_level(Botan::HSS_Level(0)); - result.test_is_eq("hash name", top_lms_params.lms_params().hash_name(), std::string("SHA-256")); - result.test_is_eq("top level - lms type", - top_lms_params.lms_params().algorithm_type(), - Botan::LMS_Algorithm_Type::SHA256_M32_H5); - result.test_is_eq("top level - ots type", - top_lms_params.lmots_params().algorithm_type(), - Botan::LMOTS_Algorithm_Type::SHA256_N32_W1); - - auto& second_lms_params = hss_params.params_at_level(Botan::HSS_Level(1)); - result.test_is_eq("2nd level - lms type", - second_lms_params.lms_params().algorithm_type(), - Botan::LMS_Algorithm_Type::SHA256_M32_H25); - result.test_is_eq("2nd level - ots type", - second_lms_params.lmots_params().algorithm_type(), - Botan::LMOTS_Algorithm_Type::SHA256_N32_W8); + test_arb_eq(result, "hss levels", hss_params.L(), Botan::HSS_Level(2)); + const auto& top_lms_params = hss_params.params_at_level(Botan::HSS_Level(0)); + result.test_str_eq("hash name", top_lms_params.lms_params().hash_name(), std::string("SHA-256")); + result.test_enum_eq("top level - lms type", + top_lms_params.lms_params().algorithm_type(), + Botan::LMS_Algorithm_Type::SHA256_M32_H5); + result.test_enum_eq("top level - ots type", + top_lms_params.lmots_params().algorithm_type(), + Botan::LMOTS_Algorithm_Type::SHA256_N32_W1); + + const auto& second_lms_params = hss_params.params_at_level(Botan::HSS_Level(1)); + result.test_enum_eq("2nd level - lms type", + second_lms_params.lms_params().algorithm_type(), + Botan::LMS_Algorithm_Type::SHA256_M32_H25); + result.test_enum_eq("2nd level - ots type", + second_lms_params.lmots_params().algorithm_type(), + Botan::LMOTS_Algorithm_Type::SHA256_N32_W8); }); }), @@ -61,10 +62,10 @@ HSS_LMS_Signature_Generation_Test() : PK_Signature_Generation_Test("HSS-LMS", "pubkey/hss_lms_sig.vec", "Msg,PrivateKey,Signature") {} - std::string default_padding(const VarMap&) const final { return ""; } + std::string default_padding(const VarMap& /*vars*/) const final { return ""; } std::unique_ptr load_private_key(const VarMap& vars) final { - const auto sk_bytes = Botan::lock(vars.get_req_bin("PrivateKey")); + const auto sk_bytes = vars.get_req_bin("PrivateKey"); return std::make_unique(sk_bytes); } }; @@ -77,7 +78,7 @@ HSS_LMS_Signature_Verify_Tests() : PK_Signature_Verification_Test("HSS-LMS", "pubkey/hss_lms_verify.vec", "Msg,PublicKey,Signature") {} - std::string default_padding(const VarMap&) const final { return ""; } + std::string default_padding(const VarMap& /*vars*/) const final { return ""; } std::unique_ptr load_public_key(const VarMap& vars) override { const std::vector pk_bytes = vars.get_req_bin("PublicKey"); @@ -94,7 +95,7 @@ PK_Signature_NonVerification_Test( "HSS_LMS", "pubkey/hss_lms_invalid.vec", "Msg,PublicKey,InvalidSignature") {} - std::string default_padding(const VarMap&) const override { return ""; } + std::string default_padding(const VarMap& /*vars*/) const override { return ""; } std::unique_ptr load_public_key(const VarMap& vars) override { const std::vector raw_key = vars.get_req_bin("PublicKey"); @@ -135,14 +136,14 @@ signer.update(mes); auto valid_sig = signer.signature(Test::rng()); verifier.update(mes); - result.confirm("Entire signature is valid", verifier.check_signature(valid_sig.data(), valid_sig.size())); + result.test_is_true("Entire signature is valid", verifier.check_signature(valid_sig)); for(size_t idx = 0; idx < valid_sig.size(); ++idx) { auto bad_sig = valid_sig; bad_sig.at(idx) ^= 0x80; result.test_no_throw(Botan::fmt("Verification does not throw (byte idx {})", idx), [&]() { verifier.update(mes); - bool valid = verifier.check_signature(bad_sig); - result.confirm(Botan::fmt("Manipulated signature is invalid (byte idx {})", idx), !valid); + const bool valid = verifier.check_signature(bad_sig); + result.test_is_true(Botan::fmt("Manipulated signature is invalid (byte idx {})", idx), !valid); }); } @@ -162,12 +163,12 @@ signer.update(mes); auto valid_sig = signer.signature(Test::rng()); verifier.update(mes); - result.confirm("Entire signature is valid", verifier.check_signature(valid_sig.data(), valid_sig.size())); + result.test_is_true("Entire signature is valid", verifier.check_signature(valid_sig)); for(size_t n = 0; n < valid_sig.size(); ++n) { result.test_no_throw("Verification does not throw", [&]() { verifier.update(mes); - bool valid = verifier.check_signature(valid_sig.data(), n); - result.confirm("Too short signature is invalid", !valid); + const bool valid = verifier.check_signature(valid_sig.data(), n); + result.test_is_true("Too short signature is invalid", !valid); }); } @@ -182,13 +183,13 @@ auto sk_bytes = sk->private_key_bits(); result.test_no_throw("Entire private key valid", [&]() { - Botan::HSS_LMS_PrivateKey key(sk_bytes); + const Botan::HSS_LMS_PrivateKey key(sk_bytes); BOTAN_UNUSED(key); }); for(size_t n = 0; n < sk_bytes.size(); ++n) { result.test_throws("Partial private key invalid", [&]() { - std::span partial_key = {sk_bytes.data(), n}; - Botan::HSS_LMS_PrivateKey key(partial_key); + const std::span partial_key = {sk_bytes.data(), n}; + const Botan::HSS_LMS_PrivateKey key(partial_key); BOTAN_UNUSED(key); }); } @@ -203,13 +204,13 @@ auto sk_bytes = sk->public_key_bits(); result.test_no_throw("Entire public key valid", [&]() { - Botan::HSS_LMS_PublicKey key(sk_bytes); + const Botan::HSS_LMS_PublicKey key(sk_bytes); BOTAN_UNUSED(key); }); for(size_t n = 0; n < sk_bytes.size(); ++n) { result.test_throws("Partial public key invalid", [&]() { - std::span partial_key = {sk_bytes.data(), n}; - Botan::HSS_LMS_PublicKey key(partial_key); + const std::span partial_key = {sk_bytes.data(), n}; + const Botan::HSS_LMS_PublicKey key(partial_key); BOTAN_UNUSED(key); }); } @@ -244,25 +245,25 @@ std::vector mes = {0xde, 0xad, 0xbe, 0xef}; auto sk_bytes_begin = sk.private_key_bits(); - // Tree hights: 5,5 => 2^(5+5) = 1024 signatures available + // Tree heights: 5,5 => 2^(5+5) = 1024 signatures available const uint64_t expected_total = 1024; - result.confirm("Fresh key starts with total number of remaining signatures.", - sk.remaining_operations() == expected_total); + result.test_opt_u64_eq( + "Fresh key starts with total number of remaining signatures.", sk.remaining_operations(), expected_total); // Creating a signature should update the private key's state auto sig_0 = signer.sign_message(mes, Test::rng()); - result.confirm( + result.test_is_true( "First signature uses index 0.", Botan::HSS_Signature::from_bytes_or_throw(sig_0).bottom_sig().q() == Botan::LMS_Tree_Node_Idx(0)); auto sk_bytes_after_sig = sk.private_key_bits(); - result.confirm("Signature decreases number of remaining signatures.", - sk.remaining_operations() == expected_total - 1); - result.test_ne("Signature updates private key.", sk_bytes_after_sig, sk_bytes_begin); + result.test_opt_u64_eq( + "Signature decreases number of remaining signatures.", sk.remaining_operations(), expected_total - 1); + result.test_bin_ne("Signature updates private key.", sk_bytes_after_sig, sk_bytes_begin); auto sig_1 = signer.sign_message(mes, Test::rng()); - result.confirm( + result.test_is_true( "Next signature uses the new index.", Botan::HSS_Signature::from_bytes_or_throw(sig_1).bottom_sig().q() == Botan::LMS_Tree_Node_Idx(1)); @@ -272,18 +273,18 @@ Test::Result test_max_sig_count() { Test::Result result("HSS-LMS"); - uint64_t total_sig_count = 32; + const uint64_t total_sig_count = 32; auto sk = create_private_key_with_idx(total_sig_count - 1); Botan::PK_Signer signer(sk, Test::rng(), ""); std::vector mes = {0xde, 0xad, 0xbe, 0xef}; auto sk_bytes_begin = sk.private_key_bits(); - result.confirm("One remaining signature.", sk.remaining_operations() == uint64_t(1)); + result.test_opt_u64_eq("One remaining signature.", sk.remaining_operations(), 1); result.test_no_throw("Use last signature index.", [&]() { signer.sign_message(mes, Test::rng()); }); - result.confirm("No remaining signatures.", sk.remaining_operations() == uint64_t(0)); + result.test_opt_u64_eq("No remaining signatures.", sk.remaining_operations(), 0); result.test_throws("Cannot sign with exhausted key.", [&]() { signer.sign_message(mes, Test::rng()); }); - result.confirm("Still zero remaining signatures.", sk.remaining_operations() == uint64_t(0)); + result.test_opt_u64_eq("Still zero remaining signatures.", sk.remaining_operations(), 0); return result; } @@ -301,23 +302,23 @@ // HSS_LMS_PublicKey::key_length() auto sk = Botan::create_private_key("HSS-LMS", Test::rng(), "SHA-256,HW(10,4)"); sk->key_length(); - result.test_gt("Public key length must be greater than the simply type information plus I", - sk->key_length(), - 3 * sizeof(uint32_t) + Botan::LMS_IDENTIFIER_LEN); + result.test_sz_gt("Public key length must be greater than the simply type information plus I", + sk->key_length(), + 3 * sizeof(uint32_t) + Botan::LMS_IDENTIFIER_LEN); // HSS_LMS_Verification_Operation::hash_function() - Botan::PK_Verifier verifier(*sk, ""); - result.test_eq("PK_Verifier should report the hash of the key", verifier.hash_function(), "SHA-256"); + const Botan::PK_Verifier verifier(*sk, ""); + result.test_str_eq("PK_Verifier should report the hash of the key", verifier.hash_function(), "SHA-256"); // HSS_LMS_PrivateKey::raw_private_key_bits() - result.test_eq("Our BER and raw encoding is the same", sk->raw_private_key_bits(), sk->private_key_bits()); + result.test_bin_eq("Our BER and raw encoding is the same", sk->raw_private_key_bits(), sk->private_key_bits()); // HSS_LMS_Signature_Operation::algorithm_identifier() - Botan::PK_Signer signer(*sk, Test::rng(), ""); - result.test_is_eq(signer.algorithm_identifier(), sk->algorithm_identifier()); + const Botan::PK_Signer signer(*sk, Test::rng(), ""); + result.test_is_true("signature algorithm", signer.algorithm_identifier() == sk->algorithm_identifier()); // HSS_LMS_Signature_Operation::hash_function() - result.test_eq("PK_Signer should report the hash of the key", signer.hash_function(), "SHA-256"); + result.test_str_eq("PK_Signer should report the hash of the key", signer.hash_function(), "SHA-256"); return {result}; } diff -Nru botan3-3.7.1+dfsg/src/tests/test_jitter_rng.cpp botan3-3.12.0+dfsg/src/tests/test_jitter_rng.cpp --- botan3-3.7.1+dfsg/src/tests/test_jitter_rng.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_jitter_rng.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -4,9 +4,7 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include - -#include +#include "tests.h" #ifdef BOTAN_HAS_JITTER_RNG @@ -15,8 +13,6 @@ #include #include - #include "tests.h" - namespace Botan_Tests { namespace { diff -Nru botan3-3.7.1+dfsg/src/tests/test_kdf.cpp botan3-3.12.0+dfsg/src/tests/test_kdf.cpp --- botan3-3.7.1+dfsg/src/tests/test_kdf.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_kdf.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,7 +6,7 @@ #include "tests.h" -#if defined(BOTAN_HAS_KDF_BASE) +#if defined(BOTAN_HAS_KDF) #include #endif @@ -19,7 +19,7 @@ namespace { -#if defined(BOTAN_HAS_KDF_BASE) +#if defined(BOTAN_HAS_KDF) class KDF_KAT_Tests final : public Text_Based_Test { public: KDF_KAT_Tests() : Text_Based_Test("kdf", "Secret,Output", "Salt,Label,IKM,XTS") {} @@ -39,18 +39,18 @@ const std::vector label = vars.get_opt_bin("Label"); const std::vector expected = vars.get_req_bin("Output"); - result.test_eq("name", kdf->name(), kdf_name); - result.test_eq("derived key", kdf->derive_key(expected.size(), secret, salt, label), expected); + result.test_str_eq("name", kdf->name(), kdf_name); + result.test_bin_eq("derived key", kdf->derive_key(expected.size(), secret, salt, label), expected); if(expected.size() == 32) { const auto key = kdf->derive_key<32>(secret, salt, label); - result.test_eq("derived key as array", Botan::secure_vector{key.begin(), key.end()}, expected); + result.test_bin_eq("derived key as array", Botan::secure_vector{key.begin(), key.end()}, expected); } // Test that clone works auto clone = kdf->new_object(); - result.confirm("Clone has different pointer", kdf.get() != clone.get()); - result.test_eq("Clone has same name", kdf->name(), clone->name()); + result.test_is_true("Clone has different pointer", kdf.get() != clone.get()); + result.test_str_eq("Clone has same name", kdf->name(), clone->name()); return result; } @@ -83,7 +83,7 @@ Botan::secure_vector output = Botan::hkdf_expand_label(hash_name, secret, label, hashval, expected.size()); - result.test_eq("Output matches", output, expected); + result.test_bin_eq("Output matches", output, expected); return result; } diff -Nru botan3-3.7.1+dfsg/src/tests/test_keccak_helpers.cpp botan3-3.12.0+dfsg/src/tests/test_keccak_helpers.cpp --- botan3-3.7.1+dfsg/src/tests/test_keccak_helpers.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_keccak_helpers.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,9 +10,8 @@ #if defined(BOTAN_HAS_KECCAK_PERM) #include - #include #include - #include + #include #if defined(BOTAN_HAS_SHAKE_XOF) #include @@ -22,33 +21,29 @@ namespace { -decltype(auto) encode_size(size_t x) { +size_t encode_size(size_t x) { return Botan::keccak_int_encoding_size(x); } -decltype(auto) left_encode(Test::Result& result, size_t x) { +std::vector left_encode(Test::Result& result, size_t x) { const auto expected_length = Botan::keccak_int_encoding_size(x); std::vector out(expected_length); - result.test_eq("left_encode return value", Botan::keccak_int_left_encode(out, x).size(), expected_length); + result.test_sz_eq("left_encode return value", Botan::keccak_int_left_encode(out, x).size(), expected_length); return out; } -decltype(auto) right_encode(Test::Result& result, size_t x) { +std::vector right_encode(Test::Result& result, size_t x) { const auto expected_length = Botan::keccak_int_encoding_size(x); std::vector out(expected_length); - result.test_eq("right_encode return value", Botan::keccak_int_right_encode(out, x).size(), expected_length); + result.test_sz_eq("right_encode return value", Botan::keccak_int_right_encode(out, x).size(), expected_length); return out; } -decltype(auto) hex(std::string_view str) { - return Botan::hex_decode(str); -} - #if defined(BOTAN_HAS_SHAKE_XOF) -decltype(auto) shake32(std::vector data) { +decltype(auto) shake32(std::string_view hex) { const auto xof = Botan::XOF::create_or_throw("SHAKE-256"); - xof->update(data); + xof->update(Botan::hex_decode(hex)); return xof->output_stdvec(32); } @@ -58,41 +53,41 @@ return { CHECK("keccak_int_encoding_size()", [](Test::Result& result) { - result.test_eq("keccak_int_encoding_size(0)", encode_size(0), 2); - result.test_eq("keccak_int_encoding_size(255)", encode_size(0xFF), 2); - result.test_eq("keccak_int_encoding_size(256)", encode_size(0xFF + 1), 3); - result.test_eq("keccak_int_encoding_size(65.535)", encode_size(0xFFFF), 3); - result.test_eq("keccak_int_encoding_size(65.536)", encode_size(0xFFFF + 1), 4); - result.test_eq("keccak_int_encoding_size(16.777.215)", encode_size(0xFFFFFF), 4); - result.test_eq("keccak_int_encoding_size(16.777.216)", encode_size(0xFFFFFF + 1), 5); + result.test_sz_eq("keccak_int_encoding_size(0)", encode_size(0), 2); + result.test_sz_eq("keccak_int_encoding_size(255)", encode_size(0xFF), 2); + result.test_sz_eq("keccak_int_encoding_size(256)", encode_size(0xFF + 1), 3); + result.test_sz_eq("keccak_int_encoding_size(65.535)", encode_size(0xFFFF), 3); + result.test_sz_eq("keccak_int_encoding_size(65.536)", encode_size(0xFFFF + 1), 4); + result.test_sz_eq("keccak_int_encoding_size(16.777.215)", encode_size(0xFFFFFF), 4); + result.test_sz_eq("keccak_int_encoding_size(16.777.216)", encode_size(0xFFFFFF + 1), 5); }), CHECK("keccak_int_left_encode()", [](Test::Result& result) { - result.test_is_eq("left_encode(0)", left_encode(result, 0), hex("0100")); - result.test_is_eq("left_encode(1)", left_encode(result, 1), hex("0101")); - result.test_is_eq("left_encode(255)", left_encode(result, 255), hex("01FF")); - result.test_is_eq("left_encode(256)", left_encode(result, 0xFF + 1), hex("020100")); - result.test_is_eq("left_encode(65.535)", left_encode(result, 0xFFFF), hex("02FFFF")); - result.test_is_eq("left_encode(65.536)", left_encode(result, 0xFFFF + 1), hex("03010000")); - result.test_is_eq("left_encode(16.777.215)", left_encode(result, 0xFFFFFF), hex("03FFFFFF")); - result.test_is_eq("left_encode(16.777.215)", left_encode(result, 0xFFFFFF), hex("03FFFFFF")); - result.test_is_eq("left_encode(16.777.216)", left_encode(result, 0xFFFFFF + 1), hex("0401000000")); - result.test_is_eq("left_encode(287.454.020)", left_encode(result, 0x11223344), hex("0411223344")); + result.test_bin_eq("left_encode(0)", left_encode(result, 0), "0100"); + result.test_bin_eq("left_encode(1)", left_encode(result, 1), "0101"); + result.test_bin_eq("left_encode(255)", left_encode(result, 255), "01FF"); + result.test_bin_eq("left_encode(256)", left_encode(result, 0xFF + 1), "020100"); + result.test_bin_eq("left_encode(65.535)", left_encode(result, 0xFFFF), "02FFFF"); + result.test_bin_eq("left_encode(65.536)", left_encode(result, 0xFFFF + 1), "03010000"); + result.test_bin_eq("left_encode(16.777.215)", left_encode(result, 0xFFFFFF), "03FFFFFF"); + result.test_bin_eq("left_encode(16.777.215)", left_encode(result, 0xFFFFFF), "03FFFFFF"); + result.test_bin_eq("left_encode(16.777.216)", left_encode(result, 0xFFFFFF + 1), "0401000000"); + result.test_bin_eq("left_encode(287.454.020)", left_encode(result, 0x11223344), "0411223344"); }), CHECK("keccak_int_right_encode()", [](Test::Result& result) { - result.test_is_eq("right_encode(0)", right_encode(result, 0), hex("0001")); - result.test_is_eq("right_encode(1)", right_encode(result, 1), hex("0101")); - result.test_is_eq("right_encode(255)", right_encode(result, 255), hex("FF01")); - result.test_is_eq("right_encode(256)", right_encode(result, 0xFF + 1), hex("010002")); - result.test_is_eq("right_encode(65.535)", right_encode(result, 0xFFFF), hex("FFFF02")); - result.test_is_eq("right_encode(65.536)", right_encode(result, 0xFFFF + 1), hex("01000003")); - result.test_is_eq("right_encode(16.777.215)", right_encode(result, 0xFFFFFF), hex("FFFFFF03")); - result.test_is_eq("right_encode(16.777.215)", right_encode(result, 0xFFFFFF), hex("FFFFFF03")); - result.test_is_eq("right_encode(16.777.216)", right_encode(result, 0xFFFFFF + 1), hex("0100000004")); - result.test_is_eq("right_encode(287.454.020)", right_encode(result, 0x11223344), hex("1122334404")); + result.test_bin_eq("right_encode(0)", right_encode(result, 0), "0001"); + result.test_bin_eq("right_encode(1)", right_encode(result, 1), "0101"); + result.test_bin_eq("right_encode(255)", right_encode(result, 255), "FF01"); + result.test_bin_eq("right_encode(256)", right_encode(result, 0xFF + 1), "010002"); + result.test_bin_eq("right_encode(65.535)", right_encode(result, 0xFFFF), "FFFF02"); + result.test_bin_eq("right_encode(65.536)", right_encode(result, 0xFFFF + 1), "01000003"); + result.test_bin_eq("right_encode(16.777.215)", right_encode(result, 0xFFFFFF), "FFFFFF03"); + result.test_bin_eq("right_encode(16.777.215)", right_encode(result, 0xFFFFFF), "FFFFFF03"); + result.test_bin_eq("right_encode(16.777.216)", right_encode(result, 0xFFFFFF + 1), "0100000004"); + result.test_bin_eq("right_encode(287.454.020)", right_encode(result, 0x11223344), "1122334404"); }), CHECK( @@ -103,15 +98,15 @@ const std::vector n{'K', 'M', 'A', 'C'}; const auto bytes_generated = Botan::keccak_absorb_padded_strings_encoding(out, padmod, n); - result.test_eq("padded bytes", bytes_generated, padmod); + result.test_sz_eq("padded bytes", bytes_generated, padmod); - result.test_is_eq( + result.test_bin_eq( + "keccak_absorb_padded_strings_encoding", out, - hex( - "0188" /* left_encode(perm.byte_rate()) */ - "0120" /* left_encode(n.size() * 8) */ - "4B4D4143" /* "KMAC" */ - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")); + "0188" /* left_encode(perm.byte_rate()) */ + "0120" /* left_encode(n.size() * 8) */ + "4B4D4143" /* "KMAC" */ + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); }), CHECK( @@ -123,20 +118,19 @@ const std::vector n{'K', 'M', 'A', 'C'}; const std::string str = "This is a long salt, that is longer than 128 bytes in order to fill up the first round of the Keccak permutation. That should do it."; - const std::vector s{Botan::cast_char_ptr_to_uint8(str.data()), - Botan::cast_char_ptr_to_uint8(str.data()) + str.size()}; - const auto bytes_generated = Botan::keccak_absorb_padded_strings_encoding(out, padmod, n, s); - result.test_eq("padded bytes", bytes_generated, padmod * 2); + const auto bytes_generated = + Botan::keccak_absorb_padded_strings_encoding(out, padmod, n, Botan::as_span_of_bytes(str)); + result.test_sz_eq("padded bytes", bytes_generated, padmod * 2); - result.test_is_eq( + result.test_bin_eq( + "keccak_absorb_padded_strings_encoding", out, - hex( - "0188" /* left_encode(perm.byte_rate()) */ - "0120" /* left_encode(n.size() * 8) */ - "4B4D4143" /* "KMAC" */ - "020420" /* left_encode(s.size() * 8) */ - "546869732069732061206c6f6e672073616c742c2074686174206973206c6f6e676572207468616e2031323820627974657320696e206f7264657220746f2066696c6c2075702074686520666972737420726f756e64206f6620746865204b656363616b207065726d75746174696f6e2e20546861742073686f756c6420646f2069742e" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")); + "0188" /* left_encode(perm.byte_rate()) */ + "0120" /* left_encode(n.size() * 8) */ + "4B4D4143" /* "KMAC" */ + "020420" /* left_encode(s.size() * 8) */ + "546869732069732061206c6f6e672073616c742c2074686174206973206c6f6e676572207468616e2031323820627974657320696e206f7264657220746f2066696c6c2075702074686520666972737420726f756e64206f6620746865204b656363616b207065726d75746174696f6e2e20546861742073686f756c6420646f2069742e" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); }), #if defined(BOTAN_HAS_SHAKE_XOF) @@ -144,45 +138,46 @@ CHECK( "keccak_absorb_padded_strings_encoding() with one byte string", [](Test::Result& result) { - std::vector out(32); + const std::vector out(32); const auto xof = Botan::XOF::create_or_throw("SHAKE-256"); const auto padmod = xof->block_size(); const std::vector n{'K', 'M', 'A', 'C'}; const auto bytes_generated = Botan::keccak_absorb_padded_strings_encoding(*xof, padmod, n); - result.test_eq("padded bytes", bytes_generated, padmod); + result.test_sz_eq("padded bytes", bytes_generated, padmod); - result.test_is_eq( + result.test_bin_eq( + "keccak_absorb_padded_strings_encoding", xof->output_stdvec(32), - shake32(hex( + shake32( "0188" /* left_encode(perm.byte_rate()) */ "0120" /* left_encode(n.size() * 8) */ "4B4D4143" /* "KMAC" */ - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"))); + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")); }), CHECK("keccak_absorb_padded_strings_encoding() with two byte strings", [](Test::Result& result) { - std::vector out(32); + const std::vector out(32); const auto xof = Botan::XOF::create_or_throw("SHAKE-256"); const auto padmod = xof->block_size(); const std::vector n{'K', 'M', 'A', 'C'}; const std::string str = "This is a long salt, that is longer than 128 bytes in order to fill up the first round of the Keccak permutation. That should do it."; - const std::vector s{Botan::cast_char_ptr_to_uint8(str.data()), - Botan::cast_char_ptr_to_uint8(str.data()) + str.size()}; - const auto bytes_generated = Botan::keccak_absorb_padded_strings_encoding(*xof, padmod, n, s); - result.test_eq("padded bytes", bytes_generated, padmod * 2); + const auto bytes_generated = + Botan::keccak_absorb_padded_strings_encoding(*xof, padmod, n, Botan::as_span_of_bytes(str)); + result.test_sz_eq("padded bytes", bytes_generated, padmod * 2); - result.test_is_eq( + result.test_bin_eq( + "keccak_absorb_padded_strings_encoding", xof->output_stdvec(32), - shake32(hex( + shake32( "0188" /* left_encode(perm.byte_rate()) */ "0120" /* left_encode(n.size() * 8) */ "4B4D4143" /* "KMAC" */ "020420" /* left_encode(s.size() * 8) */ "546869732069732061206c6f6e672073616c742c2074686174206973206c6f6e676572207468616e2031323820627974657320696e206f7264657220746f2066696c6c2075702074686520666972737420726f756e64206f6620746865204b656363616b207065726d75746174696f6e2e20546861742073686f756c6420646f2069742e" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"))); + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")); }), #endif diff -Nru botan3-3.7.1+dfsg/src/tests/test_keywrap.cpp botan3-3.12.0+dfsg/src/tests/test_keywrap.cpp --- botan3-3.7.1+dfsg/src/tests/test_keywrap.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_keywrap.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,6 +12,7 @@ #if defined(BOTAN_HAS_NIST_KEYWRAP) #include + #include #include #endif @@ -36,8 +37,8 @@ const Botan::secure_vector key_l(key.begin(), key.end()); const Botan::secure_vector exp_l(expected.begin(), expected.end()); - result.test_eq("encryption", Botan::rfc3394_keywrap(key_l, kek_sym), expected); - result.test_eq("decryption", Botan::rfc3394_keyunwrap(exp_l, kek_sym), key); + result.test_bin_eq("encryption", Botan::rfc3394_keywrap(key_l, kek_sym), expected); + result.test_bin_eq("decryption", Botan::rfc3394_keyunwrap(exp_l, kek_sym), key); } catch(std::exception& e) { result.test_failure("", e.what()); } @@ -80,7 +81,7 @@ wrapped = nist_key_wrap_padded(input.data(), input.size(), *bc); } - result.test_eq("key wrap", wrapped, expected); + result.test_bin_eq("key wrap", wrapped, expected); try { Botan::secure_vector unwrapped; @@ -90,7 +91,7 @@ unwrapped = nist_key_unwrap_padded(expected.data(), expected.size(), *bc); } - result.test_eq("key unwrap", unwrapped, input); + result.test_bin_eq("key unwrap", unwrapped, input); } catch(Botan::Integrity_Failure& e) { result.test_failure("NIST key unwrap failed with integrity failure", e.what()); } diff -Nru botan3-3.7.1+dfsg/src/tests/test_kyber.cpp botan3-3.12.0+dfsg/src/tests/test_kyber.cpp --- botan3-3.7.1+dfsg/src/tests/test_kyber.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_kyber.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -16,7 +16,6 @@ #include "test_rng.h" #include "tests.h" -#include #include #include @@ -26,14 +25,16 @@ #include #include #include + #include #include #include #include - #include #endif namespace Botan_Tests { +namespace { + #if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S) || defined(BOTAN_HAS_ML_KEM) class KYBER_Tests final : public Test { @@ -54,25 +55,25 @@ 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); - result.test_eq("estimated strength public", pub_key->estimated_strength(), strength); - result.test_eq("canonical parameter set identifier", priv_key.key_length(), psid); - result.test_eq("canonical parameter set identifier", pub_key->key_length(), psid); + result.test_sz_eq("estimated strength private", priv_key.estimated_strength(), strength); + result.test_sz_eq("estimated strength public", pub_key->estimated_strength(), strength); + result.test_sz_eq("canonical parameter set identifier", priv_key.key_length(), psid); + result.test_sz_eq("canonical parameter set identifier", pub_key->key_length(), psid); // Serialize const auto priv_key_bits = priv_key.private_key_bits(); const auto pub_key_bits = pub_key->public_key_bits(); // Bob (reading from serialized public key) - Botan::Kyber_PublicKey alice_pub_key(pub_key_bits, mode); + const 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(*rng); // Alice (reading from serialized private key) - Botan::Kyber_PrivateKey alice_priv_key(priv_key_bits, mode); + const Botan::Kyber_PrivateKey alice_priv_key(priv_key_bits, mode); 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()); + result.test_bin_eq("shared secrets are equal", key_alice, kem_result.shared_key()); // // negative tests @@ -91,11 +92,11 @@ kem_result.encapsulated_shared_key().crend(), std::back_inserter(reverse_cipher_text)); const auto key_alice_rev = dec.decrypt(reverse_cipher_text, 0, empty_salt); - result.confirm("shared secrets are not equal", key_alice != key_alice_rev); + result.test_is_true("shared secrets are not equal", key_alice != key_alice_rev); // Try to decrypt the valid ciphertext again const auto key_alice_try2 = dec.decrypt(kem_result.encapsulated_shared_key(), 0 /* no KDF */, empty_salt); - result.test_eq("shared secrets are equal", key_alice_try2, kem_result.shared_key()); + result.test_bin_eq("shared secrets are equal", key_alice_try2, kem_result.shared_key()); return result; } @@ -140,7 +141,7 @@ // We use different hash functions for Kyber 90s, as those are // consistent with the algorithm requirements of the implementations. - std::string_view hash_name = get_mode(mode).is_90s() ? "SHA-256" : "SHAKE-256(128)"; + const std::string_view hash_name = get_mode(mode).is_90s() ? "SHA-256" : "SHAKE-256(128)"; auto hash = Botan::HashFunction::create_or_throw(hash_name); const auto digest = hash->process(value); @@ -161,7 +162,8 @@ } } - Fixed_Output_RNG rng_for_encapsulation(const std::string&, Botan::RandomNumberGenerator& rng) const final { + Fixed_Output_RNG rng_for_encapsulation(const std::string& /*mode*/, + Botan::RandomNumberGenerator& rng) const final { return Fixed_Output_RNG(rng.random_vec(32)); } }; @@ -252,9 +254,35 @@ const auto skr = std::make_unique(sk_raw, mode); const auto pkr = std::make_unique(pk_raw, mode); - result.test_eq("sk's encoding of pk", skr->public_key_bits(), pk_raw); - result.test_eq("sk's encoding of sk", skr->private_key_bits(), sk_raw); - result.test_eq("pk's encoding of pk", skr->public_key_bits(), pk_raw); + result.test_bin_eq("sk's encoding of pk", skr->public_key_bits(), pk_raw); + result.test_bin_eq("sk's encoding of sk", skr->private_key_bits(), sk_raw); + result.test_bin_eq("pk's encoding of pk", pkr->public_key_bits(), pk_raw); + + // expanded vs seed encoding + if(skr->private_key_format() == Botan::MlPrivateKeyFormat::Seed) { + result.test_bin_eq("sk's seed encoding of sk", + skr->private_key_bits_with_format(Botan::MlPrivateKeyFormat::Seed), + sk_raw); + const auto skr_expanded = std::make_unique( + skr->private_key_bits_with_format(Botan::MlPrivateKeyFormat::Expanded), mode); + result.test_bin_eq("sk's expanded encoding consistency", + skr->private_key_bits_with_format(Botan::MlPrivateKeyFormat::Expanded), + skr_expanded->private_key_bits_with_format(Botan::MlPrivateKeyFormat::Expanded)); + result.test_throws("expect no seed in expanded sk", [&] { + skr_expanded->private_key_bits_with_format(Botan::MlPrivateKeyFormat::Seed); + }); + + const auto encapsulation = Botan::PK_KEM_Encryptor(*pkr, "Raw").encrypt(rng()); + result.test_bin_eq( + "expanded sk decapsulation", + Botan::PK_KEM_Decryptor(*skr_expanded, rng(), "Raw").decrypt(encapsulation.encapsulated_shared_key()), + encapsulation.shared_key()); + + } else { + result.test_bin_eq("sk's expanded encoding of sk", + skr->private_key_bits_with_format(Botan::MlPrivateKeyFormat::Expanded), + sk_raw); + } } return result; @@ -309,9 +337,8 @@ for(uint16_t x = 0; x < q; ++x) { const uint32_t c = Kyber_Algos::compress(x); - const auto twotothed = (uint32_t(1) << d); - const auto expected = (static_cast(twotothed) / q) * x; - const auto e = static_cast(std::round(expected)) % twotothed; + constexpr auto twotothed = (uint32_t(1) << d); + const auto e = ((twotothed * x + (q / 2)) / q) % twotothed; if(c != e) { res.test_failure(fmt("compress<{}>({}) = {}; expected {}", d, x, c, e)); @@ -335,8 +362,7 @@ for(from_t y = 0; y < twotothed; ++y) { const uint32_t c = Kyber_Algos::decompress(y); - const auto expected = (static_cast(q) / twotothed) * y; - const auto e = static_cast(std::round(expected)) % q; + const uint32_t e = (q * y + (twotothed / 2)) / twotothed; if(c != e) { result.test_failure(fmt("decompress<{}>({}) = {}; expected {}", d, static_cast(y), c, e)); @@ -355,6 +381,7 @@ result.start_timer(); + // NOLINTNEXTLINE(*-redundant-expression) for(uint16_t x = 0; x < q && x < (1 << d); ++x) { const uint16_t c = Kyber_Algos::compress(Kyber_Algos::decompress(x)); if(x != c) { @@ -395,4 +422,6 @@ #endif +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_lmots.cpp botan3-3.12.0+dfsg/src/tests/test_lmots.cpp --- botan3-3.7.1+dfsg/src/tests/test_lmots.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_lmots.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,8 +8,9 @@ #if defined(BOTAN_HAS_HSS_LMS) + #include + #include #include - #include namespace Botan_Tests { @@ -22,12 +23,12 @@ public: LMOTS_Test() : Text_Based_Test("pubkey/lmots.vec", "TypeId,Seed,I,q,Msg,PublicKey,HashSig") {} - bool skip_this_test(const std::string&, const VarMap& vars) override { + bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override { BOTAN_UNUSED(vars); return false; } - Test::Result run_one_test(const std::string&, const VarMap& vars) final { + Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) final { Test::Result result("LMOTS"); const auto lmots_type_id = vars.get_req_u32("TypeId"); @@ -47,18 +48,18 @@ // Test private/public OTS key creation auto sk = Botan::LMOTS_Private_Key(params, identifier, q, seed); const auto pk = Botan::LMOTS_Public_Key(sk); - result.test_is_eq("Public key generation", pk.K(), pk_ref); + result.test_bin_eq("Public key generation", pk.K(), pk_ref); // Test signature creation Botan::LMOTS_Signature_Bytes sig(Botan::LMOTS_Signature::size(params)); sk.sign(sig, msg); - result.test_is_eq("Signature generation", hash->process>(sig), sig_ref); + result.test_bin_eq("Signature generation", hash->process>(sig), sig_ref); // Test create pubkey from signature auto sig_slicer = Botan::BufferSlicer(sig); auto sig_obj = Botan::LMOTS_Signature::from_bytes_or_throw(sig_slicer); - Botan::LMOTS_K pk_from_sig = Botan::lmots_compute_pubkey_from_sig(sig_obj, msg, identifier, q); - result.test_is_eq("Public key from signature", pk_from_sig, pk_ref); + const Botan::LMOTS_K pk_from_sig = Botan::lmots_compute_pubkey_from_sig(sig_obj, msg, identifier, q); + result.test_bin_eq("Public key from signature", pk_from_sig, pk_ref); return result; } diff -Nru botan3-3.7.1+dfsg/src/tests/test_lms.cpp botan3-3.12.0+dfsg/src/tests/test_lms.cpp --- botan3-3.7.1+dfsg/src/tests/test_lms.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_lms.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,8 +8,9 @@ #if defined(BOTAN_HAS_HSS_LMS) + #include + #include #include - #include namespace Botan_Tests { @@ -22,12 +23,12 @@ public: LMS_Test() : Text_Based_Test("pubkey/lms.vec", "Seed,Msg,q,PublicKey,HashSig") {} - bool skip_this_test(const std::string&, const VarMap& vars) override { + bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override { BOTAN_UNUSED(vars); return false; } - Test::Result run_one_test(const std::string&, const VarMap& vars) final { + Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) final { Test::Result result("LMS"); const auto seed = Botan::LMS_Seed(vars.get_req_bin("Seed")); @@ -40,25 +41,25 @@ auto hash = Botan::HashFunction::create("SHA-256"); auto lms_pk_ref_slicer = Botan::BufferSlicer(pk_ref); - Botan::LMS_PublicKey lms_pk_ref = Botan::LMS_PublicKey::from_bytes_or_throw(lms_pk_ref_slicer); + const Botan::LMS_PublicKey lms_pk_ref = Botan::LMS_PublicKey::from_bytes_or_throw(lms_pk_ref_slicer); // Test public key creation auto lms_sk = Botan::LMS_PrivateKey(lms_pk_ref.lms_params(), lms_pk_ref.lmots_params(), lms_pk_ref.identifier(), seed); auto pub_key = Botan::LMS_PublicKey(lms_sk); - result.test_is_eq("Public key generation", pub_key.to_bytes(), pk_ref); + result.test_bin_eq("Public key generation", pub_key.to_bytes(), pk_ref); // Test signature creation and verification auto sk = Botan::LMS_PrivateKey(lms_pk_ref.lms_params(), lms_pk_ref.lmots_params(), lms_pk_ref.identifier(), seed); Botan::LMS_Signature_Bytes sig(Botan::LMS_Signature::size(lms_pk_ref.lms_params(), lms_pk_ref.lmots_params())); auto pk_from_sig = sk.sign_and_get_pk(sig, q, msg); - result.test_is_eq("Signature creation", hash->process>(sig), hashed_sig_ref); + result.test_bin_eq("Signature creation", hash->process>(sig), hashed_sig_ref); auto sig_slicer = Botan::BufferSlicer(sig); auto sig_obj = Botan::LMS_Signature::from_bytes_or_throw(sig_slicer); - result.confirm("Signature verification", pub_key.verify_signature(msg, sig_obj)); + result.test_is_true("Signature verification", pub_key.verify_signature(msg, sig_obj)); return result; } diff -Nru botan3-3.7.1+dfsg/src/tests/test_mac.cpp botan3-3.12.0+dfsg/src/tests/test_mac.cpp --- botan3-3.7.1+dfsg/src/tests/test_mac.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_mac.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,7 +8,9 @@ #include "tests.h" #if defined(BOTAN_HAS_MAC) + #include #include + #include #include #endif @@ -51,8 +53,8 @@ const std::string provider(mac->provider()); - result.test_is_nonempty("provider", provider); - result.test_eq(provider, mac->name(), algo); + result.test_str_not_empty("provider", provider); + result.test_str_eq(provider, mac->name(), algo); try { std::vector buf(128); @@ -62,29 +64,29 @@ result.test_success("Trying to MAC with no key set fails"); } - result.test_eq("key not set", mac->has_keying_material(), false); + result.test_is_false("key not set", mac->has_keying_material()); mac->set_key(key); - result.test_eq("key set", mac->has_keying_material(), true); + result.test_is_true("key set", mac->has_keying_material()); mac->start(iv); mac->update(input); - result.test_eq(provider, "correct mac", mac->final(), expected); + result.test_bin_eq(provider + " correct mac", mac->final(), expected); mac->set_key(key); mac->start(iv); mac->update(input); - result.test_eq(provider, "correct mac (try 2)", mac->final(), expected); + result.test_bin_eq(provider + " correct mac (try 2)", mac->final(), expected); if(iv.empty()) { mac->set_key(key); mac->update(input); - result.test_eq(provider, "correct mac (no start call)", mac->final(), expected); + result.test_bin_eq(provider + " correct mac (no start call)", mac->final(), expected); } if(!mac->fresh_key_required_per_message()) { for(size_t i = 0; i != 3; ++i) { mac->start(iv); mac->update(input); - result.test_eq(provider, "correct mac (same key)", mac->final(), expected); + result.test_bin_eq(provider + " correct mac (same key)", mac->final(), expected); } } @@ -93,7 +95,7 @@ mac->start(iv); mac->update("some discarded input"); mac->clear(); - result.test_eq("key not set", mac->has_keying_material(), false); + result.test_is_false("key not set", mac->has_keying_material()); // do the same to test verify_mac() mac->set_key(key); @@ -102,13 +104,13 @@ // Test that clone works and does not affect parent object auto clone = mac->new_object(); - result.confirm("Clone has different pointer", mac.get() != clone.get()); - result.test_eq("Clone has same name", mac->name(), clone->name()); + result.test_is_true("Clone has different pointer", mac.get() != clone.get()); + result.test_str_eq("Clone has same name", mac->name(), clone->name()); clone->set_key(key); clone->start(iv); clone->update(this->rng().random_vec(32)); - result.test_eq(provider + " verify mac", mac->verify_mac(expected.data(), expected.size()), true); + result.test_is_true(provider + " verify mac", mac->verify_mac(expected.data(), expected.size())); if(input.size() > 2) { mac->set_key(key); // Poly1305 requires the re-key @@ -118,7 +120,7 @@ mac->update(&input[1], input.size() - 2); mac->update(input[input.size() - 1]); - result.test_eq(provider, "split mac", mac->final(), expected); + result.test_bin_eq(provider + " split mac", mac->final(), expected); // do the same to test verify_mac() mac->set_key(key); @@ -128,7 +130,7 @@ mac->update(&input[1], input.size() - 2); mac->update(input[input.size() - 1]); - result.test_eq(provider + " split mac", mac->verify_mac(expected.data(), expected.size()), true); + result.test_is_true(provider + " split mac", mac->verify_mac(expected.data(), expected.size())); } mac->clear(); diff -Nru botan3-3.7.1+dfsg/src/tests/test_mceliece.cpp botan3-3.12.0+dfsg/src/tests/test_mceliece.cpp --- botan3-3.7.1+dfsg/src/tests/test_mceliece.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_mceliece.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -14,7 +14,7 @@ #include #include #include - #include + #include #if defined(BOTAN_HAS_HMAC_DRBG) #include @@ -57,10 +57,10 @@ Botan::HMAC_DRBG rng("SHA-384"); rng.initialize_with(keygen_seed.data(), keygen_seed.size()); - Botan::McEliece_PrivateKey mce_priv(rng, keygen_n, keygen_t); + const Botan::McEliece_PrivateKey mce_priv(rng, keygen_n, keygen_t); - result.test_eq("public key fingerprint", hash_bytes(mce_priv.public_key_bits()), fprint_pub); - result.test_eq("private key fingerprint", hash_bytes(mce_priv.private_key_bits()), fprint_priv); + result.test_bin_eq("public key fingerprint", hash_bytes(mce_priv.public_key_bits()), fprint_pub); + result.test_bin_eq("private key fingerprint", hash_bytes(mce_priv.private_key_bits()), fprint_priv); rng.clear(); rng.initialize_with(encrypt_seed.data(), encrypt_seed.size()); @@ -74,9 +74,9 @@ Botan::secure_vector dec_shared_key = kem_dec.decrypt(kem_result.encapsulated_shared_key(), 64, {}); - result.test_eq("ciphertext", kem_result.encapsulated_shared_key(), ciphertext); - result.test_eq("encrypt shared", kem_result.shared_key(), shared_key); - result.test_eq("decrypt shared", dec_shared_key, shared_key); + result.test_bin_eq("ciphertext", kem_result.encapsulated_shared_key(), ciphertext); + result.test_bin_eq("encrypt shared", kem_result.shared_key(), shared_key); + result.test_bin_eq("decrypt shared", dec_shared_key, shared_key); } catch(Botan::Lookup_Error&) {} result.end_timer(); @@ -135,27 +135,27 @@ std::vector results; - for(size_t i = 0; i < sizeof(param_sets) / sizeof(param_sets[0]); ++i) { - if(Test::run_long_tests() == false && param_sets[i].code_length >= 2048) { + for(const auto& params : param_sets) { + if(Test::run_long_tests() == false && params.code_length >= 2048) { continue; } - for(size_t t = param_sets[i].t_min; t <= param_sets[i].t_max; ++t) { + for(size_t t = params.t_min; t <= params.t_max; ++t) { Test::Result result("McEliece keygen"); result.start_timer(); - Botan::McEliece_PrivateKey sk1(this->rng(), param_sets[i].code_length, t); + const Botan::McEliece_PrivateKey sk1(this->rng(), params.code_length, t); const Botan::McEliece_PublicKey& pk1 = sk1; const std::vector pk_enc = pk1.public_key_bits(); const Botan::secure_vector sk_enc = sk1.private_key_bits(); - Botan::McEliece_PublicKey pk(pk_enc); - Botan::McEliece_PrivateKey sk(sk_enc); + const Botan::McEliece_PublicKey pk(pk_enc); + const Botan::McEliece_PrivateKey sk(sk_enc); - 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(this->rng(), false), true); + result.test_str_eq("decoded public key equals original", fingerprint(pk1), fingerprint(pk)); + result.test_str_eq("decoded private key equals original", fingerprint(sk1), fingerprint(sk)); + result.test_is_true("key validation passes", sk.check_key(this->rng(), false)); result.end_timer(); result.end_timer(); @@ -189,7 +189,7 @@ Botan::secure_vector shared_key2 = dec_op.decrypt(kem_result.encapsulated_shared_key(), 64, salt); - result.test_eq("same key", kem_result.shared_key(), shared_key2); + result.test_bin_eq("same key", kem_result.shared_key(), shared_key2); } result.end_timer(); return result; diff -Nru botan3-3.7.1+dfsg/src/tests/test_ml_dsa.cpp botan3-3.12.0+dfsg/src/tests/test_ml_dsa.cpp --- botan3-3.7.1+dfsg/src/tests/test_ml_dsa.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_ml_dsa.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -27,7 +27,7 @@ std::string default_padding(const VarMap& /*unused*/) const override { return "Pure"; } std::unique_ptr load_public_key(const VarMap& vars) override { - Botan::ML_DSA_Mode mode(vars.get_req_str("Mode")); + const Botan::ML_DSA_Mode mode(vars.get_req_str("Mode")); return std::make_unique(vars.get_req_bin("Key"), mode); } }; diff -Nru botan3-3.7.1+dfsg/src/tests/test_modes.cpp botan3-3.12.0+dfsg/src/tests/test_modes.cpp --- botan3-3.7.1+dfsg/src/tests/test_modes.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_modes.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,10 +10,15 @@ #if defined(BOTAN_HAS_CIPHER_MODES) #include + #include + #include + #include #endif namespace Botan_Tests { +namespace { + #if defined(BOTAN_HAS_CIPHER_MODES) class Cipher_Mode_Tests final : public Text_Based_Test { @@ -55,18 +60,19 @@ return result; } - result.test_eq("enc and dec granularity is the same", enc->update_granularity(), dec->update_granularity()); + result.test_sz_eq( + "enc and dec granularity is the same", enc->update_granularity(), dec->update_granularity()); - result.test_gt("update granularity is non-zero", enc->update_granularity(), 0); + result.test_sz_gt("update granularity is non-zero", enc->update_granularity(), 0); - result.test_eq( + result.test_sz_eq( "enc and dec ideal granularity is the same", enc->ideal_granularity(), dec->ideal_granularity()); - result.test_gt( + result.test_sz_gt( "ideal granularity is at least update granularity", enc->ideal_granularity(), enc->update_granularity()); - result.confirm("ideal granularity is a multiple of update granularity", - enc->ideal_granularity() % enc->update_granularity() == 0); + result.test_is_true("ideal granularity is a multiple of update granularity", + enc->ideal_granularity() % enc->update_granularity() == 0); try { test_mode(result, algo, provider_ask, "encryption", *enc, key, nonce, input, expected, this->rng()); @@ -75,6 +81,7 @@ } try { + // NOLINTNEXTLINE(*-suspicious-call-argument) intentionally swapping ptext and ctext arguments here 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()); @@ -98,25 +105,25 @@ const bool is_cbc = (algo.find("/CBC") != std::string::npos); const bool is_ctr = (algo.find("CTR") != std::string::npos); - result.test_eq("name", mode.name(), algo); + result.test_str_eq("name", mode.name(), algo); // Some modes report base even if got from another provider if(mode.provider() != "base") { - result.test_eq("provider", mode.provider(), provider); + result.test_str_eq("provider", mode.provider(), provider); } - result.test_eq("mode not authenticated", mode.authenticated(), false); + result.test_is_false("mode not authenticated", mode.authenticated()); const size_t update_granularity = mode.update_granularity(); const size_t min_final_bytes = mode.minimum_final_size(); // FFI currently requires this, so assure it is true for all modes - result.test_gt("buffer sizes ok", mode.ideal_granularity(), min_final_bytes); + result.test_sz_gt("buffer sizes ok", mode.ideal_granularity(), min_final_bytes); - result.test_eq("key not set", mode.has_keying_material(), false); + result.test_is_false("key not set", mode.has_keying_material()); - result.test_throws("Unkeyed object throws", [&]() { - Botan::secure_vector bad(update_granularity); + result.test_throws("Unkeyed object throws", [&]() { + Botan::secure_vector bad(min_final_bytes); mode.finish(bad); }); @@ -124,40 +131,45 @@ // can't test equal due to CBC padding if(direction == "encryption") { - result.test_lte("output_length", mode.output_length(input.size()), expected.size()); + result.test_sz_lte("output_length", mode.output_length(input.size()), expected.size()); } else { - result.test_gte("output_length", mode.output_length(input.size()), expected.size()); + result.test_sz_gte("output_length", mode.output_length(input.size()), expected.size()); } } else { // assume all other modes are not expanding (currently true) - result.test_eq("output_length", mode.output_length(input.size()), expected.size()); + result.test_sz_eq("output_length", mode.output_length(input.size()), expected.size()); } - result.confirm("default nonce size is allowed", mode.valid_nonce_length(mode.default_nonce_length())); + result.test_is_true("default nonce size is allowed", mode.valid_nonce_length(mode.default_nonce_length())); // Test that disallowed nonce sizes result in an exception static constexpr size_t large_nonce_size = 65000; - 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); }); + result.test_is_false("Large nonce not allowed", mode.valid_nonce_length(large_nonce_size)); + result.test_throws("Large nonce causes exception", + [&mode]() { mode.start(nullptr, large_nonce_size); }); Botan::secure_vector garbage = rng.random_vec(update_granularity); + Botan::secure_vector ultimate_garbage = rng.random_vec(min_final_bytes); // 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); }); + 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(ultimate_garbage); }); mode.set_key(mutate_vec(key, rng)); - if(is_ctr == false) { - result.test_throws("Cannot process data until nonce is set", [&]() { mode.update(garbage); }); + if(!is_ctr) { + result.test_throws("Cannot process data until nonce is set", + [&]() { mode.update(garbage); }); } mode.start(mutate_vec(nonce, rng)); mode.reset(); - if(is_ctr == false) { - result.test_throws("Cannot process data until nonce is set (after start/reset)", - [&]() { mode.update(garbage); }); + if(!is_ctr) { + result.test_throws("Cannot process data until nonce is set (after start/reset)", + [&]() { mode.update(garbage); }); } mode.start(mutate_vec(nonce, rng)); @@ -166,14 +178,56 @@ mode.reset(); mode.set_key(key); - result.test_eq("key is set", mode.has_keying_material(), true); + result.test_is_true("key is set", mode.has_keying_material()); mode.start(nonce); Botan::secure_vector buf; buf.assign(input.begin(), input.end()); mode.finish(buf); - result.test_eq(direction + " all-in-one", buf, expected); + result.test_bin_eq(direction + " all-in-one", buf, expected); + + // Test finish() with non-zero offset + { + const size_t test_offset = 1 + rng.next_byte() % 32; + buf.assign(test_offset, 0xAB); + buf.insert(buf.end(), input.begin(), input.end()); + + mode.start(nonce); + mode.finish(buf, test_offset); + + for(size_t i = 0; i < test_offset; ++i) { + result.test_u8_eq(direction + " prefix byte", buf[i], 0xAB); + } + result.test_bin_eq(direction + " finish with offset", std::span{buf}.subspan(test_offset), expected); + } + + // Test update() + finish() with non-zero offset + if(input.size() >= update_granularity + min_final_bytes) { + const size_t test_offset = 1 + rng.next_byte() % 32; + const size_t max_blocks = (input.size() - min_final_bytes) / update_granularity; + const size_t bytes_to_update = max_blocks * update_granularity; + + buf.assign(test_offset, 0xAB); + buf.insert(buf.end(), input.begin(), input.begin() + bytes_to_update); + + Botan::secure_vector final_buf(test_offset, 0xAB); + final_buf.insert(final_buf.end(), input.begin() + bytes_to_update, input.end()); + + mode.start(nonce); + mode.update(buf, test_offset); + mode.finish(final_buf, test_offset); + + for(size_t i = 0; i < test_offset; ++i) { + result.test_u8_eq(direction + " update offset prefix byte", buf[i], 0xAB); + result.test_u8_eq(direction + " finish offset prefix byte", final_buf[i], 0xAB); + } + + Botan::secure_vector combined; + combined.insert(combined.end(), buf.begin() + test_offset, buf.end()); + combined.insert(combined.end(), final_buf.begin() + test_offset, final_buf.end()); + result.test_bin_eq(direction + " update+finish with offset", combined, expected); + } // additionally test update() and process() if possible if(input.size() >= update_granularity + min_final_bytes) { @@ -197,7 +251,7 @@ mode.finish(last_bits); buf += last_bits; - result.test_eq(direction + " update-1", buf, expected); + result.test_bin_eq(direction + " update-1", buf, expected); } // test update with maximum length input @@ -210,7 +264,7 @@ buf += last_bits; - result.test_eq(direction + " update-all", buf, expected); + result.test_bin_eq(direction + " update-all", buf, expected); // test process with maximum length input mode.start(nonce); @@ -218,17 +272,17 @@ const size_t bytes_written = mode.process(buf.data(), bytes_to_process); - result.test_eq("correct number of bytes processed", bytes_written, bytes_to_process); + result.test_sz_eq("correct number of bytes processed", bytes_written, bytes_to_process); mode.finish(buf, bytes_to_process); - result.test_eq(direction + " process", buf, expected); + result.test_bin_eq(direction + " process", buf, expected); } mode.clear(); - result.test_eq("key is not set", mode.has_keying_material(), false); + result.test_is_false("key is not set", mode.has_keying_material()); - result.test_throws("Unkeyed object throws after clear", [&]() { - Botan::secure_vector bad(update_granularity); + result.test_throws("Unkeyed object throws after clear", [&]() { + Botan::secure_vector bad(min_final_bytes); mode.finish(bad); }); } @@ -269,17 +323,18 @@ enc->start(iv); enc->finish(msg1); - result.test_eq("First ciphertext", msg1, "9BDD7300E0CB61CA71FFF957A71605DB6836159C36781246A1ADF50982757F4B"); + result.test_bin_eq( + "First ciphertext", msg1, "9BDD7300E0CB61CA71FFF957A71605DB6836159C36781246A1ADF50982757F4B"); enc->start(); enc->finish(msg2); - result.test_eq("Second ciphertext", msg2, "AA8D682958A4A044735DAC502B274DB2"); + result.test_bin_eq("Second ciphertext", msg2, "AA8D682958A4A044735DAC502B274DB2"); enc->start(); enc->finish(msg3); - result.test_eq("Third ciphertext", msg3, "1241B9976F73051BCF809525D6E86C25"); + result.test_bin_eq("Third ciphertext", msg3, "1241B9976F73051BCF809525D6E86C25"); dec->start(iv); dec->finish(msg1); @@ -289,7 +344,7 @@ dec->start(); dec->finish(msg3); - result.test_eq("Third plaintext", msg3, "49562063617272796F76657232"); + result.test_bin_eq("Third plaintext", msg3, "49562063617272796F76657232"); #endif return result; @@ -315,29 +370,29 @@ enc->start(iv); enc->finish(msg1); - result.test_eq("First ciphertext", msg1, "a51522387c4c9b"); + result.test_bin_eq("First ciphertext", msg1, "a51522387c4c9b"); enc->start(); enc->finish(msg2); - result.test_eq("Second ciphertext", msg2, "105457dc2e0649d4"); + result.test_bin_eq("Second ciphertext", msg2, "105457dc2e0649d4"); enc->start(); enc->finish(msg3); - result.test_eq("Third ciphertext", msg3, "53bd65"); + result.test_bin_eq("Third ciphertext", msg3, "53bd65"); dec->start(iv); dec->finish(msg1); - result.test_eq("First plaintext", msg1, "ABCDEF01234567"); + result.test_bin_eq("First plaintext", msg1, "ABCDEF01234567"); dec->start(); dec->finish(msg2); - result.test_eq("Second plaintext", msg2, "0000123456ABCDEF"); + result.test_bin_eq("Second plaintext", msg2, "0000123456ABCDEF"); dec->start(); dec->finish(msg3); - result.test_eq("Third plaintext", msg3, "012345"); + result.test_bin_eq("Third plaintext", msg3, "012345"); #endif return result; } @@ -382,12 +437,12 @@ Botan::secure_vector msg(i, 0); enc->finish(msg); - result.test_eq("Ciphertext", msg, exp_ciphertext[i - 1].c_str()); + result.test_bin_eq("Ciphertext", msg, exp_ciphertext[i - 1]); dec->finish(msg); - for(size_t j = 0; j != msg.size(); ++j) { - result.test_eq("Plaintext zeros", static_cast(msg[j]), 0); + for(const uint8_t b : msg) { + result.test_u8_eq("Plaintext zeros", b, 0); } } #endif @@ -399,4 +454,6 @@ #endif +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_monty.cpp botan3-3.12.0+dfsg/src/tests/test_monty.cpp --- botan3-3.7.1+dfsg/src/tests/test_monty.cpp 1970-01-01 00:00:00.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_monty.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -0,0 +1,64 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "tests.h" + +#if defined(BOTAN_HAS_NUMBERTHEORY) + #include + #include + #include +#endif + +namespace Botan_Tests { + +namespace { + +#if defined(BOTAN_HAS_NUMBERTHEORY) + +class Montgomery_Integer_Tests : public Test { + public: + std::vector run() override { + auto rng = Test::new_rng(__func__); + + std::vector results; + + Botan::secure_vector ws; + + for(size_t i = 0; i != 100; ++i) { + Test::Result result("Montgomery_Int"); + + const size_t p_bits = (3 * i + 5); + auto p = Botan::random_prime(*rng, p_bits); + + const Botan::Montgomery_Params params(p); + + auto x = Botan::BigInt::random_integer(*rng, 1, p); + auto y = Botan::BigInt::random_integer(*rng, 1, p); + + auto monty_x = Botan::Montgomery_Int(params, x, true); + auto monty_y = Botan::Montgomery_Int(params, y, true); + + result.test_bn_eq("Montgomery addition", (monty_x + monty_y).value(), (x + y) % p); + result.test_bn_eq("Montgomery subtraction", (monty_x - monty_y).value(), (x - y) % p); + result.test_bn_eq("Montgomery multiplication", (monty_x.mul(monty_y, ws)).value(), (x * y) % p); + + result.test_bn_eq("Montgomery square x", (monty_x.square(ws)).value(), (x * x) % p); + result.test_bn_eq("Montgomery square y", (monty_y.square(ws)).value(), (y * y) % p); + + results.push_back(result); + } + + return results; + } +}; + +BOTAN_REGISTER_TEST("math", "monty_int", Montgomery_Integer_Tests); + +#endif + +} // namespace + +} // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_mp.cpp botan3-3.12.0+dfsg/src/tests/test_mp.cpp --- botan3-3.7.1+dfsg/src/tests/test_mp.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_mp.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -38,13 +38,13 @@ Botan::word a = 2; Botan::word c = Botan::bigint_cnd_add(0, &a, &max, 1); - result.test_int_eq(a, 2, "No op"); - result.test_int_eq(c, 0, "No op"); + result.test_u64_eq("No op", a, 2); + result.test_u64_eq("No op", c, 0); c = Botan::bigint_cnd_add(1, &a, &max, 1); - result.test_int_eq(a, 1, "Add"); - result.test_int_eq(c, 1, "Carry"); + result.test_u64_eq("Add", a, 1); + result.test_u64_eq("Carry", c, 1); // TODO more tests @@ -55,16 +55,16 @@ Result result("bigint_cnd_sub"); Botan::word a = 2; - Botan::word b = 3; + const Botan::word b = 3; Botan::word c = Botan::bigint_cnd_sub(0, &a, &b, 1); - result.test_int_eq(a, 2, "No op"); - result.test_int_eq(c, 0, "No op"); + result.test_u64_eq("No op", a, 2); + result.test_u64_eq("No op", c, 0); c = Botan::bigint_cnd_sub(1, &a, &b, 1); - result.test_int_eq(a, ~static_cast(0), "Sub"); - result.test_int_eq(c, 1, "Borrow"); + result.test_u64_eq("Sub", a, ~static_cast(0)); + result.test_u64_eq("Borrow", c, 1); return result; } @@ -76,25 +76,25 @@ Botan::word x1 = max; Botan::bigint_cnd_abs(1, &x1, 1); - result.test_int_eq(x1, 1, "Abs"); + result.test_u64_eq("Abs", x1, 1); x1 = 0; Botan::bigint_cnd_abs(1, &x1, 1); - result.test_int_eq(x1, 0, "Abs"); + result.test_u64_eq("Abs", x1, 0); x1 = 1; Botan::bigint_cnd_abs(1, &x1, 1); - result.test_int_eq(x1, max, "Abs"); + result.test_u64_eq("Abs", x1, max); x1 = 1; Botan::bigint_cnd_abs(0, &x1, 1); - result.test_int_eq(x1, 1, "No change"); + result.test_u64_eq("No change", x1, 1); Botan::word x2[2] = {max, max}; Botan::bigint_cnd_abs(1, x2, 2); - result.test_int_eq(x2[0], 1, "Abs"); - result.test_int_eq(x2[1], 0, "Abs"); + result.test_u64_eq("Abs", x2[0], 1); + result.test_u64_eq("Abs", x2[1], 0); return result; } @@ -106,12 +106,13 @@ Botan::bigint_cnd_swap(0, nullptr, nullptr, 0); Botan::bigint_cnd_swap(1, nullptr, nullptr, 0); - Botan::word x1 = 5, y1 = 9; + Botan::word x1 = 5; + Botan::word y1 = 9; Botan::bigint_cnd_swap(0, &x1, &y1, 1); - result.test_int_eq(x1, 5, "No swap"); + result.test_u64_eq("No swap", x1, 5); Botan::bigint_cnd_swap(1, &x1, &y1, 1); - result.test_int_eq(x1, 9, "Swap"); + result.test_u64_eq("Swap", x1, 9); Botan::word x5[5] = {0, 1, 2, 3, 4}; Botan::word y5[5] = {3, 2, 1, 0, 9}; @@ -120,14 +121,14 @@ Botan::bigint_cnd_swap(1, x5, y5, 4); for(size_t i = 0; i != 4; ++i) { - result.test_int_eq(x5[i], 3 - i, "Swap x5"); + result.test_u64_eq("Swap x5", x5[i], static_cast(3 - i)); } - result.test_int_eq(x5[4], 4, "Not touched"); + result.test_u64_eq("Not touched", x5[4], 4); for(size_t i = 0; i != 4; ++i) { - result.test_int_eq(y5[i], i, "Swap y5"); + result.test_u64_eq("Swap y5", y5[i], static_cast(i)); } - result.test_int_eq(y5[4], 9, "Not touched"); + result.test_u64_eq("Not touched", y5[4], 9); return result; } diff -Nru botan3-3.7.1+dfsg/src/tests/test_name_constraint.cpp botan3-3.12.0+dfsg/src/tests/test_name_constraint.cpp --- botan3-3.7.1+dfsg/src/tests/test_name_constraint.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_name_constraint.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,5 +1,6 @@ /* * (C) 2015,2016 Kai Michaelis +* 2026 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -7,12 +8,13 @@ #include "tests.h" #if defined(BOTAN_HAS_X509_CERTIFICATES) + #include + #include + #include #include #include #endif -#include - namespace Botan_Tests { namespace { @@ -44,12 +46,12 @@ std::vector results; const Botan::Path_Validation_Restrictions restrictions(false, 80); - std::chrono::system_clock::time_point validation_time = + const std::chrono::system_clock::time_point validation_time = Botan::calendar_point(2016, 10, 21, 4, 20, 0).to_std_timepoint(); for(const auto& t : test_cases) { - Botan::X509_Certificate root(Test::data_file("x509/name_constraint/" + std::get<0>(t))); - Botan::X509_Certificate sub(Test::data_file("x509/name_constraint/" + std::get<1>(t))); + const Botan::X509_Certificate root(Test::data_file("x509/name_constraint/" + std::get<0>(t))); + const Botan::X509_Certificate sub(Test::data_file("x509/name_constraint/" + std::get<1>(t))); Botan::Certificate_Store_In_Memory trusted; Test::Result result("X509v3 Name Constraints: " + std::get<1>(t)); @@ -61,7 +63,7 @@ path_result = Botan::Path_Validation_Result(Botan::Certificate_Status_Code::CANNOT_ESTABLISH_TRUST); } - result.test_eq("validation result", path_result.result_string(), std::get<3>(t)); + result.test_str_eq("validation result", path_result.result_string(), std::get<3>(t)); results.emplace_back(result); } @@ -71,6 +73,221 @@ BOTAN_REGISTER_TEST("x509", "x509_path_name_constraint", Name_Constraint_Tests); +// Verify that DNS constraints are case-insensitive also when falling back to the CN +class Name_Constraint_Excluded_CN_Case_Test final : public Test { + public: + std::vector run() override { + Test::Result result("X509v3 Name Constraints: excluded DNS with mixed-case CN and no SAN"); + + const Botan::X509_Certificate root( + Test::data_file("x509/name_constraint/Root_DNS_Excluded_Mixed_Case_CN.crt")); + const Botan::X509_Certificate leaf( + Test::data_file("x509/name_constraint/Invalid_DNS_Excluded_Mixed_Case_CN.crt")); + + Botan::Certificate_Store_In_Memory trusted; + trusted.add_certificate(root); + + const Botan::Path_Validation_Restrictions restrictions(false, 80); + const auto validation_time = Botan::calendar_point(2026, 6, 1, 0, 0, 0).to_std_timepoint(); + + const auto path_result = Botan::x509_path_validate( + leaf, restrictions, trusted, "" /* hostname */, Botan::Usage_Type::UNSPECIFIED, validation_time); + + result.test_str_eq( + "validation result", path_result.result_string(), "Certificate does not pass name constraint"); + + return {result}; + } +}; + +BOTAN_REGISTER_TEST("x509", "x509_name_constraint_excluded_cn_case", Name_Constraint_Excluded_CN_Case_Test); + +class Name_Constraint_Empty_Subject_Test final : public Test { + public: + std::vector run() override { + /* + - `root.pem`: + Self-signed CA, `O=Acme NC Root, C=US` + - `intermediate.pem`: + CA signed by root, `O=Acme NC Intermediate, C=US`, critical `nameConstraints` with + `permittedSubtrees: [directoryName=O=Acme, C=US]` + - `leaf.pem`: + End-entity signed by the intermediate, empty subject DN and critical SAN + `directoryName=CN=server, O=Acme, C=US` + */ + Test::Result result("X509v3 Name Constraints: empty subject + SAN directoryName inside permittedSubtrees"); + + const Botan::X509_Certificate root(Test::data_file("x509/name_constraint_empty_subject/root.pem")); + const Botan::X509_Certificate intermediate( + Test::data_file("x509/name_constraint_empty_subject/intermediate.pem")); + const Botan::X509_Certificate leaf(Test::data_file("x509/name_constraint_empty_subject/leaf.pem")); + + result.test_is_true("Leaf subject DN is empty", leaf.subject_dn().empty()); + result.test_sz_eq("Leaf SAN has one directoryName entry", leaf.subject_alt_name().directory_names().size(), 1); + + Botan::Certificate_Store_In_Memory trusted; + trusted.add_certificate(root); + + const std::vector chain{leaf, intermediate}; + const Botan::Path_Validation_Restrictions restrictions(false, 80); + const auto validation_time = Botan::calendar_point(2027, 1, 1, 0, 0, 0).to_std_timepoint(); + + const auto path_result = Botan::x509_path_validate( + chain, restrictions, trusted, "", Botan::Usage_Type::UNSPECIFIED, validation_time); + + result.test_str_eq("validation result", path_result.result_string(), "Verified"); + + return {result}; + } +}; + +BOTAN_REGISTER_TEST("x509", "x509_name_constraint_empty_subject", Name_Constraint_Empty_Subject_Test); + +class Name_Constraint_IPv6_Chain_Tests final : public Test { + public: + std::vector run() override { + struct Case { + std::string label; + std::string dir; + std::vector intermediates; + std::string leaf; + bool accept; + }; + + const std::vector cases = { + // IPv6 permittedSubtree 2001:db8::/32 + {"IPv6 permit: SAN inside subtree", "permitted", {}, "leaf_valid.pem", true}, + {"IPv6 permit: SAN outside subtree", "permitted", {}, "leaf_invalid.pem", false}, + + // IPv6 excludedSubtree 2001:db8::/32 + {"IPv6 exclude: SAN outside subtree", "excluded", {}, "leaf_valid.pem", true}, + {"IPv6 exclude: SAN inside subtree", "excluded", {}, "leaf_invalid.pem", false}, + + // Root permits only IPv4 10.0.0.0/8, so an IPv6 SAN must be rejected + // because iPAddress is a single GeneralName form (RFC 5280 4.2.1.10). + {"IPv4-only permit: IPv4 SAN", "cross_v4only", {}, "leaf_valid.pem", true}, + {"IPv4-only permit: IPv6 SAN", "cross_v4only", {}, "leaf_invalid.pem", false}, + + // Similar to previous - root permits only IPv6 2001:db8::/32 so IPv4 must be rejected + {"IPv6-only permit: IPv6 SAN", "cross_v6only", {}, "leaf_valid.pem", true}, + {"IPv6-only permit: IPv4 SAN", "cross_v6only", {}, "leaf_invalid.pem", false}, + + // Constraints across multiple issuers + // - root permits {10/8, 2001:db8::/32} + // - intermediate narrows permits to {10.1/16, 2001:db8:cafe::/48} and excludes + // {10.1.99/24, 2001:db8:cafe:bad::/64}. + // + // Every leaf has one IPv4 and one IPv6 SAN + {"Mixed v4+v6: all SANs in range", "mixed_multi", {"int.pem"}, "leaf_valid.pem", true}, + {"Mixed v4+v6: IPv4 outside int permit", "mixed_multi", {"int.pem"}, "leaf_invalid_int_v4.pem", false}, + {"Mixed v4+v6: IPv6 outside int permit", "mixed_multi", {"int.pem"}, "leaf_invalid_int_v6.pem", false}, + {"Mixed v4+v6: IPv4 hits int exclude", "mixed_multi", {"int.pem"}, "leaf_invalid_excl_v4.pem", false}, + {"Mixed v4+v6: IPv6 hits int exclude", "mixed_multi", {"int.pem"}, "leaf_invalid_excl_v6.pem", false}, + {"Mixed v4+v6: IPv4 outside root permit", "mixed_multi", {"int.pem"}, "leaf_invalid_root_v4.pem", false}, + {"Mixed v4+v6: IPv6 outside root permit", "mixed_multi", {"int.pem"}, "leaf_invalid_root_v6.pem", false}, + + // Here the root excludes IPv4 10.0.0.0/8 and the leaf certs have IPv6 SAN; the + // invalid leaf has a IPv4-mapped IPv6 address matching 10.0.0.0/8 while the valid leaf + // has some other IPv6 address which is not excluded + {"v4 exclude: mapped-v6 SAN inside v4 excl", "v4_exclude_mapped", {}, "leaf_invalid.pem", false}, + {"v4 exclude: mapped-v6 SAN outside v4 excl", "v4_exclude_mapped", {}, "leaf_valid.pem", true}, + + // Here the root permits only IPv4 10.0.0.0/8. The invalid leaf has an IPv6 address in the SAN + // which should be rejected as not being in the range. The 'valid' leaf is a questionable case: it + // has an IPv6 SAN which is an IPv4-mapped IPv6 address inside 10.0.0.0/8. Arguably it really is + // valid; RFC 5280 is silent on the issue. But lacking a clear consensus, it is rejected for now. + {"v4 permit: mapped-v6 SAN inside v4 permit", "v4_permit_mapped", {}, "leaf_valid.pem", false}, + {"v4 permit: mapped-v6 SAN outside v4 permit", "v4_permit_mapped", {}, "leaf_invalid.pem", false}, + }; + + const Botan::Path_Validation_Restrictions restrictions(false, 80); + + const auto validation_time = Botan::calendar_point(2027, 4, 22, 20, 0, 0).to_std_timepoint(); + + std::vector results; + for(const auto& c : cases) { + const std::string base = "x509/name_constraint_ipv6/" + c.dir + "/"; + const Botan::X509_Certificate root(Test::data_file(base + "root.pem")); + const Botan::X509_Certificate leaf(Test::data_file(base + c.leaf)); + + std::vector chain{leaf}; + for(const auto& intermediate_file : c.intermediates) { + chain.emplace_back(Test::data_file(base + intermediate_file)); + } + + Botan::Certificate_Store_In_Memory trusted; + trusted.add_certificate(root); + + const auto pv = Botan::x509_path_validate( + chain, restrictions, trusted, "" /* hostname */, Botan::Usage_Type::UNSPECIFIED, validation_time); + + Test::Result result("X509v3 Name Constraints (IPv6 chains): " + c.label); + + const std::string expected = c.accept ? "Verified" : "Certificate does not pass name constraint"; + result.test_str_eq("path validation result", pv.result_string(), expected); + results.emplace_back(std::move(result)); + } + + return results; + } +}; + +BOTAN_REGISTER_TEST("x509", "x509_name_constraint_ipv6_chains", Name_Constraint_IPv6_Chain_Tests); + +/* +* Validate that GeneralName iPAddress decoding rejects masks that are not a +* contiguous CIDR prefix. Drives the decoder with hand-rolled BER for a +* single [7] IMPLICIT OCTET STRING carrying {net || mask}. +*/ +class Name_Constraint_IP_Mask_Tests final : public Text_Based_Test { + public: + Name_Constraint_IP_Mask_Tests() : Text_Based_Test("x509/general_name_ip.vec", "Address,Netmask") {} + + Test::Result run_one_test(const std::string& header, const VarMap& vars) override { + Test::Result result("GeneralName iPAddress mask validation"); + + const auto address = vars.get_req_bin("Address"); + const auto netmask = vars.get_req_bin("Netmask"); + + const auto der = encode_address(address, netmask); + + Botan::BER_Decoder decoder(der, Botan::BER_Decoder::Limits::DER()); + Botan::GeneralName gn; + + if(header == "Valid") { + try { + gn.decode_from(decoder); + result.test_success("Accepted valid GeneralName IP encoding"); + } catch(Botan::Decoding_Error&) { + result.test_failure("Rejected valid GeneralName IP encoding"); + } + } else { + try { + gn.decode_from(decoder); + result.test_failure("Accepted invalid GeneralName IP encoding"); + } catch(Botan::Decoding_Error&) { + result.test_success("Rejected invalid GeneralName IP encoding"); + } + } + + return result; + } + + private: + static std::vector encode_address(std::span address, std::span netmask) { + std::vector der; + // [7] IMPLICIT OCTET STRING, primitive, context-specific. + der.push_back(0x87); + // Short for length is sufficient here + der.push_back(static_cast(address.size() + netmask.size())); + der.insert(der.end(), address.begin(), address.end()); + der.insert(der.end(), netmask.begin(), netmask.end()); + return der; + } +}; + +BOTAN_REGISTER_TEST("x509", "x509_name_constraint_ip_mask", Name_Constraint_IP_Mask_Tests); + #endif } // namespace diff -Nru botan3-3.7.1+dfsg/src/tests/test_ocb.cpp botan3-3.12.0+dfsg/src/tests/test_ocb.cpp --- botan3-3.7.1+dfsg/src/tests/test_ocb.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_ocb.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -44,7 +44,7 @@ Botan::Key_Length_Specification key_spec() const override { return Botan::Key_Length_Specification(m_bs); } void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override { - while(blocks) { + while(blocks > 0) { Botan::copy_mem(out, in, m_bs); Botan::poly_double_n(out, m_bs); @@ -59,14 +59,14 @@ } void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override { - while(blocks) { + while(blocks > 0) { for(size_t i = 0; i != m_bs; ++i) { out[i] = in[i] ^ m_key[i]; } uint8_t carry = in[m_bs - 1] & 0x01; - if(carry) { + if(carry != 0) { if(m_bs == 16 || m_bs == 24) { out[m_bs - 1] ^= 0x87; } else if(m_bs == 32) { @@ -121,14 +121,14 @@ enc.set_associated_data(ad); enc.start(nonce); enc.finish(buf); - result.test_eq("Ciphertext matches", buf, expected); + result.test_bin_eq("Ciphertext matches", buf, expected); Botan::OCB_Decryption dec(std::make_unique(bs), std::min(bs, 32)); dec.set_key(key); dec.set_associated_data(ad); dec.start(nonce); dec.finish(buf); - result.test_eq("Decryption correct", buf, input); + result.test_bin_eq("Decryption correct", buf, input); return result; } @@ -209,20 +209,20 @@ S[j] = static_cast(0x50 + j); } - Botan::store_be(static_cast(3 * i + 1), &N[0]); + Botan::store_be(static_cast(3 * i + 1), N.data()); ocb_encrypt(result, C, enc, N, S, S); - Botan::store_be(static_cast(3 * i + 2), &N[0]); + Botan::store_be(static_cast(3 * i + 2), N.data()); ocb_encrypt(result, C, enc, N, S, empty); - Botan::store_be(static_cast(3 * i + 3), &N[0]); + Botan::store_be(static_cast(3 * i + 3), N.data()); ocb_encrypt(result, C, enc, N, empty, S); } - Botan::store_be(static_cast(385), &N[0]); + Botan::store_be(static_cast(385), N.data()); std::vector final_result; ocb_encrypt(result, final_result, enc, N, empty, C); - result.test_eq("correct value", final_result, expected); + result.test_bin_eq("correct value", final_result, expected); return result; } @@ -292,7 +292,7 @@ std::vector final_result; ocb_encrypt(result, final_result, enc, dec, N, empty, C); - result.test_eq("correct value", final_result, expected); + result.test_bin_eq("correct value", final_result, expected); return result; } @@ -320,7 +320,7 @@ dec.finish(buf, 0); - result.test_eq("OCB round tripped", buf, pt); + result.test_bin_eq("OCB round tripped", buf, pt); } catch(std::exception& e) { result.test_failure("OCB round trip error", e.what()); } @@ -352,7 +352,7 @@ bool has_keying_material() const override { return m_has_key; } - void key_schedule(std::span) override { m_has_key = true; } + void key_schedule(std::span /*key*/) override { m_has_key = true; } Botan::Key_Length_Specification key_spec() const override { return Botan::Key_Length_Specification(m_bs); } diff -Nru botan3-3.7.1+dfsg/src/tests/test_ocsp.cpp botan3-3.12.0+dfsg/src/tests/test_ocsp.cpp --- botan3-3.7.1+dfsg/src/tests/test_ocsp.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_ocsp.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,15 +8,17 @@ #include "tests.h" #if defined(BOTAN_HAS_OCSP) + #include "test_arb_eq.h" #include #include #include #include - #include #endif namespace Botan_Tests { +namespace { + #if defined(BOTAN_HAS_OCSP) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1) && \ defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) @@ -39,18 +41,44 @@ for(const std::string& ocsp_input_path : ocsp_input_paths) { try { - Botan::OCSP::Response resp(Test::read_binary_data_file(ocsp_input_path)); - result.confirm("parsing was successful", resp.status() == Botan::OCSP::Response_Status_Code::Successful); + const Botan::OCSP::Response resp(Test::read_binary_data_file(ocsp_input_path)); + result.test_enum_eq( + "parsing was successful", resp.status(), Botan::OCSP::Response_Status_Code::Successful); result.test_success("Parsed input " + ocsp_input_path); } catch(Botan::Exception& e) { result.test_failure("Parsing failed", e.what()); } } - Botan::OCSP::Response resp( + const Botan::OCSP::Response resp( Test::read_binary_data_file("x509/ocsp/patrickschmidt_ocsp_try_later_wrong_sig.der")); - result.confirm("parsing exposes correct status code", - resp.status() == Botan::OCSP::Response_Status_Code::Try_Later); + result.test_enum_eq( + "parsing exposes correct status code", resp.status(), Botan::OCSP::Response_Status_Code::Try_Later); + + return result; + } + + static Test::Result test_response_with_bykey_responder_id() { + // RFC 6960's ASN.1 module is "DEFINITIONS EXPLICIT TAGS" and ResponderID's + // CHOICE alternatives are not marked IMPLICIT, so byKey is encoded as + // constructed [2] wrapping a primitive OCTET STRING. The data below + // was produced using `openssl ocsp ... -resp_key_id` + Test::Result result("OCSP response with byKey ResponderID"); + + const Botan::OCSP::Response resp(Test::read_binary_data_file("x509/ocsp/byKey_responderID.der")); + result.test_enum_eq( + "Successful response status", resp.status(), Botan::OCSP::Response_Status_Code::Successful); + + const auto responder = load_test_X509_cert("x509/ocsp/byKey_responder.pem"); + + result.test_is_true("byKey response has empty signer_name", resp.signer_name().empty()); + result.test_sz_eq("byKey hash is 20 bytes (SHA-1)", resp.signer_key_hash().size(), 20); + result.test_bin_eq("byKey hash matches responder pubkey SHA-1", + resp.signer_key_hash(), + responder.subject_public_key_bitstring_sha1()); + + test_arb_eq( + result, "Responder is found via byKey", resp.find_signing_certificate(responder), std::optional(responder)); return result; } @@ -59,19 +87,19 @@ Test::Result result("OCSP response certificate access"); try { - Botan::OCSP::Response resp1(Test::read_binary_data_file("x509/ocsp/resp1.der")); + const Botan::OCSP::Response resp1(Test::read_binary_data_file("x509/ocsp/resp1.der")); const auto& certs1 = resp1.certificates(); - if(result.test_eq("Expected count of certificates", certs1.size(), 1)) { + if(result.test_sz_eq("Expected count of certificates", certs1.size(), 1)) { const auto& cert = certs1.front(); const Botan::X509_DN expected_dn( {std::make_pair("X520.CommonName", "Symantec Class 3 EV SSL CA - G3 OCSP Responder")}); const bool matches = cert.subject_dn() == expected_dn; - result.test_eq("CN matches expected", matches, true); + result.test_is_true("CN matches expected", matches); } - Botan::OCSP::Response resp2(Test::read_binary_data_file("x509/ocsp/resp2.der")); + const Botan::OCSP::Response resp2(Test::read_binary_data_file("x509/ocsp/resp2.der")); const auto& certs2 = resp2.certificates(); - result.test_eq("Expect no certificates", certs2.size(), 0); + result.test_sz_eq("Expect no certificates", certs2.size(), 0); } catch(Botan::Exception& e) { result.test_failure("Parsing failed", e.what()); } @@ -96,10 +124,10 @@ "ME4wTKADAgEAMEUwQzBBMAkGBSsOAwIaBQAEFPLgavmFih2NcJtJGSN6qbUaKH5kBBRK3QYWG7z2aLV29YG2u2IaulqBLwIIQkg+DF+RYMY="; const Botan::OCSP::Request req1(issuer, end_entity); - result.test_eq("Encoded OCSP request", req1.base64_encode(), expected_request); + result.test_str_eq("Encoded OCSP request", req1.base64_encode(), expected_request); const Botan::OCSP::Request req2(issuer, BigInt::from_bytes(end_entity.serial_number())); - result.test_eq("Encoded OCSP request", req2.base64_encode(), expected_request); + result.test_str_eq("Encoded OCSP request", req2.base64_encode(), expected_request); return result; } @@ -107,8 +135,6 @@ static Test::Result test_response_find_signing_certificate() { Test::Result result("OCSP response finding signature certificates"); - const std::optional nullopt_cert; - // OCSP response is signed by the issuing CA itself auto randombit_ocsp = load_test_OCSP_resp("x509/ocsp/randombit_ocsp.der"); auto randombit_ca = load_test_X509_cert("x509/ocsp/letsencrypt.pem"); @@ -120,13 +146,13 @@ auto bdr_ca = load_test_X509_cert("x509/ocsp/bdr-int.pem"); // The response in bdr_ocsp contains two certificates - if(result.test_eq("both certificates found", bdr_ocsp.certificates().size(), 2)) { - result.test_eq("first cert in response", - bdr_ocsp.certificates()[0].subject_info("X520.CommonName").at(0), - "D-TRUST OCSP 4 2-2 EV 2016"); - result.test_eq("second cert in response", - bdr_ocsp.certificates()[1].subject_info("X520.CommonName").at(0), - "D-TRUST CA 2-2 EV 2016"); + if(result.test_sz_eq("both certificates found", bdr_ocsp.certificates().size(), 2)) { + result.test_str_eq("first cert in response", + bdr_ocsp.certificates()[0].subject_info("X520.CommonName").at(0), + "D-TRUST OCSP 4 2-2 EV 2016"); + result.test_str_eq("second cert in response", + bdr_ocsp.certificates()[1].subject_info("X520.CommonName").at(0), + "D-TRUST CA 2-2 EV 2016"); } // Dummy OCSP response is not signed at all @@ -138,28 +164,30 @@ auto randombit_alt_resp_ocsp = load_test_OCSP_resp("x509/ocsp/randombit_ocsp_forged_valid_nocerts.der"); auto randombit_alt_resp_cert = load_test_X509_cert("x509/ocsp/randombit_ocsp_forged_responder.pem"); - result.test_is_eq("Dummy has no signing certificate", - dummy_ocsp.find_signing_certificate(Botan::X509_Certificate()), - nullopt_cert); - - result.test_is_eq("CA is returned as signing certificate", - randombit_ocsp.find_signing_certificate(randombit_ca), - std::optional(randombit_ca)); - result.test_is_eq("No signer certificate is returned when signer couldn't be determined", - randombit_ocsp.find_signing_certificate(bdr_ca), - nullopt_cert); - - result.test_is_eq("Delegated responder certificate is returned for further validation", - bdr_ocsp.find_signing_certificate(bdr_ca), - std::optional(bdr_responder)); - - result.test_is_eq("Delegated responder without stapled certs does not find signer without user-provided certs", - randombit_alt_resp_ocsp.find_signing_certificate(randombit_ca), - nullopt_cert); + result.test_opt_is_null("Dummy has no signing certificate", + dummy_ocsp.find_signing_certificate(Botan::X509_Certificate())); + + test_arb_eq(result, + "CA is returned as signing certificate", + randombit_ocsp.find_signing_certificate(randombit_ca), + std::optional(randombit_ca)); + result.test_opt_is_null("No signer certificate is returned when signer couldn't be determined", + randombit_ocsp.find_signing_certificate(bdr_ca)); + + test_arb_eq(result, + "Delegated responder certificate is returned for further validation", + bdr_ocsp.find_signing_certificate(bdr_ca), + std::optional(bdr_responder)); + + result.test_opt_is_null( + "Delegated responder without stapled certs does not find signer without user-provided certs", + randombit_alt_resp_ocsp.find_signing_certificate(randombit_ca)); + auto trusted_responders = std::make_unique(randombit_alt_resp_cert); - result.test_is_eq("Delegated responder returns user-provided cert", - randombit_alt_resp_ocsp.find_signing_certificate(randombit_ca, trusted_responders.get()), - std::optional(randombit_alt_resp_cert)); + test_arb_eq(result, + "Delegated responder returns user-provided cert", + randombit_alt_resp_ocsp.find_signing_certificate(randombit_ca, trusted_responders.get()), + std::optional(randombit_alt_resp_cert)); return result; } @@ -183,10 +211,10 @@ const auto ocsp_status = Botan::PKIX::check_ocsp( cert_path, {ocsp}, {&certstore}, valid_time, Botan::Path_Validation_Restrictions()); - return result.test_eq("Expected size of ocsp_status", ocsp_status.size(), 1) && - result.test_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1) && - result.confirm(std::string("Status: '") + Botan::to_string(expected) + "'", - ocsp_status[0].contains(expected)); + return result.test_sz_eq("Expected size of ocsp_status", ocsp_status.size(), 2) && + result.test_sz_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1) && + result.test_is_true(std::string("Status: '") + Botan::to_string(expected) + "'", + ocsp_status[0].contains(expected)); }; check_ocsp(Botan::calendar_point(2016, 11, 11, 12, 30, 0).to_std_timepoint(), @@ -220,13 +248,13 @@ auto check_ocsp = [&](const std::chrono::system_clock::time_point valid_time, const Botan::Certificate_Status_Code expected) { - Botan::Path_Validation_Restrictions pvr(false, 110, false, max_age); + const Botan::Path_Validation_Restrictions pvr(false, 110, false, max_age); const auto ocsp_status = Botan::PKIX::check_ocsp(cert_path, {ocsp}, {&certstore}, valid_time, pvr); - return result.test_eq("Expected size of ocsp_status", ocsp_status.size(), 1) && - result.test_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1) && - result.confirm(std::string("Status: '") + Botan::to_string(expected) + "'", - ocsp_status[0].contains(expected)); + return result.test_sz_eq("Expected size of ocsp_status", ocsp_status.size(), 2) && + result.test_sz_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1) && + result.test_is_true(std::string("Status: '") + Botan::to_string(expected) + "'", + ocsp_status[0].contains(expected)); }; check_ocsp(Botan::calendar_point(2016, 11, 11, 12, 30, 0).to_std_timepoint(), @@ -260,13 +288,17 @@ auto check_ocsp = [&](const std::chrono::system_clock::time_point valid_time, const Botan::Certificate_Status_Code expected) { - Botan::Path_Validation_Restrictions pvr(false, 110, false, max_age); + const Botan::Path_Validation_Restrictions pvr(false, 110, false, max_age); const auto ocsp_status = Botan::PKIX::check_ocsp(cert_path, {ocsp}, {&certstore}, valid_time, pvr); - return result.test_eq("Expected size of ocsp_status", ocsp_status.size(), 1) && - result.test_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1) && - result.confirm(std::string("Status: '") + Botan::to_string(expected) + "'", - ocsp_status[0].contains(expected)); + result.test_sz_eq("Expected size of ocsp_status", ocsp_status.size(), 2); + + if(!ocsp_status.empty()) { + result.test_sz_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1); + + result.test_is_true(std::string("Status: '") + Botan::to_string(expected) + "'", + ocsp_status[0].contains(expected)); + } }; check_ocsp(Botan::calendar_point(2019, 5, 28, 7, 0, 0).to_std_timepoint(), @@ -298,10 +330,13 @@ const auto ocsp_status = Botan::PKIX::check_ocsp( cert_path, {ocsp}, {&certstore}, valid_time, Botan::Path_Validation_Restrictions()); - return result.test_eq("Expected size of ocsp_status", ocsp_status.size(), 1) && - result.test_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1) && - result.confirm(std::string("Status: '") + Botan::to_string(expected) + "'", - ocsp_status[0].contains(expected)); + result.test_sz_eq("Expected size of ocsp_status", ocsp_status.size(), 2); + + if(!ocsp_status.empty()) { + result.test_sz_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1); + result.test_is_true(std::string("Status: '") + Botan::to_string(expected) + "'", + ocsp_status[0].contains(expected)); + } }; check_ocsp(Botan::calendar_point(2019, 5, 28, 7, 0, 0).to_std_timepoint(), @@ -333,9 +368,9 @@ const auto ocsp_status = Botan::PKIX::check_ocsp(cert_path, {ocsp}, {&certstore}, valid_time, Botan::Path_Validation_Restrictions()); - if(result.test_eq("Expected size of ocsp_status", ocsp_status.size(), 1)) { - if(result.test_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1)) { - result.test_gt( + if(result.test_sz_eq("Expected size of ocsp_status", ocsp_status.size(), 2)) { + if(result.test_sz_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1)) { + result.test_sz_gt( "Status warning", ocsp_status[0].count(Botan::Certificate_Status_Code::OCSP_NO_REVOCATION_URL), 0); } } @@ -360,12 +395,12 @@ auto ocsp_status = Botan::PKIX::check_ocsp_online( cert_path, {&certstore}, now, ocsp_timeout, Botan::Path_Validation_Restrictions()); - if(result.test_eq("Expected size of ocsp_status", ocsp_status.size(), 1)) { - if(result.test_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1)) { + if(result.test_sz_eq("Expected size of ocsp_status", ocsp_status.size(), 1)) { + if(result.test_sz_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1)) { const bool status_good = ocsp_status[0].contains(Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD); const bool server_not_found = ocsp_status[0].contains(Botan::Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE); - result.confirm("Expected status", status_good || server_not_found); + result.test_is_true("Expected status", status_good || server_not_found); } } @@ -383,28 +418,117 @@ auto responder = load_test_X509_cert("x509/ocsp/randombit_ocsp_forged_responder.pem"); auto ca = load_test_X509_cert("x509/ocsp/letsencrypt.pem"); - std::optional nullopt_cert; - Botan::Certificate_Store_In_Memory trusted_responders; // without providing the 3rd party responder certificate no issuer will be found - result.test_is_eq("cannot find signing certificate without trusted responders", - ocsp.find_signing_certificate(ca), - nullopt_cert); - result.test_is_eq("cannot find signing certificate without additional help", - ocsp.find_signing_certificate(ca, &trusted_responders), - nullopt_cert); + result.test_opt_is_null("cannot find signing certificate without trusted responders", + ocsp.find_signing_certificate(ca)); + result.test_opt_is_null("cannot find signing certificate without additional help", + ocsp.find_signing_certificate(ca, &trusted_responders)); // add the 3rd party responder certificate to the list of trusted OCSP responder certs // to find the issuer certificate of this response trusted_responders.add_certificate(responder); - result.test_is_eq("the responder certificate is returned when it is trusted", - ocsp.find_signing_certificate(ca, &trusted_responders), - std::optional(responder)); - - result.test_is_eq("the responder's signature checks out", - ocsp.verify_signature(responder), - Botan::Certificate_Status_Code::OCSP_SIGNATURE_OK); + test_arb_eq(result, + "the responder certificate is returned when it is trusted", + ocsp.find_signing_certificate(ca, &trusted_responders), + std::optional(responder)); + + result.test_enum_eq("the responder's signature checks out", + ocsp.verify_signature(responder), + Botan::Certificate_Status_Code::OCSP_SIGNATURE_OK); + + return result; + } + + static Test::Result test_forged_ocsp_signature_is_rejected() { + Test::Result result("OCSP response with forged signature is rejected by path validation"); + + auto ee = load_test_X509_cert("x509/ocsp/randombit.pem"); + auto ca = load_test_X509_cert("x509/ocsp/letsencrypt.pem"); + auto trust_root = load_test_X509_cert("x509/ocsp/geotrust.pem"); + + const std::vector cert_path = {ee, ca, trust_root}; + + Botan::Certificate_Store_In_Memory certstore; + certstore.add_certificate(trust_root); + + const auto valid_time = Botan::calendar_point(2016, 11, 18, 12, 30, 0).to_std_timepoint(); + + // Verify the unmodified response is accepted + { + auto ocsp = load_test_OCSP_resp("x509/ocsp/randombit_ocsp.der"); + const auto ocsp_status = Botan::PKIX::check_ocsp( + cert_path, {ocsp}, {&certstore}, valid_time, Botan::Path_Validation_Restrictions()); + + if(result.test_sz_eq("Legitimate: expected result count", ocsp_status.size(), 2) && + result.test_sz_eq("Legitimate: expected status count", ocsp_status[0].size(), 1)) { + result.test_is_true("Legitimate response is accepted", + ocsp_status[0].contains(Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD)); + } + } + + // Tamper with the signature and verify check_ocsp rejects it + { + auto ocsp_bytes = Test::read_binary_data_file("x509/ocsp/randombit_ocsp.der"); + ocsp_bytes.back() ^= 0x01; + Botan::OCSP::Response forged_ocsp(ocsp_bytes.data(), ocsp_bytes.size()); + + const auto ocsp_status = Botan::PKIX::check_ocsp( + cert_path, {forged_ocsp}, {&certstore}, valid_time, Botan::Path_Validation_Restrictions()); + + if(result.test_sz_eq("Forged: expected result count", ocsp_status.size(), 2) && + result.test_sz_eq("Forged: expected status count", ocsp_status[0].size(), 1)) { + result.test_is_true("Forged signature is rejected", + ocsp_status[0].contains(Botan::Certificate_Status_Code::OCSP_SIGNATURE_ERROR)); + } + } + + return result; + } + + static Test::Result test_partial_stapling_preserves_per_slot_gap() { + Test::Result result("OCSP partial stapling preserves per-slot gap for online fallback"); + + auto ee = load_test_X509_cert("x509/ocsp/mychain_ee.pem"); + auto ca = load_test_X509_cert("x509/ocsp/mychain_int.pem"); + auto trust_root = load_test_X509_cert("x509/ocsp/mychain_root.pem"); + + auto ocsp_for_ee = load_test_OCSP_resp("x509/ocsp/mychain_ocsp_for_ee.der"); + auto ocsp_for_int = load_test_OCSP_resp("x509/ocsp/mychain_ocsp_for_int_self_signed.der"); + + Botan::Certificate_Store_In_Memory certstore; + certstore.add_certificate(trust_root); + + const std::vector cert_path = {ee, ca, trust_root}; + const auto valid_time = Botan::calendar_point(2022, 9, 22, 22, 30, 0).to_std_timepoint(); + const auto restrictions = Botan::Path_Validation_Restrictions(); + + // Here the intermediate has a stapled OCSP but the leaf does not + { + const std::vector> staples = {std::nullopt, ocsp_for_int}; + const auto ocsp_status = + Botan::PKIX::check_ocsp(cert_path, staples, {&certstore}, valid_time, restrictions); + + result.test_sz_eq("missing-leaf: ocsp_status sized to non-root certs", ocsp_status.size(), 2); + if(ocsp_status.size() == 2) { + result.test_is_true("missing-leaf: leaf slot is empty", ocsp_status[0].empty()); + result.test_is_false("missing-leaf: intermediate slot is filled", ocsp_status[1].empty()); + } + } + + // Here the leaf has a stapled OCSP but the intermediate does not + { + const std::vector> staples = {ocsp_for_ee, std::nullopt}; + const auto ocsp_status = + Botan::PKIX::check_ocsp(cert_path, staples, {&certstore}, valid_time, restrictions); + + result.test_sz_eq("missing-intermediate: ocsp_status sized to non-root certs", ocsp_status.size(), 2); + if(ocsp_status.size() == 2) { + result.test_is_false("missing-intermediate: leaf slot is filled", ocsp_status[0].empty()); + result.test_is_true("missing-intermediate: intermediate slot is empty", ocsp_status[1].empty()); + } + } return result; } @@ -418,7 +542,7 @@ return cert.v3_extensions().extension_set(Botan::OID::from_string("PKIX.OCSP.NoCheck")); }) != ocsp.certificates().end(); - result.confirm("Contains NoCheck extension", contains_cert_with_nocheck); + result.test_is_true("Contains NoCheck extension", contains_cert_with_nocheck); return result; } @@ -429,6 +553,7 @@ results.push_back(test_request_encoding()); results.push_back(test_response_parsing()); + results.push_back(test_response_with_bykey_responder_id()); results.push_back(test_response_certificate_access()); results.push_back(test_response_find_signing_certificate()); results.push_back(test_response_verification_with_next_update_without_max_age()); @@ -437,6 +562,8 @@ results.push_back(test_response_verification_without_next_update_without_max_age()); results.push_back(test_response_verification_softfail()); results.push_back(test_response_verification_with_additionally_trusted_responder()); + results.push_back(test_forged_ocsp_signature_is_rejected()); + results.push_back(test_partial_stapling_preserves_per_slot_gap()); results.push_back(test_responder_cert_with_nocheck_extension()); #if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS) @@ -453,4 +580,6 @@ #endif +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_octetstring.cpp botan3-3.12.0+dfsg/src/tests/test_octetstring.cpp --- botan3-3.7.1+dfsg/src/tests/test_octetstring.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_octetstring.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -4,10 +4,9 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#define BOTAN_NO_DEPRECATED_WARNINGS - #include "tests.h" +#include #include namespace Botan_Tests { @@ -19,8 +18,8 @@ auto rng = Test::new_rng("octet_string_from_rng"); - Botan::OctetString os(*rng, 32); - result.test_eq("length is 32 bytes", os.size(), 32); + const Botan::OctetString os(*rng, 32); + result.test_sz_eq("length is 32 bytes", os.size(), 32); return result; } @@ -28,8 +27,8 @@ Test::Result test_from_hex() { Test::Result result("OctetString"); - Botan::OctetString os("0123456789ABCDEF"); - result.test_eq("length is 8 bytes", os.size(), 8); + const Botan::OctetString os("0123456789ABCDEF"); + result.test_sz_eq("length is 8 bytes", os.size(), 8); return result; } @@ -39,8 +38,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); + const Botan::OctetString os(rand_bytes.data(), rand_bytes.size()); + result.test_sz_eq("length is 8 bytes", os.size(), 8); return result; } @@ -50,13 +49,13 @@ Botan::OctetString os("FFFFFFFFFFFFFFFF"); os.set_odd_parity(); - Botan::OctetString expected("FEFEFEFEFEFEFEFE"); - result.test_eq("odd parity set correctly", os, expected); + const Botan::OctetString expected("FEFEFEFEFEFEFEFE"); + result.test_bin_eq("odd parity set correctly", os, expected); Botan::OctetString os2("EFCBDA4FAA997F63"); os2.set_odd_parity(); - Botan::OctetString expected2("EFCBDA4FAB987F62"); - result.test_eq("odd parity set correctly", os2, expected2); + const Botan::OctetString expected2("EFCBDA4FAB987F62"); + result.test_bin_eq("odd parity set correctly", os2, expected2); return result; } @@ -64,8 +63,8 @@ Test::Result test_to_string() { Test::Result result("OctetString"); - Botan::OctetString os("0123456789ABCDEF"); - result.test_eq("OctetString::to_string() returns correct string", os.to_string(), "0123456789ABCDEF"); + const Botan::OctetString os("0123456789ABCDEF"); + result.test_str_eq("OctetString::to_string() returns correct string", os.to_string(), "0123456789ABCDEF"); return result; } @@ -73,23 +72,23 @@ Test::Result test_xor() { Test::Result result("OctetString"); - Botan::OctetString os1("0000000000000000"); - Botan::OctetString os2("FFFFFFFFFFFFFFFF"); + const Botan::OctetString os1("0000000000000000"); + const Botan::OctetString os2("FFFFFFFFFFFFFFFF"); Botan::OctetString xor_result = os1 ^ os2; - result.test_eq("OctetString XOR operations works as expected", xor_result, os2); + result.test_bin_eq("OctetString XOR operations works as expected", xor_result, os2); xor_result = os1; xor_result ^= os2; - result.test_eq("OctetString XOR operations works as expected", xor_result, os2); + result.test_bin_eq("OctetString XOR operations works as expected", xor_result, os2); xor_result = os2 ^ os2; // NOLINT(*-redundant-expression) - result.test_eq("OctetString XOR operations works as expected", xor_result, os1); + result.test_bin_eq("OctetString XOR operations works as expected", xor_result, os1); - Botan::OctetString os3("0123456789ABCDEF"); + const Botan::OctetString os3("0123456789ABCDEF"); xor_result = os3 ^ os2; - Botan::OctetString expected("FEDCBA9876543210"); - result.test_eq("OctetString XOR operations works as expected", xor_result, expected); + const Botan::OctetString expected("FEDCBA9876543210"); + result.test_bin_eq("OctetString XOR operations works as expected", xor_result, expected); return result; } @@ -102,9 +101,9 @@ const Botan::OctetString os2("FFFFFFFFFFFFFFFF"); const Botan::OctetString& os2_copy = os2; - result.confirm("OctetString equality operations works as expected", os1 == os1_copy); - result.confirm("OctetString equality operations works as expected", os2 == os2_copy); - result.confirm("OctetString equality operations works as expected", os1 != os2); + result.test_is_true("OctetString equality operations works as expected", os1 == os1_copy); + result.test_is_true("OctetString equality operations works as expected", os2 == os2_copy); + result.test_is_true("OctetString equality operations works as expected", os1 != os2); return result; } @@ -112,13 +111,13 @@ Test::Result test_append() { Test::Result result("OctetString"); - Botan::OctetString os1("0000"); - Botan::OctetString os2("FFFF"); - Botan::OctetString expected("0000FFFF"); + const Botan::OctetString os1("0000"); + const Botan::OctetString os2("FFFF"); + const Botan::OctetString expected("0000FFFF"); - Botan::OctetString append_result = os1 + os2; + const Botan::OctetString append_result = os1 + os2; - result.test_eq("OctetString append operations works as expected", append_result, expected); + result.test_bin_eq("OctetString append operations works as expected", append_result, expected); return result; } diff -Nru botan3-3.7.1+dfsg/src/tests/test_oid.cpp botan3-3.12.0+dfsg/src/tests/test_oid.cpp --- botan3-3.7.1+dfsg/src/tests/test_oid.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_oid.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,7 +11,6 @@ #include #include #include - #include #endif namespace Botan_Tests { @@ -24,10 +23,10 @@ /* See #2730 and #2237 - Certain locales format integers with thousands seperators. This + Certain locales format integers with thousands separators. This caused a subtle bug which caused OID comparisons to fail because OID::to_string(), which used ostringstream, introduced a thousands - seperator when the OID component had a value >= 1000. But this + separator when the OID component had a value >= 1000. But this only failed in certain locales (pt_BR was reported). Nominally C++ requires std::to_string to also be locale-respecting. @@ -38,17 +37,17 @@ Here we test the original issue of #2237 to verify it works. If the compiler implements std::to_string in a way that respects locale, - *and* this test is run in a locale that uses thousands seperators, + *and* this test is run in a locale that uses thousands separators, then it will fail. Which is much better than a very subtle failure. However if it ever does fail then we must replace nearly every call to std::to_string with something else that ignores locale. */ - Botan::OID oid{1, 2, 1000, 1001, 1002000}; + const Botan::OID oid{1, 2, 1000, 1001, 1002000}; Test::Result result("OID::to_string"); - result.test_eq("OID::to_string behaves as we expect", oid.to_string(), "1.2.1000.1001.1002000"); + result.test_str_eq("OID::to_string behaves as we expect", oid.to_string(), "1.2.1000.1001.1002000"); return result; } @@ -59,13 +58,13 @@ const std::string name = "botan-test-oid1"; const Botan::OID oid("1.3.6.1.4.1.25258.1000.1"); - result.test_eq("named OID not found", Botan::OID::from_name(name).has_value(), false); + result.test_is_false("named OID not found", Botan::OID::from_name(name).has_value()); Botan::OID::register_oid(oid, name); - result.test_eq("named OID found", Botan::OID::from_name(name).has_value(), true); + result.test_is_true("named OID found", Botan::OID::from_name(name).has_value()); - result.test_eq("name of OID matches expected", oid.to_formatted_string(), name); + result.test_str_eq("name of OID matches expected", oid.to_formatted_string(), name); return result; } @@ -78,12 +77,12 @@ const Botan::OID oid("1.3.6.1.4.1.25258.1001.1"); const Botan::OID oid2("1.3.6.1.4.1.25258.1001.2"); - result.test_eq("named OID not found", Botan::OID::from_name(name).has_value(), false); + result.test_is_false("named OID not found", Botan::OID::from_name(name).has_value()); Botan::OID::register_oid(oid, name); - result.confirm("named OID found", Botan::OID::from_name(name).value_or(Botan::OID()) == oid); - result.test_eq("name of OID matches expected", oid.to_formatted_string(), name); + result.test_is_true("named OID found", Botan::OID::from_name(name).value_or(Botan::OID()) == oid); + result.test_str_eq("name of OID matches expected", oid.to_formatted_string(), name); // completely redundant, nothing happens: Botan::OID::register_oid(oid, name); @@ -95,10 +94,11 @@ Botan::OID::register_oid(oid2, name); // name->oid map is unchanged: - result.confirm("named OID found after second insert", Botan::OID::from_name(name).value_or(Botan::OID()) == oid); - result.test_eq("name of OID matches expected", oid.to_formatted_string(), name); + result.test_is_true("named OID found after second insert", + Botan::OID::from_name(name).value_or(Botan::OID()) == oid); + result.test_str_eq("name of OID matches expected", oid.to_formatted_string(), name); // now second OID maps back to the string as expected: - result.test_eq("name of OID matches expected", oid2.to_formatted_string(), name); + result.test_str_eq("name of OID matches expected", oid2.to_formatted_string(), name); try { Botan::OID::register_oid(oid2, name2); @@ -139,7 +139,7 @@ public: OID_Encoding_Tests() : Text_Based_Test("asn1_oid.vec", "OID,DER") {} - Test::Result run_one_test(const std::string&, const VarMap& vars) override { + Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override { const auto oid_str = vars.get_req_str("OID"); const auto expected_der = vars.get_req_bin("DER"); @@ -151,17 +151,17 @@ std::vector der; Botan::DER_Encoder enc(der); enc.encode(oid); - result.test_eq("Encoding correct", der, expected_der); + result.test_bin_eq("Encoding correct", der, expected_der); } catch(std::exception& e) { result.test_failure("Encoding OID failed", e.what()); } try { - Botan::BER_Decoder dec(expected_der); + Botan::BER_Decoder dec(expected_der, Botan::BER_Decoder::Limits::DER()); Botan::OID dec_oid; dec.decode(dec_oid); dec.verify_end(); - result.test_eq("Decoding OID correct", dec_oid.to_string(), oid_str); + result.test_str_eq("Decoding OID correct", dec_oid.to_string(), oid_str); } catch(std::exception& e) { result.test_failure("Decoding OID failed", e.what()); } @@ -176,7 +176,7 @@ public: OID_Invalid_Encoding_Tests() : Text_Based_Test("asn1_oid_invalid.vec", "DER") {} - Test::Result run_one_test(const std::string&, const VarMap& vars) override { + Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override { const auto test_der = vars.get_req_bin("DER"); Test::Result result("OID DER decode invalid"); diff -Nru botan3-3.7.1+dfsg/src/tests/test_os_utils.cpp botan3-3.12.0+dfsg/src/tests/test_os_utils.cpp --- botan3-3.7.1+dfsg/src/tests/test_os_utils.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_os_utils.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,11 +9,7 @@ #if defined(BOTAN_HAS_OS_UTILS) #include -#endif - -// For __ud2 intrinsic -#if defined(BOTAN_TARGET_COMPILER_IS_MSVC) - #include + #include #endif namespace Botan_Tests { @@ -52,15 +48,15 @@ static Test::Result test_get_process_id() { Test::Result result("OS::get_process_id"); - uint32_t pid1 = Botan::OS::get_process_id(); - uint32_t pid2 = Botan::OS::get_process_id(); + const uint32_t pid1 = Botan::OS::get_process_id(); + const uint32_t pid2 = Botan::OS::get_process_id(); - result.test_eq("PID same across calls", static_cast(pid1), static_cast(pid2)); + result.test_u32_eq("PID same across calls", pid1, pid2); #if defined(BOTAN_TARGET_OS_IS_LLVM) || defined(BOTAN_TARGET_OS_IS_NONE) - result.test_eq("PID is expected to be zero on this platform", pid1, size_t(0)); + result.test_u32_eq("PID is expected to be zero on this platform", pid1, 0); #else - result.test_ne("PID is non-zero on systems with processes", pid1, 0); + result.test_sz_ne("PID is non-zero on systems with processes", pid1, 0); #endif return result; @@ -76,7 +72,7 @@ if(proc_ts1 == 0) { const uint64_t proc_ts2 = Botan::OS::get_cpu_cycle_counter(); - result.test_is_eq("Disabled processor timestamp stays at zero", proc_ts1, proc_ts2); + result.test_u64_eq("Disabled processor timestamp stays at zero", proc_ts1, proc_ts2); return result; } @@ -85,27 +81,31 @@ ++counts; } - result.test_lt("CPU cycle counter eventually changes value", counts, max_repeats); + result.test_sz_lt("CPU cycle counter eventually changes value", counts, max_repeats); return result; } static Test::Result test_get_high_resolution_clock() { - const size_t max_trials = 1024; - const size_t max_repeats = 128; + // We can easily test progression; however, testing precision is trickier. + // On very fast machines with very low clock resolution (like the web platform offers), + // it may be necessary to make the call quite a few times to notice any change. + constexpr auto max_trials = 32768; Test::Result result("OS::get_high_resolution_clock"); // TODO better tests - const uint64_t hr_ts1 = Botan::OS::get_high_resolution_clock(); - result.confirm("high resolution timestamp value is never zero", hr_ts1 != 0); + const auto hr_ts1 = Botan::OS::get_high_resolution_clock(); + result.test_is_true("high resolution timestamp value is never zero", hr_ts1 != 0); - size_t counts = 0; - while(counts < max_trials && (Botan::OS::get_high_resolution_clock() == hr_ts1)) { - ++counts; + for(size_t trials = 0; trials < max_trials; ++trials) { + if(hr_ts1 < Botan::OS::get_high_resolution_clock()) { + result.test_success("high resolution clock made forward progress"); + return result; + } } - result.test_lt("high resolution clock eventually changes value", counts, max_repeats); + result.test_failure("high resolution clock didn't make forward progress, even after many trials"); return result; } @@ -115,7 +115,7 @@ const size_t ta = Botan::OS::get_cpu_available(); - result.test_gte("get_cpu_available is at least 1", ta, 1); + result.test_sz_gte("get_cpu_available is at least 1", ta, 1); return result; } @@ -124,15 +124,15 @@ // TODO better tests Test::Result result("OS::get_system_timestamp_ns"); - uint64_t sys_ts1 = Botan::OS::get_system_timestamp_ns(); - result.confirm("System timestamp value is never zero", sys_ts1 != 0); + const uint64_t sys_ts1 = Botan::OS::get_system_timestamp_ns(); + result.test_is_true("System timestamp value is never zero", sys_ts1 != 0); // do something that consumes a little time Botan::OS::get_process_id(); - uint64_t sys_ts2 = Botan::OS::get_system_timestamp_ns(); + const uint64_t sys_ts2 = Botan::OS::get_system_timestamp_ns(); - result.confirm("System time moves forward", sys_ts1 <= sys_ts2); + result.test_is_true("System time moves forward", sys_ts1 <= sys_ts2); return result; } @@ -148,9 +148,9 @@ static Test::Result test_cpu_instruction_probe() { Test::Result result("OS::run_cpu_instruction_probe"); - // OS::run_cpu_instruction_probe only implemented for Unix signals or Windows SEH + // OS::run_cpu_instruction_probe is only implemented on systems supporting Unix-style signals - std::function ok_fn = []() noexcept -> int { return 5; }; + const std::function ok_fn = []() noexcept -> int { return 5; }; const int run_rc = Botan::OS::run_cpu_instruction_probe(ok_fn); if(run_rc == -3) { @@ -158,29 +158,23 @@ return {result}; } - result.confirm("Correct result returned by working probe fn", run_rc == 5); + result.test_is_true("Correct result returned by working probe fn", run_rc == 5); std::function crash_probe; - #if defined(BOTAN_TARGET_COMPILER_IS_MSVC) - crash_probe = []() noexcept -> int { - __ud2(); - return 3; - }; - - #elif defined(BOTAN_USE_GCC_INLINE_ASM) + #if defined(BOTAN_USE_GCC_INLINE_ASM) - #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + #if defined(BOTAN_TARGET_ARCH_IS_X86_FAMILY) crash_probe = []() noexcept -> int { - asm volatile("ud2"); + asm volatile("ud2"); // NOLINT(*-no-assembler) return 3; }; - #elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) + #elif defined(BOTAN_TARGET_ARCH_IS_ARM_FAMILY) //ARM: asm volatile (".word 0xf7f0a000\n"); // illegal instruction in both ARM and Thumb modes crash_probe = []() noexcept -> int { - asm volatile(".word 0xe7f0def0\n"); + asm volatile(".word 0xe7f0def0\n"); // NOLINT(*-no-assembler) return 3; }; @@ -196,7 +190,7 @@ if(crash_probe) { const int crash_rc = Botan::OS::run_cpu_instruction_probe(crash_probe); - result.confirm("Result for function executing undefined opcode", crash_rc < 0); + result.test_is_true("Result for function executing undefined opcode", crash_rc < 0); } return result; diff -Nru botan3-3.7.1+dfsg/src/tests/test_otp.cpp botan3-3.12.0+dfsg/src/tests/test_otp.cpp --- botan3-3.7.1+dfsg/src/tests/test_otp.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_otp.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -15,6 +15,8 @@ namespace Botan_Tests { +namespace { + #if defined(BOTAN_HAS_HOTP) && defined(BOTAN_HAS_TOTP) class HOTP_KAT_Tests final : public Text_Based_Test { @@ -38,26 +40,26 @@ Botan::HOTP hotp(key, hash_algo, digits); - result.test_int_eq("OTP", hotp.generate_hotp(counter), otp); + result.test_u32_eq("OTP", hotp.generate_hotp(counter), otp); std::pair otp_res = hotp.verify_hotp(otp, counter, 0); - result.test_eq("OTP verify result", otp_res.first, true); - result.confirm("OTP verify next counter", otp_res.second == counter + 1); + result.test_is_true("OTP verify result", otp_res.first); + result.test_u64_eq("OTP verify next counter", otp_res.second, counter + 1); // Test invalid OTP otp_res = hotp.verify_hotp(otp + 1, counter, 0); - result.test_eq("OTP verify result", otp_res.first, false); - result.confirm("OTP verify next counter", otp_res.second == counter); + result.test_is_false("OTP verify result", otp_res.first); + result.test_u64_eq("OTP verify next counter", otp_res.second, counter); // Test invalid OTP with long range otp_res = hotp.verify_hotp(otp + 1, counter, 100); - result.test_eq("OTP verify result", otp_res.first, false); - result.confirm("OTP verify next counter", otp_res.second == counter); + result.test_is_false("OTP verify result", otp_res.first); + result.test_u64_eq("OTP verify next counter", otp_res.second, counter); // Test valid OTP with long range otp_res = hotp.verify_hotp(otp, counter - 90, 100); - result.test_eq("OTP verify result", otp_res.first, true); - result.confirm("OTP verify next counter", otp_res.second == counter + 1); + result.test_is_true("OTP verify result", otp_res.first); + result.test_u64_eq("OTP verify next counter", otp_res.second, counter + 1); return result; } @@ -87,17 +89,17 @@ Botan::TOTP totp(key, hash_algo, digits, timestep); - std::chrono::system_clock::time_point time = from_timestring(timestamp); - std::chrono::system_clock::time_point later_time = time + std::chrono::seconds(timestep); - std::chrono::system_clock::time_point too_late = time + std::chrono::seconds(2 * timestep); - - result.test_int_eq("TOTP generate", totp.generate_totp(time), otp); - - result.test_eq("TOTP verify valid", totp.verify_totp(otp, time, 0), true); - result.test_eq("TOTP verify invalid", totp.verify_totp(otp ^ 1, time, 0), false); - result.test_eq("TOTP verify time slip", totp.verify_totp(otp, later_time, 0), false); - result.test_eq("TOTP verify time slip allowed", totp.verify_totp(otp, later_time, 1), true); - result.test_eq("TOTP verify time slip out of range", totp.verify_totp(otp, too_late, 1), false); + const std::chrono::system_clock::time_point time = from_timestring(timestamp); + const std::chrono::system_clock::time_point later_time = time + std::chrono::seconds(timestep); + const std::chrono::system_clock::time_point too_late = time + std::chrono::seconds(2 * timestep); + + result.test_u32_eq("TOTP generate", totp.generate_totp(time), otp); + + result.test_is_true("TOTP verify valid", totp.verify_totp(otp, time, 0)); + result.test_is_false("TOTP verify invalid", totp.verify_totp(otp ^ 1, time, 0)); + result.test_is_false("TOTP verify time slip", totp.verify_totp(otp, later_time, 0)); + result.test_is_true("TOTP verify time slip allowed", totp.verify_totp(otp, later_time, 1)); + result.test_is_false("TOTP verify time slip out of range", totp.verify_totp(otp, too_late, 1)); return result; } @@ -123,4 +125,6 @@ #endif +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_pad.cpp botan3-3.12.0+dfsg/src/tests/test_pad.cpp --- botan3-3.7.1+dfsg/src/tests/test_pad.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_pad.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,6 +12,8 @@ namespace Botan_Tests { +namespace { + #if defined(BOTAN_HAS_CIPHER_MODE_PADDING) class Cipher_Mode_Padding_Tests final : public Text_Based_Test { @@ -30,7 +32,7 @@ if(algo.substr(underscore + 1, std::string::npos) != "Invalid") { throw Test_Error("Unexpected padding header " + header); } - algo = algo.substr(0, underscore); + algo.resize(underscore); // Use just the part before the underscore } Test::Result result(algo); @@ -44,23 +46,37 @@ if(expected.empty()) { // This is an unpad an invalid input and ensure we reject - if(pad->unpad(input.data(), block_size) != block_size) { - result.test_failure("Did not reject invalid padding", Botan::hex_encode(input)); + if(pad->unpad(std::span{input}.last(block_size)) != block_size) { + result.test_failure("Did not reject invalid padding", input); } else { result.test_success("Rejected invalid padding"); } } else { // This is a pad plaintext and unpad valid padding round trip test Botan::secure_vector buf(input.begin(), input.end()); + const size_t outlen = pad->output_length(buf.size(), block_size); + if(!result.test_sz_eq("output length", outlen, expected.size())) { + return result; + } + buf.resize(outlen); pad->add_padding(buf, input.size() % block_size, block_size); - result.test_eq("pad", buf, expected); + result.test_bin_eq("pad", buf, expected); buf.assign(expected.begin(), expected.end()); - const size_t last_block = (buf.size() < block_size) ? 0 : buf.size() - block_size; - const size_t pad_bytes = block_size - pad->unpad(&buf[last_block], block_size); + const auto pad_bytes = [&] { + if(algo == "NoPadding") { + const size_t maybe_partial = (buf.size() < block_size) ? buf.size() : block_size; + const auto no_padding = maybe_partial - pad->unpad(std::span{buf}.last(maybe_partial)); + result.test_sz_eq("no padding", no_padding, 0); + return no_padding; + } else { + return block_size - pad->unpad(std::span{buf}.last(block_size)); + } + }(); + buf.resize(buf.size() - pad_bytes); // remove padding - result.test_eq("unpad", buf, input); + result.test_bin_eq("unpad", buf, input); } return result; @@ -71,4 +87,6 @@ #endif +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_passhash.cpp botan3-3.12.0+dfsg/src/tests/test_passhash.cpp --- botan3-3.7.1+dfsg/src/tests/test_passhash.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_passhash.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -36,12 +36,12 @@ const std::string passhash = vars.get_req_str("Passhash"); Test::Result result("bcrypt"); - result.test_eq("correct hash accepted", Botan::check_bcrypt(password, passhash), true); + result.test_is_true("correct hash accepted", Botan::check_bcrypt(password, passhash)); // 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, this->rng(), level); - result.test_eq("generated hash accepted", Botan::check_bcrypt(password, gen_hash), true); + result.test_is_true("generated hash accepted", Botan::check_bcrypt(password, gen_hash)); } return result; @@ -50,7 +50,7 @@ std::vector run_final_tests() override { Test::Result result("bcrypt"); - uint64_t start = Test::timestamp(); + const uint64_t start = Test::timestamp(); const std::string password = "ag00d1_2BE5ur3"; @@ -60,7 +60,7 @@ for(uint16_t level = 4; level <= max_level; ++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_is_true("generated hash accepted", Botan::check_bcrypt(password, gen_hash)); } result.test_throws("Invalid bcrypt version rejected", "Unknown bcrypt version 'q'", [&rng]() { @@ -90,7 +90,7 @@ if(header == "Verify") { const bool accepted = Botan::argon2_check_pwhash(password.data(), password.size(), passhash); - result.test_eq("correct hash accepted", accepted, true); + result.test_is_true("correct hash accepted", accepted); } else if(header == "Generate") { const std::vector salt = vars.get_req_bin("Salt"); const uint8_t y = vars.get_req_u8("Mode"); @@ -104,9 +104,9 @@ const std::string generated = Botan::argon2_generate_pwhash(password.data(), password.size(), rng, p, M, t, y, salt.size(), out_len); - result.test_eq("expected hash generated", generated, passhash); + result.test_str_eq("expected hash generated", generated, passhash); const bool accepted = Botan::argon2_check_pwhash(password.data(), password.size(), generated); - result.test_eq("generated hash accepted", accepted, true); + result.test_is_true("generated hash accepted", accepted); } else { throw Test_Error("Unexpected header in Argon2 password hash test file"); } @@ -135,15 +135,15 @@ Test::Result result("passhash9"); if(Botan::is_passhash9_alg_supported(uint8_t(prf))) { - result.test_eq("correct hash accepted", Botan::check_passhash9(password, passhash), true); + result.test_is_true("correct hash accepted", Botan::check_passhash9(password, passhash)); } 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, 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); + if(!result.test_is_true("generated hash accepted", Botan::check_passhash9(password, gen_hash))) { + result.test_note("rejected hash", gen_hash); } } } @@ -154,8 +154,8 @@ 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, 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); + if(!result.test_is_true("generated hash accepted", Botan::check_passhash9(password, gen_hash))) { + result.test_note("rejected hash", gen_hash); } } } @@ -166,7 +166,7 @@ std::vector run_final_tests() override { Test::Result result("passhash9"); - result.confirm("Unknown algorithm is unknown", Botan::is_passhash9_alg_supported(255) == false); + result.test_is_false("Unknown algorithm is unknown", Botan::is_passhash9_alg_supported(255)); auto& rng = this->rng(); diff -Nru botan3-3.7.1+dfsg/src/tests/test_pbkdf.cpp botan3-3.12.0+dfsg/src/tests/test_pbkdf.cpp --- botan3-3.7.1+dfsg/src/tests/test_pbkdf.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_pbkdf.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include "tests.h" #if defined(BOTAN_HAS_PBKDF) + #include #include #include #endif @@ -40,11 +41,11 @@ return result; } - result.test_eq("Expected name", pbkdf->name(), pbkdf_name); + result.test_str_eq("Expected name", pbkdf->name(), pbkdf_name); const Botan::secure_vector derived = pbkdf->derive_key(outlen, passphrase, salt.data(), salt.size(), iterations).bits_of(); - result.test_eq("derived key", derived, expected); + result.test_bin_eq("derived key", derived, expected); auto pwdhash_fam = Botan::PasswordHashFamily::create(pbkdf_name); @@ -58,7 +59,7 @@ std::vector pwdhash_derived(outlen); pwdhash->hash(pwdhash_derived, passphrase, salt); - result.test_eq("pwdhash derived key", pwdhash_derived, expected); + result.test_bin_eq("pwdhash derived key", pwdhash_derived, expected); return result; } @@ -74,8 +75,8 @@ const std::vector all_pwdhash = { "Scrypt", "PBKDF2(SHA-256)", "OpenPGP-S2K(SHA-384)", "Argon2d", "Argon2i", "Argon2id", "Bcrypt-PBKDF"}; - const auto run_time = std::chrono::milliseconds(3); - const auto tune_time = std::chrono::milliseconds(1); + const uint64_t run_time = 3; + const uint64_t tune_time = 1; const size_t max_mem = 32; for(const std::string& pwdhash : all_pwdhash) { @@ -85,35 +86,41 @@ if(pwdhash_fam) { result.start_timer(); - const std::vector salt(8); - const std::string password = "test"; - - auto tuned_pwhash = pwdhash_fam->tune(32, run_time, max_mem, tune_time); - - std::vector output1(32); - tuned_pwhash->hash(output1, password, salt); - - std::unique_ptr pwhash; - - if(pwdhash_fam->name() == "Scrypt" || pwdhash_fam->name().starts_with("Argon2")) { - pwhash = pwdhash_fam->from_params( - tuned_pwhash->memory_param(), tuned_pwhash->iterations(), tuned_pwhash->parallelism()); - } else { - pwhash = pwdhash_fam->from_params(tuned_pwhash->iterations()); + std::unique_ptr tuned_pwhash; + try { + tuned_pwhash = pwdhash_fam->tune_params(32, run_time, max_mem, tune_time); + } catch(const Botan::Not_Implemented&) { + // tune_params requires os_utils for clock access; not available in minimized builds + result.test_note("tune_params not available in current build config"); } - std::vector output2(32); - pwhash->hash(output2, password, salt); - - result.test_eq("PasswordHash produced same output when run with same params", output1, output2); - - auto default_pwhash = pwdhash_fam->default_params(); - std::vector output3(32); - default_pwhash->hash(output3, password, salt); - + if(tuned_pwhash != nullptr) { + std::vector output1(32); + const std::vector salt(8); + const std::string password = "test"; + tuned_pwhash->hash(output1, password, salt); + + std::unique_ptr pwhash; + + if(pwdhash_fam->name() == "Scrypt" || pwdhash_fam->name().starts_with("Argon2")) { + pwhash = pwdhash_fam->from_params( + tuned_pwhash->memory_param(), tuned_pwhash->iterations(), tuned_pwhash->parallelism()); + } else { + pwhash = pwdhash_fam->from_params(tuned_pwhash->iterations()); + } + + std::vector output2(32); + pwhash->hash(output2, password, salt); + + result.test_bin_eq("PasswordHash produced same output when run with same params", output1, output2); + + auto default_pwhash = pwdhash_fam->default_params(); + std::vector output3(32); + default_pwhash->hash(output3, password, salt); + } result.end_timer(); } else { - result.test_note("No such algo " + pwdhash); + result.test_note("No such algo", pwdhash); } results.push_back(result); @@ -153,7 +160,7 @@ std::vector derived(expected.size()); pwdhash->hash(derived, passphrase, salt); - result.test_eq("derived key", derived, expected); + result.test_bin_eq("derived key", derived, expected); return result; } @@ -195,7 +202,7 @@ std::vector pwdhash_derived(expected.size()); pwdhash->hash(pwdhash_derived, passphrase, salt); - result.test_eq("pwdhash derived key", pwdhash_derived, expected); + result.test_bin_eq("pwdhash derived key", pwdhash_derived, expected); return result; } @@ -237,7 +244,7 @@ std::vector pwdhash_derived(expected.size()); pwdhash->hash(pwdhash_derived, passphrase_str, salt, ad, key); - result.test_eq("pwdhash derived key", pwdhash_derived, expected); + result.test_bin_eq("pwdhash derived key", pwdhash_derived, expected); return result; } @@ -257,30 +264,30 @@ // The maximum representable iteration count const size_t max_iter = 65011712; - result.test_eq("Encoding of large value accepted", Botan::RFC4880_encode_count(max_iter * 2), size_t(255)); - result.test_eq("Encoding of small value accepted", Botan::RFC4880_encode_count(0), size_t(0)); + result.test_sz_eq("Encoding of large value accepted", Botan::RFC4880_encode_count(max_iter * 2), size_t(255)); + result.test_sz_eq("Encoding of small value accepted", Botan::RFC4880_encode_count(0), size_t(0)); for(size_t c = 0; c != 256; ++c) { const size_t dec = Botan::RFC4880_decode_count(static_cast(c)); const size_t comp_dec = (16 + (c & 0x0F)) << ((c >> 4) + 6); - result.test_eq("Decoded value matches PGP formula", dec, comp_dec); + result.test_sz_eq("Decoded value matches PGP formula", dec, comp_dec); const size_t enc = Botan::RFC4880_encode_count(comp_dec); - result.test_eq("Encoded value matches PGP formula", enc, c); + result.test_sz_eq("Encoded value matches PGP formula", enc, c); } uint8_t last_enc = 0; for(size_t i = 0; i <= max_iter; i += 64) { const uint8_t enc = Botan::RFC4880_encode_count(i); - result.test_lte("Encoded value non-decreasing", last_enc, enc); + result.test_sz_lte("Encoded value non-decreasing", last_enc, enc); /* The iteration count as encoded may not be exactly the value requested, but should never be less */ const size_t dec = Botan::RFC4880_decode_count(enc); - result.test_gte("Decoded value is >= requested", dec, i); + result.test_sz_gte("Decoded value is >= requested", dec, i); last_enc = enc; } diff -Nru botan3-3.7.1+dfsg/src/tests/test_pem.cpp botan3-3.12.0+dfsg/src/tests/test_pem.cpp --- botan3-3.7.1+dfsg/src/tests/test_pem.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_pem.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -12,21 +12,23 @@ namespace Botan_Tests { +namespace { + class PEM_Tests : public Test { public: std::vector run() override { Test::Result result("PEM encoding"); - std::vector vec = {0, 1, 2, 3, 4}; + const std::vector vec = {0, 1, 2, 3, 4}; const std::string pem1 = Botan::PEM_Code::encode(vec, "BUNNY", 3); - result.test_eq("PEM encoding", pem1, "-----BEGIN BUNNY-----\nAAE\nCAw\nQ=\n-----END BUNNY-----\n"); + result.test_str_eq("PEM encoding", pem1, "-----BEGIN BUNNY-----\nAAE\nCAw\nQ=\n-----END BUNNY-----\n"); std::string label1 = "this is overwritten"; const Botan::secure_vector decoded1 = Botan::PEM_Code::decode(pem1, label1); - result.test_eq("PEM decoding label", label1, "BUNNY"); + result.test_str_eq("PEM decoding label", label1, "BUNNY"); result.test_throws("PEM decoding unexpected label", "PEM: Label mismatch, wanted 'FLOOFY' got 'BUNNY'", @@ -48,6 +50,8 @@ BOTAN_REGISTER_TEST("pubkey", "pem", PEM_Tests); +} // namespace + } // namespace Botan_Tests #endif diff -Nru botan3-3.7.1+dfsg/src/tests/test_pk_pad.cpp botan3-3.12.0+dfsg/src/tests/test_pk_pad.cpp --- botan3-3.7.1+dfsg/src/tests/test_pk_pad.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_pk_pad.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -5,15 +5,21 @@ */ #include "tests.h" +#include -#if defined(BOTAN_HAS_PK_PADDING) - #include - #include +#if defined(BOTAN_HAS_RSA_ENCRYPTION_PADDING) + #include +#endif + +#if defined(BOTAN_HAS_RSA_SIGNATURE_PADDING) #include + #include #endif namespace Botan_Tests { +namespace { + #if defined(BOTAN_HAS_EME_PKCS1) class EME_PKCS1v15_Decoding_Tests final : public Text_Based_Test { public: @@ -24,7 +30,7 @@ Test::Result result("PKCSv15 Decoding"); - auto pkcs = Botan::EME::create("PKCS1v15"); + auto pkcs = Botan::EncryptionPaddingScheme::create("PKCS1v15"); if(!pkcs) { return result; } @@ -32,27 +38,27 @@ const std::vector ciphertext = vars.get_req_bin("RawCiphertext"); const std::vector plaintext = vars.get_opt_bin("Plaintext"); - if(is_valid == false) { - result.test_eq("Plaintext value should be empty for invalid EME inputs", plaintext.size(), 0); + if(!is_valid) { + result.test_sz_eq("Plaintext value should be empty for invalid EME inputs", plaintext.size(), 0); } std::vector decoded(ciphertext.size()); auto len = pkcs->unpad(decoded, ciphertext); - result.test_eq("EME decoding valid/invalid matches", len.has_value().as_bool(), is_valid); + result.test_bool_eq("EME decoding valid/invalid matches", len.has_value().as_bool(), is_valid); if(len.has_value().as_bool()) { decoded.resize(len.value_or(0)); - result.test_eq("EME decoded plaintext correct", decoded, plaintext); + result.test_bin_eq("EME decoded plaintext correct", decoded, plaintext); } else { bool all_zeros = true; - for(size_t i = 0; i != decoded.size(); ++i) { - if(decoded[i] != 0) { + for(const uint8_t b : decoded) { + if(b != 0) { all_zeros = false; } } - result.confirm("On invalid padding output is all zero", all_zeros); + result.test_is_true("On invalid padding output is all zero", all_zeros); } // TODO: also test that encoding is accepted @@ -64,13 +70,13 @@ BOTAN_REGISTER_TEST("pubkey", "eme_pkcs1v15", EME_PKCS1v15_Decoding_Tests); #endif -#if defined(BOTAN_HAS_PK_PADDING) -class EMSA_unit_tests final : public Test { +#if defined(BOTAN_HAS_RSA_SIGNATURE_PADDING) +class SignaturePaddingSchemeNameTests final : public Test { public: std::vector run() override { - Test::Result name_tests("EMSA_name_tests"); + Test::Result result("SignaturePaddingScheme::name"); - std::vector pads_need_hash = { + const std::vector pads_need_hash = { #if BOTAN_HAS_EMSA_X931 "X9.31", #endif @@ -87,7 +93,7 @@ #endif }; - std::vector pads_no_hash = { + const std::vector pads_no_hash = { #if BOTAN_HAS_EMSA_RAW "Raw", #endif @@ -100,47 +106,51 @@ for(const auto& pad : pads_need_hash) { try { const std::string hash_to_use = "SHA-256"; - auto emsa = Botan::EMSA::create_or_throw(Botan::fmt("{}({})", pad, hash_to_use)); - auto emsa_copy = Botan::EMSA::create(emsa->name()); - name_tests.test_eq("EMSA_name_test for " + pad, emsa->name(), emsa_copy->name()); + auto padding = Botan::SignaturePaddingScheme::create_or_throw(Botan::fmt("{}({})", pad, hash_to_use)); + auto padding_copy = Botan::SignaturePaddingScheme::create(padding->name()); + result.test_str_eq("SignaturePaddingScheme::name for " + pad, padding->name(), padding_copy->name()); } catch(Botan::Lookup_Error&) { - name_tests.test_note("Skipping test due to missing hash"); + result.test_note("Skipping test due to missing hash"); } catch(const std::exception& e) { - name_tests.test_failure("EMSA_name_test for " + pad + ": " + e.what()); + result.test_failure("SignaturePaddingScheme::name for " + pad + ": " + e.what()); } } for(const auto& pad : pads_need_hash) { - std::string algo_name = pad + "(YYZ)"; + const std::string algo_name = pad + "(YYZ)"; try { - auto emsa = Botan::EMSA::create_or_throw(algo_name); - name_tests.test_failure("EMSA_name_test for " + pad + ": " + "Could create EMSA with fantasy hash YYZ"); + auto padding = Botan::SignaturePaddingScheme::create_or_throw(algo_name); + result.test_failure("SignaturePaddingScheme::name for " + pad + ": " + + "Could create SignaturePaddingScheme with fantasy hash YYZ"); } catch(Botan::Lookup_Error&) { - name_tests.test_note("Skipping test due to missing hash"); + result.test_note("Skipping test due to missing hash"); } catch(const std::exception& e) { - name_tests.test_eq( - "EMSA_name_test for " + pad, e.what(), "Could not find any algorithm named \"" + algo_name + "\""); + result.test_str_eq("SignaturePaddingScheme::name for " + pad, + e.what(), + "Could not find any algorithm named \"" + algo_name + "\""); } } for(const auto& pad : pads_no_hash) { try { - auto emsa = Botan::EMSA::create(pad); - auto emsa_copy = Botan::EMSA::create(emsa->name()); - name_tests.test_eq("EMSA_name_test for " + pad, emsa->name(), emsa_copy->name()); + auto padding = Botan::SignaturePaddingScheme::create(pad); + auto padding_copy = Botan::SignaturePaddingScheme::create(padding->name()); + result.test_str_eq("SignaturePaddingScheme::name for " + pad, padding->name(), padding_copy->name()); } catch(Botan::Lookup_Error&) { - name_tests.test_note("Skipping test due to missing hash"); + result.test_note("Skipping test due to missing hash"); } catch(const std::exception& e) { - name_tests.test_failure("EMSA_name_test for " + pad + ": " + e.what()); + result.test_failure("SignaturePaddingScheme::name for " + pad + ": " + e.what()); } } - return {name_tests}; + return {result}; } }; -BOTAN_REGISTER_TEST("pubkey", "pk_pad_emsa_unit", EMSA_unit_tests); +BOTAN_REGISTER_TEST("pubkey", "sig_padding_name", SignaturePaddingSchemeNameTests); #endif +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_pkcs11.h botan3-3.12.0+dfsg/src/tests/test_pkcs11.h --- botan3-3.7.1+dfsg/src/tests/test_pkcs11.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_pkcs11.h 2026-05-07 01:38:28.000000000 +0000 @@ -21,18 +21,19 @@ #if defined(BOTAN_HAS_PKCS11) + // NOLINTNEXTLINE(*-macro-usage) #define STRING_AND_FUNCTION(x) #x, x // PIN is expected to be set to "123456" prior to running the tests -const std::string PKCS11_USER_PIN = "123456"; +const std::string_view PKCS11_USER_PIN = "123456"; // SO PIN is expected to be set to "12345678" prior to running the tests -const std::string PKCS11_SO_PIN = "12345678"; +const std::string_view PKCS11_SO_PIN = "12345678"; // These are pins that should just not match the above (valid) PINs -const std::string PKCS11_TEST_USER_PIN = "654321"; -const std::string PKCS11_TEST_SO_PIN = "87654321"; +const std::string_view PKCS11_TEST_USER_PIN = "654321"; +const std::string_view PKCS11_TEST_SO_PIN = "87654321"; -inline Botan::PKCS11::secure_string to_sec_string(const std::string& str) { +inline Botan::PKCS11::secure_string to_sec_string(std::string_view str) { return Botan::PKCS11::secure_string(str.begin(), str.end()); } diff -Nru botan3-3.7.1+dfsg/src/tests/test_pkcs11_high_level.cpp botan3-3.12.0+dfsg/src/tests/test_pkcs11_high_level.cpp --- botan3-3.7.1+dfsg/src/tests/test_pkcs11_high_level.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_pkcs11_high_level.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,19 +7,20 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include "test_pkcs11.h" #include "tests.h" -#include -#include -#include -#include -#include - #if defined(BOTAN_HAS_PKCS11) + #include "test_pkcs11.h" #include #include #include + #include + #include + #include + #include + #include + #include + #include #endif #if defined(BOTAN_HAS_ASN1) @@ -60,6 +61,7 @@ #if defined(BOTAN_HAS_HMAC_DRBG) #include + #include #endif namespace Botan_Tests { @@ -70,17 +72,17 @@ std::vector>>& fns) { std::vector results; - for(size_t i = 0; i != fns.size(); ++i) { + for(const auto& [fn_name, fn] : fns) { try { - results.push_back(fns[i].second()); + results.push_back(fn()); } catch(Botan::PKCS11::PKCS11_ReturnError& e) { - results.push_back(Test::Result::Failure(name + " test " + fns[i].first, e.what())); + results.push_back(Test::Result::Failure(Botan::fmt("{} test {}", name, fn_name), e.what())); if(e.get_return_value() == Botan::PKCS11::ReturnValue::PinIncorrect) { break; // Do not continue to not potentially lock the token } } catch(std::exception& e) { - results.push_back(Test::Result::Failure(name + " test " + fns[i].first, e.what())); + results.push_back(Test::Result::Failure(Botan::fmt("{} test {}", name, fn_name), e.what())); } } @@ -118,9 +120,9 @@ Test::Result test_module_ctor() { Test::Result result("Module ctor"); - result.test_throws("Module ctor fails for non existent path", []() { Module failing_module("/a/b/c"); }); + result.test_throws("Module ctor fails for non existent path", []() { Module("/a/b/c"); }); - Module module(Test::pkcs11_lib()); + const Module module(Test::pkcs11_lib()); result.test_success("Module ctor did not throw and completed successfully"); return result; @@ -142,10 +144,10 @@ Test::Result test_multiple_modules() { Test::Result result("Module copy"); - Module first_module(Test::pkcs11_lib()); + const Module first_module(Test::pkcs11_lib()); result.test_throws("Module ctor fails if module is already initialized", - []() { Module second_module(Test::pkcs11_lib()); }); + []() { const Module second_module(Test::pkcs11_lib()); }); return result; } @@ -153,10 +155,10 @@ Test::Result test_module_get_info() { Test::Result result("Module info"); - Module module(Test::pkcs11_lib()); + const Module module(Test::pkcs11_lib()); - Info info = module.get_info(); - result.test_ne("Cryptoki version != 0", info.cryptokiVersion.major, 0); + const Info info = module.get_info(); + result.test_sz_ne("Cryptoki version != 0", info.cryptokiVersion.major, 0); return result; } @@ -182,8 +184,8 @@ Test::Result result("Slot get_available_slots"); Module module(Test::pkcs11_lib()); - std::vector slot_vec = Slot::get_available_slots(module, true); - result.test_gte("Available Slots with attached token >= 1", slot_vec.size(), 1); + const std::vector slot_vec = Slot::get_available_slots(module, true); + result.test_sz_gte("Available Slots with attached token >= 1", slot_vec.size(), 1); return result; } @@ -194,9 +196,9 @@ Module module(Test::pkcs11_lib()); std::vector slot_vec = Slot::get_available_slots(module, true); - Slot slot(module, slot_vec.at(0)); + const Slot slot(module, slot_vec.at(0)); result.test_success("Slot ctor completed successfully"); - result.test_is_eq(slot.slot_id(), slot_vec.at(0)); + result.test_u64_eq(slot.slot_id(), slot_vec.at(0)); return result; } @@ -206,11 +208,11 @@ Module module(Test::pkcs11_lib()); std::vector slot_vec = Slot::get_available_slots(module, true); - Slot slot(module, slot_vec.at(0)); + const Slot slot(module, slot_vec.at(0)); SlotInfo info = slot.get_slot_info(); - std::string description = reinterpret_cast(info.slotDescription); - result.confirm("Slot description is not empty", !description.empty()); + const std::string description = reinterpret_cast(info.slotDescription); + result.test_is_true("Slot description is not empty", !description.empty()); return result; } @@ -233,7 +235,7 @@ Module module(Test::pkcs11_lib()); - SlotId invalid_id = get_invalid_slot_id(module); + const SlotId invalid_id = get_invalid_slot_id(module); Slot slot(module, invalid_id); @@ -247,11 +249,11 @@ Module module(Test::pkcs11_lib()); std::vector slot_vec = Slot::get_available_slots(module, true); - Slot slot(module, slot_vec.at(0)); + const Slot slot(module, slot_vec.at(0)); TokenInfo info = slot.get_token_info(); - std::string label = reinterpret_cast(info.label); - result.confirm("Token label is not empty", !label.empty()); + const std::string label = reinterpret_cast(info.label); + result.test_is_true("Token label is not empty", !label.empty()); return result; } @@ -261,10 +263,10 @@ Module module(Test::pkcs11_lib()); std::vector slot_vec = Slot::get_available_slots(module, true); - Slot slot(module, slot_vec.at(0)); + const Slot slot(module, slot_vec.at(0)); - std::vector mechanisms = slot.get_mechanism_list(); - result.confirm("The Slot supports at least one mechanism", !mechanisms.empty()); + const std::vector mechanisms = slot.get_mechanism_list(); + result.test_is_true("The Slot supports at least one mechanism", !mechanisms.empty()); return result; } @@ -274,7 +276,7 @@ Module module(Test::pkcs11_lib()); std::vector slot_vec = Slot::get_available_slots(module, true); - Slot slot(module, slot_vec.at(0)); + const Slot slot(module, slot_vec.at(0)); slot.get_mechanism_info(MechanismType::RsaPkcsKeyPairGen); result.test_success("get_mechanism_info() completed successfully."); @@ -310,21 +312,21 @@ Slot slot(module, slot_vec.at(0)); { - Session read_only_session(slot, true); + const Session read_only_session(slot, true); result.test_success("read only session opened successfully"); } { - Session read_write_session(slot, false); + const Session read_write_session(slot, false); result.test_success("read write session opened successfully"); } { - Flags flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); - Session read_write_session2(slot, flags, nullptr, nullptr); + const Flags flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); + const Session read_write_session2(slot, flags, nullptr, nullptr); result.test_success("read write session with flags param opened successfully"); } { - Session read_only_session(slot, true); - Session read_write_session(slot, false); + const Session read_only_session(slot, true); + const Session read_write_session(slot, false); result.test_success("Opened multiple sessions successfully"); } @@ -336,10 +338,10 @@ Module module(Test::pkcs11_lib()); - SlotId invalid_id = get_invalid_slot_id(module); + const SlotId invalid_id = get_invalid_slot_id(module); Slot slot(module, invalid_id); - result.test_throws("Session ctor with invalid slot id fails", [&slot]() { Session session(slot, true); }); + result.test_throws("Session ctor with invalid slot id fails", [&slot]() { Session(slot, true); }); return result; } @@ -352,9 +354,9 @@ Slot slot(module, slot_vec.at(0)); Session session(slot, false); - SessionHandle handle = session.release(); + const SessionHandle handle = session.release(); - Session session2(slot, handle); + const Session session2(slot, handle); result.test_success("releasing ownership and taking ownership works as expected."); return result; @@ -387,13 +389,13 @@ Session session(slot, false); SessionInfo info = session.get_info(); - result.test_is_eq("slot id is correct", info.slotID, slot_vec.at(0)); - result.test_is_eq( + result.test_u64_eq("slot id is correct", info.slotID, slot_vec.at(0)); + result.test_u64_eq( "state is a read write public session", info.state, static_cast(SessionState::RwPublicSession)); session.login(UserType::User, PIN()); info = session.get_info(); - result.test_is_eq( + result.test_u64_eq( "state is a read write user session", info.state, static_cast(SessionState::RwUserFunctions)); session.logoff(); @@ -429,10 +431,10 @@ AttributeContainer attributes; attributes.add_class(ObjectClass::PrivateKey); - std::string label("test"); + const std::string label("test"); attributes.add_string(AttributeType::Label, label); - std::vector bin(4); + const std::vector bin(4); attributes.add_binary(AttributeType::Value, bin); attributes.add_bool(AttributeType::Sensitive, true); @@ -443,27 +445,30 @@ attributes.add_numeric(AttributeType::Id, 21); attributes.add_numeric(AttributeType::PixelY, 40); - result.test_eq("8 elements in attribute container", attributes.count(), 8); + result.test_sz_eq("8 elements in attribute container", attributes.count(), 8); const std::vector& storedAttributes = attributes.attributes(); - result.test_int_eq("ObjectId type", storedAttributes.at(4).type, AttributeType::ObjectId); - result.test_int_eq("ObjectId value", *reinterpret_cast(storedAttributes.at(4).pValue), 10); - result.test_int_eq("Id type", storedAttributes.at(5).type, AttributeType::Id); - result.test_int_eq("Id value", *reinterpret_cast(storedAttributes.at(5).pValue), 21); - result.test_int_eq("PixelX type", storedAttributes.at(6).type, AttributeType::PixelX); - result.test_int_eq("PixelX value", *reinterpret_cast(storedAttributes.at(6).pValue), 30); - result.test_int_eq("PixelY type", storedAttributes.at(7).type, AttributeType::PixelY); - result.test_int_eq("PixelY value", *reinterpret_cast(storedAttributes.at(7).pValue), 40); + result.test_u64_eq( + "ObjectId type", storedAttributes.at(4).type, static_cast(AttributeType::ObjectId)); + result.test_u64_eq("ObjectId value", *reinterpret_cast(storedAttributes.at(4).pValue), 10); + result.test_u64_eq("Id type", storedAttributes.at(5).type, static_cast(AttributeType::Id)); + result.test_u64_eq("Id value", *reinterpret_cast(storedAttributes.at(5).pValue), 21); + result.test_u64_eq( + "PixelX type", storedAttributes.at(6).type, static_cast(AttributeType::PixelX)); + result.test_u64_eq("PixelX value", *reinterpret_cast(storedAttributes.at(6).pValue), 30); + result.test_u64_eq( + "PixelY type", storedAttributes.at(7).type, static_cast(AttributeType::PixelY)); + result.test_u64_eq("PixelY value", *reinterpret_cast(storedAttributes.at(7).pValue), 40); return result; } DataObjectProperties make_test_object(const std::string& label) { std::string value_string("test data"); - secure_vector value(value_string.begin(), value_string.end()); + const secure_vector value(value_string.begin(), value_string.end()); - std::size_t id = 1337; - std::string application = "Botan test application"; + const std::size_t id = 1337; + const std::string application = "Botan test application"; std::vector encoded_id; DER_Encoder(encoded_id).encode(id); @@ -483,11 +488,11 @@ Test::Result test_create_destroy_data_object() { Test::Result result("Object create/delete data object"); - TestSession test_session(true); + const TestSession test_session(true); const std::string label = "Botan test data object"; auto data_obj_props = make_test_object(label); - Object data_obj(test_session.session(), data_obj_props); + const Object data_obj(test_session.session(), data_obj_props); result.test_success("Data object creation was successful"); data_obj.destroy(); @@ -499,27 +504,27 @@ Test::Result test_get_set_attribute_values() { Test::Result result("Object get/set attributes"); - TestSession test_session(true); + const TestSession test_session(true); // create object const std::string label = "Botan test data object"; auto data_obj_props = make_test_object(label); - Object data_obj(test_session.session(), data_obj_props); + const Object data_obj(test_session.session(), data_obj_props); // get attribute secure_vector retrieved_label = data_obj.get_attribute_value(AttributeType::Label); std::string retrieved_label_string(retrieved_label.begin(), retrieved_label.end()); - result.test_eq("label was set correctly", retrieved_label_string, label); + result.test_str_eq("label was set correctly", retrieved_label_string, label); // set attribute std::string new_label = "Botan test modified data object label"; - secure_vector new_label_secvec(new_label.begin(), new_label.end()); + const secure_vector new_label_secvec(new_label.begin(), new_label.end()); data_obj.set_attribute_value(AttributeType::Label, new_label_secvec); // get and check attribute retrieved_label = data_obj.get_attribute_value(AttributeType::Label); retrieved_label_string = std::string(retrieved_label.begin(), retrieved_label.end()); - result.test_eq("label was modified correctly", retrieved_label_string, new_label); + result.test_str_eq("label was modified correctly", retrieved_label_string, new_label); data_obj.destroy(); return result; @@ -528,12 +533,12 @@ Test::Result test_object_finder() { Test::Result result("ObjectFinder"); - TestSession test_session(true); + const TestSession test_session(true); // create object const std::string label = "Botan test data object"; auto data_obj_props = make_test_object(label); - Object data_obj(test_session.session(), data_obj_props); + const Object data_obj(test_session.session(), data_obj_props); // search created object AttributeContainer search_template; @@ -541,18 +546,18 @@ ObjectFinder finder(test_session.session(), search_template.attributes()); auto search_result = finder.find(); - result.test_eq("one object found", search_result.size(), 1); + result.test_sz_eq("one object found", search_result.size(), 1); finder.finish(); - Object obj_found(test_session.session(), search_result.at(0)); - result.test_eq("found the object just created (same application)", - obj_found.get_attribute_value(AttributeType::Application), - data_obj.get_attribute_value(AttributeType::Application)); + const Object obj_found(test_session.session(), search_result.at(0)); + result.test_bin_eq("found the object just created (same application)", + obj_found.get_attribute_value(AttributeType::Application), + data_obj.get_attribute_value(AttributeType::Application)); auto search_result2 = Object::search(test_session.session(), search_template.attributes()); - result.test_eq("found the object just created (same label)", - obj_found.get_attribute_value(AttributeType::Label), - search_result2.at(0).get_attribute_value(AttributeType::Label)); + result.test_bin_eq("found the object just created (same label)", + obj_found.get_attribute_value(AttributeType::Label), + search_result2.at(0).get_attribute_value(AttributeType::Label)); data_obj.destroy(); return result; @@ -561,25 +566,25 @@ Test::Result test_object_copy() { Test::Result result("Object copy"); - TestSession test_session(true); + const TestSession test_session(true); // create object const std::string label = "Botan test data object"; auto data_obj_props = make_test_object(label); - Object data_obj(test_session.session(), data_obj_props); + const Object data_obj(test_session.session(), data_obj_props); // copy created object AttributeContainer copy_attributes; copy_attributes.add_string(AttributeType::Label, "Botan test copied object"); - ObjectHandle copied_obj_handle = data_obj.copy(copy_attributes); + const ObjectHandle copied_obj_handle = data_obj.copy(copy_attributes); - ObjectFinder searcher(test_session.session(), copy_attributes.attributes()); + const ObjectFinder searcher(test_session.session(), copy_attributes.attributes()); auto search_result = searcher.find(); - result.test_eq("one object found", search_result.size(), 1); + result.test_sz_eq("one object found", search_result.size(), 1); data_obj.destroy(); - Object copied_obj(test_session.session(), copied_obj_handle); + const Object copied_obj(test_session.session(), copied_obj_handle); copied_obj.destroy(); return result; } @@ -612,13 +617,13 @@ Test::Result test_rsa_privkey_import() { Test::Result result("PKCS11 import RSA private key"); - TestSession test_session(true); + const TestSession test_session(true); auto rng = Test::new_rng(__func__); // create private key - RSA_PrivateKey priv_key(*rng, 2048); - result.confirm("Key self test OK", priv_key.check_key(*rng, true)); + const RSA_PrivateKey priv_key(*rng, 2048); + result.test_is_true("Key self test OK", priv_key.check_key(*rng, true)); // import to card RSA_PrivateKeyImportProperties props(priv_key.get_n(), priv_key.get_d()); @@ -634,9 +639,9 @@ props.set_decrypt(true); props.set_sign(true); - PKCS11_RSA_PrivateKey pk(test_session.session(), props); + const 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(*rng, true)); + result.test_is_true("PK self test OK", pk.check_key(*rng, true)); pk.destroy(); return result; @@ -645,12 +650,12 @@ Test::Result test_rsa_privkey_export() { Test::Result result("PKCS11 export RSA private key"); - TestSession test_session(true); + const TestSession test_session(true); auto rng = Test::new_rng(__func__); // create private key - RSA_PrivateKey priv_key(*rng, 2048); + const RSA_PrivateKey priv_key(*rng, 2048); // import to card RSA_PrivateKeyImportProperties props(priv_key.get_n(), priv_key.get_d()); @@ -668,12 +673,12 @@ props.set_extractable(true); props.set_sensitive(false); - PKCS11_RSA_PrivateKey pk(test_session.session(), props); - result.confirm("Check PK11 key", pk.check_key(*rng, true)); + const PKCS11_RSA_PrivateKey pk(test_session.session(), props); + result.test_is_true("Check PK11 key", pk.check_key(*rng, true)); - RSA_PrivateKey exported = pk.export_key(); + const RSA_PrivateKey exported = pk.export_key(); result.test_success("RSA private key export was successful"); - result.confirm("Check exported key", exported.check_key(*rng, true)); + result.test_is_true("Check exported key", exported.check_key(*rng, true)); pk.destroy(); return result; @@ -682,12 +687,12 @@ Test::Result test_rsa_pubkey_import() { Test::Result result("PKCS11 import RSA public key"); - TestSession test_session(true); + const TestSession test_session(true); auto rng = Test::new_rng(__func__); // create public key from private key - RSA_PrivateKey priv_key(*rng, 2048); + const RSA_PrivateKey priv_key(*rng, 2048); // import to card RSA_PublicKeyImportProperties props(priv_key.get_n(), priv_key.get_e()); @@ -695,9 +700,9 @@ props.set_encrypt(true); props.set_private(false); - PKCS11_RSA_PublicKey pk(test_session.session(), props); + const 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(*rng, true)); + result.test_is_true("Check PK11 key", pk.check_key(*rng, true)); pk.destroy(); @@ -706,7 +711,7 @@ Test::Result test_rsa_generate_private_key() { Test::Result result("PKCS11 generate RSA private key"); - TestSession test_session(true); + const TestSession test_session(true); RSA_PrivateKeyGenerationProperties props; props.set_token(true); @@ -714,7 +719,7 @@ props.set_sign(true); props.set_decrypt(true); - PKCS11_RSA_PrivateKey pk(test_session.session(), 2048, props); + const PKCS11_RSA_PrivateKey pk(test_session.session(), 2048, props); result.test_success("RSA private key generation was successful"); pk.destroy(); @@ -743,9 +748,9 @@ Test::Result test_rsa_generate_key_pair() { Test::Result result("PKCS11 generate RSA key pair"); - TestSession test_session(true); + const TestSession test_session(true); - PKCS11_RSA_KeyPair keypair = generate_rsa_keypair(test_session); + const PKCS11_RSA_KeyPair keypair = generate_rsa_keypair(test_session); result.test_success("RSA key pair generation was successful"); keypair.first.destroy(); @@ -756,7 +761,7 @@ Test::Result test_rsa_encrypt_decrypt() { Test::Result result("PKCS11 RSA encrypt decrypt"); - TestSession test_session(true); + const TestSession test_session(true); // generate key pair PKCS11_RSA_KeyPair keypair = generate_rsa_keypair(test_session); @@ -768,7 +773,7 @@ std::vector encrypted; try { - Botan::PK_Encryptor_EME encryptor(keypair.first, *rng, padding); + const 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()); @@ -778,7 +783,7 @@ try { keypair.second.set_use_software_padding(blinding); - Botan::PK_Decryptor_EME decryptor(keypair.second, *rng, padding); + const Botan::PK_Decryptor_EME decryptor(keypair.second, *rng, padding); decrypted = decryptor.decrypt(encrypted); } catch(Botan::PKCS11::PKCS11_ReturnError& e) { std::ostringstream err; @@ -790,7 +795,7 @@ result.test_failure(err.str(), e.what()); } - result.test_eq("RSA PKCS11 encrypt and decrypt: " + padding, decrypted, plaintext); + result.test_bin_eq("RSA PKCS11 encrypt and decrypt: " + padding, decrypted, plaintext); }; std::vector plaintext(256); @@ -812,7 +817,7 @@ Test::Result test_rsa_sign_verify() { Test::Result result("PKCS11 RSA sign and verify"); - TestSession test_session(true); + const TestSession test_session(true); // generate key pair PKCS11_RSA_KeyPair keypair = generate_rsa_keypair(test_session); @@ -822,8 +827,8 @@ std::vector plaintext(256); std::iota(std::begin(plaintext), std::end(plaintext), static_cast(0)); - auto sign_and_verify = [&](const std::string& emsa, bool multipart) { - Botan::PK_Signer signer(keypair.second, *rng, emsa, Botan::Signature_Format::Standard); + auto sign_and_verify = [&](const std::string& padding, bool multipart) { + Botan::PK_Signer signer(keypair.second, *rng, padding, Botan::Signature_Format::Standard); std::vector signature; if(multipart) { signer.update(plaintext.data(), plaintext.size() / 2); @@ -832,7 +837,7 @@ signature = signer.sign_message(plaintext, *rng); } - Botan::PK_Verifier verifier(keypair.first, emsa, Botan::Signature_Format::Standard); + Botan::PK_Verifier verifier(keypair.first, padding, Botan::Signature_Format::Standard); bool rsa_ok = false; if(multipart) { verifier.update(plaintext.data(), plaintext.size() / 2); @@ -842,7 +847,7 @@ rsa_ok = verifier.verify_message(plaintext, signature); } - result.test_eq("RSA PKCS11 sign and verify: " + emsa, rsa_ok, true); + result.test_is_true("RSA PKCS11 sign and verify: " + padding, rsa_ok); }; // single-part sign @@ -894,13 +899,13 @@ Test::Result test_ecdsa_privkey_import() { Test::Result result("PKCS11 import ECDSA private key"); - TestSession test_session(true); + const TestSession test_session(true); auto rng = Test::new_rng(__func__); // create ecdsa private key - ECDSA_PrivateKey priv_key(*rng, EC_Group::from_name("secp256r1")); - result.confirm("Key self test OK", priv_key.check_key(*rng, true)); + const ECDSA_PrivateKey priv_key(*rng, EC_Group::from_name("secp256r1")); + result.test_is_true("Key self test OK", priv_key.check_key(*rng, true)); // import to card EC_PrivateKeyImportProperties props(priv_key.DER_domain(), priv_key.private_value()); @@ -909,13 +914,13 @@ props.set_sign(true); // label - std::string label = "Botan test ecdsa key"; + const std::string label = "Botan test ecdsa key"; props.set_label(label); PKCS11_ECDSA_PrivateKey pk(test_session.session(), props); result.test_success("ECDSA private key import was successful"); pk.set_public_point(priv_key._public_ec_point()); - result.confirm("P11 key self test OK", pk.check_key(*rng, false)); + result.test_is_true("P11 key self test OK", pk.check_key(*rng, false)); pk.destroy(); return result; @@ -924,14 +929,14 @@ Test::Result test_ecdsa_privkey_export() { Test::Result result("PKCS11 export ECDSA private key"); - TestSession test_session(true); + const TestSession test_session(true); auto rng = Test::new_rng(__func__); // create private key - ECDSA_PrivateKey priv_key(*rng, EC_Group::from_name("secp256r1")); + const ECDSA_PrivateKey priv_key(*rng, EC_Group::from_name("secp256r1")); - result.confirm("Check ECDSA key", priv_key.check_key(*rng, true)); + result.test_is_true("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); @@ -940,17 +945,17 @@ props.set_extractable(true); // label - std::string label = "Botan test ecdsa key"; + const std::string label = "Botan test ecdsa key"; props.set_label(label); PKCS11_ECDSA_PrivateKey pk(test_session.session(), props); pk.set_public_point(priv_key._public_ec_point()); - result.confirm("Check PK11 key", pk.check_key(*rng, false)); + result.test_is_true("Check PK11 key", pk.check_key(*rng, false)); - ECDSA_PrivateKey exported = pk.export_key(); + const ECDSA_PrivateKey exported = pk.export_key(); result.test_success("ECDSA private key export was successful"); - 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()); + result.test_is_true("Check exported key valid", exported.check_key(*rng, true)); + result.test_bin_eq("Check exported key contents", exported.private_key_bits(), priv_key.private_key_bits()); pk.destroy(); return result; @@ -959,12 +964,12 @@ Test::Result test_ecdsa_pubkey_import() { Test::Result result("PKCS11 import ECDSA public key"); - TestSession test_session(true); + const TestSession test_session(true); auto rng = Test::new_rng(__func__); // create ecdsa private key - ECDSA_PrivateKey priv_key(*rng, EC_Group::from_name("secp256r1")); + const ECDSA_PrivateKey priv_key(*rng, EC_Group::from_name("secp256r1")); const auto enc_point = encode_ec_point_in_octet_str(priv_key); @@ -975,10 +980,10 @@ props.set_private(false); // label - std::string label = "Botan test ecdsa pub key"; + const std::string label = "Botan test ecdsa pub key"; props.set_label(label); - PKCS11_ECDSA_PublicKey pk(test_session.session(), props); + const PKCS11_ECDSA_PublicKey pk(test_session.session(), props); result.test_success("ECDSA public key import was successful"); pk.destroy(); @@ -988,12 +993,12 @@ Test::Result test_ecdsa_pubkey_export() { Test::Result result("PKCS11 export ECDSA public key"); - TestSession test_session(true); + const TestSession test_session(true); auto rng = Test::new_rng(__func__); // create public key from private key - ECDSA_PrivateKey priv_key(*rng, EC_Group::from_name("secp256r1")); + const ECDSA_PrivateKey priv_key(*rng, EC_Group::from_name("secp256r1")); const auto enc_point = encode_ec_point_in_octet_str(priv_key); @@ -1004,12 +1009,12 @@ props.set_private(false); // label - std::string label = "Botan test ecdsa pub key"; + const std::string label = "Botan test ecdsa pub key"; props.set_label(label); - PKCS11_ECDSA_PublicKey pk(test_session.session(), props); + const PKCS11_ECDSA_PublicKey pk(test_session.session(), props); - ECDSA_PublicKey exported = pk.export_key(); + const ECDSA_PublicKey exported = pk.export_key(); result.test_success("ECDSA public key export was successful"); pk.destroy(); @@ -1019,14 +1024,14 @@ Test::Result test_ecdsa_generate_private_key() { Test::Result result("PKCS11 generate ECDSA private key"); - TestSession test_session(true); + const TestSession test_session(true); EC_PrivateKeyGenerationProperties props; props.set_token(true); props.set_private(true); props.set_sign(true); - PKCS11_ECDSA_PrivateKey pk(test_session.session(), EC_Group::from_name("secp256r1").DER_encode(), props); + const PKCS11_ECDSA_PrivateKey pk(test_session.session(), EC_Group::from_name("secp256r1").DER_encode(), props); result.test_success("ECDSA private key generation was successful"); pk.destroy(); @@ -1058,14 +1063,14 @@ Test::Result test_ecdsa_generate_keypair() { Test::Result result("PKCS11 generate ECDSA key pair"); - TestSession test_session(true); + const TestSession test_session(true); std::vector curves; curves.push_back("secp256r1"); curves.push_back("brainpool512r1"); - for(auto& curve : curves) { - PKCS11_ECDSA_KeyPair keypair = generate_ecdsa_keypair(test_session, curve, EC_Group_Encoding::NamedCurve); + for(const auto& curve : curves) { + const PKCS11_ECDSA_KeyPair keypair = generate_ecdsa_keypair(test_session, curve, EC_Group_Encoding::NamedCurve); keypair.first.destroy(); keypair.second.destroy(); @@ -1077,39 +1082,39 @@ Test::Result test_ecdsa_sign_verify_core(EC_Group_Encoding enc, const std::string& test_name) { Test::Result result(test_name); - TestSession test_session(true); + const TestSession test_session(true); std::vector curves; curves.push_back("secp256r1"); curves.push_back("brainpool512r1"); - Slot& slot = test_session.slot(); + const Slot& slot = test_session.slot(); SlotInfo info = slot.get_slot_info(); - std::string manufacturer(reinterpret_cast(info.manufacturerID)); + const std::string manufacturer(reinterpret_cast(info.manufacturerID)); auto rng = Test::new_rng(__func__); - for(auto& curve : curves) { + for(const auto& curve : curves) { // generate key pair PKCS11_ECDSA_KeyPair keypair = generate_ecdsa_keypair(test_session, curve, enc); std::vector plaintext(20, 0x01); - 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 sign_and_verify = [&](const std::string& padding, const Botan::Signature_Format format, bool check_soft) { + Botan::PK_Signer signer(keypair.second, *rng, padding, 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); + Botan::PK_Verifier token_verifier(keypair.first, padding, format); + const bool ecdsa_ok = token_verifier.verify_message(plaintext, signature); - result.test_eq("ECDSA PKCS11 sign and verify: " + emsa, ecdsa_ok, true); + result.test_is_true("ECDSA PKCS11 sign and verify: " + padding, ecdsa_ok); // test against software implementation if available if(check_soft) { - Botan::PK_Verifier soft_verifier(keypair.first, emsa, format); - bool soft_ecdsa_ok = soft_verifier.verify_message(plaintext, signature); + Botan::PK_Verifier soft_verifier(keypair.first, padding, format); + const bool soft_ecdsa_ok = soft_verifier.verify_message(plaintext, signature); - result.test_eq("ECDSA PKCS11 verify (in software): " + emsa, soft_ecdsa_ok, true); + result.test_is_true("ECDSA PKCS11 verify (in software): " + padding, soft_ecdsa_ok); } }; @@ -1170,12 +1175,12 @@ Test::Result test_ecdh_privkey_import() { Test::Result result("PKCS11 import ECDH private key"); - TestSession test_session(true); + const TestSession test_session(true); auto rng = Test::new_rng(__func__); // create ecdh private key - ECDH_PrivateKey priv_key(*rng, EC_Group::from_name("secp256r1")); + const ECDH_PrivateKey priv_key(*rng, EC_Group::from_name("secp256r1")); // import to card EC_PrivateKeyImportProperties props(priv_key.DER_domain(), priv_key.private_value()); @@ -1184,10 +1189,10 @@ props.set_derive(true); // label - std::string label = "Botan test ecdh key"; + const std::string label = "Botan test ecdh key"; props.set_label(label); - PKCS11_ECDH_PrivateKey pk(test_session.session(), props); + const PKCS11_ECDH_PrivateKey pk(test_session.session(), props); result.test_success("ECDH private key import was successful"); pk.destroy(); @@ -1197,12 +1202,12 @@ Test::Result test_ecdh_privkey_export() { Test::Result result("PKCS11 export ECDH private key"); - TestSession test_session(true); + const TestSession test_session(true); auto rng = Test::new_rng(__func__); // create private key - ECDH_PrivateKey priv_key(*rng, EC_Group::from_name("secp256r1")); + const ECDH_PrivateKey priv_key(*rng, EC_Group::from_name("secp256r1")); // import to card EC_PrivateKeyImportProperties props(priv_key.DER_domain(), priv_key.private_value()); @@ -1212,12 +1217,12 @@ props.set_extractable(true); // label - std::string label = "Botan test ecdh key"; + const std::string label = "Botan test ecdh key"; props.set_label(label); - PKCS11_ECDH_PrivateKey pk(test_session.session(), props); + const PKCS11_ECDH_PrivateKey pk(test_session.session(), props); - ECDH_PrivateKey exported = pk.export_key(); + const ECDH_PrivateKey exported = pk.export_key(); result.test_success("ECDH private key export was successful"); pk.destroy(); @@ -1227,12 +1232,12 @@ Test::Result test_ecdh_pubkey_import() { Test::Result result("PKCS11 import ECDH public key"); - TestSession test_session(true); + const TestSession test_session(true); auto rng = Test::new_rng(__func__); // create ECDH private key - ECDH_PrivateKey priv_key(*rng, EC_Group::from_name("secp256r1")); + const ECDH_PrivateKey priv_key(*rng, EC_Group::from_name("secp256r1")); const auto enc_point = encode_ec_point_in_octet_str(priv_key); @@ -1243,10 +1248,10 @@ props.set_derive(true); // label - std::string label = "Botan test ECDH pub key"; + const std::string label = "Botan test ECDH pub key"; props.set_label(label); - PKCS11_ECDH_PublicKey pk(test_session.session(), props); + const PKCS11_ECDH_PublicKey pk(test_session.session(), props); result.test_success("ECDH public key import was successful"); pk.destroy(); @@ -1256,12 +1261,12 @@ Test::Result test_ecdh_pubkey_export() { Test::Result result("PKCS11 export ECDH public key"); - TestSession test_session(true); + const TestSession test_session(true); auto rng = Test::new_rng(__func__); // create public key from private key - ECDH_PrivateKey priv_key(*rng, EC_Group::from_name("secp256r1")); + const ECDH_PrivateKey priv_key(*rng, EC_Group::from_name("secp256r1")); const auto enc_point = encode_ec_point_in_octet_str(priv_key); @@ -1272,12 +1277,12 @@ props.set_private(false); // label - std::string label = "Botan test ECDH pub key"; + const std::string label = "Botan test ECDH pub key"; props.set_label(label); - PKCS11_ECDH_PublicKey pk(test_session.session(), props); + const PKCS11_ECDH_PublicKey pk(test_session.session(), props); - ECDH_PublicKey exported = pk.export_key(); + const ECDH_PublicKey exported = pk.export_key(); result.test_success("ECDH public key export was successful"); pk.destroy(); @@ -1287,14 +1292,14 @@ Test::Result test_ecdh_generate_private_key() { Test::Result result("PKCS11 generate ECDH private key"); - TestSession test_session(true); + const TestSession test_session(true); EC_PrivateKeyGenerationProperties props; props.set_token(true); props.set_private(true); props.set_derive(true); - PKCS11_ECDH_PrivateKey pk(test_session.session(), EC_Group::from_name("secp256r1").DER_encode(), props); + const PKCS11_ECDH_PrivateKey pk(test_session.session(), EC_Group::from_name("secp256r1").DER_encode(), props); result.test_success("ECDH private key generation was successful"); pk.destroy(); @@ -1324,9 +1329,9 @@ Test::Result test_ecdh_generate_keypair() { Test::Result result("PKCS11 generate ECDH key pair"); - TestSession test_session(true); + const TestSession test_session(true); - PKCS11_ECDH_KeyPair keypair = generate_ecdh_keypair(test_session, "Botan test ECDH key1"); + const PKCS11_ECDH_KeyPair keypair = generate_ecdh_keypair(test_session, "Botan test ECDH key1"); result.test_success("ECDH key pair generation was successful"); keypair.first.destroy(); @@ -1337,22 +1342,22 @@ Test::Result test_ecdh_derive() { Test::Result result("PKCS11 ECDH derive"); - TestSession test_session(true); + const TestSession test_session(true); - 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"); + const PKCS11_ECDH_KeyPair keypair = generate_ecdh_keypair(test_session, "Botan test ECDH key1"); + const 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, *rng, "Raw"); - Botan::PK_Key_Agreement kb(keypair2.second, *rng, "Raw"); + const Botan::PK_Key_Agreement ka(keypair.second, *rng, "Raw"); + const Botan::PK_Key_Agreement kb(keypair2.second, *rng, "Raw"); - Botan::SymmetricKey alice_key = ka.derive_key(32, keypair2.first.raw_public_key_bits()); - Botan::SymmetricKey bob_key = kb.derive_key(32, keypair.first.raw_public_key_bits()); + const Botan::SymmetricKey alice_key = ka.derive_key(32, keypair2.first.raw_public_key_bits()); + const Botan::SymmetricKey bob_key = kb.derive_key(32, keypair.first.raw_public_key_bits()); - bool eq = alice_key == bob_key; - result.test_eq("same secret key derived", eq, true); + const bool eq = alice_key == bob_key; + result.test_is_true("same secret key derived", eq); keypair.first.destroy(); keypair.second.destroy(); @@ -1386,32 +1391,31 @@ Test::Result test_rng_generate_random() { Test::Result result("PKCS11 RNG generate random"); - TestSession test_session(true); + const TestSession test_session(true); PKCS11_RNG p11_rng(test_session.session()); - result.confirm("RNG already seeded", p11_rng.is_seeded()); + result.test_is_true("RNG already seeded", p11_rng.is_seeded()); std::vector random(20); p11_rng.randomize(random.data(), random.size()); - result.test_ne("random data generated", random, std::vector(20)); + result.test_bin_ne("random data generated", random, std::vector(20)); return result; } Test::Result test_rng_add_entropy() { Test::Result result("PKCS11 RNG add entropy random"); - TestSession test_session(true); + const TestSession test_session(true); PKCS11_RNG p11_rng(test_session.session()); - result.confirm("RNG already seeded", p11_rng.is_seeded()); + result.test_is_true("RNG already seeded", p11_rng.is_seeded()); p11_rng.clear(); - result.confirm("RNG ignores call to clear", p11_rng.is_seeded()); + result.test_is_true("RNG ignores call to clear", p11_rng.is_seeded()); #if defined(BOTAN_HAS_ENTROPY_SOURCE) - result.test_eq("RNG ignores calls to reseed", - p11_rng.reseed(Botan::Entropy_Sources::global_sources(), 256, std::chrono::milliseconds(300)), - 0); + result.test_sz_eq( + "RNG ignores calls to reseed", p11_rng.reseed_from_sources(Botan::Entropy_Sources::global_sources(), 256), 0); #endif auto rng = Test::new_rng(__func__); @@ -1426,22 +1430,22 @@ Test::Result test_pkcs11_hmac_drbg() { Test::Result result("PKCS11 HMAC_DRBG using PKCS11_RNG"); - TestSession test_session(true); + const TestSession test_session(true); PKCS11_RNG p11_rng(test_session.session()); HMAC_DRBG drbg(MessageAuthenticationCode::create("HMAC(SHA-512)"), p11_rng); // result.test_success("HMAC_DRBG(HMAC(SHA512)) instantiated with PKCS11_RNG"); - result.test_eq("HMAC_DRBG is not seeded yet.", drbg.is_seeded(), false); - secure_vector rnd = drbg.random_vec(64); - result.test_eq("HMAC_DRBG is seeded now", drbg.is_seeded(), true); + result.test_is_false("HMAC_DRBG is not seeded yet.", drbg.is_seeded()); + const secure_vector rnd = drbg.random_vec(64); + result.test_is_true("HMAC_DRBG is seeded now", drbg.is_seeded()); std::string personalization_string = "Botan PKCS#11 Tests"; std::vector personalization_data(personalization_string.begin(), personalization_string.end()); drbg.add_entropy(personalization_data.data(), personalization_data.size()); auto rnd_vec = drbg.random_vec(256); - result.test_ne("HMAC_DRBG generated a random vector", rnd_vec, std::vector(256)); + result.test_bin_ne("HMAC_DRBG generated a random vector", rnd_vec, std::vector(256)); return result; } @@ -1550,19 +1554,19 @@ Test::Result result("PKCS11 X509 cert import"); #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) - TestSession test_session(true); + const TestSession test_session(true); - X509_Certificate root(Test::data_file("x509/nist/test01/end.crt")); + const X509_Certificate root(Test::data_file("x509/nist/test01/end.crt")); X509_CertificateProperties props(root); props.set_label("Botan PKCS#11 test certificate"); props.set_private(false); props.set_token(true); - PKCS11_X509_Certificate pkcs11_cert(test_session.session(), props); + const PKCS11_X509_Certificate pkcs11_cert(test_session.session(), props); result.test_success("X509 certificate imported"); - PKCS11_X509_Certificate pkcs11_cert2(test_session.session(), pkcs11_cert.handle()); - result.test_eq("X509 certificate by handle", pkcs11_cert == pkcs11_cert2, true); + const PKCS11_X509_Certificate pkcs11_cert2(test_session.session(), pkcs11_cert.handle()); + result.test_is_true("X509 certificate by handle", pkcs11_cert == pkcs11_cert2); pkcs11_cert.destroy(); #endif diff -Nru botan3-3.7.1+dfsg/src/tests/test_pkcs11_low_level.cpp botan3-3.12.0+dfsg/src/tests/test_pkcs11_low_level.cpp --- botan3-3.7.1+dfsg/src/tests/test_pkcs11_low_level.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_pkcs11_low_level.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,19 +6,18 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include "test_pkcs11.h" #include "tests.h" -#include -#include -#include -#include -#include -#include - #if defined(BOTAN_HAS_PKCS11) + #include "test_pkcs11.h" #include #include + #include + #include + #include + #include + #include + #include #endif namespace Botan_Tests { @@ -36,7 +35,6 @@ RAII_LowLevel() : m_module(Test::pkcs11_lib()), m_func_list(nullptr), - m_low_level(), m_session_handle(0), m_is_session_open(false), m_is_logged_in(false) { @@ -88,8 +86,8 @@ } SessionHandle open_rw_session_with_user_login() { - Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); - SessionHandle handle = open_session(session_flags); + const Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); + const SessionHandle handle = open_session(session_flags); login(UserType::User, PIN()); return handle; } @@ -136,7 +134,7 @@ private: Dynamically_Loaded_Library m_module; - FunctionListPtr m_func_list; + FunctionList* m_func_list; std::unique_ptr m_low_level; SessionHandle m_session_handle; bool m_is_session_open; @@ -156,13 +154,13 @@ const PKCS11_BoundTestFunction& revert_func, bool expect_failure, ReturnValue expected_return_value) { - std::string test_name = + const std::string test_name = revert_fn_name.empty() ? "PKCS 11 low level - " + name : "PKCS 11 low level - " + name + "/" + revert_fn_name; Test::Result result(test_name); // test throw variant if(expect_failure) { - result.test_throws(name + " fails as expected", [test_func]() { test_func(ThrowException); }); + result.test_throws(name + " fails as expected", [&test_func]() { test_func(ThrowException); }); } else { test_func(ThrowException); result.test_success(name + " did not throw and completed successfully"); @@ -175,28 +173,26 @@ // test bool return variant bool success = test_func(nullptr); - result.test_eq(name, success, !expect_failure); + result.test_bool_eq(name, success, !expect_failure); if(success && !revert_fn_name.empty()) { success = revert_func(nullptr); - result.test_eq(revert_fn_name, success, !expect_failure); + result.test_bool_eq(revert_fn_name, success, !expect_failure); } // test ReturnValue variant - ReturnValue rv; + ReturnValue rv = ReturnValue::OK; success = test_func(&rv); - result.test_eq(name, success, !expect_failure); + result.test_bool_eq(name, success, !expect_failure); if(!expect_failure) { - result.test_rc_ok(name, static_cast(rv)); + result.test_u32_eq(name, static_cast(rv), 0); } else { - result.test_rc_fail(name, - "return value should be: " + std::to_string(static_cast(expected_return_value)), - static_cast(rv)); + result.test_u32_eq(name, static_cast(rv), static_cast(expected_return_value)); } if(success && !revert_fn_name.empty()) { success = revert_func(&rv); - result.test_eq(revert_fn_name, success, !expect_failure); - result.test_rc_ok(revert_fn_name, static_cast(rv)); + result.test_bool_eq(revert_fn_name, success, !expect_failure); + result.test_u32_eq(revert_fn_name, static_cast(rv), 0); } return result; @@ -223,30 +219,32 @@ Test::Result test_low_level_ctor() { Test::Result result("PKCS 11 low level - LowLevel ctor"); - Dynamically_Loaded_Library pkcs11_module(Test::pkcs11_lib()); - FunctionListPtr func_list(nullptr); + const Dynamically_Loaded_Library pkcs11_module(Test::pkcs11_lib()); + FunctionList* func_list(nullptr); LowLevel::C_GetFunctionList(pkcs11_module, &func_list); - LowLevel p11_low_level(func_list); + const LowLevel p11_low_level(func_list); result.test_success("LowLevel ctor does complete for valid function list"); - result.test_throws("LowLevel ctor fails for invalid function list pointer", - []() { LowLevel p11_low_level2(nullptr); }); + result.test_throws("LowLevel ctor fails for invalid function list pointer", []() { LowLevel(nullptr); }); return result; } +// NOLINTBEGIN(*-avoid-bind) + Test::Result test_c_get_function_list() { Dynamically_Loaded_Library pkcs11_module(Test::pkcs11_lib()); - FunctionListPtr func_list = nullptr; + // NOLINTNEXTLINE(*-const-correctness) bug in clang-tidy + FunctionList* func_list = nullptr; return test_function( "C_GetFunctionList", std::bind(&LowLevel::C_GetFunctionList, std::ref(pkcs11_module), &func_list, std::placeholders::_1)); } Test::Result test_initialize_finalize() { - Dynamically_Loaded_Library pkcs11_module(Test::pkcs11_lib()); - FunctionListPtr func_list = nullptr; + const Dynamically_Loaded_Library pkcs11_module(Test::pkcs11_lib()); + FunctionList* func_list = nullptr; LowLevel::C_GetFunctionList(pkcs11_module, &func_list); LowLevel p11_low_level(func_list); @@ -254,24 +252,24 @@ // setting Flag::OsLockingOk should be the normal use case C_InitializeArgs init_args = {nullptr, nullptr, nullptr, nullptr, static_cast(Flag::OsLockingOk), nullptr}; - auto init_bind = std::bind(&LowLevel::C_Initialize, p11_low_level, &init_args, std::placeholders::_1); - auto finalize_bind = std::bind(&LowLevel::C_Finalize, p11_low_level, nullptr, std::placeholders::_1); + auto init_bind = std::bind(&LowLevel::C_Initialize, std::ref(p11_low_level), &init_args, std::placeholders::_1); + auto finalize_bind = std::bind(&LowLevel::C_Finalize, std::ref(p11_low_level), nullptr, std::placeholders::_1); return test_function("C_Initialize", init_bind, "C_Finalize", finalize_bind); } Test::Result test_c_get_info() { - RAII_LowLevel p11_low_level; + const RAII_LowLevel p11_low_level; Info info = {}; Test::Result result = - test_function("C_GetInfo", std::bind(&LowLevel::C_GetInfo, *p11_low_level.get(), &info, std::placeholders::_1)); - result.test_ne("C_GetInfo crypto major version", info.cryptokiVersion.major, 0); + test_function("C_GetInfo", std::bind(&LowLevel::C_GetInfo, p11_low_level.get(), &info, std::placeholders::_1)); + result.test_sz_ne("C_GetInfo crypto major version", info.cryptokiVersion.major, 0); return result; } Test::Result test_c_get_slot_list() { - RAII_LowLevel p11_low_level; + const RAII_LowLevel p11_low_level; std::vector slot_vec; @@ -279,94 +277,94 @@ auto slots_no_card = std::bind( static_cast&, ReturnValue*) const>(&LowLevel::C_GetSlotList), - *p11_low_level.get(), + p11_low_level.get(), false, // no card present std::ref(slot_vec), std::placeholders::_1); Test::Result result = test_function("C_GetSlotList", slots_no_card); - result.test_ne("C_GetSlotList number of slots without attached token > 0", slot_vec.size(), 0); + result.test_sz_ne("C_GetSlotList number of slots without attached token > 0", slot_vec.size(), 0); // assumes smartcard reader is attached with a card auto slots_with_card = std::bind( static_cast&, ReturnValue*) const>(&LowLevel::C_GetSlotList), - *p11_low_level.get(), + p11_low_level.get(), true, // card present std::ref(slot_vec), std::placeholders::_1); slot_vec.clear(); result.merge(test_function("C_GetSlotList", slots_with_card)); - result.test_ne("C_GetSlotList number of slots with attached token > 0", slot_vec.size(), 0); + result.test_sz_ne("C_GetSlotList number of slots with attached token > 0", slot_vec.size(), 0); return result; } Test::Result test_c_get_slot_info() { - RAII_LowLevel p11_low_level; + const RAII_LowLevel p11_low_level; std::vector slot_vec = p11_low_level.get_slots(false); SlotInfo slot_info = {}; Test::Result result = test_function( "C_GetSlotInfo", - std::bind(&LowLevel::C_GetSlotInfo, *p11_low_level.get(), slot_vec.at(0), &slot_info, std::placeholders::_1)); + std::bind(&LowLevel::C_GetSlotInfo, p11_low_level.get(), slot_vec.at(0), &slot_info, std::placeholders::_1)); - std::string slot_desc(reinterpret_cast(slot_info.slotDescription)); - result.test_ne("C_GetSlotInfo returns non empty description", slot_desc.size(), 0); + const std::string slot_desc(reinterpret_cast(slot_info.slotDescription)); + result.test_sz_ne("C_GetSlotInfo returns non empty description", slot_desc.size(), 0); return result; } Test::Result test_c_get_token_info() { - RAII_LowLevel p11_low_level; + const RAII_LowLevel p11_low_level; std::vector slot_vec = p11_low_level.get_slots(true); TokenInfo token_info = {}; Test::Result result = test_function( "C_GetTokenInfo", - std::bind(&LowLevel::C_GetTokenInfo, *p11_low_level.get(), slot_vec.at(0), &token_info, std::placeholders::_1)); + std::bind(&LowLevel::C_GetTokenInfo, p11_low_level.get(), slot_vec.at(0), &token_info, std::placeholders::_1)); - std::string serial(reinterpret_cast(token_info.serialNumber)); - result.test_ne("C_GetTokenInfo returns non empty serial number", serial.size(), 0); + const std::string serial(reinterpret_cast(token_info.serialNumber)); + result.test_sz_ne("C_GetTokenInfo returns non empty serial number", serial.size(), 0); return result; } Test::Result test_c_wait_for_slot_event() { - RAII_LowLevel p11_low_level; + const RAII_LowLevel p11_low_level; - Flags flags = PKCS11::flags(Flag::DontBlock); + const Flags flags = PKCS11::flags(Flag::DontBlock); SlotId slot_id = 0; return test_function( "C_WaitForSlotEvent", - std::bind(&LowLevel::C_WaitForSlotEvent, *p11_low_level.get(), flags, &slot_id, nullptr, std::placeholders::_1), + std::bind(&LowLevel::C_WaitForSlotEvent, p11_low_level.get(), flags, &slot_id, nullptr, std::placeholders::_1), true, ReturnValue::NoEvent); } Test::Result test_c_get_mechanism_list() { - RAII_LowLevel p11_low_level; + const RAII_LowLevel p11_low_level; std::vector slot_vec = p11_low_level.get_slots(true); std::vector mechanisms; auto binder = std::bind(static_cast&, ReturnValue*) const>( &LowLevel::C_GetMechanismList), - *p11_low_level.get(), + p11_low_level.get(), slot_vec.at(0), std::ref(mechanisms), std::placeholders::_1); Test::Result result = test_function("C_GetMechanismList", binder); - result.confirm("C_GetMechanismList returns non empty mechanisms list", !mechanisms.empty()); + result.test_is_true("C_GetMechanismList returns non empty mechanisms list", !mechanisms.empty()); return result; } Test::Result test_c_get_mechanism_info() { - RAII_LowLevel p11_low_level; + const RAII_LowLevel p11_low_level; std::vector slot_vec = p11_low_level.get_slots(true); std::vector mechanisms; @@ -375,7 +373,7 @@ MechanismInfo mechanism_info = {}; return test_function("C_GetMechanismInfo", std::bind(&LowLevel::C_GetMechanismInfo, - *p11_low_level.get(), + p11_low_level.get(), slot_vec.at(0), mechanisms.at(0), &mechanism_info, @@ -383,16 +381,16 @@ } Test::Result test_c_init_token() { - RAII_LowLevel p11_low_level; + const RAII_LowLevel p11_low_level; std::vector slot_vec = p11_low_level.get_slots(true); - const std::string label = "Botan PKCS#11 tests"; - std::string_view label_view(label); + const std::string token_label = "Botan PKCS#11 tests"; + std::string_view label_view(token_label); auto sec_vec_binder = std::bind( static_cast&, std::string_view, ReturnValue*) const>( &LowLevel::C_InitToken>), - *p11_low_level.get(), + p11_low_level.get(), slot_vec.at(0), SO_PIN(), std::ref(label_view), @@ -402,7 +400,7 @@ } Test::Result test_open_close_session() { - RAII_LowLevel p11_low_level; + const RAII_LowLevel p11_low_level; std::vector slot_vec = p11_low_level.get_slots(true); // public read only session @@ -410,7 +408,7 @@ SessionHandle session_handle = 0; auto open_session_ro = std::bind(&LowLevel::C_OpenSession, - *p11_low_level.get(), + p11_low_level.get(), slot_vec.at(0), ro_flags, nullptr, @@ -419,7 +417,7 @@ std::placeholders::_1); auto close_session = - std::bind(&LowLevel::C_CloseSession, *p11_low_level.get(), std::ref(session_handle), std::placeholders::_1); + std::bind(&LowLevel::C_CloseSession, p11_low_level.get(), std::ref(session_handle), std::placeholders::_1); Test::Result result = test_function("C_OpenSession", open_session_ro, "C_CloseSession", close_session); @@ -427,7 +425,7 @@ const Flags rw_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); auto open_session_rw = std::bind(&LowLevel::C_OpenSession, - *p11_low_level.get(), + p11_low_level.get(), slot_vec.at(0), rw_flags, nullptr, @@ -447,7 +445,8 @@ auto open_two_sessions = [&slot_vec, &p11_low_level]() -> void { // public read only session Flags flags = PKCS11::flags(Flag::SerialSession); - SessionHandle first_session_handle = 0, second_session_handle = 0; + SessionHandle first_session_handle = 0; + SessionHandle second_session_handle = 0; p11_low_level.get()->C_OpenSession(slot_vec.at(0), flags, nullptr, nullptr, &first_session_handle); @@ -467,15 +466,15 @@ open_two_sessions(); bool success = p11_low_level.get()->C_CloseAllSessions(slot_vec.at(0), nullptr); - result.test_eq("C_CloseAllSessions", success, true); + result.test_is_true("C_CloseAllSessions", success); // test ReturnValue variant open_two_sessions(); ReturnValue rv = ReturnValue::OK; success = p11_low_level.get()->C_CloseAllSessions(slot_vec.at(0), &rv); - result.test_eq("C_CloseAllSessions", success, true); - result.test_rc_ok("C_CloseAllSessions", static_cast(rv)); + result.test_is_true("C_CloseAllSessions", success); + result.test_u32_eq("C_CloseAllSessions", static_cast(rv), 0); return result; } @@ -485,20 +484,21 @@ std::vector slot_vec = p11_low_level.get_slots(true); // public read only session - Flags flags = PKCS11::flags(Flag::SerialSession); - SessionHandle session_handle = p11_low_level.open_session(flags); + const Flags flags = PKCS11::flags(Flag::SerialSession); + const SessionHandle session_handle = p11_low_level.open_session(flags); SessionInfo session_info = {}; Test::Result result = test_function( "C_GetSessionInfo", std::bind( - &LowLevel::C_GetSessionInfo, *p11_low_level.get(), session_handle, &session_info, std::placeholders::_1)); + &LowLevel::C_GetSessionInfo, p11_low_level.get(), session_handle, &session_info, std::placeholders::_1)); - result.confirm("C_GetSessionInfo returns same slot id as during call to C_OpenSession", - session_info.slotID == slot_vec.at(0)); - result.confirm("C_GetSessionInfo returns same flags as during call to C_OpenSession", session_info.flags == flags); - result.confirm("C_GetSessionInfo returns public read only session state", - session_info.state == static_cast(SessionState::RoPublicSession)); + result.test_is_true("C_GetSessionInfo returns same slot id as during call to C_OpenSession", + session_info.slotID == slot_vec.at(0)); + result.test_is_true("C_GetSessionInfo returns same flags as during call to C_OpenSession", + session_info.flags == flags); + result.test_is_true("C_GetSessionInfo returns public read only session state", + session_info.state == static_cast(SessionState::RoPublicSession)); return result; } @@ -506,13 +506,13 @@ Test::Result login_logout_helper(const RAII_LowLevel& p11_low_level, SessionHandle handle, UserType user_type, - const std::string& pin) { + std::string_view pin) { secure_vector pin_as_sec_vec(pin.begin(), pin.end()); auto login_secvec_binder = std::bind( static_cast&, ReturnValue*) const>( &LowLevel::C_Login>), - *p11_low_level.get(), + p11_low_level.get(), handle, user_type, std::ref(pin_as_sec_vec), @@ -520,7 +520,7 @@ auto logout_binder = std::bind(static_cast(&LowLevel::C_Logout), - *p11_low_level.get(), + p11_low_level.get(), handle, std::placeholders::_1); @@ -531,8 +531,8 @@ RAII_LowLevel p11_low_level; // can only login to R/W session - Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); - SessionHandle session_handle = p11_low_level.open_session(session_flags); + const Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); + const SessionHandle session_handle = p11_low_level.open_session(session_flags); return login_logout_helper(p11_low_level, session_handle, UserType::SO, PKCS11_SO_PIN); } @@ -559,15 +559,15 @@ RAII_LowLevel p11_low_level; // C_InitPIN can only be called in the "R/W SO Functions" state - Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); - SessionHandle session_handle = p11_low_level.open_session(session_flags); + const Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); + const SessionHandle session_handle = p11_low_level.open_session(session_flags); p11_low_level.login(UserType::SO, SO_PIN()); auto sec_vec_binder = std::bind(static_cast&, ReturnValue*) const>( &LowLevel::C_InitPIN>), - *p11_low_level.get(), + p11_low_level.get(), session_handle, PIN(), std::placeholders::_1); @@ -579,7 +579,7 @@ RAII_LowLevel p11_low_level; // C_SetPIN can only be called in the "R / W Public Session" state, "R / W SO Functions" state, or "R / W User Functions" state - Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); + const Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); SessionHandle session_handle = p11_low_level.open_session(session_flags); // now we are in "R / W Public Session" state: this will change the pin of the user @@ -591,7 +591,7 @@ static_cast&, const secure_vector&, ReturnValue*) const>( &LowLevel::C_SetPIN>), - *p11_low_level.get(), + p11_low_level.get(), session_handle, old_pin, new_pin, @@ -601,8 +601,8 @@ const std::string test_pin("654321"); const auto test_pin_secvec = secure_vector(test_pin.begin(), test_pin.end()); - PKCS11_BoundTestFunction set_pin_bind = get_pin_bind(PIN(), test_pin_secvec); - PKCS11_BoundTestFunction revert_pin_bind = get_pin_bind(test_pin_secvec, PIN()); + const PKCS11_BoundTestFunction set_pin_bind = get_pin_bind(PIN(), test_pin_secvec); + const PKCS11_BoundTestFunction revert_pin_bind = get_pin_bind(test_pin_secvec, PIN()); Test::Result result = test_function("C_SetPIN", set_pin_bind, "C_SetPIN", revert_pin_bind); @@ -614,11 +614,11 @@ // change so_pin in "R / W SO Functions" state const std::string test_so_pin = "87654321"; - secure_vector test_so_pin_secvec(test_so_pin.begin(), test_so_pin.end()); + const secure_vector test_so_pin_secvec(test_so_pin.begin(), test_so_pin.end()); p11_low_level.login(UserType::SO, SO_PIN()); - PKCS11_BoundTestFunction set_so_pin_bind = get_pin_bind(SO_PIN(), test_so_pin_secvec); - PKCS11_BoundTestFunction revert_so_pin_bind = get_pin_bind(test_so_pin_secvec, SO_PIN()); + const PKCS11_BoundTestFunction set_so_pin_bind = get_pin_bind(SO_PIN(), test_so_pin_secvec); + const PKCS11_BoundTestFunction revert_so_pin_bind = get_pin_bind(test_so_pin_secvec, SO_PIN()); result.merge(test_function("C_SetPIN", set_so_pin_bind, "C_SetPIN", revert_so_pin_bind)); @@ -627,8 +627,8 @@ // Simple data object const ObjectClass object_class = ObjectClass::Data; -const std::string label = "A data object"; -const std::string data = "Sample data"; +const std::string_view label = "A data object"; +const std::string_view data = "Sample data"; const Bbool btrue = True; const std::array data_template = { @@ -637,14 +637,14 @@ sizeof(object_class)}, {static_cast(AttributeType::Token), const_cast(&btrue), sizeof(btrue)}, {static_cast(AttributeType::Label), - const_cast(label.c_str()), + const_cast(label.data()), static_cast(label.size())}, {static_cast(AttributeType::Value), - const_cast(data.c_str()), + const_cast(data.data()), static_cast(data.size())}}}; ObjectHandle create_simple_data_object(const RAII_LowLevel& p11_low_level) { - ObjectHandle object_handle; + ObjectHandle object_handle = {}; auto dtemplate = data_template; p11_low_level.get()->C_CreateObject( @@ -654,14 +654,14 @@ Test::Result test_c_create_object_c_destroy_object() { RAII_LowLevel p11_low_level; - SessionHandle session_handle = p11_low_level.open_rw_session_with_user_login(); + const SessionHandle session_handle = p11_low_level.open_rw_session_with_user_login(); ObjectHandle object_handle(0); auto dtemplate = data_template; auto create_bind = std::bind(&LowLevel::C_CreateObject, - *p11_low_level.get(), + p11_low_level.get(), session_handle, dtemplate.data(), static_cast(dtemplate.size()), @@ -669,7 +669,7 @@ std::placeholders::_1); auto destroy_bind = std::bind( - &LowLevel::C_DestroyObject, *p11_low_level.get(), session_handle, std::ref(object_handle), std::placeholders::_1); + &LowLevel::C_DestroyObject, p11_low_level.get(), session_handle, std::ref(object_handle), std::placeholders::_1); return test_function("C_CreateObject", create_bind, "C_DestroyObject", destroy_bind); } @@ -677,23 +677,23 @@ Test::Result test_c_get_object_size() { RAII_LowLevel p11_low_level; - Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); - SessionHandle session_handle = p11_low_level.open_session(session_flags); + const Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); + const SessionHandle session_handle = p11_low_level.open_session(session_flags); p11_low_level.login(UserType::User, PIN()); - ObjectHandle object_handle = create_simple_data_object(p11_low_level); + const ObjectHandle object_handle = create_simple_data_object(p11_low_level); Ulong object_size = 0; auto bind = std::bind(&LowLevel::C_GetObjectSize, - *p11_low_level.get(), + p11_low_level.get(), session_handle, object_handle, &object_size, std::placeholders::_1); Test::Result result = test_function("C_GetObjectSize", bind); - result.test_ne("Object size", object_size, 0); + result.test_sz_ne("Object size", object_size, 0); // cleanup p11_low_level.get()->C_DestroyObject(session_handle, object_handle); @@ -703,9 +703,9 @@ Test::Result test_c_get_attribute_value() { RAII_LowLevel p11_low_level; - SessionHandle session_handle = p11_low_level.open_rw_session_with_user_login(); + const SessionHandle session_handle = p11_low_level.open_rw_session_with_user_login(); - ObjectHandle object_handle = create_simple_data_object(p11_low_level); + const ObjectHandle object_handle = create_simple_data_object(p11_low_level); std::map> getter = {{AttributeType::Label, secure_vector()}, {AttributeType::Value, secure_vector()}}; @@ -714,7 +714,7 @@ std::bind(static_cast>&, ReturnValue*) const>( &LowLevel::C_GetAttributeValue>), - *p11_low_level.get(), + p11_low_level.get(), session_handle, object_handle, std::ref(getter), @@ -722,10 +722,10 @@ Test::Result result = test_function("C_GetAttributeValue", bind); - std::string _label(getter[AttributeType::Label].begin(), getter[AttributeType::Label].end()); - std::string value(getter[AttributeType::Value].begin(), getter[AttributeType::Value].end()); - result.test_eq("label", _label, "A data object"); - result.test_eq("value", value, "Sample data"); + const std::string _label(getter[AttributeType::Label].begin(), getter[AttributeType::Label].end()); + const std::string value(getter[AttributeType::Value].begin(), getter[AttributeType::Value].end()); + result.test_str_eq("label", _label, "A data object"); + result.test_str_eq("value", value, "Sample data"); // cleanup p11_low_level.get()->C_DestroyObject(session_handle, object_handle); @@ -751,12 +751,12 @@ Test::Result test_c_set_attribute_value() { RAII_LowLevel p11_low_level; - Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); - SessionHandle session_handle = p11_low_level.open_session(session_flags); + const Flags session_flags = PKCS11::flags(Flag::SerialSession | Flag::RwSession); + const SessionHandle session_handle = p11_low_level.open_session(session_flags); p11_low_level.login(UserType::User, PIN()); - ObjectHandle object_handle = create_simple_data_object(p11_low_level); + const ObjectHandle object_handle = create_simple_data_object(p11_low_level); std::string new_label = "A modified data object"; @@ -767,7 +767,7 @@ std::bind(static_cast>&, ReturnValue*) const>( &LowLevel::C_SetAttributeValue>), - *p11_low_level.get(), + p11_low_level.get(), session_handle, object_handle, std::ref(new_attributes), @@ -776,13 +776,13 @@ Test::Result result = test_function("C_SetAttributeValue", bind); // get attributes and check if they are changed correctly - std::vector types = {AttributeType::Label, AttributeType::Value}; + const std::vector types = {AttributeType::Label, AttributeType::Value}; auto received_attributes = get_attribute_values(p11_low_level, session_handle, object_handle, types); - std::string retrieved_label(received_attributes[AttributeType::Label].begin(), - received_attributes[AttributeType::Label].end()); + const std::string retrieved_label(received_attributes[AttributeType::Label].begin(), + received_attributes[AttributeType::Label].end()); - result.test_eq("label", new_label, retrieved_label); + result.test_str_eq("label", new_label, retrieved_label); // cleanup p11_low_level.get()->C_DestroyObject(session_handle, object_handle); @@ -792,19 +792,19 @@ Test::Result test_c_copy_object() { RAII_LowLevel p11_low_level; - SessionHandle session_handle = p11_low_level.open_rw_session_with_user_login(); + const SessionHandle session_handle = p11_low_level.open_rw_session_with_user_login(); - ObjectHandle object_handle = create_simple_data_object(p11_low_level); + const ObjectHandle object_handle = create_simple_data_object(p11_low_level); ObjectHandle copied_object_handle = 0; - std::string copied_label = "A copied data object"; + const std::string copied_label = "A copied data object"; Attribute copy_attribute_values = {static_cast(AttributeType::Label), const_cast(copied_label.c_str()), static_cast(copied_label.size())}; auto binder = std::bind(&LowLevel::C_CopyObject, - *p11_low_level.get(), + p11_low_level.get(), session_handle, object_handle, ©_attribute_values, @@ -815,13 +815,13 @@ Test::Result result = test_function("C_CopyObject", binder); // get attributes and check if its copied correctly - std::vector types = {AttributeType::Label, AttributeType::Value}; + const std::vector types = {AttributeType::Label, AttributeType::Value}; auto received_attributes = get_attribute_values(p11_low_level, session_handle, copied_object_handle, types); - std::string retrieved_label(received_attributes[AttributeType::Label].begin(), - received_attributes[AttributeType::Label].end()); + const std::string retrieved_label(received_attributes[AttributeType::Label].begin(), + received_attributes[AttributeType::Label].end()); - result.test_eq("label", copied_label, retrieved_label); + result.test_str_eq("label", copied_label, retrieved_label); // cleanup p11_low_level.get()->C_DestroyObject(session_handle, object_handle); @@ -830,6 +830,25 @@ return result; } +// NOLINTEND(*-avoid-bind) + +Test::Result test_load_latest_interface() { + Test::Result res("Load latest PKCS #11 interface"); + Botan::Dynamically_Loaded_Library pkcs11_module(Test::pkcs11_lib()); + res.test_no_throw("Get function lists of latest interface", [&] { + auto latest_interface = InterfaceWrapper::latest_p11_interface(pkcs11_module); + latest_interface.func_2_40(); + if(latest_interface.version().major >= 3) { + latest_interface.func_3_0(); + + if(latest_interface.version().major > 3 || latest_interface.version().minor >= 2) { + latest_interface.func_3_2(); + } + } + }); + return res; +} + class LowLevelTests final : public Test { public: std::vector run() override { @@ -857,7 +876,9 @@ {STRING_AND_FUNCTION(test_c_get_object_size)}, {STRING_AND_FUNCTION(test_c_get_attribute_value)}, {STRING_AND_FUNCTION(test_c_set_attribute_value)}, - {STRING_AND_FUNCTION(test_c_copy_object)}}; + {STRING_AND_FUNCTION(test_c_copy_object)}, + {STRING_AND_FUNCTION(test_load_latest_interface)}, + }; return run_pkcs11_tests("PKCS11 low level", fns); } diff -Nru botan3-3.7.1+dfsg/src/tests/test_psk_db.cpp botan3-3.12.0+dfsg/src/tests/test_psk_db.cpp --- botan3-3.7.1+dfsg/src/tests/test_psk_db.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_psk_db.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,8 @@ #if defined(BOTAN_HAS_PSK_DB) #include + #include + #include #if defined(BOTAN_HAS_SQLITE3) #include @@ -29,7 +31,7 @@ if(i == m_vals.end()) { result.test_failure("Expected to find encrypted name " + index); } else { - result.test_eq("Encrypted value", i->second, value); + result.test_str_eq("Encrypted value", i->second, value); } } @@ -66,8 +68,6 @@ std::map> m_vals; }; -} // namespace - class PSK_DB_Tests final : public Test { public: std::vector run() override { @@ -91,43 +91,43 @@ db.set_str("name", "value"); db.test_entry(result, "CUCJjJgWSa079ubutJQwlw==", "clYJSAf9CThuL96CP+rAfA=="); - result.test_eq("DB read", db.get_str("name"), "value"); + result.test_str_eq("DB read", db.get_str("name"), "value"); db.set_str("name", "value1"); db.test_entry(result, "CUCJjJgWSa079ubutJQwlw==", "7R8am3x/gLawOzMp5WwIJg=="); - result.test_eq("DB read", db.get_str("name"), "value1"); + result.test_str_eq("DB read", db.get_str("name"), "value1"); db.set_str("name", "value"); db.test_entry(result, "CUCJjJgWSa079ubutJQwlw==", "clYJSAf9CThuL96CP+rAfA=="); - result.test_eq("DB read", db.get_str("name"), "value"); + result.test_str_eq("DB read", db.get_str("name"), "value"); db.set_str("name2", "value"); db.test_entry(result, "7CvsM7HDCZsV6VsFwWylNg==", "BqVQo4rdwOmf+ItCzEmjAg=="); - result.test_eq("DB read", db.get_str("name2"), "value"); + result.test_str_eq("DB read", db.get_str("name2"), "value"); db.set_vec("name2", zeros); db.test_entry(result, "7CvsM7HDCZsV6VsFwWylNg==", "x+I1bUF/fJYPOTvKwOihEPWGR1XGzVuyRdsw4n5gpBRzNR7LjH7vjw=="); - result.test_eq("DB read", db.get("name2"), zeros); + result.test_bin_eq("DB read", db.get("name2"), zeros); // Test longer names db.set_str("leroy jeeeeeeeenkins", "chicken"); db.test_entry(result, "KyYo272vlSjClM2F0OZBMlRYjr33ZXv2jN1oY8OfCEs=", "tCl1qShSTsXi9tA5Kpo9vg=="); - result.test_eq("DB read", db.get_str("leroy jeeeeeeeenkins"), "chicken"); + result.test_str_eq("DB read", db.get_str("leroy jeeeeeeeenkins"), "chicken"); std::set all_names = db.list_names(); - result.test_eq("Expected number of names", all_names.size(), 3); - result.test_eq("Have expected name", all_names.count("name"), 1); - result.test_eq("Have expected name", all_names.count("name2"), 1); - result.test_eq("Have expected name", all_names.count("leroy jeeeeeeeenkins"), 1); + result.test_sz_eq("Expected number of names", all_names.size(), 3); + result.test_sz_eq("Have expected name", all_names.count("name"), 1); + result.test_sz_eq("Have expected name", all_names.count("name2"), 1); + result.test_sz_eq("Have expected name", all_names.count("leroy jeeeeeeeenkins"), 1); db.remove("name2"); all_names = db.list_names(); - result.test_eq("Expected number of names", all_names.size(), 2); - result.test_eq("Have expected name", all_names.count("name"), 1); - result.test_eq("Have expected name", all_names.count("leroy jeeeeeeeenkins"), 1); + result.test_sz_eq("Expected number of names", all_names.size(), 2); + result.test_sz_eq("Have expected name", all_names.count("name"), 1); + result.test_sz_eq("Have expected name", all_names.count("leroy jeeeeeeeenkins"), 1); result.test_throws( "exception if get called on non-existent PSK", "Named PSK not located", [&]() { db.get("name2"); }); @@ -147,11 +147,11 @@ const std::string& expected_value) { auto stmt = db.new_statement("select psk_value from " + table + " where psk_name='" + expected_name + "'"); - bool got_it = stmt->step(); - result.confirm("Had expected name", got_it); + const bool got_it = stmt->step(); + result.test_is_true("Had expected name", got_it); if(got_it) { - result.test_eq("Had expected value", stmt->get_str(0), expected_value); + result.test_str_eq("Had expected value", stmt->get_str(0), expected_value); } } @@ -162,25 +162,25 @@ 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:"); + const std::shared_ptr sqldb = std::make_shared(":memory:"); Botan::Encrypted_PSK_Database_SQL db(zeros, sqldb, table_name); db.set_str("name", "value"); test_entry(result, *sqldb, table_name, "CUCJjJgWSa079ubutJQwlw==", "clYJSAf9CThuL96CP+rAfA=="); - result.test_eq("DB read", db.get_str("name"), "value"); + result.test_str_eq("DB read", db.get_str("name"), "value"); db.set_str("name", "value1"); test_entry(result, *sqldb, table_name, "CUCJjJgWSa079ubutJQwlw==", "7R8am3x/gLawOzMp5WwIJg=="); - result.test_eq("DB read", db.get_str("name"), "value1"); + result.test_str_eq("DB read", db.get_str("name"), "value1"); db.set_str("name", "value"); test_entry(result, *sqldb, table_name, "CUCJjJgWSa079ubutJQwlw==", "clYJSAf9CThuL96CP+rAfA=="); - result.test_eq("DB read", db.get_str("name"), "value"); + result.test_str_eq("DB read", db.get_str("name"), "value"); db.set_str("name2", "value"); test_entry(result, *sqldb, table_name, "7CvsM7HDCZsV6VsFwWylNg==", "BqVQo4rdwOmf+ItCzEmjAg=="); - result.test_eq("DB read", db.get_str("name2"), "value"); + result.test_str_eq("DB read", db.get_str("name2"), "value"); db.set_vec("name2", zeros); test_entry(result, @@ -188,13 +188,13 @@ table_name, "7CvsM7HDCZsV6VsFwWylNg==", "x+I1bUF/fJYPOTvKwOihEPWGR1XGzVuyRdsw4n5gpBRzNR7LjH7vjw=="); - result.test_eq("DB read", db.get("name2"), zeros); + result.test_bin_eq("DB read", db.get("name2"), zeros); // Test longer names db.set_str("leroy jeeeeeeeenkins", "chicken"); test_entry( result, *sqldb, table_name, "KyYo272vlSjClM2F0OZBMlRYjr33ZXv2jN1oY8OfCEs=", "tCl1qShSTsXi9tA5Kpo9vg=="); - result.test_eq("DB read", db.get_str("leroy jeeeeeeeenkins"), "chicken"); + result.test_str_eq("DB read", db.get_str("leroy jeeeeeeeenkins"), "chicken"); /* * Test that we can have another database in the same table with distinct key @@ -202,23 +202,23 @@ */ Botan::Encrypted_PSK_Database_SQL db2(not_zeros, sqldb, table_name); db2.set_str("name", "price&value"); - result.test_eq("DB read", db2.get_str("name"), "price&value"); - result.test_eq("DB2 size", db2.list_names().size(), 1); + result.test_str_eq("DB read", db2.get_str("name"), "price&value"); + result.test_sz_eq("DB2 size", db2.list_names().size(), 1); std::set all_names = db.list_names(); - result.test_eq("Expected number of names", all_names.size(), 3); - result.test_eq("Have expected name", all_names.count("name"), 1); - result.test_eq("Have expected name", all_names.count("name2"), 1); - result.test_eq("Have expected name", all_names.count("leroy jeeeeeeeenkins"), 1); + result.test_sz_eq("Expected number of names", all_names.size(), 3); + result.test_sz_eq("Have expected name", all_names.count("name"), 1); + result.test_sz_eq("Have expected name", all_names.count("name2"), 1); + result.test_sz_eq("Have expected name", all_names.count("leroy jeeeeeeeenkins"), 1); db.remove("name2"); all_names = db.list_names(); - result.test_eq("Expected number of names", all_names.size(), 2); - result.test_eq("Have expected name", all_names.count("name"), 1); - result.test_eq("Have expected name", all_names.count("leroy jeeeeeeeenkins"), 1); + result.test_sz_eq("Expected number of names", all_names.size(), 2); + result.test_sz_eq("Have expected name", all_names.count("name"), 1); + result.test_sz_eq("Have expected name", all_names.count("leroy jeeeeeeeenkins"), 1); result.test_throws( "exception if get called on non-existent PSK", "Named PSK not located", [&]() { db.get("name2"); }); @@ -233,6 +233,8 @@ BOTAN_REGISTER_TEST("misc", "psk_db", PSK_DB_Tests); +} // namespace + } // namespace Botan_Tests #endif diff -Nru botan3-3.7.1+dfsg/src/tests/test_pubkey.cpp botan3-3.12.0+dfsg/src/tests/test_pubkey.cpp --- botan3-3.7.1+dfsg/src/tests/test_pubkey.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_pubkey.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -5,13 +5,15 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include "test_pubkey.h" +#include "tests.h" #if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) + #include "test_pubkey.h" #include "test_rng.h" #include + #include #include #include #include @@ -30,27 +32,53 @@ namespace { +std::vector> generate_specific_false_signatures(const std::span correct_signature) { + std::vector> result; + result.push_back(std::vector()); + result.push_back(std::vector(1)); + result.push_back(std::vector(2)); + if(correct_signature.size() > 1) { + result.push_back(std::vector(correct_signature.size() - 1)); + std::vector flip_start(correct_signature.begin(), correct_signature.end()); + flip_start[0] ^= 1; + result.push_back(flip_start); + } + + return result; +} + void check_invalid_signatures(Test::Result& result, Botan::PK_Verifier& verifier, const std::vector& message, const std::vector& signature, Botan::RandomNumberGenerator& rng) { - const size_t tests_to_run = (Test::run_long_tests() ? 20 : 5); + const size_t tests_to_run = (Test::run_long_tests() ? 24 : 9); const std::vector zero_sig(signature.size()); - result.test_eq("all zero signature invalid", verifier.verify_message(message, zero_sig), false); + result.test_is_false("all zero signature invalid", verifier.verify_message(message, zero_sig)); + + auto specific_false_sigs = generate_specific_false_signatures(signature); for(size_t i = 0; i < tests_to_run; ++i) { - const std::vector bad_sig = Test::mutate_vec(signature, rng); + std::vector bad_sig; + if(i < specific_false_sigs.size()) { + bad_sig = specific_false_sigs[i]; + } else { + bad_sig = Test::mutate_vec(signature, rng); + } try { - if(!result.test_eq("incorrect signature invalid", verifier.verify_message(message, bad_sig), false)) { - result.test_note("Accepted invalid signature " + Botan::hex_encode(bad_sig)); + if(!result.test_is_false("incorrect signature invalid", verifier.verify_message(message, bad_sig))) { + result.test_note("Accepted invalid signature", bad_sig); } } catch(std::exception& e) { - result.test_note("Accepted invalid signature " + Botan::hex_encode(bad_sig)); + result.test_note("Modified signature", bad_sig); result.test_failure("Modified signature rejected with exception", e.what()); } + if(!result.test_is_true("correct signature valid after failed verification", + verifier.verify_message(message, signature))) { + result.test_note("rejected valid signature after this invalid signature", bad_sig); + } } } @@ -64,7 +92,8 @@ Botan::RandomNumberGenerator& rng) { const size_t tests_to_run = (Test::run_long_tests() ? 20 : 5); - size_t ciphertext_accepted = 0, ciphertext_rejected = 0; + size_t ciphertext_accepted = 0; + size_t ciphertext_rejected = 0; for(size_t i = 0; i < tests_to_run; ++i) { const std::vector bad_ctext = Test::mutate_vec(ciphertext, rng); @@ -73,16 +102,20 @@ const Botan::secure_vector decrypted = decryptor.decrypt(bad_ctext); ++ciphertext_accepted; - if(!result.test_ne("incorrect ciphertext different", decrypted, plaintext)) { - result.test_eq("used corrupted ciphertext", bad_ctext, ciphertext); + if(!result.test_bin_ne("incorrect ciphertext different", decrypted, plaintext)) { + result.test_bin_eq("used corrupted ciphertext", bad_ctext, ciphertext); } } catch(std::exception&) { ++ciphertext_rejected; } } - result.test_note("Accepted " + std::to_string(ciphertext_accepted) + " invalid ciphertexts, rejected " + - std::to_string(ciphertext_rejected)); + result.test_note( + Botan::fmt("Accepted {} invalid ciphertexts, rejected {}", ciphertext_accepted, ciphertext_rejected)); + + result.test_bin_eq("After decrypting corrupted messages, PK_Decryptor can decrypt original message", + decryptor.decrypt(ciphertext), + plaintext); } std::string PK_Test::choose_padding(const VarMap& vars, const std::string& pad_hdr) { @@ -96,6 +129,11 @@ return Test::provider_filter({"base", "commoncrypto", "openssl", "tpm"}); } +std::unique_ptr PK_Signature_Generation_Test::test_rng( + const std::vector& nonce) const { + return std::make_unique(nonce); +} + Test::Result PK_Signature_Generation_Test::run_one_test(const std::string& pad_hdr, const VarMap& vars) { const std::vector message = vars.get_req_bin("Msg"); const std::vector signature = vars.get_req_bin("Signature"); @@ -118,13 +156,13 @@ return result; } - result.confirm("private key claims to support signatures", - privkey->supports_operation(Botan::PublicKeyOperation::Signature)); + result.test_is_true("private key claims to support signatures", + privkey->supports_operation(Botan::PublicKeyOperation::Signature)); auto pubkey = Botan::X509::load_key(Botan::X509::BER_encode(*privkey->public_key())); - result.confirm("public key claims to support signatures", - pubkey->supports_operation(Botan::PublicKeyOperation::Signature)); + result.test_is_true("public key claims to support signatures", + pubkey->supports_operation(Botan::PublicKeyOperation::Signature)); std::vector> verifiers; @@ -135,15 +173,15 @@ verifier = std::make_unique(*pubkey, padding, Botan::Signature_Format::Standard, verify_provider); } catch(Botan::Lookup_Error&) { - //result.test_note("Skipping verifying with " + verify_provider); + //result.test_note("Skipping verifying", verify_provider); continue; } - result.test_eq("KAT signature valid", verifier->verify_message(message, signature), true); + result.test_is_true("KAT signature valid", verifier->verify_message(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); + result.test_is_true("KAT signature valid (try 2)", verifier->verify_message(message, signature)); verifiers.push_back(std::move(verifier)); } @@ -164,19 +202,19 @@ generated_signature = signer->sign_message(message, this->rng()); } - result.test_lte( + result.test_sz_lte( "Generated signature within announced bound", generated_signature.size(), signer->signature_length()); } catch(Botan::Lookup_Error&) { - //result.test_note("Skipping signing with " + sign_provider); + //result.test_note("Skipping signing", sign_provider); continue; } if(sign_provider == "base") { - result.test_eq("generated signature matches KAT", generated_signature, signature); + result.test_bin_eq("generated signature matches KAT", generated_signature, signature); } else if(generated_signature != signature) { for(std::unique_ptr& verifier : verifiers) { - if(!result.test_eq( - "generated signature valid", verifier->verify_message(message, generated_signature), true)) { + if(!result.test_is_true("generated signature valid", + verifier->verify_message(message, generated_signature))) { result.test_failure("generated signature", generated_signature); } } @@ -210,8 +248,8 @@ result_name << " signature verification"; Test::Result result(result_name.str()); - result.confirm("public key claims to support signatures", - pubkey->supports_operation(Botan::PublicKeyOperation::Signature)); + result.test_is_true("public key claims to support signatures", + pubkey->supports_operation(Botan::PublicKeyOperation::Signature)); for(const auto& verify_provider : possible_providers(algo_name())) { std::unique_ptr verifier; @@ -219,7 +257,7 @@ try { verifier = std::make_unique(*pubkey, padding, sig_format(), verify_provider); } catch(Botan::Lookup_Error&) { - //result.test_note("Skipping verifying with " + verify_provider); + //result.test_note("Skipping verifying", verify_provider); } if(verifier) { @@ -227,13 +265,13 @@ const bool verified = verifier->verify_message(message, signature); if(expected_valid) { - result.test_eq("correct signature valid with " + verify_provider, verified, true); + result.test_is_true("correct signature valid with " + verify_provider, verified); if(test_random_invalid_sigs()) { check_invalid_signatures(result, *verifier, message, signature, this->rng()); } } else { - result.confirm("incorrect signature is rejected", verified == false); + result.test_is_true("incorrect signature is rejected", verified == false); } } catch(std::exception& e) { result.test_failure("verification threw exception", e.what()); @@ -259,9 +297,9 @@ try { verifier = std::make_unique(*pubkey, padding, Botan::Signature_Format::Standard, verify_provider); - result.test_eq("incorrect signature rejected", verifier->verify_message(message, invalid_signature), false); + result.test_is_false("incorrect signature rejected", verifier->verify_message(message, invalid_signature)); } catch(Botan::Lookup_Error&) { - result.test_note("Skipping verifying with " + verify_provider); + result.test_note("Skipping verifying", verify_provider); } } @@ -298,7 +336,7 @@ 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); + result.test_is_true("correct signature valid with " + provider, verified); if(test_random_invalid_sigs()) { check_invalid_signatures(result, *verifier, message, generated_signature, this->rng()); @@ -309,15 +347,45 @@ } } + // Below follows a regression test for a bug introduced in #4592 that caused + // an assertion in PK_Signer when setting the output format explicitly using + // signer.set_output_format(Signature_Format::DerSequence) + try { + auto signer = Botan::PK_Signer(*privkey, this->rng(), padding /*, not setting DerSequence here */); + auto verifier = Botan::PK_Verifier(*pubkey, padding /*, not setting DerSequence here */); + + // Setting the in/out formats explicitly, to ensure that PK_Signer/Verifier + // handle their internal state properly and not run into an assertion. + signer.set_output_format(Botan::Signature_Format::DerSequence); + verifier.set_input_format(Botan::Signature_Format::DerSequence); + + const auto sig = signer.sign_message(message, this->rng()); + const auto verified = verifier.verify_message(message, sig); + + result.test_is_true("signature checks out", verified); + if(test_random_invalid_sigs()) { + check_invalid_signatures(result, verifier, message, sig, this->rng()); + } + } catch(const Botan::Lookup_Error&) { + result.test_note("Skipping sign/verify regression test"); + } catch(const std::exception& e) { + result.test_failure("regression test verification failed", e.what()); + } + return {result}; } -std::vector PK_Sign_Verify_DER_Test::possible_providers(const std::string& algo) { - std::vector pk_provider = - Botan::probe_provider_private_key(algo, {"base", "commoncrypto", "openssl", "tpm"}); +std::vector PK_Sign_Verify_DER_Test::possible_providers(const std::string& algo_name) { + const std::vector pk_provider = + Botan::probe_provider_private_key(algo_name, {"base", "commoncrypto", "openssl", "tpm"}); return Test::provider_filter(pk_provider); } +std::unique_ptr PK_Encryption_Decryption_Test::test_rng( + const std::vector& nonce) const { + return std::make_unique(nonce); +} + Test::Result PK_Encryption_Decryption_Test::run_one_test(const std::string& pad_hdr, const VarMap& vars) { const std::vector plaintext = vars.get_req_bin("Msg"); const std::vector ciphertext = vars.get_req_bin("Ciphertext"); @@ -327,12 +395,10 @@ auto privkey = load_private_key(vars); - result.confirm("private key claims to support encryption", - privkey->supports_operation(Botan::PublicKeyOperation::Encryption)); + result.test_is_true("private key claims to support encryption", + privkey->supports_operation(Botan::PublicKeyOperation::Encryption)); - // instead slice the private key to work around elgamal test inputs - //auto pubkey = Botan::X509::load_key(Botan::X509::BER_encode(*privkey)); - Botan::Public_Key* pubkey = privkey.get(); + auto pubkey = privkey->public_key(); std::vector> decryptors; @@ -349,13 +415,15 @@ try { decrypted = decryptor->decrypt(ciphertext); - result.test_lte("Plaintext within length", decrypted.size(), decryptor->plaintext_length(ciphertext.size())); + result.test_sz_lte( + "Plaintext within length", decrypted.size(), decryptor->plaintext_length(ciphertext.size())); } catch(Botan::Exception& e) { result.test_failure("Failed to decrypt KAT ciphertext", e.what()); } - result.test_eq(dec_provider, "decryption of KAT", decrypted, plaintext); + result.test_bin_eq(dec_provider + " decryption of KAT", decrypted, plaintext); check_invalid_ciphertexts(result, *decryptor, plaintext, ciphertext, this->rng()); + decryptors.push_back(std::move(decryptor)); } for(const auto& enc_provider : possible_providers(algo_name())) { @@ -377,21 +445,21 @@ Hack for RSA with no padding since sometimes one more bit will fit in but maximum_input_size rounds down to nearest byte */ - result.test_lte("Input within accepted bounds", plaintext.size(), encryptor->maximum_input_size() + 1); + result.test_sz_lte("Input within accepted bounds", plaintext.size(), encryptor->maximum_input_size() + 1); } else { - result.test_lte("Input within accepted bounds", plaintext.size(), encryptor->maximum_input_size()); + result.test_sz_lte("Input within accepted bounds", plaintext.size(), encryptor->maximum_input_size()); } const std::vector generated_ciphertext = encryptor->encrypt(plaintext, kat_rng ? *kat_rng : this->rng()); - result.test_lte( + result.test_sz_lte( "Ciphertext within length", generated_ciphertext.size(), encryptor->ciphertext_length(plaintext.size())); if(enc_provider == "base") { - result.test_eq(enc_provider, "generated ciphertext matches KAT", generated_ciphertext, ciphertext); + result.test_bin_eq(enc_provider + " generated ciphertext matches KAT", generated_ciphertext, ciphertext); } else if(generated_ciphertext != ciphertext) { for(std::unique_ptr& dec : decryptors) { - result.test_eq("decryption of generated ciphertext", dec->decrypt(generated_ciphertext), plaintext); + result.test_bin_eq("decryption of generated ciphertext", dec->decrypt(generated_ciphertext), plaintext); } } } @@ -424,7 +492,7 @@ result.test_failure("Failed to decrypt KAT ciphertext", e.what()); } - result.test_eq(dec_provider, "decryption of KAT", decrypted, plaintext); + result.test_bin_eq(dec_provider + " decryption of KAT", decrypted, plaintext); check_invalid_ciphertexts(result, *decryptor, plaintext, ciphertext, this->rng()); } @@ -441,18 +509,18 @@ auto privkey = load_private_key(vars); - result.confirm("private key claims to support KEM", - privkey->supports_operation(Botan::PublicKeyOperation::KeyEncapsulation)); + result.test_is_true("private key claims to support KEM", + privkey->supports_operation(Botan::PublicKeyOperation::KeyEncapsulation)); - const Botan::Public_Key& pubkey = *privkey; + auto pubkey = privkey->public_key(); const size_t desired_key_len = K.size(); std::unique_ptr enc; try { - enc = std::make_unique(pubkey, kdf); + enc = std::make_unique(*pubkey, kdf); } catch(Botan::Lookup_Error&) { - result.test_note("Skipping due to missing KDF: " + kdf); + result.test_note("Skipping due to missing KDF", kdf); return result; } @@ -460,15 +528,15 @@ const auto kem_result = enc->encrypt(fixed_output_rng, desired_key_len, salt); - result.test_eq("encapsulated key length matches expected", - kem_result.encapsulated_shared_key().size(), - enc->encapsulated_key_length()); + result.test_sz_eq("encapsulated key length matches expected", + kem_result.encapsulated_shared_key().size(), + enc->encapsulated_key_length()); - result.test_eq( + result.test_sz_eq( "shared key length matches expected", kem_result.shared_key().size(), enc->shared_key_length(desired_key_len)); - result.test_eq("C0 matches", kem_result.encapsulated_shared_key(), C0); - result.test_eq("K matches", kem_result.shared_key(), K); + result.test_bin_eq("C0 matches", kem_result.encapsulated_shared_key(), C0); + result.test_bin_eq("K matches", kem_result.shared_key(), K); std::unique_ptr dec; try { @@ -478,17 +546,17 @@ return result; } - result.test_eq("encapsulated key length matches expected", - kem_result.encapsulated_shared_key().size(), - dec->encapsulated_key_length()); + result.test_sz_eq("encapsulated key length matches expected", + kem_result.encapsulated_shared_key().size(), + dec->encapsulated_key_length()); const Botan::secure_vector decr_shared_key = dec->decrypt(C0.data(), C0.size(), desired_key_len, salt.data(), salt.size()); - result.test_eq( + result.test_sz_eq( "shared key length matches expected", decr_shared_key.size(), dec->shared_key_length(desired_key_len)); - result.test_eq("decrypted K matches", decr_shared_key, K); + result.test_bin_eq("decrypted K matches", decr_shared_key, K); return result; } @@ -501,8 +569,8 @@ auto privkey = load_our_key(header, vars); - result.confirm("private key claims to support key agreement", - privkey->supports_operation(Botan::PublicKeyOperation::KeyAgreement)); + result.test_is_true("private key claims to support key agreement", + privkey->supports_operation(Botan::PublicKeyOperation::KeyAgreement)); const std::vector pubkey = load_their_key(header, vars); @@ -518,23 +586,23 @@ result.test_throws("key agreement fails", [&] { kas->derive_key(key_len, pubkey); }); } else { auto derived_key = kas->derive_key(key_len, pubkey).bits_of(); - result.test_eq(provider, "agreement", derived_key, shared); + result.test_bin_eq(provider + " agreement", derived_key, shared); if(key_len == 0 && kdf == "Raw") { - result.test_eq("Expected size", derived_key.size(), kas->agreed_value_size()); + result.test_sz_eq("Expected size", derived_key.size(), kas->agreed_value_size()); } } } catch(Botan::Lookup_Error&) { - //result.test_note("Skipping key agreement with with " + provider); + //result.test_note("Skipping key agreement", provider); } } return result; } -std::vector PK_Key_Generation_Test::possible_providers(const std::string& algo) { - std::vector pk_provider = - Botan::probe_provider_private_key(algo, {"base", "commoncrypto", "openssl", "tpm"}); +std::vector PK_Key_Generation_Test::possible_providers(const std::string& algo_name) { + const std::vector pk_provider = + Botan::probe_provider_private_key(algo_name, {"base", "commoncrypto", "openssl", "tpm"}); return Test::provider_filter(pk_provider); } @@ -556,9 +624,9 @@ auto loaded = Botan::PKCS8::load_key(data_src, passphrase); - result.confirm("recovered private key from encrypted blob", loaded != nullptr); - result.test_eq("reloaded key has same type", loaded->algo_name(), key.algo_name()); - result.test_eq("reloaded key has same encoding", loaded->private_key_info(), pkcs8); + result.test_is_true("recovered private key from encrypted blob", loaded != nullptr); + result.test_str_eq("reloaded key has same type", loaded->algo_name(), key.algo_name()); + result.test_bin_eq("reloaded key has same encoding", loaded->private_key_info(), pkcs8); } catch(std::exception& e) { result.test_failure("roundtrip encrypted PEM private key", e.what()); } @@ -569,9 +637,9 @@ auto loaded = Botan::PKCS8::load_key(data_src, passphrase); - result.confirm("recovered private key from BER blob", loaded != nullptr); - result.test_eq("reloaded key has same type", loaded->algo_name(), key.algo_name()); - result.test_eq("reloaded key has same encoding", loaded->private_key_info(), pkcs8); + result.test_is_true("recovered private key from BER blob", loaded != nullptr); + result.test_str_eq("reloaded key has same type", loaded->algo_name(), key.algo_name()); + result.test_bin_eq("reloaded key has same encoding", loaded->private_key_info(), pkcs8); } catch(std::exception& e) { result.test_failure("roundtrip encrypted BER private key", e.what()); } @@ -584,20 +652,20 @@ std::vector results; for(const auto& param : keygen_params()) { - const auto algo = algo_name(param); - const std::string report_name = Botan::fmt("{}{}", algo, (param.empty() ? param : " " + param)); + const auto algorithm_name = algo_name(param); + const std::string report_name = Botan::fmt("{}{}", algorithm_name, (param.empty() ? param : " " + param)); Test::Result result(report_name + " keygen"); - const std::vector providers = possible_providers(algo); + const std::vector providers = possible_providers(algorithm_name); if(providers.empty()) { - result.note_missing("provider key generation " + algo); + result.note_missing("provider key generation " + algorithm_name); } result.start_timer(); for(auto&& prov : providers) { - auto key_p = Botan::create_private_key(algo, this->rng(), param, prov); + auto key_p = Botan::create_private_key(algorithm_name, this->rng(), param, prov); if(key_p == nullptr) { continue; @@ -606,18 +674,18 @@ const Botan::Private_Key& key = *key_p; try { - result.confirm("Key passes self tests", key.check_key(this->rng(), true)); + result.test_is_true("Key passes self tests", key.check_key(this->rng(), true)); } catch(Botan::Lookup_Error&) {} const std::string name = key.algo_name(); - result.confirm("Key has a non-empty name", !name.empty()); + result.test_is_true("Key has a non-empty name", !name.empty()); if(auto oid = Botan::OID::from_name(name)) { result.test_success("Keys name maps to an OID"); - result.test_eq("Keys name OID is the same as the object oid", - oid.value().to_string(), - key.object_identifier().to_string()); + result.test_str_eq("Keys name OID is the same as the object oid", + oid.value().to_string(), + key.object_identifier().to_string()); } else { const bool exception = name == "Kyber" || name == "ML-KEM" || name == "ML-DSA" || name == "SLH-DSA" || name == "FrodoKEM" || name == "SPHINCS+" || name == "ClassicMcEliece"; @@ -626,14 +694,14 @@ } } - result.test_gte("Key has reasonable estimated strength (lower)", key.estimated_strength(), 64); - result.test_lt("Key has reasonable estimated strength (upper)", key.estimated_strength(), 512); + result.test_sz_gte("Key has reasonable estimated strength (lower)", key.estimated_strength(), 64); + result.test_sz_lt("Key has reasonable estimated strength (upper)", key.estimated_strength(), 512); auto public_key = key.public_key(); - result.test_eq("public_key has same name", public_key->algo_name(), key.algo_name()); + result.test_str_eq("public_key has same name", public_key->algo_name(), key.algo_name()); - result.test_eq( + result.test_str_eq( "public_key has same encoding", Botan::X509::PEM_encode(key), Botan::X509::PEM_encode(*public_key)); // Test generation of another key pair from a given (abstract) asymmetric key @@ -643,17 +711,17 @@ 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()); - result.test_eq("new public key has the same name", pk2->algo_name(), public_key->algo_name()); - result.test_eq( + result.test_str_eq("new private key has the same name", sk2->algo_name(), key.algo_name()); + result.test_str_eq("new public key has the same name", pk2->algo_name(), public_key->algo_name()); + result.test_sz_eq( "new private key has the same est. strength", sk2->estimated_strength(), key.estimated_strength()); - result.test_eq("new public key has the same est. strength", - pk2->estimated_strength(), - public_key->estimated_strength()); - result.test_ne("new private keys are different keys", sk2->private_key_bits(), key.private_key_bits()); + result.test_sz_eq("new public key has the same est. strength", + pk2->estimated_strength(), + public_key->estimated_strength()); + result.test_bin_ne("new private keys are different keys", sk2->private_key_bits(), key.private_key_bits()); } catch(const Botan::Not_Implemented&) { - result.confirm("KEX algorithms are required to implement 'generate_another'", - !public_key->supports_operation(Botan::PublicKeyOperation::KeyAgreement)); + result.test_is_true("KEX algorithms are required to implement 'generate_another'", + !public_key->supports_operation(Botan::PublicKeyOperation::KeyAgreement)); } // Test that the raw public key can be encoded. This is not supported @@ -661,38 +729,39 @@ const std::vector algos_that_dont_have_a_raw_encoding = {"RSA"}; try { auto raw = public_key->raw_public_key_bits(); - result.test_ne("raw_public_key_bits is not empty", raw.size(), 0); + result.test_sz_ne("raw_public_key_bits is not empty", raw.size(), 0); if(public_key->supports_operation(Botan::PublicKeyOperation::KeyAgreement)) { // For KEX algorithms, raw_public_key_bits must be equal to the canonical // public value obtained by PK_Key_Agreement_Key::public_value(). const auto* ka_key = dynamic_cast(&key); result.require("is a key agreement private key", ka_key != nullptr); - result.test_eq("public_key_bits has same encoding", raw, ka_key->public_value()); + result.test_bin_eq("public_key_bits has same encoding", raw, ka_key->public_value()); } if(auto raw_pk = public_key_from_raw(param, prov, raw)) { - result.test_eq("public_key has same type", raw_pk->algo_name(), public_key->algo_name()); - result.test_eq("public_key has same encoding", raw_pk->public_key_bits(), public_key->public_key_bits()); + result.test_str_eq("public_key has same type", raw_pk->algo_name(), public_key->algo_name()); + result.test_bin_eq( + "public_key has same encoding", raw_pk->public_key_bits(), public_key->public_key_bits()); } } catch(const Botan::Not_Implemented&) { if(!Botan::value_exists(algos_that_dont_have_a_raw_encoding, public_key->algo_name())) { result.test_failure("raw_public_key_bits not implemented for " + public_key->algo_name()); } else { - result.test_note("raw_public_key_bits threw Not_Implemented as expected for " + public_key->algo_name()); + result.test_note("raw_public_key_bits threw Not_Implemented as expected", public_key->algo_name()); } } // Test PEM public key round trips OK try { - Botan::DataSource_Memory data_src(Botan::X509::PEM_encode(key)); + Botan::DataSource_Memory data_src(Botan::X509::PEM_encode(*public_key)); auto loaded = Botan::X509::load_key(data_src); - result.confirm("recovered public key from private", loaded != nullptr); - result.test_eq("public key has same type", loaded->algo_name(), key.algo_name()); + result.test_is_true("recovered public key from private", loaded != nullptr); + result.test_str_eq("public key has same type", loaded->algo_name(), key.algo_name()); try { - result.test_eq("public key passes checks", loaded->check_key(this->rng(), false), true); + result.test_is_true("public key passes checks", loaded->check_key(this->rng(), false)); } catch(Botan::Lookup_Error&) {} } catch(std::exception& e) { result.test_failure("roundtrip PEM public key", e.what()); @@ -700,13 +769,13 @@ // Test DER public key round trips OK try { - const auto ber = key.subject_public_key(); + const auto ber = public_key->subject_public_key(); Botan::DataSource_Memory data_src(ber); auto loaded = Botan::X509::load_key(data_src); - result.confirm("recovered public key from private", loaded != nullptr); - result.test_eq("public key has same type", loaded->algo_name(), key.algo_name()); - result.test_eq("public key has same encoding", loaded->subject_public_key(), ber); + result.test_is_true("recovered public key from private", loaded != nullptr); + result.test_str_eq("public key has same type", loaded->algo_name(), key.algo_name()); + result.test_bin_eq("public key has same encoding", loaded->subject_public_key(), ber); } catch(std::exception& e) { result.test_failure("roundtrip BER public key", e.what()); } @@ -717,9 +786,9 @@ Botan::DataSource_Memory data_src(ber); auto loaded = Botan::PKCS8::load_key(data_src); - result.confirm("recovered private key from PEM blob", loaded != nullptr); - result.test_eq("reloaded key has same type", loaded->algo_name(), key.algo_name()); - result.test_eq("reloaded key has same encoding", loaded->private_key_info(), ber); + result.test_is_true("recovered private key from PEM blob", loaded != nullptr); + result.test_str_eq("reloaded key has same type", loaded->algo_name(), key.algo_name()); + result.test_bin_eq("reloaded key has same encoding", loaded->private_key_info(), ber); } catch(std::exception& e) { result.test_failure("roundtrip PEM private key", e.what()); } @@ -728,8 +797,8 @@ Botan::DataSource_Memory data_src(Botan::PKCS8::BER_encode(key)); auto loaded = Botan::PKCS8::load_key(data_src); - result.confirm("recovered public key from private", loaded != nullptr); - result.test_eq("public key has same type", loaded->algo_name(), key.algo_name()); + result.test_is_true("recovered public key from private", loaded != nullptr); + result.test_str_eq("public key has same type", loaded->algo_name(), key.algo_name()); } catch(std::exception& e) { result.test_failure("roundtrip BER private key", e.what()); } @@ -765,7 +834,7 @@ const bool tested_valid = pubkey->check_key(this->rng(), true); - result.test_eq("Expected validation result", expected_valid, tested_valid); + result.test_bool_eq("Expected validation result", tested_valid, expected_valid); return result; } @@ -774,7 +843,7 @@ const std::string& test_src) : PK_Test(algo, test_src, "Rng,RngSeed,Key", "KeyParams,RngParams") {} -Test::Result PK_Key_Generation_Stability_Test::run_one_test(const std::string&, const VarMap& vars) { +Test::Result PK_Key_Generation_Stability_Test::run_one_test(const std::string& /*header*/, const VarMap& vars) { const std::string key_param = vars.get_opt_str("KeyParams", ""); const std::string rng_algo = vars.get_req_str("Rng"); const std::string rng_params = vars.get_opt_str("RngParams", ""); @@ -815,7 +884,7 @@ auto key = Botan::create_private_key(algo_name(), *rng, key_param); if(key) { const auto key_bits = key->private_key_info(); - result.test_eq("Generated key matched expected value", key_bits, expected_key); + result.test_bin_eq("Generated key matched expected value", key_bits, expected_key); } } catch(Botan::Exception& e) { result.test_note("failed to create key", e.what()); @@ -829,6 +898,8 @@ return result; } +namespace { + /** * @brief Some general tests for minimal API sanity for signing/verification. */ @@ -867,48 +938,60 @@ return result; } - auto pubkey = Botan::X509::load_key(Botan::X509::BER_encode(*privkey)); - result.confirm("Storing and loading public key works", pubkey != nullptr); + auto pubkey = Botan::X509::load_key(Botan::X509::BER_encode(*privkey->public_key())); + result.test_is_true("Storing and loading public key works", pubkey != nullptr); - result.confirm("private key claims to support signatures", - privkey->supports_operation(Botan::PublicKeyOperation::Signature)); - result.confirm("public key claims to support signatures", - pubkey->supports_operation(Botan::PublicKeyOperation::Signature)); - result.test_gt("Public key length must be greater than 0", privkey->key_length(), 0); + result.test_is_true("private key claims to support signatures", + privkey->supports_operation(Botan::PublicKeyOperation::Signature)); + result.test_is_true("public key claims to support signatures", + pubkey->supports_operation(Botan::PublicKeyOperation::Signature)); + result.test_sz_gt("Public key length must be greater than 0", pubkey->key_length(), 0); if(privkey->stateful_operation()) { - result.confirm("A stateful key reports the number of remaining operations", - privkey->remaining_operations().has_value()); + result.test_is_true("A stateful key reports the number of remaining operations", + privkey->remaining_operations().has_value()); } else { - result.confirm("A stateless key has an unlimited number of remaining operations", - !privkey->remaining_operations().has_value()); + result.test_is_true("A stateless key has an unlimited number of remaining operations", + !privkey->remaining_operations().has_value()); } - auto signer = std::make_unique( - *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); - result.confirm("Creating PK_Signer works", verifier != nullptr); + auto [signer, verifier] = [&] { + try { + return std::make_pair(std::make_unique( + *privkey, this->rng(), sig_params, Botan::Signature_Format::Standard, provider), + std::make_unique( + *pubkey, verify_params, Botan::Signature_Format::Standard, provider)); + } catch(Botan::Algorithm_Not_Found&) {} - result.test_is_nonempty("PK_Signer should report some hash", signer->hash_function()); - result.test_is_nonempty("PK_Verifier should report some hash", verifier->hash_function()); + return std::pair, std::unique_ptr>{}; + }(); - result.test_eq( + if(!signer || !verifier) { + result.test_note(Botan::fmt( + "Skipping Sign/verify API tests for {}({}) with provider {}", algorithm, algo_params, provider)); + return result; + } + + result.test_is_true("Creating PK_Signer works", signer != nullptr); + result.test_is_true("Creating PK_Signer works", verifier != nullptr); + + result.test_str_not_empty("PK_Signer should report some hash", signer->hash_function()); + result.test_str_not_empty("PK_Verifier should report some hash", verifier->hash_function()); + + result.test_str_eq( "PK_Signer and PK_Verifier report the same hash", signer->hash_function(), verifier->hash_function()); pubkey.reset(); privkey.reset(); const std::array msg{0xde, 0xad, 0xbe, 0xef}; 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), - true); + result.test_sz_gt("Signer should still work if no one else hold a reference to the key", sig.size(), 0); + result.test_is_true("Verifier should still work if no one else hold a reference to the key", + verifier->verify_message(msg, sig)); return result; } - bool skip_this_test([[maybe_unused]] const std::string& header, const VarMap&) override { + bool skip_this_test([[maybe_unused]] const std::string& header, const VarMap& /*vars*/) override { #if !defined(BOTAN_HAS_SLH_DSA_WITH_SHA2) if(header == "SLH-DSA") { return true; @@ -928,7 +1011,7 @@ PK_Key_Decoding_Test() : Text_Based_Test("pubkey/key_encoding.vec", "Key") {} protected: - Test::Result run_one_test(const std::string&, const VarMap& vars) final { + Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) final { const auto key = vars.get_req_bin("Key"); Test::Result result("PK Key Decoding"); @@ -952,6 +1035,8 @@ BOTAN_REGISTER_TEST("pubkey", "pk_key_decoding", PK_Key_Decoding_Test); +} // namespace + } // namespace Botan_Tests #endif diff -Nru botan3-3.7.1+dfsg/src/tests/test_pubkey.h botan3-3.12.0+dfsg/src/tests/test_pubkey.h --- botan3-3.7.1+dfsg/src/tests/test_pubkey.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_pubkey.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,11 +9,19 @@ #include "tests.h" -#include "test_rng.h" - #if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) - #include + #include + #include + +namespace Botan { + +enum class Signature_Format : uint8_t; +class Public_Key; +class Private_Key; +class PK_Decryptor; + +} // namespace Botan namespace Botan_Tests { @@ -28,9 +36,9 @@ std::string algo_name() const { return m_algo; } protected: - std::vector possible_providers(const std::string& params) override; + std::vector possible_providers(const std::string& algo_name) override; - virtual std::string default_padding(const VarMap&) const { + virtual std::string default_padding(const VarMap& /*vars*/) const { throw Test_Error("No default padding scheme set for " + algo_name()); } @@ -50,12 +58,11 @@ virtual std::unique_ptr load_private_key(const VarMap& vars) = 0; - virtual std::unique_ptr test_rng(const std::vector& nonce) const { - return std::make_unique(nonce); - } + // Default is a Fixed_Output_RNG returning the nonce + virtual std::unique_ptr test_rng(const std::vector& nonce) const; private: - Test::Result run_one_test(const std::string&, const VarMap& vars) final; + Test::Result run_one_test(const std::string& pad_hdr, const VarMap& vars) final; }; class PK_Signature_Verification_Test : public PK_Test { @@ -73,7 +80,7 @@ virtual std::unique_ptr load_public_key(const VarMap& vars) = 0; private: - Test::Result run_one_test(const std::string& header, const VarMap& vars) final; + Test::Result run_one_test(const std::string& pad_hdr, const VarMap& vars) final; }; class PK_Signature_NonVerification_Test : public PK_Test { @@ -89,7 +96,7 @@ virtual std::unique_ptr load_public_key(const VarMap& vars) = 0; private: - Test::Result run_one_test(const std::string& header, const VarMap& vars) final; + Test::Result run_one_test(const std::string& pad_hdr, const VarMap& vars) final; }; class PK_Sign_Verify_DER_Test : public Test { @@ -105,7 +112,7 @@ virtual bool test_random_invalid_sigs() const { return true; } - std::vector possible_providers(const std::string& params) override; + std::vector possible_providers(const std::string& algo_name) override; private: std::string m_algo; @@ -122,14 +129,13 @@ virtual std::unique_ptr load_private_key(const VarMap& vars) = 0; - std::string default_padding(const VarMap&) const override { return "Raw"; } + std::string default_padding(const VarMap& /*vars*/) const override { return "Raw"; } - virtual std::unique_ptr test_rng(const std::vector& nonce) const { - return std::make_unique(nonce); - } + // Default is Fixed_Output_RNG returning the nonce + virtual std::unique_ptr test_rng(const std::vector& nonce) const; private: - Test::Result run_one_test(const std::string& header, const VarMap& vars) final; + Test::Result run_one_test(const std::string& pad_hdr, const VarMap& vars) final; }; class PK_Decryption_Test : public PK_Test { @@ -142,10 +148,10 @@ virtual std::unique_ptr load_private_key(const VarMap& vars) = 0; - std::string default_padding(const VarMap&) const override { return "Raw"; } + std::string default_padding(const VarMap& /*vars*/) const override { return "Raw"; } private: - Test::Result run_one_test(const std::string& header, const VarMap& vars) final; + Test::Result run_one_test(const std::string& pad_hdr, const VarMap& vars) final; }; class PK_Key_Agreement_Test : public PK_Test { @@ -156,16 +162,13 @@ const std::string& optional_keys = "") : PK_Test(algo, test_src, required_keys, optional_keys) {} - virtual bool agreement_should_fail(const std::string& header, const VarMap& vars) const { - BOTAN_UNUSED(header, vars); - return false; - } + virtual bool agreement_should_fail(const std::string& /*header*/, const VarMap& /*vars*/) const { return false; } virtual std::unique_ptr load_our_key(const std::string& header, const VarMap& vars) = 0; virtual std::vector load_their_key(const std::string& header, const VarMap& vars) = 0; - virtual std::string default_kdf(const VarMap&) const { return "Raw"; } + virtual std::string default_kdf(const VarMap& /*vars*/) const { return "Raw"; } private: Test::Result run_one_test(const std::string& header, const VarMap& vars) final; @@ -191,10 +194,7 @@ virtual std::vector keygen_params() const = 0; - virtual std::string algo_name(std::string_view param) const { - BOTAN_UNUSED(param); - return algo_name(); - } + virtual std::string algo_name(std::string_view /*param*/) const { return algo_name(); } virtual std::string algo_name() const = 0; @@ -207,7 +207,7 @@ std::string_view provider, std::span raw_key_bits) const = 0; - std::vector possible_providers(const std::string& params) override; + std::vector possible_providers(const std::string& algo_name) override; }; class PK_Key_Generation_Stability_Test : public PK_Test { diff -Nru botan3-3.7.1+dfsg/src/tests/test_pubkey_pqc.h botan3-3.12.0+dfsg/src/tests/test_pubkey_pqc.h --- botan3-3.7.1+dfsg/src/tests/test_pubkey_pqc.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_pubkey_pqc.h 2026-05-07 01:38:28.000000000 +0000 @@ -16,6 +16,7 @@ #include #include + #include #include namespace Botan_Tests { @@ -33,7 +34,7 @@ class PK_PQC_KEM_KAT_Test : public PK_Test { protected: /// Type of a KAT vector entry that can be recomputed using the seed - enum class VarType { SharedSecret, PublicKey, PrivateKey, Ciphertext }; + enum class VarType : uint8_t { SharedSecret, PublicKey, PrivateKey, Ciphertext }; PK_PQC_KEM_KAT_Test(const std::string& algo_name, const std::string& input_file, @@ -60,36 +61,32 @@ virtual bool is_available(const std::string& params) const = 0; /// Callback to test the RNG's state after key generation. If not overridden checks that the RNG is empty. - virtual void inspect_rng_after_keygen(const std::string& params, + virtual void inspect_rng_after_keygen(const std::string& /*params*/, const Fixed_Output_RNG& rng_keygen, Test::Result& result) const { - BOTAN_UNUSED(params); - result.confirm("All prepared random bits used for key generation", rng_keygen.empty()); + result.test_is_true("All prepared random bits used for key generation", rng_keygen.empty()); } /// Callback to test the RNG's state after encapsulation. If not overridden checks that the RNG is empty. - virtual void inspect_rng_after_encaps(const std::string& params, + virtual void inspect_rng_after_encaps(const std::string& /*params*/, const Fixed_Output_RNG& rng_encaps, Test::Result& result) const { - BOTAN_UNUSED(params); - result.confirm("All prepared random bits used for encapsulation", rng_encaps.empty()); + result.test_is_true("All prepared random bits used for encapsulation", rng_encaps.empty()); } private: - bool skip_this_test(const std::string& params, const VarMap&) final { + bool skip_this_test([[maybe_unused]] const std::string& params, const VarMap& /*vars*/) final { #if !defined(BOTAN_HAS_AES) - BOTAN_UNUSED(params); return true; #else return !is_available(params); #endif } - std::unique_ptr create_drbg(std::span seed) { + std::unique_ptr create_drbg([[maybe_unused]] std::span seed) { #if defined(BOTAN_HAS_AES) return std::make_unique(seed); #else - BOTAN_UNUSED(seed); throw Botan_Tests::Test_Error("PQC KAT tests require a build with AES"); #endif } @@ -110,23 +107,23 @@ if(!result.test_not_null("Successfully generated private key", sk)) { return result; } - result.test_is_eq("Generated private key", - map_value(params, sk->raw_private_key_bits(), VarType::PrivateKey), - vars.get_req_bin("SK")); + result.test_bin_eq("Generated private key", + map_value(params, sk->raw_private_key_bits(), VarType::PrivateKey), + vars.get_req_bin("SK")); inspect_rng_after_keygen(params, rng_keygen, result); // Algorithm properties - result.test_eq("Algorithm name", sk->algo_name(), algo_name()); - result.confirm("Supported operation KeyEncapsulation", - sk->supports_operation(Botan::PublicKeyOperation::KeyEncapsulation)); - result.test_gte("Key has reasonable estimated strength (lower)", sk->estimated_strength(), 64); - result.test_lt("Key has reasonable estimated strength (upper)", sk->estimated_strength(), 512); + result.test_str_eq("Algorithm name", sk->algo_name(), algo_name()); + result.test_is_true("Supported operation KeyEncapsulation", + sk->supports_operation(Botan::PublicKeyOperation::KeyEncapsulation)); + result.test_sz_gte("Key has reasonable estimated strength (lower)", sk->estimated_strength(), 64); + result.test_sz_lt("Key has reasonable estimated strength (upper)", sk->estimated_strength(), 512); // Extract Public Key auto pk = sk->public_key(); - result.test_is_eq("Generated public key", - map_value(params, pk->public_key_bits(), VarType::PublicKey), - vars.get_req_bin("PK")); + result.test_bin_eq("Generated public key", + map_value(params, pk->public_key_bits(), VarType::PublicKey), + vars.get_req_bin("PK")); // Serialize/Deserialize the Public Key auto pk2 = Botan::load_public_key(pk->algorithm_identifier(), pk->public_key_bits()); @@ -137,11 +134,11 @@ // Encapsulation auto enc = Botan::PK_KEM_Encryptor(*pk2, "Raw"); const auto encaped = enc.encrypt(rng_encaps, 0 /* no KDF */); - result.test_is_eq( + result.test_bin_eq( "Shared Secret", map_value(params, encaped.shared_key(), VarType::SharedSecret), vars.get_req_bin("SS")); - result.test_is_eq("Ciphertext", - map_value(params, encaped.encapsulated_shared_key(), VarType::Ciphertext), - vars.get_req_bin("CT")); + result.test_bin_eq("Ciphertext", + map_value(params, encaped.encapsulated_shared_key(), VarType::Ciphertext), + vars.get_req_bin("CT")); inspect_rng_after_encaps(params, rng_keygen, result); // Decapsulation @@ -153,13 +150,12 @@ Botan::Null_RNG null_rng; auto dec = Botan::PK_KEM_Decryptor(*sk2, null_rng, "Raw"); const auto shared_key = dec.decrypt(encaped.encapsulated_shared_key(), 0 /* no KDF */); - result.test_is_eq("Decaps. Shared Secret", shared_key, Botan::lock(vars.get_req_bin("SS"))); + result.test_bin_eq("Decaps. Shared Secret", shared_key, vars.get_req_bin("SS")); if(vars.has_key("CT_N")) { // Shared secret from invalid KEM ciphertext const auto shared_key_invalid = dec.decrypt(vars.get_req_bin("CT_N"), 0 /* no KDF */); - result.test_is_eq( - "Decaps. Shared Secret Invalid", shared_key_invalid, Botan::lock(vars.get_req_bin("SS_N"))); + result.test_bin_eq("Decaps. Shared Secret Invalid", shared_key_invalid, vars.get_req_bin("SS_N")); } return result; @@ -192,7 +188,7 @@ } private: - bool skip_this_test(const std::string& params, const VarMap&) final { return !is_available(params); } + bool skip_this_test(const std::string& params, const VarMap& /*vars*/) final { return !is_available(params); } Test::Result run_one_test(const std::string& params, const VarMap& vars) final { Test::Result result(Botan::fmt("PQC ACVP KAT for {} KeyGen with parameters {}", algo_name(), params)); @@ -204,20 +200,21 @@ if(!result.test_not_null("Successfully generated private key", sk)) { return result; } - result.test_is_eq("Generated private key", compress_value(sk->raw_private_key_bits()), vars.get_req_bin("DK")); + result.test_bin_eq( + "Generated private key", compress_value(sk->raw_private_key_bits()), vars.get_req_bin("DK")); // Algorithm properties - result.test_eq("Algorithm name", sk->algo_name(), algo_name()); - result.confirm("Supported operation KeyEncapsulation", - sk->supports_operation(Botan::PublicKeyOperation::KeyEncapsulation)); - result.test_gte("Key has reasonable estimated strength (lower)", sk->estimated_strength(), 64); - result.test_lt("Key has reasonable estimated strength (upper)", sk->estimated_strength(), 512); + result.test_str_eq("Algorithm name", sk->algo_name(), algo_name()); + result.test_is_true("Supported operation KeyEncapsulation", + sk->supports_operation(Botan::PublicKeyOperation::KeyEncapsulation)); + result.test_sz_gte("Key has reasonable estimated strength (lower)", sk->estimated_strength(), 64); + result.test_sz_lt("Key has reasonable estimated strength (upper)", sk->estimated_strength(), 512); // Extract Public Key auto pk = sk->public_key(); - result.test_is_eq("Generated public key", compress_value(pk->public_key_bits()), vars.get_req_bin("EK")); + result.test_bin_eq("Generated public key", compress_value(pk->public_key_bits()), vars.get_req_bin("EK")); - result.confirm("All prepared random bits used for key generation", rng_keygen.empty()); + result.test_is_true("All prepared random bits used for key generation", rng_keygen.empty()); return result; } @@ -240,7 +237,7 @@ virtual bool is_available(const std::string& params) const = 0; private: - bool skip_this_test(const std::string& params, const VarMap&) final { return !is_available(params); } + bool skip_this_test(const std::string& params, const VarMap& /*vars*/) final { return !is_available(params); } std::vector compress_value(std::span value) { // We always use SHAKE-256(128) for ML-KEM @@ -257,10 +254,10 @@ auto enc = Botan::PK_KEM_Encryptor(*pk, "Raw"); const auto encaped = enc.encrypt(rng_encap, 0 /* no KDF */); - result.test_is_eq("Shared Secret", encaped.shared_key(), Botan::lock(vars.get_req_bin("K"))); - result.test_is_eq("Ciphertext", compress_value(encaped.encapsulated_shared_key()), vars.get_req_bin("C")); + result.test_bin_eq("Shared Secret", encaped.shared_key(), vars.get_req_bin("K")); + result.test_bin_eq("Ciphertext", compress_value(encaped.encapsulated_shared_key()), vars.get_req_bin("C")); - result.confirm("All prepared random bits used for key generation", rng_encap.empty()); + result.test_is_true("All prepared random bits used for key generation", rng_encap.empty()); return result; } @@ -278,7 +275,7 @@ virtual bool is_available(const std::string& params) const = 0; private: - bool skip_this_test(const std::string& params, const VarMap&) final { return !is_available(params); } + bool skip_this_test(const std::string& params, const VarMap& /*vars*/) final { return !is_available(params); } Test::Result run_one_test(const std::string& params, const VarMap& vars) final { Test::Result result(Botan::fmt("PQC ACVP KAT for {} Decap with parameters {}", algo_name(), params)); @@ -288,7 +285,7 @@ Botan::Null_RNG null_rng; auto dec = Botan::PK_KEM_Decryptor(*sk, null_rng, "Raw"); const auto shared_key = dec.decrypt(vars.get_req_bin("C"), 0 /* no KDF */); - result.test_is_eq("Decaps. Shared Secret", shared_key, Botan::lock(vars.get_req_bin("K"))); + result.test_bin_eq("Decaps. Shared Secret", shared_key, vars.get_req_bin("K")); return result; } diff -Nru botan3-3.7.1+dfsg/src/tests/test_rfc6979.cpp botan3-3.12.0+dfsg/src/tests/test_rfc6979.cpp --- botan3-3.7.1+dfsg/src/tests/test_rfc6979.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_rfc6979.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -32,17 +32,17 @@ auto hash_func = Botan::HashFunction::create(hash); if(!hash_func) { - result.test_note("Skipping due to missing: " + hash); + result.test_note("Skipping due to missing hash", hash); return result; } - result.test_eq("vector matches", Botan::generate_rfc6979_nonce(X, Q, H, hash), K); + result.test_bn_eq("vector matches", Botan::generate_rfc6979_nonce(X, Q, H, hash), K); Botan::RFC6979_Nonce_Generator gen(hash, Q.bits(), X); - result.test_eq("vector matches", gen.nonce_for(Q, H), K); - result.test_ne("different output for H+1", gen.nonce_for(Q, H + 1), K); - result.test_eq("vector matches when run again", gen.nonce_for(Q, H), K); + result.test_bn_eq("vector matches", gen.nonce_for(Q, H), K); + result.test_bn_ne("different output for H+1", gen.nonce_for(Q, H + 1), K); + result.test_bn_eq("vector matches when run again", gen.nonce_for(Q, H), K); return result; } diff -Nru botan3-3.7.1+dfsg/src/tests/test_rng.h botan3-3.12.0+dfsg/src/tests/test_rng.h --- botan3-3.7.1+dfsg/src/tests/test_rng.h 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_rng.h 2026-05-07 01:38:28.000000000 +0000 @@ -9,15 +9,15 @@ #define BOTAN_TESTS_RNGS_FOR_TESTING_H_ #include "tests.h" -#include -#include #include -#include +#include #include -#if defined(BOTAN_HAS_AES) - #include -#endif +namespace Botan { + +class BlockCipher; + +} namespace Botan_Tests { @@ -29,34 +29,25 @@ public: bool empty() const { return !is_seeded(); } - bool is_seeded() const override { return !m_buf.empty(); } + bool is_seeded() const override { return m_buf_pos < m_buf.size(); } bool accepts_input() const override { return true; } - size_t reseed(Botan::Entropy_Sources&, size_t, std::chrono::milliseconds) override { return 0; } - std::string name() const override { return "Fixed_Output_RNG"; } void clear() noexcept override {} explicit Fixed_Output_RNG(std::span in) { m_buf.insert(m_buf.end(), in.begin(), in.end()); } - explicit Fixed_Output_RNG(const std::string& in_str) { - std::vector in = Botan::hex_decode(in_str); - m_buf.insert(m_buf.end(), in.begin(), in.end()); - } + explicit Fixed_Output_RNG(const std::string& in_str); - Fixed_Output_RNG(RandomNumberGenerator& rng, size_t len) { - std::vector output; - rng.random_vec(output, len); - m_buf.insert(m_buf.end(), output.begin(), output.end()); - } + Fixed_Output_RNG(RandomNumberGenerator& rng, size_t len); /** * Provide a non-fixed RNG as fallback to be used once the Fixed_Output_RNG runs out of bytes. * If more bytes are provided after that, those will be preferred over the fallback again. */ - Fixed_Output_RNG(RandomNumberGenerator& fallback_rng) : m_fallback(&fallback_rng) {} + explicit Fixed_Output_RNG(RandomNumberGenerator& fallback_rng) : m_fallback(&fallback_rng) {} Fixed_Output_RNG() = default; @@ -69,22 +60,11 @@ } } - uint8_t random() { - if(m_buf.empty()) { - if(m_fallback.has_value()) { - return m_fallback.value()->next_byte(); - } else { - throw Test_Error("Fixed output RNG ran out of bytes, test bug?"); - } - } - - uint8_t out = m_buf.front(); - m_buf.pop_front(); - return out; - } + uint8_t random(); private: - std::deque m_buf; + std::vector m_buf; + size_t m_buf_pos = 0; std::optional m_fallback; }; @@ -165,8 +145,6 @@ */ class Request_Counting_RNG final : public Botan::RandomNumberGenerator { public: - Request_Counting_RNG() : m_randomize_count(0) {} - size_t randomize_count() const { return m_randomize_count; } bool accepts_input() const override { return false; } @@ -183,8 +161,8 @@ The HMAC_DRBG and ChaCha reseed KATs assume this RNG type outputs all 0x80 */ - for(auto& out : output) { - out = 0x80; + for(auto& b : output) { + b = 0x80; } if(!output.empty()) { m_randomize_count++; @@ -192,7 +170,7 @@ } private: - size_t m_randomize_count; + size_t m_randomize_count = 0; }; #if defined(BOTAN_HAS_AES) @@ -210,7 +188,14 @@ bool is_seeded() const override { return true; } - CTR_DRBG_AES256(std::span seed); + explicit CTR_DRBG_AES256(std::span seed); + + ~CTR_DRBG_AES256() override; + + CTR_DRBG_AES256(const CTR_DRBG_AES256& other) = delete; + CTR_DRBG_AES256(CTR_DRBG_AES256&& other) = default; + CTR_DRBG_AES256& operator=(const CTR_DRBG_AES256& other) = delete; + CTR_DRBG_AES256& operator=(CTR_DRBG_AES256&& other) = delete; private: void fill_bytes_with_input(std::span output, std::span input) override; @@ -219,7 +204,7 @@ void update(std::span provided_data); - uint64_t m_V0, m_V1; + uint64_t m_V0 = 0, m_V1 = 0; std::unique_ptr m_cipher; }; diff -Nru botan3-3.7.1+dfsg/src/tests/test_rng_behavior.cpp botan3-3.12.0+dfsg/src/tests/test_rng_behavior.cpp --- botan3-3.7.1+dfsg/src/tests/test_rng_behavior.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_rng_behavior.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -5,15 +5,22 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include "test_rng.h" #include "tests.h" +#include "test_rng.h" + +#include +#include +#include +#include + #if defined(BOTAN_HAS_STATEFUL_RNG) #include #endif #if defined(BOTAN_HAS_HMAC_DRBG) #include + #include #endif #if defined(BOTAN_HAS_AUTO_RNG) @@ -117,27 +124,27 @@ auto rng = make_rng(counting_rng, 2); rng->random_vec(7); - result.test_eq("initial seeding", counting_rng.randomize_count(), 1); + result.test_sz_eq("initial seeding", counting_rng.randomize_count(), 1); rng->random_vec(9); - result.test_eq("still initial seed", counting_rng.randomize_count(), 1); + result.test_sz_eq("still initial seed", counting_rng.randomize_count(), 1); rng->random_vec(1); - result.test_eq("first reseed", counting_rng.randomize_count(), 2); + result.test_sz_eq("first reseed", counting_rng.randomize_count(), 2); rng->random_vec(15); - result.test_eq("still first reseed", counting_rng.randomize_count(), 2); + result.test_sz_eq("still first reseed", counting_rng.randomize_count(), 2); rng->random_vec(15); - result.test_eq("second reseed", counting_rng.randomize_count(), 3); + result.test_sz_eq("second reseed", counting_rng.randomize_count(), 3); rng->random_vec(1); - result.test_eq("still second reseed", counting_rng.randomize_count(), 3); + result.test_sz_eq("still second reseed", counting_rng.randomize_count(), 3); if(rng->max_number_of_bytes_per_request() > 0) { // request > max_number_of_bytes_per_request, do reseeds occur? rng->random_vec(64 * 1024 + 1); - result.test_eq("request exceeds output limit", counting_rng.randomize_count(), 4); + result.test_sz_eq("request exceeds output limit", counting_rng.randomize_count(), 4); rng->random_vec(9 * 64 * 1024 + 1); - result.test_eq("request exceeds output limit", counting_rng.randomize_count(), 9); + result.test_sz_eq("request exceeds output limit", counting_rng.randomize_count(), 9); } return result; @@ -168,7 +175,7 @@ // underlying_rng throws exception Botan::Null_RNG broken_entropy_input_rng; - result.test_eq("Null_RNG not seeded", broken_entropy_input_rng.is_seeded(), false); + result.test_is_false("Null_RNG not seeded", broken_entropy_input_rng.is_seeded()); auto rng_with_broken_rng = make_rng(broken_entropy_input_rng); result.test_throws("broken underlying rng", [&rng_with_broken_rng]() { rng_with_broken_rng->random_vec(16); }); @@ -224,18 +231,18 @@ // make sure the nonce has at least security_strength bits auto rng = create_rng(nullptr, nullptr, 0); - for(size_t nonce_size : {0, 4, 8, 16, 31, 32, 34, 64}) { + for(const size_t nonce_size : {0, 4, 8, 16, 31, 32, 34, 64}) { rng->clear(); - result.test_eq("not seeded", rng->is_seeded(), false); + result.test_is_false("not seeded", rng->is_seeded()); const std::vector nonce(nonce_size); rng->initialize_with(nonce.data(), nonce.size()); if(nonce_size < rng->security_level() / 8) { - result.test_eq("not seeded", rng->is_seeded(), false); + result.test_is_false("not seeded", rng->is_seeded()); result.test_throws("invalid nonce size", [&rng]() { rng->random_vec(32); }); } else { - result.test_eq("is seeded", rng->is_seeded(), true); + result.test_is_true("is seeded", rng->is_seeded()); rng->random_vec(32); } } @@ -251,13 +258,13 @@ auto rng = make_rng(counting_rng, 1); rng->random_vec(16); - result.test_eq("first request", counting_rng.randomize_count(), size_t(1)); + result.test_sz_eq("first request", counting_rng.randomize_count(), size_t(1)); rng->random_vec(16); - result.test_eq("second request", counting_rng.randomize_count(), size_t(2)); + result.test_sz_eq("second request", counting_rng.randomize_count(), size_t(2)); rng->random_vec(16); - result.test_eq("third request", counting_rng.randomize_count(), size_t(3)); + result.test_sz_eq("third request", counting_rng.randomize_count(), size_t(3)); return result; } @@ -273,18 +280,19 @@ auto rng = make_rng(counting_rng, reseed_interval); rng->random_vec(16); - result.test_eq("first request", counting_rng.randomize_count(), size_t(1)); + result.test_sz_eq("first request", counting_rng.randomize_count(), size_t(1)); // fork and request from parent and child, both should output different sequences size_t count = counting_rng.randomize_count(); - Botan::secure_vector parent_bytes(16), child_bytes(16); + Botan::secure_vector parent_bytes(16); + Botan::secure_vector child_bytes(16); int fd[2]; - int rc = ::pipe(fd); + const int rc = ::pipe(fd); if(rc != 0) { result.test_failure("failed to create pipe"); } - pid_t pid = ::fork(); + const pid_t pid = ::fork(); if(pid == -1) { #if defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN) result.test_note("failed to fork process"); @@ -298,19 +306,19 @@ ssize_t got = ::read(fd[0], &count, sizeof(count)); if(got > 0) { - result.test_eq("expected bytes from child", got, sizeof(count)); - result.test_eq("parent not reseeded", counting_rng.randomize_count(), 1); - result.test_eq("child reseed occurred", count, 2); + result.test_sz_eq("expected bytes from child", got, sizeof(count)); + result.test_sz_eq("parent not reseeded", counting_rng.randomize_count(), 1); + result.test_sz_eq("child reseed occurred", count, 2); } else { result.test_failure("Failed to read count size from child process"); } parent_bytes = rng->random_vec(16); - got = ::read(fd[0], &child_bytes[0], child_bytes.size()); + got = ::read(fd[0], child_bytes.data(), child_bytes.size()); if(got > 0) { - result.test_eq("expected bytes from child", got, child_bytes.size()); - result.test_ne("parent and child output sequences differ", parent_bytes, child_bytes); + result.test_sz_eq("expected bytes from child", got, child_bytes.size()); + result.test_bin_ne("parent and child output sequences differ", parent_bytes, child_bytes); } else { result.test_failure("Failed to read RNG bytes from child process"); } @@ -322,17 +330,15 @@ } else { // child process, send randomize_count and first output sequence back to parent ::close(fd[0]); // close read end in child - rng->randomize(&child_bytes[0], child_bytes.size()); + rng->randomize(child_bytes.data(), child_bytes.size()); count = counting_rng.randomize_count(); - ssize_t written = ::write(fd[1], &count, sizeof(count)); - BOTAN_UNUSED(written); + [[maybe_unused]] ssize_t written = ::write(fd[1], &count, sizeof(count)); try { - rng->randomize(&child_bytes[0], child_bytes.size()); + rng->randomize(child_bytes.data(), child_bytes.size()); } catch(std::exception& e) { - static_cast(fprintf(stderr, "%s", e.what())); + static_cast(fprintf(stderr, "%s", e.what())); // NOLINT(*-vararg) } - written = ::write(fd[1], &child_bytes[0], child_bytes.size()); - BOTAN_UNUSED(written); + written = ::write(fd[1], child_bytes.data(), child_bytes.size()); ::close(fd[1]); // close write end in child /* @@ -340,8 +346,8 @@ * We can't call _exit because it makes valgrind think we leaked memory. * So instead we execute something that will return 0 for us. */ - ::execl("/bin/true", "true", NULL); - ::_exit(0); // just in case /bin/true isn't available (sandbox?) + ::execl("/bin/true", "true", NULL); // NOLINT(*-vararg) + ::_exit(0); // just in case /bin/true isn't available (sandbox?) } #endif return result; @@ -367,12 +373,12 @@ rng1->randomize(output1.data(), output1.size()); rng2->randomize(output2.data(), output2.size()); - result.test_eq("equal output due to same seed", output1, output2); + result.test_bin_eq("equal output due to same seed", output1, output2); rng1->randomize_with_ts_input(output1.data(), output1.size()); rng2->randomize_with_ts_input(output2.data(), output2.size()); - result.test_ne("output differs due to different timestamp", output1, output2); + result.test_bin_ne("output differs due to different timestamp", output1, output2); return result; } @@ -411,11 +417,11 @@ std::unique_ptr mac = Botan::MessageAuthenticationCode::create("HMAC(SHA-256)"); - if(underlying_rng && underlying_es) { + if(underlying_rng != nullptr && underlying_es != nullptr) { return std::make_unique(std::move(mac), *underlying_rng, *underlying_es, reseed_interval); - } else if(underlying_rng) { + } else if(underlying_rng != nullptr) { return std::make_unique(std::move(mac), *underlying_rng, reseed_interval); - } else if(underlying_es) { + } else if(underlying_es != nullptr) { return std::make_unique(std::move(mac), *underlying_es, reseed_interval); } else if(reseed_interval == 0) { return std::make_unique(std::move(mac)); @@ -431,14 +437,15 @@ Request_Counting_RNG counting_rng; - result.test_throws( - "HMAC_DRBG does not accept 0 for max_number_of_bytes_per_request", [&mac_string, &counting_rng]() { - Botan::HMAC_DRBG failing_rng(Botan::MessageAuthenticationCode::create(mac_string), counting_rng, 2, 0); - }); + result.test_throws("HMAC_DRBG does not accept 0 for max_number_of_bytes_per_request", + [&mac_string, &counting_rng]() { + const Botan::HMAC_DRBG failing_rng( + Botan::MessageAuthenticationCode::create(mac_string), counting_rng, 2, 0); + }); result.test_throws("HMAC_DRBG does not accept values higher than 64KB for max_number_of_bytes_per_request", [&mac_string, &counting_rng]() { - Botan::HMAC_DRBG failing_rng( + const Botan::HMAC_DRBG failing_rng( Botan::MessageAuthenticationCode::create(mac_string), counting_rng, 2, 64 * 1024 + 1); }); @@ -449,25 +456,25 @@ Botan::HMAC_DRBG rng(Botan::MessageAuthenticationCode::create(mac_string), counting_rng, 1, 64); rng.random_vec(63); - result.test_eq("one request", counting_rng.randomize_count(), 1); + result.test_sz_eq("one request", counting_rng.randomize_count(), 1); rng.clear(); counting_rng.clear(); rng.random_vec(64); - result.test_eq("one request", counting_rng.randomize_count(), 1); + result.test_sz_eq("one request", counting_rng.randomize_count(), 1); rng.clear(); counting_rng.clear(); rng.random_vec(65); - result.test_eq("two requests", counting_rng.randomize_count(), 2); + result.test_sz_eq("two requests", counting_rng.randomize_count(), 2); rng.clear(); counting_rng.clear(); rng.random_vec(1025); - result.test_eq("17 requests", counting_rng.randomize_count(), 17); + result.test_sz_eq("17 requests", counting_rng.randomize_count(), 17); return result; } @@ -480,14 +487,14 @@ Request_Counting_RNG counting_rng; result.test_throws("HMAC_DRBG does not accept 0 for reseed_interval", [&mac_string, &counting_rng]() { - Botan::HMAC_DRBG failing_rng(Botan::MessageAuthenticationCode::create(mac_string), counting_rng, 0); + const Botan::HMAC_DRBG failing_rng(Botan::MessageAuthenticationCode::create(mac_string), counting_rng, 0); }); result.test_throws("HMAC_DRBG does not accept values higher than 2^24 for reseed_interval", [&mac_string, &counting_rng]() { - Botan::HMAC_DRBG failing_rng(Botan::MessageAuthenticationCode::create(mac_string), - counting_rng, - (static_cast(1) << 24) + 1); + const Botan::HMAC_DRBG failing_rng(Botan::MessageAuthenticationCode::create(mac_string), + counting_rng, + (static_cast(1) << 24) + 1); }); return result; @@ -510,8 +517,8 @@ continue; } - Botan::HMAC_DRBG rng(std::move(mac)); - result.test_eq(hash_fn + " security level", rng.security_level(), expected_security_level); + const Botan::HMAC_DRBG rng(std::move(mac)); + result.test_sz_eq(hash_fn + " security level", rng.security_level(), expected_security_level); } return result; @@ -527,20 +534,22 @@ {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}); - result.test_eq("is_seeded", rng->is_seeded(), false); + result.test_is_false("is_seeded", rng->is_seeded()); rng->initialize_with(seed_input.data(), seed_input.size()); Botan::secure_vector out(32); rng->randomize(out.data(), out.size()); - result.test_eq("underlying RNG calls", counting_rng.randomize_count(), size_t(0)); - result.test_eq("out before reseed", out, "48D3B45AAB65EF92CCFCB9427EF20C90297065ECC1B8A525BFE4DC6FF36D0E38"); + result.test_sz_eq("underlying RNG calls", counting_rng.randomize_count(), size_t(0)); + result.test_bin_eq( + "out before reseed", out, "48D3B45AAB65EF92CCFCB9427EF20C90297065ECC1B8A525BFE4DC6FF36D0E38"); // reseed must happen here rng->randomize(out.data(), out.size()); - result.test_eq("underlying RNG calls", counting_rng.randomize_count(), size_t(1)); - result.test_eq("out after reseed", out, "2F8FCA696832C984781123FD64F4B20C7379A25C87AB29A21C9BF468B0081CE2"); + result.test_sz_eq("underlying RNG calls", counting_rng.randomize_count(), size_t(1)); + result.test_bin_eq( + "out after reseed", out, "2F8FCA696832C984781123FD64F4B20C7379A25C87AB29A21C9BF468B0081CE2"); return result; } @@ -565,8 +574,8 @@ auto rng1 = make_seeded_rng(2); auto rng2 = make_seeded_rng(2); - result.confirm("RNG 1 is seeded and ready to go", rng1->is_seeded()); - result.confirm("RNG 2 is seeded and ready to go", rng2->is_seeded()); + result.test_is_true("RNG 1 is seeded and ready to go", rng1->is_seeded()); + result.test_is_true("RNG 2 is seeded and ready to go", rng2->is_seeded()); auto bulk = rng1->random_vec>(2 * rng_max_output); @@ -574,7 +583,7 @@ auto split2 = rng2->random_vec>(rng_max_output); split1.insert(split1.end(), split2.begin(), split2.end()); - result.test_eq("Output is equal, regardless bulk request", bulk, split1); + result.test_bin_eq("Output is equal, regardless bulk request", bulk, split1); return result; }), @@ -583,26 +592,26 @@ auto rng1 = make_seeded_rng(3); auto rng2 = make_seeded_rng(3); - result.confirm("RNG 1 is seeded and ready to go", rng1->is_seeded()); - result.confirm("RNG 2 is seeded and ready to go", rng2->is_seeded()); + result.test_is_true("RNG 1 is seeded and ready to go", rng1->is_seeded()); + result.test_is_true("RNG 2 is seeded and ready to go", rng2->is_seeded()); std::vector bulk(3 * rng_max_output); rng1->randomize_with_input(bulk, seed); std::vector split(3 * rng_max_output); - std::span split_span(split); + const std::span split_span(split); rng2->randomize_with_input(split_span.subspan(0, rng_max_output), seed); rng2->randomize_with_input(split_span.subspan(rng_max_output, rng_max_output), {}); rng2->randomize_with_input(split_span.subspan(2 * rng_max_output), {}); - result.test_eq("Output is equal, regardless bulk request", bulk, split); + result.test_bin_eq("Output is equal, regardless bulk request", bulk, split); return result; })}; } BOTAN_REGISTER_TEST("rng", "hmac_drbg_unit", HMAC_DRBG_Unit_Tests); -BOTAN_REGISTER_TEST_FN("rng", "hmac_drbg_multi_requst", hmac_drbg_multiple_requests); +BOTAN_REGISTER_TEST_FN("rng", "hmac_drbg_multi_request", hmac_drbg_multiple_requests); #endif @@ -615,11 +624,11 @@ std::unique_ptr create_rng(Botan::RandomNumberGenerator* underlying_rng, Botan::Entropy_Sources* underlying_es, size_t reseed_interval) override { - if(underlying_rng && underlying_es) { + if(underlying_rng != nullptr && underlying_es != nullptr) { return std::make_unique(*underlying_rng, *underlying_es, reseed_interval); - } else if(underlying_rng) { + } else if(underlying_rng != nullptr) { return std::make_unique(*underlying_rng, reseed_interval); - } else if(underlying_es) { + } else if(underlying_es != nullptr) { return std::make_unique(*underlying_es, reseed_interval); } else if(reseed_interval == 0) { return std::make_unique(); @@ -630,8 +639,8 @@ Test::Result test_security_level() override { Test::Result result("ChaCha_RNG Security Level"); - Botan::ChaCha_RNG rng; - result.test_eq("Expected security level", rng.security_level(), size_t(256)); + const Botan::ChaCha_RNG rng; + result.test_sz_eq("Expected security level", rng.security_level(), size_t(256)); return result; } @@ -655,20 +664,22 @@ const Botan::secure_vector seed_input(32); - result.test_eq("is_seeded", rng->is_seeded(), false); + result.test_is_false("is_seeded", rng->is_seeded()); rng->initialize_with(seed_input.data(), seed_input.size()); Botan::secure_vector out(32); rng->randomize(out.data(), out.size()); - result.test_eq("underlying RNG calls", counting_rng.randomize_count(), size_t(0)); - result.test_eq("out before reseed", out, "1F0E6F13429D5073B59C057C37CBE9587740A0A894D247E2596C393CE91DDC6F"); + result.test_sz_eq("underlying RNG calls", counting_rng.randomize_count(), size_t(0)); + result.test_bin_eq( + "out before reseed", out, "1F0E6F13429D5073B59C057C37CBE9587740A0A894D247E2596C393CE91DDC6F"); // reseed must happen here rng->randomize(out.data(), out.size()); - result.test_eq("underlying RNG calls", counting_rng.randomize_count(), size_t(1)); - result.test_eq("out after reseed", out, "F2CAE73F22684D5D773290B48FDCDA0E6C0661EBA0A854AFEC922832BDBB9C49"); + result.test_sz_eq("underlying RNG calls", counting_rng.randomize_count(), size_t(1)); + result.test_bin_eq( + "out after reseed", out, "F2CAE73F22684D5D773290B48FDCDA0E6C0661EBA0A854AFEC922832BDBB9C49"); return result; } @@ -687,10 +698,10 @@ Botan::Null_RNG null_rng; - result.test_eq("Null_RNG is null", null_rng.is_seeded(), false); + result.test_is_false("Null_RNG is null", null_rng.is_seeded()); try { - Botan::AutoSeeded_RNG rng(null_rng); + const Botan::AutoSeeded_RNG rng(null_rng); } catch(Botan::PRNG_Unseeded&) { result.test_success("AutoSeeded_RNG rejected useless RNG"); } @@ -699,14 +710,14 @@ Botan::Entropy_Sources no_entropy_for_you; try { - Botan::AutoSeeded_RNG rng(no_entropy_for_you); + const Botan::AutoSeeded_RNG rng(no_entropy_for_you); result.test_failure("AutoSeeded_RNG should have rejected useless entropy source"); } catch(Botan::PRNG_Unseeded&) { result.test_success("AutoSeeded_RNG rejected empty entropy source"); } try { - Botan::AutoSeeded_RNG rng(null_rng, no_entropy_for_you); + const Botan::AutoSeeded_RNG rng(null_rng, no_entropy_for_you); } catch(Botan::PRNG_Unseeded&) { result.test_success("AutoSeeded_RNG rejected useless RNG+entropy sources"); } @@ -714,30 +725,30 @@ Botan::AutoSeeded_RNG rng; - result.confirm("AutoSeeded_RNG::name", rng.name().starts_with("HMAC_DRBG(HMAC(SHA-")); + result.test_is_true("AutoSeeded_RNG::name", rng.name().starts_with("HMAC_DRBG(HMAC(SHA-")); - result.confirm("AutoSeeded_RNG starts seeded", rng.is_seeded()); + result.test_is_true("AutoSeeded_RNG starts seeded", rng.is_seeded()); rng.random_vec(16); // generate and discard output rng.clear(); - result.test_eq("AutoSeeded_RNG unseeded after calling clear", rng.is_seeded(), false); + result.test_is_false("AutoSeeded_RNG unseeded after calling clear", rng.is_seeded()); // AutoSeeded_RNG automatically reseeds as required: rng.random_vec(16); - result.confirm("AutoSeeded_RNG can be reseeded", rng.is_seeded()); + result.test_is_true("AutoSeeded_RNG can be reseeded", rng.is_seeded()); - result.confirm("AutoSeeded_RNG ", rng.is_seeded()); + result.test_is_true("AutoSeeded_RNG ", rng.is_seeded()); rng.random_vec(16); // generate and discard output rng.clear(); - result.test_eq("AutoSeeded_RNG unseeded after calling clear", rng.is_seeded(), false); + result.test_is_false("AutoSeeded_RNG unseeded after calling clear", rng.is_seeded()); #if defined(BOTAN_HAS_ENTROPY_SOURCE) - const size_t no_entropy_bits = rng.reseed(no_entropy_for_you, 256, std::chrono::milliseconds(300)); - result.test_eq("AutoSeeded_RNG can't reseed from nothing", no_entropy_bits, 0); - result.test_eq("AutoSeeded_RNG still unseeded", rng.is_seeded(), false); + const size_t no_entropy_bits = rng.reseed_from(no_entropy_for_you, 256); + result.test_sz_eq("AutoSeeded_RNG can't reseed from nothing", no_entropy_bits, 0); + result.test_is_false("AutoSeeded_RNG still unseeded", rng.is_seeded()); #endif rng.random_vec(16); // generate and discard output - result.confirm("AutoSeeded_RNG can be reseeded", rng.is_seeded()); + result.test_is_true("AutoSeeded_RNG can be reseeded", rng.is_seeded()); for(size_t i = 0; i != 4096; ++i) { std::vector buf(i); @@ -773,14 +784,14 @@ Botan::System_RNG rng; - result.test_gte("Some non-empty name is returned", rng.name().size(), 1); + result.test_sz_gte("Some non-empty name is returned", rng.name().size(), 1); - result.confirm("System RNG always seeded", rng.is_seeded()); + result.test_is_true("System RNG always seeded", rng.is_seeded()); rng.clear(); // clear is a noop for system rng - result.confirm("System RNG always seeded", rng.is_seeded()); + result.test_is_true("System RNG always seeded", rng.is_seeded()); #if defined(BOTAN_HAS_ENTROPY_SOURCE) - rng.reseed(Botan::Entropy_Sources::global_sources(), 256, std::chrono::milliseconds(100)); + rng.reseed_from(Botan::Entropy_Sources::global_sources(), 256); #endif for(size_t i = 0; i != 128; ++i) { @@ -791,17 +802,17 @@ if(Test::run_long_tests() && Test::run_memory_intensive_tests() && (sizeof(size_t) > 4)) { // Pass buffer with a size greater than 32bit - const size_t size32BitsMax = std::numeric_limits::max(); + constexpr size_t maximum_u32 = 0xFFFFFFFF; const size_t checkSize = 1024; - std::vector large_buf(size32BitsMax + checkSize); - std::memset(large_buf.data() + size32BitsMax, 0xFE, checkSize); + std::vector large_buf(maximum_u32 + checkSize); + std::memset(large_buf.data() + maximum_u32, 0xFE, checkSize); rng.randomize(large_buf.data(), large_buf.size()); std::vector check_buf(checkSize, 0xFE); - result.confirm("System RNG failed to write after 4GB boundry", - std::memcmp(large_buf.data() + size32BitsMax, check_buf.data(), checkSize) != 0); + result.test_is_true("System RNG failed to write after 4GB boundary", + std::memcmp(large_buf.data() + maximum_u32, check_buf.data(), checkSize) != 0); } return std::vector{result}; @@ -822,14 +833,14 @@ if(Botan::Processor_RNG::available()) { Botan::Processor_RNG rng; - result.test_ne("Has a name", rng.name(), ""); - result.confirm("CPU RNG always seeded", rng.is_seeded()); + result.test_str_not_empty("Has a name", rng.name()); + result.test_is_true("CPU RNG always seeded", rng.is_seeded()); rng.clear(); // clear is a noop for rdrand - result.confirm("CPU RNG always seeded", rng.is_seeded()); + result.test_is_true("CPU RNG always seeded", rng.is_seeded()); #if defined(BOTAN_HAS_ENTROPY_SOURCE) - size_t reseed_bits = rng.reseed(Botan::Entropy_Sources::global_sources(), 256, std::chrono::seconds(1)); - result.test_eq("CPU RNG cannot consume inputs", reseed_bits, size_t(0)); + const size_t reseed_bits = rng.reseed_from(Botan::Entropy_Sources::global_sources(), 256); + result.test_sz_eq("CPU RNG cannot consume inputs", reseed_bits, size_t(0)); #endif /* @@ -847,7 +858,8 @@ rng.randomize(out_buf.data(), out_buf.size()); } } else { - result.test_throws("Processor_RNG throws if instruction not available", []() { Botan::Processor_RNG rng; }); + result.test_throws("Processor_RNG throws if instruction not available", + []() { const Botan::Processor_RNG rng; }); } return std::vector{result}; diff -Nru botan3-3.7.1+dfsg/src/tests/test_rng_kat.cpp botan3-3.12.0+dfsg/src/tests/test_rng_kat.cpp --- botan3-3.7.1+dfsg/src/tests/test_rng_kat.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_rng_kat.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,6 +9,7 @@ #if defined(BOTAN_HAS_HMAC_DRBG) #include + #include #endif #if defined(BOTAN_HAS_CHACHA_RNG) @@ -55,7 +56,7 @@ rng->randomize_with_input(out.data(), out.size(), ad1.data(), ad1.size()); rng->randomize_with_input(out.data(), out.size(), ad2.data(), ad2.size()); - result.test_eq("rng", out, expected); + result.test_bin_eq("rng", out, expected); return result; } }; @@ -93,7 +94,7 @@ rng.randomize_with_input(out.data(), out.size(), ad1.data(), ad1.size()); rng.randomize_with_input(out.data(), out.size(), ad2.data(), ad2.size()); - result.test_eq("rng", out, expected); + result.test_bin_eq("rng", out, expected); return result; } }; diff -Nru botan3-3.7.1+dfsg/src/tests/test_rngs.cpp botan3-3.12.0+dfsg/src/tests/test_rngs.cpp --- botan3-3.7.1+dfsg/src/tests/test_rngs.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_rngs.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,16 +6,45 @@ #include "test_rng.h" +#include +#include + #if defined(BOTAN_HAS_AES) + #include #include #endif -#include - namespace Botan_Tests { +uint8_t Fixed_Output_RNG::random() { + if(m_buf_pos >= m_buf.size()) { + if(m_fallback.has_value()) { + return m_fallback.value()->next_byte(); + } else { + throw Test_Error("Fixed output RNG ran out of bytes, test bug?"); + } + } + + const uint8_t out = m_buf[m_buf_pos]; + m_buf_pos += 1; + return out; +} + +Fixed_Output_RNG::Fixed_Output_RNG(const std::string& in_str) { + std::vector in = Botan::hex_decode(in_str); + m_buf.insert(m_buf.end(), in.begin(), in.end()); +} + +Fixed_Output_RNG::Fixed_Output_RNG(RandomNumberGenerator& rng, size_t len) { + std::vector output; + rng.random_vec(output, len); + m_buf.insert(m_buf.end(), output.begin(), output.end()); +} + #if defined(BOTAN_HAS_AES) +CTR_DRBG_AES256::~CTR_DRBG_AES256() = default; + void CTR_DRBG_AES256::clear() { const uint8_t zeros[32] = {0}; m_cipher->set_key(zeros, 32); @@ -54,8 +83,8 @@ } } -CTR_DRBG_AES256::CTR_DRBG_AES256(std::span seed) { - m_cipher = Botan::BlockCipher::create_or_throw("AES-256"); +CTR_DRBG_AES256::CTR_DRBG_AES256(std::span seed) : + m_cipher(Botan::BlockCipher::create_or_throw("AES-256")) { add_entropy(seed); } @@ -73,7 +102,7 @@ void CTR_DRBG_AES256::update(std::span provided_data) { std::array temp = {0}; - std::span t(temp); + const std::span t(temp); for(size_t i = 0; i != 3; ++i) { incr_V_into(t.subspan(16 * i, 16)); } @@ -87,7 +116,7 @@ } } - m_cipher->set_key(temp.data(), 32); // TODO: adapt after GH #3297 + m_cipher->set_key(std::span(temp).first(32)); m_V0 = Botan::load_be(temp.data() + 32, 0); m_V1 = Botan::load_be(temp.data() + 32, 1); diff -Nru botan3-3.7.1+dfsg/src/tests/test_roughtime.cpp botan3-3.12.0+dfsg/src/tests/test_roughtime.cpp --- botan3-3.7.1+dfsg/src/tests/test_roughtime.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_roughtime.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -4,7 +4,6 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include "test_rng.h" #include "tests.h" #if defined(BOTAN_HAS_BIGINT) @@ -14,12 +13,15 @@ #if defined(BOTAN_HAS_ROUGHTIME) #include #include - #include + #include + #include #include #endif namespace Botan_Tests { +namespace { + #if defined(BOTAN_HAS_ROUGHTIME) class Roughtime_Request_Tests final : public Text_Based_Test { @@ -29,12 +31,17 @@ Test::Result run_one_test(const std::string& type, const VarMap& vars) override { Test::Result result("Roughtime request"); - const auto nonce = vars.get_req_bin("Nonce"); + const auto nonce = Botan::Roughtime::Nonce(vars.get_req_bin("Nonce")); const auto request_v = vars.get_req_bin("Request"); const auto request = Botan::Roughtime::encode_request(nonce); - result.test_eq( - "encode", type == "Valid", request == Botan::typecast_copy>(request_v.data())); + + // TODO should use byte comparison function + if(type == "Valid") { + result.test_is_true("encode", request == Botan::typecast_copy>(request_v.data())); + } else { + result.test_is_true("encode", request != Botan::typecast_copy>(request_v.data())); + } return result; } @@ -65,18 +72,18 @@ } if(!response.validate(Botan::Ed25519_PublicKey(pubkey))) { - result.confirm("fail_validation", type == "Invalid"); + result.test_is_true("fail_validation", type == "Invalid"); } else { const auto midpoint = Botan::Roughtime::Response::sys_microseconds64( std::chrono::microseconds(vars.get_req_u64("MidpointMicroSeconds"))); const auto radius = std::chrono::microseconds(vars.get_req_u32("RadiusMicroSeconds")); - result.confirm("midpoint", response.utc_midpoint() == midpoint); - result.confirm("radius", response.utc_radius() == radius); - result.confirm("OK", type == "Valid"); + result.test_is_true("midpoint", response.utc_midpoint() == midpoint); + result.test_is_true("radius", response.utc_radius() == radius); + result.test_is_true("OK", type == "Valid"); } } catch(const Botan::Roughtime::Roughtime_Error& e) { - result.confirm(e.what(), type == "Invalid"); + result.test_is_true(e.what(), type == "Invalid"); } return result; @@ -94,11 +101,16 @@ Test::Result result("roughtime nonce_from_blind"); const auto response = vars.get_req_bin("Response"); - const auto blind = vars.get_req_bin("Blind"); - const auto nonce = vars.get_req_bin("Nonce"); + const auto blind = Botan::Roughtime::Nonce(vars.get_req_bin("Blind")); + const auto nonce = Botan::Roughtime::Nonce(vars.get_req_bin("Nonce")); - result.test_eq( - "fail_validation", Botan::Roughtime::nonce_from_blind(response, blind) == nonce, type == "Valid"); + const auto from_blind = Botan::Roughtime::nonce_from_blind(response, blind); + + if(type == "Valid") { + result.test_is_true("valid nonce_from_blind", nonce == from_blind); + } else { + result.test_is_false("valid nonce_from_blind", nonce == from_blind); + } return result; } @@ -112,17 +124,19 @@ Test::Result result("roughtime nonce"); 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())); - Botan::Roughtime::Nonce nonce_a(Botan::typecast_copy>(rand64.data())); - result.confirm("nonce from array", - nonce_v.get_nonce() == Botan::typecast_copy>(rand64.data())); + const Botan::Roughtime::Nonce nonce_v(rand64); + result.test_is_true("nonce from vector", + nonce_v.get_nonce() == Botan::typecast_copy>(rand64.data())); + const Botan::Roughtime::Nonce nonce_a(Botan::typecast_copy>(rand64.data())); + result.test_is_true("nonce from array", + nonce_v.get_nonce() == Botan::typecast_copy>(rand64.data())); rand64.push_back(10); - result.test_throws("vector oversize", [&rand64]() { Botan::Roughtime::Nonce nonce_v2(rand64); }); //size 65 + result.test_throws("vector oversize", + [&rand64]() { const Botan::Roughtime::Nonce nonce_v2(rand64); }); //size 65 rand64.pop_back(); rand64.pop_back(); - result.test_throws("vector undersize", [&rand64]() { Botan::Roughtime::Nonce nonce_v2(rand64); }); //size 63 + result.test_throws("vector undersize", + [&rand64]() { const Botan::Roughtime::Nonce nonce_v2(rand64); }); //size 63 return result; } @@ -131,11 +145,11 @@ Test::Result result("roughtime chain"); Botan::Roughtime::Chain c1; - result.confirm("default constructed is empty", c1.links().empty() && c1.responses().empty()); + result.test_is_true("default constructed is empty", c1.links().empty() && c1.responses().empty()); auto rand64 = Botan::unlock(rng.random_vec(64)); - Botan::Roughtime::Nonce nonce_v(rand64); - result.confirm( + const Botan::Roughtime::Nonce nonce_v(rand64); + result.test_is_true( "empty chain nonce is blind", c1.next_nonce(nonce_v).get_nonce() == Botan::typecast_copy>(rand64.data())); @@ -144,36 +158,37 @@ "ed25519 gD63hSj3ScS+wuOeGrubXlq35N1c5Lby/S+T7MNTjxo= uLeTON9D+2HqJMzK6sYWLNDEdtBl9t/9yw1cVAOm0/sONH5Oqdq9dVPkC9syjuWbglCiCPVF+FbOtcxCkrgMmA== BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWOw1jl0uSiBEH9HE8/6r7zxoSc01f48vw+UzH8+VJoPelnvVJBj4lnH8uRLh5Aw0i4Du7XM1dp2u0r/I5PzhMQoDAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AUBo+tEqPBQC47l77to7ESFTVhlw1SC74P5ssx6gpuJ6eP+1916GuUiySGE/x3Fp0c3otUGAdsRQou5p9PDTeane/YEeVq4/8AgAAAEAAAABTSUcAREVMRe5T1ml8wHyWAcEtHP/U5Rg/jFXTEXOSglngSa4aI/CECVdy4ZNWeP6vv+2//ZW7lQsrWo7ZkXpvm9BdBONRSQIDAAAAIAAAACgAAABQVUJLTUlOVE1BWFQpXlenV0OfVisvp9jDHXLw8vymZVK9Pgw9k6Edf8ZEhUgSGEc5jwUASHLvZE2PBQAAAAAA\n"; Botan::Roughtime::Chain c2(chain_str); - result.confirm("have two elements", c2.links().size() == 2 && c2.responses().size() == 2); - result.confirm("serialize loopback", c2.to_string() == chain_str); + result.test_is_true("have two elements", c2.links().size() == 2 && c2.responses().size() == 2); + result.test_is_true("serialize loopback", c2.to_string() == chain_str); c1.append(c2.links()[0], 1); - result.confirm("append ok", c1.links().size() == 1 && c1.responses().size() == 1); + result.test_is_true("append ok", c1.links().size() == 1 && c1.responses().size() == 1); c1.append(c2.links()[1], 1); - result.confirm("max size", c1.links().size() == 1 && c1.responses().size() == 1); + result.test_is_true("max size", c1.links().size() == 1 && c1.responses().size() == 1); result.test_throws("non-positive max chain size", [&]() { c1.append(c2.links()[1], 0); }); - result.test_throws("1 field", [&]() { Botan::Roughtime::Chain a("ed25519"); }); - result.test_throws( - "2 fields", [&]() { Botan::Roughtime::Chain a("ed25519 bbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE="); }); + result.test_throws("1 field", [&]() { const Botan::Roughtime::Chain a("ed25519"); }); + result.test_throws("2 fields", [&]() { + const Botan::Roughtime::Chain a("ed25519 bbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE="); + }); result.test_throws("3 fields", [&]() { - Botan::Roughtime::Chain a( + const Botan::Roughtime::Chain a( "ed25519 bbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE= eu9yhsJfVfguVSqGZdE8WKIxaBBM0ZG3Vmuc+IyZmG2YVmrIktUByDdwIFw6F4rZqmSFsBO85ljoVPz5bVPCOw=="); }); result.test_throws("5 fields", [&]() { - Botan::Roughtime::Chain a( + const Botan::Roughtime::Chain a( "ed25519 bbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE= eu9yhsJfVfguVSqGZdE8WKIxaBBM0ZG3Vmuc+IyZmG2YVmrIktUByDdwIFw6F4rZqmSFsBO85ljoVPz5bVPCOw== BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWBnGOEajOwPA6G7oL47seBP4C7eEpr57H43C2/fK/kMA0UGZVUdf4KNX8oxOK6JIcsbVk8qhghTwA70qtwpYmQkDAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AJrA8tEqPBQAqisiuAxgy2Pj7UJAiWbCdzGz1xcCnja3T+AqhC8fwpeIwW4GPy/vEb/awXW2DgSLKJfzWIAz+2lsR7t4UjNPvAgAAAEAAAABTSUcAREVMRes9Ch4X0HIw5KdOTB8xK4VDFSJBD/G9t7Et/CU7UW61OiTBXYYQTG2JekWZmGa0OHX1JPGG+APkpbsNw0BKUgYDAAAAIAAAACgAAABQVUJLTUlOVE1BWFR/9BWjpsWTQ1f6iUJea3EfZ1MkX3ftJiV3ABqNLpncFwAAAAAAAAAA//////////8AAAAA abc"); }); result.test_throws("invalid key type", [&]() { - Botan::Roughtime::Chain a( + const Botan::Roughtime::Chain a( "rsa bbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE= eu9yhsJfVfguVSqGZdE8WKIxaBBM0ZG3Vmuc+IyZmG2YVmrIktUByDdwIFw6F4rZqmSFsBO85ljoVPz5bVPCOw== BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWBnGOEajOwPA6G7oL47seBP4C7eEpr57H43C2/fK/kMA0UGZVUdf4KNX8oxOK6JIcsbVk8qhghTwA70qtwpYmQkDAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AJrA8tEqPBQAqisiuAxgy2Pj7UJAiWbCdzGz1xcCnja3T+AqhC8fwpeIwW4GPy/vEb/awXW2DgSLKJfzWIAz+2lsR7t4UjNPvAgAAAEAAAABTSUcAREVMRes9Ch4X0HIw5KdOTB8xK4VDFSJBD/G9t7Et/CU7UW61OiTBXYYQTG2JekWZmGa0OHX1JPGG+APkpbsNw0BKUgYDAAAAIAAAACgAAABQVUJLTUlOVE1BWFR/9BWjpsWTQ1f6iUJea3EfZ1MkX3ftJiV3ABqNLpncFwAAAAAAAAAA//////////8AAAAA"); }); result.test_throws("invalid key", [&]() { - Botan::Roughtime::Chain a( + const Botan::Roughtime::Chain a( "ed25519 bbT+RPS7zKX6wssPibzmwWqU9ffRV5oj2OresSmhE= eu9yhsJfVfguVSqGZdE8WKIxaBBM0ZG3Vmuc+IyZmG2YVmrIktUByDdwIFw6F4rZqmSFsBO85ljoVPz5bVPCOw== BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWBnGOEajOwPA6G7oL47seBP4C7eEpr57H43C2/fK/kMA0UGZVUdf4KNX8oxOK6JIcsbVk8qhghTwA70qtwpYmQkDAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AJrA8tEqPBQAqisiuAxgy2Pj7UJAiWbCdzGz1xcCnja3T+AqhC8fwpeIwW4GPy/vEb/awXW2DgSLKJfzWIAz+2lsR7t4UjNPvAgAAAEAAAABTSUcAREVMRes9Ch4X0HIw5KdOTB8xK4VDFSJBD/G9t7Et/CU7UW61OiTBXYYQTG2JekWZmGa0OHX1JPGG+APkpbsNw0BKUgYDAAAAIAAAACgAAABQVUJLTUlOVE1BWFR/9BWjpsWTQ1f6iUJea3EfZ1MkX3ftJiV3ABqNLpncFwAAAAAAAAAA//////////8AAAAA"); }); result.test_throws("invalid nonce", [&]() { - Botan::Roughtime::Chain a( + const Botan::Roughtime::Chain a( "ed25519 bbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE= eu9yhsJfVfguVSqGZdE8WKIxaBBM0ZG3Vmuc+IyZmG2UByDdwIFw6F4rZqmSFsBO85ljoVPz5bVPCOw== BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWBnGOEajOwPA6G7oL47seBP4C7eEpr57H43C2/fK/kMA0UGZVUdf4KNX8oxOK6JIcsbVk8qhghTwA70qtwpYmQkDAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AJrA8tEqPBQAqisiuAxgy2Pj7UJAiWbCdzGz1xcCnja3T+AqhC8fwpeIwW4GPy/vEb/awXW2DgSLKJfzWIAz+2lsR7t4UjNPvAgAAAEAAAABTSUcAREVMRes9Ch4X0HIw5KdOTB8xK4VDFSJBD/G9t7Et/CU7UW61OiTBXYYQTG2JekWZmGa0OHX1JPGG+APkpbsNw0BKUgYDAAAAIAAAACgAAABQVUJLTUlOVE1BWFR/9BWjpsWTQ1f6iUJea3EfZ1MkX3ftJiV3ABqNLpncFwAAAAAAAAAA//////////8AAAAA"); }); @@ -190,16 +205,16 @@ "int08h-Roughtime ed25519 AW5uAoTSTDfG5NfY1bTh08GUnOqlRb+HVhbJ3ODJvsE= udp roughtime.int08h.com:2002\n" "ticktock ed25519 cj8GsiNlRkqiDElAeNMSBBMwrAl15hYPgX50+GWX/lA= udp ticktock.mixmin.net:5333\n"); - result.confirm("size", servers.size() == 5); - result.test_eq("name", servers[0].name(), "Chainpoint-Roughtime"); - result.test_eq("name", servers[4].name(), "ticktock"); - result.confirm( + result.test_is_true("size", servers.size() == 5); + result.test_str_eq("name", servers[0].name(), "Chainpoint-Roughtime"); + result.test_str_eq("name", servers[4].name(), "ticktock"); + result.test_is_true( "public key", servers[0].public_key().get_public_key() == Botan::Ed25519_PublicKey(Botan::base64_decode("bbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE=")) .get_public_key()); - result.confirm("single address", servers[0].addresses().size() == 1); - result.test_eq("address", servers[0].addresses()[0], "roughtime.chainpoint.org:2002"); + result.test_is_true("single address", servers[0].addresses().size() == 1); + result.test_str_eq("address", servers[0].addresses()[0], "roughtime.chainpoint.org:2002"); result.test_throws("1 field", [&]() { Botan::Roughtime::servers_from_str("A"); }); result.test_throws("2 fields", [&]() { Botan::Roughtime::servers_from_str("A ed25519"); }); @@ -231,16 +246,16 @@ static Test::Result test_request_online(Botan::RandomNumberGenerator& rng) { Test::Result result("roughtime request online"); - Botan::Roughtime::Nonce nonce(rng); + const Botan::Roughtime::Nonce nonce(rng); try { const auto response_raw = Botan::Roughtime::online_request("roughtime.cloudflare.com:2003", nonce, std::chrono::seconds(5)); const auto now = std::chrono::system_clock::now(); const auto response = Botan::Roughtime::Response::from_bits(response_raw, nonce); - std::chrono::milliseconds local_clock_max_error(1000); + const std::chrono::milliseconds local_clock_max_error(1000); const auto diff_abs = now >= response.utc_midpoint() ? now - response.utc_midpoint() : response.utc_midpoint() - now; - result.confirm("online", diff_abs <= (response.utc_radius() + local_clock_max_error)); + result.test_is_true("online", diff_abs <= (response.utc_radius() + local_clock_max_error)); } catch(const std::exception& e) { result.test_failure(e.what()); } @@ -265,4 +280,6 @@ #endif +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_rsa.cpp botan3-3.12.0+dfsg/src/tests/test_rsa.cpp --- botan3-3.7.1+dfsg/src/tests/test_rsa.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_rsa.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -4,12 +4,14 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include "test_rng.h" #include "tests.h" #if defined(BOTAN_HAS_RSA) #include "test_pubkey.h" + #include "test_rng.h" + #include #include + #include #include #endif @@ -167,11 +169,11 @@ Request_Counting_RNG rng; try { - Botan::RSA_PrivateKey rsa(rng, 1024); + const Botan::RSA_PrivateKey rsa(rng, 1024); result.test_failure("Generated a key with a bad RNG"); } catch(Botan::Internal_Error& e) { result.test_success("Key generation with bad RNG failed"); - result.test_eq("Expected message", e.what(), "Internal error: RNG failure during RSA key generation"); + result.test_str_eq("Expected message", e.what(), "Internal error: RNG failure during RSA key generation"); } return {result}; @@ -190,14 +192,14 @@ } #if defined(BOTAN_HAS_EMSA_RAW) || defined(BOTAN_HAS_EME_RAW) - Botan::RSA_PrivateKey rsa(this->rng(), 1024); + const Botan::RSA_PrivateKey rsa(this->rng(), 1024); Botan::Null_RNG null_rng; #endif #if defined(BOTAN_HAS_EMSA_RAW) /* - * The blinder chooses a new starting point BOTAN_BLINDING_REINIT_INTERVAL + * The blinder chooses a new starting point Blinder::ReinitInterval * so sign several times that with a single key. * * Very small values (padding/hashing disabled, only low byte set on input) @@ -208,7 +210,7 @@ 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) { + for(size_t i = 1; i <= Botan::Blinder::ReinitInterval * 6; ++i) { std::vector input(16); input[input.size() - 1] = static_cast(i | 1); @@ -217,14 +219,14 @@ // assert RNG is not called in this situation std::vector signature = signer.signature(null_rng); - result.test_eq("Signature verifies", verifier.verify_message(input, signature), true); + result.test_is_true("Signature verifies", verifier.verify_message(input, signature)); } #endif #if defined(BOTAN_HAS_EME_RAW) /* - * The blinder chooses a new starting point BOTAN_BLINDING_REINIT_INTERVAL + * The blinder chooses a new starting point Blinder::ReinitInterval * so decrypt several times that with a single key. * * Very small values (padding/hashing disabled, only low byte set on input) @@ -240,12 +242,12 @@ blinder initialization plus the exponent blinding bits which is 2*64 bits per operation. */ - const size_t rng_bytes = rsa.get_n().bytes() + (2 * 8 * BOTAN_BLINDING_REINIT_INTERVAL); + const size_t rng_bytes = rsa.get_n().bytes() + (2 * 8 * Botan::Blinder::ReinitInterval); 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) { + for(size_t i = 1; i <= Botan::Blinder::ReinitInterval; ++i) { std::vector input(16); input[input.size() - 1] = static_cast(i); @@ -254,14 +256,14 @@ std::vector plaintext = Botan::unlock(decryptor.decrypt(ciphertext)); plaintext.insert(plaintext.begin(), input.size() - 1, 0); - result.test_eq("Successful decryption", plaintext, input); + result.test_bin_eq("Successful decryption", plaintext, input); } - result.test_eq("RNG is no longer seeded", fixed_rng.is_seeded(), false); + result.test_is_false("RNG is no longer seeded", fixed_rng.is_seeded()); // one more decryption should trigger a blinder reinitialization result.test_throws("RSA blinding reinit", - "Test error Fixed output RNG ran out of bytes, test bug?", + "Fixed output RNG ran out of bytes, test bug?", [&decryptor, &encryptor, &null_rng]() { std::vector ciphertext = encryptor.encrypt(std::vector(16, 5), null_rng); @@ -310,10 +312,10 @@ auto public_key = private_key.public_key(); const auto msg = rng.random_vec(pt_len); - Botan::PK_Encryptor_EME enc(*public_key, rng, padding); + const Botan::PK_Encryptor_EME enc(*public_key, rng, padding); const auto ctext = enc.encrypt(msg, rng); - Botan::PK_Decryptor_EME dec(private_key, rng, padding); + const Botan::PK_Decryptor_EME dec(private_key, rng, padding); const BigInt modulus = public_key->get_int_field("n"); @@ -322,7 +324,7 @@ auto rec = dec.decrypt_or_random(bad_ctext.data(), bad_ctext.size(), pt_len, rng); - result.test_eq("Returns a ciphertext of expected length", rec.size(), pt_len); + result.test_sz_eq("Returns a ciphertext of expected length", rec.size(), pt_len); } // Test decrypt_or_random with content check happy path @@ -333,7 +335,7 @@ std::vector required_offsets(req_bytes); for(size_t j = 0; j != req_bytes; ++j) { - uint8_t idx = rng.next_byte() % pt_len; + const uint8_t idx = rng.next_byte() % pt_len; required_contents[j] = msg[idx]; required_offsets[j] = idx; } @@ -341,7 +343,7 @@ auto rec = dec.decrypt_or_random( ctext.data(), ctext.size(), pt_len, rng, required_contents.data(), required_offsets.data(), req_bytes); - result.test_eq("Returned the expected message", rec, msg); + result.test_bin_eq("Returned the expected message", rec, msg); } // Test decrypt_or_random with content check error path @@ -351,11 +353,11 @@ std::vector required_contents(req_bytes); std::vector required_offsets(req_bytes); - size_t corrupted = Test::random_index(rng, req_bytes); - uint8_t corruption = rng.next_nonzero_byte(); + const size_t corrupted = Test::random_index(rng, req_bytes); + const uint8_t corruption = rng.next_nonzero_byte(); for(size_t j = 0; j != req_bytes; ++j) { - uint8_t idx = rng.next_byte() % pt_len; + const uint8_t idx = rng.next_byte() % pt_len; required_offsets[j] = idx; if(idx == corrupted) { @@ -368,11 +370,11 @@ auto rec = dec.decrypt_or_random( ctext.data(), ctext.size(), pt_len, rng, required_contents.data(), required_offsets.data(), req_bytes); - result.test_ne("Returned random message", rec, ctext); + result.test_bin_ne("Returned random message", rec, ctext); for(size_t j = 0; j != req_bytes; ++j) { - result.confirm("Random message satisfies stated content requirements", - rec[required_offsets[j]] == required_contents[j]); + result.test_is_true("Random message satisfies stated content requirements", + rec[required_offsets[j]] == required_contents[j]); } } } diff -Nru botan3-3.7.1+dfsg/src/tests/test_simd.cpp botan3-3.12.0+dfsg/src/tests/test_simd.cpp --- botan3-3.7.1+dfsg/src/tests/test_simd.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_simd.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,28 +6,44 @@ #include "tests.h" -#if defined(BOTAN_HAS_SIMD_32) - #include +#include +#include +#include +#include +#include +#include + +#if defined(BOTAN_HAS_SIMD_4X32) + #include +#endif + +#if defined(BOTAN_HAS_SIMD_2X64) + #include +#endif + +#if defined(BOTAN_HAS_CPUID) #include - #include - #include - #include - #include #endif namespace Botan_Tests { -#if defined(BOTAN_HAS_SIMD_32) +namespace { + +#if defined(BOTAN_HAS_SIMD_4X32) && defined(BOTAN_HAS_CPUID) -class SIMD_32_Tests final : public Test { +class SIMD_4X32_Tests final : public Test { public: std::vector run() override { - Test::Result result("SIMD_4x32"); - - if(Botan::CPUID::has_simd_32() == false) { - result.test_note("Skipping SIMD_4x32 tests due to missing CPU support at runtime"); - return {result}; + if(!Botan::CPUID::has(Botan::CPUID::Feature::SIMD_4X32)) { + return {Test::Result::Note("simd_4x32", "Skipping tests due to missing SIMD support at runtime")}; + } else { + return {test_simd_4x32()}; } + } + + private: + Test::Result BOTAN_FN_ISA_SIMD_4X32 test_simd_4x32() { + Test::Result result("SIMD_4x32"); const uint32_t pat1 = 0xAABBCCDD; const uint32_t pat2 = 0x87654321; @@ -50,7 +66,7 @@ const Botan::SIMD_4x32 input(pat1, pat2, pat3, pat4); - Botan::SIMD_4x32 rol = input.rotl<3>(); + const Botan::SIMD_4x32 rol = input.rotl<3>(); test_eq(result, "rotl", @@ -60,7 +76,7 @@ Botan::rotl<3>(pat3), Botan::rotl<3>(pat4)); - Botan::SIMD_4x32 ror = input.rotr<9>(); + const Botan::SIMD_4x32 ror = input.rotr<9>(); test_eq(result, "rotr", @@ -151,34 +167,33 @@ auto simd_le_array_vec = Botan::store_le>(simd_le_array); auto simd_be_array_vec = Botan::store_be(simd_be_array); - result.test_is_eq("roundtrip SIMD little-endian", simd_le_vec, simd_le_in); - result.test_is_eq( + result.test_bin_eq("roundtrip SIMD little-endian", simd_le_vec, simd_le_in); + result.test_bin_eq( "roundtrip SIMD big-endian", std::vector(simd_be_vec.begin(), simd_be_vec.end()), simd_be_in); - result.test_is_eq("roundtrip SIMD array little-endian", simd_le_array_vec, simd_le_array_in); - result.test_is_eq("roundtrip SIMD array big-endian", - std::vector(simd_be_array_vec.begin(), simd_be_array_vec.end()), - simd_be_array_in); + result.test_bin_eq("roundtrip SIMD array little-endian", simd_le_array_vec, simd_le_array_in); + result.test_bin_eq("roundtrip SIMD array big-endian", + std::vector(simd_be_array_vec.begin(), simd_be_array_vec.end()), + simd_be_array_in); using StrongSIMD = Botan::Strong; const auto simd_le_strong = Botan::load_le(simd_le_in); const auto simd_be_strong = Botan::load_be(simd_be_in); - result.test_is_eq( + result.test_bin_eq( "roundtrip SIMD strong little-endian", Botan::store_le>(simd_le_strong), simd_le_in); - result.test_is_eq( + result.test_bin_eq( "roundtrip SIMD strong big-endian", Botan::store_be>(simd_be_strong), simd_be_in); return {result}; } - private: - static void test_eq(Test::Result& result, - const std::string& op, - const Botan::SIMD_4x32& simd, - uint32_t exp0, - uint32_t exp1, - uint32_t exp2, - uint32_t exp3) { + static void BOTAN_FN_ISA_SIMD_4X32 test_eq(Test::Result& result, + const std::string& op, + const Botan::SIMD_4x32& simd, + uint32_t exp0, + uint32_t exp1, + uint32_t exp2, + uint32_t exp3) { uint8_t arr_be[16 + 15]; uint8_t arr_be2[16 + 15]; uint8_t arr_le[16 + 15]; @@ -192,42 +207,216 @@ simd.store_be(mem_be); - result.test_int_eq( + result.test_u32_eq( "SIMD_4x32 " + op + " elem0 BE", Botan::make_uint32(mem_be[0], mem_be[1], mem_be[2], mem_be[3]), exp0); - result.test_int_eq( + result.test_u32_eq( "SIMD_4x32 " + op + " elem1 BE", Botan::make_uint32(mem_be[4], mem_be[5], mem_be[6], mem_be[7]), exp1); - result.test_int_eq( + result.test_u32_eq( "SIMD_4x32 " + op + " elem2 BE", Botan::make_uint32(mem_be[8], mem_be[9], mem_be[10], mem_be[11]), exp2); - result.test_int_eq("SIMD_4x32 " + op + " elem3 BE", + result.test_u32_eq("SIMD_4x32 " + op + " elem3 BE", Botan::make_uint32(mem_be[12], mem_be[13], mem_be[14], mem_be[15]), exp3); // Check load_be+store_be results in same value const Botan::SIMD_4x32 reloaded_be = Botan::SIMD_4x32::load_be(mem_be); reloaded_be.store_be(mem_be2); - result.test_eq(nullptr, "SIMD_4x32 load_be", mem_be, 16, mem_be2, 16); + result.test_bin_eq("SIMD_4x32 load_be", {mem_be, 16}, {mem_be2, 16}); simd.store_le(mem_le); - result.test_int_eq( + result.test_u32_eq( "SIMD_4x32 " + op + " elem0 LE", Botan::make_uint32(mem_le[3], mem_le[2], mem_le[1], mem_le[0]), exp0); - result.test_int_eq( + result.test_u32_eq( "SIMD_4x32 " + op + " elem1 LE", Botan::make_uint32(mem_le[7], mem_le[6], mem_le[5], mem_le[4]), exp1); - result.test_int_eq( + result.test_u32_eq( "SIMD_4x32 " + op + " elem2 LE", Botan::make_uint32(mem_le[11], mem_le[10], mem_le[9], mem_le[8]), exp2); - result.test_int_eq("SIMD_4x32 " + op + " elem3 LE", + result.test_u32_eq("SIMD_4x32 " + op + " elem3 LE", Botan::make_uint32(mem_le[15], mem_le[14], mem_le[13], mem_le[12]), exp3); // Check load_le+store_le results in same value const Botan::SIMD_4x32 reloaded_le = Botan::SIMD_4x32::load_le(mem_le); reloaded_le.store_le(mem_le2); - result.test_eq(nullptr, "SIMD_4x32 load_le", mem_le, 16, mem_le2, 16); + result.test_bin_eq("SIMD_4x32 load_le", {mem_le, 16}, {mem_le2, 16}); + } + } +}; + +BOTAN_REGISTER_TEST("utils", "simd_4x32", SIMD_4X32_Tests); +#endif + +#if defined(BOTAN_HAS_SIMD_2X64) && defined(BOTAN_HAS_CPUID) + +class SIMD_2X64_Tests final : public Test { + public: + std::vector BOTAN_FN_ISA_SIMD_2X64 run() override { + if(!Botan::CPUID::has(Botan::CPUID::Feature::SIMD_2X64)) { + return {Test::Result::Note("simd_2x64", "Skipping tests due to missing SIMD support at runtime")}; + } else { + return {test_simd_2x64()}; + } + } + + private: + Test::Result BOTAN_FN_ISA_SIMD_2X64 test_simd_2x64() { + Test::Result result("SIMD_2x64"); + + const uint64_t pat1 = 0x2F8C91D4A37E5C10; + const uint64_t pat2 = 0x1B74A6F8C29D1345; + + const uint64_t pat1_1 = pat1 + pat1; + const uint64_t pat1_2 = pat1 + pat2; + + test_eq(result, "default init", Botan::SIMD_2x64(), 0, 0); + test_eq(result, "SIMD scalar constructor", Botan::SIMD_2x64(1, 2), 1, 2); + + const auto input = Botan::SIMD_2x64(pat1, pat2); + const auto splat = Botan::SIMD_2x64(pat1, pat1); + + const auto rotl = input.rotl<3>(); + test_eq(result, "rotl", rotl, Botan::rotl<3>(pat1), Botan::rotl<3>(pat2)); + + const auto rotr = input.rotr<9>(); + test_eq(result, "rotr", rotr, Botan::rotr<9>(pat1), Botan::rotr<9>(pat2)); + + test_eq(result, "rotr<8>", input.rotr<8>(), Botan::rotr<8>(pat1), Botan::rotr<8>(pat2)); + test_eq(result, "rotr<16>", input.rotr<16>(), Botan::rotr<16>(pat1), Botan::rotr<16>(pat2)); + test_eq(result, "rotr<24>", input.rotr<24>(), Botan::rotr<24>(pat1), Botan::rotr<24>(pat2)); + test_eq(result, "rotr<32>", input.rotr<32>(), Botan::rotr<32>(pat1), Botan::rotr<32>(pat2)); + + const auto add = input + splat; + test_eq(result, "add +", add, pat1_1, pat1_2); + + test_eq(result, "xor", input ^ splat, 0, pat2 ^ pat1); + + auto shifter = Botan::SIMD_2x64(0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF); + shifter = shifter.shr<23>(); + test_eq(result, ">>", shifter, 0x1FFFFFFFFFF, 0x1FFFFFFFFFF); + + shifter = shifter.shl<27>(); + test_eq(result, "<<", shifter, 0xFFFFFFFFF8000000, 0xFFFFFFFFF8000000); + + shifter = input.andc(shifter); + test_eq(result, "andc", shifter, ~pat1 & 0xFFFFFFFFF8000000, ~pat2 & 0xFFFFFFFFF8000000); + + test_eq(result, "bswap", input.bswap(), Botan::reverse_bytes(pat1), Botan::reverse_bytes(pat2)); + + test_eq(result, + "reverse_all_bytes", + Botan::SIMD_2x64(0x0001020304050607, 0x08090a0b0c0d0e0f).reverse_all_bytes(), + 0x0f0e0d0c0b0a0908, + 0x0706050403020100); + + test_eq(result, "swap_lanes", Botan::SIMD_2x64(pat1, pat2).swap_lanes(), pat2, pat1); + + const auto interleave_a = Botan::SIMD_2x64(0x1111111122222222, 0x3333333344444444); + const auto interleave_b = Botan::SIMD_2x64(0x5555555566666666, 0x7777777788888888); + test_eq(result, + "interleave_high", + Botan::SIMD_2x64::interleave_high(interleave_a, interleave_b), + 0x3333333344444444, + 0x7777777788888888); + + test_eq(result, + "interleave_low", + Botan::SIMD_2x64::interleave_low(interleave_a, interleave_b), + 0x1111111122222222, + 0x5555555566666666); + + test_eq(result, "all_ones", Botan::SIMD_2x64::all_ones(), 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF); + + // Test load/stores SIMD wrapper types + const auto simd_le_in = Botan::hex_decode("ABCDEF01234567890123456789ABCDEF"); + const auto simd_be_in = Botan::hex_decode("0123456789ABCDEFABCDEF0123456789"); + const auto simd_le_array_in = Botan::concat(simd_le_in, simd_be_in); + const auto simd_be_array_in = Botan::concat(simd_be_in, simd_le_in); + + auto simd_le = Botan::load_le(simd_le_in); + auto simd_be = Botan::load_be(simd_be_in); + auto simd_le_array = Botan::load_le>(simd_le_array_in); + auto simd_be_array = Botan::load_be>(simd_be_array_in); + + auto simd_le_vec = Botan::store_le>(simd_le); + auto simd_be_vec = Botan::store_be(simd_be); + auto simd_le_array_vec = Botan::store_le>(simd_le_array); + auto simd_be_array_vec = Botan::store_be(simd_be_array); + + result.test_bin_eq("roundtrip SIMD little-endian", simd_le_vec, simd_le_in); + result.test_bin_eq( + "roundtrip SIMD big-endian", std::vector(simd_be_vec.begin(), simd_be_vec.end()), simd_be_in); + result.test_bin_eq("roundtrip SIMD array little-endian", simd_le_array_vec, simd_le_array_in); + result.test_bin_eq("roundtrip SIMD array big-endian", + std::vector(simd_be_array_vec.begin(), simd_be_array_vec.end()), + simd_be_array_in); + + using StrongSIMD = Botan::Strong; + const auto simd_le_strong = Botan::load_le(simd_le_in); + const auto simd_be_strong = Botan::load_be(simd_be_in); + + result.test_bin_eq( + "roundtrip SIMD strong little-endian", Botan::store_le>(simd_le_strong), simd_le_in); + result.test_bin_eq( + "roundtrip SIMD strong big-endian", Botan::store_be>(simd_be_strong), simd_be_in); + + return {result}; + } + + private: + static void BOTAN_FN_ISA_SIMD_2X64 + test_eq(Test::Result& result, const std::string& op, const Botan::SIMD_2x64& simd, uint64_t exp0, uint64_t exp1) { + uint8_t arr_be[16 + 15]; + uint8_t arr_be2[16 + 15]; + uint8_t arr_le[16 + 15]; + uint8_t arr_le2[16 + 15]; + + for(size_t misalignment = 0; misalignment != 16; ++misalignment) { + uint8_t* mem_be = arr_be + misalignment; + uint8_t* mem_be2 = arr_be2 + misalignment; + uint8_t* mem_le = arr_le + misalignment; + uint8_t* mem_le2 = arr_le2 + misalignment; + + simd.store_be(mem_be); + + result.test_u64_eq( + "SIMD_2x64 " + op + " elem0 BE", + Botan::make_uint64( + mem_be[0], mem_be[1], mem_be[2], mem_be[3], mem_be[4], mem_be[5], mem_be[6], mem_be[7]), + exp0); + result.test_u64_eq( + "SIMD_2x64 " + op + " elem1 BE", + Botan::make_uint64( + mem_be[8], mem_be[9], mem_be[10], mem_be[11], mem_be[12], mem_be[13], mem_be[14], mem_be[15]), + exp1); + + // Check load_be+store_be results in same value + const Botan::SIMD_2x64 reloaded_be = Botan::SIMD_2x64::load_be(mem_be); + reloaded_be.store_be(mem_be2); + result.test_bin_eq("SIMD_2x64 load_be", {mem_be, 16}, {mem_be2, 16}); + + simd.store_le(mem_le); + + result.test_u64_eq( + "SIMD_2x64 " + op + " elem0 LE", + Botan::make_uint64( + mem_le[7], mem_le[6], mem_le[5], mem_le[4], mem_le[3], mem_le[2], mem_le[1], mem_le[0]), + exp0); + result.test_u64_eq( + "SIMD_2x64 " + op + " elem1 LE", + Botan::make_uint64( + mem_le[15], mem_le[14], mem_le[13], mem_le[12], mem_le[11], mem_le[10], mem_le[9], mem_le[8]), + exp1); + + // Check load_le+store_le results in same value + const Botan::SIMD_2x64 reloaded_le = Botan::SIMD_2x64::load_le(mem_le); + reloaded_le.store_le(mem_le2); + result.test_bin_eq("SIMD_2x64 load_le", {mem_le, 16}, {mem_le2, 16}); } } }; -BOTAN_REGISTER_TEST("utils", "simd_32", SIMD_32_Tests); +BOTAN_REGISTER_TEST("utils", "simd_2x64", SIMD_2X64_Tests); #endif +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_siv.cpp botan3-3.12.0+dfsg/src/tests/test_siv.cpp --- botan3-3.7.1+dfsg/src/tests/test_siv.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_siv.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #if defined(BOTAN_HAS_AEAD_SIV) #include + #include #include #endif @@ -50,7 +51,7 @@ siv->start(nonce); siv->finish(buf, 0); - result.test_eq("SIV ciphertext", buf, expected); + result.test_bin_eq("SIV ciphertext", buf, expected); return result; } diff -Nru botan3-3.7.1+dfsg/src/tests/test_sm2.cpp botan3-3.12.0+dfsg/src/tests/test_sm2.cpp --- botan3-3.7.1+dfsg/src/tests/test_sm2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_sm2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -4,20 +4,22 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include "test_rng.h" #include "tests.h" #if defined(BOTAN_HAS_SM2) #include "test_pubkey.h" + #include "test_rng.h" + #include + #include #include #endif namespace Botan_Tests { -#if defined(BOTAN_HAS_SM2) - namespace { +#if defined(BOTAN_HAS_SM2) + std::unique_ptr load_sm2_private_key(const VarMap& vars) { // group params const BigInt p = vars.get_req_bn("P"); @@ -29,7 +31,7 @@ const BigInt x = vars.get_req_bn("x"); const Botan::OID oid = Botan::OID(vars.get_req_str("Oid")); - Botan::EC_Group domain(oid, p, a, b, xG, yG, order); + const Botan::EC_Group domain(oid, p, a, b, xG, yG, order); Botan::Null_RNG null_rng; return std::make_unique(null_rng, domain, x); @@ -41,7 +43,7 @@ PK_Signature_Generation_Test( "SM2", "pubkey/sm2_sig.vec", "P,A,B,xG,yG,Order,Oid,Ident,Msg,x,Nonce,Signature", "Hash") {} - bool skip_this_test(const std::string&, const VarMap&) override { + bool skip_this_test(const std::string& /*header*/, const VarMap& /*vars*/) override { return !Botan::EC_Group::supports_application_specific_group(); } @@ -68,7 +70,7 @@ PK_Encryption_Decryption_Test( "SM2", "pubkey/sm2_enc.vec", "P,A,B,xG,yG,Order,Oid,Msg,x,Nonce,Ciphertext", "Hash") {} - bool skip_this_test(const std::string&, const VarMap&) override { + bool skip_this_test(const std::string& /*header*/, const VarMap& /*vars*/) override { return !Botan::EC_Group::supports_application_specific_group(); } @@ -85,8 +87,6 @@ } }; -} // namespace - BOTAN_REGISTER_TEST("pubkey", "sm2_enc", SM2_Encryption_KAT_Tests); class SM2_Keygen_Tests final : public PK_Key_Generation_Test { @@ -106,6 +106,34 @@ BOTAN_REGISTER_TEST("pubkey", "sm2_keygen", SM2_Keygen_Tests); +class SM2_Invalid_Ciphertexts : public Text_Based_Test { + public: + SM2_Invalid_Ciphertexts() : Text_Based_Test("pubkey/sm2_invalid.vec", "Key,Ctext") {} + + bool clear_between_callbacks() const override { return false; } + + Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override { + Test::Result result("SM2 invalid ciphertext"); + + const auto key = vars.get_req_bin("Key"); + const auto ctext = vars.get_req_bin("Ctext"); + + const auto group = Botan::EC_Group::from_name("sm2p256v1"); + const auto pkey = Botan::SM2_PrivateKey(group, Botan::EC_Scalar::deserialize(group, key).value()); + + Botan::PK_Decryptor_EME dec(pkey, rng(), "SM3"); + + result.test_throws("Decryption should fail for invalid ciphertext", + [&] { dec.decrypt(ctext); }); + + return result; + } +}; + +BOTAN_REGISTER_TEST("pubkey", "sm2_invalid_ctext", SM2_Invalid_Ciphertexts); + #endif +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_sodium.cpp botan3-3.12.0+dfsg/src/tests/test_sodium.cpp --- botan3-3.7.1+dfsg/src/tests/test_sodium.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_sodium.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,11 +7,15 @@ #include "tests.h" #if defined(BOTAN_HAS_SODIUM_API) + #include #include + #include #endif namespace Botan_Tests { +namespace { + #if defined(BOTAN_HAS_SODIUM_API) class Sodium_API_Tests : public Test { @@ -63,37 +67,37 @@ static Test::Result sodium_utils() { Test::Result result("sodium math utils"); - result.confirm("sodium_is_zero", Botan::Sodium::sodium_is_zero(nullptr, 0) == 1); + result.test_is_true("sodium_is_zero", Botan::Sodium::sodium_is_zero(nullptr, 0) == 1); std::vector a(5); - result.confirm("sodium_is_zero", Botan::Sodium::sodium_is_zero(a.data(), a.size()) == 1); + result.test_is_true("sodium_is_zero", Botan::Sodium::sodium_is_zero(a.data(), a.size()) == 1); Botan::Sodium::sodium_increment(a.data(), a.size()); - result.test_eq("sodium_increment", a, "0100000000"); - result.confirm("sodium_is_zero", Botan::Sodium::sodium_is_zero(a.data(), a.size()) == 0); + result.test_bin_eq("sodium_increment", a, "0100000000"); + result.test_is_true("sodium_is_zero", Botan::Sodium::sodium_is_zero(a.data(), a.size()) == 0); std::memset(a.data(), 0xFF, a.size()); Botan::Sodium::sodium_increment(a.data(), a.size()); - result.test_eq("sodium_increment", a, "0000000000"); + result.test_bin_eq("sodium_increment", a, "0000000000"); Botan::Sodium::sodium_increment(a.data(), a.size()); - result.test_eq("sodium_increment", a, "0100000000"); + result.test_bin_eq("sodium_increment", a, "0100000000"); - result.confirm("sodium_compare", Botan::Sodium::sodium_compare(a.data(), a.data(), a.size()) == 0); - result.confirm("sodium_memcmp", Botan::Sodium::sodium_memcmp(a.data(), a.data(), a.size()) == 0); + result.test_is_true("sodium_compare", Botan::Sodium::sodium_compare(a.data(), a.data(), a.size()) == 0); + result.test_is_true("sodium_memcmp", Botan::Sodium::sodium_memcmp(a.data(), a.data(), a.size()) == 0); std::vector b(5, 0x10); - result.confirm("sodium_compare a output(64); Botan::Sodium::crypto_hash_sha512(output.data(), nullptr, 0); - result.test_eq( + result.test_bin_eq( "expected output", output, "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"); @@ -131,7 +135,8 @@ std::vector output(32); Botan::Sodium::crypto_hash_sha256(output.data(), nullptr, 0); - result.test_eq("expected output", output, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + result.test_bin_eq( + "expected output", output, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); return result; } @@ -141,15 +146,17 @@ const std::vector seed(32); - std::vector pk1(32), sk1(32); + std::vector pk1(32); + std::vector sk1(32); result.test_rc_ok("seed_keypair", Botan::Sodium::crypto_box_seed_keypair(pk1.data(), sk1.data(), seed.data())); - result.test_eq("pk1", pk1, "5BF55C73B82EBE22BE80F3430667AF570FAE2556A6415E6B30D4065300AA947D"); - result.test_eq("sk1", sk1, "5046ADC1DBA838867B2BBBFDD0C3423E58B57970B5267A90F57960924A87F196"); + result.test_bin_eq("pk1", pk1, "5BF55C73B82EBE22BE80F3430667AF570FAE2556A6415E6B30D4065300AA947D"); + result.test_bin_eq("sk1", sk1, "5046ADC1DBA838867B2BBBFDD0C3423E58B57970B5267A90F57960924A87F196"); - std::vector pk2(32), sk2(32); + std::vector pk2(32); + std::vector sk2(32); result.test_rc_ok("seed_keypair", Botan::Sodium::crypto_box_seed_keypair(pk2.data(), sk2.data(), sk1.data())); - result.test_eq("pk2", pk2, "E0CFC9C6B2FE5BF85F48671691225C03D763F2305206FE3D3B0ED7B76153684A"); - result.test_eq("sk2", sk2, "58E2E4C71F138FBC97F9341735B4581746761F9A104540007FE12CFC4D9FDA15"); + result.test_bin_eq("pk2", pk2, "E0CFC9C6B2FE5BF85F48671691225C03D763F2305206FE3D3B0ED7B76153684A"); + result.test_bin_eq("sk2", sk2, "58E2E4C71F138FBC97F9341735B4581746761F9A104540007FE12CFC4D9FDA15"); const std::vector ptext(15); std::vector ctext(ptext.size() + 16); @@ -159,14 +166,14 @@ Botan::Sodium::crypto_box_easy( ctext.data(), ptext.data(), ptext.size(), nonce.data(), pk2.data(), sk1.data())); - result.test_eq("ctext1", ctext, "11D78D4C32C5674390C0425D8BBB5928AFE7F767E2A7E4427E1A1362F1FD92"); + result.test_bin_eq("ctext1", ctext, "11D78D4C32C5674390C0425D8BBB5928AFE7F767E2A7E4427E1A1362F1FD92"); result.test_rc_ok("crypto_box_easy", Botan::Sodium::crypto_box_easy( ctext.data(), ptext.data(), ptext.size(), nonce.data(), pk1.data(), sk2.data())); // same shared secret, same nonce, same data -> same ciphertext - result.test_eq("ctext2", ctext, "11D78D4C32C5674390C0425D8BBB5928AFE7F767E2A7E4427E1A1362F1FD92"); + result.test_bin_eq("ctext2", ctext, "11D78D4C32C5674390C0425D8BBB5928AFE7F767E2A7E4427E1A1362F1FD92"); std::vector recovered(15); @@ -174,13 +181,13 @@ Botan::Sodium::crypto_box_open_easy( recovered.data(), ctext.data(), ctext.size(), nonce.data(), pk1.data(), sk2.data())); - result.test_eq("recover1", recovered, ptext); + result.test_bin_eq("recover1", recovered, ptext); result.test_rc_ok("crypto_box_open_easy", Botan::Sodium::crypto_box_open_easy( recovered.data(), ctext.data(), ctext.size(), nonce.data(), pk2.data(), sk1.data())); - result.test_eq("recover1", recovered, ptext); + result.test_bin_eq("recover1", recovered, ptext); return result; } @@ -194,8 +201,8 @@ const std::vector nonce = Botan::hex_decode("0000000000000000"); const std::vector in = Botan::hex_decode("000000000000000000000000000000"); - result.test_eq("key len", Botan::Sodium::crypto_aead_chacha20poly1305_keybytes(), key.size()); - result.test_eq("nonce len", Botan::Sodium::crypto_aead_chacha20poly1305_npubbytes(), nonce.size()); + result.test_sz_eq("key len", Botan::Sodium::crypto_aead_chacha20poly1305_keybytes(), key.size()); + result.test_sz_eq("nonce len", Botan::Sodium::crypto_aead_chacha20poly1305_npubbytes(), nonce.size()); std::vector ctext(in.size()); std::vector mac(16); @@ -211,9 +218,9 @@ nonce.data(), key.data()); - result.test_eq("maclen", size_t(maclen), 16); - result.test_eq("mac", mac, "09998877ABA156DDC68F8344098F68B9"); - result.test_eq("ctext", ctext, "9F07E7BE5551387A98BA977C732D08"); + result.test_sz_eq("maclen", size_t(maclen), 16); + result.test_bin_eq("mac", mac, "09998877ABA156DDC68F8344098F68B9"); + result.test_bin_eq("ctext", ctext, "9F07E7BE5551387A98BA977C732D08"); std::vector recovered(ctext.size()); result.test_rc_ok("decrypt", @@ -227,7 +234,7 @@ nonce.data(), key.data())); - result.test_eq("plaintext", recovered, in); + result.test_bin_eq("plaintext", recovered, in); mac[0] ^= 1; result.test_rc_fail("decrypt", @@ -243,7 +250,7 @@ key.data())); ctext.resize(in.size() + mac.size()); - unsigned long long ctext_len; + unsigned long long ctext_len = 0; result.test_rc_ok("encrypt", Botan::Sodium::crypto_aead_chacha20poly1305_encrypt(ctext.data(), &ctext_len, @@ -255,8 +262,8 @@ nonce.data(), key.data())); - result.test_eq("ctext_len", size_t(ctext_len), ctext.size()); - result.test_eq("ctext", ctext, "9F07E7BE5551387A98BA977C732D0809998877ABA156DDC68F8344098F68B9"); + result.test_sz_eq("ctext_len", size_t(ctext_len), ctext.size()); + result.test_bin_eq("ctext", ctext, "9F07E7BE5551387A98BA977C732D0809998877ABA156DDC68F8344098F68B9"); unsigned long long recovered_len = 0; result.test_rc_ok("decrypt", @@ -270,7 +277,7 @@ nonce.data(), key.data())); - result.test_eq("recovered", recovered, in); + result.test_bin_eq("recovered", recovered, in); return result; } @@ -284,8 +291,8 @@ const std::vector nonce = Botan::hex_decode("000000000000000000000000"); const std::vector in = Botan::hex_decode("000000000000000000000000000000"); - result.test_eq("key len", Botan::Sodium::crypto_aead_chacha20poly1305_ietf_keybytes(), key.size()); - result.test_eq("nonce len", Botan::Sodium::crypto_aead_chacha20poly1305_ietf_npubbytes(), nonce.size()); + result.test_sz_eq("key len", Botan::Sodium::crypto_aead_chacha20poly1305_ietf_keybytes(), key.size()); + result.test_sz_eq("nonce len", Botan::Sodium::crypto_aead_chacha20poly1305_ietf_npubbytes(), nonce.size()); std::vector ctext(in.size()); std::vector mac(16); @@ -301,9 +308,9 @@ nonce.data(), key.data()); - result.test_eq("maclen", size_t(maclen), 16); - result.test_eq("mac", mac, "3679F1FB9843FD81E26D962888296954"); - result.test_eq("ctext", ctext, "9F07E7BE5551387A98BA977C732D08"); + result.test_sz_eq("maclen", size_t(maclen), 16); + result.test_bin_eq("mac", mac, "3679F1FB9843FD81E26D962888296954"); + result.test_bin_eq("ctext", ctext, "9F07E7BE5551387A98BA977C732D08"); std::vector recovered(ctext.size()); result.test_rc_ok("decrypt", @@ -317,7 +324,7 @@ nonce.data(), key.data())); - result.test_eq("plaintext", recovered, in); + result.test_bin_eq("plaintext", recovered, in); mac[0] ^= 1; result.test_rc_fail("decrypt", @@ -333,7 +340,7 @@ key.data())); ctext.resize(in.size() + mac.size()); - unsigned long long ctext_len; + unsigned long long ctext_len = 0; result.test_rc_ok("encrypt", Botan::Sodium::crypto_aead_chacha20poly1305_ietf_encrypt(ctext.data(), &ctext_len, @@ -345,8 +352,8 @@ nonce.data(), key.data())); - result.test_eq("ctext_len", size_t(ctext_len), ctext.size()); - result.test_eq("ctext", ctext, "9F07E7BE5551387A98BA977C732D083679F1FB9843FD81E26D962888296954"); + result.test_sz_eq("ctext_len", size_t(ctext_len), ctext.size()); + result.test_bin_eq("ctext", ctext, "9F07E7BE5551387A98BA977C732D083679F1FB9843FD81E26D962888296954"); unsigned long long recovered_len = 0; result.test_rc_ok("decrypt", @@ -360,7 +367,7 @@ nonce.data(), key.data())); - result.test_eq("recovered", recovered, in); + result.test_bin_eq("recovered", recovered, in); return result; } @@ -374,8 +381,8 @@ const std::vector nonce = Botan::hex_decode("000000000000000000000000000000000000000000000000"); const std::vector in = Botan::hex_decode("000000000000000000000000000000"); - result.test_eq("key len", Botan::Sodium::crypto_aead_xchacha20poly1305_ietf_keybytes(), key.size()); - result.test_eq("nonce len", Botan::Sodium::crypto_aead_xchacha20poly1305_ietf_npubbytes(), nonce.size()); + result.test_sz_eq("key len", Botan::Sodium::crypto_aead_xchacha20poly1305_ietf_keybytes(), key.size()); + result.test_sz_eq("nonce len", Botan::Sodium::crypto_aead_xchacha20poly1305_ietf_npubbytes(), nonce.size()); std::vector ctext(in.size()); std::vector mac(16); @@ -391,9 +398,9 @@ nonce.data(), key.data()); - result.test_eq("maclen", size_t(maclen), 16); - result.test_eq("mac", mac, "b2f7033812ac9ebd3745e2c99c7bbfeb"); - result.test_eq("ctext", ctext, "789e9689e5208d7fd9e1f3c5b5341f"); + result.test_sz_eq("maclen", size_t(maclen), 16); + result.test_bin_eq("mac", mac, "b2f7033812ac9ebd3745e2c99c7bbfeb"); + result.test_bin_eq("ctext", ctext, "789e9689e5208d7fd9e1f3c5b5341f"); std::vector recovered(ctext.size()); result.test_rc_ok("decrypt", @@ -407,7 +414,7 @@ nonce.data(), key.data())); - result.test_eq("plaintext", recovered, in); + result.test_bin_eq("plaintext", recovered, in); mac[0] ^= 1; result.test_rc_fail("decrypt", @@ -423,7 +430,7 @@ key.data())); ctext.resize(in.size() + mac.size()); - unsigned long long ctext_len; + unsigned long long ctext_len = 0; result.test_rc_ok("encrypt", Botan::Sodium::crypto_aead_xchacha20poly1305_ietf_encrypt(ctext.data(), &ctext_len, @@ -435,8 +442,8 @@ nonce.data(), key.data())); - result.test_eq("ctext_len", size_t(ctext_len), ctext.size()); - result.test_eq("ctext", ctext, "789e9689e5208d7fd9e1f3c5b5341fb2f7033812ac9ebd3745e2c99c7bbfeb"); + result.test_sz_eq("ctext_len", size_t(ctext_len), ctext.size()); + result.test_bin_eq("ctext", ctext, "789e9689e5208d7fd9e1f3c5b5341fb2f7033812ac9ebd3745e2c99c7bbfeb"); unsigned long long recovered_len = 0; result.test_rc_ok("decrypt", @@ -450,7 +457,7 @@ nonce.data(), key.data())); - result.test_eq("recovered", recovered, in); + result.test_bin_eq("recovered", recovered, in); return result; } @@ -462,12 +469,12 @@ Botan::hex_decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); const std::vector in = Botan::hex_decode("616263"); - result.test_eq("key_size", key.size(), Botan::Sodium::crypto_auth_hmacsha512_keybytes()); + result.test_sz_eq("key_size", key.size(), Botan::Sodium::crypto_auth_hmacsha512_keybytes()); std::vector mac(64); Botan::Sodium::crypto_auth_hmacsha512(mac.data(), in.data(), in.size(), key.data()); - result.test_eq( + result.test_bin_eq( "expected mac", mac, "69D4A21E226BF0D348CB9A847C01CF24E93E8AC30D7C951704B936F82F795A624B470E23ABD33AC8700E797F0F2A499B932BAC7D283BBBB37D8FECF70D5E08A7"); @@ -494,7 +501,7 @@ std::vector mac(32); Botan::Sodium::crypto_auth_hmacsha512256(mac.data(), in.data(), in.size(), key.data()); - result.test_eq("expected mac", mac, "69D4A21E226BF0D348CB9A847C01CF24E93E8AC30D7C951704B936F82F795A62"); + result.test_bin_eq("expected mac", mac, "69D4A21E226BF0D348CB9A847C01CF24E93E8AC30D7C951704B936F82F795A62"); result.test_rc_ok( "verify", Botan::Sodium::crypto_auth_hmacsha512256_verify(mac.data(), in.data(), in.size(), key.data())); @@ -518,7 +525,7 @@ std::vector mac(32); Botan::Sodium::crypto_auth_hmacsha256(mac.data(), in.data(), in.size(), key.data()); - result.test_eq("expected mac", mac, "A21B1F5D4CF4F73A4DD939750F7A066A7F98CC131CB16A6692759021CFAB8181"); + result.test_bin_eq("expected mac", mac, "A21B1F5D4CF4F73A4DD939750F7A066A7F98CC131CB16A6692759021CFAB8181"); result.test_rc_ok("verify", Botan::Sodium::crypto_auth_hmacsha256_verify(mac.data(), in.data(), in.size(), key.data())); @@ -543,7 +550,7 @@ result.test_rc_ok("poly1305", Botan::Sodium::crypto_onetimeauth_poly1305(mac.data(), in.data(), in.size(), key.data())); - result.test_eq("expected mac", mac, "12154512151545121515451215154584"); + result.test_bin_eq("expected mac", mac, "12154512151545121515451215154584"); result.test_rc_ok( "poly1305 verify", @@ -567,7 +574,7 @@ std::vector mac(8); Botan::Sodium::crypto_shorthash_siphash24(mac.data(), in.data(), in.size(), key.data()); - result.test_eq("expected mac", mac, "E545BE4961CA29A1"); + result.test_bin_eq("expected mac", mac, "E545BE4961CA29A1"); return result; } @@ -584,14 +591,14 @@ Botan::Sodium::crypto_secretbox_xsalsa20poly1305( ctext.data(), ptext.data(), ptext.size(), nonce.data(), key.data())); - result.test_eq("ctext", ctext, "0000000000000000000000000000000042E45EB764A1B706D4776A849BC2526BC6"); + result.test_bin_eq("ctext", ctext, "0000000000000000000000000000000042E45EB764A1B706D4776A849BC2526BC6"); std::vector recovered(33); result.test_rc_ok("decrypt", Botan::Sodium::crypto_secretbox_xsalsa20poly1305_open( recovered.data(), ctext.data(), ctext.size(), nonce.data(), key.data())); - result.test_eq("decrypted", recovered, ptext); + result.test_bin_eq("decrypted", recovered, ptext); return result; } @@ -609,8 +616,8 @@ Botan::Sodium::crypto_secretbox_detached( ctext.data(), mac.data(), ptext.data(), ptext.size(), nonce.data(), key.data())); - result.test_eq("ctext", ctext, "C63EBBFFFE85CE2CEBDEF7DC42F494576D05BDD7B929EBB045F2A793F740277D05"); - result.test_eq("mac", mac, "0D6681DCED740667C699F0AC71BFD1BD"); + result.test_bin_eq("ctext", ctext, "C63EBBFFFE85CE2CEBDEF7DC42F494576D05BDD7B929EBB045F2A793F740277D05"); + result.test_bin_eq("mac", mac, "0D6681DCED740667C699F0AC71BFD1BD"); std::vector recovered(ctext.size()); @@ -618,7 +625,7 @@ Botan::Sodium::crypto_secretbox_open_detached( recovered.data(), ctext.data(), mac.data(), ctext.size(), nonce.data(), key.data())); - result.test_eq("recovered", recovered, ptext); + result.test_bin_eq("recovered", recovered, ptext); return result; } @@ -627,13 +634,14 @@ Test::Result result("crypto_sign_ed25519"); const std::vector seed(32); - std::vector pk(32), sk(64); + std::vector pk(32); + std::vector sk(64); result.test_rc_ok("seed_keypair", Botan::Sodium::crypto_sign_ed25519_seed_keypair(pk.data(), sk.data(), seed.data())); - result.test_eq("pk", pk, "3B6A27BCCEB6A42D62A3A8D02A6F0D73653215771DE243A63AC048A18B59DA29"); - result.test_eq( + result.test_bin_eq("pk", pk, "3B6A27BCCEB6A42D62A3A8D02A6F0D73653215771DE243A63AC048A18B59DA29"); + result.test_bin_eq( "sk", sk, "00000000000000000000000000000000000000000000000000000000000000003B6A27BCCEB6A42D62A3A8D02A6F0D73653215771DE243A63AC048A18B59DA29"); @@ -644,9 +652,9 @@ result.test_rc_ok( "sign_detached", Botan::Sodium::crypto_sign_ed25519_detached(sig.data(), &sig_len, msg.data(), msg.size(), sk.data())); - result.confirm("sig len", sig_len == 64); + result.test_is_true("sig len", sig_len == 64); - result.test_eq( + result.test_bin_eq( "sig", sig, "2A26779BA6CBB5E54292257F725AF112B273C38728329682D99ED81BA6D7670350AE4CC53C5456FA437128D19298A5D949AB46E3D41AB3DBCFB0B35C895E9304"); @@ -675,12 +683,12 @@ std::vector output(32); Botan::Sodium::crypto_stream_salsa20(output.data(), output.size(), nonce.data(), key.data()); - result.test_eq("stream", output, expected); + result.test_bin_eq("stream", output, expected); std::vector xor_output(32); Botan::Sodium::crypto_stream_salsa20_xor( xor_output.data(), output.data(), output.size(), nonce.data(), key.data()); - result.test_eq("stream", xor_output, std::vector(32)); // all zeros + result.test_bin_eq("stream", xor_output, std::vector(32)); // all zeros return result; } @@ -696,12 +704,12 @@ std::vector output(32); Botan::Sodium::crypto_stream_xsalsa20(output.data(), output.size(), nonce.data(), key.data()); - result.test_eq("stream", output, expected); + result.test_bin_eq("stream", output, expected); std::vector xor_output(32); Botan::Sodium::crypto_stream_xsalsa20_xor( xor_output.data(), output.data(), output.size(), nonce.data(), key.data()); - result.test_eq("stream", xor_output, std::vector(32)); // all zeros + result.test_bin_eq("stream", xor_output, std::vector(32)); // all zeros return result; } @@ -717,12 +725,12 @@ std::vector output(32); Botan::Sodium::crypto_stream_chacha20(output.data(), output.size(), nonce.data(), key.data()); - result.test_eq("stream", output, expected); + result.test_bin_eq("stream", output, expected); std::vector xor_output(32); Botan::Sodium::crypto_stream_chacha20_xor( xor_output.data(), output.data(), output.size(), nonce.data(), key.data()); - result.test_eq("stream", xor_output, std::vector(32)); // all zeros + result.test_bin_eq("stream", xor_output, std::vector(32)); // all zeros return result; } @@ -738,12 +746,12 @@ std::vector output(32); Botan::Sodium::crypto_stream_chacha20_ietf(output.data(), output.size(), nonce.data(), key.data()); - result.test_eq("stream", output, expected); + result.test_bin_eq("stream", output, expected); std::vector xor_output(32); Botan::Sodium::crypto_stream_chacha20_ietf_xor( xor_output.data(), output.data(), output.size(), nonce.data(), key.data()); - result.test_eq("stream", xor_output, std::vector(32)); // all zeros + result.test_bin_eq("stream", xor_output, std::vector(32)); // all zeros return result; } @@ -759,12 +767,12 @@ std::vector output(32); Botan::Sodium::crypto_stream_xchacha20(output.data(), output.size(), nonce.data(), key.data()); - result.test_eq("stream", output, expected); + result.test_bin_eq("stream", output, expected); std::vector xor_output(32); Botan::Sodium::crypto_stream_xchacha20_xor( xor_output.data(), output.data(), output.size(), nonce.data(), key.data()); - result.test_eq("stream", xor_output, std::vector(32)); // all zeros + result.test_bin_eq("stream", xor_output, std::vector(32)); // all zeros return result; } @@ -774,4 +782,6 @@ #endif +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_sphincsplus.cpp botan3-3.12.0+dfsg/src/tests/test_sphincsplus.cpp --- botan3-3.7.1+dfsg/src/tests/test_sphincsplus.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_sphincsplus.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -5,34 +5,33 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include "test_rng.h" #include "tests.h" #if defined(BOTAN_HAS_SPHINCS_PLUS_COMMON) && defined(BOTAN_HAS_AES) #include #include - #include #include #include #include #include - #include - #include #include "test_pubkey.h" + #include "test_rng.h" namespace Botan_Tests { +namespace { + /** * Test all implemented SLH-DSA instances using the data of the KAT files. */ class SPHINCS_Plus_Test_Base : public Text_Based_Test { public: - SPHINCS_Plus_Test_Base(std::string_view kat_path) : + explicit SPHINCS_Plus_Test_Base(std::string_view kat_path) : Text_Based_Test(std::string(kat_path), "SphincsParameterSet,seed,pk,sk,msg,HashSigRand", "HashSigDet") {} - bool skip_this_test(const std::string&, const VarMap& vars) override { + bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override { auto params = Botan::Sphincs_Parameters::create(vars.get_req_str("SphincsParameterSet")); if(!params.is_available()) { @@ -66,7 +65,7 @@ BOTAN_ASSERT_UNREACHABLE(); } - Test::Result run_one_test(const std::string&, const VarMap& vars) final { + Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) final { auto params = Botan::Sphincs_Parameters::create(vars.get_req_str("SphincsParameterSet")); Test::Result result(params.is_slh_dsa() ? "SLH-DSA" : "SPHINCS+"); @@ -110,26 +109,26 @@ // push the entropy used for signing twice, as we want to perform two // signing operations const auto entropy_for_signing = kat_rng.random_vec>(1 * params.n()); - // Depending on the configuation, upto 2 signatures with 'Randomized' are created + // Depending on the configuration, up to 2 signatures with 'Randomized' are created fixed_rng.add_entropy(entropy_for_signing); fixed_rng.add_entropy(entropy_for_signing); // Generate Keypair - Botan::SphincsPlus_PrivateKey priv_key(fixed_rng, params); + const Botan::SphincsPlus_PrivateKey priv_key(fixed_rng, params); - result.test_is_eq("public key bits", priv_key.public_key_bits(), pk_ref); - result.test_is_eq("private key bits", unlock(priv_key.private_key_bits()), sk_ref); + result.test_bin_eq("public key bits", priv_key.public_key_bits(), pk_ref); + result.test_bin_eq("private key bits", priv_key.private_key_bits(), sk_ref); // Signature roundtrip (Randomized mode) auto signer_rand = Botan::PK_Signer(priv_key, fixed_rng, "Randomized"); auto signature_rand = signer_rand.sign_message(msg_ref.data(), msg_ref.size(), fixed_rng); - result.test_is_eq("signature creation randomized", unlock(hash->process(signature_rand)), sig_rand_hash); + result.test_bin_eq("signature creation randomized", hash->process(signature_rand), sig_rand_hash); Botan::PK_Verifier verifier(*priv_key.public_key(), params.algorithm_identifier()); - bool verify_success = + const bool verify_success = verifier.verify_message(msg_ref.data(), msg_ref.size(), signature_rand.data(), signature_rand.size()); - result.confirm("verification of valid randomized signature", verify_success); + result.test_is_true("verification of valid randomized signature", verify_success); // Signature roundtrip (Deterministic mode) - not available for all parameter sets // For testing time reasons we only test this for some tests if not --run-long-tests @@ -138,11 +137,11 @@ auto signer_det = Botan::PK_Signer(priv_key, fixed_rng, "Deterministic"); auto signature_det = signer_det.sign_message(msg_ref.data(), msg_ref.size(), fixed_rng); - result.test_is_eq("signature creation deterministic", unlock(hash->process(signature_det)), *sig_det_hash); + result.test_bin_eq("signature creation deterministic", hash->process(signature_det), *sig_det_hash); auto verify_success_det = verifier.verify_message(msg_ref.data(), msg_ref.size(), signature_det.data(), signature_det.size()); - result.confirm("verification of valid deterministic signature", verify_success_det); + result.test_is_true("verification of valid deterministic signature", verify_success_det); } // Verification with generated Keypair @@ -153,36 +152,36 @@ if(params.parameter_set() == Botan::Sphincs_Parameter_Set::Sphincs128Fast || params.parameter_set() == Botan::Sphincs_Parameter_Set::SLHDSA128Fast) { // Deserialization of Keypair from test vector - Botan::SphincsPlus_PrivateKey deserialized_priv_key(sk_ref, params); - Botan::SphincsPlus_PublicKey deserialized_pub_key(pk_ref, params); + const Botan::SphincsPlus_PrivateKey deserialized_priv_key(sk_ref, params); + const Botan::SphincsPlus_PublicKey deserialized_pub_key(pk_ref, params); // Signature with deserialized Keypair auto deserialized_signer = Botan::PK_Signer(deserialized_priv_key, fixed_rng, "Randomized"); auto deserialized_signature = deserialized_signer.sign_message(msg_ref.data(), msg_ref.size(), fixed_rng); - result.test_is_eq("signature creation after deserialization", - unlock(hash->process(deserialized_signature)), - sig_rand_hash); + result.test_bin_eq( + "signature creation after deserialization", hash->process(deserialized_signature), sig_rand_hash); // Verification with deserialized Keypair Botan::PK_Verifier deserialized_verifier(deserialized_pub_key, params.algorithm_identifier()); - bool verify_success_deserialized = deserialized_verifier.verify_message( + const bool verify_success_deserialized = deserialized_verifier.verify_message( msg_ref.data(), msg_ref.size(), signature_rand.data(), signature_rand.size()); - result.confirm("verification of valid signature after deserialization", verify_success_deserialized); + result.test_is_true("verification of valid signature after deserialization", verify_success_deserialized); // Verification of invalid signature auto broken_sig = Test::mutate_vec(deserialized_signature, this->rng()); - bool verify_fail = deserialized_verifier.verify_message( + const 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); + result.test_is_true("verification of invalid signature", !verify_fail); - bool verify_success_after_fail = deserialized_verifier.verify_message( + const bool verify_success_after_fail = deserialized_verifier.verify_message( msg_ref.data(), msg_ref.size(), signature_rand.data(), signature_rand.size()); - result.confirm("verification of valid signature after broken signature", verify_success_after_fail); + result.test_is_true("verification of valid signature after broken signature", verify_success_after_fail); } // Misc - result.confirm("parameter serialization works", params.to_string() == vars.get_req_str("SphincsParameterSet")); + result.test_is_true("parameter serialization works", + params.to_string() == vars.get_req_str("SphincsParameterSet")); return result; } @@ -219,10 +218,11 @@ }; const auto& tested_params = Test::run_long_tests() ? all_params : short_test_params; std::vector available_params; - std::copy_if(tested_params.begin(), - tested_params.end(), - std::back_inserter(available_params), - [](const std::string& param) { return Botan::Sphincs_Parameters::create(param).is_available(); }); + for(const auto& param : tested_params) { + if(Botan::Sphincs_Parameters::create(param).is_available()) { + available_params.push_back(param); + } + } return available_params; } @@ -262,7 +262,7 @@ return vars.has_key("Nonce") ? "Randomized" : "Deterministic"; } - bool skip_this_test(const std::string&, const VarMap& vars) override { + bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override { return !Botan::Sphincs_Parameters::create(vars.get_req_str("Instance")).is_available(); } }; @@ -282,9 +282,9 @@ return std::make_unique(pubkey, Botan::Sphincs_Parameters::create(instance)); } - std::string default_padding(const VarMap&) const override { return ""; } + std::string default_padding(const VarMap& /*vars*/) const override { return ""; } - bool skip_this_test(const std::string&, const VarMap& vars) override { + bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override { return !Botan::Sphincs_Parameters::create(vars.get_req_str("Instance")).is_available(); } }; @@ -295,6 +295,8 @@ BOTAN_REGISTER_TEST("pubkey", "slh_dsa_sign_generic", Generic_SlhDsa_Signature_Tests); BOTAN_REGISTER_TEST("pubkey", "slh_dsa_verify_generic", Generic_SlhDsa_Verification_Tests); +} // namespace + } // namespace Botan_Tests #endif // BOTAN_HAS_SPHINCS_PLUS diff -Nru botan3-3.7.1+dfsg/src/tests/test_sphincsplus_fors.cpp botan3-3.12.0+dfsg/src/tests/test_sphincsplus_fors.cpp --- botan3-3.7.1+dfsg/src/tests/test_sphincsplus_fors.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_sphincsplus_fors.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,10 +9,8 @@ #if defined(BOTAN_HAS_SPHINCS_PLUS_COMMON) - #include - #include - #include + #include #include #include #include @@ -21,12 +19,14 @@ namespace Botan_Tests { +namespace { + class SPHINCS_Plus_FORS_Test final : public Text_Based_Test { private: static Botan::Sphincs_Address read_address(std::span address_buffer) { BOTAN_ASSERT_NOMSG(address_buffer.size() == 32); - std::array adrs; + std::array adrs{}; for(size_t i = 0; i < 8; ++i) { adrs[i] = Botan::load_be(address_buffer.data(), i); } @@ -39,12 +39,12 @@ Text_Based_Test("pubkey/sphincsplus_fors.vec", "SphincsParameterSet,Address,SecretSeed,PublicSeed,PublicKey,Msg,HashSig") {} - bool skip_this_test(const std::string&, const VarMap& vars) override { + bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override { auto params = Botan::Sphincs_Parameters::create(vars.get_req_str("SphincsParameterSet")); return !params.is_available(); } - Test::Result run_one_test(const std::string&, const VarMap& vars) final { + Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) final { Test::Result result("SLH-DSA's FORS"); auto params = Botan::Sphincs_Parameters::create(vars.get_req_str("SphincsParameterSet")); @@ -72,20 +72,20 @@ } auto hashes = Botan::Sphincs_Hash_Functions::create(params, public_seed); - Botan::Sphincs_Address address = read_address(vars.get_req_bin("Address")); + const Botan::Sphincs_Address address = read_address(vars.get_req_bin("Address")); Botan::ForsSignature sig(params.fors_signature_bytes()); auto pk = Botan::fors_sign_and_pkgen(sig, hashed_message, secret_seed, address, params, *hashes); const auto pk_ref = Botan::SphincsTreeNode(vars.get_req_bin("PublicKey")); - result.test_is_eq("Derived public key", pk, pk_ref); + result.test_bin_eq("Derived public key", pk, pk_ref); const auto hashed_sig_ref = Botan::ForsSignature(vars.get_req_bin("HashSig")); - result.test_is_eq("Signature result", unlock(hash->process(sig)), hashed_sig_ref.get()); + result.test_bin_eq("Signature result", hash->process(sig), hashed_sig_ref.get()); auto pk_from_sig = Botan::fors_public_key_from_signature(hashed_message, sig, address, params, *hashes); - result.test_is_eq("Public key from signature", pk_from_sig, pk); + result.test_bin_eq("Public key from signature", pk_from_sig, pk); return result; } @@ -93,6 +93,8 @@ BOTAN_REGISTER_TEST("pubkey", "sphincsplus_fors", SPHINCS_Plus_FORS_Test); +} // namespace + } // namespace Botan_Tests #endif // BOTAN_HAS_SPHINCS_PLUS_COMMON diff -Nru botan3-3.7.1+dfsg/src/tests/test_sphincsplus_utils.cpp botan3-3.12.0+dfsg/src/tests/test_sphincsplus_utils.cpp --- botan3-3.7.1+dfsg/src/tests/test_sphincsplus_utils.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_sphincsplus_utils.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,7 +9,7 @@ #if defined(BOTAN_HAS_SPHINCS_PLUS_COMMON) && defined(BOTAN_HAS_SHA2_32) - #include + #include #include namespace Botan_Tests { @@ -26,10 +26,9 @@ return { CHECK("default address", [&](Test::Result& result) { - Botan::Sphincs_Address a({0, 0, 0, 0, 0, 0, 0, 0}); - result.test_is_eq("SHA-256(32*0x00)", - sha256(a), - Botan::hex_decode("66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925")); + const Botan::Sphincs_Address a({0, 0, 0, 0, 0, 0, 0, 0}); + result.test_bin_eq( + "SHA-256(32*0x00)", sha256(a), "66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925"); }), CHECK("set up an address", @@ -41,9 +40,8 @@ .set_chain_address(Botan::WotsChainIndex(67108864)) .set_hash_address(Botan::WotsHashIndex(42)); - result.test_is_eq("SHA-256(a1)", - sha256(a), - Botan::hex_decode("aecc0696fee5c4aa601779343d01090aae0d0a3b6cf118d3c7245d48dc0f3af9")); + result.test_bin_eq( + "SHA-256(a1)", sha256(a), "aecc0696fee5c4aa601779343d01090aae0d0a3b6cf118d3c7245d48dc0f3af9"); }), CHECK("set up another address", @@ -54,9 +52,8 @@ .set_keypair_address(Botan::TreeNodeIndex(131072)) .set_tree_height(Botan::TreeLayerIndex(67108864)) .set_tree_index(Botan::TreeNodeIndex(1073741824)); - result.test_is_eq("SHA-256(a2)", - sha256(a), - Botan::hex_decode("607fdc9d063168fbea64e4da2a255693314712d859062abb80cf7c78116ded2a")); + result.test_bin_eq( + "SHA-256(a2)", sha256(a), "607fdc9d063168fbea64e4da2a255693314712d859062abb80cf7c78116ded2a"); }), CHECK("copy subtree", @@ -72,12 +69,12 @@ Botan::Sphincs_Address subtree2({0, 0, 0, 0, 0, 0, 0, 0}); subtree2.copy_subtree_from(a); - result.test_is_eq("SHA-256(subtree1)", - sha256(subtree1), - Botan::hex_decode("f192c8f8e946aa16d16eafe88bd4eabcc88a305b69bb7c0bb49e65bd122bb973")); - result.test_is_eq("SHA-256(subtree2)", - sha256(subtree2), - Botan::hex_decode("f192c8f8e946aa16d16eafe88bd4eabcc88a305b69bb7c0bb49e65bd122bb973")); + result.test_bin_eq("SHA-256(subtree1)", + sha256(subtree1), + "f192c8f8e946aa16d16eafe88bd4eabcc88a305b69bb7c0bb49e65bd122bb973"); + result.test_bin_eq("SHA-256(subtree2)", + sha256(subtree2), + "f192c8f8e946aa16d16eafe88bd4eabcc88a305b69bb7c0bb49e65bd122bb973"); }), CHECK("copy keypair", @@ -93,12 +90,12 @@ Botan::Sphincs_Address keypair2({0, 0, 0, 0, 0, 0, 0, 0}); keypair2.copy_keypair_from(a); - result.test_is_eq("SHA-256(keypair1)", - sha256(keypair1), - Botan::hex_decode("1cdd4835a6057306678e7d8cb903c140aba1d4805a8a1f75b11f1129bb22d08c")); - result.test_is_eq("SHA-256(keypair2)", - sha256(keypair2), - Botan::hex_decode("1cdd4835a6057306678e7d8cb903c140aba1d4805a8a1f75b11f1129bb22d08c")); + result.test_bin_eq("SHA-256(keypair1)", + sha256(keypair1), + "1cdd4835a6057306678e7d8cb903c140aba1d4805a8a1f75b11f1129bb22d08c"); + result.test_bin_eq("SHA-256(keypair2)", + sha256(keypair2), + "1cdd4835a6057306678e7d8cb903c140aba1d4805a8a1f75b11f1129bb22d08c"); }), }; } diff -Nru botan3-3.7.1+dfsg/src/tests/test_sphincsplus_wots.cpp botan3-3.12.0+dfsg/src/tests/test_sphincsplus_wots.cpp --- botan3-3.7.1+dfsg/src/tests/test_sphincsplus_wots.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_sphincsplus_wots.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,10 +9,8 @@ #if defined(BOTAN_HAS_SPHINCS_PLUS_COMMON) - #include - #include - #include + #include #include #include #include @@ -21,13 +19,15 @@ namespace Botan_Tests { +namespace { + class SPHINCS_Plus_WOTS_Test final : public Text_Based_Test { private: static std::pair read_address_and_leaf_idx( std::span address_buffer) { BOTAN_ASSERT_NOMSG(address_buffer.size() == 32); - std::array adrs; + std::array adrs{}; for(size_t i = 0; i < 8; ++i) { adrs[i] = Botan::load_be(address_buffer.data(), i); } @@ -40,12 +40,12 @@ Text_Based_Test("pubkey/sphincsplus_wots.vec", "SphincsParameterSet,Address,SecretSeed,PublicSeed,HashedWotsPk,Msg,HashedWotsSig") {} - bool skip_this_test(const std::string&, const VarMap& vars) override { + bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override { [[maybe_unused]] auto params = Botan::Sphincs_Parameters::create(vars.get_req_str("SphincsParameterSet")); return !params.is_available(); } - Test::Result run_one_test(const std::string&, const VarMap& vars) final { + Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) final { Test::Result result("SLH-DSA's WOTS+"); auto params = Botan::Sphincs_Parameters::create(vars.get_req_str("SphincsParameterSet")); @@ -103,17 +103,17 @@ params, *hashes); - result.test_is_eq("WOTS+ signature generation", hash->process(sig_out), hashed_wots_sig_ref.get()); - result.test_is_eq("WOTS+ public key generation", hashed_pk_out, hashed_pk_ref); + result.test_bin_eq("WOTS+ signature generation", hash->process(sig_out), hashed_wots_sig_ref.get()); + result.test_bin_eq("WOTS+ public key generation", hashed_pk_out, hashed_pk_ref); // Test: Create PK from signature (Verification) - Botan::WotsPublicKey wots_pk_from_sig = + const Botan::WotsPublicKey wots_pk_from_sig = Botan::wots_public_key_from_signature(root_to_sign, sig_out, address, params, *hashes); // The WOTS+ PK is hashed like for creating a leaf. - result.test_is_eq("WOTS+ public key from signature", - hashes->T(pk_addr_pk_from_sig, wots_pk_from_sig), - hashed_pk_ref); + result.test_bin_eq("WOTS+ public key from signature", + hashes->T(pk_addr_pk_from_sig, wots_pk_from_sig), + hashed_pk_ref); return result; } @@ -121,6 +121,8 @@ BOTAN_REGISTER_TEST("pubkey", "sphincsplus_wots", SPHINCS_Plus_WOTS_Test); +} // namespace + } // namespace Botan_Tests #endif // BOTAN_HAS_SPHINCS_PLUS_COMMON diff -Nru botan3-3.7.1+dfsg/src/tests/test_srp6.cpp botan3-3.12.0+dfsg/src/tests/test_srp6.cpp --- botan3-3.7.1+dfsg/src/tests/test_srp6.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_srp6.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -59,24 +59,24 @@ auto group = Botan::DL_Group::from_name(group_id); const Botan::BigInt v = Botan::srp6_generate_verifier(username, password, salt, group_id, hash); - result.test_eq("SRP verifier", v, exp_v); + result.test_bn_eq("SRP verifier", v, exp_v); Botan::SRP6_Server_Session server; const size_t b_bits = Botan::BigInt::from_bytes(b).bits(); Fixed_Output_RNG b_rng(b); const Botan::BigInt B = server.step1(v, group, hash, b_bits, b_rng); - result.test_eq("SRP B", B, exp_B); + result.test_bn_eq("SRP B", B, exp_B); const size_t a_bits = Botan::BigInt::from_bytes(a).bits(); Fixed_Output_RNG a_rng(a); const auto srp_resp = Botan::srp6_client_agree(username, password, group, hash, salt, B, a_bits, a_rng); - result.test_eq("SRP A", srp_resp.first, exp_A); + result.test_bn_eq("SRP A", srp_resp.first, exp_A); const auto S = server.step2(srp_resp.first); - result.test_eq("SRP client S", srp_resp.second, exp_S); - result.test_eq("SRP server S", S, exp_S); + result.test_bin_eq("SRP client S", srp_resp.second, exp_S); + result.test_bin_eq("SRP server S", S, exp_S); return result; } @@ -95,7 +95,7 @@ const std::string password = "Awellchosen1_to_be_sure_"; const std::string hash_id = "SHA-256"; - for(size_t b : {1024, 1536, 2048, 3072, 4096, 6144, 8192}) { + for(const size_t b : {1024, 1536, 2048, 3072, 4096, 6144, 8192}) { if(b >= 4096 && !Test::run_long_tests()) { continue; } @@ -122,7 +122,7 @@ const Botan::SymmetricKey server_K = server.step2(client.first); - result.test_eq("computed same keys", client.second.bits_of(), server_K.bits_of()); + result.test_bin_eq("computed same keys", client.second.bits_of(), server_K.bits_of()); } result.end_timer(); results.push_back(result); diff -Nru botan3-3.7.1+dfsg/src/tests/test_stream.cpp botan3-3.12.0+dfsg/src/tests/test_stream.cpp --- botan3-3.7.1+dfsg/src/tests/test_stream.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_stream.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,6 +7,8 @@ #include "tests.h" #if defined(BOTAN_HAS_STREAM_CIPHER) + #include + #include #include #include #endif @@ -15,6 +17,8 @@ #if defined(BOTAN_HAS_STREAM_CIPHER) +namespace { + class Stream_Cipher_Tests final : public Text_Based_Test { public: Stream_Cipher_Tests() : Text_Based_Test("stream", "Key,Out", "In,Nonce,Seek") {} @@ -48,15 +52,15 @@ } const std::string provider(cipher->provider()); - result.test_is_nonempty("provider", provider); - result.test_eq(provider, cipher->name(), algo); + result.test_str_not_empty("provider", provider); + result.test_str_eq(provider, cipher->name(), algo); - result.confirm("default iv length is valid", cipher->valid_iv_length(cipher->default_iv_length())); + result.test_is_true("default iv length is valid", cipher->valid_iv_length(cipher->default_iv_length())); - result.confirm("advertised buffer size is > 0", cipher->buffer_size() > 0); + result.test_is_true("advertised buffer size is > 0", cipher->buffer_size() > 0); if(cipher->default_iv_length() == 0) { - result.confirm("if default iv length is zero, no iv supported", nonce.empty()); + result.test_is_true("if default iv length is zero, no iv supported", nonce.empty()); // This should still succeed cipher->set_iv(nullptr, 0); @@ -101,22 +105,22 @@ continue; } - result.test_eq("key not set", cipher->has_keying_material(), false); + result.test_is_false("key not set", cipher->has_keying_material()); cipher->set_key(key); - result.test_eq("key set", cipher->has_keying_material(), true); + result.test_is_true("key set", cipher->has_keying_material()); /* Test invalid nonce sizes. this assumes no implemented cipher supports a nonce of 65000 */ const size_t large_nonce_size = 65000; - result.confirm("Stream cipher does not support very large nonce", - cipher->valid_iv_length(large_nonce_size) == false); + result.test_is_true("Stream cipher does not support very large nonce", + cipher->valid_iv_length(large_nonce_size) == false); result.test_throws("Throws if invalid nonce size given", [&]() { cipher->set_iv(nullptr, large_nonce_size); }); /* - If the set_nonce call earlier succeded, then we require that it also + If the set_nonce call earlier succeeded, then we require that it also worked (ie saved the nonce for later use) even though the key was not set. So, don't set the nonce now, to ensure the previous call had an effect. @@ -131,14 +135,14 @@ // Test that clone works and does not affect parent object 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()); + result.test_is_true("Clone has different pointer", cipher.get() != clone.get()); + result.test_str_eq("Clone has same name", cipher->name(), clone->name()); clone->set_key(this->rng().random_vec(cipher->maximum_keylength())); { std::vector buf = input; cipher->encrypt(buf); - result.test_eq(provider, "encrypt", buf, expected); + result.test_bin_eq(provider + " encrypt", buf, expected); } { @@ -152,7 +156,7 @@ } std::vector buf = input; cipher->encrypt(buf); - result.test_eq(provider, "encrypt 2", buf, expected); + result.test_bin_eq(provider + " encrypt 2", buf, expected); } if(!nonce.empty()) { @@ -162,7 +166,7 @@ } std::vector buf = input; cipher->encrypt(buf); - result.test_eq(provider, "second encrypt", buf, expected); + result.test_bin_eq(provider + " second encrypt", buf, expected); } { @@ -180,7 +184,7 @@ size_t buf_len = buf.size(); while(buf_len > 0) { - size_t next = std::min(buf_len, this->rng().next_byte()); + const size_t next = std::min(buf_len, this->rng().next_byte()); cipher->write_keystream(buf_ptr, next); buf_ptr += next; buf_len -= next; @@ -189,12 +193,12 @@ for(size_t i = 0; i != input.size(); ++i) { buf[i] ^= input[i]; } - result.test_eq(provider, "write_keystream", buf, expected); + result.test_bin_eq(provider + " write_keystream", buf, expected); } - result.test_eq("key set", cipher->has_keying_material(), true); + result.test_is_true("key set", cipher->has_keying_material()); cipher->clear(); - result.test_eq("key not set", cipher->has_keying_material(), false); + result.test_is_false("key not set", cipher->has_keying_material()); try { std::vector buf(128); @@ -211,6 +215,8 @@ BOTAN_REGISTER_SERIALIZED_SMOKE_TEST("stream", "stream_ciphers", Stream_Cipher_Tests); +} // namespace + #endif } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_strong_type.cpp botan3-3.12.0+dfsg/src/tests/test_strong_type.cpp --- botan3-3.7.1+dfsg/src/tests/test_strong_type.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_strong_type.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -1,20 +1,21 @@ /* - * (C) 2023 Jack Lloyd + * (C) 2023,2026 Jack Lloyd * 2023 René Meusel - Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ -#include "test_rng.h" #include "tests.h" +#include "test_rng.h" + #include #include #include #include - #include #include +#include #include #include #include @@ -26,607 +27,690 @@ using Test_Size = Botan::Strong; using Test_Length = Botan::Strong; -std::string foo(Test_Size) { +std::string foo(Test_Size /*unused*/) { return "some size"; } -std::string foo(Test_Length) { +std::string foo(Test_Length /*unused*/) { return "some length"; } using Test_Nonce = Botan::Strong, struct Test_Nonce_>; using Test_Hash_Name = Botan::Strong; -std::vector test_strong_type() { - return { - CHECK("strong type initialization", - [](auto&) { - // default constructor - Test_Size size1; - - // value initialization - [[maybe_unused]] Test_Size size2(42); - - // assignment operator - size1 = Test_Size(42); - }), - - CHECK("value retrieval", - [](auto& result) { - Test_Size a(42); - const Test_Size b(42); - - result.test_is_eq("get()", a.get(), size_t(42)); - result.test_is_eq("const get()", b.get(), size_t(42)); - }), - - CHECK("comparisons", - [](auto& result) { - const Test_Size a(42); - const Test_Size b(42); - - result.confirm("equal", a == b); - result.confirm("lower than", a < Test_Size(1337)); - result.confirm("greater than", Test_Size(1337) > b); - }), - - CHECK("function overloading", - [](auto& result) { - result.test_eq("overloading size", foo(Test_Size(42)), "some size"); - result.test_eq("overloading size", foo(Test_Length(42)), "some length"); - }), - - CHECK("is_strong_type", - [](auto& result) { - result.confirm("strong type (int)", Botan::is_strong_type_v); - result.confirm("no strong type (int)", !Botan::is_strong_type_v); - result.confirm("strong type (vector)", Botan::is_strong_type_v); - result.confirm("no strong type (vector)", !Botan::is_strong_type_v>); - result.confirm("strong type (const vector)", Botan::is_strong_type_v); - result.confirm("no strong type (const vector)", !Botan::is_strong_type_v>); - }), - }; -} - -std::vector test_container_strong_type() { - return { - CHECK("initialization", - [](auto&) { - [[maybe_unused]] Test_Nonce empty_nonce; - [[maybe_unused]] Test_Nonce short_nonce(Botan::hex_decode("DEADBEEF")); - }), - - CHECK("behaves like a standard container", - [](auto& result) { - auto base_nonce = Botan::hex_decode("DEADBEEF"); - auto dataptr = base_nonce.data(); - auto nonce = Test_Nonce(std::move(base_nonce)); - - result.test_is_eq("size()", nonce.size(), size_t(4)); - result.confirm("empty()", !nonce.empty()); - result.test_is_eq("data()", nonce.data(), dataptr); - - for(auto& c : nonce) { - result.confirm("iteration", c > 0); - } - }), - - CHECK( - "container concepts are satisfied", - [](auto& result) { - using Test_Map = Botan::Strong, struct Test_Map_>; - using Test_Array = Botan::Strong, struct Test_Array_>; - - result.confirm("Test_Nonce is container", Botan::concepts::container); - result.confirm("Test_Array is container", Botan::concepts::container); - result.confirm("Test_Map is container", Botan::concepts::container); - result.confirm("Test_Size is not container", !Botan::concepts::container); - - result.confirm("Test_Nonce is contiguous_container", Botan::concepts::contiguous_container); - result.confirm("Test_Array is contiguous_container", Botan::concepts::contiguous_container); - result.confirm("Test_Map is not contiguous_container", !Botan::concepts::contiguous_container); - result.confirm("Test_Size is not contiguous_container", !Botan::concepts::contiguous_container); - - result.confirm("Test_Nonce is resizable_container", Botan::concepts::resizable_container); - result.confirm("Test_Array is not resizable_container", !Botan::concepts::resizable_container); - result.confirm("Test_Map is not resizable_container", !Botan::concepts::resizable_container); - result.confirm("Test_Size is not resizable_container", !Botan::concepts::resizable_container); - }), - - CHECK("binds to a std::span<>", - [](auto& result) { - auto get_size = [](std::span data) { return data.size(); }; - - const auto nonce = Test_Nonce(Botan::hex_decode("DEADBEEF")); - - result.test_is_eq("can bind to std::span<>", get_size(nonce), nonce.size()); - }), - - CHECK("std::string container", - [](auto& result) { - Test_Hash_Name thn("SHA-1"); - - std::stringstream stream; - stream << thn; - result.test_eq("strong types are streamable", stream.str(), std::string("SHA-1")); - }), - - CHECK("strong types are sortable", - [](auto& result) { - using Test_Length_List = Botan::Strong, struct Test_Length_List_>; - - Test_Length_List hashes({Test_Length(3), Test_Length(1), Test_Length(4), Test_Length(2)}); - - // TODO: C++20 - std::ranges::sort - std::sort(hashes.begin(), hashes.end()); - - result.test_eq("1", hashes.get().at(0).get(), size_t(1)); - result.test_eq("2", hashes.get().at(1).get(), size_t(2)); - result.test_eq("3", hashes.get().at(2).get(), size_t(3)); - result.test_eq("4", hashes.get().at(3).get(), size_t(4)); - }), - - CHECK("byte-container strong types can be randomly generated", - [](auto& result) { - using Test_Buffer = Botan::Strong, struct Test_Buffer_>; - using Test_Secure_Buffer = Botan::Strong, struct Test_Secure_Buffer_>; - using Test_Fixed_Array = Botan::Strong, struct Test_Fixed_Array_>; - - Fixed_Output_RNG rng; - const auto e1 = Botan::hex_decode("deadbeef"); - const auto e2 = Botan::hex_decode("baadcafe"); - const auto e3 = Botan::hex_decode("baadf00d"); - rng.add_entropy(e1.data(), e1.size()); - rng.add_entropy(e2.data(), e2.size()); - rng.add_entropy(e3.data(), e3.size()); - - auto tb = rng.random_vec(4); - auto tsb = rng.random_vec(4); - Test_Fixed_Array tfa; - rng.random_vec(tfa); - - result.test_eq("generated expected output", tb.get(), Botan::hex_decode("deadbeef")); - result.test_eq("generated expected secure output", tsb.get(), Botan::hex_decode_locked("baadcafe")); - result.test_eq("generated expected fixed output", - std::vector(tfa.begin(), tfa.end()), - Botan::hex_decode("baadf00d")); - }), - - CHECK("bounds-checked accessors are exposed opportunistically", - [](Test::Result& result) { - using Test_Array = Botan::Strong, struct Test_Array_>; - using Test_Map = Botan::Strong, struct Test_Map_>; - using Test_Vector = Botan::Strong, struct Test_Vector_>; - - Test_Array a({1, 2, 3, 4}); - result.test_is_eq("at() returns 3", a.at(2), 3); - result.test_throws("at() throws on out-of-bounds access", [&a]() { a.at(4); }); - - Test_Map m({{1, "one"}, {2, "two"}, {3, "three"}}); - result.test_is_eq("at() returns 'two'", m.at(2), "two"); - result.test_throws("at() throws on out-of-bounds access", [&m]() { m.at(4); }); - - Test_Vector v({1, 2, 3, 4}); - result.test_is_eq("at() returns 2", v.at(1), 2); - result.test_throws("at() throws on out-of-bounds access", [&v]() { v.at(4); }); - }), - - CHECK("subscript accessors are exposed", - [](Test::Result& result) { - using Test_Array = Botan::Strong, struct Test_Array_>; - using Test_Map = Botan::Strong, struct Test_Map_>; - using Test_Vector = Botan::Strong, struct Test_Vector_>; - - Test_Array a({1, 2, 3, 4}); - result.test_is_eq("[] returns 3", a[2], 3); - - Test_Map m({{1, "one"}, {2, "two"}, {3, "three"}}); - result.test_is_eq("[] returns 'two'", m[2], "two"); - - Test_Vector v({1, 2, 3, 4}); - result.test_is_eq("[] returns 2", v[1], 2); - }), - }; -} - -std::vector test_integer_strong_type() { - using StrongInt = Botan::Strong; - using StrongIntWithPodArithmetics = Botan::Strong; - - return { - CHECK("comparison operators with POD are always allowed", - [](auto& result) { - StrongInt i(42); - - result.confirm("i ==", i == 42); - result.confirm("i !=", i != 0); - result.confirm("i >", i > 41); - result.confirm("i >= 1", i >= 41); - result.confirm("i >= 2", i >= 42); - result.confirm("i <", i < 43); - result.confirm("i <= 1", i <= 43); - result.confirm("i <= 2", i <= 42); - - result.confirm("== i", 42 == i); - result.confirm("!= i", 0 != i); - result.confirm("> i", 43 > i); - result.confirm(">= 1 i", 43 >= i); - result.confirm(">= 2 i", 42 >= i); - result.confirm("< i", 41 < i); - result.confirm("<= 1 i", 41 <= i); - result.confirm("<= 2 i", 42 <= i); - }), - - CHECK("increment/decrement are always allowed", - [](auto& result) { - StrongInt i(42); - - result.confirm("i++", i++ == 42); - result.confirm("i post-incremented", i == 43); - result.confirm("++i", ++i == 44); - result.confirm("i pre-incremented", i == 44); - - result.confirm("i--", i-- == 44); - result.confirm("i post-decremented", i == 43); - result.confirm("--i", --i == 42); - result.confirm("i pre-decremented", i == 42); - }), - - CHECK("comparison operators with Strong<>", - [](auto& result) { - StrongInt i(42); - StrongInt i42(42); - StrongInt i41(41); - StrongInt i43(43); - StrongInt i0(0); - - result.confirm("==", i == i42); - result.confirm("!=", i != i0); - result.confirm(">", i > i41); - result.confirm(">= 1", i >= i41); - result.confirm(">= 2", i >= i42); - result.confirm("<", i < i43); - result.confirm("<= 1", i <= i43); - result.confirm("<= 2", i <= i42); - }), - - CHECK("arithmetics with Strong<>", - [](auto& result) { - StrongInt i(42); - StrongInt i2(2); - StrongInt i4(4); - StrongInt i12(12); - - result.confirm("+", i + i == 84); - result.confirm("-", i - i == 0); - result.confirm("*", i * i == 1764); - result.confirm("/", i / i == 1); - result.confirm("^", (i ^ i) == 0); - result.confirm("&", (i & i) == 42); - result.confirm("|", (i | i) == 42); - result.confirm(">>", (i >> i2) == 10); - result.confirm("<<", (i << i2) == 168); - - result.confirm("+=", (i += i2) == 44); - result.confirm("-=", (i -= i2) == 42); - result.confirm("*=", (i *= i2) == 84); - result.confirm("/=", (i /= i2) == 42); - result.confirm("^=", (i ^= i2) == 40); - result.confirm("&=", (i &= i12) == 8); - result.confirm("|=", (i |= i2) == 10); - result.confirm("<<=", (i <<= i2) == 40); - result.confirm(">>=", (i >>= i4) == 2); - }), - - CHECK("arithmetics with POD", - [](auto& result) { - StrongIntWithPodArithmetics i(42); - StrongIntWithPodArithmetics i2(2); - - result.confirm("i +", i + 1 == 43); - result.confirm("i -", i - 1 == 41); - result.confirm("i *", i * 2 == 84); - result.confirm("i /", i / 2 == 21); - result.confirm("i ^", (i ^ 10) == 32); - result.confirm("i &", (i & 15) == 10); - result.confirm("i |", (i | 4) == 46); - result.confirm("i >>", (i >> 2) == 10); - result.confirm("i <<", (i << 2) == 168); - - result.confirm("+ i", 1 + i == 43); - result.confirm("- i", 1 - i == -41); - result.confirm("* i", 2 * i == 84); - result.confirm("/ i", 84 / i == 2); - result.confirm("^ i", (10 ^ i) == 32); - result.confirm("& i", (15 & i) == 10); - result.confirm("| i", (4 | i) == 46); - result.confirm(">> i", (4 >> i2) == 1); - result.confirm("<< i", (2 << i2) == 8); - - result.confirm("i +=", (i += 2) == 44); - result.confirm("i -=", (i -= 2) == 42); - result.confirm("i *=", (i *= 2) == 84); - result.confirm("i /=", (i /= 2) == 42); - result.confirm("i ^=", (i ^= 2) == 40); - result.confirm("i &=", (i &= 12) == 8); - result.confirm("i |=", (i |= 2) == 10); - result.confirm("i <<=", (i <<= 2) == 40); - result.confirm("i >>=", (i >>= 4) == 2); - }), - - CHECK("arithmetics with POD is still Strong<>", - [](auto& result) { - StrongIntWithPodArithmetics i(42); - StrongIntWithPodArithmetics i2(2); - - result.confirm("i +", Botan::is_strong_type_v); - result.confirm("i -", Botan::is_strong_type_v); - result.confirm("i *", Botan::is_strong_type_v); - result.confirm("i /", Botan::is_strong_type_v); - result.confirm("i ^", Botan::is_strong_type_v); - result.confirm("i &", Botan::is_strong_type_v); - result.confirm("i |", Botan::is_strong_type_v); - result.confirm("i >>", Botan::is_strong_type_v> 2))>); - result.confirm("i <<", Botan::is_strong_type_v); - - result.confirm("+ i", Botan::is_strong_type_v); - result.confirm("- i", Botan::is_strong_type_v); - result.confirm("* i", Botan::is_strong_type_v); - result.confirm("/ i", Botan::is_strong_type_v); - result.confirm("^ i", Botan::is_strong_type_v); - result.confirm("& i", Botan::is_strong_type_v); - result.confirm("| i", Botan::is_strong_type_v); - result.confirm(">> i", Botan::is_strong_type_v> i2))>); - result.confirm("<< i", Botan::is_strong_type_v); - - result.confirm("i +=", Botan::is_strong_type_v); - result.confirm("i -=", Botan::is_strong_type_v); - result.confirm("i *=", Botan::is_strong_type_v); - result.confirm("i /=", Botan::is_strong_type_v); - result.confirm("i ^=", Botan::is_strong_type_v); - result.confirm("i &=", Botan::is_strong_type_v); - result.confirm("i |=", Botan::is_strong_type_v); - result.confirm("i <<=", Botan::is_strong_type_v); - result.confirm("i >>=", Botan::is_strong_type_v>= 4)>); - }), - }; -} - using Test_Foo = Botan::Strong, struct Test_Foo_>; using Test_Bar = Botan::Strong, struct Test_Bar_>; -[[maybe_unused]] int test_strong_helper(const Botan::StrongSpan&) { +[[maybe_unused]] uint32_t test_strong_helper(const Botan::StrongSpan& /*unused*/) { return 0; } -[[maybe_unused]] int test_strong_helper(const Botan::StrongSpan&) { +[[maybe_unused]] uint32_t test_strong_helper(const Botan::StrongSpan& /*unused*/) { return 1; } -[[maybe_unused]] int test_strong_helper(const Botan::StrongSpan&) { +[[maybe_unused]] uint32_t test_strong_helper(const Botan::StrongSpan& /*unused*/) { return 2; } -Test::Result test_strong_span() { - Test::Result result("StrongSpan<>"); - - const Test_Foo foo(Botan::hex_decode("DEADBEEF")); - result.test_is_eq("binds to StrongSpan", test_strong_helper(foo), 1); - - Test_Bar bar(Botan::hex_decode("CAFECAFE")); - result.test_is_eq("binds to StrongSpan", test_strong_helper(bar), 2); - - Botan::StrongSpan span(foo); - - result.confirm("underlying type is uint8_t", std::is_same_v); - result.confirm("strong type is a contiguous buffer", Botan::concepts::contiguous_container); - result.confirm("strong type is a contiguous strong type buffer", - Botan::concepts::contiguous_strong_type); - result.confirm("strong span is not a contiguous buffer", !Botan::concepts::contiguous_container); - result.confirm("strong span is not a contiguous strong type buffer", - !Botan::concepts::contiguous_strong_type); +class Strong_Type_Tests final : public Test { + public: + std::vector run() override { + return { + test_strong_type_initialization(), + test_value_retrieval(), + test_comparisons(), + test_function_overloading(), + test_is_strong_type(), + + test_container_initialization(), + test_behaves_like_standard_container(), + test_container_concepts(), + test_binds_to_span(), + test_string_container(), + test_sortable(), + test_random_generation(), + test_subscript_accessors(), + + test_int_comparison_with_pod(), + test_int_increment_decrement(), + test_int_comparison_with_strong(), + test_int_arithmetic_with_strong(), + test_int_arithmetic_with_pod(), + test_int_arithmetic_with_pod_is_strong(), + + test_strong_span(), + + test_wrapping_object_into_strong_type(), + test_wrapping_strong_type_from_itself(), + test_unwrapping_return_reference_type(), + test_unwrapping_non_strong_type_return_type(), + test_unwrapping_object_from_strong_type(), + test_unwrapping_non_strong_type(), + }; + } + + private: + static Test::Result test_strong_type_initialization() { + Test::Result result("strong type initialization"); + + // default constructor + Test_Size size1; + + // value initialization + [[maybe_unused]] const Test_Size size2(42); + + // assignment operator + size1 = Test_Size(42); + + return result; + } + + static Test::Result test_value_retrieval() { + Test::Result result("value retrieval"); + + Test_Size a(42); + const Test_Size b(42); + + result.test_sz_eq("get()", a.get(), 42); + result.test_sz_eq("const get()", b.get(), 42); + + return result; + } + + static Test::Result test_comparisons() { + Test::Result result("comparisons"); + + const Test_Size a(42); + const Test_Size b(42); + + result.test_is_true("equal", a == b); + result.test_is_true("lower than", a < Test_Size(1337)); + result.test_is_true("greater than", Test_Size(1337) > b); + + return result; + } + + static Test::Result test_function_overloading() { + Test::Result result("function overloading"); + + result.test_str_eq("overloading size", foo(Test_Size(42)), "some size"); + result.test_str_eq("overloading size", foo(Test_Length(42)), "some length"); + + return result; + } + + static Test::Result test_is_strong_type() { + Test::Result result("is_strong_type"); + + result.test_is_true("strong type (int)", Botan::is_strong_type_v); + result.test_is_true("no strong type (int)", !Botan::is_strong_type_v); + result.test_is_true("strong type (vector)", Botan::is_strong_type_v); + result.test_is_true("no strong type (vector)", !Botan::is_strong_type_v>); + result.test_is_true("strong type (const vector)", Botan::is_strong_type_v); + result.test_is_true("no strong type (const vector)", !Botan::is_strong_type_v>); + + return result; + } + + static Test::Result test_container_initialization() { + Test::Result result("initialization"); + + [[maybe_unused]] const Test_Nonce empty_nonce; + [[maybe_unused]] const Test_Nonce short_nonce(Botan::hex_decode("DEADBEEF")); + + return result; + } + + static Test::Result test_behaves_like_standard_container() { + Test::Result result("behaves like a standard container"); + + auto base_nonce = Botan::hex_decode("DEADBEEF"); + auto* dataptr = base_nonce.data(); + auto nonce = Test_Nonce(std::move(base_nonce)); + + result.test_sz_eq("size()", nonce.size(), 4); + result.test_is_true("empty()", !nonce.empty()); + result.test_is_true("data()", nonce.data() == dataptr); + + for(const auto& c : nonce) { + result.test_is_true("iteration", c > 0); + } + + return result; + } + + static Test::Result test_container_concepts() { + Test::Result result("container concepts are satisfied"); + + using Test_Map = Botan::Strong, struct Test_Map_>; + using Test_Array = Botan::Strong, struct Test_Array_>; + + result.test_is_true("Test_Nonce is container", Botan::concepts::container); + result.test_is_true("Test_Array is container", Botan::concepts::container); + result.test_is_true("Test_Map is container", Botan::concepts::container); + result.test_is_true("Test_Size is not container", !Botan::concepts::container); + + result.test_is_true("Test_Nonce is contiguous_container", Botan::concepts::contiguous_container); + result.test_is_true("Test_Array is contiguous_container", Botan::concepts::contiguous_container); + result.test_is_true("Test_Map is not contiguous_container", !Botan::concepts::contiguous_container); + result.test_is_true("Test_Size is not contiguous_container", + !Botan::concepts::contiguous_container); + + result.test_is_true("Test_Nonce is resizable_container", Botan::concepts::resizable_container); + result.test_is_true("Test_Array is not resizable_container", + !Botan::concepts::resizable_container); + result.test_is_true("Test_Map is not resizable_container", !Botan::concepts::resizable_container); + result.test_is_true("Test_Size is not resizable_container", !Botan::concepts::resizable_container); + + return result; + } + + static Test::Result test_binds_to_span() { + Test::Result result("binds to a std::span<>"); + + auto get_size = [](std::span data) { return data.size(); }; + + const auto nonce = Test_Nonce(Botan::hex_decode("DEADBEEF")); + + result.test_sz_eq("can bind to std::span<>", get_size(nonce), nonce.size()); + + return result; + } + + static Test::Result test_string_container() { + Test::Result result("std::string container"); + + const Test_Hash_Name name("SHA-1"); + + std::stringstream stream; + stream << name; + result.test_str_eq("strong types are streamable", stream.str(), std::string("SHA-1")); + + return result; + } + + static Test::Result test_sortable() { + Test::Result result("strong types are sortable"); + + using Test_Length_List = Botan::Strong, struct Test_Length_List_>; + + Test_Length_List hashes({Test_Length(3), Test_Length(1), Test_Length(4), Test_Length(2)}); + + std::ranges::sort(hashes); + + result.test_sz_eq("1", hashes.get().at(0).get(), size_t(1)); + result.test_sz_eq("2", hashes.get().at(1).get(), size_t(2)); + result.test_sz_eq("3", hashes.get().at(2).get(), size_t(3)); + result.test_sz_eq("4", hashes.get().at(3).get(), size_t(4)); + + return result; + } + + static Test::Result test_random_generation() { + Test::Result result("byte-container strong types can be randomly generated"); + + using Test_Buffer = Botan::Strong, struct Test_Buffer_>; + using Test_Secure_Buffer = Botan::Strong, struct Test_Secure_Buffer_>; + using Test_Fixed_Array = Botan::Strong, struct Test_Fixed_Array_>; + + Fixed_Output_RNG rng; + const auto e1 = Botan::hex_decode("deadbeef"); + const auto e2 = Botan::hex_decode("baadcafe"); + const auto e3 = Botan::hex_decode("baadf00d"); + rng.add_entropy(e1.data(), e1.size()); + rng.add_entropy(e2.data(), e2.size()); + rng.add_entropy(e3.data(), e3.size()); + + auto tb = rng.random_vec(4); + auto tsb = rng.random_vec(4); + Test_Fixed_Array tfa; + rng.random_vec(tfa); + + result.test_bin_eq("generated expected output", tb.get(), "deadbeef"); + result.test_bin_eq("generated expected secure output", tsb.get(), "baadcafe"); + result.test_bin_eq("generated expected fixed output", std::vector(tfa.begin(), tfa.end()), "baadf00d"); + + return result; + } + + static Test::Result test_subscript_accessors() { + Test::Result result("subscript accessors are exposed"); + + using Test_Array = Botan::Strong, struct Test_Array_>; + using Test_Map = Botan::Strong, struct Test_Map_>; + using Test_Vector = Botan::Strong, struct Test_Vector_>; + + Test_Array a({1, 2, 3, 4}); + result.test_u8_eq("[] returns 3", a[2], 3); + + Test_Map m({{1, "one"}, {2, "two"}, {3, "three"}}); + result.test_str_eq("[] returns 'two'", m[2], "two"); + + Test_Vector v({1, 2, 3, 4}); + result.test_u8_eq("[] returns 2", v[1], 2); + + return result; + } + + static Test::Result test_int_comparison_with_pod() { + Test::Result result("comparison operators with POD are always allowed"); + + using StrongInt = Botan::Strong; + const StrongInt i(42); + + result.test_is_true("i ==", i == 42); + result.test_is_true("i !=", i != 0); + result.test_is_true("i >", i > 41); + result.test_is_true("i >= 1", i >= 41); + result.test_is_true("i >= 2", i >= 42); + result.test_is_true("i <", i < 43); + result.test_is_true("i <= 1", i <= 43); + result.test_is_true("i <= 2", i <= 42); + + result.test_is_true("== i", 42 == i); + result.test_is_true("!= i", 0 != i); + result.test_is_true("> i", 43 > i); + result.test_is_true(">= 1 i", 43 >= i); + result.test_is_true(">= 2 i", 42 >= i); + result.test_is_true("< i", 41 < i); + result.test_is_true("<= 1 i", 41 <= i); + result.test_is_true("<= 2 i", 42 <= i); + + return result; + } + + static Test::Result test_int_increment_decrement() { + Test::Result result("increment/decrement are always allowed"); + + using StrongInt = Botan::Strong; + StrongInt i(42); + + result.test_is_true("i++", i++ == 42); + result.test_is_true("i post-incremented", i == 43); + result.test_is_true("++i", ++i == 44); + result.test_is_true("i pre-incremented", i == 44); + + result.test_is_true("i--", i-- == 44); + result.test_is_true("i post-decremented", i == 43); + result.test_is_true("--i", --i == 42); + result.test_is_true("i pre-decremented", i == 42); + + return result; + } + + static Test::Result test_int_comparison_with_strong() { + Test::Result result("comparison operators with Strong<>"); + + using StrongInt = Botan::Strong; + const StrongInt i(42); + const StrongInt i42(42); + const StrongInt i41(41); + const StrongInt i43(43); + const StrongInt i0(0); + + result.test_is_true("==", i == i42); + result.test_is_true("!=", i != i0); + result.test_is_true(">", i > i41); + result.test_is_true(">= 1", i >= i41); + result.test_is_true(">= 2", i >= i42); + result.test_is_true("<", i < i43); + result.test_is_true("<= 1", i <= i43); + result.test_is_true("<= 2", i <= i42); + + return result; + } + + static Test::Result test_int_arithmetic_with_strong() { + Test::Result result("arithmetics with Strong<>"); + + using StrongInt = Botan::Strong; + StrongInt i(42); + const StrongInt i2(2); + const StrongInt i4(4); + const StrongInt i12(12); + + result.test_is_true("+", i + i == 84); + result.test_is_true("-", i - i == 0); + result.test_is_true("*", i * i == 1764); + result.test_is_true("/", i / i == 1); + result.test_is_true("^", (i ^ i) == 0); + result.test_is_true("&", (i & i) == 42); + result.test_is_true("|", (i | i) == 42); + result.test_is_true(">>", (i >> i2) == 10); + result.test_is_true("<<", (i << i2) == 168); + + result.test_is_true("+=", (i += i2) == 44); + result.test_is_true("-=", (i -= i2) == 42); + result.test_is_true("*=", (i *= i2) == 84); + result.test_is_true("/=", (i /= i2) == 42); + result.test_is_true("^=", (i ^= i2) == 40); + result.test_is_true("&=", (i &= i12) == 8); + result.test_is_true("|=", (i |= i2) == 10); + result.test_is_true("<<=", (i <<= i2) == 40); + result.test_is_true(">>=", (i >>= i4) == 2); + + return result; + } + + static Test::Result test_int_arithmetic_with_pod() { + Test::Result result("arithmetics with POD"); + + using StrongIntWithPodArithmetics = + Botan::Strong; + StrongIntWithPodArithmetics i(42); + const StrongIntWithPodArithmetics i2(2); + + result.test_is_true("i +", i + 1 == 43); + result.test_is_true("i -", i - 1 == 41); + result.test_is_true("i *", i * 2 == 84); + result.test_is_true("i /", i / 2 == 21); + result.test_is_true("i ^", (i ^ 10) == 32); + result.test_is_true("i &", (i & 15) == 10); + result.test_is_true("i |", (i | 4) == 46); + result.test_is_true("i >>", (i >> 2) == 10); + result.test_is_true("i <<", (i << 2) == 168); + + result.test_is_true("+ i", 1 + i == 43); + result.test_is_true("- i", 1 - i == -41); + result.test_is_true("* i", 2 * i == 84); + result.test_is_true("/ i", 84 / i == 2); + result.test_is_true("^ i", (10 ^ i) == 32); + result.test_is_true("& i", (15 & i) == 10); + result.test_is_true("| i", (4 | i) == 46); + result.test_is_true(">> i", (4 >> i2) == 1); + result.test_is_true("<< i", (2 << i2) == 8); + + result.test_is_true("i +=", (i += 2) == 44); + result.test_is_true("i -=", (i -= 2) == 42); + result.test_is_true("i *=", (i *= 2) == 84); + result.test_is_true("i /=", (i /= 2) == 42); + result.test_is_true("i ^=", (i ^= 2) == 40); + result.test_is_true("i &=", (i &= 12) == 8); + result.test_is_true("i |=", (i |= 2) == 10); + result.test_is_true("i <<=", (i <<= 2) == 40); + result.test_is_true("i >>=", (i >>= 4) == 2); + + return result; + } + + static Test::Result test_int_arithmetic_with_pod_is_strong() { + Test::Result result("arithmetics with POD is still Strong<>"); + + using StrongIntWithPodArithmetics = + Botan::Strong; + StrongIntWithPodArithmetics i(42); // NOLINT(*-const-correctness) clang-tidy bug + const StrongIntWithPodArithmetics i2(2); + + result.test_is_true("i +", Botan::is_strong_type_v); + result.test_is_true("i -", Botan::is_strong_type_v); + result.test_is_true("i *", Botan::is_strong_type_v); + result.test_is_true("i /", Botan::is_strong_type_v); + result.test_is_true("i ^", Botan::is_strong_type_v); + result.test_is_true("i &", Botan::is_strong_type_v); + result.test_is_true("i |", Botan::is_strong_type_v); + result.test_is_true("i >>", Botan::is_strong_type_v> 2))>); + result.test_is_true("i <<", Botan::is_strong_type_v); + + result.test_is_true("+ i", Botan::is_strong_type_v); + result.test_is_true("- i", Botan::is_strong_type_v); + result.test_is_true("* i", Botan::is_strong_type_v); + result.test_is_true("/ i", Botan::is_strong_type_v); + result.test_is_true("^ i", Botan::is_strong_type_v); + result.test_is_true("& i", Botan::is_strong_type_v); + result.test_is_true("| i", Botan::is_strong_type_v); + result.test_is_true(">> i", Botan::is_strong_type_v> i2))>); + result.test_is_true("<< i", Botan::is_strong_type_v); + + result.test_is_true("i +=", Botan::is_strong_type_v); + result.test_is_true("i -=", Botan::is_strong_type_v); + result.test_is_true("i *=", Botan::is_strong_type_v); + result.test_is_true("i /=", Botan::is_strong_type_v); + result.test_is_true("i ^=", Botan::is_strong_type_v); + result.test_is_true("i &=", Botan::is_strong_type_v); + result.test_is_true("i |=", Botan::is_strong_type_v); + result.test_is_true("i <<=", Botan::is_strong_type_v); + result.test_is_true("i >>=", Botan::is_strong_type_v>= 4)>); + + return result; + } + + static Test::Result test_strong_span() { + Test::Result result("StrongSpan<>"); + + const Test_Foo foo(Botan::hex_decode("DEADBEEF")); + result.test_u32_eq("binds to StrongSpan", test_strong_helper(foo), 1); + + Test_Bar bar(Botan::hex_decode("CAFECAFE")); + result.test_u32_eq("binds to StrongSpan", test_strong_helper(bar), 2); + + const Botan::StrongSpan span(foo); + + result.test_is_true("underlying type is uint8_t", std::is_same_v); + result.test_is_true("strong type is a contiguous buffer", + Botan::concepts::contiguous_container); + result.test_is_true("strong type is a contiguous strong type buffer", + Botan::concepts::contiguous_strong_type); + result.test_is_true("strong span is not a contiguous buffer", + !Botan::concepts::contiguous_container); + result.test_is_true("strong span is not a contiguous strong type buffer", + !Botan::concepts::contiguous_strong_type); + + return result; + } + + static Test::Result test_wrapping_object_into_strong_type() { + Test::Result result("generically wrapping an object into a strong type"); + + using Strong_String = Botan::Strong; + using Strong_Unique = Botan::Strong, struct Strong_Unique_>; + using namespace std::string_literals; + + const std::string expl("explicit creation"s); + std::string rval("rvalue-ref creation"s); + auto stt_copy = Botan::wrap_strong_type(expl); + auto stt_implicit = Botan::wrap_strong_type("implicit conversion from const char*"); + auto stt_rvalue_ref = Botan::wrap_strong_type(std::move(rval)); + auto stt_rvalue = Botan::wrap_strong_type("rvalue creation from std::string (literal)"s); + + result.test_str_eq("stt_copy", stt_copy.get(), "explicit creation"); + result.test_str_eq("stt_implicit", stt_implicit.get(), "implicit conversion from const char*"); + result.test_str_eq("stt_rvalue_ref", stt_rvalue_ref.get(), "rvalue-ref creation"); + result.test_str_eq("stt_rvalue", stt_rvalue.get(), "rvalue creation from std::string (literal)"); + + // unique_ptr does not support copy construction and prohibits + // implicit conversion from a raw pointer of its wrapped type. + auto rval_ptr = std::make_unique("rvalue creation from ptr"); + auto stt_implicit_ptr = + // NOLINTNEXTLINE(*-owning-memory) + Botan::wrap_strong_type(new std::string("implicit creation from ptr")); + auto stt_rvalue_ptr = Botan::wrap_strong_type(std::move(rval_ptr)); + + result.test_str_eq("stt_implicit_ptr", *stt_implicit_ptr.get(), "implicit creation from ptr"); + result.test_str_eq("stt_rvalue_ptr", *stt_rvalue_ptr.get(), "rvalue creation from ptr"); + + return result; + } + + static Test::Result test_wrapping_strong_type_from_itself() { + Test::Result result("generically wrapping a strong type from itself"); + + using Strong_String = Botan::Strong; + using Strong_Unique = Botan::Strong, struct Strong_Unique_>; + + const Strong_String stt("wrapped"); + Strong_String stt_rval("wrapped and moved"); + + auto stt_copy = Botan::wrap_strong_type(stt); + auto stt_move = Botan::wrap_strong_type(std::move(stt_rval)); + auto stt_inplace = Botan::wrap_strong_type(Strong_String("inplace")); + + result.test_str_eq("stt_copy", stt_copy.get(), "wrapped"); + result.test_str_eq("stt_move", stt_move.get(), "wrapped and moved"); + result.test_str_eq("stt_inplace", stt_inplace.get(), "inplace"); + + Strong_Unique stt_ptr(std::make_unique("wrapped ptr")); + + auto stt_ptr_move = Botan::wrap_strong_type(std::move(stt_ptr)); + + result.test_str_eq("stt_ptr_move", *stt_ptr_move.get(), "wrapped ptr"); + + return result; + } + + static Test::Result test_unwrapping_return_reference_type() { + Test::Result result("unwrapping a strong type wisely chooses return reference type"); + + using Strong_String = Botan::Strong; + + Strong_String stt("wrapped"); // NOLINT(*-const-correctness) clang-tidy bug + const Strong_String stt_const("const wrapped"); + + using lvalue_ref = decltype(Botan::unwrap_strong_type(stt)); + result.test_is_true("unpack() on non-const is an lvalue reference", std::is_lvalue_reference_v); + result.test_is_true("unpack() on non-const is a non-const lvalue reference", + !std::is_const_v>); + result.test_is_true( + "wrapped_type on non-const lvalue strong type", + std::same_as, std::remove_cvref_t>); + + using const_lvalue_ref = decltype(Botan::unwrap_strong_type(stt_const)); + result.test_is_true("unpack() on const is an lvalue reference", std::is_lvalue_reference_v); + result.test_is_true("unpack() on const is a const lvalue reference", + std::is_const_v>); + result.test_is_true( + "wrapped_type on const lvalue strong type", + std::same_as, std::remove_cvref_t>); + + using lvalue = decltype(Botan::unwrap_strong_type(std::move(stt))); + result.test_is_true("unpack() on rvalue reference is an rvalue reference", std::is_rvalue_reference_v); + result.test_is_true("unpack() on rvalue is a non-const rvalue reference", + !std::is_const_v>); + result.test_is_true( + "wrapped_type on rvalue reference strong type", + std::same_as, std::remove_cvref_t>); + + using lvalue2 = decltype(Botan::unwrap_strong_type(Strong_String("wrapped rvalue"))); + result.test_is_true("unpack() on rvalue is an rvalue reference", std::is_rvalue_reference_v); + result.test_is_true("unpack() on rvalue is a non-const rvalue reference", + !std::is_const_v>); + result.test_is_true("wrapped_type on rvalue strong type", + std::same_as, + std::remove_cvref_t>); + + return result; + } + + static Test::Result test_unwrapping_non_strong_type_return_type() { + Test::Result result("unwrapping a non-strong type does not alter return reference type"); + + std::string stt("wrapped"); // NOLINT(*-const-correctness) required for the test + const std::string stt_const("const wrapped"); + + using lvalue_ref = decltype(Botan::unwrap_strong_type(stt)); + result.test_is_true("unpack() on non-const is an lvalue reference", std::is_lvalue_reference_v); + result.test_is_true("unpack() on non-const is a non-const lvalue reference", + !std::is_const_v>); + result.test_is_true( + "wrapped_type on lvalue non-strong type", + std::same_as, std::remove_cvref_t>); + + using const_lvalue_ref = decltype(Botan::unwrap_strong_type(stt_const)); + result.test_is_true("unpack() on const is an lvalue reference", std::is_lvalue_reference_v); + result.test_is_true("unpack() on const is a const lvalue reference", + std::is_const_v>); + result.test_is_true( + "wrapped_type on const lvalue non-strong type", + std::same_as, std::remove_cvref_t>); + + using lvalue = decltype(Botan::unwrap_strong_type(std::move(stt))); + result.test_is_true("unpack() on rvalue reference is an rvalue reference", std::is_rvalue_reference_v); + result.test_is_true("unpack() on rvalue is a non-const rvalue reference", + !std::is_const_v>); + result.test_is_true( + "wrapped_type on rvalue reference non-strong type", + std::same_as, std::remove_cvref_t>); + + using lvalue2 = decltype(Botan::unwrap_strong_type(std::string("rvalue"))); + result.test_is_true("unpack() on rvalue reference is an rvalue reference", + std::is_rvalue_reference_v); + result.test_is_true("unpack() on rvalue is a non-const rvalue reference", + !std::is_const_v>); + result.test_is_true("wrapped_type on rvalue non-strong type", + std::same_as, + std::remove_cvref_t>); + + return result; + } + + static Test::Result test_unwrapping_object_from_strong_type() { + Test::Result result("generically unwrapping an object from a strong type"); + + using Strong_String = Botan::Strong; + using Strong_Unique = Botan::Strong, struct Strong_Unique_>; + + Strong_String stt("wrapped lvalue"); + Strong_String stt_move("wrapped lvalue to be moved"); + const Strong_String const_stt("wrapped const lvalue"); + + auto& unwrapped_stt = Botan::unwrap_strong_type(stt); + const auto& unwrapped_const_stt = Botan::unwrap_strong_type(const_stt); + auto unwrapped_rvalue = Botan::unwrap_strong_type(std::move(stt_move)); + auto unwrapped_rvalue2 = Botan::unwrap_strong_type(Strong_String("wrapped rvalue")); + + result.test_str_eq("unwrapped_stt", unwrapped_stt, "wrapped lvalue"); + result.test_str_eq("unwrapped_const_stt", unwrapped_const_stt, "wrapped const lvalue"); + result.test_str_eq("unwrapped_rvalue", unwrapped_rvalue, "wrapped lvalue to be moved"); + result.test_str_eq("unwrapped_rvalue2", unwrapped_rvalue2, "wrapped rvalue"); + + Strong_Unique stt_ptr(std::make_unique("wrapped ptr")); + Strong_Unique stt_ptr_move(std::make_unique("wrapped ptr to be moved")); + + auto& unwrapped_ptr = Botan::unwrap_strong_type(stt_ptr); + auto unwrapped_ptr_move = Botan::unwrap_strong_type(std::move(stt_ptr_move)); + auto unwrapped_ptr_rvalue = Botan::unwrap_strong_type(std::make_unique("wrapped ptr rvalue")); + + result.test_str_eq("unwrapped_ptr", *unwrapped_ptr, "wrapped ptr"); + result.test_str_eq("unwrapped_ptr_move", *unwrapped_ptr_move, "wrapped ptr to be moved"); + result.test_str_eq("unwrapped_ptr_rvalue", *unwrapped_ptr_rvalue, "wrapped ptr rvalue"); + + return result; + } + + static Test::Result test_unwrapping_non_strong_type() { + Test::Result result("generically unwrapping an object that isn't a strong type"); + + std::string stt("wrapped lvalue"); + std::string stt_move("wrapped lvalue to be moved"); + const std::string const_stt("wrapped const lvalue"); + + auto& unwrapped_stt = Botan::unwrap_strong_type(stt); + const auto& unwrapped_const_stt = Botan::unwrap_strong_type(const_stt); + auto unwrapped_rvalue = Botan::unwrap_strong_type(std::move(stt_move)); + auto unwrapped_rvalue2 = Botan::unwrap_strong_type(std::string("wrapped rvalue")); + + result.test_str_eq("unwrapped_stt", unwrapped_stt, "wrapped lvalue"); + result.test_str_eq("unwrapped_const_stt", unwrapped_const_stt, "wrapped const lvalue"); + result.test_str_eq("unwrapped_rvalue", unwrapped_rvalue, "wrapped lvalue to be moved"); + result.test_str_eq("unwrapped_rvalue2", unwrapped_rvalue2, "wrapped rvalue"); + + std::unique_ptr stt_ptr(std::make_unique("wrapped ptr")); + std::unique_ptr stt_ptr_move(std::make_unique("wrapped ptr to be moved")); + + auto& unwrapped_ptr = Botan::unwrap_strong_type(stt_ptr); + auto unwrapped_ptr_move = Botan::unwrap_strong_type(std::move(stt_ptr_move)); + auto unwrapped_ptr_rvalue = Botan::unwrap_strong_type(std::make_unique("wrapped ptr rvalue")); + + result.test_str_eq("unwrapped_ptr", *unwrapped_ptr, "wrapped ptr"); + result.test_str_eq("unwrapped_ptr_move", *unwrapped_ptr_move, "wrapped ptr to be moved"); + result.test_str_eq("unwrapped_ptr_rvalue", *unwrapped_ptr_rvalue, "wrapped ptr rvalue"); + + return result; + } +}; - return result; -} - -std::vector test_wrapping_unwrapping() { - using Strong_String = Botan::Strong; - using Strong_Unique = Botan::Strong, struct Strong_Unique_>; - - using namespace std::string_literals; - - return { - CHECK("generically wrapping an object into a strong type", - [&](Test::Result& result) { - const std::string expl("explicit creation"s); - std::string rval("rvalue-ref creation"s); - auto stt_copy = Botan::wrap_strong_type(expl); - auto stt_implicit = Botan::wrap_strong_type("implicit conversion from const char*"); - auto stt_rvalue_ref = Botan::wrap_strong_type(std::move(rval)); - auto stt_rvalue = Botan::wrap_strong_type("rvalue creation from std::string (literal)"s); - - result.test_eq("stt_copy", stt_copy.get(), "explicit creation"); - result.test_eq("stt_implicit", stt_implicit.get(), "implicit conversion from const char*"); - result.test_eq("stt_rvalue_ref", stt_rvalue_ref.get(), "rvalue-ref creation"); - result.test_eq("stt_rvalue", stt_rvalue.get(), "rvalue creation from std::string (literal)"); - - // unique_ptr does not support copy construction and prohibits - // implicit conversion from a raw pointer of its wrapped type. - auto rval_ptr = std::make_unique("rvalue creation from ptr"); - auto stt_implicit_ptr = - Botan::wrap_strong_type(new std::string("implicit creation from ptr")); - auto stt_rvalue_ptr = Botan::wrap_strong_type(std::move(rval_ptr)); - - result.test_eq("stt_implicit_ptr", *stt_implicit_ptr.get(), "implicit creation from ptr"); - result.test_eq("stt_rvalue_ptr", *stt_rvalue_ptr.get(), "rvalue creation from ptr"); - }), - - CHECK("generically wrapping a strong type from itself", - [&](Test::Result& result) { - const Strong_String stt("wrapped"); - Strong_String stt_rval("wrapped and moved"); - - auto stt_copy = Botan::wrap_strong_type(stt); - auto stt_move = Botan::wrap_strong_type(std::move(stt_rval)); - auto stt_inplace = Botan::wrap_strong_type(Strong_String("inplace")); - - result.test_eq("stt_copy", stt_copy.get(), "wrapped"); - result.test_eq("stt_move", stt_move.get(), "wrapped and moved"); - result.test_eq("stt_inplace", stt_inplace.get(), "inplace"); - - Strong_Unique stt_ptr(std::make_unique("wrapped ptr")); - - auto stt_ptr_move = Botan::wrap_strong_type(std::move(stt_ptr)); - - result.test_eq("stt_ptr_move", *stt_ptr_move.get(), "wrapped ptr"); - }), - - CHECK("unwrapping a strong type wisely chooses return reference type", - [&](Test::Result& result) { - Strong_String stt("wrapped"); - const Strong_String stt_const("const wrapped"); - - using lvalue_ref = decltype(Botan::unwrap_strong_type(stt)); - result.confirm("unpack() on non-const is an lvalue reference", std::is_lvalue_reference_v); - result.confirm("unpack() on non-const is a non-const lvalue reference", - !std::is_const_v>); - result.confirm( - "wrapped_type on non-const lvalue strong type", - std::same_as, std::remove_cvref_t>); - - using const_lvalue_ref = decltype(Botan::unwrap_strong_type(stt_const)); - result.confirm("unpack() on const is an lvalue reference", std::is_lvalue_reference_v); - result.confirm("unpack() on const is a const lvalue reference", - std::is_const_v>); - result.confirm( - "wrapped_type on const lvalue strong type", - std::same_as, std::remove_cvref_t>); - - using lvalue = decltype(Botan::unwrap_strong_type(std::move(stt))); - result.confirm("unpack() on rvalue reference is an rvalue reference", - std::is_rvalue_reference_v); - result.confirm("unpack() on rvalue is a non-const rvalue reference", - !std::is_const_v>); - result.confirm( - "wrapped_type on rvalue reference strong type", - std::same_as, std::remove_cvref_t>); - - using lvalue2 = decltype(Botan::unwrap_strong_type(Strong_String("wrapped rvalue"))); - result.confirm("unpack() on rvalue is an rvalue reference", std::is_rvalue_reference_v); - result.confirm("unpack() on rvalue is a non-const rvalue reference", - !std::is_const_v>); - result.confirm("wrapped_type on rvalue strong type", - std::same_as, - std::remove_cvref_t>); - }), - - CHECK( - "unwrapping a non-strong type does not alter return reference type", - [](Test::Result& result) { - std::string stt("wrapped"); - const std::string stt_const("const wrapped"); - - using lvalue_ref = decltype(Botan::unwrap_strong_type(stt)); - result.confirm("unpack() on non-const is an lvalue reference", std::is_lvalue_reference_v); - result.confirm("unpack() on non-const is a non-const lvalue reference", - !std::is_const_v>); - result.confirm( - "wrapped_type on lvalue non-strong type", - std::same_as, std::remove_cvref_t>); - - using const_lvalue_ref = decltype(Botan::unwrap_strong_type(stt_const)); - result.confirm("unpack() on const is an lvalue reference", std::is_lvalue_reference_v); - result.confirm("unpack() on const is a const lvalue reference", - std::is_const_v>); - result.confirm("wrapped_type on const lvalue non-strong type", - std::same_as, - std::remove_cvref_t>); - - using lvalue = decltype(Botan::unwrap_strong_type(std::move(stt))); - result.confirm("unpack() on rvalue reference is an rvalue reference", std::is_rvalue_reference_v); - result.confirm("unpack() on rvalue is a non-const rvalue reference", - !std::is_const_v>); - result.confirm( - "wrapped_type on rvalue reference non-strong type", - std::same_as, std::remove_cvref_t>); - - using lvalue2 = decltype(Botan::unwrap_strong_type(std::string("rvalue"))); - result.confirm("unpack() on rvalue reference is an rvalue reference", std::is_rvalue_reference_v); - result.confirm("unpack() on rvalue is a non-const rvalue reference", - !std::is_const_v>); - result.confirm("wrapped_type on rvalue non-strong type", - std::same_as, - std::remove_cvref_t>); - }), - - CHECK("generically unwrapping an object from a strong type", - [&](Test::Result& result) { - Strong_String stt("wrapped lvalue"); - Strong_String stt_move("wrapped lvalue to be moved"); - const Strong_String const_stt("wrapped const lvalue"); - - auto& unwrapped_stt = Botan::unwrap_strong_type(stt); - auto& unwrapped_const_stt = Botan::unwrap_strong_type(const_stt); - auto unwrapped_rvalue = Botan::unwrap_strong_type(std::move(stt_move)); - auto unwrapped_rvalue2 = Botan::unwrap_strong_type(Strong_String("wrapped rvalue")); - - result.test_eq("unwrapped_stt", unwrapped_stt, "wrapped lvalue"); - result.test_eq("unwrapped_const_stt", unwrapped_const_stt, "wrapped const lvalue"); - result.test_eq("unwrapped_rvalue", unwrapped_rvalue, "wrapped lvalue to be moved"); - result.test_eq("unwrapped_rvalue2", unwrapped_rvalue2, "wrapped rvalue"); - - Strong_Unique stt_ptr(std::make_unique("wrapped ptr")); - Strong_Unique stt_ptr_move(std::make_unique("wrapped ptr to be moved")); - - auto& unwrapped_ptr = Botan::unwrap_strong_type(stt_ptr); - auto unwrapped_ptr_move = Botan::unwrap_strong_type(std::move(stt_ptr_move)); - auto unwrapped_ptr_rvalue = - Botan::unwrap_strong_type(std::make_unique("wrapped ptr rvalue")); - - result.test_eq("unwrapped_ptr", *unwrapped_ptr, "wrapped ptr"); - result.test_eq("unwrapped_ptr_move", *unwrapped_ptr_move, "wrapped ptr to be moved"); - result.test_eq("unwrapped_ptr_rvalue", *unwrapped_ptr_rvalue, "wrapped ptr rvalue"); - }), - - CHECK("generically unwrapping an object that isn't a strong type", - [&](Test::Result& result) { - std::string stt("wrapped lvalue"); - std::string stt_move("wrapped lvalue to be moved"); - const std::string const_stt("wrapped const lvalue"); - - auto& unwrapped_stt = Botan::unwrap_strong_type(stt); - auto& unwrapped_const_stt = Botan::unwrap_strong_type(const_stt); - auto unwrapped_rvalue = Botan::unwrap_strong_type(std::move(stt_move)); - auto unwrapped_rvalue2 = Botan::unwrap_strong_type(std::string("wrapped rvalue")); - - result.test_eq("unwrapped_stt", unwrapped_stt, "wrapped lvalue"); - result.test_eq("unwrapped_const_stt", unwrapped_const_stt, "wrapped const lvalue"); - result.test_eq("unwrapped_rvalue", unwrapped_rvalue, "wrapped lvalue to be moved"); - result.test_eq("unwrapped_rvalue2", unwrapped_rvalue2, "wrapped rvalue"); - - std::unique_ptr stt_ptr(std::make_unique("wrapped ptr")); - std::unique_ptr stt_ptr_move(std::make_unique("wrapped ptr to be moved")); - - auto& unwrapped_ptr = Botan::unwrap_strong_type(stt_ptr); - auto unwrapped_ptr_move = Botan::unwrap_strong_type(std::move(stt_ptr_move)); - auto unwrapped_ptr_rvalue = - Botan::unwrap_strong_type(std::make_unique("wrapped ptr rvalue")); - - result.test_eq("unwrapped_ptr", *unwrapped_ptr, "wrapped ptr"); - result.test_eq("unwrapped_ptr_move", *unwrapped_ptr_move, "wrapped ptr to be moved"); - result.test_eq("unwrapped_ptr_rvalue", *unwrapped_ptr_rvalue, "wrapped ptr rvalue"); - }), - }; -} +BOTAN_REGISTER_TEST("utils", "strong_type", Strong_Type_Tests); } // namespace -BOTAN_REGISTER_TEST_FN("utils", - "strong_type", - test_strong_type, - test_container_strong_type, - test_integer_strong_type, - test_strong_span, - test_wrapping_unwrapping); - } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_tests.cpp botan3-3.12.0+dfsg/src/tests/test_tests.cpp --- botan3-3.7.1+dfsg/src/tests/test_tests.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_tests.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -6,12 +6,16 @@ #include "tests.h" +#include + #if defined(BOTAN_HAS_BIGINT) #include #endif namespace Botan_Tests { +namespace { + /* * Test the test framework :) */ @@ -56,66 +60,63 @@ { Test::Result test_result(testcase_name); - std::vector vec1(5), vec2(3, 9); - test_result.test_eq("test vectors equal", vec1, vec2); - verify_failure("test vectors equal", result, test_result); - } - - { - Test::Result test_result(testcase_name); - std::vector vec1(5), vec2(5); - test_result.test_ne("test vectors not equal", vec1, vec2); + std::vector vec1(5); + std::vector vec2(3, 9); + test_result.test_bin_eq("test vectors equal", vec1, vec2); verify_failure("test vectors equal", result, test_result); } { Test::Result test_result(testcase_name); - std::vector vec1(5), vec2(5); - test_result.test_ne("test arrays not equal", vec1.data(), vec1.size(), vec2.data(), vec2.size()); + std::vector vec1(5); + std::vector vec2(5); + test_result.test_bin_ne("test vectors not equal", vec1, vec2); verify_failure("test vectors equal", result, test_result); } { Test::Result test_result(testcase_name); - size_t x = 5, y = 6; - test_result.test_eq("test ints equal", x, y); + const size_t x = 5; + const size_t y = 6; + test_result.test_sz_eq("test ints equal", x, y); verify_failure("test ints equal", result, test_result); } { Test::Result test_result(testcase_name); - size_t x = 5, y = 5; - test_result.test_ne("test ints not equal", x, y); + const size_t x = 5; + const size_t y = 5; + test_result.test_sz_ne("test ints not equal", x, y); verify_failure("test ints not equal", result, test_result); } { Test::Result test_result(testcase_name); - test_result.test_is_nonempty("empty", ""); - verify_failure("test_is_nonempty", result, test_result); + test_result.test_str_not_empty("empty", ""); + verify_failure("test_str_not_empty", result, test_result); } { Test::Result test_result(testcase_name); - test_result.test_lt("not less", 5, 5); + test_result.test_sz_lt("not less", 5, 5); verify_failure("test_lt", result, test_result); } { Test::Result test_result(testcase_name); - test_result.test_lte("not lte", 6, 5); + test_result.test_sz_lte("not lte", 6, 5); verify_failure("test_lte", result, test_result); } { Test::Result test_result(testcase_name); - test_result.test_gte("not gte", 5, 6); + test_result.test_sz_gte("not gte", 5, 6); verify_failure("test_gte", result, test_result); } { Test::Result test_result(testcase_name); - test_result.test_ne("string ne", "foo", "foo"); + test_result.test_str_ne("string ne", "foo", "foo"); verify_failure("test_ne", result, test_result); } @@ -184,7 +185,7 @@ Test::Result test_result(testcase_name); const auto x = Botan::BigInt::from_word(5); const auto y = Botan::BigInt::from_word(6); - test_result.test_eq("test ints equal", x, y); + test_result.test_bn_eq("test ints equal", x, y); verify_failure("test ints equal", result, test_result); } @@ -192,7 +193,7 @@ Test::Result test_result(testcase_name); const auto x = Botan::BigInt::from_word(5); const auto y = Botan::BigInt::from_word(5); - test_result.test_ne("test ints not equal", x, y); + test_result.test_bn_ne("test ints not equal", x, y); verify_failure("test ints not equal", result, test_result); } #endif @@ -214,8 +215,8 @@ histogram[rng->next_byte()] += 1; } - for(size_t i = 0; i != 256; ++i) { - if(histogram[i] < RUNS / 2 || histogram[i] > RUNS * 2) { + for(const size_t count : histogram) { + if(count < RUNS / 2 || count > RUNS * 2) { result.test_failure("Testsuite_RNG produced non-uniform output"); } else { result.test_success("Testsuite_RNG seemed roughly uniform"); @@ -230,7 +231,7 @@ result.test_success("Got expected failure for " + what); const std::string result_str = test_result.result_string(); - result.confirm("result string contains FAIL", result_str.find("FAIL") != std::string::npos); + result.test_is_true("result string contains FAIL", result_str.find("FAIL") != std::string::npos); } else { result.test_failure("Expected test to fail for " + what); } @@ -239,4 +240,6 @@ BOTAN_REGISTER_TEST("utils", "testcode", Test_Tests); +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_thread_utils.cpp botan3-3.12.0+dfsg/src/tests/test_thread_utils.cpp --- botan3-3.7.1+dfsg/src/tests/test_thread_utils.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_thread_utils.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -45,10 +45,10 @@ futures[i].get(); result.test_failure("Expected future to throw"); } catch(size_t x) { - result.test_eq("Expected thrown value", x, i); + result.test_sz_eq("Expected thrown value", x, i); } } else { - result.test_eq("Expected return value", futures[i].get(), i); + result.test_sz_eq("Expected return value", futures[i].get(), i); } } diff -Nru botan3-3.7.1+dfsg/src/tests/test_tls.cpp botan3-3.12.0+dfsg/src/tests/test_tls.cpp --- botan3-3.7.1+dfsg/src/tests/test_tls.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_tls.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,19 +11,43 @@ #if defined(BOTAN_HAS_TLS) #include "test_rng.h" + #include #include + #include #include #include + #include #include + #include + #include + #include #if defined(BOTAN_HAS_TLS_CBC) + #include + #include #include #endif + #if defined(BOTAN_HAS_TLS_NULL) + #include + #endif + + #if defined(BOTAN_HAS_TLS_13) + #include + #endif + + #if defined(BOTAN_HAS_TLS_12) + #include + #include + #include + #include + #endif #endif namespace Botan_Tests { +namespace { + #if defined(BOTAN_HAS_TLS) class TLS_Session_Tests final : public Test { @@ -31,56 +55,55 @@ std::vector run() override { Test::Result result("TLS::Session"); - Botan::TLS::Session session(Botan::secure_vector{0xCC, 0xDD}, - Botan::TLS::Protocol_Version::TLS_V12, - 0xC02F, - Botan::TLS::Connection_Side::Client, - true, - false, - std::vector(), - Botan::TLS::Server_Information("server"), - 0x0000, - std::chrono::system_clock::now()); + const Botan::TLS::Session session(Botan::secure_vector(48, 0xCC), + Botan::TLS::Protocol_Version::TLS_V12, + 0xC02F, + Botan::TLS::Connection_Side::Client, + true, + false, + std::vector(), + Botan::TLS::Server_Information("server"), + 0x0000, + std::chrono::system_clock::now()); const std::string pem = session.PEM_encode(); - Botan::TLS::Session session_from_pem(pem); - result.test_eq("Roundtrip from pem", session.DER_encode(), session_from_pem.DER_encode()); + const Botan::TLS::Session session_from_pem(pem); + result.test_bin_eq("Roundtrip from pem", session.DER_encode(), session_from_pem.DER_encode()); const auto der = session.DER_encode(); - Botan::TLS::Session session_from_der(der); - result.test_eq("Roundtrip from der", session.DER_encode(), session_from_der.DER_encode()); + const Botan::TLS::Session session_from_der(der); + result.test_bin_eq("Roundtrip from der", session.DER_encode(), session_from_der.DER_encode()); const Botan::SymmetricKey key("ABCDEF"); 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()); - - const std::vector expected_hdr = Botan::hex_decode("068B5A9D396C0000F2322CAE"); + result.test_bin_ne("TLS session encryption is non-deterministic", ctext1, ctext2); - result.test_eq("tls", "TLS session encryption same header", ctext1.data(), 12, expected_hdr.data(), 12); - result.test_eq("tls", "TLS session encryption same header", ctext2.data(), 12, expected_hdr.data(), 12); + result.test_bin_eq( + "TLS session encryption same header", std::span{ctext1}.first(12), "068B5A9D396C0000F2322CAE"); + result.test_bin_eq( + "TLS session encryption same header", std::span{ctext2}.first(12), "068B5A9D396C0000F2322CAE"); - Botan::TLS::Session dsession = Botan::TLS::Session::decrypt(ctext1.data(), ctext1.size(), key); + const Botan::TLS::Session dsession = Botan::TLS::Session::decrypt(ctext1.data(), ctext1.size(), key); Fixed_Output_RNG frng1("00112233445566778899AABBCCDDEEFF802802802802802802802802"); const std::vector ctextf1 = session.encrypt(key, frng1); Fixed_Output_RNG frng2("00112233445566778899AABBCCDDEEFF802802802802802802802802"); const std::vector ctextf2 = session.encrypt(key, frng2); - result.test_eq("Only randomness comes from RNG", ctextf1, ctextf2); + result.test_bin_eq("Only randomness comes from RNG", ctextf1, ctextf2); - Botan::TLS::Session session2(Botan::secure_vector{0xCC, 0xEE}, - Botan::TLS::Protocol_Version::TLS_V12, - 0xBAAD, // cipher suite does not exist - Botan::TLS::Connection_Side::Client, - true, - false, - std::vector(), - Botan::TLS::Server_Information("server"), - 0x0000, - std::chrono::system_clock::now()); + const Botan::TLS::Session session2(Botan::secure_vector{0xCC, 0xEE}, + Botan::TLS::Protocol_Version::TLS_V12, + 0xBAAD, // cipher suite does not exist + Botan::TLS::Connection_Side::Client, + true, + false, + std::vector(), + Botan::TLS::Server_Information("server"), + 0x0000, + std::chrono::system_clock::now()); const std::string pem_with_unknown_ciphersuite = session2.PEM_encode(); result.test_throws("unknown ciphersuite during session parsing", @@ -103,10 +126,10 @@ const std::vector record = vars.get_req_bin("Record"); const size_t output = vars.get_req_sz("Output"); - uint16_t res = Botan::TLS::check_tls_cbc_padding(record.data(), record.size()); + const uint16_t res = Botan::TLS::check_tls_cbc_padding(record.data(), record.size()); Test::Result result("TLS CBC padding check"); - result.test_eq("Expected", res, output); + result.test_sz_eq("Expected", res, output); return result; } }; @@ -194,7 +217,7 @@ const bool is_valid = vars.get_req_sz("Valid") == 1; // todo test permutations - bool encrypt_then_mac = false; + const bool encrypt_then_mac = false; Botan::TLS::TLS_CBC_HMAC_AEAD_Decryption tls_cbc(std::make_unique(block_size), std::make_unique(mac_len), @@ -228,7 +251,209 @@ } }; +class TLS_CBC_KAT_Tests final : public Text_Based_Test { + public: + TLS_CBC_KAT_Tests() : + Text_Based_Test( + "tls_cbc_kat.vec", + "BlockCipher,MAC,KeylenCipher,KeylenMAC,EncryptThenMAC,Protocol,Key,AssociatedData,Nonce,Plaintext,Ciphertext") { + } + + Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override { + Test::Result result("TLS CBC KAT"); + + run_kat(result, vars); + run_kat(result, vars); + + return result; + } + + bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override { + try { + std::ignore = get_cipher_and_mac(vars); + return false; + } catch(const Botan::Lookup_Error&) { + return true; + } + } + + private: + [[nodiscard]] static std::pair, + std::unique_ptr> + get_cipher_and_mac(const VarMap& vars) { + return { + Botan::BlockCipher::create_or_throw(vars.get_req_str("BlockCipher")), + Botan::MessageAuthenticationCode::create_or_throw(vars.get_req_str("MAC")), + }; + } + + template + requires(std::same_as || + std::same_as) + static void run_kat(Test::Result& result, const VarMap& vars) { + constexpr bool encrypt = std::same_as; + constexpr auto direction = [] { + if constexpr(encrypt) { + return "encryption"; + } else { + return "decryption"; + } + }(); + + const auto keylen_cipher = vars.get_req_sz("KeylenCipher"); + const auto keylen_mac = vars.get_req_sz("KeylenMAC"); + const auto encrypt_then_mac = vars.get_req_bool("EncryptThenMAC"); + const auto protocol = [&] { + const auto p = vars.get_req_str("Protocol"); + if(p == "TLS") { + return Botan::TLS::Version_Code::TLS_V12; + } else if(p == "DTLS") { + return Botan::TLS::Version_Code::DTLS_V12; + } else { + throw Test_Error("unexpected protocol version"); + } + }(); + + const auto key = vars.get_req_bin("Key"); + const auto ad = vars.get_req_bin("AssociatedData"); + const auto nonce = vars.get_req_bin("Nonce"); + const auto pt = vars.get_req_bin("Plaintext"); + const auto ct = vars.get_req_bin("Ciphertext"); + + auto [cipher, mac] = get_cipher_and_mac(vars); + + auto tls_cbc = T(std::move(cipher), std::move(mac), keylen_cipher, keylen_mac, protocol, encrypt_then_mac); + + tls_cbc.set_key(key); + tls_cbc.set_associated_data(ad); + + std::vector in(pt.begin(), pt.end()); + std::vector out(ct.begin(), ct.end()); + + if constexpr(!encrypt) { + std::swap(in, out); + } + + // Test 1: process the entire message at once + std::vector inout = in; + tls_cbc.start(nonce); + tls_cbc.finish(inout); // in-place processing ('in' should now contain 'out') + result.test_bin_eq(std::string("expected output of ") + direction, inout, out); + + // Test 2: process the message in chunks + auto in_span = std::span{in}; + tls_cbc.start(nonce); + constexpr size_t chunk_size = 7; + while(in_span.size() >= chunk_size && in_span.size() > tls_cbc.minimum_final_size() + chunk_size) { + tls_cbc.process(in_span.first(chunk_size)); + in_span = in_span.subspan(chunk_size); + } + + std::vector chunked_out(in_span.begin(), in_span.end()); + tls_cbc.finish(chunked_out); + result.test_bin_eq(std::string("expected output with chunking of ") + direction, chunked_out, out); + } +}; + BOTAN_REGISTER_TEST("tls", "tls_cbc", TLS_CBC_Tests); +BOTAN_REGISTER_TEST("tls", "tls_cbc_kat", TLS_CBC_KAT_Tests); + + #endif + + #if defined(BOTAN_HAS_TLS_NULL) + +class TLS_Null_Tests final : public Text_Based_Test { + public: + TLS_Null_Tests() : Text_Based_Test("tls_null.vec", "Hash,Key,AssociatedData,Message,Fragment") {} + + void encryption_test(Test::Result& result, + const std::string& hash, + const std::vector& key, + const std::vector& associated_data, + const std::vector& message, + const std::vector& expected_tls_fragment) { + auto mac = Botan::MessageAuthenticationCode::create_or_throw(Botan::fmt("HMAC({})", hash)); + + const auto mac_output_length = mac->output_length(); + Botan::TLS::TLS_NULL_HMAC_AEAD_Encryption tls_null_encrypt(std::move(mac), mac_output_length); + + tls_null_encrypt.set_key(key); + tls_null_encrypt.set_associated_data(associated_data); + tls_null_encrypt.start(); + + Botan::secure_vector buffer(message.begin(), message.end()); + tls_null_encrypt.finish(buffer); + + result.test_bin_eq("Encrypted TLS fragment matches expectation", buffer, expected_tls_fragment); + } + + void decryption_test(Test::Result& result, + const std::string& hash, + const std::vector& key, + const std::vector& associated_data, + const std::vector& expected_message, + const std::vector& tls_fragment, + const std::string& header) { + auto mac = Botan::MessageAuthenticationCode::create_or_throw(Botan::fmt("HMAC({})", hash)); + + const auto mac_output_length = mac->output_length(); + Botan::TLS::TLS_NULL_HMAC_AEAD_Decryption tls_null_decrypt(std::move(mac), mac_output_length); + + tls_null_decrypt.set_key(key); + tls_null_decrypt.set_associated_data(associated_data); + tls_null_decrypt.start(); + + Botan::secure_vector buffer(tls_fragment.begin(), tls_fragment.end()); + + if(header == "InvalidMAC") { + result.test_throws("TLS_NULL_HMAC_AEAD_Decryption::finish()", "Message authentication failure", [&]() { + tls_null_decrypt.finish(buffer, 0); + }); + } else { + tls_null_decrypt.finish(buffer, 0); + result.test_bin_eq("Decrypted TLS fragment matches expectation", buffer, expected_message); + } + } + + void invalid_ad_length_test(Test::Result& result, + const std::string& hash, + const std::vector& associated_data) { + auto mac = Botan::MessageAuthenticationCode::create_or_throw(Botan::fmt("HMAC({})", hash)); + + const auto mac_output_length = mac->output_length(); + Botan::TLS::TLS_NULL_HMAC_AEAD_Decryption tls_null_decrypt(std::move(mac), mac_output_length); + + result.test_throws("TLS_NULL_HMAC_AEAD_Decryption::set_associated_data()", + [&]() { tls_null_decrypt.set_associated_data(associated_data); }); + } + + Test::Result run_one_test(const std::string& header, const VarMap& vars) override { + Test::Result result("TLS Null Cipher"); + + const std::string hash = vars.get_req_str("Hash"); + const std::vector key = vars.get_req_bin("Key"); + const std::vector associated_data = vars.get_req_bin("AssociatedData"); + const std::vector expected_message = vars.get_req_bin("Message"); + const std::vector tls_fragment = vars.get_req_bin("Fragment"); + + if(header.empty()) { + encryption_test(result, hash, key, associated_data, expected_message, tls_fragment); + decryption_test(result, hash, key, associated_data, expected_message, tls_fragment, header); + } + + if(header == "InvalidMAC") { + decryption_test(result, hash, key, associated_data, expected_message, tls_fragment, header); + } + + if(header == "InvalidAssociatedDataLength") { + invalid_ad_length_test(result, hash, associated_data); + } + + return result; + } +}; + +BOTAN_REGISTER_TEST("tls", "tls_null", TLS_Null_Tests); #endif @@ -277,13 +502,13 @@ for(auto alert : alert_types) { const std::string str = Botan::TLS::Alert(alert).type_string(); - result.test_eq("No duplicate strings", seen.count(str), 0); + result.test_sz_eq("No duplicate strings", seen.count(str), 0); seen.insert(str); } - Botan::TLS::Alert unknown_alert = Botan::TLS::Alert({01, 66}); + const Botan::TLS::Alert unknown_alert = Botan::TLS::Alert({01, 66}); - result.test_eq("Unknown alert str", unknown_alert.type_string(), "unrecognized_alert_66"); + result.test_str_eq("Unknown alert str", unknown_alert.type_string(), "unrecognized_alert_66"); return {result}; } @@ -291,6 +516,87 @@ BOTAN_REGISTER_TEST("tls", "tls_alert_strings", Test_TLS_Alert_Strings); + #if defined(BOTAN_HAS_TLS_12) + +// Test that NoRenegotiation warning only tears down a pending state if +// there is already an active state +class Test_TLS12_NoRenegotiation_During_Initial_Handshake : public Test { + private: + class Capture_Callbacks final : public Botan::TLS::Callbacks { + public: + void tls_emit_data(std::span bits) override { + m_emitted.insert(m_emitted.end(), bits.begin(), bits.end()); + } + + void tls_record_received(uint64_t /*seq*/, std::span /*data*/) override {} + + void tls_alert(Botan::TLS::Alert alert) override { m_alerts.push_back(alert); } + + std::span alerts() const { return m_alerts; } + + std::span emitted() const { return m_emitted; } + + private: + std::vector m_emitted; + std::vector m_alerts; + }; + + public: + std::vector run() override { + Test::Result result("TLS 1.2 NoRenegotiation alert during initial handshake"); + + auto rng = Test::new_shared_rng(this->test_name()); + auto callbacks = std::make_shared(); + auto sessions = std::make_shared(rng); + auto creds = std::make_shared(); + auto policy = std::make_shared(); + + Botan::TLS::Client client(callbacks, + sessions, + creds, + policy, + rng, + Botan::TLS::Server_Information("server.example.com"), + Botan::TLS::Protocol_Version::TLS_V12); + + result.test_is_true("client emitted ClientHello", !callbacks->emitted().empty()); + result.test_is_true("client not active yet", !client.is_active()); + + // RFC 5246: record header type=21 (alert) version=0x0303, length=2, followed by + // alert level=1 (warning), description=100 (no_renegotiation) + const std::vector no_renegotiation_alert = {21, 0x03, 0x03, 0x00, 0x02, 1, 100}; + result.test_no_throw("alert record accepted", [&]() { (void)client.received_data(no_renegotiation_alert); }); + + const auto alerts = callbacks->alerts(); + result.test_sz_eq("alert was delivered to application", alerts.size(), 1); + if(!alerts.empty()) { + result.test_is_true("alert was no_renegotiation", alerts[0].type() == Botan::TLS::Alert::NoRenegotiation); + } + result.test_is_true("client still not active", !client.is_active()); + result.test_is_true("client not closed", !client.is_closed()); + + /* + Here we use renegotiate as a probe as to the internal state. + + If there is already a pending state, renegotiate silently returns. But if + the state is torn down then renegotiate will throw because there is neither + an active or pending state. + */ + result.test_no_throw("pending handshake state survived", [&]() { client.renegotiate(); }); + + return {result}; + } +}; + +BOTAN_REGISTER_TEST("tls", + "tls12_no_renegotiation_during_initial_handshake", + Test_TLS12_NoRenegotiation_During_Initial_Handshake); + + #endif + + #if defined(BOTAN_HAS_TLS_12) && defined(BOTAN_HAS_TLS_13) && defined(BOTAN_HAS_TLS_13_PQC) && \ + defined(BOTAN_HAS_X25519) && defined(BOTAN_HAS_X448) + class Test_TLS_Policy_Text : public Test { public: std::vector run() override { @@ -301,21 +607,48 @@ for(const std::string& policy : policies) { const std::string from_policy_obj = tls_policy_string(policy); - #if defined(BOTAN_HAS_TLS_13) const std::string policy_file = policy + (policy == "default" || policy == "strict" ? "_tls13" : ""); - #else - const std::string policy_file = policy; - #endif const std::string from_file = read_tls_policy(policy_file); - result.test_eq("Values for TLS policy from " + policy_file, from_policy_obj, from_file); + if(from_policy_obj != from_file) { + const std::string d = diff(from_policy_obj, from_file); + result.test_failure(Botan::fmt("Values for TLS policy from {} don't match (diff {})", policy_file, d)); + } else { + result.test_success("Values from TLS policy from " + policy_file + " match"); + } } return {result}; } private: + static std::string diff(const std::string& a_str, const std::string& b_str) { + std::istringstream a_ss(a_str); + std::istringstream b_ss(b_str); + + std::ostringstream diff; + + for(;;) { + if(!a_ss && !b_ss) { + break; // done + } + + std::string a_line; + std::getline(a_ss, a_line, '\n'); + + std::string b_line; + std::getline(b_ss, b_line, '\n'); + + if(a_line != b_line) { + diff << "- " << a_line << "\n" + << "+ " << b_line << "\n"; + } + } + + return diff.str(); + } + static std::string read_tls_policy(const std::string& policy_str) { const std::string fspath = Test::data_file("tls-policy/" + policy_str + ".txt"); @@ -324,7 +657,7 @@ throw Test_Error("Missing policy file " + fspath); } - Botan::TLS::Text_Policy policy(is); + const Botan::TLS::Text_Policy policy(is); return policy.to_string(); } @@ -351,6 +684,7 @@ }; BOTAN_REGISTER_TEST("tls", "tls_policy_text", Test_TLS_Policy_Text); + #endif class Test_TLS_Ciphersuites : public Test { public: @@ -362,17 +696,22 @@ auto ciphersuite = Botan::TLS::Ciphersuite::by_id(csuite_id16); if(ciphersuite && ciphersuite->valid()) { - result.test_eq("Valid Ciphersuite is not SCSV", Botan::TLS::Ciphersuite::is_scsv(csuite_id16), false); + result.test_is_false("Valid Ciphersuite is not SCSV", Botan::TLS::Ciphersuite::is_scsv(csuite_id16)); - if(ciphersuite->cbc_ciphersuite() == false) { - result.test_eq("Expected AEAD ciphersuite", ciphersuite->aead_ciphersuite(), true); - result.test_eq("Expected MAC name for AEAD ciphersuites", ciphersuite->mac_algo(), "AEAD"); + if(ciphersuite->cbc_ciphersuite() == false && ciphersuite->null_ciphersuite() == false) { + result.test_is_true("Expected AEAD ciphersuite", ciphersuite->aead_ciphersuite()); + result.test_str_eq("Expected MAC name for AEAD ciphersuites", ciphersuite->mac_algo(), "AEAD"); } else { - result.test_eq("Did not expect AEAD ciphersuite", ciphersuite->aead_ciphersuite(), false); - result.test_eq( - "MAC algo and PRF algo same for CBC suites", ciphersuite->prf_algo(), ciphersuite->mac_algo()); + result.test_is_false("Did not expect AEAD ciphersuite", ciphersuite->aead_ciphersuite()); + result.test_str_eq("MAC algo and PRF algo same for CBC and NULL suites", + ciphersuite->prf_algo(), + ciphersuite->mac_algo()); } + if(ciphersuite->null_ciphersuite()) { + result.test_str_eq("Expected NULL ciphersuite", ciphersuite->cipher_algo(), "NULL"); + }; + // TODO more tests here } } @@ -399,13 +738,11 @@ static Test::Result test_tls_sig_method_strings() { Test::Result result("TLS::Signature_Scheme"); - std::vector schemes = Botan::TLS::Signature_Scheme::all_available_schemes(); - std::set scheme_strs; - for(auto scheme : schemes) { - std::string scheme_str = scheme.to_string(); + for(auto scheme : Botan::TLS::Signature_Scheme::all_available_schemes()) { + const std::string scheme_str = scheme.to_string(); - result.test_eq("Scheme strings unique", scheme_strs.count(scheme_str), 0); + result.test_sz_eq("Scheme strings unique", scheme_strs.count(scheme_str), 0); scheme_strs.insert(scheme_str); } @@ -422,11 +759,11 @@ Botan::TLS::Auth_Method::IMPLICIT, }); - for(Botan::TLS::Auth_Method meth : auth_methods) { - std::string meth_str = Botan::TLS::auth_method_to_string(meth); - result.test_ne("Method string is not empty", meth_str, ""); - Botan::TLS::Auth_Method meth2 = Botan::TLS::auth_method_from_string(meth_str); - result.confirm("Decoded method matches", meth == meth2); + for(const Botan::TLS::Auth_Method meth : auth_methods) { + const std::string meth_str = Botan::TLS::auth_method_to_string(meth); + result.test_str_not_empty("Method string is not empty", meth_str); + const Botan::TLS::Auth_Method meth2 = Botan::TLS::auth_method_from_string(meth_str); + result.test_is_true("Decoded method matches", meth == meth2); } return result; @@ -441,11 +778,11 @@ Botan::TLS::Kex_Algo::PSK, Botan::TLS::Kex_Algo::ECDHE_PSK}); - for(Botan::TLS::Kex_Algo meth : kex_algos) { - std::string meth_str = Botan::TLS::kex_method_to_string(meth); - result.test_ne("Method string is not empty", meth_str, ""); - Botan::TLS::Kex_Algo meth2 = Botan::TLS::kex_method_from_string(meth_str); - result.confirm("Decoded method matches", meth == meth2); + for(const Botan::TLS::Kex_Algo meth : kex_algos) { + const std::string meth_str = Botan::TLS::kex_method_to_string(meth); + result.test_str_not_empty("Method string is not empty", meth_str); + const Botan::TLS::Kex_Algo meth2 = Botan::TLS::kex_method_from_string(meth_str); + result.test_is_true("Decoded method matches", meth == meth2); } return result; @@ -454,6 +791,39 @@ BOTAN_REGISTER_TEST("tls", "tls_algo_strings", Test_TLS_Algo_Strings); + #if defined(BOTAN_HAS_TLS_13) + +class TLS13_PSK_Import_Tests final : public Text_Based_Test { + public: + TLS13_PSK_Import_Tests() : + Text_Based_Test("tls_13_psk_import.vec", "Key,Identity,TargetHash,Output", "Context") {} + + Test::Result run_one_test(const std::string& hash_name, const VarMap& vars) override { + Test::Result result("PSK Import " + hash_name); + + const auto key = vars.get_req_bin("Key"); + const auto identity = vars.get_req_bin("Identity"); + const auto context = vars.get_opt_bin("Context"); + const auto target_hash = vars.get_req_str("TargetHash"); + const auto expected = vars.get_req_bin("Output"); + + const Botan::TLS::PSKImporter importer(key, identity, context, hash_name); + auto psk = importer.derive_imported_psk(Botan::TLS::Protocol_Version::TLS_V13, target_hash); + + result.test_is_true("PSK is marked as imported", psk.is_imported()); + result.test_str_eq("PRF algo matches target hash", psk.prf_algo(), target_hash); + result.test_bin_eq("Derived PSK", psk.extract_master_secret(), expected); + + return result; + } +}; + +BOTAN_REGISTER_TEST("tls", "tls13_psk_import", TLS13_PSK_Import_Tests); + + #endif + #endif +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_tls_cipher_state.cpp botan3-3.12.0+dfsg/src/tests/test_tls_cipher_state.cpp --- botan3-3.7.1+dfsg/src/tests/test_tls_cipher_state.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_tls_cipher_state.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,11 +9,13 @@ #if defined(BOTAN_HAS_TLS_13) + #include #include #include #include #include + #include namespace Botan_Tests { @@ -29,7 +31,7 @@ } public: - mutable std::map> secrets; // NOLINT(*-non-private-member-variables-in-classes) + mutable std::map> secrets; // NOLINT(*-non-private-member-variable*) }; decltype(auto) make_CHECK_both(Cipher_State* cs_client, @@ -38,10 +40,12 @@ Journaling_Secret_Logger* sl_server) { using namespace std::placeholders; return [=](const std::string& name, auto lambda) -> std::vector { + // NOLINTBEGIN(*-avoid-bind) return {CHECK(std::string(name + " (client)").c_str(), std::bind(lambda, cs_client, sl_client, Connection_Side::Client, _1)), CHECK(std::string(name + " (server)").c_str(), std::bind(lambda, cs_server, sl_server, Connection_Side::Server, _1))}; + // NOLINTEND(*-avoid-bind) }; } @@ -70,7 +74,7 @@ result.test_no_throw("encryption is successful for " + name, [&] { cs->encrypt_record_fragment(record_header, plaintext_fragment_copy); }); - result.test_eq("encrypted payload for " + name, plaintext_fragment_copy, encrypted_fragment); + result.test_bin_eq("encrypted payload for " + name, plaintext_fragment_copy, encrypted_fragment); } void decrypt(Test::Result& result, Cipher_State* cs) const { @@ -78,7 +82,7 @@ result.test_no_throw("decryption is successful for " + name, [&] { cs->decrypt_record_fragment(record_header, encrypted_fragment_copy); }); - result.test_eq("plaintext for " + name, encrypted_fragment_copy, plaintext_fragment); + result.test_bin_eq("plaintext for " + name, encrypted_fragment_copy, plaintext_fragment); } void xxcrypt(Test::Result& result, Cipher_State* cs, Connection_Side side) const { @@ -314,32 +318,32 @@ {CHECK_both( "secret logging during initialization", [&](Cipher_State*, Journaling_Secret_Logger* sl, Connection_Side, Test::Result& result) { - result.test_eq("logged expected secrets", sl->secrets.size(), 2); + result.test_sz_eq("logged expected secrets", sl->secrets.size(), 2); result.require("has client traffic secret", sl->secrets.contains("CLIENT_HANDSHAKE_TRAFFIC_SECRET")); result.require("has server traffic secret", sl->secrets.contains("SERVER_HANDSHAKE_TRAFFIC_SECRET")); - result.test_is_eq("client traffic secret", - sl->secrets.at("CLIENT_HANDSHAKE_TRAFFIC_SECRET"), - client_handshake_traffic_secret); - result.test_is_eq("server traffic secret", - sl->secrets.at("SERVER_HANDSHAKE_TRAFFIC_SECRET"), - server_handshake_traffic_secret); + result.test_bin_eq("client traffic secret", + sl->secrets.at("CLIENT_HANDSHAKE_TRAFFIC_SECRET"), + client_handshake_traffic_secret); + result.test_bin_eq("server traffic secret", + sl->secrets.at("SERVER_HANDSHAKE_TRAFFIC_SECRET"), + server_handshake_traffic_secret); }), CHECK_both("ciphersuite compatibility", [&](Cipher_State* cs, Journaling_Secret_Logger*, Connection_Side side, Test::Result& result) { - result.confirm("self-compatibility", cs->is_compatible_with(cipher)); - result.confirm( + result.test_is_true("self-compatibility", cs->is_compatible_with(cipher)); + result.test_is_true( "fully defined state is not compatible to other suites", !cs->is_compatible_with(Ciphersuite::from_name("CHACHA20_POLY1305_SHA256").value()) && !cs->is_compatible_with(Ciphersuite::from_name("AES_128_CCM_SHA256").value()) && !cs->is_compatible_with(Ciphersuite::from_name("PSK_WITH_AES_128_GCM_SHA256").value())); if(side == Connection_Side::Client) { - result.confirm("Clients don't expect unprotected alerts after server hello", - !cs->must_expect_unprotected_alert_traffic()); + result.test_is_true("Clients don't expect unprotected alerts after server hello", + !cs->must_expect_unprotected_alert_traffic()); } else { - result.confirm("Servers must expect unprotected alerts in response to their server hello", - cs->must_expect_unprotected_alert_traffic()); + result.test_is_true("Servers must expect unprotected alerts in response to their server hello", + cs->must_expect_unprotected_alert_traffic()); } }), @@ -349,115 +353,110 @@ [&] { cs->next_ticket_nonce(); }); }), - CHECK_both("handshake traffic without PSK", - [&](Cipher_State* cs, Journaling_Secret_Logger* sl, Connection_Side side, Test::Result& result) { - result.confirm("can not yet write application data", !cs->can_encrypt_application_traffic()); - result.confirm("can not yet export key material", !cs->can_export_keys()); - - // decrypt encrypted extensions from server - encrypted_extensions.xxcrypt(result, cs, side); - - // validate the MAC we receive in server Finished message - const auto expected_server_mac = Botan::hex_decode( - "9b 9b 14 1d 90 63 37 fb d2 cb dc e7 1d f4" - "de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 18"); - if(side == Connection_Side::Client) { - result.confirm("expecting the correct MAC for server finished", - cs->verify_peer_finished_mac(th_pre_server_finished, expected_server_mac)); - } else { - result.test_eq("expecting the correct MAC for server finished", - cs->finished_mac(th_pre_server_finished), - expected_server_mac); - } + CHECK_both( + "handshake traffic without PSK", + [&](Cipher_State* cs, Journaling_Secret_Logger* sl, Connection_Side side, Test::Result& result) { + result.test_is_true("can not yet write application data", !cs->can_encrypt_application_traffic()); + result.test_is_true("can not yet export key material", !cs->can_export_keys()); - // advance Cipher_State with client_hello...server_Finished - // (allows receiving of application data, but does not yet allow such sending) - result.test_no_throw("state advancement is legal", - [&] { cs->advance_with_server_finished(th_server_finished, *sl); }); + // decrypt encrypted extensions from server + encrypted_extensions.xxcrypt(result, cs, side); - if(side == Connection_Side::Client) { - result.confirm("can read application data", cs->can_decrypt_application_traffic()); - result.confirm("can not yet write application data", !cs->can_encrypt_application_traffic()); - result.confirm("Clients don't expect unprotected alerts after server hello", - !cs->must_expect_unprotected_alert_traffic()); - } else { - result.confirm("can not yet read application data", !cs->can_decrypt_application_traffic()); - result.confirm("can write application data", cs->can_encrypt_application_traffic()); - result.confirm("Servers must expect unprotected alerts in response to their first flight", - cs->must_expect_unprotected_alert_traffic()); - } + // validate the MAC we receive in server Finished message + const auto expected_server_mac = Botan::hex_decode( + "9b 9b 14 1d 90 63 37 fb d2 cb dc e7 1d f4" + "de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 18"); + if(side == Connection_Side::Client) { + result.test_is_true("expecting the correct MAC for server finished", + cs->verify_peer_finished_mac(th_pre_server_finished, expected_server_mac)); + } else { + result.test_bin_eq("expecting the correct MAC for server finished", + cs->finished_mac(th_pre_server_finished), + expected_server_mac); + } - // check the logged key material - result.test_eq("contains expected number of keys", sl->secrets.size(), 5); - result.require("has client traffic secret", sl->secrets.contains("CLIENT_TRAFFIC_SECRET_0")); - result.require("has server traffic secret", sl->secrets.contains("SERVER_TRAFFIC_SECRET_0")); - result.require("has exporter secret", sl->secrets.contains("EXPORTER_SECRET")); - result.test_eq( - "client traffic secret (0)", sl->secrets.at("CLIENT_TRAFFIC_SECRET_0"), client_traffic_secret); - result.test_eq( - "server traffic secret (0)", sl->secrets.at("SERVER_TRAFFIC_SECRET_0"), server_traffic_secret); + // advance Cipher_State with client_hello...server_Finished + // (allows receiving of application data, but does not yet allow such sending) + result.test_no_throw("state advancement is legal", + [&] { cs->advance_with_server_finished(th_server_finished, *sl); }); - // generate the MAC for the client Finished message - const auto expected_client_mac = Botan::hex_decode( - "a8 ec 43 6d 67 76 34 ae 52 5a c1 fc eb e1 1a 03" - "9e c1 76 94 fa c6 e9 85 27 b6 42 f2 ed d5 ce 61"); - if(side == Connection_Side::Client) { - result.test_eq("generating the correct MAC for client finished", - cs->finished_mac(th_server_finished), - expected_client_mac); - } else { - result.confirm("verify the correct MAC for client finished", - cs->verify_peer_finished_mac(th_server_finished, expected_client_mac)); - } + if(side == Connection_Side::Client) { + result.test_is_true("can read application data", cs->can_decrypt_application_traffic()); + result.test_is_true("can not yet write application data", !cs->can_encrypt_application_traffic()); + result.test_is_true("Clients don't expect unprotected alerts after server hello", + !cs->must_expect_unprotected_alert_traffic()); + } else { + result.test_is_true("can not yet read application data", !cs->can_decrypt_application_traffic()); + result.test_is_true("can write application data", cs->can_encrypt_application_traffic()); + result.test_is_true("Servers must expect unprotected alerts in response to their first flight", + cs->must_expect_unprotected_alert_traffic()); + } - // encrypt client Finished message by client - // (under the client handshake traffic secret) - encrypted_client_finished_message.xxcrypt(result, cs, side); + // check the logged key material + result.test_sz_eq("contains expected number of keys", sl->secrets.size(), 5); + result.require("has client traffic secret", sl->secrets.contains("CLIENT_TRAFFIC_SECRET_0")); + result.require("has server traffic secret", sl->secrets.contains("SERVER_TRAFFIC_SECRET_0")); + result.require("has exporter secret", sl->secrets.contains("EXPORTER_SECRET")); + result.test_bin_eq( + "client traffic secret (0)", sl->secrets.at("CLIENT_TRAFFIC_SECRET_0"), client_traffic_secret); + result.test_bin_eq( + "server traffic secret (0)", sl->secrets.at("SERVER_TRAFFIC_SECRET_0"), server_traffic_secret); - // advance Cipher_State with client_hello...client_Finished - // (allows generation of resumption PSKs) - result.test_no_throw("state advancement is legal", - [&] { cs->advance_with_client_finished(th_client_finished); }); + // generate the MAC for the client Finished message + const auto expected_client_mac = Botan::hex_decode( + "a8 ec 43 6d 67 76 34 ae 52 5a c1 fc eb e1 1a 03" + "9e c1 76 94 fa c6 e9 85 27 b6 42 f2 ed d5 ce 61"); + if(side == Connection_Side::Client) { + result.test_bin_eq("generating the correct MAC for client finished", + cs->finished_mac(th_server_finished), + expected_client_mac); + } else { + result.test_is_true("verify the correct MAC for client finished", + cs->verify_peer_finished_mac(th_server_finished, expected_client_mac)); + } - result.confirm("can write application data", cs->can_encrypt_application_traffic()); - result.confirm("can read application data", cs->can_decrypt_application_traffic()); - result.confirm("doesn't need to expect unprotected alerts", - !cs->must_expect_unprotected_alert_traffic()); - result.confirm("can export key material", cs->can_export_keys()); - result.test_eq("key export produces expected result", - cs->export_key(export_label, export_context, 16), - expected_key_export); - - // decrypt "new session ticket" post-handshake message from server - // (encrypted under the application traffic secret) - encrypted_new_session_ticket.xxcrypt(result, cs, side); + // encrypt client Finished message by client + // (under the client handshake traffic secret) + encrypted_client_finished_message.xxcrypt(result, cs, side); - // encrypt application data by client - encrypted_application_data_client.xxcrypt(result, cs, side); + // advance Cipher_State with client_hello...client_Finished + // (allows generation of resumption PSKs) + result.test_no_throw("state advancement is legal", + [&] { cs->advance_with_client_finished(th_client_finished); }); - // decrypt application data from server - // (encrypted under the application traffic secret -- and a new sequence number) - encrypted_application_data_server.xxcrypt(result, cs, side); - - result.confirm("can export key material still", cs->can_export_keys()); - result.test_eq("key export result did not change", - cs->export_key(export_label, export_context, 16), - expected_key_export); - }), + result.test_is_true("can write application data", cs->can_encrypt_application_traffic()); + result.test_is_true("can read application data", cs->can_decrypt_application_traffic()); + result.test_is_true("doesn't need to expect unprotected alerts", + !cs->must_expect_unprotected_alert_traffic()); + result.test_is_true("can export key material", cs->can_export_keys()); + result.test_bin_eq("key export produces expected result", + cs->export_key(export_label, export_context, 16), + expected_key_export); + + // decrypt "new session ticket" post-handshake message from server + // (encrypted under the application traffic secret) + encrypted_new_session_ticket.xxcrypt(result, cs, side); + + // encrypt application data by client + encrypted_application_data_client.xxcrypt(result, cs, side); + + // decrypt application data from server + // (encrypted under the application traffic secret -- and a new sequence number) + encrypted_application_data_server.xxcrypt(result, cs, side); + + result.test_is_true("can export key material still", cs->can_export_keys()); + result.test_bin_eq("key export result did not change", + cs->export_key(export_label, export_context, 16), + expected_key_export); + }), CHECK_both("ticket nonce counter counts", [&](Cipher_State* cs, Journaling_Secret_Logger*, Connection_Side, Test::Result& result) { - result.test_is_eq("nonce is 0x00, 0x00", - cs->next_ticket_nonce(), - Botan::TLS::Ticket_Nonce(std::vector{0x00, 0x00})); - result.test_is_eq("nonce is 0x00, 0x01", - cs->next_ticket_nonce(), - Botan::TLS::Ticket_Nonce(std::vector{0x00, 0x01})); - result.test_is_eq("nonce is 0x00, 0x02", - cs->next_ticket_nonce(), - Botan::TLS::Ticket_Nonce(std::vector{0x00, 0x02})); + result.test_bin_eq("nonce is 0x00, 0x00", cs->next_ticket_nonce().get(), "0000"); + result.test_bin_eq("nonce is 0x00, 0x01", cs->next_ticket_nonce().get(), "0001"); + result.test_bin_eq("nonce is 0x00, 0x02", cs->next_ticket_nonce().get(), "0002"); - for(uint32_t i = 3; i < std::numeric_limits::max(); ++i) { + for(uint32_t i = 3; i <= std::numeric_limits::max(); ++i) { cs->next_ticket_nonce(); } @@ -470,43 +469,43 @@ // derive PSK for resumption const auto psk = cs->psk(Botan::TLS::Ticket_Nonce( std::vector{0x00, 0x00}) /* ticket_nonce as defined in RFC 8448 */); - result.test_eq("PSK matches", psk, expected_psk); + result.test_bin_eq("PSK matches", psk, expected_psk); }), CHECK_both("key update", [&](Cipher_State* cs, Journaling_Secret_Logger* sl, Connection_Side side, Test::Result& result) { - const auto read_label = + const auto* const read_label = side == Connection_Side::Client ? "SERVER_TRAFFIC_SECRET_1" : "CLIENT_TRAFFIC_SECRET_1"; - const auto write_label = + const auto* const write_label = side == Connection_Side::Client ? "CLIENT_TRAFFIC_SECRET_1" : "SERVER_TRAFFIC_SECRET_1"; cs->update_read_keys(*sl); - result.test_eq("read secret update is here", sl->secrets.size(), 6); + result.test_sz_eq("read secret update is here", sl->secrets.size(), 6); result.require("has new read traffic secret", sl->secrets.contains(read_label)); cs->update_write_keys(*sl); - result.test_eq("write secret update is here", sl->secrets.size(), 7); + result.test_sz_eq("write secret update is here", sl->secrets.size(), 7); result.require("has new write traffic secret", sl->secrets.contains(write_label)); - result.test_eq("client traffic secret (1)", - sl->secrets.at("CLIENT_TRAFFIC_SECRET_1"), - updated_client_traffic_secret); - result.test_eq("server traffic secret (1)", - sl->secrets.at("SERVER_TRAFFIC_SECRET_1"), - updated_server_traffic_secret); + result.test_bin_eq("client traffic secret (1)", + sl->secrets.at("CLIENT_TRAFFIC_SECRET_1"), + updated_client_traffic_secret); + result.test_bin_eq("server traffic secret (1)", + sl->secrets.at("SERVER_TRAFFIC_SECRET_1"), + updated_server_traffic_secret); - result.confirm("can encrypt application traffic", cs->can_encrypt_application_traffic()); + result.test_is_true("can encrypt application traffic", cs->can_encrypt_application_traffic()); }), CHECK_both("cleanup", [&](Cipher_State* cs, Journaling_Secret_Logger*, Connection_Side, Test::Result& result) { // cleanup cs->clear_write_keys(); - result.confirm("can no longer write application data", !cs->can_encrypt_application_traffic()); - result.confirm("can still read application data", cs->can_decrypt_application_traffic()); + result.test_is_true("can no longer write application data", !cs->can_encrypt_application_traffic()); + result.test_is_true("can still read application data", cs->can_decrypt_application_traffic()); cs->clear_read_keys(); - result.confirm("can no longer write application data", !cs->can_encrypt_application_traffic()); - result.confirm("can no longer read application data", !cs->can_decrypt_application_traffic()); + result.test_is_true("can no longer write application data", !cs->can_encrypt_application_traffic()); + result.test_is_true("can no longer read application data", !cs->can_decrypt_application_traffic()); })}); } @@ -673,143 +672,145 @@ return Test::flatten_result_lists( {CHECK_both("no secrets logged for PSK initialization", [&](Cipher_State*, Journaling_Secret_Logger* sl, Connection_Side, Test::Result& result) { - result.test_eq("no secrets logged", sl->secrets.size(), 0); + result.test_sz_eq("no secrets logged", sl->secrets.size(), 0); }), CHECK_both("calculating PSK binder", [&](Cipher_State* cs, Journaling_Secret_Logger*, Connection_Side, Test::Result& result) { const auto mac = cs->psk_binder_mac(th_client_hello_prefix); - result.test_eq("PSK binder is as expected", mac, expected_psk_binder); + result.test_bin_eq("PSK binder is as expected", mac, expected_psk_binder); }), - CHECK_both( - "ciphersuite compatibility", - [&](Cipher_State* cs, Journaling_Secret_Logger*, Connection_Side, Test::Result& result) { - result.confirm("self-compatibility", cs->is_compatible_with(cipher)); - result.confirm("partially defined state is compatible with suites using the same hash", - cs->is_compatible_with(Ciphersuite::from_name("CHACHA20_POLY1305_SHA256").value()) && - cs->is_compatible_with(Ciphersuite::from_name("AES_128_CCM_SHA256").value()) && - cs->is_compatible_with(Ciphersuite::from_name("AES_128_CCM_8_SHA256").value())); - - result.confirm("partially defined state is not compatible with other hashes or protocol versions", - !cs->is_compatible_with(Ciphersuite::from_name("PSK_WITH_AES_128_GCM_SHA256").value()) && - !cs->is_compatible_with(Ciphersuite::from_name("AES_256_GCM_SHA384").value())); - }), + CHECK_both("ciphersuite compatibility", + [&](Cipher_State* cs, Journaling_Secret_Logger*, Connection_Side, Test::Result& result) { + result.test_is_true("self-compatibility", cs->is_compatible_with(cipher)); + result.test_is_true( + "partially defined state is compatible with suites using the same hash", + cs->is_compatible_with(Ciphersuite::from_name("CHACHA20_POLY1305_SHA256").value()) && + cs->is_compatible_with(Ciphersuite::from_name("AES_128_CCM_SHA256").value()) && + cs->is_compatible_with(Ciphersuite::from_name("AES_128_CCM_8_SHA256").value())); + + result.test_is_true( + "partially defined state is not compatible with other hashes or protocol versions", + !cs->is_compatible_with(Ciphersuite::from_name("PSK_WITH_AES_128_GCM_SHA256").value()) && + !cs->is_compatible_with(Ciphersuite::from_name("AES_256_GCM_SHA384").value())); + }), CHECK_both("calculate the early traffic secrets", [&](Cipher_State* cs, Journaling_Secret_Logger* sl, Connection_Side side, Test::Result& result) { cs->advance_with_client_hello(th_client_hello, *sl); result.require("early key export is possible", cs->can_export_keys()); - result.test_eq("early key export produces expected result", - cs->export_key(early_export_label, early_export_context, 16), - early_expected_key_export); + result.test_bin_eq("early key export produces expected result", + cs->export_key(early_export_label, early_export_context, 16), + early_expected_key_export); if(side == Connection_Side::Client) { - result.confirm("Clients must expect servers to respond with an unprotected alert", - cs->must_expect_unprotected_alert_traffic()); + result.test_is_true("Clients must expect servers to respond with an unprotected alert", + cs->must_expect_unprotected_alert_traffic()); } else { - result.confirm( + result.test_is_true( "Servers do not expect clients to send alerts protected with the early data secret", !cs->must_expect_unprotected_alert_traffic()); } - result.test_eq("logged early secrets", sl->secrets.size(), 1); + result.test_sz_eq("logged early secrets", sl->secrets.size(), 1); result.require("has early exporter secret", sl->secrets.contains("EARLY_EXPORTER_MASTER_SECRET")); - result.test_eq( + result.test_bin_eq( "early exporter secret", sl->secrets.at("EARLY_EXPORTER_MASTER_SECRET"), early_exporter_secret); // TODO: Once 0-RTT traffic is implemented this will likely allow handling of // application traffic in this state. - result.confirm("can not yet write application data", !cs->can_encrypt_application_traffic()); - result.confirm("can not yet read application data", !cs->can_decrypt_application_traffic()); + result.test_is_true("can not yet write application data", !cs->can_encrypt_application_traffic()); + result.test_is_true("can not yet read application data", !cs->can_decrypt_application_traffic()); }), - CHECK_both( - "handshake traffic after PSK", - [&](Cipher_State* cs, Journaling_Secret_Logger* sl, Connection_Side side, Test::Result& result) { - cs->advance_with_server_hello(cipher, secure_vector(shared_secret), th_server_hello, *sl); + CHECK_both("handshake traffic after PSK", + [&](Cipher_State* cs, Journaling_Secret_Logger* sl, Connection_Side side, Test::Result& result) { + cs->advance_with_server_hello(cipher, secure_vector(shared_secret), th_server_hello, *sl); - // decrypt encrypted extensions from server - encrypted_extensions.xxcrypt(result, cs, side); + // decrypt encrypted extensions from server + encrypted_extensions.xxcrypt(result, cs, side); - // check the logged key material - result.test_eq("contains expected number of keys", sl->secrets.size(), 3); - result.require("has client handshake traffic secret", - sl->secrets.contains("CLIENT_HANDSHAKE_TRAFFIC_SECRET")); - result.require("has server handshake traffic secret", - sl->secrets.contains("SERVER_HANDSHAKE_TRAFFIC_SECRET")); - result.test_eq("client handshake traffic secret", - sl->secrets.at("CLIENT_HANDSHAKE_TRAFFIC_SECRET"), - client_handshake_traffic_secret); - result.test_eq("server handshake traffic secret", - sl->secrets.at("SERVER_HANDSHAKE_TRAFFIC_SECRET"), - server_handshake_traffic_secret); + // check the logged key material + result.test_sz_eq("contains expected number of keys", sl->secrets.size(), 3); + result.require("has client handshake traffic secret", + sl->secrets.contains("CLIENT_HANDSHAKE_TRAFFIC_SECRET")); + result.require("has server handshake traffic secret", + sl->secrets.contains("SERVER_HANDSHAKE_TRAFFIC_SECRET")); + result.test_bin_eq("client handshake traffic secret", + sl->secrets.at("CLIENT_HANDSHAKE_TRAFFIC_SECRET"), + client_handshake_traffic_secret); + result.test_bin_eq("server handshake traffic secret", + sl->secrets.at("SERVER_HANDSHAKE_TRAFFIC_SECRET"), + server_handshake_traffic_secret); - // TODO: Handling of early traffic is left out as 0-RTT is not implemented yet. + // TODO: Handling of early traffic is left out as 0-RTT is not implemented yet. - // validate the MAC we receive in server Finished message - const auto expected_server_mac = Botan::hex_decode( - "48 d3 e0 e1 b3 d9 07 c6 ac ff 14 5e 16 09 03 88" - "c7 7b 05 c0 50 b6 34 ab 1a 88 bb d0 dd 1a 34 b2"); - if(side == Connection_Side::Client) { - result.confirm("expecting the correct MAC for server finished", - cs->verify_peer_finished_mac(th_pre_server_finished, expected_server_mac)); - result.confirm("Clients don't expect unprotected alerts after server hello", - !cs->must_expect_unprotected_alert_traffic()); - } else { - result.test_eq("expecting the correct MAC for server finished", - cs->finished_mac(th_pre_server_finished), - expected_server_mac); - result.confirm("Servers must expect unprotected alerts in response to their server hello", - cs->must_expect_unprotected_alert_traffic()); - } + // validate the MAC we receive in server Finished message + const auto expected_server_mac = Botan::hex_decode( + "48 d3 e0 e1 b3 d9 07 c6 ac ff 14 5e 16 09 03 88" + "c7 7b 05 c0 50 b6 34 ab 1a 88 bb d0 dd 1a 34 b2"); + if(side == Connection_Side::Client) { + result.test_is_true("expecting the correct MAC for server finished", + cs->verify_peer_finished_mac(th_pre_server_finished, expected_server_mac)); + result.test_is_true("Clients don't expect unprotected alerts after server hello", + !cs->must_expect_unprotected_alert_traffic()); + } else { + result.test_bin_eq("expecting the correct MAC for server finished", + cs->finished_mac(th_pre_server_finished), + expected_server_mac); + result.test_is_true("Servers must expect unprotected alerts in response to their server hello", + cs->must_expect_unprotected_alert_traffic()); + } - result.confirm("cannot read application data", !cs->can_decrypt_application_traffic()); - result.confirm("cannot write application data", !cs->can_encrypt_application_traffic()); + result.test_is_true("cannot read application data", !cs->can_decrypt_application_traffic()); + result.test_is_true("cannot write application data", !cs->can_encrypt_application_traffic()); - // advance Cipher_State with client_hello...server_Finished - // (allows receiving of application data, but no such sending) - result.test_no_throw("state advancement is legal", - [&] { cs->advance_with_server_finished(th_server_finished, *sl); }); + // advance Cipher_State with client_hello...server_Finished + // (allows receiving of application data, but no such sending) + result.test_no_throw("state advancement is legal", + [&] { cs->advance_with_server_finished(th_server_finished, *sl); }); - if(side == Connection_Side::Client) { - result.confirm("can read application data", cs->can_decrypt_application_traffic()); - result.confirm("cannot write application data", !cs->can_encrypt_application_traffic()); - result.confirm("Clients don't expect unprotected alerts after server hello", - !cs->must_expect_unprotected_alert_traffic()); - } else { - result.confirm("cannot read application data", !cs->can_decrypt_application_traffic()); - result.confirm("can write application data", cs->can_encrypt_application_traffic()); - result.confirm("Servers must expect unprotected alerts in response to their first flight", - cs->must_expect_unprotected_alert_traffic()); - } + if(side == Connection_Side::Client) { + result.test_is_true("can read application data", cs->can_decrypt_application_traffic()); + result.test_is_true("cannot write application data", !cs->can_encrypt_application_traffic()); + result.test_is_true("Clients don't expect unprotected alerts after server hello", + !cs->must_expect_unprotected_alert_traffic()); + } else { + result.test_is_true("cannot read application data", !cs->can_decrypt_application_traffic()); + result.test_is_true("can write application data", cs->can_encrypt_application_traffic()); + result.test_is_true("Servers must expect unprotected alerts in response to their first flight", + cs->must_expect_unprotected_alert_traffic()); + } - // check the logged key material - result.test_eq("contains expected number of keys", sl->secrets.size(), 6); - result.require("has client traffic secret", sl->secrets.contains("CLIENT_TRAFFIC_SECRET_0")); - result.require("has server traffic secret", sl->secrets.contains("SERVER_TRAFFIC_SECRET_0")); - result.require("has exporter secret", sl->secrets.contains("EXPORTER_SECRET")); - result.test_eq("client traffic secret", sl->secrets.at("CLIENT_TRAFFIC_SECRET_0"), client_traffic_secret); - result.test_eq("server traffic secret", sl->secrets.at("SERVER_TRAFFIC_SECRET_0"), server_traffic_secret); - result.test_eq("exporter secret", sl->secrets.at("EXPORTER_SECRET"), exporter_secret); + // check the logged key material + result.test_sz_eq("contains expected number of keys", sl->secrets.size(), 6); + result.require("has client traffic secret", sl->secrets.contains("CLIENT_TRAFFIC_SECRET_0")); + result.require("has server traffic secret", sl->secrets.contains("SERVER_TRAFFIC_SECRET_0")); + result.require("has exporter secret", sl->secrets.contains("EXPORTER_SECRET")); + result.test_bin_eq( + "client traffic secret", sl->secrets.at("CLIENT_TRAFFIC_SECRET_0"), client_traffic_secret); + result.test_bin_eq( + "server traffic secret", sl->secrets.at("SERVER_TRAFFIC_SECRET_0"), server_traffic_secret); + result.test_bin_eq("exporter secret", sl->secrets.at("EXPORTER_SECRET"), exporter_secret); - // generate the MAC for the client Finished message - const auto expected_client_mac = Botan::hex_decode( - "72 30 a9 c9 52 c2 5c d6 13 8f c5 e6 62 83 08 c4" - "1c 53 35 dd 81 b9 f9 6b ce a5 0f d3 2b da 41 6d"); - if(side == Connection_Side::Client) { - result.test_eq("generating the correct MAC for client finished", - cs->finished_mac(th_end_of_early_data), - expected_client_mac); - } else { - result.confirm("verify the correct MAC for client finished", - cs->verify_peer_finished_mac(th_end_of_early_data, expected_client_mac)); - } + // generate the MAC for the client Finished message + const auto expected_client_mac = Botan::hex_decode( + "72 30 a9 c9 52 c2 5c d6 13 8f c5 e6 62 83 08 c4" + "1c 53 35 dd 81 b9 f9 6b ce a5 0f d3 2b da 41 6d"); + if(side == Connection_Side::Client) { + result.test_bin_eq("generating the correct MAC for client finished", + cs->finished_mac(th_end_of_early_data), + expected_client_mac); + } else { + result.test_is_true("verify the correct MAC for client finished", + cs->verify_peer_finished_mac(th_end_of_early_data, expected_client_mac)); + } - // encrypt client Finished message by client - // (under the client handshake traffic secret) - encrypted_client_finished_message.xxcrypt(result, cs, side); - }), + // encrypt client Finished message by client + // (under the client handshake traffic secret) + encrypted_client_finished_message.xxcrypt(result, cs, side); + }), CHECK_both("application traffic after PSK", [&](Cipher_State* cs, Journaling_Secret_Logger*, Connection_Side side, Test::Result& result) { @@ -818,14 +819,14 @@ result.test_no_throw("state advancement is legal", [&] { cs->advance_with_client_finished(th_client_finished); }); - result.confirm("can read application data", cs->can_decrypt_application_traffic()); - result.confirm("can write application data", cs->can_encrypt_application_traffic()); - result.confirm("doesn't need to expect unprotected alerts", - !cs->must_expect_unprotected_alert_traffic()); - result.confirm("can export key material", cs->can_export_keys()); - result.test_eq("key export produces expected result", - cs->export_key(export_label, export_context, 16), - expected_key_export); + result.test_is_true("can read application data", cs->can_decrypt_application_traffic()); + result.test_is_true("can write application data", cs->can_encrypt_application_traffic()); + result.test_is_true("doesn't need to expect unprotected alerts", + !cs->must_expect_unprotected_alert_traffic()); + result.test_is_true("can export key material", cs->can_export_keys()); + result.test_bin_eq("key export produces expected result", + cs->export_key(export_label, export_context, 16), + expected_key_export); // encrypt application data by client encrypted_application_data_client.xxcrypt(result, cs, side); diff -Nru botan3-3.7.1+dfsg/src/tests/test_tls_handshake_layer_13.cpp botan3-3.12.0+dfsg/src/tests/test_tls_handshake_layer_13.cpp --- botan3-3.7.1+dfsg/src/tests/test_tls_handshake_layer_13.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_tls_handshake_layer_13.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,12 +9,12 @@ #if defined(BOTAN_HAS_TLS_13) + #include #include #include - - #include + #include + #include #include - #include #include using namespace Botan::TLS; @@ -36,6 +36,7 @@ return std::get(read_result.value()); } +// NOLINTBEGIN(cert-err58-cpp,bugprone-throwing-static-initialization) const auto client_hello_message = Botan::hex_decode_locked( // from RFC 8448 "01 00 00 c0 03 03 cb" "34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12" @@ -140,6 +141,8 @@ {static_cast(Handshake_Type::CertificateUrl), 0x00, 0x00, 0x02, 0x42, 0x42}, {static_cast(Handshake_Type::CertificateStatus), 0x00, 0x00, 0x02, 0x42, 0x42}}; +// NOLINTEND(cert-err58-cpp,bugprone-throwing-static-initialization) + void check_transcript_hash_empty(Test::Result& result, const Transcript_Hash_State& transcript_hash) { result.test_throws("empty transcript_hash throws", [&] { transcript_hash.current(); }); } @@ -154,7 +157,7 @@ [&](auto& result) { Handshake_Layer hl(Connection_Side::Client); Transcript_Hash_State th("SHA-256"); - result.confirm("needs header bytes", !hl.next_message(Policy(), th)); + result.test_is_true("needs header bytes", !hl.next_message(Policy(), th)); check_transcript_hash_empty(result, th); }), @@ -163,7 +166,7 @@ Handshake_Layer hl(Connection_Side::Client); Transcript_Hash_State th("SHA-256"); hl.copy_data(std::vector{0x00, 0x01, 0x02}); - result.confirm("needs more bytes", !hl.next_message(Policy(), th)); + result.test_is_true("needs more bytes", !hl.next_message(Policy(), th)); check_transcript_hash_empty(result, th); }), @@ -172,7 +175,8 @@ Handshake_Layer hl(Connection_Side::Client); Transcript_Hash_State th("SHA-256"); hl.copy_data(client_hello_message); - result.confirm("is a client hello", has_message(result, hl.next_message(Policy(), th))); + result.test_is_true("is a client hello", + has_message(result, hl.next_message(Policy(), th))); check_transcript_hash_filled(result, th); }), @@ -181,7 +185,8 @@ Handshake_Layer hl(Connection_Side::Client); Transcript_Hash_State th("SHA-256"); hl.copy_data(server_hello_message); - result.confirm("is a server hello", has_message(result, hl.next_message(Policy(), th))); + result.test_is_true("is a server hello", + has_message(result, hl.next_message(Policy(), th))); check_transcript_hash_filled(result, th); }), @@ -190,8 +195,8 @@ Handshake_Layer hl(Connection_Side::Client); Transcript_Hash_State th("SHA-256"); hl.copy_data(server_hello_12_message); - result.confirm("is a legacy server hello", - has_message(result, hl.next_message(Policy(), th))); + result.test_is_true("is a legacy server hello", + has_message(result, hl.next_message(Policy(), th))); check_transcript_hash_filled(result, th); }), @@ -203,13 +208,14 @@ const Botan::secure_vector partial_client_hello_message(client_hello_message.cbegin(), client_hello_message.cend() - 15); hl.copy_data(partial_client_hello_message); - result.confirm("needs more bytes", !hl.next_message(Policy(), th)); - result.confirm("holds pending message data", hl.has_pending_data()); + result.test_is_true("needs more bytes", !hl.next_message(Policy(), th)); + result.test_is_true("holds pending message data", hl.has_pending_data()); const Botan::secure_vector remaining_client_hello_message(client_hello_message.cend() - 15, client_hello_message.cend()); hl.copy_data(remaining_client_hello_message); - result.confirm("is a client hello", has_message(result, hl.next_message(Policy(), th))); + result.test_is_true("is a client hello", + has_message(result, hl.next_message(Policy(), th))); check_transcript_hash_filled(result, th); }), @@ -219,9 +225,10 @@ Handshake_Layer hl(Connection_Side::Client); Transcript_Hash_State th("SHA-256"); hl.copy_data(Botan::concat(server_hello_message, encrypted_extensions)); - result.confirm("is a server hello", has_message(result, hl.next_message(Policy(), th))); - result.confirm("is encrypted extensions", - has_message(result, hl.next_message(Policy(), th))); + result.test_is_true("is a server hello", + has_message(result, hl.next_message(Policy(), th))); + result.test_is_true("is encrypted extensions", + has_message(result, hl.next_message(Policy(), th))); check_transcript_hash_filled(result, th); }), @@ -265,9 +272,9 @@ [&](auto& result) { auto hello = std::get( Client_Hello_13::parse({client_hello_message.cbegin() + 4, client_hello_message.cend()})); - Handshake_Layer hl(Connection_Side::Client); + const Handshake_Layer hl(Connection_Side::Client); Transcript_Hash_State th("SHA-256"); - result.test_eq("produces the same message", hl.prepare_message(hello, th), client_hello_message); + result.test_bin_eq("produces the same message", hl.prepare_message(hello, th), client_hello_message); check_transcript_hash_filled(result, th); }), @@ -275,9 +282,9 @@ [&](auto& result) { auto hello = std::get( Server_Hello_13::parse({server_hello_message.cbegin() + 4, server_hello_message.cend()})); - Handshake_Layer hl(Connection_Side::Server); + const Handshake_Layer hl(Connection_Side::Server); Transcript_Hash_State th("SHA-256"); - result.test_eq("produces the same message", hl.prepare_message(hello, th), server_hello_message); + result.test_bin_eq("produces the same message", hl.prepare_message(hello, th), server_hello_message); check_transcript_hash_filled(result, th); }), }; @@ -303,7 +310,7 @@ hl.copy_data(server_hello_message); const auto server_hello = hl.next_message(policy, th); - result.confirm("is a Server Hello", has_message(result, server_hello)); + result.test_is_true("is a Server Hello", has_message(result, server_hello)); // we now know the algorithm from the Server Hello th.set_algorithm("SHA-256"); @@ -313,34 +320,35 @@ const auto expected_after_server_hello = Botan::hex_decode( "86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"); - result.test_eq( + result.test_bin_eq( "correct transcript hash produced after server hello", th.current(), expected_after_server_hello); }), - CHECK("server handshake messages", - [&](auto& result) { - hl.copy_data(server_handshake_messages); - - const auto enc_exts = hl.next_message(policy, th); - result.confirm("is Encrypted Extensions", has_message(result, enc_exts)); - - const auto cert = hl.next_message(policy, th); - result.confirm("is Certificate", has_message(result, cert)); - - const auto expected_after_certificate = Botan::hex_decode( - "76 4d 66 32 b3 c3 5c 3f 32 05 e3 49 9a c3 ed ba ab b8 82 95 fb a7 51 46 1d 36 78 e2 e5 ea 06 87"); - - const auto cert_verify = hl.next_message(policy, th); - result.confirm("is Certificate Verify", has_message(result, cert_verify)); - result.test_eq("hash before Cert Verify is still available", th.previous(), expected_after_certificate); - - const auto expected_after_server_finished = Botan::hex_decode( - "96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"); - - const auto server_finished = hl.next_message(policy, th); - result.confirm("is Finished", has_message(result, server_finished)); - result.test_eq("hash is updated after server Finished", th.current(), expected_after_server_finished); - }), + CHECK( + "server handshake messages", + [&](auto& result) { + hl.copy_data(server_handshake_messages); + + const auto enc_exts = hl.next_message(policy, th); + result.test_is_true("is Encrypted Extensions", has_message(result, enc_exts)); + + const auto cert = hl.next_message(policy, th); + result.test_is_true("is Certificate", has_message(result, cert)); + + const auto expected_after_certificate = Botan::hex_decode( + "76 4d 66 32 b3 c3 5c 3f 32 05 e3 49 9a c3 ed ba ab b8 82 95 fb a7 51 46 1d 36 78 e2 e5 ea 06 87"); + + const auto cert_verify = hl.next_message(policy, th); + result.test_is_true("is Certificate Verify", has_message(result, cert_verify)); + result.test_bin_eq("hash before Cert Verify is still available", th.previous(), expected_after_certificate); + + const auto expected_after_server_finished = Botan::hex_decode( + "96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"); + + const auto server_finished = hl.next_message(policy, th); + result.test_is_true("is Finished", has_message(result, server_finished)); + result.test_bin_eq("hash is updated after server Finished", th.current(), expected_after_server_finished); + }), CHECK("client finished", [&](auto& result) { @@ -349,7 +357,8 @@ Finished_13 client_finished({client_finished_message.cbegin() + 4, client_finished_message.cend()}); hl.prepare_message(client_finished, th); - result.test_eq("hash is updated after client Finished", th.current(), expected_after_client_finished); + result.test_bin_eq( + "hash is updated after client Finished", th.current(), expected_after_client_finished); }), }; } @@ -366,7 +375,7 @@ auto hello = std::get( Client_Hello_13::parse({hrr_client_hello_msg.cbegin() + 4, hrr_client_hello_msg.cend()})); auto msg = hl.prepare_message(hello, th); - result.test_eq("parsing and re-marshalling produces same message", msg, hrr_client_hello_msg); + result.test_bin_eq("parsing and re-marshalling produces same message", msg, hrr_client_hello_msg); check_transcript_hash_empty(result, th); }), @@ -375,7 +384,7 @@ hl.copy_data(hrr_hello_retry_request_msg); const auto hrr = hl.next_message(policy, th); - result.confirm("is a Hello Retry Request", has_message(result, hrr)); + result.test_is_true("is a Hello Retry Request", has_message(result, hrr)); // we now know the algorithm from the Hello Retry Request // which will not change with the future Server Hello anymore (RFC 8446 4.1.4) @@ -386,9 +395,9 @@ const auto expected_after_hello_retry_request = Botan::hex_decode("74EEC04D09C926E86C0647C37BA4DC18D277EEC3337E4608C4D829B77E2FD2B3"); - result.test_eq("correct transcript hash produced after hello retry request", - th.current(), - expected_after_hello_retry_request); + result.test_bin_eq("correct transcript hash produced after hello retry request", + th.current(), + expected_after_hello_retry_request); }), // ... the rest of the handshake will work just like in full_client_handshake diff -Nru botan3-3.7.1+dfsg/src/tests/test_tls_handshake_state_13.cpp botan3-3.12.0+dfsg/src/tests/test_tls_handshake_state_13.cpp --- botan3-3.7.1+dfsg/src/tests/test_tls_handshake_state_13.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_tls_handshake_state_13.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #include "tests.h" #if defined(BOTAN_HAS_TLS_13) + #include #include #include #endif @@ -18,7 +19,8 @@ #if defined(BOTAN_HAS_TLS_13) -const auto client_hello_message = Botan::hex_decode( // from RFC 8448 +// From RFC 8448 +const auto client_hello_message_hex = "03 03 cb" "34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12" "ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00" @@ -28,23 +30,23 @@ "e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d" "54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e" "04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02" - "01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"); + "01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"; -const auto server_hello_message = Botan::hex_decode( +const auto server_hello_message_hex = "03 03 a6" "af 06 a4 12 18 60 dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14" "34 da c1 55 77 2e d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00" "1d 00 20 c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6" - "cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04"); + "cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04"; -const auto server_finished_message = Botan::hex_decode( +const auto server_finished_message_hex = "9b 9b 14 1d 90 63 37 fb" "d2 cb dc e7 1d f4 de da 4a b4 2c 30" - "95 72 cb 7f ff ee 54 54 b7 8f 07 18"); + "95 72 cb 7f ff ee 54 54 b7 8f 07 18"; -const auto client_finished_message = Botan::hex_decode( +const auto client_finished_message_hex = "a8 ec 43 6d 67 76 34 ae 52 5a c1" - "fc eb e1 1a 03 9e c1 76 94 fa c6 e9 85 27 b6 42 f2 ed d5 ce 61"); + "fc eb e1 1a 03 9e c1 76 94 fa c6 e9 85 27 b6 42 f2 ed d5 ce 61"; std::vector finished_message_handling() { return { @@ -52,13 +54,16 @@ [&](auto& result) { Botan::TLS::Client_Handshake_State_13 state; + const auto client_finished_message = Botan::hex_decode(client_finished_message_hex); + const auto server_finished_message = Botan::hex_decode(server_finished_message_hex); + Botan::TLS::Finished_13 client_finished(client_finished_message); [[maybe_unused]] // just making sure that the return type of .sending is correct - std::reference_wrapper + const std::reference_wrapper client_fin = state.sending(std::move(client_finished)); result.test_throws("not stored as server Finished", [&] { state.server_finished(); }); - result.test_eq( + result.test_bin_eq( "correct client Finished stored", state.client_finished().serialize(), client_finished_message); Botan::TLS::Finished_13 server_finished(server_finished_message); @@ -66,9 +71,9 @@ auto server_fin = state.received(std::move(server_finished)); result.require("client can receive server finished", std::holds_alternative>(server_fin)); - result.test_eq( + result.test_bin_eq( "correct client Finished stored", state.client_finished().serialize(), client_finished_message); - result.test_eq( + result.test_bin_eq( "correct server Finished stored", state.server_finished().serialize(), server_finished_message); }), }; @@ -80,13 +85,16 @@ [&](auto& result) { Botan::TLS::Client_Handshake_State_13 state; + const auto client_hello_message = Botan::hex_decode(client_hello_message_hex); + auto client_hello = std::get(Botan::TLS::Client_Hello_13::parse(client_hello_message)); [[maybe_unused]] // just making sure that the return type of .sending is correct - std::reference_wrapper + const std::reference_wrapper filtered = state.sending(std::move(client_hello)); - result.test_eq("correct client hello stored", state.client_hello().serialize(), client_hello_message); + result.test_bin_eq( + "correct client hello stored", state.client_hello().serialize(), client_hello_message); result.template test_throws( "client cannot receive client hello", "received an illegal handshake message", [&] { @@ -95,19 +103,22 @@ state.received(std::move(ch)); }); }), - CHECK("Client with server hello", - [&](auto& result) { - Botan::TLS::Client_Handshake_State_13 state; + CHECK( + "Client with server hello", + [&](auto& result) { + Botan::TLS::Client_Handshake_State_13 state; + + const auto server_hello_message = Botan::hex_decode(server_hello_message_hex); + + auto server_hello = + std::get(Botan::TLS::Server_Hello_13::parse(server_hello_message)); + + auto filtered = state.received(std::move(server_hello)); + result.test_is_true("client can receive server hello", + std::holds_alternative>(filtered)); - auto server_hello = - std::get(Botan::TLS::Server_Hello_13::parse(server_hello_message)); - - auto filtered = state.received(std::move(server_hello)); - result.confirm("client can receive server hello", - std::holds_alternative>(filtered)); - - result.test_eq("correct server hello stored", state.server_hello().serialize(), server_hello_message); - }), + result.test_bin_eq("correct server hello stored", state.server_hello().serialize(), server_hello_message); + }), }; } diff -Nru botan3-3.7.1+dfsg/src/tests/test_tls_handshake_transitions.cpp botan3-3.12.0+dfsg/src/tests/test_tls_handshake_transitions.cpp --- botan3-3.7.1+dfsg/src/tests/test_tls_handshake_transitions.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_tls_handshake_transitions.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -20,10 +20,10 @@ CHECK("uninitialized expects nothing", [](Test::Result& result) { Botan::TLS::Handshake_Transitions ht; - result.confirm("CCS is not expected by default", !ht.change_cipher_spec_expected()); + result.test_is_true("CCS is not expected by default", !ht.change_cipher_spec_expected()); - result.confirm("no messages were received", - !ht.received_handshake_msg(Botan::TLS::Handshake_Type::ClientHello)); + result.test_is_true("no messages were received", + !ht.received_handshake_msg(Botan::TLS::Handshake_Type::ClientHello)); result.test_throws("no expectations set, always throws", [&] { ht.confirm_transition_to(Botan::TLS::Handshake_Type::ClientHello); }); }), @@ -36,8 +36,8 @@ result.test_no_throw("client hello met expectation", [&] { ht.confirm_transition_to(Botan::TLS::Handshake_Type::ClientHello); }); - result.confirm("received client hello", - ht.received_handshake_msg(Botan::TLS::Handshake_Type::ClientHello)); + result.test_is_true("received client hello", + ht.received_handshake_msg(Botan::TLS::Handshake_Type::ClientHello)); result.test_throws("confirmation resets expectations", [&] { ht.confirm_transition_to(Botan::TLS::Handshake_Type::ClientHello); }); @@ -62,20 +62,20 @@ result.test_no_throw("CERTIFICATE", [&] { ht.confirm_transition_to(Botan::TLS::Handshake_Type::Certificate); }); - result.confirm("received CERTIFICATE", - ht.received_handshake_msg(Botan::TLS::Handshake_Type::Certificate)); + result.test_is_true("received CERTIFICATE", + ht.received_handshake_msg(Botan::TLS::Handshake_Type::Certificate)); result.test_no_throw("CERTIFICATE_REQUEST", [&] { ht2.confirm_transition_to(Botan::TLS::Handshake_Type::CertificateRequest); }); - result.confirm("received CERTIFICATE_REQUEST", - ht2.received_handshake_msg(Botan::TLS::Handshake_Type::CertificateRequest)); + result.test_is_true("received CERTIFICATE_REQUEST", + ht2.received_handshake_msg(Botan::TLS::Handshake_Type::CertificateRequest)); }), CHECK("expect CCS", [](Test::Result& result) { Botan::TLS::Handshake_Transitions ht; ht.set_expected_next(Botan::TLS::Handshake_Type::HandshakeCCS); - result.confirm("CCS expected", ht.change_cipher_spec_expected()); + result.test_is_true("CCS expected", ht.change_cipher_spec_expected()); }), }; } diff -Nru botan3-3.7.1+dfsg/src/tests/test_tls_hybrid_kem_key.cpp botan3-3.12.0+dfsg/src/tests/test_tls_hybrid_kem_key.cpp --- botan3-3.7.1+dfsg/src/tests/test_tls_hybrid_kem_key.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_tls_hybrid_kem_key.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -11,9 +11,11 @@ defined(BOTAN_HAS_ECDSA) #include + #include #include #include #include + #include namespace Botan_Tests { @@ -39,8 +41,8 @@ std::unique_ptr kex_dh() { 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) { + auto* kex_sk = dynamic_cast(sk.get()); + if(kex_sk != nullptr) { // NOLINTNEXTLINE(bugprone-unused-return-value) (void)sk.release(); return std::unique_ptr(kex_sk); @@ -52,8 +54,8 @@ std::unique_ptr kex_ecdh() { 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) { + auto* kex_sk = dynamic_cast(sk.get()); + if(kex_sk != nullptr) { // NOLINTNEXTLINE(bugprone-unused-return-value) (void)sk.release(); return std::unique_ptr(kex_sk); @@ -92,41 +94,54 @@ 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, global_test_rng(), "Raw"); - return ka.agreed_value_size(); - }, - [](const Botan::Private_Key& kem_key) { - Botan::PK_KEM_Encryptor enc(kem_key, "Raw"); - return enc.shared_key_length(0); - }}; + const Botan::overloaded f{[](const Botan::PK_Key_Agreement_Key& kex_key) { + const Botan::PK_Key_Agreement ka(kex_key, global_test_rng(), "Raw"); + return ka.agreed_value_size(); + }, + [](const Botan::Private_Key& kem_key) { + const Botan::PK_KEM_Encryptor enc(kem_key, "Raw"); + return enc.shared_key_length(0); + }}; return (f(*kex_kem_fn()) + ...); } template size_t length_of_hybrid_ciphertext(Ts... kex_kem_fn) { - Botan::overloaded f{[](const Botan::PK_Key_Agreement_Key& kex_key) { return kex_key.public_value().size(); }, - [](const Botan::Private_Key& kem_key) { - Botan::PK_KEM_Encryptor enc(kem_key, "Raw"); - return enc.encapsulated_key_length(); - }}; + const Botan::overloaded f{[](const Botan::PK_Key_Agreement_Key& kex_key) { return kex_key.public_value().size(); }, + [](const Botan::Private_Key& kem_key) { + const Botan::PK_KEM_Encryptor enc(kem_key, "Raw"); + return enc.encapsulated_key_length(); + }}; return (f(*kex_kem_fn()) + ...); } template size_t length_of_hybrid_public_value(Ts... kex_kem_fn) { - Botan::overloaded f{[](const Botan::PK_Key_Agreement_Key& kex_key) { return kex_key.public_value().size(); }, - [](const Botan::Private_Key& kem_key) { return kem_key.public_key_bits().size(); }}; + const Botan::overloaded f{[](const Botan::PK_Key_Agreement_Key& kex_key) { return kex_key.public_value().size(); }, + [](const Botan::Private_Key& kem_key) { return kem_key.public_key_bits().size(); }}; return (f(*kex_kem_fn()) + ...); } +/// Public_Key::key_length() +template +size_t key_length_of_hybrid_public_key(Ts... kex_kem_fn) { + std::vector key_lengths = {kex_kem_fn()->key_length()...}; + return *std::max_element(key_lengths.begin(), key_lengths.end()); +} + +template +size_t estimated_strength_of_hybrid_public_key(Ts... kex_kem_fn) { + std::vector strengths = {kex_kem_fn()->estimated_strength()...}; + return *std::max_element(strengths.begin(), strengths.end()); +} + template 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()...)); + const Botan::TLS::Hybrid_KEM_PrivateKey hybrid_key(keys(kex_kem_fn()...)); + const Botan::TLS::Hybrid_KEM_PublicKey hybrid_public_key(pubkeys(kex_kem_fn()...)); auto& rng = global_test_rng(); @@ -136,40 +151,52 @@ const auto expected_shared_secret_length = length_of_hybrid_shared_key(kex_kem_fn...); const auto expected_ciphertext_length = length_of_hybrid_ciphertext(kex_kem_fn...); const auto expected_public_key_length = length_of_hybrid_public_value(kex_kem_fn...); + const auto expected_key_length = key_length_of_hybrid_public_key(kex_kem_fn...); + const auto expected_strength = estimated_strength_of_hybrid_public_key(kex_kem_fn...); - result.test_eq( + result.test_sz_eq( "ciphertext has expected length", kem_result.encapsulated_shared_key().size(), expected_ciphertext_length); - result.test_eq("shared secret has expected length", kem_result.shared_key().size(), expected_shared_secret_length); - result.test_eq( + result.test_sz_eq( + "shared secret has expected length", kem_result.shared_key().size(), expected_shared_secret_length); + result.test_sz_eq( "expected length of ciphertext is as expected", encryptor.encapsulated_key_length(), expected_ciphertext_length); - result.test_eq("shared secret has expected length", encryptor.shared_key_length(0), expected_shared_secret_length); + result.test_sz_eq( + "shared secret has expected length", encryptor.shared_key_length(0), expected_shared_secret_length); Botan::PK_KEM_Decryptor decryptor(hybrid_key, rng, "Raw"); Botan::secure_vector decaps_shared_secret = decryptor.decrypt(kem_result.encapsulated_shared_key(), 0, {}); - result.test_eq("shared secret after KEM roundtrip matches", decaps_shared_secret, kem_result.shared_key()); - result.test_eq( + result.test_bin_eq("shared secret after KEM roundtrip matches", decaps_shared_secret, kem_result.shared_key()); + result.test_sz_eq( "expected shared secret has expected length", decryptor.shared_key_length(0), expected_shared_secret_length); - result.test_eq("shared secret has expected length", decaps_shared_secret.size(), expected_shared_secret_length); + result.test_sz_eq("shared secret has expected length", decaps_shared_secret.size(), expected_shared_secret_length); - result.test_eq("public key bits is the sum of its parts", - hybrid_public_key.raw_public_key_bits().size(), - expected_public_key_length); + result.test_sz_eq("public key bits is the sum of its parts", + hybrid_public_key.raw_public_key_bits().size(), + expected_public_key_length); + + result.test_sz_eq( + "Public_Key::key_length is the maximum of its parts", hybrid_public_key.key_length(), expected_key_length); + result.test_sz_eq("Public_Key::estimated_strength is the maximum of its parts", + hybrid_public_key.estimated_strength(), + expected_strength); } std::vector hybrid_kem_keypair() { return { - CHECK("public handles empty list", - [](auto& result) { - result.test_throws("hybrid KEM key does not accept an empty list of keys", - [] { Botan::TLS::Hybrid_KEM_PublicKey({}); }); - }), - - CHECK("private handles empty list", - [](auto& result) { - result.test_throws("hybrid KEM key does not accept an empty list of keys", - [] { Botan::TLS::Hybrid_KEM_PrivateKey({}); }); - }), + Botan_Tests::CHECK("public handles empty list", + [](auto& result) { + result.test_throws("hybrid KEM key does not accept an empty list of keys", [] { + Botan::TLS::Hybrid_KEM_PublicKey(std::vector>(0)); + }); + }), + + Botan_Tests::CHECK("private handles empty list", + [](auto& result) { + result.test_throws("hybrid KEM key does not accept an empty list of keys", [] { + Botan::TLS::Hybrid_KEM_PrivateKey(std::vector>(0)); + }); + }), CHECK("public key handles nullptr", [&](auto& result) { @@ -215,48 +242,49 @@ void kex_to_kem_roundtrip(Test::Result& result, const std::function()>& kex_fn) { - Botan::TLS::KEX_to_KEM_Adapter_PrivateKey kexkem_key(kex_fn()); - Botan::TLS::KEX_to_KEM_Adapter_PublicKey kexkem_public_key(kex_fn()); + const Botan::KEX_to_KEM_Adapter_PrivateKey kexkem_key(kex_fn()); + const Botan::KEX_to_KEM_Adapter_PublicKey kexkem_public_key(kex_fn()); auto& rng = global_test_rng(); Botan::PK_KEM_Encryptor encryptor(kexkem_public_key, "Raw"); const auto kem_result = encryptor.encrypt(rng); - result.test_eq("ciphertext has expected length", - kem_result.encapsulated_shared_key().size(), - encryptor.encapsulated_key_length()); - result.test_eq("shared secret has expected length", kem_result.shared_key().size(), encryptor.shared_key_length(0)); + result.test_sz_eq("ciphertext has expected length", + kem_result.encapsulated_shared_key().size(), + encryptor.encapsulated_key_length()); + result.test_sz_eq( + "shared secret has expected length", kem_result.shared_key().size(), encryptor.shared_key_length(0)); Botan::PK_KEM_Decryptor decryptor(kexkem_key, rng, "Raw"); - result.test_eq("encapsulated length matches the decryptor's expectation", - kem_result.encapsulated_shared_key().size(), - decryptor.encapsulated_key_length()); + result.test_sz_eq("encapsulated length matches the decryptor's expectation", + kem_result.encapsulated_shared_key().size(), + decryptor.encapsulated_key_length()); Botan::secure_vector decaps_shared_secret = decryptor.decrypt(kem_result.encapsulated_shared_key(), 0, {}); - result.test_eq( + result.test_sz_eq( "decapsulated shared secret has expected length", decaps_shared_secret.size(), decryptor.shared_key_length(0)); - result.test_eq("shared secret after KEM roundtrip matches", decaps_shared_secret, kem_result.shared_key()); + result.test_bin_eq("shared secret after KEM roundtrip matches", decaps_shared_secret, kem_result.shared_key()); } std::vector kex_to_kem_adapter() { return { - CHECK("handles nullptr", - [](auto& result) { - result.test_throws("private KEM adapter handles nullptr", - [] { Botan::TLS::KEX_to_KEM_Adapter_PrivateKey(nullptr); }); - result.test_throws("public KEM adapter handles nullptr", - [] { Botan::TLS::KEX_to_KEM_Adapter_PublicKey(nullptr); }); - }), - - CHECK("handles non-KEX keys", - [](auto& result) { - result.test_throws("public KEM adapter does not work with KEM keys", - [] { Botan::TLS::KEX_to_KEM_Adapter_PublicKey{kem()}; }); - }), + Botan_Tests::CHECK("handles nullptr", + [](auto& result) { + result.test_throws("private KEM adapter handles nullptr", + [] { Botan::KEX_to_KEM_Adapter_PrivateKey(nullptr); }); + result.test_throws("public KEM adapter handles nullptr", + [] { Botan::KEX_to_KEM_Adapter_PublicKey(nullptr); }); + }), + + Botan_Tests::CHECK("handles non-KEX keys", + [](auto& result) { + result.test_throws("public KEM adapter does not work with KEM keys", + [] { Botan::KEX_to_KEM_Adapter_PublicKey{kem()}; }); + }), CHECK("Diffie-Hellman roundtrip", [](auto& result) { kex_to_kem_roundtrip(result, kex_dh); }), CHECK("ECDH roundtrip", [](auto& result) { kex_to_kem_roundtrip(result, kex_ecdh); }), diff -Nru botan3-3.7.1+dfsg/src/tests/test_tls_messages.cpp botan3-3.12.0+dfsg/src/tests/test_tls_messages.cpp --- botan3-3.7.1+dfsg/src/tests/test_tls_messages.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_tls_messages.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -10,20 +10,25 @@ #include "tests.h" #if defined(BOTAN_HAS_TLS) - #include #include #include #include #include #include - #include - #include + #include #include #include + #include #include + + #if defined(BOTAN_HAS_TLS_12) + #include + #endif + #if defined(BOTAN_HAS_TLS_13) #include "test_rng.h" - + #include + #include #include #endif #endif @@ -33,43 +38,46 @@ namespace { #if defined(BOTAN_HAS_TLS) + + #if defined(BOTAN_HAS_TLS_12) Test::Result test_hello_verify_request() { Test::Result result("hello_verify_request construction"); - std::vector test_data; + const std::vector test_data; std::vector key_data(32); - Botan::SymmetricKey sk(key_data); + const Botan::SymmetricKey sk(key_data); // Compute cookie over an empty string with an empty test data - Botan::TLS::Hello_Verify_Request hfr(test_data, "", sk); + const Botan::TLS::Hello_Verify_Request hfr(test_data, "", sk); // Compute HMAC auto hmac = Botan::MessageAuthenticationCode::create("HMAC(SHA-256)"); hmac->set_key(sk); hmac->update_be(uint64_t(0)); // length of client hello hmac->update_be(uint64_t(0)); // length of client identity - std::vector test = unlock(hmac->final()); + std::vector test = hmac->final>(); - result.test_eq("Cookie comparison", hfr.cookie(), test); + result.test_bin_eq("Cookie comparison", hfr.cookie(), test); return result; } + #endif class Test_Callbacks : public Botan::TLS::Callbacks { public: - Test_Callbacks(Test::Result& result) : m_result(result) {} + explicit Test_Callbacks(Test::Result& result) : m_result(result) {} public: - void tls_emit_data(std::span) override { + void tls_emit_data(std::span /*data*/) override { m_result.test_failure("unsolicited call to tls_emit_data"); } - void tls_record_received(uint64_t, std::span) override { + void tls_record_received(uint64_t /*rec_no*/, std::span /*data*/) override { m_result.test_failure("unsolicited call to tls_record_received"); } - void tls_alert(Botan::TLS::Alert) override { m_result.test_failure("unsolicited call to tls_alert"); } + void tls_alert(Botan::TLS::Alert /*alert*/) override { m_result.test_failure("unsolicited call to tls_alert"); } - void tls_session_established(const Botan::TLS::Session_Summary&) override { + void tls_session_established(const Botan::TLS::Session_Summary& /*session_info*/) override { m_result.test_failure("unsolicited call to tls_session_established"); } @@ -77,6 +85,7 @@ Test::Result& m_result; }; + #if defined(BOTAN_HAS_TLS_12) class TLS_Message_Parsing_Test final : public Text_Based_Test { public: TLS_Message_Parsing_Test() : @@ -95,56 +104,57 @@ if(is_positive_test) { try { if(algo == "cert_verify") { - Botan::TLS::Certificate_Verify message(buffer); + const Botan::TLS::Certificate_Verify message(buffer); } else if(algo == "client_hello") { const std::string extensions = vars.get_req_str("AdditionalData"); - Botan::TLS::Protocol_Version pv(protocol[0], protocol[1]); - Botan::TLS::Client_Hello_12 message(buffer); - result.test_eq("Protocol version", message.legacy_version().to_string(), pv.to_string()); + const Botan::TLS::Protocol_Version pv(protocol[0], protocol[1]); + const Botan::TLS::Client_Hello_12 message(buffer); + result.test_str_eq("Protocol version", message.legacy_version().to_string(), pv.to_string()); std::vector buf; for(const Botan::TLS::Extension_Code& type : message.extension_types()) { - uint16_t u16type = static_cast(type); + const uint16_t u16type = static_cast(type); buf.push_back(Botan::get_byte<0>(u16type)); buf.push_back(Botan::get_byte<1>(u16type)); } - result.test_eq("Hello extensions", Botan::hex_encode(buf), extensions); + result.test_bin_eq("Hello extensions", buf, extensions); } else if(algo == "hello_verify") { - Botan::TLS::Hello_Verify_Request message(buffer); + const Botan::TLS::Hello_Verify_Request message(buffer); } else if(algo == "hello_request") { - Botan::TLS::Hello_Request message(buffer); + const Botan::TLS::Hello_Request message(buffer); } else if(algo == "new_session_ticket") { - Botan::TLS::New_Session_Ticket_12 message(buffer); + const Botan::TLS::New_Session_Ticket_12 message(buffer); } else if(algo == "server_hello") { const std::string extensions = vars.get_req_str("AdditionalData"); - Botan::TLS::Protocol_Version pv(protocol[0], protocol[1]); - Botan::TLS::Ciphersuite cs = + const Botan::TLS::Protocol_Version pv(protocol[0], protocol[1]); + const Botan::TLS::Ciphersuite cs = Botan::TLS::Ciphersuite::by_id(Botan::make_uint16(ciphersuite[0], ciphersuite[1])).value(); - Botan::TLS::Server_Hello_12 message(buffer); - result.test_eq("Protocol version", message.legacy_version().to_string(), pv.to_string()); - result.confirm("Ciphersuite", (message.ciphersuite() == cs.ciphersuite_code())); + const Botan::TLS::Server_Hello_12 message(buffer); + result.test_str_eq("Protocol version", message.legacy_version().to_string(), pv.to_string()); + result.test_is_true("Ciphersuite", (message.ciphersuite() == cs.ciphersuite_code())); std::vector buf; for(const Botan::TLS::Extension_Code& type : message.extension_types()) { - uint16_t u16type = static_cast(type); + const uint16_t u16type = static_cast(type); buf.push_back(Botan::get_byte<0>(u16type)); buf.push_back(Botan::get_byte<1>(u16type)); } - result.test_eq("Hello extensions", Botan::hex_encode(buf), extensions); + result.test_bin_eq("Hello extensions", buf, extensions); } else if(algo == "alert") { - Botan::secure_vector sb(buffer.begin(), buffer.end()); - Botan::TLS::Alert message(sb); - result.test_lt("Alert type vectors result to UNKNOWN_CA or ACCESS_DENIED, which is shorter than 15", - message.type_string().size(), - 15); + const Botan::secure_vector sb(buffer.begin(), buffer.end()); + const Botan::TLS::Alert message(sb); + result.test_sz_lt( + "Alert type vectors result to UNKNOWN_CA or ACCESS_DENIED, which is shorter than 15", + message.type_string().size(), + 15); } else if(algo == "cert_status") { - Botan::TLS::Certificate_Status message(buffer, Botan::TLS::Connection_Side::Server); + const Botan::TLS::Certificate_Status message(buffer, Botan::TLS::Connection_Side::Server); - Botan::OCSP::Response resp(message.response()); + const Botan::OCSP::Response resp(message.response()); const std::vector CNs = resp.signer_name().get_attribute("CN"); - // This is not requird by OCSP protocol, we are just using it as a test here - if(result.test_eq("OCSP response has signer name", CNs.size(), 1)) { - result.test_eq("Expected name", CNs[0], expected_name); + // This is not required by OCSP protocol, we are just using it as a test here + if(result.test_sz_eq("OCSP response has signer name", CNs.size(), 1)) { + result.test_str_eq("Expected name", CNs[0], expected_name); } } else { throw Test_Error("Unknown message type " + algo + " in TLS parsing tests"); @@ -156,35 +166,36 @@ } else { if(algo == "cert_verify") { result.test_throws("invalid cert_verify input", exception, [&buffer]() { - Botan::TLS::Certificate_Verify message(buffer); + const Botan::TLS::Certificate_Verify message(buffer); }); } else if(algo == "client_hello") { result.test_throws("invalid client_hello input", exception, [&buffer]() { - Botan::TLS::Client_Hello_12 message(buffer); + const Botan::TLS::Client_Hello_12 message(buffer); }); } else if(algo == "hello_verify") { result.test_throws("invalid hello_verify input", exception, [&buffer]() { - Botan::TLS::Hello_Verify_Request message(buffer); + const Botan::TLS::Hello_Verify_Request message(buffer); }); } else if(algo == "hello_request") { - result.test_throws( - "invalid hello_request input", exception, [&buffer]() { Botan::TLS::Hello_Request message(buffer); }); + result.test_throws("invalid hello_request input", exception, [&buffer]() { + const Botan::TLS::Hello_Request message(buffer); + }); } else if(algo == "cert_status") { result.test_throws("invalid cert_status input", exception, [&buffer]() { - Botan::TLS::Certificate_Status message(buffer, Botan::TLS::Connection_Side::Server); + const Botan::TLS::Certificate_Status message(buffer, Botan::TLS::Connection_Side::Server); }); } else if(algo == "new_session_ticket") { result.test_throws("invalid new_session_ticket input", exception, [&buffer]() { - Botan::TLS::New_Session_Ticket_12 message(buffer); + const Botan::TLS::New_Session_Ticket_12 message(buffer); }); } else if(algo == "server_hello") { result.test_throws("invalid server_hello input", exception, [&buffer]() { - Botan::TLS::Server_Hello_12 message(buffer); + const Botan::TLS::Server_Hello_12 message(buffer); }); } else if(algo == "alert") { result.test_throws("invalid alert input", exception, [&buffer]() { - Botan::secure_vector sb(buffer.begin(), buffer.end()); - Botan::TLS::Alert message(sb); + const Botan::secure_vector sb(buffer.begin(), buffer.end()); + const Botan::TLS::Alert message(sb); }); } else { throw Test_Error("Unknown message type " + algo + " in TLS parsing tests"); @@ -204,6 +215,7 @@ }; BOTAN_REGISTER_TEST("tls", "tls_messages", TLS_Message_Parsing_Test); + #endif #if defined(BOTAN_HAS_TLS_13) #if defined(BOTAN_HAS_X25519) @@ -223,17 +235,17 @@ const auto expected_key_share = vars.get_req_bin("Expected_Content"); Test_Callbacks cb(result); - Botan::TLS::Text_Policy policy("key_exchange_groups = " + groups + - "\n" - "key_exchange_groups_to_offer = " + - offered_groups); + const Botan::TLS::Text_Policy policy("key_exchange_groups = " + groups + + "\n" + "key_exchange_groups_to_offer = " + + offered_groups); Fixed_Output_RNG rng; rng.add_entropy(rng_data.data(), rng_data.size()); - Botan::TLS::Key_Share share(policy, cb, rng); + const Botan::TLS::Key_Share share(policy, cb, rng); const auto serialized_buffer = share.serialize(Botan::TLS::Connection_Side::Client); - result.test_eq("key_share_CH_offers test", serialized_buffer, expected_key_share); + result.test_bin_eq("key_share_CH_offers test", serialized_buffer, expected_key_share); return result; } @@ -252,8 +264,6 @@ Test::Result run_one_test(const std::string& extension, const VarMap& vars) override { const std::vector buffer = vars.get_req_bin("Buffer"); - const std::vector protocol = vars.get_opt_bin("Protocol"); - const std::vector ciphersuite = vars.get_opt_bin("Ciphersuite"); const std::string exception = vars.get_req_str("Exception"); const bool is_positive_test = exception.empty(); @@ -262,23 +272,23 @@ if(is_positive_test) { try { if(extension == "supported_version") { - const std::string expected_buffer = Botan::hex_encode(buffer); Botan::TLS::TLS_Data_Reader tls_data_reader("ClientHello", buffer); - Botan::TLS::Supported_Versions supported_versions( + const Botan::TLS::Supported_Versions supported_versions( tls_data_reader, static_cast(buffer.size()), Botan::TLS::Connection_Side::Client); const auto serialized_buffer = supported_versions.serialize(Botan::TLS::Connection_Side::Client); const std::vector> expected_versions = vars.get_req_bin_list("Expected_Content"); for(const auto& expected_version : expected_versions) { - result.confirm("Expected_Content", - supported_versions.supports( - Botan::TLS::Protocol_Version(expected_version[0], expected_version[1]))); + result.test_is_true("Expected_Content", + supported_versions.supports( + Botan::TLS::Protocol_Version(expected_version[0], expected_version[1]))); } - result.test_eq("supported_version test 1", Botan::hex_encode(serialized_buffer), expected_buffer); + result.test_bin_eq("supported_version test 1", serialized_buffer, buffer); } else if(extension == "supported_groups") { Botan::TLS::TLS_Data_Reader tls_data_reader("ClientHello", buffer); - Botan::TLS::Supported_Groups supp_groups_ext(tls_data_reader, static_cast(buffer.size())); + const Botan::TLS::Supported_Groups supp_groups_ext(tls_data_reader, + static_cast(buffer.size())); const auto serialized_buffer = supp_groups_ext.serialize(Botan::TLS::Connection_Side::Client); const auto expected_content = vars.get_req_bin("Expected_Content"); @@ -286,94 +296,103 @@ const auto dh_groups = supp_groups_ext.dh_groups(); const auto ec_groups = supp_groups_ext.ec_groups(); - std::vector named_groupes; + std::vector named_groups; std::merge(dh_groups.begin(), dh_groups.end(), ec_groups.begin(), ec_groups.end(), - std::back_inserter(named_groupes)); + std::back_inserter(named_groups)); - result.confirm("supported_groups extension - size check", - (named_groupes.size() * 2) == expected_content.size()); + result.test_is_true("supported_groups extension - size check", + (named_groups.size() * 2) == expected_content.size()); for(size_t i = 0; i < expected_content.size(); i += 2) { const auto expected_named_group = Botan::make_uint16(expected_content.at(i), expected_content.at(i + 1)); - result.confirm("signature_algorithms_cert extension - named group check", - std::any_of(named_groupes.cbegin(), - named_groupes.cend(), - [&expected_named_group](const Botan::TLS::Named_Group& named_group) { - return static_cast(expected_named_group) == - named_group; - })); + result.test_is_true( + "signature_algorithms_cert extension - named group check", + std::any_of(named_groups.cbegin(), + named_groups.cend(), + [&expected_named_group](const Botan::TLS::Named_Group& named_group) { + return static_cast(expected_named_group) == named_group; + })); } - result.test_eq("supported_groups extension - serialization test", serialized_buffer, buffer); + result.test_bin_eq("supported_groups extension - serialization test", serialized_buffer, buffer); } else if(extension == "signature_algorithms_cert") { Botan::TLS::TLS_Data_Reader tls_data_reader("ClientHello", buffer); - Botan::TLS::Signature_Algorithms_Cert sig_algo_cert(tls_data_reader, - static_cast(buffer.size())); + const Botan::TLS::Signature_Algorithms_Cert sig_algo_cert(tls_data_reader, + static_cast(buffer.size())); const auto serialized_buffer = sig_algo_cert.serialize(Botan::TLS::Connection_Side::Client); const auto expected_content = vars.get_req_bin("Expected_Content"); - result.confirm("signature_algorithms_cert extension - size check", - sig_algo_cert.supported_schemes().size() * 2 == expected_content.size()); + result.test_is_true("signature_algorithms_cert extension - size check", + sig_algo_cert.supported_schemes().size() * 2 == expected_content.size()); size_t offset = 0; for(const auto& sig_scheme : sig_algo_cert.supported_schemes()) { const auto expected_sig_scheme = Botan::make_uint16(expected_content.at(offset), expected_content.at(offset + 1)); - result.confirm("signature_algorithms_cert extension - sig scheme check", - Botan::TLS::Signature_Scheme(expected_sig_scheme) == sig_scheme); + result.test_is_true("signature_algorithms_cert extension - sig scheme check", + Botan::TLS::Signature_Scheme(expected_sig_scheme) == sig_scheme); offset += 2; } - result.test_eq("signature_algorithms_cert extension - serialization test", serialized_buffer, buffer); + result.test_bin_eq( + "signature_algorithms_cert extension - serialization test", serialized_buffer, buffer); } else if(extension == "cookie") { Botan::TLS::TLS_Data_Reader tls_data_reader("HelloRetryRequest", buffer); - Botan::TLS::Cookie cookie(tls_data_reader, static_cast(buffer.size())); + const Botan::TLS::Cookie cookie(tls_data_reader, static_cast(buffer.size())); const auto serialized_buffer = cookie.serialize(Botan::TLS::Connection_Side::Server); const auto expected_cookie = vars.get_req_bin("Expected_Content"); - result.test_eq("Cookie extension test", - Botan::hex_encode(expected_cookie), - Botan::hex_encode(cookie.get_cookie())); + result.test_bin_eq("Cookie extension test", expected_cookie, cookie.get_cookie()); } else if(extension == "key_share_HRR") { Botan::TLS::TLS_Data_Reader tls_data_reader("HelloRetryRequest", buffer); - Botan::TLS::Key_Share key_share(tls_data_reader, - static_cast(buffer.size()), - Botan::TLS::Handshake_Type::HelloRetryRequest); + const Botan::TLS::Key_Share key_share(tls_data_reader, + static_cast(buffer.size()), + Botan::TLS::Handshake_Type::HelloRetryRequest); const auto serialized_buffer = key_share.serialize(Botan::TLS::Connection_Side::Client); const auto expected_key_share = vars.get_req_bin("Expected_Content"); - result.test_eq( - "key_share_HRR test", Botan::hex_encode(serialized_buffer), Botan::hex_encode(expected_key_share)); + result.test_bin_eq("key_share_HRR test", serialized_buffer, expected_key_share); } else if(extension == "key_share_SH") { Botan::TLS::TLS_Data_Reader tls_data_reader("ServerHello", buffer); - Botan::TLS::Key_Share key_share( + const Botan::TLS::Key_Share key_share( tls_data_reader, static_cast(buffer.size()), Botan::TLS::Handshake_Type::ServerHello); const auto serialized_buffer = key_share.serialize(Botan::TLS::Connection_Side::Client); const auto expected_key_share = vars.get_req_bin("Expected_Content"); - result.test_eq( - "key_share_SH test", Botan::hex_encode(serialized_buffer), Botan::hex_encode(expected_key_share)); + result.test_bin_eq("key_share_SH test", serialized_buffer, expected_key_share); } else if(extension == "key_share_CH") { Botan::TLS::TLS_Data_Reader tls_data_reader("ClientHello", buffer); - Botan::TLS::Key_Share key_share( + const Botan::TLS::Key_Share key_share( tls_data_reader, static_cast(buffer.size()), Botan::TLS::Handshake_Type::ClientHello); const auto serialized_buffer = key_share.serialize(Botan::TLS::Connection_Side::Server); const auto expected_key_share = vars.get_req_bin("Expected_Content"); - result.test_eq( - "key_share_CH test", Botan::hex_encode(serialized_buffer), Botan::hex_encode(expected_key_share)); + result.test_bin_eq("key_share_CH test", serialized_buffer, expected_key_share); + } else if(extension == "alpn") { + Botan::TLS::TLS_Data_Reader tls_data_reader("ClientHello", buffer); + const Botan::TLS::Application_Layer_Protocol_Notification alpn( + tls_data_reader, static_cast(buffer.size()), Botan::TLS::Connection_Side::Client); + + std::string protocols_joined; + for(const auto& p : alpn.protocols()) { + if(!protocols_joined.empty()) { + protocols_joined.push_back(','); + } + protocols_joined += p; + } + result.test_str_eq("alpn protocols", protocols_joined, vars.get_req_str("Expected_Content")); } else { throw Test_Error("Unknown extension type " + extension + " in TLS parsing tests"); } @@ -382,6 +401,51 @@ result.test_failure(e.what()); } } else { + if(extension == "cookie") { + result.test_throws("invalid cookie extension input", exception, [&buffer]() { + Botan::TLS::TLS_Data_Reader tls_data_reader("HelloRetryRequest", buffer); + const Botan::TLS::Cookie cookie(tls_data_reader, static_cast(buffer.size())); + }); + } else if(extension == "supported_groups") { + result.test_throws("invalid supported_groups extension input", exception, [&buffer]() { + Botan::TLS::TLS_Data_Reader tls_data_reader("ClientHello", buffer); + const Botan::TLS::Supported_Groups supp_groups_ext(tls_data_reader, + static_cast(buffer.size())); + }); + } else if(extension == "key_share_CH") { + result.test_throws("invalid key_share_CH extension input", exception, [&buffer]() { + Botan::TLS::TLS_Data_Reader tls_data_reader("ClientHello", buffer); + const Botan::TLS::Key_Share key_share( + tls_data_reader, static_cast(buffer.size()), Botan::TLS::Handshake_Type::ClientHello); + }); + } else if(extension == "key_share_HRR") { + result.test_throws("invalid key_share_HRR extension input", exception, [&buffer]() { + Botan::TLS::TLS_Data_Reader tls_data_reader("HelloRetryRequest", buffer); + const Botan::TLS::Key_Share key_share(tls_data_reader, + static_cast(buffer.size()), + Botan::TLS::Handshake_Type::HelloRetryRequest); + }); + } else if(extension == "key_share_SH") { + result.test_throws("invalid key_share_SH extension input", exception, [&buffer]() { + Botan::TLS::TLS_Data_Reader tls_data_reader("ServerHello", buffer); + const Botan::TLS::Key_Share key_share( + tls_data_reader, static_cast(buffer.size()), Botan::TLS::Handshake_Type::ServerHello); + }); + } else if(extension == "signature_algorithms_cert") { + result.test_throws("invalid signature_algorithms_cert extension input", exception, [&buffer]() { + Botan::TLS::TLS_Data_Reader tls_data_reader("Extension", buffer); + const Botan::TLS::Signature_Algorithms_Cert sig_algo_cert(tls_data_reader, + static_cast(buffer.size())); + }); + } else if(extension == "alpn") { + result.test_throws("invalid alpn extension input", exception, [&buffer]() { + Botan::TLS::TLS_Data_Reader tls_data_reader("ClientHello", buffer); + const Botan::TLS::Application_Layer_Protocol_Notification alpn( + tls_data_reader, static_cast(buffer.size()), Botan::TLS::Connection_Side::Client); + }); + } else { + throw Test_Error("Unknown extension type " + extension + " in TLS parsing negative tests"); + } } return result; @@ -390,7 +454,9 @@ std::vector run_final_tests() override { std::vector results; + #if defined(BOTAN_HAS_TLS_12) results.push_back(test_hello_verify_request()); + #endif return results; } @@ -417,35 +483,35 @@ try { std::visit( [&](auto ch) { - if constexpr(std::is_same_v) { - result.confirm("expected Client_Hello_12", msg_type == "client_hello_12"); + if constexpr(std::is_same_v) { + result.test_is_true("expected Client_Hello_12_Shim", msg_type == "client_hello_12"); } if constexpr(std::is_same_v) { - result.confirm("expected Client_Hello_13", msg_type == "client_hello_13"); + result.test_is_true("expected Client_Hello_13", msg_type == "client_hello_13"); } const std::string extensions = vars.get_req_str("AdditionalData"); std::vector exts_buffer; - for(Botan::TLS::Extension_Code const& type : ch.extensions().extension_types()) { - uint16_t u16type = static_cast(type); + for(const Botan::TLS::Extension_Code& type : ch.extensions().extension_types()) { + const uint16_t u16type = static_cast(type); exts_buffer.push_back(Botan::get_byte<0>(u16type)); exts_buffer.push_back(Botan::get_byte<1>(u16type)); } - result.test_eq("Hello extensions", Botan::hex_encode(exts_buffer), extensions); + result.test_bin_eq("Hello extensions", exts_buffer, extensions); std::vector ciphersuites_buffer; for(const auto& cs : ch.ciphersuites()) { ciphersuites_buffer.push_back(Botan::get_byte<0>(cs)); ciphersuites_buffer.push_back(Botan::get_byte<1>(cs)); } - result.test_eq("Supported ciphersuites", ciphersuites_buffer, ciphersuite); + result.test_bin_eq("Supported ciphersuites", ciphersuites_buffer, ciphersuite); - result.confirm("this is a positive test that should not have failed yet", is_positive_test); + result.test_is_true("this is a positive test that should not have failed yet", is_positive_test); }, Botan::TLS::Client_Hello_13::parse(buffer)); } catch(const std::exception& ex) { - result.test_eq("correct error produced", ex.what(), exception); - result.confirm("negative test", !is_positive_test); + result.test_str_eq("correct error produced", ex.what(), exception); + result.test_is_true("negative test", !is_positive_test); } } @@ -458,29 +524,29 @@ try { std::visit( [&](auto msg) { - if constexpr(std::is_same_v) { - result.confirm("expected Server_Hello_12", msg_type == "server_hello_12"); - result.confirm("expected pre TLS 1.3 message", pv == msg.legacy_version()); + if constexpr(std::is_same_v) { + result.test_is_true("expected Server_Hello_12", msg_type == "server_hello_12"); + result.test_is_true("expected pre TLS 1.3 message", pv == msg.selected_version()); } else if constexpr(std::is_same_v) { - result.confirm("expected Server_Hello_13", msg_type == "server_hello_13"); + result.test_is_true("expected Server_Hello_13", msg_type == "server_hello_13"); } else if constexpr(std::is_same_v) { - result.confirm("expected Hello_Retry_Request", msg_type == "hello_retry_request"); + result.test_is_true("expected Hello_Retry_Request", msg_type == "hello_retry_request"); } - result.confirm("Ciphersuite", (msg.ciphersuite() == cs.ciphersuite_code())); + result.test_is_true("Ciphersuite", (msg.ciphersuite() == cs.ciphersuite_code())); std::vector buf; - for(Botan::TLS::Extension_Code const& type : msg.extensions().extension_types()) { - uint16_t u16type = static_cast(type); + for(const Botan::TLS::Extension_Code& type : msg.extensions().extension_types()) { + const uint16_t u16type = static_cast(type); buf.push_back(Botan::get_byte<0>(u16type)); buf.push_back(Botan::get_byte<1>(u16type)); } - result.test_eq("Hello extensions", Botan::hex_encode(buf), extensions); + result.test_bin_eq("Hello extensions", buf, extensions); }, Botan::TLS::Server_Hello_13::parse(buffer)); } catch(const std::exception& ex) { - result.test_eq("correct error produced", ex.what(), exception); - result.confirm("negative test", !is_positive_test); + result.test_str_eq("correct error produced", ex.what(), exception); + result.test_is_true("negative test", !is_positive_test); } } diff -Nru botan3-3.7.1+dfsg/src/tests/test_tls_record_layer_13.cpp botan3-3.12.0+dfsg/src/tests/test_tls_record_layer_13.cpp --- botan3-3.7.1+dfsg/src/tests/test_tls_record_layer_13.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_tls_record_layer_13.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,16 +9,15 @@ #if defined(BOTAN_HAS_TLS_13) + #include #include #include #include - #include + #include + #include #include #include - - #include #include - #include namespace Botan_Tests { @@ -53,7 +52,7 @@ class Mocked_Secret_Logger : public Botan::TLS::Secret_Logger { public: - void maybe_log_secret(std::string_view, std::span) const override {} + void maybe_log_secret(std::string_view /*label*/, std::span /*secret*/) const override {} }; std::unique_ptr rfc8448_rtt1_handshake_traffic( @@ -65,7 +64,7 @@ "8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d" "35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d"); auto cipher = TLS::Ciphersuite::from_name("AES_128_GCM_SHA256").value(); - Mocked_Secret_Logger logger; + const Mocked_Secret_Logger logger; return TLS::Cipher_State::init_with_server_hello(side, std::move(shared_secret), cipher, transcript_hash, logger); } @@ -92,10 +91,10 @@ result.require("received something", std::holds_alternative(read)); auto record = std::get(read); - result.confirm("received CCS", record.type == TLS::Record_Type::ChangeCipherSpec); - result.test_eq("CCS byte is 0x01", record.fragment, Botan::hex_decode("01")); + result.test_enum_eq("received CCS", record.type, TLS::Record_Type::ChangeCipherSpec); + result.test_bin_eq("CCS byte is 0x01", record.fragment, "01"); - result.confirm("no more records", std::holds_alternative(rl.next_record())); + result.test_is_true("no more records", std::holds_alternative(rl.next_record())); }), CHECK("two CCS messages", @@ -110,17 +109,17 @@ result.require("received something", std::holds_alternative(read)); auto record = std::get(read); - result.confirm("received CCS 1", record.type == TLS::Record_Type::ChangeCipherSpec); - result.test_eq("CCS byte is 0x01", record.fragment, Botan::hex_decode("01")); + result.test_enum_eq("received CCS 1", record.type, TLS::Record_Type::ChangeCipherSpec); + result.test_bin_eq("CCS byte is 0x01", record.fragment, "01"); read = rl.next_record(); result.require("received something", std::holds_alternative(read)); record = std::get(read); - result.confirm("received CCS 2", record.type == TLS::Record_Type::ChangeCipherSpec); - result.test_eq("CCS byte is 0x01", record.fragment, Botan::hex_decode("01")); + result.test_enum_eq("received CCS 2", record.type, TLS::Record_Type::ChangeCipherSpec); + result.test_bin_eq("CCS byte is 0x01", record.fragment, "01"); - result.confirm("no more records", std::holds_alternative(rl.next_record())); + result.test_is_true("no more records", std::holds_alternative(rl.next_record())); }), CHECK("read full handshake message", @@ -129,16 +128,16 @@ rl.copy_data(client_hello_record); auto read = rl.next_record(); - result.confirm("received something", std::holds_alternative(read)); + result.test_is_true("received something", std::holds_alternative(read)); auto rec = std::get(read); - result.confirm("received handshake record", rec.type == TLS::Record_Type::Handshake); - result.test_eq("contains the full handshake message", - Botan::secure_vector(client_hello_record.begin() + TLS::TLS_HEADER_SIZE, - client_hello_record.end()), - rec.fragment); + result.test_is_true("received handshake record", rec.type == TLS::Record_Type::Handshake); + result.test_bin_eq("contains the full handshake message", + Botan::secure_vector(client_hello_record.begin() + TLS::TLS_HEADER_SIZE, + client_hello_record.end()), + rec.fragment); - result.confirm("no more records", std::holds_alternative(rl.next_record())); + result.test_is_true("no more records", std::holds_alternative(rl.next_record())); }), CHECK("read full handshake message followed by CCS", [&](auto& result) { @@ -151,20 +150,20 @@ result.require("received something", std::holds_alternative(read)); auto rec = std::get(read); - result.confirm("received handshake record", rec.type == TLS::Record_Type::Handshake); - result.test_eq("contains the full handshake message", - Botan::secure_vector(client_hello_record.begin() + TLS::TLS_HEADER_SIZE, - client_hello_record.end()), - rec.fragment); + result.test_is_true("received handshake record", rec.type == TLS::Record_Type::Handshake); + result.test_bin_eq("contains the full handshake message", + Botan::secure_vector(client_hello_record.begin() + TLS::TLS_HEADER_SIZE, + client_hello_record.end()), + rec.fragment); read = rl.next_record(); result.require("received something", std::holds_alternative(read)); rec = std::get(read); - result.confirm("received CCS record", rec.type == TLS::Record_Type::ChangeCipherSpec); - result.test_eq("CCS byte is 0x01", rec.fragment, Botan::hex_decode("01")); + result.test_enum_eq("received CCS record", rec.type, TLS::Record_Type::ChangeCipherSpec); + result.test_bin_eq("CCS byte is 0x01", rec.fragment, "01"); - result.confirm("no more records", std::holds_alternative(rl.next_record())); + result.test_is_true("no more records", std::holds_alternative(rl.next_record())); })}; } @@ -179,33 +178,33 @@ [&](auto& result) { auto read = parse_records({}); result.require("needs bytes", std::holds_alternative(read)); - result.test_eq( + result.test_sz_eq( "need all the header bytes", std::get(read), Botan::TLS::TLS_HEADER_SIZE); }), CHECK("incomplete header asks for more data", [&](auto& result) { - std::vector partial_header{'\x23', '\x03', '\x03'}; + const std::vector partial_header{'\x23', '\x03', '\x03'}; auto read = parse_records(partial_header); result.require("returned 'bytes needed'", std::holds_alternative(read)); - result.test_eq("asks for some more bytes", - std::get(read), - Botan::TLS::TLS_HEADER_SIZE - partial_header.size()); + result.test_sz_eq("asks for some more bytes", + std::get(read), + Botan::TLS::TLS_HEADER_SIZE - partial_header.size()); }), CHECK("complete header asks for enough data to finish processing the record", [&](auto& result) { - std::vector full_header{'\x17', '\x03', '\x03', '\x00', '\x42'}; + const std::vector full_header{'\x17', '\x03', '\x03', '\x00', '\x42'}; auto read = parse_records(full_header); result.require("returned 'bytes needed'", std::holds_alternative(read)); - result.test_eq("asks for many more bytes", std::get(read), 0x42); + result.test_sz_eq("asks for many more bytes", std::get(read), 0x42); }), CHECK("received an empty record (that is not application data)", [&](auto& result) { - std::vector empty_record{'\x16', '\x03', '\x03', '\x00', '\x00'}; + const std::vector empty_record{'\x16', '\x03', '\x03', '\x00', '\x00'}; result.test_throws("record empty", "empty record received", [&] { parse_records(empty_record); }); }), @@ -214,7 +213,7 @@ std::vector full_record{'\x16', '\x03', '\x03', '\x40', '\x00'}; full_record.resize(TLS::MAX_PLAINTEXT_SIZE + TLS::TLS_HEADER_SIZE); auto read = parse_records(full_record); - result.confirm("returned 'record'", !std::holds_alternative(read)); + result.test_is_true("returned 'record'", !std::holds_alternative(read)); }), CHECK("received too many bytes in one protected record", @@ -252,7 +251,7 @@ CHECK("invalid record type", [&](auto& result) { - std::vector invalid_record_type{'\x42', '\x03', '\x03', '\x41', '\x01'}; + const std::vector invalid_record_type{'\x42', '\x03', '\x03', '\x41', '\x01'}; result.test_throws("invalid record type", "TLS record type had unexpected value", [&] { parse_records(invalid_record_type); }); @@ -260,7 +259,7 @@ CHECK("invalid record version", [&](auto& result) { - std::vector invalid_record_version{'\x17', '\x13', '\x37', '\x00', '\x01', '\x42'}; + const std::vector invalid_record_version{'\x17', '\x13', '\x37', '\x00', '\x01', '\x42'}; result.test_throws("invalid record version", "Received unexpected record version", [&] { parse_records(invalid_record_version); }); @@ -292,7 +291,7 @@ CHECK("malformed change cipher spec", [&](auto& result) { - std::vector invalid_ccs_record{'\x14', '\x03', '\x03', '\x00', '\x01', '\x02'}; + const std::vector invalid_ccs_record{'\x14', '\x03', '\x03', '\x00', '\x01', '\x02'}; result.test_throws("invalid CCS record", "malformed change cipher spec record received", [&] { parse_records(invalid_ccs_record); }); @@ -316,14 +315,14 @@ [](Botan::TLS::BytesNeeded bytes_needed, auto& record_layer, std::vector bytes, auto& result) { record_layer.copy_data(bytes); const auto rlr = record_layer.next_record(); - if(result.confirm("waiting for bytes", std::holds_alternative(rlr))) { - result.test_eq("right amount", std::get(rlr), bytes_needed); + if(result.test_is_true("waiting for bytes", std::holds_alternative(rlr))) { + result.test_sz_eq("right amount", std::get(rlr), bytes_needed); } }; return {CHECK("change cipher spec in many small pieces", [&](auto& result) { - std::vector ccs_record{'\x14', '\x03', '\x03', '\x00', '\x01', '\x01'}; + const std::vector ccs_record{'\x14', '\x03', '\x03', '\x00', '\x01', '\x01'}; wait_for_more_bytes(4, rl, {'\x14'}, result); wait_for_more_bytes(3, rl, {'\x03'}, result); @@ -336,10 +335,10 @@ result.require("received something 1", std::holds_alternative(res1)); auto rec1 = std::get(res1); - result.confirm("received CCS", rec1.type == TLS::Record_Type::ChangeCipherSpec); - result.test_eq("CCS byte is 0x01", rec1.fragment, Botan::hex_decode("01")); + result.test_enum_eq("received CCS", rec1.type, TLS::Record_Type::ChangeCipherSpec); + result.test_bin_eq("CCS byte is 0x01", rec1.fragment, "01"); - result.confirm("no more records", std::holds_alternative(rl.next_record())); + result.test_is_true("no more records", std::holds_alternative(rl.next_record())); }), CHECK("two change cipher specs in several pieces", [&](auto& result) { @@ -351,8 +350,8 @@ result.require("received something 2", std::holds_alternative(res2)); auto rec2 = std::get(res2); - result.confirm("received CCS", rec2.type == TLS::Record_Type::ChangeCipherSpec); - result.confirm("demands more bytes", std::holds_alternative(rl.next_record())); + result.test_enum_eq("received CCS", rec2.type, TLS::Record_Type::ChangeCipherSpec); + result.test_is_true("demands more bytes", std::holds_alternative(rl.next_record())); wait_for_more_bytes(2, rl, {'\x03'}, result); @@ -361,9 +360,9 @@ result.require("received something 3", std::holds_alternative(res3)); auto rec3 = std::get(res3); - result.confirm("received CCS", rec3.type == TLS::Record_Type::ChangeCipherSpec); + result.test_enum_eq("received CCS", rec3.type, TLS::Record_Type::ChangeCipherSpec); - result.confirm("no more records", std::holds_alternative(rl.next_record())); + result.test_is_true("no more records", std::holds_alternative(rl.next_record())); })}; } @@ -398,7 +397,7 @@ record.size() == client_hello_msg.size() + Botan::TLS::TLS_HEADER_SIZE); const auto header = std::vector(record.cbegin(), record.cbegin() + Botan::TLS::TLS_HEADER_SIZE); - result.test_eq("record header is well-formed", header, Botan::hex_decode("16030100c4")); + result.test_bin_eq("record header is well-formed", header, "16030100c4"); }), CHECK("prepare a dummy CCS", [&](auto& result) { @@ -407,7 +406,7 @@ record_layer_client(true).prepare_records(Botan::TLS::Record_Type::ChangeCipherSpec, ccs_content); result.require("record was created", record.size() == Botan::TLS::TLS_HEADER_SIZE + 1); - result.test_eq("CCS record is well-formed", record, Botan::hex_decode("140303000101")); + result.test_bin_eq("CCS record is well-formed", record, "140303000101"); }), CHECK("cannot prepare non-dummy CCS", [&](auto& result) { @@ -420,9 +419,9 @@ const std::vector large_client_hello(Botan::TLS::MAX_PLAINTEXT_SIZE + 4096); auto record = record_layer_client().prepare_records(Botan::TLS::Record_Type::Handshake, large_client_hello); - result.test_gte("produces at least two record headers", - record.size(), - large_client_hello.size() + 2 * Botan::TLS::TLS_HEADER_SIZE); + result.test_sz_gte("produces at least two record headers", + record.size(), + large_client_hello.size() + 2 * Botan::TLS::TLS_HEADER_SIZE); })}; } @@ -521,10 +520,10 @@ result.require("some records decrypted", !std::holds_alternative(res)); auto record = std::get(res); - result.test_is_eq("inner type was 'HANDSHAKE'", record.type, Botan::TLS::Record_Type::Handshake); - result.test_eq("decrypted payload length", record.fragment.size(), 657 /* taken from RFC 8448 */); + result.test_enum_eq("inner type was 'HANDSHAKE'", record.type, Botan::TLS::Record_Type::Handshake); + result.test_sz_eq("decrypted payload length", record.fragment.size(), 657 /* taken from RFC 8448 */); - result.confirm("no more records", std::holds_alternative(rl.next_record())); + result.test_is_true("no more records", std::holds_alternative(rl.next_record())); }), CHECK("premature application data", @@ -650,7 +649,7 @@ auto cs = rfc8448_rtt1_handshake_traffic(); // advance with arbitrary hashes that were used to produce the input data - Mocked_Secret_Logger logger; + const Mocked_Secret_Logger logger; cs->advance_with_server_finished( Botan::hex_decode("e1935a480babfc4403b2517f0ad414bed0ca51fa671e2061804afa78fd71d55c"), logger); cs->advance_with_client_finished( @@ -660,40 +659,41 @@ auto res = rl.next_record(cs.get()); result.require("decrypted a record", std::holds_alternative(res)); auto records = std::get(res); - result.test_eq("first record", records.fragment, plaintext_records.at(0)); + result.test_bin_eq("first record", records.fragment, plaintext_records.at(0)); res = rl.next_record(cs.get()); result.require("decrypted a record", std::holds_alternative(res)); records = std::get(res); - result.test_eq("second record", records.fragment, plaintext_records.at(1)); + result.test_bin_eq("second record", records.fragment, plaintext_records.at(1)); res = rl.next_record(cs.get()); result.require("decrypted a record", std::holds_alternative(res)); records = std::get(res); - result.test_eq("third record", records.fragment, plaintext_records.at(2)); + result.test_bin_eq("third record", records.fragment, plaintext_records.at(2)); - result.confirm("no more records", std::holds_alternative(rl.next_record())); + result.test_is_true("no more records", std::holds_alternative(rl.next_record())); }), - CHECK( - "read coalesced server hello and encrypted extensions", - [&](Test::Result& result) { - // contains the plaintext server hello and the encrypted extensions in one go - auto coalesced = server_hello; - coalesced.insert(coalesced.end(), encrypted_record.cbegin(), encrypted_record.cend()); - - auto client = record_layer_client(true); - client.copy_data(coalesced); - - const auto srv_hello = client.next_record(nullptr); - result.confirm("read a record", std::holds_alternative(srv_hello)); - result.confirm("is handshake record", std::get(srv_hello).type == TLS::Record_Type::Handshake); + CHECK("read coalesced server hello and encrypted extensions", + [&](Test::Result& result) { + // contains the plaintext server hello and the encrypted extensions in one go + auto coalesced = server_hello; + coalesced.insert(coalesced.end(), encrypted_record.cbegin(), encrypted_record.cend()); - auto cs = rfc8448_rtt1_handshake_traffic(); - const auto enc_exts = client.next_record(cs.get()); - result.confirm("read a record", std::holds_alternative(enc_exts)); - result.confirm("is handshake record", std::get(enc_exts).type == TLS::Record_Type::Handshake); - }), + auto client = record_layer_client(true); + client.copy_data(coalesced); + + const auto srv_hello = client.next_record(nullptr); + result.test_is_true("read a record", std::holds_alternative(srv_hello)); + result.test_is_true("is handshake record", + std::get(srv_hello).type == TLS::Record_Type::Handshake); + + auto cs = rfc8448_rtt1_handshake_traffic(); + const auto enc_exts = client.next_record(cs.get()); + result.test_is_true("read a record", std::holds_alternative(enc_exts)); + result.test_is_true("is handshake record", + std::get(enc_exts).type == TLS::Record_Type::Handshake); + }), CHECK("read a padded record", [&](Test::Result& result) { @@ -702,7 +702,7 @@ auto cs = rfc8448_rtt1_handshake_traffic(); const auto record = client.next_record(cs.get()); - result.confirm("read a record with padding", std::holds_alternative(record)); + result.test_is_true("read a record with padding", std::holds_alternative(record)); }), CHECK("read an empty encrypted record", [&](Test::Result& result) { @@ -711,7 +711,7 @@ auto cs = rfc8448_rtt1_handshake_traffic(); const auto record = client.next_record(cs.get()); - result.confirm("read an empty record", std::holds_alternative(record)); + result.test_is_true("read an empty record", std::holds_alternative(record)); })}; } @@ -730,7 +730,7 @@ "17 03 03 00 35 75 ec 4d c2 38 cc e6" "0b 29 80 44 a7 1e 21 9c 56 cc 77 b0 51 7f e9 b9 3c 7a 4b fc 44 d8 7f" "38 f8 03 38 ac 98 fc 46 de b3 84 bd 1c ae ac ab 68 67 d7 26 c4 05 46"); - result.test_eq("produced the expected ciphertext", ct, expected_ct); + result.test_bin_eq("produced the expected ciphertext", ct, expected_ct); }), CHECK("write a dummy CCS (that must not be encrypted)", @@ -740,7 +740,7 @@ Botan::TLS::Record_Type::ChangeCipherSpec, ccs_content, cs.get()); result.require("record was created and not encrypted", record.size() == Botan::TLS::TLS_HEADER_SIZE + 1); - result.test_eq("CCS record is well-formed", record, Botan::hex_decode("140303000101")); + result.test_bin_eq("CCS record is well-formed", record, "140303000101"); }), CHECK("write a lot of data producing two protected records", [&](Test::Result& result) { @@ -750,12 +750,12 @@ ct.size() > big_data.size() + Botan::TLS::TLS_HEADER_SIZE * 2); auto read_record_header = [&](auto& reader) { - result.test_is_eq( + result.test_u8_eq( "APPLICATION_DATA", reader.get_byte(), static_cast(TLS::Record_Type::ApplicationData)); - result.test_is_eq("TLS legacy version", reader.get_uint16_t(), uint16_t(0x0303)); + result.test_u16_eq("TLS legacy version", reader.get_uint16_t(), uint16_t(0x0303)); const auto fragment_length = reader.get_uint16_t(); - result.test_lte("TLS limts", fragment_length, TLS::MAX_CIPHERTEXT_SIZE_TLS13); + result.test_sz_lte("TLS limits", fragment_length, TLS::MAX_CIPHERTEXT_SIZE_TLS13); result.require("enough data", fragment_length + Botan::TLS::TLS_HEADER_SIZE < ct.size()); return fragment_length; }; @@ -767,7 +767,7 @@ const auto fragment_length2 = read_record_header(reader); reader.discard_next(fragment_length2); - result.confirm("consumed all bytes", !reader.has_remaining()); + result.test_is_true("consumed all bytes", !reader.has_remaining()); })}; } @@ -803,12 +803,12 @@ [&](Test::Result& result) { auto rl = record_layer_client(); auto rec = rl.prepare_records(TLS::Record_Type::Handshake, std::vector(5)); - result.confirm("first record has version 0x0301", has_version(rec, 0x0301)); + result.test_is_true("first record has version 0x0301", has_version(rec, 0x0301)); rl.disable_sending_compat_mode(); rec = rl.prepare_records(TLS::Record_Type::Handshake, std::vector(5)); - result.confirm("next record has version 0x0303", has_version(rec, 0x0303)); + result.test_is_true("next record has version 0x0303", has_version(rec, 0x0303)); }), CHECK("client side starts with version 0x0301 (even if multiple reconds are required)", @@ -816,20 +816,20 @@ auto rl = record_layer_client(); auto rec = rl.prepare_records(TLS::Record_Type::Handshake, std::vector(5 * Botan::TLS::MAX_PLAINTEXT_SIZE)); - result.confirm("first record has version 0x0301", has_version(rec, 0x0301)); + result.test_is_true("first record has version 0x0301", has_version(rec, 0x0301)); rl.disable_sending_compat_mode(); rec = rl.prepare_records(TLS::Record_Type::Handshake, std::vector(5 * Botan::TLS::MAX_PLAINTEXT_SIZE)); - result.confirm("next record has version 0x0303", has_version(rec, 0x0303)); + result.test_is_true("next record has version 0x0303", has_version(rec, 0x0303)); }), CHECK("server side starts with version 0x0303", [&](Test::Result& result) { auto rl = record_layer_server(true); auto rec = rl.prepare_records(TLS::Record_Type::Handshake, std::vector(5)); - result.confirm("first record has version 0x0303", has_version(rec, 0x0303)); + result.test_is_true("first record has version 0x0303", has_version(rec, 0x0303)); }), CHECK("server side accepts version 0x0301 for the first record", @@ -908,22 +908,22 @@ auto csc = rfc8448_rtt1_handshake_traffic(Botan::TLS::Connection_Side::Client); auto rlc = record_layer_client(true); - const auto r1 = rlc.prepare_records( + const auto rec1 = rlc.prepare_records( TLS::Record_Type::ApplicationData, std::vector(Botan::TLS::MAX_PLAINTEXT_SIZE), csc.get()); - result.test_eq("one record generated", count_records(r1), 1); + result.test_sz_eq("one record generated", count_records(rec1), 1); - const auto r2 = rlc.prepare_records(TLS::Record_Type::ApplicationData, - std::vector(Botan::TLS::MAX_PLAINTEXT_SIZE + 1), - csc.get()); - result.test_eq("two records generated", count_records(r2), 2); + const auto rec2 = rlc.prepare_records(TLS::Record_Type::ApplicationData, + std::vector(Botan::TLS::MAX_PLAINTEXT_SIZE + 1), + csc.get()); + result.test_sz_eq("two records generated", count_records(rec2), 2); auto css = rfc8448_rtt1_handshake_traffic(Botan::TLS::Connection_Side::Server); auto rls = record_layer_server(true); - rls.copy_data(r1); + rls.copy_data(rec1); - result.test_eq("correct length record", - record_length(result, rls.next_record(css.get())), - Botan::TLS::MAX_PLAINTEXT_SIZE); + result.test_sz_eq("correct length record", + record_length(result, rls.next_record(css.get())), + Botan::TLS::MAX_PLAINTEXT_SIZE); }), CHECK("outgoing record size limit", @@ -933,13 +933,13 @@ rl.set_record_size_limits(127 + 1 /* content type byte */, Botan::TLS::MAX_PLAINTEXT_SIZE + 1); - const auto r1 = + const auto rec1 = rl.prepare_records(TLS::Record_Type::ApplicationData, std::vector(127), cs.get()); - result.test_eq("one record generated", count_records(r1), 1); + result.test_sz_eq("one record generated", count_records(rec1), 1); - const auto r2 = + const auto rec2 = rl.prepare_records(TLS::Record_Type::ApplicationData, std::vector(128), cs.get()); - result.test_eq("two records generated", count_records(r2), 2); + result.test_sz_eq("two records generated", count_records(rec2), 2); }), CHECK( @@ -948,21 +948,21 @@ auto cs = rfc8448_rtt1_handshake_traffic(); auto rl = record_layer_client(true); - const auto r1 = rl.prepare_records( + const auto rec1 = rl.prepare_records( TLS::Record_Type::ApplicationData, std::vector(Botan::TLS::MAX_PLAINTEXT_SIZE), cs.get()); - result.test_eq("one record generated", count_records(r1), 1); + result.test_sz_eq("one record generated", count_records(rec1), 1); - const auto r2 = rl.prepare_records( + const auto rec2 = rl.prepare_records( TLS::Record_Type::ApplicationData, std::vector(Botan::TLS::MAX_PLAINTEXT_SIZE + 1), cs.get()); - result.test_eq("two records generated", count_records(r2), 2); + result.test_sz_eq("two records generated", count_records(rec2), 2); rl.set_record_size_limits(127 + 1 /* content type byte */, Botan::TLS::MAX_PLAINTEXT_SIZE + 1); const auto r3 = rl.prepare_records(TLS::Record_Type::ApplicationData, std::vector(127), cs.get()); - result.test_eq("one record generated", count_records(r3), 1); + result.test_sz_eq("one record generated", count_records(r3), 1); const auto r4 = rl.prepare_records(TLS::Record_Type::ApplicationData, std::vector(128), cs.get()); - result.test_eq("two records generated", count_records(r4), 2); + result.test_sz_eq("two records generated", count_records(r4), 2); }), CHECK("outgoing record limit does not affect unencrypted records", @@ -971,13 +971,13 @@ rl.set_record_size_limits(127 + 1 /* content type byte */, Botan::TLS::MAX_PLAINTEXT_SIZE + 1); - const auto r1 = + const auto rec1 = rl.prepare_records(TLS::Record_Type::Handshake, std::vector(Botan::TLS::MAX_PLAINTEXT_SIZE)); - result.test_eq("one record generated", count_records(r1), 1); + result.test_sz_eq("one record generated", count_records(rec1), 1); - const auto r2 = rl.prepare_records(TLS::Record_Type::Handshake, - std::vector(Botan::TLS::MAX_PLAINTEXT_SIZE + 1)); - result.test_eq("two records generated", count_records(r2), 2); + const auto rec2 = rl.prepare_records(TLS::Record_Type::Handshake, + std::vector(Botan::TLS::MAX_PLAINTEXT_SIZE + 1)); + result.test_sz_eq("two records generated", count_records(rec2), 2); }), CHECK("incoming limit is not checked on unprotected records", @@ -987,7 +987,7 @@ rlc.set_record_size_limits(Botan::TLS::MAX_PLAINTEXT_SIZE + 1, 95 + 1); rlc.copy_data(Botan::concat(Botan::hex_decode("16 03 03 00 80"), std::vector(128))); - result.test_eq("correct length record", record_length(result, rlc.next_record()), 128); + result.test_sz_eq("correct length record", record_length(result, rlc.next_record()), 128); }), CHECK("incoming limit is checked on protected records", @@ -1003,7 +1003,7 @@ "21db0afa05601af25b61df82fb728c772ad860081d96c86008c08d0c21f991cf0d" "4a0eadc840d1ea8fb1f5dd852980d78fcc")); - result.test_eq("correct length record", record_length(result, rls.next_record(css.get())), 127); + result.test_sz_eq("correct length record", record_length(result, rls.next_record(css.get())), 127); rls.copy_data( Botan::hex_decode("1703030091234d4a480092fa6a55f1443345ee8d2250cd9c676370be68f86234db" diff -Nru botan3-3.7.1+dfsg/src/tests/test_tls_rfc8448.cpp botan3-3.12.0+dfsg/src/tests/test_tls_rfc8448.cpp --- botan3-3.7.1+dfsg/src/tests/test_tls_rfc8448.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_tls_rfc8448.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -13,9 +13,9 @@ // Since RFC 8448 uses a specific set of cipher suites we can only run this // test if all of them are enabled. -#if defined(BOTAN_HAS_TLS_13) && defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305) && defined(BOTAN_HAS_AEAD_GCM) && \ - defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_X25519) && defined(BOTAN_HAS_SHA2_32) && defined(BOTAN_HAS_SHA2_64) && \ - defined(BOTAN_HAS_ECDSA) +#if defined(BOTAN_HAS_TLS_12) && defined(BOTAN_HAS_TLS_13) && defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305) && \ + defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_X25519) && defined(BOTAN_HAS_SHA2_32) && \ + defined(BOTAN_HAS_SHA2_64) && defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_PSS) #define BOTAN_CAN_RUN_TEST_TLS_RFC8448 #endif @@ -25,25 +25,32 @@ #include #include #include - #include - #include + #include + #include + #include #include #include - #include - #include - #include + #include + #include + #include + #include #include + #include + #include + #include #include + #include + #include #include #include #endif namespace Botan_Tests { -#if defined(BOTAN_CAN_RUN_TEST_TLS_RFC8448) - namespace { +#if defined(BOTAN_CAN_RUN_TEST_TLS_RFC8448) + void add_entropy(Fixed_Output_RNG& rng, const std::vector& bin) { rng.add_entropy(bin.data(), bin.size()); } @@ -99,7 +106,7 @@ explicit Padding(const size_t padding_bytes) : m_padding_bytes(padding_bytes) {} - std::vector serialize(Botan::TLS::Connection_Side) const override { + std::vector serialize(Botan::TLS::Connection_Side /*whoami*/) const override { return std::vector(m_padding_bytes, 0x00); } @@ -175,8 +182,8 @@ } void tls_session_established(const Botan::TLS::Session_Summary& summary) override { - if(summary.psk_used()) { - negotiated_psk_identity = summary.external_psk_identity().value(); + if(const auto& psk_id = summary.external_psk_identity()) { + negotiated_psk_identity = *psk_id; } count_callback_invocation("tls_session_established"); } @@ -186,25 +193,25 @@ session_activated_called = true; } - bool tls_should_persist_resumption_information(const Session&) override { + bool tls_should_persist_resumption_information(const Session& /*session*/) override { count_callback_invocation("tls_should_persist_resumption_information"); return true; // should always store the session } void tls_verify_cert_chain(const std::vector& cert_chain, - const std::vector>&, - const std::vector&, - Botan::Usage_Type, - std::string_view, - const Botan::TLS::Policy&) override { + const std::vector>& /*ocsp*/, + const std::vector& /*trusted*/, + Botan::Usage_Type /*usage*/, + std::string_view /*hostname*/, + const Botan::TLS::Policy& /*policy*/) override { count_callback_invocation("tls_verify_cert_chain"); certificate_chain = cert_chain; } void tls_verify_raw_public_key(const Public_Key& raw_pk, - Usage_Type, - std::string_view, - const TLS::Policy&) override { + Usage_Type /*usage*/, + std::string_view /*hostname*/, + const TLS::Policy& /*policy*/) override { count_callback_invocation("tls_verify_raw_public_key"); // TODO: is there a better way to copy a generic public key? raw_public_key = Botan::X509::load_key(raw_pk.subject_public_key()); @@ -349,17 +356,17 @@ } public: - bool session_activated_called; // NOLINT(*-non-private-member-variables-in-classes) - std::vector certificate_chain; // NOLINT(*-non-private-member-variables-in-classes) - std::unique_ptr raw_public_key; // NOLINT(*-non-private-member-variables-in-classes) - std::string negotiated_psk_identity; // NOLINT(*-non-private-member-variables-in-classes) + bool session_activated_called; // NOLINT(*-non-private-member-variable*) + std::vector certificate_chain; // NOLINT(*-non-private-member-variable*) + std::unique_ptr raw_public_key; // NOLINT(*-non-private-member-variable*) + std::string negotiated_psk_identity; // NOLINT(*-non-private-member-variable*) std::map>> - serialized_messages; // NOLINT(*-non-private-member-variables-in-classes) + serialized_messages; // NOLINT(*-non-private-member-variable*) private: std::vector send_buffer; std::vector receive_buffer; - uint64_t received_seq_no; + uint64_t received_seq_no = 0; Modify_Exts_Fn m_modify_exts; std::vector m_mock_signatures; std::chrono::system_clock::time_point m_timestamp; @@ -487,7 +494,7 @@ } public: - RFC8448_Text_Policy(const std::string& policy_file, bool rfc8448 = true) : + explicit RFC8448_Text_Policy(const std::string& policy_file, bool rfc8448 = true) : Botan::TLS::Text_Policy(read_policy(policy_file)), m_rfc8448(rfc8448) {} std::vector allowed_signature_schemes() const override { @@ -568,7 +575,9 @@ m_sessions.push_back({session, handle}); } - std::optional establish(const Session& session, const std::optional&, bool) override { + std::optional establish(const Session& session, + const std::optional& /*session*/, + bool /*no_ticket*/) override { // we assume that the 'mocked' session is already stored in the manager, // verify that it is equivalent to the one created by the testee and // return the associated handle stored with it @@ -594,7 +603,8 @@ } } - std::vector find_some(const Server_Information& info, const size_t) override { + std::vector find_some(const Server_Information& info, + const size_t /*max_sessions_hint*/) override { std::vector found_sessions; for(const auto& [session, handle] : m_sessions) { if(session.server_info() == info) { @@ -605,19 +615,7 @@ return found_sessions; } - size_t remove(const Session_Handle& handle) override { - // TODO: C++20 allows to simply implement the entire method like: - // - // return std::erase_if(m_sessions, find_by_handle(handle)); - // - // Unfortunately, at the time of this writing Android NDK shipped with - // a std::erase_if that returns void. - auto rm_itr = std::remove_if(m_sessions.begin(), m_sessions.end(), find_by_handle(handle)); - - const auto elements_being_removed = std::distance(rm_itr, m_sessions.end()); - m_sessions.erase(rm_itr); - return elements_being_removed; - } + size_t remove(const Session_Handle& handle) override { return std::erase_if(m_sessions, find_by_handle(handle)); } size_t remove_all() override { const auto sessions = m_sessions.size(); @@ -653,7 +651,7 @@ m_policy(std::move(policy)) { if(session_and_ticket.has_value()) { m_session_mgr->store(std::get(session_and_ticket.value()), - std::get(session_and_ticket.value())); + Botan::TLS::Session_Handle(std::get(session_and_ticket.value()))); } } @@ -682,15 +680,15 @@ const std::vector& callback_names) { const auto& invokes = m_callbacks->callback_invocations(); for(const auto& cbn : callback_names) { - result.confirm(Botan::fmt("{} was invoked (Context: {})", cbn, context), - invokes.contains(cbn) && invokes.at(cbn) > 0); + result.test_is_true(Botan::fmt("{} was invoked (Context: {})", cbn, context), + invokes.contains(cbn) && invokes.at(cbn) > 0); } for(const auto& invoke : invokes) { if(invoke.second == 0) { continue; } - result.confirm( + result.test_is_true( invoke.first + " was expected (Context: " + context + ")", std::find(callback_names.cbegin(), callback_names.cend(), invoke.first) != callback_names.cend()); } @@ -712,12 +710,12 @@ virtual void send(const std::vector& data) = 0; protected: - std::shared_ptr m_callbacks; // NOLINT(*-non-private-member-variables-in-classes) - std::shared_ptr m_creds; // NOLINT(*-non-private-member-variables-in-classes) + std::shared_ptr m_callbacks; // NOLINT(*-non-private-member-variable*) + std::shared_ptr m_creds; // NOLINT(*-non-private-member-variable*) - std::shared_ptr m_rng; // NOLINT(*-non-private-member-variables-in-classes) - std::shared_ptr m_session_mgr; // NOLINT(*-non-private-member-variables-in-classes) - std::shared_ptr m_policy; // NOLINT(*-non-private-member-variables-in-classes) + std::shared_ptr m_rng; // NOLINT(*-non-private-member-variable*) + std::shared_ptr m_session_mgr; // NOLINT(*-non-private-member-variable*) + std::shared_ptr m_policy; // NOLINT(*-non-private-member-variable*) }; class Client_Context : public TLS_Context { @@ -747,7 +745,7 @@ void send(const std::vector& data) override { client.send(data.data(), data.size()); } - Botan::TLS::Client client; // NOLINT(*-non-private-member-variables-in-classes) + Botan::TLS::Client client; // NOLINT(*-non-private-member-variable*) }; class Server_Context : public TLS_Context { @@ -772,65 +770,54 @@ void send(const std::vector& data) override { server.send(data.data(), data.size()); } - Botan::TLS::Server server; // NOLINT(*-non-private-member-variables-in-classes) + Botan::TLS::Server server; // NOLINT(*-non-private-member-variable*) }; -void sort_extensions(Botan::TLS::Extensions& exts, const std::vector& expected_order) { - for(const auto ext_type : expected_order) { - auto ext = exts.take(ext_type); - if(ext != nullptr) { - exts.add(std::move(ext)); - } - } -} - /** * Because of the nature of the RFC 8448 test data we need to produce bit-compatible * TLS messages. Hence we sort the generated TLS extensions exactly as expected. */ void sort_rfc8448_extensions(Botan::TLS::Extensions& exts, Botan::TLS::Connection_Side side, - Botan::TLS::Handshake_Type = Botan::TLS::Handshake_Type::ClientHello) { + Botan::TLS::Handshake_Type /*type*/ = Botan::TLS::Handshake_Type::ClientHello) { if(side == Botan::TLS::Connection_Side::Client) { - sort_extensions(exts, - { - Botan::TLS::Extension_Code::ServerNameIndication, - Botan::TLS::Extension_Code::SafeRenegotiation, - Botan::TLS::Extension_Code::SupportedGroups, - Botan::TLS::Extension_Code::SessionTicket, - Botan::TLS::Extension_Code::KeyShare, - Botan::TLS::Extension_Code::EarlyData, - Botan::TLS::Extension_Code::SupportedVersions, - Botan::TLS::Extension_Code::SignatureAlgorithms, - Botan::TLS::Extension_Code::Cookie, - Botan::TLS::Extension_Code::PskKeyExchangeModes, - Botan::TLS::Extension_Code::RecordSizeLimit, - Padding::static_type(), - Botan::TLS::Extension_Code::PresharedKey, - }); + exts.reorder({ + Botan::TLS::Extension_Code::ServerNameIndication, + Botan::TLS::Extension_Code::SafeRenegotiation, + Botan::TLS::Extension_Code::SupportedGroups, + Botan::TLS::Extension_Code::SessionTicket, + Botan::TLS::Extension_Code::KeyShare, + Botan::TLS::Extension_Code::EarlyData, + Botan::TLS::Extension_Code::SupportedVersions, + Botan::TLS::Extension_Code::SignatureAlgorithms, + Botan::TLS::Extension_Code::Cookie, + Botan::TLS::Extension_Code::PskKeyExchangeModes, + Botan::TLS::Extension_Code::RecordSizeLimit, + Padding::static_type(), + Botan::TLS::Extension_Code::PresharedKey, + }); } else { - sort_extensions(exts, - { - Botan::TLS::Extension_Code::SupportedGroups, - Botan::TLS::Extension_Code::KeyShare, - Botan::TLS::Extension_Code::Cookie, - Botan::TLS::Extension_Code::SupportedVersions, - Botan::TLS::Extension_Code::SignatureAlgorithms, - Botan::TLS::Extension_Code::RecordSizeLimit, - Botan::TLS::Extension_Code::ServerNameIndication, - Botan::TLS::Extension_Code::EarlyData, - }); + exts.reorder({ + Botan::TLS::Extension_Code::SupportedGroups, + Botan::TLS::Extension_Code::KeyShare, + Botan::TLS::Extension_Code::Cookie, + Botan::TLS::Extension_Code::SupportedVersions, + Botan::TLS::Extension_Code::SignatureAlgorithms, + Botan::TLS::Extension_Code::RecordSizeLimit, + Botan::TLS::Extension_Code::ServerNameIndication, + Botan::TLS::Extension_Code::EarlyData, + }); } } void add_renegotiation_extension(Botan::TLS::Extensions& exts) { // Renegotiation is not possible in TLS 1.3. Nevertheless, RFC 8448 requires // to add this to the Client Hello for reasons. - exts.add(new Renegotiation_Extension()); + exts.add(new Renegotiation_Extension()); // NOLINT(*-owning-memory) } void add_early_data_indication(Botan::TLS::Extensions& exts) { - exts.add(new Botan::TLS::EarlyDataIndication()); + exts.add(new Botan::TLS::EarlyDataIndication()); // NOLINT(*-owning-memory) } std::vector strip_message_header(const std::vector& msg) { @@ -853,8 +840,6 @@ return result; } -} // namespace - /** * Traffic transcripts and supporting data for the TLS RFC 8448 and TLS policy * configuration is kept in data files (accessible via `Test:::data_file()`). @@ -988,7 +973,7 @@ // For some reason, presumably checking compatibility, the RFC 8448 Client // Hello includes a (TLS 1.2) Session_Ticket extension. We don't normally add // this obsoleted extension in a TLS 1.3 client. - exts.add(new Botan::TLS::Session_Ticket_Extension()); + exts.add(new Botan::TLS::Session_Ticket_Extension()); // NOLINT(*-owning-memory) add_renegotiation_extension(exts); sort_rfc8448_extensions(exts, side); @@ -1005,7 +990,7 @@ vars.get_req_u64("CurrentTimestamp"), add_extensions_and_sort); - result.confirm("client not closed", !ctx->client.is_closed()); + result.test_is_true("client not closed", !ctx->client.is_closed()); ctx->check_callback_invocations(result, "client hello prepared", { @@ -1016,7 +1001,7 @@ "tls_current_timestamp", }); - result.test_eq( + result.test_bin_eq( "TLS client hello", ctx->pull_send_buffer(), vars.get_req_bin("Record_ClientHello_1")); }), @@ -1038,8 +1023,8 @@ "tls_examine_extensions_server_hello", "tls_ephemeral_key_agreement"}); - result.confirm("client is not yet active", !ctx->client.is_active()); - result.confirm("handshake is not yet complete", !ctx->client.is_handshake_complete()); + result.test_is_true("client is not yet active", !ctx->client.is_active()); + result.test_is_true("handshake is not yet complete", !ctx->client.is_handshake_complete()); }), CHECK("Server HS messages .. Client Finished", @@ -1064,11 +1049,11 @@ result.require("certificate exists", !ctx->certs_verified().empty()); result.require("correct certificate", ctx->certs_verified().front() == server_certificate()); result.require("client is active", ctx->client.is_active()); - result.confirm("handshake is complete", ctx->client.is_handshake_complete()); + result.test_is_true("handshake is complete", ctx->client.is_handshake_complete()); - result.test_eq("correct handshake finished", - ctx->pull_send_buffer(), - vars.get_req_bin("Record_ClientFinished")); + result.test_bin_eq("correct handshake finished", + ctx->pull_send_buffer(), + vars.get_req_bin("Record_ClientFinished")); }), CHECK("Post-Handshake: NewSessionTicket", @@ -1082,12 +1067,12 @@ {"tls_examine_extensions_new_session_ticket", "tls_should_persist_resumption_information", "tls_current_timestamp"}); - if(result.test_eq("session was stored", ctx->stored_sessions().size(), 1)) { + if(result.test_sz_eq("session was stored", ctx->stored_sessions().size(), 1)) { const auto& [stored_session, stored_handle] = ctx->stored_sessions().front(); result.require("session handle contains a ticket", stored_handle.ticket().has_value()); - result.test_is_eq("session was serialized as expected", - Botan::unlock(stored_session.DER_encode()), - vars.get_req_bin("Client_SessionData")); + result.test_bin_eq("session was serialized as expected", + stored_session.DER_encode(), + vars.get_req_bin("Client_SessionData")); } }), @@ -1098,9 +1083,9 @@ ctx->check_callback_invocations(result, "application data sent", {"tls_emit_data"}); - result.test_eq("correct client application data", - ctx->pull_send_buffer(), - vars.get_req_bin("Record_Client_AppData")); + result.test_bin_eq("correct client application data", + ctx->pull_send_buffer(), + vars.get_req_bin("Record_Client_AppData")); }), CHECK("Receive Application Data", @@ -1111,8 +1096,8 @@ ctx->check_callback_invocations(result, "application data sent", {"tls_record_received"}); const auto rcvd = ctx->pull_receive_buffer(); - result.test_eq("decrypted application traffic", rcvd, vars.get_req_bin("Server_AppData")); - result.test_is_eq("sequence number", ctx->last_received_seq_no(), uint64_t(1)); + result.test_bin_eq("decrypted application traffic", rcvd, vars.get_req_bin("Server_AppData")); + result.test_u64_eq("sequence number", ctx->last_received_seq_no(), uint64_t(1)); }), CHECK("Close Connection", @@ -1120,7 +1105,7 @@ result.require("ctx is available", ctx != nullptr); ctx->client.close(); - result.test_eq( + result.test_bin_eq( "close payload", ctx->pull_send_buffer(), vars.get_req_bin("Record_Client_CloseNotify")); ctx->check_callback_invocations(result, "CLOSE_NOTIFY sent", {"tls_emit_data"}); @@ -1128,7 +1113,7 @@ ctx->check_callback_invocations( result, "CLOSE_NOTIFY received", {"tls_alert", "tls_peer_closed_connection"}); - result.confirm("connection is closed", ctx->client.is_closed()); + result.test_is_true("connection is closed", ctx->client.is_closed()); }), }; } @@ -1144,7 +1129,7 @@ Botan::TLS::Connection_Side side, Botan::TLS::Handshake_Type which_message) { if(which_message == Handshake_Type::ClientHello) { - exts.add(new Padding(87)); + exts.add(new Padding(87)); // NOLINT(*-owning-memory) add_renegotiation_extension(exts); @@ -1170,7 +1155,7 @@ std::pair{Botan::TLS::Session(vars.get_req_bin("Client_SessionData")), Botan::TLS::Session_Ticket(vars.get_req_bin("SessionTicket"))}); - result.confirm("client not closed", !ctx->client.is_closed()); + result.test_is_true("client not closed", !ctx->client.is_closed()); ctx->check_callback_invocations(result, "client hello prepared", { @@ -1181,7 +1166,7 @@ "tls_generate_ephemeral_key", }); - result.test_eq( + result.test_bin_eq( "TLS client hello", ctx->pull_send_buffer(), vars.get_req_bin("Record_ClientHello_1")); }) @@ -1205,7 +1190,7 @@ // For some reason RFC8448 decided to require this (fairly obscure) extension // in the second flight of the Client_Hello. if(flights == 2) { - exts.add(new Padding(175)); + exts.add(new Padding(175)); // NOLINT(*-owning-memory) } sort_rfc8448_extensions(exts, side); @@ -1230,7 +1215,7 @@ std::make_shared("rfc8448_hrr_client"), vars.get_req_u64("CurrentTimestamp"), add_extensions_and_sort); - result.confirm("client not closed", !ctx->client.is_closed()); + result.test_is_true("client not closed", !ctx->client.is_closed()); ctx->check_callback_invocations(result, "client hello prepared", @@ -1242,7 +1227,7 @@ "tls_current_timestamp", }); - result.test_eq( + result.test_bin_eq( "TLS client hello (1)", ctx->pull_send_buffer(), vars.get_req_bin("Record_ClientHello_1")); }), @@ -1262,7 +1247,7 @@ "tls_generate_ephemeral_key", }); - result.test_eq( + result.test_bin_eq( "TLS client hello (2)", ctx->pull_send_buffer(), vars.get_req_bin("Record_ClientHello_2")); }), @@ -1300,7 +1285,7 @@ "tls_verify_cert_chain", "tls_verify_message"}); - result.test_eq( + result.test_bin_eq( "client finished", ctx->pull_send_buffer(), vars.get_req_bin("Record_ClientFinished")); }), @@ -1310,14 +1295,14 @@ ctx->client.close(); ctx->check_callback_invocations( result, "encrypted handshake messages received", {"tls_emit_data"}); - result.test_eq( + result.test_bin_eq( "client close notify", ctx->pull_send_buffer(), vars.get_req_bin("Record_Client_CloseNotify")); ctx->client.received_data(vars.get_req_bin("Record_Server_CloseNotify")); ctx->check_callback_invocations( result, "encrypted handshake messages received", {"tls_alert", "tls_peer_closed_connection"}); - result.confirm("connection is closed", ctx->client.is_closed()); + result.test_is_true("connection is closed", ctx->client.is_closed()); }), }; } @@ -1361,7 +1346,8 @@ "tls_current_timestamp", }); - result.test_eq("Client Hello", ctx->pull_send_buffer(), vars.get_req_bin("Record_ClientHello_1")); + result.test_bin_eq( + "Client Hello", ctx->pull_send_buffer(), vars.get_req_bin("Record_ClientHello_1")); }), CHECK("Server Hello", @@ -1406,7 +1392,7 @@ // ClientFinished contains the entire coalesced client authentication flight // Messages: Certificate, CertificateVerify, Finished - result.test_eq( + result.test_bin_eq( "Client Auth and Finished", ctx->pull_send_buffer(), vars.get_req_bin("Record_ClientFinished")); }), @@ -1414,7 +1400,7 @@ [&](Test::Result& result) { result.require("ctx is available", ctx != nullptr); ctx->client.close(); - result.test_eq( + result.test_bin_eq( "Client close_notify", ctx->pull_send_buffer(), vars.get_req_bin("Record_Client_CloseNotify")); ctx->check_callback_invocations(result, @@ -1424,7 +1410,7 @@ }); ctx->client.received_data(vars.get_req_bin("Record_Server_CloseNotify")); - result.confirm("connection closed", ctx->client.is_closed()); + result.test_is_true("connection closed", ctx->client.is_closed()); ctx->check_callback_invocations( result, "after receiving close notify", {"tls_alert", "tls_peer_closed_connection"}); @@ -1452,26 +1438,26 @@ std::unique_ptr ctx; return { - CHECK("Client Hello", - [&](Test::Result& result) { - ctx = - std::make_unique(std::move(rng), + CHECK( + "Client Hello", + [&](Test::Result& result) { + ctx = std::make_unique(std::move(rng), std::make_shared("rfc8448_compat_client"), vars.get_req_u64("CurrentTimestamp"), add_extensions_and_sort); - result.test_eq("Client Hello", ctx->pull_send_buffer(), vars.get_req_bin("Record_ClientHello_1")); + result.test_bin_eq("Client Hello", ctx->pull_send_buffer(), vars.get_req_bin("Record_ClientHello_1")); - ctx->check_callback_invocations(result, - "client hello prepared", - { - "tls_emit_data", - "tls_inspect_handshake_msg_client_hello", - "tls_modify_extensions_client_hello", - "tls_generate_ephemeral_key", - "tls_current_timestamp", - }); - }), + ctx->check_callback_invocations(result, + "client hello prepared", + { + "tls_emit_data", + "tls_inspect_handshake_msg_client_hello", + "tls_modify_extensions_client_hello", + "tls_generate_ephemeral_key", + "tls_current_timestamp", + }); + }), CHECK("Server Hello + other handshake messages", [&](Test::Result& result) { @@ -1501,13 +1487,13 @@ "tls_ephemeral_key_agreement", }); - result.test_eq("CCS + Client Finished", - ctx->pull_send_buffer(), - // ClientFinished contains the expected ChangeCipherSpec record - vars.get_req_bin("Record_ClientFinished")); + result.test_bin_eq("CCS + Client Finished", + ctx->pull_send_buffer(), + // ClientFinished contains the expected ChangeCipherSpec record + vars.get_req_bin("Record_ClientFinished")); - result.confirm("client is ready to send application traffic", ctx->client.is_active()); - result.confirm("handshake is complete", ctx->client.is_handshake_complete()); + result.test_is_true("client is ready to send application traffic", ctx->client.is_active()); + result.test_is_true("handshake is complete", ctx->client.is_handshake_complete()); }), CHECK("Close connection", @@ -1515,16 +1501,16 @@ result.require("ctx is available", ctx != nullptr); ctx->client.close(); - result.test_eq( + result.test_bin_eq( "Client close_notify", ctx->pull_send_buffer(), vars.get_req_bin("Record_Client_CloseNotify")); result.require("client cannot send application traffic anymore", !ctx->client.is_active()); result.require("client is not fully closed yet", !ctx->client.is_closed()); - result.confirm("handshake stays completed", ctx->client.is_handshake_complete()); + result.test_is_true("handshake stays completed", ctx->client.is_handshake_complete()); ctx->client.received_data(vars.get_req_bin("Record_Server_CloseNotify")); - result.confirm("client connection was terminated", ctx->client.is_closed()); + result.test_is_true("client connection was terminated", ctx->client.is_closed()); }), }; } @@ -1542,17 +1528,16 @@ // This is the order of extensions when we first introduced the PSK // implementation and generated the transcript. To stay compatible // with the now hard-coded transcript, we pin the extension order. - sort_extensions(exts, - { - Botan::TLS::Extension_Code::ServerNameIndication, - Botan::TLS::Extension_Code::SupportedGroups, - Botan::TLS::Extension_Code::KeyShare, - Botan::TLS::Extension_Code::SupportedVersions, - Botan::TLS::Extension_Code::SignatureAlgorithms, - Botan::TLS::Extension_Code::PskKeyExchangeModes, - Botan::TLS::Extension_Code::RecordSizeLimit, - Botan::TLS::Extension_Code::PresharedKey, - }); + exts.reorder({ + Botan::TLS::Extension_Code::ServerNameIndication, + Botan::TLS::Extension_Code::SupportedGroups, + Botan::TLS::Extension_Code::KeyShare, + Botan::TLS::Extension_Code::SupportedVersions, + Botan::TLS::Extension_Code::SignatureAlgorithms, + Botan::TLS::Extension_Code::PskKeyExchangeModes, + Botan::TLS::Extension_Code::RecordSizeLimit, + Botan::TLS::Extension_Code::PresharedKey, + }); }; std::unique_ptr ctx; @@ -1570,7 +1555,7 @@ vars.get_req_str("PskPRF"), lock(vars.get_req_bin("PskSecret")))); - result.confirm("client not closed", !ctx->client.is_closed()); + result.test_is_true("client not closed", !ctx->client.is_closed()); ctx->check_callback_invocations(result, "client hello prepared", { @@ -1581,7 +1566,7 @@ "tls_generate_ephemeral_key", }); - result.test_eq( + result.test_bin_eq( "TLS client hello", ctx->pull_send_buffer(), vars.get_req_bin("Record_ClientHello_1")); }), @@ -1596,8 +1581,8 @@ "tls_examine_extensions_server_hello", "tls_ephemeral_key_agreement"}); - result.confirm("client is not yet active", !ctx->client.is_active()); - result.confirm("handshake is not yet complete", !ctx->client.is_handshake_complete()); + result.test_is_true("client is not yet active", !ctx->client.is_active()); + result.test_is_true("handshake is not yet complete", !ctx->client.is_handshake_complete()); }), CHECK( @@ -1617,9 +1602,9 @@ "tls_session_activated"}); result.require("PSK negotiated", ctx->psk_identity_negotiated() == vars.get_req_str("PskIdentity")); result.require("client is active", ctx->client.is_active()); - result.confirm("handshake is complete", ctx->client.is_handshake_complete()); + result.test_is_true("handshake is complete", ctx->client.is_handshake_complete()); - result.test_eq( + result.test_bin_eq( "correct handshake finished", ctx->pull_send_buffer(), vars.get_req_bin("Record_ClientFinished")); }), @@ -1630,9 +1615,9 @@ ctx->check_callback_invocations(result, "application data sent", {"tls_emit_data"}); - result.test_eq("correct client application data", - ctx->pull_send_buffer(), - vars.get_req_bin("Record_Client_AppData")); + result.test_bin_eq("correct client application data", + ctx->pull_send_buffer(), + vars.get_req_bin("Record_Client_AppData")); }), CHECK("Receive Application Data", @@ -1643,8 +1628,8 @@ ctx->check_callback_invocations(result, "application data sent", {"tls_record_received"}); const auto rcvd = ctx->pull_receive_buffer(); - result.test_eq("decrypted application traffic", rcvd, vars.get_req_bin("Server_AppData")); - result.test_is_eq("sequence number", ctx->last_received_seq_no(), uint64_t(0)); + result.test_bin_eq("decrypted application traffic", rcvd, vars.get_req_bin("Server_AppData")); + result.test_u64_eq("sequence number", ctx->last_received_seq_no(), uint64_t(0)); }), CHECK("Close Connection", @@ -1652,7 +1637,7 @@ result.require("ctx is available", ctx != nullptr); ctx->client.close(); - result.test_eq( + result.test_bin_eq( "close payload", ctx->pull_send_buffer(), vars.get_req_bin("Record_Client_CloseNotify")); ctx->check_callback_invocations(result, "CLOSE_NOTIFY sent", {"tls_emit_data"}); @@ -1660,7 +1645,7 @@ ctx->check_callback_invocations( result, "CLOSE_NOTIFY received", {"tls_alert", "tls_peer_closed_connection"}); - result.confirm("connection is closed", ctx->client.is_closed()); + result.test_is_true("connection is closed", ctx->client.is_closed()); }), }; } @@ -1679,18 +1664,17 @@ // public key authentication implementation and generated the transcript. // To stay compatible with the now hard-coded transcript, we pin the // extension order. - sort_extensions(exts, - { - Botan::TLS::Extension_Code::ServerNameIndication, - Botan::TLS::Extension_Code::SupportedGroups, - Botan::TLS::Extension_Code::KeyShare, - Botan::TLS::Extension_Code::SupportedVersions, - Botan::TLS::Extension_Code::SignatureAlgorithms, - Botan::TLS::Extension_Code::PskKeyExchangeModes, - Botan::TLS::Extension_Code::RecordSizeLimit, - Botan::TLS::Extension_Code::ClientCertificateType, - Botan::TLS::Extension_Code::ServerCertificateType, - }); + exts.reorder({ + Botan::TLS::Extension_Code::ServerNameIndication, + Botan::TLS::Extension_Code::SupportedGroups, + Botan::TLS::Extension_Code::KeyShare, + Botan::TLS::Extension_Code::SupportedVersions, + Botan::TLS::Extension_Code::SignatureAlgorithms, + Botan::TLS::Extension_Code::PskKeyExchangeModes, + Botan::TLS::Extension_Code::RecordSizeLimit, + Botan::TLS::Extension_Code::ClientCertificateType, + Botan::TLS::Extension_Code::ServerCertificateType, + }); }; std::unique_ptr ctx; @@ -1716,7 +1700,8 @@ "tls_current_timestamp", }); - result.test_eq("Client Hello", ctx->pull_send_buffer(), vars.get_req_bin("Record_ClientHello_1")); + result.test_bin_eq( + "Client Hello", ctx->pull_send_buffer(), vars.get_req_bin("Record_ClientHello_1")); }), CHECK("Server Hello", @@ -1760,13 +1745,13 @@ }); const auto raw_pk = ctx->client.peer_raw_public_key(); - result.confirm( + result.test_is_true( "Received server's raw public key", raw_pk && raw_pk->fingerprint_public() == server_raw_public_key_pair()->fingerprint_public()); // ClientFinished contains the entire coalesced client authentication flight // Messages: Certificate, CertificateVerify, Finished - result.test_eq( + result.test_bin_eq( "Client Auth and Finished", ctx->pull_send_buffer(), vars.get_req_bin("Record_ClientFinished")); }), @@ -1774,7 +1759,7 @@ [&](Test::Result& result) { result.require("ctx is available", ctx != nullptr); ctx->client.close(); - result.test_eq( + result.test_bin_eq( "Client close_notify", ctx->pull_send_buffer(), vars.get_req_bin("Record_Client_CloseNotify")); ctx->check_callback_invocations(result, @@ -1784,7 +1769,7 @@ }); ctx->client.received_data(vars.get_req_bin("Record_Server_CloseNotify")); - result.confirm("connection closed", ctx->client.is_closed()); + result.test_is_true("connection closed", ctx->client.is_closed()); ctx->check_callback_invocations( result, "after receiving close notify", {"tls_alert", "tls_peer_closed_connection"}); @@ -1814,7 +1799,7 @@ Botan::TLS::Connection_Side side, Botan::TLS::Handshake_Type type) { if(type == Handshake_Type::NewSessionTicket) { - exts.add(new EarlyDataIndication(1024)); + exts.add(new EarlyDataIndication(1024)); // NOLINT(*-owning-memory) } sort_rfc8448_extensions(exts, side, type); }; @@ -1828,7 +1813,7 @@ false, std::pair{Botan::TLS::Session(vars.get_req_bin("Client_SessionData")), Botan::TLS::Session_Ticket(vars.get_req_bin("SessionTicket"))}); - result.confirm("server not closed", !ctx->server.is_closed()); + result.test_is_true("server not closed", !ctx->server.is_closed()); ctx->server.received_data(vars.get_req_bin("Record_ClientHello_1")); @@ -1855,31 +1840,31 @@ result.require("ctx is available", ctx != nullptr); const auto& msgs = ctx->observed_handshake_messages(); - result.test_eq("Server Hello", - msgs.at("server_hello")[0], - strip_message_header(vars.get_opt_bin("Message_ServerHello"))); - result.test_eq("Encrypted Extensions", - msgs.at("encrypted_extensions")[0], - strip_message_header(vars.get_opt_bin("Message_EncryptedExtensions"))); - result.test_eq("Certificate", - msgs.at("certificate")[0], - strip_message_header(vars.get_opt_bin("Message_Server_Certificate"))); - result.test_eq("CertificateVerify", - msgs.at("certificate_verify")[0], - strip_message_header(vars.get_opt_bin("Message_Server_CertificateVerify"))); - - result.test_eq("Server's entire first flight", - ctx->pull_send_buffer(), - concat(vars.get_req_bin("Record_ServerHello"), - vars.get_req_bin("Record_ServerHandshakeMessages"))); + result.test_bin_eq("Server Hello", + msgs.at("server_hello")[0], + strip_message_header(vars.get_opt_bin("Message_ServerHello"))); + result.test_bin_eq("Encrypted Extensions", + msgs.at("encrypted_extensions")[0], + strip_message_header(vars.get_opt_bin("Message_EncryptedExtensions"))); + result.test_bin_eq("Certificate", + msgs.at("certificate")[0], + strip_message_header(vars.get_opt_bin("Message_Server_Certificate"))); + result.test_bin_eq("CertificateVerify", + msgs.at("certificate_verify")[0], + strip_message_header(vars.get_opt_bin("Message_Server_CertificateVerify"))); + + result.test_bin_eq("Server's entire first flight", + ctx->pull_send_buffer(), + concat(vars.get_req_bin("Record_ServerHello"), + vars.get_req_bin("Record_ServerHandshakeMessages"))); // Note: is_active() defines that we can send application data. // RFC 8446 Section 4.4.4 explicitly allows that for servers // that did not receive the client's Finished message, yet. // However, before receiving and validating this message, // the handshake is not yet finished. - result.confirm("Server can now send application data", ctx->server.is_active()); - result.confirm("handshake is not yet complete", !ctx->server.is_handshake_complete()); + result.test_is_true("Server can now send application data", ctx->server.is_active()); + result.test_is_true("handshake is not yet complete", !ctx->server.is_handshake_complete()); }), CHECK("Send Client Finished", @@ -1900,7 +1885,7 @@ result.require("ctx is available", ctx != nullptr); const auto new_tickets = ctx->server.send_new_session_tickets(1); - result.test_eq("session ticket was sent", new_tickets, 1); + result.test_sz_eq("session ticket was sent", new_tickets, 1); ctx->check_callback_invocations(result, "issued new session ticket", @@ -1914,7 +1899,7 @@ CHECK("Verify generated new session ticket message", [&](Test::Result& result) { result.require("ctx is available", ctx != nullptr); - result.test_eq( + result.test_bin_eq( "New Session Ticket", ctx->pull_send_buffer(), vars.get_req_bin("Record_NewSessionTicket")); }), @@ -1925,8 +1910,8 @@ ctx->check_callback_invocations(result, "application data received", {"tls_record_received"}); const auto rcvd = ctx->pull_receive_buffer(); - result.test_eq("decrypted application traffic", rcvd, vars.get_req_bin("Client_AppData")); - result.test_is_eq("sequence number", ctx->last_received_seq_no(), uint64_t(0)); + result.test_bin_eq("decrypted application traffic", rcvd, vars.get_req_bin("Client_AppData")); + result.test_u64_eq("sequence number", ctx->last_received_seq_no(), uint64_t(0)); }), CHECK("Send Application Data", @@ -1936,9 +1921,9 @@ ctx->check_callback_invocations(result, "application data sent", {"tls_emit_data"}); - result.test_eq("correct server application data", - ctx->pull_send_buffer(), - vars.get_req_bin("Record_Server_AppData")); + result.test_bin_eq("correct server application data", + ctx->pull_send_buffer(), + vars.get_req_bin("Record_Server_AppData")); }), CHECK("Receive Client's close_notify", @@ -1949,9 +1934,9 @@ ctx->check_callback_invocations( result, "client finished received", {"tls_alert", "tls_peer_closed_connection"}); - result.confirm("connection is not yet closed", !ctx->server.is_closed()); - result.confirm("connection is still active", ctx->server.is_active()); - result.confirm("handshake is still finished", ctx->server.is_handshake_complete()); + result.test_is_true("connection is not yet closed", !ctx->server.is_closed()); + result.test_is_true("connection is still active", ctx->server.is_active()); + result.test_is_true("handshake is still finished", ctx->server.is_handshake_complete()); }), CHECK("Expect Server close_notify", @@ -1959,12 +1944,12 @@ result.require("ctx is available", ctx != nullptr); ctx->server.close(); - result.confirm("connection is now inactive", !ctx->server.is_active()); - result.confirm("connection is now closed", ctx->server.is_closed()); - result.confirm("handshake is still finished", ctx->server.is_handshake_complete()); - result.test_eq("Server's close notify", - ctx->pull_send_buffer(), - vars.get_req_bin("Record_Server_CloseNotify")); + result.test_is_true("connection is now inactive", !ctx->server.is_active()); + result.test_is_true("connection is now closed", ctx->server.is_closed()); + result.test_is_true("handshake is still finished", ctx->server.is_handshake_complete()); + result.test_bin_eq("Server's close notify", + ctx->pull_send_buffer(), + vars.get_req_bin("Record_Server_CloseNotify")); }), }; } @@ -1985,7 +1970,7 @@ Botan::TLS::Connection_Side side, Botan::TLS::Handshake_Type type) { if(type == Handshake_Type::EncryptedExtensions) { - exts.add(new EarlyDataIndication()); + exts.add(new EarlyDataIndication()); // NOLINT(*-owning-memory) } sort_rfc8448_extensions(exts, side, type); }; @@ -1999,7 +1984,7 @@ false, std::pair{Botan::TLS::Session(vars.get_req_bin("Client_SessionData")), Botan::TLS::Session_Ticket(vars.get_req_bin("SessionTicket"))}); - result.confirm("server not closed", !ctx->server.is_closed()); + result.test_is_true("server not closed", !ctx->server.is_closed()); ctx->server.received_data(vars.get_req_bin("Record_ClientHello_1")); @@ -2025,25 +2010,25 @@ result.require("ctx is available", ctx != nullptr); const auto& msgs = ctx->observed_handshake_messages(); - result.test_eq("Server Hello", - msgs.at("server_hello")[0], - strip_message_header(vars.get_opt_bin("Message_ServerHello"))); - result.test_eq("Encrypted Extensions", - msgs.at("encrypted_extensions")[0], - strip_message_header(vars.get_opt_bin("Message_EncryptedExtensions"))); - - result.test_eq("Server's entire first flight", - ctx->pull_send_buffer(), - concat(vars.get_req_bin("Record_ServerHello"), - vars.get_req_bin("Record_ServerHandshakeMessages"))); + result.test_bin_eq("Server Hello", + msgs.at("server_hello")[0], + strip_message_header(vars.get_opt_bin("Message_ServerHello"))); + result.test_bin_eq("Encrypted Extensions", + msgs.at("encrypted_extensions")[0], + strip_message_header(vars.get_opt_bin("Message_EncryptedExtensions"))); + + result.test_bin_eq("Server's entire first flight", + ctx->pull_send_buffer(), + concat(vars.get_req_bin("Record_ServerHello"), + vars.get_req_bin("Record_ServerHandshakeMessages"))); // Note: is_active() defines that we can send application data. // RFC 8446 Section 4.4.4 explicitly allows that for servers // that did not receive the client's Finished message, yet. // However, before receiving and validating this message, // the handshake is not yet finished. - result.confirm("Server can now send application data", ctx->server.is_active()); - result.confirm("handshake is not yet complete", !ctx->server.is_handshake_complete()); + result.test_is_true("Server can now send application data", ctx->server.is_active()); + result.test_is_true("handshake is not yet complete", !ctx->server.is_handshake_complete()); }), // TODO: The rest of this test vector requires 0-RTT which is not @@ -2071,7 +2056,8 @@ Botan::TLS::Handshake_Type type) { if(type == Handshake_Type::HelloRetryRequest) { // This cookie needs to be mocked into the HRR since RFC 8448 contains it. - exts.add(new Cookie(vars.get_opt_bin("HelloRetryRequest_Cookie"))); + exts.add( + new Cookie(vars.get_opt_bin("HelloRetryRequest_Cookie"))); // NOLINT(*-owning-memory) } sort_rfc8448_extensions(exts, side, type); }; @@ -2081,7 +2067,7 @@ vars.get_req_u64("CurrentTimestamp"), add_cookie_and_sort, make_mock_signatures(vars)); - result.confirm("server not closed", !ctx->server.is_closed()); + result.test_is_true("server not closed", !ctx->server.is_closed()); ctx->server.received_data(vars.get_req_bin("Record_ClientHello_1")); @@ -2097,11 +2083,11 @@ CHECK("Verify generated Hello Retry Request message", [&](Test::Result& result) { result.require("ctx is available", ctx != nullptr); - result.test_eq("Server's Hello Retry Request record", - ctx->pull_send_buffer(), - vars.get_req_bin("Record_HelloRetryRequest")); - result.confirm("TLS handshake not yet finished", !ctx->server.is_active()); - result.confirm("handshake is not yet complete", !ctx->server.is_handshake_complete()); + result.test_bin_eq("Server's Hello Retry Request record", + ctx->pull_send_buffer(), + vars.get_req_bin("Record_HelloRetryRequest")); + result.test_is_true("TLS handshake not yet finished", !ctx->server.is_active()); + result.test_is_true("handshake is not yet complete", !ctx->server.is_handshake_complete()); }), CHECK("Receive updated Client Hello message", @@ -2132,29 +2118,29 @@ result.require("ctx is available", ctx != nullptr); const auto& msgs = ctx->observed_handshake_messages(); - result.test_eq("Server Hello", - msgs.at("server_hello")[0], - strip_message_header(vars.get_opt_bin("Message_ServerHello"))); - result.test_eq("Encrypted Extensions", - msgs.at("encrypted_extensions")[0], - strip_message_header(vars.get_opt_bin("Message_EncryptedExtensions"))); - result.test_eq("Certificate", - msgs.at("certificate")[0], - strip_message_header(vars.get_opt_bin("Message_Server_Certificate"))); - result.test_eq("CertificateVerify", - msgs.at("certificate_verify")[0], - strip_message_header(vars.get_opt_bin("Message_Server_CertificateVerify"))); - result.test_eq("Finished", - msgs.at("finished")[0], - strip_message_header(vars.get_opt_bin("Message_Server_Finished"))); - - result.test_eq("Server's entire second flight", - ctx->pull_send_buffer(), - concat(vars.get_req_bin("Record_ServerHello"), - vars.get_req_bin("Record_ServerHandshakeMessages"))); - result.confirm("Server could now send application data", ctx->server.is_active()); - result.confirm("handshake is not yet complete", - !ctx->server.is_handshake_complete()); // See RFC 8446 4.4.4 + result.test_bin_eq("Server Hello", + msgs.at("server_hello")[0], + strip_message_header(vars.get_opt_bin("Message_ServerHello"))); + result.test_bin_eq("Encrypted Extensions", + msgs.at("encrypted_extensions")[0], + strip_message_header(vars.get_opt_bin("Message_EncryptedExtensions"))); + result.test_bin_eq("Certificate", + msgs.at("certificate")[0], + strip_message_header(vars.get_opt_bin("Message_Server_Certificate"))); + result.test_bin_eq("CertificateVerify", + msgs.at("certificate_verify")[0], + strip_message_header(vars.get_opt_bin("Message_Server_CertificateVerify"))); + result.test_bin_eq("Finished", + msgs.at("finished")[0], + strip_message_header(vars.get_opt_bin("Message_Server_Finished"))); + + result.test_bin_eq("Server's entire second flight", + ctx->pull_send_buffer(), + concat(vars.get_req_bin("Record_ServerHello"), + vars.get_req_bin("Record_ServerHandshakeMessages"))); + result.test_is_true("Server could now send application data", ctx->server.is_active()); + result.test_is_true("handshake is not yet complete", + !ctx->server.is_handshake_complete()); // See RFC 8446 4.4.4 }), CHECK("Receive Client Finished", @@ -2169,8 +2155,8 @@ "tls_session_established", "tls_session_activated"}); - result.confirm("TLS handshake finished", ctx->server.is_active()); - result.confirm("handshake is complete", ctx->server.is_handshake_complete()); + result.test_is_true("TLS handshake finished", ctx->server.is_active()); + result.test_is_true("handshake is complete", ctx->server.is_handshake_complete()); }), CHECK("Receive Client close_notify", @@ -2181,9 +2167,9 @@ ctx->check_callback_invocations( result, "client finished received", {"tls_alert", "tls_peer_closed_connection"}); - result.confirm("connection is not yet closed", !ctx->server.is_closed()); - result.confirm("connection is still active", ctx->server.is_active()); - result.confirm("handshake is still complete", ctx->server.is_handshake_complete()); + result.test_is_true("connection is not yet closed", !ctx->server.is_closed()); + result.test_is_true("connection is still active", ctx->server.is_active()); + result.test_is_true("handshake is still complete", ctx->server.is_handshake_complete()); }), CHECK("Expect Server close_notify", @@ -2191,12 +2177,12 @@ result.require("ctx is available", ctx != nullptr); ctx->server.close(); - result.confirm("connection is now inactive", !ctx->server.is_active()); - result.confirm("connection is now closed", ctx->server.is_closed()); - result.confirm("handshake is still complete", ctx->server.is_handshake_complete()); - result.test_eq("Server's close notify", - ctx->pull_send_buffer(), - vars.get_req_bin("Record_Server_CloseNotify")); + result.test_is_true("connection is now inactive", !ctx->server.is_active()); + result.test_is_true("connection is now closed", ctx->server.is_closed()); + result.test_is_true("handshake is still complete", ctx->server.is_handshake_complete()); + result.test_bin_eq("Server's close notify", + ctx->pull_send_buffer(), + vars.get_req_bin("Record_Server_CloseNotify")); }), }; @@ -2221,7 +2207,7 @@ sort_rfc8448_extensions, make_mock_signatures(vars), true /* use alternative certificate */); - result.confirm("server not closed", !ctx->server.is_closed()); + result.test_is_true("server not closed", !ctx->server.is_closed()); ctx->server.received_data(vars.get_req_bin("Record_ClientHello_1")); @@ -2250,34 +2236,34 @@ result.require("ctx is available", ctx != nullptr); const auto& msgs = ctx->observed_handshake_messages(); - result.test_eq("Server Hello", - msgs.at("server_hello")[0], - strip_message_header(vars.get_opt_bin("Message_ServerHello"))); - result.test_eq("Encrypted Extensions", - msgs.at("encrypted_extensions")[0], - strip_message_header(vars.get_opt_bin("Message_EncryptedExtensions"))); - result.test_eq("Certificate Request", - msgs.at("certificate_request")[0], - strip_message_header(vars.get_opt_bin("Message_CertificateRequest"))); - result.test_eq("Certificate", - msgs.at("certificate")[0], - strip_message_header(vars.get_opt_bin("Message_Server_Certificate"))); - result.test_eq("CertificateVerify", - msgs.at("certificate_verify")[0], - strip_message_header(vars.get_opt_bin("Message_Server_CertificateVerify"))); - result.test_eq("Finished", - msgs.at("finished")[0], - strip_message_header(vars.get_opt_bin("Message_Server_Finished"))); - - result.test_eq("Server's entire first flight", - ctx->pull_send_buffer(), - concat(vars.get_req_bin("Record_ServerHello"), - vars.get_req_bin("Record_ServerHandshakeMessages"))); - - result.confirm("Not yet aware of client's cert chain", ctx->server.peer_cert_chain().empty()); - result.confirm("Server could now send application data", ctx->server.is_active()); - result.confirm("handshake is not yet complete", - !ctx->server.is_handshake_complete()); // See RFC 8446 4.4.4 + result.test_bin_eq("Server Hello", + msgs.at("server_hello")[0], + strip_message_header(vars.get_opt_bin("Message_ServerHello"))); + result.test_bin_eq("Encrypted Extensions", + msgs.at("encrypted_extensions")[0], + strip_message_header(vars.get_opt_bin("Message_EncryptedExtensions"))); + result.test_bin_eq("Certificate Request", + msgs.at("certificate_request")[0], + strip_message_header(vars.get_opt_bin("Message_CertificateRequest"))); + result.test_bin_eq("Certificate", + msgs.at("certificate")[0], + strip_message_header(vars.get_opt_bin("Message_Server_Certificate"))); + result.test_bin_eq("CertificateVerify", + msgs.at("certificate_verify")[0], + strip_message_header(vars.get_opt_bin("Message_Server_CertificateVerify"))); + result.test_bin_eq("Finished", + msgs.at("finished")[0], + strip_message_header(vars.get_opt_bin("Message_Server_Finished"))); + + result.test_bin_eq("Server's entire first flight", + ctx->pull_send_buffer(), + concat(vars.get_req_bin("Record_ServerHello"), + vars.get_req_bin("Record_ServerHandshakeMessages"))); + + result.test_is_true("Not yet aware of client's cert chain", ctx->server.peer_cert_chain().empty()); + result.test_is_true("Server could now send application data", ctx->server.is_active()); + result.test_is_true("handshake is not yet complete", + !ctx->server.is_handshake_complete()); // See RFC 8446 4.4.4 }), CHECK("Receive Client's second flight", @@ -2302,11 +2288,11 @@ "tls_session_activated"}); const auto cert_chain = ctx->server.peer_cert_chain(); - result.confirm("Received client's cert chain", - !cert_chain.empty() && cert_chain.front() == client_certificate()); + result.test_is_true("Received client's cert chain", + !cert_chain.empty() && cert_chain.front() == client_certificate()); - result.confirm("TLS handshake finished", ctx->server.is_active()); - result.confirm("handshake is complete", ctx->server.is_handshake_complete()); + result.test_is_true("TLS handshake finished", ctx->server.is_active()); + result.test_is_true("handshake is complete", ctx->server.is_handshake_complete()); }), CHECK("Receive Client close_notify", @@ -2317,9 +2303,9 @@ ctx->check_callback_invocations( result, "client finished received", {"tls_alert", "tls_peer_closed_connection"}); - result.confirm("connection is not yet closed", !ctx->server.is_closed()); - result.confirm("connection is still active", ctx->server.is_active()); - result.confirm("handshake is still complete", ctx->server.is_handshake_complete()); + result.test_is_true("connection is not yet closed", !ctx->server.is_closed()); + result.test_is_true("connection is still active", ctx->server.is_active()); + result.test_is_true("handshake is still complete", ctx->server.is_handshake_complete()); }), CHECK("Expect Server close_notify", @@ -2327,12 +2313,12 @@ result.require("ctx is available", ctx != nullptr); ctx->server.close(); - result.confirm("connection is now inactive", !ctx->server.is_active()); - result.confirm("connection is now closed", ctx->server.is_closed()); - result.confirm("handshake is still complete", ctx->server.is_handshake_complete()); - result.test_eq("Server's close notify", - ctx->pull_send_buffer(), - vars.get_req_bin("Record_Server_CloseNotify")); + result.test_is_true("connection is now inactive", !ctx->server.is_active()); + result.test_is_true("connection is now closed", ctx->server.is_closed()); + result.test_is_true("handshake is still complete", ctx->server.is_handshake_complete()); + result.test_bin_eq("Server's close notify", + ctx->pull_send_buffer(), + vars.get_req_bin("Record_Server_CloseNotify")); }), }; @@ -2356,7 +2342,7 @@ vars.get_req_u64("CurrentTimestamp"), sort_rfc8448_extensions, make_mock_signatures(vars)); - result.confirm("server not closed", !ctx->server.is_closed()); + result.test_is_true("server not closed", !ctx->server.is_closed()); ctx->server.received_data(vars.get_req_bin("Record_ClientHello_1")); @@ -2383,31 +2369,31 @@ result.require("ctx is available", ctx != nullptr); const auto& msgs = ctx->observed_handshake_messages(); - result.test_eq("Server Hello", - msgs.at("server_hello")[0], - strip_message_header(vars.get_opt_bin("Message_ServerHello"))); - result.test_eq("Encrypted Extensions", - msgs.at("encrypted_extensions")[0], - strip_message_header(vars.get_opt_bin("Message_EncryptedExtensions"))); - result.test_eq("Certificate", - msgs.at("certificate")[0], - strip_message_header(vars.get_opt_bin("Message_Server_Certificate"))); - result.test_eq("CertificateVerify", - msgs.at("certificate_verify")[0], - strip_message_header(vars.get_opt_bin("Message_Server_CertificateVerify"))); - result.test_eq("Finished", - msgs.at("finished")[0], - strip_message_header(vars.get_opt_bin("Message_Server_Finished"))); + result.test_bin_eq("Server Hello", + msgs.at("server_hello")[0], + strip_message_header(vars.get_opt_bin("Message_ServerHello"))); + result.test_bin_eq("Encrypted Extensions", + msgs.at("encrypted_extensions")[0], + strip_message_header(vars.get_opt_bin("Message_EncryptedExtensions"))); + result.test_bin_eq("Certificate", + msgs.at("certificate")[0], + strip_message_header(vars.get_opt_bin("Message_Server_Certificate"))); + result.test_bin_eq("CertificateVerify", + msgs.at("certificate_verify")[0], + strip_message_header(vars.get_opt_bin("Message_Server_CertificateVerify"))); + result.test_bin_eq("Finished", + msgs.at("finished")[0], + strip_message_header(vars.get_opt_bin("Message_Server_Finished"))); // Those records contain the required Change Cipher Spec message the server must produce for compatibility mode compliance - result.test_eq("Server's entire first flight", - ctx->pull_send_buffer(), - concat(vars.get_req_bin("Record_ServerHello"), - vars.get_req_bin("Record_ServerHandshakeMessages"))); - - result.confirm("Server could now send application data", ctx->server.is_active()); - result.confirm("handshake is not yet complete", - !ctx->server.is_handshake_complete()); // See RFC 8446 4.4.4 + result.test_bin_eq("Server's entire first flight", + ctx->pull_send_buffer(), + concat(vars.get_req_bin("Record_ServerHello"), + vars.get_req_bin("Record_ServerHandshakeMessages"))); + + result.test_is_true("Server could now send application data", ctx->server.is_active()); + result.test_is_true("handshake is not yet complete", + !ctx->server.is_handshake_complete()); // See RFC 8446 4.4.4 }), CHECK("Receive Client Finished", @@ -2422,8 +2408,8 @@ "tls_session_established", "tls_session_activated"}); - result.confirm("TLS handshake fully finished", ctx->server.is_active()); - result.confirm("handshake is complete", ctx->server.is_handshake_complete()); + result.test_is_true("TLS handshake fully finished", ctx->server.is_active()); + result.test_is_true("handshake is complete", ctx->server.is_handshake_complete()); }), CHECK("Receive Client close_notify", @@ -2434,9 +2420,9 @@ ctx->check_callback_invocations( result, "client finished received", {"tls_alert", "tls_peer_closed_connection"}); - result.confirm("connection is not yet closed", !ctx->server.is_closed()); - result.confirm("connection is still active", ctx->server.is_active()); - result.confirm("handshake is still complete", ctx->server.is_handshake_complete()); + result.test_is_true("connection is not yet closed", !ctx->server.is_closed()); + result.test_is_true("connection is still active", ctx->server.is_active()); + result.test_is_true("handshake is still complete", ctx->server.is_handshake_complete()); }), CHECK("Expect Server close_notify", @@ -2444,12 +2430,12 @@ result.require("ctx is available", ctx != nullptr); ctx->server.close(); - result.confirm("connection is now inactive", !ctx->server.is_active()); - result.confirm("connection is now closed", ctx->server.is_closed()); - result.confirm("handshake is still complete", ctx->server.is_handshake_complete()); - result.test_eq("Server's close notify", - ctx->pull_send_buffer(), - vars.get_req_bin("Record_Server_CloseNotify")); + result.test_is_true("connection is now inactive", !ctx->server.is_active()); + result.test_is_true("connection is now closed", ctx->server.is_closed()); + result.test_is_true("handshake is still complete", ctx->server.is_handshake_complete()); + result.test_bin_eq("Server's close notify", + ctx->pull_send_buffer(), + vars.get_req_bin("Record_Server_CloseNotify")); }), }; @@ -2474,19 +2460,17 @@ // implementation and generated the transcript. To stay compatible // with the now hard-coded transcript, we pin the extension order. if(type == Botan::TLS::Handshake_Type::EncryptedExtensions) { - sort_extensions(exts, - { - Botan::TLS::Extension_Code::SupportedGroups, - Botan::TLS::Extension_Code::RecordSizeLimit, - Botan::TLS::Extension_Code::ServerNameIndication, - }); + exts.reorder({ + Botan::TLS::Extension_Code::SupportedGroups, + Botan::TLS::Extension_Code::RecordSizeLimit, + Botan::TLS::Extension_Code::ServerNameIndication, + }); } else if(type == Botan::TLS::Handshake_Type::ServerHello) { - sort_extensions(exts, - { - Botan::TLS::Extension_Code::SupportedVersions, - Botan::TLS::Extension_Code::KeyShare, - Botan::TLS::Extension_Code::PresharedKey, - }); + exts.reorder({ + Botan::TLS::Extension_Code::SupportedVersions, + Botan::TLS::Extension_Code::KeyShare, + Botan::TLS::Extension_Code::PresharedKey, + }); } }; @@ -2501,7 +2485,7 @@ ExternalPSK(vars.get_req_str("PskIdentity"), vars.get_req_str("PskPRF"), lock(vars.get_req_bin("PskSecret")))); - result.confirm("server not closed", !ctx->server.is_closed()); + result.test_is_true("server not closed", !ctx->server.is_closed()); ctx->server.received_data(vars.get_req_bin("Record_ClientHello_1")); @@ -2524,24 +2508,24 @@ result.require("ctx is available", ctx != nullptr); const auto& msgs = ctx->observed_handshake_messages(); - result.test_eq("Server Hello", - msgs.at("server_hello")[0], - strip_message_header(vars.get_opt_bin("Message_ServerHello"))); - result.test_eq("Encrypted Extensions", - msgs.at("encrypted_extensions")[0], - strip_message_header(vars.get_opt_bin("Message_EncryptedExtensions"))); - result.test_eq("Server Finished", - msgs.at("finished")[0], - strip_message_header(vars.get_opt_bin("Message_Server_Finished"))); - - result.test_eq("Server's entire first flight", - ctx->pull_send_buffer(), - concat(vars.get_req_bin("Record_ServerHello"), - vars.get_req_bin("Record_ServerHandshakeMessages"))); - - result.confirm("Server can now send application data", ctx->server.is_active()); - result.confirm("handshake is not yet complete", - !ctx->server.is_handshake_complete()); // See RFC 8446 4.4.4 + result.test_bin_eq("Server Hello", + msgs.at("server_hello")[0], + strip_message_header(vars.get_opt_bin("Message_ServerHello"))); + result.test_bin_eq("Encrypted Extensions", + msgs.at("encrypted_extensions")[0], + strip_message_header(vars.get_opt_bin("Message_EncryptedExtensions"))); + result.test_bin_eq("Server Finished", + msgs.at("finished")[0], + strip_message_header(vars.get_opt_bin("Message_Server_Finished"))); + + result.test_bin_eq("Server's entire first flight", + ctx->pull_send_buffer(), + concat(vars.get_req_bin("Record_ServerHello"), + vars.get_req_bin("Record_ServerHandshakeMessages"))); + + result.test_is_true("Server can now send application data", ctx->server.is_active()); + result.test_is_true("handshake is not yet complete", + !ctx->server.is_handshake_complete()); // See RFC 8446 4.4.4 }), CHECK("Send Client Finished", @@ -2566,14 +2550,14 @@ ctx->check_callback_invocations(result, "application data received", {"tls_record_received"}); const auto rcvd = ctx->pull_receive_buffer(); - result.test_eq("decrypted application traffic", rcvd, vars.get_req_bin("Client_AppData")); - result.test_is_eq("sequence number", ctx->last_received_seq_no(), uint64_t(0)); + result.test_bin_eq("decrypted application traffic", rcvd, vars.get_req_bin("Client_AppData")); + result.test_u64_eq("sequence number", ctx->last_received_seq_no(), uint64_t(0)); ctx->send(vars.get_req_bin("Server_AppData")); ctx->check_callback_invocations(result, "application data sent", {"tls_emit_data"}); - result.test_eq("correct server application data", - ctx->pull_send_buffer(), - vars.get_req_bin("Record_Server_AppData")); + result.test_bin_eq("correct server application data", + ctx->pull_send_buffer(), + vars.get_req_bin("Record_Server_AppData")); }), CHECK("Terminate Connection", @@ -2584,18 +2568,18 @@ ctx->check_callback_invocations( result, "client finished received", {"tls_alert", "tls_peer_closed_connection"}); - result.confirm("connection is not yet closed", !ctx->server.is_closed()); - result.confirm("connection is still active", ctx->server.is_active()); - result.confirm("handshake is still complete", ctx->server.is_handshake_complete()); + result.test_is_true("connection is not yet closed", !ctx->server.is_closed()); + result.test_is_true("connection is still active", ctx->server.is_active()); + result.test_is_true("handshake is still complete", ctx->server.is_handshake_complete()); ctx->server.close(); - result.confirm("connection is now inactive", !ctx->server.is_active()); - result.confirm("connection is now closed", ctx->server.is_closed()); - result.confirm("handshake is still complete", ctx->server.is_handshake_complete()); - result.test_eq("Server's close notify", - ctx->pull_send_buffer(), - vars.get_req_bin("Record_Server_CloseNotify")); + result.test_is_true("connection is now inactive", !ctx->server.is_active()); + result.test_is_true("connection is now closed", ctx->server.is_closed()); + result.test_is_true("handshake is still complete", ctx->server.is_handshake_complete()); + result.test_bin_eq("Server's close notify", + ctx->pull_send_buffer(), + vars.get_req_bin("Record_Server_CloseNotify")); }), }; } @@ -2614,20 +2598,18 @@ // To stay compatible with the now hard-coded transcript, we pin the // extension order. if(type == Botan::TLS::Handshake_Type::EncryptedExtensions) { - sort_extensions(exts, - { - Botan::TLS::Extension_Code::ClientCertificateType, - Botan::TLS::Extension_Code::ServerCertificateType, - Botan::TLS::Extension_Code::SupportedGroups, - Botan::TLS::Extension_Code::RecordSizeLimit, - Botan::TLS::Extension_Code::ServerNameIndication, - }); + exts.reorder({ + Botan::TLS::Extension_Code::ClientCertificateType, + Botan::TLS::Extension_Code::ServerCertificateType, + Botan::TLS::Extension_Code::SupportedGroups, + Botan::TLS::Extension_Code::RecordSizeLimit, + Botan::TLS::Extension_Code::ServerNameIndication, + }); } else if(type == Botan::TLS::Handshake_Type::ServerHello) { - sort_extensions(exts, - { - Botan::TLS::Extension_Code::KeyShare, - Botan::TLS::Extension_Code::SupportedVersions, - }); + exts.reorder({ + Botan::TLS::Extension_Code::KeyShare, + Botan::TLS::Extension_Code::SupportedVersions, + }); } }; @@ -2641,7 +2623,7 @@ vars.get_req_u64("CurrentTimestamp"), sort_our_extensions, make_mock_signatures(vars)); - result.confirm("server not closed", !ctx->server.is_closed()); + result.test_is_true("server not closed", !ctx->server.is_closed()); ctx->server.received_data(vars.get_req_bin("Record_ClientHello_1")); @@ -2670,34 +2652,34 @@ result.require("ctx is available", ctx != nullptr); const auto& msgs = ctx->observed_handshake_messages(); - result.test_eq("Server Hello", - msgs.at("server_hello")[0], - strip_message_header(vars.get_opt_bin("Message_ServerHello"))); - result.test_eq("Encrypted Extensions", - msgs.at("encrypted_extensions")[0], - strip_message_header(vars.get_opt_bin("Message_EncryptedExtensions"))); - result.test_eq("Certificate Request", - msgs.at("certificate_request")[0], - strip_message_header(vars.get_opt_bin("Message_CertificateRequest"))); - result.test_eq("Certificate", - msgs.at("certificate")[0], - strip_message_header(vars.get_opt_bin("Message_Server_Certificate"))); - result.test_eq("CertificateVerify", - msgs.at("certificate_verify")[0], - strip_message_header(vars.get_opt_bin("Message_Server_CertificateVerify"))); - result.test_eq("Finished", - msgs.at("finished")[0], - strip_message_header(vars.get_opt_bin("Message_Server_Finished"))); - - result.test_eq("Server's entire first flight", - ctx->pull_send_buffer(), - concat(vars.get_req_bin("Record_ServerHello"), - vars.get_req_bin("Record_ServerHandshakeMessages"))); - - result.confirm("Not yet aware of client's cert chain", ctx->server.peer_cert_chain().empty()); - result.confirm("Server could now send application data", ctx->server.is_active()); - result.confirm("handshake is not yet complete", - !ctx->server.is_handshake_complete()); // See RFC 8446 4.4.4 + result.test_bin_eq("Server Hello", + msgs.at("server_hello")[0], + strip_message_header(vars.get_opt_bin("Message_ServerHello"))); + result.test_bin_eq("Encrypted Extensions", + msgs.at("encrypted_extensions")[0], + strip_message_header(vars.get_opt_bin("Message_EncryptedExtensions"))); + result.test_bin_eq("Certificate Request", + msgs.at("certificate_request")[0], + strip_message_header(vars.get_opt_bin("Message_CertificateRequest"))); + result.test_bin_eq("Certificate", + msgs.at("certificate")[0], + strip_message_header(vars.get_opt_bin("Message_Server_Certificate"))); + result.test_bin_eq("CertificateVerify", + msgs.at("certificate_verify")[0], + strip_message_header(vars.get_opt_bin("Message_Server_CertificateVerify"))); + result.test_bin_eq("Finished", + msgs.at("finished")[0], + strip_message_header(vars.get_opt_bin("Message_Server_Finished"))); + + result.test_bin_eq("Server's entire first flight", + ctx->pull_send_buffer(), + concat(vars.get_req_bin("Record_ServerHello"), + vars.get_req_bin("Record_ServerHandshakeMessages"))); + + result.test_is_true("Not yet aware of client's cert chain", ctx->server.peer_cert_chain().empty()); + result.test_is_true("Server could now send application data", ctx->server.is_active()); + result.test_is_true("handshake is not yet complete", + !ctx->server.is_handshake_complete()); // See RFC 8446 4.4.4 }), CHECK("Receive Client's second flight", @@ -2722,12 +2704,12 @@ "tls_session_activated"}); const auto raw_pk = ctx->server.peer_raw_public_key(); - result.confirm( + result.test_is_true( "Received client's raw public key", raw_pk && raw_pk->fingerprint_public() == client_raw_public_key_pair()->fingerprint_public()); - result.confirm("TLS handshake finished", ctx->server.is_active()); - result.confirm("handshake is complete", ctx->server.is_handshake_complete()); + result.test_is_true("TLS handshake finished", ctx->server.is_active()); + result.test_is_true("handshake is complete", ctx->server.is_handshake_complete()); }), CHECK("Receive Client close_notify", @@ -2738,9 +2720,9 @@ ctx->check_callback_invocations( result, "client finished received", {"tls_alert", "tls_peer_closed_connection"}); - result.confirm("connection is not yet closed", !ctx->server.is_closed()); - result.confirm("connection is still active", ctx->server.is_active()); - result.confirm("handshake is still complete", ctx->server.is_handshake_complete()); + result.test_is_true("connection is not yet closed", !ctx->server.is_closed()); + result.test_is_true("connection is still active", ctx->server.is_active()); + result.test_is_true("handshake is still complete", ctx->server.is_handshake_complete()); }), CHECK("Expect Server close_notify", @@ -2748,12 +2730,12 @@ result.require("ctx is available", ctx != nullptr); ctx->server.close(); - result.confirm("connection is now inactive", !ctx->server.is_active()); - result.confirm("connection is now closed", ctx->server.is_closed()); - result.confirm("handshake is still complete", ctx->server.is_handshake_complete()); - result.test_eq("Server's close notify", - ctx->pull_send_buffer(), - vars.get_req_bin("Record_Server_CloseNotify")); + result.test_is_true("connection is now inactive", !ctx->server.is_active()); + result.test_is_true("connection is now closed", ctx->server.is_closed()); + result.test_is_true("handshake is still complete", ctx->server.is_handshake_complete()); + result.test_bin_eq("Server's close notify", + ctx->pull_send_buffer(), + vars.get_req_bin("Record_Server_CloseNotify")); }), }; } @@ -2764,4 +2746,6 @@ #endif +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_tls_session_manager.cpp botan3-3.12.0+dfsg/src/tests/test_tls_session_manager.cpp --- botan3-3.7.1+dfsg/src/tests/test_tls_session_manager.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_tls_session_manager.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,24 +7,30 @@ #include "tests.h" -#include -#include -#include - #if defined(BOTAN_HAS_TLS) + #include #include + #include + #include #include #include #include #include #include + #include #include + #include + #include #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER) #include #endif + #if defined(BOTAN_HAS_TLS_13) + #include + #endif + #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) #include #if defined(__cpp_lib_filesystem) @@ -37,10 +43,12 @@ // where `Botan::TLS::Protocol_Version::TLS_V12` would lead to an "Internal // Compiler Error" when used in the affected context. // -// TODO: remove the workaround once GCC 11 is not supported anymore. +// TODO(Botan4): remove the workaround once GCC 11 is not supported anymore. namespace Botan_Tests { +namespace { + class Test_Credentials_Manager : public Botan::Credentials_Manager { public: Botan::secure_vector session_ticket_key() override { @@ -59,13 +67,17 @@ class Session_Manager_Callbacks : public Botan::TLS::Callbacks { public: - void tls_emit_data(std::span) override { BOTAN_ASSERT_NOMSG(false); } + void tls_emit_data(std::span /*data*/) override { BOTAN_ASSERT_NOMSG(false); } - void tls_record_received(uint64_t, std::span) override { BOTAN_ASSERT_NOMSG(false); } + void tls_record_received(uint64_t /*record*/, std::span /*data*/) override { + BOTAN_ASSERT_NOMSG(false); + } - void tls_alert(Botan::TLS::Alert) override { BOTAN_ASSERT_NOMSG(false); } + void tls_alert(Botan::TLS::Alert /*alert*/) override { BOTAN_ASSERT_NOMSG(false); } - void tls_session_established(const Botan::TLS::Session_Summary&) override { BOTAN_ASSERT_NOMSG(false); } + void tls_session_established(const Botan::TLS::Session_Summary& /*summary*/) override { + BOTAN_ASSERT_NOMSG(false); + } std::chrono::system_clock::time_point tls_current_timestamp() override { return std::chrono::system_clock::now() + std::chrono::hours(m_ticks); @@ -94,8 +106,6 @@ bool m_allow_session_reuse = true; }; -namespace { - decltype(auto) random_id(Botan::RandomNumberGenerator& rng) { return rng.random_vec(32); } @@ -108,17 +118,28 @@ return rng.random_vec(32); } -const Botan::TLS::Server_Information server_info("botan.randombit.net"); +const Botan::TLS::Server_Information& server_info() { + static const Botan::TLS::Server_Information si("botan.randombit.net"); + return si; +} decltype(auto) default_session(Botan::TLS::Connection_Side side, Botan::TLS::Callbacks& cbs, Botan::TLS::Protocol_Version version = Botan::TLS::Protocol_Version::TLS_V12) { if(version.is_pre_tls_13()) { - return Botan::TLS::Session( - {}, version, 0x009C, side, true, true, {}, server_info, 0, cbs.tls_current_timestamp()); + return Botan::TLS::Session(Botan::secure_vector(48, 0x42), + version, + 0x009C, + side, + true, + true, + {}, + server_info(), + 0, + cbs.tls_current_timestamp()); } else { #if defined(BOTAN_HAS_TLS_13) - return Botan::TLS::Session({}, + return Botan::TLS::Session(Botan::secure_vector(32, 0x42), std::nullopt, 0, std::chrono::seconds(1024), @@ -127,7 +148,7 @@ side, {}, nullptr, - server_info, + server_info(), cbs.tls_current_timestamp()); #else throw Test_Error("TLS 1.3 is not available in this build"); @@ -152,126 +173,119 @@ CHECK("empty cache does not obtain anything", [&](auto& result) { - result.confirm("no session found via server info", mgr->find(server_info, cbs, plcy).empty()); + result.test_is_true("no session found via server info", mgr->find(server_info(), cbs, plcy).empty()); - Botan::TLS::Session_ID mock_id = random_id(*rng); + const 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)); + result.test_is_true("no session found via ID", !mgr->retrieve(mock_id, cbs, plcy)); + result.test_is_true("no session found via ID", !mgr->retrieve(mock_ticket, cbs, plcy)); }), CHECK("clearing empty cache", - [&](auto& result) { result.test_eq("does not delete anything", mgr->remove_all(), 0); }), + [&](auto& result) { result.test_sz_eq("does not delete anything", mgr->remove_all(), 0); }), CHECK("establish new session", [&](auto& result) { auto handle = mgr->establish(default_session(Botan::TLS::Connection_Side::Server, cbs), default_id); - if(result.confirm("establishment was successful", handle.has_value())) { + if(result.test_is_true("establishment was successful", handle.has_value())) { result.require("session id was set", handle->id().has_value()); - result.confirm("session ticket was empty", !handle->ticket().has_value()); - result.test_is_eq("session id is correct", handle->id().value(), default_id); + result.test_is_true("session ticket was empty", !handle->ticket().has_value()); + result.test_bin_eq("session id is correct", handle->id().value(), default_id); } }), - CHECK("obtain session from server info", - [&](auto& result) { - auto sessions = mgr->find(server_info, cbs, plcy); - if(result.confirm("session was found successfully", sessions.size() == 1)) { - result.test_is_eq("protocol version was echoed", - sessions[0].session.version(), - Botan::TLS::Protocol_Version(Botan::TLS::Version_Code::TLS_V12)); - result.test_is_eq("ciphersuite was echoed", sessions[0].session.ciphersuite_code(), uint16_t(0x009C)); - result.test_is_eq("ID was echoed", sessions[0].handle.id().value(), default_id); - result.confirm("not a ticket", !sessions[0].handle.ticket().has_value()); - } - }), + CHECK( + "obtain session from server info", + [&](auto& result) { + auto sessions = mgr->find(server_info(), cbs, plcy); + if(result.test_is_true("session was found successfully", sessions.size() == 1)) { + result.test_u16_eq("protocol version was echoed", sessions[0].session.version().version_code(), 0x0303); + result.test_u16_eq("ciphersuite was echoed", sessions[0].session.ciphersuite_code(), uint16_t(0x009C)); + result.test_bin_eq("ID was echoed", sessions[0].handle.id().value(), default_id); + result.test_is_true("not a ticket", !sessions[0].handle.ticket().has_value()); + } + }), CHECK("obtain session from ID", [&](auto& result) { auto session = mgr->retrieve(default_id, cbs, plcy); - if(result.confirm("session was found successfully", session.has_value())) { - result.test_is_eq("protocol version was echoed", - session->version(), - Botan::TLS::Protocol_Version(Botan::TLS::Version_Code::TLS_V12)); - result.test_is_eq("ciphersuite was echoed", session->ciphersuite_code(), uint16_t(0x009C)); + if(result.test_is_true("session was found successfully", session.has_value())) { + result.test_u16_eq("protocol version was echoed", session->version().version_code(), 0x0303); + result.test_u16_eq("ciphersuite was echoed", session->ciphersuite_code(), uint16_t(0x009C)); } }), CHECK("obtain session from ID disguised as opaque handle", [&](auto& result) { auto session = mgr->retrieve(Botan::TLS::Opaque_Session_Handle(default_id), cbs, plcy); - if(result.confirm("session was found successfully", session.has_value())) { - result.test_is_eq("protocol version was echoed", - session->version(), - Botan::TLS::Protocol_Version(Botan::TLS::Version_Code::TLS_V12)); - result.test_is_eq("ciphersuite was echoed", session->ciphersuite_code(), uint16_t(0x009C)); + if(result.test_is_true("session was found successfully", session.has_value())) { + result.test_u16_eq("protocol version was echoed", session->version().version_code(), 0x0303); + result.test_u16_eq("ciphersuite was echoed", session->ciphersuite_code(), uint16_t(0x009C)); } }), CHECK("obtain session from ticket == id does not work", [&](auto& result) { auto session = mgr->retrieve(Botan::TLS::Session_Ticket(default_id), cbs, plcy); - result.confirm("session was not found", !session.has_value()); + result.test_is_true("session was not found", !session.has_value()); }), CHECK("invalid ticket causes std::nullopt", [&](auto& result) { auto no_session = mgr->retrieve(random_ticket(*rng), cbs, plcy); - result.confirm("std::nullopt on bogus ticket", !no_session.has_value()); + result.test_is_true("std::nullopt on bogus ticket", !no_session.has_value()); }), CHECK("invalid ID causes std::nullopt", [&](auto& result) { auto no_session = mgr->retrieve(random_id(*rng), cbs, plcy); - result.confirm("std::nullopt on bogus ID", !no_session.has_value()); + result.test_is_true("std::nullopt on bogus ID", !no_session.has_value()); }), CHECK("remove_all", [&](auto& result) { - result.test_eq("removed one element", mgr->remove_all(), 1); - result.test_eq("should be empty now", mgr->remove_all(), 0); + result.test_sz_eq("removed one element", mgr->remove_all(), 1); + result.test_sz_eq("should be empty now", mgr->remove_all(), 0); }), - CHECK("add session with ID", - [&](auto& result) { - Botan::TLS::Session_ID new_id = random_id(*rng); + CHECK( + "add session with ID", + [&](auto& result) { + const 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()); + 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()); - auto sessions = mgr->find(server_info, cbs, plcy); - if(result.confirm("found via server info", sessions.size() == 1)) { - result.test_is_eq("protocol version was echoed", - sessions[0].session.version(), - Botan::TLS::Protocol_Version(Botan::TLS::Version_Code::TLS_V12)); - result.test_is_eq("ciphersuite was echoed", sessions[0].session.ciphersuite_code(), uint16_t(0x009C)); - result.test_is_eq("ID was echoed", sessions[0].handle.id().value(), new_id); - result.confirm("ticket was not stored", !sessions[0].handle.ticket().has_value()); - } + auto sessions = mgr->find(server_info(), cbs, plcy); + if(result.test_is_true("found via server info", sessions.size() == 1)) { + result.test_u16_eq("protocol version was echoed", sessions[0].session.version().version_code(), 0x0303); + result.test_u16_eq("ciphersuite was echoed", sessions[0].session.ciphersuite_code(), uint16_t(0x009C)); + result.test_bin_eq("ID was echoed", sessions[0].handle.id().value(), new_id); + result.test_is_true("ticket was not stored", !sessions[0].handle.ticket().has_value()); + } - mgr->remove_all(); - }), + mgr->remove_all(); + }), - CHECK("add session with ticket", - [&](auto& result) { - Botan::TLS::Session_Ticket new_ticket = random_ticket(*rng); + CHECK( + "add session with ticket", + [&](auto& result) { + const 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 + mgr->store(default_session(Botan::TLS::Connection_Side::Client, cbs), new_ticket); + // cannot be obtained by (non-existent) ID or randomly generated ticket - auto sessions = mgr->find(server_info, cbs, plcy); - if(result.confirm("found via server info", sessions.size() == 1)) { - result.test_is_eq("protocol version was echoed", - sessions[0].session.version(), - Botan::TLS::Protocol_Version(Botan::TLS::Version_Code::TLS_V12)); - result.test_is_eq("ciphersuite was echoed", sessions[0].session.ciphersuite_code(), uint16_t(0x009C)); - result.confirm("ID was not stored", !sessions[0].handle.id().has_value()); - result.test_is_eq("ticket was echoed", sessions[0].handle.ticket().value(), new_ticket); - } + auto sessions = mgr->find(server_info(), cbs, plcy); + if(result.test_is_true("found via server info", sessions.size() == 1)) { + result.test_u16_eq("protocol version was echoed", sessions[0].session.version().version_code(), 0x0303); + result.test_u16_eq("ciphersuite was echoed", sessions[0].session.ciphersuite_code(), uint16_t(0x009C)); + result.test_is_true("ID was not stored", !sessions[0].handle.id().has_value()); + result.test_bin_eq("ticket was echoed", sessions[0].handle.ticket().value(), new_ticket); + } - mgr->remove_all(); - }), + mgr->remove_all(); + }), CHECK("removing by ID or opaque handle", [&](auto& result) { @@ -287,45 +301,42 @@ "saving worked", new_session2.has_value() && new_session2->id().has_value() && !new_session2->ticket().has_value()); - result.test_is_eq("can find via server info", local_mgr.find(server_info, cbs, plcy).size(), size_t(2)); + result.test_sz_eq("can find via server info", local_mgr.find(server_info(), cbs, plcy).size(), 2); - result.test_is_eq("one was deleted", local_mgr.remove(default_id), size_t(1)); - result.confirm("cannot obtain via default ID anymore", - !local_mgr.retrieve(default_id, cbs, plcy).has_value()); - result.test_is_eq( - "can find less via server info", local_mgr.find(server_info, cbs, plcy).size(), size_t(1)); + result.test_sz_eq("one was deleted", local_mgr.remove(default_id), 1); + result.test_is_true("cannot obtain via default ID anymore", + !local_mgr.retrieve(default_id, cbs, plcy).has_value()); + result.test_sz_eq("can find less via server info", local_mgr.find(server_info(), cbs, plcy).size(), 1); - result.test_is_eq("last one was deleted", + result.test_sz_eq("last one was deleted", local_mgr.remove(Botan::TLS::Opaque_Session_Handle(new_session2->id().value())), - size_t(1)); - result.confirm("cannot obtain via ID anymore", - !local_mgr.retrieve(new_session2->id().value(), cbs, plcy).has_value()); - result.confirm("cannot find via server info", local_mgr.find(server_info, cbs, plcy).empty()); + 1); + result.test_is_true("cannot obtain via ID anymore", + !local_mgr.retrieve(new_session2->id().value(), cbs, plcy).has_value()); + result.test_is_true("cannot find via server info", local_mgr.find(server_info(), cbs, plcy).empty()); }), CHECK("removing by ticket or opaque handle", [&](auto& result) { Botan::TLS::Session_Manager_In_Memory local_mgr(rng); - Botan::TLS::Session_Ticket ticket1 = random_ticket(*rng); - Botan::TLS::Session_Ticket ticket2 = random_ticket(*rng); + const Botan::TLS::Session_Ticket ticket1 = random_ticket(*rng); + const 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); local_mgr.store(default_session(Botan::TLS::Connection_Side::Client, cbs), ticket3); - result.test_is_eq( - "can find them via server info ", local_mgr.find(server_info, cbs, plcy).size(), size_t(3)); + result.test_sz_eq("can find them via server info ", local_mgr.find(server_info(), cbs, plcy).size(), 3); - result.test_is_eq("remove one session by ticket", local_mgr.remove(ticket2), size_t(1)); - result.test_is_eq( - "can find two via server info", local_mgr.find(server_info, cbs, plcy).size(), size_t(2)); + result.test_sz_eq("remove one session by ticket", local_mgr.remove(ticket2), 1); + result.test_sz_eq("can find two via server info", local_mgr.find(server_info(), cbs, plcy).size(), 2); - result.test_is_eq("remove one session by opaque handle", + result.test_sz_eq("remove one session by opaque handle", local_mgr.remove(Botan::TLS::Opaque_Session_Handle(ticket3.get())), - size_t(1)); - result.test_is_eq( - "can find only one via server info", local_mgr.find(server_info, cbs, plcy).size(), size_t(1)); + 1); + result.test_sz_eq( + "can find only one via server info", local_mgr.find(server_info(), cbs, plcy).size(), 1); }), CHECK( @@ -341,25 +352,25 @@ mgr->establish(default_session(Botan::TLS::Connection_Side::Server, cbs), random_id(*rng)).value()); } - for(size_t i = 0; i < handles.size(); ++i) { - result.confirm("session still there", mgr->retrieve(handles[i], cbs, plcy).has_value()); + for(const auto& handle : handles) { + result.test_is_true("session still there", mgr->retrieve(handle, cbs, plcy).has_value()); } // 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(*rng)).value()); - result.confirm("oldest session gone", !mgr->retrieve(handles[0], cbs, plcy).has_value()); + result.test_is_true("oldest session gone", !mgr->retrieve(handles[0], cbs, plcy).has_value()); for(size_t i = 1; i < handles.size(); ++i) { - result.confirm("session still there", mgr->retrieve(handles[i], cbs, plcy).has_value()); + result.test_is_true("session still there", mgr->retrieve(handles[i], cbs, plcy).has_value()); } // remove a session to cause a 'gap' in the FIFO mgr->remove(handles[4]); - result.confirm("oldest session gone", !mgr->retrieve(handles[0], cbs, plcy).has_value()); - result.confirm("deleted session gone", !mgr->retrieve(handles[4], cbs, plcy).has_value()); + result.test_is_true("oldest session gone", !mgr->retrieve(handles[0], cbs, plcy).has_value()); + result.test_is_true("deleted session gone", !mgr->retrieve(handles[4], cbs, plcy).has_value()); for(size_t i = 1; i < handles.size(); ++i) { - result.confirm("session still there", i == 4 || mgr->retrieve(handles[i], cbs, plcy).has_value()); + result.test_is_true("session still there", i == 4 || mgr->retrieve(handles[i], cbs, plcy).has_value()); } // insert enough new sessions to fully purge the ones currently held @@ -369,15 +380,15 @@ } for(size_t i = 0; i < handles.size() - mgr->capacity(); ++i) { - result.confirm("session gone", !mgr->retrieve(handles[i], cbs, plcy).has_value()); + result.test_is_true("session gone", !mgr->retrieve(handles[i], cbs, plcy).has_value()); } for(size_t i = handles.size() - mgr->capacity(); i < handles.size(); ++i) { - result.confirm("session still there", mgr->retrieve(handles[i], cbs, plcy).has_value()); + result.test_is_true("session still there", mgr->retrieve(handles[i], cbs, plcy).has_value()); } // clear it all out - result.test_eq("rest of the sessions removed", mgr->remove_all(), size_t(5)); + result.test_sz_eq("rest of the sessions removed", mgr->remove_all(), size_t(5)); }), }; } @@ -392,18 +403,21 @@ auto default_session = [&](const std::string& suite, Botan::TLS::Callbacks& mycbs, Botan::TLS::Protocol_Version version = Botan::TLS::Protocol_Version::TLS_V13) { + // The session deserializer enforces 48-byte master_secret on TLS 1.2 + // and 32-or-48-byte session_psk on TLS 1.3. 48 bytes covers both. + const Botan::secure_vector dummy_secret(48, 0x42); return (version.is_pre_tls_13()) - ? Botan::TLS::Session({}, + ? Botan::TLS::Session(dummy_secret, version, Botan::TLS::Ciphersuite::from_name(suite)->ciphersuite_code(), Botan::TLS::Connection_Side::Server, true, true, {}, - server_info, + server_info(), 0, mycbs.tls_current_timestamp()) - : Botan::TLS::Session({}, + : Botan::TLS::Session(dummy_secret, std::nullopt, 0, std::chrono::seconds(1024), @@ -412,7 +426,7 @@ Botan::TLS::Connection_Side::Server, {}, nullptr, - server_info, + server_info(), mycbs.tls_current_timestamp()); }; @@ -427,9 +441,9 @@ 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()); - result.confirm( + result.test_is_true("empty ticket list, no session", + !mgr.choose_from_offered_tickets({}, "SHA-256", cbs, plcy).has_value()); + result.test_is_true( "empty session manager, no session", !mgr.choose_from_offered_tickets(std::vector{ticket(random_session_ticket)}, "SHA-256", cbs, plcy) .has_value()); @@ -447,19 +461,19 @@ auto session1 = mgr.choose_from_offered_tickets(std::vector{ticket(handles[0].id().value())}, "SHA-256", cbs, plcy); result.require("ticket was chosen and produced a session (1)", session1.has_value()); - result.test_is_eq("chosen offset", session1->second, uint16_t(0)); + result.test_u16_eq("chosen offset", session1->second, uint16_t(0)); // choose from a list of tickets that contains only handles[1] auto session2 = mgr.choose_from_offered_tickets(std::vector{ticket(handles[1].id().value())}, "SHA-256", cbs, plcy); result.require("ticket was chosen and produced a session (2)", session2.has_value()); - result.test_is_eq("chosen offset", session2->second, uint16_t(0)); + result.test_u16_eq("chosen offset", session2->second, uint16_t(0)); // 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(*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)); + result.test_u16_eq("chosen second offset", session3->second, uint16_t(1)); }), CHECK("choose ticket by ticket", @@ -475,19 +489,19 @@ auto session1 = mgr.choose_from_offered_tickets( std::vector{ticket(handles[0].ticket().value())}, "SHA-256", cbs, plcy); result.require("ticket was chosen and produced a session (1)", session1.has_value()); - result.test_is_eq("chosen offset", session1->second, uint16_t(0)); + result.test_u16_eq("chosen offset", session1->second, uint16_t(0)); // choose from a list of tickets that contains only handles[1] auto session2 = mgr.choose_from_offered_tickets( std::vector{ticket(handles[1].ticket().value())}, "SHA-256", cbs, plcy); result.require("ticket was chosen and produced a session (2)", session2.has_value()); - result.test_is_eq("chosen offset", session2->second, uint16_t(0)); + result.test_u16_eq("chosen offset", session2->second, uint16_t(0)); // 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(*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)); + result.test_u16_eq("chosen second offset", session3->second, uint16_t(1)); }), CHECK("choose ticket based on requested hash function", @@ -506,7 +520,7 @@ 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)); + result.test_u16_eq("chosen second offset", session->second, uint16_t(2)); }), CHECK("choose ticket based on protocol version", @@ -527,7 +541,7 @@ 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)); + result.test_u16_eq("chosen second offset (TLS 1.3 ticket)", session->second, uint16_t(2)); }), }; #else @@ -548,26 +562,26 @@ return { CHECK("establish with default parameters", [&](auto& result) { - result.confirm("will emit tickets", mgr.emits_session_tickets()); + result.test_is_true("will emit tickets", mgr.emits_session_tickets()); auto ticket = mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs)); - result.confirm("returned ticket", ticket.has_value() && ticket->is_ticket()); + result.test_is_true("returned ticket", ticket.has_value() && ticket->is_ticket()); }), CHECK("establish with disabled tickets", [&](auto& result) { - result.confirm("will emit tickets", mgr.emits_session_tickets()); + result.test_is_true("will emit tickets", mgr.emits_session_tickets()); auto ticket = mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs), std::nullopt, true); - result.confirm("returned std::nullopt", !ticket.has_value()); + result.test_is_true("returned std::nullopt", !ticket.has_value()); }), 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()); + result.test_is_true("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.test_is_true("returned std::nullopt", !ticket.has_value()); }), CHECK("retrieve via ticket", @@ -577,9 +591,9 @@ result.require("tickets created successfully", ticket1.has_value() && ticket2.has_value()); 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", - local_mgr.retrieve(ticket2.value(), cbs, plcy).has_value()); + result.test_is_true("can retrieve ticket 1", mgr.retrieve(ticket1.value(), cbs, plcy).has_value()); + result.test_is_true("can retrieve ticket 2 from different manager but sam credentials", + local_mgr.retrieve(ticket2.value(), cbs, plcy).has_value()); }), CHECK("retrieve via ID does not work", @@ -587,7 +601,8 @@ 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("retrieval by ID does not work", !mgr.retrieve(random_id(*rng), cbs, plcy).has_value()); + result.test_is_true("retrieval by ID does not work", + !mgr.retrieve(random_id(*rng), cbs, plcy).has_value()); }), CHECK("retrieve via opaque handle does work", @@ -595,8 +610,8 @@ 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("retrieval by opaque handle", - mgr.retrieve(ticket1->opaque_handle(), cbs, plcy).has_value()); + result.test_is_true("retrieval by opaque handle", + mgr.retrieve(ticket1->opaque_handle(), cbs, plcy).has_value()); }), CHECK("no retrieve without or with wrong ticket key", @@ -609,11 +624,11 @@ 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.test_is_true("no successful retrieval (without key)", + !local_mgr1.retrieve(ticket1.value(), cbs, plcy).has_value()); + result.test_is_true("no successful retrieval (with wrong key)", + !local_mgr2.retrieve(ticket1.value(), cbs, plcy).has_value()); + result.test_is_true("successful retrieval", mgr.retrieve(ticket1.value(), cbs, plcy).has_value()); }), CHECK("Clients cannot be stateless", @@ -630,7 +645,7 @@ 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()); + result.test_is_true("finding tickets does not work", mgr.find(server_info(), cbs, plcy).empty()); }), CHECK("remove is a NOOP", @@ -638,11 +653,11 @@ auto ticket1 = mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs)); result.require("tickets created successfully", ticket1.has_value() && ticket1.has_value()); - result.test_is_eq("remove the ticket", mgr.remove(ticket1.value()), size_t(0)); - result.confirm("successful retrieval 1", mgr.retrieve(ticket1.value(), cbs, plcy).has_value()); + result.test_sz_eq("remove the ticket", mgr.remove(ticket1.value()), 0); + result.test_is_true("successful retrieval 1", mgr.retrieve(ticket1.value(), cbs, plcy).has_value()); - result.test_is_eq("remove the ticket", mgr.remove_all(), size_t(0)); - result.confirm("successful retrieval 1", mgr.retrieve(ticket1.value(), cbs, plcy).has_value()); + result.test_sz_eq("remove the ticket", mgr.remove_all(), 0); + result.test_is_true("successful retrieval 1", mgr.retrieve(ticket1.value(), cbs, plcy).has_value()); }), CHECK( @@ -654,7 +669,7 @@ auto session_after = mgr.retrieve(ticket.value(), cbs, plcy); result.require("got the session back", session_after.has_value()); - result.test_is_eq( + result.test_u64_eq( "timestamps match", std::chrono::duration_cast(session_before.start_time().time_since_epoch()).count(), std::chrono::duration_cast(session_after->start_time().time_since_epoch()) @@ -675,7 +690,7 @@ // transparently constructs a hybrid manager with the respective internal // stateful manager. auto CHECK_all = [&](const std::string& name, auto lambda) -> std::vector { - std::vector()>>> + const std::vector()>>> stateful_manager_factories = { {"In Memory", [&rng]() -> std::unique_ptr { @@ -692,15 +707,15 @@ std::vector results; using namespace std::placeholders; - for(auto& factory_and_name : stateful_manager_factories) { - auto& stateful_manager_name = factory_and_name.first; - auto& stateful_manager_factory = factory_and_name.second; + for(const auto& factory_and_name : stateful_manager_factories) { + const auto& stateful_manager_name = factory_and_name.first; + const auto& stateful_manager_factory = factory_and_name.second; 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); - auto fn = std::bind(lambda, make_manager, _1); + auto fn = std::bind(lambda, make_manager, _1); // NOLINT(*-avoid-bind) results.push_back(CHECK(nm.c_str(), fn)); } return results; @@ -712,44 +727,44 @@ auto mgr_prefers_tickets = make_manager(true); auto ticket1 = mgr_prefers_tickets.establish(default_session(Botan::TLS::Connection_Side::Server, cbs)); - result.confirm("emits a ticket", ticket1.has_value() && ticket1->is_ticket()); + result.test_is_true("emits a ticket", ticket1.has_value() && ticket1->is_ticket()); auto mgr_prefers_ids = make_manager(false); auto ticket2 = mgr_prefers_ids.establish(default_session(Botan::TLS::Connection_Side::Server, cbs)); - result.confirm("emits an ID", ticket2.has_value() && ticket2->is_id()); + result.test_is_true("emits an ID", ticket2.has_value() && ticket2->is_id()); auto ticket3 = mgr_prefers_ids.establish(default_session(Botan::TLS::Connection_Side::Server, cbs), std::nullopt, true /* TLS 1.2 no ticket support */); - result.confirm("emits an ID", ticket3.has_value() && ticket3->is_id()); + result.test_is_true("emits an ID", ticket3.has_value() && ticket3->is_id()); auto ticket4 = mgr_prefers_ids.establish(default_session(Botan::TLS::Connection_Side::Server, cbs), std::nullopt, true /* TLS 1.2 no ticket support */); - result.confirm("emits an ID", ticket4.has_value() && ticket4->is_id()); + result.test_is_true("emits an ID", ticket4.has_value() && ticket4->is_id()); }), - CHECK_all("ticket vs ID preference in retrieval", - [&](const auto& make_manager, auto& result) { - auto mgr_prefers_tickets = make_manager(true); - auto mgr_prefers_ids = make_manager(false); - - auto id1 = mgr_prefers_tickets.underlying_stateful_manager()->establish( - default_session(Botan::TLS::Connection_Side::Server, cbs)); - auto id2 = mgr_prefers_ids.underlying_stateful_manager()->establish( - default_session(Botan::TLS::Connection_Side::Server, cbs)); - auto ticket = - mgr_prefers_tickets.establish(default_session(Botan::TLS::Connection_Side::Server, cbs)); - - result.require("establishments worked", id1.has_value() && id2.has_value() && ticket.has_value()); - - result.confirm("mgr1 + ID1", mgr_prefers_tickets.retrieve(id1.value(), cbs, plcy).has_value()); - result.confirm("mgr1 + ID2", !mgr_prefers_tickets.retrieve(id2.value(), cbs, plcy).has_value()); - result.confirm("mgr2 + ID1", !mgr_prefers_ids.retrieve(id1.value(), cbs, plcy).has_value()); - result.confirm("mgr2 + ID2", mgr_prefers_ids.retrieve(id2.value(), cbs, plcy).has_value()); - result.confirm("mgr1 + ticket", mgr_prefers_tickets.retrieve(ticket.value(), cbs, plcy).has_value()); - result.confirm("mgr2 + ticket", mgr_prefers_ids.retrieve(ticket.value(), cbs, plcy).has_value()); - }), + CHECK_all( + "ticket vs ID preference in retrieval", + [&](const auto& make_manager, auto& result) { + auto mgr_prefers_tickets = make_manager(true); + auto mgr_prefers_ids = make_manager(false); + + auto id1 = mgr_prefers_tickets.underlying_stateful_manager()->establish( + default_session(Botan::TLS::Connection_Side::Server, cbs)); + auto id2 = mgr_prefers_ids.underlying_stateful_manager()->establish( + default_session(Botan::TLS::Connection_Side::Server, cbs)); + auto ticket = mgr_prefers_tickets.establish(default_session(Botan::TLS::Connection_Side::Server, cbs)); + + result.require("establishments worked", id1.has_value() && id2.has_value() && ticket.has_value()); + + result.test_is_true("mgr1 + ID1", mgr_prefers_tickets.retrieve(id1.value(), cbs, plcy).has_value()); + result.test_is_true("mgr1 + ID2", !mgr_prefers_tickets.retrieve(id2.value(), cbs, plcy).has_value()); + result.test_is_true("mgr2 + ID1", !mgr_prefers_ids.retrieve(id1.value(), cbs, plcy).has_value()); + result.test_is_true("mgr2 + ID2", mgr_prefers_ids.retrieve(id2.value(), cbs, plcy).has_value()); + result.test_is_true("mgr1 + ticket", mgr_prefers_tickets.retrieve(ticket.value(), cbs, plcy).has_value()); + result.test_is_true("mgr2 + ticket", mgr_prefers_ids.retrieve(ticket.value(), cbs, plcy).has_value()); + }), CHECK_all("no session tickets if hybrid manager cannot create them", [&](const auto& make_manager, auto& result) { @@ -760,21 +775,20 @@ auto mgr_prefers_tickets = make_manager(true); auto mgr_prefers_ids = make_manager(false); - result.confirm("does not emit tickets", !empty_mgr.emits_session_tickets()); - result.confirm("does emit tickets 1", mgr_prefers_tickets.emits_session_tickets()); - result.confirm("does emit tickets 2", mgr_prefers_ids.emits_session_tickets()); + result.test_is_true("does not emit tickets", !empty_mgr.emits_session_tickets()); + result.test_is_true("does emit tickets 1", mgr_prefers_tickets.emits_session_tickets()); + result.test_is_true("does emit tickets 2", mgr_prefers_ids.emits_session_tickets()); }), }); } -namespace { - class Temporary_Database_File { private: std::string m_temp_file; public: - Temporary_Database_File(const std::string& db_file) : m_temp_file(Test::data_file_as_temporary_copy(db_file)) { + explicit Temporary_Database_File(const std::string& db_file) : + m_temp_file(Test::data_file_as_temporary_copy(db_file)) { if(m_temp_file.empty()) { throw Test_Error("Failed to create temporary database file"); } @@ -796,8 +810,6 @@ Temporary_Database_File& operator=(Temporary_Database_File&&) = delete; }; -} // namespace - std::vector test_session_manager_sqlite() { #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER) auto rng = Test::new_shared_rng(__func__); @@ -805,42 +817,45 @@ Session_Manager_Policy plcy; return { - CHECK("migrate session database scheme (purges database)", - [&](auto& result) { - Temporary_Database_File dbfile("tls-sessions/botan-2.19.3.sqlite"); + CHECK( + "migrate session database scheme (purges database)", + [&](auto& result) { + const Temporary_Database_File dbfile("tls-sessions/botan-2.19.3.sqlite"); - // legacy database (encrypted with 'thetruthisoutthere') containing: - // $ 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", rng, dbfile.get()); - - result.confirm("Session_ID for randombit.net is gone", - !legacy_db - .retrieve(Botan::TLS::Session_ID(Botan::hex_decode( - "63C137030387E4A6CDAD303CCB1F53884944FDE5B4EDD91E6FCF74DCB033DCEB")), - cbs, - plcy) - .has_value()); - result.confirm("Session_ID for cloudflare.com is gone", - !legacy_db - .retrieve(Botan::TLS::Session_ID(Botan::hex_decode( - "63C136FAD49F05A184F910FD6568A3884164216C11E41CEBFDCD149AF66C1714")), - cbs, - plcy) - .has_value()); - result.confirm("no more session for randombit.net", - legacy_db.find(Botan::TLS::Server_Information("randombit.net", 443), cbs, plcy).empty()); - result.confirm("no more session for cloudflare.com", - legacy_db.find(Botan::TLS::Server_Information("cloudflare.com", 443), cbs, plcy).empty()); + // legacy database (encrypted with 'thetruthisoutthere') containing: + // $ 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", rng, dbfile.get()); + + result.test_is_true("Session_ID for randombit.net is gone", + !legacy_db + .retrieve(Botan::TLS::Session_ID(Botan::hex_decode( + "63C137030387E4A6CDAD303CCB1F53884944FDE5B4EDD91E6FCF74DCB033DCEB")), + cbs, + plcy) + .has_value()); + result.test_is_true("Session_ID for cloudflare.com is gone", + !legacy_db + .retrieve(Botan::TLS::Session_ID(Botan::hex_decode( + "63C136FAD49F05A184F910FD6568A3884164216C11E41CEBFDCD149AF66C1714")), + cbs, + plcy) + .has_value()); + result.test_is_true( + "no more session for randombit.net", + legacy_db.find(Botan::TLS::Server_Information("randombit.net", 443), cbs, plcy).empty()); + result.test_is_true( + "no more session for cloudflare.com", + legacy_db.find(Botan::TLS::Server_Information("cloudflare.com", 443), cbs, plcy).empty()); - result.test_is_eq("empty database won't get more empty", legacy_db.remove_all(), size_t(0)); - }), + result.test_sz_eq("empty database won't get more empty", legacy_db.remove_all(), 0); + }), CHECK("clearing empty database", [&](auto& result) { Botan::TLS::Session_Manager_SQLite mgr("thetruthisoutthere", rng, Test::temp_file_name("empty.sqlite")); - result.test_eq("does not delete anything", mgr.remove_all(), 0); + result.test_sz_eq("does not delete anything", mgr.remove_all(), 0); }), CHECK("establish new session", @@ -852,7 +867,7 @@ mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs), some_random_id); result.require("establishment was successful", some_random_handle.has_value()); result.require("session id was set", some_random_handle->id().has_value()); - result.test_is_eq("session id is correct", some_random_handle->id().value(), some_random_id); + result.test_bin_eq("session id is correct", some_random_handle->id().value(), some_random_id); auto some_virtual_handle = mgr.establish(default_session(Botan::TLS::Connection_Side::Server, cbs)); result.require("establishment was successful", some_virtual_handle.has_value()); @@ -872,23 +887,19 @@ some_random_handle->is_id() && some_virtual_handle->is_id()); auto session1 = mgr.retrieve(some_random_handle.value(), cbs, plcy); - if(result.confirm("found session by user-provided ID", session1.has_value())) { - result.test_is_eq("protocol version was echoed", - session1->version(), - Botan::TLS::Protocol_Version(Botan::TLS::Version_Code::TLS_V12)); - result.test_is_eq("ciphersuite was echoed", session1->ciphersuite_code(), uint16_t(0x009C)); + if(result.test_is_true("found session by user-provided ID", session1.has_value())) { + result.test_u16_eq("protocol version was echoed", session1->version().version_code(), 0x0303); + result.test_u16_eq("ciphersuite was echoed", session1->ciphersuite_code(), uint16_t(0x009C)); } auto session2 = mgr.retrieve(some_virtual_handle.value(), cbs, plcy); - if(result.confirm("found session by manager-generated ID", session2.has_value())) { - result.test_is_eq("protocol version was echoed", - session2->version(), - Botan::TLS::Protocol_Version(Botan::TLS::Version_Code::TLS_V12)); - result.test_is_eq("ciphersuite was echoed", session2->ciphersuite_code(), uint16_t(0x009C)); + if(result.test_is_true("found session by manager-generated ID", session2.has_value())) { + result.test_u16_eq("protocol version was echoed", session2->version().version_code(), 0x0303); + result.test_u16_eq("ciphersuite was echoed", session2->ciphersuite_code(), uint16_t(0x009C)); } auto session3 = mgr.retrieve(random_id(*rng), cbs, plcy); - result.confirm("random ID creates empty result", !session3.has_value()); + result.test_is_true("random ID creates empty result", !session3.has_value()); }), CHECK("retrieval via ticket creates empty result", @@ -899,8 +910,8 @@ 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(*rng), cbs, plcy).has_value()); + result.test_is_true("std::nullopt on random ticket", + !mgr.retrieve(random_ticket(*rng), cbs, plcy).has_value()); }), CHECK("storing sessions and finding them by server info", @@ -912,11 +923,11 @@ 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))) { + auto found_sessions = mgr.find(server_info(), cbs, plcy); + if(result.test_sz_eq("found both sessions", found_sessions.size(), 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.test_is_true("ID matches", !handle.is_id() || handle.id().value() == id); + result.test_is_true("ticket matches", !handle.is_ticket() || handle.ticket().value() == ticket); } } }), @@ -931,18 +942,19 @@ 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)); + result.test_sz_eq("deletes one session by ID", mgr.remove(id), 1); + result.test_sz_eq("deletes one session by ticket", mgr.remove(ticket), 1); - auto found_sessions = mgr.find(server_info, cbs, plcy); - if(result.test_is_eq("found some other sessions", found_sessions.size(), size_t(2))) { + auto found_sessions = mgr.find(server_info(), cbs, plcy); + if(result.test_sz_eq("found some other sessions", found_sessions.size(), 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); + result.test_is_true("ID does not match", !handle.is_id() || handle.id().value() != id); + result.test_is_true("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)); + result.test_sz_eq("removing the rest of the sessions", mgr.remove_all(), 2); }), CHECK("old sessions are purged when needed", @@ -968,7 +980,7 @@ 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_sz_eq("only one entry exists", mgr.remove_all(), 1); }), CHECK("session purging can be disabled", @@ -980,7 +992,7 @@ 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)); + result.test_sz_eq("no entries were purged along the way", mgr.remove_all(), 25); }), }; #else @@ -994,7 +1006,7 @@ Session_Manager_Policy plcy; auto CHECK_all = [&](const std::string& name, auto lambda) -> std::vector { - std::vector()>>> + const std::vector()>>> stateful_manager_factories = { {"In Memory", [&rng]() -> std::unique_ptr { @@ -1017,9 +1029,9 @@ std::vector results; results.reserve(stateful_manager_factories.size()); using namespace std::placeholders; - for(auto& [sub_name, factory] : stateful_manager_factories) { + for(const auto& [sub_name, factory] : stateful_manager_factories) { auto nm = Botan::fmt("{} ({})", name, sub_name); - auto fn = std::bind(lambda, sub_name, factory, _1); + auto fn = std::bind(lambda, sub_name, factory, _1); // NOLINT(*-avoid-bind) results.push_back(CHECK(nm.c_str(), fn)); } return results; @@ -1034,8 +1046,8 @@ result.require("saved successfully", handle.has_value()); result.require("session was found", mgr->retrieve(handle.value(), cbs, plcy).has_value()); cbs.tick(); - result.confirm("session has expired", !mgr->retrieve(handle.value(), cbs, plcy).has_value()); - result.test_is_eq("session was deleted when it expired", mgr->remove_all(), size_t(0)); + result.test_is_true("session has expired", !mgr->retrieve(handle.value(), cbs, plcy).has_value()); + result.test_sz_eq("session was deleted when it expired", mgr->remove_all(), 0); }), CHECK_all("expired sessions are not found", @@ -1055,13 +1067,13 @@ 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()); - auto sessions_and_handles = mgr->find(server_info, cbs, plcy); + auto sessions_and_handles = mgr->find(server_info(), cbs, plcy); result.require("sessions are found", !sessions_and_handles.empty()); - result.test_is_eq("exactly one session is found", sessions_and_handles.size(), size_t(1)); - result.test_is_eq( + result.test_sz_eq("exactly one session is found", sessions_and_handles.size(), 1); + result.test_bin_eq( "the new session is found", sessions_and_handles.front().handle.id().value(), handle_new); - result.test_is_eq("old session was deleted when it expired", mgr->remove_all(), size_t(1)); + result.test_sz_eq("old session was deleted when it expired", mgr->remove_all(), 1); }), CHECK_all( @@ -1091,16 +1103,16 @@ plcy.set_allow_session_reuse(false); - auto sessions_and_handles1 = mgr->find(server_info, cbs, plcy); + auto sessions_and_handles1 = mgr->find(server_info(), cbs, plcy); result.require("all sessions are found", sessions_and_handles1.size() > 1); - auto sessions_and_handles2 = mgr->find(server_info, cbs, plcy); - result.test_is_eq("only one session is found", sessions_and_handles2.size(), size_t(1)); - result.confirm("found session is the Session_ID", sessions_and_handles2.front().handle.is_id()); - result.test_is_eq( + auto sessions_and_handles2 = mgr->find(server_info(), cbs, plcy); + result.test_sz_eq("only one session is found", sessions_and_handles2.size(), 1); + result.test_is_true("found session is the Session_ID", sessions_and_handles2.front().handle.is_id()); + result.test_bin_eq( "found session is the Session_ID", sessions_and_handles2.front().handle.id().value(), handle_1); - result.confirm("found session is TLS 1.2", - sessions_and_handles2.front().session.version().is_pre_tls_13()); + result.test_is_true("found session is TLS 1.2", + sessions_and_handles2.front().session.version().is_pre_tls_13()); }), CHECK_all("number of found tickets is capped", @@ -1120,17 +1132,17 @@ plcy.set_allow_session_reuse(true); plcy.set_session_limit(1); - result.test_is_eq("find one", - mgr->find(server_info, cbs, plcy).size(), + result.test_sz_eq("find one", + mgr->find(server_info(), cbs, plcy).size(), plcy.maximum_session_tickets_per_client_hello()); plcy.set_session_limit(3); - result.test_is_eq("find three", - mgr->find(server_info, cbs, plcy).size(), + result.test_sz_eq("find three", + mgr->find(server_info(), cbs, plcy).size(), plcy.maximum_session_tickets_per_client_hello()); plcy.set_session_limit(10); - result.test_is_eq("find all five", mgr->find(server_info, cbs, plcy).size(), size_t(5)); + result.test_sz_eq("find all five", mgr->find(server_info(), cbs, plcy).size(), 5); }), #if defined(BOTAN_HAS_TLS_13) @@ -1152,7 +1164,7 @@ auto session_and_index = mgr->choose_from_offered_tickets( std::vector{ticket(old_handle.value()), ticket(new_handle.value())}, "SHA-256", cbs, plcy); result.require("a ticket was chosen", session_and_index.has_value()); - result.test_is_eq("the new ticket was chosen", session_and_index->second, uint16_t(1)); + result.test_u16_eq("the new ticket was chosen", session_and_index->second, uint16_t(1)); cbs.tick(); @@ -1164,8 +1176,6 @@ }); } -} // namespace - BOTAN_REGISTER_TEST_FN("tls", "tls_session_manager", test_session_manager_in_memory, @@ -1175,6 +1185,8 @@ test_session_manager_sqlite, tls_session_manager_expiry); +} // namespace + } // namespace Botan_Tests #endif // BOTAN_HAS_TLS diff -Nru botan3-3.7.1+dfsg/src/tests/test_tls_signature_scheme.cpp botan3-3.12.0+dfsg/src/tests/test_tls_signature_scheme.cpp --- botan3-3.7.1+dfsg/src/tests/test_tls_signature_scheme.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_tls_signature_scheme.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,10 +8,10 @@ #include "tests.h" #if defined(BOTAN_HAS_TLS) - #include +#endif - #include +#if defined(BOTAN_HAS_TLS) namespace Botan_Tests { @@ -24,33 +24,33 @@ for(const auto& s : Botan::TLS::Signature_Scheme::all_available_schemes()) { results.push_back(CHECK(s.to_string().c_str(), [&](auto& result) { - result.confirm("is_set handles all cases", s.is_set()); - result.confirm("is_available handles all cases", s.is_available()); + result.test_is_true("is_set handles all cases", s.is_set()); + result.test_is_true("is_available handles all cases", s.is_available()); - result.confirm("to_string handles all cases", not_unknown(s.to_string())); - result.confirm("hash_function_name handles all cases", not_unknown(s.hash_function_name())); - result.confirm("padding_string handles all cases", not_unknown(s.padding_string())); - result.confirm("algorithm_name handles all cases", not_unknown(s.algorithm_name())); - - result.confirm("format handles all cases", s.format().has_value()); - result.confirm("algorithm_identifier handles all cases", - Botan::AlgorithmIdentifier() != s.key_algorithm_identifier()); + result.test_is_true("to_string handles all cases", not_unknown(s.to_string())); + result.test_is_true("hash_function_name handles all cases", not_unknown(s.hash_function_name())); + result.test_is_true("padding_string handles all cases", not_unknown(s.padding_string())); + result.test_is_true("algorithm_name handles all cases", not_unknown(s.algorithm_name())); + + result.test_is_true("format handles all cases", s.format().has_value()); + result.test_is_true("algorithm_identifier handles all cases", + Botan::AlgorithmIdentifier() != s.key_algorithm_identifier()); })); } Botan::TLS::Signature_Scheme bogus(0x1337); results.push_back(CHECK("bogus scheme", [&](auto& result) { - result.confirm("is_set still works", bogus.is_set()); - result.confirm("is not available", !bogus.is_available()); + result.test_is_true("is_set still works", bogus.is_set()); + result.test_is_true("is not available", !bogus.is_available()); - result.confirm("to_string deals with bogus schemes", !not_unknown(bogus.to_string())); - result.confirm("hash_function_name deals with bogus schemes", !not_unknown(bogus.hash_function_name())); - result.confirm("padding_string deals with bogus schemes", !not_unknown(bogus.padding_string())); - result.confirm("algorithm_name deals with bogus schemes", !not_unknown(bogus.algorithm_name())); - - result.confirm("format deals with bogus schemes", !bogus.format().has_value()); - result.confirm("algorithm_identifier deals with bogus schemes", - Botan::AlgorithmIdentifier() == bogus.key_algorithm_identifier()); + result.test_is_true("to_string deals with bogus schemes", !not_unknown(bogus.to_string())); + result.test_is_true("hash_function_name deals with bogus schemes", !not_unknown(bogus.hash_function_name())); + result.test_is_true("padding_string deals with bogus schemes", !not_unknown(bogus.padding_string())); + result.test_is_true("algorithm_name deals with bogus schemes", !not_unknown(bogus.algorithm_name())); + + result.test_is_true("format deals with bogus schemes", !bogus.format().has_value()); + result.test_is_true("algorithm_identifier deals with bogus schemes", + Botan::AlgorithmIdentifier() == bogus.key_algorithm_identifier()); })); return results; diff -Nru botan3-3.7.1+dfsg/src/tests/test_tls_stream_integration.cpp botan3-3.12.0+dfsg/src/tests/test_tls_stream_integration.cpp --- botan3-3.7.1+dfsg/src/tests/test_tls_stream_integration.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_tls_stream_integration.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -37,6 +37,9 @@ namespace Botan_Tests { #if defined(BOTAN_TEST_TLS_STREAM_INTEGRATION) + +// NOLINTBEGIN(*-avoid-bind) + namespace { namespace net = boost::asio; @@ -46,8 +49,10 @@ using ssl_stream = Botan::TLS::Stream; using namespace std::placeholders; +// NOLINTBEGIN(cert-err58-cpp) const auto k_timeout = std::chrono::seconds(30); const auto k_endpoints = std::vector{tcp::endpoint{net::ip::make_address("127.0.0.1"), 8082}}; +// NOLINTEND(cert-err58-cpp) constexpr size_t MAX_MSG_LENGTH = 512; @@ -147,7 +152,7 @@ m_stream->lowest_layer().close(); } - throw Timeout_Exception("timeout occured: " + message); + throw Timeout_Exception("timeout occurred: " + message); } }); } @@ -176,21 +181,21 @@ net::system_timer m_timeout_timer; std::function m_on_timeout; - char m_data[MAX_MSG_LENGTH]; + char m_data[MAX_MSG_LENGTH]{}; }; class Result_Wrapper { public: - Result_Wrapper(std::string name) : m_result(std::move(name)) {} + explicit Result_Wrapper(std::string_view name) : m_result(name) {} Test::Result& result() { return m_result; } - void expect_success(const std::string& msg, const error_code& ec) { - error_code success; + void expect_success(std::string_view msg, const error_code& ec) { + const error_code success; expect_ec(msg, success, ec); } - void expect_ec(const std::string& msg, const error_code& expected, const error_code& ec) { + void expect_ec(std::string_view msg, const error_code& expected, const error_code& ec) { if(ec != expected) { m_result.test_failure(msg, "Unexpected error code: " + ec.message() + " expected: " + expected.message()); } else { @@ -198,14 +203,20 @@ } } - void confirm(const std::string& msg, bool condition) { m_result.confirm(msg, condition); } + void test_is_true(std::string_view msg, bool condition) { m_result.test_is_true(msg, condition); } + + void test_str_eq(std::string_view what, std::string_view produced, std::string_view expected) { + m_result.test_str_eq(what, produced, expected); + } - void test_failure(const std::string& msg) { m_result.test_failure(msg); } + void test_failure(std::string_view msg) { m_result.test_failure(msg); } private: Test::Result m_result; }; +// NOLINTBEGIN(cert-err58-cpp) + // Control messages // The messages below can be used by the test clients in order to configure the server's behavior during a test // case. @@ -215,6 +226,8 @@ // Prepare the server for the test case "Shutdown No Response" const std::string PREPARE_SHUTDOWN_NO_RESPONSE_MESSAGE = "SHUTDOWN_NOW"; +// NOLINTEND(cert-err58-cpp) + class Server : public Peer, public std::enable_shared_from_this { public: @@ -361,12 +374,13 @@ }; class Client : public Peer { - static void accept_all(const std::vector&, - const std::vector>&, - const std::vector&, - Botan::Usage_Type, - std::string_view, - const Botan::TLS::Policy&) {} + private: + static void accept_all(const std::vector& /*cert*/, + const std::vector>& /*ocsp*/, + const std::vector& /*trusted*/, + Botan::Usage_Type /*usage*/, + std::string_view /*hostname*/, + const Botan::TLS::Policy& /*policy*/) {} public: Client(const std::shared_ptr& policy, net::io_context& ioc) : Peer(policy, ioc) { @@ -412,7 +426,7 @@ virtual void finishAsynchronousWork() {} - void fail(const std::string& msg) { m_result.test_failure(msg); } + void fail(std::string_view msg) { m_result.test_failure(msg); } void extend_results(std::vector& results) { results.push_back(m_result.result()); @@ -444,7 +458,7 @@ void finishAsynchronousWork() override { m_client_thread.join(); } - void run(const error_code&) { + void run(const error_code& /*err*/) { m_client_thread = std::thread([this] { try { this->run_synchronous_client(); @@ -508,7 +522,7 @@ std::bind(&Client::received_zero_byte, client().get(), _1, _2), std::bind(test_case, shared_from_this(), _1)); result().expect_success("receive_response", ec); - result().confirm("correct message", client()->message() == message); + result().test_str_eq("correct message", client()->message(), message); client()->reset_timeout("shutdown"); yield client()->stream().async_shutdown(std::bind(test_case, shared_from_this(), _1)); @@ -516,7 +530,7 @@ client()->reset_timeout("await close_notify"); yield net::async_read(client()->stream(), client()->buffer(), std::bind(test_case, shared_from_this(), _1)); - result().confirm("received close_notify", client()->stream().shutdown_received()); + result().test_is_true("received close_notify", client()->stream().shutdown_received()); result().expect_ec("closed with EOF", net::error::eof, ec); teardown(); @@ -550,13 +564,13 @@ net::read( client()->stream(), client()->buffer(), std::bind(&Client::received_zero_byte, client().get(), _1, _2), ec); result().expect_success("receive_response", ec); - result().confirm("correct message", client()->message() == message); + result().test_str_eq("correct message", client()->message(), message); client()->stream().shutdown(ec); result().expect_success("shutdown", ec); net::read(client()->stream(), client()->buffer(), ec); - result().confirm("received close_notify", client()->stream().shutdown_received()); + result().test_is_true("received close_notify", client()->stream().shutdown_received()); result().expect_ec("closed with EOF", net::error::eof, ec); teardown(); @@ -595,7 +609,7 @@ result().expect_success("shutdown", ec); client()->close_socket(); - result().confirm("did not receive close_notify", !client()->stream().shutdown_received()); + result().test_is_true("did not receive close_notify", !client()->stream().shutdown_received()); teardown(); } @@ -623,7 +637,7 @@ result().expect_success("shutdown", ec); client()->close_socket(); - result().confirm("did not receive close_notify", !client()->stream().shutdown_received()); + result().test_is_true("did not receive close_notify", !client()->stream().shutdown_received()); teardown(); } @@ -672,7 +686,7 @@ result().expect_success("receive_response", ec); client()->close_socket(); - result().confirm("did not receive close_notify", !client()->stream().shutdown_received()); + result().test_is_true("did not receive close_notify", !client()->stream().shutdown_received()); teardown(); } @@ -698,7 +712,7 @@ server()->expect_short_read(); client()->close_socket(); - result().confirm("did not receive close_notify", !client()->stream().shutdown_received()); + result().test_is_true("did not receive close_notify", !client()->stream().shutdown_received()); teardown(); } @@ -744,7 +758,7 @@ client()->reset_timeout("read close_notify"); yield net::async_read(client()->stream(), client()->buffer(), std::bind(test_case, shared_from_this(), _1)); result().expect_ec("read gives EOF", net::error::eof, ec); - result().confirm("received close_notify", client()->stream().shutdown_received()); + result().test_is_true("received close_notify", client()->stream().shutdown_received()); // close the socket rather than shutting down client()->close_socket(); @@ -778,7 +792,7 @@ net::read(client()->stream(), client()->buffer(), ec); result().expect_ec("read gives EOF", net::error::eof, ec); - result().confirm("received close_notify", client()->stream().shutdown_received()); + result().test_is_true("received close_notify", client()->stream().shutdown_received()); // close the socket rather than shutting down client()->close_socket(); @@ -900,10 +914,16 @@ std::vector get_configurations() { return { + #if defined(BOTAN_HAS_TLS_12) SystemConfiguration("TLS 1.2 only", "allow_tls12=true\nallow_tls13=false", "allow_tls12=true\nallow_tls13=false"), + #endif + #if defined(BOTAN_HAS_TLS_13) SystemConfiguration( "TLS 1.3 only", "allow_tls12=false\nallow_tls13=true", "allow_tls12=false\nallow_tls13=true"), + #endif + + #if defined(BOTAN_HAS_TLS_12) && defined(BOTAN_HAS_TLS_13) SystemConfiguration("TLS 1.x server, TLS 1.2 client", "allow_tls12=true\nallow_tls13=false", "allow_tls12=true\nallow_tls13=true"), @@ -942,6 +962,8 @@ } // namespace +// NOLINTEND(*-avoid-bind) + #endif } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_tls_transcript_hash_13.cpp botan3-3.12.0+dfsg/src/tests/test_tls_transcript_hash_13.cpp --- botan3-3.7.1+dfsg/src/tests/test_tls_transcript_hash_13.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_tls_transcript_hash_13.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -9,10 +9,11 @@ #if defined(BOTAN_HAS_TLS_13) - #include - + #include + #include #include #include + #include using namespace Botan::TLS; @@ -97,11 +98,11 @@ h.update(Botan::hex_decode("baadbeef")); result.test_throws("previous throws invalid state exception", [&] { h.previous(); }); - result.test_eq("c = SHA-256(baadbeef)", h.current(), sha256("baadbeef")); + result.test_bin_eq("c = SHA-256(baadbeef)", h.current(), sha256("baadbeef")); h.update(Botan::hex_decode("600df00d")); - result.test_eq("p = SHA-256(baadbeef)", h.previous(), sha256("baadbeef")); - result.test_eq("c = SHA-256(deadbeef | goodfood)", h.current(), sha256("baadbeef600df00d")); + result.test_bin_eq("p = SHA-256(baadbeef)", h.previous(), sha256("baadbeef")); + result.test_bin_eq("c = SHA-256(deadbeef | goodfood)", h.current(), sha256("baadbeef600df00d")); }), CHECK("update and result retrieval (deferred algorithm specification)", @@ -113,7 +114,7 @@ result.test_throws("previous throws invalid state exception", [&] { h.previous(); }); - result.test_eq("c = SHA-256(baadbeef)", h.current(), sha256("baadbeef")); + result.test_bin_eq("c = SHA-256(baadbeef)", h.current(), sha256("baadbeef")); }), CHECK("update and result retrieval (deferred algorithm specification multiple updates)", @@ -124,7 +125,7 @@ h.update(Botan::hex_decode("600df00d")); h.set_algorithm("SHA-256"); - result.test_eq("c = SHA-256(baadbeef | goodfood)", h.current(), sha256("baadbeef600df00d")); + result.test_bin_eq("c = SHA-256(baadbeef | goodfood)", h.current(), sha256("baadbeef600df00d")); }), CHECK("C-style update interface", @@ -141,7 +142,7 @@ h.update(std::array{0x60, 0x0d}); h.update(food); - result.test_eq("c = SHA-256(baadbeef | goodfood)", h.current(), sha256("baadbeef600df00d")); + result.test_bin_eq("c = SHA-256(baadbeef | goodfood)", h.current(), sha256("baadbeef600df00d")); }), CHECK("cloning creates independent transcript_hash instances", @@ -152,13 +153,13 @@ h1.update(std::array{0x60, 0x0d, 0xf0, 0x0d}); auto h2 = h1.clone(); - result.test_eq("c1 = SHA-256(baadbeef | goodfood)", h1.current(), sha256("baadbeef600df00d")); - result.test_eq("c2 = SHA-256(baadbeef | goodfood)", h2.current(), sha256("baadbeef600df00d")); + result.test_bin_eq("c1 = SHA-256(baadbeef | goodfood)", h1.current(), sha256("baadbeef600df00d")); + result.test_bin_eq("c2 = SHA-256(baadbeef | goodfood)", h2.current(), sha256("baadbeef600df00d")); h1.update(std::array{0xca, 0xfe, 0xd0, 0x0d}); - result.test_eq( + result.test_bin_eq( "c1 = SHA-256(baadbeef | goodfood | cafedude)", h1.current(), sha256("baadbeef600df00dcafed00d")); - result.test_eq("c2 = SHA-256(baadbeef | goodfood)", h2.current(), sha256("baadbeef600df00d")); + result.test_bin_eq("c2 = SHA-256(baadbeef | goodfood)", h2.current(), sha256("baadbeef600df00d")); }), CHECK("recreation after hello retry request", @@ -173,7 +174,7 @@ // RFC 8446 4.4.1 const std::string hash_of_client_hello = Botan::hex_encode(sha256("c0cac01a")); const std::string transcript = "fe000020" + hash_of_client_hello + "c001f00d"; - result.test_eq("transcript hash of hello retry request", h2.current(), sha256(transcript)); + result.test_bin_eq("transcript hash of hello retry request", h2.current(), sha256(transcript)); }), CHECK("truncated transcript hash in client hellos with PSK", @@ -187,8 +188,8 @@ h1.update(psk_client_hello); h1.set_algorithm("SHA-256"); - result.test_eq("truncated hash", h1.truncated(), sha256_truncated_ch); - result.test_eq("current hash", h1.current(), sha256_full_ch); + result.test_bin_eq("truncated hash", h1.truncated(), sha256_truncated_ch); + result.test_bin_eq("current hash", h1.current(), sha256_full_ch); // truncated hash is cleared as soon as new messages are read h1.update(std::array{0xc0, 0xca, 0xc0, 0x1a} /* server hello */); diff -Nru botan3-3.7.1+dfsg/src/tests/test_tpm.cpp botan3-3.12.0+dfsg/src/tests/test_tpm.cpp --- botan3-3.7.1+dfsg/src/tests/test_tpm.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_tpm.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -28,7 +28,7 @@ ctx.reset(new Botan::TPM_Context(pin_cb, nullptr)); result.test_success("Created TPM context"); } catch(Botan::TPM_Error& e) { - result.test_success("Error conecting to TPM, skipping tests"); + result.test_success("Error connecting to TPM, skipping tests"); return {result}; } @@ -38,7 +38,7 @@ Botan::TPM_RNG rng(*ctx); Botan::secure_vector output = rng.random_vec(16); - result.test_ne("TPM RNG output not all zeros", output, std::vector(16)); + result.test_bin_ne("TPM RNG output not all zeros", output, std::vector(16)); Botan::TPM_PrivateKey key(*ctx, 1024, nullptr); result.test_success("Created TPM RSA key"); @@ -46,11 +46,11 @@ std::vector blob = key.export_blob(); // Has to be at least as large as the key - result.test_gte("Blob size is reasonable", blob.size(), 1024 / 8); + result.test_sz_gte("Blob size is reasonable", blob.size(), 1024 / 8); std::vector registered_keys = Botan::TPM_PrivateKey::registered_keys(*ctx); - for(auto url : registered_keys) { + for(const auto& url : registered_keys) { result.test_note("TPM registered key " + url); } @@ -74,19 +74,19 @@ const Botan::UUID empty_uuid; - result.test_eq("Uninitialized UUID not valid", empty_uuid.is_valid(), false); + result.test_is_false("Uninitialized UUID not valid", empty_uuid.is_valid()); const Botan::UUID random_uuid(this->rng()); - result.test_eq("Random UUID is valid", empty_uuid.is_valid(), false); + result.test_is_false("Random UUID is valid", empty_uuid.is_valid()); const Botan::UUID binary_copy(random_uuid.binary_value()); - result.confirm("UUID copied by binary equals original", random_uuid == binary_copy); + result.test_is_true("UUID copied by binary equals original", random_uuid == binary_copy); std::string uuid_str = random_uuid.to_string(); - result.test_eq("UUID string in expected format", uuid_str.size(), 36); + result.test_sz_eq("UUID string in expected format", uuid_str.size(), 36); const Botan::UUID string_copy(random_uuid.to_string()); - result.confirm("UUID copied by string equals original", random_uuid == string_copy); + result.test_is_true("UUID copied by string equals original", random_uuid == string_copy); return {result}; } diff -Nru botan3-3.7.1+dfsg/src/tests/test_tpm2.cpp botan3-3.12.0+dfsg/src/tests/test_tpm2.cpp --- botan3-3.7.1+dfsg/src/tests/test_tpm2.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_tpm2.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,17 +7,19 @@ #include "tests.h" -#include -#include -#include - #if defined(BOTAN_HAS_TPM2) - #include - + #include #include #include #include #include + #include + #include + #include + #include + #include + #include + #include #if defined(BOTAN_HAS_TPM2_RSA_ADAPTER) #include @@ -39,9 +41,10 @@ namespace Botan_Tests { -#if defined(BOTAN_HAS_TPM2) namespace { +#if defined(BOTAN_HAS_TPM2) + #if defined(BOTAN_HAS_TPM2_CRYPTO_BACKEND) && defined(BOTAN_TSS2_SUPPORTS_CRYPTO_CALLBACKS) constexpr bool crypto_backend_should_be_available = true; #else @@ -74,8 +77,8 @@ /// RAII helper to manage raw transient resources (ESYS_TR) handles class TR { private: - ESYS_CONTEXT* m_esys_ctx; - ESYS_TR m_handle; + ESYS_CONTEXT* m_esys_ctx{}; + ESYS_TR m_handle{}; public: TR(ESYS_CONTEXT* esys_ctx, ESYS_TR handle) : m_esys_ctx(esys_ctx), m_handle(handle) {} @@ -94,18 +97,19 @@ TR& operator=(const TR&) = delete; ~TR() { - if(m_esys_ctx && m_handle != ESYS_TR_NONE) { + if(m_esys_ctx != nullptr && m_handle != ESYS_TR_NONE) { Esys_FlushContext(m_esys_ctx, m_handle); } } + // NOLINTNEXTLINE(*-explicit-conversions) FIXME constexpr operator ESYS_TR() const { return m_handle; } }; struct esys_context_liberator { void operator()(ESYS_CONTEXT* esys_ctx) { - TSS2_TCTI_CONTEXT* tcti_ctx = nullptr; - Esys_GetTcti(esys_ctx, &tcti_ctx); // ignore error in destructor + TSS2_TCTI_CONTEXT* tcti_ctx = nullptr; // NOLINT(*-const-correctness) clang-tidy bug + Esys_GetTcti(esys_ctx, &tcti_ctx); // ignore error in destructor if(tcti_ctx != nullptr) { Tss2_TctiLdr_Finalize(&tcti_ctx); } @@ -121,8 +125,8 @@ return nullptr; } - TSS2_RC rc; - TSS2_TCTI_CONTEXT* tcti_ctx; + TSS2_RC rc = 0; + TSS2_TCTI_CONTEXT* tcti_ctx = nullptr; std::unique_ptr esys_ctx; rc = Tss2_TctiLdr_Initialize_Ex(tcti_name->c_str(), tcti_conf->c_str(), &tcti_ctx); @@ -183,34 +187,34 @@ return { CHECK("Vendor and Manufacturer", [&](Test::Result& result) { - result.test_eq("Vendor", ctx->vendor(), "SW TPM"); - result.test_eq("Manufacturer", ctx->manufacturer(), "IBM"); + result.test_str_eq("Vendor", ctx->vendor(), "SW TPM"); + result.test_str_eq("Manufacturer", ctx->manufacturer(), "IBM"); }), CHECK("Max random bytes per request", [&](Test::Result& result) { const auto prop = ctx->max_random_bytes_per_request(); - result.test_gte("at least as long as SHA-256", prop, 32); - result.test_lte("at most as long as SHA-512", prop, 64); + result.test_sz_gte("at least as long as SHA-256", prop, 32); + result.test_sz_lte("at most as long as SHA-512", prop, 64); }), CHECK("Supports basic algorithms", [&](Test::Result& result) { - result.confirm("RSA is supported", ctx->supports_algorithm("RSA")); - result.confirm("AES-128 is supported", ctx->supports_algorithm("AES-128")); - result.confirm("AES-256 is supported", ctx->supports_algorithm("AES-256")); - result.confirm("SHA-1 is supported", ctx->supports_algorithm("SHA-1")); - result.confirm("SHA-256 is supported", ctx->supports_algorithm("SHA-256")); - result.confirm("OFB(AES-128) is supported", ctx->supports_algorithm("OFB(AES-128)")); - result.confirm("OFB is supported", ctx->supports_algorithm("OFB")); + result.test_is_true("RSA is supported", ctx->supports_algorithm("RSA")); + result.test_is_true("AES-128 is supported", ctx->supports_algorithm("AES-128")); + result.test_is_true("AES-256 is supported", ctx->supports_algorithm("AES-256")); + result.test_is_true("SHA-1 is supported", ctx->supports_algorithm("SHA-1")); + result.test_is_true("SHA-256 is supported", ctx->supports_algorithm("SHA-256")); + result.test_is_true("OFB(AES-128) is supported", ctx->supports_algorithm("OFB(AES-128)")); + result.test_is_true("OFB is supported", ctx->supports_algorithm("OFB")); }), CHECK("Unsupported algorithms aren't supported", [&](Test::Result& result) { - result.confirm("Enigma is not supported", !ctx->supports_algorithm("Enigma")); - result.confirm("MD5 is not supported", !ctx->supports_algorithm("MD5")); - result.confirm("DES is not supported", !ctx->supports_algorithm("DES")); - result.confirm("OAEP(Keccak) is not supported", !ctx->supports_algorithm("OAEP(Keccak)")); + result.test_is_true("Enigma is not supported", !ctx->supports_algorithm("Enigma")); + result.test_is_true("MD5 is not supported", !ctx->supports_algorithm("MD5")); + result.test_is_true("DES is not supported", !ctx->supports_algorithm("DES")); + result.test_is_true("OAEP(Keccak) is not supported", !ctx->supports_algorithm("OAEP(Keccak)")); }), }; } @@ -227,11 +231,11 @@ CHECK("Persistent handles", [&](Test::Result& result) { const auto handles = ctx->persistent_handles(); - result.confirm("At least one persistent handle", !handles.empty()); - result.confirm("SRK is in the list", Botan::value_exists(handles, 0x81000001)); - result.confirm("Test private key is in the list", Botan::value_exists(handles, persistent_key_id)); - result.confirm("Test persistence location is not in the list", - !Botan::value_exists(handles, persistent_key_id + 1)); + result.test_is_true("At least one persistent handle", !handles.empty()); + result.test_is_true("SRK is in the list", Botan::value_exists(handles, 0x81000001)); + result.test_is_true("Test private key is in the list", Botan::value_exists(handles, persistent_key_id)); + result.test_is_true("Test persistence location is not in the list", + !Botan::value_exists(handles, persistent_key_id + 1)); }), CHECK("Crypto backend", @@ -246,13 +250,13 @@ if(backend_used) { result.test_throws( "If the backend is already in use, we cannot enable it once more", - [&] { ctx->use_botan_crypto_backend(Test::new_rng(__func__)); }); + [&] { ctx->use_botan_crypto_backend(Test::new_rng("tpm2_backend_test")); }); } if(!backend_supported) { result.test_throws( "If the backend is not supported, we cannot enable it", - [&] { ctx->use_botan_crypto_backend(Test::new_rng(__func__)); }); + [&] { ctx->use_botan_crypto_backend(Test::new_rng("tpm2_backend_test")); }); } }), @@ -261,9 +265,9 @@ CHECK("Fetch Storage Root Key RSA", [&](Test::Result& result) { auto srk = ctx->storage_root_key({}, {}); result.require("SRK is not null", srk != nullptr); - result.test_eq("Algo", srk->algo_name(), "RSA"); - result.test_eq("Key size", srk->key_length(), 2048); - result.confirm("Has persistent handle", srk->handles().has_persistent_handle()); + result.test_str_eq("Algo", srk->algo_name(), "RSA"); + result.test_sz_eq("Key size", srk->key_length(), 2048); + result.test_is_true("Has persistent handle", srk->handles().has_persistent_handle()); }), #endif }; @@ -276,7 +280,7 @@ .keyBits = {.sym = 256}, .mode = {.sym = TPM2_ALG_CFB}, }; - ESYS_TR session; + ESYS_TR session = 0; auto rc = Esys_StartAuthSession(esys_ctx, ESYS_TR_NONE, @@ -322,7 +326,7 @@ auto rng = Botan::TPM2::RandomNumberGenerator(ctx, session); auto bytes = rng.random_vec(16); - result.test_eq("some random bytes generated", bytes.size(), 16); + result.test_sz_eq("some random bytes generated", bytes.size(), 16); // All Botan-wrapped things go out of scope... } @@ -332,7 +336,7 @@ auto [bytes, rc_random] = raw_get_random_bytes(esys_ctx.get(), 16, raw_session); Botan::TPM2::check_rc("random byte generation successful", rc_random); - result.test_eq_sz("some raw random bytes generated", bytes->size, 16); + result.test_sz_eq("some raw random bytes generated", bytes->size, 16); }), CHECK("TPM2::Context-managed crypto backend fails gracefully after TPM2::Context destruction", @@ -350,7 +354,7 @@ return; } - ctx->use_botan_crypto_backend(Test::new_rng(__func__)); + ctx->use_botan_crypto_backend(Test::new_rng("tpm2_backend_context_test")); } auto [session, session_rc1] = raw_start_session(esys_ctx.get()); @@ -370,7 +374,7 @@ auto [bytes, rc_random] = raw_get_random_bytes(esys_ctx.get(), 16, raw_session); Botan::TPM2::check_rc("random byte generation successful", rc_random); - result.test_eq_sz("some raw random bytes generated", bytes->size, 16); + result.test_sz_eq("some raw random bytes generated", bytes->size, 16); #endif }), @@ -387,14 +391,14 @@ return; } - auto cb_state = Botan::TPM2::use_botan_crypto_backend(esys_ctx.get(), Test::new_rng(__func__)); + auto cb_state = Botan::TPM2::use_botan_crypto_backend(esys_ctx.get(), Test::new_rng("tpm2_crypto_backend")); auto [raw_session, session_rc2] = raw_start_session(esys_ctx.get()); Botan::TPM2::check_rc("session creation successful", session_rc2); auto [bytes, rc_random] = raw_get_random_bytes(esys_ctx.get(), 16, raw_session); Botan::TPM2::check_rc("random byte generation successful", rc_random); - result.test_eq_sz("some raw random bytes generated", bytes->size, 16); + result.test_sz_eq("some raw random bytes generated", bytes->size, 16); }), #endif }; @@ -408,8 +412,8 @@ auto ok = [](Test::Result& result, std::string_view name, const std::shared_ptr& session) { result.require(Botan::fmt("Session '{}' is non-null", name), session != nullptr); - result.confirm(Botan::fmt("Session '{}' has a valid handle", name), session->handle() != ESYS_TR_NONE); - result.confirm(Botan::fmt("Session '{}' has a non-empty nonce", name), !session->tpm_nonce().empty()); + result.test_is_true(Botan::fmt("Session '{}' has a valid handle", name), session->handle() != ESYS_TR_NONE); + result.test_is_true(Botan::fmt("Session '{}' has a non-empty nonce", name), !session->tpm_nonce().empty()); }; return { @@ -444,8 +448,8 @@ auto ecc_key = Botan::TPM2::EC_PrivateKey::load_persistent(ctx, persistent_key_id, {}, {}); result.require("EK is not null", ecc_key != nullptr); - result.test_eq("Algo", ecc_key->algo_name(), "ECDSA"); - result.confirm("Has persistent handle", ecc_key->handles().has_persistent_handle()); + result.test_str_eq("Algo", ecc_key->algo_name(), "ECDSA"); + result.test_is_true("Has persistent handle", ecc_key->handles().has_persistent_handle()); ok(result, "default", Session::authenticated_session(ctx, *ecc_key)); ok(result, "CFB(AES-128)", Session::authenticated_session(ctx, *ecc_key, "CFB(AES-128)")); @@ -469,9 +473,9 @@ return { CHECK("Basic functionalities", [&](Test::Result& result) { - result.confirm("Accepts input", rng.accepts_input()); - result.confirm("Is seeded", rng.is_seeded()); - result.test_eq("Right name", rng.name(), "TPM2_RNG"); + result.test_is_true("Accepts input", rng.accepts_input()); + result.test_is_true("Is seeded", rng.is_seeded()); + result.test_str_eq("Right name", rng.name(), "TPM2_RNG"); result.test_no_throw("Clear", [&] { rng.clear(); }); }), @@ -480,30 +484,30 @@ [&](Test::Result& result) { std::array buf1 = {}; rng.randomize(buf1); - result.confirm("Is at least not 0 (8)", not_zero_64(buf1)); + result.test_is_true("Is at least not 0 (8)", not_zero_64(buf1)); std::array buf2 = {}; rng.randomize(buf2); - result.confirm("Is at least not 0 (15)", not_zero_64(buf2)); + result.test_is_true("Is at least not 0 (15)", not_zero_64(buf2)); std::array buf3 = {}; rng.randomize(buf3); - result.confirm("Is at least not 0 (256)", not_zero_64(buf3)); + result.test_is_true("Is at least not 0 (256)", not_zero_64(buf3)); }), CHECK("Randomize with inputs", [&](Test::Result& result) { std::array buf1 = {}; rng.randomize_with_input(buf1, std::array{}); - result.confirm("Randomized with inputs is at least not 0 (9)", not_zero_64(buf1)); + result.test_is_true("Randomized with inputs is at least not 0 (9)", not_zero_64(buf1)); std::array buf2 = {}; rng.randomize_with_input(buf2, std::array{}); - result.confirm("Randomized with inputs is at least not 0 (66)", not_zero_64(buf2)); + result.test_is_true("Randomized with inputs is at least not 0 (66)", not_zero_64(buf2)); std::array buf3 = {}; rng.randomize_with_input(buf3, std::array{}); - result.confirm("Randomized with inputs is at least not 0 (256)", not_zero_64(buf3)); + result.test_is_true("Randomized with inputs is at least not 0 (256)", not_zero_64(buf3)); }), }; } @@ -517,7 +521,7 @@ std::span auth_value, const std::shared_ptr& session) { const auto persistent_handles = ctx->persistent_handles(); - result.confirm( + result.test_is_true( "Persistent key available", std::find(persistent_handles.begin(), persistent_handles.end(), persistent_key_id) != persistent_handles.end()); @@ -529,8 +533,8 @@ } }(); - result.test_eq("Algo", key->algo_name(), "RSA" /* TODO ECC support*/); - result.test_is_eq("Handle", key->handles().persistent_handle(), persistent_key_id); + result.test_str_eq("Algo", key->algo_name(), "RSA" /* TODO ECC support*/); + result.test_u64_eq("Handle", key->handles().persistent_handle(), persistent_key_id); return key; } @@ -543,18 +547,18 @@ auto session = Botan::TPM2::Session::unauthenticated_session(ctx); const auto persistent_key_id = Test::options().tpm2_persistent_rsa_handle(); - const auto password = Test::options().tpm2_persistent_auth_value(); + const auto password = Botan::as_span_of_bytes(Test::options().tpm2_persistent_auth_value()); return { CHECK("RSA and its helpers are supported", [&](Test::Result& result) { - result.confirm("RSA is supported", ctx->supports_algorithm("RSA")); - result.confirm("PKCS1 is supported", ctx->supports_algorithm("PKCS1v15")); - result.confirm("PKCS1 with hash is supported", ctx->supports_algorithm("PKCS1v15(SHA-1)")); - result.confirm("OAEP is supported", ctx->supports_algorithm("OAEP")); - result.confirm("OAEP with hash is supported", ctx->supports_algorithm("OAEP(SHA-256)")); - result.confirm("PSS is supported", ctx->supports_algorithm("PSS")); - result.confirm("PSS with hash is supported", ctx->supports_algorithm("PSS(SHA-256)")); + result.test_is_true("RSA is supported", ctx->supports_algorithm("RSA")); + result.test_is_true("PKCS1 is supported", ctx->supports_algorithm("PKCS1v15")); + result.test_is_true("PKCS1 with hash is supported", ctx->supports_algorithm("PKCS1v15(SHA-1)")); + result.test_is_true("OAEP is supported", ctx->supports_algorithm("OAEP")); + result.test_is_true("OAEP with hash is supported", ctx->supports_algorithm("OAEP(SHA-256)")); + result.test_is_true("PSS is supported", ctx->supports_algorithm("PSS")); + result.test_is_true("PSS with hash is supported", ctx->supports_algorithm("PSS(SHA-256)")); }), CHECK("Load the private key multiple times", @@ -562,7 +566,7 @@ for(size_t i = 0; i < 20; ++i) { auto key = load_persistent(result, ctx, persistent_key_id, password, session); - result.test_eq(Botan::fmt("Key loaded successfully ({})", i), key->algo_name(), "RSA"); + result.test_str_eq(Botan::fmt("Key loaded successfully ({})", i), key->algo_name(), "RSA"); } }), @@ -587,7 +591,7 @@ auto public_key = key->public_key(); Botan::PK_Verifier verifier(*public_key, "PSS(SHA-256)"); - result.confirm("Signature is valid", verifier.verify_message(message, signature)); + result.test_is_true("Signature is valid", verifier.verify_message(message, signature)); }), CHECK("verify signature", @@ -610,12 +614,12 @@ const auto message = Botan::hex_decode("baadcafe"); const auto signature = sign(message); - result.confirm("verification successful", verify(message, signature)); + result.test_is_true("verification successful", verify(message, signature)); // change the message - auto rng = Test::new_rng(__func__); + auto rng = Test::new_rng("tpm2_verify_message"); auto mutated_message = Test::mutate_vec(message, *rng); - result.confirm("verification failed", !verify(mutated_message, signature)); + result.test_is_true("verification failed", !verify(mutated_message, signature)); // ESAPI manipulates the session attributes internally and does // not reset them when an error occurs. A failure to validate a @@ -623,10 +627,10 @@ // leaving the session attributes in an unexpected state. // The Botan wrapper has a workaround for this... const auto attrs = session->attributes(); - result.confirm("encrypt flag was not cleared by ESAPI", attrs.encrypt); + result.test_is_true("encrypt flag was not cleared by ESAPI", attrs.encrypt); - // orignal message again - result.confirm("verification still successful", verify(message, signature)); + // original message again + result.test_is_true("verification still successful", verify(message, signature)); }), CHECK("sign and verify multiple messages with the same Signer/Verifier objects", @@ -655,16 +659,16 @@ auto pk = load_persistent(result, ctx, persistent_key_id, password, session); Botan::PK_Verifier verifier(*pk, "PSS(SHA-256)"); for(size_t i = 0; i < messages.size(); ++i) { - result.confirm(Botan::fmt("verification successful ({})", i), - verifier.verify_message(messages[i], signatures[i])); + result.test_is_true(Botan::fmt("verification successful ({})", i), + verifier.verify_message(messages[i], signatures[i])); } // verify via software auto soft_pk = Botan::RSA_PublicKey(pk->algorithm_identifier(), pk->public_key_bits()); Botan::PK_Verifier soft_verifier(soft_pk, "PSS(SHA-256)"); for(size_t i = 0; i < messages.size(); ++i) { - result.confirm(Botan::fmt("software verification successful ({})", i), - soft_verifier.verify_message(messages[i], signatures[i])); + result.test_is_true(Botan::fmt("software verification successful ({})", i), + soft_verifier.verify_message(messages[i], signatures[i])); } }), @@ -691,13 +695,13 @@ // encrypt a message using the TPM's public key Botan::Null_RNG null_rng; - Botan::PK_Encryptor_EME enc(*pk, null_rng, "OAEP(SHA-256)"); + const Botan::PK_Encryptor_EME enc(*pk, null_rng, "OAEP(SHA-256)"); const auto ciphertext = enc.encrypt(plaintext, null_rng); // decrypt the message using the TPM's private RSA key - Botan::PK_Decryptor_EME dec(*sk, null_rng, "OAEP(SHA-256)"); + const Botan::PK_Decryptor_EME dec(*sk, null_rng, "OAEP(SHA-256)"); const auto decrypted = dec.decrypt(ciphertext); - result.test_eq("decrypted message", decrypted, plaintext); + result.test_bin_eq("decrypted message", decrypted, plaintext); }), CHECK("Decrypt a message", @@ -710,14 +714,14 @@ // encrypt a message using a software RSA key for the TPM's private key auto pk = key->public_key(); auto rng = Test::new_rng("tpm2 rsa decrypt"); - Botan::PK_Encryptor_EME enc(*pk, *rng, "OAEP(SHA-256)"); + const Botan::PK_Encryptor_EME enc(*pk, *rng, "OAEP(SHA-256)"); const auto ciphertext = enc.encrypt(plaintext, *rng); // decrypt the message using the TPM's private key Botan::Null_RNG null_rng; Botan::PK_Decryptor_EME dec(*key, null_rng /* TPM takes care of this */, "OAEP(SHA-256)"); const auto decrypted = dec.decrypt(ciphertext); - result.test_eq("decrypted message", decrypted, plaintext); + result.test_bin_eq("decrypted message", decrypted, plaintext); // corrupt the ciphertext and try to decrypt it auto mutated_ciphertext = Test::mutate_vec(ciphertext, *rng); @@ -738,24 +742,24 @@ const auto plaintext = Botan::hex_decode("feedc0debaadcafe"); // encrypt a message using the TPM's public key - auto rng = Test::new_rng(__func__); - Botan::PK_Encryptor_EME enc(*pk, *rng, "OAEP(SHA-256)"); + auto rng = Test::new_rng("tpm2_transient_key_encrypt"); + const Botan::PK_Encryptor_EME enc(*pk, *rng, "OAEP(SHA-256)"); const auto ciphertext = enc.encrypt(plaintext, *rng); // decrypt the message using the TPM's private RSA key Botan::Null_RNG null_rng; - Botan::PK_Decryptor_EME dec(*sk, null_rng, "OAEP(SHA-256)"); + const Botan::PK_Decryptor_EME dec(*sk, null_rng, "OAEP(SHA-256)"); const auto decrypted = dec.decrypt(ciphertext); - result.test_eq("decrypted message", decrypted, plaintext); + result.test_bin_eq("decrypted message", decrypted, plaintext); // encrypt a message using the TPM's public key (using PKCS#1) - Botan::PK_Encryptor_EME enc_pkcs(*pk, *rng, "PKCS1v15"); + const Botan::PK_Encryptor_EME enc_pkcs(*pk, *rng, "PKCS1v15"); const auto ciphertext_pkcs = enc_pkcs.encrypt(plaintext, *rng); // decrypt the message using the TPM's private RSA key (using PKCS#1) - Botan::PK_Decryptor_EME dec_pkcs(*sk, null_rng, "PKCS1v15"); + const Botan::PK_Decryptor_EME dec_pkcs(*sk, null_rng, "PKCS1v15"); const auto decrypted_pkcs = dec_pkcs.decrypt(ciphertext_pkcs); - result.test_eq("decrypted message", decrypted_pkcs, plaintext); + result.test_bin_eq("decrypted message", decrypted_pkcs, plaintext); }), CHECK("Cannot export private key blob from persistent key", @@ -780,15 +784,15 @@ Botan::TPM2::RSA_PrivateKey::create_unrestricted_transient(ctx, authed_session, secret, *srk, 2048); result.require("key was created", sk != nullptr); - result.confirm("is transient", sk->handles().has_transient_handle()); - result.confirm("is not persistent", !sk->handles().has_persistent_handle()); + result.test_is_true("is transient", sk->handles().has_transient_handle()); + result.test_is_true("is not persistent", !sk->handles().has_persistent_handle()); const auto sk_blob = sk->raw_private_key_bits(); const auto pk_blob = sk->raw_public_key_bits(); const auto pk = sk->public_key(); - result.confirm("secret blob is not empty", !sk_blob.empty()); - result.confirm("public blob is not empty", !pk_blob.empty()); + result.test_is_true("secret blob is not empty", !sk_blob.empty()); + result.test_is_true("public blob is not empty", !pk_blob.empty()); // Perform a round-trip sign/verify test with the new key pair std::vector message = {'h', 'e', 'l', 'l', 'o'}; @@ -798,98 +802,98 @@ result.require("signature is not empty", !signature.empty()); Botan::PK_Verifier verifier(*pk, "PSS(SHA-256)"); - result.confirm("Signature is valid", verifier.verify_message(message, signature)); + result.test_is_true("Signature is valid", verifier.verify_message(message, signature)); // Destruct the key and load it again from the encrypted blob sk.reset(); auto sk_loaded = Botan::TPM2::PrivateKey::load_transient(ctx, secret, *srk, pk_blob, sk_blob, authed_session); result.require("key was loaded", sk_loaded != nullptr); - result.test_eq("loaded key is RSA", sk_loaded->algo_name(), "RSA"); + result.test_str_eq("loaded key is RSA", sk_loaded->algo_name(), "RSA"); const auto sk_blob_loaded = sk_loaded->raw_private_key_bits(); const auto pk_blob_loaded = sk_loaded->raw_public_key_bits(); - result.test_is_eq("secret blob did not change", sk_blob, sk_blob_loaded); - result.test_is_eq("public blob did not change", pk_blob, pk_blob_loaded); + result.test_bin_eq("secret blob did not change", sk_blob, sk_blob_loaded); + result.test_bin_eq("public blob did not change", pk_blob, pk_blob_loaded); // Perform a round-trip sign/verify test with the new key pair std::vector message_loaded = {'g', 'u', 't', 'e', 'n', ' ', 't', 'a', 'g'}; Botan::PK_Signer signer_loaded(*sk_loaded, null_rng /* TPM takes care of this */, "PSS(SHA-256)"); const auto signature_loaded = signer_loaded.sign_message(message_loaded, null_rng); result.require("Next signature is not empty", !signature_loaded.empty()); - result.confirm("Existing verifier can validate signature", - verifier.verify_message(message_loaded, signature_loaded)); + result.test_is_true("Existing verifier can validate signature", + verifier.verify_message(message_loaded, signature_loaded)); // Load the public portion of the key auto pk_loaded = Botan::TPM2::PublicKey::load_transient(ctx, pk_blob, {}); result.require("public key was loaded", pk_loaded != nullptr); Botan::PK_Verifier verifier_loaded(*pk_loaded, "PSS(SHA-256)"); - result.confirm("TPM-verified signature is valid", - verifier_loaded.verify_message(message_loaded, signature_loaded)); + result.test_is_true("TPM-verified signature is valid", + verifier_loaded.verify_message(message_loaded, signature_loaded)); // Perform a round-trip sign/verify test with the new key pair (PKCS#1) std::vector message_pkcs = {'b', 'o', 'n', 'j', 'o', 'u', 'r'}; Botan::PK_Signer signer_pkcs(*sk_loaded, null_rng /* TPM takes care of this */, "PKCS1v15(SHA-256)"); const auto signature_pkcs = signer_pkcs.sign_message(message_pkcs, null_rng); result.require("Next signature is not empty", !signature_pkcs.empty()); - result.confirm("Existing verifier cannot validate signature", - !verifier.verify_message(message_pkcs, signature_pkcs)); + result.test_is_true("Existing verifier cannot validate signature", + !verifier.verify_message(message_pkcs, signature_pkcs)); // Create a verifier for PKCS#1 Botan::PK_Verifier verifier_pkcs(*pk_loaded, "PKCS1v15(SHA-256)"); - result.confirm("TPM-verified signature is valid", - verifier_pkcs.verify_message(message_pkcs, signature_pkcs)); + result.test_is_true("TPM-verified signature is valid", + verifier_pkcs.verify_message(message_pkcs, signature_pkcs)); }), - CHECK("Make a transient key persistent then remove it again", - [&](Test::Result& result) { - auto srk = ctx->storage_root_key({}, {}); - - auto sign_verify_roundtrip = [&](const Botan::TPM2::PrivateKey& key) { - std::vector message = {'h', 'e', 'l', 'l', 'o'}; - Botan::Null_RNG null_rng; - Botan::PK_Signer signer(key, null_rng /* TPM takes care of this */, "PSS(SHA-256)"); - const auto signature = signer.sign_message(message, null_rng); - result.require("signature is not empty", !signature.empty()); - - auto pk = key.public_key(); - Botan::PK_Verifier verifier(*pk, "PSS(SHA-256)"); - result.confirm("Signature is valid", verifier.verify_message(message, signature)); - }; + CHECK( + "Make a transient key persistent then remove it again", + [&](Test::Result& result) { + auto srk = ctx->storage_root_key({}, {}); - // Create Key - auto authed_session = Botan::TPM2::Session::authenticated_session(ctx, *srk); + auto sign_verify_roundtrip = [&](const Botan::TPM2::PrivateKey& key) { + std::vector message = {'h', 'e', 'l', 'l', 'o'}; + Botan::Null_RNG null_rng; + Botan::PK_Signer signer(key, null_rng /* TPM takes care of this */, "PSS(SHA-256)"); + const auto signature = signer.sign_message(message, null_rng); + result.require("signature is not empty", !signature.empty()); - const std::array secret = {'s', 'e', 'c', 'r', 'e', 't'}; - auto sk = - Botan::TPM2::RSA_PrivateKey::create_unrestricted_transient(ctx, authed_session, secret, *srk, 2048); - result.require("key was created", sk != nullptr); - result.confirm("is transient", sk->handles().has_transient_handle()); - result.confirm("is not persistent", !sk->handles().has_persistent_handle()); - result.test_no_throw("use key after creation", [&] { sign_verify_roundtrip(*sk); }); + auto pk = key.public_key(); + Botan::PK_Verifier verifier(*pk, "PSS(SHA-256)"); + result.test_is_true("Signature is valid", verifier.verify_message(message, signature)); + }; - // Make it persistent - const auto handles = ctx->persistent_handles().size(); - const auto new_location = ctx->persist(*sk, authed_session, secret); - result.test_eq("One more handle", ctx->persistent_handles().size(), handles + 1); - result.confirm("New location occupied", Botan::value_exists(ctx->persistent_handles(), new_location)); - result.confirm("is persistent", sk->handles().has_persistent_handle()); - result.test_is_eq( - "Persistent handle is the new handle", sk->handles().persistent_handle(), new_location); - result.test_throws( - "Cannot persist to the same location", [&] { ctx->persist(*sk, authed_session, {}, new_location); }); - result.test_throws("Cannot persist and already persistent key", - [&] { ctx->persist(*sk, authed_session); }); - result.test_no_throw("use key after persisting", [&] { sign_verify_roundtrip(*sk); }); + // Create Key + auto authed_session = Botan::TPM2::Session::authenticated_session(ctx, *srk); - // Evict it - ctx->evict(std::move(sk), authed_session); - result.test_eq("One less handle", ctx->persistent_handles().size(), handles); - result.confirm("New location no longer occupied", - !Botan::value_exists(ctx->persistent_handles(), new_location)); - }), + const std::array secret = {'s', 'e', 'c', 'r', 'e', 't'}; + auto sk = + Botan::TPM2::RSA_PrivateKey::create_unrestricted_transient(ctx, authed_session, secret, *srk, 2048); + result.require("key was created", sk != nullptr); + result.test_is_true("is transient", sk->handles().has_transient_handle()); + result.test_is_true("is not persistent", !sk->handles().has_persistent_handle()); + result.test_no_throw("use key after creation", [&] { sign_verify_roundtrip(*sk); }); + + // Make it persistent + const auto handles = ctx->persistent_handles().size(); + const auto new_location = ctx->persist(*sk, authed_session, secret); + result.test_sz_eq("One more handle", ctx->persistent_handles().size(), handles + 1); + result.test_is_true("New location occupied", Botan::value_exists(ctx->persistent_handles(), new_location)); + result.test_is_true("is persistent", sk->handles().has_persistent_handle()); + result.test_u64_eq("Persistent handle is the new handle", sk->handles().persistent_handle(), new_location); + result.test_throws("Cannot persist to the same location", + [&] { ctx->persist(*sk, authed_session, {}, new_location); }); + result.test_throws("Cannot persist and already persistent key", + [&] { ctx->persist(*sk, authed_session); }); + result.test_no_throw("use key after persisting", [&] { sign_verify_roundtrip(*sk); }); + + // Evict it + ctx->evict(std::move(sk), authed_session); + result.test_sz_eq("One less handle", ctx->persistent_handles().size(), handles); + result.test_is_true("New location no longer occupied", + !Botan::value_exists(ctx->persistent_handles(), new_location)); + }), }; } @@ -904,7 +908,7 @@ const std::shared_ptr& session) { // TODO: Merge with RSA const auto persistent_handles = ctx->persistent_handles(); - result.confirm( + result.test_is_true( "Persistent key available", std::find(persistent_handles.begin(), persistent_handles.end(), persistent_key_id) != persistent_handles.end()); @@ -916,8 +920,8 @@ } }(); - result.test_eq("Algo", key->algo_name(), "ECDSA"); - result.test_is_eq("Handle", key->handles().persistent_handle(), persistent_key_id); + result.test_str_eq("Algo", key->algo_name(), "ECDSA"); + result.test_u64_eq("Handle", key->handles().persistent_handle(), persistent_key_id); return key; } @@ -931,20 +935,20 @@ auto session = Botan::TPM2::Session::unauthenticated_session(ctx); const auto persistent_key_id = Test::options().tpm2_persistent_ecc_handle(); - const auto password = Test::options().tpm2_persistent_auth_value(); + const auto password = Botan::as_span_of_bytes(Test::options().tpm2_persistent_auth_value()); return { CHECK("ECC and its helpers are supported", [&](Test::Result& result) { - result.confirm("ECC is supported", ctx->supports_algorithm("ECC")); - result.confirm("ECDSA is supported", ctx->supports_algorithm("ECDSA")); + result.test_is_true("ECC is supported", ctx->supports_algorithm("ECC")); + result.test_is_true("ECDSA is supported", ctx->supports_algorithm("ECDSA")); }), CHECK("Load the private key multiple times", [&](Test::Result& result) { for(size_t i = 0; i < 20; ++i) { auto key = load_persistent_ecc( result, ctx, persistent_key_id, password, session); - result.test_eq(Botan::fmt("Key loaded successfully ({})", i), key->algo_name(), "ECDSA"); + result.test_str_eq(Botan::fmt("Key loaded successfully ({})", i), key->algo_name(), "ECDSA"); } }), CHECK("Sign a message ECDSA", @@ -968,7 +972,7 @@ auto public_key = key->public_key(); Botan::PK_Verifier verifier(*public_key, "SHA-256"); - result.confirm("Signature is valid", verifier.verify_message(message, signature)); + result.test_is_true("Signature is valid", verifier.verify_message(message, signature)); }), CHECK("verify signature ECDSA", [&](Test::Result& result) { @@ -990,12 +994,12 @@ const auto message = Botan::hex_decode("baadcafe"); const auto signature = sign(message); - result.confirm("verification successful", verify(message, signature)); + result.test_is_true("verification successful", verify(message, signature)); // change the message - auto rng = Test::new_rng(__func__); + auto rng = Test::new_rng("tpm2_verify_ecdsa"); auto mutated_message = Test::mutate_vec(message, *rng); - result.confirm("verification failed", !verify(mutated_message, signature)); + result.test_is_true("verification failed", !verify(mutated_message, signature)); // ESAPI manipulates the session attributes internally and does // not reset them when an error occurs. A failure to validate a @@ -1003,10 +1007,10 @@ // leaving the session attributes in an unexpected state. // The Botan wrapper has a workaround for this... const auto attrs = session->attributes(); - result.confirm("encrypt flag was not cleared by ESAPI", attrs.encrypt); + result.test_is_true("encrypt flag was not cleared by ESAPI", attrs.encrypt); - // orignal message again - result.confirm("verification still successful", verify(message, signature)); + // original message again + result.test_is_true("verification still successful", verify(message, signature)); }), CHECK("sign and verify multiple messages with the same Signer/Verifier objects", @@ -1036,8 +1040,8 @@ load_persistent_ecc(result, ctx, persistent_key_id, password, session); Botan::PK_Verifier verifier(*pk, "SHA-256"); for(size_t i = 0; i < messages.size(); ++i) { - result.confirm(Botan::fmt("verification successful ({})", i), - verifier.verify_message(messages[i], signatures[i])); + result.test_is_true(Botan::fmt("verification successful ({})", i), + verifier.verify_message(messages[i], signatures[i])); } // verify via software @@ -1046,8 +1050,8 @@ ->public_key(); Botan::PK_Verifier soft_verifier(*soft_pk, "SHA-256"); for(size_t i = 0; i < messages.size(); ++i) { - result.confirm(Botan::fmt("software verification successful ({})", i), - soft_verifier.verify_message(messages[i], signatures[i])); + result.test_is_true(Botan::fmt("software verification successful ({})", i), + soft_verifier.verify_message(messages[i], signatures[i])); } }), @@ -1096,7 +1100,7 @@ auto public_key = sk->public_key(); Botan::PK_Verifier verifier(*public_key, "SHA-256"); - result.confirm("Signature is valid", verifier.verify_message(message, signature)); + result.test_is_true("Signature is valid", verifier.verify_message(message, signature)); }), CHECK("Create a new transient ECDSA key", @@ -1113,15 +1117,15 @@ ctx, authed_session, secret, *srk, Botan::EC_Group::from_name("secp384r1")); result.require("key was created", sk != nullptr); - result.confirm("is transient", sk->handles().has_transient_handle()); - result.confirm("is not persistent", !sk->handles().has_persistent_handle()); + result.test_is_true("is transient", sk->handles().has_transient_handle()); + result.test_is_true("is not persistent", !sk->handles().has_persistent_handle()); const auto sk_blob = sk->raw_private_key_bits(); const auto pk_blob = sk->raw_public_key_bits(); const auto pk = sk->public_key(); - result.confirm("secret blob is not empty", !sk_blob.empty()); - result.confirm("public blob is not empty", !pk_blob.empty()); + result.test_is_true("secret blob is not empty", !sk_blob.empty()); + result.test_is_true("public blob is not empty", !pk_blob.empty()); // Perform a round-trip sign/verify test with the new key pair std::vector message = {'h', 'e', 'l', 'l', 'o'}; @@ -1131,36 +1135,36 @@ result.require("signature is not empty", !signature.empty()); Botan::PK_Verifier verifier(*pk, "SHA-256"); - result.confirm("Signature is valid", verifier.verify_message(message, signature)); + result.test_is_true("Signature is valid", verifier.verify_message(message, signature)); // Destruct the key and load it again from the encrypted blob sk.reset(); auto sk_loaded = Botan::TPM2::PrivateKey::load_transient(ctx, secret, *srk, pk_blob, sk_blob, authed_session); result.require("key was loaded", sk_loaded != nullptr); - result.test_eq("loaded key is ECDSA", sk_loaded->algo_name(), "ECDSA"); + result.test_str_eq("loaded key is ECDSA", sk_loaded->algo_name(), "ECDSA"); const auto sk_blob_loaded = sk_loaded->raw_private_key_bits(); const auto pk_blob_loaded = sk_loaded->raw_public_key_bits(); - result.test_is_eq("secret blob did not change", sk_blob, sk_blob_loaded); - result.test_is_eq("public blob did not change", pk_blob, pk_blob_loaded); + result.test_bin_eq("secret blob did not change", sk_blob, sk_blob_loaded); + result.test_bin_eq("public blob did not change", pk_blob, pk_blob_loaded); // Perform a round-trip sign/verify test with the new key pair std::vector message_loaded = {'g', 'u', 't', 'e', 'n', ' ', 't', 'a', 'g'}; Botan::PK_Signer signer_loaded(*sk_loaded, null_rng /* TPM takes care of this */, "SHA-256"); const auto signature_loaded = signer_loaded.sign_message(message_loaded, null_rng); result.require("Next signature is not empty", !signature_loaded.empty()); - result.confirm("Existing verifier can validate signature", - verifier.verify_message(message_loaded, signature_loaded)); + result.test_is_true("Existing verifier can validate signature", + verifier.verify_message(message_loaded, signature_loaded)); // Load the public portion of the key auto pk_loaded = Botan::TPM2::PublicKey::load_transient(ctx, pk_blob, {}); result.require("public key was loaded", pk_loaded != nullptr); Botan::PK_Verifier verifier_loaded(*pk_loaded, "SHA-256"); - result.confirm("TPM-verified signature is valid", - verifier_loaded.verify_message(message_loaded, signature_loaded)); + result.test_is_true("TPM-verified signature is valid", + verifier_loaded.verify_message(message_loaded, signature_loaded)); }), CHECK( @@ -1178,7 +1182,7 @@ auto pk = key.public_key(); Botan::PK_Verifier verifier(*pk, "SHA-256"); - result.confirm("Signature is valid", verifier.verify_message(message, signature)); + result.test_is_true("Signature is valid", verifier.verify_message(message, signature)); }; // Create Key @@ -1188,17 +1192,18 @@ auto sk = Botan::TPM2::EC_PrivateKey::create_unrestricted_transient( ctx, authed_session, secret, *srk, Botan::EC_Group::from_name("secp192r1")); result.require("key was created", sk != nullptr); - result.confirm("is transient", sk->handles().has_transient_handle()); - result.confirm("is not persistent", !sk->handles().has_persistent_handle()); + result.test_is_true("is transient", sk->handles().has_transient_handle()); + result.test_is_true("is not persistent", !sk->handles().has_persistent_handle()); result.test_no_throw("use key after creation", [&] { sign_verify_roundtrip(*sk); }); // Make it persistent const auto handles = ctx->persistent_handles().size(); const auto new_location = ctx->persist(*sk, authed_session, secret); - result.test_eq("One more handle", ctx->persistent_handles().size(), handles + 1); - result.confirm("New location occupied", Botan::value_exists(ctx->persistent_handles(), new_location)); - result.confirm("is persistent", sk->handles().has_persistent_handle()); - result.test_is_eq( + result.test_sz_eq("One more handle", ctx->persistent_handles().size(), handles + 1); + result.test_is_true("New location occupied", + Botan::value_exists(ctx->persistent_handles(), new_location)); + result.test_is_true("is persistent", sk->handles().has_persistent_handle()); + result.test_u64_eq( "Persistent handle is the new handle", sk->handles().persistent_handle(), new_location); result.test_throws( "Cannot persist to the same location", [&] { ctx->persist(*sk, authed_session, {}, new_location); }); @@ -1208,22 +1213,22 @@ // Evict it ctx->evict(std::move(sk), authed_session); - result.test_eq("One less handle", ctx->persistent_handles().size(), handles); - result.confirm("New location no longer occupied", - !Botan::value_exists(ctx->persistent_handles(), new_location)); + result.test_sz_eq("One less handle", ctx->persistent_handles().size(), handles); + result.test_is_true("New location no longer occupied", + !Botan::value_exists(ctx->persistent_handles(), new_location)); }), #endif CHECK("Read a software public key from a TPM serialization", [&](Test::Result& result) { auto pk = load_persistent_ecc(result, ctx, persistent_key_id, password, session); result.test_no_throw("Botan can read serialized ECC public key", [&] { - auto pk_sw = Botan::ECDSA_PublicKey(pk->algorithm_identifier(), pk->public_key_bits()); + std::ignore = Botan::ECDSA_PublicKey(pk->algorithm_identifier(), pk->public_key_bits()); }); auto sk = load_persistent_ecc(result, ctx, persistent_key_id, password, session); result.test_no_throw("Botan can read serialized public key from ECC private key", [&] { - auto sk_sw = Botan::ECDSA_PublicKey(sk->algorithm_identifier(), sk->public_key_bits()); + std::ignore = Botan::ECDSA_PublicKey(sk->algorithm_identifier(), sk->public_key_bits()); }); }), }; @@ -1257,16 +1262,17 @@ return; } - result.test_eq("Name", tpm_hash->name(), soft_hash->name()); - result.test_eq("Output length", tpm_hash->output_length(), soft_hash->output_length()); + result.test_str_eq("Name", tpm_hash->name(), soft_hash->name()); + result.test_sz_eq("Output length", tpm_hash->output_length(), soft_hash->output_length()); // multiple update calls tpm_hash->update("Hello, "); tpm_hash->update("world!"); - result.test_eq("digest (multi-update)", tpm_hash->final(), soft_hash->process("Hello, world!")); + result.test_bin_eq("digest (multi-update)", tpm_hash->final(), soft_hash->process("Hello, world!")); // single process call - result.test_eq("digest (single-process)", tpm_hash->process("Hallo, Welt."), soft_hash->process("Hallo, Welt.")); + result.test_bin_eq( + "digest (single-process)", tpm_hash->process("Hallo, Welt."), soft_hash->process("Hallo, Welt.")); // create a message that is larger than the TPM2 max buffer size const auto long_message = [] { @@ -1278,32 +1284,32 @@ }(); tpm_hash->update(long_message); - result.test_eq("digest (long msg via update)", tpm_hash->final(), soft_hash->process(long_message)); - result.test_eq( + result.test_bin_eq("digest (long msg via update)", tpm_hash->final(), soft_hash->process(long_message)); + result.test_bin_eq( "digest (long msg via process)", tpm_hash->process(long_message), soft_hash->process(long_message)); // test clear tpm_hash->update("Hello"); tpm_hash->clear(); tpm_hash->update("Bonjour"); - result.test_eq("digest (clear)", tpm_hash->final(), soft_hash->process("Bonjour")); + result.test_bin_eq("digest (clear)", tpm_hash->final(), soft_hash->process("Bonjour")); // new_object auto new_tpm_hash = tpm_hash->new_object(); - result.test_eq("Name (new_object)", new_tpm_hash->name(), tpm_hash->name()); - result.test_eq("Output length (new_object)", new_tpm_hash->output_length(), tpm_hash->output_length()); - result.test_eq("digest (new object)", - new_tpm_hash->process("Salut tout le monde!"), - soft_hash->process("Salut tout le monde!")); + result.test_str_eq("Name (new_object)", new_tpm_hash->name(), tpm_hash->name()); + result.test_sz_eq("Output length (new_object)", new_tpm_hash->output_length(), tpm_hash->output_length()); + result.test_bin_eq("digest (new object)", + new_tpm_hash->process("Salut tout le monde!"), + soft_hash->process("Salut tout le monde!")); }; return { CHECK("Hashes are supported", [&](Test::Result& result) { - result.confirm("SHA-1 is supported", ctx->supports_algorithm("SHA-1")); - result.confirm("SHA-256 is supported", ctx->supports_algorithm("SHA-256")); - result.confirm("SHA-384 is supported", ctx->supports_algorithm("SHA-384")); - result.confirm("SHA-512 is supported", ctx->supports_algorithm("SHA-512")); + result.test_is_true("SHA-1 is supported", ctx->supports_algorithm("SHA-1")); + result.test_is_true("SHA-256 is supported", ctx->supports_algorithm("SHA-256")); + result.test_is_true("SHA-384 is supported", ctx->supports_algorithm("SHA-384")); + result.test_is_true("SHA-512 is supported", ctx->supports_algorithm("SHA-512")); }), CHECK("SHA-1", [&](Test::Result& result) { test(result, "SHA-1"); }), @@ -1336,7 +1342,7 @@ const auto [digest_null, ticket_null] = tpm_hash_null.final_with_ticket(); result.require("digest is set", digest_null != nullptr); result.require("ticket is set", ticket_null != nullptr); - result.confirm("ticket is empty", ticket_null->digest.size == 0); + result.test_is_true("ticket is empty", ticket_null->digest.size == 0); // using the OWNER hierarchy (for instance) enables the validation ticket auto tpm_hash_owner = Botan::TPM2::HashFunction( @@ -1345,22 +1351,21 @@ const auto [digest_owner, ticket_owner] = tpm_hash_owner.final_with_ticket(); result.require("digest is set", digest_owner != nullptr); result.require("ticket is set", ticket_owner != nullptr); - result.confirm("ticket is not empty", ticket_owner->digest.size > 0); + result.test_is_true("ticket is not empty", ticket_owner->digest.size > 0); const auto digest_vec = Botan::TPM2::copy_into>(*digest_owner); - result.test_eq("digest", - digest_vec, - Botan::hex_decode("1e479f4d871e59e9054aad62105a259726801d5f494acbfcd40591c82f9b3136")); - - result.test_eq("digests are the same, regardless of ticket", - Botan::TPM2::copy_into>(*digest_null), - digest_vec); + result.test_bin_eq( + "digest", + digest_vec, + Botan::hex_decode("1e479f4d871e59e9054aad62105a259726801d5f494acbfcd40591c82f9b3136")); + + result.test_bin_eq("digests are the same, regardless of ticket", + Botan::TPM2::copy_into>(*digest_null), + digest_vec); }), }; } -} // namespace - BOTAN_REGISTER_TEST_FN("tpm2", "tpm2_props", test_tpm2_properties); BOTAN_REGISTER_TEST_FN("tpm2", "tpm2_ctx", test_tpm2_context); BOTAN_REGISTER_TEST_FN("tpm2", "tpm2_external_ctx", test_external_tpm2_context); @@ -1376,4 +1381,6 @@ #endif +} // namespace + } // namespace Botan_Tests diff -Nru botan3-3.7.1+dfsg/src/tests/test_tss.cpp botan3-3.12.0+dfsg/src/tests/test_tss.cpp --- botan3-3.7.1+dfsg/src/tests/test_tss.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_tss.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -8,6 +8,7 @@ #if defined(BOTAN_HAS_THRESHOLD_SECRET_SHARING) #include "test_rng.h" + #include #include #endif @@ -38,7 +39,7 @@ } auto reconstructed_secret_all = Botan::RTSS_Share::reconstruct(shares); - result.test_eq("Reconstructed secret correctly from all shares", reconstructed_secret_all, input); + result.test_bin_eq("Reconstructed secret correctly from all shares", reconstructed_secret_all, input); if(header == "Invalid") { result.test_failure("Invalid shares should not result in recovery"); @@ -46,11 +47,12 @@ if(N != M) { while(shares.size() > M) { - size_t to_remove = this->rng().next_byte() % shares.size(); + const 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); - result.test_eq("Reconstructed secret correctly from reduced shares", reconstructed_secret, input); + result.test_bin_eq( + "Reconstructed secret correctly from reduced shares", reconstructed_secret, input); } catch(Botan::Decoding_Error&) { result.test_failure("Reconstruction failed with share count " + std::to_string(shares.size())); } @@ -111,23 +113,23 @@ std::vector shares = Botan::RTSS_Share::split(M, N, input.data(), static_cast(input.size()), id, hash, fixed_rng); - result.test_eq("Expected number of shares", shares.size(), N); + result.test_sz_eq("Expected number of shares", shares.size(), N); for(size_t i = 0; i != N; ++i) { - result.test_eq("Expected share", shares[i].data(), expected_shares[i]); + result.test_bin_eq("Expected share", shares[i].data(), expected_shares[i]); } auto reconstructed_secret_all = Botan::RTSS_Share::reconstruct(shares); - result.test_eq("Reconstructed secret correctly from all shares", reconstructed_secret_all, input); + result.test_bin_eq("Reconstructed secret correctly from all shares", reconstructed_secret_all, input); if(N != M) { while(shares.size() > M) { - size_t to_remove = this->rng().next_byte() % shares.size(); + const 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); - result.test_eq("Reconstructed secret correctly from reduced shares", reconstructed_secret, input); + result.test_bin_eq("Reconstructed secret correctly from reduced shares", reconstructed_secret, input); } catch(Botan::Decoding_Error&) { result.test_failure("Reconstruction failed with share count " + std::to_string(shares.size())); } diff -Nru botan3-3.7.1+dfsg/src/tests/test_uri.cpp botan3-3.12.0+dfsg/src/tests/test_uri.cpp --- botan3-3.7.1+dfsg/src/tests/test_uri.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_uri.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,30 +7,37 @@ #include "tests.h" +#include + #if defined(BOTAN_HAS_SOCKETS) && (defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2)) + #include #include namespace Botan_Tests { +namespace { + class URI_Tests final : public Test { private: static Test::Result test_uri_ctor() { Test::Result result("URI constructors"); - Botan::URI uri(Botan::URI::Type::Domain, "localhost", 9000); - result.confirm("type", uri.type() == Botan::URI::Type::Domain); - result.test_eq("host", uri.host(), "localhost"); - result.test_eq("post", size_t(uri.port()), 9000); + const Botan::URI uri(Botan::URI::Type::Domain, "localhost", 9000); + result.test_is_true("type", uri.type() == Botan::URI::Type::Domain); + result.test_str_eq("host", uri.host(), "localhost"); + result.test_sz_eq("post", size_t(uri.port()), 9000); return result; } static Test::Result test_uri_tostring() { Test::Result result("URI to_string"); - result.test_eq("domain", Botan::URI(Botan::URI::Type::Domain, "localhost", 23).to_string(), "localhost:23"); - result.test_eq("IPv4", Botan::URI(Botan::URI::Type::IPv4, "192.168.1.1", 25).to_string(), "192.168.1.1:25"); - result.test_eq("IPv6", Botan::URI(Botan::URI::Type::IPv6, "::1", 65535).to_string(), "[::1]:65535"); - result.test_eq("IPv6 no port", Botan::URI(Botan::URI::Type::IPv6, "::1", 0).to_string(), "::1"); + result.test_str_eq( + "domain", Botan::URI(Botan::URI::Type::Domain, "localhost", 23).to_string(), "localhost:23"); + result.test_str_eq( + "IPv4", Botan::URI(Botan::URI::Type::IPv4, "192.168.1.1", 25).to_string(), "192.168.1.1:25"); + result.test_str_eq("IPv6", Botan::URI(Botan::URI::Type::IPv6, "::1", 65535).to_string(), "[::1]:65535"); + result.test_str_eq("IPv6 no port", Botan::URI(Botan::URI::Type::IPv6, "::1", 0).to_string(), "::1"); return result; } @@ -38,23 +45,25 @@ static Test::Result test_uri_parsing() { Test::Result result("URI parsing"); - struct { + struct URITestCase { std::string uri; std::string host; Botan::URI::Type type; uint16_t port; - } tests[]{ - {"localhost:80", "localhost", Botan::URI::Type::Domain, 80}, - {"www.example.com", "www.example.com", Botan::URI::Type::Domain, 0}, - {"192.168.1.1", "192.168.1.1", Botan::URI::Type::IPv4, 0}, - {"192.168.1.1:34567", "192.168.1.1", Botan::URI::Type::IPv4, 34567}, - {"[::1]:61234", "::1", Botan::URI::Type::IPv6, 61234}, + }; + + const std::vector tests{ + URITestCase{"localhost:80", "localhost", Botan::URI::Type::Domain, 80}, + URITestCase{"www.example.com", "www.example.com", Botan::URI::Type::Domain, 0}, + URITestCase{"192.168.1.1", "192.168.1.1", Botan::URI::Type::IPv4, 0}, + URITestCase{"192.168.1.1:34567", "192.168.1.1", Botan::URI::Type::IPv4, 34567}, + URITestCase{"[::1]:61234", "::1", Botan::URI::Type::IPv6, 61234}, }; for(const auto& t : tests) { auto test_URI = [&result](const Botan::URI& uri, const std::string& host, const uint16_t port) { - result.test_eq("host", uri.host(), host); - result.test_int_eq("port", uri.port(), port); + result.test_str_eq("host", uri.host(), host); + result.test_u16_eq("port", uri.port(), port); }; if(t.type != Botan::URI::Type::IPv4) { @@ -68,7 +77,7 @@ } const auto any = Botan::URI::from_any(t.uri); - result.confirm("from_any type is expected", any.type() == t.type); + result.test_is_true("from_any type is expected", any.type() == t.type); test_URI(any, t.host, t.port); if(t.type == Botan::URI::Type::Domain) { test_URI(Botan::URI::from_domain(t.uri), t.host, t.port); @@ -79,7 +88,7 @@ } } - //since GCC 4.8 does not support regex this would possibly be acceped as valid domains, + //since GCC 4.8 does not support regex this would possibly be accepted as valid domains, //but we just want to test IPv6 parsing, so the test needs to be individual result.test_throws("invalid IPv6", []() { Botan::URI::from_ipv6("]"); }); result.test_throws("invalid IPv6", []() { Botan::URI::from_ipv6("[::1]1"); }); @@ -126,6 +135,8 @@ BOTAN_REGISTER_TEST("utils", "uri", URI_Tests); +} // namespace + } // namespace Botan_Tests #endif diff -Nru botan3-3.7.1+dfsg/src/tests/test_utils.cpp botan3-3.12.0+dfsg/src/tests/test_utils.cpp --- botan3-3.7.1+dfsg/src/tests/test_utils.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_utils.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,21 +7,28 @@ */ #include "tests.h" + +#include "test_arb_eq.h" +#include +#include #include #include #include #include -#include +#include +#include #include #include #include #include #include -#include +#include #include -#include -#include + +#if defined(BOTAN_HAS_CPUID) + #include +#endif #if defined(BOTAN_HAS_POLY_DBL) #include @@ -63,12 +70,12 @@ auto sum1 = Botan::checked_add(i, zero, zero, zero, large); auto sum2 = Botan::checked_add(large, zero, zero, zero, i); - result.confirm("checked_add looks at all args", sum1 == sum2); + result.test_is_true("checked_add looks at all args", sum1 == sum2); if(i < 5) { - result.test_eq("checked_add worked", sum1.value(), i + large); + result.test_sz_eq("checked_add worked", sum1.value(), i + large); } else { - result.confirm("checked_add did not return a result", !sum1.has_value()); + result.test_is_true("checked_add did not return a result", !sum1.has_value()); } } @@ -81,9 +88,9 @@ const uint32_t ref = static_cast(x) + y; if(auto z = Botan::checked_add(x, y)) { - result.test_int_eq("checked_add adds", z.value(), ref); + result.test_u32_eq("checked_add adds", static_cast(z.value()), ref); } else { - result.confirm("checked_add checks", (ref >> 16) > 0); + result.test_is_true("checked_add checks", (ref >> 16) > 0); } } @@ -102,9 +109,9 @@ const uint32_t ref = static_cast(x) * y; if(auto z = Botan::checked_mul(x, y)) { - result.test_int_eq("checked_mul multiplies", z.value(), ref); + result.test_u32_eq("checked_mul multiplies", static_cast(z.value()), ref); } else { - result.confirm("checked_mul checks", (ref >> 16) > 0); + result.test_is_true("checked_mul checks", (ref >> 16) > 0); } } @@ -121,9 +128,9 @@ result.test_throws("checked_cast checks", [&] { Botan::checked_cast_to(large); }); result.test_throws("checked_cast checks", [&] { Botan::checked_cast_to(large); }); - result.test_int_eq("checked_cast converts", Botan::checked_cast_to(large), large); - result.test_int_eq("checked_cast converts", Botan::checked_cast_to(is_16_bits), 0x8123); - result.test_int_eq("checked_cast converts", Botan::checked_cast_to(is_8_bits), 0x89); + result.test_u32_eq("checked_cast converts", Botan::checked_cast_to(large), large); + result.test_u16_eq("checked_cast converts", Botan::checked_cast_to(is_16_bits), 0x8123); + result.test_u8_eq("checked_cast converts", Botan::checked_cast_to(is_8_bits), 0x89); return result; } @@ -143,14 +150,14 @@ }; // clang-format on - for(size_t i : inputs) { - for(size_t m : alignments) { + for(const size_t i : inputs) { + for(const size_t m : alignments) { try { const size_t z = Botan::round_up(i, m); - result.confirm("z % m == 0", z % m == 0); - result.confirm("z >= i", z >= i); - result.confirm("z <= i + m", z <= i + m); + result.test_is_true("z % m == 0", z % m == 0); + result.test_is_true("z >= i", z >= i); + result.test_is_true("z <= i + m", z <= i + m); } catch(Botan::Exception& e) { result.test_failure(Botan::fmt("round_up({},{})", i, m), e.what()); } @@ -186,141 +193,144 @@ const uint32_t in32 = 0xA0B0C0D0; const uint64_t in64 = 0xABCDEF0123456789; - result.test_is_eq(Botan::get_byte<0>(in32), 0xA0); - result.test_is_eq(Botan::get_byte<1>(in32), 0xB0); - result.test_is_eq(Botan::get_byte<2>(in32), 0xC0); - result.test_is_eq(Botan::get_byte<3>(in32), 0xD0); - - result.test_is_eq(Botan::make_uint16(0xAA, 0xBB), 0xAABB); - result.test_is_eq(Botan::make_uint32(0x01, 0x02, 0x03, 0x04), 0x01020304); - - result.test_is_eq(Botan::load_be(mem, 0), 0x0011); - result.test_is_eq(Botan::load_be(mem, 1), 0x2233); - result.test_is_eq(Botan::load_be(mem, 2), 0x4455); - result.test_is_eq(Botan::load_be(mem, 3), 0x6677); - - result.test_is_eq(Botan::load_le(mem, 0), 0x1100); - result.test_is_eq(Botan::load_le(mem, 1), 0x3322); - result.test_is_eq(Botan::load_le(mem, 2), 0x5544); - result.test_is_eq(Botan::load_le(mem, 3), 0x7766); - - result.test_is_eq(Botan::load_be(mem, 0), 0x00112233); - result.test_is_eq(Botan::load_be(mem, 1), 0x44556677); - result.test_is_eq(Botan::load_be(mem, 2), 0x8899AABB); - result.test_is_eq(Botan::load_be(mem, 3), 0xCCDDEEFF); - - result.test_is_eq(Botan::load_le(mem, 0), 0x33221100); - result.test_is_eq(Botan::load_le(mem, 1), 0x77665544); - result.test_is_eq(Botan::load_le(mem, 2), 0xBBAA9988); - result.test_is_eq(Botan::load_le(mem, 3), 0xFFEEDDCC); + result.test_u8_eq(Botan::get_byte<0>(in32), 0xA0); + result.test_u8_eq(Botan::get_byte<1>(in32), 0xB0); + result.test_u8_eq(Botan::get_byte<2>(in32), 0xC0); + result.test_u8_eq(Botan::get_byte<3>(in32), 0xD0); + + result.test_u16_eq(Botan::make_uint16(0xAA, 0xBB), 0xAABB); + result.test_u32_eq(Botan::make_uint32(0x01, 0x02, 0x03, 0x04), 0x01020304); + + result.test_u16_eq(Botan::load_be(mem, 0), 0x0011); + result.test_u16_eq(Botan::load_be(mem, 1), 0x2233); + result.test_u16_eq(Botan::load_be(mem, 2), 0x4455); + result.test_u16_eq(Botan::load_be(mem, 3), 0x6677); + + result.test_u16_eq(Botan::load_le(mem, 0), 0x1100); + result.test_u16_eq(Botan::load_le(mem, 1), 0x3322); + result.test_u16_eq(Botan::load_le(mem, 2), 0x5544); + result.test_u16_eq(Botan::load_le(mem, 3), 0x7766); + + result.test_u32_eq(Botan::load_be(mem, 0), 0x00112233); + result.test_u32_eq(Botan::load_be(mem, 1), 0x44556677); + result.test_u32_eq(Botan::load_be(mem, 2), 0x8899AABB); + result.test_u32_eq(Botan::load_be(mem, 3), 0xCCDDEEFF); + + result.test_u32_eq(Botan::load_le(mem, 0), 0x33221100); + result.test_u32_eq(Botan::load_le(mem, 1), 0x77665544); + result.test_u32_eq(Botan::load_le(mem, 2), 0xBBAA9988); + result.test_u32_eq(Botan::load_le(mem, 3), 0xFFEEDDCC); - result.test_is_eq(Botan::load_be(mem, 0), 0x0011223344556677); - result.test_is_eq(Botan::load_be(mem, 1), 0x8899AABBCCDDEEFF); + result.test_u64_eq(Botan::load_be(mem, 0), 0x0011223344556677); + result.test_u64_eq(Botan::load_be(mem, 1), 0x8899AABBCCDDEEFF); - result.test_is_eq(Botan::load_le(mem, 0), 0x7766554433221100); - result.test_is_eq(Botan::load_le(mem, 1), 0xFFEEDDCCBBAA9988); + result.test_u64_eq(Botan::load_le(mem, 0), 0x7766554433221100); + result.test_u64_eq(Botan::load_le(mem, 1), 0xFFEEDDCCBBAA9988); // Check misaligned loads: - result.test_is_eq(Botan::load_be(mem + 1, 0), 0x1122); - result.test_is_eq(Botan::load_le(mem + 3, 0), 0x4433); + result.test_u16_eq(Botan::load_be(mem + 1, 0), 0x1122); + result.test_u16_eq(Botan::load_le(mem + 3, 0), 0x4433); - result.test_is_eq(Botan::load_be(mem + 1, 1), 0x55667788); - result.test_is_eq(Botan::load_le(mem + 3, 1), 0xAA998877); + result.test_u32_eq(Botan::load_be(mem + 1, 1), 0x55667788); + result.test_u32_eq(Botan::load_le(mem + 3, 1), 0xAA998877); - result.test_is_eq(Botan::load_be(mem + 1, 0), 0x1122334455667788); - result.test_is_eq(Botan::load_le(mem + 7, 0), 0xEEDDCCBBAA998877); - result.test_is_eq(Botan::load_le(mem + 5, 0), 0xCCBBAA9988776655); + result.test_u64_eq(Botan::load_be(mem + 1, 0), 0x1122334455667788); + result.test_u64_eq(Botan::load_le(mem + 7, 0), 0xEEDDCCBBAA998877); + result.test_u64_eq(Botan::load_le(mem + 5, 0), 0xCCBBAA9988776655); uint8_t outbuf[16] = {0}; for(size_t offset = 0; offset != 7; ++offset) { - uint8_t* out = outbuf + offset; + uint8_t* out = outbuf + offset; // NOLINT(*-const-correctness) clang-tidy bug Botan::store_be(in16, out); - result.test_is_eq(out[0], 0x12); - result.test_is_eq(out[1], 0x34); + result.test_u8_eq(out[0], 0x12); + result.test_u8_eq(out[1], 0x34); Botan::store_le(in16, out); - result.test_is_eq(out[0], 0x34); - result.test_is_eq(out[1], 0x12); + result.test_u8_eq(out[0], 0x34); + result.test_u8_eq(out[1], 0x12); Botan::store_be(in32, out); - result.test_is_eq(out[0], 0xA0); - result.test_is_eq(out[1], 0xB0); - result.test_is_eq(out[2], 0xC0); - result.test_is_eq(out[3], 0xD0); + result.test_u8_eq(out[0], 0xA0); + result.test_u8_eq(out[1], 0xB0); + result.test_u8_eq(out[2], 0xC0); + result.test_u8_eq(out[3], 0xD0); Botan::store_le(in32, out); - result.test_is_eq(out[0], 0xD0); - result.test_is_eq(out[1], 0xC0); - result.test_is_eq(out[2], 0xB0); - result.test_is_eq(out[3], 0xA0); + result.test_u8_eq(out[0], 0xD0); + result.test_u8_eq(out[1], 0xC0); + result.test_u8_eq(out[2], 0xB0); + result.test_u8_eq(out[3], 0xA0); Botan::store_be(in64, out); - result.test_is_eq(out[0], 0xAB); - result.test_is_eq(out[1], 0xCD); - result.test_is_eq(out[2], 0xEF); - result.test_is_eq(out[3], 0x01); - result.test_is_eq(out[4], 0x23); - result.test_is_eq(out[5], 0x45); - result.test_is_eq(out[6], 0x67); - result.test_is_eq(out[7], 0x89); + result.test_u8_eq(out[0], 0xAB); + result.test_u8_eq(out[1], 0xCD); + result.test_u8_eq(out[2], 0xEF); + result.test_u8_eq(out[3], 0x01); + result.test_u8_eq(out[4], 0x23); + result.test_u8_eq(out[5], 0x45); + result.test_u8_eq(out[6], 0x67); + result.test_u8_eq(out[7], 0x89); Botan::store_le(in64, out); - result.test_is_eq(out[0], 0x89); - result.test_is_eq(out[1], 0x67); - result.test_is_eq(out[2], 0x45); - result.test_is_eq(out[3], 0x23); - result.test_is_eq(out[4], 0x01); - result.test_is_eq(out[5], 0xEF); - result.test_is_eq(out[6], 0xCD); - result.test_is_eq(out[7], 0xAB); + result.test_u8_eq(out[0], 0x89); + result.test_u8_eq(out[1], 0x67); + result.test_u8_eq(out[2], 0x45); + result.test_u8_eq(out[3], 0x23); + result.test_u8_eq(out[4], 0x01); + result.test_u8_eq(out[5], 0xEF); + result.test_u8_eq(out[6], 0xCD); + result.test_u8_eq(out[7], 0xAB); } - std::array outarr; - uint16_t i0, i1, i2, i3; + std::array outarr{}; + uint16_t i0 = 0; + uint16_t i1 = 0; + uint16_t i2 = 0; + uint16_t i3 = 0; Botan::store_be(in64, outarr); Botan::load_be(outarr, i0, i1, i2, i3); - result.test_is_eq(i0, 0xABCD); - result.test_is_eq(i1, 0xEF01); - result.test_is_eq(i2, 0x2345); - result.test_is_eq(i3, 0x6789); + result.test_u16_eq(i0, 0xABCD); + result.test_u16_eq(i1, 0xEF01); + result.test_u16_eq(i2, 0x2345); + result.test_u16_eq(i3, 0x6789); Botan::load_le(std::span{outarr}.first<6>(), i0, i1, i2); - result.test_is_eq(i0, 0xCDAB); - result.test_is_eq(i1, 0x01EF); - result.test_is_eq(i2, 0x4523); - result.test_is_eq(i3, 0x6789); // remains unchanged + result.test_u16_eq(i0, 0xCDAB); + result.test_u16_eq(i1, 0x01EF); + result.test_u16_eq(i2, 0x4523); + result.test_u16_eq(i3, 0x6789); // remains unchanged Botan::store_le(in64, outarr); Botan::load_le(outarr, i0, i1, i2, i3); - result.test_is_eq(i0, 0x6789); - result.test_is_eq(i1, 0x2345); - result.test_is_eq(i2, 0xEF01); - result.test_is_eq(i3, 0xABCD); + result.test_u16_eq(i0, 0x6789); + result.test_u16_eq(i1, 0x2345); + result.test_u16_eq(i2, 0xEF01); + result.test_u16_eq(i3, 0xABCD); Botan::load_be(std::span{outarr}.first<6>(), i0, i1, i2); - result.test_is_eq(i0, 0x8967); - result.test_is_eq(i1, 0x4523); - result.test_is_eq(i2, 0x01EF); - result.test_is_eq(i3, 0xABCD); // remains unchanged + result.test_u16_eq(i0, 0x8967); + result.test_u16_eq(i1, 0x4523); + result.test_u16_eq(i2, 0x01EF); + result.test_u16_eq(i3, 0xABCD); // remains unchanged i0 = 0xAA11; i1 = 0xBB22; i2 = 0xCC33; i3 = 0xDD44; Botan::store_be(outarr, i0, i1, i2, i3); - result.test_is_eq(outarr, {0xAA, 0x11, 0xBB, 0x22, 0xCC, 0x33, 0xDD, 0x44}); + result.test_bin_eq("store_be", outarr, "AA11BB22CC33DD44"); std::vector outvec(8); Botan::store_be(outvec, i0, i1, i2, i3); - result.test_is_eq(outvec, Botan::hex_decode("AA11BB22CC33DD44")); + result.test_bin_eq("store_be", outvec, "AA11BB22CC33DD44"); Botan::store_le(outarr, i0, i1, i2, i3); - result.test_is_eq(outarr, {0x11, 0xAA, 0x22, 0xBB, 0x33, 0xCC, 0x44, 0xDD}); + result.test_bin_eq("store_le(arr)", outarr, "11AA22BB33CC44DD"); Botan::store_le(outvec, i0, i1, i2, i3); - result.test_is_eq(outvec, Botan::hex_decode("11AA22BB33CC44DD")); + result.test_bin_eq("store_le", outvec, "11AA22BB33CC44DD"); #if !defined(BOTAN_TERMINATE_ON_ASSERTS) std::vector sink56bits(7); @@ -339,23 +349,23 @@ auto out64_array_be = Botan::store_be(i0, i1, i2, i3); auto out64_vec_be = Botan::store_be>(i0, i1, i2, i3); auto out64_strong_be = Botan::store_be(i0, i1, i2, i3); - result.test_is_eq(out64_array_be, {0xAA, 0x11, 0xBB, 0x22, 0xCC, 0x33, 0xDD, 0x44}); - result.test_is_eq(out64_vec_be, Botan::hex_decode("AA11BB22CC33DD44")); - result.test_is_eq(out64_strong_be, TestVectorSink(Botan::hex_decode("AA11BB22CC33DD44"))); + result.test_bin_eq("store_be(arr)", out64_array_be, "AA11BB22CC33DD44"); + result.test_bin_eq("store_be(vec)", out64_vec_be, "AA11BB22CC33DD44"); + result.test_bin_eq("store_be(strong)", out64_strong_be, "AA11BB22CC33DD44"); auto out64_array_le = Botan::store_le(i0, i1, i2, i3); auto out64_vec_le = Botan::store_le>(i0, i1, i2, i3); auto out64_strong_le = Botan::store_le(i0, i1, i2, i3); - result.test_is_eq(out64_array_le, {0x11, 0xAA, 0x22, 0xBB, 0x33, 0xCC, 0x44, 0xDD}); - result.test_is_eq(out64_vec_le, Botan::hex_decode("11AA22BB33CC44DD")); - result.test_is_eq(out64_strong_le, TestVectorSink(Botan::hex_decode("11AA22BB33CC44DD"))); - - result.test_is_eq(in16, Botan::load_be(Botan::store_be(in16))); - result.test_is_eq(in32, Botan::load_be(Botan::store_be(in32))); - result.test_is_eq(in64, Botan::load_be(Botan::store_be(in64))); - - result.test_is_eq(in16, Botan::load_le(Botan::store_le(in16))); - result.test_is_eq(in32, Botan::load_le(Botan::store_le(in32))); - result.test_is_eq(in64, Botan::load_le(Botan::store_le(in64))); + result.test_bin_eq("store_le(arr)", out64_array_le, "11AA22BB33CC44DD"); + result.test_bin_eq("store_le(vec)", out64_vec_le, "11AA22BB33CC44DD"); + result.test_bin_eq("store_le(strong)", out64_strong_le, "11AA22BB33CC44DD"); + + result.test_u16_eq(in16, Botan::load_be(Botan::store_be(in16))); + result.test_u32_eq(in32, Botan::load_be(Botan::store_be(in32))); + result.test_u64_eq(in64, Botan::load_be(Botan::store_be(in64))); + + result.test_u16_eq(in16, Botan::load_le(Botan::store_le(in16))); + result.test_u32_eq(in32, Botan::load_le(Botan::store_le(in32))); + result.test_u64_eq(in64, Botan::load_le(Botan::store_le(in64))); // Test that the runtime detects incompatible range sizes #if !defined(BOTAN_TERMINATE_ON_ASSERTS) @@ -383,114 +393,121 @@ #endif // Test store of entire ranges - std::array in16_array = {0x0A0B, 0x0C0D}; - result.test_is_eq(Botan::store_be>(in16_array), Botan::hex_decode("0A0B0C0D")); - result.test_is_eq(Botan::store_le>(in16_array), Botan::hex_decode("0B0A0D0C")); - - std::vector in16_vector = {0x0A0B, 0x0C0D}; - result.test_is_eq(Botan::store_be>(in16_vector), Botan::hex_decode("0A0B0C0D")); - result.test_is_eq(Botan::store_le>(in16_vector), Botan::hex_decode("0B0A0D0C")); + const std::array in16_array = {0x0A0B, 0x0C0D}; + result.test_bin_eq("store_be(vec)", Botan::store_be>(in16_array), "0A0B0C0D"); + result.test_bin_eq("store_le(vec)", Botan::store_le>(in16_array), "0B0A0D0C"); + + const std::vector in16_vector = {0x0A0B, 0x0C0D}; + result.test_bin_eq("store_be(vec)", Botan::store_be>(in16_vector), "0A0B0C0D"); + result.test_bin_eq("store_le(vec)", Botan::store_le>(in16_vector), "0B0A0D0C"); - std::array out_array; + std::array out_array{}; Botan::store_be(out_array, in16_array); - result.test_is_eq(out_array, std::array{0x0A, 0x0B, 0x0C, 0x0D}); + result.test_bin_eq("store_be(arr)", out_array, "0A0B0C0D"); Botan::store_le(out_array, in16_array); - result.test_is_eq(out_array, std::array{0x0B, 0x0A, 0x0D, 0x0C}); + result.test_bin_eq("store_le(arr)", out_array, "0B0A0D0C"); const auto be_inferred = Botan::store_be(in16_array); - result.test_is_eq(be_inferred, std::array{0x0A, 0x0B, 0x0C, 0x0D}); + result.test_bin_eq("store_be(arr)", be_inferred, "0A0B0C0D"); const auto le_inferred = Botan::store_le(in16_array); - result.test_is_eq(le_inferred, std::array{0x0B, 0x0A, 0x0D, 0x0C}); + result.test_bin_eq("store_le(arr)", le_inferred, "0B0A0D0C"); // Test load of entire ranges const auto in_buffer = Botan::hex_decode("AABBCCDD"); auto out16_array_be = Botan::load_be>(in_buffer); - result.test_is_eq(out16_array_be[0], 0xAABB); - result.test_is_eq(out16_array_be[1], 0xCCDD); + result.test_u16_eq(out16_array_be[0], 0xAABB); + result.test_u16_eq(out16_array_be[1], 0xCCDD); auto out16_vec_be = Botan::load_be>(in_buffer); - result.test_eq_sz("be-vector has expected size", out16_vec_be.size(), 2); - result.test_is_eq(out16_vec_be[0], 0xAABB); - result.test_is_eq(out16_vec_be[1], 0xCCDD); + result.test_sz_eq("be-vector has expected size", out16_vec_be.size(), 2); + result.test_u16_eq(out16_vec_be[0], 0xAABB); + result.test_u16_eq(out16_vec_be[1], 0xCCDD); auto out16_array_le = Botan::load_le>(in_buffer); - result.test_is_eq(out16_array_le[0], 0xBBAA); - result.test_is_eq(out16_array_le[1], 0xDDCC); + result.test_u16_eq(out16_array_le[0], 0xBBAA); + result.test_u16_eq(out16_array_le[1], 0xDDCC); auto out16_vec_le = Botan::load_le>(in_buffer); - result.test_eq_sz("le-vector has expected size", out16_vec_be.size(), 2); - result.test_is_eq(out16_vec_le[0], 0xBBAA); - result.test_is_eq(out16_vec_le[1], 0xDDCC); + result.test_sz_eq("le-vector has expected size", out16_vec_be.size(), 2); + result.test_u16_eq(out16_vec_le[0], 0xBBAA); + result.test_u16_eq(out16_vec_le[1], 0xDDCC); // Test loading/storing of strong type integers const TestInt64 in64_strong{0xABCDEF0123456789}; const TestInt32 in32_strong{0xABCDEF01}; - result.test_is_eq(Botan::store_be>(in64_strong), Botan::hex_decode("ABCDEF0123456789")); - result.test_is_eq(Botan::store_le>(in64_strong), Botan::hex_decode("8967452301EFCDAB")); - result.test_is_eq(Botan::store_be>(in32_strong), Botan::hex_decode("ABCDEF01")); - result.test_is_eq(Botan::store_le>(in32_strong), Botan::hex_decode("01EFCDAB")); - - result.test_is_eq(Botan::load_be(Botan::hex_decode("ABCDEF0123456789")), in64_strong); - result.test_is_eq(Botan::load_le(Botan::hex_decode("8967452301EFCDAB")), in64_strong); - result.test_is_eq(Botan::load_be(Botan::hex_decode("ABCDEF01")), in32_strong); - result.test_is_eq(Botan::load_le(Botan::hex_decode("01EFCDAB")), in32_strong); - - std::vector some_in64_strongs{TestInt64{0xABCDEF0123456789}, TestInt64{0x0123456789ABCDEF}}; - result.test_is_eq(Botan::store_be>(some_in64_strongs), - Botan::hex_decode("ABCDEF01234567890123456789ABCDEF")); - result.test_is_eq(Botan::store_le>(some_in64_strongs), - Botan::hex_decode("8967452301EFCDABEFCDAB8967452301")); + result.test_bin_eq( + "store_be(u64,strong)", Botan::store_be>(in64_strong), "ABCDEF0123456789"); + result.test_bin_eq( + "store_le(u64,strong)", Botan::store_le>(in64_strong), "8967452301EFCDAB"); + result.test_bin_eq("store_be(u32,strong)", Botan::store_be>(in32_strong), "ABCDEF01"); + result.test_bin_eq("store_le(u32,strong)", Botan::store_le>(in32_strong), "01EFCDAB"); + + test_arb_eq( + result, "load_be(strong64)", Botan::load_be(Botan::hex_decode("ABCDEF0123456789")), in64_strong); + test_arb_eq( + result, "load_le(strong64)", Botan::load_le(Botan::hex_decode("8967452301EFCDAB")), in64_strong); + test_arb_eq( + result, "load_be(strong32)", Botan::load_be(Botan::hex_decode("ABCDEF01")), in32_strong); + test_arb_eq( + result, "load_le(strong32)", Botan::load_le(Botan::hex_decode("01EFCDAB")), in32_strong); + + const std::vector some_in64_strongs{TestInt64{0xABCDEF0123456789}, TestInt64{0x0123456789ABCDEF}}; + result.test_bin_eq("store_be(vector,strong)", + Botan::store_be>(some_in64_strongs), + "ABCDEF01234567890123456789ABCDEF"); + result.test_bin_eq("store_le(vector,strong)", + Botan::store_le>(some_in64_strongs), + "8967452301EFCDABEFCDAB8967452301"); const auto in64_strongs_le = Botan::load_le>(Botan::hex_decode("8967452301EFCDABEFCDAB8967452301")); - result.test_is_eq(in64_strongs_le[0], TestInt64{0xABCDEF0123456789}); - result.test_is_eq(in64_strongs_le[1], TestInt64{0x0123456789ABCDEF}); + test_arb_eq(result, "load_le(strong arr)", in64_strongs_le[0], TestInt64{0xABCDEF0123456789}); + test_arb_eq(result, "load_le(strong arr)", in64_strongs_le[1], TestInt64{0x0123456789ABCDEF}); const auto in64_strongs_be = Botan::load_be>(Botan::hex_decode("ABCDEF01234567890123456789ABCDEF")); - result.test_is_eq(in64_strongs_be[0], TestInt64{0xABCDEF0123456789}); - result.test_is_eq(in64_strongs_be[1], TestInt64{0x0123456789ABCDEF}); + test_arb_eq(result, "load_be(strong arr)", in64_strongs_be[0], TestInt64{0xABCDEF0123456789}); + test_arb_eq(result, "load_be(strong arr)", in64_strongs_be[1], TestInt64{0x0123456789ABCDEF}); // Test loading/storing of enum types with different endianness const auto in64_enum_le = Botan::load_le(Botan::hex_decode("1234567890ABCDEF")); - result.test_is_eq(in64_enum_le, TestEnum64::_2); + result.test_enum_eq("load_le(enum64)", in64_enum_le, TestEnum64::_2); const auto in64_enum_be = Botan::load_be(Botan::hex_decode("1234567890ABCDEF")); - result.test_is_eq(in64_enum_be, TestEnum64::_1); - result.test_is_eq(Botan::store_le>(TestEnum64::_1), - Botan::hex_decode("EFCDAB9078563412")); - result.test_is_eq>(Botan::store_be(TestEnum64::_2), - {0xEF, 0xCD, 0xAB, 0x90, 0x78, 0x56, 0x34, 0x12}); + result.test_enum_eq("load_be(enum64)", in64_enum_be, TestEnum64::_1); + result.test_bin_eq( + "store_be(enum64)", Botan::store_le>(TestEnum64::_1), "EFCDAB9078563412"); + result.test_bin_eq("store_be(enum64)", Botan::store_be(TestEnum64::_2), "EFCDAB9078563412"); const auto in32_enum_le = Botan::load_le(Botan::hex_decode("78563412")); - result.test_is_eq(in32_enum_le, TestEnum32::_1); + result.test_enum_eq("load_le(enum32)", in32_enum_le, TestEnum32::_1); const auto in32_enum_be = Botan::load_be(Botan::hex_decode("78563412")); - result.test_is_eq(in32_enum_be, TestEnum32::_2); - result.test_is_eq(Botan::store_le>(TestEnum32::_1), Botan::hex_decode("78563412")); - result.test_is_eq>(Botan::store_be(TestEnum32::_2), {0x78, 0x56, 0x34, 0x12}); + result.test_enum_eq("load_be(enum32)", in32_enum_be, TestEnum32::_2); + result.test_bin_eq("store_le(enum32)", Botan::store_le>(TestEnum32::_1), "78563412"); + result.test_bin_eq("store_be(enum32)", Botan::store_be(TestEnum32::_2), "78563412"); return result; } template static T fb_load_be(std::array in) { - return Botan::detail::fallback_load_any(in); + return Botan::detail::fallback_load_any(in); } template static T fb_load_le(std::array in) { - return Botan::detail::fallback_load_any(in); + return Botan::detail::fallback_load_any(in); } template static decltype(auto) fb_store_be(const T in) { - std::array out; - Botan::detail::fallback_store_any(in, out); + std::array out{}; + Botan::detail::fallback_store_any(in, out); return out; } template static decltype(auto) fb_store_le(const T in) { - std::array out; - Botan::detail::fallback_store_any(in, out); + std::array out{}; + Botan::detail::fallback_store_any(in, out); return out; } @@ -517,12 +534,12 @@ const auto out_be_szt = Botan::store_be(inszt); const auto out_le_szt = Botan::store_le(inszt); - result.test_is_eq("be 32", Botan::load_be(out_be_32), in32); - result.test_is_eq("le 32", Botan::load_le(out_le_32), in32); - result.test_is_eq("be 64", Botan::load_be(out_be_64), in64); - result.test_is_eq("le 64", Botan::load_le(out_le_64), in64); - result.test_is_eq("be szt", Botan::load_be(out_be_szt), inszt); - result.test_is_eq("le szt", Botan::load_le(out_le_szt), inszt); + result.test_u32_eq("be 32", Botan::load_be(out_be_32), in32); + result.test_u32_eq("le 32", Botan::load_le(out_le_32), in32); + result.test_u64_eq("be 64", Botan::load_be(out_be_64), in64); + result.test_u64_eq("le 64", Botan::load_le(out_le_64), in64); + result.test_sz_eq("be sz", Botan::load_be(out_be_szt), inszt); + result.test_sz_eq("le sz", Botan::load_le(out_le_szt), inszt); return result; } @@ -534,21 +551,21 @@ // won't be called in production. Test::Result result("Util load/store fallback"); - result.test_is_eq("lLE 16", fb_load_le({1, 2}), 0x0201); - result.test_is_eq("lLE 32", fb_load_le({1, 2, 3, 4}), 0x04030201); - result.test_is_eq("lLE 64", fb_load_le({1, 2, 3, 4, 5, 6, 7, 8}), 0x0807060504030201); - - result.test_is_eq("lBE 16", fb_load_be({1, 2}), 0x0102); - result.test_is_eq("lBE 32", fb_load_be({1, 2, 3, 4}), 0x01020304); - result.test_is_eq("lBE 64", fb_load_be({1, 2, 3, 4, 5, 6, 7, 8}), 0x0102030405060708); - - result.test_is_eq>("sLE 16", fb_store_le(0x0201), {1, 2}); - result.test_is_eq>("sLE 32", fb_store_le(0x04030201), {1, 2, 3, 4}); - result.test_is_eq>("sLE 64", fb_store_le(0x0807060504030201), {1, 2, 3, 4, 5, 6, 7, 8}); - - result.test_is_eq>("sBE 16", fb_store_be(0x0102), {1, 2}); - result.test_is_eq>("sBE 32", fb_store_be(0x01020304), {1, 2, 3, 4}); - result.test_is_eq>("sBE 64", fb_store_be(0x0102030405060708), {1, 2, 3, 4, 5, 6, 7, 8}); + result.test_u16_eq("lLE 16", fb_load_le({1, 2}), 0x0201); + result.test_u32_eq("lLE 32", fb_load_le({1, 2, 3, 4}), 0x04030201); + result.test_u64_eq("lLE 64", fb_load_le({1, 2, 3, 4, 5, 6, 7, 8}), 0x0807060504030201); + + result.test_u16_eq("lBE 16", fb_load_be({1, 2}), 0x0102); + result.test_u32_eq("lBE 32", fb_load_be({1, 2, 3, 4}), 0x01020304); + result.test_u64_eq("lBE 64", fb_load_be({1, 2, 3, 4, 5, 6, 7, 8}), 0x0102030405060708); + + result.test_bin_eq("sLE 16", fb_store_le(0x0201), "0102"); + result.test_bin_eq("sLE 32", fb_store_le(0x04030201), "01020304"); + result.test_bin_eq("sLE 64", fb_store_le(0x0807060504030201), "0102030405060708"); + + result.test_bin_eq("sBE 16", fb_store_be(0x0102), "0102"); + result.test_bin_eq("sBE 32", fb_store_be(0x01020304), "01020304"); + result.test_bin_eq("sBE 64", fb_store_be(0x0102030405060708), "0102030405060708"); return result; } @@ -568,133 +585,129 @@ // get_byte<> w/ 16bit constexpr auto cex_byte_16_0 = Botan::get_byte<0>(in16); - result.test_is_eq(cex_byte_16_0, 0x12); + result.test_u8_eq(cex_byte_16_0, 0x12); constexpr auto cex_byte_16_1 = Botan::get_byte<1>(in16); - result.test_is_eq(cex_byte_16_1, 0x34); + result.test_u8_eq(cex_byte_16_1, 0x34); // get_byte<> w/ 32bit constexpr auto cex_byte_32_0 = Botan::get_byte<0>(in32); - result.test_is_eq(cex_byte_32_0, 0xA0); + result.test_u8_eq(cex_byte_32_0, 0xA0); constexpr auto cex_byte_32_1 = Botan::get_byte<1>(in32); - result.test_is_eq(cex_byte_32_1, 0xB0); + result.test_u8_eq(cex_byte_32_1, 0xB0); constexpr auto cex_byte_32_2 = Botan::get_byte<2>(in32); - result.test_is_eq(cex_byte_32_2, 0xC0); + result.test_u8_eq(cex_byte_32_2, 0xC0); constexpr auto cex_byte_32_3 = Botan::get_byte<3>(in32); - result.test_is_eq(cex_byte_32_3, 0xD0); + result.test_u8_eq(cex_byte_32_3, 0xD0); // get_byte<> w/ 64bit constexpr auto cex_byte_64_0 = Botan::get_byte<0>(in64); - result.test_is_eq(cex_byte_64_0, 0xAB); + result.test_u8_eq(cex_byte_64_0, 0xAB); constexpr auto cex_byte_64_1 = Botan::get_byte<1>(in64); - result.test_is_eq(cex_byte_64_1, 0xCD); + result.test_u8_eq(cex_byte_64_1, 0xCD); constexpr auto cex_byte_64_2 = Botan::get_byte<2>(in64); - result.test_is_eq(cex_byte_64_2, 0xEF); + result.test_u8_eq(cex_byte_64_2, 0xEF); constexpr auto cex_byte_64_3 = Botan::get_byte<3>(in64); - result.test_is_eq(cex_byte_64_3, 0x01); + result.test_u8_eq(cex_byte_64_3, 0x01); constexpr auto cex_byte_64_4 = Botan::get_byte<4>(in64); - result.test_is_eq(cex_byte_64_4, 0x23); + result.test_u8_eq(cex_byte_64_4, 0x23); constexpr auto cex_byte_64_5 = Botan::get_byte<5>(in64); - result.test_is_eq(cex_byte_64_5, 0x45); + result.test_u8_eq(cex_byte_64_5, 0x45); constexpr auto cex_byte_64_6 = Botan::get_byte<6>(in64); - result.test_is_eq(cex_byte_64_6, 0x67); + result.test_u8_eq(cex_byte_64_6, 0x67); constexpr auto cex_byte_64_7 = Botan::get_byte<7>(in64); - result.test_is_eq(cex_byte_64_7, 0x89); + result.test_u8_eq(cex_byte_64_7, 0x89); // make_uintXX() constexpr auto cex_uint16_t = Botan::make_uint16(0x12, 0x34); - result.test_is_eq(cex_uint16_t, in16); + result.test_u16_eq(cex_uint16_t, in16); constexpr auto cex_uint32_t = Botan::make_uint32(0xA0, 0xB0, 0xC0, 0xD0); - result.test_is_eq(cex_uint32_t, in32); + result.test_u32_eq(cex_uint32_t, in32); constexpr auto cex_uint64_t = Botan::make_uint64(0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89); - result.test_is_eq(cex_uint64_t, in64); + result.test_u64_eq(cex_uint64_t, in64); // store_le/be with a single integer constexpr std::array cex_store_le16 = Botan::store_le(in16); - result.test_is_eq(cex_store_le16, std::array{0x34, 0x12}); + result.test_bin_eq("store_le(u16 arr)", cex_store_le16, "3412"); constexpr std::array cex_store_le32 = Botan::store_le(in32); - result.test_is_eq(cex_store_le32, std::array{0xD0, 0xC0, 0xB0, 0xA0}); + result.test_bin_eq("store_le(u32 arr)", cex_store_le32, "D0C0B0A0"); constexpr std::array cex_store_le64 = Botan::store_le(in64); - result.test_is_eq(cex_store_le64, std::array{0x89, 0x67, 0x45, 0x23, 0x01, 0xEF, 0xCD, 0xAB}); + result.test_bin_eq("store_le(u32,arr)", cex_store_le64, "8967452301EFCDAB"); constexpr std::array cex_store_be16 = Botan::store_be(in16); - result.test_is_eq(cex_store_be16, std::array{0x12, 0x34}); + result.test_bin_eq("store_be(u16 arr)", cex_store_be16, "1234"); constexpr std::array cex_store_be32 = Botan::store_be(in32); - result.test_is_eq(cex_store_be32, std::array{0xA0, 0xB0, 0xC0, 0xD0}); + result.test_bin_eq("store_be(u32 arr)", cex_store_be32, "A0B0C0D0"); constexpr std::array cex_store_be64 = Botan::store_be(in64); - result.test_is_eq(cex_store_be64, std::array{0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89}); + result.test_bin_eq("store_be(u64 arr)", cex_store_be64, "ABCDEF0123456789"); // store_le/be with multiple integers, both as a parameter pack and a range (std::array for constexpr) constexpr std::array cex_store_le16s = Botan::store_le(in16, in16, in16, in16, in16, in16, in16, in16); constexpr std::array cex_store_le16s2 = Botan::store_le(std::array{in16, in16, in16, in16, in16, in16, in16, in16}); - result.test_is_eq( - cex_store_le16s, - {0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12}); - result.test_is_eq(cex_store_le16s, cex_store_le16s2); + result.test_bin_eq("store_le", cex_store_le16s, "34123412341234123412341234123412"); + result.test_bin_eq("cex_store_le16s", cex_store_le16s, cex_store_le16s2); constexpr std::array cex_store_le32s = Botan::store_le(in32, in32, in32, in32); constexpr std::array cex_store_le32s2 = Botan::store_le(std::array{in32, in32, in32, in32}); - result.test_is_eq( - cex_store_le32s, - {0xD0, 0xC0, 0xB0, 0xA0, 0xD0, 0xC0, 0xB0, 0xA0, 0xD0, 0xC0, 0xB0, 0xA0, 0xD0, 0xC0, 0xB0, 0xA0}); - result.test_is_eq(cex_store_le32s, cex_store_le32s2); + result.test_bin_eq("cex_store_le32s", cex_store_le32s, "D0C0B0A0D0C0B0A0D0C0B0A0D0C0B0A0"); + result.test_bin_eq("cex_store_le32s2", cex_store_le32s, cex_store_le32s2); constexpr std::array cex_store_le64s = Botan::store_le(in64, in64); constexpr std::array cex_store_le64s2 = Botan::store_le(std::array{in64, in64}); - result.test_is_eq( - cex_store_le64s, - {0x89, 0x67, 0x45, 0x23, 0x01, 0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01, 0xEF, 0xCD, 0xAB}); - result.test_is_eq(cex_store_le64s, cex_store_le64s2); + result.test_bin_eq("cex_store_le64s", cex_store_le64s, "8967452301EFCDAB8967452301EFCDAB"); + result.test_bin_eq("cex_store_le64s2", cex_store_le64s, cex_store_le64s2); constexpr std::array cex_store_be16s = Botan::store_be(in16, in16, in16, in16, in16, in16, in16, in16); constexpr std::array cex_store_be16s2 = Botan::store_be(std::array{in16, in16, in16, in16, in16, in16, in16, in16}); - result.test_is_eq( - cex_store_be16s, - {0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34}); - result.test_is_eq(cex_store_be16s, cex_store_be16s2); + result.test_bin_eq("cex_store_be16s", cex_store_be16s, "12341234123412341234123412341234"); + result.test_bin_eq("cex_store_be16s2", cex_store_be16s, cex_store_be16s2); constexpr std::array cex_store_be32s = Botan::store_be(in32, in32, in32, in32); constexpr std::array cex_store_be32s2 = Botan::store_be(std::array{in32, in32, in32, in32}); - result.test_is_eq( - cex_store_be32s, - {0xA0, 0xB0, 0xC0, 0xD0, 0xA0, 0xB0, 0xC0, 0xD0, 0xA0, 0xB0, 0xC0, 0xD0, 0xA0, 0xB0, 0xC0, 0xD0}); - result.test_is_eq(cex_store_be32s, cex_store_be32s2); + result.test_bin_eq("cex_store_be32s", cex_store_be32s, "A0B0C0D0A0B0C0D0A0B0C0D0A0B0C0D0"); + result.test_bin_eq("cex_store_be32s2", cex_store_be32s, cex_store_be32s2); constexpr std::array cex_store_be64s = Botan::store_be(in64, in64); constexpr std::array cex_store_be64s2 = Botan::store_be(std::array{in64, in64}); - result.test_is_eq( - cex_store_be64s, - {0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89}); - result.test_is_eq(cex_store_be64s, cex_store_be64s2); + result.test_bin_eq("cex_store_be64s", cex_store_be64s, "ABCDEF0123456789ABCDEF0123456789"); + result.test_bin_eq("cex_store_be64s2", cex_store_be64s, cex_store_be64s2); // load_le/be a single integer constexpr uint16_t cex_load_le16 = Botan::load_le(cex_store_le16); - result.test_is_eq(cex_load_le16, in16); + result.test_u16_eq(cex_load_le16, in16); constexpr uint32_t cex_load_le32 = Botan::load_le(cex_store_le32); - result.test_is_eq(cex_load_le32, in32); + result.test_u32_eq(cex_load_le32, in32); constexpr uint64_t cex_load_le64 = Botan::load_le(cex_store_le64); - result.test_is_eq(cex_load_le64, in64); + result.test_u64_eq(cex_load_le64, in64); constexpr uint16_t cex_load_be16 = Botan::load_be(cex_store_be16); - result.test_is_eq(cex_load_be16, in16); + result.test_u16_eq(cex_load_be16, in16); constexpr uint32_t cex_load_be32 = Botan::load_be(cex_store_be32); - result.test_is_eq(cex_load_be32, in32); + result.test_u32_eq(cex_load_be32, in32); constexpr uint64_t cex_load_be64 = Botan::load_be(cex_store_be64); - result.test_is_eq(cex_load_be64, in64); + result.test_u64_eq(cex_load_be64, in64); // load_le/be multiple integers into a std::array for constexpr constexpr auto cex_load_le16s = Botan::load_le>(cex_mem); - result.test_is_eq(cex_load_le16s, {0x1100, 0x3322, 0x5544, 0x7766, 0x9988, 0xBBAA, 0xDDCC, 0xFFEE}); + test_arb_eq(result, + "constexpr load_le(u16)", + cex_load_le16s, + {0x1100, 0x3322, 0x5544, 0x7766, 0x9988, 0xBBAA, 0xDDCC, 0xFFEE}); constexpr auto cex_load_le32s = Botan::load_le>(cex_mem); - result.test_is_eq(cex_load_le32s, {0x33221100, 0x77665544, 0xBBAA9988, 0xFFEEDDCC}); + test_arb_eq( + result, "constexpr load_le(u32)", cex_load_le32s, {0x33221100, 0x77665544, 0xBBAA9988, 0xFFEEDDCC}); constexpr auto cex_load_le64s = Botan::load_le>(cex_mem); - result.test_is_eq(cex_load_le64s, {0x7766554433221100, 0xFFEEDDCCBBAA9988}); + test_arb_eq(result, "constexpr load_le(u64)", cex_load_le64s, {0x7766554433221100, 0xFFEEDDCCBBAA9988}); constexpr auto cex_load_be16s = Botan::load_be>(cex_mem); - result.test_is_eq(cex_load_be16s, {0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xAABB, 0xCCDD, 0xEEFF}); + test_arb_eq(result, + "constexpr load_be(u16)", + cex_load_be16s, + {0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xAABB, 0xCCDD, 0xEEFF}); constexpr auto cex_load_be32s = Botan::load_be>(cex_mem); - result.test_is_eq(cex_load_be32s, {0x00112233, 0x44556677, 0x8899AABB, 0xCCDDEEFF}); + test_arb_eq( + result, "constexpr load_be(u32)", cex_load_be32s, {0x00112233, 0x44556677, 0x8899AABB, 0xCCDDEEFF}); constexpr auto cex_load_be64s = Botan::load_be>(cex_mem); - result.test_is_eq(cex_load_be64s, {0x0011223344556677, 0x8899AABBCCDDEEFF}); + test_arb_eq(result, "constexpr load_be(u64)", cex_load_be64s, {0x0011223344556677, 0x8899AABBCCDDEEFF}); return result; } @@ -706,7 +719,7 @@ std::vector out_vector(4); const std::array in_array = {0x0A0B, 0x0C0D}; Botan::copy_out_be(out_vector, in_array); - result.test_is_eq(out_vector, Botan::hex_decode("0A0B0C0D")); + result.test_bin_eq("copy_out_be", out_vector, "0A0B0C0D"); }), CHECK("copy_out_be with 16bit input (partial words)", @@ -714,7 +727,7 @@ std::vector out_vector(3); const std::array in_array = {0x0A0B, 0x0C0D}; Botan::copy_out_be(out_vector, in_array); - result.test_is_eq(out_vector, Botan::hex_decode("0A0B0C")); + result.test_bin_eq("copy_out_be(u16)", out_vector, "0A0B0C"); }), CHECK("copy_out_le with 16bit input (word aligned)", @@ -722,7 +735,7 @@ std::vector out_vector(4); const std::array in_array = {0x0A0B, 0x0C0D}; Botan::copy_out_le(out_vector, in_array); - result.test_is_eq(out_vector, Botan::hex_decode("0B0A0D0C")); + result.test_bin_eq("copy_out_le(u16)", out_vector, "0B0A0D0C"); }), CHECK("copy_out_le with 16bit input (partial words)", @@ -730,7 +743,7 @@ std::vector out_vector(3); const std::array in_array = {0x0A0B, 0x0C0D}; Botan::copy_out_le(out_vector, in_array); - result.test_is_eq(out_vector, Botan::hex_decode("0B0A0D")); + result.test_bin_eq("copy_out_le(u16)", out_vector, "0B0A0D"); }), CHECK("copy_out_be with 64bit input (word aligned)", @@ -738,7 +751,7 @@ std::vector out_vector(16); const std::array in_array = {0x0A0B0C0D0E0F1011, 0x1213141516171819}; Botan::copy_out_be(out_vector, in_array); - result.test_is_eq(out_vector, Botan::hex_decode("0A0B0C0D0E0F10111213141516171819")); + result.test_bin_eq("copy_out_be(u64)", out_vector, "0A0B0C0D0E0F10111213141516171819"); }), CHECK("copy_out_le with 64bit input (word aligned)", @@ -746,7 +759,7 @@ std::vector out_vector(16); const std::array in_array = {0x0A0B0C0D0E0F1011, 0x1213141516171819}; Botan::copy_out_le(out_vector, in_array); - result.test_is_eq(out_vector, Botan::hex_decode("11100F0E0D0C0B0A1918171615141312")); + result.test_bin_eq("copy_out_le(u64)", out_vector, "11100F0E0D0C0B0A1918171615141312"); }), CHECK("copy_out_be with 64bit input (partial words)", @@ -754,7 +767,7 @@ std::vector out_vector(15); const std::array in_array = {0x0A0B0C0D0E0F1011, 0x1213141516171819}; Botan::copy_out_be(out_vector, in_array); - result.test_is_eq(out_vector, Botan::hex_decode("0A0B0C0D0E0F101112131415161718")); + result.test_bin_eq("copy_out_be(u64)", out_vector, "0A0B0C0D0E0F101112131415161718"); }), CHECK("copy_out_le with 64bit input (partial words)", @@ -762,7 +775,7 @@ std::vector out_vector(15); const std::array in_array = {0x0A0B0C0D0E0F1011, 0x1213141516171819}; Botan::copy_out_le(out_vector, in_array); - result.test_is_eq(out_vector, Botan::hex_decode("11100F0E0D0C0B0A19181716151413")); + result.test_bin_eq("copy_out_le(u64)", out_vector, "11100F0E0D0C0B0A19181716151413"); }), }; } @@ -787,7 +800,10 @@ private: template void test_ctz(Test::Result& result, T val, size_t expected) { - result.test_eq("ctz(" + std::to_string(val) + ")", Botan::ctz(val), expected); + Botan::CT::poison(val); + const size_t computed = Botan::ctz(val); + Botan::CT::unpoison_all(computed, val); + result.test_sz_eq("ctz(" + std::to_string(val) + ")", computed, expected); } Test::Result test_ctz() { @@ -804,7 +820,10 @@ template void test_sig_bytes(Test::Result& result, T val, size_t expected) { - result.test_eq("significant_bytes(" + std::to_string(val) + ")", Botan::significant_bytes(val), expected); + Botan::CT::poison(val); + const size_t computed = Botan::significant_bytes(val); + Botan::CT::unpoison_all(computed, val); + result.test_sz_eq("significant_bytes(" + std::to_string(val) + ")", computed, expected); } Test::Result test_sig_bytes() { @@ -830,7 +849,7 @@ template void test_power_of_2(Test::Result& result, T val, bool expected) { - result.test_eq("power_of_2(" + std::to_string(val) + ")", Botan::is_power_of_2(val), expected); + result.test_bool_eq("power_of_2(" + std::to_string(val) + ")", Botan::is_power_of_2(val), expected); } Test::Result test_power_of_2() { @@ -864,31 +883,31 @@ template auto random_pc(Test::Result& result) { auto n = Botan::load_le(Test::rng().random_array()); - result.test_is_eq(Botan::fmt("popcount({}) == {}", n, std::popcount(n)), pc(n), std::popcount(n)); + result.test_sz_eq(Botan::fmt("popcount({}) == {}", n, std::popcount(n)), pc(n), std::popcount(n)); } Test::Result test_popcount() { Test::Result result("popcount"); - result.test_is_eq("popcount(0)", pc(0), 0); - result.test_is_eq("popcount(0)", pc(0), 0); - result.test_is_eq("popcount(0)", pc(0), 0); - result.test_is_eq("popcount(0)", pc(0), 0); - - result.test_is_eq("popcount(1)", pc(1), 1); - result.test_is_eq("popcount(1)", pc(1), 1); - result.test_is_eq("popcount(1)", pc(1), 1); - result.test_is_eq("popcount(1)", pc(1), 1); - - result.test_is_eq("popcount(0xAA)", pc(0xAA), 4); - result.test_is_eq("popcount(0xAAAA)", pc(0xAAAA), 8); - result.test_is_eq("popcount(0xAAAA...)", pc(0xAAAAAAAA), 16); - result.test_is_eq("popcount(0xAAAA...)", pc(0xAAAAAAAAAAAAAAAA), 32); - - result.test_is_eq("popcount(0xFF)", pc(0xFF), 8); - result.test_is_eq("popcount(0xFFFF)", pc(0xFFFF), 16); - result.test_is_eq("popcount(0xFFFF...)", pc(0xFFFFFFFF), 32); - result.test_is_eq("popcount(0xFFFF...)", pc(0xFFFFFFFFFFFFFFFF), 64); + result.test_u8_eq("popcount(0)", pc(0), 0); + result.test_u8_eq("popcount(0)", pc(0), 0); + result.test_u8_eq("popcount(0)", pc(0), 0); + result.test_u8_eq("popcount(0)", pc(0), 0); + + result.test_u8_eq("popcount(1)", pc(1), 1); + result.test_u8_eq("popcount(1)", pc(1), 1); + result.test_u8_eq("popcount(1)", pc(1), 1); + result.test_u8_eq("popcount(1)", pc(1), 1); + + result.test_u8_eq("popcount(0xAA)", pc(0xAA), 4); + result.test_u8_eq("popcount(0xAAAA)", pc(0xAAAA), 8); + result.test_u8_eq("popcount(0xAAAA...)", pc(0xAAAAAAAA), 16); + result.test_u8_eq("popcount(0xAAAA...)", pc(0xAAAAAAAAAAAAAAAA), 32); + + result.test_u8_eq("popcount(0xFF)", pc(0xFF), 8); + result.test_u8_eq("popcount(0xFFFF)", pc(0xFFFF), 16); + result.test_u8_eq("popcount(0xFFFF...)", pc(0xFFFFFFFF), 32); + result.test_u8_eq("popcount(0xFFFF...)", pc(0xFFFFFFFFFFFFFFFF), 64); random_pc(result); random_pc(result); @@ -901,27 +920,21 @@ Test::Result test_reverse_bits() { Test::Result result("reverse_bits"); - result.test_is_eq("rev(0u8)", Botan::ct_reverse_bits(0b00000000), 0b00000000); - result.test_is_eq("rev(1u8)", Botan::ct_reverse_bits(0b01010101), 0b10101010); - result.test_is_eq("rev(2u8)", Botan::ct_reverse_bits(0b01001011), 0b11010010); - - result.test_is_eq( - "rev(0u16)", Botan::ct_reverse_bits(0b0000000000000000), 0b0000000000000000); - result.test_is_eq( - "rev(1u16)", Botan::ct_reverse_bits(0b0101010101010101), 0b1010101010101010); - result.test_is_eq( - "rev(2u16)", Botan::ct_reverse_bits(0b0100101101011010), 0b0101101011010010); - - result.test_is_eq("rev(0u32)", Botan::ct_reverse_bits(0xFFFFFFFF), 0xFFFFFFFF); - result.test_is_eq("rev(1u32)", Botan::ct_reverse_bits(0x55555555), 0xAAAAAAAA); - result.test_is_eq("rev(2u32)", Botan::ct_reverse_bits(0x4B6A2C1D), 0xB83456D2); - - result.test_is_eq( - "rev(0u64)", Botan::ct_reverse_bits(0xF0E0D0C005040302), 0x40C020A0030B070F); - result.test_is_eq( - "rev(1u64)", Botan::ct_reverse_bits(0x5555555555555555), 0xAAAAAAAAAAAAAAAA); - result.test_is_eq( - "rev(2u64)", Botan::ct_reverse_bits(0x4B6A2C1D5E7F8A90), 0x951FE7AB83456D2); + result.test_u8_eq("rev(0u8)", Botan::ct_reverse_bits(0b00000000), 0b00000000); + result.test_u8_eq("rev(1u8)", Botan::ct_reverse_bits(0b01010101), 0b10101010); + result.test_u8_eq("rev(2u8)", Botan::ct_reverse_bits(0b01001011), 0b11010010); + + result.test_u16_eq("rev(0u16)", Botan::ct_reverse_bits(0b0000000000000000), 0b0000000000000000); + result.test_u16_eq("rev(1u16)", Botan::ct_reverse_bits(0b0101010101010101), 0b1010101010101010); + result.test_u16_eq("rev(2u16)", Botan::ct_reverse_bits(0b0100101101011010), 0b0101101011010010); + + result.test_u32_eq("rev(0u32)", Botan::ct_reverse_bits(0xFFFFFFFF), 0xFFFFFFFF); + result.test_u32_eq("rev(1u32)", Botan::ct_reverse_bits(0x55555555), 0xAAAAAAAA); + result.test_u32_eq("rev(2u32)", Botan::ct_reverse_bits(0x4B6A2C1D), 0xB83456D2); + + result.test_u64_eq("rev(0u64)", Botan::ct_reverse_bits(0xF0E0D0C005040302), 0x40C020A0030B070F); + result.test_u64_eq("rev(1u64)", Botan::ct_reverse_bits(0x5555555555555555), 0xAAAAAAAAAAAAAAAA); + result.test_u64_eq("rev(2u64)", Botan::ct_reverse_bits(0x4B6A2C1D5E7F8A90), 0x951FE7AB83456D2); return result; } @@ -943,7 +956,7 @@ std::vector b = in; Botan::poly_double_n(b.data(), b.size()); - result.test_eq("Expected value", b, out); + result.test_bin_eq("Expected value", b, out); return result; } }; @@ -957,37 +970,33 @@ std::vector run() override { Test::Result result("Versions"); - result.confirm("Version datestamp matches macro", Botan::version_datestamp() == BOTAN_VERSION_DATESTAMP); + result.test_u32_eq("Version datestamp matches macro", Botan::version_datestamp(), BOTAN_VERSION_DATESTAMP); const char* version_cstr = Botan::version_cstr(); - std::string version_str = Botan::version_string(); - result.test_eq("Same version string", version_str, std::string(version_cstr)); + const std::string version_str = Botan::version_string(); + result.test_str_eq("Same version string", version_str, std::string(version_cstr)); const char* sversion_cstr = Botan::short_version_cstr(); - std::string sversion_str = Botan::short_version_string(); - result.test_eq("Same short version string", sversion_str, std::string(sversion_cstr)); + const std::string sversion_str = Botan::short_version_string(); + result.test_str_eq("Same short version string", sversion_str, std::string(sversion_cstr)); - std::string expected_sversion = std::to_string(BOTAN_VERSION_MAJOR) + "." + - std::to_string(BOTAN_VERSION_MINOR) + "." + - std::to_string(BOTAN_VERSION_PATCH); - -#if defined(BOTAN_VERSION_SUFFIX) - expected_sversion += BOTAN_VERSION_SUFFIX_STR; -#endif + const auto expected_sversion = + Botan::fmt("{}.{}.{}", BOTAN_VERSION_MAJOR, BOTAN_VERSION_MINOR, BOTAN_VERSION_PATCH); - result.test_eq("Short version string has expected format", sversion_str, expected_sversion); + // May have a suffix eg 4.0.0-rc2 + result.test_is_true("Short version string has expected format", sversion_str.starts_with(expected_sversion)); const std::string version_check_ok = Botan::runtime_version_check(BOTAN_VERSION_MAJOR, BOTAN_VERSION_MINOR, BOTAN_VERSION_PATCH); - result.confirm("Correct version no warning", version_check_ok.empty()); + result.test_is_true("Correct version no warning", version_check_ok.empty()); const std::string version_check_bad = Botan::runtime_version_check(1, 19, 42); const std::string expected_error = "Warning: linked version (" + sversion_str + ") does not match version built against (1.19.42)\n"; - result.test_eq("Expected warning text", version_check_bad, expected_error); + result.test_str_eq("Expected warning text", version_check_bad, expected_error); return {result}; } @@ -1020,28 +1029,29 @@ const std::vector d = parse_date(date_str); if(type == "valid" || type == "valid.not_std" || type == "valid.64_bit_time_t") { - Botan::calendar_point c(d[0], d[1], d[2], d[3], d[4], d[5]); - result.test_is_eq(date_str + " year", c.year(), d[0]); - result.test_is_eq(date_str + " month", c.month(), d[1]); - result.test_is_eq(date_str + " day", c.day(), d[2]); - result.test_is_eq(date_str + " hour", c.hour(), d[3]); - result.test_is_eq(date_str + " minute", c.minutes(), d[4]); - result.test_is_eq(date_str + " second", c.seconds(), d[5]); + const Botan::calendar_point c(d[0], d[1], d[2], d[3], d[4], d[5]); + result.test_u32_eq(date_str + " year", c.year(), d[0]); + result.test_u32_eq(date_str + " month", c.month(), d[1]); + result.test_u32_eq(date_str + " day", c.day(), d[2]); + result.test_u32_eq(date_str + " hour", c.hour(), d[3]); + result.test_u32_eq(date_str + " minute", c.minutes(), d[4]); + result.test_u32_eq(date_str + " second", c.seconds(), d[5]); if(type == "valid.not_std" || (type == "valid.64_bit_time_t" && c.year() > 2037 && sizeof(std::time_t) == 4)) { result.test_throws("valid but out of std::timepoint range", [c]() { c.to_std_timepoint(); }); } else { - Botan::calendar_point c2(c.to_std_timepoint()); - result.test_is_eq(date_str + " year", c2.year(), d[0]); - result.test_is_eq(date_str + " month", c2.month(), d[1]); - result.test_is_eq(date_str + " day", c2.day(), d[2]); - result.test_is_eq(date_str + " hour", c2.hour(), d[3]); - result.test_is_eq(date_str + " minute", c2.minutes(), d[4]); - result.test_is_eq(date_str + " second", c2.seconds(), d[5]); + const Botan::calendar_point c2(c.to_std_timepoint()); + result.test_u32_eq(date_str + " year", c2.year(), d[0]); + result.test_u32_eq(date_str + " month", c2.month(), d[1]); + result.test_u32_eq(date_str + " day", c2.day(), d[2]); + result.test_u32_eq(date_str + " hour", c2.hour(), d[3]); + result.test_u32_eq(date_str + " minute", c2.minutes(), d[4]); + result.test_u32_eq(date_str + " second", c2.seconds(), d[5]); } } else if(type == "invalid") { - result.test_throws("invalid date", [d]() { Botan::calendar_point c(d[0], d[1], d[2], d[3], d[4], d[5]); }); + result.test_throws("invalid date", + [d]() { const Botan::calendar_point c(d[0], d[1], d[2], d[3], d[4], d[5]); }); } else { throw Test_Error("Unexpected header '" + type + "' in date format tests"); } @@ -1051,9 +1061,9 @@ std::vector run_final_tests() override { Test::Result result("calendar_point::to_string"); - Botan::calendar_point d(2008, 5, 15, 9, 30, 33); + const Botan::calendar_point d(2008, 5, 15, 9, 30, 33); // desired format: --
T:: - result.test_eq("calendar_point::to_string", d.to_string(), "2008-05-15T09:30:33"); + result.test_str_eq("calendar_point::to_string", d.to_string(), "2008-05-15T09:30:33"); return {result}; } }; @@ -1068,6 +1078,19 @@ Test::Result result("Charset"); const std::vector in = vars.get_req_bin("In"); + + if(type == "UTF8-UCS2-INVALID") { + result.test_throws("utf8_to_ucs2 rejects invalid input", + [&] { Botan::utf8_to_ucs2(std::string(in.begin(), in.end())); }); + return result; + } + + if(type == "UTF8-UCS4-INVALID") { + result.test_throws("utf8_to_ucs4 rejects invalid input", + [&] { Botan::utf8_to_ucs4(std::string(in.begin(), in.end())); }); + return result; + } + const std::vector expected = vars.get_req_bin("Out"); std::string converted; @@ -1076,13 +1099,19 @@ converted = Botan::ucs2_to_utf8(in.data(), in.size()); } else if(type == "UCS4-UTF8") { converted = Botan::ucs4_to_utf8(in.data(), in.size()); + } else if(type == "UTF8-UCS2") { + std::vector ucs2 = Botan::utf8_to_ucs2(std::string(in.begin(), in.end())); + converted = std::string(ucs2.begin(), ucs2.end()); + } else if(type == "UTF8-UCS4") { + std::vector ucs4 = Botan::utf8_to_ucs4(std::string(in.begin(), in.end())); + converted = std::string(ucs4.begin(), ucs4.end()); } else if(type == "LATIN1-UTF8") { converted = Botan::latin1_to_utf8(in.data(), in.size()); } else { throw Test_Error("Unexpected header '" + type + "' in charset tests"); } - result.test_eq( + result.test_bin_eq( "string converted successfully", std::vector(converted.begin(), converted.end()), expected); return result; @@ -1103,7 +1132,7 @@ const bool expected = (type == "Invalid") ? false : true; const std::string what = hostname + ((expected == true) ? " matches " : " does not match ") + issued; - result.test_eq(what, Botan::host_wildcard_match(issued, hostname), expected); + result.test_bool_eq(what, Botan::host_wildcard_match(issued, hostname), expected); return result; } @@ -1111,6 +1140,39 @@ BOTAN_REGISTER_TEST("utils", "hostname", Hostname_Tests); +class DNS_Check_Tests final : public Text_Based_Test { + public: + DNS_Check_Tests() : Text_Based_Test("utils/dns.vec", "DNS") {} + + Test::Result run_one_test(const std::string& type, const VarMap& vars) override { + Test::Result result("DNS name validation"); + + const std::string name = vars.get_req_str("DNS"); + const bool valid = (type == "Invalid") ? false : true; + + try { + const auto canonicalized = Botan::check_and_canonicalize_dns_name(name); + BOTAN_UNUSED(canonicalized); + + if(valid) { + result.test_success("Accepted valid name"); + } else { + result.test_failure("Accepted invalid name"); + } + } catch(Botan::Decoding_Error&) { + if(valid) { + result.test_failure("Rejected valid name"); + } else { + result.test_success("Rejected invalid name"); + } + } + + return result; + } +}; + +BOTAN_REGISTER_TEST("utils", "dns_check", DNS_Check_Tests); + class IPv4_Parsing_Tests final : public Text_Based_Test { public: IPv4_Parsing_Tests() : Text_Based_Test("utils/ipv4.vec", "IPv4") {} @@ -1123,11 +1185,11 @@ auto ipv4 = Botan::string_to_ipv4(input); - result.test_eq("string_to_ipv4 accepts only valid", valid, ipv4.has_value()); + result.test_bool_eq("string_to_ipv4 accepts only valid", ipv4.has_value(), valid); if(ipv4) { const std::string rt = Botan::ipv4_to_string(ipv4.value()); - result.test_eq("ipv4_to_string and string_to_ipv4 round trip", input, rt); + result.test_str_eq("ipv4_to_string and string_to_ipv4 round trip", input, rt); } return result; @@ -1136,6 +1198,57 @@ BOTAN_REGISTER_TEST("utils", "ipv4_parse", IPv4_Parsing_Tests); +class IPv6_Parsing_Tests final : public Text_Based_Test { + public: + IPv6_Parsing_Tests() : Text_Based_Test("utils/ipv6.vec", "IPv6") {} + + Test::Result run_one_test(const std::string& header, const VarMap& vars) override { + Test::Result result("IPv6 parsing"); + + const std::string input = vars.get_req_str("IPv6"); + const bool valid = (header == "Valid"); + + auto ipv6 = Botan::string_to_ipv6(input); + + result.test_bool_eq("string_to_ipv6 accepts only valid", ipv6.has_value(), valid); + + if(ipv6) { + const std::string rt = Botan::ipv6_to_string(ipv6.value()); + result.test_str_eq("ipv6_to_string and string_to_ipv6 round trip", input, rt); + } + + return result; + } +}; + +BOTAN_REGISTER_TEST("utils", "ipv6_parse", IPv6_Parsing_Tests); + +class IPv6_Noncanonical_Parsing_Tests final : public Text_Based_Test { + public: + IPv6_Noncanonical_Parsing_Tests() : Text_Based_Test("utils/ipv6_nc.vec", "Input,Canonical") {} + + Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override { + Test::Result result("IPv6 parsing of non-canonical form"); + + const std::string input_str = vars.get_req_str("Input"); + const std::string canonical_str = vars.get_req_str("Canonical"); + + const auto ipv6 = Botan::string_to_ipv6(input_str); + const auto canonical = Botan::string_to_ipv6(canonical_str); + + result.test_is_true("IPv6 non-canonical parsing worked", ipv6.has_value()); + result.test_is_true("IPv6 canonical parsing worked", canonical.has_value()); + + if(ipv6.has_value() && canonical.has_value()) { + result.test_bin_eq("IPv6 non-canonical decoding", ipv6.value(), canonical.value()); + } + + return result; + } +}; + +BOTAN_REGISTER_TEST("utils", "ipv6_parse_non_canonical", IPv6_Noncanonical_Parsing_Tests); + class ReadKV_Tests final : public Text_Based_Test { public: ReadKV_Tests() : Text_Based_Test("utils/read_kv.vec", "Input,Expected") {} @@ -1165,12 +1278,12 @@ } std::string substr; - for(auto i = str.begin(); i != str.end(); ++i) { - if(*i == '|') { + for(const char c : str) { + if(c == '|') { elems.push_back(substr); substr.clear(); } else { - substr += *i; + substr += c; } } @@ -1184,53 +1297,71 @@ static void confirm_kv(Test::Result& result, const std::map& kv, const std::vector& expected) { - if(!result.test_eq("expected size", expected.size() % 2, size_t(0))) { + if(!result.test_sz_eq("expected size", expected.size() % 2, size_t(0))) { return; } for(size_t i = 0; i != expected.size(); i += 2) { auto j = kv.find(expected[i]); - if(result.confirm("Found key", j != kv.end())) { - result.test_eq("Matching value", j->second, expected[i + 1]); + if(result.test_is_true("Found key", j != kv.end())) { + result.test_str_eq("Matching value", j->second, expected[i + 1]); } } - result.test_eq("KV has same size as expected", kv.size(), expected.size() / 2); + result.test_sz_eq("KV has same size as expected", kv.size(), expected.size() / 2); } }; BOTAN_REGISTER_TEST("utils", "util_read_kv", ReadKV_Tests); +#if defined(BOTAN_HAS_CPUID) + class CPUID_Tests final : public Test { public: std::vector run() override { Test::Result result("CPUID"); - result.confirm("Endian is either little or big", - Botan::CPUID::is_big_endian() || Botan::CPUID::is_little_endian()); + const std::string cpuid_string = Botan::CPUID::to_string(); + result.test_success("CPUID::to_string doesn't crash"); - if(Botan::CPUID::is_little_endian()) { - result.test_eq("If endian is little, it is not also big endian", Botan::CPUID::is_big_endian(), false); - } else { - result.test_eq("If endian is big, it is not also little endian", Botan::CPUID::is_little_endian(), false); + for(size_t b = 0; b != 32; ++b) { + try { + const auto bit = static_cast(1) << b; + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) + const auto feat = Botan::CPUID::Feature(static_cast(bit)); + + const std::string feat_str = feat.to_string(); + + result.test_is_true("Feature string is not empty", !feat_str.empty()); + + if(auto from_str = Botan::CPUID::Feature::from_string(feat_str)) { + result.test_u32_eq("Feature::from_string returns expected bit", from_str->as_u32(), bit); + } else { + result.test_failure( + Botan::fmt("Feature::from_string didn't recognize its own output ({})", feat_str)); + } + } catch(Botan::Invalid_State&) { + // This will thrown if the bit is not a valid one + } } - const std::string cpuid_string = Botan::CPUID::to_string(); - result.test_success("CPUID::to_string doesn't crash"); + #if defined(BOTAN_TARGET_ARCH_IS_X86_FAMILY) -#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + const auto bit = Botan::CPUID::Feature::SSE2; - if(Botan::CPUID::has_sse2()) { - result.confirm("Output string includes sse2", cpuid_string.find("sse2") != std::string::npos); + if(Botan::CPUID::has(bit)) { + result.test_is_true("Output string includes sse2", cpuid_string.find("sse2") != std::string::npos); - Botan::CPUID::clear_cpuid_bit(Botan::CPUID::CPUID_SSE2_BIT); + Botan::CPUID::clear_cpuid_bit(bit); - result.test_eq("After clearing cpuid bit, has_sse2 returns false", Botan::CPUID::has_sse2(), false); + result.test_is_false("After clearing cpuid bit, CPUID::has for SSE2 returns false", Botan::CPUID::has(bit)); Botan::CPUID::initialize(); // reset state - result.test_eq("After reinitializing, has_sse2 returns true", Botan::CPUID::has_sse2(), true); + result.test_is_true("After reinitializing, CPUID::has for SSE2 returns true again", Botan::CPUID::has(bit)); } -#endif + #else + BOTAN_UNUSED(cpuid_string); + #endif return {result}; } @@ -1238,6 +1369,8 @@ BOTAN_REGISTER_SERIALIZED_TEST("utils", "cpuid", CPUID_Tests); +#endif + #if defined(BOTAN_HAS_UUID) class UUID_Tests : public Test { @@ -1250,33 +1383,32 @@ 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)); }); + result.test_throws("Cannot load wrong number of bytes", + []() { const Botan::UUID u(std::vector(15)); }); - result.test_eq("Empty UUID is empty", empty_uuid.is_valid(), false); - result.confirm("Empty UUID equals another empty UUID", empty_uuid == Botan::UUID()); + result.test_is_false("Empty UUID is empty", empty_uuid.is_valid()); + result.test_is_true("Empty UUID equals another empty UUID", empty_uuid == Botan::UUID()); result.test_throws("Empty UUID cannot become a string", [&]() { empty_uuid.to_string(); }); - result.test_eq("Random UUID not empty", random_uuid1.is_valid(), true); - result.test_eq("Random UUID not empty", random_uuid2.is_valid(), true); + result.test_is_true("Random UUID not empty", random_uuid1.is_valid()); + result.test_is_true("Random UUID not empty", random_uuid2.is_valid()); - result.confirm("Random UUIDs are distinct", random_uuid1 != random_uuid2); - result.confirm("Random UUIDs not equal to empty", random_uuid1 != empty_uuid); + result.test_is_true("Random UUIDs are distinct", random_uuid1 != random_uuid2); + result.test_is_true("Random UUIDs not equal to empty", random_uuid1 != empty_uuid); const std::string uuid4_str = loaded_uuid.to_string(); - result.test_eq("String matches expected", uuid4_str, "04040404-0404-0404-0404-040404040404"); + result.test_str_eq("String matches expected", uuid4_str, "04040404-0404-0404-0404-040404040404"); const std::string uuid_r1_str = random_uuid1.to_string(); - result.confirm("UUID from string matches", Botan::UUID(uuid_r1_str) == random_uuid1); + result.test_is_true("UUID from string matches", Botan::UUID(uuid_r1_str) == random_uuid1); class AllSame_RNG : public Botan::RandomNumberGenerator { public: explicit AllSame_RNG(uint8_t b) : m_val(b) {} void fill_bytes_with_input(std::span output, std::span /* ignored */) override { - for(auto& byte : output) { - byte = m_val; - } + std::fill(output.begin(), output.end(), m_val); } std::string name() const override { return "zeros"; } @@ -1293,11 +1425,13 @@ AllSame_RNG zeros(0x00); const Botan::UUID zero_uuid(zeros); - result.test_eq("Zero UUID matches expected", zero_uuid.to_string(), "00000000-0000-4000-8000-000000000000"); + result.test_str_eq( + "Zero UUID matches expected", zero_uuid.to_string(), "00000000-0000-4000-8000-000000000000"); AllSame_RNG ones(0xFF); const Botan::UUID ones_uuid(ones); - result.test_eq("Ones UUID matches expected", ones_uuid.to_string(), "FFFFFFFF-FFFF-4FFF-BFFF-FFFFFFFFFFFF"); + result.test_str_eq( + "Ones UUID matches expected", ones_uuid.to_string(), "FFFFFFFF-FFFF-4FFF-BFFF-FFFFFFFFFFFF"); return {result}; } @@ -1318,12 +1452,12 @@ checking that we don't crash, rather than we return that precise string. */ - result.test_eq("test 1", Botan::fmt("hi"), "hi"); - result.test_eq("test 2", Botan::fmt("ignored", 5), "ignored"); - result.test_eq("test 3", Botan::fmt("answer is {}", 42), "answer is 42"); - result.test_eq("test 4", Botan::fmt("{", 5), "{"); - result.test_eq("test 4", Botan::fmt("{}"), "{}"); - result.test_eq("test 5", Botan::fmt("{} == '{}'", 5, "five"), "5 == 'five'"); + result.test_str_eq("test 1", Botan::fmt("hi"), "hi"); + result.test_str_eq("test 2", Botan::fmt("ignored", 5), "ignored"); + result.test_str_eq("test 3", Botan::fmt("answer is {}", 42), "answer is 42"); + result.test_str_eq("test 4", Botan::fmt("{", 5), "{"); + result.test_str_eq("test 4", Botan::fmt("{}"), "{}"); + result.test_str_eq("test 5", Botan::fmt("{} == '{}'", 5, "five"), "5 == 'five'"); return {result}; } @@ -1341,7 +1475,7 @@ { auto clean = Botan::scoped_cleanup([&] { ran = true; }); } - result.confirm("cleanup ran", ran); + result.test_is_true("cleanup ran", ran); }), CHECK("leaving a function, results in cleanup", @@ -1353,10 +1487,10 @@ fn_called = true; }; - result.confirm("cleanup not yet ran", !ran); + result.test_is_true("cleanup not yet ran", !ran); fn(); - result.confirm("fn called", fn_called); - result.confirm("cleanup ran", ran); + result.test_is_true("fn called", fn_called); + result.test_is_true("cleanup ran", ran); }), CHECK("stack unwinding results in cleanup", @@ -1370,16 +1504,16 @@ throw std::runtime_error("test"); }; - result.confirm("cleanup not yet ran", !ran); + result.test_is_true("cleanup not yet ran", !ran); try { fn(); } catch(const std::exception&) { exception_caught = true; } - result.confirm("fn called", fn_called); - result.confirm("cleanup ran", ran); - result.confirm("exception caught", exception_caught); + result.test_is_true("fn called", fn_called); + result.test_is_true("cleanup ran", ran); + result.test_is_true("exception caught", exception_caught); }), CHECK("cleanup isn't called after disengaging", @@ -1389,7 +1523,7 @@ auto clean = Botan::scoped_cleanup([&] { ran = true; }); clean.disengage(); } - result.confirm("cleanup not ran", !ran); + result.test_is_true("cleanup not ran", !ran); }), }; diff -Nru botan3-3.7.1+dfsg/src/tests/test_utils_bitvector.cpp botan3-3.12.0+dfsg/src/tests/test_utils_bitvector.cpp --- botan3-3.7.1+dfsg/src/tests/test_utils_bitvector.cpp 2025-02-05 07:30:54.000000000 +0000 +++ botan3-3.12.0+dfsg/src/tests/test_utils_bitvector.cpp 2026-05-07 01:38:28.000000000 +0000 @@ -7,28 +7,29 @@ #include "tests.h" -#include - #if defined(BOTAN_HAS_BITVECTOR) + #include + #include #include + #include + #include + #include + #include #endif -#include -#include - namespace Botan_Tests { -#if defined(BOTAN_HAS_BITVECTOR) - namespace { +#if defined(BOTAN_HAS_BITVECTOR) + /// Returns a random number in the range [min, max) size_t rand_in_range(Botan::RandomNumberGenerator& rng, size_t min, size_t max) { if(min == max) { return min; } - size_t val = Botan::load_le(rng.random_array()); + const size_t val = Botan::load_le(rng.random_array()); return min + (val % (max - min)); } @@ -54,8 +55,8 @@ /// Create an empty bitvector of random size and chose a random number of points of interests std::pair> rnd_bitvector_with_rnd_pois(Botan::RandomNumberGenerator& rng) { - Botan::bitvector bv(rand_in_range(rng, 0, 65)); - size_t no_poi = rand_in_range(rng, 0, bv.size()); + const Botan::bitvector bv(rand_in_range(rng, 0, 65)); + const size_t no_poi = rand_in_range(rng, 0, bv.size()); auto points_of_interest = rand_indices(rng, no_poi, bv.size()); return {bv, {points_of_interest.begin(), points_of_interest.end()}}; @@ -74,18 +75,18 @@ return { CHECK("default constructed bitvector", [](auto& result) { - Botan::bitvector bv; - result.confirm("default constructed bitvector is empty", bv.empty()); - result.test_eq("default constructed bitvector has zero size", bv.size(), size_t(0)); + const Botan::bitvector bv; + result.test_is_true("default constructed bitvector is empty", bv.empty()); + result.test_sz_eq("default constructed bitvector has zero size", bv.size(), size_t(0)); }), CHECK("preallocated construction of bitvector", [](auto& result) { Botan::bitvector bv(10); - result.confirm("allocated bitvector is not empty", !bv.empty()); - result.test_eq("allocated bitvector has allocated size", bv.size(), size_t(10)); + result.test_is_true("allocated bitvector is not empty", !bv.empty()); + result.test_sz_eq("allocated bitvector has allocated size", bv.size(), size_t(10)); for(size_t i = 0; i < 10; ++i) { - result.confirm("bit not set yet", !bv.at(i)); + result.test_is_true("bit not set yet", !bv.at(i)); } }), @@ -93,7 +94,7 @@ [&](auto& result) { auto [bv, ones] = rnd_bitvector_with_rnd_pois(rng); - for(size_t i : ones) { + for(const size_t i : ones) { if(rng.next_byte() % 2 == 0) { bv.set(i); } else { @@ -101,7 +102,7 @@ } } for(size_t i = 0; i < bv.size(); ++i) { - result.confirm(Botan::fmt("bit {} in expected state", i), bv.at(i) == ones.contains(i)); + result.test_is_true(Botan::fmt("bit {} in expected state", i), bv.at(i) == ones.contains(i)); } }), @@ -112,7 +113,7 @@ b.set(); } - for(size_t i : zeros) { + for(const size_t i : zeros) { if(rng.next_byte() % 2 == 0) { bv.unset(i); } else { @@ -120,7 +121,7 @@ } } for(size_t i = 0; i < bv.size(); ++i) { - result.confirm(Botan::fmt("bit {} in expected state", i), bv.at(i) == !zeros.contains(i)); + result.test_is_true(Botan::fmt("bit {} in expected state", i), bv.at(i) == !zeros.contains(i)); } }), @@ -135,7 +136,7 @@ bv.flip(i); } for(size_t i = 0; i < bv.size(); ++i) { - result.confirm(Botan::fmt("bit {} in expected state", i), bv.at(i) == ones.contains(i)); + result.test_is_true(Botan::fmt("bit {} in expected state", i), bv.at(i) == ones.contains(i)); } }), @@ -153,11 +154,11 @@ CHECK("multiblock handling", [](auto& result) { Botan::bitvector bv(128); - result.test_eq("has more than 64 bits", bv.size(), 128); + result.test_sz_eq("has more than 64 bits", bv.size(), 128); bv.set(1).set(63).set(64).set(127); for(size_t i = 0; i < bv.size(); ++i) { - bool expected = (i == 1 || i == 63 || i == 64 || i == 127); - result.test_eq(Botan::fmt("bit {} in expected state", i), bv.at(i), expected); + const bool expected = (i == 1 || i == 63 || i == 64 || i == 127); + result.test_bool_eq(Botan::fmt("bit {} in expected state", i), bv.at(i), expected); } }), @@ -169,12 +170,12 @@ bv[2].flip(); bv[64] = true; bv[80] = true; - result.confirm("bit 0", bv[0]); - result.confirm("bit 1", bv[1]); - result.confirm("bit 2", bv[2]); - result.confirm("bit 3", !bv[3]); - result.confirm("bit 64", bv[64]); - result.confirm("bit 80", bv[80]); + result.test_is_true("bit 0", bv[0]); + result.test_is_true("bit 1", bv[1]); + result.test_is_true("bit 2", bv[2]); + result.test_is_true("bit 3", !bv[3]); + result.test_is_true("bit 64", bv[64]); + result.test_is_true("bit 80", bv[80]); }), CHECK("subscript operator does not validate offsets", @@ -193,89 +194,89 @@ result.require("precondition", !bv[0] && !bv[1]); bv[0] &= 1; // NOLINT(*-use-bool-literals) - result.confirm("bv[0] still 0", !bv[0]); + result.test_is_true("bv[0] still 0", !bv[0]); bv[0].set(); bv[0] &= 1; // NOLINT(*-use-bool-literals) - result.confirm("bv[0] still 1", bv[0]); + result.test_is_true("bv[0] still 1", bv[0]); bv[0] &= false; - result.confirm("bv[0] now 0 again", !bv[0]); + result.test_is_true("bv[0] now 0 again", !bv[0]); bv[0] &= !bv[1]; - result.confirm("bv[0] still 0 once more", !bv[0]); + result.test_is_true("bv[0] still 0 once more", !bv[0]); result.require("precondition 2", !bv[1] && !bv[2]); bv[1] |= 1; // NOLINT(modernize-use-bool-literals) - result.confirm("bv[1] is now 1", bv[1]); + result.test_is_true("bv[1] is now 1", bv[1]); bv[1] |= 0; // NOLINT(modernize-use-bool-literals) - result.confirm("bv[1] is still 1", bv[1]); + result.test_is_true("bv[1] is still 1", bv[1]); bv[1].unset(); bv[1] |= false; - result.confirm("bv[1] is 0", !bv[1]); + result.test_is_true("bv[1] is 0", !bv[1]); bv[1] |= !bv[2]; - result.confirm("bv[1] is 1 again", bv[1]); + result.test_is_true("bv[1] is 1 again", bv[1]); result.require("precondition 3", !bv[2] && !bv[3]); bv[2] ^= 0; // NOLINT(modernize-use-bool-literals) - result.confirm("bv[2] is still 0", !bv[2]); + result.test_is_true("bv[2] is still 0", !bv[2]); bv[2] ^= true; - result.confirm("bv[2] is now 1", bv[2]); + result.test_is_true("bv[2] is now 1", bv[2]); bv[2] ^= !bv[3]; - result.confirm("bv[2] is 0 again", !bv[2]); + result.test_is_true("bv[2] is 0 again", !bv[2]); }), }; } -std::vector test_bitvector_capacity(Botan::RandomNumberGenerator&) { +std::vector test_bitvector_capacity(Botan::RandomNumberGenerator& /*rng*/) { return { CHECK("default constructed bitvector", [](auto& result) { - Botan::bitvector bv; - result.confirm("empty", bv.empty()); - result.test_eq("no size", bv.size(), size_t(0)); - result.test_eq("no capacity", bv.capacity(), size_t(0)); + const Botan::bitvector bv; + result.test_is_true("empty", bv.empty()); + result.test_sz_eq("no size", bv.size(), size_t(0)); + result.test_sz_eq("no capacity", bv.capacity(), size_t(0)); }), CHECK("allocated bitvector has capacity", [](auto& result) { - Botan::bitvector bv(1); - result.confirm("empty", !bv.empty()); - result.test_eq("small size", bv.size(), size_t(1)); - result.test_gte("a little capacity", bv.capacity(), size_t(8)); + const Botan::bitvector bv(1); + result.test_is_true("empty", !bv.empty()); + result.test_sz_eq("small size", bv.size(), size_t(1)); + result.test_sz_gte("a little capacity", bv.capacity(), size_t(8)); }), CHECK("reserved bitvector has capacity", [](auto& result) { Botan::bitvector bv; - result.test_eq("no size", bv.size(), size_t(0)); - result.test_eq("no capacity", bv.capacity(), size_t(0)); + result.test_sz_eq("no size", bv.size(), size_t(0)); + result.test_sz_eq("no capacity", bv.capacity(), size_t(0)); bv.reserve(64); - result.test_eq("no size", bv.size(), size_t(0)); - result.test_gte("no capacity", bv.capacity(), size_t(64)); + result.test_sz_eq("no size", bv.size(), size_t(0)); + result.test_sz_gte("no capacity", bv.capacity(), size_t(64)); bv.reserve(128); - result.test_eq("no size", bv.size(), size_t(0)); - result.test_gte("no capacity", bv.capacity(), size_t(128)); + result.test_sz_eq("no size", bv.size(), size_t(0)); + result.test_sz_gte("no capacity", bv.capacity(), size_t(128)); }), CHECK("push_back() extends bitvector", [](Test::Result& result) { Botan::bitvector bv; - result.confirm("empty", bv.empty()); - result.test_eq("no size", bv.size(), size_t(0)); + result.test_is_true("empty", bv.empty()); + result.test_sz_eq("no size", bv.size(), size_t(0)); bv.push_back(true); bv.push_back(false); bv.push_back(true); bv.push_back(false); - result.confirm("not empty", !bv.empty()); - result.test_eq("some size", bv.size(), size_t(4)); - result.test_gte("capacity is typically bigger than size", bv.capacity(), size_t(8)); - - result.confirm("bit 0", bv.at(0)); - result.confirm("bit 1", !bv.at(1)); - result.confirm("bit 2", bv.at(2)); - result.confirm("bit 3", !bv.at(3)); + result.test_is_true("not empty", !bv.empty()); + result.test_sz_eq("some size", bv.size(), size_t(4)); + result.test_sz_gte("capacity is typically bigger than size", bv.capacity(), size_t(8)); + + result.test_is_true("bit 0", bv.at(0)); + result.test_is_true("bit 1", !bv.at(1)); + result.test_is_true("bit 2", bv.at(2)); + result.test_is_true("bit 3", !bv.at(3)); result.test_throws("bit 4 is not yet allocated", [&] { bv.at(4); }); }), @@ -287,23 +288,23 @@ bv.push_back(false); bv.push_back(true); bv.push_back(false); - result.confirm("last is false", !bv.back()); + result.test_is_true("last is false", !bv.back()); bv.pop_back(); - result.test_eq("size() == 3", bv.size(), 3); - result.confirm("last is true", bv.back()); + result.test_sz_eq("size() == 3", bv.size(), 3); + result.test_is_true("last is true", bv.back()); bv.pop_back(); - result.test_eq("size() == 2", bv.size(), 2); - result.confirm("last is false", !bv.back()); + result.test_sz_eq("size() == 2", bv.size(), 2); + result.test_is_true("last is false", !bv.back()); bv.pop_back(); - result.test_eq("size() == 1", bv.size(), 1); - result.confirm("last is true", bv.back()); - result.confirm("first is true", bv.front()); + result.test_sz_eq("size() == 1", bv.size(), 1); + result.test_is_true("last is true", bv.back()); + result.test_is_true("first is true", bv.front()); bv.pop_back(); - result.confirm("empty", bv.empty()); + result.test_is_true("empty", bv.empty()); result.test_throws("bit 4 is not yet allocated", [&] { bv.at(4); }); }), @@ -316,24 +317,55 @@ bv[9] = true; bv.resize(8); - result.test_eq("size is reduced", bv.size(), size_t(8)); + result.test_sz_eq("size is reduced", bv.size(), size_t(8)); for(size_t i = 0; i < bv.size(); ++i) { const bool expected = (i == 0 || i == 5); - result.test_eq(Botan::fmt("{} is as expected", i), bv[i], expected); + result.test_bool_eq(Botan::fmt("{} is as expected", i), bv[i], expected); } bv.resize(0); - result.confirm("resize(0) empties buffer", bv.empty()); + result.test_is_true("resize(0) empties buffer", bv.empty()); bv.resize(8); - result.confirm("0 is false", !bv[0]); - result.confirm("5 is false", !bv[5]); + result.test_is_true("0 is false", !bv[0]); + result.test_is_true("5 is false", !bv[5]); + }), + + CHECK("binary bitwise and comparison operators", + [](Test::Result& result) { + Botan::bitvector a(8); + a.set(0).set(3); + Botan::bitvector b(8); + b.set(1).set(3); + Botan::bitvector c(8); + c.set(0).set(3); // same is a + + result.test_is_true("equal bitvectors compare equal", a == c); + result.test_is_true("different bitvectors do not compare equal", !(a == b)); + result.test_is_true("different bitvectors compare not equal", a != b); + result.test_is_true("equal bitvectors do not compare not equal", !(a != c)); + + auto or_ab = a | b; + result.test_is_true("OR sets union bit 0", or_ab.at(0).is_set()); + result.test_is_true("OR sets union bit 1", or_ab.at(1).is_set()); + result.test_is_true("OR sets union bit 3", or_ab.at(3).is_set()); + result.test_is_true("OR leaves unset bit 2", !or_ab.at(2).is_set()); + + auto and_ab = a & b; + result.test_is_true("AND keeps common bit 3", and_ab.at(3).is_set()); + result.test_is_true("AND clears non-common bit 0", !and_ab.at(0).is_set()); + result.test_is_true("AND clears non-common bit 1", !and_ab.at(1).is_set()); + + auto xor_ab = a ^ b; + result.test_is_true("XOR sets differing bit 0", xor_ab.at(0).is_set()); + result.test_is_true("XOR sets differing bit 1", xor_ab.at(1).is_set()); + result.test_is_true("XOR clears common bit 3", !xor_ab.at(3).is_set()); }), }; } -std::vector test_bitvector_subvector(Botan::RandomNumberGenerator&) { +std::vector test_bitvector_subvector(Botan::RandomNumberGenerator& /*rng*/) { auto make_bitpattern = [&](T& bitvector, size_t pattern_offset = 0) { auto next = pattern_generator<3>(pattern_offset); @@ -360,11 +392,11 @@ if constexpr(std::unsigned_integral) { for(size_t i = 0; i < sizeof(bv_t) * 8; ++i) { - result.confirm(Botan::fmt("{} is as expected", i), (bitvector & (bv_t(1) << i)) != 0, next()); + result.test_bool_eq(Botan::fmt("{} is as expected", i), (bitvector & (bv_t(1) << i)) != 0, next()); } } else { for(size_t i = 0; i < bitvector.size(); ++i) { - result.confirm(Botan::fmt("{} is as expected", i), bitvector[i], next()); + result.test_bool_eq(Botan::fmt("{} is as expected", i), bitvector[i], next()); } } }; @@ -374,14 +406,14 @@ for(size_t i = 0; i < bitvector.size(); ++i) { const bool i_in_range = (zero_region.first <= i && i < zero_region.second); const bool expected = next(); - result.confirm(Botan::fmt("{} is as expected", i), bitvector[i], !i_in_range && expected); + result.test_bool_eq(Botan::fmt("{} is as expected", i), bitvector[i], !i_in_range && expected); } }; return { CHECK("range errors are caught", [&](auto& result) { - Botan::bitvector bv(100); + const Botan::bitvector bv(100); result.template test_throws("out of range", [&] { bv.subvector(0, 101); }); result.template test_throws("out of range", [&] { bv.subvector(90, 11); }); result.template test_throws("out of range", [&] { bv.subvector(100, 1); }); @@ -390,13 +422,13 @@ CHECK("empty copy is allowed", [&](auto& result) { - Botan::bitvector bv1(100); + const Botan::bitvector bv1(100); auto bv2 = bv1.subvector(0, 0); - result.test_eq("empty at 0", bv2.size(), size_t(0)); + result.test_sz_eq("empty at 0", bv2.size(), size_t(0)); auto bv3 = bv1.subvector(10, 0); - result.test_eq("empty at 10", bv3.size(), size_t(0)); + result.test_sz_eq("empty at 10", bv3.size(), size_t(0)); auto bv4 = bv1.subvector(100, 0); - result.test_eq("empty at 100", bv3.size(), size_t(0)); + result.test_sz_eq("empty at 100", bv3.size(), size_t(0)); }), CHECK("byte-aligned copy", @@ -405,11 +437,11 @@ make_bitpattern(bv1); auto bv2 = bv1.subvector(16, 58); - result.test_eq("size is as requested", bv2.size(), size_t(58)); + result.test_sz_eq("size is as requested", bv2.size(), size_t(58)); check_bitpattern(result, bv2, 16); auto bv3 = bv1.subvector(32); // copy until the end - result.test_eq("size is as expected", bv3.size(), size_t(68)); + result.test_sz_eq("size is as expected", bv3.size(), size_t(68)); check_bitpattern(result, bv3, 32); }), @@ -419,23 +451,23 @@ make_bitpattern(bv1); auto bv2 = bv1.subvector(8, 91); - result.test_eq("size is as expected", bv2.size(), size_t(91)); + result.test_sz_eq("size is as expected", bv2.size(), size_t(91)); check_bitpattern(result, bv2, 8); auto bv3 = bv1.subvector(16, 58); - result.test_eq("size is as requested", bv3.size(), size_t(58)); + result.test_sz_eq("size is as requested", bv3.size(), size_t(58)); check_bitpattern(result, bv3, 16); auto bv4 = bv1.subvector(24); // copy until the end - result.test_eq("size is as expected", bv4.size(), size_t(100 - 24)); + result.test_sz_eq("size is as expected", bv4.size(), size_t(100 - 24)); check_bitpattern(result, bv4, 24); auto bv5 = bv1.subvector(32); // copy until the end - result.test_eq("size is as expected", bv5.size(), size_t(100 - 32)); + result.test_sz_eq("size is as expected", bv5.size(), size_t(100 - 32)); check_bitpattern(result, bv5, 32); auto bv6 = bv1.subvector(48, 51); // copy until the end - result.test_eq("size is as expected", bv6.size(), size_t(51)); + result.test_sz_eq("size is as expected", bv6.size(), size_t(51)); check_bitpattern(result, bv6, 48); }), @@ -445,12 +477,12 @@ make_bitpattern(bv1); auto bv2 = bv1.subvector(16, 17); - result.test_eq("size is as requested", bv2.size(), size_t(17)); + result.test_sz_eq("size is as requested", bv2.size(), size_t(17)); check_bitpattern(result, bv2, 16); bv2.resize(32); for(size_t i = 17; i < bv2.size(); ++i) { - result.confirm("tail is zero", !bv2[i]); + result.test_is_true("tail is zero", !bv2[i]); } }), @@ -460,31 +492,31 @@ make_bitpattern(bv1); auto bv2 = bv1.subvector(19, 69); - result.test_eq("size is as requested", bv2.size(), size_t(69)); + result.test_sz_eq("size is as requested", bv2.size(), size_t(69)); check_bitpattern(result, bv2, 19); auto bv3 = bv1.subvector(21); // copy until the end - result.test_eq("size is as expected", bv3.size(), size_t(79)); + result.test_sz_eq("size is as expected", bv3.size(), size_t(79)); check_bitpattern(result, bv3, 21); auto bv4 = bv1.subvector(1, 16); - result.test_eq("size is as expected", bv4.size(), size_t(16)); + result.test_sz_eq("size is as expected", bv4.size(), size_t(16)); check_bitpattern(result, bv4, 1); auto bv5 = bv1.subvector(1, 32); - result.test_eq("size is as expected", bv5.size(), size_t(32)); + result.test_sz_eq("size is as expected", bv5.size(), size_t(32)); check_bitpattern(result, bv5, 1); auto bv6 = bv5.subvector(1, 12); - result.test_eq("size is as expected", bv6.size(), size_t(12)); + result.test_sz_eq("size is as expected", bv6.size(), size_t(12)); check_bitpattern(result, bv6, 1 + 1); auto bv7 = bv1.subvector(17, 67); - result.test_eq("size is as expected", bv7.size(), size_t(67)); + result.test_sz_eq("size is as expected", bv7.size(), size_t(67)); check_bitpattern(result, bv7, 17); auto bv8 = bv1.subvector(33); // copy until the end - result.test_eq("size is as expected", bv8.size(), size_t(67)); + result.test_sz_eq("size is as expected", bv8.size(), size_t(67)); check_bitpattern(result, bv8, 33); }), @@ -624,7 +656,7 @@ }; } -std::vector test_bitvector_global_modifiers_and_predicates(Botan::RandomNumberGenerator&) { +std::vector test_bitvector_global_modifiers_and_predicates(Botan::RandomNumberGenerator& /*rng*/) { auto make_bitpattern = [](auto& bitvector) { auto next = pattern_generator<5>(); for(auto& i : bitvector) { @@ -635,14 +667,14 @@ auto check_bitpattern = [](auto& result, auto& bitvector) { auto next = pattern_generator<5>(); for(size_t i = 0; i < bitvector.size(); ++i) { - result.confirm(Botan::fmt("{} is as expected", i), bitvector[i], next()); + result.test_bool_eq(Botan::fmt("{} is as expected", i), bitvector[i], next()); } }; auto check_flipped_bitpattern = [](auto& result, auto& bitvector) { auto next = pattern_generator<5>(); for(size_t i = 0; i < bitvector.size(); ++i) { - result.confirm(Botan::fmt("{} is as expected", i), bitvector[i], !next()); + result.test_bool_eq(Botan::fmt("{} is as expected", i), bitvector[i], !next()); } }; @@ -653,17 +685,17 @@ bv.push_back(true); bv.flip(); - result.confirm("bit is flipped", !bv[0]); + result.test_is_true("bit is flipped", !bv[0]); // check that unused bits aren't flipped bv.resize(8); - for(size_t i = 0; i < bv.size(); ++i) { - result.confirm("all bits are false", !bv[i]); + for(auto&& b : bv) { + result.test_is_true("all bits are false", !b); } bv.resize(1); bv.flip(); - result.confirm("bit is flipped again", bv[0]); + result.test_is_true("bit is flipped again", bv[0]); }), CHECK("bits in many blocks", @@ -679,7 +711,7 @@ bv.resize(112); for(size_t i = 99; i < bv.size(); ++i) { - result.confirm("just-allocated bit is not set", !bv[i]); + result.test_is_true("just-allocated bit is not set", !bv[i]); } }), @@ -692,12 +724,12 @@ bv.resize(128); for(size_t i = 0; i < bv.size(); ++i) { const bool expected = (i < 99); - result.test_eq("only set bits are set", bv[i], expected); + result.test_bool_eq("only set bits are set", bv[i], expected); } bv.unset(); - for(size_t i = 0; i < bv.size(); ++i) { - result.confirm("bit is not set", !bv[i]); + for(auto&& b : bv) { + result.test_is_true("bit is not set", !b); } }), @@ -705,58 +737,58 @@ [&](auto& result) { Botan::bitvector bv(99); - result.confirm("default construction yields all-zero", bv.none_vartime()); - result.confirm("default construction yields all-zero 2", !bv.any_vartime()); - result.confirm("default construction yields all-zero 3", !bv.all_vartime()); - result.confirm("default construction yields all-zero 4", bv.none()); - result.confirm("default construction yields all-zero 5", !bv.any()); - result.confirm("default construction yields all-zero 6", !bv.all()); + result.test_is_true("default construction yields all-zero", bv.none_vartime()); + result.test_is_true("default construction yields all-zero 2", !bv.any_vartime()); + result.test_is_true("default construction yields all-zero 3", !bv.all_vartime()); + result.test_is_true("default construction yields all-zero 4", bv.none()); + result.test_is_true("default construction yields all-zero 5", !bv.any()); + result.test_is_true("default construction yields all-zero 6", !bv.all()); bv.set(42); - result.confirm("setting a bit means there's a bit set", !bv.none_vartime()); - result.confirm("setting a bit means there's a bit set 2", bv.any_vartime()); - result.confirm("setting a bit means there's not all bits set", !bv.all_vartime()); - result.confirm("setting a bit means there's a bit set 3", !bv.none()); - result.confirm("setting a bit means there's a bit set 4", bv.any()); - result.confirm("setting a bit means there's not all bits set 2", !bv.all()); + result.test_is_true("setting a bit means there's a bit set", !bv.none_vartime()); + result.test_is_true("setting a bit means there's a bit set 2", bv.any_vartime()); + result.test_is_true("setting a bit means there's not all bits set", !bv.all_vartime()); + result.test_is_true("setting a bit means there's a bit set 3", !bv.none()); + result.test_is_true("setting a bit means there's a bit set 4", bv.any()); + result.test_is_true("setting a bit means there's not all bits set 2", !bv.all()); bv.set(); - result.confirm("setting all bits means there's a bit set", !bv.none_vartime()); - result.confirm("setting all bits means there's a bit set 2", bv.any_vartime()); - result.confirm("setting all bits means all bits are set", bv.all_vartime()); - result.confirm("setting all bits means there's a bit set 3", !bv.none()); - result.confirm("setting all bits means there's a bit set 4", bv.any()); - result.confirm("setting all bits means all bits are set 2", bv.all()); + result.test_is_true("setting all bits means there's a bit set", !bv.none_vartime()); + result.test_is_true("setting all bits means there's a bit set 2", bv.any_vartime()); + result.test_is_true("setting all bits means all bits are set", bv.all_vartime()); + result.test_is_true("setting all bits means there's a bit set 3", !bv.none()); + result.test_is_true("setting all bits means there's a bit set 4", bv.any()); + result.test_is_true("setting all bits means all bits are set 2", bv.all()); bv.unset(97); - result.confirm("a single 0 at the end means that there's a bit set", !bv.none_vartime()); - result.confirm("a single 0 at the end means that there are bits set", bv.any_vartime()); - result.confirm("a single 0 at the end means that there are not all bits set", !bv.all_vartime()); - result.confirm("a single 0 at the end means that there's a bit set 2", !bv.none()); - result.confirm("a single 0 at the end means that there are bits set 2", bv.any()); - result.confirm("a single 0 at the end means that there are not all bits set 2", !bv.all()); + result.test_is_true("a single 0 at the end means that there's a bit set", !bv.none_vartime()); + result.test_is_true("a single 0 at the end means that there are bits set", bv.any_vartime()); + result.test_is_true("a single 0 at the end means that there are not all bits set", !bv.all_vartime()); + result.test_is_true("a single 0 at the end means that there's a bit set 2", !bv.none()); + result.test_is_true("a single 0 at the end means that there are bits set 2", bv.any()); + result.test_is_true("a single 0 at the end means that there are not all bits set 2", !bv.all()); bv.unset(); - result.confirm("unsetting all bits means there's no bit set", bv.none_vartime()); - result.confirm("unsetting all bits means there's no bit set 2", !bv.any_vartime()); - result.confirm("unsetting all bits means there's not all bits set", !bv.all_vartime()); - result.confirm("unsetting all bits means there's no bit set 3", bv.none()); - result.confirm("unsetting all bits means there's no bit set 4", !bv.any()); - result.confirm("unsetting all bits means there's not all bits set 2", !bv.all()); + result.test_is_true("unsetting all bits means there's no bit set", bv.none_vartime()); + result.test_is_true("unsetting all bits means there's no bit set 2", !bv.any_vartime()); + result.test_is_true("unsetting all bits means there's not all bits set", !bv.all_vartime()); + result.test_is_true("unsetting all bits means there's no bit set 3", bv.none()); + result.test_is_true("unsetting all bits means there's no bit set 4", !bv.any()); + result.test_is_true("unsetting all bits means there's not all bits set 2", !bv.all()); }), CHECK("hamming weight oddness", [](auto& result) { - const auto evn = Botan::hex_decode("FE3410CB0278E4D26602"); + const auto even = Botan::hex_decode("FE3410CB0278E4D26602"); const auto odd = Botan::hex_decode("BB2418C2B4F288921203"); - result.confirm("odd hamming", Botan::bitvector(odd).has_odd_hamming_weight().as_bool()); - result.confirm("even hamming", !Botan::bitvector(evn).has_odd_hamming_weight().as_bool()); + result.test_is_true("odd hamming", Botan::bitvector(odd).has_odd_hamming_weight().as_bool()); + result.test_is_true("even hamming", !Botan::bitvector(even).has_odd_hamming_weight().as_bool()); }), CHECK("hamming weight", [](auto& result) { - auto naive_count = [](auto& v) { + auto naive_count = [](const auto& v) { size_t weight = 0; for(const auto& bit : v) { weight += bit.template as(); @@ -766,45 +798,46 @@ // the last three bits of this bitvector are set, then there's a gap auto bv = Botan::bitvector(Botan::hex_decode("FE3410CB0278E4D26602E0")); - result.test_eq("hamming weight", bv.hamming_weight(), size_t(37)); - result.test_eq("hamming weight", bv.hamming_weight(), naive_count(bv)); + result.test_sz_eq("hamming weight", bv.hamming_weight(), size_t(37)); + result.test_sz_eq("hamming weight", bv.hamming_weight(), naive_count(bv)); bv.pop_back(); - result.test_eq("hamming weight", bv.hamming_weight(), size_t(36)); - result.test_eq("hamming weight", bv.hamming_weight(), naive_count(bv)); + result.test_sz_eq("hamming weight", bv.hamming_weight(), size_t(36)); + result.test_sz_eq("hamming weight", bv.hamming_weight(), naive_count(bv)); bv.pop_back(); - result.test_eq("hamming weight", bv.hamming_weight(), size_t(35)); - result.test_eq("hamming weight", bv.hamming_weight(), naive_count(bv)); + result.test_sz_eq("hamming weight", bv.hamming_weight(), size_t(35)); + result.test_sz_eq("hamming weight", bv.hamming_weight(), naive_count(bv)); bv.pop_back(); - result.test_eq("hamming weight", bv.hamming_weight(), size_t(34)); - result.test_eq("hamming weight", bv.hamming_weight(), naive_count(bv)); + result.test_sz_eq("hamming weight", bv.hamming_weight(), size_t(34)); + result.test_sz_eq("hamming weight", bv.hamming_weight(), naive_count(bv)); bv.pop_back(); - result.test_eq("hamming weight", bv.hamming_weight(), size_t(34)); - result.test_eq("hamming weight", bv.hamming_weight(), naive_count(bv)); + result.test_sz_eq("hamming weight", bv.hamming_weight(), size_t(34)); + result.test_sz_eq("hamming weight", bv.hamming_weight(), naive_count(bv)); }), }; } -std::vector test_bitvector_binary_operators(Botan::RandomNumberGenerator&) { +std::vector test_bitvector_binary_operators(Botan::RandomNumberGenerator& /*rng*/) { auto check_set = [](auto& result, auto bits, std::vector set_bits) { for(size_t i = 0; i < bits.size(); ++i) { - const auto should_be_set = std::find(set_bits.begin(), set_bits.end(), i) != set_bits.end(); - result.test_eq(Botan::fmt("{} should {}be set", i, (!should_be_set ? "not " : "")), bits[i], should_be_set); + const bool should_be_set = std::find(set_bits.begin(), set_bits.end(), i) != set_bits.end(); + result.test_bool_eq( + Botan::fmt("{} should {}be set", i, (!should_be_set ? "not " : "")), bits[i], should_be_set); } }; auto is_secure_allocator = []